Skip to content

About

Medusa v2 plugin for hierarchical product metadata — tree-based templates, drag-and-drop ordering, and per-variant overrides.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@codemind.ec/medusa-plugin-additional-info

Medusa v2 plugin for hierarchical product metadata — tree-based templates, diff-based updates, drag-and-drop ordering, and per-product overrides.

npm version Medusa v2 License: MIT


Features

  • Tree-based templates — define hierarchical structures of sections, items, and content blocks.
  • Versioned schemas — tree@1 and diff@1 schemas with Zod validation.
  • Diff-based overrides — apply per-product patches, additions, reordering, and hiding without duplicating templates.
  • Content blocks — markdown (md) and list (bullet/number) blocks per node.
  • Admin UI — full CRUD interface with drag-and-drop tree editing for templates and product values.
  • Store API — public endpoint to fetch resolved additional info for any product.
  • Product linking — automatic Medusa link between additional info values and products.

Prerequisites

Requirement Version
Node.js >= 20
Medusa >= 2.13.3 (built and tested against 2.21.2)

Installation

npm install @codemind.ec/medusa-plugin-additional-info
# or
pnpm add @codemind.ec/medusa-plugin-additional-info

Configuration

Add the plugin and its module to your medusa-config.ts:

import { defineConfig } from "@medusajs/framework/utils"

export default defineConfig({
  // ...
  modules: [
    {
      resolve: "@codemind.ec/medusa-plugin-additional-info/additional-info",
      definition: { isQueryable: true },
    },
  ],
  plugins: [
    {
      resolve: "@codemind.ec/medusa-plugin-additional-info",
      options: {},
    },
  ],
})

Nota: El módulo se registra por separado en modules[] con isQueryable: true para que sea accesible desde el query graph de Medusa. La entrada en plugins[] carga admin UI, API routes, links y workflows.


Architecture

Module: additional-info

additional_info_template

Reusable tree structures that define the shape of additional info.

Field Type Description
id string Primary key
name string Template name
description string? Optional description
attributes JSON Tree document (tree@1 schema)

additional_info_value

Per-product values, optionally referencing a template.

Field Type Description
id string Primary key
product_id string Associated product ID
template relation Parent template (optional)
values JSON Diff document or standalone tree

Schema: Tree v1

Schema identifier: com.mariquita.additional-info/tree@1

type AITreeDocV1 = {
  schema: "com.mariquita.additional-info/tree@1"
  rev: number
  root: "root"
  nodes: Record<string, AINodeV1>   // id → node
  children: Record<string, string[]> // parentId → [childIds]
  meta?: { createdAt?: string; updatedAt?: string }
}

type AINodeV1 = {
  id: string
  type: "root" | "section" | "item"
  title: string
  blocks?: AIBlockV1[]
  tags?: string[]
  ui?: { icon?: string; collapsed?: boolean }
}

type AIBlockV1 =
  | { kind: "md"; value: string }
  | { kind: "list"; style: "bullet" | "number"; items: string[] }

Schema: Diff v1

Schema identifier: com.mariquita.additional-info/diff@1

type AIDiffDocV1 = {
  schema: "com.mariquita.additional-info/diff@1"
  baseRev: number
  overrides?: Record<string, AINodePatchV1>   // per-node patches
  additions?: AIAdditionsV1                    // new subtrees
  childrenOrder?: Record<string, string[]>     // reorder children
}

type AIAdditionsV1 = {
  mount: {
    parentId: string
    position?: "start" | "end"
    index?: number
  }
  nodes: Record<string, AINodeV1>
  children: Record<string, string[]>
  rootIds: string[]
}

Operations

Function Description
createEmptyTree() Creates a canonical empty tree with a root node
validateTreeDocV1(input) Validates schema, root existence, cycle detection (DFS), and child references
resolveTree(base, diff?) Merges a base tree with a diff: applies overrides, prunes hidden subtrees, mounts additions, and reorders children
pruneHidden(tree, hiddenIds) Removes subtrees by ID

API Reference

Admin Routes

Method Endpoint Description
GET /admin/additional-info/templates List templates (paginated: limit, offset)
POST /admin/additional-info/templates Create a template
GET /admin/additional-info/templates/:id Get a template
PUT /admin/additional-info/templates/:id Update a template
DELETE /admin/additional-info/templates/:id Delete a template
GET /admin/additional-info/values List values (filter: product_id, expand: template)
POST /admin/additional-info/values Create value(s) — single or array
PUT /admin/additional-info/values/:id Update a value
DELETE /admin/additional-info/values/:id Delete a value

Store Routes

Method Endpoint Description
GET /store/products/:id/additional-info Get resolved additional info for a product

Create a Template

curl -X POST /admin/additional-info/templates \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Product Specs",
    "attributes": {
      "schema": "com.mariquita.additional-info/tree@1",
      "rev": 1,
      "root": "root",
      "nodes": {
        "root": { "id": "root", "type": "root", "title": "Root" },
        "specs": { "id": "specs", "type": "section", "title": "Specifications", "blocks": [
          { "kind": "md", "value": "Technical details for this product." }
        ]}
      },
      "children": { "root": ["specs"] }
    }
  }'

Workflows

createAdditionalInfoValuesWorkflow

  1. Step 1: createAdditionalInfoValuesStep — creates value records (rollback: soft-delete).
  2. Step 2: linkAdditionalInfoProductStep — establishes links to products (rollback: dismiss links).

Product Linking

The plugin defines a Medusa link between additional_info_value and Product (using Modules.PRODUCT). This enables:

  • Querying values through product relations
  • Automatic cleanup on product deletion
  • Multiple values per product (isList: true)

Medusa >= 2.21: core Store API routes only return fields in their strict allowed list, so the additional_info_value link is not exposed through fields on /store/products by default. The plugin's own GET /store/products/:id/additional-info route is unaffected. If your storefront requests the link directly, opt in from your app with a global middleware (no method key):

// src/api/middlewares.ts (your Medusa app)
import { defineMiddlewares, allowFields } from "@medusajs/framework/http"

export default defineMiddlewares({
  routes: [
    {
      matcher: "/store/products*",
      middlewares: [allowFields("additional_info_value", "additional_info_value.id", "additional_info_value.values")],
    },
  ],
})

TypeScript Exports

// Types
import type { AITreeDocV1, AIDiffDocV1, AINodeV1, AIBlockV1 } from "@codemind.ec/medusa-plugin-additional-info/types"

// Module
import AdditionalInfoModule from "@codemind.ec/medusa-plugin-additional-info/modules/additional-info"

// Workflows
import { createAdditionalInfoValuesWorkflow } from "@codemind.ec/medusa-plugin-additional-info/workflows"

License

MIT — CodeMind

About

Medusa v2 plugin for hierarchical product metadata — tree-based templates, drag-and-drop ordering, and per-variant overrides.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages