@o2vend/theme-cli 1.0.0

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.

Potentially problematic release.


This version of @o2vend/theme-cli might be problematic. Click here for more details.

@@ -0,0 +1,225 @@
1
+ /**
2
+ * Mock O2VEND Storefront API Server
3
+ * Provides realistic mock data for local development
4
+ */
5
+
6
+ const express = require('express');
7
+ const mockDataGenerator = require('./mock-data');
8
+
9
+ class MockApiServer {
10
+ constructor(options = {}) {
11
+ this.port = options.port || 3001;
12
+ this.app = express();
13
+ this.server = null;
14
+ this.mockData = mockDataGenerator.generateMockData();
15
+ }
16
+
17
+ /**
18
+ * Setup routes
19
+ */
20
+ setupRoutes() {
21
+ // Body parser
22
+ this.app.use(express.json());
23
+ this.app.use(express.urlencoded({ extended: true }));
24
+
25
+ // Store Info
26
+ this.app.get('/shopfront/api/v2/storeinfo', (req, res) => {
27
+ res.json(this.mockData.store);
28
+ });
29
+
30
+ // Store Settings
31
+ this.app.get('/shopfront/api/v2/settings', (req, res) => {
32
+ res.json(this.mockData.settings);
33
+ });
34
+
35
+ // Products
36
+ this.app.post('/shopfront/api/v2/products', (req, res) => {
37
+ const { limit = 20, offset = 0, categoryId, brandId, search } = req.body;
38
+ let products = [...this.mockData.products];
39
+
40
+ // Filter by category
41
+ if (categoryId) {
42
+ products = products.filter(p => p.categoryId === categoryId);
43
+ }
44
+
45
+ // Filter by brand
46
+ if (brandId) {
47
+ products = products.filter(p => p.brandId === brandId);
48
+ }
49
+
50
+ // Search
51
+ if (search) {
52
+ const searchLower = search.toLowerCase();
53
+ products = products.filter(p =>
54
+ p.title.toLowerCase().includes(searchLower) ||
55
+ p.description?.toLowerCase().includes(searchLower)
56
+ );
57
+ }
58
+
59
+ // Paginate
60
+ const paginated = products.slice(offset, offset + limit);
61
+
62
+ res.json({
63
+ products: paginated,
64
+ total: products.length,
65
+ limit,
66
+ offset
67
+ });
68
+ });
69
+
70
+ // Product by ID
71
+ this.app.get('/shopfront/api/v2/products/:id', (req, res) => {
72
+ const product = this.mockData.products.find(p => p.id === req.params.id);
73
+ if (product) {
74
+ res.json(product);
75
+ } else {
76
+ res.status(404).json({ error: 'Product not found' });
77
+ }
78
+ });
79
+
80
+ // Product Search
81
+ this.app.post('/shopfront/api/v2/products/search', (req, res) => {
82
+ const { query, limit = 20, offset = 0 } = req.body;
83
+ const searchLower = (query || '').toLowerCase();
84
+
85
+ let products = this.mockData.products.filter(p =>
86
+ p.title.toLowerCase().includes(searchLower) ||
87
+ p.description?.toLowerCase().includes(searchLower)
88
+ );
89
+
90
+ const paginated = products.slice(offset, offset + limit);
91
+
92
+ res.json({
93
+ products: paginated,
94
+ total: products.length,
95
+ limit,
96
+ offset,
97
+ query
98
+ });
99
+ });
100
+
101
+ // Categories
102
+ this.app.post('/shopfront/api/v2/categories', (req, res) => {
103
+ const { limit = 50, offset = 0 } = req.body;
104
+ const paginated = this.mockData.categories.slice(offset, offset + limit);
105
+
106
+ res.json({
107
+ categories: paginated,
108
+ total: this.mockData.categories.length,
109
+ limit,
110
+ offset
111
+ });
112
+ });
113
+
114
+ // Category by ID
115
+ this.app.get('/shopfront/api/v2/categories/:id', (req, res) => {
116
+ const category = this.mockData.categories.find(c => c.id === req.params.id);
117
+ if (category) {
118
+ res.json(category);
119
+ } else {
120
+ res.status(404).json({ error: 'Category not found' });
121
+ }
122
+ });
123
+
124
+ // Brands
125
+ this.app.post('/shopfront/api/v2/brands', (req, res) => {
126
+ const { limit = 50, offset = 0 } = req.body;
127
+ const paginated = this.mockData.brands.slice(offset, offset + limit);
128
+
129
+ res.json({
130
+ brands: paginated,
131
+ total: this.mockData.brands.length,
132
+ limit,
133
+ offset
134
+ });
135
+ });
136
+
137
+ // Widgets
138
+ this.app.post('/shopfront/api/v2/widgets', (req, res) => {
139
+ const { section, pageId, status } = req.body;
140
+
141
+ let widgets = [...this.mockData.widgets];
142
+
143
+ // Filter by section
144
+ if (section) {
145
+ widgets = widgets.filter(w => w.section === section || w.sectionName === section);
146
+ }
147
+
148
+ // Filter by status
149
+ if (status) {
150
+ widgets = widgets.filter(w => w.status === status);
151
+ }
152
+
153
+ res.json({
154
+ widgets: widgets,
155
+ total: widgets.length
156
+ });
157
+ });
158
+
159
+ // Cart endpoints
160
+ this.app.get('/shopfront/api/v2/cart/:cartId', (req, res) => {
161
+ res.json(this.mockData.cart);
162
+ });
163
+
164
+ this.app.post('/shopfront/api/v2/cart/:cartId/items', (req, res) => {
165
+ // Add item to cart
166
+ const newItem = {
167
+ id: Date.now().toString(),
168
+ productId: req.body.productId,
169
+ variantId: req.body.variantId,
170
+ quantity: req.body.quantity || 1,
171
+ ...req.body
172
+ };
173
+ this.mockData.cart.items.push(newItem);
174
+ this.mockData.cart.itemCount = this.mockData.cart.items.length;
175
+ this.mockData.cart.total = this.mockData.cart.items.reduce((sum, item) =>
176
+ sum + (item.price * item.quantity), 0
177
+ );
178
+ res.json(this.mockData.cart);
179
+ });
180
+
181
+ // Default route
182
+ this.app.use((req, res) => {
183
+ res.status(404).json({
184
+ error: 'Not found',
185
+ message: `Mock API endpoint not found: ${req.method} ${req.path}`
186
+ });
187
+ });
188
+ }
189
+
190
+ /**
191
+ * Start mock API server
192
+ */
193
+ async start() {
194
+ return new Promise((resolve, reject) => {
195
+ this.setupRoutes();
196
+
197
+ this.server = this.app.listen(this.port, (error) => {
198
+ if (error) {
199
+ reject(error);
200
+ return;
201
+ }
202
+
203
+ console.log(`✅ Mock API server running on http://localhost:${this.port}`);
204
+ resolve(this.server);
205
+ });
206
+ });
207
+ }
208
+
209
+ /**
210
+ * Stop mock API server
211
+ */
212
+ async stop() {
213
+ return new Promise((resolve) => {
214
+ if (this.server) {
215
+ this.server.close(() => {
216
+ resolve();
217
+ });
218
+ } else {
219
+ resolve();
220
+ }
221
+ });
222
+ }
223
+ }
224
+
225
+ module.exports = MockApiServer;
@@ -0,0 +1,290 @@
1
+ /**
2
+ * Mock Data Generator
3
+ * Generates realistic mock data for development
4
+ */
5
+
6
+ /**
7
+ * Generate mock data
8
+ * @returns {Object} Mock data object
9
+ */
10
+ function generateMockData() {
11
+ return {
12
+ store: {
13
+ id: 'mock-store-1',
14
+ name: 'My O2VEND Store',
15
+ description: 'A beautiful e-commerce store powered by O2VEND',
16
+ domain: 'localhost:3000',
17
+ email: 'store@example.com',
18
+ phone: '+1234567890',
19
+ address: {
20
+ address1: '123 Main Street',
21
+ city: 'New York',
22
+ state: 'NY',
23
+ zip: '10001',
24
+ country: 'US'
25
+ },
26
+ currency: 'USD',
27
+ currencySymbol: '$',
28
+ locale: 'en-US',
29
+ timezone: 'America/New_York',
30
+ settings: {
31
+ currencySymbol: '$',
32
+ currencyFormat: '#,##0.00',
33
+ currencyDecimalDigits: 2,
34
+ currencyGroupSeparator: ',',
35
+ currencyDecimalSeparator: '.'
36
+ }
37
+ },
38
+
39
+ settings: {
40
+ shopName: 'My O2VEND Store',
41
+ shopEmail: 'store@example.com',
42
+ shopPhone: '+1234567890'
43
+ },
44
+
45
+ products: generateMockProducts(20),
46
+ categories: generateMockCategories(10),
47
+ brands: generateMockBrands(8),
48
+ widgets: generateMockWidgets(),
49
+ cart: {
50
+ id: 'mock-cart-1',
51
+ items: [],
52
+ total: 0,
53
+ itemCount: 0,
54
+ currency: 'USD'
55
+ }
56
+ };
57
+ }
58
+
59
+ /**
60
+ * Generate mock products
61
+ * @param {number} count - Number of products to generate
62
+ * @returns {Array} Mock products
63
+ */
64
+ function generateMockProducts(count = 20) {
65
+ const products = [];
66
+ const productNames = [
67
+ 'Wireless Headphones', 'Smart Watch', 'Laptop Stand', 'USB-C Cable',
68
+ 'Wireless Mouse', 'Keyboard', 'Monitor', 'Webcam', 'Speakers',
69
+ 'Tablet', 'Smartphone', 'Camera', 'Drone', 'Gaming Chair',
70
+ 'Desk Lamp', 'Notebook', 'Pen Set', 'Backpack', 'Water Bottle',
71
+ 'Coffee Mug', 'T-Shirt', 'Jeans', 'Sneakers', 'Sunglasses'
72
+ ];
73
+
74
+ for (let i = 0; i < count; i++) {
75
+ const productId = `product-${i + 1}`;
76
+ const price = Math.floor(Math.random() * 50000) + 1000; // $10 to $500 (in cents)
77
+ const comparePrice = price * 1.5;
78
+
79
+ products.push({
80
+ id: productId,
81
+ title: productNames[i % productNames.length] + ` ${i + 1}`,
82
+ handle: `product-${i + 1}`.toLowerCase(),
83
+ description: `Description for ${productNames[i % productNames.length]}. High quality product with excellent features.`,
84
+ price: price,
85
+ compareAtPrice: comparePrice,
86
+ discountPercentage: Math.floor(((comparePrice - price) / comparePrice) * 100),
87
+ currency: 'USD',
88
+ inStock: Math.random() > 0.2,
89
+ stock: Math.floor(Math.random() * 100) + 10,
90
+ sku: `SKU-${i + 1}`,
91
+ categoryId: `category-${(i % 10) + 1}`,
92
+ brandId: `brand-${(i % 8) + 1}`,
93
+ images: [
94
+ {
95
+ id: `img-${i + 1}-1`,
96
+ url: `https://via.placeholder.com/800x800?text=${encodeURIComponent(productNames[i % productNames.length])}`,
97
+ alt: productNames[i % productNames.length],
98
+ width: 800,
99
+ height: 800
100
+ }
101
+ ],
102
+ variants: [
103
+ {
104
+ id: `variant-${i + 1}-1`,
105
+ title: 'Default',
106
+ price: price,
107
+ compareAtPrice: comparePrice,
108
+ sku: `SKU-${i + 1}`,
109
+ inStock: Math.random() > 0.2,
110
+ stock: Math.floor(Math.random() * 100) + 10
111
+ }
112
+ ],
113
+ tags: ['featured', 'new', 'sale'],
114
+ createdAt: new Date(Date.now() - Math.random() * 365 * 24 * 60 * 60 * 1000).toISOString()
115
+ });
116
+ }
117
+
118
+ return products;
119
+ }
120
+
121
+ /**
122
+ * Generate mock categories
123
+ * @param {number} count - Number of categories to generate
124
+ * @returns {Array} Mock categories
125
+ */
126
+ function generateMockCategories(count = 10) {
127
+ const categories = [];
128
+ const categoryNames = [
129
+ 'Electronics', 'Clothing', 'Accessories', 'Home & Garden',
130
+ 'Sports', 'Books', 'Toys', 'Beauty', 'Automotive', 'Food & Beverages'
131
+ ];
132
+
133
+ for (let i = 0; i < count; i++) {
134
+ categories.push({
135
+ id: `category-${i + 1}`,
136
+ name: categoryNames[i] || `Category ${i + 1}`,
137
+ handle: categoryNames[i]?.toLowerCase().replace(/\s+/g, '-') || `category-${i + 1}`,
138
+ description: `Products in ${categoryNames[i] || `Category ${i + 1}`} category`,
139
+ image: `https://via.placeholder.com/400x300?text=${encodeURIComponent(categoryNames[i] || `Category ${i + 1}`)}`,
140
+ productCount: Math.floor(Math.random() * 50) + 5,
141
+ parentId: i > 5 ? `category-${Math.floor(Math.random() * 5) + 1}` : null
142
+ });
143
+ }
144
+
145
+ return categories;
146
+ }
147
+
148
+ /**
149
+ * Generate mock brands
150
+ * @param {number} count - Number of brands to generate
151
+ * @returns {Array} Mock brands
152
+ */
153
+ function generateMockBrands(count = 8) {
154
+ const brands = [];
155
+ const brandNames = [
156
+ 'TechBrand', 'StyleCo', 'HomeGoods', 'SportMax',
157
+ 'BookCorner', 'ToyWorld', 'BeautyPro', 'AutoParts'
158
+ ];
159
+
160
+ for (let i = 0; i < count; i++) {
161
+ brands.push({
162
+ id: `brand-${i + 1}`,
163
+ name: brandNames[i] || `Brand ${i + 1}`,
164
+ handle: brandNames[i]?.toLowerCase() || `brand-${i + 1}`,
165
+ description: `Premium products from ${brandNames[i] || `Brand ${i + 1}`}`,
166
+ logo: `https://via.placeholder.com/200x100?text=${encodeURIComponent(brandNames[i] || `Brand ${i + 1}`)}`,
167
+ productCount: Math.floor(Math.random() * 30) + 5
168
+ });
169
+ }
170
+
171
+ return brands;
172
+ }
173
+
174
+ /**
175
+ * Generate mock widgets for different sections
176
+ * @returns {Array} Mock widgets
177
+ */
178
+ function generateMockWidgets() {
179
+ const widgets = [
180
+ {
181
+ id: 'widget-hero-1',
182
+ type: 'Banner',
183
+ section: 'hero',
184
+ sectionName: 'hero',
185
+ status: 'active',
186
+ position: 1,
187
+ title: 'Welcome to Our Store',
188
+ subtitle: 'Discover amazing products',
189
+ settings: {
190
+ title: 'Welcome to Our Store',
191
+ subtitle: 'Discover amazing products',
192
+ showTitle: true,
193
+ backgroundColor: '#ffffff'
194
+ },
195
+ data: {
196
+ content: {
197
+ image: 'https://via.placeholder.com/1200x600?text=Hero+Banner',
198
+ linkText: 'Shop Now',
199
+ targetUrl: '/collections/all'
200
+ }
201
+ },
202
+ content: JSON.stringify({
203
+ image: 'https://via.placeholder.com/1200x600?text=Hero+Banner',
204
+ linkText: 'Shop Now',
205
+ targetUrl: '/collections/all'
206
+ })
207
+ },
208
+ {
209
+ id: 'widget-products-1',
210
+ type: 'ProductGrid',
211
+ section: 'products',
212
+ sectionName: 'products',
213
+ status: 'active',
214
+ position: 1,
215
+ title: 'Featured Products',
216
+ settings: {
217
+ title: 'Featured Products',
218
+ columns: 4,
219
+ showTitle: true
220
+ },
221
+ data: {
222
+ content: {
223
+ productIds: ['product-1', 'product-2', 'product-3', 'product-4']
224
+ }
225
+ }
226
+ },
227
+ {
228
+ id: 'widget-footer-1',
229
+ type: 'Footer',
230
+ section: 'footer',
231
+ sectionName: 'footer',
232
+ status: 'active',
233
+ position: 1,
234
+ settings: {},
235
+ data: {
236
+ content: {
237
+ links: [
238
+ { text: 'About', url: '/pages/about' },
239
+ { text: 'Contact', url: '/pages/contact' },
240
+ { text: 'Privacy', url: '/pages/privacy' }
241
+ ]
242
+ }
243
+ }
244
+ }
245
+ ];
246
+
247
+ // Add more widgets for different sections
248
+ widgets.push(
249
+ {
250
+ id: 'widget-hero-2',
251
+ type: 'ProductCarousel',
252
+ section: 'hero',
253
+ sectionName: 'hero',
254
+ status: 'active',
255
+ position: 2,
256
+ settings: { title: 'Featured Products' },
257
+ data: { content: {} }
258
+ },
259
+ {
260
+ id: 'widget-content-1',
261
+ type: 'ProductGrid',
262
+ section: 'content',
263
+ sectionName: 'content',
264
+ status: 'active',
265
+ position: 1,
266
+ settings: { title: 'New Arrivals', columns: 4 },
267
+ data: { content: {} }
268
+ },
269
+ {
270
+ id: 'widget-sidebar-1',
271
+ type: 'CategoryList',
272
+ section: 'sidebar',
273
+ sectionName: 'sidebar',
274
+ status: 'active',
275
+ position: 1,
276
+ settings: { title: 'Categories' },
277
+ data: { content: {} }
278
+ }
279
+ );
280
+
281
+ return widgets;
282
+ }
283
+
284
+ module.exports = {
285
+ generateMockData,
286
+ generateMockProducts,
287
+ generateMockCategories,
288
+ generateMockBrands,
289
+ generateMockWidgets
290
+ };