@microteia/console 2.8.0 → 2.18.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.
Files changed (37) hide show
  1. package/CONSOLE-VERSIONS.md +97 -0
  2. package/README.md +1 -1
  3. package/lib/console/appliedFunctions.d.ts +79 -0
  4. package/lib/console/appliedFunctions.js +88 -0
  5. package/lib/console/backendResidue.d.ts +55 -0
  6. package/lib/console/backendResidue.js +53 -0
  7. package/lib/console/customFunctions.d.ts +51 -1
  8. package/lib/console/customFunctions.js +94 -1
  9. package/lib/console/deploy.js +32 -7
  10. package/lib/console/environment.d.ts +22 -6
  11. package/lib/console/environment.js +11 -0
  12. package/lib/console/exportCatalog.d.ts +124 -0
  13. package/lib/console/exportCatalog.js +264 -0
  14. package/lib/console/firestoreRest.d.ts +27 -0
  15. package/lib/console/firestoreRest.js +23 -0
  16. package/lib/console/functionGate.d.ts +198 -0
  17. package/lib/console/functionGate.js +400 -0
  18. package/lib/console/init.js +1 -0
  19. package/lib/console/installedExtensions.d.ts +37 -0
  20. package/lib/console/installedExtensions.js +37 -0
  21. package/lib/console/loginExtensions.d.ts +29 -0
  22. package/lib/console/loginExtensions.js +101 -0
  23. package/lib/console/provision.d.ts +26 -13
  24. package/lib/console/provision.js +312 -73
  25. package/lib/index.js +8 -0
  26. package/lib/kernel/backendShape.d.ts +27 -0
  27. package/lib/kernel/backendShape.js +38 -0
  28. package/lib/kernel/manifest.d.ts +16 -0
  29. package/lib/kernel/manifest.js +16 -0
  30. package/lib/shared/paths.d.ts +1 -1
  31. package/lib/shared/paths.js +1 -1
  32. package/lib/vendor/exampleSources.js +7 -3
  33. package/lib/vendor/installationDocs.js +18 -0
  34. package/lib/vendor/pack.js +3 -1
  35. package/lib/vendor/packageTemplates.d.ts +12 -1
  36. package/lib/vendor/packageTemplates.js +42 -2
  37. package/package.json +1 -1
@@ -61,6 +61,44 @@ export const functionClaimProblems = (claims, packagedFunctions) => {
61
61
  }
62
62
  return problems;
63
63
  };
64
+ /**
65
+ * Reivindicar uma function que a PLATAFORMA sobe sempre.
66
+ *
67
+ * É conferência de PROVISIONAMENTO, não de empacotamento, e a assimetria é da própria definição: no
68
+ * `pack` a baseline é DERIVADA (`todas − reivindicadas`), então reivindicar é o que tira o nome dela e
69
+ * a pergunta não existe. Do lado de quem instala a baseline já está decidida e viaja no manifesto, e
70
+ * aí a reivindicação de um nome dela é uma contradição com duas consequências, nenhuma visível: o
71
+ * `deploy` sobe a function de qualquer jeito, e a desinstalação da extensão faz `--prune` apagar um
72
+ * gatilho do produto num escritório que não fez nada.
73
+ */
74
+ export const baselineClaimProblems = (claims, baselineFunctions) => {
75
+ const baseline = new Set(baselineFunctions);
76
+ return claims.flatMap((claim) => (claim.backend.functions ?? [])
77
+ .filter((name) => baseline.has(name))
78
+ .map((name) => `${claim.pluginId} claims function "${name}", which the platform itself deploys`));
79
+ };
80
+ /**
81
+ * Reivindicar uma function COMUM do escritório — a que mora em `functions/`, ao lado do
82
+ * `microteia.yaml`.
83
+ *
84
+ * É o mesmo defeito de reivindicar uma function da plataforma, com o mesmo par de consequências
85
+ * invisíveis, e por isso é conferido no mesmo lugar. A comum não tem dono por construção: ela é o lugar
86
+ * de quem serve várias extensões, nenhuma pode declará-la (duas reivindicando o mesmo nome é recusa), e
87
+ * o `deploy` a sobe SEMPRE. Reivindicá-la, então, não muda nada enquanto a extensão está instalada — e
88
+ * no dia em que ela sai, a function vira órfã, `--prune` a apaga, e o `deploy` seguinte a repõe. As
89
+ * outras extensões que dependiam dela perdem o backend numa janela que nada explica, e a fonte continua
90
+ * intacta em `functions/`, sem nada na árvore que sequer sugira o que aconteceu.
91
+ *
92
+ * A assimetria com o `pack` é a mesma de `baselineClaimProblems`: o pacote do kernel não sabe o que o
93
+ * escritório vai escrever em `functions/`, então a pergunta só existe do lado de quem instala.
94
+ */
95
+ export const sharedClaimProblems = (claims, sharedFunctions) => {
96
+ const shared = new Set(sharedFunctions);
97
+ return claims.flatMap((claim) => (claim.backend.functions ?? [])
98
+ .filter((name) => shared.has(name))
99
+ .map((name) => `${claim.pluginId} claims function "${name}", which is a shared function of this `
100
+ + 'installation'));
101
+ };
64
102
  /**
65
103
  * Índice reivindicado que não existe na raiz. É conferência de EMPACOTAMENTO, não de provisionamento:
66
104
  * a raiz é o que roda em `microteia-dev` e `curatino`, então uma declaração que não esteja lá deixaria
@@ -44,6 +44,16 @@ export interface KernelManifest {
44
44
  baselineIndexes: FirestoreIndexSpec[];
45
45
  /** Ausente nos pacotes anteriores a 7.51.0, que não conheciam a verificação de alcance. */
46
46
  callables?: string[];
47
+ /**
48
+ * Os pontos de extensão que fazem efeito ANTES do login. É contra esta lista que o `deploy` decide
49
+ * se um pacote carimbado tem o que fazer no boot — carimbar um que não contribua para nenhum deles
50
+ * o carregaria sem efeito, e quem o carimbou acreditaria que está no ar.
51
+ *
52
+ * Ela é DERIVADA do kernel e viaja no manifesto justamente para o Console não a conhecer de cor: o
53
+ * Console é versionado separadamente, e uma cópia aqui passaria a descrever o contrato de outra
54
+ * versão em silêncio. Ausente nos pacotes anteriores às extensões carimbadas.
55
+ */
56
+ preLoginPoints?: string[];
47
57
  backendParameters: BackendParameter[];
48
58
  manualPrerequisites: KernelPrerequisite[];
49
59
  /**
@@ -135,4 +145,10 @@ export declare const callableFunctionNames: (source: string) => string[];
135
145
  export declare const declaredParametersIn: (source: string) => BackendParameter[];
136
146
  /** Comparação de versão suficiente para `minimumCliVersion`: três números, sem pré-lançamento. */
137
147
  export declare const compareVersions: (left: string, right: string) => number;
148
+ /**
149
+ * Os pontos que valem antes do login, lidos de `domain/schema/plugin/preLoginPoints.ts`. Lista vazia é
150
+ * recusada em vez de aceita: um pacote sem nenhum ponto pré-login recusaria todo carimbo, e a causa
151
+ * (o parser não achou nada) não teria nada a ver com o sintoma.
152
+ */
153
+ export declare const parsePreLoginPoints: (source: string) => string[];
138
154
  export declare const parseKernelVersion: (source: string) => string;
@@ -137,6 +137,22 @@ export const compareVersions = (left, right) => {
137
137
  }
138
138
  return 0;
139
139
  };
140
+ /**
141
+ * Os pontos que valem antes do login, lidos de `domain/schema/plugin/preLoginPoints.ts`. Lista vazia é
142
+ * recusada em vez de aceita: um pacote sem nenhum ponto pré-login recusaria todo carimbo, e a causa
143
+ * (o parser não achou nada) não teria nada a ver com o sintoma.
144
+ */
145
+ export const parsePreLoginPoints = (source) => {
146
+ const block = source.match(/PRE_LOGIN_POINTS[^=]*=\s*\[([^\]]*)\]/);
147
+ if (block === null) {
148
+ throw new Error('PRE_LOGIN_POINTS not found in domain/schema/plugin/preLoginPoints.ts');
149
+ }
150
+ const points = [...block[1].matchAll(/'([^']+)'/g)].map((match) => match[1]);
151
+ if (points.length === 0) {
152
+ throw new Error('PRE_LOGIN_POINTS is empty in domain/schema/plugin/preLoginPoints.ts');
153
+ }
154
+ return points;
155
+ };
140
156
  export const parseKernelVersion = (source) => {
141
157
  const match = source.match(/KERNEL_VERSION\s*=\s*'([^']+)'/);
142
158
  if (match === null)
@@ -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.8.0";
90
+ export declare const CLI_VERSION = "2.18.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.8.0';
97
+ export const CLI_VERSION = '2.18.0';
@@ -31,7 +31,9 @@ export const EXAMPLE_SOURCES = {
31
31
  * extensão tem a sua pasta — aqui elas moram num diretório comum, com um nível a mais.
32
32
  */
33
33
  export const rewriteImports = (source) => source.replace(/from '(\.[^']*)'/g, (match, specifier) => {
34
- if (/(^|\/)contributionBuilders$/.test(specifier) || specifier.includes('domain/')) {
34
+ if (/(^|\/)contributionBuilders$/.test(specifier)
35
+ || specifier.includes('domain/')
36
+ || specifier.includes('sdk/')) {
35
37
  return "from '@microteia/sdk'";
36
38
  }
37
39
  // `./views/<plugin>/Nome.tsx?raw` -> `./views/Nome.tsx?raw`
@@ -44,8 +46,10 @@ const HEADER = (id) => `// Example: a Microteia first-party extension, as SOURCE
44
46
  //
45
47
  // npx microteia-plugin build ${id}/plugin.ts
46
48
  //
47
- // The only difference from how it is written inside Microteia is the import path: there it reaches into
48
- // the kernel tree, here everything comes from '@microteia/sdk'.
49
+ // Two things differ from how you would write the same extension. The imports: inside Microteia they
50
+ // reach into the kernel tree, here everything comes from '@microteia/sdk'. And the view sources: these
51
+ // travel as TEXT and are merged by 'mergeViewSources', because the platform bundles them; you point at
52
+ // the files with 'sourceFile' and the compiler merges them for you. See AUTHORING.md, section 7.
49
53
 
50
54
  `;
51
55
  /**
@@ -94,6 +94,7 @@ const commandRows = () => [
94
94
  '| `microteia provision [--prune] [--all]` | Gives installed extensions the functions and indexes they ask for |',
95
95
  '| `microteia connect --project <id>` | Binds this installation to a Firebase project you own |',
96
96
  '| `microteia plugin-init` | Prepares `extensions/` for authoring in TypeScript |',
97
+ '| `microteia export-catalog --tenant <id>` | Writes what an office has — screens, lists, installed extensions — into `extensions/`, so the authoring tools can see it. Reads only; with no `--tenant` it lists the offices. The file is a snapshot and is git-ignored: it carries names the office typed |',
97
98
  ].join('\n');
98
99
  /**
99
100
  * O documento técnico. SOBREPOSTO a cada `unpack`, e por isso é onde o detalhe que acompanha a versão
@@ -145,6 +146,23 @@ it is what makes an upgrade trustworthy, and the reason your files are elsewhere
145
146
 
146
147
  ${commandRows()}
147
148
 
149
+ ### Signing in before there is an organization
150
+
151
+ The sign-in screen runs above the organization: there is no tenant there, so nothing an organization
152
+ installed can reach it. That is why \`login.extensions\` in \`microteia.yaml\` exists — it lists compiled
153
+ extension packages that \`deploy\` stamps into the site itself, and it is how a corporate SSO, or your own
154
+ mark on the sign-in screen, gets there.
155
+
156
+ Two things follow, and both are deliberate:
157
+
158
+ - **Nothing stamped runs JavaScript.** The packages load untrusted, so only declarations apply. That is
159
+ what lets you put an SSO live without anyone reviewing code.
160
+ - **Stamping is not installing.** A stamped package applies before sign-in only; inside the organization
161
+ what applies is what the organization installed, from the Extensions screen. To have both, do both.
162
+
163
+ \`deploy\` refuses a package that contributes to nothing which works before sign-in, rather than stamping
164
+ it: it would load with no effect, and you would believe it was live.
165
+
148
166
  \`unpack\` and \`deploy\` are separate so that taking a new version does not oblige you to publish
149
167
  everything, and so that there is a moment between the two to fill in a setting the new version asks for.
150
168
 
@@ -4,7 +4,7 @@ import { flagValue, hasFlag } from '../shared/args.js';
4
4
  import { repoRoot } from '../shared/paths.js';
5
5
  import { run } from '../shared/run.js';
6
6
  import { blank, bullets, heading, info, step, success } from '../shared/ui.js';
7
- import { DEFAULT_FUNCTIONS_REGION, KERNEL_COMPONENTS, KERNEL_DISTRIBUTION_ID, MANUAL_PREREQUISITES, MINIMUM_CLI_VERSION, REQUIRED_NODE_VERSION, callableFunctionNames, declaredParametersIn, exportedFunctionNames, parseKernelVersion, } from '../kernel/manifest.js';
7
+ import { DEFAULT_FUNCTIONS_REGION, KERNEL_COMPONENTS, KERNEL_DISTRIBUTION_ID, MANUAL_PREREQUISITES, MINIMUM_CLI_VERSION, REQUIRED_NODE_VERSION, callableFunctionNames, declaredParametersIn, exportedFunctionNames, parseKernelVersion, parsePreLoginPoints, } from '../kernel/manifest.js';
8
8
  import { digestOfFile } from '../shared/digest.js';
9
9
  import { kernelTarballName } from '../kernel/channel.js';
10
10
  import { baselineFunctionsOf, baselineIndexesOf, claimProblems, claimsFromTemplates, } from '../kernel/backendShape.js';
@@ -123,6 +123,7 @@ export const pack = async (args) => {
123
123
  */
124
124
  const changelog = await readChangelog(root, KERNEL_CHANGELOG, version);
125
125
  const kernelVersion = parseKernelVersion(await readFile(join(root, 'src/domain/schema/plugin/kernelVersion.ts'), 'utf8'));
126
+ const preLoginPoints = parsePreLoginPoints(await readFile(join(root, 'src/domain/schema/plugin/preLoginPoints.ts'), 'utf8'));
126
127
  const functionNames = exportedFunctionNames(await readFile(join(root, 'functions/src/index.ts'), 'utf8'));
127
128
  const scan = await scanBackendSources(join(root, 'functions/src'));
128
129
  const backendParameters = scan.parameters;
@@ -225,6 +226,7 @@ export const pack = async (args) => {
225
226
  baselineFunctions,
226
227
  baselineIndexes,
227
228
  callables,
229
+ preLoginPoints,
228
230
  backendParameters,
229
231
  manualPrerequisites: MANUAL_PREREQUISITES,
230
232
  sdk,
@@ -16,10 +16,21 @@ export interface HostingConfig {
16
16
  headers: unknown[];
17
17
  }
18
18
  export declare const packageFirebaseJson: (hosting: HostingConfig) => string;
19
+ /**
20
+ * O nome do pacote de functions da instalação, e o subcaminho da superfície curada que a Cloud Function
21
+ * do ESCRITÓRIO importa dele: `import { forInstalledExtension } from 'microteia-functions/office'`.
22
+ *
23
+ * O nome é parte do contrato de autoria a partir do momento em que alguém o escreve num import, e por
24
+ * isso ele deixou de ser uma string solta aqui dentro.
25
+ */
26
+ export declare const FUNCTIONS_PACKAGE_NAME = "microteia-functions";
27
+ export declare const OFFICE_SUBPATH = "office";
28
+ export declare const OFFICE_MODULE = "microteia-functions/office";
19
29
  /**
20
30
  * As dependências são copiadas INTACTAS do `functions/package.json` do repositório, para que o
21
31
  * `package-lock.json` que viaja junto continue válido. O que muda é só o entrypoint (passa a ser o
22
- * `index.js` que soma kernel e custom) e o `build`, que agora compila apenas `custom/`.
32
+ * `index.js` que soma kernel e custom), o `build`, que agora compila apenas `custom/`, e o `exports`,
33
+ * que abre a superfície curada para o `custom/` sem custar uma dependência.
23
34
  */
24
35
  /**
25
36
  * A `version` não é enfeite: sem ela o npm não consegue formar `nome@versão` e todo aviso dele sobre
@@ -24,10 +24,21 @@ export const packageFirebaseJson = (hosting) => JSON.stringify({
24
24
  headers: hosting.headers,
25
25
  },
26
26
  }, null, 2) + '\n';
27
+ /**
28
+ * O nome do pacote de functions da instalação, e o subcaminho da superfície curada que a Cloud Function
29
+ * do ESCRITÓRIO importa dele: `import { forInstalledExtension } from 'microteia-functions/office'`.
30
+ *
31
+ * O nome é parte do contrato de autoria a partir do momento em que alguém o escreve num import, e por
32
+ * isso ele deixou de ser uma string solta aqui dentro.
33
+ */
34
+ export const FUNCTIONS_PACKAGE_NAME = 'microteia-functions';
35
+ export const OFFICE_SUBPATH = 'office';
36
+ export const OFFICE_MODULE = `${FUNCTIONS_PACKAGE_NAME}/${OFFICE_SUBPATH}`;
27
37
  /**
28
38
  * As dependências são copiadas INTACTAS do `functions/package.json` do repositório, para que o
29
39
  * `package-lock.json` que viaja junto continue válido. O que muda é só o entrypoint (passa a ser o
30
- * `index.js` que soma kernel e custom) e o `build`, que agora compila apenas `custom/`.
40
+ * `index.js` que soma kernel e custom), o `build`, que agora compila apenas `custom/`, e o `exports`,
41
+ * que abre a superfície curada para o `custom/` sem custar uma dependência.
31
42
  */
32
43
  /**
33
44
  * A `version` não é enfeite: sem ela o npm não consegue formar `nome@versão` e todo aviso dele sobre
@@ -40,10 +51,29 @@ export const packageFirebaseJson = (hosting) => JSON.stringify({
40
51
  * o Firebase — o preço errado pela linha de saída.
41
52
  */
42
53
  export const packageFunctionsPackageJson = (source, version) => JSON.stringify({
43
- name: 'microteia-functions',
54
+ name: FUNCTIONS_PACKAGE_NAME,
44
55
  version,
45
56
  private: true,
46
57
  main: 'index.js',
58
+ /**
59
+ * O `exports` está aqui por UMA razão, e ela não é organização: é ele que faz a auto-referência de
60
+ * pacote funcionar. Sem ele, uma Cloud Function do escritório em `custom/` não tem como importar nada
61
+ * da plataforma — não há `node_modules` que a contenha, e um caminho relativo dependeria de onde o
62
+ * arquivo foi montado, que é justamente o que muda entre escrever e deployar.
63
+ *
64
+ * Com ele, `require('${FUNCTIONS_PACKAGE_NAME}/office')` resolve de dentro do próprio pacote. É por
65
+ * isso que não há dependência nova nem alteração no `package-lock.json` que viaja ao lado.
66
+ *
67
+ * O `.` continua sendo o `index.js`: é ele que o Firebase carrega, e restringir os subcaminhos sem
68
+ * declará-lo tiraria a plataforma do ar inteira.
69
+ */
70
+ exports: {
71
+ '.': './index.js',
72
+ [`./${OFFICE_SUBPATH}`]: {
73
+ types: `./lib/${OFFICE_SUBPATH}/index.d.ts`,
74
+ default: `./lib/${OFFICE_SUBPATH}/index.js`,
75
+ },
76
+ },
47
77
  engines: source.engines ?? { node: '22' },
48
78
  scripts: {
49
79
  build: 'tsc -p custom/tsconfig.json',
@@ -74,6 +104,16 @@ export const packageCustomTsconfig = () => JSON.stringify({
74
104
  compilerOptions: {
75
105
  module: 'commonjs',
76
106
  moduleResolution: 'node',
107
+ /**
108
+ * O `paths` só resolve TIPO; o `require` emitido continua sendo `microteia-functions/office`, e é o
109
+ * `exports` do pacote que o resolve em execução. Os dois lados são necessários porque o
110
+ * `moduleResolution: 'node'` é anterior ao `exports` e não o lê — e trocá-lo por `node16` mudaria,
111
+ * de quebra, como TODO import do escritório resolve.
112
+ *
113
+ * Sem `baseUrl` de propósito: o `paths` relativo ao tsconfig basta desde o TypeScript 4.1, e um
114
+ * `baseUrl` faria qualquer arquivo de `src/` virar um módulo de nome curto por acidente.
115
+ */
116
+ paths: { [OFFICE_MODULE]: [`../lib/${OFFICE_SUBPATH}/index.d.ts`] },
77
117
  target: 'es2022',
78
118
  lib: ['ES2022'],
79
119
  types: ['node'],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microteia/console",
3
- "version": "2.8.0",
3
+ "version": "2.18.0",
4
4
  "description": "Install and update Microteia in a Firebase project you own.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",