ont-run 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -4
- package/dist/bin/ont.js +202 -6
- package/dist/index.js +18 -54
- package/dist/src/browser/server.d.ts +2 -0
- package/dist/src/browser/transform.d.ts +0 -1
- package/dist/src/config/define.d.ts +37 -2
- package/dist/src/config/schema.d.ts +8 -8
- package/dist/src/config/types.d.ts +27 -27
- package/dist/src/index.d.ts +3 -2
- package/dist/src/server/api/index.d.ts +0 -2
- package/dist/src/server/api/router.d.ts +1 -1
- package/dist/src/server/mcp/index.d.ts +0 -2
- package/dist/src/server/mcp/tools.d.ts +2 -2
- package/dist/src/server/resolver.d.ts +4 -15
- package/package.json +1 -1
- package/src/browser/server.ts +203 -2
- package/src/browser/transform.ts +0 -2
- package/src/cli/commands/review.ts +2 -1
- package/src/config/define.ts +49 -1
- package/src/config/schema.ts +1 -1
- package/src/config/types.ts +33 -31
- package/src/index.ts +3 -2
- package/src/server/api/index.ts +2 -15
- package/src/server/api/router.ts +3 -6
- package/src/server/mcp/index.ts +2 -4
- package/src/server/mcp/tools.ts +3 -5
- package/src/server/resolver.ts +5 -78
- package/src/server/start.ts +0 -2
|
@@ -32,10 +32,32 @@ export interface FieldOption {
|
|
|
32
32
|
/** Human-readable label for display */
|
|
33
33
|
label: string;
|
|
34
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Context passed to resolvers
|
|
37
|
+
*/
|
|
38
|
+
export interface ResolverContext {
|
|
39
|
+
/** Current environment name */
|
|
40
|
+
env: string;
|
|
41
|
+
/** Environment configuration */
|
|
42
|
+
envConfig: EnvironmentConfig;
|
|
43
|
+
/** Logger instance */
|
|
44
|
+
logger: {
|
|
45
|
+
info: (message: string, ...args: unknown[]) => void;
|
|
46
|
+
warn: (message: string, ...args: unknown[]) => void;
|
|
47
|
+
error: (message: string, ...args: unknown[]) => void;
|
|
48
|
+
debug: (message: string, ...args: unknown[]) => void;
|
|
49
|
+
};
|
|
50
|
+
/** Access groups for the current request */
|
|
51
|
+
accessGroups: string[];
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Resolver function signature
|
|
55
|
+
*/
|
|
56
|
+
export type ResolverFunction<TArgs = unknown, TResult = unknown> = (ctx: ResolverContext, args: TArgs) => Promise<TResult> | TResult;
|
|
35
57
|
/**
|
|
36
58
|
* Definition of a function in the ontology
|
|
37
59
|
*/
|
|
38
|
-
export interface FunctionDefinition<TGroups extends string = string, TEntities extends string = string
|
|
60
|
+
export interface FunctionDefinition<TGroups extends string = string, TEntities extends string = string, TInputs extends z.ZodType = z.ZodType<unknown>, TOutputs extends z.ZodType = z.ZodType<unknown>> {
|
|
39
61
|
/** Human-readable description of what this function does */
|
|
40
62
|
description: string;
|
|
41
63
|
/** Which access groups can call this function */
|
|
@@ -43,11 +65,11 @@ export interface FunctionDefinition<TGroups extends string = string, TEntities e
|
|
|
43
65
|
/** Which entities this function relates to (use empty array [] if none) */
|
|
44
66
|
entities: TEntities[];
|
|
45
67
|
/** Zod schema for input validation */
|
|
46
|
-
inputs:
|
|
68
|
+
inputs: TInputs;
|
|
47
69
|
/** Zod schema for output validation/documentation */
|
|
48
|
-
outputs?:
|
|
49
|
-
/**
|
|
50
|
-
resolver:
|
|
70
|
+
outputs?: TOutputs;
|
|
71
|
+
/** Resolver function that handles this function's logic */
|
|
72
|
+
resolver: ResolverFunction<z.infer<TInputs>, z.infer<TOutputs>>;
|
|
51
73
|
}
|
|
52
74
|
/**
|
|
53
75
|
* Result returned by the auth function
|
|
@@ -82,25 +104,3 @@ export interface OntologyConfig<TGroups extends string = string, TEntities exten
|
|
|
82
104
|
/** Function definitions */
|
|
83
105
|
functions: TFunctions;
|
|
84
106
|
}
|
|
85
|
-
/**
|
|
86
|
-
* Context passed to resolvers
|
|
87
|
-
*/
|
|
88
|
-
export interface ResolverContext {
|
|
89
|
-
/** Current environment name */
|
|
90
|
-
env: string;
|
|
91
|
-
/** Environment configuration */
|
|
92
|
-
envConfig: EnvironmentConfig;
|
|
93
|
-
/** Logger instance */
|
|
94
|
-
logger: {
|
|
95
|
-
info: (message: string, ...args: unknown[]) => void;
|
|
96
|
-
warn: (message: string, ...args: unknown[]) => void;
|
|
97
|
-
error: (message: string, ...args: unknown[]) => void;
|
|
98
|
-
debug: (message: string, ...args: unknown[]) => void;
|
|
99
|
-
};
|
|
100
|
-
/** Access groups for the current request */
|
|
101
|
-
accessGroups: string[];
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Resolver function signature
|
|
105
|
-
*/
|
|
106
|
-
export type ResolverFunction<TArgs = unknown, TResult = unknown> = (ctx: ResolverContext, args: TArgs) => Promise<TResult> | TResult;
|
package/dist/src/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* ```ts
|
|
6
6
|
* import { defineOntology, fieldFrom } from 'ont-run';
|
|
7
7
|
* import { z } from 'zod';
|
|
8
|
+
* import { hello } from './resolvers/hello.js';
|
|
8
9
|
*
|
|
9
10
|
* export default defineOntology({
|
|
10
11
|
* name: 'my-api',
|
|
@@ -23,13 +24,13 @@
|
|
|
23
24
|
* access: ['public'],
|
|
24
25
|
* entities: [],
|
|
25
26
|
* inputs: z.object({ name: z.string() }),
|
|
26
|
-
* resolver:
|
|
27
|
+
* resolver: hello, // Direct function reference for type safety
|
|
27
28
|
* },
|
|
28
29
|
* },
|
|
29
30
|
* });
|
|
30
31
|
* ```
|
|
31
32
|
*/
|
|
32
|
-
export { defineOntology } from "./config/define.js";
|
|
33
|
+
export { defineOntology, defineFunction } from "./config/define.js";
|
|
33
34
|
export { fieldFrom, userContext } from "./config/categorical.js";
|
|
34
35
|
export { startOnt } from "./server/start.js";
|
|
35
36
|
export type { StartOntOptions, StartOntResult } from "./server/start.js";
|
|
@@ -4,8 +4,6 @@ import { type OntologyVariables } from "./middleware.js";
|
|
|
4
4
|
export interface ApiServerOptions {
|
|
5
5
|
/** The ontology configuration */
|
|
6
6
|
config: OntologyConfig;
|
|
7
|
-
/** Directory containing the ontology.config.ts (for resolving resolver paths) */
|
|
8
|
-
configDir: string;
|
|
9
7
|
/** Environment to use (e.g., 'dev', 'prod') */
|
|
10
8
|
env: string;
|
|
11
9
|
/** Enable CORS (default: true) */
|
|
@@ -4,7 +4,7 @@ import { type OntologyVariables } from "./middleware.js";
|
|
|
4
4
|
/**
|
|
5
5
|
* Create API routes from function definitions
|
|
6
6
|
*/
|
|
7
|
-
export declare function createApiRoutes(config: OntologyConfig
|
|
7
|
+
export declare function createApiRoutes(config: OntologyConfig): Hono<{
|
|
8
8
|
Variables: OntologyVariables;
|
|
9
9
|
}>;
|
|
10
10
|
/**
|
|
@@ -3,8 +3,6 @@ import type { OntologyConfig } from "../../config/types.js";
|
|
|
3
3
|
export interface McpServerOptions {
|
|
4
4
|
/** The ontology configuration */
|
|
5
5
|
config: OntologyConfig;
|
|
6
|
-
/** Directory containing the ontology.config.ts */
|
|
7
|
-
configDir: string;
|
|
8
6
|
/** Environment to use */
|
|
9
7
|
env: string;
|
|
10
8
|
/** Port for the MCP HTTP server */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { OntologyConfig, EnvironmentConfig, AuthResult } from "../../config/types.js";
|
|
2
|
-
import {
|
|
2
|
+
import type { Logger } from "../resolver.js";
|
|
3
3
|
/**
|
|
4
4
|
* Field reference info for MCP tools
|
|
5
5
|
*/
|
|
@@ -32,4 +32,4 @@ export declare function filterToolsByAccess(tools: McpTool[], accessGroups: stri
|
|
|
32
32
|
/**
|
|
33
33
|
* Create a tool executor function that accepts per-request auth result
|
|
34
34
|
*/
|
|
35
|
-
export declare function createToolExecutor(config: OntologyConfig,
|
|
35
|
+
export declare function createToolExecutor(config: OntologyConfig, env: string, envConfig: EnvironmentConfig, logger: Logger): (toolName: string, args: unknown, authResult: AuthResult) => Promise<unknown>;
|
|
@@ -1,20 +1,9 @@
|
|
|
1
|
-
import type { ResolverFunction
|
|
1
|
+
import type { ResolverFunction } from "../config/types.js";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* @param resolverPath - Path to the resolver file (relative to configDir)
|
|
7
|
-
* @param configDir - Directory containing the ontology.config.ts
|
|
3
|
+
* Get a resolver function. Since resolvers are now passed directly as functions,
|
|
4
|
+
* this is a simple passthrough that could be removed in the future.
|
|
8
5
|
*/
|
|
9
|
-
export declare function loadResolver(
|
|
10
|
-
/**
|
|
11
|
-
* Clear the resolver cache (useful for hot reloading)
|
|
12
|
-
*/
|
|
13
|
-
export declare function clearResolverCache(): void;
|
|
14
|
-
/**
|
|
15
|
-
* Check which resolvers are missing and return their paths
|
|
16
|
-
*/
|
|
17
|
-
export declare function findMissingResolvers(config: OntologyConfig, configDir: string): string[];
|
|
6
|
+
export declare function loadResolver(resolver: ResolverFunction): ResolverFunction;
|
|
18
7
|
/**
|
|
19
8
|
* Logger type returned by createLogger
|
|
20
9
|
*/
|
package/package.json
CHANGED
package/src/browser/server.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
|
+
import { readFileSync } from "fs";
|
|
3
|
+
import { basename } from "path";
|
|
2
4
|
import open from "open";
|
|
3
5
|
import type { OntologyConfig } from "../config/types.js";
|
|
4
6
|
import type { OntologyDiff } from "../lockfile/types.js";
|
|
@@ -12,6 +14,8 @@ export interface BrowserServerOptions {
|
|
|
12
14
|
diff?: OntologyDiff | null;
|
|
13
15
|
/** Directory to write the lockfile to on approval */
|
|
14
16
|
configDir?: string;
|
|
17
|
+
/** Path to the ontology.config.ts file */
|
|
18
|
+
configPath?: string;
|
|
15
19
|
port?: number;
|
|
16
20
|
openBrowser?: boolean;
|
|
17
21
|
}
|
|
@@ -22,7 +26,7 @@ export interface BrowserServerResult {
|
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
export async function startBrowserServer(options: BrowserServerOptions): Promise<BrowserServerResult> {
|
|
25
|
-
const { config, diff = null, configDir, port: preferredPort, openBrowser = true } = options;
|
|
29
|
+
const { config, diff = null, configDir, configPath, port: preferredPort, openBrowser = true } = options;
|
|
26
30
|
|
|
27
31
|
// Transform config to graph data and enhance with diff info
|
|
28
32
|
const baseGraphData = transformToGraphData(config);
|
|
@@ -90,6 +94,26 @@ export async function startBrowserServer(options: BrowserServerOptions): Promise
|
|
|
90
94
|
return c.json({ success: true });
|
|
91
95
|
});
|
|
92
96
|
|
|
97
|
+
// API: Get raw TypeScript source
|
|
98
|
+
app.get("/api/source", (c) => {
|
|
99
|
+
if (!configPath) {
|
|
100
|
+
return c.json({ error: "Config path not available" }, 400);
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
const source = readFileSync(configPath, "utf-8");
|
|
104
|
+
const filename = basename(configPath);
|
|
105
|
+
return c.json({ source, filename, path: configPath });
|
|
106
|
+
} catch (error) {
|
|
107
|
+
return c.json(
|
|
108
|
+
{
|
|
109
|
+
error: "Failed to read config file",
|
|
110
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
111
|
+
},
|
|
112
|
+
500
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
93
117
|
// Serve UI
|
|
94
118
|
app.get("/", (c) => c.html(generateBrowserUI(graphData)));
|
|
95
119
|
|
|
@@ -705,6 +729,89 @@ function generateBrowserUI(graphData: EnhancedGraphData): string {
|
|
|
705
729
|
color: var(--change-added);
|
|
706
730
|
}
|
|
707
731
|
|
|
732
|
+
/* Source View */
|
|
733
|
+
.source-view {
|
|
734
|
+
display: none;
|
|
735
|
+
grid-column: 2 / 4;
|
|
736
|
+
padding: 24px;
|
|
737
|
+
overflow-y: auto;
|
|
738
|
+
background: linear-gradient(to bottom, rgba(255, 255, 255, 0.5), rgba(231, 225, 207, 0.3));
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
.source-view.active {
|
|
742
|
+
display: flex;
|
|
743
|
+
flex-direction: column;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
.source-header {
|
|
747
|
+
display: flex;
|
|
748
|
+
align-items: center;
|
|
749
|
+
justify-content: space-between;
|
|
750
|
+
padding: 12px 20px;
|
|
751
|
+
background: rgba(2, 61, 96, 0.95);
|
|
752
|
+
border-radius: 12px 12px 0 0;
|
|
753
|
+
color: white;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
.source-filename {
|
|
757
|
+
font-family: 'Space Mono', monospace;
|
|
758
|
+
font-size: 13px;
|
|
759
|
+
font-weight: 500;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
.copy-btn {
|
|
763
|
+
display: flex;
|
|
764
|
+
align-items: center;
|
|
765
|
+
gap: 6px;
|
|
766
|
+
padding: 6px 12px;
|
|
767
|
+
background: rgba(255, 255, 255, 0.1);
|
|
768
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
769
|
+
border-radius: 6px;
|
|
770
|
+
color: white;
|
|
771
|
+
font-family: 'Space Grotesk', sans-serif;
|
|
772
|
+
font-size: 12px;
|
|
773
|
+
cursor: pointer;
|
|
774
|
+
transition: all 0.2s ease;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
.copy-btn:hover {
|
|
778
|
+
background: rgba(255, 255, 255, 0.2);
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
.copy-btn.copied {
|
|
782
|
+
background: rgba(21, 168, 168, 0.3);
|
|
783
|
+
border-color: var(--vanna-teal);
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
.source-code {
|
|
787
|
+
flex: 1;
|
|
788
|
+
margin: 0;
|
|
789
|
+
padding: 20px;
|
|
790
|
+
background: #1e1e1e;
|
|
791
|
+
border-radius: 0 0 12px 12px;
|
|
792
|
+
overflow: auto;
|
|
793
|
+
font-family: 'Space Mono', monospace;
|
|
794
|
+
font-size: 13px;
|
|
795
|
+
line-height: 1.6;
|
|
796
|
+
color: #d4d4d4;
|
|
797
|
+
tab-size: 2;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
.source-code code {
|
|
801
|
+
display: block;
|
|
802
|
+
white-space: pre;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
/* Syntax highlighting classes */
|
|
806
|
+
.source-code .keyword { color: #569cd6; }
|
|
807
|
+
.source-code .string { color: #ce9178; }
|
|
808
|
+
.source-code .number { color: #b5cea8; }
|
|
809
|
+
.source-code .comment { color: #6a9955; }
|
|
810
|
+
.source-code .function { color: #dcdcaa; }
|
|
811
|
+
.source-code .type { color: #4ec9b0; }
|
|
812
|
+
.source-code .property { color: #9cdcfe; }
|
|
813
|
+
.source-code .punctuation { color: #d4d4d4; }
|
|
814
|
+
|
|
708
815
|
/* No Changes State */
|
|
709
816
|
.no-changes {
|
|
710
817
|
text-align: center;
|
|
@@ -1433,6 +1540,7 @@ function generateBrowserUI(graphData: EnhancedGraphData): string {
|
|
|
1433
1540
|
<div class="view-tabs">
|
|
1434
1541
|
<button class="view-tab active" data-view="graph">Graph</button>
|
|
1435
1542
|
<button class="view-tab" data-view="table">Table</button>
|
|
1543
|
+
<button class="view-tab" data-view="source">Source</button>
|
|
1436
1544
|
</div>
|
|
1437
1545
|
|
|
1438
1546
|
<div class="filter-buttons" id="graphFilters">
|
|
@@ -1553,6 +1661,20 @@ function generateBrowserUI(graphData: EnhancedGraphData): string {
|
|
|
1553
1661
|
<div class="table-view" id="tableView">
|
|
1554
1662
|
<div id="tableContent"></div>
|
|
1555
1663
|
</div>
|
|
1664
|
+
|
|
1665
|
+
<!-- Source View -->
|
|
1666
|
+
<div class="source-view" id="sourceView">
|
|
1667
|
+
<div class="source-header">
|
|
1668
|
+
<span class="source-filename" id="sourceFilename">ontology.config.ts</span>
|
|
1669
|
+
<button class="copy-btn" id="copySourceBtn" title="Copy to clipboard">
|
|
1670
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1671
|
+
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
|
|
1672
|
+
</svg>
|
|
1673
|
+
Copy
|
|
1674
|
+
</button>
|
|
1675
|
+
</div>
|
|
1676
|
+
<pre class="source-code" id="sourceCode"><code>Loading...</code></pre>
|
|
1677
|
+
</div>
|
|
1556
1678
|
</div>
|
|
1557
1679
|
|
|
1558
1680
|
<!-- Review Footer -->
|
|
@@ -2429,6 +2551,7 @@ function generateBrowserUI(graphData: EnhancedGraphData): string {
|
|
|
2429
2551
|
const graphContainer = document.querySelector('.graph-container');
|
|
2430
2552
|
const detailPanel = document.getElementById('detailPanel');
|
|
2431
2553
|
const tableView = document.getElementById('tableView');
|
|
2554
|
+
const sourceView = document.getElementById('sourceView');
|
|
2432
2555
|
const graphFilters = document.getElementById('graphFilters');
|
|
2433
2556
|
const layoutSelector = document.querySelector('.layout-selector');
|
|
2434
2557
|
|
|
@@ -2436,15 +2559,25 @@ function generateBrowserUI(graphData: EnhancedGraphData): string {
|
|
|
2436
2559
|
graphContainer.style.display = 'block';
|
|
2437
2560
|
detailPanel.style.display = 'block';
|
|
2438
2561
|
tableView.classList.remove('active');
|
|
2562
|
+
sourceView.classList.remove('active');
|
|
2439
2563
|
if (graphFilters) graphFilters.style.display = 'flex';
|
|
2440
2564
|
if (layoutSelector) layoutSelector.style.display = 'flex';
|
|
2441
|
-
} else {
|
|
2565
|
+
} else if (view === 'table') {
|
|
2442
2566
|
graphContainer.style.display = 'none';
|
|
2443
2567
|
detailPanel.style.display = 'none';
|
|
2444
2568
|
tableView.classList.add('active');
|
|
2569
|
+
sourceView.classList.remove('active');
|
|
2445
2570
|
if (graphFilters) graphFilters.style.display = 'none';
|
|
2446
2571
|
if (layoutSelector) layoutSelector.style.display = 'none';
|
|
2447
2572
|
renderTableView();
|
|
2573
|
+
} else if (view === 'source') {
|
|
2574
|
+
graphContainer.style.display = 'none';
|
|
2575
|
+
detailPanel.style.display = 'none';
|
|
2576
|
+
tableView.classList.remove('active');
|
|
2577
|
+
sourceView.classList.add('active');
|
|
2578
|
+
if (graphFilters) graphFilters.style.display = 'none';
|
|
2579
|
+
if (layoutSelector) layoutSelector.style.display = 'none';
|
|
2580
|
+
loadSourceView();
|
|
2448
2581
|
}
|
|
2449
2582
|
}
|
|
2450
2583
|
|
|
@@ -2486,6 +2619,74 @@ function generateBrowserUI(graphData: EnhancedGraphData): string {
|
|
|
2486
2619
|
});
|
|
2487
2620
|
}
|
|
2488
2621
|
|
|
2622
|
+
// Source view
|
|
2623
|
+
let sourceLoaded = false;
|
|
2624
|
+
let sourceContent = '';
|
|
2625
|
+
|
|
2626
|
+
async function loadSourceView() {
|
|
2627
|
+
if (sourceLoaded) return;
|
|
2628
|
+
|
|
2629
|
+
const codeEl = document.getElementById('sourceCode').querySelector('code');
|
|
2630
|
+
const filenameEl = document.getElementById('sourceFilename');
|
|
2631
|
+
|
|
2632
|
+
try {
|
|
2633
|
+
const res = await fetch('/api/source');
|
|
2634
|
+
if (!res.ok) throw new Error('Failed to load source');
|
|
2635
|
+
const data = await res.json();
|
|
2636
|
+
|
|
2637
|
+
sourceContent = data.source;
|
|
2638
|
+
filenameEl.textContent = data.filename;
|
|
2639
|
+
codeEl.innerHTML = highlightTypeScript(data.source);
|
|
2640
|
+
sourceLoaded = true;
|
|
2641
|
+
} catch (err) {
|
|
2642
|
+
codeEl.textContent = 'Error loading source: ' + err.message;
|
|
2643
|
+
}
|
|
2644
|
+
}
|
|
2645
|
+
|
|
2646
|
+
function highlightTypeScript(code) {
|
|
2647
|
+
// Escape HTML first
|
|
2648
|
+
code = code.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
2649
|
+
|
|
2650
|
+
// Comments (single and multi-line)
|
|
2651
|
+
code = code.replace(/(\\/\\/.*$)/gm, '<span class="comment">$1</span>');
|
|
2652
|
+
code = code.replace(/(\\/\\*[\\s\\S]*?\\*\\/)/g, '<span class="comment">$1</span>');
|
|
2653
|
+
|
|
2654
|
+
// Strings (double, single, and template)
|
|
2655
|
+
code = code.replace(/("(?:[^"\\\\]|\\\\.)*")/g, '<span class="string">$1</span>');
|
|
2656
|
+
code = code.replace(/('(?:[^'\\\\]|\\\\.)*')/g, '<span class="string">$1</span>');
|
|
2657
|
+
code = code.replace(/(\`(?:[^\`\\\\]|\\\\.)*\`)/g, '<span class="string">$1</span>');
|
|
2658
|
+
|
|
2659
|
+
// Keywords
|
|
2660
|
+
const keywords = ['import', 'export', 'from', 'const', 'let', 'var', 'function', 'return', 'if', 'else', 'for', 'while', 'class', 'extends', 'new', 'this', 'true', 'false', 'null', 'undefined', 'typeof', 'instanceof', 'async', 'await', 'default', 'as', 'type', 'interface'];
|
|
2661
|
+
keywords.forEach(kw => {
|
|
2662
|
+
code = code.replace(new RegExp('\\\\b(' + kw + ')\\\\b', 'g'), '<span class="keyword">$1</span>');
|
|
2663
|
+
});
|
|
2664
|
+
|
|
2665
|
+
// Numbers
|
|
2666
|
+
code = code.replace(/\\b(\\d+\\.?\\d*)\\b/g, '<span class="number">$1</span>');
|
|
2667
|
+
|
|
2668
|
+
// Function calls
|
|
2669
|
+
code = code.replace(/\\b([a-zA-Z_][a-zA-Z0-9_]*)\\s*\\(/g, '<span class="function">$1</span>(');
|
|
2670
|
+
|
|
2671
|
+
return code;
|
|
2672
|
+
}
|
|
2673
|
+
|
|
2674
|
+
// Copy source button
|
|
2675
|
+
document.getElementById('copySourceBtn').addEventListener('click', async () => {
|
|
2676
|
+
const btn = document.getElementById('copySourceBtn');
|
|
2677
|
+
try {
|
|
2678
|
+
await navigator.clipboard.writeText(sourceContent);
|
|
2679
|
+
btn.classList.add('copied');
|
|
2680
|
+
btn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg> Copied!';
|
|
2681
|
+
setTimeout(() => {
|
|
2682
|
+
btn.classList.remove('copied');
|
|
2683
|
+
btn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg> Copy';
|
|
2684
|
+
}, 2000);
|
|
2685
|
+
} catch (err) {
|
|
2686
|
+
console.error('Failed to copy:', err);
|
|
2687
|
+
}
|
|
2688
|
+
});
|
|
2689
|
+
|
|
2489
2690
|
function renderTableSection(title, items, type) {
|
|
2490
2691
|
const changedCount = items.filter(n => n.changeStatus !== 'unchanged').length;
|
|
2491
2692
|
|
package/src/browser/transform.ts
CHANGED
|
@@ -16,7 +16,6 @@ export interface GraphNode {
|
|
|
16
16
|
metadata: {
|
|
17
17
|
inputs?: Record<string, unknown>;
|
|
18
18
|
outputs?: Record<string, unknown>;
|
|
19
|
-
resolver?: string;
|
|
20
19
|
functionCount?: number;
|
|
21
20
|
usesUserContext?: boolean;
|
|
22
21
|
};
|
|
@@ -173,7 +172,6 @@ export function transformToGraphData(config: OntologyConfig): GraphData {
|
|
|
173
172
|
metadata: {
|
|
174
173
|
inputs: safeZodToJsonSchema(fn.inputs),
|
|
175
174
|
outputs: fn.outputs ? safeZodToJsonSchema(fn.outputs) : undefined,
|
|
176
|
-
resolver: fn.resolver,
|
|
177
175
|
usesUserContext: usesUserContext || undefined,
|
|
178
176
|
},
|
|
179
177
|
});
|
|
@@ -48,7 +48,7 @@ export const reviewCommand = defineCommand({
|
|
|
48
48
|
|
|
49
49
|
// Load config
|
|
50
50
|
consola.info("Loading ontology config...");
|
|
51
|
-
const { config, configDir } = await loadConfig();
|
|
51
|
+
const { config, configDir, configPath } = await loadConfig();
|
|
52
52
|
|
|
53
53
|
// Compute current ontology snapshot
|
|
54
54
|
const { ontology: newOntology, hash: newHash } = computeOntologyHash(config);
|
|
@@ -107,6 +107,7 @@ export const reviewCommand = defineCommand({
|
|
|
107
107
|
config,
|
|
108
108
|
diff: diff.hasChanges ? diff : null,
|
|
109
109
|
configDir,
|
|
110
|
+
configPath,
|
|
110
111
|
});
|
|
111
112
|
|
|
112
113
|
if (diff.hasChanges) {
|
package/src/config/define.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
1
2
|
import {
|
|
2
3
|
OntologyConfigSchema,
|
|
3
4
|
validateAccessGroups,
|
|
@@ -11,6 +12,7 @@ import type {
|
|
|
11
12
|
EnvironmentConfig,
|
|
12
13
|
EntityDefinition,
|
|
13
14
|
AuthFunction,
|
|
15
|
+
ResolverFunction,
|
|
14
16
|
} from "./types.js";
|
|
15
17
|
|
|
16
18
|
/**
|
|
@@ -20,6 +22,7 @@ import type {
|
|
|
20
22
|
* ```ts
|
|
21
23
|
* import { defineOntology, fieldFrom } from 'ont-run';
|
|
22
24
|
* import { z } from 'zod';
|
|
25
|
+
* import { getUser } from './resolvers/getUser.js';
|
|
23
26
|
*
|
|
24
27
|
* export default defineOntology({
|
|
25
28
|
* name: 'my-api',
|
|
@@ -44,7 +47,7 @@ import type {
|
|
|
44
47
|
* access: ['public', 'admin'],
|
|
45
48
|
* entities: ['User'],
|
|
46
49
|
* inputs: z.object({ id: z.string() }),
|
|
47
|
-
* resolver:
|
|
50
|
+
* resolver: getUser, // Direct function reference for type safety
|
|
48
51
|
* },
|
|
49
52
|
* },
|
|
50
53
|
* });
|
|
@@ -76,3 +79,48 @@ export function defineOntology<
|
|
|
76
79
|
|
|
77
80
|
return config as OntologyConfig<TGroups, TEntities, TFunctions>;
|
|
78
81
|
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Define a function with full type inference for resolver type safety.
|
|
85
|
+
*
|
|
86
|
+
* This helper ensures that the resolver function's return type matches
|
|
87
|
+
* the outputs Zod schema at compile time.
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```ts
|
|
91
|
+
* import { defineFunction, z } from 'ont-run';
|
|
92
|
+
* import type { ResolverContext } from 'ont-run';
|
|
93
|
+
*
|
|
94
|
+
* const getUser = defineFunction({
|
|
95
|
+
* description: 'Get a user by ID',
|
|
96
|
+
* access: ['public', 'admin'] as const,
|
|
97
|
+
* entities: ['User'] as const,
|
|
98
|
+
* inputs: z.object({ id: z.string() }),
|
|
99
|
+
* outputs: z.object({ id: z.string(), name: z.string() }),
|
|
100
|
+
* resolver: async (ctx, args) => {
|
|
101
|
+
* // TypeScript knows args is { id: string }
|
|
102
|
+
* // TypeScript enforces return type is { id: string, name: string }
|
|
103
|
+
* return { id: args.id, name: 'Example User' };
|
|
104
|
+
* },
|
|
105
|
+
* });
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
export function defineFunction<
|
|
109
|
+
TGroups extends string,
|
|
110
|
+
TEntities extends string,
|
|
111
|
+
TInputs extends z.ZodType,
|
|
112
|
+
TOutputs extends z.ZodType,
|
|
113
|
+
>(config: {
|
|
114
|
+
description: string;
|
|
115
|
+
access: readonly TGroups[];
|
|
116
|
+
entities: readonly TEntities[];
|
|
117
|
+
inputs: TInputs;
|
|
118
|
+
outputs?: TOutputs;
|
|
119
|
+
resolver: ResolverFunction<z.infer<TInputs>, z.infer<TOutputs>>;
|
|
120
|
+
}): FunctionDefinition<TGroups, TEntities, TInputs, TOutputs> {
|
|
121
|
+
return {
|
|
122
|
+
...config,
|
|
123
|
+
access: [...config.access],
|
|
124
|
+
entities: [...config.entities],
|
|
125
|
+
};
|
|
126
|
+
}
|
package/src/config/schema.ts
CHANGED
package/src/config/types.ts
CHANGED
|
@@ -37,12 +37,41 @@ export interface FieldOption {
|
|
|
37
37
|
label: string;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Context passed to resolvers
|
|
42
|
+
*/
|
|
43
|
+
export interface ResolverContext {
|
|
44
|
+
/** Current environment name */
|
|
45
|
+
env: string;
|
|
46
|
+
/** Environment configuration */
|
|
47
|
+
envConfig: EnvironmentConfig;
|
|
48
|
+
/** Logger instance */
|
|
49
|
+
logger: {
|
|
50
|
+
info: (message: string, ...args: unknown[]) => void;
|
|
51
|
+
warn: (message: string, ...args: unknown[]) => void;
|
|
52
|
+
error: (message: string, ...args: unknown[]) => void;
|
|
53
|
+
debug: (message: string, ...args: unknown[]) => void;
|
|
54
|
+
};
|
|
55
|
+
/** Access groups for the current request */
|
|
56
|
+
accessGroups: string[];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Resolver function signature
|
|
61
|
+
*/
|
|
62
|
+
export type ResolverFunction<TArgs = unknown, TResult = unknown> = (
|
|
63
|
+
ctx: ResolverContext,
|
|
64
|
+
args: TArgs
|
|
65
|
+
) => Promise<TResult> | TResult;
|
|
66
|
+
|
|
40
67
|
/**
|
|
41
68
|
* Definition of a function in the ontology
|
|
42
69
|
*/
|
|
43
70
|
export interface FunctionDefinition<
|
|
44
71
|
TGroups extends string = string,
|
|
45
72
|
TEntities extends string = string,
|
|
73
|
+
TInputs extends z.ZodType = z.ZodType<unknown>,
|
|
74
|
+
TOutputs extends z.ZodType = z.ZodType<unknown>,
|
|
46
75
|
> {
|
|
47
76
|
/** Human-readable description of what this function does */
|
|
48
77
|
description: string;
|
|
@@ -51,11 +80,11 @@ export interface FunctionDefinition<
|
|
|
51
80
|
/** Which entities this function relates to (use empty array [] if none) */
|
|
52
81
|
entities: TEntities[];
|
|
53
82
|
/** Zod schema for input validation */
|
|
54
|
-
inputs:
|
|
83
|
+
inputs: TInputs;
|
|
55
84
|
/** Zod schema for output validation/documentation */
|
|
56
|
-
outputs?:
|
|
57
|
-
/**
|
|
58
|
-
resolver:
|
|
85
|
+
outputs?: TOutputs;
|
|
86
|
+
/** Resolver function that handles this function's logic */
|
|
87
|
+
resolver: ResolverFunction<z.infer<TInputs>, z.infer<TOutputs>>;
|
|
59
88
|
}
|
|
60
89
|
|
|
61
90
|
/**
|
|
@@ -105,30 +134,3 @@ export interface OntologyConfig<
|
|
|
105
134
|
/** Function definitions */
|
|
106
135
|
functions: TFunctions;
|
|
107
136
|
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Context passed to resolvers
|
|
111
|
-
*/
|
|
112
|
-
export interface ResolverContext {
|
|
113
|
-
/** Current environment name */
|
|
114
|
-
env: string;
|
|
115
|
-
/** Environment configuration */
|
|
116
|
-
envConfig: EnvironmentConfig;
|
|
117
|
-
/** Logger instance */
|
|
118
|
-
logger: {
|
|
119
|
-
info: (message: string, ...args: unknown[]) => void;
|
|
120
|
-
warn: (message: string, ...args: unknown[]) => void;
|
|
121
|
-
error: (message: string, ...args: unknown[]) => void;
|
|
122
|
-
debug: (message: string, ...args: unknown[]) => void;
|
|
123
|
-
};
|
|
124
|
-
/** Access groups for the current request */
|
|
125
|
-
accessGroups: string[];
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Resolver function signature
|
|
130
|
-
*/
|
|
131
|
-
export type ResolverFunction<TArgs = unknown, TResult = unknown> = (
|
|
132
|
-
ctx: ResolverContext,
|
|
133
|
-
args: TArgs
|
|
134
|
-
) => Promise<TResult> | TResult;
|