@almadar/ui 1.0.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,808 @@
1
+ import { createContext, useRef, useCallback, useEffect, useContext, useState } from 'react';
2
+ import componentMappingJson from '@almadar/patterns/component-mapping.json';
3
+ import registryJson from '@almadar/patterns/registry.json';
4
+
5
+ var __defProp = Object.defineProperty;
6
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
7
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
8
+
9
+ // renderer/pattern-resolver.ts
10
+ var componentMapping = {};
11
+ var patternRegistry = {};
12
+ function initializePatternResolver(config) {
13
+ componentMapping = config.componentMapping;
14
+ patternRegistry = config.patternRegistry;
15
+ }
16
+ function setComponentMapping(mapping) {
17
+ componentMapping = mapping;
18
+ }
19
+ function setPatternRegistry(registry) {
20
+ patternRegistry = registry;
21
+ }
22
+ function resolvePattern(config) {
23
+ const { type, ...props } = config;
24
+ const mapping = componentMapping[type];
25
+ if (!mapping) {
26
+ if (Object.keys(componentMapping).length === 0) {
27
+ console.warn(
28
+ "[PatternResolver] Component mapping not initialized. Call initializePatternResolver() at app startup."
29
+ );
30
+ }
31
+ throw new Error(`Unknown pattern type: ${type}`);
32
+ }
33
+ if (mapping.deprecated) {
34
+ console.warn(
35
+ `[PatternResolver] Pattern "${type}" is deprecated.` + (mapping.replacedBy ? ` Use "${mapping.replacedBy}" instead.` : "")
36
+ );
37
+ }
38
+ const validatedProps = validatePatternProps(type, props);
39
+ return {
40
+ component: mapping.component,
41
+ importPath: mapping.importPath,
42
+ category: mapping.category,
43
+ validatedProps
44
+ };
45
+ }
46
+ function validatePatternProps(patternType, props) {
47
+ const definition = patternRegistry[patternType];
48
+ if (!definition || !definition.propsSchema) {
49
+ return props;
50
+ }
51
+ const validated = { ...props };
52
+ const schema = definition.propsSchema;
53
+ for (const [propName, propDef] of Object.entries(schema)) {
54
+ if (propDef.required && !(propName in validated)) {
55
+ console.warn(
56
+ `[PatternResolver] Missing required prop "${propName}" for pattern "${patternType}"`
57
+ );
58
+ }
59
+ }
60
+ return validated;
61
+ }
62
+ function isKnownPattern(type) {
63
+ return type in componentMapping;
64
+ }
65
+ function getKnownPatterns() {
66
+ return Object.keys(componentMapping);
67
+ }
68
+ function getPatternsByCategory(category) {
69
+ return Object.entries(componentMapping).filter(([, mapping]) => mapping.category === category).map(([type]) => type);
70
+ }
71
+ function getPatternMapping(type) {
72
+ return componentMapping[type];
73
+ }
74
+ function getPatternDefinition(type) {
75
+ return patternRegistry[type];
76
+ }
77
+
78
+ // renderer/client-effect-executor.ts
79
+ function executeClientEffects(effects, config) {
80
+ if (!effects || effects.length === 0) {
81
+ return;
82
+ }
83
+ for (const effect of effects) {
84
+ try {
85
+ executeEffect(effect, config);
86
+ } catch (error) {
87
+ console.error(
88
+ `[ClientEffectExecutor] Error executing effect:`,
89
+ effect,
90
+ error
91
+ );
92
+ }
93
+ }
94
+ config.onComplete?.();
95
+ }
96
+ function executeEffect(effect, config) {
97
+ const [effectType, ...args] = effect;
98
+ switch (effectType) {
99
+ case "render-ui": {
100
+ const [slot, patternConfig] = args;
101
+ executeRenderUI(slot, patternConfig, config);
102
+ break;
103
+ }
104
+ case "navigate": {
105
+ const [path, params] = args;
106
+ executeNavigate(path, params, config);
107
+ break;
108
+ }
109
+ case "notify": {
110
+ const [message, options] = args;
111
+ executeNotify(message, options, config);
112
+ break;
113
+ }
114
+ case "emit": {
115
+ const [event, payload] = args;
116
+ executeEmit(event, payload, config);
117
+ break;
118
+ }
119
+ default:
120
+ console.warn(`[ClientEffectExecutor] Unknown effect type: ${effectType}`);
121
+ }
122
+ }
123
+ function executeRenderUI(slot, patternConfig, config) {
124
+ config.renderToSlot(slot, patternConfig);
125
+ }
126
+ function executeNavigate(path, params, config) {
127
+ config.navigate(path, params);
128
+ }
129
+ function executeNotify(message, options, config) {
130
+ config.notify(message, options);
131
+ }
132
+ function executeEmit(event, payload, config) {
133
+ config.eventBus.emit(event, payload);
134
+ }
135
+ function parseClientEffect(raw) {
136
+ if (!Array.isArray(raw) || raw.length < 1) {
137
+ console.warn("[ClientEffectExecutor] Invalid effect format:", raw);
138
+ return null;
139
+ }
140
+ const [type, ...args] = raw;
141
+ if (typeof type !== "string") {
142
+ console.warn("[ClientEffectExecutor] Effect type must be string:", raw);
143
+ return null;
144
+ }
145
+ switch (type) {
146
+ case "render-ui":
147
+ return ["render-ui", args[0], args[1]];
148
+ case "navigate":
149
+ return ["navigate", args[0], args[1]];
150
+ case "notify":
151
+ return ["notify", args[0], args[1]];
152
+ case "emit":
153
+ return ["emit", args[0], args[1]];
154
+ default:
155
+ console.warn(`[ClientEffectExecutor] Unknown effect type: ${type}`);
156
+ return null;
157
+ }
158
+ }
159
+ function parseClientEffects(raw) {
160
+ if (!raw || !Array.isArray(raw)) {
161
+ return [];
162
+ }
163
+ return raw.map((effect) => parseClientEffect(effect)).filter((effect) => effect !== null);
164
+ }
165
+ function filterEffectsByType(effects, type) {
166
+ return effects.filter(
167
+ (effect) => effect[0] === type
168
+ );
169
+ }
170
+ function getRenderUIEffects(effects) {
171
+ return filterEffectsByType(effects, "render-ui");
172
+ }
173
+ function getNavigateEffects(effects) {
174
+ return filterEffectsByType(effects, "navigate");
175
+ }
176
+ function getNotifyEffects(effects) {
177
+ return filterEffectsByType(effects, "notify");
178
+ }
179
+ function getEmitEffects(effects) {
180
+ return filterEffectsByType(effects, "emit");
181
+ }
182
+ function useClientEffects(effects, options) {
183
+ const {
184
+ enabled = true,
185
+ debug = false,
186
+ onComplete,
187
+ ...config
188
+ } = options;
189
+ const executedRef = useRef(/* @__PURE__ */ new Set());
190
+ const executingRef = useRef(false);
191
+ const executedCountRef = useRef(0);
192
+ const getEffectKey = useCallback((effect) => {
193
+ return JSON.stringify(effect);
194
+ }, []);
195
+ const execute = useCallback((effectsToExecute) => {
196
+ if (executingRef.current || effectsToExecute.length === 0) {
197
+ return;
198
+ }
199
+ executingRef.current = true;
200
+ const newEffects = effectsToExecute.filter((effect) => {
201
+ const key = getEffectKey(effect);
202
+ if (executedRef.current.has(key)) {
203
+ if (debug) {
204
+ console.log("[useClientEffects] Skipping duplicate effect:", effect);
205
+ }
206
+ return false;
207
+ }
208
+ return true;
209
+ });
210
+ if (newEffects.length === 0) {
211
+ executingRef.current = false;
212
+ return;
213
+ }
214
+ if (debug) {
215
+ console.log("[useClientEffects] Executing effects:", newEffects);
216
+ }
217
+ newEffects.forEach((effect) => {
218
+ executedRef.current.add(getEffectKey(effect));
219
+ });
220
+ executeClientEffects(newEffects, {
221
+ ...config,
222
+ onComplete: () => {
223
+ executedCountRef.current = newEffects.length;
224
+ executingRef.current = false;
225
+ onComplete?.();
226
+ }
227
+ });
228
+ }, [config, debug, getEffectKey, onComplete]);
229
+ useEffect(() => {
230
+ if (!enabled || !effects || effects.length === 0) {
231
+ return;
232
+ }
233
+ execute(effects);
234
+ }, [effects, enabled, execute]);
235
+ const prevEffectsRef = useRef();
236
+ useEffect(() => {
237
+ if (effects !== prevEffectsRef.current) {
238
+ prevEffectsRef.current = effects;
239
+ }
240
+ }, [effects]);
241
+ return {
242
+ executedCount: executedCountRef.current,
243
+ executing: executingRef.current,
244
+ execute
245
+ };
246
+ }
247
+ var ClientEffectConfigContext = createContext(null);
248
+ var ClientEffectConfigProvider = ClientEffectConfigContext.Provider;
249
+ function useClientEffectConfig() {
250
+ const context = useContext(ClientEffectConfigContext);
251
+ if (!context) {
252
+ throw new Error(
253
+ "useClientEffectConfig must be used within a ClientEffectConfigProvider. Make sure your component tree is wrapped with OrbitalProvider."
254
+ );
255
+ }
256
+ return context;
257
+ }
258
+ function useClientEffectConfigOptional() {
259
+ return useContext(ClientEffectConfigContext);
260
+ }
261
+
262
+ // renderer/data-resolver.ts
263
+ function resolveEntityData(entityName, context) {
264
+ if (context.fetchedData && entityName in context.fetchedData) {
265
+ const data = context.fetchedData[entityName];
266
+ return {
267
+ data: Array.isArray(data) ? data : [],
268
+ loading: false
269
+ };
270
+ }
271
+ if (context.entityStore) {
272
+ try {
273
+ const data = context.entityStore.getRecords(entityName);
274
+ return {
275
+ data: Array.isArray(data) ? data : [],
276
+ loading: false
277
+ };
278
+ } catch (error) {
279
+ console.warn(
280
+ `[DataResolver] Error getting records from entity store for "${entityName}":`,
281
+ error
282
+ );
283
+ }
284
+ }
285
+ const hasAnySources = context.fetchedData || context.entityStore;
286
+ return {
287
+ data: [],
288
+ loading: !hasAnySources
289
+ // Only loading if no sources configured
290
+ };
291
+ }
292
+ function resolveEntityDataWithQuery(entityName, queryRef, context) {
293
+ const resolution = resolveEntityData(entityName, context);
294
+ if (!queryRef || !context.querySingleton) {
295
+ return resolution;
296
+ }
297
+ try {
298
+ const filters = context.querySingleton.getFilters(queryRef);
299
+ const filteredData = applyFilters(resolution.data, filters);
300
+ return {
301
+ ...resolution,
302
+ data: filteredData
303
+ };
304
+ } catch (error) {
305
+ console.warn(
306
+ `[DataResolver] Error applying query filters for "${queryRef}":`,
307
+ error
308
+ );
309
+ return resolution;
310
+ }
311
+ }
312
+ function applyFilters(data, filters) {
313
+ if (!filters || Object.keys(filters).length === 0) {
314
+ return data;
315
+ }
316
+ return data.filter((item) => {
317
+ if (typeof item !== "object" || item === null) {
318
+ return false;
319
+ }
320
+ const record = item;
321
+ return Object.entries(filters).every(([key, value]) => {
322
+ if (value === void 0 || value === null) {
323
+ return true;
324
+ }
325
+ const recordValue = record[key];
326
+ if (Array.isArray(value)) {
327
+ return value.includes(recordValue);
328
+ }
329
+ if (typeof value === "string" && typeof recordValue === "string") {
330
+ if (value.startsWith("*") && value.endsWith("*")) {
331
+ const pattern = value.slice(1, -1);
332
+ return recordValue.toLowerCase().includes(pattern.toLowerCase());
333
+ }
334
+ }
335
+ return recordValue === value;
336
+ });
337
+ });
338
+ }
339
+ function resolveEntityById(entityName, id, context) {
340
+ const { data } = resolveEntityData(entityName, context);
341
+ return data.find((item) => {
342
+ if (typeof item !== "object" || item === null) {
343
+ return false;
344
+ }
345
+ const record = item;
346
+ return record.id === id || record._id === id;
347
+ }) ?? null;
348
+ }
349
+ function resolveEntityCount(entityName, context, filters) {
350
+ const { data } = resolveEntityData(entityName, context);
351
+ if (filters) {
352
+ return applyFilters(data, filters).length;
353
+ }
354
+ return data.length;
355
+ }
356
+ function hasEntities(entityName, context) {
357
+ const { data } = resolveEntityData(entityName, context);
358
+ return data.length > 0;
359
+ }
360
+ function createFetchedDataContext(data) {
361
+ return { fetchedData: data };
362
+ }
363
+ function mergeDataContexts(...contexts) {
364
+ const merged = {};
365
+ for (const context of contexts) {
366
+ if (context.fetchedData) {
367
+ merged.fetchedData = {
368
+ ...merged.fetchedData,
369
+ ...context.fetchedData
370
+ };
371
+ }
372
+ if (context.entityStore) {
373
+ merged.entityStore = context.entityStore;
374
+ }
375
+ if (context.querySingleton) {
376
+ merged.querySingleton = context.querySingleton;
377
+ }
378
+ }
379
+ return merged;
380
+ }
381
+
382
+ // renderer/slot-definitions.ts
383
+ var SLOT_DEFINITIONS = {
384
+ // -------------------------------------------------------------------------
385
+ // Inline Slots - Render in place within the component tree
386
+ // -------------------------------------------------------------------------
387
+ main: {
388
+ name: "main",
389
+ type: "inline"
390
+ },
391
+ sidebar: {
392
+ name: "sidebar",
393
+ type: "inline"
394
+ },
395
+ // -------------------------------------------------------------------------
396
+ // Portal Slots - Render to document.body via React Portal
397
+ // -------------------------------------------------------------------------
398
+ modal: {
399
+ name: "modal",
400
+ type: "portal",
401
+ portalTarget: "body",
402
+ zIndex: 1e3
403
+ },
404
+ drawer: {
405
+ name: "drawer",
406
+ type: "portal",
407
+ portalTarget: "body",
408
+ zIndex: 900
409
+ },
410
+ overlay: {
411
+ name: "overlay",
412
+ type: "portal",
413
+ portalTarget: "body",
414
+ zIndex: 1100
415
+ },
416
+ center: {
417
+ name: "center",
418
+ type: "portal",
419
+ portalTarget: "body",
420
+ zIndex: 1e3
421
+ },
422
+ toast: {
423
+ name: "toast",
424
+ type: "portal",
425
+ portalTarget: "body",
426
+ zIndex: 1200
427
+ },
428
+ // -------------------------------------------------------------------------
429
+ // Game HUD Slots - Portal for game overlay UI
430
+ // -------------------------------------------------------------------------
431
+ "hud-top": {
432
+ name: "hud-top",
433
+ type: "portal",
434
+ portalTarget: "body",
435
+ zIndex: 500
436
+ },
437
+ "hud-bottom": {
438
+ name: "hud-bottom",
439
+ type: "portal",
440
+ portalTarget: "body",
441
+ zIndex: 500
442
+ },
443
+ floating: {
444
+ name: "floating",
445
+ type: "portal",
446
+ portalTarget: "body",
447
+ zIndex: 800
448
+ }
449
+ };
450
+ function getSlotDefinition(slot) {
451
+ return SLOT_DEFINITIONS[slot];
452
+ }
453
+ function isPortalSlot(slot) {
454
+ return SLOT_DEFINITIONS[slot]?.type === "portal";
455
+ }
456
+ function isInlineSlot(slot) {
457
+ return SLOT_DEFINITIONS[slot]?.type === "inline";
458
+ }
459
+ function getSlotsByType(type) {
460
+ return Object.entries(SLOT_DEFINITIONS).filter(([, def]) => def.type === type).map(([name]) => name);
461
+ }
462
+ function getInlineSlots() {
463
+ return getSlotsByType("inline");
464
+ }
465
+ function getPortalSlots() {
466
+ return getSlotsByType("portal");
467
+ }
468
+ var ALL_SLOTS = Object.keys(SLOT_DEFINITIONS);
469
+ var effectIdCounter = 0;
470
+ function generateEffectId() {
471
+ return `offline-effect-${++effectIdCounter}-${Date.now()}`;
472
+ }
473
+ var OfflineExecutor = class {
474
+ constructor(config) {
475
+ __publicField(this, "config");
476
+ __publicField(this, "state");
477
+ __publicField(this, "storage");
478
+ /**
479
+ * Handle going online
480
+ */
481
+ __publicField(this, "handleOnline", () => {
482
+ this.state.isOffline = false;
483
+ });
484
+ /**
485
+ * Handle going offline
486
+ */
487
+ __publicField(this, "handleOffline", () => {
488
+ this.state.isOffline = true;
489
+ });
490
+ this.config = {
491
+ enableSyncQueue: true,
492
+ maxQueueSize: 100,
493
+ ...config
494
+ };
495
+ this.state = {
496
+ isOffline: !this.checkOnline(),
497
+ syncQueue: [],
498
+ localEffectsProcessed: 0,
499
+ effectsSynced: 0
500
+ };
501
+ this.storage = typeof localStorage !== "undefined" ? localStorage : null;
502
+ this.loadSyncQueue();
503
+ if (typeof window !== "undefined") {
504
+ window.addEventListener("online", this.handleOnline);
505
+ window.addEventListener("offline", this.handleOffline);
506
+ }
507
+ }
508
+ /**
509
+ * Check if we're online (browser API)
510
+ */
511
+ checkOnline() {
512
+ return typeof navigator !== "undefined" ? navigator.onLine : true;
513
+ }
514
+ /**
515
+ * Load sync queue from localStorage
516
+ */
517
+ loadSyncQueue() {
518
+ if (!this.storage) return;
519
+ try {
520
+ const stored = this.storage.getItem("orbital-offline-queue");
521
+ if (stored) {
522
+ this.state.syncQueue = JSON.parse(stored);
523
+ }
524
+ } catch (error) {
525
+ console.warn("[OfflineExecutor] Failed to load sync queue:", error);
526
+ }
527
+ }
528
+ /**
529
+ * Save sync queue to localStorage
530
+ */
531
+ saveSyncQueue() {
532
+ if (!this.storage) return;
533
+ try {
534
+ this.storage.setItem("orbital-offline-queue", JSON.stringify(this.state.syncQueue));
535
+ } catch (error) {
536
+ console.warn("[OfflineExecutor] Failed to save sync queue:", error);
537
+ }
538
+ }
539
+ /**
540
+ * Add an effect to the sync queue
541
+ */
542
+ queueForSync(type, payload) {
543
+ if (!this.config.enableSyncQueue) return;
544
+ const effect = {
545
+ id: generateEffectId(),
546
+ timestamp: Date.now(),
547
+ type,
548
+ payload,
549
+ retries: 0,
550
+ maxRetries: 3
551
+ };
552
+ this.state.syncQueue.push(effect);
553
+ if (this.state.syncQueue.length > (this.config.maxQueueSize ?? 100)) {
554
+ this.state.syncQueue.shift();
555
+ }
556
+ this.saveSyncQueue();
557
+ this.config.onEffectQueued?.(effect);
558
+ this.config.onQueueChange?.(this.state.syncQueue);
559
+ }
560
+ /**
561
+ * Execute client effects immediately.
562
+ */
563
+ executeClientEffects(effects) {
564
+ if (effects.length === 0) return;
565
+ executeClientEffects(effects, this.config);
566
+ this.state.localEffectsProcessed += effects.length;
567
+ }
568
+ /**
569
+ * Process an event in offline mode.
570
+ *
571
+ * Returns a simulated EventResponse with mock data.
572
+ * Client effects are executed immediately.
573
+ * Server effects are queued for sync.
574
+ */
575
+ processEventOffline(event, payload, effects) {
576
+ const clientEffects = [];
577
+ const fetchedData = {};
578
+ if (effects) {
579
+ for (const effect of effects) {
580
+ if (!Array.isArray(effect) || effect.length < 1) continue;
581
+ const [type, ...args] = effect;
582
+ switch (type) {
583
+ // Client effects - execute immediately
584
+ case "render-ui":
585
+ case "navigate":
586
+ case "notify":
587
+ case "emit":
588
+ clientEffects.push(effect);
589
+ break;
590
+ // Fetch effect - use mock data
591
+ case "fetch": {
592
+ const [entityName, _query] = args;
593
+ if (typeof entityName === "string" && this.config.mockDataProvider) {
594
+ fetchedData[entityName] = this.config.mockDataProvider(entityName);
595
+ }
596
+ break;
597
+ }
598
+ // Server effects - queue for sync
599
+ case "persist":
600
+ case "call-service":
601
+ case "spawn":
602
+ case "despawn":
603
+ this.queueForSync(type, { args, event, payload });
604
+ break;
605
+ default:
606
+ console.warn(`[OfflineExecutor] Unknown effect type: ${type}`);
607
+ }
608
+ }
609
+ }
610
+ if (clientEffects.length > 0) {
611
+ this.executeClientEffects(clientEffects);
612
+ }
613
+ return {
614
+ success: true,
615
+ data: Object.keys(fetchedData).length > 0 ? fetchedData : void 0,
616
+ clientEffects: clientEffects.length > 0 ? clientEffects : void 0
617
+ };
618
+ }
619
+ /**
620
+ * Sync pending effects to server.
621
+ *
622
+ * @param serverUrl - Base URL for the orbital server
623
+ * @param authToken - Optional auth token for requests
624
+ * @returns Number of successfully synced effects
625
+ */
626
+ async syncPendingEffects(serverUrl, authToken) {
627
+ if (this.state.syncQueue.length === 0) {
628
+ return 0;
629
+ }
630
+ this.state.lastSyncAttempt = Date.now();
631
+ let syncedCount = 0;
632
+ const failedEffects = [];
633
+ const headers = {
634
+ "Content-Type": "application/json"
635
+ };
636
+ if (authToken) {
637
+ headers["Authorization"] = `Bearer ${authToken}`;
638
+ }
639
+ for (const effect of this.state.syncQueue) {
640
+ try {
641
+ const response = await fetch(`${serverUrl}/sync-effect`, {
642
+ method: "POST",
643
+ headers,
644
+ body: JSON.stringify({
645
+ type: effect.type,
646
+ payload: effect.payload,
647
+ offlineId: effect.id,
648
+ offlineTimestamp: effect.timestamp
649
+ })
650
+ });
651
+ if (response.ok) {
652
+ syncedCount++;
653
+ } else {
654
+ effect.retries++;
655
+ if (effect.retries < effect.maxRetries) {
656
+ failedEffects.push(effect);
657
+ }
658
+ }
659
+ } catch (error) {
660
+ effect.retries++;
661
+ if (effect.retries < effect.maxRetries) {
662
+ failedEffects.push(effect);
663
+ }
664
+ }
665
+ }
666
+ this.state.syncQueue = failedEffects;
667
+ this.state.effectsSynced += syncedCount;
668
+ if (syncedCount > 0) {
669
+ this.state.lastSuccessfulSync = Date.now();
670
+ }
671
+ this.saveSyncQueue();
672
+ this.config.onQueueChange?.(this.state.syncQueue);
673
+ return syncedCount;
674
+ }
675
+ /**
676
+ * Get current executor state
677
+ */
678
+ getState() {
679
+ return { ...this.state };
680
+ }
681
+ /**
682
+ * Get number of pending effects
683
+ */
684
+ getPendingCount() {
685
+ return this.state.syncQueue.length;
686
+ }
687
+ /**
688
+ * Clear the sync queue
689
+ */
690
+ clearQueue() {
691
+ this.state.syncQueue = [];
692
+ this.saveSyncQueue();
693
+ this.config.onQueueChange?.(this.state.syncQueue);
694
+ }
695
+ /**
696
+ * Dispose the executor and clean up listeners
697
+ */
698
+ dispose() {
699
+ if (typeof window !== "undefined") {
700
+ window.removeEventListener("online", this.handleOnline);
701
+ window.removeEventListener("offline", this.handleOffline);
702
+ }
703
+ }
704
+ };
705
+ function createOfflineExecutor(config) {
706
+ return new OfflineExecutor(config);
707
+ }
708
+ function useOfflineExecutor(options) {
709
+ const executorRef = useRef(null);
710
+ const [state, setState] = useState({
711
+ isOffline: false,
712
+ syncQueue: [],
713
+ localEffectsProcessed: 0,
714
+ effectsSynced: 0
715
+ });
716
+ useEffect(() => {
717
+ const executor = new OfflineExecutor({
718
+ ...options,
719
+ onQueueChange: (queue) => {
720
+ setState(executor.getState());
721
+ options.onQueueChange?.(queue);
722
+ }
723
+ });
724
+ executorRef.current = executor;
725
+ setState(executor.getState());
726
+ return () => {
727
+ executor.dispose();
728
+ executorRef.current = null;
729
+ };
730
+ }, []);
731
+ useEffect(() => {
732
+ if (!options.autoSync || !options.serverUrl) return;
733
+ const handleOnline = async () => {
734
+ if (executorRef.current) {
735
+ await executorRef.current.syncPendingEffects(
736
+ options.serverUrl,
737
+ options.authToken
738
+ );
739
+ setState(executorRef.current.getState());
740
+ }
741
+ };
742
+ window.addEventListener("online", handleOnline);
743
+ return () => window.removeEventListener("online", handleOnline);
744
+ }, [options.autoSync, options.serverUrl, options.authToken]);
745
+ const executeEffects = useCallback((effects) => {
746
+ executorRef.current?.executeClientEffects(effects);
747
+ if (executorRef.current) {
748
+ setState(executorRef.current.getState());
749
+ }
750
+ }, []);
751
+ const processEventOffline = useCallback(
752
+ (event, payload, effects) => {
753
+ const result = executorRef.current?.processEventOffline(event, payload, effects);
754
+ if (executorRef.current) {
755
+ setState(executorRef.current.getState());
756
+ }
757
+ return result ?? { success: false, error: "Executor not initialized" };
758
+ },
759
+ []
760
+ );
761
+ const sync = useCallback(async () => {
762
+ if (!executorRef.current || !options.serverUrl) return 0;
763
+ const count = await executorRef.current.syncPendingEffects(
764
+ options.serverUrl,
765
+ options.authToken
766
+ );
767
+ setState(executorRef.current.getState());
768
+ return count;
769
+ }, [options.serverUrl, options.authToken]);
770
+ const clearQueue = useCallback(() => {
771
+ executorRef.current?.clearQueue();
772
+ if (executorRef.current) {
773
+ setState(executorRef.current.getState());
774
+ }
775
+ }, []);
776
+ return {
777
+ state,
778
+ isOffline: state.isOffline,
779
+ pendingCount: state.syncQueue.length,
780
+ executeClientEffects: executeEffects,
781
+ processEventOffline,
782
+ sync,
783
+ clearQueue
784
+ };
785
+ }
786
+ function initializePatterns() {
787
+ console.log("[PatternResolver] initializePatterns called");
788
+ console.log("[PatternResolver] componentMappingJson:", componentMappingJson);
789
+ console.log("[PatternResolver] registryJson keys:", Object.keys(registryJson));
790
+ const componentMappingData = componentMappingJson;
791
+ const componentMapping2 = componentMappingData.mappings || {};
792
+ console.log("[PatternResolver] Extracted mappings count:", Object.keys(componentMapping2).length);
793
+ console.log("[PatternResolver] Sample mappings:", Object.keys(componentMapping2).slice(0, 5));
794
+ const registryData = registryJson;
795
+ const patternRegistry2 = registryData.patterns || {};
796
+ console.log("[PatternResolver] Extracted patterns count:", Object.keys(patternRegistry2).length);
797
+ initializePatternResolver({
798
+ componentMapping: componentMapping2,
799
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
800
+ patternRegistry: patternRegistry2
801
+ });
802
+ console.log(`[PatternResolver] Initialized with ${Object.keys(componentMapping2).length} component mappings and ${Object.keys(patternRegistry2).length} pattern definitions`);
803
+ return Object.keys(componentMapping2).length;
804
+ }
805
+
806
+ export { ALL_SLOTS, ClientEffectConfigContext, ClientEffectConfigProvider, OfflineExecutor, SLOT_DEFINITIONS, createFetchedDataContext, createOfflineExecutor, executeClientEffects, filterEffectsByType, getEmitEffects, getInlineSlots, getKnownPatterns, getNavigateEffects, getNotifyEffects, getPatternDefinition, getPatternMapping, getPatternsByCategory, getPortalSlots, getRenderUIEffects, getSlotDefinition, getSlotsByType, hasEntities, initializePatternResolver, initializePatterns, isInlineSlot, isKnownPattern, isPortalSlot, mergeDataContexts, parseClientEffect, parseClientEffects, resolveEntityById, resolveEntityCount, resolveEntityData, resolveEntityDataWithQuery, resolvePattern, setComponentMapping, setPatternRegistry, useClientEffectConfig, useClientEffectConfigOptional, useClientEffects, useOfflineExecutor };
807
+ //# sourceMappingURL=index.js.map
808
+ //# sourceMappingURL=index.js.map