@xdelivered/emberflow 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/server/index.js +23 -2
- package/dist/server/nodesPayload.d.ts +14 -1
- package/dist/server/nodesPayload.js +15 -2
- package/dist/server/projectMode.js +5 -2
- package/dist/server/sourceNav.d.ts +77 -0
- package/dist/server/sourceNav.js +503 -0
- package/dist/src/engine/registry.d.ts +27 -2
- package/dist/src/engine/registry.js +84 -2
- package/dist/src/nodes/index.d.ts +8 -2
- package/dist/src/nodes/index.js +7 -3
- package/dist/src/nodes/login.d.ts +3 -1
- package/dist/src/nodes/login.js +2 -2
- package/package.json +2 -2
- package/server/index.ts +24 -2
- package/server/nodesPayload.test.ts +38 -0
- package/server/nodesPayload.ts +28 -3
- package/server/projectMode.ts +5 -2
- package/server/sourceNav.test.ts +382 -0
- package/server/sourceNav.ts +644 -0
- package/server/sourceNavRoute.test.ts +163 -0
- package/src/components/Inspector.tsx +9 -26
- package/src/components/SourceNavigator.test.tsx +226 -0
- package/src/components/SourceNavigator.tsx +520 -0
- package/src/engine/registry.sourceRef.test.ts +112 -0
- package/src/engine/registry.ts +103 -2
- package/src/nodes/index.ts +10 -3
- package/src/nodes/login.ts +5 -2
- package/src/store/builderStore.ts +2 -2
- package/src/store/nodeMeta.ts +10 -2
- package/src/store/sourceNavClient.ts +48 -0
- package/studio-dist/assets/{index-CGwEx82J.js → index-BbzppDpt.js} +25 -25
- package/studio-dist/assets/{index-CAIDjNhv.css → index-CLf6blcA.css} +1 -1
- package/studio-dist/index.html +2 -2
- package/templates/skills/emberflow-basics/SKILL.md +29 -1
- package/templates/skills/emberflow-model-process/SKILL.md +11 -1
- package/templates/skills/emberflow-new-workflow/SKILL.md +8 -1
- package/templates/skills/emberflow-review-workflow/SKILL.md +28 -1
package/dist/server/index.js
CHANGED
|
@@ -16,6 +16,7 @@ import { loadInfrastructure } from './infrastructure.js';
|
|
|
16
16
|
import { configPathFor, loadProjectConfig } from './projectConfig.js';
|
|
17
17
|
import { buildApiStore, buildRegistries, requireProjectWhenExplicit } from './projectMode.js';
|
|
18
18
|
import { nodesPayload } from './nodesPayload.js';
|
|
19
|
+
import { getSourceFile } from './sourceNav.js';
|
|
19
20
|
import { openBrowser } from './openBrowser.js';
|
|
20
21
|
import { AgentRunManager, AgentStartError } from './agents/runManager.js';
|
|
21
22
|
import { isGitRepo } from './agents/gitScope.js';
|
|
@@ -127,7 +128,7 @@ if (!project) {
|
|
|
127
128
|
apiStore.save(flow, flow.id.includes('/') ? flow.id : `default/${flow.id}`);
|
|
128
129
|
}
|
|
129
130
|
}
|
|
130
|
-
const agentRuns = new AgentRunManager(project ? project.root : projectDir, apiStore.dir, apiStore.pathOf.bind(apiStore), () => nodesPayload(validationRegistry).nodes.map(({ type, label, description }) => ({ type, label, description })), project?.language ?? 'typescript',
|
|
131
|
+
const agentRuns = new AgentRunManager(project ? project.root : projectDir, apiStore.dir, apiStore.pathOf.bind(apiStore), () => nodesPayload(validationRegistry, project ? project.root : process.cwd()).nodes.map(({ type, label, description }) => ({ type, label, description })), project?.language ?? 'typescript',
|
|
131
132
|
// Fresh per run: re-read emberflow/infrastructure.json so a scout that ran
|
|
132
133
|
// earlier this session primes later prompts. Malformed → null (agent guesses).
|
|
133
134
|
() => loadInfrastructure(project ? project.root : projectDir),
|
|
@@ -212,7 +213,27 @@ api.post('/serving', (req, res) => {
|
|
|
212
213
|
res.status(204).end();
|
|
213
214
|
});
|
|
214
215
|
api.get('/nodes', (_req, res) => {
|
|
215
|
-
res.json(nodesPayload(validationRegistry));
|
|
216
|
+
res.json(nodesPayload(validationRegistry, project ? project.root : process.cwd()));
|
|
217
|
+
});
|
|
218
|
+
// Whole-file source view + symbol table for the studio's source navigator.
|
|
219
|
+
// Works in project mode AND no-project mode (root = cwd) — path guards in
|
|
220
|
+
// sourceNav (isPathWithin, node_modules, secret basenames) bound what is
|
|
221
|
+
// servable either way. 400s stay generic: the requested path is never echoed.
|
|
222
|
+
api.get('/source-file', (req, res) => {
|
|
223
|
+
void (async () => {
|
|
224
|
+
const path = req.query.path;
|
|
225
|
+
if (typeof path !== 'string' || path.length === 0) {
|
|
226
|
+
res.status(400).json({ error: 'Query param path is required' });
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const root = project ? project.root : process.cwd();
|
|
230
|
+
const result = await getSourceFile(root, path);
|
|
231
|
+
if (!result.ok) {
|
|
232
|
+
res.status(result.status).json({ error: result.error });
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
res.json(result.payload);
|
|
236
|
+
})();
|
|
216
237
|
});
|
|
217
238
|
// Validate a flow against the runner's LIVE registry (built-ins + project
|
|
218
239
|
// nodes) so the CLI `validate` command doesn't false-reject ops that use
|
|
@@ -5,10 +5,23 @@ import type { NodeDefinition, NodeRegistry } from '../src/engine/index.js';
|
|
|
5
5
|
* implementation's toString() so the Inspector can show a node's code without
|
|
6
6
|
* the browser bundling the implementation — the key to consumer nodes
|
|
7
7
|
* appearing in the palette without a per-project browser build.
|
|
8
|
+
*
|
|
9
|
+
* `sourceRef` (repo-relative file + line of the register() call) is present
|
|
10
|
+
* only when registration provenance was captured AND the file lives inside
|
|
11
|
+
* the project root — the studio uses it to open the real source via
|
|
12
|
+
* GET /source-file. A captured ref OUTSIDE the root (Emberflow's own
|
|
13
|
+
* built-ins registered from the package) is flagged `builtin: true` instead:
|
|
14
|
+
* those files are never served (node_modules exposure rule), so the
|
|
15
|
+
* Inspector keeps the toString() view for them.
|
|
8
16
|
*/
|
|
9
17
|
export interface NodeMetaPayload {
|
|
10
18
|
nodes: Array<NodeDefinition & {
|
|
11
19
|
source?: string;
|
|
20
|
+
sourceRef?: {
|
|
21
|
+
file: string;
|
|
22
|
+
line?: number;
|
|
23
|
+
};
|
|
24
|
+
builtin?: boolean;
|
|
12
25
|
}>;
|
|
13
26
|
}
|
|
14
|
-
export declare function nodesPayload(registry: NodeRegistry): NodeMetaPayload;
|
|
27
|
+
export declare function nodesPayload(registry: NodeRegistry, projectRoot?: string): NodeMetaPayload;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import { isAbsolute, relative, resolve } from 'node:path';
|
|
2
|
+
export function nodesPayload(registry, projectRoot) {
|
|
3
|
+
const root = resolve(projectRoot ?? process.cwd());
|
|
2
4
|
return {
|
|
3
5
|
nodes: registry.list().map((definition) => {
|
|
4
6
|
let source;
|
|
@@ -8,7 +10,18 @@ export function nodesPayload(registry) {
|
|
|
8
10
|
catch {
|
|
9
11
|
source = undefined;
|
|
10
12
|
}
|
|
11
|
-
|
|
13
|
+
const ref = registry.getSourceRef(definition.type);
|
|
14
|
+
if (!ref)
|
|
15
|
+
return { ...definition, source };
|
|
16
|
+
const rel = relative(root, resolve(root, ref.file));
|
|
17
|
+
const inside = rel !== '' && !rel.startsWith('..') && !isAbsolute(rel);
|
|
18
|
+
if (!inside)
|
|
19
|
+
return { ...definition, source, builtin: true };
|
|
20
|
+
return {
|
|
21
|
+
...definition,
|
|
22
|
+
source,
|
|
23
|
+
sourceRef: ref.line !== undefined ? { file: rel, line: ref.line } : { file: rel },
|
|
24
|
+
};
|
|
12
25
|
}),
|
|
13
26
|
};
|
|
14
27
|
}
|
|
@@ -34,8 +34,11 @@ export function buildApiStore(project) {
|
|
|
34
34
|
return new ApiStore(apisDir);
|
|
35
35
|
}
|
|
36
36
|
export function buildRegistries(project) {
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
// Server-side registries capture registration provenance (file:line of each
|
|
38
|
+
// register() call) so the studio can navigate to a node's real source.
|
|
39
|
+
// Browser registries never enable this — see createDefaultRegistry.
|
|
40
|
+
const validation = createDefaultRegistry(undefined, { captureSourceRefs: true });
|
|
41
|
+
const execution = createDefaultRegistry(undefined, { captureSourceRefs: true });
|
|
39
42
|
if (project?.registerNodes) {
|
|
40
43
|
project.registerNodes(validation);
|
|
41
44
|
project.registerNodes(execution);
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type * as TS from 'typescript';
|
|
2
|
+
/**
|
|
3
|
+
* Source navigation for the studio's node-implementation view: serve a whole
|
|
4
|
+
* project file plus a flat symbol table (top-level declarations, imports with
|
|
5
|
+
* resolutions, re-exports) so the client can render identifiers as links and
|
|
6
|
+
* navigate file-by-file. One flat parse per file, mtime-cached; recursion
|
|
7
|
+
* happens client-side by fetching the next file — except re-export chains,
|
|
8
|
+
* which the server follows (bounded, cycle-safe) so an import through an
|
|
9
|
+
* index module resolves straight to the declaring file.
|
|
10
|
+
*
|
|
11
|
+
* TypeScript is loaded lazily on first use (`await import('typescript')`);
|
|
12
|
+
* when that fails the file is still served with `resolver: 'unavailable'`
|
|
13
|
+
* and empty symbols, and the studio shows unresolved-with-reason.
|
|
14
|
+
*/
|
|
15
|
+
export type Resolution = {
|
|
16
|
+
kind: 'project';
|
|
17
|
+
path: string;
|
|
18
|
+
line?: number;
|
|
19
|
+
} | {
|
|
20
|
+
kind: 'external';
|
|
21
|
+
package: string;
|
|
22
|
+
} | {
|
|
23
|
+
kind: 'builtin';
|
|
24
|
+
} | {
|
|
25
|
+
kind: 'unresolved';
|
|
26
|
+
reason: string;
|
|
27
|
+
};
|
|
28
|
+
export interface SourceDeclaration {
|
|
29
|
+
name: string;
|
|
30
|
+
kind: 'fn' | 'const' | 'class' | 'other';
|
|
31
|
+
line: number;
|
|
32
|
+
endLine: number;
|
|
33
|
+
exported: boolean;
|
|
34
|
+
}
|
|
35
|
+
export interface SourceImport {
|
|
36
|
+
/** Imported name in the source module ('default', '*' for namespace, 'import()' for dynamic). */
|
|
37
|
+
name: string;
|
|
38
|
+
/** Local binding name in the served file ('' when there is none). */
|
|
39
|
+
local: string;
|
|
40
|
+
/** The specifier as written. */
|
|
41
|
+
from: string;
|
|
42
|
+
resolution: Resolution;
|
|
43
|
+
}
|
|
44
|
+
export interface SourceReexport {
|
|
45
|
+
name: string;
|
|
46
|
+
from: string;
|
|
47
|
+
resolution: Resolution;
|
|
48
|
+
}
|
|
49
|
+
export interface SourceFilePayload {
|
|
50
|
+
path: string;
|
|
51
|
+
content: string;
|
|
52
|
+
language: 'ts' | 'js';
|
|
53
|
+
/** Present (as 'unavailable') only when the typescript module could not be loaded. */
|
|
54
|
+
resolver?: 'unavailable';
|
|
55
|
+
symbols: {
|
|
56
|
+
declarations: SourceDeclaration[];
|
|
57
|
+
imports: SourceImport[];
|
|
58
|
+
reexports: SourceReexport[];
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export type SourceFileResult = {
|
|
62
|
+
ok: true;
|
|
63
|
+
payload: SourceFilePayload;
|
|
64
|
+
} | {
|
|
65
|
+
ok: false;
|
|
66
|
+
status: 400 | 404;
|
|
67
|
+
error: string;
|
|
68
|
+
};
|
|
69
|
+
export interface SourceNavOptions {
|
|
70
|
+
/** Test seam: overrides the lazy `import('typescript')` for this call only. */
|
|
71
|
+
tsLoader?: () => Promise<TsModule>;
|
|
72
|
+
}
|
|
73
|
+
type TsModule = typeof TS;
|
|
74
|
+
/** Clears all sourceNav caches (parse, tsconfig, ts module) — tests only. */
|
|
75
|
+
export declare function resetSourceNavCaches(): void;
|
|
76
|
+
export declare function getSourceFile(projectRoot: string, relPath: string, opts?: SourceNavOptions): Promise<SourceFileResult>;
|
|
77
|
+
export {};
|