@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.
- package/dist/config.d.ts +20 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +32 -0
- package/dist/config.js.map +1 -0
- package/dist/context/index.d.ts +27 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +72 -0
- package/dist/context/index.js.map +1 -0
- package/dist/extractor/frontmatter.d.ts +25 -0
- package/dist/extractor/frontmatter.d.ts.map +1 -0
- package/dist/extractor/frontmatter.js +49 -0
- package/dist/extractor/frontmatter.js.map +1 -0
- package/dist/extractor/hash.d.ts +7 -0
- package/dist/extractor/hash.d.ts.map +1 -0
- package/dist/extractor/hash.js +25 -0
- package/dist/extractor/hash.js.map +1 -0
- package/dist/extractor/typescript.d.ts +14 -0
- package/dist/extractor/typescript.d.ts.map +1 -0
- package/dist/extractor/typescript.js +232 -0
- package/dist/extractor/typescript.js.map +1 -0
- package/dist/extractor/validator.d.ts +24 -0
- package/dist/extractor/validator.d.ts.map +1 -0
- package/dist/extractor/validator.js +141 -0
- package/dist/extractor/validator.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/reconciler/import-suggestions.d.ts +10 -0
- package/dist/reconciler/import-suggestions.d.ts.map +1 -0
- package/dist/reconciler/import-suggestions.js +110 -0
- package/dist/reconciler/import-suggestions.js.map +1 -0
- package/dist/reconciler/index.d.ts +53 -0
- package/dist/reconciler/index.d.ts.map +1 -0
- package/dist/reconciler/index.js +214 -0
- package/dist/reconciler/index.js.map +1 -0
- package/dist/store/factory.d.ts +7 -0
- package/dist/store/factory.d.ts.map +1 -0
- package/dist/store/factory.js +52 -0
- package/dist/store/factory.js.map +1 -0
- package/dist/store/postgres.d.ts +25 -0
- package/dist/store/postgres.d.ts.map +1 -0
- package/dist/store/postgres.js +68 -0
- package/dist/store/postgres.js.map +1 -0
- package/dist/store/sqlite.d.ts +25 -0
- package/dist/store/sqlite.d.ts.map +1 -0
- package/dist/store/sqlite.js +180 -0
- package/dist/store/sqlite.js.map +1 -0
- package/dist/store/types.d.ts +79 -0
- package/dist/store/types.d.ts.map +1 -0
- package/dist/store/types.js +2 -0
- package/dist/store/types.js.map +1 -0
- package/dist/utils/paths.d.ts +6 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +21 -0
- package/dist/utils/paths.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { SqliteStore } from "./sqlite.js";
|
|
4
|
+
import { PostgresStore } from "./postgres.js";
|
|
5
|
+
import { loadConfig } from "../config.js";
|
|
6
|
+
import { findProjectRoot } from "../utils/paths.js";
|
|
7
|
+
/**
|
|
8
|
+
* Evaluates the current environment and configuration (handling overrides from the CI pipeline, S005)
|
|
9
|
+
* and returns the correct initialized database engine.
|
|
10
|
+
*/
|
|
11
|
+
export async function getStore() {
|
|
12
|
+
// S005: If FERRET_DATABASE_URL is set in the environment, use PostgreSQL without prompts.
|
|
13
|
+
const envUrl = process.env.FERRET_DATABASE_URL;
|
|
14
|
+
if (envUrl) {
|
|
15
|
+
const store = new PostgresStore(envUrl);
|
|
16
|
+
// In production we should ensure init() was run by `ferret init`, but we do it gracefully here
|
|
17
|
+
// or rely on the user to run init. If this was just the runner, it shouldn't recreate tables.
|
|
18
|
+
// For now we assume the caller handles init if necessary, or we just return the instantiated store.
|
|
19
|
+
return store;
|
|
20
|
+
}
|
|
21
|
+
// At this point FERRET_DATABASE_URL is NOT set in the environment (we returned early above if it was).
|
|
22
|
+
// If the config explicitly opts into postgres, that means the user ran `ferret upgrade --postgres`
|
|
23
|
+
// which writes FERRET_DATABASE_URL to .env, but that .env has not been loaded into the environment.
|
|
24
|
+
// Fail fast with a clear error rather than silently falling through to SQLite.
|
|
25
|
+
try {
|
|
26
|
+
const config = await loadConfig();
|
|
27
|
+
if (config.store === "postgres") {
|
|
28
|
+
console.error("FATAL: ferret.config.json sets store to postgres but FERRET_DATABASE_URL is not set. Source your .env file or set the variable in your environment.");
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// Suppress config missing errors and fallback to logic
|
|
34
|
+
}
|
|
35
|
+
// S005: If FERRET_DATABASE_URL is not set, and config is missing / local, fall back to SQLite
|
|
36
|
+
// Check if .ferret/graph.db exists relative to the discovered project root
|
|
37
|
+
const dbPath = path.join(findProjectRoot(), ".ferret", "graph.db");
|
|
38
|
+
try {
|
|
39
|
+
await fs.access(dbPath);
|
|
40
|
+
return new SqliteStore(); // Implicit fallback to SQLite
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
// CI Mode checking:
|
|
44
|
+
if (process.env.FERRET_CI === "true") {
|
|
45
|
+
console.error("FATAL: FERRET_CI is true, but no database configuration found.");
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
// SQLite by default
|
|
49
|
+
return new SqliteStore();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/store/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ;IAC5B,0FAA0F;IAC1F,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;IAC/C,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,KAAK,GAAG,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC;QACxC,+FAA+F;QAC/F,8FAA8F;QAC9F,oGAAoG;QACpG,OAAO,KAAK,CAAC;IACf,CAAC;IAED,uGAAuG;IACvG,mGAAmG;IACnG,oGAAoG;IACpG,+EAA+E;IAC/E,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;QAClC,IAAI,MAAM,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;YAChC,OAAO,CAAC,KAAK,CAAC,qJAAqJ,CAAC,CAAC;YACrK,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,uDAAuD;IACzD,CAAC;IAED,8FAA8F;IAC9F,2EAA2E;IAC3E,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;IACnE,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACxB,OAAO,IAAI,WAAW,EAAE,CAAC,CAAC,8BAA8B;IAC1D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,qBAAqB;QACrB,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,KAAK,MAAM,EAAE,CAAC;YACrC,OAAO,CAAC,KAAK,CAAC,gEAAgE,CAAC,CAAC;YAChF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,oBAAoB;QACpB,OAAO,IAAI,WAAW,EAAE,CAAC;IAC3B,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { DBStore, FerretNode, FerretContract, FerretDependency, FerretReconciliationLog, FerretPlacementDecision, NodeStatus } from "./types.js";
|
|
2
|
+
export declare class PostgresStore implements DBStore {
|
|
3
|
+
private connectionString;
|
|
4
|
+
constructor(connectionString: string);
|
|
5
|
+
private notImplemented;
|
|
6
|
+
init(): Promise<void>;
|
|
7
|
+
close(): Promise<void>;
|
|
8
|
+
getNodeByFilePath(_filePath: string): Promise<FerretNode | null>;
|
|
9
|
+
upsertNode(_node: FerretNode): Promise<void>;
|
|
10
|
+
getAllContractIds(): Promise<string[]>;
|
|
11
|
+
upsertContract(_contract: FerretContract): Promise<void>;
|
|
12
|
+
upsertDependency(_dependency: FerretDependency): Promise<void>;
|
|
13
|
+
replaceDependenciesForSourceNode(_sourceNodeId: string, _targetNodeIds: string[]): Promise<void>;
|
|
14
|
+
getNodes(): Promise<FerretNode[]>;
|
|
15
|
+
getNodesByStatus(_status: NodeStatus): Promise<FerretNode[]>;
|
|
16
|
+
getContracts(): Promise<FerretContract[]>;
|
|
17
|
+
getContract(_id: string): Promise<FerretContract | null>;
|
|
18
|
+
getDependencies(): Promise<FerretDependency[]>;
|
|
19
|
+
updateNodeStatus(_nodeId: string, _status: NodeStatus): Promise<void>;
|
|
20
|
+
insertReconciliationLog(_log: FerretReconciliationLog): Promise<void>;
|
|
21
|
+
getReconciliationLogs(): Promise<FerretReconciliationLog[]>;
|
|
22
|
+
insertPlacementDecision(_decision: FerretPlacementDecision): Promise<void>;
|
|
23
|
+
getPlacementDecisions(): Promise<FerretPlacementDecision[]>;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=postgres.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../src/store/postgres.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,OAAO,EACP,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,uBAAuB,EACvB,uBAAuB,EACvB,UAAU,EACX,MAAM,YAAY,CAAC;AAEpB,qBAAa,aAAc,YAAW,OAAO;IAC/B,OAAO,CAAC,gBAAgB;gBAAhB,gBAAgB,EAAE,MAAM;IAE5C,OAAO,CAAC,cAAc;IAOhB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAGrB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAGtB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAGhE,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAG5C,iBAAiB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAGtC,cAAc,CAAC,SAAS,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAGxD,gBAAgB,CAAC,WAAW,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAG9D,gCAAgC,CACpC,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,EAAE,GACvB,OAAO,CAAC,IAAI,CAAC;IAGV,QAAQ,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;IAGjC,gBAAgB,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAG5D,YAAY,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAGzC,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAGxD,eAAe,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAG9C,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAGrE,uBAAuB,CAAC,IAAI,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;IAGrE,qBAAqB,IAAI,OAAO,CAAC,uBAAuB,EAAE,CAAC;IAG3D,uBAAuB,CAC3B,SAAS,EAAE,uBAAuB,GACjC,OAAO,CAAC,IAAI,CAAC;IAGV,qBAAqB,IAAI,OAAO,CAAC,uBAAuB,EAAE,CAAC;CAGlE"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Sprint 2 roadmap — Postgres support not yet implemented.
|
|
2
|
+
// This stub exists so factory.ts compiles.
|
|
3
|
+
// Do not use this in production until Sprint 2 is complete.
|
|
4
|
+
export class PostgresStore {
|
|
5
|
+
connectionString;
|
|
6
|
+
constructor(connectionString) {
|
|
7
|
+
this.connectionString = connectionString;
|
|
8
|
+
}
|
|
9
|
+
notImplemented() {
|
|
10
|
+
throw new Error("PostgresStore: Postgres support is Sprint 2 roadmap. " +
|
|
11
|
+
'Set FERRET_DATABASE_URL to use Postgres, or remove the store: "postgres" config to use SQLite.');
|
|
12
|
+
}
|
|
13
|
+
async init() {
|
|
14
|
+
this.notImplemented();
|
|
15
|
+
}
|
|
16
|
+
async close() {
|
|
17
|
+
this.notImplemented();
|
|
18
|
+
}
|
|
19
|
+
async getNodeByFilePath(_filePath) {
|
|
20
|
+
this.notImplemented();
|
|
21
|
+
}
|
|
22
|
+
async upsertNode(_node) {
|
|
23
|
+
this.notImplemented();
|
|
24
|
+
}
|
|
25
|
+
async getAllContractIds() {
|
|
26
|
+
this.notImplemented();
|
|
27
|
+
}
|
|
28
|
+
async upsertContract(_contract) {
|
|
29
|
+
this.notImplemented();
|
|
30
|
+
}
|
|
31
|
+
async upsertDependency(_dependency) {
|
|
32
|
+
this.notImplemented();
|
|
33
|
+
}
|
|
34
|
+
async replaceDependenciesForSourceNode(_sourceNodeId, _targetNodeIds) {
|
|
35
|
+
this.notImplemented();
|
|
36
|
+
}
|
|
37
|
+
async getNodes() {
|
|
38
|
+
this.notImplemented();
|
|
39
|
+
}
|
|
40
|
+
async getNodesByStatus(_status) {
|
|
41
|
+
this.notImplemented();
|
|
42
|
+
}
|
|
43
|
+
async getContracts() {
|
|
44
|
+
this.notImplemented();
|
|
45
|
+
}
|
|
46
|
+
async getContract(_id) {
|
|
47
|
+
this.notImplemented();
|
|
48
|
+
}
|
|
49
|
+
async getDependencies() {
|
|
50
|
+
this.notImplemented();
|
|
51
|
+
}
|
|
52
|
+
async updateNodeStatus(_nodeId, _status) {
|
|
53
|
+
this.notImplemented();
|
|
54
|
+
}
|
|
55
|
+
async insertReconciliationLog(_log) {
|
|
56
|
+
this.notImplemented();
|
|
57
|
+
}
|
|
58
|
+
async getReconciliationLogs() {
|
|
59
|
+
this.notImplemented();
|
|
60
|
+
}
|
|
61
|
+
async insertPlacementDecision(_decision) {
|
|
62
|
+
this.notImplemented();
|
|
63
|
+
}
|
|
64
|
+
async getPlacementDecisions() {
|
|
65
|
+
this.notImplemented();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=postgres.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres.js","sourceRoot":"","sources":["../../src/store/postgres.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,2CAA2C;AAC3C,4DAA4D;AAY5D,MAAM,OAAO,aAAa;IACJ;IAApB,YAAoB,gBAAwB;QAAxB,qBAAgB,GAAhB,gBAAgB,CAAQ;IAAG,CAAC;IAExC,cAAc;QACpB,MAAM,IAAI,KAAK,CACb,uDAAuD;YACrD,gGAAgG,CACnG,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,IAAI;QACR,IAAI,CAAC,cAAc,EAAE,CAAC;IACxB,CAAC;IACD,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,cAAc,EAAE,CAAC;IACxB,CAAC;IACD,KAAK,CAAC,iBAAiB,CAAC,SAAiB;QACvC,IAAI,CAAC,cAAc,EAAE,CAAC;IACxB,CAAC;IACD,KAAK,CAAC,UAAU,CAAC,KAAiB;QAChC,IAAI,CAAC,cAAc,EAAE,CAAC;IACxB,CAAC;IACD,KAAK,CAAC,iBAAiB;QACrB,IAAI,CAAC,cAAc,EAAE,CAAC;IACxB,CAAC;IACD,KAAK,CAAC,cAAc,CAAC,SAAyB;QAC5C,IAAI,CAAC,cAAc,EAAE,CAAC;IACxB,CAAC;IACD,KAAK,CAAC,gBAAgB,CAAC,WAA6B;QAClD,IAAI,CAAC,cAAc,EAAE,CAAC;IACxB,CAAC;IACD,KAAK,CAAC,gCAAgC,CACpC,aAAqB,EACrB,cAAwB;QAExB,IAAI,CAAC,cAAc,EAAE,CAAC;IACxB,CAAC;IACD,KAAK,CAAC,QAAQ;QACZ,IAAI,CAAC,cAAc,EAAE,CAAC;IACxB,CAAC;IACD,KAAK,CAAC,gBAAgB,CAAC,OAAmB;QACxC,IAAI,CAAC,cAAc,EAAE,CAAC;IACxB,CAAC;IACD,KAAK,CAAC,YAAY;QAChB,IAAI,CAAC,cAAc,EAAE,CAAC;IACxB,CAAC;IACD,KAAK,CAAC,WAAW,CAAC,GAAW;QAC3B,IAAI,CAAC,cAAc,EAAE,CAAC;IACxB,CAAC;IACD,KAAK,CAAC,eAAe;QACnB,IAAI,CAAC,cAAc,EAAE,CAAC;IACxB,CAAC;IACD,KAAK,CAAC,gBAAgB,CAAC,OAAe,EAAE,OAAmB;QACzD,IAAI,CAAC,cAAc,EAAE,CAAC;IACxB,CAAC;IACD,KAAK,CAAC,uBAAuB,CAAC,IAA6B;QACzD,IAAI,CAAC,cAAc,EAAE,CAAC;IACxB,CAAC;IACD,KAAK,CAAC,qBAAqB;QACzB,IAAI,CAAC,cAAc,EAAE,CAAC;IACxB,CAAC;IACD,KAAK,CAAC,uBAAuB,CAC3B,SAAkC;QAElC,IAAI,CAAC,cAAc,EAAE,CAAC;IACxB,CAAC;IACD,KAAK,CAAC,qBAAqB;QACzB,IAAI,CAAC,cAAc,EAAE,CAAC;IACxB,CAAC;CACF"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { DBStore, FerretNode, FerretContract, FerretDependency, FerretReconciliationLog, FerretPlacementDecision, NodeStatus } from "./types.js";
|
|
2
|
+
export declare class SqliteStore implements DBStore {
|
|
3
|
+
private db;
|
|
4
|
+
private dbPath;
|
|
5
|
+
constructor(customPath?: string);
|
|
6
|
+
init(): Promise<void>;
|
|
7
|
+
close(): Promise<void>;
|
|
8
|
+
getNodeByFilePath(filePath: string): Promise<FerretNode | null>;
|
|
9
|
+
upsertNode(node: FerretNode): Promise<void>;
|
|
10
|
+
getAllContractIds(): Promise<string[]>;
|
|
11
|
+
upsertContract(contract: FerretContract): Promise<void>;
|
|
12
|
+
upsertDependency(dependency: FerretDependency): Promise<void>;
|
|
13
|
+
replaceDependenciesForSourceNode(sourceNodeId: string, targetContractIds: string[]): Promise<void>;
|
|
14
|
+
getNodes(): Promise<FerretNode[]>;
|
|
15
|
+
getNodesByStatus(status: NodeStatus): Promise<FerretNode[]>;
|
|
16
|
+
getContracts(): Promise<FerretContract[]>;
|
|
17
|
+
getContract(id: string): Promise<FerretContract | null>;
|
|
18
|
+
getDependencies(): Promise<FerretDependency[]>;
|
|
19
|
+
updateNodeStatus(nodeId: string, status: NodeStatus): Promise<void>;
|
|
20
|
+
insertReconciliationLog(log: FerretReconciliationLog): Promise<void>;
|
|
21
|
+
getReconciliationLogs(): Promise<FerretReconciliationLog[]>;
|
|
22
|
+
insertPlacementDecision(decision: FerretPlacementDecision): Promise<void>;
|
|
23
|
+
getPlacementDecisions(): Promise<FerretPlacementDecision[]>;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=sqlite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/store/sqlite.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,OAAO,EACP,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,uBAAuB,EACvB,uBAAuB,EACvB,UAAU,EACX,MAAM,YAAY,CAAC;AAEpB,qBAAa,WAAY,YAAW,OAAO;IACzC,OAAO,CAAC,EAAE,CAAyB;IACnC,OAAO,CAAC,MAAM,CAAS;gBAEX,UAAU,CAAC,EAAE,MAAM;IAWzB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAyErB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAOtB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAO/D,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAc3C,iBAAiB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAMtC,cAAc,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAsBvD,gBAAgB,CAAC,UAAU,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAc7D,gCAAgC,CACpC,YAAY,EAAE,MAAM,EACpB,iBAAiB,EAAE,MAAM,EAAE,GAC1B,OAAO,CAAC,IAAI,CAAC;IAsBV,QAAQ,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;IAMjC,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAM3D,YAAY,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAMzC,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAOvD,eAAe,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAM9C,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAMnE,uBAAuB,CAAC,GAAG,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;IAepE,qBAAqB,IAAI,OAAO,CAAC,uBAAuB,EAAE,CAAC;IAM3D,uBAAuB,CAC3B,QAAQ,EAAE,uBAAuB,GAChC,OAAO,CAAC,IAAI,CAAC;IAcV,qBAAqB,IAAI,OAAO,CAAC,uBAAuB,EAAE,CAAC;CAKlE"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import * as fs from "node:fs";
|
|
4
|
+
import { randomUUID } from "node:crypto";
|
|
5
|
+
import { findProjectRoot } from "../utils/paths.js";
|
|
6
|
+
export class SqliteStore {
|
|
7
|
+
db = null;
|
|
8
|
+
dbPath;
|
|
9
|
+
constructor(customPath) {
|
|
10
|
+
const root = findProjectRoot();
|
|
11
|
+
const defaultPath = path.join(root, ".ferret", "graph.db");
|
|
12
|
+
this.dbPath =
|
|
13
|
+
customPath === ":memory:"
|
|
14
|
+
? ":memory:"
|
|
15
|
+
: customPath
|
|
16
|
+
? path.resolve(root, customPath)
|
|
17
|
+
: defaultPath;
|
|
18
|
+
}
|
|
19
|
+
async init() {
|
|
20
|
+
if (this.db)
|
|
21
|
+
return;
|
|
22
|
+
if (this.dbPath !== ":memory:") {
|
|
23
|
+
const ferretDir = path.dirname(this.dbPath);
|
|
24
|
+
if (!fs.existsSync(ferretDir)) {
|
|
25
|
+
fs.mkdirSync(ferretDir, { recursive: true });
|
|
26
|
+
}
|
|
27
|
+
const gitignorePath = path.join(ferretDir, ".gitignore");
|
|
28
|
+
try {
|
|
29
|
+
fs.writeFileSync(gitignorePath, "*\n!.gitignore\n!context.json\n", "utf-8");
|
|
30
|
+
}
|
|
31
|
+
catch { }
|
|
32
|
+
}
|
|
33
|
+
this.db = new Database(this.dbPath);
|
|
34
|
+
this.db.exec("PRAGMA journal_mode = WAL;");
|
|
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
|
+
);
|
|
78
|
+
`);
|
|
79
|
+
try {
|
|
80
|
+
this.db.exec(`ALTER TABLE ferret_contracts ADD COLUMN shape_schema TEXT NOT NULL DEFAULT '{}';`);
|
|
81
|
+
}
|
|
82
|
+
catch { }
|
|
83
|
+
}
|
|
84
|
+
async close() {
|
|
85
|
+
if (this.db) {
|
|
86
|
+
this.db.close();
|
|
87
|
+
this.db = null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async getNodeByFilePath(filePath) {
|
|
91
|
+
const row = this.db.prepare("SELECT * FROM ferret_nodes WHERE file_path = ?").get(filePath);
|
|
92
|
+
return row ?? null;
|
|
93
|
+
}
|
|
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
|
|
103
|
+
`).run(node.id, node.file_path, node.hash, node.status);
|
|
104
|
+
}
|
|
105
|
+
async getAllContractIds() {
|
|
106
|
+
return this.db.prepare("SELECT id FROM ferret_contracts").all().map((r) => r.id);
|
|
107
|
+
}
|
|
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
|
|
118
|
+
`).run(contract.id, contract.node_id, contract.shape_hash, contract.shape_schema, contract.type, contract.status);
|
|
119
|
+
}
|
|
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
|
|
125
|
+
`).run(dependency.id, dependency.source_node_id, dependency.target_contract_id);
|
|
126
|
+
}
|
|
127
|
+
async replaceDependenciesForSourceNode(sourceNodeId, targetContractIds) {
|
|
128
|
+
const uniqueTargets = [...new Set(targetContractIds)].sort();
|
|
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 (?, ?, ?)
|
|
133
|
+
`);
|
|
134
|
+
const transaction = this.db.transaction((sourceId) => {
|
|
135
|
+
deleteStatement.run(sourceId);
|
|
136
|
+
for (const targetId of uniqueTargets) {
|
|
137
|
+
insertStatement.run(randomUUID(), sourceId, targetId);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
transaction(sourceNodeId);
|
|
141
|
+
}
|
|
142
|
+
async getNodes() {
|
|
143
|
+
return this.db.prepare("SELECT * FROM ferret_nodes").all();
|
|
144
|
+
}
|
|
145
|
+
async getNodesByStatus(status) {
|
|
146
|
+
return this.db.prepare("SELECT * FROM ferret_nodes WHERE status = ?").all(status);
|
|
147
|
+
}
|
|
148
|
+
async getContracts() {
|
|
149
|
+
return this.db.prepare("SELECT * FROM ferret_contracts").all();
|
|
150
|
+
}
|
|
151
|
+
async getContract(id) {
|
|
152
|
+
const row = this.db.prepare("SELECT * FROM ferret_contracts WHERE id = ?").get(id);
|
|
153
|
+
return row ?? null;
|
|
154
|
+
}
|
|
155
|
+
async getDependencies() {
|
|
156
|
+
return this.db.prepare("SELECT * FROM ferret_dependencies").all();
|
|
157
|
+
}
|
|
158
|
+
async updateNodeStatus(nodeId, status) {
|
|
159
|
+
this.db.prepare("UPDATE ferret_nodes SET status = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?").run(status, nodeId);
|
|
160
|
+
}
|
|
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 (?, ?, ?, ?, ?)
|
|
165
|
+
`).run(log.id, log.node_id, log.triggered_by, log.resolved_by ?? null, log.resolution_notes ?? null);
|
|
166
|
+
}
|
|
167
|
+
async getReconciliationLogs() {
|
|
168
|
+
return this.db.prepare("SELECT id, node_id, triggered_by, resolved_by, resolution_notes FROM ferret_reconciliation_log").all();
|
|
169
|
+
}
|
|
170
|
+
async insertPlacementDecision(decision) {
|
|
171
|
+
this.db.prepare(`
|
|
172
|
+
INSERT INTO ferret_placement_decisions (id, node_id, placed_by, reasoning)
|
|
173
|
+
VALUES (?, ?, ?, ?)
|
|
174
|
+
`).run(decision.id, decision.node_id, decision.placed_by, decision.reasoning ?? null);
|
|
175
|
+
}
|
|
176
|
+
async getPlacementDecisions() {
|
|
177
|
+
return this.db.prepare("SELECT id, node_id, placed_by, reasoning FROM ferret_placement_decisions").all();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=sqlite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../../src/store/sqlite.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export type NodeStatus = "stable" | "needs-review" | "roadmap" | "blocked";
|
|
2
|
+
export type ContractStatus = "stable" | "roadmap" | "needs-review";
|
|
3
|
+
export interface FerretNode {
|
|
4
|
+
id: string;
|
|
5
|
+
file_path: string;
|
|
6
|
+
hash: string;
|
|
7
|
+
status: NodeStatus;
|
|
8
|
+
updated_at?: string | Date;
|
|
9
|
+
}
|
|
10
|
+
export interface FerretContract {
|
|
11
|
+
id: string;
|
|
12
|
+
node_id: string;
|
|
13
|
+
shape_hash: string;
|
|
14
|
+
shape_schema: string;
|
|
15
|
+
type: string;
|
|
16
|
+
status: ContractStatus;
|
|
17
|
+
}
|
|
18
|
+
export interface FerretDependency {
|
|
19
|
+
id: string;
|
|
20
|
+
source_node_id: string;
|
|
21
|
+
target_contract_id: string;
|
|
22
|
+
}
|
|
23
|
+
export interface FerretReconciliationLog {
|
|
24
|
+
id: string;
|
|
25
|
+
node_id: string;
|
|
26
|
+
triggered_by: string;
|
|
27
|
+
resolved_by?: string;
|
|
28
|
+
resolution_notes?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface FerretPlacementDecision {
|
|
31
|
+
id: string;
|
|
32
|
+
node_id: string;
|
|
33
|
+
placed_by: string;
|
|
34
|
+
reasoning?: string;
|
|
35
|
+
}
|
|
36
|
+
export interface FerretMigrationSnapshot {
|
|
37
|
+
nodes: FerretNode[];
|
|
38
|
+
contracts: FerretContract[];
|
|
39
|
+
dependencies: FerretDependency[];
|
|
40
|
+
reconciliationLogs: FerretReconciliationLog[];
|
|
41
|
+
placementDecisions: FerretPlacementDecision[];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The standard contract for our Data Access Layer (DAL).
|
|
45
|
+
* All implementers (SQLite, Postgres) MUST satisfy this completely so the CLI
|
|
46
|
+
* can remain ignorant to the underlying storage type.
|
|
47
|
+
*/
|
|
48
|
+
export interface DBStore {
|
|
49
|
+
/** Core initialisation & teardown */
|
|
50
|
+
init(): Promise<void>;
|
|
51
|
+
close(): Promise<void>;
|
|
52
|
+
/** Retrieves a node based on its unique file path relative to specDir */
|
|
53
|
+
getNodeByFilePath(filePath: string): Promise<FerretNode | null>;
|
|
54
|
+
/** Upserts a node based on its ID */
|
|
55
|
+
upsertNode(node: FerretNode): Promise<void>;
|
|
56
|
+
/** Retrieves all existing contract IDs for LLM context injection */
|
|
57
|
+
getAllContractIds(): Promise<string[]>;
|
|
58
|
+
/** Upserts an extracted contract */
|
|
59
|
+
upsertContract(contract: FerretContract): Promise<void>;
|
|
60
|
+
/** Creates a dependency edge between a Node and a Contract */
|
|
61
|
+
upsertDependency(dependency: FerretDependency): Promise<void>;
|
|
62
|
+
/** Replaces all dependency edges for a source node with the provided targets */
|
|
63
|
+
replaceDependenciesForSourceNode(sourceNodeId: string, targetContractIds: string[]): Promise<void>;
|
|
64
|
+
/** Full graph retrieval methods for the Reconciler engine */
|
|
65
|
+
getNodes(): Promise<FerretNode[]>;
|
|
66
|
+
getNodesByStatus(status: NodeStatus): Promise<FerretNode[]>;
|
|
67
|
+
getContracts(): Promise<FerretContract[]>;
|
|
68
|
+
getContract(id: string): Promise<FerretContract | null>;
|
|
69
|
+
getDependencies(): Promise<FerretDependency[]>;
|
|
70
|
+
/** Update helper for the Reconciler to safely flag nodes */
|
|
71
|
+
updateNodeStatus(nodeId: string, status: NodeStatus): Promise<void>;
|
|
72
|
+
/** Records review actions against flagged nodes S013 */
|
|
73
|
+
insertReconciliationLog(log: FerretReconciliationLog): Promise<void>;
|
|
74
|
+
getReconciliationLogs(): Promise<FerretReconciliationLog[]>;
|
|
75
|
+
/** Tracks feature placement decisions S016 */
|
|
76
|
+
insertPlacementDecision(decision: FerretPlacementDecision): Promise<void>;
|
|
77
|
+
getPlacementDecisions(): Promise<FerretPlacementDecision[]>;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/store/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/store/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAgBxE"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* Walks up from cwd looking for .ferret/ or ferret.config.json.
|
|
5
|
+
* Falls back to cwd if neither is found.
|
|
6
|
+
*/
|
|
7
|
+
export function findProjectRoot(startDir = process.cwd()) {
|
|
8
|
+
let current = path.resolve(startDir);
|
|
9
|
+
for (let i = 0; i < 20; i++) {
|
|
10
|
+
if (fs.existsSync(path.join(current, '.ferret')) ||
|
|
11
|
+
fs.existsSync(path.join(current, 'ferret.config.json'))) {
|
|
12
|
+
return current;
|
|
13
|
+
}
|
|
14
|
+
const parent = path.dirname(current);
|
|
15
|
+
if (parent === current)
|
|
16
|
+
break; // filesystem root
|
|
17
|
+
current = parent;
|
|
18
|
+
}
|
|
19
|
+
return path.resolve(startDir);
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,WAAmB,OAAO,CAAC,GAAG,EAAE;IAC9D,IAAI,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAErC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5B,IACE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;YAC5C,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,oBAAoB,CAAC,CAAC,EACvD,CAAC;YACD,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACrC,IAAI,MAAM,KAAK,OAAO;YAAE,MAAM,CAAC,kBAAkB;QACjD,OAAO,GAAG,MAAM,CAAC;IACnB,CAAC;IAED,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;AAChC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +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
|
+
}
|