@orkastery/cli 0.5.0 → 0.5.3
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/adapters/claude-code/README.md +11 -2
- package/adapters/claude-code/agents/ork-master.md +2 -0
- package/adapters/claude-code/commands/master.md +8 -0
- package/adapters/claude-code/commands/onboarding.md +3 -2
- package/adapters/claude-code/commands/ork.md +9 -0
- package/adapters/codex/skills/ork/SKILL.md +18 -3
- package/adapters/hermes/README.md +1 -0
- package/adapters/hermes/bin/ork-network-status.sh +10 -0
- package/adapters/hermes/hermes.plugin.json +1 -0
- package/adapters/hermes/hitl-ingress/__init__.py +2 -2
- package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +6 -5
- package/adapters/openclaw/README.md +35 -4
- package/adapters/openclaw/dist/hitl-ingress.js +2 -2
- package/adapters/openclaw/dist/index.js +47 -12
- package/adapters/openclaw/openclaw.plugin.json +5 -1
- package/adapters/openclaw/package.json +2 -2
- package/adapters/openclaw/src/hitl-ingress.ts +2 -2
- package/adapters/openclaw/src/index.ts +50 -12
- package/assets/orkmind_bridge.py +117 -9
- package/dist/adapters/claude-bg.js +59 -11
- package/dist/adapters/codex.js +4 -3
- package/dist/agents-md.js +1 -0
- package/dist/auditrun.js +6 -2
- package/dist/board.js +16 -14
- package/dist/branch-de-estado.js +66 -10
- package/dist/busca-semantica.js +52 -12
- package/dist/canarios-rm057.js +149 -0
- package/dist/canarios.js +2 -0
- package/dist/ci.js +8 -1
- package/dist/claim-lint.js +3 -1
- package/dist/claims.js +39 -1
- package/dist/company-brain-context.js +158 -22
- package/dist/docs.js +110 -3
- package/dist/doctor-pulse-cron.js +194 -0
- package/dist/doctor-rede.js +149 -0
- package/dist/doctor.js +294 -37
- package/dist/entrega-pr.js +27 -8
- package/dist/esgotamentos.js +181 -0
- package/dist/experiencia.js +12 -2
- package/dist/fabrica-estado.js +35 -9
- package/dist/fabrica-publicar.js +6 -0
- package/dist/fechamento.js +20 -66
- package/dist/fix.js +3 -3
- package/dist/forja.js +31 -7
- package/dist/gates.js +5 -1
- package/dist/hitl-contract.js +59 -4
- package/dist/hitl-curto.js +11 -6
- package/dist/hitl-gates.js +5 -0
- package/dist/hitl-lote.js +5 -2
- package/dist/hitl-resumo.js +33 -1
- package/dist/hitl-tempo-parado.js +201 -0
- package/dist/hitl-texto-livre.js +5 -5
- package/dist/horario.js +27 -0
- package/dist/hosts.js +59 -13
- package/dist/impedimento.js +205 -0
- package/dist/index.js +433 -88
- package/dist/indice-vetorial.js +87 -10
- package/dist/indice.js +3 -1
- package/dist/init.js +28 -5
- package/dist/integracoes-locais.js +4 -0
- package/dist/intelligence-graph-cli.js +267 -30
- package/dist/intelligence-graph-contexto.js +215 -0
- package/dist/intelligence-graph-contract.js +71 -12
- package/dist/intelligence-graph-extract-md.js +41 -23
- package/dist/intelligence-graph-extract-ts.js +181 -11
- package/dist/intelligence-graph-extract.js +186 -9
- package/dist/intelligence-graph-index.js +205 -24
- package/dist/intelligence-graph-parsers.js +6 -4
- package/dist/intelligence-graph-repo.js +10 -0
- package/dist/leases.js +680 -87
- package/dist/ledger-stats.js +15 -1
- package/dist/ledger.js +12 -0
- package/dist/licoes.js +6 -4
- package/dist/liveness.js +2 -1
- package/dist/maestro-runtime.js +7 -1
- package/dist/manifest.js +44 -1
- package/dist/master-nota.js +14 -5
- package/dist/master.js +129 -12
- package/dist/mcp-artifacts.js +36 -3
- package/dist/mcp-git.js +80 -15
- package/dist/mcp-grafo-worker.js +93 -0
- package/dist/mcp-grafo.js +279 -0
- package/dist/mcp-install.js +2 -1
- package/dist/mcp-server.js +23 -1
- package/dist/mcp-ship.js +2 -0
- package/dist/mcp-verify.js +3 -1
- package/dist/memoria.js +48 -6
- package/dist/network-roadmap.js +195 -20
- package/dist/ocupacao.js +17 -5
- package/dist/onboarding.js +9 -4
- package/dist/orkmind.js +90 -5
- package/dist/orquestracao.js +14 -11
- package/dist/parado-no-condutor.js +1089 -0
- package/dist/phase.js +68 -15
- package/dist/policies.js +79 -3
- package/dist/portfolio.js +4 -1
- package/dist/projeto-alvo.js +5 -1
- package/dist/prompts.js +36 -1
- package/dist/prova-ativacao.js +414 -0
- package/dist/pulse-delivery.js +60 -7
- package/dist/pulse.js +99 -3
- package/dist/ratelimit.js +20 -4
- package/dist/recall.js +3 -1
- package/dist/rede-adesao.js +399 -0
- package/dist/rede-forja.js +289 -0
- package/dist/rede-projetos.js +239 -0
- package/dist/rede-status.js +336 -0
- package/dist/rede.js +980 -0
- package/dist/retry.js +69 -2
- package/dist/roadmap-status.js +162 -7
- package/dist/runtime-context.js +5 -1
- package/dist/runtime-profiles.js +22 -1
- package/dist/saida-segura.js +58 -0
- package/dist/session-watcher-claude.js +113 -23
- package/dist/sessoes-contas.js +187 -0
- package/dist/sessoes-inventario.js +6 -12
- package/dist/sessoes.js +87 -3
- package/dist/ship.js +110 -20
- package/dist/thread.js +212 -14
- package/dist/util.js +71 -4
- package/dist/verify.js +2 -2
- package/dist/worktree.js +34 -8
- package/eval/casos/onboarding.json +1 -1
- package/eval/fixtures/fx-pedido-colado/caso.json +20 -0
- package/package.json +7 -3
- package/references/definition-of-done.md +1 -1
- package/skills/README.md +1 -1
- package/skills/core/onboarding/SKILL.md +3 -2
- package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
- package/skills/phases/master-metrics/SKILL.md +8 -3
- package/skills/phases/ship-release/SKILL.md +6 -3
|
@@ -10,6 +10,7 @@ exports.derivarIds = derivarIds;
|
|
|
10
10
|
exports.caminhoValido = caminhoValido;
|
|
11
11
|
exports.validarGrafo = validarGrafo;
|
|
12
12
|
exports.conferirFontes = conferirFontes;
|
|
13
|
+
exports.conferirFontesDoGrafoValidado = conferirFontesDoGrafoValidado;
|
|
13
14
|
/**
|
|
14
15
|
* I-31 KG1 (D1 a D5, D9, D10): contrato `ork.code-artifact-graph/v1` do grafo deterministico
|
|
15
16
|
* de codigo e artefatos.
|
|
@@ -134,19 +135,70 @@ function compararUtf8(a, b) {
|
|
|
134
135
|
}
|
|
135
136
|
return a.length - b.length;
|
|
136
137
|
}
|
|
137
|
-
|
|
138
|
+
const FORMAS = new Map();
|
|
139
|
+
const TETO_DE_FORMAS = 4096;
|
|
140
|
+
let formasGuardadas = 0;
|
|
141
|
+
function formaDe(chaves) {
|
|
142
|
+
const primeira = chaves.length ? chaves[0] : '', candidatas = FORMAS.get(primeira);
|
|
143
|
+
if (candidatas) {
|
|
144
|
+
for (const f of candidatas) {
|
|
145
|
+
if (f.chaves.length !== chaves.length)
|
|
146
|
+
continue;
|
|
147
|
+
let igual = true;
|
|
148
|
+
for (let i = 1; i < chaves.length && igual; i++)
|
|
149
|
+
igual = f.chaves[i] === chaves[i];
|
|
150
|
+
if (igual)
|
|
151
|
+
return f.ordem;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
const ordem = [...chaves].sort(compararUtf8).map((k) => [k, `${JSON.stringify(k)}:`]);
|
|
155
|
+
if (formasGuardadas < TETO_DE_FORMAS) {
|
|
156
|
+
formasGuardadas++;
|
|
157
|
+
if (candidatas)
|
|
158
|
+
candidatas.push({ chaves: [...chaves], ordem });
|
|
159
|
+
else
|
|
160
|
+
FORMAS.set(primeira, [{ chaves: [...chaves], ordem }]);
|
|
161
|
+
}
|
|
162
|
+
return ordem;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* D2: JSON canonico. Chaves na ordem de bytes UTF-8, sem espaco, so numero finito. KG4 (D6): mesma
|
|
166
|
+
* saida da forma original (`map` e `join`, com o buraco de array esparso vazio), sem ordenar as
|
|
167
|
+
* chaves a cada objeto; a ordem de visita, e com ela o primeiro erro, e a mesma.
|
|
168
|
+
*/
|
|
138
169
|
function canonico(v) {
|
|
139
|
-
if (v === null
|
|
140
|
-
return
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
170
|
+
if (v === null)
|
|
171
|
+
return 'null';
|
|
172
|
+
switch (typeof v) {
|
|
173
|
+
case 'string':
|
|
174
|
+
return JSON.stringify(v);
|
|
175
|
+
case 'boolean':
|
|
176
|
+
return v ? 'true' : 'false';
|
|
177
|
+
case 'number':
|
|
178
|
+
return Number.isFinite(v) ? JSON.stringify(v) : falha('grafo.canonico.numero-invalido');
|
|
179
|
+
case 'object': {
|
|
180
|
+
if (Array.isArray(v)) {
|
|
181
|
+
let s = '[';
|
|
182
|
+
for (let i = 0; i < v.length; i++) {
|
|
183
|
+
if (i > 0)
|
|
184
|
+
s += ',';
|
|
185
|
+
if (i in v)
|
|
186
|
+
s += canonico(v[i]);
|
|
187
|
+
}
|
|
188
|
+
return `${s}]`;
|
|
189
|
+
}
|
|
190
|
+
const o = v, forma = formaDe(Object.keys(o));
|
|
191
|
+
let s = '{';
|
|
192
|
+
for (let i = 0; i < forma.length; i++) {
|
|
193
|
+
if (i > 0)
|
|
194
|
+
s += ',';
|
|
195
|
+
s += forma[i][1] + canonico(o[forma[i][0]]);
|
|
196
|
+
}
|
|
197
|
+
return `${s}}`;
|
|
198
|
+
}
|
|
199
|
+
default:
|
|
200
|
+
return falha('grafo.canonico.tipo-invalido');
|
|
148
201
|
}
|
|
149
|
-
return falha('grafo.canonico.tipo-invalido');
|
|
150
202
|
}
|
|
151
203
|
/** SHA-256 sobre os bytes UTF-8 do conteudo canonico. */
|
|
152
204
|
const sha256DoCanonico = (v) => (0, node_crypto_1.createHash)('sha256').update(canonico(v), 'utf8').digest('hex');
|
|
@@ -508,7 +560,14 @@ function linhaDoByte(quebras, o) {
|
|
|
508
560
|
* parser interpretou a relacao certo; essa prova vem da auditoria de arestas do benchmark.
|
|
509
561
|
*/
|
|
510
562
|
function conferirFontes(entrada, fontes) {
|
|
511
|
-
|
|
563
|
+
return conferirFontesDoGrafoValidado(validarGrafo(entrada), fontes);
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* RM-031 KG4 (D6): a conferencia de `conferirFontes` sobre o grafo que `validarGrafo` acabou de
|
|
567
|
+
* devolver, sem validar de novo. So para o produtor que tem esse retorno em maos (a construcao do
|
|
568
|
+
* indice, logo depois da extracao); qualquer outra entrada passa por `conferirFontes`.
|
|
569
|
+
*/
|
|
570
|
+
function conferirFontesDoGrafoValidado(g, fontes) {
|
|
512
571
|
const manifesto = new Map(g.snapshot.source_manifest.map((m) => [m.path, m]));
|
|
513
572
|
for (const [p, f] of fontes) {
|
|
514
573
|
if (!manifesto.has(p))
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.TETO_DE_LINHAS_DE_TABELA = exports.TOKENS_SEM_MENCAO = exports.CHAVES_DO_FRONTMATTER = exports.PADRAO_DE_ID = void 0;
|
|
4
4
|
exports.slugDeTexto = slugDeTexto;
|
|
5
|
+
exports.estruturarMarkdown = estruturarMarkdown;
|
|
5
6
|
exports.extrairMarkdown = extrairMarkdown;
|
|
7
|
+
exports.ligarMarkdown = ligarMarkdown;
|
|
6
8
|
const yaml_1 = require("./yaml");
|
|
7
9
|
/** D6: ID de artefato governado (`RM-031`, `FEAT-018`). */
|
|
8
10
|
exports.PADRAO_DE_ID = /^[A-Z][A-Z0-9]*-[0-9]+$/;
|
|
@@ -329,7 +331,25 @@ const decodificar = (t) => {
|
|
|
329
331
|
return null;
|
|
330
332
|
}
|
|
331
333
|
};
|
|
334
|
+
function estruturarMarkdown(fonte, e) {
|
|
335
|
+
const s = estruturar(fonte, e.aceitaFragmento, e.analisar, e.referencia);
|
|
336
|
+
const mencoes = [];
|
|
337
|
+
for (const l of s.textoDeMencao)
|
|
338
|
+
for (const m of l.texto.matchAll(MENCAO))
|
|
339
|
+
mencoes.push({ inicio: l.inicio + m.index, id: m[0] });
|
|
340
|
+
const d = s.frontmatter?.dados;
|
|
341
|
+
return {
|
|
342
|
+
path: fonte.path, secoes: s.secoes, links: s.links, mencoes,
|
|
343
|
+
frontmatter: s.frontmatter && d
|
|
344
|
+
? { id: typeof d.id === 'string' ? d.id : null, tipo: typeof d.tipo === 'string' && d.tipo !== '', valores: s.frontmatter.valores }
|
|
345
|
+
: null,
|
|
346
|
+
frontmatterInvalido: s.frontmatterInvalido, tabelaGrande: s.tabelaGrande,
|
|
347
|
+
};
|
|
348
|
+
}
|
|
332
349
|
function extrairMarkdown(e) {
|
|
350
|
+
return ligarMarkdown({ ...e, estruturas: e.fontes.map((f) => estruturarMarkdown(f, e)) });
|
|
351
|
+
}
|
|
352
|
+
function ligarMarkdown(e) {
|
|
333
353
|
const saida = { nos: [], arestas: [], diagnosticos: [], lacunas: [] };
|
|
334
354
|
const arquivos = new Set(e.arquivos), diretorios = new Set();
|
|
335
355
|
for (const p of e.arquivos) {
|
|
@@ -337,8 +357,8 @@ function extrairMarkdown(e) {
|
|
|
337
357
|
for (let i = 1; i < partes.length; i++)
|
|
338
358
|
diretorios.add(partes.slice(0, i).join('/'));
|
|
339
359
|
}
|
|
340
|
-
const estruturas = e.
|
|
341
|
-
const slugs = new Map(estruturas.map((s) => [s.
|
|
360
|
+
const estruturas = e.estruturas;
|
|
361
|
+
const slugs = new Map(estruturas.map((s) => [s.path, new Set(s.secoes.map((x) => x.slug).filter((x) => x !== null))]));
|
|
342
362
|
const aresta = (kind, from, to, extrator, metodo, t) => {
|
|
343
363
|
saida.arestas.push({ kind, from, to, extrator, metodo, trecho: t });
|
|
344
364
|
};
|
|
@@ -349,17 +369,17 @@ function extrairMarkdown(e) {
|
|
|
349
369
|
const donos = new Map();
|
|
350
370
|
for (const s of estruturas) {
|
|
351
371
|
if (s.frontmatterInvalido)
|
|
352
|
-
lacuna('frontmatter-invalido', s.
|
|
372
|
+
lacuna('frontmatter-invalido', s.path, 0, null);
|
|
353
373
|
if (s.tabelaGrande)
|
|
354
|
-
lacuna('markdown-tabela-grande', s.
|
|
355
|
-
const nome = s.
|
|
356
|
-
if (!d || nome.startsWith('_') ||
|
|
374
|
+
lacuna('markdown-tabela-grande', s.path, null, null);
|
|
375
|
+
const nome = s.path.slice(s.path.lastIndexOf('/') + 1), d = s.frontmatter;
|
|
376
|
+
if (!d || nome.startsWith('_') || d.id === null || !exports.PADRAO_DE_ID.test(d.id) || !d.tipo)
|
|
357
377
|
continue;
|
|
358
378
|
if (!e.aceitaFragmento(d.id)) {
|
|
359
|
-
lacuna('artefato-recusado', s.
|
|
379
|
+
lacuna('artefato-recusado', s.path, 0, null);
|
|
360
380
|
continue;
|
|
361
381
|
}
|
|
362
|
-
donos.set(d.id, [...(donos.get(d.id) ?? []), s.
|
|
382
|
+
donos.set(d.id, [...(donos.get(d.id) ?? []), s.path]);
|
|
363
383
|
}
|
|
364
384
|
const artefatos = new Map();
|
|
365
385
|
for (const [id, caminhos] of donos) {
|
|
@@ -385,20 +405,15 @@ function extrairMarkdown(e) {
|
|
|
385
405
|
const atual = baixo > 0 ? s.secoes[baixo - 1] : null;
|
|
386
406
|
return atual && atual.slug !== null ? { kind: 'section', path: arquivo.path, fragment: atual.slug } : arquivo;
|
|
387
407
|
};
|
|
388
|
-
const
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
continue;
|
|
395
|
-
}
|
|
396
|
-
aresta('references', origem(inicio), alvo, e.extratorId, 'text-location', { path: fonte.path, inicio, fim: inicio + m[0].length });
|
|
397
|
-
}
|
|
398
|
-
}
|
|
408
|
+
const mencao = (path, inicio, id, origem) => {
|
|
409
|
+
const alvo = artefatos.get(id);
|
|
410
|
+
if (!alvo)
|
|
411
|
+
lacuna('id-sem-artefato', path, inicio, id);
|
|
412
|
+
else
|
|
413
|
+
aresta('references', origem(inicio), alvo, e.extratorId, 'text-location', { path, inicio, fim: inicio + id.length });
|
|
399
414
|
};
|
|
400
415
|
for (const s of estruturas) {
|
|
401
|
-
const path = s.
|
|
416
|
+
const path = s.path, arquivo = { kind: 'file', path, fragment: null };
|
|
402
417
|
for (const x of s.secoes) {
|
|
403
418
|
if (x.slug === null) {
|
|
404
419
|
lacuna('secao-recusada', path, x.inicio, null);
|
|
@@ -444,7 +459,7 @@ function extrairMarkdown(e) {
|
|
|
444
459
|
continue;
|
|
445
460
|
aresta('references', origem, { kind: 'file', path: alvo, fragment: null }, e.extratorMd, 'explicit-link', t);
|
|
446
461
|
}
|
|
447
|
-
const artefato = s.frontmatter &&
|
|
462
|
+
const artefato = s.frontmatter && s.frontmatter.id !== null ? artefatos.get(s.frontmatter.id) : undefined;
|
|
448
463
|
if (artefato && artefato.path === path && s.frontmatter) {
|
|
449
464
|
for (const v of s.frontmatter.valores) {
|
|
450
465
|
const [kind, destino] = exports.CHAVES_DO_FRONTMATTER[v.chave].split(':');
|
|
@@ -459,11 +474,14 @@ function extrairMarkdown(e) {
|
|
|
459
474
|
lacuna('frontmatter-sem-alvo', path, v.inicio, `${v.chave}: ${v.valor}`);
|
|
460
475
|
}
|
|
461
476
|
}
|
|
462
|
-
|
|
477
|
+
for (const m of s.mencoes)
|
|
478
|
+
mencao(path, m.inicio, m.id, (o) => secaoEm(s, arquivo, o));
|
|
463
479
|
}
|
|
464
480
|
for (const f of e.codigo) {
|
|
465
481
|
const arquivo = { kind: 'file', path: f.path, fragment: null };
|
|
466
|
-
|
|
482
|
+
for (const l of linhasDe(f.texto))
|
|
483
|
+
for (const m of l.texto.matchAll(MENCAO))
|
|
484
|
+
mencao(f.path, l.inicio + m.index, m[0], () => arquivo);
|
|
467
485
|
}
|
|
468
486
|
return saida;
|
|
469
487
|
}
|
|
@@ -1,7 +1,33 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.OPCOES_TS_DESCRITAS = void 0;
|
|
4
|
+
exports.casaSonda = casaSonda;
|
|
4
5
|
exports.extrairTypeScript = extrairTypeScript;
|
|
6
|
+
/** Extensoes que o extrator TS le; as do `extensaoDe` do KG2. */
|
|
7
|
+
const EXTENSOES_DO_PROGRAMA = ['.cjs', '.cts', '.js', '.jsx', '.mjs', '.mts', '.ts', '.tsx'];
|
|
8
|
+
const DECLARACOES_TS = ['.d.ts', '.d.mts', '.d.cts'];
|
|
9
|
+
/** A base sem a extensao (o TypeScript troca `.js` por `.ts`, e `.d.ts` conta inteira). */
|
|
10
|
+
function semExtensao(base) {
|
|
11
|
+
const nome = base.slice(base.lastIndexOf('/') + 1);
|
|
12
|
+
for (const d of DECLARACOES_TS)
|
|
13
|
+
if (nome.endsWith(d) && nome.length > d.length)
|
|
14
|
+
return base.slice(0, -d.length);
|
|
15
|
+
const i = nome.lastIndexOf('.');
|
|
16
|
+
return i <= 0 ? base : base.slice(0, base.length - (nome.length - i));
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* RM-031 KG4 (D3): o caminho `p`, novo ou removido, pode mudar uma resolucao de base `base`? O
|
|
20
|
+
* TypeScript e o Node so tentam a base exata, a base com extensao, a base como pasta (`index`,
|
|
21
|
+
* `package.json`) e o mesmo sem a extensao; base vazia (a raiz) casa tudo.
|
|
22
|
+
*/
|
|
23
|
+
function casaSonda(base, p) {
|
|
24
|
+
if (base === '')
|
|
25
|
+
return true;
|
|
26
|
+
if (p === base || p.startsWith(`${base}.`) || p.startsWith(`${base}/`))
|
|
27
|
+
return true;
|
|
28
|
+
const sem = semExtensao(base);
|
|
29
|
+
return sem !== base && (p === sem || p.startsWith(`${sem}.`) || p.startsWith(`${sem}/`));
|
|
30
|
+
}
|
|
5
31
|
/** D2: opcoes fixas do compilador, descritas por nome para entrar no `config_hash`. */
|
|
6
32
|
exports.OPCOES_TS_DESCRITAS = Object.freeze({
|
|
7
33
|
allowJs: true, checkJs: false, esModuleInterop: true, jsx: 'preserve', module: 'commonjs', moduleResolution: 'node10',
|
|
@@ -68,7 +94,12 @@ function caminhoLiteral(de, especificador) {
|
|
|
68
94
|
}
|
|
69
95
|
return r.join('/');
|
|
70
96
|
}
|
|
71
|
-
|
|
97
|
+
/**
|
|
98
|
+
* RM-031 KG4 (D3): modulo vazio fora da raiz virtual (nenhum caminho do manifesto o nomeia, e nada o
|
|
99
|
+
* resolve), so para ler o escopo global do checker sem local que esconda global de mesmo nome.
|
|
100
|
+
*/
|
|
101
|
+
const TEXTO_DO_SINTETICO = 'export {};\n';
|
|
102
|
+
function criarHost(ts, e, sintetico) {
|
|
72
103
|
const arquivos = new Set(e.arquivos), dirs = new Set([e.raiz]);
|
|
73
104
|
for (const p of e.arquivos) {
|
|
74
105
|
const partes = p.split('/');
|
|
@@ -83,6 +114,8 @@ function criarHost(ts, e) {
|
|
|
83
114
|
};
|
|
84
115
|
return {
|
|
85
116
|
getSourceFile: (nome, alvo) => {
|
|
117
|
+
if (nome === sintetico)
|
|
118
|
+
return ts.createSourceFile(nome, TEXTO_DO_SINTETICO, alvo, true);
|
|
86
119
|
const p = dentro(nome), t = p === null ? undefined : e.texto(p);
|
|
87
120
|
return t === undefined ? undefined : ts.createSourceFile(nome, t, alvo, true);
|
|
88
121
|
},
|
|
@@ -109,13 +142,14 @@ function criarHost(ts, e) {
|
|
|
109
142
|
};
|
|
110
143
|
}
|
|
111
144
|
function extrairTypeScript(e, ts) {
|
|
112
|
-
const
|
|
145
|
+
const resultado = { unidades: new Map(), globais: [], faltantes: [] };
|
|
113
146
|
if (e.fontes.length === 0)
|
|
114
|
-
return
|
|
147
|
+
return resultado;
|
|
115
148
|
const absoluto = (p) => `${e.raiz}/${p}`;
|
|
116
149
|
const relativo = (p) => (p.startsWith(`${e.raiz}/`) ? p.slice(e.raiz.length + 1) : null);
|
|
117
|
-
const
|
|
118
|
-
const
|
|
150
|
+
const sintetico = `${e.raiz}-escopo-global.ts`;
|
|
151
|
+
const host = criarHost(ts, e, sintetico), opts = opcoes(ts);
|
|
152
|
+
const programa = ts.createProgram([...e.fontes.map((f) => absoluto(f.path)), sintetico], opts, host);
|
|
119
153
|
const checker = programa.getTypeChecker();
|
|
120
154
|
const cache = ts.createModuleResolutionCache(e.raiz, (p) => p, opts);
|
|
121
155
|
const arquivos = new Set(e.arquivos);
|
|
@@ -473,41 +507,117 @@ function extrairTypeScript(e, ts) {
|
|
|
473
507
|
* lanca erro). Especificador nao relativo vai a node_modules e fica sem alvo. Em ESM o especificador e
|
|
474
508
|
* URL: so liga o que a URL le como o caminho literal (`urlComoCaminho`).
|
|
475
509
|
*/
|
|
510
|
+
/** KG4 (D3): `bruto` e o caminho que a resolucao do Node achou, antes de conferir se ele carrega. */
|
|
476
511
|
function alvoNoNode(de, chave) {
|
|
477
512
|
const esp = especificadorDaChave(chave), formato = formatoDe(de);
|
|
478
513
|
const relativoAoArquivo = esp === '.' || esp === '..' || esp.startsWith('./') || esp.startsWith('../');
|
|
479
514
|
if (!relativoAoArquivo || formato === 'falha')
|
|
480
|
-
return null;
|
|
515
|
+
return { alvo: null, bruto: null };
|
|
481
516
|
let alvo;
|
|
482
517
|
if (chave[0] === 'd' || (chave[0] === 'v' && formato === 'esm'))
|
|
483
518
|
alvo = urlComoCaminho(esp) ? resolverNode(de, esp, true) : null;
|
|
484
519
|
else if (chave[0] === 'r' && formato === 'cjs' && !escopoDe(de).invalido)
|
|
485
520
|
alvo = resolverNode(de, esp, false);
|
|
486
521
|
else
|
|
487
|
-
return null;
|
|
488
|
-
return alvo !== null && carregavel(alvo, chave[0] !== 'r') ? alvo : null;
|
|
522
|
+
return { alvo: null, bruto: null };
|
|
523
|
+
return { alvo: alvo !== null && carregavel(alvo, chave[0] !== 'r') ? alvo : null, bruto: alvo };
|
|
489
524
|
}
|
|
525
|
+
/**
|
|
526
|
+
* KG4 (D3): os nomes de modulo que o TypeScript coleta do arquivo para o programa (import, export,
|
|
527
|
+
* require, import(), tipo importado, inclusive no JSDoc de JavaScript). Sem a lista, nao ha como provar
|
|
528
|
+
* as dependencias e a extracao falha, em vez de reaproveitar sem prova.
|
|
529
|
+
*/
|
|
530
|
+
const nomesDeModulo = (sf) => {
|
|
531
|
+
const imports = sf.imports;
|
|
532
|
+
if (!Array.isArray(imports))
|
|
533
|
+
throw new Error('extracao.interna.typescript-sem-imports');
|
|
534
|
+
return imports.map((n) => n.text);
|
|
535
|
+
};
|
|
536
|
+
/** Caminho relativo a raiz por segmentos; `null` se sobe acima da raiz. */
|
|
537
|
+
const normalizar = (partes) => {
|
|
538
|
+
const r = [];
|
|
539
|
+
for (const p of partes) {
|
|
540
|
+
if (p === '' || p === '.')
|
|
541
|
+
continue;
|
|
542
|
+
if (p === '..') {
|
|
543
|
+
if (!r.length)
|
|
544
|
+
return null;
|
|
545
|
+
r.pop();
|
|
546
|
+
}
|
|
547
|
+
else
|
|
548
|
+
r.push(p);
|
|
549
|
+
}
|
|
550
|
+
return r.join('/');
|
|
551
|
+
};
|
|
552
|
+
/** KG4 (D3): base de uma resolucao relativa como o TypeScript a ve (a barra invertida tambem separa); `null` se nao e relativa ou sai da raiz. */
|
|
553
|
+
const baseDaSonda = (de, nome) => (/^\.\.?($|[\\/])/.test(nome) ? normalizar([...de.split('/').slice(0, -1), ...nome.replace(/\\/g, '/').split('/')]) : null);
|
|
554
|
+
/** `/// <reference path>`: relativo ao arquivo, com ou sem `./`. */
|
|
555
|
+
const baseDaReferencia = (de, nome) => (nome.startsWith('/') || nome.startsWith('\\') ? null : normalizar([...de.split('/').slice(0, -1), ...nome.replace(/\\/g, '/').split('/')]));
|
|
556
|
+
/**
|
|
557
|
+
* KG4 (D3): a resolucao de uma pasta le o `package.json` dela, e `main`, `types` e `typings` podem
|
|
558
|
+
* apontar para fora da base: os alvos entram como sondas, tambem com a barra invertida trocada, como o
|
|
559
|
+
* TypeScript le. `typesVersions` remapeia qualquer caminho, e entao a sonda casa tudo; o mesmo vale
|
|
560
|
+
* para `package.json` que nao e JSON estrito (CHECK, A1: o TypeScript aceita comentario e virgula
|
|
561
|
+
* final). Mudanca no proprio `package.json` extrai o TypeScript inteiro.
|
|
562
|
+
*/
|
|
563
|
+
const sondasDoPacote = (base, sondas) => {
|
|
564
|
+
for (const pasta of new Set([base, semExtensao(base)])) {
|
|
565
|
+
const arquivo = junta(pasta, 'package.json');
|
|
566
|
+
if (!arquivos.has(arquivo))
|
|
567
|
+
continue;
|
|
568
|
+
const v = lerJson(arquivo);
|
|
569
|
+
if (v === null || typeof v !== 'object' || Array.isArray(v)) {
|
|
570
|
+
sondas.add('');
|
|
571
|
+
continue;
|
|
572
|
+
}
|
|
573
|
+
const o = v;
|
|
574
|
+
if ('typesVersions' in o)
|
|
575
|
+
sondas.add('');
|
|
576
|
+
for (const campo of ['main', 'types', 'typings']) {
|
|
577
|
+
const alvo = o[campo];
|
|
578
|
+
if (typeof alvo !== 'string' || !alvo)
|
|
579
|
+
continue;
|
|
580
|
+
for (const forma of new Set([alvo, alvo.replace(/\\/g, '/')])) {
|
|
581
|
+
const t = forma.startsWith('/') ? null : caminhoLiteral(arquivo, `./${forma}`);
|
|
582
|
+
if (t !== null)
|
|
583
|
+
sondas.add(t);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
const sondar = (base, sondas) => {
|
|
589
|
+
if (base === null)
|
|
590
|
+
return;
|
|
591
|
+
sondas.add(base);
|
|
592
|
+
sondasDoPacote(base, sondas);
|
|
593
|
+
};
|
|
490
594
|
// Primeira passada, em todos os arquivos: o alvo de cada especificador para o compilador e para o
|
|
491
595
|
// runtime. Onde divergem, a aresta de import vai ao que roda e nenhum simbolo passa por ali (D3).
|
|
492
596
|
const resolucoes = new Map();
|
|
493
597
|
const divergentesGlobais = new Set();
|
|
598
|
+
// KG4 (D3): por fonte, os caminhos a que as referencias de modulo resolvem e as bases das relativas.
|
|
599
|
+
const dependenciasDe = new Map(), sondasDe = new Map();
|
|
494
600
|
for (const fonte of e.fontes) {
|
|
495
601
|
const sf = programa.getSourceFile(absoluto(fonte.path));
|
|
496
602
|
if (!sf || sf.fileName !== absoluto(fonte.path))
|
|
497
603
|
continue;
|
|
498
604
|
const ext = extensao(fonte.path), mapa = new Map();
|
|
605
|
+
const dependencias = new Set(), sondas = new Set();
|
|
499
606
|
for (const chave of especificadoresEm(sf)) {
|
|
500
607
|
if (mapa.has(chave))
|
|
501
608
|
continue;
|
|
502
609
|
const esp = especificadorDaChave(chave), doCompilador = resolver(esp, fonte.path);
|
|
610
|
+
sondar(baseDaSonda(fonte.path, esp), sondas);
|
|
503
611
|
let alvo = doCompilador, divergente = false;
|
|
504
612
|
// Import so de tipo nao roda: vale o que o compilador liga. Fonte JavaScript roda no Node, e onde
|
|
505
613
|
// o Node falha (formato, escopo, alvo que nao carrega) nao ha aresta.
|
|
506
614
|
if (chave[0] !== 't' && EXTENSOES_JS.includes(ext)) {
|
|
507
615
|
const runtime = alvoNoNode(fonte.path, chave);
|
|
508
|
-
if (runtime !==
|
|
616
|
+
if (runtime.bruto !== null)
|
|
617
|
+
dependencias.add(runtime.bruto);
|
|
618
|
+
if (runtime.alvo !== doCompilador) {
|
|
509
619
|
divergente = true;
|
|
510
|
-
alvo = runtime;
|
|
620
|
+
alvo = runtime.alvo;
|
|
511
621
|
}
|
|
512
622
|
}
|
|
513
623
|
const impl = chave[0] !== 't' && !divergente && doCompilador !== null ? implementacaoDe(doCompilador) : null;
|
|
@@ -515,11 +625,45 @@ function extrairTypeScript(e, ts) {
|
|
|
515
625
|
divergente = true;
|
|
516
626
|
alvo = impl;
|
|
517
627
|
}
|
|
628
|
+
for (const x of [doCompilador, alvo])
|
|
629
|
+
if (x !== null)
|
|
630
|
+
dependencias.add(x);
|
|
518
631
|
mapa.set(chave, { alvo, divergente, doCompilador });
|
|
519
632
|
if (divergente)
|
|
520
633
|
divergentesGlobais.add(`${fonte.path}\u0000${chave}`);
|
|
521
634
|
}
|
|
635
|
+
// O que o TypeScript coleta e a varredura nao ve (tipo importado no JSDoc) tambem liga o checker a outro arquivo.
|
|
636
|
+
for (const nome of nomesDeModulo(sf)) {
|
|
637
|
+
sondar(baseDaSonda(fonte.path, nome), sondas);
|
|
638
|
+
const alvo = resolver(nome, fonte.path);
|
|
639
|
+
if (alvo !== null)
|
|
640
|
+
dependencias.add(alvo);
|
|
641
|
+
}
|
|
642
|
+
for (const r of sf.referencedFiles) {
|
|
643
|
+
const base = baseDaReferencia(fonte.path, r.fileName);
|
|
644
|
+
if (base === null)
|
|
645
|
+
continue;
|
|
646
|
+
sondas.add(base);
|
|
647
|
+
for (const x of [base, `${base}.ts`, `${base}.tsx`, `${base}.d.ts`, `${base}.js`, `${base}.jsx`])
|
|
648
|
+
if (arquivos.has(x))
|
|
649
|
+
dependencias.add(x);
|
|
650
|
+
}
|
|
522
651
|
resolucoes.set(fonte.path, mapa);
|
|
652
|
+
dependenciasDe.set(fonte.path, dependencias);
|
|
653
|
+
sondasDe.set(fonte.path, sondas);
|
|
654
|
+
}
|
|
655
|
+
// KG4 (D3): no programa parcial, todo alvo TS/JS das fontes precisa ser fonte, para ter formato e
|
|
656
|
+
// resolucoes; senao o caminho cresce e a extracao recomeca.
|
|
657
|
+
if (e.emitir) {
|
|
658
|
+
const noPrograma = new Set(e.fontes.map((f) => f.path)), faltantes = new Set();
|
|
659
|
+
for (const dependencias of dependenciasDe.values()) {
|
|
660
|
+
for (const d of dependencias) {
|
|
661
|
+
if (!noPrograma.has(d) && EXTENSOES_DO_PROGRAMA.includes(extensao(d)) && e.texto(d) !== undefined)
|
|
662
|
+
faltantes.add(d);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
if (faltantes.size)
|
|
666
|
+
return { ...resultado, faltantes: [...faltantes].sort() };
|
|
523
667
|
}
|
|
524
668
|
// B-N2: modulo que importa, direto ou por outro modulo, um arquivo com import divergente pode reexportar
|
|
525
669
|
// o que o compilador liga e o runtime nao (`export *`, `module.exports = require(...)`). O fecho segue
|
|
@@ -577,6 +721,13 @@ function extrairTypeScript(e, ts) {
|
|
|
577
721
|
return false;
|
|
578
722
|
}
|
|
579
723
|
for (const fonte of e.fontes) {
|
|
724
|
+
if (e.emitir && !e.emitir.has(fonte.path))
|
|
725
|
+
continue;
|
|
726
|
+
// KG4 (D2): os achados ficam por arquivo, com as dependencias e as sondas, para as unidades do indice.
|
|
727
|
+
const saida = { nos: [], arestas: [], diagnosticos: [], lacunas: [] };
|
|
728
|
+
resultado.unidades.set(fonte.path, {
|
|
729
|
+
dependencias: [...(dependenciasDe.get(fonte.path) ?? [])].sort(), sondas: [...(sondasDe.get(fonte.path) ?? [])].sort(), achados: saida,
|
|
730
|
+
});
|
|
580
731
|
const sf = programa.getSourceFile(absoluto(fonte.path));
|
|
581
732
|
// Fonte que o compilador trocou por outra (pacote duplicado) nao e lida: o no seria do outro arquivo.
|
|
582
733
|
if (!sf || sf.fileName !== absoluto(fonte.path)) {
|
|
@@ -806,5 +957,24 @@ function extrairTypeScript(e, ts) {
|
|
|
806
957
|
for (const st of sf.statements)
|
|
807
958
|
visitar(st, null);
|
|
808
959
|
}
|
|
809
|
-
|
|
960
|
+
// KG4 (D3): quem declara no escopo global (visto de um modulo vazio, sem local que o esconda) ou
|
|
961
|
+
// aumenta modulo alcanca arquivo que nao o importa; mudanca nele reextrai o TypeScript inteiro.
|
|
962
|
+
const doEscopoGlobal = programa.getSourceFile(sintetico);
|
|
963
|
+
if (!doEscopoGlobal)
|
|
964
|
+
throw new Error('extracao.interna.escopo-global');
|
|
965
|
+
const globais = new Set();
|
|
966
|
+
for (const simbolo of checker.getSymbolsInScope(doEscopoGlobal, -1)) {
|
|
967
|
+
for (const d of simbolo.declarations ?? []) {
|
|
968
|
+
const p = relativo(d.getSourceFile().fileName);
|
|
969
|
+
if (p !== null)
|
|
970
|
+
globais.add(p);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
for (const sf of programa.getSourceFiles()) {
|
|
974
|
+
const aumentos = sf.moduleAugmentations, p = relativo(sf.fileName);
|
|
975
|
+
if (p !== null && aumentos && aumentos.length)
|
|
976
|
+
globais.add(p);
|
|
977
|
+
}
|
|
978
|
+
resultado.globais = [...globais].sort();
|
|
979
|
+
return resultado;
|
|
810
980
|
}
|