@kirrosh/zond 0.21.0 → 0.23.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/CHANGELOG.md +758 -3
- package/README.md +78 -15
- package/package.json +17 -10
- package/src/cli/argv.ts +122 -0
- package/src/cli/commands/add-api.ts +134 -0
- package/src/cli/commands/api/annotate/idempotency.ts +59 -0
- package/src/cli/commands/api/annotate/index.ts +525 -0
- package/src/cli/commands/api/annotate/lifecycle.ts +74 -0
- package/src/cli/commands/api/annotate/overlay.ts +206 -0
- package/src/cli/commands/api/annotate/pagination.ts +60 -0
- package/src/cli/commands/api/annotate/prompts.ts +183 -0
- package/src/cli/commands/api/annotate/readback.ts +58 -0
- package/src/cli/commands/api/annotate/resources.ts +91 -0
- package/src/cli/commands/api/annotate/seed-bodies.ts +61 -0
- package/src/cli/commands/audit.ts +480 -0
- package/src/cli/commands/bootstrap.ts +710 -0
- package/src/cli/commands/catalog.ts +35 -0
- package/src/cli/commands/check.ts +348 -0
- package/src/cli/commands/checks.ts +756 -0
- package/src/cli/commands/ci-init.ts +55 -6
- package/src/cli/commands/clean.ts +212 -0
- package/src/cli/commands/cleanup.ts +262 -0
- package/src/cli/commands/completions.ts +192 -0
- package/src/cli/commands/coverage.ts +605 -132
- package/src/cli/commands/db.ts +180 -8
- package/src/cli/commands/describe.ts +37 -2
- package/src/cli/commands/discover.ts +1236 -0
- package/src/cli/commands/doctor.ts +607 -0
- package/src/cli/commands/fixtures.ts +402 -0
- package/src/cli/commands/generate.ts +420 -47
- package/src/cli/commands/init/agents-md.ts +61 -0
- package/src/cli/commands/init/bootstrap.ts +108 -0
- package/src/cli/commands/init/index.ts +244 -0
- package/src/cli/commands/init/skills.ts +98 -0
- package/src/cli/commands/init/templates/agents.md +77 -0
- package/src/cli/commands/init/templates/markdown.d.ts +4 -0
- package/src/cli/commands/init/templates/skills/zond-checks.md +397 -0
- package/src/cli/commands/init/templates/skills/zond-triage.md +210 -0
- package/src/cli/commands/init/templates/skills/zond.md +651 -0
- package/src/cli/commands/init/templates/zond-config.yml +14 -0
- package/src/cli/commands/prepare-fixtures.ts +135 -0
- package/src/cli/commands/probe/mass-assignment.ts +503 -0
- package/src/cli/commands/probe/security.ts +454 -0
- package/src/cli/commands/probe/static.ts +255 -0
- package/src/cli/commands/probe/webhooks.ts +161 -0
- package/src/cli/commands/probe.ts +459 -0
- package/src/cli/commands/reference.ts +87 -0
- package/src/cli/commands/refresh-api.ts +169 -0
- package/src/cli/commands/remove-api.ts +150 -0
- package/src/cli/commands/report-bundle.ts +318 -0
- package/src/cli/commands/report.ts +241 -0
- package/src/cli/commands/request.ts +379 -4
- package/src/cli/commands/run.ts +911 -33
- package/src/cli/commands/session.ts +244 -0
- package/src/cli/commands/use.ts +74 -0
- package/src/cli/index.ts +36 -607
- package/src/cli/json-envelope.ts +112 -3
- package/src/cli/json-schemas.ts +263 -0
- package/src/cli/program.ts +218 -0
- package/src/cli/resolve.ts +105 -0
- package/src/cli/status-filter.ts +124 -0
- package/src/cli/util/api-context.ts +85 -0
- package/src/cli/version.ts +8 -0
- package/src/core/anti-fp/bootstrap.ts +34 -0
- package/src/core/anti-fp/index.ts +33 -0
- package/src/core/anti-fp/registry.ts +44 -0
- package/src/core/anti-fp/rules/baseline-echo.ts +74 -0
- package/src/core/anti-fp/rules/schemathesis/body_negation_becomes_valid.ts +52 -0
- package/src/core/anti-fp/rules/schemathesis/coverage_phase_boundary_positive.ts +38 -0
- package/src/core/anti-fp/rules/schemathesis/has_unverifiable_mutations.ts +35 -0
- package/src/core/anti-fp/rules/schemathesis/index.ts +24 -0
- package/src/core/anti-fp/rules/schemathesis/string_type_mutation_becomes_valid.ts +53 -0
- package/src/core/anti-fp/rules/subscription-gated/index.ts +11 -0
- package/src/core/anti-fp/rules/subscription-gated/paid-plan-403.ts +75 -0
- package/src/core/anti-fp/types.ts +68 -0
- package/src/core/checks/checks/_crud-helpers.ts +133 -0
- package/src/core/checks/checks/_negative_mutator.ts +133 -0
- package/src/core/checks/checks/_readback-helpers.ts +133 -0
- package/src/core/checks/checks/content_type_conformance.ts +39 -0
- package/src/core/checks/checks/cross_call_references.ts +134 -0
- package/src/core/checks/checks/ensure_resource_availability.ts +62 -0
- package/src/core/checks/checks/idempotency_replay.ts +246 -0
- package/src/core/checks/checks/ignored_auth.ts +211 -0
- package/src/core/checks/checks/index.ts +65 -0
- package/src/core/checks/checks/lifecycle_transitions.ts +273 -0
- package/src/core/checks/checks/missing_required_header.ts +40 -0
- package/src/core/checks/checks/negative_data_rejection.ts +45 -0
- package/src/core/checks/checks/not_a_server_error.ts +27 -0
- package/src/core/checks/checks/open_cors_on_sensitive.ts +131 -0
- package/src/core/checks/checks/pagination_invariants.ts +238 -0
- package/src/core/checks/checks/positive_data_acceptance.ts +36 -0
- package/src/core/checks/checks/rate_limit_headers_absent.ts +77 -0
- package/src/core/checks/checks/response_headers_conformance.ts +74 -0
- package/src/core/checks/checks/response_schema_conformance.ts +30 -0
- package/src/core/checks/checks/status_code_conformance.ts +61 -0
- package/src/core/checks/checks/unsupported_method.ts +63 -0
- package/src/core/checks/checks/use_after_free.ts +78 -0
- package/src/core/checks/index.ts +30 -0
- package/src/core/checks/mode.ts +79 -0
- package/src/core/checks/recommended-action.ts +64 -0
- package/src/core/checks/registry.ts +78 -0
- package/src/core/checks/runner.ts +874 -0
- package/src/core/checks/sarif.ts +230 -0
- package/src/core/checks/stateful.ts +121 -0
- package/src/core/checks/types.ts +189 -0
- package/src/core/classifier/recommended-action.ts +222 -0
- package/src/core/context/current.ts +51 -0
- package/src/core/context/session.ts +78 -0
- package/src/core/coverage/loader.ts +185 -0
- package/src/core/coverage/reasons.ts +300 -0
- package/src/core/diagnostics/db-analysis.ts +161 -12
- package/src/core/diagnostics/failure-class.ts +120 -0
- package/src/core/diagnostics/failure-hints.ts +212 -9
- package/src/core/diagnostics/spec-pointer.ts +99 -0
- package/src/core/diagnostics/suggested-fixes.ts +156 -0
- package/src/core/exporter/case-study/index.ts +270 -0
- package/src/core/exporter/curl.ts +40 -0
- package/src/core/exporter/exporter.ts +48 -0
- package/src/core/exporter/html-report/escape.ts +24 -0
- package/src/core/exporter/html-report/index.ts +479 -0
- package/src/core/exporter/html-report/script.ts +100 -0
- package/src/core/exporter/html-report/styles.ts +408 -0
- package/src/core/generator/chunker.ts +53 -15
- package/src/core/generator/coverage-phase.ts +0 -0
- package/src/core/generator/create-body.ts +89 -0
- package/src/core/generator/data-factory.ts +490 -33
- package/src/core/generator/describe.ts +1 -1
- package/src/core/generator/fixtures-builder.ts +325 -0
- package/src/core/generator/index.ts +7 -5
- package/src/core/generator/openapi-reader.ts +55 -3
- package/src/core/generator/path-param-disambig.ts +114 -0
- package/src/core/generator/resources-builder.ts +648 -0
- package/src/core/generator/schema-utils.ts +11 -3
- package/src/core/generator/serializer.ts +114 -15
- package/src/core/generator/suite-generator.ts +484 -77
- package/src/core/generator/types.ts +8 -0
- package/src/core/identity/identity-file.ts +129 -0
- package/src/core/lint/affects.ts +28 -0
- package/src/core/lint/config.ts +96 -0
- package/src/core/lint/format.ts +42 -0
- package/src/core/lint/index.ts +94 -0
- package/src/core/lint/reporter.ts +128 -0
- package/src/core/lint/rules/consistency.ts +158 -0
- package/src/core/lint/rules/heuristics.ts +97 -0
- package/src/core/lint/rules/strictness.ts +109 -0
- package/src/core/lint/types.ts +96 -0
- package/src/core/lint/walker.ts +248 -0
- package/src/core/meta/meta-store.ts +6 -73
- package/src/core/output/README.md +91 -0
- package/src/core/output/index.ts +13 -0
- package/src/core/output/run.ts +126 -0
- package/src/core/output/types.ts +129 -0
- package/src/core/parser/env-interpolation.ts +104 -0
- package/src/core/parser/filter.ts +57 -0
- package/src/core/parser/schema.ts +132 -5
- package/src/core/parser/types.ts +29 -2
- package/src/core/parser/variables.ts +0 -0
- package/src/core/parser/yaml-parser.ts +108 -13
- package/src/core/probe/bootstrap.ts +34 -0
- package/src/core/probe/dry-run-envelope.ts +57 -0
- package/src/core/probe/mass-assignment-probe-class.ts +198 -0
- package/src/core/probe/mass-assignment-probe.ts +1122 -0
- package/src/core/probe/mass-assignment-template.ts +212 -0
- package/src/core/probe/method-probe.ts +164 -0
- package/src/core/probe/method-shared.ts +69 -0
- package/src/core/probe/negative-probe.ts +691 -0
- package/src/core/probe/orphan-tracker.ts +188 -0
- package/src/core/probe/path-discovery.ts +440 -0
- package/src/core/probe/probe-harness.ts +120 -0
- package/src/core/probe/registry.ts +89 -0
- package/src/core/probe/runner.ts +136 -0
- package/src/core/probe/security-probe-class.ts +201 -0
- package/src/core/probe/security-probe.ts +1453 -0
- package/src/core/probe/shared.ts +505 -0
- package/src/core/probe/static-probe-class.ts +125 -0
- package/src/core/probe/types.ts +165 -0
- package/src/core/probe/verdict-aggregator.ts +33 -0
- package/src/core/probe/webhooks-probe.ts +284 -0
- package/src/core/reporter/console.ts +69 -4
- package/src/core/reporter/index.ts +2 -3
- package/src/core/reporter/json.ts +15 -2
- package/src/core/reporter/junit.ts +27 -12
- package/src/core/reporter/ndjson.ts +37 -0
- package/src/core/reporter/types.ts +3 -0
- package/src/core/runner/assertions.ts +62 -2
- package/src/core/runner/async-pool.ts +108 -0
- package/src/core/runner/auth-path.ts +8 -0
- package/src/core/runner/ci-context.ts +72 -0
- package/src/core/runner/executor.ts +391 -52
- package/src/core/runner/form-encode.ts +51 -0
- package/src/core/runner/http-client.ts +115 -7
- package/src/core/runner/learn-drift.ts +293 -0
- package/src/core/runner/preflight-vars.ts +149 -0
- package/src/core/runner/progress-tracker.ts +73 -0
- package/src/core/runner/rate-limiter.ts +203 -0
- package/src/core/runner/run-kind.ts +39 -0
- package/src/core/runner/schema-validator.ts +312 -0
- package/src/core/runner/send-request.ts +153 -20
- package/src/core/runner/types.ts +38 -0
- package/src/core/secrets/registry.ts +164 -0
- package/src/core/secrets/secrets-file.ts +115 -0
- package/src/core/selectors/operation-filter.ts +144 -0
- package/src/core/setup-api.ts +419 -17
- package/src/core/severity/category.ts +94 -0
- package/src/core/severity/index.ts +121 -0
- package/src/core/spec/layers.ts +154 -0
- package/src/core/util/format-eta.ts +21 -0
- package/src/core/utils.ts +5 -1
- package/src/core/workspace/config.ts +129 -0
- package/src/core/workspace/manifest.ts +283 -0
- package/src/core/workspace/output-rotation.ts +62 -0
- package/src/core/workspace/root.ts +94 -0
- package/src/core/workspace/triage-path.ts +87 -0
- package/src/db/lint-runs.ts +47 -0
- package/src/db/migrate.ts +126 -0
- package/src/db/migrations/0001_run_kind.sql +25 -0
- package/src/db/migrations/sql.d.ts +4 -0
- package/src/db/queries/collections.ts +133 -0
- package/src/db/queries/coverage.ts +9 -0
- package/src/db/queries/dashboard.ts +59 -0
- package/src/db/queries/results.ts +128 -0
- package/src/db/queries/runs.ts +235 -0
- package/src/db/queries/sessions.ts +42 -0
- package/src/db/queries/settings.ts +28 -0
- package/src/db/queries/types.ts +172 -0
- package/src/db/queries.ts +72 -802
- package/src/db/schema.ts +179 -48
- package/src/cli/commands/export.ts +0 -144
- package/src/cli/commands/guide.ts +0 -127
- package/src/cli/commands/init.ts +0 -57
- package/src/cli/commands/serve.ts +0 -81
- package/src/cli/commands/sync.ts +0 -269
- package/src/cli/commands/update.ts +0 -189
- package/src/cli/commands/validate.ts +0 -34
- package/src/core/exporter/postman.ts +0 -963
- package/src/core/generator/guide-builder.ts +0 -253
- package/src/core/meta/types.ts +0 -21
- package/src/core/parser/index.ts +0 -21
- package/src/core/runner/execute-run.ts +0 -132
- package/src/core/runner/index.ts +0 -12
- package/src/core/sync/spec-differ.ts +0 -38
- package/src/web/data/collection-state.ts +0 -362
- package/src/web/routes/api.ts +0 -314
- package/src/web/routes/dashboard.ts +0 -350
- package/src/web/routes/runs.ts +0 -64
- package/src/web/schemas.ts +0 -121
- package/src/web/server.ts +0 -134
- package/src/web/static/htmx.min.cjs +0 -1
- package/src/web/static/style.css +0 -1148
- package/src/web/views/endpoints-tab.ts +0 -174
- package/src/web/views/explorer-tab.ts +0 -402
- package/src/web/views/health-strip.ts +0 -92
- package/src/web/views/layout.ts +0 -48
- package/src/web/views/results.ts +0 -210
- package/src/web/views/runs-tab.ts +0 -126
- package/src/web/views/suites-tab.ts +0 -181
package/src/db/schema.ts
CHANGED
|
@@ -1,12 +1,27 @@
|
|
|
1
1
|
import { Database } from "bun:sqlite";
|
|
2
|
-
import { resolve } from "path";
|
|
3
|
-
import { existsSync } from "fs";
|
|
2
|
+
import { dirname, resolve } from "path";
|
|
3
|
+
import { existsSync, mkdirSync } from "fs";
|
|
4
|
+
import { findWorkspaceRoot } from "../core/workspace/root.ts";
|
|
5
|
+
import { applyMigrations } from "./migrate.ts";
|
|
4
6
|
|
|
5
7
|
let _db: Database | null = null;
|
|
6
8
|
let _dbPath: string | null = null;
|
|
7
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Default DB path lives under `<workspace>/.zond/zond.db` to keep runtime
|
|
12
|
+
* artifacts out of the project root. For back-compat we still recognise a
|
|
13
|
+
* legacy `<workspace>/zond.db` if it exists — old workspaces keep working
|
|
14
|
+
* without migration.
|
|
15
|
+
*/
|
|
16
|
+
function defaultDbPath(): string {
|
|
17
|
+
const root = findWorkspaceRoot().root;
|
|
18
|
+
const legacy = resolve(root, "zond.db");
|
|
19
|
+
if (existsSync(legacy)) return legacy;
|
|
20
|
+
return resolve(root, ".zond", "zond.db");
|
|
21
|
+
}
|
|
22
|
+
|
|
8
23
|
export function getDb(dbPath?: string): Database {
|
|
9
|
-
const path = dbPath ? resolve(dbPath) : (_dbPath ??
|
|
24
|
+
const path = dbPath ? resolve(dbPath) : (_dbPath ?? defaultDbPath());
|
|
10
25
|
|
|
11
26
|
// If cached connection exists, verify the file still exists
|
|
12
27
|
if (_db && _dbPath === path && existsSync(path)) return _db;
|
|
@@ -17,19 +32,68 @@ export function getDb(dbPath?: string): Database {
|
|
|
17
32
|
_db = null;
|
|
18
33
|
_dbPath = null;
|
|
19
34
|
}
|
|
35
|
+
// SQLite won't auto-create parent dirs; ensure `.zond/` (or any custom
|
|
36
|
+
// path's parent) exists before opening the file.
|
|
37
|
+
const parent = dirname(path);
|
|
38
|
+
if (!existsSync(parent)) mkdirSync(parent, { recursive: true });
|
|
39
|
+
|
|
20
40
|
const db = new Database(path, { create: true });
|
|
21
41
|
|
|
22
42
|
// Performance and integrity settings
|
|
23
43
|
db.exec("PRAGMA journal_mode = WAL");
|
|
24
44
|
db.exec("PRAGMA foreign_keys = ON");
|
|
45
|
+
// ARV-163: concurrent zond processes (e.g. `probe security` in one terminal
|
|
46
|
+
// + `checks run` in another) collide on the write-lock and surface as
|
|
47
|
+
// "database is locked". Letting SQLite spin at the C-level for up to 5s
|
|
48
|
+
// resolves the overwhelming majority without any per-call retry logic.
|
|
49
|
+
// `synchronous=NORMAL` is safe under WAL and cuts fsync overhead in the
|
|
50
|
+
// bulk-insert path (saveResults). withDbRetry() in this file remains the
|
|
51
|
+
// belt-and-suspenders for the rare long-running transactions.
|
|
52
|
+
db.exec("PRAGMA busy_timeout = 5000");
|
|
53
|
+
db.exec("PRAGMA synchronous = NORMAL");
|
|
25
54
|
|
|
26
55
|
runMigrations(db);
|
|
27
56
|
|
|
57
|
+
// ARV-127: file-based migrations sit on top of the legacy PRAGMA
|
|
58
|
+
// path. On an existing DB the runner pre-seeds `schema_migrations`
|
|
59
|
+
// so the v9→v10 inline migration (now mirrored in
|
|
60
|
+
// src/db/migrations/0001_run_kind.sql) is treated as applied.
|
|
61
|
+
applyMigrations(db);
|
|
62
|
+
|
|
28
63
|
_db = db;
|
|
29
64
|
_dbPath = path;
|
|
30
65
|
return db;
|
|
31
66
|
}
|
|
32
67
|
|
|
68
|
+
/**
|
|
69
|
+
* ARV-163: retry wrapper for SQLite write paths that may collide with
|
|
70
|
+
* concurrent zond processes. `PRAGMA busy_timeout` already absorbs short
|
|
71
|
+
* contention at the C level — this wrapper only catches the residual cases
|
|
72
|
+
* where the lock holder runs longer than the timeout (e.g. a big
|
|
73
|
+
* `saveResults` bulk insert during a parallel `probe security` cleanup).
|
|
74
|
+
*
|
|
75
|
+
* Detects bun:sqlite's "database is locked" / "SQLITE_BUSY" message shapes;
|
|
76
|
+
* other errors propagate immediately. Backoff: 100, 200, 400, 800ms capped at
|
|
77
|
+
* 4 attempts (≈1.5s total) so we never silently stall a CLI command.
|
|
78
|
+
*/
|
|
79
|
+
export function withDbRetry<T>(label: string, fn: () => T): T {
|
|
80
|
+
const delaysMs = [100, 200, 400, 800];
|
|
81
|
+
let lastError: unknown;
|
|
82
|
+
for (let attempt = 0; attempt <= delaysMs.length; attempt++) {
|
|
83
|
+
try {
|
|
84
|
+
return fn();
|
|
85
|
+
} catch (err) {
|
|
86
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
87
|
+
if (!/database is locked|SQLITE_BUSY/i.test(msg)) throw err;
|
|
88
|
+
lastError = err;
|
|
89
|
+
if (attempt === delaysMs.length) break;
|
|
90
|
+
Bun.sleepSync(delaysMs[attempt]!);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const msg = lastError instanceof Error ? lastError.message : String(lastError);
|
|
94
|
+
throw new Error(`SQLite still locked after ${delaysMs.length + 1} attempts (${label}): ${msg}`);
|
|
95
|
+
}
|
|
96
|
+
|
|
33
97
|
export function closeDb(): void {
|
|
34
98
|
if (_db) {
|
|
35
99
|
try { _db.close(); } catch {}
|
|
@@ -38,7 +102,7 @@ export function closeDb(): void {
|
|
|
38
102
|
}
|
|
39
103
|
}
|
|
40
104
|
|
|
41
|
-
|
|
105
|
+
function resetDb(): void {
|
|
42
106
|
if (_db) { try { _db.close(); } catch {} }
|
|
43
107
|
_db = null;
|
|
44
108
|
_dbPath = null;
|
|
@@ -48,7 +112,7 @@ export function resetDb(): void {
|
|
|
48
112
|
// Schema
|
|
49
113
|
// ──────────────────────────────────────────────
|
|
50
114
|
|
|
51
|
-
const SCHEMA_VERSION =
|
|
115
|
+
const SCHEMA_VERSION = 10;
|
|
52
116
|
|
|
53
117
|
const SCHEMA = `
|
|
54
118
|
CREATE TABLE IF NOT EXISTS runs (
|
|
@@ -64,7 +128,13 @@ const SCHEMA = `
|
|
|
64
128
|
branch TEXT,
|
|
65
129
|
environment TEXT,
|
|
66
130
|
duration_ms INTEGER,
|
|
67
|
-
collection_id INTEGER REFERENCES collections(id)
|
|
131
|
+
collection_id INTEGER REFERENCES collections(id),
|
|
132
|
+
session_id TEXT,
|
|
133
|
+
tags TEXT,
|
|
134
|
+
-- ARV-55: classify a run once at INSERT time so coverage / diagnose
|
|
135
|
+
-- queries don't have to re-derive "is this a probe-only run?" from
|
|
136
|
+
-- the results' suite_file paths.
|
|
137
|
+
run_kind TEXT NOT NULL DEFAULT 'regular' CHECK (run_kind IN ('regular','probe','check'))
|
|
68
138
|
);
|
|
69
139
|
|
|
70
140
|
CREATE TABLE IF NOT EXISTS results (
|
|
@@ -83,7 +153,12 @@ const SCHEMA = `
|
|
|
83
153
|
assertions TEXT,
|
|
84
154
|
captures TEXT,
|
|
85
155
|
response_headers TEXT,
|
|
86
|
-
suite_file TEXT
|
|
156
|
+
suite_file TEXT,
|
|
157
|
+
provenance TEXT,
|
|
158
|
+
failure_class TEXT,
|
|
159
|
+
failure_class_reason TEXT,
|
|
160
|
+
spec_pointer TEXT,
|
|
161
|
+
spec_excerpt TEXT
|
|
87
162
|
);
|
|
88
163
|
|
|
89
164
|
CREATE TABLE IF NOT EXISTS collections (
|
|
@@ -95,44 +170,6 @@ const SCHEMA = `
|
|
|
95
170
|
base_dir TEXT
|
|
96
171
|
);
|
|
97
172
|
|
|
98
|
-
CREATE TABLE IF NOT EXISTS ai_generations (
|
|
99
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
100
|
-
collection_id INTEGER REFERENCES collections(id),
|
|
101
|
-
prompt TEXT NOT NULL,
|
|
102
|
-
model TEXT NOT NULL,
|
|
103
|
-
provider TEXT NOT NULL,
|
|
104
|
-
generated_yaml TEXT,
|
|
105
|
-
output_path TEXT,
|
|
106
|
-
status TEXT NOT NULL DEFAULT 'pending',
|
|
107
|
-
error_message TEXT,
|
|
108
|
-
prompt_tokens INTEGER,
|
|
109
|
-
completion_tokens INTEGER,
|
|
110
|
-
duration_ms INTEGER,
|
|
111
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
112
|
-
);
|
|
113
|
-
|
|
114
|
-
CREATE TABLE IF NOT EXISTS chat_sessions (
|
|
115
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
116
|
-
title TEXT,
|
|
117
|
-
provider TEXT NOT NULL,
|
|
118
|
-
model TEXT NOT NULL,
|
|
119
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
120
|
-
last_active TEXT NOT NULL DEFAULT (datetime('now'))
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
CREATE TABLE IF NOT EXISTS chat_messages (
|
|
124
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
125
|
-
session_id INTEGER NOT NULL REFERENCES chat_sessions(id),
|
|
126
|
-
role TEXT NOT NULL,
|
|
127
|
-
content TEXT NOT NULL,
|
|
128
|
-
tool_name TEXT,
|
|
129
|
-
tool_args TEXT,
|
|
130
|
-
tool_result TEXT,
|
|
131
|
-
input_tokens INTEGER,
|
|
132
|
-
output_tokens INTEGER,
|
|
133
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
134
|
-
);
|
|
135
|
-
|
|
136
173
|
CREATE TABLE IF NOT EXISTS settings (
|
|
137
174
|
key TEXT PRIMARY KEY,
|
|
138
175
|
value TEXT NOT NULL
|
|
@@ -140,13 +177,27 @@ const SCHEMA = `
|
|
|
140
177
|
|
|
141
178
|
CREATE INDEX IF NOT EXISTS idx_runs_started ON runs(started_at DESC);
|
|
142
179
|
CREATE INDEX IF NOT EXISTS idx_runs_collection ON runs(collection_id);
|
|
180
|
+
CREATE INDEX IF NOT EXISTS idx_runs_session ON runs(session_id, started_at DESC);
|
|
143
181
|
CREATE INDEX IF NOT EXISTS idx_results_run ON results(run_id);
|
|
144
182
|
CREATE INDEX IF NOT EXISTS idx_results_status ON results(status);
|
|
145
183
|
CREATE INDEX IF NOT EXISTS idx_results_name ON results(suite_name, test_name);
|
|
146
184
|
CREATE INDEX IF NOT EXISTS idx_collections_name ON collections(name);
|
|
147
|
-
|
|
148
|
-
CREATE
|
|
149
|
-
|
|
185
|
+
|
|
186
|
+
CREATE TABLE IF NOT EXISTS lint_runs (
|
|
187
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
188
|
+
spec_path TEXT NOT NULL,
|
|
189
|
+
started_at TEXT NOT NULL,
|
|
190
|
+
finished_at TEXT,
|
|
191
|
+
total INTEGER NOT NULL DEFAULT 0,
|
|
192
|
+
high_count INTEGER NOT NULL DEFAULT 0,
|
|
193
|
+
medium_count INTEGER NOT NULL DEFAULT 0,
|
|
194
|
+
low_count INTEGER NOT NULL DEFAULT 0,
|
|
195
|
+
endpoint_count INTEGER NOT NULL DEFAULT 0,
|
|
196
|
+
config_json TEXT,
|
|
197
|
+
issues_json TEXT
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
CREATE INDEX IF NOT EXISTS idx_lint_runs_spec ON lint_runs(spec_path, started_at DESC);
|
|
150
201
|
`;
|
|
151
202
|
|
|
152
203
|
function runMigrations(db: Database): void {
|
|
@@ -162,6 +213,86 @@ function runMigrations(db: Database): void {
|
|
|
162
213
|
// Migration v1→v2: add suite_file column to results
|
|
163
214
|
db.exec("ALTER TABLE results ADD COLUMN suite_file TEXT");
|
|
164
215
|
}
|
|
216
|
+
if (ver >= 2 && ver < 3) {
|
|
217
|
+
// Migration v2→v3: add provenance column (test source metadata)
|
|
218
|
+
db.exec("ALTER TABLE results ADD COLUMN provenance TEXT");
|
|
219
|
+
}
|
|
220
|
+
if (ver >= 3 && ver < 4) {
|
|
221
|
+
// Migration v3→v4: add failure classification columns
|
|
222
|
+
db.exec("ALTER TABLE results ADD COLUMN failure_class TEXT");
|
|
223
|
+
db.exec("ALTER TABLE results ADD COLUMN failure_class_reason TEXT");
|
|
224
|
+
}
|
|
225
|
+
if (ver >= 4 && ver < 5) {
|
|
226
|
+
// Migration v4→v5: add spec_pointer + spec_excerpt (frozen OpenAPI evidence)
|
|
227
|
+
db.exec("ALTER TABLE results ADD COLUMN spec_pointer TEXT");
|
|
228
|
+
db.exec("ALTER TABLE results ADD COLUMN spec_excerpt TEXT");
|
|
229
|
+
}
|
|
230
|
+
if (ver >= 5 && ver < 6) {
|
|
231
|
+
// Migration v5→v6: add lint_runs table for `zond lint-spec` history.
|
|
232
|
+
db.exec(`
|
|
233
|
+
CREATE TABLE IF NOT EXISTS lint_runs (
|
|
234
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
235
|
+
spec_path TEXT NOT NULL,
|
|
236
|
+
started_at TEXT NOT NULL,
|
|
237
|
+
finished_at TEXT,
|
|
238
|
+
total INTEGER NOT NULL DEFAULT 0,
|
|
239
|
+
high_count INTEGER NOT NULL DEFAULT 0,
|
|
240
|
+
medium_count INTEGER NOT NULL DEFAULT 0,
|
|
241
|
+
low_count INTEGER NOT NULL DEFAULT 0,
|
|
242
|
+
endpoint_count INTEGER NOT NULL DEFAULT 0,
|
|
243
|
+
config_json TEXT,
|
|
244
|
+
issues_json TEXT
|
|
245
|
+
);
|
|
246
|
+
CREATE INDEX IF NOT EXISTS idx_lint_runs_spec ON lint_runs(spec_path, started_at DESC);
|
|
247
|
+
`);
|
|
248
|
+
}
|
|
249
|
+
if (ver >= 6 && ver < 7) {
|
|
250
|
+
// Migration v6→v7: add session_id column to runs for grouping CLI invocations
|
|
251
|
+
// (e.g. `zond hunt`, scripted post-init runs) into one campaign.
|
|
252
|
+
db.exec("ALTER TABLE runs ADD COLUMN session_id TEXT");
|
|
253
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_runs_session ON runs(session_id, started_at DESC)");
|
|
254
|
+
}
|
|
255
|
+
if (ver >= 7 && ver < 8) {
|
|
256
|
+
// Migration v7→v8: drop the unused AI/chat tables. They were a legacy
|
|
257
|
+
// experiment (in-app chat-driven YAML generation) that never shipped a
|
|
258
|
+
// user-facing surface and have no consumers in the codebase.
|
|
259
|
+
db.exec("DROP TABLE IF EXISTS chat_messages");
|
|
260
|
+
db.exec("DROP TABLE IF EXISTS chat_sessions");
|
|
261
|
+
db.exec("DROP TABLE IF EXISTS ai_generations");
|
|
262
|
+
}
|
|
263
|
+
if (ver >= 8 && ver < 9) {
|
|
264
|
+
// Migration v8→v9: tags column on runs (JSON array of strings — union
|
|
265
|
+
// of suite-level tags actually executed in the run, plus any explicit
|
|
266
|
+
// --tag filters). Powers `coverage --union tag:<name>` (TASK-274).
|
|
267
|
+
db.exec("ALTER TABLE runs ADD COLUMN tags TEXT");
|
|
268
|
+
}
|
|
269
|
+
if (ver >= 9 && ver < 10) {
|
|
270
|
+
// Migration v9→v10 (ARV-55): classify each historical run by suite
|
|
271
|
+
// kind so coverage's default query becomes a column compare. The
|
|
272
|
+
// CHECK constraint can't be added retroactively without a table
|
|
273
|
+
// rebuild — accept the looser column for legacy rows; new INSERTs
|
|
274
|
+
// go through `createRun()` which only emits known kinds.
|
|
275
|
+
db.exec("ALTER TABLE runs ADD COLUMN run_kind TEXT NOT NULL DEFAULT 'regular'");
|
|
276
|
+
// Backfill: derive kind per existing run from its stored results.
|
|
277
|
+
// `every` semantics mirror the runtime `detectRunKind` helper —
|
|
278
|
+
// pure-probe / pure-check vs anything else.
|
|
279
|
+
db.exec(`
|
|
280
|
+
UPDATE runs SET run_kind = 'probe'
|
|
281
|
+
WHERE id IN (
|
|
282
|
+
SELECT r.id FROM runs r
|
|
283
|
+
WHERE EXISTS (SELECT 1 FROM results WHERE run_id = r.id AND suite_file IS NOT NULL AND suite_file LIKE '%probes/%')
|
|
284
|
+
AND NOT EXISTS (SELECT 1 FROM results WHERE run_id = r.id AND suite_file IS NOT NULL AND suite_file NOT LIKE '%probes/%')
|
|
285
|
+
)
|
|
286
|
+
`);
|
|
287
|
+
db.exec(`
|
|
288
|
+
UPDATE runs SET run_kind = 'check'
|
|
289
|
+
WHERE id IN (
|
|
290
|
+
SELECT r.id FROM runs r
|
|
291
|
+
WHERE EXISTS (SELECT 1 FROM results WHERE run_id = r.id AND suite_file IS NOT NULL AND suite_file LIKE '%checks/%')
|
|
292
|
+
AND NOT EXISTS (SELECT 1 FROM results WHERE run_id = r.id AND suite_file IS NOT NULL AND suite_file NOT LIKE '%checks/%')
|
|
293
|
+
)
|
|
294
|
+
`);
|
|
295
|
+
}
|
|
165
296
|
db.exec(`PRAGMA user_version = ${SCHEMA_VERSION}`);
|
|
166
297
|
})();
|
|
167
298
|
}
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import { dirname, basename, join } from "path";
|
|
2
|
-
import { parse } from "../../core/parser/yaml-parser.ts";
|
|
3
|
-
import {
|
|
4
|
-
buildCollection,
|
|
5
|
-
buildEnvironment,
|
|
6
|
-
deriveCollectionName,
|
|
7
|
-
} from "../../core/exporter/postman.ts";
|
|
8
|
-
import { printError, printSuccess, printWarning } from "../output.ts";
|
|
9
|
-
import { jsonOk, jsonError, printJson } from "../json-envelope.ts";
|
|
10
|
-
|
|
11
|
-
export interface ExportOptions {
|
|
12
|
-
testsPath: string;
|
|
13
|
-
output: string;
|
|
14
|
-
env?: string;
|
|
15
|
-
collectionName?: string;
|
|
16
|
-
json?: boolean;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export async function exportCommand(options: ExportOptions): Promise<number> {
|
|
20
|
-
// 1. Parse test suites
|
|
21
|
-
let suites;
|
|
22
|
-
try {
|
|
23
|
-
suites = await parse(options.testsPath);
|
|
24
|
-
} catch (err) {
|
|
25
|
-
const msg = `Failed to parse tests: ${(err as Error).message}`;
|
|
26
|
-
if (options.json) {
|
|
27
|
-
printJson(jsonError("export postman", [msg]));
|
|
28
|
-
} else {
|
|
29
|
-
printError(msg);
|
|
30
|
-
}
|
|
31
|
-
return 2;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (suites.length === 0) {
|
|
35
|
-
const msg = "No test suites found";
|
|
36
|
-
if (options.json) {
|
|
37
|
-
printJson(jsonError("export postman", [msg]));
|
|
38
|
-
} else {
|
|
39
|
-
printError(msg);
|
|
40
|
-
}
|
|
41
|
-
return 1;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// 2. Derive collection name
|
|
45
|
-
const collectionName =
|
|
46
|
-
options.collectionName ?? deriveCollectionName(options.testsPath);
|
|
47
|
-
|
|
48
|
-
// 3. Build collection
|
|
49
|
-
const { collection, warnings } = buildCollection(suites, collectionName);
|
|
50
|
-
|
|
51
|
-
// Count total items across all folders
|
|
52
|
-
const totalItems = collection.item.reduce((sum, folder) => sum + folder.item.length, 0);
|
|
53
|
-
|
|
54
|
-
// 4. Write collection file
|
|
55
|
-
try {
|
|
56
|
-
await Bun.write(options.output, JSON.stringify(collection, null, 2));
|
|
57
|
-
} catch (err) {
|
|
58
|
-
const msg = `Failed to write collection file: ${(err as Error).message}`;
|
|
59
|
-
if (options.json) {
|
|
60
|
-
printJson(jsonError("export postman", [msg], warnings));
|
|
61
|
-
} else {
|
|
62
|
-
printError(msg);
|
|
63
|
-
}
|
|
64
|
-
return 2;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// 5. Optional env export
|
|
68
|
-
let envOutput: string | undefined;
|
|
69
|
-
if (options.env) {
|
|
70
|
-
let envVars: Record<string, string>;
|
|
71
|
-
try {
|
|
72
|
-
const text = await Bun.file(options.env).text();
|
|
73
|
-
const parsed = Bun.YAML.parse(text);
|
|
74
|
-
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
75
|
-
throw new Error("Environment file must be a YAML object");
|
|
76
|
-
}
|
|
77
|
-
// Convert all values to strings
|
|
78
|
-
envVars = {};
|
|
79
|
-
for (const [k, v] of Object.entries(parsed as Record<string, unknown>)) {
|
|
80
|
-
envVars[k] = String(v);
|
|
81
|
-
}
|
|
82
|
-
} catch (err) {
|
|
83
|
-
const msg = `Failed to read env file: ${(err as Error).message}`;
|
|
84
|
-
if (options.json) {
|
|
85
|
-
printJson(jsonError("export postman", [msg], warnings));
|
|
86
|
-
} else {
|
|
87
|
-
printError(msg);
|
|
88
|
-
}
|
|
89
|
-
return 2;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Derive env name: e.g. ".env.staging.yaml" → "staging", ".env.yaml" → collectionName
|
|
93
|
-
const envBasename = basename(options.env);
|
|
94
|
-
const envNameMatch = envBasename.match(/^\.?env\.(.+?)\.ya?ml$/);
|
|
95
|
-
const envName = envNameMatch ? envNameMatch[1]! : collectionName;
|
|
96
|
-
|
|
97
|
-
const environment = buildEnvironment(envVars, envName);
|
|
98
|
-
|
|
99
|
-
// Output path: same directory as output, same base name with .postman_environment.json
|
|
100
|
-
const outBase = basename(options.output).replace(/\.postman\.json$/, "").replace(/\.json$/, "");
|
|
101
|
-
const outDir = dirname(options.output);
|
|
102
|
-
envOutput = join(outDir, `${outBase}.postman_environment.json`);
|
|
103
|
-
|
|
104
|
-
try {
|
|
105
|
-
await Bun.write(envOutput, JSON.stringify(environment, null, 2));
|
|
106
|
-
} catch (err) {
|
|
107
|
-
const msg = `Failed to write environment file: ${(err as Error).message}`;
|
|
108
|
-
if (options.json) {
|
|
109
|
-
printJson(jsonError("export postman", [msg], warnings));
|
|
110
|
-
} else {
|
|
111
|
-
printError(msg);
|
|
112
|
-
}
|
|
113
|
-
return 2;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// 6. Output result
|
|
118
|
-
if (options.json) {
|
|
119
|
-
printJson(
|
|
120
|
-
jsonOk(
|
|
121
|
-
"export postman",
|
|
122
|
-
{
|
|
123
|
-
output: options.output,
|
|
124
|
-
suites: suites.length,
|
|
125
|
-
items: totalItems,
|
|
126
|
-
...(envOutput !== undefined ? { envOutput } : {}),
|
|
127
|
-
},
|
|
128
|
-
warnings
|
|
129
|
-
)
|
|
130
|
-
);
|
|
131
|
-
} else {
|
|
132
|
-
for (const w of warnings) {
|
|
133
|
-
printWarning(w);
|
|
134
|
-
}
|
|
135
|
-
printSuccess(
|
|
136
|
-
`Exported ${suites.length} suite(s) / ${totalItems} request(s) → ${options.output}`
|
|
137
|
-
);
|
|
138
|
-
if (envOutput) {
|
|
139
|
-
printSuccess(`Environment exported → ${envOutput}`);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
return 0;
|
|
144
|
-
}
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
readOpenApiSpec,
|
|
3
|
-
extractEndpoints,
|
|
4
|
-
extractSecuritySchemes,
|
|
5
|
-
scanCoveredEndpoints,
|
|
6
|
-
filterUncoveredEndpoints,
|
|
7
|
-
} from "../../core/generator/index.ts";
|
|
8
|
-
import { compressEndpointsWithSchemas, buildGenerationGuide } from "../../core/generator/guide-builder.ts";
|
|
9
|
-
import { planChunks, filterByTag } from "../../core/generator/chunker.ts";
|
|
10
|
-
import { findCollectionBySpec } from "../../db/queries.ts";
|
|
11
|
-
import { printError } from "../output.ts";
|
|
12
|
-
import { jsonOk, jsonError, printJson } from "../json-envelope.ts";
|
|
13
|
-
|
|
14
|
-
export interface GuideOptions {
|
|
15
|
-
specPath: string;
|
|
16
|
-
testsDir?: string;
|
|
17
|
-
tag?: string;
|
|
18
|
-
json?: boolean;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export async function guideCommand(options: GuideOptions): Promise<number> {
|
|
22
|
-
try {
|
|
23
|
-
const doc = await readOpenApiSpec(options.specPath);
|
|
24
|
-
let endpoints = extractEndpoints(doc);
|
|
25
|
-
const securitySchemes = extractSecuritySchemes(doc);
|
|
26
|
-
const baseUrl = ((doc as any).servers?.[0]?.url) as string | undefined;
|
|
27
|
-
const title = (doc as any).info?.title as string | undefined;
|
|
28
|
-
|
|
29
|
-
let outputDir = options.testsDir;
|
|
30
|
-
if (!outputDir) {
|
|
31
|
-
try {
|
|
32
|
-
const collection = findCollectionBySpec(options.specPath);
|
|
33
|
-
outputDir = collection?.test_path ?? "./tests/";
|
|
34
|
-
} catch {
|
|
35
|
-
outputDir = "./tests/";
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
let coverageInfo: { covered: number; total: number; percentage: number } | undefined;
|
|
40
|
-
if (options.testsDir) {
|
|
41
|
-
const totalBefore = endpoints.length;
|
|
42
|
-
const covered = await scanCoveredEndpoints(options.testsDir);
|
|
43
|
-
const uncovered = filterUncoveredEndpoints(endpoints, covered);
|
|
44
|
-
const coveredCount = totalBefore - uncovered.length;
|
|
45
|
-
const percentage = totalBefore > 0 ? Math.round((coveredCount / totalBefore) * 100) : 100;
|
|
46
|
-
coverageInfo = { covered: coveredCount, total: totalBefore, percentage };
|
|
47
|
-
endpoints = uncovered;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (endpoints.length === 0) {
|
|
51
|
-
if (options.json) {
|
|
52
|
-
printJson(jsonOk("guide", { fullyCovered: true, ...coverageInfo }));
|
|
53
|
-
} else {
|
|
54
|
-
console.log("All endpoints are covered.");
|
|
55
|
-
}
|
|
56
|
-
return 0;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (options.tag) {
|
|
60
|
-
endpoints = filterByTag(endpoints, options.tag);
|
|
61
|
-
if (endpoints.length === 0) {
|
|
62
|
-
const msg = `No endpoints found for tag "${options.tag}"`;
|
|
63
|
-
if (options.json) printJson(jsonError("guide", [msg]));
|
|
64
|
-
else printError(msg);
|
|
65
|
-
return 1;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const plan = planChunks(endpoints);
|
|
70
|
-
|
|
71
|
-
if (plan.needsChunking && !options.tag) {
|
|
72
|
-
if (options.json) {
|
|
73
|
-
printJson(jsonOk("guide", {
|
|
74
|
-
mode: "plan",
|
|
75
|
-
title: title ?? "API",
|
|
76
|
-
totalEndpoints: plan.totalEndpoints,
|
|
77
|
-
chunks: plan.chunks,
|
|
78
|
-
...(coverageInfo ? { coverage: coverageInfo } : {}),
|
|
79
|
-
}));
|
|
80
|
-
} else {
|
|
81
|
-
console.log(`API has ${plan.totalEndpoints} endpoints across ${plan.chunks.length} tags.`);
|
|
82
|
-
console.log("Generate per-tag with --tag <name>:\n");
|
|
83
|
-
for (const chunk of plan.chunks) {
|
|
84
|
-
console.log(` --tag ${chunk.tag} (${chunk.count} endpoints)`);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return 0;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const coverageHeader = coverageInfo
|
|
91
|
-
? `## Coverage: ${coverageInfo.covered}/${coverageInfo.total} endpoints covered (${coverageInfo.percentage}%). Generating tests for ${endpoints.length} uncovered endpoints:`
|
|
92
|
-
: undefined;
|
|
93
|
-
|
|
94
|
-
const apiContext = compressEndpointsWithSchemas(endpoints, securitySchemes);
|
|
95
|
-
const guide = buildGenerationGuide({
|
|
96
|
-
title: options.tag ? `${title ?? "API"} — tag: ${options.tag}` : (title ?? "API"),
|
|
97
|
-
baseUrl,
|
|
98
|
-
apiContext,
|
|
99
|
-
outputDir,
|
|
100
|
-
securitySchemes,
|
|
101
|
-
endpointCount: endpoints.length,
|
|
102
|
-
coverageHeader,
|
|
103
|
-
includeFormat: true,
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
if (options.json) {
|
|
107
|
-
printJson(jsonOk("guide", {
|
|
108
|
-
title: title ?? "API",
|
|
109
|
-
endpointCount: endpoints.length,
|
|
110
|
-
outputDir,
|
|
111
|
-
guide,
|
|
112
|
-
...(coverageInfo ? { coverage: coverageInfo } : {}),
|
|
113
|
-
}));
|
|
114
|
-
} else {
|
|
115
|
-
console.log(guide);
|
|
116
|
-
}
|
|
117
|
-
return 0;
|
|
118
|
-
} catch (err) {
|
|
119
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
120
|
-
if (options.json) {
|
|
121
|
-
printJson(jsonError("guide", [message]));
|
|
122
|
-
} else {
|
|
123
|
-
printError(message);
|
|
124
|
-
}
|
|
125
|
-
return 2;
|
|
126
|
-
}
|
|
127
|
-
}
|
package/src/cli/commands/init.ts
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { setupApi } from "../../core/setup-api.ts";
|
|
2
|
-
import { printError, printSuccess } from "../output.ts";
|
|
3
|
-
import { jsonOk, jsonError, printJson } from "../json-envelope.ts";
|
|
4
|
-
|
|
5
|
-
export interface InitOptions {
|
|
6
|
-
name?: string;
|
|
7
|
-
spec?: string;
|
|
8
|
-
baseUrl?: string;
|
|
9
|
-
dir?: string;
|
|
10
|
-
force?: boolean;
|
|
11
|
-
insecure?: boolean;
|
|
12
|
-
dbPath?: string;
|
|
13
|
-
json?: boolean;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export async function initCommand(options: InitOptions): Promise<number> {
|
|
17
|
-
try {
|
|
18
|
-
const envVars: Record<string, string> = {};
|
|
19
|
-
if (options.baseUrl) envVars.base_url = options.baseUrl;
|
|
20
|
-
|
|
21
|
-
const result = await setupApi({
|
|
22
|
-
name: options.name,
|
|
23
|
-
spec: options.spec,
|
|
24
|
-
dir: options.dir,
|
|
25
|
-
envVars: Object.keys(envVars).length > 0 ? envVars : undefined,
|
|
26
|
-
dbPath: options.dbPath,
|
|
27
|
-
force: options.force,
|
|
28
|
-
insecure: options.insecure,
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
if (options.json) {
|
|
32
|
-
printJson(jsonOk("init", {
|
|
33
|
-
collectionId: result.collectionId,
|
|
34
|
-
baseDir: result.baseDir,
|
|
35
|
-
testPath: result.testPath,
|
|
36
|
-
endpoints: result.specEndpoints,
|
|
37
|
-
warnings: result.warnings ?? [],
|
|
38
|
-
}, result.warnings));
|
|
39
|
-
} else {
|
|
40
|
-
printSuccess(`Created API '${options.name ?? "api"}' at ${result.baseDir} (${result.specEndpoints} endpoints)`);
|
|
41
|
-
if (result.warnings) {
|
|
42
|
-
for (const w of result.warnings) {
|
|
43
|
-
process.stderr.write(`Warning: ${w}\n`);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
return 0;
|
|
48
|
-
} catch (err) {
|
|
49
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
50
|
-
if (options.json) {
|
|
51
|
-
printJson(jsonError("init", [message]));
|
|
52
|
-
} else {
|
|
53
|
-
printError(message);
|
|
54
|
-
}
|
|
55
|
-
return 2;
|
|
56
|
-
}
|
|
57
|
-
}
|