# Metaobjects Manager

A Node.js CLI tool for managing Shopify Metaobject definitions via the GraphQL Admin API. This tool allows you to synchronize Metaobject definitions between your local JSON files and your Shopify store.

## Features

- **PUSH**: Publish local Metaobject definitions to Shopify (create new or update existing)
  - Handles field additions, updates, and deletions
  - Optional auto-confirmation with `-y`/`--yes` flag
  - Default prompt response is "yes" (just press Enter)
- **PULL**: Retrieve Metaobject definitions from Shopify to local JSON files
  - Automatically filters out Shopify internal metaobjects and those from other apps
  - Only pulls merchant-owned metaobjects by default
- **VALIDATE**: Validate local JSON files without syncing
- **Selective Operations**: All commands support optional file/type parameters to process specific definitions

## Installation

```bash
npm install -g @mraichelson/metaobjects-manager
```

Or use locally in your project:

```bash
npm install @mraichelson/metaobjects-manager
```

## Setup

1. Copy the example environment file:

   ```bash
   cp env.example .env
   ```

2. Configure your `.env` file with your Shopify store credentials:

   ```env
   SHOPIFY_STORE_URL=your-store-url.myshopify.com
   SHOPIFY_CLIENT_ID=your-client-id
   SHOPIFY_CLIENT_SECRET=your-client-secret
   ```

3. Get your credentials from your custom app:
   - **Shopify Partner Dashboard**: https://partners.shopify.com
   - Navigate to: **Apps → Develop apps → Your app → API credentials**
   - Copy the **Client ID** and **Client Secret**
   - Ensure your app has the following Admin API access scopes enabled:
     - `read_metaobject_definitions` - Read metaobject definition schemas
     - `write_metaobject_definitions` - Create and update metaobject definition schemas
     - `read_metaobjects` - Read metaobject data instances
     - `write_metaobjects` - Create and update metaobject data instances

## Usage

### Push Command

Push local Metaobject definitions to Shopify:

```bash
metaobjects-manager push
# or
shopify-mom push
```

Push specific files:

```bash
metaobjects-manager push example.json dictionary.json
# or
metaobjects-manager push example dictionary
# (with or without .json extension)
```

Auto-confirm all prompts (useful for CI/CD):

```bash
metaobjects-manager push -y
# or
metaobjects-manager push --yes
```

This command will:

- Read JSON files from `metaobjects` (or configured directory)
  - If files are specified, only process those files
  - If no files specified, process all files in the directory
- Validate each file's structure and field types
- Check if the definition exists in Shopify
- Compare local and remote definitions to detect field changes
- Prompt for confirmation before creating or updating (defaults to "yes")
  - Use `-y`/`--yes` to skip all prompts
- **Handle field deletions**: If a field is removed from a local JSON file, it will be deleted from Shopify during update
- Skip files with errors and continue with others

### Pull Command

Pull Metaobject definitions from Shopify to local files:

```bash
metaobjects-manager pull
# or
shopify-mom pull
```

Pull specific metaobject types:

```bash
metaobjects-manager pull example dictionary
# (specify by type name, not filename)
```

This command will:

- Fetch Metaobject definitions from your Shopify store
  - **Automatically filters out:**
    - Shopify internal metaobjects (prefixed with `shopify--`)
    - Metaobjects from other apps (prefixed with `app--` or `$app:`)
  - Only processes merchant-owned metaobjects
  - If types are specified, only pull those types
  - If no types specified, pull all merchant-owned definitions
- Create new local JSON files for definitions that don't exist locally
- Update existing local files with current definitions from Shopify
- Log all changes to the terminal

### Validate Command

Validate local metaobject JSON files without pushing or pulling:

```bash
metaobjects-manager validate
# or
shopify-mom validate
```

Validate specific files:

```bash
metaobjects-manager validate example.json dictionary.json
# or
metaobjects-manager validate example dictionary
# (with or without .json extension)
```

This command will:

- Read JSON files from `metaobjects` (or configured directory)
  - If files are specified, only validate those files
  - If no files specified, validate all files in the directory
- Validate each file's structure and field types
- Report which files are valid or invalid
- Display detailed error messages for invalid files
- Exit with code 0 if all files are valid, code 1 if any are invalid

**Use cases:**

- Verify files that were pulled from a store instance
- Verify files that were written locally before pushing
- Validate changes made locally before pushing them

## Configuration

### Metaobjects Directory

By default, the tool looks for Metaobject definition files in `metaobjects` (project root). You can customize this by setting the `METAOBJECTS_DIR` environment variable:

```env
METAOBJECTS_DIR=custom/path/to/metaobjects
```

## JSON File Format

Each Metaobject definition should be stored as a JSON file named `{type}.json`. Example:

```json
{
  "type": "size_chart",
  "name": "Size Chart",
  "description": "Product sizing information",
  "displayNameKey": "size",
  "access": {
    "admin": "MERCHANT_READ_WRITE",
    "storefront": "PUBLIC_READ"
  },
  "capabilities": {
    "publishable": {
      "enabled": true
    },
    "translatable": {
      "enabled": true
    },
    "renderable": {
      "enabled": true,
      "data": {
        "metaTitleKey": "title",
        "metaDescriptionKey": "description"
      }
    },
    "onlineStore": {
      "enabled": true,
      "data": {
        "urlHandle": "size-charts",
        "canCreateRedirects": true
      }
    }
  },
  "fieldDefinitions": [
    {
      "key": "size",
      "name": "Size",
      "type": "single_line_text_field",
      "description": "The size identifier",
      "required": true,
      "capabilities": {
        "adminFilterable": {
          "enabled": true
        }
      },
      "validations": [
        {
          "name": "choices",
          "value": ["XS", "S", "M", "L", "XL"]
        }
      ]
    },
    {
      "key": "chest_inches",
      "name": "Chest (inches)",
      "type": "number_decimal",
      "required": false
    }
  ]
}
```

### Metaobject Type Naming

**⚠️ Important: Reserved Type Names**

Metaobject types cannot conflict with Shopify's native content types. Do not use reserved type names such as:

- `article`, `page`, `blog`, `product`, `collection`, `customer`, `order`, `cart`, `checkout`
- Any type that matches Shopify's built-in content object types

**Naming Patterns and Requirements:**

For detailed information on naming conventions, ownership types, and type identifier requirements, see the official Shopify documentation:

- [Ownership and Reserved Prefixes](https://shopify.dev/docs/apps/build/custom-data/ownership) - Learn about app-owned vs merchant-owned metaobjects and the `$app:` prefix
- [Manage Metaobject Definitions](https://shopify.dev/docs/apps/build/metaobjects/manage-metaobject-definitions) - Type identifier requirements and naming best practices
- [About Metaobjects](https://shopify.dev/docs/apps/build/metaobjects) - Overview of metaobject ownership types and naming patterns

### Available Options

**Metaobject-level options:**

- `type` (required): Unique identifier for the metaobject type (see naming guidelines above)
- `name` (required): Human-readable name
- `description` (optional): Administrative description
- `displayNameKey` (optional): Field key to use as display name
- `access` (required): Access permissions (admin and storefront)
- `capabilities` (optional): Optional features:
  - `publishable`: Enable draft/active status workflow
  - `translatable`: Enable translation support
  - `renderable`: Enable SEO metadata (requires `metaTitleKey` and/or `metaDescriptionKey`)
  - `onlineStore`: Enable Online Store pages (requires `urlHandle`)

**Field-level options:**

- `key` (required): Unique field identifier
- `name` (required): Human-readable field name
- `type` (required): Field data type (see valid types below)
- `description` (optional): Field description
- `required` (optional): Whether field is required
- `validations` (optional): Validation rules (e.g., min/max, choices, regex)
- `capabilities` (optional): Field capabilities:
  - `adminFilterable`: Enable filtering in Shopify admin

See `metaobjects/example.json` for a sample Metaobject definition.

### Valid Field Types

Field types must match Shopify's Metaobject field types. See the [official documentation](https://shopify.dev/docs/apps/build/metafields/list-of-data-types) for the complete list.

Common types include:

- `single_line_text_field`
- `multi_line_text_field`
- `number_integer`
- `number_decimal`
- `date`
- `date_time`
- `boolean`
- `url`
- `file_reference`
- `color`

## Error Handling

The tool handles errors gracefully:

- **Invalid JSON**: Logs the error and skips the file
- **Invalid field types**: Lists invalid types and references valid types
- **Missing required fields**: Lists missing fields
- **API errors**: Displays user-friendly error messages from Shopify
- **Network errors**: Logs with retry suggestions

On any error, the tool skips the current operation and continues with remaining files.

## Development

### Building

```bash
npm run build
```

This compiles TypeScript files from `src/` to `dist/`.

### Project Structure

```
.
├── src/
│   ├── cli/          # CLI command implementations
│   ├── files/        # File reading/writing utilities
│   ├── graphql/      # GraphQL queries and mutations
│   ├── shopify/      # Shopify API client and auth
│   ├── types/        # TypeScript type definitions
│   └── validators/   # JSON validation logic
├── metaobjects/      # Default directory for Metaobject JSON files
├── dist/             # Compiled JavaScript (generated)
└── package.json
```

## License

ISC
