pulse-js-framework 1.10.4 → 1.11.1
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/README.md +11 -0
- package/cli/build.js +13 -3
- package/compiler/directives.js +356 -0
- package/compiler/lexer.js +18 -3
- package/compiler/parser/core.js +6 -0
- package/compiler/parser/view.js +2 -6
- package/compiler/preprocessor.js +43 -23
- package/compiler/sourcemap.js +3 -1
- package/compiler/transformer/actions.js +329 -0
- package/compiler/transformer/export.js +7 -0
- package/compiler/transformer/expressions.js +85 -33
- package/compiler/transformer/imports.js +3 -0
- package/compiler/transformer/index.js +2 -0
- package/compiler/transformer/store.js +1 -1
- package/compiler/transformer/style.js +45 -16
- package/compiler/transformer/view.js +23 -2
- package/loader/rollup-plugin-server-components.js +391 -0
- package/loader/vite-plugin-server-components.js +420 -0
- package/loader/webpack-loader-server-components.js +356 -0
- package/package.json +124 -82
- package/runtime/async.js +4 -0
- package/runtime/context.js +16 -3
- package/runtime/dom-adapter.js +5 -3
- package/runtime/dom-virtual-list.js +2 -1
- package/runtime/form.js +8 -3
- package/runtime/graphql/cache.js +1 -1
- package/runtime/graphql/client.js +22 -0
- package/runtime/graphql/hooks.js +12 -6
- package/runtime/graphql/subscriptions.js +2 -0
- package/runtime/hmr.js +6 -3
- package/runtime/http.js +1 -0
- package/runtime/i18n.js +2 -0
- package/runtime/lru-cache.js +3 -1
- package/runtime/native.js +46 -20
- package/runtime/pulse.js +3 -0
- package/runtime/router/core.js +5 -1
- package/runtime/router/index.js +17 -1
- package/runtime/router/psc-integration.js +301 -0
- package/runtime/security.js +58 -29
- package/runtime/server-components/actions-server.js +798 -0
- package/runtime/server-components/actions.js +389 -0
- package/runtime/server-components/client.js +447 -0
- package/runtime/server-components/error-sanitizer.js +438 -0
- package/runtime/server-components/index.js +275 -0
- package/runtime/server-components/security-csrf.js +593 -0
- package/runtime/server-components/security-errors.js +227 -0
- package/runtime/server-components/security-ratelimit.js +733 -0
- package/runtime/server-components/security-validation.js +467 -0
- package/runtime/server-components/security.js +598 -0
- package/runtime/server-components/serializer.js +617 -0
- package/runtime/server-components/server.js +382 -0
- package/runtime/server-components/types.js +383 -0
- package/runtime/server-components/utils/mutex.js +60 -0
- package/runtime/server-components/utils/path-sanitizer.js +109 -0
- package/runtime/ssr.js +2 -1
- package/runtime/store.js +19 -10
- package/runtime/utils.js +12 -128
- package/types/animation.d.ts +300 -0
- package/types/i18n.d.ts +283 -0
- package/types/persistence.d.ts +267 -0
- package/types/sse.d.ts +248 -0
- package/types/sw.d.ts +150 -0
- package/runtime/a11y.js.original +0 -1844
- package/runtime/graphql.js.original +0 -1326
- package/runtime/router.js.original +0 -1605
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pulse Server Components - Error Sanitizer
|
|
3
|
+
*
|
|
4
|
+
* Sanitizes error objects before sending to client to prevent sensitive data leakage.
|
|
5
|
+
*
|
|
6
|
+
* Security Protections:
|
|
7
|
+
* 1. Stack Trace Filtering - Removes internal file paths, function names
|
|
8
|
+
* 2. Message Redaction - Strips secrets, connection strings, env vars
|
|
9
|
+
* 3. Production Mode - Returns minimal safe errors in production
|
|
10
|
+
* 4. Context Filtering - Removes sensitive error context properties
|
|
11
|
+
*
|
|
12
|
+
* @module pulse-js-framework/runtime/server-components/error-sanitizer
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { loggers } from '../logger.js';
|
|
16
|
+
import { escapeHtml } from '../security.js';
|
|
17
|
+
|
|
18
|
+
const log = loggers.dom;
|
|
19
|
+
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// Constants - Sensitive Patterns
|
|
22
|
+
// ============================================================================
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Patterns to redact from error messages.
|
|
26
|
+
* Covers secrets, connection strings, file paths, env vars.
|
|
27
|
+
*/
|
|
28
|
+
const SENSITIVE_MESSAGE_PATTERNS = [
|
|
29
|
+
// Connection strings
|
|
30
|
+
/postgres:\/\/[^@]+@[^/]+/gi, // PostgreSQL
|
|
31
|
+
/mongodb(\+srv)?:\/\/[^@]+@[^/]+/gi, // MongoDB
|
|
32
|
+
/mysql:\/\/[^@]+@[^/]+/gi, // MySQL
|
|
33
|
+
/redis:\/\/[^@]+@[^/]+/gi, // Redis
|
|
34
|
+
|
|
35
|
+
// API keys and tokens (common formats)
|
|
36
|
+
/[a-zA-Z0-9_-]{20,}/g, // Generic long tokens
|
|
37
|
+
|
|
38
|
+
// Environment variable values (KEY=value patterns)
|
|
39
|
+
/\b[A-Z_]+=[^\s]+/g,
|
|
40
|
+
|
|
41
|
+
// File paths (Unix and Windows)
|
|
42
|
+
/\/[a-zA-Z0-9_\-./]+\.(js|ts|json|env|config)/gi,
|
|
43
|
+
/[A-Z]:\\[^"\s]+\.(js|ts|json|env|config)/gi,
|
|
44
|
+
|
|
45
|
+
// Email addresses
|
|
46
|
+
/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g,
|
|
47
|
+
|
|
48
|
+
// IP addresses
|
|
49
|
+
/\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Stack trace line patterns to remove entirely.
|
|
54
|
+
* Removes internal framework paths, node_modules, and system paths.
|
|
55
|
+
*/
|
|
56
|
+
const STACK_TRACE_FILTER_PATTERNS = [
|
|
57
|
+
/node_modules/, // Dependencies
|
|
58
|
+
/node:internal/, // Node.js internals
|
|
59
|
+
/runtime\/server-components/, // Internal framework paths (keep only public error location)
|
|
60
|
+
/\/Users\/[^/]+/, // User home directories (macOS/Linux)
|
|
61
|
+
/C:\\Users\\[^\\]+/, // User home directories (Windows)
|
|
62
|
+
/<anonymous>/ // Anonymous functions (often internal)
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Error properties to remove before serialization.
|
|
67
|
+
* These often contain sensitive context or internal state.
|
|
68
|
+
*/
|
|
69
|
+
const SENSITIVE_ERROR_PROPERTIES = [
|
|
70
|
+
'request', // HTTP request object
|
|
71
|
+
'response', // HTTP response object
|
|
72
|
+
'config', // Config objects (may contain secrets)
|
|
73
|
+
'data', // Request/response data
|
|
74
|
+
'headers', // HTTP headers (may contain auth tokens)
|
|
75
|
+
'cookies', // Cookies
|
|
76
|
+
'session', // Session data
|
|
77
|
+
'locals', // Express locals
|
|
78
|
+
'params', // Request params
|
|
79
|
+
'query', // Query strings
|
|
80
|
+
'body', // Request body
|
|
81
|
+
'connection', // Database connections
|
|
82
|
+
'socket', // Network sockets
|
|
83
|
+
'client', // Client objects
|
|
84
|
+
'_originalError' // Original error chains
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
// ============================================================================
|
|
88
|
+
// Mode Detection
|
|
89
|
+
// ============================================================================
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Check if running in production mode.
|
|
93
|
+
*
|
|
94
|
+
* @returns {boolean} True if production
|
|
95
|
+
*/
|
|
96
|
+
export function isProductionMode() {
|
|
97
|
+
return process.env.NODE_ENV === 'production';
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Check if running in development mode.
|
|
102
|
+
*
|
|
103
|
+
* @returns {boolean} True if development
|
|
104
|
+
*/
|
|
105
|
+
export function isDevelopmentMode() {
|
|
106
|
+
return process.env.NODE_ENV === 'development';
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ============================================================================
|
|
110
|
+
// Stack Trace Sanitization
|
|
111
|
+
// ============================================================================
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Sanitize a stack trace by removing internal paths and sensitive info.
|
|
115
|
+
*
|
|
116
|
+
* Filters out:
|
|
117
|
+
* - node_modules paths
|
|
118
|
+
* - User home directories
|
|
119
|
+
* - Internal framework paths
|
|
120
|
+
* - Anonymous function traces
|
|
121
|
+
*
|
|
122
|
+
* Preserves:
|
|
123
|
+
* - User application code paths (relative)
|
|
124
|
+
* - Line numbers
|
|
125
|
+
* - Function names (if not anonymous)
|
|
126
|
+
*
|
|
127
|
+
* @param {string} stack - Raw stack trace
|
|
128
|
+
* @returns {string} Sanitized stack trace
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* const raw = `Error: Test
|
|
132
|
+
* at /Users/alice/project/app.js:10:5
|
|
133
|
+
* at /Users/alice/project/node_modules/express/lib/router.js:42:12`;
|
|
134
|
+
* const sanitized = sanitizeStackTrace(raw);
|
|
135
|
+
* // Returns: "Error: Test\n at app.js:10:5"
|
|
136
|
+
*/
|
|
137
|
+
export function sanitizeStackTrace(stack) {
|
|
138
|
+
if (!stack || typeof stack !== 'string') {
|
|
139
|
+
return '';
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const lines = stack.split('\n');
|
|
143
|
+
const sanitized = [];
|
|
144
|
+
|
|
145
|
+
for (const line of lines) {
|
|
146
|
+
// Keep error message line (first line)
|
|
147
|
+
if (!line.trim().startsWith('at ')) {
|
|
148
|
+
sanitized.push(line);
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// First, strip absolute paths to relative paths
|
|
153
|
+
let sanitizedLine = line;
|
|
154
|
+
|
|
155
|
+
// Replace absolute paths with relative (Unix)
|
|
156
|
+
sanitizedLine = sanitizedLine.replace(/\/[a-zA-Z0-9_\-./]+\/([a-zA-Z0-9_\-./]+\.(js|ts))/g, '$1');
|
|
157
|
+
|
|
158
|
+
// Replace absolute paths with relative (Windows)
|
|
159
|
+
sanitizedLine = sanitizedLine.replace(/[A-Z]:\\[^"]+\\([a-zA-Z0-9_\-./\\]+\.(js|ts))/g, '$1');
|
|
160
|
+
|
|
161
|
+
// NOW filter out internal/sensitive paths (after path conversion)
|
|
162
|
+
let shouldFilter = false;
|
|
163
|
+
for (const pattern of STACK_TRACE_FILTER_PATTERNS) {
|
|
164
|
+
if (pattern.test(sanitizedLine)) {
|
|
165
|
+
shouldFilter = true;
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (shouldFilter) {
|
|
171
|
+
continue; // Skip this line
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
sanitized.push(sanitizedLine);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return sanitized.join('\n');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Limit stack trace to a maximum number of lines.
|
|
182
|
+
*
|
|
183
|
+
* @param {string} stack - Stack trace
|
|
184
|
+
* @param {number} maxLines - Maximum lines to keep (default: 5)
|
|
185
|
+
* @returns {string} Truncated stack trace
|
|
186
|
+
*/
|
|
187
|
+
export function truncateStackTrace(stack, maxLines = 5) {
|
|
188
|
+
if (!stack) {
|
|
189
|
+
return '';
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const lines = stack.split('\n');
|
|
193
|
+
if (lines.length <= maxLines) {
|
|
194
|
+
return stack;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const truncated = lines.slice(0, maxLines);
|
|
198
|
+
truncated.push(` ... (${lines.length - maxLines} more lines)`);
|
|
199
|
+
return truncated.join('\n');
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// ============================================================================
|
|
203
|
+
// Message Sanitization
|
|
204
|
+
// ============================================================================
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Sanitize an error message by redacting sensitive patterns.
|
|
208
|
+
*
|
|
209
|
+
* Redacts:
|
|
210
|
+
* - Connection strings (postgres://, mongodb://, etc.)
|
|
211
|
+
* - API keys and tokens
|
|
212
|
+
* - File paths
|
|
213
|
+
* - Email addresses
|
|
214
|
+
* - IP addresses
|
|
215
|
+
* - Environment variables
|
|
216
|
+
*
|
|
217
|
+
* @param {string} message - Raw error message
|
|
218
|
+
* @returns {string} Sanitized message
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* const raw = 'Connection failed: postgres://user:pass@localhost/db';
|
|
222
|
+
* const sanitized = sanitizeErrorMessage(raw);
|
|
223
|
+
* // Returns: 'Connection failed: [REDACTED]'
|
|
224
|
+
*/
|
|
225
|
+
export function sanitizeErrorMessage(message) {
|
|
226
|
+
if (!message || typeof message !== 'string') {
|
|
227
|
+
return '';
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
let sanitized = message;
|
|
231
|
+
|
|
232
|
+
// Replace each sensitive pattern with [REDACTED]
|
|
233
|
+
for (const pattern of SENSITIVE_MESSAGE_PATTERNS) {
|
|
234
|
+
sanitized = sanitized.replace(pattern, '[REDACTED]');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return sanitized;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// ============================================================================
|
|
241
|
+
// Error Object Sanitization
|
|
242
|
+
// ============================================================================
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Sanitization options.
|
|
246
|
+
*
|
|
247
|
+
* @typedef {Object} SanitizationOptions
|
|
248
|
+
* @property {'production'|'development'|'test'} [mode] - Override mode detection
|
|
249
|
+
* @property {boolean} [includeStack=true] - Include stack trace (sanitized)
|
|
250
|
+
* @property {number} [maxStackLines=5] - Max stack trace lines
|
|
251
|
+
* @property {boolean} [redactMessages=true] - Redact sensitive patterns in messages
|
|
252
|
+
* @property {string[]} [allowedProperties] - Whitelist of error properties to keep
|
|
253
|
+
*/
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Sanitize an error object for safe transmission to client.
|
|
257
|
+
*
|
|
258
|
+
* Removes:
|
|
259
|
+
* - Sensitive error properties (request, config, headers, etc.)
|
|
260
|
+
* - Internal stack traces
|
|
261
|
+
* - Secrets from error messages
|
|
262
|
+
*
|
|
263
|
+
* Preserves:
|
|
264
|
+
* - Error type/name
|
|
265
|
+
* - Sanitized message
|
|
266
|
+
* - Sanitized stack (if enabled)
|
|
267
|
+
* - Error code (if present)
|
|
268
|
+
* - Allowed custom properties
|
|
269
|
+
*
|
|
270
|
+
* @param {Error} error - Error object to sanitize
|
|
271
|
+
* @param {SanitizationOptions} [options] - Sanitization options
|
|
272
|
+
* @returns {Object} Sanitized error object (plain object, not Error instance)
|
|
273
|
+
*
|
|
274
|
+
* @example
|
|
275
|
+
* try {
|
|
276
|
+
* await db.connect('postgres://user:secret@localhost/db');
|
|
277
|
+
* } catch (error) {
|
|
278
|
+
* const safe = sanitizeError(error);
|
|
279
|
+
* res.json({ error: safe });
|
|
280
|
+
* }
|
|
281
|
+
* // Client receives: { name: 'Error', message: 'Connection failed: [REDACTED]' }
|
|
282
|
+
*/
|
|
283
|
+
export function sanitizeError(error, options = {}) {
|
|
284
|
+
const {
|
|
285
|
+
mode = isProductionMode() ? 'production' : 'development',
|
|
286
|
+
includeStack = mode === 'development',
|
|
287
|
+
maxStackLines = 5,
|
|
288
|
+
redactMessages = true,
|
|
289
|
+
allowedProperties = []
|
|
290
|
+
} = options;
|
|
291
|
+
|
|
292
|
+
// Base safe error
|
|
293
|
+
const safe = {
|
|
294
|
+
name: error.name || 'Error',
|
|
295
|
+
message: redactMessages ? sanitizeErrorMessage(error.message) : error.message
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
// Add error code if present (usually safe)
|
|
299
|
+
if (error.code) {
|
|
300
|
+
safe.code = error.code;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Add stack trace (development only, sanitized)
|
|
304
|
+
if (includeStack && error.stack) {
|
|
305
|
+
let stack = sanitizeStackTrace(error.stack);
|
|
306
|
+
if (maxStackLines > 0) {
|
|
307
|
+
stack = truncateStackTrace(stack, maxStackLines);
|
|
308
|
+
}
|
|
309
|
+
safe.stack = stack;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Add allowed custom properties (whitelist only)
|
|
313
|
+
for (const prop of allowedProperties) {
|
|
314
|
+
if (error[prop] !== undefined && !SENSITIVE_ERROR_PROPERTIES.includes(prop)) {
|
|
315
|
+
safe[prop] = error[prop];
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Special handling for Pulse errors (preserve suggestion, context if safe)
|
|
320
|
+
if (error.suggestion && typeof error.suggestion === 'string') {
|
|
321
|
+
safe.suggestion = error.suggestion;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (error.context && typeof error.context === 'string') {
|
|
325
|
+
safe.context = redactMessages ? sanitizeErrorMessage(error.context) : error.context;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return safe;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Create a production-safe error with minimal information.
|
|
333
|
+
*
|
|
334
|
+
* Returns a generic error message without any potentially sensitive details.
|
|
335
|
+
* Use this for critical errors where you want to hide all implementation details.
|
|
336
|
+
*
|
|
337
|
+
* @param {Error} error - Original error
|
|
338
|
+
* @param {string} [genericMessage='An error occurred'] - Generic message
|
|
339
|
+
* @returns {Object} Minimal safe error object
|
|
340
|
+
*
|
|
341
|
+
* @example
|
|
342
|
+
* try {
|
|
343
|
+
* await processPayment(secretKey, card);
|
|
344
|
+
* } catch (error) {
|
|
345
|
+
* const safe = createProductionSafeError(error, 'Payment processing failed');
|
|
346
|
+
* res.status(500).json({ error: safe });
|
|
347
|
+
* }
|
|
348
|
+
* // Client receives: { name: 'Error', message: 'Payment processing failed' }
|
|
349
|
+
*/
|
|
350
|
+
export function createProductionSafeError(error, genericMessage = 'An error occurred') {
|
|
351
|
+
// Log original error server-side (for debugging)
|
|
352
|
+
log.error('Production error (details hidden from client):', error);
|
|
353
|
+
|
|
354
|
+
return {
|
|
355
|
+
name: 'Error',
|
|
356
|
+
message: genericMessage
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Sanitize an array of errors (for aggregate errors, validation errors, etc.).
|
|
362
|
+
*
|
|
363
|
+
* @param {Error[]} errors - Array of errors
|
|
364
|
+
* @param {SanitizationOptions} [options] - Sanitization options
|
|
365
|
+
* @returns {Object[]} Array of sanitized errors
|
|
366
|
+
*
|
|
367
|
+
* @example
|
|
368
|
+
* const validationErrors = [
|
|
369
|
+
* new Error('Invalid email'),
|
|
370
|
+
* new Error('Password too short')
|
|
371
|
+
* ];
|
|
372
|
+
* const safe = sanitizeErrors(validationErrors);
|
|
373
|
+
*/
|
|
374
|
+
export function sanitizeErrors(errors, options = {}) {
|
|
375
|
+
if (!Array.isArray(errors)) {
|
|
376
|
+
return [];
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return errors.map(error => sanitizeError(error, options));
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// ============================================================================
|
|
383
|
+
// Validation Error Sanitization
|
|
384
|
+
// ============================================================================
|
|
385
|
+
|
|
386
|
+
// escapeHtml imported from ../security.js (single source of truth)
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Sanitize validation errors specifically.
|
|
390
|
+
* Validation errors are usually safe, but may contain user input that needs XSS sanitization.
|
|
391
|
+
*
|
|
392
|
+
* @param {Object} validationErrors - Validation errors object (key: fieldName, value: error)
|
|
393
|
+
* @returns {Object} Sanitized validation errors
|
|
394
|
+
*
|
|
395
|
+
* @example
|
|
396
|
+
* const errors = {
|
|
397
|
+
* email: 'Invalid email: <script>alert(1)</script>',
|
|
398
|
+
* password: 'Too short'
|
|
399
|
+
* };
|
|
400
|
+
* const safe = sanitizeValidationErrors(errors);
|
|
401
|
+
* // { email: 'Invalid email: <script>alert(1)</script>', password: 'Too short' }
|
|
402
|
+
*/
|
|
403
|
+
export function sanitizeValidationErrors(validationErrors) {
|
|
404
|
+
if (!validationErrors || typeof validationErrors !== 'object') {
|
|
405
|
+
return {};
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const sanitized = {};
|
|
409
|
+
|
|
410
|
+
for (const [field, error] of Object.entries(validationErrors)) {
|
|
411
|
+
if (typeof error === 'string') {
|
|
412
|
+
// Escape HTML/XSS first, then redact sensitive patterns
|
|
413
|
+
sanitized[field] = sanitizeErrorMessage(escapeHtml(error));
|
|
414
|
+
} else if (error instanceof Error) {
|
|
415
|
+
sanitized[field] = sanitizeError(error, { includeStack: false }).message;
|
|
416
|
+
} else {
|
|
417
|
+
sanitized[field] = String(error);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
return sanitized;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// ============================================================================
|
|
425
|
+
// Exports
|
|
426
|
+
// ============================================================================
|
|
427
|
+
|
|
428
|
+
export default {
|
|
429
|
+
sanitizeError,
|
|
430
|
+
sanitizeErrors,
|
|
431
|
+
sanitizeStackTrace,
|
|
432
|
+
truncateStackTrace,
|
|
433
|
+
sanitizeErrorMessage,
|
|
434
|
+
sanitizeValidationErrors,
|
|
435
|
+
createProductionSafeError,
|
|
436
|
+
isProductionMode,
|
|
437
|
+
isDevelopmentMode
|
|
438
|
+
};
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pulse Server Components (PSC)
|
|
3
|
+
*
|
|
4
|
+
* Main entry point for Server Components functionality.
|
|
5
|
+
* Re-exports all public APIs from submodules.
|
|
6
|
+
*
|
|
7
|
+
* @module pulse-js-framework/runtime/server-components
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* // Server-side
|
|
11
|
+
* import { serializeToPSC, renderServerComponent } from 'pulse-js-framework/runtime/server-components';
|
|
12
|
+
*
|
|
13
|
+
* const node = await renderServerComponent(MyComponent, props);
|
|
14
|
+
* const payload = serializeToPSC(node, { clientManifest });
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* // Client-side
|
|
18
|
+
* import { reconstructPSCTree, loadClientComponent } from 'pulse-js-framework/runtime/server-components';
|
|
19
|
+
*
|
|
20
|
+
* const payload = await fetch('/api/component').then(r => r.json());
|
|
21
|
+
* const domTree = await reconstructPSCTree(payload);
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
// ============================================================================
|
|
25
|
+
// Type Definitions & Constants
|
|
26
|
+
// ============================================================================
|
|
27
|
+
|
|
28
|
+
export {
|
|
29
|
+
// Type guards
|
|
30
|
+
isPSCElement,
|
|
31
|
+
isPSCText,
|
|
32
|
+
isPSCClientBoundary,
|
|
33
|
+
isPSCFragment,
|
|
34
|
+
isPSCComment,
|
|
35
|
+
// Validation
|
|
36
|
+
validatePSCPayload,
|
|
37
|
+
validatePSCNode,
|
|
38
|
+
// Constants
|
|
39
|
+
PSCNodeType,
|
|
40
|
+
PSC_VERSION
|
|
41
|
+
} from './types.js';
|
|
42
|
+
|
|
43
|
+
// ============================================================================
|
|
44
|
+
// Serialization (Server-Side)
|
|
45
|
+
// ============================================================================
|
|
46
|
+
|
|
47
|
+
export {
|
|
48
|
+
// Core serialization
|
|
49
|
+
serializeNode,
|
|
50
|
+
serializeToPSC,
|
|
51
|
+
// Client boundary detection
|
|
52
|
+
isClientBoundary,
|
|
53
|
+
markClientBoundary,
|
|
54
|
+
// Constants
|
|
55
|
+
CLIENT_BOUNDARY_ATTR,
|
|
56
|
+
CLIENT_PROPS_ATTR
|
|
57
|
+
} from './serializer.js';
|
|
58
|
+
|
|
59
|
+
// ============================================================================
|
|
60
|
+
// Reconstruction (Client-Side)
|
|
61
|
+
// ============================================================================
|
|
62
|
+
|
|
63
|
+
export {
|
|
64
|
+
// Core reconstruction
|
|
65
|
+
reconstructNode,
|
|
66
|
+
reconstructPSCTree,
|
|
67
|
+
// Client component loading
|
|
68
|
+
loadClientComponent,
|
|
69
|
+
preloadClientComponent,
|
|
70
|
+
// Hydration
|
|
71
|
+
hydrateClientComponents,
|
|
72
|
+
// Cache management
|
|
73
|
+
clearComponentCache,
|
|
74
|
+
getComponentCacheStats
|
|
75
|
+
} from './client.js';
|
|
76
|
+
|
|
77
|
+
// ============================================================================
|
|
78
|
+
// Server Rendering Helpers
|
|
79
|
+
// ============================================================================
|
|
80
|
+
|
|
81
|
+
export {
|
|
82
|
+
// Component rendering
|
|
83
|
+
renderServerComponent,
|
|
84
|
+
executeAsyncComponent,
|
|
85
|
+
// Boundary detection
|
|
86
|
+
markClientBoundaries,
|
|
87
|
+
// SSR integration
|
|
88
|
+
renderServerComponentToHTML,
|
|
89
|
+
// Component registry
|
|
90
|
+
componentRegistry,
|
|
91
|
+
createComponentRegistry
|
|
92
|
+
} from './server.js';
|
|
93
|
+
|
|
94
|
+
// ============================================================================
|
|
95
|
+
// Server Actions (RPC Mechanism)
|
|
96
|
+
// ============================================================================
|
|
97
|
+
|
|
98
|
+
export {
|
|
99
|
+
// Client-side actions
|
|
100
|
+
registerAction,
|
|
101
|
+
createActionInvoker,
|
|
102
|
+
useServerAction,
|
|
103
|
+
bindFormAction,
|
|
104
|
+
getActionConfig,
|
|
105
|
+
clearActionRegistry
|
|
106
|
+
} from './actions.js';
|
|
107
|
+
|
|
108
|
+
export {
|
|
109
|
+
// Server-side actions
|
|
110
|
+
registerServerAction,
|
|
111
|
+
executeServerAction,
|
|
112
|
+
getServerActions,
|
|
113
|
+
clearServerActions,
|
|
114
|
+
// Middleware
|
|
115
|
+
createServerActionMiddleware,
|
|
116
|
+
createFastifyActionPlugin,
|
|
117
|
+
createHonoActionMiddleware,
|
|
118
|
+
// CSRF helpers
|
|
119
|
+
generateCSRFTokenForResponse,
|
|
120
|
+
getGlobalCSRFStore,
|
|
121
|
+
setCSRFStore
|
|
122
|
+
} from './actions-server.js';
|
|
123
|
+
|
|
124
|
+
// ============================================================================
|
|
125
|
+
// Security (Prop Validation & Error Sanitization)
|
|
126
|
+
// ============================================================================
|
|
127
|
+
|
|
128
|
+
export {
|
|
129
|
+
// Secret detection
|
|
130
|
+
detectSecrets,
|
|
131
|
+
// XSS sanitization
|
|
132
|
+
sanitizePropsForXSS,
|
|
133
|
+
// Size validation
|
|
134
|
+
validatePropSizeLimits,
|
|
135
|
+
PROP_SIZE_LIMITS,
|
|
136
|
+
// Main validator
|
|
137
|
+
validatePropSecurity
|
|
138
|
+
} from './security.js';
|
|
139
|
+
|
|
140
|
+
export {
|
|
141
|
+
// Serialization validation (NEW)
|
|
142
|
+
detectNonSerializable,
|
|
143
|
+
detectEnvironmentVariables,
|
|
144
|
+
validatePropSerialization
|
|
145
|
+
} from './security-validation.js';
|
|
146
|
+
|
|
147
|
+
export {
|
|
148
|
+
// Security error classes
|
|
149
|
+
PSCSecurityError,
|
|
150
|
+
PSCSerializationError,
|
|
151
|
+
PSCEnvVarError,
|
|
152
|
+
PSCCSRFError,
|
|
153
|
+
PSCRateLimitError
|
|
154
|
+
} from './security-errors.js';
|
|
155
|
+
|
|
156
|
+
export {
|
|
157
|
+
// Error sanitization
|
|
158
|
+
sanitizeError,
|
|
159
|
+
sanitizeErrors,
|
|
160
|
+
sanitizeStackTrace,
|
|
161
|
+
truncateStackTrace,
|
|
162
|
+
sanitizeErrorMessage,
|
|
163
|
+
sanitizeValidationErrors,
|
|
164
|
+
createProductionSafeError,
|
|
165
|
+
// Mode detection
|
|
166
|
+
isProductionMode,
|
|
167
|
+
isDevelopmentMode
|
|
168
|
+
} from './error-sanitizer.js';
|
|
169
|
+
|
|
170
|
+
// ============================================================================
|
|
171
|
+
// CSRF Protection
|
|
172
|
+
// ============================================================================
|
|
173
|
+
|
|
174
|
+
export {
|
|
175
|
+
// CSRF token management
|
|
176
|
+
CSRFTokenStore,
|
|
177
|
+
generateCSRFToken,
|
|
178
|
+
validateCSRFToken,
|
|
179
|
+
createCSRFMiddleware
|
|
180
|
+
} from './security-csrf.js';
|
|
181
|
+
|
|
182
|
+
// ============================================================================
|
|
183
|
+
// Rate Limiting
|
|
184
|
+
// ============================================================================
|
|
185
|
+
|
|
186
|
+
export {
|
|
187
|
+
// Rate limiter
|
|
188
|
+
RateLimiter,
|
|
189
|
+
RateLimitStore,
|
|
190
|
+
MemoryRateLimitStore,
|
|
191
|
+
RedisRateLimitStore,
|
|
192
|
+
createRateLimitMiddleware
|
|
193
|
+
} from './security-ratelimit.js';
|
|
194
|
+
|
|
195
|
+
// ============================================================================
|
|
196
|
+
// Default Export (import everything first for bundle)
|
|
197
|
+
// ============================================================================
|
|
198
|
+
|
|
199
|
+
import * as types from './types.js';
|
|
200
|
+
import * as serializer from './serializer.js';
|
|
201
|
+
import * as client from './client.js';
|
|
202
|
+
import * as server from './server.js';
|
|
203
|
+
import * as actions from './actions.js';
|
|
204
|
+
import * as actionsServer from './actions-server.js';
|
|
205
|
+
import * as security from './security.js';
|
|
206
|
+
import * as securityValidation from './security-validation.js';
|
|
207
|
+
import * as securityErrors from './security-errors.js';
|
|
208
|
+
import * as errorSanitizer from './error-sanitizer.js';
|
|
209
|
+
import * as csrf from './security-csrf.js';
|
|
210
|
+
import * as ratelimit from './security-ratelimit.js';
|
|
211
|
+
|
|
212
|
+
export default {
|
|
213
|
+
// Types
|
|
214
|
+
PSCNodeType: types.PSCNodeType,
|
|
215
|
+
PSC_VERSION: types.PSC_VERSION,
|
|
216
|
+
// Serialization
|
|
217
|
+
serializeNode: serializer.serializeNode,
|
|
218
|
+
serializeToPSC: serializer.serializeToPSC,
|
|
219
|
+
isClientBoundary: serializer.isClientBoundary,
|
|
220
|
+
markClientBoundary: serializer.markClientBoundary,
|
|
221
|
+
// Reconstruction
|
|
222
|
+
reconstructNode: client.reconstructNode,
|
|
223
|
+
reconstructPSCTree: client.reconstructPSCTree,
|
|
224
|
+
loadClientComponent: client.loadClientComponent,
|
|
225
|
+
preloadClientComponent: client.preloadClientComponent,
|
|
226
|
+
hydrateClientComponents: client.hydrateClientComponents,
|
|
227
|
+
// Server
|
|
228
|
+
renderServerComponent: server.renderServerComponent,
|
|
229
|
+
executeAsyncComponent: server.executeAsyncComponent,
|
|
230
|
+
markClientBoundaries: server.markClientBoundaries,
|
|
231
|
+
renderServerComponentToHTML: server.renderServerComponentToHTML,
|
|
232
|
+
componentRegistry: server.componentRegistry,
|
|
233
|
+
createComponentRegistry: server.createComponentRegistry,
|
|
234
|
+
// Actions (client)
|
|
235
|
+
registerAction: actions.registerAction,
|
|
236
|
+
createActionInvoker: actions.createActionInvoker,
|
|
237
|
+
useServerAction: actions.useServerAction,
|
|
238
|
+
bindFormAction: actions.bindFormAction,
|
|
239
|
+
// Actions (server)
|
|
240
|
+
registerServerAction: actionsServer.registerServerAction,
|
|
241
|
+
executeServerAction: actionsServer.executeServerAction,
|
|
242
|
+
createServerActionMiddleware: actionsServer.createServerActionMiddleware,
|
|
243
|
+
generateCSRFTokenForResponse: actionsServer.generateCSRFTokenForResponse,
|
|
244
|
+
getGlobalCSRFStore: actionsServer.getGlobalCSRFStore,
|
|
245
|
+
setCSRFStore: actionsServer.setCSRFStore,
|
|
246
|
+
// Security
|
|
247
|
+
validatePropSecurity: security.validatePropSecurity,
|
|
248
|
+
detectSecrets: security.detectSecrets,
|
|
249
|
+
sanitizePropsForXSS: security.sanitizePropsForXSS,
|
|
250
|
+
// Serialization validation
|
|
251
|
+
detectNonSerializable: securityValidation.detectNonSerializable,
|
|
252
|
+
detectEnvironmentVariables: securityValidation.detectEnvironmentVariables,
|
|
253
|
+
validatePropSerialization: securityValidation.validatePropSerialization,
|
|
254
|
+
// Security errors
|
|
255
|
+
PSCSecurityError: securityErrors.PSCSecurityError,
|
|
256
|
+
PSCSerializationError: securityErrors.PSCSerializationError,
|
|
257
|
+
PSCEnvVarError: securityErrors.PSCEnvVarError,
|
|
258
|
+
PSCCSRFError: securityErrors.PSCCSRFError,
|
|
259
|
+
PSCRateLimitError: securityErrors.PSCRateLimitError,
|
|
260
|
+
// CSRF protection
|
|
261
|
+
CSRFTokenStore: csrf.CSRFTokenStore,
|
|
262
|
+
generateCSRFToken: csrf.generateCSRFToken,
|
|
263
|
+
validateCSRFToken: csrf.validateCSRFToken,
|
|
264
|
+
createCSRFMiddleware: csrf.createCSRFMiddleware,
|
|
265
|
+
// Rate limiting
|
|
266
|
+
RateLimiter: ratelimit.RateLimiter,
|
|
267
|
+
RateLimitStore: ratelimit.RateLimitStore,
|
|
268
|
+
MemoryRateLimitStore: ratelimit.MemoryRateLimitStore,
|
|
269
|
+
RedisRateLimitStore: ratelimit.RedisRateLimitStore,
|
|
270
|
+
createRateLimitMiddleware: ratelimit.createRateLimitMiddleware,
|
|
271
|
+
// Error sanitization
|
|
272
|
+
sanitizeError: errorSanitizer.sanitizeError,
|
|
273
|
+
sanitizeStackTrace: errorSanitizer.sanitizeStackTrace,
|
|
274
|
+
sanitizeErrorMessage: errorSanitizer.sanitizeErrorMessage
|
|
275
|
+
};
|