@ismaelsoilet/synapsejs 1.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 (61) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +206 -0
  3. package/bin/synapse.ts +919 -0
  4. package/package.json +70 -0
  5. package/src/client/components.tsx +508 -0
  6. package/src/client/context.tsx +62 -0
  7. package/src/client/hooks.ts +152 -0
  8. package/src/client/i18n.ts +40 -0
  9. package/src/client.ts +66 -0
  10. package/src/compiler/agent-diagnostic-json.ts +113 -0
  11. package/src/compiler/ast-daemon-compressor.ts +190 -0
  12. package/src/compiler/client-bundler.ts +356 -0
  13. package/src/compiler/context-bench.ts +191 -0
  14. package/src/compiler/contract.ts +260 -0
  15. package/src/compiler/db-schema-generator.ts +306 -0
  16. package/src/compiler/fields-parser.ts +267 -0
  17. package/src/compiler/impact-analyzer.ts +279 -0
  18. package/src/compiler/migration-runner.ts +555 -0
  19. package/src/compiler/oracle-runner.ts +293 -0
  20. package/src/compiler/scaffolder.ts +528 -0
  21. package/src/compiler/schema-dag.ts +137 -0
  22. package/src/compiler/schema-drift.ts +184 -0
  23. package/src/compiler/slice-discovery.ts +154 -0
  24. package/src/compiler/slice-splitter.ts +838 -0
  25. package/src/compiler/slice-templates.ts +1527 -0
  26. package/src/compiler/standalone-builder.ts +117 -0
  27. package/src/core/action-context.ts +152 -0
  28. package/src/core/config.ts +77 -0
  29. package/src/core/database-client.ts +246 -0
  30. package/src/core/database-factory.ts +46 -0
  31. package/src/core/image-optimizer.ts +83 -0
  32. package/src/core/index.ts +14 -0
  33. package/src/core/jobs.ts +53 -0
  34. package/src/core/machine-types.ts +75 -0
  35. package/src/core/oracle.ts +20 -0
  36. package/src/core/postgres-client.ts +164 -0
  37. package/src/core/postgres-queue.ts +288 -0
  38. package/src/core/query-builder.ts +343 -0
  39. package/src/core/rpc-client.ts +70 -0
  40. package/src/core/session-context.ts +87 -0
  41. package/src/core/session-cookie.ts +81 -0
  42. package/src/core/session-token.ts +170 -0
  43. package/src/core/sqlite-client.ts +201 -0
  44. package/src/core/storage.ts +317 -0
  45. package/src/index.ts +133 -0
  46. package/src/mcp/server.ts +553 -0
  47. package/src/runtime/client-entry.ts +65 -0
  48. package/src/runtime/discovery-rules.ts +63 -0
  49. package/src/runtime/event-hub.ts +97 -0
  50. package/src/runtime/postgres-event-hub.ts +148 -0
  51. package/src/runtime/queue-engine.ts +255 -0
  52. package/src/runtime/rate-limiter.ts +152 -0
  53. package/src/runtime/server.ts +1550 -0
  54. package/src/runtime/uploads.ts +192 -0
  55. package/templates/starter/.dockerignore +7 -0
  56. package/templates/starter/Dockerfile +40 -0
  57. package/templates/starter/README.md +40 -0
  58. package/templates/starter/gitignore +6 -0
  59. package/templates/starter/package.json +34 -0
  60. package/templates/starter/src/slices/welcome/hello-world.slice.tsx +172 -0
  61. package/templates/starter/tsconfig.json +21 -0
@@ -0,0 +1,152 @@
1
+ import { useCallback, useEffect, useRef, useState } from 'react';
2
+ import type { Result } from '../core/machine-types';
3
+
4
+ export interface UseSubscriptionOptions {
5
+ enabled?: boolean;
6
+ onOpen?: () => void;
7
+ onError?: (error: Event) => void;
8
+ }
9
+
10
+ export interface UseSubscriptionResult {
11
+ isConnected: boolean;
12
+ error: Error | null;
13
+ }
14
+
15
+ /**
16
+ * Hook for subscribing to Server-Sent Events (SSE) from client UI.
17
+ * Handles automatic subscription, message parsing, and teardown on unmount.
18
+ */
19
+ export function useSubscription<T = unknown>(
20
+ topic: string,
21
+ onMessage: (data: T) => void,
22
+ options: UseSubscriptionOptions = {}
23
+ ): UseSubscriptionResult {
24
+ const { enabled = true, onOpen, onError } = options;
25
+ const [isConnected, setIsConnected] = useState<boolean>(false);
26
+ const [error, setError] = useState<Error | null>(null);
27
+
28
+ const onMessageRef = useRef(onMessage);
29
+ onMessageRef.current = onMessage;
30
+ const onOpenRef = useRef(onOpen);
31
+ onOpenRef.current = onOpen;
32
+ const onErrorRef = useRef(onError);
33
+ onErrorRef.current = onError;
34
+
35
+ useEffect(() => {
36
+ if (
37
+ !enabled ||
38
+ typeof window === 'undefined' ||
39
+ !(window as unknown as { EventSource?: typeof EventSource }).EventSource
40
+ ) {
41
+ return;
42
+ }
43
+
44
+ const sseUrl = `/_synapse/sse/${encodeURIComponent(topic)}`;
45
+ const eventSource = new EventSource(sseUrl);
46
+
47
+ eventSource.onopen = () => {
48
+ setIsConnected(true);
49
+ setError(null);
50
+ onOpenRef.current?.();
51
+ };
52
+
53
+ eventSource.onmessage = (e) => {
54
+ try {
55
+ const parsed = JSON.parse(e.data) as T;
56
+ onMessageRef.current(parsed);
57
+ } catch (err) {
58
+ console.error(`[useSubscription] Falha ao parsear evento SSE do tópico "${topic}":`, err);
59
+ }
60
+ };
61
+
62
+ eventSource.onerror = (e) => {
63
+ setIsConnected(false);
64
+ const err = new Error(`Conexão SSE perdida para o tópico "${topic}"`);
65
+ setError(err);
66
+ onErrorRef.current?.(e);
67
+ };
68
+
69
+ return () => {
70
+ eventSource.close();
71
+ setIsConnected(false);
72
+ };
73
+ }, [topic, enabled]);
74
+
75
+ return { isConnected, error };
76
+ }
77
+
78
+ export interface UseActionResult<TPayload, TData, TError> {
79
+ execute: (payload: TPayload) => Promise<Result<TData, TError>>;
80
+ isSubmitting: boolean;
81
+ isLoading: boolean;
82
+ data: TData | null;
83
+ error: TError | null;
84
+ isSuccess: boolean;
85
+ isError: boolean;
86
+ reset: () => void;
87
+ }
88
+
89
+ /**
90
+ * Hook for executing server actions from client UI with reactive state management.
91
+ */
92
+ export function useAction<TPayload = unknown, TData = unknown, TError = string>(
93
+ actionFn?: (payload: TPayload) => Promise<Result<TData, TError>>
94
+ ): UseActionResult<TPayload, TData, TError> {
95
+ const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
96
+ const [data, setData] = useState<TData | null>(null);
97
+ const [error, setError] = useState<TError | null>(null);
98
+ const [isSuccess, setIsSuccess] = useState<boolean>(false);
99
+
100
+ const reset = useCallback(() => {
101
+ setIsSubmitting(false);
102
+ setData(null);
103
+ setError(null);
104
+ setIsSuccess(false);
105
+ }, []);
106
+
107
+ const execute = useCallback(
108
+ async (payload: TPayload): Promise<Result<TData, TError>> => {
109
+ if (!actionFn) {
110
+ const errVal = 'NO_ACTION_FUNCTION' as unknown as TError;
111
+ setError(errVal);
112
+ return { ok: false, error: errVal };
113
+ }
114
+
115
+ setIsSubmitting(true);
116
+ setError(null);
117
+ setIsSuccess(false);
118
+
119
+ try {
120
+ const result = await actionFn(payload);
121
+ if (result.ok) {
122
+ setData(result.value);
123
+ setIsSuccess(true);
124
+ setError(null);
125
+ } else {
126
+ setError(result.error);
127
+ setIsSuccess(false);
128
+ }
129
+ return result;
130
+ } catch (err: unknown) {
131
+ const failure = (err instanceof Error ? err.message : String(err)) as unknown as TError;
132
+ setError(failure);
133
+ setIsSuccess(false);
134
+ return { ok: false, error: failure };
135
+ } finally {
136
+ setIsSubmitting(false);
137
+ }
138
+ },
139
+ [actionFn]
140
+ );
141
+
142
+ return {
143
+ execute,
144
+ isSubmitting,
145
+ isLoading: isSubmitting,
146
+ data,
147
+ error,
148
+ isSuccess,
149
+ isError: error !== null,
150
+ reset
151
+ };
152
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * SynapseJS - Isomorphic i18n Translation Primitive
3
+ *
4
+ * Lightweight, zero-dependency translation helper for client and server components.
5
+ * Guarantees zero server-leakage when used in client artifacts.
6
+ */
7
+
8
+ export type TranslationDictionary = Record<string, Record<string, string>>;
9
+ export type TranslateFn = (key: string, params?: Record<string, string | number>) => string;
10
+
11
+ /**
12
+ * Creates an isomorphic translation function for a given dictionary and locale.
13
+ * Supports interpolation with `{param}` placeholders and graceful fallback.
14
+ *
15
+ * @example
16
+ * const t = createTranslator({
17
+ * 'en': { 'welcome': 'Hello, {name}!' },
18
+ * 'pt-BR': { 'welcome': 'Olá, {name}!' }
19
+ * }, 'pt-BR');
20
+ *
21
+ * t('welcome', { name: 'Maria' }) // 'Olá, Maria!'
22
+ */
23
+ export function createTranslator(
24
+ dictionary: TranslationDictionary,
25
+ currentLocale: string,
26
+ fallbackLocale = 'pt-BR'
27
+ ): TranslateFn {
28
+ const dict = dictionary[currentLocale] ?? dictionary[fallbackLocale] ?? {};
29
+ const fallbackDict = dictionary[fallbackLocale] ?? {};
30
+
31
+ return (key: string, params?: Record<string, string | number>): string => {
32
+ let template = dict[key] ?? fallbackDict[key] ?? key;
33
+ if (params) {
34
+ for (const [paramKey, paramValue] of Object.entries(params)) {
35
+ template = template.replaceAll(`{${paramKey}}`, String(paramValue));
36
+ }
37
+ }
38
+ return template;
39
+ };
40
+ }
package/src/client.ts ADDED
@@ -0,0 +1,66 @@
1
+ /**
2
+ * SynapseJS - Client Entry (browser)
3
+ *
4
+ * What a slice's UI is allowed to import. The package root is the server surface:
5
+ * it reaches SQLite, PostgreSQL and `process.env`, so a browser bundle that pulls it
6
+ * in cannot even be built. This entry exposes exactly the values that are valid in
7
+ * a browser — the RPC transport and the session cookie — and nothing else.
8
+ *
9
+ * The splitter's leak gate enforces this: a value imported from the bare package
10
+ * inside a client artifact fails the build and says so.
11
+ */
12
+
13
+ // Declarative UI Components
14
+ export {
15
+ Badge,
16
+ type BadgeProps,
17
+ Button,
18
+ type ButtonProps,
19
+ Card,
20
+ type CardProps,
21
+ type Column,
22
+ DataForm,
23
+ type DataFormProps,
24
+ DataTable,
25
+ type DataTableProps,
26
+ expandNestedObject,
27
+ type FormField,
28
+ getNestedProperty,
29
+ Pagination,
30
+ type PaginationProps,
31
+ setNestedProperty
32
+ } from './client/components';
33
+ // Context & SSR data isolation
34
+ export {
35
+ type SynapseContextValue,
36
+ SynapseProvider,
37
+ type SynapseProviderProps,
38
+ useLoaderData,
39
+ useSession,
40
+ useSynapseContext
41
+ } from './client/context';
42
+ // Reactive Action & Realtime Subscription Hooks
43
+ export {
44
+ type UseActionResult,
45
+ type UseSubscriptionOptions,
46
+ type UseSubscriptionResult,
47
+ useAction,
48
+ useSubscription
49
+ } from './client/hooks';
50
+ export {
51
+ createTranslator,
52
+ type TranslateFn,
53
+ type TranslationDictionary
54
+ } from './client/i18n';
55
+ export type { Result } from './core/machine-types';
56
+ export { type RpcTransportError, rpcCall, rpcTransportFailure } from './core/rpc-client';
57
+ export type { SessionContext } from './core/session-context';
58
+ export {
59
+ clearSession,
60
+ currentRoles,
61
+ DEFAULT_SESSION_MAX_AGE_SECONDS,
62
+ ROLES_COOKIE,
63
+ SESSION_COOKIE,
64
+ sessionCookie,
65
+ storeSession
66
+ } from './core/session-cookie';
@@ -0,0 +1,113 @@
1
+ /**
2
+ * SynapseJS - Agent Diagnostic JSON Engine
3
+ *
4
+ * Machine-only compiler diagnostic interceptor.
5
+ * Replaces human-targeted ANSI text output with structured JSON coordinates:
6
+ * { status, errorCount, issues: [{ file, line, column, errorCode, message }] }
7
+ * Enables closed-loop deterministic auto-correction by AI agents.
8
+ */
9
+
10
+ import * as path from 'path';
11
+ import * as ts from 'typescript';
12
+
13
+ export interface DiagnosticIssue {
14
+ file: string;
15
+ line: number;
16
+ column: number;
17
+ errorCode: number;
18
+ message: string;
19
+ }
20
+
21
+ export interface DiagnosticReport {
22
+ status: 'PASS' | 'FAIL';
23
+ errorCount: number;
24
+ timestamp: string;
25
+ durationMs: number;
26
+ issues: DiagnosticIssue[];
27
+ code?: string;
28
+ message?: string;
29
+ }
30
+
31
+ export function runMachineVerifications(projectRoot: string, targetFile?: string): DiagnosticReport {
32
+ const startTime = Date.now();
33
+ const tsConfigPath = path.join(projectRoot, 'tsconfig.json');
34
+ const configFile = ts.readConfigFile(tsConfigPath, ts.sys.readFile);
35
+ const parsed = ts.parseJsonConfigFileContent(configFile.config, ts.sys, projectRoot);
36
+
37
+ const fileNames = targetFile
38
+ ? parsed.fileNames.filter((f) => f.endsWith(targetFile) || f.includes(targetFile))
39
+ : parsed.fileNames;
40
+
41
+ // Nothing matched is not a pass: no file was verified.
42
+ if (fileNames.length === 0) {
43
+ return {
44
+ status: 'FAIL',
45
+ errorCount: 1,
46
+ timestamp: new Date().toISOString(),
47
+ durationMs: Date.now() - startTime,
48
+ issues: [],
49
+ code: targetFile ? 'TARGET_FILE_NOT_FOUND' : 'NO_FILES_MATCHED',
50
+ message: targetFile
51
+ ? `Nenhum arquivo do projeto corresponde a '${targetFile}'.`
52
+ : `O tsconfig em '${tsConfigPath}' não inclui nenhum arquivo — nada foi verificado.`
53
+ };
54
+ }
55
+
56
+ const program = ts.createProgram(fileNames, parsed.options);
57
+ const diagnostics = ts.getPreEmitDiagnostics(program);
58
+
59
+ const durationMs = Date.now() - startTime;
60
+
61
+ if (diagnostics.length === 0) {
62
+ return {
63
+ status: 'PASS',
64
+ errorCount: 0,
65
+ timestamp: new Date().toISOString(),
66
+ durationMs,
67
+ issues: []
68
+ };
69
+ }
70
+
71
+ const structuredErrors: DiagnosticIssue[] = diagnostics.map((d) => {
72
+ let file = 'unknown';
73
+ let line = 0;
74
+ let column = 0;
75
+
76
+ if (d.file && d.start !== undefined) {
77
+ const position = d.file.getLineAndCharacterOfPosition(d.start);
78
+ file = path.relative(projectRoot, d.file.fileName);
79
+ line = position.line + 1;
80
+ column = position.character + 1;
81
+ }
82
+
83
+ return {
84
+ file,
85
+ line,
86
+ column,
87
+ errorCode: d.code,
88
+ message: ts.flattenDiagnosticMessageText(d.messageText, '\n')
89
+ };
90
+ });
91
+
92
+ return {
93
+ status: 'FAIL',
94
+ errorCount: structuredErrors.length,
95
+ timestamp: new Date().toISOString(),
96
+ durationMs,
97
+ issues: structuredErrors
98
+ };
99
+ }
100
+
101
+ if (import.meta.main) {
102
+ const target = process.argv[2];
103
+ const report = runMachineVerifications(process.cwd(), target);
104
+
105
+ // Write pure, unadulterated JSON to stdout for agent consumption
106
+ process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
107
+
108
+ if (report.status === 'FAIL') {
109
+ process.exit(1);
110
+ } else {
111
+ process.exit(0);
112
+ }
113
+ }
@@ -0,0 +1,190 @@
1
+ /**
2
+ * SynapseJS - Native AST Daemon & Codebase Compressor
3
+ *
4
+ * Extracts exact structural topology and signatures from slices and core modules.
5
+ * Suppresses implementation bodies to produce a compressed skeleton map (.codebase/repo-map.d.ts)
6
+ * that fits well within a 3,000-token budget for LLM context injection.
7
+ */
8
+
9
+ import * as fs from 'fs';
10
+ import * as path from 'path';
11
+ import * as ts from 'typescript';
12
+ import { generateDatabaseSchemaCatalog } from './db-schema-generator';
13
+
14
+ /**
15
+ * Resolves the runtime contract behind a TypeBox schema declaration.
16
+ *
17
+ * TypeBox carries the inferred shape on the `static` phantom property, which is
18
+ * exactly what `Static<typeof Schema>` exposes — the contract the agent needs.
19
+ */
20
+ function describeSchemaType(checker: ts.TypeChecker, name: ts.Node): string {
21
+ const type = checker.getTypeAtLocation(name);
22
+ const staticProperty = type.getProperty('static');
23
+
24
+ if (staticProperty) {
25
+ const staticType = checker.getTypeOfSymbolAtLocation(staticProperty, name);
26
+ const printable = checker.typeToString(staticType, name, ts.TypeFormatFlags.NoTruncation);
27
+ return printable.length > 400 ? checker.typeToString(staticType, name) : printable;
28
+ }
29
+
30
+ return checker.typeToString(type, name);
31
+ }
32
+
33
+ interface SliceMetadata {
34
+ slicePath: string;
35
+ exportedTypes: string[];
36
+ exportedFunctions: string[];
37
+ hasUI: boolean;
38
+ hasPBT: boolean;
39
+ }
40
+
41
+ function isTestFile(fileName: string): boolean {
42
+ return /\.(test|spec)\.[cm]?tsx?$/.test(fileName);
43
+ }
44
+
45
+ export function compressRepositoryAST(
46
+ baseDir: string,
47
+ outputFile: string,
48
+ graphFile?: string
49
+ ): {
50
+ manifestTokensEstimate: number;
51
+ totalModules: number;
52
+ totalSlices: number;
53
+ } {
54
+ const tsConfigPath = path.join(baseDir, 'tsconfig.json');
55
+ const configFile = ts.readConfigFile(tsConfigPath, ts.sys.readFile);
56
+ const parsed = ts.parseJsonConfigFileContent(configFile.config, ts.sys, baseDir);
57
+
58
+ // Every file the project compiles is mapped, not only `slices/` and `core/`:
59
+ // the tooling must also work on a conventional app that adopts it.
60
+ const sourceFiles = parsed.fileNames.filter((f) => !f.endsWith('.d.ts') && !isTestFile(f));
61
+
62
+ const program = ts.createProgram(sourceFiles, parsed.options);
63
+ const typeChecker = program.getTypeChecker();
64
+
65
+ let manifest = `// [SYNAPSE-JS AUTO-GENERATED SKELETON MAP]\n`;
66
+ manifest += `// STRICT CONTRACTS, ALGEBRAIC TYPES AND FUNCTION SIGNATURES ONLY.\n`;
67
+ manifest += `// Regenerate with 'synapse skeleton' — output is deterministic and committed.\n`;
68
+ manifest += `// NOT COMPILABLE: this is a signature digest for LLM context, not a .d.ts module.\n`;
69
+ manifest += `// It carries no imports and names repeat across modules by design.\n\n`;
70
+
71
+ const architectureGraph: SliceMetadata[] = [];
72
+
73
+ for (const src of program.getSourceFiles()) {
74
+ if (src.isDeclarationFile || !sourceFiles.includes(src.fileName)) {
75
+ continue;
76
+ }
77
+
78
+ const relPath = path.relative(baseDir, src.fileName);
79
+ manifest += `// ============================================================================\n`;
80
+ manifest += `// MODULE: ${relPath}\n`;
81
+ manifest += `// ============================================================================\n`;
82
+
83
+ const sliceMeta: SliceMetadata = {
84
+ slicePath: relPath,
85
+ exportedTypes: [],
86
+ exportedFunctions: [],
87
+ hasUI: false,
88
+ hasPBT: false
89
+ };
90
+
91
+ ts.forEachChild(src, (node) => {
92
+ const modifiers = ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined;
93
+ const isExported = modifiers?.some((m: ts.Modifier) => m.kind === ts.SyntaxKind.ExportKeyword);
94
+
95
+ // 1. Exported Type Aliases & Interfaces (e.g. TypeBox static types, Result types)
96
+ if (isExported && (ts.isTypeAliasDeclaration(node) || ts.isInterfaceDeclaration(node))) {
97
+ manifest += `${node.getText(src)}\n\n`;
98
+ sliceMeta.exportedTypes.push(node.name.text);
99
+ }
100
+
101
+ // 2. Exported Schemas & constants, resolved to their real inferred types
102
+ if (isExported && ts.isVariableStatement(node)) {
103
+ for (const decl of node.declarationList.declarations) {
104
+ const varName = decl.name.getText(src);
105
+ if (varName.endsWith('Schema')) {
106
+ manifest += `export declare const ${varName}: ${describeSchemaType(typeChecker, decl.name)};\n`;
107
+ sliceMeta.exportedTypes.push(varName);
108
+ }
109
+ if (varName === 'sliceTests') {
110
+ sliceMeta.hasPBT = true;
111
+ }
112
+ }
113
+ }
114
+
115
+ // 3. Exported Functions (Skeletonized: signature only, body stripped)
116
+ if (isExported && ts.isFunctionDeclaration(node) && node.name) {
117
+ const fnName = node.name.text;
118
+ const params = node.parameters
119
+ .map((p) => {
120
+ const pName = p.name.getText(src);
121
+ const pType = p.type ? p.type.getText(src) : 'unknown';
122
+ return `${pName}: ${pType}`;
123
+ })
124
+ .join(', ');
125
+
126
+ let retType = 'void';
127
+ if (node.type) {
128
+ retType = node.type.getText(src);
129
+ } else {
130
+ const signature = typeChecker.getSignatureFromDeclaration(node);
131
+ if (signature) {
132
+ retType = typeChecker.typeToString(typeChecker.getReturnTypeOfSignature(signature));
133
+ }
134
+ }
135
+
136
+ manifest += `export declare function ${fnName}(${params}): ${retType};\n`;
137
+ sliceMeta.exportedFunctions.push(fnName);
138
+
139
+ if (fnName.endsWith('Trigger') || fnName.endsWith('View') || fnName.endsWith('Component')) {
140
+ sliceMeta.hasUI = true;
141
+ }
142
+ }
143
+ });
144
+
145
+ manifest += `\n`;
146
+ architectureGraph.push(sliceMeta);
147
+ }
148
+
149
+ // Ensure output directory exists
150
+ const outDir = path.dirname(outputFile);
151
+ if (!fs.existsSync(outDir)) {
152
+ fs.mkdirSync(outDir, { recursive: true });
153
+ }
154
+
155
+ fs.writeFileSync(outputFile, manifest, 'utf-8');
156
+
157
+ // Also generate the database schema catalog if outputting into .codebase
158
+ try {
159
+ const dbSchemaPath = path.join(outDir, 'db-schema.d.ts');
160
+ generateDatabaseSchemaCatalog(baseDir, dbSchemaPath);
161
+ } catch {
162
+ // Non-fatal if project has no slices yet
163
+ }
164
+
165
+ if (graphFile) {
166
+ fs.writeFileSync(graphFile, JSON.stringify(architectureGraph, null, 2), 'utf-8');
167
+ }
168
+
169
+ // Heuristic token estimation: ~4 chars per token
170
+ const tokenEstimate = Math.round(manifest.length / 4);
171
+
172
+ return {
173
+ manifestTokensEstimate: tokenEstimate,
174
+ totalModules: architectureGraph.length,
175
+ totalSlices: architectureGraph.filter((entry) => entry.slicePath.endsWith('.slice.tsx')).length
176
+ };
177
+ }
178
+
179
+ if (import.meta.main) {
180
+ const root = process.cwd();
181
+ const repoMapPath = path.join(root, '.codebase/repo-map.d.ts');
182
+ const graphPath = path.join(root, '.codebase/architecture-graph.json');
183
+
184
+ console.log('⚡ [AST Daemon] Extraindo esqueleto e comprimindo repositório...');
185
+ const stats = compressRepositoryAST(root, repoMapPath, graphPath);
186
+ console.log(`✅ [AST Daemon PASS] Manifesto gerado com sucesso!`);
187
+ console.log(` - Arquivo: .codebase/repo-map.d.ts`);
188
+ console.log(` - Fatias e módulos processados: ${stats.totalSlices}`);
189
+ console.log(` - Estimativa de tokens: ~${stats.manifestTokensEstimate} tokens (limite: 3.000 tokens)`);
190
+ }