@specferret/core 0.1.0 → 0.1.1

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/README.md ADDED
@@ -0,0 +1,25 @@
1
+ # @specferret/core
2
+
3
+ SpecFerret core engine for contract extraction, validation, storage, and reconciliation.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm i @specferret/core
9
+ ```
10
+
11
+ ## Runtime
12
+
13
+ SpecFerret is designed to run with Bun and expects Bun-compatible runtime behavior.
14
+
15
+ ## What this package contains
16
+
17
+ - Store implementations (`sqlite`, `postgres`)
18
+ - Contract extractors and schema validator
19
+ - Reconciler for drift impact analysis
20
+ - Context writer utilities
21
+
22
+ ## Repository
23
+
24
+ - Source: https://github.com/BenGardiner123/spec-ferret
25
+ - Homepage: https://specferret.dev
@@ -33,48 +33,48 @@ export class SqliteStore {
33
33
  this.db = new Database(this.dbPath);
34
34
  this.db.exec("PRAGMA journal_mode = WAL;");
35
35
  this.db.exec("PRAGMA foreign_keys = ON;");
36
- this.db.exec(`
37
- CREATE TABLE IF NOT EXISTS ferret_nodes (
38
- id TEXT PRIMARY KEY,
39
- file_path TEXT NOT NULL,
40
- hash TEXT NOT NULL,
41
- status TEXT NOT NULL,
42
- updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
43
- );
44
-
45
- CREATE TABLE IF NOT EXISTS ferret_contracts (
46
- id TEXT PRIMARY KEY,
47
- node_id TEXT NOT NULL,
48
- shape_hash TEXT NOT NULL,
49
- shape_schema TEXT NOT NULL DEFAULT '{}',
50
- type TEXT NOT NULL,
51
- status TEXT NOT NULL,
52
- FOREIGN KEY (node_id) REFERENCES ferret_nodes(id)
53
- );
54
-
55
- CREATE TABLE IF NOT EXISTS ferret_dependencies (
56
- id TEXT PRIMARY KEY,
57
- source_node_id TEXT NOT NULL,
58
- target_contract_id TEXT NOT NULL,
59
- FOREIGN KEY (source_node_id) REFERENCES ferret_nodes(id)
60
- );
61
-
62
- CREATE TABLE IF NOT EXISTS ferret_reconciliation_log (
63
- id TEXT PRIMARY KEY,
64
- node_id TEXT NOT NULL,
65
- triggered_by TEXT NOT NULL,
66
- resolved_by TEXT,
67
- resolution_notes TEXT,
68
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP
69
- );
70
-
71
- CREATE TABLE IF NOT EXISTS ferret_placement_decisions (
72
- id TEXT PRIMARY KEY,
73
- node_id TEXT NOT NULL,
74
- placed_by TEXT NOT NULL,
75
- reasoning TEXT,
76
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP
77
- );
36
+ this.db.exec(`
37
+ CREATE TABLE IF NOT EXISTS ferret_nodes (
38
+ id TEXT PRIMARY KEY,
39
+ file_path TEXT NOT NULL,
40
+ hash TEXT NOT NULL,
41
+ status TEXT NOT NULL,
42
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
43
+ );
44
+
45
+ CREATE TABLE IF NOT EXISTS ferret_contracts (
46
+ id TEXT PRIMARY KEY,
47
+ node_id TEXT NOT NULL,
48
+ shape_hash TEXT NOT NULL,
49
+ shape_schema TEXT NOT NULL DEFAULT '{}',
50
+ type TEXT NOT NULL,
51
+ status TEXT NOT NULL,
52
+ FOREIGN KEY (node_id) REFERENCES ferret_nodes(id)
53
+ );
54
+
55
+ CREATE TABLE IF NOT EXISTS ferret_dependencies (
56
+ id TEXT PRIMARY KEY,
57
+ source_node_id TEXT NOT NULL,
58
+ target_contract_id TEXT NOT NULL,
59
+ FOREIGN KEY (source_node_id) REFERENCES ferret_nodes(id)
60
+ );
61
+
62
+ CREATE TABLE IF NOT EXISTS ferret_reconciliation_log (
63
+ id TEXT PRIMARY KEY,
64
+ node_id TEXT NOT NULL,
65
+ triggered_by TEXT NOT NULL,
66
+ resolved_by TEXT,
67
+ resolution_notes TEXT,
68
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
69
+ );
70
+
71
+ CREATE TABLE IF NOT EXISTS ferret_placement_decisions (
72
+ id TEXT PRIMARY KEY,
73
+ node_id TEXT NOT NULL,
74
+ placed_by TEXT NOT NULL,
75
+ reasoning TEXT,
76
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
77
+ );
78
78
  `);
79
79
  try {
80
80
  this.db.exec(`ALTER TABLE ferret_contracts ADD COLUMN shape_schema TEXT NOT NULL DEFAULT '{}';`);
@@ -92,44 +92,44 @@ export class SqliteStore {
92
92
  return row ?? null;
93
93
  }
94
94
  async upsertNode(node) {
95
- this.db.prepare(`
96
- INSERT INTO ferret_nodes (id, file_path, hash, status)
97
- VALUES (?, ?, ?, ?)
98
- ON CONFLICT(id) DO UPDATE SET
99
- file_path = excluded.file_path,
100
- hash = excluded.hash,
101
- status = excluded.status,
102
- updated_at = CURRENT_TIMESTAMP
95
+ this.db.prepare(`
96
+ INSERT INTO ferret_nodes (id, file_path, hash, status)
97
+ VALUES (?, ?, ?, ?)
98
+ ON CONFLICT(id) DO UPDATE SET
99
+ file_path = excluded.file_path,
100
+ hash = excluded.hash,
101
+ status = excluded.status,
102
+ updated_at = CURRENT_TIMESTAMP
103
103
  `).run(node.id, node.file_path, node.hash, node.status);
104
104
  }
105
105
  async getAllContractIds() {
106
106
  return this.db.prepare("SELECT id FROM ferret_contracts").all().map((r) => r.id);
107
107
  }
108
108
  async upsertContract(contract) {
109
- this.db.prepare(`
110
- INSERT INTO ferret_contracts (id, node_id, shape_hash, shape_schema, type, status)
111
- VALUES (?, ?, ?, ?, ?, ?)
112
- ON CONFLICT(id) DO UPDATE SET
113
- node_id = excluded.node_id,
114
- shape_hash = excluded.shape_hash,
115
- shape_schema = excluded.shape_schema,
116
- type = excluded.type,
117
- status = excluded.status
109
+ this.db.prepare(`
110
+ INSERT INTO ferret_contracts (id, node_id, shape_hash, shape_schema, type, status)
111
+ VALUES (?, ?, ?, ?, ?, ?)
112
+ ON CONFLICT(id) DO UPDATE SET
113
+ node_id = excluded.node_id,
114
+ shape_hash = excluded.shape_hash,
115
+ shape_schema = excluded.shape_schema,
116
+ type = excluded.type,
117
+ status = excluded.status
118
118
  `).run(contract.id, contract.node_id, contract.shape_hash, contract.shape_schema, contract.type, contract.status);
119
119
  }
120
120
  async upsertDependency(dependency) {
121
- this.db.prepare(`
122
- INSERT INTO ferret_dependencies (id, source_node_id, target_contract_id)
123
- VALUES (?, ?, ?)
124
- ON CONFLICT(id) DO NOTHING
121
+ this.db.prepare(`
122
+ INSERT INTO ferret_dependencies (id, source_node_id, target_contract_id)
123
+ VALUES (?, ?, ?)
124
+ ON CONFLICT(id) DO NOTHING
125
125
  `).run(dependency.id, dependency.source_node_id, dependency.target_contract_id);
126
126
  }
127
127
  async replaceDependenciesForSourceNode(sourceNodeId, targetContractIds) {
128
128
  const uniqueTargets = [...new Set(targetContractIds)].sort();
129
129
  const deleteStatement = this.db.prepare("DELETE FROM ferret_dependencies WHERE source_node_id = ?");
130
- const insertStatement = this.db.prepare(`
131
- INSERT INTO ferret_dependencies (id, source_node_id, target_contract_id)
132
- VALUES (?, ?, ?)
130
+ const insertStatement = this.db.prepare(`
131
+ INSERT INTO ferret_dependencies (id, source_node_id, target_contract_id)
132
+ VALUES (?, ?, ?)
133
133
  `);
134
134
  const transaction = this.db.transaction((sourceId) => {
135
135
  deleteStatement.run(sourceId);
@@ -159,18 +159,18 @@ export class SqliteStore {
159
159
  this.db.prepare("UPDATE ferret_nodes SET status = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?").run(status, nodeId);
160
160
  }
161
161
  async insertReconciliationLog(log) {
162
- this.db.prepare(`
163
- INSERT INTO ferret_reconciliation_log (id, node_id, triggered_by, resolved_by, resolution_notes)
164
- VALUES (?, ?, ?, ?, ?)
162
+ this.db.prepare(`
163
+ INSERT INTO ferret_reconciliation_log (id, node_id, triggered_by, resolved_by, resolution_notes)
164
+ VALUES (?, ?, ?, ?, ?)
165
165
  `).run(log.id, log.node_id, log.triggered_by, log.resolved_by ?? null, log.resolution_notes ?? null);
166
166
  }
167
167
  async getReconciliationLogs() {
168
168
  return this.db.prepare("SELECT id, node_id, triggered_by, resolved_by, resolution_notes FROM ferret_reconciliation_log").all();
169
169
  }
170
170
  async insertPlacementDecision(decision) {
171
- this.db.prepare(`
172
- INSERT INTO ferret_placement_decisions (id, node_id, placed_by, reasoning)
173
- VALUES (?, ?, ?, ?)
171
+ this.db.prepare(`
172
+ INSERT INTO ferret_placement_decisions (id, node_id, placed_by, reasoning)
173
+ VALUES (?, ?, ?, ?)
174
174
  `).run(decision.id, decision.node_id, decision.placed_by, decision.reasoning ?? null);
175
175
  }
176
176
  async getPlacementDecisions() {
package/package.json CHANGED
@@ -1,44 +1,44 @@
1
- {
2
- "name": "@specferret/core",
3
- "version": "0.1.0",
4
- "description": "SpecFerret core engine — spec drift detection.",
5
- "type": "module",
6
- "main": "./dist/index.js",
7
- "types": "./dist/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "bun": "./src/index.ts",
11
- "import": "./dist/index.js",
12
- "types": "./dist/index.d.ts"
13
- }
14
- },
15
- "files": [
16
- "dist/",
17
- "README.md",
18
- "LICENSE"
19
- ],
20
- "scripts": {
21
- "build": "bun ../../scripts/clean-dist.ts dist && tsc --project tsconfig.build.json",
22
- "test": "bun test"
23
- },
24
- "dependencies": {
25
- "ajv": "^8.12.0",
26
- "ajv-formats": "^2.1.1",
27
- "glob": "^13.0.6",
28
- "gray-matter": "^4.0.3",
29
- "zod": "^3.22.4"
30
- },
31
- "devDependencies": {
32
- "@types/bun": "^1.3.11",
33
- "typescript": "^6.0.2"
34
- },
35
- "engines": {
36
- "bun": ">=1.0.0"
37
- },
38
- "license": "MIT",
39
- "repository": {
40
- "type": "git",
41
- "url": "git+https://github.com/BenGardiner123/spec-ferret.git"
42
- },
43
- "homepage": "https://specferret.dev"
44
- }
1
+ {
2
+ "name": "@specferret/core",
3
+ "version": "0.1.1",
4
+ "description": "SpecFerret core engine — spec drift detection.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "bun": "./src/index.ts",
11
+ "import": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist/",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
20
+ "scripts": {
21
+ "build": "bun ../../scripts/clean-dist.ts dist && tsc --project tsconfig.build.json",
22
+ "test": "bun test"
23
+ },
24
+ "dependencies": {
25
+ "ajv": "^8.12.0",
26
+ "ajv-formats": "^2.1.1",
27
+ "glob": "^13.0.6",
28
+ "gray-matter": "^4.0.3",
29
+ "zod": "^3.22.4"
30
+ },
31
+ "devDependencies": {
32
+ "@types/bun": "^1.3.11",
33
+ "typescript": "^6.0.2"
34
+ },
35
+ "engines": {
36
+ "bun": ">=1.0.0"
37
+ },
38
+ "license": "MIT",
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "git+https://github.com/BenGardiner123/spec-ferret.git"
42
+ },
43
+ "homepage": "https://specferret.dev"
44
+ }