@wix/agent-skills 1.20.0 → 1.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/package.json +1 -1
  2. package/skills/wix-headless-fast/SKILL.md +200 -34
  3. package/skills/wix-headless-fast/entry/skill.md +10 -2
  4. package/skills/wix-headless-fast/install/attach.mjs +253 -0
  5. package/skills/wix-headless-fast/install/deploy.mjs +109 -9
  6. package/skills/wix-headless-fast/install/fast-path.mjs +29 -34
  7. package/skills/wix-headless-fast/references/blog/INSTRUCTIONS.md +224 -60
  8. package/skills/wix-headless-fast/references/blog/app/components/blog/BlogFeedView.tsx +2 -3
  9. package/skills/wix-headless-fast/references/blog/app/components/blog/PostView.tsx +2 -3
  10. package/skills/wix-headless-fast/references/blog/app/hooks/blog/useBlogFeed.ts +24 -116
  11. package/skills/wix-headless-fast/references/blog/app/hooks/blog/usePost.ts +16 -64
  12. package/skills/wix-headless-fast/references/blog/app/wix/blog/blog-feed-store.ts +137 -0
  13. package/skills/wix-headless-fast/references/blog/app/wix/blog/post-store.ts +78 -0
  14. package/skills/wix-headless-fast/references/blog/app/wix/blog/posts-core.ts +117 -0
  15. package/skills/wix-headless-fast/references/blog/app/wix/blog/posts.ts +13 -60
  16. package/skills/wix-headless-fast/references/blog/app/wix/blog/taxonomy-core.ts +47 -0
  17. package/skills/wix-headless-fast/references/blog/app/wix/blog/taxonomy.ts +10 -28
  18. package/skills/wix-headless-fast/references/blog/app-astro/pages/blog/[...slug].astro +2 -4
  19. package/skills/wix-headless-fast/references/blog/rest/posts.ts +42 -0
  20. package/skills/wix-headless-fast/references/blog/rest/taxonomy.ts +62 -0
  21. package/skills/wix-headless-fast/references/blog/seed/SEED.md +9 -0
  22. package/skills/wix-headless-fast/references/bookings/INSTRUCTIONS.md +179 -33
  23. package/skills/wix-headless-fast/references/bookings/app/components/bookings/ServiceBookingView.tsx +2 -1
  24. package/skills/wix-headless-fast/references/bookings/app/hooks/bookings/useBookingFlow.ts +26 -132
  25. package/skills/wix-headless-fast/references/bookings/app/hooks/bookings/useServices.ts +18 -49
  26. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking-core.ts +218 -0
  27. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking-flow-store.ts +169 -0
  28. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking.ts +57 -142
  29. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services-core.ts +92 -0
  30. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services-store.ts +90 -0
  31. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services.ts +22 -61
  32. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/types.ts +3 -1
  33. package/skills/wix-headless-fast/references/bookings/rest/booking.ts +113 -0
  34. package/skills/wix-headless-fast/references/bookings/rest/services.ts +55 -0
  35. package/skills/wix-headless-fast/references/bookings/seed/SEED.md +10 -0
  36. package/skills/wix-headless-fast/references/cms/INSTRUCTIONS.md +236 -62
  37. package/skills/wix-headless-fast/references/cms/app/hooks/cms/useCollection.ts +19 -52
  38. package/skills/wix-headless-fast/references/cms/app/hooks/cms/useItem.ts +16 -36
  39. package/skills/wix-headless-fast/references/cms/app/wix/cms/collection-store.ts +158 -0
  40. package/skills/wix-headless-fast/references/cms/app/wix/cms/item-store.ts +93 -0
  41. package/skills/wix-headless-fast/references/cms/app/wix/cms/items-core.ts +177 -0
  42. package/skills/wix-headless-fast/references/cms/app/wix/cms/items.ts +13 -41
  43. package/skills/wix-headless-fast/references/cms/rest/items.ts +125 -0
  44. package/skills/wix-headless-fast/references/cms/seed/SEED.md +9 -0
  45. package/skills/wix-headless-fast/references/events/INSTRUCTIONS.md +239 -83
  46. package/skills/wix-headless-fast/references/events/app/components/events/EventConfirmationView.tsx +1 -1
  47. package/skills/wix-headless-fast/references/events/app/components/events/EventRegistrationView.tsx +7 -5
  48. package/skills/wix-headless-fast/references/events/app/components/events/EventsView.tsx +2 -2
  49. package/skills/wix-headless-fast/references/events/app/hooks/events/useEventRegistration.ts +28 -110
  50. package/skills/wix-headless-fast/references/events/app/hooks/events/useEvents.ts +19 -54
  51. package/skills/wix-headless-fast/references/events/app/wix/events/events-core.ts +130 -0
  52. package/skills/wix-headless-fast/references/events/app/wix/events/events-store.ts +97 -0
  53. package/skills/wix-headless-fast/references/events/app/wix/events/events.ts +13 -94
  54. package/skills/wix-headless-fast/references/events/app/wix/events/registration-core.ts +113 -0
  55. package/skills/wix-headless-fast/references/events/app/wix/events/registration-store.ts +156 -0
  56. package/skills/wix-headless-fast/references/events/app/wix/events/registration.ts +38 -69
  57. package/skills/wix-headless-fast/references/events/rest/events.ts +57 -0
  58. package/skills/wix-headless-fast/references/events/rest/registration.ts +83 -0
  59. package/skills/wix-headless-fast/references/events/seed/SEED.md +9 -0
  60. package/skills/wix-headless-fast/references/forms/INSTRUCTIONS.md +214 -26
  61. package/skills/wix-headless-fast/references/forms/app/hooks/forms/useWixForm.ts +35 -267
  62. package/skills/wix-headless-fast/references/forms/app/wix/forms/form-store.ts +332 -0
  63. package/skills/wix-headless-fast/references/forms/app/wix/forms/forms-core.ts +238 -0
  64. package/skills/wix-headless-fast/references/forms/app/wix/forms/forms.ts +28 -243
  65. package/skills/wix-headless-fast/references/forms/app/wix/forms/submissions-core.ts +216 -0
  66. package/skills/wix-headless-fast/references/forms/app/wix/forms/submissions.ts +32 -204
  67. package/skills/wix-headless-fast/references/forms/rest/forms.ts +34 -0
  68. package/skills/wix-headless-fast/references/forms/rest/submissions.ts +56 -0
  69. package/skills/wix-headless-fast/references/forms/seed/SEED.md +8 -0
  70. package/skills/wix-headless-fast/references/forms/seed/seed-forms.mjs +15 -8
  71. package/skills/wix-headless-fast/references/members/INSTRUCTIONS.md +314 -63
  72. package/skills/wix-headless-fast/references/members/app/components/members/LoginForm.tsx +1 -1
  73. package/skills/wix-headless-fast/references/members/app/wix/members/auth-core.ts +105 -0
  74. package/skills/wix-headless-fast/references/members/app/wix/members/auth.ts +36 -65
  75. package/skills/wix-headless-fast/references/members/app/wix/members/client.ts +1 -1
  76. package/skills/wix-headless-fast/references/members/app/wix/members/members-core.ts +43 -0
  77. package/skills/wix-headless-fast/references/members/app/wix/members/members.ts +11 -32
  78. package/skills/wix-headless-fast/references/members/rest/auth.ts +215 -0
  79. package/skills/wix-headless-fast/references/members/rest/members.ts +26 -0
  80. package/skills/wix-headless-fast/references/portfolio/INSTRUCTIONS.md +238 -59
  81. package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/CollectionProjectsView.tsx +2 -2
  82. package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/CollectionsView.tsx +2 -2
  83. package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/GalleryMedia.tsx +35 -0
  84. package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/ProjectDetailView.tsx +8 -38
  85. package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useCollectionProjects.ts +23 -50
  86. package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useCollections.ts +16 -34
  87. package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useProjectDetail.ts +22 -49
  88. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/collection-projects-store.ts +82 -0
  89. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/collections-store.ts +58 -0
  90. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/portfolio-core.ts +177 -0
  91. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/portfolio.ts +31 -118
  92. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/project-detail-store.ts +81 -0
  93. package/skills/wix-headless-fast/references/portfolio/rest/portfolio.ts +75 -0
  94. package/skills/wix-headless-fast/references/portfolio/seed/SEED.md +8 -0
  95. package/skills/wix-headless-fast/references/pricing-plans/INSTRUCTIONS.md +212 -51
  96. package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/PlanDetailView.tsx +9 -20
  97. package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/PlansView.tsx +7 -32
  98. package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/SubscribeButton.tsx +41 -0
  99. package/skills/wix-headless-fast/references/pricing-plans/app/hooks/pricing-plans/usePlanPurchase.ts +12 -33
  100. package/skills/wix-headless-fast/references/pricing-plans/app/hooks/pricing-plans/usePlans.ts +17 -34
  101. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plan-purchase-store.ts +54 -0
  102. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans-core.ts +106 -0
  103. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans-store.ts +64 -0
  104. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans.ts +7 -55
  105. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/purchase-core.ts +43 -0
  106. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/purchase.ts +11 -34
  107. package/skills/wix-headless-fast/references/pricing-plans/rest/plans.ts +31 -0
  108. package/skills/wix-headless-fast/references/pricing-plans/rest/purchase.ts +20 -0
  109. package/skills/wix-headless-fast/references/pricing-plans/seed/SEED.md +8 -0
  110. package/skills/wix-headless-fast/references/restaurants/INSTRUCTIONS.md +240 -47
  111. package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/MenuView.tsx +2 -2
  112. package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/OrderCartDrawer.tsx +2 -2
  113. package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useMenus.ts +14 -35
  114. package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useReservation.ts +28 -171
  115. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu-core.ts +155 -0
  116. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu-store.ts +89 -0
  117. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu.ts +23 -117
  118. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/ordering-core.ts +124 -0
  119. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/ordering.ts +45 -114
  120. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservation-store.ts +193 -0
  121. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservations-core.ts +94 -0
  122. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservations.ts +15 -87
  123. package/skills/wix-headless-fast/references/restaurants/rest/menu.ts +60 -0
  124. package/skills/wix-headless-fast/references/restaurants/rest/ordering.ts +169 -0
  125. package/skills/wix-headless-fast/references/restaurants/rest/reservations.ts +68 -0
  126. package/skills/wix-headless-fast/references/restaurants/seed/SEED.md +8 -0
  127. package/skills/wix-headless-fast/references/shared/SUPPLIED-CONTENT.md +23 -0
  128. package/skills/wix-headless-fast/references/shared/app/wix/media.ts +45 -4
  129. package/skills/wix-headless-fast/references/shared/app/wix/sdk.ts +13 -0
  130. package/skills/wix-headless-fast/references/shared/rest/client.ts +155 -0
  131. package/skills/wix-headless-fast/references/shared/rest/config.ts +3 -0
  132. package/skills/wix-headless-fast/references/shared/rest/media.ts +65 -0
  133. package/skills/wix-headless-fast/references/storefront/INSTRUCTIONS.md +182 -63
  134. package/skills/wix-headless-fast/references/storefront/app/components/storefront/OptionPicker.tsx +95 -0
  135. package/skills/wix-headless-fast/references/storefront/app/components/storefront/QuickAdd.tsx +29 -58
  136. package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useProductDetail.ts +29 -163
  137. package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useShop.ts +26 -300
  138. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart-core.ts +121 -0
  139. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart.ts +49 -162
  140. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog-core.ts +309 -0
  141. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog.ts +47 -370
  142. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/product-detail-store.ts +147 -0
  143. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/shop-store.ts +289 -0
  144. package/skills/wix-headless-fast/references/storefront/rest/cart.ts +132 -0
  145. package/skills/wix-headless-fast/references/storefront/rest/catalog.ts +123 -0
  146. package/skills/wix-headless-fast/references/storefront/seed/SEED.md +25 -9
  147. package/skills/wix-headless-fast/references/storefront/seed/seed-store.mjs +6 -35
  148. package/skills/wix-manage/references/ecommerce/ecom-shipping.md +1 -1
  149. package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +0 -40
  150. package/skills/wix-vibe-headless/references/storefront/seed/SEED.md +3 -0
  151. package/skills/wix-vibe-headless/references/storefront/seed/seed-store.cjs +6 -0
  152. package/skills/wix-headless-fast/install/pin-agents-md.mjs +0 -57
@@ -0,0 +1,43 @@
1
+ // Member rules and DTO mapping — transport-agnostic, imported by BOTH transports: ./members.ts (the
2
+ // SDK, managed Astro and React) and the REST twin in references/members/rest/members.ts (fetch, a
3
+ // static site or a port to another language). The one rule about who a member "is" on screen lives
4
+ // HERE, once. A raw member comes from the SDK (`_id`, `_createdDate`) or from REST (`id`,
5
+ // `createdDate`); the mapper accepts both. Imports are type-only so a strip to JS emits no imports.
6
+ import type { CurrentMember } from "./types";
7
+
8
+ /** A raw Members V1 member as either transport returns it. */
9
+ export type Raw = Record<string, any>;
10
+ /** Media value + size → https URL. Injected: the SDK transport scales through @wix/sdk, REST through the URL form. */
11
+ export type ImgSrc = (value: any, width: number, height: number) => string;
12
+
13
+ // Requested on the current-member read. FULL → loginEmail, status, the contact's names, the profile
14
+ // (nickname, photo), and the dates; the default PUBLIC fieldset has no login email and no names.
15
+ export const MEMBER_FIELDSETS = ["FULL"] as const;
16
+
17
+ /** The member's id on either transport; "" when the raw object isn't a member. */
18
+ export const memberId = (raw: Raw | undefined): string => raw?._id ?? raw?.id ?? "";
19
+
20
+ /**
21
+ * Raw member → CurrentMember. displayName is the first non-empty of nickname, "first last", the
22
+ * login email's local part, "Member". photoUrl is "" without a profile photo. memberSince is the
23
+ * creation date as "YYYY-MM-DD" ("" when unknown).
24
+ */
25
+ export function toCurrentMember(raw: Raw, imgSrc: ImgSrc): CurrentMember {
26
+ const nickname: string = raw.profile?.nickname ?? "";
27
+ const firstName: string = raw.contact?.firstName ?? "";
28
+ const lastName: string = raw.contact?.lastName ?? "";
29
+ const loginEmail: string = raw.loginEmail ?? "";
30
+ const fullName = [firstName, lastName].filter(Boolean).join(" ");
31
+ const created = raw._createdDate ?? raw.createdDate;
32
+ return {
33
+ id: memberId(raw),
34
+ loginEmail,
35
+ displayName: nickname || fullName || loginEmail.split("@")[0] || "Member",
36
+ firstName,
37
+ lastName,
38
+ nickname,
39
+ photoUrl: imgSrc(raw.profile?.photo, 200, 200),
40
+ contactId: raw.contactId ?? "",
41
+ memberSince: created ? new Date(created).toISOString().slice(0, 10) : "",
42
+ };
43
+ }
@@ -1,49 +1,28 @@
1
- // Current-member read (Wix Members) — the only file that touches raw member entities.
2
- // Identity vs. profile are different layers: "is this caller logged in?" needs no app
3
- // install, but getCurrentMember returns profile DATA only once the Wix Members Area app is
4
- // installed (the seed installs it). Copy as-is; extend by adding functions, not by editing
5
- // these.
6
- // docs: https://dev.wix.com/docs/api-reference/crm/members-contacts/members/members/get-my-member.md
1
+ // Current-member read (Wix Members) over the SDK — the only file that touches raw member entities
2
+ // on this transport. The DTO mapper lives in ./members-core (shared with the REST twin in
3
+ // references/members/rest/members.ts); this file is the transport only. Identity vs. profile are
4
+ // different layers: "is this caller logged in?" needs no app install, but getCurrentMember returns
5
+ // profile DATA only once the Wix Members Area app is installed (the seed installs it). Copy as-is;
6
+ // extend by adding functions, not by editing these.
7
+ // docs: https://dev.wix.com/docs/api-reference/crm/members-contacts/members/member-management/members/get-my-member.md
7
8
  import { membersApi } from "./client";
8
9
  import { imgSrc } from "../media";
10
+ import { MEMBER_FIELDSETS, memberId, toCurrentMember, type Raw } from "./members-core";
9
11
  import type { CurrentMember } from "./types";
10
12
 
11
- type Raw = Record<string, any>;
12
-
13
- function toCurrentMember(raw: Raw): CurrentMember {
14
- const nickname = raw.profile?.nickname ?? "";
15
- const firstName = raw.contact?.firstName ?? "";
16
- const lastName = raw.contact?.lastName ?? "";
17
- const loginEmail = raw.loginEmail ?? "";
18
- const fullName = [firstName, lastName].filter(Boolean).join(" ");
19
- return {
20
- id: raw._id ?? "",
21
- loginEmail,
22
- displayName: nickname || fullName || loginEmail.split("@")[0] || "Member",
23
- firstName,
24
- lastName,
25
- nickname,
26
- photoUrl: imgSrc(raw.profile?.photo, 200, 200),
27
- contactId: raw.contactId ?? "",
28
- memberSince: raw._createdDate
29
- ? new Date(raw._createdDate).toISOString().slice(0, 10)
30
- : "",
31
- };
32
- }
33
-
34
13
  /**
35
14
  * The current member, or null for an anonymous visitor — and null for a LOGGED-IN member
36
15
  * when the Members Area app isn't installed (setup, not a code bug; the seed installs it).
37
16
  * Never throws: anonymous is a normal state for the client-side custom-login gate.
38
- * ⚠️ The SDK export is getCurrentMember — the REST name "Get My Member" does not exist on
17
+ * The SDK export is getCurrentMember — the REST name "Get My Member" does not exist on
39
18
  * the module; getMyMember(...) throws `is not a function`, and only once a real member
40
19
  * loads the page.
41
20
  */
42
21
  export async function fetchCurrentMember(): Promise<CurrentMember | null> {
43
22
  try {
44
- const res = await membersApi.getCurrentMember({ fieldsets: ["FULL"] });
23
+ const res = await membersApi.getCurrentMember({ fieldsets: [...MEMBER_FIELDSETS] as any });
45
24
  const raw = res.member as Raw | undefined;
46
- return raw?._id ? toCurrentMember(raw) : null;
25
+ return raw && memberId(raw) ? toCurrentMember(raw, imgSrc) : null;
47
26
  } catch {
48
27
  return null;
49
28
  }
@@ -0,0 +1,215 @@
1
+ // Member authentication over REST — the twin of app/wix/members/auth.ts. Same exports, same
2
+ // LoginResult; the state and error-code rules come from auth-core (the SAME file the SDK transport
3
+ // uses). The custom credential flow, as the docs order it: login/register with the VISITOR token →
4
+ // a one-shot sessionToken (5-minute life) → a redirect session that authorizes it (PKCE,
5
+ // web_message) → an authorization code → POST /oauth2/token grantType authorization_code → MEMBER
6
+ // tokens, adopted by ./client.js so every later call (the current-member read, a member's own
7
+ // records) runs as the member and the refresh keeps the member. All from a page with the public
8
+ // client id; nothing elevated. Porting: keep the paths and bodies; the exchange is three calls.
9
+ // docs: https://dev.wix.com/docs/go-headless/authentication/members/custom-login-page/build-a-custom-login-page-rest.md
10
+ // docs: https://dev.wix.com/docs/api-reference/business-management/headless/redirects/create-redirect-session.md
11
+ import { WIX_CLIENT_ID } from "./config.js";
12
+ import { WixApiError, clearTokens, loadTokens, mintTokens, setTokens, wixRequest, type Tokens } from "./client.js";
13
+ import {
14
+ NO_SESSION_ERROR,
15
+ authOutcome,
16
+ failure,
17
+ loginErrorCode,
18
+ registerErrorCode,
19
+ toLoginResult,
20
+ type LoginResult,
21
+ type LoginState,
22
+ type RawAuth,
23
+ } from "./auth-core.js";
24
+
25
+ export type { LoginResult, LoginState };
26
+
27
+ const REDIRECT_SESSION = "/headless/v1/redirect-session";
28
+ /** The stateToken of a login/registration that stopped at email verification — verifyMemberEmail continues it. */
29
+ let pendingStateToken: string | null = null;
30
+
31
+ /** True when the persisted token set is a member's (no network) — the store skips the profile read when false. */
32
+ export function loggedInHint(): boolean {
33
+ return loadTokens()?.role === "member";
34
+ }
35
+
36
+ // A SUCCESS carries the one-shot session token: exchange it for member tokens and adopt them. Other
37
+ // states pass through; a verification state keeps its stateToken for verifyMemberEmail.
38
+ async function finish(raw: RawAuth): Promise<LoginResult> {
39
+ const outcome = authOutcome(raw);
40
+ if (outcome.state === "SUCCESS") {
41
+ pendingStateToken = null;
42
+ if (!outcome.sessionToken) return failure(NO_SESSION_ERROR);
43
+ setTokens(await memberTokensForSession(outcome.sessionToken));
44
+ return { state: "SUCCESS" };
45
+ }
46
+ if (outcome.stateToken) pendingStateToken = outcome.stateToken;
47
+ return toLoginResult(outcome);
48
+ }
49
+
50
+ const message = (e: unknown): string => (e instanceof Error ? e.message : String(e));
51
+
52
+ /**
53
+ * Sign in with email + password. A wrong password or unknown email is a FAILURE result (errorCode
54
+ * invalidPassword / invalidEmail, Wix's message in `error`), never a throw.
55
+ * POST /_api/iam/authentication/v2/login { loginId: { email }, password } → { state, sessionToken?, stateToken? }
56
+ */
57
+ export async function loginMember(email: string, password: string): Promise<LoginResult> {
58
+ let raw: RawAuth;
59
+ try {
60
+ raw = await wixRequest<RawAuth>("/_api/iam/authentication/v2/login", { body: { loginId: { email }, password } });
61
+ } catch (e) {
62
+ return failure(message(e), e instanceof WixApiError ? loginErrorCode(e.code) : undefined);
63
+ }
64
+ return finish(raw);
65
+ }
66
+
67
+ /**
68
+ * Sign up with email + password (+ first/last name). An email already registered is a FAILURE with
69
+ * errorCode emailAlreadyExists. Whether the result is SUCCESS, EMAIL_VERIFICATION_REQUIRED, or
70
+ * OWNER_APPROVAL_REQUIRED is the site's signup policy (dashboard), not the caller's.
71
+ * POST /_api/iam/authentication/v2/register { loginId: { email }, password, profile?: { firstName, lastName } }
72
+ */
73
+ export async function registerMember(
74
+ email: string,
75
+ password: string,
76
+ profile?: { firstName?: string; lastName?: string },
77
+ ): Promise<LoginResult> {
78
+ let raw: RawAuth;
79
+ try {
80
+ raw = await wixRequest<RawAuth>("/_api/iam/authentication/v2/register", {
81
+ body: { loginId: { email }, password, ...(profile ? { profile } : {}) },
82
+ });
83
+ } catch (e) {
84
+ return failure(message(e), e instanceof WixApiError ? registerErrorCode(e.code, e.details) : undefined);
85
+ }
86
+ return finish(raw);
87
+ }
88
+
89
+ /**
90
+ * Continue an EMAIL_VERIFICATION_REQUIRED flow with the code from the email; the state token of the
91
+ * last login/registration is kept here. A FAILURE when nothing is waiting or the code is wrong.
92
+ * POST /_api/iam/verification/v1/auth/verify { code, stateToken } → { state, sessionToken?, stateToken? }
93
+ */
94
+ export async function verifyMemberEmail(verificationCode: string): Promise<LoginResult> {
95
+ if (!pendingStateToken) return failure("No sign-in is waiting for a verification code.");
96
+ let raw: RawAuth;
97
+ try {
98
+ raw = await wixRequest<RawAuth>("/_api/iam/verification/v1/auth/verify", { body: { code: verificationCode, stateToken: pendingStateToken } });
99
+ } catch (e) {
100
+ return failure(message(e));
101
+ }
102
+ return finish(raw);
103
+ }
104
+
105
+ /**
106
+ * The Wix logout URL for the current member — navigating the document there ends the Wix session
107
+ * and lands on `returnTo` (an absolute URL). Called with the MEMBER token.
108
+ * POST /headless/v1/redirect-session { logout: { clientId }, callbacks: { postFlowUrl } } → { redirectSession: { fullUrl } }
109
+ */
110
+ export async function logoutUrl(returnTo: string): Promise<string> {
111
+ const res = await wixRequest<RawAuth>(REDIRECT_SESSION, { body: { logout: { clientId: WIX_CLIENT_ID }, callbacks: { postFlowUrl: returnTo } } });
112
+ const url = res?.redirectSession?.fullUrl;
113
+ if (!url) throw new Error("Logout couldn't start: no redirect URL returned.");
114
+ return url;
115
+ }
116
+
117
+ /** Log out: drop the member tokens (the next call is a fresh visitor) and navigate to Wix's logout, which returns to `returnTo`. */
118
+ export async function logoutMember(returnTo = "/"): Promise<void> {
119
+ const target = typeof window !== "undefined" ? new URL(returnTo, window.location.origin).href : returnTo;
120
+ const url = await logoutUrl(target);
121
+ clearTokens();
122
+ if (typeof window !== "undefined") window.location.assign(url);
123
+ }
124
+
125
+ /**
126
+ * sessionToken → member tokens. Three calls, as the docs order them:
127
+ * 1. POST /headless/v1/redirect-session
128
+ * { auth: { authRequest: { clientId, codeChallenge, codeChallengeMethod: "S256", responseMode: "web_message",
129
+ * responseType: "code", scope: "offline_access", state, sessionToken }, prompt: "none" } }
130
+ * → { redirectSession: { fullUrl } } (with the visitor token)
131
+ * 2. fullUrl is a page whose script posts { code, state, error } to its parent — a hidden iframe in a
132
+ * browser (its origin must be one of the OAuth app's allowed domains, or the message is dropped
133
+ * and this times out); without a DOM (a server-side port, a Node check) the same page is fetched
134
+ * and read.
135
+ * 3. POST /oauth2/token { clientId, grantType: "authorization_code", code, codeVerifier }
136
+ * → member access + refresh tokens (role "member"); the code is single-use.
137
+ */
138
+ export async function memberTokensForSession(sessionToken: string): Promise<Tokens> {
139
+ const { verifier, challenge } = await pkce();
140
+ const state = randomString();
141
+ const res = await wixRequest<RawAuth>(REDIRECT_SESSION, {
142
+ body: {
143
+ auth: {
144
+ authRequest: {
145
+ clientId: WIX_CLIENT_ID,
146
+ codeChallenge: challenge,
147
+ codeChallengeMethod: "S256",
148
+ responseMode: "web_message",
149
+ responseType: "code",
150
+ scope: "offline_access",
151
+ state,
152
+ sessionToken,
153
+ },
154
+ prompt: "none",
155
+ },
156
+ },
157
+ });
158
+ const url: string | undefined = res?.redirectSession?.fullUrl;
159
+ if (!url) throw new Error("Login couldn't finish: no authorization URL returned.");
160
+ const code = await authorizationCode(url, state);
161
+ return mintTokens({ grantType: "authorization_code", code, codeVerifier: verifier }, "member");
162
+ }
163
+
164
+ // The authorize page posts { code, state, error }. Browser: a hidden iframe and a message listener
165
+ // keyed on our state (the SDK's own mechanism). No DOM: fetch the page and read the same three values.
166
+ async function authorizationCode(url: string, state: string): Promise<string> {
167
+ if (typeof document === "undefined") {
168
+ const html = await (await fetch(url)).text();
169
+ const field = (name: string) => html.match(new RegExp(`'${name}':\\s*'([^']*)'`))?.[1];
170
+ const error = field("error");
171
+ if (error) throw new Error(`Login was refused: ${error}`);
172
+ const code = field("code");
173
+ if (!code || field("state") !== state) throw new Error("Login couldn't finish: no authorization code.");
174
+ return code;
175
+ }
176
+ return new Promise<string>((resolve, reject) => {
177
+ const frame = document.createElement("iframe");
178
+ frame.style.display = "none";
179
+ const done = () => {
180
+ window.removeEventListener("message", onMessage);
181
+ clearTimeout(timer);
182
+ frame.remove();
183
+ };
184
+ const onMessage = (e: MessageEvent) => {
185
+ const data = e.data as { code?: string; state?: string; error?: string } | null;
186
+ if (!data || data.state !== state) return; // a message not meant for us
187
+ done();
188
+ if (data.error) reject(new Error(`Login was refused: ${data.error}`));
189
+ else if (data.code) resolve(data.code);
190
+ else reject(new Error("Login couldn't finish: no authorization code."));
191
+ };
192
+ const timer = setTimeout(() => {
193
+ done();
194
+ reject(new Error("Login timed out. Is this page's origin on the OAuth app's allowed domains?"));
195
+ }, 120_000);
196
+ window.addEventListener("message", onMessage);
197
+ frame.src = url;
198
+ document.body.appendChild(frame);
199
+ });
200
+ }
201
+
202
+ // PKCE: a 43-char base64url verifier, its SHA-256 as the S256 challenge. Web Crypto (browser + Node).
203
+ function base64url(bytes: Uint8Array): string {
204
+ let s = "";
205
+ for (const b of bytes) s += String.fromCharCode(b);
206
+ return btoa(s).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
207
+ }
208
+ function randomString(): string {
209
+ return base64url(crypto.getRandomValues(new Uint8Array(32)));
210
+ }
211
+ async function pkce(): Promise<{ verifier: string; challenge: string }> {
212
+ const verifier = randomString();
213
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier));
214
+ return { verifier, challenge: base64url(new Uint8Array(digest)) };
215
+ }
@@ -0,0 +1,26 @@
1
+ // Current-member read over REST — the twin of app/wix/members/members.ts. Same export, same DTO;
2
+ // the mapper comes from members-core (the SAME file the SDK transport uses, deployed flat next to
3
+ // this one by deploy.mjs --stack static), so this file is only the transport: one GET. It runs with
4
+ // whatever token ./client.js holds — a member's after a rest/auth.ts login → the profile; a
5
+ // visitor's → 403 → null. Porting: keep the path and the fieldsets, port the core once.
6
+ // docs: https://dev.wix.com/docs/api-reference/crm/members-contacts/members/member-management/members/get-my-member.md
7
+ import { wixRequest } from "./client.js";
8
+ import { imgSrc } from "./media.js";
9
+ import { MEMBER_FIELDSETS, memberId, toCurrentMember, type Raw } from "./members-core.js";
10
+ import type { CurrentMember } from "./types.js";
11
+
12
+ /**
13
+ * The current member, or null: for a visitor (the call is refused — "Missing site member id"), for
14
+ * a logged-in member whose site lacks the Members Area app (no profile), and on any failure —
15
+ * anonymous is a normal state, never an error.
16
+ * GET /members/v1/members/my?fieldsets=FULL → { member: { id, loginEmail, contactId, contact: { firstName, lastName }, profile: { nickname, photo? }, createdDate, … } }
17
+ */
18
+ export async function fetchCurrentMember(): Promise<CurrentMember | null> {
19
+ try {
20
+ const res = await wixRequest<Raw>("/members/v1/members/my", { method: "GET", query: { fieldsets: MEMBER_FIELDSETS } });
21
+ const raw: Raw | undefined = res?.member;
22
+ return raw && memberId(raw) ? toCurrentMember(raw, imgSrc) : null;
23
+ } catch {
24
+ return null;
25
+ }
26
+ }