pulse-js-framework 1.4.3 → 1.4.5

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/package.json CHANGED
@@ -1,18 +1,59 @@
1
1
  {
2
2
  "name": "pulse-js-framework",
3
- "version": "1.4.3",
3
+ "version": "1.4.5",
4
4
  "description": "A declarative DOM framework with CSS selector-based structure and reactive pulsations",
5
5
  "type": "module",
6
6
  "main": "index.js",
7
+ "types": "types/index.d.ts",
7
8
  "bin": {
8
9
  "pulse": "cli/index.js"
9
10
  },
10
11
  "exports": {
11
- ".": "./index.js",
12
- "./runtime": "./runtime/index.js",
13
- "./runtime/*": "./runtime/*.js",
14
- "./compiler": "./compiler/index.js",
15
- "./vite": "./loader/vite-plugin.js"
12
+ ".": {
13
+ "types": "./types/index.d.ts",
14
+ "default": "./index.js"
15
+ },
16
+ "./runtime": {
17
+ "types": "./types/index.d.ts",
18
+ "default": "./runtime/index.js"
19
+ },
20
+ "./runtime/pulse": {
21
+ "types": "./types/pulse.d.ts",
22
+ "default": "./runtime/pulse.js"
23
+ },
24
+ "./runtime/dom": {
25
+ "types": "./types/dom.d.ts",
26
+ "default": "./runtime/dom.js"
27
+ },
28
+ "./runtime/router": {
29
+ "types": "./types/router.d.ts",
30
+ "default": "./runtime/router.js"
31
+ },
32
+ "./runtime/store": {
33
+ "types": "./types/store.d.ts",
34
+ "default": "./runtime/store.js"
35
+ },
36
+ "./runtime/native": {
37
+ "types": "./types/index.d.ts",
38
+ "default": "./runtime/native.js"
39
+ },
40
+ "./runtime/logger": {
41
+ "types": "./types/logger.d.ts",
42
+ "default": "./runtime/logger.js"
43
+ },
44
+ "./compiler": {
45
+ "types": "./types/index.d.ts",
46
+ "default": "./compiler/index.js"
47
+ },
48
+ "./compiler/lexer": "./compiler/lexer.js",
49
+ "./compiler/parser": "./compiler/parser.js",
50
+ "./compiler/transformer": "./compiler/transformer.js",
51
+ "./vite": {
52
+ "types": "./types/index.d.ts",
53
+ "default": "./loader/vite-plugin.js"
54
+ },
55
+ "./mobile": "./mobile/bridge/pulse-native.js",
56
+ "./package.json": "./package.json"
16
57
  },
17
58
  "files": [
18
59
  "index.js",
@@ -21,18 +62,22 @@
21
62
  "compiler/",
22
63
  "loader/",
23
64
  "mobile/",
65
+ "types/",
24
66
  "README.md",
25
67
  "LICENSE"
26
68
  ],
27
69
  "scripts": {
28
- "test": "npm run test:compiler && npm run test:pulse && npm run test:dom && npm run test:lint && npm run test:format && npm run test:analyze",
70
+ "test": "npm run test:compiler && npm run test:pulse && npm run test:dom && npm run test:router && npm run test:store && npm run test:lint && npm run test:format && npm run test:analyze",
29
71
  "test:compiler": "node test/compiler.test.js",
30
72
  "test:pulse": "node test/pulse.test.js",
31
73
  "test:dom": "node test/dom.test.js",
74
+ "test:router": "node test/router.test.js",
75
+ "test:store": "node test/store.test.js",
32
76
  "test:lint": "node test/lint.test.js",
33
77
  "test:format": "node test/format.test.js",
34
78
  "test:analyze": "node test/analyze.test.js",
35
- "build:netlify": "node scripts/build-netlify.js"
79
+ "build:netlify": "node scripts/build-netlify.js",
80
+ "version": "node scripts/sync-version.js"
36
81
  },
37
82
  "keywords": [
38
83
  "framework",
package/runtime/dom.js CHANGED
@@ -6,6 +6,9 @@
6
6
  */
7
7
 
8
8
  import { effect, pulse, batch, onCleanup } from './pulse.js';
9
+ import { loggers } from './logger.js';
10
+
11
+ const log = loggers.dom;
9
12
 
10
13
  // Lifecycle tracking
11
14
  let mountCallbacks = [];
@@ -523,7 +526,7 @@ export function component(setup) {
523
526
  try {
524
527
  cb();
525
528
  } catch (e) {
526
- console.error('Mount callback error:', e);
529
+ log.error('Mount callback error:', e);
527
530
  }
528
531
  }
529
532
  });
@@ -559,7 +562,7 @@ export function portal(children, target) {
559
562
  : target;
560
563
 
561
564
  if (!resolvedTarget) {
562
- console.warn('Portal target not found:', target);
565
+ log.warn('Portal target not found:', target);
563
566
  return document.createComment('portal-target-not-found');
564
567
  }
565
568
 
@@ -653,7 +656,7 @@ export function errorBoundary(children, fallback) {
653
656
  marker.parentNode?.insertBefore(fragment, marker.nextSibling);
654
657
  }
655
658
  } catch (e) {
656
- console.error('Error in component:', e);
659
+ log.error('Error in component:', e);
657
660
  error.set(e);
658
661
  // Re-render with error
659
662
  if (!hasError) {
package/runtime/index.js CHANGED
@@ -7,9 +7,11 @@ export * from './dom.js';
7
7
  export * from './router.js';
8
8
  export * from './store.js';
9
9
  export * from './native.js';
10
+ export * from './logger.js';
10
11
 
11
12
  export { default as PulseCore } from './pulse.js';
12
13
  export { default as PulseDOM } from './dom.js';
13
14
  export { default as PulseRouter } from './router.js';
14
15
  export { default as PulseStore } from './store.js';
15
16
  export { default as PulseNative } from './native.js';
17
+ export { default as PulseLogger } from './logger.js';
@@ -0,0 +1,304 @@
1
+ /**
2
+ * Pulse Logger - Centralized logging with namespaces and levels
3
+ * @module pulse-js-framework/runtime/logger
4
+ *
5
+ * @example
6
+ * import { logger, createLogger } from './logger.js';
7
+ *
8
+ * // Default logger
9
+ * logger.info('Hello');
10
+ * logger.warn('Warning');
11
+ * logger.error('Error');
12
+ *
13
+ * // Namespaced logger
14
+ * const log = createLogger('Router');
15
+ * log.info('Navigating to /home'); // [Router] Navigating to /home
16
+ */
17
+
18
+ /**
19
+ * Log level constants
20
+ * @readonly
21
+ * @enum {number}
22
+ */
23
+ export const LogLevel = {
24
+ /** No logging */
25
+ SILENT: 0,
26
+ /** Only errors */
27
+ ERROR: 1,
28
+ /** Errors and warnings */
29
+ WARN: 2,
30
+ /** Errors, warnings, and info (default) */
31
+ INFO: 3,
32
+ /** All messages including debug */
33
+ DEBUG: 4
34
+ };
35
+
36
+ /** @type {number} */
37
+ let globalLevel = LogLevel.INFO;
38
+
39
+ /** @type {LogFormatter|null} */
40
+ let globalFormatter = null;
41
+
42
+ /**
43
+ * @callback LogFormatter
44
+ * @param {'error'|'warn'|'info'|'debug'} level - The log level
45
+ * @param {string|null} namespace - The logger namespace
46
+ * @param {Array<*>} args - The arguments to log
47
+ * @returns {string} The formatted log message
48
+ */
49
+
50
+ /**
51
+ * Set the global log level for all loggers
52
+ * @param {number} level - A LogLevel value (SILENT=0, ERROR=1, WARN=2, INFO=3, DEBUG=4)
53
+ * @returns {void}
54
+ * @example
55
+ * setLogLevel(LogLevel.DEBUG); // Enable all logging
56
+ * setLogLevel(LogLevel.SILENT); // Disable all logging
57
+ */
58
+ export function setLogLevel(level) {
59
+ globalLevel = level;
60
+ }
61
+
62
+ /**
63
+ * Get the current global log level
64
+ * @returns {number} The current LogLevel value
65
+ * @example
66
+ * const level = getLogLevel();
67
+ * if (level >= LogLevel.DEBUG) {
68
+ * // Debug logging is enabled
69
+ * }
70
+ */
71
+ export function getLogLevel() {
72
+ return globalLevel;
73
+ }
74
+
75
+ /**
76
+ * Set a custom formatter function for all loggers
77
+ * @param {LogFormatter|null} formatter - Custom formatter function, or null to use default
78
+ * @returns {void}
79
+ * @example
80
+ * setFormatter((level, namespace, args) => {
81
+ * const timestamp = new Date().toISOString();
82
+ * const prefix = namespace ? `[${namespace}]` : '';
83
+ * return `${timestamp} ${level.toUpperCase()} ${prefix} ${args.join(' ')}`;
84
+ * });
85
+ */
86
+ export function setFormatter(formatter) {
87
+ globalFormatter = formatter;
88
+ }
89
+
90
+ /**
91
+ * Format message arguments with optional namespace prefix
92
+ * @private
93
+ * @param {string|null} namespace - The logger namespace
94
+ * @param {Array<*>} args - Arguments to format
95
+ * @returns {Array<*>} Formatted arguments array
96
+ */
97
+ function formatArgs(namespace, args) {
98
+ if (!namespace) return args;
99
+
100
+ // If first arg is a string, prepend namespace
101
+ if (typeof args[0] === 'string') {
102
+ return [`[${namespace}] ${args[0]}`, ...args.slice(1)];
103
+ }
104
+
105
+ // Otherwise, add namespace as first arg
106
+ return [`[${namespace}]`, ...args];
107
+ }
108
+
109
+ /**
110
+ * @typedef {Object} Logger
111
+ * @property {function(...*): void} error - Log error message
112
+ * @property {function(...*): void} warn - Log warning message
113
+ * @property {function(...*): void} info - Log info message
114
+ * @property {function(...*): void} debug - Log debug message
115
+ * @property {function(string): void} group - Start a collapsed log group
116
+ * @property {function(): void} groupEnd - End the current log group
117
+ * @property {function(number, ...*): void} log - Log with custom level
118
+ * @property {function(string): Logger} child - Create a child logger with sub-namespace
119
+ */
120
+
121
+ /**
122
+ * @typedef {Object} LoggerOptions
123
+ * @property {number} [level] - Override global level for this logger instance
124
+ */
125
+
126
+ /**
127
+ * Create a logger instance with optional namespace
128
+ * @param {string|null} [namespace=null] - Logger namespace (e.g., 'Router', 'Store')
129
+ * @param {LoggerOptions} [options={}] - Logger configuration options
130
+ * @returns {Logger} A logger instance with error, warn, info, debug methods
131
+ * @example
132
+ * const log = createLogger('MyComponent');
133
+ * log.info('Initialized'); // [MyComponent] Initialized
134
+ * log.error('Failed', { code: 500 }); // [MyComponent] Failed { code: 500 }
135
+ *
136
+ * // With custom level
137
+ * const debugLog = createLogger('Debug', { level: LogLevel.DEBUG });
138
+ */
139
+ export function createLogger(namespace = null, options = {}) {
140
+ const localLevel = options.level;
141
+
142
+ /**
143
+ * Check if a message at the given level should be logged
144
+ * @param {number} level - The log level to check
145
+ * @returns {boolean} True if the message should be logged
146
+ */
147
+ function shouldLog(level) {
148
+ const effectiveLevel = localLevel !== undefined ? localLevel : globalLevel;
149
+ return level <= effectiveLevel;
150
+ }
151
+
152
+ return {
153
+ /**
154
+ * Log an error message (shown unless level is SILENT)
155
+ * @param {...*} args - Values to log
156
+ * @returns {void}
157
+ */
158
+ error(...args) {
159
+ if (shouldLog(LogLevel.ERROR)) {
160
+ if (globalFormatter) {
161
+ console.error(globalFormatter('error', namespace, args));
162
+ } else {
163
+ console.error(...formatArgs(namespace, args));
164
+ }
165
+ }
166
+ },
167
+
168
+ /**
169
+ * Log a warning message (shown at WARN level and above)
170
+ * @param {...*} args - Values to log
171
+ * @returns {void}
172
+ */
173
+ warn(...args) {
174
+ if (shouldLog(LogLevel.WARN)) {
175
+ if (globalFormatter) {
176
+ console.warn(globalFormatter('warn', namespace, args));
177
+ } else {
178
+ console.warn(...formatArgs(namespace, args));
179
+ }
180
+ }
181
+ },
182
+
183
+ /**
184
+ * Log an info message (shown at INFO level and above)
185
+ * @param {...*} args - Values to log
186
+ * @returns {void}
187
+ */
188
+ info(...args) {
189
+ if (shouldLog(LogLevel.INFO)) {
190
+ if (globalFormatter) {
191
+ console.log(globalFormatter('info', namespace, args));
192
+ } else {
193
+ console.log(...formatArgs(namespace, args));
194
+ }
195
+ }
196
+ },
197
+
198
+ /**
199
+ * Log a debug message (only shown at DEBUG level)
200
+ * @param {...*} args - Values to log
201
+ * @returns {void}
202
+ */
203
+ debug(...args) {
204
+ if (shouldLog(LogLevel.DEBUG)) {
205
+ if (globalFormatter) {
206
+ console.log(globalFormatter('debug', namespace, args));
207
+ } else {
208
+ console.log(...formatArgs(namespace, args));
209
+ }
210
+ }
211
+ },
212
+
213
+ /**
214
+ * Start a collapsed log group (only shown at DEBUG level)
215
+ * @param {string} label - The group label
216
+ * @returns {void}
217
+ */
218
+ group(label) {
219
+ if (shouldLog(LogLevel.DEBUG)) {
220
+ console.group(namespace ? `[${namespace}] ${label}` : label);
221
+ }
222
+ },
223
+
224
+ /**
225
+ * End the current log group
226
+ * @returns {void}
227
+ */
228
+ groupEnd() {
229
+ if (shouldLog(LogLevel.DEBUG)) {
230
+ console.groupEnd();
231
+ }
232
+ },
233
+
234
+ /**
235
+ * Log a message at a custom level
236
+ * @param {number} level - The LogLevel to use
237
+ * @param {...*} args - Values to log
238
+ * @returns {void}
239
+ */
240
+ log(level, ...args) {
241
+ if (shouldLog(level)) {
242
+ const formatted = formatArgs(namespace, args);
243
+ switch (level) {
244
+ case LogLevel.ERROR:
245
+ console.error(...formatted);
246
+ break;
247
+ case LogLevel.WARN:
248
+ console.warn(...formatted);
249
+ break;
250
+ default:
251
+ console.log(...formatted);
252
+ }
253
+ }
254
+ },
255
+
256
+ /**
257
+ * Create a child logger with an additional namespace segment
258
+ * @param {string} childNamespace - The child namespace to append
259
+ * @returns {Logger} A new logger with combined namespace
260
+ * @example
261
+ * const log = createLogger('App');
262
+ * const routerLog = log.child('Router');
263
+ * routerLog.info('Navigate'); // [App:Router] Navigate
264
+ */
265
+ child(childNamespace) {
266
+ const combined = namespace
267
+ ? `${namespace}:${childNamespace}`
268
+ : childNamespace;
269
+ return createLogger(combined, options);
270
+ }
271
+ };
272
+ }
273
+
274
+ /**
275
+ * Default logger instance without namespace
276
+ * @type {Logger}
277
+ * @example
278
+ * import { logger } from './logger.js';
279
+ * logger.info('Application started');
280
+ */
281
+ export const logger = createLogger();
282
+
283
+ /**
284
+ * Pre-configured loggers for common Pulse subsystems
285
+ * @type {Object.<string, Logger>}
286
+ * @property {Logger} pulse - Logger for core reactivity system
287
+ * @property {Logger} dom - Logger for DOM operations
288
+ * @property {Logger} router - Logger for routing
289
+ * @property {Logger} store - Logger for state management
290
+ * @property {Logger} native - Logger for native/mobile features
291
+ * @property {Logger} hmr - Logger for hot module replacement
292
+ * @property {Logger} cli - Logger for CLI tools
293
+ */
294
+ export const loggers = {
295
+ pulse: createLogger('Pulse'),
296
+ dom: createLogger('DOM'),
297
+ router: createLogger('Router'),
298
+ store: createLogger('Store'),
299
+ native: createLogger('Native'),
300
+ hmr: createLogger('HMR'),
301
+ cli: createLogger('CLI')
302
+ };
303
+
304
+ export default logger;
package/runtime/native.js CHANGED
@@ -5,6 +5,9 @@
5
5
  */
6
6
 
7
7
  import { pulse, effect, batch } from './pulse.js';
8
+ import { loggers } from './logger.js';
9
+
10
+ const log = loggers.native;
8
11
 
9
12
  /**
10
13
  * Check if PulseMobile bridge is available
@@ -223,8 +226,8 @@ export const NativeUI = {
223
226
  if (isNativeAvailable()) {
224
227
  return getNative().UI.showToast(message, isLong);
225
228
  }
226
- // Fallback: simple console log
227
- console.log('[Toast]', message);
229
+ // Fallback: log toast message
230
+ log.info('Toast:', message);
228
231
  return Promise.resolve();
229
232
  },
230
233
 
@@ -335,7 +338,7 @@ export function exitApp() {
335
338
  if (isNativeAvailable() && getNative().isAndroid) {
336
339
  return getNative().App.exit();
337
340
  }
338
- console.warn('exitApp is only available on Android');
341
+ log.warn('exitApp is only available on Android');
339
342
  return Promise.resolve();
340
343
  }
341
344
 
@@ -346,7 +349,7 @@ export function minimizeApp() {
346
349
  if (isNativeAvailable()) {
347
350
  return getNative().App.minimize();
348
351
  }
349
- console.warn('minimizeApp is only available in native apps');
352
+ log.warn('minimizeApp is only available in native apps');
350
353
  return Promise.resolve();
351
354
  }
352
355