@fayz-ai/sdk 0.9.0 → 0.10.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 (56) hide show
  1. package/README.md +2 -2
  2. package/dist/ai-builder.js +1 -1
  3. package/dist/{chunk-2YJXKDHO.js → chunk-6UZOXHGJ.js} +11 -6
  4. package/dist/chunk-6UZOXHGJ.js.map +1 -0
  5. package/dist/{chunk-V4GWI7WH.js → chunk-7UXTYT6I.js} +65 -4
  6. package/dist/chunk-7UXTYT6I.js.map +1 -0
  7. package/dist/{chunk-7XM4LONE.js → chunk-BJFMBVMP.js} +2 -2
  8. package/dist/chunk-BJFMBVMP.js.map +1 -0
  9. package/dist/chunk-HA5O3CW7.js +181 -0
  10. package/dist/chunk-HA5O3CW7.js.map +1 -0
  11. package/dist/chunk-KVMRRT3O.js +135 -0
  12. package/dist/chunk-KVMRRT3O.js.map +1 -0
  13. package/dist/deprecations.d.ts +98 -0
  14. package/dist/deprecations.d.ts.map +1 -0
  15. package/dist/deprecations.js +3 -0
  16. package/dist/deprecations.js.map +1 -0
  17. package/dist/deprecations.json +119 -0
  18. package/dist/index.d.ts +2 -0
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +5 -4
  21. package/dist/release-channels.js +1 -1
  22. package/dist/release-channels.json +103 -0
  23. package/dist/shop.d.ts +32 -0
  24. package/dist/shop.d.ts.map +1 -1
  25. package/dist/shop.js +2 -1
  26. package/dist/supported-surface.d.ts +8 -1
  27. package/dist/supported-surface.d.ts.map +1 -1
  28. package/dist/supported-surface.js +1 -1
  29. package/{src → dist}/supported-surface.json +5 -3
  30. package/dist/vite.d.ts +30 -1
  31. package/dist/vite.d.ts.map +1 -1
  32. package/dist/vite.js +10 -2
  33. package/dist/vite.js.map +1 -1
  34. package/package.json +10 -17
  35. package/dist/chunk-2YJXKDHO.js.map +0 -1
  36. package/dist/chunk-7XM4LONE.js.map +0 -1
  37. package/dist/chunk-GQPVDQEB.js +0 -135
  38. package/dist/chunk-GQPVDQEB.js.map +0 -1
  39. package/dist/chunk-V4GWI7WH.js.map +0 -1
  40. package/src/agent.test.ts +0 -114
  41. package/src/agent.ts +0 -335
  42. package/src/ai-builder.ts +0 -115
  43. package/src/app-params.ts +0 -69
  44. package/src/client.test.ts +0 -176
  45. package/src/client.ts +0 -229
  46. package/src/index.ts +0 -94
  47. package/src/release-channels.json +0 -103
  48. package/src/release-channels.test.ts +0 -41
  49. package/src/release-channels.ts +0 -41
  50. package/src/runtime.test.ts +0 -123
  51. package/src/runtime.ts +0 -211
  52. package/src/shop.test.ts +0 -114
  53. package/src/shop.ts +0 -1118
  54. package/src/supported-surface.ts +0 -33
  55. package/src/types.ts +0 -67
  56. package/src/vite.ts +0 -121
package/src/shop.ts DELETED
@@ -1,1118 +0,0 @@
1
- export interface FayzShopProviderOptions {
2
- supabaseUrl?: string
3
- publishableKey?: string
4
- storeId?: string
5
- apiBaseUrl?: string
6
- projectId?: string
7
- mode?: 'broker' | 'supabase'
8
- productMetadata?: FayzShopProductMetadataOverlay[]
9
- fetcher?: typeof fetch
10
- }
11
-
12
- /**
13
- * The signed-in shopper's access token, when there is one.
14
- *
15
- * PostgREST decides who you are from the Authorization bearer, and this client
16
- * used to send the publishable key unconditionally — so every request arrived
17
- * as `anon` even with a customer logged in. auth.uid() was therefore always
18
- * null, which is why shop_resolve_customer never linked auth_user_id (0 of 519
19
- * customers in the pool) and why the self-read policy on public.addresses could
20
- * never match a row.
21
- *
22
- * A module-level resolver rather than a per-app option on purpose: the wiring
23
- * is done once by initStorefrontRuntime, so a storefront cannot forget it and
24
- * silently fall back to anonymous. Mirrors setShopTenantResolver in plugin-shop.
25
- */
26
- type AccessTokenResolver = () => string | null | undefined
27
-
28
- let _accessTokenResolver: AccessTokenResolver | null = null
29
-
30
- export function setShopAccessTokenResolver(resolver: AccessTokenResolver | null): void {
31
- _accessTokenResolver = resolver
32
- }
33
-
34
- export interface FayzShopProductMetadataOverlay {
35
- sku?: string | null
36
- slug?: string | null
37
- metadata: Record<string, unknown>
38
- }
39
-
40
- export type FayzShopProductStatus = 'draft' | 'active' | 'archived'
41
- export type FayzShopOrderStatus = 'open' | 'archived' | 'cancelled'
42
- export type FayzShopFinancialStatus = 'pending' | 'paid' | 'partially_paid' | 'refunded' | 'partially_refunded' | 'voided'
43
- export type FayzShopFulfillmentStatus = 'unfulfilled' | 'partially_fulfilled' | 'fulfilled'
44
-
45
- export interface FayzShopListProductsOptions {
46
- status?: FayzShopProductStatus
47
- categoryId?: string
48
- search?: string
49
- slug?: string
50
- limit?: number
51
- offset?: number
52
- orderBy?: 'name' | 'price' | 'created_at' | 'sort_order'
53
- order?: 'asc' | 'desc'
54
- }
55
-
56
- export interface FayzShopListOrdersOptions {
57
- status?: FayzShopOrderStatus
58
- financialStatus?: FayzShopFinancialStatus
59
- fulfillmentStatus?: FayzShopFulfillmentStatus
60
- customerId?: string
61
- customerEmail?: string
62
- search?: string
63
- limit?: number
64
- offset?: number
65
- }
66
-
67
- export interface FayzShopListCustomersOptions {
68
- search?: string
69
- limit?: number
70
- offset?: number
71
- }
72
-
73
- export interface FayzShopListDiscountsOptions {
74
- status?: string
75
- type?: string
76
- search?: string
77
- limit?: number
78
- offset?: number
79
- }
80
-
81
- interface ProductImageRow {
82
- id: string
83
- product_id: string
84
- url: string
85
- alt_text?: string | null
86
- sort_order?: number | null
87
- is_primary?: boolean | null
88
- created_at?: string | null
89
- }
90
-
91
- interface ProductRow {
92
- id: string
93
- tenant_id: string
94
- name: string
95
- slug?: string | null
96
- description?: string | null
97
- price?: number | null
98
- compare_at_price?: number | null
99
- currency?: string | null
100
- status?: FayzShopProductStatus | null
101
- inventory_count?: number | null
102
- sku?: string | null
103
- sort_order?: number | null
104
- metadata?: Record<string, unknown> | null
105
- images?: ProductImageRow[] | null
106
- category_id?: string | null
107
- category?: { name?: string | null } | null
108
- is_physical?: boolean | null
109
- weight?: number | null
110
- weight_unit?: string | null
111
- created_at?: string | null
112
- updated_at?: string | null
113
- }
114
-
115
- interface StoreCategoryMetadata {
116
- id?: string | null
117
- name?: string | null
118
- slug?: string | null
119
- description?: string | null
120
- imageUrl?: string | null
121
- sortOrder?: number | null
122
- }
123
-
124
- interface CategoryRow {
125
- id: string
126
- tenant_id?: string | null
127
- name: string
128
- slug?: string | null
129
- description?: string | null
130
- parent_id?: string | null
131
- sort_order?: number | null
132
- image_url?: string | null
133
- created_at?: string | null
134
- }
135
-
136
- interface OrderItemRow {
137
- id: string
138
- order_id: string
139
- product_id?: string | null
140
- name: string
141
- sku?: string | null
142
- quantity?: number | null
143
- unit_price?: number | null
144
- total?: number | null
145
- image_url?: string | null
146
- }
147
-
148
- interface OrderRow {
149
- id: string
150
- tenant_id: string
151
- order_number?: number | null
152
- status?: FayzShopOrderStatus | null
153
- financial_status?: FayzShopFinancialStatus | null
154
- fulfillment_status?: FayzShopFulfillmentStatus | null
155
- currency?: string | null
156
- subtotal?: number | null
157
- tax_total?: number | null
158
- discount_total?: number | null
159
- shipping_total?: number | null
160
- total?: number | null
161
- customer_id?: string | null
162
- customer_name?: string | null
163
- customer_email?: string | null
164
- discount_code?: string | null
165
- notes?: string | null
166
- shipping_address?: Record<string, string | undefined> | null
167
- payment_method_kind?: string | null
168
- paid_at?: string | null
169
- items?: OrderItemRow[] | null
170
- created_at?: string | null
171
- updated_at?: string | null
172
- }
173
-
174
- /** One row of shop_quote_shipping's result set. */
175
- interface ShippingQuoteRow {
176
- zone_id: string
177
- name: string
178
- carrier: string | null
179
- rate: number | string | null
180
- base_rate: number | string | null
181
- free_above: number | string | null
182
- eta_min_days: number | null
183
- eta_max_days: number | null
184
- free: boolean | null
185
- }
186
-
187
- /** public.addresses, as PostgREST returns it. */
188
- interface AddressRow {
189
- id: string
190
- label: string | null
191
- recipient: string | null
192
- phone: string | null
193
- postal_code: string | null
194
- street: string | null
195
- number: string | null
196
- complement: string | null
197
- district: string | null
198
- city: string | null
199
- state: string | null
200
- country: string | null
201
- is_default: boolean | null
202
- }
203
-
204
- interface CustomerRow {
205
- id: string
206
- tenant_id: string
207
- first_name: string
208
- last_name?: string | null
209
- email?: string | null
210
- phone?: string | null
211
- notes?: string | null
212
- orders_count?: number | null
213
- total_spent?: number | null
214
- created_at?: string | null
215
- updated_at?: string | null
216
- }
217
-
218
- interface DiscountRow {
219
- id: string
220
- tenant_id: string
221
- title: string
222
- code?: string | null
223
- type: string
224
- method?: string | null
225
- value?: number | null
226
- usage_limit?: number | null
227
- once_per_customer?: boolean | null
228
- starts_at?: string | null
229
- ends_at?: string | null
230
- status?: string | null
231
- times_used?: number | null
232
- created_at?: string | null
233
- updated_at?: string | null
234
- }
235
-
236
- export class FayzShopError extends Error {
237
- constructor(
238
- message: string,
239
- readonly status: number,
240
- readonly responseBody?: unknown,
241
- ) {
242
- super(message)
243
- this.name = 'FayzShopError'
244
- }
245
- }
246
-
247
- function normalizeBaseUrl(url: string): string {
248
- return url.replace(/\/+$/, '')
249
- }
250
-
251
- function slugify(name: string): string {
252
- return name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '')
253
- }
254
-
255
- function nowIso(): string {
256
- return new Date().toISOString()
257
- }
258
-
259
- function present<T>(value: T | null | undefined, fallback: T): T {
260
- return value == null ? fallback : value
261
- }
262
-
263
- function productMetadataKey(kind: 'sku' | 'slug', value: string): string {
264
- return `${kind}:${value.toLowerCase()}`
265
- }
266
-
267
- function buildProductMetadataOverlay(overlays: FayzShopProductMetadataOverlay[] | undefined) {
268
- const map = new Map<string, Record<string, unknown>>()
269
- for (const overlay of overlays ?? []) {
270
- if (overlay.sku) map.set(productMetadataKey('sku', overlay.sku), overlay.metadata)
271
- if (overlay.slug) map.set(productMetadataKey('slug', overlay.slug), overlay.metadata)
272
- }
273
- return map
274
- }
275
-
276
- function asUuid(value: string | null | undefined): string | null {
277
- if (!value) return null
278
- return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value)
279
- ? value
280
- : null
281
- }
282
-
283
- function rowToImage(row: ProductImageRow) {
284
- return {
285
- id: row.id,
286
- productId: row.product_id,
287
- url: row.url,
288
- altText: row.alt_text ?? null,
289
- sortOrder: row.sort_order ?? 0,
290
- isPrimary: row.is_primary ?? false,
291
- createdAt: row.created_at ?? nowIso(),
292
- }
293
- }
294
-
295
- function getProductMetadata(row: ProductRow, overlay?: Map<string, Record<string, unknown>>): Record<string, unknown> {
296
- const metadata = row.metadata ?? {}
297
- const bySlug = overlay?.get(productMetadataKey('slug', row.slug ?? slugify(row.name)))
298
- const bySku = row.sku ? overlay?.get(productMetadataKey('sku', row.sku)) : undefined
299
- return { ...(bySlug ?? {}), ...(bySku ?? {}), ...metadata }
300
- }
301
-
302
- function getStoreCategory(row: ProductRow, overlay?: Map<string, Record<string, unknown>>): StoreCategoryMetadata | null {
303
- const raw = getProductMetadata(row, overlay).fayzStoreCategory
304
- return raw && typeof raw === 'object' ? raw as StoreCategoryMetadata : null
305
- }
306
-
307
- function rowToProduct(row: ProductRow, overlay?: Map<string, Record<string, unknown>>) {
308
- const metadata = getProductMetadata(row, overlay)
309
- const storeCategory = getStoreCategory(row, overlay)
310
- return {
311
- id: row.id,
312
- tenantId: row.tenant_id,
313
- name: row.name,
314
- slug: row.slug ?? slugify(row.name),
315
- description: row.description ?? null,
316
- price: row.price ?? 0,
317
- compareAtPrice: row.compare_at_price ?? null,
318
- currency: row.currency ?? 'BRL',
319
- status: row.status ?? 'active',
320
- inventoryCount: row.inventory_count ?? 0,
321
- sku: row.sku ?? null,
322
- sortOrder: row.sort_order ?? 0,
323
- metadata,
324
- images: Array.isArray(row.images) ? row.images.map(rowToImage) : [],
325
- categoryId: storeCategory?.id ?? row.category_id ?? null,
326
- categoryName: storeCategory?.name ?? row.category?.name ?? null,
327
- isPhysical: row.is_physical ?? true,
328
- weight: row.weight ?? null,
329
- weightUnit: row.weight_unit ?? 'kg',
330
- createdAt: row.created_at ?? nowIso(),
331
- updatedAt: row.updated_at ?? row.created_at ?? nowIso(),
332
- }
333
- }
334
-
335
- function rowToCategory(row: CategoryRow) {
336
- return {
337
- id: row.id,
338
- tenantId: row.tenant_id ?? 'global',
339
- name: row.name,
340
- slug: row.slug ?? slugify(row.name),
341
- description: row.description ?? null,
342
- parentId: row.parent_id ?? null,
343
- sortOrder: row.sort_order ?? 0,
344
- imageUrl: row.image_url ?? null,
345
- createdAt: row.created_at ?? nowIso(),
346
- }
347
- }
348
-
349
- function storeCategoryToCategory(category: StoreCategoryMetadata, tenantId: string) {
350
- const name = category.name ?? 'Category'
351
- return {
352
- id: category.id ?? slugify(name),
353
- tenantId,
354
- name,
355
- slug: category.slug ?? slugify(name),
356
- description: category.description ?? null,
357
- parentId: null,
358
- sortOrder: category.sortOrder ?? 0,
359
- imageUrl: category.imageUrl ?? null,
360
- createdAt: nowIso(),
361
- }
362
- }
363
-
364
- function deriveStoreCategories(products: ProductRow[], tenantId: string) {
365
- const categories = new Map<string, ReturnType<typeof storeCategoryToCategory>>()
366
- for (const product of products) {
367
- const category = getStoreCategory(product)
368
- if (!category?.name && !category?.id) continue
369
- const mapped = storeCategoryToCategory(category, tenantId)
370
- categories.set(mapped.id, mapped)
371
- }
372
- return [...categories.values()].sort((a, b) => a.sortOrder - b.sortOrder || a.name.localeCompare(b.name))
373
- }
374
-
375
- function rowToOrder(row: OrderRow) {
376
- return {
377
- id: row.id,
378
- tenantId: row.tenant_id,
379
- orderNumber: row.order_number ?? 0,
380
- status: row.status ?? 'open',
381
- financialStatus: row.financial_status ?? 'pending',
382
- fulfillmentStatus: row.fulfillment_status ?? 'unfulfilled',
383
- currency: row.currency ?? 'BRL',
384
- subtotal: row.subtotal ?? 0,
385
- taxTotal: row.tax_total ?? 0,
386
- discountTotal: row.discount_total ?? 0,
387
- shippingTotal: row.shipping_total ?? 0,
388
- total: row.total ?? 0,
389
- customerId: row.customer_id ?? null,
390
- customerName: row.customer_name ?? null,
391
- customerEmail: row.customer_email ?? null,
392
- discountCode: row.discount_code ?? null,
393
- notes: row.notes ?? null,
394
- shippingAddress: row.shipping_address ?? null,
395
- paymentMethodKind: row.payment_method_kind ?? null,
396
- paidAt: row.paid_at ?? null,
397
- items: Array.isArray(row.items) ? row.items.map(rowToOrderItem) : [],
398
- createdAt: row.created_at ?? nowIso(),
399
- updatedAt: row.updated_at ?? row.created_at ?? nowIso(),
400
- }
401
- }
402
-
403
- function rowToOrderItem(row: OrderItemRow) {
404
- return {
405
- id: row.id,
406
- orderId: row.order_id,
407
- productId: row.product_id ?? null,
408
- name: row.name,
409
- sku: row.sku ?? null,
410
- quantity: row.quantity ?? 0,
411
- unitPrice: row.unit_price ?? 0,
412
- total: row.total ?? 0,
413
- imageUrl: row.image_url ?? null,
414
- }
415
- }
416
-
417
- function rowToCustomer(row: CustomerRow) {
418
- return {
419
- id: row.id,
420
- tenantId: row.tenant_id,
421
- firstName: row.first_name,
422
- lastName: row.last_name ?? '',
423
- email: row.email ?? null,
424
- phone: row.phone ?? null,
425
- notes: row.notes ?? null,
426
- ordersCount: row.orders_count ?? 0,
427
- totalSpent: row.total_spent ?? 0,
428
- createdAt: row.created_at ?? nowIso(),
429
- updatedAt: row.updated_at ?? row.created_at ?? nowIso(),
430
- }
431
- }
432
-
433
- function rowToDiscount(row: DiscountRow) {
434
- return {
435
- id: row.id,
436
- tenantId: row.tenant_id,
437
- title: row.title,
438
- code: row.code ?? null,
439
- type: row.type,
440
- method: row.method ?? 'code',
441
- value: row.value ?? 0,
442
- usageLimit: row.usage_limit ?? null,
443
- oncePerCustomer: row.once_per_customer ?? false,
444
- startsAt: row.starts_at ?? nowIso(),
445
- endsAt: row.ends_at ?? null,
446
- status: row.status ?? 'active',
447
- timesUsed: row.times_used ?? 0,
448
- createdAt: row.created_at ?? nowIso(),
449
- updatedAt: row.updated_at ?? row.created_at ?? nowIso(),
450
- }
451
- }
452
-
453
- function updateOrderPayload(input: {
454
- status?: FayzShopOrderStatus
455
- financialStatus?: FayzShopFinancialStatus
456
- fulfillmentStatus?: FayzShopFulfillmentStatus
457
- notes?: string
458
- }) {
459
- const updates: Record<string, unknown> = {}
460
- if (input.status !== undefined) updates.status = input.status
461
- if (input.financialStatus !== undefined) updates.financial_status = input.financialStatus
462
- if (input.fulfillmentStatus !== undefined) updates.fulfillment_status = input.fulfillmentStatus
463
- if (input.notes !== undefined) updates.notes = input.notes
464
- return updates
465
- }
466
-
467
- export function createFayzShopProvider(options: FayzShopProviderOptions) {
468
- if (options.mode === 'broker' || options.apiBaseUrl || options.projectId) {
469
- return createBrokerShopProvider(options)
470
- }
471
-
472
- if (!options.supabaseUrl || !options.publishableKey || !options.storeId) {
473
- throw new FayzShopError('Fayz Shop provider requires either broker apiBaseUrl/projectId or legacy supabaseUrl/publishableKey/storeId.', 400)
474
- }
475
-
476
- const baseUrl = `${normalizeBaseUrl(options.supabaseUrl)}/rest/v1`
477
- const fetcher = options.fetcher ?? fetch
478
- const key = options.publishableKey
479
- const storeId = options.storeId
480
- const productMetadataOverlay = buildProductMetadataOverlay(options.productMetadata)
481
-
482
- async function request<T>(table: string, init: RequestInit & { query?: URLSearchParams } = {}): Promise<T> {
483
- const query = init.query?.toString()
484
- const response = await fetcher(`${baseUrl}/${table}${query ? `?${query}` : ''}`, {
485
- ...init,
486
- headers: {
487
- apikey: key,
488
- // Declares which store this read is for. Until 0020 tenant isolation was
489
- // a client-side convention (a tenant_id filter the caller could simply
490
- // omit, returning every merchant's catalogue); the RLS policy now reads
491
- // this header, so the scope is enforced server-side.
492
- 'x-fayz-store': storeId,
493
- // The shopper's JWT when signed in, the publishable key otherwise. The
494
- // apikey header stays the publishable key either way — Supabase uses it
495
- // to route the project, not to authenticate.
496
- Authorization: `Bearer ${_accessTokenResolver?.() || key}`,
497
- ...(init.body ? { 'Content-Type': 'application/json', Prefer: 'return=representation' } : {}),
498
- ...init.headers,
499
- },
500
- })
501
- const text = await response.text()
502
- let body: unknown
503
- try {
504
- body = text ? JSON.parse(text) : undefined
505
- } catch {
506
- body = text
507
- }
508
- if (!response.ok) {
509
- const message = typeof body === 'object' && body
510
- ? String((body as { message?: unknown; error?: unknown }).message ?? (body as { error?: unknown }).error ?? `Fayz Shop request failed with status ${response.status}`)
511
- : `Fayz Shop request failed with status ${response.status}`
512
- throw new FayzShopError(message, response.status, body)
513
- }
514
- return body as T
515
- }
516
-
517
- const tenantQuery = () => {
518
- const query = new URLSearchParams()
519
- query.set('tenant_id', `eq.${storeId}`)
520
- return query
521
- }
522
-
523
- const singleById = (id: string) => {
524
- const query = tenantQuery()
525
- query.set('id', `eq.${id}`)
526
- query.set('limit', '1')
527
- return query
528
- }
529
-
530
- return {
531
- async listProducts(options?: FayzShopListProductsOptions) {
532
- const query = tenantQuery()
533
- query.set('select', '*,images:plg_shop_product_images(*),category:plg_shop_categories(name)')
534
- if (options?.status) query.set('status', `eq.${options.status}`)
535
- if (options?.slug) query.set('slug', `eq.${options.slug}`)
536
- if (options?.search) query.set('name', `ilike.*${options.search}*`)
537
- query.set('order', `${options?.orderBy ?? 'sort_order'}.${options?.order ?? 'asc'}`)
538
- if (options?.limit) query.set('limit', String(options.limit))
539
- if (options?.offset) query.set('offset', String(options.offset))
540
- const products = (await request<ProductRow[]>('plg_shop_products', { query })).map((row) => rowToProduct(row, productMetadataOverlay))
541
- return options?.categoryId ? products.filter((product) => product.categoryId === options.categoryId) : products
542
- },
543
- async getProduct(id: string) {
544
- // Cart lines can carry non-uuid ids (mock catalog, or a cart persisted in
545
- // an earlier mock-mode session). PostgREST would 400 on `id=eq.p-05`
546
- // (invalid uuid), so treat any non-uuid id as "not found" instead.
547
- if (!asUuid(id)) return null
548
- const query = singleById(id)
549
- query.set('select', '*,images:plg_shop_product_images(*),category:plg_shop_categories(name)')
550
- const rows = await request<ProductRow[]>('plg_shop_products', { query })
551
- return rows[0] ? rowToProduct(rows[0], productMetadataOverlay) : null
552
- },
553
- async createProduct() {
554
- throw new FayzShopError('Product writes require the Fayz admin/broker API.', 501)
555
- },
556
- async updateProduct() {
557
- throw new FayzShopError('Product writes require the Fayz admin/broker API.', 501)
558
- },
559
- async deleteProduct() {
560
- throw new FayzShopError('Product writes require the Fayz admin/broker API.', 501)
561
- },
562
- async uploadProductImage() {
563
- throw new FayzShopError('Product image uploads require the Fayz admin/broker API.', 501)
564
- },
565
- async deleteProductImage() {
566
- throw new FayzShopError('Product image deletes require the Fayz admin/broker API.', 501)
567
- },
568
- async listCategories() {
569
- const productQuery = tenantQuery()
570
- productQuery.set('select', 'metadata')
571
- productQuery.set('order', 'sort_order.asc')
572
- const products = await request<ProductRow[]>('plg_shop_products', { query: productQuery })
573
- const storeCategories = deriveStoreCategories(products, storeId)
574
- if (storeCategories.length > 0) return storeCategories
575
-
576
- const query = new URLSearchParams()
577
- query.set('select', '*')
578
- query.set('order', 'sort_order.asc')
579
- return (await request<CategoryRow[]>('plg_shop_categories', { query })).map(rowToCategory)
580
- },
581
- async createCategory() {
582
- throw new FayzShopError('Category writes require the Fayz admin/broker API.', 501)
583
- },
584
- async updateCategory() {
585
- throw new FayzShopError('Category writes require the Fayz admin/broker API.', 501)
586
- },
587
- async deleteCategory() {
588
- throw new FayzShopError('Category deletes require the Fayz admin/broker API.', 501)
589
- },
590
- async listOrders(options?: FayzShopListOrdersOptions) {
591
- const query = tenantQuery()
592
- query.set('select', '*,items:plg_shop_order_items(*)')
593
- if (options?.status) query.set('status', `eq.${options.status}`)
594
- if (options?.financialStatus) query.set('financial_status', `eq.${options.financialStatus}`)
595
- if (options?.fulfillmentStatus) query.set('fulfillment_status', `eq.${options.fulfillmentStatus}`)
596
- if (options?.customerId) query.set('customer_id', `eq.${options.customerId}`)
597
- if (options?.customerEmail) query.set('customer_email', `eq.${options.customerEmail}`)
598
- if (options?.search) query.set('or', `(customer_name.ilike.*${options.search}*,customer_email.ilike.*${options.search}*)`)
599
- query.set('order', 'created_at.desc')
600
- if (options?.limit) query.set('limit', String(options.limit))
601
- if (options?.offset) query.set('offset', String(options.offset))
602
- // Anon storefronts have no table grant (RLS restricts plg_shop_orders reads to
603
- // `authenticated` users linked via auth.uid) — direct read 401s. RLS can also
604
- // just filter every row out silently (200 OK + []) instead of 401ing, so an
605
- // empty result here doesn't mean "no orders" either — both cases must fall
606
- // back to the customer-uuid-capability RPC, same pattern as getOrder's
607
- // shop_get_order. This is what made "Minhas compras" empty after a guest
608
- // checkout — the order was there, the read just wasn't allowed (or silently
609
- // filtered), and only the throw case used to be handled.
610
- let rows: OrderRow[] = []
611
- try {
612
- rows = await request<OrderRow[]>('plg_shop_orders', { query })
613
- } catch (error) {
614
- if (!options?.customerId) throw error
615
- }
616
- if (rows.length || !options?.customerId) return rows.map(rowToOrder)
617
- // Only the customer-scoped query has an RPC counterpart: shop_list_orders
618
- // treats the customer uuid as the read capability, exactly like
619
- // shop_get_order treats the order uuid. Any other filter combination has
620
- // no anon-safe equivalent, so the original error (if any) stands.
621
- const rpcRows = await request<OrderRow[]>('rpc/shop_list_orders', {
622
- method: 'POST',
623
- body: JSON.stringify({ p_customer_id: options.customerId, p_limit: options.limit ?? 50 }),
624
- })
625
- return (rpcRows ?? []).map(rowToOrder)
626
- },
627
- async getOrder(id: string) {
628
- const query = singleById(id)
629
- query.set('select', '*,items:plg_shop_order_items(*)')
630
- // Anon storefronts have no table grant at all — the direct read throws
631
- // (401) rather than returning empty, so it must not short-circuit the
632
- // RPC fallback below.
633
- let rows: OrderRow[] = []
634
- try {
635
- rows = await request<OrderRow[]>('plg_shop_orders', { query })
636
- } catch {
637
- /* fall through to shop_get_order */
638
- }
639
- if (rows[0]) return rowToOrder(rows[0])
640
- // Anon storefronts can't SELECT plg_shop_orders (RLS); the order uuid acts
641
- // as the read capability through the whitelisted RPC.
642
- try {
643
- const row = await request<OrderRow | null>('rpc/shop_get_order', {
644
- method: 'POST',
645
- body: JSON.stringify({ p_order_id: id }),
646
- })
647
- return row ? rowToOrder(row) : null
648
- } catch {
649
- return null
650
- }
651
- },
652
- async confirmPayment(id: string, reference?: string) {
653
- // Mock/dev payment seam: anon has no UPDATE grant on plg_shop_orders,
654
- // so the pending->paid transition goes through the whitelisted RPC
655
- // (order uuid = capability, same contract as shop_get_order).
656
- const row = await request<OrderRow | null>('rpc/shop_confirm_payment', {
657
- method: 'POST',
658
- body: JSON.stringify({ p_order_id: id, p_reference: reference ?? null }),
659
- })
660
- return row ? rowToOrder(row) : null
661
- },
662
- async createOrder(input: {
663
- customerId?: string
664
- customerName?: string
665
- customerEmail?: string
666
- currency?: string
667
- notes?: string
668
- discountCode?: string
669
- discountTotal?: number
670
- shippingTotal?: number
671
- items: Array<{ productId?: string; name: string; sku?: string; quantity: number; unitPrice: number; imageUrl?: string }>
672
- }) {
673
- const subtotal = input.items.reduce((sum, item) => sum + item.quantity * item.unitPrice, 0)
674
- const discountTotal = input.discountTotal ?? 0
675
- const shippingTotal = input.shippingTotal ?? 0
676
- const total = Math.round((subtotal - discountTotal + shippingTotal) * 100) / 100
677
- const [order] = await request<OrderRow[]>('plg_shop_orders', {
678
- method: 'POST',
679
- body: JSON.stringify({
680
- tenant_id: storeId,
681
- customer_id: input.customerId ?? null,
682
- customer_name: input.customerName ?? null,
683
- customer_email: input.customerEmail ?? null,
684
- currency: input.currency ?? 'BRL',
685
- notes: input.notes ?? null,
686
- subtotal,
687
- discount_total: discountTotal,
688
- shipping_total: shippingTotal,
689
- discount_code: input.discountCode ?? null,
690
- total,
691
- status: 'open',
692
- financial_status: 'pending',
693
- fulfillment_status: 'unfulfilled',
694
- }),
695
- })
696
- if (!order) throw new FayzShopError('Order insert did not return a row.', 500)
697
- if (input.items.length) {
698
- await request<OrderItemRow[]>('plg_shop_order_items', {
699
- method: 'POST',
700
- body: JSON.stringify(input.items.map((item) => ({
701
- order_id: order.id,
702
- product_id: asUuid(item.productId),
703
- name: item.name,
704
- sku: item.sku ?? null,
705
- quantity: item.quantity,
706
- unit_price: item.unitPrice,
707
- total: item.quantity * item.unitPrice,
708
- image_url: item.imageUrl ?? null,
709
- }))),
710
- })
711
- }
712
- return this.getOrder(order.id)
713
- },
714
- async updateOrder(id: string, input: Parameters<typeof updateOrderPayload>[0]) {
715
- await request<OrderRow[]>('plg_shop_orders', {
716
- method: 'PATCH',
717
- query: singleById(id),
718
- body: JSON.stringify(updateOrderPayload(input)),
719
- })
720
- return this.getOrder(id)
721
- },
722
- async placeOrder(input: {
723
- tenantId?: string
724
- customerId?: string
725
- customer?: { name?: string; email?: string }
726
- currency?: string
727
- notes?: string
728
- discountCode?: string
729
- shippingTotal?: number
730
- /** Structured delivery address; frozen on the order and saved to the
731
- * customer's address book by the RPC. */
732
- shippingAddress?: {
733
- postalCode: string
734
- street: string
735
- number?: string
736
- complement?: string
737
- district?: string
738
- city: string
739
- state: string
740
- country?: string
741
- recipient?: string
742
- phone?: string
743
- label?: string
744
- }
745
- paymentMethod?: 'pix' | 'credit_card' | 'debit_card' | 'boleto' | 'cash' | 'other'
746
- items: Array<{ productId: string; quantity: number; optionsLabel?: string }>
747
- }) {
748
- // Trusted placement: the shop_place_order SECURITY DEFINER RPC re-reads
749
- // prices, validates discount/stock and writes order+items atomically.
750
- // Direct INSERTs are blocked for anon by RLS on the shared FayzApi.
751
- const orderId = await request<string>('rpc/shop_place_order', {
752
- method: 'POST',
753
- body: JSON.stringify({
754
- p_tenant_id: input.tenantId ?? storeId,
755
- p_items: input.items.map((line) => ({
756
- product_id: line.productId,
757
- quantity: line.quantity,
758
- options_label: line.optionsLabel ?? null,
759
- })),
760
- p_customer_id: asUuid(input.customerId ?? undefined),
761
- p_customer_name: input.customer?.name ?? null,
762
- p_customer_email: input.customer?.email ?? null,
763
- p_currency: input.currency ?? 'BRL',
764
- p_discount_code: input.discountCode ?? null,
765
- p_shipping_total: input.shippingTotal ?? 0,
766
- // snake_case keys on purpose: the RPC freezes this jsonb onto the
767
- // order and public.addresses uses these exact column names.
768
- p_shipping_address: input.shippingAddress
769
- ? {
770
- postal_code: input.shippingAddress.postalCode,
771
- street: input.shippingAddress.street,
772
- number: input.shippingAddress.number ?? null,
773
- complement: input.shippingAddress.complement ?? null,
774
- district: input.shippingAddress.district ?? null,
775
- city: input.shippingAddress.city,
776
- state: input.shippingAddress.state,
777
- country: input.shippingAddress.country ?? 'BR',
778
- recipient: input.shippingAddress.recipient ?? null,
779
- phone: input.shippingAddress.phone ?? null,
780
- label: input.shippingAddress.label ?? null,
781
- }
782
- : null,
783
- p_payment_method: input.paymentMethod ?? null,
784
- p_notes: input.notes ?? null,
785
- }),
786
- })
787
- const order = await this.getOrder(orderId)
788
- if (!order) throw new FayzShopError('Order was placed but could not be read back.', 500)
789
- return order
790
- },
791
- async listCustomers(options?: FayzShopListCustomersOptions) {
792
- const query = tenantQuery()
793
- query.set('select', '*')
794
- query.set('order', 'first_name.asc')
795
- if (options?.search) query.set('or', `(first_name.ilike.*${options.search}*,last_name.ilike.*${options.search}*,email.ilike.*${options.search}*)`)
796
- if (options?.limit) query.set('limit', String(options.limit))
797
- if (options?.offset) query.set('offset', String(options.offset))
798
- return (await request<CustomerRow[]>('plg_shop_customers', { query })).map(rowToCustomer)
799
- },
800
- async getCustomer(id: string) {
801
- const rows = await request<CustomerRow[]>('plg_shop_customers', { query: singleById(id) })
802
- return rows[0] ? rowToCustomer(rows[0]) : null
803
- },
804
- async resolveCustomer(input: { email: string; name?: string }) {
805
- const row = await request<CustomerRow | CustomerRow[]>('rpc/shop_resolve_customer', {
806
- method: 'POST',
807
- body: JSON.stringify({
808
- p_tenant_id: storeId,
809
- p_email: input.email,
810
- p_name: input.name ?? null,
811
- }),
812
- })
813
- const customer = Array.isArray(row) ? row[0] : row
814
- if (!customer) throw new FayzShopError('shop_resolve_customer returned no row.', 500)
815
- return rowToCustomer(customer)
816
- },
817
- async quoteShipping(postalCode: string, subtotal: number) {
818
- const digits = (postalCode || '').replace(/\D/g, '')
819
- if (digits.length !== 8) return []
820
- const rows = await request<ShippingQuoteRow[]>('rpc/shop_quote_shipping', {
821
- method: 'POST',
822
- body: JSON.stringify({
823
- p_tenant_id: storeId,
824
- p_postal_code: digits,
825
- p_subtotal: subtotal,
826
- }),
827
- })
828
- return (rows ?? []).map((row) => ({
829
- zoneId: row.zone_id,
830
- name: row.name,
831
- carrier: row.carrier ?? undefined,
832
- rate: Number(row.rate ?? 0),
833
- baseRate: Number(row.base_rate ?? row.rate ?? 0),
834
- freeAbove: row.free_above == null ? null : Number(row.free_above),
835
- etaMinDays: row.eta_min_days ?? undefined,
836
- etaMaxDays: row.eta_max_days ?? undefined,
837
- free: Boolean(row.free),
838
- }))
839
- },
840
- async listCustomerAddresses(customerId: string) {
841
- if (!asUuid(customerId)) return []
842
- const query = new URLSearchParams()
843
- query.set('select', 'id,label,recipient,phone,postal_code,street,number,complement,district,city,state,country,is_default')
844
- query.set('owner_type', 'eq.shop_customer')
845
- query.set('owner_id', `eq.${customerId}`)
846
- query.set('order', 'is_default.desc,created_at.desc')
847
- // RLS (addresses_self_read) is the real filter — it matches only rows
848
- // whose customer is linked to auth.uid(). An anonymous or mismatched
849
- // caller gets [] rather than an error, so guest checkout just sees an
850
- // empty address book instead of a failure it cannot act on.
851
- const rows = await request<AddressRow[]>('addresses', { query }).catch(() => [] as AddressRow[])
852
- return rows.map((row) => ({
853
- id: row.id,
854
- label: row.label ?? undefined,
855
- recipient: row.recipient ?? undefined,
856
- phone: row.phone ?? undefined,
857
- postalCode: row.postal_code ?? '',
858
- street: row.street ?? '',
859
- number: row.number ?? undefined,
860
- complement: row.complement ?? undefined,
861
- district: row.district ?? undefined,
862
- city: row.city ?? '',
863
- state: row.state ?? '',
864
- country: row.country ?? undefined,
865
- isDefault: row.is_default ?? false,
866
- }))
867
- },
868
- async createCustomer(input: { firstName: string; lastName?: string; email?: string; phone?: string; notes?: string }) {
869
- const [customer] = await request<CustomerRow[]>('plg_shop_customers', {
870
- method: 'POST',
871
- body: JSON.stringify({
872
- tenant_id: storeId,
873
- first_name: input.firstName,
874
- last_name: input.lastName ?? '',
875
- email: input.email ?? null,
876
- phone: input.phone ?? null,
877
- notes: input.notes ?? null,
878
- }),
879
- })
880
- if (!customer) throw new FayzShopError('Customer insert did not return a row.', 500)
881
- return rowToCustomer(customer)
882
- },
883
- async updateCustomer(id: string, input: { firstName?: string; lastName?: string; email?: string | null; phone?: string | null; notes?: string | null }) {
884
- const updates: Record<string, unknown> = {}
885
- if (input.firstName !== undefined) updates.first_name = input.firstName
886
- if (input.lastName !== undefined) updates.last_name = input.lastName
887
- if (input.email !== undefined) updates.email = input.email
888
- if (input.phone !== undefined) updates.phone = input.phone
889
- if (input.notes !== undefined) updates.notes = input.notes
890
- const [customer] = await request<CustomerRow[]>('plg_shop_customers', {
891
- method: 'PATCH',
892
- query: singleById(id),
893
- body: JSON.stringify(updates),
894
- })
895
- if (!customer) throw new FayzShopError('Customer update did not return a row.', 500)
896
- return rowToCustomer(customer)
897
- },
898
- async deleteCustomer(id: string) {
899
- await request<unknown>('plg_shop_customers', { method: 'DELETE', query: singleById(id) })
900
- },
901
- async listDiscounts(options?: FayzShopListDiscountsOptions) {
902
- const query = tenantQuery()
903
- query.set('select', '*')
904
- query.set('order', 'created_at.desc')
905
- if (options?.status) query.set('status', `eq.${options.status}`)
906
- if (options?.type) query.set('type', `eq.${options.type}`)
907
- if (options?.search) query.set('title', `ilike.*${options.search}*`)
908
- if (options?.limit) query.set('limit', String(options.limit))
909
- if (options?.offset) query.set('offset', String(options.offset))
910
- return (await request<DiscountRow[]>('plg_shop_discounts', { query })).map(rowToDiscount)
911
- },
912
- async getDiscount(id: string) {
913
- const rows = await request<DiscountRow[]>('plg_shop_discounts', { query: singleById(id) })
914
- return rows[0] ? rowToDiscount(rows[0]) : null
915
- },
916
- async createDiscount() {
917
- throw new FayzShopError('Discount writes require the Fayz admin/broker API.', 501)
918
- },
919
- async updateDiscount() {
920
- throw new FayzShopError('Discount writes require the Fayz admin/broker API.', 501)
921
- },
922
- async deleteDiscount() {
923
- throw new FayzShopError('Discount deletes require the Fayz admin/broker API.', 501)
924
- },
925
- }
926
- }
927
-
928
- function appendQuery(url: string, values: Record<string, string | number | undefined>): string {
929
- const query = new URLSearchParams()
930
- for (const [key, value] of Object.entries(values)) {
931
- if (value !== undefined) query.set(key, String(value))
932
- }
933
- const serialized = query.toString()
934
- return serialized ? `${url}?${serialized}` : url
935
- }
936
-
937
- function normalizeBrokerProduct(product: Record<string, unknown>, overlay?: Map<string, Record<string, unknown>>) {
938
- const category = product.category && typeof product.category === 'object'
939
- ? product.category as Record<string, unknown>
940
- : null
941
- const row = {
942
- id: product.id as string,
943
- tenant_id: product.tenantId as string,
944
- name: product.name as string,
945
- slug: product.slug as string | undefined,
946
- description: product.description as string | null | undefined,
947
- price: product.price as number | null | undefined,
948
- compare_at_price: product.compareAtPrice as number | null | undefined,
949
- currency: product.currency as string | null | undefined,
950
- status: product.status as FayzShopProductStatus | null | undefined,
951
- inventory_count: product.inventoryCount as number | null | undefined,
952
- sku: product.sku as string | null | undefined,
953
- sort_order: product.sortOrder as number | null | undefined,
954
- metadata: product.metadata as Record<string, unknown> | null | undefined,
955
- images: Array.isArray(product.images)
956
- ? product.images.map((image): ProductImageRow => {
957
- const row = image as Record<string, unknown>
958
- return {
959
- id: String(row.id ?? ''),
960
- product_id: String(row.productId ?? product.id ?? ''),
961
- url: String(row.url ?? ''),
962
- alt_text: (row.altText as string | null | undefined) ?? null,
963
- sort_order: (row.sortOrder as number | null | undefined) ?? 0,
964
- is_primary: (row.isPrimary as boolean | null | undefined) ?? false,
965
- created_at: (row.createdAt as string | null | undefined) ?? null,
966
- }
967
- })
968
- : [],
969
- category_id: (product.categoryId ?? category?.id) as string | null | undefined,
970
- category: category ? { name: category.name as string | null | undefined } : null,
971
- is_physical: product.isPhysical as boolean | null | undefined,
972
- weight: product.weight as number | null | undefined,
973
- weight_unit: product.weightUnit as string | null | undefined,
974
- created_at: product.createdAt as string | null | undefined,
975
- updated_at: product.updatedAt as string | null | undefined,
976
- } satisfies ProductRow
977
- return rowToProduct(row, overlay)
978
- }
979
-
980
- function normalizeBrokerCategory(category: Record<string, unknown>) {
981
- return {
982
- id: category.id as string,
983
- tenantId: (category.tenantId as string | undefined) ?? 'global',
984
- name: category.name as string,
985
- slug: (category.slug as string | undefined) ?? slugify(category.name as string),
986
- description: (category.description as string | null | undefined) ?? null,
987
- parentId: (category.parentId as string | null | undefined) ?? null,
988
- sortOrder: (category.sortOrder as number | null | undefined) ?? 0,
989
- imageUrl: (category.imageUrl as string | null | undefined) ?? null,
990
- createdAt: (category.createdAt as string | null | undefined) ?? nowIso(),
991
- }
992
- }
993
-
994
- function createBrokerShopProvider(options: FayzShopProviderOptions) {
995
- if (!options.apiBaseUrl || !options.projectId) {
996
- throw new FayzShopError('Fayz Shop broker mode requires apiBaseUrl and projectId.', 400)
997
- }
998
-
999
- const fetcher = options.fetcher ?? fetch
1000
- const apiBaseUrl = normalizeBaseUrl(options.apiBaseUrl)
1001
- const projectId = encodeURIComponent(options.projectId)
1002
- const productMetadataOverlay = buildProductMetadataOverlay(options.productMetadata)
1003
-
1004
- async function request<T>(path: string, init: RequestInit = {}): Promise<T> {
1005
- const response = await fetcher(`${apiBaseUrl}/public/projects/${projectId}/shop${path}`, init)
1006
- const text = await response.text()
1007
- let body: unknown
1008
- try {
1009
- body = text ? JSON.parse(text) : undefined
1010
- } catch {
1011
- body = text
1012
- }
1013
- if (!response.ok) {
1014
- const message = typeof body === 'object' && body
1015
- ? String((body as { message?: unknown; error?: unknown }).message ?? (body as { error?: unknown }).error ?? `Fayz Shop broker request failed with status ${response.status}`)
1016
- : `Fayz Shop broker request failed with status ${response.status}`
1017
- throw new FayzShopError(message, response.status, body)
1018
- }
1019
- return body as T
1020
- }
1021
-
1022
- return {
1023
- async listProducts(options?: FayzShopListProductsOptions) {
1024
- if (options?.slug) {
1025
- const product = await this.getProduct(options.slug)
1026
- return product ? [product] : []
1027
- }
1028
-
1029
- const response = await request<{ products?: Array<Record<string, unknown>> }>(appendQuery('/products', {
1030
- status: options?.status,
1031
- categoryId: options?.categoryId,
1032
- search: options?.search,
1033
- limit: options?.limit,
1034
- offset: options?.offset,
1035
- orderBy: options?.orderBy,
1036
- order: options?.order,
1037
- }))
1038
- return (response.products ?? []).map((product) => normalizeBrokerProduct(product, productMetadataOverlay))
1039
- },
1040
- async getProduct(id: string) {
1041
- const response = await request<{ product?: Record<string, unknown> | null }>(`/products/${encodeURIComponent(id)}`)
1042
- return response.product ? normalizeBrokerProduct(response.product, productMetadataOverlay) : null
1043
- },
1044
- async createProduct() {
1045
- throw new FayzShopError('Product writes require the Fayz admin/broker API.', 501)
1046
- },
1047
- async updateProduct() {
1048
- throw new FayzShopError('Product writes require the Fayz admin/broker API.', 501)
1049
- },
1050
- async deleteProduct() {
1051
- throw new FayzShopError('Product writes require the Fayz admin/broker API.', 501)
1052
- },
1053
- async uploadProductImage() {
1054
- throw new FayzShopError('Product image uploads require the Fayz admin/broker API.', 501)
1055
- },
1056
- async deleteProductImage() {
1057
- throw new FayzShopError('Product image deletes require the Fayz admin/broker API.', 501)
1058
- },
1059
- async listCategories() {
1060
- const response = await request<{ categories?: Array<Record<string, unknown>> }>('/categories')
1061
- return (response.categories ?? []).map(normalizeBrokerCategory)
1062
- },
1063
- async createCategory() {
1064
- throw new FayzShopError('Category writes require the Fayz admin/broker API.', 501)
1065
- },
1066
- async updateCategory() {
1067
- throw new FayzShopError('Category writes require the Fayz admin/broker API.', 501)
1068
- },
1069
- async deleteCategory() {
1070
- throw new FayzShopError('Category deletes require the Fayz admin/broker API.', 501)
1071
- },
1072
- async listOrders() {
1073
- throw new FayzShopError('Order reads require the Fayz admin/broker API.', 501)
1074
- },
1075
- async getOrder() {
1076
- throw new FayzShopError('Order reads require the Fayz admin/broker API.', 501)
1077
- },
1078
- async createOrder() {
1079
- throw new FayzShopError('Order writes require the Fayz admin/broker API.', 501)
1080
- },
1081
- async updateOrder() {
1082
- throw new FayzShopError('Order writes require the Fayz admin/broker API.', 501)
1083
- },
1084
- async placeOrder() {
1085
- throw new FayzShopError('Order placement requires the Fayz admin/broker API.', 501)
1086
- },
1087
- async listCustomers() {
1088
- throw new FayzShopError('Customer reads require the Fayz admin/broker API.', 501)
1089
- },
1090
- async getCustomer() {
1091
- throw new FayzShopError('Customer reads require the Fayz admin/broker API.', 501)
1092
- },
1093
- async createCustomer() {
1094
- throw new FayzShopError('Customer writes require the Fayz admin/broker API.', 501)
1095
- },
1096
- async updateCustomer() {
1097
- throw new FayzShopError('Customer writes require the Fayz admin/broker API.', 501)
1098
- },
1099
- async deleteCustomer() {
1100
- throw new FayzShopError('Customer writes require the Fayz admin/broker API.', 501)
1101
- },
1102
- async listDiscounts() {
1103
- throw new FayzShopError('Discount reads require the Fayz admin/broker API.', 501)
1104
- },
1105
- async getDiscount() {
1106
- throw new FayzShopError('Discount reads require the Fayz admin/broker API.', 501)
1107
- },
1108
- async createDiscount() {
1109
- throw new FayzShopError('Discount writes require the Fayz admin/broker API.', 501)
1110
- },
1111
- async updateDiscount() {
1112
- throw new FayzShopError('Discount writes require the Fayz admin/broker API.', 501)
1113
- },
1114
- async deleteDiscount() {
1115
- throw new FayzShopError('Discount deletes require the Fayz admin/broker API.', 501)
1116
- },
1117
- }
1118
- }