@cimplify/sdk 0.92.0 → 0.93.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 (59) hide show
  1. package/dist/advanced.d.mts +3 -3
  2. package/dist/advanced.d.ts +3 -3
  3. package/dist/advanced.js +20 -20
  4. package/dist/advanced.mjs +1 -1
  5. package/dist/{chunk-OAHSF542.js → chunk-6Y7KC5YZ.js} +11 -0
  6. package/dist/{chunk-3MBMIEWT.js → chunk-AWNWP5J3.js} +4 -4
  7. package/dist/{chunk-3XD2FVP4.mjs → chunk-FKARGZK3.mjs} +195 -60
  8. package/dist/{chunk-DCRN67NO.js → chunk-QLGZIBRO.js} +237 -102
  9. package/dist/{chunk-54H2YYIW.mjs → chunk-QXWX7CD2.mjs} +2 -2
  10. package/dist/{chunk-IVJEUGWF.js → chunk-VEOAMBEX.js} +1 -3
  11. package/dist/{chunk-T7LDBZZZ.mjs → chunk-VUSHG3HA.mjs} +1 -3
  12. package/dist/{chunk-PYBFTMHI.mjs → chunk-X36FZZT6.mjs} +11 -0
  13. package/dist/{client-CeW3acve.d.ts → client-BTL9d_nN.d.mts} +29 -2
  14. package/dist/{client-BQbSKlHS.d.mts → client-BX30FAWZ.d.ts} +29 -2
  15. package/dist/{client-Bw4HtLP1.d.mts → client-Bhe2TnFW.d.mts} +2 -2
  16. package/dist/{client-Otzv_rgP.d.ts → client-DrkuEK_I.d.ts} +2 -2
  17. package/dist/{index-CmIboQjl.d.mts → index-BuNjyfaV.d.mts} +3 -3
  18. package/dist/{index-DgoRwz8l.d.ts → index-D-w5h82t.d.ts} +3 -3
  19. package/dist/index.d.mts +7 -7
  20. package/dist/index.d.ts +7 -7
  21. package/dist/index.js +69 -69
  22. package/dist/index.mjs +3 -3
  23. package/dist/mock/cli.mjs +11 -0
  24. package/dist/mock/library.js +11 -0
  25. package/dist/mock/library.mjs +11 -0
  26. package/dist/mock/msw.js +11 -0
  27. package/dist/mock/msw.mjs +11 -0
  28. package/dist/{payment-Cq8FT1B6.d.ts → payment-By0JidTe.d.ts} +1 -1
  29. package/dist/{payment-BFtLqiG2.d.mts → payment-pKPpaORI.d.mts} +1 -1
  30. package/dist/{price-DaFXT-Lr.d.ts → price-23RNXftz.d.ts} +1 -1
  31. package/dist/{price-DleDWk_s.d.mts → price-BvRtp6ak.d.mts} +1 -1
  32. package/dist/{product-bZ8G5Z8D.d.mts → product-O8-wcHSe.d.mts} +11 -0
  33. package/dist/{product-bZ8G5Z8D.d.ts → product-O8-wcHSe.d.ts} +11 -0
  34. package/dist/react.d.mts +13 -5
  35. package/dist/react.d.ts +13 -5
  36. package/dist/react.js +174 -75
  37. package/dist/react.mjs +156 -58
  38. package/dist/{server-D-iK-bNt.d.mts → server-BN9ZCAKT.d.mts} +5 -1
  39. package/dist/{server-DwsY9hlX.d.ts → server-DDGbfW_S.d.ts} +5 -1
  40. package/dist/server.d.mts +4 -4
  41. package/dist/server.d.ts +4 -4
  42. package/dist/server.js +7 -7
  43. package/dist/server.mjs +2 -2
  44. package/dist/testing/msw.d.mts +2 -2
  45. package/dist/testing/msw.d.ts +2 -2
  46. package/dist/testing/msw.js +2 -2
  47. package/dist/testing/msw.mjs +1 -1
  48. package/dist/testing/suite.d.mts +6 -6
  49. package/dist/testing/suite.d.ts +6 -6
  50. package/dist/testing/suite.js +23 -23
  51. package/dist/testing/suite.mjs +4 -4
  52. package/dist/testing.d.mts +5 -5
  53. package/dist/testing.d.ts +5 -5
  54. package/dist/testing.js +79 -79
  55. package/dist/testing.mjs +5 -5
  56. package/dist/utils.d.mts +4 -4
  57. package/dist/utils.d.ts +4 -4
  58. package/package.json +1 -1
  59. package/registry/chat-widget.json +1 -1
package/dist/react.mjs CHANGED
@@ -3,10 +3,10 @@ import { DURATION_UNIT, getVariantDisplayName, INPUT_FIELD_TYPE, isMultiDayServi
3
3
  export { getVariantDisplayName } from './chunk-RRBY35TJ.mjs';
4
4
  import { messagePreview } from './chunk-U4MW6PYU.mjs';
5
5
  export { messagePreview, splitGenUIContent } from './chunk-U4MW6PYU.mjs';
6
- import { SESSION_CLAIMED, SESSION_CLEARED, createCimplifyClient, MUTATION_ERROR } from './chunk-3XD2FVP4.mjs';
7
- import { ELEMENT_TYPES, EVENT_TYPES, startSignIn } from './chunk-T7LDBZZZ.mjs';
6
+ import { SESSION_CLAIMED, SESSION_CLEARED, createCimplifyClient, MUTATION_ERROR } from './chunk-FKARGZK3.mjs';
7
+ import { generateIdempotencyKey, ELEMENT_TYPES, EVENT_TYPES, startSignIn } from './chunk-VUSHG3HA.mjs';
8
8
  import { parsePrice, formatMoney, formatPrice, getProductCurrency, getUnitPriceAtQuantity, isOnSale, getDiscountPercentage, getBasePrice, getPriceRange, formatPriceRange } from './chunk-KFLGQGMC.mjs';
9
- import { isSupportedCurrency } from './chunk-K5EN2W2Q.mjs';
9
+ import { isSupportedCurrency, CimplifyError } from './chunk-K5EN2W2Q.mjs';
10
10
  import './chunk-3G6RQLXK.mjs';
11
11
  import React14, { createContext, lazy, memo, Suspense, useContext, useMemo, useRef, useState, useCallback, useEffect, useId, useSyncExternalStore } from 'react';
12
12
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
@@ -5889,6 +5889,20 @@ var IDLE_TIMEOUT_MS = 6e4;
5889
5889
  var WS_MAX_RECONNECT_ATTEMPTS = 10;
5890
5890
  var WS_PATH = "/api/v1/support/conversation/ws";
5891
5891
  var AWAIT_REPLY_TIMEOUT_MS = 3e4;
5892
+ function mergeMessages(prev, incoming) {
5893
+ const ids = new Set(prev.map((m) => m.id));
5894
+ const fresh = incoming.filter((m) => !ids.has(m.id));
5895
+ if (fresh.length === 0) return prev;
5896
+ const merged = [...prev, ...fresh].sort(
5897
+ (a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime()
5898
+ );
5899
+ const confirmed = new Set(
5900
+ merged.filter((m) => !m.id.startsWith("opt_") && m.client_id).map((m) => m.client_id)
5901
+ );
5902
+ return merged.filter(
5903
+ (m) => !(m.id.startsWith("opt_") && m.client_id && confirmed.has(m.client_id))
5904
+ );
5905
+ }
5892
5906
  function useChat(options = {}) {
5893
5907
  const context = useOptionalCimplifyClient();
5894
5908
  const client = options.client ?? context?.client;
@@ -5941,18 +5955,22 @@ function useChat(options = {}) {
5941
5955
  if (incoming.some((m) => m.sender_type === "bot" || m.sender_type === "agent")) {
5942
5956
  stopAwaitingReply();
5943
5957
  }
5944
- setMessages((prev) => {
5945
- const ids = new Set(prev.map((m) => m.id));
5946
- const fresh = incoming.filter((m) => !ids.has(m.id));
5947
- return fresh.length > 0 ? [...prev, ...fresh] : prev;
5948
- });
5958
+ if (incoming.length > 0) lastActivityRef.current = Date.now();
5959
+ setMessages((prev) => mergeMessages(prev, incoming));
5949
5960
  },
5950
5961
  [stopAwaitingReply]
5951
5962
  );
5963
+ const lastConfirmedMessage = useCallback(() => {
5964
+ const list = messagesRef.current;
5965
+ for (let i = list.length - 1; i >= 0; i--) {
5966
+ if (!list[i].id.startsWith("opt_")) return list[i];
5967
+ }
5968
+ return void 0;
5969
+ }, []);
5952
5970
  const fetchNewMessages = useCallback(async () => {
5953
5971
  const c = clientRef.current;
5954
5972
  if (!c) return;
5955
- const last = messagesRef.current[messagesRef.current.length - 1];
5973
+ const last = lastConfirmedMessage();
5956
5974
  const result = await c.support.getMessages({
5957
5975
  after: last?.created_at,
5958
5976
  limit: 50
@@ -5960,16 +5978,23 @@ function useChat(options = {}) {
5960
5978
  if (result.ok && result.value.length > 0) {
5961
5979
  appendMessages(result.value);
5962
5980
  }
5963
- }, [appendMessages]);
5964
- const pollMessages = useCallback(async () => {
5965
- if (Date.now() - lastActivityRef.current > IDLE_TIMEOUT_MS) return;
5966
- await fetchNewMessages();
5967
- }, [fetchNewMessages]);
5981
+ }, [appendMessages, lastConfirmedMessage]);
5968
5982
  const startPolling = useCallback(() => {
5969
5983
  if (pollRef.current) clearInterval(pollRef.current);
5970
- const interval = Date.now() - lastActivityRef.current > IDLE_TIMEOUT_MS ? pollIdleInterval : pollInterval;
5971
- pollRef.current = setInterval(pollMessages, interval);
5972
- }, [pollMessages, pollInterval, pollIdleInterval]);
5984
+ const idle = () => Date.now() - lastActivityRef.current > IDLE_TIMEOUT_MS;
5985
+ let current = idle() ? pollIdleInterval : pollInterval;
5986
+ const tick = () => {
5987
+ void fetchNewMessages().then(() => {
5988
+ const desired = idle() ? pollIdleInterval : pollInterval;
5989
+ if (desired !== current && pollRef.current) {
5990
+ current = desired;
5991
+ clearInterval(pollRef.current);
5992
+ pollRef.current = setInterval(tick, current);
5993
+ }
5994
+ });
5995
+ };
5996
+ pollRef.current = setInterval(tick, current);
5997
+ }, [fetchNewMessages, pollInterval, pollIdleInterval]);
5973
5998
  const stopPolling = useCallback(() => {
5974
5999
  if (pollRef.current) {
5975
6000
  clearInterval(pollRef.current);
@@ -5981,7 +6006,15 @@ function useChat(options = {}) {
5981
6006
  if (!c || !openedRef.current) return;
5982
6007
  const result = await c.support.openConversation();
5983
6008
  if (!result.ok) return;
5984
- setMessages(result.value.messages);
6009
+ const snapshot = result.value.messages;
6010
+ setMessages((prev) => {
6011
+ const snapshotIds = new Set(snapshot.map((m) => m.id));
6012
+ const tail = snapshot.length > 0 ? new Date(snapshot[snapshot.length - 1].created_at).getTime() : 0;
6013
+ const extras = prev.filter(
6014
+ (m) => !snapshotIds.has(m.id) && (m.id.startsWith("opt_") || new Date(m.created_at).getTime() > tail)
6015
+ );
6016
+ return mergeMessages(snapshot, extras);
6017
+ });
5985
6018
  aiEnabledRef.current = result.value.conversation.ai_enabled;
5986
6019
  setHumanRequested(result.value.conversation.needs_human_review ?? false);
5987
6020
  setSeenAt(result.value.conversation.team_read_at ?? null);
@@ -5993,7 +6026,13 @@ function useChat(options = {}) {
5993
6026
  startPolling();
5994
6027
  return;
5995
6028
  }
6029
+ if (wsReconnectTimerRef.current) {
6030
+ clearTimeout(wsReconnectTimerRef.current);
6031
+ wsReconnectTimerRef.current = null;
6032
+ }
5996
6033
  if (wsRef.current) {
6034
+ wsRef.current.onclose = null;
6035
+ wsRef.current.onmessage = null;
5997
6036
  wsRef.current.close();
5998
6037
  wsRef.current = null;
5999
6038
  }
@@ -6017,7 +6056,7 @@ function useChat(options = {}) {
6017
6056
  else stopAwaitingReply();
6018
6057
  } else if (envelope.type === "resync") {
6019
6058
  const latest = envelope.latest_message_at;
6020
- const last = messagesRef.current[messagesRef.current.length - 1];
6059
+ const last = lastConfirmedMessage();
6021
6060
  if (latest && (!last || new Date(last.created_at).getTime() < new Date(latest).getTime())) {
6022
6061
  fetchNewMessages();
6023
6062
  }
@@ -6034,6 +6073,7 @@ function useChat(options = {}) {
6034
6073
  if (wsReconnectAttemptRef.current < WS_MAX_RECONNECT_ATTEMPTS) {
6035
6074
  const attempt = wsReconnectAttemptRef.current++;
6036
6075
  const delay = Math.min(1e3 * Math.pow(2, attempt) + Math.random() * 500, 3e4);
6076
+ if (wsReconnectTimerRef.current) clearTimeout(wsReconnectTimerRef.current);
6037
6077
  wsReconnectTimerRef.current = setTimeout(connectWebSocket, delay);
6038
6078
  } else {
6039
6079
  startPolling();
@@ -6042,7 +6082,7 @@ function useChat(options = {}) {
6042
6082
  ws.onerror = () => {
6043
6083
  };
6044
6084
  wsRef.current = ws;
6045
- }, [appendMessages, startPolling, stopPolling, fetchNewMessages, resyncConversation]);
6085
+ }, [appendMessages, startPolling, stopPolling, fetchNewMessages, resyncConversation, lastConfirmedMessage]);
6046
6086
  const resetConversation = useCallback(() => {
6047
6087
  stopPolling();
6048
6088
  if (wsReconnectTimerRef.current) {
@@ -6065,12 +6105,42 @@ function useChat(options = {}) {
6065
6105
  setError(null);
6066
6106
  setClaimedAs(null);
6067
6107
  }, [stopPolling, stopAwaitingReply]);
6108
+ const hydrateConversation = useCallback(
6109
+ (value) => {
6110
+ const c = clientRef.current;
6111
+ setMessages(value.messages);
6112
+ setConversationOpened(true);
6113
+ aiEnabledRef.current = value.conversation.ai_enabled;
6114
+ setHumanRequested(value.conversation.needs_human_review ?? false);
6115
+ setSeenAt(value.conversation.team_read_at ?? null);
6116
+ setMayHaveOlder(value.messages.length >= 50);
6117
+ setClaimedAs(c?.support.claimedCustomerName ?? null);
6118
+ connectWebSocket();
6119
+ },
6120
+ [connectWebSocket]
6121
+ );
6122
+ const openIfExisting = useCallback(async () => {
6123
+ const c = clientRef.current;
6124
+ if (!c || openedRef.current) return;
6125
+ openedRef.current = true;
6126
+ setIsLoading(true);
6127
+ const result = await c.support.peekConversation();
6128
+ if (result.ok && result.value) {
6129
+ hydrateConversation(result.value);
6130
+ } else {
6131
+ openedRef.current = false;
6132
+ }
6133
+ setIsLoading(false);
6134
+ }, [hydrateConversation]);
6068
6135
  const watchSessionClaims = useCallback(() => {
6069
6136
  const c = clientRef.current;
6070
6137
  if (!c || claimUnsubRef.current) return;
6071
6138
  const unsubClaimed = c.events.on(SESSION_CLAIMED, (event) => {
6072
- setClaimedAs(event.customerName || null);
6073
- if (!openedRef.current) return;
6139
+ setClaimedAs(event.customerName || c.support.claimedCustomerName || null);
6140
+ if (!openedRef.current) {
6141
+ void openIfExisting();
6142
+ return;
6143
+ }
6074
6144
  connectWebSocket();
6075
6145
  void resyncConversation();
6076
6146
  });
@@ -6079,7 +6149,7 @@ function useChat(options = {}) {
6079
6149
  unsubClaimed();
6080
6150
  unsubCleared();
6081
6151
  };
6082
- }, [connectWebSocket, resyncConversation, resetConversation]);
6152
+ }, [connectWebSocket, resyncConversation, resetConversation, openIfExisting]);
6083
6153
  const signOut = useCallback(async () => {
6084
6154
  const c = clientRef.current;
6085
6155
  if (!c) return false;
@@ -6087,6 +6157,7 @@ function useChat(options = {}) {
6087
6157
  return result.ok;
6088
6158
  }, []);
6089
6159
  useEffect(() => {
6160
+ watchSessionClaims();
6090
6161
  return () => {
6091
6162
  if (pollRef.current) clearInterval(pollRef.current);
6092
6163
  if (wsReconnectTimerRef.current) clearTimeout(wsReconnectTimerRef.current);
@@ -6095,6 +6166,7 @@ function useChat(options = {}) {
6095
6166
  claimUnsubRef.current = null;
6096
6167
  if (wsRef.current) {
6097
6168
  wsRef.current.onclose = null;
6169
+ wsRef.current.onmessage = null;
6098
6170
  wsRef.current.close();
6099
6171
  }
6100
6172
  };
@@ -6106,21 +6178,13 @@ function useChat(options = {}) {
6106
6178
  setError(null);
6107
6179
  const result = await client.support.openConversation();
6108
6180
  if (result.ok) {
6109
- setMessages(result.value.messages);
6110
- setConversationOpened(true);
6111
- aiEnabledRef.current = result.value.conversation.ai_enabled;
6112
- setHumanRequested(result.value.conversation.needs_human_review ?? false);
6113
- setSeenAt(result.value.conversation.team_read_at ?? null);
6114
- setMayHaveOlder(result.value.messages.length >= 50);
6115
- setClaimedAs(client.support.claimedCustomerName);
6116
- connectWebSocket();
6117
- watchSessionClaims();
6181
+ hydrateConversation(result.value);
6118
6182
  } else {
6119
6183
  openedRef.current = false;
6120
6184
  setError(result.error);
6121
6185
  }
6122
6186
  setIsLoading(false);
6123
- }, [client, connectWebSocket, watchSessionClaims]);
6187
+ }, [client, hydrateConversation]);
6124
6188
  const send = useCallback(
6125
6189
  async (content, opts) => {
6126
6190
  const attachments = opts?.attachments ?? [];
@@ -6132,7 +6196,8 @@ function useChat(options = {}) {
6132
6196
  if (!openedRef.current) return false;
6133
6197
  }
6134
6198
  const replyTo = opts?.replyTo;
6135
- const optimisticId = `opt_${Date.now()}`;
6199
+ const clientId = generateIdempotencyKey();
6200
+ const optimisticId = `opt_${clientId}`;
6136
6201
  const optimistic = {
6137
6202
  id: optimisticId,
6138
6203
  sender_type: "customer",
@@ -6140,6 +6205,7 @@ function useChat(options = {}) {
6140
6205
  content_type: attachments.length > 0 ? "image" : "text",
6141
6206
  attachments,
6142
6207
  metadata: {},
6208
+ client_id: clientId,
6143
6209
  // Same shape the server echoes back, so the bubble doesn't reflow
6144
6210
  // when the confirmed message replaces it.
6145
6211
  reply_to: replyTo ? {
@@ -6152,6 +6218,7 @@ function useChat(options = {}) {
6152
6218
  setMessages((prev) => [...prev, optimistic]);
6153
6219
  setIsSending(true);
6154
6220
  const result = await client.support.sendMessage(content.trim(), {
6221
+ idempotencyKey: clientId,
6155
6222
  replyToMessageId: replyTo?.id,
6156
6223
  attachments: attachments.length > 0 ? attachments : void 0
6157
6224
  });
@@ -6222,11 +6289,7 @@ function useChat(options = {}) {
6222
6289
  const older = result.value;
6223
6290
  setMayHaveOlder(older.length >= 50);
6224
6291
  if (older.length === 0) return false;
6225
- setMessages((prev) => {
6226
- const ids = new Set(prev.map((m) => m.id));
6227
- const fresh = older.filter((m) => !ids.has(m.id));
6228
- return fresh.length > 0 ? [...fresh, ...prev] : prev;
6229
- });
6292
+ setMessages((prev) => mergeMessages(prev, older));
6230
6293
  return true;
6231
6294
  }, []);
6232
6295
  const agentArrived = messages.some((m) => m.sender_type === "agent");
@@ -6246,7 +6309,8 @@ function useChat(options = {}) {
6246
6309
  startConversation,
6247
6310
  react,
6248
6311
  requestHuman,
6249
- loadOlder
6312
+ loadOlder,
6313
+ openIfExisting
6250
6314
  };
6251
6315
  }
6252
6316
  var ElementsContext = createContext({
@@ -12414,6 +12478,9 @@ function ProductGrid({
12414
12478
 
12415
12479
  // src/react/utils/attachment.ts
12416
12480
  var IMAGE_EXTENSIONS = [".jpg", ".jpeg", ".png", ".webp", ".gif", ".bmp", ".svg"];
12481
+ function safeAttachmentUrl(url) {
12482
+ return /^https?:\/\//i.test(url) ? url : null;
12483
+ }
12417
12484
  function isImageAttachment(attachment) {
12418
12485
  if (attachment.mime_type?.startsWith("image/")) return true;
12419
12486
  if (attachment.mime_type) return false;
@@ -17060,6 +17127,7 @@ function partitionMessages(messages) {
17060
17127
  }
17061
17128
  return { bubbles, reactionsByTarget };
17062
17129
  }
17130
+ var MAX_ATTACHMENT_BYTES = 5 * 1024 * 1024;
17063
17131
  function ChatWidget({
17064
17132
  client: clientProp,
17065
17133
  businessName = "Support",
@@ -17076,7 +17144,8 @@ function ChatWidget({
17076
17144
  const context = useOptionalCimplifyClient();
17077
17145
  const client = clientProp ?? context?.client;
17078
17146
  const [open, setOpen] = useState(defaultOpen);
17079
- const [readCount, setReadCount] = useState(0);
17147
+ const [readAt, setReadAt] = useState(null);
17148
+ const [attachError, setAttachError] = useState(null);
17080
17149
  const {
17081
17150
  messages,
17082
17151
  isLoading,
@@ -17089,14 +17158,27 @@ function ChatWidget({
17089
17158
  signOut,
17090
17159
  send,
17091
17160
  react,
17092
- startConversation
17161
+ startConversation,
17162
+ openIfExisting
17093
17163
  } = useChat({ client, pollInterval });
17094
17164
  const showTyping = isSending || awaitingReply;
17095
17165
  const attachPhoto = useCallback(
17096
17166
  async (file) => {
17097
- if (!client || !file.type.startsWith("image/") || file.size > 5 * 1024 * 1024) return;
17167
+ if (!client) return;
17168
+ setAttachError(null);
17169
+ if (!file.type.startsWith("image/")) {
17170
+ setAttachError(new CimplifyError("invalid_attachment", "Only photos can be attached."));
17171
+ return;
17172
+ }
17173
+ if (file.size > MAX_ATTACHMENT_BYTES) {
17174
+ setAttachError(new CimplifyError("attachment_too_large", "Photos must be under 5MB."));
17175
+ return;
17176
+ }
17098
17177
  const uploaded = await client.uploads.upload(file);
17099
- if (!uploaded.ok) return;
17178
+ if (!uploaded.ok) {
17179
+ setAttachError(uploaded.error);
17180
+ return;
17181
+ }
17100
17182
  const attachment = {
17101
17183
  upload_id: uploaded.value.id,
17102
17184
  url: uploaded.value.url,
@@ -17108,14 +17190,28 @@ function ChatWidget({
17108
17190
  },
17109
17191
  [client, send]
17110
17192
  );
17111
- const unread = open ? 0 : messages.slice(readCount).filter((m) => m.sender_type !== "customer").length;
17193
+ const handleSend = useCallback(
17194
+ (content) => {
17195
+ setAttachError(null);
17196
+ return send(content);
17197
+ },
17198
+ [send]
17199
+ );
17200
+ const unread = open ? 0 : messages.filter(
17201
+ (m) => m.sender_type !== "customer" && (!readAt || m.created_at > readAt)
17202
+ ).length;
17203
+ useState(() => {
17204
+ if (defaultOpen) void openIfExisting();
17205
+ });
17112
17206
  const handleOpen = useCallback(() => {
17113
17207
  setOpen(true);
17114
- }, []);
17208
+ void openIfExisting();
17209
+ }, [openIfExisting]);
17115
17210
  const handleClose = useCallback(() => {
17116
- setReadCount(messages.length);
17211
+ const newest = messages[messages.length - 1];
17212
+ setReadAt(newest?.created_at ?? null);
17117
17213
  setOpen(false);
17118
- }, [messages.length]);
17214
+ }, [messages]);
17119
17215
  const isLeft = position === "bottom-left";
17120
17216
  return /* @__PURE__ */ jsxs(
17121
17217
  "div",
@@ -17148,13 +17244,13 @@ function ChatWidget({
17148
17244
  isSending,
17149
17245
  isTyping: showTyping,
17150
17246
  seenAt,
17151
- error,
17247
+ error: error ?? attachError,
17152
17248
  isActive,
17153
17249
  isLeft,
17154
17250
  claimedAs,
17155
17251
  onSignOut: () => void signOut(),
17156
17252
  onClose: handleClose,
17157
- onSend: send,
17253
+ onSend: handleSend,
17158
17254
  onReact: react,
17159
17255
  onAttach: attachPhoto,
17160
17256
  onStartConversation: startConversation,
@@ -17265,7 +17361,7 @@ function Panel({
17265
17361
  className: classNames?.messages
17266
17362
  }
17267
17363
  ),
17268
- error != null && /* @__PURE__ */ jsx("div", { className: "border-t border-red-200 bg-red-50 px-4 py-2 text-xs text-red-600", children: "Something went wrong. Please try again." }),
17364
+ error != null && /* @__PURE__ */ jsx("div", { className: "border-t border-red-200 bg-red-50 px-4 py-2 text-xs text-red-600", children: error.userMessage || "Something went wrong. Please try again." }),
17269
17365
  (isActive || isLoading) && /* @__PURE__ */ jsx(
17270
17366
  ChatInput,
17271
17367
  {
@@ -17475,11 +17571,13 @@ function MessageList({
17475
17571
  ]
17476
17572
  }
17477
17573
  ),
17478
- msg.attachments.length > 0 && /* @__PURE__ */ jsx("div", { className: cn("flex flex-wrap gap-1.5", msg.content && "mb-1.5"), children: msg.attachments.map(
17479
- (attachment, index) => attachment.mime_type?.startsWith("image/") ? /* @__PURE__ */ jsx("a", { href: attachment.url, target: "_blank", rel: "noreferrer", children: /* @__PURE__ */ jsx(
17574
+ msg.attachments.length > 0 && /* @__PURE__ */ jsx("div", { className: cn("flex flex-wrap gap-1.5", msg.content && "mb-1.5"), children: msg.attachments.map((attachment, index) => {
17575
+ const url = safeAttachmentUrl(attachment.url);
17576
+ if (!url) return null;
17577
+ return attachment.mime_type?.startsWith("image/") ? /* @__PURE__ */ jsx("a", { href: url, target: "_blank", rel: "noreferrer", children: /* @__PURE__ */ jsx(
17480
17578
  "img",
17481
17579
  {
17482
- src: attachment.url,
17580
+ src: url,
17483
17581
  alt: attachment.filename ?? "Photo",
17484
17582
  loading: "lazy",
17485
17583
  className: "max-h-48 max-w-full rounded-lg object-cover"
@@ -17487,15 +17585,15 @@ function MessageList({
17487
17585
  ) }, index) : /* @__PURE__ */ jsx(
17488
17586
  "a",
17489
17587
  {
17490
- href: attachment.url,
17588
+ href: url,
17491
17589
  target: "_blank",
17492
17590
  rel: "noreferrer",
17493
17591
  className: "truncate text-xs underline",
17494
17592
  children: attachment.filename ?? "Attachment"
17495
17593
  },
17496
17594
  index
17497
- )
17498
- ) }),
17595
+ );
17596
+ }) }),
17499
17597
  isCustomer ? msg.content && /* @__PURE__ */ jsx("div", { className: "whitespace-pre-wrap", children: msg.content }) : /* @__PURE__ */ jsx(
17500
17598
  ChatMarkdown,
17501
17599
  {
@@ -19576,4 +19674,4 @@ async function genUIFingerprint(manifest) {
19576
19674
  return Array.from(new Uint8Array(digest)).map((byte) => byte.toString(16).padStart(2, "0")).join("").slice(0, 16);
19577
19675
  }
19578
19676
 
19579
- export { AccommodationCard, AccountContent, AccountDashboardPage, AccountHero, AccountIdentityStrip, AccountLineItem, AccountLineItems, AccountNav, AccountOrderDetailPage, AccountOrderView, AccountOrdersPage, AccountShell, AccountSidebar, AccountSignedOutPrompt, AccountSubscriptionDetailPage, AccountSubscriptionsPage, Ad, AdProvider, AddOnSelector, AddressAutocomplete, AddressElement, AuthElement, AvailabilityBadge, BackLink, BillingPlanSelector, BookingCard, BookingList, BookingPage, BookingSummary, BookingsPage, BoxIcon24, BundleProductCard, BundleProductLayout, BundleSelector, Button, CIMPLIFY_GENUI_REGISTRY, CardImage, CardShell, CardVariant, CartDrawer, CartDrawerProvider, CartPage, CartSummary, CartTemplate, CatalogueCollectionLayout, CataloguePage, CatalogueTemplate, CategoryFilter, CategoryGrid, ChatMarkdown, ChatWidget, CheckIcon, CheckoutPage, CimplifyCheckout, CimplifyProvider, CimplifySignInButton, CimplifyUserButton, CollectionPage, CollectionTemplate, CompactCartLayout, CompactCatalogueLayout, CompactSearchLayout, CompactServiceCard, CompositeProductCard, CompositeProductLayout, CompositeSelector, ContextCard, ContinuityCard, CurrencySelector, CustomAttributesTable, CustomerInputFields, DashboardIcon, DashboardIcon20, DatePicker, DateSlotPicker, DealBanner, DealsPage, DefaultCartLayout, DefaultCatalogueLayout, DefaultCollectionLayout, DefaultProductLayout, DefaultSearchLayout, DeliveryEstimate, DeliveryTracker, DeliveryTruckIcon, DigitalProductCard, DigitalProductLayout, DiscountInput, ElementsProvider, EmptyState, ErrorState, FeaturedCollectionLayout, FilterTabs, FoodProductCard, FoodProductLayout, GenUIView, HappeningCard, IdentityIcon, IdentityOfframp, InventoryBadge, ItemsCard, LeaseServiceCard, LineItemDetails, LocationPicker, MediaGallery, MetadataStringList, MobileAccountShell, MobileBackButton, MobileMoreButton, MobileTabBar, NextDeliveryCard, OrderDetailHeader, OrderDetailMetaPipe, OrderDetailPage, OrderHistory, OrderHistoryPage, OrderList, OrderRow, OrderSummary, OrderTimeline, OrderTotals, OrdersIcon, OrdersIcon20, OvenIcon, PackageIcon, PaymentElement, Price, PriceRange, ProductCard, ProductCustomizer, ProductGrid, ProductImageGallery, ProductModel3D, ProductPage, ProductSheet, ProductTemplate, PropertiesTable, QuantitySelector, QuickAddButton, RecentlyViewed, RecommendationCarousel, RelatedProductsSection, RentalServiceCard, ResourcePicker, RetailProductCard, RichText, SaleBadge, SalePrice, SaleTeaser, ScheduleServiceCard, SearchInput, SearchPage, SearchTemplate, Section, SectionMore, ServiceProductLayout, SessionMessageBanner, SignOutIcon, Skeleton, SlotPicker, SoldOutOverlay, StaffPicker, StandardServiceCard, Status, StoreNav, StoreVideo, SubscriptionCard, SubscriptionList, SubscriptionRow, SubscriptionsIcon, SubscriptionsIcon20, TagPills, TimePicker, Toast, TwoColumnGrid, VariantSelector, VolumePricing, WelcomeCard, WholesaleProductCard, WholesaleProductLayout, WishlistButton, clearOrdersCache, cn, createPlacesSessionToken, defaultBoundaryKind, genUICatalog, genUIFingerprint, genUIManifest, genUIManifestJson, getBoundaryLabels, isImageAttachment, isTerminalOrderStatus, parse as parseGenUIProgram, partitionMessages, prefetchProduct, prefetchProducts, resolveBoundaryKind, roomToResource, sanitizeRichTextHtml, stripHtmlToText, toAccountLineItem, toAccountLineItems, toAccountOrderView, useActivityState, useAds, useAttributeDefinitions, useAvailableSlots, useBillingPlans, useBookings, useBootstrap, useBundles, useCart, useCartDrawer, useCategories, useChat, useCheckout, useCimplify, useCimplifyClient, useCimplifySession, useCollection, useCollections, useDeals, useDeliveryFee, useDeliveryOptions, useElements, useElementsReady, useFxRate, useGenUIProgram, useLocations, useOptionalCimplify, useOrder, useOrders, useProduct, useProductAvailability, useProductDeals, useProductPrice, useProductSchedules, useProducts, useProductsOnSale, usePropertyFacets, useQuote, useRecommendations, useSearch, useServiceAvailability, useServices, useStoreCredit, useSubscription, useSubscriptions, useTaxonomies, useTaxonomy, useTaxonomyPath, useValidateDiscount, useVariantSelector };
19677
+ export { AccommodationCard, AccountContent, AccountDashboardPage, AccountHero, AccountIdentityStrip, AccountLineItem, AccountLineItems, AccountNav, AccountOrderDetailPage, AccountOrderView, AccountOrdersPage, AccountShell, AccountSidebar, AccountSignedOutPrompt, AccountSubscriptionDetailPage, AccountSubscriptionsPage, Ad, AdProvider, AddOnSelector, AddressAutocomplete, AddressElement, AuthElement, AvailabilityBadge, BackLink, BillingPlanSelector, BookingCard, BookingList, BookingPage, BookingSummary, BookingsPage, BoxIcon24, BundleProductCard, BundleProductLayout, BundleSelector, Button, CIMPLIFY_GENUI_REGISTRY, CardImage, CardShell, CardVariant, CartDrawer, CartDrawerProvider, CartPage, CartSummary, CartTemplate, CatalogueCollectionLayout, CataloguePage, CatalogueTemplate, CategoryFilter, CategoryGrid, ChatMarkdown, ChatWidget, CheckIcon, CheckoutPage, CimplifyCheckout, CimplifyProvider, CimplifySignInButton, CimplifyUserButton, CollectionPage, CollectionTemplate, CompactCartLayout, CompactCatalogueLayout, CompactSearchLayout, CompactServiceCard, CompositeProductCard, CompositeProductLayout, CompositeSelector, ContextCard, ContinuityCard, CurrencySelector, CustomAttributesTable, CustomerInputFields, DashboardIcon, DashboardIcon20, DatePicker, DateSlotPicker, DealBanner, DealsPage, DefaultCartLayout, DefaultCatalogueLayout, DefaultCollectionLayout, DefaultProductLayout, DefaultSearchLayout, DeliveryEstimate, DeliveryTracker, DeliveryTruckIcon, DigitalProductCard, DigitalProductLayout, DiscountInput, ElementsProvider, EmptyState, ErrorState, FeaturedCollectionLayout, FilterTabs, FoodProductCard, FoodProductLayout, GenUIView, HappeningCard, IdentityIcon, IdentityOfframp, InventoryBadge, ItemsCard, LeaseServiceCard, LineItemDetails, LocationPicker, MediaGallery, MetadataStringList, MobileAccountShell, MobileBackButton, MobileMoreButton, MobileTabBar, NextDeliveryCard, OrderDetailHeader, OrderDetailMetaPipe, OrderDetailPage, OrderHistory, OrderHistoryPage, OrderList, OrderRow, OrderSummary, OrderTimeline, OrderTotals, OrdersIcon, OrdersIcon20, OvenIcon, PackageIcon, PaymentElement, Price, PriceRange, ProductCard, ProductCustomizer, ProductGrid, ProductImageGallery, ProductModel3D, ProductPage, ProductSheet, ProductTemplate, PropertiesTable, QuantitySelector, QuickAddButton, RecentlyViewed, RecommendationCarousel, RelatedProductsSection, RentalServiceCard, ResourcePicker, RetailProductCard, RichText, SaleBadge, SalePrice, SaleTeaser, ScheduleServiceCard, SearchInput, SearchPage, SearchTemplate, Section, SectionMore, ServiceProductLayout, SessionMessageBanner, SignOutIcon, Skeleton, SlotPicker, SoldOutOverlay, StaffPicker, StandardServiceCard, Status, StoreNav, StoreVideo, SubscriptionCard, SubscriptionList, SubscriptionRow, SubscriptionsIcon, SubscriptionsIcon20, TagPills, TimePicker, Toast, TwoColumnGrid, VariantSelector, VolumePricing, WelcomeCard, WholesaleProductCard, WholesaleProductLayout, WishlistButton, clearOrdersCache, cn, createPlacesSessionToken, defaultBoundaryKind, genUICatalog, genUIFingerprint, genUIManifest, genUIManifestJson, getBoundaryLabels, isImageAttachment, isTerminalOrderStatus, parse as parseGenUIProgram, partitionMessages, prefetchProduct, prefetchProducts, resolveBoundaryKind, roomToResource, safeAttachmentUrl, sanitizeRichTextHtml, stripHtmlToText, toAccountLineItem, toAccountLineItems, toAccountOrderView, useActivityState, useAds, useAttributeDefinitions, useAvailableSlots, useBillingPlans, useBookings, useBootstrap, useBundles, useCart, useCartDrawer, useCategories, useChat, useCheckout, useCimplify, useCimplifyClient, useCimplifySession, useCollection, useCollections, useDeals, useDeliveryFee, useDeliveryOptions, useElements, useElementsReady, useFxRate, useGenUIProgram, useLocations, useOptionalCimplify, useOrder, useOrders, useProduct, useProductAvailability, useProductDeals, useProductPrice, useProductSchedules, useProducts, useProductsOnSale, usePropertyFacets, useQuote, useRecommendations, useSearch, useServiceAvailability, useServices, useStoreCredit, useSubscription, useSubscriptions, useTaxonomies, useTaxonomy, useTaxonomyPath, useValidateDiscount, useVariantSelector };
@@ -1,5 +1,5 @@
1
1
  import { Hono } from 'hono';
2
- import { b3 as ProductInputField } from './product-bZ8G5Z8D.mjs';
2
+ import { b3 as ProductInputField } from './product-O8-wcHSe.mjs';
3
3
 
4
4
  interface Clock {
5
5
  now(): Date;
@@ -952,6 +952,10 @@ interface ActivityService {
952
952
 
953
953
  interface SupportService {
954
954
  openConversation(businessId: string, customerId?: string): MockConversation;
955
+ peekConversation(businessId: string, customerId?: string): {
956
+ conversation: MockConversation | null;
957
+ messages: MockChatMessage[];
958
+ };
955
959
  sendMessage(conversationId: string, body: {
956
960
  content: string;
957
961
  content_type?: string;
@@ -1,5 +1,5 @@
1
1
  import { Hono } from 'hono';
2
- import { b3 as ProductInputField } from './product-bZ8G5Z8D.js';
2
+ import { b3 as ProductInputField } from './product-O8-wcHSe.js';
3
3
 
4
4
  interface Clock {
5
5
  now(): Date;
@@ -952,6 +952,10 @@ interface ActivityService {
952
952
 
953
953
  interface SupportService {
954
954
  openConversation(businessId: string, customerId?: string): MockConversation;
955
+ peekConversation(businessId: string, customerId?: string): {
956
+ conversation: MockConversation | null;
957
+ messages: MockChatMessage[];
958
+ };
955
959
  sendMessage(conversationId: string, body: {
956
960
  content: string;
957
961
  content_type?: string;
package/dist/server.d.mts CHANGED
@@ -1,8 +1,8 @@
1
- import { af as CimplifyClient } from './client-BQbSKlHS.mjs';
2
- export { J as CacheOptions, cC as ReadRequestOptions, cZ as Result } from './client-BQbSKlHS.mjs';
3
- export { S as Category, V as CimplifyError, W as Collection, aZ as Product, bb as ProductWithDetails } from './product-bZ8G5Z8D.mjs';
1
+ import { af as CimplifyClient } from './client-BTL9d_nN.mjs';
2
+ export { J as CacheOptions, cC as ReadRequestOptions, cZ as Result } from './client-BTL9d_nN.mjs';
3
+ export { S as Category, V as CimplifyError, W as Collection, aZ as Product, bb as ProductWithDetails } from './product-O8-wcHSe.mjs';
4
4
  import { JWTVerifyResult } from 'jose';
5
- import './payment-BFtLqiG2.mjs';
5
+ import './payment-pKPpaORI.mjs';
6
6
 
7
7
  interface ServerClientOptions {
8
8
  /**
package/dist/server.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { af as CimplifyClient } from './client-CeW3acve.js';
2
- export { J as CacheOptions, cC as ReadRequestOptions, cZ as Result } from './client-CeW3acve.js';
3
- export { S as Category, V as CimplifyError, W as Collection, aZ as Product, bb as ProductWithDetails } from './product-bZ8G5Z8D.js';
1
+ import { af as CimplifyClient } from './client-BX30FAWZ.js';
2
+ export { J as CacheOptions, cC as ReadRequestOptions, cZ as Result } from './client-BX30FAWZ.js';
3
+ export { S as Category, V as CimplifyError, W as Collection, aZ as Product, bb as ProductWithDetails } from './product-O8-wcHSe.js';
4
4
  import { JWTVerifyResult } from 'jose';
5
- import './payment-Cq8FT1B6.js';
5
+ import './payment-By0JidTe.js';
6
6
 
7
7
  interface ServerClientOptions {
8
8
  /**
package/dist/server.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var chunkDCRN67NO_js = require('./chunk-DCRN67NO.js');
4
- var chunkIVJEUGWF_js = require('./chunk-IVJEUGWF.js');
3
+ var chunkQLGZIBRO_js = require('./chunk-QLGZIBRO.js');
4
+ var chunkVEOAMBEX_js = require('./chunk-VEOAMBEX.js');
5
5
  require('./chunk-PTPOQVXS.js');
6
6
  var chunkAETVRBKI_js = require('./chunk-AETVRBKI.js');
7
7
  require('./chunk-OWW5GUSB.js');
@@ -26,7 +26,7 @@ function readEnv(...keys) {
26
26
  var getServerClient = react.cache((opts = {}) => {
27
27
  const baseUrl = opts.apiUrl ?? readEnv("CIMPLIFY_API_URL", "NEXT_PUBLIC_CIMPLIFY_API_URL") ?? defaultBaseUrl();
28
28
  const publicKey = opts.secretKey ?? readEnv("CIMPLIFY_SECRET_KEY", "NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY") ?? "mock-dev";
29
- const client = chunkDCRN67NO_js.createCimplifyClient({
29
+ const client = chunkQLGZIBRO_js.createCimplifyClient({
30
30
  baseUrl,
31
31
  publicKey,
32
32
  suppressPublicKeyWarning: true
@@ -250,7 +250,7 @@ var QUERY_AUTH_ERROR = "cimplify_auth_error";
250
250
  var discoveryCache = /* @__PURE__ */ new Map();
251
251
  var jwksCache = /* @__PURE__ */ new Map();
252
252
  function issuerBase(cfg) {
253
- return (cfg.issuer ?? cfg.authUrl ?? chunkIVJEUGWF_js.DEFAULT_ISSUER).replace(/\/$/, "");
253
+ return (cfg.issuer ?? cfg.authUrl ?? chunkVEOAMBEX_js.DEFAULT_ISSUER).replace(/\/$/, "");
254
254
  }
255
255
  function cookieName(cfg) {
256
256
  return cfg.cookieName ?? SESSION_COOKIE;
@@ -465,12 +465,12 @@ async function handleOidcCallback(req, opts) {
465
465
  }
466
466
  function readPkceCookie(cookieHeader) {
467
467
  if (!cookieHeader) return null;
468
- const match = cookieHeader.split(/;\s*/).find((p) => p.startsWith(`${chunkIVJEUGWF_js.PKCE_COOKIE}=`));
468
+ const match = cookieHeader.split(/;\s*/).find((p) => p.startsWith(`${chunkVEOAMBEX_js.PKCE_COOKIE}=`));
469
469
  if (!match) return null;
470
- return chunkIVJEUGWF_js.decodePkceState(match.slice(chunkIVJEUGWF_js.PKCE_COOKIE.length + 1));
470
+ return chunkVEOAMBEX_js.decodePkceState(match.slice(chunkVEOAMBEX_js.PKCE_COOKIE.length + 1));
471
471
  }
472
472
  function buildPkceClearCookie() {
473
- return `${chunkIVJEUGWF_js.PKCE_COOKIE}=; Path=${chunkIVJEUGWF_js.PKCE_COOKIE_PATH}; Max-Age=0; Secure; SameSite=Lax`;
473
+ return `${chunkVEOAMBEX_js.PKCE_COOKIE}=; Path=${chunkVEOAMBEX_js.PKCE_COOKIE_PATH}; Max-Age=0; Secure; SameSite=Lax`;
474
474
  }
475
475
  function safeReturnPath(returnTo, fallback) {
476
476
  if (returnTo && returnTo.startsWith("/") && !returnTo.startsWith("//")) return returnTo;
package/dist/server.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { createCimplifyClient } from './chunk-3XD2FVP4.mjs';
2
- import { PKCE_COOKIE, decodePkceState, PKCE_COOKIE_PATH, DEFAULT_ISSUER } from './chunk-T7LDBZZZ.mjs';
1
+ import { createCimplifyClient } from './chunk-FKARGZK3.mjs';
2
+ import { PKCE_COOKIE, decodePkceState, PKCE_COOKIE_PATH, DEFAULT_ISSUER } from './chunk-VUSHG3HA.mjs';
3
3
  import './chunk-KFLGQGMC.mjs';
4
4
  export { CimplifyError } from './chunk-K5EN2W2Q.mjs';
5
5
  import './chunk-3G6RQLXK.mjs';
@@ -1,6 +1,6 @@
1
- import { A as AppHandle, C as CreateAppOptions } from '../server-D-iK-bNt.mjs';
1
+ import { A as AppHandle, C as CreateAppOptions } from '../server-BN9ZCAKT.mjs';
2
2
  import 'hono';
3
- import '../product-bZ8G5Z8D.mjs';
3
+ import '../product-O8-wcHSe.mjs';
4
4
 
5
5
  interface MswHandlerSetup {
6
6
  handlers: unknown[];
@@ -1,6 +1,6 @@
1
- import { A as AppHandle, C as CreateAppOptions } from '../server-DwsY9hlX.js';
1
+ import { A as AppHandle, C as CreateAppOptions } from '../server-DDGbfW_S.js';
2
2
  import 'hono';
3
- import '../product-bZ8G5Z8D.js';
3
+ import '../product-O8-wcHSe.js';
4
4
 
5
5
  interface MswHandlerSetup {
6
6
  handlers: unknown[];
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkOAHSF542_js = require('../chunk-OAHSF542.js');
3
+ var chunk6Y7KC5YZ_js = require('../chunk-6Y7KC5YZ.js');
4
4
  require('../chunk-XREIZ56K.js');
5
5
  require('../chunk-AETVRBKI.js');
6
6
  require('../chunk-OWW5GUSB.js');
@@ -8,7 +8,7 @@ require('../chunk-OWW5GUSB.js');
8
8
  // src/mock/msw.ts
9
9
  async function createMswHandlers(options = {}) {
10
10
  const baseUrl = options.baseUrl ?? "http://localhost:8787";
11
- const handle = chunkOAHSF542_js.createMockApp(options);
11
+ const handle = chunk6Y7KC5YZ_js.createMockApp(options);
12
12
  const msw = await import('msw').catch(() => {
13
13
  throw new Error("msw is required to use @cimplify/sdk/mock/msw \u2014 install it as a peer dependency");
14
14
  });
@@ -1,4 +1,4 @@
1
- import { createMockApp } from '../chunk-PYBFTMHI.mjs';
1
+ import { createMockApp } from '../chunk-X36FZZT6.mjs';
2
2
  import '../chunk-U4MW6PYU.mjs';
3
3
  import '../chunk-K5EN2W2Q.mjs';
4
4
  import '../chunk-3G6RQLXK.mjs';
@@ -1,10 +1,10 @@
1
1
  import { TestAPI } from 'vitest';
2
- import { T as TestClientHandle } from '../client-Bw4HtLP1.mjs';
3
- import { C as CreateAppOptions } from '../server-D-iK-bNt.mjs';
4
- export { S as SeedName } from '../server-D-iK-bNt.mjs';
5
- import '../client-BQbSKlHS.mjs';
6
- import '../product-bZ8G5Z8D.mjs';
7
- import '../payment-BFtLqiG2.mjs';
2
+ import { T as TestClientHandle } from '../client-Bhe2TnFW.mjs';
3
+ import { C as CreateAppOptions } from '../server-BN9ZCAKT.mjs';
4
+ export { S as SeedName } from '../server-BN9ZCAKT.mjs';
5
+ import '../client-BTL9d_nN.mjs';
6
+ import '../product-O8-wcHSe.mjs';
7
+ import '../payment-pKPpaORI.mjs';
8
8
  import 'hono';
9
9
 
10
10
  /**