@sigil-engine/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 (48) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/dist/cache.d.ts +24 -0
  3. package/dist/cache.d.ts.map +1 -0
  4. package/dist/cache.js +97 -0
  5. package/dist/cache.js.map +1 -0
  6. package/dist/diff.d.ts +34 -0
  7. package/dist/diff.d.ts.map +1 -0
  8. package/dist/diff.js +319 -0
  9. package/dist/diff.js.map +1 -0
  10. package/dist/extract.d.ts +27 -0
  11. package/dist/extract.d.ts.map +1 -0
  12. package/dist/extract.js +162 -0
  13. package/dist/extract.js.map +1 -0
  14. package/dist/index.d.ts +26 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +21 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/plugin.d.ts +103 -0
  19. package/dist/plugin.d.ts.map +1 -0
  20. package/dist/plugin.js +47 -0
  21. package/dist/plugin.js.map +1 -0
  22. package/dist/safety.d.ts +25 -0
  23. package/dist/safety.d.ts.map +1 -0
  24. package/dist/safety.js +139 -0
  25. package/dist/safety.js.map +1 -0
  26. package/dist/schema.d.ts +184 -0
  27. package/dist/schema.d.ts.map +1 -0
  28. package/dist/schema.js +33 -0
  29. package/dist/schema.js.map +1 -0
  30. package/dist/serialize.d.ts +16 -0
  31. package/dist/serialize.d.ts.map +1 -0
  32. package/dist/serialize.js +75 -0
  33. package/dist/serialize.js.map +1 -0
  34. package/package.json +35 -0
  35. package/src/cache.ts +133 -0
  36. package/src/diff.ts +421 -0
  37. package/src/extract.ts +196 -0
  38. package/src/index.ts +94 -0
  39. package/src/plugin.ts +186 -0
  40. package/src/safety.ts +185 -0
  41. package/src/schema.ts +270 -0
  42. package/src/serialize.ts +97 -0
  43. package/tests/cache.test.ts +47 -0
  44. package/tests/diff.test.ts +222 -0
  45. package/tests/plugin.test.ts +107 -0
  46. package/tests/schema.test.ts +132 -0
  47. package/tests/serialize.test.ts +92 -0
  48. package/tsconfig.json +20 -0
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Sigil Context Document Schema — the universal intermediate representation.
3
+ *
4
+ * DESIGN PRINCIPLE: This schema is LANGUAGE-AGNOSTIC. It contains zero
5
+ * references to any specific language, framework, ORM, or toolchain.
6
+ * Technology-specific metadata is contributed by plugins via the `tags`
7
+ * system and extensible record types.
8
+ *
9
+ * The _meta anchor on every element enables bidirectional sync —
10
+ * each context element links back to its source location.
11
+ */
12
+ export interface Meta {
13
+ /** Relative path from project root to source file */
14
+ file: string;
15
+ /** Exported symbol name (function, class, variable, type) */
16
+ symbol: string;
17
+ /** Generic element category — language-agnostic */
18
+ type: MetaType;
19
+ /** Plugin-contributed tags for technology-specific classification */
20
+ tags?: string[];
21
+ /** Line number in source file (for precise anchoring) */
22
+ line?: number;
23
+ /** Truncated SHA-256 hash of source file at extraction time */
24
+ hash?: string;
25
+ }
26
+ /**
27
+ * Generic element categories. These are structural roles, not
28
+ * technology-specific types. Plugins use `tags` for specificity.
29
+ *
30
+ * Example: a Drizzle table has type "entity" with tags ["orm:drizzle", "db:postgresql"]
31
+ * Example: a React component has type "component" with tags ["framework:react", "client"]
32
+ * Example: a Django model has type "entity" with tags ["orm:django", "db:postgresql"]
33
+ */
34
+ export type MetaType = "entity" | "component" | "route" | "function" | "class" | "type" | "constant" | "middleware" | "hook" | "config";
35
+ /**
36
+ * Project stack information — plugins contribute their own key-value pairs.
37
+ *
38
+ * Common keys (by convention, not enforced):
39
+ * framework, language, orm, database, ui, auth, testing, package_manager
40
+ *
41
+ * Values are strings or string arrays.
42
+ */
43
+ export type StackInfo = Record<string, string | string[]>;
44
+ /** Extraction metadata — when and how the context was generated */
45
+ export interface ExtractionMeta {
46
+ extracted_at: string;
47
+ sigil_version?: string;
48
+ source_commit?: string;
49
+ extraction_ms?: number;
50
+ plugins_used?: string[];
51
+ }
52
+ export interface Entity {
53
+ name: string;
54
+ _meta: Meta;
55
+ fields: EntityField[];
56
+ relations: EntityRelation[];
57
+ }
58
+ export interface EntityField {
59
+ name: string;
60
+ type: string;
61
+ primary?: boolean;
62
+ unique?: boolean;
63
+ nullable?: boolean;
64
+ default?: string;
65
+ references?: {
66
+ table: string;
67
+ column: string;
68
+ };
69
+ }
70
+ export interface EntityRelation {
71
+ to: string;
72
+ type: "one-to-one" | "one-to-many" | "many-to-many";
73
+ via?: string;
74
+ through?: string;
75
+ }
76
+ export interface Component {
77
+ name: string;
78
+ _meta: Meta;
79
+ props: ComponentProp[];
80
+ dependencies: string[];
81
+ hooks: string[];
82
+ data_sources: string[];
83
+ route?: string;
84
+ }
85
+ export interface ComponentProp {
86
+ name: string;
87
+ type: string;
88
+ required?: boolean;
89
+ default?: string;
90
+ }
91
+ export interface Route {
92
+ path: string;
93
+ _meta: Meta;
94
+ methods: Record<string, RouteMethod>;
95
+ }
96
+ export interface RouteMethod {
97
+ auth?: boolean;
98
+ returns?: string;
99
+ params?: string[];
100
+ body?: string;
101
+ description?: string;
102
+ }
103
+ export interface ExportedSymbol {
104
+ name: string;
105
+ _meta: Meta;
106
+ kind: "function" | "class" | "interface" | "type" | "enum" | "constant";
107
+ signature?: string;
108
+ description?: string;
109
+ }
110
+ /**
111
+ * Detected architectural patterns. Plugins contribute entries.
112
+ * Keys are pattern categories (e.g., "auth", "data_fetching", "state").
113
+ * Values describe the detected pattern.
114
+ */
115
+ export type Patterns = Record<string, string>;
116
+ /**
117
+ * Detected conventions. Plugins contribute entries.
118
+ * Keys are convention categories (e.g., "file_naming", "import_style").
119
+ * Values describe the convention.
120
+ */
121
+ export type Conventions = Record<string, string>;
122
+ export interface Dependency {
123
+ name: string;
124
+ version: string;
125
+ dev?: boolean;
126
+ }
127
+ export interface CallEdge {
128
+ /** Fully qualified caller: "file::symbol" */
129
+ caller: string;
130
+ /** Fully qualified callee: "file::symbol" */
131
+ callee: string;
132
+ location: {
133
+ file: string;
134
+ line: number;
135
+ };
136
+ data_flow?: {
137
+ args: string[];
138
+ returns: string;
139
+ };
140
+ }
141
+ export interface ComponentEdge {
142
+ source: string;
143
+ target: string;
144
+ weight: number;
145
+ }
146
+ export interface EnvVar {
147
+ name: string;
148
+ required: boolean;
149
+ used_in: string[];
150
+ }
151
+ export interface ContextDocument {
152
+ /** Schema version for compatibility checking */
153
+ version: string;
154
+ /** Project name */
155
+ project: string;
156
+ /** Extraction metadata */
157
+ _extraction: ExtractionMeta;
158
+ /** Technology stack (extensible key-value) */
159
+ stack: StackInfo;
160
+ /** Data entities (DB tables, models) */
161
+ entities: Entity[];
162
+ /** UI components */
163
+ components: Component[];
164
+ /** API routes / endpoints */
165
+ routes: Route[];
166
+ /** Other exported symbols */
167
+ exports: ExportedSymbol[];
168
+ /** Function-level call graph */
169
+ call_graph: CallEdge[];
170
+ /** File-level interaction graph */
171
+ component_graph: ComponentEdge[];
172
+ /** Environment variables */
173
+ env_vars: EnvVar[];
174
+ /** Detected architectural patterns */
175
+ patterns: Patterns;
176
+ /** Detected conventions */
177
+ conventions: Conventions;
178
+ /** Package dependencies */
179
+ dependencies: Dependency[];
180
+ /** Plugin-contributed custom sections */
181
+ extensions?: Record<string, unknown>;
182
+ }
183
+ export declare function createEmptyDocument(project: string): ContextDocument;
184
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
package/dist/schema.js ADDED
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Sigil Context Document Schema — the universal intermediate representation.
3
+ *
4
+ * DESIGN PRINCIPLE: This schema is LANGUAGE-AGNOSTIC. It contains zero
5
+ * references to any specific language, framework, ORM, or toolchain.
6
+ * Technology-specific metadata is contributed by plugins via the `tags`
7
+ * system and extensible record types.
8
+ *
9
+ * The _meta anchor on every element enables bidirectional sync —
10
+ * each context element links back to its source location.
11
+ */
12
+ // ---------------------------------------------------------------------------
13
+ // Factory
14
+ // ---------------------------------------------------------------------------
15
+ export function createEmptyDocument(project) {
16
+ return {
17
+ version: "1.0",
18
+ project,
19
+ _extraction: { extracted_at: new Date().toISOString() },
20
+ stack: {},
21
+ entities: [],
22
+ components: [],
23
+ routes: [],
24
+ exports: [],
25
+ call_graph: [],
26
+ component_graph: [],
27
+ env_vars: [],
28
+ patterns: {},
29
+ conventions: {},
30
+ dependencies: [],
31
+ };
32
+ }
33
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AA8OH,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E,MAAM,UAAU,mBAAmB,CAAC,OAAe;IACjD,OAAO;QACL,OAAO,EAAE,KAAK;QACd,OAAO;QACP,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE;QACvD,KAAK,EAAE,EAAE;QACT,QAAQ,EAAE,EAAE;QACZ,UAAU,EAAE,EAAE;QACd,MAAM,EAAE,EAAE;QACV,OAAO,EAAE,EAAE;QACX,UAAU,EAAE,EAAE;QACd,eAAe,EAAE,EAAE;QACnB,QAAQ,EAAE,EAAE;QACZ,QAAQ,EAAE,EAAE;QACZ,WAAW,EAAE,EAAE;QACf,YAAY,EAAE,EAAE;KACjB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Context document serialization — YAML and JSON.
3
+ *
4
+ * YAML is the primary human-readable format.
5
+ * JSON is the machine-consumption format.
6
+ *
7
+ * _meta anchors are serialized as compact flow mappings for readability.
8
+ */
9
+ import type { ContextDocument } from "./schema.js";
10
+ export declare function serializeYaml(doc: ContextDocument): string;
11
+ export declare function serializeJson(doc: ContextDocument): string;
12
+ export declare function deserializeYaml(yamlStr: string): ContextDocument;
13
+ export declare function deserializeJson(jsonStr: string): ContextDocument;
14
+ export declare function writeContextFile(doc: ContextDocument, outputPath: string, format?: "yaml" | "json"): void;
15
+ export declare function readContextFile(inputPath: string): ContextDocument;
16
+ //# sourceMappingURL=serialize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../src/serialize.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAMnD,wBAAgB,aAAa,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,CA0C1D;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,CAE1D;AAMD,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,CAEhE;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,CAEhE;AAMD,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,eAAe,EACpB,UAAU,EAAE,MAAM,EAClB,MAAM,GAAE,MAAM,GAAG,MAAe,GAC/B,IAAI,CAGN;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe,CAMlE"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Context document serialization — YAML and JSON.
3
+ *
4
+ * YAML is the primary human-readable format.
5
+ * JSON is the machine-consumption format.
6
+ *
7
+ * _meta anchors are serialized as compact flow mappings for readability.
8
+ */
9
+ import { stringify, parse } from "yaml";
10
+ import { readFileSync, writeFileSync } from "fs";
11
+ // ---------------------------------------------------------------------------
12
+ // Serialize
13
+ // ---------------------------------------------------------------------------
14
+ export function serializeYaml(doc) {
15
+ const header = `# context.yaml — generated by sigil extract\n# ${doc._extraction.extracted_at}\n\n`;
16
+ const yamlStr = stringify(doc, {
17
+ lineWidth: 120,
18
+ defaultKeyType: "PLAIN",
19
+ defaultStringType: "QUOTE_DOUBLE",
20
+ nullStr: "",
21
+ sortMapEntries: false,
22
+ flowCollectionPadding: true,
23
+ });
24
+ // Post-process: compact _meta blocks that are simple (no nested arrays/objects).
25
+ // _meta blocks with tags arrays are left in standard YAML format.
26
+ const processed = yamlStr.replace(/^(\s+)_meta:\n((?:\1\s+[^\s].*\n)*)/gm, (match, indent, body) => {
27
+ // If body contains array items (- "...") or nested objects, skip compaction
28
+ if (body.includes("\n" + indent + " - ") || body.includes("\n" + indent + " ")) {
29
+ return match; // Leave as-is
30
+ }
31
+ const lines = body.trim().split("\n").map((l) => l.trim()).filter((l) => l.length > 0);
32
+ // Only compact if all lines are simple key: value pairs
33
+ const pairs = [];
34
+ for (const line of lines) {
35
+ const colonIdx = line.indexOf(":");
36
+ if (colonIdx === -1)
37
+ return match; // Not a simple pair, abort
38
+ const key = line.slice(0, colonIdx).trim();
39
+ let val = line.slice(colonIdx + 1).trim();
40
+ if (val.startsWith('"') && val.endsWith('"')) {
41
+ val = val.slice(1, -1);
42
+ }
43
+ pairs.push(`${key}: "${val}"`);
44
+ }
45
+ return `${indent}_meta: { ${pairs.join(", ")} }\n`;
46
+ });
47
+ return header + processed;
48
+ }
49
+ export function serializeJson(doc) {
50
+ return JSON.stringify(doc, null, 2);
51
+ }
52
+ // ---------------------------------------------------------------------------
53
+ // Deserialize
54
+ // ---------------------------------------------------------------------------
55
+ export function deserializeYaml(yamlStr) {
56
+ return parse(yamlStr);
57
+ }
58
+ export function deserializeJson(jsonStr) {
59
+ return JSON.parse(jsonStr);
60
+ }
61
+ // ---------------------------------------------------------------------------
62
+ // File I/O helpers
63
+ // ---------------------------------------------------------------------------
64
+ export function writeContextFile(doc, outputPath, format = "yaml") {
65
+ const content = format === "json" ? serializeJson(doc) : serializeYaml(doc);
66
+ writeFileSync(outputPath, content, "utf-8");
67
+ }
68
+ export function readContextFile(inputPath) {
69
+ const content = readFileSync(inputPath, "utf-8");
70
+ if (inputPath.endsWith(".json")) {
71
+ return deserializeJson(content);
72
+ }
73
+ return deserializeYaml(content);
74
+ }
75
+ //# sourceMappingURL=serialize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialize.js","sourceRoot":"","sources":["../src/serialize.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@sigil-engine/core",
3
+ "version": "0.1.0",
4
+ "description": "Core engine for Sigil — schema, plugin interface, diff, plan, apply",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "dev": "tsc --watch",
17
+ "clean": "rm -rf dist",
18
+ "typecheck": "tsc --noEmit",
19
+ "test": "vitest run",
20
+ "test:watch": "vitest"
21
+ },
22
+ "dependencies": {
23
+ "yaml": "^2.7.0",
24
+ "glob": "^11.0.0"
25
+ },
26
+ "devDependencies": {
27
+ "typescript": "^5.7.0",
28
+ "@types/node": "^22.0.0",
29
+ "vitest": "^3.0.0"
30
+ },
31
+ "license": "MIT",
32
+ "engines": {
33
+ "node": ">=18.0.0"
34
+ }
35
+ }
package/src/cache.ts ADDED
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Incremental extraction cache — skip unchanged files on re-extraction.
3
+ *
4
+ * Saves file hashes after extraction. On re-extract, compares hashes
5
+ * and returns which files need re-parsing.
6
+ */
7
+
8
+ import { resolve } from "path";
9
+ import { readFileSync, writeFileSync, existsSync, statSync } from "fs";
10
+ import { createHash } from "crypto";
11
+
12
+ // ---------------------------------------------------------------------------
13
+ // Types
14
+ // ---------------------------------------------------------------------------
15
+
16
+ export interface FileCache {
17
+ files: Record<string, { hash: string; mtime: number }>;
18
+ last_full_extract: string;
19
+ }
20
+
21
+ export interface ChangedFiles {
22
+ changed: string[];
23
+ unchanged: string[];
24
+ added: string[];
25
+ removed: string[];
26
+ }
27
+
28
+ // ---------------------------------------------------------------------------
29
+ // Cache file path
30
+ // ---------------------------------------------------------------------------
31
+
32
+ const CACHE_FILENAME = ".sigil-cache.json";
33
+
34
+ function cachePath(projectPath: string): string {
35
+ return resolve(projectPath, CACHE_FILENAME);
36
+ }
37
+
38
+ // ---------------------------------------------------------------------------
39
+ // Read / Write
40
+ // ---------------------------------------------------------------------------
41
+
42
+ export function readCache(projectPath: string): FileCache | null {
43
+ const cp = cachePath(projectPath);
44
+ if (!existsSync(cp)) return null;
45
+ try {
46
+ return JSON.parse(readFileSync(cp, "utf-8"));
47
+ } catch {
48
+ return null;
49
+ }
50
+ }
51
+
52
+ export function writeCache(projectPath: string, cache: FileCache): void {
53
+ writeFileSync(cachePath(projectPath), JSON.stringify(cache, null, 2), "utf-8");
54
+ }
55
+
56
+ // ---------------------------------------------------------------------------
57
+ // Build cache from current file state
58
+ // ---------------------------------------------------------------------------
59
+
60
+ export function buildCacheFromFiles(
61
+ projectPath: string,
62
+ files: string[],
63
+ ): FileCache {
64
+ const fileMap: Record<string, { hash: string; mtime: number }> = {};
65
+ const absProject = resolve(projectPath);
66
+
67
+ for (const relFile of files) {
68
+ const absFile = resolve(absProject, relFile);
69
+ try {
70
+ const stat = statSync(absFile);
71
+ const content = readFileSync(absFile, "utf-8");
72
+ const hash = createHash("sha256").update(content).digest("hex").slice(0, 12);
73
+ fileMap[relFile] = { hash, mtime: stat.mtimeMs };
74
+ } catch {
75
+ // Skip unreadable files
76
+ }
77
+ }
78
+
79
+ return {
80
+ files: fileMap,
81
+ last_full_extract: new Date().toISOString(),
82
+ };
83
+ }
84
+
85
+ // ---------------------------------------------------------------------------
86
+ // Determine changed files
87
+ // ---------------------------------------------------------------------------
88
+
89
+ export function getChangedFiles(
90
+ projectPath: string,
91
+ currentFiles: string[],
92
+ cache: FileCache,
93
+ ): ChangedFiles {
94
+ const absProject = resolve(projectPath);
95
+ const changed: string[] = [];
96
+ const unchanged: string[] = [];
97
+ const added: string[] = [];
98
+ const currentSet = new Set(currentFiles);
99
+
100
+ for (const relFile of currentFiles) {
101
+ const cached = cache.files[relFile];
102
+ if (!cached) {
103
+ added.push(relFile);
104
+ continue;
105
+ }
106
+
107
+ try {
108
+ const absFile = resolve(absProject, relFile);
109
+ const stat = statSync(absFile);
110
+
111
+ // Quick check: mtime unchanged = file unchanged
112
+ if (stat.mtimeMs === cached.mtime) {
113
+ unchanged.push(relFile);
114
+ continue;
115
+ }
116
+
117
+ // mtime changed — verify with hash
118
+ const content = readFileSync(absFile, "utf-8");
119
+ const hash = createHash("sha256").update(content).digest("hex").slice(0, 12);
120
+ if (hash === cached.hash) {
121
+ unchanged.push(relFile);
122
+ } else {
123
+ changed.push(relFile);
124
+ }
125
+ } catch {
126
+ changed.push(relFile);
127
+ }
128
+ }
129
+
130
+ const removed = Object.keys(cache.files).filter((f) => !currentSet.has(f));
131
+
132
+ return { changed, unchanged, added, removed };
133
+ }