{"_id":"@b10cks/migrate-storyblok","_rev":"3-7fd7705fe51992e1ce43efc1dc45d033","name":"@b10cks/migrate-storyblok","dist-tags":{"latest":"0.11.0"},"versions":{"0.9.0":{"name":"@b10cks/migrate-storyblok","version":"0.9.0","keywords":["b10cks","cli","cms","migration","storyblok","headless-cms"],"author":{"name":"Michael Wallner @ Coder's Cantina"},"license":"MIT","_id":"@b10cks/migrate-storyblok@0.9.0","maintainers":[{"name":"badmike","email":"m.wallner@badsoft.net"}],"homepage":"https://github.com/b10cks/migration/tree/main/packages/storyblok#readme","bugs":{"url":"https://github.com/b10cks/migration/issues"},"bin":{"b10cks-migrate-storyblok":"dist/cli.mjs"},"dist":{"shasum":"c66c5a88a700440af0c8dc9b0e87206e1510ce09","tarball":"https://registry.npmjs.org/@b10cks/migrate-storyblok/-/migrate-storyblok-0.9.0.tgz","fileCount":18,"integrity":"sha512-yPHR73uL3CqEjpaQwKnwWZueUYoMfBtiEUDkQyZ7X8ASrJKGxlmaZnVNyKWXRTxqRhvtb6yZ7pMgdT0z/OreIg==","signatures":[{"sig":"MEYCIQDU+kXTQuioDBVI/iB7fIR9winjhZ74pFlCUnLM1Ls8IAIhAMusjXZ/gavTvIisAY7xUOC7/H6ILhkSZMdju+nlYjz2","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":919896},"main":"./dist/index.mjs","type":"module","_from":"file:b10cks-migrate-storyblok-0.9.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.mjs","source":"src/index.ts","engines":{"node":">=24.7.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","default":"./dist/index.mjs"},"./package.json":"./package.json"},"release":{"branches":["main"]},"scripts":{"dev":"vite build --watch","lint":"oxlint .","test":"vitest","build":"vite build","clean":"rm -rf dist","typecheck":"tsc -p tsconfig.json --noEmit"},"_npmUser":{"name":"badmike","email":"m.wallner@badsoft.net"},"_resolved":"/private/var/folders/2_/vp34lpn54jb98yk33ns76sc80000gn/T/7b5bfbf939e84da0d43aba52d94fb7bb/b10cks-migrate-storyblok-0.9.0.tgz","_integrity":"sha512-yPHR73uL3CqEjpaQwKnwWZueUYoMfBtiEUDkQyZ7X8ASrJKGxlmaZnVNyKWXRTxqRhvtb6yZ7pMgdT0z/OreIg==","repository":{"url":"git+https://github.com/b10cks/migration.git","type":"git","directory":"packages/storyblok"},"_npmVersion":"11.12.1","description":"CLI and library for migrating Storyblok spaces into b10cks","directories":{},"sideEffects":false,"_nodeVersion":"25.9.0","dependencies":{"chalk":"^5.6.2","inquirer":"^13.1.0","commander":"^12.1.0","@b10cks/mgmt-client":"^0.2.1","@storyblok/management-api-client":"^0.1.7"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^7.2.4","vitest":"^3.2.4","@types/node":"^20.0.0","vite-plugin-dts":"^4.5.4"},"_npmOperationalInternal":{"tmp":"tmp/migrate-storyblok_0.9.0_1776499192740_0.9869959798950956","host":"s3://npm-registry-packages-npm-production"}},"0.10.0":{"name":"@b10cks/migrate-storyblok","version":"0.10.0","keywords":["b10cks","cli","cms","headless-cms","migration","storyblok"],"author":{"name":"Michael Wallner @ Coder's Cantina"},"license":"MIT","_id":"@b10cks/migrate-storyblok@0.10.0","maintainers":[{"name":"badmike","email":"m.wallner@badsoft.net"}],"homepage":"https://github.com/b10cks/migration/tree/main/packages/storyblok#readme","bugs":{"url":"https://github.com/b10cks/migration/issues"},"bin":{"b10cks-migrate-storyblok":"dist/cli.mjs"},"dist":{"shasum":"994a159525f0fedc0073432e0346c2e8dfea0dd3","tarball":"https://registry.npmjs.org/@b10cks/migrate-storyblok/-/migrate-storyblok-0.10.0.tgz","fileCount":18,"integrity":"sha512-dodwa531dqdpi6MfQVpxr8XEXrf8SPltGPekmRbDCxfKxhzE4VOERWJMhpGukYYENqd2YlDVfI3aHsf5GfTvXw==","signatures":[{"sig":"MEYCIQDOzgXD83wO5MLzFRGpy/ISospfGbJhTZtwceKn/jzbpwIhALQU9EWgjbE/H9859VNi7zvaFOzmQJ6Bm/RgEPDzpYF9","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":939184},"main":"./dist/index.mjs","type":"module","_from":"file:b10cks-migrate-storyblok-0.10.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.mjs","source":"src/index.ts","engines":{"node":">=24.7.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","default":"./dist/index.mjs"},"./package.json":"./package.json"},"release":{"branches":["main"]},"scripts":{"dev":"vite build --watch","lint":"oxlint .","test":"vitest","build":"vite build","clean":"rm -rf dist","typecheck":"tsc -p tsconfig.json --noEmit"},"_npmUser":{"name":"badmike","email":"m.wallner@badsoft.net"},"_resolved":"/private/var/folders/2_/vp34lpn54jb98yk33ns76sc80000gn/T/8d850943118e6bef7108fe66d6b035e5/b10cks-migrate-storyblok-0.10.0.tgz","_integrity":"sha512-dodwa531dqdpi6MfQVpxr8XEXrf8SPltGPekmRbDCxfKxhzE4VOERWJMhpGukYYENqd2YlDVfI3aHsf5GfTvXw==","repository":{"url":"git+https://github.com/b10cks/migration.git","type":"git","directory":"packages/storyblok"},"_npmVersion":"11.12.1","description":"CLI and library for migrating Storyblok spaces into b10cks","directories":{},"sideEffects":false,"_nodeVersion":"25.9.0","dependencies":{"chalk":"^5.6.2","inquirer":"^13.1.0","commander":"^12.1.0","@b10cks/mgmt-client":"^0.3.0","@storyblok/management-api-client":"^0.1.7"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^7.2.4","vitest":"^3.2.4","@types/node":"^20.0.0","vite-plugin-dts":"^4.5.4"},"_npmOperationalInternal":{"tmp":"tmp/migrate-storyblok_0.10.0_1776520783753_0.6091989131289939","host":"s3://npm-registry-packages-npm-production"}},"0.11.0":{"name":"@b10cks/migrate-storyblok","version":"0.11.0","description":"CLI and library for migrating Storyblok spaces into b10cks","keywords":["b10cks","cli","cms","headless-cms","migration","storyblok"],"homepage":"https://github.com/b10cks/migration/tree/main/packages/storyblok#readme","bugs":{"url":"https://github.com/b10cks/migration/issues"},"license":"MIT","author":{"name":"Michael Wallner @ Coder's Cantina"},"repository":{"type":"git","url":"git+https://github.com/b10cks/migration.git","directory":"packages/storyblok"},"bin":{"b10cks-migrate-storyblok":"dist/cli.mjs"},"source":"src/index.ts","type":"module","sideEffects":false,"main":"./dist/index.mjs","module":"./dist/index.mjs","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","default":"./dist/index.mjs"},"./package.json":"./package.json"},"publishConfig":{"access":"public"},"dependencies":{"@b10cks/mgmt-client":"^0.3.0","@storyblok/management-api-client":"^0.1.7","chalk":"^5.6.2","commander":"^12.1.0","inquirer":"^13.1.0"},"devDependencies":{"@types/node":"^20.0.0","vite":"^7.2.4","vite-plugin-dts":"^4.5.4","vitest":"^3.2.4"},"release":{"branches":["main"]},"engines":{"node":">=24.7.0"},"scripts":{"build":"vite build","dev":"vite build --watch","test":"vitest","typecheck":"tsc -p tsconfig.json --noEmit","lint":"oxlint .","clean":"rm -rf dist"},"_id":"@b10cks/migrate-storyblok@0.11.0","_integrity":"sha512-GVkb3qw6LZgvL18+6yQFAU7bmvrU+5XQryyEFBcN6c9XmWBBkFIFX/JG3Cnf2PQZDarNkL8ijPbydMQAE4KHrg==","_resolved":"/private/var/folders/2_/vp34lpn54jb98yk33ns76sc80000gn/T/c5149af93bf9f05a5596da1df64d428a/b10cks-migrate-storyblok-0.11.0.tgz","_from":"file:b10cks-migrate-storyblok-0.11.0.tgz","_nodeVersion":"24.15.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-GVkb3qw6LZgvL18+6yQFAU7bmvrU+5XQryyEFBcN6c9XmWBBkFIFX/JG3Cnf2PQZDarNkL8ijPbydMQAE4KHrg==","shasum":"ab5a0b4042881a21723a04bcfed94ec1d7813333","tarball":"https://registry.npmjs.org/@b10cks/migrate-storyblok/-/migrate-storyblok-0.11.0.tgz","fileCount":18,"unpackedSize":995803,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQD5LxTMFo2VIdE9b5SYSUsmZnYqG/IPUWrojApPps7AvgIgcO8VH3W8zc2osJuiXPCmPIr6OL1RvvArgz21bNblXwg="}]},"_npmUser":{"name":"badmike","email":"m.wallner@badsoft.net"},"directories":{},"maintainers":[{"name":"badmike","email":"m.wallner@badsoft.net"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/migrate-storyblok_0.11.0_1777544633879_0.7332003328281596"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-18T07:59:52.680Z","modified":"2026-04-30T10:23:54.228Z","0.9.0":"2026-04-18T07:59:52.920Z","0.10.0":"2026-04-18T13:59:43.921Z","0.11.0":"2026-04-30T10:23:54.087Z"},"bugs":{"url":"https://github.com/b10cks/migration/issues"},"author":{"name":"Michael Wallner @ Coder's Cantina"},"license":"MIT","homepage":"https://github.com/b10cks/migration/tree/main/packages/storyblok#readme","keywords":["b10cks","cli","cms","headless-cms","migration","storyblok"],"repository":{"type":"git","url":"git+https://github.com/b10cks/migration.git","directory":"packages/storyblok"},"description":"CLI and library for migrating Storyblok spaces into b10cks","maintainers":[{"name":"badmike","email":"m.wallner@badsoft.net"}],"readme":"# @b10cks/migrate-storyblok\n\nStoryblok to b10cks migration tooling for teams that need more than a one-off export.\n\nThis package ships both a CLI and a library API for migrating Storyblok spaces into b10cks. It is designed to be rerunnable, source-aware, and practical for real migration projects where you iterate, validate, re-run, and reconcile before cutover.\n\n## What It Migrates\n\nThe current adapter supports all of the following:\n\n- Asset folders, preserving hierarchy and storing Storyblok IDs in `external_id`\n- Assets, including folder mapping, tags, translated metadata, and focus-point normalization\n- Datasources and datasource entries, with reconciliation by `external_id`, slug, and logical key\n- Storyblok components into b10cks blocks, including folders, tags, datasource-backed options, nested block fields, references, assets, links, richtext, meta fields, and `sb-price`\n- Stories into b10cks content with parent-child relationships, block resolution, localized overlays, and optional publish reconciliation\n- Interactive CLI usage and programmatic usage through exported migrator classes\n\n## Operational Model\n\nThis package is built around safe reruns:\n\n- Existing b10cks records are matched using `external_id` wherever possible.\n- Content migration runs in two passes: first upserting primary records, then reconciling references, hierarchy, and optional publish state.\n- Delta sync is built in for content. Without `--force`, older or unchanged stories are skipped instead of overwritten.\n- Missing Storyblok assets referenced from content can be uploaded on demand during content migration.\n\n## Installation\n\nInstall from npm when published:\n\n```bash\npnpm add @b10cks/migrate-storyblok\n```\n\nLocal development from the monorepo:\n\n```bash\npnpm install\npnpm build\n```\n\n## Prerequisites\n\nYou will typically need:\n\n- A Storyblok management token\n- A numeric Storyblok space ID\n- A b10cks management token\n- A b10cks space ID or slug, depending on your environment\n\nThe package expects Node `>=24.7.0` in this repository.\n\n## Quick Start\n\nGenerate a local environment template:\n\n```bash\nb10cks-migrate-storyblok init-env > .env.local\n```\n\nValidate credentials before a run:\n\n```bash\nb10cks-migrate-storyblok validate\n```\n\nGenerate a reusable Storyblok story cache:\n\n```bash\nb10cks-migrate-storyblok cache-stories \\\n  --storyblok-token YOUR_STORYBLOK_TOKEN \\\n  --storyblok-space-id 123456 \\\n  --story-cache-dir ./.storyblok-cache \\\n  --backup-story-cache\n```\n\nStart an interactive migration:\n\n```bash\nb10cks-migrate-storyblok migrate\n```\n\nRun a non-interactive migration:\n\n```bash\nb10cks-migrate-storyblok migrate \\\n  --storyblok-token YOUR_STORYBLOK_TOKEN \\\n  --storyblok-space-id 123456 \\\n  --b10cks-token YOUR_B10CKS_TOKEN \\\n  --b10cks-space-id your-space-id \\\n  --asset-folders \\\n  --assets \\\n  --datasources \\\n  --datasource-entries \\\n  --blocks \\\n  --content\n```\n\n## Recommended Run Order\n\nThe CLI lets you run any subset of migrators, but the most reliable production sequence is:\n\n1. `asset-folders`\n2. `assets`\n3. `datasources`\n4. `datasource-entries`\n5. `blocks`\n6. `content`\n\nThat order ensures referenced folders, assets, datasources, and blocks exist before content translation begins.\n\n## CLI Commands\n\n| Command | Purpose |\n| --- | --- |\n| `migrate` | Runs one or more Storyblok-to-b10cks migration stages, either interactively or fully scripted |\n| `cache-stories` | Fetches Storyblok stories into a reusable local JSON cache directory |\n| `validate` | Checks that the provided credentials can be resolved and that Storyblok access works before a migration run |\n| `init-env` | Prints a ready-to-fill environment template for local setup |\n\n### `migrate`\n\nRuns one or more migration stages.\n\n| Option | Description |\n| --- | --- |\n| `--storyblok-token <token>` | Storyblok management token. Overrides `STORYBLOK_TOKEN` when provided on the command line. |\n| `--storyblok-space-id <spaceId>` | Numeric Storyblok space ID. Accepts the direct ID instead of prompting or reading `STORYBLOK_SPACE_ID` / `STORYBLOK_SPACE`. |\n| `--b10cks-token <token>` | b10cks management token. Overrides `B10CKS_TOKEN` when provided on the command line. |\n| `--b10cks-space-id <spaceId>` | Target b10cks space identifier used for all writes. Overrides `B10CKS_SPACE_ID` / `B10CKS_SPACE`. |\n| `--b10cks-base-url <url>` | Custom b10cks API base URL. Useful for non-production environments or local API targets. |\n| `--config-file <path>` | JSON file used by content migration for `last_migrated` cutoffs and post-translation field mapping rules. |\n| `--mapping-file <path>` | Deprecated alias for `--config-file`. Keep only for backward compatibility. |\n| `--force` | Forces content writes even when delta-sync logic would normally skip unchanged or newer b10cks entries. |\n| `--publish` | Publishes migrated content variants during the reconciliation pass when the source Storyblok story is considered published. |\n| `--verbose` | Enables deeper debug logging, including payload dumps for `422` b10cks content write failures. |\n| `--story-cache-dir <path>` | Directory used to read or write cached Storyblok stories. |\n| `--reuse-story-cache` | Reads Storyblok stories from `--story-cache-dir` during content migration instead of fetching them from Storyblok. |\n| `--write-story-cache` | Writes fetched Storyblok stories to `--story-cache-dir` during content migration. |\n| `--backup-story-cache` | When writing a cache, also writes a timestamped story snapshot into `backups/`. |\n| `--asset-folders` | Runs only the asset-folder migrator. Use before assets to preserve folder hierarchy. |\n| `--assets` | Runs only the asset migrator. Preserves `external_id`, folder assignments, metadata, and tags. |\n| `--blocks` | Runs the block migrator that translates Storyblok components into b10cks block definitions. |\n| `--content` | Runs the content migrator with two-pass reconciliation, localization handling, and optional publish behavior. |\n| `--datasources` | Runs only the datasource migrator, creating or updating datasource definitions in b10cks. |\n| `--datasource-entries` | Runs only the datasource-entry migrator, reconciling entries under their resolved target datasources. |\n\nIf no migration flags are provided, the CLI falls back to an interactive checklist.\n\n### `cache-stories`\n\nFetches Storyblok stories, including hydrated content payloads, and stores them in a local directory:\n\n- `stories.json` contains the normalized story array used by the content migrator.\n- `manifest.json` records the cache format version, generation time, Storyblok space ID, and story count.\n- `backups/stories.<timestamp>.json` is created when `--backup-story-cache` is used.\n\nYou can then run content migration from the cache:\n\n```bash\nb10cks-migrate-storyblok migrate \\\n  --storyblok-token YOUR_STORYBLOK_TOKEN \\\n  --storyblok-space-id 123456 \\\n  --b10cks-token YOUR_B10CKS_TOKEN \\\n  --b10cks-space-id your-space-id \\\n  --content \\\n  --story-cache-dir ./.storyblok-cache \\\n  --reuse-story-cache\n```\n\nOr write/update the cache as part of a live content migration:\n\n```bash\nb10cks-migrate-storyblok migrate \\\n  --storyblok-token YOUR_STORYBLOK_TOKEN \\\n  --storyblok-space-id 123456 \\\n  --b10cks-token YOUR_B10CKS_TOKEN \\\n  --b10cks-space-id your-space-id \\\n  --content \\\n  --story-cache-dir ./.storyblok-cache \\\n  --write-story-cache \\\n  --backup-story-cache\n```\n\n| Option | Description |\n| --- | --- |\n| `--storyblok-token <token>` | Storyblok management token. Overrides `STORYBLOK_TOKEN` when provided on the command line. |\n| `--storyblok-space-id <spaceId>` | Numeric Storyblok space ID. Accepts the direct ID instead of prompting or reading `STORYBLOK_SPACE_ID` / `STORYBLOK_SPACE`. |\n| `--story-cache-dir <path>` | Required output directory for `stories.json` and `manifest.json`. |\n| `--backup-story-cache` | Writes a timestamped story snapshot into `backups/` in addition to the reusable `stories.json`. |\n\n### `validate`\n\nPrompts for or reads credentials, then validates connectivity.\n\n| Option | Description |\n| --- | --- |\n| `--storyblok-token <token>` | Storyblok management token used for the validation request. |\n| `--storyblok-space-id <spaceId>` | Numeric Storyblok space ID to validate against. |\n| `--b10cks-token <token>` | b10cks management token to resolve from CLI or environment before validation. |\n| `--b10cks-space-id <spaceId>` | b10cks space identifier to resolve from CLI or environment before validation. |\n\nCurrent behavior:\n\n- Storyblok validation is implemented and performs a real API check against the configured space.\n- Full b10cks validation is not yet implemented in the CLI, so this command should be treated as a partial preflight check.\n\n### `init-env`\n\nPrints a `.env.local` template to stdout.\n\n| Option | Description |\n| --- | --- |\n| None | `init-env` does not accept command-specific flags and always prints the same template structure. |\n\n## Configuration Sources\n\nConfiguration is resolved in this order:\n\n1. CLI options\n2. Environment variables\n3. `.env.local`\n4. `.env`\n5. Interactive prompts for any missing required values\n\nSupported environment variables:\n\n```env\nSTORYBLOK_TOKEN=your_storyblok_token_here\nSTORYBLOK_SPACE_ID=123456\n# Optional alias form if your environment stores structured values\n# STORYBLOK_SPACE={\"id\":123456}\n\nB10CKS_TOKEN=your_b10cks_token_here\nB10CKS_SPACE_ID=your-space-id\n# Optional alias form if your environment stores structured values\n# B10CKS_SPACE={\"id\":\"your-space-id\"}\n\nB10CKS_BASE_URL=https://api.b10cks.com\n```\n\n## Content Migration Semantics\n\nContent migration is the most opinionated part of the adapter. A few behaviors are worth understanding before you run it on a live project.\n\n### Two-Pass Reconciliation\n\nThe content migrator performs two passes:\n\n1. It loads Storyblok stories, assets, components, space language settings, and existing b10cks content, then upserts the primary content entries.\n2. It rebuilds translation context from the newly resolved records and reconciles references, parent links, overlays, and optional publish state.\n\nThis design improves correctness for internal links, references, and parent-child content structures that are hard to resolve in a single pass.\n\n### Folder and Startpage Handling\n\nStoryblok folder structures are normalized into b10cks content as follows:\n\n- Folders without a startpage become synthetic `folder` content entries backed by a virtual Storyblok folder block.\n- Folder startpages are merged with their parent folder aliases so the resulting b10cks content keeps the expected path identity.\n- Stories are sorted by resolved parent relationship before migration so parents exist before children whenever possible.\n\n### Localization\n\nLocalized content is split into:\n\n- A default-language base entry\n- One overlay content entry per detected non-default language\n\nOverlay entries are linked via `i18n_parent_id` and use external IDs in the form:\n\n```text\n<story-id>__i18n__<language>\n```\n\nThe migrator also recognizes translated field keys that follow Storyblok-style suffix patterns such as `__i18n__` and `_i18n_`.\n\n### Delta Sync and Forced Reconciliation\n\nWithout `--force`, a story is skipped when either of these is true:\n\n- The story `updated_at` is not newer than the config file's `last_migrated` threshold\n- The existing b10cks content `updated_at` is newer than or equal to the Storyblok story `updated_at`\n\nUse `--force` when you intentionally want to override those safeguards during cutover or backfill.\n\n### Asset Backfilling During Content Migration\n\nIf content references a Storyblok asset that has not yet been migrated, the content migrator will try to:\n\n1. Discover the referenced Storyblok asset URL or ID\n2. Download the asset from Storyblok\n3. Upload it into b10cks\n4. Continue content translation using the newly resolved asset\n\nIf that asset upload fails, the migration continues, logs a warning, and emits a manual-intervention note for the affected story.\n\n## Block Translation Coverage\n\nStoryblok component schemas are translated into b10cks block schemas with support for the most important field families, including:\n\n- `text`, `textarea`, `markdown`, and `richtext`\n- `number`, `boolean`, and `datetime`\n- `asset` and `multiasset`\n- `bloks`\n- Single and multi option fields\n- Datasource-backed option fields\n- Internal story references\n- Link fields, including asset links and mailto handling\n- Table fields\n- Custom `meta-fields`\n- Custom `sb-price`\n\nWhen datasource-backed fields are present, the block migrator resolves Storyblok datasource slugs to existing b10cks datasource IDs so the resulting schema remains connected.\n\n## Content Mapping Config\n\nThe content migrator accepts a JSON config file through `--config-file` that can define:\n\n- `last_migrated` for delta-sync cutoffs\n- Content field remapping rules applied after Storyblok-to-b10cks translation\n\nThe adapter accepts three mapping styles:\n\n- Block-scoped mappings\n- Global flat mappings\n- An array of explicit `{ from, target, override }` entries\n\n### Block-Scoped Example\n\n```json\n{\n  \"last_migrated\": \"2026-04-12T00:00:00.000Z\",\n  \"mapping\": {\n    \"page\": {\n      \"content.ogImage\": { \"target\": \"content.meta.ogImage\", \"override\": true },\n      \"content.pageTitle\": { \"target\": [\"name\", \"content.meta.title\"], \"override\": false },\n      \"content.canonical\": { \"target\": \"content.meta.canonical\", \"override\": true }\n    }\n  }\n}\n```\n\n### Global Flat Example\n\n```json\n{\n  \"content.pageTitle\": [\"name\", \"content.meta.title\"],\n  \"content.canonical\": \"content.meta.canonical\"\n}\n```\n\n### Explicit Array Example\n\n```json\n[\n  {\n    \"from\": \"content.pageTitle\",\n    \"target\": [\"name\", \"content.meta.title\"],\n    \"override\": false\n  }\n]\n```\n\nMapping behavior:\n\n- `target` may be a single path or an array of target paths\n- `override: false` only writes when the source has a value and the current target is empty\n- When a mapping is applied and the source path is not one of the targets, the source field is removed from the payload\n\nSee [`config.json.example`](./config.json.example) for a minimal example.\n\n## Programmatic Usage\n\n```ts\nimport {\n  AssetFolderMigrator,\n  AssetMigrator,\n  BlockMigrator,\n  ContentMigrator,\n  DataSourceMigrator,\n  DataSourceEntryMigrator,\n  type MigrationConfig,\n} from '@b10cks/migrate-storyblok'\n\nconst config: MigrationConfig = {\n  storyblokToken: 'your-storyblok-token',\n  storyblokSpaceId: 123456,\n  b10cksToken: 'your-b10cks-token',\n  b10cksSpaceId: 'your-space-id',\n  b10cksBaseUrl: 'https://api.b10cks.com',\n  configFile: './content-config.json',\n  publish: false,\n}\n\nconst migrator = new ContentMigrator(config, (progress) => {\n  console.log(`${progress.current}/${progress.total} ${progress.itemName}`)\n})\n\nconst result = await migrator.migrate()\nconsole.log(result)\n```\n\n## Development\n\nFrom the repository root:\n\n```bash\npnpm install\npnpm --filter @b10cks/migrate-storyblok build\npnpm --filter @b10cks/migrate-storyblok test\n```\n\n## Known Limitations\n\n- CLI `validate` currently verifies Storyblok access but does not yet perform full b10cks validation.\n- Content publishing only happens when `--publish` is set and the source story is considered published.\n- Content write validation errors are surfaced from the b10cks API; `--verbose` is the best way to inspect the payload that caused a `422`.\n","readmeFilename":"README.md"}