{ "kind": "open-flow-workflow-bundle", "version": 1, "entryFlowId": "wordpress-post-categorizer", "workflows": [ { "flowId": "wordpress-post-categorizer", "file": { "kind": "open-flow-workflow", "version": 1, "name": "WordPress Post Categorizer", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_40e75fbfa953ccad": { "name": "Site category taxonomy", "kind": "start", "inputDefs": [ { "jsonSchema": { "type": "string", "minLength": 2 }, "nullable": false, "handle": "taxonomyJson" } ] }, "n_ea9b28ebea0ce728": { "inputs": { "taxonomyJson": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_40e75fbfa953ccad", "output": "taxonomyJson" } ] } }, "name": "Validate existing category IDs", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "taxonomyJson" } ], "outputs": [ { "jsonSchema": { "type": "array", "minItems": 1, "items": { "type": "object", "properties": { "id": { "type": "integer", "minimum": 1 }, "name": { "type": "string", "minLength": 1 } }, "required": [ "id", "name" ], "additionalProperties": false } }, "nullable": false, "handle": "taxonomy" } ], "name": "Validate existing category IDs", "moduleId": "n_17bc10fd7c869276", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_93ea75cbb9f1ce05": { "inputs": { "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_93ea75cbb9f1ce05", "output": "state" } ] }, "pagePosts": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_7d2b360ab98485a0", "output": "posts" } ] }, "pagination": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_7d2b360ab98485a0", "output": "pagination" } ] } }, "name": "Collect WordPress post pages", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object" }, "nullable": true, "handle": "previousState" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "integer", "exclusiveMinimum": 0, "description": "The numeric WordPress post ID." }, "slug": { "type": "string", "description": "The WordPress post slug." }, "status": { "type": "string", "description": "The WordPress post status." }, "title": { "type": "object", "properties": { "rendered": { "type": "string", "description": "The rendered HTML value returned by WordPress." } }, "additionalProperties": true, "description": "A WordPress rendered object." } }, "required": [ "id", "slug", "status", "title" ], "additionalProperties": true, "description": "A WordPress post object." }, "description": "The WordPress posts returned by the list request." }, "nullable": true, "handle": "pagePosts" }, { "jsonSchema": { "type": "object", "properties": { "total": { "type": [ "integer", "null" ], "description": "The total number of matching WordPress resources." }, "totalPages": { "type": [ "integer", "null" ], "description": "The total number of WordPress result pages." } }, "required": [ "total", "totalPages" ], "additionalProperties": false, "description": "Pagination metadata returned in WordPress response headers." }, "nullable": true, "handle": "pagination" } ], "outputs": [ { "jsonSchema": { "type": "object", "properties": { "posts": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "integer", "minimum": 1 }, "title": { "type": "string" } }, "required": [ "id", "title" ], "additionalProperties": false } }, "page": { "type": "integer", "minimum": 0 } }, "required": [ "posts", "page" ], "additionalProperties": false }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "integer", "minimum": 1 }, "nullable": false, "handle": "nextPage" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" } ], "name": "Collect WordPress post pages", "moduleId": "n_f1667c960fbcaea3", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_9ad12eaea79a48c2": { "inputs": {}, "name": "More WordPress pages", "kind": "condition", "matchMode": "first", "cases": [ { "output": "more", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_93ea75cbb9f1ce05", "output": "more" } }, "operator": "==", "right": { "kind": "value", "value": true } } ] } ] } ] }, "n_7d2b360ab98485a0": { "inputs": { "page": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_93ea75cbb9f1ce05", "output": "nextPage" } ] }, "perPage": { "kind": "value", "value": 10 } }, "name": "Read one WordPress post page", "kind": "task", "task": { "inputs": [ { "description": "Limit results to resources matching this search string.", "jsonSchema": { "type": "string", "minLength": 1, "description": "Limit results to resources matching this search string." }, "nullable": true, "handle": "search", "value": null }, { "description": "The page number of WordPress results to return.", "jsonSchema": { "type": "integer", "exclusiveMinimum": 0, "description": "The page number of WordPress results to return." }, "nullable": true, "handle": "page", "value": null }, { "description": "The maximum number of WordPress results to return.", "jsonSchema": { "type": "integer", "minimum": 1, "maximum": 100, "description": "The maximum number of WordPress results to return." }, "nullable": true, "handle": "perPage", "value": null }, { "description": "The sort direction for WordPress list results.", "jsonSchema": { "type": "string", "enum": [ "asc", "desc" ], "description": "The sort direction for WordPress list results." }, "nullable": true, "handle": "order", "value": null }, { "description": "WordPress statuses to include.", "jsonSchema": { "type": "array", "items": { "type": "string", "minLength": 1, "description": "A WordPress post status to include, including trash or any." }, "minItems": 1, "description": "WordPress statuses to include." }, "nullable": true, "handle": "status", "value": null }, { "description": "Category IDs used to filter WordPress posts.", "jsonSchema": { "type": "array", "items": { "type": "integer", "exclusiveMinimum": 0, "description": "One numeric WordPress resource ID." }, "minItems": 1, "description": "Category IDs used to filter WordPress posts." }, "nullable": true, "handle": "categories", "value": null }, { "description": "Tag IDs used to filter WordPress posts.", "jsonSchema": { "type": "array", "items": { "type": "integer", "exclusiveMinimum": 0, "description": "One numeric WordPress resource ID." }, "minItems": 1, "description": "Tag IDs used to filter WordPress posts." }, "nullable": true, "handle": "tags", "value": null }, { "description": "Post IDs to include in the response.", "jsonSchema": { "type": "array", "items": { "type": "integer", "exclusiveMinimum": 0, "description": "One numeric WordPress resource ID." }, "minItems": 1, "description": "Post IDs to include in the response." }, "nullable": true, "handle": "include", "value": null }, { "description": "Post IDs to exclude from the response.", "jsonSchema": { "type": "array", "items": { "type": "integer", "exclusiveMinimum": 0, "description": "One numeric WordPress resource ID." }, "minItems": 1, "description": "Post IDs to exclude from the response." }, "nullable": true, "handle": "exclude", "value": null }, { "description": "Author user IDs used to filter WordPress posts.", "jsonSchema": { "type": "array", "items": { "type": "integer", "exclusiveMinimum": 0, "description": "One numeric WordPress resource ID." }, "minItems": 1, "description": "Author user IDs used to filter WordPress posts." }, "nullable": true, "handle": "author", "value": null }, { "description": "Post slugs used to filter WordPress posts.", "jsonSchema": { "type": "array", "items": { "type": "string", "pattern": "\\S", "minLength": 1, "description": "The WordPress URL slug." }, "minItems": 1, "description": "Post slugs used to filter WordPress posts." }, "nullable": true, "handle": "slug", "value": null }, { "description": "The field used to sort WordPress posts or pages.", "jsonSchema": { "type": "string", "enum": [ "author", "date", "id", "include", "modified", "parent", "relevance", "slug", "include_slugs", "title" ], "description": "The field used to sort WordPress posts or pages." }, "nullable": true, "handle": "orderby", "value": null } ], "outputs": [ { "description": "The WordPress posts returned by the list request.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "integer", "exclusiveMinimum": 0, "description": "The numeric WordPress post ID." }, "slug": { "type": "string", "description": "The WordPress post slug." }, "status": { "type": "string", "description": "The WordPress post status." }, "title": { "type": "object", "properties": { "rendered": { "type": "string", "description": "The rendered HTML value returned by WordPress." } }, "additionalProperties": true, "description": "A WordPress rendered object." } }, "required": [ "id", "slug", "status", "title" ], "additionalProperties": true, "description": "A WordPress post object." }, "description": "The WordPress posts returned by the list request." }, "nullable": false, "handle": "posts" }, { "description": "Pagination metadata returned in WordPress response headers.", "jsonSchema": { "type": "object", "properties": { "total": { "type": [ "integer", "null" ], "description": "The total number of matching WordPress resources." }, "totalPages": { "type": [ "integer", "null" ], "description": "The total number of WordPress result pages." } }, "required": [ "total", "totalPages" ], "additionalProperties": false, "description": "Pagination metadata returned in WordPress response headers." }, "nullable": false, "handle": "pagination" } ], "name": "Read one WordPress post page", "executor": { "kind": "connector", "action": "wordpress.list_posts" } } }, "n_fdbf64754ee23b5d": { "inputs": { "posts": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_93ea75cbb9f1ce05", "output": "state", "field": "posts" } ] }, "taxonomy": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_ea9b28ebea0ce728", "output": "taxonomy" } ] }, "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_fdbf64754ee23b5d", "output": "state" } ] }, "category": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "takfnrsm5m", "output": "category" } ] } }, "name": "Collect validated classifications", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "integer", "minimum": 1 }, "title": { "type": "string" } }, "required": [ "id", "title" ], "additionalProperties": false } }, "nullable": false, "handle": "posts" }, { "jsonSchema": { "type": "array", "minItems": 1, "items": { "type": "object", "properties": { "id": { "type": "integer", "minimum": 1 }, "name": { "type": "string", "minLength": 1 } }, "required": [ "id", "name" ], "additionalProperties": false } }, "nullable": false, "handle": "taxonomy" }, { "jsonSchema": { "type": "object" }, "nullable": true, "handle": "previousState" }, { "jsonSchema": { "type": "object", "properties": { "id": { "type": "integer", "minimum": 1 }, "categoryId": { "type": "integer", "minimum": 1 } }, "required": [ "id", "categoryId" ], "additionalProperties": false }, "nullable": true, "handle": "category" } ], "outputs": [ { "jsonSchema": { "type": "object", "properties": { "categories": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "integer", "minimum": 1 }, "categoryId": { "type": "integer", "minimum": 1 } }, "required": [ "id", "categoryId" ], "additionalProperties": false } }, "index": { "type": "integer", "minimum": 0 } }, "required": [ "categories", "index" ], "additionalProperties": false }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "index" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" } ], "name": "Collect validated classifications", "moduleId": "n_ebd28b504d696a18", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_a910d8a8d166d856": { "inputs": {}, "name": "More titles to classify", "kind": "condition", "matchMode": "first", "cases": [ { "output": "more", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_fdbf64754ee23b5d", "output": "more" } }, "operator": "==", "right": { "kind": "value", "value": true } } ] } ] } ] }, "n_6287d728fc322cb7": { "inputs": { "categories": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_fdbf64754ee23b5d", "output": "state", "field": "categories" } ] }, "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_6287d728fc322cb7", "output": "state" } ] } }, "name": "Track category update progress", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "integer", "minimum": 1 }, "categoryId": { "type": "integer", "minimum": 1 } }, "required": [ "id", "categoryId" ], "additionalProperties": false } }, "nullable": false, "handle": "categories" }, { "jsonSchema": { "type": "object" }, "nullable": true, "handle": "previousState" } ], "outputs": [ { "jsonSchema": { "type": "object", "properties": { "index": { "type": "integer", "minimum": 0 } }, "required": [ "index" ], "additionalProperties": false }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "integer", "minimum": 1 }, "nullable": false, "handle": "id" }, { "jsonSchema": { "type": "array", "items": { "type": "integer", "minimum": 1 } }, "nullable": false, "handle": "categoryIds" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" }, { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "count" } ], "name": "Track category update progress", "moduleId": "n_4c93320486819fee", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_9c154c226ca1ce67": { "inputs": {}, "name": "More categories to apply", "kind": "condition", "matchMode": "first", "cases": [ { "output": "more", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_6287d728fc322cb7", "output": "more" } }, "operator": "==", "right": { "kind": "value", "value": true } } ] } ] } ] }, "n_84466ead2fa83422": { "inputs": { "id": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_6287d728fc322cb7", "output": "id" } ] }, "categories": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_6287d728fc322cb7", "output": "categoryIds" } ] } }, "name": "Apply one validated category", "kind": "task", "task": { "inputs": [ { "description": "The post title.", "jsonSchema": { "type": "string", "description": "The post title." }, "nullable": true, "handle": "title", "value": null }, { "description": "The post content.", "jsonSchema": { "type": "string", "description": "The post content." }, "nullable": true, "handle": "content", "value": null }, { "description": "The post excerpt.", "jsonSchema": { "type": "string", "description": "The post excerpt." }, "nullable": true, "handle": "excerpt", "value": null }, { "description": "The WordPress URL slug.", "jsonSchema": { "type": "string", "pattern": "\\S", "minLength": 1, "description": "The WordPress URL slug." }, "nullable": true, "handle": "slug", "value": null }, { "description": "The WordPress publication status.", "jsonSchema": { "type": "string", "enum": [ "publish", "future", "draft", "pending", "private" ], "description": "The WordPress publication status." }, "nullable": true, "handle": "status", "value": null }, { "description": "Category IDs assigned to the post; an empty array clears all assignments.", "jsonSchema": { "type": "array", "items": { "type": "integer", "exclusiveMinimum": 0, "description": "The numeric WordPress resource ID." }, "description": "Category IDs assigned to the post; an empty array clears all assignments." }, "nullable": true, "handle": "categories", "value": null }, { "description": "Tag IDs assigned to the post; an empty array clears all assignments.", "jsonSchema": { "type": "array", "items": { "type": "integer", "exclusiveMinimum": 0, "description": "The numeric WordPress resource ID." }, "description": "Tag IDs assigned to the post; an empty array clears all assignments." }, "nullable": true, "handle": "tags", "value": null }, { "description": "The featured media attachment ID; zero removes the featured image.", "jsonSchema": { "type": "integer", "minimum": 0, "description": "The featured media attachment ID; zero removes the featured image." }, "nullable": true, "handle": "featuredMedia", "value": null }, { "description": "Meta fields to send to WordPress.", "jsonSchema": { "type": "object", "properties": {}, "additionalProperties": true, "description": "Meta fields to send to WordPress." }, "nullable": true, "handle": "meta", "value": null }, { "description": "The numeric WordPress resource ID.", "jsonSchema": { "type": "integer", "exclusiveMinimum": 0, "description": "The numeric WordPress resource ID." }, "nullable": false, "handle": "id" } ], "outputs": [ { "description": "A WordPress post object.", "jsonSchema": { "type": "object", "properties": { "id": { "type": "integer", "exclusiveMinimum": 0, "description": "The numeric WordPress post ID." }, "slug": { "type": "string", "description": "The WordPress post slug." }, "status": { "type": "string", "description": "The WordPress post status." }, "title": { "type": "object", "properties": { "rendered": { "type": "string", "description": "The rendered HTML value returned by WordPress." } }, "additionalProperties": true, "description": "A WordPress rendered object." } }, "required": [ "id", "slug", "status", "title" ], "additionalProperties": true, "description": "A WordPress post object." }, "nullable": false, "handle": "post" } ], "name": "Apply one validated category", "executor": { "kind": "connector", "action": "wordpress.update_post" } } }, "n_5e477b588c9646ca": { "name": "Categorized post summary", "kind": "outputs", "outputDefs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "integer", "minimum": 1 }, "categoryId": { "type": "integer", "minimum": 1 } }, "required": [ "id", "categoryId" ], "additionalProperties": false } }, "nullable": false, "handle": "categories" }, { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "count" } ], "outputMappings": { "categories": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_fdbf64754ee23b5d", "output": "state", "field": "categories" } ] }, "count": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_6287d728fc322cb7", "output": "count" } ] } } }, "takfnrsm5m": { "inputs": { "posts": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_93ea75cbb9f1ce05", "output": "state", "field": "posts" } ] }, "index": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_fdbf64754ee23b5d", "output": "index" } ] }, "taxonomy": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_ea9b28ebea0ce728", "output": "taxonomy" } ] } }, "name": "Classify one post title", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "integer", "minimum": 1 }, "title": { "type": "string" } }, "required": [ "id", "title" ], "additionalProperties": false } }, "nullable": false, "handle": "posts" }, { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "index" }, { "jsonSchema": { "type": "array", "minItems": 1, "items": { "type": "object", "properties": { "id": { "type": "integer", "minimum": 1 }, "name": { "type": "string", "minLength": 1 } }, "required": [ "id", "name" ], "additionalProperties": false } }, "nullable": false, "handle": "taxonomy" } ], "outputs": [ { "jsonSchema": { "type": "object", "properties": { "id": { "type": "integer", "minimum": 1 }, "categoryId": { "type": "integer", "minimum": 1 } }, "required": [ "id", "categoryId" ], "additionalProperties": false }, "nullable": false, "handle": "category" } ], "name": "Classify one post title", "moduleId": "6ewwu3w2yt", "capabilities": [ { "kind": "workflow", "name": "classify-post", "flowId": "wordpress-category-classifier", "revisionId": "", "inputs": [ { "jsonSchema": { "type": "integer", "minimum": 1 }, "nullable": false, "handle": "id" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "title" }, { "jsonSchema": { "type": "array", "minItems": 1, "items": { "type": "object", "properties": { "id": { "type": "integer", "minimum": 1 }, "name": { "type": "string", "minLength": 1 } }, "required": [ "id", "name" ], "additionalProperties": false } }, "nullable": false, "handle": "taxonomy" } ], "outputs": [ { "jsonSchema": { "type": "integer", "minimum": 1 }, "nullable": false, "handle": "id" }, { "jsonSchema": { "type": "integer", "minimum": 1 }, "nullable": false, "handle": "categoryId" } ] } ] } } }, "edges": [ { "source": "n_40e75fbfa953ccad", "target": "n_ea9b28ebea0ce728" }, { "source": "n_ea9b28ebea0ce728", "target": "n_93ea75cbb9f1ce05" }, { "source": "n_93ea75cbb9f1ce05", "target": "n_9ad12eaea79a48c2" }, { "source": "n_9ad12eaea79a48c2", "target": "n_7d2b360ab98485a0", "sourceHandle": "more" }, { "source": "n_7d2b360ab98485a0", "target": "n_93ea75cbb9f1ce05" }, { "source": "n_9ad12eaea79a48c2", "target": "n_fdbf64754ee23b5d", "sourceHandle": "otherwise" }, { "source": "n_fdbf64754ee23b5d", "target": "n_a910d8a8d166d856" }, { "source": "n_a910d8a8d166d856", "target": "n_6287d728fc322cb7", "sourceHandle": "otherwise" }, { "source": "n_6287d728fc322cb7", "target": "n_9c154c226ca1ce67" }, { "source": "n_9c154c226ca1ce67", "target": "n_84466ead2fa83422", "sourceHandle": "more" }, { "source": "n_84466ead2fa83422", "target": "n_6287d728fc322cb7" }, { "source": "n_9c154c226ca1ce67", "target": "n_5e477b588c9646ca", "sourceHandle": "otherwise" }, { "source": "n_a910d8a8d166d856", "target": "takfnrsm5m", "sourceHandle": "more" }, { "source": "takfnrsm5m", "target": "n_fdbf64754ee23b5d" } ] } }, "modules": { "n_17bc10fd7c869276": { "name": "Validate existing category IDs", "imports": [], "source": "export function validateTaxonomy(taxonomy) {\n if (!Array.isArray(taxonomy) || !taxonomy.length) throw new Error('Provide a nonempty category taxonomy.');\n const ids = new Set();\n return taxonomy.map(category => {\n if (!category || !Number.isSafeInteger(category.id) || category.id < 1 || typeof category.name !== 'string' || !category.name.trim()) {\n throw new Error('Each category needs a positive integer ID and nonempty name.');\n }\n if (ids.has(category.id)) throw new Error('Category IDs must be unique.');\n ids.add(category.id);\n return { id: category.id, name: category.name.trim() };\n });\n}\n\nexport default function prepareTaxonomy({ taxonomyJson }) {\n if (typeof taxonomyJson !== 'string') throw new Error('Provide the category taxonomy as JSON text.');\n let parsed;\n try { parsed = JSON.parse(taxonomyJson); } catch { throw new Error('The category taxonomy must be valid JSON.'); }\n return { taxonomy: validateTaxonomy(parsed) };\n}\n" }, "n_f1667c960fbcaea3": { "name": "Collect WordPress post pages", "imports": [], "source": "export function normalizePosts(posts) {\n if (!Array.isArray(posts)) throw new Error('WordPress must return a post array.');\n const ids = new Set();\n return posts.map(post => {\n if (!post || !Number.isSafeInteger(post.id) || post.id < 1 || typeof post.title?.rendered !== 'string') throw new Error('Every WordPress post needs an ID and rendered title.');\n if (ids.has(post.id)) throw new Error('Repeated post IDs were returned; keep the site stable while listing and try again.');\n ids.add(post.id);\n return { id: post.id, title: post.title.rendered };\n });\n}\n\nexport default function collectPages({ previousState, pagePosts, pagination }) {\n if (previousState === null) {\n if (pagePosts !== null || pagination !== null) throw new Error('Unexpected WordPress page before collection starts.');\n return { state: { posts: [], page: 0 }, nextPage: 1, more: true };\n }\n if (!Array.isArray(previousState.posts) || !Number.isSafeInteger(previousState.page) || previousState.page < 0) throw new Error('Invalid WordPress page collection state.');\n const page = previousState.page + 1, added = normalizePosts(pagePosts), ids = new Set(previousState.posts.map(post => post.id));\n if (added.some(post => ids.has(post.id))) throw new Error('Repeated post IDs were returned; keep the site stable while listing and try again.');\n const posts = [...previousState.posts, ...added], totalPages = pagination?.totalPages;\n if (totalPages !== null && (!Number.isSafeInteger(totalPages) || totalPages < 0 || (totalPages < page && !(page === 1 && posts.length === 0 && totalPages === 0)))) throw new Error('Invalid WordPress page-count metadata.');\n return { state: { posts, page }, nextPage: page + 1, more: totalPages !== null && page < totalPages };\n}\n" }, "n_ebd28b504d696a18": { "name": "Collect validated classifications", "imports": [], "source": "export default function collectPostClassifications({ posts, taxonomy, previousState, category }) {\n if (!Array.isArray(posts) || !Array.isArray(taxonomy) || taxonomy.length === 0) throw new Error('Posts and the validated category taxonomy are required.');\n let state;\n if (previousState === null) {\n if (category !== null) throw new Error('Unexpected classification before processing starts.');\n state = { index: 0, categories: [] };\n } else {\n if (!Number.isSafeInteger(previousState.index) || previousState.index < 0 || previousState.index >= posts.length || !Array.isArray(previousState.categories) || previousState.categories.length !== previousState.index) throw new Error('Invalid WordPress classification state.');\n if (category?.id !== posts[previousState.index].id || !taxonomy.some(item => item.id === category.categoryId)) throw new Error('The classification does not match this post and configured taxonomy.');\n state = { index: previousState.index + 1, categories: [...previousState.categories, category] };\n }\n return { state, index: state.index, more: state.index < posts.length };\n}\n" }, "n_4c93320486819fee": { "name": "Track category update progress", "imports": [], "source": "export default function trackPostUpdates({ categories, previousState }) {\n if (!Array.isArray(categories)) throw new Error('The complete validated category collection is required.');\n let index = 0;\n if (previousState !== null) {\n if (!Number.isSafeInteger(previousState.index) || previousState.index < 0 || previousState.index >= categories.length) throw new Error('Invalid WordPress update progress.');\n index = previousState.index + 1;\n }\n const category = categories[index];\n return { state: { index }, id: category?.id ?? 1, categoryIds: category ? [category.categoryId] : [], more: index < categories.length, count: index };\n}\n" }, "6ewwu3w2yt": { "name": "Classify one post title", "imports": [], "source": "export default async function classifyPost({ posts, index, taxonomy }, context) {\n if (!Array.isArray(posts) || !Number.isSafeInteger(index) || index < 0 || index >= posts.length) throw new Error('A selected WordPress post is required.');\n context.signal?.throwIfAborted();\n const category = await context.workflows.call('classify-post', { ...posts[index], taxonomy });\n return { category };\n}\n" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } } }, { "flowId": "wordpress-category-classifier", "file": { "kind": "open-flow-workflow", "version": 1, "name": "WordPress Category Classifier", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_96d4ec1986a7888b": { "name": "One post and taxonomy", "kind": "start", "inputDefs": [ { "jsonSchema": { "type": "integer", "minimum": 1 }, "nullable": false, "handle": "id" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "title" }, { "jsonSchema": { "type": "array", "minItems": 1, "items": { "type": "object", "properties": { "id": { "type": "integer", "minimum": 1 }, "name": { "type": "string", "minLength": 1 } }, "required": [ "id", "name" ], "additionalProperties": false } }, "nullable": false, "handle": "taxonomy" } ] }, "n_08fd76dac37e08ac": { "inputs": { "id": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_96d4ec1986a7888b", "output": "id" } ] }, "title": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_96d4ec1986a7888b", "output": "title" } ] }, "taxonomy": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_96d4ec1986a7888b", "output": "taxonomy" } ] } }, "name": "Prepare title and category choices", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "integer", "minimum": 1 }, "nullable": false, "handle": "id" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "title" }, { "jsonSchema": { "type": "array", "minItems": 1, "items": { "type": "object", "properties": { "id": { "type": "integer", "minimum": 1 }, "name": { "type": "string", "minLength": 1 } }, "required": [ "id", "name" ], "additionalProperties": false } }, "nullable": false, "handle": "taxonomy" } ], "outputs": [ { "jsonSchema": { "type": "integer", "minimum": 1 }, "nullable": false, "handle": "id" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "title" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "categoryList" }, { "jsonSchema": { "type": "array", "items": { "type": "integer", "minimum": 1 }, "minItems": 1 }, "nullable": false, "handle": "allowedIds" } ], "name": "Prepare title and category choices", "moduleId": "n_49e2472c885d6388", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_d9a6e439728c1c50": { "inputs": { "title": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_08fd76dac37e08ac", "output": "title" } ] }, "categoryList": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_08fd76dac37e08ac", "output": "categoryList" } ] } }, "name": "Choose one primary category", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "items": { "type": "object" }, "type": "array" }, "nullable": true, "handle": "messages", "value": null }, { "jsonSchema": { "items": { "type": "object" }, "minItems": 1, "type": "array" }, "nullable": false, "handle": "template", "value": [ { "role": "system", "content": "You are a helpful assistant" }, { "role": "user", "content": "You are an expert content strategist and taxonomy specialist with extensive experience in blog categorization and content organization.\n\nI will provide you with a blog post's title. Your task is to assign ONE primary category ID from this fixed list:\n\n{{categoryList}}\n\nAnalyze the title and return only the single most relevant category ID number that best represents the main focus of the post. While a post might touch on multiple topics, select the dominant theme that would be most useful for navigation purposes.\n\n{{title}}\n\nOutput only the category number" } ] }, { "jsonSchema": { "type": "object" }, "nullable": false, "handle": "model", "value": { "model": "gpt-5-mini" } }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "title" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "categoryList" } ], "outputs": [ { "description": "Generated response.", "jsonSchema": { "type": "string" }, "nullable": false, "handle": "output" } ], "name": "Choose one primary category", "executor": { "kind": "llm", "mode": "chat" } } }, "n_87210081b8738fec": { "inputs": { "id": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_08fd76dac37e08ac", "output": "id" } ] }, "completion": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_d9a6e439728c1c50", "output": "output" } ] }, "allowedIds": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_08fd76dac37e08ac", "output": "allowedIds" } ] } }, "name": "Validate category allowlist", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "integer", "minimum": 1 }, "nullable": false, "handle": "id" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "completion" }, { "jsonSchema": { "type": "array", "items": { "type": "integer", "minimum": 1 }, "minItems": 1 }, "nullable": false, "handle": "allowedIds" } ], "outputs": [ { "jsonSchema": { "type": "integer", "minimum": 1 }, "nullable": false, "handle": "id" }, { "jsonSchema": { "type": "integer", "minimum": 1 }, "nullable": false, "handle": "categoryId" } ], "name": "Validate category allowlist", "moduleId": "n_dbff623d02980fd6", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_757a87ac8beef80f": { "name": "One validated classification", "kind": "outputs", "outputDefs": [ { "jsonSchema": { "type": "integer", "minimum": 1 }, "nullable": false, "handle": "id" }, { "jsonSchema": { "type": "integer", "minimum": 1 }, "nullable": false, "handle": "categoryId" } ], "outputMappings": { "id": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_87210081b8738fec", "output": "id" } ] }, "categoryId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_87210081b8738fec", "output": "categoryId" } ] } } } }, "edges": [ { "source": "n_96d4ec1986a7888b", "target": "n_08fd76dac37e08ac" }, { "source": "n_08fd76dac37e08ac", "target": "n_d9a6e439728c1c50" }, { "source": "n_d9a6e439728c1c50", "target": "n_87210081b8738fec" }, { "source": "n_87210081b8738fec", "target": "n_757a87ac8beef80f" } ] } }, "modules": { "n_49e2472c885d6388": { "name": "Prepare title and category choices", "imports": [], "source": "function validateTaxonomy(taxonomy) {\n if (!Array.isArray(taxonomy) || !taxonomy.length) throw new Error('Provide a nonempty category taxonomy.');\n const ids = new Set();\n return taxonomy.map(category => {\n if (!category || !Number.isSafeInteger(category.id) || category.id < 1 || typeof category.name !== 'string' || !category.name.trim()) {\n throw new Error('Each category needs a positive integer ID and nonempty name.');\n }\n if (ids.has(category.id)) throw new Error('Category IDs must be unique.');\n ids.add(category.id);\n return { id: category.id, name: category.name.trim() };\n });\n}\n\nexport default function preparePost({ id, title, taxonomy }) {\n if (!Number.isSafeInteger(id) || id < 1 || typeof title !== 'string') throw new Error('Provide a post ID and rendered title.');\n const categories = validateTaxonomy(taxonomy);\n return { id, title, categoryList: categories.map(category => category.id + ' = ' + category.name).join('\\n'), allowedIds: categories.map(category => category.id) };\n}\n" }, "n_dbff623d02980fd6": { "name": "Validate category allowlist", "imports": [], "source": "export default function validateCategory({ id, completion, allowedIds }) {\n if (typeof completion !== 'string' || !/^[1-9]\\d*$/.test(completion.trim())) throw new Error('The model must return exactly one positive category ID.');\n const categoryId = Number(completion.trim());\n if (!Number.isSafeInteger(categoryId) || !allowedIds.includes(categoryId)) throw new Error('The model selected a category outside the configured taxonomy.');\n if (!Number.isSafeInteger(id) || id < 1) throw new Error('The source post ID is invalid.');\n return { id, categoryId };\n}\n" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } } } ] }