cmx-sdk 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -10,6 +10,7 @@ import { createDataType } from "./commands/create-data-type.js";
10
10
  import { createForm } from "./commands/create-form.js";
11
11
  import { syncComponents } from "./commands/sync-components.js";
12
12
  import { importSchema } from "./commands/import-schema.js";
13
+ import { generate } from "./commands/generate.js";
13
14
  // Load environment variables
14
15
  config();
15
16
  const program = new Command();
@@ -78,14 +79,10 @@ program
78
79
  .description("Import collections, data types, and forms from a schema file")
79
80
  .requiredOption("--file <file>", "Path to schema JSON file")
80
81
  .action(importSchema);
81
- // generate (placeholder for future implementation)
82
+ // generate
82
83
  program
83
84
  .command("generate")
84
85
  .description("Generate TypeScript types and functions from CMX schema")
85
- .option("--output <dir>", "Output directory (default: generated/cmx)")
86
- .action(() => {
87
- console.log("The 'generate' command is not yet implemented.");
88
- console.log("This command will be added in a future release.");
89
- process.exit(1);
90
- });
86
+ .option("--output <dir>", "Output directory (default: cmx/generated)")
87
+ .action(generate);
91
88
  program.parse();
@@ -0,0 +1,3 @@
1
+ export declare function generate(options: {
2
+ output?: string;
3
+ }): Promise<void>;
@@ -0,0 +1,164 @@
1
+ import { createApiClient } from "../lib/api-client.js";
2
+ import * as fs from "fs";
3
+ import * as path from "path";
4
+ function mapFieldType(field) {
5
+ const baseType = (() => {
6
+ switch (field.type) {
7
+ case "text":
8
+ case "textarea":
9
+ case "richtext":
10
+ case "markdown":
11
+ case "email":
12
+ case "url":
13
+ case "slug":
14
+ return "string";
15
+ case "number":
16
+ return "number";
17
+ case "boolean":
18
+ case "checkbox":
19
+ return "boolean";
20
+ case "date":
21
+ case "datetime":
22
+ return "string"; // ISO 8601 string
23
+ case "select":
24
+ case "radio":
25
+ return "string";
26
+ case "image":
27
+ case "file":
28
+ return "string"; // URL
29
+ case "relation":
30
+ return "string"; // ID or slug
31
+ default:
32
+ return "unknown";
33
+ }
34
+ })();
35
+ return field.required ? baseType : `${baseType} | null`;
36
+ }
37
+ function generateDataTypeFile(dataType) {
38
+ const interfaceName = toPascalCase(dataType.slug);
39
+ const fields = dataType.fields
40
+ .map((field) => {
41
+ const comment = field.description
42
+ ? `/** ${field.label} - ${field.description} */`
43
+ : `/** ${field.label} */`;
44
+ return ` ${comment}\n ${field.key}: ${mapFieldType(field)}`;
45
+ })
46
+ .join("\n");
47
+ return `// Auto-generated by cmx-sdk generate
48
+ // Data Type: ${dataType.name} (${dataType.slug})
49
+ // Do not edit manually.
50
+
51
+ import { getDataEntries, getDataEntry } from "cmx-sdk"
52
+
53
+ export interface ${interfaceName} {
54
+ ${fields}
55
+ _meta: {
56
+ sortOrder: number
57
+ createdAt: string
58
+ updatedAt: string
59
+ }
60
+ }
61
+
62
+ export async function get${interfaceName}(options?: {
63
+ sortBy?: string
64
+ sortOrder?: string
65
+ limit?: number
66
+ offset?: number
67
+ }): Promise<{ type: string; items: ${interfaceName}[]; count: number }> {
68
+ const result = await getDataEntries("${dataType.slug}", options)
69
+ return result as unknown as { type: string; items: ${interfaceName}[]; count: number }
70
+ }
71
+
72
+ export async function get${interfaceName}ById(id: string): Promise<${interfaceName}> {
73
+ const result = await getDataEntry("${dataType.slug}", id)
74
+ return result as unknown as ${interfaceName}
75
+ }
76
+ `;
77
+ }
78
+ function generateCollectionFile(collection) {
79
+ const functionPrefix = toPascalCase(collection.slug);
80
+ return `// Auto-generated by cmx-sdk generate
81
+ // Collection: ${collection.name} (${collection.slug})
82
+ // Do not edit manually.
83
+
84
+ import { getCollectionPosts, getCollectionPostDetail } from "cmx-sdk"
85
+ import type { CollectionPostsResponse, CollectionPostDetailResponse } from "cmx-sdk"
86
+
87
+ export async function get${functionPrefix}Posts(): Promise<CollectionPostsResponse> {
88
+ return getCollectionPosts("${collection.slug}")
89
+ }
90
+
91
+ export async function get${functionPrefix}PostDetail(slug: string): Promise<CollectionPostDetailResponse> {
92
+ return getCollectionPostDetail("${collection.slug}", slug)
93
+ }
94
+ `;
95
+ }
96
+ function toPascalCase(str) {
97
+ return str
98
+ .split(/[-_]/)
99
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
100
+ .join("");
101
+ }
102
+ export async function generate(options) {
103
+ const outputDir = options.output || "cmx/generated";
104
+ try {
105
+ console.log("Fetching schema from CMX API...");
106
+ const client = createApiClient();
107
+ // Fetch schema from /schema endpoint
108
+ const schema = (await client.request("GET", "/schema"));
109
+ console.log(`Found ${schema.collections.length} collections and ${schema.dataTypes.length} data types`);
110
+ // Create output directories
111
+ const collectionsDir = path.join(outputDir, "collections");
112
+ const dataTypesDir = path.join(outputDir, "data-types");
113
+ fs.mkdirSync(collectionsDir, { recursive: true });
114
+ fs.mkdirSync(dataTypesDir, { recursive: true });
115
+ // Generate collection files
116
+ for (const collection of schema.collections) {
117
+ const filePath = path.join(collectionsDir, `${collection.slug}.ts`);
118
+ const content = generateCollectionFile(collection);
119
+ fs.writeFileSync(filePath, content, "utf-8");
120
+ console.log(`✓ Generated ${filePath}`);
121
+ }
122
+ // Generate collection index
123
+ const collectionIndexContent = `// Auto-generated by cmx-sdk generate
124
+ // Do not edit manually.
125
+
126
+ ${schema.collections.map((c) => `export * from "./${c.slug}"`).join("\n")}
127
+ `;
128
+ fs.writeFileSync(path.join(collectionsDir, "index.ts"), collectionIndexContent, "utf-8");
129
+ console.log(`✓ Generated ${path.join(collectionsDir, "index.ts")}`);
130
+ // Generate data type files
131
+ for (const dataType of schema.dataTypes) {
132
+ const filePath = path.join(dataTypesDir, `${dataType.slug}.ts`);
133
+ const content = generateDataTypeFile(dataType);
134
+ fs.writeFileSync(filePath, content, "utf-8");
135
+ console.log(`✓ Generated ${filePath}`);
136
+ }
137
+ // Generate data types index
138
+ const dataTypesIndexContent = `// Auto-generated by cmx-sdk generate
139
+ // Do not edit manually.
140
+
141
+ ${schema.dataTypes.map((dt) => `export * from "./${dt.slug}"`).join("\n")}
142
+ `;
143
+ fs.writeFileSync(path.join(dataTypesDir, "index.ts"), dataTypesIndexContent, "utf-8");
144
+ console.log(`✓ Generated ${path.join(dataTypesDir, "index.ts")}`);
145
+ // Generate main index
146
+ const mainIndexContent = `// Auto-generated by cmx-sdk generate
147
+ // Do not edit manually.
148
+
149
+ export * from "./data-types"
150
+ export * from "./collections"
151
+ `;
152
+ fs.writeFileSync(path.join(outputDir, "index.ts"), mainIndexContent, "utf-8");
153
+ console.log(`✓ Generated ${path.join(outputDir, "index.ts")}`);
154
+ console.log("\n✨ Generation complete!");
155
+ console.log(`Output directory: ${outputDir}`);
156
+ }
157
+ catch (error) {
158
+ console.error("Error generating types:", error);
159
+ if (error instanceof Error) {
160
+ console.error(error.message);
161
+ }
162
+ process.exit(1);
163
+ }
164
+ }
@@ -12,7 +12,7 @@ export declare class AdminApiClient {
12
12
  private apiKey;
13
13
  private basePath;
14
14
  constructor(config: ApiClientConfig);
15
- private request;
15
+ request(method: string, path: string, body?: unknown): Promise<any>;
16
16
  listCollections(): Promise<any>;
17
17
  createCollection(data: {
18
18
  type: string;
@@ -9,7 +9,7 @@ export class AdminApiClient {
9
9
  constructor(config) {
10
10
  this.apiUrl = config.apiUrl.replace(/\/$/, "");
11
11
  this.apiKey = config.apiKey;
12
- this.basePath = config.basePath || "/api/v1/sdk/manage";
12
+ this.basePath = config.basePath || "/api/v1/sdk";
13
13
  }
14
14
  async request(method, path, body) {
15
15
  // パスが / で始まる場合は basePath を使わない(絶対パス)
@@ -34,31 +34,31 @@ export class AdminApiClient {
34
34
  }
35
35
  // Collections
36
36
  async listCollections() {
37
- return this.request("GET", "/collections");
37
+ return this.request("GET", "/manage/collections");
38
38
  }
39
39
  async createCollection(data) {
40
- return this.request("POST", "/collections", data);
40
+ return this.request("POST", "/manage/collections", data);
41
41
  }
42
42
  // Data Types
43
43
  async listDataTypes() {
44
- return this.request("GET", "/data-types");
44
+ return this.request("GET", "/manage/data-types");
45
45
  }
46
46
  async createDataType(data) {
47
- return this.request("POST", "/data-types", data);
47
+ return this.request("POST", "/manage/data-types", data);
48
48
  }
49
49
  // Form Definitions
50
50
  async listFormDefinitions() {
51
- return this.request("GET", "/form-definitions");
51
+ return this.request("GET", "/manage/form-definitions");
52
52
  }
53
53
  async createFormDefinition(data) {
54
- return this.request("POST", "/form-definitions", data);
54
+ return this.request("POST", "/manage/form-definitions", data);
55
55
  }
56
56
  // Custom Components
57
57
  async listCustomComponents() {
58
- return this.request("GET", "/components");
58
+ return this.request("GET", "/manage/components");
59
59
  }
60
60
  async syncComponents(data) {
61
- return this.request("POST", "/components/sync", data);
61
+ return this.request("POST", "/manage/components/sync", data);
62
62
  }
63
63
  }
64
64
  export function createApiClient() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cmx-sdk",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "CMX SDK - CLI tool for managing CMX schemas and content",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.ts CHANGED
@@ -11,6 +11,7 @@ import { createDataType } from "./commands/create-data-type.js"
11
11
  import { createForm } from "./commands/create-form.js"
12
12
  import { syncComponents } from "./commands/sync-components.js"
13
13
  import { importSchema } from "./commands/import-schema.js"
14
+ import { generate } from "./commands/generate.js"
14
15
 
15
16
  // Load environment variables
16
17
  config()
@@ -92,15 +93,11 @@ program
92
93
  .requiredOption("--file <file>", "Path to schema JSON file")
93
94
  .action(importSchema)
94
95
 
95
- // generate (placeholder for future implementation)
96
+ // generate
96
97
  program
97
98
  .command("generate")
98
99
  .description("Generate TypeScript types and functions from CMX schema")
99
- .option("--output <dir>", "Output directory (default: generated/cmx)")
100
- .action(() => {
101
- console.log("The 'generate' command is not yet implemented.")
102
- console.log("This command will be added in a future release.")
103
- process.exit(1)
104
- })
100
+ .option("--output <dir>", "Output directory (default: cmx/generated)")
101
+ .action(generate)
105
102
 
106
103
  program.parse()
@@ -0,0 +1,219 @@
1
+ import { createApiClient } from "../lib/api-client.js"
2
+ import * as fs from "fs"
3
+ import * as path from "path"
4
+
5
+ interface Field {
6
+ key: string
7
+ type: string
8
+ label: string
9
+ required: boolean
10
+ description?: string
11
+ options?: {
12
+ choices?: Array<{ value: string; label: string }>
13
+ multiple?: boolean
14
+ targetType?: string
15
+ }
16
+ }
17
+
18
+ interface DataType {
19
+ slug: string
20
+ name: string
21
+ fields: Field[]
22
+ }
23
+
24
+ interface Collection {
25
+ slug: string
26
+ name: string
27
+ type: string
28
+ description?: string
29
+ }
30
+
31
+ interface SchemaResponse {
32
+ collections: Collection[]
33
+ dataTypes: DataType[]
34
+ forms: Array<{
35
+ slug: string
36
+ name: string
37
+ fields: Field[]
38
+ }>
39
+ }
40
+
41
+ function mapFieldType(field: Field): string {
42
+ const baseType = (() => {
43
+ switch (field.type) {
44
+ case "text":
45
+ case "textarea":
46
+ case "richtext":
47
+ case "markdown":
48
+ case "email":
49
+ case "url":
50
+ case "slug":
51
+ return "string"
52
+ case "number":
53
+ return "number"
54
+ case "boolean":
55
+ case "checkbox":
56
+ return "boolean"
57
+ case "date":
58
+ case "datetime":
59
+ return "string" // ISO 8601 string
60
+ case "select":
61
+ case "radio":
62
+ return "string"
63
+ case "image":
64
+ case "file":
65
+ return "string" // URL
66
+ case "relation":
67
+ return "string" // ID or slug
68
+ default:
69
+ return "unknown"
70
+ }
71
+ })()
72
+
73
+ return field.required ? baseType : `${baseType} | null`
74
+ }
75
+
76
+ function generateDataTypeFile(dataType: DataType): string {
77
+ const interfaceName = toPascalCase(dataType.slug)
78
+
79
+ const fields = dataType.fields
80
+ .map((field) => {
81
+ const comment = field.description
82
+ ? `/** ${field.label} - ${field.description} */`
83
+ : `/** ${field.label} */`
84
+ return ` ${comment}\n ${field.key}: ${mapFieldType(field)}`
85
+ })
86
+ .join("\n")
87
+
88
+ return `// Auto-generated by cmx-sdk generate
89
+ // Data Type: ${dataType.name} (${dataType.slug})
90
+ // Do not edit manually.
91
+
92
+ import { getDataEntries, getDataEntry } from "cmx-sdk"
93
+
94
+ export interface ${interfaceName} {
95
+ ${fields}
96
+ _meta: {
97
+ sortOrder: number
98
+ createdAt: string
99
+ updatedAt: string
100
+ }
101
+ }
102
+
103
+ export async function get${interfaceName}(options?: {
104
+ sortBy?: string
105
+ sortOrder?: string
106
+ limit?: number
107
+ offset?: number
108
+ }): Promise<{ type: string; items: ${interfaceName}[]; count: number }> {
109
+ const result = await getDataEntries("${dataType.slug}", options)
110
+ return result as unknown as { type: string; items: ${interfaceName}[]; count: number }
111
+ }
112
+
113
+ export async function get${interfaceName}ById(id: string): Promise<${interfaceName}> {
114
+ const result = await getDataEntry("${dataType.slug}", id)
115
+ return result as unknown as ${interfaceName}
116
+ }
117
+ `
118
+ }
119
+
120
+ function generateCollectionFile(collection: Collection): string {
121
+ const functionPrefix = toPascalCase(collection.slug)
122
+
123
+ return `// Auto-generated by cmx-sdk generate
124
+ // Collection: ${collection.name} (${collection.slug})
125
+ // Do not edit manually.
126
+
127
+ import { getCollectionPosts, getCollectionPostDetail } from "cmx-sdk"
128
+ import type { CollectionPostsResponse, CollectionPostDetailResponse } from "cmx-sdk"
129
+
130
+ export async function get${functionPrefix}Posts(): Promise<CollectionPostsResponse> {
131
+ return getCollectionPosts("${collection.slug}")
132
+ }
133
+
134
+ export async function get${functionPrefix}PostDetail(slug: string): Promise<CollectionPostDetailResponse> {
135
+ return getCollectionPostDetail("${collection.slug}", slug)
136
+ }
137
+ `
138
+ }
139
+
140
+ function toPascalCase(str: string): string {
141
+ return str
142
+ .split(/[-_]/)
143
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
144
+ .join("")
145
+ }
146
+
147
+ export async function generate(options: { output?: string }) {
148
+ const outputDir = options.output || "cmx/generated"
149
+
150
+ try {
151
+ console.log("Fetching schema from CMX API...")
152
+ const client = createApiClient()
153
+
154
+ // Fetch schema from /schema endpoint
155
+ const schema = (await client.request("GET", "/schema")) as SchemaResponse
156
+
157
+ console.log(`Found ${schema.collections.length} collections and ${schema.dataTypes.length} data types`)
158
+
159
+ // Create output directories
160
+ const collectionsDir = path.join(outputDir, "collections")
161
+ const dataTypesDir = path.join(outputDir, "data-types")
162
+
163
+ fs.mkdirSync(collectionsDir, { recursive: true })
164
+ fs.mkdirSync(dataTypesDir, { recursive: true })
165
+
166
+ // Generate collection files
167
+ for (const collection of schema.collections) {
168
+ const filePath = path.join(collectionsDir, `${collection.slug}.ts`)
169
+ const content = generateCollectionFile(collection)
170
+ fs.writeFileSync(filePath, content, "utf-8")
171
+ console.log(`✓ Generated ${filePath}`)
172
+ }
173
+
174
+ // Generate collection index
175
+ const collectionIndexContent = `// Auto-generated by cmx-sdk generate
176
+ // Do not edit manually.
177
+
178
+ ${schema.collections.map((c) => `export * from "./${c.slug}"`).join("\n")}
179
+ `
180
+ fs.writeFileSync(path.join(collectionsDir, "index.ts"), collectionIndexContent, "utf-8")
181
+ console.log(`✓ Generated ${path.join(collectionsDir, "index.ts")}`)
182
+
183
+ // Generate data type files
184
+ for (const dataType of schema.dataTypes) {
185
+ const filePath = path.join(dataTypesDir, `${dataType.slug}.ts`)
186
+ const content = generateDataTypeFile(dataType)
187
+ fs.writeFileSync(filePath, content, "utf-8")
188
+ console.log(`✓ Generated ${filePath}`)
189
+ }
190
+
191
+ // Generate data types index
192
+ const dataTypesIndexContent = `// Auto-generated by cmx-sdk generate
193
+ // Do not edit manually.
194
+
195
+ ${schema.dataTypes.map((dt) => `export * from "./${dt.slug}"`).join("\n")}
196
+ `
197
+ fs.writeFileSync(path.join(dataTypesDir, "index.ts"), dataTypesIndexContent, "utf-8")
198
+ console.log(`✓ Generated ${path.join(dataTypesDir, "index.ts")}`)
199
+
200
+ // Generate main index
201
+ const mainIndexContent = `// Auto-generated by cmx-sdk generate
202
+ // Do not edit manually.
203
+
204
+ export * from "./data-types"
205
+ export * from "./collections"
206
+ `
207
+ fs.writeFileSync(path.join(outputDir, "index.ts"), mainIndexContent, "utf-8")
208
+ console.log(`✓ Generated ${path.join(outputDir, "index.ts")}`)
209
+
210
+ console.log("\n✨ Generation complete!")
211
+ console.log(`Output directory: ${outputDir}`)
212
+ } catch (error) {
213
+ console.error("Error generating types:", error)
214
+ if (error instanceof Error) {
215
+ console.error(error.message)
216
+ }
217
+ process.exit(1)
218
+ }
219
+ }
@@ -7,7 +7,7 @@
7
7
  interface ApiClientConfig {
8
8
  apiUrl: string
9
9
  apiKey: string
10
- basePath?: string // デフォルト: /api/v1/sdk/manage
10
+ basePath?: string // デフォルト: /api/v1/sdk
11
11
  }
12
12
 
13
13
  export class AdminApiClient {
@@ -18,10 +18,10 @@ export class AdminApiClient {
18
18
  constructor(config: ApiClientConfig) {
19
19
  this.apiUrl = config.apiUrl.replace(/\/$/, "")
20
20
  this.apiKey = config.apiKey
21
- this.basePath = config.basePath || "/api/v1/sdk/manage"
21
+ this.basePath = config.basePath || "/api/v1/sdk"
22
22
  }
23
23
 
24
- private async request(method: string, path: string, body?: unknown) {
24
+ async request(method: string, path: string, body?: unknown) {
25
25
  // パスが / で始まる場合は basePath を使わない(絶対パス)
26
26
  // それ以外の場合は basePath を結合する(相対パス)
27
27
  const url = path.startsWith("/api/")
@@ -50,7 +50,7 @@ export class AdminApiClient {
50
50
 
51
51
  // Collections
52
52
  async listCollections() {
53
- return this.request("GET", "/collections")
53
+ return this.request("GET", "/manage/collections")
54
54
  }
55
55
 
56
56
  async createCollection(data: {
@@ -59,12 +59,12 @@ export class AdminApiClient {
59
59
  name: string
60
60
  description?: string
61
61
  }) {
62
- return this.request("POST", "/collections", data)
62
+ return this.request("POST", "/manage/collections", data)
63
63
  }
64
64
 
65
65
  // Data Types
66
66
  async listDataTypes() {
67
- return this.request("GET", "/data-types")
67
+ return this.request("GET", "/manage/data-types")
68
68
  }
69
69
 
70
70
  async createDataType(data: {
@@ -80,12 +80,12 @@ export class AdminApiClient {
80
80
  options?: Record<string, unknown>
81
81
  }>
82
82
  }) {
83
- return this.request("POST", "/data-types", data)
83
+ return this.request("POST", "/manage/data-types", data)
84
84
  }
85
85
 
86
86
  // Form Definitions
87
87
  async listFormDefinitions() {
88
- return this.request("GET", "/form-definitions")
88
+ return this.request("GET", "/manage/form-definitions")
89
89
  }
90
90
 
91
91
  async createFormDefinition(data: {
@@ -101,12 +101,12 @@ export class AdminApiClient {
101
101
  options?: Record<string, unknown>
102
102
  }>
103
103
  }) {
104
- return this.request("POST", "/form-definitions", data)
104
+ return this.request("POST", "/manage/form-definitions", data)
105
105
  }
106
106
 
107
107
  // Custom Components
108
108
  async listCustomComponents() {
109
- return this.request("GET", "/components")
109
+ return this.request("GET", "/manage/components")
110
110
  }
111
111
 
112
112
  async syncComponents(data: {
@@ -118,7 +118,7 @@ export class AdminApiClient {
118
118
  examples?: string[]
119
119
  }>
120
120
  }) {
121
- return this.request("POST", "/components/sync", data)
121
+ return this.request("POST", "/manage/components/sync", data)
122
122
  }
123
123
  }
124
124