@almadar/ui 2.7.0 → 2.9.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,375 @@
1
+ import { SuspenseConfigProvider } from './chunk-NES4SBB7.js';
2
+ import { ThemeProvider } from './chunk-DKQN5FVU.js';
3
+ import { SelectionProvider, EntityDataProvider } from './chunk-WGJIL4YR.js';
4
+ import { useEventBus, EventBusProvider } from './chunk-YXZM3WCF.js';
5
+ import { recordTransition, registerCheck, bindEventBus, bindTraitStateGetter } from './chunk-A5J5CNCU.js';
6
+ import { useOfflineExecutor } from './chunk-K2D5D3WK.js';
7
+ import { createContext, useState, useCallback, useMemo, useContext, useRef, useEffect } from 'react';
8
+ import { jsx, Fragment } from 'react/jsx-runtime';
9
+
10
+ var FetchedDataContext = createContext(null);
11
+ function FetchedDataProvider({
12
+ initialData,
13
+ children
14
+ }) {
15
+ const [state, setState] = useState(() => ({
16
+ data: initialData || {},
17
+ fetchedAt: {},
18
+ loading: false,
19
+ error: null
20
+ }));
21
+ const getData = useCallback(
22
+ (entityName) => {
23
+ return state.data[entityName] || [];
24
+ },
25
+ [state.data]
26
+ );
27
+ const getById = useCallback(
28
+ (entityName, id) => {
29
+ const records = state.data[entityName];
30
+ return records?.find((r) => r.id === id);
31
+ },
32
+ [state.data]
33
+ );
34
+ const hasData = useCallback(
35
+ (entityName) => {
36
+ return entityName in state.data && state.data[entityName].length > 0;
37
+ },
38
+ [state.data]
39
+ );
40
+ const getFetchedAt = useCallback(
41
+ (entityName) => {
42
+ return state.fetchedAt[entityName];
43
+ },
44
+ [state.fetchedAt]
45
+ );
46
+ const setData = useCallback((data) => {
47
+ const now = Date.now();
48
+ setState((prev) => ({
49
+ ...prev,
50
+ data: {
51
+ ...prev.data,
52
+ ...data
53
+ },
54
+ fetchedAt: {
55
+ ...prev.fetchedAt,
56
+ ...Object.keys(data).reduce(
57
+ (acc, key) => ({ ...acc, [key]: now }),
58
+ {}
59
+ )
60
+ },
61
+ loading: false,
62
+ error: null
63
+ }));
64
+ }, []);
65
+ const clearData = useCallback(() => {
66
+ setState((prev) => ({
67
+ ...prev,
68
+ data: {},
69
+ fetchedAt: {}
70
+ }));
71
+ }, []);
72
+ const clearEntity = useCallback((entityName) => {
73
+ setState((prev) => {
74
+ const newData = { ...prev.data };
75
+ const newFetchedAt = { ...prev.fetchedAt };
76
+ delete newData[entityName];
77
+ delete newFetchedAt[entityName];
78
+ return {
79
+ ...prev,
80
+ data: newData,
81
+ fetchedAt: newFetchedAt
82
+ };
83
+ });
84
+ }, []);
85
+ const setLoading = useCallback((loading) => {
86
+ setState((prev) => ({ ...prev, loading }));
87
+ }, []);
88
+ const setError = useCallback((error) => {
89
+ setState((prev) => ({ ...prev, error, loading: false }));
90
+ }, []);
91
+ const contextValue = useMemo(
92
+ () => ({
93
+ getData,
94
+ getById,
95
+ hasData,
96
+ getFetchedAt,
97
+ setData,
98
+ clearData,
99
+ clearEntity,
100
+ loading: state.loading,
101
+ setLoading,
102
+ error: state.error,
103
+ setError
104
+ }),
105
+ [
106
+ getData,
107
+ getById,
108
+ hasData,
109
+ getFetchedAt,
110
+ setData,
111
+ clearData,
112
+ clearEntity,
113
+ state.loading,
114
+ setLoading,
115
+ state.error,
116
+ setError
117
+ ]
118
+ );
119
+ return /* @__PURE__ */ jsx(FetchedDataContext.Provider, { value: contextValue, children });
120
+ }
121
+ function useFetchedDataContext() {
122
+ return useContext(FetchedDataContext);
123
+ }
124
+ function useFetchedData() {
125
+ const context = useContext(FetchedDataContext);
126
+ if (!context) {
127
+ return {
128
+ getData: () => [],
129
+ getById: () => void 0,
130
+ hasData: () => false,
131
+ getFetchedAt: () => void 0,
132
+ setData: () => {
133
+ },
134
+ clearData: () => {
135
+ },
136
+ clearEntity: () => {
137
+ },
138
+ loading: false,
139
+ setLoading: () => {
140
+ },
141
+ error: null,
142
+ setError: () => {
143
+ }
144
+ };
145
+ }
146
+ return context;
147
+ }
148
+ function useFetchedEntity(entityName) {
149
+ const context = useFetchedData();
150
+ return {
151
+ /** All fetched records for this entity */
152
+ records: context.getData(entityName),
153
+ /** Get a record by ID */
154
+ getById: (id) => context.getById(entityName, id),
155
+ /** Whether data has been fetched for this entity */
156
+ hasData: context.hasData(entityName),
157
+ /** When data was last fetched */
158
+ fetchedAt: context.getFetchedAt(entityName),
159
+ /** Whether data is loading */
160
+ loading: context.loading,
161
+ /** Current error */
162
+ error: context.error
163
+ };
164
+ }
165
+ var DISPATCH_SUFFIX = ":DISPATCH";
166
+ var SUCCESS_SUFFIX = ":SUCCESS";
167
+ var ERROR_SUFFIX = ":ERROR";
168
+ function parseLifecycleEvent(type) {
169
+ if (type.endsWith(DISPATCH_SUFFIX)) {
170
+ const traitName = type.slice(0, -DISPATCH_SUFFIX.length);
171
+ if (traitName) return { kind: "dispatch", traitName };
172
+ } else if (type.endsWith(SUCCESS_SUFFIX)) {
173
+ const rest = type.slice(0, -SUCCESS_SUFFIX.length);
174
+ const colonIdx = rest.indexOf(":");
175
+ if (colonIdx > 0) {
176
+ return {
177
+ kind: "success",
178
+ traitName: rest.slice(0, colonIdx),
179
+ event: rest.slice(colonIdx + 1)
180
+ };
181
+ }
182
+ } else if (type.endsWith(ERROR_SUFFIX)) {
183
+ const rest = type.slice(0, -ERROR_SUFFIX.length);
184
+ const colonIdx = rest.indexOf(":");
185
+ if (colonIdx > 0) {
186
+ return {
187
+ kind: "error",
188
+ traitName: rest.slice(0, colonIdx),
189
+ event: rest.slice(colonIdx + 1)
190
+ };
191
+ }
192
+ }
193
+ return null;
194
+ }
195
+ function VerificationProvider({
196
+ children,
197
+ enabled,
198
+ runtimeManager,
199
+ traitStateGetter
200
+ }) {
201
+ const isEnabled = enabled ?? (typeof process !== "undefined" && process.env?.NODE_ENV !== "production");
202
+ const eventBus = useEventBus();
203
+ const pendingRef = useRef(/* @__PURE__ */ new Map());
204
+ useEffect(() => {
205
+ if (!isEnabled) return;
206
+ if (!eventBus.onAny) return;
207
+ const unsub = eventBus.onAny((evt) => {
208
+ const parsed = parseLifecycleEvent(evt.type);
209
+ if (!parsed) return;
210
+ const payload = evt.payload ?? {};
211
+ if (parsed.kind === "dispatch") {
212
+ const key = `${parsed.traitName}:${String(payload["event"] ?? "")}`;
213
+ pendingRef.current.set(key, {
214
+ traitName: parsed.traitName,
215
+ event: String(payload["event"] ?? ""),
216
+ from: payload["currentState"],
217
+ timestamp: evt.timestamp
218
+ });
219
+ } else if (parsed.kind === "success" && parsed.event) {
220
+ const key = `${parsed.traitName}:${parsed.event}`;
221
+ const pending = pendingRef.current.get(key);
222
+ pendingRef.current.delete(key);
223
+ const newState = payload["newState"] ?? payload["state"] ?? "unknown";
224
+ const effects = Array.isArray(payload["clientEffects"]) ? payload["clientEffects"].map((e) => ({
225
+ type: String(e["type"] ?? "unknown"),
226
+ args: Array.isArray(e["args"]) ? e["args"] : [],
227
+ status: "executed"
228
+ })) : [];
229
+ recordTransition({
230
+ traitName: parsed.traitName,
231
+ from: pending?.from ?? "unknown",
232
+ to: newState,
233
+ event: parsed.event,
234
+ effects,
235
+ timestamp: Date.now()
236
+ });
237
+ } else if (parsed.kind === "error" && parsed.event) {
238
+ const key = `${parsed.traitName}:${parsed.event}`;
239
+ const pending = pendingRef.current.get(key);
240
+ pendingRef.current.delete(key);
241
+ const errorMsg = payload["error"] ?? "Unknown error";
242
+ recordTransition({
243
+ traitName: parsed.traitName,
244
+ from: pending?.from ?? "unknown",
245
+ to: pending?.from ?? "unknown",
246
+ // state didn't change on error
247
+ event: parsed.event,
248
+ effects: [{
249
+ type: "server-call",
250
+ args: [],
251
+ status: "failed",
252
+ error: errorMsg
253
+ }],
254
+ timestamp: Date.now()
255
+ });
256
+ }
257
+ });
258
+ registerCheck(
259
+ "verification-provider",
260
+ "VerificationProvider active (compiled path)",
261
+ "pass"
262
+ );
263
+ return unsub;
264
+ }, [isEnabled, eventBus]);
265
+ useEffect(() => {
266
+ if (!isEnabled) return;
267
+ if (!runtimeManager) return;
268
+ runtimeManager.setObserver({
269
+ onTransition(trace) {
270
+ recordTransition({
271
+ traitName: trace.traitName,
272
+ from: trace.from,
273
+ to: trace.to,
274
+ event: trace.event,
275
+ guardResult: trace.guardResult,
276
+ effects: trace.effects,
277
+ timestamp: Date.now()
278
+ });
279
+ }
280
+ });
281
+ registerCheck(
282
+ "verification-provider",
283
+ "VerificationProvider active (runtime path)",
284
+ "pass"
285
+ );
286
+ }, [isEnabled, runtimeManager]);
287
+ useEffect(() => {
288
+ if (!isEnabled) return;
289
+ bindEventBus(eventBus);
290
+ }, [isEnabled, eventBus]);
291
+ useEffect(() => {
292
+ if (!isEnabled) return;
293
+ if (traitStateGetter) {
294
+ bindTraitStateGetter(traitStateGetter);
295
+ } else if (runtimeManager?.getState) {
296
+ const mgr = runtimeManager;
297
+ bindTraitStateGetter((traitName) => mgr.getState(traitName));
298
+ }
299
+ }, [isEnabled, traitStateGetter, runtimeManager]);
300
+ return /* @__PURE__ */ jsx(Fragment, { children });
301
+ }
302
+ VerificationProvider.displayName = "VerificationProvider";
303
+ function FetchedDataBridge({ children }) {
304
+ const fetchedData = useFetchedData();
305
+ const adapter = useMemo(() => ({
306
+ getData: (entity) => fetchedData.getData(entity),
307
+ getById: (entity, id) => fetchedData.getById(entity, id),
308
+ isLoading: fetchedData.loading,
309
+ error: fetchedData.error
310
+ }), [fetchedData.getData, fetchedData.getById, fetchedData.loading, fetchedData.error]);
311
+ return /* @__PURE__ */ jsx(EntityDataProvider, { adapter, children });
312
+ }
313
+ function OrbitalProvider({
314
+ children,
315
+ themes,
316
+ defaultTheme = "wireframe",
317
+ defaultMode = "system",
318
+ targetRef,
319
+ skipTheme = false,
320
+ debug = false,
321
+ initialData,
322
+ suspense = false,
323
+ verification
324
+ }) {
325
+ const suspenseConfig = useMemo(
326
+ () => ({ enabled: suspense }),
327
+ [suspense]
328
+ );
329
+ const inner = /* @__PURE__ */ jsx(FetchedDataProvider, { initialData, children: /* @__PURE__ */ jsx(FetchedDataBridge, { children: /* @__PURE__ */ jsx(EventBusProvider, { debug, children: /* @__PURE__ */ jsx(VerificationProvider, { enabled: verification, children: /* @__PURE__ */ jsx(SelectionProvider, { debug, children: /* @__PURE__ */ jsx(SuspenseConfigProvider, { config: suspenseConfig, children }) }) }) }) }) });
330
+ if (skipTheme) {
331
+ return inner;
332
+ }
333
+ return /* @__PURE__ */ jsx(
334
+ ThemeProvider,
335
+ {
336
+ themes,
337
+ defaultTheme,
338
+ defaultMode,
339
+ targetRef,
340
+ children: inner
341
+ }
342
+ );
343
+ }
344
+ OrbitalProvider.displayName = "OrbitalProvider";
345
+ var OfflineModeContext = createContext(null);
346
+ function OfflineModeProvider({
347
+ children,
348
+ ...executorOptions
349
+ }) {
350
+ const [forceOffline, setForceOffline] = useState(false);
351
+ const executor = useOfflineExecutor(executorOptions);
352
+ const effectivelyOffline = executor.isOffline || forceOffline;
353
+ const contextValue = useMemo(
354
+ () => ({
355
+ ...executor,
356
+ forceOffline,
357
+ setForceOffline,
358
+ effectivelyOffline
359
+ }),
360
+ [executor, forceOffline, effectivelyOffline]
361
+ );
362
+ return /* @__PURE__ */ jsx(OfflineModeContext.Provider, { value: contextValue, children });
363
+ }
364
+ function useOfflineMode() {
365
+ const context = useContext(OfflineModeContext);
366
+ if (!context) {
367
+ throw new Error("useOfflineMode must be used within OfflineModeProvider");
368
+ }
369
+ return context;
370
+ }
371
+ function useOptionalOfflineMode() {
372
+ return useContext(OfflineModeContext);
373
+ }
374
+
375
+ export { FetchedDataContext, FetchedDataProvider, OfflineModeProvider, OrbitalProvider, VerificationProvider, useFetchedData, useFetchedDataContext, useFetchedEntity, useOfflineMode, useOptionalOfflineMode };