@microteia/console 2.38.0 → 2.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/NOTICE ADDED
@@ -0,0 +1,97 @@
1
+ Microteia — An AI-First Business Application Platform
2
+ Copyright (C) 2026 Alexnaldo Carvalho dos Santos and the Microteia contributors
3
+ Source code: https://github.com/microteia/microteia
4
+
5
+ This program is free software: you can redistribute it and/or modify it under the terms of the
6
+ GNU Affero General Public License as published by the Free Software Foundation, either version 3 of
7
+ the License, or (at your option) any later version.
8
+
9
+ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
10
+ even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
11
+ General Public License for more details.
12
+
13
+ You should have received a copy of the GNU Affero General Public License along with this program, in
14
+ the file LICENSE. If not, see <https://www.gnu.org/licenses/>.
15
+
16
+ The license is supplemented by the additional terms and the additional permission below, as allowed
17
+ by section 7 of the GNU Affero General Public License. Both apply to every copy of Microteia and to
18
+ every work based on it, and this file must be conveyed together with the LICENSE file.
19
+
20
+
21
+ ===================================================================================================
22
+ ADDITIONAL TERMS UNDER SECTION 7 OF THE GNU AFFERO GENERAL PUBLIC LICENSE
23
+ ===================================================================================================
24
+
25
+ 1. Attribution — section 7(b).
26
+
27
+ Every copy of Microteia, modified or not, that is conveyed or that users interact with remotely
28
+ over a computer network, must preserve the copyright notices and display the following
29
+ attribution, unaltered and legible:
30
+
31
+ Based on Microteia — https://github.com/microteia/microteia
32
+ Copyright (C) Alexnaldo Carvalho dos Santos and the Microteia contributors.
33
+ Licensed under the GNU Affero General Public License, version 3 or later.
34
+
35
+ The attribution must appear (a) in the Appropriate Legal Notices displayed by the interactive user
36
+ interface — in Microteia, the "Copyright and contact" section of the Terms of Use page, served at
37
+ the "/license" route, which is reachable before signing in — and (b) in the README, or equivalent
38
+ documentation, of every distribution. A translated interface may display the attribution in its
39
+ own language, provided the link and the names are kept.
40
+
41
+ 2. Marking of modified versions — section 7(c).
42
+
43
+ A modified version must be marked in reasonable ways as different from the original version, and
44
+ must not misrepresent its origin: it must not be presented as the original Microteia, nor as made,
45
+ endorsed or supported by its author or contributors.
46
+
47
+ 3. Trade names and trademarks — section 7(e).
48
+
49
+ This license grants no rights under trademark law to the name "Microteia", to its logos or to any
50
+ other brand identifier, beyond the use required to comply with the attribution in term 1 and to
51
+ state truthfully that a work is based on Microteia.
52
+
53
+
54
+ ===================================================================================================
55
+ ADDITIONAL PERMISSION UNDER SECTION 7 — MICROTEIA EXTENSION EXCEPTION
56
+ ===================================================================================================
57
+
58
+ Microteia is built to be extended without being modified. As a special exception, the copyright
59
+ holders of Microteia give you permission to create an Extension, and to use, convey, sell and offer
60
+ it to users over a computer network, under terms of your choice — proprietary, permissive or
61
+ copyleft — and to run it together with Microteia, without the Extension, or the combination of the
62
+ Extension with Microteia, being subject to the terms of the GNU Affero General Public License as to
63
+ the Extension, provided that the Extension interacts with Microteia only through the Published
64
+ Interfaces.
65
+
66
+ "Extension" means a plugin, extension package, module, theme, language package, view, startup
67
+ script, sandbox script, workflow, document template, or Cloud Function — together with its source
68
+ code and with every artifact compiled from it — that is not itself part of Microteia and that is
69
+ written to be installed in, or deployed beside, an installation of Microteia.
70
+
71
+ "Published Interfaces" means:
72
+
73
+ a. the extension package format — the manifest, its extension points and the artifact that the
74
+ platform validates and installs;
75
+ b. the authoring SDK, published as "@microteia/sdk", including its command-line tools and its type
76
+ definitions;
77
+ c. the curated host API offered to extension views and startup scripts (the "@microteia/*"
78
+ modules described by the SDK type definitions);
79
+ d. the API offered to sandbox scripts;
80
+ e. the office functions API (the "office" entry point of the platform's functions package), and the
81
+ directories in which an installation mounts its own Cloud Functions;
82
+ f. the Model Context Protocol server and the callable functions that the platform exposes to
83
+ external clients.
84
+
85
+ You may also copy code from the authoring examples that are distributed with the SDK (the
86
+ "examples" directory of the SDK package and the first-party extensions it is built from) into an
87
+ Extension, and convey that copied code as part of the Extension, under the Extension's terms.
88
+
89
+ This exception does not cover modifications of Microteia itself. A change to the kernel, to a
90
+ first-party plugin, to the SDK or to any other part of Microteia — including a change made in order
91
+ to support an Extension — is a modification of Microteia and remains subject to the GNU Affero
92
+ General Public License and to the additional terms above. The exception also grants no right to use
93
+ the Microteia name or trademarks for an Extension beyond stating truthfully that the Extension works
94
+ with Microteia.
95
+
96
+ If you modify Microteia, you may extend this exception to your version, but you are not obligated to
97
+ do so. If you do not wish to do so, delete this exception statement from your version.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Microteia Console 2.38.0
1
+ # Microteia Console 2.42.0
2
2
 
3
3
  Installs and updates Microteia in a Firebase project you own. It has no npm dependencies of its own —
4
4
  what it needs are command-line tools that are already on the machine, listed at the bottom.
@@ -64,3 +64,10 @@ the Microteia repository.
64
64
 
65
65
  `CONSOLE-VERSIONS.md`, in this folder. `unpack` copies it into the installation it manages, beside the
66
66
  platform's own `KERNEL-VERSIONS.md` — each history travels with the artifact it describes.
67
+
68
+ ## License
69
+
70
+ Free software, licensed under the GNU Affero General Public License, version 3 or (at your option) any
71
+ later version — the full text is in `LICENSE`, and the additional terms in `NOTICE`, in this folder.
72
+ The source code of this Console and of the platform it installs is at
73
+ https://github.com/microteia/microteia.
@@ -0,0 +1,58 @@
1
+ import type { FirestoreTypedValue } from './firestoreRest.js';
2
+ /**
3
+ * O que o PROJETO tem aplicado, devolvido ao banco onde a aplicação pode ler.
4
+ *
5
+ * O carimbo mora em cada cópia instalada (`backendState`), e o `provision` o grava nas cópias que
6
+ * existem no momento em que roda. A cópia que chega DEPOIS — o segundo escritório que atualiza para a
7
+ * versão que o primeiro já pôs no ar — ficava "Backend pendente" até alguém rodar o comando de novo, e o
8
+ * comando não decidia nada: a aprovação por hash já é do projeto e as functions já estavam no ar. Ele
9
+ * só copiava um registro.
10
+ *
11
+ * Este documento é esse registro, uma vez por projeto: para cada extensão, o fingerprint que o projeto
12
+ * roda. A tela libera a cópia cujo fingerprint está aqui. Quem afirma continua sendo só o Console — a
13
+ * regra de `backend_state` nega escrita ao cliente.
14
+ */
15
+ export declare const BACKEND_APPLIED_PATH = "/backend_state/applied";
16
+ export interface AppliedEntry {
17
+ pluginId: string;
18
+ fingerprint: string;
19
+ approvedHash?: string;
20
+ approvedBy?: string;
21
+ }
22
+ /** O que uma cópia exige, na forma mínima que as contas abaixo precisam. */
23
+ export interface FingerprintClaim {
24
+ pluginId: string;
25
+ fingerprint: string;
26
+ }
27
+ /**
28
+ * Os campos do documento, montados sem tocar na rede.
29
+ *
30
+ * Lista e não mapa por `pluginId` porque o id tem pontos, e ponto em chave de mapa vira caminho de
31
+ * campo no Firestore. A FORMA é contrato com o kernel, versionado à parte — é o que o teste confere.
32
+ */
33
+ export declare const backendAppliedFields: (entries: readonly AppliedEntry[], writtenAt: Date) => Record<string, FirestoreTypedValue>;
34
+ /**
35
+ * O documento lido, defensivo pelo mesmo motivo do lado do kernel: pode ter sido escrito por um Console
36
+ * de outra versão. Entrada sem id ou sem fingerprint não libera nada, então é descartada.
37
+ */
38
+ export declare const parseBackendApplied: (data: Record<string, unknown> | null) => AppliedEntry[];
39
+ export declare const appliedEntryOf: (entries: readonly AppliedEntry[], claim: FingerprintClaim) => AppliedEntry | undefined;
40
+ /**
41
+ * O que continua verdadeiro aconteça o que acontecer nesta execução.
42
+ *
43
+ * Só fica a entrada de extensão ainda instalada E com o fingerprint da cópia mais nova — a que o
44
+ * deploy vai pôr no ar. A extensão que saiu de todo escritório pode ter a function apagada pelo
45
+ * `--prune`; a versão superada pode ter o código trocado pelo deploy. Nos dois casos a entrada
46
+ * anunciaria no ar o que talvez não esteja, e uma cópia nova nasceria liberada contra uma function que
47
+ * não existe — o defeito que parece CORS e que a pendência existe para impedir.
48
+ *
49
+ * Só REMOVE: acrescentar é do fim da execução, quando o deploy já respondeu.
50
+ */
51
+ export declare const reduceBackendApplied: (entries: readonly AppliedEntry[], newest: readonly FingerprintClaim[]) => AppliedEntry[];
52
+ /**
53
+ * `[]` quando o documento não existe. Leitura recusada também vira `[]`, com aviso de quem chama: o
54
+ * que se perde é só o atalho — sem entradas, nada é materializado, e a redução grava a lista vazia,
55
+ * que é a resposta segura para "não sei o que o projeto tem".
56
+ */
57
+ export declare const readBackendApplied: (projectId: string) => Promise<AppliedEntry[]>;
58
+ export declare const writeBackendApplied: (projectId: string, entries: readonly AppliedEntry[]) => Promise<void>;
@@ -0,0 +1,81 @@
1
+ import { asArray, asMap, asString, asTimestamp, getDocument, patchFields } from './firestoreRest.js';
2
+ import { CLI_VERSION } from '../shared/paths.js';
3
+ /**
4
+ * O que o PROJETO tem aplicado, devolvido ao banco onde a aplicação pode ler.
5
+ *
6
+ * O carimbo mora em cada cópia instalada (`backendState`), e o `provision` o grava nas cópias que
7
+ * existem no momento em que roda. A cópia que chega DEPOIS — o segundo escritório que atualiza para a
8
+ * versão que o primeiro já pôs no ar — ficava "Backend pendente" até alguém rodar o comando de novo, e o
9
+ * comando não decidia nada: a aprovação por hash já é do projeto e as functions já estavam no ar. Ele
10
+ * só copiava um registro.
11
+ *
12
+ * Este documento é esse registro, uma vez por projeto: para cada extensão, o fingerprint que o projeto
13
+ * roda. A tela libera a cópia cujo fingerprint está aqui. Quem afirma continua sendo só o Console — a
14
+ * regra de `backend_state` nega escrita ao cliente.
15
+ */
16
+ export const BACKEND_APPLIED_PATH = '/backend_state/applied';
17
+ /**
18
+ * Os campos do documento, montados sem tocar na rede.
19
+ *
20
+ * Lista e não mapa por `pluginId` porque o id tem pontos, e ponto em chave de mapa vira caminho de
21
+ * campo no Firestore. A FORMA é contrato com o kernel, versionado à parte — é o que o teste confere.
22
+ */
23
+ export const backendAppliedFields = (entries, writtenAt) => ({
24
+ writtenAt: asTimestamp(writtenAt),
25
+ consoleVersion: asString(CLI_VERSION),
26
+ extensions: asArray(entries.map((entry) => asMap({
27
+ pluginId: asString(entry.pluginId),
28
+ fingerprint: asString(entry.fingerprint),
29
+ ...(entry.approvedHash !== undefined ? { approvedHash: asString(entry.approvedHash) } : {}),
30
+ ...(entry.approvedBy !== undefined ? { approvedBy: asString(entry.approvedBy) } : {}),
31
+ }))),
32
+ });
33
+ /**
34
+ * O documento lido, defensivo pelo mesmo motivo do lado do kernel: pode ter sido escrito por um Console
35
+ * de outra versão. Entrada sem id ou sem fingerprint não libera nada, então é descartada.
36
+ */
37
+ export const parseBackendApplied = (data) => {
38
+ const list = data?.extensions;
39
+ if (!Array.isArray(list))
40
+ return [];
41
+ return list.flatMap((item) => {
42
+ if (typeof item !== 'object' || item === null)
43
+ return [];
44
+ const fields = item;
45
+ if (typeof fields.pluginId !== 'string' || fields.pluginId === '')
46
+ return [];
47
+ if (typeof fields.fingerprint !== 'string' || fields.fingerprint === '')
48
+ return [];
49
+ return [{
50
+ pluginId: fields.pluginId,
51
+ fingerprint: fields.fingerprint,
52
+ ...(typeof fields.approvedHash === 'string' ? { approvedHash: fields.approvedHash } : {}),
53
+ ...(typeof fields.approvedBy === 'string' ? { approvedBy: fields.approvedBy } : {}),
54
+ }];
55
+ });
56
+ };
57
+ export const appliedEntryOf = (entries, claim) => entries.find((entry) => entry.pluginId === claim.pluginId && entry.fingerprint === claim.fingerprint);
58
+ /**
59
+ * O que continua verdadeiro aconteça o que acontecer nesta execução.
60
+ *
61
+ * Só fica a entrada de extensão ainda instalada E com o fingerprint da cópia mais nova — a que o
62
+ * deploy vai pôr no ar. A extensão que saiu de todo escritório pode ter a function apagada pelo
63
+ * `--prune`; a versão superada pode ter o código trocado pelo deploy. Nos dois casos a entrada
64
+ * anunciaria no ar o que talvez não esteja, e uma cópia nova nasceria liberada contra uma function que
65
+ * não existe — o defeito que parece CORS e que a pendência existe para impedir.
66
+ *
67
+ * Só REMOVE: acrescentar é do fim da execução, quando o deploy já respondeu.
68
+ */
69
+ export const reduceBackendApplied = (entries, newest) => {
70
+ const standing = new Map(newest.map((claim) => [claim.pluginId, claim.fingerprint]));
71
+ return entries.filter((entry) => standing.get(entry.pluginId) === entry.fingerprint);
72
+ };
73
+ /**
74
+ * `[]` quando o documento não existe. Leitura recusada também vira `[]`, com aviso de quem chama: o
75
+ * que se perde é só o atalho — sem entradas, nada é materializado, e a redução grava a lista vazia,
76
+ * que é a resposta segura para "não sei o que o projeto tem".
77
+ */
78
+ export const readBackendApplied = async (projectId) => parseBackendApplied(await getDocument(projectId, BACKEND_APPLIED_PATH));
79
+ export const writeBackendApplied = async (projectId, entries) => {
80
+ await patchFields(projectId, BACKEND_APPLIED_PATH, backendAppliedFields(entries, new Date()));
81
+ };
@@ -1,228 +1,57 @@
1
1
  import type { ParsedArgs } from '../shared/args.js';
2
- import type { InstalledExtensionRecord, TenantSummary } from './installedExtensions.js';
3
- /**
4
- * `microteia export-catalog` — o RETRATO do domínio instalado, para o diretório de autoria.
5
- *
6
- * O servidor MCP do SDK monta o catálogo a partir dos `plugin.ts` do diretório mais o registro de
7
- * entidades do kernel. Numa instalação real isso deixa de fora as duas coisas que mais importam: a
8
- * tela que o escritório criou no Form Design e a extensão de terceiro já instalada. O detector de
9
- * "campo que pertence a outra entidade conhecida" não podia disparar contra elas, porque para ele elas
10
- * não existiam.
11
- *
12
- * A saída NÃO foi ligar o SDK ao Firestore. `microteia-plugin` é offline por desenho, e arrastar
13
- * credencial para dentro da ferramenta de autoria trocaria uma cegueira por um problema pior. Quem já
14
- * fala com o projeto do escritório é o Console — o token vem do `gcloud`, a cada execução, e nada é
15
- * guardado. Então é o Console que lê e escreve o arquivo, e o SDK apenas o encontra ali.
16
- *
17
- * Três decisões que não são preferência:
18
- *
19
- * - **O arquivo NÃO mora em `.microteia/`.** Aquele diretório é apagado por inteiro a cada `unpack`,
20
- * e um retrato que some na troca de kernel é um retrato que ninguém pode depender de ter. Ele fica
21
- * em `extensions/`, ao lado do `microteia.yaml`, que é onde o autor trabalha.
22
- * - **Ele é gitignorado**, pela conciliação que o SDK já faz do `.gitignore` daquele diretório. Ele
23
- * carrega nome de tela e de campo que o escritório digitou: dado do tenant, não artefato do produto.
24
- * - **`--tenant` é obrigatório.** Um projeto Firebase pode ter vários escritórios, e escolher um por
25
- * adivinhação escreveria o vocabulário de um dentro do trabalho de outro. Sem a opção, o comando
26
- * LISTA os que existem.
27
- */
28
- /** O nome é contrato com o SDK, que o procura por este nome — ver `installedCatalog.ts` do kernel. */
29
- export declare const CATALOG_FILE = "installed-catalog.json";
30
- /**
31
- * A versão do FORMATO. Ela existe para que um Console de outra época não seja lido como se falasse a
32
- * mesma língua: o SDK recusa o que não for exatamente esta, e diz o comando que refaz o arquivo.
33
- */
34
- export declare const CATALOG_FORMAT_VERSION = 1;
35
- /**
36
- * O que viaja de cada campo, e nada além.
37
- *
38
- * A linha é ESQUEMA versus CONTEÚDO. Nome, tipo, obrigatoriedade, referência, agregado e expressão
39
- * descrevem o que a entidade É, e é disso que o catálogo e os detectores precisam. Opção de lista,
40
- * máscara, texto de ajuda, valor padrão e regra de formulário são conteúdo e apresentação: o catálogo
41
- * nunca os lê, e cada um deles é mais vocabulário do escritório saindo do banco sem precisar.
42
- */
43
- export interface CatalogFieldExport {
44
- key: string;
45
- label: string;
46
- type: string;
47
- required: boolean;
48
- order: number;
49
- multi?: boolean;
50
- refEntity?: string;
51
- refDenormKey?: string;
52
- lookupCollection?: string;
53
- section?: string;
54
- origin: string;
55
- ownerPluginId?: string;
56
- aggregation?: Record<string, unknown>;
57
- compute?: string;
58
- /** O atalho de chave de um campo só — o kernel o desaçucara em `field:<key>`. */
59
- unique?: true;
60
- }
61
- /**
62
- * Uma chave da coleção (`ScreenDefinition.keys`): o que não pode se repetir. É esquema, como o campo:
63
- * quem modela precisa saber que ela existe antes de propor outra, e o plano a propunha como nova a
64
- * cada execução quando ela só existia na instalação.
65
- *
66
- * A mensagem do conflito NÃO viaja — é apresentação, e não muda qual reserva um registro recebe. O
67
- * `status` também não: ele é ignorado pelo kernel desde que a adoção passou a ser estado dele.
68
- */
69
- export interface CatalogKeyExport {
70
- name: string;
71
- kind: string;
72
- fields: string[];
73
- scope?: string;
74
- normalize?: {
75
- trim?: boolean;
76
- caseInsensitive?: boolean;
77
- formatPreset?: string;
78
- };
79
- sparse?: boolean;
80
- ownerPluginId?: string;
81
- }
82
- export interface CatalogScreenExport {
83
- key: string;
84
- /**
85
- * O id UNIVERSAL da tela. A chave acima é a instalada, que pode ter sido desambiguada; é pelo
86
- * `sourceId` que uma extensão endereça a tela de outra (`record:<sourceId>`).
87
- */
88
- sourceId?: string;
89
- collection?: string;
90
- titleSingular: string;
91
- titlePlural?: string;
92
- titleFieldKey: string;
93
- origin: string;
94
- ownerPluginId?: string;
95
- parentEntity?: string;
96
- parentFieldKey?: string;
97
- parentRelation?: string;
98
- tagCollection?: string;
99
- eventPrefix?: string;
100
- /**
101
- * Sempre escrita, mesmo vazia, pela mesma razão de `CatalogExtensionExport.exports`: a lista vazia diz
102
- * "esta tela não tem chave"; a chave ausente é o silêncio de um Console anterior a este campo.
103
- */
104
- keys: CatalogKeyExport[];
105
- fields: CatalogFieldExport[];
106
- }
107
- export interface CatalogListExport {
108
- collection: string;
109
- label: string;
110
- ownerPluginId?: string;
111
- }
112
- /**
113
- * Um artefato que a extensão declara em `manifest.exports` — a superfície que outra extensão pode
114
- * referenciar como `<pluginId>/<sourceId>`.
115
- *
116
- * O `kind` viaja porque é ele que decide ONDE a referência cabe: script em `scriptId`, automação em
117
- * `targetWorkflowId`. `unknown` é o export declarado que não casa com artefato nenhum do pacote —
118
- * dizê-lo é melhor que omiti-lo, porque uma referência a ele passaria no portão da instalação, que
119
- * confere a LISTA, e falharia só na primeira execução.
120
- *
121
- * O que NÃO viaja é o que o artefato faz: os passos e o código são comportamento, e o retrato é
122
- * esquema. O nome e o tipo são contrato.
123
- */
124
- export interface CatalogExtensionExportedArtifact {
2
+ import type { TenantSummary } from './installedExtensions.js';
3
+ /** Um documento já decodificado, com o id que a coleção lhe deu — a entrada que o escritor do SDK pede. */
4
+ export interface CatalogSourceDocument {
125
5
  id: string;
126
- kind: 'workflow' | 'action' | 'script' | 'unknown';
127
- name: string;
128
- }
129
- /**
130
- * Um grupo da barra lateral que a extensão declara (`ui.nav.group`). Viaja porque a CHAVE é contrato:
131
- * é o que outra extensão escreve para entrar no grupo, e quem autora sem enxergá-la inventa outra — e
132
- * o menu passa a mostrar dois grupos com o mesmo nome.
133
- *
134
- * O nome vai resolvido em cada idioma que o próprio pacote traz, porque é pelo nome que o autor
135
- * reconhece o grupo; o rótulo literal vai como está, com `translations` vazio.
136
- */
137
- export interface CatalogExtensionNavGroup {
138
- key: string;
139
- label: string;
140
- translations: Record<string, string>;
141
- order: number;
142
- }
143
- /**
144
- * Um modelo de documento que a extensão declara (`document.template`), como CONTRATO: a chave, a origem
145
- * e a forma de cada parâmetro. Viaja porque outra extensão GERA esse modelo pelo passo
146
- * `render-document`, com `param.<chave>` e `belongsTo`, e um nome trocado ali só aparece na geração —
147
- * com o campo em branco, ou com a recusa no clique.
148
- *
149
- * O que NÃO viaja é o texto do modelo, o rótulo, a regra e a amostra: é conteúdo, e o passo não os lê.
150
- */
151
- export interface CatalogExtensionDocumentParameter {
152
- key: string;
153
- type: string;
154
- required?: boolean;
155
- list?: boolean;
156
- entityType?: string;
157
- }
158
- export interface CatalogExtensionDocumentTemplate {
159
- key: string;
160
- source: 'record' | 'payload';
161
- parameters: CatalogExtensionDocumentParameter[];
6
+ data: Record<string, unknown>;
162
7
  }
163
8
  /**
164
- * Um preset de campo que a extensão declara (`data.field.preset`), como as PARTES que ele tem — nunca o
165
- * template, o formato nem a regra. É contra ele que o `mask` de um campo de outra extensão é conferido:
166
- * `mask: 'cpf-cnpj'` contra o `cpfCnpj` da Brasil instalava, e o campo ficava sem máscara.
9
+ * O pedaço do retrato que o Console ainda lê, para o resumo no terminal. A forma inteira é do SDK — o
10
+ * Console não a conhece, e é exatamente isso que o impede de escrever um formato que o kernel não lê.
167
11
  */
168
- export interface CatalogExtensionFieldPreset {
169
- key: string;
170
- slots: ('mask' | 'format' | 'validate')[];
12
+ interface WrittenCatalog {
13
+ screens: {
14
+ origin: string;
15
+ }[];
16
+ lookups: unknown[];
17
+ tagLists: unknown[];
18
+ extensions: {
19
+ exports: unknown[];
20
+ navGroups: unknown[];
21
+ documentTemplates: unknown[];
22
+ }[];
171
23
  }
172
- export interface CatalogExtensionExport {
173
- id: string;
174
- name: string;
175
- version: string;
176
- active: boolean;
177
- /**
178
- * Sempre escrita, mesmo vazia. É a lista PRESENTE e vazia que diz "esta extensão não exporta nada";
179
- * a chave ausente é o silêncio de um Console anterior a este campo, e o kernel distingue as duas.
180
- */
181
- exports: CatalogExtensionExportedArtifact[];
182
- /** Sempre escrita, mesmo vazia, pela mesma razão de `exports`. */
183
- navGroups: CatalogExtensionNavGroup[];
184
- /** Sempre escrita, mesmo vazia, pela mesma razão de `exports`. */
185
- documentTemplates: CatalogExtensionDocumentTemplate[];
186
- /** Sempre escrita, mesmo vazia, pela mesma razão de `exports`. */
187
- fieldPresets: CatalogExtensionFieldPreset[];
188
- }
189
- export interface CatalogExport {
190
- version: number;
191
- projectId: string;
192
- tenantId: string;
193
- tenantName: string;
194
- exportedAt: string;
195
- kernelVersion: string;
196
- consoleVersion: string;
197
- extensions: CatalogExtensionExport[];
198
- screens: CatalogScreenExport[];
199
- lookups: CatalogListExport[];
200
- tagLists: CatalogListExport[];
201
- }
202
- /** Um documento já decodificado, com o id que a coleção lhe deu. */
203
- export interface CatalogSourceDocument {
204
- id: string;
205
- data: Record<string, unknown>;
206
- }
207
- export interface CatalogExportInput {
24
+ interface CatalogWriterInput {
208
25
  projectId: string;
209
26
  tenant: TenantSummary;
210
27
  kernelVersion: string;
211
28
  exportedAt: Date;
29
+ exporter: {
30
+ tool: 'console';
31
+ version: string;
32
+ };
212
33
  screens: CatalogSourceDocument[];
213
34
  lookups: CatalogSourceDocument[];
214
35
  tagLists: CatalogSourceDocument[];
215
- extensions: InstalledExtensionRecord[];
36
+ extensions: CatalogSourceDocument[];
37
+ }
38
+ /** O que o Console usa do barril do `@microteia/sdk` instalado em `extensions/`. */
39
+ export interface CatalogWriter {
40
+ file: string;
41
+ build: (input: CatalogWriterInput) => WrittenCatalog;
42
+ serialize: (snapshot: WrittenCatalog) => string;
216
43
  }
217
44
  /**
218
- * O documento, montado a partir do que foi lido. É PURA de propósito: o que ela produz é o contrato com
219
- * o SDK, e um contrato que só pode ser exercitado com um projeto Firebase na frente é um contrato que
220
- * ninguém confere.
45
+ * O escritor do retrato, carregado do SDK que o escritório tem em `extensions/`.
46
+ *
47
+ * O Console trazia uma CÓPIA do escritor, e o leitor morava no SDK: os dois lados eram compilados
48
+ * separadamente e casados só por teste, e o Console é distribuído à parte do kernel. Carregar o
49
+ * escritor de onde mora o leitor faz o formato ter um dono só — e é a mesma função que a aplicação usa
50
+ * no botão Exportar catálogo da tela de Extensões.
221
51
  *
222
- * Uma tela DESLIGADA (`enabled: false`) fica de fora: a coleção dela pode até existir no banco, mas
223
- * nada da plataforma a desenha, e trazê-la faria o catálogo afirmar que a entidade existe. Uma tela de
224
- * extensão DESATIVADA continua: o documento dela segue gravado, e é ele que explica um campo que o
225
- * autor vai encontrar.
52
+ * O SDK de um kernel anterior a esse escritor não o tem, e aí o comando RECUSA dizendo como trazê-lo,
53
+ * em vez de escrever com uma cópia que já não é conferida contra nada.
226
54
  */
227
- export declare const buildCatalogExport: (input: CatalogExportInput) => CatalogExport;
55
+ export declare const loadCatalogWriter: (directory: string) => CatalogWriter;
228
56
  export declare const exportCatalog: (args: ParsedArgs) => Promise<void>;
57
+ export {};