@synergenius/flow-weaver 0.21.17 → 0.21.19
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/cli/flow-weaver.mjs +41 -2
- package/dist/generated-version.d.ts +1 -1
- package/dist/generated-version.js +1 -1
- package/dist/npm-packages.d.ts +1 -0
- package/dist/npm-packages.js +27 -72
- package/dist/validator.js +61 -0
- package/package.json +1 -1
package/dist/cli/flow-weaver.mjs
CHANGED
|
@@ -9671,7 +9671,7 @@ var VERSION;
|
|
|
9671
9671
|
var init_generated_version = __esm({
|
|
9672
9672
|
"src/generated-version.ts"() {
|
|
9673
9673
|
"use strict";
|
|
9674
|
-
VERSION = "0.21.
|
|
9674
|
+
VERSION = "0.21.19";
|
|
9675
9675
|
}
|
|
9676
9676
|
});
|
|
9677
9677
|
|
|
@@ -39494,6 +39494,45 @@ Add '@param ${fromPort}' to the workflow JSDoc and include it in the params obje
|
|
|
39494
39494
|
}
|
|
39495
39495
|
}
|
|
39496
39496
|
}
|
|
39497
|
+
const parentOf = /* @__PURE__ */ new Map();
|
|
39498
|
+
for (const inst of workflow.instances) {
|
|
39499
|
+
parentOf.set(inst.id, inst.parent ?? void 0);
|
|
39500
|
+
}
|
|
39501
|
+
const scopedParentIds = /* @__PURE__ */ new Set();
|
|
39502
|
+
for (const inst of workflow.instances) {
|
|
39503
|
+
const nt = instanceMap.get(inst.id);
|
|
39504
|
+
if (!nt) continue;
|
|
39505
|
+
const hasScopedPorts = [
|
|
39506
|
+
...Object.values(nt.inputs ?? {}),
|
|
39507
|
+
...Object.values(nt.outputs ?? {})
|
|
39508
|
+
].some((p) => p.scope);
|
|
39509
|
+
if (hasScopedPorts) scopedParentIds.add(inst.id);
|
|
39510
|
+
}
|
|
39511
|
+
for (const conn of workflow.connections) {
|
|
39512
|
+
if (conn.from.scope || conn.to.scope) continue;
|
|
39513
|
+
const fromParent = parentOf.get(conn.from.node);
|
|
39514
|
+
const toParent = parentOf.get(conn.to.node);
|
|
39515
|
+
if (!parentOf.has(conn.from.node) || !parentOf.has(conn.to.node)) continue;
|
|
39516
|
+
if (!fromParent && !toParent) continue;
|
|
39517
|
+
const fromInScopedParent = fromParent && scopedParentIds.has(fromParent.id);
|
|
39518
|
+
const toInScopedParent = toParent && scopedParentIds.has(toParent.id);
|
|
39519
|
+
if (!fromInScopedParent && !toInScopedParent) continue;
|
|
39520
|
+
const fromIsParentOfTo = toParent && toParent.id === conn.from.node;
|
|
39521
|
+
const toIsParentOfFrom = fromParent && fromParent.id === conn.to.node;
|
|
39522
|
+
if (fromIsParentOfTo || toIsParentOfFrom) continue;
|
|
39523
|
+
const sameScope = fromParent && toParent && fromParent.id === toParent.id && fromParent.scope === toParent.scope;
|
|
39524
|
+
if (!sameScope) {
|
|
39525
|
+
const fromCtx = fromParent ? `${fromParent.id}.${fromParent.scope}` : "root";
|
|
39526
|
+
const toCtx = toParent ? `${toParent.id}.${toParent.scope}` : "root";
|
|
39527
|
+
this.errors.push({
|
|
39528
|
+
type: "error",
|
|
39529
|
+
code: "CROSS_SCOPE_CONNECTION",
|
|
39530
|
+
message: `Connection from "${conn.from.node}.${conn.from.port}" (in ${fromCtx}) to "${conn.to.node}.${conn.to.port}" (in ${toCtx}) crosses scope boundaries. Nodes in different scopes cannot connect directly.`,
|
|
39531
|
+
connection: conn,
|
|
39532
|
+
location: this.getConnectionLocation(conn)
|
|
39533
|
+
});
|
|
39534
|
+
}
|
|
39535
|
+
}
|
|
39497
39536
|
}
|
|
39498
39537
|
// ── H: Duplicate instance IDs ──────────────────────────────────────────
|
|
39499
39538
|
validateDuplicateInstanceIds(workflow) {
|
|
@@ -93231,7 +93270,7 @@ function displayInstalledPackage(pkg) {
|
|
|
93231
93270
|
// src/cli/index.ts
|
|
93232
93271
|
init_logger();
|
|
93233
93272
|
init_error_utils();
|
|
93234
|
-
var version2 = true ? "0.21.
|
|
93273
|
+
var version2 = true ? "0.21.19" : "0.0.0-dev";
|
|
93235
93274
|
var program2 = new Command();
|
|
93236
93275
|
program2.name("fw").description("Flow Weaver Annotations - Compile and validate workflow files").option("-v, --version", "Output the current version").option("--no-color", "Disable colors").option("--color", "Force colors").on("option:version", () => {
|
|
93237
93276
|
logger.banner(version2);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.21.
|
|
1
|
+
export declare const VERSION = "0.21.19";
|
|
2
2
|
//# sourceMappingURL=generated-version.d.ts.map
|
package/dist/npm-packages.d.ts
CHANGED
|
@@ -32,6 +32,7 @@ export type TNpmNodeType = {
|
|
|
32
32
|
};
|
|
33
33
|
/**
|
|
34
34
|
* Get list of packages that have TypeScript declarations (.d.ts files).
|
|
35
|
+
* Only includes direct dependencies from package.json (not transitive or dev).
|
|
35
36
|
* Excludes @types/* packages as they are type augmentations.
|
|
36
37
|
*
|
|
37
38
|
* @param workdir - Directory to start searching from
|
package/dist/npm-packages.js
CHANGED
|
@@ -11,64 +11,34 @@ import { extractFunctionLikes } from './function-like.js';
|
|
|
11
11
|
import { inferDataTypeFromTS } from './type-mappings.js';
|
|
12
12
|
import { getSharedProject } from './shared-project.js';
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
14
|
+
* Read direct dependency names from the closest package.json.
|
|
15
|
+
* Only includes `dependencies` (not devDependencies, peerDependencies, etc.)
|
|
16
|
+
* since those are the packages available at runtime in workflows.
|
|
15
17
|
*/
|
|
16
|
-
function
|
|
17
|
-
|
|
18
|
-
let current = path.resolve(fromDir);
|
|
18
|
+
function readDirectDependencies(workdir) {
|
|
19
|
+
let current = path.resolve(workdir);
|
|
19
20
|
const root = path.parse(current).root;
|
|
20
21
|
while (current !== root) {
|
|
21
|
-
const
|
|
22
|
-
if (fs.existsSync(
|
|
23
|
-
|
|
22
|
+
const pkgJsonPath = path.join(current, 'package.json');
|
|
23
|
+
if (fs.existsSync(pkgJsonPath)) {
|
|
24
|
+
try {
|
|
25
|
+
const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'));
|
|
26
|
+
return Object.keys(pkgJson.dependencies ?? {});
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// malformed package.json, keep walking
|
|
30
|
+
}
|
|
24
31
|
}
|
|
25
32
|
const parent = path.dirname(current);
|
|
26
33
|
if (parent === current)
|
|
27
34
|
break;
|
|
28
35
|
current = parent;
|
|
29
36
|
}
|
|
30
|
-
return
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* List all packages in a node_modules directory (including scoped packages).
|
|
34
|
-
*/
|
|
35
|
-
function listPackagesInNodeModules(nmDir) {
|
|
36
|
-
const packages = [];
|
|
37
|
-
if (!fs.existsSync(nmDir))
|
|
38
|
-
return packages;
|
|
39
|
-
try {
|
|
40
|
-
const entries = fs.readdirSync(nmDir, { withFileTypes: true });
|
|
41
|
-
for (const entry of entries) {
|
|
42
|
-
if (!entry.isDirectory())
|
|
43
|
-
continue;
|
|
44
|
-
// Handle scoped packages (@scope/pkg)
|
|
45
|
-
if (entry.name.startsWith('@')) {
|
|
46
|
-
const scopeDir = path.join(nmDir, entry.name);
|
|
47
|
-
try {
|
|
48
|
-
const scopedEntries = fs.readdirSync(scopeDir, { withFileTypes: true });
|
|
49
|
-
for (const scopedEntry of scopedEntries) {
|
|
50
|
-
if (scopedEntry.isDirectory()) {
|
|
51
|
-
packages.push(`${entry.name}/${scopedEntry.name}`);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
catch {
|
|
56
|
-
// Ignore permission errors
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
// Regular package
|
|
61
|
-
packages.push(entry.name);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
catch {
|
|
66
|
-
// Ignore permission errors
|
|
67
|
-
}
|
|
68
|
-
return packages;
|
|
37
|
+
return [];
|
|
69
38
|
}
|
|
70
39
|
/**
|
|
71
40
|
* Get list of packages that have TypeScript declarations (.d.ts files).
|
|
41
|
+
* Only includes direct dependencies from package.json (not transitive or dev).
|
|
72
42
|
* Excludes @types/* packages as they are type augmentations.
|
|
73
43
|
*
|
|
74
44
|
* @param workdir - Directory to start searching from
|
|
@@ -76,40 +46,26 @@ function listPackagesInNodeModules(nmDir) {
|
|
|
76
46
|
* @returns Object with packages array, each containing name and typesPath
|
|
77
47
|
*/
|
|
78
48
|
export function getTypedPackages(workdir, nodeModulesOverride) {
|
|
79
|
-
const
|
|
80
|
-
? [nodeModulesOverride]
|
|
81
|
-
: findNodeModulesDirs(workdir);
|
|
49
|
+
const directDeps = readDirectDependencies(workdir);
|
|
82
50
|
const typed = [];
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
continue;
|
|
90
|
-
// Skip if already seen (from a closer node_modules)
|
|
91
|
-
if (seenPackages.has(pkg))
|
|
92
|
-
continue;
|
|
93
|
-
seenPackages.add(pkg);
|
|
94
|
-
// Check if package has types
|
|
95
|
-
const typesPath = resolvePackageTypesPath(pkg, workdir, nodeModulesOverride);
|
|
96
|
-
if (typesPath) {
|
|
97
|
-
typed.push({ name: pkg, typesPath });
|
|
98
|
-
}
|
|
51
|
+
for (const pkg of directDeps) {
|
|
52
|
+
if (pkg.startsWith('@types/'))
|
|
53
|
+
continue;
|
|
54
|
+
const typesPath = resolvePackageTypesPath(pkg, workdir, nodeModulesOverride);
|
|
55
|
+
if (typesPath) {
|
|
56
|
+
typed.push({ name: pkg, typesPath });
|
|
99
57
|
}
|
|
100
58
|
}
|
|
101
59
|
return { packages: typed };
|
|
102
60
|
}
|
|
103
|
-
|
|
104
|
-
* Capitalize first letter of a string.
|
|
105
|
-
*/
|
|
61
|
+
const PRIMITIVE_TYPES = new Set(['string', 'number', 'boolean', 'any', 'unknown', 'never']);
|
|
106
62
|
function capitalize(str) {
|
|
107
63
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
108
64
|
}
|
|
109
65
|
/**
|
|
110
66
|
* Infer node type from a function declaration in a .d.ts file.
|
|
111
67
|
*/
|
|
112
|
-
function inferNodeTypeFromDtsFunction(fn, packageName
|
|
68
|
+
function inferNodeTypeFromDtsFunction(fn, packageName) {
|
|
113
69
|
const fnName = fn.getName();
|
|
114
70
|
if (!fnName)
|
|
115
71
|
return null;
|
|
@@ -149,8 +105,7 @@ function inferNodeTypeFromDtsFunction(fn, packageName, _dtsPath) {
|
|
|
149
105
|
}
|
|
150
106
|
const unwrappedText = returnType.getText();
|
|
151
107
|
if (unwrappedText !== 'void' && unwrappedText !== 'undefined') {
|
|
152
|
-
const
|
|
153
|
-
const isPrimitive = primitiveTypes.has(unwrappedText);
|
|
108
|
+
const isPrimitive = PRIMITIVE_TYPES.has(unwrappedText);
|
|
154
109
|
const isArray = unwrappedText.endsWith('[]') || unwrappedText.startsWith('Array<');
|
|
155
110
|
const properties = returnType.getProperties();
|
|
156
111
|
const isObjectLike = !isPrimitive && !isArray && returnType.isObject() && properties.length > 0;
|
|
@@ -239,7 +194,7 @@ export function getPackageExports(packageName, workdir, nodeModulesOverride) {
|
|
|
239
194
|
if (!fnName || seenFunctionNames.has(fnName))
|
|
240
195
|
continue;
|
|
241
196
|
seenFunctionNames.add(fnName);
|
|
242
|
-
const nodeType = inferNodeTypeFromDtsFunction(fn, packageName
|
|
197
|
+
const nodeType = inferNodeTypeFromDtsFunction(fn, packageName);
|
|
243
198
|
if (nodeType) {
|
|
244
199
|
nodeTypes.push(nodeType);
|
|
245
200
|
}
|
package/dist/validator.js
CHANGED
|
@@ -1374,6 +1374,67 @@ export class WorkflowValidator {
|
|
|
1374
1374
|
}
|
|
1375
1375
|
}
|
|
1376
1376
|
}
|
|
1377
|
+
// Check: non-scoped connections must not cross scope boundaries
|
|
1378
|
+
// when the parent is a scoped node type (has scoped ports).
|
|
1379
|
+
// A child in scope "a" cannot connect to a child in scope "b" (or root),
|
|
1380
|
+
// because they execute in separate callback contexts.
|
|
1381
|
+
// Only applies to per-port scoped parents (not flat grouping scopes).
|
|
1382
|
+
const parentOf = new Map();
|
|
1383
|
+
for (const inst of workflow.instances) {
|
|
1384
|
+
parentOf.set(inst.id, inst.parent ?? undefined);
|
|
1385
|
+
}
|
|
1386
|
+
// Build set of node IDs that are scoped parent types (have scoped ports)
|
|
1387
|
+
const scopedParentIds = new Set();
|
|
1388
|
+
for (const inst of workflow.instances) {
|
|
1389
|
+
const nt = instanceMap.get(inst.id);
|
|
1390
|
+
if (!nt)
|
|
1391
|
+
continue;
|
|
1392
|
+
const hasScopedPorts = [
|
|
1393
|
+
...Object.values(nt.inputs ?? {}),
|
|
1394
|
+
...Object.values(nt.outputs ?? {}),
|
|
1395
|
+
].some((p) => p.scope);
|
|
1396
|
+
if (hasScopedPorts)
|
|
1397
|
+
scopedParentIds.add(inst.id);
|
|
1398
|
+
}
|
|
1399
|
+
for (const conn of workflow.connections) {
|
|
1400
|
+
// Skip scoped connections (already validated above)
|
|
1401
|
+
if (conn.from.scope || conn.to.scope)
|
|
1402
|
+
continue;
|
|
1403
|
+
const fromParent = parentOf.get(conn.from.node);
|
|
1404
|
+
const toParent = parentOf.get(conn.to.node);
|
|
1405
|
+
// Both must exist in the workflow
|
|
1406
|
+
if (!parentOf.has(conn.from.node) || !parentOf.has(conn.to.node))
|
|
1407
|
+
continue;
|
|
1408
|
+
// If neither node is inside a scope, skip (root-to-root is fine)
|
|
1409
|
+
if (!fromParent && !toParent)
|
|
1410
|
+
continue;
|
|
1411
|
+
// Only validate when the parent is a scoped node type (has scoped ports).
|
|
1412
|
+
// Flat grouping scopes (createScope without scoped ports) are allowed to cross.
|
|
1413
|
+
const fromInScopedParent = fromParent && scopedParentIds.has(fromParent.id);
|
|
1414
|
+
const toInScopedParent = toParent && scopedParentIds.has(toParent.id);
|
|
1415
|
+
if (!fromInScopedParent && !toInScopedParent)
|
|
1416
|
+
continue;
|
|
1417
|
+
// Allow connections where one side is the scoped PARENT itself (not a child)
|
|
1418
|
+
const fromIsParentOfTo = toParent && toParent.id === conn.from.node;
|
|
1419
|
+
const toIsParentOfFrom = fromParent && fromParent.id === conn.to.node;
|
|
1420
|
+
if (fromIsParentOfTo || toIsParentOfFrom)
|
|
1421
|
+
continue;
|
|
1422
|
+
// Check if they share the same parent+scope
|
|
1423
|
+
const sameScope = fromParent && toParent &&
|
|
1424
|
+
fromParent.id === toParent.id &&
|
|
1425
|
+
fromParent.scope === toParent.scope;
|
|
1426
|
+
if (!sameScope) {
|
|
1427
|
+
const fromCtx = fromParent ? `${fromParent.id}.${fromParent.scope}` : 'root';
|
|
1428
|
+
const toCtx = toParent ? `${toParent.id}.${toParent.scope}` : 'root';
|
|
1429
|
+
this.errors.push({
|
|
1430
|
+
type: 'error',
|
|
1431
|
+
code: 'CROSS_SCOPE_CONNECTION',
|
|
1432
|
+
message: `Connection from "${conn.from.node}.${conn.from.port}" (in ${fromCtx}) to "${conn.to.node}.${conn.to.port}" (in ${toCtx}) crosses scope boundaries. Nodes in different scopes cannot connect directly.`,
|
|
1433
|
+
connection: conn,
|
|
1434
|
+
location: this.getConnectionLocation(conn),
|
|
1435
|
+
});
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1377
1438
|
}
|
|
1378
1439
|
// ── H: Duplicate instance IDs ──────────────────────────────────────────
|
|
1379
1440
|
validateDuplicateInstanceIds(workflow) {
|
package/package.json
CHANGED