@wix/agent-skills 1.20.0 → 1.21.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/package.json +1 -1
- package/skills/wix-app/SKILL.md +14 -112
- package/skills/wix-app/references/APP_TOOLS.md +2 -1
- package/skills/wix-app/references/BACKEND_EVENT.md +3 -3
- package/skills/wix-app/references/CUSTOM_ELEMENT_WIDGET.md +41 -152
- package/skills/wix-app/references/DASHBOARD_MODAL.md +4 -4
- package/skills/wix-app/references/SDK_MODULE_MAP.md +44 -0
- package/skills/wix-app/references/SITE_PLUGIN.md +36 -190
- package/skills/wix-app/references/app-tools/TOOLS.md +3 -1
- package/skills/wix-app/references/custom-element-widget/SETTINGS_PANEL.md +21 -0
- package/skills/wix-app/references/dashboard-page/ECOM_NAVIGATION.md +17 -0
- package/skills/wix-app/references/dashboard-plugin/SLOTS.md +4 -18
- package/skills/wix-app/references/site-plugin/EXAMPLES.md +1 -1
- package/skills/wix-headless-fast/SKILL.md +302 -51
- package/skills/wix-headless-fast/entry/skill.md +11 -2
- package/skills/wix-headless-fast/install/AGENTS.md +32 -0
- package/skills/wix-headless-fast/install/agents-md.mjs +39 -0
- package/skills/wix-headless-fast/install/attach.mjs +360 -0
- package/skills/wix-headless-fast/install/deploy.mjs +109 -9
- package/skills/wix-headless-fast/install/setup.mjs +239 -0
- package/skills/wix-headless-fast/references/blog/INSTRUCTIONS.md +226 -60
- package/skills/wix-headless-fast/references/blog/app/components/blog/BlogFeedView.tsx +2 -3
- package/skills/wix-headless-fast/references/blog/app/components/blog/PostView.tsx +2 -3
- package/skills/wix-headless-fast/references/blog/app/hooks/blog/useBlogFeed.ts +24 -116
- package/skills/wix-headless-fast/references/blog/app/hooks/blog/usePost.ts +16 -64
- package/skills/wix-headless-fast/references/blog/app/wix/blog/blog-feed-store.ts +137 -0
- package/skills/wix-headless-fast/references/blog/app/wix/blog/post-store.ts +78 -0
- package/skills/wix-headless-fast/references/blog/app/wix/blog/posts-core.ts +117 -0
- package/skills/wix-headless-fast/references/blog/app/wix/blog/posts.ts +13 -60
- package/skills/wix-headless-fast/references/blog/app/wix/blog/taxonomy-core.ts +47 -0
- package/skills/wix-headless-fast/references/blog/app/wix/blog/taxonomy.ts +10 -28
- package/skills/wix-headless-fast/references/blog/app-astro/pages/blog/[...slug].astro +2 -4
- package/skills/wix-headless-fast/references/blog/rest/posts.ts +42 -0
- package/skills/wix-headless-fast/references/blog/rest/taxonomy.ts +62 -0
- package/skills/wix-headless-fast/references/blog/seed/SEED.md +9 -0
- package/skills/wix-headless-fast/references/blog/seed/read-site.mjs +22 -0
- package/skills/wix-headless-fast/references/bookings/INSTRUCTIONS.md +181 -33
- package/skills/wix-headless-fast/references/bookings/app/components/bookings/ServiceBookingView.tsx +2 -1
- package/skills/wix-headless-fast/references/bookings/app/hooks/bookings/useBookingFlow.ts +26 -132
- package/skills/wix-headless-fast/references/bookings/app/hooks/bookings/useServices.ts +18 -49
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking-core.ts +218 -0
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking-flow-store.ts +169 -0
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking.ts +57 -142
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services-core.ts +92 -0
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services-store.ts +90 -0
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services.ts +22 -61
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/types.ts +3 -1
- package/skills/wix-headless-fast/references/bookings/rest/booking.ts +113 -0
- package/skills/wix-headless-fast/references/bookings/rest/services.ts +55 -0
- package/skills/wix-headless-fast/references/bookings/seed/SEED.md +10 -0
- package/skills/wix-headless-fast/references/bookings/seed/read-site.mjs +28 -0
- package/skills/wix-headless-fast/references/cms/INSTRUCTIONS.md +238 -62
- package/skills/wix-headless-fast/references/cms/app/hooks/cms/useCollection.ts +19 -52
- package/skills/wix-headless-fast/references/cms/app/hooks/cms/useItem.ts +16 -36
- package/skills/wix-headless-fast/references/cms/app/wix/cms/collection-store.ts +158 -0
- package/skills/wix-headless-fast/references/cms/app/wix/cms/item-store.ts +93 -0
- package/skills/wix-headless-fast/references/cms/app/wix/cms/items-core.ts +177 -0
- package/skills/wix-headless-fast/references/cms/app/wix/cms/items.ts +13 -41
- package/skills/wix-headless-fast/references/cms/rest/items.ts +125 -0
- package/skills/wix-headless-fast/references/cms/seed/SEED.md +9 -0
- package/skills/wix-headless-fast/references/cms/seed/read-site.mjs +20 -0
- package/skills/wix-headless-fast/references/events/INSTRUCTIONS.md +241 -83
- package/skills/wix-headless-fast/references/events/app/components/events/EventConfirmationView.tsx +1 -1
- package/skills/wix-headless-fast/references/events/app/components/events/EventRegistrationView.tsx +7 -5
- package/skills/wix-headless-fast/references/events/app/components/events/EventsView.tsx +2 -2
- package/skills/wix-headless-fast/references/events/app/hooks/events/useEventRegistration.ts +28 -110
- package/skills/wix-headless-fast/references/events/app/hooks/events/useEvents.ts +19 -54
- package/skills/wix-headless-fast/references/events/app/wix/events/events-core.ts +130 -0
- package/skills/wix-headless-fast/references/events/app/wix/events/events-store.ts +97 -0
- package/skills/wix-headless-fast/references/events/app/wix/events/events.ts +13 -94
- package/skills/wix-headless-fast/references/events/app/wix/events/registration-core.ts +113 -0
- package/skills/wix-headless-fast/references/events/app/wix/events/registration-store.ts +156 -0
- package/skills/wix-headless-fast/references/events/app/wix/events/registration.ts +38 -69
- package/skills/wix-headless-fast/references/events/rest/events.ts +57 -0
- package/skills/wix-headless-fast/references/events/rest/registration.ts +83 -0
- package/skills/wix-headless-fast/references/events/seed/SEED.md +9 -0
- package/skills/wix-headless-fast/references/events/seed/read-site.mjs +26 -0
- package/skills/wix-headless-fast/references/forms/INSTRUCTIONS.md +216 -26
- package/skills/wix-headless-fast/references/forms/app/hooks/forms/useWixForm.ts +35 -267
- package/skills/wix-headless-fast/references/forms/app/wix/forms/form-store.ts +332 -0
- package/skills/wix-headless-fast/references/forms/app/wix/forms/forms-core.ts +238 -0
- package/skills/wix-headless-fast/references/forms/app/wix/forms/forms.ts +28 -243
- package/skills/wix-headless-fast/references/forms/app/wix/forms/submissions-core.ts +216 -0
- package/skills/wix-headless-fast/references/forms/app/wix/forms/submissions.ts +32 -204
- package/skills/wix-headless-fast/references/forms/rest/forms.ts +34 -0
- package/skills/wix-headless-fast/references/forms/rest/submissions.ts +56 -0
- package/skills/wix-headless-fast/references/forms/seed/SEED.md +8 -0
- package/skills/wix-headless-fast/references/forms/seed/read-site.mjs +20 -0
- package/skills/wix-headless-fast/references/forms/seed/seed-forms.mjs +15 -8
- package/skills/wix-headless-fast/references/members/INSTRUCTIONS.md +316 -63
- package/skills/wix-headless-fast/references/members/app/components/members/LoginForm.tsx +1 -1
- package/skills/wix-headless-fast/references/members/app/wix/members/auth-core.ts +105 -0
- package/skills/wix-headless-fast/references/members/app/wix/members/auth.ts +36 -65
- package/skills/wix-headless-fast/references/members/app/wix/members/client.ts +1 -1
- package/skills/wix-headless-fast/references/members/app/wix/members/members-core.ts +43 -0
- package/skills/wix-headless-fast/references/members/app/wix/members/members.ts +11 -32
- package/skills/wix-headless-fast/references/members/rest/auth.ts +215 -0
- package/skills/wix-headless-fast/references/members/rest/members.ts +26 -0
- package/skills/wix-headless-fast/references/members/seed/read-site.mjs +15 -0
- package/skills/wix-headless-fast/references/portfolio/INSTRUCTIONS.md +240 -59
- package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/CollectionProjectsView.tsx +2 -2
- package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/CollectionsView.tsx +2 -2
- package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/GalleryMedia.tsx +35 -0
- package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/ProjectDetailView.tsx +8 -38
- package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useCollectionProjects.ts +23 -50
- package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useCollections.ts +16 -34
- package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useProjectDetail.ts +22 -49
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/collection-projects-store.ts +82 -0
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/collections-store.ts +58 -0
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/portfolio-core.ts +177 -0
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/portfolio.ts +31 -118
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/project-detail-store.ts +81 -0
- package/skills/wix-headless-fast/references/portfolio/rest/portfolio.ts +75 -0
- package/skills/wix-headless-fast/references/portfolio/seed/SEED.md +8 -0
- package/skills/wix-headless-fast/references/portfolio/seed/read-site.mjs +20 -0
- package/skills/wix-headless-fast/references/portfolio/seed/seed-portfolio.mjs +19 -21
- package/skills/wix-headless-fast/references/pricing-plans/INSTRUCTIONS.md +214 -51
- package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/PlanDetailView.tsx +9 -20
- package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/PlansView.tsx +7 -32
- package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/SubscribeButton.tsx +41 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/hooks/pricing-plans/usePlanPurchase.ts +12 -33
- package/skills/wix-headless-fast/references/pricing-plans/app/hooks/pricing-plans/usePlans.ts +17 -34
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plan-purchase-store.ts +54 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans-core.ts +106 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans-store.ts +64 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans.ts +7 -55
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/purchase-core.ts +43 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/purchase.ts +11 -34
- package/skills/wix-headless-fast/references/pricing-plans/rest/plans.ts +31 -0
- package/skills/wix-headless-fast/references/pricing-plans/rest/purchase.ts +20 -0
- package/skills/wix-headless-fast/references/pricing-plans/seed/SEED.md +8 -0
- package/skills/wix-headless-fast/references/pricing-plans/seed/read-site.mjs +21 -0
- package/skills/wix-headless-fast/references/restaurants/INSTRUCTIONS.md +242 -47
- package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/MenuView.tsx +2 -2
- package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/OrderCartDrawer.tsx +2 -2
- package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useMenus.ts +14 -35
- package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useReservation.ts +28 -171
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu-core.ts +155 -0
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu-store.ts +89 -0
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu.ts +23 -117
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/ordering-core.ts +124 -0
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/ordering.ts +45 -114
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservation-store.ts +193 -0
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservations-core.ts +94 -0
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservations.ts +15 -87
- package/skills/wix-headless-fast/references/restaurants/rest/menu.ts +60 -0
- package/skills/wix-headless-fast/references/restaurants/rest/ordering.ts +169 -0
- package/skills/wix-headless-fast/references/restaurants/rest/reservations.ts +68 -0
- package/skills/wix-headless-fast/references/restaurants/seed/SEED.md +8 -0
- package/skills/wix-headless-fast/references/restaurants/seed/read-site.mjs +25 -0
- package/skills/wix-headless-fast/references/restaurants/seed/seed-restaurants.mjs +25 -5
- package/skills/wix-headless-fast/references/shared/SUPPLIED-CONTENT.md +23 -0
- package/skills/wix-headless-fast/references/shared/app/wix/media.ts +45 -4
- package/skills/wix-headless-fast/references/shared/app/wix/sdk.ts +13 -0
- package/skills/wix-headless-fast/references/shared/rest/client.ts +155 -0
- package/skills/wix-headless-fast/references/shared/rest/config.ts +3 -0
- package/skills/wix-headless-fast/references/shared/rest/media.ts +65 -0
- package/skills/wix-headless-fast/references/shared/seed/read-site.mjs +99 -0
- package/skills/wix-headless-fast/references/storefront/INSTRUCTIONS.md +186 -63
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/OptionPicker.tsx +95 -0
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/QuickAdd.tsx +29 -58
- package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useProductDetail.ts +29 -163
- package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useShop.ts +26 -300
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart-core.ts +121 -0
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart.ts +49 -162
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog-core.ts +314 -0
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog.ts +47 -370
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/product-detail-store.ts +147 -0
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/shop-store.ts +289 -0
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/types.ts +2 -0
- package/skills/wix-headless-fast/references/storefront/rest/cart.ts +132 -0
- package/skills/wix-headless-fast/references/storefront/rest/catalog.ts +123 -0
- package/skills/wix-headless-fast/references/storefront/seed/SEED.md +31 -9
- package/skills/wix-headless-fast/references/storefront/seed/read-site.mjs +38 -0
- package/skills/wix-headless-fast/references/storefront/seed/seed-store.mjs +57 -50
- package/skills/wix-manage/references/ecommerce/ecom-shipping.md +1 -1
- package/skills/wix-manage/references/ecommerce/pricing-promotions/ecom-pricing-create-coupon.md +10 -19
- package/skills/wix-manage/references/events/create-wix-event.md +5 -1
- package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +0 -40
- package/skills/wix-vibe-headless/references/storefront/seed/SEED.md +3 -0
- package/skills/wix-vibe-headless/references/storefront/seed/seed-store.cjs +6 -0
- package/skills/wix-headless-fast/install/fast-path.mjs +0 -186
- package/skills/wix-headless-fast/install/pin-agents-md.mjs +0 -57
|
@@ -1,71 +1,24 @@
|
|
|
1
|
-
// Post reads (Wix Blog V3) — the only file that touches raw post entities
|
|
2
|
-
// returns is a plain DTO from ./types.
|
|
3
|
-
//
|
|
1
|
+
// Post reads (Wix Blog V3) over the SDK — the only file that touches raw post entities on this
|
|
2
|
+
// transport. Everything it returns is a plain DTO from ./types. The rules and mappers live in
|
|
3
|
+
// ./posts-core (shared with the REST twin in references/blog/rest/); this file is the transport
|
|
4
|
+
// only. Copy as-is; extend by adding functions, not by editing these. Blog posts are NOT CMS
|
|
5
|
+
// collections — always @wix/blog, never @wix/data.
|
|
4
6
|
// docs: https://dev.wix.com/docs/api-reference/business-solutions/blog/posts-stats/query-posts.md
|
|
5
7
|
// docs: https://dev.wix.com/docs/api-reference/business-solutions/blog/posts-stats/get-post-by-slug.md
|
|
6
8
|
import { posts as postsModule } from "@wix/blog";
|
|
7
9
|
import { wixModule } from "../sdk";
|
|
8
10
|
import { imgSrc } from "../media";
|
|
9
|
-
import
|
|
11
|
+
import { BLOG_APP_ID, DETAIL_FIELDSETS, postMetaLine, toDetail, toSummary, type FetchPostsOptions, type Raw } from "./posts-core";
|
|
12
|
+
import type { PostDetail, PostPage } from "./types";
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
/** The Wix Blog app id (Astro item-page routing; comments use it as appId). */
|
|
14
|
-
export const BLOG_APP_ID = "14bcded7-0066-7c35-14d7-466cb3f09103";
|
|
15
|
-
|
|
16
|
-
type Raw = Record<string, any>;
|
|
14
|
+
export { BLOG_APP_ID, postMetaLine, type FetchPostsOptions };
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
if (!value) return { dateLabel: "", dateISO: "" };
|
|
20
|
-
const d = value instanceof Date ? value : new Date(String(value));
|
|
21
|
-
if (Number.isNaN(d.getTime())) return { dateLabel: "", dateISO: "" };
|
|
22
|
-
return {
|
|
23
|
-
dateLabel: d.toLocaleDateString(undefined, { year: "numeric", month: "short", day: "numeric" }),
|
|
24
|
-
dateISO: d.toISOString(),
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function toSummary(raw: Raw): PostSummary {
|
|
29
|
-
return {
|
|
30
|
-
id: raw._id ?? "",
|
|
31
|
-
slug: raw.slug ?? "",
|
|
32
|
-
title: raw.title ?? "",
|
|
33
|
-
excerpt: raw.excerpt ?? "",
|
|
34
|
-
...dateParts(raw.firstPublishedDate),
|
|
35
|
-
minutesToRead: raw.minutesToRead ?? 0,
|
|
36
|
-
featured: raw.featured === true,
|
|
37
|
-
pinned: raw.pinned === true,
|
|
38
|
-
// The cover is a media STRING (wix:image:// or https) at media.wixMedia.image.
|
|
39
|
-
coverUrl: imgSrc(raw.media?.wixMedia?.image, 1200, 675),
|
|
40
|
-
categoryIds: raw.categoryIds ?? [],
|
|
41
|
-
tagIds: raw.tagIds ?? [],
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function toDetail(raw: Raw): PostDetail {
|
|
46
|
-
return {
|
|
47
|
-
...toSummary(raw),
|
|
48
|
-
richContent: raw.richContent ?? null,
|
|
49
|
-
// contentText is plain text — split on newlines for the fallback body.
|
|
50
|
-
paragraphs: String(raw.contentText ?? "")
|
|
51
|
-
.split("\n")
|
|
52
|
-
.map((s: string) => s.trim())
|
|
53
|
-
.filter(Boolean),
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export interface FetchPostsOptions {
|
|
58
|
-
limit?: number;
|
|
59
|
-
/** `nextCursor` from a previous page. */
|
|
60
|
-
cursor?: string | null;
|
|
61
|
-
/** Server-side filters (first page only — the cursor carries them on later pages). */
|
|
62
|
-
categoryId?: string | null;
|
|
63
|
-
tagId?: string | null;
|
|
64
|
-
}
|
|
16
|
+
const posts = wixModule(postsModule);
|
|
65
17
|
|
|
66
18
|
/**
|
|
67
19
|
* One page of published posts, newest first (pinned posts lead). Only published posts come
|
|
68
20
|
* back to a visitor token — a "missing" post usually wasn't published, not a query bug.
|
|
21
|
+
* The builder below spells posts-core's feedQuery: sort + filters on the first page, the cursor alone after.
|
|
69
22
|
*/
|
|
70
23
|
export async function fetchPosts({ limit = 20, cursor, categoryId, tagId }: FetchPostsOptions = {}): Promise<PostPage> {
|
|
71
24
|
let q = posts.queryPosts().limit(limit);
|
|
@@ -79,7 +32,7 @@ export async function fetchPosts({ limit = 20, cursor, categoryId, tagId }: Fetc
|
|
|
79
32
|
}
|
|
80
33
|
const res = await q.find();
|
|
81
34
|
return {
|
|
82
|
-
posts: (res.items ?? []).map((p: Raw) => toSummary(p)),
|
|
35
|
+
posts: (res.items ?? []).map((p: Raw) => toSummary(p, imgSrc)),
|
|
83
36
|
nextCursor: res.hasNext() ? (res.cursors?.next ?? null) : null,
|
|
84
37
|
};
|
|
85
38
|
}
|
|
@@ -90,10 +43,10 @@ export async function fetchPosts({ limit = 20, cursor, categoryId, tagId }: Fetc
|
|
|
90
43
|
*/
|
|
91
44
|
export async function fetchPostBySlug(slug: string): Promise<PostDetail | null> {
|
|
92
45
|
const res = await posts
|
|
93
|
-
.queryPosts({ fieldsets: [
|
|
46
|
+
.queryPosts({ fieldsets: [...DETAIL_FIELDSETS] as any })
|
|
94
47
|
.eq("slug", slug)
|
|
95
48
|
.limit(1)
|
|
96
49
|
.find();
|
|
97
50
|
const raw = res.items?.[0];
|
|
98
|
-
return raw ? toDetail(raw as Raw) : null;
|
|
51
|
+
return raw ? toDetail(raw as Raw, imgSrc) : null;
|
|
99
52
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Category + tag rules and DTO mapping — transport-agnostic, imported by both ./taxonomy.ts (SDK)
|
|
2
|
+
// and the REST twin in references/blog/rest/taxonomy.ts (fetch). Raw entities may carry `_id`
|
|
3
|
+
// (SDK) or `id` (REST). Has its own rawId/mediaValue (copies of posts-core's) so it stands alone
|
|
4
|
+
// when stripped; imports are type-only.
|
|
5
|
+
import type { BlogCategory, BlogTag } from "./types";
|
|
6
|
+
import type { ImgSrc, Raw } from "./posts-core";
|
|
7
|
+
|
|
8
|
+
/** Menu order — displayPosition ascending; -1 (unplaced) sorts first, which is also what the SDK path shows. */
|
|
9
|
+
export const CATEGORY_SORT = [{ fieldName: "displayPosition", order: "ASC" }] as const;
|
|
10
|
+
/** Most-published-posts first. */
|
|
11
|
+
export const TAG_SORT = [{ fieldName: "publishedPostCount", order: "DESC" }] as const;
|
|
12
|
+
/** Blog taxonomies are small — one read of up to 100 is the whole list. */
|
|
13
|
+
export const TAXONOMY_LIMIT = 100;
|
|
14
|
+
|
|
15
|
+
const rawId = (raw: Raw | undefined | null): string => raw?._id ?? raw?.id ?? "";
|
|
16
|
+
|
|
17
|
+
// A copy of posts-core's mediaValue: the REST Image object → the wix:image form imgSrc scales.
|
|
18
|
+
function mediaValue(value: unknown): string {
|
|
19
|
+
if (!value) return "";
|
|
20
|
+
if (typeof value === "string") return value;
|
|
21
|
+
const o = value as Raw;
|
|
22
|
+
if (o.id && !String(o.id).startsWith("http")) {
|
|
23
|
+
const size = o.width && o.height ? `#originWidth=${o.width}&originHeight=${o.height}` : "";
|
|
24
|
+
return `wix:image://v1/${o.id}/${o.filename ?? ""}${size}`;
|
|
25
|
+
}
|
|
26
|
+
return o.url ?? o.image ?? "";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function toCategory(raw: Raw, imgSrc: ImgSrc): BlogCategory {
|
|
30
|
+
return {
|
|
31
|
+
id: rawId(raw),
|
|
32
|
+
slug: raw.slug ?? "",
|
|
33
|
+
label: raw.label ?? "", // the API's display name is `label`, never `name`
|
|
34
|
+
description: raw.description ?? "",
|
|
35
|
+
postCount: raw.postCount ?? 0,
|
|
36
|
+
coverUrl: imgSrc(mediaValue(raw.coverImage), 1200, 675),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function toTag(raw: Raw): BlogTag {
|
|
41
|
+
return {
|
|
42
|
+
id: rawId(raw),
|
|
43
|
+
slug: raw.slug ?? "",
|
|
44
|
+
label: raw.label ?? "",
|
|
45
|
+
postCount: raw.publishedPostCount ?? 0, // PUBLISHED posts — postCount also counts drafts
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -1,42 +1,24 @@
|
|
|
1
|
-
// Category + tag reads (Wix Blog V3) — the only file that touches raw taxonomy
|
|
2
|
-
// Everything it returns is a plain DTO from ./types.
|
|
1
|
+
// Category + tag reads (Wix Blog V3) over the SDK — the only file that touches raw taxonomy
|
|
2
|
+
// entities on this transport. Everything it returns is a plain DTO from ./types. The mappers live
|
|
3
|
+
// in ./taxonomy-core (shared with the REST twin in references/blog/rest/); this file is the
|
|
4
|
+
// transport only. Copy as-is; extend by adding functions.
|
|
3
5
|
// docs: https://dev.wix.com/docs/api-reference/business-solutions/blog/category/query-categories.md
|
|
4
6
|
// docs: https://dev.wix.com/docs/api-reference/business-solutions/blog/tags/query-tags.md
|
|
5
7
|
import { categories as categoriesModule, tags as tagsModule } from "@wix/blog";
|
|
6
8
|
import { wixModule } from "../sdk";
|
|
7
9
|
import { imgSrc } from "../media";
|
|
10
|
+
import { TAXONOMY_LIMIT, toCategory, toTag } from "./taxonomy-core";
|
|
11
|
+
import type { Raw } from "./posts-core";
|
|
8
12
|
import type { BlogCategory, BlogTag } from "./types";
|
|
9
13
|
|
|
10
14
|
const categories = wixModule(categoriesModule);
|
|
11
15
|
const tags = wixModule(tagsModule);
|
|
12
16
|
|
|
13
|
-
type Raw = Record<string, any>;
|
|
14
|
-
|
|
15
|
-
function toCategory(raw: Raw): BlogCategory {
|
|
16
|
-
return {
|
|
17
|
-
id: raw._id ?? "",
|
|
18
|
-
slug: raw.slug ?? "",
|
|
19
|
-
label: raw.label ?? "",
|
|
20
|
-
description: raw.description ?? "",
|
|
21
|
-
postCount: raw.postCount ?? 0,
|
|
22
|
-
coverUrl: imgSrc(raw.coverImage, 1200, 675),
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function toTag(raw: Raw): BlogTag {
|
|
27
|
-
return {
|
|
28
|
-
id: raw._id ?? "",
|
|
29
|
-
slug: raw.slug ?? "",
|
|
30
|
-
label: raw.label ?? "",
|
|
31
|
-
postCount: raw.publishedPostCount ?? 0,
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
17
|
/** Categories in menu order (displayPosition) — non-fatal (empty array on failure). */
|
|
36
18
|
export async function fetchBlogCategories(): Promise<BlogCategory[]> {
|
|
37
19
|
try {
|
|
38
|
-
const res = await categories.queryCategories().ascending("displayPosition").limit(
|
|
39
|
-
return (res.items ?? []).map((c: Raw) => toCategory(c)).filter((c) => c.id);
|
|
20
|
+
const res = await categories.queryCategories().ascending("displayPosition").limit(TAXONOMY_LIMIT).find();
|
|
21
|
+
return (res.items ?? []).map((c: Raw) => toCategory(c, imgSrc)).filter((c) => c.id);
|
|
40
22
|
} catch {
|
|
41
23
|
return [];
|
|
42
24
|
}
|
|
@@ -45,7 +27,7 @@ export async function fetchBlogCategories(): Promise<BlogCategory[]> {
|
|
|
45
27
|
/** Tags, most-published-posts first — non-fatal (empty array on failure). */
|
|
46
28
|
export async function fetchBlogTags(): Promise<BlogTag[]> {
|
|
47
29
|
try {
|
|
48
|
-
const res = await tags.queryTags().descending("publishedPostCount").limit(
|
|
30
|
+
const res = await tags.queryTags().descending("publishedPostCount").limit(TAXONOMY_LIMIT).find();
|
|
49
31
|
return (res.items ?? []).map((t: Raw) => toTag(t)).filter((t) => t.id);
|
|
50
32
|
} catch {
|
|
51
33
|
return [];
|
|
@@ -59,7 +41,7 @@ export async function fetchBlogTags(): Promise<BlogTag[]> {
|
|
|
59
41
|
export async function fetchCategoryBySlug(slug: string): Promise<BlogCategory | null> {
|
|
60
42
|
try {
|
|
61
43
|
const res = await categories.getCategoryBySlug(slug);
|
|
62
|
-
return res.category ? toCategory(res.category as Raw) : null;
|
|
44
|
+
return res.category ? toCategory(res.category as Raw, imgSrc) : null;
|
|
63
45
|
} catch {
|
|
64
46
|
return null;
|
|
65
47
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// template below freely. The body island is client:only — the ricos viewer breaks under SSR.
|
|
8
8
|
import SiteLayout from "../../layouts/SiteLayout.astro";
|
|
9
9
|
import RichContent from "../../components/blog/RichContent";
|
|
10
|
-
import { fetchPostBySlug } from "../../wix/blog/posts";
|
|
10
|
+
import { fetchPostBySlug, postMetaLine } from "../../wix/blog/posts";
|
|
11
11
|
import { fetchBlogCategories, fetchBlogTags } from "../../wix/blog/taxonomy";
|
|
12
12
|
import type { BlogCategory, BlogTag } from "../../wix/blog/types";
|
|
13
13
|
import { WIX_APPS } from "@wix/essentials";
|
|
@@ -70,9 +70,7 @@ const postTags = allTags.filter((t) => post!.tagIds.includes(t.id));
|
|
|
70
70
|
{postCategories.length > 0 && <p class="eyebrow">{postCategories.map((c) => c.label).join(" · ")}</p>}
|
|
71
71
|
<h1 class="mt-2 text-3xl font-semibold tracking-tight">{post.title}</h1>
|
|
72
72
|
<p class="mt-3 text-sm text-muted-foreground">
|
|
73
|
-
|
|
74
|
-
{post.dateLabel && post.minutesToRead > 0 ? " · " : ""}
|
|
75
|
-
{post.minutesToRead > 0 ? `${post.minutesToRead} min read` : ""}
|
|
73
|
+
<time datetime={post.dateISO}>{postMetaLine(post)}</time>
|
|
76
74
|
</p>
|
|
77
75
|
{
|
|
78
76
|
post.coverUrl && (
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Post reads over REST — the twin of app/wix/blog/posts.ts. Same exports, same DTOs; the rules and
|
|
2
|
+
// mappers come from posts-core (the SAME file the SDK transport uses, deployed flat next to this one
|
|
3
|
+
// by deploy.mjs --stack static), so this file is only the transport: one fetch with a literal body
|
|
4
|
+
// per function. Every call here is safe from a browser with a visitor token — only PUBLISHED posts
|
|
5
|
+
// come back to it. Porting: keep the paths, keep the bodies, port the core once.
|
|
6
|
+
// docs: https://dev.wix.com/docs/api-reference/business-solutions/blog/posts-stats/query-posts.md
|
|
7
|
+
// docs: https://dev.wix.com/docs/api-reference/business-solutions/blog/posts-stats/get-post-by-slug.md
|
|
8
|
+
import { wixRequest } from "./client.js";
|
|
9
|
+
import { imgSrc } from "./media.js";
|
|
10
|
+
import { BLOG_APP_ID, DETAIL_FIELDSETS, feedQuery, postMetaLine, slugQuery, toDetail, toSummary, type FetchPostsOptions, type Raw } from "./posts-core.js";
|
|
11
|
+
import type { PostDetail, PostPage } from "./types.js";
|
|
12
|
+
|
|
13
|
+
export { BLOG_APP_ID, postMetaLine, type FetchPostsOptions };
|
|
14
|
+
|
|
15
|
+
const POSTS_QUERY = "/blog/v3/posts/query";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* One page of published posts, newest first (pinned posts lead); `nextCursor` continues the same
|
|
19
|
+
* query — send it alone, the filters ride inside it.
|
|
20
|
+
* POST /blog/v3/posts/query { query: { filter?, sort, cursorPaging: { limit } } } — first page
|
|
21
|
+
* POST /blog/v3/posts/query { query: { cursorPaging: { limit, cursor } } } — later pages
|
|
22
|
+
*/
|
|
23
|
+
export async function fetchPosts(o: FetchPostsOptions = {}): Promise<PostPage> {
|
|
24
|
+
const res = await wixRequest<Raw>(POSTS_QUERY, { body: { query: feedQuery(o) } });
|
|
25
|
+
const paging: Raw = res?.pagingMetadata ?? {};
|
|
26
|
+
return {
|
|
27
|
+
posts: ((res?.posts ?? []) as Raw[]).map((p) => toSummary(p, imgSrc)),
|
|
28
|
+
nextCursor: paging.hasNext ? (paging.cursors?.next ?? null) : null,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* One post by URL slug with the full body (the RICH_CONTENT + CONTENT_TEXT fieldsets — without
|
|
34
|
+
* them richContent and contentText are absent). Null when the slug resolves to nothing — a real
|
|
35
|
+
* 404 for the page, never a fallback to another post.
|
|
36
|
+
* POST /blog/v3/posts/query { fieldsets: ["RICH_CONTENT", "CONTENT_TEXT"], query: { filter: { slug: { $eq } }, cursorPaging: { limit: 1 } } }
|
|
37
|
+
*/
|
|
38
|
+
export async function fetchPostBySlug(slug: string): Promise<PostDetail | null> {
|
|
39
|
+
const res = await wixRequest<Raw>(POSTS_QUERY, { body: { fieldsets: DETAIL_FIELDSETS, query: slugQuery(slug) } });
|
|
40
|
+
const raw: Raw | undefined = res?.posts?.[0];
|
|
41
|
+
return raw ? toDetail(raw, imgSrc) : null;
|
|
42
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Category + tag reads over REST — the twin of app/wix/blog/taxonomy.ts. Same exports, same DTOs;
|
|
2
|
+
// the mappers come from taxonomy-core (the SAME file the SDK transport uses, deployed flat next to
|
|
3
|
+
// this one). Taxonomy queries page by OFFSET (`paging`), unlike posts (`cursorPaging`); a blog's
|
|
4
|
+
// taxonomy fits in one read of 100. All four calls are visitor-safe.
|
|
5
|
+
// docs: https://dev.wix.com/docs/api-reference/business-solutions/blog/category/query-categories.md
|
|
6
|
+
// docs: https://dev.wix.com/docs/api-reference/business-solutions/blog/category/get-category-by-slug.md
|
|
7
|
+
// docs: https://dev.wix.com/docs/api-reference/business-solutions/blog/tags/query-tags.md
|
|
8
|
+
// docs: https://dev.wix.com/docs/api-reference/business-solutions/blog/tags/get-tag-by-slug.md
|
|
9
|
+
import { wixRequest } from "./client.js";
|
|
10
|
+
import { imgSrc } from "./media.js";
|
|
11
|
+
import { CATEGORY_SORT, TAG_SORT, TAXONOMY_LIMIT, toCategory, toTag } from "./taxonomy-core.js";
|
|
12
|
+
import type { Raw } from "./posts-core.js";
|
|
13
|
+
import type { BlogCategory, BlogTag } from "./types.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Categories in menu order. Non-fatal: [] on failure (the feed renders without its filter row).
|
|
17
|
+
* POST /blog/v3/categories/query { query: { sort: [{ fieldName: "displayPosition", order: "ASC" }], paging: { limit: 100 } } }
|
|
18
|
+
*/
|
|
19
|
+
export async function fetchBlogCategories(): Promise<BlogCategory[]> {
|
|
20
|
+
try {
|
|
21
|
+
const res = await wixRequest<Raw>("/blog/v3/categories/query", { body: { query: { sort: CATEGORY_SORT, paging: { limit: TAXONOMY_LIMIT } } } });
|
|
22
|
+
return ((res?.categories ?? []) as Raw[]).map((c) => toCategory(c, imgSrc)).filter((c) => c.id);
|
|
23
|
+
} catch {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Tags, most-published-posts first. Non-fatal: [] on failure.
|
|
30
|
+
* POST /blog/v3/tags/query { query: { sort: [{ fieldName: "publishedPostCount", order: "DESC" }], paging: { limit: 100 } } }
|
|
31
|
+
*/
|
|
32
|
+
export async function fetchBlogTags(): Promise<BlogTag[]> {
|
|
33
|
+
try {
|
|
34
|
+
const res = await wixRequest<Raw>("/blog/v3/tags/query", { body: { query: { sort: TAG_SORT, paging: { limit: TAXONOMY_LIMIT } } } });
|
|
35
|
+
return ((res?.tags ?? []) as Raw[]).map(toTag).filter((t) => t.id);
|
|
36
|
+
} catch {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// The two by-slug getters answer in DIFFERENT envelopes ({ category } and { tag }); a missing slug
|
|
42
|
+
// is a 404 — mapped to null here, as the SDK transport does.
|
|
43
|
+
|
|
44
|
+
/** One category by its URL slug. Null when not found. GET /blog/v3/categories/slugs/{slug} */
|
|
45
|
+
export async function fetchCategoryBySlug(slug: string): Promise<BlogCategory | null> {
|
|
46
|
+
try {
|
|
47
|
+
const res = await wixRequest<Raw>(`/blog/v3/categories/slugs/${encodeURIComponent(slug)}`, { method: "GET" });
|
|
48
|
+
return res?.category ? toCategory(res.category, imgSrc) : null;
|
|
49
|
+
} catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** One tag by its URL slug. Null when not found. GET /blog/v3/tags/slugs/{slug} */
|
|
55
|
+
export async function fetchTagBySlug(slug: string): Promise<BlogTag | null> {
|
|
56
|
+
try {
|
|
57
|
+
const res = await wixRequest<Raw>(`/blog/v3/tags/slugs/${encodeURIComponent(slug)}`, { method: "GET" });
|
|
58
|
+
return res?.tag ? toTag(res.tag) : null;
|
|
59
|
+
} catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -62,6 +62,15 @@ feed without covers looks broken.
|
|
|
62
62
|
**Seeding is additive — never delete or overwrite existing content**; ask first if a cleanup
|
|
63
63
|
seems needed.
|
|
64
64
|
|
|
65
|
+
## Supplied content
|
|
66
|
+
|
|
67
|
+
The general rules are in `references/shared/SUPPLIED-CONTENT.md`. For a blog, each post the user
|
|
68
|
+
hands over is one entry. Its title becomes `title`; its body becomes `content` blocks, one block per
|
|
69
|
+
paragraph, heading or list, whether the body arrived as text, HTML or Markdown; a category becomes
|
|
70
|
+
`category` (and joins the `categories` list); tags become `tags`; their cover image becomes
|
|
71
|
+
`coverImageUrl`. A post with no body is a question for the user. Authors and publish dates are
|
|
72
|
+
not seeded — say so.
|
|
73
|
+
|
|
65
74
|
## Escape hatch — individual functions
|
|
66
75
|
`setupBlog` composes exported steps — `installBlogApp`, `getAuthorMemberId`, `createPosts`,
|
|
67
76
|
`createCategories`, `createTags`, `importImage`, `attachPostCovers`, plus `makeCtx()` —
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// What the blog holds: posts, categories, tags.
|
|
2
|
+
// node <SKILL_ROOT>/references/blog/seed/read-site.mjs [--site <siteId>] [--limit <n>]
|
|
3
|
+
import { runReader } from "../../shared/seed/read-site.mjs";
|
|
4
|
+
|
|
5
|
+
const BLOG_APP_ID = "14bcded7-0066-7c35-14d7-466cb3f09103";
|
|
6
|
+
const D = "https://dev.wix.com/docs/api-reference/business-solutions/blog";
|
|
7
|
+
|
|
8
|
+
await runReader({
|
|
9
|
+
vertical: "blog",
|
|
10
|
+
appId: BLOG_APP_ID,
|
|
11
|
+
async read(api, { limit }) {
|
|
12
|
+
const posts = await api.call({ path: "/v3/posts/query", body: { fieldsets: ["URL"], query: { paging: { limit } } }, docs: `${D}/posts-stats/query-posts` });
|
|
13
|
+
const cats = await api.tryCall({ path: "/blog/v3/categories/query", body: { query: { paging: { limit: 100 } } }, docs: `${D}/category/query-categories` });
|
|
14
|
+
const tags = await api.tryCall({ path: "/v3/tags/query", body: { query: { paging: { limit: 100 } } }, docs: `${D}/tags/query-tags` });
|
|
15
|
+
return {
|
|
16
|
+
postCount: posts.metaData?.total ?? posts.pagingMetadata?.total ?? (posts.posts ?? []).length,
|
|
17
|
+
posts: (posts.posts ?? []).map((p) => ({ title: p.title, slug: p.slug, published: p.firstPublishedDate ?? null, cover: !!p.coverMedia?.image, categories: (p.categoryIds ?? []).length, tags: (p.tagIds ?? []).length, minutesToRead: p.minutesToRead ?? null })),
|
|
18
|
+
categories: (cats?.categories ?? []).map((c) => ({ id: c.id, label: c.label, slug: c.slug, postCount: c.postCount ?? null })),
|
|
19
|
+
tags: (tags?.tags ?? []).map((t) => ({ id: t.id, label: t.label, postCount: t.postCount ?? null })),
|
|
20
|
+
};
|
|
21
|
+
},
|
|
22
|
+
});
|