evo360-types 1.3.656 → 1.3.659

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,329 @@
1
+ // ======================================================
2
+ // evo-modules (feat-263 — Módulos da plataforma)
3
+ //
4
+ // Entitlement de módulos por tenant: "este cliente tem direito ao módulo X?".
5
+ // NÃO é feature flag: o kill switch operacional de cada módulo é o `rollout`
6
+ // (doc de plataforma), um gate independente.
7
+ //
8
+ // Storage do estado continua em `tenants/{t}.modules[key]`
9
+ // (`ITenantModuleEntitlement`, feat-160 D5). Este namespace acrescenta:
10
+ // - o catálogo em código (`PLATFORM_MODULE_CATALOG`), que carrega
11
+ // comportamento (passos de provisionamento, grupos RBAC, métricas de franquia);
12
+ // - a decisão única `isModuleActive` / `hasModuleCapability`, usada por
13
+ // backend, web-app e Nexus (o Flutter espelha pela fixture de paridade);
14
+ // - os tipos do run de provisionamento, do doc de rollout genérico e da
15
+ // auditoria genérica.
16
+ //
17
+ // Firestore (por tenant):
18
+ // - App doc (locks): /tenants/{t}/apps/evo-modules campo `locks.{module}`
19
+ // - Auditoria: /tenants/{t}/apps/evo-modules/entitlement-audit/{id}
20
+ // - Provisionamento: /tenants/{t}/apps/evo-modules/provisioning/{runId}
21
+ //
22
+ // Plataforma (só backend):
23
+ // - Rollout genérico: /platform/evo-modules/rollout/{module}
24
+ // (a telemedicina continua em `platform/evo-telemedicine/config/rollout`,
25
+ // lida pelo mesmo reader via `rollout.layout = 'legacy_fields'`).
26
+ // ======================================================
27
+
28
+ import type { IFireDoc, IFireGlobalDoc } from "../shared";
29
+ import type {
30
+ ITenantModuleEntitlement,
31
+ TenantModuleSource,
32
+ TenantModuleStatus,
33
+ } from "../evo-tenant";
34
+ import type {
35
+ EntitlementAuditAction,
36
+ IEntitlementAuditActor,
37
+ } from "../evo-telemedicine";
38
+
39
+ export * from "./catalog";
40
+ export * from "./activation";
41
+
42
+ export const EVO_MODULES_APP = "evo-modules";
43
+ export const MODULES_ENTITLEMENT_AUDIT_COLLECTION = "entitlement-audit";
44
+ export const MODULES_PROVISIONING_COLLECTION = "provisioning";
45
+ export const MODULES_ROLLOUT_PLATFORM_PATH = "platform/evo-modules/rollout";
46
+ /** TTL do lock por tenant+módulo durante um apply. */
47
+ export const MODULES_LOCK_TTL_MS = 10 * 60 * 1000;
48
+
49
+ /** Regex da chave de módulo (`tenants/{t}.modules[key]`). */
50
+ export const MODULE_KEY_PATTERN = /^[a-z][a-z0-9_]{1,40}$/;
51
+
52
+ export type ModuleAction = "enable" | "disable" | "trial" | "override";
53
+ export const ModuleActionEnum = {
54
+ Enable: "enable",
55
+ Disable: "disable",
56
+ Trial: "trial",
57
+ Override: "override",
58
+ } as const;
59
+
60
+ export type ModuleStepKind =
61
+ | "entitlement"
62
+ | "rbac_group"
63
+ | "rbac_recompute"
64
+ | "contract_allowance"
65
+ | "contract_check"
66
+ | "rollout_check";
67
+ export const ModuleStepKindEnum = {
68
+ Entitlement: "entitlement",
69
+ RbacGroup: "rbac_group",
70
+ RbacRecompute: "rbac_recompute",
71
+ ContractAllowance: "contract_allowance",
72
+ ContractCheck: "contract_check",
73
+ RolloutCheck: "rollout_check",
74
+ } as const;
75
+ export const MODULE_STEP_KINDS: readonly ModuleStepKind[] = [
76
+ "entitlement",
77
+ "rbac_group",
78
+ "rbac_recompute",
79
+ "contract_allowance",
80
+ "contract_check",
81
+ "rollout_check",
82
+ ];
83
+
84
+ // ----- Catálogo
85
+
86
+ export interface IModuleRolloutDescriptor {
87
+ /** Path completo do doc (4 segmentos: `platform/<app>/<col>/<doc>`). */
88
+ path: string;
89
+ /**
90
+ * `flags_map`: doc genérico `{ flags: { [flag]: boolean }, emergency_kill }`.
91
+ * `legacy_fields`: doc com um campo booleano por flag (telemedicina).
92
+ */
93
+ layout: "flags_map" | "legacy_fields";
94
+ /** flag -> campo do doc (`legacy_fields`) ou chave em `flags` (`flags_map`). */
95
+ flags: Record<string, string>;
96
+ }
97
+
98
+ export interface IModuleCapabilityDefinition {
99
+ label: string;
100
+ default: boolean | number | string;
101
+ }
102
+
103
+ export interface IModuleAllowanceMetric {
104
+ /** Chave de `NexFinopsMetricCatalog`. */
105
+ metric: string;
106
+ /** Gravado no contrato vigente quando a chave está ausente (Q6). */
107
+ default_included: number;
108
+ }
109
+
110
+ export interface IPlatformModuleDefinition {
111
+ /** `MODULE_KEY_PATTERN`; é a chave em `tenants/{t}.modules`. */
112
+ key: string;
113
+ label: string;
114
+ description?: string;
115
+ /** App do Firestore onde vivem os dados do módulo (`evo-telemedicine`, `evo-csq`). */
116
+ app_id: string;
117
+ capabilities: Record<string, IModuleCapabilityDefinition>;
118
+ /** Status/origem gravados no `enable` de um tenant sem entitlement. */
119
+ default_entitlement: { status: TenantModuleStatus; source: TenantModuleSource };
120
+ /** Tenant novo nasce com o módulo (`createTenant`). */
121
+ default_for_new_tenants: boolean;
122
+ /** Grupos RBAC globais que tornam os papéis do módulo atribuíveis. */
123
+ rbac_groups: string[];
124
+ allowance_metrics: IModuleAllowanceMetric[];
125
+ rollout: IModuleRolloutDescriptor | null;
126
+ depends_on: string[];
127
+ provisioning: { enable: ModuleStepKind[]; disable: ModuleStepKind[] };
128
+ legacy?: {
129
+ /** Trilhas antigas (relativas a `tenants/{t}/`), lidas pelo histórico e nunca escritas. */
130
+ audit_collections?: string[];
131
+ };
132
+ }
133
+
134
+ // ----- Run de provisionamento: tenants/{t}/apps/evo-modules/provisioning/{runId}
135
+
136
+ export type ModuleStepStatus =
137
+ | "pending"
138
+ | "noop"
139
+ | "change"
140
+ | "done"
141
+ | "failed"
142
+ | "blocked"
143
+ | "skipped";
144
+
145
+ export interface IModuleProvisioningStep {
146
+ kind: ModuleStepKind;
147
+ status: ModuleStepStatus;
148
+ summary: string;
149
+ before?: unknown;
150
+ after?: unknown;
151
+ error?: string | null;
152
+ started_at?: Date | null;
153
+ finished_at?: Date | null;
154
+ }
155
+
156
+ export type ModuleRunMode = "plan" | "apply";
157
+ export type ModuleRunStatus =
158
+ | "planned"
159
+ | "running"
160
+ | "done"
161
+ | "failed"
162
+ | "blocked"
163
+ | "superseded";
164
+
165
+ export interface IModuleChangeParams {
166
+ /** `trial`: obrigatório. */
167
+ expires_at?: Date | null;
168
+ /** `override`: obrigatório. */
169
+ override_until?: Date | null;
170
+ /** `enable`: libera o passo `contract_allowance` quando ele vem `blocked` (Q6). */
171
+ allow_without_contract?: boolean;
172
+ /** `disable`: remove o grupo RBAC se nenhum outro módulo ativo o declara (Q7). */
173
+ purge_rbac?: boolean;
174
+ /** `disable`: grava `suspended` em vez de `cancelled`. */
175
+ suspend?: boolean;
176
+ }
177
+
178
+ export interface IModuleRunActor extends IEntitlementAuditActor {
179
+ login_id?: string;
180
+ }
181
+
182
+ export interface IModuleProvisioningRun extends IFireDoc {
183
+ tenant: string;
184
+ module: string;
185
+ action: ModuleAction;
186
+ mode: ModuleRunMode;
187
+ status: ModuleRunStatus;
188
+ params: IModuleChangeParams;
189
+ /** Hash estável dos passos planejados (kind+status+after). Muda ⇒ `PLAN_DRIFT`. */
190
+ plan_hash: string;
191
+ /** `PLATFORM_MODULE_CATALOG_VERSION` de quem planejou/aplicou. */
192
+ types_version: string;
193
+ steps: IModuleProvisioningStep[];
194
+ actor: IModuleRunActor;
195
+ reason: string;
196
+ /** Run `apply` que este retoma. */
197
+ resumed_from?: string | null;
198
+ /** Run `plan` que originou este `apply`. */
199
+ plan_run_id?: string | null;
200
+ error?: string | null;
201
+ finished_at?: Date | null;
202
+ }
203
+
204
+ /** Lock no doc `tenants/{t}/apps/evo-modules`, campo `locks.{module}`. */
205
+ export interface IModuleLock {
206
+ run_id: string;
207
+ expires_at: Date;
208
+ }
209
+
210
+ // ----- Rollout genérico: platform/evo-modules/rollout/{module}
211
+
212
+ export interface IModuleRolloutDoc extends IFireGlobalDoc {
213
+ flags: Record<string, boolean>;
214
+ emergency_kill: boolean;
215
+ updated_at: Date;
216
+ updated_by: string;
217
+ }
218
+
219
+ /** Estado de rollout normalizado (o mesmo para os dois layouts). */
220
+ export interface IModuleRolloutState {
221
+ module: string;
222
+ path: string;
223
+ /** `false` ⇒ doc ausente/ilegível: tudo negado. */
224
+ exists: boolean;
225
+ flags: Record<string, boolean>;
226
+ emergency_kill: boolean;
227
+ updated_at: Date | null;
228
+ updated_by: string | null;
229
+ }
230
+
231
+ // ----- Auditoria genérica: tenants/{t}/apps/evo-modules/entitlement-audit/{id}
232
+
233
+ export interface IModuleEntitlementAudit extends IFireDoc {
234
+ module: string;
235
+ action: EntitlementAuditAction;
236
+ before?: ITenantModuleEntitlement | null;
237
+ after: ITenantModuleEntitlement;
238
+ actor: IEntitlementAuditActor;
239
+ reason: string;
240
+ occurred_at: Date;
241
+ correlation_id?: string;
242
+ run_id?: string;
243
+ }
244
+
245
+ // ----- Executor de passo injetável
246
+ //
247
+ // Permite que um passo seja implementado fora do `@evo/model` (ex.: a franquia
248
+ // no `@evo/nex-model`) sem dependência entre os packages: quem roda o runner
249
+ // (functions-nexus, CLI) registra o executor.
250
+
251
+ export interface IModuleStepContext {
252
+ tenant: string;
253
+ module: IPlatformModuleDefinition;
254
+ action: ModuleAction;
255
+ params: IModuleChangeParams;
256
+ actor_user_id: string | null;
257
+ /** Passos anteriores do mesmo run (estado corrente). */
258
+ previous_steps?: IModuleProvisioningStep[];
259
+ }
260
+
261
+ export type ModuleStepPlanResult = Pick<
262
+ IModuleProvisioningStep,
263
+ "status" | "summary" | "before" | "after"
264
+ >;
265
+ export type ModuleStepApplyResult = Pick<
266
+ IModuleProvisioningStep,
267
+ "status" | "summary" | "after"
268
+ >;
269
+
270
+ export interface IModuleStepExecutor {
271
+ plan(ctx: IModuleStepContext): Promise<ModuleStepPlanResult>;
272
+ apply(ctx: IModuleStepContext): Promise<ModuleStepApplyResult>;
273
+ }
274
+
275
+ // ----- Callables do Nexus (`nex_tenants-*`)
276
+ //
277
+ // Datas atravessam o callable como ISO string.
278
+
279
+ export interface IModuleChangeRequest {
280
+ tenant: string;
281
+ module: string;
282
+ action: ModuleAction;
283
+ params?: IModuleChangeParams;
284
+ reason: string;
285
+ }
286
+
287
+ export interface IModuleApplyRequest {
288
+ tenant: string;
289
+ /** Aplica o plano deste run (`mode: 'plan'`). Exclusivo com `resume_run_id`. */
290
+ plan_run_id?: string;
291
+ /** Retoma um run `apply` em `failed`/`blocked`. */
292
+ resume_run_id?: string;
293
+ /** Sobrepõe params do plano (só `allow_without_contract` faz sentido aqui). */
294
+ params?: Pick<IModuleChangeParams, "allow_without_contract">;
295
+ }
296
+
297
+ export interface IModuleOverviewRequest {
298
+ tenant: string;
299
+ }
300
+
301
+ export interface IModuleOverviewEntry {
302
+ definition: IPlatformModuleDefinition;
303
+ entitlement: ITenantModuleEntitlement | null;
304
+ active: boolean;
305
+ rollout: IModuleRolloutState | null;
306
+ }
307
+
308
+ export interface IModuleOverviewResponse {
309
+ ok: true;
310
+ tenant: string;
311
+ catalog_version: string;
312
+ modules: IModuleOverviewEntry[];
313
+ /** Últimos 10 runs (plan e apply), mais recentes primeiro. */
314
+ runs: IModuleProvisioningRun[];
315
+ /** Últimas 30 entradas de auditoria (caminho genérico + legados), por `occurred_at` desc. */
316
+ audit: IModuleEntitlementAudit[];
317
+ }
318
+
319
+ export interface IModulePlanResponse {
320
+ ok: true;
321
+ run: IModuleProvisioningRun;
322
+ }
323
+
324
+ export type ModuleApplyErrorCode = "PLAN_DRIFT" | "MODULE_LOCKED" | "RUN_NOT_FOUND" | "RUN_NOT_RESUMABLE";
325
+
326
+ export type IModuleApplyResponse =
327
+ | { ok: true; run: IModuleProvisioningRun }
328
+ /** O estado mudou desde o plano: `plan` é o run novo (`mode: 'plan'`) para revisar. */
329
+ | { ok: false; error: "PLAN_DRIFT"; plan: IModuleProvisioningRun };
@@ -13,7 +13,7 @@ exports.TELEMEDICINE_CONFIG_TRANSCRIPTION_DOC = exports.TELEMEDICINE_CONFIG_ROLL
13
13
  // - Events: .../teleconsultations/{id}/events/{eventId} (append-only)
14
14
  // - Webhook events: .../teleconsultations/{id}/webhook-events/{providerEventId} (dedup + outbox)
15
15
  // - Appointment index: /tenants/{t}/apps/evo-telemedicine/appointment-index/{calendarId}__{appointmentId}
16
- // - Entitlement audit: /tenants/{t}/apps/evo-telemedicine/entitlement-audit/{id}
16
+ // - Entitlement audit: /tenants/{t}/apps/evo-telemedicine/entitlement-audit/{id} (congelada; nova: apps/evo-modules, feat-263)
17
17
  //
18
18
  // Plataforma (só backend; rules negam leitura ao FE):
19
19
  // - Legal texts: /platform/evo-telemedicine/legal-texts/{subject}-{locale}-{version}
@@ -10,7 +10,7 @@
10
10
  // - Events: .../teleconsultations/{id}/events/{eventId} (append-only)
11
11
  // - Webhook events: .../teleconsultations/{id}/webhook-events/{providerEventId} (dedup + outbox)
12
12
  // - Appointment index: /tenants/{t}/apps/evo-telemedicine/appointment-index/{calendarId}__{appointmentId}
13
- // - Entitlement audit: /tenants/{t}/apps/evo-telemedicine/entitlement-audit/{id}
13
+ // - Entitlement audit: /tenants/{t}/apps/evo-telemedicine/entitlement-audit/{id} (congelada; nova: apps/evo-modules, feat-263)
14
14
  //
15
15
  // Plataforma (só backend; rules negam leitura ao FE):
16
16
  // - Legal texts: /platform/evo-telemedicine/legal-texts/{subject}-{locale}-{version}
@@ -1,6 +1,6 @@
1
1
  export * from "./fb_collections";
2
2
  import type { IFireDoc, IFireGlobalDoc } from "../shared";
3
- import type { ITenantModuleEntitlement } from "../evo-tenant";
3
+ import type { IModuleEntitlementAudit } from "../evo-modules";
4
4
  import type { VirtualCareRecordingMode } from "../evo-med/calendar";
5
5
  export declare const EvoTelemedicinePermissions: {
6
6
  /** Ver estado e dados operacionais básicos. */
@@ -816,17 +816,13 @@ export interface IEntitlementAuditActor {
816
816
  type: "user" | "system" | "script";
817
817
  id?: string;
818
818
  }
819
- /** Fora do doc do tenant, como pede o data-model. `before/after` são a projeção sanitizada (sem preço/contrato). */
820
- export interface ITelemedicineEntitlementAudit extends IFireDoc {
821
- module: string;
822
- action: EntitlementAuditAction;
823
- before?: ITenantModuleEntitlement | null;
824
- after: ITenantModuleEntitlement;
825
- actor: IEntitlementAuditActor;
826
- reason: string;
827
- occurred_at: Date;
828
- correlation_id?: string;
829
- }
819
+ /**
820
+ * Fora do doc do tenant, como pede o data-model. `before/after` são a projeção sanitizada (sem preço/contrato).
821
+ * feat-263: alias da auditoria genérica. Docs novos vão para
822
+ * `tenants/{t}/apps/evo-modules/entitlement-audit/{id}`; a trilha antiga em
823
+ * `apps/evo-telemedicine/entitlement-audit` fica congelada (só leitura).
824
+ */
825
+ export type ITelemedicineEntitlementAudit = IModuleEntitlementAudit;
830
826
  export declare const TelemedicineErrorCode: {
831
827
  /** 403 — entitlement ausente/inativo (sem bypass de super admin). */
832
828
  readonly TenantModuleDisabled: "TENANT_MODULE_DISABLED";
@@ -1,6 +1,6 @@
1
1
  export * from "./fb_collections";
2
2
  import type { IFireDoc, IFireGlobalDoc } from "../shared";
3
- import type { ITenantModuleEntitlement } from "../evo-tenant";
3
+ import type { IModuleEntitlementAudit } from "../evo-modules";
4
4
  // feat-160 F15/F16 — a política da sala é projeção do `calendar.virtual_care`:
5
5
  // o MODO de gravação é o mesmo tipo dos dois lados, de propósito.
6
6
  import type { VirtualCareRecordingMode } from "../evo-med/calendar";
@@ -930,17 +930,13 @@ export interface IEntitlementAuditActor {
930
930
  id?: string;
931
931
  }
932
932
 
933
- /** Fora do doc do tenant, como pede o data-model. `before/after` são a projeção sanitizada (sem preço/contrato). */
934
- export interface ITelemedicineEntitlementAudit extends IFireDoc {
935
- module: string;
936
- action: EntitlementAuditAction;
937
- before?: ITenantModuleEntitlement | null;
938
- after: ITenantModuleEntitlement;
939
- actor: IEntitlementAuditActor;
940
- reason: string;
941
- occurred_at: Date;
942
- correlation_id?: string;
943
- }
933
+ /**
934
+ * Fora do doc do tenant, como pede o data-model. `before/after` são a projeção sanitizada (sem preço/contrato).
935
+ * feat-263: alias da auditoria genérica. Docs novos vão para
936
+ * `tenants/{t}/apps/evo-modules/entitlement-audit/{id}`; a trilha antiga em
937
+ * `apps/evo-telemedicine/entitlement-audit` fica congelada (só leitura).
938
+ */
939
+ export type ITelemedicineEntitlementAudit = IModuleEntitlementAudit;
944
940
 
945
941
  // ======================================================
946
942
  // Códigos de erro (plan §4 — `{ ok: false, error: '<CODE>', message? }` com status estável)
@@ -58,7 +58,9 @@ export enum ITenantStatus {
58
58
  // - ausência da chave em `modules` equivale a desabilitado;
59
59
  // - `enabled=false`, `suspended`, `cancelled` ou trial expirado bloqueia usos novos;
60
60
  // - capability ausente equivale a `false`;
61
- // - alteração gera auditoria FORA do doc do tenant (`entitlement-audit` do módulo);
61
+ // - alteração gera auditoria FORA do doc do tenant, no caminho genérico
62
+ // `tenants/{t}/apps/evo-modules/entitlement-audit/{id}` (feat-263; a trilha
63
+ // antiga da telemedicina em `apps/evo-telemedicine/entitlement-audit` fica só leitura);
62
64
  // - super admin NÃO ignora entitlement (D6) — exceção só via `override_until`,
63
65
  // temporária e auditada.
64
66
  export type TenantModuleStatus = "active" | "trial" | "suspended" | "cancelled";