@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,145 @@
|
|
|
1
|
+
export const KERNEL_DISTRIBUTION_ID = 'com.microteia.kernel';
|
|
2
|
+
/**
|
|
3
|
+
* O Console mínimo que consegue instalar um pacote montado hoje. Sobe junto com uma mudança que o
|
|
4
|
+
* pacote PASSA A DEPENDER.
|
|
5
|
+
*
|
|
6
|
+
* Em `2.0.0`: o arquivo de configuração de backend na raiz da instalação. Um Console `1.0.0`
|
|
7
|
+
* desempacotaria este pacote e escreveria a credencial em `.microteia/install/functions/`, que o upgrade
|
|
8
|
+
* seguinte apaga — recusar é mais barato que isso.
|
|
9
|
+
*
|
|
10
|
+
* Em `2.1.0`: as Cloud Functions do ESCRITÓRIO. O pacote carrega o SDK cujo `create function` gera uma
|
|
11
|
+
* extensão que declara a própria function em `manifest.backend.functions`, e um Console anterior monta
|
|
12
|
+
* essa function dentro do pacote e nunca a deploya — a extensão fica "Backend pendente" para sempre,
|
|
13
|
+
* com a mensagem errada ("this kernel package does not contain"), apontando para o lugar errado. Foi
|
|
14
|
+
* exatamente o que aconteceu quando um `microteia` de PATH, linkado a um Console antigo, provisionou uma
|
|
15
|
+
* instalação montada por um Console novo: nada no relato dizia que o problema era a ferramenta.
|
|
16
|
+
*/
|
|
17
|
+
export const MINIMUM_CLI_VERSION = '2.1.0';
|
|
18
|
+
export const DEFAULT_FUNCTIONS_REGION = 'southamerica-east1';
|
|
19
|
+
/**
|
|
20
|
+
* A região padrão do Google, que NÃO é a nossa. Uma instalação fora dela chama as callables por um
|
|
21
|
+
* host próprio da região, e é lá que a permissão de invocação tem de existir — daí o aviso no
|
|
22
|
+
* `connect`.
|
|
23
|
+
*/
|
|
24
|
+
export const GOOGLE_DEFAULT_REGION = 'us-central1';
|
|
25
|
+
export const REQUIRED_NODE_VERSION = '22';
|
|
26
|
+
export const KERNEL_COMPONENTS = [
|
|
27
|
+
{ id: 'hosting', path: 'hosting' },
|
|
28
|
+
{ id: 'functions', path: 'functions' },
|
|
29
|
+
{ id: 'firestore.rules', path: 'firestore.rules' },
|
|
30
|
+
{ id: 'firestore.indexes', path: 'firestore.indexes.json' },
|
|
31
|
+
{ id: 'storage.rules', path: 'storage.rules' },
|
|
32
|
+
];
|
|
33
|
+
/**
|
|
34
|
+
* As armadilhas de projeto novo, que até aqui viviam só como prosa no ARCHITECTURE.md (seção "Ao
|
|
35
|
+
* criar um ambiente do zero"). Elas viajam NO MANIFESTO porque quem instala não tem o ARCHITECTURE.md
|
|
36
|
+
* — e porque cada uma delas já custou um diagnóstico errado: a falta do App Engine aparece como um
|
|
37
|
+
* 404 de autenticação de service agent, que manda procurar no lugar errado.
|
|
38
|
+
*/
|
|
39
|
+
export const MANUAL_PREREQUISITES = [
|
|
40
|
+
{
|
|
41
|
+
id: 'billing',
|
|
42
|
+
title: 'Put the Firebase project on the Blaze (pay as you go) plan',
|
|
43
|
+
detail: 'This one comes before everything else: Cloud Functions of the 2nd generation do not exist on the '
|
|
44
|
+
+ 'free plan, and neither the App Engine application nor the default Storage bucket can be created '
|
|
45
|
+
+ 'without billing. Firebase console, project settings, Usage and billing. Every other step here '
|
|
46
|
+
+ 'fails until this is done.',
|
|
47
|
+
optional: false,
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: 'app-engine',
|
|
51
|
+
title: 'Create the App Engine application before the first deploy',
|
|
52
|
+
detail: 'Run: gcloud app create --region=<region> --project=<projectId>. Without it the functions deploy '
|
|
53
|
+
+ 'fails at generateUploadUrl with a 404 about the gcf-admin-robot service agent, which does not '
|
|
54
|
+
+ 'mention App Engine at all. The region is set once and can never be changed — use the same '
|
|
55
|
+
+ 'region as the functions.',
|
|
56
|
+
optional: false,
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: 'firestore-location',
|
|
60
|
+
title: 'Create Firestore in the same region as the functions',
|
|
61
|
+
detail: 'Firestore triggers do not work across regions. The database location is permanent, so getting '
|
|
62
|
+
+ 'this wrong means recreating the project.',
|
|
63
|
+
optional: false,
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
id: 'storage-bucket',
|
|
67
|
+
title: 'Set up Cloud Storage for Firebase',
|
|
68
|
+
detail: 'Firebase console, Storage, "Get Started". Enabling the API is not enough — the default bucket '
|
|
69
|
+
+ 'has to be created, and no CLI command does it. Without it the very first deploy stops on the '
|
|
70
|
+
+ 'storage rules. Documents attached to records and the cold backup of expired audit logs both '
|
|
71
|
+
+ 'live here.',
|
|
72
|
+
optional: false,
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: 'auth-provider',
|
|
76
|
+
title: 'Enable the Google sign-in provider in Firebase Authentication',
|
|
77
|
+
detail: 'Firebase console, Authentication, Sign-in method. Without it nobody can log in.',
|
|
78
|
+
optional: false,
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
id: 'functions-invoker',
|
|
82
|
+
title: 'Allow unauthenticated invocation of the callable functions, if your organization blocks it',
|
|
83
|
+
detail: 'The deploy grants allUsers the roles/run.invoker role on every callable, and the deploy checks '
|
|
84
|
+
+ 'it afterwards. An organization that enforces Domain Restricted Sharing refuses that grant, and '
|
|
85
|
+
+ 'then the application loads but every server call fails in the browser with what looks like a '
|
|
86
|
+
+ 'CORS error. The fix is one command per function, and the deploy prints the exact list: gcloud '
|
|
87
|
+
+ 'run services add-iam-policy-binding <function-name-in-lowercase> --region=<region> '
|
|
88
|
+
+ '--project=<projectId> --member=allUsers --role=roles/run.invoker. Unauthenticated invocation '
|
|
89
|
+
+ 'is not open access: every callable still rejects callers with no signed-in user.',
|
|
90
|
+
optional: true,
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
id: 'audit-ttl',
|
|
94
|
+
title: 'Enable the Firestore TTL policy on auditLogs.expiresAt',
|
|
95
|
+
detail: 'Firestore console, TTL, collection auditLogs, field expiresAt. It cannot be declared in code or '
|
|
96
|
+
+ 'rules. Without it audit logs are never expired and grow without bound.',
|
|
97
|
+
optional: false,
|
|
98
|
+
},
|
|
99
|
+
];
|
|
100
|
+
/**
|
|
101
|
+
* Os nomes das Cloud Functions saem de `functions/src/index.ts`, a mesma fonte e o mesmo parse que
|
|
102
|
+
* `scripts/deployLists.test.ts` já usa para garantir que nenhuma function fique fora dos grupos de
|
|
103
|
+
* deploy. Reaproveitar a fonte guardada evita criar uma terceira lista para desatualizar.
|
|
104
|
+
*/
|
|
105
|
+
export const exportedFunctionNames = (indexSource) => [...indexSource.matchAll(/export\s*\{([^}]+)\}/g)]
|
|
106
|
+
.flatMap((match) => match[1].split(','))
|
|
107
|
+
.map((name) => name.trim())
|
|
108
|
+
.filter((name) => name !== '');
|
|
109
|
+
/**
|
|
110
|
+
* As callables são as functions que o NAVEGADOR chama. Elas se distinguem das de gatilho por uma
|
|
111
|
+
* razão prática: só elas podem ser verificadas de fora, e só nelas a permissão de invocação pública
|
|
112
|
+
* importa. Sem ela a aplicação abre e toda chamada ao servidor falha — parecendo erro de CORS, que
|
|
113
|
+
* manda procurar no lugar errado.
|
|
114
|
+
*/
|
|
115
|
+
export const callableFunctionNames = (source) => [...source.matchAll(/export\s+const\s+([a-zA-Z0-9_]+)\s*=\s*onCall\b/g)].map((match) => match[1]);
|
|
116
|
+
/**
|
|
117
|
+
* Os parâmetros que o backend declara com `defineString`. O `firebase-functions` os valida
|
|
118
|
+
* GLOBALMENTE, e não por function: um parâmetro sem valor e sem default recusa o deploy de TODAS as
|
|
119
|
+
* functions, inclusive as que não têm relação nenhuma com ele. Foi assim que um `GOOGLE_GMAIL_TOPIC`
|
|
120
|
+
* ausente derrubou o deploy de `callAIProxy` numa instalação nova.
|
|
121
|
+
*
|
|
122
|
+
* Por isso a lista é DERIVADA do código, e não copiada de um `.env.example` — que já estava
|
|
123
|
+
* desatualizado quando esta função foi escrita.
|
|
124
|
+
*/
|
|
125
|
+
export const declaredParametersIn = (source) => [...source.matchAll(/defineString\(\s*'([A-Z0-9_]+)'\s*(,\s*\{[^}]*\})?\s*\)/g)].map((match) => ({
|
|
126
|
+
name: match[1],
|
|
127
|
+
required: match[2] === undefined || !match[2].includes('default'),
|
|
128
|
+
}));
|
|
129
|
+
/** Comparação de versão suficiente para `minimumCliVersion`: três números, sem pré-lançamento. */
|
|
130
|
+
export const compareVersions = (left, right) => {
|
|
131
|
+
const a = left.split('.').map((part) => Number.parseInt(part, 10));
|
|
132
|
+
const b = right.split('.').map((part) => Number.parseInt(part, 10));
|
|
133
|
+
for (let index = 0; index < 3; index += 1) {
|
|
134
|
+
const difference = (a[index] || 0) - (b[index] || 0);
|
|
135
|
+
if (difference !== 0)
|
|
136
|
+
return difference;
|
|
137
|
+
}
|
|
138
|
+
return 0;
|
|
139
|
+
};
|
|
140
|
+
export const parseKernelVersion = (source) => {
|
|
141
|
+
const match = source.match(/KERNEL_VERSION\s*=\s*'([^']+)'/);
|
|
142
|
+
if (match === null)
|
|
143
|
+
throw new Error('KERNEL_VERSION not found in domain/schema/plugin/kernelVersion.ts');
|
|
144
|
+
return match[1];
|
|
145
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface ParsedArgs {
|
|
2
|
+
positionals: string[];
|
|
3
|
+
flags: Map<string, string | true>;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Parser mínimo, na mesma linha do que `deploy-functions-individually.cjs` já faz à mão: aceita
|
|
7
|
+
* `--flag`, `--flag valor` e `--flag=valor`. Não há biblioteca de CLI no repositório e este milestone
|
|
8
|
+
* não é motivo para introduzir uma.
|
|
9
|
+
*/
|
|
10
|
+
export declare const parseArgs: (argv: string[]) => ParsedArgs;
|
|
11
|
+
export declare const flagValue: (args: ParsedArgs, name: string) => string | undefined;
|
|
12
|
+
export declare const hasFlag: (args: ParsedArgs, name: string) => boolean;
|
|
13
|
+
/** Falha cedo e com o nome do que faltou, em vez de deixar o comando descobrir no meio. */
|
|
14
|
+
export declare const requiredFlag: (args: ParsedArgs, name: string) => string;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser mínimo, na mesma linha do que `deploy-functions-individually.cjs` já faz à mão: aceita
|
|
3
|
+
* `--flag`, `--flag valor` e `--flag=valor`. Não há biblioteca de CLI no repositório e este milestone
|
|
4
|
+
* não é motivo para introduzir uma.
|
|
5
|
+
*/
|
|
6
|
+
export const parseArgs = (argv) => {
|
|
7
|
+
const positionals = [];
|
|
8
|
+
const flags = new Map();
|
|
9
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
10
|
+
const token = argv[index];
|
|
11
|
+
if (!token.startsWith('--')) {
|
|
12
|
+
positionals.push(token);
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
const body = token.slice(2);
|
|
16
|
+
const equals = body.indexOf('=');
|
|
17
|
+
if (equals !== -1) {
|
|
18
|
+
flags.set(body.slice(0, equals), body.slice(equals + 1));
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
const next = argv[index + 1];
|
|
22
|
+
if (next !== undefined && !next.startsWith('--')) {
|
|
23
|
+
flags.set(body, next);
|
|
24
|
+
index += 1;
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
flags.set(body, true);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return { positionals, flags };
|
|
31
|
+
};
|
|
32
|
+
export const flagValue = (args, name) => {
|
|
33
|
+
const value = args.flags.get(name);
|
|
34
|
+
return typeof value === 'string' ? value : undefined;
|
|
35
|
+
};
|
|
36
|
+
export const hasFlag = (args, name) => args.flags.has(name);
|
|
37
|
+
/** Falha cedo e com o nome do que faltou, em vez de deixar o comando descobrir no meio. */
|
|
38
|
+
export const requiredFlag = (args, name) => {
|
|
39
|
+
const value = flagValue(args, name);
|
|
40
|
+
if (value === undefined || value === '') {
|
|
41
|
+
throw new Error(`Missing required option --${name}.`);
|
|
42
|
+
}
|
|
43
|
+
return value;
|
|
44
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* O tamanho e a impressão digital de um arquivo, que juntos são a única garantia que um artefato
|
|
3
|
+
* BAIXADO oferece de ser o que foi empacotado.
|
|
4
|
+
*
|
|
5
|
+
* Enquanto o tarball chegava à mão — anexo de e-mail, pendrive, Drive —, o transporte era confiável por
|
|
6
|
+
* construção e o pacote não carregava hash nenhum. Baixado de uma URL, não é: um download interrompido
|
|
7
|
+
* produz um `.tgz` menor que o `tar` recusa com uma mensagem sobre formato, e a pessoa vai procurar o
|
|
8
|
+
* defeito no pacote em vez de na rede.
|
|
9
|
+
*
|
|
10
|
+
* O `bytes` vem junto de propósito. Ele não acrescenta segurança nenhuma ao `sha256`, mas é o que
|
|
11
|
+
* permite dizer "veio pela metade" em vez de "não confere" — e essas duas frases mandam procurar em
|
|
12
|
+
* lugares diferentes.
|
|
13
|
+
*/
|
|
14
|
+
export interface FileDigest {
|
|
15
|
+
bytes: number;
|
|
16
|
+
sha256: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Lê por stream, e não com `readFile`: o tarball do kernel passa de 7 MB e cresce a cada versão, e
|
|
20
|
+
* carregar o arquivo inteiro na memória para depois descartá-lo não tem motivo.
|
|
21
|
+
*/
|
|
22
|
+
export declare const digestOfFile: (path: string) => Promise<FileDigest>;
|
|
23
|
+
/**
|
|
24
|
+
* O que está errado num arquivo que devia casar com um digest, em prosa que diz onde procurar.
|
|
25
|
+
*
|
|
26
|
+
* Devolve `null` quando confere. A ordem importa: o tamanho é conferido primeiro porque é o caso
|
|
27
|
+
* comum e o que tem conserto óbvio (baixar de novo), e um `sha256` divergente com o tamanho certo é a
|
|
28
|
+
* situação grave — ali o arquivo não é o que o índice diz que é.
|
|
29
|
+
*/
|
|
30
|
+
export declare const digestMismatch: (expected: FileDigest, actual: FileDigest) => string | null;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { createReadStream } from 'node:fs';
|
|
3
|
+
import { stat } from 'node:fs/promises';
|
|
4
|
+
/**
|
|
5
|
+
* Lê por stream, e não com `readFile`: o tarball do kernel passa de 7 MB e cresce a cada versão, e
|
|
6
|
+
* carregar o arquivo inteiro na memória para depois descartá-lo não tem motivo.
|
|
7
|
+
*/
|
|
8
|
+
export const digestOfFile = async (path) => {
|
|
9
|
+
const hash = createHash('sha256');
|
|
10
|
+
const stream = createReadStream(path);
|
|
11
|
+
await new Promise((resolve, reject) => {
|
|
12
|
+
stream.on('data', (chunk) => hash.update(chunk));
|
|
13
|
+
stream.on('error', (error) => reject(error));
|
|
14
|
+
stream.on('end', () => resolve());
|
|
15
|
+
});
|
|
16
|
+
const { size } = await stat(path);
|
|
17
|
+
return { bytes: size, sha256: hash.digest('hex') };
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* O que está errado num arquivo que devia casar com um digest, em prosa que diz onde procurar.
|
|
21
|
+
*
|
|
22
|
+
* Devolve `null` quando confere. A ordem importa: o tamanho é conferido primeiro porque é o caso
|
|
23
|
+
* comum e o que tem conserto óbvio (baixar de novo), e um `sha256` divergente com o tamanho certo é a
|
|
24
|
+
* situação grave — ali o arquivo não é o que o índice diz que é.
|
|
25
|
+
*/
|
|
26
|
+
export const digestMismatch = (expected, actual) => {
|
|
27
|
+
if (actual.bytes !== expected.bytes) {
|
|
28
|
+
return `it has ${actual.bytes} bytes and should have ${expected.bytes} — the download did not finish.`;
|
|
29
|
+
}
|
|
30
|
+
if (actual.sha256 !== expected.sha256) {
|
|
31
|
+
return `its sha256 is ${actual.sha256} and should be ${expected.sha256}.`;
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
export declare const repoRoot: string;
|
|
2
|
+
/**
|
|
3
|
+
* A raiz do CONSOLE: `cli/` dentro do repositório, e a própria pasta do pacote quando ela é montada por
|
|
4
|
+
* `pack-console`. Vale nos dois porque a profundidade de `shared/` até ela é a mesma (`cli/lib/shared`,
|
|
5
|
+
* `cli/src/shared` em teste, `<pasta>/lib/shared` no pacote) — o mesmo motivo do cálculo acima.
|
|
6
|
+
*
|
|
7
|
+
* É por aqui que o `unpack` acha o `CONSOLE-VERSIONS.md`: o histórico do Console viaja com o binário do
|
|
8
|
+
* Console, e não com o pacote do kernel. Ao contrário de `repoRoot`, este caminho continua verdadeiro
|
|
9
|
+
* fora do repositório.
|
|
10
|
+
*/
|
|
11
|
+
export declare const consoleRoot: string;
|
|
12
|
+
/**
|
|
13
|
+
* A versão do CONSOLE, deliberadamente independente do `version` do produto: ela diz o que a
|
|
14
|
+
* ferramenta sabe fazer, e é contra ela que o `minimumCliVersion` do manifesto é conferido. Um
|
|
15
|
+
* pacote novo pode exigir um Console mais novo sem que o número do produto signifique nada aqui.
|
|
16
|
+
*
|
|
17
|
+
* Semver do lado da FERRAMENTA: major quando um comando muda de contrato, minor quando ganha
|
|
18
|
+
* capacidade, patch quando muda o que ele escreve sem que o pacote passe a depender disso. Em
|
|
19
|
+
* `2.0.0`: o `deploy` deixou de aceitar `--kernel` (o pacote passa a chegar pelo `unpack`), e as
|
|
20
|
+
* configurações de backend saíram de `.microteia/` para a raiz da instalação — um Console anterior
|
|
21
|
+
* gravaria a credencial no lugar que o upgrade apaga. `unpack` e `plugin-init` são aditivos e
|
|
22
|
+
* sozinhos pediriam só o minor. Em `2.0.1`: o `.gitignore` da instalação passa a descartar os
|
|
23
|
+
* `examples/`, que o `unpack` reescreve e ninguém versiona. Em `2.0.2`: o `AGENTS.md` e o
|
|
24
|
+
* `AUTHORING.md` que ele escreve passam a cobrar versão, entrada de histórico e teste a cada
|
|
25
|
+
* alteração de extensão. Em `2.1.0`: as Cloud Functions que o ESCRITÓRIO escreve deixam de ser
|
|
26
|
+
* montadas e esquecidas — as de `functions/` sobem no `deploy`, as de `extensions/<plugin>/functions/`
|
|
27
|
+
* sobem pelo provisionamento (e `manifest.backend.functions` pode nomeá-las, que era o que o
|
|
28
|
+
* `create function` já mandava escrever e o `provision` recusava), cada diretório pode declarar as
|
|
29
|
+
* próprias dependências npm, e os `defineString` delas entram no `.env.<projectId>`. Em `2.1.1`: o
|
|
30
|
+
* `extensions/` gerado ganha `npm run sdk:refresh`, porque `npm install` NÃO traz o SDK novo — o
|
|
31
|
+
* caminho do tarball não leva versão e o lock guarda a impressão digital do anterior, então o npm diz
|
|
32
|
+
* "up to date" e o escritório segue escrevendo contra um contrato que a instalação não tem mais. Em
|
|
33
|
+
* `2.1.2`: a recusa de uma function reivindicada passa a nomear as DUAS origens possíveis — o pacote e
|
|
34
|
+
* o `extensions/<pasta>/functions/` do escritório —, porque dizer só "o pacote não a contém" manda
|
|
35
|
+
* procurar no lugar errado justamente no caso mais comum. Em `2.1.3`: a montagem deixa os arquivos de
|
|
36
|
+
* TESTE fora do artefato. O `predeploy` do pacote compila `custom/` com o `tsc`, e um `import` de
|
|
37
|
+
* `vitest` ali é `Cannot find module` — a compilação falha e leva embora o deploy de TODAS as functions,
|
|
38
|
+
* não só das da extensão que trouxe o teste. O `AGENTS.md` manda escrever teste ao lado da function;
|
|
39
|
+
* era este lado que faltava. Em `2.1.4`: o `unpack` preserva `functions/node_modules` MOVENDO em vez de
|
|
40
|
+
* copiando — o `cp` recursivo de ~250 pacotes estourava o limite de descritores do processo no macOS
|
|
41
|
+
* (`ENFILE: file table overflow`), de forma intermitente e apontando um diretório qualquer de dentro de
|
|
42
|
+
* uma dependência. A cópia nunca teve para que existir: a origem é apagada em seguida. Em `2.1.5`: o carimbo
|
|
43
|
+
* `backendState.appliedAt` passou a ser gravado como DATA (`timestampValue`) e não como texto — o
|
|
44
|
+
* `stringValue` chegava à tela como string com o tipo de `Date`, e derrubava o botão de atualizar a
|
|
45
|
+
* extensão na gravação seguinte. Em `2.2.0`: o `sourceFile` de uma view passa a aceitar LISTA, e o
|
|
46
|
+
* `AUTHORING.md` que este Console escrevia ganhou a seção que explica a fusão. Em `2.3.0`: ele deixa de
|
|
47
|
+
* escrever documento nenhum em `extensions/`. `AGENTS.md`, `AUTHORING.md`, `tsconfig.json` e `.mcp.json`
|
|
48
|
+
* descrevem o que o KERNEL sabe fazer, e passaram para o SDK, que viaja no mesmo pacote daquelas
|
|
49
|
+
* capacidades — enquanto saíam daqui, um Console de duas versões atrás escrevia, em silêncio, a
|
|
50
|
+
* documentação de outro contrato. O `plugin-init` semeia e delega; o `unpack` traz o SDK novo e manda
|
|
51
|
+
* ele reescrever, que é o gatilho que não existia: aqueles arquivos eram escritos UMA vez e nunca mais.
|
|
52
|
+
* O refresh do SDK perdeu o `--no-save` que tinha: com ele o pacote novo é extraído e o
|
|
53
|
+
* `package-lock.json` fica no anterior, então o `npm install` seguinte reinstala o antigo e desfaz o
|
|
54
|
+
* refresh — em silêncio, que é justamente o que ele existe para evitar. E instala pelo caminho RELATIVO
|
|
55
|
+
* ao diretório, porque o npm reescreve a dependência com o caminho que recebe, e um absoluto no
|
|
56
|
+
* `package.json` mataria a portabilidade que o relativo existe para dar.
|
|
57
|
+
*
|
|
58
|
+
* Em `2.4.0`: o índice composto de uma extensão passa a sobreviver à troca de kernel. Ele era registrado
|
|
59
|
+
* só no `firestore.indexes.json` de dentro de `.microteia/install/`, que o `unpack` apaga inteiro — e o
|
|
60
|
+
* pacote traz apenas a baseline, porque o que uma extensão reivindica sai dela para não viajar até quem
|
|
61
|
+
* não a instalou. O resultado era o `microteia deploy` PERGUNTANDO, no meio de um upgrade, se podia
|
|
62
|
+
* apagar do projeto o índice de toda extensão instalada; e o aviso de índice órfão, que lia a mesma
|
|
63
|
+
* memória, emudecia justamente ali. A memória mudou para `backend.indexes` do `microteia.yaml`, na raiz,
|
|
64
|
+
* e o `unpack` devolve ao arquivo o que ela lembra antes de qualquer comando poder publicá-lo. No mesmo
|
|
65
|
+
* passo, o `kernel.version` do `microteia.yaml` deixa de mentir entre desempacotar e publicar: ele é
|
|
66
|
+
* gravado pelo `unpack`, e `kernel.deployed` — novo — é o que o `deploy` carimba. E `--version` responde
|
|
67
|
+
* as três, porque quem pergunta a versão quase nunca quer a da ferramenta.
|
|
68
|
+
*
|
|
69
|
+
* Em `2.5.0`: a function que ficou para trás numa região abandonada deixa de travar o deploy em
|
|
70
|
+
* silêncio. Trocar a região de uma Cloud Function não move nada — o Firebase cria a nova e deixa a
|
|
71
|
+
* antiga —, e a partir dali o `firebase deploy --only functions:<nome>` recusa: o filtro `--only`
|
|
72
|
+
* casa por id e ignora região, então a cópia velha entra no plano como algo a apagar, e apagar exige
|
|
73
|
+
* uma confirmação que um deploy sem terminal não tem como dar. O `deploy` e o `provision` agora
|
|
74
|
+
* conferem isso ANTES de subir, e `--prune` remove as cópias. Junto vieram as duas razões de o
|
|
75
|
+
* defeito ter levado tanto tempo para ser lido: o motivo impresso era o aviso de descontinuação do
|
|
76
|
+
* Runtime Config — a única coisa que o `firebase` manda pelo stderr, enquanto o erro sai pelo stdout,
|
|
77
|
+
* e a busca era pela última linha casada —, e a segunda passada repetia as oito functions para
|
|
78
|
+
* receber palavra por palavra a mesma recusa. O aviso virou UMA nota no fim, o motivo passou a ser
|
|
79
|
+
* escolhido por quanto informa, e a repetição ficou só para a falha que repetir pode consertar.
|
|
80
|
+
*
|
|
81
|
+
* Ela NÃO acompanha o `version` do produto, e não é obrigada a andar junto do `MINIMUM_CLI_VERSION`:
|
|
82
|
+
* este último só se move quando o PACOTE passa a depender de algo que o Console faz.
|
|
83
|
+
*
|
|
84
|
+
* **A narrativa acima para na `2.5.0`, e é de propósito.** De `2.6.0` em diante a entrada de cada
|
|
85
|
+
* versão é escrita em `cli/CONSOLE-VERSIONS.md` — em inglês, no modelo do git —, porque aquele arquivo
|
|
86
|
+
* viaja com o Console e chega à raiz da instalação a cada `unpack`. Um histórico dentro de um comentário
|
|
87
|
+
* de código nunca chegou a quem instala. Este bloco fica sendo o que ele já era antes de crescer: a
|
|
88
|
+
* política de semver da ferramenta, mais o registro do que motivou as versões até a `2.5.0`.
|
|
89
|
+
*/
|
|
90
|
+
export declare const CLI_VERSION = "2.7.0";
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { dirname, resolve } from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
/**
|
|
4
|
+
* A raiz do repositório, a partir da localização deste módulo. A profundidade é a mesma compilado
|
|
5
|
+
* (`cli/lib/shared/`) e em teste (`cli/src/shared/`), então um único cálculo serve aos dois.
|
|
6
|
+
*/
|
|
7
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
export const repoRoot = resolve(here, '..', '..', '..');
|
|
9
|
+
/**
|
|
10
|
+
* A raiz do CONSOLE: `cli/` dentro do repositório, e a própria pasta do pacote quando ela é montada por
|
|
11
|
+
* `pack-console`. Vale nos dois porque a profundidade de `shared/` até ela é a mesma (`cli/lib/shared`,
|
|
12
|
+
* `cli/src/shared` em teste, `<pasta>/lib/shared` no pacote) — o mesmo motivo do cálculo acima.
|
|
13
|
+
*
|
|
14
|
+
* É por aqui que o `unpack` acha o `CONSOLE-VERSIONS.md`: o histórico do Console viaja com o binário do
|
|
15
|
+
* Console, e não com o pacote do kernel. Ao contrário de `repoRoot`, este caminho continua verdadeiro
|
|
16
|
+
* fora do repositório.
|
|
17
|
+
*/
|
|
18
|
+
export const consoleRoot = resolve(here, '..', '..');
|
|
19
|
+
/**
|
|
20
|
+
* A versão do CONSOLE, deliberadamente independente do `version` do produto: ela diz o que a
|
|
21
|
+
* ferramenta sabe fazer, e é contra ela que o `minimumCliVersion` do manifesto é conferido. Um
|
|
22
|
+
* pacote novo pode exigir um Console mais novo sem que o número do produto signifique nada aqui.
|
|
23
|
+
*
|
|
24
|
+
* Semver do lado da FERRAMENTA: major quando um comando muda de contrato, minor quando ganha
|
|
25
|
+
* capacidade, patch quando muda o que ele escreve sem que o pacote passe a depender disso. Em
|
|
26
|
+
* `2.0.0`: o `deploy` deixou de aceitar `--kernel` (o pacote passa a chegar pelo `unpack`), e as
|
|
27
|
+
* configurações de backend saíram de `.microteia/` para a raiz da instalação — um Console anterior
|
|
28
|
+
* gravaria a credencial no lugar que o upgrade apaga. `unpack` e `plugin-init` são aditivos e
|
|
29
|
+
* sozinhos pediriam só o minor. Em `2.0.1`: o `.gitignore` da instalação passa a descartar os
|
|
30
|
+
* `examples/`, que o `unpack` reescreve e ninguém versiona. Em `2.0.2`: o `AGENTS.md` e o
|
|
31
|
+
* `AUTHORING.md` que ele escreve passam a cobrar versão, entrada de histórico e teste a cada
|
|
32
|
+
* alteração de extensão. Em `2.1.0`: as Cloud Functions que o ESCRITÓRIO escreve deixam de ser
|
|
33
|
+
* montadas e esquecidas — as de `functions/` sobem no `deploy`, as de `extensions/<plugin>/functions/`
|
|
34
|
+
* sobem pelo provisionamento (e `manifest.backend.functions` pode nomeá-las, que era o que o
|
|
35
|
+
* `create function` já mandava escrever e o `provision` recusava), cada diretório pode declarar as
|
|
36
|
+
* próprias dependências npm, e os `defineString` delas entram no `.env.<projectId>`. Em `2.1.1`: o
|
|
37
|
+
* `extensions/` gerado ganha `npm run sdk:refresh`, porque `npm install` NÃO traz o SDK novo — o
|
|
38
|
+
* caminho do tarball não leva versão e o lock guarda a impressão digital do anterior, então o npm diz
|
|
39
|
+
* "up to date" e o escritório segue escrevendo contra um contrato que a instalação não tem mais. Em
|
|
40
|
+
* `2.1.2`: a recusa de uma function reivindicada passa a nomear as DUAS origens possíveis — o pacote e
|
|
41
|
+
* o `extensions/<pasta>/functions/` do escritório —, porque dizer só "o pacote não a contém" manda
|
|
42
|
+
* procurar no lugar errado justamente no caso mais comum. Em `2.1.3`: a montagem deixa os arquivos de
|
|
43
|
+
* TESTE fora do artefato. O `predeploy` do pacote compila `custom/` com o `tsc`, e um `import` de
|
|
44
|
+
* `vitest` ali é `Cannot find module` — a compilação falha e leva embora o deploy de TODAS as functions,
|
|
45
|
+
* não só das da extensão que trouxe o teste. O `AGENTS.md` manda escrever teste ao lado da function;
|
|
46
|
+
* era este lado que faltava. Em `2.1.4`: o `unpack` preserva `functions/node_modules` MOVENDO em vez de
|
|
47
|
+
* copiando — o `cp` recursivo de ~250 pacotes estourava o limite de descritores do processo no macOS
|
|
48
|
+
* (`ENFILE: file table overflow`), de forma intermitente e apontando um diretório qualquer de dentro de
|
|
49
|
+
* uma dependência. A cópia nunca teve para que existir: a origem é apagada em seguida. Em `2.1.5`: o carimbo
|
|
50
|
+
* `backendState.appliedAt` passou a ser gravado como DATA (`timestampValue`) e não como texto — o
|
|
51
|
+
* `stringValue` chegava à tela como string com o tipo de `Date`, e derrubava o botão de atualizar a
|
|
52
|
+
* extensão na gravação seguinte. Em `2.2.0`: o `sourceFile` de uma view passa a aceitar LISTA, e o
|
|
53
|
+
* `AUTHORING.md` que este Console escrevia ganhou a seção que explica a fusão. Em `2.3.0`: ele deixa de
|
|
54
|
+
* escrever documento nenhum em `extensions/`. `AGENTS.md`, `AUTHORING.md`, `tsconfig.json` e `.mcp.json`
|
|
55
|
+
* descrevem o que o KERNEL sabe fazer, e passaram para o SDK, que viaja no mesmo pacote daquelas
|
|
56
|
+
* capacidades — enquanto saíam daqui, um Console de duas versões atrás escrevia, em silêncio, a
|
|
57
|
+
* documentação de outro contrato. O `plugin-init` semeia e delega; o `unpack` traz o SDK novo e manda
|
|
58
|
+
* ele reescrever, que é o gatilho que não existia: aqueles arquivos eram escritos UMA vez e nunca mais.
|
|
59
|
+
* O refresh do SDK perdeu o `--no-save` que tinha: com ele o pacote novo é extraído e o
|
|
60
|
+
* `package-lock.json` fica no anterior, então o `npm install` seguinte reinstala o antigo e desfaz o
|
|
61
|
+
* refresh — em silêncio, que é justamente o que ele existe para evitar. E instala pelo caminho RELATIVO
|
|
62
|
+
* ao diretório, porque o npm reescreve a dependência com o caminho que recebe, e um absoluto no
|
|
63
|
+
* `package.json` mataria a portabilidade que o relativo existe para dar.
|
|
64
|
+
*
|
|
65
|
+
* Em `2.4.0`: o índice composto de uma extensão passa a sobreviver à troca de kernel. Ele era registrado
|
|
66
|
+
* só no `firestore.indexes.json` de dentro de `.microteia/install/`, que o `unpack` apaga inteiro — e o
|
|
67
|
+
* pacote traz apenas a baseline, porque o que uma extensão reivindica sai dela para não viajar até quem
|
|
68
|
+
* não a instalou. O resultado era o `microteia deploy` PERGUNTANDO, no meio de um upgrade, se podia
|
|
69
|
+
* apagar do projeto o índice de toda extensão instalada; e o aviso de índice órfão, que lia a mesma
|
|
70
|
+
* memória, emudecia justamente ali. A memória mudou para `backend.indexes` do `microteia.yaml`, na raiz,
|
|
71
|
+
* e o `unpack` devolve ao arquivo o que ela lembra antes de qualquer comando poder publicá-lo. No mesmo
|
|
72
|
+
* passo, o `kernel.version` do `microteia.yaml` deixa de mentir entre desempacotar e publicar: ele é
|
|
73
|
+
* gravado pelo `unpack`, e `kernel.deployed` — novo — é o que o `deploy` carimba. E `--version` responde
|
|
74
|
+
* as três, porque quem pergunta a versão quase nunca quer a da ferramenta.
|
|
75
|
+
*
|
|
76
|
+
* Em `2.5.0`: a function que ficou para trás numa região abandonada deixa de travar o deploy em
|
|
77
|
+
* silêncio. Trocar a região de uma Cloud Function não move nada — o Firebase cria a nova e deixa a
|
|
78
|
+
* antiga —, e a partir dali o `firebase deploy --only functions:<nome>` recusa: o filtro `--only`
|
|
79
|
+
* casa por id e ignora região, então a cópia velha entra no plano como algo a apagar, e apagar exige
|
|
80
|
+
* uma confirmação que um deploy sem terminal não tem como dar. O `deploy` e o `provision` agora
|
|
81
|
+
* conferem isso ANTES de subir, e `--prune` remove as cópias. Junto vieram as duas razões de o
|
|
82
|
+
* defeito ter levado tanto tempo para ser lido: o motivo impresso era o aviso de descontinuação do
|
|
83
|
+
* Runtime Config — a única coisa que o `firebase` manda pelo stderr, enquanto o erro sai pelo stdout,
|
|
84
|
+
* e a busca era pela última linha casada —, e a segunda passada repetia as oito functions para
|
|
85
|
+
* receber palavra por palavra a mesma recusa. O aviso virou UMA nota no fim, o motivo passou a ser
|
|
86
|
+
* escolhido por quanto informa, e a repetição ficou só para a falha que repetir pode consertar.
|
|
87
|
+
*
|
|
88
|
+
* Ela NÃO acompanha o `version` do produto, e não é obrigada a andar junto do `MINIMUM_CLI_VERSION`:
|
|
89
|
+
* este último só se move quando o PACOTE passa a depender de algo que o Console faz.
|
|
90
|
+
*
|
|
91
|
+
* **A narrativa acima para na `2.5.0`, e é de propósito.** De `2.6.0` em diante a entrada de cada
|
|
92
|
+
* versão é escrita em `cli/CONSOLE-VERSIONS.md` — em inglês, no modelo do git —, porque aquele arquivo
|
|
93
|
+
* viaja com o Console e chega à raiz da instalação a cada `unpack`. Um histórico dentro de um comentário
|
|
94
|
+
* de código nunca chegou a quem instala. Este bloco fica sendo o que ele já era antes de crescer: a
|
|
95
|
+
* política de semver da ferramenta, mais o registro do que motivou as versões até a `2.5.0`.
|
|
96
|
+
*/
|
|
97
|
+
export const CLI_VERSION = '2.7.0';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface RunOptions {
|
|
2
|
+
cwd?: string;
|
|
3
|
+
env?: NodeJS.ProcessEnv;
|
|
4
|
+
}
|
|
5
|
+
export interface CaptureResult {
|
|
6
|
+
code: number;
|
|
7
|
+
stdout: string;
|
|
8
|
+
stderr: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* O erro carrega o comando e a saída para que a falha seja legível sem ter que reproduzir a mão.
|
|
12
|
+
* O `deploy-functions-individually.cjs` roda os deploys com `stdio: 'pipe'` e descarta a saída no
|
|
13
|
+
* catch — a falha vira só "❌ Falha no deploy de X", e descobrir o motivo exige rodar de novo. Aqui
|
|
14
|
+
* a saída capturada é sempre reanexada ao erro.
|
|
15
|
+
*/
|
|
16
|
+
export interface CommandError extends Error {
|
|
17
|
+
command: string;
|
|
18
|
+
code: number;
|
|
19
|
+
stdout: string;
|
|
20
|
+
stderr: string;
|
|
21
|
+
}
|
|
22
|
+
/** Executa herdando stdio: a saída do `firebase` e do `npm` vai direto para o terminal de quem instala. */
|
|
23
|
+
export declare const run: (command: string, args: string[], options?: RunOptions) => Promise<void>;
|
|
24
|
+
/** Executa capturando a saída, para quando ela é DADO (`firebase apps:sdkconfig --json`). */
|
|
25
|
+
export declare const capture: (command: string, args: string[], options?: RunOptions) => Promise<CaptureResult>;
|
|
26
|
+
/** Como `capture`, mas exige sucesso. Use quando a saída é dado e a falha é fatal. */
|
|
27
|
+
export declare const captureOrThrow: (command: string, args: string[], options?: RunOptions) => Promise<string>;
|
|
28
|
+
export declare const sleep: (seconds: number) => Promise<void>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
const commandLine = (command, args) => [command, ...args].join(' ');
|
|
3
|
+
const commandError = (command, args, code, stdout, stderr) => {
|
|
4
|
+
const detail = [stderr.trim(), stdout.trim()].filter((part) => part !== '').join('\n');
|
|
5
|
+
const error = new Error(`Command failed (exit ${code}): ${commandLine(command, args)}${detail === '' ? '' : `\n${detail}`}`);
|
|
6
|
+
error.command = commandLine(command, args);
|
|
7
|
+
error.code = code;
|
|
8
|
+
error.stdout = stdout;
|
|
9
|
+
error.stderr = stderr;
|
|
10
|
+
return error;
|
|
11
|
+
};
|
|
12
|
+
/** Executa herdando stdio: a saída do `firebase` e do `npm` vai direto para o terminal de quem instala. */
|
|
13
|
+
export const run = (command, args, options = {}) => new Promise((resolve, reject) => {
|
|
14
|
+
const child = spawn(command, args, {
|
|
15
|
+
cwd: options.cwd,
|
|
16
|
+
env: options.env === undefined ? process.env : { ...process.env, ...options.env },
|
|
17
|
+
stdio: 'inherit',
|
|
18
|
+
});
|
|
19
|
+
child.on('error', (error) => reject(error));
|
|
20
|
+
child.on('close', (code) => {
|
|
21
|
+
if (code === 0)
|
|
22
|
+
resolve();
|
|
23
|
+
else
|
|
24
|
+
reject(commandError(command, args, code ?? -1, '', ''));
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
/** Executa capturando a saída, para quando ela é DADO (`firebase apps:sdkconfig --json`). */
|
|
28
|
+
export const capture = (command, args, options = {}) => new Promise((resolve, reject) => {
|
|
29
|
+
const child = spawn(command, args, {
|
|
30
|
+
cwd: options.cwd,
|
|
31
|
+
env: options.env === undefined ? process.env : { ...process.env, ...options.env },
|
|
32
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
33
|
+
});
|
|
34
|
+
let stdout = '';
|
|
35
|
+
let stderr = '';
|
|
36
|
+
child.stdout.on('data', (chunk) => { stdout += chunk.toString(); });
|
|
37
|
+
child.stderr.on('data', (chunk) => { stderr += chunk.toString(); });
|
|
38
|
+
child.on('error', (error) => reject(error));
|
|
39
|
+
child.on('close', (code) => resolve({ code: code ?? -1, stdout, stderr }));
|
|
40
|
+
});
|
|
41
|
+
/** Como `capture`, mas exige sucesso. Use quando a saída é dado e a falha é fatal. */
|
|
42
|
+
export const captureOrThrow = async (command, args, options = {}) => {
|
|
43
|
+
const result = await capture(command, args, options);
|
|
44
|
+
if (result.code !== 0) {
|
|
45
|
+
throw commandError(command, args, result.code, result.stdout, result.stderr);
|
|
46
|
+
}
|
|
47
|
+
return result.stdout;
|
|
48
|
+
};
|
|
49
|
+
export const sleep = (seconds) => new Promise((resolve) => { setTimeout(resolve, seconds * 1000); });
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Saída do Console. Ela é lida por QUEM INSTALA — que pode não ser quem empacotou, e pode não estar
|
|
3
|
+
* no Brasil —, então é em inglês, pela mesma razão que o diagnóstico de boot em
|
|
4
|
+
* `infrastructure/firebase/config.ts` é. Comentário continua em português.
|
|
5
|
+
*/
|
|
6
|
+
export declare const heading: (text: string) => void;
|
|
7
|
+
export declare const info: (text: string) => void;
|
|
8
|
+
export declare const step: (index: number, total: number, text: string) => void;
|
|
9
|
+
/**
|
|
10
|
+
* Uma linha de "começando", para o passo que demora. Existe porque só imprimir o resultado deixa a
|
|
11
|
+
* saída MUDA justamente durante a espera: o primeiro deploy de function leva minutos (o Firebase
|
|
12
|
+
* provisiona build e Artifact Registry na primeira vez), e quem instala não tem como distinguir
|
|
13
|
+
* "trabalhando" de "travado".
|
|
14
|
+
*
|
|
15
|
+
* É uma linha inteira, e não um `\r` reescrito depois: o retorno de carro fica bonito no terminal e
|
|
16
|
+
* vira lixo no arquivo de log — que é justamente o que se cola num pedido de ajuda.
|
|
17
|
+
*/
|
|
18
|
+
export declare const pending: (text: string) => void;
|
|
19
|
+
export declare const success: (text: string) => void;
|
|
20
|
+
export declare const warn: (text: string) => void;
|
|
21
|
+
export declare const failure: (text: string) => void;
|
|
22
|
+
export declare const bullets: (items: string[]) => void;
|
|
23
|
+
export declare const blank: () => void;
|
package/lib/shared/ui.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Saída do Console. Ela é lida por QUEM INSTALA — que pode não ser quem empacotou, e pode não estar
|
|
3
|
+
* no Brasil —, então é em inglês, pela mesma razão que o diagnóstico de boot em
|
|
4
|
+
* `infrastructure/firebase/config.ts` é. Comentário continua em português.
|
|
5
|
+
*/
|
|
6
|
+
const RULE = '─'.repeat(64);
|
|
7
|
+
export const heading = (text) => {
|
|
8
|
+
process.stdout.write(`\n${text}\n${RULE}\n`);
|
|
9
|
+
};
|
|
10
|
+
export const info = (text) => {
|
|
11
|
+
process.stdout.write(`${text}\n`);
|
|
12
|
+
};
|
|
13
|
+
export const step = (index, total, text) => {
|
|
14
|
+
process.stdout.write(`\n[${index}/${total}] ${text}\n`);
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Uma linha de "começando", para o passo que demora. Existe porque só imprimir o resultado deixa a
|
|
18
|
+
* saída MUDA justamente durante a espera: o primeiro deploy de function leva minutos (o Firebase
|
|
19
|
+
* provisiona build e Artifact Registry na primeira vez), e quem instala não tem como distinguir
|
|
20
|
+
* "trabalhando" de "travado".
|
|
21
|
+
*
|
|
22
|
+
* É uma linha inteira, e não um `\r` reescrito depois: o retorno de carro fica bonito no terminal e
|
|
23
|
+
* vira lixo no arquivo de log — que é justamente o que se cola num pedido de ajuda.
|
|
24
|
+
*/
|
|
25
|
+
export const pending = (text) => {
|
|
26
|
+
process.stdout.write(` ... ${text}\n`);
|
|
27
|
+
};
|
|
28
|
+
export const success = (text) => {
|
|
29
|
+
process.stdout.write(` ok ${text}\n`);
|
|
30
|
+
};
|
|
31
|
+
export const warn = (text) => {
|
|
32
|
+
process.stderr.write(` warn ${text}\n`);
|
|
33
|
+
};
|
|
34
|
+
export const failure = (text) => {
|
|
35
|
+
process.stderr.write(` fail ${text}\n`);
|
|
36
|
+
};
|
|
37
|
+
export const bullets = (items) => {
|
|
38
|
+
for (const item of items)
|
|
39
|
+
process.stdout.write(` - ${item}\n`);
|
|
40
|
+
};
|
|
41
|
+
export const blank = () => {
|
|
42
|
+
process.stdout.write('\n');
|
|
43
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `AUTHORING.md` — o manual de quem escreve extensão, e o contraponto do `ARCHITECTURE.md`: aquele
|
|
3
|
+
* explica o kernel a quem o altera, este explica o CONTRATO a quem nunca vai vê-lo.
|
|
4
|
+
*
|
|
5
|
+
* Em inglês, e não por preferência: é superfície de produto, entregue a escritórios em qualquer lugar.
|
|
6
|
+
* A regra de idioma do repositório é exatamente essa — o produto fala inglês porque tem de poder falar
|
|
7
|
+
* qualquer idioma; o português é a nossa comunicação interna.
|
|
8
|
+
*
|
|
9
|
+
* Mora no Console (e não como arquivo `.md` solto) para que a versão do kernel e a lista de pontos de
|
|
10
|
+
* extensão sejam CARIMBADAS no texto: um manual que fala de um contrato diferente do instalado é pior
|
|
11
|
+
* que manual nenhum.
|
|
12
|
+
*/
|
|
13
|
+
export interface ExtensionPointRow {
|
|
14
|
+
id: string;
|
|
15
|
+
apiVersion: string;
|
|
16
|
+
allowedImplKinds: string[];
|
|
17
|
+
multiple: boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare const authoringGuide: (kernelVersion: string, points: ExtensionPointRow[], hostModules: string[]) => string;
|