@oondemand/oon-core-front 0.5.6 → 0.6.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.
@@ -0,0 +1,694 @@
1
+ // src/api/ApiProvider.tsx
2
+ import { createContext, useContext, useMemo } from "react";
3
+ import { useQuery } from "@tanstack/react-query";
4
+
5
+ // src/api/coreClient.ts
6
+ function createCoreClient(http) {
7
+ return {
8
+ async metadata() {
9
+ const { data } = await http.get("/core/metadata");
10
+ return data;
11
+ },
12
+ async models() {
13
+ const { data } = await http.get("/core/models");
14
+ return data.results;
15
+ },
16
+ async model(name) {
17
+ const { data } = await http.get(`/core/models/${name}`);
18
+ return data;
19
+ },
20
+ async permissions() {
21
+ const { data } = await http.get(
22
+ "/core/permissions"
23
+ );
24
+ return data.results;
25
+ },
26
+ async menus() {
27
+ const { data } = await http.get("/core/menus");
28
+ return data.results;
29
+ }
30
+ };
31
+ }
32
+
33
+ // src/api/createRestClient.ts
34
+ import axios2 from "axios";
35
+
36
+ // src/api/errorNormalizer.ts
37
+ import axios from "axios";
38
+ function normalizeError(err) {
39
+ if (axios.isAxiosError(err)) {
40
+ const status = err.response?.status;
41
+ const data = err.response?.data;
42
+ const responseRequestId = err.response?.headers?.["x-request-id"] ?? err.response?.headers?.["x-correlation-id"];
43
+ const contentType = String(err.response?.headers?.["content-type"] || "");
44
+ const isTextPayload = typeof data === "string";
45
+ const isProbablyHtml = isTextPayload && /<html|<!doctype/i.test(data);
46
+ if (isTextPayload || contentType && !contentType.includes("json")) {
47
+ return {
48
+ code: codeFromStatus(status),
49
+ message: nonJsonMessage(status, err.config?.url, isProbablyHtml),
50
+ status,
51
+ details: isProbablyHtml ? void 0 : data,
52
+ requestId: responseRequestId
53
+ };
54
+ }
55
+ const rawError = data?.error;
56
+ const envelope = rawError && typeof rawError === "object" ? rawError : void 0;
57
+ const legacyDetails = envelope && envelope.details === void 0 && envelope.field ? envelope : void 0;
58
+ const rawTechnicalDetails = typeof rawError === "string" ? { technicalMessage: rawError } : void 0;
59
+ return {
60
+ code: envelope?.code || data?.code || codeFromStatus(status),
61
+ message: envelope?.message || data?.message || err.message || "Erro inesperado.",
62
+ status,
63
+ details: envelope?.details ?? legacyDetails ?? data?.details ?? rawTechnicalDetails,
64
+ requestId: envelope?.requestId || data?.requestId || responseRequestId
65
+ };
66
+ }
67
+ if (err instanceof Error) {
68
+ return { code: "UNKNOWN", message: err.message };
69
+ }
70
+ return { code: "UNKNOWN", message: "Erro inesperado." };
71
+ }
72
+ function nonJsonMessage(status, url, html) {
73
+ const path = url ? ` (${url})` : "";
74
+ if (status === 404) {
75
+ return `Rota da API n\xE3o encontrada${path}. Verifique VITE_API_URL, porta do backend e modo da Central.`;
76
+ }
77
+ if (html) {
78
+ return `Backend retornou HTML em vez de JSON${path}. Verifique se o frontend est\xE1 apontando para a API correta.`;
79
+ }
80
+ return `Backend retornou resposta n\xE3o JSON${path}. Verifique a configura\xE7\xE3o da API.`;
81
+ }
82
+ function codeFromStatus(status) {
83
+ switch (status) {
84
+ case 400:
85
+ return "VALIDATION_ERROR";
86
+ case 401:
87
+ return "UNAUTHORIZED";
88
+ case 403:
89
+ return "FORBIDDEN";
90
+ case 404:
91
+ return "NOT_FOUND";
92
+ case 409:
93
+ return "CONFLICT";
94
+ default:
95
+ return status && status >= 500 ? "SERVER_ERROR" : "REQUEST_ERROR";
96
+ }
97
+ }
98
+
99
+ // src/api/createRestClient.ts
100
+ function readCookie(name) {
101
+ try {
102
+ const prefix = `${name}=`;
103
+ const value = document.cookie.split(";").map((item) => item.trim()).find((item) => item.startsWith(prefix));
104
+ return value ? decodeURIComponent(value.slice(prefix.length)) : null;
105
+ } catch {
106
+ return null;
107
+ }
108
+ }
109
+ function createRestClient(deps) {
110
+ const { api, getToken, getTenantId, onUnauthorized, appId, runtimeMode = "platform" } = deps;
111
+ const prefix = api.versionPrefix ?? "";
112
+ const instance = axios2.create({
113
+ baseURL: `${api.baseUrl.replace(/\/$/, "")}${prefix}`,
114
+ timeout: api.timeout ?? 3e4,
115
+ withCredentials: runtimeMode === "local"
116
+ });
117
+ instance.interceptors.request.use((config) => {
118
+ const token = runtimeMode === "local" ? null : getToken();
119
+ if (token && !config.headers.Authorization) {
120
+ config.headers.Authorization = `Bearer ${token}`;
121
+ }
122
+ const explicitTenantId = config.headers.get("x-oon-tenant-id");
123
+ const tenantId = explicitTenantId || getTenantId?.();
124
+ if (tenantId) config.headers.set("x-oon-tenant-id", String(tenantId));
125
+ else config.headers.delete("x-oon-tenant-id");
126
+ if (appId) config.headers["x-oon-app"] = appId;
127
+ if (runtimeMode === "local" && !["get", "head", "options"].includes(String(config.method || "get").toLowerCase())) {
128
+ const csrf = readCookie("oon_local_csrf");
129
+ if (csrf) config.headers.set("x-oon-local-csrf", csrf);
130
+ }
131
+ return config;
132
+ });
133
+ instance.interceptors.response.use(
134
+ (res) => res,
135
+ (error) => {
136
+ const normalized = normalizeError(error);
137
+ const requestPath = String(error.config?.url || "").split("?")[0].replace(/\/$/, "");
138
+ const isLogin = requestPath.endsWith("/auth/autenticar");
139
+ if (normalized.status === 401 && !isLogin) onUnauthorized?.();
140
+ return Promise.reject(normalized);
141
+ }
142
+ );
143
+ return instance;
144
+ }
145
+ function auditHeaders(audit) {
146
+ if (!audit) return {};
147
+ const headers = {};
148
+ if (audit.origem) headers["x-oon-origin"] = audit.origem;
149
+ if (audit.module) headers["x-oon-module"] = audit.module;
150
+ if (audit.entity) headers["x-oon-entity"] = audit.entity;
151
+ return headers;
152
+ }
153
+
154
+ // src/api/resourceClient.ts
155
+ var META_KEYS = /* @__PURE__ */ new Set(["pagination", "results", "message", "requestId"]);
156
+ function unwrap(data) {
157
+ for (const [key, value] of Object.entries(data)) {
158
+ if (!META_KEYS.has(key) && value && typeof value === "object") {
159
+ return value;
160
+ }
161
+ }
162
+ return data;
163
+ }
164
+ function createResourceClient(http, basePath) {
165
+ const base = `/${basePath.replace(/^\/+/, "")}`;
166
+ return {
167
+ async list(params) {
168
+ const { data } = await http.get(base, { params });
169
+ return data;
170
+ },
171
+ async get(id) {
172
+ const { data } = await http.get(`${base}/${id}`);
173
+ return unwrap(data);
174
+ },
175
+ async create(body, audit) {
176
+ const { data } = await http.post(base, body, {
177
+ headers: auditHeaders(audit ?? { origem: "form" })
178
+ });
179
+ return unwrap(data);
180
+ },
181
+ async update(id, body, audit) {
182
+ const { data } = await http.put(`${base}/${id}`, body, {
183
+ headers: auditHeaders(audit ?? { origem: "form" })
184
+ });
185
+ return unwrap(data);
186
+ },
187
+ async patch(id, body, audit) {
188
+ const { data } = await http.patch(`${base}/${id}`, body, {
189
+ headers: auditHeaders(audit ?? { origem: "datagrid" })
190
+ });
191
+ return unwrap(data);
192
+ },
193
+ async delete(id, audit) {
194
+ const { data } = await http.delete(`${base}/${id}`, {
195
+ headers: auditHeaders(audit ?? { origem: "datagrid" })
196
+ });
197
+ return unwrap(data);
198
+ },
199
+ async importMany(rows, audit) {
200
+ const { data } = await http.post(
201
+ `${base}/import`,
202
+ { rows },
203
+ { headers: auditHeaders(audit ?? { origem: "import" }) }
204
+ );
205
+ return data;
206
+ },
207
+ async exportAll(params) {
208
+ const { data } = await http.get(`${base}/export`, { params });
209
+ return Array.isArray(data) ? data : data.results;
210
+ }
211
+ };
212
+ }
213
+
214
+ // src/api/ApiProvider.tsx
215
+ import { jsx } from "react/jsx-runtime";
216
+ var ApiContext = createContext(null);
217
+ function ApiProvider({ http, children }) {
218
+ const value = useMemo(() => {
219
+ const cache = /* @__PURE__ */ new Map();
220
+ return {
221
+ http,
222
+ core: createCoreClient(http),
223
+ resource(basePath) {
224
+ let client = cache.get(basePath);
225
+ if (!client) {
226
+ client = createResourceClient(http, basePath);
227
+ cache.set(basePath, client);
228
+ }
229
+ return client;
230
+ }
231
+ };
232
+ }, [http]);
233
+ return /* @__PURE__ */ jsx(ApiContext.Provider, { value, children });
234
+ }
235
+ function useOonApi() {
236
+ const ctx = useContext(ApiContext);
237
+ if (!ctx) throw new Error("useOonApi deve ser usado dentro de <ApiProvider> (oonCoreFront.start).");
238
+ return ctx;
239
+ }
240
+ function useOonResource(basePath) {
241
+ const { resource } = useOonApi();
242
+ return useMemo(() => resource(basePath), [resource, basePath]);
243
+ }
244
+ function useCoreMetadata() {
245
+ const { core } = useOonApi();
246
+ return useQuery({
247
+ queryKey: ["oon", "core", "metadata"],
248
+ queryFn: () => core.metadata(),
249
+ staleTime: 5 * 60 * 1e3
250
+ });
251
+ }
252
+ function useModelSchema(name, enabled = true) {
253
+ const { core } = useOonApi();
254
+ return useQuery({
255
+ queryKey: ["oon", "core", "model", name],
256
+ queryFn: () => core.model(name),
257
+ staleTime: 5 * 60 * 1e3,
258
+ enabled: enabled && !!name
259
+ });
260
+ }
261
+
262
+ // src/security/TenantProvider.tsx
263
+ import {
264
+ createContext as createContext2,
265
+ useCallback,
266
+ useContext as useContext2,
267
+ useMemo as useMemo2,
268
+ useState
269
+ } from "react";
270
+ import { useQueryClient } from "@tanstack/react-query";
271
+ import { jsx as jsx2 } from "react/jsx-runtime";
272
+ var TenantContext = createContext2(null);
273
+ function applyHeader(http, tenantId) {
274
+ if (tenantId) {
275
+ http.defaults.headers.common["x-oon-tenant-id"] = tenantId;
276
+ return;
277
+ }
278
+ delete http.defaults.headers.common["x-oon-tenant-id"];
279
+ }
280
+ function TenantProvider({ http, storage, children }) {
281
+ const queryClient = useQueryClient();
282
+ const [activeTenantId, setActiveTenantId] = useState(() => storage.get());
283
+ const replaceTenant = useCallback(
284
+ (tenantId) => {
285
+ const normalized = String(tenantId ?? "").trim() || null;
286
+ if (normalized) storage.set(normalized);
287
+ else storage.clear();
288
+ applyHeader(http, normalized);
289
+ setActiveTenantId(normalized);
290
+ queryClient.clear();
291
+ },
292
+ [http, queryClient, storage]
293
+ );
294
+ const value = useMemo2(
295
+ () => ({
296
+ activeTenantId,
297
+ switchTenant(tenantId) {
298
+ if (!tenantId?.trim()) throw new Error("tenantId \xE9 obrigat\xF3rio.");
299
+ replaceTenant(tenantId);
300
+ },
301
+ clearTenant() {
302
+ replaceTenant(null);
303
+ }
304
+ }),
305
+ [activeTenantId, replaceTenant]
306
+ );
307
+ return /* @__PURE__ */ jsx2(TenantContext.Provider, { value, children });
308
+ }
309
+ function useOptionalOonTenant() {
310
+ return useContext2(TenantContext);
311
+ }
312
+ function useOonTenant() {
313
+ const value = useOptionalOonTenant();
314
+ if (!value) throw new Error("useOonTenant deve ser usado dentro de TenantProvider.");
315
+ return value;
316
+ }
317
+
318
+ // src/security/AuthProvider.tsx
319
+ import {
320
+ createContext as createContext4,
321
+ useCallback as useCallback3,
322
+ useContext as useContext4,
323
+ useEffect as useEffect2,
324
+ useState as useState3
325
+ } from "react";
326
+
327
+ // src/security/tokenStorage.ts
328
+ var STORAGE_KEY = "oon.auth.token";
329
+ function createTokenStorage(namespace) {
330
+ const key = namespace ? `${STORAGE_KEY}.${namespace}` : STORAGE_KEY;
331
+ return {
332
+ get() {
333
+ try {
334
+ return window.localStorage.getItem(key);
335
+ } catch {
336
+ return null;
337
+ }
338
+ },
339
+ set(token) {
340
+ try {
341
+ window.localStorage.setItem(key, token);
342
+ } catch {
343
+ }
344
+ },
345
+ clear() {
346
+ try {
347
+ window.localStorage.removeItem(key);
348
+ } catch {
349
+ }
350
+ }
351
+ };
352
+ }
353
+ function captureTokenFromUrl(tokenParam = "code") {
354
+ try {
355
+ const url = new URL(window.location.href);
356
+ const token = url.searchParams.get(tokenParam);
357
+ if (!token) return null;
358
+ url.searchParams.delete(tokenParam);
359
+ window.history.replaceState({}, document.title, url.pathname + url.search + url.hash);
360
+ return token;
361
+ } catch {
362
+ return null;
363
+ }
364
+ }
365
+
366
+ // src/security/ActivationProvider.tsx
367
+ import {
368
+ createContext as createContext3,
369
+ useCallback as useCallback2,
370
+ useContext as useContext3,
371
+ useEffect,
372
+ useState as useState2
373
+ } from "react";
374
+ import { jsx as jsx3 } from "react/jsx-runtime";
375
+ var ActivationContext = createContext3(null);
376
+ function mapStatus(data) {
377
+ if (["ativa", "ativa_local"].includes(data.status)) return "active";
378
+ if (data.status === "suspensa") return "suspended";
379
+ if (data.status === "revogada") return "revoked";
380
+ if (data.status === "configurando") return "configuring";
381
+ if (data.status === "erro" && !data.podeAtivar) return "error";
382
+ return "notActivated";
383
+ }
384
+ function ActivationProvider({
385
+ http,
386
+ children
387
+ }) {
388
+ const [status, setStatus] = useState2("loading");
389
+ const [details, setDetails] = useState2(null);
390
+ const refresh = useCallback2(async () => {
391
+ setStatus("loading");
392
+ try {
393
+ const response = await http.get("/ativacao/status");
394
+ setDetails(response.data);
395
+ setStatus(mapStatus(response.data));
396
+ } catch {
397
+ setStatus("error");
398
+ }
399
+ }, [http]);
400
+ useEffect(() => {
401
+ void refresh();
402
+ }, [refresh]);
403
+ return /* @__PURE__ */ jsx3(ActivationContext.Provider, { value: { status, details, refresh }, children });
404
+ }
405
+ function useOptionalActivation() {
406
+ return useContext3(ActivationContext);
407
+ }
408
+ function useActivation() {
409
+ const context = useOptionalActivation();
410
+ if (!context) {
411
+ throw new Error(
412
+ "useActivation deve ser usado dentro de <ActivationProvider>."
413
+ );
414
+ }
415
+ return context;
416
+ }
417
+
418
+ // src/security/AuthProvider.tsx
419
+ import { jsx as jsx4 } from "react/jsx-runtime";
420
+ var AuthContext = createContext4(null);
421
+ function errorStatus(error) {
422
+ return error?.status;
423
+ }
424
+ function encodeBasicCredentials(email, password) {
425
+ const bytes = new TextEncoder().encode(`${email}:${password}`);
426
+ let binary = "";
427
+ for (const byte of bytes) binary += String.fromCharCode(byte);
428
+ return window.btoa(binary);
429
+ }
430
+ function normalizeList(value) {
431
+ if (Array.isArray(value)) return value.map(String).filter(Boolean);
432
+ if (typeof value === "string") return value.split(/[\s,;]+/).map((item) => item.trim()).filter(Boolean);
433
+ return [];
434
+ }
435
+ function unique(values) {
436
+ return values.filter((item, index, all) => Boolean(item) && all.indexOf(item) === index);
437
+ }
438
+ function extractCapabilities(user) {
439
+ if (!user) return [];
440
+ return unique([
441
+ ...normalizeList(user.capabilities),
442
+ ...normalizeList(user.permissoes?.capabilities),
443
+ ...normalizeList(user.aplicativo?.capabilities),
444
+ ...normalizeList(user.aplicativo?.permissoes?.capabilities)
445
+ ]);
446
+ }
447
+ function explicitPermissions(user) {
448
+ if (!user) return [];
449
+ const value = user.permissions;
450
+ return Array.isArray(value) ? unique(value.map(String)) : null;
451
+ }
452
+ function can(user, permission) {
453
+ if (!permission) return true;
454
+ const required = Array.isArray(permission) ? permission : [permission];
455
+ if (!required.length) return true;
456
+ const canonical = explicitPermissions(user);
457
+ if (canonical) {
458
+ return canonical.includes("*") || required.some((value) => canonical.includes(value));
459
+ }
460
+ const roles = unique(normalizeList(user?.roleCode).concat(normalizeList(user?.tipo)));
461
+ const capabilities = extractCapabilities(user);
462
+ if (roles.includes("admin") || roles.includes("master")) return true;
463
+ return required.some((value) => roles.includes(value) || capabilities.includes(value));
464
+ }
465
+ function AuthProvider({ http, storage, auth, loginUrl, runtimeMode = "platform", children }) {
466
+ const tokenParam = auth?.tokenParam ?? "code";
467
+ const devToken = auth?.devToken?.trim() || null;
468
+ const [user, setUser] = useState3(null);
469
+ const [status, setStatus] = useState3("loading");
470
+ const activation = useOptionalActivation();
471
+ const tenantContext = useOptionalOonTenant();
472
+ const redirectToExternalLogin = useCallback3(() => {
473
+ if (runtimeMode === "local") return;
474
+ if (loginUrl) window.location.href = loginUrl;
475
+ }, [loginUrl, runtimeMode]);
476
+ const applyUser = useCallback3((next) => {
477
+ setUser(next);
478
+ const tenant = next.tenant;
479
+ const resolvedTenantId = String(next.tenantId || tenant?.id || "").trim();
480
+ if (resolvedTenantId && tenantContext?.activeTenantId !== resolvedTenantId) {
481
+ tenantContext?.switchTenant(resolvedTenantId);
482
+ }
483
+ const firstAccess = Boolean(
484
+ next.mustChangePassword || next.firstAccessPending
485
+ );
486
+ setStatus(firstAccess ? "first-access" : "authenticated");
487
+ }, [tenantContext]);
488
+ const applyValidationError = useCallback3(
489
+ (error) => {
490
+ const currentStatus = errorStatus(error);
491
+ setUser(null);
492
+ if (currentStatus === 403) {
493
+ setStatus("forbidden");
494
+ return;
495
+ }
496
+ if (currentStatus === 401) {
497
+ storage.clear();
498
+ setStatus("unauthenticated");
499
+ redirectToExternalLogin();
500
+ return;
501
+ }
502
+ setStatus("error");
503
+ },
504
+ [storage, redirectToExternalLogin]
505
+ );
506
+ const validateSession = useCallback3(async () => {
507
+ const response = await http.get("/auth/validar-token");
508
+ applyUser(response.data.usuario);
509
+ }, [http, applyUser]);
510
+ const retry = useCallback3(async () => {
511
+ setStatus("loading");
512
+ try {
513
+ await validateSession();
514
+ } catch (error) {
515
+ applyValidationError(error);
516
+ throw error;
517
+ }
518
+ }, [validateSession, applyValidationError]);
519
+ const login = useCallback3(
520
+ async (email, password, tenantId) => {
521
+ if (runtimeMode === "local") {
522
+ const error = {
523
+ code: "LOCAL_LOGIN_NOT_SUPPORTED",
524
+ message: "O runtime local n\xE3o utiliza conta da plataforma.",
525
+ status: 403
526
+ };
527
+ throw error;
528
+ }
529
+ setStatus("loading");
530
+ setUser(null);
531
+ try {
532
+ const normalizedTenantId = String(tenantId || "").trim();
533
+ if (normalizedTenantId) tenantContext?.switchTenant(normalizedTenantId);
534
+ else tenantContext?.clearTenant();
535
+ const response = await http.post(
536
+ "/auth/autenticar",
537
+ {},
538
+ {
539
+ headers: {
540
+ Authorization: `Basic ${encodeBasicCredentials(email, password)}`,
541
+ ...normalizedTenantId ? { "x-oon-tenant-id": normalizedTenantId } : {}
542
+ }
543
+ }
544
+ );
545
+ if (!response.data.token) {
546
+ const error = { code: "AUTH_TOKEN_MISSING", message: "Token n\xE3o retornado pelo backend.", status: 502 };
547
+ throw error;
548
+ }
549
+ storage.set(response.data.token);
550
+ await validateSession();
551
+ } catch (error) {
552
+ if (error?.code === "TENANT_SELECTION_REQUIRED") {
553
+ setStatus("unauthenticated");
554
+ } else {
555
+ applyValidationError(error);
556
+ }
557
+ throw error;
558
+ }
559
+ },
560
+ [http, storage, validateSession, applyValidationError, tenantContext, runtimeMode]
561
+ );
562
+ const completeFirstAccess = useCallback3(async (password) => {
563
+ await http.post("/auth/first-access/password", { password });
564
+ await validateSession();
565
+ }, [http, validateSession]);
566
+ const logout = useCallback3(() => {
567
+ storage.clear();
568
+ tenantContext?.clearTenant();
569
+ setUser(null);
570
+ setStatus("unauthenticated");
571
+ if (runtimeMode === "local") {
572
+ void http.post("/auth/local/logout").catch(() => void 0);
573
+ return;
574
+ }
575
+ redirectToExternalLogin();
576
+ }, [storage, redirectToExternalLogin, tenantContext, runtimeMode, http]);
577
+ useEffect2(() => {
578
+ let active = true;
579
+ if (activation && !["active", "error"].includes(activation.status)) {
580
+ setUser(null);
581
+ setStatus("unauthenticated");
582
+ return;
583
+ }
584
+ if (runtimeMode === "local") {
585
+ storage.clear();
586
+ const bootstrapCode = captureTokenFromUrl("oon_local_bootstrap");
587
+ setStatus("loading");
588
+ const bootstrap = bootstrapCode ? http.post("/auth/local/bootstrap", { code: bootstrapCode }) : Promise.resolve();
589
+ bootstrap.then(() => http.get("/auth/validar-token")).then((response) => {
590
+ if (!active) return;
591
+ applyUser(response.data.usuario);
592
+ }).catch((error) => {
593
+ if (!active) return;
594
+ applyValidationError(error);
595
+ });
596
+ return () => {
597
+ active = false;
598
+ };
599
+ }
600
+ const launchCode = captureTokenFromUrl("oon_launch");
601
+ const incoming = captureTokenFromUrl(tokenParam);
602
+ const stored = storage.get();
603
+ if (launchCode) {
604
+ storage.clear();
605
+ setStatus("loading");
606
+ http.post("/auth/launch/exchange", { code: launchCode }).then((response) => {
607
+ if (!response.data.token) {
608
+ const error = {
609
+ code: "APP_LAUNCH_TOKEN_MISSING",
610
+ message: "A troca de abertura n\xE3o retornou uma sess\xE3o.",
611
+ status: 502
612
+ };
613
+ throw error;
614
+ }
615
+ storage.set(response.data.token);
616
+ return http.get("/auth/validar-token");
617
+ }).then((response) => {
618
+ if (!active) return;
619
+ applyUser(response.data.usuario);
620
+ }).catch((error) => {
621
+ if (!active) return;
622
+ storage.clear();
623
+ applyValidationError(error);
624
+ });
625
+ return () => {
626
+ active = false;
627
+ };
628
+ }
629
+ const token = incoming ?? devToken ?? stored;
630
+ if (token && token !== stored) storage.set(token);
631
+ if (!token) {
632
+ setUser(null);
633
+ setStatus("unauthenticated");
634
+ redirectToExternalLogin();
635
+ return;
636
+ }
637
+ setStatus("loading");
638
+ http.get("/auth/validar-token").then((response) => {
639
+ if (!active) return;
640
+ applyUser(response.data.usuario);
641
+ }).catch((error) => {
642
+ if (!active) return;
643
+ applyValidationError(error);
644
+ });
645
+ return () => {
646
+ active = false;
647
+ };
648
+ }, [http, storage, tokenParam, devToken, redirectToExternalLogin, applyValidationError, applyUser, activation?.status, runtimeMode]);
649
+ const roleCode = String(user?.roleCode || user?.tipo || "");
650
+ const roles = roleCode ? [roleCode] : [];
651
+ const capabilities = extractCapabilities(user);
652
+ const permissions = explicitPermissions(user) ?? capabilities;
653
+ const hasPermission = useCallback3(
654
+ (permission) => can(user, permission),
655
+ [user]
656
+ );
657
+ const value = {
658
+ user,
659
+ roles,
660
+ capabilities,
661
+ permissions,
662
+ status,
663
+ isAuthenticated: status === "authenticated" || status === "first-access",
664
+ hasPermission,
665
+ login,
666
+ completeFirstAccess,
667
+ retry,
668
+ logout
669
+ };
670
+ return /* @__PURE__ */ jsx4(AuthContext.Provider, { value, children });
671
+ }
672
+ function useOonAuth() {
673
+ const context = useContext4(AuthContext);
674
+ if (!context) throw new Error("useOonAuth deve ser usado dentro de <AuthProvider> (oonCoreFront.start).");
675
+ return context;
676
+ }
677
+
678
+ export {
679
+ createRestClient,
680
+ createTokenStorage,
681
+ ApiProvider,
682
+ useOonApi,
683
+ useOonResource,
684
+ useCoreMetadata,
685
+ useModelSchema,
686
+ ActivationProvider,
687
+ useActivation,
688
+ TenantProvider,
689
+ useOonTenant,
690
+ can,
691
+ AuthProvider,
692
+ useOonAuth
693
+ };
694
+ //# sourceMappingURL=chunk-VJ7YNFF7.js.map