# Inspection Commands

> Read-only commands for understanding Storyblok state before migration.
These commands are read-only against Storyblok. They exist to make migration
state inspectable before you run write commands.

## inspect component-usage [#inspect-component-usage]

<span className="inline-flex items-center rounded-md border border-amber-500/40 bg-amber-500/10 px-2 py-0.5 text-xs font-medium text-amber-800 dark:text-amber-300">
  Beta: Needle in a haystack
</span>

```bash
sb-mig inspect component-usage \
  --from 12345 \
  --all \
  --query flex-group-width-child
```

`inspect component-usage` scans selected Storyblok stories with a local query
file. Use it when you need to find rare component patterns, such as a specific
component with a child component that has a particular field.

The command is read-only against Storyblok. It does not support `--dry-run`
because it never writes to Storyblok. It writes a local JSON report only when
`--outputPath` is passed.

### Query files [#query-files]

Query files are normal JavaScript modules. The first implementation supports:

```txt
*.sb.query.js
*.sb.query.cjs
*.sb.query.mjs
```

TypeScript query files are planned, but not supported by this command yet.

The query file must default-export an object with a `name` and a `match`
function:

```js
export default {
  name: "flex-group-width-child",

  match(node, context) {
    if (node.component !== "flex-group") {
      return null;
    }

    return {
      story: context.story.full_slug,
      path: context.path,
    };
  },
};
```

`match(node, context)` is called for every Storyblok component node found under
`story.content`.

| Return value          | Meaning                                       |
| --------------------- | --------------------------------------------- |
| `null` or `undefined` | No match.                                     |
| `true`                | Match with no extra details.                  |
| object                | Match with details stored in the JSON report. |

The context object contains:

| Field               | Meaning                                                       |
| ------------------- | ------------------------------------------------------------- |
| `context.story`     | Story metadata such as `id`, `name`, `slug`, and `full_slug`. |
| `context.path`      | Component path, for example `content.body[0]`.                |
| `context.parent`    | Parent component object when there is one.                    |
| `context.ancestors` | Ancestor component objects.                                   |

When `--query` is a name, sb-mig searches configured local component
directories, such as `src` and `storyblok`, for a matching query file:

```bash
sb-mig inspect component-usage \
  --from 12345 \
  --all \
  --query flex-group-width-child
```

When `--query` is a path, sb-mig loads that file directly:

```bash
sb-mig inspect component-usage \
  --from 12345 \
  --startsWith landing-pages \
  --query ./queries/flex-group-width-child.sb.query.js
```

### Example: flex group with width child [#example-flex-group-with-width-child]

Create a file such as:

```txt
src/storyblok-queries/flex-group-width-child.sb.query.js
```

```js
const hasValue = (value) =>
  value !== undefined && value !== null && value !== "";

const directArrayChildren = (node) =>
  Object.values(node)
    .filter(Array.isArray)
    .flat()
    .filter((item) => item && typeof item === "object" && item.component);

export default {
  name: "flex-group-width-child",
  description:
    "Find flex groups with vertical or reverse direction and children with width.",

  match(node) {
    if (node.component !== "flex-group") {
      return null;
    }

    if (!["vertical", "reverse"].includes(node.direction)) {
      return null;
    }

    const childrenWithWidth = directArrayChildren(node).filter((child) =>
      hasValue(child.width),
    );

    if (childrenWithWidth.length === 0) {
      return null;
    }

    return {
      childMatches: childrenWithWidth.length,
      matchingChildUids: childrenWithWidth.map((child) => child._uid),
    };
  },
};
```

Run it by query name:

```bash
sb-mig inspect component-usage \
  --from 12345 \
  --all \
  --query flex-group-width-child
```

Or run it by path and write the full report:

```bash
sb-mig inspect component-usage \
  --from 12345 \
  --startsWith landing-pages \
  --query ./src/storyblok-queries/flex-group-width-child.sb.query.js \
  --outputPath sbmig/usage/flex-group-width-child.json
```

### Selectors [#selectors]

Pass exactly one selector:

| Selector       | Type              | Effect                                                    |
| -------------- | ----------------- | --------------------------------------------------------- |
| `--all`        | boolean           | Inspect all non-folder stories.                           |
| `--withSlug`   | repeatable string | Inspect exact story `full_slug` values.                   |
| `--startsWith` | string            | Inspect stories whose `full_slug` starts with the prefix. |

### Options [#options]

| Flag           | Type   | Default              | Effect                                                                 |
| -------------- | ------ | -------------------- | ---------------------------------------------------------------------- |
| `--from`       | string | configured `spaceId` | Source space ID to inspect. Pass it explicitly for repeatable reports. |
| `--query`      | string | none                 | Query file name or direct path. Required.                              |
| `--outputPath` | string | none                 | Write the JSON report to this path.                                    |

### Output [#output]

The command prints a summary:

```txt
Component usage inspection
Space: 12345
Query: flex-group-width-child
Stories scanned: 1240
Stories matched: 37
Total matches: 52
Matches by story:
- home: 2
- campaign/summer: 1
```

When `--outputPath` is passed, the JSON report includes:

```txt
queryName
spaceId
generatedAt
filters
totals.storiesScanned
totals.storiesMatched
totals.matches
matches[]
```

Each match includes story identifiers, component name, `_uid`, path, optional
parent component metadata, and any details returned from the query.

### Invalid combinations [#invalid-combinations]

| Combination                                       | Result                                           |
| ------------------------------------------------- | ------------------------------------------------ |
| Missing `--query`                                 | Rejected.                                        |
| Missing `--all`, `--withSlug`, and `--startsWith` | Rejected.                                        |
| More than one selector family                     | Rejected.                                        |
| `--dry-run`                                       | Not supported. The command is already read-only. |

Use this command for inspection and audit work. Do not use it to migrate,
publish, update, or delete Storyblok content.

## language-publish-state [#language-publish-state]

```bash
sb-mig language-publish-state --from 12345
```

Reads source stories and writes a JSON map of publication state by story and
language.

### Options [#options-1]

| Flag            | Type              | Default                 | Effect                                                                       |
| --------------- | ----------------- | ----------------------- | ---------------------------------------------------------------------------- |
| `--from`        | string            | none                    | Source space ID. Required.                                                   |
| `--accessToken` | string            | configured access token | Delivery API token override for translated language checks.                  |
| `--languages`   | string            | `all`                   | `all`, `default`, or comma-separated language codes such as `default,fr,de`. |
| `--withSlug`    | repeatable string | none                    | Inspect exact story `full_slug` values.                                      |
| `--startsWith`  | string            | none                    | Inspect stories whose `full_slug` starts with the prefix.                    |
| `--fileName`    | string            | derived name            | Stable output base name under `sbmig/language-publish-state`.                |
| `--outputPath`  | string            | default folder          | Explicit output path for the generated JSON file.                            |

### Examples [#examples]

```bash
sb-mig language-publish-state \
  --from 12345 \
  --startsWith about/ \
  --languages all \
  --fileName about-prod

sb-mig language-publish-state \
  --from 12345 \
  --accessToken xxx \
  --withSlug about/testimonials \
  --languages default,fr
```

Default-language state comes from the Management API. Translated language state
uses Delivery API published and draft comparisons, so a Delivery API token is
required when translated languages are requested and no configured token exists.

## published-layer-export [#published-layer-export]

```bash
sb-mig published-layer-export --from 12345 --startsWith about/
```

Exports selected draft/current stories, latest published Story Versions API
content, and a comparison summary.

### Selectors [#selectors-1]

Pass one selector family for clarity:

| Selector       | Type              | Effect                                    |
| -------------- | ----------------- | ----------------------------------------- |
| `--all`        | boolean           | Export all non-folder stories.            |
| `--storyId`    | repeatable string | Export exact story IDs.                   |
| `--withSlug`   | repeatable string | Export exact story `full_slug` values.    |
| `--startsWith` | string            | Export non-folder stories under a prefix. |

If multiple selector families are passed, implementation priority is
`--storyId`, then `--withSlug`, then `--startsWith`, then `--all`.

### Options [#options-2]

| Flag                | Type   | Default                        | Effect                                            |
| ------------------- | ------ | ------------------------------ | ------------------------------------------------- |
| `--from`            | string | none                           | Source space ID. Required.                        |
| `--fileName`        | string | derived from selector          | Stable output base name.                          |
| `--outputPath`      | string | `sbmig/published-layer-export` | Output directory.                                 |
| `--versionsPerPage` | number | `25`                           | Story versions per page.                          |
| `--maxVersionPages` | number | `4`                            | Maximum story version pages to inspect per story. |

### Output files [#output-files]

```txt
<name>---draft-current-full.json
<name>---published-layer-full.json
<name>---dual-layer-summary.json
```

### Examples [#examples-1]

```bash
sb-mig published-layer-export \
  --from 12345 \
  --withSlug about/contact \
  --fileName contact-layer-check

sb-mig published-layer-export \
  --from 12345 \
  --storyId 178888427520390

sb-mig published-layer-export \
  --from 12345 \
  --startsWith about/ \
  --versionsPerPage 50 \
  --maxVersionPages 8
```

## story-versions [#story-versions]

```bash
sb-mig story-versions --from 12345 --storyId 98765
sb-mig story-versions --from 12345 --withSlug about/contact
```

Reads Management API `story_versions` for one story.

### Selectors [#selectors-2]

| Selector     | Type   | Effect                                                                        |
| ------------ | ------ | ----------------------------------------------------------------------------- |
| `--storyId`  | string | Inspect one story ID. Required unless `--withSlug` is passed.                 |
| `--withSlug` | string | Resolve one `full_slug` to a story ID. Required unless `--storyId` is passed. |

### Options [#options-3]

| Flag            | Type    | Default | Effect                                                           |
| --------------- | ------- | ------- | ---------------------------------------------------------------- |
| `--from`        | string  | none    | Source space ID. Required.                                       |
| `--showContent` | boolean | `true`  | Ask Storyblok to include version content.                        |
| `--page`        | number  | `1`     | Versions page.                                                   |
| `--perPage`     | number  | `25`    | Versions per page.                                               |
| `--raw`         | boolean | `false` | Print the raw Storyblok API response instead of compact summary. |
| `--outputPath`  | string  | none    | Write JSON output to this file instead of stdout.                |

### Examples [#examples-2]

```bash
sb-mig story-versions --from 12345 --storyId 98765

sb-mig story-versions \
  --from 12345 \
  --withSlug tours/europe \
  --raw \
  --outputPath sbmig/story-versions/tours-europe.raw.json
```

When slug resolution fails, the command searches close Storyblok slugs and
prints possible matches.
