Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
c98fafe
feat: add asset management package for AM2.0 support
Mar 2, 2026
4a815c0
chore: moved constants to constants file
Mar 4, 2026
253d793
Merge branch 'v2-dev' into feat/DX-4444
Mar 4, 2026
bd80e5a
chore: update lock file
Mar 4, 2026
8410ace
Merge pull request #7 from contentstack/feat/DX-4444
naman-contentstack Mar 5, 2026
20c2ab2
Merge branch 'v2-dev' into feat/AM2.0
Mar 17, 2026
64c6e36
feat: add support for AM in import module
Mar 20, 2026
a01a6fc
chore: update implementation for AM import
Mar 24, 2026
cb6feca
Merge branch 'v2-dev' into feat/AM2.0
Mar 24, 2026
de7d9bb
Merge branch 'feat/AM2.0' into feat/DX-4976
Mar 24, 2026
340ba38
Merge branch 'v2-dev' into feat/AM2.0
Mar 25, 2026
bfb990b
Merge branch 'feat/AM2.0' into feat/DX-4976
Mar 25, 2026
cbd6d7f
chore: made implementation consistent with other modules
Mar 27, 2026
6368499
Merge 'v2-dev' in 'feat/AM2.0'
Mar 31, 2026
93c7444
Merge branch 'feat/AM2.0' into feat/DX-4976
Mar 31, 2026
2ef8e3a
chore: updated implementation for AM import
Mar 31, 2026
4f99b26
chore: optimise the code
Apr 2, 2026
db9d960
Merge pull request #41 from contentstack/feat/DX-4976
naman-contentstack Apr 2, 2026
41014b1
Merge branch 'v2-dev' into feat/AM2.0
Apr 2, 2026
101c368
Merge branch 'v2-dev' into feat/AM2.0
Apr 14, 2026
39304d8
chore: optimise code
Apr 3, 2026
de4e34d
chore: add test case for clone
Apr 14, 2026
4536bd2
chore: update lockfile
Apr 14, 2026
521ba9e
chore: update test cases
Apr 14, 2026
bf671f9
chore: update test cases
Apr 16, 2026
53f89b7
chore: fix test cases
Apr 16, 2026
248ce74
Merge pull request #87 from contentstack/feat/optimise_import
naman-contentstack Apr 16, 2026
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
2 changes: 1 addition & 1 deletion .talismanrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
fileignoreconfig:
- filename: pnpm-lock.yaml
checksum: 78b7fca30ae03e2570a384c5432c10f0e6b023f492b68929795adcb4613e8673
checksum: 8b5a2f43585d3191cdc71ad611f50c94b6d13fb7442cf4218ee0851a068af178
version: '1.0'
54 changes: 54 additions & 0 deletions packages/contentstack-asset-management/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"env": {
"node": true
},
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "tsconfig.json",
"sourceType": "module"
},
"extends": [
"oclif-typescript",
"plugin:@typescript-eslint/recommended"
],
"rules": {
"@typescript-eslint/no-unused-vars": [
"error",
{
"args": "none"
}
],
"@typescript-eslint/prefer-namespace-keyword": "error",
"@typescript-eslint/quotes": [
"error",
"single",
{
"avoidEscape": true,
"allowTemplateLiterals": true
}
],
"semi": "off",
"@typescript-eslint/type-annotation-spacing": "error",
"@typescript-eslint/no-redeclare": "off",
"eqeqeq": [
"error",
"smart"
],
"id-match": "error",
"no-eval": "error",
"no-var": "error",
"quotes": "off",
"indent": "off",
"camelcase": "off",
"comma-dangle": "off",
"arrow-parens": "off",
"operator-linebreak": "off",
"object-curly-spacing": "off",
"node/no-missing-import": "off",
"padding-line-between-statements": "off",
"@typescript-eslint/ban-ts-ignore": "off",
"unicorn/no-abusive-eslint-disable": "off",
"unicorn/consistent-function-scoping": "off",
"@typescript-eslint/no-use-before-define": "off"
}
}
3 changes: 3 additions & 0 deletions packages/contentstack-asset-management/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
lib/
node_modules/
*.tsbuildinfo
49 changes: 49 additions & 0 deletions packages/contentstack-asset-management/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# @contentstack/cli-asset-management

Asset Management 2.0 API adapter for Contentstack CLI export and import. Used by the export and import plugins when Asset Management (AM 2.0) is enabled. To learn how to export and import content in Contentstack, refer to the [Migration guide](https://www.contentstack.com/docs/developers/cli/migration/).

[![License](https://img.shields.io/npm/l/@contentstack/cli)](https://github.com/contentstack/cli/blob/main/LICENSE)

<!-- toc -->
* [@contentstack/cli-asset-management](#contentstackcli-asset-management)
* [Overview](#overview)
* [Usage](#usage)
* [Exports](#exports)
<!-- tocstop -->

# Overview

This package provides:

- **AssetManagementAdapter** – HTTP client for the Asset Management API (spaces, assets, folders, fields, asset types).
- **exportSpaceStructure** – Exports space metadata and full workspace structure (metadata, folders, assets, fields, asset types) for linked workspaces.
- **Types** – `AssetManagementExportOptions`, `LinkedWorkspace`, `IAssetManagementAdapter`, and related types for export/import integration.

# Usage

This package is consumed by the export and import plugins. When using the export CLI with the `--asset-management` flag (or when the host app enables AM 2.0), the export plugin calls `exportSpaceStructure` with linked workspaces and options:

```ts
import { exportSpaceStructure } from '@contentstack/cli-asset-management';

await exportSpaceStructure({
linkedWorkspaces,
exportDir,
branchName: 'main',
assetManagementUrl,
org_uid,
context,
progressManager,
progressProcessName,
updateStatus,
downloadAsset, // optional
});
```

# Exports

| Export | Description |
|--------|-------------|
| `exportSpaceStructure` | Async function to export space structure for given linked workspaces. |
| `AssetManagementAdapter` | Class to call the Asset Management API (getSpace, getWorkspaceFields, getWorkspaceAssets, etc.). |
| Types from `./types` | `AssetManagementExportOptions`, `ExportSpaceOptions`, `ChunkedJsonWriteOptions`, `LinkedWorkspace`, `SpaceResponse`, `FieldsResponse`, `AssetTypesResponse`, and related API types. |
58 changes: 58 additions & 0 deletions packages/contentstack-asset-management/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"name": "@contentstack/cli-asset-management",
"version": "1.0.0",
"description": "Asset Management 2.0 API adapter for export and import",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"files": [
"lib",
"oclif.manifest.json"
],
"scripts": {
"build": "pnpm compile",
"clean": "rm -rf ./lib ./node_modules tsconfig.build.tsbuildinfo",
"compile": "tsc -b tsconfig.json",
"postpack": "rm -f oclif.manifest.json",
"prepack": "pnpm compile && oclif manifest && oclif readme",
"version": "oclif readme && git add README.md",
"lint": "eslint src/**/*.ts",
"format": "eslint src/**/*.ts --fix",
"test": "nyc --extension .ts mocha --require ts-node/register --forbid-only \"test/**/*.test.ts\"",
"posttest": "npm run lint",
"test:unit": "mocha --require ts-node/register --forbid-only \"test/unit/**/*.test.ts\"",
"test:unit:report": "nyc --extension .ts mocha --require ts-node/register --forbid-only \"test/unit/**/*.test.ts\""
},
"keywords": [
"contentstack",
"asset-management",
"cli"
],
"license": "MIT",
"dependencies": {
"@contentstack/cli-utilities": "~2.0.0-beta.5"
},
"oclif": {
"commands": "./lib/commands",
"bin": "csdx",
"devPlugins": [
"@oclif/plugin-help"
],
"repositoryPrefix": "<%- repo %>/blob/main/packages/contentstack-asset-management/<%- commandPath %>"
},
"devDependencies": {
"@types/chai": "^4.3.11",
"@types/mocha": "^10.0.6",
"@types/node": "^20.17.50",
"@types/sinon": "^17.0.2",
"chai": "^4.4.1",
"eslint": "^8.57.1",
"eslint-config-oclif": "^6.0.68",
"mocha": "^10.8.2",
"nyc": "^15.1.0",
"oclif": "^4.17.46",
"sinon": "^17.0.1",
"source-map-support": "^0.5.21",
"ts-node": "^10.9.2",
"typescript": "^5.8.3"
}
}
98 changes: 98 additions & 0 deletions packages/contentstack-asset-management/src/constants/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/** Fallback when export/import do not pass `chunkFileSizeMb`. */
export const FALLBACK_AM_CHUNK_FILE_SIZE_MB = 1;
/** Fallback when import does not pass `apiConcurrency`. */
export const FALLBACK_AM_API_CONCURRENCY = 5;
/** @deprecated Use FALLBACK_AM_API_CONCURRENCY */
export const DEFAULT_AM_API_CONCURRENCY = FALLBACK_AM_API_CONCURRENCY;

/** Fallback strip lists when import options omit `fieldsImportInvalidKeys` / `assetTypesImportInvalidKeys`. */
export const FALLBACK_FIELDS_IMPORT_INVALID_KEYS = [
'created_at',
'created_by',
'updated_at',
'updated_by',
'is_system',
'asset_types_count',
] as const;
export const FALLBACK_ASSET_TYPES_IMPORT_INVALID_KEYS = [
'created_at',
'created_by',
'updated_at',
'updated_by',
'is_system',
'category',
'preview_image_url',
'category_detail',
] as const;

/** @deprecated Use FALLBACK_AM_CHUNK_FILE_SIZE_MB */
export const CHUNK_FILE_SIZE_MB = FALLBACK_AM_CHUNK_FILE_SIZE_MB;

/**
* Main process name for Asset Management 2.0 export (single progress bar).
* Use this when adding/starting the process and for all ticks.
*/
export const AM_MAIN_PROCESS_NAME = 'Asset Management 2.0';

/**
* Process names for Asset Management 2.0 export progress (for tick labels).
*/
export const PROCESS_NAMES = {
AM_SPACE_METADATA: 'Space metadata',
AM_FOLDERS: 'Folders',
AM_ASSETS: 'Assets',
AM_FIELDS: 'Fields',
AM_ASSET_TYPES: 'Asset types',
AM_DOWNLOADS: 'Asset downloads',
// Import process names
AM_IMPORT_FIELDS: 'Import fields',
AM_IMPORT_ASSET_TYPES: 'Import asset types',
AM_IMPORT_FOLDERS: 'Import folders',
AM_IMPORT_ASSETS: 'Import assets',
} as const;

/**
* Status messages for each process (exporting, fetching, importing, failed).
*/
export const PROCESS_STATUS = {
[PROCESS_NAMES.AM_SPACE_METADATA]: {
EXPORTING: 'Exporting space metadata...',
FAILED: 'Failed to export space metadata.',
},
[PROCESS_NAMES.AM_FOLDERS]: {
FETCHING: 'Fetching folders...',
FAILED: 'Failed to fetch folders.',
},
[PROCESS_NAMES.AM_ASSETS]: {
FETCHING: 'Fetching assets...',
FAILED: 'Failed to fetch assets.',
},
[PROCESS_NAMES.AM_FIELDS]: {
FETCHING: 'Fetching fields...',
FAILED: 'Failed to fetch fields.',
},
[PROCESS_NAMES.AM_ASSET_TYPES]: {
FETCHING: 'Fetching asset types...',
FAILED: 'Failed to fetch asset types.',
},
[PROCESS_NAMES.AM_DOWNLOADS]: {
DOWNLOADING: 'Downloading asset files...',
FAILED: 'Failed to download assets.',
},
[PROCESS_NAMES.AM_IMPORT_FIELDS]: {
IMPORTING: 'Importing shared fields...',
FAILED: 'Failed to import fields.',
},
[PROCESS_NAMES.AM_IMPORT_ASSET_TYPES]: {
IMPORTING: 'Importing shared asset types...',
FAILED: 'Failed to import asset types.',
},
[PROCESS_NAMES.AM_IMPORT_FOLDERS]: {
IMPORTING: 'Importing folders...',
FAILED: 'Failed to import folders.',
},
[PROCESS_NAMES.AM_IMPORT_ASSETS]: {
IMPORTING: 'Importing assets...',
FAILED: 'Failed to import assets.',
},
} as const;
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { log } from '@contentstack/cli-utilities';

import type { AssetManagementAPIConfig } from '../types/asset-management-api';
import type { ExportContext } from '../types/export-types';
import { AssetManagementExportAdapter } from './base';
import { getArrayFromResponse } from '../utils/export-helpers';
import { PROCESS_NAMES } from '../constants/index';

export default class ExportAssetTypes extends AssetManagementExportAdapter {
constructor(apiConfig: AssetManagementAPIConfig, exportContext: ExportContext) {
super(apiConfig, exportContext);
}

async start(spaceUid: string): Promise<void> {
await this.init();
const assetTypesData = await this.getWorkspaceAssetTypes(spaceUid);
const items = getArrayFromResponse(assetTypesData, 'asset_types');
const dir = this.getAssetTypesDir();
log.debug(
items.length === 0
? 'No asset types, wrote empty asset-types'
: `Writing ${items.length} shared asset types`,
this.exportContext.context,
);
await this.writeItemsToChunkedJson(dir, 'asset-types.json', 'asset_types', ['uid', 'title', 'category', 'file_extension'], items);
this.tick(true, PROCESS_NAMES.AM_ASSET_TYPES, null);
}
}
Loading
Loading