@joai/core 4.16.0 → 5.0.1

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/dist/index.mjs CHANGED
@@ -410,6 +410,63 @@ function integrationSlugForChannel(channel) {
410
410
  return channel === "sms" ? "twilio" : channel;
411
411
  }
412
412
  //#endregion
413
+ //#region src/content/image-processing.ts
414
+ const positiveFinite = (value) => typeof value === "number" && Number.isFinite(value) && value > 0;
415
+ const validateImageDimensions = (dimensions, rules) => {
416
+ if (!positiveFinite(dimensions.width) || !positiveFinite(dimensions.height)) return "too-small";
417
+ if (rules.minWidth && dimensions.width < rules.minWidth || rules.minHeight && dimensions.height < rules.minHeight) return "too-small";
418
+ if (rules.maxWidth && dimensions.width > rules.maxWidth || rules.maxHeight && dimensions.height > rules.maxHeight || rules.maxPixels && dimensions.width * dimensions.height > rules.maxPixels) return "too-large";
419
+ return null;
420
+ };
421
+ const imageCropRectangle = (dimensions, aspectRatio, crop) => {
422
+ if (!positiveFinite(aspectRatio)) throw new RangeError("Crop aspect ratio must be a positive finite number.");
423
+ if (!positiveFinite(dimensions.width) || !positiveFinite(dimensions.height)) throw new RangeError("Image dimensions must be positive finite numbers.");
424
+ const sourceAspect = dimensions.width / dimensions.height;
425
+ const baseWidth = sourceAspect > aspectRatio ? dimensions.height * aspectRatio : dimensions.width;
426
+ const baseHeight = sourceAspect > aspectRatio ? dimensions.height : dimensions.width / aspectRatio;
427
+ const zoom = Number.isFinite(crop.zoom) ? Math.max(1, crop.zoom) : 1;
428
+ const positionX = Number.isFinite(crop.x) ? Math.max(-1, Math.min(1, crop.x)) : 0;
429
+ const positionY = Number.isFinite(crop.y) ? Math.max(-1, Math.min(1, crop.y)) : 0;
430
+ const width = baseWidth / zoom;
431
+ const height = baseHeight / zoom;
432
+ const availableX = dimensions.width - width;
433
+ const availableY = dimensions.height - height;
434
+ return {
435
+ x: availableX * ((positionX + 1) / 2),
436
+ y: availableY * ((positionY + 1) / 2),
437
+ width,
438
+ height
439
+ };
440
+ };
441
+ const containedImageDimensions = (source, options) => {
442
+ if (!positiveFinite(source.width) || !positiveFinite(source.height)) throw new RangeError("Image dimensions must be positive finite numbers.");
443
+ const maxWidth = positiveFinite(options.maxWidth) ? options.maxWidth : source.width;
444
+ const maxHeight = positiveFinite(options.maxHeight) ? options.maxHeight : source.height;
445
+ const scale = Math.min(1, maxWidth / source.width, maxHeight / source.height);
446
+ return {
447
+ width: Math.max(1, Math.round(source.width * scale)),
448
+ height: Math.max(1, Math.round(source.height * scale))
449
+ };
450
+ };
451
+ const cropOutputDimensions = (source, options, compression) => {
452
+ if (!positiveFinite(options.aspectRatio)) throw new RangeError("Crop aspect ratio must be a positive finite number.");
453
+ const width = positiveFinite(options.outputWidth) ? options.outputWidth : void 0;
454
+ const height = positiveFinite(options.outputHeight) ? options.outputHeight : void 0;
455
+ if (width && height) return {
456
+ width: Math.max(1, Math.round(width)),
457
+ height: Math.max(1, Math.round(height))
458
+ };
459
+ if (width) return {
460
+ width: Math.max(1, Math.round(width)),
461
+ height: Math.max(1, Math.round(width / options.aspectRatio))
462
+ };
463
+ if (height) return {
464
+ width: Math.max(1, Math.round(height * options.aspectRatio)),
465
+ height: Math.max(1, Math.round(height))
466
+ };
467
+ return containedImageDimensions(source, compression);
468
+ };
469
+ //#endregion
413
470
  //#region src/address/enums.ts
414
471
  const AddressTypes = {
415
472
  Billing: "billing",
@@ -629,6 +686,173 @@ const cartLineTotal = (items) => items.reduce((total, item) => total + item.pric
629
686
  const cartPayableTotal = (items, discountCents = 0) => Math.max(0, cartLineTotal(items) - Math.max(0, discountCents));
630
687
  const isCompleteAddressInput = (address) => Boolean(address.name.trim() && address.addressOne.trim() && address.city.trim() && address.zipCode.trim() && address.country.trim());
631
688
  //#endregion
689
+ //#region src/commerce/returns.ts
690
+ /** Pickup "collected" is `delivered`; goods must have left the shop before a return makes sense. */
691
+ const RETURNABLE_FULFILLMENT_STATUSES = ["shipped", "delivered"];
692
+ const resolveOrderItemId = (item) => typeof item.orderItemId === "number" && Number.isFinite(item.orderItemId) ? item.orderItemId : Number(item.id);
693
+ const resolveFulfillmentId = (value) => {
694
+ if (value === null || value === void 0 || value === "") return null;
695
+ return String(value);
696
+ };
697
+ /** Physical quantities of a paid order that were shipped/delivered and are not yet in a non-rejected return. */
698
+ const getReturnableItems = (order, returns = order.returns ?? []) => {
699
+ if (!isOrderInFulfillment(order.status)) return [];
700
+ const shippedByItem = /* @__PURE__ */ new Map();
701
+ (order.fulfillments ?? []).filter((fulfillment) => RETURNABLE_FULFILLMENT_STATUSES.includes(fulfillment.status)).forEach((fulfillment) => {
702
+ const fulfillmentId = resolveFulfillmentId(fulfillment.id);
703
+ if (fulfillmentId === null) return;
704
+ fulfillment.items.forEach((item) => {
705
+ const byFulfillment = shippedByItem.get(item.orderItemId) ?? /* @__PURE__ */ new Map();
706
+ byFulfillment.set(fulfillmentId, (byFulfillment.get(fulfillmentId) ?? 0) + item.quantity);
707
+ shippedByItem.set(item.orderItemId, byFulfillment);
708
+ });
709
+ });
710
+ returns.filter((request) => request.status !== "rejected").forEach((request) => {
711
+ const preferredFulfillmentId = resolveFulfillmentId(request.fulfillmentId);
712
+ request.items.forEach((item) => {
713
+ const byFulfillment = shippedByItem.get(item.orderItemId);
714
+ if (!byFulfillment) return;
715
+ let remaining = item.quantity;
716
+ if (preferredFulfillmentId !== null && byFulfillment.has(preferredFulfillmentId)) {
717
+ const available = byFulfillment.get(preferredFulfillmentId) ?? 0;
718
+ const used = Math.min(available, remaining);
719
+ byFulfillment.set(preferredFulfillmentId, available - used);
720
+ remaining -= used;
721
+ }
722
+ for (const [fulfillmentId, available] of byFulfillment) {
723
+ if (remaining <= 0) break;
724
+ if (preferredFulfillmentId !== null && fulfillmentId === preferredFulfillmentId) continue;
725
+ const used = Math.min(available, remaining);
726
+ byFulfillment.set(fulfillmentId, available - used);
727
+ remaining -= used;
728
+ }
729
+ });
730
+ });
731
+ return order.items.filter((item) => !item.digital && item.type !== "coupon").map((item) => {
732
+ const orderItemId = resolveOrderItemId(item);
733
+ const byFulfillment = shippedByItem.get(orderItemId) ?? /* @__PURE__ */ new Map();
734
+ const quantity = Math.min(item.quantity, [...byFulfillment.values()].reduce((sum, value) => sum + value, 0));
735
+ const fulfillmentIds = [...byFulfillment.entries()].filter(([, available]) => available > 0).map(([id]) => id);
736
+ return {
737
+ item,
738
+ orderItemId,
739
+ quantity,
740
+ fulfillmentId: fulfillmentIds.length === 1 ? fulfillmentIds[0] : null
741
+ };
742
+ }).filter(({ orderItemId, quantity }) => Number.isFinite(orderItemId) && quantity > 0);
743
+ };
744
+ /**
745
+ * Split selected return quantities into create payloads, one per fulfillment when possible.
746
+ * Quantities without a single fulfillment go in an unscoped payload.
747
+ */
748
+ const buildReturnCreatePayloads = (order, selected, returns = order.returns ?? []) => {
749
+ const returnable = getReturnableItems(order, returns);
750
+ const remainingByItem = new Map(returnable.map((item) => [item.orderItemId, item.quantity]));
751
+ const fulfillmentPools = /* @__PURE__ */ new Map();
752
+ (order.fulfillments ?? []).filter((fulfillment) => RETURNABLE_FULFILLMENT_STATUSES.includes(fulfillment.status)).forEach((fulfillment) => {
753
+ const fulfillmentId = resolveFulfillmentId(fulfillment.id);
754
+ if (fulfillmentId === null) return;
755
+ const pool = /* @__PURE__ */ new Map();
756
+ fulfillment.items.forEach((item) => pool.set(item.orderItemId, (pool.get(item.orderItemId) ?? 0) + item.quantity));
757
+ fulfillmentPools.set(fulfillmentId, pool);
758
+ });
759
+ returns.filter((request) => request.status !== "rejected").forEach((request) => {
760
+ const preferredFulfillmentId = resolveFulfillmentId(request.fulfillmentId);
761
+ request.items.forEach((item) => {
762
+ let remaining = item.quantity;
763
+ if (preferredFulfillmentId !== null) {
764
+ const pool = fulfillmentPools.get(preferredFulfillmentId);
765
+ if (pool) {
766
+ const available = pool.get(item.orderItemId) ?? 0;
767
+ const used = Math.min(available, remaining);
768
+ pool.set(item.orderItemId, available - used);
769
+ remaining -= used;
770
+ }
771
+ }
772
+ for (const pool of fulfillmentPools.values()) {
773
+ if (remaining <= 0) break;
774
+ const available = pool.get(item.orderItemId) ?? 0;
775
+ const used = Math.min(available, remaining);
776
+ pool.set(item.orderItemId, available - used);
777
+ remaining -= used;
778
+ }
779
+ });
780
+ });
781
+ const payloads = /* @__PURE__ */ new Map();
782
+ selected.filter((item) => item.quantity > 0).forEach((item) => {
783
+ let remaining = Math.min(item.quantity, remainingByItem.get(item.orderItemId) ?? 0);
784
+ remainingByItem.set(item.orderItemId, (remainingByItem.get(item.orderItemId) ?? 0) - remaining);
785
+ const preferred = returnable.find((candidate) => candidate.orderItemId === item.orderItemId)?.fulfillmentId;
786
+ if (preferred !== null && preferred !== void 0) {
787
+ const pool = fulfillmentPools.get(preferred);
788
+ const available = pool?.get(item.orderItemId) ?? 0;
789
+ const used = Math.min(available, remaining);
790
+ if (used > 0) {
791
+ pool?.set(item.orderItemId, available - used);
792
+ const key = preferred;
793
+ const payload = payloads.get(key) ?? {
794
+ fulfillmentId: preferred,
795
+ items: []
796
+ };
797
+ payload.items.push({
798
+ orderItemId: item.orderItemId,
799
+ quantity: used,
800
+ condition: item.condition
801
+ });
802
+ payloads.set(key, payload);
803
+ remaining -= used;
804
+ }
805
+ }
806
+ for (const [fulfillmentId, pool] of fulfillmentPools) {
807
+ if (remaining <= 0) break;
808
+ if (preferred !== null && preferred !== void 0 && fulfillmentId === preferred) continue;
809
+ const available = pool.get(item.orderItemId) ?? 0;
810
+ const used = Math.min(available, remaining);
811
+ if (used <= 0) continue;
812
+ pool.set(item.orderItemId, available - used);
813
+ const payload = payloads.get(fulfillmentId) ?? {
814
+ fulfillmentId,
815
+ items: []
816
+ };
817
+ payload.items.push({
818
+ orderItemId: item.orderItemId,
819
+ quantity: used,
820
+ condition: item.condition
821
+ });
822
+ payloads.set(fulfillmentId, payload);
823
+ remaining -= used;
824
+ }
825
+ if (remaining > 0) {
826
+ const payload = payloads.get("unscoped") ?? { items: [] };
827
+ payload.items.push({
828
+ orderItemId: item.orderItemId,
829
+ quantity: remaining,
830
+ condition: item.condition
831
+ });
832
+ payloads.set("unscoped", payload);
833
+ }
834
+ });
835
+ return [...payloads.values()].filter((payload) => payload.items.length > 0);
836
+ };
837
+ /** Customer-paid amount for returned units (includes proportional tax when prices are net). */
838
+ const getReturnRefundAmount = (order, request, alreadyRefunded = 0) => {
839
+ if (typeof request.suggestedRefundAmount === "number" && Number.isFinite(request.suggestedRefundAmount)) return Math.max(0, request.suggestedRefundAmount);
840
+ if (typeof request.refundAmount === "number" && request.refundAmount > 0) return request.refundAmount;
841
+ const amount = request.items.reduce((sum, returnItem) => {
842
+ const orderItem = order.items.find((item) => resolveOrderItemId(item) === returnItem.orderItemId);
843
+ if (!orderItem || orderItem.quantity < 1) return sum;
844
+ const quantity = Math.min(returnItem.quantity, orderItem.quantity);
845
+ const line = orderItem.unitPrice * quantity;
846
+ if ((order.priceMode ?? "gross") === "net") {
847
+ const tax = Math.round((orderItem.taxAmount ?? 0) * quantity / orderItem.quantity);
848
+ return sum + line + tax;
849
+ }
850
+ return sum + line;
851
+ }, 0);
852
+ const remaining = typeof order.total === "number" ? Math.max(0, order.total - alreadyRefunded) : amount;
853
+ return Math.max(0, Math.min(amount, remaining));
854
+ };
855
+ //#endregion
632
856
  //#region src/budget/types.ts
633
857
  const PeriodTypeLabels = {
634
858
  0: "One-time",
@@ -878,7 +1102,8 @@ const NativeApps = {
878
1102
  Artifacts: "artifacts",
879
1103
  Social: "social",
880
1104
  Studio: "studio",
881
- Loyalty: "loyalty"
1105
+ Loyalty: "loyalty",
1106
+ Documents: "documents"
882
1107
  };
883
1108
  const nativeAppSlugs = Object.values(NativeApps);
884
1109
  const isNativeApp = (value) => typeof value === "string" && nativeAppSlugs.includes(value);
@@ -893,6 +1118,7 @@ const UserPermissions = {
893
1118
  ContentPublish: "content-publish",
894
1119
  TeamMemberInvite: "team-member-invite",
895
1120
  TeamMemberRemove: "team-member-remove",
1121
+ TeamMemberRole: "team-member-role",
896
1122
  TeamSecretView: "team-secret-view",
897
1123
  TeamSecretManage: "team-secret-manage",
898
1124
  AgentExecute: "agent-execute",
@@ -917,6 +1143,7 @@ const PermissionPresetNames = {
917
1143
  SecretManage: "secretManage",
918
1144
  TeamMemberInvite: "teamMemberInvite",
919
1145
  TeamMemberRemove: "teamMemberRemove",
1146
+ TeamMemberRole: "teamMemberRole",
920
1147
  AgentWalletManage: "agentWalletManage",
921
1148
  SwarmCreate: "swarmCreate"
922
1149
  };
@@ -930,6 +1157,7 @@ const permissionPresetRules = {
930
1157
  secretManage: teamManageOr(UserPermissions.TeamSecretManage),
931
1158
  teamMemberInvite: teamManageOr(UserPermissions.TeamMemberInvite),
932
1159
  teamMemberRemove: teamManageOr(UserPermissions.TeamMemberRemove),
1160
+ teamMemberRole: teamManageOr(UserPermissions.TeamMemberRole),
933
1161
  agentWalletManage: teamManageOr(UserPermissions.AgentWalletManage),
934
1162
  swarmCreate: teamManageOr(UserPermissions.SwarmCreate)
935
1163
  };
@@ -974,6 +1202,14 @@ const InviteableTeamRoles = [
974
1202
  ];
975
1203
  const DefaultInviteTeamRole = UserRoles.Member;
976
1204
  const inviteableTeamRoles = () => [...InviteableTeamRoles];
1205
+ /** Roles assignable to an existing member — unlike invites, ownership can be transferred. */
1206
+ const AssignableTeamRoles = [
1207
+ UserRoles.Owner,
1208
+ UserRoles.Admin,
1209
+ UserRoles.Developer,
1210
+ UserRoles.Member
1211
+ ];
1212
+ const assignableTeamRoles = () => [...AssignableTeamRoles];
977
1213
  const defaultInviteTeamRole = () => DefaultInviteTeamRole;
978
1214
  //#endregion
979
- export { AUTOMATION_SEND_CHANNELS, AddressTypes, AgentToolingAccess, BillingManageAccess, CAMPAIGN_CHANNELS, CONTACT_MESSAGE_SEND_WARP, ContractCategoryDisplayNames, DefaultAppLocale, DefaultCodeMetadata, DefaultInviteTeamRole, FulfillmentMethods, FulfillmentStatuses, HttpService, InviteableTeamRoles, JoaiUrls, NATIVE_AUTH_TOKEN_PARAM, NativeApps, OG_IMAGE_CONFIG, OG_IMAGE_HEIGHT, OG_IMAGE_WIDTH, ORDER_FULFILLMENT_STATUSES, ORDER_POST_CHECKOUT_STATUSES, OUTBOUND_CHANNELS, OgImageColors, PLATFORM_CURRENCY, PeriodTypeLabels, PeriodTypeOptions, PermissionPresetNames, ReturnStatuses, ReturnUpdateStatuses, SkillMediaBindingRole, SkillScope, SkillType, SupportedAppLocales, TeamManageAccess, TeamViewAccess, UNIFIED_MCP_SERVER_NAME, UserPermissions, UserRoles, abbreviateNumber, addressTypeValues, automationSendChannel, buildAgentMcpServerName, buildAgentSetupClipboardText, buildAppSetupClipboardText, buildFeatureSetupClipboardText, buildMcpServerName, canOpenFulfillmentFlow, canTransitionOrderStatus, cartLineTotal, cartPayableTotal, createCursorMcpInstallLink, defaultInviteTeamRole, documentShareUrl, firstValidationMessage, formatAddressSnapshotLines, fulfillmentMethodValues, fulfillmentStatusValues, getAgentSetupPromptUrl, getAgentsBaseUrl, getApiBaseUrl, getAppBaseUrl, getAssetAppLogoUrl, getAssetChainLogoUrl, getAssetPartnerLogoUrl, getAssetTokenLogoUrl, getAssetWalletLogoUrl, getCortexBaseUrl, getDigestBaseUrl, getDocumentsBaseUrl, getEditableOrderStatuses, getFormsBaseUrl, getFulfillmentFlowAction, getFulfillmentFlowStepperIndex, getGenBaseUrl, getInvoiceEditableStatuses, getLoginUrl, getMcpServerAgentUrl, getMcpServerUrl, getPickupEditableStatuses, getPickupNextAction, getShippingEditableStatuses, getSiteUrl, getSitesBaseUrl, getSitesRootDomain, getStoreBaseUrl, getWarpScreenshotUrl, hasAllPermissions, hasAllTeamPermissions, hasAnyPermissions, hasPermission, hasTeamPermission, integrationSlugForChannel, inviteableTeamRoles, isAutomationSendChannelId, isCampaignChannelId, isCompleteAddressInput, isContactMessageAction, isContactMessageWarpId, isFileFormatAudio, isFileFormatImage, isFileFormatVideo, isNativeApp, isNativeAppAccessible, isNativeAppGated, isNativeAppLocked, isOrderCanceled, isOrderInFulfillment, isOrderPending, isOrderPostCheckout, isPickupOrderReady, isPickupReady, isSupportedAppLocale, isTeamFeatureAccessible, isTeamFeatureGated, isTeamFeatureLocked, isUnifiedMcpUrl, isUserRole, localize, nativeAppSlugs, normalizeAutomationActionType, ogImageUrl, outboundChannel, permissionPresetRules, resolveAccessCatalog, resolveAgentMcpUrl, resolveConnectMcpUrl, resolveFulfillmentFlowStep, resolveOrderStatusDisplay, resolveTeamCurrency, resolveWarpText, returnStatusValues, returnUpdateStatusValues, sanitizeAlphanumeric, sanitizeNumeric, slugifyMcpSegment, teamCategories, toCurrencyDisplay, toHexEncodedNonce, toNameInitials, toNftId, toPreviewText, userPermissionValues, userRoleValues, validationMessageFor, withRetry };
1215
+ export { AUTOMATION_SEND_CHANNELS, AddressTypes, AgentToolingAccess, AssignableTeamRoles, BillingManageAccess, CAMPAIGN_CHANNELS, CONTACT_MESSAGE_SEND_WARP, ContractCategoryDisplayNames, DefaultAppLocale, DefaultCodeMetadata, DefaultInviteTeamRole, FulfillmentMethods, FulfillmentStatuses, HttpService, InviteableTeamRoles, JoaiUrls, NATIVE_AUTH_TOKEN_PARAM, NativeApps, OG_IMAGE_CONFIG, OG_IMAGE_HEIGHT, OG_IMAGE_WIDTH, ORDER_FULFILLMENT_STATUSES, ORDER_POST_CHECKOUT_STATUSES, OUTBOUND_CHANNELS, OgImageColors, PLATFORM_CURRENCY, PeriodTypeLabels, PeriodTypeOptions, PermissionPresetNames, RETURNABLE_FULFILLMENT_STATUSES, ReturnStatuses, ReturnUpdateStatuses, SkillMediaBindingRole, SkillScope, SkillType, SupportedAppLocales, TeamManageAccess, TeamViewAccess, UNIFIED_MCP_SERVER_NAME, UserPermissions, UserRoles, abbreviateNumber, addressTypeValues, assignableTeamRoles, automationSendChannel, buildAgentMcpServerName, buildAgentSetupClipboardText, buildAppSetupClipboardText, buildFeatureSetupClipboardText, buildMcpServerName, buildReturnCreatePayloads, canOpenFulfillmentFlow, canTransitionOrderStatus, cartLineTotal, cartPayableTotal, containedImageDimensions, createCursorMcpInstallLink, cropOutputDimensions, defaultInviteTeamRole, documentShareUrl, firstValidationMessage, formatAddressSnapshotLines, fulfillmentMethodValues, fulfillmentStatusValues, getAgentSetupPromptUrl, getAgentsBaseUrl, getApiBaseUrl, getAppBaseUrl, getAssetAppLogoUrl, getAssetChainLogoUrl, getAssetPartnerLogoUrl, getAssetTokenLogoUrl, getAssetWalletLogoUrl, getCortexBaseUrl, getDigestBaseUrl, getDocumentsBaseUrl, getEditableOrderStatuses, getFormsBaseUrl, getFulfillmentFlowAction, getFulfillmentFlowStepperIndex, getGenBaseUrl, getInvoiceEditableStatuses, getLoginUrl, getMcpServerAgentUrl, getMcpServerUrl, getPickupEditableStatuses, getPickupNextAction, getReturnRefundAmount, getReturnableItems, getShippingEditableStatuses, getSiteUrl, getSitesBaseUrl, getSitesRootDomain, getStoreBaseUrl, getWarpScreenshotUrl, hasAllPermissions, hasAllTeamPermissions, hasAnyPermissions, hasPermission, hasTeamPermission, imageCropRectangle, integrationSlugForChannel, inviteableTeamRoles, isAutomationSendChannelId, isCampaignChannelId, isCompleteAddressInput, isContactMessageAction, isContactMessageWarpId, isFileFormatAudio, isFileFormatImage, isFileFormatVideo, isNativeApp, isNativeAppAccessible, isNativeAppGated, isNativeAppLocked, isOrderCanceled, isOrderInFulfillment, isOrderPending, isOrderPostCheckout, isPickupOrderReady, isPickupReady, isSupportedAppLocale, isTeamFeatureAccessible, isTeamFeatureGated, isTeamFeatureLocked, isUnifiedMcpUrl, isUserRole, localize, nativeAppSlugs, normalizeAutomationActionType, ogImageUrl, outboundChannel, permissionPresetRules, resolveAccessCatalog, resolveAgentMcpUrl, resolveConnectMcpUrl, resolveFulfillmentFlowStep, resolveOrderStatusDisplay, resolveTeamCurrency, resolveWarpText, returnStatusValues, returnUpdateStatusValues, sanitizeAlphanumeric, sanitizeNumeric, slugifyMcpSegment, teamCategories, toCurrencyDisplay, toHexEncodedNonce, toNameInitials, toNftId, toPreviewText, userPermissionValues, userRoleValues, validateImageDimensions, validationMessageFor, withRetry };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joai/core",
3
- "version": "4.16.0",
3
+ "version": "5.0.1",
4
4
  "description": "JoAi shared types, HTTP service, and config",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -10,10 +10,6 @@
10
10
  ".": {
11
11
  "import": "./dist/index.mjs",
12
12
  "types": "./dist/index.d.mts"
13
- },
14
- "./react": {
15
- "import": "./dist/react/index.mjs",
16
- "types": "./dist/react/index.d.mts"
17
13
  }
18
14
  },
19
15
  "files": [
@@ -26,17 +22,10 @@
26
22
  "test:watch": "jest --watch"
27
23
  },
28
24
  "peerDependencies": {
29
- "@joai/warps": ">=4.0.0",
30
- "react": ">=18.0.0"
31
- },
32
- "peerDependenciesMeta": {
33
- "react": {
34
- "optional": true
35
- }
25
+ "@joai/warps": ">=4.0.0"
36
26
  },
37
27
  "devDependencies": {
38
28
  "@types/jest": "^30.0.0",
39
- "@types/react": "^19.2.14",
40
29
  "jest": "^30.3.0",
41
30
  "ts-jest": "^29.4.6",
42
31
  "tsdown": "^0.21.0",
@@ -1,362 +0,0 @@
1
- 'use client';
2
- import * as _$react from "react";
3
- import { ReactNode } from "react";
4
-
5
- //#region src/react/types.d.ts
6
- type EmbedChatItem = {
7
- content: string;
8
- byUser: boolean;
9
- streaming?: boolean;
10
- error?: boolean;
11
- };
12
- //#endregion
13
- //#region src/react/guest.d.ts
14
- declare function getOrCreateGuestId(storageKey?: string): string;
15
- declare function loadRoom(storageKey: string): string | null;
16
- declare function saveRoom(storageKey: string, roomId: string): void;
17
- declare function clearRoom(storageKey: string): void;
18
- //#endregion
19
- //#region src/react/stream-connection.d.ts
20
- type StreamDataHandler = (data: unknown) => void;
21
- declare function openEventSourceStream(url: string, onData: StreamDataHandler, options?: {
22
- withCredentials?: boolean;
23
- onError?: (e: unknown) => void;
24
- onConnectionError?: (e: Event | null) => void;
25
- }): () => void;
26
- //#endregion
27
- //#region src/react/components.d.ts
28
- type ListClassNames = {
29
- container?: string;
30
- userBubble?: string;
31
- agentBubble?: string;
32
- errorBubble?: string;
33
- thinkingWrapper?: string;
34
- dot?: string;
35
- };
36
- type EmbedChatListProps = {
37
- items: EmbedChatItem[];
38
- agentName: string;
39
- awaitingResponse: boolean;
40
- classNames?: ListClassNames;
41
- renderEmptyState?: () => ReactNode;
42
- renderAgentContent?: (item: EmbedChatItem) => ReactNode;
43
- };
44
- declare function EmbedChatList({
45
- items,
46
- agentName,
47
- awaitingResponse,
48
- classNames,
49
- renderEmptyState,
50
- renderAgentContent
51
- }: EmbedChatListProps): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | _$react.ReactPortal | _$react.ReactElement<unknown, string | _$react.JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | _$react.JSX.Element | null | undefined;
52
- type EmbedChatInputProps = {
53
- onSend: (message: string) => void;
54
- disabled: boolean;
55
- placeholder?: string;
56
- maxLength?: number;
57
- classNames?: {
58
- container?: string;
59
- textarea?: string;
60
- };
61
- renderSendButton: (props: {
62
- onClick: () => void;
63
- disabled: boolean;
64
- }) => ReactNode;
65
- };
66
- declare function EmbedChatInput({
67
- onSend,
68
- disabled,
69
- placeholder,
70
- maxLength,
71
- classNames,
72
- renderSendButton
73
- }: EmbedChatInputProps): _$react.JSX.Element;
74
- //#endregion
75
- //#region src/http/types.d.ts
76
- interface IHttpService {
77
- readonly baseUrl: string;
78
- readonly authToken: string | null;
79
- onNetworkError: (error: Error) => void;
80
- onUnauthorized: (response: IAppResponse<any>) => void;
81
- onSuccessfulResponse: (response: IAppResponse<any>) => void;
82
- onMaintenanceMode: (response: IAppResponse<any>) => void;
83
- onTooManyRequests: (response: IAppResponse<any>) => void;
84
- get: <T>(url: string, options?: IRequestOptions) => Promise<IAppResponse<T>>;
85
- post: <T>(url: string, data?: any, options?: IRequestOptions) => Promise<IAppResponse<T>>;
86
- put: <T>(url: string, data?: any, options?: IRequestOptions) => Promise<IAppResponse<T>>;
87
- patch: <T>(url: string, data?: any, options?: IRequestOptions) => Promise<IAppResponse<T>>;
88
- delete: <T>(url: string, options?: IRequestOptions) => Promise<IAppResponse<T>>;
89
- }
90
- interface IAppResponse<T> {
91
- data: T;
92
- meta?: any;
93
- body: any;
94
- errors: any;
95
- ok: boolean;
96
- paymentRequired: boolean;
97
- noContent: boolean;
98
- original: Response;
99
- }
100
- interface IRequestOptions {
101
- isJson?: boolean;
102
- headers?: {
103
- [name: string]: string;
104
- };
105
- }
106
- //#endregion
107
- //#region src/react/use-embed-chat.d.ts
108
- type Options = {
109
- agentUuid: string | null;
110
- http: IHttpService;
111
- cortexBaseUrl: string;
112
- guestId: string;
113
- roomStorageKey: string;
114
- enabled?: boolean;
115
- extraPayload?: Record<string, unknown>;
116
- };
117
- declare function useEmbedChat({
118
- agentUuid,
119
- http,
120
- cortexBaseUrl,
121
- guestId,
122
- roomStorageKey,
123
- enabled,
124
- extraPayload
125
- }: Options): {
126
- items: EmbedChatItem[];
127
- setItems: _$react.Dispatch<_$react.SetStateAction<EmbedChatItem[]>>;
128
- sending: boolean;
129
- awaitingResponse: boolean;
130
- roomId: string | null;
131
- setRoomId: _$react.Dispatch<_$react.SetStateAction<string | null>>;
132
- send: (message: string) => Promise<void>;
133
- connectionError: boolean;
134
- };
135
- //#endregion
136
- //#region src/content/types.d.ts
137
- type ContentStatus = 'draft' | 'published' | 'archived';
138
- type ContentFieldType = 'text' | 'richText' | 'number' | 'boolean' | 'url' | 'image' | 'media' | 'list' | 'object' | 'reference';
139
- type ContentField = {
140
- key: string;
141
- type: ContentFieldType;
142
- label?: string;
143
- required?: boolean;
144
- };
145
- type ContentDefinition = {
146
- fields: ContentField[];
147
- };
148
- type ContentValue = string | number | boolean | null | ContentValue[] | {
149
- [key: string]: ContentValue;
150
- };
151
- type ContentEntry = {
152
- key: string;
153
- value: ContentValue;
154
- updatedAt: string;
155
- };
156
- type ContentVersion = {
157
- id: string;
158
- number: number;
159
- status: ContentStatus;
160
- entries: ContentEntry[];
161
- snapshotHash: string;
162
- origin: string;
163
- createdAt: string;
164
- updatedAt: string;
165
- publishedAt: string | null;
166
- };
167
- type Content = {
168
- id: string;
169
- name: string;
170
- slug: string;
171
- schema: string | null;
172
- definition: ContentDefinition;
173
- metadata: Record<string, ContentValue>;
174
- version: ContentVersion | null;
175
- createdAt: string;
176
- updatedAt: string;
177
- };
178
- type ContentPatch = {
179
- key: string;
180
- value: ContentValue;
181
- };
182
- type PatchContentInput = {
183
- expectedVersion: number;
184
- patches: ContentPatch[];
185
- origin?: 'api' | 'inlineEditor' | 'joai' | 'mcp';
186
- publish?: boolean;
187
- };
188
- //#endregion
189
- //#region src/json/types.d.ts
190
- type JsonValue = string | number | boolean | null | JsonValue[] | {
191
- [key: string]: JsonValue;
192
- };
193
- //#endregion
194
- //#region src/media/types.d.ts
195
- type MediaLibraryAttributes = {
196
- uuid: string;
197
- order: number;
198
- name: string;
199
- custom_properties: {
200
- [key: string]: JsonValue;
201
- };
202
- preview_url: null | string;
203
- original_url: null | string;
204
- extension?: string;
205
- size?: number;
206
- };
207
- type MediaMime = 'image/png' | 'image/jpeg' | 'image/gif' | 'image/webp' | 'video/mp4' | 'video/webm' | 'video/quicktime' | 'video/x-msvideo' | 'audio/mpeg' | 'audio/mp3' | 'audio/ogg' | 'audio/wav' | 'audio/webm' | 'audio/aac' | 'audio/m4a' | 'audio/m4b' | 'audio/m4p' | 'audio/m4v' | 'application/pdf' | 'application/json';
208
- type Media = {
209
- id: string;
210
- name: string;
211
- mime: MediaMime;
212
- url: string;
213
- library: MediaLibraryAttributes;
214
- tags?: string[];
215
- copyright?: string | null;
216
- aiGenerated?: boolean;
217
- };
218
- //#endregion
219
- //#region src/react/content/types.d.ts
220
- type ContentResource = {
221
- get: (contentId: string) => Promise<{
222
- ok: boolean;
223
- data?: Content;
224
- }>;
225
- getPublic: (slug: string) => Promise<{
226
- ok: boolean;
227
- data?: Content;
228
- }>;
229
- patch: (contentId: string, input: PatchContentInput) => Promise<{
230
- ok: boolean;
231
- data?: ContentVersion;
232
- }>;
233
- publish: (contentId: string, expectedVersion: number) => Promise<{
234
- ok: boolean;
235
- data?: ContentVersion;
236
- }>;
237
- };
238
- type ContentClient = {
239
- content: ContentResource;
240
- media?: {
241
- list: (team: string, params?: {
242
- page?: number;
243
- mime?: string;
244
- }) => Promise<{
245
- ok: boolean;
246
- data?: Media[];
247
- }>;
248
- upload: (data: {
249
- team: string;
250
- file: File;
251
- type: 'output';
252
- label?: string;
253
- }) => Promise<{
254
- ok: boolean;
255
- data?: Media;
256
- }>;
257
- };
258
- };
259
- type ContentContextValue = {
260
- content: Content | null;
261
- values: Record<string, ContentValue>;
262
- isLoading: boolean;
263
- isSaving: boolean;
264
- isEditing: boolean;
265
- isDirty: boolean;
266
- canEdit: boolean;
267
- canPublish: boolean;
268
- teamSlug?: string;
269
- media?: ContentClient['media'];
270
- error: string | null;
271
- setEditing: (editing: boolean) => void;
272
- setValue: (key: string, value: ContentValue) => void;
273
- save: () => Promise<boolean>;
274
- publish: () => Promise<boolean>;
275
- };
276
- type ContentProviderProp = {
277
- children: ReactNode;
278
- client: ContentClient;
279
- contentId?: string;
280
- publicSlug?: string;
281
- initialContent?: Content;
282
- canEdit?: boolean;
283
- canPublish?: boolean;
284
- teamSlug?: string;
285
- };
286
- type EditableContentProp = {
287
- field: string;
288
- as?: 'span' | 'p' | 'div' | 'h1' | 'h2' | 'h3';
289
- className?: string;
290
- placeholder?: string;
291
- multiline?: boolean;
292
- };
293
- type EditableImageProp = {
294
- field: string;
295
- alt?: string;
296
- className?: string;
297
- onSelect?: () => void;
298
- inputClassName?: string;
299
- placeholder?: string;
300
- };
301
- type ContentToolbarProp = {
302
- className?: string;
303
- confirmPublish?: () => boolean | Promise<boolean>;
304
- labels?: {
305
- edit?: string;
306
- save?: string;
307
- publish?: string;
308
- cancel?: string;
309
- };
310
- };
311
- //#endregion
312
- //#region src/react/content/provider.d.ts
313
- declare function ContentProvider({
314
- children,
315
- client,
316
- contentId,
317
- publicSlug,
318
- initialContent,
319
- canEdit,
320
- canPublish,
321
- teamSlug
322
- }: ContentProviderProp): _$react.JSX.Element;
323
- //#endregion
324
- //#region src/react/content/editable-content.d.ts
325
- declare function EditableContent({
326
- field,
327
- as: Tag,
328
- className,
329
- placeholder,
330
- multiline
331
- }: EditableContentProp): _$react.JSX.Element;
332
- declare function useContentValue<T extends ContentValue = ContentValue>(key: string): T | undefined;
333
- //#endregion
334
- //#region src/react/content/editable-image.d.ts
335
- declare function EditableImage({
336
- field,
337
- alt,
338
- className,
339
- inputClassName,
340
- placeholder,
341
- onSelect
342
- }: EditableImageProp): _$react.JSX.Element | null;
343
- //#endregion
344
- //#region src/react/content/toolbar.d.ts
345
- declare function ContentToolbar({
346
- className,
347
- labels,
348
- confirmPublish
349
- }: ContentToolbarProp): _$react.JSX.Element | null;
350
- //#endregion
351
- //#region src/react/content/content-editor.d.ts
352
- type Prop = ContentProviderProp & {
353
- children: ReactNode;
354
- title?: string;
355
- };
356
- declare function ContentEditor({
357
- children,
358
- title,
359
- ...provider
360
- }: Prop): _$react.JSX.Element;
361
- //#endregion
362
- export { ContentClient, ContentContextValue, ContentEditor, ContentProvider, ContentProviderProp, ContentResource, ContentToolbar, ContentToolbarProp, EditableContent, EditableContentProp, EditableImage, EditableImageProp, EmbedChatInput, EmbedChatItem, EmbedChatList, StreamDataHandler, clearRoom, getOrCreateGuestId, loadRoom, openEventSourceStream, saveRoom, useContentValue, useEmbedChat };