@vegait/accounts-sdk 0.1.20 → 0.1.21-beta.54136

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 CHANGED
@@ -153,10 +153,15 @@ var PRODUCT_CATALOG = {
153
153
  code: "feedback",
154
154
  name: "Feedback",
155
155
  description: "Pesquisas e NPS de hospedes",
156
+ // Login pelo Accounts entrou em homolog em 10/11 (client feedback-frontend).
157
+ // url fica vazia de proposito, mesmo criterio do gdi/easycheckin2: o SDK e
158
+ // publicado no npm e consumido por outros produtos, entao quem liga e cada
159
+ // consumidor por override de ambiente (NEXT_PUBLIC_FEEDBACK_URL), nunca o
160
+ // default daqui. Quando subir em prod, o pipeline ganha o case de `main` (mesmo
161
+ // caminho do gdi); o default aqui segue vazio pra sempre.
156
162
  url: "",
157
- // nao ligado no Accounts
158
163
  accent: "#C26A8C",
159
- ssoEnabled: false
164
+ ssoEnabled: true
160
165
  },
161
166
  behavior: {
162
167
  code: "behavior",
@@ -177,6 +182,41 @@ var PRODUCT_CATALOG = {
177
182
  // no NEXT_PUBLIC_VEGAI_URL do pipeline.
178
183
  accent: "#6E56CF",
179
184
  ssoEnabled: true
185
+ },
186
+ easycheckin: {
187
+ code: "easycheckin",
188
+ name: "Easy Check-in",
189
+ description: "Check-in online servido pelo HXP (plataforma atual)",
190
+ url: "",
191
+ // sem painel proprio pra abrir daqui: e superficie do HXP, nao produto
192
+ // com frontend. A entrada existe justamente pra que o code NAO caia no
193
+ // fallback de code desconhecido (url "#", que e truthy e sobrevive ao
194
+ // filtro) e renderize um card morto rotulado "easycheckin". Mesmo papel
195
+ // das entradas de `hxp` e `feedback`.
196
+ //
197
+ // Contratar na unidade segue sendo o caminho normal (e o que alimenta o
198
+ // roteamento do Integration v2). Atribuir a USUARIO continua sendo coisa
199
+ // que nao se faz -- mas nada na api impede: o AssignUserProductCommandValidator
200
+ // so checa IsActiveAsync, e desativar o produto quebraria o contrato na
201
+ // unidade junto. Esta entrada nao cria a trava; so torna o engano invisivel.
202
+ accent: "#8A6F5C",
203
+ ssoEnabled: false
204
+ // nao tem client OIDC no Accounts; login e do proprio HXP
205
+ },
206
+ easycheckin2: {
207
+ code: "easycheckin2",
208
+ name: "Easy Check-in (novo)",
209
+ description: "Check-in online e web check-in do hospede (plataforma nova)",
210
+ url: "",
211
+ // so existe em homolog (hml.easycheckin.app.vegait.com); a URL por
212
+ // ambiente vem de NEXT_PUBLIC_EASYCHECKIN2_URL (.ci/pipeline.yml).
213
+ // Quando o Easy novo subir em prod, o default vira
214
+ // https://easycheckin.app.vegait.com/ -- e as RedirectUris de prod do
215
+ // client ja estao no accounts.release esperando por isso.
216
+ accent: "#1C8FA8",
217
+ ssoEnabled: true
218
+ // client OIDC `easycheckin2-admin` seedado pela api
219
+ // (EasyCheckin2AdminClientSeeder), Code + PKCE.
180
220
  }
181
221
  };
182
222
  var LEGACY_CODE_ALIASES = {
@@ -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: \"Gerenciamento Dinamico de Internet\",\n url: \"\", // vazio de proposito: a URL por ambiente vem de NEXT_PUBLIC_GDI_URL (.ci/pipeline.yml)\n accent: \"#7B8B7E\",\n ssoEnabled: true,\n },\n sim: {\n code: \"sim\",\n name: \"SIM\",\n description: \"Service Issue Management\",\n url: \"https://sim.app.vegait.com/\", // devolvido ao default em 03/11 (pedido do Leandro).\n // Ficou vazio de 13/09 ate aqui; o override de HML\n // (NEXT_PUBLIC_SIM_URL -> hml.sim.app.vegait.com)\n // segue no pipeline e continua valendo em homolog.\n accent: \"#5B7DB1\",\n ssoEnabled: true, // sim-unit/sim-mobile/sim-pwa sao clients OpenIddict seedados pela\n // api (SimUnitClientSeeder/SimMobileClientSeeder/SimPwaClientSeeder).\n // As RedirectUris de prod entraram no accounts.release em 03/11\n // (!18616, 9bc919c). O seeder so roda no BOOT, entao o login\n // unificado so passa a existir depois de um deploy da api de prod;\n // antes disso o que esta gravado e o default localhost do\n // appsettings.json (o [] do appsettings.Production.json NAO zera o\n // array -- array vazio nao contribui chave, a :0 de baixo sobrevive).\n // Este campo e metadado inerte, nada aqui quebra. Mas antes de\n // prometer SSO, confira o estado real em vez de confiar nesta linha:\n // SELECT client_id, redirect_uris FROM \"OpenIddictApplications\"\n // WHERE client_id LIKE 'sim-%';\n // Se vier localhost, o deploy da api ainda nao rodou.\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: \"Gerenciamento Dinamico de Internet\",\n url: \"\", // vazio de proposito: a URL por ambiente vem de NEXT_PUBLIC_GDI_URL (.ci/pipeline.yml)\n accent: \"#7B8B7E\",\n ssoEnabled: true,\n },\n sim: {\n code: \"sim\",\n name: \"SIM\",\n description: \"Service Issue Management\",\n url: \"https://sim.app.vegait.com/\", // devolvido ao default em 03/11 (pedido do Leandro).\n // Ficou vazio de 13/09 ate aqui; o override de HML\n // (NEXT_PUBLIC_SIM_URL -> hml.sim.app.vegait.com)\n // segue no pipeline e continua valendo em homolog.\n accent: \"#5B7DB1\",\n ssoEnabled: true, // sim-unit/sim-mobile/sim-pwa sao clients OpenIddict seedados pela\n // api (SimUnitClientSeeder/SimMobileClientSeeder/SimPwaClientSeeder).\n // As RedirectUris de prod entraram no accounts.release em 03/11\n // (!18616, 9bc919c). O seeder so roda no BOOT, entao o login\n // unificado so passa a existir depois de um deploy da api de prod;\n // antes disso o que esta gravado e o default localhost do\n // appsettings.json (o [] do appsettings.Production.json NAO zera o\n // array -- array vazio nao contribui chave, a :0 de baixo sobrevive).\n // Este campo e metadado inerte, nada aqui quebra. Mas antes de\n // prometer SSO, confira o estado real em vez de confiar nesta linha:\n // SELECT client_id, redirect_uris FROM \"OpenIddictApplications\"\n // WHERE client_id LIKE 'sim-%';\n // Se vier localhost, o deploy da api ainda nao rodou.\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 // Login pelo Accounts entrou em homolog em 10/11 (client feedback-frontend).\n // url fica vazia de proposito, mesmo criterio do gdi/easycheckin2: o SDK e\n // publicado no npm e consumido por outros produtos, entao quem liga e cada\n // consumidor por override de ambiente (NEXT_PUBLIC_FEEDBACK_URL), nunca o\n // default daqui. Quando subir em prod, o pipeline ganha o case de `main` (mesmo\n // caminho do gdi); o default aqui segue vazio pra sempre.\n url: \"\",\n accent: \"#C26A8C\",\n ssoEnabled: true,\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 easycheckin: {\n code: \"easycheckin\",\n name: \"Easy Check-in\",\n description: \"Check-in online servido pelo HXP (plataforma atual)\",\n url: \"\", // sem painel proprio pra abrir daqui: e superficie do HXP, nao produto\n // com frontend. A entrada existe justamente pra que o code NAO caia no\n // fallback de code desconhecido (url \"#\", que e truthy e sobrevive ao\n // filtro) e renderize um card morto rotulado \"easycheckin\". Mesmo papel\n // das entradas de `hxp` e `feedback`.\n //\n // Contratar na unidade segue sendo o caminho normal (e o que alimenta o\n // roteamento do Integration v2). Atribuir a USUARIO continua sendo coisa\n // que nao se faz -- mas nada na api impede: o AssignUserProductCommandValidator\n // so checa IsActiveAsync, e desativar o produto quebraria o contrato na\n // unidade junto. Esta entrada nao cria a trava; so torna o engano invisivel.\n accent: \"#8A6F5C\",\n ssoEnabled: false, // nao tem client OIDC no Accounts; login e do proprio HXP\n },\n easycheckin2: {\n code: \"easycheckin2\",\n name: \"Easy Check-in (novo)\",\n description: \"Check-in online e web check-in do hospede (plataforma nova)\",\n url: \"\", // so existe em homolog (hml.easycheckin.app.vegait.com); a URL por\n // ambiente vem de NEXT_PUBLIC_EASYCHECKIN2_URL (.ci/pipeline.yml).\n // Quando o Easy novo subir em prod, o default vira\n // https://easycheckin.app.vegait.com/ -- e as RedirectUris de prod do\n // client ja estao no accounts.release esperando por isso.\n accent: \"#1C8FA8\",\n ssoEnabled: true, // client OIDC `easycheckin2-admin` seedado pela api\n // (EasyCheckin2AdminClientSeeder), Code + PKCE.\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
@@ -118,10 +118,15 @@ var PRODUCT_CATALOG = {
118
118
  code: "feedback",
119
119
  name: "Feedback",
120
120
  description: "Pesquisas e NPS de hospedes",
121
+ // Login pelo Accounts entrou em homolog em 10/11 (client feedback-frontend).
122
+ // url fica vazia de proposito, mesmo criterio do gdi/easycheckin2: o SDK e
123
+ // publicado no npm e consumido por outros produtos, entao quem liga e cada
124
+ // consumidor por override de ambiente (NEXT_PUBLIC_FEEDBACK_URL), nunca o
125
+ // default daqui. Quando subir em prod, o pipeline ganha o case de `main` (mesmo
126
+ // caminho do gdi); o default aqui segue vazio pra sempre.
121
127
  url: "",
122
- // nao ligado no Accounts
123
128
  accent: "#C26A8C",
124
- ssoEnabled: false
129
+ ssoEnabled: true
125
130
  },
126
131
  behavior: {
127
132
  code: "behavior",
@@ -142,6 +147,41 @@ var PRODUCT_CATALOG = {
142
147
  // no NEXT_PUBLIC_VEGAI_URL do pipeline.
143
148
  accent: "#6E56CF",
144
149
  ssoEnabled: true
150
+ },
151
+ easycheckin: {
152
+ code: "easycheckin",
153
+ name: "Easy Check-in",
154
+ description: "Check-in online servido pelo HXP (plataforma atual)",
155
+ url: "",
156
+ // sem painel proprio pra abrir daqui: e superficie do HXP, nao produto
157
+ // com frontend. A entrada existe justamente pra que o code NAO caia no
158
+ // fallback de code desconhecido (url "#", que e truthy e sobrevive ao
159
+ // filtro) e renderize um card morto rotulado "easycheckin". Mesmo papel
160
+ // das entradas de `hxp` e `feedback`.
161
+ //
162
+ // Contratar na unidade segue sendo o caminho normal (e o que alimenta o
163
+ // roteamento do Integration v2). Atribuir a USUARIO continua sendo coisa
164
+ // que nao se faz -- mas nada na api impede: o AssignUserProductCommandValidator
165
+ // so checa IsActiveAsync, e desativar o produto quebraria o contrato na
166
+ // unidade junto. Esta entrada nao cria a trava; so torna o engano invisivel.
167
+ accent: "#8A6F5C",
168
+ ssoEnabled: false
169
+ // nao tem client OIDC no Accounts; login e do proprio HXP
170
+ },
171
+ easycheckin2: {
172
+ code: "easycheckin2",
173
+ name: "Easy Check-in (novo)",
174
+ description: "Check-in online e web check-in do hospede (plataforma nova)",
175
+ url: "",
176
+ // so existe em homolog (hml.easycheckin.app.vegait.com); a URL por
177
+ // ambiente vem de NEXT_PUBLIC_EASYCHECKIN2_URL (.ci/pipeline.yml).
178
+ // Quando o Easy novo subir em prod, o default vira
179
+ // https://easycheckin.app.vegait.com/ -- e as RedirectUris de prod do
180
+ // client ja estao no accounts.release esperando por isso.
181
+ accent: "#1C8FA8",
182
+ ssoEnabled: true
183
+ // client OIDC `easycheckin2-admin` seedado pela api
184
+ // (EasyCheckin2AdminClientSeeder), Code + PKCE.
145
185
  }
146
186
  };
147
187
  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: \"Gerenciamento Dinamico de Internet\",\n url: \"\", // vazio de proposito: a URL por ambiente vem de NEXT_PUBLIC_GDI_URL (.ci/pipeline.yml)\n accent: \"#7B8B7E\",\n ssoEnabled: true,\n },\n sim: {\n code: \"sim\",\n name: \"SIM\",\n description: \"Service Issue Management\",\n url: \"https://sim.app.vegait.com/\", // devolvido ao default em 03/11 (pedido do Leandro).\n // Ficou vazio de 13/09 ate aqui; o override de HML\n // (NEXT_PUBLIC_SIM_URL -> hml.sim.app.vegait.com)\n // segue no pipeline e continua valendo em homolog.\n accent: \"#5B7DB1\",\n ssoEnabled: true, // sim-unit/sim-mobile/sim-pwa sao clients OpenIddict seedados pela\n // api (SimUnitClientSeeder/SimMobileClientSeeder/SimPwaClientSeeder).\n // As RedirectUris de prod entraram no accounts.release em 03/11\n // (!18616, 9bc919c). O seeder so roda no BOOT, entao o login\n // unificado so passa a existir depois de um deploy da api de prod;\n // antes disso o que esta gravado e o default localhost do\n // appsettings.json (o [] do appsettings.Production.json NAO zera o\n // array -- array vazio nao contribui chave, a :0 de baixo sobrevive).\n // Este campo e metadado inerte, nada aqui quebra. Mas antes de\n // prometer SSO, confira o estado real em vez de confiar nesta linha:\n // SELECT client_id, redirect_uris FROM \"OpenIddictApplications\"\n // WHERE client_id LIKE 'sim-%';\n // Se vier localhost, o deploy da api ainda nao rodou.\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: \"Gerenciamento Dinamico de Internet\",\n url: \"\", // vazio de proposito: a URL por ambiente vem de NEXT_PUBLIC_GDI_URL (.ci/pipeline.yml)\n accent: \"#7B8B7E\",\n ssoEnabled: true,\n },\n sim: {\n code: \"sim\",\n name: \"SIM\",\n description: \"Service Issue Management\",\n url: \"https://sim.app.vegait.com/\", // devolvido ao default em 03/11 (pedido do Leandro).\n // Ficou vazio de 13/09 ate aqui; o override de HML\n // (NEXT_PUBLIC_SIM_URL -> hml.sim.app.vegait.com)\n // segue no pipeline e continua valendo em homolog.\n accent: \"#5B7DB1\",\n ssoEnabled: true, // sim-unit/sim-mobile/sim-pwa sao clients OpenIddict seedados pela\n // api (SimUnitClientSeeder/SimMobileClientSeeder/SimPwaClientSeeder).\n // As RedirectUris de prod entraram no accounts.release em 03/11\n // (!18616, 9bc919c). O seeder so roda no BOOT, entao o login\n // unificado so passa a existir depois de um deploy da api de prod;\n // antes disso o que esta gravado e o default localhost do\n // appsettings.json (o [] do appsettings.Production.json NAO zera o\n // array -- array vazio nao contribui chave, a :0 de baixo sobrevive).\n // Este campo e metadado inerte, nada aqui quebra. Mas antes de\n // prometer SSO, confira o estado real em vez de confiar nesta linha:\n // SELECT client_id, redirect_uris FROM \"OpenIddictApplications\"\n // WHERE client_id LIKE 'sim-%';\n // Se vier localhost, o deploy da api ainda nao rodou.\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 // Login pelo Accounts entrou em homolog em 10/11 (client feedback-frontend).\n // url fica vazia de proposito, mesmo criterio do gdi/easycheckin2: o SDK e\n // publicado no npm e consumido por outros produtos, entao quem liga e cada\n // consumidor por override de ambiente (NEXT_PUBLIC_FEEDBACK_URL), nunca o\n // default daqui. Quando subir em prod, o pipeline ganha o case de `main` (mesmo\n // caminho do gdi); o default aqui segue vazio pra sempre.\n url: \"\",\n accent: \"#C26A8C\",\n ssoEnabled: true,\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 easycheckin: {\n code: \"easycheckin\",\n name: \"Easy Check-in\",\n description: \"Check-in online servido pelo HXP (plataforma atual)\",\n url: \"\", // sem painel proprio pra abrir daqui: e superficie do HXP, nao produto\n // com frontend. A entrada existe justamente pra que o code NAO caia no\n // fallback de code desconhecido (url \"#\", que e truthy e sobrevive ao\n // filtro) e renderize um card morto rotulado \"easycheckin\". Mesmo papel\n // das entradas de `hxp` e `feedback`.\n //\n // Contratar na unidade segue sendo o caminho normal (e o que alimenta o\n // roteamento do Integration v2). Atribuir a USUARIO continua sendo coisa\n // que nao se faz -- mas nada na api impede: o AssignUserProductCommandValidator\n // so checa IsActiveAsync, e desativar o produto quebraria o contrato na\n // unidade junto. Esta entrada nao cria a trava; so torna o engano invisivel.\n accent: \"#8A6F5C\",\n ssoEnabled: false, // nao tem client OIDC no Accounts; login e do proprio HXP\n },\n easycheckin2: {\n code: \"easycheckin2\",\n name: \"Easy Check-in (novo)\",\n description: \"Check-in online e web check-in do hospede (plataforma nova)\",\n url: \"\", // so existe em homolog (hml.easycheckin.app.vegait.com); a URL por\n // ambiente vem de NEXT_PUBLIC_EASYCHECKIN2_URL (.ci/pipeline.yml).\n // Quando o Easy novo subir em prod, o default vira\n // https://easycheckin.app.vegait.com/ -- e as RedirectUris de prod do\n // client ja estao no accounts.release esperando por isso.\n accent: \"#1C8FA8\",\n ssoEnabled: true, // client OIDC `easycheckin2-admin` seedado pela api\n // (EasyCheckin2AdminClientSeeder), Code + PKCE.\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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vegait/accounts-sdk",
3
- "version": "0.1.20",
3
+ "version": "0.1.21-beta.54136",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "tag": "beta"