@infuro/cms-core 1.0.41 → 1.0.43

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)) {
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";
3825
4223
  return json({
3826
- error: "Categories are managed by the platform administrator"
3827
- }, {
3828
- status: 403
3829
- });
3830
- }
3831
- if (vendorCannotMutateCatalogCollections(resource, scopePost, body)) {
3832
- 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
  }, {
@@ -8088,6 +8635,12 @@ function createUploadHandler(config) {
8088
8635
  parentId
8089
8636
  });
8090
8637
  } catch (err) {
8638
+ console.error("[upload] File upload failed", {
8639
+ error: err instanceof Error ? err.message : String(err),
8640
+ stack: err instanceof Error ? err.stack : void 0,
8641
+ name: err instanceof Error ? err.name : void 0,
8642
+ metadata: err && typeof err === "object" && "$metadata" in err ? err.$metadata : void 0
8643
+ });
8091
8644
  return json({
8092
8645
  error: "File upload failed"
8093
8646
  }, {
@@ -8793,41 +9346,68 @@ function createUsersApiHandlers(config) {
8793
9346
  const page = Math.max(1, parseInt(url.searchParams.get("page") || "1", 10));
8794
9347
  const limit = Math.min(100, parseInt(url.searchParams.get("limit") || "10", 10));
8795
9348
  const skip = (page - 1) * limit;
8796
- const sortField = url.searchParams.get("sortField") || "createdAt";
9349
+ const sortFieldRaw = url.searchParams.get("sortField") || "createdAt";
9350
+ const allowedSort = /* @__PURE__ */ new Set([
9351
+ "createdAt",
9352
+ "updatedAt",
9353
+ "name",
9354
+ "email",
9355
+ "id",
9356
+ "blocked"
9357
+ ]);
9358
+ const sortField = allowedSort.has(sortFieldRaw) ? sortFieldRaw : "createdAt";
8797
9359
  const sortOrder = url.searchParams.get("sortOrder") === "desc" ? "DESC" : "ASC";
8798
- const search = url.searchParams.get("search");
8799
- const where = search ? [
8800
- {
8801
- name: typeorm.ILike(`%${search}%`),
8802
- deleted: false
8803
- },
8804
- {
8805
- email: typeorm.ILike(`%${search}%`),
8806
- deleted: false
8807
- }
8808
- ] : {
9360
+ const search = url.searchParams.get("search")?.trim() || "";
9361
+ const groupName = url.searchParams.get("groupName")?.trim() || "";
9362
+ const qb = userRepo().createQueryBuilder("u").leftJoinAndSelect("u.group", "g").where("u.deleted = :deleted", {
8809
9363
  deleted: false
8810
- };
8811
- const [data, total] = await userRepo().findAndCount({
8812
- skip,
8813
- take: limit,
8814
- order: {
8815
- [sortField]: sortOrder
8816
- },
8817
- where,
8818
- relations: [
8819
- "group"
8820
- ],
8821
- select: [
8822
- "id",
8823
- "name",
8824
- "email",
8825
- "blocked",
8826
- "createdAt",
8827
- "updatedAt",
8828
- "groupId"
8829
- ]
8830
- });
9364
+ }).orderBy(`u.${sortField}`, sortOrder).skip(skip).take(limit).select([
9365
+ "u.id",
9366
+ "u.name",
9367
+ "u.email",
9368
+ "u.blocked",
9369
+ "u.createdAt",
9370
+ "u.updatedAt",
9371
+ "u.groupId",
9372
+ "g.id",
9373
+ "g.name"
9374
+ ]);
9375
+ if (search) {
9376
+ qb.andWhere("(u.name ILIKE :search OR u.email ILIKE :search)", {
9377
+ search: `%${search}%`
9378
+ });
9379
+ }
9380
+ if (groupName) {
9381
+ if (!entityMap.user_groups) {
9382
+ return json({
9383
+ total: 0,
9384
+ page,
9385
+ limit,
9386
+ totalPages: 0,
9387
+ data: []
9388
+ });
9389
+ }
9390
+ const groupRepo = dataSource.getRepository(entityMap.user_groups);
9391
+ const group = await groupRepo.findOne({
9392
+ where: {
9393
+ name: groupName,
9394
+ deleted: false
9395
+ }
9396
+ });
9397
+ if (!group) {
9398
+ return json({
9399
+ total: 0,
9400
+ page,
9401
+ limit,
9402
+ totalPages: 0,
9403
+ data: []
9404
+ });
9405
+ }
9406
+ qb.andWhere("u.groupId = :groupId", {
9407
+ groupId: Number(group.id)
9408
+ });
9409
+ }
9410
+ const [data, total] = await qb.getManyAndCount();
8831
9411
  return json({
8832
9412
  total,
8833
9413
  page,
@@ -11468,7 +12048,7 @@ chunkUSNT2KNT_cjs.__name(findActiveRefundPolicyForVendor, "findActiveRefundPolic
11468
12048
  // src/api/admin-roles-handlers.ts
11469
12049
  function createAdminRolesHandlers(config) {
11470
12050
  const { dataSource, entityMap, json, getSessionUser } = config;
11471
- const baseEntities = chunkWMXPKKXU_cjs.getPermissionableEntityKeys(entityMap);
12051
+ const baseEntities = chunkX6UQFV4X_cjs.getPermissionableEntityKeys(entityMap);
11472
12052
  const allowEntities = /* @__PURE__ */ new Set([
11473
12053
  ...baseEntities,
11474
12054
  "users"
@@ -11483,7 +12063,7 @@ function createAdminRolesHandlers(config) {
11483
12063
  }, {
11484
12064
  status: 401
11485
12065
  });
11486
- if (!chunkOIGCAGH7_cjs.canManageRoles(u) && !chunkWMXPKKXU_cjs.canManageVendorRoles(u)) {
12066
+ if (!chunkZMRQ72F6_cjs.canManageRoles(u) && !chunkX6UQFV4X_cjs.canManageVendorRoles(u)) {
11487
12067
  return json({
11488
12068
  error: "Forbidden"
11489
12069
  }, {
@@ -11494,11 +12074,11 @@ function createAdminRolesHandlers(config) {
11494
12074
  }
11495
12075
  chunkUSNT2KNT_cjs.__name(gate, "gate");
11496
12076
  function isVendorRolesOnly(u) {
11497
- return chunkWMXPKKXU_cjs.canManageVendorRoles(u) && !chunkOIGCAGH7_cjs.canManageRoles(u);
12077
+ return chunkX6UQFV4X_cjs.canManageVendorRoles(u) && !chunkZMRQ72F6_cjs.canManageRoles(u);
11498
12078
  }
11499
12079
  chunkUSNT2KNT_cjs.__name(isVendorRolesOnly, "isVendorRolesOnly");
11500
12080
  const vendorRoleEntities = [
11501
- ...chunkWMXPKKXU_cjs.VENDOR_STORE_RBAC_ENTITIES,
12081
+ ...chunkX6UQFV4X_cjs.VENDOR_STORE_RBAC_ENTITIES,
11502
12082
  "dashboard",
11503
12083
  "settings"
11504
12084
  ].sort();
@@ -11655,7 +12235,7 @@ function createAdminRolesHandlers(config) {
11655
12235
  }, {
11656
12236
  status: 404
11657
12237
  });
11658
- if (chunkWMXPKKXU_cjs.isSuperAdminGroupName(g.name) && !chunkWMXPKKXU_cjs.isSuperAdminGroupName(name)) {
12238
+ if (chunkX6UQFV4X_cjs.isSuperAdminGroupName(g.name) && !chunkX6UQFV4X_cjs.isSuperAdminGroupName(name)) {
11659
12239
  return json({
11660
12240
  error: "Cannot rename the administrator group"
11661
12241
  }, {
@@ -11712,7 +12292,7 @@ function createAdminRolesHandlers(config) {
11712
12292
  }, {
11713
12293
  status: 404
11714
12294
  });
11715
- if (chunkWMXPKKXU_cjs.isSuperAdminGroupName(g.name)) return json({
12295
+ if (chunkX6UQFV4X_cjs.isSuperAdminGroupName(g.name)) return json({
11716
12296
  error: "Cannot delete the administrator group"
11717
12297
  }, {
11718
12298
  status: 400
@@ -11880,7 +12460,7 @@ chunkUSNT2KNT_cjs.__name(sendVendorOnboardEmails, "sendVendorOnboardEmails");
11880
12460
 
11881
12461
  // src/lib/seed-vendor-roles.ts
11882
12462
  async function insertRolePermissions(exec, vendorRoleId, permissions) {
11883
- for (const entity of chunkWMXPKKXU_cjs.VENDOR_RBAC_ENTITIES) {
12463
+ for (const entity of chunkX6UQFV4X_cjs.VENDOR_RBAC_ENTITIES) {
11884
12464
  const flags = permissions[entity];
11885
12465
  if (!flags) continue;
11886
12466
  await exec.query(`
@@ -11911,7 +12491,7 @@ async function seedVendorDefaultRoles(em, vendorId) {
11911
12491
  for (const r of existing) {
11912
12492
  roleIdsByName2[r.name] = Number(r.id);
11913
12493
  if (r.isOwnerRole) ownerRoleId2 = Number(r.id);
11914
- if (r.name === chunkWMXPKKXU_cjs.VENDOR_VIEWER_ROLE_NAME) viewerRoleId2 = Number(r.id);
12494
+ if (r.name === chunkX6UQFV4X_cjs.VENDOR_VIEWER_ROLE_NAME) viewerRoleId2 = Number(r.id);
11915
12495
  }
11916
12496
  return {
11917
12497
  roleIdsByName: roleIdsByName2,
@@ -11922,7 +12502,7 @@ async function seedVendorDefaultRoles(em, vendorId) {
11922
12502
  const roleIdsByName = {};
11923
12503
  let ownerRoleId = 0;
11924
12504
  let viewerRoleId = 0;
11925
- for (const def of chunkWMXPKKXU_cjs.DEFAULT_VENDOR_ROLE_DEFS) {
12505
+ for (const def of chunkX6UQFV4X_cjs.DEFAULT_VENDOR_ROLE_DEFS) {
11926
12506
  const inserted = await exec.query(`
11927
12507
  INSERT INTO "vendor_roles"
11928
12508
  ("vendorId", "name", "description", "isSystem", "isOwnerRole")
@@ -11939,7 +12519,7 @@ async function seedVendorDefaultRoles(em, vendorId) {
11939
12519
  if (!roleId) continue;
11940
12520
  roleIdsByName[def.name] = roleId;
11941
12521
  if (def.isOwnerRole) ownerRoleId = roleId;
11942
- if (def.name === chunkWMXPKKXU_cjs.VENDOR_VIEWER_ROLE_NAME) viewerRoleId = roleId;
12522
+ if (def.name === chunkX6UQFV4X_cjs.VENDOR_VIEWER_ROLE_NAME) viewerRoleId = roleId;
11943
12523
  await insertRolePermissions(exec, roleId, def.permissions);
11944
12524
  }
11945
12525
  return {
@@ -11960,7 +12540,7 @@ async function backfillVendorUserRoleIds(em, vendorId, seeded) {
11960
12540
  vendorId
11961
12541
  ]);
11962
12542
  }
11963
- const viewerId = seeded.viewerRoleId || seeded.roleIdsByName[chunkWMXPKKXU_cjs.VENDOR_VIEWER_ROLE_NAME];
12543
+ const viewerId = seeded.viewerRoleId || seeded.roleIdsByName[chunkX6UQFV4X_cjs.VENDOR_VIEWER_ROLE_NAME];
11964
12544
  if (viewerId) {
11965
12545
  await em.query(`
11966
12546
  UPDATE "vendor_users"
@@ -11986,7 +12566,7 @@ async function getDefaultStaffRoleIdForVendor(em, vendorId) {
11986
12566
  LIMIT 1
11987
12567
  `, [
11988
12568
  vendorId,
11989
- chunkWMXPKKXU_cjs.VENDOR_VIEWER_ROLE_NAME
12569
+ chunkX6UQFV4X_cjs.VENDOR_VIEWER_ROLE_NAME
11990
12570
  ]);
11991
12571
  const id = rows[0]?.id;
11992
12572
  return id != null ? Number(id) : null;
@@ -12162,7 +12742,7 @@ function createVendorOnboardHandlers(config) {
12162
12742
  }, {
12163
12743
  status: 401
12164
12744
  });
12165
- if (!chunkWMXPKKXU_cjs.isPlatformAdministrator(u)) return json({
12745
+ if (!chunkX6UQFV4X_cjs.isPlatformAdministrator(u)) return json({
12166
12746
  error: "Forbidden",
12167
12747
  reason: "admin_only"
12168
12748
  }, {
@@ -12172,7 +12752,7 @@ function createVendorOnboardHandlers(config) {
12172
12752
  }
12173
12753
  chunkUSNT2KNT_cjs.__name(gateAdmin, "gateAdmin");
12174
12754
  async function resolveActiveVendorId(u) {
12175
- const scope = chunkWMXPKKXU_cjs.resolveVendorScopeFromSessionUser(u);
12755
+ const scope = chunkX6UQFV4X_cjs.resolveVendorScopeFromSessionUser(u);
12176
12756
  if (scope.type === "vendor") return scope.vendorId;
12177
12757
  if (scope.type === "all") {
12178
12758
  const id = u.activeVendorId ?? u.vendorIds?.[0];
@@ -12188,7 +12768,7 @@ function createVendorOnboardHandlers(config) {
12188
12768
  }, {
12189
12769
  status: 401
12190
12770
  });
12191
- if (!chunkWMXPKKXU_cjs.canManageVendorTeam(u)) return json({
12771
+ if (!chunkX6UQFV4X_cjs.canManageVendorTeam(u)) return json({
12192
12772
  error: "Forbidden"
12193
12773
  }, {
12194
12774
  status: 403
@@ -12304,19 +12884,19 @@ function createVendorOnboardHandlers(config) {
12304
12884
  if (dupVendor) throw new Error("VENDOR_SLUG_EXISTS");
12305
12885
  let ownerGroup = await groupRepo.findOne({
12306
12886
  where: {
12307
- name: chunkWMXPKKXU_cjs.VENDOR_OWNER_GROUP_NAME,
12887
+ name: chunkX6UQFV4X_cjs.VENDOR_OWNER_GROUP_NAME,
12308
12888
  deleted: false
12309
12889
  }
12310
12890
  });
12311
12891
  if (!ownerGroup) {
12312
12892
  try {
12313
12893
  await groupRepo.save(groupRepo.create({
12314
- name: chunkWMXPKKXU_cjs.VENDOR_OWNER_GROUP_NAME,
12894
+ name: chunkX6UQFV4X_cjs.VENDOR_OWNER_GROUP_NAME,
12315
12895
  deleted: false
12316
12896
  }));
12317
12897
  ownerGroup = await groupRepo.findOne({
12318
12898
  where: {
12319
- name: chunkWMXPKKXU_cjs.VENDOR_OWNER_GROUP_NAME,
12899
+ name: chunkX6UQFV4X_cjs.VENDOR_OWNER_GROUP_NAME,
12320
12900
  deleted: false
12321
12901
  }
12322
12902
  });
@@ -12776,7 +13356,7 @@ function createVendorOnboardHandlers(config) {
12776
13356
  if (!groupId) {
12777
13357
  const g = await groupRepo.findOne({
12778
13358
  where: {
12779
- name: chunkWMXPKKXU_cjs.VENDOR_OWNER_GROUP_NAME,
13359
+ name: chunkX6UQFV4X_cjs.VENDOR_OWNER_GROUP_NAME,
12780
13360
  deleted: false
12781
13361
  }
12782
13362
  });
@@ -13086,10 +13666,10 @@ chunkUSNT2KNT_cjs.__name(createVendorOnboardHandlers, "createVendorOnboardHandle
13086
13666
  function createVendorRolesHandlers(config) {
13087
13667
  const { dataSource, entityMap, json, getSessionUser } = config;
13088
13668
  const vendorRoleEntities = [
13089
- ...chunkWMXPKKXU_cjs.VENDOR_RBAC_ENTITIES
13669
+ ...chunkX6UQFV4X_cjs.VENDOR_RBAC_ENTITIES
13090
13670
  ].sort();
13091
13671
  async function resolveActiveVendorId(u) {
13092
- const scope = chunkWMXPKKXU_cjs.resolveVendorScopeFromSessionUser(u);
13672
+ const scope = chunkX6UQFV4X_cjs.resolveVendorScopeFromSessionUser(u);
13093
13673
  if (scope.type === "vendor") return scope.vendorId;
13094
13674
  if (scope.type === "all") {
13095
13675
  const id = u.activeVendorId ?? u.vendorIds?.[0];
@@ -13105,7 +13685,7 @@ function createVendorRolesHandlers(config) {
13105
13685
  }, {
13106
13686
  status: 401
13107
13687
  });
13108
- if (!chunkWMXPKKXU_cjs.canManageVendorRoles(u)) return json({
13688
+ if (!chunkX6UQFV4X_cjs.canManageVendorRoles(u)) return json({
13109
13689
  error: "Forbidden"
13110
13690
  }, {
13111
13691
  status: 403
@@ -17044,6 +17624,7 @@ exports.Brand = class Brand {
17044
17624
  chunkUSNT2KNT_cjs.__name(this, "Brand");
17045
17625
  }
17046
17626
  id;
17627
+ /** Null for platform catalog brands (`isCatalog`). */
17047
17628
  vendorId;
17048
17629
  name;
17049
17630
  slug;
@@ -17051,6 +17632,8 @@ exports.Brand = class Brand {
17051
17632
  metadata;
17052
17633
  description;
17053
17634
  active;
17635
+ /** Platform-wide catalog brand — vendors pick these on products unless allowed to create their own. */
17636
+ isCatalog;
17054
17637
  sortOrder;
17055
17638
  createdAt;
17056
17639
  updatedAt;
@@ -17070,8 +17653,10 @@ _ts_decorate30([
17070
17653
  _ts_metadata30("design:type", Number)
17071
17654
  ], exports.Brand.prototype, "id", void 0);
17072
17655
  _ts_decorate30([
17073
- typeorm.Column("int"),
17074
- _ts_metadata30("design:type", Number)
17656
+ typeorm.Column("int", {
17657
+ nullable: true
17658
+ }),
17659
+ _ts_metadata30("design:type", Object)
17075
17660
  ], exports.Brand.prototype, "vendorId", void 0);
17076
17661
  _ts_decorate30([
17077
17662
  typeorm.Column("varchar"),
@@ -17105,6 +17690,12 @@ _ts_decorate30([
17105
17690
  }),
17106
17691
  _ts_metadata30("design:type", Boolean)
17107
17692
  ], exports.Brand.prototype, "active", void 0);
17693
+ _ts_decorate30([
17694
+ typeorm.Column("boolean", {
17695
+ default: false
17696
+ }),
17697
+ _ts_metadata30("design:type", Boolean)
17698
+ ], exports.Brand.prototype, "isCatalog", void 0);
17108
17699
  _ts_decorate30([
17109
17700
  typeorm.Column("int", {
17110
17701
  default: 0
@@ -17164,12 +17755,13 @@ _ts_decorate30([
17164
17755
  ], exports.Brand.prototype, "seoId", void 0);
17165
17756
  _ts_decorate30([
17166
17757
  typeorm.ManyToOne(() => exports.Vendor, {
17167
- onDelete: "RESTRICT"
17758
+ onDelete: "RESTRICT",
17759
+ nullable: true
17168
17760
  }),
17169
17761
  typeorm.JoinColumn({
17170
17762
  name: "vendorId"
17171
17763
  }),
17172
- _ts_metadata30("design:type", typeof exports.Vendor === "undefined" ? Object : exports.Vendor)
17764
+ _ts_metadata30("design:type", Object)
17173
17765
  ], exports.Brand.prototype, "vendor", void 0);
17174
17766
  _ts_decorate30([
17175
17767
  typeorm.ManyToOne(() => exports.Seo, {
@@ -17453,6 +18045,14 @@ exports.Product = class Product {
17453
18045
  currencyPrices;
17454
18046
  quantity;
17455
18047
  status;
18048
+ /**
18049
+ * Separate from catalog status. Used when multi_vendor.requireProductApproval is on.
18050
+ * `approved` also forces `status = available` (live).
18051
+ */
18052
+ approvalStatus;
18053
+ rejectionReason;
18054
+ rejectedAt;
18055
+ rejectedBy;
17456
18056
  featured;
17457
18057
  metadata;
17458
18058
  createdAt;
@@ -17576,6 +18176,31 @@ _ts_decorate32([
17576
18176
  }),
17577
18177
  _ts_metadata32("design:type", String)
17578
18178
  ], exports.Product.prototype, "status", void 0);
18179
+ _ts_decorate32([
18180
+ typeorm.Column("varchar", {
18181
+ nullable: true
18182
+ }),
18183
+ _ts_metadata32("design:type", Object)
18184
+ ], exports.Product.prototype, "approvalStatus", void 0);
18185
+ _ts_decorate32([
18186
+ typeorm.Column("text", {
18187
+ nullable: true
18188
+ }),
18189
+ _ts_metadata32("design:type", Object)
18190
+ ], exports.Product.prototype, "rejectionReason", void 0);
18191
+ _ts_decorate32([
18192
+ typeorm.Column({
18193
+ type: "timestamptz",
18194
+ nullable: true
18195
+ }),
18196
+ _ts_metadata32("design:type", Object)
18197
+ ], exports.Product.prototype, "rejectedAt", void 0);
18198
+ _ts_decorate32([
18199
+ typeorm.Column("int", {
18200
+ nullable: true
18201
+ }),
18202
+ _ts_metadata32("design:type", Object)
18203
+ ], exports.Product.prototype, "rejectedBy", void 0);
17579
18204
  _ts_decorate32([
17580
18205
  typeorm.Column("boolean", {
17581
18206
  default: false
@@ -20664,6 +21289,14 @@ exports.Event = class Event {
20664
21289
  expectedSpeakers;
20665
21290
  expectedParticipants;
20666
21291
  isActive;
21292
+ /**
21293
+ * Separate from isActive. Used when events.requireEventApproval is on.
21294
+ * `approved` also forces `isActive = true` (live).
21295
+ */
21296
+ approvalStatus;
21297
+ rejectionReason;
21298
+ rejectedAt;
21299
+ rejectedBy;
20667
21300
  comingSoon;
20668
21301
  bannerImageUrl;
20669
21302
  logoUrl;
@@ -20758,6 +21391,31 @@ _ts_decorate60([
20758
21391
  }),
20759
21392
  _ts_metadata60("design:type", Boolean)
20760
21393
  ], exports.Event.prototype, "isActive", void 0);
21394
+ _ts_decorate60([
21395
+ typeorm.Column("varchar", {
21396
+ nullable: true
21397
+ }),
21398
+ _ts_metadata60("design:type", Object)
21399
+ ], exports.Event.prototype, "approvalStatus", void 0);
21400
+ _ts_decorate60([
21401
+ typeorm.Column("text", {
21402
+ nullable: true
21403
+ }),
21404
+ _ts_metadata60("design:type", Object)
21405
+ ], exports.Event.prototype, "rejectionReason", void 0);
21406
+ _ts_decorate60([
21407
+ typeorm.Column({
21408
+ type: "timestamptz",
21409
+ nullable: true
21410
+ }),
21411
+ _ts_metadata60("design:type", Object)
21412
+ ], exports.Event.prototype, "rejectedAt", void 0);
21413
+ _ts_decorate60([
21414
+ typeorm.Column("int", {
21415
+ nullable: true
21416
+ }),
21417
+ _ts_metadata60("design:type", Object)
21418
+ ], exports.Event.prototype, "rejectedBy", void 0);
20761
21419
  _ts_decorate60([
20762
21420
  typeorm.Column("boolean", {
20763
21421
  default: false
@@ -21318,18 +21976,20 @@ _ts_decorate63([
21318
21976
  ], exports.Combo.prototype, "desc", void 0);
21319
21977
  _ts_decorate63([
21320
21978
  typeorm.Column({
21321
- type: "integer"
21979
+ type: "integer",
21980
+ nullable: true
21322
21981
  }),
21323
- _ts_metadata63("design:type", Number)
21982
+ _ts_metadata63("design:type", Object)
21324
21983
  ], exports.Combo.prototype, "eventId", void 0);
21325
21984
  _ts_decorate63([
21326
21985
  typeorm.ManyToOne(() => exports.Event, {
21327
- onDelete: "CASCADE"
21986
+ onDelete: "CASCADE",
21987
+ nullable: true
21328
21988
  }),
21329
21989
  typeorm.JoinColumn({
21330
21990
  name: "eventId"
21331
21991
  }),
21332
- _ts_metadata63("design:type", typeof exports.Event === "undefined" ? Object : exports.Event)
21992
+ _ts_metadata63("design:type", Object)
21333
21993
  ], exports.Combo.prototype, "event", void 0);
21334
21994
  _ts_decorate63([
21335
21995
  typeorm.Column({
@@ -25589,7 +26249,7 @@ function createCmsApiHandler(config) {
25589
26249
  ] : baseCrudResources;
25590
26250
  const requireEntityPermissionEffective = /* @__PURE__ */ chunkUSNT2KNT_cjs.__name(async (_req, entity, action) => {
25591
26251
  if (!resolveSessionUser) {
25592
- chunkWMXPKKXU_cjs.logRbac("requireEntityPermission: getSessionUser not passed \u2014 vendor store APIs may 403", {
26252
+ chunkX6UQFV4X_cjs.logRbac("requireEntityPermission: getSessionUser not passed \u2014 vendor store APIs may 403", {
25593
26253
  entity,
25594
26254
  action,
25595
26255
  hint: "Pass getSessionUser: auth.getAuthenticatedUser from createCmsAuthBundle()"
@@ -25599,7 +26259,7 @@ function createCmsApiHandler(config) {
25599
26259
  const raw = await resolveSessionUser();
25600
26260
  const u = await hydrateVendorSessionUser(dataSource, raw);
25601
26261
  if (!u?.email) {
25602
- chunkWMXPKKXU_cjs.logRbac("requireEntityPermission: no session user", {
26262
+ chunkX6UQFV4X_cjs.logRbac("requireEntityPermission: no session user", {
25603
26263
  entity,
25604
26264
  action
25605
26265
  });
@@ -25609,13 +26269,13 @@ function createCmsApiHandler(config) {
25609
26269
  status: 401
25610
26270
  });
25611
26271
  }
25612
- const allowed = chunkOIGCAGH7_cjs.sessionHasEntityAccess(u, entity, action);
25613
- await chunkWMXPKKXU_cjs.logEntityAccessDecision(dataSource, "cmsApi.requireEntityPermission", u, entity, action, {
26272
+ const allowed = chunkZMRQ72F6_cjs.sessionHasEntityAccess(u, entity, action);
26273
+ await chunkX6UQFV4X_cjs.logEntityAccessDecision(dataSource, "cmsApi.requireEntityPermission", u, entity, action, {
25614
26274
  allowed,
25615
26275
  resolvePath: "hydrated_session"
25616
26276
  });
25617
26277
  if (allowed) return null;
25618
- const { reason } = chunkWMXPKKXU_cjs.explainSessionEntityAccess(u, entity, action);
26278
+ const { reason } = chunkX6UQFV4X_cjs.explainSessionEntityAccess(u, entity, action);
25619
26279
  return config.json({
25620
26280
  error: "Forbidden",
25621
26281
  entity,
@@ -25626,7 +26286,7 @@ function createCmsApiHandler(config) {
25626
26286
  });
25627
26287
  }
25628
26288
  if (userRequireEntityPermission) {
25629
- chunkWMXPKKXU_cjs.logRbac("requireEntityPermission: using app callback only (no getSessionUser)", {
26289
+ chunkX6UQFV4X_cjs.logRbac("requireEntityPermission: using app callback only (no getSessionUser)", {
25630
26290
  entity,
25631
26291
  action
25632
26292
  });
@@ -25713,10 +26373,10 @@ function createCmsApiHandler(config) {
25713
26373
  getVendorScope: /* @__PURE__ */ chunkUSNT2KNT_cjs.__name(async () => {
25714
26374
  const multiVendorEnabled = await checkMultiVendorEnabled(dataSource);
25715
26375
  if (!multiVendorEnabled) {
25716
- return chunkWMXPKKXU_cjs.resolveDisabledMultiVendorScope(dataSource);
26376
+ return chunkX6UQFV4X_cjs.resolveDisabledMultiVendorScope(dataSource);
25717
26377
  }
25718
26378
  const u = await hydrateVendorSessionUser(dataSource, await resolveSessionUser());
25719
- return chunkWMXPKKXU_cjs.resolveVendorScopeFromSessionUser(u ?? null);
26379
+ return chunkX6UQFV4X_cjs.resolveVendorScopeFromSessionUser(u ?? null);
25720
26380
  }, "getVendorScope"),
25721
26381
  getHydratedSessionUser: /* @__PURE__ */ chunkUSNT2KNT_cjs.__name(async () => {
25722
26382
  const u = await hydrateVendorSessionUser(dataSource, await resolveSessionUser());
@@ -26642,16 +27302,24 @@ function createCmsApiHandler(config) {
26642
27302
  if (pe) return pe;
26643
27303
  if (group === "multi_vendor") {
26644
27304
  invalidateMultiVendorCache();
27305
+ invalidateVendorCatalogCreateFlagsCache();
27306
+ invalidateRequireProductApprovalCache();
27307
+ invalidateRequireEventApprovalCache();
26645
27308
  }
26646
27309
  if (group === "events") {
26647
27310
  invalidateEventsCache();
27311
+ invalidateRequireEventApprovalCache();
26648
27312
  }
26649
27313
  const res = await settingsHandlers.PUT(req, group);
26650
27314
  if (group === "multi_vendor") {
26651
27315
  invalidateMultiVendorCache();
27316
+ invalidateVendorCatalogCreateFlagsCache();
27317
+ invalidateRequireProductApprovalCache();
27318
+ invalidateRequireEventApprovalCache();
26652
27319
  }
26653
27320
  if (group === "events") {
26654
27321
  invalidateEventsCache();
27322
+ invalidateRequireEventApprovalCache();
26655
27323
  }
26656
27324
  return res;
26657
27325
  }
@@ -26975,7 +27643,7 @@ function createCmsApiHandler(config) {
26975
27643
  let vendorId = null;
26976
27644
  if (resolveSessionUser) {
26977
27645
  const u = await hydrateVendorSessionUser(dataSource, await resolveSessionUser());
26978
- const scope = chunkWMXPKKXU_cjs.resolveVendorScopeFromSessionUser(u ?? null);
27646
+ const scope = chunkX6UQFV4X_cjs.resolveVendorScopeFromSessionUser(u ?? null);
26979
27647
  if (scope.type === "vendor") vendorId = scope.vendorId;
26980
27648
  }
26981
27649
  const result = await validateCoupon(dataSource, entityMap, couponCode, orderLinesNorm, currency, contactId, isAutomatic, discountId ?? void 0, vendorId);
@@ -27055,7 +27723,7 @@ function createCmsApiHandler(config) {
27055
27723
  let vendorId = null;
27056
27724
  if (resolveSessionUser) {
27057
27725
  const u = await hydrateVendorSessionUser(dataSource, await resolveSessionUser());
27058
- const scope = chunkWMXPKKXU_cjs.resolveVendorScopeFromSessionUser(u ?? null);
27726
+ const scope = chunkX6UQFV4X_cjs.resolveVendorScopeFromSessionUser(u ?? null);
27059
27727
  if (scope.type === "vendor") vendorId = scope.vendorId;
27060
27728
  }
27061
27729
  const orderLinesNorm = normalizeOrderLinesInput(body?.orderLines);
@@ -27669,7 +28337,7 @@ function createCmsApiHandler(config) {
27669
28337
  if (resolveSessionUser) {
27670
28338
  const raw = await resolveSessionUser();
27671
28339
  const u = await hydrateVendorSessionUser(dataSource, raw);
27672
- const scope = chunkWMXPKKXU_cjs.resolveVendorScopeFromSessionUser(u ?? null);
28340
+ const scope = chunkX6UQFV4X_cjs.resolveVendorScopeFromSessionUser(u ?? null);
27673
28341
  if (scope.type === "vendor") {
27674
28342
  scopedVendorIds = scope.vendorIds;
27675
28343
  }
@@ -30968,10 +31636,16 @@ exports.BLOG_METADATA_ENRICHER_LLM_AGENT_SLUG = BLOG_METADATA_ENRICHER_LLM_AGENT
30968
31636
  exports.BlogGeneratorService = BlogGeneratorService;
30969
31637
  exports.CMS_ENTITY_MAP = CMS_ENTITY_MAP;
30970
31638
  exports.ZIP_MIME_TYPES = ZIP_MIME_TYPES;
31639
+ exports.applyApprovalStatusSideEffects = applyApprovalStatusSideEffects;
31640
+ exports.applyEventApprovalStatusSideEffects = applyEventApprovalStatusSideEffects;
30971
31641
  exports.applyRotatingVendorInvite = applyRotatingVendorInvite;
30972
31642
  exports.applyVendorCustomersContactFilter = applyVendorCustomersContactFilter;
31643
+ exports.applyVendorEventCreateApproval = applyVendorEventCreateApproval;
31644
+ exports.applyVendorProductCreateApproval = applyVendorProductCreateApproval;
30973
31645
  exports.assertCaptchaOk = assertCaptchaOk;
30974
31646
  exports.assertContactAllowedForVendorOrder = assertContactAllowedForVendorOrder;
31647
+ exports.assertEventApprovalUpdate = assertEventApprovalUpdate;
31648
+ exports.assertProductApprovalUpdate = assertProductApprovalUpdate;
30975
31649
  exports.buildBlogMetadataUserPrompt = buildBlogMetadataUserPrompt;
30976
31650
  exports.buildCronFromSchedule = buildCronFromSchedule;
30977
31651
  exports.buildRssUserPromptFromFeeds = buildRssUserPromptFromFeeds;
@@ -31024,11 +31698,17 @@ exports.findVendorByInviteToken = findVendorByInviteToken;
31024
31698
  exports.formatTierRange = formatTierRange;
31025
31699
  exports.generateNumericOtp = generateNumericOtp;
31026
31700
  exports.getPublicSettingsGroup = getPublicSettingsGroup;
31701
+ exports.getRequireEventApproval = getRequireEventApproval;
31702
+ exports.getRequireProductApproval = getRequireProductApproval;
31027
31703
  exports.getRssArticleSummaryFromItem = getRssArticleSummaryFromItem;
31704
+ exports.getVendorCatalogCreateFlags = getVendorCatalogCreateFlags;
31028
31705
  exports.hashOtpCode = hashOtpCode;
31029
31706
  exports.hydrateVendorSessionUser = hydrateVendorSessionUser;
31030
31707
  exports.invalidateEventsCache = invalidateEventsCache;
31031
31708
  exports.invalidateMultiVendorCache = invalidateMultiVendorCache;
31709
+ exports.invalidateRequireEventApprovalCache = invalidateRequireEventApprovalCache;
31710
+ exports.invalidateRequireProductApprovalCache = invalidateRequireProductApprovalCache;
31711
+ exports.invalidateVendorCatalogCreateFlagsCache = invalidateVendorCatalogCreateFlagsCache;
31032
31712
  exports.isCustomerTypeContact = isCustomerTypeContact;
31033
31713
  exports.isZipMedia = isZipMedia;
31034
31714
  exports.linkUnclaimedContactToUser = linkUnclaimedContactToUser;