@jytextiles/medusa-plugin-faire-store-sync 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.
Files changed (104) hide show
  1. package/.medusa/server/src/__tests__/ingest-faire-order-support.spec.js +74 -0
  2. package/.medusa/server/src/__tests__/webhook.spec.js +77 -0
  3. package/.medusa/server/src/admin/index.js +960 -0
  4. package/.medusa/server/src/admin/index.mjs +959 -0
  5. package/.medusa/server/src/api/admin/faire/auth/authorize/route.js +12 -0
  6. package/.medusa/server/src/api/admin/faire/auth/callback/route.js +25 -0
  7. package/.medusa/server/src/api/admin/faire/auth/disconnect/route.js +12 -0
  8. package/.medusa/server/src/api/admin/faire/brand/route.js +14 -0
  9. package/.medusa/server/src/api/admin/faire/ingest/orders/[batchId]/route.js +27 -0
  10. package/.medusa/server/src/api/admin/faire/ingest/orders/route.js +20 -0
  11. package/.medusa/server/src/api/admin/faire/products/route.js +16 -0
  12. package/.medusa/server/src/api/admin/faire/settings/route.js +19 -0
  13. package/.medusa/server/src/api/admin/faire/status/product/[id]/route.js +30 -0
  14. package/.medusa/server/src/api/admin/faire/status/route.js +46 -0
  15. package/.medusa/server/src/api/admin/faire/sync/bulk/[batchId]/route.js +29 -0
  16. package/.medusa/server/src/api/admin/faire/sync/bulk/route.js +20 -0
  17. package/.medusa/server/src/api/admin/faire/sync/product/[id]/route.js +14 -0
  18. package/.medusa/server/src/api/admin/faire/syncs/[id]/route.js +32 -0
  19. package/.medusa/server/src/api/admin/faire/syncs/route.js +18 -0
  20. package/.medusa/server/src/api/webhooks/faire/route.js +125 -0
  21. package/.medusa/server/src/jobs/pull-faire-orders.js +32 -0
  22. package/.medusa/server/src/jobs/refresh-faire-token.js +25 -0
  23. package/.medusa/server/src/lib/faire-client.js +321 -0
  24. package/.medusa/server/src/lib/types.js +10 -0
  25. package/.medusa/server/src/lib/webhook.js +58 -0
  26. package/.medusa/server/src/links/product-faire-sync-link.js +57 -0
  27. package/.medusa/server/src/modules/faire-sync/index.js +15 -0
  28. package/.medusa/server/src/modules/faire-sync/migrations/Migration20260707211346.js +34 -0
  29. package/.medusa/server/src/modules/faire-sync/models/faire-order.js +21 -0
  30. package/.medusa/server/src/modules/faire-sync/models/faire-sync-account.js +17 -0
  31. package/.medusa/server/src/modules/faire-sync/models/faire-sync-batch.js +18 -0
  32. package/.medusa/server/src/modules/faire-sync/models/faire-sync-record.js +22 -0
  33. package/.medusa/server/src/modules/faire-sync/models/faire-sync-settings.js +26 -0
  34. package/.medusa/server/src/modules/faire-sync/models/faire-webhook-event.js +21 -0
  35. package/.medusa/server/src/modules/faire-sync/service.js +222 -0
  36. package/.medusa/server/src/subscribers/faire-inventory-sync.js +87 -0
  37. package/.medusa/server/src/subscribers/faire-listing-events.js +46 -0
  38. package/.medusa/server/src/subscribers/faire-order-ingest.js +54 -0
  39. package/.medusa/server/src/subscribers/faire-order-lifecycle.js +71 -0
  40. package/.medusa/server/src/subscribers/faire-product-status-sync.js +71 -0
  41. package/.medusa/server/src/workflows/index.js +21 -0
  42. package/.medusa/server/src/workflows/ingest-faire-order-support.js +76 -0
  43. package/.medusa/server/src/workflows/ingest-faire-order.js +148 -0
  44. package/.medusa/server/src/workflows/ingest-faire-orders-bulk.js +124 -0
  45. package/.medusa/server/src/workflows/refresh-faire-token.js +20 -0
  46. package/.medusa/server/src/workflows/sync-product-to-faire.js +262 -0
  47. package/.medusa/server/src/workflows/sync-products-to-faire.js +90 -0
  48. package/LICENSE +21 -0
  49. package/README.md +180 -0
  50. package/package.json +84 -0
  51. package/scripts/postbuild.js +36 -0
  52. package/src/__tests__/ingest-faire-order-support.spec.ts +77 -0
  53. package/src/__tests__/webhook.spec.ts +81 -0
  54. package/src/admin/lib/api.ts +88 -0
  55. package/src/admin/routes/settings/faire/[id]/page.tsx +184 -0
  56. package/src/admin/routes/settings/faire/hooks/use-faire-sync-columns.tsx +80 -0
  57. package/src/admin/routes/settings/faire/page.tsx +445 -0
  58. package/src/admin/routes/settings/faire/settings/page.tsx +286 -0
  59. package/src/admin/routes/settings/oauth/faire/callback/page.tsx +67 -0
  60. package/src/admin/widgets/faire-product-widget.tsx +180 -0
  61. package/src/api/admin/faire/auth/authorize/route.ts +10 -0
  62. package/src/api/admin/faire/auth/callback/route.ts +29 -0
  63. package/src/api/admin/faire/auth/disconnect/route.ts +10 -0
  64. package/src/api/admin/faire/brand/route.ts +12 -0
  65. package/src/api/admin/faire/ingest/orders/[batchId]/route.ts +31 -0
  66. package/src/api/admin/faire/ingest/orders/route.ts +29 -0
  67. package/src/api/admin/faire/products/route.ts +14 -0
  68. package/src/api/admin/faire/settings/route.ts +17 -0
  69. package/src/api/admin/faire/status/product/[id]/route.ts +30 -0
  70. package/src/api/admin/faire/status/route.ts +47 -0
  71. package/src/api/admin/faire/sync/bulk/[batchId]/route.ts +33 -0
  72. package/src/api/admin/faire/sync/bulk/route.ts +27 -0
  73. package/src/api/admin/faire/sync/product/[id]/route.ts +13 -0
  74. package/src/api/admin/faire/syncs/[id]/route.ts +32 -0
  75. package/src/api/admin/faire/syncs/route.ts +17 -0
  76. package/src/api/webhooks/faire/route.ts +133 -0
  77. package/src/jobs/pull-faire-orders.ts +30 -0
  78. package/src/jobs/refresh-faire-token.ts +23 -0
  79. package/src/lib/faire-client.ts +436 -0
  80. package/src/lib/types.ts +117 -0
  81. package/src/lib/webhook.ts +85 -0
  82. package/src/links/product-faire-sync-link.ts +56 -0
  83. package/src/modules/faire-sync/index.ts +11 -0
  84. package/src/modules/faire-sync/migrations/.snapshot-medusa-faire-sync.json +1486 -0
  85. package/src/modules/faire-sync/migrations/Migration20260707211346.ts +43 -0
  86. package/src/modules/faire-sync/models/faire-order.ts +20 -0
  87. package/src/modules/faire-sync/models/faire-sync-account.ts +16 -0
  88. package/src/modules/faire-sync/models/faire-sync-batch.ts +17 -0
  89. package/src/modules/faire-sync/models/faire-sync-record.ts +21 -0
  90. package/src/modules/faire-sync/models/faire-sync-settings.ts +25 -0
  91. package/src/modules/faire-sync/models/faire-webhook-event.ts +20 -0
  92. package/src/modules/faire-sync/service.ts +252 -0
  93. package/src/subscribers/faire-inventory-sync.ts +97 -0
  94. package/src/subscribers/faire-listing-events.ts +48 -0
  95. package/src/subscribers/faire-order-ingest.ts +61 -0
  96. package/src/subscribers/faire-order-lifecycle.ts +81 -0
  97. package/src/subscribers/faire-product-status-sync.ts +76 -0
  98. package/src/workflows/index.ts +4 -0
  99. package/src/workflows/ingest-faire-order-support.ts +96 -0
  100. package/src/workflows/ingest-faire-order.ts +212 -0
  101. package/src/workflows/ingest-faire-orders-bulk.ts +168 -0
  102. package/src/workflows/refresh-faire-token.ts +28 -0
  103. package/src/workflows/sync-product-to-faire.ts +375 -0
  104. package/src/workflows/sync-products-to-faire.ts +133 -0
@@ -0,0 +1,321 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FaireClient = exports.FaireApiError = void 0;
4
+ const types_1 = require("./types");
5
+ class FaireApiError extends Error {
6
+ constructor(message, status, body) {
7
+ super(message);
8
+ this.name = "FaireApiError";
9
+ this.status = status;
10
+ this.body = body;
11
+ }
12
+ }
13
+ exports.FaireApiError = FaireApiError;
14
+ /**
15
+ * Faire External Platform API v2 client.
16
+ *
17
+ * Auth model:
18
+ * - OAuth 2.0 authorization-code flow (no PKCE; Faire does not use PKCE).
19
+ * - Scoped requests send `Authorization: Bearer <access_token>`.
20
+ * - Faire's API-key mode (`X-FAIRE-ACCESS-TOKEN`) is not used here because the
21
+ * plugin connects a brand account via OAuth redirect.
22
+ *
23
+ * NOTE: Faire's developer portal is access-gated. Endpoint paths, payload
24
+ * shapes and the webhook signature scheme below reflect the publicly documented
25
+ * v2 surface and standard OAuth2 conventions; adjust via options / env if your
26
+ * Faire app uses a different base or signing scheme.
27
+ */
28
+ class FaireClient {
29
+ constructor(opts) {
30
+ this.clientId = opts.clientId;
31
+ this.clientSecret = opts.clientSecret;
32
+ this.redirectUri = opts.redirectUri;
33
+ this.apiBase = opts.apiBase || types_1.DEFAULT_API_BASE;
34
+ this.authUrl = opts.authUrl || types_1.DEFAULT_AUTH_URL;
35
+ this.tokenUrl = opts.tokenUrl || types_1.DEFAULT_TOKEN_URL;
36
+ this.scope = opts.scope ?? "";
37
+ }
38
+ get redirectUriValue() {
39
+ return this.redirectUri;
40
+ }
41
+ // ── OAuth ───────────────────────────────────────────────────────────────
42
+ /**
43
+ * Build the Faire OAuth authorize URL. The caller must persist `state`
44
+ * (keyed by it) for the callback verification.
45
+ */
46
+ getAuthorizationUrl(state) {
47
+ const params = new URLSearchParams({
48
+ response_type: "code",
49
+ client_id: this.clientId,
50
+ redirect_uri: this.redirectUri,
51
+ state,
52
+ });
53
+ if (this.scope)
54
+ params.set("scope", this.scope);
55
+ return `${this.authUrl}?${params.toString()}`;
56
+ }
57
+ async exchangeCodeForToken(code) {
58
+ const body = new URLSearchParams({
59
+ grant_type: "authorization_code",
60
+ client_id: this.clientId,
61
+ client_secret: this.clientSecret,
62
+ redirect_uri: this.redirectUri,
63
+ code,
64
+ });
65
+ const data = await this.requestJson(this.tokenUrl, {
66
+ method: "POST",
67
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
68
+ body: body.toString(),
69
+ auth: false,
70
+ });
71
+ return {
72
+ access_token: data.access_token,
73
+ refresh_token: data.refresh_token,
74
+ token_type: data.token_type || "Bearer",
75
+ expires_in: data.expires_in,
76
+ retrieved_at: Date.now(),
77
+ };
78
+ }
79
+ async refreshAccessToken(refresh_token) {
80
+ const body = new URLSearchParams({
81
+ grant_type: "refresh_token",
82
+ client_id: this.clientId,
83
+ client_secret: this.clientSecret,
84
+ refresh_token,
85
+ });
86
+ const data = await this.requestJson(this.tokenUrl, {
87
+ method: "POST",
88
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
89
+ body: body.toString(),
90
+ auth: false,
91
+ });
92
+ return {
93
+ access_token: data.access_token,
94
+ refresh_token: data.refresh_token || refresh_token,
95
+ token_type: data.token_type || "Bearer",
96
+ expires_in: data.expires_in,
97
+ retrieved_at: Date.now(),
98
+ };
99
+ }
100
+ /**
101
+ * Resolve the brand the access token belongs to. Faire exposes the current
102
+ * brand via GET /api/v2/brand. Falls back gracefully if the shape differs.
103
+ */
104
+ async getBrand(accessToken) {
105
+ const data = await this.requestJson(`${this.apiBase}/brand`, {
106
+ method: "GET",
107
+ accessToken,
108
+ });
109
+ const brand = data && typeof data === "object" && (data.brand_id || data.id)
110
+ ? data
111
+ : data?.brand ?? data?.results?.[0] ?? data;
112
+ if (!brand?.brand_id && !brand?.id) {
113
+ throw new Error("No Faire brand found for the authorized account. Make sure you connected the brand owner.");
114
+ }
115
+ return {
116
+ brand_id: String(brand.brand_id ?? brand.id),
117
+ brand_name: brand.name ?? brand.brand_name ?? "Faire Brand",
118
+ currency: brand.currency ?? brand.currency_code,
119
+ country: brand.country ?? brand.country_code,
120
+ raw: brand,
121
+ };
122
+ }
123
+ async fetchResource(accessToken, url) {
124
+ return this.requestJson(url, { method: "GET", accessToken });
125
+ }
126
+ // ── Products ────────────────────────────────────────────────────────────
127
+ async createProduct(accessToken, input) {
128
+ const data = await this.requestJson(`${this.apiBase}/products`, {
129
+ method: "POST",
130
+ headers: { "Content-Type": "application/json" },
131
+ body: JSON.stringify(input),
132
+ accessToken,
133
+ });
134
+ return this.mapProduct(data?.product ?? data);
135
+ }
136
+ async updateProduct(accessToken, productToken, input) {
137
+ const data = await this.requestJson(`${this.apiBase}/products/${productToken}`, {
138
+ method: "PUT",
139
+ headers: { "Content-Type": "application/json" },
140
+ body: JSON.stringify(input),
141
+ accessToken,
142
+ });
143
+ return this.mapProduct(data?.product ?? data);
144
+ }
145
+ async getProduct(accessToken, productToken) {
146
+ const data = await this.requestJson(`${this.apiBase}/products/${productToken}`, { method: "GET", accessToken });
147
+ return this.mapProduct(data?.product ?? data);
148
+ }
149
+ async listProducts(accessToken, opts = {}) {
150
+ const params = new URLSearchParams();
151
+ params.set("limit", String(opts.limit ?? 100));
152
+ if (opts.page != null)
153
+ params.set("page", String(opts.page));
154
+ const data = await this.requestJson(`${this.apiBase}/products?${params.toString()}`, { method: "GET", accessToken });
155
+ const results = (data.products ?? data.results ?? []).map((p) => this.mapProduct(p));
156
+ return { count: data.count ?? results.length, results };
157
+ }
158
+ // ── Inventory ───────────────────────────────────────────────────────────
159
+ async getInventory(accessToken, productToken) {
160
+ const params = new URLSearchParams();
161
+ if (productToken)
162
+ params.set("product_token", productToken);
163
+ const data = await this.requestJson(`${this.apiBase}/inventory?${params.toString()}`, { method: "GET", accessToken });
164
+ const rows = data.inventory ?? data.results ?? [];
165
+ return (Array.isArray(rows) ? rows : []).map((r) => ({
166
+ sku: String(r.sku ?? r.variant_id ?? ""),
167
+ product_token: r.product_token ? String(r.product_token) : undefined,
168
+ current_count: Number(r.current_count ?? r.inventory_count ?? 0),
169
+ raw: r,
170
+ }));
171
+ }
172
+ async updateInventory(accessToken, levels) {
173
+ return this.requestJson(`${this.apiBase}/inventory`, {
174
+ method: "PUT",
175
+ headers: { "Content-Type": "application/json" },
176
+ body: JSON.stringify({ inventory: levels }),
177
+ accessToken,
178
+ });
179
+ }
180
+ // ── Orders ──────────────────────────────────────────────────────────────
181
+ async listOrders(accessToken, opts = {}) {
182
+ const params = new URLSearchParams();
183
+ params.set("limit", String(opts.limit ?? 100));
184
+ if (opts.page != null)
185
+ params.set("page", String(opts.page));
186
+ const data = await this.requestJson(`${this.apiBase}/orders?${params.toString()}`, { method: "GET", accessToken });
187
+ const results = (data.orders ?? data.results ?? []).map((o) => this.mapOrder(o));
188
+ return { count: data.count ?? results.length, results };
189
+ }
190
+ async getOrder(accessToken, orderToken) {
191
+ const data = await this.requestJson(`${this.apiBase}/orders/${orderToken}`, { method: "GET", accessToken });
192
+ return this.mapOrder(data?.order ?? data);
193
+ }
194
+ async setOrderItemAvailability(accessToken, orderToken, items) {
195
+ return this.requestJson(`${this.apiBase}/orders/${orderToken}/items/availability`, {
196
+ method: "POST",
197
+ headers: { "Content-Type": "application/json" },
198
+ body: JSON.stringify({ items }),
199
+ accessToken,
200
+ });
201
+ }
202
+ // ── Webhooks ────────────────────────────────────────────────────────────
203
+ async registerWebhook(accessToken, payload) {
204
+ return this.requestJson(`${this.apiBase}/webhooks`, {
205
+ method: "POST",
206
+ headers: { "Content-Type": "application/json" },
207
+ body: JSON.stringify(payload),
208
+ accessToken,
209
+ });
210
+ }
211
+ // ── Helpers ─────────────────────────────────────────────────────────────
212
+ /**
213
+ * Fetch a remote image URL into a Buffer (used when Faire requires binary
214
+ * image payloads rather than remote URLs).
215
+ */
216
+ static async fetchImageBuffer(url) {
217
+ const res = await fetch(url);
218
+ if (!res.ok) {
219
+ throw new Error(`Failed to fetch image ${url}: ${res.status}`);
220
+ }
221
+ const arrayBuffer = await res.arrayBuffer();
222
+ const buffer = Buffer.from(arrayBuffer);
223
+ const filename = url.split("/").pop()?.split("?")[0] || "image.jpg";
224
+ return { buffer, filename };
225
+ }
226
+ mapProduct(data) {
227
+ return {
228
+ product_token: String(data.product_token ?? data.id ?? data.token),
229
+ brand_id: data.brand_id != null ? String(data.brand_id) : undefined,
230
+ name: data.name,
231
+ description: data.description,
232
+ state: data.state,
233
+ url: data.url,
234
+ wholesale_price_cents: data.wholesale_price_cents,
235
+ retail_price_cents: data.retail_price_cents,
236
+ variants: data.variants,
237
+ images: data.images,
238
+ raw: data,
239
+ };
240
+ }
241
+ mapOrder(data) {
242
+ return {
243
+ order_token: String(data.order_token ?? data.id ?? data.token),
244
+ state: data.state ?? data.status,
245
+ currency: data.currency ?? data.currency_code,
246
+ total_cents: data.total_cents ?? data.grand_total_cents,
247
+ buyer_name: data.buyer_name ?? data.customer?.name,
248
+ raw: data,
249
+ };
250
+ }
251
+ // ── Low-level request with rate-limit handling ──────────────────────────
252
+ async requestJson(url, init) {
253
+ const headers = {
254
+ Accept: "application/json",
255
+ ...(init.headers || {}),
256
+ };
257
+ if (init.accessToken) {
258
+ headers["Authorization"] = `Bearer ${init.accessToken}`;
259
+ }
260
+ let attempt = 0;
261
+ const maxAttempts = 4;
262
+ while (attempt < maxAttempts) {
263
+ attempt++;
264
+ let res;
265
+ try {
266
+ res = await fetch(url, { method: init.method, headers, body: init.body });
267
+ }
268
+ catch (err) {
269
+ if (attempt >= maxAttempts)
270
+ throw err;
271
+ await this.backoff(attempt);
272
+ continue;
273
+ }
274
+ if (res.status === 429) {
275
+ const retryAfter = Number(res.headers.get("retry-after") || 1);
276
+ await this.sleep(Math.min(retryAfter * 1000, 10000));
277
+ continue;
278
+ }
279
+ if (res.status === 401) {
280
+ throw new FaireApiError("Faire access token expired or invalid", 401, null);
281
+ }
282
+ if (res.status === 204) {
283
+ return undefined;
284
+ }
285
+ const text = await res.text();
286
+ let body = null;
287
+ if (text) {
288
+ try {
289
+ body = JSON.parse(text);
290
+ }
291
+ catch {
292
+ body = text;
293
+ }
294
+ }
295
+ if (!res.ok) {
296
+ const detail = body?.error_description ||
297
+ body?.error ||
298
+ body?.message ||
299
+ (Array.isArray(body?.errors)
300
+ ? body.errors.map((e) => e?.message || e?.error || e).join("; ")
301
+ : undefined) ||
302
+ (typeof body === "string" && body ? body : undefined) ||
303
+ (body && typeof body === "object" ? JSON.stringify(body) : undefined);
304
+ const msg = detail
305
+ ? `Faire API ${res.status}: ${detail}`
306
+ : `Faire API error ${res.status}`;
307
+ throw new FaireApiError(msg, res.status, body);
308
+ }
309
+ return body;
310
+ }
311
+ throw new FaireApiError("Max retries exceeded", 599, null);
312
+ }
313
+ async backoff(attempt) {
314
+ await this.sleep(Math.min(500 * Math.pow(2, attempt - 1), 8000));
315
+ }
316
+ sleep(ms) {
317
+ return new Promise((resolve) => setTimeout(resolve, ms));
318
+ }
319
+ }
320
+ exports.FaireClient = FaireClient;
321
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFpcmUtY2xpZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2xpYi9mYWlyZS1jbGllbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQ0EsbUNBWWdCO0FBRWhCLE1BQWEsYUFBYyxTQUFRLEtBQUs7SUFHdEMsWUFBWSxPQUFlLEVBQUUsTUFBYyxFQUFFLElBQVM7UUFDcEQsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFBO1FBQ2QsSUFBSSxDQUFDLElBQUksR0FBRyxlQUFlLENBQUE7UUFDM0IsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUE7UUFDcEIsSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUE7SUFDbEIsQ0FBQztDQUNGO0FBVEQsc0NBU0M7QUFFRDs7Ozs7Ozs7Ozs7OztHQWFHO0FBQ0gsTUFBYSxXQUFXO0lBU3RCLFlBQVksSUFBd0I7UUFDbEMsSUFBSSxDQUFDLFFBQVEsR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFBO1FBQzdCLElBQUksQ0FBQyxZQUFZLEdBQUcsSUFBSSxDQUFDLFlBQVksQ0FBQTtRQUNyQyxJQUFJLENBQUMsV0FBVyxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUE7UUFDbkMsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLENBQUMsT0FBTyxJQUFJLHdCQUFnQixDQUFBO1FBQy9DLElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxDQUFDLE9BQU8sSUFBSSx3QkFBZ0IsQ0FBQTtRQUMvQyxJQUFJLENBQUMsUUFBUSxHQUFHLElBQUksQ0FBQyxRQUFRLElBQUkseUJBQWlCLENBQUE7UUFDbEQsSUFBSSxDQUFDLEtBQUssR0FBRyxJQUFJLENBQUMsS0FBSyxJQUFJLEVBQUUsQ0FBQTtJQUMvQixDQUFDO0lBRUQsSUFBSSxnQkFBZ0I7UUFDbEIsT0FBTyxJQUFJLENBQUMsV0FBVyxDQUFBO0lBQ3pCLENBQUM7SUFFRCwyRUFBMkU7SUFFM0U7OztPQUdHO0lBQ0gsbUJBQW1CLENB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
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_SCOPE = exports.DEFAULT_TOKEN_URL = exports.DEFAULT_AUTH_URL = exports.DEFAULT_API_BASE = void 0;
4
+ exports.DEFAULT_API_BASE = "https://www.faire.com/api/v2";
5
+ exports.DEFAULT_AUTH_URL = "https://www.faire.com/oauth/authorize";
6
+ exports.DEFAULT_TOKEN_URL = "https://www.faire.com/oauth/token";
7
+ // Faire OAuth currently exposes no granular scopes; the parameter is kept for
8
+ // forward-compat and to match the OAuth2 authorize URL shape.
9
+ exports.DEFAULT_SCOPE = "";
10
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL3R5cGVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQVdhLFFBQUEsZ0JBQWdCLEdBQUcsOEJBQThCLENBQUE7QUFDakQsUUFBQSxnQkFBZ0IsR0FBRyx1Q0FBdUMsQ0FBQTtBQUMxRCxRQUFBLGlCQUFpQixHQUFHLG1DQUFtQyxDQUFBO0FBQ3BFLDhFQUE4RTtBQUM5RSw4REFBOEQ7QUFDakQsUUFBQSxhQUFhLEdBQUcsRUFBRSxDQUFBIn0=
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.verifyFaireWebhook = verifyFaireWebhook;
7
+ const crypto_1 = __importDefault(require("crypto"));
8
+ const TOLERANCE_SECONDS = 5 * 60;
9
+ const isHex = (s) => /^[0-9a-fA-F]+$/.test(s) && s.length % 2 === 0;
10
+ const safeEqual = (a, b) => {
11
+ if (a.length !== b.length)
12
+ return false;
13
+ return crypto_1.default.timingSafeEqual(a, b);
14
+ };
15
+ function verifyFaireWebhook(opts) {
16
+ const { secret, headers, rawBody } = opts;
17
+ const { signature, timestamp } = headers;
18
+ if (!secret)
19
+ return { valid: false, reason: "no signing secret configured" };
20
+ if (!signature) {
21
+ return { valid: false, reason: "missing webhook signature header" };
22
+ }
23
+ // Optional replay guard — only enforced when a timestamp is present.
24
+ if (timestamp) {
25
+ const ts = Number(timestamp);
26
+ const now = Math.floor((opts.now ?? Date.now()) / 1000);
27
+ const tolerance = opts.toleranceSeconds ?? TOLERANCE_SECONDS;
28
+ if (Number.isFinite(ts) && Math.abs(now - ts) > tolerance) {
29
+ return { valid: false, reason: "timestamp outside tolerance" };
30
+ }
31
+ }
32
+ const key = Buffer.from(secret, "utf8");
33
+ const computed = crypto_1.default
34
+ .createHmac("sha256", key)
35
+ .update(rawBody, "utf8")
36
+ .digest();
37
+ const candidates = signature
38
+ .split(" ")
39
+ .map((part) => (part.includes(",") ? part.split(",")[1] : part))
40
+ .filter(Boolean);
41
+ let passed = false;
42
+ for (const sig of candidates) {
43
+ const encoding = opts.encoding || (isHex(sig) ? "hex" : "base64");
44
+ let provided;
45
+ try {
46
+ provided = Buffer.from(sig, encoding);
47
+ }
48
+ catch {
49
+ continue;
50
+ }
51
+ if (provided.length && safeEqual(provided, computed)) {
52
+ passed = true;
53
+ break;
54
+ }
55
+ }
56
+ return passed ? { valid: true } : { valid: false, reason: "signature mismatch" };
57
+ }
58
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2ViaG9vay5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvd2ViaG9vay50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQStCQSxnREFxREM7QUFwRkQsb0RBQTJCO0FBc0IzQixNQUFNLGlCQUFpQixHQUFHLENBQUMsR0FBRyxFQUFFLENBQUE7QUFFaEMsTUFBTSxLQUFLLEdBQUcsQ0FBQyxDQUFTLEVBQVcsRUFBRSxDQUFDLGdCQUFnQixDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsTUFBTSxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUE7QUFFcEYsTUFBTSxTQUFTLEdBQUcsQ0FBQyxDQUFTLEVBQUUsQ0FBUyxFQUFXLEVBQUU7SUFDbEQsSUFBSSxDQUFDLENBQUMsTUFBTSxLQUFLLENBQUMsQ0FBQyxNQUFNO1FBQUUsT0FBTyxLQUFLLENBQUE7SUFDdkMsT0FBTyxnQkFBTSxDQUFDLGVBQWUsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUE7QUFDckMsQ0FBQyxDQUFBO0FBRUQsU0FBZ0Isa0JBQWtCLENBQUMsSUFPbEM7SUFDQyxNQUFNLEVBQUUsTUFBTSxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsR0FBRyxJQUFJLENBQUE7SUFDekMsTUFBTSxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQUUsR0FBRyxPQUFPLENBQUE7SUFFeEMsSUFBSSxDQUFDLE1BQU07UUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsOEJBQThCLEVBQUUsQ0FBQTtJQUM1RSxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7UUFDZixPQUFPLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsa0NBQWtDLEVBQUUsQ0FBQTtJQUNyRSxDQUFDO0lBRUQscUVBQXFFO0lBQ3JFLElBQUksU0FBUyxFQUFFLENBQUM7UUFDZCxNQUFNLEVBQUUsR0FBRyxNQUFNLENBQUMsU0FBUyxDQUFDLENBQUE7UUFDNUIsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLElBQUksSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDLENBQUE7UUFDdkQsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLGdCQUFnQixJQUFJLGlCQUFpQixDQUFBO1FBQzVELElBQUksTUFBTSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsSUFBSSxJQUFJLENBQUMsR0FBRyxDQUFDLEdBQUcsR0FBRyxFQUFFLENBQUMsR0FBRyxTQUFTLEVBQUUsQ0FBQztZQUMxRCxPQUFPLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsNkJBQTZCLEVBQUUsQ0FBQTtRQUNoRSxDQUFDO0lBQ0gsQ0FBQztJQUVELE1BQU0sR0FBRyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLE1BQU0sQ0FBQyxDQUFBO0lBQ3ZDLE1BQU0sUUFBUSxHQUFHLGdCQUFNO1NBQ3BCLFVBQVUsQ0FBQyxRQUFRLEVBQUUsR0FBRyxDQUFDO1NBQ3pCLE1BQU0sQ0FBQyxPQUFPLEVBQUUsTUFBTSxDQUFDO1NBQ3ZCLE1BQU0sRUFBRSxDQUFBO0lBRVgsTUFBTSxVQUFVLEdBQUcsU0FBUztTQUN6QixLQUFLLENBQUMsR0FBRyxDQUFDO1NBQ1YsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDO1NBQy9ELE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQTtJQUVsQixJQUFJLE1BQU0sR0FBRyxLQUFLLENBQUE7SUFDbEIsS0FBSyxNQUFNLEdBQUcsSUFBSSxVQUFVLEVBQUUsQ0FBQztRQUM3QixNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsUUFBUSxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFBO1FBQ2pFLElBQUksUUFBZ0IsQ0FBQTtRQUNwQixJQUFJLENBQUM7WUFDSCxRQUFRLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUUsUUFBUSxDQUFDLENBQUE7UUFDdkMsQ0FBQztRQUFDLE1BQU0sQ0FBQztZQUNQLFNBQVE7UUFDVixDQUFDO1FBQ0QsSUFBSSxRQUFRLENBQUMsTUFBTSxJQUFJLFNBQVMsQ0FBQyxRQUFRLEVBQUUsUUFBUSxDQUFDLEVBQUUsQ0FBQztZQUNyRCxNQUFNLEdBQUcsSUFBSSxDQUFBO1lBQ2IsTUFBSztRQUNQLENBQUM7SUFDSCxDQUFDO0lBRUQsT0FBTyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLG9CQUFvQixFQUFFLENBQUE7QUFDbEYsQ0FBQyJ9
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const product_1 = __importDefault(require("@medusajs/medusa/product"));
7
+ const faire_sync_1 = __importDefault(require("../modules/faire-sync"));
8
+ const utils_1 = require("@medusajs/framework/utils");
9
+ /**
10
+ * Link between Product and Faire account, storing the *current* per-product
11
+ * sync state. Historical sync attempts live in the faire_sync_record table.
12
+ *
13
+ * extraColumns:
14
+ * - faire_product_token: Faire product token once created
15
+ * - faire_url: public Faire product URL
16
+ * - sync_status: pending | synced | failed | out_of_sync
17
+ * - last_synced_at: last successful sync
18
+ * - sync_error: last error message
19
+ */
20
+ exports.default = (0, utils_1.defineLink)({
21
+ linkable: product_1.default.linkable.product,
22
+ isList: true,
23
+ }, {
24
+ linkable: faire_sync_1.default.linkable.faireSyncAccount,
25
+ isList: false,
26
+ }, {
27
+ database: {
28
+ extraColumns: {
29
+ faire_product_token: {
30
+ type: "text",
31
+ nullable: true,
32
+ },
33
+ faire_url: {
34
+ type: "text",
35
+ nullable: true,
36
+ },
37
+ sync_status: {
38
+ type: "text",
39
+ defaultValue: "'pending'",
40
+ nullable: false,
41
+ },
42
+ last_synced_at: {
43
+ type: "datetime",
44
+ nullable: true,
45
+ },
46
+ sync_error: {
47
+ type: "text",
48
+ nullable: true,
49
+ },
50
+ metadata: {
51
+ type: "json",
52
+ nullable: true,
53
+ },
54
+ },
55
+ },
56
+ });
57
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvZHVjdC1mYWlyZS1zeW5jLWxpbmsuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGlua3MvcHJvZHVjdC1mYWlyZS1zeW5jLWxpbmsudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSx1RUFBb0Q7QUFDcEQsdUVBQW1EO0FBQ25ELHFEQUFzRDtBQUV0RDs7Ozs7Ozs7OztHQVVHO0FBQ0gsa0JBQWUsSUFBQSxrQkFBVSxFQUN2QjtJQUNFLFFBQVEsRUFBRSxpQkFBYSxDQUFDLFFBQVEsQ0FBQyxPQUFPO0lBQ3hDLE1BQU0sRUFBRSxJQUFJO0NBQ2IsRUFDRDtJQUNFLFFBQVEsRUFBRSxvQkFBZSxDQUFDLFFBQVEsQ0FBQyxnQkFBZ0I7SUFDbkQsTUFBTSxFQUFFLEtBQUs7Q0FDZCxFQUNEO0lBQ0UsUUFBUSxFQUFFO1FBQ1IsWUFBWSxFQUFFO1lBQ1osbUJBQW1CLEVBQUU7Z0JBQ25CLElBQUksRUFBRSxNQUFNO2dCQUNaLFFBQVEsRUFBRSxJQUFJO2FBQ2Y7WUFDRCxTQUFTLEVBQUU7Z0JBQ1QsSUFBSSxFQUFFLE1BQU07Z0JBQ1osUUFBUSxFQUFFLElBQUk7YUFDZjtZQUNELFdBQVcsRUFBRTtnQkFDWCxJQUFJLEVBQUUsTUFBTTtnQkFDWixZQUFZLEVBQUUsV0FBVztnQkFDekIsUUFBUSxFQUFFLEtBQUs7YUFDaEI7WUFDRCxjQUFjLEVBQUU7Z0JBQ2QsSUFBSSxFQUFFLFVBQVU7Z0JBQ2hCLFFBQVEsRUFBRSxJQUFJO2FBQ2Y7WUFDRCxVQUFVLEVBQUU7Z0JBQ1YsSUFBSSxFQUFFLE1BQU07Z0JBQ1osUUFBUSxFQUFFLElBQUk7YUFDZjtZQUNELFFBQVEsRUFBRTtnQkFDUixJQUFJLEVBQUUsTUFBTTtnQkFDWixRQUFRLEVBQUUsSUFBSTthQUNmO1NBQ0Y7S0FDRjtDQUNGLENBQ0YsQ0FBQSJ9
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.FaireSyncService = exports.FAIRE_SYNC_MODULE = void 0;
7
+ const utils_1 = require("@medusajs/framework/utils");
8
+ const service_1 = __importDefault(require("./service"));
9
+ exports.FaireSyncService = service_1.default;
10
+ exports.FAIRE_SYNC_MODULE = "faireSync";
11
+ const FaireSyncModule = (0, utils_1.Module)(exports.FAIRE_SYNC_MODULE, {
12
+ service: service_1.default,
13
+ });
14
+ exports.default = FaireSyncModule;
15
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvbW9kdWxlcy9mYWlyZS1zeW5jL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQUFBLHFEQUFrRDtBQUNsRCx3REFBd0M7QUFTL0IsMkJBVEYsaUJBQWdCLENBU0U7QUFQWixRQUFBLGlCQUFpQixHQUFHLFdBQVcsQ0FBQTtBQUU1QyxNQUFNLGVBQWUsR0FBRyxJQUFBLGNBQU0sRUFBQyx5QkFBaUIsRUFBRTtJQUNoRCxPQUFPLEVBQUUsaUJBQWdCO0NBQzFCLENBQUMsQ0FBQTtBQUVGLGtCQUFlLGVBQWUsQ0FBQSJ9
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Migration20260707211346 = void 0;
4
+ const migrations_1 = require("@medusajs/framework/mikro-orm/migrations");
5
+ class Migration20260707211346 extends migrations_1.Migration {
6
+ async up() {
7
+ this.addSql(`alter table if exists "faire_webhook_event" drop constraint if exists "faire_webhook_event_webhook_id_unique";`);
8
+ this.addSql(`alter table if exists "faire_order" drop constraint if exists "faire_order_order_token_unique";`);
9
+ this.addSql(`create table if not exists "faire_order" ("id" text not null, "order_token" text not null, "order_id" text null, "status" text not null default 'created', "currency" text null, "total" text null, "buyer_name" text null, "raw" jsonb null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "faire_order_pkey" primary key ("id"));`);
10
+ this.addSql(`CREATE UNIQUE INDEX IF NOT EXISTS "IDX_faire_order_order_token_unique" ON "faire_order" ("order_token") WHERE deleted_at IS NULL;`);
11
+ this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_faire_order_deleted_at" ON "faire_order" ("deleted_at") WHERE deleted_at IS NULL;`);
12
+ this.addSql(`create table if not exists "faire_sync_account" ("id" text not null, "brand_id" text not null, "brand_name" text not null, "currency" text null, "country" text null, "access_token" text not null, "refresh_token" text null, "token_expires_at" timestamptz null, "brand_info" jsonb null, "is_active" boolean not null default true, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "faire_sync_account_pkey" primary key ("id"));`);
13
+ this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_faire_sync_account_deleted_at" ON "faire_sync_account" ("deleted_at") WHERE deleted_at IS NULL;`);
14
+ this.addSql(`create table if not exists "faire_sync_batch" ("id" text not null, "transaction_id" text null, "status" text check ("status" in ('pending', 'processing', 'completed', 'failed')) not null default 'pending', "total_products" integer not null default 0, "synced_count" integer not null default 0, "failed_count" integer not null default 0, "error_log" jsonb null, "started_at" timestamptz null, "completed_at" timestamptz null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "faire_sync_batch_pkey" primary key ("id"));`);
15
+ this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_faire_sync_batch_deleted_at" ON "faire_sync_batch" ("deleted_at") WHERE deleted_at IS NULL;`);
16
+ this.addSql(`create table if not exists "faire_sync_record" ("id" text not null, "product_id" text not null, "account_id" text not null, "product_token" text null, "product_url" text null, "product_state" text null, "action" text check ("action" in ('create', 'update', 'delete')) not null default 'create', "status" text check ("status" in ('pending', 'syncing', 'success', 'failed', 'draft')) not null default 'pending', "published" boolean not null default false, "error_message" text null, "warnings" jsonb null, "metadata" jsonb null, "synced_at" timestamptz null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "faire_sync_record_pkey" primary key ("id"));`);
17
+ this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_faire_sync_record_deleted_at" ON "faire_sync_record" ("deleted_at") WHERE deleted_at IS NULL;`);
18
+ this.addSql(`create table if not exists "faire_sync_settings" ("id" text not null, "account_id" text null, "default_brand_id" text null, "default_wholesale_markup_percent" integer null, "default_min_order_quantity" integer not null default 1, "default_lead_time_days" integer null, "default_shipping_policy_id" text null, "default_category" text null, "auto_publish" boolean not null default false, "follow_product_status" boolean not null default true, "pending_oauth" jsonb null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "faire_sync_settings_pkey" primary key ("id"));`);
19
+ this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_faire_sync_settings_deleted_at" ON "faire_sync_settings" ("deleted_at") WHERE deleted_at IS NULL;`);
20
+ this.addSql(`create table if not exists "faire_webhook_event" ("id" text not null, "webhook_id" text not null, "event_type" text not null, "brand_id" text null, "resource_url" text null, "payload" jsonb null, "resource" jsonb null, "processed" boolean not null default false, "error" text null, "received_at" timestamptz null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "faire_webhook_event_pkey" primary key ("id"));`);
21
+ this.addSql(`CREATE UNIQUE INDEX IF NOT EXISTS "IDX_faire_webhook_event_webhook_id_unique" ON "faire_webhook_event" ("webhook_id") WHERE deleted_at IS NULL;`);
22
+ this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_faire_webhook_event_deleted_at" ON "faire_webhook_event" ("deleted_at") WHERE deleted_at IS NULL;`);
23
+ }
24
+ async down() {
25
+ this.addSql(`drop table if exists "faire_order" cascade;`);
26
+ this.addSql(`drop table if exists "faire_sync_account" cascade;`);
27
+ this.addSql(`drop table if exists "faire_sync_batch" cascade;`);
28
+ this.addSql(`drop table if exists "faire_sync_record" cascade;`);
29
+ this.addSql(`drop table if exists "faire_sync_settings" cascade;`);
30
+ this.addSql(`drop table if exists "faire_webhook_event" cascade;`);
31
+ }
32
+ }
33
+ exports.Migration20260707211346 = Migration20260707211346;
34
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTWlncmF0aW9uMjAyNjA3MDcyMTEzNDYuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvbW9kdWxlcy9mYWlyZS1zeW5jL21pZ3JhdGlvbnMvTWlncmF0aW9uMjAyNjA3MDcyMTEzNDYudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEseUVBQXFFO0FBRXJFLE1BQWEsdUJBQXdCLFNBQVEsc0JBQVM7SUFFM0MsS0FBSyxDQUFDLEVBQUU7UUFDZixJQUFJLENBQUMsTUFBTSxDQUFDLGdIQUFnSCxDQUFDLENBQUM7UUFDOUgsSUFBSSxDQUFDLE1BQU0sQ0FBQyxpR0FBaUcsQ0FBQyxDQUFDO1FBQy9HLElBQUksQ0FBQyxNQUFNLENBQUMsbWFBQW1hLENBQUMsQ0FBQztRQUNqYixJQUFJLENBQUMsTUFBTSxDQUFDLG1JQUFtSSxDQUFDLENBQUM7UUFDakosSUFBSSxDQUFDLE1BQU0sQ0FBQyxtSEFBbUgsQ0FBQyxDQUFDO1FBRWpJLElBQUksQ0FBQyxNQUFNLENBQUMsb2dCQUFvZ0IsQ0FBQyxDQUFDO1FBQ2xoQixJQUFJLENBQUMsTUFBTSxDQUFDLGlJQUFpSSxDQUFDLENBQUM7UUFFL0ksSUFBSSxDQUFDLE1BQU0sQ0FBQyxtbUJBQW1tQixDQUFDLENBQUM7UUFDam5CLElBQUksQ0FBQyxNQUFNLENBQUMsNkhBQTZILENBQUMsQ0FBQztRQUUzSSxJQUFJLENBQUMsTUFBTSxDQUFDLHd1QkFBd3VCLENBQUMsQ0FBQztRQUN0dkIsSUFBSSxDQUFDLE1BQU0sQ0FBQywrSEFBK0gsQ0FBQyxDQUFDO1FBRTdJLElBQUksQ0FBQyxNQUFNLENBQUMsa3BCQUFrcEIsQ0FBQyxDQUFDO1FBQ2hxQixJQUFJLENBQUMsTUFBTSxDQUFDLG1JQUFtSSxDQUFDLENBQUM7UUFFakosSUFBSSxDQUFDLE1BQU0sQ0FBQyx1ZkFBdWYsQ0FBQyxDQUFDO1FBQ3JnQixJQUFJLENBQUMsTUFBTSxDQUFDLGlKQUFpSixDQUFDLENBQUM7UUFDL0osSUFBSSxDQUFDLE1BQU0sQ0FBQyxtSUFBbUksQ0FBQyxDQUFDO0lBQ25KLENBQUM7SUFFUSxLQUFLLENBQUMsSUFBSTtRQUNqQixJQUFJLENBQUMsTUFBTSxDQUFDLDZDQUE2QyxDQUFDLENBQUM7UUFFM0QsSUFBSSxDQUFDLE1BQU0sQ0FBQyxvREFBb0QsQ0FBQyxDQUFDO1FBRWxFLElBQUksQ0FBQyxNQUFNLENBQUMsa0RBQWtELENBQUMsQ0FBQztRQUVoRSxJQUFJLENBQUMsTUFBTSxDQUFDLG1EQUFtRCxDQUFDLENBQUM7UUFFakUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxxREFBcUQsQ0FBQyxDQUFDO1FBRW5FLElBQUksQ0FBQyxNQUFNLENBQUMscURBQXFELENBQUMsQ0FBQztJQUNyRSxDQUFDO0NBRUY7QUF4Q0QsMERBd0NDIn0=
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const utils_1 = require("@medusajs/framework/utils");
4
+ /**
5
+ * Maps a Faire order to the Medusa order created from it. `order_token` is
6
+ * unique — the idempotency key so a Faire webhook retry (or a later
7
+ * order.canceled/shipped event for the same order) never re-creates the
8
+ * Medusa order.
9
+ */
10
+ const FaireOrder = utils_1.model.define("faire_order", {
11
+ id: utils_1.model.id().primaryKey(),
12
+ order_token: utils_1.model.text().unique(),
13
+ order_id: utils_1.model.text().nullable(), // Medusa order id
14
+ status: utils_1.model.text().default("created"),
15
+ currency: utils_1.model.text().nullable(),
16
+ total: utils_1.model.text().nullable(), // decimal string (cents as integer string)
17
+ buyer_name: utils_1.model.text().nullable(),
18
+ raw: utils_1.model.json().nullable(),
19
+ });
20
+ exports.default = FaireOrder;
21
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFpcmUtb3JkZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvbW9kdWxlcy9mYWlyZS1zeW5jL21vZGVscy9mYWlyZS1vcmRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLHFEQUFpRDtBQUVqRDs7Ozs7R0FLRztBQUNILE1BQU0sVUFBVSxHQUFHLGFBQUssQ0FBQyxNQUFNLENBQUMsYUFBYSxFQUFFO0lBQzdDLEVBQUUsRUFBRSxhQUFLLENBQUMsRUFBRSxFQUFFLENBQUMsVUFBVSxFQUFFO0lBQzNCLFdBQVcsRUFBRSxhQUFLLENBQUMsSUFBSSxFQUFFLENBQUMsTUFBTSxFQUFFO0lBQ2xDLFFBQVEsRUFBRSxhQUFLLENBQUMsSUFBSSxFQUFFLENBQUMsUUFBUSxFQUFFLEVBQUUsa0JBQWtCO0lBQ3JELE1BQU0sRUFBRSxhQUFLLENBQUMsSUFBSSxFQUFFLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQztJQUN2QyxRQUFRLEVBQUUsYUFBSyxDQUFDLElBQUksRUFBRSxDQUFDLFFBQVEsRUFBRTtJQUNqQyxLQUFLLEVBQUUsYUFBSyxDQUFDLElBQUksRUFBRSxDQUFDLFFBQVEsRUFBRSxFQUFFLDJDQUEyQztJQUMzRSxVQUFVLEVBQUUsYUFBSyxDQUFDLElBQUksRUFBRSxDQUFDLFFBQVEsRUFBRTtJQUNuQyxHQUFHLEVBQUUsYUFBSyxDQUFDLElBQUksRUFBRSxDQUFDLFFBQVEsRUFBRTtDQUM3QixDQUFDLENBQUE7QUFFRixrQkFBZSxVQUFVLENBQUEifQ==
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const utils_1 = require("@medusajs/framework/utils");
4
+ const FaireSyncAccount = utils_1.model.define("faire_sync_account", {
5
+ id: utils_1.model.id().primaryKey(),
6
+ brand_id: utils_1.model.text(),
7
+ brand_name: utils_1.model.text(),
8
+ currency: utils_1.model.text().nullable(),
9
+ country: utils_1.model.text().nullable(),
10
+ access_token: utils_1.model.text(),
11
+ refresh_token: utils_1.model.text().nullable(),
12
+ token_expires_at: utils_1.model.dateTime().nullable(),
13
+ brand_info: utils_1.model.json().nullable(),
14
+ is_active: utils_1.model.boolean().default(true),
15
+ });
16
+ exports.default = FaireSyncAccount;
17
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFpcmUtc3luYy1hY2NvdW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL21vZHVsZXMvZmFpcmUtc3luYy9tb2RlbHMvZmFpcmUtc3luYy1hY2NvdW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEscURBQWlEO0FBRWpELE1BQU0sZ0JBQWdCLEdBQUcsYUFBSyxDQUFDLE1BQU0sQ0FBQyxvQkFBb0IsRUFBRTtJQUMxRCxFQUFFLEVBQUUsYUFBSyxDQUFDLEVBQUUsRUFBRSxDQUFDLFVBQVUsRUFBRTtJQUMzQixRQUFRLEVBQUUsYUFBSyxDQUFDLElBQUksRUFBRTtJQUN0QixVQUFVLEVBQUUsYUFBSyxDQUFDLElBQUksRUFBRTtJQUN4QixRQUFRLEVBQUUsYUFBSyxDQUFDLElBQUksRUFBRSxDQUFDLFFBQVEsRUFBRTtJQUNqQyxPQUFPLEVBQUUsYUFBSyxDQUFDLElBQUksRUFBRSxDQUFDLFFBQVEsRUFBRTtJQUNoQyxZQUFZLEVBQUUsYUFBSyxDQUFDLElBQUksRUFBRTtJQUMxQixhQUFhLEVBQUUsYUFBSyxDQUFDLElBQUksRUFBRSxDQUFDLFFBQVEsRUFBRTtJQUN0QyxnQkFBZ0IsRUFBRSxhQUFLLENBQUMsUUFBUSxFQUFFLENBQUMsUUFBUSxFQUFFO0lBQzdDLFVBQVUsRUFBRSxhQUFLLENBQUMsSUFBSSxFQUFFLENBQUMsUUFBUSxFQUFFO0lBQ25DLFNBQVMsRUFBRSxhQUFLLENBQUMsT0FBTyxFQUFFLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQztDQUN6QyxDQUFDLENBQUE7QUFFRixrQkFBZSxnQkFBZ0IsQ0FBQSJ9
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const utils_1 = require("@medusajs/framework/utils");
4
+ const FaireSyncBatch = utils_1.model.define("faire_sync_batch", {
5
+ id: utils_1.model.id().primaryKey(),
6
+ transaction_id: utils_1.model.text().nullable(),
7
+ status: utils_1.model
8
+ .enum(["pending", "processing", "completed", "failed"])
9
+ .default("pending"),
10
+ total_products: utils_1.model.number().default(0),
11
+ synced_count: utils_1.model.number().default(0),
12
+ failed_count: utils_1.model.number().default(0),
13
+ error_log: utils_1.model.json().nullable(),
14
+ started_at: utils_1.model.dateTime().nullable(),
15
+ completed_at: utils_1.model.dateTime().nullable(),
16
+ });
17
+ exports.default = FaireSyncBatch;
18
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFpcmUtc3luYy1iYXRjaC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3NyYy9tb2R1bGVzL2ZhaXJlLXN5bmMvbW9kZWxzL2ZhaXJlLXN5bmMtYmF0Y2gudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSxxREFBaUQ7QUFFakQsTUFBTSxjQUFjLEdBQUcsYUFBSyxDQUFDLE1BQU0sQ0FBQyxrQkFBa0IsRUFBRTtJQUN0RCxFQUFFLEVBQUUsYUFBSyxDQUFDLEVBQUUsRUFBRSxDQUFDLFVBQVUsRUFBRTtJQUMzQixjQUFjLEVBQUUsYUFBSyxDQUFDLElBQUksRUFBRSxDQUFDLFFBQVEsRUFBRTtJQUN2QyxNQUFNLEVBQUUsYUFBSztTQUNWLElBQUksQ0FBQyxDQUFDLFNBQVMsRUFBRSxZQUFZLEVBQUUsV0FBVyxFQUFFLFFBQVEsQ0FBQyxDQUFDO1NBQ3RELE9BQU8sQ0FBQyxTQUFTLENBQUM7SUFDckIsY0FBYyxFQUFFLGFBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO0lBQ3pDLFlBQVksRUFBRSxhQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQztJQUN2QyxZQUFZLEVBQUUsYUFBSyxDQUFDLE1BQU0sRUFBRSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7SUFDdkMsU0FBUyxFQUFFLGFBQUssQ0FBQyxJQUFJLEVBQUUsQ0FBQyxRQUFRLEVBQUU7SUFDbEMsVUFBVSxFQUFFLGFBQUssQ0FBQyxRQUFRLEVBQUUsQ0FBQyxRQUFRLEVBQUU7SUFDdkMsWUFBWSxFQUFFLGFBQUssQ0FBQyxRQUFRLEVBQUUsQ0FBQyxRQUFRLEVBQUU7Q0FDMUMsQ0FBQyxDQUFBO0FBRUYsa0JBQWUsY0FBYyxDQUFBIn0=
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const utils_1 = require("@medusajs/framework/utils");
4
+ const FaireSyncRecord = utils_1.model.define("faire_sync_record", {
5
+ id: utils_1.model.id().primaryKey(),
6
+ product_id: utils_1.model.text(),
7
+ account_id: utils_1.model.text(),
8
+ product_token: utils_1.model.text().nullable(),
9
+ product_url: utils_1.model.text().nullable(),
10
+ product_state: utils_1.model.text().nullable(),
11
+ action: utils_1.model.enum(["create", "update", "delete"]).default("create"),
12
+ status: utils_1.model
13
+ .enum(["pending", "syncing", "success", "failed", "draft"])
14
+ .default("pending"),
15
+ published: utils_1.model.boolean().default(false),
16
+ error_message: utils_1.model.text().nullable(),
17
+ warnings: utils_1.model.json().nullable(),
18
+ metadata: utils_1.model.json().nullable(),
19
+ synced_at: utils_1.model.dateTime().nullable(),
20
+ });
21
+ exports.default = FaireSyncRecord;
22
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFpcmUtc3luYy1yZWNvcmQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvbW9kdWxlcy9mYWlyZS1zeW5jL21vZGVscy9mYWlyZS1zeW5jLXJlY29yZC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLHFEQUFpRDtBQUVqRCxNQUFNLGVBQWUsR0FBRyxhQUFLLENBQUMsTUFBTSxDQUFDLG1CQUFtQixFQUFFO0lBQ3hELEVBQUUsRUFBRSxhQUFLLENBQUMsRUFBRSxFQUFFLENBQUMsVUFBVSxFQUFFO0lBQzNCLFVBQVUsRUFBRSxhQUFLLENBQUMsSUFBSSxFQUFFO0lBQ3hCLFVBQVUsRUFBRSxhQUFLLENBQUMsSUFBSSxFQUFFO0lBQ3hCLGFBQWEsRUFBRSxhQUFLLENBQUMsSUFBSSxFQUFFLENBQUMsUUFBUSxFQUFFO0lBQ3RDLFdBQVcsRUFBRSxhQUFLLENBQUMsSUFBSSxFQUFFLENBQUMsUUFBUSxFQUFFO0lBQ3BDLGFBQWEsRUFBRSxhQUFLLENBQUMsSUFBSSxFQUFFLENBQUMsUUFBUSxFQUFFO0lBQ3RDLE1BQU0sRUFBRSxhQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsUUFBUSxFQUFFLFFBQVEsRUFBRSxRQUFRLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUM7SUFDcEUsTUFBTSxFQUFFLGFBQUs7U0FDVixJQUFJLENBQUMsQ0FBQyxTQUFTLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxRQUFRLEVBQUUsT0FBTyxDQUFDLENBQUM7U0FDMUQsT0FBTyxDQUFDLFNBQVMsQ0FBQztJQUNyQixTQUFTLEVBQUUsYUFBSyxDQUFDLE9BQU8sRUFBRSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUM7SUFDekMsYUFBYSxFQUFFLGFBQUssQ0FBQyxJQUFJLEVBQUUsQ0FBQyxRQUFRLEVBQUU7SUFDdEMsUUFBUSxFQUFFLGFBQUssQ0FBQyxJQUFJLEVBQUUsQ0FBQyxRQUFRLEVBQUU7SUFDakMsUUFBUSxFQUFFLGFBQUssQ0FBQyxJQUFJLEVBQUUsQ0FBQyxRQUFRLEVBQUU7SUFDakMsU0FBUyxFQUFFLGFBQUssQ0FBQyxRQUFRLEVBQUUsQ0FBQyxRQUFRLEVBQUU7Q0FDdkMsQ0FBQyxDQUFBO0FBRUYsa0JBQWUsZUFBZSxDQUFBIn0=