@softlutions/hooks 1.0.8 → 1.0.10

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.
@@ -1,15 +1,27 @@
1
1
  export type IRolesCan = 'admin' | 'funcionario' | 'supervisor' | 'contabilidade' | 'master' | 'gerente' | 'suporte';
2
2
  export type IRolesCannot = `!${IRolesCan}`;
3
3
  export type IUseCheckRoleProps = readonly (IRolesCan | IRolesCannot)[];
4
- declare function setRole(role: string | undefined): void;
4
+ export interface IGetTokenRolesOptions {
5
+ /** Apelidos de roles do token para roles do sistema (ex.: { administrador: 'admin' }) */
6
+ map?: Record<string, string>;
7
+ }
8
+ declare function setRoles(roles: readonly string[] | undefined): void;
9
+ declare function setRole(role: string | readonly string[] | undefined): void;
10
+ declare function clearRoles(): void;
5
11
  declare function clearRole(): void;
6
- declare function getRole(): string | undefined;
12
+ declare function getRoles(): string[];
13
+ declare function getRole(): string;
7
14
  declare function checkRole(roles?: IUseCheckRoleProps): any;
15
+ export declare function getTokenRoles(token: string | null | undefined, options?: IGetTokenRolesOptions): string[];
8
16
  export declare const useCheckRole: () => {
9
17
  setRole: typeof setRole;
18
+ setRoles: typeof setRoles;
10
19
  clearRole: typeof clearRole;
20
+ clearRoles: typeof clearRoles;
11
21
  getRole: typeof getRole;
22
+ getRoles: typeof getRoles;
12
23
  checkRole: typeof checkRole;
24
+ getTokenRoles: typeof getTokenRoles;
13
25
  };
14
26
  export {};
15
27
  //# sourceMappingURL=use-check-role.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-check-role.d.ts","sourceRoot":"","sources":["../src/use-check-role.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,SAAS,GACf,OAAO,GACP,aAAa,GACb,YAAY,GACZ,eAAe,GACf,QAAQ,GACR,SAAS,GACT,SAAS,CAAC;AAEhB,MAAM,MAAM,YAAY,GAAG,IAAI,SAAS,EAAE,CAAC;AAC3C,MAAM,MAAM,kBAAkB,GAAG,SAAS,CAAC,SAAS,GAAG,YAAY,CAAC,EAAE,CAAC;AAOvE,iBAAS,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,QAExC;AAED,iBAAS,SAAS,SAEjB;AAED,iBAAS,OAAO,uBAEf;AAED,iBAAS,SAAS,CAAC,KAAK,CAAC,EAAE,kBAAkB,GAAG,GAAG,CAkBlD;AAED,eAAO,MAAM,YAAY;;;;;CAKvB,CAAC"}
1
+ {"version":3,"file":"use-check-role.d.ts","sourceRoot":"","sources":["../src/use-check-role.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,SAAS,GACf,OAAO,GACP,aAAa,GACb,YAAY,GACZ,eAAe,GACf,QAAQ,GACR,SAAS,GACT,SAAS,CAAC;AAEhB,MAAM,MAAM,YAAY,GAAG,IAAI,SAAS,EAAE,CAAC;AAC3C,MAAM,MAAM,kBAAkB,GAAG,SAAS,CAAC,SAAS,GAAG,YAAY,CAAC,EAAE,CAAC;AAEvE,MAAM,WAAW,qBAAqB;IAClC,yFAAyF;IACzF,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAsBD,iBAAS,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,QAGrD;AAED,iBAAS,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,QAG5D;AAED,iBAAS,UAAU,SAElB;AAED,iBAAS,SAAS,SAEjB;AAED,iBAAS,QAAQ,aAEhB;AAED,iBAAS,OAAO,WAEf;AAED,iBAAS,SAAS,CAAC,KAAK,CAAC,EAAE,kBAAkB,GAAG,GAAG,CAiBlD;AAmBD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,MAAM,EAAE,CAuBzG;AAED,eAAO,MAAM,YAAY;;;;;;;;;CASvB,CAAC"}
@@ -1,36 +1,97 @@
1
1
  'use client';
2
- let _role;
3
- const norm = (s) => s.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
2
+ const SYSTEM_ROLES = [
3
+ 'admin',
4
+ 'funcionario',
5
+ 'supervisor',
6
+ 'contabilidade',
7
+ 'master',
8
+ 'gerente',
9
+ 'suporte',
10
+ ];
11
+ let _roles = [];
12
+ const norm = (s) => s.normalize('NFD').replace(/[̀-ͯ]/g, '').toLowerCase();
13
+ const clean = (s) => norm(s)
14
+ .replace(/\s+/g, '')
15
+ .replace(/[^a-z0-9-_]/g, '');
16
+ function setRoles(roles) {
17
+ const list = (roles ?? []).map((r) => String(r).trim()).filter(Boolean);
18
+ _roles = Array.from(new Set(list));
19
+ }
4
20
  function setRole(role) {
5
- _role = role?.trim();
21
+ if (Array.isArray(role))
22
+ setRoles(role);
23
+ else
24
+ setRoles(role ? [role] : []);
25
+ }
26
+ function clearRoles() {
27
+ _roles = [];
6
28
  }
7
29
  function clearRole() {
8
- _role = undefined;
30
+ clearRoles();
31
+ }
32
+ function getRoles() {
33
+ return [..._roles];
9
34
  }
10
35
  function getRole() {
11
- return _role;
36
+ return _roles[0];
12
37
  }
13
38
  function checkRole(roles) {
14
39
  if (!roles || roles.length === 0)
15
40
  return true;
16
- const current = _role;
17
- if (!current)
41
+ if (_roles.length === 0)
18
42
  return false;
19
- const userRole = norm(current);
43
+ const userRoles = _roles.map(norm);
20
44
  const hasNegation = roles.some((r) => String(r).startsWith('!'));
21
45
  if (hasNegation) {
22
- return roles.every((r) => {
23
- const rr = String(r);
24
- const target = rr.startsWith('!') ? rr.slice(1) : rr;
25
- return userRole !== norm(target);
26
- });
46
+ const negated = roles.map((r) => norm(String(r).replace(/^!/, '')));
47
+ const known = userRoles.filter((r) => SYSTEM_ROLES.includes(r));
48
+ const pool = known.length > 0 ? known : userRoles;
49
+ return pool.some((r) => !negated.includes(r));
27
50
  }
28
- return roles.some((r) => norm(String(r)) === userRole);
51
+ return roles.some((r) => userRoles.includes(norm(String(r))));
52
+ }
53
+ function decodeJwtPayload(token) {
54
+ try {
55
+ const base64Url = token.replace(/^"|"$/g, '').split('.')[1];
56
+ const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
57
+ const padded = base64.padEnd(base64.length + ((4 - (base64.length % 4)) % 4), '=');
58
+ const json = decodeURIComponent(atob(padded)
59
+ .split('')
60
+ .map((c) => `%${`00${c.charCodeAt(0).toString(16)}`.slice(-2)}`)
61
+ .join(''));
62
+ return JSON.parse(json);
63
+ }
64
+ catch {
65
+ return undefined;
66
+ }
67
+ }
68
+ export function getTokenRoles(token, options) {
69
+ if (!token)
70
+ return [];
71
+ const payload = decodeJwtPayload(token);
72
+ if (!payload)
73
+ return [];
74
+ const raw = [
75
+ ...(payload.realm_access?.roles ?? []),
76
+ ...Object.values(payload.resource_access ?? {}).flatMap((client) => client?.roles ?? []),
77
+ ...(Array.isArray(payload.roles) ? payload.roles : []),
78
+ ];
79
+ const map = Object.fromEntries(Object.entries(options?.map ?? {}).map(([from, to]) => [clean(from), clean(to)]));
80
+ const roles = raw
81
+ .filter((r) => typeof r === 'string')
82
+ .map(clean)
83
+ .filter(Boolean)
84
+ .map((r) => map[r] ?? r);
85
+ return Array.from(new Set(roles));
29
86
  }
30
87
  export const useCheckRole = () => ({
31
88
  setRole,
89
+ setRoles,
32
90
  clearRole,
91
+ clearRoles,
33
92
  getRole,
93
+ getRoles,
34
94
  checkRole,
95
+ getTokenRoles,
35
96
  });
36
97
  //# sourceMappingURL=use-check-role.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-check-role.js","sourceRoot":"","sources":["../src/use-check-role.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAcb,IAAI,KAAyB,CAAC;AAE9B,MAAM,IAAI,GAAG,CAAC,CAAS,EAAE,EAAE,CACvB,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;AAErE,SAAS,OAAO,CAAC,IAAwB;IACrC,KAAK,GAAG,IAAI,EAAE,IAAI,EAAE,CAAC;AACzB,CAAC;AAED,SAAS,SAAS;IACd,KAAK,GAAG,SAAS,CAAC;AACtB,CAAC;AAED,SAAS,OAAO;IACZ,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,SAAS,SAAS,CAAC,KAA0B;IACzC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAE9C,MAAM,OAAO,GAAG,KAAK,CAAC;IACtB,IAAI,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IAE3B,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;IAC/B,MAAM,WAAW,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;IAEjE,IAAI,WAAW,EAAE,CAAC;QACd,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE;YACrB,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;YACrB,MAAM,MAAM,GAAG,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACrD,OAAO,QAAQ,KAAK,IAAI,CAAC,MAAM,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;IACP,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAA;AAC1D,CAAC;AAED,MAAM,CAAC,MAAM,YAAY,GAAG,GAAG,EAAE,CAAC,CAAC;IAC/B,OAAO;IACP,SAAS;IACT,OAAO;IACP,SAAS;CACZ,CAAC,CAAC"}
1
+ {"version":3,"file":"use-check-role.js","sourceRoot":"","sources":["../src/use-check-role.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,41 +1,41 @@
1
- {
2
- "name": "@softlutions/hooks",
3
- "version": "1.0.8",
4
- "description": "Lib de utilitários",
5
- "type": "module",
6
- "main": "./dist/index.js",
7
- "module": "./dist/index.js",
8
- "types": "./dist/index.d.ts",
9
- "exports": {
10
- ".": {
11
- "types": "./dist/index.d.ts",
12
- "default": "./dist/index.js"
13
- }
14
- },
15
- "files": [
16
- "dist",
17
- "README.md",
18
- "LICENSE"
19
- ],
20
- "scripts": {
21
- "build": "tsc -p tsconfig.json",
22
- "deploy": "npm run build && npm version patch && npm publish --access public"
23
- },
24
- "repository": {
25
- "type": "git",
26
- "url": "git+https://github.com/Softlutions-Expertise/utils.git"
27
- },
28
- "author": "softlutions",
29
- "license": "ISC",
30
- "devDependencies": {
31
- "@types/react": "^18.2.15",
32
- "rimraf": "^6.0.1",
33
- "typescript": "^5.9.3"
34
- },
35
- "dependencies": {
36
- "@mui/material": "^7.3.4",
37
- "framer-motion": "^12.23.22",
38
- "notistack": "^3.0.2",
39
- "react": "^19.2.0"
40
- }
41
- }
1
+ {
2
+ "name": "@softlutions/hooks",
3
+ "version": "1.0.10",
4
+ "description": "Lib de utilitários",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "default": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsc -p tsconfig.json",
22
+ "deploy": "npm run build && npm version patch && npm publish --access public"
23
+ },
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/Softlutions-Expertise/utils.git"
27
+ },
28
+ "author": "softlutions",
29
+ "license": "ISC",
30
+ "devDependencies": {
31
+ "@types/react": "^18.2.15",
32
+ "rimraf": "^6.0.1",
33
+ "typescript": "^5.9.3"
34
+ },
35
+ "dependencies": {
36
+ "@mui/material": "^7.3.4",
37
+ "framer-motion": "^12.23.22",
38
+ "notistack": "^3.0.2",
39
+ "react": "^19.2.0"
40
+ }
41
+ }