@infuro/cms-core 1.0.40 → 1.0.42

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.
@@ -5,8 +5,8 @@ import { queueEmail, queueVendorOnboardEmails, registerEmailQueueProcessor } fro
5
5
  import { getSmsTemplateDefault, SMS_MESSAGE_TEMPLATE_DEFAULTS } from './chunk-MQBT33IV.js';
6
6
  import { EVENT_ORDER_MESSAGE_TEMPLATE_DEFAULTS, EVENT_ORDER_TEMPLATE_KEY, EVENT_ORDER_TEMPLATE_VARIABLES } from './chunk-KFFB6EMZ.js';
7
7
  import { JOB_RUNNER_QUEUE } from './chunk-GO7PPYNU.js';
8
- import { canManageRoles, sessionHasEntityAccess } from './chunk-UXMDSNIG.js';
9
- import { permissionRowsToRecord, logRbac, isSuperAdmin, vendorPortalFlagsFromUser, VENDOR_SCOPED_STORE_ENTITIES, isVendorPortalUser, getPermissionableEntityKeys, VENDOR_STORE_RBAC_ENTITIES, isSuperAdminGroupName, VENDOR_RBAC_ENTITIES, VENDOR_VIEWER_ROLE_NAME, DEFAULT_VENDOR_ROLE_DEFS, VENDOR_OWNER_GROUP_NAME, resolveVendorScopeFromSessionUser, canManageVendorRoles, isPlatformAdministrator, canManageVendorTeam, resolveDisabledMultiVendorScope, logEntityAccessDecision, explainSessionEntityAccess } from './chunk-P7QCLE5W.js';
8
+ import { canManageRoles, sessionHasEntityAccess } from './chunk-BLR6H5GL.js';
9
+ import { permissionRowsToRecord, logRbac, isSuperAdmin, vendorPortalFlagsFromUser, VENDOR_SCOPED_STORE_ENTITIES, isVendorPortalUser, getPermissionableEntityKeys, VENDOR_STORE_RBAC_ENTITIES, isSuperAdminGroupName, VENDOR_RBAC_ENTITIES, VENDOR_VIEWER_ROLE_NAME, DEFAULT_VENDOR_ROLE_DEFS, VENDOR_OWNER_GROUP_NAME, resolveVendorScopeFromSessionUser, canManageVendorRoles, isPlatformAdministrator, canManageVendorTeam, resolveDisabledMultiVendorScope, logEntityAccessDecision, explainSessionEntityAccess } from './chunk-DN65KDIA.js';
10
10
  import { queueErp } from './chunk-SYBOCAWB.js';
11
11
  import { isErpIntegrationEnabled } from './chunk-JC6DLWTE.js';
12
12
  import { __name } from './chunk-SHUYVCID.js';
@@ -94,6 +94,79 @@ async function loadUserVendorContext(dataSource, userId, preferredVendorId) {
94
94
  }
95
95
  __name(loadUserVendorContext, "loadUserVendorContext");
96
96
 
97
+ // src/lib/vendor-catalog-create-flags.ts
98
+ var ALL_OFF = {
99
+ categories: false,
100
+ collections: false,
101
+ brands: false
102
+ };
103
+ var cachedFlags = null;
104
+ function invalidateVendorCatalogCreateFlagsCache() {
105
+ cachedFlags = null;
106
+ }
107
+ __name(invalidateVendorCatalogCreateFlagsCache, "invalidateVendorCatalogCreateFlagsCache");
108
+ function parseTrue(value) {
109
+ return value === "true";
110
+ }
111
+ __name(parseTrue, "parseTrue");
112
+ async function isMultiVendorEnabled(dataSource) {
113
+ try {
114
+ const rows = await dataSource.query(`
115
+ SELECT value FROM configs
116
+ WHERE settings = 'multi_vendor' AND key = 'enabled' AND deleted = false
117
+ LIMIT 1
118
+ `);
119
+ return rows.length === 0 || rows[0].value !== "false";
120
+ } catch {
121
+ return true;
122
+ }
123
+ }
124
+ __name(isMultiVendorEnabled, "isMultiVendorEnabled");
125
+ async function getVendorCatalogCreateFlags(dataSource) {
126
+ if (cachedFlags !== null) return cachedFlags;
127
+ const multiVendorEnabled = await isMultiVendorEnabled(dataSource);
128
+ if (!multiVendorEnabled) {
129
+ cachedFlags = {
130
+ ...ALL_OFF
131
+ };
132
+ return cachedFlags;
133
+ }
134
+ try {
135
+ const rows = await dataSource.query(`
136
+ SELECT key, value FROM configs
137
+ WHERE settings = 'multi_vendor'
138
+ AND key IN (
139
+ 'vendorCanCreateCategories',
140
+ 'vendorCanCreateCollections',
141
+ 'vendorCanCreateBrands'
142
+ )
143
+ AND deleted = false
144
+ `);
145
+ const map = new Map(rows.map((r) => [
146
+ r.key,
147
+ r.value
148
+ ]));
149
+ cachedFlags = {
150
+ categories: parseTrue(map.get("vendorCanCreateCategories")),
151
+ collections: parseTrue(map.get("vendorCanCreateCollections")),
152
+ brands: parseTrue(map.get("vendorCanCreateBrands"))
153
+ };
154
+ } catch {
155
+ cachedFlags = {
156
+ ...ALL_OFF
157
+ };
158
+ }
159
+ return cachedFlags;
160
+ }
161
+ __name(getVendorCatalogCreateFlags, "getVendorCatalogCreateFlags");
162
+ function vendorCanCreateResource(resource, flags) {
163
+ if (resource === "product_categories") return flags.categories;
164
+ if (resource === "collections") return flags.collections;
165
+ if (resource === "brands") return flags.brands;
166
+ return false;
167
+ }
168
+ __name(vendorCanCreateResource, "vendorCanCreateResource");
169
+
97
170
  // src/lib/hydrate-vendor-session-user.ts
98
171
  async function hydrateVendorSessionUser(dataSource, user) {
99
172
  if (!user?.email) return user;
@@ -196,6 +269,7 @@ __name(hydrateVendorSessionUser, "hydrateVendorSessionUser");
196
269
  var cachedMultiVendorEnabled = null;
197
270
  function invalidateMultiVendorCache() {
198
271
  cachedMultiVendorEnabled = null;
272
+ invalidateVendorCatalogCreateFlagsCache();
199
273
  }
200
274
  __name(invalidateMultiVendorCache, "invalidateMultiVendorCache");
201
275
  async function checkMultiVendorEnabled(dataSource) {
@@ -234,6 +308,279 @@ async function checkEventsEnabled(dataSource) {
234
308
  }
235
309
  __name(checkEventsEnabled, "checkEventsEnabled");
236
310
 
311
+ // src/lib/product-approval.ts
312
+ var PRODUCT_APPROVAL_STATUSES = [
313
+ "pending",
314
+ "approved",
315
+ "rejected"
316
+ ];
317
+ var APPROVAL_SET = new Set(PRODUCT_APPROVAL_STATUSES);
318
+ var PRODUCT_STATUSES = /* @__PURE__ */ new Set([
319
+ "draft",
320
+ "available",
321
+ "reserved",
322
+ "sold"
323
+ ]);
324
+ var cachedRequireApproval = null;
325
+ function invalidateRequireProductApprovalCache() {
326
+ cachedRequireApproval = null;
327
+ }
328
+ __name(invalidateRequireProductApprovalCache, "invalidateRequireProductApprovalCache");
329
+ function parseTrue2(value) {
330
+ return value === "true";
331
+ }
332
+ __name(parseTrue2, "parseTrue");
333
+ async function getRequireProductApproval(dataSource) {
334
+ if (cachedRequireApproval !== null) return cachedRequireApproval;
335
+ const multiVendorEnabled = await checkMultiVendorEnabled(dataSource);
336
+ if (!multiVendorEnabled) {
337
+ cachedRequireApproval = false;
338
+ return false;
339
+ }
340
+ try {
341
+ const rows = await dataSource.query(`
342
+ SELECT value FROM configs
343
+ WHERE settings = 'multi_vendor' AND key = 'requireProductApproval' AND deleted = false
344
+ LIMIT 1
345
+ `);
346
+ cachedRequireApproval = rows.length > 0 && parseTrue2(rows[0].value);
347
+ } catch {
348
+ cachedRequireApproval = false;
349
+ }
350
+ return cachedRequireApproval;
351
+ }
352
+ __name(getRequireProductApproval, "getRequireProductApproval");
353
+ function applyVendorProductCreateApproval(persistBody, opts) {
354
+ if (!opts.isVendor || !opts.requireApproval) return;
355
+ persistBody.approvalStatus = "pending";
356
+ if (persistBody.status === "available") persistBody.status = "draft";
357
+ persistBody.rejectionReason = null;
358
+ persistBody.rejectedAt = null;
359
+ persistBody.rejectedBy = null;
360
+ }
361
+ __name(applyVendorProductCreateApproval, "applyVendorProductCreateApproval");
362
+ function assertProductApprovalUpdate(opts) {
363
+ const fromApproval = opts.fromApproval == null || opts.fromApproval === "" ? null : String(opts.fromApproval);
364
+ const toApproval = opts.toApproval == null || opts.toApproval === "" ? null : String(opts.toApproval);
365
+ const fromStatus = String(opts.fromStatus || "draft");
366
+ const toStatus = String(opts.toStatus || fromStatus);
367
+ if (opts.statusChanged && !PRODUCT_STATUSES.has(toStatus)) {
368
+ return {
369
+ ok: false,
370
+ error: `Invalid product status: ${toStatus}`
371
+ };
372
+ }
373
+ if (opts.approvalChanged && toApproval != null && !APPROVAL_SET.has(toApproval)) {
374
+ return {
375
+ ok: false,
376
+ error: `Invalid approval status: ${toApproval}`
377
+ };
378
+ }
379
+ if (!opts.requireApproval) {
380
+ if (opts.isVendor && opts.approvalChanged) {
381
+ return {
382
+ ok: false,
383
+ error: "Vendors cannot change approval status"
384
+ };
385
+ }
386
+ return {
387
+ ok: true
388
+ };
389
+ }
390
+ if (opts.isVendor) {
391
+ if (opts.approvalChanged) {
392
+ return {
393
+ ok: false,
394
+ error: "Vendors cannot change approval status"
395
+ };
396
+ }
397
+ if (opts.statusChanged && toStatus === "available" && fromApproval !== "approved") {
398
+ return {
399
+ ok: false,
400
+ error: "Product must be approved before it can be available"
401
+ };
402
+ }
403
+ if (fromApproval === "rejected" && opts.statusChanged && toStatus === "available") {
404
+ return {
405
+ ok: false,
406
+ error: "Rejected product cannot be made available"
407
+ };
408
+ }
409
+ return {
410
+ ok: true
411
+ };
412
+ }
413
+ if (opts.approvalChanged && toApproval === "rejected") {
414
+ const reason = String(opts.rejectionReason ?? "").trim();
415
+ if (!reason) return {
416
+ ok: false,
417
+ error: "Rejection reason is required"
418
+ };
419
+ }
420
+ return {
421
+ ok: true
422
+ };
423
+ }
424
+ __name(assertProductApprovalUpdate, "assertProductApprovalUpdate");
425
+ function applyApprovalStatusSideEffects(updatePayload, opts) {
426
+ const to = opts.toApproval == null || opts.toApproval === "" ? null : String(opts.toApproval);
427
+ if (to == null) return;
428
+ if (to === "approved") {
429
+ updatePayload.approvalStatus = "approved";
430
+ updatePayload.status = "available";
431
+ updatePayload.rejectionReason = null;
432
+ updatePayload.rejectedAt = null;
433
+ updatePayload.rejectedBy = null;
434
+ return;
435
+ }
436
+ if (to === "rejected") {
437
+ updatePayload.approvalStatus = "rejected";
438
+ updatePayload.rejectionReason = String(opts.rejectionReason ?? "").trim();
439
+ updatePayload.rejectedAt = /* @__PURE__ */ new Date();
440
+ updatePayload.rejectedBy = opts.rejectedBy != null && Number.isFinite(opts.rejectedBy) ? opts.rejectedBy : null;
441
+ const nextStatus = "status" in updatePayload ? String(updatePayload.status) : opts.currentStatus;
442
+ if (nextStatus === "available") updatePayload.status = "draft";
443
+ return;
444
+ }
445
+ if (to === "pending") {
446
+ updatePayload.approvalStatus = "pending";
447
+ updatePayload.rejectionReason = null;
448
+ updatePayload.rejectedAt = null;
449
+ updatePayload.rejectedBy = null;
450
+ const nextStatus = "status" in updatePayload ? String(updatePayload.status) : opts.currentStatus;
451
+ if (nextStatus === "available") updatePayload.status = "draft";
452
+ }
453
+ }
454
+ __name(applyApprovalStatusSideEffects, "applyApprovalStatusSideEffects");
455
+
456
+ // src/lib/event-approval.ts
457
+ var EVENT_APPROVAL_STATUSES = [
458
+ "pending",
459
+ "approved",
460
+ "rejected"
461
+ ];
462
+ var APPROVAL_SET2 = new Set(EVENT_APPROVAL_STATUSES);
463
+ var cachedRequireApproval2 = null;
464
+ function invalidateRequireEventApprovalCache() {
465
+ cachedRequireApproval2 = null;
466
+ }
467
+ __name(invalidateRequireEventApprovalCache, "invalidateRequireEventApprovalCache");
468
+ function parseTrue3(value) {
469
+ return value === "true";
470
+ }
471
+ __name(parseTrue3, "parseTrue");
472
+ async function getRequireEventApproval(dataSource) {
473
+ if (cachedRequireApproval2 !== null) return cachedRequireApproval2;
474
+ const [eventsOn, multiVendorOn] = await Promise.all([
475
+ checkEventsEnabled(dataSource),
476
+ checkMultiVendorEnabled(dataSource)
477
+ ]);
478
+ if (!eventsOn || !multiVendorOn) {
479
+ cachedRequireApproval2 = false;
480
+ return false;
481
+ }
482
+ try {
483
+ const rows = await dataSource.query(`
484
+ SELECT value FROM configs
485
+ WHERE settings = 'events' AND key = 'requireEventApproval' AND deleted = false
486
+ LIMIT 1
487
+ `);
488
+ cachedRequireApproval2 = rows.length > 0 && parseTrue3(rows[0].value);
489
+ } catch {
490
+ cachedRequireApproval2 = false;
491
+ }
492
+ return cachedRequireApproval2;
493
+ }
494
+ __name(getRequireEventApproval, "getRequireEventApproval");
495
+ function applyVendorEventCreateApproval(persistBody, opts) {
496
+ if (!opts.isVendor || !opts.requireApproval) return;
497
+ persistBody.approvalStatus = "pending";
498
+ persistBody.isActive = false;
499
+ persistBody.rejectionReason = null;
500
+ persistBody.rejectedAt = null;
501
+ persistBody.rejectedBy = null;
502
+ }
503
+ __name(applyVendorEventCreateApproval, "applyVendorEventCreateApproval");
504
+ function assertEventApprovalUpdate(opts) {
505
+ const fromApproval = opts.fromApproval == null || opts.fromApproval === "" ? null : String(opts.fromApproval);
506
+ const toApproval = opts.toApproval == null || opts.toApproval === "" ? null : String(opts.toApproval);
507
+ if (opts.approvalChanged && toApproval != null && !APPROVAL_SET2.has(toApproval)) {
508
+ return {
509
+ ok: false,
510
+ error: `Invalid approval status: ${toApproval}`
511
+ };
512
+ }
513
+ if (!opts.requireApproval) {
514
+ if (opts.isVendor && opts.approvalChanged) {
515
+ return {
516
+ ok: false,
517
+ error: "Vendors cannot change approval status"
518
+ };
519
+ }
520
+ return {
521
+ ok: true
522
+ };
523
+ }
524
+ if (opts.isVendor) {
525
+ if (opts.approvalChanged) {
526
+ return {
527
+ ok: false,
528
+ error: "Vendors cannot change approval status"
529
+ };
530
+ }
531
+ if (opts.activeChanged && opts.toActive && fromApproval !== "approved") {
532
+ return {
533
+ ok: false,
534
+ error: "Event must be approved before it can be activated"
535
+ };
536
+ }
537
+ return {
538
+ ok: true
539
+ };
540
+ }
541
+ if (opts.approvalChanged && toApproval === "rejected") {
542
+ const reason = String(opts.rejectionReason ?? "").trim();
543
+ if (!reason) return {
544
+ ok: false,
545
+ error: "Rejection reason is required"
546
+ };
547
+ }
548
+ return {
549
+ ok: true
550
+ };
551
+ }
552
+ __name(assertEventApprovalUpdate, "assertEventApprovalUpdate");
553
+ function applyEventApprovalStatusSideEffects(updatePayload, opts) {
554
+ const to = opts.toApproval == null || opts.toApproval === "" ? null : String(opts.toApproval);
555
+ if (to == null) return;
556
+ if (to === "approved") {
557
+ updatePayload.approvalStatus = "approved";
558
+ updatePayload.isActive = true;
559
+ updatePayload.rejectionReason = null;
560
+ updatePayload.rejectedAt = null;
561
+ updatePayload.rejectedBy = null;
562
+ return;
563
+ }
564
+ if (to === "rejected") {
565
+ updatePayload.approvalStatus = "rejected";
566
+ updatePayload.rejectionReason = String(opts.rejectionReason ?? "").trim();
567
+ updatePayload.rejectedAt = /* @__PURE__ */ new Date();
568
+ updatePayload.rejectedBy = opts.rejectedBy != null && Number.isFinite(opts.rejectedBy) ? opts.rejectedBy : null;
569
+ const nextActive = "isActive" in updatePayload ? Boolean(updatePayload.isActive) : opts.currentActive;
570
+ if (nextActive) updatePayload.isActive = false;
571
+ return;
572
+ }
573
+ if (to === "pending") {
574
+ updatePayload.approvalStatus = "pending";
575
+ updatePayload.rejectionReason = null;
576
+ updatePayload.rejectedAt = null;
577
+ updatePayload.rejectedBy = null;
578
+ const nextActive = "isActive" in updatePayload ? Boolean(updatePayload.isActive) : opts.currentActive;
579
+ if (nextActive) updatePayload.isActive = false;
580
+ }
581
+ }
582
+ __name(applyEventApprovalStatusSideEffects, "applyEventApprovalStatusSideEffects");
583
+
237
584
  // src/lib/default-vendor-id.ts
238
585
  function getDefaultVendorId() {
239
586
  const raw = process.env.DEFAULT_VENDOR_ID;
@@ -269,17 +616,20 @@ async function assertCatalogCategoryId(dataSource, categoriesEntity, categoryId,
269
616
  const row = await repo.findOne({
270
617
  where: {
271
618
  id,
272
- deleted: false,
273
- isCatalog: true
619
+ deleted: false
274
620
  }
275
621
  });
276
622
  if (!row) {
277
623
  return "Invalid catalog category. Choose one of the platform product category types.";
278
624
  }
279
- return null;
625
+ if (row.isCatalog === true) return null;
626
+ if (options?.allowVendorId != null && Number(row.vendorId) === options.allowVendorId && row.isCatalog !== true) {
627
+ return null;
628
+ }
629
+ return "Invalid catalog category. Choose one of the platform product category types.";
280
630
  }
281
631
  __name(assertCatalogCategoryId, "assertCatalogCategoryId");
282
- async function assertVendorOwnedCollectionForProduct(dataSource, collectionsEntity, collectionId, categoryId, vendorId) {
632
+ async function assertCollectionForVendorProduct(dataSource, collectionsEntity, collectionId, categoryId, vendorId, options) {
283
633
  const cid = Number(collectionId);
284
634
  if (!Number.isFinite(cid) || cid <= 0) return null;
285
635
  if (!collectionsEntity) return "Collections are not configured";
@@ -290,11 +640,13 @@ async function assertVendorOwnedCollectionForProduct(dataSource, collectionsEnti
290
640
  deleted: false
291
641
  }
292
642
  });
293
- if (!row || row.isCatalog === true) {
294
- return "Invalid collection. Choose one of your collections in this category.";
643
+ if (!row) {
644
+ return "Invalid collection. Choose a valid collection in this category.";
295
645
  }
296
- if (Number(row.vendorId) !== vendorId) {
297
- return "Invalid collection. Choose one of your collections in this category.";
646
+ const isCatalog = row.isCatalog === true;
647
+ const isOwn = Number(row.vendorId) === vendorId && !isCatalog;
648
+ if (!isCatalog && !(options?.allowVendorOwned && isOwn)) {
649
+ return "Invalid collection. Choose one of the platform collections (or your own if allowed).";
298
650
  }
299
651
  const catId = Number(categoryId);
300
652
  if (Number.isFinite(catId) && catId > 0 && Number(row.categoryId) !== catId) {
@@ -302,7 +654,7 @@ async function assertVendorOwnedCollectionForProduct(dataSource, collectionsEnti
302
654
  }
303
655
  return null;
304
656
  }
305
- __name(assertVendorOwnedCollectionForProduct, "assertVendorOwnedCollectionForProduct");
657
+ __name(assertCollectionForVendorProduct, "assertCollectionForVendorProduct");
306
658
 
307
659
  // src/lib/catalog-collection.ts
308
660
  function isCatalogCollectionRow(row) {
@@ -316,7 +668,24 @@ function applyPlatformCatalogCollectionFields(persistBody) {
316
668
  }
317
669
  __name(applyPlatformCatalogCollectionFields, "applyPlatformCatalogCollectionFields");
318
670
 
671
+ // src/lib/catalog-brand.ts
672
+ function isCatalogBrandRow(row) {
673
+ return row?.isCatalog === true;
674
+ }
675
+ __name(isCatalogBrandRow, "isCatalogBrandRow");
676
+ function applyPlatformCatalogBrandFields(persistBody) {
677
+ persistBody.isCatalog = true;
678
+ delete persistBody.vendorId;
679
+ persistBody.vendorId = getDefaultVendorId();
680
+ }
681
+ __name(applyPlatformCatalogBrandFields, "applyPlatformCatalogBrandFields");
682
+
319
683
  // src/api/vendor-scope-crud.ts
684
+ var CATALOG_LIST_RESOURCES = /* @__PURE__ */ new Set([
685
+ "product_categories",
686
+ "collections",
687
+ "brands"
688
+ ]);
320
689
  function repoHasVendorIdColumn(repo) {
321
690
  return repo.metadata.columns.some((c) => c.propertyName === "vendorId");
322
691
  }
@@ -325,7 +694,43 @@ function resourceUsesVendorScope(resource) {
325
694
  return resource === "vendors" || VENDOR_SCOPED_STORE_ENTITIES.has(resource);
326
695
  }
327
696
  __name(resourceUsesVendorScope, "resourceUsesVendorScope");
328
- function mergeVendorScopeIntoWhere(where, scope, resource) {
697
+ function mergeWhereClause(where, extra) {
698
+ if (Array.isArray(where)) {
699
+ return where.map((w) => ({
700
+ ...w,
701
+ ...extra
702
+ }));
703
+ }
704
+ if (where && typeof where === "object" && Object.keys(where).length > 0) {
705
+ return {
706
+ ...where,
707
+ ...extra
708
+ };
709
+ }
710
+ return extra;
711
+ }
712
+ __name(mergeWhereClause, "mergeWhereClause");
713
+ function mergeCatalogOrOwnWhere(where, vendorId) {
714
+ const catalog = mergeWhereClause(where, {
715
+ isCatalog: true
716
+ });
717
+ const own = mergeWhereClause(where, {
718
+ vendorId,
719
+ isCatalog: false
720
+ });
721
+ const catalogArr = Array.isArray(catalog) ? catalog : [
722
+ catalog
723
+ ];
724
+ const ownArr = Array.isArray(own) ? own : [
725
+ own
726
+ ];
727
+ return [
728
+ ...catalogArr,
729
+ ...ownArr
730
+ ];
731
+ }
732
+ __name(mergeCatalogOrOwnWhere, "mergeCatalogOrOwnWhere");
733
+ function mergeVendorScopeIntoWhere(where, scope, resource, flags) {
329
734
  if (scope.type === "all") return where;
330
735
  if (scope.type === "deny") {
331
736
  if (resource === "vendors") {
@@ -342,38 +747,20 @@ function mergeVendorScopeIntoWhere(where, scope, resource) {
342
747
  id: In(scope.vendorIds)
343
748
  });
344
749
  }
345
- if (resource === "product_categories" && scope.type === "vendor") {
750
+ if (CATALOG_LIST_RESOURCES.has(resource) && scope.type === "vendor") {
751
+ const canCreate = flags ? vendorCanCreateResource(resource, flags) : false;
752
+ if (canCreate) {
753
+ return mergeCatalogOrOwnWhere(where, scope.vendorId);
754
+ }
346
755
  return mergeWhereClause(where, {
347
756
  isCatalog: true
348
757
  });
349
758
  }
350
- if (resource === "collections" && scope.type === "vendor") {
351
- return mergeWhereClause(where, {
352
- vendorId: scope.vendorId,
353
- isCatalog: false
354
- });
355
- }
356
759
  return mergeWhereClause(where, {
357
760
  vendorId: scope.vendorId
358
761
  });
359
762
  }
360
763
  __name(mergeVendorScopeIntoWhere, "mergeVendorScopeIntoWhere");
361
- function mergeWhereClause(where, extra) {
362
- if (Array.isArray(where)) {
363
- return where.map((w) => ({
364
- ...w,
365
- ...extra
366
- }));
367
- }
368
- if (where && typeof where === "object" && Object.keys(where).length > 0) {
369
- return {
370
- ...where,
371
- ...extra
372
- };
373
- }
374
- return extra;
375
- }
376
- __name(mergeWhereClause, "mergeWhereClause");
377
764
  function applyVendorScopeToQueryBuilder(qb, alias, scope) {
378
765
  if (scope.type === "all") return;
379
766
  if (scope.type === "deny") {
@@ -385,7 +772,7 @@ function applyVendorScopeToQueryBuilder(qb, alias, scope) {
385
772
  });
386
773
  }
387
774
  __name(applyVendorScopeToQueryBuilder, "applyVendorScopeToQueryBuilder");
388
- function rowMatchesVendorScope(row, scope, resource) {
775
+ function rowMatchesVendorScope(row, scope, resource, flags) {
389
776
  if (!row) return false;
390
777
  if (scope.type === "all") return true;
391
778
  if (scope.type === "deny") return false;
@@ -393,27 +780,32 @@ function rowMatchesVendorScope(row, scope, resource) {
393
780
  const id = Number(row.id);
394
781
  return scope.vendorIds.includes(id);
395
782
  }
396
- if (resource === "product_categories" && scope.type === "vendor") {
397
- return isCatalogCategoryRow(row);
398
- }
399
- if (resource === "collections" && scope.type === "vendor") {
400
- return Number(row.vendorId) === scope.vendorId && !isCatalogCollectionRow(row);
783
+ if (CATALOG_LIST_RESOURCES.has(resource) && scope.type === "vendor") {
784
+ const isCatalog = resource === "product_categories" ? isCatalogCategoryRow(row) : resource === "collections" ? isCatalogCollectionRow(row) : isCatalogBrandRow(row);
785
+ if (isCatalog) return true;
786
+ const canCreate = flags ? vendorCanCreateResource(resource, flags) : false;
787
+ return canCreate && Number(row.vendorId) === scope.vendorId && !isCatalog;
401
788
  }
402
789
  return Number(row.vendorId) === scope.vendorId;
403
790
  }
404
791
  __name(rowMatchesVendorScope, "rowMatchesVendorScope");
405
- function vendorCannotMutateCatalogCategories(resource, scope) {
406
- return resource === "product_categories" && scope.type === "vendor";
407
- }
408
- __name(vendorCannotMutateCatalogCategories, "vendorCannotMutateCatalogCategories");
409
- function vendorCannotMutateCatalogCollections(resource, scope, body) {
410
- if (resource !== "collections" || scope.type !== "vendor") return false;
792
+ function vendorCannotMutateCatalogResource(resource, scope, flags, body) {
793
+ if (scope.type !== "vendor" || !CATALOG_LIST_RESOURCES.has(resource)) return false;
794
+ if (!vendorCanCreateResource(resource, flags)) return true;
411
795
  return body?.isCatalog === true;
412
796
  }
413
- __name(vendorCannotMutateCatalogCollections, "vendorCannotMutateCatalogCollections");
414
- function vendorScopeRowAccess(row, scope, resource) {
797
+ __name(vendorCannotMutateCatalogResource, "vendorCannotMutateCatalogResource");
798
+ function vendorCannotMutateExistingCatalogRow(resource, scope, row) {
799
+ if (scope.type !== "vendor" || !CATALOG_LIST_RESOURCES.has(resource) || !row) return false;
800
+ if (resource === "product_categories") return isCatalogCategoryRow(row);
801
+ if (resource === "collections") return isCatalogCollectionRow(row);
802
+ if (resource === "brands") return isCatalogBrandRow(row);
803
+ return false;
804
+ }
805
+ __name(vendorCannotMutateExistingCatalogRow, "vendorCannotMutateExistingCatalogRow");
806
+ function vendorScopeRowAccess(row, scope, resource, flags) {
415
807
  if (!row) return "not_found";
416
- if (rowMatchesVendorScope(row, scope, resource)) return "ok";
808
+ if (rowMatchesVendorScope(row, scope, resource, flags)) return "ok";
417
809
  return scope.type === "vendor" ? "forbidden" : "not_found";
418
810
  }
419
811
  __name(vendorScopeRowAccess, "vendorScopeRowAccess");
@@ -445,14 +837,7 @@ function requireVendorIdForScopedCreate(resource, persistBody, scope, repo, cont
445
837
  ok: true
446
838
  };
447
839
  }
448
- if (resource === "collections" && persistBody.isCatalog === true) {
449
- delete persistBody.vendorId;
450
- persistBody.vendorId = getDefaultVendorId();
451
- return {
452
- ok: true
453
- };
454
- }
455
- if (resource === "product_categories" && persistBody.isCatalog === true) {
840
+ if ((resource === "collections" || resource === "product_categories" || resource === "brands") && persistBody.isCatalog === true) {
456
841
  delete persistBody.vendorId;
457
842
  persistBody.vendorId = getDefaultVendorId();
458
843
  return {
@@ -464,7 +849,7 @@ function requireVendorIdForScopedCreate(resource, persistBody, scope, repo, cont
464
849
  };
465
850
  enforceVendorIdOnCreateBody(persistBody, scope, repo);
466
851
  if (scope.type === "vendor") {
467
- if (resource === "collections") {
852
+ if (resource === "collections" || resource === "product_categories" || resource === "brands") {
468
853
  persistBody.isCatalog = false;
469
854
  }
470
855
  return {
@@ -3411,6 +3796,7 @@ function createCrudHandler(dataSource, entityMap, options) {
3411
3796
  const scope = await resolveScope();
3412
3797
  const repo2 = dataSource.getRepository(entity);
3413
3798
  const statusFilter = searchParams.get("status")?.trim();
3799
+ const approvalStatusFilter = searchParams.get("approvalStatus")?.trim();
3414
3800
  const inventory = searchParams.get("inventory")?.trim();
3415
3801
  let productWhere = {
3416
3802
  deleted: false,
@@ -3418,6 +3804,7 @@ function createCrudHandler(dataSource, entityMap, options) {
3418
3804
  };
3419
3805
  productWhere = mergeVendorScopeIntoWhere(productWhere, scope, resource);
3420
3806
  if (statusFilter) productWhere.status = statusFilter;
3807
+ if (approvalStatusFilter) productWhere.approvalStatus = approvalStatusFilter;
3421
3808
  if (inventory === "in_stock") productWhere.quantity = MoreThan(0);
3422
3809
  if (inventory === "out_of_stock") productWhere.quantity = 0;
3423
3810
  for (const key of [
@@ -3718,6 +4105,7 @@ function createCrudHandler(dataSource, entityMap, options) {
3718
4105
  where = mergeDeletedFalseWhere(repo, where);
3719
4106
  if (resourceUsesVendorScope(resource)) {
3720
4107
  const scope = await resolveScope();
4108
+ const catalogFlags = await getVendorCatalogCreateFlags(dataSource);
3721
4109
  if (scope.type === "deny") {
3722
4110
  where = resource === "vendors" ? {
3723
4111
  id: -1
@@ -3726,7 +4114,7 @@ function createCrudHandler(dataSource, entityMap, options) {
3726
4114
  };
3727
4115
  } else if (resource === "collections") {
3728
4116
  if (scope.type === "vendor") {
3729
- where = mergeVendorScopeIntoWhere(where, scope, resource);
4117
+ where = mergeVendorScopeIntoWhere(where, scope, resource, catalogFlags);
3730
4118
  } else if (searchParams.get("isCatalog") !== "true") {
3731
4119
  where = mergeListWhereAnd(where, {
3732
4120
  isCatalog: false
@@ -3734,14 +4122,22 @@ function createCrudHandler(dataSource, entityMap, options) {
3734
4122
  }
3735
4123
  } else if (resource === "product_categories") {
3736
4124
  if (scope.type === "vendor") {
3737
- where = mergeVendorScopeIntoWhere(where, scope, resource);
4125
+ where = mergeVendorScopeIntoWhere(where, scope, resource, catalogFlags);
4126
+ } else if (searchParams.get("isCatalog") !== "false") {
4127
+ where = mergeListWhereAnd(where, {
4128
+ isCatalog: true
4129
+ });
4130
+ }
4131
+ } else if (resource === "brands") {
4132
+ if (scope.type === "vendor") {
4133
+ where = mergeVendorScopeIntoWhere(where, scope, resource, catalogFlags);
3738
4134
  } else if (searchParams.get("isCatalog") !== "false") {
3739
4135
  where = mergeListWhereAnd(where, {
3740
4136
  isCatalog: true
3741
4137
  });
3742
4138
  }
3743
4139
  } else {
3744
- where = mergeVendorScopeIntoWhere(where, scope, resource);
4140
+ where = mergeVendorScopeIntoWhere(where, scope, resource, catalogFlags);
3745
4141
  }
3746
4142
  }
3747
4143
  let data;
@@ -3811,16 +4207,11 @@ function createCrudHandler(dataSource, entityMap, options) {
3811
4207
  });
3812
4208
  }
3813
4209
  const body = rawPostBody;
3814
- if (vendorCannotMutateCatalogCategories(resource, scopePost)) {
4210
+ const catalogFlagsPost = await getVendorCatalogCreateFlags(dataSource);
4211
+ if (vendorCannotMutateCatalogResource(resource, scopePost, catalogFlagsPost, body)) {
4212
+ const label = resource === "product_categories" ? "Categories" : resource === "collections" ? "Collections" : resource === "brands" ? "Brands" : "This resource";
3815
4213
  return json({
3816
- error: "Categories are managed by the platform administrator"
3817
- }, {
3818
- status: 403
3819
- });
3820
- }
3821
- if (vendorCannotMutateCatalogCollections(resource, scopePost, body)) {
3822
- return json({
3823
- error: "Collections are managed by the platform administrator"
4214
+ error: `${label} are managed by the platform administrator`
3824
4215
  }, {
3825
4216
  status: 403
3826
4217
  });
@@ -4205,8 +4596,15 @@ function createCrudHandler(dataSource, entityMap, options) {
4205
4596
  }
4206
4597
  if (resource === "products") {
4207
4598
  const scopeForProduct = await resolveScope();
4599
+ const productFlags = await getVendorCatalogCreateFlags(dataSource);
4600
+ const requireApproval = await getRequireProductApproval(dataSource);
4601
+ applyVendorProductCreateApproval(persistBody, {
4602
+ isVendor: scopeForProduct.type === "vendor",
4603
+ requireApproval
4604
+ });
4208
4605
  const categoryErr = await assertCatalogCategoryId(dataSource, entityMap.product_categories, persistBody.categoryId, {
4209
- required: scopeForProduct.type === "vendor"
4606
+ required: scopeForProduct.type === "vendor",
4607
+ allowVendorId: scopeForProduct.type === "vendor" && productFlags.categories ? scopeForProduct.vendorId : void 0
4210
4608
  });
4211
4609
  if (categoryErr) return json({
4212
4610
  error: categoryErr
@@ -4215,7 +4613,9 @@ function createCrudHandler(dataSource, entityMap, options) {
4215
4613
  });
4216
4614
  if (scopeForProduct.type === "vendor") {
4217
4615
  if (entityMap.collections && persistBody.collectionId != null && persistBody.collectionId !== "") {
4218
- const collectionErr = await assertVendorOwnedCollectionForProduct(dataSource, entityMap.collections, persistBody.collectionId, persistBody.categoryId, scopeForProduct.vendorId);
4616
+ const collectionErr = await assertCollectionForVendorProduct(dataSource, entityMap.collections, persistBody.collectionId, persistBody.categoryId, scopeForProduct.vendorId, {
4617
+ allowVendorOwned: productFlags.collections
4618
+ });
4219
4619
  if (collectionErr) return json({
4220
4620
  error: collectionErr
4221
4621
  }, {
@@ -4224,6 +4624,14 @@ function createCrudHandler(dataSource, entityMap, options) {
4224
4624
  }
4225
4625
  }
4226
4626
  }
4627
+ if (resource === "events") {
4628
+ const scopeForEvent = await resolveScope();
4629
+ const requireEventApproval = await getRequireEventApproval(dataSource);
4630
+ applyVendorEventCreateApproval(persistBody, {
4631
+ isVendor: scopeForEvent.type === "vendor",
4632
+ requireApproval: requireEventApproval
4633
+ });
4634
+ }
4227
4635
  if (resource === "collections" && scopePost.type === "vendor") {
4228
4636
  const categoryErr = await assertCatalogCategoryId(dataSource, entityMap.product_categories, persistBody.categoryId, {
4229
4637
  required: true
@@ -4428,7 +4836,27 @@ function createCrudHandler(dataSource, entityMap, options) {
4428
4836
  }
4429
4837
  }
4430
4838
  }
4431
- if (!((resource === "collections" || resource === "product_categories") && persistBody.isCatalog === true)) {
4839
+ if (resource === "brands" && scopeCreate.type === "all") {
4840
+ applyPlatformCatalogBrandFields(persistBody);
4841
+ const slug = String(persistBody.slug ?? "").trim();
4842
+ if (slug) {
4843
+ const dup = await repo.findOne({
4844
+ where: {
4845
+ slug,
4846
+ isCatalog: true,
4847
+ deleted: false
4848
+ }
4849
+ });
4850
+ if (dup) {
4851
+ return json({
4852
+ error: "A catalog brand with this slug already exists"
4853
+ }, {
4854
+ status: 400
4855
+ });
4856
+ }
4857
+ }
4858
+ }
4859
+ if (!((resource === "collections" || resource === "product_categories" || resource === "brands") && persistBody.isCatalog === true)) {
4432
4860
  await tryAssignSingleVendorOnAdminCreate(resource, scopeCreate, persistBody);
4433
4861
  }
4434
4862
  const vendorIdCheck = requireVendorIdForScopedCreate(resource, persistBody, scopeCreate, repo, await vendorCreateContext(body));
@@ -4908,13 +5336,14 @@ function createCrudHandler(dataSource, entityMap, options) {
4908
5336
  }
4909
5337
  __name(createCrudHandler, "createCrudHandler");
4910
5338
  function createCrudByIdHandler(dataSource, entityMap, options) {
4911
- const { requireAuth, json, requireEntityPermission: reqPerm, getCms, getDeletedByUserId, getVendorScope } = options;
5339
+ const { requireAuth, json, requireEntityPermission: reqPerm, getCms, getDeletedByUserId, getVendorScope, getHydratedSessionUser } = options;
4912
5340
  const syncContactRowToErp = makeContactErpSync(dataSource, entityMap, getCms);
4913
5341
  const resolveScopeById = getVendorScope ?? (async () => ({
4914
5342
  type: "all"
4915
5343
  }));
4916
- function vendorScopeAccessJson(row, scope, resource) {
4917
- const access = vendorScopeRowAccess(row, scope, resource);
5344
+ async function vendorScopeAccessJson(row, scope, resource) {
5345
+ const flags = await getVendorCatalogCreateFlags(dataSource);
5346
+ const access = vendorScopeRowAccess(row, scope, resource, flags);
4918
5347
  if (access === "ok") return null;
4919
5348
  if (access === "forbidden") return json({
4920
5349
  error: "Forbidden"
@@ -4994,7 +5423,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
4994
5423
  "rules"
4995
5424
  ]
4996
5425
  });
4997
- const discountDenied = vendorScopeAccessJson(discount, scope2, resource);
5426
+ const discountDenied = await vendorScopeAccessJson(discount, scope2, resource);
4998
5427
  if (discountDenied) return discountDenied;
4999
5428
  const flatRules = discount.rules ?? [];
5000
5429
  const nestedRules = nestDiscountRules2(flatRules);
@@ -5052,7 +5481,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5052
5481
  "payments"
5053
5482
  ]
5054
5483
  });
5055
- const orderDenied = vendorScopeAccessJson(order, scope2, resource);
5484
+ const orderDenied = await vendorScopeAccessJson(order, scope2, resource);
5056
5485
  if (orderDenied) return orderDenied;
5057
5486
  const relatedOrders = await repo.find({
5058
5487
  where: {
@@ -5116,7 +5545,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5116
5545
  "contact"
5117
5546
  ]
5118
5547
  });
5119
- const paymentDenied = vendorScopeAccessJson(payment, scope2, resource);
5548
+ const paymentDenied = await vendorScopeAccessJson(payment, scope2, resource);
5120
5549
  if (paymentDenied) return paymentDenied;
5121
5550
  const p = payment;
5122
5551
  const order = p.order;
@@ -5155,7 +5584,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5155
5584
  "attributes.attribute"
5156
5585
  ]
5157
5586
  });
5158
- const productDenied = vendorScopeAccessJson(product, scope2, resource);
5587
+ const productDenied = await vendorScopeAccessJson(product, scope2, resource);
5159
5588
  if (productDenied) return productDenied;
5160
5589
  return product ? json(hydrateProductDisplayName(product)) : json({
5161
5590
  message: "Not found"
@@ -5173,7 +5602,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5173
5602
  "contact"
5174
5603
  ]
5175
5604
  });
5176
- const vcDenied = vendorScopeAccessJson(row, scope2, resource);
5605
+ const vcDenied = await vendorScopeAccessJson(row, scope2, resource);
5177
5606
  if (vcDenied) return vcDenied;
5178
5607
  const contact = row.contact;
5179
5608
  return json({
@@ -5196,7 +5625,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5196
5625
  "items.product"
5197
5626
  ]
5198
5627
  });
5199
- const comboDenied = vendorScopeAccessJson(combo, scope2, resource);
5628
+ const comboDenied = await vendorScopeAccessJson(combo, scope2, resource);
5200
5629
  if (comboDenied) return comboDenied;
5201
5630
  return combo ? json(combo) : json({
5202
5631
  message: "Not found"
@@ -5233,7 +5662,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5233
5662
  where: idWhere
5234
5663
  });
5235
5664
  if (resourceUsesVendorScope(resource)) {
5236
- const itemDenied = vendorScopeAccessJson(item, scope, resource);
5665
+ const itemDenied = await vendorScopeAccessJson(item, scope, resource);
5237
5666
  if (itemDenied) return itemDenied;
5238
5667
  } else if (!item) {
5239
5668
  return json({
@@ -5262,16 +5691,11 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5262
5691
  }
5263
5692
  const rawBody = await req.json();
5264
5693
  const scopePutEarly = await resolveScopeById();
5265
- if (vendorCannotMutateCatalogCategories(resource, scopePutEarly)) {
5694
+ const catalogFlagsPut = await getVendorCatalogCreateFlags(dataSource);
5695
+ if (vendorCannotMutateCatalogResource(resource, scopePutEarly, catalogFlagsPut, rawBody ?? void 0)) {
5696
+ const label = resource === "product_categories" ? "Categories" : resource === "collections" ? "Collections" : resource === "brands" ? "Brands" : "This resource";
5266
5697
  return json({
5267
- error: "Categories are managed by the platform administrator"
5268
- }, {
5269
- status: 403
5270
- });
5271
- }
5272
- if (vendorCannotMutateCatalogCollections(resource, scopePutEarly, rawBody ?? void 0)) {
5273
- return json({
5274
- error: "Collections are managed by the platform administrator"
5698
+ error: `${label} are managed by the platform administrator`
5275
5699
  }, {
5276
5700
  status: 403
5277
5701
  });
@@ -5637,7 +6061,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5637
6061
  id: numericId
5638
6062
  }
5639
6063
  });
5640
- const discountPutDenied = vendorScopeAccessJson(existing, scopePut2, resource);
6064
+ const discountPutDenied = await vendorScopeAccessJson(existing, scopePut2, resource);
5641
6065
  if (discountPutDenied) return discountPutDenied;
5642
6066
  const updatePayload2 = pickColumnUpdates(repo, rawBody);
5643
6067
  delete updatePayload2.vendorId;
@@ -5695,8 +6119,16 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5695
6119
  const existingScope = await repo.findOne({
5696
6120
  where: idWhereCheck
5697
6121
  });
5698
- const scopePutDenied = vendorScopeAccessJson(existingScope, scopePut, resource);
6122
+ const scopePutDenied = await vendorScopeAccessJson(existingScope, scopePut, resource);
5699
6123
  if (scopePutDenied) return scopePutDenied;
6124
+ if (vendorCannotMutateExistingCatalogRow(resource, scopePut, existingScope)) {
6125
+ const label = resource === "product_categories" ? "Categories" : resource === "collections" ? "Collections" : "Brands";
6126
+ return json({
6127
+ error: `${label} are managed by the platform administrator`
6128
+ }, {
6129
+ status: 403
6130
+ });
6131
+ }
5700
6132
  }
5701
6133
  const updatePayload = rawBody && typeof rawBody === "object" ? pickColumnUpdates(repo, rawBody) : {};
5702
6134
  if (resourceUsesVendorScope(resource)) {
@@ -5843,7 +6275,8 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5843
6275
  });
5844
6276
  const mergedCategoryId = "categoryId" in updatePayload ? updatePayload.categoryId : currentRow.categoryId;
5845
6277
  const categoryErr = await assertCatalogCategoryId(dataSource, entityMap.product_categories, "categoryId" in updatePayload ? updatePayload.categoryId : mergedCategoryId, {
5846
- required: scopePut.type === "vendor"
6278
+ required: scopePut.type === "vendor",
6279
+ allowVendorId: scopePut.type === "vendor" && catalogFlagsPut.categories ? scopePut.vendorId : void 0
5847
6280
  });
5848
6281
  if (categoryErr) return json({
5849
6282
  error: categoryErr
@@ -5852,7 +6285,9 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5852
6285
  });
5853
6286
  if (scopePut.type === "vendor") {
5854
6287
  if (entityMap.collections && "collectionId" in updatePayload && updatePayload.collectionId != null) {
5855
- const collectionErr = await assertVendorOwnedCollectionForProduct(dataSource, entityMap.collections, updatePayload.collectionId, mergedCategoryId, scopePut.vendorId);
6288
+ const collectionErr = await assertCollectionForVendorProduct(dataSource, entityMap.collections, updatePayload.collectionId, mergedCategoryId, scopePut.vendorId, {
6289
+ allowVendorOwned: catalogFlagsPut.collections
6290
+ });
5856
6291
  if (collectionErr) return json({
5857
6292
  error: collectionErr
5858
6293
  }, {
@@ -5860,6 +6295,88 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5860
6295
  });
5861
6296
  }
5862
6297
  }
6298
+ if ("status" in updatePayload || "approvalStatus" in updatePayload || "rejectionReason" in updatePayload) {
6299
+ const fromStatus = String(currentRow.status ?? "draft");
6300
+ const toStatus = String("status" in updatePayload ? updatePayload.status ?? fromStatus : fromStatus);
6301
+ const fromApproval = currentRow.approvalStatus;
6302
+ const toApproval = "approvalStatus" in updatePayload ? updatePayload.approvalStatus : fromApproval;
6303
+ const requireApproval = await getRequireProductApproval(dataSource);
6304
+ const reasonFromBody = "rejectionReason" in updatePayload ? updatePayload.rejectionReason : currentRow.rejectionReason;
6305
+ const reasonStr = typeof reasonFromBody === "string" ? reasonFromBody : reasonFromBody == null ? null : String(reasonFromBody);
6306
+ const transition = assertProductApprovalUpdate({
6307
+ fromApproval,
6308
+ toApproval,
6309
+ fromStatus,
6310
+ toStatus,
6311
+ isVendor: scopePut.type === "vendor",
6312
+ requireApproval,
6313
+ approvalChanged: "approvalStatus" in updatePayload,
6314
+ statusChanged: "status" in updatePayload,
6315
+ rejectionReason: reasonStr
6316
+ });
6317
+ if (!transition.ok) return json({
6318
+ error: transition.error
6319
+ }, {
6320
+ status: 400
6321
+ });
6322
+ if ("approvalStatus" in updatePayload && scopePut.type !== "vendor") {
6323
+ const session = await getHydratedSessionUser?.() ?? null;
6324
+ const rejectedByRaw = session?.id != null ? Number(session.id) : null;
6325
+ applyApprovalStatusSideEffects(updatePayload, {
6326
+ toApproval,
6327
+ currentStatus: fromStatus,
6328
+ rejectionReason: reasonStr,
6329
+ rejectedBy: Number.isFinite(rejectedByRaw) ? rejectedByRaw : null
6330
+ });
6331
+ }
6332
+ }
6333
+ }
6334
+ if (resource === "events") {
6335
+ const currentEvent = await repo.findOne({
6336
+ where: {
6337
+ id: numericId,
6338
+ deleted: false
6339
+ }
6340
+ });
6341
+ if (!currentEvent) return json({
6342
+ message: "Not found"
6343
+ }, {
6344
+ status: 404
6345
+ });
6346
+ if ("isActive" in updatePayload || "approvalStatus" in updatePayload || "rejectionReason" in updatePayload) {
6347
+ const fromActive = Boolean(currentEvent.isActive);
6348
+ const toActive = "isActive" in updatePayload ? Boolean(updatePayload.isActive) : fromActive;
6349
+ const fromApproval = currentEvent.approvalStatus;
6350
+ const toApproval = "approvalStatus" in updatePayload ? updatePayload.approvalStatus : fromApproval;
6351
+ const requireEventApproval = await getRequireEventApproval(dataSource);
6352
+ const reasonFromBody = "rejectionReason" in updatePayload ? updatePayload.rejectionReason : currentEvent.rejectionReason;
6353
+ const reasonStr = typeof reasonFromBody === "string" ? reasonFromBody : reasonFromBody == null ? null : String(reasonFromBody);
6354
+ const transition = assertEventApprovalUpdate({
6355
+ fromApproval,
6356
+ toApproval,
6357
+ toActive,
6358
+ isVendor: scopePut.type === "vendor",
6359
+ requireApproval: requireEventApproval,
6360
+ approvalChanged: "approvalStatus" in updatePayload,
6361
+ activeChanged: "isActive" in updatePayload,
6362
+ rejectionReason: reasonStr
6363
+ });
6364
+ if (!transition.ok) return json({
6365
+ error: transition.error
6366
+ }, {
6367
+ status: 400
6368
+ });
6369
+ if ("approvalStatus" in updatePayload && scopePut.type !== "vendor") {
6370
+ const session = await getHydratedSessionUser?.() ?? null;
6371
+ const rejectedByRaw = session?.id != null ? Number(session.id) : null;
6372
+ applyEventApprovalStatusSideEffects(updatePayload, {
6373
+ toApproval,
6374
+ currentActive: fromActive,
6375
+ rejectionReason: reasonStr,
6376
+ rejectedBy: Number.isFinite(rejectedByRaw) ? rejectedByRaw : null
6377
+ });
6378
+ }
6379
+ }
5863
6380
  }
5864
6381
  if (resource === "product_categories" && scopePut.type === "all") {
5865
6382
  const existingCatalogCat = await repo.findOne({
@@ -5917,6 +6434,34 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5917
6434
  }
5918
6435
  }
5919
6436
  }
6437
+ if (resource === "brands" && scopePut.type === "all") {
6438
+ const existingCatalogBrand = await repo.findOne({
6439
+ where: {
6440
+ id: numericId,
6441
+ deleted: false
6442
+ }
6443
+ });
6444
+ if (existingCatalogBrand && existingCatalogBrand.isCatalog === true) {
6445
+ applyPlatformCatalogBrandFields(updatePayload);
6446
+ const slug = String(updatePayload.slug ?? existingCatalogBrand.slug ?? "").trim();
6447
+ if (slug && "slug" in updatePayload) {
6448
+ const dup = await repo.findOne({
6449
+ where: {
6450
+ slug,
6451
+ isCatalog: true,
6452
+ deleted: false
6453
+ }
6454
+ });
6455
+ if (dup && Number(dup.id) !== numericId) {
6456
+ return json({
6457
+ error: "A catalog brand with this slug already exists"
6458
+ }, {
6459
+ status: 400
6460
+ });
6461
+ }
6462
+ }
6463
+ }
6464
+ }
5920
6465
  if (resource === "addresses" && Object.keys(updatePayload).length > 0) {
5921
6466
  const currentRow = await repo.findOne({
5922
6467
  where: {
@@ -6072,9 +6617,11 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
6072
6617
  const authError = await authz(req, resource, "delete");
6073
6618
  if (authError) return authError;
6074
6619
  const scopeDeleteEarly = await resolveScopeById();
6075
- if (vendorCannotMutateCatalogCategories(resource, scopeDeleteEarly)) {
6620
+ const catalogFlagsDelete = await getVendorCatalogCreateFlags(dataSource);
6621
+ if (vendorCannotMutateCatalogResource(resource, scopeDeleteEarly, catalogFlagsDelete)) {
6622
+ const label = resource === "product_categories" ? "Categories" : resource === "collections" ? "Collections" : resource === "brands" ? "Brands" : "This resource";
6076
6623
  return json({
6077
- error: "Categories are managed by the platform administrator"
6624
+ error: `${label} are managed by the platform administrator`
6078
6625
  }, {
6079
6626
  status: 403
6080
6627
  });
@@ -6103,7 +6650,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
6103
6650
  }
6104
6651
  });
6105
6652
  if (resourceUsesVendorScope(resource)) {
6106
- const deleteDenied = vendorScopeAccessJson(existing, scopeDelete, resource);
6653
+ const deleteDenied = await vendorScopeAccessJson(existing, scopeDelete, resource);
6107
6654
  if (deleteDenied) return deleteDenied;
6108
6655
  } else if (!existing) {
6109
6656
  return json({
@@ -6161,7 +6708,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
6161
6708
  id: numericId
6162
6709
  }
6163
6710
  });
6164
- const hardDeleteDenied = vendorScopeAccessJson(existingHard, scopeDelete, resource);
6711
+ const hardDeleteDenied = await vendorScopeAccessJson(existingHard, scopeDelete, resource);
6165
6712
  if (hardDeleteDenied) return hardDeleteDenied;
6166
6713
  }
6167
6714
  const result = await repo.delete(numericId);
@@ -12256,6 +12803,13 @@ function createVendorOnboardHandlers(config) {
12256
12803
  status: 400
12257
12804
  });
12258
12805
  }
12806
+ if (body.activation === "password" || body.user?.password) {
12807
+ return json({
12808
+ error: "Setting a password during vendor onboard is no longer supported. Create the vendor, then send an invite or copy the invite link so the owner can set their own password."
12809
+ }, {
12810
+ status: 400
12811
+ });
12812
+ }
12259
12813
  const profile = parseVendorProfileFromBody(body.vendor, {
12260
12814
  defaultRegistrationStatus: "approved"
12261
12815
  });
@@ -12265,34 +12819,8 @@ function createVendorOnboardHandlers(config) {
12265
12819
  }, {
12266
12820
  status: 400
12267
12821
  });
12268
- const activation = body.activation === "password" ? "password" : "invite";
12269
- const sendOwnerEmail = body.sendOwnerEmail !== false && body.sendInviteEmail !== false;
12270
- let ownerPasswordHash = null;
12271
- if (activation === "password") {
12272
- const plain = body.user?.password?.trim();
12273
- if (!plain) {
12274
- return json({
12275
- error: "Password is required when activating with a set password"
12276
- }, {
12277
- status: 400
12278
- });
12279
- }
12280
- if (plain.length < minPasswordLength) {
12281
- return json({
12282
- error: `Password must be at least ${minPasswordLength} characters`
12283
- }, {
12284
- status: 400
12285
- });
12286
- }
12287
- if (!hashPassword) {
12288
- return json({
12289
- error: "Password hashing is not configured on the server"
12290
- }, {
12291
- status: 501
12292
- });
12293
- }
12294
- ownerPasswordHash = await hashPassword(plain);
12295
- }
12822
+ const activation = "invite";
12823
+ const ownerPasswordHash = null;
12296
12824
  const slug = trimOrNull(body.vendor?.slug) || slugify(vendorName);
12297
12825
  if (!slug) return json({
12298
12826
  error: "Could not derive vendor slug"
@@ -12349,7 +12877,7 @@ function createVendorOnboardHandlers(config) {
12349
12877
  email: userEmail,
12350
12878
  phone: ownerPhone,
12351
12879
  password: ownerPasswordHash,
12352
- blocked: activation === "invite",
12880
+ blocked: true,
12353
12881
  groupId: ownerGroup.id,
12354
12882
  adminAccess: true,
12355
12883
  updatedAt: /* @__PURE__ */ new Date()
@@ -12366,15 +12894,14 @@ function createVendorOnboardHandlers(config) {
12366
12894
  email: userEmail,
12367
12895
  phone: ownerPhone,
12368
12896
  password: ownerPasswordHash,
12369
- blocked: activation === "invite",
12897
+ blocked: true,
12370
12898
  groupId: ownerGroup.id,
12371
12899
  adminAccess: true
12372
12900
  }));
12373
- const baseMetadata = buildVendorMetadata(null, {
12901
+ const metadata = applyRotatingVendorInvite(buildVendorMetadata(null, {
12374
12902
  ownerDesignation,
12375
12903
  termsAccepted: true
12376
- });
12377
- const metadata = activation === "invite" ? applyRotatingVendorInvite(baseMetadata) : baseMetadata;
12904
+ }));
12378
12905
  const vendor = await vendorRepo.save(vendorRepo.create({
12379
12906
  name: vendorName,
12380
12907
  slug,
@@ -12442,39 +12969,14 @@ function createVendorOnboardHandlers(config) {
12442
12969
  };
12443
12970
  });
12444
12971
  let inviteLink;
12445
- let emailSent = false;
12446
- if (activation === "invite") {
12447
- const token = readVendorInviteToken(result.vendor.metadata);
12448
- if (token) {
12449
- inviteLink = buildVendorInviteLink(baseUrl, token);
12450
- }
12451
- }
12452
- if (sendOwnerEmail) {
12453
- emailSent = await trySendVendorOnboardEmails({
12454
- vendorName,
12455
- vendorSlug: slug,
12456
- ownerName: userName,
12457
- ownerEmail: userEmail,
12458
- activation,
12459
- inviteLink,
12460
- sendToOwner: true
12461
- });
12462
- } else if (getCms) {
12463
- await trySendVendorOnboardEmails({
12464
- vendorName,
12465
- vendorSlug: slug,
12466
- ownerName: userName,
12467
- ownerEmail: userEmail,
12468
- activation,
12469
- inviteLink,
12470
- sendToOwner: false
12471
- });
12972
+ const token = readVendorInviteToken(result.vendor.metadata);
12973
+ if (token) {
12974
+ inviteLink = buildVendorInviteLink(baseUrl, token);
12472
12975
  }
12473
- const message = activation === "password" ? emailSent ? "Vendor onboarded successfully. Welcome email sent to the owner." : sendOwnerEmail ? "Vendor onboarded successfully. Owner can sign in with the password you set (welcome email may not have been sent \u2014 check email plugin)." : "Vendor onboarded successfully. Owner can sign in with the password you set." : emailSent ? "Vendor onboarded successfully. Invite email sent." : sendOwnerEmail ? "Vendor onboarded successfully. Invite link created (email may not have been sent \u2014 check email plugin)." : "Vendor onboarded successfully. Share the invite link with the owner.";
12474
12976
  return json({
12475
- message,
12977
+ message: "Vendor created successfully.",
12476
12978
  activation,
12477
- emailSent,
12979
+ emailSent: false,
12478
12980
  vendor: result.vendor,
12479
12981
  user: {
12480
12982
  id: result.user.id,
@@ -12509,9 +13011,11 @@ function createVendorOnboardHandlers(config) {
12509
13011
  });
12510
13012
  }
12511
13013
  let sendEmail = true;
13014
+ let rotate = true;
12512
13015
  try {
12513
13016
  const body = await req.json().catch(() => ({}));
12514
13017
  if (body.sendEmail === false) sendEmail = false;
13018
+ if (body.rotate === false) rotate = false;
12515
13019
  } catch {
12516
13020
  }
12517
13021
  try {
@@ -12577,12 +13081,16 @@ function createVendorOnboardHandlers(config) {
12577
13081
  status: 400
12578
13082
  });
12579
13083
  }
12580
- const rotatedMetadata = applyRotatingVendorInvite(v.metadata);
12581
- await vendorRepo.update(vendorId, {
12582
- metadata: rotatedMetadata,
12583
- inviteStatus: "pending"
12584
- });
12585
- const token = readVendorInviteToken(rotatedMetadata);
13084
+ const nextMetadata = rotate ? applyRotatingVendorInvite(v.metadata) : {
13085
+ ...v.metadata ?? {}
13086
+ };
13087
+ if (rotate || v.inviteStatus !== "pending") {
13088
+ await vendorRepo.update(vendorId, {
13089
+ metadata: nextMetadata,
13090
+ inviteStatus: "pending"
13091
+ });
13092
+ }
13093
+ const token = readVendorInviteToken(nextMetadata);
12586
13094
  if (!token) {
12587
13095
  return json({
12588
13096
  error: "Failed to generate invite token"
@@ -12603,8 +13111,9 @@ function createVendorOnboardHandlers(config) {
12603
13111
  sendToOwner: true
12604
13112
  });
12605
13113
  }
13114
+ const rotatedNote = rotate ? " Previous invite link is no longer valid." : "";
12606
13115
  return json({
12607
- message: emailSent ? "Invite resent. Previous invite link is no longer valid." : sendEmail ? "New invite link created (email may not have been sent \u2014 check email plugin). Previous link is invalid." : "New invite link created. Previous invite link is no longer valid.",
13116
+ message: emailSent ? `Invite email sent.${rotatedNote}` : sendEmail ? `Invite link ready (email may not have been sent \u2014 check email plugin).${rotatedNote}` : rotate ? "New invite link created. Previous invite link is no longer valid." : "Invite link ready.",
12608
13117
  emailSent,
12609
13118
  inviteLink,
12610
13119
  inviteStatus: "pending"
@@ -17072,6 +17581,7 @@ var Brand = class {
17072
17581
  __name(this, "Brand");
17073
17582
  }
17074
17583
  id;
17584
+ /** Null for platform catalog brands (`isCatalog`). */
17075
17585
  vendorId;
17076
17586
  name;
17077
17587
  slug;
@@ -17079,6 +17589,8 @@ var Brand = class {
17079
17589
  metadata;
17080
17590
  description;
17081
17591
  active;
17592
+ /** Platform-wide catalog brand — vendors pick these on products unless allowed to create their own. */
17593
+ isCatalog;
17082
17594
  sortOrder;
17083
17595
  createdAt;
17084
17596
  updatedAt;
@@ -17098,8 +17610,10 @@ _ts_decorate30([
17098
17610
  _ts_metadata30("design:type", Number)
17099
17611
  ], Brand.prototype, "id", void 0);
17100
17612
  _ts_decorate30([
17101
- Column("int"),
17102
- _ts_metadata30("design:type", Number)
17613
+ Column("int", {
17614
+ nullable: true
17615
+ }),
17616
+ _ts_metadata30("design:type", Object)
17103
17617
  ], Brand.prototype, "vendorId", void 0);
17104
17618
  _ts_decorate30([
17105
17619
  Column("varchar"),
@@ -17133,6 +17647,12 @@ _ts_decorate30([
17133
17647
  }),
17134
17648
  _ts_metadata30("design:type", Boolean)
17135
17649
  ], Brand.prototype, "active", void 0);
17650
+ _ts_decorate30([
17651
+ Column("boolean", {
17652
+ default: false
17653
+ }),
17654
+ _ts_metadata30("design:type", Boolean)
17655
+ ], Brand.prototype, "isCatalog", void 0);
17136
17656
  _ts_decorate30([
17137
17657
  Column("int", {
17138
17658
  default: 0
@@ -17192,12 +17712,13 @@ _ts_decorate30([
17192
17712
  ], Brand.prototype, "seoId", void 0);
17193
17713
  _ts_decorate30([
17194
17714
  ManyToOne(() => Vendor, {
17195
- onDelete: "RESTRICT"
17715
+ onDelete: "RESTRICT",
17716
+ nullable: true
17196
17717
  }),
17197
17718
  JoinColumn({
17198
17719
  name: "vendorId"
17199
17720
  }),
17200
- _ts_metadata30("design:type", typeof Vendor === "undefined" ? Object : Vendor)
17721
+ _ts_metadata30("design:type", Object)
17201
17722
  ], Brand.prototype, "vendor", void 0);
17202
17723
  _ts_decorate30([
17203
17724
  ManyToOne(() => Seo, {
@@ -17481,6 +18002,14 @@ var Product = class {
17481
18002
  currencyPrices;
17482
18003
  quantity;
17483
18004
  status;
18005
+ /**
18006
+ * Separate from catalog status. Used when multi_vendor.requireProductApproval is on.
18007
+ * `approved` also forces `status = available` (live).
18008
+ */
18009
+ approvalStatus;
18010
+ rejectionReason;
18011
+ rejectedAt;
18012
+ rejectedBy;
17484
18013
  featured;
17485
18014
  metadata;
17486
18015
  createdAt;
@@ -17604,6 +18133,31 @@ _ts_decorate32([
17604
18133
  }),
17605
18134
  _ts_metadata32("design:type", String)
17606
18135
  ], Product.prototype, "status", void 0);
18136
+ _ts_decorate32([
18137
+ Column("varchar", {
18138
+ nullable: true
18139
+ }),
18140
+ _ts_metadata32("design:type", Object)
18141
+ ], Product.prototype, "approvalStatus", void 0);
18142
+ _ts_decorate32([
18143
+ Column("text", {
18144
+ nullable: true
18145
+ }),
18146
+ _ts_metadata32("design:type", Object)
18147
+ ], Product.prototype, "rejectionReason", void 0);
18148
+ _ts_decorate32([
18149
+ Column({
18150
+ type: "timestamptz",
18151
+ nullable: true
18152
+ }),
18153
+ _ts_metadata32("design:type", Object)
18154
+ ], Product.prototype, "rejectedAt", void 0);
18155
+ _ts_decorate32([
18156
+ Column("int", {
18157
+ nullable: true
18158
+ }),
18159
+ _ts_metadata32("design:type", Object)
18160
+ ], Product.prototype, "rejectedBy", void 0);
17607
18161
  _ts_decorate32([
17608
18162
  Column("boolean", {
17609
18163
  default: false
@@ -20692,6 +21246,14 @@ var Event = class {
20692
21246
  expectedSpeakers;
20693
21247
  expectedParticipants;
20694
21248
  isActive;
21249
+ /**
21250
+ * Separate from isActive. Used when events.requireEventApproval is on.
21251
+ * `approved` also forces `isActive = true` (live).
21252
+ */
21253
+ approvalStatus;
21254
+ rejectionReason;
21255
+ rejectedAt;
21256
+ rejectedBy;
20695
21257
  comingSoon;
20696
21258
  bannerImageUrl;
20697
21259
  logoUrl;
@@ -20786,6 +21348,31 @@ _ts_decorate60([
20786
21348
  }),
20787
21349
  _ts_metadata60("design:type", Boolean)
20788
21350
  ], Event.prototype, "isActive", void 0);
21351
+ _ts_decorate60([
21352
+ Column("varchar", {
21353
+ nullable: true
21354
+ }),
21355
+ _ts_metadata60("design:type", Object)
21356
+ ], Event.prototype, "approvalStatus", void 0);
21357
+ _ts_decorate60([
21358
+ Column("text", {
21359
+ nullable: true
21360
+ }),
21361
+ _ts_metadata60("design:type", Object)
21362
+ ], Event.prototype, "rejectionReason", void 0);
21363
+ _ts_decorate60([
21364
+ Column({
21365
+ type: "timestamptz",
21366
+ nullable: true
21367
+ }),
21368
+ _ts_metadata60("design:type", Object)
21369
+ ], Event.prototype, "rejectedAt", void 0);
21370
+ _ts_decorate60([
21371
+ Column("int", {
21372
+ nullable: true
21373
+ }),
21374
+ _ts_metadata60("design:type", Object)
21375
+ ], Event.prototype, "rejectedBy", void 0);
20789
21376
  _ts_decorate60([
20790
21377
  Column("boolean", {
20791
21378
  default: false
@@ -21346,18 +21933,20 @@ _ts_decorate63([
21346
21933
  ], Combo.prototype, "desc", void 0);
21347
21934
  _ts_decorate63([
21348
21935
  Column({
21349
- type: "integer"
21936
+ type: "integer",
21937
+ nullable: true
21350
21938
  }),
21351
- _ts_metadata63("design:type", Number)
21939
+ _ts_metadata63("design:type", Object)
21352
21940
  ], Combo.prototype, "eventId", void 0);
21353
21941
  _ts_decorate63([
21354
21942
  ManyToOne(() => Event, {
21355
- onDelete: "CASCADE"
21943
+ onDelete: "CASCADE",
21944
+ nullable: true
21356
21945
  }),
21357
21946
  JoinColumn({
21358
21947
  name: "eventId"
21359
21948
  }),
21360
- _ts_metadata63("design:type", typeof Event === "undefined" ? Object : Event)
21949
+ _ts_metadata63("design:type", Object)
21361
21950
  ], Combo.prototype, "event", void 0);
21362
21951
  _ts_decorate63([
21363
21952
  Column({
@@ -26670,16 +27259,24 @@ function createCmsApiHandler(config) {
26670
27259
  if (pe) return pe;
26671
27260
  if (group === "multi_vendor") {
26672
27261
  invalidateMultiVendorCache();
27262
+ invalidateVendorCatalogCreateFlagsCache();
27263
+ invalidateRequireProductApprovalCache();
27264
+ invalidateRequireEventApprovalCache();
26673
27265
  }
26674
27266
  if (group === "events") {
26675
27267
  invalidateEventsCache();
27268
+ invalidateRequireEventApprovalCache();
26676
27269
  }
26677
27270
  const res = await settingsHandlers.PUT(req, group);
26678
27271
  if (group === "multi_vendor") {
26679
27272
  invalidateMultiVendorCache();
27273
+ invalidateVendorCatalogCreateFlagsCache();
27274
+ invalidateRequireProductApprovalCache();
27275
+ invalidateRequireEventApprovalCache();
26680
27276
  }
26681
27277
  if (group === "events") {
26682
27278
  invalidateEventsCache();
27279
+ invalidateRequireEventApprovalCache();
26683
27280
  }
26684
27281
  return res;
26685
27282
  }
@@ -30984,4 +31581,4 @@ function createStorefrontApiHandler(config) {
30984
31581
  }
30985
31582
  __name(createStorefrontApiHandler, "createStorefrontApiHandler");
30986
31583
 
30987
- export { Address, Attendee, Attribute, BLOG_GENERATOR_AGENT_NAME, BLOG_GENERATOR_DEFAULT_SYSTEM_INSTRUCTION, BLOG_GENERATOR_DEFAULT_VALIDATION_RULES, BLOG_GENERATOR_LLM_AGENT_SLUG, BLOG_GENERATOR_MARKDOWN_ARTICLE_SEPARATOR, BLOG_METADATA_ENRICHER_AGENT_NAME, BLOG_METADATA_ENRICHER_DEFAULT_SYSTEM_INSTRUCTION, BLOG_METADATA_ENRICHER_DEFAULT_VALIDATION_RULES, BLOG_METADATA_ENRICHER_LLM_AGENT_SLUG, Blog, BlogGeneratorService, Brand, CMS_ENTITY_MAP, Cart, CartItem, Category, ChatConversation, ChatMessage, Collection, Combo, ComboItem, Comment, Config, Contact, Currency, CurrencyExchange, Customer, Customer_Contacts, Discount, DiscountRules, Event, EventProduct, Form, FormField, FormSubmission, JobSchedule, JobScheduleRun, KnowledgeBaseChunk, KnowledgeBaseDocument, LlmAgent, Media, MessageTemplate, Order, OrderAddresses, OrderDiscounts, OrderItem, OrderNotificationBinding, OrderNotificationTrigger, OtpChallenge, Page, PasswordResetToken, Payment, Permission, Product, ProductAttribute, ProductCategory, ProductConfig, ProductVariant, RefundPolicy, RefundRequest, RssArticle, RssFeed, Seo, Tag, Tax, User, UserGroup, Vendor, VendorCustomer, VendorRole, VendorRolePermission, VendorUser, Wishlist, WishlistItem, ZIP_MIME_TYPES, applyRotatingVendorInvite, applyVendorCustomersContactFilter, assertCaptchaOk, assertContactAllowedForVendorOrder, buildBlogMetadataUserPrompt, buildCronFromSchedule, buildRssUserPromptFromFeeds, buildVendorInviteLink, calculateOrderRefundPreview, calculateRefundFromPolicy, checkEventsEnabled, checkMultiVendorEnabled, contactIsVendorCustomer, countRecentOtpSends, createAnalyticsHandlers, createBlogBySlugHandler, createChangePasswordHandler, createCmsApiHandler, createCmsApp, createCmsAppWithMessaging, createCrudByIdHandler, createCrudHandler, createDashboardStatsHandler, createEcommerceAnalyticsHandler, createEventOrderMessageTemplateHandlers, createForgotPasswordHandler, createFormBySlugHandler, createInviteAcceptHandler, createJobScheduleHandlers, createLlmAgentKnowledgeHandlers, createMediaZipExtractHandler, createMessageTemplateRowLoader, createOtpChallenge, createSetPasswordHandler, createSettingsApiHandlers, createSocialMediaHandlers, createStorefrontApiHandler, createUploadHandler, createUserAuthApiRouter, createUserAvatarHandler, createUserProfileHandler, createUsersApiHandlers, createVendorDashboardHandler, createVendorOnboardHandlers, customerPhoneForUser, daysBeforeEventStart, describeEventTierPolicy, ensureCustomerForUser, ensureMessagingPluginsOnCms, ensureScheduleQueueWorker, ensureVendorCustomerForOrderContact, findActiveRefundPolicyForVendor, findVendorByInviteToken, formatTierRange, generateNumericOtp, getPublicSettingsGroup, getRssArticleSummaryFromItem, hashOtpCode, hydrateVendorSessionUser, invalidateEventsCache, invalidateMultiVendorCache, isCustomerTypeContact, isZipMedia, linkUnclaimedContactToUser, llmAgentToChatAgentOptions, loadSettingsGroupFromDb, loadUserVendorContext, mergeGuardrailsIntoSystemPrompt, messagingPlugins, metaFetchUserManagedPages, metaPostPageFeed, metaPostPagePhoto, metaResolvePageAccessToken, normalizePhoneE164, normalizeRefundTiers, overlayCmsPlugins, parseBlogGeneratorAgentContent, parseBlogGeneratorModelOutput, parseBlogMetadataEnrichmentJson, parseLlmAgentValidationRules, pgBossScheduleNameForId, queueErpCreateContactIfEnabled, queueJobScheduleNow, queuePlugin, queueSms, registerJobRunnerWorker, registerMessagingQueueProcessors, registerSmsQueueProcessor, relativePathFromMediaParentId, resolveBlogCategoryIdByName, resolveSettingsEncryptionKey, resolveVendorIdForContactCheck, sanitizeMediaFolderPath, sanitizeStorageSegment, sendVendorOnboardEmails, simpleDecrypt, simpleEncrypt, syncJobScheduleToPgBoss, validateRefundTiers, validateScheduleInput, validateUserMessageAgainstAgentRules, validateUserMessageAgainstStructuredRules, verifyAndConsumeOtpChallenge, verifyOtpCodeHash, whatsappPlugin, wrapGetCmsWithMessaging };
31584
+ export { Address, Attendee, Attribute, BLOG_GENERATOR_AGENT_NAME, BLOG_GENERATOR_DEFAULT_SYSTEM_INSTRUCTION, BLOG_GENERATOR_DEFAULT_VALIDATION_RULES, BLOG_GENERATOR_LLM_AGENT_SLUG, BLOG_GENERATOR_MARKDOWN_ARTICLE_SEPARATOR, BLOG_METADATA_ENRICHER_AGENT_NAME, BLOG_METADATA_ENRICHER_DEFAULT_SYSTEM_INSTRUCTION, BLOG_METADATA_ENRICHER_DEFAULT_VALIDATION_RULES, BLOG_METADATA_ENRICHER_LLM_AGENT_SLUG, Blog, BlogGeneratorService, Brand, CMS_ENTITY_MAP, Cart, CartItem, Category, ChatConversation, ChatMessage, Collection, Combo, ComboItem, Comment, Config, Contact, Currency, CurrencyExchange, Customer, Customer_Contacts, Discount, DiscountRules, Event, EventProduct, Form, FormField, FormSubmission, JobSchedule, JobScheduleRun, KnowledgeBaseChunk, KnowledgeBaseDocument, LlmAgent, Media, MessageTemplate, Order, OrderAddresses, OrderDiscounts, OrderItem, OrderNotificationBinding, OrderNotificationTrigger, OtpChallenge, Page, PasswordResetToken, Payment, Permission, Product, ProductAttribute, ProductCategory, ProductConfig, ProductVariant, RefundPolicy, RefundRequest, RssArticle, RssFeed, Seo, Tag, Tax, User, UserGroup, Vendor, VendorCustomer, VendorRole, VendorRolePermission, VendorUser, Wishlist, WishlistItem, ZIP_MIME_TYPES, applyApprovalStatusSideEffects, applyEventApprovalStatusSideEffects, applyRotatingVendorInvite, applyVendorCustomersContactFilter, applyVendorEventCreateApproval, applyVendorProductCreateApproval, assertCaptchaOk, assertContactAllowedForVendorOrder, assertEventApprovalUpdate, assertProductApprovalUpdate, buildBlogMetadataUserPrompt, buildCronFromSchedule, buildRssUserPromptFromFeeds, buildVendorInviteLink, calculateOrderRefundPreview, calculateRefundFromPolicy, checkEventsEnabled, checkMultiVendorEnabled, contactIsVendorCustomer, countRecentOtpSends, createAnalyticsHandlers, createBlogBySlugHandler, createChangePasswordHandler, createCmsApiHandler, createCmsApp, createCmsAppWithMessaging, createCrudByIdHandler, createCrudHandler, createDashboardStatsHandler, createEcommerceAnalyticsHandler, createEventOrderMessageTemplateHandlers, createForgotPasswordHandler, createFormBySlugHandler, createInviteAcceptHandler, createJobScheduleHandlers, createLlmAgentKnowledgeHandlers, createMediaZipExtractHandler, createMessageTemplateRowLoader, createOtpChallenge, createSetPasswordHandler, createSettingsApiHandlers, createSocialMediaHandlers, createStorefrontApiHandler, createUploadHandler, createUserAuthApiRouter, createUserAvatarHandler, createUserProfileHandler, createUsersApiHandlers, createVendorDashboardHandler, createVendorOnboardHandlers, customerPhoneForUser, daysBeforeEventStart, describeEventTierPolicy, ensureCustomerForUser, ensureMessagingPluginsOnCms, ensureScheduleQueueWorker, ensureVendorCustomerForOrderContact, findActiveRefundPolicyForVendor, findVendorByInviteToken, formatTierRange, generateNumericOtp, getPublicSettingsGroup, getRequireEventApproval, getRequireProductApproval, getRssArticleSummaryFromItem, getVendorCatalogCreateFlags, hashOtpCode, hydrateVendorSessionUser, invalidateEventsCache, invalidateMultiVendorCache, invalidateRequireEventApprovalCache, invalidateRequireProductApprovalCache, invalidateVendorCatalogCreateFlagsCache, isCustomerTypeContact, isZipMedia, linkUnclaimedContactToUser, llmAgentToChatAgentOptions, loadSettingsGroupFromDb, loadUserVendorContext, mergeGuardrailsIntoSystemPrompt, messagingPlugins, metaFetchUserManagedPages, metaPostPageFeed, metaPostPagePhoto, metaResolvePageAccessToken, normalizePhoneE164, normalizeRefundTiers, overlayCmsPlugins, parseBlogGeneratorAgentContent, parseBlogGeneratorModelOutput, parseBlogMetadataEnrichmentJson, parseLlmAgentValidationRules, pgBossScheduleNameForId, queueErpCreateContactIfEnabled, queueJobScheduleNow, queuePlugin, queueSms, registerJobRunnerWorker, registerMessagingQueueProcessors, registerSmsQueueProcessor, relativePathFromMediaParentId, resolveBlogCategoryIdByName, resolveSettingsEncryptionKey, resolveVendorIdForContactCheck, sanitizeMediaFolderPath, sanitizeStorageSegment, sendVendorOnboardEmails, simpleDecrypt, simpleEncrypt, syncJobScheduleToPgBoss, validateRefundTiers, validateScheduleInput, validateUserMessageAgainstAgentRules, validateUserMessageAgainstStructuredRules, verifyAndConsumeOtpChallenge, verifyOtpCodeHash, whatsappPlugin, wrapGetCmsWithMessaging };