@microteia/console 2.7.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 +85 -0
- package/README.md +64 -0
- package/lib/console/backendEnv.d.ts +52 -0
- package/lib/console/backendEnv.js +118 -0
- package/lib/console/backendIndexes.d.ts +40 -0
- package/lib/console/backendIndexes.js +178 -0
- package/lib/console/backendParameters.d.ts +48 -0
- package/lib/console/backendParameters.js +100 -0
- package/lib/console/callableAccess.d.ts +39 -0
- package/lib/console/callableAccess.js +61 -0
- package/lib/console/channel.d.ts +49 -0
- package/lib/console/channel.js +145 -0
- package/lib/console/connect.d.ts +2 -0
- package/lib/console/connect.js +74 -0
- package/lib/console/customFunctions.d.ts +120 -0
- package/lib/console/customFunctions.js +298 -0
- package/lib/console/deploy.d.ts +2 -0
- package/lib/console/deploy.js +261 -0
- package/lib/console/deployFunctions.d.ts +45 -0
- package/lib/console/deployFunctions.js +204 -0
- package/lib/console/environment.d.ts +62 -0
- package/lib/console/environment.js +151 -0
- package/lib/console/firebaseCli.d.ts +15 -0
- package/lib/console/firebaseCli.js +90 -0
- package/lib/console/firestoreRest.d.ts +39 -0
- package/lib/console/firestoreRest.js +131 -0
- package/lib/console/hostingCheck.d.ts +29 -0
- package/lib/console/hostingCheck.js +44 -0
- package/lib/console/init.d.ts +2 -0
- package/lib/console/init.js +60 -0
- package/lib/console/pluginWorkspace.d.ts +48 -0
- package/lib/console/pluginWorkspace.js +148 -0
- package/lib/console/provision.d.ts +70 -0
- package/lib/console/provision.js +478 -0
- package/lib/console/regionOrphans.d.ts +43 -0
- package/lib/console/regionOrphans.js +99 -0
- package/lib/console/runtimeConfigShape.d.ts +25 -0
- package/lib/console/runtimeConfigShape.js +35 -0
- package/lib/console/unpack.d.ts +9 -0
- package/lib/console/unpack.js +333 -0
- package/lib/console/versions.d.ts +10 -0
- package/lib/console/versions.js +72 -0
- package/lib/console/workspace.d.ts +19 -0
- package/lib/console/workspace.js +33 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +136 -0
- package/lib/kernel/backendShape.d.ts +74 -0
- package/lib/kernel/backendShape.js +91 -0
- package/lib/kernel/channel.d.ts +79 -0
- package/lib/kernel/channel.js +157 -0
- package/lib/kernel/manifest.d.ts +138 -0
- package/lib/kernel/manifest.js +145 -0
- package/lib/shared/args.d.ts +14 -0
- package/lib/shared/args.js +44 -0
- package/lib/shared/digest.d.ts +30 -0
- package/lib/shared/digest.js +34 -0
- package/lib/shared/paths.d.ts +90 -0
- package/lib/shared/paths.js +97 -0
- package/lib/shared/run.d.ts +28 -0
- package/lib/shared/run.js +49 -0
- package/lib/shared/ui.d.ts +23 -0
- package/lib/shared/ui.js +43 -0
- package/lib/vendor/authoringGuide.d.ts +19 -0
- package/lib/vendor/authoringGuide.js +284 -0
- package/lib/vendor/bundleAudit.d.ts +19 -0
- package/lib/vendor/bundleAudit.js +95 -0
- package/lib/vendor/changelogs.d.ts +62 -0
- package/lib/vendor/changelogs.js +131 -0
- package/lib/vendor/exampleSources.d.ts +29 -0
- package/lib/vendor/exampleSources.js +126 -0
- package/lib/vendor/installationDocs.d.ts +35 -0
- package/lib/vendor/installationDocs.js +243 -0
- package/lib/vendor/pack.d.ts +2 -0
- package/lib/vendor/pack.js +276 -0
- package/lib/vendor/packConsole.d.ts +13 -0
- package/lib/vendor/packConsole.js +63 -0
- package/lib/vendor/packageTemplates.d.ts +66 -0
- package/lib/vendor/packageTemplates.js +200 -0
- package/lib/vendor/pluginWorkspaceTemplates.d.ts +19 -0
- package/lib/vendor/pluginWorkspaceTemplates.js +29 -0
- package/lib/vendor/publish.d.ts +2 -0
- package/lib/vendor/publish.js +277 -0
- package/lib/vendor/repository.d.ts +9 -0
- package/lib/vendor/repository.js +26 -0
- package/package.json +21 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { mkdir, readFile, readdir, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* As extensões de primeira parte, empacotadas como FONTE — `plugin.ts` e as views em `.tsx` —, uma pasta
|
|
5
|
+
* por extensão.
|
|
6
|
+
*
|
|
7
|
+
* Viajavam como `.json`. Um `.json` de 90 KB não ensina nada: é o artefato, e quem abre não vê como se
|
|
8
|
+
* chega nele. O que ensina é a fonte, e ela já viaja de qualquer forma — o código das views está dentro
|
|
9
|
+
* do `.json`, em string. Passar a mandar o `.ts` não expõe nada novo; expõe de forma legível o que já ia.
|
|
10
|
+
*
|
|
11
|
+
* A única transformação é de IMPORT: neste repositório um plugin importa `../../domain/entities/...` e
|
|
12
|
+
* `../contributionBuilders`; do lado do escritório, tudo isso é `@microteia/sdk`. Todo símbolo usado
|
|
13
|
+
* pelos doze está no barril do SDK — e é por isso que a conferência é possível: `exampleSources.test.ts`
|
|
14
|
+
* COMPILA cada exemplo reescrito e exige que o `.json` saia byte a byte igual ao versionado. Um símbolo
|
|
15
|
+
* que faltasse no barril reprovaria ali, e não no escritório de alguém.
|
|
16
|
+
*/
|
|
17
|
+
/** Arquivo distribuído → módulo de origem. O nome da pasta do exemplo é o do `.json`, sem extensão. */
|
|
18
|
+
export const EXAMPLE_SOURCES = {
|
|
19
|
+
brasil: 'brasil.ts',
|
|
20
|
+
calendar: 'calendar.ts',
|
|
21
|
+
dashboard: 'dashboard.ts',
|
|
22
|
+
'locale-pt-br': 'localePtBr',
|
|
23
|
+
news: 'news.ts',
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Reescreve os imports de um plugin para a forma que o escritório usa.
|
|
27
|
+
*
|
|
28
|
+
* Um especificador relativo que alcance `domain/` ou os construtores de contribuição é, do lado de fora,
|
|
29
|
+
* `@microteia/sdk`. As views passam a ser vizinhas do `plugin.ts` (`./views/X`), porque no exemplo cada
|
|
30
|
+
* extensão tem a sua pasta — aqui elas moram num diretório comum, com um nível a mais.
|
|
31
|
+
*/
|
|
32
|
+
export const rewriteImports = (source) => source.replace(/from '(\.[^']*)'/g, (match, specifier) => {
|
|
33
|
+
if (/(^|\/)contributionBuilders$/.test(specifier) || specifier.includes('domain/')) {
|
|
34
|
+
return "from '@microteia/sdk'";
|
|
35
|
+
}
|
|
36
|
+
// `./views/<plugin>/Nome.tsx?raw` -> `./views/Nome.tsx?raw`
|
|
37
|
+
const view = /^\.\/views\/[^/]+\/(.+)$/.exec(specifier);
|
|
38
|
+
return view === null ? match : `from './views/${view[1]}'`;
|
|
39
|
+
});
|
|
40
|
+
const HEADER = (id) => `// Example: a Microteia first-party extension, as SOURCE.
|
|
41
|
+
//
|
|
42
|
+
// This is the same code that produces ${id}.json — the artifact the platform installs. Build it with:
|
|
43
|
+
//
|
|
44
|
+
// npx microteia-plugin build ${id}/plugin.ts
|
|
45
|
+
//
|
|
46
|
+
// The only difference from how it is written inside Microteia is the import path: there it reaches into
|
|
47
|
+
// the kernel tree, here everything comes from '@microteia/sdk'.
|
|
48
|
+
|
|
49
|
+
`;
|
|
50
|
+
/**
|
|
51
|
+
* Os módulos VIZINHOS que o plugin importa — hoje, os catálogos de mensagem (`./newsMessages`,
|
|
52
|
+
* `./newsMessagesPtBr`). Um exemplo tem de viajar com o que ele importa, ou o `plugin.ts` staged não
|
|
53
|
+
* compila; enumerar os arquivos à mão numa lista aqui seria a mesma dívida que a lista de views era.
|
|
54
|
+
*
|
|
55
|
+
* A busca é transitiva porque um catálogo importa o outro (o de tradução lê o namespace do de origem),
|
|
56
|
+
* e é limitada ao diretório do marketplace: o que alcança `domain/` já virou `@microteia/sdk`.
|
|
57
|
+
*/
|
|
58
|
+
const siblingModules = async (marketplace, entry) => {
|
|
59
|
+
const collected = new Map();
|
|
60
|
+
const pending = [entry];
|
|
61
|
+
while (pending.length > 0) {
|
|
62
|
+
const name = pending.shift();
|
|
63
|
+
const source = await readFile(join(marketplace, name), 'utf8');
|
|
64
|
+
for (const match of source.matchAll(/from '\.\/([A-Za-z][\w.-]*)'/g)) {
|
|
65
|
+
const file = `${match[1]}.ts`;
|
|
66
|
+
if (collected.has(file))
|
|
67
|
+
continue;
|
|
68
|
+
try {
|
|
69
|
+
collected.set(file, rewriteImports(await readFile(join(marketplace, file), 'utf8')));
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
pending.push(file);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return collected;
|
|
78
|
+
};
|
|
79
|
+
const viewFiles = async (viewsDir) => {
|
|
80
|
+
try {
|
|
81
|
+
return (await readdir(viewsDir)).filter((name) => name.endsWith('.tsx') || name.endsWith('.ts'));
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Escreve `<target>/<plugin>/plugin.ts` e `<target>/<plugin>/views/*` para cada exemplo. Devolve os
|
|
89
|
+
* nomes escritos, que é o que o `pack` reporta.
|
|
90
|
+
*/
|
|
91
|
+
export const stageExamples = async (repo, target) => {
|
|
92
|
+
const marketplace = join(repo, 'src/plugins/marketplace');
|
|
93
|
+
const written = [];
|
|
94
|
+
for (const [id, origin] of Object.entries(EXAMPLE_SOURCES)) {
|
|
95
|
+
const directory = join(target, id);
|
|
96
|
+
await mkdir(directory, { recursive: true });
|
|
97
|
+
if (origin.endsWith('.ts')) {
|
|
98
|
+
const source = await readFile(join(marketplace, origin), 'utf8');
|
|
99
|
+
await writeFile(join(directory, 'plugin.ts'), HEADER(id) + rewriteImports(source));
|
|
100
|
+
for (const [name, content] of await siblingModules(marketplace, origin)) {
|
|
101
|
+
await writeFile(join(directory, name), content);
|
|
102
|
+
}
|
|
103
|
+
const plugin = origin.replace(/\.ts$/, '');
|
|
104
|
+
const views = join(marketplace, 'views', plugin);
|
|
105
|
+
const files = await viewFiles(views);
|
|
106
|
+
if (files.length > 0)
|
|
107
|
+
await mkdir(join(directory, 'views'), { recursive: true });
|
|
108
|
+
for (const name of files) {
|
|
109
|
+
await writeFile(join(directory, 'views', name), await readFile(join(views, name), 'utf8'));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
// Extensão que é um DIRETÓRIO (o pacote de idioma, com um arquivo por namespace). O `index.ts`
|
|
114
|
+
// vira `plugin.ts`; os demais são vizinhos dele e os imports relativos seguem valendo.
|
|
115
|
+
for (const name of await readdir(join(marketplace, origin))) {
|
|
116
|
+
if (!name.endsWith('.ts') || name.endsWith('.test.ts'))
|
|
117
|
+
continue;
|
|
118
|
+
const source = rewriteImports(await readFile(join(marketplace, origin, name), 'utf8'));
|
|
119
|
+
const isEntry = name === 'index.ts';
|
|
120
|
+
await writeFile(join(directory, isEntry ? 'plugin.ts' : name), isEntry ? HEADER(id) + source : source);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
written.push(id);
|
|
124
|
+
}
|
|
125
|
+
return written.sort();
|
|
126
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { KernelManifest } from '../kernel/manifest.js';
|
|
2
|
+
/**
|
|
3
|
+
* Os dois documentos que ficam ao lado do `microteia.yaml`.
|
|
4
|
+
*
|
|
5
|
+
* Eram um `README.md` DENTRO do pacote, em `.microteia/install/`. Estava no lugar errado por duas razões:
|
|
6
|
+
* ninguém abre um diretório que se anuncia como descartável para ler documentação, e o que ele explicava
|
|
7
|
+
* — onde escrever function, onde ficam as configurações — apontava para caminhos de dentro dele mesmo,
|
|
8
|
+
* que é justamente o que não se deve editar.
|
|
9
|
+
*
|
|
10
|
+
* A divisão entre os dois é sobre QUEM ESCREVE:
|
|
11
|
+
*
|
|
12
|
+
* - `README.md` é do escritório. Nasce se não existir e nunca é reescrito — ele vai renomear coisas,
|
|
13
|
+
* acrescentar o contexto do negócio dele, apagar o que não usa. Por isso só contém o que não muda.
|
|
14
|
+
* - `MICROTEIA.md` é nosso. É SOBREPOSTO a cada `unpack`, e é onde vai o detalhe que acompanha a versão:
|
|
15
|
+
* a lista de comandos, os números do pacote, os caminhos. Um documento que promete estar atualizado
|
|
16
|
+
* não pode ser editável, e um documento que a pessoa edita não pode prometer isso.
|
|
17
|
+
*
|
|
18
|
+
* Ao lado deles ficam os dois históricos técnicos, que seguem a política do `MICROTEIA.md` e moram em
|
|
19
|
+
* `changelogs.ts` — cada um viaja no artefato que descreve, e não neste módulo.
|
|
20
|
+
*/
|
|
21
|
+
export declare const INSTALLATION_README = "README.md";
|
|
22
|
+
export declare const INSTALLATION_GUIDE = "MICROTEIA.md";
|
|
23
|
+
/**
|
|
24
|
+
* O README do escritório. Escrito UMA vez, e daí em diante é dele.
|
|
25
|
+
*
|
|
26
|
+
* Deliberadamente sem número de versão, sem contagem de function e sem nada que envelheça: o que
|
|
27
|
+
* envelhece vive no `MICROTEIA.md`, que é reescrito. Um README com um dado velho ensina a desconfiar do
|
|
28
|
+
* arquivo inteiro.
|
|
29
|
+
*/
|
|
30
|
+
export declare const installationReadme: (officeName: string) => string;
|
|
31
|
+
/**
|
|
32
|
+
* O documento técnico. SOBREPOSTO a cada `unpack`, e por isso é onde o detalhe que acompanha a versão
|
|
33
|
+
* pode viver — carimbado com os números do pacote que está instalado, não com uma cópia deles.
|
|
34
|
+
*/
|
|
35
|
+
export declare const installationGuide: (officeName: string, manifest: KernelManifest, examples: string[]) => string;
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Os dois documentos que ficam ao lado do `microteia.yaml`.
|
|
3
|
+
*
|
|
4
|
+
* Eram um `README.md` DENTRO do pacote, em `.microteia/install/`. Estava no lugar errado por duas razões:
|
|
5
|
+
* ninguém abre um diretório que se anuncia como descartável para ler documentação, e o que ele explicava
|
|
6
|
+
* — onde escrever function, onde ficam as configurações — apontava para caminhos de dentro dele mesmo,
|
|
7
|
+
* que é justamente o que não se deve editar.
|
|
8
|
+
*
|
|
9
|
+
* A divisão entre os dois é sobre QUEM ESCREVE:
|
|
10
|
+
*
|
|
11
|
+
* - `README.md` é do escritório. Nasce se não existir e nunca é reescrito — ele vai renomear coisas,
|
|
12
|
+
* acrescentar o contexto do negócio dele, apagar o que não usa. Por isso só contém o que não muda.
|
|
13
|
+
* - `MICROTEIA.md` é nosso. É SOBREPOSTO a cada `unpack`, e é onde vai o detalhe que acompanha a versão:
|
|
14
|
+
* a lista de comandos, os números do pacote, os caminhos. Um documento que promete estar atualizado
|
|
15
|
+
* não pode ser editável, e um documento que a pessoa edita não pode prometer isso.
|
|
16
|
+
*
|
|
17
|
+
* Ao lado deles ficam os dois históricos técnicos, que seguem a política do `MICROTEIA.md` e moram em
|
|
18
|
+
* `changelogs.ts` — cada um viaja no artefato que descreve, e não neste módulo.
|
|
19
|
+
*/
|
|
20
|
+
export const INSTALLATION_README = 'README.md';
|
|
21
|
+
export const INSTALLATION_GUIDE = 'MICROTEIA.md';
|
|
22
|
+
/**
|
|
23
|
+
* O README do escritório. Escrito UMA vez, e daí em diante é dele.
|
|
24
|
+
*
|
|
25
|
+
* Deliberadamente sem número de versão, sem contagem de function e sem nada que envelheça: o que
|
|
26
|
+
* envelhece vive no `MICROTEIA.md`, que é reescrito. Um README com um dado velho ensina a desconfiar do
|
|
27
|
+
* arquivo inteiro.
|
|
28
|
+
*/
|
|
29
|
+
export const installationReadme = (officeName) => `# ${officeName}
|
|
30
|
+
|
|
31
|
+
This directory is a **Microteia** installation: a multi-tenant business platform where the applications
|
|
32
|
+
are built by *declaration* — screens, automations, actions and pages arrive as extensions, and the
|
|
33
|
+
platform itself is never modified.
|
|
34
|
+
|
|
35
|
+
It is yours. Rename it, describe your business in it, delete what you do not use. Nothing rewrites this
|
|
36
|
+
file. The reference that has to stay current lives in **[MICROTEIA.md](./MICROTEIA.md)**, which is
|
|
37
|
+
replaced on every \`microteia unpack\` — read that one for commands, paths and version detail. Beside it,
|
|
38
|
+
**[KERNEL-VERSIONS.md](./KERNEL-VERSIONS.md)** and **[CONSOLE-VERSIONS.md](./CONSOLE-VERSIONS.md)** say
|
|
39
|
+
what changed in each version of the platform and of the Console.
|
|
40
|
+
|
|
41
|
+
## The shape of this directory
|
|
42
|
+
|
|
43
|
+
microteia.yaml this installation: name, Firebase project, region, kernel version
|
|
44
|
+
.env.<projectId> your backend settings. Credentials — gitignored
|
|
45
|
+
KERNEL-VERSIONS.md what changed in each version of the platform
|
|
46
|
+
CONSOLE-VERSIONS.md what changed in each version of the Console that installs it
|
|
47
|
+
extensions/ the extensions you write
|
|
48
|
+
functions/ Cloud Functions shared by more than one extension
|
|
49
|
+
.microteia/ the unpacked platform. DISPOSABLE — nothing of yours lives here
|
|
50
|
+
|
|
51
|
+
The line that explains the rest: **nothing you write lives in \`.microteia/\`.** That directory is
|
|
52
|
+
replaced whole on every upgrade. Everything of yours sits beside this file.
|
|
53
|
+
|
|
54
|
+
## Day to day
|
|
55
|
+
|
|
56
|
+
microteia unpack --version latest # take the newest published platform version
|
|
57
|
+
microteia deploy # publish it — or --only hosting, --only functions
|
|
58
|
+
microteia provision # apply what installed extensions need
|
|
59
|
+
|
|
60
|
+
\`unpack --version\` reads the published catalog, downloads the version and checks its sha256 before
|
|
61
|
+
touching what is installed here. On a machine that cannot reach the catalog, name a file instead:
|
|
62
|
+
\`microteia unpack --kernel <package>.tgz\`. And \`microteia --version\` says when a newer one is out.
|
|
63
|
+
|
|
64
|
+
## Building your own extensions
|
|
65
|
+
|
|
66
|
+
An extension is a declaration the platform installs — a screen with its fields, an automation, a page.
|
|
67
|
+
You write it in TypeScript and a compiler produces the \`.json\` that gets installed:
|
|
68
|
+
|
|
69
|
+
cd extensions
|
|
70
|
+
npx microteia-plugin create # guided: asks what the extension should carry
|
|
71
|
+
npx microteia-plugin check <folder>/plugin.ts
|
|
72
|
+
npx microteia-plugin build <folder>/plugin.ts
|
|
73
|
+
|
|
74
|
+
\`extensions/examples/\` holds every extension that ships with Microteia, in source form — the best
|
|
75
|
+
reference there is. \`extensions/AUTHORING.md\` is the contract: extension points, the API a page may
|
|
76
|
+
use, translation rules, size limits.
|
|
77
|
+
|
|
78
|
+
## Working with an AI assistant
|
|
79
|
+
|
|
80
|
+
\`extensions/\` registers an MCP server (\`.mcp.json\`), so an assistant in your editor can ask **this**
|
|
81
|
+
installation what it offers — the extension points it has, what a page may import, and whether what it
|
|
82
|
+
just wrote would be accepted — instead of guessing from documentation. \`extensions/AGENTS.md\` holds the
|
|
83
|
+
rules it should follow.
|
|
84
|
+
|
|
85
|
+
The point of that is not novelty: it is that the assistant's answer can be *checked* before anything is
|
|
86
|
+
installed, by the same validation the platform runs.
|
|
87
|
+
`;
|
|
88
|
+
const commandRows = () => [
|
|
89
|
+
'| Command | What it does |',
|
|
90
|
+
'|---|---|',
|
|
91
|
+
'| `microteia unpack --version <latest\\|x.y.z>` | Downloads a platform version, verifies it, and puts it in place. Touches no Firebase — safe any time |',
|
|
92
|
+
'| `microteia unpack --kernel <pkg>.tgz` | The same, from a file you already have — for a machine with no access to the catalog |',
|
|
93
|
+
'| `microteia deploy [--only …] [--prune]` | Publishes what is in place: `rules`, `indexes`, `functions`, `hosting`. `--prune` also deletes functions left behind in a region this version no longer uses |',
|
|
94
|
+
'| `microteia provision [--prune]` | Gives installed extensions the functions and indexes they ask for |',
|
|
95
|
+
'| `microteia connect --project <id>` | Binds this installation to a Firebase project you own |',
|
|
96
|
+
'| `microteia plugin-init` | Prepares `extensions/` for authoring in TypeScript |',
|
|
97
|
+
].join('\n');
|
|
98
|
+
/**
|
|
99
|
+
* O documento técnico. SOBREPOSTO a cada `unpack`, e por isso é onde o detalhe que acompanha a versão
|
|
100
|
+
* pode viver — carimbado com os números do pacote que está instalado, não com uma cópia deles.
|
|
101
|
+
*/
|
|
102
|
+
export const installationGuide = (officeName, manifest, examples) => {
|
|
103
|
+
const baseline = (manifest.baselineFunctions ?? manifest.functions).length;
|
|
104
|
+
const claimed = manifest.functions.length - baseline;
|
|
105
|
+
return `# Microteia — installation reference
|
|
106
|
+
|
|
107
|
+
> Generated by \`microteia unpack\`. **Replaced on every unpack** — do not edit it; put your own notes in
|
|
108
|
+
> [README.md](./README.md), which is never rewritten.
|
|
109
|
+
|
|
110
|
+
| | |
|
|
111
|
+
|---|---|
|
|
112
|
+
| Installation | ${officeName} |
|
|
113
|
+
| Platform version | ${manifest.version} |
|
|
114
|
+
| Extension contract | ${manifest.kernelVersion} |
|
|
115
|
+
| Console required | ${manifest.minimumCliVersion} or newer |
|
|
116
|
+
| Functions region | ${manifest.requires.functionsRegion} |
|
|
117
|
+
| Cloud Functions | ${baseline} baseline, ${claimed} claimed by extensions |
|
|
118
|
+
${manifest.sdk === undefined ? '' : `| Authoring SDK | ${manifest.sdk.version} |\n`}
|
|
119
|
+
## 1. What lives where, and why
|
|
120
|
+
|
|
121
|
+
microteia.yaml the installation record. Written by the commands; readable by you
|
|
122
|
+
.env.<projectId> YOUR backend settings — credentials
|
|
123
|
+
.gitignore covers the two above
|
|
124
|
+
README.md yours, never rewritten
|
|
125
|
+
MICROTEIA.md this file, replaced on every unpack
|
|
126
|
+
KERNEL-VERSIONS.md the platform's technical history, replaced on every unpack
|
|
127
|
+
CONSOLE-VERSIONS.md the Console's own, replaced on every unpack
|
|
128
|
+
extensions/ the extensions you write
|
|
129
|
+
<plugin>/plugin.ts the source of one extension
|
|
130
|
+
<plugin>/views/*.tsx its pages, as real type-checked files
|
|
131
|
+
<plugin>/functions/ its own Cloud Functions
|
|
132
|
+
<plugin>/<id>.json the built artifact — generated, do not hand-edit
|
|
133
|
+
examples/ every first-party extension, in source form
|
|
134
|
+
AUTHORING.md the authoring contract
|
|
135
|
+
functions/ Cloud Functions shared by more than one extension
|
|
136
|
+
.microteia/ DISPOSABLE
|
|
137
|
+
install/ the unpacked platform. Replaced whole on every unpack
|
|
138
|
+
firebase-config.json resolved from your Firebase project by \`connect\`
|
|
139
|
+
|
|
140
|
+
**Nothing you write belongs in \`.microteia/\`.** \`unpack\` deletes that directory and replaces it; only
|
|
141
|
+
\`functions/node_modules\` survives, and only because it is a cache. This is not a convention to respect —
|
|
142
|
+
it is what makes an upgrade trustworthy, and the reason your files are elsewhere.
|
|
143
|
+
|
|
144
|
+
## 2. Commands
|
|
145
|
+
|
|
146
|
+
${commandRows()}
|
|
147
|
+
|
|
148
|
+
\`unpack\` and \`deploy\` are separate so that taking a new version does not oblige you to publish
|
|
149
|
+
everything, and so that there is a moment between the two to fill in a setting the new version asks for.
|
|
150
|
+
|
|
151
|
+
Between the two, \`microteia.yaml\` says both: \`kernel.version\` is what is in place, \`kernel.deployed\`
|
|
152
|
+
is what the project runs. \`microteia --version\` prints them next to the Console's own. What changed between
|
|
153
|
+
two versions is in \`KERNEL-VERSIONS.md\` and \`CONSOLE-VERSIONS.md\`. Each is written by the artifact it
|
|
154
|
+
describes — the first travels inside the package, the second with the Console — which is what keeps
|
|
155
|
+
either from describing a version that is not installed here. Both are replaced on every unpack, so put
|
|
156
|
+
nothing of yours in them.
|
|
157
|
+
|
|
158
|
+
\`unpack\` replaces \`.microteia/install/\` whole, and a package carries only the platform's own composite
|
|
159
|
+
indexes — an index an extension asks for is not in it, so that it does not travel to installations that
|
|
160
|
+
never install that extension. So \`unpack\` puts back what this installation had already declared, from
|
|
161
|
+
\`backend.indexes\` in \`microteia.yaml\`. Without that, publishing indexes after an upgrade would find
|
|
162
|
+
indexes in the project that the file no longer declares, and **offer to delete them** — for extensions
|
|
163
|
+
that are merely turned off, and whose indexes are the reason their screens are fast.
|
|
164
|
+
|
|
165
|
+
An index of an extension you removed stays declared and is reported on every \`provision\`. It costs only
|
|
166
|
+
writes on the collection it covers. \`microteia provision --prune\` lets it go: it drops out of this
|
|
167
|
+
installation, and the next \`microteia deploy --only indexes\` offers to delete it from the project —
|
|
168
|
+
deliberately, because you asked.
|
|
169
|
+
|
|
170
|
+
## 3. Backend settings
|
|
171
|
+
|
|
172
|
+
They live in \`.env.<projectId>\`, next to \`microteia.yaml\`. \`unpack\` creates the file and adds any
|
|
173
|
+
setting a new version introduces, **never overwriting what you filled in**.
|
|
174
|
+
|
|
175
|
+
After changing a value, run \`microteia provision\`. The value is stamped into each Cloud Function at
|
|
176
|
+
*its* deploy, so a function already running keeps the previous one until it is deployed again; provision
|
|
177
|
+
notices the change (by a salted digest in \`microteia.yaml\`) and requeues exactly the functions that
|
|
178
|
+
carry that setting.
|
|
179
|
+
|
|
180
|
+
An empty line means "not configured". A feature that needs it stays off rather than failing halfway.
|
|
181
|
+
|
|
182
|
+
## 4. Your own Cloud Functions
|
|
183
|
+
|
|
184
|
+
Two places, answering two different questions:
|
|
185
|
+
|
|
186
|
+
| Where | When |
|
|
187
|
+
|---|---|
|
|
188
|
+
| \`extensions/<plugin>/functions/\` | the function belongs to that extension |
|
|
189
|
+
| \`functions/\` | the function is shared by more than one |
|
|
190
|
+
|
|
191
|
+
Each directory is read through its \`index.ts\` — export from there what should be deployed.
|
|
192
|
+
\`microteia deploy\` gathers them into the package, generates the entry point, and refuses the deploy if
|
|
193
|
+
two of them export the same name, or if one collides with a platform function. That refusal matters: the
|
|
194
|
+
entry point spreads yours *after* the platform's, so a repeated name would replace a platform trigger
|
|
195
|
+
silently, and a trigger that was replaced does not fail — it simply stops happening.
|
|
196
|
+
|
|
197
|
+
A function that belongs to an extension should also be declared in its
|
|
198
|
+
\`manifest.backend.functions\`. That is what makes the installation deploy it only where the extension is
|
|
199
|
+
installed, instead of everywhere.
|
|
200
|
+
|
|
201
|
+
Functions are pinned to **${manifest.requires.functionsRegion}**. One in another region receives no
|
|
202
|
+
Firestore trigger from this project, and the deploy reports nothing.
|
|
203
|
+
|
|
204
|
+
## 5. Writing extensions
|
|
205
|
+
|
|
206
|
+
cd extensions
|
|
207
|
+
npx microteia-plugin create # guided
|
|
208
|
+
npx microteia-plugin create function # adds a Cloud Function to an existing extension
|
|
209
|
+
npx tsc --noEmit # types
|
|
210
|
+
npx microteia-plugin check <folder>/plugin.ts
|
|
211
|
+
npx microteia-plugin build <folder>/plugin.ts
|
|
212
|
+
|
|
213
|
+
\`check\` is not an approximation of the platform's validation — it *is* it, the same function and the
|
|
214
|
+
same messages. If \`check\` passes, the package installs.
|
|
215
|
+
|
|
216
|
+
**A package that carries code installs inert.** An administrator opens the source, reads it, and trusts
|
|
217
|
+
it; the trust is bound to that exact code by hash. Changing one character requires trusting it again.
|
|
218
|
+
|
|
219
|
+
### Examples included with this version
|
|
220
|
+
|
|
221
|
+
${examples.length === 0 ? '_None in this package._' : examples.map((id) => `- \`extensions/examples/${id}/\``).join('\n')}
|
|
222
|
+
|
|
223
|
+
Each is the real source of an extension that ships with Microteia. Build any of them and you get the
|
|
224
|
+
same \`.json\` the platform installs:
|
|
225
|
+
|
|
226
|
+
npx microteia-plugin build examples/pessoa/plugin.ts
|
|
227
|
+
|
|
228
|
+
## 6. An AI assistant in this directory
|
|
229
|
+
|
|
230
|
+
\`extensions/.mcp.json\` registers an MCP server that answers from **this** installation: the extension
|
|
231
|
+
points it has, what a page may import, the authoring guide, and whether a package would be accepted.
|
|
232
|
+
\`extensions/AGENTS.md\` (and \`CLAUDE.md\`, the same content) holds the rules.
|
|
233
|
+
|
|
234
|
+
The validation tool is the part that matters — it closes the loop between what an assistant proposes and
|
|
235
|
+
what the platform would do with it, before anything is installed.
|
|
236
|
+
|
|
237
|
+
## 7. Manual prerequisites
|
|
238
|
+
|
|
239
|
+
Things no command can do for you, in the Firebase or Google Cloud console:
|
|
240
|
+
|
|
241
|
+
${manifest.manualPrerequisites.map((prerequisite) => `- **${prerequisite.title}**${prerequisite.optional ? ' _(optional)_' : ''}\n ${prerequisite.detail}`).join('\n')}
|
|
242
|
+
`;
|
|
243
|
+
};
|