@weaverse/react 5.17.0 → 5.18.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.
@@ -0,0 +1,544 @@
1
+ // Data connector utilities for replacing placeholders with real data
2
+
3
+ /** Route-keyed or flat data available when resolving content placeholders. */
4
+ export type DataContext = Record<string, unknown>
5
+
6
+ // Cached regex pattern for better performance
7
+ const TEMPLATE_REGEX = /\{\{([^}]+)\}\}/g
8
+ const ARRAY_INDEX_REGEX = /^(\w+)\[(\d+)\]$/
9
+
10
+ // Cache eviction ratio - remove oldest 25% when cache is full
11
+ const CACHE_EVICTION_RATIO = 0.25
12
+
13
+ /**
14
+ * Validates that a property name is safe to access (prevents prototype pollution)
15
+ * Enhanced to handle route-specific patterns with special characters
16
+ */
17
+ function isSafePropertyName(key: string): boolean {
18
+ // Block dangerous prototype pollution patterns
19
+ const dangerousPatterns = ['__proto__', 'constructor', 'prototype']
20
+ return !dangerousPatterns.includes(key)
21
+ }
22
+
23
+ /**
24
+ * Parses a template path into route key and property path components
25
+ * Handles complex routes like: routes/($locale).blogs.$blogHandle.$articleHandle
26
+ * Enhanced with caching for improved performance
27
+ *
28
+ * @param path - The full path from template (e.g., "routes/($locale)._index.weaverseData.page.name")
29
+ * @param dataContext - Available route data context
30
+ * @returns Object with routeKey and propertyPath
31
+ */
32
+ function parseRouteAndProperty(
33
+ path: string,
34
+ dataContext: DataContext
35
+ ): {
36
+ routeKey: string | null
37
+ propertyPath: string
38
+ } {
39
+ if (!dataContext || typeof path !== 'string') {
40
+ return { routeKey: null, propertyPath: path }
41
+ }
42
+
43
+ const trimmedPath = path.trim()
44
+
45
+ // Create a cache key using stable hash instead of serializing route keys
46
+ const dataHash = getDataContextHash(dataContext)
47
+ const cacheKey = `${trimmedPath}::${dataHash}`
48
+
49
+ // Check cache first
50
+ const cachedResult = routeParsingCache.get(cacheKey)
51
+ if (cachedResult) {
52
+ // Update timestamp on cache hit
53
+ cachedResult.timestamp = Date.now()
54
+ return {
55
+ routeKey: cachedResult.routeKey,
56
+ propertyPath: cachedResult.propertyPath,
57
+ }
58
+ }
59
+
60
+ // Get all available routes and sort by length (longest first for specificity)
61
+ const availableRoutes = Object.keys(dataContext).sort(
62
+ (a, b) => b.length - a.length
63
+ )
64
+
65
+ let result: { routeKey: string | null; propertyPath: string } = {
66
+ routeKey: null,
67
+ propertyPath: trimmedPath,
68
+ }
69
+
70
+ // Try to find the longest matching route key
71
+ let found = false
72
+ for (const routeKey of availableRoutes) {
73
+ // Exact match
74
+ if (trimmedPath === routeKey) {
75
+ result = { routeKey, propertyPath: '' }
76
+ found = true
77
+ break
78
+ }
79
+
80
+ // Route key with property path
81
+ if (trimmedPath.startsWith(`${routeKey}.`)) {
82
+ const propertyPath = trimmedPath.slice(routeKey.length + 1)
83
+ result = { routeKey, propertyPath }
84
+ found = true
85
+ break
86
+ }
87
+ }
88
+
89
+ if (!found) {
90
+ // No route key found, treat entire path as property path
91
+ result = { routeKey: null, propertyPath: trimmedPath }
92
+ }
93
+
94
+ // Cache the result
95
+ if (routeParsingCache.size >= ROUTE_PARSING_CACHE_MAX_SIZE) {
96
+ // Clean cache by removing oldest entries
97
+ const entries = Array.from(routeParsingCache.entries())
98
+ entries.sort((a, b) => a[1].timestamp - b[1].timestamp)
99
+ const toRemove = Math.floor(entries.length * CACHE_EVICTION_RATIO)
100
+ for (let i = 0; i < toRemove; i++) {
101
+ routeParsingCache.delete(entries[i][0])
102
+ }
103
+ }
104
+
105
+ routeParsingCache.set(cacheKey, {
106
+ routeKey: result.routeKey,
107
+ propertyPath: result.propertyPath,
108
+ timestamp: Date.now(),
109
+ })
110
+
111
+ return result
112
+ }
113
+
114
+ // Simple cache for parsed templates (max 100 entries)
115
+ const templateCache = new Map<string, string>()
116
+ const MAX_CACHE_SIZE = 100
117
+
118
+ // Cache for fallback lookups to improve performance (moved to module level to prevent memory leaks)
119
+ const fallbackCache = new Map<
120
+ string,
121
+ { routeKey: string; value: unknown; timestamp: number }
122
+ >()
123
+ const FALLBACK_CACHE_MAX_SIZE = 50
124
+
125
+ // Route parsing cache for performance optimization
126
+ const routeParsingCache = new Map<
127
+ string,
128
+ { routeKey: string | null; propertyPath: string; timestamp: number }
129
+ >()
130
+ const ROUTE_PARSING_CACHE_MAX_SIZE = 100
131
+
132
+ // WeakMap for stable dataContext hashing - prevents repeated JSON.stringify
133
+ const dataContextHashCache = new WeakMap<object, string>()
134
+ let hashCounter = 0
135
+
136
+ /**
137
+ * Safely gets a nested value from an object using dot notation path
138
+ * Supports array indexing with [index] syntax
139
+ * @param obj - The object to traverse
140
+ * @param path - Dot notation path (e.g., "user.name" or "items[0].title")
141
+ * @param visited - Set to track visited objects for circular reference protection
142
+ * @returns The resolved value or undefined if not found
143
+ */
144
+ function getNestedValue(
145
+ obj: unknown,
146
+ path: string,
147
+ visited = new WeakSet()
148
+ ): unknown {
149
+ return path.split('.').reduce((current, key) => {
150
+ if (!current || typeof current !== 'object') {
151
+ return
152
+ }
153
+
154
+ // Protect against circular references
155
+ if (visited.has(current as WeakKey)) {
156
+ console.warn('Circular reference detected in data connector path:', path)
157
+ return
158
+ }
159
+
160
+ // Add to circular reference tracking
161
+ visited.add(current as WeakKey)
162
+
163
+ // Handle array indexing like items[0] or items[1]
164
+ const arrayMatch = key.match(ARRAY_INDEX_REGEX)
165
+ if (arrayMatch) {
166
+ const [, arrayKey, index] = arrayMatch
167
+ const arrayObj = (current as Record<string, unknown>)[arrayKey]
168
+ if (Array.isArray(arrayObj)) {
169
+ const indexNum = Number.parseInt(index, 10)
170
+ if (indexNum >= 0 && indexNum < arrayObj.length) {
171
+ return arrayObj[indexNum]
172
+ }
173
+ }
174
+ return
175
+ }
176
+
177
+ // Handle regular object property access with security validation
178
+ if (!isSafePropertyName(key)) {
179
+ console.warn(`Unsafe property access attempted: ${key}`)
180
+ return
181
+ }
182
+
183
+ return current !== null && key in current
184
+ ? (current as Record<string, unknown>)[key]
185
+ : undefined
186
+ }, obj)
187
+ }
188
+
189
+ /**
190
+ * Sanitizes a string value to prevent XSS attacks
191
+ * @param value - The value to sanitize
192
+ * @returns Sanitized string safe for HTML rendering
193
+ */
194
+ function sanitizeValue(value: unknown): string {
195
+ if (value === null || value === undefined) {
196
+ return ''
197
+ }
198
+
199
+ const stringValue = String(value)
200
+
201
+ // Basic HTML entity encoding to prevent XSS
202
+ return stringValue
203
+ .replace(/&/g, '&amp;')
204
+ .replace(/</g, '&lt;')
205
+ .replace(/>/g, '&gt;')
206
+ .replace(/"/g, '&quot;')
207
+ .replace(/'/g, '&#x27;')
208
+ .replace(/\//g, '&#x2F;')
209
+ }
210
+
211
+ /**
212
+ * Creates a stable hash for dataContext object
213
+ * Uses WeakMap to cache hash per object reference (no serialization needed)
214
+ */
215
+ function getDataContextHash(data: Record<string, unknown>): string {
216
+ if (!dataContextHashCache.has(data)) {
217
+ // Generate unique hash for this dataContext instance
218
+ dataContextHashCache.set(data, `ctx_${++hashCounter}`)
219
+ }
220
+ const hash = dataContextHashCache.get(data)
221
+ return hash || `ctx_${++hashCounter}`
222
+ }
223
+
224
+ /**
225
+ * Creates a safe cache key from content and data
226
+ * Uses stable hashing instead of JSON.stringify for massive performance gain
227
+ */
228
+ function createCacheKey(
229
+ content: string,
230
+ data: Record<string, unknown>
231
+ ): string {
232
+ // Use stable hash instead of expensive JSON.stringify
233
+ const dataHash = getDataContextHash(data)
234
+ return `${content}:${dataHash}`
235
+ }
236
+
237
+ /**
238
+ * Resolves data from the flat route-keyed data context
239
+ * Enhanced to support complex route patterns with special characters
240
+ * Supports: {{root.layout.shop.name}}, {{routes/($locale)._index.data}}, {{routes/($locale).blogs.$handle.$article.content}}
241
+ */
242
+ function resolveDataFromContext(
243
+ path: string,
244
+ dataContext: DataContext | null | undefined
245
+ ): unknown {
246
+ if (!dataContext) {
247
+ return
248
+ }
249
+
250
+ const trimmedPath = path.trim()
251
+
252
+ // Use intelligent route parsing to separate route key from property path
253
+ const { routeKey, propertyPath } = parseRouteAndProperty(
254
+ trimmedPath,
255
+ dataContext
256
+ )
257
+
258
+ // If we found a specific route key, resolve from that route's data
259
+ if (routeKey && dataContext[routeKey]) {
260
+ const routeData = dataContext[routeKey]
261
+ if (typeof routeData === 'object' && routeData !== null) {
262
+ if (propertyPath) {
263
+ return getNestedValue(routeData, propertyPath)
264
+ }
265
+ return routeData
266
+ }
267
+ }
268
+
269
+ // Fallback: search across all route data for backward compatibility
270
+ // This handles cases like {{shop.name}} without route prefix
271
+
272
+ // Check cache first
273
+ const cachedResult = fallbackCache.get(trimmedPath)
274
+ if (cachedResult && dataContext[cachedResult.routeKey]) {
275
+ const value = getNestedValue(
276
+ dataContext[cachedResult.routeKey],
277
+ trimmedPath
278
+ )
279
+ if (value !== undefined) {
280
+ // Update timestamp on cache hit
281
+ cachedResult.timestamp = Date.now()
282
+ return value
283
+ }
284
+ }
285
+
286
+ // Search with priority order (most common routes first)
287
+ const priorityRoutes = ['root', 'routes/product', 'routes/collection']
288
+ const allRoutes = [
289
+ ...priorityRoutes,
290
+ ...Object.keys(dataContext).filter((k) => !priorityRoutes.includes(k)),
291
+ ]
292
+
293
+ for (const routeKey of allRoutes) {
294
+ const routeData = dataContext[routeKey]
295
+ if (routeData && typeof routeData === 'object' && routeData !== null) {
296
+ const value = getNestedValue(routeData, trimmedPath)
297
+ if (value !== undefined) {
298
+ // Clean cache if it's getting too large
299
+ if (fallbackCache.size >= FALLBACK_CACHE_MAX_SIZE) {
300
+ const entries = Array.from(fallbackCache.entries())
301
+ entries.sort((a, b) => a[1].timestamp - b[1].timestamp)
302
+ // Remove oldest 25% of entries
303
+ const toRemove = Math.floor(entries.length * CACHE_EVICTION_RATIO)
304
+ for (let i = 0; i < toRemove; i++) {
305
+ fallbackCache.delete(entries[i][0])
306
+ }
307
+ }
308
+ fallbackCache.set(trimmedPath, {
309
+ routeKey,
310
+ value,
311
+ timestamp: Date.now(),
312
+ })
313
+ return value
314
+ }
315
+ }
316
+ }
317
+
318
+ // Final fallback: try direct access (for non-route-keyed legacy data)
319
+ return getNestedValue(dataContext, trimmedPath)
320
+ }
321
+
322
+ /**
323
+ * Replaces data connector placeholders in content with actual values from data context
324
+ * Supports {{path}} syntax with dot notation, array indexing, and route prefixes
325
+ *
326
+ * Enhanced syntax:
327
+ * - {{root.siteSettings.title}} - Access root route data
328
+ * - {{layout.navigation.items}} - Access layout route data
329
+ * - {{parent.category.name}} - Access parent route data
330
+ * - {{current.product.title}} - Access current route data
331
+ * - {{user.name}} - Auto-resolved from combined data (backward compatible)
332
+ *
333
+ * @param content - The content string containing {{}} placeholders
334
+ * @param dataContext - The enhanced WeaverseDataContext with route-keyed data
335
+ * @returns The content with placeholders replaced by actual values
336
+ */
337
+ export function replaceContentDataConnectors(
338
+ content: string,
339
+ dataContext: DataContext | null | undefined
340
+ ): string {
341
+ if (!dataContext || typeof content !== 'string') {
342
+ return content
343
+ }
344
+
345
+ try {
346
+ // Simplified caching - dataContext is already the combined data
347
+ const cacheKey = createCacheKey(content, dataContext)
348
+
349
+ const cachedContent = templateCache.get(cacheKey)
350
+ if (cachedContent) {
351
+ return cachedContent
352
+ }
353
+
354
+ const newContent = content.replace(TEMPLATE_REGEX, (match, path) => {
355
+ try {
356
+ const value = resolveDataFromContext(path, dataContext)
357
+
358
+ if (value === undefined || value === null) {
359
+ return match // Return original placeholder if value not found
360
+ }
361
+
362
+ return sanitizeValue(value)
363
+ } catch (pathError) {
364
+ console.warn(
365
+ `Error resolving data connector path "${path}":`,
366
+ pathError
367
+ )
368
+ return match
369
+ }
370
+ })
371
+
372
+ // Cache the result (with size limit)
373
+ if (templateCache.size >= MAX_CACHE_SIZE) {
374
+ const firstKey = templateCache.keys().next().value
375
+ templateCache.delete(firstKey)
376
+ }
377
+ templateCache.set(cacheKey, newContent)
378
+
379
+ return newContent
380
+ } catch (error) {
381
+ console.warn('Error processing data connectors:', error, { content })
382
+ return content
383
+ }
384
+ }
385
+
386
+ /**
387
+ * Fast check if data contains any {{...}} placeholders
388
+ * Returns true if any string value contains the pattern
389
+ * This is MUCH faster than processing and allows early exit
390
+ *
391
+ * IMPORTANT: Uses global TEMPLATE_REGEX, must reset lastIndex
392
+ */
393
+ function hasPlaceholders(data: unknown, visited = new WeakSet()): boolean {
394
+ if (typeof data === 'string') {
395
+ // Reset regex state before testing to ensure consistent results
396
+ TEMPLATE_REGEX.lastIndex = 0
397
+ return TEMPLATE_REGEX.test(data)
398
+ }
399
+
400
+ if (typeof data !== 'object' || data === null) {
401
+ return false
402
+ }
403
+
404
+ // Skip Date objects - they can't contain placeholders
405
+ if (data instanceof Date) {
406
+ return false
407
+ }
408
+
409
+ // Protect against circular references
410
+ if (visited.has(data as WeakKey)) {
411
+ return false
412
+ }
413
+ visited.add(data as WeakKey)
414
+
415
+ try {
416
+ if (Array.isArray(data)) {
417
+ return data.some((item) => hasPlaceholders(item, visited))
418
+ }
419
+
420
+ // Check object values safely (skip property access errors)
421
+ for (const key of Object.keys(data)) {
422
+ try {
423
+ const value = (data as Record<string, unknown>)[key]
424
+ if (hasPlaceholders(value, visited)) {
425
+ return true
426
+ }
427
+ } catch {
428
+ // Ignore property access errors - continue checking other properties
429
+ }
430
+ }
431
+
432
+ return false
433
+ } finally {
434
+ visited.delete(data as WeakKey)
435
+ }
436
+ }
437
+
438
+ /**
439
+ * Recursively replaces data connector placeholders in any data structure
440
+ * IMMUTABLE: Creates deep copies during processing to avoid mutating original data
441
+ * Handles strings, objects, arrays, and nested combinations
442
+ *
443
+ * PERFORMANCE: Now includes early exit if no placeholders detected
444
+ *
445
+ * @param data - The data to process (string, object, array, or primitive)
446
+ * @param dataContext - The data context for replacements
447
+ * @param visited - Set to track visited objects for circular reference protection
448
+ * @param forceClone - Internal flag to force cloning even if no placeholders (for parent immutability)
449
+ * @returns IMMUTABLE copy of the data with all string placeholders replaced
450
+ */
451
+ export function replaceContentDataConnectorsDeep<T>(
452
+ data: T,
453
+ dataContext: DataContext | null | undefined,
454
+ visited = new WeakSet(),
455
+ forceClone = false
456
+ ): T {
457
+ if (!dataContext) {
458
+ return data
459
+ }
460
+
461
+ const dataHasPlaceholders = hasPlaceholders(data, new WeakSet())
462
+
463
+ // PERFORMANCE: Fast check - if no placeholders exist and not forced to clone, skip ALL processing
464
+ // This saves 90%+ of CPU time for components without data connectors
465
+ if (!(dataHasPlaceholders || forceClone)) {
466
+ return data
467
+ }
468
+
469
+ // Handle primitive types - no cloning needed for primitives
470
+ if (typeof data === 'string') {
471
+ return replaceContentDataConnectors(data, dataContext) as T
472
+ }
473
+
474
+ if (typeof data !== 'object' || data === null) {
475
+ return data
476
+ }
477
+
478
+ // Protect against circular references
479
+ if (visited.has(data as WeakKey)) {
480
+ console.warn(
481
+ 'Circular reference detected in deep data connector replacement'
482
+ )
483
+ return data
484
+ }
485
+
486
+ // Add to circular reference tracking
487
+ visited.add(data as WeakKey)
488
+
489
+ try {
490
+ // Handle special objects that should be cloned as-is
491
+ if (data instanceof Date) {
492
+ // Date objects can't contain placeholders, but clone if forced (parent has placeholders)
493
+ // or if somehow detected (shouldn't happen but be safe)
494
+ if (forceClone || dataHasPlaceholders) {
495
+ return new Date(data.getTime()) as T
496
+ }
497
+ return data
498
+ }
499
+
500
+ // Handle arrays - create new array with processed items
501
+ // Force clone children if parent has placeholders (for immutability)
502
+ if (Array.isArray(data)) {
503
+ const result = data.map((item) =>
504
+ replaceContentDataConnectorsDeep(
505
+ item,
506
+ dataContext,
507
+ visited,
508
+ dataHasPlaceholders
509
+ )
510
+ ) as T
511
+ return result
512
+ }
513
+
514
+ // Handle plain objects - create new object with processed values
515
+ // Force clone children if parent has placeholders (for immutability)
516
+ const result = {} as T
517
+
518
+ // Use Object.entries with try-catch per property to handle getter errors
519
+ for (const key of Object.keys(data)) {
520
+ try {
521
+ const value = (data as Record<string, unknown>)[key]
522
+ ;(result as Record<string, unknown>)[key] =
523
+ replaceContentDataConnectorsDeep(
524
+ value,
525
+ dataContext,
526
+ visited,
527
+ dataHasPlaceholders
528
+ )
529
+ } catch (propertyError) {
530
+ // If property access throws, skip it but continue processing
531
+ console.warn(`Error accessing property "${key}":`, propertyError)
532
+ // Don't include this property in result
533
+ }
534
+ }
535
+
536
+ return result
537
+ } catch (error) {
538
+ console.warn('Error in deep data connector replacement:', error)
539
+ return data
540
+ } finally {
541
+ // Remove from tracking when done processing this object
542
+ visited.delete(data as WeakKey)
543
+ }
544
+ }
package/dist/index.d.mts DELETED
@@ -1,67 +0,0 @@
1
- import { Weaverse, WeaverseItemStore, ElementCSS } from '@weaverse/core';
2
- export * from '@weaverse/core';
3
- import * as react from 'react';
4
- import react__default, { ReactElement, ReactNode } from 'react';
5
- import * as react_jsx_runtime from 'react/jsx-runtime';
6
-
7
- declare let WeaverseContext: react.Context<Weaverse>;
8
- declare let WeaverseContextProvider: react.Provider<Weaverse>;
9
- declare let WeaverseItemContext: react.Context<{
10
- id: string;
11
- parentId: string;
12
- }>;
13
-
14
- type DataContext = Record<string, unknown>;
15
- /**
16
- * Replaces data connector placeholders in content with actual values from data context
17
- * Supports {{path}} syntax with dot notation, array indexing, and route prefixes
18
- *
19
- * Enhanced syntax:
20
- * - {{root.siteSettings.title}} - Access root route data
21
- * - {{layout.navigation.items}} - Access layout route data
22
- * - {{parent.category.name}} - Access parent route data
23
- * - {{current.product.title}} - Access current route data
24
- * - {{user.name}} - Auto-resolved from combined data (backward compatible)
25
- *
26
- * @param content - The content string containing {{}} placeholders
27
- * @param dataContext - The enhanced WeaverseDataContext with route-keyed data
28
- * @returns The content with placeholders replaced by actual values
29
- */
30
- declare function replaceContentDataConnectors(content: string, dataContext: DataContext | null | undefined): string;
31
- /**
32
- * Recursively replaces data connector placeholders in any data structure
33
- * IMMUTABLE: Creates deep copies during processing to avoid mutating original data
34
- * Handles strings, objects, arrays, and nested combinations
35
- *
36
- * PERFORMANCE: Now includes early exit if no placeholders detected
37
- *
38
- * @param data - The data to process (string, object, array, or primitive)
39
- * @param dataContext - The data context for replacements
40
- * @param visited - Set to track visited objects for circular reference protection
41
- * @param forceClone - Internal flag to force cloning even if no placeholders (for parent immutability)
42
- * @returns IMMUTABLE copy of the data with all string placeholders replaced
43
- */
44
- declare function replaceContentDataConnectorsDeep<T>(data: T, dataContext: DataContext | null | undefined, visited?: WeakSet<WeakKey>, forceClone?: boolean): T;
45
-
46
- declare function useWeaverse<T = Weaverse>(): T;
47
- declare let useItemInstance: (id?: string) => WeaverseItemStore | null;
48
- declare let useParentInstance: () => WeaverseItemStore | null;
49
- declare let useChildInstances: (id?: string) => WeaverseItemStore[];
50
-
51
- interface WeaverseElementProps extends Partial<ReactElement> {
52
- children?: ReactElement | ReactElement[] | ReactNode | ReactNode[];
53
- className?: string;
54
- css?: ElementCSS;
55
- 'data-wv-id'?: string;
56
- }
57
- type WeaverseRootPropsType = {
58
- context: Weaverse;
59
- };
60
- type ItemComponentProps = {
61
- instance: InstanceType<typeof WeaverseItemStore>;
62
- };
63
-
64
- declare const useSafeExternalStore: (subscribe: any, getSnapshot: any, getServerSnapshot?: any) => any;
65
- declare const WeaverseRoot: react__default.MemoExoticComponent<({ context }: WeaverseRootPropsType) => react_jsx_runtime.JSX.Element | null>;
66
-
67
- export { type ItemComponentProps, WeaverseContext, WeaverseContextProvider, type WeaverseElementProps, WeaverseItemContext, WeaverseRoot, type WeaverseRootPropsType, replaceContentDataConnectors, replaceContentDataConnectorsDeep, useChildInstances, useItemInstance, useParentInstance, useSafeExternalStore, useWeaverse };
package/dist/index.d.ts DELETED
@@ -1,67 +0,0 @@
1
- import { Weaverse, WeaverseItemStore, ElementCSS } from '@weaverse/core';
2
- export * from '@weaverse/core';
3
- import * as react from 'react';
4
- import react__default, { ReactElement, ReactNode } from 'react';
5
- import * as react_jsx_runtime from 'react/jsx-runtime';
6
-
7
- declare let WeaverseContext: react.Context<Weaverse>;
8
- declare let WeaverseContextProvider: react.Provider<Weaverse>;
9
- declare let WeaverseItemContext: react.Context<{
10
- id: string;
11
- parentId: string;
12
- }>;
13
-
14
- type DataContext = Record<string, unknown>;
15
- /**
16
- * Replaces data connector placeholders in content with actual values from data context
17
- * Supports {{path}} syntax with dot notation, array indexing, and route prefixes
18
- *
19
- * Enhanced syntax:
20
- * - {{root.siteSettings.title}} - Access root route data
21
- * - {{layout.navigation.items}} - Access layout route data
22
- * - {{parent.category.name}} - Access parent route data
23
- * - {{current.product.title}} - Access current route data
24
- * - {{user.name}} - Auto-resolved from combined data (backward compatible)
25
- *
26
- * @param content - The content string containing {{}} placeholders
27
- * @param dataContext - The enhanced WeaverseDataContext with route-keyed data
28
- * @returns The content with placeholders replaced by actual values
29
- */
30
- declare function replaceContentDataConnectors(content: string, dataContext: DataContext | null | undefined): string;
31
- /**
32
- * Recursively replaces data connector placeholders in any data structure
33
- * IMMUTABLE: Creates deep copies during processing to avoid mutating original data
34
- * Handles strings, objects, arrays, and nested combinations
35
- *
36
- * PERFORMANCE: Now includes early exit if no placeholders detected
37
- *
38
- * @param data - The data to process (string, object, array, or primitive)
39
- * @param dataContext - The data context for replacements
40
- * @param visited - Set to track visited objects for circular reference protection
41
- * @param forceClone - Internal flag to force cloning even if no placeholders (for parent immutability)
42
- * @returns IMMUTABLE copy of the data with all string placeholders replaced
43
- */
44
- declare function replaceContentDataConnectorsDeep<T>(data: T, dataContext: DataContext | null | undefined, visited?: WeakSet<WeakKey>, forceClone?: boolean): T;
45
-
46
- declare function useWeaverse<T = Weaverse>(): T;
47
- declare let useItemInstance: (id?: string) => WeaverseItemStore | null;
48
- declare let useParentInstance: () => WeaverseItemStore | null;
49
- declare let useChildInstances: (id?: string) => WeaverseItemStore[];
50
-
51
- interface WeaverseElementProps extends Partial<ReactElement> {
52
- children?: ReactElement | ReactElement[] | ReactNode | ReactNode[];
53
- className?: string;
54
- css?: ElementCSS;
55
- 'data-wv-id'?: string;
56
- }
57
- type WeaverseRootPropsType = {
58
- context: Weaverse;
59
- };
60
- type ItemComponentProps = {
61
- instance: InstanceType<typeof WeaverseItemStore>;
62
- };
63
-
64
- declare const useSafeExternalStore: (subscribe: any, getSnapshot: any, getServerSnapshot?: any) => any;
65
- declare const WeaverseRoot: react__default.MemoExoticComponent<({ context }: WeaverseRootPropsType) => react_jsx_runtime.JSX.Element | null>;
66
-
67
- export { type ItemComponentProps, WeaverseContext, WeaverseContextProvider, type WeaverseElementProps, WeaverseItemContext, WeaverseRoot, type WeaverseRootPropsType, replaceContentDataConnectors, replaceContentDataConnectorsDeep, useChildInstances, useItemInstance, useParentInstance, useSafeExternalStore, useWeaverse };