@umxr/bagsy 0.0.0-stage → 0.1.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.
@@ -0,0 +1,309 @@
1
+ import { B as BagsyOptions, c as components, L as ListRef, P as PageOptions, b as ProductRef, I as ItemOptions, V as VariantRef, S as SavedLookup, O as Owner } from './types-C1ZgQf5w.js';
2
+
3
+ /**
4
+ * The Bagsy client. Every method is a thin wrapper over a standalone function
5
+ * in `operations` — use those directly if you want only what you import.
6
+ */
7
+ declare function createBagsy(options: BagsyOptions): {
8
+ lists: {
9
+ /** The owner's lists, default first. */
10
+ list: () => Promise<{
11
+ id: string;
12
+ name: string;
13
+ isDefault: boolean;
14
+ visibility: "private" | "shared";
15
+ itemCount: number;
16
+ shareToken?: string | null;
17
+ createdAt: string;
18
+ updatedAt: string;
19
+ items?: components["schemas"]["ItemPage"];
20
+ }[]>;
21
+ get: (listId?: ListRef, opts?: {
22
+ includeItems?: boolean;
23
+ }) => Promise<{
24
+ id: string;
25
+ name: string;
26
+ isDefault: boolean;
27
+ visibility: "private" | "shared";
28
+ itemCount: number;
29
+ shareToken?: string | null;
30
+ createdAt: string;
31
+ updatedAt: string;
32
+ items?: components["schemas"]["ItemPage"];
33
+ }>;
34
+ create: (name: string) => Promise<{
35
+ id: string;
36
+ name: string;
37
+ isDefault: boolean;
38
+ visibility: "private" | "shared";
39
+ itemCount: number;
40
+ shareToken?: string | null;
41
+ createdAt: string;
42
+ updatedAt: string;
43
+ items?: components["schemas"]["ItemPage"];
44
+ }>;
45
+ rename: (listId: ListRef, name: string) => Promise<{
46
+ id: string;
47
+ name: string;
48
+ isDefault: boolean;
49
+ visibility: "private" | "shared";
50
+ itemCount: number;
51
+ shareToken?: string | null;
52
+ createdAt: string;
53
+ updatedAt: string;
54
+ items?: components["schemas"]["ItemPage"];
55
+ }>;
56
+ delete: (listId: ListRef) => Promise<void>;
57
+ /** The list's share token, created on first share. `rotate: true` issues a new one. */
58
+ share: (listId?: ListRef, opts?: {
59
+ rotate?: boolean;
60
+ }) => Promise<string>;
61
+ unshare: (listId?: ListRef) => Promise<void>;
62
+ };
63
+ items: {
64
+ list: (listId?: ListRef, page?: PageOptions) => Promise<{
65
+ data: components["schemas"]["Item"][];
66
+ pageInfo: {
67
+ hasNextPage: boolean;
68
+ endCursor?: string | null;
69
+ };
70
+ }>;
71
+ /** Every item in a list, fetched page by page. */
72
+ iterate: (listId?: ListRef, opts?: {
73
+ limit?: number;
74
+ }) => AsyncGenerator<{
75
+ id: string;
76
+ listId: string;
77
+ productId: components["schemas"]["ProductGid"];
78
+ variantId?: components["schemas"]["VariantGid"] | null;
79
+ quantity: number;
80
+ note?: string | null;
81
+ addedAt: string;
82
+ }, any, any>;
83
+ /** Save a product. Saving it again returns the existing item. */
84
+ add: (product: ProductRef, opts?: ItemOptions & {
85
+ quantity?: number;
86
+ note?: string;
87
+ }) => Promise<{
88
+ id: string;
89
+ listId: string;
90
+ productId: components["schemas"]["ProductGid"];
91
+ variantId?: components["schemas"]["VariantGid"] | null;
92
+ quantity: number;
93
+ note?: string | null;
94
+ addedAt: string;
95
+ }>;
96
+ /** Save if unsaved, unsave if saved. Built for heart buttons. */
97
+ toggle: (product: ProductRef, opts?: ItemOptions) => Promise<{
98
+ saved: boolean;
99
+ }>;
100
+ update: (itemId: string, patch: {
101
+ variantId?: VariantRef | null;
102
+ quantity?: number;
103
+ note?: string | null;
104
+ }, opts?: {
105
+ listId?: ListRef;
106
+ }) => Promise<{
107
+ id: string;
108
+ listId: string;
109
+ productId: components["schemas"]["ProductGid"];
110
+ variantId?: components["schemas"]["VariantGid"] | null;
111
+ quantity: number;
112
+ note?: string | null;
113
+ addedAt: string;
114
+ }>;
115
+ remove: (itemId: string, opts?: {
116
+ listId?: ListRef;
117
+ }) => Promise<void>;
118
+ /** Unsave by product, without knowing the item id. No-op if not saved. */
119
+ removeProduct: (product: ProductRef, opts?: ItemOptions) => Promise<void>;
120
+ };
121
+ /**
122
+ * Saved state for a set of products across all of the owner's lists.
123
+ * Accepts any number of products; batches of 100 are fetched in parallel.
124
+ */
125
+ saved: (products: Iterable<ProductRef>) => Promise<SavedLookup>;
126
+ identity: {
127
+ /** Fold a guest's lists into the logged-in customer's. Safe to retry. */
128
+ merge: (visitorId?: string) => Promise<{
129
+ mergedItems: number;
130
+ lists: components["schemas"]["List"][];
131
+ }>;
132
+ };
133
+ shared: {
134
+ /** Public, read-only view of a shared list. */
135
+ get: (shareToken: string, page?: PageOptions) => Promise<{
136
+ list: {
137
+ name: string;
138
+ itemCount: number;
139
+ };
140
+ items: components["schemas"]["ItemPage"];
141
+ }>;
142
+ };
143
+ insights: {
144
+ /** Most-saved products. Server key only. */
145
+ topProducts: (query?: {
146
+ since?: Date | string;
147
+ limit?: number;
148
+ }) => Promise<{
149
+ productId: components["schemas"]["ProductGid"];
150
+ saves: number;
151
+ }[]>;
152
+ };
153
+ /**
154
+ * A copy of this client acting for a specific shopper. In Hydrogen, call
155
+ * once per request with the customer from your Customer Account session.
156
+ */
157
+ as(owner: Owner): {
158
+ lists: {
159
+ /** The owner's lists, default first. */
160
+ list: () => Promise<{
161
+ id: string;
162
+ name: string;
163
+ isDefault: boolean;
164
+ visibility: "private" | "shared";
165
+ itemCount: number;
166
+ shareToken?: string | null;
167
+ createdAt: string;
168
+ updatedAt: string;
169
+ items?: components["schemas"]["ItemPage"];
170
+ }[]>;
171
+ get: (listId?: ListRef, opts?: {
172
+ includeItems?: boolean;
173
+ }) => Promise<{
174
+ id: string;
175
+ name: string;
176
+ isDefault: boolean;
177
+ visibility: "private" | "shared";
178
+ itemCount: number;
179
+ shareToken?: string | null;
180
+ createdAt: string;
181
+ updatedAt: string;
182
+ items?: components["schemas"]["ItemPage"];
183
+ }>;
184
+ create: (name: string) => Promise<{
185
+ id: string;
186
+ name: string;
187
+ isDefault: boolean;
188
+ visibility: "private" | "shared";
189
+ itemCount: number;
190
+ shareToken?: string | null;
191
+ createdAt: string;
192
+ updatedAt: string;
193
+ items?: components["schemas"]["ItemPage"];
194
+ }>;
195
+ rename: (listId: ListRef, name: string) => Promise<{
196
+ id: string;
197
+ name: string;
198
+ isDefault: boolean;
199
+ visibility: "private" | "shared";
200
+ itemCount: number;
201
+ shareToken?: string | null;
202
+ createdAt: string;
203
+ updatedAt: string;
204
+ items?: components["schemas"]["ItemPage"];
205
+ }>;
206
+ delete: (listId: ListRef) => Promise<void>;
207
+ /** The list's share token, created on first share. `rotate: true` issues a new one. */
208
+ share: (listId?: ListRef, opts?: {
209
+ rotate?: boolean;
210
+ }) => Promise<string>;
211
+ unshare: (listId?: ListRef) => Promise<void>;
212
+ };
213
+ items: {
214
+ list: (listId?: ListRef, page?: PageOptions) => Promise<{
215
+ data: components["schemas"]["Item"][];
216
+ pageInfo: {
217
+ hasNextPage: boolean;
218
+ endCursor?: string | null;
219
+ };
220
+ }>;
221
+ /** Every item in a list, fetched page by page. */
222
+ iterate: (listId?: ListRef, opts?: {
223
+ limit?: number;
224
+ }) => AsyncGenerator<{
225
+ id: string;
226
+ listId: string;
227
+ productId: components["schemas"]["ProductGid"];
228
+ variantId?: components["schemas"]["VariantGid"] | null;
229
+ quantity: number;
230
+ note?: string | null;
231
+ addedAt: string;
232
+ }, any, any>;
233
+ /** Save a product. Saving it again returns the existing item. */
234
+ add: (product: ProductRef, opts?: ItemOptions & {
235
+ quantity?: number;
236
+ note?: string;
237
+ }) => Promise<{
238
+ id: string;
239
+ listId: string;
240
+ productId: components["schemas"]["ProductGid"];
241
+ variantId?: components["schemas"]["VariantGid"] | null;
242
+ quantity: number;
243
+ note?: string | null;
244
+ addedAt: string;
245
+ }>;
246
+ /** Save if unsaved, unsave if saved. Built for heart buttons. */
247
+ toggle: (product: ProductRef, opts?: ItemOptions) => Promise<{
248
+ saved: boolean;
249
+ }>;
250
+ update: (itemId: string, patch: {
251
+ variantId?: VariantRef | null;
252
+ quantity?: number;
253
+ note?: string | null;
254
+ }, opts?: {
255
+ listId?: ListRef;
256
+ }) => Promise<{
257
+ id: string;
258
+ listId: string;
259
+ productId: components["schemas"]["ProductGid"];
260
+ variantId?: components["schemas"]["VariantGid"] | null;
261
+ quantity: number;
262
+ note?: string | null;
263
+ addedAt: string;
264
+ }>;
265
+ remove: (itemId: string, opts?: {
266
+ listId?: ListRef;
267
+ }) => Promise<void>;
268
+ /** Unsave by product, without knowing the item id. No-op if not saved. */
269
+ removeProduct: (product: ProductRef, opts?: ItemOptions) => Promise<void>;
270
+ };
271
+ /**
272
+ * Saved state for a set of products across all of the owner's lists.
273
+ * Accepts any number of products; batches of 100 are fetched in parallel.
274
+ */
275
+ saved: (products: Iterable<ProductRef>) => Promise<SavedLookup>;
276
+ identity: {
277
+ /** Fold a guest's lists into the logged-in customer's. Safe to retry. */
278
+ merge: (visitorId?: string) => Promise<{
279
+ mergedItems: number;
280
+ lists: components["schemas"]["List"][];
281
+ }>;
282
+ };
283
+ shared: {
284
+ /** Public, read-only view of a shared list. */
285
+ get: (shareToken: string, page?: PageOptions) => Promise<{
286
+ list: {
287
+ name: string;
288
+ itemCount: number;
289
+ };
290
+ items: components["schemas"]["ItemPage"];
291
+ }>;
292
+ };
293
+ insights: {
294
+ /** Most-saved products. Server key only. */
295
+ topProducts: (query?: {
296
+ since?: Date | string;
297
+ limit?: number;
298
+ }) => Promise<{
299
+ productId: components["schemas"]["ProductGid"];
300
+ saves: number;
301
+ }[]>;
302
+ };
303
+ as(owner: Owner): /*elided*/ any;
304
+ };
305
+ };
306
+ type Bagsy = ReturnType<typeof createBagsy>;
307
+
308
+ export { createBagsy as c };
309
+ export type { Bagsy as B };
@@ -0,0 +1,170 @@
1
+ import { components } from './index.js';
2
+ import { Transport } from './index.js';
3
+
4
+ /**
5
+ * `<bagsy-button product-id="123">` — a save/unsave toggle.
6
+ *
7
+ * Attributes: `product-id` (required, numeric or GID), `variant-id` (save a
8
+ * specific variant), `variant-source` (follow the selected variant in the
9
+ * product form on the page), `label` / `label-saved` (text, also used as the
10
+ * accessible name), `icon-only`. Reflects `saved` for styling.
11
+ * Default look: `@umxr/bagsy/styles.css`. Style with `--bagsy-accent`,
12
+ * `--bagsy-color`, `--bagsy-background`, `--bagsy-border`, `--bagsy-radius`,
13
+ * `--bagsy-padding`, or `bagsy-button::part(button | icon | label)`.
14
+ * Put your own markup inside to replace the default icon and label.
15
+ */
16
+ declare class BagsyButton extends HTMLElement {
17
+ static observedAttributes: string[];
18
+ private button;
19
+ private label;
20
+ private unwatch?;
21
+ private busy;
22
+ private onVariantChange;
23
+ constructor();
24
+ connectedCallback(): void;
25
+ disconnectedCallback(): void;
26
+ /** Follow the selected variant in the product form, preferring this section's form. */
27
+ private syncVariant;
28
+ attributeChangedCallback(name: string, previous: string | null, next: string | null): void;
29
+ get productId(): string;
30
+ get variantId(): string | undefined;
31
+ private subscribe;
32
+ private render;
33
+ private onClick;
34
+ }
35
+
36
+ /**
37
+ * `<bagsy-count>` — how many items are in the shopper's wishlist, e.g. for a
38
+ * header link. Renders plain text so it inherits theme styles. Add
39
+ * `hide-empty` to hide it at zero.
40
+ */
41
+ declare class BagsyCount extends HTMLElement {
42
+ private onCount;
43
+ connectedCallback(): void;
44
+ disconnectedCallback(): void;
45
+ private render;
46
+ }
47
+
48
+ type Image = {
49
+ url: string;
50
+ altText: string | null;
51
+ width: number | null;
52
+ height: number | null;
53
+ } | null;
54
+ type Money = {
55
+ amount: string;
56
+ currencyCode: string;
57
+ };
58
+ /** One card: a saved product, or a saved variant of one. */
59
+ interface Card {
60
+ productId: string;
61
+ variantId?: string;
62
+ title: string;
63
+ variantTitle?: string;
64
+ handle: string;
65
+ available: boolean;
66
+ image: Image;
67
+ price: Money;
68
+ }
69
+ /**
70
+ * `<bagsy-wishlist>`: the shopper's saved products as a grid of cards, or a
71
+ * shared list when the URL has `?bagsy_share=<token>`.
72
+ *
73
+ * Product data comes live from the store's Storefront API (tokenless, same
74
+ * origin), so titles, prices and availability match the shopper's market.
75
+ * Attributes: `country`, `language`, `share` (show share controls),
76
+ * `empty-text`, `remove-label`, `save-label`, `saved-label`, `sold-out-label`,
77
+ * `share-label`, `unshare-label`, `copied-label`, `unshared-label`,
78
+ * `shared-heading`, `shared-missing-text`. Renders light DOM with
79
+ * `bagsy-wishlist__*` classes, styled by `@umxr/bagsy/styles.css`.
80
+ */
81
+ declare class BagsyWishlist extends HTMLElement {
82
+ private shared;
83
+ private onChange;
84
+ private text;
85
+ connectedCallback(): void;
86
+ disconnectedCallback(): void;
87
+ private loadOwn;
88
+ private loadShared;
89
+ /** Saved items → live cards, in saved order. Deleted or unpublished products drop out. */
90
+ cards(items: {
91
+ productId: string;
92
+ variantId?: string | null;
93
+ }[]): Promise<Card[]>;
94
+ private shareControls;
95
+ private actionButton;
96
+ private message;
97
+ private renderEmpty;
98
+ private grid;
99
+ }
100
+
101
+ interface BagsyConfig {
102
+ /** Shopify `routes.root_url`, e.g. `/` or `/en-gb`. */
103
+ root?: string;
104
+ /** Whether Shopify has a logged-in customer for this page. */
105
+ customer?: boolean;
106
+ /** Override the proxy base URL entirely (tests, custom setups). */
107
+ baseUrl?: string;
108
+ fetch?: typeof fetch;
109
+ }
110
+ interface ChangeDetail {
111
+ productId: string;
112
+ variantId?: string;
113
+ saved: boolean;
114
+ }
115
+ type Listener = () => void;
116
+ /**
117
+ * One per page: a single client, one batched `/saved` lookup for every button
118
+ * on the page, optimistic toggles, and the guest → customer merge on login.
119
+ */
120
+ declare class BagsyStore extends EventTarget {
121
+ private config;
122
+ /** Use with any `@umxr/bagsy` operation, e.g. `listLists(Bagsy.transport)`. */
123
+ readonly transport: Transport;
124
+ private saved;
125
+ private pending;
126
+ private flush?;
127
+ private listeners;
128
+ /** Resolves once any pending guest merge has finished. */
129
+ readonly ready: Promise<void>;
130
+ count?: number;
131
+ /** Storefront root without a trailing slash: '' or '/en-gb'. */
132
+ readonly root: string;
133
+ constructor(config?: BagsyConfig);
134
+ /** Fold the pre-login guest list into the customer's, once. */
135
+ private mergeGuestIfNeeded;
136
+ /** Subscribe to a product's saved state; triggers a (batched) lookup. */
137
+ watch(productId: string, listener: Listener): () => void;
138
+ isSaved(productId: string, variantId?: string): boolean | undefined;
139
+ /** Optimistic: listeners see the new state immediately and revert on failure. */
140
+ toggle(productId: string, variantId?: string): Promise<boolean>;
141
+ /** Items in the default list, newest first. */
142
+ items(): AsyncGenerator<{
143
+ id: string;
144
+ listId: string;
145
+ productId: components["schemas"]["ProductGid"];
146
+ variantId?: components["schemas"]["VariantGid"] | null;
147
+ quantity: number;
148
+ note?: string | null;
149
+ addedAt: string;
150
+ }, void, any>;
151
+ private countRequest?;
152
+ /** Load the count once per page; later calls share the first request. */
153
+ ensureCount(): Promise<number>;
154
+ /** Number of items in the default list, for header badges. */
155
+ loadCount(): Promise<number>;
156
+ private adjustCount;
157
+ private queue;
158
+ private notify;
159
+ private emit;
160
+ }
161
+ /** The page's store, configured from the app embed's JSON on first use. */
162
+ declare function getStore(): BagsyStore;
163
+ /** Replace the page store (tests, or custom setups before elements connect). */
164
+ declare function setStore(store: BagsyStore | undefined): void;
165
+
166
+ /** Register the elements. Safe to call more than once. */
167
+ declare function define(): void;
168
+
169
+ export { BagsyButton, BagsyCount, BagsyStore, BagsyWishlist, define, getStore, setStore };
170
+ export type { BagsyConfig, ChangeDetail };