@mohasinac/appkit 2.7.4 → 2.7.5

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.
@@ -0,0 +1,8 @@
1
+ export { buildRobots } from "./robots";
2
+ export type { RobotsOptions } from "./robots";
3
+ export { buildManifest } from "./manifest";
4
+ export type { ManifestOptions } from "./manifest";
5
+ export { buildSitemap } from "./sitemap";
6
+ export type { SitemapOptions } from "./sitemap";
7
+ export { buildDefaultOgImage, DEFAULT_OG_SIZE } from "./og";
8
+ export type { DefaultOgOptions } from "./og";
@@ -0,0 +1,4 @@
1
+ export { buildRobots } from "./robots";
2
+ export { buildManifest } from "./manifest";
3
+ export { buildSitemap } from "./sitemap";
4
+ export { buildDefaultOgImage, DEFAULT_OG_SIZE } from "./og";
@@ -0,0 +1,7 @@
1
+ import type { MetadataRoute } from "next";
2
+ export interface ManifestOptions {
3
+ name: string;
4
+ shortName: string;
5
+ description: string;
6
+ }
7
+ export declare function buildManifest({ name, shortName, description }: ManifestOptions): MetadataRoute.Manifest;
@@ -0,0 +1,48 @@
1
+ import { ROUTES } from "../../../../next/routing/route-map";
2
+ export function buildManifest({ name, shortName, description }) {
3
+ return {
4
+ name,
5
+ short_name: shortName,
6
+ description,
7
+ start_url: "/",
8
+ display: "standalone",
9
+ orientation: "portrait",
10
+ background_color: "#ffffff",
11
+ theme_color: "#3570fc",
12
+ categories: ["shopping", "marketplace", "business"],
13
+ lang: "en",
14
+ dir: "ltr",
15
+ prefer_related_applications: false,
16
+ icons: [
17
+ {
18
+ src: "/favicon.svg",
19
+ sizes: "any",
20
+ type: "image/svg+xml",
21
+ purpose: "any maskable",
22
+ },
23
+ ],
24
+ screenshots: [],
25
+ shortcuts: [
26
+ {
27
+ name: "Browse Products",
28
+ url: String(ROUTES.PUBLIC.PRODUCTS),
29
+ description: "Browse the latest products",
30
+ },
31
+ {
32
+ name: "Live Auctions",
33
+ url: String(ROUTES.PUBLIC.AUCTIONS),
34
+ description: "Join live auction bidding",
35
+ },
36
+ {
37
+ name: "Categories",
38
+ url: String(ROUTES.PUBLIC.CATEGORIES),
39
+ description: "Explore product categories",
40
+ },
41
+ {
42
+ name: "Stores",
43
+ url: String(ROUTES.PUBLIC.STORES),
44
+ description: "Browse seller storefronts",
45
+ },
46
+ ],
47
+ };
48
+ }
@@ -0,0 +1,11 @@
1
+ import { ImageResponse } from "next/og";
2
+ export declare const DEFAULT_OG_SIZE: {
3
+ width: number;
4
+ height: number;
5
+ };
6
+ export interface DefaultOgOptions {
7
+ siteName: string;
8
+ tagline?: string;
9
+ domain?: string;
10
+ }
11
+ export declare function buildDefaultOgImage({ siteName, tagline, domain }: DefaultOgOptions): ImageResponse;
@@ -0,0 +1,52 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ImageResponse } from "next/og";
3
+ export const DEFAULT_OG_SIZE = { width: 1200, height: 630 };
4
+ export function buildDefaultOgImage({ siteName, tagline, domain }) {
5
+ const subtitle = tagline ?? "Shop, Bid & Sell — India's Multi-Seller Marketplace";
6
+ return new ImageResponse(_jsxs("div", { style: {
7
+ width: "100%",
8
+ height: "100%",
9
+ display: "flex",
10
+ flexDirection: "column",
11
+ alignItems: "center",
12
+ justifyContent: "center",
13
+ background: "linear-gradient(135deg, #3570fc 0%, #65c408 50%, #e91e8c 100%)",
14
+ fontFamily: "sans-serif",
15
+ position: "relative",
16
+ overflow: "hidden",
17
+ }, children: [_jsx("div", { style: {
18
+ position: "absolute",
19
+ inset: 0,
20
+ opacity: 0.07,
21
+ backgroundImage: "radial-gradient(circle at 25% 25%, white 1px, transparent 0), radial-gradient(circle at 75% 75%, white 1px, transparent 0)",
22
+ backgroundSize: "48px 48px",
23
+ } }), _jsx("div", { style: {
24
+ display: "flex",
25
+ alignItems: "center",
26
+ justifyContent: "center",
27
+ width: 120,
28
+ height: 120,
29
+ borderRadius: 24,
30
+ background: "rgba(255,255,255,0.15)",
31
+ marginBottom: 32,
32
+ fontSize: 64,
33
+ }, children: "\uD83D\uDECD\uFE0F" }), _jsx("div", { style: {
34
+ fontSize: 80,
35
+ fontWeight: 800,
36
+ color: "white",
37
+ letterSpacing: "-2px",
38
+ marginBottom: 16,
39
+ textShadow: "0 4px 24px rgba(0,0,0,0.3)",
40
+ }, children: siteName }), _jsx("div", { style: {
41
+ fontSize: 32,
42
+ color: "rgba(255,255,255,0.85)",
43
+ fontWeight: 400,
44
+ letterSpacing: "0.5px",
45
+ }, children: subtitle }), domain && (_jsx("div", { style: {
46
+ position: "absolute",
47
+ bottom: 36,
48
+ fontSize: 22,
49
+ color: "rgba(255,255,255,0.6)",
50
+ letterSpacing: "1px",
51
+ }, children: domain }))] }), { ...DEFAULT_OG_SIZE });
52
+ }
@@ -0,0 +1,5 @@
1
+ import type { MetadataRoute } from "next";
2
+ export interface RobotsOptions {
3
+ siteUrl: string;
4
+ }
5
+ export declare function buildRobots({ siteUrl }: RobotsOptions): MetadataRoute.Robots;
@@ -0,0 +1,30 @@
1
+ export function buildRobots({ siteUrl }) {
2
+ return {
3
+ rules: [
4
+ {
5
+ userAgent: "*",
6
+ allow: "/",
7
+ disallow: [
8
+ "/admin/",
9
+ "/api/",
10
+ "/seller/",
11
+ "/user/",
12
+ "/auth/",
13
+ "/checkout/",
14
+ "/cart/",
15
+ "/demo/",
16
+ "/track/",
17
+ "/unauthorized/",
18
+ "/_next/",
19
+ "/profile/*/edit",
20
+ ],
21
+ },
22
+ {
23
+ userAgent: ["GPTBot", "ChatGPT-User", "Google-Extended", "CCBot"],
24
+ disallow: "/",
25
+ },
26
+ ],
27
+ sitemap: `${siteUrl}/sitemap.xml`,
28
+ host: siteUrl,
29
+ };
30
+ }
@@ -0,0 +1,5 @@
1
+ import type { MetadataRoute } from "next";
2
+ export interface SitemapOptions {
3
+ baseUrl: string;
4
+ }
5
+ export declare function buildSitemap({ baseUrl }: SitemapOptions): Promise<MetadataRoute.Sitemap>;
@@ -0,0 +1,254 @@
1
+ import { getAdminDb } from "../../../../providers/db-firebase";
2
+ import { ROUTES } from "../../../../next/routing/route-map";
3
+ import { PRODUCT_COLLECTION } from "../../../..";
4
+ import { EVENTS_COLLECTION, EVENT_FIELDS } from "../../../../features/events";
5
+ import { BLOG_POSTS_COLLECTION, BLOG_POST_FIELDS } from "../../../../features/blog";
6
+ import { CATEGORIES_COLLECTION, CATEGORY_FIELDS } from "../../../../features/categories";
7
+ import { STORE_COLLECTION, STORE_FIELDS } from "../../../../features/stores";
8
+ import { SCAMMER_COLLECTION } from "../../../../features/scams/schemas/firestore";
9
+ import { serverLogger } from "../../../../monitoring/server-logger";
10
+ // Product field strings — matches consumer field-names.ts
11
+ const PRODUCT_STATUS = "status";
12
+ const PRODUCT_STATUS_PUBLISHED = "published";
13
+ const PRODUCT_SLUG = "slug";
14
+ const PRODUCT_UPDATED_AT = "updatedAt";
15
+ function staticPages(baseUrl) {
16
+ const page = (path, changeFreq, priority) => ({ url: `${baseUrl}${path}`, lastModified: new Date(), changeFrequency: changeFreq, priority });
17
+ return [
18
+ page(String(ROUTES.HOME), "daily", 1.0),
19
+ page(String(ROUTES.PUBLIC.PRODUCTS), "hourly", 0.9),
20
+ page(String(ROUTES.PUBLIC.AUCTIONS), "hourly", 0.9),
21
+ page(String(ROUTES.PUBLIC.CATEGORIES), "weekly", 0.8),
22
+ page(String(ROUTES.PUBLIC.BLOG), "daily", 0.7),
23
+ page(String(ROUTES.PUBLIC.EVENTS), "daily", 0.7),
24
+ page(String(ROUTES.PUBLIC.SELLERS), "weekly", 0.6),
25
+ page(String(ROUTES.PUBLIC.ABOUT), "monthly", 0.5),
26
+ page(String(ROUTES.PUBLIC.CONTACT), "monthly", 0.5),
27
+ page(String(ROUTES.PUBLIC.FAQS), "monthly", 0.4),
28
+ page(String(ROUTES.PUBLIC.TERMS), "yearly", 0.3),
29
+ page(String(ROUTES.PUBLIC.PRIVACY), "yearly", 0.3),
30
+ page(String(ROUTES.PUBLIC.SECURITY), "yearly", 0.4),
31
+ page(String(ROUTES.PUBLIC.HELP), "monthly", 0.4),
32
+ page(String(ROUTES.PUBLIC.STORES), "weekly", 0.7),
33
+ page(String(ROUTES.PUBLIC.PROMOTIONS), "daily", 0.6),
34
+ page(String(ROUTES.PUBLIC.REVIEWS), "daily", 0.5),
35
+ page(String(ROUTES.PUBLIC.SELLER_GUIDE), "monthly", 0.5),
36
+ page(String(ROUTES.PUBLIC.COOKIE_POLICY), "yearly", 0.2),
37
+ page(String(ROUTES.PUBLIC.REFUND_POLICY), "yearly", 0.3),
38
+ page(String(ROUTES.PUBLIC.SHIPPING_POLICY), "yearly", 0.3),
39
+ page(String(ROUTES.PUBLIC.PRE_ORDERS), "daily", 0.7),
40
+ page(String(ROUTES.PUBLIC.FEES), "monthly", 0.4),
41
+ page(String(ROUTES.PUBLIC.HOW_AUCTIONS_WORK), "monthly", 0.4),
42
+ page(String(ROUTES.PUBLIC.HOW_PRE_ORDERS_WORK), "monthly", 0.4),
43
+ page(String(ROUTES.PUBLIC.HOW_OFFERS_WORK), "monthly", 0.4),
44
+ page(String(ROUTES.PUBLIC.HOW_CHECKOUT_WORKS), "monthly", 0.4),
45
+ page(String(ROUTES.PUBLIC.HOW_ORDERS_WORK), "monthly", 0.4),
46
+ page(String(ROUTES.PUBLIC.HOW_REVIEWS_WORK), "monthly", 0.4),
47
+ page(String(ROUTES.PUBLIC.HOW_PAYOUTS_WORK), "monthly", 0.4),
48
+ page(String(ROUTES.PUBLIC.SCAMS), "daily", 0.8),
49
+ page(String(ROUTES.PUBLIC.SCAM_TYPES), "monthly", 0.7),
50
+ page(String(ROUTES.PUBLIC.SCAM_REPORT), "monthly", 0.6),
51
+ page(String(ROUTES.PUBLIC.SCAM_FAQS), "weekly", 0.7),
52
+ ];
53
+ }
54
+ async function fetchProductUrls(baseUrl) {
55
+ try {
56
+ const db = getAdminDb();
57
+ const snap = await db
58
+ .collection(PRODUCT_COLLECTION)
59
+ .where(PRODUCT_STATUS, "==", PRODUCT_STATUS_PUBLISHED)
60
+ .where("listingType", "==", "standard")
61
+ .select(PRODUCT_SLUG, PRODUCT_UPDATED_AT)
62
+ .limit(5000)
63
+ .get();
64
+ return snap.docs.map((doc) => {
65
+ const data = doc.data();
66
+ const slug = data[PRODUCT_SLUG] ?? doc.id;
67
+ return {
68
+ url: `${baseUrl}${ROUTES.PUBLIC.PRODUCT_DETAIL(slug)}`,
69
+ lastModified: data[PRODUCT_UPDATED_AT]?.toDate?.() ?? new Date(),
70
+ changeFrequency: "daily",
71
+ priority: 0.8,
72
+ };
73
+ });
74
+ }
75
+ catch (err) {
76
+ serverLogger.warn("sitemap: failed to fetch product URLs", { error: err });
77
+ return [];
78
+ }
79
+ }
80
+ async function fetchAuctionUrls(baseUrl) {
81
+ try {
82
+ const db = getAdminDb();
83
+ const snap = await db
84
+ .collection(PRODUCT_COLLECTION)
85
+ .where(PRODUCT_STATUS, "==", PRODUCT_STATUS_PUBLISHED)
86
+ .where("listingType", "==", "auction")
87
+ .select(PRODUCT_SLUG, PRODUCT_UPDATED_AT)
88
+ .limit(2000)
89
+ .get();
90
+ return snap.docs.map((doc) => {
91
+ const data = doc.data();
92
+ const slug = data[PRODUCT_SLUG] ?? doc.id;
93
+ return {
94
+ url: `${baseUrl}${ROUTES.PUBLIC.AUCTION_DETAIL(slug)}`,
95
+ lastModified: data[PRODUCT_UPDATED_AT]?.toDate?.() ?? new Date(),
96
+ changeFrequency: "daily",
97
+ priority: 0.8,
98
+ };
99
+ });
100
+ }
101
+ catch (err) {
102
+ serverLogger.warn("sitemap: failed to fetch auction URLs", { error: err });
103
+ return [];
104
+ }
105
+ }
106
+ async function fetchEventUrls(baseUrl) {
107
+ try {
108
+ const db = getAdminDb();
109
+ const snap = await db
110
+ .collection(EVENTS_COLLECTION)
111
+ .where(EVENT_FIELDS.STATUS, "==", EVENT_FIELDS.STATUS_VALUES.ACTIVE)
112
+ .select(EVENT_FIELDS.UPDATED_AT)
113
+ .limit(500)
114
+ .get();
115
+ return snap.docs.map((doc) => {
116
+ const data = doc.data();
117
+ return {
118
+ url: `${baseUrl}${ROUTES.PUBLIC.EVENT_DETAIL(doc.id)}`,
119
+ lastModified: data[EVENT_FIELDS.UPDATED_AT]?.toDate?.() ?? new Date(),
120
+ changeFrequency: "daily",
121
+ priority: 0.7,
122
+ };
123
+ });
124
+ }
125
+ catch (err) {
126
+ serverLogger.warn("sitemap: failed to fetch event URLs", { error: err });
127
+ return [];
128
+ }
129
+ }
130
+ async function fetchCategoryUrls(baseUrl) {
131
+ try {
132
+ const db = getAdminDb();
133
+ const snap = await db
134
+ .collection(CATEGORIES_COLLECTION)
135
+ .where(CATEGORY_FIELDS.IS_ACTIVE, "==", true)
136
+ .select(CATEGORY_FIELDS.SLUG, CATEGORY_FIELDS.UPDATED_AT)
137
+ .limit(500)
138
+ .get();
139
+ return snap.docs.map((doc) => {
140
+ const data = doc.data();
141
+ const slug = data[CATEGORY_FIELDS.SLUG] ?? doc.id;
142
+ return {
143
+ url: `${baseUrl}/categories/${slug}`,
144
+ lastModified: data[CATEGORY_FIELDS.UPDATED_AT]?.toDate?.() ?? new Date(),
145
+ changeFrequency: "weekly",
146
+ priority: 0.7,
147
+ };
148
+ });
149
+ }
150
+ catch (err) {
151
+ serverLogger.warn("sitemap: failed to fetch category URLs", { error: err });
152
+ return [];
153
+ }
154
+ }
155
+ async function fetchBlogPostUrls(baseUrl) {
156
+ try {
157
+ const db = getAdminDb();
158
+ const snap = await db
159
+ .collection(BLOG_POSTS_COLLECTION)
160
+ .where(BLOG_POST_FIELDS.STATUS, "==", BLOG_POST_FIELDS.STATUS_VALUES.PUBLISHED)
161
+ .select(BLOG_POST_FIELDS.SLUG, BLOG_POST_FIELDS.PUBLISHED_AT, BLOG_POST_FIELDS.UPDATED_AT)
162
+ .limit(1000)
163
+ .get();
164
+ return snap.docs.map((doc) => {
165
+ const data = doc.data();
166
+ const slug = data[BLOG_POST_FIELDS.SLUG] ?? doc.id;
167
+ const lastModified = data[BLOG_POST_FIELDS.UPDATED_AT]?.toDate?.() ??
168
+ data[BLOG_POST_FIELDS.PUBLISHED_AT]?.toDate?.() ??
169
+ new Date();
170
+ return {
171
+ url: `${baseUrl}${ROUTES.BLOG.ARTICLE(slug)}`,
172
+ lastModified,
173
+ changeFrequency: "weekly",
174
+ priority: 0.7,
175
+ };
176
+ });
177
+ }
178
+ catch (err) {
179
+ serverLogger.warn("sitemap: failed to fetch blog post URLs", { error: err });
180
+ return [];
181
+ }
182
+ }
183
+ async function fetchStoreUrls(baseUrl) {
184
+ try {
185
+ const db = getAdminDb();
186
+ const snap = await db
187
+ .collection(STORE_COLLECTION)
188
+ .where(STORE_FIELDS.STATUS, "==", STORE_FIELDS.STATUS_VALUES.ACTIVE)
189
+ .where(STORE_FIELDS.IS_PUBLIC, "==", true)
190
+ .select(STORE_FIELDS.STORE_SLUG, STORE_FIELDS.UPDATED_AT)
191
+ .limit(1000)
192
+ .get();
193
+ return snap.docs.map((doc) => {
194
+ const data = doc.data();
195
+ const slug = data[STORE_FIELDS.STORE_SLUG] ?? doc.id;
196
+ return {
197
+ url: `${baseUrl}${ROUTES.PUBLIC.STORE_DETAIL(slug)}`,
198
+ lastModified: data[STORE_FIELDS.UPDATED_AT]?.toDate?.() ?? new Date(),
199
+ changeFrequency: "weekly",
200
+ priority: 0.6,
201
+ };
202
+ });
203
+ }
204
+ catch (err) {
205
+ serverLogger.warn("sitemap: failed to fetch store URLs", { error: err });
206
+ return [];
207
+ }
208
+ }
209
+ async function fetchScammerUrls(baseUrl) {
210
+ try {
211
+ const db = getAdminDb();
212
+ const snap = await db
213
+ .collection(SCAMMER_COLLECTION)
214
+ .where("status", "==", "verified")
215
+ .select("seo", "updatedAt")
216
+ .limit(2000)
217
+ .get();
218
+ return snap.docs.map((doc) => {
219
+ const data = doc.data();
220
+ const slug = data.seo?.slug ?? doc.id;
221
+ return {
222
+ url: `${baseUrl}${ROUTES.PUBLIC.SCAM_DETAIL(slug)}`,
223
+ lastModified: data.updatedAt?.toDate?.() ?? new Date(),
224
+ changeFrequency: "weekly",
225
+ priority: 0.7,
226
+ };
227
+ });
228
+ }
229
+ catch (err) {
230
+ serverLogger.warn("sitemap: failed to fetch scammer URLs", { error: err });
231
+ return [];
232
+ }
233
+ }
234
+ export async function buildSitemap({ baseUrl }) {
235
+ const [productUrls, categoryUrls, eventUrls, blogUrls, auctionUrls, storeUrls, scammerUrls] = await Promise.all([
236
+ fetchProductUrls(baseUrl),
237
+ fetchCategoryUrls(baseUrl),
238
+ fetchEventUrls(baseUrl),
239
+ fetchBlogPostUrls(baseUrl),
240
+ fetchAuctionUrls(baseUrl),
241
+ fetchStoreUrls(baseUrl),
242
+ fetchScammerUrls(baseUrl),
243
+ ]);
244
+ return [
245
+ ...staticPages(baseUrl),
246
+ ...categoryUrls,
247
+ ...blogUrls,
248
+ ...productUrls,
249
+ ...auctionUrls,
250
+ ...eventUrls,
251
+ ...storeUrls,
252
+ ...scammerUrls,
253
+ ];
254
+ }
@@ -108,6 +108,11 @@ export function defineNextConfig(override = {}) {
108
108
  "./node_modules/duplexify/**",
109
109
  "./node_modules/uuid/**",
110
110
  "./node_modules/lodash.camelcase/**",
111
+ // gaxios transitive deps missing from Vercel tracer (dynamic require, not statically analysed)
112
+ "./node_modules/is-stream/**",
113
+ "./node_modules/extend/**",
114
+ "./node_modules/https-proxy-agent/**",
115
+ "./node_modules/agent-base/**",
111
116
  ],
112
117
  };
113
118
  const mergedOutputFileTracingIncludes = {
package/dist/server.d.ts CHANGED
@@ -483,3 +483,5 @@ export { getStoreCapabilities, storeHasCapability, } from "./_internal/server/fe
483
483
  export { isSoftBanned, getBanSummary } from "./features/auth/server/checkSoftBan";
484
484
  export type { UserSoftBan } from "./features/auth/schemas/firestore";
485
485
  export type { BannedAction } from "./features/auth/permissions/constants";
486
+ export { buildSitemap, buildRobots, buildManifest, buildDefaultOgImage, DEFAULT_OG_SIZE } from "./_internal/server/features/seo";
487
+ export type { SitemapOptions, RobotsOptions, ManifestOptions, DefaultOgOptions } from "./_internal/server/features/seo";
package/dist/server.js CHANGED
@@ -1356,3 +1356,5 @@ export { getServerPermissions, checkPermission, checkAnyPermission, makeAdminSec
1356
1356
  export { getStoreCapabilities, storeHasCapability, } from "./_internal/server/features/auth/capabilities";
1357
1357
  // ── Soft ban helpers ──────────────────────────────────────────────────────────
1358
1358
  export { isSoftBanned, getBanSummary } from "./features/auth/server/checkSoftBan";
1359
+ // -- SEO builders (sitemap / robots / manifest / og-image) --
1360
+ export { buildSitemap, buildRobots, buildManifest, buildDefaultOgImage, DEFAULT_OG_SIZE } from "./_internal/server/features/seo";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mohasinac/appkit",
3
- "version": "2.7.4",
3
+ "version": "2.7.5",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"