@skip-ai/scanner 0.5.0 → 0.6.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/sam.d.ts ADDED
@@ -0,0 +1,120 @@
1
+ import type { ProjectSignature } from './detector.js';
2
+ import type { RouteEntry } from './scanner/routes.js';
3
+ import type { ComponentEntry } from './scanner/components.js';
4
+ import type { ApiCallEntry } from './scanner/api-calls.js';
5
+ import type { GuidedFlow } from './scanner/guided-flows.js';
6
+ import type { NavigationMap } from './scanner/navigation.js';
7
+ import { type FileManifestEntry } from './artifacts.js';
8
+ export type SamStatus = 'complete' | 'partial' | 'missing' | 'failed';
9
+ export type SamEntityType = 'ROUTE' | 'SCREEN' | 'COMPONENT' | 'FORM' | 'INPUT' | 'BUTTON' | 'LINK' | 'IMAGE' | 'HEADING' | 'LANDMARK' | 'DIALOG' | 'MENU' | 'TABLE' | 'API' | 'FLOW' | 'BUSINESS_RULE' | 'ACTION' | 'STATE';
10
+ export interface SamEntity {
11
+ id: string;
12
+ type: SamEntityType;
13
+ name: string;
14
+ slug: string;
15
+ semanticLabels: string[];
16
+ description: string;
17
+ accessibilityHint: string;
18
+ confidence: number;
19
+ source?: {
20
+ filePath: string;
21
+ line: number;
22
+ column: number;
23
+ };
24
+ selector?: string;
25
+ accessibleName?: string;
26
+ role?: string;
27
+ evidence: Array<{
28
+ type: string;
29
+ value: string;
30
+ filePath?: string;
31
+ line?: number;
32
+ column?: number;
33
+ }>;
34
+ metadata: Record<string, unknown>;
35
+ }
36
+ export interface SamRelationship {
37
+ id: string;
38
+ type: 'CONTAINS' | 'RENDERS' | 'NAVIGATES_TO' | 'TRIGGERS' | 'SUBMITS' | 'FILLS' | 'CALLS' | 'USES' | 'IMPORTS' | 'SHARES_COMPONENT' | 'BELONGS_TO_FLOW' | 'DEPENDS_ON' | 'CHANGES_STATE';
39
+ fromEntityId: string;
40
+ toEntityId: string;
41
+ confidence: number;
42
+ evidence: Array<{
43
+ type: string;
44
+ value: string;
45
+ }>;
46
+ metadata: Record<string, unknown>;
47
+ }
48
+ export interface SemanticApplicationMap {
49
+ schemaVersion: '1.0.0';
50
+ artifactType: 'semantic-map';
51
+ filename: '.skip-sam.json';
52
+ scanId: string;
53
+ status: SamStatus;
54
+ generatedAt: string;
55
+ sourceManifestSha256: string;
56
+ generator: {
57
+ scannerVersion: string;
58
+ skillVersion: string;
59
+ strategy: 'static';
60
+ };
61
+ project: {
62
+ name: string;
63
+ framework: string;
64
+ language: string;
65
+ packageManager: string;
66
+ rootPath: string;
67
+ monorepo: boolean;
68
+ };
69
+ stats: Record<string, number>;
70
+ coverage: Record<string, number>;
71
+ entities: SamEntity[];
72
+ relationships: SamRelationship[];
73
+ screens: unknown[];
74
+ sharedComponents: unknown[];
75
+ warnings: string[];
76
+ missingData: Array<{
77
+ type: string;
78
+ entityId?: string;
79
+ reason: string;
80
+ }>;
81
+ validation: {
82
+ valid: boolean;
83
+ errors: string[];
84
+ warnings: string[];
85
+ };
86
+ }
87
+ export interface SamBuildInput {
88
+ scanId: string;
89
+ startedAt: string;
90
+ rootDir: string;
91
+ signature: ProjectSignature;
92
+ routes: RouteEntry[];
93
+ components: ComponentEntry[];
94
+ apiCalls: ApiCallEntry[];
95
+ navigationMap: NavigationMap;
96
+ guidedFlows: GuidedFlow[];
97
+ fileManifest: FileManifestEntry[];
98
+ sourceManifestSha256: string;
99
+ projectName: string;
100
+ }
101
+ export interface SamValidationContext {
102
+ currentScanId?: string;
103
+ scanStartedAt?: string;
104
+ fileManifest?: FileManifestEntry[];
105
+ sourceManifestSha256?: string;
106
+ expectedFilename?: string;
107
+ }
108
+ export declare function createFileManifest(rootDir: string, ignorePatterns?: string[]): Promise<{
109
+ files: FileManifestEntry[];
110
+ sha256: string;
111
+ }>;
112
+ export declare function buildSemanticApplicationMap(input: SamBuildInput): SemanticApplicationMap;
113
+ export declare function validateSamArtifact(payload: Record<string, unknown>, context?: SamValidationContext): {
114
+ valid: boolean;
115
+ errors: string[];
116
+ warnings: string[];
117
+ artifactType: "semantic-map";
118
+ };
119
+ export declare function writeSkipSamFile(outputDir: string, sam: SemanticApplicationMap): string;
120
+ //# sourceMappingURL=sam.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sam.d.ts","sourceRoot":"","sources":["../src/sam.ts"],"names":[],"mappings":"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"}
package/dist/sam.js ADDED
@@ -0,0 +1,361 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { glob } from 'glob';
4
+ import { DEFAULT_IGNORE_PATTERNS, toRelative } from './scanner/ignore.js';
5
+ import { CLI_VERSION } from './reporter.js';
6
+ import { sha256File, sha256Json, toPosixPath } from './artifacts.js';
7
+ function slugify(value) {
8
+ return (value
9
+ .toLowerCase()
10
+ .normalize('NFD')
11
+ .replace(/[\u0300-\u036f]/g, '')
12
+ .replace(/[^a-z0-9]+/g, '-')
13
+ .replace(/^-+|-+$/g, '') || 'item');
14
+ }
15
+ function stableId(prefix, value) {
16
+ return `${prefix}-${slugify(value)}`.slice(0, 120);
17
+ }
18
+ function findLocation(rootDir, relativeFile, needle) {
19
+ const abs = path.join(rootDir, relativeFile);
20
+ if (!fs.existsSync(abs))
21
+ return undefined;
22
+ const content = fs.readFileSync(abs, 'utf-8');
23
+ const idx = needle ? content.indexOf(needle) : -1;
24
+ const safeIdx = idx >= 0 ? idx : 0;
25
+ const before = content.slice(0, safeIdx);
26
+ const lines = before.split(/\r?\n/);
27
+ return { filePath: toPosixPath(relativeFile), line: lines.length, column: lines[lines.length - 1].length + 1 };
28
+ }
29
+ function actionType(action) {
30
+ if (action.kind === 'fill')
31
+ return 'INPUT';
32
+ if (action.kind === 'navigation')
33
+ return 'LINK';
34
+ if (action.kind === 'submit')
35
+ return 'BUTTON';
36
+ return 'ACTION';
37
+ }
38
+ function roleFor(action) {
39
+ if (action.kind === 'fill')
40
+ return action.inputType === 'textarea' ? 'textbox' : 'textbox';
41
+ if (action.kind === 'navigation')
42
+ return 'link';
43
+ return 'button';
44
+ }
45
+ function addRelationship(relationships, type, fromEntityId, toEntityId, value) {
46
+ relationships.push({
47
+ id: stableId(`rel-${type.toLowerCase()}`, `${fromEntityId}-${toEntityId}-${relationships.length}`),
48
+ type,
49
+ fromEntityId,
50
+ toEntityId,
51
+ confidence: 0.9,
52
+ evidence: [{ type: 'static-analysis', value }],
53
+ metadata: {},
54
+ });
55
+ }
56
+ export async function createFileManifest(rootDir, ignorePatterns = []) {
57
+ const files = await glob('**/*', {
58
+ cwd: rootDir,
59
+ absolute: true,
60
+ nodir: true,
61
+ ignore: [...DEFAULT_IGNORE_PATTERNS, '.skip-sam.json', '.skip-sam.json.tmp', '.skip-report.json', '.skip-wcag-audit.json', 'scan-file-manifest.json', 'upload-manifest.json', 'scan-validation.json', ...ignorePatterns],
62
+ });
63
+ const entries = files
64
+ .map((file) => {
65
+ const stat = fs.statSync(file);
66
+ return { path: toRelative(rootDir, file), sizeBytes: stat.size, sha256: sha256File(file) };
67
+ })
68
+ .sort((a, b) => a.path.localeCompare(b.path));
69
+ return { files: entries, sha256: sha256Json(entries) };
70
+ }
71
+ export function buildSemanticApplicationMap(input) {
72
+ const entities = [];
73
+ const relationships = [];
74
+ const missingData = [];
75
+ const routeEntityByRoute = new Map();
76
+ const screenEntityByRoute = new Map();
77
+ const apiEntityIds = [];
78
+ for (const route of input.routes) {
79
+ const id = stableId('route', route.logicalRoute || 'root');
80
+ routeEntityByRoute.set(route.logicalRoute, id);
81
+ entities.push({
82
+ id,
83
+ type: 'ROUTE',
84
+ name: route.logicalRoute,
85
+ slug: slugify(route.logicalRoute || 'root'),
86
+ semanticLabels: [route.logicalRoute, `ir para ${route.logicalRoute}`],
87
+ description: `Rota ${route.logicalRoute}`,
88
+ accessibilityHint: `Navegue para ${route.logicalRoute}`,
89
+ confidence: 0.95,
90
+ source: findLocation(input.rootDir, route.physicalPath, 'export'),
91
+ role: 'route',
92
+ evidence: [{ type: 'route-file', value: route.physicalPath, filePath: route.physicalPath }],
93
+ metadata: { physicalPath: route.physicalPath, exportedComponents: route.exportedComponents },
94
+ });
95
+ }
96
+ for (const screen of input.navigationMap.screens) {
97
+ const id = stableId('screen', screen.route);
98
+ screenEntityByRoute.set(screen.route, id);
99
+ entities.push({
100
+ id,
101
+ type: 'SCREEN',
102
+ name: screen.title,
103
+ slug: slugify(screen.route || screen.title),
104
+ semanticLabels: [screen.title, screen.route],
105
+ description: screen.description ?? `Tela ${screen.title}`,
106
+ accessibilityHint: `Tela ${screen.title}`,
107
+ confidence: 0.95,
108
+ source: findLocation(input.rootDir, screen.filePath, '<'),
109
+ role: 'main',
110
+ evidence: [{ type: 'screen-file', value: screen.filePath, filePath: screen.filePath }],
111
+ metadata: { route: screen.route },
112
+ });
113
+ const routeId = routeEntityByRoute.get(screen.route);
114
+ if (routeId)
115
+ addRelationship(relationships, 'CONTAINS', routeId, id, `${screen.route} contém ${screen.title}`);
116
+ for (const action of screen.actions) {
117
+ const type = actionType(action);
118
+ const actionId = stableId(type.toLowerCase(), `${screen.route}-${action.id}-${action.label}`);
119
+ const source = findLocation(input.rootDir, screen.filePath, action.anchorId ?? action.label);
120
+ if (!source)
121
+ missingData.push({ type: 'source-location', entityId: actionId, reason: 'Não foi possível determinar linha e coluna.' });
122
+ entities.push({
123
+ id: actionId,
124
+ type,
125
+ name: action.label,
126
+ slug: slugify(`${screen.route}-${action.label}-${action.kind}`),
127
+ semanticLabels: [action.label, `${action.kind} ${action.label}`],
128
+ description: `Ação ${action.kind} em ${screen.title}`,
129
+ accessibilityHint: action.warning ?? `${action.label}`,
130
+ confidence: action.confidence === 'high' ? 0.95 : action.confidence === 'medium' ? 0.75 : 0.45,
131
+ source,
132
+ selector: action.selector,
133
+ accessibleName: action.label,
134
+ role: roleFor(action),
135
+ evidence: [{ type: 'jsx-element', value: action.selector, filePath: screen.filePath, line: source?.line, column: source?.column }],
136
+ metadata: {
137
+ route: screen.route,
138
+ cssSelector: action.selector,
139
+ componentType: type.toLowerCase(),
140
+ kind: action.kind,
141
+ intent: action.intent,
142
+ inputType: action.inputType,
143
+ inputName: action.inputName,
144
+ targetRoute: action.targetRoute,
145
+ },
146
+ });
147
+ addRelationship(relationships, 'CONTAINS', id, actionId, `${screen.title} contém ${action.label}`);
148
+ if (action.kind === 'fill')
149
+ addRelationship(relationships, 'FILLS', actionId, id, `${action.label} preenche dados em ${screen.title}`);
150
+ if (action.kind === 'submit')
151
+ addRelationship(relationships, 'SUBMITS', actionId, id, `${action.label} submete ${screen.title}`);
152
+ if (action.targetRoute) {
153
+ const target = routeEntityByRoute.get(action.targetRoute) ?? screenEntityByRoute.get(action.targetRoute);
154
+ if (target)
155
+ addRelationship(relationships, 'NAVIGATES_TO', actionId, target, `${action.label} navega para ${action.targetRoute}`);
156
+ }
157
+ }
158
+ }
159
+ for (const component of input.components) {
160
+ const id = stableId('component', `${component.filepath}-${component.name}`);
161
+ const isShared = !input.navigationMap.screens.some((screen) => screen.filePath === component.filepath);
162
+ entities.push({
163
+ id,
164
+ type: component.hasForm ? 'FORM' : 'COMPONENT',
165
+ name: component.name,
166
+ slug: slugify(component.name),
167
+ semanticLabels: [component.name],
168
+ description: `Componente ${component.name}`,
169
+ accessibilityHint: component.hasForm ? 'Formulário detectado' : 'Componente de interface',
170
+ confidence: 0.8,
171
+ source: findLocation(input.rootDir, component.filepath, component.name),
172
+ role: component.hasForm ? 'form' : 'component',
173
+ evidence: [{ type: 'component-export', value: component.name, filePath: component.filepath }],
174
+ metadata: { props: component.props, events: component.events, shared: isShared },
175
+ });
176
+ for (const screen of input.navigationMap.screens.filter((screen) => screen.filePath === component.filepath)) {
177
+ const screenId = screenEntityByRoute.get(screen.route);
178
+ if (screenId)
179
+ addRelationship(relationships, 'RENDERS', screenId, id, `${screen.title} renderiza ${component.name}`);
180
+ }
181
+ }
182
+ for (const api of input.apiCalls) {
183
+ const id = stableId('api', `${api.method}-${api.endpoint}`);
184
+ apiEntityIds.push(id);
185
+ entities.push({
186
+ id,
187
+ type: 'API',
188
+ name: `${api.method} ${api.endpoint}`,
189
+ slug: slugify(`${api.method}-${api.endpoint}`),
190
+ semanticLabels: [api.endpoint, `${api.method} ${api.endpoint}`],
191
+ description: `Chamada de API ${api.method} ${api.endpoint}`,
192
+ accessibilityHint: 'Chamada de API usada por ações da interface',
193
+ confidence: 0.8,
194
+ source: findLocation(input.rootDir, api.filepath, api.endpoint),
195
+ role: 'api',
196
+ evidence: [{ type: 'api-call', value: api.endpoint, filePath: api.filepath }],
197
+ metadata: { method: api.method, endpoint: api.endpoint },
198
+ });
199
+ }
200
+ for (const flow of input.guidedFlows) {
201
+ const id = stableId('flow', flow.name);
202
+ entities.push({
203
+ id,
204
+ type: 'FLOW',
205
+ name: flow.name,
206
+ slug: slugify(flow.name),
207
+ semanticLabels: [flow.name],
208
+ description: `Fluxo guiado ${flow.name}`,
209
+ accessibilityHint: `Fluxo ${flow.name}`,
210
+ confidence: 0.85,
211
+ evidence: [{ type: 'guided-flow', value: flow.name }],
212
+ metadata: { steps: flow.steps },
213
+ });
214
+ }
215
+ if (apiEntityIds.length > 0) {
216
+ for (const button of entities.filter((entity) => entity.type === 'BUTTON' || entity.type === 'ACTION')) {
217
+ addRelationship(relationships, 'CALLS', button.id, apiEntityIds[0], `${button.name} pode acionar API`);
218
+ }
219
+ }
220
+ const warnings = [];
221
+ const hasUsefulEntity = entities.some((e) => e.type !== 'ROUTE');
222
+ if (!input.routes.length && !input.navigationMap.screens.length)
223
+ warnings.push('Nenhuma rota ou tela detectada.');
224
+ const status = input.navigationMap.screens.length > 0 && hasUsefulEntity && missingData.length === 0 ? 'complete' : 'partial';
225
+ const counts = (type) => entities.filter((entity) => entity.type === type).length;
226
+ const interactive = input.navigationMap.screens.reduce((acc, screen) => acc + screen.actions.length, 0);
227
+ return {
228
+ schemaVersion: '1.0.0',
229
+ artifactType: 'semantic-map',
230
+ filename: '.skip-sam.json',
231
+ scanId: input.scanId,
232
+ status,
233
+ generatedAt: new Date().toISOString(),
234
+ sourceManifestSha256: input.sourceManifestSha256,
235
+ generator: { scannerVersion: CLI_VERSION, skillVersion: 'unknown', strategy: 'static' },
236
+ project: {
237
+ name: input.projectName,
238
+ framework: input.signature.framework,
239
+ language: input.signature.language,
240
+ packageManager: fs.existsSync(path.join(input.rootDir, 'pnpm-lock.yaml')) ? 'pnpm' : fs.existsSync(path.join(input.rootDir, 'yarn.lock')) ? 'yarn' : 'npm',
241
+ rootPath: input.rootDir,
242
+ monorepo: fs.existsSync(path.join(input.rootDir, 'pnpm-workspace.yaml')) || fs.existsSync(path.join(input.rootDir, 'apps')),
243
+ },
244
+ stats: {
245
+ entities: entities.length,
246
+ relationships: relationships.length,
247
+ routes: counts('ROUTE'),
248
+ screens: counts('SCREEN'),
249
+ components: counts('COMPONENT'),
250
+ sharedComponents: input.components.filter((component) => !input.navigationMap.screens.some((screen) => screen.filePath === component.filepath)).length,
251
+ forms: counts('FORM'),
252
+ inputs: counts('INPUT'),
253
+ buttons: counts('BUTTON'),
254
+ links: counts('LINK'),
255
+ images: counts('IMAGE'),
256
+ headings: counts('HEADING'),
257
+ landmarks: counts('LANDMARK'),
258
+ dialogs: counts('DIALOG'),
259
+ menus: counts('MENU'),
260
+ tables: counts('TABLE'),
261
+ apis: counts('API'),
262
+ flows: counts('FLOW'),
263
+ businessRules: counts('BUSINESS_RULE'),
264
+ actions: counts('ACTION') + counts('BUTTON') + counts('LINK') + counts('INPUT'),
265
+ },
266
+ coverage: {
267
+ discoveredSourceFiles: input.fileManifest.length,
268
+ analyzedSourceFiles: new Set([...input.routes.map((r) => r.physicalPath), ...input.components.map((c) => c.filepath), ...input.apiCalls.map((a) => a.filepath)]).size,
269
+ discoveredRoutes: input.routes.length,
270
+ mappedRoutes: input.navigationMap.screens.length,
271
+ discoveredInteractiveElements: interactive,
272
+ mappedInteractiveElements: interactive,
273
+ percentage: input.fileManifest.length ? Math.round((Math.min(input.fileManifest.length, input.navigationMap.screens.length + input.components.length) / input.fileManifest.length) * 100) : 0,
274
+ },
275
+ entities,
276
+ relationships,
277
+ screens: input.navigationMap.screens,
278
+ sharedComponents: input.components.filter((component) => !input.navigationMap.screens.some((screen) => screen.filePath === component.filepath)),
279
+ warnings,
280
+ missingData,
281
+ validation: { valid: true, errors: [], warnings },
282
+ };
283
+ }
284
+ export function validateSamArtifact(payload, context = {}) {
285
+ const errors = [];
286
+ const warnings = [];
287
+ const entities = payload.entities;
288
+ const relationships = payload.relationships;
289
+ const filename = String(payload.filename ?? context.expectedFilename ?? '');
290
+ if ((context.expectedFilename ?? '.skip-sam.json') !== '.skip-sam.json' || filename !== '.skip-sam.json') {
291
+ errors.push('filename deve ser `.skip-sam.json`.');
292
+ }
293
+ if (payload.artifactType !== 'semantic-map')
294
+ errors.push('artifactType deve ser semantic-map.');
295
+ if (!Array.isArray(entities))
296
+ errors.push('entities deve ser array.');
297
+ if (!Array.isArray(relationships))
298
+ errors.push('relationships deve ser array.');
299
+ if (Array.isArray(entities) && entities.length === 0)
300
+ errors.push('entities não pode estar vazio.');
301
+ if (context.currentScanId && payload.scanId !== context.currentScanId) {
302
+ errors.push('O `.skip-sam.json` encontrado não pertence ao scan atual.');
303
+ }
304
+ if (context.scanStartedAt && typeof payload.generatedAt === 'string') {
305
+ if (Date.parse(payload.generatedAt) < Date.parse(context.scanStartedAt)) {
306
+ errors.push('O `.skip-sam.json` encontrado não pertence ao scan atual.');
307
+ }
308
+ }
309
+ if (context.sourceManifestSha256 && payload.sourceManifestSha256 !== context.sourceManifestSha256) {
310
+ errors.push('sourceManifestSha256 não corresponde ao manifesto atual.');
311
+ }
312
+ const ids = new Set();
313
+ const duplicates = new Set();
314
+ for (const entity of entities ?? []) {
315
+ if (!entity.id)
316
+ errors.push('Entidade sem id.');
317
+ if (ids.has(entity.id))
318
+ duplicates.add(entity.id);
319
+ ids.add(entity.id);
320
+ }
321
+ for (const id of duplicates)
322
+ errors.push(`ID duplicado: ${id}.`);
323
+ for (const relationship of relationships ?? []) {
324
+ if (!ids.has(relationship.fromEntityId))
325
+ errors.push(`Relacionamento inválido: origem inexistente ${relationship.fromEntityId}.`);
326
+ if (!ids.has(relationship.toEntityId))
327
+ errors.push(`Relacionamento inválido: destino inexistente ${relationship.toEntityId}.`);
328
+ }
329
+ const stats = payload.stats;
330
+ if (stats && Array.isArray(entities) && stats.entities !== entities.length)
331
+ errors.push('stats.entities diverge de entities.length.');
332
+ if (stats && Array.isArray(relationships) && stats.relationships !== relationships.length)
333
+ errors.push('stats.relationships diverge de relationships.length.');
334
+ const manifestPaths = new Set((context.fileManifest ?? []).map((entry) => toPosixPath(entry.path)));
335
+ if (manifestPaths.size > 0) {
336
+ for (const entity of entities ?? []) {
337
+ if (entity.source?.filePath && !manifestPaths.has(toPosixPath(entity.source.filePath))) {
338
+ errors.push(`Arquivo referenciado não existe no manifesto: ${entity.source.filePath}.`);
339
+ }
340
+ }
341
+ }
342
+ if (payload.status === 'complete') {
343
+ const hasRouteOrScreen = (entities ?? []).some((entity) => entity.type === 'ROUTE' || entity.type === 'SCREEN');
344
+ if (!hasRouteOrScreen)
345
+ errors.push('status complete exige pelo menos uma rota ou tela.');
346
+ if (!Array.isArray(entities) || entities.length === 0)
347
+ errors.push('status complete exige entidades.');
348
+ if (errors.length > 0)
349
+ errors.push('status complete é incoerente com erros críticos.');
350
+ }
351
+ return { valid: errors.length === 0, errors, warnings, artifactType: 'semantic-map' };
352
+ }
353
+ export function writeSkipSamFile(outputDir, sam) {
354
+ fs.mkdirSync(outputDir, { recursive: true });
355
+ const finalPath = path.join(outputDir, '.skip-sam.json');
356
+ const tmpPath = path.join(outputDir, '.skip-sam.json.tmp');
357
+ fs.writeFileSync(tmpPath, JSON.stringify(sam, null, 2));
358
+ fs.renameSync(tmpPath, finalPath);
359
+ return finalPath;
360
+ }
361
+ //# sourceMappingURL=sam.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sam.js","sourceRoot":"","sources":["../src/sam.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import type { ScanArtifact } from './artifacts.js';
1
2
  export type ScanStatus = 'PROCESSING' | 'ENRICHING' | 'COMPLETED' | 'FAILED';
2
3
  export interface ScanStatusResponse {
3
4
  scanId: string;
@@ -8,6 +9,20 @@ export interface ScanStatusResponse {
8
9
  export interface UploadResponse {
9
10
  scanId: string;
10
11
  }
12
+ export interface ArtifactUploadConfirmation {
13
+ artifactType: string;
14
+ filename: string;
15
+ scanId?: string;
16
+ received: boolean;
17
+ valid: boolean;
18
+ persisted: boolean;
19
+ processed?: boolean;
20
+ sha256: string;
21
+ }
22
+ export interface ArtifactBundleUploadResponse {
23
+ scanId: string;
24
+ artifacts?: ArtifactUploadConfirmation[];
25
+ }
11
26
  /** Limite de tamanho do payload (5 MB) imposto pela API. */
12
27
  export declare const MAX_PAYLOAD_BYTES: number;
13
28
  /**
@@ -15,6 +30,10 @@ export declare const MAX_PAYLOAD_BYTES: number;
15
30
  * Retorna o scanId criado.
16
31
  */
17
32
  export declare function uploadReport(report: object, token: string, baseUrl: string): Promise<string>;
33
+ export declare function uploadArtifactBundle(artifacts: ScanArtifact[], token: string, baseUrl: string, manifest: Record<string, unknown>): Promise<{
34
+ scanId: string;
35
+ confirmations: ArtifactUploadConfirmation[];
36
+ }>;
18
37
  /**
19
38
  * Faz polling do status do scan até `COMPLETED` ou `FAILED`,
20
39
  * exibindo um spinner no terminal.
@@ -1 +1 @@
1
- {"version":3,"file":"uploader.d.ts","sourceRoot":"","sources":["../src/uploader.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,WAAW,GAAG,WAAW,GAAG,QAAQ,CAAC;AAE7E,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,UAAU,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,4DAA4D;AAC5D,eAAO,MAAM,iBAAiB,QAAkB,CAAC;AAEjD;;;GAGG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC,CA6BjB;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,IAAI,GAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,GAC1E,OAAO,CAAC,kBAAkB,CAAC,CAsC7B"}
1
+ {"version":3,"file":"uploader.d.ts","sourceRoot":"","sources":["../src/uploader.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,WAAW,GAAG,WAAW,GAAG,QAAQ,CAAC;AAE7E,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,UAAU,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,0BAA0B;IACzC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,4BAA4B;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,0BAA0B,EAAE,CAAC;CAC1C;AAED,4DAA4D;AAC5D,eAAO,MAAM,iBAAiB,QAAkB,CAAC;AAEjD;;;GAGG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC,CA6BjB;AAgBD,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,YAAY,EAAE,EACzB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,0BAA0B,EAAE,CAAA;CAAE,CAAC,CAyD1E;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,IAAI,GAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,GAC1E,OAAO,CAAC,kBAAkB,CAAC,CAsC7B"}
package/dist/uploader.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import ora from 'ora';
2
+ import fs from 'fs';
2
3
  /** Limite de tamanho do payload (5 MB) imposto pela API. */
3
4
  export const MAX_PAYLOAD_BYTES = 5 * 1024 * 1024;
4
5
  /**
@@ -31,6 +32,73 @@ export async function uploadReport(report, token, baseUrl) {
31
32
  const data = (await res.json());
32
33
  return data.scanId;
33
34
  }
35
+ function normalizeArtifactConfirmations(data, artifacts) {
36
+ if (Array.isArray(data.artifacts))
37
+ return data.artifacts;
38
+ return artifacts.map((artifact) => ({
39
+ artifactType: artifact.artifactType,
40
+ filename: artifact.filename,
41
+ scanId: data.scanId,
42
+ received: Boolean(data.scanId),
43
+ valid: false,
44
+ persisted: false,
45
+ processed: false,
46
+ sha256: artifact.sha256,
47
+ }));
48
+ }
49
+ export async function uploadArtifactBundle(artifacts, token, baseUrl, manifest) {
50
+ if (!artifacts.length)
51
+ throw new Error('Nenhum artefato informado para upload.');
52
+ const scanId = artifacts[0].scanId;
53
+ const form = new FormData();
54
+ form.set('scanId', scanId);
55
+ form.set('schemaVersion', '1.0.0');
56
+ form.set('bundleVersion', '1.0.0');
57
+ form.set('manifest', JSON.stringify(manifest));
58
+ for (const artifact of artifacts) {
59
+ form.append('artifacts[]', new Blob([fs.readFileSync(artifact.absolutePath)], { type: 'application/json' }), artifact.filename);
60
+ form.append(`${artifact.filename}:artifactType`, artifact.artifactType);
61
+ form.append(`${artifact.filename}:sha256`, artifact.sha256);
62
+ form.append(`${artifact.filename}:sizeBytes`, String(artifact.sizeBytes));
63
+ }
64
+ const endpoints = [`${baseUrl}/scanner/artifacts`, `${baseUrl}/artifacts`, `${baseUrl}/api/scanner/artifacts`, `${baseUrl}/api/scanner`];
65
+ let lastError = '';
66
+ for (const endpoint of endpoints) {
67
+ const res = await fetch(endpoint, {
68
+ method: 'POST',
69
+ headers: { Authorization: `Bearer ${token}` },
70
+ body: form,
71
+ });
72
+ if (res.status === 404 || res.status === 405) {
73
+ lastError = `HTTP ${res.status}`;
74
+ continue;
75
+ }
76
+ if (res.status === 401)
77
+ throw new Error('Token inválido. Verifique seu token no Dashboard.');
78
+ if (res.status === 413)
79
+ throw new Error('Payload muito grande (>5MB). Use --ignore para excluir mais arquivos.');
80
+ if (!res.ok) {
81
+ const text = await res.text().catch(() => '');
82
+ throw new Error(`Erro ao enviar artefatos: HTTP ${res.status}${text ? ` — ${text}` : ''}`);
83
+ }
84
+ const data = (await res.json().catch(() => ({ scanId })));
85
+ const confirmations = normalizeArtifactConfirmations(data, artifacts);
86
+ for (const artifact of artifacts) {
87
+ const confirmation = confirmations.find((item) => item.filename === artifact.filename || item.artifactType === artifact.artifactType);
88
+ if (!confirmation ||
89
+ confirmation.scanId && confirmation.scanId !== artifact.scanId ||
90
+ confirmation.filename !== artifact.filename ||
91
+ confirmation.received !== true ||
92
+ confirmation.valid !== true ||
93
+ confirmation.persisted !== true ||
94
+ confirmation.sha256 !== artifact.sha256) {
95
+ throw new Error(`Upload incompleto:\n\`${artifact.filename}\` não foi confirmado pela API.`);
96
+ }
97
+ }
98
+ return { scanId: data.scanId ?? scanId, confirmations };
99
+ }
100
+ throw new Error(`Endpoint de upload de artefatos não encontrado (${lastError || 'sem resposta'}).`);
101
+ }
34
102
  /**
35
103
  * Faz polling do status do scan até `COMPLETED` ou `FAILED`,
36
104
  * exibindo um spinner no terminal.