@vegait/accounts-sdk 0.1.8 → 0.1.9-beta.49447
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.cjs +2 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -17
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -120,10 +120,7 @@ var PRODUCT_CATALOG = {
|
|
|
120
120
|
code: "sim",
|
|
121
121
|
name: "SIM",
|
|
122
122
|
description: "Service Issue Management",
|
|
123
|
-
url: "",
|
|
124
|
-
// nao pode aparecer em prod ainda (13/09); override de HML via NEXT_PUBLIC_SIM_URL,
|
|
125
|
-
// que o pipeline ja aponta pra hml.sim.app.vegait.com. URL de prod real:
|
|
126
|
-
// https://sim.app.vegait.com/ -- devolver ao default quando liberar.
|
|
123
|
+
url: "https://sim.app.vegait.com/",
|
|
127
124
|
accent: "#5B7DB1",
|
|
128
125
|
ssoEnabled: false
|
|
129
126
|
},
|
|
@@ -151,19 +148,7 @@ var PRODUCT_CATALOG = {
|
|
|
151
148
|
description: "Inteligencia comportamental",
|
|
152
149
|
url: "https://behavior.app.vegait.com/",
|
|
153
150
|
accent: "#4A7C7E",
|
|
154
|
-
ssoEnabled:
|
|
155
|
-
// publicado em prod; cliente OIDC `behavior-admin` seedado (.env.prod)
|
|
156
|
-
},
|
|
157
|
-
vegai: {
|
|
158
|
-
code: "vegai",
|
|
159
|
-
name: "VegAI",
|
|
160
|
-
description: "Atendimento com IA para hospedes",
|
|
161
|
-
url: "https://vegai.app.vegait.com/",
|
|
162
|
-
// prod subiu depois da !16890, que assumia
|
|
163
|
-
// que so existia HML; override de HML segue
|
|
164
|
-
// no NEXT_PUBLIC_VEGAI_URL do pipeline.
|
|
165
|
-
accent: "#6E56CF",
|
|
166
|
-
ssoEnabled: true
|
|
151
|
+
ssoEnabled: false
|
|
167
152
|
}
|
|
168
153
|
};
|
|
169
154
|
var LEGACY_CODE_ALIASES = {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/claims.ts","../src/persona.ts","../src/productCatalog.ts","../src/identityModel.ts"],"sourcesContent":["// Barrel do @vegait/accounts-sdk. Preenchido pelas tasks seguintes.\n\nexport type { AccountsClaims } from \"./claims\";\nexport {\n decodeJwtPayload,\n decodeAccessToken,\n parseJsonArrayClaim,\n normalizeClaimArray,\n} from \"./claims\";\n\nexport type { Persona } from \"./persona\";\nexport { derivePersona } from \"./persona\";\n\nexport type { ProductMeta, CatalogProduct } from \"./productCatalog\";\nexport {\n PRODUCT_CATALOG,\n resolveProduct,\n getAccessibleProducts,\n getCatalogWithAccess,\n} from \"./productCatalog\";\n\nexport type { IdentityModel } from \"./identityModel\";\nexport { buildIdentityModel } from \"./identityModel\";\n","// Decoder de payload JWT sem verificacao criptografica. Universal (browser +\n// Node). Usar APENAS para extrair claims de tokens ja confiaveis (recebidos do\n// Accounts via fluxo OIDC). NUNCA para validar tokens de origem externa.\n\nexport function decodeJwtPayload<T = Record<string, unknown>>(token: string): T {\n const parts = token.split(\".\");\n if (parts.length !== 3) {\n throw new Error(\"Invalid JWT format\");\n }\n const payload = parts[1].replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded = payload + \"=\".repeat((4 - (payload.length % 4)) % 4);\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n const json = new TextDecoder(\"utf-8\").decode(bytes);\n return JSON.parse(json) as T;\n}\n\n// Claims do AT emitido pelo Accounts via OpenIddict. Tudo opcional (nem todo\n// campo chega em todo contexto). unit_ids/products vem como string contendo\n// JSON array. platform_role aparece so para Time VEGA. Espelha o token; fonte\n// da verdade = VegaClaimsHandler no accounts.api.\nexport type AccountsClaims = {\n sub?: string;\n email?: string;\n name?: string;\n platform_role?: string;\n can_manage_scope_members?: string;\n org_id?: string | string[];\n unit_ids?: string;\n unit_owner_ids?: string;\n active_unit_id?: string;\n products?: string;\n exp?: number;\n iat?: number;\n iss?: string;\n aud?: string | string[];\n};\n\n// Normaliza claim que pode vir como string ou array (OpenIddict emite array\n// quando ha multiplos valores, scalar quando ha um so).\nexport function normalizeClaimArray(\n value: string | string[] | undefined,\n): string[] {\n if (!value) return [];\n return Array.isArray(value) ? value : [value];\n}\n\n// Parseia claim cujo value e string contendo JSON array (unit_ids, products).\n// Lista vazia em claim ausente ou JSON malformado.\nexport function parseJsonArrayClaim(value: string | undefined): string[] {\n if (!value) return [];\n try {\n const parsed = JSON.parse(value);\n return Array.isArray(parsed)\n ? parsed.filter((v): v is string => typeof v === \"string\")\n : [];\n } catch {\n return [];\n }\n}\n\n// Wrapper safe que devolve null em vez de jogar em token invalido.\nexport function decodeAccessToken(\n token: string | null | undefined,\n): AccountsClaims | null {\n if (!token) return null;\n try {\n return decodeJwtPayload<AccountsClaims>(token);\n } catch {\n return null;\n }\n}\n","import type { AccountsClaims } from \"./claims\";\n\nexport type Persona = \"usuario\" | \"gestor\" | \"vega\";\n\n// Deriva a persona a partir das claims do AT. Fail-closed: sem claims ou sem\n// sinal de gestao, cai em 'usuario' (menor privilegio). platform_role (Time\n// VEGA) tem precedencia sobre a flag de gestor de cliente.\n// ATENCAO: presentation gating, NAO autorizacao (ver README).\nexport function derivePersona(claims: AccountsClaims | null): Persona {\n if (claims?.platform_role) return \"vega\";\n if (claims?.can_manage_scope_members === \"true\") return \"gestor\";\n return \"usuario\";\n}\n","import type { AccountsClaims } from \"./claims\";\nimport { parseJsonArrayClaim } from \"./claims\";\nimport { derivePersona } from \"./persona\";\n\n// Catalogo cliente-side dos produtos VEGA. Mapeia o code (claim `products` do\n// AT) pra display name + URL default + accent. codes batem com o seed do\n// backend (SeedMediaHubInProductCatalog etc.).\n//\n// ssoEnabled = METADADO de COMO o produto abre (SSO unificado via Accounts vs.\n// login proprio). NAO e gate de visibilidade desde o deploy waffle-completo\n// (17/08). Um produto com ssoEnabled=false aparece normalmente; ao clicar, o\n// usuario cai no login proprio do produto — comportamento aceito pelo produto/PO.\n// Visibilidade = acesso (persona/claim) AND url truthy (pos-override de env).\n//\n// URLs aqui sao DEFAULTS de producao. O consumidor sobrescreve por ambiente via\n// o parametro `urls` (do seu proprio env) -- o SDK NAO le process.env.\n\nexport type ProductMeta = {\n code: string;\n name: string;\n description: string;\n url: string;\n accent: string;\n ssoEnabled: boolean;\n};\n\nexport const PRODUCT_CATALOG: Record<string, ProductMeta> = {\n accounts: {\n code: \"accounts\",\n name: \"Accounts\",\n description: \"Identidade unificada do ecossistema\",\n url: \"/\",\n accent: \"#1F2E52\",\n ssoEnabled: true, // e o proprio IdP; excluido da gaveta de qualquer forma\n },\n \"media-hub\": {\n code: \"media-hub\",\n name: \"Media Hub\",\n description: \"Sinalizacao digital nas unidades\",\n url: \"\", // sem frontend publico deployado; override local via NEXT_PUBLIC_MEDIA_HUB_URL\n accent: \"#EF802D\",\n ssoEnabled: true,\n },\n sig: {\n code: \"sig\",\n name: \"SIG\",\n description: \"Housekeeping e governanca das unidades\",\n url: \"https://sig.app.vegait.com/\",\n accent: \"#B0C925\",\n ssoEnabled: true,\n },\n gdi: {\n code: \"gdi\",\n name: \"GDI\",\n description: \"Gestao de dispositivos e internet\",\n url: \"\", // ainda nao esta em prod; override de HML via NEXT_PUBLIC_GDI_URL\n // quando ligar em prod, sera https://gdi.app.vegait.com/\n accent: \"#7B8B7E\",\n ssoEnabled: false,\n },\n sim: {\n code: \"sim\",\n name: \"SIM\",\n description: \"Service Issue Management\",\n url: \"\", // nao pode aparecer em prod ainda (13/09); override de HML via NEXT_PUBLIC_SIM_URL,\n // que o pipeline ja aponta pra hml.sim.app.vegait.com. URL de prod real:\n // https://sim.app.vegait.com/ -- devolver ao default quando liberar.\n accent: \"#5B7DB1\",\n ssoEnabled: false,\n },\n hxp: {\n code: \"hxp\",\n name: \"HXP\",\n description: \"Hotel Experience Platform\",\n url: \"\", // nao ligado no Accounts\n accent: \"#9B6A4F\",\n ssoEnabled: false,\n },\n feedback: {\n code: \"feedback\",\n name: \"Feedback\",\n description: \"Pesquisas e NPS de hospedes\",\n url: \"\", // nao ligado no Accounts\n accent: \"#C26A8C\",\n ssoEnabled: false,\n },\n behavior: {\n code: \"behavior\",\n name: \"Behavior\",\n description: \"Inteligencia comportamental\",\n url: \"https://behavior.app.vegait.com/\",\n accent: \"#4A7C7E\",\n ssoEnabled: true, // publicado em prod; cliente OIDC `behavior-admin` seedado (.env.prod)\n },\n vegai: {\n code: \"vegai\",\n name: \"VegAI\",\n description: \"Atendimento com IA para hospedes\",\n url: \"https://vegai.app.vegait.com/\", // prod subiu depois da !16890, que assumia\n // que so existia HML; override de HML segue\n // no NEXT_PUBLIC_VEGAI_URL do pipeline.\n accent: \"#6E56CF\",\n ssoEnabled: true,\n },\n};\n\n// Aliases de code legado -> canonico. Resolve dados gravados ANTES de uma\n// renomeacao (ex.: governanca -> sig) sem duplicar entrada no catalogo.\nconst LEGACY_CODE_ALIASES: Record<string, string> = {\n governanca: \"sig\",\n};\n\n// resolveProduct: retorna o meta cru do produto (com override aplicado se houver).\n// NAO filtra url falsy — o chamador decide o que fazer com produto sem URL.\n// getCatalogWithAccess e getAccessibleProducts filtram apos o override (Opcao A).\n// `urls` sobrescreve a URL default pelo code CANONICO. Code desconhecido => fallback ssoEnabled=false.\nexport function resolveProduct(\n code: string,\n urls?: Record<string, string>,\n): ProductMeta {\n const canonical = LEGACY_CODE_ALIASES[code] ?? code;\n const base =\n PRODUCT_CATALOG[canonical] ??\n ({\n code,\n name: code,\n description: \"\",\n url: \"#\",\n accent: \"#1F2E52\",\n ssoEnabled: false,\n } satisfies ProductMeta);\n const override = urls?.[canonical];\n return override ? { ...base, url: override } : base;\n}\n\nexport type CatalogProduct = ProductMeta & { accessible: boolean };\n\n// Catalogo COMPLETO pro app launcher (menos o Accounts, que e o proprio IdP),\n// com flag de acesso: Time VEGA acessa tudo; cliente acessa os codes da claim\n// `products` (aliases legados resolvidos). ssoEnabled NAO esconde produto aqui\n// — e metadado de COMO o produto abre, nao de quem ve. Sem claims: tudo\n// inacessivel (fail-closed). `urls` sobrescreve URLs por ambiente.\nexport function getCatalogWithAccess(\n claims: AccountsClaims | null,\n opts?: { urls?: Record<string, string> },\n): CatalogProduct[] {\n const urls = opts?.urls;\n const isVega = claims != null && derivePersona(claims) === \"vega\";\n const accessibleCodes = new Set(\n parseJsonArrayClaim(claims?.products).map(\n (code) => LEGACY_CODE_ALIASES[code] ?? code,\n ),\n );\n return Object.values(PRODUCT_CATALOG)\n .filter((p) => p.code !== \"accounts\")\n .map((p) => ({\n ...(urls?.[p.code] ? { ...p, url: urls[p.code] } : p),\n accessible: isVega || accessibleCodes.has(p.code),\n }))\n .filter((p) => p.url); // Opcao A: produto sem URL publica some do waffle/card/drawer\n}\n\n// Produtos do app launcher (gaveta/drawer). Visibilidade = acesso (persona/claim)\n// AND url truthy (pos-override de env). ssoEnabled NAO filtra — e apenas metadado\n// de como o produto abre. `urls` sobrescreve URLs por ambiente (override aplicado\n// ANTES do filtro de url, para que env possa ressuscitar produto sem URL default).\nexport function getAccessibleProducts(\n claims: AccountsClaims | null,\n opts?: { urls?: Record<string, string> },\n): ProductMeta[] {\n if (!claims) return [];\n const urls = opts?.urls;\n if (derivePersona(claims) === \"vega\") {\n return Object.values(PRODUCT_CATALOG)\n .filter((p) => p.code !== \"accounts\")\n .map((p) => (urls?.[p.code] ? { ...p, url: urls[p.code] } : p))\n .filter((p) => p.url); // Opcao A: sem URL publica = nao lancavel\n }\n return parseJsonArrayClaim(claims.products)\n .map((code) => resolveProduct(code, urls))\n .filter((p) => p.url); // Opcao A: idem; ssoEnabled nao filtra\n}\n","import type { AccountsClaims } from \"./claims\";\nimport { derivePersona, type Persona } from \"./persona\";\nimport {\n getAccessibleProducts,\n getCatalogWithAccess,\n type CatalogProduct,\n type ProductMeta,\n} from \"./productCatalog\";\n\n// Modelo de dados da casca de identidade (SEM React). O host mapeia isto pros\n// props do <IdentityBar> do @vegait/lyra-ds-v2, adicionando icones/labels/\n// callbacks a partir de `capabilities`.\nexport type IdentityModel = {\n persona: Persona;\n isUnitOwner: boolean;\n user: { name: string; email: string };\n products: ProductMeta[];\n /** Catalogo completo (menos Accounts) com flag de acesso — pro waffle\n * mostrar tudo e desabilitar o que a persona nao acessa. `products`\n * permanece so-acessiveis por retrocompat (SIG consome hoje). */\n catalog: CatalogProduct[];\n currentProductCode?: string;\n capabilities: {\n manageUsers: boolean; // gestor/vega\n adminConsole: boolean; // vega\n switchContext: boolean; // gestor/vega\n manageNetwork: boolean; // gestor (rodape do launcher)\n manageProducts: boolean; // owner da unidade ativa\n appointAdmins: boolean; // owner delega admin por produto\n };\n};\n\nfunction deriveIsUnitOwner(claims: AccountsClaims | null): boolean {\n if (!claims?.unit_owner_ids || !claims.active_unit_id) return false;\n try {\n const owned = JSON.parse(claims.unit_owner_ids) as string[];\n return Array.isArray(owned) && owned.includes(claims.active_unit_id);\n } catch {\n return false;\n }\n}\n\nexport function buildIdentityModel(\n claims: AccountsClaims | null,\n cfg?: { currentProductCode?: string; urls?: Record<string, string> },\n): IdentityModel {\n const persona = derivePersona(claims);\n const isUnitOwner = deriveIsUnitOwner(claims);\n return {\n persona,\n isUnitOwner,\n user: { name: claims?.name ?? \"\", email: claims?.email ?? \"\" },\n products: getAccessibleProducts(claims, { urls: cfg?.urls }),\n catalog: getCatalogWithAccess(claims, { urls: cfg?.urls }),\n currentProductCode: cfg?.currentProductCode,\n capabilities: {\n manageUsers: persona === \"gestor\" || persona === \"vega\",\n adminConsole: persona === \"vega\",\n switchContext: persona === \"gestor\" || persona === \"vega\",\n manageNetwork: persona === \"gestor\",\n manageProducts: isUnitOwner,\n appointAdmins: isUnitOwner,\n },\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/claims.ts","../src/persona.ts","../src/productCatalog.ts","../src/identityModel.ts"],"sourcesContent":["// Barrel do @vegait/accounts-sdk. Preenchido pelas tasks seguintes.\n\nexport type { AccountsClaims } from \"./claims\";\nexport {\n decodeJwtPayload,\n decodeAccessToken,\n parseJsonArrayClaim,\n normalizeClaimArray,\n} from \"./claims\";\n\nexport type { Persona } from \"./persona\";\nexport { derivePersona } from \"./persona\";\n\nexport type { ProductMeta, CatalogProduct } from \"./productCatalog\";\nexport {\n PRODUCT_CATALOG,\n resolveProduct,\n getAccessibleProducts,\n getCatalogWithAccess,\n} from \"./productCatalog\";\n\nexport type { IdentityModel } from \"./identityModel\";\nexport { buildIdentityModel } from \"./identityModel\";\n","// Decoder de payload JWT sem verificacao criptografica. Universal (browser +\n// Node). Usar APENAS para extrair claims de tokens ja confiaveis (recebidos do\n// Accounts via fluxo OIDC). NUNCA para validar tokens de origem externa.\n\nexport function decodeJwtPayload<T = Record<string, unknown>>(token: string): T {\n const parts = token.split(\".\");\n if (parts.length !== 3) {\n throw new Error(\"Invalid JWT format\");\n }\n const payload = parts[1].replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded = payload + \"=\".repeat((4 - (payload.length % 4)) % 4);\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n const json = new TextDecoder(\"utf-8\").decode(bytes);\n return JSON.parse(json) as T;\n}\n\n// Claims do AT emitido pelo Accounts via OpenIddict. Tudo opcional (nem todo\n// campo chega em todo contexto). unit_ids/products vem como string contendo\n// JSON array. platform_role aparece so para Time VEGA. Espelha o token; fonte\n// da verdade = VegaClaimsHandler no accounts.api.\nexport type AccountsClaims = {\n sub?: string;\n email?: string;\n name?: string;\n platform_role?: string;\n can_manage_scope_members?: string;\n org_id?: string | string[];\n unit_ids?: string;\n unit_owner_ids?: string;\n active_unit_id?: string;\n products?: string;\n exp?: number;\n iat?: number;\n iss?: string;\n aud?: string | string[];\n};\n\n// Normaliza claim que pode vir como string ou array (OpenIddict emite array\n// quando ha multiplos valores, scalar quando ha um so).\nexport function normalizeClaimArray(\n value: string | string[] | undefined,\n): string[] {\n if (!value) return [];\n return Array.isArray(value) ? value : [value];\n}\n\n// Parseia claim cujo value e string contendo JSON array (unit_ids, products).\n// Lista vazia em claim ausente ou JSON malformado.\nexport function parseJsonArrayClaim(value: string | undefined): string[] {\n if (!value) return [];\n try {\n const parsed = JSON.parse(value);\n return Array.isArray(parsed)\n ? parsed.filter((v): v is string => typeof v === \"string\")\n : [];\n } catch {\n return [];\n }\n}\n\n// Wrapper safe que devolve null em vez de jogar em token invalido.\nexport function decodeAccessToken(\n token: string | null | undefined,\n): AccountsClaims | null {\n if (!token) return null;\n try {\n return decodeJwtPayload<AccountsClaims>(token);\n } catch {\n return null;\n }\n}\n","import type { AccountsClaims } from \"./claims\";\n\nexport type Persona = \"usuario\" | \"gestor\" | \"vega\";\n\n// Deriva a persona a partir das claims do AT. Fail-closed: sem claims ou sem\n// sinal de gestao, cai em 'usuario' (menor privilegio). platform_role (Time\n// VEGA) tem precedencia sobre a flag de gestor de cliente.\n// ATENCAO: presentation gating, NAO autorizacao (ver README).\nexport function derivePersona(claims: AccountsClaims | null): Persona {\n if (claims?.platform_role) return \"vega\";\n if (claims?.can_manage_scope_members === \"true\") return \"gestor\";\n return \"usuario\";\n}\n","import type { AccountsClaims } from \"./claims\";\nimport { parseJsonArrayClaim } from \"./claims\";\nimport { derivePersona } from \"./persona\";\n\n// Catalogo cliente-side dos produtos VEGA. Mapeia o code (claim `products` do\n// AT) pra display name + URL default + accent. codes batem com o seed do\n// backend (SeedMediaHubInProductCatalog etc.).\n//\n// ssoEnabled = METADADO de COMO o produto abre (SSO unificado via Accounts vs.\n// login proprio). NAO e gate de visibilidade desde o deploy waffle-completo\n// (17/08). Um produto com ssoEnabled=false aparece normalmente; ao clicar, o\n// usuario cai no login proprio do produto — comportamento aceito pelo produto/PO.\n// Visibilidade = acesso (persona/claim) AND url truthy (pos-override de env).\n//\n// URLs aqui sao DEFAULTS de producao. O consumidor sobrescreve por ambiente via\n// o parametro `urls` (do seu proprio env) -- o SDK NAO le process.env.\n\nexport type ProductMeta = {\n code: string;\n name: string;\n description: string;\n url: string;\n accent: string;\n ssoEnabled: boolean;\n};\n\nexport const PRODUCT_CATALOG: Record<string, ProductMeta> = {\n accounts: {\n code: \"accounts\",\n name: \"Accounts\",\n description: \"Identidade unificada do ecossistema\",\n url: \"/\",\n accent: \"#1F2E52\",\n ssoEnabled: true, // e o proprio IdP; excluido da gaveta de qualquer forma\n },\n \"media-hub\": {\n code: \"media-hub\",\n name: \"Media Hub\",\n description: \"Sinalizacao digital nas unidades\",\n url: \"\", // sem frontend publico deployado; override local via NEXT_PUBLIC_MEDIA_HUB_URL\n accent: \"#EF802D\",\n ssoEnabled: true,\n },\n sig: {\n code: \"sig\",\n name: \"SIG\",\n description: \"Housekeeping e governanca das unidades\",\n url: \"https://sig.app.vegait.com/\",\n accent: \"#B0C925\",\n ssoEnabled: true,\n },\n gdi: {\n code: \"gdi\",\n name: \"GDI\",\n description: \"Gestao de dispositivos e internet\",\n url: \"\", // ainda nao esta em prod; override de HML via NEXT_PUBLIC_GDI_URL\n // quando ligar em prod, sera https://gdi.app.vegait.com/\n accent: \"#7B8B7E\",\n ssoEnabled: false,\n },\n sim: {\n code: \"sim\",\n name: \"SIM\",\n description: \"Service Issue Management\",\n url: \"https://sim.app.vegait.com/\",\n accent: \"#5B7DB1\",\n ssoEnabled: false,\n },\n hxp: {\n code: \"hxp\",\n name: \"HXP\",\n description: \"Hotel Experience Platform\",\n url: \"\", // nao ligado no Accounts\n accent: \"#9B6A4F\",\n ssoEnabled: false,\n },\n feedback: {\n code: \"feedback\",\n name: \"Feedback\",\n description: \"Pesquisas e NPS de hospedes\",\n url: \"\", // nao ligado no Accounts\n accent: \"#C26A8C\",\n ssoEnabled: false,\n },\n behavior: {\n code: \"behavior\",\n name: \"Behavior\",\n description: \"Inteligencia comportamental\",\n url: \"https://behavior.app.vegait.com/\",\n accent: \"#4A7C7E\",\n ssoEnabled: false,\n },\n};\n\n// Aliases de code legado -> canonico. Resolve dados gravados ANTES de uma\n// renomeacao (ex.: governanca -> sig) sem duplicar entrada no catalogo.\nconst LEGACY_CODE_ALIASES: Record<string, string> = {\n governanca: \"sig\",\n};\n\n// resolveProduct: retorna o meta cru do produto (com override aplicado se houver).\n// NAO filtra url falsy — o chamador decide o que fazer com produto sem URL.\n// getCatalogWithAccess e getAccessibleProducts filtram apos o override (Opcao A).\n// `urls` sobrescreve a URL default pelo code CANONICO. Code desconhecido => fallback ssoEnabled=false.\nexport function resolveProduct(\n code: string,\n urls?: Record<string, string>,\n): ProductMeta {\n const canonical = LEGACY_CODE_ALIASES[code] ?? code;\n const base =\n PRODUCT_CATALOG[canonical] ??\n ({\n code,\n name: code,\n description: \"\",\n url: \"#\",\n accent: \"#1F2E52\",\n ssoEnabled: false,\n } satisfies ProductMeta);\n const override = urls?.[canonical];\n return override ? { ...base, url: override } : base;\n}\n\nexport type CatalogProduct = ProductMeta & { accessible: boolean };\n\n// Catalogo COMPLETO pro app launcher (menos o Accounts, que e o proprio IdP),\n// com flag de acesso: Time VEGA acessa tudo; cliente acessa os codes da claim\n// `products` (aliases legados resolvidos). ssoEnabled NAO esconde produto aqui\n// — e metadado de COMO o produto abre, nao de quem ve. Sem claims: tudo\n// inacessivel (fail-closed). `urls` sobrescreve URLs por ambiente.\nexport function getCatalogWithAccess(\n claims: AccountsClaims | null,\n opts?: { urls?: Record<string, string> },\n): CatalogProduct[] {\n const urls = opts?.urls;\n const isVega = claims != null && derivePersona(claims) === \"vega\";\n const accessibleCodes = new Set(\n parseJsonArrayClaim(claims?.products).map(\n (code) => LEGACY_CODE_ALIASES[code] ?? code,\n ),\n );\n return Object.values(PRODUCT_CATALOG)\n .filter((p) => p.code !== \"accounts\")\n .map((p) => ({\n ...(urls?.[p.code] ? { ...p, url: urls[p.code] } : p),\n accessible: isVega || accessibleCodes.has(p.code),\n }))\n .filter((p) => p.url); // Opcao A: produto sem URL publica some do waffle/card/drawer\n}\n\n// Produtos do app launcher (gaveta/drawer). Visibilidade = acesso (persona/claim)\n// AND url truthy (pos-override de env). ssoEnabled NAO filtra — e apenas metadado\n// de como o produto abre. `urls` sobrescreve URLs por ambiente (override aplicado\n// ANTES do filtro de url, para que env possa ressuscitar produto sem URL default).\nexport function getAccessibleProducts(\n claims: AccountsClaims | null,\n opts?: { urls?: Record<string, string> },\n): ProductMeta[] {\n if (!claims) return [];\n const urls = opts?.urls;\n if (derivePersona(claims) === \"vega\") {\n return Object.values(PRODUCT_CATALOG)\n .filter((p) => p.code !== \"accounts\")\n .map((p) => (urls?.[p.code] ? { ...p, url: urls[p.code] } : p))\n .filter((p) => p.url); // Opcao A: sem URL publica = nao lancavel\n }\n return parseJsonArrayClaim(claims.products)\n .map((code) => resolveProduct(code, urls))\n .filter((p) => p.url); // Opcao A: idem; ssoEnabled nao filtra\n}\n","import type { AccountsClaims } from \"./claims\";\nimport { derivePersona, type Persona } from \"./persona\";\nimport {\n getAccessibleProducts,\n getCatalogWithAccess,\n type CatalogProduct,\n type ProductMeta,\n} from \"./productCatalog\";\n\n// Modelo de dados da casca de identidade (SEM React). O host mapeia isto pros\n// props do <IdentityBar> do @vegait/lyra-ds-v2, adicionando icones/labels/\n// callbacks a partir de `capabilities`.\nexport type IdentityModel = {\n persona: Persona;\n isUnitOwner: boolean;\n user: { name: string; email: string };\n products: ProductMeta[];\n /** Catalogo completo (menos Accounts) com flag de acesso — pro waffle\n * mostrar tudo e desabilitar o que a persona nao acessa. `products`\n * permanece so-acessiveis por retrocompat (SIG consome hoje). */\n catalog: CatalogProduct[];\n currentProductCode?: string;\n capabilities: {\n manageUsers: boolean; // gestor/vega\n adminConsole: boolean; // vega\n switchContext: boolean; // gestor/vega\n manageNetwork: boolean; // gestor (rodape do launcher)\n manageProducts: boolean; // owner da unidade ativa\n appointAdmins: boolean; // owner delega admin por produto\n };\n};\n\nfunction deriveIsUnitOwner(claims: AccountsClaims | null): boolean {\n if (!claims?.unit_owner_ids || !claims.active_unit_id) return false;\n try {\n const owned = JSON.parse(claims.unit_owner_ids) as string[];\n return Array.isArray(owned) && owned.includes(claims.active_unit_id);\n } catch {\n return false;\n }\n}\n\nexport function buildIdentityModel(\n claims: AccountsClaims | null,\n cfg?: { currentProductCode?: string; urls?: Record<string, string> },\n): IdentityModel {\n const persona = derivePersona(claims);\n const isUnitOwner = deriveIsUnitOwner(claims);\n return {\n persona,\n isUnitOwner,\n user: { name: claims?.name ?? \"\", email: claims?.email ?? \"\" },\n products: getAccessibleProducts(claims, { urls: cfg?.urls }),\n catalog: getCatalogWithAccess(claims, { urls: cfg?.urls }),\n currentProductCode: cfg?.currentProductCode,\n capabilities: {\n manageUsers: persona === \"gestor\" || persona === \"vega\",\n adminConsole: persona === \"vega\",\n switchContext: persona === \"gestor\" || persona === \"vega\",\n manageNetwork: persona === \"gestor\",\n manageProducts: isUnitOwner,\n appointAdmins: isUnitOwner,\n },\n };\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.js
CHANGED
|
@@ -85,10 +85,7 @@ var PRODUCT_CATALOG = {
|
|
|
85
85
|
code: "sim",
|
|
86
86
|
name: "SIM",
|
|
87
87
|
description: "Service Issue Management",
|
|
88
|
-
url: "",
|
|
89
|
-
// nao pode aparecer em prod ainda (13/09); override de HML via NEXT_PUBLIC_SIM_URL,
|
|
90
|
-
// que o pipeline ja aponta pra hml.sim.app.vegait.com. URL de prod real:
|
|
91
|
-
// https://sim.app.vegait.com/ -- devolver ao default quando liberar.
|
|
88
|
+
url: "https://sim.app.vegait.com/",
|
|
92
89
|
accent: "#5B7DB1",
|
|
93
90
|
ssoEnabled: false
|
|
94
91
|
},
|
|
@@ -116,19 +113,7 @@ var PRODUCT_CATALOG = {
|
|
|
116
113
|
description: "Inteligencia comportamental",
|
|
117
114
|
url: "https://behavior.app.vegait.com/",
|
|
118
115
|
accent: "#4A7C7E",
|
|
119
|
-
ssoEnabled:
|
|
120
|
-
// publicado em prod; cliente OIDC `behavior-admin` seedado (.env.prod)
|
|
121
|
-
},
|
|
122
|
-
vegai: {
|
|
123
|
-
code: "vegai",
|
|
124
|
-
name: "VegAI",
|
|
125
|
-
description: "Atendimento com IA para hospedes",
|
|
126
|
-
url: "https://vegai.app.vegait.com/",
|
|
127
|
-
// prod subiu depois da !16890, que assumia
|
|
128
|
-
// que so existia HML; override de HML segue
|
|
129
|
-
// no NEXT_PUBLIC_VEGAI_URL do pipeline.
|
|
130
|
-
accent: "#6E56CF",
|
|
131
|
-
ssoEnabled: true
|
|
116
|
+
ssoEnabled: false
|
|
132
117
|
}
|
|
133
118
|
};
|
|
134
119
|
var LEGACY_CODE_ALIASES = {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/claims.ts","../src/persona.ts","../src/productCatalog.ts","../src/identityModel.ts"],"sourcesContent":["// Decoder de payload JWT sem verificacao criptografica. Universal (browser +\n// Node). Usar APENAS para extrair claims de tokens ja confiaveis (recebidos do\n// Accounts via fluxo OIDC). NUNCA para validar tokens de origem externa.\n\nexport function decodeJwtPayload<T = Record<string, unknown>>(token: string): T {\n const parts = token.split(\".\");\n if (parts.length !== 3) {\n throw new Error(\"Invalid JWT format\");\n }\n const payload = parts[1].replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded = payload + \"=\".repeat((4 - (payload.length % 4)) % 4);\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n const json = new TextDecoder(\"utf-8\").decode(bytes);\n return JSON.parse(json) as T;\n}\n\n// Claims do AT emitido pelo Accounts via OpenIddict. Tudo opcional (nem todo\n// campo chega em todo contexto). unit_ids/products vem como string contendo\n// JSON array. platform_role aparece so para Time VEGA. Espelha o token; fonte\n// da verdade = VegaClaimsHandler no accounts.api.\nexport type AccountsClaims = {\n sub?: string;\n email?: string;\n name?: string;\n platform_role?: string;\n can_manage_scope_members?: string;\n org_id?: string | string[];\n unit_ids?: string;\n unit_owner_ids?: string;\n active_unit_id?: string;\n products?: string;\n exp?: number;\n iat?: number;\n iss?: string;\n aud?: string | string[];\n};\n\n// Normaliza claim que pode vir como string ou array (OpenIddict emite array\n// quando ha multiplos valores, scalar quando ha um so).\nexport function normalizeClaimArray(\n value: string | string[] | undefined,\n): string[] {\n if (!value) return [];\n return Array.isArray(value) ? value : [value];\n}\n\n// Parseia claim cujo value e string contendo JSON array (unit_ids, products).\n// Lista vazia em claim ausente ou JSON malformado.\nexport function parseJsonArrayClaim(value: string | undefined): string[] {\n if (!value) return [];\n try {\n const parsed = JSON.parse(value);\n return Array.isArray(parsed)\n ? parsed.filter((v): v is string => typeof v === \"string\")\n : [];\n } catch {\n return [];\n }\n}\n\n// Wrapper safe que devolve null em vez de jogar em token invalido.\nexport function decodeAccessToken(\n token: string | null | undefined,\n): AccountsClaims | null {\n if (!token) return null;\n try {\n return decodeJwtPayload<AccountsClaims>(token);\n } catch {\n return null;\n }\n}\n","import type { AccountsClaims } from \"./claims\";\n\nexport type Persona = \"usuario\" | \"gestor\" | \"vega\";\n\n// Deriva a persona a partir das claims do AT. Fail-closed: sem claims ou sem\n// sinal de gestao, cai em 'usuario' (menor privilegio). platform_role (Time\n// VEGA) tem precedencia sobre a flag de gestor de cliente.\n// ATENCAO: presentation gating, NAO autorizacao (ver README).\nexport function derivePersona(claims: AccountsClaims | null): Persona {\n if (claims?.platform_role) return \"vega\";\n if (claims?.can_manage_scope_members === \"true\") return \"gestor\";\n return \"usuario\";\n}\n","import type { AccountsClaims } from \"./claims\";\nimport { parseJsonArrayClaim } from \"./claims\";\nimport { derivePersona } from \"./persona\";\n\n// Catalogo cliente-side dos produtos VEGA. Mapeia o code (claim `products` do\n// AT) pra display name + URL default + accent. codes batem com o seed do\n// backend (SeedMediaHubInProductCatalog etc.).\n//\n// ssoEnabled = METADADO de COMO o produto abre (SSO unificado via Accounts vs.\n// login proprio). NAO e gate de visibilidade desde o deploy waffle-completo\n// (17/08). Um produto com ssoEnabled=false aparece normalmente; ao clicar, o\n// usuario cai no login proprio do produto — comportamento aceito pelo produto/PO.\n// Visibilidade = acesso (persona/claim) AND url truthy (pos-override de env).\n//\n// URLs aqui sao DEFAULTS de producao. O consumidor sobrescreve por ambiente via\n// o parametro `urls` (do seu proprio env) -- o SDK NAO le process.env.\n\nexport type ProductMeta = {\n code: string;\n name: string;\n description: string;\n url: string;\n accent: string;\n ssoEnabled: boolean;\n};\n\nexport const PRODUCT_CATALOG: Record<string, ProductMeta> = {\n accounts: {\n code: \"accounts\",\n name: \"Accounts\",\n description: \"Identidade unificada do ecossistema\",\n url: \"/\",\n accent: \"#1F2E52\",\n ssoEnabled: true, // e o proprio IdP; excluido da gaveta de qualquer forma\n },\n \"media-hub\": {\n code: \"media-hub\",\n name: \"Media Hub\",\n description: \"Sinalizacao digital nas unidades\",\n url: \"\", // sem frontend publico deployado; override local via NEXT_PUBLIC_MEDIA_HUB_URL\n accent: \"#EF802D\",\n ssoEnabled: true,\n },\n sig: {\n code: \"sig\",\n name: \"SIG\",\n description: \"Housekeeping e governanca das unidades\",\n url: \"https://sig.app.vegait.com/\",\n accent: \"#B0C925\",\n ssoEnabled: true,\n },\n gdi: {\n code: \"gdi\",\n name: \"GDI\",\n description: \"Gestao de dispositivos e internet\",\n url: \"\", // ainda nao esta em prod; override de HML via NEXT_PUBLIC_GDI_URL\n // quando ligar em prod, sera https://gdi.app.vegait.com/\n accent: \"#7B8B7E\",\n ssoEnabled: false,\n },\n sim: {\n code: \"sim\",\n name: \"SIM\",\n description: \"Service Issue Management\",\n url: \"\", // nao pode aparecer em prod ainda (13/09); override de HML via NEXT_PUBLIC_SIM_URL,\n // que o pipeline ja aponta pra hml.sim.app.vegait.com. URL de prod real:\n // https://sim.app.vegait.com/ -- devolver ao default quando liberar.\n accent: \"#5B7DB1\",\n ssoEnabled: false,\n },\n hxp: {\n code: \"hxp\",\n name: \"HXP\",\n description: \"Hotel Experience Platform\",\n url: \"\", // nao ligado no Accounts\n accent: \"#9B6A4F\",\n ssoEnabled: false,\n },\n feedback: {\n code: \"feedback\",\n name: \"Feedback\",\n description: \"Pesquisas e NPS de hospedes\",\n url: \"\", // nao ligado no Accounts\n accent: \"#C26A8C\",\n ssoEnabled: false,\n },\n behavior: {\n code: \"behavior\",\n name: \"Behavior\",\n description: \"Inteligencia comportamental\",\n url: \"https://behavior.app.vegait.com/\",\n accent: \"#4A7C7E\",\n ssoEnabled: true, // publicado em prod; cliente OIDC `behavior-admin` seedado (.env.prod)\n },\n vegai: {\n code: \"vegai\",\n name: \"VegAI\",\n description: \"Atendimento com IA para hospedes\",\n url: \"https://vegai.app.vegait.com/\", // prod subiu depois da !16890, que assumia\n // que so existia HML; override de HML segue\n // no NEXT_PUBLIC_VEGAI_URL do pipeline.\n accent: \"#6E56CF\",\n ssoEnabled: true,\n },\n};\n\n// Aliases de code legado -> canonico. Resolve dados gravados ANTES de uma\n// renomeacao (ex.: governanca -> sig) sem duplicar entrada no catalogo.\nconst LEGACY_CODE_ALIASES: Record<string, string> = {\n governanca: \"sig\",\n};\n\n// resolveProduct: retorna o meta cru do produto (com override aplicado se houver).\n// NAO filtra url falsy — o chamador decide o que fazer com produto sem URL.\n// getCatalogWithAccess e getAccessibleProducts filtram apos o override (Opcao A).\n// `urls` sobrescreve a URL default pelo code CANONICO. Code desconhecido => fallback ssoEnabled=false.\nexport function resolveProduct(\n code: string,\n urls?: Record<string, string>,\n): ProductMeta {\n const canonical = LEGACY_CODE_ALIASES[code] ?? code;\n const base =\n PRODUCT_CATALOG[canonical] ??\n ({\n code,\n name: code,\n description: \"\",\n url: \"#\",\n accent: \"#1F2E52\",\n ssoEnabled: false,\n } satisfies ProductMeta);\n const override = urls?.[canonical];\n return override ? { ...base, url: override } : base;\n}\n\nexport type CatalogProduct = ProductMeta & { accessible: boolean };\n\n// Catalogo COMPLETO pro app launcher (menos o Accounts, que e o proprio IdP),\n// com flag de acesso: Time VEGA acessa tudo; cliente acessa os codes da claim\n// `products` (aliases legados resolvidos). ssoEnabled NAO esconde produto aqui\n// — e metadado de COMO o produto abre, nao de quem ve. Sem claims: tudo\n// inacessivel (fail-closed). `urls` sobrescreve URLs por ambiente.\nexport function getCatalogWithAccess(\n claims: AccountsClaims | null,\n opts?: { urls?: Record<string, string> },\n): CatalogProduct[] {\n const urls = opts?.urls;\n const isVega = claims != null && derivePersona(claims) === \"vega\";\n const accessibleCodes = new Set(\n parseJsonArrayClaim(claims?.products).map(\n (code) => LEGACY_CODE_ALIASES[code] ?? code,\n ),\n );\n return Object.values(PRODUCT_CATALOG)\n .filter((p) => p.code !== \"accounts\")\n .map((p) => ({\n ...(urls?.[p.code] ? { ...p, url: urls[p.code] } : p),\n accessible: isVega || accessibleCodes.has(p.code),\n }))\n .filter((p) => p.url); // Opcao A: produto sem URL publica some do waffle/card/drawer\n}\n\n// Produtos do app launcher (gaveta/drawer). Visibilidade = acesso (persona/claim)\n// AND url truthy (pos-override de env). ssoEnabled NAO filtra — e apenas metadado\n// de como o produto abre. `urls` sobrescreve URLs por ambiente (override aplicado\n// ANTES do filtro de url, para que env possa ressuscitar produto sem URL default).\nexport function getAccessibleProducts(\n claims: AccountsClaims | null,\n opts?: { urls?: Record<string, string> },\n): ProductMeta[] {\n if (!claims) return [];\n const urls = opts?.urls;\n if (derivePersona(claims) === \"vega\") {\n return Object.values(PRODUCT_CATALOG)\n .filter((p) => p.code !== \"accounts\")\n .map((p) => (urls?.[p.code] ? { ...p, url: urls[p.code] } : p))\n .filter((p) => p.url); // Opcao A: sem URL publica = nao lancavel\n }\n return parseJsonArrayClaim(claims.products)\n .map((code) => resolveProduct(code, urls))\n .filter((p) => p.url); // Opcao A: idem; ssoEnabled nao filtra\n}\n","import type { AccountsClaims } from \"./claims\";\nimport { derivePersona, type Persona } from \"./persona\";\nimport {\n getAccessibleProducts,\n getCatalogWithAccess,\n type CatalogProduct,\n type ProductMeta,\n} from \"./productCatalog\";\n\n// Modelo de dados da casca de identidade (SEM React). O host mapeia isto pros\n// props do <IdentityBar> do @vegait/lyra-ds-v2, adicionando icones/labels/\n// callbacks a partir de `capabilities`.\nexport type IdentityModel = {\n persona: Persona;\n isUnitOwner: boolean;\n user: { name: string; email: string };\n products: ProductMeta[];\n /** Catalogo completo (menos Accounts) com flag de acesso — pro waffle\n * mostrar tudo e desabilitar o que a persona nao acessa. `products`\n * permanece so-acessiveis por retrocompat (SIG consome hoje). */\n catalog: CatalogProduct[];\n currentProductCode?: string;\n capabilities: {\n manageUsers: boolean; // gestor/vega\n adminConsole: boolean; // vega\n switchContext: boolean; // gestor/vega\n manageNetwork: boolean; // gestor (rodape do launcher)\n manageProducts: boolean; // owner da unidade ativa\n appointAdmins: boolean; // owner delega admin por produto\n };\n};\n\nfunction deriveIsUnitOwner(claims: AccountsClaims | null): boolean {\n if (!claims?.unit_owner_ids || !claims.active_unit_id) return false;\n try {\n const owned = JSON.parse(claims.unit_owner_ids) as string[];\n return Array.isArray(owned) && owned.includes(claims.active_unit_id);\n } catch {\n return false;\n }\n}\n\nexport function buildIdentityModel(\n claims: AccountsClaims | null,\n cfg?: { currentProductCode?: string; urls?: Record<string, string> },\n): IdentityModel {\n const persona = derivePersona(claims);\n const isUnitOwner = deriveIsUnitOwner(claims);\n return {\n persona,\n isUnitOwner,\n user: { name: claims?.name ?? \"\", email: claims?.email ?? \"\" },\n products: getAccessibleProducts(claims, { urls: cfg?.urls }),\n catalog: getCatalogWithAccess(claims, { urls: cfg?.urls }),\n currentProductCode: cfg?.currentProductCode,\n capabilities: {\n manageUsers: persona === \"gestor\" || persona === \"vega\",\n adminConsole: persona === \"vega\",\n switchContext: persona === \"gestor\" || persona === \"vega\",\n manageNetwork: persona === \"gestor\",\n manageProducts: isUnitOwner,\n appointAdmins: isUnitOwner,\n },\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/claims.ts","../src/persona.ts","../src/productCatalog.ts","../src/identityModel.ts"],"sourcesContent":["// Decoder de payload JWT sem verificacao criptografica. Universal (browser +\n// Node). Usar APENAS para extrair claims de tokens ja confiaveis (recebidos do\n// Accounts via fluxo OIDC). NUNCA para validar tokens de origem externa.\n\nexport function decodeJwtPayload<T = Record<string, unknown>>(token: string): T {\n const parts = token.split(\".\");\n if (parts.length !== 3) {\n throw new Error(\"Invalid JWT format\");\n }\n const payload = parts[1].replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded = payload + \"=\".repeat((4 - (payload.length % 4)) % 4);\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n const json = new TextDecoder(\"utf-8\").decode(bytes);\n return JSON.parse(json) as T;\n}\n\n// Claims do AT emitido pelo Accounts via OpenIddict. Tudo opcional (nem todo\n// campo chega em todo contexto). unit_ids/products vem como string contendo\n// JSON array. platform_role aparece so para Time VEGA. Espelha o token; fonte\n// da verdade = VegaClaimsHandler no accounts.api.\nexport type AccountsClaims = {\n sub?: string;\n email?: string;\n name?: string;\n platform_role?: string;\n can_manage_scope_members?: string;\n org_id?: string | string[];\n unit_ids?: string;\n unit_owner_ids?: string;\n active_unit_id?: string;\n products?: string;\n exp?: number;\n iat?: number;\n iss?: string;\n aud?: string | string[];\n};\n\n// Normaliza claim que pode vir como string ou array (OpenIddict emite array\n// quando ha multiplos valores, scalar quando ha um so).\nexport function normalizeClaimArray(\n value: string | string[] | undefined,\n): string[] {\n if (!value) return [];\n return Array.isArray(value) ? value : [value];\n}\n\n// Parseia claim cujo value e string contendo JSON array (unit_ids, products).\n// Lista vazia em claim ausente ou JSON malformado.\nexport function parseJsonArrayClaim(value: string | undefined): string[] {\n if (!value) return [];\n try {\n const parsed = JSON.parse(value);\n return Array.isArray(parsed)\n ? parsed.filter((v): v is string => typeof v === \"string\")\n : [];\n } catch {\n return [];\n }\n}\n\n// Wrapper safe que devolve null em vez de jogar em token invalido.\nexport function decodeAccessToken(\n token: string | null | undefined,\n): AccountsClaims | null {\n if (!token) return null;\n try {\n return decodeJwtPayload<AccountsClaims>(token);\n } catch {\n return null;\n }\n}\n","import type { AccountsClaims } from \"./claims\";\n\nexport type Persona = \"usuario\" | \"gestor\" | \"vega\";\n\n// Deriva a persona a partir das claims do AT. Fail-closed: sem claims ou sem\n// sinal de gestao, cai em 'usuario' (menor privilegio). platform_role (Time\n// VEGA) tem precedencia sobre a flag de gestor de cliente.\n// ATENCAO: presentation gating, NAO autorizacao (ver README).\nexport function derivePersona(claims: AccountsClaims | null): Persona {\n if (claims?.platform_role) return \"vega\";\n if (claims?.can_manage_scope_members === \"true\") return \"gestor\";\n return \"usuario\";\n}\n","import type { AccountsClaims } from \"./claims\";\nimport { parseJsonArrayClaim } from \"./claims\";\nimport { derivePersona } from \"./persona\";\n\n// Catalogo cliente-side dos produtos VEGA. Mapeia o code (claim `products` do\n// AT) pra display name + URL default + accent. codes batem com o seed do\n// backend (SeedMediaHubInProductCatalog etc.).\n//\n// ssoEnabled = METADADO de COMO o produto abre (SSO unificado via Accounts vs.\n// login proprio). NAO e gate de visibilidade desde o deploy waffle-completo\n// (17/08). Um produto com ssoEnabled=false aparece normalmente; ao clicar, o\n// usuario cai no login proprio do produto — comportamento aceito pelo produto/PO.\n// Visibilidade = acesso (persona/claim) AND url truthy (pos-override de env).\n//\n// URLs aqui sao DEFAULTS de producao. O consumidor sobrescreve por ambiente via\n// o parametro `urls` (do seu proprio env) -- o SDK NAO le process.env.\n\nexport type ProductMeta = {\n code: string;\n name: string;\n description: string;\n url: string;\n accent: string;\n ssoEnabled: boolean;\n};\n\nexport const PRODUCT_CATALOG: Record<string, ProductMeta> = {\n accounts: {\n code: \"accounts\",\n name: \"Accounts\",\n description: \"Identidade unificada do ecossistema\",\n url: \"/\",\n accent: \"#1F2E52\",\n ssoEnabled: true, // e o proprio IdP; excluido da gaveta de qualquer forma\n },\n \"media-hub\": {\n code: \"media-hub\",\n name: \"Media Hub\",\n description: \"Sinalizacao digital nas unidades\",\n url: \"\", // sem frontend publico deployado; override local via NEXT_PUBLIC_MEDIA_HUB_URL\n accent: \"#EF802D\",\n ssoEnabled: true,\n },\n sig: {\n code: \"sig\",\n name: \"SIG\",\n description: \"Housekeeping e governanca das unidades\",\n url: \"https://sig.app.vegait.com/\",\n accent: \"#B0C925\",\n ssoEnabled: true,\n },\n gdi: {\n code: \"gdi\",\n name: \"GDI\",\n description: \"Gestao de dispositivos e internet\",\n url: \"\", // ainda nao esta em prod; override de HML via NEXT_PUBLIC_GDI_URL\n // quando ligar em prod, sera https://gdi.app.vegait.com/\n accent: \"#7B8B7E\",\n ssoEnabled: false,\n },\n sim: {\n code: \"sim\",\n name: \"SIM\",\n description: \"Service Issue Management\",\n url: \"https://sim.app.vegait.com/\",\n accent: \"#5B7DB1\",\n ssoEnabled: false,\n },\n hxp: {\n code: \"hxp\",\n name: \"HXP\",\n description: \"Hotel Experience Platform\",\n url: \"\", // nao ligado no Accounts\n accent: \"#9B6A4F\",\n ssoEnabled: false,\n },\n feedback: {\n code: \"feedback\",\n name: \"Feedback\",\n description: \"Pesquisas e NPS de hospedes\",\n url: \"\", // nao ligado no Accounts\n accent: \"#C26A8C\",\n ssoEnabled: false,\n },\n behavior: {\n code: \"behavior\",\n name: \"Behavior\",\n description: \"Inteligencia comportamental\",\n url: \"https://behavior.app.vegait.com/\",\n accent: \"#4A7C7E\",\n ssoEnabled: false,\n },\n};\n\n// Aliases de code legado -> canonico. Resolve dados gravados ANTES de uma\n// renomeacao (ex.: governanca -> sig) sem duplicar entrada no catalogo.\nconst LEGACY_CODE_ALIASES: Record<string, string> = {\n governanca: \"sig\",\n};\n\n// resolveProduct: retorna o meta cru do produto (com override aplicado se houver).\n// NAO filtra url falsy — o chamador decide o que fazer com produto sem URL.\n// getCatalogWithAccess e getAccessibleProducts filtram apos o override (Opcao A).\n// `urls` sobrescreve a URL default pelo code CANONICO. Code desconhecido => fallback ssoEnabled=false.\nexport function resolveProduct(\n code: string,\n urls?: Record<string, string>,\n): ProductMeta {\n const canonical = LEGACY_CODE_ALIASES[code] ?? code;\n const base =\n PRODUCT_CATALOG[canonical] ??\n ({\n code,\n name: code,\n description: \"\",\n url: \"#\",\n accent: \"#1F2E52\",\n ssoEnabled: false,\n } satisfies ProductMeta);\n const override = urls?.[canonical];\n return override ? { ...base, url: override } : base;\n}\n\nexport type CatalogProduct = ProductMeta & { accessible: boolean };\n\n// Catalogo COMPLETO pro app launcher (menos o Accounts, que e o proprio IdP),\n// com flag de acesso: Time VEGA acessa tudo; cliente acessa os codes da claim\n// `products` (aliases legados resolvidos). ssoEnabled NAO esconde produto aqui\n// — e metadado de COMO o produto abre, nao de quem ve. Sem claims: tudo\n// inacessivel (fail-closed). `urls` sobrescreve URLs por ambiente.\nexport function getCatalogWithAccess(\n claims: AccountsClaims | null,\n opts?: { urls?: Record<string, string> },\n): CatalogProduct[] {\n const urls = opts?.urls;\n const isVega = claims != null && derivePersona(claims) === \"vega\";\n const accessibleCodes = new Set(\n parseJsonArrayClaim(claims?.products).map(\n (code) => LEGACY_CODE_ALIASES[code] ?? code,\n ),\n );\n return Object.values(PRODUCT_CATALOG)\n .filter((p) => p.code !== \"accounts\")\n .map((p) => ({\n ...(urls?.[p.code] ? { ...p, url: urls[p.code] } : p),\n accessible: isVega || accessibleCodes.has(p.code),\n }))\n .filter((p) => p.url); // Opcao A: produto sem URL publica some do waffle/card/drawer\n}\n\n// Produtos do app launcher (gaveta/drawer). Visibilidade = acesso (persona/claim)\n// AND url truthy (pos-override de env). ssoEnabled NAO filtra — e apenas metadado\n// de como o produto abre. `urls` sobrescreve URLs por ambiente (override aplicado\n// ANTES do filtro de url, para que env possa ressuscitar produto sem URL default).\nexport function getAccessibleProducts(\n claims: AccountsClaims | null,\n opts?: { urls?: Record<string, string> },\n): ProductMeta[] {\n if (!claims) return [];\n const urls = opts?.urls;\n if (derivePersona(claims) === \"vega\") {\n return Object.values(PRODUCT_CATALOG)\n .filter((p) => p.code !== \"accounts\")\n .map((p) => (urls?.[p.code] ? { ...p, url: urls[p.code] } : p))\n .filter((p) => p.url); // Opcao A: sem URL publica = nao lancavel\n }\n return parseJsonArrayClaim(claims.products)\n .map((code) => resolveProduct(code, urls))\n .filter((p) => p.url); // Opcao A: idem; ssoEnabled nao filtra\n}\n","import type { AccountsClaims } from \"./claims\";\nimport { derivePersona, type Persona } from \"./persona\";\nimport {\n getAccessibleProducts,\n getCatalogWithAccess,\n type CatalogProduct,\n type ProductMeta,\n} from \"./productCatalog\";\n\n// Modelo de dados da casca de identidade (SEM React). O host mapeia isto pros\n// props do <IdentityBar> do @vegait/lyra-ds-v2, adicionando icones/labels/\n// callbacks a partir de `capabilities`.\nexport type IdentityModel = {\n persona: Persona;\n isUnitOwner: boolean;\n user: { name: string; email: string };\n products: ProductMeta[];\n /** Catalogo completo (menos Accounts) com flag de acesso — pro waffle\n * mostrar tudo e desabilitar o que a persona nao acessa. `products`\n * permanece so-acessiveis por retrocompat (SIG consome hoje). */\n catalog: CatalogProduct[];\n currentProductCode?: string;\n capabilities: {\n manageUsers: boolean; // gestor/vega\n adminConsole: boolean; // vega\n switchContext: boolean; // gestor/vega\n manageNetwork: boolean; // gestor (rodape do launcher)\n manageProducts: boolean; // owner da unidade ativa\n appointAdmins: boolean; // owner delega admin por produto\n };\n};\n\nfunction deriveIsUnitOwner(claims: AccountsClaims | null): boolean {\n if (!claims?.unit_owner_ids || !claims.active_unit_id) return false;\n try {\n const owned = JSON.parse(claims.unit_owner_ids) as string[];\n return Array.isArray(owned) && owned.includes(claims.active_unit_id);\n } catch {\n return false;\n }\n}\n\nexport function buildIdentityModel(\n claims: AccountsClaims | null,\n cfg?: { currentProductCode?: string; urls?: Record<string, string> },\n): IdentityModel {\n const persona = derivePersona(claims);\n const isUnitOwner = deriveIsUnitOwner(claims);\n return {\n persona,\n isUnitOwner,\n user: { name: claims?.name ?? \"\", email: claims?.email ?? \"\" },\n products: getAccessibleProducts(claims, { urls: cfg?.urls }),\n catalog: getCatalogWithAccess(claims, { urls: cfg?.urls }),\n currentProductCode: cfg?.currentProductCode,\n capabilities: {\n manageUsers: persona === \"gestor\" || persona === \"vega\",\n adminConsole: persona === \"vega\",\n switchContext: persona === \"gestor\" || persona === \"vega\",\n manageNetwork: persona === \"gestor\",\n manageProducts: isUnitOwner,\n appointAdmins: isUnitOwner,\n },\n };\n}\n"],"mappings":"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","names":[]}
|