@zappie/sdk 0.1.3 → 0.1.5
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/CHANGELOG.md +12 -0
- package/README.md +1 -0
- package/dist/cart/branch.d.ts +26 -0
- package/dist/cart/branch.js +79 -0
- package/dist/cart/branch.js.map +7 -0
- package/dist/cart/index.js +6 -1
- package/dist/cart/index.js.map +2 -2
- package/dist/cart/item.d.ts +3 -2
- package/dist/cart/quote-client.js +48 -3
- package/dist/cart/quote-client.js.map +2 -2
- package/dist/cart/quote-response.js +27 -0
- package/dist/cart/quote-response.js.map +3 -3
- package/dist/cart/quote.d.ts +8 -3
- package/dist/cart/storage.js +1 -1
- package/dist/cart/storage.js.map +2 -2
- package/dist/cart/wire.js +1 -0
- package/dist/cart/wire.js.map +2 -2
- package/dist/{client-BctSrEhO.d.ts → client-Bm2cCYea.d.ts} +17 -342
- package/dist/client.js +4 -2
- package/dist/client.js.map +2 -2
- package/dist/config.js.map +2 -2
- package/dist/debug.d.ts +3 -2
- package/dist/debug.js +1 -1
- package/dist/index.d.ts +9 -5
- package/dist/index.js +1 -1
- package/dist/index.js.map +2 -2
- package/dist/integrations/index.d.ts +39 -0
- package/dist/integrations/index.js +78 -0
- package/dist/integrations/index.js.map +7 -0
- package/dist/internal.d.ts +3 -3
- package/dist/{models-Dj-VkdU1.d.ts → models-Rg5kQUkv.d.ts} +26 -0
- package/dist/persist/catalogue.d.ts +3 -2
- package/dist/react/index.d.ts +3 -2
- package/dist/server/index.d.ts +4 -2
- package/dist/testing/index.d.ts +4 -2
- package/dist/transport/category-page.d.ts +1 -1
- package/dist/transport/mappers.js +20 -0
- package/dist/transport/mappers.js.map +2 -2
- package/dist/transport/quote-body.js +24 -0
- package/dist/transport/quote-body.js.map +7 -0
- package/dist/transport/rest.js +10 -2
- package/dist/transport/rest.js.map +2 -2
- package/dist/transport/search.d.ts +3 -2
- package/dist/transport/selected-menu.d.ts +3 -2
- package/dist/transport/store.d.ts +2 -2
- package/dist/transport/store.js +14 -1
- package/dist/transport/store.js.map +3 -3
- package/dist/types-CuDvfW9g.d.ts +380 -0
- package/package.json +9 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/transport/mappers.ts"],
|
|
4
|
-
"sourcesContent": ["import { ZappieError } from \"../errors\";\nimport type {\n Item,\n Menu,\n MenuSchedule,\n OpeningTime,\n OptionGroup,\n Variant,\n} from \"../models\";\nimport type { Currency, Money } from \"../money\";\nimport { money } from \"../money\";\n/** A value newer than this SDK; the item that carries it is shown as unavailable. */\nclass UnsupportedValue extends Error {}\nexport function record(value: unknown): Record<string, unknown> {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n throw new ZappieError(\"server_error\", \"Invalid storefront response object\");\n }\n return value as Record<string, unknown>;\n}\nexport function text(value: unknown): string {\n if (typeof value !== \"string\") {\n throw new ZappieError(\"server_error\", \"Invalid storefront response string\");\n }\n return value;\n}\nexport function list(value: unknown): unknown[] {\n if (!Array.isArray(value)) {\n throw new ZappieError(\"server_error\", \"Invalid storefront response list\");\n }\n return value;\n}\nfunction number(value: unknown): number {\n if (typeof value !== \"number\" || !Number.isFinite(value)) {\n throw new ZappieError(\"server_error\", \"Invalid storefront response number\");\n }\n return value;\n}\nfunction nonnegative(value: unknown): number {\n const result = number(value);\n if (result < 0) {\n throw new ZappieError(\n \"server_error\",\n \"Negative selection or quantity bound\",\n );\n }\n return result;\n}\nfunction bounds(value: unknown): { min: number; max?: number } {\n const input = record(value);\n const min = nonnegative(input.min);\n if (input.max == null) {\n return { min };\n }\n const max = nonnegative(input.max);\n if (max < min) {\n throw new ZappieError(\n \"server_error\",\n \"Maximum quantity is below its minimum\",\n );\n }\n return { min, max };\n}\nexport const isString = (value: unknown): value is string =>\n typeof value === \"string\";\nexport const isBoolean = (value: unknown): value is boolean =>\n typeof value === \"boolean\";\n/** `{ [key]: value }` when the value passes the guard, otherwise `{}`, for optional wire fields. */\nexport function opt<K extends string, T>(\n key: K,\n value: unknown,\n is: (value: unknown) => value is T,\n): { [P in K]?: T } {\n return is(value) ? ({ [key]: value } as { [P in K]?: T }) : {};\n}\nexport function mapMoney(value: unknown): Money {\n const v = record(value);\n return money(number(v.amount_minor), text(v.currency) as Currency);\n}\nexport function hours(value: unknown): OpeningTime[] {\n return list(value ?? []).map((slot) => {\n const v = record(slot);\n const day = number(v.day);\n if (!Number.isInteger(day) || day < 0 || day > 6) {\n throw new ZappieError(\"server_error\", \"Invalid opening day\");\n }\n return {\n day: day as OpeningTime[\"day\"],\n opens: text(v.open),\n closes: text(v.close),\n ...opt(\"disabled\", v.disabled, isBoolean),\n };\n });\n}\nfunction schedule(v: Record<string, unknown>): MenuSchedule | undefined {\n if (v.hours === undefined && v.preorder === undefined) {\n return undefined;\n }\n const preorder = v.preorder === undefined ? undefined : record(v.preorder);\n return {\n openingTimes: hours(v.hours),\n ...(preorder\n ? {\n preorder: true,\n preorderNoticeHours: number(preorder.notice_hours),\n preorderMaxDays: number(preorder.max_days),\n }\n : {}),\n };\n}\nfunction mapGroup(value: unknown): OptionGroup {\n const g = record(value);\n const selection = { min: g.min, max: g.max };\n const isAttribute = g.kind === \"attribute\";\n const kind = text(g.kind);\n if (![\"attribute\", \"choice\", \"text\"].includes(kind)) {\n throw new UnsupportedValue(`option input kind \"${kind}\"`);\n }\n return {\n id: text(g.id),\n name: text(g.name),\n kind: kind === \"text\" ? \"text\" : \"select\",\n required:\n typeof g.required === \"boolean\" ? g.required : number(selection.min) > 0,\n selection: bounds(selection),\n isAttribute,\n choices: list(g.choices ?? []).map((raw) => {\n const c = record(raw);\n const price = mapMoney(c.price);\n return {\n id: text(c.id),\n name: text(c.name),\n price: isAttribute ? money(0, price.currency) : price,\n ...(isAttribute && c.price_hint !== undefined\n ? { priceHint: mapMoney(c.price_hint) }\n : {}),\n isAvailable: c.available === true,\n ...(c.variable_quantity === true\n ? {\n quantity: bounds({\n min: c.min_quantity ?? 1,\n max: c.max_quantity,\n }),\n }\n : {}),\n };\n }),\n ...opt(\"helpText\", g.help_text, isString),\n ...(isString(g.placeholder)\n ? { input: { placeholder: g.placeholder } }\n : {}),\n ...(g.conditions\n ? {\n conditions: list(g.conditions).map((raw) => {\n const c = record(raw);\n const count = c.count ? record(c.count) : undefined;\n if (count && ![\"eq\", \"gte\", \"lte\"].includes(text(count.op))) {\n throw new UnsupportedValue(\n `option count operator \"${text(count.op)}\"`,\n );\n }\n if (\n c.match !== undefined &&\n c.match !== \"any\" &&\n c.match !== \"all\"\n ) {\n throw new UnsupportedValue(\n `option match operator \"${String(c.match)}\"`,\n );\n }\n if (c.set_required != null && typeof c.set_required !== \"boolean\") {\n throw new ZappieError(\n \"server_error\",\n \"Invalid conditional required flag\",\n );\n }\n return {\n groupId: text(c.group_id),\n choiceIds: list(c.choice_ids ?? []).map(text),\n match: c.match === \"all\" ? (\"all\" as const) : (\"any\" as const),\n ...(count\n ? {\n count: {\n op: text(count.op) as \"eq\" | \"gte\" | \"lte\",\n value: nonnegative(count.value),\n },\n }\n : {}),\n ...(c.set_required !== undefined\n ? { setRequired: c.set_required as boolean | null }\n : {}),\n ...(c.set_min !== undefined\n ? { setMin: c.set_min === null ? null : nonnegative(c.set_min) }\n : {}),\n ...(c.set_max !== undefined\n ? { setMax: c.set_max === null ? null : nonnegative(c.set_max) }\n : {}),\n };\n }),\n }\n : {}),\n };\n}\nfunction mapPromo(value: unknown): NonNullable<Item[\"promo\"]> {\n const p = record(value);\n return { name: text(p.name) };\n}\nexport function mapItem(value: unknown): Item {\n const v = record(value);\n const id = text(v.id);\n const optionGroups: OptionGroup[] = [];\n let unsupported = false;\n for (const raw of list(v.option_groups ?? [])) {\n try {\n optionGroups.push(mapGroup(raw));\n } catch (error) {\n if (!(error instanceof UnsupportedValue)) {\n throw error;\n }\n unsupported = true;\n console.warn(\n `Zappie item ${id} uses an ${error.message} this SDK can't validate; it is shown as unavailable`,\n );\n }\n }\n let unresolved = false;\n const variants = list(v.variants).map((raw) => {\n const x = record(raw);\n const seen = new Set<string>();\n return {\n id: text(x.id),\n attributeChoices: Object.fromEntries(\n list(x.choice_ids)\n .map((rawId) => {\n const choiceId = text(rawId);\n const groups = optionGroups.filter(\n (group) =>\n group.isAttribute &&\n group.choices.some((choice) => choice.id === choiceId),\n );\n // An unresolvable choice (for example a deleted option) makes this\n // item unavailable instead of failing the whole menu read.\n if (groups.length !== 1 || seen.has(groups[0]!.id)) {\n unresolved = true;\n return [\"\", choiceId];\n }\n seen.add(groups[0]!.id);\n return [groups[0]!.id, choiceId];\n })\n .filter(([groupId]) => groupId),\n ),\n price: mapMoney(x.price),\n ...opt(\"lowStock\", x.low_stock, isBoolean),\n isAvailable: x.available === true,\n };\n });\n if (unresolved) {\n console.warn(\n `Zappie item ${id} has a variant whose choices don't match its attributes; it is shown as unavailable`,\n );\n }\n if (unsupported || unresolved) {\n for (const variant of variants) {\n variant.isAvailable = false;\n }\n }\n if (!variants.length) {\n throw new ZappieError(\n \"server_error\",\n \"An item must carry at least one exact variant\",\n );\n }\n const quantity = { min: v.min_quantity ?? 1, max: v.max_quantity };\n if (new Set(variants.map((variant) => variant.id)).size !== variants.length) {\n throw new ZappieError(\"server_error\", \"Duplicate variant identity\");\n }\n return {\n ...(v.placements !== undefined\n ? {\n placements: list(v.placements).map((raw) => {\n const placement = record(raw);\n return {\n menuId: text(placement.menu_id),\n categoryId: text(placement.category_id),\n };\n }),\n }\n : {}),\n id,\n name: text(v.name),\n images: list(v.images ?? []).map(text),\n price: mapMoney(v.price),\n isAvailable: !unsupported && !unresolved && v.available === true,\n quantity: { ...bounds(quantity), step: 1 },\n variants: variants as [Variant, ...Variant[]],\n optionGroups,\n tags: list(v.tags ?? []).map(text),\n ...opt(\"stockLabel\", v.stock_label, isString),\n ...(v.promo !== undefined ? { promo: mapPromo(v.promo) } : {}),\n ...opt(\"description\", v.description, isString),\n ...(v.previous_price ? { compareAtPrice: mapMoney(v.previous_price) } : {}),\n };\n}\nexport function mapMenu(value: unknown, branchId: string): Menu {\n const v = record(value);\n const sources = list(v.menus);\n if (\n v.completeness !== undefined &&\n v.completeness !== \"partial\" &&\n v.completeness !== \"complete\"\n ) {\n throw new ZappieError(\"server_error\", \"Invalid catalogue completeness\");\n }\n const categories = new Map<string, Menu[\"categories\"][number]>();\n const menus = sources.map((raw) => {\n const m = record(raw);\n if (\n (m.placement_count !== undefined &&\n (!Number.isSafeInteger(m.placement_count) ||\n Number(m.placement_count) < 0)) ||\n (m.paginated !== undefined && typeof m.paginated !== \"boolean\")\n ) {\n throw new ZappieError(\"server_error\", \"Invalid menu loading metadata\");\n }\n const categoryIds = list(m.categories ?? []).map((rawCategory) => {\n const c = record(rawCategory);\n const id = text(c.id);\n const previous = categories.get(id);\n const itemIds = [\n ...new Set([\n ...(previous?.itemIds ?? []),\n ...list(c.item_ids ?? []).map(text),\n ]),\n ];\n categories.set(id, {\n id,\n name: text(c.name),\n itemIds,\n });\n return id;\n });\n const s = schedule(m);\n return {\n id: text(m.id),\n name: text(m.name),\n categoryIds,\n ...(m.placement_count !== undefined\n ? { placementCount: m.placement_count as number }\n : {}),\n ...opt(\"paginated\", m.paginated, isBoolean),\n ...(s ? { schedule: s } : {}),\n ...opt(\"infoBanner\", m.info_banner, isString),\n };\n });\n const items = Object.fromEntries(\n Object.entries(record(v.items)).map(([id, item]) => {\n const mapped = mapItem(item);\n if (mapped.id !== id) {\n throw new ZappieError(\n \"server_error\",\n \"Item map key does not match its identity\",\n );\n }\n return [id, mapped];\n }),\n );\n if (v.branch_id !== undefined && v.branch_id !== branchId) {\n throw new ZappieError(\"server_error\", \"Menu branch mismatch\");\n }\n return {\n id: menus.length === 1 ? menus[0]!.id : `branch:${branchId}`,\n branchId,\n ...(v.completeness === \"partial\"\n ? { completeness: \"partial\" as const }\n : {}),\n name: menus.length === 1 ? menus[0]!.name : \"Catalogue\",\n version: text(v.menu_version),\n categories: [...categories.values()],\n items,\n menus,\n ...(menus.length === 1 && menus[0]!.schedule\n ? { schedule: menus[0]!.schedule }\n : {}),\n };\n}\n"],
|
|
5
|
-
"mappings": ";AAAA,SAAS,mBAAmB;
|
|
4
|
+
"sourcesContent": ["import { ZappieError } from \"../errors\";\nimport type {\n Item,\n Menu,\n MenuSchedule,\n OpeningTime,\n OptionGroup,\n Store,\n Variant,\n} from \"../models\";\nimport type { Currency, Money } from \"../money\";\nimport { money } from \"../money\";\n/** A value newer than this SDK; the item that carries it is shown as unavailable. */\nclass UnsupportedValue extends Error {}\nexport function record(value: unknown): Record<string, unknown> {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n throw new ZappieError(\"server_error\", \"Invalid storefront response object\");\n }\n return value as Record<string, unknown>;\n}\nexport function text(value: unknown): string {\n if (typeof value !== \"string\") {\n throw new ZappieError(\"server_error\", \"Invalid storefront response string\");\n }\n return value;\n}\nexport function list(value: unknown): unknown[] {\n if (!Array.isArray(value)) {\n throw new ZappieError(\"server_error\", \"Invalid storefront response list\");\n }\n return value;\n}\nfunction number(value: unknown): number {\n if (typeof value !== \"number\" || !Number.isFinite(value)) {\n throw new ZappieError(\"server_error\", \"Invalid storefront response number\");\n }\n return value;\n}\nfunction nonnegative(value: unknown): number {\n const result = number(value);\n if (result < 0) {\n throw new ZappieError(\n \"server_error\",\n \"Negative selection or quantity bound\",\n );\n }\n return result;\n}\nfunction bounds(value: unknown): { min: number; max?: number } {\n const input = record(value);\n const min = nonnegative(input.min);\n if (input.max == null) {\n return { min };\n }\n const max = nonnegative(input.max);\n if (max < min) {\n throw new ZappieError(\n \"server_error\",\n \"Maximum quantity is below its minimum\",\n );\n }\n return { min, max };\n}\nexport const isString = (value: unknown): value is string =>\n typeof value === \"string\";\nexport const isBoolean = (value: unknown): value is boolean =>\n typeof value === \"boolean\";\n/** `{ [key]: value }` when the value passes the guard, otherwise `{}`, for optional wire fields. */\nexport function opt<K extends string, T>(\n key: K,\n value: unknown,\n is: (value: unknown) => value is T,\n): { [P in K]?: T } {\n return is(value) ? ({ [key]: value } as { [P in K]?: T }) : {};\n}\nexport function mapMoney(value: unknown): Money {\n const v = record(value);\n return money(number(v.amount_minor), text(v.currency) as Currency);\n}\nexport function hours(value: unknown): OpeningTime[] {\n return list(value ?? []).map((slot) => {\n const v = record(slot);\n const day = number(v.day);\n if (!Number.isInteger(day) || day < 0 || day > 6) {\n throw new ZappieError(\"server_error\", \"Invalid opening day\");\n }\n return {\n day: day as OpeningTime[\"day\"],\n opens: text(v.open),\n closes: text(v.close),\n ...opt(\"disabled\", v.disabled, isBoolean),\n };\n });\n}\nfunction schedule(v: Record<string, unknown>): MenuSchedule | undefined {\n if (v.hours === undefined && v.preorder === undefined) {\n return undefined;\n }\n const preorder = v.preorder === undefined ? undefined : record(v.preorder);\n return {\n openingTimes: hours(v.hours),\n ...(preorder\n ? {\n preorder: true,\n preorderNoticeHours: number(preorder.notice_hours),\n preorderMaxDays: number(preorder.max_days),\n }\n : {}),\n };\n}\nfunction mapGroup(value: unknown): OptionGroup {\n const g = record(value);\n const selection = { min: g.min, max: g.max };\n const isAttribute = g.kind === \"attribute\";\n const kind = text(g.kind);\n if (![\"attribute\", \"choice\", \"text\"].includes(kind)) {\n throw new UnsupportedValue(`option input kind \"${kind}\"`);\n }\n return {\n id: text(g.id),\n name: text(g.name),\n kind: kind === \"text\" ? \"text\" : \"select\",\n required:\n typeof g.required === \"boolean\" ? g.required : number(selection.min) > 0,\n selection: bounds(selection),\n isAttribute,\n choices: list(g.choices ?? []).map((raw) => {\n const c = record(raw);\n const price = mapMoney(c.price);\n return {\n id: text(c.id),\n name: text(c.name),\n price: isAttribute ? money(0, price.currency) : price,\n ...(isAttribute && c.price_hint !== undefined\n ? { priceHint: mapMoney(c.price_hint) }\n : {}),\n isAvailable: c.available === true,\n ...(c.variable_quantity === true\n ? {\n quantity: bounds({\n min: c.min_quantity ?? 1,\n max: c.max_quantity,\n }),\n }\n : {}),\n };\n }),\n ...opt(\"helpText\", g.help_text, isString),\n ...(isString(g.placeholder)\n ? { input: { placeholder: g.placeholder } }\n : {}),\n ...(g.conditions\n ? {\n conditions: list(g.conditions).map((raw) => {\n const c = record(raw);\n const count = c.count ? record(c.count) : undefined;\n if (count && ![\"eq\", \"gte\", \"lte\"].includes(text(count.op))) {\n throw new UnsupportedValue(\n `option count operator \"${text(count.op)}\"`,\n );\n }\n if (\n c.match !== undefined &&\n c.match !== \"any\" &&\n c.match !== \"all\"\n ) {\n throw new UnsupportedValue(\n `option match operator \"${String(c.match)}\"`,\n );\n }\n if (c.set_required != null && typeof c.set_required !== \"boolean\") {\n throw new ZappieError(\n \"server_error\",\n \"Invalid conditional required flag\",\n );\n }\n return {\n groupId: text(c.group_id),\n choiceIds: list(c.choice_ids ?? []).map(text),\n match: c.match === \"all\" ? (\"all\" as const) : (\"any\" as const),\n ...(count\n ? {\n count: {\n op: text(count.op) as \"eq\" | \"gte\" | \"lte\",\n value: nonnegative(count.value),\n },\n }\n : {}),\n ...(c.set_required !== undefined\n ? { setRequired: c.set_required as boolean | null }\n : {}),\n ...(c.set_min !== undefined\n ? { setMin: c.set_min === null ? null : nonnegative(c.set_min) }\n : {}),\n ...(c.set_max !== undefined\n ? { setMax: c.set_max === null ? null : nonnegative(c.set_max) }\n : {}),\n };\n }),\n }\n : {}),\n };\n}\nfunction mapPromo(value: unknown): NonNullable<Item[\"promo\"]> {\n const p = record(value);\n return { name: text(p.name) };\n}\nexport function mapItem(value: unknown): Item {\n const v = record(value);\n const id = text(v.id);\n const optionGroups: OptionGroup[] = [];\n let unsupported = false;\n for (const raw of list(v.option_groups ?? [])) {\n try {\n optionGroups.push(mapGroup(raw));\n } catch (error) {\n if (!(error instanceof UnsupportedValue)) {\n throw error;\n }\n unsupported = true;\n console.warn(\n `Zappie item ${id} uses an ${error.message} this SDK can't validate; it is shown as unavailable`,\n );\n }\n }\n let unresolved = false;\n const variants = list(v.variants).map((raw) => {\n const x = record(raw);\n const seen = new Set<string>();\n return {\n id: text(x.id),\n attributeChoices: Object.fromEntries(\n list(x.choice_ids)\n .map((rawId) => {\n const choiceId = text(rawId);\n const groups = optionGroups.filter(\n (group) =>\n group.isAttribute &&\n group.choices.some((choice) => choice.id === choiceId),\n );\n // An unresolvable choice (for example a deleted option) makes this\n // item unavailable instead of failing the whole menu read.\n if (groups.length !== 1 || seen.has(groups[0]!.id)) {\n unresolved = true;\n return [\"\", choiceId];\n }\n seen.add(groups[0]!.id);\n return [groups[0]!.id, choiceId];\n })\n .filter(([groupId]) => groupId),\n ),\n price: mapMoney(x.price),\n ...opt(\"lowStock\", x.low_stock, isBoolean),\n isAvailable: x.available === true,\n };\n });\n if (unresolved) {\n console.warn(\n `Zappie item ${id} has a variant whose choices don't match its attributes; it is shown as unavailable`,\n );\n }\n if (unsupported || unresolved) {\n for (const variant of variants) {\n variant.isAvailable = false;\n }\n }\n if (!variants.length) {\n throw new ZappieError(\n \"server_error\",\n \"An item must carry at least one exact variant\",\n );\n }\n const quantity = { min: v.min_quantity ?? 1, max: v.max_quantity };\n if (new Set(variants.map((variant) => variant.id)).size !== variants.length) {\n throw new ZappieError(\"server_error\", \"Duplicate variant identity\");\n }\n return {\n ...(v.placements !== undefined\n ? {\n placements: list(v.placements).map((raw) => {\n const placement = record(raw);\n return {\n menuId: text(placement.menu_id),\n categoryId: text(placement.category_id),\n };\n }),\n }\n : {}),\n id,\n name: text(v.name),\n images: list(v.images ?? []).map(text),\n price: mapMoney(v.price),\n isAvailable: !unsupported && !unresolved && v.available === true,\n quantity: { ...bounds(quantity), step: 1 },\n variants: variants as [Variant, ...Variant[]],\n optionGroups,\n tags: list(v.tags ?? []).map(text),\n ...(v.fulfilment !== undefined\n ? { fulfilment: list(v.fulfilment).map(text) }\n : {}),\n ...opt(\"stockLabel\", v.stock_label, isString),\n ...(v.promo !== undefined ? { promo: mapPromo(v.promo) } : {}),\n ...opt(\"description\", v.description, isString),\n ...(v.previous_price ? { compareAtPrice: mapMoney(v.previous_price) } : {}),\n };\n}\nexport function mapMenu(value: unknown, branchId: string): Menu {\n const v = record(value);\n const sources = list(v.menus);\n if (\n v.completeness !== undefined &&\n v.completeness !== \"partial\" &&\n v.completeness !== \"complete\"\n ) {\n throw new ZappieError(\"server_error\", \"Invalid catalogue completeness\");\n }\n const categories = new Map<string, Menu[\"categories\"][number]>();\n const menus = sources.map((raw) => {\n const m = record(raw);\n if (\n (m.placement_count !== undefined &&\n (!Number.isSafeInteger(m.placement_count) ||\n Number(m.placement_count) < 0)) ||\n (m.paginated !== undefined && typeof m.paginated !== \"boolean\")\n ) {\n throw new ZappieError(\"server_error\", \"Invalid menu loading metadata\");\n }\n const categoryIds = list(m.categories ?? []).map((rawCategory) => {\n const c = record(rawCategory);\n const id = text(c.id);\n const previous = categories.get(id);\n const itemIds = [\n ...new Set([\n ...(previous?.itemIds ?? []),\n ...list(c.item_ids ?? []).map(text),\n ]),\n ];\n categories.set(id, {\n id,\n name: text(c.name),\n itemIds,\n });\n return id;\n });\n const s = schedule(m);\n return {\n id: text(m.id),\n name: text(m.name),\n categoryIds,\n ...(m.fulfilment !== undefined\n ? { fulfilment: list(m.fulfilment).map(text) }\n : {}),\n ...(m.placement_count !== undefined\n ? { placementCount: m.placement_count as number }\n : {}),\n ...opt(\"paginated\", m.paginated, isBoolean),\n ...(s ? { schedule: s } : {}),\n ...opt(\"infoBanner\", m.info_banner, isString),\n };\n });\n const items = Object.fromEntries(\n Object.entries(record(v.items)).map(([id, item]) => {\n const mapped = mapItem(item);\n if (mapped.id !== id) {\n throw new ZappieError(\n \"server_error\",\n \"Item map key does not match its identity\",\n );\n }\n return [id, mapped];\n }),\n );\n if (v.branch_id !== undefined && v.branch_id !== branchId) {\n throw new ZappieError(\"server_error\", \"Menu branch mismatch\");\n }\n return {\n id: menus.length === 1 ? menus[0]!.id : `branch:${branchId}`,\n branchId,\n ...(v.completeness === \"partial\"\n ? { completeness: \"partial\" as const }\n : {}),\n name: menus.length === 1 ? menus[0]!.name : \"Catalogue\",\n version: text(v.menu_version),\n categories: [...categories.values()],\n items,\n menus,\n ...(menus.length === 1 && menus[0]!.schedule\n ? { schedule: menus[0]!.schedule }\n : {}),\n };\n}\n\nexport function mapSeo(value: unknown): NonNullable<Store[\"seo\"]> {\n const seo = record(value);\n if (typeof seo.hidden !== \"boolean\") {\n throw new ZappieError(\"server_error\", \"Invalid store search visibility\");\n }\n return {\n titlePattern: text(seo.title_pattern),\n description: text(seo.description),\n shareImageUrl: text(seo.share_image_url),\n searchConsoleToken: text(seo.search_console_token),\n hidden: seo.hidden,\n redirects: list(seo.redirects).map((raw) => {\n const redirect = record(raw);\n return { from: text(redirect.from), to: text(redirect.to) };\n }),\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// src/transport/quote-body.ts
|
|
2
|
+
function quoteBody(body) {
|
|
3
|
+
const f = body.fulfilment;
|
|
4
|
+
if (!f) {
|
|
5
|
+
return body;
|
|
6
|
+
}
|
|
7
|
+
const { table: _table, address, ...fulfilment } = f;
|
|
8
|
+
if (!address) {
|
|
9
|
+
return { ...body, fulfilment };
|
|
10
|
+
}
|
|
11
|
+
const { line2: _line2, instructions, ...rest } = address;
|
|
12
|
+
return {
|
|
13
|
+
...body,
|
|
14
|
+
fulfilment: {
|
|
15
|
+
...fulfilment,
|
|
16
|
+
...fulfilment.instructions === void 0 && instructions !== void 0 ? { instructions } : {},
|
|
17
|
+
address: rest
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export {
|
|
22
|
+
quoteBody
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=quote-body.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/transport/quote-body.ts"],
|
|
4
|
+
"sourcesContent": ["import type { QuoteRequest } from \"../cart/types\";\n\n// Keep legacy fields in saved carts and host requests. REST accepts only these address fields.\nexport function quoteBody(body: QuoteRequest): QuoteRequest {\n const f = body.fulfilment;\n if (!f) {\n return body;\n }\n const { table: _table, address, ...fulfilment } = f;\n if (!address) {\n return { ...body, fulfilment };\n }\n const { line2: _line2, instructions, ...rest } = address;\n return {\n ...body,\n fulfilment: {\n ...fulfilment,\n ...(fulfilment.instructions === undefined && instructions !== undefined\n ? { instructions }\n : {}),\n address: rest,\n },\n };\n}\n"],
|
|
5
|
+
"mappings": ";AAGO,SAAS,UAAU,MAAkC;AAC1D,QAAM,IAAI,KAAK;AACf,MAAI,CAAC,GAAG;AACN,WAAO;AAAA,EACT;AACA,QAAM,EAAE,OAAO,QAAQ,SAAS,GAAG,WAAW,IAAI;AAClD,MAAI,CAAC,SAAS;AACZ,WAAO,EAAE,GAAG,MAAM,WAAW;AAAA,EAC/B;AACA,QAAM,EAAE,OAAO,QAAQ,cAAc,GAAG,KAAK,IAAI;AACjD,SAAO;AAAA,IACL,GAAG;AAAA,IACH,YAAY;AAAA,MACV,GAAG;AAAA,MACH,GAAI,WAAW,iBAAiB,UAAa,iBAAiB,SAC1D,EAAE,aAAa,IACf,CAAC;AAAA,MACL,SAAS;AAAA,IACX;AAAA,EACF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/transport/rest.js
CHANGED
|
@@ -11,11 +11,13 @@ import {
|
|
|
11
11
|
mapItem,
|
|
12
12
|
mapMenu,
|
|
13
13
|
mapMoney,
|
|
14
|
+
mapSeo,
|
|
14
15
|
opt,
|
|
15
16
|
record,
|
|
16
17
|
text
|
|
17
18
|
} from "./mappers.js";
|
|
18
19
|
var MAX_RETRY_WAIT_MS = 5e3;
|
|
20
|
+
var wireFulfilment = (value) => value === "dineIn" ? "dine_in" : value;
|
|
19
21
|
var RestTransport = class {
|
|
20
22
|
constructor(opts = {}) {
|
|
21
23
|
const url = new URL(opts.baseUrl ?? "https://api.zappie.ai/storefront/v1");
|
|
@@ -104,6 +106,7 @@ var RestTransport = class {
|
|
|
104
106
|
}
|
|
105
107
|
}
|
|
106
108
|
async quote(ctx, body) {
|
|
109
|
+
body = (await import("./quote-body.js")).quoteBody(body);
|
|
107
110
|
const request = {
|
|
108
111
|
...ctx,
|
|
109
112
|
idempotencyKey: ctx.idempotencyKey ?? await payloadKey(JSON.stringify(body))
|
|
@@ -135,7 +138,8 @@ var RestTransport = class {
|
|
|
135
138
|
money,
|
|
136
139
|
opt,
|
|
137
140
|
isString,
|
|
138
|
-
isBoolean
|
|
141
|
+
isBoolean,
|
|
142
|
+
mapSeo
|
|
139
143
|
);
|
|
140
144
|
}
|
|
141
145
|
async getMenu(ctx) {
|
|
@@ -146,6 +150,7 @@ var RestTransport = class {
|
|
|
146
150
|
do {
|
|
147
151
|
const query = new URLSearchParams({
|
|
148
152
|
branch: ctx.branchId,
|
|
153
|
+
...ctx.fulfilment ? { fulfilment: wireFulfilment(ctx.fulfilment) } : {},
|
|
149
154
|
...ctx.metadataOnly ? { metadata_only: "true" } : {},
|
|
150
155
|
...cursor ? { cursor } : {}
|
|
151
156
|
});
|
|
@@ -276,7 +281,10 @@ var RestTransport = class {
|
|
|
276
281
|
const value = record(
|
|
277
282
|
await this.json(
|
|
278
283
|
await this.request(
|
|
279
|
-
`/menus/version?${new URLSearchParams({
|
|
284
|
+
`/menus/version?${new URLSearchParams({
|
|
285
|
+
branch: ctx.branchId,
|
|
286
|
+
...ctx.fulfilment ? { fulfilment: wireFulfilment(ctx.fulfilment) } : {}
|
|
287
|
+
})}`,
|
|
280
288
|
ctx
|
|
281
289
|
)
|
|
282
290
|
)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/transport/rest.ts"],
|
|
4
|
-
"sourcesContent": ["import type { QuoteRequest, WireQuote } from \"../cart/types\";\nimport { ZappieError, wireError } from \"../errors\";\nimport { delay, throwIfAborted } from \"../internal/abort\";\nimport { payloadKey } from \"../internal/digest\";\nimport { money } from \"../money\";\nimport {\n hours,\n isBoolean,\n isString,\n list,\n mapItem,\n mapMenu,\n mapMoney,\n opt,\n record,\n text,\n} from \"./mappers\";\nimport type { SelectedMenuRequest } from \"./selected-menu\";\nimport type {\n MenuResponse,\n MenuVersion,\n RequestCtx,\n SearchRequest,\n Transport,\n} from \"./types\";\n/** The longest Retry-After the SDK waits out before surfacing the error instead. */\nconst MAX_RETRY_WAIT_MS = 5_000;\n/** REST connection options. A custom fetch can provide tracing or mock responses. */\nexport type RestTransportOptions = { baseUrl?: string; fetch?: typeof fetch };\n/** REST read adapter for the versioned storefront API. */\nexport class RestTransport implements Transport {\n readonly baseUrl: string;\n private fetcher: typeof fetch;\n constructor(opts: RestTransportOptions = {}) {\n const url = new URL(opts.baseUrl ?? \"https://api.zappie.ai/storefront/v1\");\n if (\n url.username ||\n url.password ||\n url.search ||\n url.hash ||\n (url.protocol !== \"https:\" &&\n !(\n url.protocol === \"http:\" &&\n [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(url.hostname)\n ))\n ) {\n throw new ZappieError(\n \"invalid_request\",\n \"REST baseUrl requires HTTPS, or HTTP on localhost\",\n );\n }\n this.baseUrl = url.href.replace(/\\/$/, \"\");\n // Called as this.fetcher(), a stored global fetch would run with the\n // transport as `this`, which Workers refuses (\"Illegal invocation\").\n this.fetcher =\n opts.fetch ?? ((input, init) => globalThis.fetch(input, init));\n }\n private async request(\n path: string,\n ctx: RequestCtx,\n etag?: string,\n body?: QuoteRequest,\n ): Promise<Response> {\n if (typeof window !== \"undefined\" && ctx.key.startsWith(\"sk_\")) {\n throw new ZappieError(\n \"invalid_request\",\n \"Secret keys cannot be sent from a browser\",\n );\n }\n for (let attempt = 0; ; attempt++) {\n throwIfAborted(ctx.signal);\n let response: Response;\n try {\n response = await this.fetcher(this.baseUrl + path, {\n method: body ? \"POST\" : \"GET\",\n ...(body ? { body: JSON.stringify(body) } : {}),\n // Workers reject redirect: \"error\"; a redirect is still refused below\n // as a non-OK response (a 3xx, or opaqueredirect in browsers).\n redirect: \"manual\",\n headers: {\n ...(body ? { \"Content-Type\": \"application/json\" } : {}),\n ...(ctx.idempotencyKey\n ? { \"Idempotency-Key\": ctx.idempotencyKey }\n : {}),\n Authorization: `Bearer ${ctx.key}`,\n \"Zappie-Version\": ctx.version,\n ...(etag ? { \"If-None-Match\": etag } : {}),\n },\n ...(ctx.signal ? { signal: ctx.signal } : {}),\n });\n } catch (error) {\n throwIfAborted(ctx.signal);\n if (attempt >= 2) {\n throw new ZappieError(\n \"network_error\",\n \"Could not reach the storefront service\",\n { cause: error },\n );\n }\n await delay(100 * 2 ** attempt + Math.random() * 100, ctx.signal);\n continue;\n }\n if (response.ok || response.status === 304) {\n return response;\n }\n const retry = response.headers.get(\"Retry-After\");\n const seconds = retry !== null ? Number(retry) : NaN;\n const wait =\n retry !== null\n ? Number.isFinite(seconds)\n ? Math.max(0, seconds * 1000)\n : Math.max(0, Date.parse(retry) - Date.now())\n : NaN;\n // A longer wait than this is the caller's decision, not a silently stalled read.\n const tooLong = Number.isFinite(wait) && wait > MAX_RETRY_WAIT_MS;\n if (\n attempt < 2 &&\n !tooLong &&\n (response.status === 429 || response.status >= 500)\n ) {\n await response.body?.cancel();\n await delay(\n Number.isFinite(wait)\n ? wait\n : 100 * 2 ** attempt + Math.random() * 100,\n ctx.signal,\n );\n continue;\n }\n const errorBody = await response.json().catch(() => ({}));\n throw wireError(\n errorBody,\n response.status,\n response.headers.get(\"Zappie-Request-Id\") ?? undefined,\n );\n }\n }\n private async json(response: Response): Promise<unknown> {\n try {\n return await response.json();\n } catch (cause) {\n if (cause instanceof Error && cause.name === \"AbortError\") {\n throw cause;\n }\n throw new ZappieError(\n \"server_error\",\n \"The storefront service returned invalid JSON\",\n { status: response.status, cause },\n );\n }\n }\n async quote(ctx: RequestCtx, body: QuoteRequest): Promise<WireQuote> {\n const request = {\n ...ctx,\n idempotencyKey:\n ctx.idempotencyKey ?? (await payloadKey(JSON.stringify(body))),\n };\n const raw = await this.json(\n await this.request(\"/quote\", request, undefined, body),\n );\n // The cart's quote client validates and maps the response.\n return raw as WireQuote;\n }\n async getStore(ctx: RequestCtx) {\n const mapper = import(\"./store\").catch((cause) => {\n throw new ZappieError(\n \"server_error\",\n \"Could not load the storefront store mapper\",\n { cause },\n );\n });\n const response = this.request(\"/store\", ctx).then((value) =>\n this.json(value),\n );\n const [{ mapStore }, raw] = await Promise.all([mapper, response]);\n return mapStore(\n raw,\n record,\n text,\n list,\n hours,\n mapMoney,\n money,\n opt,\n isString,\n isBoolean,\n );\n }\n async getMenu(\n ctx: RequestCtx & {\n branchId: string;\n ifNoneMatch?: string;\n metadataOnly?: boolean;\n },\n ): Promise<MenuResponse> {\n let cursor: string | undefined;\n const seen = new Set<string>();\n let output: ReturnType<typeof mapMenu> | undefined;\n let etag: string | undefined;\n do {\n const query = new URLSearchParams({\n branch: ctx.branchId,\n ...(ctx.metadataOnly ? { metadata_only: \"true\" } : {}),\n ...(cursor ? { cursor } : {}),\n });\n const response = await this.request(\n `/menus?${query}`,\n ctx,\n cursor ? undefined : ctx.ifNoneMatch,\n );\n if (response.status === 304) {\n if (cursor) {\n throw new ZappieError(\n \"server_error\",\n \"Unexpected 304 on a continuation page\",\n );\n }\n return {\n notModified: true,\n ...(response.headers.get(\"ETag\")\n ? { etag: response.headers.get(\"ETag\")! }\n : {}),\n };\n }\n const raw = record(await this.json(response));\n const page = mapMenu(raw, ctx.branchId);\n if (ctx.metadataOnly) {\n if (Object.keys(page.items).length || raw.next_cursor) {\n throw new ZappieError(\"server_error\", \"Invalid metadata response\");\n }\n return { menu: { ...page, completeness: \"partial\" } };\n }\n if (!output) {\n output = page;\n etag = response.headers.get(\"ETag\") ?? undefined;\n } else {\n if (page.version !== output.version) {\n throw new ZappieError(\n \"server_error\",\n \"Menu changed while fetching pages; retry the read\",\n );\n }\n Object.assign(output.items, page.items);\n for (const category of page.categories) {\n const existing = output.categories.find((c) => c.id === category.id);\n if (existing) {\n existing.itemIds = [\n ...new Set([...existing.itemIds, ...category.itemIds]),\n ];\n } else {\n output.categories.push(category);\n }\n }\n for (const section of page.menus ?? []) {\n const existing = output.menus?.find((m) => m.id === section.id);\n if (existing) {\n existing.categoryIds = [\n ...new Set([...existing.categoryIds, ...section.categoryIds]),\n ];\n } else {\n output.menus?.push(section);\n }\n }\n }\n cursor =\n typeof raw.next_cursor === \"string\" && raw.next_cursor\n ? raw.next_cursor\n : undefined;\n if (cursor) {\n if (seen.has(cursor)) {\n throw new ZappieError(\n \"server_error\",\n \"Repeated menu pagination cursor\",\n );\n }\n seen.add(cursor);\n }\n } while (cursor);\n if (output.menus && output.menus.length > 1) {\n output.id = `branch:${ctx.branchId}`;\n output.name = \"Catalogue\";\n delete output.schedule;\n }\n for (const category of output.categories) {\n for (const id of category.itemIds) {\n if (!Object.prototype.hasOwnProperty.call(output.items, id)) {\n throw new ZappieError(\n \"server_error\",\n \"Category refers to a missing item\",\n );\n }\n }\n }\n if (Object.keys(output.items).length > 2000) {\n console.warn(\"Zappie menu contains more than 2,000 items\");\n }\n return { menu: output, ...(etag ? { etag } : {}) };\n }\n async getSelectedMenu(ctx: SelectedMenuRequest) {\n return (await import(\"./selected-menu\")).selectedMenu(\n ctx,\n (path) => this.request(path, ctx, ctx.ifNoneMatch),\n (response) => this.json(response),\n mapMenu,\n () => new ZappieError(\"server_error\", \"Invalid selected menu\"),\n );\n }\n async search(ctx: SearchRequest) {\n return (await import(\"./search\")).searchPage(\n ctx,\n async (path) => this.json(await this.request(path, ctx)),\n mapMenu,\n () => new ZappieError(\"server_error\", \"Invalid search page\"),\n );\n }\n async getCategoryPage(\n ctx: RequestCtx & {\n branchId: string;\n menuId: string;\n categoryId: string;\n cursor?: string;\n },\n ) {\n const query = new URLSearchParams({\n branch: ctx.branchId,\n ...(ctx.cursor ? { cursor: ctx.cursor } : {}),\n });\n const raw = await this.json(\n await this.request(\n `/menus/${encodeURIComponent(ctx.menuId)}/categories/${encodeURIComponent(ctx.categoryId)}/items?${query}`,\n ctx,\n ),\n );\n // Loaded on first use: only progressive catalogues read pages.\n return (await import(\"./category-page\")).categoryPage(\n record(raw),\n ctx,\n mapMenu,\n () => new ZappieError(\"server_error\", \"Invalid category page\"),\n );\n }\n async getMenuVersion(\n ctx: RequestCtx & { branchId: string },\n ): Promise<MenuVersion> {\n const value = record(\n await this.json(\n await this.request(\n `/menus/version?${new URLSearchParams({ branch: ctx.branchId })}`,\n ctx,\n ),\n ),\n );\n if (\n typeof value.menu_version !== \"string\" ||\n typeof value.availability_hash !== \"string\"\n ) {\n throw new ZappieError(\"server_error\", \"Invalid menu version response\");\n }\n return {\n version: value.menu_version,\n availabilityHash: value.availability_hash,\n };\n }\n async getItem(ctx: RequestCtx & { branchId: string; itemId: string }) {\n const item = mapItem(\n await this.json(\n await this.request(\n `/items/${encodeURIComponent(ctx.itemId)}?${new URLSearchParams({ branch: ctx.branchId })}`,\n ctx,\n ),\n ),\n );\n if (item.id !== ctx.itemId) {\n throw new ZappieError(\n \"server_error\",\n \"Item response identity does not match the request\",\n );\n }\n return item;\n }\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["import type { QuoteRequest, WireQuote } from \"../cart/types\";\nimport type { MenuFulfilment } from \"../client\";\nimport { ZappieError, wireError } from \"../errors\";\nimport { delay, throwIfAborted } from \"../internal/abort\";\nimport { payloadKey } from \"../internal/digest\";\nimport { money } from \"../money\";\nimport {\n hours,\n isBoolean,\n isString,\n list,\n mapItem,\n mapMenu,\n mapMoney,\n mapSeo,\n opt,\n record,\n text,\n} from \"./mappers\";\nimport type { SelectedMenuRequest } from \"./selected-menu\";\nimport type {\n MenuResponse,\n MenuVersion,\n RequestCtx,\n SearchRequest,\n Transport,\n} from \"./types\";\n/** The longest Retry-After the SDK waits out before surfacing the error instead. */\nconst MAX_RETRY_WAIT_MS = 5_000;\nconst wireFulfilment = (value: MenuFulfilment) =>\n value === \"dineIn\" ? \"dine_in\" : value;\n/** REST connection options. A custom fetch can provide tracing or mock responses. */\nexport type RestTransportOptions = { baseUrl?: string; fetch?: typeof fetch };\n/** REST read adapter for the versioned storefront API. */\nexport class RestTransport implements Transport {\n readonly baseUrl: string;\n private fetcher: typeof fetch;\n constructor(opts: RestTransportOptions = {}) {\n const url = new URL(opts.baseUrl ?? \"https://api.zappie.ai/storefront/v1\");\n if (\n url.username ||\n url.password ||\n url.search ||\n url.hash ||\n (url.protocol !== \"https:\" &&\n !(\n url.protocol === \"http:\" &&\n [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(url.hostname)\n ))\n ) {\n throw new ZappieError(\n \"invalid_request\",\n \"REST baseUrl requires HTTPS, or HTTP on localhost\",\n );\n }\n this.baseUrl = url.href.replace(/\\/$/, \"\");\n // Called as this.fetcher(), a stored global fetch would run with the\n // transport as `this`, which Workers refuses (\"Illegal invocation\").\n this.fetcher =\n opts.fetch ?? ((input, init) => globalThis.fetch(input, init));\n }\n private async request(\n path: string,\n ctx: RequestCtx,\n etag?: string,\n body?: QuoteRequest,\n ): Promise<Response> {\n if (typeof window !== \"undefined\" && ctx.key.startsWith(\"sk_\")) {\n throw new ZappieError(\n \"invalid_request\",\n \"Secret keys cannot be sent from a browser\",\n );\n }\n for (let attempt = 0; ; attempt++) {\n throwIfAborted(ctx.signal);\n let response: Response;\n try {\n response = await this.fetcher(this.baseUrl + path, {\n method: body ? \"POST\" : \"GET\",\n ...(body ? { body: JSON.stringify(body) } : {}),\n // Workers reject redirect: \"error\"; a redirect is still refused below\n // as a non-OK response (a 3xx, or opaqueredirect in browsers).\n redirect: \"manual\",\n headers: {\n ...(body ? { \"Content-Type\": \"application/json\" } : {}),\n ...(ctx.idempotencyKey\n ? { \"Idempotency-Key\": ctx.idempotencyKey }\n : {}),\n Authorization: `Bearer ${ctx.key}`,\n \"Zappie-Version\": ctx.version,\n ...(etag ? { \"If-None-Match\": etag } : {}),\n },\n ...(ctx.signal ? { signal: ctx.signal } : {}),\n });\n } catch (error) {\n throwIfAborted(ctx.signal);\n if (attempt >= 2) {\n throw new ZappieError(\n \"network_error\",\n \"Could not reach the storefront service\",\n { cause: error },\n );\n }\n await delay(100 * 2 ** attempt + Math.random() * 100, ctx.signal);\n continue;\n }\n if (response.ok || response.status === 304) {\n return response;\n }\n const retry = response.headers.get(\"Retry-After\");\n const seconds = retry !== null ? Number(retry) : NaN;\n const wait =\n retry !== null\n ? Number.isFinite(seconds)\n ? Math.max(0, seconds * 1000)\n : Math.max(0, Date.parse(retry) - Date.now())\n : NaN;\n // A longer wait than this is the caller's decision, not a silently stalled read.\n const tooLong = Number.isFinite(wait) && wait > MAX_RETRY_WAIT_MS;\n if (\n attempt < 2 &&\n !tooLong &&\n (response.status === 429 || response.status >= 500)\n ) {\n await response.body?.cancel();\n await delay(\n Number.isFinite(wait)\n ? wait\n : 100 * 2 ** attempt + Math.random() * 100,\n ctx.signal,\n );\n continue;\n }\n const errorBody = await response.json().catch(() => ({}));\n throw wireError(\n errorBody,\n response.status,\n response.headers.get(\"Zappie-Request-Id\") ?? undefined,\n );\n }\n }\n private async json(response: Response): Promise<unknown> {\n try {\n return await response.json();\n } catch (cause) {\n if (cause instanceof Error && cause.name === \"AbortError\") {\n throw cause;\n }\n throw new ZappieError(\n \"server_error\",\n \"The storefront service returned invalid JSON\",\n { status: response.status, cause },\n );\n }\n }\n async quote(ctx: RequestCtx, body: QuoteRequest): Promise<WireQuote> {\n body = (await import(\"./quote-body\")).quoteBody(body);\n const request = {\n ...ctx,\n idempotencyKey:\n ctx.idempotencyKey ?? (await payloadKey(JSON.stringify(body))),\n };\n const raw = await this.json(\n await this.request(\"/quote\", request, undefined, body),\n );\n // The cart's quote client validates and maps the response.\n return raw as WireQuote;\n }\n async getStore(ctx: RequestCtx) {\n const mapper = import(\"./store\").catch((cause) => {\n throw new ZappieError(\n \"server_error\",\n \"Could not load the storefront store mapper\",\n { cause },\n );\n });\n const response = this.request(\"/store\", ctx).then((value) =>\n this.json(value),\n );\n const [{ mapStore }, raw] = await Promise.all([mapper, response]);\n return mapStore(\n raw,\n record,\n text,\n list,\n hours,\n mapMoney,\n money,\n opt,\n isString,\n isBoolean,\n mapSeo,\n );\n }\n async getMenu(\n ctx: RequestCtx & {\n branchId: string;\n ifNoneMatch?: string;\n metadataOnly?: boolean;\n fulfilment?: MenuFulfilment;\n },\n ): Promise<MenuResponse> {\n let cursor: string | undefined;\n const seen = new Set<string>();\n let output: ReturnType<typeof mapMenu> | undefined;\n let etag: string | undefined;\n do {\n const query = new URLSearchParams({\n branch: ctx.branchId,\n ...(ctx.fulfilment\n ? { fulfilment: wireFulfilment(ctx.fulfilment) }\n : {}),\n ...(ctx.metadataOnly ? { metadata_only: \"true\" } : {}),\n ...(cursor ? { cursor } : {}),\n });\n const response = await this.request(\n `/menus?${query}`,\n ctx,\n cursor ? undefined : ctx.ifNoneMatch,\n );\n if (response.status === 304) {\n if (cursor) {\n throw new ZappieError(\n \"server_error\",\n \"Unexpected 304 on a continuation page\",\n );\n }\n return {\n notModified: true,\n ...(response.headers.get(\"ETag\")\n ? { etag: response.headers.get(\"ETag\")! }\n : {}),\n };\n }\n const raw = record(await this.json(response));\n const page = mapMenu(raw, ctx.branchId);\n if (ctx.metadataOnly) {\n if (Object.keys(page.items).length || raw.next_cursor) {\n throw new ZappieError(\"server_error\", \"Invalid metadata response\");\n }\n return { menu: { ...page, completeness: \"partial\" } };\n }\n if (!output) {\n output = page;\n etag = response.headers.get(\"ETag\") ?? undefined;\n } else {\n if (page.version !== output.version) {\n throw new ZappieError(\n \"server_error\",\n \"Menu changed while fetching pages; retry the read\",\n );\n }\n Object.assign(output.items, page.items);\n for (const category of page.categories) {\n const existing = output.categories.find((c) => c.id === category.id);\n if (existing) {\n existing.itemIds = [\n ...new Set([...existing.itemIds, ...category.itemIds]),\n ];\n } else {\n output.categories.push(category);\n }\n }\n for (const section of page.menus ?? []) {\n const existing = output.menus?.find((m) => m.id === section.id);\n if (existing) {\n existing.categoryIds = [\n ...new Set([...existing.categoryIds, ...section.categoryIds]),\n ];\n } else {\n output.menus?.push(section);\n }\n }\n }\n cursor =\n typeof raw.next_cursor === \"string\" && raw.next_cursor\n ? raw.next_cursor\n : undefined;\n if (cursor) {\n if (seen.has(cursor)) {\n throw new ZappieError(\n \"server_error\",\n \"Repeated menu pagination cursor\",\n );\n }\n seen.add(cursor);\n }\n } while (cursor);\n if (output.menus && output.menus.length > 1) {\n output.id = `branch:${ctx.branchId}`;\n output.name = \"Catalogue\";\n delete output.schedule;\n }\n for (const category of output.categories) {\n for (const id of category.itemIds) {\n if (!Object.prototype.hasOwnProperty.call(output.items, id)) {\n throw new ZappieError(\n \"server_error\",\n \"Category refers to a missing item\",\n );\n }\n }\n }\n if (Object.keys(output.items).length > 2000) {\n console.warn(\"Zappie menu contains more than 2,000 items\");\n }\n return { menu: output, ...(etag ? { etag } : {}) };\n }\n async getSelectedMenu(ctx: SelectedMenuRequest) {\n return (await import(\"./selected-menu\")).selectedMenu(\n ctx,\n (path) => this.request(path, ctx, ctx.ifNoneMatch),\n (response) => this.json(response),\n mapMenu,\n () => new ZappieError(\"server_error\", \"Invalid selected menu\"),\n );\n }\n async search(ctx: SearchRequest) {\n return (await import(\"./search\")).searchPage(\n ctx,\n async (path) => this.json(await this.request(path, ctx)),\n mapMenu,\n () => new ZappieError(\"server_error\", \"Invalid search page\"),\n );\n }\n async getCategoryPage(\n ctx: RequestCtx & {\n branchId: string;\n menuId: string;\n categoryId: string;\n cursor?: string;\n },\n ) {\n const query = new URLSearchParams({\n branch: ctx.branchId,\n ...(ctx.cursor ? { cursor: ctx.cursor } : {}),\n });\n const raw = await this.json(\n await this.request(\n `/menus/${encodeURIComponent(ctx.menuId)}/categories/${encodeURIComponent(ctx.categoryId)}/items?${query}`,\n ctx,\n ),\n );\n // Loaded on first use: only progressive catalogues read pages.\n return (await import(\"./category-page\")).categoryPage(\n record(raw),\n ctx,\n mapMenu,\n () => new ZappieError(\"server_error\", \"Invalid category page\"),\n );\n }\n async getMenuVersion(\n ctx: RequestCtx & { branchId: string; fulfilment?: MenuFulfilment },\n ): Promise<MenuVersion> {\n const value = record(\n await this.json(\n await this.request(\n `/menus/version?${new URLSearchParams({\n branch: ctx.branchId,\n ...(ctx.fulfilment\n ? { fulfilment: wireFulfilment(ctx.fulfilment) }\n : {}),\n })}`,\n ctx,\n ),\n ),\n );\n if (\n typeof value.menu_version !== \"string\" ||\n typeof value.availability_hash !== \"string\"\n ) {\n throw new ZappieError(\"server_error\", \"Invalid menu version response\");\n }\n return {\n version: value.menu_version,\n availabilityHash: value.availability_hash,\n };\n }\n async getItem(ctx: RequestCtx & { branchId: string; itemId: string }) {\n const item = mapItem(\n await this.json(\n await this.request(\n `/items/${encodeURIComponent(ctx.itemId)}?${new URLSearchParams({ branch: ctx.branchId })}`,\n ctx,\n ),\n ),\n );\n if (item.id !== ctx.itemId) {\n throw new ZappieError(\n \"server_error\",\n \"Item response identity does not match the request\",\n );\n }\n return item;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { a as Menu } from '../models-
|
|
2
|
-
import {
|
|
1
|
+
import { a as Menu } from '../models-Rg5kQUkv.js';
|
|
2
|
+
import { j as SearchRequest, k as SearchPage } from '../client-Bm2cCYea.js';
|
|
3
|
+
import '../types-CuDvfW9g.js';
|
|
3
4
|
|
|
4
5
|
/** Loaded only when search is used; request authentication/retries stay in REST. */
|
|
5
6
|
declare function searchPage(ctx: SearchRequest, read: (path: string) => Promise<unknown>, mapMenu: (value: unknown, branchId: string) => Menu, invalid: () => Error): Promise<SearchPage>;
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export {
|
|
2
|
-
import '../models-
|
|
1
|
+
export { z as SelectedMenu, h as SelectedMenuRequest, i as SelectedMenuResponse, B as SelectedMenuTransport, D as createSelectedMenuReader, F as selectedMenu, G as selectedMenuKey } from '../client-Bm2cCYea.js';
|
|
2
|
+
import '../models-Rg5kQUkv.js';
|
|
3
|
+
import '../types-CuDvfW9g.js';
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { i as OpeningTime, M as Money, d as Currency, S as Store } from '../models-
|
|
1
|
+
import { i as OpeningTime, M as Money, d as Currency, S as Store } from '../models-Rg5kQUkv.js';
|
|
2
2
|
|
|
3
3
|
type Guard<T> = (value: unknown) => value is T;
|
|
4
4
|
type Optional = <K extends string, T>(key: K, value: unknown, is: Guard<T>) => {
|
|
5
5
|
[P in K]?: T;
|
|
6
6
|
};
|
|
7
7
|
/** Core parsing helpers are passed in, in this order, so this lazy entry has no runtime import. */
|
|
8
|
-
declare function mapStore(value: unknown, record: (value: unknown) => Record<string, unknown>, text: (value: unknown) => string, list: (value: unknown) => unknown[], hours: (value: unknown) => OpeningTime[], mapMoney: (value: unknown) => Money, money: (amountMinor: number, currency: Currency) => Money, opt: Optional, isString: Guard<string>, isBoolean: Guard<boolean>): Store;
|
|
8
|
+
declare function mapStore(value: unknown, record: (value: unknown) => Record<string, unknown>, text: (value: unknown) => string, list: (value: unknown) => unknown[], hours: (value: unknown) => OpeningTime[], mapMoney: (value: unknown) => Money, money: (amountMinor: number, currency: Currency) => Money, opt: Optional, isString: Guard<string>, isBoolean: Guard<boolean>, mapSeo: (value: unknown) => NonNullable<Store["seo"]>): Store;
|
|
9
9
|
|
|
10
10
|
export { mapStore };
|
package/dist/transport/store.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/transport/store.ts
|
|
2
|
-
function mapStore(value, record, text, list, hours, mapMoney, money, opt, isString, isBoolean) {
|
|
2
|
+
function mapStore(value, record, text, list, hours, mapMoney, money, opt, isString, isBoolean, mapSeo) {
|
|
3
3
|
const coordinates = (input) => {
|
|
4
4
|
const coordinates2 = record(input ?? {});
|
|
5
5
|
return typeof coordinates2.lat === "number" && typeof coordinates2.lng === "number" ? { lat: coordinates2.lat, lng: coordinates2.lng } : void 0;
|
|
@@ -27,6 +27,18 @@ function mapStore(value, record, text, list, hours, mapMoney, money, opt, isStri
|
|
|
27
27
|
const addressFields = branch.address && typeof branch.address === "object" ? record(branch.address) : {};
|
|
28
28
|
const branchCoordinates = coordinates(addressFields);
|
|
29
29
|
return {
|
|
30
|
+
...branch.currency !== void 0 ? { currency: money(0, text(branch.currency)).currency } : {},
|
|
31
|
+
...opt("country", addressFields.country, isString),
|
|
32
|
+
...branch.delivery_regions !== void 0 ? {
|
|
33
|
+
deliveryRegions: list(branch.delivery_regions).map((raw2) => {
|
|
34
|
+
const region = record(raw2);
|
|
35
|
+
return {
|
|
36
|
+
id: text(region.id),
|
|
37
|
+
name: text(region.name),
|
|
38
|
+
fee: mapMoney(region.fee)
|
|
39
|
+
};
|
|
40
|
+
})
|
|
41
|
+
} : {},
|
|
30
42
|
id: text(branch.id),
|
|
31
43
|
slug: text(branch.slug ?? branch.id),
|
|
32
44
|
name: text(branch.name),
|
|
@@ -96,6 +108,7 @@ function mapStore(value, record, text, list, hours, mapMoney, money, opt, isStri
|
|
|
96
108
|
}
|
|
97
109
|
const templateSettings = record(source.template_settings ?? {});
|
|
98
110
|
return {
|
|
111
|
+
...source.seo != null ? { seo: mapSeo(source.seo) } : {},
|
|
99
112
|
id: text(source.id),
|
|
100
113
|
slug: text(source.slug),
|
|
101
114
|
name: text(source.name),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/transport/store.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Branch, Fulfilment, OpeningTime, Store } from \"../models\";\nimport type { Currency, Money } from \"../money\";\n\ntype Guard<T> = (value: unknown) => value is T;\ntype Optional = <K extends string, T>(\n key: K,\n value: unknown,\n is: Guard<T>,\n) => { [P in K]?: T };\n\n/** Core parsing helpers are passed in, in this order, so this lazy entry has no runtime import. */\nexport function mapStore(\n value: unknown,\n record: (value: unknown) => Record<string, unknown>,\n text: (value: unknown) => string,\n list: (value: unknown) => unknown[],\n hours: (value: unknown) => OpeningTime[],\n mapMoney: (value: unknown) => Money,\n money: (amountMinor: number, currency: Currency) => Money,\n opt: Optional,\n isString: Guard<string>,\n isBoolean: Guard<boolean>,\n): Store {\n const coordinates = (input: unknown) => {\n const coordinates = record(input ?? {});\n return typeof coordinates.lat === \"number\" &&\n typeof coordinates.lng === \"number\"\n ? { lat: coordinates.lat, lng: coordinates.lng }\n : undefined;\n };\n const fulfilment = (input: unknown): Fulfilment => {\n const value = record(input);\n return {\n pickup: value.pickup === true,\n delivery: value.delivery === true,\n dineIn: value.dine_in === true,\n scheduling: value.scheduled === true,\n };\n };\n const source = record(value);\n const brand = record(source.brand ?? {});\n const branches: Branch[] = list(source.branches).map((raw) => {\n const branch = record(raw);\n const pickup = record(branch.pickup_address ?? {});\n const pickupCoordinates = coordinates(pickup.coordinates);\n const pickupAddress = {\n ...opt(\"placeId\", pickup.place_id, isString),\n ...(pickupCoordinates ? { coordinates: pickupCoordinates } : {}),\n };\n const address =\n typeof branch.address === \"string\"\n ? branch.address\n : branch.address\n ? text(record(branch.address).line1).trim()\n : undefined;\n const addressFields =\n branch.address && typeof branch.address === \"object\"\n ? record(branch.address)\n : {};\n const branchCoordinates = coordinates(addressFields);\n return {\n id: text(branch.id),\n slug: text(branch.slug ?? branch.id),\n name: text(branch.name),\n openingTimes: hours(branch.hours),\n isOpen: branch.is_open === true,\n fulfilment: fulfilment(branch.fulfilment),\n ...(address !== undefined ? { address } : {}),\n ...(isString(branch.phone) ? { phone: branch.phone.trim() } : {}),\n ...opt(\"timezone\", branch.timezone, isString),\n ...(branchCoordinates ? { coordinates: branchCoordinates } : {}),\n ...opt(\"closedUntil\", branch.closed_until, isString),\n ...opt(\"nextChangeAt\", branch.next_change_at, isString),\n ...opt(\"isDefault\", branch.is_default, isBoolean),\n ...(Object.keys(pickupAddress).length > 0 ? { pickupAddress } : {}),\n ...(branch.social_links !== undefined\n ? {\n socialLinks: list(branch.social_links).flatMap((rawLink) => {\n const link = record(rawLink);\n // Templates render these as links; only http(s) URLs are kept.\n try {\n const url = new URL(text(link.url));\n return isString(link.type) &&\n (url.protocol === \"https:\" || url.protocol === \"http:\")\n ? [{ type: link.type, url: url.href }]\n : [];\n } catch {\n return [];\n }\n }),\n }\n : {}),\n ...(branch.minimum_order\n ? { minimumOrder: mapMoney(branch.minimum_order) }\n : {}),\n ...(branch.special_hours\n ? {\n specialHours: list(branch.special_hours).map((rawHours) => {\n const special = record(rawHours);\n return {\n date: text(special.date),\n closed: special.closed === true,\n ...opt(\"opens\", special.open, isString),\n ...opt(\"closes\", special.close, isString),\n };\n }),\n }\n : {}),\n };\n });\n const allowed = [\n \"card\",\n \"bank_transfer\",\n \"wallet\",\n \"zap\",\n \"apple_pay\",\n \"google_pay\",\n ];\n const paymentMethods = list(source.payment_methods)\n .map(text)\n .filter((method) => {\n if (allowed.includes(method)) {\n return true;\n }\n console.warn(\n `Zappie payment method \"${method}\" is newer than this SDK and was skipped`,\n );\n return false;\n });\n const aggregate = {\n pickup: false,\n delivery: false,\n dineIn: false,\n scheduling: false,\n };\n for (const branch of branches) {\n for (const key of Object.keys(aggregate) as (keyof Fulfilment)[]) {\n aggregate[key] ||= branch.fulfilment[key];\n }\n }\n const templateSettings = record(source.template_settings ?? {}) as Record<\n string,\n string | number | boolean\n >;\n return {\n id: text(source.id),\n slug: text(source.slug),\n name: text(source.name),\n currency: money(0, text(source.currency) as Currency).currency,\n timezone: text(source.timezone),\n ...opt(\"description\", source.description, isString),\n ...(source.template_settings !== undefined ? { templateSettings } : {}),\n brand: {\n ...opt(\"logoUrl\", brand.logo_url, isString),\n ...opt(\"coverImageUrl\", brand.cover_image_url, isString),\n },\n fulfilment: source.fulfilment ? fulfilment(source.fulfilment) : aggregate,\n paymentMethods: paymentMethods as Store[\"paymentMethods\"],\n branches,\n };\n}\n"],
|
|
5
|
-
"mappings": ";AAWO,SAAS,SACd,OACA,QACA,MACA,MACA,OACA,UACA,OACA,KACA,UACA,
|
|
6
|
-
"names": ["coordinates", "value"]
|
|
4
|
+
"sourcesContent": ["import type { Branch, Fulfilment, OpeningTime, Store } from \"../models\";\nimport type { Currency, Money } from \"../money\";\n\ntype Guard<T> = (value: unknown) => value is T;\ntype Optional = <K extends string, T>(\n key: K,\n value: unknown,\n is: Guard<T>,\n) => { [P in K]?: T };\n\n/** Core parsing helpers are passed in, in this order, so this lazy entry has no runtime import. */\nexport function mapStore(\n value: unknown,\n record: (value: unknown) => Record<string, unknown>,\n text: (value: unknown) => string,\n list: (value: unknown) => unknown[],\n hours: (value: unknown) => OpeningTime[],\n mapMoney: (value: unknown) => Money,\n money: (amountMinor: number, currency: Currency) => Money,\n opt: Optional,\n isString: Guard<string>,\n isBoolean: Guard<boolean>,\n mapSeo: (value: unknown) => NonNullable<Store[\"seo\"]>,\n): Store {\n const coordinates = (input: unknown) => {\n const coordinates = record(input ?? {});\n return typeof coordinates.lat === \"number\" &&\n typeof coordinates.lng === \"number\"\n ? { lat: coordinates.lat, lng: coordinates.lng }\n : undefined;\n };\n const fulfilment = (input: unknown): Fulfilment => {\n const value = record(input);\n return {\n pickup: value.pickup === true,\n delivery: value.delivery === true,\n dineIn: value.dine_in === true,\n scheduling: value.scheduled === true,\n };\n };\n const source = record(value);\n const brand = record(source.brand ?? {});\n const branches: Branch[] = list(source.branches).map((raw) => {\n const branch = record(raw);\n const pickup = record(branch.pickup_address ?? {});\n const pickupCoordinates = coordinates(pickup.coordinates);\n const pickupAddress = {\n ...opt(\"placeId\", pickup.place_id, isString),\n ...(pickupCoordinates ? { coordinates: pickupCoordinates } : {}),\n };\n const address =\n typeof branch.address === \"string\"\n ? branch.address\n : branch.address\n ? text(record(branch.address).line1).trim()\n : undefined;\n const addressFields =\n branch.address && typeof branch.address === \"object\"\n ? record(branch.address)\n : {};\n const branchCoordinates = coordinates(addressFields);\n return {\n ...(branch.currency !== undefined\n ? { currency: money(0, text(branch.currency) as Currency).currency }\n : {}),\n ...opt(\"country\", addressFields.country, isString),\n ...(branch.delivery_regions !== undefined\n ? {\n deliveryRegions: list(branch.delivery_regions).map((raw) => {\n const region = record(raw);\n return {\n id: text(region.id),\n name: text(region.name),\n fee: mapMoney(region.fee),\n };\n }),\n }\n : {}),\n id: text(branch.id),\n slug: text(branch.slug ?? branch.id),\n name: text(branch.name),\n openingTimes: hours(branch.hours),\n isOpen: branch.is_open === true,\n fulfilment: fulfilment(branch.fulfilment),\n ...(address !== undefined ? { address } : {}),\n ...(isString(branch.phone) ? { phone: branch.phone.trim() } : {}),\n ...opt(\"timezone\", branch.timezone, isString),\n ...(branchCoordinates ? { coordinates: branchCoordinates } : {}),\n ...opt(\"closedUntil\", branch.closed_until, isString),\n ...opt(\"nextChangeAt\", branch.next_change_at, isString),\n ...opt(\"isDefault\", branch.is_default, isBoolean),\n ...(Object.keys(pickupAddress).length > 0 ? { pickupAddress } : {}),\n ...(branch.social_links !== undefined\n ? {\n socialLinks: list(branch.social_links).flatMap((rawLink) => {\n const link = record(rawLink);\n // Templates render these as links; only http(s) URLs are kept.\n try {\n const url = new URL(text(link.url));\n return isString(link.type) &&\n (url.protocol === \"https:\" || url.protocol === \"http:\")\n ? [{ type: link.type, url: url.href }]\n : [];\n } catch {\n return [];\n }\n }),\n }\n : {}),\n ...(branch.minimum_order\n ? { minimumOrder: mapMoney(branch.minimum_order) }\n : {}),\n ...(branch.special_hours\n ? {\n specialHours: list(branch.special_hours).map((rawHours) => {\n const special = record(rawHours);\n return {\n date: text(special.date),\n closed: special.closed === true,\n ...opt(\"opens\", special.open, isString),\n ...opt(\"closes\", special.close, isString),\n };\n }),\n }\n : {}),\n };\n });\n const allowed = [\n \"card\",\n \"bank_transfer\",\n \"wallet\",\n \"zap\",\n \"apple_pay\",\n \"google_pay\",\n ];\n const paymentMethods = list(source.payment_methods)\n .map(text)\n .filter((method) => {\n if (allowed.includes(method)) {\n return true;\n }\n console.warn(\n `Zappie payment method \"${method}\" is newer than this SDK and was skipped`,\n );\n return false;\n });\n const aggregate = {\n pickup: false,\n delivery: false,\n dineIn: false,\n scheduling: false,\n };\n for (const branch of branches) {\n for (const key of Object.keys(aggregate) as (keyof Fulfilment)[]) {\n aggregate[key] ||= branch.fulfilment[key];\n }\n }\n const templateSettings = record(source.template_settings ?? {}) as Record<\n string,\n string | number | boolean\n >;\n return {\n ...(source.seo != null ? { seo: mapSeo(source.seo) } : {}),\n id: text(source.id),\n slug: text(source.slug),\n name: text(source.name),\n currency: money(0, text(source.currency) as Currency).currency,\n timezone: text(source.timezone),\n ...opt(\"description\", source.description, isString),\n ...(source.template_settings !== undefined ? { templateSettings } : {}),\n brand: {\n ...opt(\"logoUrl\", brand.logo_url, isString),\n ...opt(\"coverImageUrl\", brand.cover_image_url, isString),\n },\n fulfilment: source.fulfilment ? fulfilment(source.fulfilment) : aggregate,\n paymentMethods: paymentMethods as Store[\"paymentMethods\"],\n branches,\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["coordinates", "value", "raw"]
|
|
7
7
|
}
|