@xbg.solutions/create-backend 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.
Files changed (32) hide show
  1. package/bin/create-backend.js +3 -0
  2. package/lib/cli.d.ts +12 -0
  3. package/lib/cli.js +55 -0
  4. package/lib/cli.js.map +1 -0
  5. package/lib/commands/add-util.d.ts +9 -0
  6. package/lib/commands/add-util.js +119 -0
  7. package/lib/commands/add-util.js.map +1 -0
  8. package/lib/commands/init.d.ts +11 -0
  9. package/lib/commands/init.js +372 -0
  10. package/lib/commands/init.js.map +1 -0
  11. package/lib/commands/sync.d.ts +10 -0
  12. package/lib/commands/sync.js +161 -0
  13. package/lib/commands/sync.js.map +1 -0
  14. package/lib/utils-registry.d.ts +25 -0
  15. package/lib/utils-registry.js +187 -0
  16. package/lib/utils-registry.js.map +1 -0
  17. package/package.json +38 -0
  18. package/src/project-template/__examples__/README.md +559 -0
  19. package/src/project-template/__examples__/blog-platform.model.ts +528 -0
  20. package/src/project-template/__examples__/communications-usage.ts +175 -0
  21. package/src/project-template/__examples__/ecommerce-store.model.ts +1200 -0
  22. package/src/project-template/__examples__/saas-multi-tenant.model.ts +798 -0
  23. package/src/project-template/__examples__/user.model.ts +221 -0
  24. package/src/project-template/__scripts__/deploy.js +115 -0
  25. package/src/project-template/__scripts__/generate.js +122 -0
  26. package/src/project-template/__scripts__/setup.js +425 -0
  27. package/src/project-template/__scripts__/validate.js +325 -0
  28. package/src/project-template/firebase.json +32 -0
  29. package/src/project-template/firestore.rules +12 -0
  30. package/src/project-template/functions/jest.config.js +49 -0
  31. package/src/project-template/functions/src/index.ts +46 -0
  32. package/src/project-template/functions/tsconfig.json +38 -0
@@ -0,0 +1,1200 @@
1
+ /**
2
+ * E-commerce Store Data Model
3
+ *
4
+ * A complete e-commerce platform with:
5
+ * - Product catalog with variants
6
+ * - Shopping cart and orders
7
+ * - Inventory management
8
+ * - Customer reviews and ratings
9
+ * - Payment processing
10
+ * - Shipping and tracking
11
+ *
12
+ * Features demonstrated:
13
+ * - Complex product variants (size, color, etc.)
14
+ * - Order state machine
15
+ * - Inventory tracking
16
+ * - Financial calculations
17
+ * - Multi-step workflows
18
+ * - Audit trails
19
+ *
20
+ * To use this model:
21
+ * 1. Copy to your project
22
+ * 2. Run: npm run generate __examples__/ecommerce-store.model.ts
23
+ * 3. Integrate payment provider (Stripe, etc.)
24
+ * 4. Set up shipping provider
25
+ * 5. Configure tax calculations
26
+ */
27
+
28
+ import { DataModelSpecification } from '../functions/src/generator/types';
29
+
30
+ export const EcommerceStoreModel: DataModelSpecification = {
31
+ entities: {
32
+ Customer: {
33
+ fields: {
34
+ email: {
35
+ type: 'email',
36
+ unique: true,
37
+ required: true,
38
+ },
39
+ firstName: {
40
+ type: 'string',
41
+ required: true,
42
+ },
43
+ lastName: {
44
+ type: 'string',
45
+ required: true,
46
+ },
47
+ phone: {
48
+ type: 'phone',
49
+ required: false,
50
+ },
51
+ stripeCustomerId: {
52
+ type: 'string',
53
+ unique: true,
54
+ required: false,
55
+ },
56
+ defaultShippingAddressId: {
57
+ type: 'reference',
58
+ entity: 'Address',
59
+ required: false,
60
+ },
61
+ defaultBillingAddressId: {
62
+ type: 'reference',
63
+ entity: 'Address',
64
+ required: false,
65
+ },
66
+ totalSpent: {
67
+ type: 'number',
68
+ default: 0,
69
+ },
70
+ orderCount: {
71
+ type: 'number',
72
+ default: 0,
73
+ },
74
+ loyaltyPoints: {
75
+ type: 'number',
76
+ default: 0,
77
+ },
78
+ marketingOptIn: {
79
+ type: 'boolean',
80
+ default: false,
81
+ },
82
+ createdAt: {
83
+ type: 'timestamp',
84
+ required: true,
85
+ },
86
+ updatedAt: {
87
+ type: 'timestamp',
88
+ required: true,
89
+ },
90
+ },
91
+ relationships: {
92
+ addresses: {
93
+ type: 'one-to-many',
94
+ entity: 'Address',
95
+ foreignKey: 'customerId',
96
+ },
97
+ orders: {
98
+ type: 'one-to-many',
99
+ entity: 'Order',
100
+ foreignKey: 'customerId',
101
+ },
102
+ cart: {
103
+ type: 'one-to-one',
104
+ entity: 'Cart',
105
+ foreignKey: 'customerId',
106
+ },
107
+ reviews: {
108
+ type: 'one-to-many',
109
+ entity: 'Review',
110
+ foreignKey: 'customerId',
111
+ },
112
+ },
113
+ access: {
114
+ create: ['public'],
115
+ read: ['self', 'admin'],
116
+ update: ['self', 'admin'],
117
+ delete: ['self', 'admin'],
118
+ },
119
+ validation: {
120
+ email: 'Must be a valid email',
121
+ phone: 'Must be a valid phone number',
122
+ },
123
+ businessRules: [
124
+ 'Email must be verified before placing orders',
125
+ 'Customer data must be encrypted at rest',
126
+ 'Deleted customers have their PII anonymized',
127
+ ],
128
+ indexes: [
129
+ { fields: ['email'], unique: true },
130
+ { fields: ['stripeCustomerId'], unique: true },
131
+ { fields: ['totalSpent'] },
132
+ ],
133
+ },
134
+
135
+ Address: {
136
+ fields: {
137
+ customerId: {
138
+ type: 'reference',
139
+ entity: 'Customer',
140
+ required: true,
141
+ },
142
+ addressLine1: {
143
+ type: 'string',
144
+ required: true,
145
+ },
146
+ addressLine2: {
147
+ type: 'string',
148
+ required: false,
149
+ },
150
+ city: {
151
+ type: 'string',
152
+ required: true,
153
+ },
154
+ state: {
155
+ type: 'string',
156
+ required: true,
157
+ },
158
+ postalCode: {
159
+ type: 'string',
160
+ required: true,
161
+ },
162
+ country: {
163
+ type: 'string',
164
+ required: true,
165
+ },
166
+ isDefault: {
167
+ type: 'boolean',
168
+ default: false,
169
+ },
170
+ isValidated: {
171
+ type: 'boolean',
172
+ default: false,
173
+ },
174
+ },
175
+ relationships: {
176
+ customer: {
177
+ type: 'many-to-one',
178
+ entity: 'Customer',
179
+ },
180
+ },
181
+ access: {
182
+ create: ['authenticated'],
183
+ read: ['self', 'admin'],
184
+ update: ['self', 'admin'],
185
+ delete: ['self', 'admin'],
186
+ },
187
+ validation: {
188
+ postalCode: 'Must be valid for country',
189
+ },
190
+ businessRules: [
191
+ 'Addresses are validated using Google Maps API',
192
+ 'Only one default address per customer',
193
+ ],
194
+ indexes: [
195
+ { fields: ['customerId'] },
196
+ ],
197
+ },
198
+
199
+ Product: {
200
+ fields: {
201
+ name: {
202
+ type: 'string',
203
+ required: true,
204
+ },
205
+ slug: {
206
+ type: 'string',
207
+ unique: true,
208
+ required: true,
209
+ },
210
+ description: {
211
+ type: 'text',
212
+ required: true,
213
+ },
214
+ shortDescription: {
215
+ type: 'string',
216
+ required: false,
217
+ },
218
+ basePrice: {
219
+ type: 'number',
220
+ required: true,
221
+ },
222
+ compareAtPrice: {
223
+ type: 'number',
224
+ required: false,
225
+ },
226
+ costPerItem: {
227
+ type: 'number',
228
+ required: false,
229
+ },
230
+ sku: {
231
+ type: 'string',
232
+ unique: true,
233
+ required: false,
234
+ },
235
+ barcode: {
236
+ type: 'string',
237
+ required: false,
238
+ },
239
+ status: {
240
+ type: 'enum',
241
+ values: ['draft', 'active', 'archived'],
242
+ default: 'draft',
243
+ },
244
+ categoryId: {
245
+ type: 'reference',
246
+ entity: 'Category',
247
+ required: true,
248
+ },
249
+ brandId: {
250
+ type: 'reference',
251
+ entity: 'Brand',
252
+ required: false,
253
+ },
254
+ taxable: {
255
+ type: 'boolean',
256
+ default: true,
257
+ },
258
+ weight: {
259
+ type: 'number',
260
+ required: false,
261
+ },
262
+ weightUnit: {
263
+ type: 'enum',
264
+ values: ['g', 'kg', 'oz', 'lb'],
265
+ default: 'kg',
266
+ },
267
+ requiresShipping: {
268
+ type: 'boolean',
269
+ default: true,
270
+ },
271
+ hasVariants: {
272
+ type: 'boolean',
273
+ default: false,
274
+ },
275
+ totalInventory: {
276
+ type: 'number',
277
+ default: 0,
278
+ },
279
+ viewCount: {
280
+ type: 'number',
281
+ default: 0,
282
+ },
283
+ salesCount: {
284
+ type: 'number',
285
+ default: 0,
286
+ },
287
+ averageRating: {
288
+ type: 'number',
289
+ default: 0,
290
+ },
291
+ reviewCount: {
292
+ type: 'number',
293
+ default: 0,
294
+ },
295
+ isFeatured: {
296
+ type: 'boolean',
297
+ default: false,
298
+ },
299
+ createdAt: {
300
+ type: 'timestamp',
301
+ required: true,
302
+ },
303
+ updatedAt: {
304
+ type: 'timestamp',
305
+ required: true,
306
+ },
307
+ },
308
+ relationships: {
309
+ category: {
310
+ type: 'many-to-one',
311
+ entity: 'Category',
312
+ },
313
+ brand: {
314
+ type: 'many-to-one',
315
+ entity: 'Brand',
316
+ optional: true,
317
+ },
318
+ variants: {
319
+ type: 'one-to-many',
320
+ entity: 'ProductVariant',
321
+ foreignKey: 'productId',
322
+ },
323
+ images: {
324
+ type: 'one-to-many',
325
+ entity: 'ProductImage',
326
+ foreignKey: 'productId',
327
+ },
328
+ reviews: {
329
+ type: 'one-to-many',
330
+ entity: 'Review',
331
+ foreignKey: 'productId',
332
+ },
333
+ collections: {
334
+ type: 'many-to-many',
335
+ entity: 'Collection',
336
+ through: 'ProductCollection',
337
+ },
338
+ },
339
+ access: {
340
+ create: ['admin'],
341
+ read: ['public'],
342
+ update: ['admin'],
343
+ delete: ['admin'],
344
+ },
345
+ validation: {
346
+ name: 'Must be 3-200 characters',
347
+ basePrice: 'Must be greater than 0',
348
+ weight: 'Must be positive number',
349
+ },
350
+ businessRules: [
351
+ 'Products with variants cannot have direct inventory',
352
+ 'Draft products are not visible to customers',
353
+ 'Products cannot be deleted if they have order history',
354
+ 'Price changes are logged for audit',
355
+ ],
356
+ indexes: [
357
+ { fields: ['slug'], unique: true },
358
+ { fields: ['sku'], unique: true },
359
+ { fields: ['categoryId', 'status'] },
360
+ { fields: ['status', 'isFeatured'] },
361
+ ],
362
+ },
363
+
364
+ ProductVariant: {
365
+ fields: {
366
+ productId: {
367
+ type: 'reference',
368
+ entity: 'Product',
369
+ required: true,
370
+ },
371
+ sku: {
372
+ type: 'string',
373
+ unique: true,
374
+ required: true,
375
+ },
376
+ barcode: {
377
+ type: 'string',
378
+ required: false,
379
+ },
380
+ price: {
381
+ type: 'number',
382
+ required: true,
383
+ },
384
+ compareAtPrice: {
385
+ type: 'number',
386
+ required: false,
387
+ },
388
+ costPerItem: {
389
+ type: 'number',
390
+ required: false,
391
+ },
392
+ option1: {
393
+ type: 'string',
394
+ required: false,
395
+ },
396
+ option2: {
397
+ type: 'string',
398
+ required: false,
399
+ },
400
+ option3: {
401
+ type: 'string',
402
+ required: false,
403
+ },
404
+ inventoryQuantity: {
405
+ type: 'number',
406
+ default: 0,
407
+ },
408
+ inventoryPolicy: {
409
+ type: 'enum',
410
+ values: ['deny', 'continue'],
411
+ default: 'deny',
412
+ },
413
+ weight: {
414
+ type: 'number',
415
+ required: false,
416
+ },
417
+ imageId: {
418
+ type: 'reference',
419
+ entity: 'ProductImage',
420
+ required: false,
421
+ },
422
+ isAvailable: {
423
+ type: 'boolean',
424
+ default: true,
425
+ },
426
+ position: {
427
+ type: 'number',
428
+ default: 0,
429
+ },
430
+ },
431
+ relationships: {
432
+ product: {
433
+ type: 'many-to-one',
434
+ entity: 'Product',
435
+ },
436
+ image: {
437
+ type: 'many-to-one',
438
+ entity: 'ProductImage',
439
+ optional: true,
440
+ },
441
+ },
442
+ access: {
443
+ create: ['admin'],
444
+ read: ['public'],
445
+ update: ['admin'],
446
+ delete: ['admin'],
447
+ },
448
+ validation: {
449
+ price: 'Must be greater than 0',
450
+ inventoryQuantity: 'Cannot be negative',
451
+ },
452
+ businessRules: [
453
+ 'SKU must be unique across all variants',
454
+ 'Inventory changes are tracked for audit',
455
+ 'Low stock alerts are sent when inventory < threshold',
456
+ ],
457
+ indexes: [
458
+ { fields: ['productId'] },
459
+ { fields: ['sku'], unique: true },
460
+ ],
461
+ },
462
+
463
+ Category: {
464
+ fields: {
465
+ name: {
466
+ type: 'string',
467
+ unique: true,
468
+ required: true,
469
+ },
470
+ slug: {
471
+ type: 'string',
472
+ unique: true,
473
+ required: true,
474
+ },
475
+ description: {
476
+ type: 'text',
477
+ required: false,
478
+ },
479
+ parentId: {
480
+ type: 'reference',
481
+ entity: 'Category',
482
+ required: false,
483
+ },
484
+ imageUrl: {
485
+ type: 'url',
486
+ required: false,
487
+ },
488
+ isActive: {
489
+ type: 'boolean',
490
+ default: true,
491
+ },
492
+ productCount: {
493
+ type: 'number',
494
+ default: 0,
495
+ },
496
+ position: {
497
+ type: 'number',
498
+ default: 0,
499
+ },
500
+ },
501
+ relationships: {
502
+ parent: {
503
+ type: 'many-to-one',
504
+ entity: 'Category',
505
+ optional: true,
506
+ },
507
+ children: {
508
+ type: 'one-to-many',
509
+ entity: 'Category',
510
+ foreignKey: 'parentId',
511
+ },
512
+ products: {
513
+ type: 'one-to-many',
514
+ entity: 'Product',
515
+ foreignKey: 'categoryId',
516
+ },
517
+ },
518
+ access: {
519
+ create: ['admin'],
520
+ read: ['public'],
521
+ update: ['admin'],
522
+ delete: ['admin'],
523
+ },
524
+ indexes: [
525
+ { fields: ['slug'], unique: true },
526
+ { fields: ['parentId'] },
527
+ ],
528
+ },
529
+
530
+ Brand: {
531
+ fields: {
532
+ name: {
533
+ type: 'string',
534
+ unique: true,
535
+ required: true,
536
+ },
537
+ slug: {
538
+ type: 'string',
539
+ unique: true,
540
+ required: true,
541
+ },
542
+ description: {
543
+ type: 'text',
544
+ required: false,
545
+ },
546
+ logoUrl: {
547
+ type: 'url',
548
+ required: false,
549
+ },
550
+ websiteUrl: {
551
+ type: 'url',
552
+ required: false,
553
+ },
554
+ isActive: {
555
+ type: 'boolean',
556
+ default: true,
557
+ },
558
+ },
559
+ relationships: {
560
+ products: {
561
+ type: 'one-to-many',
562
+ entity: 'Product',
563
+ foreignKey: 'brandId',
564
+ },
565
+ },
566
+ access: {
567
+ create: ['admin'],
568
+ read: ['public'],
569
+ update: ['admin'],
570
+ delete: ['admin'],
571
+ },
572
+ indexes: [
573
+ { fields: ['slug'], unique: true },
574
+ ],
575
+ },
576
+
577
+ Cart: {
578
+ fields: {
579
+ customerId: {
580
+ type: 'reference',
581
+ entity: 'Customer',
582
+ required: false, // Null for guest carts
583
+ },
584
+ sessionId: {
585
+ type: 'string',
586
+ required: false, // For guest sessions
587
+ },
588
+ subtotal: {
589
+ type: 'number',
590
+ default: 0,
591
+ },
592
+ tax: {
593
+ type: 'number',
594
+ default: 0,
595
+ },
596
+ shipping: {
597
+ type: 'number',
598
+ default: 0,
599
+ },
600
+ total: {
601
+ type: 'number',
602
+ default: 0,
603
+ },
604
+ itemCount: {
605
+ type: 'number',
606
+ default: 0,
607
+ },
608
+ currency: {
609
+ type: 'string',
610
+ default: 'USD',
611
+ },
612
+ expiresAt: {
613
+ type: 'timestamp',
614
+ required: false,
615
+ },
616
+ createdAt: {
617
+ type: 'timestamp',
618
+ required: true,
619
+ },
620
+ updatedAt: {
621
+ type: 'timestamp',
622
+ required: true,
623
+ },
624
+ },
625
+ relationships: {
626
+ customer: {
627
+ type: 'many-to-one',
628
+ entity: 'Customer',
629
+ optional: true,
630
+ },
631
+ items: {
632
+ type: 'one-to-many',
633
+ entity: 'CartItem',
634
+ foreignKey: 'cartId',
635
+ cascadeDelete: true,
636
+ },
637
+ },
638
+ access: {
639
+ create: ['public'],
640
+ read: ['self', 'admin'],
641
+ update: ['self', 'admin'],
642
+ delete: ['self', 'admin'],
643
+ },
644
+ businessRules: [
645
+ 'Guest carts expire after 7 days',
646
+ 'Customer carts never expire',
647
+ 'Cart totals are recalculated on every update',
648
+ 'Out-of-stock items are automatically removed',
649
+ ],
650
+ indexes: [
651
+ { fields: ['customerId'], unique: true },
652
+ { fields: ['sessionId'] },
653
+ { fields: ['expiresAt'] },
654
+ ],
655
+ },
656
+
657
+ CartItem: {
658
+ fields: {
659
+ cartId: {
660
+ type: 'reference',
661
+ entity: 'Cart',
662
+ required: true,
663
+ },
664
+ productId: {
665
+ type: 'reference',
666
+ entity: 'Product',
667
+ required: true,
668
+ },
669
+ variantId: {
670
+ type: 'reference',
671
+ entity: 'ProductVariant',
672
+ required: false,
673
+ },
674
+ quantity: {
675
+ type: 'number',
676
+ required: true,
677
+ },
678
+ price: {
679
+ type: 'number',
680
+ required: true,
681
+ },
682
+ subtotal: {
683
+ type: 'number',
684
+ required: true,
685
+ },
686
+ },
687
+ relationships: {
688
+ cart: {
689
+ type: 'many-to-one',
690
+ entity: 'Cart',
691
+ },
692
+ product: {
693
+ type: 'many-to-one',
694
+ entity: 'Product',
695
+ },
696
+ variant: {
697
+ type: 'many-to-one',
698
+ entity: 'ProductVariant',
699
+ optional: true,
700
+ },
701
+ },
702
+ access: {
703
+ create: ['self', 'admin'],
704
+ read: ['self', 'admin'],
705
+ update: ['self', 'admin'],
706
+ delete: ['self', 'admin'],
707
+ },
708
+ validation: {
709
+ quantity: 'Must be greater than 0',
710
+ },
711
+ indexes: [
712
+ { fields: ['cartId'] },
713
+ { fields: ['productId'] },
714
+ ],
715
+ },
716
+
717
+ Order: {
718
+ fields: {
719
+ orderNumber: {
720
+ type: 'string',
721
+ unique: true,
722
+ required: true,
723
+ },
724
+ customerId: {
725
+ type: 'reference',
726
+ entity: 'Customer',
727
+ required: true,
728
+ },
729
+ status: {
730
+ type: 'enum',
731
+ values: ['pending', 'confirmed', 'processing', 'shipped', 'delivered', 'cancelled', 'refunded'],
732
+ default: 'pending',
733
+ },
734
+ paymentStatus: {
735
+ type: 'enum',
736
+ values: ['pending', 'authorized', 'paid', 'partially_refunded', 'refunded', 'failed'],
737
+ default: 'pending',
738
+ },
739
+ fulfillmentStatus: {
740
+ type: 'enum',
741
+ values: ['unfulfilled', 'partially_fulfilled', 'fulfilled'],
742
+ default: 'unfulfilled',
743
+ },
744
+ shippingAddressId: {
745
+ type: 'reference',
746
+ entity: 'Address',
747
+ required: true,
748
+ },
749
+ billingAddressId: {
750
+ type: 'reference',
751
+ entity: 'Address',
752
+ required: true,
753
+ },
754
+ subtotal: {
755
+ type: 'number',
756
+ required: true,
757
+ },
758
+ tax: {
759
+ type: 'number',
760
+ required: true,
761
+ },
762
+ shipping: {
763
+ type: 'number',
764
+ required: true,
765
+ },
766
+ discount: {
767
+ type: 'number',
768
+ default: 0,
769
+ },
770
+ total: {
771
+ type: 'number',
772
+ required: true,
773
+ },
774
+ currency: {
775
+ type: 'string',
776
+ default: 'USD',
777
+ },
778
+ stripePaymentIntentId: {
779
+ type: 'string',
780
+ required: false,
781
+ },
782
+ notes: {
783
+ type: 'text',
784
+ required: false,
785
+ },
786
+ customerNotes: {
787
+ type: 'text',
788
+ required: false,
789
+ },
790
+ confirmedAt: {
791
+ type: 'timestamp',
792
+ required: false,
793
+ },
794
+ shippedAt: {
795
+ type: 'timestamp',
796
+ required: false,
797
+ },
798
+ deliveredAt: {
799
+ type: 'timestamp',
800
+ required: false,
801
+ },
802
+ cancelledAt: {
803
+ type: 'timestamp',
804
+ required: false,
805
+ },
806
+ createdAt: {
807
+ type: 'timestamp',
808
+ required: true,
809
+ },
810
+ updatedAt: {
811
+ type: 'timestamp',
812
+ required: true,
813
+ },
814
+ },
815
+ relationships: {
816
+ customer: {
817
+ type: 'many-to-one',
818
+ entity: 'Customer',
819
+ },
820
+ shippingAddress: {
821
+ type: 'many-to-one',
822
+ entity: 'Address',
823
+ },
824
+ billingAddress: {
825
+ type: 'many-to-one',
826
+ entity: 'Address',
827
+ },
828
+ items: {
829
+ type: 'one-to-many',
830
+ entity: 'OrderItem',
831
+ foreignKey: 'orderId',
832
+ },
833
+ shipments: {
834
+ type: 'one-to-many',
835
+ entity: 'Shipment',
836
+ foreignKey: 'orderId',
837
+ },
838
+ },
839
+ access: {
840
+ create: ['authenticated'],
841
+ read: ['self', 'admin'],
842
+ update: ['admin'],
843
+ delete: ['admin'],
844
+ },
845
+ validation: {
846
+ total: 'Must be greater than 0',
847
+ },
848
+ businessRules: [
849
+ 'Order numbers are auto-generated and sequential',
850
+ 'Orders cannot be deleted, only cancelled',
851
+ 'Cancelled orders release inventory',
852
+ 'Order status changes are logged for audit',
853
+ 'Customers are notified of status changes',
854
+ ],
855
+ indexes: [
856
+ { fields: ['orderNumber'], unique: true },
857
+ { fields: ['customerId', 'createdAt'] },
858
+ { fields: ['status', 'createdAt'] },
859
+ { fields: ['paymentStatus'] },
860
+ ],
861
+ },
862
+
863
+ OrderItem: {
864
+ fields: {
865
+ orderId: {
866
+ type: 'reference',
867
+ entity: 'Order',
868
+ required: true,
869
+ },
870
+ productId: {
871
+ type: 'reference',
872
+ entity: 'Product',
873
+ required: true,
874
+ },
875
+ variantId: {
876
+ type: 'reference',
877
+ entity: 'ProductVariant',
878
+ required: false,
879
+ },
880
+ productName: {
881
+ type: 'string',
882
+ required: true,
883
+ },
884
+ variantTitle: {
885
+ type: 'string',
886
+ required: false,
887
+ },
888
+ sku: {
889
+ type: 'string',
890
+ required: false,
891
+ },
892
+ quantity: {
893
+ type: 'number',
894
+ required: true,
895
+ },
896
+ price: {
897
+ type: 'number',
898
+ required: true,
899
+ },
900
+ subtotal: {
901
+ type: 'number',
902
+ required: true,
903
+ },
904
+ tax: {
905
+ type: 'number',
906
+ default: 0,
907
+ },
908
+ total: {
909
+ type: 'number',
910
+ required: true,
911
+ },
912
+ },
913
+ relationships: {
914
+ order: {
915
+ type: 'many-to-one',
916
+ entity: 'Order',
917
+ },
918
+ product: {
919
+ type: 'many-to-one',
920
+ entity: 'Product',
921
+ },
922
+ variant: {
923
+ type: 'many-to-one',
924
+ entity: 'ProductVariant',
925
+ optional: true,
926
+ },
927
+ },
928
+ access: {
929
+ create: ['admin'],
930
+ read: ['self', 'admin'],
931
+ update: ['admin'],
932
+ delete: ['admin'],
933
+ },
934
+ businessRules: [
935
+ 'Product details are snapshotted at order time',
936
+ 'Price changes do not affect existing orders',
937
+ ],
938
+ indexes: [
939
+ { fields: ['orderId'] },
940
+ ],
941
+ },
942
+
943
+ Shipment: {
944
+ fields: {
945
+ orderId: {
946
+ type: 'reference',
947
+ entity: 'Order',
948
+ required: true,
949
+ },
950
+ trackingNumber: {
951
+ type: 'string',
952
+ required: false,
953
+ },
954
+ carrier: {
955
+ type: 'string',
956
+ required: false,
957
+ },
958
+ trackingUrl: {
959
+ type: 'url',
960
+ required: false,
961
+ },
962
+ status: {
963
+ type: 'enum',
964
+ values: ['pending', 'in_transit', 'out_for_delivery', 'delivered', 'failed'],
965
+ default: 'pending',
966
+ },
967
+ shippedAt: {
968
+ type: 'timestamp',
969
+ required: false,
970
+ },
971
+ estimatedDeliveryAt: {
972
+ type: 'timestamp',
973
+ required: false,
974
+ },
975
+ deliveredAt: {
976
+ type: 'timestamp',
977
+ required: false,
978
+ },
979
+ createdAt: {
980
+ type: 'timestamp',
981
+ required: true,
982
+ },
983
+ updatedAt: {
984
+ type: 'timestamp',
985
+ required: true,
986
+ },
987
+ },
988
+ relationships: {
989
+ order: {
990
+ type: 'many-to-one',
991
+ entity: 'Order',
992
+ },
993
+ },
994
+ access: {
995
+ create: ['admin'],
996
+ read: ['self', 'admin'],
997
+ update: ['admin'],
998
+ delete: ['admin'],
999
+ },
1000
+ businessRules: [
1001
+ 'Customers are notified when shipment is created',
1002
+ 'Tracking updates are fetched from carrier API',
1003
+ 'Delivery confirmation updates order status',
1004
+ ],
1005
+ indexes: [
1006
+ { fields: ['orderId'] },
1007
+ { fields: ['trackingNumber'] },
1008
+ ],
1009
+ },
1010
+
1011
+ Review: {
1012
+ fields: {
1013
+ productId: {
1014
+ type: 'reference',
1015
+ entity: 'Product',
1016
+ required: true,
1017
+ },
1018
+ customerId: {
1019
+ type: 'reference',
1020
+ entity: 'Customer',
1021
+ required: true,
1022
+ },
1023
+ rating: {
1024
+ type: 'number',
1025
+ required: true,
1026
+ },
1027
+ title: {
1028
+ type: 'string',
1029
+ required: false,
1030
+ },
1031
+ content: {
1032
+ type: 'text',
1033
+ required: true,
1034
+ },
1035
+ status: {
1036
+ type: 'enum',
1037
+ values: ['pending', 'approved', 'rejected'],
1038
+ default: 'pending',
1039
+ },
1040
+ isVerifiedPurchase: {
1041
+ type: 'boolean',
1042
+ default: false,
1043
+ },
1044
+ helpfulCount: {
1045
+ type: 'number',
1046
+ default: 0,
1047
+ },
1048
+ createdAt: {
1049
+ type: 'timestamp',
1050
+ required: true,
1051
+ },
1052
+ updatedAt: {
1053
+ type: 'timestamp',
1054
+ required: true,
1055
+ },
1056
+ },
1057
+ relationships: {
1058
+ product: {
1059
+ type: 'many-to-one',
1060
+ entity: 'Product',
1061
+ },
1062
+ customer: {
1063
+ type: 'many-to-one',
1064
+ entity: 'Customer',
1065
+ },
1066
+ },
1067
+ access: {
1068
+ create: ['authenticated'],
1069
+ read: ['public'],
1070
+ update: ['self', 'admin'],
1071
+ delete: ['self', 'admin'],
1072
+ },
1073
+ validation: {
1074
+ rating: 'Must be between 1 and 5',
1075
+ content: 'Must be 10-2000 characters',
1076
+ },
1077
+ businessRules: [
1078
+ 'Customers can only review products they purchased',
1079
+ 'One review per customer per product',
1080
+ 'Reviews affect product average rating',
1081
+ ],
1082
+ indexes: [
1083
+ { fields: ['productId', 'status'] },
1084
+ { fields: ['customerId'] },
1085
+ { fields: ['productId', 'customerId'], unique: true },
1086
+ ],
1087
+ },
1088
+
1089
+ Collection: {
1090
+ fields: {
1091
+ name: {
1092
+ type: 'string',
1093
+ unique: true,
1094
+ required: true,
1095
+ },
1096
+ slug: {
1097
+ type: 'string',
1098
+ unique: true,
1099
+ required: true,
1100
+ },
1101
+ description: {
1102
+ type: 'text',
1103
+ required: false,
1104
+ },
1105
+ imageUrl: {
1106
+ type: 'url',
1107
+ required: false,
1108
+ },
1109
+ isActive: {
1110
+ type: 'boolean',
1111
+ default: true,
1112
+ },
1113
+ isFeatured: {
1114
+ type: 'boolean',
1115
+ default: false,
1116
+ },
1117
+ position: {
1118
+ type: 'number',
1119
+ default: 0,
1120
+ },
1121
+ },
1122
+ relationships: {
1123
+ products: {
1124
+ type: 'many-to-many',
1125
+ entity: 'Product',
1126
+ through: 'ProductCollection',
1127
+ },
1128
+ },
1129
+ access: {
1130
+ create: ['admin'],
1131
+ read: ['public'],
1132
+ update: ['admin'],
1133
+ delete: ['admin'],
1134
+ },
1135
+ indexes: [
1136
+ { fields: ['slug'], unique: true },
1137
+ ],
1138
+ },
1139
+
1140
+ ProductImage: {
1141
+ fields: {
1142
+ productId: {
1143
+ type: 'reference',
1144
+ entity: 'Product',
1145
+ required: true,
1146
+ },
1147
+ url: {
1148
+ type: 'url',
1149
+ required: true,
1150
+ },
1151
+ altText: {
1152
+ type: 'string',
1153
+ required: false,
1154
+ },
1155
+ position: {
1156
+ type: 'number',
1157
+ default: 0,
1158
+ },
1159
+ },
1160
+ relationships: {
1161
+ product: {
1162
+ type: 'many-to-one',
1163
+ entity: 'Product',
1164
+ },
1165
+ },
1166
+ access: {
1167
+ create: ['admin'],
1168
+ read: ['public'],
1169
+ update: ['admin'],
1170
+ delete: ['admin'],
1171
+ },
1172
+ indexes: [
1173
+ { fields: ['productId', 'position'] },
1174
+ ],
1175
+ },
1176
+
1177
+ ProductCollection: {
1178
+ fields: {
1179
+ productId: {
1180
+ type: 'reference',
1181
+ entity: 'Product',
1182
+ required: true,
1183
+ },
1184
+ collectionId: {
1185
+ type: 'reference',
1186
+ entity: 'Collection',
1187
+ required: true,
1188
+ },
1189
+ position: {
1190
+ type: 'number',
1191
+ default: 0,
1192
+ },
1193
+ },
1194
+ indexes: [
1195
+ { fields: ['productId', 'collectionId'], unique: true },
1196
+ { fields: ['collectionId', 'position'] },
1197
+ ],
1198
+ },
1199
+ },
1200
+ };