@octaviaflow/flow-rules 0.1.1 → 0.3.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/index.d.ts CHANGED
@@ -23,3 +23,19 @@ 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, type ConditionConfig, type ConditionFilter, type ConditionOperator, } from "./catalog/logic/condition/config";
27
+ export { type ForEachConfig, type ForEachConfigType, type ForEachMode, } from "./catalog/logic/for-each/config";
28
+ export { buildPathOptions, collectArrayPaths, collectArrayPathsFromSample, inferSchemaFromSample, type UpstreamNodeRef, } from "./catalog/logic/for-each/pathOptions";
29
+ export { type QueryMode, type QueryType } from "./catalog/general/query/types";
30
+ export { type VariableField, type VariableSource, type VariableSourceKind, type VariableType, type VariablesConfig, } from "./catalog/utility/variables/config";
31
+ export { type DebugConfig, type DebugConfigType, type DebugMode, type DebugSamplingStrategy, type WatchExpression, } from "./catalog/utility/debug/config";
32
+ export { migrateDelayConfig, } from "./catalog/actions/delay/migrate";
33
+ export { type DelayConfig, } from "./catalog/actions/delay/types";
34
+ export { emailDefinition, } from "./catalog/communication/email-send/index";
35
+ export { EmailConfig, } from "./catalog/communication/email-send/config";
36
+ export { queryDefinition, } from "./catalog/general/query/index";
37
+ export { isQueryableConnector, } from "./catalog/general/query/config";
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
+ export { DATABASE_CONNECTOR_IDS, type FieldMapping, TRANSFORM_OPERATIONS, TransformConfig, type TransformConfigType, type TransformOperation, } from "./catalog/general/transform/config";
40
+ export { STORAGE_CONNECTOR_IDS, type StorageExtraField, type StoragePathField, getStorageProvider, storageConfigKeys, } from "./catalog/storage/storageCapabilities";
41
+ export { ManualTriggerConfig, } from "./catalog/triggers/manual-trigger/index";
package/dist/index.js CHANGED
@@ -133,6 +133,48 @@ function generateEdgeId(sourceId, targetId) {
133
133
  return `edge_${sourceId}_${targetId}_${Date.now()}_${randomSuffix(7)}`;
134
134
  }
135
135
 
136
+ // src/catalog/triggers/manual-trigger/config.ts
137
+ var ManualTriggerConfig = {
138
+ defaults: {
139
+ description: ""
140
+ },
141
+ schema: {
142
+ description: {
143
+ type: "string",
144
+ maxLength: 500,
145
+ required: false,
146
+ description: "Optional description for this trigger (shown on node)"
147
+ }
148
+ },
149
+ validate: (config) => {
150
+ const errors = [];
151
+ if (config.description !== undefined) {
152
+ if (typeof config.description !== "string") {
153
+ errors.push("Description must be a string");
154
+ }
155
+ if (config.description.length > 500) {
156
+ errors.push("Description must not exceed 500 characters");
157
+ }
158
+ }
159
+ return {
160
+ valid: errors.length === 0,
161
+ errors
162
+ };
163
+ },
164
+ merge: (userConfig) => {
165
+ return {
166
+ ...ManualTriggerConfig.defaults,
167
+ ...userConfig
168
+ };
169
+ },
170
+ getLabel: (config) => {
171
+ if (config.description) {
172
+ return `Manual Trigger: ${config.description}`;
173
+ }
174
+ return "Manual Trigger";
175
+ }
176
+ };
177
+
136
178
  // src/catalog/triggers/manual-trigger/index.ts
137
179
  var manualTriggerDefinition = {
138
180
  id: "manual_trigger",
@@ -982,6 +1024,34 @@ var groupDefinition = {
982
1024
  isOfficial: true,
983
1025
  testable: false
984
1026
  };
1027
+
1028
+ // src/catalog/general/query/types.ts
1029
+ var LogicalOperator;
1030
+ ((LogicalOperator) => {
1031
+ LogicalOperator["And"] = "and";
1032
+ LogicalOperator["Or"] = "or";
1033
+ })(LogicalOperator ||= {});
1034
+ var SortDirection;
1035
+ ((SortDirection) => {
1036
+ SortDirection["Ascending"] = "asc";
1037
+ SortDirection["Descending"] = "desc";
1038
+ })(SortDirection ||= {});
1039
+ var JoinType;
1040
+ ((JoinType) => {
1041
+ JoinType["Inner"] = "inner";
1042
+ JoinType["Left"] = "left";
1043
+ JoinType["Right"] = "right";
1044
+ JoinType["Full"] = "full";
1045
+ })(JoinType ||= {});
1046
+ var AggregateFunction;
1047
+ ((AggregateFunction) => {
1048
+ AggregateFunction["Count"] = "count";
1049
+ AggregateFunction["Sum"] = "sum";
1050
+ AggregateFunction["Average"] = "avg";
1051
+ AggregateFunction["Min"] = "min";
1052
+ AggregateFunction["Max"] = "max";
1053
+ AggregateFunction["CountDistinct"] = "count_distinct";
1054
+ })(AggregateFunction ||= {});
985
1055
  // src/catalog/general/query/config.ts
986
1056
  var BuildQueryConfigDefaults = {
987
1057
  mode: "visual" /* Visual */,
@@ -1003,6 +1073,31 @@ var BuildQueryConfigDefaults = {
1003
1073
  parameters: {}
1004
1074
  }
1005
1075
  };
1076
+ function isQueryableConnector(connector) {
1077
+ if (connector.isQueryable !== undefined) {
1078
+ return connector.isQueryable;
1079
+ }
1080
+ if (connector.category === "storage") {
1081
+ return false;
1082
+ }
1083
+ if (!connector.type) {
1084
+ return false;
1085
+ }
1086
+ const storageTypes = [
1087
+ "dropbox",
1088
+ "google_drive",
1089
+ "google-drive",
1090
+ "onedrive",
1091
+ "box",
1092
+ "ftp",
1093
+ "sftp",
1094
+ "s3",
1095
+ "azure_blob",
1096
+ "azure-blob",
1097
+ "storage"
1098
+ ];
1099
+ return !storageTypes.includes(connector.type.toLowerCase());
1100
+ }
1006
1101
 
1007
1102
  // src/catalog/general/query/index.ts
1008
1103
  var queryDefinition = {
@@ -2872,12 +2967,489 @@ function getEdgeValidationStatus(edgeId, validationResult) {
2872
2967
  return "warning";
2873
2968
  return "valid";
2874
2969
  }
2970
+ // src/catalog/logic/condition/config.ts
2971
+ var CONDITION_OPERATORS = [
2972
+ { id: "equals", label: "Equals", needsValue: true },
2973
+ { id: "not_equals", label: "Not Equals", needsValue: true },
2974
+ { id: "greater_than", label: "Greater Than", needsValue: true },
2975
+ { id: "greater_than_equals", label: "Greater Than or Equals", needsValue: true },
2976
+ { id: "less_than", label: "Less Than", needsValue: true },
2977
+ { id: "less_than_equals", label: "Less Than or Equals", needsValue: true },
2978
+ { id: "like", label: "Like", needsValue: true },
2979
+ { id: "contains", label: "Contains", needsValue: true },
2980
+ { id: "starts_with", label: "Starts With", needsValue: true },
2981
+ { id: "ends_with", label: "Ends With", needsValue: true },
2982
+ { id: "is_null", label: "Is Null", needsValue: false },
2983
+ { id: "is_not_null", label: "Is Not Null", needsValue: false },
2984
+ { id: "is_empty", label: "Is Empty", needsValue: false }
2985
+ ];
2986
+ // src/catalog/logic/for-each/pathOptions.ts
2987
+ function inferSchemaFromSample(sample) {
2988
+ const properties = {};
2989
+ for (const [key, value] of Object.entries(sample)) {
2990
+ if (value === null || value === undefined) {
2991
+ properties[key] = { type: "string" };
2992
+ } else if (Array.isArray(value)) {
2993
+ if (value.length > 0 && typeof value[0] === "object" && value[0] !== null) {
2994
+ properties[key] = { type: "array", items: inferSchemaFromSample(value[0]) };
2995
+ } else {
2996
+ properties[key] = { type: "array" };
2997
+ }
2998
+ } else if (typeof value === "object") {
2999
+ properties[key] = inferSchemaFromSample(value);
3000
+ } else if (typeof value === "number") {
3001
+ properties[key] = { type: "number" };
3002
+ } else if (typeof value === "boolean") {
3003
+ properties[key] = { type: "boolean" };
3004
+ } else {
3005
+ properties[key] = { type: "string" };
3006
+ }
3007
+ }
3008
+ return { type: "object", properties };
3009
+ }
3010
+ function collectArrayPaths(schema, prefix, add, depth = 0) {
3011
+ if (!schema || typeof schema !== "object" || depth > 8)
3012
+ return;
3013
+ if (schema.type === "array") {
3014
+ if (prefix) {
3015
+ const itemsType = schema.items?.type;
3016
+ const label = itemsType === "object" ? "array of objects" : "array";
3017
+ add(prefix, `${prefix} (${label})`, `Iterate over "${prefix}"`);
3018
+ }
3019
+ if (schema.items) {
3020
+ collectArrayPaths(schema.items, prefix, add, depth + 1);
3021
+ }
3022
+ return;
3023
+ }
3024
+ if (schema.type === "object" && schema.properties) {
3025
+ for (const [key, child] of Object.entries(schema.properties)) {
3026
+ const next = prefix ? `${prefix}.${key}` : key;
3027
+ collectArrayPaths(child, next, add, depth + 1);
3028
+ }
3029
+ }
3030
+ }
3031
+ function collectArrayPathsFromSample(sample, prefix, add, depth = 0) {
3032
+ if (!sample || typeof sample !== "object" || depth > 8)
3033
+ return;
3034
+ if (Array.isArray(sample)) {
3035
+ if (prefix) {
3036
+ const itemsObj = sample.length > 0 && typeof sample[0] === "object" && sample[0] !== null;
3037
+ const label = itemsObj ? "array of objects" : "array";
3038
+ add(prefix, `${prefix} (${label})`, `Iterate over "${prefix}"`);
3039
+ }
3040
+ if (sample.length > 0) {
3041
+ collectArrayPathsFromSample(sample[0], prefix, add, depth + 1);
3042
+ }
3043
+ return;
3044
+ }
3045
+ for (const [key, value] of Object.entries(sample)) {
3046
+ const next = prefix ? `${prefix}.${key}` : key;
3047
+ collectArrayPathsFromSample(value, next, add, depth + 1);
3048
+ }
3049
+ }
3050
+ function buildPathOptions(upstreamNodes) {
3051
+ const paths = [];
3052
+ const seen = new Set;
3053
+ const add = (id, text, description) => {
3054
+ if (seen.has(id))
3055
+ return;
3056
+ seen.add(id);
3057
+ paths.push({ id, text, description });
3058
+ };
3059
+ if (upstreamNodes) {
3060
+ for (const node of upstreamNodes) {
3061
+ const label = node.label || node.type || "Upstream";
3062
+ const queryTable = node.config?.visualConfig?.tables?.[0];
3063
+ add("data", queryTable ? `${queryTable} (${label})` : `All records (${label})`, queryTable ? `Iterate over every ${queryTable} record` : "Iterate over every record from upstream");
3064
+ const schema = node.outputSchema || node.config?._outputSchema;
3065
+ if (schema)
3066
+ collectArrayPaths(schema, "", add);
3067
+ const output = node.executionData?.output;
3068
+ if (output) {
3069
+ const records = Array.isArray(output.data) ? output.data : Array.isArray(output) ? output : null;
3070
+ if (records && records.length > 0 && typeof records[0] === "object") {
3071
+ collectArrayPathsFromSample(records[0], "", add);
3072
+ }
3073
+ }
3074
+ }
3075
+ }
3076
+ if (paths.length === 0) {
3077
+ add("data", "data (upstream output)");
3078
+ }
3079
+ return paths;
3080
+ }
3081
+ // src/catalog/actions/delay/migrate.ts
3082
+ var LEGACY_UNITS = new Set([
3083
+ "milliseconds",
3084
+ "seconds",
3085
+ "minutes",
3086
+ "hours"
3087
+ ]);
3088
+ function migrateDelayConfig(raw) {
3089
+ if (raw && typeof raw === "object" && "mode" in raw) {
3090
+ return raw;
3091
+ }
3092
+ const legacy = raw ?? {};
3093
+ const duration = typeof legacy.duration === "number" && Number.isFinite(legacy.duration) ? legacy.duration : 1;
3094
+ const unit = typeof legacy.unit === "string" && LEGACY_UNITS.has(legacy.unit) ? legacy.unit : "milliseconds";
3095
+ return {
3096
+ mode: "fixed",
3097
+ fixed: { duration, unit }
3098
+ };
3099
+ }
3100
+ // src/catalog/communication/email-send/config.ts
3101
+ var EmailConfig = {
3102
+ defaults: {
3103
+ connection: "",
3104
+ operation: "send",
3105
+ to: "",
3106
+ cc: "",
3107
+ bcc: "",
3108
+ subject: "",
3109
+ body: "",
3110
+ isHtml: false,
3111
+ attachments: [],
3112
+ folder: "Inbox",
3113
+ query: "",
3114
+ limit: 10
3115
+ },
3116
+ schema: {
3117
+ connection: { type: "connection", required: true },
3118
+ operation: { type: "select", required: true },
3119
+ to: { type: "string", required: false },
3120
+ cc: { type: "string", required: false },
3121
+ bcc: { type: "string", required: false },
3122
+ subject: { type: "string", required: false },
3123
+ body: { type: "string", required: false },
3124
+ isHtml: { type: "boolean", required: false },
3125
+ attachments: { type: "array", required: false },
3126
+ folder: { type: "string", required: false },
3127
+ query: { type: "string", required: false },
3128
+ limit: { type: "number", required: false }
3129
+ },
3130
+ validate: (config) => {
3131
+ const errors = [];
3132
+ if (!config.connection)
3133
+ errors.push("Email service connection is required");
3134
+ if (!config.operation)
3135
+ errors.push("Operation is required");
3136
+ if (config.operation === "send") {
3137
+ if (!config.to)
3138
+ errors.push("Recipient email is required for send operation");
3139
+ if (!config.subject)
3140
+ errors.push("Subject is required for send operation");
3141
+ if (!config.body)
3142
+ errors.push("Body is required for send operation");
3143
+ } else if (config.operation) {
3144
+ errors.push("Only Send is available for the Email action right now");
3145
+ }
3146
+ return { valid: errors.length === 0, errors };
3147
+ },
3148
+ merge: (userConfig) => {
3149
+ return { ...EmailConfig.defaults, ...userConfig };
3150
+ },
3151
+ getLabel: (config) => {
3152
+ switch (config.operation) {
3153
+ case "send":
3154
+ return `Email: Send to ${config.to}`;
3155
+ case "read":
3156
+ return `Email: Read from ${config.folder}`;
3157
+ case "search":
3158
+ return `Email: Search "${config.query}"`;
3159
+ case "delete":
3160
+ return `Email: Delete from ${config.folder}`;
3161
+ case "move":
3162
+ return `Email: Move to ${config.folder}`;
3163
+ case "mark_read":
3164
+ return `Email: Mark as read`;
3165
+ default:
3166
+ return `Email: ${config.operation}`;
3167
+ }
3168
+ }
3169
+ };
3170
+ // src/catalog/general/transform/config.ts
3171
+ var TRANSFORM_OPERATIONS = [
3172
+ { id: "upsert", label: "Upsert" },
3173
+ { id: "insert", label: "Insert" },
3174
+ { id: "update", label: "Update" },
3175
+ { id: "delete", label: "Delete" }
3176
+ ];
3177
+ var DATABASE_CONNECTOR_IDS = new Set(["mongodb", "postgresql", "mysql", "sqlite", "google_sheets"]);
3178
+ var TransformConfig = {
3179
+ defaults: {
3180
+ name: "Transform",
3181
+ operation: "upsert",
3182
+ connectorId: "",
3183
+ database: "",
3184
+ objectName: "",
3185
+ mode: "visual",
3186
+ mappings: [],
3187
+ expression: "",
3188
+ fieldDefaults: {},
3189
+ upsertMatchKey: "",
3190
+ sheetHeaders: [],
3191
+ createTabIfMissing: true,
3192
+ writeHeaderRow: true,
3193
+ clearBeforeWrite: false,
3194
+ sheetCustomLayout: false,
3195
+ sheetHeaderRow: 1,
3196
+ sheetColumns: ""
3197
+ },
3198
+ schema: {
3199
+ operation: { type: "select", required: true },
3200
+ connectorId: { type: "connection", required: true },
3201
+ database: { type: "text", required: false },
3202
+ objectName: { type: "select", required: true },
3203
+ mode: { type: "select", required: true },
3204
+ mappings: { type: "array", required: false },
3205
+ expression: { type: "code", required: false }
3206
+ },
3207
+ generateName: (operation, connectorLabel, objectName) => {
3208
+ const opLabel = TRANSFORM_OPERATIONS.find((o) => o.id === operation)?.label || "Transform";
3209
+ const target = connectorLabel || "Target";
3210
+ const obj = objectName || "Object";
3211
+ return `${opLabel} → ${target} · ${obj}`;
3212
+ },
3213
+ validate: (config) => {
3214
+ const errors = [];
3215
+ if (!config.operation) {
3216
+ errors.push("Operation is required");
3217
+ }
3218
+ if (!config.connectorId) {
3219
+ errors.push("Target connector is required");
3220
+ }
3221
+ if (!config.objectName) {
3222
+ errors.push("Object (table) is required");
3223
+ }
3224
+ const isLegacyExpression = config.mode === "expression" && !!config.expression?.trim();
3225
+ if (!isLegacyExpression) {
3226
+ if (!config.mappings || config.mappings.length === 0) {
3227
+ errors.push("At least one field mapping is required");
3228
+ } else {
3229
+ config.mappings.forEach((mapping, index) => {
3230
+ if (!mapping.sourceField) {
3231
+ errors.push(`Mapping ${index + 1}: Source field is required`);
3232
+ }
3233
+ if (!mapping.targetField) {
3234
+ errors.push(`Mapping ${index + 1}: Target field is required`);
3235
+ }
3236
+ });
3237
+ }
3238
+ }
3239
+ return { valid: errors.length === 0, errors };
3240
+ },
3241
+ merge: (userConfig) => {
3242
+ return { ...TransformConfig.defaults, ...userConfig };
3243
+ },
3244
+ getLabel: (config) => {
3245
+ const count = config.mappings?.length ?? 0;
3246
+ return `${config.name || "Transform"} (${count} field${count !== 1 ? "s" : ""})`;
3247
+ },
3248
+ createMappingId: () => {
3249
+ return `map_${Date.now()}_${randomSuffix(5)}`;
3250
+ }
3251
+ };
3252
+ // src/catalog/storage/storageCapabilities.ts
3253
+ var contentTypeField = {
3254
+ key: "contentType",
3255
+ label: "Content type",
3256
+ type: "string",
3257
+ placeholder: "application/octet-stream",
3258
+ advanced: true,
3259
+ hint: "MIME type for the uploaded file."
3260
+ };
3261
+ var STORAGE_PROVIDERS = {
3262
+ aws_s3: {
3263
+ connectorId: "aws_s3",
3264
+ label: "Amazon S3",
3265
+ browse: true,
3266
+ operations: [
3267
+ { value: "list", label: "List files", path: { key: "prefix", label: "Folder", selectMode: "folder" }, fields: [{ key: "maxKeys", label: "Max results", type: "number", defaultValue: 100, advanced: true }] },
3268
+ { value: "download", label: "Download file", path: { key: "key", label: "File", selectMode: "both", required: true } },
3269
+ { value: "upload", label: "Upload file", path: { key: "key", label: "Destination key", selectMode: "both", required: true }, content: true, fields: [contentTypeField] },
3270
+ { value: "delete", label: "Delete file", path: { key: "key", label: "File", selectMode: "both", required: true } },
3271
+ { value: "copy", label: "Copy file", path: { key: "key", label: "Source file", selectMode: "both", required: true }, dest: { key: "destKey", label: "Destination key", selectMode: "both", required: true } },
3272
+ { value: "move", label: "Move / rename", path: { key: "key", label: "Source file", selectMode: "both", required: true }, dest: { key: "destKey", label: "Destination key", selectMode: "both", required: true } },
3273
+ { value: "presign", label: "Get signed URL", path: { key: "key", label: "File", selectMode: "both", required: true }, fields: [{ key: "expiresIn", label: "Expiry (seconds)", type: "number", defaultValue: 3600, advanced: true }] }
3274
+ ]
3275
+ },
3276
+ ftp_sftp: {
3277
+ connectorId: "ftp_sftp",
3278
+ label: "FTP / SFTP",
3279
+ browse: true,
3280
+ operations: [
3281
+ { value: "list", label: "List files", path: { key: "remotePath", label: "Folder", selectMode: "folder" } },
3282
+ { value: "download", label: "Download file", path: { key: "remotePath", label: "File", selectMode: "both", required: true } },
3283
+ { value: "upload", label: "Upload file", path: { key: "remotePath", label: "Destination path", selectMode: "both", required: true }, content: true },
3284
+ { value: "delete", label: "Delete file", path: { key: "remotePath", label: "File", selectMode: "both", required: true } },
3285
+ { value: "move", label: "Move / rename", path: { key: "remotePath", label: "Source file", selectMode: "both", required: true }, dest: { key: "destPath", label: "Destination path", selectMode: "both", required: true } }
3286
+ ]
3287
+ },
3288
+ dropbox: {
3289
+ connectorId: "dropbox",
3290
+ label: "Dropbox",
3291
+ browse: true,
3292
+ operations: [
3293
+ { value: "list", label: "List files", path: { key: "path", label: "Folder", selectMode: "folder" } },
3294
+ { value: "download", label: "Download file", path: { key: "path", label: "File", selectMode: "both", required: true } },
3295
+ { value: "upload", label: "Upload file", path: { key: "path", label: "Destination path", selectMode: "both", required: true }, content: true, fields: [{ key: "mode", label: "Write mode", type: "select", options: [{ label: "Overwrite", value: "overwrite" }, { label: "Add (keep both)", value: "add" }], defaultValue: "overwrite", advanced: true }] },
3296
+ { value: "delete", label: "Delete file", path: { key: "path", label: "File", selectMode: "both", required: true } },
3297
+ { value: "copy", label: "Copy file", path: { key: "path", label: "Source file", selectMode: "both", required: true }, dest: { key: "destPath", label: "Destination path", selectMode: "both", required: true } },
3298
+ { value: "move", label: "Move / rename", path: { key: "path", label: "Source file", selectMode: "both", required: true }, dest: { key: "destPath", label: "Destination path", selectMode: "both", required: true } }
3299
+ ]
3300
+ },
3301
+ onedrive: {
3302
+ connectorId: "onedrive",
3303
+ label: "OneDrive",
3304
+ browse: true,
3305
+ operations: [
3306
+ { value: "list", label: "List files", path: { key: "path", label: "Folder", selectMode: "folder" } },
3307
+ { value: "download", label: "Download file", path: { key: "path", label: "File", selectMode: "both", required: true } },
3308
+ { value: "upload", label: "Upload file", path: { key: "path", label: "Destination path", selectMode: "both", required: true }, content: true, fields: [contentTypeField] },
3309
+ { value: "delete", label: "Delete file", path: { key: "path", label: "File", selectMode: "both", required: true } },
3310
+ { value: "copy", label: "Copy file", path: { key: "path", label: "Source file", selectMode: "both", required: true }, dest: { key: "destPath", label: "Destination path", selectMode: "both", required: true } },
3311
+ { value: "move", label: "Move / rename", path: { key: "path", label: "Source file", selectMode: "both", required: true }, dest: { key: "destPath", label: "Destination path", selectMode: "both", required: true } }
3312
+ ]
3313
+ },
3314
+ google_drive: {
3315
+ connectorId: "google_drive",
3316
+ label: "Google Drive",
3317
+ browse: true,
3318
+ operations: [
3319
+ { value: "list", label: "List files", path: { key: "folderId", label: "Folder", selectMode: "folder" }, fields: [{ key: "query", label: "Search query", type: "string", advanced: true }, { key: "pageSize", label: "Max results", type: "number", defaultValue: 50, advanced: true }] },
3320
+ { value: "download", label: "Download file", path: { key: "fileId", label: "File", selectMode: "both", required: true } },
3321
+ { value: "upload", label: "Upload file", path: { key: "folderId", label: "Destination folder", selectMode: "both" }, content: true, fields: [{ key: "fileName", label: "File name", type: "string", required: true }, { key: "mimeType", label: "Content type", type: "string", advanced: true }] },
3322
+ { value: "delete", label: "Delete file", path: { key: "fileId", label: "File", selectMode: "both", required: true } },
3323
+ { value: "move", label: "Move", path: { key: "fileId", label: "File", selectMode: "both", required: true }, dest: { key: "destFolderId", label: "Destination folder", selectMode: "both", required: true } },
3324
+ { value: "copy", label: "Copy", path: { key: "fileId", label: "File", selectMode: "both", required: true }, dest: { key: "destFolderId", label: "Destination folder", selectMode: "both" }, fields: [{ key: "fileName", label: "New name", type: "string" }] },
3325
+ { value: "share", label: "Share", path: { key: "fileId", label: "File", selectMode: "both", required: true }, fields: [{ key: "shareEmail", label: "Share with (email)", type: "string", required: true }, { key: "shareRole", label: "Role", type: "select", options: [{ label: "Viewer", value: "reader" }, { label: "Editor", value: "writer" }], defaultValue: "reader" }] },
3326
+ { value: "create_folder", label: "Create folder", path: { key: "folderId", label: "Parent folder", selectMode: "both" }, fields: [{ key: "fileName", label: "Folder name", type: "string", required: true }] }
3327
+ ]
3328
+ },
3329
+ gcs: {
3330
+ connectorId: "gcs",
3331
+ label: "Google Cloud Storage",
3332
+ browse: true,
3333
+ operations: [
3334
+ { value: "list", label: "List files", path: { key: "prefix", label: "Folder", selectMode: "folder" }, fields: [{ key: "maxKeys", label: "Max results", type: "number", defaultValue: 100, advanced: true }] },
3335
+ { value: "download", label: "Download file", path: { key: "key", label: "File", selectMode: "both", required: true } },
3336
+ { value: "upload", label: "Upload file", path: { key: "key", label: "Destination key", selectMode: "both", required: true }, content: true, fields: [contentTypeField] },
3337
+ { value: "delete", label: "Delete file", path: { key: "key", label: "File", selectMode: "both", required: true } },
3338
+ { value: "copy", label: "Copy file", path: { key: "key", label: "Source file", selectMode: "both", required: true }, dest: { key: "destKey", label: "Destination key", selectMode: "both", required: true } },
3339
+ { value: "move", label: "Move / rename", path: { key: "key", label: "Source file", selectMode: "both", required: true }, dest: { key: "destKey", label: "Destination key", selectMode: "both", required: true } },
3340
+ { value: "presign", label: "Get signed URL", path: { key: "key", label: "File", selectMode: "both", required: true }, fields: [{ key: "expiresIn", label: "Expiry (seconds)", type: "number", defaultValue: 3600, advanced: true }] }
3341
+ ]
3342
+ },
3343
+ azure_blob: {
3344
+ connectorId: "azure_blob",
3345
+ label: "Azure Blob Storage",
3346
+ browse: true,
3347
+ operations: [
3348
+ { value: "list", label: "List files", path: { key: "prefix", label: "Folder", selectMode: "folder" }, fields: [{ key: "maxKeys", label: "Max results", type: "number", defaultValue: 100, advanced: true }] },
3349
+ { value: "download", label: "Download file", path: { key: "blob", label: "File", selectMode: "both", required: true } },
3350
+ { value: "upload", label: "Upload file", path: { key: "blob", label: "Destination blob", selectMode: "both", required: true }, content: true, fields: [contentTypeField] },
3351
+ { value: "delete", label: "Delete file", path: { key: "blob", label: "File", selectMode: "both", required: true } },
3352
+ { value: "copy", label: "Copy file", path: { key: "blob", label: "Source blob", selectMode: "both", required: true }, dest: { key: "destBlob", label: "Destination blob", selectMode: "both", required: true } },
3353
+ { value: "move", label: "Move / rename", path: { key: "blob", label: "Source blob", selectMode: "both", required: true }, dest: { key: "destBlob", label: "Destination blob", selectMode: "both", required: true } },
3354
+ { value: "presign", label: "Get signed URL", path: { key: "blob", label: "File", selectMode: "both", required: true }, fields: [{ key: "expiresIn", label: "Expiry (seconds)", type: "number", defaultValue: 3600, advanced: true }] }
3355
+ ]
3356
+ }
3357
+ };
3358
+ STORAGE_PROVIDERS.google_sheets = { ...STORAGE_PROVIDERS.google_drive, connectorId: "google_sheets" };
3359
+ var transferOp = {
3360
+ value: "transfer",
3361
+ label: "Transfer to another connector",
3362
+ path: { key: "path", label: "Source file or folder", selectMode: "both", required: true }
3363
+ };
3364
+ for (const id of ["aws_s3", "ftp_sftp", "dropbox", "onedrive", "google_drive", "gcs", "azure_blob"]) {
3365
+ STORAGE_PROVIDERS[id].operations.push(transferOp);
3366
+ }
3367
+ var syncOp = {
3368
+ value: "sync",
3369
+ label: "Sync folder to another connector",
3370
+ path: { key: "path", label: "Source folder", selectMode: "both", required: true },
3371
+ fields: [
3372
+ {
3373
+ key: "ifExists",
3374
+ label: "If a file already exists",
3375
+ type: "select",
3376
+ options: [
3377
+ { label: "Overwrite", value: "overwrite" },
3378
+ { label: "Skip it", value: "skip" },
3379
+ { label: "Keep both (rename)", value: "rename" }
3380
+ ],
3381
+ defaultValue: "overwrite",
3382
+ hint: "What to do when the destination already has the file."
3383
+ },
3384
+ {
3385
+ key: "onError",
3386
+ label: "On file error",
3387
+ type: "select",
3388
+ options: [
3389
+ { label: "Stop the sync", value: "abort" },
3390
+ { label: "Skip the file & continue", value: "skip" }
3391
+ ],
3392
+ defaultValue: "abort",
3393
+ hint: "A bad file (timeout, lock, permission) stops the whole sync, or is skipped."
3394
+ }
3395
+ ]
3396
+ };
3397
+ for (const id of ["aws_s3", "ftp_sftp", "dropbox", "onedrive", "google_drive", "gcs", "azure_blob"]) {
3398
+ STORAGE_PROVIDERS[id].operations.push(syncOp);
3399
+ }
3400
+ var STORAGE_CONNECTOR_IDS = Object.keys(STORAGE_PROVIDERS).filter((id) => id !== "google_sheets");
3401
+ function getStorageProvider(connectorId) {
3402
+ if (!connectorId)
3403
+ return;
3404
+ return STORAGE_PROVIDERS[connectorId] ?? STORAGE_PROVIDERS[connectorId.replace(/-/g, "_")];
3405
+ }
3406
+ function storageConfigKeys() {
3407
+ const keys = new Set([
3408
+ "operation",
3409
+ "connector",
3410
+ "content",
3411
+ "outputAs",
3412
+ "path",
3413
+ "destPath",
3414
+ "destConnector",
3415
+ "sourceIsDirectory",
3416
+ "cursor",
3417
+ "fileBudget",
3418
+ "timeBudgetMs"
3419
+ ]);
3420
+ for (const provider of Object.values(STORAGE_PROVIDERS)) {
3421
+ for (const op of provider.operations) {
3422
+ if (op.path)
3423
+ keys.add(op.path.key);
3424
+ if (op.dest)
3425
+ keys.add(op.dest.key);
3426
+ for (const f of op.fields ?? [])
3427
+ keys.add(f.key);
3428
+ }
3429
+ }
3430
+ return [...keys];
3431
+ }
2875
3432
  export {
2876
3433
  ActionCategory,
2877
3434
  ActionStatus,
2878
3435
  ActionType,
3436
+ AggregateFunction,
3437
+ CONDITION_OPERATORS,
3438
+ DATABASE_CONNECTOR_IDS,
3439
+ EmailConfig,
2879
3440
  FieldType,
3441
+ JoinType,
3442
+ LogicalOperator,
3443
+ ManualTriggerConfig,
3444
+ STORAGE_CONNECTOR_IDS,
3445
+ SortDirection,
3446
+ TRANSFORM_OPERATIONS,
3447
+ TransformConfig,
2880
3448
  actionsRegistry,
3449
+ buildPathOptions,
3450
+ collectArrayPaths,
3451
+ collectArrayPathsFromSample,
3452
+ emailDefinition,
2881
3453
  generateEdgeId,
2882
3454
  generateStepId,
2883
3455
  generateUUIDv4,
@@ -2888,6 +3460,12 @@ export {
2888
3460
  getEdgeValidationStatus,
2889
3461
  getNodeConfigStatus,
2890
3462
  getNodeValidationStatus,
3463
+ getStorageProvider,
3464
+ inferSchemaFromSample,
3465
+ isQueryableConnector,
3466
+ migrateDelayConfig,
3467
+ queryDefinition,
3468
+ storageConfigKeys,
2891
3469
  validateEdge,
2892
3470
  validateNode,
2893
3471
  validateWorkflow
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@octaviaflow/flow-rules",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "description": "The flow action catalog and the rules engine behind Flow Doctor — 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
6
  "main": "./dist/index.js",