@microteia/console 2.25.0 → 2.28.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.
@@ -22,6 +22,22 @@
22
22
  - fix(command): what was broken, and what it did to whoever hit it.
23
23
  -->
24
24
 
25
+ #### [2.28.0] — 2026-10-13
26
+
27
+ - feat(examples): the Console ships `business-days` as authoring source — a collection the kernel reads without knowing its name (`time.business.calendar`), and a code view that edits data no field type describes.
28
+
29
+ #### [2.27.0] — 2026-10-13
30
+
31
+ - feat(examples): the Console ships `document-example` as authoring source — a `document.template` contribution (Markdown, with `{{#each}}`/`{{#if}}` and a child-record `dataset`) over two screens addressed by `sourceId`.
32
+
33
+ #### [2.26.1] — 2026-10-12
34
+
35
+ - fix(connect): the "enable the Firestore TTL policy on auditLogs.expiresAt" prerequisite is gone from `connect` and from `MICROTEIA.md`. The policy is declared — `"ttl": true` in a `fieldOverride` of `firestore.indexes.json` — and the `firebase deploy --only firestore:indexes` that `deploy` runs applies it; the step asked the organization to redo by hand what the deploy had already done. The same holds for every TTL the kernel declares (`workflow_trigger_marks.expiresAt` too).
36
+
37
+ #### [2.26.0] — 2026-10-09
38
+
39
+ - feat(plugin-init): the organization's namespace is required — `--namespace com.acme`, or asked for when there is a terminal — validated before anything is written, and recorded in the seed `package.json` under `microteia.namespace`. `--force` reuses the one already recorded. The SDK derives every extension id in the directory from it.
40
+
25
41
  #### [2.25.0] — 2026-10-07
26
42
 
27
43
  - feat(examples): the Console ships `record-tab-example` as authoring source — a tab inside another screen's record, as code and as Widget DSL, and a list locked to the open record.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Microteia Console 2.25.0
1
+ # Microteia Console 2.28.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.
@@ -1,11 +1,12 @@
1
1
  import { access, mkdir, readFile, writeFile } from 'node:fs/promises';
2
2
  import { dirname, join, relative, resolve } from 'node:path';
3
+ import { createInterface } from 'node:readline/promises';
3
4
  import { flagValue, hasFlag } from '../shared/args.js';
4
5
  import { blank, bullets, heading, info, success, warn } from '../shared/ui.js';
5
6
  import { run } from '../shared/run.js';
6
7
  import { ENVIRONMENT_FILE, readEnvironment } from './environment.js';
7
8
  import { WORKSPACE_DIR } from './workspace.js';
8
- import { SDK_TARBALL, workspacePackageJson } from '../vendor/pluginWorkspaceTemplates.js';
9
+ import { SDK_TARBALL, isValidNamespace, workspacePackageJson } from '../vendor/pluginWorkspaceTemplates.js';
9
10
  /**
10
11
  * `microteia plugin-init` — prepara `extensions/`, onde o escritório escreve extensão em TypeScript.
11
12
  *
@@ -107,6 +108,45 @@ export const refreshAuthoringWorkspace = async (root, officeName) => {
107
108
  return false;
108
109
  }
109
110
  };
111
+ /** O que um `--force` anterior deixou gravado: reescrever a semente não é motivo para perguntar de novo. */
112
+ const existingNamespace = async (directory) => {
113
+ try {
114
+ const pkg = JSON.parse(await readFile(join(directory, 'package.json'), 'utf8'));
115
+ const value = pkg.microteia?.namespace;
116
+ return typeof value === 'string' && isValidNamespace(value) ? value : null;
117
+ }
118
+ catch {
119
+ return null;
120
+ }
121
+ };
122
+ /**
123
+ * O namespace é OBRIGATÓRIO, e não tem padrão: qualquer padrão seria um prefixo que não é do escritório,
124
+ * e o id que nasce dele é permanente. Sem terminal, só a flag responde.
125
+ */
126
+ const resolveNamespace = async (args, directory) => {
127
+ const given = flagValue(args, 'namespace') ?? await existingNamespace(directory) ?? await askNamespace();
128
+ const namespace = given.trim();
129
+ if (!isValidNamespace(namespace)) {
130
+ throw new Error(`"${namespace}" is not a valid namespace. Use reverse-DNS, lowercase, from a domain your organization`
131
+ + ' controls — for example: --namespace com.acme');
132
+ }
133
+ return namespace;
134
+ };
135
+ const askNamespace = async () => {
136
+ if (!process.stdin.isTTY) {
137
+ throw new Error('A namespace is required: every extension written in this directory is named below it, and its ids\n'
138
+ + 'are permanent. Pass it with --namespace, for example: microteia plugin-init --namespace com.acme');
139
+ }
140
+ info('Every extension written here is named below a namespace — the reverse-DNS of a domain your');
141
+ info('organization controls, like com.acme. Its ids are permanent, so choose one that is yours.');
142
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
143
+ try {
144
+ return (await rl.question('namespace: ')).trim();
145
+ }
146
+ finally {
147
+ rl.close();
148
+ }
149
+ };
110
150
  export const pluginInit = async (args) => {
111
151
  const file = resolve(flagValue(args, 'file') ?? join(process.cwd(), ENVIRONMENT_FILE));
112
152
  const root = dirname(file);
@@ -120,17 +160,19 @@ export const pluginInit = async (args) => {
120
160
  }
121
161
  const sdk = await locateSdk(root);
122
162
  const installDir = join(root, WORKSPACE_DIR, 'install');
163
+ const namespace = await resolveNamespace(args, directory);
123
164
  heading(`Extension authoring for "${environment.project.name}"`);
124
165
  await mkdir(directory, { recursive: true });
125
166
  // O caminho do tarball é relativo, para que o diretório continue válido se a instalação inteira for
126
167
  // movida — o que acontece assim que alguém a versiona num repositório do escritório.
127
- await writeFile(join(directory, 'package.json'), workspacePackageJson(environment.environment.id, relative(directory, sdk.path)));
128
- success('wrote the seed package.json');
168
+ await writeFile(join(directory, 'package.json'), workspacePackageJson(environment.environment.id, relative(directory, sdk.path), namespace));
169
+ success(`wrote the seed package.json (namespace ${namespace})`);
129
170
  blank();
130
171
  info('Installing @microteia/sdk and writing the guides...');
131
172
  await syncAuthoringWorkspace(directory, sdk.path, installDir, environment.project.name);
132
173
  blank();
133
174
  info(`Extension authoring ready in ${relative(root, directory)}/ (kernel contract ${sdk.kernelVersion}).`);
175
+ info(`Every extension created there is ${namespace}.<name> — recorded in package.json, under microteia.namespace.`);
134
176
  bullets([
135
177
  `cd ${relative(process.cwd(), directory) || '.'}`,
136
178
  'npm run create',
package/lib/index.js CHANGED
@@ -59,7 +59,7 @@ const COMMANDS = [
59
59
  },
60
60
  {
61
61
  name: 'plugin-init',
62
- usage: 'microteia plugin-init [--dir <path>] [--force]',
62
+ usage: 'microteia plugin-init --namespace <com.acme> [--dir <path>] [--force]',
63
63
  summary: 'Prepare "extensions/", to author extensions in TypeScript in your editor.',
64
64
  audience: 'office',
65
65
  execute: pluginInit,
@@ -91,13 +91,6 @@ export const MANUAL_PREREQUISITES = [
91
91
  + 'with the credential it was given and refuses everything else.',
92
92
  optional: true,
93
93
  },
94
- {
95
- id: 'audit-ttl',
96
- title: 'Enable the Firestore TTL policy on auditLogs.expiresAt',
97
- detail: 'Firestore console, TTL, collection auditLogs, field expiresAt. It cannot be declared in code or '
98
- + 'rules. Without it audit logs are never expired and grow without bound.',
99
- optional: false,
100
- },
101
94
  ];
102
95
  /**
103
96
  * Os nomes das Cloud Functions saem de `functions/src/index.ts`, a mesma fonte e o mesmo parse que
@@ -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.25.0";
90
+ export declare const CLI_VERSION = "2.28.0";
@@ -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.25.0';
97
+ export const CLI_VERSION = '2.28.0';
@@ -35,6 +35,7 @@ export const EXAMPLE_SOURCES = {
35
35
  agent: 'agent.ts',
36
36
  brasil: 'brasil.ts',
37
37
  calendar: 'calendar.ts',
38
+ 'business-days': 'businessDays.ts',
38
39
  dashboard: 'dashboard.ts',
39
40
  'locale-pt-br': 'localePtBr',
40
41
  news: 'news.ts',
@@ -50,6 +51,7 @@ export const EXAMPLE_SOURCES = {
50
51
  'command-example': 'examples/command-example',
51
52
  'settings-example': 'examples/settings-example',
52
53
  'record-tab-example': 'examples/record-tab-example',
54
+ 'document-example': 'examples/document-example',
53
55
  };
54
56
  /**
55
57
  * Os exemplos que existem SÓ para ser lidos — os que não estão no catálogo embutido.
@@ -16,4 +16,5 @@ export declare const SDK_TARBALL = "microteia-sdk.tgz";
16
16
  * duplicá-los aqui era exatamente o problema. O `sync` os acrescenta logo depois do `npm install`, e o
17
17
  * segundo `npm install` os instala.
18
18
  */
19
- export declare const workspacePackageJson: (officeName: string, sdkPath: string) => string;
19
+ export declare const workspacePackageJson: (officeName: string, sdkPath: string, namespace: string) => string;
20
+ export declare const isValidNamespace: (value: string) => boolean;
@@ -16,7 +16,7 @@ export const SDK_TARBALL = 'microteia-sdk.tgz';
16
16
  * duplicá-los aqui era exatamente o problema. O `sync` os acrescenta logo depois do `npm install`, e o
17
17
  * segundo `npm install` os instala.
18
18
  */
19
- export const workspacePackageJson = (officeName, sdkPath) => JSON.stringify({
19
+ export const workspacePackageJson = (officeName, sdkPath, namespace) => JSON.stringify({
20
20
  name: `${officeName}-extensions`,
21
21
  version: '0.0.0',
22
22
  private: true,
@@ -26,4 +26,14 @@ export const workspacePackageJson = (officeName, sdkPath) => JSON.stringify({
26
26
  // traz o SDK novo é `npm run sdk:refresh` — ou o `microteia unpack`, que o faz sozinho.
27
27
  '@microteia/sdk': `file:${sdkPath}`,
28
28
  },
29
+ // O prefixo de todo id escrito neste diretório. É o SDK que o lê (`microteia-plugin create`, o MCP);
30
+ // o Console só o recebe de quem roda o `plugin-init`, que é a única pessoa que pode decidi-lo.
31
+ microteia: { namespace },
29
32
  }, null, 2) + '\n';
33
+ /**
34
+ * O formato de id de extensão (`isValidPluginId`, no domínio) — repetido porque o Console não importa
35
+ * o kernel, e conferir ANTES de escrever a semente é o que evita um `--force` só para corrigir um erro
36
+ * de digitação. `pluginWorkspaceTemplates.test.ts` prende os dois um ao outro.
37
+ */
38
+ const NAMESPACE_PATTERN = /^[a-z][a-z0-9]*(\.[a-z][a-z0-9-]*)+$/;
39
+ export const isValidNamespace = (value) => NAMESPACE_PATTERN.test(value);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microteia/console",
3
- "version": "2.25.0",
3
+ "version": "2.28.0",
4
4
  "description": "Install and update Microteia in a Firebase project you own.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",