@zappie/foundations 0.1.10 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/account/index.js.map +0 -1
  3. package/dist/branch-map/colors.js.map +0 -1
  4. package/dist/branch-map/delivery-map.js.map +0 -1
  5. package/dist/branch-map/delivery-runtime.js.map +0 -1
  6. package/dist/branch-map/fit.js.map +0 -1
  7. package/dist/branch-map/geometry.js.map +0 -1
  8. package/dist/branch-map/index.js.map +0 -1
  9. package/dist/branch-map/loader.js.map +0 -1
  10. package/dist/branch-map/markers.js.map +0 -1
  11. package/dist/branch-map/runtime.js.map +0 -1
  12. package/dist/branch-map/use-map-runtime.js.map +0 -1
  13. package/dist/cart/index.js.map +0 -1
  14. package/dist/deferred/index.js.map +0 -1
  15. package/dist/fulfilment/address-picker.js.map +0 -1
  16. package/dist/fulfilment/branch-picker.js.map +0 -1
  17. package/dist/fulfilment/schedule.js +13 -5
  18. package/dist/fulfilment/schedule.js.map +1 -2
  19. package/dist/fulfilment/switch.js.map +0 -1
  20. package/dist/index.d.ts +34 -5
  21. package/dist/index.js +4 -0
  22. package/dist/index.js.map +1 -2
  23. package/dist/internal/combobox.js.map +0 -1
  24. package/dist/internal/data-attrs.js.map +0 -1
  25. package/dist/internal/ids.js.map +0 -1
  26. package/dist/internal/slot.js.map +0 -1
  27. package/dist/internal/use-controllable-state.js.map +0 -1
  28. package/dist/item-form/index.js.map +0 -1
  29. package/dist/item-form/option-group.js.map +0 -1
  30. package/dist/item-form/options.js.map +0 -1
  31. package/dist/item-form/quantity.js.map +0 -1
  32. package/dist/item-form/root.js.map +0 -1
  33. package/dist/media/image.js.map +0 -1
  34. package/dist/money.js.map +0 -1
  35. package/dist/next.js.map +0 -1
  36. package/dist/otp/input.js.map +0 -1
  37. package/dist/overlay/engine.js.map +0 -1
  38. package/dist/overlay/history.js.map +0 -1
  39. package/dist/overlay/react.js.map +0 -1
  40. package/dist/payment/methods.js.map +0 -1
  41. package/dist/rich-text/banners.js.map +0 -1
  42. package/dist/rich-text/browser.js.map +0 -1
  43. package/dist/rich-text/core.js.map +0 -1
  44. package/dist/rich-text/index.js.map +0 -1
  45. package/dist/search/engine.js.map +0 -1
  46. package/dist/search/index.js.map +0 -1
  47. package/dist/status/contact.js +19 -0
  48. package/dist/status/contact.js.map +6 -0
  49. package/dist/status/hours.js +7 -1
  50. package/dist/status/hours.js.map +1 -2
  51. package/dist/status/open-status.js +4 -1
  52. package/dist/status/open-status.js.map +1 -2
  53. package/dist/version.js +1 -1
  54. package/dist/version.js.map +1 -2
  55. package/package.json +3 -3
@@ -0,0 +1,6 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/status/contact.ts"],
4
+ "mappings": ";AAcO,SAAS,aACd,QACe;AACf,QAAM,QAAuB,CAAC;AAC9B,QAAM,QAAQ,OAAO,OAAO,QAAQ,WAAW,EAAE;AACjD,MAAI,SAAS,KAAK,KAAK,KAAK,GAAG;AAC7B,UAAM,KAAK,EAAE,MAAM,SAAS,OAAO,QAAQ,MAAM,OAAO,KAAK,GAAG,CAAC;AAAA,EACnE;AACA,QAAM,UAAU,OAAO,aAAa;AAAA,IAClC,CAAC,SAAS,KAAK,KAAK,YAAY,MAAM;AAAA,EACxC;AACA,MAAI,SAAS;AACX,UAAM,KAAK,EAAE,MAAM,WAAW,OAAO,kBAAkB,MAAM,QAAQ,IAAI,CAAC;AAAA,EAC5E;AACA,SAAO;AACT;",
5
+ "names": []
6
+ }
@@ -142,14 +142,18 @@ function openingIntervals(branch, timezone, now, days = 8) {
142
142
  }
143
143
  return merged;
144
144
  }
145
+ function canOrderAhead(branch) {
146
+ return branch.fulfilment.scheduling && (branch.orderAheadDays === void 0 || branch.orderAheadDays > 0);
147
+ }
145
148
  function getOpenStatus(branch, timezone, now = /* @__PURE__ */ new Date()) {
146
149
  const intervals = openingIntervals(branch, timezone, now);
147
150
  const current = intervals.find(
148
151
  ({ start, end }) => start <= now.getTime() && end > now.getTime()
149
152
  );
150
153
  const next = current?.end ?? intervals.find(({ start }) => start > now.getTime())?.start;
154
+ const ahead = canOrderAhead(branch);
151
155
  if (next === void 0) {
152
- return { isOpen: false, label: "Closed" };
156
+ return { isOpen: false, canOrderAhead: ahead, label: "Closed" };
153
157
  }
154
158
  const nextChange = new Date(next);
155
159
  const local = localParts(nextChange, timezone);
@@ -160,11 +164,13 @@ function getOpenStatus(branch, timezone, now = /* @__PURE__ */ new Date()) {
160
164
  const suffix = local.date === today ? "" : local.date === dateOffset(today, 1) ? " tomorrow" : ` ${new Intl.DateTimeFormat("en", { timeZone: timezone, weekday: "long" }).format(nextChange)}`;
161
165
  return {
162
166
  isOpen: Boolean(current),
167
+ canOrderAhead: ahead,
163
168
  label: current ? `Open until ${clock}` : `Opens ${clock}${suffix}`,
164
169
  nextChange
165
170
  };
166
171
  }
167
172
  export {
173
+ canOrderAhead,
168
174
  dateOffset,
169
175
  formatOpeningHours,
170
176
  formatWallClock,
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/status/hours.ts"],
4
- "sourcesContent": ["import type { Branch, OpeningTime } from \"@zappie/sdk\";\nconst DAY = 86400000;\nconst formatters = new Map<string, Intl.DateTimeFormat>();\nfunction formatter(timezone: string) {\n let value = formatters.get(timezone);\n if (!value) {\n value = new Intl.DateTimeFormat(\"en-CA\", {\n timeZone: timezone,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n hourCycle: \"h23\",\n });\n formatters.set(timezone, value);\n }\n return value;\n}\nexport function localParts(date: Date, timezone: string) {\n const parts = Object.fromEntries(\n formatter(timezone)\n .formatToParts(date)\n .map(({ type, value }) => [type, value]),\n );\n return {\n date: `${parts.year}-${parts.month}-${parts.day}`,\n hour: Number(parts.hour),\n minute: Number(parts.minute),\n second: Number(parts.second),\n };\n}\n/**\n * Parse a zero-padded HH:mm or HH:mm:ss wall-clock value without applying a time zone.\n *\n * @returns Clock parts with 12-hour and 24-hour values, or undefined for an invalid time.\n * @example\n * ```ts\n * parseWallClock(\"14:30\"); // { hour24: 14, hour12: 2, minute: 30, second: 0, period: \"PM\" }\n * ```\n */\nexport function parseWallClock(value: string) {\n const match = /^(\\d{2}):(\\d{2})(?::(\\d{2}))?$/.exec(value);\n if (\n !match ||\n Number(match[1]) > 23 ||\n Number(match[2]) > 59 ||\n Number(match[3] ?? 0) > 59\n ) {\n return undefined;\n }\n const hour24 = Number(match[1]);\n return {\n hour24,\n hour12: hour24 % 12 || 12,\n minute: Number(match[2]),\n second: Number(match[3] ?? 0),\n period: hour24 >= 12 ? \"PM\" : \"AM\",\n };\n}\n/**\n * Format a valid wall-clock time as a 12-hour label with AM or PM.\n * Omits zero minutes and all seconds. Returns invalid input unchanged.\n *\n * @example\n * ```ts\n * formatWallClock(\"14:00\"); // \"2 PM\"\n * formatWallClock(\"09:30:15\"); // \"9:30 AM\"\n * ```\n */\nexport function formatWallClock(value: string) {\n const time = parseWallClock(value);\n return time\n ? `${time.hour12}${time.minute ? `:${String(time.minute).padStart(2, \"0\")}` : \"\"} ${time.period}`\n : value;\n}\n/**\n * Group consecutive days with identical opening intervals into display labels.\n * Skips disabled intervals and days with no enabled hours, and orders the groups from Sunday to Saturday.\n */\nexport function formatOpeningHours(times: OpeningTime[]) {\n const names = [\n \"Sunday\",\n \"Monday\",\n \"Tuesday\",\n \"Wednesday\",\n \"Thursday\",\n \"Friday\",\n \"Saturday\",\n ];\n const groups: { days: number[]; dayLabel: string; hoursLabel: string }[] = [];\n for (let day = 0; day < 7; day++) {\n const intervals = times\n .filter((time) => !time.disabled && time.day === day)\n .sort((a, b) => a.opens.localeCompare(b.opens));\n if (!intervals.length) {\n continue;\n }\n const hoursLabel = intervals\n .map(\n (time) =>\n `${formatWallClock(time.opens)} \u2013 ${formatWallClock(time.closes)}`,\n )\n .join(\", \");\n const last = groups[groups.length - 1];\n if (\n last &&\n last.days[last.days.length - 1] === day - 1 &&\n last.hoursLabel === hoursLabel\n ) {\n last.days.push(day);\n last.dayLabel = `${names[last.days[0]!]} \u2013 ${names[day]}`;\n } else {\n groups.push({ days: [day], dayLabel: names[day]!, hoursLabel });\n }\n }\n return groups;\n}\nexport function dateOffset(date: string, days: number) {\n return new Date(Date.parse(`${date}T00:00:00Z`) + days * DAY)\n .toISOString()\n .slice(0, 10);\n}\n/** Return all matching instants (two at a DST fold, none in a DST gap). */\nexport function wallInstants(date: string, clock: string, timezone: string) {\n const time = parseWallClock(clock);\n if (!time) {\n return [];\n }\n const wall =\n Date.parse(`${date}T00:00:00Z`) +\n ((time.hour24 * 60 + time.minute) * 60 + time.second) * 1000;\n const offsets = new Set<number>();\n for (const shift of [-DAY, 0, DAY]) {\n const sample = wall + shift;\n const local = localParts(new Date(sample), timezone);\n const asUTC =\n Date.parse(`${local.date}T00:00:00Z`) +\n ((local.hour * 60 + local.minute) * 60 + local.second) * 1000;\n offsets.add(asUTC - sample);\n }\n const matches = [];\n for (const offset of offsets) {\n const instant = wall - offset;\n const local = localParts(new Date(instant), timezone);\n if (\n local.date === date &&\n local.hour === time.hour24 &&\n local.minute === time.minute &&\n local.second === time.second\n ) {\n matches.push(instant);\n }\n }\n return matches.sort((a, b) => a - b);\n}\nexport type OpeningInterval = { start: number; end: number };\nexport function openingIntervals(\n branch: Branch,\n timezone: string,\n now: Date,\n days = 8,\n): OpeningInterval[] {\n const today = localParts(now, timezone).date;\n const intervals: OpeningInterval[] = [];\n for (let offset = -1; offset < days; offset++) {\n const date = dateOffset(today, offset);\n const day = new Date(`${date}T00:00:00Z`).getUTCDay();\n const special = branch.specialHours?.find((hours) => hours.date === date);\n const times = special\n ? special.closed || !special.opens || !special.closes\n ? []\n : [{ opens: special.opens, closes: special.closes }]\n : branch.openingTimes.filter(\n (hours) => hours.day === day && !hours.disabled,\n );\n for (const hours of times) {\n const starts = wallInstants(date, hours.opens, timezone);\n const overnight = hours.closes <= hours.opens;\n const endDate = overnight ? dateOffset(date, 1) : date;\n const ends = wallInstants(endDate, hours.closes, timezone);\n const start = starts[0];\n let end = ends[ends.length - 1];\n if (\n overnight &&\n branch.specialHours?.some(\n (hours) => hours.date === endDate && hours.closed,\n )\n ) {\n end = wallInstants(endDate, \"00:00\", timezone)[0];\n }\n if (start !== undefined && end !== undefined && start < end) {\n intervals.push({ start, end });\n }\n }\n }\n const closedUntil = branch.closedUntil\n ? Date.parse(branch.closedUntil)\n : -Infinity;\n const sorted = intervals\n .map(({ start, end }) => ({ start: Math.max(start, closedUntil), end }))\n .filter(({ start, end }) => start < end)\n .sort((a, b) => a.start - b.start);\n const merged: OpeningInterval[] = [];\n for (const interval of sorted) {\n const last = merged[merged.length - 1];\n if (last && interval.start <= last.end) {\n last.end = Math.max(last.end, interval.end);\n } else {\n merged.push({ ...interval });\n }\n }\n return merged;\n}\nexport type OpenStatusState = {\n isOpen: boolean;\n label: string;\n nextChange?: Date;\n};\n/**\n * Calculate whether a branch is open and provide its next opening or closing label.\n * Accounts for weekly hours, special hours, overnight intervals and closedUntil.\n *\n * @param branch - The branch whose opening rules apply.\n * @param timezone - The time zone used to interpret and display its hours.\n * @param now - The instant to check. Defaults to the current time.\n * @returns Open state, a display label and the next boundary found in the opening-hours window.\n */\nexport function getOpenStatus(\n branch: Branch,\n timezone: string,\n now = new Date(),\n): OpenStatusState {\n const intervals = openingIntervals(branch, timezone, now);\n const current = intervals.find(\n ({ start, end }) => start <= now.getTime() && end > now.getTime(),\n );\n const next =\n current?.end ?? intervals.find(({ start }) => start > now.getTime())?.start;\n if (next === undefined) {\n return { isOpen: false, label: \"Closed\" };\n }\n const nextChange = new Date(next);\n const local = localParts(nextChange, timezone);\n const clock = formatWallClock(\n `${String(local.hour).padStart(2, \"0\")}:${String(local.minute).padStart(2, \"0\")}`,\n )\n .toLowerCase()\n .replace(\" \", \"\");\n const today = localParts(now, timezone).date;\n const suffix =\n local.date === today\n ? \"\"\n : local.date === dateOffset(today, 1)\n ? \" tomorrow\"\n : ` ${new Intl.DateTimeFormat(\"en\", { timeZone: timezone, weekday: \"long\" }).format(nextChange)}`;\n return {\n isOpen: Boolean(current),\n label: current ? `Open until ${clock}` : `Opens ${clock}${suffix}`,\n nextChange,\n };\n}\n"],
5
- "mappings": "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",
4
+ "mappings": "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",
6
5
  "names": ["hours"]
7
6
  }
@@ -34,7 +34,10 @@ var OpenStatus = forwardRef(function OpenStatus2({ branch, timezone, ...props },
34
34
  "span",
35
35
  props,
36
36
  state,
37
- { "data-state": state.isOpen ? "open" : "closed" },
37
+ {
38
+ "data-state": state.isOpen ? "open" : "closed",
39
+ "data-order-ahead": state.canOrderAhead ? "" : void 0
40
+ },
38
41
  ref
39
42
  );
40
43
  });
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/status/open-status.tsx"],
4
- "sourcesContent": ["\"use client\";\nimport type { Branch } from \"@zappie/sdk\";\nimport { useStore } from \"@zappie/sdk/react\";\nimport { forwardRef, useEffect, useState } from \"react\";\nimport { part, type PartProps } from \"../internal/slot\";\nimport { getOpenStatus, type OpenStatusState } from \"./hours\";\nexport function useOpenStatus(branch: Branch, timezone: string) {\n const [now, setNow] = useState(() => new Date());\n const state = getOpenStatus(branch, timezone, now);\n const boundary = state.nextChange?.getTime();\n useEffect(() => {\n // With no weekly boundary, retry at the next day for future calendar exceptions.\n const delay = boundary !== undefined ? boundary - Date.now() : 86400000;\n const timeout = setTimeout(\n () => setNow(new Date()),\n Math.max(1, Math.min(2147483647, delay)),\n );\n const refresh = () => setNow(new Date());\n window.addEventListener(\"focus\", refresh);\n return () => {\n clearTimeout(timeout);\n window.removeEventListener(\"focus\", refresh);\n };\n }, [branch, timezone, boundary]);\n return state;\n}\n/**\n * Expose a branch opening label and open/closed state in a span or your render function.\n * Uses your time zone override, then the branch, store or UTC, and refreshes at boundaries and window focus.\n */\nexport const OpenStatus = forwardRef<\n HTMLElement,\n PartProps<OpenStatusState> & { branch: Branch; timezone?: string }\n>(function OpenStatus({ branch, timezone, ...props }, ref) {\n const store = useStore();\n const state = useOpenStatus(\n branch,\n timezone ?? branch.timezone ?? store.data?.timezone ?? \"UTC\",\n );\n return part(\n \"span\",\n props,\n state,\n { \"data-state\": state.isOpen ? \"open\" : \"closed\" },\n ref,\n );\n});\n"],
5
- "mappings": ";;;AAEA,SAAS,gBAAgB;AACzB,SAAS,YAAY,WAAW,gBAAgB;AAChD,SAAS,YAA4B;AACrC,SAAS,qBAA2C;AAC7C,SAAS,cAAc,QAAgB,UAAkB;AAC9D,QAAM,CAAC,KAAK,MAAM,IAAI,SAAS,MAAM,oBAAI,KAAK,CAAC;AAC/C,QAAM,QAAQ,cAAc,QAAQ,UAAU,GAAG;AACjD,QAAM,WAAW,MAAM,YAAY,QAAQ;AAC3C,YAAU,MAAM;AAEd,UAAM,QAAQ,aAAa,SAAY,WAAW,KAAK,IAAI,IAAI;AAC/D,UAAM,UAAU;AAAA,MACd,MAAM,OAAO,oBAAI,KAAK,CAAC;AAAA,MACvB,KAAK,IAAI,GAAG,KAAK,IAAI,YAAY,KAAK,CAAC;AAAA,IACzC;AACA,UAAM,UAAU,MAAM,OAAO,oBAAI,KAAK,CAAC;AACvC,WAAO,iBAAiB,SAAS,OAAO;AACxC,WAAO,MAAM;AACX,mBAAa,OAAO;AACpB,aAAO,oBAAoB,SAAS,OAAO;AAAA,IAC7C;AAAA,EACF,GAAG,CAAC,QAAQ,UAAU,QAAQ,CAAC;AAC/B,SAAO;AACT;AAKO,IAAM,aAAa,WAGxB,SAASA,YAAW,EAAE,QAAQ,UAAU,GAAG,MAAM,GAAG,KAAK;AACzD,QAAM,QAAQ,SAAS;AACvB,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA,YAAY,OAAO,YAAY,MAAM,MAAM,YAAY;AAAA,EACzD;AACA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,EAAE,cAAc,MAAM,SAAS,SAAS,SAAS;AAAA,IACjD;AAAA,EACF;AACF,CAAC;",
4
+ "mappings": ";;;AAEA,SAAS,gBAAgB;AACzB,SAAS,YAAY,WAAW,gBAAgB;AAChD,SAAS,YAA4B;AACrC,SAAS,qBAA2C;AAC7C,SAAS,cAAc,QAAgB,UAAkB;AAC9D,QAAM,CAAC,KAAK,MAAM,IAAI,SAAS,MAAM,oBAAI,KAAK,CAAC;AAC/C,QAAM,QAAQ,cAAc,QAAQ,UAAU,GAAG;AACjD,QAAM,WAAW,MAAM,YAAY,QAAQ;AAC3C,YAAU,MAAM;AAEd,UAAM,QAAQ,aAAa,SAAY,WAAW,KAAK,IAAI,IAAI;AAC/D,UAAM,UAAU;AAAA,MACd,MAAM,OAAO,oBAAI,KAAK,CAAC;AAAA,MACvB,KAAK,IAAI,GAAG,KAAK,IAAI,YAAY,KAAK,CAAC;AAAA,IACzC;AACA,UAAM,UAAU,MAAM,OAAO,oBAAI,KAAK,CAAC;AACvC,WAAO,iBAAiB,SAAS,OAAO;AACxC,WAAO,MAAM;AACX,mBAAa,OAAO;AACpB,aAAO,oBAAoB,SAAS,OAAO;AAAA,IAC7C;AAAA,EACF,GAAG,CAAC,QAAQ,UAAU,QAAQ,CAAC;AAC/B,SAAO;AACT;AAOO,IAAM,aAAa,WAGxB,SAASA,YAAW,EAAE,QAAQ,UAAU,GAAG,MAAM,GAAG,KAAK;AACzD,QAAM,QAAQ,SAAS;AACvB,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA,YAAY,OAAO,YAAY,MAAM,MAAM,YAAY;AAAA,EACzD;AACA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,MACE,cAAc,MAAM,SAAS,SAAS;AAAA,MACtC,oBAAoB,MAAM,gBAAgB,KAAK;AAAA,IACjD;AAAA,IACA;AAAA,EACF;AACF,CAAC;",
6
5
  "names": ["OpenStatus"]
7
6
  }
package/dist/version.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/version.ts
2
- var PACKAGE_VERSION = true ? "0.1.10" : "0.0.0-development";
2
+ var PACKAGE_VERSION = true ? "0.2.0" : "0.0.0-development";
3
3
  export {
4
4
  PACKAGE_VERSION
5
5
  };
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/version.ts"],
4
- "sourcesContent": ["// Builds replace __ZAPPIE_FOUNDATIONS_VERSION__ with the version in package.json. Code run straight\n// from source, such as a plain Node script, has no build step and gets the\n// development label instead.\ndeclare const __ZAPPIE_FOUNDATIONS_VERSION__: string | undefined;\n\n/** The installed @zappie/foundations version. */\nexport const PACKAGE_VERSION: string =\n typeof __ZAPPIE_FOUNDATIONS_VERSION__ === \"string\"\n ? __ZAPPIE_FOUNDATIONS_VERSION__\n : \"0.0.0-development\";\n"],
5
- "mappings": ";AAMO,IAAM,kBACX,OACI,WACA;",
4
+ "mappings": ";AAMO,IAAM,kBACX,OACI,UACA;",
6
5
  "names": []
7
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zappie/foundations",
3
- "version": "0.1.10",
3
+ "version": "0.2.0",
4
4
  "description": "UI-less React primitives for Zappie storefronts: behaviour, state and accessibility without styles.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -33,7 +33,7 @@
33
33
  "dependencies": {
34
34
  "@base-ui/react": "^1.9.0",
35
35
  "@googlemaps/markerclusterer": "^2.6.2",
36
- "@zappie/sdk": "^0.1.24"
36
+ "@zappie/sdk": "^0.2.1"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "react": "^18.2.0 || ^19.0.0",
@@ -54,7 +54,7 @@
54
54
  "url": "https://github.com/zappie/storefront-packages/issues"
55
55
  },
56
56
  "scripts": {
57
- "build": "tsup && node ../../scripts/release-packages/build-modules.mjs",
57
+ "build": "tsup && node ../../scripts/release-packages/build-modules.mjs --no-sources-content",
58
58
  "size": "node scripts/check-size.mjs"
59
59
  }
60
60
  }