@frontdesk-africa/store-js 0.1.0 → 0.2.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/README.md +21 -0
- package/dist/index.d.mts +65 -1
- package/dist/index.d.ts +65 -1
- package/dist/index.js +10 -1
- package/dist/index.mjs +10 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -49,6 +49,27 @@ const item = await store.product('blue-mug')
|
|
|
49
49
|
|
|
50
50
|
You never choose the workspace: the key does. Any workspace ref you send is ignored.
|
|
51
51
|
|
|
52
|
+
### Products sold in several dimensions
|
|
53
|
+
|
|
54
|
+
A product whose merchant sells it by Size *and* Colour carries an `options` array: one entry per
|
|
55
|
+
group, each with its own `display` (`dropdown`, `text`, `color`, `image`) and its list of values.
|
|
56
|
+
The buyer picks one value per group, and that set resolves to a single variant — the one whose
|
|
57
|
+
`optionValueRefs` holds exactly those value refs. Checkout is unchanged: you still send that
|
|
58
|
+
variant's `ref`.
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
const item = await store.product('shirt-dress')
|
|
62
|
+
|
|
63
|
+
// options is absent on an ordinary product — render item.variants as a flat list then.
|
|
64
|
+
const picked = { [item.options![0].ref]: 'value-ref-small', [item.options![1].ref]: 'value-ref-teal' }
|
|
65
|
+
const chosen = item.variants.find(
|
|
66
|
+
(v) => v.optionValueRefs?.length === 2 && v.optionValueRefs.every((r) => Object.values(picked).includes(r)),
|
|
67
|
+
)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Dim a value when every variant still holding it is `soldOut` (or has no price in the buyer's
|
|
71
|
+
currency), and swap your gallery to a value's `media` when it is selected.
|
|
72
|
+
|
|
52
73
|
## Checkout (server)
|
|
53
74
|
|
|
54
75
|
You never handle card details and you never price a cart. The buyer pays on a FrontDesk-hosted page
|
package/dist/index.d.mts
CHANGED
|
@@ -576,7 +576,13 @@ type PublicProductInfo = Omit<ProductInfoView, 'hsCode'>;
|
|
|
576
576
|
/** How a package product's options are chosen: one tier (radio) vs a menu (pick many + quantity). */
|
|
577
577
|
type SelectionMode = 'choose_one' | 'choose_many';
|
|
578
578
|
/** How the storefront draws a product's option picker. A variant missing an image/hex falls back to text. */
|
|
579
|
-
type VariantDisplay = 'text' | 'image' | 'color';
|
|
579
|
+
type VariantDisplay = 'text' | 'image' | 'color' | 'dropdown';
|
|
580
|
+
/**
|
|
581
|
+
* Multi-group variations: how ONE variation group's picker is drawn. Per-group (Size = dropdown,
|
|
582
|
+
* Color = swatches), unlike the legacy product-level VariantDisplay, which keeps governing products
|
|
583
|
+
* that have no groups. A value missing its hex/image falls back to a text chip.
|
|
584
|
+
*/
|
|
585
|
+
type OptionGroupDisplay = 'dropdown' | 'text' | 'color' | 'image';
|
|
580
586
|
/** Product/option payment modes: the shipped package modes + Pay Later (Save-For-It deferred). */
|
|
581
587
|
type ProductPricingMode = PackagePricingMode | 'pay_later';
|
|
582
588
|
interface ProductAddonOption {
|
|
@@ -645,6 +651,23 @@ interface StoreAvailabilitySummary {
|
|
|
645
651
|
leadTimeMinutes: number;
|
|
646
652
|
}
|
|
647
653
|
type PreorderDepositType = 'percent' | 'fixed';
|
|
654
|
+
interface PublicProductOptionValue {
|
|
655
|
+
ref: string;
|
|
656
|
+
label: string;
|
|
657
|
+
/** '#RRGGBB' for the colour swatch under display 'color'; absent/null falls back to a text chip. */
|
|
658
|
+
swatchHex?: string | null;
|
|
659
|
+
/** Selecting this value swaps the product gallery to these images. Absent = keep the gallery. */
|
|
660
|
+
media?: string[];
|
|
661
|
+
}
|
|
662
|
+
/** One variation axis of a multi-group product (e.g. Size, Color). The buyer picks one value per
|
|
663
|
+
* group; the picked set resolves to exactly one variant (see PublicProductVariant.optionValueRefs). */
|
|
664
|
+
interface PublicProductOptionGroup {
|
|
665
|
+
ref: string;
|
|
666
|
+
name: string;
|
|
667
|
+
/** How to draw THIS group's picker: a select, text chips, colour swatches, or image tiles. */
|
|
668
|
+
display: OptionGroupDisplay;
|
|
669
|
+
values: PublicProductOptionValue[];
|
|
670
|
+
}
|
|
648
671
|
interface PublicProductVariant {
|
|
649
672
|
ref: string;
|
|
650
673
|
name: string;
|
|
@@ -668,6 +691,9 @@ interface PublicProductVariant {
|
|
|
668
691
|
availableQty?: number | null;
|
|
669
692
|
/** '#RRGGBB' for the colour swatch; null under 'color' mode falls back to a text chip. */
|
|
670
693
|
swatchHex?: string | null;
|
|
694
|
+
/** Multi-group products: the option values this combination is made of (one value ref per group,
|
|
695
|
+
* in group sort order). Absent on ungrouped products — render the flat `variants` picker then. */
|
|
696
|
+
optionValueRefs?: string[];
|
|
671
697
|
description?: string | null;
|
|
672
698
|
badge?: string | null;
|
|
673
699
|
features?: PackageFeature[];
|
|
@@ -702,6 +728,10 @@ interface PublicProduct {
|
|
|
702
728
|
selectionMode?: SelectionMode;
|
|
703
729
|
/** How the option picker is drawn: text chips (default), image swatches, or colour swatches. */
|
|
704
730
|
variantDisplay?: VariantDisplay;
|
|
731
|
+
/** Multi-group variations: one picker per group, each drawn per its `display`. When present, the
|
|
732
|
+
* buyer picks one value per group and the picks resolve to a variant via `optionValueRefs`; when
|
|
733
|
+
* absent, render the flat `variants` list exactly as before (existing storefronts keep working). */
|
|
734
|
+
options?: PublicProductOptionGroup[];
|
|
705
735
|
schedulingEnabled?: boolean;
|
|
706
736
|
/** Add-on groups the buyer can pick from (option-scoped groups carry variantRef). */
|
|
707
737
|
addonGroups?: ProductAddonGroup[];
|
|
@@ -1042,6 +1072,34 @@ interface StoreCheckoutSessionView {
|
|
|
1042
1072
|
createdAt: string;
|
|
1043
1073
|
}
|
|
1044
1074
|
|
|
1075
|
+
/**
|
|
1076
|
+
* `POST /v1/store/customers/link` — attach the caller's own signed-in user to this workspace's
|
|
1077
|
+
* contacts (Supabase Phase 1c; design doc §7.6).
|
|
1078
|
+
*
|
|
1079
|
+
* TRUST MODEL: the caller holds a secret key, i.e. IS the merchant (or their installed backend).
|
|
1080
|
+
* They can already create and edit their own contacts in the portal, so accepting their assertion
|
|
1081
|
+
* that `email` was verified by THEIR auth system delegates nothing they do not have. The identity
|
|
1082
|
+
* being asserted must still be verified on the caller's side — that is stated at the API boundary,
|
|
1083
|
+
* not enforced here, because we cannot see their auth system.
|
|
1084
|
+
*/
|
|
1085
|
+
interface StoreCustomerLinkInput {
|
|
1086
|
+
/** The email the CALLER's auth system verified. Never a guessed or user-typed-but-unverified one. */
|
|
1087
|
+
email: string;
|
|
1088
|
+
/** The caller's own user id (e.g. a Supabase Auth user id). Idempotency key: same ref, same link. */
|
|
1089
|
+
externalRef: string;
|
|
1090
|
+
/** Optional display name; wins over an empty contact name, never overwrites a set one. */
|
|
1091
|
+
name?: string;
|
|
1092
|
+
phone?: string;
|
|
1093
|
+
}
|
|
1094
|
+
interface StoreCustomerLinkView {
|
|
1095
|
+
/** The buyer's account ref — what `customer.linked` carries and checkout accepts. */
|
|
1096
|
+
customerRef: string;
|
|
1097
|
+
/** The workspace contact this landed on. */
|
|
1098
|
+
contactRef: string;
|
|
1099
|
+
/** True when this call created the link; false when the externalRef was already linked. */
|
|
1100
|
+
alreadyLinked: boolean;
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1045
1103
|
/** A resolved commission rate (after applying any per-workspace override over the platform default). */
|
|
1046
1104
|
interface EventCommissionConfig {
|
|
1047
1105
|
/** Percentage cut on the gross (0..100). */
|
|
@@ -2398,6 +2456,12 @@ declare function createStoreClient(opts: StoreClientOptions): {
|
|
|
2398
2456
|
* stock until the session lapses. Cancelling an already-paid checkout returns 409.
|
|
2399
2457
|
*/
|
|
2400
2458
|
cancelCheckout: (ref: string) => Promise<StoreCheckoutSessionView>;
|
|
2459
|
+
/**
|
|
2460
|
+
* Attach YOUR OWN signed-in user to this workspace's contacts. SERVER ONLY. Only send an email
|
|
2461
|
+
* your auth system verified. Idempotent per externalRef, and the response is identical whether
|
|
2462
|
+
* the contact existed before — it cannot be used to probe who is already a customer.
|
|
2463
|
+
*/
|
|
2464
|
+
linkCustomer: (input: StoreCustomerLinkInput) => Promise<StoreCustomerLinkView>;
|
|
2401
2465
|
};
|
|
2402
2466
|
type StoreClient = ReturnType<typeof createStoreClient>;
|
|
2403
2467
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -576,7 +576,13 @@ type PublicProductInfo = Omit<ProductInfoView, 'hsCode'>;
|
|
|
576
576
|
/** How a package product's options are chosen: one tier (radio) vs a menu (pick many + quantity). */
|
|
577
577
|
type SelectionMode = 'choose_one' | 'choose_many';
|
|
578
578
|
/** How the storefront draws a product's option picker. A variant missing an image/hex falls back to text. */
|
|
579
|
-
type VariantDisplay = 'text' | 'image' | 'color';
|
|
579
|
+
type VariantDisplay = 'text' | 'image' | 'color' | 'dropdown';
|
|
580
|
+
/**
|
|
581
|
+
* Multi-group variations: how ONE variation group's picker is drawn. Per-group (Size = dropdown,
|
|
582
|
+
* Color = swatches), unlike the legacy product-level VariantDisplay, which keeps governing products
|
|
583
|
+
* that have no groups. A value missing its hex/image falls back to a text chip.
|
|
584
|
+
*/
|
|
585
|
+
type OptionGroupDisplay = 'dropdown' | 'text' | 'color' | 'image';
|
|
580
586
|
/** Product/option payment modes: the shipped package modes + Pay Later (Save-For-It deferred). */
|
|
581
587
|
type ProductPricingMode = PackagePricingMode | 'pay_later';
|
|
582
588
|
interface ProductAddonOption {
|
|
@@ -645,6 +651,23 @@ interface StoreAvailabilitySummary {
|
|
|
645
651
|
leadTimeMinutes: number;
|
|
646
652
|
}
|
|
647
653
|
type PreorderDepositType = 'percent' | 'fixed';
|
|
654
|
+
interface PublicProductOptionValue {
|
|
655
|
+
ref: string;
|
|
656
|
+
label: string;
|
|
657
|
+
/** '#RRGGBB' for the colour swatch under display 'color'; absent/null falls back to a text chip. */
|
|
658
|
+
swatchHex?: string | null;
|
|
659
|
+
/** Selecting this value swaps the product gallery to these images. Absent = keep the gallery. */
|
|
660
|
+
media?: string[];
|
|
661
|
+
}
|
|
662
|
+
/** One variation axis of a multi-group product (e.g. Size, Color). The buyer picks one value per
|
|
663
|
+
* group; the picked set resolves to exactly one variant (see PublicProductVariant.optionValueRefs). */
|
|
664
|
+
interface PublicProductOptionGroup {
|
|
665
|
+
ref: string;
|
|
666
|
+
name: string;
|
|
667
|
+
/** How to draw THIS group's picker: a select, text chips, colour swatches, or image tiles. */
|
|
668
|
+
display: OptionGroupDisplay;
|
|
669
|
+
values: PublicProductOptionValue[];
|
|
670
|
+
}
|
|
648
671
|
interface PublicProductVariant {
|
|
649
672
|
ref: string;
|
|
650
673
|
name: string;
|
|
@@ -668,6 +691,9 @@ interface PublicProductVariant {
|
|
|
668
691
|
availableQty?: number | null;
|
|
669
692
|
/** '#RRGGBB' for the colour swatch; null under 'color' mode falls back to a text chip. */
|
|
670
693
|
swatchHex?: string | null;
|
|
694
|
+
/** Multi-group products: the option values this combination is made of (one value ref per group,
|
|
695
|
+
* in group sort order). Absent on ungrouped products — render the flat `variants` picker then. */
|
|
696
|
+
optionValueRefs?: string[];
|
|
671
697
|
description?: string | null;
|
|
672
698
|
badge?: string | null;
|
|
673
699
|
features?: PackageFeature[];
|
|
@@ -702,6 +728,10 @@ interface PublicProduct {
|
|
|
702
728
|
selectionMode?: SelectionMode;
|
|
703
729
|
/** How the option picker is drawn: text chips (default), image swatches, or colour swatches. */
|
|
704
730
|
variantDisplay?: VariantDisplay;
|
|
731
|
+
/** Multi-group variations: one picker per group, each drawn per its `display`. When present, the
|
|
732
|
+
* buyer picks one value per group and the picks resolve to a variant via `optionValueRefs`; when
|
|
733
|
+
* absent, render the flat `variants` list exactly as before (existing storefronts keep working). */
|
|
734
|
+
options?: PublicProductOptionGroup[];
|
|
705
735
|
schedulingEnabled?: boolean;
|
|
706
736
|
/** Add-on groups the buyer can pick from (option-scoped groups carry variantRef). */
|
|
707
737
|
addonGroups?: ProductAddonGroup[];
|
|
@@ -1042,6 +1072,34 @@ interface StoreCheckoutSessionView {
|
|
|
1042
1072
|
createdAt: string;
|
|
1043
1073
|
}
|
|
1044
1074
|
|
|
1075
|
+
/**
|
|
1076
|
+
* `POST /v1/store/customers/link` — attach the caller's own signed-in user to this workspace's
|
|
1077
|
+
* contacts (Supabase Phase 1c; design doc §7.6).
|
|
1078
|
+
*
|
|
1079
|
+
* TRUST MODEL: the caller holds a secret key, i.e. IS the merchant (or their installed backend).
|
|
1080
|
+
* They can already create and edit their own contacts in the portal, so accepting their assertion
|
|
1081
|
+
* that `email` was verified by THEIR auth system delegates nothing they do not have. The identity
|
|
1082
|
+
* being asserted must still be verified on the caller's side — that is stated at the API boundary,
|
|
1083
|
+
* not enforced here, because we cannot see their auth system.
|
|
1084
|
+
*/
|
|
1085
|
+
interface StoreCustomerLinkInput {
|
|
1086
|
+
/** The email the CALLER's auth system verified. Never a guessed or user-typed-but-unverified one. */
|
|
1087
|
+
email: string;
|
|
1088
|
+
/** The caller's own user id (e.g. a Supabase Auth user id). Idempotency key: same ref, same link. */
|
|
1089
|
+
externalRef: string;
|
|
1090
|
+
/** Optional display name; wins over an empty contact name, never overwrites a set one. */
|
|
1091
|
+
name?: string;
|
|
1092
|
+
phone?: string;
|
|
1093
|
+
}
|
|
1094
|
+
interface StoreCustomerLinkView {
|
|
1095
|
+
/** The buyer's account ref — what `customer.linked` carries and checkout accepts. */
|
|
1096
|
+
customerRef: string;
|
|
1097
|
+
/** The workspace contact this landed on. */
|
|
1098
|
+
contactRef: string;
|
|
1099
|
+
/** True when this call created the link; false when the externalRef was already linked. */
|
|
1100
|
+
alreadyLinked: boolean;
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1045
1103
|
/** A resolved commission rate (after applying any per-workspace override over the platform default). */
|
|
1046
1104
|
interface EventCommissionConfig {
|
|
1047
1105
|
/** Percentage cut on the gross (0..100). */
|
|
@@ -2398,6 +2456,12 @@ declare function createStoreClient(opts: StoreClientOptions): {
|
|
|
2398
2456
|
* stock until the session lapses. Cancelling an already-paid checkout returns 409.
|
|
2399
2457
|
*/
|
|
2400
2458
|
cancelCheckout: (ref: string) => Promise<StoreCheckoutSessionView>;
|
|
2459
|
+
/**
|
|
2460
|
+
* Attach YOUR OWN signed-in user to this workspace's contacts. SERVER ONLY. Only send an email
|
|
2461
|
+
* your auth system verified. Idempotent per externalRef, and the response is identical whether
|
|
2462
|
+
* the contact existed before — it cannot be used to probe who is already a customer.
|
|
2463
|
+
*/
|
|
2464
|
+
linkCustomer: (input: StoreCustomerLinkInput) => Promise<StoreCustomerLinkView>;
|
|
2401
2465
|
};
|
|
2402
2466
|
type StoreClient = ReturnType<typeof createStoreClient>;
|
|
2403
2467
|
/**
|
package/dist/index.js
CHANGED
|
@@ -151,7 +151,16 @@ function createStoreClient(opts) {
|
|
|
151
151
|
*/
|
|
152
152
|
cancelCheckout: /* @__PURE__ */ __name((ref) => request(`/store/checkouts/${enc(ref)}/cancel`, {
|
|
153
153
|
method: "POST"
|
|
154
|
-
}), "cancelCheckout")
|
|
154
|
+
}), "cancelCheckout"),
|
|
155
|
+
/**
|
|
156
|
+
* Attach YOUR OWN signed-in user to this workspace's contacts. SERVER ONLY. Only send an email
|
|
157
|
+
* your auth system verified. Idempotent per externalRef, and the response is identical whether
|
|
158
|
+
* the contact existed before — it cannot be used to probe who is already a customer.
|
|
159
|
+
*/
|
|
160
|
+
linkCustomer: /* @__PURE__ */ __name((input) => request("/store/customers/link", {
|
|
161
|
+
method: "POST",
|
|
162
|
+
body: JSON.stringify(input)
|
|
163
|
+
}), "linkCustomer")
|
|
155
164
|
};
|
|
156
165
|
}
|
|
157
166
|
__name(createStoreClient, "createStoreClient");
|
package/dist/index.mjs
CHANGED
|
@@ -117,7 +117,16 @@ function createStoreClient(opts) {
|
|
|
117
117
|
*/
|
|
118
118
|
cancelCheckout: /* @__PURE__ */ __name((ref) => request(`/store/checkouts/${enc(ref)}/cancel`, {
|
|
119
119
|
method: "POST"
|
|
120
|
-
}), "cancelCheckout")
|
|
120
|
+
}), "cancelCheckout"),
|
|
121
|
+
/**
|
|
122
|
+
* Attach YOUR OWN signed-in user to this workspace's contacts. SERVER ONLY. Only send an email
|
|
123
|
+
* your auth system verified. Idempotent per externalRef, and the response is identical whether
|
|
124
|
+
* the contact existed before — it cannot be used to probe who is already a customer.
|
|
125
|
+
*/
|
|
126
|
+
linkCustomer: /* @__PURE__ */ __name((input) => request("/store/customers/link", {
|
|
127
|
+
method: "POST",
|
|
128
|
+
body: JSON.stringify(input)
|
|
129
|
+
}), "linkCustomer")
|
|
121
130
|
};
|
|
122
131
|
}
|
|
123
132
|
__name(createStoreClient, "createStoreClient");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontdesk-africa/store-js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Typed client for the FrontDesk Storefront API: catalogue, events, forms, hosted checkout and signed webhooks.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://api.frontdesk.africa/v1/store/docs",
|