@njeybe/nativ 2.0.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 +40 -0
- package/LICENSE +21 -0
- package/README.md +334 -0
- package/bin/cli.js +31 -0
- package/dist/commands/bench.d.ts +8 -0
- package/dist/commands/bench.js +52 -0
- package/dist/commands/db.d.ts +22 -0
- package/dist/commands/db.js +415 -0
- package/dist/commands/doctor.d.ts +38 -0
- package/dist/commands/doctor.js +169 -0
- package/dist/commands/hook.d.ts +31 -0
- package/dist/commands/hook.js +105 -0
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.js +144 -0
- package/dist/commands/mcp.d.ts +5 -0
- package/dist/commands/mcp.js +16 -0
- package/dist/commands/setup.d.ts +17 -0
- package/dist/commands/setup.js +51 -0
- package/dist/commands/status.d.ts +5 -0
- package/dist/commands/status.js +75 -0
- package/dist/commands/task.d.ts +83 -0
- package/dist/commands/task.js +807 -0
- package/dist/commands/test-gen.d.ts +14 -0
- package/dist/commands/test-gen.js +52 -0
- package/dist/commands/triage.d.ts +70 -0
- package/dist/commands/triage.js +343 -0
- package/dist/commands/update.d.ts +14 -0
- package/dist/commands/update.js +112 -0
- package/dist/commands/validate.d.ts +1 -0
- package/dist/commands/validate.js +89 -0
- package/dist/commands/verify.d.ts +8 -0
- package/dist/commands/verify.js +65 -0
- package/dist/commands/worktree.d.ts +47 -0
- package/dist/commands/worktree.js +396 -0
- package/dist/core/benchmark.d.ts +57 -0
- package/dist/core/benchmark.js +772 -0
- package/dist/core/enforcement.d.ts +80 -0
- package/dist/core/enforcement.js +271 -0
- package/dist/core/legacy-templates.d.ts +24 -0
- package/dist/core/legacy-templates.js +60 -0
- package/dist/core/lock-manager.d.ts +54 -0
- package/dist/core/lock-manager.js +260 -0
- package/dist/core/root-resolver.d.ts +32 -0
- package/dist/core/root-resolver.js +149 -0
- package/dist/core/setup-assets.d.ts +123 -0
- package/dist/core/setup-assets.js +438 -0
- package/dist/core/telemetry.d.ts +156 -0
- package/dist/core/telemetry.js +416 -0
- package/dist/core/test-generator-types.d.ts +227 -0
- package/dist/core/test-generator-types.js +179 -0
- package/dist/core/test-generator.d.ts +34 -0
- package/dist/core/test-generator.js +1690 -0
- package/dist/core/tier1-liaison.d.ts +139 -0
- package/dist/core/tier1-liaison.js +320 -0
- package/dist/core/verifier.d.ts +52 -0
- package/dist/core/verifier.js +249 -0
- package/dist/core/version.d.ts +5 -0
- package/dist/core/version.js +14 -0
- package/dist/core/worktree-merge.d.ts +30 -0
- package/dist/core/worktree-merge.js +98 -0
- package/dist/db/data-engine.d.ts +46 -0
- package/dist/db/data-engine.js +802 -0
- package/dist/db/diff.d.ts +39 -0
- package/dist/db/diff.js +420 -0
- package/dist/db/env-parser.d.ts +86 -0
- package/dist/db/env-parser.js +549 -0
- package/dist/db/introspector.d.ts +21 -0
- package/dist/db/introspector.js +835 -0
- package/dist/db/types.d.ts +217 -0
- package/dist/db/types.js +9 -0
- package/dist/governor/circuit-breaker.d.ts +100 -0
- package/dist/governor/circuit-breaker.js +231 -0
- package/dist/governor/evaluator.d.ts +67 -0
- package/dist/governor/evaluator.js +851 -0
- package/dist/governor/index.d.ts +6 -0
- package/dist/governor/index.js +6 -0
- package/dist/governor/rules/api-rules.d.ts +2 -0
- package/dist/governor/rules/api-rules.js +140 -0
- package/dist/governor/rules/db-rules.d.ts +2 -0
- package/dist/governor/rules/db-rules.js +180 -0
- package/dist/governor/store.d.ts +33 -0
- package/dist/governor/store.js +84 -0
- package/dist/governor/types.d.ts +64 -0
- package/dist/governor/types.js +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +358 -0
- package/dist/mcp/server.d.ts +3 -0
- package/dist/mcp/server.js +309 -0
- package/dist/providers/claude-api.d.ts +5 -0
- package/dist/providers/claude-api.js +75 -0
- package/dist/providers/claude-cli.d.ts +13 -0
- package/dist/providers/claude-cli.js +134 -0
- package/dist/providers/gemini.d.ts +8 -0
- package/dist/providers/gemini.js +78 -0
- package/dist/providers/index.d.ts +5 -0
- package/dist/providers/index.js +5 -0
- package/dist/providers/registry.d.ts +38 -0
- package/dist/providers/registry.js +121 -0
- package/dist/providers/types.d.ts +74 -0
- package/dist/providers/types.js +25 -0
- package/dist/runner/agent-supervisor.d.ts +363 -0
- package/dist/runner/agent-supervisor.js +1672 -0
- package/dist/scanner/context-builder.d.ts +2 -0
- package/dist/scanner/context-builder.js +29 -0
- package/dist/scanner/detector.d.ts +2 -0
- package/dist/scanner/detector.js +760 -0
- package/dist/scanner/types.d.ts +80 -0
- package/dist/scanner/types.js +50 -0
- package/dist/server/studio-server.d.ts +39 -0
- package/dist/server/studio-server.js +1931 -0
- package/dist/server/studio-ui.d.ts +15 -0
- package/dist/server/studio-ui.js +5321 -0
- package/package.json +70 -0
- package/plugin/.claude-plugin/plugin.json +18 -0
- package/plugin/.mcp.json +10 -0
- package/plugin/agents/architect.md +28 -0
- package/plugin/agents/verifier.md +28 -0
- package/plugin/agents/worker.md +26 -0
- package/plugin/hooks/hooks.json +28 -0
- package/plugin/skills/nativ/SKILL.md +75 -0
- package/templates/.claudeignore +45 -0
- package/templates/AGENTS.md +67 -0
- package/templates/CLAUDE.md +32 -0
- package/templates/GEMINI.md +66 -0
- package/templates/claude-agents/architect.md +28 -0
- package/templates/claude-agents/verifier.md +28 -0
- package/templates/claude-agents/worker.md +26 -0
- package/templates/dot-ai/api_contracts.json +66 -0
- package/templates/dot-ai/context.md +38 -0
- package/templates/dot-ai/db_schema.json +39 -0
- package/templates/dot-ai/master_plan.json +28 -0
- package/templates/dot-ai/subagents/backend.md +11 -0
- package/templates/dot-ai/subagents/database.md +10 -0
- package/templates/dot-ai/subagents/db-migration.md +11 -0
- package/templates/dot-ai/subagents/devops-agent.md +11 -0
- package/templates/dot-ai/subagents/flutter-developer.md +11 -0
- package/templates/dot-ai/subagents/frontend.md +126 -0
- package/templates/dot-ai/subagents/qa-tester.md +13 -0
- package/templates/dot-ai/subagents/security-auditor.md +11 -0
- package/templates/dot-ai/ui_specs.md +78 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contract-to-test generator types (nativ test gen).
|
|
3
|
+
* Result/request shapes conform to POST /api/tests/generate in .ai/api_contracts.json.
|
|
4
|
+
* Endpoint suites are built from .ai/api_contracts.json, database integrity suites from .ai/db_schema.json.
|
|
5
|
+
*/
|
|
6
|
+
export const TEST_FRAMEWORKS = ['vitest', 'jest', 'node:test', 'pytest', 'go'];
|
|
7
|
+
export function isTestFramework(value) {
|
|
8
|
+
return TEST_FRAMEWORKS.includes(value);
|
|
9
|
+
}
|
|
10
|
+
export const DEFAULT_TEST_OUTPUT_DIR = 'tests/contract';
|
|
11
|
+
export const DEFAULT_TEST_FRAMEWORK = 'vitest';
|
|
12
|
+
export const DEFAULT_TEST_BASE_URL = 'http://localhost:3000';
|
|
13
|
+
const PRIMITIVES = {
|
|
14
|
+
string: 'string',
|
|
15
|
+
number: 'number',
|
|
16
|
+
integer: 'integer',
|
|
17
|
+
int: 'integer',
|
|
18
|
+
boolean: 'boolean',
|
|
19
|
+
bool: 'boolean',
|
|
20
|
+
null: 'null',
|
|
21
|
+
any: 'any',
|
|
22
|
+
unknown: 'any',
|
|
23
|
+
};
|
|
24
|
+
/** Splits on `separator` only outside (), {}, [] and quotes. */
|
|
25
|
+
function splitTopLevel(input, separator) {
|
|
26
|
+
const parts = [];
|
|
27
|
+
let depth = 0;
|
|
28
|
+
let quote = null;
|
|
29
|
+
let start = 0;
|
|
30
|
+
for (let i = 0; i < input.length; i++) {
|
|
31
|
+
const ch = input[i];
|
|
32
|
+
if (quote) {
|
|
33
|
+
if (ch === quote)
|
|
34
|
+
quote = null;
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (ch === "'" || ch === '"')
|
|
38
|
+
quote = ch;
|
|
39
|
+
else if (ch === '(' || ch === '{' || ch === '[')
|
|
40
|
+
depth++;
|
|
41
|
+
else if (ch === ')' || ch === '}' || ch === ']')
|
|
42
|
+
depth--;
|
|
43
|
+
else if (depth === 0 && input.startsWith(separator, i)) {
|
|
44
|
+
parts.push(input.slice(start, i));
|
|
45
|
+
start = i + separator.length;
|
|
46
|
+
i += separator.length - 1;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
parts.push(input.slice(start));
|
|
50
|
+
return parts.map((p) => p.trim()).filter((p) => p.length > 0);
|
|
51
|
+
}
|
|
52
|
+
/** Separates "head (note)" when the parenthetical is top-level and trailing. */
|
|
53
|
+
function splitNote(descriptor) {
|
|
54
|
+
if (!descriptor.endsWith(')'))
|
|
55
|
+
return { head: descriptor, note: null };
|
|
56
|
+
let depth = 0;
|
|
57
|
+
for (let i = descriptor.length - 1; i >= 0; i--) {
|
|
58
|
+
const ch = descriptor[i];
|
|
59
|
+
if (ch === ')')
|
|
60
|
+
depth++;
|
|
61
|
+
else if (ch === '(') {
|
|
62
|
+
depth--;
|
|
63
|
+
if (depth === 0) {
|
|
64
|
+
const head = descriptor.slice(0, i).trim();
|
|
65
|
+
return head ? { head, note: descriptor.slice(i + 1, -1).trim() } : { head: descriptor, note: null };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return { head: descriptor, note: null };
|
|
70
|
+
}
|
|
71
|
+
const LITERAL_UNION = /^(?:'[^']*'|-?\d+(?:\.\d+)?)(?:\s*\|\s*(?:'[^']*'|-?\d+(?:\.\d+)?))*$/;
|
|
72
|
+
function parseLiteral(token) {
|
|
73
|
+
const t = token.trim();
|
|
74
|
+
return t.startsWith("'") ? t.slice(1, -1) : Number(t);
|
|
75
|
+
}
|
|
76
|
+
function parseHead(head) {
|
|
77
|
+
const variants = splitTopLevel(head, '|');
|
|
78
|
+
if (variants.length > 1) {
|
|
79
|
+
return { kind: 'union', variants: variants.map((v) => parseHead(v)) };
|
|
80
|
+
}
|
|
81
|
+
const h = head.trim();
|
|
82
|
+
const arrayMatch = /^array of\s+(.+)$/i.exec(h);
|
|
83
|
+
if (arrayMatch)
|
|
84
|
+
return { kind: 'array', items: parseHead(arrayMatch[1]) };
|
|
85
|
+
if (h.endsWith('[]'))
|
|
86
|
+
return { kind: 'array', items: parseHead(h.slice(0, -2)) };
|
|
87
|
+
if (h.startsWith('{') && h.endsWith('}'))
|
|
88
|
+
return parseInlineObject(h.slice(1, -1));
|
|
89
|
+
if (/^object$/i.test(h))
|
|
90
|
+
return { kind: 'object', properties: {}, open: true };
|
|
91
|
+
if (LITERAL_UNION.test(h)) {
|
|
92
|
+
const value = parseLiteral(h);
|
|
93
|
+
return { kind: 'enum', base: typeof value === 'number' ? 'number' : 'string', values: [value] };
|
|
94
|
+
}
|
|
95
|
+
const primitive = PRIMITIVES[h.toLowerCase()];
|
|
96
|
+
if (primitive)
|
|
97
|
+
return { kind: 'primitive', type: primitive };
|
|
98
|
+
if (/^[A-Z][A-Za-z0-9_]*$/.test(h))
|
|
99
|
+
return { kind: 'ref', name: h };
|
|
100
|
+
return { kind: 'primitive', type: 'any', note: h };
|
|
101
|
+
}
|
|
102
|
+
/** Parses "key: type, key2: 'a' | 'b'" (the inside of an inline `{ ... }` descriptor). */
|
|
103
|
+
function parseInlineObject(body) {
|
|
104
|
+
const properties = {};
|
|
105
|
+
for (const entry of splitTopLevel(body, ',')) {
|
|
106
|
+
const colon = entry.indexOf(':');
|
|
107
|
+
if (colon === -1)
|
|
108
|
+
continue;
|
|
109
|
+
let key = entry.slice(0, colon).trim();
|
|
110
|
+
const optional = key.endsWith('?');
|
|
111
|
+
if (optional)
|
|
112
|
+
key = key.slice(0, -1).trim();
|
|
113
|
+
const node = parseFieldDescriptor(entry.slice(colon + 1).trim());
|
|
114
|
+
properties[key] = optional ? { ...node, optional: true } : node;
|
|
115
|
+
}
|
|
116
|
+
return { kind: 'object', properties, open: false };
|
|
117
|
+
}
|
|
118
|
+
/** Collapses "T | null" style unions of literals into one enum node. */
|
|
119
|
+
function mergeLiteralUnion(node) {
|
|
120
|
+
if (node.kind !== 'union' || !node.variants.every((v) => v.kind === 'enum'))
|
|
121
|
+
return node;
|
|
122
|
+
const values = node.variants.flatMap((v) => v.values);
|
|
123
|
+
const base = values.every((v) => typeof v === 'number') ? 'number' : 'string';
|
|
124
|
+
return { kind: 'enum', base, values };
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Parses a contract descriptor string into a SchemaNode.
|
|
128
|
+
* Examples: "boolean", "string | null", "integer (optional, default 25, max 100)",
|
|
129
|
+
* "string ('dev' | 'prod')", "boolean (required if env === 'prod')", "array of TableSchema",
|
|
130
|
+
* "array of { key: string, targetEnv: 'dev' | 'prod' }".
|
|
131
|
+
*/
|
|
132
|
+
export function parseFieldDescriptor(descriptor) {
|
|
133
|
+
const { head, note } = splitNote(descriptor.trim());
|
|
134
|
+
let node = mergeLiteralUnion(parseHead(head));
|
|
135
|
+
if (note === null)
|
|
136
|
+
return node;
|
|
137
|
+
const extras = {};
|
|
138
|
+
let rest = note;
|
|
139
|
+
const requiredIf = /^required if\s+(.+)$/i.exec(rest);
|
|
140
|
+
if (requiredIf) {
|
|
141
|
+
extras.optional = true;
|
|
142
|
+
extras.requiredIf = requiredIf[1].trim();
|
|
143
|
+
rest = '';
|
|
144
|
+
}
|
|
145
|
+
else if (/^optional\b/i.test(rest)) {
|
|
146
|
+
extras.optional = true;
|
|
147
|
+
rest = rest.replace(/^optional\b\s*[:,]?\s*/i, '');
|
|
148
|
+
}
|
|
149
|
+
// "string ('dev' | 'prod')" or "string (optional: 'asc' | 'desc')" narrows to an enum.
|
|
150
|
+
if (rest && LITERAL_UNION.test(rest) && (node.kind === 'primitive' || node.kind === 'enum')) {
|
|
151
|
+
const values = splitTopLevel(rest, '|').map(parseLiteral);
|
|
152
|
+
const base = values.every((v) => typeof v === 'number') ? 'number' : 'string';
|
|
153
|
+
node = { kind: 'enum', base, values };
|
|
154
|
+
rest = '';
|
|
155
|
+
}
|
|
156
|
+
if (rest)
|
|
157
|
+
extras.note = node.note ? `${node.note}; ${rest}` : rest;
|
|
158
|
+
return { ...node, ...extras };
|
|
159
|
+
}
|
|
160
|
+
/** Builds a SchemaNode from a contract body value (nested objects, descriptor strings, or null). */
|
|
161
|
+
export function buildSchemaNode(value) {
|
|
162
|
+
if (value === null || value === undefined)
|
|
163
|
+
return { kind: 'primitive', type: 'null' };
|
|
164
|
+
if (typeof value === 'string')
|
|
165
|
+
return parseFieldDescriptor(value);
|
|
166
|
+
const properties = {};
|
|
167
|
+
for (const [key, child] of Object.entries(value)) {
|
|
168
|
+
properties[key] = buildSchemaNode(child);
|
|
169
|
+
}
|
|
170
|
+
return { kind: 'object', properties, open: false };
|
|
171
|
+
}
|
|
172
|
+
/** True when a schema node accepts null (nullable union or explicit null). */
|
|
173
|
+
export function isNullableNode(node) {
|
|
174
|
+
if (node.kind === 'primitive')
|
|
175
|
+
return node.type === 'null' || node.type === 'any';
|
|
176
|
+
if (node.kind === 'union')
|
|
177
|
+
return node.variants.some(isNullableNode);
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type DatabaseEngine, type TableSchema } from '../db/types.js';
|
|
2
|
+
import { type ApiContractsDocument, type ApiEndpointContract, type DbSchemaDocument, type ResolvedTestGenOptions, type SchemaNode, type TestFramework, type TestFrameworkAdapter, type TestGenOptions, type TestGenResult, type TestSuiteSpec } from './test-generator-types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Contract-to-test generator (nativ test gen).
|
|
5
|
+
*
|
|
6
|
+
* Contracts are compiled into framework-agnostic TestSuiteSpecs, then rendered by a
|
|
7
|
+
* TestFrameworkAdapter. Generated endpoint tests never send a well-formed request to a
|
|
8
|
+
* mutating endpoint: POST/PUT/PATCH/DELETE are only exercised with invalid or anonymous
|
|
9
|
+
* requests, so running the suite cannot write data or trigger side effects.
|
|
10
|
+
*/
|
|
11
|
+
/** Present in the header of every generated file; files without it are never overwritten. */
|
|
12
|
+
export declare const GENERATED_MARKER = "Code generated by nativ test gen";
|
|
13
|
+
export declare class TestGenError extends Error {
|
|
14
|
+
readonly code: string;
|
|
15
|
+
constructor(code: string, message: string);
|
|
16
|
+
}
|
|
17
|
+
export declare function resolveTestGenOptions(options: TestGenOptions): ResolvedTestGenOptions;
|
|
18
|
+
export declare function loadApiContracts(targetDir: string): ApiContractsDocument | null;
|
|
19
|
+
/** .ai/db_schema.json with tables normalized the same way `nativ db diff --target contract` reads them. */
|
|
20
|
+
export declare function loadDbSchemaContract(targetDir: string): DbSchemaDocument | null;
|
|
21
|
+
/** Same semantics as the generated shapeErrors helpers: true when `value` conforms to `node`. */
|
|
22
|
+
export declare function matchesSchema(value: unknown, node: SchemaNode): boolean;
|
|
23
|
+
export declare function buildEndpointSuite(endpoint: ApiEndpointContract): TestSuiteSpec;
|
|
24
|
+
export declare function buildEndpointSuites(doc: ApiContractsDocument): TestSuiteSpec[];
|
|
25
|
+
/**
|
|
26
|
+
* Integrity checks for one contract table: existence, columns, primary key, nullability and
|
|
27
|
+
* unique constraints. Collections only get required-field checks, because sampled document
|
|
28
|
+
* stores report a field as nullable whenever some sampled document lacks it.
|
|
29
|
+
*/
|
|
30
|
+
export declare function buildTableSuite(table: TableSchema, engine: DatabaseEngine | null): TestSuiteSpec;
|
|
31
|
+
export declare function buildDbSuites(doc: DbSchemaDocument): TestSuiteSpec[];
|
|
32
|
+
export declare const TEST_FRAMEWORK_ADAPTERS: Record<TestFramework, TestFrameworkAdapter>;
|
|
33
|
+
/** Compiles .ai contracts into test files for the chosen framework (written unless dryRun). */
|
|
34
|
+
export declare function generateContractTests(options: TestGenOptions): TestGenResult;
|