@joai/sdk 4.7.0 → 5.0.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 (3) hide show
  1. package/dist/index.d.mts +626 -3930
  2. package/dist/index.mjs +172 -205
  3. package/package.json +5 -2
package/dist/index.mjs CHANGED
@@ -1,205 +1,5 @@
1
+ import { HttpService, getApiBaseUrl, isCompleteAddressInput } from "@joai/core";
1
2
  import { WarpClient } from "@joai/warps";
2
- //#region ../core/dist/index.mjs
3
- const toSubdomainBaseUrl = (env, subdomain) => {
4
- if (env === "local") return `http://${subdomain}.joai--api.test`;
5
- if (env === "devnet") return `https://devnet-${subdomain}.joai.ai`;
6
- if (env === "testnet") return `https://testnet-${subdomain}.joai.ai`;
7
- return `https://${subdomain}.joai.ai`;
8
- };
9
- const getApiBaseUrl = (env) => toSubdomainBaseUrl(env, "api");
10
- var HttpService = class {
11
- constructor(baseUrl, cookies, authToken, options) {
12
- this.get = async (url, options) => await this.request("GET", url, void 0, options);
13
- this.post = async (url, data, options) => await this.request("POST", url, data, options);
14
- this.put = async (url, data, options) => await this.request("PUT", url, data, options);
15
- this.patch = async (url, data, options) => await this.request("PATCH", url, data, options);
16
- this.delete = async (url, options) => await this.request("DELETE", url, void 0, options);
17
- this.getCsrfToken = () => {
18
- if (typeof document === "undefined") return null;
19
- const match = document.cookie.match(/(?:^|;\s*)XSRF-TOKEN=([^;]*)/);
20
- return match ? decodeURIComponent(match[1]) : null;
21
- };
22
- this.request = async (httpMethod, url, data, options, isRetry) => {
23
- const requestUrl = url.startsWith("http") ? url : `${this.baseUrl}/${url}`;
24
- const requestInfo = this.getRequestInfo(httpMethod, data, options);
25
- const response = await this.convertResponse(this.fetchFn(requestUrl, requestInfo));
26
- if (response.original.status === 419 && !isRetry) {
27
- await this.refreshCsrfToken();
28
- return this.request(httpMethod, url, data, options, true);
29
- }
30
- if (response.original.status === 401) this.onUnauthorized(response);
31
- if (response.original.status === 429) {
32
- console.warn(`[HTTP 429] ${httpMethod} ${requestUrl}`);
33
- this.onTooManyRequests(response);
34
- }
35
- if (response.original.status === 503) this.onMaintenanceMode(response);
36
- if (response.ok) this.onSuccessfulResponse(response);
37
- return response;
38
- };
39
- this.refreshCsrfToken = async () => {
40
- await this.fetchFn(`${this.csrfBaseUrl}/sanctum/csrf-cookie`, {
41
- method: "GET",
42
- credentials: "include",
43
- headers: {
44
- Accept: "application/json",
45
- ...this.headers
46
- }
47
- });
48
- };
49
- this.convertResponse = async (apiRes) => {
50
- try {
51
- const response = await apiRes;
52
- const body = response.status === 204 ? {} : await response.json();
53
- return {
54
- data: body.data || null,
55
- meta: body.meta,
56
- body,
57
- errors: body.errors || [],
58
- ok: response.ok && !(body.errors && body.errors.length > 0),
59
- noContent: response.status === 204,
60
- paymentRequired: response.status === 402,
61
- original: response
62
- };
63
- } catch (error) {
64
- this.onNetworkError(error);
65
- const anyError = error;
66
- return {
67
- data: {},
68
- errors: anyError.response && (anyError.response.data || {}).errors,
69
- body: {},
70
- ok: false,
71
- noContent: false,
72
- paymentRequired: false,
73
- original: anyError
74
- };
75
- }
76
- };
77
- this.getRequestInfo = (httpMethod = "GET", data, options) => {
78
- const isJson = options?.isJson !== false;
79
- const isFormData = data instanceof FormData;
80
- const situationalHeaders = options?.headers || {};
81
- const csrfToken = httpMethod !== "GET" ? this.getCsrfToken() : null;
82
- const headers = {
83
- Accept: "application/json",
84
- ...this.authToken ? { Authorization: `Bearer ${this.authToken}` } : {},
85
- ...csrfToken ? { "X-XSRF-TOKEN": csrfToken } : {},
86
- ...situationalHeaders,
87
- ...this.headers
88
- };
89
- if (isJson && !isFormData) headers["Content-Type"] = "application/json";
90
- return {
91
- method: httpMethod,
92
- credentials: "include",
93
- headers,
94
- body: httpMethod !== "GET" ? isJson && !isFormData ? JSON.stringify(data || {}) : data : void 0
95
- };
96
- };
97
- this.baseUrl = baseUrl;
98
- this.authToken = authToken || null;
99
- this.fetchFn = options?.customFetch ?? globalThis.fetch.bind(globalThis);
100
- this.csrfBaseUrl = options?.csrfBaseUrl ?? baseUrl;
101
- this.onNetworkError = () => {};
102
- this.onSuccessfulResponse = () => {};
103
- this.onUnauthorized = () => {};
104
- this.onMaintenanceMode = () => {};
105
- this.onTooManyRequests = () => {};
106
- this.headers = {
107
- ...options?.refererUrl ? { Referer: options.refererUrl } : {},
108
- ...cookies ? { Cookie: cookies } : {},
109
- ...options?.headers ?? {}
110
- };
111
- }
112
- };
113
- Object.values({
114
- Billing: "billing",
115
- Shipping: "shipping"
116
- });
117
- Object.values({
118
- Pending: "pending",
119
- ReadyForPickup: "ready_for_pickup",
120
- Shipped: "shipped",
121
- Delivered: "delivered",
122
- Cancelled: "cancelled"
123
- });
124
- Object.values({
125
- Shipping: "shipping",
126
- Pickup: "pickup"
127
- });
128
- Object.values({
129
- Requested: "requested",
130
- Approved: "approved",
131
- Rejected: "rejected",
132
- Received: "received",
133
- Closed: "closed"
134
- });
135
- Object.values({
136
- Approved: "approved",
137
- Rejected: "rejected",
138
- Received: "received",
139
- Closed: "closed"
140
- });
141
- Object.values({
142
- Board: "board",
143
- Shop: "shop",
144
- SmartLinks: "smart-links",
145
- Contacts: "contacts",
146
- Forms: "forms",
147
- Appointments: "appointments",
148
- Workspace: "workspace",
149
- Meeting: "meeting",
150
- VoiceDictation: "voice-dictation",
151
- Heartbeats: "heartbeats",
152
- Wallets: "wallets",
153
- Campaigns: "campaigns",
154
- News: "news",
155
- Sites: "sites",
156
- Contracts: "contracts",
157
- Artifacts: "artifacts"
158
- });
159
- /** Permission string values — must match joai--api `UserPermission::value`. */
160
- const UserPermissions = {
161
- BillingManage: "billing-manage",
162
- TeamManage: "team-manage",
163
- TeamView: "team-view",
164
- ContentEdit: "content-edit",
165
- ContentPublish: "content-publish",
166
- TeamMemberInvite: "team-member-invite",
167
- TeamMemberRemove: "team-member-remove",
168
- TeamSecretView: "team-secret-view",
169
- TeamSecretManage: "team-secret-manage",
170
- AgentExecute: "agent-execute",
171
- AgentWalletManage: "agent-wallet-manage",
172
- SwarmCreate: "swarm-create"
173
- };
174
- Object.values(UserPermissions);
175
- const teamManageOr = (...permissions) => ({ permissionsAny: [UserPermissions.TeamManage, ...permissions] });
176
- const permissionPresetRules = {
177
- teamManage: { permissions: [UserPermissions.TeamManage] },
178
- teamView: teamManageOr(UserPermissions.TeamView),
179
- billingManage: teamManageOr(UserPermissions.BillingManage),
180
- agentTooling: teamManageOr(UserPermissions.AgentExecute),
181
- secretView: teamManageOr(UserPermissions.TeamSecretView),
182
- secretManage: teamManageOr(UserPermissions.TeamSecretManage),
183
- teamMemberInvite: teamManageOr(UserPermissions.TeamMemberInvite),
184
- teamMemberRemove: teamManageOr(UserPermissions.TeamMemberRemove),
185
- agentWalletManage: teamManageOr(UserPermissions.AgentWalletManage),
186
- swarmCreate: teamManageOr(UserPermissions.SwarmCreate)
187
- };
188
- permissionPresetRules.teamManage;
189
- permissionPresetRules.teamView;
190
- permissionPresetRules.agentTooling;
191
- permissionPresetRules.billingManage;
192
- /** Team member roles — values must match joai--api `UserRole::toName()`. */
193
- const UserRoles = {
194
- Owner: "owner",
195
- Admin: "admin",
196
- Member: "member",
197
- Developer: "developer"
198
- };
199
- Object.values(UserRoles);
200
- UserRoles.Member, UserRoles.Developer, UserRoles.Admin;
201
- UserRoles.Member;
202
- //#endregion
203
3
  //#region src/resources/agents.ts
204
4
  var AgentsResource = class {
205
5
  constructor(http) {
@@ -505,6 +305,171 @@ function createGuestCommerceStorage(options) {
505
305
  };
506
306
  }
507
307
  //#endregion
308
+ //#region src/commerce/guestCheckout.ts
309
+ var GuestCheckoutError = class extends Error {
310
+ constructor(code) {
311
+ super(code);
312
+ this.code = code;
313
+ this.name = "GuestCheckoutError";
314
+ }
315
+ };
316
+ const createEmptyGuestCheckoutState = (options = {}) => ({
317
+ cart: [],
318
+ identity: {
319
+ name: "",
320
+ email: "",
321
+ phone: ""
322
+ },
323
+ fulfillmentMethod: options.fulfillmentMethod ?? "shipping",
324
+ address: {
325
+ name: "",
326
+ addressOne: "",
327
+ city: "",
328
+ zipCode: "",
329
+ country: options.country ?? "AT",
330
+ phone: ""
331
+ },
332
+ shippingRateId: null,
333
+ fulfillmentLocationId: null
334
+ });
335
+ const toGuestCheckoutDraft = (state) => ({
336
+ cart: state.cart,
337
+ fulfillmentMethod: state.fulfillmentMethod,
338
+ shippingRateId: state.shippingRateId,
339
+ fulfillmentLocationId: state.fulfillmentLocationId
340
+ });
341
+ const assertGuestCheckoutReady = (state, pickupLocationIds = []) => {
342
+ if (!state.cart.length) throw new GuestCheckoutError("empty_cart");
343
+ if (!state.identity.name.trim() || !state.identity.email.trim()) throw new GuestCheckoutError("missing_identity");
344
+ if (state.fulfillmentMethod === "pickup" && !state.identity.phone?.trim()) throw new GuestCheckoutError("missing_pickup_phone");
345
+ const address = {
346
+ ...state.address,
347
+ name: state.identity.name
348
+ };
349
+ if (state.fulfillmentMethod === "shipping" && !isCompleteAddressInput(address)) throw new GuestCheckoutError("incomplete_shipping_address");
350
+ if (state.fulfillmentMethod === "pickup" && !state.fulfillmentLocationId && !pickupLocationIds[0]) throw new GuestCheckoutError("pickup_unavailable");
351
+ };
352
+ const buildGuestCheckoutSnapshotRequest = (state, options = {}) => {
353
+ assertGuestCheckoutReady(state, options.pickupLocationIds ?? []);
354
+ const items = state.cart.map(({ id, type, quantity }) => ({
355
+ id,
356
+ type,
357
+ quantity
358
+ }));
359
+ const address = {
360
+ ...state.address,
361
+ name: state.identity.name
362
+ };
363
+ if (state.fulfillmentMethod === "shipping") {
364
+ const rates = options.shippingRates ?? [];
365
+ const rate = state.shippingRateId == null ? rates[0] : rates.find((item) => item.id === state.shippingRateId);
366
+ if (!rate) throw new GuestCheckoutError("missing_shipping_rate");
367
+ return {
368
+ fulfillmentMethod: "shipping",
369
+ items,
370
+ billingAddress: {
371
+ ...address,
372
+ type: "billing"
373
+ },
374
+ shippingAddress: {
375
+ ...address,
376
+ type: "shipping"
377
+ },
378
+ shippingRateId: rate.id
379
+ };
380
+ }
381
+ return {
382
+ fulfillmentMethod: "pickup",
383
+ items,
384
+ fulfillmentLocationId: state.fulfillmentLocationId || options.pickupLocationIds?.[0] || null
385
+ };
386
+ };
387
+ async function createGuestCheckoutSnapshot(options) {
388
+ const { client, team, state } = options;
389
+ const items = state.cart.map(({ id, type, quantity }) => ({
390
+ id,
391
+ type,
392
+ quantity
393
+ }));
394
+ let shippingRates = options.shippingRates ?? [];
395
+ if (state.fulfillmentMethod === "shipping" && !options.shippingRates) {
396
+ const address = {
397
+ ...state.address,
398
+ name: state.identity.name
399
+ };
400
+ const ratesResponse = await client.commerce.checkout.shippingRates(team, {
401
+ address: {
402
+ country: address.country,
403
+ zipCode: address.zipCode
404
+ },
405
+ items
406
+ });
407
+ if (!ratesResponse.ok || !Array.isArray(ratesResponse.data)) throw new GuestCheckoutError("rates_failed");
408
+ shippingRates = ratesResponse.data;
409
+ }
410
+ const snapshotRequest = buildGuestCheckoutSnapshotRequest(state, {
411
+ shippingRates,
412
+ pickupLocationIds: options.pickupLocationIds
413
+ });
414
+ const snapshot = await client.commerce.checkout.snapshot(team, snapshotRequest);
415
+ if (!snapshot.ok || !snapshot.data?.id || !snapshot.data.accessToken) throw new GuestCheckoutError("snapshot_failed");
416
+ return {
417
+ snapshot: {
418
+ ...snapshot.data,
419
+ accessToken: snapshot.data.accessToken
420
+ },
421
+ shippingRates,
422
+ shippingRateId: typeof snapshotRequest.shippingRateId === "number" ? snapshotRequest.shippingRateId : null
423
+ };
424
+ }
425
+ /**
426
+ * Places a guest order from a checkout snapshot and optionally starts payment.
427
+ *
428
+ * The client must already carry team context (e.g. `createClient({ team })` → `X-Team`).
429
+ * Body `team` alone is not enough for `orders.create`.
430
+ *
431
+ * When `pay` is enabled (default), `paymentUrl` is only set from a successful `orders.pay`
432
+ * response. `order.paymentUrl` from create is the JoAi thank-you URL and must not be used
433
+ * as a gateway redirect.
434
+ */
435
+ async function placeGuestCheckoutOrder(options) {
436
+ if (!options.acceptedTerms) throw new GuestCheckoutError("terms_required");
437
+ const expiresAt = new Date(options.snapshot.expiresAt).getTime();
438
+ if (!Number.isFinite(expiresAt) || expiresAt <= Date.now()) throw new GuestCheckoutError("review_expired");
439
+ const order = await options.client.orders.create({
440
+ team: options.team,
441
+ snapshotId: options.snapshot.id,
442
+ snapshotAccessToken: options.snapshot.accessToken,
443
+ guest: options.guest,
444
+ idempotencyKey: options.idempotencyKey ?? crypto.randomUUID(),
445
+ notes: options.notes,
446
+ sendConfirmation: true
447
+ });
448
+ if (!order.ok || !order.data?.id) throw new GuestCheckoutError("order_failed");
449
+ const accessToken = typeof order.meta?.orderAccessToken === "string" ? order.meta.orderAccessToken : null;
450
+ if (options.pay === false) return {
451
+ order: order.data,
452
+ accessToken,
453
+ paymentUrl: null
454
+ };
455
+ if (!accessToken) throw new GuestCheckoutError("payment_failed");
456
+ const payment = await options.client.orders.pay(order.data.id, void 0, accessToken);
457
+ if (!payment.ok || !payment.data?.url) return {
458
+ order: order.data,
459
+ accessToken,
460
+ paymentUrl: null
461
+ };
462
+ return {
463
+ order: order.data,
464
+ accessToken,
465
+ paymentUrl: payment.data.url
466
+ };
467
+ }
468
+ const isGuestCheckoutError = (error) => {
469
+ if (error instanceof GuestCheckoutError) return true;
470
+ return typeof error === "object" && error !== null && error.name === "GuestCheckoutError" && typeof error.code === "string";
471
+ };
472
+ //#endregion
508
473
  //#region src/commerce/index.ts
509
474
  var CommerceResource = class {
510
475
  constructor(http) {
@@ -1921,7 +1886,7 @@ var OrdersResource = class {
1921
1886
  return this.http.get(`orders/${id}`, orderAccessHeaders(accessToken));
1922
1887
  }
1923
1888
  create(data) {
1924
- return this.http.post("orders", data);
1889
+ return this.http.post("orders", data, { headers: { "X-Team": data.team } });
1925
1890
  }
1926
1891
  update(id, data) {
1927
1892
  return this.http.put(`orders/${id}`, data);
@@ -2175,10 +2140,9 @@ var SitesResource = class {
2175
2140
  listMine(teamSlug) {
2176
2141
  return this.http.get(`sites/mine?team=${encodeURIComponent(teamSlug)}`);
2177
2142
  }
2178
- create(teamSlug, slug, data) {
2143
+ create(teamSlug, data) {
2179
2144
  return this.http.post("sites", {
2180
2145
  team: teamSlug,
2181
- slug,
2182
2146
  ...data
2183
2147
  });
2184
2148
  }
@@ -2611,6 +2575,9 @@ var AutomationsResource = class {
2611
2575
  list() {
2612
2576
  return this.http.get("automations");
2613
2577
  }
2578
+ stats() {
2579
+ return this.http.get("automations/stats");
2580
+ }
2614
2581
  get(id) {
2615
2582
  return this.http.get(`automations/${id}`);
2616
2583
  }
@@ -3355,4 +3322,4 @@ var DistributionResource = class {
3355
3322
  }
3356
3323
  };
3357
3324
  //#endregion
3358
- export { AddressesResource, AgentsResource, AlertsResource, ApiTokensResource, AppointmentsResource, AuthResource, AutomationsResource, BillingResource, BlueprintsResource, CachedHttpService, CampaignsResource, CardsResource, CartsResource, ChangelogResource, CharactersResource, CheckoutResource, CommerceResource, ContactsResource, ContentResource, ContractsResource, DeskResource, DistributionResource, DocumentsResource, ElementsResource, EntitiesResource, FlowsResource, FulfillmentsResource, GoalsResource, HooksResource, IngestionsResource, InquiriesResource, IntegrationsResource, IntentsResource, ItemsResource, JoAiClient, KvResource, LinksResource, LoyaltyResource, McpResource, MeetingsResource, MemoriesResource, MessagesResource, MetricsResource, MissionsResource, NotificationsResource, OfficeResource, OrdersResource, PersonaResource, ProductsResource, ProjectsResource, RemindersResource, ReturnsResource, SegmentsResource, ServicesResource, ShippingResource, ShortcutsResource, SitesResource, SkillsResource, StatsResource, StoreResource, SubscriptionsResource, SwarmsResource, TagsResource, TasksResource, TeamsResource, UpdatesResource, UpgradesResource, UsersResource, VoiceResource, WalletsResource, WarpsResource, WorkspaceResource, completePortalSession, createClient, createGuestCommerceStorage, formatOpeningHours, getAuthFragmentToken, isAllowedRedirectUrl, isPortalCallbackUrl, openPortalSession, sanitizePortalCallbackUrl };
3325
+ export { AddressesResource, AgentsResource, AlertsResource, ApiTokensResource, AppointmentsResource, AuthResource, AutomationsResource, BillingResource, BlueprintsResource, CachedHttpService, CampaignsResource, CardsResource, CartsResource, ChangelogResource, CharactersResource, CheckoutResource, CommerceResource, ContactsResource, ContentResource, ContractsResource, DeskResource, DistributionResource, DocumentsResource, ElementsResource, EntitiesResource, FlowsResource, FulfillmentsResource, GoalsResource, GuestCheckoutError, HooksResource, IngestionsResource, InquiriesResource, IntegrationsResource, IntentsResource, ItemsResource, JoAiClient, KvResource, LinksResource, LoyaltyResource, McpResource, MeetingsResource, MemoriesResource, MessagesResource, MetricsResource, MissionsResource, NotificationsResource, OfficeResource, OrdersResource, PersonaResource, ProductsResource, ProjectsResource, RemindersResource, ReturnsResource, SegmentsResource, ServicesResource, ShippingResource, ShortcutsResource, SitesResource, SkillsResource, StatsResource, StoreResource, SubscriptionsResource, SwarmsResource, TagsResource, TasksResource, TeamsResource, UpdatesResource, UpgradesResource, UsersResource, VoiceResource, WalletsResource, WarpsResource, WorkspaceResource, assertGuestCheckoutReady, buildGuestCheckoutSnapshotRequest, completePortalSession, createClient, createEmptyGuestCheckoutState, createGuestCheckoutSnapshot, createGuestCommerceStorage, formatOpeningHours, getAuthFragmentToken, isAllowedRedirectUrl, isGuestCheckoutError, isPortalCallbackUrl, openPortalSession, placeGuestCheckoutOrder, sanitizePortalCallbackUrl, toGuestCheckoutDraft };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joai/sdk",
3
- "version": "4.7.0",
3
+ "version": "5.0.0",
4
4
  "description": "JoAi public API SDK — build with your AI agents",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -35,10 +35,13 @@
35
35
  "test": "jest --config jest.config.js"
36
36
  },
37
37
  "dependencies": {
38
+ "@joai/core": "^4.13.0",
38
39
  "@joai/warps": "*"
39
40
  },
41
+ "peerDependencies": {
42
+ "@joai/core": "^4.13.0"
43
+ },
40
44
  "devDependencies": {
41
- "@joai/core": "^4.12.0",
42
45
  "tsdown": "^0.21.0",
43
46
  "typescript": "^5.9.3"
44
47
  }