@skuio/storefront 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @skuio/storefront
2
2
 
3
- Typed catalogue, checkout and order primitives for storefronts on the SKU.io
3
+ Typed catalog, checkout and order primitives for storefronts on the SKU.io
4
4
  storefront kit. Framework-agnostic: takes and returns plain data.
5
5
 
6
6
  ```ts
@@ -17,7 +17,7 @@ import { createStripeCheckout, parseStripeWebhook } from '@skuio/storefront/stri
17
17
  Storefront API: live availability, checkout created by SKU.io on the
18
18
  tenant\'s processor, orders born in SKU.io).
19
19
  - Stripe helpers behind a separate entry point so non-Stripe stores never
20
- bundle it. Prices are only ever read from the catalogue on the server.
20
+ bundle it. Prices are only ever read from the catalog on the server.
21
21
  - Cart functions — pure, browser-agnostic, never trusted for money.
22
22
 
23
23
  Start a store with `npx @skuio/cli create my-store`. Kit and docs:
@@ -0,0 +1,59 @@
1
+ -- Quotes (request for quote): a buyer — signed in or a guest who never makes an account — asks for a price on a
2
+ -- list of products; the operations system prices it through the Store API; the buyer accepts it, which places an
3
+ -- order at exactly the quoted prices, or declines it. Money is integer minor units; the priced columns stay NULL
4
+ -- until an offer exists.
5
+ CREATE TABLE quotes (
6
+ id TEXT PRIMARY KEY,
7
+ number INTEGER NOT NULL UNIQUE,
8
+ status TEXT NOT NULL, -- requested | quoted | accepted | declined | expired | cancelled
9
+ customer_id TEXT NOT NULL REFERENCES customers (id),
10
+ email TEXT NOT NULL,
11
+ company TEXT NULL,
12
+ message TEXT NULL,
13
+ shipping_address_json TEXT NULL,
14
+ currency TEXT NOT NULL,
15
+ subtotal INTEGER NULL,
16
+ shipping INTEGER NULL,
17
+ tax INTEGER NULL,
18
+ total INTEGER NULL,
19
+ offer_note TEXT NULL,
20
+ expires_at TEXT NULL,
21
+ external_reference TEXT NULL,
22
+ order_id TEXT NULL REFERENCES orders (id),
23
+ decline_reason TEXT NULL,
24
+ metadata_json TEXT NOT NULL DEFAULT '{}',
25
+ access_key TEXT NOT NULL,
26
+ created_at TEXT NOT NULL,
27
+ updated_at TEXT NOT NULL,
28
+ quoted_at TEXT NULL,
29
+ accepted_at TEXT NULL,
30
+ declined_at TEXT NULL,
31
+ cancelled_at TEXT NULL
32
+ );
33
+ CREATE INDEX quotes_updated ON quotes (updated_at, id);
34
+ CREATE INDEX quotes_customer ON quotes (customer_id, created_at);
35
+ CREATE INDEX quotes_email ON quotes (email, created_at);
36
+ CREATE INDEX quotes_status ON quotes (status, expires_at);
37
+
38
+ CREATE TABLE quote_lines (
39
+ id TEXT PRIMARY KEY,
40
+ quote_id TEXT NOT NULL REFERENCES quotes (id) ON DELETE CASCADE,
41
+ sku TEXT NOT NULL,
42
+ name TEXT NOT NULL,
43
+ quantity INTEGER NOT NULL,
44
+ list_amount INTEGER NULL, -- the catalog price when the buyer asked; for reference only
45
+ unit_amount INTEGER NULL, -- the offered price; NULL until quoted
46
+ total_amount INTEGER NULL,
47
+ image_url TEXT NULL,
48
+ position INTEGER NOT NULL DEFAULT 0
49
+ );
50
+ CREATE INDEX quote_lines_quote ON quote_lines (quote_id, position);
51
+
52
+ CREATE TABLE quote_events (
53
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
54
+ quote_id TEXT NOT NULL REFERENCES quotes (id) ON DELETE CASCADE,
55
+ type TEXT NOT NULL,
56
+ data_json TEXT NULL,
57
+ created_at TEXT NOT NULL
58
+ );
59
+ CREATE INDEX quote_events_quote ON quote_events (quote_id, id);
package/dist/api.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { CategoryRecord, ProductRecord, Connection, Store } from './store.js';
2
- import { O as Order, F as Fulfillment } from './config-C4YCi7m4.js';
2
+ import { O as Order, Q as QuoteStatus, F as Fulfillment, S as StoreQuote } from './config-CCxTka_z.js';
3
3
  import './driver-Cr4l6GgG.js';
4
4
  import './types-CWreCyxc.js';
5
5
 
@@ -79,6 +79,48 @@ interface ApiOrder {
79
79
  paid_at: string | null;
80
80
  cancelled_at: string | null;
81
81
  }
82
+ interface ApiQuote {
83
+ id: string;
84
+ /** With the store's quote prefix: "Q1001". */
85
+ number: string;
86
+ status: QuoteStatus;
87
+ currency: string;
88
+ customer: {
89
+ id: string;
90
+ email: string;
91
+ name: string | null;
92
+ phone: string | null;
93
+ };
94
+ company: string | null;
95
+ message: string | null;
96
+ shipping_address: ApiAddress | null;
97
+ lines: {
98
+ id: string;
99
+ sku: string;
100
+ name: string;
101
+ quantity: number;
102
+ list_amount: ApiMoney | null;
103
+ unit_amount: ApiMoney | null;
104
+ total_amount: ApiMoney | null;
105
+ }[];
106
+ subtotal: ApiMoney | null;
107
+ shipping: ApiMoney | null;
108
+ tax: ApiMoney | null;
109
+ total: ApiMoney | null;
110
+ note: string | null;
111
+ expires_at: string | null;
112
+ external_reference: string | null;
113
+ /** The order the buyer's acceptance placed; it also arrives through GET /orders with `metadata.quote_id`. */
114
+ order_id: string | null;
115
+ decline_reason: string | null;
116
+ metadata: Record<string, string>;
117
+ created_at: string;
118
+ updated_at: string;
119
+ quoted_at: string | null;
120
+ accepted_at: string | null;
121
+ declined_at: string | null;
122
+ cancelled_at: string | null;
123
+ }
82
124
  interface ApiProduct {
83
125
  sku: string;
84
126
  name: string;
@@ -126,7 +168,9 @@ declare function serializeFulfillment(f: Fulfillment): ApiFulfillment;
126
168
  * sees, with the store's prefix (VLD4200), so the consumer shows the same number.
127
169
  */
128
170
  declare function serializeOrder(o: Order, numberPrefix?: string): ApiOrder;
129
- /** The Store API's view of a catalogue product — the stored row, not the shopper projection. */
171
+ /** The Store API's view of a quote. `access_key` never leaves the store. */
172
+ declare function serializeQuote(q: StoreQuote, numberPrefix?: string): ApiQuote;
173
+ /** The Store API's view of a catalog product — the stored row, not the shopper projection. */
130
174
  declare function serializeProduct(p: ProductRecord): ApiProduct;
131
175
  declare function serializeCategory(c: CategoryRecord): ApiCategory;
132
176
 
@@ -171,7 +215,7 @@ declare function serializeConnection(c: Connection): {
171
215
  id: string;
172
216
  name: string;
173
217
  tenant: string | null;
174
- scopes: ("orders" | "payments" | "fulfillments" | "inventory" | "catalog")[];
218
+ scopes: ("orders" | "payments" | "fulfillments" | "inventory" | "catalog" | "quotes")[];
175
219
  key_prefix: string | null;
176
220
  created_at: string;
177
221
  issued_at: string | null;
@@ -179,4 +223,4 @@ declare function serializeConnection(c: Connection): {
179
223
  revoked_at: string | null;
180
224
  };
181
225
 
182
- export { type ApiCaller, type ApiCategory, type ApiFulfillment, type ApiOrder, type ApiProduct, STORE_API_BASE, STORE_API_BASES, type StoreApiOptions, ValidationError, handleStoreApi, serializeCategory, serializeConnection, serializeFulfillment, serializeOrder, serializeProduct };
226
+ export { type ApiCaller, type ApiCategory, type ApiFulfillment, type ApiOrder, type ApiProduct, type ApiQuote, STORE_API_BASE, STORE_API_BASES, type StoreApiOptions, ValidationError, handleStoreApi, serializeCategory, serializeConnection, serializeFulfillment, serializeOrder, serializeProduct, serializeQuote };
package/dist/api.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  API_KEY_PREFIX,
3
3
  StoreError,
4
4
  secretsEqual
5
- } from "./chunk-X7VXO4QU.js";
5
+ } from "./chunk-RUUUZ2EL.js";
6
6
 
7
7
  // src/api/serialize.ts
8
8
  var money = (m) => ({ amount: m.amount, currency: m.currency.toUpperCase() });
@@ -64,6 +64,44 @@ function serializeOrder(o, numberPrefix = "") {
64
64
  cancelled_at: o.cancelledAt
65
65
  };
66
66
  }
67
+ var maybeMoney = (m) => m ? money(m) : null;
68
+ function serializeQuote(q, numberPrefix = "") {
69
+ return {
70
+ id: q.id,
71
+ number: `${numberPrefix}${q.number}`,
72
+ status: q.status,
73
+ currency: q.currency.toUpperCase(),
74
+ customer: { id: q.customer.id, email: q.customer.email, name: q.customer.name, phone: q.customer.phone },
75
+ company: q.company,
76
+ message: q.message,
77
+ shipping_address: address(q.shippingAddress),
78
+ lines: q.lines.map((l) => ({
79
+ id: l.id,
80
+ sku: l.sku,
81
+ name: l.name,
82
+ quantity: l.quantity,
83
+ list_amount: maybeMoney(l.listAmount),
84
+ unit_amount: maybeMoney(l.unitAmount),
85
+ total_amount: maybeMoney(l.totalAmount)
86
+ })),
87
+ subtotal: maybeMoney(q.subtotal),
88
+ shipping: maybeMoney(q.shipping),
89
+ tax: maybeMoney(q.tax),
90
+ total: maybeMoney(q.total),
91
+ note: q.offerNote,
92
+ expires_at: q.expiresAt,
93
+ external_reference: q.externalReference,
94
+ order_id: q.orderId,
95
+ decline_reason: q.declineReason,
96
+ metadata: q.metadata,
97
+ created_at: q.createdAt,
98
+ updated_at: q.updatedAt,
99
+ quoted_at: q.quotedAt,
100
+ accepted_at: q.acceptedAt,
101
+ declined_at: q.declinedAt,
102
+ cancelled_at: q.cancelledAt
103
+ };
104
+ }
67
105
  function serializeProduct(p) {
68
106
  return {
69
107
  sku: p.sku,
@@ -370,6 +408,55 @@ function validateOrderPatch(body) {
370
408
  finish(details, "Invalid order patch");
371
409
  return out;
372
410
  }
411
+ function validateQuotePatch(body) {
412
+ const details = {};
413
+ const out = {};
414
+ if ("external_reference" in body) {
415
+ const ref = str(details, body, "external_reference", "external_reference", { nullable: true, max: 64 });
416
+ if (ref !== void 0) out.external_reference = ref;
417
+ }
418
+ if (body.status !== void 0) {
419
+ if (body.status !== "cancelled") details.status = 'can only be set to "cancelled"';
420
+ else out.status = "cancelled";
421
+ }
422
+ if (body.offer !== void 0) {
423
+ if (!isObject(body.offer)) details.offer = "must be an object";
424
+ else {
425
+ const o = body.offer;
426
+ const lines = [];
427
+ if (!Array.isArray(o.lines) || o.lines.length === 0) details["offer.lines"] = "must be a non-empty array";
428
+ else
429
+ o.lines.forEach((raw, i) => {
430
+ if (!isObject(raw)) {
431
+ details[`offer.lines.${i}`] = "must be an object";
432
+ return;
433
+ }
434
+ const sku = str(details, raw, "sku", `offer.lines.${i}.sku`, { required: true, max: 100 });
435
+ const name = str(details, raw, "name", `offer.lines.${i}.name`, { nullable: true, max: 300 });
436
+ if (!isInt(raw.quantity) || raw.quantity < 1) details[`offer.lines.${i}.quantity`] = "must be an integer of at least 1";
437
+ const unit = moneyField(details, raw.unit_amount, `offer.lines.${i}.unit_amount`, { required: true });
438
+ if (sku && isInt(raw.quantity) && raw.quantity >= 1 && unit) lines.push({ sku: sku.trim(), name: name ?? null, quantity: raw.quantity, unit_amount: unit });
439
+ });
440
+ const shipping = moneyField(details, o.shipping, "offer.shipping", { nullable: true });
441
+ const tax = moneyField(details, o.tax, "offer.tax", { nullable: true });
442
+ const expiresAt = isoDate(details, o.expires_at, "offer.expires_at");
443
+ const note = str(details, o, "note", "offer.note", { nullable: true, max: 4e3 });
444
+ if (o.notify_customer !== void 0 && typeof o.notify_customer !== "boolean") details["offer.notify_customer"] = "must be a boolean";
445
+ out.offer = {
446
+ lines,
447
+ shipping: shipping ?? null,
448
+ tax: tax ?? null,
449
+ expires_at: expiresAt ?? null,
450
+ note: note ?? null,
451
+ notify_customer: o.notify_customer === void 0 ? true : o.notify_customer
452
+ };
453
+ }
454
+ }
455
+ if (out.status && out.offer) details.status = "cannot be combined with an offer";
456
+ if (!("external_reference" in body) && body.status === void 0 && body.offer === void 0) details.body = "must include external_reference, status or offer";
457
+ finish(details, "Invalid quote patch");
458
+ return out;
459
+ }
373
460
 
374
461
  // src/api/index.ts
375
462
  var STORE_API_BASES = ["/api/store/v1", "/api/skuio/v1"];
@@ -448,6 +535,7 @@ function scopeFor(path) {
448
535
  if (first === "orders") return /^\/orders\/[^/]+\/fulfillments$/.test(path) ? "fulfillments" : "orders";
449
536
  if (first === "inventory") return "inventory";
450
537
  if (first === "products" || first === "categories") return "catalog";
538
+ if (first === "quotes") return "quotes";
451
539
  return null;
452
540
  }
453
541
  async function route(method, path, url, request, store, caller) {
@@ -498,6 +586,27 @@ async function route(method, path, url, request, store, caller) {
498
586
  return json({ data: serializeFulfillment(result.fulfillment) }, result.created ? 201 : 200);
499
587
  }
500
588
  }
589
+ if (segments[0] === "quotes") {
590
+ const prefix = store.config.quotes.numberPrefix;
591
+ if (segments.length === 1 && method === "GET") {
592
+ const query = validateListQuery(url.searchParams, { defaultLimit: 50, maxLimit: 200, allowUpdatedSince: true });
593
+ await store.quotes.expireStale();
594
+ const page = await store.quotes.changedSince(query.updatedSince, query.cursor, query.limit);
595
+ return json({ data: page.quotes.map((q) => serializeQuote(q, prefix)), next_cursor: page.nextCursor });
596
+ }
597
+ const id = segments[1] ? decodeURIComponent(segments[1]) : "";
598
+ if (segments.length === 2 && id) {
599
+ if (method === "GET") return json({ data: serializeQuote(await store.quotes.require(id), prefix) });
600
+ if (method === "PATCH") {
601
+ const patch = validateQuotePatch(await readJson(request));
602
+ let quote = await store.quotes.require(id);
603
+ if (patch.external_reference !== void 0) quote = await store.quotes.setExternalReference(id, patch.external_reference);
604
+ if (patch.offer) quote = await store.quotes.offer(id, patch.offer);
605
+ if (patch.status === "cancelled") quote = await store.quotes.cancel(id, "Cancelled by the operations system");
606
+ return json({ data: serializeQuote(quote, prefix) });
607
+ }
608
+ }
609
+ }
501
610
  if (path === "/inventory" && method === "PUT") {
502
611
  const items = validateInventory(await readJson(request));
503
612
  return json({ data: await store.inventory.set(items) });
@@ -540,6 +649,7 @@ export {
540
649
  serializeConnection,
541
650
  serializeFulfillment,
542
651
  serializeOrder,
543
- serializeProduct
652
+ serializeProduct,
653
+ serializeQuote
544
654
  };
545
655
  //# sourceMappingURL=api.js.map
package/dist/api.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/api/serialize.ts","../src/api/validate.ts","../src/api/index.ts"],"sourcesContent":["import type { CategoryRecord, ProductRecord } from '../store/catalog'\nimport type { Address, Fulfillment, Money, Order } from '../types'\n\nexport interface ApiMoney {\n amount: number\n currency: string\n}\n\nexport interface ApiAddress {\n name: string | null\n line1: string | null\n line2: string | null\n city: string | null\n state: string | null\n postal_code: string | null\n country: string | null\n}\n\nexport interface ApiFulfillment {\n id: string\n external_id: string\n carrier: string | null\n service: string | null\n tracking_number: string | null\n tracking_url: string | null\n shipped_at: string | null\n lines: { sku: string; quantity: number }[]\n created_at: string\n}\n\nexport interface ApiOrder {\n id: string\n number: string\n status: Order['status']\n payment: { status: Order['payment']['status']; provider: string | null; reference: string | null; method_label: string | null; paid_at: string | null }\n currency: string\n subtotal: ApiMoney\n shipping: ApiMoney\n tax: ApiMoney\n discount: ApiMoney\n /** The store discount code the buyer used, upper case. Partner and commission stay in the store. */\n discount_code: string | null\n total: ApiMoney\n customer: { id: string; email: string; name: string | null; phone: string | null }\n shipping_address: ApiAddress\n billing_address: ApiAddress | null\n shipping_method: { code: string; name: string } | null\n lines: { id: string; sku: string; name: string; quantity: number; unit_amount: ApiMoney; total_amount: ApiMoney; fulfilled_quantity: number }[]\n fulfillments: ApiFulfillment[]\n external_reference: string | null\n notes: string | null\n metadata: Record<string, string>\n created_at: string\n updated_at: string\n paid_at: string | null\n cancelled_at: string | null\n}\n\nexport interface ApiProduct {\n sku: string\n name: string\n slug: string\n description_html: string | null\n short_description_html: string | null\n price: ApiMoney\n compare_at: ApiMoney | null\n attributes: Record<string, string>\n active: boolean\n position: number\n legacy_path: string | null\n categories: { id: string; slug: string | null; name: string | null }[]\n images: { url: string; alt: string | null; position: number }[]\n inventory: { available: number; allow_backorder: boolean; updated_at: string } | null\n created_at: string\n updated_at: string\n}\n\nexport interface ApiCategory {\n id: string\n parent_id: string | null\n slug: string\n name: string\n description_html: string | null\n position: number\n product_count: number\n created_at: string\n updated_at: string\n}\n\nconst money = (m: Money): ApiMoney => ({ amount: m.amount, currency: m.currency.toUpperCase() })\n\nconst address = (a: Address | null): ApiAddress | null =>\n a\n ? {\n name: a.name ?? null,\n line1: a.line1 ?? null,\n line2: a.line2 ?? null,\n city: a.city ?? null,\n state: a.state ?? null,\n postal_code: a.postalCode ?? null,\n country: a.country ?? null,\n }\n : null\n\nexport function serializeFulfillment(f: Fulfillment): ApiFulfillment {\n return {\n id: f.id,\n external_id: f.externalId,\n carrier: f.carrier,\n service: f.service,\n tracking_number: f.trackingNumber,\n tracking_url: f.trackingUrl,\n shipped_at: f.shippedAt,\n lines: f.lines.map((l) => ({ sku: l.sku, quantity: l.quantity })),\n created_at: f.createdAt,\n }\n}\n\n/**\n * The Store API's view of an order. `access_key` never leaves the store. `number` is the human number the buyer\n * sees, with the store's prefix (VLD4200), so the consumer shows the same number.\n */\nexport function serializeOrder(o: Order, numberPrefix = ''): ApiOrder {\n return {\n id: o.id,\n number: `${numberPrefix}${o.number}`,\n status: o.status,\n payment: { status: o.payment.status, provider: o.payment.provider, reference: o.payment.reference, method_label: o.payment.methodLabel, paid_at: o.payment.paidAt },\n currency: o.currency.toUpperCase(),\n subtotal: money(o.subtotal),\n shipping: money(o.shipping),\n tax: money(o.tax),\n discount: money(o.discount),\n discount_code: o.discountCode,\n total: money(o.total),\n customer: { id: o.customer.id, email: o.customer.email, name: o.customer.name, phone: o.customer.phone },\n shipping_address: address(o.shippingAddress) ?? { name: null, line1: null, line2: null, city: null, state: null, postal_code: null, country: null },\n billing_address: address(o.billingAddress),\n shipping_method: o.shippingMethod,\n lines: o.lines.map((l) => ({\n id: l.id,\n sku: l.sku,\n name: l.name,\n quantity: l.quantity,\n unit_amount: money(l.unitAmount),\n total_amount: money(l.totalAmount),\n fulfilled_quantity: l.fulfilledQuantity,\n })),\n fulfillments: o.fulfillments.map(serializeFulfillment),\n external_reference: o.externalReference,\n notes: o.notes,\n metadata: o.metadata,\n created_at: o.createdAt,\n updated_at: o.updatedAt,\n paid_at: o.paidAt,\n cancelled_at: o.cancelledAt,\n }\n}\n\n/** The Store API's view of a catalogue product — the stored row, not the shopper projection. */\nexport function serializeProduct(p: ProductRecord): ApiProduct {\n return {\n sku: p.sku,\n name: p.name,\n slug: p.slug,\n description_html: p.descriptionHtml,\n short_description_html: p.shortDescriptionHtml,\n price: money(p.price),\n compare_at: p.compareAtPrice ? money(p.compareAtPrice) : null,\n attributes: p.attributes,\n active: p.active,\n position: p.position,\n legacy_path: p.legacyPath,\n categories: p.categories.map((c) => ({ id: c.id, slug: c.slug, name: c.name })),\n images: p.images.map((i) => ({ url: i.url, alt: i.alt, position: i.position })),\n inventory: p.inventory ? { available: p.inventory.available, allow_backorder: p.inventory.allowBackorder, updated_at: p.inventory.updatedAt } : null,\n created_at: p.createdAt,\n updated_at: p.updatedAt,\n }\n}\n\nexport function serializeCategory(c: CategoryRecord): ApiCategory {\n return {\n id: c.id,\n parent_id: c.parentId,\n slug: c.slug,\n name: c.name,\n description_html: c.descriptionHtml,\n position: c.position,\n product_count: c.productCount,\n created_at: c.createdAt,\n updated_at: c.updatedAt,\n }\n}\n","import type { CategoryInput, ProductInput } from '../store/catalog'\nimport type { InventoryItemInput } from '../store/inventory'\nimport type { FulfillmentInput } from '../store/orders'\nimport type { Money } from '../types'\n\nexport class ValidationError extends Error {\n readonly details: Record<string, string>\n constructor(message: string, details: Record<string, string>) {\n super(message)\n this.name = 'ValidationError'\n this.details = details\n }\n}\n\ntype Details = Record<string, string>\n\nconst isObject = (v: unknown): v is Record<string, unknown> => !!v && typeof v === 'object' && !Array.isArray(v)\nconst isString = (v: unknown): v is string => typeof v === 'string'\nconst isInt = (v: unknown): v is number => typeof v === 'number' && Number.isInteger(v)\n\nfunction str(details: Details, obj: Record<string, unknown>, key: string, path: string, opts: { required?: boolean; max?: number; nullable?: boolean } = {}): string | null | undefined {\n const v = obj[key]\n if (v === undefined) {\n if (opts.required) details[path] = 'is required'\n return undefined\n }\n if (v === null) {\n if (opts.required && !opts.nullable) details[path] = 'is required'\n return opts.nullable || !opts.required ? null : undefined\n }\n if (!isString(v)) {\n details[path] = 'must be a string'\n return undefined\n }\n if (opts.required && !v.trim()) details[path] = 'is required'\n if (opts.max && v.length > opts.max) details[path] = `must be at most ${opts.max} characters`\n return v\n}\n\nfunction moneyField(details: Details, v: unknown, path: string, opts: { required?: boolean; nullable?: boolean } = {}): Money | null | undefined {\n if (v === undefined) {\n if (opts.required) details[path] = 'is required'\n return undefined\n }\n if (v === null) {\n if (!opts.nullable) details[path] = 'is required'\n return null\n }\n if (!isObject(v) || !isInt(v.amount) || !isString(v.currency) || v.currency.length !== 3) {\n details[path] = 'must be { amount: integer minor units, currency: ISO 4217 }'\n return undefined\n }\n if (v.amount < 0) details[path] = 'must not be negative'\n return { amount: v.amount, currency: v.currency }\n}\n\nfunction isoDate(details: Details, v: unknown, path: string): string | null | undefined {\n if (v === undefined) return undefined\n if (v === null) return null\n if (!isString(v) || Number.isNaN(Date.parse(v))) {\n details[path] = 'must be an ISO-8601 timestamp'\n return undefined\n }\n return new Date(v).toISOString()\n}\n\nfunction finish(details: Details, message: string): void {\n if (Object.keys(details).length) throw new ValidationError(message, details)\n}\n\nexport interface ListQuery {\n updatedSince: string | null\n cursor: string | null\n limit: number\n active: boolean | null\n}\n\n/**\n * Query string for the catalogue list endpoints. Out-of-range values are a 422\n * rather than a silent clamp, so a caller paging with a bad limit is told.\n */\nexport function validateListQuery(\n params: URLSearchParams,\n opts: { defaultLimit: number; maxLimit: number; allowUpdatedSince?: boolean; allowActive?: boolean }\n): ListQuery {\n const details: Details = {}\n const limitRaw = params.get('limit')\n const limit = limitRaw ? Number(limitRaw) : opts.defaultLimit\n if (limitRaw && (!Number.isInteger(limit) || limit < 1 || limit > opts.maxLimit)) {\n details.limit = `must be an integer from 1 to ${opts.maxLimit}`\n }\n\n const updatedSinceRaw = opts.allowUpdatedSince ? params.get('updated_since') : null\n if (updatedSinceRaw && Number.isNaN(Date.parse(updatedSinceRaw))) details.updated_since = 'must be an ISO-8601 timestamp'\n\n const activeRaw = opts.allowActive ? params.get('active') : null\n let active: boolean | null = null\n if (activeRaw !== null) {\n if (activeRaw === 'true') active = true\n else if (activeRaw === 'false') active = false\n else details.active = 'must be true or false'\n }\n\n finish(details, 'Invalid query')\n return {\n updatedSince: updatedSinceRaw ? new Date(updatedSinceRaw).toISOString() : null,\n cursor: params.get('cursor'),\n limit: Number.isInteger(limit) ? limit : opts.defaultLimit,\n active,\n }\n}\n\nexport function validateProducts(body: Record<string, unknown>): ProductInput[] {\n const details: Details = {}\n const list = body.products\n if (!Array.isArray(list)) {\n details.products = 'must be an array'\n finish(details, 'Invalid products payload')\n }\n const items = list as unknown[]\n if (items.length > 200) details.products = 'must have at most 200 items'\n const out: ProductInput[] = []\n const seen = new Set<string>()\n items.forEach((raw, i) => {\n const p = `products.${i}`\n if (!isObject(raw)) {\n details[p] = 'must be an object'\n return\n }\n const sku = str(details, raw, 'sku', `${p}.sku`, { required: true, max: 64 })\n const name = str(details, raw, 'name', `${p}.name`, { required: true, max: 255 })\n const slug = str(details, raw, 'slug', `${p}.slug`, { max: 255, nullable: true })\n const description = str(details, raw, 'description_html', `${p}.description_html`, { nullable: true })\n const short = str(details, raw, 'short_description_html', `${p}.short_description_html`, { nullable: true })\n const price = moneyField(details, raw.price, `${p}.price`, { required: true })\n const compare = moneyField(details, raw.compare_at_price, `${p}.compare_at_price`, { nullable: true })\n if (sku && seen.has(sku)) details[`${p}.sku`] = 'appears more than once'\n if (sku) seen.add(sku)\n if (slug && !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(slug)) details[`${p}.slug`] = 'must be lower-case letters, digits and hyphens'\n\n const images: { url: string; alt?: string | null }[] = []\n if (raw.images !== undefined) {\n if (!Array.isArray(raw.images)) details[`${p}.images`] = 'must be an array'\n else\n raw.images.forEach((img, j) => {\n if (!isObject(img) || !isString(img.url) || !img.url.trim()) details[`${p}.images.${j}.url`] = 'is required'\n else images.push({ url: img.url, alt: isString(img.alt) ? img.alt : null })\n })\n }\n const categoryIds: string[] = []\n if (raw.category_ids !== undefined) {\n if (!Array.isArray(raw.category_ids) || !raw.category_ids.every(isString)) details[`${p}.category_ids`] = 'must be an array of strings'\n else categoryIds.push(...(raw.category_ids as string[]))\n }\n let attributes: Record<string, string> = {}\n if (raw.attributes !== undefined) {\n if (!isObject(raw.attributes) || !Object.values(raw.attributes).every(isString)) details[`${p}.attributes`] = 'must be an object of strings'\n else attributes = raw.attributes as Record<string, string>\n }\n if (raw.active !== undefined && typeof raw.active !== 'boolean') details[`${p}.active`] = 'must be a boolean'\n if (raw.position !== undefined && !isInt(raw.position)) details[`${p}.position`] = 'must be an integer'\n\n if (sku && name && price) {\n out.push({\n sku: sku.trim(),\n name: name.trim(),\n slug: slug ?? null,\n description_html: description ?? null,\n short_description_html: short ?? null,\n price,\n compare_at_price: compare ?? null,\n images,\n category_ids: categoryIds,\n attributes,\n active: raw.active === undefined ? true : (raw.active as boolean),\n ...(isInt(raw.position) ? { position: raw.position } : {}),\n })\n }\n })\n finish(details, 'Invalid products payload')\n return out\n}\n\nexport function validateCategories(body: Record<string, unknown>): CategoryInput[] {\n const details: Details = {}\n const list = body.categories\n if (!Array.isArray(list)) {\n details.categories = 'must be an array'\n finish(details, 'Invalid categories payload')\n }\n const items = list as unknown[]\n if (items.length > 500) details.categories = 'must have at most 500 items'\n const out: CategoryInput[] = []\n const seen = new Set<string>()\n items.forEach((raw, i) => {\n const p = `categories.${i}`\n if (!isObject(raw)) {\n details[p] = 'must be an object'\n return\n }\n const id = str(details, raw, 'id', `${p}.id`, { required: true, max: 64 })\n const name = str(details, raw, 'name', `${p}.name`, { required: true, max: 255 })\n const slug = str(details, raw, 'slug', `${p}.slug`, { max: 255, nullable: true })\n const parent = str(details, raw, 'parent_id', `${p}.parent_id`, { max: 64, nullable: true })\n const description = str(details, raw, 'description_html', `${p}.description_html`, { nullable: true })\n if (id && seen.has(id)) details[`${p}.id`] = 'appears more than once'\n if (id) seen.add(id)\n if (id && parent && parent === id) details[`${p}.parent_id`] = 'cannot be the category itself'\n if (slug && !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(slug)) details[`${p}.slug`] = 'must be lower-case letters, digits and hyphens'\n let image: { url: string; alt?: string | null } | null = null\n if (raw.image !== undefined && raw.image !== null) {\n if (!isObject(raw.image) || !isString(raw.image.url)) details[`${p}.image`] = 'must be { url, alt? } or null'\n else image = { url: raw.image.url, alt: isString(raw.image.alt) ? raw.image.alt : null }\n }\n if (raw.position !== undefined && !isInt(raw.position)) details[`${p}.position`] = 'must be an integer'\n if (id && name) {\n out.push({ id: id.trim(), name: name.trim(), slug: slug ?? null, parent_id: parent ?? null, description_html: description ?? null, image, ...(isInt(raw.position) ? { position: raw.position } : {}) })\n }\n })\n finish(details, 'Invalid categories payload')\n return out\n}\n\nexport function validateInventory(body: Record<string, unknown>): InventoryItemInput[] {\n const details: Details = {}\n const list = body.items\n if (!Array.isArray(list)) {\n details.items = 'must be an array'\n finish(details, 'Invalid inventory payload')\n }\n const items = list as unknown[]\n if (items.length > 500) details.items = 'must have at most 500 items'\n const out: InventoryItemInput[] = []\n items.forEach((raw, i) => {\n const p = `items.${i}`\n if (!isObject(raw)) {\n details[p] = 'must be an object'\n return\n }\n const sku = str(details, raw, 'sku', `${p}.sku`, { required: true, max: 64 })\n if (!isInt(raw.available)) details[`${p}.available`] = 'must be an integer'\n else if (raw.available < 0) details[`${p}.available`] = 'must be a non-negative integer'\n if (raw.allow_backorder !== undefined && typeof raw.allow_backorder !== 'boolean') details[`${p}.allow_backorder`] = 'must be a boolean'\n if (sku && isInt(raw.available) && raw.available >= 0) out.push({ sku: sku.trim(), available: raw.available, allow_backorder: raw.allow_backorder === true })\n })\n finish(details, 'Invalid inventory payload')\n return out\n}\n\nexport function validateFulfillment(body: Record<string, unknown>): FulfillmentInput {\n const details: Details = {}\n const externalId = str(details, body, 'external_id', 'external_id', { required: true, max: 64 })\n const carrier = str(details, body, 'carrier', 'carrier', { nullable: true, max: 100 })\n const service = str(details, body, 'service', 'service', { nullable: true, max: 100 })\n const trackingNumber = str(details, body, 'tracking_number', 'tracking_number', { nullable: true, max: 100 })\n const trackingUrl = str(details, body, 'tracking_url', 'tracking_url', { nullable: true, max: 2000 })\n if (trackingUrl && !/^https?:\\/\\//.test(trackingUrl)) details.tracking_url = 'must be an absolute http(s) URL'\n const shippedAt = isoDate(details, body.shipped_at, 'shipped_at')\n if (body.notify_customer !== undefined && typeof body.notify_customer !== 'boolean') details.notify_customer = 'must be a boolean'\n const lines: { sku: string; quantity: number }[] = []\n if (!Array.isArray(body.lines) || body.lines.length === 0) details.lines = 'must be a non-empty array'\n else\n body.lines.forEach((raw, i) => {\n if (!isObject(raw) || !isString(raw.sku) || !raw.sku.trim()) details[`lines.${i}.sku`] = 'is required'\n if (!isObject(raw) || !isInt(raw.quantity) || raw.quantity < 1) details[`lines.${i}.quantity`] = 'must be an integer of at least 1'\n if (isObject(raw) && isString(raw.sku) && isInt(raw.quantity) && raw.quantity >= 1) lines.push({ sku: raw.sku.trim(), quantity: raw.quantity })\n })\n finish(details, 'Invalid fulfillment payload')\n return {\n external_id: externalId!.trim(),\n carrier: carrier ?? null,\n service: service ?? null,\n tracking_number: trackingNumber ?? null,\n tracking_url: trackingUrl ?? null,\n shipped_at: shippedAt ?? null,\n lines,\n notify_customer: body.notify_customer === undefined ? true : (body.notify_customer as boolean),\n }\n}\n\nexport interface OrderPatch {\n external_reference?: string | null\n status?: 'cancelled'\n payment?: { status: 'paid' | 'refunded' | 'failed'; provider?: string | null; reference?: string | null; paid_at?: string | null }\n}\n\nexport function validateOrderPatch(body: Record<string, unknown>): OrderPatch {\n const details: Details = {}\n const out: OrderPatch = {}\n if ('external_reference' in body) {\n const ref = str(details, body, 'external_reference', 'external_reference', { nullable: true, max: 64 })\n if (ref !== undefined) out.external_reference = ref\n }\n if (body.status !== undefined) {\n if (body.status !== 'cancelled') details.status = 'can only be set to \"cancelled\"'\n else out.status = 'cancelled'\n }\n if (body.payment !== undefined) {\n if (!isObject(body.payment)) details.payment = 'must be an object'\n else {\n const p = body.payment\n if (p.status !== 'paid' && p.status !== 'refunded' && p.status !== 'failed') details['payment.status'] = 'must be paid, refunded or failed'\n const provider = str(details, p, 'provider', 'payment.provider', { nullable: true, max: 64 })\n const reference = str(details, p, 'reference', 'payment.reference', { nullable: true, max: 255 })\n const paidAt = isoDate(details, p.paid_at, 'payment.paid_at')\n if (p.status === 'paid' || p.status === 'refunded' || p.status === 'failed') {\n out.payment = { status: p.status, provider: provider ?? null, reference: reference ?? null, paid_at: paidAt ?? null }\n }\n }\n }\n if (!('external_reference' in body) && body.status === undefined && body.payment === undefined) details.body = 'must include external_reference, status or payment'\n finish(details, 'Invalid order patch')\n return out\n}\n","import type { Connection, Store } from '../store'\nimport { API_KEY_PREFIX, type ConnectScope } from '../store/connections'\nimport { secretsEqual } from '../store/crypto'\nimport { StoreError } from '../store/errors'\nimport { serializeCategory, serializeFulfillment, serializeOrder, serializeProduct } from './serialize'\nimport {\n validateCategories, validateFulfillment, validateInventory, validateListQuery, validateOrderPatch, validateProducts, ValidationError,\n} from './validate'\n\nexport interface StoreApiOptions {\n store: Store\n /**\n * The STORE_API_KEY secret — the break-glass key that works without an owner\n * or a connection. Optional: a store whose consumers all connect through the\n * owner-approved flow can leave it unset.\n */\n apiKey: string | undefined\n /** Where the API is mounted in the request URL. */\n basePath?: string\n}\n\n/** Who a request authenticated as: the environment key, or an approved connection. */\nexport type ApiCaller = { kind: 'env' } | { kind: 'connection'; connection: Connection }\n\n/**\n * Where the Store API is served. `/api/store/v1` is the neutral path for any\n * operations system; `/api/skuio/v1` is the original path SKU.io's Storefront\n * integration calls, kept for good. Same handler, same keys, same responses.\n */\nexport const STORE_API_BASES = ['/api/store/v1', '/api/skuio/v1'] as const\n/** @deprecated The SKU.io path; use STORE_API_BASES. */\nexport const STORE_API_BASE = '/api/skuio/v1'\n\nconst json = (body: unknown, status = 200, headers: Record<string, string> = {}) =>\n new Response(JSON.stringify(body), { status, headers: { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store', ...headers } })\n\nconst error = (status: number, code: string, message: string, details: Record<string, string> | null = null) =>\n json({ error: { code, message, details } }, status)\n\n/**\n * The Store API (`docs/store-api.md`) as one fetch-style handler. Mount it on\n * a catch-all route under `/api/store/v1` (and `/api/skuio/v1`, the path SKU.io\n * calls) and hand it every method.\n */\nexport async function handleStoreApi(request: Request, options: StoreApiOptions): Promise<Response> {\n const url = new URL(request.url)\n const bases = options.basePath ? [options.basePath] : [...STORE_API_BASES]\n const base = bases.map((b) => b.replace(/\\/+$/, '')).find((b) => url.pathname.includes(b))\n if (!base) return error(404, 'not_found', 'No such endpoint')\n const at = url.pathname.indexOf(base)\n const path = url.pathname.slice(at + base.length).replace(/\\/+$/, '') || '/'\n const method = request.method.toUpperCase()\n\n try {\n // The token exchange is the one unauthenticated endpoint: the code is the credential.\n if (path === '/connect/token' && method === 'POST') {\n const issued = await options.store.connections.exchange(await readJson(request), clientAddress(request))\n return json({ data: { api_key: issued.apiKey, connection: serializeConnection(issued.connection) } }, 201)\n }\n\n const caller = await authorized(request, options.apiKey, options.store)\n if (!caller) return error(401, 'unauthorized', 'A valid bearer key is required: a connection key issued through /admin/connect, or the STORE_API_KEY secret')\n\n // A connection may use only what the owner approved; the STORE_API_KEY secret may use everything.\n const scope = scopeFor(path)\n if (scope && caller.kind === 'connection' && !caller.connection.scopes.includes(scope)) {\n return error(403, 'forbidden', `This connection was not approved for \"${scope}\"; reconnect and approve it at /admin/connect`)\n }\n\n return await route(method, path, url, request, options.store, caller)\n } catch (e) {\n if (e instanceof ValidationError) return error(422, 'validation_failed', e.message, e.details)\n if (e instanceof StoreError) return error(e.status, e.code, e.message, e.details)\n console.error('store api: unhandled error', e)\n return error(500, 'internal_error', 'The store could not complete the request')\n }\n}\n\nasync function authorized(request: Request, apiKey: string | undefined, store: Store): Promise<ApiCaller | null> {\n const header = request.headers.get('authorization') ?? ''\n const match = /^Bearer\\s+(.+)$/i.exec(header.trim())\n if (!match) return null\n const bearer = match[1]!.trim()\n if (bearer.startsWith(API_KEY_PREFIX)) {\n const connection = await store.connections.resolveKey(bearer)\n return connection ? { kind: 'connection', connection } : null\n }\n if (apiKey && (await secretsEqual(bearer, apiKey))) return { kind: 'env' }\n return null\n}\n\n/** The caller's address for failure counting; Cloudflare sets the first header, proxies the second. */\nfunction clientAddress(request: Request): string {\n return request.headers.get('cf-connecting-ip') ?? request.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ?? 'unknown'\n}\n\nexport function serializeConnection(c: Connection) {\n return {\n id: c.id,\n name: c.name,\n tenant: c.tenant,\n scopes: c.scopes,\n key_prefix: c.keyPrefix,\n created_at: c.createdAt,\n issued_at: c.issuedAt,\n last_used_at: c.lastUsedAt,\n revoked_at: c.revokedAt,\n }\n}\n\nasync function readJson(request: Request): Promise<Record<string, unknown>> {\n const text = await request.text()\n if (!text.trim()) throw new ValidationError('Request body must be JSON', { body: 'is empty' })\n let parsed: unknown\n try {\n parsed = JSON.parse(text)\n } catch {\n throw new ValidationError('Request body must be JSON', { body: 'is not valid JSON' })\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) throw new ValidationError('Request body must be a JSON object', { body: 'is not an object' })\n return parsed as Record<string, unknown>\n}\n\n/**\n * The scope an endpoint needs. There is deliberately no endpoint that creates\n * an order: orders come only from the store's own checkout, priced from its\n * database, so a leaked key cannot invent sales.\n */\nfunction scopeFor(path: string): ConnectScope | null {\n const first = path.split('/').filter(Boolean)[0]\n if (first === 'orders') return /^\\/orders\\/[^/]+\\/fulfillments$/.test(path) ? 'fulfillments' : 'orders'\n if (first === 'inventory') return 'inventory'\n if (first === 'products' || first === 'categories') return 'catalog'\n return null // /health, /connect\n}\n\nasync function route(method: string, path: string, url: URL, request: Request, store: Store, caller: ApiCaller): Promise<Response> {\n const segments = path.split('/').filter(Boolean)\n\n if (path === '/health' && method === 'GET') {\n const health = await store.health()\n const connection = caller.kind === 'connection' ? { id: caller.connection.id, name: caller.connection.name, tenant: caller.connection.tenant } : null\n return json({ data: { ...health, connection } })\n }\n\n if (path === '/connect') {\n if (method === 'GET') return json({ data: caller.kind === 'connection' ? serializeConnection(caller.connection) : null })\n if (method === 'DELETE') {\n if (caller.kind !== 'connection') return error(409, 'conflict', 'The STORE_API_KEY secret is not a connection; rotate it on the Worker instead')\n await store.connections.revoke(caller.connection.id)\n return new Response(null, { status: 204 })\n }\n }\n\n if (segments[0] === 'orders') {\n if (segments.length === 1 && method === 'GET') {\n const updatedSince = url.searchParams.get('updated_since')\n const cursor = url.searchParams.get('cursor')\n const limitRaw = url.searchParams.get('limit')\n const limit = limitRaw ? Number(limitRaw) : 50\n if (limitRaw && (!Number.isInteger(limit) || limit < 1 || limit > 200)) throw new ValidationError('Invalid query', { limit: 'must be an integer from 1 to 200' })\n if (updatedSince && Number.isNaN(Date.parse(updatedSince))) throw new ValidationError('Invalid query', { updated_since: 'must be an ISO-8601 timestamp' })\n // Lazy housekeeping: abandoned processor checkouts become cancelled before the consumer sees the list.\n await store.orders.expireAbandoned()\n const page = await store.orders.changedSince(updatedSince ? new Date(updatedSince).toISOString() : null, cursor, limit)\n return json({ data: page.orders.map((o) => serializeOrder(o, store.config.orders.numberPrefix)), next_cursor: page.nextCursor })\n }\n const id = segments[1] ? decodeURIComponent(segments[1]) : ''\n if (segments.length === 2 && id) {\n if (method === 'GET') return json({ data: serializeOrder(await store.orders.require(id), store.config.orders.numberPrefix) })\n if (method === 'PATCH') {\n const patch = validateOrderPatch(await readJson(request))\n // Recording a payment is its own scope: a key that may read and stamp orders cannot\n // turn an unpaid order into a paid one unless the owner approved that too.\n if (patch.payment && caller.kind === 'connection' && !caller.connection.scopes.includes('payments')) {\n return error(403, 'forbidden', 'This connection was not approved for \"payments\"; reconnect and approve it at /admin/connect')\n }\n let order = await store.orders.require(id)\n if (patch.external_reference !== undefined) order = await store.orders.setExternalReference(id, patch.external_reference)\n if (patch.payment) order = await store.orders.setPaymentStatus(id, patch.payment)\n if (patch.status === 'cancelled') order = await store.orders.cancel(id, 'Cancelled by the operations system')\n return json({ data: serializeOrder(order, store.config.orders.numberPrefix) })\n }\n }\n if (segments.length === 3 && id && segments[2] === 'fulfillments' && method === 'POST') {\n const input = validateFulfillment(await readJson(request))\n const result = await store.orders.addFulfillment(id, input)\n return json({ data: serializeFulfillment(result.fulfillment) }, result.created ? 201 : 200)\n }\n }\n\n if (path === '/inventory' && method === 'PUT') {\n const items = validateInventory(await readJson(request))\n return json({ data: await store.inventory.set(items) })\n }\n\n if (path === '/products' && method === 'PUT') {\n const products = validateProducts(await readJson(request))\n return json({ data: { upserted: await store.catalog.upsertProducts(products) } })\n }\n\n if (path === '/products' && method === 'GET') {\n const query = validateListQuery(url.searchParams, { defaultLimit: 50, maxLimit: 200, allowUpdatedSince: true, allowActive: true })\n const page = await store.catalog.listProducts(query)\n return json({ data: page.products.map(serializeProduct), next_cursor: page.nextCursor })\n }\n\n if (segments[0] === 'products' && segments.length === 2) {\n const sku = decodeURIComponent(segments[1]!)\n if (method === 'GET') return json({ data: serializeProduct(await store.catalog.requireProduct(sku)) })\n if (method === 'DELETE') {\n const done = await store.catalog.deactivateProduct(sku)\n if (!done) return error(404, 'not_found', `Product ${sku} not found`)\n return new Response(null, { status: 204 })\n }\n }\n\n if (path === '/categories' && method === 'PUT') {\n const categories = validateCategories(await readJson(request))\n return json({ data: { upserted: await store.catalog.upsertCategories(categories) } })\n }\n\n if (path === '/categories' && method === 'GET') {\n const query = validateListQuery(url.searchParams, { defaultLimit: 500, maxLimit: 1000 })\n const page = await store.catalog.listCategories(query)\n return json({ data: page.categories.map(serializeCategory), next_cursor: page.nextCursor })\n }\n\n return error(404, 'not_found', `No such endpoint: ${method} ${path}`)\n}\n\nexport {\n serializeOrder, serializeFulfillment, serializeProduct, serializeCategory,\n type ApiOrder, type ApiFulfillment, type ApiProduct, type ApiCategory,\n} from './serialize'\nexport { ValidationError } from './validate'\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/api/serialize.ts","../src/api/validate.ts","../src/api/index.ts"],"sourcesContent":["import type { CategoryRecord, ProductRecord } from '../store/catalog'\nimport type { Address, Fulfillment, Money, Order, QuoteStatus, StoreQuote } from '../types'\n\nexport interface ApiMoney {\n amount: number\n currency: string\n}\n\nexport interface ApiAddress {\n name: string | null\n line1: string | null\n line2: string | null\n city: string | null\n state: string | null\n postal_code: string | null\n country: string | null\n}\n\nexport interface ApiFulfillment {\n id: string\n external_id: string\n carrier: string | null\n service: string | null\n tracking_number: string | null\n tracking_url: string | null\n shipped_at: string | null\n lines: { sku: string; quantity: number }[]\n created_at: string\n}\n\nexport interface ApiOrder {\n id: string\n number: string\n status: Order['status']\n payment: { status: Order['payment']['status']; provider: string | null; reference: string | null; method_label: string | null; paid_at: string | null }\n currency: string\n subtotal: ApiMoney\n shipping: ApiMoney\n tax: ApiMoney\n discount: ApiMoney\n /** The store discount code the buyer used, upper case. Partner and commission stay in the store. */\n discount_code: string | null\n total: ApiMoney\n customer: { id: string; email: string; name: string | null; phone: string | null }\n shipping_address: ApiAddress\n billing_address: ApiAddress | null\n shipping_method: { code: string; name: string } | null\n lines: { id: string; sku: string; name: string; quantity: number; unit_amount: ApiMoney; total_amount: ApiMoney; fulfilled_quantity: number }[]\n fulfillments: ApiFulfillment[]\n external_reference: string | null\n notes: string | null\n metadata: Record<string, string>\n created_at: string\n updated_at: string\n paid_at: string | null\n cancelled_at: string | null\n}\n\nexport interface ApiQuote {\n id: string\n /** With the store's quote prefix: \"Q1001\". */\n number: string\n status: QuoteStatus\n currency: string\n customer: { id: string; email: string; name: string | null; phone: string | null }\n company: string | null\n message: string | null\n shipping_address: ApiAddress | null\n lines: { id: string; sku: string; name: string; quantity: number; list_amount: ApiMoney | null; unit_amount: ApiMoney | null; total_amount: ApiMoney | null }[]\n subtotal: ApiMoney | null\n shipping: ApiMoney | null\n tax: ApiMoney | null\n total: ApiMoney | null\n note: string | null\n expires_at: string | null\n external_reference: string | null\n /** The order the buyer's acceptance placed; it also arrives through GET /orders with `metadata.quote_id`. */\n order_id: string | null\n decline_reason: string | null\n metadata: Record<string, string>\n created_at: string\n updated_at: string\n quoted_at: string | null\n accepted_at: string | null\n declined_at: string | null\n cancelled_at: string | null\n}\n\nexport interface ApiProduct {\n sku: string\n name: string\n slug: string\n description_html: string | null\n short_description_html: string | null\n price: ApiMoney\n compare_at: ApiMoney | null\n attributes: Record<string, string>\n active: boolean\n position: number\n legacy_path: string | null\n categories: { id: string; slug: string | null; name: string | null }[]\n images: { url: string; alt: string | null; position: number }[]\n inventory: { available: number; allow_backorder: boolean; updated_at: string } | null\n created_at: string\n updated_at: string\n}\n\nexport interface ApiCategory {\n id: string\n parent_id: string | null\n slug: string\n name: string\n description_html: string | null\n position: number\n product_count: number\n created_at: string\n updated_at: string\n}\n\nconst money = (m: Money): ApiMoney => ({ amount: m.amount, currency: m.currency.toUpperCase() })\n\nconst address = (a: Address | null): ApiAddress | null =>\n a\n ? {\n name: a.name ?? null,\n line1: a.line1 ?? null,\n line2: a.line2 ?? null,\n city: a.city ?? null,\n state: a.state ?? null,\n postal_code: a.postalCode ?? null,\n country: a.country ?? null,\n }\n : null\n\nexport function serializeFulfillment(f: Fulfillment): ApiFulfillment {\n return {\n id: f.id,\n external_id: f.externalId,\n carrier: f.carrier,\n service: f.service,\n tracking_number: f.trackingNumber,\n tracking_url: f.trackingUrl,\n shipped_at: f.shippedAt,\n lines: f.lines.map((l) => ({ sku: l.sku, quantity: l.quantity })),\n created_at: f.createdAt,\n }\n}\n\n/**\n * The Store API's view of an order. `access_key` never leaves the store. `number` is the human number the buyer\n * sees, with the store's prefix (VLD4200), so the consumer shows the same number.\n */\nexport function serializeOrder(o: Order, numberPrefix = ''): ApiOrder {\n return {\n id: o.id,\n number: `${numberPrefix}${o.number}`,\n status: o.status,\n payment: { status: o.payment.status, provider: o.payment.provider, reference: o.payment.reference, method_label: o.payment.methodLabel, paid_at: o.payment.paidAt },\n currency: o.currency.toUpperCase(),\n subtotal: money(o.subtotal),\n shipping: money(o.shipping),\n tax: money(o.tax),\n discount: money(o.discount),\n discount_code: o.discountCode,\n total: money(o.total),\n customer: { id: o.customer.id, email: o.customer.email, name: o.customer.name, phone: o.customer.phone },\n shipping_address: address(o.shippingAddress) ?? { name: null, line1: null, line2: null, city: null, state: null, postal_code: null, country: null },\n billing_address: address(o.billingAddress),\n shipping_method: o.shippingMethod,\n lines: o.lines.map((l) => ({\n id: l.id,\n sku: l.sku,\n name: l.name,\n quantity: l.quantity,\n unit_amount: money(l.unitAmount),\n total_amount: money(l.totalAmount),\n fulfilled_quantity: l.fulfilledQuantity,\n })),\n fulfillments: o.fulfillments.map(serializeFulfillment),\n external_reference: o.externalReference,\n notes: o.notes,\n metadata: o.metadata,\n created_at: o.createdAt,\n updated_at: o.updatedAt,\n paid_at: o.paidAt,\n cancelled_at: o.cancelledAt,\n }\n}\n\nconst maybeMoney = (m: Money | null): ApiMoney | null => (m ? money(m) : null)\n\n/** The Store API's view of a quote. `access_key` never leaves the store. */\nexport function serializeQuote(q: StoreQuote, numberPrefix = ''): ApiQuote {\n return {\n id: q.id,\n number: `${numberPrefix}${q.number}`,\n status: q.status,\n currency: q.currency.toUpperCase(),\n customer: { id: q.customer.id, email: q.customer.email, name: q.customer.name, phone: q.customer.phone },\n company: q.company,\n message: q.message,\n shipping_address: address(q.shippingAddress),\n lines: q.lines.map((l) => ({\n id: l.id,\n sku: l.sku,\n name: l.name,\n quantity: l.quantity,\n list_amount: maybeMoney(l.listAmount),\n unit_amount: maybeMoney(l.unitAmount),\n total_amount: maybeMoney(l.totalAmount),\n })),\n subtotal: maybeMoney(q.subtotal),\n shipping: maybeMoney(q.shipping),\n tax: maybeMoney(q.tax),\n total: maybeMoney(q.total),\n note: q.offerNote,\n expires_at: q.expiresAt,\n external_reference: q.externalReference,\n order_id: q.orderId,\n decline_reason: q.declineReason,\n metadata: q.metadata,\n created_at: q.createdAt,\n updated_at: q.updatedAt,\n quoted_at: q.quotedAt,\n accepted_at: q.acceptedAt,\n declined_at: q.declinedAt,\n cancelled_at: q.cancelledAt,\n }\n}\n\n/** The Store API's view of a catalog product — the stored row, not the shopper projection. */\nexport function serializeProduct(p: ProductRecord): ApiProduct {\n return {\n sku: p.sku,\n name: p.name,\n slug: p.slug,\n description_html: p.descriptionHtml,\n short_description_html: p.shortDescriptionHtml,\n price: money(p.price),\n compare_at: p.compareAtPrice ? money(p.compareAtPrice) : null,\n attributes: p.attributes,\n active: p.active,\n position: p.position,\n legacy_path: p.legacyPath,\n categories: p.categories.map((c) => ({ id: c.id, slug: c.slug, name: c.name })),\n images: p.images.map((i) => ({ url: i.url, alt: i.alt, position: i.position })),\n inventory: p.inventory ? { available: p.inventory.available, allow_backorder: p.inventory.allowBackorder, updated_at: p.inventory.updatedAt } : null,\n created_at: p.createdAt,\n updated_at: p.updatedAt,\n }\n}\n\nexport function serializeCategory(c: CategoryRecord): ApiCategory {\n return {\n id: c.id,\n parent_id: c.parentId,\n slug: c.slug,\n name: c.name,\n description_html: c.descriptionHtml,\n position: c.position,\n product_count: c.productCount,\n created_at: c.createdAt,\n updated_at: c.updatedAt,\n }\n}\n","import type { CategoryInput, ProductInput } from '../store/catalog'\nimport type { InventoryItemInput } from '../store/inventory'\nimport type { FulfillmentInput } from '../store/orders'\nimport type { QuoteOfferInput } from '../store/quotes'\nimport type { Money } from '../types'\n\nexport class ValidationError extends Error {\n readonly details: Record<string, string>\n constructor(message: string, details: Record<string, string>) {\n super(message)\n this.name = 'ValidationError'\n this.details = details\n }\n}\n\ntype Details = Record<string, string>\n\nconst isObject = (v: unknown): v is Record<string, unknown> => !!v && typeof v === 'object' && !Array.isArray(v)\nconst isString = (v: unknown): v is string => typeof v === 'string'\nconst isInt = (v: unknown): v is number => typeof v === 'number' && Number.isInteger(v)\n\nfunction str(details: Details, obj: Record<string, unknown>, key: string, path: string, opts: { required?: boolean; max?: number; nullable?: boolean } = {}): string | null | undefined {\n const v = obj[key]\n if (v === undefined) {\n if (opts.required) details[path] = 'is required'\n return undefined\n }\n if (v === null) {\n if (opts.required && !opts.nullable) details[path] = 'is required'\n return opts.nullable || !opts.required ? null : undefined\n }\n if (!isString(v)) {\n details[path] = 'must be a string'\n return undefined\n }\n if (opts.required && !v.trim()) details[path] = 'is required'\n if (opts.max && v.length > opts.max) details[path] = `must be at most ${opts.max} characters`\n return v\n}\n\nfunction moneyField(details: Details, v: unknown, path: string, opts: { required?: boolean; nullable?: boolean } = {}): Money | null | undefined {\n if (v === undefined) {\n if (opts.required) details[path] = 'is required'\n return undefined\n }\n if (v === null) {\n if (!opts.nullable) details[path] = 'is required'\n return null\n }\n if (!isObject(v) || !isInt(v.amount) || !isString(v.currency) || v.currency.length !== 3) {\n details[path] = 'must be { amount: integer minor units, currency: ISO 4217 }'\n return undefined\n }\n if (v.amount < 0) details[path] = 'must not be negative'\n return { amount: v.amount, currency: v.currency }\n}\n\nfunction isoDate(details: Details, v: unknown, path: string): string | null | undefined {\n if (v === undefined) return undefined\n if (v === null) return null\n if (!isString(v) || Number.isNaN(Date.parse(v))) {\n details[path] = 'must be an ISO-8601 timestamp'\n return undefined\n }\n return new Date(v).toISOString()\n}\n\nfunction finish(details: Details, message: string): void {\n if (Object.keys(details).length) throw new ValidationError(message, details)\n}\n\nexport interface ListQuery {\n updatedSince: string | null\n cursor: string | null\n limit: number\n active: boolean | null\n}\n\n/**\n * Query string for the catalog list endpoints. Out-of-range values are a 422\n * rather than a silent clamp, so a caller paging with a bad limit is told.\n */\nexport function validateListQuery(\n params: URLSearchParams,\n opts: { defaultLimit: number; maxLimit: number; allowUpdatedSince?: boolean; allowActive?: boolean }\n): ListQuery {\n const details: Details = {}\n const limitRaw = params.get('limit')\n const limit = limitRaw ? Number(limitRaw) : opts.defaultLimit\n if (limitRaw && (!Number.isInteger(limit) || limit < 1 || limit > opts.maxLimit)) {\n details.limit = `must be an integer from 1 to ${opts.maxLimit}`\n }\n\n const updatedSinceRaw = opts.allowUpdatedSince ? params.get('updated_since') : null\n if (updatedSinceRaw && Number.isNaN(Date.parse(updatedSinceRaw))) details.updated_since = 'must be an ISO-8601 timestamp'\n\n const activeRaw = opts.allowActive ? params.get('active') : null\n let active: boolean | null = null\n if (activeRaw !== null) {\n if (activeRaw === 'true') active = true\n else if (activeRaw === 'false') active = false\n else details.active = 'must be true or false'\n }\n\n finish(details, 'Invalid query')\n return {\n updatedSince: updatedSinceRaw ? new Date(updatedSinceRaw).toISOString() : null,\n cursor: params.get('cursor'),\n limit: Number.isInteger(limit) ? limit : opts.defaultLimit,\n active,\n }\n}\n\nexport function validateProducts(body: Record<string, unknown>): ProductInput[] {\n const details: Details = {}\n const list = body.products\n if (!Array.isArray(list)) {\n details.products = 'must be an array'\n finish(details, 'Invalid products payload')\n }\n const items = list as unknown[]\n if (items.length > 200) details.products = 'must have at most 200 items'\n const out: ProductInput[] = []\n const seen = new Set<string>()\n items.forEach((raw, i) => {\n const p = `products.${i}`\n if (!isObject(raw)) {\n details[p] = 'must be an object'\n return\n }\n const sku = str(details, raw, 'sku', `${p}.sku`, { required: true, max: 64 })\n const name = str(details, raw, 'name', `${p}.name`, { required: true, max: 255 })\n const slug = str(details, raw, 'slug', `${p}.slug`, { max: 255, nullable: true })\n const description = str(details, raw, 'description_html', `${p}.description_html`, { nullable: true })\n const short = str(details, raw, 'short_description_html', `${p}.short_description_html`, { nullable: true })\n const price = moneyField(details, raw.price, `${p}.price`, { required: true })\n const compare = moneyField(details, raw.compare_at_price, `${p}.compare_at_price`, { nullable: true })\n if (sku && seen.has(sku)) details[`${p}.sku`] = 'appears more than once'\n if (sku) seen.add(sku)\n if (slug && !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(slug)) details[`${p}.slug`] = 'must be lower-case letters, digits and hyphens'\n\n const images: { url: string; alt?: string | null }[] = []\n if (raw.images !== undefined) {\n if (!Array.isArray(raw.images)) details[`${p}.images`] = 'must be an array'\n else\n raw.images.forEach((img, j) => {\n if (!isObject(img) || !isString(img.url) || !img.url.trim()) details[`${p}.images.${j}.url`] = 'is required'\n else images.push({ url: img.url, alt: isString(img.alt) ? img.alt : null })\n })\n }\n const categoryIds: string[] = []\n if (raw.category_ids !== undefined) {\n if (!Array.isArray(raw.category_ids) || !raw.category_ids.every(isString)) details[`${p}.category_ids`] = 'must be an array of strings'\n else categoryIds.push(...(raw.category_ids as string[]))\n }\n let attributes: Record<string, string> = {}\n if (raw.attributes !== undefined) {\n if (!isObject(raw.attributes) || !Object.values(raw.attributes).every(isString)) details[`${p}.attributes`] = 'must be an object of strings'\n else attributes = raw.attributes as Record<string, string>\n }\n if (raw.active !== undefined && typeof raw.active !== 'boolean') details[`${p}.active`] = 'must be a boolean'\n if (raw.position !== undefined && !isInt(raw.position)) details[`${p}.position`] = 'must be an integer'\n\n if (sku && name && price) {\n out.push({\n sku: sku.trim(),\n name: name.trim(),\n slug: slug ?? null,\n description_html: description ?? null,\n short_description_html: short ?? null,\n price,\n compare_at_price: compare ?? null,\n images,\n category_ids: categoryIds,\n attributes,\n active: raw.active === undefined ? true : (raw.active as boolean),\n ...(isInt(raw.position) ? { position: raw.position } : {}),\n })\n }\n })\n finish(details, 'Invalid products payload')\n return out\n}\n\nexport function validateCategories(body: Record<string, unknown>): CategoryInput[] {\n const details: Details = {}\n const list = body.categories\n if (!Array.isArray(list)) {\n details.categories = 'must be an array'\n finish(details, 'Invalid categories payload')\n }\n const items = list as unknown[]\n if (items.length > 500) details.categories = 'must have at most 500 items'\n const out: CategoryInput[] = []\n const seen = new Set<string>()\n items.forEach((raw, i) => {\n const p = `categories.${i}`\n if (!isObject(raw)) {\n details[p] = 'must be an object'\n return\n }\n const id = str(details, raw, 'id', `${p}.id`, { required: true, max: 64 })\n const name = str(details, raw, 'name', `${p}.name`, { required: true, max: 255 })\n const slug = str(details, raw, 'slug', `${p}.slug`, { max: 255, nullable: true })\n const parent = str(details, raw, 'parent_id', `${p}.parent_id`, { max: 64, nullable: true })\n const description = str(details, raw, 'description_html', `${p}.description_html`, { nullable: true })\n if (id && seen.has(id)) details[`${p}.id`] = 'appears more than once'\n if (id) seen.add(id)\n if (id && parent && parent === id) details[`${p}.parent_id`] = 'cannot be the category itself'\n if (slug && !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(slug)) details[`${p}.slug`] = 'must be lower-case letters, digits and hyphens'\n let image: { url: string; alt?: string | null } | null = null\n if (raw.image !== undefined && raw.image !== null) {\n if (!isObject(raw.image) || !isString(raw.image.url)) details[`${p}.image`] = 'must be { url, alt? } or null'\n else image = { url: raw.image.url, alt: isString(raw.image.alt) ? raw.image.alt : null }\n }\n if (raw.position !== undefined && !isInt(raw.position)) details[`${p}.position`] = 'must be an integer'\n if (id && name) {\n out.push({ id: id.trim(), name: name.trim(), slug: slug ?? null, parent_id: parent ?? null, description_html: description ?? null, image, ...(isInt(raw.position) ? { position: raw.position } : {}) })\n }\n })\n finish(details, 'Invalid categories payload')\n return out\n}\n\nexport function validateInventory(body: Record<string, unknown>): InventoryItemInput[] {\n const details: Details = {}\n const list = body.items\n if (!Array.isArray(list)) {\n details.items = 'must be an array'\n finish(details, 'Invalid inventory payload')\n }\n const items = list as unknown[]\n if (items.length > 500) details.items = 'must have at most 500 items'\n const out: InventoryItemInput[] = []\n items.forEach((raw, i) => {\n const p = `items.${i}`\n if (!isObject(raw)) {\n details[p] = 'must be an object'\n return\n }\n const sku = str(details, raw, 'sku', `${p}.sku`, { required: true, max: 64 })\n if (!isInt(raw.available)) details[`${p}.available`] = 'must be an integer'\n else if (raw.available < 0) details[`${p}.available`] = 'must be a non-negative integer'\n if (raw.allow_backorder !== undefined && typeof raw.allow_backorder !== 'boolean') details[`${p}.allow_backorder`] = 'must be a boolean'\n if (sku && isInt(raw.available) && raw.available >= 0) out.push({ sku: sku.trim(), available: raw.available, allow_backorder: raw.allow_backorder === true })\n })\n finish(details, 'Invalid inventory payload')\n return out\n}\n\nexport function validateFulfillment(body: Record<string, unknown>): FulfillmentInput {\n const details: Details = {}\n const externalId = str(details, body, 'external_id', 'external_id', { required: true, max: 64 })\n const carrier = str(details, body, 'carrier', 'carrier', { nullable: true, max: 100 })\n const service = str(details, body, 'service', 'service', { nullable: true, max: 100 })\n const trackingNumber = str(details, body, 'tracking_number', 'tracking_number', { nullable: true, max: 100 })\n const trackingUrl = str(details, body, 'tracking_url', 'tracking_url', { nullable: true, max: 2000 })\n if (trackingUrl && !/^https?:\\/\\//.test(trackingUrl)) details.tracking_url = 'must be an absolute http(s) URL'\n const shippedAt = isoDate(details, body.shipped_at, 'shipped_at')\n if (body.notify_customer !== undefined && typeof body.notify_customer !== 'boolean') details.notify_customer = 'must be a boolean'\n const lines: { sku: string; quantity: number }[] = []\n if (!Array.isArray(body.lines) || body.lines.length === 0) details.lines = 'must be a non-empty array'\n else\n body.lines.forEach((raw, i) => {\n if (!isObject(raw) || !isString(raw.sku) || !raw.sku.trim()) details[`lines.${i}.sku`] = 'is required'\n if (!isObject(raw) || !isInt(raw.quantity) || raw.quantity < 1) details[`lines.${i}.quantity`] = 'must be an integer of at least 1'\n if (isObject(raw) && isString(raw.sku) && isInt(raw.quantity) && raw.quantity >= 1) lines.push({ sku: raw.sku.trim(), quantity: raw.quantity })\n })\n finish(details, 'Invalid fulfillment payload')\n return {\n external_id: externalId!.trim(),\n carrier: carrier ?? null,\n service: service ?? null,\n tracking_number: trackingNumber ?? null,\n tracking_url: trackingUrl ?? null,\n shipped_at: shippedAt ?? null,\n lines,\n notify_customer: body.notify_customer === undefined ? true : (body.notify_customer as boolean),\n }\n}\n\nexport interface OrderPatch {\n external_reference?: string | null\n status?: 'cancelled'\n payment?: { status: 'paid' | 'refunded' | 'failed'; provider?: string | null; reference?: string | null; paid_at?: string | null }\n}\n\nexport function validateOrderPatch(body: Record<string, unknown>): OrderPatch {\n const details: Details = {}\n const out: OrderPatch = {}\n if ('external_reference' in body) {\n const ref = str(details, body, 'external_reference', 'external_reference', { nullable: true, max: 64 })\n if (ref !== undefined) out.external_reference = ref\n }\n if (body.status !== undefined) {\n if (body.status !== 'cancelled') details.status = 'can only be set to \"cancelled\"'\n else out.status = 'cancelled'\n }\n if (body.payment !== undefined) {\n if (!isObject(body.payment)) details.payment = 'must be an object'\n else {\n const p = body.payment\n if (p.status !== 'paid' && p.status !== 'refunded' && p.status !== 'failed') details['payment.status'] = 'must be paid, refunded or failed'\n const provider = str(details, p, 'provider', 'payment.provider', { nullable: true, max: 64 })\n const reference = str(details, p, 'reference', 'payment.reference', { nullable: true, max: 255 })\n const paidAt = isoDate(details, p.paid_at, 'payment.paid_at')\n if (p.status === 'paid' || p.status === 'refunded' || p.status === 'failed') {\n out.payment = { status: p.status, provider: provider ?? null, reference: reference ?? null, paid_at: paidAt ?? null }\n }\n }\n }\n if (!('external_reference' in body) && body.status === undefined && body.payment === undefined) details.body = 'must include external_reference, status or payment'\n finish(details, 'Invalid order patch')\n return out\n}\n\nexport interface QuotePatch {\n external_reference?: string | null\n status?: 'cancelled'\n offer?: QuoteOfferInput\n}\n\n/** `PATCH /quotes/{id}`: stamp a reference, withdraw the quote, or price it (`offer`). */\nexport function validateQuotePatch(body: Record<string, unknown>): QuotePatch {\n const details: Details = {}\n const out: QuotePatch = {}\n if ('external_reference' in body) {\n const ref = str(details, body, 'external_reference', 'external_reference', { nullable: true, max: 64 })\n if (ref !== undefined) out.external_reference = ref\n }\n if (body.status !== undefined) {\n if (body.status !== 'cancelled') details.status = 'can only be set to \"cancelled\"'\n else out.status = 'cancelled'\n }\n if (body.offer !== undefined) {\n if (!isObject(body.offer)) details.offer = 'must be an object'\n else {\n const o = body.offer\n const lines: QuoteOfferInput['lines'] = []\n if (!Array.isArray(o.lines) || o.lines.length === 0) details['offer.lines'] = 'must be a non-empty array'\n else\n o.lines.forEach((raw, i) => {\n if (!isObject(raw)) {\n details[`offer.lines.${i}`] = 'must be an object'\n return\n }\n const sku = str(details, raw, 'sku', `offer.lines.${i}.sku`, { required: true, max: 100 })\n const name = str(details, raw, 'name', `offer.lines.${i}.name`, { nullable: true, max: 300 })\n if (!isInt(raw.quantity) || raw.quantity < 1) details[`offer.lines.${i}.quantity`] = 'must be an integer of at least 1'\n const unit = moneyField(details, raw.unit_amount, `offer.lines.${i}.unit_amount`, { required: true })\n if (sku && isInt(raw.quantity) && raw.quantity >= 1 && unit) lines.push({ sku: sku.trim(), name: name ?? null, quantity: raw.quantity, unit_amount: unit })\n })\n const shipping = moneyField(details, o.shipping, 'offer.shipping', { nullable: true })\n const tax = moneyField(details, o.tax, 'offer.tax', { nullable: true })\n const expiresAt = isoDate(details, o.expires_at, 'offer.expires_at')\n const note = str(details, o, 'note', 'offer.note', { nullable: true, max: 4000 })\n if (o.notify_customer !== undefined && typeof o.notify_customer !== 'boolean') details['offer.notify_customer'] = 'must be a boolean'\n out.offer = {\n lines,\n shipping: shipping ?? null,\n tax: tax ?? null,\n expires_at: expiresAt ?? null,\n note: note ?? null,\n notify_customer: o.notify_customer === undefined ? true : (o.notify_customer as boolean),\n }\n }\n }\n if (out.status && out.offer) details.status = 'cannot be combined with an offer'\n if (!('external_reference' in body) && body.status === undefined && body.offer === undefined) details.body = 'must include external_reference, status or offer'\n finish(details, 'Invalid quote patch')\n return out\n}\n","import type { Connection, Store } from '../store'\nimport { API_KEY_PREFIX, type ConnectScope } from '../store/connections'\nimport { secretsEqual } from '../store/crypto'\nimport { StoreError } from '../store/errors'\nimport { serializeCategory, serializeFulfillment, serializeOrder, serializeProduct, serializeQuote } from './serialize'\nimport {\n validateCategories, validateFulfillment, validateInventory, validateListQuery, validateOrderPatch, validateProducts, validateQuotePatch, ValidationError,\n} from './validate'\n\nexport interface StoreApiOptions {\n store: Store\n /**\n * The STORE_API_KEY secret — the break-glass key that works without an owner\n * or a connection. Optional: a store whose consumers all connect through the\n * owner-approved flow can leave it unset.\n */\n apiKey: string | undefined\n /** Where the API is mounted in the request URL. */\n basePath?: string\n}\n\n/** Who a request authenticated as: the environment key, or an approved connection. */\nexport type ApiCaller = { kind: 'env' } | { kind: 'connection'; connection: Connection }\n\n/**\n * Where the Store API is served. `/api/store/v1` is the neutral path for any\n * operations system; `/api/skuio/v1` is the original path SKU.io's Storefront\n * integration calls, kept for good. Same handler, same keys, same responses.\n */\nexport const STORE_API_BASES = ['/api/store/v1', '/api/skuio/v1'] as const\n/** @deprecated The SKU.io path; use STORE_API_BASES. */\nexport const STORE_API_BASE = '/api/skuio/v1'\n\nconst json = (body: unknown, status = 200, headers: Record<string, string> = {}) =>\n new Response(JSON.stringify(body), { status, headers: { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store', ...headers } })\n\nconst error = (status: number, code: string, message: string, details: Record<string, string> | null = null) =>\n json({ error: { code, message, details } }, status)\n\n/**\n * The Store API (`docs/store-api.md`) as one fetch-style handler. Mount it on\n * a catch-all route under `/api/store/v1` (and `/api/skuio/v1`, the path SKU.io\n * calls) and hand it every method.\n */\nexport async function handleStoreApi(request: Request, options: StoreApiOptions): Promise<Response> {\n const url = new URL(request.url)\n const bases = options.basePath ? [options.basePath] : [...STORE_API_BASES]\n const base = bases.map((b) => b.replace(/\\/+$/, '')).find((b) => url.pathname.includes(b))\n if (!base) return error(404, 'not_found', 'No such endpoint')\n const at = url.pathname.indexOf(base)\n const path = url.pathname.slice(at + base.length).replace(/\\/+$/, '') || '/'\n const method = request.method.toUpperCase()\n\n try {\n // The token exchange is the one unauthenticated endpoint: the code is the credential.\n if (path === '/connect/token' && method === 'POST') {\n const issued = await options.store.connections.exchange(await readJson(request), clientAddress(request))\n return json({ data: { api_key: issued.apiKey, connection: serializeConnection(issued.connection) } }, 201)\n }\n\n const caller = await authorized(request, options.apiKey, options.store)\n if (!caller) return error(401, 'unauthorized', 'A valid bearer key is required: a connection key issued through /admin/connect, or the STORE_API_KEY secret')\n\n // A connection may use only what the owner approved; the STORE_API_KEY secret may use everything.\n const scope = scopeFor(path)\n if (scope && caller.kind === 'connection' && !caller.connection.scopes.includes(scope)) {\n return error(403, 'forbidden', `This connection was not approved for \"${scope}\"; reconnect and approve it at /admin/connect`)\n }\n\n return await route(method, path, url, request, options.store, caller)\n } catch (e) {\n if (e instanceof ValidationError) return error(422, 'validation_failed', e.message, e.details)\n if (e instanceof StoreError) return error(e.status, e.code, e.message, e.details)\n console.error('store api: unhandled error', e)\n return error(500, 'internal_error', 'The store could not complete the request')\n }\n}\n\nasync function authorized(request: Request, apiKey: string | undefined, store: Store): Promise<ApiCaller | null> {\n const header = request.headers.get('authorization') ?? ''\n const match = /^Bearer\\s+(.+)$/i.exec(header.trim())\n if (!match) return null\n const bearer = match[1]!.trim()\n if (bearer.startsWith(API_KEY_PREFIX)) {\n const connection = await store.connections.resolveKey(bearer)\n return connection ? { kind: 'connection', connection } : null\n }\n if (apiKey && (await secretsEqual(bearer, apiKey))) return { kind: 'env' }\n return null\n}\n\n/** The caller's address for failure counting; Cloudflare sets the first header, proxies the second. */\nfunction clientAddress(request: Request): string {\n return request.headers.get('cf-connecting-ip') ?? request.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ?? 'unknown'\n}\n\nexport function serializeConnection(c: Connection) {\n return {\n id: c.id,\n name: c.name,\n tenant: c.tenant,\n scopes: c.scopes,\n key_prefix: c.keyPrefix,\n created_at: c.createdAt,\n issued_at: c.issuedAt,\n last_used_at: c.lastUsedAt,\n revoked_at: c.revokedAt,\n }\n}\n\nasync function readJson(request: Request): Promise<Record<string, unknown>> {\n const text = await request.text()\n if (!text.trim()) throw new ValidationError('Request body must be JSON', { body: 'is empty' })\n let parsed: unknown\n try {\n parsed = JSON.parse(text)\n } catch {\n throw new ValidationError('Request body must be JSON', { body: 'is not valid JSON' })\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) throw new ValidationError('Request body must be a JSON object', { body: 'is not an object' })\n return parsed as Record<string, unknown>\n}\n\n/**\n * The scope an endpoint needs. There is deliberately no endpoint that creates\n * an order: orders come only from the store's own checkout, priced from its\n * database, or from a buyer accepting a quote, so a leaked key cannot invent\n * sales. A key with `quotes` can price a quote; only the buyer turns it into\n * an order.\n */\nfunction scopeFor(path: string): ConnectScope | null {\n const first = path.split('/').filter(Boolean)[0]\n if (first === 'orders') return /^\\/orders\\/[^/]+\\/fulfillments$/.test(path) ? 'fulfillments' : 'orders'\n if (first === 'inventory') return 'inventory'\n if (first === 'products' || first === 'categories') return 'catalog'\n if (first === 'quotes') return 'quotes'\n return null // /health, /connect\n}\n\nasync function route(method: string, path: string, url: URL, request: Request, store: Store, caller: ApiCaller): Promise<Response> {\n const segments = path.split('/').filter(Boolean)\n\n if (path === '/health' && method === 'GET') {\n const health = await store.health()\n const connection = caller.kind === 'connection' ? { id: caller.connection.id, name: caller.connection.name, tenant: caller.connection.tenant } : null\n return json({ data: { ...health, connection } })\n }\n\n if (path === '/connect') {\n if (method === 'GET') return json({ data: caller.kind === 'connection' ? serializeConnection(caller.connection) : null })\n if (method === 'DELETE') {\n if (caller.kind !== 'connection') return error(409, 'conflict', 'The STORE_API_KEY secret is not a connection; rotate it on the Worker instead')\n await store.connections.revoke(caller.connection.id)\n return new Response(null, { status: 204 })\n }\n }\n\n if (segments[0] === 'orders') {\n if (segments.length === 1 && method === 'GET') {\n const updatedSince = url.searchParams.get('updated_since')\n const cursor = url.searchParams.get('cursor')\n const limitRaw = url.searchParams.get('limit')\n const limit = limitRaw ? Number(limitRaw) : 50\n if (limitRaw && (!Number.isInteger(limit) || limit < 1 || limit > 200)) throw new ValidationError('Invalid query', { limit: 'must be an integer from 1 to 200' })\n if (updatedSince && Number.isNaN(Date.parse(updatedSince))) throw new ValidationError('Invalid query', { updated_since: 'must be an ISO-8601 timestamp' })\n // Lazy housekeeping: abandoned processor checkouts become cancelled before the consumer sees the list.\n await store.orders.expireAbandoned()\n const page = await store.orders.changedSince(updatedSince ? new Date(updatedSince).toISOString() : null, cursor, limit)\n return json({ data: page.orders.map((o) => serializeOrder(o, store.config.orders.numberPrefix)), next_cursor: page.nextCursor })\n }\n const id = segments[1] ? decodeURIComponent(segments[1]) : ''\n if (segments.length === 2 && id) {\n if (method === 'GET') return json({ data: serializeOrder(await store.orders.require(id), store.config.orders.numberPrefix) })\n if (method === 'PATCH') {\n const patch = validateOrderPatch(await readJson(request))\n // Recording a payment is its own scope: a key that may read and stamp orders cannot\n // turn an unpaid order into a paid one unless the owner approved that too.\n if (patch.payment && caller.kind === 'connection' && !caller.connection.scopes.includes('payments')) {\n return error(403, 'forbidden', 'This connection was not approved for \"payments\"; reconnect and approve it at /admin/connect')\n }\n let order = await store.orders.require(id)\n if (patch.external_reference !== undefined) order = await store.orders.setExternalReference(id, patch.external_reference)\n if (patch.payment) order = await store.orders.setPaymentStatus(id, patch.payment)\n if (patch.status === 'cancelled') order = await store.orders.cancel(id, 'Cancelled by the operations system')\n return json({ data: serializeOrder(order, store.config.orders.numberPrefix) })\n }\n }\n if (segments.length === 3 && id && segments[2] === 'fulfillments' && method === 'POST') {\n const input = validateFulfillment(await readJson(request))\n const result = await store.orders.addFulfillment(id, input)\n return json({ data: serializeFulfillment(result.fulfillment) }, result.created ? 201 : 200)\n }\n }\n\n if (segments[0] === 'quotes') {\n const prefix = store.config.quotes.numberPrefix\n if (segments.length === 1 && method === 'GET') {\n const query = validateListQuery(url.searchParams, { defaultLimit: 50, maxLimit: 200, allowUpdatedSince: true })\n // Lazy housekeeping: offers past their expiry become `expired` before the consumer sees the list.\n await store.quotes.expireStale()\n const page = await store.quotes.changedSince(query.updatedSince, query.cursor, query.limit)\n return json({ data: page.quotes.map((q) => serializeQuote(q, prefix)), next_cursor: page.nextCursor })\n }\n const id = segments[1] ? decodeURIComponent(segments[1]) : ''\n if (segments.length === 2 && id) {\n if (method === 'GET') return json({ data: serializeQuote(await store.quotes.require(id), prefix) })\n if (method === 'PATCH') {\n const patch = validateQuotePatch(await readJson(request))\n let quote = await store.quotes.require(id)\n if (patch.external_reference !== undefined) quote = await store.quotes.setExternalReference(id, patch.external_reference)\n if (patch.offer) quote = await store.quotes.offer(id, patch.offer)\n if (patch.status === 'cancelled') quote = await store.quotes.cancel(id, 'Cancelled by the operations system')\n return json({ data: serializeQuote(quote, prefix) })\n }\n }\n }\n\n if (path === '/inventory' && method === 'PUT') {\n const items = validateInventory(await readJson(request))\n return json({ data: await store.inventory.set(items) })\n }\n\n if (path === '/products' && method === 'PUT') {\n const products = validateProducts(await readJson(request))\n return json({ data: { upserted: await store.catalog.upsertProducts(products) } })\n }\n\n if (path === '/products' && method === 'GET') {\n const query = validateListQuery(url.searchParams, { defaultLimit: 50, maxLimit: 200, allowUpdatedSince: true, allowActive: true })\n const page = await store.catalog.listProducts(query)\n return json({ data: page.products.map(serializeProduct), next_cursor: page.nextCursor })\n }\n\n if (segments[0] === 'products' && segments.length === 2) {\n const sku = decodeURIComponent(segments[1]!)\n if (method === 'GET') return json({ data: serializeProduct(await store.catalog.requireProduct(sku)) })\n if (method === 'DELETE') {\n const done = await store.catalog.deactivateProduct(sku)\n if (!done) return error(404, 'not_found', `Product ${sku} not found`)\n return new Response(null, { status: 204 })\n }\n }\n\n if (path === '/categories' && method === 'PUT') {\n const categories = validateCategories(await readJson(request))\n return json({ data: { upserted: await store.catalog.upsertCategories(categories) } })\n }\n\n if (path === '/categories' && method === 'GET') {\n const query = validateListQuery(url.searchParams, { defaultLimit: 500, maxLimit: 1000 })\n const page = await store.catalog.listCategories(query)\n return json({ data: page.categories.map(serializeCategory), next_cursor: page.nextCursor })\n }\n\n return error(404, 'not_found', `No such endpoint: ${method} ${path}`)\n}\n\nexport {\n serializeOrder, serializeFulfillment, serializeProduct, serializeCategory, serializeQuote,\n type ApiOrder, type ApiFulfillment, type ApiProduct, type ApiCategory, type ApiQuote,\n} from './serialize'\nexport { ValidationError } from './validate'\n"],"mappings":"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","names":[]}