@specferret/core 0.1.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.
Files changed (57) hide show
  1. package/dist/config.d.ts +20 -0
  2. package/dist/config.d.ts.map +1 -0
  3. package/dist/config.js +32 -0
  4. package/dist/config.js.map +1 -0
  5. package/dist/context/index.d.ts +27 -0
  6. package/dist/context/index.d.ts.map +1 -0
  7. package/dist/context/index.js +72 -0
  8. package/dist/context/index.js.map +1 -0
  9. package/dist/extractor/frontmatter.d.ts +25 -0
  10. package/dist/extractor/frontmatter.d.ts.map +1 -0
  11. package/dist/extractor/frontmatter.js +49 -0
  12. package/dist/extractor/frontmatter.js.map +1 -0
  13. package/dist/extractor/hash.d.ts +7 -0
  14. package/dist/extractor/hash.d.ts.map +1 -0
  15. package/dist/extractor/hash.js +25 -0
  16. package/dist/extractor/hash.js.map +1 -0
  17. package/dist/extractor/typescript.d.ts +14 -0
  18. package/dist/extractor/typescript.d.ts.map +1 -0
  19. package/dist/extractor/typescript.js +232 -0
  20. package/dist/extractor/typescript.js.map +1 -0
  21. package/dist/extractor/validator.d.ts +24 -0
  22. package/dist/extractor/validator.d.ts.map +1 -0
  23. package/dist/extractor/validator.js +141 -0
  24. package/dist/extractor/validator.js.map +1 -0
  25. package/dist/index.d.ts +13 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +15 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/reconciler/import-suggestions.d.ts +10 -0
  30. package/dist/reconciler/import-suggestions.d.ts.map +1 -0
  31. package/dist/reconciler/import-suggestions.js +110 -0
  32. package/dist/reconciler/import-suggestions.js.map +1 -0
  33. package/dist/reconciler/index.d.ts +53 -0
  34. package/dist/reconciler/index.d.ts.map +1 -0
  35. package/dist/reconciler/index.js +214 -0
  36. package/dist/reconciler/index.js.map +1 -0
  37. package/dist/store/factory.d.ts +7 -0
  38. package/dist/store/factory.d.ts.map +1 -0
  39. package/dist/store/factory.js +52 -0
  40. package/dist/store/factory.js.map +1 -0
  41. package/dist/store/postgres.d.ts +25 -0
  42. package/dist/store/postgres.d.ts.map +1 -0
  43. package/dist/store/postgres.js +68 -0
  44. package/dist/store/postgres.js.map +1 -0
  45. package/dist/store/sqlite.d.ts +25 -0
  46. package/dist/store/sqlite.d.ts.map +1 -0
  47. package/dist/store/sqlite.js +180 -0
  48. package/dist/store/sqlite.js.map +1 -0
  49. package/dist/store/types.d.ts +79 -0
  50. package/dist/store/types.d.ts.map +1 -0
  51. package/dist/store/types.js +2 -0
  52. package/dist/store/types.js.map +1 -0
  53. package/dist/utils/paths.d.ts +6 -0
  54. package/dist/utils/paths.d.ts.map +1 -0
  55. package/dist/utils/paths.js +21 -0
  56. package/dist/utils/paths.js.map +1 -0
  57. package/package.json +44 -0
@@ -0,0 +1,20 @@
1
+ export interface FerretConfig {
2
+ specDir: string;
3
+ filePattern: string;
4
+ includes?: string[];
5
+ store: "sqlite" | "postgres";
6
+ importSuggestions?: {
7
+ enabled?: boolean;
8
+ };
9
+ codeContracts?: {
10
+ include: string[];
11
+ watchNodes?: string[];
12
+ };
13
+ }
14
+ export declare const DEFAULT_CONFIG: FerretConfig;
15
+ /**
16
+ * Loads ferret.config.json from the project root.
17
+ * Falls back to DEFAULT_CONFIG if the file is not found.
18
+ */
19
+ export declare function loadConfig(): FerretConfig;
20
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,KAAK,EAAE,QAAQ,GAAG,UAAU,CAAC;IAC7B,iBAAiB,CAAC,EAAE;QAClB,OAAO,CAAC,EAAE,OAAO,CAAC;KACnB,CAAC;IACF,aAAa,CAAC,EAAE;QACd,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;KACvB,CAAC;CACH;AAED,eAAO,MAAM,cAAc,EAAE,YAQ5B,CAAC;AAEF;;;GAGG;AACH,wBAAgB,UAAU,IAAI,YAAY,CAezC"}
package/dist/config.js ADDED
@@ -0,0 +1,32 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { findProjectRoot } from "./utils/paths.js";
4
+ export const DEFAULT_CONFIG = {
5
+ specDir: "contracts/",
6
+ filePattern: "**/*.contract.md",
7
+ includes: ["**/*.contract.md"],
8
+ store: "sqlite",
9
+ importSuggestions: {
10
+ enabled: true,
11
+ },
12
+ };
13
+ /**
14
+ * Loads ferret.config.json from the project root.
15
+ * Falls back to DEFAULT_CONFIG if the file is not found.
16
+ */
17
+ export function loadConfig() {
18
+ const root = findProjectRoot();
19
+ const configPath = path.join(root, "ferret.config.json");
20
+ if (!fs.existsSync(configPath)) {
21
+ return { ...DEFAULT_CONFIG };
22
+ }
23
+ try {
24
+ const raw = fs.readFileSync(configPath, "utf-8");
25
+ const parsed = JSON.parse(raw);
26
+ return { ...DEFAULT_CONFIG, ...parsed };
27
+ }
28
+ catch {
29
+ return { ...DEFAULT_CONFIG };
30
+ }
31
+ }
32
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAgBnD,MAAM,CAAC,MAAM,cAAc,GAAiB;IAC1C,OAAO,EAAE,YAAY;IACrB,WAAW,EAAE,kBAAkB;IAC/B,QAAQ,EAAE,CAAC,kBAAkB,CAAC;IAC9B,KAAK,EAAE,QAAQ;IACf,iBAAiB,EAAE;QACjB,OAAO,EAAE,IAAI;KACd;CACF,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,UAAU;IACxB,MAAM,IAAI,GAAG,eAAe,EAAE,CAAC;IAC/B,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,oBAAoB,CAAC,CAAC;IAEzD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,OAAO,EAAE,GAAG,cAAc,EAAE,CAAC;IAC/B,CAAC;IAED,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACjD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA0B,CAAC;QACxD,OAAO,EAAE,GAAG,cAAc,EAAE,GAAG,MAAM,EAAE,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,GAAG,cAAc,EAAE,CAAC;IAC/B,CAAC;AACH,CAAC"}
@@ -0,0 +1,27 @@
1
+ import type { DBStore } from "../store/types.js";
2
+ export interface ContextContract {
3
+ id: string;
4
+ type: string;
5
+ shape: unknown;
6
+ status: string;
7
+ specFile: string | null;
8
+ codeFile: string | null;
9
+ }
10
+ export interface ContextEdge {
11
+ from: string;
12
+ to: string;
13
+ }
14
+ export interface FerretContext {
15
+ version: "2.0";
16
+ generated: string;
17
+ contracts: ContextContract[];
18
+ edges: ContextEdge[];
19
+ needsReview: string[];
20
+ }
21
+ /**
22
+ * Reads the full graph from the store and writes .ferret/context.json.
23
+ * Called automatically at the end of every ferret scan.
24
+ * Silent — no output. If write fails, it logs to stderr and continues.
25
+ */
26
+ export declare function writeContext(store: DBStore, projectRoot: string): Promise<void>;
27
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/context/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEjD,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,KAAK,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,OAAO,EACd,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CA4Ef"}
@@ -0,0 +1,72 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ /**
4
+ * Reads the full graph from the store and writes .ferret/context.json.
5
+ * Called automatically at the end of every ferret scan.
6
+ * Silent — no output. If write fails, it logs to stderr and continues.
7
+ */
8
+ export async function writeContext(store, projectRoot) {
9
+ const [nodes, contracts, dependencies] = await Promise.all([
10
+ store.getNodes(),
11
+ store.getContracts(),
12
+ store.getDependencies(),
13
+ ]);
14
+ // Build a map of node_id → node for quick lookup
15
+ const nodeById = new Map(nodes.map((n) => [n.id, n]));
16
+ // Map contracts to context shape
17
+ const contextContracts = contracts.map((c) => {
18
+ const parentNode = nodeById.get(c.node_id);
19
+ const nodeType = parentNode?.type ?? "spec"; // nodes don't store type in V2 yet
20
+ let shape = {};
21
+ try {
22
+ shape = JSON.parse(c.shape_schema);
23
+ }
24
+ catch {
25
+ shape = {};
26
+ }
27
+ return {
28
+ id: c.id,
29
+ type: c.type,
30
+ shape,
31
+ status: c.status,
32
+ specFile: nodeType !== "code"
33
+ ? (parentNode?.file_path?.replace(/\\/g, "/") ?? null)
34
+ : null,
35
+ codeFile: nodeType === "code"
36
+ ? (parentNode?.file_path?.replace(/\\/g, "/") ?? null)
37
+ : null,
38
+ };
39
+ });
40
+ // Build edges: source_node file_path → target_contract_id
41
+ const edges = dependencies.map((d) => {
42
+ const sourceNode = nodeById.get(d.source_node_id);
43
+ return {
44
+ from: (sourceNode?.file_path ?? d.source_node_id).replace(/\\/g, "/"),
45
+ to: d.target_contract_id,
46
+ };
47
+ });
48
+ // needsReview: contract IDs whose parent node is flagged needs-review
49
+ const needsReviewNodeIds = new Set(nodes.filter((n) => n.status === "needs-review").map((n) => n.id));
50
+ const needsReview = contracts
51
+ .filter((c) => needsReviewNodeIds.has(c.node_id) || c.status === "needs-review")
52
+ .map((c) => c.id);
53
+ const context = {
54
+ version: "2.0",
55
+ generated: new Date().toISOString(),
56
+ contracts: contextContracts,
57
+ edges,
58
+ needsReview,
59
+ };
60
+ const ferretDir = path.join(projectRoot, ".ferret");
61
+ const contextPath = path.join(ferretDir, "context.json");
62
+ try {
63
+ if (!fs.existsSync(ferretDir)) {
64
+ fs.mkdirSync(ferretDir, { recursive: true });
65
+ }
66
+ fs.writeFileSync(contextPath, JSON.stringify(context, null, 2), "utf-8");
67
+ }
68
+ catch (err) {
69
+ process.stderr.write(`⚠ Could not write context.json: ${err}\n`);
70
+ }
71
+ }
72
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/context/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ export interface ExtractionResult {
2
+ filePath: string;
3
+ fileType: 'spec' | 'code';
4
+ contracts: Array<{
5
+ id: string;
6
+ type: string;
7
+ shape: object;
8
+ shape_hash: string;
9
+ imports: string[];
10
+ }>;
11
+ extractedBy: 'gray-matter' | 'tree-sitter';
12
+ extractedAt: number;
13
+ warning?: 'no-frontmatter';
14
+ }
15
+ /**
16
+ * Extracts a FerretContract from a spec file's YAML frontmatter.
17
+ *
18
+ * Rules:
19
+ * - No frontmatter → return empty contracts + warning: 'no-frontmatter'
20
+ * - Missing required field (id, type, shape) → throw with field name in message
21
+ * - Unsupported schema keyword → warn via validateFerretSchema, continue
22
+ * - Identical files → identical shape_hash (deterministic)
23
+ */
24
+ export declare function extractFromSpecFile(filePath: string, fileContent: string): ExtractionResult;
25
+ //# sourceMappingURL=frontmatter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../../src/extractor/frontmatter.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B,SAAS,EAAE,KAAK,CAAC;QACf,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,EAAE,CAAC;KACnB,CAAC,CAAC;IACH,WAAW,EAAE,aAAa,GAAG,aAAa,CAAC;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,gBAAgB,CAAC;CAC5B;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,GAClB,gBAAgB,CAuClB"}
@@ -0,0 +1,49 @@
1
+ // Synchronous only. No async. No await. Anywhere in this file.
2
+ // Extractor layer — turns a spec file into an ExtractionResult using gray-matter.
3
+ import matter from 'gray-matter';
4
+ import { validateFerretSchema } from './validator.js';
5
+ import { hashSchema } from './hash.js';
6
+ /**
7
+ * Extracts a FerretContract from a spec file's YAML frontmatter.
8
+ *
9
+ * Rules:
10
+ * - No frontmatter → return empty contracts + warning: 'no-frontmatter'
11
+ * - Missing required field (id, type, shape) → throw with field name in message
12
+ * - Unsupported schema keyword → warn via validateFerretSchema, continue
13
+ * - Identical files → identical shape_hash (deterministic)
14
+ */
15
+ export function extractFromSpecFile(filePath, fileContent) {
16
+ const { data } = matter(fileContent);
17
+ const ferret = data?.ferret;
18
+ if (!ferret) {
19
+ return {
20
+ filePath,
21
+ fileType: 'spec',
22
+ contracts: [],
23
+ extractedBy: 'gray-matter',
24
+ extractedAt: Date.now(),
25
+ warning: 'no-frontmatter',
26
+ };
27
+ }
28
+ const missingFields = ['id', 'type', 'shape'].filter(f => !ferret[f]);
29
+ if (missingFields.length > 0) {
30
+ throw new Error(`Missing required frontmatter fields in ${filePath}: ${missingFields.join(', ')}`);
31
+ }
32
+ // validateFerretSchema never throws — only warns on unsupported keywords
33
+ const validation = validateFerretSchema(ferret.shape, filePath);
34
+ validation.warnings.forEach(w => process.stderr.write(w + '\n'));
35
+ return {
36
+ filePath,
37
+ fileType: 'spec',
38
+ contracts: [{
39
+ id: ferret.id,
40
+ type: ferret.type,
41
+ shape: ferret.shape,
42
+ shape_hash: hashSchema(ferret.shape),
43
+ imports: Array.isArray(ferret.imports) ? ferret.imports : [],
44
+ }],
45
+ extractedBy: 'gray-matter',
46
+ extractedAt: Date.now(),
47
+ };
48
+ }
49
+ //# sourceMappingURL=frontmatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontmatter.js","sourceRoot":"","sources":["../../src/extractor/frontmatter.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,kFAAkF;AAElF,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAiBvC;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CACjC,QAAgB,EAChB,WAAmB;IAEnB,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;IACrC,MAAM,MAAM,GAAG,IAAI,EAAE,MAAM,CAAC;IAE5B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO;YACL,QAAQ;YACR,QAAQ,EAAE,MAAM;YAChB,SAAS,EAAE,EAAE;YACb,WAAW,EAAE,aAAa;YAC1B,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE;YACvB,OAAO,EAAE,gBAAgB;SAC1B,CAAC;IACJ,CAAC;IAED,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACtE,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CACb,0CAA0C,QAAQ,KAAK,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAClF,CAAC;IACJ,CAAC;IAED,yEAAyE;IACzE,MAAM,UAAU,GAAG,oBAAoB,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAChE,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;IAEjE,OAAO;QACL,QAAQ;QACR,QAAQ,EAAE,MAAM;QAChB,SAAS,EAAE,CAAC;gBACV,EAAE,EAAE,MAAM,CAAC,EAAY;gBACvB,IAAI,EAAE,MAAM,CAAC,IAAc;gBAC3B,KAAK,EAAE,MAAM,CAAC,KAAe;gBAC7B,UAAU,EAAE,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC;gBACpC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAmB,CAAC,CAAC,CAAC,EAAE;aACzE,CAAC;QACF,WAAW,EAAE,aAAa;QAC1B,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE;KACxB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Produces a stable SHA-256 hex fingerprint of a schema object.
3
+ * Used for quick change detection — if the hash changes, the schema changed.
4
+ * JSON.stringify is order-sensitive on keys, so we sort keys for stability.
5
+ */
6
+ export declare function hashSchema(schema: unknown): string;
7
+ //# sourceMappingURL=hash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../../src/extractor/hash.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAkBlD"}
@@ -0,0 +1,25 @@
1
+ import { createHash } from 'node:crypto';
2
+ /**
3
+ * Produces a stable SHA-256 hex fingerprint of a schema object.
4
+ * Used for quick change detection — if the hash changes, the schema changed.
5
+ * JSON.stringify is order-sensitive on keys, so we sort keys for stability.
6
+ */
7
+ export function hashSchema(schema) {
8
+ const sortObject = (obj) => {
9
+ if (obj === null || typeof obj !== 'object') {
10
+ return obj;
11
+ }
12
+ if (Array.isArray(obj)) {
13
+ return obj.map(sortObject);
14
+ }
15
+ return Object.keys(obj)
16
+ .sort()
17
+ .reduce((acc, key) => {
18
+ acc[key] = sortObject(obj[key]);
19
+ return acc;
20
+ }, {});
21
+ };
22
+ const stable = JSON.stringify(sortObject(schema));
23
+ return createHash('sha256').update(stable).digest('hex');
24
+ }
25
+ //# sourceMappingURL=hash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash.js","sourceRoot":"","sources":["../../src/extractor/hash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,MAAe;IACxC,MAAM,UAAU,GAAG,CAAC,GAAQ,EAAO,EAAE;QACnC,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YAC5C,OAAO,GAAG,CAAC;QACb,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YACvB,OAAO,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC7B,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;aACpB,IAAI,EAAE;aACN,MAAM,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YACnB,GAAG,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;YAChC,OAAO,GAAG,CAAC;QACb,CAAC,EAAE,EAAS,CAAC,CAAC;IAClB,CAAC,CAAC;IAEF,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;IAClD,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC3D,CAAC"}
@@ -0,0 +1,14 @@
1
+ export type ContractType = "api" | "table" | "type" | "event" | "flow" | "config";
2
+ export interface ExtractedCodeContract {
3
+ id: string;
4
+ type: ContractType;
5
+ shape: Record<string, unknown>;
6
+ sourceSymbol: string;
7
+ filePath: string;
8
+ }
9
+ export interface TypeScriptExtractionResult {
10
+ contracts: ExtractedCodeContract[];
11
+ diagnostics: string[];
12
+ }
13
+ export declare function extractContractsFromTypeScript(filePath: string, content: string): TypeScriptExtractionResult;
14
+ //# sourceMappingURL=typescript.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typescript.d.ts","sourceRoot":"","sources":["../../src/extractor/typescript.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GACpB,KAAK,GACL,OAAO,GACP,MAAM,GACN,OAAO,GACP,MAAM,GACN,QAAQ,CAAC;AAEb,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,YAAY,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,0BAA0B;IACzC,SAAS,EAAE,qBAAqB,EAAE,CAAC;IACnC,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AA4PD,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GACd,0BAA0B,CAgC5B"}
@@ -0,0 +1,232 @@
1
+ class TypeParser {
2
+ input;
3
+ i = 0;
4
+ constructor(input) {
5
+ this.input = input;
6
+ }
7
+ parseObject() {
8
+ const diagnostics = [];
9
+ const object = this.parseObjectType(diagnostics);
10
+ return { shape: object, diagnostics };
11
+ }
12
+ parseObjectType(diagnostics) {
13
+ this.skipWs();
14
+ if (this.peek() !== "{") {
15
+ diagnostics.push("Expected object type starting with '{'.");
16
+ return { type: "object", properties: {}, required: [] };
17
+ }
18
+ this.i++; // '{'
19
+ const properties = {};
20
+ const required = [];
21
+ while (this.i < this.input.length) {
22
+ this.skipWs();
23
+ if (this.peek() === "}") {
24
+ this.i++; // '}'
25
+ break;
26
+ }
27
+ const name = this.readIdentifier();
28
+ if (!name) {
29
+ diagnostics.push("Unable to parse property name in object type.");
30
+ this.advanceToDelimiter();
31
+ continue;
32
+ }
33
+ this.skipWs();
34
+ let optional = false;
35
+ if (this.peek() === "?") {
36
+ optional = true;
37
+ this.i++;
38
+ }
39
+ this.skipWs();
40
+ if (this.peek() !== ":") {
41
+ diagnostics.push(`Expected ':' after property '${name}'.`);
42
+ this.advanceToDelimiter();
43
+ continue;
44
+ }
45
+ this.i++; // ':'
46
+ this.skipWs();
47
+ const schema = this.parseTypeExpr(diagnostics);
48
+ properties[name] = schema;
49
+ if (!optional) {
50
+ required.push(name);
51
+ }
52
+ this.skipWs();
53
+ if (this.peek() === ";" || this.peek() === ",") {
54
+ this.i++;
55
+ }
56
+ }
57
+ return {
58
+ type: "object",
59
+ properties,
60
+ required,
61
+ };
62
+ }
63
+ parseTypeExpr(diagnostics) {
64
+ this.skipWs();
65
+ if (this.peek() === "{") {
66
+ return this.parseObjectType(diagnostics);
67
+ }
68
+ const ident = this.readIdentifier();
69
+ if (!ident) {
70
+ diagnostics.push("Unable to parse type expression.");
71
+ return { type: "object" };
72
+ }
73
+ let base;
74
+ switch (ident) {
75
+ case "string":
76
+ base = { type: "string" };
77
+ break;
78
+ case "number":
79
+ base = { type: "number" };
80
+ break;
81
+ case "boolean":
82
+ base = { type: "boolean" };
83
+ break;
84
+ case "Date":
85
+ base = { type: "string", format: "date-time" };
86
+ break;
87
+ default:
88
+ diagnostics.push(`Unsupported referenced type '${ident}'. Falling back to object.`);
89
+ base = { type: "object" };
90
+ break;
91
+ }
92
+ while (true) {
93
+ this.skipWs();
94
+ if (this.peek() === "[" && this.peek(1) === "]") {
95
+ this.i += 2;
96
+ base = { type: "array", items: base };
97
+ continue;
98
+ }
99
+ break;
100
+ }
101
+ this.skipWs();
102
+ if (this.peek() === "|") {
103
+ diagnostics.push("Union types are not supported in Sprint 3 extraction.");
104
+ while (this.i < this.input.length) {
105
+ const ch = this.peek();
106
+ if (ch === ";" || ch === "," || ch === "}" || ch === "\n") {
107
+ break;
108
+ }
109
+ this.i++;
110
+ }
111
+ }
112
+ return base;
113
+ }
114
+ readIdentifier() {
115
+ this.skipWs();
116
+ const start = this.i;
117
+ while (this.i < this.input.length) {
118
+ const ch = this.input[this.i];
119
+ const ok = /[A-Za-z0-9_]/.test(ch);
120
+ if (!ok) {
121
+ break;
122
+ }
123
+ this.i++;
124
+ }
125
+ return this.input.slice(start, this.i);
126
+ }
127
+ skipWs() {
128
+ while (this.i < this.input.length) {
129
+ const ch = this.input[this.i];
130
+ if (!/\s/.test(ch)) {
131
+ break;
132
+ }
133
+ this.i++;
134
+ }
135
+ }
136
+ advanceToDelimiter() {
137
+ while (this.i < this.input.length) {
138
+ const ch = this.input[this.i];
139
+ if (ch === ";" || ch === "\n" || ch === "}") {
140
+ return;
141
+ }
142
+ this.i++;
143
+ }
144
+ }
145
+ peek(offset = 0) {
146
+ return this.input[this.i + offset] ?? "";
147
+ }
148
+ }
149
+ function parseAnnotations(content) {
150
+ const pattern = /^\s*\/\/\s*@ferret-contract:\s*([^\s]+)\s+(api|table|type|event|flow|config)\s*$/gm;
151
+ const out = [];
152
+ let match;
153
+ while ((match = pattern.exec(content)) !== null) {
154
+ out.push({
155
+ id: match[1],
156
+ type: match[2],
157
+ index: match.index + match[0].length,
158
+ });
159
+ }
160
+ return out;
161
+ }
162
+ function extractDeclaration(content, start) {
163
+ const tail = content.slice(start);
164
+ const decl = /(?:export\s+)?(interface|type)\s+([A-Za-z0-9_]+)\s*(?:=)?\s*/m.exec(tail);
165
+ if (!decl || decl.index === undefined) {
166
+ return {
167
+ symbol: "unknown",
168
+ objectBody: "",
169
+ error: "No interface/type declaration found after annotation.",
170
+ };
171
+ }
172
+ const symbol = decl[2];
173
+ const offset = start + decl.index + decl[0].length;
174
+ const bodyStart = content.indexOf("{", offset);
175
+ if (bodyStart === -1) {
176
+ return {
177
+ symbol,
178
+ objectBody: "",
179
+ error: `Declaration '${symbol}' is not an object-like type.`,
180
+ };
181
+ }
182
+ let depth = 0;
183
+ let end = -1;
184
+ for (let i = bodyStart; i < content.length; i++) {
185
+ const ch = content[i];
186
+ if (ch === "{")
187
+ depth++;
188
+ if (ch === "}") {
189
+ depth--;
190
+ if (depth === 0) {
191
+ end = i;
192
+ break;
193
+ }
194
+ }
195
+ }
196
+ if (end === -1) {
197
+ return {
198
+ symbol,
199
+ objectBody: "",
200
+ error: `Unclosed object type for declaration '${symbol}'.`,
201
+ };
202
+ }
203
+ const objectBody = content.slice(bodyStart, end + 1);
204
+ return { symbol, objectBody };
205
+ }
206
+ export function extractContractsFromTypeScript(filePath, content) {
207
+ const annotations = parseAnnotations(content);
208
+ const contracts = [];
209
+ const diagnostics = [];
210
+ for (const annotation of annotations) {
211
+ const declaration = extractDeclaration(content, annotation.index);
212
+ if (declaration.error) {
213
+ diagnostics.push(`${filePath}: ${declaration.error}`);
214
+ continue;
215
+ }
216
+ const parser = new TypeParser(declaration.objectBody);
217
+ const { shape, diagnostics: parserDiagnostics } = parser.parseObject();
218
+ for (const d of parserDiagnostics) {
219
+ diagnostics.push(`${filePath} (${declaration.symbol}): ${d}`);
220
+ }
221
+ contracts.push({
222
+ id: annotation.id,
223
+ type: annotation.type,
224
+ shape,
225
+ sourceSymbol: declaration.symbol,
226
+ filePath,
227
+ });
228
+ }
229
+ contracts.sort((a, b) => a.id.localeCompare(b.id));
230
+ return { contracts, diagnostics };
231
+ }
232
+ //# sourceMappingURL=typescript.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typescript.js","sourceRoot":"","sources":["../../src/extractor/typescript.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ export type ChangeClassification = 'breaking' | 'non-breaking' | 'no-change';
2
+ export interface SchemaValidationResult {
3
+ valid: boolean;
4
+ warnings: string[];
5
+ }
6
+ export interface SchemaComparisonResult {
7
+ classification: ChangeClassification;
8
+ reason: string;
9
+ }
10
+ /**
11
+ * Validates a schema object against the Ferret JSON Schema subset.
12
+ * Always returns valid: true — unsupported keywords produce warnings, not errors.
13
+ */
14
+ export declare function validateFerretSchema(shape: unknown, filePath: string): SchemaValidationResult;
15
+ /**
16
+ * Compares two schema objects and classifies the change.
17
+ *
18
+ * Breaking: required field removed, field type changed, enum value removed,
19
+ * response/array type changed, required field added
20
+ * Non-breaking: optional field added, enum value added
21
+ * No-change: property order changed, whitespace, required array reordered
22
+ */
23
+ export declare function compareSchemas(previous: unknown, current: unknown): SchemaComparisonResult;
24
+ //# sourceMappingURL=validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/extractor/validator.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,oBAAoB,GAAG,UAAU,GAAG,cAAc,GAAG,WAAW,CAAC;AAE7E,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,sBAAsB;IACrC,cAAc,EAAE,oBAAoB,CAAC;IACrC,MAAM,EAAE,MAAM,CAAC;CAChB;AAaD;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,MAAM,GACf,sBAAsB,CAqBxB;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,OAAO,EACjB,OAAO,EAAE,OAAO,GACf,sBAAsB,CAqGxB"}