@mohasinac/appkit 4.43.0 → 4.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/server/features/categories/adapters.d.ts +47 -0
- package/dist/_internal/server/features/categories/adapters.js +235 -0
- package/dist/_internal/server/features/checkout/actions.js +161 -22
- package/dist/_internal/server/features/checkout/expand-order-items.js +34 -0
- package/dist/_internal/server/features/orders/adapters.js +21 -0
- package/dist/_internal/server/features/refunds/actions.js +1 -1
- package/dist/_internal/server/features/seo/sitemap.js +54 -15
- package/dist/_internal/server/features/stores/adapters.d.ts +1 -1
- package/dist/_internal/server/features/stores/adapters.js +6 -0
- package/dist/_internal/server/functions/scheduled.d.ts +4 -2
- package/dist/_internal/server/functions/scheduled.js +64 -6
- package/dist/_internal/server/jobs/core/adminAnalytics.d.ts +0 -1
- package/dist/_internal/server/jobs/core/adminAnalytics.js +9 -1
- package/dist/_internal/server/jobs/core/analyticsAlertEvaluate.d.ts +47 -0
- package/dist/_internal/server/jobs/core/analyticsAlertEvaluate.js +468 -0
- package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +14 -1
- package/dist/_internal/server/jobs/core/countersReconcile.js +24 -8
- package/dist/_internal/server/jobs/core/onCategoryWrite.js +54 -0
- package/dist/_internal/server/jobs/core/priceIndexRollup.d.ts +47 -0
- package/dist/_internal/server/jobs/core/priceIndexRollup.js +234 -0
- package/dist/_internal/server/jobs/core/{testerSandboxCleanup.d.ts → testDataCleanup.d.ts} +3 -3
- package/dist/_internal/server/jobs/core/{testerSandboxCleanup.js → testDataCleanup.js} +3 -3
- package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +5 -1
- package/dist/_internal/server/jobs/handlers/analyticsAlertEvaluate.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/analyticsAlertEvaluate.js +2 -0
- package/dist/_internal/server/jobs/handlers/index.d.ts +3 -1
- package/dist/_internal/server/jobs/handlers/index.js +3 -1
- package/dist/_internal/server/jobs/handlers/messages.d.ts +5 -2
- package/dist/_internal/server/jobs/handlers/messages.js +5 -2
- package/dist/_internal/server/jobs/handlers/priceIndexRollup.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/priceIndexRollup.js +2 -0
- package/dist/_internal/server/jobs/handlers/testDataCleanup.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/testDataCleanup.js +2 -0
- package/dist/_internal/shared/actions/action-registry.js +34 -0
- package/dist/_internal/shared/checkout/order-math.d.ts +113 -3
- package/dist/_internal/shared/checkout/order-math.js +194 -7
- package/dist/_internal/shared/features/categories/price-guidance.d.ts +110 -0
- package/dist/_internal/shared/features/categories/price-guidance.js +136 -0
- package/dist/_internal/shared/features/site-settings/gst-registration.d.ts +90 -0
- package/dist/_internal/shared/features/site-settings/gst-registration.js +126 -0
- package/dist/_internal/shared/fees/calculator.d.ts +105 -0
- package/dist/_internal/shared/fees/calculator.js +143 -0
- package/dist/_internal/shared/listing-types/art/config.d.ts +0 -6
- package/dist/_internal/shared/listing-types/classified/config.d.ts +0 -6
- package/dist/_internal/shared/listing-types/digital-code/config.d.ts +0 -6
- package/dist/_internal/shared/listing-types/live/config.d.ts +0 -6
- package/dist/_internal/shared/listing-types/standard/config.d.ts +0 -6
- package/dist/_internal/shared/listing-types/stickers/config.d.ts +0 -6
- package/dist/_internal/shared/templating/placeholders.d.ts +70 -0
- package/dist/_internal/shared/templating/placeholders.js +73 -0
- package/dist/client.d.ts +2 -0
- package/dist/client.js +1 -0
- package/dist/constants/api-endpoints.d.ts +42 -0
- package/dist/constants/api-endpoints.js +14 -0
- package/dist/features/admin/actions/admin-actions.d.ts +1 -1
- package/dist/features/admin/actions/admin-actions.js +1 -1
- package/dist/features/admin/components/AdminCategoryEditorView.js +269 -3
- package/dist/features/admin/components/AdminProductEditorView.js +8 -1
- package/dist/features/admin/components/AdminProductsView.js +78 -7
- package/dist/features/admin/components/AdminSiteSettingsView.js +20 -1
- package/dist/features/admin/components/AdminTaxCodeEditorView.d.ts +10 -0
- package/dist/features/admin/components/AdminTaxCodeEditorView.js +160 -0
- package/dist/features/admin/components/AdminTaxCodesView.d.ts +13 -0
- package/dist/features/admin/components/AdminTaxCodesView.js +86 -0
- package/dist/features/admin/components/AdminTesterChecklistView.js +1 -1
- package/dist/features/admin/components/analytics/AdminTopProductsTable.js +1 -1
- package/dist/features/auth/schemas/firestore.d.ts +0 -1
- package/dist/features/blog/schemas/firestore.d.ts +2 -2
- package/dist/features/catalogue/components/CatalogueItemEditorView.js +14 -3
- package/dist/features/categories/api/[id]/description-template/route.d.ts +45 -0
- package/dist/features/categories/api/[id]/description-template/route.js +252 -0
- package/dist/features/categories/api/route.js +156 -11
- package/dist/features/categories/components/CategoryDetailPageView.js +43 -10
- package/dist/features/categories/components/CategoryTemplatePicker.d.ts +65 -0
- package/dist/features/categories/components/CategoryTemplatePicker.js +170 -0
- package/dist/features/categories/constants/listing-categories.d.ts +17 -0
- package/dist/features/categories/constants/listing-categories.js +53 -0
- package/dist/features/categories/hooks/useCategoryTree.js +28 -2
- package/dist/features/categories/repository/categories.repository.d.ts +62 -11
- package/dist/features/categories/repository/categories.repository.js +178 -35
- package/dist/features/categories/repository/price-index.repository.d.ts +115 -0
- package/dist/features/categories/repository/price-index.repository.js +85 -0
- package/dist/features/categories/schemas/category-content.d.ts +206 -0
- package/dist/features/categories/schemas/category-content.js +28 -0
- package/dist/features/categories/schemas/firestore.d.ts +74 -5
- package/dist/features/categories/schemas/firestore.js +74 -3
- package/dist/features/categories/schemas/index.d.ts +1047 -0
- package/dist/features/categories/schemas/index.js +96 -0
- package/dist/features/categories/server.d.ts +1 -0
- package/dist/features/categories/server.js +7 -0
- package/dist/features/categories/types/index.d.ts +9 -0
- package/dist/features/events/schemas/firestore.d.ts +2 -2
- package/dist/features/faq/schemas/firestore.d.ts +2 -1
- package/dist/features/faq/schemas/firestore.js +20 -8
- package/dist/features/orders/schemas/firestore.d.ts +70 -0
- package/dist/features/orders/types/index.d.ts +55 -0
- package/dist/features/products/columns/index.d.ts +0 -1
- package/dist/features/products/columns/index.js +5 -1
- package/dist/features/products/components/ProductFilters.d.ts +0 -9
- package/dist/features/products/components/ProductForm.d.ts +27 -1
- package/dist/features/products/components/ProductForm.js +26 -12
- package/dist/features/products/constants/sieve.d.ts +0 -6
- package/dist/features/products/constants/sieve.js +35 -2
- package/dist/features/products/repository/products.repository.d.ts +115 -1
- package/dist/features/products/repository/products.repository.js +491 -39
- package/dist/features/products/schemas/firestore.d.ts +65 -5
- package/dist/features/products/schemas/firestore.js +9 -0
- package/dist/features/products/schemas/product-features.d.ts +140 -2
- package/dist/features/products/schemas/product-features.js +62 -1
- package/dist/features/promotions/repository/coupons.repository.js +6 -1
- package/dist/features/promotions/schemas/firestore.d.ts +44 -1
- package/dist/features/promotions/schemas/firestore.js +76 -2
- package/dist/features/seller/actions/seller-actions.js +17 -2
- package/dist/features/seller/components/PrintCenterView.d.ts +3 -2
- package/dist/features/seller/components/PrintCenterView.js +3 -2
- package/dist/features/seller/components/SellerAnalyticsAlertsView.js +21 -11
- package/dist/features/seller/components/SellerOrdersView.js +6 -1
- package/dist/features/seller/components/SellerPayoutSettingsView.js +19 -3
- package/dist/features/seller/components/SellerProductShell.d.ts +50 -2
- package/dist/features/seller/components/SellerProductShell.js +101 -9
- package/dist/features/seller/components/SellerProductsFilterDrawer.d.ts +13 -1
- package/dist/features/seller/components/SellerProductsFilterDrawer.js +2 -2
- package/dist/features/seller/components/SellerProductsView.js +108 -3
- package/dist/features/seller/types/index.d.ts +17 -0
- package/dist/features/seller/utils/product-draft-mapping.d.ts +13 -0
- package/dist/features/seller/utils/product-draft-mapping.js +17 -0
- package/dist/features/store-extensions/constants/alert-metrics.d.ts +86 -0
- package/dist/features/store-extensions/constants/alert-metrics.js +142 -0
- package/dist/features/store-extensions/schemas/firestore.d.ts +19 -1
- package/dist/features/stores/schemas/firestore.d.ts +31 -2
- package/dist/features/tax-codes/repository/tax-codes.repository.d.ts +55 -0
- package/dist/features/tax-codes/repository/tax-codes.repository.js +131 -0
- package/dist/features/tax-codes/schemas/firestore.d.ts +125 -0
- package/dist/features/tax-codes/schemas/firestore.js +82 -0
- package/dist/features/tax-codes/schemas/tax-code-form.d.ts +63 -0
- package/dist/features/tax-codes/schemas/tax-code-form.js +152 -0
- package/dist/features/tax-codes/schemas/tax-codes.validators.d.ts +107 -0
- package/dist/features/tax-codes/schemas/tax-codes.validators.js +85 -0
- package/dist/features/tester/actions/index.d.ts +0 -1
- package/dist/features/tester/actions/index.js +0 -1
- package/dist/features/tester/components/AdminTesterFeedbackIssuesView.js +1 -1
- package/dist/features/tester/components/AdminTesterFeedbackListView.js +1 -1
- package/dist/features/tester/components/index.d.ts +0 -4
- package/dist/features/tester/components/index.js +0 -2
- package/dist/features/tester/repository/tester-checklist-item.repository.d.ts +1 -20
- package/dist/features/tester/repository/tester-checklist-item.repository.js +0 -52
- package/dist/features/tester/schemas/firestore.d.ts +0 -6
- package/dist/features/tester/seed-data/authored/admin__admin-detail-round-trips.js +3 -3
- package/dist/features/tester/seed-data/authored/admin__site-system.js +4 -5
- package/dist/features/tester/seed-data/authored/content-discovery__category-counts-and-rollup.js +2 -2
- package/dist/features/tester/seed-data/authored/index.js +0 -2
- package/dist/features/tester/seed-data/authored/page-wiring__reachability.js +0 -19
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +8 -27
- package/dist/index.d.ts +21 -7
- package/dist/index.js +53 -7
- package/dist/next/routing/route-map.d.ts +2 -4
- package/dist/next/routing/route-map.js +1 -2
- package/dist/repositories/index.d.ts +1 -0
- package/dist/repositories/index.js +1 -0
- package/dist/schemas/registry.d.ts +506 -0
- package/dist/seed/_helpers/brand-rows.d.ts +35 -0
- package/dist/seed/_helpers/brand-rows.js +576 -0
- package/dist/seed/_helpers/category-content.d.ts +39 -0
- package/dist/seed/_helpers/category-content.js +634 -0
- package/dist/seed/_helpers/category-forest.d.ts +2 -0
- package/dist/seed/_helpers/category-forest.js +585 -243
- package/dist/seed/_helpers/category-models.d.ts +5 -0
- package/dist/seed/_helpers/category-models.js +1364 -0
- package/dist/seed/_helpers/category-tree.js +17 -1
- package/dist/seed/_helpers/character-features.d.ts +10 -0
- package/dist/seed/_helpers/character-features.js +118 -0
- package/dist/seed/_helpers/search-txt-wrappers.d.ts +10 -0
- package/dist/seed/_helpers/search-txt-wrappers.js +12 -1
- package/dist/seed/categories-seed-data.js +147 -169
- package/dist/seed/index.d.ts +1 -0
- package/dist/seed/index.js +1 -0
- package/dist/seed/manifest.d.ts +1 -1
- package/dist/seed/manifest.js +5 -0
- package/dist/seed/product-features-seed-data.d.ts +1 -8
- package/dist/seed/product-features-seed-data.js +358 -154
- package/dist/seed/products-art-seed-data.js +16 -4
- package/dist/seed/products-auctions-seed-data.js +6 -6
- package/dist/seed/products-classifieds-seed-data.js +17 -7
- package/dist/seed/products-digital-codes-seed-data.js +12 -12
- package/dist/seed/products-preorders-seed-data.js +8 -8
- package/dist/seed/products-prize-draws-seed-data.js +25 -8
- package/dist/seed/products-standard-seed-data.js +10 -60
- package/dist/seed/products-stickers-seed-data.js +6 -6
- package/dist/seed/site-settings-seed-data.js +24 -0
- package/dist/seed/store-extensions-seed-data.js +10 -4
- package/dist/seed/tax-codes-seed-data.d.ts +34 -0
- package/dist/seed/tax-codes-seed-data.js +100 -0
- package/dist/seed/test-data-ttl.d.ts +29 -0
- package/dist/seed/test-data-ttl.js +31 -0
- package/dist/server-entry.d.ts +1 -1
- package/dist/server-entry.js +6 -1
- package/dist/server.d.ts +2 -1
- package/dist/server.js +5 -2
- package/dist/styles.css +40 -0
- package/dist/ui/components/PaginatedSelect.d.ts +14 -0
- package/dist/ui/components/PaginatedSelect.js +0 -0
- package/dist/ui/components/PaginatedSelect.style.css +40 -0
- package/dist/utils/physical-location.d.ts +23 -0
- package/dist/utils/physical-location.js +52 -0
- package/dist/utils/search-txt-builders.d.ts +20 -0
- package/dist/utils/search-txt-builders.js +36 -0
- package/package.json +1 -1
- package/scripts/seed-cli.mjs +4 -1
- package/dist/_internal/server/jobs/handlers/testerSandboxCleanup.d.ts +0 -2
- package/dist/_internal/server/jobs/handlers/testerSandboxCleanup.js +0 -2
- package/dist/features/tester/actions/leaderboard-actions.d.ts +0 -3
- package/dist/features/tester/actions/leaderboard-actions.js +0 -5
- package/dist/features/tester/components/BugHunterLeaderboardView.d.ts +0 -23
- package/dist/features/tester/components/BugHunterLeaderboardView.js +0 -18
- package/dist/features/tester/components/TesterHubView.d.ts +0 -5
- package/dist/features/tester/components/TesterHubView.js +0 -110
- package/dist/features/tester/seed-data/authored/public-pages__bug-hunters.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/public-pages__bug-hunters.js +0 -78
- package/dist/features/tester/seed-data/tester-ttl.d.ts +0 -4
- package/dist/features/tester/seed-data/tester-ttl.js +0 -6
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { normalizeError } from "../../../../errors/normalize";
|
|
2
2
|
import { getAdminDb } from "../../../../providers/db-firebase";
|
|
3
3
|
import { ROUTES } from "../../../../next/routing/route-map";
|
|
4
|
-
import { PRODUCT_COLLECTION } from "
|
|
4
|
+
import { PRODUCT_COLLECTION } from "../../../../features/products/schemas/firestore";
|
|
5
5
|
import { EVENTS_COLLECTION, EVENT_FIELDS } from "../../../../features/events";
|
|
6
6
|
import { BLOG_POSTS_COLLECTION, BLOG_POST_FIELDS } from "../../../../features/blog";
|
|
7
7
|
import { CATEGORIES_COLLECTION, CATEGORY_FIELDS } from "../../../../features/categories";
|
|
8
|
+
// Imported from the DEFINING module, not the feature barrel (Root Cause #18).
|
|
9
|
+
import { isListingCategory } from "../../../../features/categories/constants/listing-categories";
|
|
8
10
|
import { STORE_COLLECTION, STORE_FIELDS } from "../../../../features/stores";
|
|
9
11
|
import { SCAMMER_COLLECTION } from "../../../../features/scams/schemas/firestore";
|
|
10
12
|
import { serverLogger } from "../../../../monitoring/server-logger";
|
|
@@ -16,7 +18,7 @@ const PRODUCT_SLUG = "slug";
|
|
|
16
18
|
const PRODUCT_UPDATED_AT = "updatedAt";
|
|
17
19
|
/**
|
|
18
20
|
* Tester-sandbox fixtures must never reach the public sitemap. They are created
|
|
19
|
-
* and destroyed on a cycle by `
|
|
21
|
+
* and destroyed on a cycle by `testDataCleanup` and by each tester run's
|
|
20
22
|
* wipe/seed, so anything Google indexes from them 404s shortly after. 34 of 182 sitemap
|
|
21
23
|
* URLs were disposable test fixtures before this filter existed.
|
|
22
24
|
*
|
|
@@ -58,6 +60,47 @@ function sitemapSectionFailed(label, err) {
|
|
|
58
60
|
serverLogger.error(`sitemap: section "${label}" failed and returned 0 URLs`, { section: label, error: err });
|
|
59
61
|
return [];
|
|
60
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Read every document a query matches, in pages.
|
|
65
|
+
*
|
|
66
|
+
* 🛑 WHY: three category fetchers below carried a bare `.limit(500)` and then
|
|
67
|
+
* filtered in memory, so past 500 active rows the sitemap emitted an
|
|
68
|
+
* **unpredictable subset** — and `scripts/deploy.mjs` only asserts a section is
|
|
69
|
+
* non-ZERO, so truncation passes the deploy gate silently. The taxonomy this
|
|
70
|
+
* plan seeds takes `categories` from 58 to ~330 rows, which is still under 500
|
|
71
|
+
* today; the point is that crossing it would be invisible.
|
|
72
|
+
*
|
|
73
|
+
* `MAX_SITEMAP_DOCS` is a real ceiling, kept well under the 50,000-URL
|
|
74
|
+
* sitemap-file limit, and crossing it is LOGGED rather than swallowed — the
|
|
75
|
+
* opposite of a bare `.limit()`, which cannot tell you it truncated.
|
|
76
|
+
*
|
|
77
|
+
* An explicit `orderBy("__name__")` is required: `startAfter(docSnapshot)`
|
|
78
|
+
* needs a defined sort to page against, and a `where`-only query's implicit
|
|
79
|
+
* document order is not something to rely on across pages.
|
|
80
|
+
*/
|
|
81
|
+
const SITEMAP_PAGE_SIZE = 500;
|
|
82
|
+
const MAX_SITEMAP_DOCS = 10000;
|
|
83
|
+
async function readAllDocs(query, label) {
|
|
84
|
+
const out = [];
|
|
85
|
+
let cursor = null;
|
|
86
|
+
for (;;) {
|
|
87
|
+
let page = query.orderBy("__name__").limit(SITEMAP_PAGE_SIZE);
|
|
88
|
+
if (cursor)
|
|
89
|
+
page = page.startAfter(cursor);
|
|
90
|
+
const snap = await page.get();
|
|
91
|
+
if (snap.empty)
|
|
92
|
+
break;
|
|
93
|
+
out.push(...snap.docs);
|
|
94
|
+
if (out.length >= MAX_SITEMAP_DOCS) {
|
|
95
|
+
serverLogger.error(`sitemap: section "${label}" hit the ${MAX_SITEMAP_DOCS}-document ceiling and is TRUNCATED`, { section: label, scanned: out.length });
|
|
96
|
+
return out.slice(0, MAX_SITEMAP_DOCS);
|
|
97
|
+
}
|
|
98
|
+
if (snap.size < SITEMAP_PAGE_SIZE)
|
|
99
|
+
break;
|
|
100
|
+
cursor = snap.docs[snap.docs.length - 1];
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
61
104
|
function staticPages(baseUrl) {
|
|
62
105
|
const page = (path, changeFreq, priority) => ({ url: `${baseUrl}${path}`, lastModified: new Date(), changeFrequency: changeFreq, priority });
|
|
63
106
|
return [
|
|
@@ -250,14 +293,12 @@ async function fetchEventUrls(baseUrl) {
|
|
|
250
293
|
async function fetchCategoryTypeUrls(baseUrl, categoryType, buildUrl, label) {
|
|
251
294
|
try {
|
|
252
295
|
const db = getAdminDb();
|
|
253
|
-
const
|
|
296
|
+
const docs = await readAllDocs(db
|
|
254
297
|
.collection(CATEGORIES_COLLECTION)
|
|
255
298
|
.where(CATEGORY_FIELDS.CATEGORY_TYPE, "==", categoryType)
|
|
256
299
|
.where(CATEGORY_FIELDS.IS_ACTIVE, "==", true)
|
|
257
|
-
.select(CATEGORY_FIELDS.SLUG, CATEGORY_FIELDS.UPDATED_AT, TEST_DATA_FIELD)
|
|
258
|
-
|
|
259
|
-
.get();
|
|
260
|
-
return snap.docs
|
|
300
|
+
.select(CATEGORY_FIELDS.SLUG, CATEGORY_FIELDS.UPDATED_AT, TEST_DATA_FIELD), label);
|
|
301
|
+
return docs
|
|
261
302
|
.filter((doc) => !isTestDoc(doc.data()))
|
|
262
303
|
.map((doc) => {
|
|
263
304
|
const data = doc.data();
|
|
@@ -289,23 +330,21 @@ async function fetchCategoryTypeUrls(baseUrl, categoryType, buildUrl, label) {
|
|
|
289
330
|
* stay in the `.select()` list or it comes back undefined and nothing is
|
|
290
331
|
* rejected.
|
|
291
332
|
*/
|
|
292
|
-
const NON_LISTING_CATEGORY_TYPES = new Set(["brand", "bundle", "sublisting"]);
|
|
293
333
|
async function fetchCategoryUrls(baseUrl) {
|
|
294
334
|
try {
|
|
295
335
|
const db = getAdminDb();
|
|
296
|
-
const
|
|
336
|
+
const docs = await readAllDocs(db
|
|
297
337
|
.collection(CATEGORIES_COLLECTION)
|
|
298
338
|
.where(CATEGORY_FIELDS.IS_ACTIVE, "==", true)
|
|
299
|
-
.select(CATEGORY_FIELDS.SLUG, CATEGORY_FIELDS.UPDATED_AT, CATEGORY_FIELDS.CATEGORY_TYPE, TEST_DATA_FIELD)
|
|
300
|
-
|
|
301
|
-
.get();
|
|
302
|
-
return snap.docs
|
|
339
|
+
.select(CATEGORY_FIELDS.SLUG, CATEGORY_FIELDS.UPDATED_AT, CATEGORY_FIELDS.CATEGORY_TYPE, TEST_DATA_FIELD), "category");
|
|
340
|
+
return docs
|
|
303
341
|
.filter((doc) => {
|
|
304
342
|
const data = doc.data();
|
|
305
343
|
if (isTestDoc(data))
|
|
306
344
|
return false;
|
|
307
|
-
|
|
308
|
-
|
|
345
|
+
return isListingCategory({
|
|
346
|
+
categoryType: data[CATEGORY_FIELDS.CATEGORY_TYPE],
|
|
347
|
+
});
|
|
309
348
|
})
|
|
310
349
|
.map((doc) => {
|
|
311
350
|
const data = doc.data();
|
|
@@ -27,7 +27,7 @@ import type { StoreDetail, StoreListItem } from "../../../../features/stores/typ
|
|
|
27
27
|
*/
|
|
28
28
|
export declare const PUBLIC_STORE_FIELDS: readonly ["id", "storeSlug", "ownerId", "storeName", "storeDescription", "storeCategory", "storeLogoURL", "storeBannerURL", "status", "isPublic", "stats", "bio", "location", "website", "socialLinks", "returnPolicy", "shippingPolicy", "isVacationMode", "vacationMessage", "createdAt"];
|
|
29
29
|
/** Everything else, with the reason it stays server-side. */
|
|
30
|
-
export declare const PRIVATE_STORE_FIELDS: readonly ["searchTxt", "updatedAt", "whatsappConfig", "adminNotes", "statusHistory", "statusHistoryTruncated", "isVerified", "suspensionReason", "capabilities", "customCommissionRate", "googleReviews", "shippingConfig", "emiEnabled", "isFeatured", "vacationReturnDate", "isTestData", "testDataExpiresAt"];
|
|
30
|
+
export declare const PRIVATE_STORE_FIELDS: readonly ["searchTxt", "updatedAt", "whatsappConfig", "adminNotes", "statusHistory", "statusHistoryTruncated", "isVerified", "suspensionReason", "capabilities", "customCommissionRate", "googleReviews", "shippingConfig", "emiEnabled", "pricesIncludeGst", "isFeatured", "vacationReturnDate", "isTestData", "testDataExpiresAt"];
|
|
31
31
|
/**
|
|
32
32
|
* Accepted input: a `StoreDocument` (the common case) or a loosely-typed
|
|
33
33
|
* Firestore row. The legacy top-level counters below predate `stats` and are
|
|
@@ -46,6 +46,12 @@ export const PRIVATE_STORE_FIELDS = [
|
|
|
46
46
|
"googleReviews", // placeId is billed-API config; the RSC reads it server-side
|
|
47
47
|
"shippingConfig", // resolved server-side into per-item shipping at cart time
|
|
48
48
|
"emiEnabled", // read server-side by the checkout/product RSCs, never from a public payload
|
|
49
|
+
// A seller AUTHORING preference with no public reader by construction: the
|
|
50
|
+
// stored `price` is always tax-inclusive, so a buyer never needs to know how
|
|
51
|
+
// the seller typed it. Read only by `ProductRepository` at write time, and
|
|
52
|
+
// publishing it would invite a client to "correct" a price that is already
|
|
53
|
+
// correct.
|
|
54
|
+
"pricesIncludeGst",
|
|
49
55
|
"isFeatured", // ranking input; not part of the public store profile
|
|
50
56
|
"vacationReturnDate", // not rendered by StoreAboutView; add to PUBLIC when a reader exists
|
|
51
57
|
"isTestData", // tester-sandbox tagging; filtered by filterTestDataForViewer
|
|
@@ -7,6 +7,8 @@ export declare const couponExpiry: import("./types").ScheduledFunctionDefinition
|
|
|
7
7
|
export declare const offerExpiry: import("./types").ScheduledFunctionDefinition;
|
|
8
8
|
export declare const productStatsSync: import("./types").ScheduledFunctionDefinition;
|
|
9
9
|
export declare const revenueRollup: import("./types").ScheduledFunctionDefinition;
|
|
10
|
+
export declare const priceIndexRollup: import("./types").ScheduledFunctionDefinition;
|
|
11
|
+
export declare const analyticsAlertEvaluate: import("./types").ScheduledFunctionDefinition;
|
|
10
12
|
export declare const pageViewRollup: import("./types").ScheduledFunctionDefinition;
|
|
11
13
|
export declare const pageViewPrune: import("./types").ScheduledFunctionDefinition;
|
|
12
14
|
export declare const dailyStatusDigest: import("./types").ScheduledFunctionDefinition;
|
|
@@ -20,9 +22,9 @@ export declare const autoPayoutEligibility: import("./types").ScheduledFunctionD
|
|
|
20
22
|
export declare const cleanupRtdbEvents: import("./types").ScheduledFunctionDefinition;
|
|
21
23
|
export declare const mediaTmpCleanup: import("./types").ScheduledFunctionDefinition;
|
|
22
24
|
export declare const draftPrune: import("./types").ScheduledFunctionDefinition;
|
|
23
|
-
export declare const
|
|
25
|
+
export declare const testDataCleanup: import("./types").ScheduledFunctionDefinition;
|
|
24
26
|
export declare const prizeDrawExpiryReveal: import("./types").ScheduledFunctionDefinition;
|
|
25
27
|
export declare const bundleStockSync: import("./types").ScheduledFunctionDefinition;
|
|
26
28
|
export declare const emiInstallmentReminder: import("./types").ScheduledFunctionDefinition;
|
|
27
29
|
export declare const catalogueImageStalenessReminder: import("./types").ScheduledFunctionDefinition;
|
|
28
|
-
export declare const SCHEDULED_FUNCTIONS: readonly [import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition];
|
|
30
|
+
export declare const SCHEDULED_FUNCTIONS: readonly [import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Scheduled function definitions (Cloud Scheduler triggers).
|
|
2
2
|
// Cron syntax accepts both Firebase shorthand and standard cron expressions.
|
|
3
|
-
import { auctionSettlementHandler, autoPayoutEligibilityHandler, bundleStockSyncHandler, cartPruneHandler, cleanupRtdbEventsHandler, countersReconcileHandler, couponExpiryHandler, dailyDataCleanupHandler, draftPruneHandler, emiInstallmentReminderHandler, catalogueImageStalenessReminderHandler, mediaTmpCleanupHandler, notificationPruneHandler, offerExpiryHandler, paymentWindowTimeoutHandler, hardBanReinstatementHandler, paymentReviewAutoApproveHandler, pendingOrderTimeoutHandler, positionsReconcileHandler, prizeDrawExpiryRevealHandler, productStatsSyncHandler, revenueRollupHandler, pageViewRollupHandler, pageViewPruneHandler, dailyStatusDigestHandler, weeklyPayoutEligibilityHandler,
|
|
3
|
+
import { auctionSettlementHandler, autoPayoutEligibilityHandler, bundleStockSyncHandler, cartPruneHandler, cleanupRtdbEventsHandler, countersReconcileHandler, couponExpiryHandler, dailyDataCleanupHandler, draftPruneHandler, emiInstallmentReminderHandler, catalogueImageStalenessReminderHandler, mediaTmpCleanupHandler, notificationPruneHandler, offerExpiryHandler, paymentWindowTimeoutHandler, hardBanReinstatementHandler, paymentReviewAutoApproveHandler, pendingOrderTimeoutHandler, positionsReconcileHandler, prizeDrawExpiryRevealHandler, productStatsSyncHandler, revenueRollupHandler, priceIndexRollupHandler, analyticsAlertEvaluateHandler, pageViewRollupHandler, pageViewPruneHandler, dailyStatusDigestHandler, weeklyPayoutEligibilityHandler, testDataCleanupHandler, } from "../jobs/handlers";
|
|
4
4
|
import { defineFunction } from "./define";
|
|
5
5
|
const REGION = "asia-south1";
|
|
6
6
|
const EVERY_5_MIN = "every 5 minutes";
|
|
@@ -67,6 +67,49 @@ export const revenueRollup = defineFunction({
|
|
|
67
67
|
handler: revenueRollupHandler,
|
|
68
68
|
options: { region: REGION, timeoutSeconds: 300, memory: "256MiB", maxInstances: 1 },
|
|
69
69
|
});
|
|
70
|
+
/*
|
|
71
|
+
* The realised-price index — 04:30 UTC, i.e. **10:00 IST**, the cadence the
|
|
72
|
+
* page states as "prices as of".
|
|
73
|
+
*
|
|
74
|
+
* 🛑 Its OWN function rather than a pass inside `countersReconcile`, which
|
|
75
|
+
* already runs at its 540s ceiling. A separate job can fail without taking
|
|
76
|
+
* category metrics down with it, and the two share nothing beyond reading
|
|
77
|
+
* products.
|
|
78
|
+
*
|
|
79
|
+
* It writes `priceIndex/{leafId}` and never `categories` — see the
|
|
80
|
+
* repository's header for why that is not a preference. A job writing a price
|
|
81
|
+
* band onto the collection `onCategoryWrite` watches is Root Cause #92's exact
|
|
82
|
+
* shape (1,017,548 invocations in 24h), and `notifyRevalidate` would
|
|
83
|
+
* additionally drop each category page's ISR entry nightly on a project that
|
|
84
|
+
* was paused for exceeding ISR writes by 15.5×.
|
|
85
|
+
*/
|
|
86
|
+
export const priceIndexRollup = defineFunction({
|
|
87
|
+
name: "priceIndexRollup",
|
|
88
|
+
description: "Roll up realised sale prices per leaf category into priceIndex/{leafId} (daily 04:30 UTC = 10:00 IST).",
|
|
89
|
+
trigger: { kind: "schedule", cron: "30 4 * * *", timeZone: "UTC" },
|
|
90
|
+
handler: priceIndexRollupHandler,
|
|
91
|
+
options: { region: REGION, timeoutSeconds: 540, memory: "512MiB", maxInstances: 1 },
|
|
92
|
+
});
|
|
93
|
+
/*
|
|
94
|
+
* Evaluate the alert rules sellers and admins configured — hourly, 20 past.
|
|
95
|
+
*
|
|
96
|
+
* 🛑 This feature had a schema, a CRUD UI, two API routes, four seeded
|
|
97
|
+
* fixtures and **no evaluator anywhere**: zero references across all 59 job
|
|
98
|
+
* files, with `lastTriggeredAt` declared and written nowhere. So every alert a
|
|
99
|
+
* seller configured could never fire.
|
|
100
|
+
*
|
|
101
|
+
* Hourly rather than daily, because an alert whose point is "tell me when
|
|
102
|
+
* nothing is selling" is useless a day late — and the job's own 24h
|
|
103
|
+
* re-notify cooldown is what stops hourly evaluation becoming hourly mail.
|
|
104
|
+
* 20 past the hour keeps it clear of the dense :00/:15 band.
|
|
105
|
+
*/
|
|
106
|
+
export const analyticsAlertEvaluate = defineFunction({
|
|
107
|
+
name: "analyticsAlertEvaluate",
|
|
108
|
+
description: "Evaluate seller and admin analytics alerts against live metrics and notify on a crossing (hourly at :20).",
|
|
109
|
+
trigger: { kind: "schedule", cron: "20 * * * *", timeZone: "UTC" },
|
|
110
|
+
handler: analyticsAlertEvaluateHandler,
|
|
111
|
+
options: { region: REGION, timeoutSeconds: 300, memory: "256MiB", maxInstances: 1 },
|
|
112
|
+
});
|
|
70
113
|
/*
|
|
71
114
|
* The rollup runs BEFORE the prune, and both before the retention boundary
|
|
72
115
|
* moves. `pageViews` had neither until 2026-08-31: it wrote one document per
|
|
@@ -168,11 +211,24 @@ export const draftPrune = defineFunction({
|
|
|
168
211
|
handler: draftPruneHandler,
|
|
169
212
|
options: { region: REGION, timeoutSeconds: 300, memory: "256MiB", maxInstances: 1 },
|
|
170
213
|
});
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
214
|
+
/*
|
|
215
|
+
* 🛑 RENAMED from `testerSandboxCleanup` (2026-10-10, plan §7). The job never
|
|
216
|
+
* had anything to do with the tester HARNESS — it sweeps any document carrying
|
|
217
|
+
* `isTestData: true` past its `testDataExpiresAt`, which is a seed-data concern
|
|
218
|
+
* and outlives the human tester programme being retired in B2.
|
|
219
|
+
*
|
|
220
|
+
* 🛑 A Scheduler job rename is DELETE-AND-RECREATE on deploy, not a rename:
|
|
221
|
+
* `firebase deploy --only functions` creates `testDataCleanup` and leaves
|
|
222
|
+
* `testerSandboxCleanup` running until it is explicitly deleted. Two jobs
|
|
223
|
+
* sweeping the same documents is not redundancy — it is the two-owners race
|
|
224
|
+
* that got `testerSandboxRefresh` removed (see the note below). Delete the old
|
|
225
|
+
* function in the same deploy.
|
|
226
|
+
*/
|
|
227
|
+
export const testDataCleanup = defineFunction({
|
|
228
|
+
name: "testDataCleanup",
|
|
229
|
+
description: "Delete expired isTestData documents past their TTL (daily 05:00 UTC).",
|
|
174
230
|
trigger: { kind: "schedule", cron: "0 5 * * *", timeZone: "UTC" },
|
|
175
|
-
handler:
|
|
231
|
+
handler: testDataCleanupHandler,
|
|
176
232
|
options: { region: REGION, timeoutSeconds: 300, memory: "256MiB", maxInstances: 1 },
|
|
177
233
|
});
|
|
178
234
|
// testerSandboxRefresh (every 4h: revert fixtures to seed shape, prune extras) was
|
|
@@ -220,6 +276,8 @@ export const SCHEDULED_FUNCTIONS = [
|
|
|
220
276
|
offerExpiry,
|
|
221
277
|
productStatsSync,
|
|
222
278
|
revenueRollup,
|
|
279
|
+
priceIndexRollup,
|
|
280
|
+
analyticsAlertEvaluate,
|
|
223
281
|
pageViewRollup,
|
|
224
282
|
pageViewPrune,
|
|
225
283
|
dailyStatusDigest,
|
|
@@ -233,7 +291,7 @@ export const SCHEDULED_FUNCTIONS = [
|
|
|
233
291
|
cleanupRtdbEvents,
|
|
234
292
|
mediaTmpCleanup,
|
|
235
293
|
draftPrune,
|
|
236
|
-
|
|
294
|
+
testDataCleanup,
|
|
237
295
|
prizeDrawExpiryReveal,
|
|
238
296
|
bundleStockSync,
|
|
239
297
|
emiInstallmentReminder,
|
|
@@ -82,7 +82,15 @@ export async function runAdminAnalytics(_input, ctx) {
|
|
|
82
82
|
const topProductsWithImages = topProducts.map((p, i) => ({
|
|
83
83
|
...p,
|
|
84
84
|
mainImage: topProductDocs[i]?.mainImage ?? "",
|
|
85
|
-
|
|
85
|
+
/*
|
|
86
|
+
* 🛑 `viewCount: … ?? 0` removed 2026-10-10. The `?? 0` was the active
|
|
87
|
+
* ingredient: `viewCount` is absent from every product document, and
|
|
88
|
+
* coercing that absence to `0` made it pass `AdminTopProductsTable`'s
|
|
89
|
+
* `!= null` guard, so the table rendered a permanent "0 views".
|
|
90
|
+
*
|
|
91
|
+
* Dropping the key leaves it `undefined`, which is the truth. Real counts
|
|
92
|
+
* come from the `pageViews` collection once it is rolled up.
|
|
93
|
+
*/
|
|
86
94
|
}));
|
|
87
95
|
return {
|
|
88
96
|
summary: {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core: evaluate the alert rules sellers and admins have configured — C3b.
|
|
3
|
+
*
|
|
4
|
+
* ## 🛑 The defect this closes: there was no evaluator at all
|
|
5
|
+
*
|
|
6
|
+
* `analyticsAlerts` had a schema, a seller CRUD UI, two API routes and four
|
|
7
|
+
* seeded fixtures — and **zero references across all 59 job files and
|
|
8
|
+
* `functions/`**. `lastTriggeredAt` was declared and written nowhere. So every
|
|
9
|
+
* alert a seller configured, and every one the seed created, could never fire.
|
|
10
|
+
*
|
|
11
|
+
* CLAUDE.md overstates this feature for exactly that reason: the parts that
|
|
12
|
+
* exist look complete, and the one that was missing is the one nothing points
|
|
13
|
+
* at. It also hid the metric-vocabulary split that C3a closed — two disjoint
|
|
14
|
+
* lists of metric names cannot be noticed while nothing reads the field.
|
|
15
|
+
*
|
|
16
|
+
* ## The switch is EXHAUSTIVE over `AlertMetric`
|
|
17
|
+
*
|
|
18
|
+
* `satisfies never` on the default branch, so adding a vocabulary member
|
|
19
|
+
* without an implementation does not compile. That is the mechanism behind the
|
|
20
|
+
* vocabulary's rule — *a metric is offerable only if the evaluator computes
|
|
21
|
+
* it* — rather than a convention someone has to remember.
|
|
22
|
+
*
|
|
23
|
+
* ## Read cost, and why it is bounded
|
|
24
|
+
*
|
|
25
|
+
* Metrics are computed ONCE PER OWNER, not once per alert: a seller with four
|
|
26
|
+
* alerts on orders pays for one orders query. Each query is bounded by
|
|
27
|
+
* `QUERY_LIMIT` and every count is reported as saturated when it hits the cap,
|
|
28
|
+
* because a count that silently stopped at 500 is a threshold comparison
|
|
29
|
+
* against a number that is not the number.
|
|
30
|
+
*
|
|
31
|
+
* 🛑 It does NOT scan per-product for `low_stock`. `availableQuantity <= N` is
|
|
32
|
+
* an inequality, and pairing one with an arbitrary sort is what demands an
|
|
33
|
+
* index nobody declares (Root Cause #59) — so the window is bounded and the
|
|
34
|
+
* predicate runs in memory, the same asymmetry § Availability describes.
|
|
35
|
+
*/
|
|
36
|
+
import type { AlertOperator, AnalyticsAlertDocument } from "../../../../features/store-extensions/schemas/firestore";
|
|
37
|
+
import type { JobContext } from "../runtime/types";
|
|
38
|
+
/** Compare, with the operator the alert carries. */
|
|
39
|
+
export declare function alertFires(value: number, operator: AlertOperator, threshold: number): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Whether enough time has passed to notify again.
|
|
42
|
+
*
|
|
43
|
+
* An alert that has NEVER fired always notifies — absence of
|
|
44
|
+
* `lastTriggeredAt` means "not yet", never "recently".
|
|
45
|
+
*/
|
|
46
|
+
export declare function mayRenotify(alert: AnalyticsAlertDocument, now: Date): boolean;
|
|
47
|
+
export declare function runAnalyticsAlertEvaluate(ctx: JobContext): Promise<void>;
|