@octaviaflow/flow-rules 0.3.0 → 0.4.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/catalog/logic/for-each/pathOptions.d.ts +0 -1
- package/dist/index.cjs +3783 -0
- package/dist/index.d.ts +7 -6
- package/dist/index.js +269 -0
- package/package.json +7 -5
package/dist/index.d.ts
CHANGED
|
@@ -23,12 +23,12 @@ export { actionsRegistry, getAllActions, getActionById, getActionsByCategory, }
|
|
|
23
23
|
export { validateNode, validateEdge, validateWorkflow, getNodeValidationStatus, getEdgeValidationStatus, type ValidationError, type ValidationResult, } from "./rules/workflowValidator";
|
|
24
24
|
export { getNodeConfigStatus, type NodeConfigStatus, type NodeConfigStatusResult, } from "./rules/nodeConfigStatus";
|
|
25
25
|
export { generateWorkflowId, generateStepId, generateEdgeId, generateUUIDv4, } from "./ids";
|
|
26
|
-
export { CONDITION_OPERATORS,
|
|
27
|
-
export {
|
|
26
|
+
export { CONDITION_OPERATORS, ConditionConfig, type ConditionFilter, type ConditionOperator, } from "./catalog/logic/condition/config";
|
|
27
|
+
export { ForEachConfig, type ForEachConfigType, type ForEachMode, } from "./catalog/logic/for-each/config";
|
|
28
28
|
export { buildPathOptions, collectArrayPaths, collectArrayPathsFromSample, inferSchemaFromSample, type UpstreamNodeRef, } from "./catalog/logic/for-each/pathOptions";
|
|
29
|
-
export {
|
|
30
|
-
export { type VariableField, type VariableSource, type VariableSourceKind, type VariableType,
|
|
31
|
-
export {
|
|
29
|
+
export { QueryMode, QueryType } from "./catalog/general/query/types";
|
|
30
|
+
export { type VariableField, type VariableSource, type VariableSourceKind, type VariableType, VariablesConfig, } from "./catalog/utility/variables/config";
|
|
31
|
+
export { DebugConfig, type DebugConfigType, type DebugMode, type DebugSamplingStrategy, type WatchExpression, } from "./catalog/utility/debug/config";
|
|
32
32
|
export { migrateDelayConfig, } from "./catalog/actions/delay/migrate";
|
|
33
33
|
export { type DelayConfig, } from "./catalog/actions/delay/types";
|
|
34
34
|
export { emailDefinition, } from "./catalog/communication/email-send/index";
|
|
@@ -38,4 +38,5 @@ export { isQueryableConnector, } from "./catalog/general/query/config";
|
|
|
38
38
|
export { AggregateFunction, type BuildQueryConfig, type ColumnSchema, type ConnectorSchema, JoinType, LogicalOperator, type QueryExecutionResult, type QueryField, type QueryFilter, type QueryFilterGroup, type QueryJoin, type QuerySort, SortDirection, type VisualQueryConfig, } from "./catalog/general/query/types";
|
|
39
39
|
export { DATABASE_CONNECTOR_IDS, type FieldMapping, TRANSFORM_OPERATIONS, TransformConfig, type TransformConfigType, type TransformOperation, } from "./catalog/general/transform/config";
|
|
40
40
|
export { STORAGE_CONNECTOR_IDS, type StorageExtraField, type StoragePathField, getStorageProvider, storageConfigKeys, } from "./catalog/storage/storageCapabilities";
|
|
41
|
-
export { ManualTriggerConfig
|
|
41
|
+
export { ManualTriggerConfig } from "./catalog/triggers/manual-trigger/index";
|
|
42
|
+
export { type ManualTriggerConfigType } from "./catalog/triggers/manual-trigger/config";
|
package/dist/index.js
CHANGED
|
@@ -1026,6 +1026,19 @@ var groupDefinition = {
|
|
|
1026
1026
|
};
|
|
1027
1027
|
|
|
1028
1028
|
// src/catalog/general/query/types.ts
|
|
1029
|
+
var QueryMode;
|
|
1030
|
+
((QueryMode) => {
|
|
1031
|
+
QueryMode["Visual"] = "visual";
|
|
1032
|
+
QueryMode["SQL"] = "sql";
|
|
1033
|
+
})(QueryMode ||= {});
|
|
1034
|
+
var QueryType;
|
|
1035
|
+
((QueryType) => {
|
|
1036
|
+
QueryType["Select"] = "select";
|
|
1037
|
+
QueryType["Insert"] = "insert";
|
|
1038
|
+
QueryType["Update"] = "update";
|
|
1039
|
+
QueryType["Delete"] = "delete";
|
|
1040
|
+
QueryType["Custom"] = "custom";
|
|
1041
|
+
})(QueryType ||= {});
|
|
1029
1042
|
var LogicalOperator;
|
|
1030
1043
|
((LogicalOperator) => {
|
|
1031
1044
|
LogicalOperator["And"] = "and";
|
|
@@ -2983,6 +2996,135 @@ var CONDITION_OPERATORS = [
|
|
|
2983
2996
|
{ id: "is_not_null", label: "Is Not Null", needsValue: false },
|
|
2984
2997
|
{ id: "is_empty", label: "Is Empty", needsValue: false }
|
|
2985
2998
|
];
|
|
2999
|
+
var ConditionConfig = {
|
|
3000
|
+
defaults: {
|
|
3001
|
+
mode: "visual",
|
|
3002
|
+
filters: [],
|
|
3003
|
+
condition: ""
|
|
3004
|
+
},
|
|
3005
|
+
schema: {
|
|
3006
|
+
mode: { type: "select", required: true },
|
|
3007
|
+
filters: { type: "array", required: false },
|
|
3008
|
+
condition: { type: "expression", required: false }
|
|
3009
|
+
},
|
|
3010
|
+
validate: (config) => {
|
|
3011
|
+
const errors = [];
|
|
3012
|
+
if (config.mode === "expression") {
|
|
3013
|
+
if (!config.condition) {
|
|
3014
|
+
errors.push("Condition expression is required");
|
|
3015
|
+
} else {
|
|
3016
|
+
try {
|
|
3017
|
+
new Function(`return ${config.condition}`);
|
|
3018
|
+
} catch (e) {
|
|
3019
|
+
errors.push(`Invalid expression: ${e instanceof Error ? e.message : "Unknown error"}`);
|
|
3020
|
+
}
|
|
3021
|
+
}
|
|
3022
|
+
} else {
|
|
3023
|
+
if (!config.filters || config.filters.length === 0) {
|
|
3024
|
+
errors.push("At least one condition filter is required");
|
|
3025
|
+
} else {
|
|
3026
|
+
config.filters.forEach((f, i) => {
|
|
3027
|
+
if (!f.field)
|
|
3028
|
+
errors.push(`Filter ${i + 1}: Field is required`);
|
|
3029
|
+
if (!f.operator)
|
|
3030
|
+
errors.push(`Filter ${i + 1}: Operator is required`);
|
|
3031
|
+
const opDef = CONDITION_OPERATORS.find((o) => o.id === f.operator);
|
|
3032
|
+
if (opDef?.needsValue && !f.value) {
|
|
3033
|
+
errors.push(`Filter ${i + 1}: Value is required`);
|
|
3034
|
+
}
|
|
3035
|
+
});
|
|
3036
|
+
}
|
|
3037
|
+
}
|
|
3038
|
+
return { valid: errors.length === 0, errors };
|
|
3039
|
+
},
|
|
3040
|
+
merge: (userConfig) => {
|
|
3041
|
+
return { ...ConditionConfig.defaults, ...userConfig };
|
|
3042
|
+
},
|
|
3043
|
+
generateName: (filters) => {
|
|
3044
|
+
if (filters.length === 0)
|
|
3045
|
+
return "If …";
|
|
3046
|
+
const parts = filters.map((f, i) => {
|
|
3047
|
+
const opLabel = CONDITION_OPERATORS.find((o) => o.id === f.operator)?.label || f.operator;
|
|
3048
|
+
const base = `${f.field || "?"} ${opLabel}${f.value ? ` ${f.value}` : ""}`;
|
|
3049
|
+
if (i === 0)
|
|
3050
|
+
return base;
|
|
3051
|
+
return `${(f.logicalOperator || "and").toUpperCase()} ${base}`;
|
|
3052
|
+
});
|
|
3053
|
+
return `If ${parts.join(" ")}`;
|
|
3054
|
+
},
|
|
3055
|
+
getLabel: (config) => {
|
|
3056
|
+
if (config.mode === "expression")
|
|
3057
|
+
return `If ${config.condition || "…"}`;
|
|
3058
|
+
return ConditionConfig.generateName(config.filters);
|
|
3059
|
+
},
|
|
3060
|
+
createFilterId: () => `cond_${Date.now()}_${randomSuffix(5)}`
|
|
3061
|
+
};
|
|
3062
|
+
// src/catalog/logic/for-each/config.ts
|
|
3063
|
+
var SOURCELESS_MODES = new Set(["range", "cursor", "while"]);
|
|
3064
|
+
var ForEachConfig = {
|
|
3065
|
+
defaults: {
|
|
3066
|
+
arrayPath: "",
|
|
3067
|
+
iterationMode: "records",
|
|
3068
|
+
itemVariable: "item",
|
|
3069
|
+
indexVariable: "index",
|
|
3070
|
+
batchSize: 0,
|
|
3071
|
+
continueOnError: false,
|
|
3072
|
+
concurrency: "sequential",
|
|
3073
|
+
collect: "array"
|
|
3074
|
+
},
|
|
3075
|
+
schema: {
|
|
3076
|
+
arrayPath: { type: "select", required: false },
|
|
3077
|
+
iterationMode: { type: "select", required: false },
|
|
3078
|
+
itemVariable: { type: "string", required: true },
|
|
3079
|
+
indexVariable: { type: "string", required: false },
|
|
3080
|
+
batchSize: { type: "number", required: false },
|
|
3081
|
+
continueOnError: { type: "boolean", required: false }
|
|
3082
|
+
},
|
|
3083
|
+
validate: (config) => {
|
|
3084
|
+
const errors = [];
|
|
3085
|
+
const mode = config.iterationMode || "records";
|
|
3086
|
+
if (mode === "range") {
|
|
3087
|
+
if (!config.count || config.count <= 0) {
|
|
3088
|
+
errors.push("Range mode needs a positive iteration count");
|
|
3089
|
+
}
|
|
3090
|
+
} else if (mode === "nested") {
|
|
3091
|
+
if (!config.arrayPath)
|
|
3092
|
+
errors.push("Select the records to drill into");
|
|
3093
|
+
if (!config.nestedPath)
|
|
3094
|
+
errors.push('Nested mode needs the sub-array path to iterate (e.g. "lineItems")');
|
|
3095
|
+
} else if (!SOURCELESS_MODES.has(mode)) {
|
|
3096
|
+
if (!config.arrayPath) {
|
|
3097
|
+
errors.push("Array path is required — select which records to iterate over");
|
|
3098
|
+
}
|
|
3099
|
+
}
|
|
3100
|
+
if (config.concurrency === "parallel" && config.maxConcurrency !== undefined && config.maxConcurrency < 1) {
|
|
3101
|
+
errors.push("Max concurrency must be at least 1");
|
|
3102
|
+
}
|
|
3103
|
+
if (config.batchSize !== undefined && config.batchSize < 0) {
|
|
3104
|
+
errors.push("Batch size must be 0 (all) or a positive number");
|
|
3105
|
+
}
|
|
3106
|
+
if (config.collect === "groupBy" && !config.groupByPath) {
|
|
3107
|
+
errors.push("Group-by collection needs a field path to group on");
|
|
3108
|
+
}
|
|
3109
|
+
return { valid: errors.length === 0, errors };
|
|
3110
|
+
},
|
|
3111
|
+
merge: (userConfig) => {
|
|
3112
|
+
return { ...ForEachConfig.defaults, ...userConfig };
|
|
3113
|
+
},
|
|
3114
|
+
getLabel: (config) => {
|
|
3115
|
+
const mode = config.iterationMode || "records";
|
|
3116
|
+
if (mode === "range")
|
|
3117
|
+
return `ForEach ×${config.count ?? "…"}`;
|
|
3118
|
+
if (mode === "cursor")
|
|
3119
|
+
return "ForEach (paginate)";
|
|
3120
|
+
if (mode === "while")
|
|
3121
|
+
return "ForEach (while)";
|
|
3122
|
+
return `ForEach in ${config.arrayPath || "…"}`;
|
|
3123
|
+
},
|
|
3124
|
+
generateName: (arrayPath) => {
|
|
3125
|
+
return `ForEach in ${arrayPath || "…"}`;
|
|
3126
|
+
}
|
|
3127
|
+
};
|
|
2986
3128
|
// src/catalog/logic/for-each/pathOptions.ts
|
|
2987
3129
|
function inferSchemaFromSample(sample) {
|
|
2988
3130
|
const properties = {};
|
|
@@ -3078,6 +3220,127 @@ function buildPathOptions(upstreamNodes) {
|
|
|
3078
3220
|
}
|
|
3079
3221
|
return paths;
|
|
3080
3222
|
}
|
|
3223
|
+
// src/catalog/utility/variables/config.ts
|
|
3224
|
+
var RESERVED_KEYS = new Set(["_lineage", "_meta", "_redacted", "_error"]);
|
|
3225
|
+
var KEY_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
3226
|
+
var VariablesConfig = {
|
|
3227
|
+
defaults: {
|
|
3228
|
+
fields: []
|
|
3229
|
+
},
|
|
3230
|
+
validate(config) {
|
|
3231
|
+
const errors = [];
|
|
3232
|
+
const fields = config.fields ?? [];
|
|
3233
|
+
const seen = new Set;
|
|
3234
|
+
for (const f of fields) {
|
|
3235
|
+
if (!f.key) {
|
|
3236
|
+
errors.push("Each field needs a key.");
|
|
3237
|
+
continue;
|
|
3238
|
+
}
|
|
3239
|
+
if (RESERVED_KEYS.has(f.key)) {
|
|
3240
|
+
errors.push(`"${f.key}" is reserved — pick another key.`);
|
|
3241
|
+
continue;
|
|
3242
|
+
}
|
|
3243
|
+
if (!KEY_PATTERN.test(f.key)) {
|
|
3244
|
+
errors.push(`"${f.key}" is not a valid identifier (letters, digits, underscore; must not start with a digit).`);
|
|
3245
|
+
continue;
|
|
3246
|
+
}
|
|
3247
|
+
if (seen.has(f.key)) {
|
|
3248
|
+
errors.push(`Duplicate key "${f.key}".`);
|
|
3249
|
+
continue;
|
|
3250
|
+
}
|
|
3251
|
+
seen.add(f.key);
|
|
3252
|
+
if (!f.type)
|
|
3253
|
+
errors.push(`"${f.key}" is missing a type.`);
|
|
3254
|
+
if (!f.source)
|
|
3255
|
+
errors.push(`"${f.key}" is missing a source.`);
|
|
3256
|
+
}
|
|
3257
|
+
return { valid: errors.length === 0, errors };
|
|
3258
|
+
},
|
|
3259
|
+
computeOutputSchema(fields) {
|
|
3260
|
+
const properties = {};
|
|
3261
|
+
for (const f of fields) {
|
|
3262
|
+
properties[f.key] = fieldToJsonSchema(f);
|
|
3263
|
+
}
|
|
3264
|
+
return { type: "object", properties };
|
|
3265
|
+
}
|
|
3266
|
+
};
|
|
3267
|
+
function fieldToJsonSchema(f) {
|
|
3268
|
+
const node = { type: f.type };
|
|
3269
|
+
if (f.description)
|
|
3270
|
+
node.description = f.description;
|
|
3271
|
+
if (f.validation?.enum)
|
|
3272
|
+
node.enum = f.validation.enum;
|
|
3273
|
+
return node;
|
|
3274
|
+
}
|
|
3275
|
+
// src/catalog/utility/debug/config.ts
|
|
3276
|
+
var DebugConfig = {
|
|
3277
|
+
defaults: {
|
|
3278
|
+
mode: "log",
|
|
3279
|
+
label: "",
|
|
3280
|
+
log: { level: "info", message: "" }
|
|
3281
|
+
},
|
|
3282
|
+
validate(config) {
|
|
3283
|
+
const errors = [];
|
|
3284
|
+
if (!config.mode)
|
|
3285
|
+
errors.push("Mode is required.");
|
|
3286
|
+
if (!config.label?.trim())
|
|
3287
|
+
errors.push("Label is required.");
|
|
3288
|
+
switch (config.mode) {
|
|
3289
|
+
case "log":
|
|
3290
|
+
if (!config.log?.message?.trim())
|
|
3291
|
+
errors.push("Log mode: message is required.");
|
|
3292
|
+
if (!config.log?.level)
|
|
3293
|
+
errors.push("Log mode: level is required.");
|
|
3294
|
+
break;
|
|
3295
|
+
case "snapshot":
|
|
3296
|
+
break;
|
|
3297
|
+
case "watch":
|
|
3298
|
+
if (!config.watch?.expressions?.length) {
|
|
3299
|
+
errors.push("Watch mode: add at least one expression.");
|
|
3300
|
+
} else {
|
|
3301
|
+
for (const e of config.watch.expressions) {
|
|
3302
|
+
if (!e.label?.trim())
|
|
3303
|
+
errors.push("Watch mode: each expression needs a label.");
|
|
3304
|
+
if (!e.expression?.trim())
|
|
3305
|
+
errors.push("Watch mode: each expression needs an expression string.");
|
|
3306
|
+
}
|
|
3307
|
+
}
|
|
3308
|
+
break;
|
|
3309
|
+
case "assert":
|
|
3310
|
+
if (!config.assert?.predicate?.trim())
|
|
3311
|
+
errors.push("Assert mode: predicate is required.");
|
|
3312
|
+
if (!config.assert?.failureMessage?.trim())
|
|
3313
|
+
errors.push("Assert mode: failure message is required.");
|
|
3314
|
+
break;
|
|
3315
|
+
case "profile":
|
|
3316
|
+
if (!config.profile?.scope)
|
|
3317
|
+
errors.push("Profile mode: scope is required.");
|
|
3318
|
+
break;
|
|
3319
|
+
}
|
|
3320
|
+
return { valid: errors.length === 0, errors };
|
|
3321
|
+
},
|
|
3322
|
+
migrateLegacyStub(config) {
|
|
3323
|
+
if (config.mode) {
|
|
3324
|
+
const { value: _stub, ...rest } = config;
|
|
3325
|
+
return { ...DebugConfig.defaults, ...rest };
|
|
3326
|
+
}
|
|
3327
|
+
return {
|
|
3328
|
+
mode: "log",
|
|
3329
|
+
label: config.label ?? "Debug",
|
|
3330
|
+
log: {
|
|
3331
|
+
level: "info",
|
|
3332
|
+
message: config.label ?? "Debug",
|
|
3333
|
+
includeInputData: true
|
|
3334
|
+
}
|
|
3335
|
+
};
|
|
3336
|
+
},
|
|
3337
|
+
merge(userConfig) {
|
|
3338
|
+
return { ...DebugConfig.defaults, ...userConfig };
|
|
3339
|
+
},
|
|
3340
|
+
getLabel(config) {
|
|
3341
|
+
return `Debug [${config.mode}]: ${config.label}`;
|
|
3342
|
+
}
|
|
3343
|
+
};
|
|
3081
3344
|
// src/catalog/actions/delay/migrate.ts
|
|
3082
3345
|
var LEGACY_UNITS = new Set([
|
|
3083
3346
|
"milliseconds",
|
|
@@ -3435,16 +3698,22 @@ export {
|
|
|
3435
3698
|
ActionType,
|
|
3436
3699
|
AggregateFunction,
|
|
3437
3700
|
CONDITION_OPERATORS,
|
|
3701
|
+
ConditionConfig,
|
|
3438
3702
|
DATABASE_CONNECTOR_IDS,
|
|
3703
|
+
DebugConfig,
|
|
3439
3704
|
EmailConfig,
|
|
3440
3705
|
FieldType,
|
|
3706
|
+
ForEachConfig,
|
|
3441
3707
|
JoinType,
|
|
3442
3708
|
LogicalOperator,
|
|
3443
3709
|
ManualTriggerConfig,
|
|
3710
|
+
QueryMode,
|
|
3711
|
+
QueryType,
|
|
3444
3712
|
STORAGE_CONNECTOR_IDS,
|
|
3445
3713
|
SortDirection,
|
|
3446
3714
|
TRANSFORM_OPERATIONS,
|
|
3447
3715
|
TransformConfig,
|
|
3716
|
+
VariablesConfig,
|
|
3448
3717
|
actionsRegistry,
|
|
3449
3718
|
buildPathOptions,
|
|
3450
3719
|
collectArrayPaths,
|
package/package.json
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@octaviaflow/flow-rules",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "The flow action catalog and the rules engine behind Flow Doctor
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "The flow action catalog and the rules engine behind Flow Doctor \u2014 one definition of what a step accepts and what makes a flow valid, shared by the editor and the server",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "./dist/index.
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
7
|
"module": "./dist/index.js",
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
11
11
|
"types": "./dist/index.d.ts",
|
|
12
|
-
"import": "./dist/index.js"
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs",
|
|
14
|
+
"default": "./dist/index.js"
|
|
13
15
|
}
|
|
14
16
|
},
|
|
15
17
|
"files": [
|
|
@@ -17,7 +19,7 @@
|
|
|
17
19
|
"README.md"
|
|
18
20
|
],
|
|
19
21
|
"scripts": {
|
|
20
|
-
"build": "rm -rf dist && tsc -p tsconfig.build.json --emitDeclarationOnly && bun build src/index.ts --outdir dist --target node --format esm",
|
|
22
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json --emitDeclarationOnly && bun build src/index.ts --outdir dist --target node --format esm && bun build src/index.ts --outfile dist/index.cjs --target node --format cjs",
|
|
21
23
|
"test": "bun test",
|
|
22
24
|
"typecheck": "tsc --noEmit"
|
|
23
25
|
},
|