Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/slim-peer-dependencies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@workflowbuilder/sdk': minor
---

Slimmer install: the base install is now `@workflowbuilder/sdk @xyflow/react zustand`. `@jsonforms/core`, `@jsonforms/react`, `i18next`, `react-i18next`, `i18next-browser-languagedetector` and `immer` moved from peer to regular dependencies and install automatically. JsonForms authoring primitives (`withJsonFormsControlProps`, `rankWith`, `useJsonForms`, `RuleEffect`, `ControlProps`, …) are now re-exported from `@workflowbuilder/sdk`, so custom renderers need no extra installs.
9 changes: 3 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,11 @@ Don't want to install or clone anything yet? [Try the live demo](https://app.wor
Use Workflow Builder inside your own React app. No clone, no Docker. Install the SDK and its peer dependencies from npm:

```bash
npm install @workflowbuilder/sdk \
react react-dom \
@xyflow/react \
@jsonforms/core @jsonforms/react \
i18next react-i18next i18next-browser-languagedetector \
immer zustand
npm install @workflowbuilder/sdk @xyflow/react zustand
```

Requires React 18 or 19.

Render the editor:

```tsx
Expand Down
2 changes: 0 additions & 2 deletions apps/ai-studio/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
"test:watch": "vitest --passWithNoTests"
},
"dependencies": {
"@jsonforms/core": "^3.4.1",
"@jsonforms/react": "^3.4.1",
"@phosphor-icons/react": "^2.1.7",
"@synergycodes/overflow-ui": "1.0.0-beta.27",
"@workflow-builder/types": "workspace:*",
Expand Down
3 changes: 0 additions & 3 deletions apps/demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
"test:watch": "vitest"
},
"dependencies": {
"@jsonforms/core": "^3.4.1",
"@jsonforms/react": "^3.4.1",
"@microsoft/clarity": "^1.0.0",
"@phosphor-icons/react": "^2.1.7",
"@synergycodes/overflow-ui": "1.0.0-beta.27",
Expand All @@ -27,7 +25,6 @@
"elkjs": "^0.9.3",
"html-to-image": "1.11.11",
"i18next": "^24.2.3",
"i18next-browser-languagedetector": "^8.0.5",
"immer": "^10.1.1",
"jspdf": "^3.0.1",
"libavoid-js": "0.4.0-beta.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,31 +27,28 @@ Install the SDK along with its peer dependencies:
<TabItem label="npm">

```bash
npm install @workflowbuilder/sdk react react-dom @xyflow/react @jsonforms/core @jsonforms/react i18next react-i18next i18next-browser-languagedetector immer zustand
npm install @workflowbuilder/sdk @xyflow/react zustand
```

</TabItem>
<TabItem label="pnpm">

```bash
pnpm add @workflowbuilder/sdk react react-dom @xyflow/react @jsonforms/core @jsonforms/react i18next react-i18next i18next-browser-languagedetector immer zustand
pnpm add @workflowbuilder/sdk @xyflow/react zustand
```

</TabItem>
<TabItem label="yarn">

```bash
yarn add @workflowbuilder/sdk react react-dom @xyflow/react @jsonforms/core @jsonforms/react i18next react-i18next i18next-browser-languagedetector immer zustand
yarn add @workflowbuilder/sdk @xyflow/react zustand
```

</TabItem>
</Tabs>

The SDK ships its non-peer dependencies bundled inside `dist/`, so the
peer list above is everything you need to install yourself. React,
xyflow, JsonForms, i18next, immer and zustand are kept external because
they expose singletons (store identity, i18next instance, frozen-object
caches) — your app and the SDK must share a single copy of each.
Requires React 18 or 19. Everything else the SDK uses (JsonForms,
i18next, immer, …) is a regular dependency and installs automatically.

## Usage

Expand Down
2 changes: 1 addition & 1 deletion apps/docs/src/content/docs/get-started/side-effects.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Importing `@workflowbuilder/sdk` runs a handful of module-level side effects on

## Side effects on import

- **`immer`** — calls `setAutoFreeze(false)`. ReactFlow mutates the objects produced by the SDK's `produce` calls (size, position, internal flags), so the SDK's drafts must not be auto-frozen. Because `immer` is a singleton peer, this disables auto-freeze **globally** for the host app — any of your own reducers, RTK slices, or libraries that rely on frozen drafts lose that protection. If you have your own immer flows that depend on frozen drafts, treat it as a known caveat.
- **`immer`** — calls `setAutoFreeze(false)`. ReactFlow mutates the objects produced by the SDK's `produce` calls (size, position, internal flags), so the SDK's drafts must not be auto-frozen. Because `immer` is a shared, deduped dependency, this disables auto-freeze **globally** for the host app — any of your own reducers, RTK slices, or libraries that rely on frozen drafts lose that protection. If you have your own immer flows that depend on frozen drafts, treat it as a known caveat.
- **`i18next`** — initialises the i18next instance with `react-i18next`, the language detector, and the SDK's bundled `en` / `pl` translations. If your app already configured i18next before importing the SDK, the SDK's `i18n.init(...)` is a no-op for the second `init` per i18next's contract — the registry is shared.

## Known limitations
Expand Down
15 changes: 4 additions & 11 deletions apps/docs/src/content/docs/guides/add-a-custom-node.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -151,16 +151,9 @@ To render a property with a custom React component, split the renderer into two
`renderers/color-picker.tsx`:

```tsx
import { withJsonFormsControlProps } from '@jsonforms/react';
import { type ControlProps, withJsonFormsControlProps } from '@workflowbuilder/sdk';

type Props = {
data: string;
handleChange: (path: string, value: string) => void;
path: string;
label?: string;
};

function ColorPickerControl({ data, handleChange, path, label }: Props) {
function ColorPickerControl({ data, handleChange, path, label }: ControlProps) {
return (
<label style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
{label && <span>{label}</span>}
Expand All @@ -175,11 +168,11 @@ export const ColorPicker = withJsonFormsControlProps(ColorPickerControl);
`renderers/color-picker-renderer.ts`:

```ts
import { type JsonFormsRendererRegistryEntry, rankWith, uiTypeIs } from '@jsonforms/core';
import { type JsonFormsRendererExtension, rankWith, uiTypeIs } from '@workflowbuilder/sdk';

import { ColorPicker } from './color-picker';

export const colorPickerRenderer: JsonFormsRendererRegistryEntry = {
export const colorPickerRenderer: JsonFormsRendererExtension = {
tester: rankWith(5, uiTypeIs('ColorPicker')),
renderer: ColorPicker,
};
Expand Down
31 changes: 17 additions & 14 deletions apps/docs/src/content/docs/guides/custom-jsonforms-control.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,25 @@ Consumer-supplied renderers are tried **before** the built-ins. When two testers

## Custom renderer — full example

Everything you need is re-exported from `@workflowbuilder/sdk` — you never install or import `@jsonforms/*` yourself. That matters for more than convenience: a renderer wrapped with a HOC from your own copy of JsonForms would read from a different React context than the SDK renders with and silently receive empty props. Importing from the SDK guarantees a single shared copy.

```tsx
import { type JsonFormsRendererRegistryEntry, rankWith, uiTypeIs } from '@jsonforms/core';
import { withJsonFormsControlProps } from '@jsonforms/react';
import { WorkflowBuilder } from '@workflowbuilder/sdk';
import {
type ControlProps,
type JsonFormsRendererExtension,
WorkflowBuilder,
rankWith,
uiTypeIs,
withJsonFormsControlProps,
} from '@workflowbuilder/sdk';

import '@workflowbuilder/sdk/style.css';

function ColorPicker({
data,
handleChange,
path,
}: {
data: string;
handleChange: (path: string, value: string) => void;
path: string;
}) {
function ColorPicker({ data, handleChange, path }: ControlProps) {
return <input type="color" value={data ?? '#000000'} onChange={(e) => handleChange(path, e.target.value)} />;
}

const colorPickerRenderer: JsonFormsRendererRegistryEntry = {
const colorPickerRenderer: JsonFormsRendererExtension = {
tester: rankWith(5, uiTypeIs('ColorPicker')),
renderer: withJsonFormsControlProps(ColorPicker),
};
Expand All @@ -62,7 +61,7 @@ Any node whose `uischema` contains `{ type: 'ColorPicker', scope: '...' }` will

## Cells

`cells` work the same way — use `JsonFormsCellRendererRegistryEntry` for list/array cell rendering. Built-in cells are passed through when consumer cells are absent; if you provide any, yours are used as-is (no merging with built-ins for cells).
`cells` work the same way — type the entry with `JsonFormsCellExtension` and wrap your component with `withJsonFormsCellProps` (also from `@workflowbuilder/sdk`) for list/array cell rendering. Built-in cells are passed through when consumer cells are absent; if you provide any, yours are used as-is (no merging with built-ins for cells).

## Translations

Expand Down Expand Up @@ -102,6 +101,10 @@ Translations are merged into the `plugins.*` namespace of the built-in i18n reso

The same translations can also be registered imperatively via [`registerPluginTranslation`](/guides/build-a-plugin/#registerplugintranslation).

## Authoring primitives

All the JsonForms building blocks — the `withJsonForms*Props` HOCs, the `useJsonForms` hook, `JsonFormsDispatch`, the testers (`rankWith`, `uiTypeIs`, `schemaTypeIs`, …), `RuleEffect`, and the prop types (`ControlProps`, `CellProps`, …) — are re-exported from `@workflowbuilder/sdk`. The full list lives in the Forms section of the [API reference](/api/).

## Related types

Available via `import type { ... } from '@workflowbuilder/sdk'`:
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/src/landing-pages/api-index.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ For walkthroughs, integration patterns, and design rationale, see the hand-writt
| [Hooks](/docs/api/hooks/) | React hooks (`useStore` selectors, `useFitView`, change-tracker, …). |
| [Store](/docs/api/store/) | One-shot store accessors (`getStoreNodes` / `setStoreNodes`, selection helpers, `openModal`). |
| [Listeners](/docs/api/listeners/) | Diagram event hooks — `addNodeChangedListener`, `addNodeDragStartListener` and their pairs. |
| [Forms](/docs/api/forms/) | Form-authoring helpers `getScope`, `DynamicCondition`, `ComparisonOperator`. |
| [Forms](/docs/api/forms/) | Form-authoring helpers (`getScope`, `DynamicCondition`) + re-exported JsonForms primitives for custom renderers (`withJsonForms*Props`, testers, `ControlProps`, …). |
| [Integration](/docs/api/integration/) | Persistence-strategy types and save-callback shapes. |
| [Types](/docs/api/types/) | Domain types — `NodeData`, `NodeSchema`, `UISchema`, `PaletteItem`, … |
| [Utilities](/docs/api/utilities/) | Reusable schema fragments + small helpers (`getHandleId`, `sharedProperties`, `DeepPartial`). |
Expand Down
42 changes: 23 additions & 19 deletions packages/sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,10 @@ Battle-tested in production by teams shipping AI workflow products.
## Install

```bash
npm install @workflowbuilder/sdk \
react react-dom \
@xyflow/react \
@jsonforms/core @jsonforms/react \
i18next react-i18next i18next-browser-languagedetector \
immer zustand
npm install @workflowbuilder/sdk @xyflow/react zustand
```

The SDK ships its non-peer dependencies bundled in `dist/`. React, xyflow, JsonForms, i18next, immer, and zustand are declared as `peerDependencies` so your app and the SDK share a single copy of each. See [Peer dependencies](#peer-dependencies) below.
Requires React 18 or 19. Everything else the SDK needs (JsonForms, i18next, immer, …) is a regular dependency and installs automatically. See [Peer dependencies](#peer-dependencies) below.

## Quick start

Expand Down Expand Up @@ -271,18 +266,27 @@ Full API reference: <https://www.workflowbuilder.io/docs/api/core/workflowbuilde

## Peer dependencies

Install alongside the SDK (ranges declared in `peerDependencies`):

- `react`, `react-dom` (`^18.0.0 || ^19.0.0`)
- `@xyflow/react` (`^12.0.0`)
- `@jsonforms/core`, `@jsonforms/react` (`^3.4.0`)
- `i18next` (`^24.0.0`), `react-i18next` (`^15.0.0`), `i18next-browser-languagedetector` (`^8.0.0`)
- `immer` (`^10.0.0`)
- `zustand` (`^5.0.0`)

These are declared as `peerDependencies` so the consumer app and the SDK share a single
copy of each — required for singletons (zustand store identity, i18next instance,
immer's frozen-object cache).
Only the libraries your app touches directly are peers. Install them alongside the SDK
(ranges declared in `peerDependencies`):

- `react`, `react-dom` (`^18.0.0 || ^19.0.0`) — already in any React app
- `@xyflow/react` (`^12.0.0`) — `Node`, `Edge`, `EdgeProps`, `NodeChange` types you use
- `zustand` (`^5.0.0`) — `useStore` / `useChangesTrackerStore` return zustand store types

They stay peers so the consumer app and the SDK share a single copy of each — required
for singletons (zustand store identity, xyflow's React context). The remaining libraries
the SDK relies on — `@jsonforms/core`, `@jsonforms/react`, `i18next`, `react-i18next`,
`i18next-browser-languagedetector`, `immer` — are regular `dependencies`: they install
automatically and are still externalized from the build, so package managers dedupe them
to a single copy via the caret ranges (they hold singletons too — the i18next instance,
immer's frozen-object cache, the JsonForms contexts — which is why they are never
bundled).

Writing custom JsonForms renderers, cells, or testers still needs only the SDK: the
authoring primitives (`withJsonFormsControlProps`, `rankWith`, `uiTypeIs`, `useJsonForms`,
`ControlProps`, …) are re-exported from `@workflowbuilder/sdk`. Import them from there,
never from `@jsonforms/*` directly — that guarantees your renderer uses the SDK's single
JsonForms copy instead of a second one that would break the React context.

## Support

Expand Down
18 changes: 6 additions & 12 deletions packages/sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,34 +64,32 @@
"prepublishOnly": "pnpm build:lib"
},
"peerDependencies": {
"@jsonforms/core": "^3.4.0",
"@jsonforms/react": "^3.4.0",
"@xyflow/react": "^12.0.0",
"i18next": "^24.0.0",
"i18next-browser-languagedetector": "^8.0.0",
"immer": "^10.0.0",
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0",
"react-i18next": "^15.0.0",
"zustand": "^5.0.0"
},
"dependencies": {
"@cfworker/json-schema": "4.1.1",
"@fontsource/poppins": "^5.2.7",
"@jsonforms/core": "^3.4.0",
"@jsonforms/react": "^3.4.0",
"@phosphor-icons/react": "^2.1.7",
"@synergycodes/overflow-ui": "1.0.0-beta.27",
"ace-builds": "^1.43.4",
"ajv": "catalog:",
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
"i18next": "^24.0.0",
"i18next-browser-languagedetector": "^8.0.0",
"immer": "^10.0.0",
"notistack": "^3.0.1",
"react-ace": "^14.0.1",
"react-i18next": "^15.0.0",
"react-mentions-ts": "^5.4.7",
"remeda": "^2.19.2"
},
"devDependencies": {
"@jsonforms/core": "^3.4.1",
"@jsonforms/react": "^3.4.1",
"@testing-library/react": "^16.3.0",
"@types/react": "catalog:",
"@vitejs/plugin-react": "^4.3.4",
Expand All @@ -100,10 +98,6 @@
"eslint-plugin-react": "7.37.2",
"eslint-plugin-react-hooks": "5.1.0-rc.1",
"eslint-plugin-tsdoc": "^0.4.0",
"i18next": "^24.2.3",
"i18next-browser-languagedetector": "^8.0.5",
"immer": "^10.1.1",
"react-i18next": "^15.4.1",
"vite": "^6.0.7",
"vite-plugin-dts": "^4.5.0",
"vite-plugin-svgr": "^4.3.0",
Expand Down
Loading
Loading