teraprox-core-sdk 0.3.2 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -2,18 +2,296 @@ import {
2
2
  CoreServiceContext,
3
3
  DevAutoLogin,
4
4
  FederatedBridge,
5
+ NullObservabilityAdapter,
5
6
  StandaloneProvider,
6
7
  createReducersBundle
7
- } from "./chunk-PATJPPYP.mjs";
8
+ } from "./chunk-JK654W4P.mjs";
9
+
10
+ // src/adapters/null/NullObjectAdapters.ts
11
+ var NullToastService = class {
12
+ success(message) {
13
+ console.log(`[Toast Fallback] SUCCESS: ${message}`);
14
+ }
15
+ warning(message) {
16
+ console.warn(`[Toast Fallback] WARNING: ${message}`);
17
+ }
18
+ error(message) {
19
+ console.error(`[Toast Fallback] ERROR: ${message}`);
20
+ }
21
+ info(message) {
22
+ console.info(`[Toast Fallback] INFO: ${message}`);
23
+ }
24
+ };
25
+ var NullHttpController = class {
26
+ logAndResolve(method, path) {
27
+ console.warn(`[Http Fallback] MOCK ${method} requisitado para ${path} (Nenhum adapter injetado)`);
28
+ return Promise.resolve([]);
29
+ }
30
+ get(path) {
31
+ return this.logAndResolve("GET", path);
32
+ }
33
+ post(path) {
34
+ return this.logAndResolve("POST", path);
35
+ }
36
+ put(path) {
37
+ return this.logAndResolve("PUT", path);
38
+ }
39
+ patch(path) {
40
+ return this.logAndResolve("PATCH", path);
41
+ }
42
+ delete(path, id) {
43
+ return this.logAndResolve("DELETE", `${path}/${id}`);
44
+ }
45
+ deleteSimple(path) {
46
+ return this.logAndResolve("DELETE", path);
47
+ }
48
+ save(path) {
49
+ return this.logAndResolve("SAVE", path);
50
+ }
51
+ read(path, id) {
52
+ return this.logAndResolve("READ", `${path}/${id}`);
53
+ }
54
+ readAll(path) {
55
+ return this.logAndResolve("READ_ALL", path);
56
+ }
57
+ readAllwithPage(path) {
58
+ return this.logAndResolve("READ_ALL_PAGE", path);
59
+ }
60
+ bulkDelete(path) {
61
+ return this.logAndResolve("BULK_DELETE", path);
62
+ }
63
+ };
64
+ var NullCoreService = {
65
+ toast: new NullToastService(),
66
+ createController: (context) => {
67
+ console.warn(`[CoreService Fallback] createController chamado para "${context}" sem Adapter. Usando NullHttpController.`);
68
+ return new NullHttpController();
69
+ },
70
+ subscribe: (mo) => console.log(`[CoreService Fallback] Inscri\xE7\xE3o simulada RTDB: ${mo.context}`),
71
+ unsubscribe: (mo) => console.log(`[CoreService Fallback] Desinscri\xE7\xE3o simulada RTDB: ${mo.context}`),
72
+ subscribeEvent: (evt) => console.log(`[CoreService Fallback] Inscri\xE7\xE3o Evento: ${evt}`),
73
+ unsubscribeEvent: (evt) => console.log(`[CoreService Fallback] Desinscri\xE7\xE3o Evento: ${evt}`),
74
+ handleLogout: () => console.log(`[CoreService Fallback] Realizando logout...`),
75
+ hostedByCore: false,
76
+ observability: new NullObservabilityAdapter()
77
+ };
78
+
79
+ // src/utils/tracing.ts
80
+ function toHex(bytes) {
81
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
82
+ }
83
+ function generateTraceId() {
84
+ const bytes = new Uint8Array(16);
85
+ crypto.getRandomValues(bytes);
86
+ return toHex(bytes);
87
+ }
88
+ function generateSpanId() {
89
+ const bytes = new Uint8Array(8);
90
+ crypto.getRandomValues(bytes);
91
+ return toHex(bytes);
92
+ }
93
+ function buildTraceparent(traceId, spanId, sampled = true) {
94
+ return `00-${traceId}-${spanId}-${sampled ? "01" : "00"}`;
95
+ }
96
+
97
+ // src/adapters/TracingHttpAdapter.ts
98
+ var TracingHttpAdapter = class {
99
+ constructor(endpoint) {
100
+ this.endpoint = endpoint;
101
+ this.traceId = generateTraceId();
102
+ }
103
+ mergeHeaders(extraHeaders, withContentType = false) {
104
+ const spanId = generateSpanId();
105
+ const headers = {
106
+ traceparent: buildTraceparent(this.traceId, spanId),
107
+ ...extraHeaders
108
+ };
109
+ if (withContentType && !headers["Content-Type"]) {
110
+ headers["Content-Type"] = "application/json";
111
+ }
112
+ return headers;
113
+ }
114
+ async request(method, extraPath, data, extraHeaders) {
115
+ const url = extraPath ? `${this.endpoint}/${extraPath}` : this.endpoint;
116
+ const isFormData = data instanceof FormData;
117
+ const headers = this.mergeHeaders(extraHeaders, data !== void 0 && !isFormData);
118
+ try {
119
+ const res = await fetch(url, {
120
+ method,
121
+ headers,
122
+ body: data !== void 0 ? isFormData ? data : JSON.stringify(data) : void 0
123
+ });
124
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
125
+ return await res.json();
126
+ } catch (e) {
127
+ console.warn(`[TracingHttpAdapter] Falha ao fazer ${method} para ${url}:`, e);
128
+ return [];
129
+ }
130
+ }
131
+ get(path) {
132
+ return this.request("GET", path || "");
133
+ }
134
+ post(path, data, extraHeaders) {
135
+ return this.request("POST", path || "", data, extraHeaders);
136
+ }
137
+ put(path, data, extraHeaders) {
138
+ return this.request("PUT", path || "", data, extraHeaders);
139
+ }
140
+ patch(path, data, extraHeaders) {
141
+ return this.request("PATCH", path || "", data, extraHeaders);
142
+ }
143
+ delete(path, id, extraHeaders) {
144
+ return this.request("DELETE", `${path || ""}/${id}`, void 0, extraHeaders);
145
+ }
146
+ deleteSimple(path, extraHeaders) {
147
+ return this.request("DELETE", path || "", void 0, extraHeaders);
148
+ }
149
+ save(path, data, extraHeaders) {
150
+ return this.request("POST", path || "", data, extraHeaders);
151
+ }
152
+ read(path, id, extraHeaders) {
153
+ return this.request("GET", `${path || ""}/${id}`, void 0, extraHeaders);
154
+ }
155
+ readAll(path, extraHeaders) {
156
+ return this.request("GET", path || "", void 0, extraHeaders);
157
+ }
158
+ readAllwithPage(path) {
159
+ return this.request("GET", path || "");
160
+ }
161
+ bulkDelete(path, ids, extraHeaders) {
162
+ return this.request("DELETE", path || "", ids, extraHeaders);
163
+ }
164
+ };
165
+
166
+ // src/factories/CoreServiceBuilder.ts
167
+ var FetchHttpAdapter = class {
168
+ constructor(endpoint) {
169
+ this.endpoint = endpoint;
170
+ }
171
+ async request(method, extraPath, data) {
172
+ const url = extraPath ? `${this.endpoint}/${extraPath}` : this.endpoint;
173
+ try {
174
+ const res = await fetch(url, {
175
+ method,
176
+ headers: data ? { "Content-Type": "application/json" } : void 0,
177
+ body: data ? JSON.stringify(data) : void 0
178
+ });
179
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
180
+ return await res.json();
181
+ } catch (e) {
182
+ console.warn(`[FetchHttpAdapter] Falha ao fazer ${method} para ${url}:`, e);
183
+ return [];
184
+ }
185
+ }
186
+ get(path) {
187
+ return this.request("GET", path || "");
188
+ }
189
+ post(path, data) {
190
+ return this.request("POST", path || "", data);
191
+ }
192
+ put(path, data) {
193
+ return this.request("PUT", path || "", data);
194
+ }
195
+ patch(path, data) {
196
+ return this.request("PATCH", path || "", data);
197
+ }
198
+ delete(path, id) {
199
+ return this.request("DELETE", `${path || ""}/${id}`);
200
+ }
201
+ deleteSimple(path) {
202
+ return this.request("DELETE", path || "");
203
+ }
204
+ save(path, data) {
205
+ return this.request("POST", path || "", data);
206
+ }
207
+ read(path, id) {
208
+ return this.request("GET", `${path || ""}/${id}`);
209
+ }
210
+ readAll(path) {
211
+ return this.request("GET", path || "");
212
+ }
213
+ readAllwithPage(path) {
214
+ return this.request("GET", path || "");
215
+ }
216
+ bulkDelete(path) {
217
+ return this.request("DELETE", path || "");
218
+ }
219
+ };
220
+ var CoreServiceBuilder = class {
221
+ constructor() {
222
+ this._toast = new NullToastService();
223
+ this._hostedByCore = false;
224
+ this._observability = new NullObservabilityAdapter();
225
+ this._tracing = false;
226
+ }
227
+ withToast(toast) {
228
+ this._toast = toast;
229
+ return this;
230
+ }
231
+ withHttpEndpoint(url) {
232
+ this._httpEndpoint = url;
233
+ return this;
234
+ }
235
+ withRtdbConfig(config) {
236
+ this._rtdbConfig = config;
237
+ return this;
238
+ }
239
+ setHostedByCore(hosted) {
240
+ this._hostedByCore = hosted;
241
+ return this;
242
+ }
243
+ withObservability(observability) {
244
+ this._observability = observability;
245
+ return this;
246
+ }
247
+ /**
248
+ * Ativa Distributed Tracing W3C nos controllers HTTP.
249
+ * Quando habilitado, `createController` retorna `TracingHttpAdapter` em vez de `FetchHttpAdapter`.
250
+ * O header `traceparent` é injetado em todas as requisições automaticamente.
251
+ */
252
+ withTracing(enabled = true) {
253
+ this._tracing = enabled;
254
+ return this;
255
+ }
256
+ build() {
257
+ return {
258
+ toast: this._toast,
259
+ createController: (context, baseEndPoint) => {
260
+ const endpoint = baseEndPoint != null ? baseEndPoint : this._httpEndpoint ? `${this._httpEndpoint}/${context}` : null;
261
+ if (!endpoint) {
262
+ console.warn(`[CoreServiceBuilder] HttpEndpoint nulo para "${context}". Usando NullHttpController.`);
263
+ return new NullHttpController();
264
+ }
265
+ return this._tracing ? new TracingHttpAdapter(endpoint) : new FetchHttpAdapter(endpoint);
266
+ },
267
+ subscribe: (mo) => {
268
+ console.log(`[CoreServiceBuilder RTDB] Subscribe > ${mo.context}`);
269
+ },
270
+ unsubscribe: (mo) => {
271
+ console.log(`[CoreServiceBuilder RTDB] Unsubscribe > ${mo.context}`);
272
+ },
273
+ subscribeEvent: (evt) => {
274
+ },
275
+ unsubscribeEvent: (evt) => {
276
+ },
277
+ handleLogout: () => console.log("Logout invocado no Standalone mode"),
278
+ hostedByCore: this._hostedByCore,
279
+ observability: this._observability
280
+ };
281
+ }
282
+ };
8
283
 
9
284
  // src/hooks/useCoreService.ts
10
285
  import { useContext } from "react";
11
286
  function useCoreService() {
12
287
  const ctx = useContext(CoreServiceContext);
13
288
  if (!ctx) {
14
- throw new Error(
15
- "useCoreService must be used within a CoreServiceProvider. Are you running outside Core (standalone mode)?"
16
- );
289
+ if (process.env.NODE_ENV !== "production") {
290
+ console.warn(
291
+ "[useCoreService] rodando sem Provider. O SDK ativou os Fallbacks (Null Object Pattern)."
292
+ );
293
+ }
294
+ return NullCoreService;
17
295
  }
18
296
  return ctx;
19
297
  }
@@ -114,6 +392,11 @@ function useNavigator(config) {
114
392
  );
115
393
  }
116
394
 
395
+ // src/hooks/useObservability.ts
396
+ function useObservability() {
397
+ return useCoreService().observability;
398
+ }
399
+
117
400
  // src/hooks/useFetchData.ts
118
401
  import { useState, useCallback as useCallback2, useRef } from "react";
119
402
  function useFetchData() {
@@ -1169,6 +1452,39 @@ var {
1169
1452
  } = pickerSlice.actions;
1170
1453
  var pickerReducer_default = pickerSlice.reducer;
1171
1454
 
1455
+ // src/utils/webVitals.ts
1456
+ function initWebVitals(observability) {
1457
+ if (typeof PerformanceObserver === "undefined") return;
1458
+ try {
1459
+ const lcpObserver = new PerformanceObserver((list) => {
1460
+ const entries = list.getEntries();
1461
+ if (!entries.length) return;
1462
+ const last = entries[entries.length - 1];
1463
+ observability.captureVitals({ name: "LCP", value: Math.round(last.startTime), unit: "ms" });
1464
+ });
1465
+ lcpObserver.observe({ type: "largest-contentful-paint", buffered: true });
1466
+ } catch (_) {
1467
+ }
1468
+ try {
1469
+ let clsValue = 0;
1470
+ const clsObserver = new PerformanceObserver((list) => {
1471
+ for (const entry of list.getEntries()) {
1472
+ const ls = entry;
1473
+ if (!ls.hadRecentInput && typeof ls.value === "number") {
1474
+ clsValue += ls.value;
1475
+ }
1476
+ }
1477
+ observability.captureVitals({
1478
+ name: "CLS",
1479
+ value: parseFloat(clsValue.toFixed(4)),
1480
+ unit: "score"
1481
+ });
1482
+ });
1483
+ clsObserver.observe({ type: "layout-shift", buffered: true });
1484
+ } catch (_) {
1485
+ }
1486
+ }
1487
+
1172
1488
  // src/utils/dateUtils.ts
1173
1489
  import dayjs from "dayjs";
1174
1490
  function formatDate(date, format = "DD/MM/YYYY") {
@@ -1213,13 +1529,21 @@ function isBlank(str) {
1213
1529
  return !str || str.trim().length === 0;
1214
1530
  }
1215
1531
  export {
1532
+ CoreServiceBuilder,
1216
1533
  CoreServiceContext,
1217
1534
  DevAutoLogin,
1218
1535
  FederatedBridge,
1536
+ FetchHttpAdapter,
1537
+ NullCoreService,
1538
+ NullHttpController,
1539
+ NullObservabilityAdapter,
1540
+ NullToastService,
1219
1541
  RecursoDisplayer_default as RecursoDisplayer,
1220
1542
  StandaloneProvider,
1543
+ TracingHttpAdapter,
1221
1544
  addDays,
1222
1545
  branchLevelReducer_default as branchLevelReducer,
1546
+ buildTraceparent,
1223
1547
  capitalize,
1224
1548
  clearBranchLevelForm,
1225
1549
  clearPicker,
@@ -1227,6 +1551,9 @@ export {
1227
1551
  daysBetween,
1228
1552
  formatDate,
1229
1553
  formatDateTime,
1554
+ generateSpanId,
1555
+ generateTraceId,
1556
+ initWebVitals,
1230
1557
  isBlank,
1231
1558
  isDateAfter,
1232
1559
  isDateBefore,
@@ -1255,6 +1582,7 @@ export {
1255
1582
  useMatchingObject,
1256
1583
  useNavigator,
1257
1584
  useNotifications,
1585
+ useObservability,
1258
1586
  usePostData,
1259
1587
  useSmartSearch,
1260
1588
  useToast,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teraprox-core-sdk",
3
- "version": "0.3.2",
3
+ "version": "0.3.5",
4
4
  "description": "Contrato tipado Core ↔ Federados — interfaces, context, hooks e componentes compartilhados",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -46,7 +46,9 @@
46
46
  "react-router-dom": ">=6.0.0"
47
47
  },
48
48
  "peerDependenciesMeta": {
49
- "firebase": { "optional": true }
49
+ "firebase": {
50
+ "optional": true
51
+ }
50
52
  },
51
53
  "devDependencies": {
52
54
  "@reduxjs/toolkit": "^2.0.0",
@@ -60,5 +62,5 @@
60
62
  "tsup": "^8.0.0",
61
63
  "typescript": "^5.3.0"
62
64
  },
63
- "license": "UNLICENSED"
65
+ "license": "MIT"
64
66
  }