@microteia/console 2.31.1 → 2.33.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/CONSOLE-VERSIONS.md +8 -0
- package/README.md +1 -1
- package/lib/console/exportCatalog.d.ts +39 -0
- package/lib/console/exportCatalog.js +63 -0
- package/lib/shared/paths.d.ts +1 -1
- package/lib/shared/paths.js +1 -1
- package/package.json +1 -1
package/CONSOLE-VERSIONS.md
CHANGED
|
@@ -22,6 +22,14 @@
|
|
|
22
22
|
- fix(command): what was broken, and what it did to whoever hit it.
|
|
23
23
|
-->
|
|
24
24
|
|
|
25
|
+
#### [2.33.0] — 2026-10-28
|
|
26
|
+
|
|
27
|
+
- feat(export-catalog): each screen now carries `keys` — every record key of the collection as `name`, `kind`, `fields`, `scope`, `normalize` and `sparse`, plus `ownerPluginId` when an extension declared it — and each field carries `unique: true` when it is set. The conflict `message` and the deprecated `status` stay out: they do not change which reservation a record gets. Without this, the artifact plan proposed as new, on every run, a key the office declared in the Form Design or a third-party extension installed. Always written, even empty; a snapshot taken by an older Console is read as "keys unknown", and the authoring tools say so.
|
|
28
|
+
|
|
29
|
+
#### [2.32.0] — 2026-10-27
|
|
30
|
+
|
|
31
|
+
- feat(export-catalog): each installed extension now carries `fieldPresets` — every `data.field.preset` it declares, as its key and the parts it has (`mask`, `format`, `validate`), never the templates, formatting ops or validation rules. The authoring tools check a field's `mask`, `rules.format.preset` and `rules.validate.check` against it, so a key the dependency does not declare (`cpf-cnpj` for the Brasil extension's `cpfCnpj`) is refused by `check` instead of installing a field that silently has no mask. Always written, even empty; a snapshot taken by an older Console is read as "presets unknown".
|
|
32
|
+
|
|
25
33
|
#### [2.31.1] — 2026-10-26
|
|
26
34
|
|
|
27
35
|
- docs(kernel/manifest): the optional prerequisite `portal-identity-signing` now names a third feature that signs with the functions' runtime account: a portal screen declaring `documentLinks` hands guests V4 signed URLs, and without `iam.serviceAccounts.signBlob` the guest cannot open its documents (the denial shows in the log of `getPortalDocumentUrl`). `microteia grant-signing` grants the same permission for all three.
|
package/README.md
CHANGED
|
@@ -55,6 +55,29 @@ export interface CatalogFieldExport {
|
|
|
55
55
|
ownerPluginId?: string;
|
|
56
56
|
aggregation?: Record<string, unknown>;
|
|
57
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;
|
|
58
81
|
}
|
|
59
82
|
export interface CatalogScreenExport {
|
|
60
83
|
key: string;
|
|
@@ -74,6 +97,11 @@ export interface CatalogScreenExport {
|
|
|
74
97
|
parentRelation?: string;
|
|
75
98
|
tagCollection?: string;
|
|
76
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[];
|
|
77
105
|
fields: CatalogFieldExport[];
|
|
78
106
|
}
|
|
79
107
|
export interface CatalogListExport {
|
|
@@ -132,6 +160,15 @@ export interface CatalogExtensionDocumentTemplate {
|
|
|
132
160
|
source: 'record' | 'payload';
|
|
133
161
|
parameters: CatalogExtensionDocumentParameter[];
|
|
134
162
|
}
|
|
163
|
+
/**
|
|
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.
|
|
167
|
+
*/
|
|
168
|
+
export interface CatalogExtensionFieldPreset {
|
|
169
|
+
key: string;
|
|
170
|
+
slots: ('mask' | 'format' | 'validate')[];
|
|
171
|
+
}
|
|
135
172
|
export interface CatalogExtensionExport {
|
|
136
173
|
id: string;
|
|
137
174
|
name: string;
|
|
@@ -146,6 +183,8 @@ export interface CatalogExtensionExport {
|
|
|
146
183
|
navGroups: CatalogExtensionNavGroup[];
|
|
147
184
|
/** Sempre escrita, mesmo vazia, pela mesma razão de `exports`. */
|
|
148
185
|
documentTemplates: CatalogExtensionDocumentTemplate[];
|
|
186
|
+
/** Sempre escrita, mesmo vazia, pela mesma razão de `exports`. */
|
|
187
|
+
fieldPresets: CatalogExtensionFieldPreset[];
|
|
149
188
|
}
|
|
150
189
|
export interface CatalogExport {
|
|
151
190
|
version: number;
|
|
@@ -165,6 +165,33 @@ const documentTemplatesOf = (extension) => {
|
|
|
165
165
|
})
|
|
166
166
|
.sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0));
|
|
167
167
|
};
|
|
168
|
+
const FIELD_PRESET_POINT = 'data.field.preset';
|
|
169
|
+
/**
|
|
170
|
+
* Os presets de campo de uma cópia instalada. É a mesma leitura que `declaredFieldPresets` faz no
|
|
171
|
+
* kernel — chave e rótulo obrigatórios, e as partes pelo critério do editor de campo —, repetida porque
|
|
172
|
+
* o Console não alcança `src/`; o teste que atravessa a fronteira confere as duas.
|
|
173
|
+
*/
|
|
174
|
+
const fieldPresetsOf = (extension) => {
|
|
175
|
+
const contributes = Array.isArray(extension.manifest?.contributes) ? extension.manifest.contributes : [];
|
|
176
|
+
return contributes
|
|
177
|
+
.map(asRecord)
|
|
178
|
+
.filter((one) => one?.point === FIELD_PRESET_POINT)
|
|
179
|
+
.flatMap((one) => {
|
|
180
|
+
const metadata = asRecord(one.metadata);
|
|
181
|
+
const key = text(metadata?.key);
|
|
182
|
+
if (metadata === undefined || key === undefined || text(metadata.label) === undefined)
|
|
183
|
+
return [];
|
|
184
|
+
return [{
|
|
185
|
+
key,
|
|
186
|
+
slots: [
|
|
187
|
+
...(metadata.mask ? ['mask'] : []),
|
|
188
|
+
...(Array.isArray(metadata.format) && metadata.format.length > 0 ? ['format'] : []),
|
|
189
|
+
...(metadata.validate ? ['validate'] : []),
|
|
190
|
+
],
|
|
191
|
+
}];
|
|
192
|
+
})
|
|
193
|
+
.sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0));
|
|
194
|
+
};
|
|
168
195
|
/**
|
|
169
196
|
* A mesma dedução que a aplicação faz ao ler a tela (`screenDefinitionMapper.ts`): o `origin` gravado
|
|
170
197
|
* quando ele existe, senão `plugin` para quem tem dono e `design` para o que o escritório criou. Ela é
|
|
@@ -200,6 +227,38 @@ const fieldExport = (raw, index, screenOrigin) => {
|
|
|
200
227
|
...(text(field.ownerPluginId) === undefined ? {} : { ownerPluginId: text(field.ownerPluginId) }),
|
|
201
228
|
...(asRecord(field.aggregation) === undefined ? {} : { aggregation: asRecord(field.aggregation) }),
|
|
202
229
|
...(compute === undefined ? {} : { compute }),
|
|
230
|
+
...(field.unique === true ? { unique: true } : {}),
|
|
231
|
+
};
|
|
232
|
+
};
|
|
233
|
+
const normalizeExport = (raw) => {
|
|
234
|
+
const normalize = asRecord(raw);
|
|
235
|
+
if (normalize === undefined)
|
|
236
|
+
return undefined;
|
|
237
|
+
const formatPreset = text(normalize.formatPreset);
|
|
238
|
+
const picked = {
|
|
239
|
+
...(typeof normalize.trim === 'boolean' ? { trim: normalize.trim } : {}),
|
|
240
|
+
...(typeof normalize.caseInsensitive === 'boolean' ? { caseInsensitive: normalize.caseInsensitive } : {}),
|
|
241
|
+
...(formatPreset === undefined ? {} : { formatPreset }),
|
|
242
|
+
};
|
|
243
|
+
return Object.keys(picked).length > 0 ? picked : undefined;
|
|
244
|
+
};
|
|
245
|
+
/** A chave como o kernel a grava, sem o que é apresentação. Sem nome ou sem campo, não é chave. */
|
|
246
|
+
const keyExport = (raw) => {
|
|
247
|
+
const key = asRecord(raw);
|
|
248
|
+
const name = text(key?.name);
|
|
249
|
+
const kind = text(key?.kind);
|
|
250
|
+
const fields = (Array.isArray(key?.fields) ? key.fields : []).filter((one) => typeof one === 'string' && one !== '');
|
|
251
|
+
if (key === undefined || name === undefined || kind === undefined || fields.length === 0)
|
|
252
|
+
return null;
|
|
253
|
+
const normalize = normalizeExport(key.normalize);
|
|
254
|
+
return {
|
|
255
|
+
name,
|
|
256
|
+
kind,
|
|
257
|
+
fields,
|
|
258
|
+
...(text(key.scope) === undefined ? {} : { scope: text(key.scope) }),
|
|
259
|
+
...(normalize === undefined ? {} : { normalize }),
|
|
260
|
+
...(typeof key.sparse === 'boolean' ? { sparse: key.sparse } : {}),
|
|
261
|
+
...(text(key.ownerPluginId) === undefined ? {} : { ownerPluginId: text(key.ownerPluginId) }),
|
|
203
262
|
};
|
|
204
263
|
};
|
|
205
264
|
const screenExport = (document) => {
|
|
@@ -220,6 +279,9 @@ const screenExport = (document) => {
|
|
|
220
279
|
...(text(data.parentRelation) === undefined ? {} : { parentRelation: text(data.parentRelation) }),
|
|
221
280
|
...(text(data.tagCollection) === undefined ? {} : { tagCollection: text(data.tagCollection) }),
|
|
222
281
|
...(text(data.eventPrefix) === undefined ? {} : { eventPrefix: text(data.eventPrefix) }),
|
|
282
|
+
keys: (Array.isArray(data.keys) ? data.keys : [])
|
|
283
|
+
.map(keyExport)
|
|
284
|
+
.filter((key) => key !== null),
|
|
223
285
|
fields: fields
|
|
224
286
|
.map((field, index) => fieldExport(field, index, origin))
|
|
225
287
|
.filter((field) => field !== null),
|
|
@@ -263,6 +325,7 @@ export const buildCatalogExport = (input) => ({
|
|
|
263
325
|
exports: exportedArtifacts(one),
|
|
264
326
|
navGroups: navGroupsOf(one),
|
|
265
327
|
documentTemplates: documentTemplatesOf(one),
|
|
328
|
+
fieldPresets: fieldPresetsOf(one),
|
|
266
329
|
}))
|
|
267
330
|
.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)),
|
|
268
331
|
screens: input.screens
|
package/lib/shared/paths.d.ts
CHANGED
|
@@ -87,4 +87,4 @@ export declare const consoleRoot: string;
|
|
|
87
87
|
* de código nunca chegou a quem instala. Este bloco fica sendo o que ele já era antes de crescer: a
|
|
88
88
|
* política de semver da ferramenta, mais o registro do que motivou as versões até a `2.5.0`.
|
|
89
89
|
*/
|
|
90
|
-
export declare const CLI_VERSION = "2.
|
|
90
|
+
export declare const CLI_VERSION = "2.33.0";
|
package/lib/shared/paths.js
CHANGED
|
@@ -94,4 +94,4 @@ export const consoleRoot = resolve(here, '..', '..');
|
|
|
94
94
|
* de código nunca chegou a quem instala. Este bloco fica sendo o que ele já era antes de crescer: a
|
|
95
95
|
* política de semver da ferramenta, mais o registro do que motivou as versões até a `2.5.0`.
|
|
96
96
|
*/
|
|
97
|
-
export const CLI_VERSION = '2.
|
|
97
|
+
export const CLI_VERSION = '2.33.0';
|