{"_id":"@apipass-dev/workflow-canvas","name":"@apipass-dev/workflow-canvas","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@apipass-dev/workflow-canvas","version":"0.1.0","description":"Apipass-first workflow canvas components, nodes, and runtime powered by @xyflow/react and @apipass-dev/apipass-sdk.","license":"MIT","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./react-flow":{"types":"./dist/react-flow.d.ts","import":"./dist/react-flow.js","require":"./dist/react-flow.cjs"},"./style.css":"./dist/style.css"},"sideEffects":["./dist/style.css","./src/styles/workflow-canvas.css"],"scripts":{"build":"tsup src/index.ts src/react-flow.ts --format esm,cjs --dts --sourcemap --clean && cat node_modules/@xyflow/react/dist/style.css src/styles/workflow-canvas.css > dist/style.css","typecheck":"tsc --noEmit","prepublishOnly":"npm run typecheck && npm run build"},"peerDependencies":{"@apipass-dev/apipass-sdk":"^1.1.0","react":">=18","react-dom":">=18"},"dependencies":{"@xyflow/react":"^12.10.2","lucide-react":"^0.561.0"},"devDependencies":{"@types/react":"^19.0.0","@types/react-dom":"^19.0.0","tsup":"^8.5.0","typescript":"^5.8.0"},"keywords":["apipass","workflow","canvas","react-flow","xyflow","ai","sdk"],"publishConfig":{"access":"public"},"_id":"@apipass-dev/workflow-canvas@0.1.0","gitHead":"eb115a20a4dab3bfaeb69015a1dc56acd43903ea","_nodeVersion":"24.4.1","_npmVersion":"11.4.2","dist":{"integrity":"sha512-j2f1SthVCJBuNm/vtcPqr0JYPZJrol1Oz2QU1q1KYF+KjVUxOeqX8EoAxKW8OQlX5KHvZ8JPRT3bMiRyG1c3vA==","shasum":"50875b0f01319c44440a511337c8be6722988451","tarball":"https://registry.npmjs.org/@apipass-dev/workflow-canvas/-/workflow-canvas-0.1.0.tgz","fileCount":16,"unpackedSize":653042,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIFUHkT7tsRgt0FddLnX5cbAujV7LzjkCQIcn/cw+yPLLAiEAyUJJ2Zs9J5WViqVkeQJeMmnSRcMrZFWNsu2pvfdFQpk="}]},"_npmUser":{"name":"apipass_dev","email":"myemailliao@gmail.com"},"directories":{},"maintainers":[{"name":"apipass_dev","email":"myemailliao@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/workflow-canvas_0.1.0_1781704577068_0.35455439045971815"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-17T13:56:16.759Z","0.1.0":"2026-06-17T13:56:17.224Z","modified":"2026-06-17T13:56:17.677Z"},"maintainers":[{"name":"apipass_dev","email":"myemailliao@gmail.com"}],"description":"Apipass-first workflow canvas components, nodes, and runtime powered by @xyflow/react and @apipass-dev/apipass-sdk.","keywords":["apipass","workflow","canvas","react-flow","xyflow","ai","sdk"],"license":"MIT","readme":"# @apipass-dev/workflow-canvas\n\nApipass-first workflow canvas components, nodes, and runtime powered by\n[`@xyflow/react`](https://xyflow.com/) and [`@apipass-dev/apipass-sdk`](https://www.npmjs.com/package/@apipass-dev/apipass-sdk).\n\nThis package is intentionally not a generic React Flow wrapper. It provides a reusable workflow canvas plus built-in Apipass generation nodes so teams can build node-based AI products while keeping `@apipass-dev/apipass-sdk` visible in the integration.\n\n## Install\n\n```bash\nnpm install @apipass-dev/apipass-sdk @apipass-dev/workflow-canvas\n```\n\nYour app also needs React. `@xyflow/react` is an internal dependency of this package and does not need to be installed directly by the app.\nThe package stylesheet includes React Flow base CSS plus the Apipass node and canvas styles.\n\n## Basic Usage\n\n```tsx\n\"use client\";\n\nimport {\n  ApipassWorkflowProvider,\n  WorkflowCanvas,\n  createAdapterWorkflowRuntime,\n  defaultApipassEdgeTypes,\n  defaultApipassNodeTypes,\n  type ApipassWorkflowRuntimeAdapter,\n} from \"@apipass-dev/workflow-canvas\";\nimport \"@apipass-dev/workflow-canvas/style.css\";\n\nconst adapter: ApipassWorkflowRuntimeAdapter = {\n  async generateImage(input) {\n    const response = await fetch(\"/api/workflows/run-node\", {\n      method: \"POST\",\n      headers: { \"Content-Type\": \"application/json\" },\n      body: JSON.stringify({\n        kind: input.kind,\n        nodeId: input.nodeId,\n        nodeType: input.node.type,\n        prompt: input.prompt,\n        request: input.request,\n      }),\n      signal: input.signal,\n    });\n    if (!response.ok) throw new Error(\"Workflow API route failed.\");\n    return response.json();\n  },\n};\n\nconst runtime = createAdapterWorkflowRuntime({ adapter });\n\nexport function Editor() {\n  return (\n    <ApipassWorkflowProvider\n      runtime={runtime}\n      actions={{\n        updateNodeData: (nodeId, patch) => {\n          // Update your local state, Redux store, server document, etc.\n        },\n        regenerateNode: async (nodeId) => {\n          // Call runtime.runNode or your app-specific runner.\n        },\n      }}\n    >\n      <WorkflowCanvas\n        nodes={nodes}\n        edges={edges}\n        nodeTypes={defaultApipassNodeTypes}\n        edgeTypes={defaultApipassEdgeTypes}\n        onNodesChange={onNodesChange}\n        onEdgesChange={onEdgesChange}\n        onConnect={onConnect}\n        onDropNode={(type, position, initialData) => {\n          // Add a node to your state.\n        }}\n        backgroundProps={{\n          gap: 24,\n          color: \"var(--canvas-dot)\",\n        }}\n        controlsProps={{\n          showInteractive: false,\n        }}\n        miniMapProps={{\n          nodeColor: (node) => node.type === \"mediaOutput\" ? \"#3f3f46\" : \"#71717a\",\n          maskColor: \"rgba(0,0,0,0.08)\",\n        }}\n      />\n    </ApipassWorkflowProvider>\n  );\n}\n```\n\nKeep private Apipass API keys on the server. In a Next.js app, your API route can read `process.env.APIPASS_API_KEY`, call `@apipass-dev/apipass-sdk`, and return only the workflow node result to the browser. Production routes should use your app's auth and rate limits.\n\n## Kit Usage\n\nUse `createApipassWorkflowKit` when an app wants the default Apipass node map, edge map, add-menu metadata, initial data factory, and connection validator as one object:\n\n```tsx\nimport {\n  ApipassWorkflowEditor,\n  createApipassWorkflowKit,\n} from \"@apipass-dev/workflow-canvas\";\n\nconst kit = createApipassWorkflowKit({\n  runtime,\n  nodeOverrides: {\n    customNode: CustomNode,\n  },\n  initialDataOverrides: {\n    generateImage: { resolution: \"2K\" },\n  },\n});\n\nexport function Editor() {\n  return (\n    <ApipassWorkflowEditor\n      kit={kit}\n      nodes={nodes}\n      edges={edges}\n      onNodesChange={onNodesChange}\n      onEdgesChange={onEdgesChange}\n      onConnect={onConnect}\n      onDropNode={(type, position, initialData) => {\n        addNode({\n          type,\n          position,\n          data: kit.createNodeData(type, initialData),\n        });\n      }}\n      workflowProviderProps={{\n        runtime: kit.runtime,\n        actions: {\n          updateNodeData,\n          regenerateNode,\n        },\n      }}\n    />\n  );\n}\n```\n\n`ApipassWorkflowEditor` is intentionally a thin provider-plus-canvas component. It does not own persistence, auth, billing, routes, workflow lists, or product shell UI.\n\n## Runtime Adapters\n\nApps that execute workflow nodes through product-specific API routes can provide an adapter instead of exposing private API keys in the browser:\n\n```ts\nimport {\n  createAdapterWorkflowRuntime,\n  type ApipassWorkflowRuntimeAdapter,\n} from \"@apipass-dev/workflow-canvas\";\n\nconst adapter: ApipassWorkflowRuntimeAdapter = {\n  async generateImage(input) {\n    const response = await fetch(\"/api/workflows/generate-image\", {\n      method: \"POST\",\n      headers: { \"Content-Type\": \"application/json\" },\n      body: JSON.stringify(input.request),\n      signal: input.signal,\n    });\n    if (!response.ok) throw new Error(\"Workflow API route failed.\");\n    const data = await response.json();\n\n    return {\n      nodeId: input.nodeId,\n      type: input.node.type,\n      status: \"complete\",\n      data: {\n        status: \"complete\",\n        outputImage: data.imageUrl,\n      },\n    };\n  },\n};\n\nexport const runtime = createAdapterWorkflowRuntime({ adapter });\n```\n\nThe SDK-backed `createApipassWorkflowRuntime({ client })` remains available for server-side execution, tests, and trusted environments. Do not pass production API keys through `NEXT_PUBLIC_*` variables or instantiate the SDK with private keys in browser code.\n\n## What Is Included\n\n- `WorkflowCanvas`: a controlled `@xyflow/react` canvas with Apipass-friendly defaults.\n- `ApipassWorkflowProvider`: context for the SDK client, runtime, upload adapter, and node actions.\n- `createApipassWorkflowKit`: default node/edge maps, connection validation, add-menu metadata, and initial node data factories.\n- `ApipassWorkflowEditor`: a thin provider-plus-canvas editor primitive for controlled workflow state.\n- Built-in Apipass nodes: prompt, image/audio/video input, image/video/audio generation, LLM generation, output, and gallery.\n- Canvas defaults and passthrough props: `defaultApipassCanvasProps`, `backgroundProps`, `controlsProps`, `miniMapProps`, and `reactFlowProps`.\n- React Flow facade exports from `@apipass-dev/workflow-canvas/react-flow` for apps that should not import `@xyflow/react` directly.\n- `createApipassWorkflowRuntime`: graph execution helpers that call `@apipass-dev/apipass-sdk`.\n- `createAdapterWorkflowRuntime`: graph execution through app-provided generation/upload handlers.\n- Workflow schema helpers: normalize, serialize, deserialize, and migrate workflow documents.\n\n## React Flow Facade\n\nApps can keep `@xyflow/react` behind this package boundary by importing low-level canvas APIs from the facade entry:\n\n```tsx\nimport {\n  BaseEdge,\n  EdgeLabelRenderer,\n  Handle,\n  NodeResizer,\n  Position,\n  addEdge,\n  applyEdgeChanges,\n  applyNodeChanges,\n  getBezierPath,\n  getSmoothStepPath,\n  useReactFlow,\n  useUpdateNodeInternals,\n} from \"@apipass-dev/workflow-canvas/react-flow\";\n```\n\nThis is useful when your app owns custom nodes, custom edges, or a custom store, but you still want `@apipass-dev/workflow-canvas` to be the only package that talks to React Flow directly.\n\n## Design Boundary\n\nThe package owns:\n\n- canvas rendering\n- node and edge UI\n- handle validation\n- workflow document shape\n- Apipass model request builders\n- Apipass workflow runtime\n\nYour app owns:\n\n- auth\n- persistence\n- billing\n- routing\n- API-key custody\n- product shell\n- template/library management\n\n## Built-In Node Types\n\n| Type | Purpose |\n| --- | --- |\n| `prompt` | Stores prompt text and emits `text`. |\n| `imageInput` | Stores or uploads an image and emits `image`. |\n| `videoInput` | Stores or uploads a video and emits `video`. |\n| `audioInput` | Stores or uploads audio and emits `audio`. |\n| `nanoBanana` / `generateImage` | Starts an Apipass image generation job and emits `image`. |\n| `generateVideo` | Starts an Apipass video generation job and emits `video`. |\n| `generateAudio` | Starts an Apipass audio generation job and emits `audio`. |\n| `llmGenerate` | Calls Apipass chat completions and emits `text`. |\n| `mediaOutput` | Displays the first connected media result. |\n| `outputGallery` | Displays connected image outputs as a gallery. |\n\n## Runnable Example\n\nThe repository includes a minimal Next.js example app:\n\n```bash\ncd examples/nextjs-basic\nnpm install\nnpm run dev\n```\n\nOpen the local URL printed by Next.js. The canvas renders without an Apipass key. To run the built-in Apipass generation node, set:\n\n```bash\ncp .env.example .env.local\n# edit .env.local and set APIPASS_API_KEY\n```\n\n`APIPASS_API_KEY` is read only by the example API route under `src/app/api/workflows/run-node/route.ts`; it is not exposed to the browser bundle.\n\nThe example depends on this package through `file:../..`. Its `.npmrc` sets `install-links=true` so npm installs the local package like a packed npm dependency instead of a symlink; this keeps peer dependency resolution close to the published package. After changing library source, run `npm run build` in the package root, then run `npm install` again inside the example.\n\nThe published npm package does not include `examples/`. `package.json` uses a `files` allowlist containing only `dist`, `README.md`, and `LICENSE`.\n\n## Build\n\n```bash\nnpm install\nnpm run typecheck\nnpm run build\n```\n\n## Publish\n\n```bash\nnpm login\nnpm publish --access public\n```\n","readmeFilename":"README.md","_rev":"1-93ca5bdc1971b99a84b14eb00a1c3c42"}