@infuro/cms-core 1.0.41 → 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.
@@ -7,8 +7,8 @@ var chunkXW5ATPRW_cjs = require('./chunk-XW5ATPRW.cjs');
7
7
  var chunk3DZRIZOD_cjs = require('./chunk-3DZRIZOD.cjs');
8
8
  var chunkSPQFNIYJ_cjs = require('./chunk-SPQFNIYJ.cjs');
9
9
  var chunkXQ3QUHWR_cjs = require('./chunk-XQ3QUHWR.cjs');
10
- var chunkOIGCAGH7_cjs = require('./chunk-OIGCAGH7.cjs');
11
- var chunkWMXPKKXU_cjs = require('./chunk-WMXPKKXU.cjs');
10
+ var chunkZMRQ72F6_cjs = require('./chunk-ZMRQ72F6.cjs');
11
+ var chunkX6UQFV4X_cjs = require('./chunk-X6UQFV4X.cjs');
12
12
  var chunkD5FBNKQM_cjs = require('./chunk-D5FBNKQM.cjs');
13
13
  var chunkUPLVMVRX_cjs = require('./chunk-UPLVMVRX.cjs');
14
14
  var chunkUSNT2KNT_cjs = require('./chunk-USNT2KNT.cjs');
@@ -87,7 +87,7 @@ async function loadUserVendorContext(dataSource, userId, preferredVendorId) {
87
87
  `, [
88
88
  primary.vendorRoleId
89
89
  ]);
90
- vendorEntityPerms = chunkWMXPKKXU_cjs.permissionRowsToRecord(permRows);
90
+ vendorEntityPerms = chunkX6UQFV4X_cjs.permissionRowsToRecord(permRows);
91
91
  } catch (err) {
92
92
  if (!isMissingVendorRoleSchemaError(err)) throw err;
93
93
  }
@@ -104,6 +104,79 @@ async function loadUserVendorContext(dataSource, userId, preferredVendorId) {
104
104
  }
105
105
  chunkUSNT2KNT_cjs.__name(loadUserVendorContext, "loadUserVendorContext");
106
106
 
107
+ // src/lib/vendor-catalog-create-flags.ts
108
+ var ALL_OFF = {
109
+ categories: false,
110
+ collections: false,
111
+ brands: false
112
+ };
113
+ var cachedFlags = null;
114
+ function invalidateVendorCatalogCreateFlagsCache() {
115
+ cachedFlags = null;
116
+ }
117
+ chunkUSNT2KNT_cjs.__name(invalidateVendorCatalogCreateFlagsCache, "invalidateVendorCatalogCreateFlagsCache");
118
+ function parseTrue(value) {
119
+ return value === "true";
120
+ }
121
+ chunkUSNT2KNT_cjs.__name(parseTrue, "parseTrue");
122
+ async function isMultiVendorEnabled(dataSource) {
123
+ try {
124
+ const rows = await dataSource.query(`
125
+ SELECT value FROM configs
126
+ WHERE settings = 'multi_vendor' AND key = 'enabled' AND deleted = false
127
+ LIMIT 1
128
+ `);
129
+ return rows.length === 0 || rows[0].value !== "false";
130
+ } catch {
131
+ return true;
132
+ }
133
+ }
134
+ chunkUSNT2KNT_cjs.__name(isMultiVendorEnabled, "isMultiVendorEnabled");
135
+ async function getVendorCatalogCreateFlags(dataSource) {
136
+ if (cachedFlags !== null) return cachedFlags;
137
+ const multiVendorEnabled = await isMultiVendorEnabled(dataSource);
138
+ if (!multiVendorEnabled) {
139
+ cachedFlags = {
140
+ ...ALL_OFF
141
+ };
142
+ return cachedFlags;
143
+ }
144
+ try {
145
+ const rows = await dataSource.query(`
146
+ SELECT key, value FROM configs
147
+ WHERE settings = 'multi_vendor'
148
+ AND key IN (
149
+ 'vendorCanCreateCategories',
150
+ 'vendorCanCreateCollections',
151
+ 'vendorCanCreateBrands'
152
+ )
153
+ AND deleted = false
154
+ `);
155
+ const map = new Map(rows.map((r) => [
156
+ r.key,
157
+ r.value
158
+ ]));
159
+ cachedFlags = {
160
+ categories: parseTrue(map.get("vendorCanCreateCategories")),
161
+ collections: parseTrue(map.get("vendorCanCreateCollections")),
162
+ brands: parseTrue(map.get("vendorCanCreateBrands"))
163
+ };
164
+ } catch {
165
+ cachedFlags = {
166
+ ...ALL_OFF
167
+ };
168
+ }
169
+ return cachedFlags;
170
+ }
171
+ chunkUSNT2KNT_cjs.__name(getVendorCatalogCreateFlags, "getVendorCatalogCreateFlags");
172
+ function vendorCanCreateResource(resource, flags) {
173
+ if (resource === "product_categories") return flags.categories;
174
+ if (resource === "collections") return flags.collections;
175
+ if (resource === "brands") return flags.brands;
176
+ return false;
177
+ }
178
+ chunkUSNT2KNT_cjs.__name(vendorCanCreateResource, "vendorCanCreateResource");
179
+
107
180
  // src/lib/hydrate-vendor-session-user.ts
108
181
  async function hydrateVendorSessionUser(dataSource, user) {
109
182
  if (!user?.email) return user;
@@ -120,7 +193,7 @@ async function hydrateVendorSessionUser(dataSource, user) {
120
193
  ]);
121
194
  const resolvedId = Number(byEmailRows[0]?.id);
122
195
  if (!Number.isFinite(resolvedId)) {
123
- chunkWMXPKKXU_cjs.logRbac("hydrateVendorSessionUser: unable to resolve numeric user id", {
196
+ chunkX6UQFV4X_cjs.logRbac("hydrateVendorSessionUser: unable to resolve numeric user id", {
124
197
  email: user.email,
125
198
  rawSessionId: user.id ?? null
126
199
  });
@@ -140,7 +213,7 @@ async function hydrateVendorSessionUser(dataSource, user) {
140
213
  const groupName = rows[0]?.groupName ?? user.groupName ?? null;
141
214
  const groupId = rows[0]?.groupId ?? user.groupId ?? void 0;
142
215
  const adminAccess = rows[0]?.adminAccess ?? user.adminAccess;
143
- const isRBACAdmin = user.isRBACAdmin ?? chunkWMXPKKXU_cjs.isSuperAdmin({
216
+ const isRBACAdmin = user.isRBACAdmin ?? chunkX6UQFV4X_cjs.isSuperAdmin({
144
217
  groupId,
145
218
  groupName,
146
219
  isRBACAdmin: user.isRBACAdmin
@@ -153,7 +226,7 @@ async function hydrateVendorSessionUser(dataSource, user) {
153
226
  };
154
227
  if (multiVendorEnabled) {
155
228
  ctx = await loadUserVendorContext(dataSource, id, user.activeVendorId);
156
- flags = chunkWMXPKKXU_cjs.vendorPortalFlagsFromUser({
229
+ flags = chunkX6UQFV4X_cjs.vendorPortalFlagsFromUser({
157
230
  email: user.email,
158
231
  isRBACAdmin,
159
232
  groupName,
@@ -188,7 +261,7 @@ async function hydrateVendorSessionUser(dataSource, user) {
188
261
  isVendorPortal: flags.isVendorPortal,
189
262
  isVendorOwner: flags.isVendorOwner
190
263
  };
191
- chunkWMXPKKXU_cjs.logRbac("hydrateVendorSessionUser", {
264
+ chunkX6UQFV4X_cjs.logRbac("hydrateVendorSessionUser", {
192
265
  email: user.email,
193
266
  userId: id,
194
267
  groupName,
@@ -206,6 +279,7 @@ chunkUSNT2KNT_cjs.__name(hydrateVendorSessionUser, "hydrateVendorSessionUser");
206
279
  var cachedMultiVendorEnabled = null;
207
280
  function invalidateMultiVendorCache() {
208
281
  cachedMultiVendorEnabled = null;
282
+ invalidateVendorCatalogCreateFlagsCache();
209
283
  }
210
284
  chunkUSNT2KNT_cjs.__name(invalidateMultiVendorCache, "invalidateMultiVendorCache");
211
285
  async function checkMultiVendorEnabled(dataSource) {
@@ -244,6 +318,279 @@ async function checkEventsEnabled(dataSource) {
244
318
  }
245
319
  chunkUSNT2KNT_cjs.__name(checkEventsEnabled, "checkEventsEnabled");
246
320
 
321
+ // src/lib/product-approval.ts
322
+ var PRODUCT_APPROVAL_STATUSES = [
323
+ "pending",
324
+ "approved",
325
+ "rejected"
326
+ ];
327
+ var APPROVAL_SET = new Set(PRODUCT_APPROVAL_STATUSES);
328
+ var PRODUCT_STATUSES = /* @__PURE__ */ new Set([
329
+ "draft",
330
+ "available",
331
+ "reserved",
332
+ "sold"
333
+ ]);
334
+ var cachedRequireApproval = null;
335
+ function invalidateRequireProductApprovalCache() {
336
+ cachedRequireApproval = null;
337
+ }
338
+ chunkUSNT2KNT_cjs.__name(invalidateRequireProductApprovalCache, "invalidateRequireProductApprovalCache");
339
+ function parseTrue2(value) {
340
+ return value === "true";
341
+ }
342
+ chunkUSNT2KNT_cjs.__name(parseTrue2, "parseTrue");
343
+ async function getRequireProductApproval(dataSource) {
344
+ if (cachedRequireApproval !== null) return cachedRequireApproval;
345
+ const multiVendorEnabled = await checkMultiVendorEnabled(dataSource);
346
+ if (!multiVendorEnabled) {
347
+ cachedRequireApproval = false;
348
+ return false;
349
+ }
350
+ try {
351
+ const rows = await dataSource.query(`
352
+ SELECT value FROM configs
353
+ WHERE settings = 'multi_vendor' AND key = 'requireProductApproval' AND deleted = false
354
+ LIMIT 1
355
+ `);
356
+ cachedRequireApproval = rows.length > 0 && parseTrue2(rows[0].value);
357
+ } catch {
358
+ cachedRequireApproval = false;
359
+ }
360
+ return cachedRequireApproval;
361
+ }
362
+ chunkUSNT2KNT_cjs.__name(getRequireProductApproval, "getRequireProductApproval");
363
+ function applyVendorProductCreateApproval(persistBody, opts) {
364
+ if (!opts.isVendor || !opts.requireApproval) return;
365
+ persistBody.approvalStatus = "pending";
366
+ if (persistBody.status === "available") persistBody.status = "draft";
367
+ persistBody.rejectionReason = null;
368
+ persistBody.rejectedAt = null;
369
+ persistBody.rejectedBy = null;
370
+ }
371
+ chunkUSNT2KNT_cjs.__name(applyVendorProductCreateApproval, "applyVendorProductCreateApproval");
372
+ function assertProductApprovalUpdate(opts) {
373
+ const fromApproval = opts.fromApproval == null || opts.fromApproval === "" ? null : String(opts.fromApproval);
374
+ const toApproval = opts.toApproval == null || opts.toApproval === "" ? null : String(opts.toApproval);
375
+ const fromStatus = String(opts.fromStatus || "draft");
376
+ const toStatus = String(opts.toStatus || fromStatus);
377
+ if (opts.statusChanged && !PRODUCT_STATUSES.has(toStatus)) {
378
+ return {
379
+ ok: false,
380
+ error: `Invalid product status: ${toStatus}`
381
+ };
382
+ }
383
+ if (opts.approvalChanged && toApproval != null && !APPROVAL_SET.has(toApproval)) {
384
+ return {
385
+ ok: false,
386
+ error: `Invalid approval status: ${toApproval}`
387
+ };
388
+ }
389
+ if (!opts.requireApproval) {
390
+ if (opts.isVendor && opts.approvalChanged) {
391
+ return {
392
+ ok: false,
393
+ error: "Vendors cannot change approval status"
394
+ };
395
+ }
396
+ return {
397
+ ok: true
398
+ };
399
+ }
400
+ if (opts.isVendor) {
401
+ if (opts.approvalChanged) {
402
+ return {
403
+ ok: false,
404
+ error: "Vendors cannot change approval status"
405
+ };
406
+ }
407
+ if (opts.statusChanged && toStatus === "available" && fromApproval !== "approved") {
408
+ return {
409
+ ok: false,
410
+ error: "Product must be approved before it can be available"
411
+ };
412
+ }
413
+ if (fromApproval === "rejected" && opts.statusChanged && toStatus === "available") {
414
+ return {
415
+ ok: false,
416
+ error: "Rejected product cannot be made available"
417
+ };
418
+ }
419
+ return {
420
+ ok: true
421
+ };
422
+ }
423
+ if (opts.approvalChanged && toApproval === "rejected") {
424
+ const reason = String(opts.rejectionReason ?? "").trim();
425
+ if (!reason) return {
426
+ ok: false,
427
+ error: "Rejection reason is required"
428
+ };
429
+ }
430
+ return {
431
+ ok: true
432
+ };
433
+ }
434
+ chunkUSNT2KNT_cjs.__name(assertProductApprovalUpdate, "assertProductApprovalUpdate");
435
+ function applyApprovalStatusSideEffects(updatePayload, opts) {
436
+ const to = opts.toApproval == null || opts.toApproval === "" ? null : String(opts.toApproval);
437
+ if (to == null) return;
438
+ if (to === "approved") {
439
+ updatePayload.approvalStatus = "approved";
440
+ updatePayload.status = "available";
441
+ updatePayload.rejectionReason = null;
442
+ updatePayload.rejectedAt = null;
443
+ updatePayload.rejectedBy = null;
444
+ return;
445
+ }
446
+ if (to === "rejected") {
447
+ updatePayload.approvalStatus = "rejected";
448
+ updatePayload.rejectionReason = String(opts.rejectionReason ?? "").trim();
449
+ updatePayload.rejectedAt = /* @__PURE__ */ new Date();
450
+ updatePayload.rejectedBy = opts.rejectedBy != null && Number.isFinite(opts.rejectedBy) ? opts.rejectedBy : null;
451
+ const nextStatus = "status" in updatePayload ? String(updatePayload.status) : opts.currentStatus;
452
+ if (nextStatus === "available") updatePayload.status = "draft";
453
+ return;
454
+ }
455
+ if (to === "pending") {
456
+ updatePayload.approvalStatus = "pending";
457
+ updatePayload.rejectionReason = null;
458
+ updatePayload.rejectedAt = null;
459
+ updatePayload.rejectedBy = null;
460
+ const nextStatus = "status" in updatePayload ? String(updatePayload.status) : opts.currentStatus;
461
+ if (nextStatus === "available") updatePayload.status = "draft";
462
+ }
463
+ }
464
+ chunkUSNT2KNT_cjs.__name(applyApprovalStatusSideEffects, "applyApprovalStatusSideEffects");
465
+
466
+ // src/lib/event-approval.ts
467
+ var EVENT_APPROVAL_STATUSES = [
468
+ "pending",
469
+ "approved",
470
+ "rejected"
471
+ ];
472
+ var APPROVAL_SET2 = new Set(EVENT_APPROVAL_STATUSES);
473
+ var cachedRequireApproval2 = null;
474
+ function invalidateRequireEventApprovalCache() {
475
+ cachedRequireApproval2 = null;
476
+ }
477
+ chunkUSNT2KNT_cjs.__name(invalidateRequireEventApprovalCache, "invalidateRequireEventApprovalCache");
478
+ function parseTrue3(value) {
479
+ return value === "true";
480
+ }
481
+ chunkUSNT2KNT_cjs.__name(parseTrue3, "parseTrue");
482
+ async function getRequireEventApproval(dataSource) {
483
+ if (cachedRequireApproval2 !== null) return cachedRequireApproval2;
484
+ const [eventsOn, multiVendorOn] = await Promise.all([
485
+ checkEventsEnabled(dataSource),
486
+ checkMultiVendorEnabled(dataSource)
487
+ ]);
488
+ if (!eventsOn || !multiVendorOn) {
489
+ cachedRequireApproval2 = false;
490
+ return false;
491
+ }
492
+ try {
493
+ const rows = await dataSource.query(`
494
+ SELECT value FROM configs
495
+ WHERE settings = 'events' AND key = 'requireEventApproval' AND deleted = false
496
+ LIMIT 1
497
+ `);
498
+ cachedRequireApproval2 = rows.length > 0 && parseTrue3(rows[0].value);
499
+ } catch {
500
+ cachedRequireApproval2 = false;
501
+ }
502
+ return cachedRequireApproval2;
503
+ }
504
+ chunkUSNT2KNT_cjs.__name(getRequireEventApproval, "getRequireEventApproval");
505
+ function applyVendorEventCreateApproval(persistBody, opts) {
506
+ if (!opts.isVendor || !opts.requireApproval) return;
507
+ persistBody.approvalStatus = "pending";
508
+ persistBody.isActive = false;
509
+ persistBody.rejectionReason = null;
510
+ persistBody.rejectedAt = null;
511
+ persistBody.rejectedBy = null;
512
+ }
513
+ chunkUSNT2KNT_cjs.__name(applyVendorEventCreateApproval, "applyVendorEventCreateApproval");
514
+ function assertEventApprovalUpdate(opts) {
515
+ const fromApproval = opts.fromApproval == null || opts.fromApproval === "" ? null : String(opts.fromApproval);
516
+ const toApproval = opts.toApproval == null || opts.toApproval === "" ? null : String(opts.toApproval);
517
+ if (opts.approvalChanged && toApproval != null && !APPROVAL_SET2.has(toApproval)) {
518
+ return {
519
+ ok: false,
520
+ error: `Invalid approval status: ${toApproval}`
521
+ };
522
+ }
523
+ if (!opts.requireApproval) {
524
+ if (opts.isVendor && opts.approvalChanged) {
525
+ return {
526
+ ok: false,
527
+ error: "Vendors cannot change approval status"
528
+ };
529
+ }
530
+ return {
531
+ ok: true
532
+ };
533
+ }
534
+ if (opts.isVendor) {
535
+ if (opts.approvalChanged) {
536
+ return {
537
+ ok: false,
538
+ error: "Vendors cannot change approval status"
539
+ };
540
+ }
541
+ if (opts.activeChanged && opts.toActive && fromApproval !== "approved") {
542
+ return {
543
+ ok: false,
544
+ error: "Event must be approved before it can be activated"
545
+ };
546
+ }
547
+ return {
548
+ ok: true
549
+ };
550
+ }
551
+ if (opts.approvalChanged && toApproval === "rejected") {
552
+ const reason = String(opts.rejectionReason ?? "").trim();
553
+ if (!reason) return {
554
+ ok: false,
555
+ error: "Rejection reason is required"
556
+ };
557
+ }
558
+ return {
559
+ ok: true
560
+ };
561
+ }
562
+ chunkUSNT2KNT_cjs.__name(assertEventApprovalUpdate, "assertEventApprovalUpdate");
563
+ function applyEventApprovalStatusSideEffects(updatePayload, opts) {
564
+ const to = opts.toApproval == null || opts.toApproval === "" ? null : String(opts.toApproval);
565
+ if (to == null) return;
566
+ if (to === "approved") {
567
+ updatePayload.approvalStatus = "approved";
568
+ updatePayload.isActive = true;
569
+ updatePayload.rejectionReason = null;
570
+ updatePayload.rejectedAt = null;
571
+ updatePayload.rejectedBy = null;
572
+ return;
573
+ }
574
+ if (to === "rejected") {
575
+ updatePayload.approvalStatus = "rejected";
576
+ updatePayload.rejectionReason = String(opts.rejectionReason ?? "").trim();
577
+ updatePayload.rejectedAt = /* @__PURE__ */ new Date();
578
+ updatePayload.rejectedBy = opts.rejectedBy != null && Number.isFinite(opts.rejectedBy) ? opts.rejectedBy : null;
579
+ const nextActive = "isActive" in updatePayload ? Boolean(updatePayload.isActive) : opts.currentActive;
580
+ if (nextActive) updatePayload.isActive = false;
581
+ return;
582
+ }
583
+ if (to === "pending") {
584
+ updatePayload.approvalStatus = "pending";
585
+ updatePayload.rejectionReason = null;
586
+ updatePayload.rejectedAt = null;
587
+ updatePayload.rejectedBy = null;
588
+ const nextActive = "isActive" in updatePayload ? Boolean(updatePayload.isActive) : opts.currentActive;
589
+ if (nextActive) updatePayload.isActive = false;
590
+ }
591
+ }
592
+ chunkUSNT2KNT_cjs.__name(applyEventApprovalStatusSideEffects, "applyEventApprovalStatusSideEffects");
593
+
247
594
  // src/lib/default-vendor-id.ts
248
595
  function getDefaultVendorId() {
249
596
  const raw = process.env.DEFAULT_VENDOR_ID;
@@ -279,17 +626,20 @@ async function assertCatalogCategoryId(dataSource, categoriesEntity, categoryId,
279
626
  const row = await repo.findOne({
280
627
  where: {
281
628
  id,
282
- deleted: false,
283
- isCatalog: true
629
+ deleted: false
284
630
  }
285
631
  });
286
632
  if (!row) {
287
633
  return "Invalid catalog category. Choose one of the platform product category types.";
288
634
  }
289
- return null;
635
+ if (row.isCatalog === true) return null;
636
+ if (options?.allowVendorId != null && Number(row.vendorId) === options.allowVendorId && row.isCatalog !== true) {
637
+ return null;
638
+ }
639
+ return "Invalid catalog category. Choose one of the platform product category types.";
290
640
  }
291
641
  chunkUSNT2KNT_cjs.__name(assertCatalogCategoryId, "assertCatalogCategoryId");
292
- async function assertVendorOwnedCollectionForProduct(dataSource, collectionsEntity, collectionId, categoryId, vendorId) {
642
+ async function assertCollectionForVendorProduct(dataSource, collectionsEntity, collectionId, categoryId, vendorId, options) {
293
643
  const cid = Number(collectionId);
294
644
  if (!Number.isFinite(cid) || cid <= 0) return null;
295
645
  if (!collectionsEntity) return "Collections are not configured";
@@ -300,11 +650,13 @@ async function assertVendorOwnedCollectionForProduct(dataSource, collectionsEnti
300
650
  deleted: false
301
651
  }
302
652
  });
303
- if (!row || row.isCatalog === true) {
304
- return "Invalid collection. Choose one of your collections in this category.";
653
+ if (!row) {
654
+ return "Invalid collection. Choose a valid collection in this category.";
305
655
  }
306
- if (Number(row.vendorId) !== vendorId) {
307
- return "Invalid collection. Choose one of your collections in this category.";
656
+ const isCatalog = row.isCatalog === true;
657
+ const isOwn = Number(row.vendorId) === vendorId && !isCatalog;
658
+ if (!isCatalog && !(options?.allowVendorOwned && isOwn)) {
659
+ return "Invalid collection. Choose one of the platform collections (or your own if allowed).";
308
660
  }
309
661
  const catId = Number(categoryId);
310
662
  if (Number.isFinite(catId) && catId > 0 && Number(row.categoryId) !== catId) {
@@ -312,7 +664,7 @@ async function assertVendorOwnedCollectionForProduct(dataSource, collectionsEnti
312
664
  }
313
665
  return null;
314
666
  }
315
- chunkUSNT2KNT_cjs.__name(assertVendorOwnedCollectionForProduct, "assertVendorOwnedCollectionForProduct");
667
+ chunkUSNT2KNT_cjs.__name(assertCollectionForVendorProduct, "assertCollectionForVendorProduct");
316
668
 
317
669
  // src/lib/catalog-collection.ts
318
670
  function isCatalogCollectionRow(row) {
@@ -326,16 +678,69 @@ function applyPlatformCatalogCollectionFields(persistBody) {
326
678
  }
327
679
  chunkUSNT2KNT_cjs.__name(applyPlatformCatalogCollectionFields, "applyPlatformCatalogCollectionFields");
328
680
 
681
+ // src/lib/catalog-brand.ts
682
+ function isCatalogBrandRow(row) {
683
+ return row?.isCatalog === true;
684
+ }
685
+ chunkUSNT2KNT_cjs.__name(isCatalogBrandRow, "isCatalogBrandRow");
686
+ function applyPlatformCatalogBrandFields(persistBody) {
687
+ persistBody.isCatalog = true;
688
+ delete persistBody.vendorId;
689
+ persistBody.vendorId = getDefaultVendorId();
690
+ }
691
+ chunkUSNT2KNT_cjs.__name(applyPlatformCatalogBrandFields, "applyPlatformCatalogBrandFields");
692
+
329
693
  // src/api/vendor-scope-crud.ts
694
+ var CATALOG_LIST_RESOURCES = /* @__PURE__ */ new Set([
695
+ "product_categories",
696
+ "collections",
697
+ "brands"
698
+ ]);
330
699
  function repoHasVendorIdColumn(repo) {
331
700
  return repo.metadata.columns.some((c) => c.propertyName === "vendorId");
332
701
  }
333
702
  chunkUSNT2KNT_cjs.__name(repoHasVendorIdColumn, "repoHasVendorIdColumn");
334
703
  function resourceUsesVendorScope(resource) {
335
- return resource === "vendors" || chunkWMXPKKXU_cjs.VENDOR_SCOPED_STORE_ENTITIES.has(resource);
704
+ return resource === "vendors" || chunkX6UQFV4X_cjs.VENDOR_SCOPED_STORE_ENTITIES.has(resource);
336
705
  }
337
706
  chunkUSNT2KNT_cjs.__name(resourceUsesVendorScope, "resourceUsesVendorScope");
338
- function mergeVendorScopeIntoWhere(where, scope, resource) {
707
+ function mergeWhereClause(where, extra) {
708
+ if (Array.isArray(where)) {
709
+ return where.map((w) => ({
710
+ ...w,
711
+ ...extra
712
+ }));
713
+ }
714
+ if (where && typeof where === "object" && Object.keys(where).length > 0) {
715
+ return {
716
+ ...where,
717
+ ...extra
718
+ };
719
+ }
720
+ return extra;
721
+ }
722
+ chunkUSNT2KNT_cjs.__name(mergeWhereClause, "mergeWhereClause");
723
+ function mergeCatalogOrOwnWhere(where, vendorId) {
724
+ const catalog = mergeWhereClause(where, {
725
+ isCatalog: true
726
+ });
727
+ const own = mergeWhereClause(where, {
728
+ vendorId,
729
+ isCatalog: false
730
+ });
731
+ const catalogArr = Array.isArray(catalog) ? catalog : [
732
+ catalog
733
+ ];
734
+ const ownArr = Array.isArray(own) ? own : [
735
+ own
736
+ ];
737
+ return [
738
+ ...catalogArr,
739
+ ...ownArr
740
+ ];
741
+ }
742
+ chunkUSNT2KNT_cjs.__name(mergeCatalogOrOwnWhere, "mergeCatalogOrOwnWhere");
743
+ function mergeVendorScopeIntoWhere(where, scope, resource, flags) {
339
744
  if (scope.type === "all") return where;
340
745
  if (scope.type === "deny") {
341
746
  if (resource === "vendors") {
@@ -352,38 +757,20 @@ function mergeVendorScopeIntoWhere(where, scope, resource) {
352
757
  id: typeorm.In(scope.vendorIds)
353
758
  });
354
759
  }
355
- if (resource === "product_categories" && scope.type === "vendor") {
760
+ if (CATALOG_LIST_RESOURCES.has(resource) && scope.type === "vendor") {
761
+ const canCreate = flags ? vendorCanCreateResource(resource, flags) : false;
762
+ if (canCreate) {
763
+ return mergeCatalogOrOwnWhere(where, scope.vendorId);
764
+ }
356
765
  return mergeWhereClause(where, {
357
766
  isCatalog: true
358
767
  });
359
768
  }
360
- if (resource === "collections" && scope.type === "vendor") {
361
- return mergeWhereClause(where, {
362
- vendorId: scope.vendorId,
363
- isCatalog: false
364
- });
365
- }
366
769
  return mergeWhereClause(where, {
367
770
  vendorId: scope.vendorId
368
771
  });
369
772
  }
370
773
  chunkUSNT2KNT_cjs.__name(mergeVendorScopeIntoWhere, "mergeVendorScopeIntoWhere");
371
- function mergeWhereClause(where, extra) {
372
- if (Array.isArray(where)) {
373
- return where.map((w) => ({
374
- ...w,
375
- ...extra
376
- }));
377
- }
378
- if (where && typeof where === "object" && Object.keys(where).length > 0) {
379
- return {
380
- ...where,
381
- ...extra
382
- };
383
- }
384
- return extra;
385
- }
386
- chunkUSNT2KNT_cjs.__name(mergeWhereClause, "mergeWhereClause");
387
774
  function applyVendorScopeToQueryBuilder(qb, alias, scope) {
388
775
  if (scope.type === "all") return;
389
776
  if (scope.type === "deny") {
@@ -395,7 +782,7 @@ function applyVendorScopeToQueryBuilder(qb, alias, scope) {
395
782
  });
396
783
  }
397
784
  chunkUSNT2KNT_cjs.__name(applyVendorScopeToQueryBuilder, "applyVendorScopeToQueryBuilder");
398
- function rowMatchesVendorScope(row, scope, resource) {
785
+ function rowMatchesVendorScope(row, scope, resource, flags) {
399
786
  if (!row) return false;
400
787
  if (scope.type === "all") return true;
401
788
  if (scope.type === "deny") return false;
@@ -403,27 +790,32 @@ function rowMatchesVendorScope(row, scope, resource) {
403
790
  const id = Number(row.id);
404
791
  return scope.vendorIds.includes(id);
405
792
  }
406
- if (resource === "product_categories" && scope.type === "vendor") {
407
- return isCatalogCategoryRow(row);
408
- }
409
- if (resource === "collections" && scope.type === "vendor") {
410
- return Number(row.vendorId) === scope.vendorId && !isCatalogCollectionRow(row);
793
+ if (CATALOG_LIST_RESOURCES.has(resource) && scope.type === "vendor") {
794
+ const isCatalog = resource === "product_categories" ? isCatalogCategoryRow(row) : resource === "collections" ? isCatalogCollectionRow(row) : isCatalogBrandRow(row);
795
+ if (isCatalog) return true;
796
+ const canCreate = flags ? vendorCanCreateResource(resource, flags) : false;
797
+ return canCreate && Number(row.vendorId) === scope.vendorId && !isCatalog;
411
798
  }
412
799
  return Number(row.vendorId) === scope.vendorId;
413
800
  }
414
801
  chunkUSNT2KNT_cjs.__name(rowMatchesVendorScope, "rowMatchesVendorScope");
415
- function vendorCannotMutateCatalogCategories(resource, scope) {
416
- return resource === "product_categories" && scope.type === "vendor";
417
- }
418
- chunkUSNT2KNT_cjs.__name(vendorCannotMutateCatalogCategories, "vendorCannotMutateCatalogCategories");
419
- function vendorCannotMutateCatalogCollections(resource, scope, body) {
420
- if (resource !== "collections" || scope.type !== "vendor") return false;
802
+ function vendorCannotMutateCatalogResource(resource, scope, flags, body) {
803
+ if (scope.type !== "vendor" || !CATALOG_LIST_RESOURCES.has(resource)) return false;
804
+ if (!vendorCanCreateResource(resource, flags)) return true;
421
805
  return body?.isCatalog === true;
422
806
  }
423
- chunkUSNT2KNT_cjs.__name(vendorCannotMutateCatalogCollections, "vendorCannotMutateCatalogCollections");
424
- function vendorScopeRowAccess(row, scope, resource) {
807
+ chunkUSNT2KNT_cjs.__name(vendorCannotMutateCatalogResource, "vendorCannotMutateCatalogResource");
808
+ function vendorCannotMutateExistingCatalogRow(resource, scope, row) {
809
+ if (scope.type !== "vendor" || !CATALOG_LIST_RESOURCES.has(resource) || !row) return false;
810
+ if (resource === "product_categories") return isCatalogCategoryRow(row);
811
+ if (resource === "collections") return isCatalogCollectionRow(row);
812
+ if (resource === "brands") return isCatalogBrandRow(row);
813
+ return false;
814
+ }
815
+ chunkUSNT2KNT_cjs.__name(vendorCannotMutateExistingCatalogRow, "vendorCannotMutateExistingCatalogRow");
816
+ function vendorScopeRowAccess(row, scope, resource, flags) {
425
817
  if (!row) return "not_found";
426
- if (rowMatchesVendorScope(row, scope, resource)) return "ok";
818
+ if (rowMatchesVendorScope(row, scope, resource, flags)) return "ok";
427
819
  return scope.type === "vendor" ? "forbidden" : "not_found";
428
820
  }
429
821
  chunkUSNT2KNT_cjs.__name(vendorScopeRowAccess, "vendorScopeRowAccess");
@@ -455,14 +847,7 @@ function requireVendorIdForScopedCreate(resource, persistBody, scope, repo, cont
455
847
  ok: true
456
848
  };
457
849
  }
458
- if (resource === "collections" && persistBody.isCatalog === true) {
459
- delete persistBody.vendorId;
460
- persistBody.vendorId = getDefaultVendorId();
461
- return {
462
- ok: true
463
- };
464
- }
465
- if (resource === "product_categories" && persistBody.isCatalog === true) {
850
+ if ((resource === "collections" || resource === "product_categories" || resource === "brands") && persistBody.isCatalog === true) {
466
851
  delete persistBody.vendorId;
467
852
  persistBody.vendorId = getDefaultVendorId();
468
853
  return {
@@ -474,7 +859,7 @@ function requireVendorIdForScopedCreate(resource, persistBody, scope, repo, cont
474
859
  };
475
860
  enforceVendorIdOnCreateBody(persistBody, scope, repo);
476
861
  if (scope.type === "vendor") {
477
- if (resource === "collections") {
862
+ if (resource === "collections" || resource === "product_categories" || resource === "brands") {
478
863
  persistBody.isCatalog = false;
479
864
  }
480
865
  return {
@@ -3421,6 +3806,7 @@ function createCrudHandler(dataSource, entityMap, options) {
3421
3806
  const scope = await resolveScope();
3422
3807
  const repo2 = dataSource.getRepository(entity);
3423
3808
  const statusFilter = searchParams.get("status")?.trim();
3809
+ const approvalStatusFilter = searchParams.get("approvalStatus")?.trim();
3424
3810
  const inventory = searchParams.get("inventory")?.trim();
3425
3811
  let productWhere = {
3426
3812
  deleted: false,
@@ -3428,6 +3814,7 @@ function createCrudHandler(dataSource, entityMap, options) {
3428
3814
  };
3429
3815
  productWhere = mergeVendorScopeIntoWhere(productWhere, scope, resource);
3430
3816
  if (statusFilter) productWhere.status = statusFilter;
3817
+ if (approvalStatusFilter) productWhere.approvalStatus = approvalStatusFilter;
3431
3818
  if (inventory === "in_stock") productWhere.quantity = typeorm.MoreThan(0);
3432
3819
  if (inventory === "out_of_stock") productWhere.quantity = 0;
3433
3820
  for (const key of [
@@ -3728,6 +4115,7 @@ function createCrudHandler(dataSource, entityMap, options) {
3728
4115
  where = mergeDeletedFalseWhere(repo, where);
3729
4116
  if (resourceUsesVendorScope(resource)) {
3730
4117
  const scope = await resolveScope();
4118
+ const catalogFlags = await getVendorCatalogCreateFlags(dataSource);
3731
4119
  if (scope.type === "deny") {
3732
4120
  where = resource === "vendors" ? {
3733
4121
  id: -1
@@ -3736,7 +4124,7 @@ function createCrudHandler(dataSource, entityMap, options) {
3736
4124
  };
3737
4125
  } else if (resource === "collections") {
3738
4126
  if (scope.type === "vendor") {
3739
- where = mergeVendorScopeIntoWhere(where, scope, resource);
4127
+ where = mergeVendorScopeIntoWhere(where, scope, resource, catalogFlags);
3740
4128
  } else if (searchParams.get("isCatalog") !== "true") {
3741
4129
  where = mergeListWhereAnd(where, {
3742
4130
  isCatalog: false
@@ -3744,14 +4132,22 @@ function createCrudHandler(dataSource, entityMap, options) {
3744
4132
  }
3745
4133
  } else if (resource === "product_categories") {
3746
4134
  if (scope.type === "vendor") {
3747
- where = mergeVendorScopeIntoWhere(where, scope, resource);
4135
+ where = mergeVendorScopeIntoWhere(where, scope, resource, catalogFlags);
4136
+ } else if (searchParams.get("isCatalog") !== "false") {
4137
+ where = mergeListWhereAnd(where, {
4138
+ isCatalog: true
4139
+ });
4140
+ }
4141
+ } else if (resource === "brands") {
4142
+ if (scope.type === "vendor") {
4143
+ where = mergeVendorScopeIntoWhere(where, scope, resource, catalogFlags);
3748
4144
  } else if (searchParams.get("isCatalog") !== "false") {
3749
4145
  where = mergeListWhereAnd(where, {
3750
4146
  isCatalog: true
3751
4147
  });
3752
4148
  }
3753
4149
  } else {
3754
- where = mergeVendorScopeIntoWhere(where, scope, resource);
4150
+ where = mergeVendorScopeIntoWhere(where, scope, resource, catalogFlags);
3755
4151
  }
3756
4152
  }
3757
4153
  let data;
@@ -3821,16 +4217,11 @@ function createCrudHandler(dataSource, entityMap, options) {
3821
4217
  });
3822
4218
  }
3823
4219
  const body = rawPostBody;
3824
- if (vendorCannotMutateCatalogCategories(resource, scopePost)) {
3825
- return json({
3826
- error: "Categories are managed by the platform administrator"
3827
- }, {
3828
- status: 403
3829
- });
3830
- }
3831
- if (vendorCannotMutateCatalogCollections(resource, scopePost, body)) {
4220
+ const catalogFlagsPost = await getVendorCatalogCreateFlags(dataSource);
4221
+ if (vendorCannotMutateCatalogResource(resource, scopePost, catalogFlagsPost, body)) {
4222
+ const label = resource === "product_categories" ? "Categories" : resource === "collections" ? "Collections" : resource === "brands" ? "Brands" : "This resource";
3832
4223
  return json({
3833
- error: "Collections are managed by the platform administrator"
4224
+ error: `${label} are managed by the platform administrator`
3834
4225
  }, {
3835
4226
  status: 403
3836
4227
  });
@@ -4215,8 +4606,15 @@ function createCrudHandler(dataSource, entityMap, options) {
4215
4606
  }
4216
4607
  if (resource === "products") {
4217
4608
  const scopeForProduct = await resolveScope();
4609
+ const productFlags = await getVendorCatalogCreateFlags(dataSource);
4610
+ const requireApproval = await getRequireProductApproval(dataSource);
4611
+ applyVendorProductCreateApproval(persistBody, {
4612
+ isVendor: scopeForProduct.type === "vendor",
4613
+ requireApproval
4614
+ });
4218
4615
  const categoryErr = await assertCatalogCategoryId(dataSource, entityMap.product_categories, persistBody.categoryId, {
4219
- required: scopeForProduct.type === "vendor"
4616
+ required: scopeForProduct.type === "vendor",
4617
+ allowVendorId: scopeForProduct.type === "vendor" && productFlags.categories ? scopeForProduct.vendorId : void 0
4220
4618
  });
4221
4619
  if (categoryErr) return json({
4222
4620
  error: categoryErr
@@ -4225,7 +4623,9 @@ function createCrudHandler(dataSource, entityMap, options) {
4225
4623
  });
4226
4624
  if (scopeForProduct.type === "vendor") {
4227
4625
  if (entityMap.collections && persistBody.collectionId != null && persistBody.collectionId !== "") {
4228
- const collectionErr = await assertVendorOwnedCollectionForProduct(dataSource, entityMap.collections, persistBody.collectionId, persistBody.categoryId, scopeForProduct.vendorId);
4626
+ const collectionErr = await assertCollectionForVendorProduct(dataSource, entityMap.collections, persistBody.collectionId, persistBody.categoryId, scopeForProduct.vendorId, {
4627
+ allowVendorOwned: productFlags.collections
4628
+ });
4229
4629
  if (collectionErr) return json({
4230
4630
  error: collectionErr
4231
4631
  }, {
@@ -4234,6 +4634,14 @@ function createCrudHandler(dataSource, entityMap, options) {
4234
4634
  }
4235
4635
  }
4236
4636
  }
4637
+ if (resource === "events") {
4638
+ const scopeForEvent = await resolveScope();
4639
+ const requireEventApproval = await getRequireEventApproval(dataSource);
4640
+ applyVendorEventCreateApproval(persistBody, {
4641
+ isVendor: scopeForEvent.type === "vendor",
4642
+ requireApproval: requireEventApproval
4643
+ });
4644
+ }
4237
4645
  if (resource === "collections" && scopePost.type === "vendor") {
4238
4646
  const categoryErr = await assertCatalogCategoryId(dataSource, entityMap.product_categories, persistBody.categoryId, {
4239
4647
  required: true
@@ -4438,7 +4846,27 @@ function createCrudHandler(dataSource, entityMap, options) {
4438
4846
  }
4439
4847
  }
4440
4848
  }
4441
- if (!((resource === "collections" || resource === "product_categories") && persistBody.isCatalog === true)) {
4849
+ if (resource === "brands" && scopeCreate.type === "all") {
4850
+ applyPlatformCatalogBrandFields(persistBody);
4851
+ const slug = String(persistBody.slug ?? "").trim();
4852
+ if (slug) {
4853
+ const dup = await repo.findOne({
4854
+ where: {
4855
+ slug,
4856
+ isCatalog: true,
4857
+ deleted: false
4858
+ }
4859
+ });
4860
+ if (dup) {
4861
+ return json({
4862
+ error: "A catalog brand with this slug already exists"
4863
+ }, {
4864
+ status: 400
4865
+ });
4866
+ }
4867
+ }
4868
+ }
4869
+ if (!((resource === "collections" || resource === "product_categories" || resource === "brands") && persistBody.isCatalog === true)) {
4442
4870
  await tryAssignSingleVendorOnAdminCreate(resource, scopeCreate, persistBody);
4443
4871
  }
4444
4872
  const vendorIdCheck = requireVendorIdForScopedCreate(resource, persistBody, scopeCreate, repo, await vendorCreateContext(body));
@@ -4918,13 +5346,14 @@ function createCrudHandler(dataSource, entityMap, options) {
4918
5346
  }
4919
5347
  chunkUSNT2KNT_cjs.__name(createCrudHandler, "createCrudHandler");
4920
5348
  function createCrudByIdHandler(dataSource, entityMap, options) {
4921
- const { requireAuth, json, requireEntityPermission: reqPerm, getCms, getDeletedByUserId, getVendorScope } = options;
5349
+ const { requireAuth, json, requireEntityPermission: reqPerm, getCms, getDeletedByUserId, getVendorScope, getHydratedSessionUser } = options;
4922
5350
  const syncContactRowToErp = makeContactErpSync(dataSource, entityMap, getCms);
4923
5351
  const resolveScopeById = getVendorScope ?? (async () => ({
4924
5352
  type: "all"
4925
5353
  }));
4926
- function vendorScopeAccessJson(row, scope, resource) {
4927
- const access = vendorScopeRowAccess(row, scope, resource);
5354
+ async function vendorScopeAccessJson(row, scope, resource) {
5355
+ const flags = await getVendorCatalogCreateFlags(dataSource);
5356
+ const access = vendorScopeRowAccess(row, scope, resource, flags);
4928
5357
  if (access === "ok") return null;
4929
5358
  if (access === "forbidden") return json({
4930
5359
  error: "Forbidden"
@@ -5004,7 +5433,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5004
5433
  "rules"
5005
5434
  ]
5006
5435
  });
5007
- const discountDenied = vendorScopeAccessJson(discount, scope2, resource);
5436
+ const discountDenied = await vendorScopeAccessJson(discount, scope2, resource);
5008
5437
  if (discountDenied) return discountDenied;
5009
5438
  const flatRules = discount.rules ?? [];
5010
5439
  const nestedRules = nestDiscountRules2(flatRules);
@@ -5062,7 +5491,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5062
5491
  "payments"
5063
5492
  ]
5064
5493
  });
5065
- const orderDenied = vendorScopeAccessJson(order, scope2, resource);
5494
+ const orderDenied = await vendorScopeAccessJson(order, scope2, resource);
5066
5495
  if (orderDenied) return orderDenied;
5067
5496
  const relatedOrders = await repo.find({
5068
5497
  where: {
@@ -5126,7 +5555,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5126
5555
  "contact"
5127
5556
  ]
5128
5557
  });
5129
- const paymentDenied = vendorScopeAccessJson(payment, scope2, resource);
5558
+ const paymentDenied = await vendorScopeAccessJson(payment, scope2, resource);
5130
5559
  if (paymentDenied) return paymentDenied;
5131
5560
  const p = payment;
5132
5561
  const order = p.order;
@@ -5165,7 +5594,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5165
5594
  "attributes.attribute"
5166
5595
  ]
5167
5596
  });
5168
- const productDenied = vendorScopeAccessJson(product, scope2, resource);
5597
+ const productDenied = await vendorScopeAccessJson(product, scope2, resource);
5169
5598
  if (productDenied) return productDenied;
5170
5599
  return product ? json(hydrateProductDisplayName(product)) : json({
5171
5600
  message: "Not found"
@@ -5183,7 +5612,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5183
5612
  "contact"
5184
5613
  ]
5185
5614
  });
5186
- const vcDenied = vendorScopeAccessJson(row, scope2, resource);
5615
+ const vcDenied = await vendorScopeAccessJson(row, scope2, resource);
5187
5616
  if (vcDenied) return vcDenied;
5188
5617
  const contact = row.contact;
5189
5618
  return json({
@@ -5206,7 +5635,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5206
5635
  "items.product"
5207
5636
  ]
5208
5637
  });
5209
- const comboDenied = vendorScopeAccessJson(combo, scope2, resource);
5638
+ const comboDenied = await vendorScopeAccessJson(combo, scope2, resource);
5210
5639
  if (comboDenied) return comboDenied;
5211
5640
  return combo ? json(combo) : json({
5212
5641
  message: "Not found"
@@ -5243,7 +5672,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5243
5672
  where: idWhere
5244
5673
  });
5245
5674
  if (resourceUsesVendorScope(resource)) {
5246
- const itemDenied = vendorScopeAccessJson(item, scope, resource);
5675
+ const itemDenied = await vendorScopeAccessJson(item, scope, resource);
5247
5676
  if (itemDenied) return itemDenied;
5248
5677
  } else if (!item) {
5249
5678
  return json({
@@ -5272,16 +5701,11 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5272
5701
  }
5273
5702
  const rawBody = await req.json();
5274
5703
  const scopePutEarly = await resolveScopeById();
5275
- if (vendorCannotMutateCatalogCategories(resource, scopePutEarly)) {
5704
+ const catalogFlagsPut = await getVendorCatalogCreateFlags(dataSource);
5705
+ if (vendorCannotMutateCatalogResource(resource, scopePutEarly, catalogFlagsPut, rawBody ?? void 0)) {
5706
+ const label = resource === "product_categories" ? "Categories" : resource === "collections" ? "Collections" : resource === "brands" ? "Brands" : "This resource";
5276
5707
  return json({
5277
- error: "Categories are managed by the platform administrator"
5278
- }, {
5279
- status: 403
5280
- });
5281
- }
5282
- if (vendorCannotMutateCatalogCollections(resource, scopePutEarly, rawBody ?? void 0)) {
5283
- return json({
5284
- error: "Collections are managed by the platform administrator"
5708
+ error: `${label} are managed by the platform administrator`
5285
5709
  }, {
5286
5710
  status: 403
5287
5711
  });
@@ -5647,7 +6071,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5647
6071
  id: numericId
5648
6072
  }
5649
6073
  });
5650
- const discountPutDenied = vendorScopeAccessJson(existing, scopePut2, resource);
6074
+ const discountPutDenied = await vendorScopeAccessJson(existing, scopePut2, resource);
5651
6075
  if (discountPutDenied) return discountPutDenied;
5652
6076
  const updatePayload2 = pickColumnUpdates(repo, rawBody);
5653
6077
  delete updatePayload2.vendorId;
@@ -5705,8 +6129,16 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5705
6129
  const existingScope = await repo.findOne({
5706
6130
  where: idWhereCheck
5707
6131
  });
5708
- const scopePutDenied = vendorScopeAccessJson(existingScope, scopePut, resource);
6132
+ const scopePutDenied = await vendorScopeAccessJson(existingScope, scopePut, resource);
5709
6133
  if (scopePutDenied) return scopePutDenied;
6134
+ if (vendorCannotMutateExistingCatalogRow(resource, scopePut, existingScope)) {
6135
+ const label = resource === "product_categories" ? "Categories" : resource === "collections" ? "Collections" : "Brands";
6136
+ return json({
6137
+ error: `${label} are managed by the platform administrator`
6138
+ }, {
6139
+ status: 403
6140
+ });
6141
+ }
5710
6142
  }
5711
6143
  const updatePayload = rawBody && typeof rawBody === "object" ? pickColumnUpdates(repo, rawBody) : {};
5712
6144
  if (resourceUsesVendorScope(resource)) {
@@ -5853,7 +6285,8 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5853
6285
  });
5854
6286
  const mergedCategoryId = "categoryId" in updatePayload ? updatePayload.categoryId : currentRow.categoryId;
5855
6287
  const categoryErr = await assertCatalogCategoryId(dataSource, entityMap.product_categories, "categoryId" in updatePayload ? updatePayload.categoryId : mergedCategoryId, {
5856
- required: scopePut.type === "vendor"
6288
+ required: scopePut.type === "vendor",
6289
+ allowVendorId: scopePut.type === "vendor" && catalogFlagsPut.categories ? scopePut.vendorId : void 0
5857
6290
  });
5858
6291
  if (categoryErr) return json({
5859
6292
  error: categoryErr
@@ -5862,7 +6295,9 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5862
6295
  });
5863
6296
  if (scopePut.type === "vendor") {
5864
6297
  if (entityMap.collections && "collectionId" in updatePayload && updatePayload.collectionId != null) {
5865
- const collectionErr = await assertVendorOwnedCollectionForProduct(dataSource, entityMap.collections, updatePayload.collectionId, mergedCategoryId, scopePut.vendorId);
6298
+ const collectionErr = await assertCollectionForVendorProduct(dataSource, entityMap.collections, updatePayload.collectionId, mergedCategoryId, scopePut.vendorId, {
6299
+ allowVendorOwned: catalogFlagsPut.collections
6300
+ });
5866
6301
  if (collectionErr) return json({
5867
6302
  error: collectionErr
5868
6303
  }, {
@@ -5870,6 +6305,88 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5870
6305
  });
5871
6306
  }
5872
6307
  }
6308
+ if ("status" in updatePayload || "approvalStatus" in updatePayload || "rejectionReason" in updatePayload) {
6309
+ const fromStatus = String(currentRow.status ?? "draft");
6310
+ const toStatus = String("status" in updatePayload ? updatePayload.status ?? fromStatus : fromStatus);
6311
+ const fromApproval = currentRow.approvalStatus;
6312
+ const toApproval = "approvalStatus" in updatePayload ? updatePayload.approvalStatus : fromApproval;
6313
+ const requireApproval = await getRequireProductApproval(dataSource);
6314
+ const reasonFromBody = "rejectionReason" in updatePayload ? updatePayload.rejectionReason : currentRow.rejectionReason;
6315
+ const reasonStr = typeof reasonFromBody === "string" ? reasonFromBody : reasonFromBody == null ? null : String(reasonFromBody);
6316
+ const transition = assertProductApprovalUpdate({
6317
+ fromApproval,
6318
+ toApproval,
6319
+ fromStatus,
6320
+ toStatus,
6321
+ isVendor: scopePut.type === "vendor",
6322
+ requireApproval,
6323
+ approvalChanged: "approvalStatus" in updatePayload,
6324
+ statusChanged: "status" in updatePayload,
6325
+ rejectionReason: reasonStr
6326
+ });
6327
+ if (!transition.ok) return json({
6328
+ error: transition.error
6329
+ }, {
6330
+ status: 400
6331
+ });
6332
+ if ("approvalStatus" in updatePayload && scopePut.type !== "vendor") {
6333
+ const session = await getHydratedSessionUser?.() ?? null;
6334
+ const rejectedByRaw = session?.id != null ? Number(session.id) : null;
6335
+ applyApprovalStatusSideEffects(updatePayload, {
6336
+ toApproval,
6337
+ currentStatus: fromStatus,
6338
+ rejectionReason: reasonStr,
6339
+ rejectedBy: Number.isFinite(rejectedByRaw) ? rejectedByRaw : null
6340
+ });
6341
+ }
6342
+ }
6343
+ }
6344
+ if (resource === "events") {
6345
+ const currentEvent = await repo.findOne({
6346
+ where: {
6347
+ id: numericId,
6348
+ deleted: false
6349
+ }
6350
+ });
6351
+ if (!currentEvent) return json({
6352
+ message: "Not found"
6353
+ }, {
6354
+ status: 404
6355
+ });
6356
+ if ("isActive" in updatePayload || "approvalStatus" in updatePayload || "rejectionReason" in updatePayload) {
6357
+ const fromActive = Boolean(currentEvent.isActive);
6358
+ const toActive = "isActive" in updatePayload ? Boolean(updatePayload.isActive) : fromActive;
6359
+ const fromApproval = currentEvent.approvalStatus;
6360
+ const toApproval = "approvalStatus" in updatePayload ? updatePayload.approvalStatus : fromApproval;
6361
+ const requireEventApproval = await getRequireEventApproval(dataSource);
6362
+ const reasonFromBody = "rejectionReason" in updatePayload ? updatePayload.rejectionReason : currentEvent.rejectionReason;
6363
+ const reasonStr = typeof reasonFromBody === "string" ? reasonFromBody : reasonFromBody == null ? null : String(reasonFromBody);
6364
+ const transition = assertEventApprovalUpdate({
6365
+ fromApproval,
6366
+ toApproval,
6367
+ toActive,
6368
+ isVendor: scopePut.type === "vendor",
6369
+ requireApproval: requireEventApproval,
6370
+ approvalChanged: "approvalStatus" in updatePayload,
6371
+ activeChanged: "isActive" in updatePayload,
6372
+ rejectionReason: reasonStr
6373
+ });
6374
+ if (!transition.ok) return json({
6375
+ error: transition.error
6376
+ }, {
6377
+ status: 400
6378
+ });
6379
+ if ("approvalStatus" in updatePayload && scopePut.type !== "vendor") {
6380
+ const session = await getHydratedSessionUser?.() ?? null;
6381
+ const rejectedByRaw = session?.id != null ? Number(session.id) : null;
6382
+ applyEventApprovalStatusSideEffects(updatePayload, {
6383
+ toApproval,
6384
+ currentActive: fromActive,
6385
+ rejectionReason: reasonStr,
6386
+ rejectedBy: Number.isFinite(rejectedByRaw) ? rejectedByRaw : null
6387
+ });
6388
+ }
6389
+ }
5873
6390
  }
5874
6391
  if (resource === "product_categories" && scopePut.type === "all") {
5875
6392
  const existingCatalogCat = await repo.findOne({
@@ -5927,6 +6444,34 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
5927
6444
  }
5928
6445
  }
5929
6446
  }
6447
+ if (resource === "brands" && scopePut.type === "all") {
6448
+ const existingCatalogBrand = await repo.findOne({
6449
+ where: {
6450
+ id: numericId,
6451
+ deleted: false
6452
+ }
6453
+ });
6454
+ if (existingCatalogBrand && existingCatalogBrand.isCatalog === true) {
6455
+ applyPlatformCatalogBrandFields(updatePayload);
6456
+ const slug = String(updatePayload.slug ?? existingCatalogBrand.slug ?? "").trim();
6457
+ if (slug && "slug" in updatePayload) {
6458
+ const dup = await repo.findOne({
6459
+ where: {
6460
+ slug,
6461
+ isCatalog: true,
6462
+ deleted: false
6463
+ }
6464
+ });
6465
+ if (dup && Number(dup.id) !== numericId) {
6466
+ return json({
6467
+ error: "A catalog brand with this slug already exists"
6468
+ }, {
6469
+ status: 400
6470
+ });
6471
+ }
6472
+ }
6473
+ }
6474
+ }
5930
6475
  if (resource === "addresses" && Object.keys(updatePayload).length > 0) {
5931
6476
  const currentRow = await repo.findOne({
5932
6477
  where: {
@@ -6082,9 +6627,11 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
6082
6627
  const authError = await authz(req, resource, "delete");
6083
6628
  if (authError) return authError;
6084
6629
  const scopeDeleteEarly = await resolveScopeById();
6085
- if (vendorCannotMutateCatalogCategories(resource, scopeDeleteEarly)) {
6630
+ const catalogFlagsDelete = await getVendorCatalogCreateFlags(dataSource);
6631
+ if (vendorCannotMutateCatalogResource(resource, scopeDeleteEarly, catalogFlagsDelete)) {
6632
+ const label = resource === "product_categories" ? "Categories" : resource === "collections" ? "Collections" : resource === "brands" ? "Brands" : "This resource";
6086
6633
  return json({
6087
- error: "Categories are managed by the platform administrator"
6634
+ error: `${label} are managed by the platform administrator`
6088
6635
  }, {
6089
6636
  status: 403
6090
6637
  });
@@ -6113,7 +6660,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
6113
6660
  }
6114
6661
  });
6115
6662
  if (resourceUsesVendorScope(resource)) {
6116
- const deleteDenied = vendorScopeAccessJson(existing, scopeDelete, resource);
6663
+ const deleteDenied = await vendorScopeAccessJson(existing, scopeDelete, resource);
6117
6664
  if (deleteDenied) return deleteDenied;
6118
6665
  } else if (!existing) {
6119
6666
  return json({
@@ -6171,7 +6718,7 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
6171
6718
  id: numericId
6172
6719
  }
6173
6720
  });
6174
- const hardDeleteDenied = vendorScopeAccessJson(existingHard, scopeDelete, resource);
6721
+ const hardDeleteDenied = await vendorScopeAccessJson(existingHard, scopeDelete, resource);
6175
6722
  if (hardDeleteDenied) return hardDeleteDenied;
6176
6723
  }
6177
6724
  const result = await repo.delete(numericId);
@@ -7649,7 +8196,7 @@ function createVendorDashboardHandler(config) {
7649
8196
  if (pe) return pe;
7650
8197
  }
7651
8198
  const user = await getSessionUser();
7652
- if (!user || !chunkWMXPKKXU_cjs.isVendorPortalUser(user)) {
8199
+ if (!user || !chunkX6UQFV4X_cjs.isVendorPortalUser(user)) {
7653
8200
  return json({
7654
8201
  error: "Forbidden"
7655
8202
  }, {
@@ -11468,7 +12015,7 @@ chunkUSNT2KNT_cjs.__name(findActiveRefundPolicyForVendor, "findActiveRefundPolic
11468
12015
  // src/api/admin-roles-handlers.ts
11469
12016
  function createAdminRolesHandlers(config) {
11470
12017
  const { dataSource, entityMap, json, getSessionUser } = config;
11471
- const baseEntities = chunkWMXPKKXU_cjs.getPermissionableEntityKeys(entityMap);
12018
+ const baseEntities = chunkX6UQFV4X_cjs.getPermissionableEntityKeys(entityMap);
11472
12019
  const allowEntities = /* @__PURE__ */ new Set([
11473
12020
  ...baseEntities,
11474
12021
  "users"
@@ -11483,7 +12030,7 @@ function createAdminRolesHandlers(config) {
11483
12030
  }, {
11484
12031
  status: 401
11485
12032
  });
11486
- if (!chunkOIGCAGH7_cjs.canManageRoles(u) && !chunkWMXPKKXU_cjs.canManageVendorRoles(u)) {
12033
+ if (!chunkZMRQ72F6_cjs.canManageRoles(u) && !chunkX6UQFV4X_cjs.canManageVendorRoles(u)) {
11487
12034
  return json({
11488
12035
  error: "Forbidden"
11489
12036
  }, {
@@ -11494,11 +12041,11 @@ function createAdminRolesHandlers(config) {
11494
12041
  }
11495
12042
  chunkUSNT2KNT_cjs.__name(gate, "gate");
11496
12043
  function isVendorRolesOnly(u) {
11497
- return chunkWMXPKKXU_cjs.canManageVendorRoles(u) && !chunkOIGCAGH7_cjs.canManageRoles(u);
12044
+ return chunkX6UQFV4X_cjs.canManageVendorRoles(u) && !chunkZMRQ72F6_cjs.canManageRoles(u);
11498
12045
  }
11499
12046
  chunkUSNT2KNT_cjs.__name(isVendorRolesOnly, "isVendorRolesOnly");
11500
12047
  const vendorRoleEntities = [
11501
- ...chunkWMXPKKXU_cjs.VENDOR_STORE_RBAC_ENTITIES,
12048
+ ...chunkX6UQFV4X_cjs.VENDOR_STORE_RBAC_ENTITIES,
11502
12049
  "dashboard",
11503
12050
  "settings"
11504
12051
  ].sort();
@@ -11655,7 +12202,7 @@ function createAdminRolesHandlers(config) {
11655
12202
  }, {
11656
12203
  status: 404
11657
12204
  });
11658
- if (chunkWMXPKKXU_cjs.isSuperAdminGroupName(g.name) && !chunkWMXPKKXU_cjs.isSuperAdminGroupName(name)) {
12205
+ if (chunkX6UQFV4X_cjs.isSuperAdminGroupName(g.name) && !chunkX6UQFV4X_cjs.isSuperAdminGroupName(name)) {
11659
12206
  return json({
11660
12207
  error: "Cannot rename the administrator group"
11661
12208
  }, {
@@ -11712,7 +12259,7 @@ function createAdminRolesHandlers(config) {
11712
12259
  }, {
11713
12260
  status: 404
11714
12261
  });
11715
- if (chunkWMXPKKXU_cjs.isSuperAdminGroupName(g.name)) return json({
12262
+ if (chunkX6UQFV4X_cjs.isSuperAdminGroupName(g.name)) return json({
11716
12263
  error: "Cannot delete the administrator group"
11717
12264
  }, {
11718
12265
  status: 400
@@ -11880,7 +12427,7 @@ chunkUSNT2KNT_cjs.__name(sendVendorOnboardEmails, "sendVendorOnboardEmails");
11880
12427
 
11881
12428
  // src/lib/seed-vendor-roles.ts
11882
12429
  async function insertRolePermissions(exec, vendorRoleId, permissions) {
11883
- for (const entity of chunkWMXPKKXU_cjs.VENDOR_RBAC_ENTITIES) {
12430
+ for (const entity of chunkX6UQFV4X_cjs.VENDOR_RBAC_ENTITIES) {
11884
12431
  const flags = permissions[entity];
11885
12432
  if (!flags) continue;
11886
12433
  await exec.query(`
@@ -11911,7 +12458,7 @@ async function seedVendorDefaultRoles(em, vendorId) {
11911
12458
  for (const r of existing) {
11912
12459
  roleIdsByName2[r.name] = Number(r.id);
11913
12460
  if (r.isOwnerRole) ownerRoleId2 = Number(r.id);
11914
- if (r.name === chunkWMXPKKXU_cjs.VENDOR_VIEWER_ROLE_NAME) viewerRoleId2 = Number(r.id);
12461
+ if (r.name === chunkX6UQFV4X_cjs.VENDOR_VIEWER_ROLE_NAME) viewerRoleId2 = Number(r.id);
11915
12462
  }
11916
12463
  return {
11917
12464
  roleIdsByName: roleIdsByName2,
@@ -11922,7 +12469,7 @@ async function seedVendorDefaultRoles(em, vendorId) {
11922
12469
  const roleIdsByName = {};
11923
12470
  let ownerRoleId = 0;
11924
12471
  let viewerRoleId = 0;
11925
- for (const def of chunkWMXPKKXU_cjs.DEFAULT_VENDOR_ROLE_DEFS) {
12472
+ for (const def of chunkX6UQFV4X_cjs.DEFAULT_VENDOR_ROLE_DEFS) {
11926
12473
  const inserted = await exec.query(`
11927
12474
  INSERT INTO "vendor_roles"
11928
12475
  ("vendorId", "name", "description", "isSystem", "isOwnerRole")
@@ -11939,7 +12486,7 @@ async function seedVendorDefaultRoles(em, vendorId) {
11939
12486
  if (!roleId) continue;
11940
12487
  roleIdsByName[def.name] = roleId;
11941
12488
  if (def.isOwnerRole) ownerRoleId = roleId;
11942
- if (def.name === chunkWMXPKKXU_cjs.VENDOR_VIEWER_ROLE_NAME) viewerRoleId = roleId;
12489
+ if (def.name === chunkX6UQFV4X_cjs.VENDOR_VIEWER_ROLE_NAME) viewerRoleId = roleId;
11943
12490
  await insertRolePermissions(exec, roleId, def.permissions);
11944
12491
  }
11945
12492
  return {
@@ -11960,7 +12507,7 @@ async function backfillVendorUserRoleIds(em, vendorId, seeded) {
11960
12507
  vendorId
11961
12508
  ]);
11962
12509
  }
11963
- const viewerId = seeded.viewerRoleId || seeded.roleIdsByName[chunkWMXPKKXU_cjs.VENDOR_VIEWER_ROLE_NAME];
12510
+ const viewerId = seeded.viewerRoleId || seeded.roleIdsByName[chunkX6UQFV4X_cjs.VENDOR_VIEWER_ROLE_NAME];
11964
12511
  if (viewerId) {
11965
12512
  await em.query(`
11966
12513
  UPDATE "vendor_users"
@@ -11986,7 +12533,7 @@ async function getDefaultStaffRoleIdForVendor(em, vendorId) {
11986
12533
  LIMIT 1
11987
12534
  `, [
11988
12535
  vendorId,
11989
- chunkWMXPKKXU_cjs.VENDOR_VIEWER_ROLE_NAME
12536
+ chunkX6UQFV4X_cjs.VENDOR_VIEWER_ROLE_NAME
11990
12537
  ]);
11991
12538
  const id = rows[0]?.id;
11992
12539
  return id != null ? Number(id) : null;
@@ -12162,7 +12709,7 @@ function createVendorOnboardHandlers(config) {
12162
12709
  }, {
12163
12710
  status: 401
12164
12711
  });
12165
- if (!chunkWMXPKKXU_cjs.isPlatformAdministrator(u)) return json({
12712
+ if (!chunkX6UQFV4X_cjs.isPlatformAdministrator(u)) return json({
12166
12713
  error: "Forbidden",
12167
12714
  reason: "admin_only"
12168
12715
  }, {
@@ -12172,7 +12719,7 @@ function createVendorOnboardHandlers(config) {
12172
12719
  }
12173
12720
  chunkUSNT2KNT_cjs.__name(gateAdmin, "gateAdmin");
12174
12721
  async function resolveActiveVendorId(u) {
12175
- const scope = chunkWMXPKKXU_cjs.resolveVendorScopeFromSessionUser(u);
12722
+ const scope = chunkX6UQFV4X_cjs.resolveVendorScopeFromSessionUser(u);
12176
12723
  if (scope.type === "vendor") return scope.vendorId;
12177
12724
  if (scope.type === "all") {
12178
12725
  const id = u.activeVendorId ?? u.vendorIds?.[0];
@@ -12188,7 +12735,7 @@ function createVendorOnboardHandlers(config) {
12188
12735
  }, {
12189
12736
  status: 401
12190
12737
  });
12191
- if (!chunkWMXPKKXU_cjs.canManageVendorTeam(u)) return json({
12738
+ if (!chunkX6UQFV4X_cjs.canManageVendorTeam(u)) return json({
12192
12739
  error: "Forbidden"
12193
12740
  }, {
12194
12741
  status: 403
@@ -12304,19 +12851,19 @@ function createVendorOnboardHandlers(config) {
12304
12851
  if (dupVendor) throw new Error("VENDOR_SLUG_EXISTS");
12305
12852
  let ownerGroup = await groupRepo.findOne({
12306
12853
  where: {
12307
- name: chunkWMXPKKXU_cjs.VENDOR_OWNER_GROUP_NAME,
12854
+ name: chunkX6UQFV4X_cjs.VENDOR_OWNER_GROUP_NAME,
12308
12855
  deleted: false
12309
12856
  }
12310
12857
  });
12311
12858
  if (!ownerGroup) {
12312
12859
  try {
12313
12860
  await groupRepo.save(groupRepo.create({
12314
- name: chunkWMXPKKXU_cjs.VENDOR_OWNER_GROUP_NAME,
12861
+ name: chunkX6UQFV4X_cjs.VENDOR_OWNER_GROUP_NAME,
12315
12862
  deleted: false
12316
12863
  }));
12317
12864
  ownerGroup = await groupRepo.findOne({
12318
12865
  where: {
12319
- name: chunkWMXPKKXU_cjs.VENDOR_OWNER_GROUP_NAME,
12866
+ name: chunkX6UQFV4X_cjs.VENDOR_OWNER_GROUP_NAME,
12320
12867
  deleted: false
12321
12868
  }
12322
12869
  });
@@ -12776,7 +13323,7 @@ function createVendorOnboardHandlers(config) {
12776
13323
  if (!groupId) {
12777
13324
  const g = await groupRepo.findOne({
12778
13325
  where: {
12779
- name: chunkWMXPKKXU_cjs.VENDOR_OWNER_GROUP_NAME,
13326
+ name: chunkX6UQFV4X_cjs.VENDOR_OWNER_GROUP_NAME,
12780
13327
  deleted: false
12781
13328
  }
12782
13329
  });
@@ -13086,10 +13633,10 @@ chunkUSNT2KNT_cjs.__name(createVendorOnboardHandlers, "createVendorOnboardHandle
13086
13633
  function createVendorRolesHandlers(config) {
13087
13634
  const { dataSource, entityMap, json, getSessionUser } = config;
13088
13635
  const vendorRoleEntities = [
13089
- ...chunkWMXPKKXU_cjs.VENDOR_RBAC_ENTITIES
13636
+ ...chunkX6UQFV4X_cjs.VENDOR_RBAC_ENTITIES
13090
13637
  ].sort();
13091
13638
  async function resolveActiveVendorId(u) {
13092
- const scope = chunkWMXPKKXU_cjs.resolveVendorScopeFromSessionUser(u);
13639
+ const scope = chunkX6UQFV4X_cjs.resolveVendorScopeFromSessionUser(u);
13093
13640
  if (scope.type === "vendor") return scope.vendorId;
13094
13641
  if (scope.type === "all") {
13095
13642
  const id = u.activeVendorId ?? u.vendorIds?.[0];
@@ -13105,7 +13652,7 @@ function createVendorRolesHandlers(config) {
13105
13652
  }, {
13106
13653
  status: 401
13107
13654
  });
13108
- if (!chunkWMXPKKXU_cjs.canManageVendorRoles(u)) return json({
13655
+ if (!chunkX6UQFV4X_cjs.canManageVendorRoles(u)) return json({
13109
13656
  error: "Forbidden"
13110
13657
  }, {
13111
13658
  status: 403
@@ -17044,6 +17591,7 @@ exports.Brand = class Brand {
17044
17591
  chunkUSNT2KNT_cjs.__name(this, "Brand");
17045
17592
  }
17046
17593
  id;
17594
+ /** Null for platform catalog brands (`isCatalog`). */
17047
17595
  vendorId;
17048
17596
  name;
17049
17597
  slug;
@@ -17051,6 +17599,8 @@ exports.Brand = class Brand {
17051
17599
  metadata;
17052
17600
  description;
17053
17601
  active;
17602
+ /** Platform-wide catalog brand — vendors pick these on products unless allowed to create their own. */
17603
+ isCatalog;
17054
17604
  sortOrder;
17055
17605
  createdAt;
17056
17606
  updatedAt;
@@ -17070,8 +17620,10 @@ _ts_decorate30([
17070
17620
  _ts_metadata30("design:type", Number)
17071
17621
  ], exports.Brand.prototype, "id", void 0);
17072
17622
  _ts_decorate30([
17073
- typeorm.Column("int"),
17074
- _ts_metadata30("design:type", Number)
17623
+ typeorm.Column("int", {
17624
+ nullable: true
17625
+ }),
17626
+ _ts_metadata30("design:type", Object)
17075
17627
  ], exports.Brand.prototype, "vendorId", void 0);
17076
17628
  _ts_decorate30([
17077
17629
  typeorm.Column("varchar"),
@@ -17105,6 +17657,12 @@ _ts_decorate30([
17105
17657
  }),
17106
17658
  _ts_metadata30("design:type", Boolean)
17107
17659
  ], exports.Brand.prototype, "active", void 0);
17660
+ _ts_decorate30([
17661
+ typeorm.Column("boolean", {
17662
+ default: false
17663
+ }),
17664
+ _ts_metadata30("design:type", Boolean)
17665
+ ], exports.Brand.prototype, "isCatalog", void 0);
17108
17666
  _ts_decorate30([
17109
17667
  typeorm.Column("int", {
17110
17668
  default: 0
@@ -17164,12 +17722,13 @@ _ts_decorate30([
17164
17722
  ], exports.Brand.prototype, "seoId", void 0);
17165
17723
  _ts_decorate30([
17166
17724
  typeorm.ManyToOne(() => exports.Vendor, {
17167
- onDelete: "RESTRICT"
17725
+ onDelete: "RESTRICT",
17726
+ nullable: true
17168
17727
  }),
17169
17728
  typeorm.JoinColumn({
17170
17729
  name: "vendorId"
17171
17730
  }),
17172
- _ts_metadata30("design:type", typeof exports.Vendor === "undefined" ? Object : exports.Vendor)
17731
+ _ts_metadata30("design:type", Object)
17173
17732
  ], exports.Brand.prototype, "vendor", void 0);
17174
17733
  _ts_decorate30([
17175
17734
  typeorm.ManyToOne(() => exports.Seo, {
@@ -17453,6 +18012,14 @@ exports.Product = class Product {
17453
18012
  currencyPrices;
17454
18013
  quantity;
17455
18014
  status;
18015
+ /**
18016
+ * Separate from catalog status. Used when multi_vendor.requireProductApproval is on.
18017
+ * `approved` also forces `status = available` (live).
18018
+ */
18019
+ approvalStatus;
18020
+ rejectionReason;
18021
+ rejectedAt;
18022
+ rejectedBy;
17456
18023
  featured;
17457
18024
  metadata;
17458
18025
  createdAt;
@@ -17576,6 +18143,31 @@ _ts_decorate32([
17576
18143
  }),
17577
18144
  _ts_metadata32("design:type", String)
17578
18145
  ], exports.Product.prototype, "status", void 0);
18146
+ _ts_decorate32([
18147
+ typeorm.Column("varchar", {
18148
+ nullable: true
18149
+ }),
18150
+ _ts_metadata32("design:type", Object)
18151
+ ], exports.Product.prototype, "approvalStatus", void 0);
18152
+ _ts_decorate32([
18153
+ typeorm.Column("text", {
18154
+ nullable: true
18155
+ }),
18156
+ _ts_metadata32("design:type", Object)
18157
+ ], exports.Product.prototype, "rejectionReason", void 0);
18158
+ _ts_decorate32([
18159
+ typeorm.Column({
18160
+ type: "timestamptz",
18161
+ nullable: true
18162
+ }),
18163
+ _ts_metadata32("design:type", Object)
18164
+ ], exports.Product.prototype, "rejectedAt", void 0);
18165
+ _ts_decorate32([
18166
+ typeorm.Column("int", {
18167
+ nullable: true
18168
+ }),
18169
+ _ts_metadata32("design:type", Object)
18170
+ ], exports.Product.prototype, "rejectedBy", void 0);
17579
18171
  _ts_decorate32([
17580
18172
  typeorm.Column("boolean", {
17581
18173
  default: false
@@ -20664,6 +21256,14 @@ exports.Event = class Event {
20664
21256
  expectedSpeakers;
20665
21257
  expectedParticipants;
20666
21258
  isActive;
21259
+ /**
21260
+ * Separate from isActive. Used when events.requireEventApproval is on.
21261
+ * `approved` also forces `isActive = true` (live).
21262
+ */
21263
+ approvalStatus;
21264
+ rejectionReason;
21265
+ rejectedAt;
21266
+ rejectedBy;
20667
21267
  comingSoon;
20668
21268
  bannerImageUrl;
20669
21269
  logoUrl;
@@ -20758,6 +21358,31 @@ _ts_decorate60([
20758
21358
  }),
20759
21359
  _ts_metadata60("design:type", Boolean)
20760
21360
  ], exports.Event.prototype, "isActive", void 0);
21361
+ _ts_decorate60([
21362
+ typeorm.Column("varchar", {
21363
+ nullable: true
21364
+ }),
21365
+ _ts_metadata60("design:type", Object)
21366
+ ], exports.Event.prototype, "approvalStatus", void 0);
21367
+ _ts_decorate60([
21368
+ typeorm.Column("text", {
21369
+ nullable: true
21370
+ }),
21371
+ _ts_metadata60("design:type", Object)
21372
+ ], exports.Event.prototype, "rejectionReason", void 0);
21373
+ _ts_decorate60([
21374
+ typeorm.Column({
21375
+ type: "timestamptz",
21376
+ nullable: true
21377
+ }),
21378
+ _ts_metadata60("design:type", Object)
21379
+ ], exports.Event.prototype, "rejectedAt", void 0);
21380
+ _ts_decorate60([
21381
+ typeorm.Column("int", {
21382
+ nullable: true
21383
+ }),
21384
+ _ts_metadata60("design:type", Object)
21385
+ ], exports.Event.prototype, "rejectedBy", void 0);
20761
21386
  _ts_decorate60([
20762
21387
  typeorm.Column("boolean", {
20763
21388
  default: false
@@ -21318,18 +21943,20 @@ _ts_decorate63([
21318
21943
  ], exports.Combo.prototype, "desc", void 0);
21319
21944
  _ts_decorate63([
21320
21945
  typeorm.Column({
21321
- type: "integer"
21946
+ type: "integer",
21947
+ nullable: true
21322
21948
  }),
21323
- _ts_metadata63("design:type", Number)
21949
+ _ts_metadata63("design:type", Object)
21324
21950
  ], exports.Combo.prototype, "eventId", void 0);
21325
21951
  _ts_decorate63([
21326
21952
  typeorm.ManyToOne(() => exports.Event, {
21327
- onDelete: "CASCADE"
21953
+ onDelete: "CASCADE",
21954
+ nullable: true
21328
21955
  }),
21329
21956
  typeorm.JoinColumn({
21330
21957
  name: "eventId"
21331
21958
  }),
21332
- _ts_metadata63("design:type", typeof exports.Event === "undefined" ? Object : exports.Event)
21959
+ _ts_metadata63("design:type", Object)
21333
21960
  ], exports.Combo.prototype, "event", void 0);
21334
21961
  _ts_decorate63([
21335
21962
  typeorm.Column({
@@ -25589,7 +26216,7 @@ function createCmsApiHandler(config) {
25589
26216
  ] : baseCrudResources;
25590
26217
  const requireEntityPermissionEffective = /* @__PURE__ */ chunkUSNT2KNT_cjs.__name(async (_req, entity, action) => {
25591
26218
  if (!resolveSessionUser) {
25592
- chunkWMXPKKXU_cjs.logRbac("requireEntityPermission: getSessionUser not passed \u2014 vendor store APIs may 403", {
26219
+ chunkX6UQFV4X_cjs.logRbac("requireEntityPermission: getSessionUser not passed \u2014 vendor store APIs may 403", {
25593
26220
  entity,
25594
26221
  action,
25595
26222
  hint: "Pass getSessionUser: auth.getAuthenticatedUser from createCmsAuthBundle()"
@@ -25599,7 +26226,7 @@ function createCmsApiHandler(config) {
25599
26226
  const raw = await resolveSessionUser();
25600
26227
  const u = await hydrateVendorSessionUser(dataSource, raw);
25601
26228
  if (!u?.email) {
25602
- chunkWMXPKKXU_cjs.logRbac("requireEntityPermission: no session user", {
26229
+ chunkX6UQFV4X_cjs.logRbac("requireEntityPermission: no session user", {
25603
26230
  entity,
25604
26231
  action
25605
26232
  });
@@ -25609,13 +26236,13 @@ function createCmsApiHandler(config) {
25609
26236
  status: 401
25610
26237
  });
25611
26238
  }
25612
- const allowed = chunkOIGCAGH7_cjs.sessionHasEntityAccess(u, entity, action);
25613
- await chunkWMXPKKXU_cjs.logEntityAccessDecision(dataSource, "cmsApi.requireEntityPermission", u, entity, action, {
26239
+ const allowed = chunkZMRQ72F6_cjs.sessionHasEntityAccess(u, entity, action);
26240
+ await chunkX6UQFV4X_cjs.logEntityAccessDecision(dataSource, "cmsApi.requireEntityPermission", u, entity, action, {
25614
26241
  allowed,
25615
26242
  resolvePath: "hydrated_session"
25616
26243
  });
25617
26244
  if (allowed) return null;
25618
- const { reason } = chunkWMXPKKXU_cjs.explainSessionEntityAccess(u, entity, action);
26245
+ const { reason } = chunkX6UQFV4X_cjs.explainSessionEntityAccess(u, entity, action);
25619
26246
  return config.json({
25620
26247
  error: "Forbidden",
25621
26248
  entity,
@@ -25626,7 +26253,7 @@ function createCmsApiHandler(config) {
25626
26253
  });
25627
26254
  }
25628
26255
  if (userRequireEntityPermission) {
25629
- chunkWMXPKKXU_cjs.logRbac("requireEntityPermission: using app callback only (no getSessionUser)", {
26256
+ chunkX6UQFV4X_cjs.logRbac("requireEntityPermission: using app callback only (no getSessionUser)", {
25630
26257
  entity,
25631
26258
  action
25632
26259
  });
@@ -25713,10 +26340,10 @@ function createCmsApiHandler(config) {
25713
26340
  getVendorScope: /* @__PURE__ */ chunkUSNT2KNT_cjs.__name(async () => {
25714
26341
  const multiVendorEnabled = await checkMultiVendorEnabled(dataSource);
25715
26342
  if (!multiVendorEnabled) {
25716
- return chunkWMXPKKXU_cjs.resolveDisabledMultiVendorScope(dataSource);
26343
+ return chunkX6UQFV4X_cjs.resolveDisabledMultiVendorScope(dataSource);
25717
26344
  }
25718
26345
  const u = await hydrateVendorSessionUser(dataSource, await resolveSessionUser());
25719
- return chunkWMXPKKXU_cjs.resolveVendorScopeFromSessionUser(u ?? null);
26346
+ return chunkX6UQFV4X_cjs.resolveVendorScopeFromSessionUser(u ?? null);
25720
26347
  }, "getVendorScope"),
25721
26348
  getHydratedSessionUser: /* @__PURE__ */ chunkUSNT2KNT_cjs.__name(async () => {
25722
26349
  const u = await hydrateVendorSessionUser(dataSource, await resolveSessionUser());
@@ -26642,16 +27269,24 @@ function createCmsApiHandler(config) {
26642
27269
  if (pe) return pe;
26643
27270
  if (group === "multi_vendor") {
26644
27271
  invalidateMultiVendorCache();
27272
+ invalidateVendorCatalogCreateFlagsCache();
27273
+ invalidateRequireProductApprovalCache();
27274
+ invalidateRequireEventApprovalCache();
26645
27275
  }
26646
27276
  if (group === "events") {
26647
27277
  invalidateEventsCache();
27278
+ invalidateRequireEventApprovalCache();
26648
27279
  }
26649
27280
  const res = await settingsHandlers.PUT(req, group);
26650
27281
  if (group === "multi_vendor") {
26651
27282
  invalidateMultiVendorCache();
27283
+ invalidateVendorCatalogCreateFlagsCache();
27284
+ invalidateRequireProductApprovalCache();
27285
+ invalidateRequireEventApprovalCache();
26652
27286
  }
26653
27287
  if (group === "events") {
26654
27288
  invalidateEventsCache();
27289
+ invalidateRequireEventApprovalCache();
26655
27290
  }
26656
27291
  return res;
26657
27292
  }
@@ -26975,7 +27610,7 @@ function createCmsApiHandler(config) {
26975
27610
  let vendorId = null;
26976
27611
  if (resolveSessionUser) {
26977
27612
  const u = await hydrateVendorSessionUser(dataSource, await resolveSessionUser());
26978
- const scope = chunkWMXPKKXU_cjs.resolveVendorScopeFromSessionUser(u ?? null);
27613
+ const scope = chunkX6UQFV4X_cjs.resolveVendorScopeFromSessionUser(u ?? null);
26979
27614
  if (scope.type === "vendor") vendorId = scope.vendorId;
26980
27615
  }
26981
27616
  const result = await validateCoupon(dataSource, entityMap, couponCode, orderLinesNorm, currency, contactId, isAutomatic, discountId ?? void 0, vendorId);
@@ -27055,7 +27690,7 @@ function createCmsApiHandler(config) {
27055
27690
  let vendorId = null;
27056
27691
  if (resolveSessionUser) {
27057
27692
  const u = await hydrateVendorSessionUser(dataSource, await resolveSessionUser());
27058
- const scope = chunkWMXPKKXU_cjs.resolveVendorScopeFromSessionUser(u ?? null);
27693
+ const scope = chunkX6UQFV4X_cjs.resolveVendorScopeFromSessionUser(u ?? null);
27059
27694
  if (scope.type === "vendor") vendorId = scope.vendorId;
27060
27695
  }
27061
27696
  const orderLinesNorm = normalizeOrderLinesInput(body?.orderLines);
@@ -27669,7 +28304,7 @@ function createCmsApiHandler(config) {
27669
28304
  if (resolveSessionUser) {
27670
28305
  const raw = await resolveSessionUser();
27671
28306
  const u = await hydrateVendorSessionUser(dataSource, raw);
27672
- const scope = chunkWMXPKKXU_cjs.resolveVendorScopeFromSessionUser(u ?? null);
28307
+ const scope = chunkX6UQFV4X_cjs.resolveVendorScopeFromSessionUser(u ?? null);
27673
28308
  if (scope.type === "vendor") {
27674
28309
  scopedVendorIds = scope.vendorIds;
27675
28310
  }
@@ -30968,10 +31603,16 @@ exports.BLOG_METADATA_ENRICHER_LLM_AGENT_SLUG = BLOG_METADATA_ENRICHER_LLM_AGENT
30968
31603
  exports.BlogGeneratorService = BlogGeneratorService;
30969
31604
  exports.CMS_ENTITY_MAP = CMS_ENTITY_MAP;
30970
31605
  exports.ZIP_MIME_TYPES = ZIP_MIME_TYPES;
31606
+ exports.applyApprovalStatusSideEffects = applyApprovalStatusSideEffects;
31607
+ exports.applyEventApprovalStatusSideEffects = applyEventApprovalStatusSideEffects;
30971
31608
  exports.applyRotatingVendorInvite = applyRotatingVendorInvite;
30972
31609
  exports.applyVendorCustomersContactFilter = applyVendorCustomersContactFilter;
31610
+ exports.applyVendorEventCreateApproval = applyVendorEventCreateApproval;
31611
+ exports.applyVendorProductCreateApproval = applyVendorProductCreateApproval;
30973
31612
  exports.assertCaptchaOk = assertCaptchaOk;
30974
31613
  exports.assertContactAllowedForVendorOrder = assertContactAllowedForVendorOrder;
31614
+ exports.assertEventApprovalUpdate = assertEventApprovalUpdate;
31615
+ exports.assertProductApprovalUpdate = assertProductApprovalUpdate;
30975
31616
  exports.buildBlogMetadataUserPrompt = buildBlogMetadataUserPrompt;
30976
31617
  exports.buildCronFromSchedule = buildCronFromSchedule;
30977
31618
  exports.buildRssUserPromptFromFeeds = buildRssUserPromptFromFeeds;
@@ -31024,11 +31665,17 @@ exports.findVendorByInviteToken = findVendorByInviteToken;
31024
31665
  exports.formatTierRange = formatTierRange;
31025
31666
  exports.generateNumericOtp = generateNumericOtp;
31026
31667
  exports.getPublicSettingsGroup = getPublicSettingsGroup;
31668
+ exports.getRequireEventApproval = getRequireEventApproval;
31669
+ exports.getRequireProductApproval = getRequireProductApproval;
31027
31670
  exports.getRssArticleSummaryFromItem = getRssArticleSummaryFromItem;
31671
+ exports.getVendorCatalogCreateFlags = getVendorCatalogCreateFlags;
31028
31672
  exports.hashOtpCode = hashOtpCode;
31029
31673
  exports.hydrateVendorSessionUser = hydrateVendorSessionUser;
31030
31674
  exports.invalidateEventsCache = invalidateEventsCache;
31031
31675
  exports.invalidateMultiVendorCache = invalidateMultiVendorCache;
31676
+ exports.invalidateRequireEventApprovalCache = invalidateRequireEventApprovalCache;
31677
+ exports.invalidateRequireProductApprovalCache = invalidateRequireProductApprovalCache;
31678
+ exports.invalidateVendorCatalogCreateFlagsCache = invalidateVendorCatalogCreateFlagsCache;
31032
31679
  exports.isCustomerTypeContact = isCustomerTypeContact;
31033
31680
  exports.isZipMedia = isZipMedia;
31034
31681
  exports.linkUnclaimedContactToUser = linkUnclaimedContactToUser;