@nocios/crudify-components 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.
- package/.github/workflows/test.yml +59 -0
- package/.nvmrc +1 -0
- package/README.md +398 -0
- package/README_DEPTH.md +1230 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/coverage-final.json +85 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +506 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +210 -0
- package/dist/CrudiaMarkdownField-C54-A_J3.d.mts +328 -0
- package/dist/CrudiaMarkdownField-C8HQh7s5.d.ts +328 -0
- package/dist/GlobalNotificationProvider-Zq18OkpI.d.mts +96 -0
- package/dist/GlobalNotificationProvider-Zq18OkpI.d.ts +96 -0
- package/dist/api-B4uXiHF0.d.mts +118 -0
- package/dist/api-B4uXiHF0.d.ts +118 -0
- package/dist/chunk-2XOTIEKS.js +1 -0
- package/dist/chunk-5HFI5CZ5.js +1 -0
- package/dist/chunk-CHDM7KGH.js +1 -0
- package/dist/chunk-HVTRRU4W.mjs +1 -0
- package/dist/chunk-JAPL7EZJ.mjs +1 -0
- package/dist/chunk-JNEWPO2J.mjs +1 -0
- package/dist/chunk-MFYHD6S5.js +1 -0
- package/dist/chunk-MGJZTOEM.mjs +1 -0
- package/dist/chunk-NBQH6QOU.mjs +1 -0
- package/dist/chunk-NSV6ECYO.js +1 -0
- package/dist/chunk-PNI3ZBZV.js +1 -0
- package/dist/chunk-U4RS66TB.mjs +1 -0
- package/dist/components.d.mts +24 -0
- package/dist/components.d.ts +24 -0
- package/dist/components.js +1 -0
- package/dist/components.mjs +1 -0
- package/dist/errorTranslation-DGdrMidg.d.ts +143 -0
- package/dist/errorTranslation-qwwQTvCO.d.mts +143 -0
- package/dist/hooks.d.mts +6 -0
- package/dist/hooks.d.ts +6 -0
- package/dist/hooks.js +1 -0
- package/dist/hooks.mjs +1 -0
- package/dist/index-BUKX3duW.d.ts +854 -0
- package/dist/index-Y9tTsinC.d.mts +854 -0
- package/dist/index.d.mts +1274 -0
- package/dist/index.d.ts +1274 -0
- package/dist/index.js +6 -0
- package/dist/index.mjs +6 -0
- package/dist/utils.d.mts +175 -0
- package/dist/utils.d.ts +175 -0
- package/dist/utils.js +1 -0
- package/dist/utils.mjs +1 -0
- package/package.json +88 -0
- package/vitest.config.ts +28 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _createStarExport(obj) { Object.keys(obj) .filter((key) => key !== "default" && key !== "__esModule") .forEach((key) => { if (exports.hasOwnProperty(key)) { return; } Object.defineProperty(exports, key, {enumerable: true, configurable: true, get: () => obj[key]}); }); } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } async function _asyncOptionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = await fn(value); } else if (op === 'call' || op === 'optionalCall') { value = await fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }var _chunkPNI3ZBZVjs = require('./chunk-PNI3ZBZV.js');var _chunk2XOTIEKSjs = require('./chunk-2XOTIEKS.js');var _chunkCHDM7KGHjs = require('./chunk-CHDM7KGH.js');var _chunk5HFI5CZ5js = require('./chunk-5HFI5CZ5.js');var _chunkNSV6ECYOjs = require('./chunk-NSV6ECYO.js');var _chunkMFYHD6S5js = require('./chunk-MFYHD6S5.js');var _crudifysdk = require('@nocios/crudify-sdk'); var _crudifysdk2 = _interopRequireDefault(_crudifysdk); _createStarExport(_crudifysdk);var _react = require('react');var _material = require('@mui/material');var _jsxruntime = require('react/jsx-runtime');var ir=(e={})=>{try{let r=_chunkMFYHD6S5js.b.call(void 0, "theme");if(r){let t=JSON.parse(decodeURIComponent(r));return{...e,...t}}}catch(r){_chunkMFYHD6S5js.a.warn("Error parsing theme from cookie",r instanceof Error?{errorMessage:r.message}:{message:String(r)})}return e};function nr({children:e,defaultTheme:r={},disableCssBaseline:t=!1}){let o=_react.useMemo.call(void 0, ()=>{let n=ir(r);return _material.createTheme.call(void 0, n)},[r]);return _jsxruntime.jsxs.call(void 0, _material.ThemeProvider,{theme:o,children:[!t&&_jsxruntime.jsx.call(void 0, _material.CssBaseline,{}),e]})}var _reactrouterdom = require('react-router-dom');var pr={border:"5px solid rgba(0, 0, 0, 0.1)",borderTopColor:"#3B82F6",borderRadius:"50%",width:"50px",height:"50px",animation:"spin 1s linear infinite"},ur=()=>_jsxruntime.jsx.call(void 0, "style",{children:`
|
|
2
|
+
@keyframes spin {
|
|
3
|
+
0% { transform: rotate(0deg); }
|
|
4
|
+
100% { transform: rotate(360deg); }
|
|
5
|
+
}
|
|
6
|
+
`});function g({stage:e="loading",message:r}){let o=r||{initializing:"Initializing application...","validating-session":"Validating session...",loading:"Loading..."}[e];return _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment,{children:[_jsxruntime.jsx.call(void 0, ur,{}),_jsxruntime.jsxs.call(void 0, "div",{style:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",height:"100vh",width:"100vw",backgroundColor:"#f0f2f5",fontFamily:'"Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',color:"#333",textAlign:"center",boxSizing:"border-box",padding:"20px",background:"#fff"},children:[_jsxruntime.jsx.call(void 0, "div",{style:pr}),_jsxruntime.jsx.call(void 0, "p",{style:{fontSize:"1.25em",fontWeight:"500",marginTop:"24px",color:"#1f2937"},children:o})]})]})}var lr=/^[a-zA-Z0-9\-_./\?=&%#]+$/,dr=[/^https?:\/\//i,/^ftp:\/\//i,/^\/\//,/javascript:/i,/data:/i,/vbscript:/i,/about:/i,/\.\.\//,/\.\.\\/,/%2e%2e%2f/i,/%2e%2e%5c/i,/%2f%2f/i,/%5c%5c/i,/[\x00-\x1f\x7f-\x9f]/,/\\/],k= exports.validateInternalRedirect =(e,r="/")=>{if(!e||typeof e!="string")return r;let t=e.trim();if(!t)return r;if(!t.startsWith("/"))return _chunkMFYHD6S5js.a.warn("Open redirect blocked (relative path)",{path:e}),r;if(!lr.test(t))return _chunkMFYHD6S5js.a.warn("Open redirect blocked (invalid characters)",{path:e}),r;let o=t.toLowerCase();for(let i of dr)if(i.test(o))return _chunkMFYHD6S5js.a.warn("Open redirect blocked (dangerous pattern)",{path:e}),r;let n=t.split("?")[0].split("/").filter(Boolean);if(n.length===0)return t;for(let i of n)if(i===".."||i.includes(":")||i.length>100)return _chunkMFYHD6S5js.a.warn("Open redirect blocked (suspicious path part)",{part:i}),r;return t},M= exports.extractSafeRedirectFromUrl =(e,r="/")=>{try{let o=(typeof e=="string"?new URLSearchParams(e):e).get("redirect");if(!o)return r;let n=decodeURIComponent(o);return k(n,r)}catch(t){return _chunkMFYHD6S5js.a.warn("Error parsing redirect parameter",t instanceof Error?{errorMessage:t.message}:{message:String(t)}),r}};function V({children:e,loadingComponent:r,loginPath:t="/login"}){let{isAuthenticated:o,isLoading:n,isLoggingOut:i,isInitialized:y,tokens:c,error:f}=_chunkCHDM7KGHjs.l.call(void 0, ),u=_reactrouterdom.useLocation.call(void 0, );if(!y||n&&!i)return _jsxruntime.jsx.call(void 0, _jsxruntime.Fragment,{children:r||_jsxruntime.jsx.call(void 0, g,{stage:"validating-session"})});let l=o&&_optionalChain([c, 'optionalAccess', _2 => _2.accessToken])&&c.accessToken.length>0;if(f||!o||!l){if(i)return _jsxruntime.jsx.call(void 0, _reactrouterdom.Navigate,{to:t,replace:!0});let R=u.pathname+u.search;if(u.pathname===t||u.pathname.startsWith(t))return _jsxruntime.jsx.call(void 0, _reactrouterdom.Navigate,{to:t,replace:!0});let S=k(R),w=encodeURIComponent(S);return _jsxruntime.jsx.call(void 0, _reactrouterdom.Navigate,{to:`${t}?redirect=${w}`,replace:!0})}return _jsxruntime.jsx.call(void 0, _jsxruntime.Fragment,{children:e})}var L="crudify_tokens",mr="crudify_enc_key";function B(){try{let e=localStorage.getItem(L);return e?e.startsWith("v2:")&&e.length>50:!1}catch (e2){return!1}}function W(){try{let e=localStorage.getItem(mr);return e!==null&&e.length>=32}catch (e3){return!1}}function U(){return B()&&W()}function H(){try{let e=localStorage.getItem(L);return e?!e.startsWith("v2:")||e.length<50:!1}catch (e4){return!1}}function gr(){try{H()&&localStorage.removeItem(L)}catch (e5){}}function Y({children:e,redirectTo:r="/",loadingComponent:t,initTimeout:o=3e3}){let{isAuthenticated:n,isInitialized:i}=_chunkCHDM7KGHjs.l.call(void 0, ),y=_reactrouterdom.useLocation.call(void 0, ),[c]=_react.useState.call(void 0, ()=>U()),[f,u]=_react.useState.call(void 0, !1);if(_react.useEffect.call(void 0, ()=>{if(!i&&c){let l=setTimeout(()=>{u(!0)},o);return()=>clearTimeout(l)}},[i,c,o]),i&&n){let l=new URLSearchParams(y.search),R=M(l,r);return _jsxruntime.jsx.call(void 0, _reactrouterdom.Navigate,{to:R,replace:!0})}return i&&!n?_jsxruntime.jsx.call(void 0, _jsxruntime.Fragment,{children:e}):c&&!f?_jsxruntime.jsx.call(void 0, _jsxruntime.Fragment,{children:t||_jsxruntime.jsx.call(void 0, g,{stage:"validating-session"})}):_jsxruntime.jsx.call(void 0, _jsxruntime.Fragment,{children:e})}function J({children:e,behavior:r="redirect-if-authenticated",redirectTo:t="/",loadingComponent:o}){let{isAuthenticated:n,isInitialized:i}=_chunkCHDM7KGHjs.l.call(void 0, );return i?r==="allow-all"?_jsxruntime.jsx.call(void 0, _jsxruntime.Fragment,{children:e}):n?_jsxruntime.jsx.call(void 0, _reactrouterdom.Navigate,{to:t,replace:!0}):_jsxruntime.jsx.call(void 0, _jsxruntime.Fragment,{children:e}):_jsxruntime.jsx.call(void 0, _jsxruntime.Fragment,{children:o||_jsxruntime.jsx.call(void 0, g,{stage:"validating-session"})})}var Q=_react.createContext.call(void 0, void 0),hr= exports.CrudifyInitializer =({config:e,children:r,fallback:t=null,onInitialized:o,onError:n})=>{let[i,y]=_react.useState.call(void 0, !1),[c,f]=_react.useState.call(void 0, !1),[u,l]=_react.useState.call(void 0, null),R=_react.useRef.call(void 0, !1),S=_react.useRef.call(void 0, !1);_react.useEffect.call(void 0, ()=>{R.current||(_chunkCHDM7KGHjs.q.registerHighPriorityInitializer(),R.current=!0),S.current||(S.current=!0,(async()=>{f(!0),l(null);try{let d=_chunkMFYHD6S5js.c.call(void 0, {publicApiKey:_optionalChain([e, 'optionalAccess', _3 => _3.publicApiKey]),env:_optionalChain([e, 'optionalAccess', _4 => _4.env]),enableDebug:_optionalChain([e, 'optionalAccess', _5 => _5.enableLogging])});if(!d.publicApiKey)throw new Error("Crudify configuration missing. Please provide publicApiKey via props or ensure cookies are set by Lambda.");let v=d.env||"prod";_chunkMFYHD6S5js.a.setEnvironment(v),await _chunkCHDM7KGHjs.q.initialize({priority:"HIGH",publicApiKey:d.publicApiKey,env:d.env||"prod",enableLogging:_optionalChain([e, 'optionalAccess', _6 => _6.enableLogging]),requestedBy:"CrudifyInitializer"}),y(!0),f(!1),o&&o()}catch(d){let v=d instanceof Error?d:new Error(String(d));l(v),f(!1),n&&n(v)}})())},[_optionalChain([e, 'optionalAccess', _7 => _7.publicApiKey]),_optionalChain([e, 'optionalAccess', _8 => _8.env]),_optionalChain([e, 'optionalAccess', _9 => _9.enableLogging]),o,n]);let w={isInitialized:i,isInitializing:c,error:u};return c&&t?_jsxruntime.jsx.call(void 0, _jsxruntime.Fragment,{children:t}):(u&&_chunkMFYHD6S5js.a.error("[CrudifyInitializer] Initialization failed",u),_jsxruntime.jsx.call(void 0, Q.Provider,{value:w,children:r}))},Sr= exports.useCrudifyInitializer =()=>{let e=_react.useContext.call(void 0, Q);if(!e)throw new Error("useCrudifyInitializer must be used within CrudifyInitializer");return e};var _crudifyadmin = require('@nocios/crudify-admin'); var _crudifyadmin2 = _interopRequireDefault(_crudifyadmin);var X=!1,I=null,h=null;async function Tr(){let r=await _chunkCHDM7KGHjs.d.getInstance().getTokenInfo(),t=_optionalChain([r, 'optionalAccess', _10 => _10.apiEndpointAdmin]),o=_optionalChain([r, 'optionalAccess', _11 => _11.apiKeyEndpointAdmin]);return t&&o?{apiUrl:t,apiKey:o}:_chunkCHDM7KGHjs.h.waitForCredentials()}async function Er(){if(!X)return I||(I=(async()=>{try{let e=_chunkCHDM7KGHjs.d.getInstance(),{apiUrl:r,apiKey:t}=await Tr();h=await _asyncOptionalChain([(await e.getTokenInfo()), 'optionalAccess', async _12 => _12.crudifyTokens, 'optionalAccess', async _13 => _13.accessToken])||null,_crudifyadmin2.default.init({url:r,apiKey:t,getAdditionalHeaders:()=>h?{Authorization:`Bearer ${h}`}:{}}),X=!0}catch(e){throw I=null,_chunkMFYHD6S5js.a.error("[crudifyAdminWrapper] Initialization failed",e instanceof Error?e:{message:String(e)}),e}})(),I)}async function a(e){try{await Er();let r=_chunkCHDM7KGHjs.d.getInstance();h=await _asyncOptionalChain([(await r.getTokenInfo()), 'optionalAccess', async _14 => _14.crudifyTokens, 'optionalAccess', async _15 => _15.accessToken])||null;let o=await e(),n=o.errors&&(typeof o.errors=="string"&&o.errors.includes("401")||Array.isArray(o.errors)&&o.errors.some(i=>typeof i=="string"&&i.includes("401")));return!o.success&&n?await r.refreshTokens()?(h=await _asyncOptionalChain([(await r.getTokenInfo()), 'optionalAccess', async _16 => _16.crudifyTokens, 'optionalAccess', async _17 => _17.accessToken])||null,await e()):(_chunkMFYHD6S5js.a.error("[crudifyAdmin] Token refresh failed"),typeof window<"u"&&(window.location.href="/login"),o):o}catch(r){return _chunkMFYHD6S5js.a.error("[crudifyAdmin] Operation error",r instanceof Error?r:{message:String(r)}),{success:!1,errors:r instanceof Error?r.message:"Unknown error"}}}var kr={listModules:()=>a(()=>_crudifyadmin2.default.listModules()),getModule:e=>a(()=>_crudifyadmin2.default.getModule(e)),createModule:e=>a(()=>_crudifyadmin2.default.createModule(e)),editModule:(e,r)=>a(()=>_crudifyadmin2.default.editModule(e,r)),deleteModule:e=>a(()=>_crudifyadmin2.default.deleteModule(e)),activateModule:e=>a(()=>_crudifyadmin2.default.activateModule(e)),deactivateModule:e=>a(()=>_crudifyadmin2.default.deactivateModule(e)),getModuleVersions:e=>a(()=>_crudifyadmin2.default.getModuleVersions(e)),listActions:e=>a(()=>_crudifyadmin2.default.listActions(e)),getAction:e=>a(()=>_crudifyadmin2.default.getAction(e)),createAction:e=>a(()=>_crudifyadmin2.default.createAction(e)),editAction:(e,r)=>a(()=>_crudifyadmin2.default.editAction(e,r)),deleteAction:e=>a(()=>_crudifyadmin2.default.deleteAction(e)),activateAction:e=>a(()=>_crudifyadmin2.default.activateAction(e)),deactivateAction:e=>a(()=>_crudifyadmin2.default.deactivateAction(e)),getActionVersions:e=>a(()=>_crudifyadmin2.default.getActionVersions(e)),getActionsByProfile:e=>a(()=>_crudifyadmin2.default.getActionsByProfile(e)),updateActionsProfiles:e=>a(()=>_crudifyadmin2.default.updateActionsProfiles(e)),calculatePermissions:e=>a(()=>_crudifyadmin2.default.calculatePermissions(e))};exports.AuthRoute = Y; exports.CRITICAL_TRANSLATIONS = _chunkPNI3ZBZVjs.a; exports.CrossTabSyncManager = _chunkCHDM7KGHjs.b; exports.CrudiaAutoGenerate = _chunkPNI3ZBZVjs.s; exports.CrudiaFileField = _chunkPNI3ZBZVjs.t; exports.CrudiaMarkdownField = _chunkPNI3ZBZVjs.u; exports.CrudifyInitializationManager = _chunkCHDM7KGHjs.p; exports.CrudifyInitializer = hr; exports.CrudifyLogin = _chunkPNI3ZBZVjs.l; exports.CrudifyProvider = _chunkCHDM7KGHjs.i; exports.CrudifyThemeProvider = nr; exports.DEFAULT_PASSWORD_RULES = _chunkPNI3ZBZVjs.k; exports.ERROR_CODES = _chunkNSV6ECYOjs.a; exports.ERROR_SEVERITY_MAP = _chunkNSV6ECYOjs.b; exports.GlobalNotificationProvider = _chunkCHDM7KGHjs.f; exports.GuestRoute = J; exports.LoginComponent = _chunkPNI3ZBZVjs.q; exports.POLICY_ACTIONS = _chunkPNI3ZBZVjs.n; exports.PREFERRED_POLICY_ORDER = _chunkPNI3ZBZVjs.o; exports.PasswordRequirements = _chunkPNI3ZBZVjs.h; exports.Policies = _chunkPNI3ZBZVjs.p; exports.ProtectedRoute = V; exports.SessionDebugInfo = _chunkCHDM7KGHjs.m; exports.SessionLoadingScreen = g; exports.SessionManager = _chunkCHDM7KGHjs.d; exports.SessionProvider = _chunkCHDM7KGHjs.k; exports.SessionStatus = _chunkPNI3ZBZVjs.r; exports.TokenStorage = _chunkCHDM7KGHjs.a; exports.TranslationService = _chunkPNI3ZBZVjs.d; exports.TranslationsProvider = _chunkPNI3ZBZVjs.f; exports.UserProfileDisplay = _chunkPNI3ZBZVjs.m; exports.allPasswordRulesPassed = _chunkPNI3ZBZVjs.j; exports.clearCorruptedTokens = gr; exports.createErrorTranslator = _chunkMFYHD6S5js.o; exports.crossTabSync = _chunkCHDM7KGHjs.c; exports.crudify = _crudifysdk2.default; exports.crudifyAdmin = kr; exports.crudifyInitManager = _chunkCHDM7KGHjs.q; exports.decodeJwtSafely = _chunkMFYHD6S5js.q; exports.evaluatePasswordRules = _chunkPNI3ZBZVjs.i; exports.extractSafeRedirectFromUrl = M; exports.getCookie = _chunkMFYHD6S5js.b; exports.getCriticalLanguages = _chunkPNI3ZBZVjs.b; exports.getCriticalTranslations = _chunkPNI3ZBZVjs.c; exports.getCurrentUserEmail = _chunkMFYHD6S5js.r; exports.getErrorMessage = _chunkNSV6ECYOjs.e; exports.handleCrudifyError = _chunkNSV6ECYOjs.g; exports.hasCorruptedTokens = H; exports.hasEncryptionKeyHash = W; exports.hasStoredTokens = B; exports.isTokenExpired = _chunkMFYHD6S5js.s; exports.logger = _chunkMFYHD6S5js.a; exports.parseApiError = _chunkNSV6ECYOjs.c; exports.parseJavaScriptError = _chunkNSV6ECYOjs.f; exports.parseTransactionError = _chunkNSV6ECYOjs.d; exports.secureLocalStorage = _chunk5HFI5CZ5js.b; exports.secureSessionStorage = _chunk5HFI5CZ5js.a; exports.shouldWaitForInitialization = U; exports.translateError = _chunkMFYHD6S5js.n; exports.translateErrorCode = _chunkMFYHD6S5js.l; exports.translateErrorCodes = _chunkMFYHD6S5js.m; exports.translationService = _chunkPNI3ZBZVjs.e; exports.useAuth = _chunk2XOTIEKSjs.b; exports.useAutoGenerate = _chunkCHDM7KGHjs.o; exports.useCrudify = _chunkCHDM7KGHjs.j; exports.useCrudifyInitializer = Sr; exports.useCrudifyWithNotifications = _chunk2XOTIEKSjs.d; exports.useData = _chunk2XOTIEKSjs.c; exports.useFileUpload = _chunkCHDM7KGHjs.r; exports.useGlobalNotification = _chunkCHDM7KGHjs.g; exports.useSession = _chunkCHDM7KGHjs.e; exports.useSessionContext = _chunkCHDM7KGHjs.l; exports.useTranslations = _chunkPNI3ZBZVjs.g; exports.useUserData = _chunk2XOTIEKSjs.a; exports.useUserProfile = _chunkCHDM7KGHjs.n; exports.validateInternalRedirect = k;
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import{a as j,b as ee,c as re,d as te,e as oe,f as ie,g as ne,h as be,i as Me,j as ze,k as Le,l as Ue,m as Fe,n as Ne,o as Ge,p as De,q as Ke,r as _e,s as Be,t as qe,u as Ye}from"./chunk-HVTRRU4W.mjs";import{a as Je,b as $e,c as Ze,d as je}from"./chunk-U4RS66TB.mjs";import{a as se,b as le,c as de,d as A,e as fe,f as Re,g as xe,h as D,i as Ae,j as Ce,k as Pe,l as m,m as Ie,n as Oe,o as Ve,p as We,q as T,r as He}from"./chunk-NBQH6QOU.mjs";import{a as Qe,b as Xe}from"./chunk-JNEWPO2J.mjs";import{a as he,b as Se,c as ve,d as Te,e as Ee,f as ke,g as we}from"./chunk-JAPL7EZJ.mjs";import{a as p,b,c as G,l as ae,m as pe,n as ue,o as ce,q as me,r as ge,s as ye}from"./chunk-MGJZTOEM.mjs";import{default as Tt}from"@nocios/crudify-sdk";export*from"@nocios/crudify-sdk";import{useMemo as er}from"react";import{ThemeProvider as rr,createTheme as tr,CssBaseline as or}from"@mui/material";import{jsx as sr,jsxs as ar}from"react/jsx-runtime";var ir=(e={})=>{try{let r=b("theme");if(r){let t=JSON.parse(decodeURIComponent(r));return{...e,...t}}}catch(r){p.warn("Error parsing theme from cookie",r instanceof Error?{errorMessage:r.message}:{message:String(r)})}return e};function nr({children:e,defaultTheme:r={},disableCssBaseline:t=!1}){let o=er(()=>{let n=ir(r);return tr(n)},[r]);return ar(rr,{theme:o,children:[!t&&sr(or,{}),e]})}import{Navigate as z,useLocation as fr}from"react-router-dom";import{Fragment as cr,jsx as E,jsxs as K}from"react/jsx-runtime";var pr={border:"5px solid rgba(0, 0, 0, 0.1)",borderTopColor:"#3B82F6",borderRadius:"50%",width:"50px",height:"50px",animation:"spin 1s linear infinite"},ur=()=>E("style",{children:`
|
|
2
|
+
@keyframes spin {
|
|
3
|
+
0% { transform: rotate(0deg); }
|
|
4
|
+
100% { transform: rotate(360deg); }
|
|
5
|
+
}
|
|
6
|
+
`});function g({stage:e="loading",message:r}){let o=r||{initializing:"Initializing application...","validating-session":"Validating session...",loading:"Loading..."}[e];return K(cr,{children:[E(ur,{}),K("div",{style:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",height:"100vh",width:"100vw",backgroundColor:"#f0f2f5",fontFamily:'"Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',color:"#333",textAlign:"center",boxSizing:"border-box",padding:"20px",background:"#fff"},children:[E("div",{style:pr}),E("p",{style:{fontSize:"1.25em",fontWeight:"500",marginTop:"24px",color:"#1f2937"},children:o})]})]})}var lr=/^[a-zA-Z0-9\-_./\?=&%#]+$/,dr=[/^https?:\/\//i,/^ftp:\/\//i,/^\/\//,/javascript:/i,/data:/i,/vbscript:/i,/about:/i,/\.\.\//,/\.\.\\/,/%2e%2e%2f/i,/%2e%2e%5c/i,/%2f%2f/i,/%5c%5c/i,/[\x00-\x1f\x7f-\x9f]/,/\\/],k=(e,r="/")=>{if(!e||typeof e!="string")return r;let t=e.trim();if(!t)return r;if(!t.startsWith("/"))return p.warn("Open redirect blocked (relative path)",{path:e}),r;if(!lr.test(t))return p.warn("Open redirect blocked (invalid characters)",{path:e}),r;let o=t.toLowerCase();for(let i of dr)if(i.test(o))return p.warn("Open redirect blocked (dangerous pattern)",{path:e}),r;let n=t.split("?")[0].split("/").filter(Boolean);if(n.length===0)return t;for(let i of n)if(i===".."||i.includes(":")||i.length>100)return p.warn("Open redirect blocked (suspicious path part)",{part:i}),r;return t},M=(e,r="/")=>{try{let o=(typeof e=="string"?new URLSearchParams(e):e).get("redirect");if(!o)return r;let n=decodeURIComponent(o);return k(n,r)}catch(t){return p.warn("Error parsing redirect parameter",t instanceof Error?{errorMessage:t.message}:{message:String(t)}),r}};import{Fragment as _,jsx as x}from"react/jsx-runtime";function V({children:e,loadingComponent:r,loginPath:t="/login"}){let{isAuthenticated:o,isLoading:n,isLoggingOut:i,isInitialized:y,tokens:c,error:f}=m(),u=fr();if(!y||n&&!i)return x(_,{children:r||x(g,{stage:"validating-session"})});let l=o&&c?.accessToken&&c.accessToken.length>0;if(f||!o||!l){if(i)return x(z,{to:t,replace:!0});let R=u.pathname+u.search;if(u.pathname===t||u.pathname.startsWith(t))return x(z,{to:t,replace:!0});let S=k(R),w=encodeURIComponent(S);return x(z,{to:`${t}?redirect=${w}`,replace:!0})}return x(_,{children:e})}import{useState as q,useEffect as yr}from"react";import{Navigate as Rr,useLocation as xr}from"react-router-dom";var L="crudify_tokens",mr="crudify_enc_key";function B(){try{let e=localStorage.getItem(L);return e?e.startsWith("v2:")&&e.length>50:!1}catch{return!1}}function W(){try{let e=localStorage.getItem(mr);return e!==null&&e.length>=32}catch{return!1}}function U(){return B()&&W()}function H(){try{let e=localStorage.getItem(L);return e?!e.startsWith("v2:")||e.length<50:!1}catch{return!1}}function gr(){try{H()&&localStorage.removeItem(L)}catch{}}import{Fragment as O,jsx as C}from"react/jsx-runtime";function Y({children:e,redirectTo:r="/",loadingComponent:t,initTimeout:o=3e3}){let{isAuthenticated:n,isInitialized:i}=m(),y=xr(),[c]=q(()=>U()),[f,u]=q(!1);if(yr(()=>{if(!i&&c){let l=setTimeout(()=>{u(!0)},o);return()=>clearTimeout(l)}},[i,c,o]),i&&n){let l=new URLSearchParams(y.search),R=M(l,r);return C(Rr,{to:R,replace:!0})}return i&&!n?C(O,{children:e}):c&&!f?C(O,{children:t||C(g,{stage:"validating-session"})}):C(O,{children:e})}import{Navigate as Ar}from"react-router-dom";import{Fragment as F,jsx as P}from"react/jsx-runtime";function J({children:e,behavior:r="redirect-if-authenticated",redirectTo:t="/",loadingComponent:o}){let{isAuthenticated:n,isInitialized:i}=m();return i?r==="allow-all"?P(F,{children:e}):n?P(Ar,{to:t,replace:!0}):P(F,{children:e}):P(F,{children:o||P(g,{stage:"validating-session"})})}import{createContext as Cr,useContext as Pr,useEffect as Ir,useRef as $,useState as N}from"react";import{Fragment as vr,jsx as Z}from"react/jsx-runtime";var Q=Cr(void 0),hr=({config:e,children:r,fallback:t=null,onInitialized:o,onError:n})=>{let[i,y]=N(!1),[c,f]=N(!1),[u,l]=N(null),R=$(!1),S=$(!1);Ir(()=>{R.current||(T.registerHighPriorityInitializer(),R.current=!0),S.current||(S.current=!0,(async()=>{f(!0),l(null);try{let d=G({publicApiKey:e?.publicApiKey,env:e?.env,enableDebug:e?.enableLogging});if(!d.publicApiKey)throw new Error("Crudify configuration missing. Please provide publicApiKey via props or ensure cookies are set by Lambda.");let v=d.env||"prod";p.setEnvironment(v),await T.initialize({priority:"HIGH",publicApiKey:d.publicApiKey,env:d.env||"prod",enableLogging:e?.enableLogging,requestedBy:"CrudifyInitializer"}),y(!0),f(!1),o&&o()}catch(d){let v=d instanceof Error?d:new Error(String(d));l(v),f(!1),n&&n(v)}})())},[e?.publicApiKey,e?.env,e?.enableLogging,o,n]);let w={isInitialized:i,isInitializing:c,error:u};return c&&t?Z(vr,{children:t}):(u&&p.error("[CrudifyInitializer] Initialization failed",u),Z(Q.Provider,{value:w,children:r}))},Sr=()=>{let e=Pr(Q);if(!e)throw new Error("useCrudifyInitializer must be used within CrudifyInitializer");return e};import s from"@nocios/crudify-admin";var X=!1,I=null,h=null;async function Tr(){let r=await A.getInstance().getTokenInfo(),t=r?.apiEndpointAdmin,o=r?.apiKeyEndpointAdmin;return t&&o?{apiUrl:t,apiKey:o}:D.waitForCredentials()}async function Er(){if(!X)return I||(I=(async()=>{try{let e=A.getInstance(),{apiUrl:r,apiKey:t}=await Tr();h=(await e.getTokenInfo())?.crudifyTokens?.accessToken||null,s.init({url:r,apiKey:t,getAdditionalHeaders:()=>h?{Authorization:`Bearer ${h}`}:{}}),X=!0}catch(e){throw I=null,p.error("[crudifyAdminWrapper] Initialization failed",e instanceof Error?e:{message:String(e)}),e}})(),I)}async function a(e){try{await Er();let r=A.getInstance();h=(await r.getTokenInfo())?.crudifyTokens?.accessToken||null;let o=await e(),n=o.errors&&(typeof o.errors=="string"&&o.errors.includes("401")||Array.isArray(o.errors)&&o.errors.some(i=>typeof i=="string"&&i.includes("401")));return!o.success&&n?await r.refreshTokens()?(h=(await r.getTokenInfo())?.crudifyTokens?.accessToken||null,await e()):(p.error("[crudifyAdmin] Token refresh failed"),typeof window<"u"&&(window.location.href="/login"),o):o}catch(r){return p.error("[crudifyAdmin] Operation error",r instanceof Error?r:{message:String(r)}),{success:!1,errors:r instanceof Error?r.message:"Unknown error"}}}var kr={listModules:()=>a(()=>s.listModules()),getModule:e=>a(()=>s.getModule(e)),createModule:e=>a(()=>s.createModule(e)),editModule:(e,r)=>a(()=>s.editModule(e,r)),deleteModule:e=>a(()=>s.deleteModule(e)),activateModule:e=>a(()=>s.activateModule(e)),deactivateModule:e=>a(()=>s.deactivateModule(e)),getModuleVersions:e=>a(()=>s.getModuleVersions(e)),listActions:e=>a(()=>s.listActions(e)),getAction:e=>a(()=>s.getAction(e)),createAction:e=>a(()=>s.createAction(e)),editAction:(e,r)=>a(()=>s.editAction(e,r)),deleteAction:e=>a(()=>s.deleteAction(e)),activateAction:e=>a(()=>s.activateAction(e)),deactivateAction:e=>a(()=>s.deactivateAction(e)),getActionVersions:e=>a(()=>s.getActionVersions(e)),getActionsByProfile:e=>a(()=>s.getActionsByProfile(e)),updateActionsProfiles:e=>a(()=>s.updateActionsProfiles(e)),calculatePermissions:e=>a(()=>s.calculatePermissions(e))};export{Y as AuthRoute,j as CRITICAL_TRANSLATIONS,le as CrossTabSyncManager,Be as CrudiaAutoGenerate,qe as CrudiaFileField,Ye as CrudiaMarkdownField,We as CrudifyInitializationManager,hr as CrudifyInitializer,Ue as CrudifyLogin,Ae as CrudifyProvider,nr as CrudifyThemeProvider,Le as DEFAULT_PASSWORD_RULES,he as ERROR_CODES,Se as ERROR_SEVERITY_MAP,Re as GlobalNotificationProvider,J as GuestRoute,Ke as LoginComponent,Ne as POLICY_ACTIONS,Ge as PREFERRED_POLICY_ORDER,be as PasswordRequirements,De as Policies,V as ProtectedRoute,Ie as SessionDebugInfo,g as SessionLoadingScreen,A as SessionManager,Pe as SessionProvider,_e as SessionStatus,se as TokenStorage,te as TranslationService,ie as TranslationsProvider,Fe as UserProfileDisplay,ze as allPasswordRulesPassed,gr as clearCorruptedTokens,ce as createErrorTranslator,de as crossTabSync,Tt as crudify,kr as crudifyAdmin,T as crudifyInitManager,me as decodeJwtSafely,Me as evaluatePasswordRules,M as extractSafeRedirectFromUrl,b as getCookie,ee as getCriticalLanguages,re as getCriticalTranslations,ge as getCurrentUserEmail,Ee as getErrorMessage,we as handleCrudifyError,H as hasCorruptedTokens,W as hasEncryptionKeyHash,B as hasStoredTokens,ye as isTokenExpired,p as logger,ve as parseApiError,ke as parseJavaScriptError,Te as parseTransactionError,Xe as secureLocalStorage,Qe as secureSessionStorage,U as shouldWaitForInitialization,ue as translateError,ae as translateErrorCode,pe as translateErrorCodes,oe as translationService,$e as useAuth,Ve as useAutoGenerate,Ce as useCrudify,Sr as useCrudifyInitializer,je as useCrudifyWithNotifications,Ze as useData,He as useFileUpload,xe as useGlobalNotification,fe as useSession,m as useSessionContext,ne as useTranslations,Je as useUserData,Oe as useUserProfile,k as validateInternalRedirect};
|
package/dist/utils.d.mts
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
export { E as ERROR_CODES, j as ERROR_SEVERITY_MAP, n as ErrorCode, o as ErrorSeverity, q as ErrorTranslationConfig, P as ParsedError, m as createErrorTranslator, d as decodeJwtSafely, a as getCookie, g as getCurrentUserEmail, f as getErrorMessage, h as handleCrudifyError, i as isTokenExpired, p as parseApiError, e as parseJavaScriptError, c as parseTransactionError, b as secureLocalStorage, s as secureSessionStorage, l as translateError, t as translateErrorCode, k as translateErrorCodes } from './errorTranslation-qwwQTvCO.mjs';
|
|
2
|
+
import './api-B4uXiHF0.mjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Sistema centralizado de resolución de configuración para npm-crudify-ui
|
|
6
|
+
*
|
|
7
|
+
* Prioridad:
|
|
8
|
+
* 1. Cookies (producción con Lambda@Edge multitenancy) - MAYOR PRIORIDAD
|
|
9
|
+
* 2. Props explícitas (desarrollo local con .env) - FALLBACK
|
|
10
|
+
* 3. Error (ninguna configuración disponible)
|
|
11
|
+
*
|
|
12
|
+
* En producción, Lambda@Edge inyecta cookies con la configuración del tenant.
|
|
13
|
+
* En desarrollo local, no hay cookies y se usan las variables de .env como fallback.
|
|
14
|
+
*/
|
|
15
|
+
type CrudifyEnvironment = "dev" | "stg" | "api" | "prod";
|
|
16
|
+
interface ResolvedConfig {
|
|
17
|
+
publicApiKey?: string;
|
|
18
|
+
env?: CrudifyEnvironment;
|
|
19
|
+
appName?: string;
|
|
20
|
+
logo?: string;
|
|
21
|
+
loginActions?: string[];
|
|
22
|
+
featureKeys?: string[];
|
|
23
|
+
theme?: Record<string, unknown>;
|
|
24
|
+
configSource: "props" | "cookies" | "none";
|
|
25
|
+
}
|
|
26
|
+
interface ConfigResolverOptions {
|
|
27
|
+
publicApiKey?: string;
|
|
28
|
+
env?: CrudifyEnvironment;
|
|
29
|
+
appName?: string;
|
|
30
|
+
logo?: string;
|
|
31
|
+
loginActions?: string[];
|
|
32
|
+
featureKeys?: string[];
|
|
33
|
+
enableDebug?: boolean;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Resolves Crudify configuration with the following priority:
|
|
37
|
+
* 1. Cookies (production with Lambda multitenancy) - HIGHEST PRIORITY
|
|
38
|
+
* 2. Props (local development with .env) - FALLBACK
|
|
39
|
+
* 3. None (error - missing configuration)
|
|
40
|
+
*
|
|
41
|
+
* This resolver is responsible for reading cookies and applying correct priority.
|
|
42
|
+
*/
|
|
43
|
+
declare function resolveConfig(options?: ConfigResolverOptions): ResolvedConfig;
|
|
44
|
+
/**
|
|
45
|
+
* React hook to use config resolver
|
|
46
|
+
* Useful when you need config in a component
|
|
47
|
+
*/
|
|
48
|
+
declare function useResolvedConfig(options?: ConfigResolverOptions): ResolvedConfig;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Event Bus para coordinar acciones de autenticación
|
|
52
|
+
* Previene race conditions y asegura respuesta única a errores
|
|
53
|
+
*
|
|
54
|
+
* Uso:
|
|
55
|
+
* - Emitir eventos cuando hay errores de autenticación
|
|
56
|
+
* - Suscribirse para recibir notificaciones de eventos
|
|
57
|
+
* - Debounce automático para evitar múltiples disparos
|
|
58
|
+
*/
|
|
59
|
+
type AuthEventType = "SESSION_EXPIRED" | "TOKEN_REFRESH_FAILED" | "UNAUTHORIZED" | "TOKEN_EXPIRED" | "LOGOUT";
|
|
60
|
+
type AuthEventDetails = {
|
|
61
|
+
message?: string;
|
|
62
|
+
error?: unknown;
|
|
63
|
+
source?: string;
|
|
64
|
+
};
|
|
65
|
+
type AuthEvent = {
|
|
66
|
+
type: AuthEventType;
|
|
67
|
+
details?: AuthEventDetails;
|
|
68
|
+
timestamp: number;
|
|
69
|
+
};
|
|
70
|
+
type AuthEventListener = (event: AuthEvent) => void;
|
|
71
|
+
declare class AuthEventBus {
|
|
72
|
+
private static instance;
|
|
73
|
+
private listeners;
|
|
74
|
+
private isHandlingAuthError;
|
|
75
|
+
private lastErrorTime;
|
|
76
|
+
private lastEventType;
|
|
77
|
+
private readonly DEBOUNCE_TIME;
|
|
78
|
+
private constructor();
|
|
79
|
+
static getInstance(): AuthEventBus;
|
|
80
|
+
/**
|
|
81
|
+
* Emitir evento de error de autenticación
|
|
82
|
+
* Con debounce para evitar múltiples disparos
|
|
83
|
+
*/
|
|
84
|
+
emit(type: AuthEventType, details?: AuthEventDetails): void;
|
|
85
|
+
/**
|
|
86
|
+
* Suscribirse a eventos de autenticación
|
|
87
|
+
* @returns Función de cleanup para desuscribirse
|
|
88
|
+
*/
|
|
89
|
+
subscribe(listener: AuthEventListener): () => void;
|
|
90
|
+
/**
|
|
91
|
+
* Limpiar todos los listeners
|
|
92
|
+
*/
|
|
93
|
+
clear(): void;
|
|
94
|
+
/**
|
|
95
|
+
* Verificar si hay un evento siendo manejado
|
|
96
|
+
*/
|
|
97
|
+
isHandling(): boolean;
|
|
98
|
+
}
|
|
99
|
+
declare const authEventBus: AuthEventBus;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Sistema global de tracking de navegación para evitar monkey-patching anidado
|
|
103
|
+
* Usa patrón singleton con reference counting
|
|
104
|
+
*
|
|
105
|
+
* Problema que resuelve:
|
|
106
|
+
* - Si múltiples componentes usan useSession, cada uno crearía su propio monkey-patch
|
|
107
|
+
* - Esto causa anidamiento infinito de patches
|
|
108
|
+
* - NavigationTracker centraliza el patching con reference counting
|
|
109
|
+
*
|
|
110
|
+
* Uso:
|
|
111
|
+
* ```typescript
|
|
112
|
+
* const tracker = NavigationTracker.getInstance();
|
|
113
|
+
* const unsubscribe = tracker.subscribe(() => {
|
|
114
|
+
* console.log("Navigation detected!");
|
|
115
|
+
* });
|
|
116
|
+
*
|
|
117
|
+
* // Cleanup
|
|
118
|
+
* unsubscribe();
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
type NavigationCallback = () => void;
|
|
122
|
+
declare class NavigationTracker {
|
|
123
|
+
private static instance;
|
|
124
|
+
private isPatched;
|
|
125
|
+
private refCount;
|
|
126
|
+
private listeners;
|
|
127
|
+
private originalPushState;
|
|
128
|
+
private originalReplaceState;
|
|
129
|
+
private constructor();
|
|
130
|
+
/**
|
|
131
|
+
* Obtener instancia singleton
|
|
132
|
+
*/
|
|
133
|
+
static getInstance(): NavigationTracker;
|
|
134
|
+
/**
|
|
135
|
+
* Suscribirse a eventos de navegación
|
|
136
|
+
* @param callback - Función a llamar cuando hay navegación
|
|
137
|
+
* @returns Función de cleanup para desuscribirse
|
|
138
|
+
*/
|
|
139
|
+
subscribe(callback: NavigationCallback): () => void;
|
|
140
|
+
/**
|
|
141
|
+
* Desuscribirse de eventos de navegación
|
|
142
|
+
* @private
|
|
143
|
+
*/
|
|
144
|
+
private unsubscribe;
|
|
145
|
+
/**
|
|
146
|
+
* Aplicar monkey-patches a history API
|
|
147
|
+
* @private
|
|
148
|
+
*/
|
|
149
|
+
private applyPatches;
|
|
150
|
+
/**
|
|
151
|
+
* Remover monkey-patches y restaurar métodos originales
|
|
152
|
+
* @private
|
|
153
|
+
*/
|
|
154
|
+
private removePatches;
|
|
155
|
+
/**
|
|
156
|
+
* Notificar a todos los listeners
|
|
157
|
+
* @private
|
|
158
|
+
*/
|
|
159
|
+
private notifyListeners;
|
|
160
|
+
/**
|
|
161
|
+
* Para testing: limpiar completamente el tracker
|
|
162
|
+
* ⚠️ SOLO USAR EN TESTS
|
|
163
|
+
*/
|
|
164
|
+
static reset(): void;
|
|
165
|
+
/**
|
|
166
|
+
* Obtener número de suscriptores activos (útil para debugging)
|
|
167
|
+
*/
|
|
168
|
+
getSubscriberCount(): number;
|
|
169
|
+
/**
|
|
170
|
+
* Verificar si los patches están activos
|
|
171
|
+
*/
|
|
172
|
+
isActive(): boolean;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export { type AuthEvent, type AuthEventDetails, type AuthEventType, type ConfigResolverOptions, type CrudifyEnvironment, NavigationTracker, type ResolvedConfig, authEventBus, resolveConfig, useResolvedConfig };
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
export { E as ERROR_CODES, j as ERROR_SEVERITY_MAP, n as ErrorCode, o as ErrorSeverity, q as ErrorTranslationConfig, P as ParsedError, m as createErrorTranslator, d as decodeJwtSafely, a as getCookie, g as getCurrentUserEmail, f as getErrorMessage, h as handleCrudifyError, i as isTokenExpired, p as parseApiError, e as parseJavaScriptError, c as parseTransactionError, b as secureLocalStorage, s as secureSessionStorage, l as translateError, t as translateErrorCode, k as translateErrorCodes } from './errorTranslation-DGdrMidg.js';
|
|
2
|
+
import './api-B4uXiHF0.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Sistema centralizado de resolución de configuración para npm-crudify-ui
|
|
6
|
+
*
|
|
7
|
+
* Prioridad:
|
|
8
|
+
* 1. Cookies (producción con Lambda@Edge multitenancy) - MAYOR PRIORIDAD
|
|
9
|
+
* 2. Props explícitas (desarrollo local con .env) - FALLBACK
|
|
10
|
+
* 3. Error (ninguna configuración disponible)
|
|
11
|
+
*
|
|
12
|
+
* En producción, Lambda@Edge inyecta cookies con la configuración del tenant.
|
|
13
|
+
* En desarrollo local, no hay cookies y se usan las variables de .env como fallback.
|
|
14
|
+
*/
|
|
15
|
+
type CrudifyEnvironment = "dev" | "stg" | "api" | "prod";
|
|
16
|
+
interface ResolvedConfig {
|
|
17
|
+
publicApiKey?: string;
|
|
18
|
+
env?: CrudifyEnvironment;
|
|
19
|
+
appName?: string;
|
|
20
|
+
logo?: string;
|
|
21
|
+
loginActions?: string[];
|
|
22
|
+
featureKeys?: string[];
|
|
23
|
+
theme?: Record<string, unknown>;
|
|
24
|
+
configSource: "props" | "cookies" | "none";
|
|
25
|
+
}
|
|
26
|
+
interface ConfigResolverOptions {
|
|
27
|
+
publicApiKey?: string;
|
|
28
|
+
env?: CrudifyEnvironment;
|
|
29
|
+
appName?: string;
|
|
30
|
+
logo?: string;
|
|
31
|
+
loginActions?: string[];
|
|
32
|
+
featureKeys?: string[];
|
|
33
|
+
enableDebug?: boolean;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Resolves Crudify configuration with the following priority:
|
|
37
|
+
* 1. Cookies (production with Lambda multitenancy) - HIGHEST PRIORITY
|
|
38
|
+
* 2. Props (local development with .env) - FALLBACK
|
|
39
|
+
* 3. None (error - missing configuration)
|
|
40
|
+
*
|
|
41
|
+
* This resolver is responsible for reading cookies and applying correct priority.
|
|
42
|
+
*/
|
|
43
|
+
declare function resolveConfig(options?: ConfigResolverOptions): ResolvedConfig;
|
|
44
|
+
/**
|
|
45
|
+
* React hook to use config resolver
|
|
46
|
+
* Useful when you need config in a component
|
|
47
|
+
*/
|
|
48
|
+
declare function useResolvedConfig(options?: ConfigResolverOptions): ResolvedConfig;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Event Bus para coordinar acciones de autenticación
|
|
52
|
+
* Previene race conditions y asegura respuesta única a errores
|
|
53
|
+
*
|
|
54
|
+
* Uso:
|
|
55
|
+
* - Emitir eventos cuando hay errores de autenticación
|
|
56
|
+
* - Suscribirse para recibir notificaciones de eventos
|
|
57
|
+
* - Debounce automático para evitar múltiples disparos
|
|
58
|
+
*/
|
|
59
|
+
type AuthEventType = "SESSION_EXPIRED" | "TOKEN_REFRESH_FAILED" | "UNAUTHORIZED" | "TOKEN_EXPIRED" | "LOGOUT";
|
|
60
|
+
type AuthEventDetails = {
|
|
61
|
+
message?: string;
|
|
62
|
+
error?: unknown;
|
|
63
|
+
source?: string;
|
|
64
|
+
};
|
|
65
|
+
type AuthEvent = {
|
|
66
|
+
type: AuthEventType;
|
|
67
|
+
details?: AuthEventDetails;
|
|
68
|
+
timestamp: number;
|
|
69
|
+
};
|
|
70
|
+
type AuthEventListener = (event: AuthEvent) => void;
|
|
71
|
+
declare class AuthEventBus {
|
|
72
|
+
private static instance;
|
|
73
|
+
private listeners;
|
|
74
|
+
private isHandlingAuthError;
|
|
75
|
+
private lastErrorTime;
|
|
76
|
+
private lastEventType;
|
|
77
|
+
private readonly DEBOUNCE_TIME;
|
|
78
|
+
private constructor();
|
|
79
|
+
static getInstance(): AuthEventBus;
|
|
80
|
+
/**
|
|
81
|
+
* Emitir evento de error de autenticación
|
|
82
|
+
* Con debounce para evitar múltiples disparos
|
|
83
|
+
*/
|
|
84
|
+
emit(type: AuthEventType, details?: AuthEventDetails): void;
|
|
85
|
+
/**
|
|
86
|
+
* Suscribirse a eventos de autenticación
|
|
87
|
+
* @returns Función de cleanup para desuscribirse
|
|
88
|
+
*/
|
|
89
|
+
subscribe(listener: AuthEventListener): () => void;
|
|
90
|
+
/**
|
|
91
|
+
* Limpiar todos los listeners
|
|
92
|
+
*/
|
|
93
|
+
clear(): void;
|
|
94
|
+
/**
|
|
95
|
+
* Verificar si hay un evento siendo manejado
|
|
96
|
+
*/
|
|
97
|
+
isHandling(): boolean;
|
|
98
|
+
}
|
|
99
|
+
declare const authEventBus: AuthEventBus;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Sistema global de tracking de navegación para evitar monkey-patching anidado
|
|
103
|
+
* Usa patrón singleton con reference counting
|
|
104
|
+
*
|
|
105
|
+
* Problema que resuelve:
|
|
106
|
+
* - Si múltiples componentes usan useSession, cada uno crearía su propio monkey-patch
|
|
107
|
+
* - Esto causa anidamiento infinito de patches
|
|
108
|
+
* - NavigationTracker centraliza el patching con reference counting
|
|
109
|
+
*
|
|
110
|
+
* Uso:
|
|
111
|
+
* ```typescript
|
|
112
|
+
* const tracker = NavigationTracker.getInstance();
|
|
113
|
+
* const unsubscribe = tracker.subscribe(() => {
|
|
114
|
+
* console.log("Navigation detected!");
|
|
115
|
+
* });
|
|
116
|
+
*
|
|
117
|
+
* // Cleanup
|
|
118
|
+
* unsubscribe();
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
type NavigationCallback = () => void;
|
|
122
|
+
declare class NavigationTracker {
|
|
123
|
+
private static instance;
|
|
124
|
+
private isPatched;
|
|
125
|
+
private refCount;
|
|
126
|
+
private listeners;
|
|
127
|
+
private originalPushState;
|
|
128
|
+
private originalReplaceState;
|
|
129
|
+
private constructor();
|
|
130
|
+
/**
|
|
131
|
+
* Obtener instancia singleton
|
|
132
|
+
*/
|
|
133
|
+
static getInstance(): NavigationTracker;
|
|
134
|
+
/**
|
|
135
|
+
* Suscribirse a eventos de navegación
|
|
136
|
+
* @param callback - Función a llamar cuando hay navegación
|
|
137
|
+
* @returns Función de cleanup para desuscribirse
|
|
138
|
+
*/
|
|
139
|
+
subscribe(callback: NavigationCallback): () => void;
|
|
140
|
+
/**
|
|
141
|
+
* Desuscribirse de eventos de navegación
|
|
142
|
+
* @private
|
|
143
|
+
*/
|
|
144
|
+
private unsubscribe;
|
|
145
|
+
/**
|
|
146
|
+
* Aplicar monkey-patches a history API
|
|
147
|
+
* @private
|
|
148
|
+
*/
|
|
149
|
+
private applyPatches;
|
|
150
|
+
/**
|
|
151
|
+
* Remover monkey-patches y restaurar métodos originales
|
|
152
|
+
* @private
|
|
153
|
+
*/
|
|
154
|
+
private removePatches;
|
|
155
|
+
/**
|
|
156
|
+
* Notificar a todos los listeners
|
|
157
|
+
* @private
|
|
158
|
+
*/
|
|
159
|
+
private notifyListeners;
|
|
160
|
+
/**
|
|
161
|
+
* Para testing: limpiar completamente el tracker
|
|
162
|
+
* ⚠️ SOLO USAR EN TESTS
|
|
163
|
+
*/
|
|
164
|
+
static reset(): void;
|
|
165
|
+
/**
|
|
166
|
+
* Obtener número de suscriptores activos (útil para debugging)
|
|
167
|
+
*/
|
|
168
|
+
getSubscriberCount(): number;
|
|
169
|
+
/**
|
|
170
|
+
* Verificar si los patches están activos
|
|
171
|
+
*/
|
|
172
|
+
isActive(): boolean;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export { type AuthEvent, type AuthEventDetails, type AuthEventType, type ConfigResolverOptions, type CrudifyEnvironment, NavigationTracker, type ResolvedConfig, authEventBus, resolveConfig, useResolvedConfig };
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});var _chunk5HFI5CZ5js = require('./chunk-5HFI5CZ5.js');var _chunkNSV6ECYOjs = require('./chunk-NSV6ECYO.js');var _chunkMFYHD6S5js = require('./chunk-MFYHD6S5.js');exports.ERROR_CODES = _chunkNSV6ECYOjs.a; exports.ERROR_SEVERITY_MAP = _chunkNSV6ECYOjs.b; exports.NavigationTracker = _chunkMFYHD6S5js.p; exports.authEventBus = _chunkMFYHD6S5js.e; exports.createErrorTranslator = _chunkMFYHD6S5js.o; exports.decodeJwtSafely = _chunkMFYHD6S5js.q; exports.getCookie = _chunkMFYHD6S5js.b; exports.getCurrentUserEmail = _chunkMFYHD6S5js.r; exports.getErrorMessage = _chunkNSV6ECYOjs.e; exports.handleCrudifyError = _chunkNSV6ECYOjs.g; exports.isTokenExpired = _chunkMFYHD6S5js.s; exports.parseApiError = _chunkNSV6ECYOjs.c; exports.parseJavaScriptError = _chunkNSV6ECYOjs.f; exports.parseTransactionError = _chunkNSV6ECYOjs.d; exports.resolveConfig = _chunkMFYHD6S5js.c; exports.secureLocalStorage = _chunk5HFI5CZ5js.b; exports.secureSessionStorage = _chunk5HFI5CZ5js.a; exports.translateError = _chunkMFYHD6S5js.n; exports.translateErrorCode = _chunkMFYHD6S5js.l; exports.translateErrorCodes = _chunkMFYHD6S5js.m; exports.useResolvedConfig = _chunkMFYHD6S5js.d;
|
package/dist/utils.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as R,b as c}from"./chunk-JNEWPO2J.mjs";import{a as m,b as v,c as d,d as g,e as u,f as x,g as C}from"./chunk-JAPL7EZJ.mjs";import{b as r,c as e,d as o,e as t,l as a,m as n,n as s,o as E,p as i,q as p,r as f,s as l}from"./chunk-MGJZTOEM.mjs";export{m as ERROR_CODES,v as ERROR_SEVERITY_MAP,i as NavigationTracker,t as authEventBus,E as createErrorTranslator,p as decodeJwtSafely,r as getCookie,f as getCurrentUserEmail,u as getErrorMessage,C as handleCrudifyError,l as isTokenExpired,d as parseApiError,x as parseJavaScriptError,g as parseTransactionError,e as resolveConfig,c as secureLocalStorage,R as secureSessionStorage,s as translateError,a as translateErrorCode,n as translateErrorCodes,o as useResolvedConfig};
|
package/package.json
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nocios/crudify-components",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"engines": {
|
|
5
|
+
"node": ">=24.12.0"
|
|
6
|
+
},
|
|
7
|
+
"description": "Biblioteca de componentes UI para Crudify",
|
|
8
|
+
"author": "Nocios",
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"main": "dist/index.js",
|
|
11
|
+
"module": "dist/index.mjs",
|
|
12
|
+
"types": "dist/index.d.ts",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"import": "./dist/index.mjs",
|
|
17
|
+
"require": "./dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./components": {
|
|
20
|
+
"types": "./dist/components/index.d.ts",
|
|
21
|
+
"import": "./dist/components/index.mjs",
|
|
22
|
+
"require": "./dist/components/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./hooks": {
|
|
25
|
+
"types": "./dist/hooks/index.d.ts",
|
|
26
|
+
"import": "./dist/hooks/index.mjs",
|
|
27
|
+
"require": "./dist/hooks/index.js"
|
|
28
|
+
},
|
|
29
|
+
"./utils": {
|
|
30
|
+
"types": "./dist/utils/index.d.ts",
|
|
31
|
+
"import": "./dist/utils/index.mjs",
|
|
32
|
+
"require": "./dist/utils/index.js"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsup",
|
|
37
|
+
"build:analyze": "tsup --metafile",
|
|
38
|
+
"start": "vite src/example",
|
|
39
|
+
"test": "vitest --coverage",
|
|
40
|
+
"test:ui": "vitest --ui",
|
|
41
|
+
"prepublishOnly": "npm run build"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@mdxeditor/editor": "^3.52.0",
|
|
45
|
+
"@nocios/crudify-admin": "^5.0.2",
|
|
46
|
+
"@nocios/crudify-sdk": "^1.0.0",
|
|
47
|
+
"dompurify": "^3.2.7",
|
|
48
|
+
"uuid": "^13.0.0"
|
|
49
|
+
},
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"@emotion/react": "^11.13.1",
|
|
52
|
+
"@emotion/styled": "^11.13.0",
|
|
53
|
+
"@mui/icons-material": "^7.1.0",
|
|
54
|
+
"@mui/material": "^7.1.0",
|
|
55
|
+
"@mui/x-data-grid": "^8.5.1",
|
|
56
|
+
"i18next-browser-languagedetector": "^8.1.0",
|
|
57
|
+
"i18next-http-backend": "^3.0.2",
|
|
58
|
+
"react": "^19.1.0",
|
|
59
|
+
"react-dom": "^19.1.0",
|
|
60
|
+
"react-i18next": "^15.5.2",
|
|
61
|
+
"react-router-dom": "^6.0.0 || ^7.0.0"
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@emotion/react": "^11.14.0",
|
|
65
|
+
"@emotion/styled": "^11.14.1",
|
|
66
|
+
"@mui/icons-material": "^7.3.4",
|
|
67
|
+
"@mui/material": "^7.3.4",
|
|
68
|
+
"@mui/x-data-grid": "^8.13.1",
|
|
69
|
+
"@testing-library/jest-dom": "^6.1.5",
|
|
70
|
+
"@testing-library/react": "^16.3.0",
|
|
71
|
+
"@testing-library/user-event": "^14.5.1",
|
|
72
|
+
"@types/react": "^19.0.3",
|
|
73
|
+
"@types/react-dom": "^19.0.1",
|
|
74
|
+
"@vitest/coverage-v8": "^1.1.0",
|
|
75
|
+
"@vitest/ui": "^1.1.0",
|
|
76
|
+
"i18next-browser-languagedetector": "^8.2.0",
|
|
77
|
+
"i18next-http-backend": "^3.0.2",
|
|
78
|
+
"jsdom": "^23.0.1",
|
|
79
|
+
"react-i18next": "^16.0.0",
|
|
80
|
+
"react-router-dom": "^7.9.3",
|
|
81
|
+
"tsup": "^8.4.0",
|
|
82
|
+
"typescript": "^5.1.3",
|
|
83
|
+
"vitest": "^1.1.0"
|
|
84
|
+
},
|
|
85
|
+
"publishConfig": {
|
|
86
|
+
"access": "public"
|
|
87
|
+
}
|
|
88
|
+
}
|
package/vitest.config.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { defineConfig } from "vitest/config";
|
|
2
|
+
import path from "path";
|
|
3
|
+
|
|
4
|
+
export default defineConfig({
|
|
5
|
+
test: {
|
|
6
|
+
globals: true,
|
|
7
|
+
environment: "jsdom",
|
|
8
|
+
setupFiles: ["./src/__tests__/setup.ts"],
|
|
9
|
+
testTimeout: 10000, // 10 seconds timeout for tests
|
|
10
|
+
hookTimeout: 10000, // 10 seconds timeout for hooks
|
|
11
|
+
pool: "forks", // Use forks pool for better isolation in CI
|
|
12
|
+
poolOptions: {
|
|
13
|
+
forks: {
|
|
14
|
+
singleFork: true, // Use single fork to reduce memory usage
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
coverage: {
|
|
18
|
+
provider: "v8",
|
|
19
|
+
reporter: ["text", "json", "html"],
|
|
20
|
+
exclude: ["node_modules/", "src/__tests__/", "dist/", "**/*.d.ts", "**/*.config.*", "**/mockData.ts", "**/testUtils.tsx", "**/testUtils.ts"],
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
resolve: {
|
|
24
|
+
alias: {
|
|
25
|
+
"@": path.resolve(__dirname, "./src"),
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
});
|