@unifiedcommerce/cli 0.0.1

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.
@@ -0,0 +1,13 @@
1
+ export declare const deployCommand: import("citty").CommandDef<{
2
+ target: {
3
+ type: "string";
4
+ required: true;
5
+ description: string;
6
+ };
7
+ prod: {
8
+ type: "boolean";
9
+ default: false;
10
+ description: string;
11
+ };
12
+ }>;
13
+ //# sourceMappingURL=deploy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,aAAa;;;;;;;;;;;EA4CxB,CAAC"}
@@ -0,0 +1,45 @@
1
+ import { spawn } from "node:child_process";
2
+ import { defineCommand } from "citty";
3
+ import consola from "consola";
4
+ export const deployCommand = defineCommand({
5
+ meta: {
6
+ name: "deploy",
7
+ description: "Deploy UnifiedCommerce app to a target environment.",
8
+ },
9
+ args: {
10
+ target: {
11
+ type: "string",
12
+ required: true,
13
+ description: "Deployment target. Supported: vercel",
14
+ },
15
+ prod: {
16
+ type: "boolean",
17
+ default: false,
18
+ description: "Deploy production build",
19
+ },
20
+ },
21
+ async run({ args }) {
22
+ const target = String(args.target).toLowerCase();
23
+ if (target !== "vercel") {
24
+ throw new Error(`Unsupported deploy target: ${target}. Currently supported: vercel.`);
25
+ }
26
+ const command = process.platform === "win32" ? "npx.cmd" : "npx";
27
+ const deployArgs = ["vercel", "deploy"];
28
+ if (args.prod)
29
+ deployArgs.push("--prod");
30
+ consola.info(`Running deploy for target=${target} (${deployArgs.join(" ")})`);
31
+ await new Promise((resolvePromise, rejectPromise) => {
32
+ const proc = spawn(command, deployArgs, {
33
+ stdio: "inherit",
34
+ shell: false,
35
+ });
36
+ proc.on("exit", (code) => {
37
+ if (code === 0)
38
+ resolvePromise();
39
+ else
40
+ rejectPromise(new Error(`Deploy command failed with code ${code}`));
41
+ });
42
+ proc.on("error", rejectPromise);
43
+ });
44
+ },
45
+ });
@@ -0,0 +1,7 @@
1
+ export declare const devCommand: import("citty").CommandDef<{
2
+ port: {
3
+ type: "string";
4
+ default: string;
5
+ };
6
+ }>;
7
+ //# sourceMappingURL=dev.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,UAAU;;;;;EAgCrB,CAAC"}
@@ -0,0 +1,32 @@
1
+ import { spawn } from "node:child_process";
2
+ import { defineCommand } from "citty";
3
+ import consola from "consola";
4
+ export const devCommand = defineCommand({
5
+ meta: {
6
+ name: "dev",
7
+ description: "Run local dev server with file watching",
8
+ },
9
+ args: {
10
+ port: {
11
+ type: "string",
12
+ default: "3000",
13
+ },
14
+ },
15
+ async run({ args }) {
16
+ const port = String(args.port);
17
+ consola.info(`Starting dev server on port ${port}`);
18
+ const proc = spawn(process.platform === "win32" ? "npx.cmd" : "npx", ["tsx", "watch", "src/dev-server.ts", "--port", port], {
19
+ stdio: "inherit",
20
+ shell: false,
21
+ });
22
+ await new Promise((resolvePromise, rejectPromise) => {
23
+ proc.on("exit", (code) => {
24
+ if (code === 0)
25
+ resolvePromise();
26
+ else
27
+ rejectPromise(new Error(`dev exited with code ${code}`));
28
+ });
29
+ proc.on("error", rejectPromise);
30
+ });
31
+ },
32
+ });
@@ -0,0 +1,2 @@
1
+ export declare const generateMigrationCommand: import("citty").CommandDef<import("citty").ArgsDef>;
2
+ //# sourceMappingURL=generate-migration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-migration.d.ts","sourceRoot":"","sources":["../../src/commands/generate-migration.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,wBAAwB,qDAmBnC,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { spawn } from "node:child_process";
2
+ import { defineCommand } from "citty";
3
+ export const generateMigrationCommand = defineCommand({
4
+ meta: {
5
+ name: "generate migration",
6
+ description: "Generate Drizzle migration",
7
+ },
8
+ async run() {
9
+ await new Promise((resolvePromise, rejectPromise) => {
10
+ const proc = spawn(process.platform === "win32" ? "npx.cmd" : "npx", ["drizzle-kit", "generate"], { stdio: "inherit" });
11
+ proc.on("exit", (code) => {
12
+ if (code === 0)
13
+ resolvePromise();
14
+ else
15
+ rejectPromise(new Error(`drizzle-kit generate failed with code ${code}`));
16
+ });
17
+ proc.on("error", rejectPromise);
18
+ });
19
+ },
20
+ });
@@ -0,0 +1,45 @@
1
+ export declare const importCommand: import("citty").CommandDef<{
2
+ source: {
3
+ type: "string";
4
+ required: true;
5
+ description: string;
6
+ };
7
+ input: {
8
+ type: "string";
9
+ description: string;
10
+ };
11
+ targetUrl: {
12
+ type: "string";
13
+ default: string;
14
+ description: string;
15
+ };
16
+ authToken: {
17
+ type: "string";
18
+ description: string;
19
+ };
20
+ apiKey: {
21
+ type: "string";
22
+ description: string;
23
+ };
24
+ storeUrl: {
25
+ type: "string";
26
+ description: string;
27
+ };
28
+ consumerKey: {
29
+ type: "string";
30
+ description: string;
31
+ };
32
+ consumerSecret: {
33
+ type: "string";
34
+ description: string;
35
+ };
36
+ mapping: {
37
+ type: "string";
38
+ description: string;
39
+ };
40
+ entityType: {
41
+ type: "string";
42
+ default: string;
43
+ };
44
+ }>;
45
+ //# sourceMappingURL=import.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import.d.ts","sourceRoot":"","sources":["../../src/commands/import.ts"],"names":[],"mappings":"AAwJA,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2KxB,CAAC"}
@@ -0,0 +1,237 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { extname, resolve } from "node:path";
3
+ import { defineCommand } from "citty";
4
+ import consola from "consola";
5
+ // @ts-ignore — missing type declarations
6
+ import { importFlat } from "@unifiedcommerce/import-flat";
7
+ // @ts-ignore — missing type declarations
8
+ import { importShopifyCatalog } from "@unifiedcommerce/import-shopify";
9
+ // @ts-ignore — missing type declarations
10
+ import { importWooCommerceCatalog } from "@unifiedcommerce/import-woocommerce";
11
+ function toBaseUrl(raw) {
12
+ return (raw ?? "http://localhost:3000").replace(/\/$/, "");
13
+ }
14
+ function toHeaders(token) {
15
+ if (!token)
16
+ return {};
17
+ return { authorization: `Bearer ${token}` };
18
+ }
19
+ async function requestJson(baseUrl, path, method, body, token) {
20
+ const response = await fetch(`${baseUrl}${path}`, {
21
+ method,
22
+ headers: {
23
+ "content-type": "application/json",
24
+ ...toHeaders(token),
25
+ },
26
+ body: JSON.stringify(body),
27
+ });
28
+ const payload = (await response.json().catch(() => ({})));
29
+ if (!response.ok) {
30
+ const message = payload.error?.message ?? `HTTP ${response.status}`;
31
+ throw new Error(`Request failed for ${method} ${path}: ${message}`);
32
+ }
33
+ if (payload.data === undefined) {
34
+ throw new Error(`Expected data payload for ${method} ${path}.`);
35
+ }
36
+ return payload.data;
37
+ }
38
+ function createRestImportTarget(baseUrl, token) {
39
+ return {
40
+ async createEntity(input) {
41
+ return requestJson(baseUrl, "/api/catalog/entities", "POST", input, token);
42
+ },
43
+ async createOptionType(input) {
44
+ return requestJson(baseUrl, `/api/catalog/entities/${input.entityId}/options`, "POST", input, token);
45
+ },
46
+ async createOptionValue(input) {
47
+ return requestJson(baseUrl, `/api/catalog/options/${input.optionTypeId}/values`, "POST", input, token);
48
+ },
49
+ async createVariant(input) {
50
+ return requestJson(baseUrl, `/api/catalog/entities/${input.entityId}/variants`, "POST", input, token);
51
+ },
52
+ async uploadMedia(input) {
53
+ const form = new FormData();
54
+ form.set("file", new File([input.data], input.filename, {
55
+ type: input.contentType,
56
+ }));
57
+ if (input.alt)
58
+ form.set("alt", input.alt);
59
+ const response = await fetch(`${baseUrl}/api/media/upload`, {
60
+ method: "POST",
61
+ headers: {
62
+ ...toHeaders(token),
63
+ },
64
+ body: form,
65
+ });
66
+ const payload = (await response.json().catch(() => ({})));
67
+ if (!response.ok || !payload.data) {
68
+ throw new Error(payload.error?.message ?? `Media upload failed (${response.status}).`);
69
+ }
70
+ return payload.data;
71
+ },
72
+ async attachMedia(input) {
73
+ await requestJson(baseUrl, "/api/media/attach", "POST", input, token);
74
+ },
75
+ };
76
+ }
77
+ async function loadJsonInput(path) {
78
+ const absolute = resolve(process.cwd(), path);
79
+ const raw = await readFile(absolute, "utf8");
80
+ return JSON.parse(raw);
81
+ }
82
+ export const importCommand = defineCommand({
83
+ meta: {
84
+ name: "import",
85
+ description: "Import catalog data from Shopify, WooCommerce, or flat CSV/JSON into UnifiedCommerce.",
86
+ },
87
+ args: {
88
+ source: {
89
+ type: "string",
90
+ required: true,
91
+ description: "shopify | woocommerce | flat",
92
+ },
93
+ input: {
94
+ type: "string",
95
+ description: "Path to input JSON/CSV for offline imports",
96
+ },
97
+ targetUrl: {
98
+ type: "string",
99
+ default: "http://localhost:3000",
100
+ description: "UnifiedCommerce API base URL",
101
+ },
102
+ authToken: {
103
+ type: "string",
104
+ description: "Optional bearer token for target API",
105
+ },
106
+ apiKey: {
107
+ type: "string",
108
+ description: "Shopify admin API key/token",
109
+ },
110
+ storeUrl: {
111
+ type: "string",
112
+ description: "Shopify or WooCommerce store URL",
113
+ },
114
+ consumerKey: {
115
+ type: "string",
116
+ description: "WooCommerce consumer key",
117
+ },
118
+ consumerSecret: {
119
+ type: "string",
120
+ description: "WooCommerce consumer secret",
121
+ },
122
+ mapping: {
123
+ type: "string",
124
+ description: "Path to flat-import mapping JSON (required for source=flat)",
125
+ },
126
+ entityType: {
127
+ type: "string",
128
+ default: "product",
129
+ },
130
+ },
131
+ async run({ args }) {
132
+ const source = String(args.source).toLowerCase();
133
+ const baseUrl = toBaseUrl(args.targetUrl ? String(args.targetUrl) : undefined);
134
+ const token = args.authToken ? String(args.authToken) : undefined;
135
+ const target = createRestImportTarget(baseUrl, token);
136
+ if (source === "shopify") {
137
+ let products;
138
+ let customers;
139
+ if (args.input) {
140
+ const parsed = await loadJsonInput(String(args.input));
141
+ if (Array.isArray(parsed)) {
142
+ products = parsed;
143
+ }
144
+ else if (parsed && typeof parsed === "object") {
145
+ products = parsed.products;
146
+ customers = parsed.customers;
147
+ }
148
+ }
149
+ const imported = await importShopifyCatalog({
150
+ target,
151
+ ...(args.storeUrl ? { storeUrl: String(args.storeUrl) } : {}),
152
+ ...(args.apiKey ? { apiKey: String(args.apiKey) } : {}),
153
+ entityType: String(args.entityType),
154
+ ...(Array.isArray(products) ? { products: products } : {}),
155
+ ...(Array.isArray(customers) ? { customers: customers } : {}),
156
+ });
157
+ if (!imported.ok) {
158
+ throw new Error(imported.error.message);
159
+ }
160
+ consola.success(`Shopify import completed: ${imported.value.entitiesImported} entities, ${imported.value.variantsImported} variants, ${imported.value.mediaImported} media.`);
161
+ if (imported.value.customersImported > 0) {
162
+ consola.info(`Imported customers: ${imported.value.customersImported}`);
163
+ }
164
+ if (imported.value.errors.length > 0) {
165
+ consola.warn(`Import completed with ${imported.value.errors.length} warnings.`);
166
+ }
167
+ return;
168
+ }
169
+ if (source === "woocommerce") {
170
+ let products;
171
+ let customers;
172
+ if (args.input) {
173
+ const parsed = await loadJsonInput(String(args.input));
174
+ if (Array.isArray(parsed)) {
175
+ products = parsed;
176
+ }
177
+ else if (parsed && typeof parsed === "object") {
178
+ products = parsed.products;
179
+ customers = parsed.customers;
180
+ }
181
+ }
182
+ const imported = await importWooCommerceCatalog({
183
+ target,
184
+ ...(args.storeUrl ? { storeUrl: String(args.storeUrl) } : {}),
185
+ ...(args.consumerKey ? { consumerKey: String(args.consumerKey) } : {}),
186
+ ...(args.consumerSecret ? { consumerSecret: String(args.consumerSecret) } : {}),
187
+ entityType: String(args.entityType),
188
+ ...(Array.isArray(products) ? { products: products } : {}),
189
+ ...(Array.isArray(customers) ? { customers: customers } : {}),
190
+ });
191
+ if (!imported.ok) {
192
+ throw new Error(imported.error.message);
193
+ }
194
+ consola.success(`WooCommerce import completed: ${imported.value.entitiesImported} entities, ${imported.value.variantsImported} variants, ${imported.value.mediaImported} media.`);
195
+ if (imported.value.customersImported > 0) {
196
+ consola.info(`Imported customers: ${imported.value.customersImported}`);
197
+ }
198
+ if (imported.value.errors.length > 0) {
199
+ consola.warn(`Import completed with ${imported.value.errors.length} warnings.`);
200
+ }
201
+ return;
202
+ }
203
+ if (source === "flat") {
204
+ if (!args.mapping) {
205
+ throw new Error("--mapping is required for source=flat");
206
+ }
207
+ const mappingParsed = await loadJsonInput(String(args.mapping));
208
+ if (!mappingParsed || typeof mappingParsed !== "object") {
209
+ throw new Error("Flat import mapping file must be a JSON object.");
210
+ }
211
+ if (!args.input) {
212
+ throw new Error("--input is required for source=flat");
213
+ }
214
+ const inputPath = resolve(process.cwd(), String(args.input));
215
+ const raw = await readFile(inputPath, "utf8");
216
+ const extension = extname(inputPath).toLowerCase();
217
+ const imported = await importFlat({
218
+ mapping: mappingParsed,
219
+ target: {
220
+ async createEntity(input) {
221
+ return target.createEntity(input);
222
+ },
223
+ },
224
+ ...(extension === ".csv" ? { csv: raw } : { json: raw }),
225
+ });
226
+ if (!imported.ok) {
227
+ throw new Error(imported.error.message);
228
+ }
229
+ consola.success(`Flat import completed: ${imported.value.imported} entities imported.`);
230
+ if (imported.value.failed > 0) {
231
+ consola.warn(`Failed rows: ${imported.value.failed}`);
232
+ }
233
+ return;
234
+ }
235
+ throw new Error(`Unsupported source: ${source}. Expected shopify, woocommerce, or flat.`);
236
+ },
237
+ });
@@ -0,0 +1,12 @@
1
+ export declare const initCommand: import("citty").CommandDef<{
2
+ projectName: {
3
+ type: "positional";
4
+ description: string;
5
+ required: true;
6
+ };
7
+ template: {
8
+ type: "string";
9
+ default: string;
10
+ };
11
+ }>;
12
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAeA,eAAO,MAAM,WAAW;;;;;;;;;;EA+CtB,CAAC"}
@@ -0,0 +1,52 @@
1
+ import { join, resolve } from "node:path";
2
+ import { existsSync } from "node:fs";
3
+ import { fileURLToPath } from "node:url";
4
+ import { defineCommand } from "citty";
5
+ import consola from "consola";
6
+ import { downloadTemplate } from "giget";
7
+ import { copyDir, readJson, writeJson } from "../utils";
8
+ const currentDir = fileURLToPath(new URL(".", import.meta.url));
9
+ export const initCommand = defineCommand({
10
+ meta: {
11
+ name: "init",
12
+ description: "Scaffold a UnifiedCommerce project",
13
+ },
14
+ args: {
15
+ projectName: {
16
+ type: "positional",
17
+ description: "Project directory name",
18
+ required: true,
19
+ },
20
+ template: {
21
+ type: "string",
22
+ default: "starter",
23
+ },
24
+ },
25
+ async run({ args }) {
26
+ const projectName = String(args.projectName);
27
+ const destination = resolve(process.cwd(), projectName);
28
+ if (existsSync(destination)) {
29
+ throw new Error(`Directory ${destination} already exists.`);
30
+ }
31
+ const localTemplatePath = resolve(currentDir, "../../templates", String(args.template));
32
+ if (existsSync(localTemplatePath)) {
33
+ await copyDir(localTemplatePath, destination);
34
+ }
35
+ else {
36
+ await downloadTemplate(`gh:unifiedcommerce/templates/${String(args.template)}`, {
37
+ dir: destination,
38
+ });
39
+ }
40
+ const packageJsonPath = join(destination, "package.json");
41
+ if (existsSync(packageJsonPath)) {
42
+ const pkg = await readJson(packageJsonPath);
43
+ pkg.name = projectName;
44
+ await writeJson(packageJsonPath, pkg);
45
+ }
46
+ consola.success(`Project created at ${destination}`);
47
+ consola.info(`Next steps:`);
48
+ consola.info(` cd ${projectName}`);
49
+ consola.info(` bun install`);
50
+ consola.info(` bun run dev`);
51
+ },
52
+ });
@@ -0,0 +1,2 @@
1
+ export declare const migrateCommand: import("citty").CommandDef<import("citty").ArgsDef>;
2
+ //# sourceMappingURL=migrate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/commands/migrate.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,cAAc,qDAmBzB,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { spawn } from "node:child_process";
2
+ import { defineCommand } from "citty";
3
+ export const migrateCommand = defineCommand({
4
+ meta: {
5
+ name: "migrate",
6
+ description: "Apply Drizzle migrations",
7
+ },
8
+ async run() {
9
+ await new Promise((resolvePromise, rejectPromise) => {
10
+ const proc = spawn(process.platform === "win32" ? "npx.cmd" : "npx", ["drizzle-kit", "migrate"], { stdio: "inherit" });
11
+ proc.on("exit", (code) => {
12
+ if (code === 0)
13
+ resolvePromise();
14
+ else
15
+ rejectPromise(new Error(`drizzle-kit migrate failed with code ${code}`));
16
+ });
17
+ proc.on("error", rejectPromise);
18
+ });
19
+ },
20
+ });
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env node
2
+ import { defineCommand, runMain } from "citty";
3
+ import { initCommand } from "./commands/init";
4
+ import { devCommand } from "./commands/dev";
5
+ import { generateMigrationCommand } from "./commands/generate-migration";
6
+ import { migrateCommand } from "./commands/migrate";
7
+ import { deployCommand } from "./commands/deploy";
8
+ import { importCommand } from "./commands/import";
9
+ const main = defineCommand({
10
+ meta: {
11
+ name: "@unifiedcommerce/cli",
12
+ version: "0.0.1",
13
+ description: "UnifiedCommerce Engine CLI",
14
+ },
15
+ subCommands: {
16
+ init: initCommand,
17
+ dev: devCommand,
18
+ migrate: migrateCommand,
19
+ deploy: deployCommand,
20
+ import: importCommand,
21
+ generate: defineCommand({
22
+ subCommands: {
23
+ migration: generateMigrationCommand,
24
+ },
25
+ }),
26
+ },
27
+ });
28
+ await runMain(main);