@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.
- package/.turbo/turbo-build.log +4 -0
- package/dist/cache.d.ts +24 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +97 -0
- package/dist/cache.js.map +1 -0
- package/dist/diff.d.ts +34 -0
- package/dist/diff.d.ts.map +1 -0
- package/dist/diff.js +319 -0
- package/dist/diff.js.map +1 -0
- package/dist/extract.d.ts +27 -0
- package/dist/extract.d.ts.map +1 -0
- package/dist/extract.js +162 -0
- package/dist/extract.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin.d.ts +103 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +47 -0
- package/dist/plugin.js.map +1 -0
- package/dist/safety.d.ts +25 -0
- package/dist/safety.d.ts.map +1 -0
- package/dist/safety.js +139 -0
- package/dist/safety.js.map +1 -0
- package/dist/schema.d.ts +184 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +33 -0
- package/dist/schema.js.map +1 -0
- package/dist/serialize.d.ts +16 -0
- package/dist/serialize.d.ts.map +1 -0
- package/dist/serialize.js +75 -0
- package/dist/serialize.js.map +1 -0
- package/package.json +35 -0
- package/src/cache.ts +133 -0
- package/src/diff.ts +421 -0
- package/src/extract.ts +196 -0
- package/src/index.ts +94 -0
- package/src/plugin.ts +186 -0
- package/src/safety.ts +185 -0
- package/src/schema.ts +270 -0
- package/src/serialize.ts +97 -0
- package/tests/cache.test.ts +47 -0
- package/tests/diff.test.ts +222 -0
- package/tests/plugin.test.ts +107 -0
- package/tests/schema.test.ts +132 -0
- package/tests/serialize.test.ts +92 -0
- package/tsconfig.json +20 -0
package/src/plugin.ts
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sigil Plugin System — the extensibility contract.
|
|
3
|
+
*
|
|
4
|
+
* Every language parser implements `ExtractorPlugin`. The core engine
|
|
5
|
+
* discovers registered plugins and orchestrates them.
|
|
6
|
+
*
|
|
7
|
+
* To add Python support:
|
|
8
|
+
* 1. Create @sigil-engine/parser-python
|
|
9
|
+
* 2. Implement ExtractorPlugin
|
|
10
|
+
* 3. Export a default plugin instance
|
|
11
|
+
* 4. Users install it — Sigil discovers it automatically
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { ContextDocument } from "./schema.js";
|
|
15
|
+
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Plugin interface
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The parsed project representation passed to plugins.
|
|
22
|
+
* Language-agnostic — each plugin casts or wraps this with its own type.
|
|
23
|
+
*/
|
|
24
|
+
export interface ParsedProject {
|
|
25
|
+
/** Absolute path to the project root */
|
|
26
|
+
projectPath: string;
|
|
27
|
+
/** List of source file paths (absolute) */
|
|
28
|
+
sourceFiles: string[];
|
|
29
|
+
/** Package manifest (package.json, pyproject.toml, etc.) parsed as object */
|
|
30
|
+
manifest?: Record<string, unknown>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Result returned by a plugin's extract method.
|
|
35
|
+
* Partial<ContextDocument> — plugins only fill what they understand.
|
|
36
|
+
*/
|
|
37
|
+
export type ExtractResult = Partial<Omit<ContextDocument, "version" | "project" | "_extraction">>;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The core plugin contract. Every language parser implements this.
|
|
41
|
+
*/
|
|
42
|
+
export interface ExtractorPlugin {
|
|
43
|
+
/** Unique plugin name (e.g., "typescript", "python", "go") */
|
|
44
|
+
name: string;
|
|
45
|
+
|
|
46
|
+
/** Semver version of the plugin */
|
|
47
|
+
version: string;
|
|
48
|
+
|
|
49
|
+
/** Languages/frameworks this plugin handles */
|
|
50
|
+
languages: string[];
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Check if this plugin should run for the given project.
|
|
54
|
+
* Called before extract() — return false to skip.
|
|
55
|
+
*
|
|
56
|
+
* Example: typescript plugin checks for tsconfig.json or .ts files
|
|
57
|
+
*/
|
|
58
|
+
detect(projectPath: string): boolean | Promise<boolean>;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Initialize the plugin for a specific project.
|
|
62
|
+
* Returns a ParsedProject (or plugin-specific extension of it).
|
|
63
|
+
* Called once before extract().
|
|
64
|
+
*/
|
|
65
|
+
init(projectPath: string): ParsedProject | Promise<ParsedProject>;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Extract context from the project.
|
|
69
|
+
* Returns partial context — the orchestrator merges results from all plugins.
|
|
70
|
+
*/
|
|
71
|
+
extract(project: ParsedProject): ExtractResult | Promise<ExtractResult>;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Optional: handle apply intents for elements this plugin created.
|
|
75
|
+
* Called by the apply engine when an intent targets a _meta.tags
|
|
76
|
+
* value owned by this plugin.
|
|
77
|
+
*/
|
|
78
|
+
apply?(intent: ApplyIntent, projectPath: string): ApplyResult | Promise<ApplyResult>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
// Apply types (used by plugins that support apply)
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
|
|
85
|
+
export type IntentType =
|
|
86
|
+
| "AddEntity"
|
|
87
|
+
| "RemoveEntity"
|
|
88
|
+
| "AddField"
|
|
89
|
+
| "RemoveField"
|
|
90
|
+
| "ModifyField"
|
|
91
|
+
| "RenameSymbol"
|
|
92
|
+
| "AddComponent"
|
|
93
|
+
| "RemoveComponent"
|
|
94
|
+
| "AddRoute"
|
|
95
|
+
| "RemoveRoute"
|
|
96
|
+
| "AddMethod"
|
|
97
|
+
| "RemoveMethod"
|
|
98
|
+
| "ModifyProps"
|
|
99
|
+
| "ChangeSignature"
|
|
100
|
+
| "MoveFile"
|
|
101
|
+
| "AddDependency"
|
|
102
|
+
| "RemoveDependency";
|
|
103
|
+
|
|
104
|
+
export type IntentConfidence = "deterministic" | "needs-ai";
|
|
105
|
+
|
|
106
|
+
export interface ApplyIntent {
|
|
107
|
+
type: IntentType;
|
|
108
|
+
confidence: IntentConfidence;
|
|
109
|
+
target_file?: string;
|
|
110
|
+
target_symbol?: string;
|
|
111
|
+
description: string;
|
|
112
|
+
old_value?: unknown;
|
|
113
|
+
new_value?: unknown;
|
|
114
|
+
priority: number;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export interface ApplyResult {
|
|
118
|
+
intent: ApplyIntent;
|
|
119
|
+
success: boolean;
|
|
120
|
+
error?: string;
|
|
121
|
+
files_created: string[];
|
|
122
|
+
files_modified: string[];
|
|
123
|
+
preview?: string;
|
|
124
|
+
code?: string;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ---------------------------------------------------------------------------
|
|
128
|
+
// Plugin registry
|
|
129
|
+
// ---------------------------------------------------------------------------
|
|
130
|
+
|
|
131
|
+
const registry: ExtractorPlugin[] = [];
|
|
132
|
+
|
|
133
|
+
/** Register a plugin with Sigil */
|
|
134
|
+
export function registerPlugin(plugin: ExtractorPlugin): void {
|
|
135
|
+
// Prevent duplicate registration
|
|
136
|
+
const existing = registry.findIndex((p) => p.name === plugin.name);
|
|
137
|
+
if (existing >= 0) {
|
|
138
|
+
registry[existing] = plugin;
|
|
139
|
+
} else {
|
|
140
|
+
registry.push(plugin);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Get all registered plugins */
|
|
145
|
+
export function getPlugins(): readonly ExtractorPlugin[] {
|
|
146
|
+
return registry;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Get plugins that detect support for a given project */
|
|
150
|
+
export async function detectPlugins(projectPath: string): Promise<ExtractorPlugin[]> {
|
|
151
|
+
const detected: ExtractorPlugin[] = [];
|
|
152
|
+
for (const plugin of registry) {
|
|
153
|
+
const supports = await plugin.detect(projectPath);
|
|
154
|
+
if (supports) {
|
|
155
|
+
detected.push(plugin);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return detected;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** Clear all registered plugins (for testing) */
|
|
162
|
+
export function clearPlugins(): void {
|
|
163
|
+
registry.length = 0;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ---------------------------------------------------------------------------
|
|
167
|
+
// Progress reporting
|
|
168
|
+
// ---------------------------------------------------------------------------
|
|
169
|
+
|
|
170
|
+
export type ProgressPhase =
|
|
171
|
+
| "init"
|
|
172
|
+
| "detect"
|
|
173
|
+
| "extract"
|
|
174
|
+
| "merge"
|
|
175
|
+
| "serialize"
|
|
176
|
+
| "cache"
|
|
177
|
+
| "done";
|
|
178
|
+
|
|
179
|
+
export interface ProgressEvent {
|
|
180
|
+
phase: ProgressPhase;
|
|
181
|
+
plugin?: string;
|
|
182
|
+
message: string;
|
|
183
|
+
elapsed_ms?: number;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export type ProgressCallback = (event: ProgressEvent) => void;
|
package/src/safety.ts
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Apply safety mechanisms — git stash before, validation after, rollback on failure.
|
|
3
|
+
*
|
|
4
|
+
* SECURITY: All git operations use execFileSync (not execSync) to prevent
|
|
5
|
+
* shell injection. Destructive operations require explicit confirmation.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { execFileSync } from "child_process";
|
|
9
|
+
import { resolve } from "path";
|
|
10
|
+
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Pre-apply: save state
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
|
|
15
|
+
export interface SafetyCheckpoint {
|
|
16
|
+
hadDirtyFiles: boolean;
|
|
17
|
+
stashCreated: boolean;
|
|
18
|
+
originalHead: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Callback for confirming destructive operations.
|
|
23
|
+
* Return true to proceed, false to abort.
|
|
24
|
+
*/
|
|
25
|
+
export type ConfirmCallback = (message: string) => boolean | Promise<boolean>;
|
|
26
|
+
|
|
27
|
+
/** Default confirmation — always denies (safe default) */
|
|
28
|
+
const denyAll: ConfirmCallback = () => false;
|
|
29
|
+
|
|
30
|
+
export function preApplySafety(
|
|
31
|
+
projectPath: string,
|
|
32
|
+
confirm: ConfirmCallback = denyAll,
|
|
33
|
+
): SafetyCheckpoint {
|
|
34
|
+
const absPath = resolve(projectPath);
|
|
35
|
+
|
|
36
|
+
// Get current HEAD — using execFileSync (no shell)
|
|
37
|
+
let originalHead = "";
|
|
38
|
+
try {
|
|
39
|
+
originalHead = execFileSync("git", ["rev-parse", "HEAD"], {
|
|
40
|
+
cwd: absPath,
|
|
41
|
+
encoding: "utf-8",
|
|
42
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
43
|
+
}).trim();
|
|
44
|
+
} catch {
|
|
45
|
+
// Not a git repo — skip safety
|
|
46
|
+
return { hadDirtyFiles: false, stashCreated: false, originalHead: "" };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Check for dirty files
|
|
50
|
+
let gitStatus = "";
|
|
51
|
+
try {
|
|
52
|
+
gitStatus = execFileSync("git", ["status", "--porcelain"], {
|
|
53
|
+
cwd: absPath,
|
|
54
|
+
encoding: "utf-8",
|
|
55
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
56
|
+
}).trim();
|
|
57
|
+
} catch {
|
|
58
|
+
return { hadDirtyFiles: false, stashCreated: false, originalHead };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const hadDirtyFiles = gitStatus.length > 0;
|
|
62
|
+
|
|
63
|
+
if (hadDirtyFiles) {
|
|
64
|
+
try {
|
|
65
|
+
execFileSync("git", ["stash", "push", "-m", "sigil-pre-apply"], {
|
|
66
|
+
cwd: absPath,
|
|
67
|
+
encoding: "utf-8",
|
|
68
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
69
|
+
});
|
|
70
|
+
return { hadDirtyFiles, stashCreated: true, originalHead };
|
|
71
|
+
} catch {
|
|
72
|
+
return { hadDirtyFiles, stashCreated: false, originalHead };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return { hadDirtyFiles, stashCreated: false, originalHead };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
// Post-apply: validate types
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
|
|
83
|
+
export interface ValidationResult {
|
|
84
|
+
typesOk: boolean;
|
|
85
|
+
errors: string[];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function postApplyValidation(projectPath: string): ValidationResult {
|
|
89
|
+
const absPath = resolve(projectPath);
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
execFileSync("npx", ["tsc", "--noEmit"], {
|
|
93
|
+
cwd: absPath,
|
|
94
|
+
encoding: "utf-8",
|
|
95
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
96
|
+
timeout: 30000,
|
|
97
|
+
});
|
|
98
|
+
return { typesOk: true, errors: [] };
|
|
99
|
+
} catch (err: unknown) {
|
|
100
|
+
const output =
|
|
101
|
+
(err as { stdout?: string }).stdout ??
|
|
102
|
+
(err as { stderr?: string }).stderr ??
|
|
103
|
+
"";
|
|
104
|
+
const errors = output
|
|
105
|
+
.split("\n")
|
|
106
|
+
.filter((line) => line.includes("error TS"))
|
|
107
|
+
.slice(0, 10);
|
|
108
|
+
return { typesOk: false, errors };
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
// Rollback
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
|
|
116
|
+
export async function rollbackApply(
|
|
117
|
+
projectPath: string,
|
|
118
|
+
checkpoint: SafetyCheckpoint,
|
|
119
|
+
confirm: ConfirmCallback = denyAll,
|
|
120
|
+
): Promise<boolean> {
|
|
121
|
+
const absPath = resolve(projectPath);
|
|
122
|
+
|
|
123
|
+
// Require confirmation before destructive rollback
|
|
124
|
+
const confirmed = await confirm(
|
|
125
|
+
"Apply produced type errors. Roll back all changes and restore previous state?",
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
if (!confirmed) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Discard apply changes — execFileSync, no shell
|
|
133
|
+
try {
|
|
134
|
+
execFileSync("git", ["checkout", "."], {
|
|
135
|
+
cwd: absPath,
|
|
136
|
+
encoding: "utf-8",
|
|
137
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
138
|
+
});
|
|
139
|
+
} catch {
|
|
140
|
+
// Could not roll back
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Clean untracked files created by apply
|
|
145
|
+
try {
|
|
146
|
+
execFileSync("git", ["clean", "-fd"], {
|
|
147
|
+
cwd: absPath,
|
|
148
|
+
encoding: "utf-8",
|
|
149
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
150
|
+
});
|
|
151
|
+
} catch {
|
|
152
|
+
// Non-fatal
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Restore stash if we created one
|
|
156
|
+
if (checkpoint.stashCreated) {
|
|
157
|
+
try {
|
|
158
|
+
execFileSync("git", ["stash", "pop"], {
|
|
159
|
+
cwd: absPath,
|
|
160
|
+
encoding: "utf-8",
|
|
161
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
162
|
+
});
|
|
163
|
+
} catch {
|
|
164
|
+
// Stash pop failed — user must handle manually
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ---------------------------------------------------------------------------
|
|
172
|
+
// Git commit helper (for post-apply)
|
|
173
|
+
// ---------------------------------------------------------------------------
|
|
174
|
+
|
|
175
|
+
export function getGitStatus(projectPath: string): string {
|
|
176
|
+
try {
|
|
177
|
+
return execFileSync("git", ["status", "--porcelain"], {
|
|
178
|
+
cwd: resolve(projectPath),
|
|
179
|
+
encoding: "utf-8",
|
|
180
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
181
|
+
}).trim();
|
|
182
|
+
} catch {
|
|
183
|
+
return "";
|
|
184
|
+
}
|
|
185
|
+
}
|
package/src/schema.ts
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
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
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Meta anchor — the bidirectional link
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
export interface Meta {
|
|
18
|
+
/** Relative path from project root to source file */
|
|
19
|
+
file: string;
|
|
20
|
+
/** Exported symbol name (function, class, variable, type) */
|
|
21
|
+
symbol: string;
|
|
22
|
+
/** Generic element category — language-agnostic */
|
|
23
|
+
type: MetaType;
|
|
24
|
+
/** Plugin-contributed tags for technology-specific classification */
|
|
25
|
+
tags?: string[];
|
|
26
|
+
/** Line number in source file (for precise anchoring) */
|
|
27
|
+
line?: number;
|
|
28
|
+
/** Truncated SHA-256 hash of source file at extraction time */
|
|
29
|
+
hash?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Generic element categories. These are structural roles, not
|
|
34
|
+
* technology-specific types. Plugins use `tags` for specificity.
|
|
35
|
+
*
|
|
36
|
+
* Example: a Drizzle table has type "entity" with tags ["orm:drizzle", "db:postgresql"]
|
|
37
|
+
* Example: a React component has type "component" with tags ["framework:react", "client"]
|
|
38
|
+
* Example: a Django model has type "entity" with tags ["orm:django", "db:postgresql"]
|
|
39
|
+
*/
|
|
40
|
+
export type MetaType =
|
|
41
|
+
| "entity" // Data model (DB table, ORM model, GraphQL type)
|
|
42
|
+
| "component" // UI component (React, Vue, Svelte, etc.)
|
|
43
|
+
| "route" // HTTP route / API endpoint
|
|
44
|
+
| "function" // Standalone function
|
|
45
|
+
| "class" // Class declaration
|
|
46
|
+
| "type" // Type alias, interface, enum
|
|
47
|
+
| "constant" // Constant / config value
|
|
48
|
+
| "middleware" // Request middleware / interceptor
|
|
49
|
+
| "hook" // Framework-specific hook (React hook, lifecycle, etc.)
|
|
50
|
+
| "config"; // Configuration file / entry
|
|
51
|
+
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// Stack & project metadata (extensible)
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Project stack information — plugins contribute their own key-value pairs.
|
|
58
|
+
*
|
|
59
|
+
* Common keys (by convention, not enforced):
|
|
60
|
+
* framework, language, orm, database, ui, auth, testing, package_manager
|
|
61
|
+
*
|
|
62
|
+
* Values are strings or string arrays.
|
|
63
|
+
*/
|
|
64
|
+
export type StackInfo = Record<string, string | string[]>;
|
|
65
|
+
|
|
66
|
+
/** Extraction metadata — when and how the context was generated */
|
|
67
|
+
export interface ExtractionMeta {
|
|
68
|
+
extracted_at: string;
|
|
69
|
+
sigil_version?: string;
|
|
70
|
+
source_commit?: string;
|
|
71
|
+
extraction_ms?: number;
|
|
72
|
+
plugins_used?: string[];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
// Entities (data models — ORM tables, GraphQL types, etc.)
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
|
|
79
|
+
export interface Entity {
|
|
80
|
+
name: string;
|
|
81
|
+
_meta: Meta;
|
|
82
|
+
fields: EntityField[];
|
|
83
|
+
relations: EntityRelation[];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface EntityField {
|
|
87
|
+
name: string;
|
|
88
|
+
type: string;
|
|
89
|
+
primary?: boolean;
|
|
90
|
+
unique?: boolean;
|
|
91
|
+
nullable?: boolean;
|
|
92
|
+
default?: string;
|
|
93
|
+
references?: { table: string; column: string };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface EntityRelation {
|
|
97
|
+
to: string;
|
|
98
|
+
type: "one-to-one" | "one-to-many" | "many-to-many";
|
|
99
|
+
via?: string;
|
|
100
|
+
through?: string;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// Components (UI elements — framework-agnostic)
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
export interface Component {
|
|
108
|
+
name: string;
|
|
109
|
+
_meta: Meta;
|
|
110
|
+
props: ComponentProp[];
|
|
111
|
+
dependencies: string[];
|
|
112
|
+
hooks: string[];
|
|
113
|
+
data_sources: string[];
|
|
114
|
+
route?: string;
|
|
115
|
+
/** Plugin-specific flags via tags instead of hardcoded booleans */
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface ComponentProp {
|
|
119
|
+
name: string;
|
|
120
|
+
type: string;
|
|
121
|
+
required?: boolean;
|
|
122
|
+
default?: string;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ---------------------------------------------------------------------------
|
|
126
|
+
// Routes (API endpoints — framework-agnostic)
|
|
127
|
+
// ---------------------------------------------------------------------------
|
|
128
|
+
|
|
129
|
+
export interface Route {
|
|
130
|
+
path: string;
|
|
131
|
+
_meta: Meta;
|
|
132
|
+
methods: Record<string, RouteMethod>;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export interface RouteMethod {
|
|
136
|
+
auth?: boolean;
|
|
137
|
+
returns?: string;
|
|
138
|
+
params?: string[];
|
|
139
|
+
body?: string;
|
|
140
|
+
description?: string;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
// Exports (generic symbols not captured elsewhere)
|
|
145
|
+
// ---------------------------------------------------------------------------
|
|
146
|
+
|
|
147
|
+
export interface ExportedSymbol {
|
|
148
|
+
name: string;
|
|
149
|
+
_meta: Meta;
|
|
150
|
+
kind: "function" | "class" | "interface" | "type" | "enum" | "constant";
|
|
151
|
+
signature?: string;
|
|
152
|
+
description?: string;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ---------------------------------------------------------------------------
|
|
156
|
+
// Patterns & conventions (extensible key-value)
|
|
157
|
+
// ---------------------------------------------------------------------------
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Detected architectural patterns. Plugins contribute entries.
|
|
161
|
+
* Keys are pattern categories (e.g., "auth", "data_fetching", "state").
|
|
162
|
+
* Values describe the detected pattern.
|
|
163
|
+
*/
|
|
164
|
+
export type Patterns = Record<string, string>;
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Detected conventions. Plugins contribute entries.
|
|
168
|
+
* Keys are convention categories (e.g., "file_naming", "import_style").
|
|
169
|
+
* Values describe the convention.
|
|
170
|
+
*/
|
|
171
|
+
export type Conventions = Record<string, string>;
|
|
172
|
+
|
|
173
|
+
// ---------------------------------------------------------------------------
|
|
174
|
+
// Dependencies (from package manifest)
|
|
175
|
+
// ---------------------------------------------------------------------------
|
|
176
|
+
|
|
177
|
+
export interface Dependency {
|
|
178
|
+
name: string;
|
|
179
|
+
version: string;
|
|
180
|
+
dev?: boolean;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ---------------------------------------------------------------------------
|
|
184
|
+
// Call graph (behavior layer)
|
|
185
|
+
// ---------------------------------------------------------------------------
|
|
186
|
+
|
|
187
|
+
export interface CallEdge {
|
|
188
|
+
/** Fully qualified caller: "file::symbol" */
|
|
189
|
+
caller: string;
|
|
190
|
+
/** Fully qualified callee: "file::symbol" */
|
|
191
|
+
callee: string;
|
|
192
|
+
location: { file: string; line: number };
|
|
193
|
+
data_flow?: { args: string[]; returns: string };
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export interface ComponentEdge {
|
|
197
|
+
source: string;
|
|
198
|
+
target: string;
|
|
199
|
+
weight: number;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// ---------------------------------------------------------------------------
|
|
203
|
+
// Environment variables
|
|
204
|
+
// ---------------------------------------------------------------------------
|
|
205
|
+
|
|
206
|
+
export interface EnvVar {
|
|
207
|
+
name: string;
|
|
208
|
+
required: boolean;
|
|
209
|
+
used_in: string[];
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ---------------------------------------------------------------------------
|
|
213
|
+
// The root document
|
|
214
|
+
// ---------------------------------------------------------------------------
|
|
215
|
+
|
|
216
|
+
export interface ContextDocument {
|
|
217
|
+
/** Schema version for compatibility checking */
|
|
218
|
+
version: string;
|
|
219
|
+
/** Project name */
|
|
220
|
+
project: string;
|
|
221
|
+
/** Extraction metadata */
|
|
222
|
+
_extraction: ExtractionMeta;
|
|
223
|
+
/** Technology stack (extensible key-value) */
|
|
224
|
+
stack: StackInfo;
|
|
225
|
+
/** Data entities (DB tables, models) */
|
|
226
|
+
entities: Entity[];
|
|
227
|
+
/** UI components */
|
|
228
|
+
components: Component[];
|
|
229
|
+
/** API routes / endpoints */
|
|
230
|
+
routes: Route[];
|
|
231
|
+
/** Other exported symbols */
|
|
232
|
+
exports: ExportedSymbol[];
|
|
233
|
+
/** Function-level call graph */
|
|
234
|
+
call_graph: CallEdge[];
|
|
235
|
+
/** File-level interaction graph */
|
|
236
|
+
component_graph: ComponentEdge[];
|
|
237
|
+
/** Environment variables */
|
|
238
|
+
env_vars: EnvVar[];
|
|
239
|
+
/** Detected architectural patterns */
|
|
240
|
+
patterns: Patterns;
|
|
241
|
+
/** Detected conventions */
|
|
242
|
+
conventions: Conventions;
|
|
243
|
+
/** Package dependencies */
|
|
244
|
+
dependencies: Dependency[];
|
|
245
|
+
/** Plugin-contributed custom sections */
|
|
246
|
+
extensions?: Record<string, unknown>;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// ---------------------------------------------------------------------------
|
|
250
|
+
// Factory
|
|
251
|
+
// ---------------------------------------------------------------------------
|
|
252
|
+
|
|
253
|
+
export function createEmptyDocument(project: string): ContextDocument {
|
|
254
|
+
return {
|
|
255
|
+
version: "1.0",
|
|
256
|
+
project,
|
|
257
|
+
_extraction: { extracted_at: new Date().toISOString() },
|
|
258
|
+
stack: {},
|
|
259
|
+
entities: [],
|
|
260
|
+
components: [],
|
|
261
|
+
routes: [],
|
|
262
|
+
exports: [],
|
|
263
|
+
call_graph: [],
|
|
264
|
+
component_graph: [],
|
|
265
|
+
env_vars: [],
|
|
266
|
+
patterns: {},
|
|
267
|
+
conventions: {},
|
|
268
|
+
dependencies: [],
|
|
269
|
+
};
|
|
270
|
+
}
|