@mohasinac/appkit 4.40.0 → 4.40.1

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.
@@ -381,7 +381,7 @@ export class CategoriesRepository extends BaseRepository {
381
381
  .update({ isLeaf: true, updatedAt: new Date() });
382
382
  }
383
383
  }
384
- await this.rewriteProductChains(rebuilt);
384
+ await this.rewriteProductChains(rebuilt, categoryId);
385
385
  return writes.map((w) => w.id);
386
386
  }
387
387
  /**
@@ -405,11 +405,33 @@ export class CategoriesRepository extends BaseRepository {
405
405
  * no repository in this codebase imports another feature's, because that is
406
406
  * the import chain that drags firebase-admin into a client bundle.
407
407
  */
408
- async rewriteProductChains(rebuilt) {
408
+ async rewriteProductChains(rebuilt, movedId) {
409
+ /*
410
+ * ONE query for the whole subtree, matched on `categorySlugs` — NOT N queries
411
+ * on the scalar `category`.
412
+ *
413
+ * 🛑 MEASURED 2026-09-14: `category` is unset on all 95 production products.
414
+ * It is written by deriveTaxonomy, but every product here came from the seed,
415
+ * which hand-writes `categorySlugs` only — so a `where("category","==",id)`
416
+ * query matched nothing and this rewrite silently did nothing at all. The
417
+ * array field is the one that actually exists, and matching the MOVED node
418
+ * gets the entire subtree in a single read because `categorySlugs` holds the
419
+ * full ancestor chain.
420
+ */
421
+ const snap = await this.db
422
+ .collection("products")
423
+ .where(PRODUCT_FIELDS.CATEGORY_SLUGS, "array-contains", movedId)
424
+ .get();
409
425
  const writes = [];
410
- for (const [categoryId, cat] of rebuilt) {
426
+ for (const doc of snap.docs) {
427
+ const current = doc.data().categorySlugs ?? [];
428
+ const leaf = current[0];
429
+ const cat = leaf ? rebuilt.get(leaf) : undefined;
430
+ // A product whose leaf is outside the moved subtree has nothing to re-derive.
431
+ if (!leaf || !cat)
432
+ continue;
411
433
  // Self first, root last — byte-identical to what deriveTaxonomy produces.
412
- const chain = [categoryId, ...(cat.parentIds ?? []).slice().reverse()];
434
+ const chain = [leaf, ...(cat.parentIds ?? []).slice().reverse()];
413
435
  /*
414
436
  * Byte-identical to what deriveTaxonomy produces, INCLUDING the falsy
415
437
  * filter — these two must agree, or a product's names chain would differ
@@ -420,16 +442,12 @@ export class CategoriesRepository extends BaseRepository {
420
442
  cat.name,
421
443
  ...(cat.ancestors ?? []).map((a) => a?.name).reverse(),
422
444
  ].filter((n) => !!n);
423
- const snap = await this.db
424
- .collection("products")
425
- .where(PRODUCT_FIELDS.CATEGORY, "==", categoryId)
426
- .get();
427
- for (const doc of snap.docs) {
428
- writes.push({
429
- id: doc.id,
430
- data: { categorySlugs: chain, categoryNames: names, updatedAt: new Date() },
431
- });
432
- }
445
+ if (chain.join(">") === current.join(">"))
446
+ continue; // nothing moved for this row
447
+ writes.push({
448
+ id: doc.id,
449
+ data: { categorySlugs: chain, categoryNames: names, updatedAt: new Date() },
450
+ });
433
451
  }
434
452
  if (!writes.length)
435
453
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mohasinac/appkit",
3
- "version": "4.40.0",
3
+ "version": "4.40.1",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"