@skip-ai/scanner 0.2.0 → 0.5.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/dist/cli.d.ts +16 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +51 -9
- package/dist/cli.js.map +1 -1
- package/dist/index.js +158 -46
- package/dist/index.js.map +1 -1
- package/dist/reporter.d.ts +13 -1
- package/dist/reporter.d.ts.map +1 -1
- package/dist/reporter.js +5 -2
- package/dist/reporter.js.map +1 -1
- package/dist/scanner/guided-flows.d.ts +23 -0
- package/dist/scanner/guided-flows.d.ts.map +1 -0
- package/dist/scanner/guided-flows.js +61 -0
- package/dist/scanner/guided-flows.js.map +1 -0
- package/dist/scanner/history.d.ts +23 -0
- package/dist/scanner/history.d.ts.map +1 -0
- package/dist/scanner/history.js +38 -0
- package/dist/scanner/history.js.map +1 -0
- package/dist/scanner/score.d.ts +19 -0
- package/dist/scanner/score.d.ts.map +1 -0
- package/dist/scanner/score.js +50 -0
- package/dist/scanner/score.js.map +1 -0
- package/dist/scanner/wcag.d.ts +94 -0
- package/dist/scanner/wcag.d.ts.map +1 -0
- package/dist/scanner/wcag.js +487 -0
- package/dist/scanner/wcag.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
export const HISTORY_FILE = '.skip-history.json';
|
|
4
|
+
const MAX_ENTRIES = 100;
|
|
5
|
+
/**
|
|
6
|
+
* Lê o histórico local do projeto (.skip-history.json).
|
|
7
|
+
* Retorna [] se não existir ou for inválido.
|
|
8
|
+
*/
|
|
9
|
+
export function readHistory(rootDir) {
|
|
10
|
+
const file = path.join(rootDir, HISTORY_FILE);
|
|
11
|
+
if (!fs.existsSync(file))
|
|
12
|
+
return [];
|
|
13
|
+
try {
|
|
14
|
+
const data = JSON.parse(fs.readFileSync(file, 'utf-8'));
|
|
15
|
+
return Array.isArray(data) ? data : [];
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Adiciona uma entrada ao histórico local e retorna o histórico atualizado.
|
|
23
|
+
* Mantém apenas as últimas MAX_ENTRIES entradas.
|
|
24
|
+
*/
|
|
25
|
+
export function appendHistory(rootDir, entry) {
|
|
26
|
+
const history = readHistory(rootDir);
|
|
27
|
+
history.push(entry);
|
|
28
|
+
const trimmed = history.slice(-MAX_ENTRIES);
|
|
29
|
+
const file = path.join(rootDir, HISTORY_FILE);
|
|
30
|
+
try {
|
|
31
|
+
fs.writeFileSync(file, JSON.stringify(trimmed, null, 2), 'utf-8');
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// Falha ao salvar histórico não deve quebrar o scan.
|
|
35
|
+
}
|
|
36
|
+
return trimmed;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=history.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history.js","sourceRoot":"","sources":["../../src/scanner/history.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAcxB,MAAM,CAAC,MAAM,YAAY,GAAG,oBAAoB,CAAC;AACjD,MAAM,WAAW,GAAG,GAAG,CAAC;AAExB;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IAC9C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IACpC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;QACxD,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,OAAe,EAAE,KAAmB;IAChE,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACpB,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC,CAAC;IAE5C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IAC9C,IAAI,CAAC;QACH,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IACpE,CAAC;IAAC,MAAM,CAAC;QACP,qDAAqD;IACvD,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Severity, WcagLevel, WcagViolation } from './wcag.js';
|
|
2
|
+
/** Penalização por severidade (multiplicada pela contagem). */
|
|
3
|
+
export declare const SEVERITY_WEIGHT: Record<Severity, number>;
|
|
4
|
+
export interface ScoreResult {
|
|
5
|
+
/** 0–100 */
|
|
6
|
+
score: number;
|
|
7
|
+
/** Nível WCAG alcançável: A, AA ou AAA. */
|
|
8
|
+
level: WcagLevel;
|
|
9
|
+
/** Total de violações consideradas. */
|
|
10
|
+
total: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Calcula o score de acessibilidade (0–100).
|
|
14
|
+
* Começa em 100 e desconta por severidade, com floor em 0.
|
|
15
|
+
*/
|
|
16
|
+
export declare function calculateScore(violations: WcagViolation[]): ScoreResult;
|
|
17
|
+
/** Cor sugerida para o score (pra usar no dashboard/terminal). */
|
|
18
|
+
export declare function scoreColor(score: number): string;
|
|
19
|
+
//# sourceMappingURL=score.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"score.d.ts","sourceRoot":"","sources":["../../src/scanner/score.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAEpE,+DAA+D;AAC/D,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAMpD,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,YAAY;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,KAAK,EAAE,SAAS,CAAC;IACjB,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,aAAa,EAAE,GAAG,WAAW,CAcvE;AAmBD,kEAAkE;AAClE,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKhD"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/** Penalização por severidade (multiplicada pela contagem). */
|
|
2
|
+
export const SEVERITY_WEIGHT = {
|
|
3
|
+
critical: 12,
|
|
4
|
+
serious: 6,
|
|
5
|
+
moderate: 3,
|
|
6
|
+
minor: 1,
|
|
7
|
+
unknown: 0, // indeterminado (ex.: contraste sem cálculo) não penaliza o score
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Calcula o score de acessibilidade (0–100).
|
|
11
|
+
* Começa em 100 e desconta por severidade, com floor em 0.
|
|
12
|
+
*/
|
|
13
|
+
export function calculateScore(violations) {
|
|
14
|
+
let penalty = 0;
|
|
15
|
+
let total = violations.length;
|
|
16
|
+
for (const v of violations) {
|
|
17
|
+
penalty += SEVERITY_WEIGHT[v.severity];
|
|
18
|
+
}
|
|
19
|
+
// Cada tela sem violações ameniza o desconto (evita score punitivo em projetos grandes)
|
|
20
|
+
const score = Math.max(0, Math.round(100 - penalty));
|
|
21
|
+
const level = determineLevel(violations);
|
|
22
|
+
return { score, level, total };
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Determina o nível WCAG alcançável.
|
|
26
|
+
* Ignora violações `unknown` (indeterminadas), pois não são falhas confirmadas.
|
|
27
|
+
*/
|
|
28
|
+
function determineLevel(violations) {
|
|
29
|
+
const confirmed = violations.filter((v) => v.severity !== 'unknown');
|
|
30
|
+
if (confirmed.length === 0)
|
|
31
|
+
return 'AAA';
|
|
32
|
+
const hasA = confirmed.some((v) => v.level === 'A');
|
|
33
|
+
if (hasA)
|
|
34
|
+
return 'A';
|
|
35
|
+
const hasAA = confirmed.some((v) => v.level === 'AA');
|
|
36
|
+
if (hasAA)
|
|
37
|
+
return 'AA';
|
|
38
|
+
return 'AA';
|
|
39
|
+
}
|
|
40
|
+
/** Cor sugerida para o score (pra usar no dashboard/terminal). */
|
|
41
|
+
export function scoreColor(score) {
|
|
42
|
+
if (score >= 90)
|
|
43
|
+
return 'green';
|
|
44
|
+
if (score >= 70)
|
|
45
|
+
return 'yellow';
|
|
46
|
+
if (score >= 50)
|
|
47
|
+
return 'magenta';
|
|
48
|
+
return 'red';
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=score.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"score.js","sourceRoot":"","sources":["../../src/scanner/score.ts"],"names":[],"mappings":"AAEA,+DAA+D;AAC/D,MAAM,CAAC,MAAM,eAAe,GAA6B;IACvD,QAAQ,EAAE,EAAE;IACZ,OAAO,EAAE,CAAC;IACV,QAAQ,EAAE,CAAC;IACX,KAAK,EAAE,CAAC;IACR,OAAO,EAAE,CAAC,EAAE,kEAAkE;CAC/E,CAAC;AAWF;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,UAA2B;IACxD,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC;IAE9B,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QAC3B,OAAO,IAAI,eAAe,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IACzC,CAAC;IAED,wFAAwF;IACxF,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC;IAErD,MAAM,KAAK,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;IAEzC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AACjC,CAAC;AAED;;;GAGG;AACH,SAAS,cAAc,CAAC,UAA2B;IACjD,MAAM,SAAS,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC;IACrE,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAEzC,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,GAAG,CAAC,CAAC;IACpD,IAAI,IAAI;QAAE,OAAO,GAAG,CAAC;IAErB,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC;IACtD,IAAI,KAAK;QAAE,OAAO,IAAI,CAAC;IAEvB,OAAO,IAAI,CAAC;AACd,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,IAAI,KAAK,IAAI,EAAE;QAAE,OAAO,OAAO,CAAC;IAChC,IAAI,KAAK,IAAI,EAAE;QAAE,OAAO,QAAQ,CAAC;IACjC,IAAI,KAAK,IAAI,EAAE;QAAE,OAAO,SAAS,CAAC;IAClC,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { ScreenMap } from './navigation.js';
|
|
2
|
+
export type Severity = 'critical' | 'serious' | 'moderate' | 'minor' | 'unknown';
|
|
3
|
+
export type WcagLevel = 'A' | 'AA' | 'AAA';
|
|
4
|
+
export interface WcagSourceLocation {
|
|
5
|
+
filePath: string;
|
|
6
|
+
line: number;
|
|
7
|
+
column: number;
|
|
8
|
+
}
|
|
9
|
+
export interface WcagViolation {
|
|
10
|
+
/** Fingerprint estável: hash(ruleId + normalizedFilePath + normalizedSelector). */
|
|
11
|
+
fingerprint: string;
|
|
12
|
+
id: string;
|
|
13
|
+
ruleId: string;
|
|
14
|
+
rule: string;
|
|
15
|
+
level: WcagLevel;
|
|
16
|
+
severity: Severity;
|
|
17
|
+
impact: string;
|
|
18
|
+
title: string;
|
|
19
|
+
description: string;
|
|
20
|
+
/** Tela onde ocorre (para a ocorrência primária). */
|
|
21
|
+
screen: string;
|
|
22
|
+
filePath: string;
|
|
23
|
+
selector: string;
|
|
24
|
+
fix: string;
|
|
25
|
+
wcagUrl: string;
|
|
26
|
+
/** Localização no código-fonte (linha/coluna). */
|
|
27
|
+
source: WcagSourceLocation;
|
|
28
|
+
/** Telas afetadas por esse mesmo problema (deduplicação). */
|
|
29
|
+
affectedScreens: string[];
|
|
30
|
+
/** Quantas ocorrências do mesmo problema foram consolidadas. */
|
|
31
|
+
occurrenceCount: number;
|
|
32
|
+
}
|
|
33
|
+
export interface WcagSummary {
|
|
34
|
+
critical: number;
|
|
35
|
+
serious: number;
|
|
36
|
+
moderate: number;
|
|
37
|
+
minor: number;
|
|
38
|
+
unknown: number;
|
|
39
|
+
}
|
|
40
|
+
export interface WcagChecks {
|
|
41
|
+
total: number;
|
|
42
|
+
passed: number;
|
|
43
|
+
failed: number;
|
|
44
|
+
/** Verificações indeterminadas (ex.: contraste sem cálculo real). */
|
|
45
|
+
needsReview: number;
|
|
46
|
+
}
|
|
47
|
+
export interface WcagReport {
|
|
48
|
+
score: number;
|
|
49
|
+
level: WcagLevel;
|
|
50
|
+
violations: WcagViolation[];
|
|
51
|
+
summary: WcagSummary;
|
|
52
|
+
checks: WcagChecks;
|
|
53
|
+
auditedAt: string;
|
|
54
|
+
rulesVersion: string;
|
|
55
|
+
}
|
|
56
|
+
export declare const WCAG_RULES_VERSION = "1.1.0";
|
|
57
|
+
export interface AuditContext {
|
|
58
|
+
screen: ScreenMap;
|
|
59
|
+
/** Conteúdo do arquivo da tela (lido localmente; NUNCA enviado ao servidor). */
|
|
60
|
+
content: string;
|
|
61
|
+
}
|
|
62
|
+
/** Resultado interno de uma regra: violações + contagem de elementos verificados. */
|
|
63
|
+
interface RuleResult {
|
|
64
|
+
violations: WcagViolation[];
|
|
65
|
+
/** Elementos verificados por essa regra (para contar checks aprovados). */
|
|
66
|
+
elementsChecked: number;
|
|
67
|
+
}
|
|
68
|
+
export interface RuleDef {
|
|
69
|
+
id: string;
|
|
70
|
+
rule: string;
|
|
71
|
+
level: WcagLevel;
|
|
72
|
+
severity: Severity;
|
|
73
|
+
impact: string;
|
|
74
|
+
title: string;
|
|
75
|
+
wcagUrl: string;
|
|
76
|
+
defaultFix: string;
|
|
77
|
+
/** Extrai as violações desta regra no contexto. Recebe a própria regra. */
|
|
78
|
+
check: (ctx: AuditContext, rule: RuleDef) => RuleResult;
|
|
79
|
+
}
|
|
80
|
+
export declare const RULES: RuleDef[];
|
|
81
|
+
/**
|
|
82
|
+
* Aplica todas as regras WCAG a um conjunto de telas.
|
|
83
|
+
* Os conteúdos dos arquivos são lidos localmente e NÃO são enviados ao servidor.
|
|
84
|
+
* Realiza deduplicação por fingerprint: o mesmo problema em N telas vira 1 violação
|
|
85
|
+
* com occurrenceCount = N e affectedScreens = [...].
|
|
86
|
+
*/
|
|
87
|
+
export declare function auditWcag(screens: ScreenMap[], contentsByFile: Record<string, string>): {
|
|
88
|
+
violations: WcagViolation[];
|
|
89
|
+
checks: WcagChecks;
|
|
90
|
+
};
|
|
91
|
+
/** Conta violações por severidade (após deduplicação). */
|
|
92
|
+
export declare function summarize(violations: WcagViolation[]): WcagSummary;
|
|
93
|
+
export {};
|
|
94
|
+
//# sourceMappingURL=wcag.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wcag.d.ts","sourceRoot":"","sources":["../../src/scanner/wcag.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,OAAO,GAAG,SAAS,CAAC;AACjF,MAAM,MAAM,SAAS,GAAG,GAAG,GAAG,IAAI,GAAG,KAAK,CAAC;AAE3C,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,mFAAmF;IACnF,WAAW,EAAE,MAAM,CAAC;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,SAAS,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,MAAM,EAAE,kBAAkB,CAAC;IAC3B,6DAA6D;IAC7D,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,gEAAgE;IAChE,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,CAAC;IACjB,UAAU,EAAE,aAAa,EAAE,CAAC;IAC5B,OAAO,EAAE,WAAW,CAAC;IACrB,MAAM,EAAE,UAAU,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,kBAAkB,UAAU,CAAC;AAI1C,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,SAAS,CAAC;IAClB,gFAAgF;IAChF,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qFAAqF;AACrF,UAAU,UAAU;IAClB,UAAU,EAAE,aAAa,EAAE,CAAC;IAC5B,2EAA2E;IAC3E,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,SAAS,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,KAAK,EAAE,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,KAAK,UAAU,CAAC;CACzD;AA0LD,eAAO,MAAM,KAAK,EAAE,OAAO,EAkQ1B,CAAC;AAeF;;;;;GAKG;AACH,wBAAgB,SAAS,CACvB,OAAO,EAAE,SAAS,EAAE,EACpB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACrC;IAAE,UAAU,EAAE,aAAa,EAAE,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,CAkCrD;AAED,0DAA0D;AAC1D,wBAAgB,SAAS,CAAC,UAAU,EAAE,aAAa,EAAE,GAAG,WAAW,CAIlE"}
|