feeef 0.8.4 → 0.8.9
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/README.md +81 -0
- package/build/index.js +222 -1
- package/build/index.js.map +1 -1
- package/build/src/core/entities/store.d.ts +2 -0
- package/build/src/feeef/feeef.d.ts +5 -0
- package/build/src/feeef/repositories/apps.d.ts +54 -1
- package/build/src/feeef/repositories/oauth.d.ts +77 -0
- package/build/src/feeef/repositories/orders.d.ts +3 -0
- package/build/src/feeef/repositories/promos.d.ts +7 -0
- package/build/src/index.d.ts +2 -0
- package/build/src/realtime/crud_event.d.ts +9 -0
- package/build/src/realtime/index.d.ts +2 -0
- package/build/src/realtime/transmit.d.ts +59 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
`feeefjs` is a TypeScript library for managing feeef e-commerce platforms for self-hosted stores. It provides a wrapper for feeef rest api such like send order..., also have frontend srvices like the `CartService` class for managing cart items, shipping methods, and calculating totals. The library also includes a `NotifiableService` base class for handling listeners that react to changes in the service state.
|
|
4
4
|
|
|
5
|
+
## Developer OAuth (third-party apps)
|
|
6
|
+
|
|
7
|
+
Use `OAuthRepository.buildAuthorizeUrl` (point `baseUrl` at the **accounts** host, e.g. `https://accounts.feeef.org`) and `FeeeF` client `oauth.exchangeAuthorizationCode` / `revokeToken` / `introspectToken` against the **API** base (`.../v1`). Full flow, scopes, and troubleshooting: Feeef backend **`docs/OAUTH2_DEVELOPER.md`** (in the Adonis API repo).
|
|
8
|
+
|
|
9
|
+
## Realtime (AdonisJS Transmit)
|
|
10
|
+
|
|
11
|
+
Server-Sent Events use the same **API origin** as the REST client, but **without** the `/v1` or `/api/v1` prefix (e.g. `https://api.feeef.org/__transmit/events`). Use `createFeeefTransmit({ apiBaseUrl, getAccessToken })` so subscribe/unsubscribe POSTs get a proper `Authorization: Bearer …` header. The underlying client is the official [`@adonisjs/transmit-client`](https://www.npmjs.com/package/@adonisjs/transmit-client) (`Transmit`, `Subscription` are re-exported).
|
|
12
|
+
|
|
5
13
|
---
|
|
6
14
|
|
|
7
15
|
# CartService & NotifiableService
|
|
@@ -213,3 +221,76 @@ interface ProductOffer {
|
|
|
213
221
|
## Contributing
|
|
214
222
|
|
|
215
223
|
Feel free to fork this repository and contribute improvements, bug fixes, or additional features. Open a pull request for any major changes!
|
|
224
|
+
|
|
225
|
+
## Developer OAuth (Authorization Code)
|
|
226
|
+
|
|
227
|
+
Use this flow when a third-party app (created in `apps`) needs user authorization.
|
|
228
|
+
|
|
229
|
+
### Endpoints
|
|
230
|
+
|
|
231
|
+
- Authorize: `GET /v1/oauth/authorize`
|
|
232
|
+
- Token: `POST /v1/oauth/token`
|
|
233
|
+
- Revoke: `POST /v1/oauth/revoke`
|
|
234
|
+
- Introspect: `POST /v1/oauth/introspect`
|
|
235
|
+
|
|
236
|
+
### Step-by-step (Google-like)
|
|
237
|
+
|
|
238
|
+
1. Build authorize URL:
|
|
239
|
+
|
|
240
|
+
```ts
|
|
241
|
+
import { FeeeF, OAuthRepository } from 'feeef'
|
|
242
|
+
|
|
243
|
+
const feeef = new FeeeF({
|
|
244
|
+
apiKey: '<your_api_key>',
|
|
245
|
+
baseURL: 'https://api.feeef.org/v1',
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
const authorizeUrl = OAuthRepository.buildAuthorizeUrl({
|
|
249
|
+
baseUrl: 'https://api.feeef.org/v1',
|
|
250
|
+
clientId: '<client_id>',
|
|
251
|
+
redirectUri: 'https://your-app.com/oauth/callback',
|
|
252
|
+
scope: ['*'], // or explicit scopes
|
|
253
|
+
state: crypto.randomUUID(),
|
|
254
|
+
})
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
2. Open `authorizeUrl` in browser.
|
|
258
|
+
3. If user is not signed in, API may return `401` with:
|
|
259
|
+
- `error: "login_required"`
|
|
260
|
+
- `login_url` (accounts sign-in URL with `next`)
|
|
261
|
+
4. Redirect user to `login_url`.
|
|
262
|
+
5. After sign-in + authorization, user is redirected to your callback with `code` (and `state`).
|
|
263
|
+
6. Exchange `code` for token:
|
|
264
|
+
|
|
265
|
+
```ts
|
|
266
|
+
const tokenResponse = await feeef.oauth.exchangeAuthorizationCode({
|
|
267
|
+
code: '<code_from_callback>',
|
|
268
|
+
redirectUri: 'https://your-app.com/oauth/callback',
|
|
269
|
+
clientId: '<client_id>',
|
|
270
|
+
clientSecret: '<client_secret>',
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
await feeef.oauth.revokeToken(tokenResponse.access_token)
|
|
274
|
+
|
|
275
|
+
const introspection = await feeef.oauth.introspectToken(tokenResponse.access_token)
|
|
276
|
+
console.log(introspection.active)
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Alternative static helper:
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
import { OAuthRepository } from 'feeef'
|
|
283
|
+
|
|
284
|
+
const authorizeUrl = OAuthRepository.buildAuthorizeUrl({
|
|
285
|
+
baseUrl: 'https://api.feeef.org/v1',
|
|
286
|
+
clientId: '<client_id>',
|
|
287
|
+
redirectUri: 'https://your-app.com/oauth/callback',
|
|
288
|
+
})
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Important
|
|
292
|
+
|
|
293
|
+
- `redirect_uri` must exactly match one of the app's registered redirect URIs.
|
|
294
|
+
- Keep `client_secret` server-side only.
|
|
295
|
+
- Always validate `state` on callback.
|
|
296
|
+
- Prefer PKCE (`code_challenge`, `code_verifier`) for public clients.
|
package/build/index.js
CHANGED
|
@@ -141,7 +141,12 @@ var OrderRepository = class extends ModelRepository {
|
|
|
141
141
|
if (options.page) params.page = options.page;
|
|
142
142
|
if (options.offset) params.offset = options.offset;
|
|
143
143
|
if (options.limit) params.limit = options.limit;
|
|
144
|
-
|
|
144
|
+
const useMultiStore = options.storeIds != null && options.storeIds.length > 0;
|
|
145
|
+
if (useMultiStore) {
|
|
146
|
+
params.store_ids = options.storeIds;
|
|
147
|
+
} else if (options.storeId) {
|
|
148
|
+
params.store_id = options.storeId;
|
|
149
|
+
}
|
|
145
150
|
if (options.status) {
|
|
146
151
|
params.status = Array.isArray(options.status) ? options.status : [options.status];
|
|
147
152
|
}
|
|
@@ -841,6 +846,17 @@ var AppRepository = class extends ModelRepository {
|
|
|
841
846
|
}
|
|
842
847
|
/**
|
|
843
848
|
* Builds the OAuth authorize URL to which the user should be redirected.
|
|
849
|
+
* This is the first step of the authorization-code flow (similar UX to Google OAuth).
|
|
850
|
+
*
|
|
851
|
+
* Production: opening this URL on the **API** host (`api.*`) issues a redirect to the same path on
|
|
852
|
+
* **accounts.*** so the consent screen appears on the trusted accounts domain; query params are preserved.
|
|
853
|
+
*
|
|
854
|
+
* If the user is not logged in yet, API `GET /oauth/authorize` returns:
|
|
855
|
+
* - `401 login_required`
|
|
856
|
+
* - `login_url` (accounts sign-in URL with `next=...`)
|
|
857
|
+
*
|
|
858
|
+
* The client should navigate to `login_url`, let the user sign in, and then
|
|
859
|
+
* continue by opening the original authorize URL again (or rely on `next`).
|
|
844
860
|
*
|
|
845
861
|
* @param params - Parameters for the authorize URL.
|
|
846
862
|
* @param params.baseUrl - API base URL (e.g. https://api.feeef.org/api/v1).
|
|
@@ -867,6 +883,120 @@ var AppRepository = class extends ModelRepository {
|
|
|
867
883
|
}
|
|
868
884
|
return url.toString();
|
|
869
885
|
}
|
|
886
|
+
/**
|
|
887
|
+
* Public `apps.public_data` (no auth). Pass [config] to skip Authorization if your axios instance adds it globally.
|
|
888
|
+
*/
|
|
889
|
+
async getPublicData(id, config) {
|
|
890
|
+
const res = await this.client.get(`/${this.resource}/${id}/public-data`, config);
|
|
891
|
+
return res.data;
|
|
892
|
+
}
|
|
893
|
+
async getPrivateData(id) {
|
|
894
|
+
const res = await this.client.get(`/${this.resource}/${id}/private-data`);
|
|
895
|
+
return res.data;
|
|
896
|
+
}
|
|
897
|
+
async putPublicData(id, publicNamespaces) {
|
|
898
|
+
const res = await this.client.put(`/${this.resource}/${id}/public-data`, {
|
|
899
|
+
public: publicNamespaces
|
|
900
|
+
});
|
|
901
|
+
return res.data;
|
|
902
|
+
}
|
|
903
|
+
async putPrivateData(id, privateNamespaces) {
|
|
904
|
+
const res = await this.client.put(`/${this.resource}/${id}/private-data`, {
|
|
905
|
+
private: privateNamespaces
|
|
906
|
+
});
|
|
907
|
+
return res.data;
|
|
908
|
+
}
|
|
909
|
+
async getUserDataMe(id) {
|
|
910
|
+
const res = await this.client.get(`/${this.resource}/${id}/user-data/me`);
|
|
911
|
+
return res.data;
|
|
912
|
+
}
|
|
913
|
+
async putUserDataMe(id, publicNamespaces) {
|
|
914
|
+
const res = await this.client.put(`/${this.resource}/${id}/user-data/me`, {
|
|
915
|
+
public: publicNamespaces
|
|
916
|
+
});
|
|
917
|
+
return res.data;
|
|
918
|
+
}
|
|
919
|
+
async getUserDataForUser(appId, userId) {
|
|
920
|
+
const res = await this.client.get(`/${this.resource}/${appId}/user-data/users/${userId}`);
|
|
921
|
+
return res.data;
|
|
922
|
+
}
|
|
923
|
+
async putUserDataForUser(appId, userId, body) {
|
|
924
|
+
const res = await this.client.put(`/${this.resource}/${appId}/user-data/users/${userId}`, body);
|
|
925
|
+
return res.data;
|
|
926
|
+
}
|
|
927
|
+
};
|
|
928
|
+
|
|
929
|
+
// src/feeef/repositories/oauth.ts
|
|
930
|
+
var OAuthRepository = class {
|
|
931
|
+
client;
|
|
932
|
+
constructor(client) {
|
|
933
|
+
this.client = client;
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
936
|
+
* Builds the authorize URL for browser redirect.
|
|
937
|
+
*/
|
|
938
|
+
static buildAuthorizeUrl(params) {
|
|
939
|
+
const base = params.baseUrl.endsWith("/") ? params.baseUrl : `${params.baseUrl}/`;
|
|
940
|
+
const url = new URL("oauth/authorize", base);
|
|
941
|
+
url.searchParams.set("client_id", params.clientId);
|
|
942
|
+
url.searchParams.set("redirect_uri", params.redirectUri);
|
|
943
|
+
url.searchParams.set("response_type", "code");
|
|
944
|
+
if (params.scope?.length) url.searchParams.set("scope", params.scope.join(" "));
|
|
945
|
+
if (params.state) url.searchParams.set("state", params.state);
|
|
946
|
+
if (params.codeChallenge) url.searchParams.set("code_challenge", params.codeChallenge);
|
|
947
|
+
if (params.codeChallengeMethod) {
|
|
948
|
+
url.searchParams.set("code_challenge_method", params.codeChallengeMethod);
|
|
949
|
+
}
|
|
950
|
+
return url.toString();
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* Exchanges an authorization code for an access token.
|
|
954
|
+
*/
|
|
955
|
+
async exchangeAuthorizationCode(params) {
|
|
956
|
+
const body = new URLSearchParams({
|
|
957
|
+
grant_type: "authorization_code",
|
|
958
|
+
code: params.code,
|
|
959
|
+
redirect_uri: params.redirectUri,
|
|
960
|
+
client_id: params.clientId,
|
|
961
|
+
client_secret: params.clientSecret
|
|
962
|
+
});
|
|
963
|
+
if (params.codeVerifier) {
|
|
964
|
+
body.set("code_verifier", params.codeVerifier);
|
|
965
|
+
}
|
|
966
|
+
const response = await this.client.post("/oauth/token", body.toString(), {
|
|
967
|
+
headers: {
|
|
968
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
969
|
+
}
|
|
970
|
+
});
|
|
971
|
+
return response.data;
|
|
972
|
+
}
|
|
973
|
+
/**
|
|
974
|
+
* Revokes an OAuth token.
|
|
975
|
+
*/
|
|
976
|
+
async revokeToken(token, tokenTypeHint) {
|
|
977
|
+
const body = new URLSearchParams({ token });
|
|
978
|
+
if (tokenTypeHint) {
|
|
979
|
+
body.set("token_type_hint", tokenTypeHint);
|
|
980
|
+
}
|
|
981
|
+
const response = await this.client.post("/oauth/revoke", body.toString(), {
|
|
982
|
+
headers: {
|
|
983
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
984
|
+
}
|
|
985
|
+
});
|
|
986
|
+
return response.data;
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* Introspects an OAuth token.
|
|
990
|
+
*/
|
|
991
|
+
async introspectToken(token) {
|
|
992
|
+
const body = new URLSearchParams({ token });
|
|
993
|
+
const response = await this.client.post("/oauth/introspect", body.toString(), {
|
|
994
|
+
headers: {
|
|
995
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
996
|
+
}
|
|
997
|
+
});
|
|
998
|
+
return response.data;
|
|
999
|
+
}
|
|
870
1000
|
};
|
|
871
1001
|
|
|
872
1002
|
// src/feeef/repositories/deposits.ts
|
|
@@ -1017,9 +1147,17 @@ var PromoRepository = class {
|
|
|
1017
1147
|
if (params?.page != null) query.page = params.page;
|
|
1018
1148
|
if (params?.limit != null) query.limit = params.limit;
|
|
1019
1149
|
if (params?.validNow === true) query.validNow = "1";
|
|
1150
|
+
if (params?.filterator) query.filterator = params.filterator;
|
|
1020
1151
|
const res = await this.client.get("/promos", { params: query });
|
|
1021
1152
|
return res.data;
|
|
1022
1153
|
}
|
|
1154
|
+
/**
|
|
1155
|
+
* Fetches a single promo by id.
|
|
1156
|
+
*/
|
|
1157
|
+
async find(params) {
|
|
1158
|
+
const res = await this.client.get(`/promos/${params.id}`);
|
|
1159
|
+
return res.data;
|
|
1160
|
+
}
|
|
1023
1161
|
/**
|
|
1024
1162
|
* Validates a promo code. Returns validation result with discount info or reason.
|
|
1025
1163
|
*/
|
|
@@ -3513,6 +3651,10 @@ var FeeeF = class {
|
|
|
3513
3651
|
* The repository for managing developer-registered apps (OAuth clients).
|
|
3514
3652
|
*/
|
|
3515
3653
|
apps;
|
|
3654
|
+
/**
|
|
3655
|
+
* The repository for OAuth2 authorize/token/revoke/introspect operations.
|
|
3656
|
+
*/
|
|
3657
|
+
oauth;
|
|
3516
3658
|
/**
|
|
3517
3659
|
* The repository for managing orders.
|
|
3518
3660
|
*/
|
|
@@ -3604,6 +3746,7 @@ var FeeeF = class {
|
|
|
3604
3746
|
this.imageGenerations = new ImageGenerationsRepository(this.client);
|
|
3605
3747
|
this.users = new UserRepository(this.client);
|
|
3606
3748
|
this.apps = new AppRepository(this.client);
|
|
3749
|
+
this.oauth = new OAuthRepository(this.client);
|
|
3607
3750
|
this.orders = new OrderRepository(this.client);
|
|
3608
3751
|
this.deposits = new DepositRepository(this.client);
|
|
3609
3752
|
this.transfers = new TransferRepository(this.client);
|
|
@@ -3957,6 +4100,77 @@ var EmbaddedContactType = /* @__PURE__ */ ((EmbaddedContactType2) => {
|
|
|
3957
4100
|
return EmbaddedContactType2;
|
|
3958
4101
|
})(EmbaddedContactType || {});
|
|
3959
4102
|
|
|
4103
|
+
// src/realtime/transmit.ts
|
|
4104
|
+
import { Transmit } from "@adonisjs/transmit-client";
|
|
4105
|
+
import { Subscription, Transmit as Transmit2 } from "@adonisjs/transmit-client";
|
|
4106
|
+
function transmitRootFromApiBaseUrl(apiBaseUrl) {
|
|
4107
|
+
let u = apiBaseUrl.trim().replace(/\/+$/, "");
|
|
4108
|
+
if (u.endsWith("/api/v1")) {
|
|
4109
|
+
u = u.slice(0, -"/api/v1".length);
|
|
4110
|
+
} else if (u.endsWith("/v1")) {
|
|
4111
|
+
u = u.slice(0, -"/v1".length);
|
|
4112
|
+
}
|
|
4113
|
+
return u;
|
|
4114
|
+
}
|
|
4115
|
+
function retrieveXsrfTokenFromCookie() {
|
|
4116
|
+
const cookie = globalThis.document?.cookie;
|
|
4117
|
+
if (!cookie) return null;
|
|
4118
|
+
const match = cookie.match(new RegExp("(^|;\\s*)(XSRF-TOKEN)=([^;]*)"));
|
|
4119
|
+
return match ? decodeURIComponent(match[3]) : null;
|
|
4120
|
+
}
|
|
4121
|
+
var FeeefTransmitHttpClient = class {
|
|
4122
|
+
constructor(options, getAuthorizationHeader) {
|
|
4123
|
+
this.options = options;
|
|
4124
|
+
this.getAuthorizationHeader = getAuthorizationHeader;
|
|
4125
|
+
}
|
|
4126
|
+
send(request) {
|
|
4127
|
+
return fetch(request);
|
|
4128
|
+
}
|
|
4129
|
+
createRequest(path, body) {
|
|
4130
|
+
const headers = {
|
|
4131
|
+
"Content-Type": "application/json",
|
|
4132
|
+
"X-XSRF-TOKEN": retrieveXsrfTokenFromCookie() ?? ""
|
|
4133
|
+
};
|
|
4134
|
+
const auth = this.getAuthorizationHeader();
|
|
4135
|
+
if (auth) {
|
|
4136
|
+
headers.Authorization = auth.startsWith("Bearer ") ? auth : `Bearer ${auth}`;
|
|
4137
|
+
}
|
|
4138
|
+
return new Request(`${this.options.baseUrl}${path}`, {
|
|
4139
|
+
method: "POST",
|
|
4140
|
+
headers,
|
|
4141
|
+
body: JSON.stringify({ uid: this.options.uid, ...body }),
|
|
4142
|
+
credentials: "include"
|
|
4143
|
+
});
|
|
4144
|
+
}
|
|
4145
|
+
};
|
|
4146
|
+
function createFeeefTransmit(options) {
|
|
4147
|
+
const baseUrl = transmitRootFromApiBaseUrl(options.apiBaseUrl);
|
|
4148
|
+
const getToken = options.getAccessToken ?? (() => void 0);
|
|
4149
|
+
return new Transmit({
|
|
4150
|
+
...options.transmit,
|
|
4151
|
+
baseUrl,
|
|
4152
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Transmit’s HttpClient is not exported; FeeefTransmitHttpClient matches its shape.
|
|
4153
|
+
httpClientFactory: (url, uid) => new FeeefTransmitHttpClient({ baseUrl: url, uid }, () => {
|
|
4154
|
+
const t = getToken();
|
|
4155
|
+
if (t === null || t === void 0 || t === "") return void 0;
|
|
4156
|
+
const s = String(t).trim();
|
|
4157
|
+
return s.startsWith("Bearer ") ? s : `Bearer ${s}`;
|
|
4158
|
+
})
|
|
4159
|
+
});
|
|
4160
|
+
}
|
|
4161
|
+
function createFeeefTransmitFromAxios(client, options) {
|
|
4162
|
+
const apiBaseUrl = client.defaults.baseURL ?? "";
|
|
4163
|
+
return createFeeefTransmit({
|
|
4164
|
+
...options,
|
|
4165
|
+
apiBaseUrl,
|
|
4166
|
+
getAccessToken: () => {
|
|
4167
|
+
const raw = client.defaults.headers.common?.Authorization;
|
|
4168
|
+
if (typeof raw !== "string") return void 0;
|
|
4169
|
+
return raw.replace(/^Bearer\s+/i, "").trim() || void 0;
|
|
4170
|
+
}
|
|
4171
|
+
});
|
|
4172
|
+
}
|
|
4173
|
+
|
|
3960
4174
|
// src/utils.ts
|
|
3961
4175
|
var convertDartColorToCssNumber = (dartColor) => {
|
|
3962
4176
|
const alpha = dartColor >> 24 & 255;
|
|
@@ -4059,6 +4273,7 @@ export {
|
|
|
4059
4273
|
FeedbackRepository,
|
|
4060
4274
|
FeedbackStatus,
|
|
4061
4275
|
FeeeF,
|
|
4276
|
+
FeeefTransmitHttpClient,
|
|
4062
4277
|
GoogleSheetIntegrationApi,
|
|
4063
4278
|
ImageGenerationsRepository,
|
|
4064
4279
|
ImagePromptTemplatesRepository,
|
|
@@ -4067,6 +4282,7 @@ export {
|
|
|
4067
4282
|
ModelRepository,
|
|
4068
4283
|
NoestDeliveryIntegrationApi,
|
|
4069
4284
|
NotificationsService,
|
|
4285
|
+
OAuthRepository,
|
|
4070
4286
|
OrderRepository,
|
|
4071
4287
|
OrderStatus,
|
|
4072
4288
|
PaymentStatus,
|
|
@@ -4092,8 +4308,10 @@ export {
|
|
|
4092
4308
|
StoreRepository,
|
|
4093
4309
|
StoreSubscriptionStatus,
|
|
4094
4310
|
StoreSubscriptionType,
|
|
4311
|
+
Subscription,
|
|
4095
4312
|
TiktokPixelEvent,
|
|
4096
4313
|
TransferRepository,
|
|
4314
|
+
Transmit2 as Transmit,
|
|
4097
4315
|
UserRepository,
|
|
4098
4316
|
VariantOptionType,
|
|
4099
4317
|
WebhookEvent,
|
|
@@ -4102,6 +4320,8 @@ export {
|
|
|
4102
4320
|
ZimouDeliveryIntegrationApi,
|
|
4103
4321
|
convertDartColorToCssNumber,
|
|
4104
4322
|
convertOrderEntityToOrderTrackEntity,
|
|
4323
|
+
createFeeefTransmit,
|
|
4324
|
+
createFeeefTransmitFromAxios,
|
|
4105
4325
|
createImageGenerationFormData,
|
|
4106
4326
|
cssColorToHslString,
|
|
4107
4327
|
dartColorToCssColor,
|
|
@@ -4137,6 +4357,7 @@ export {
|
|
|
4137
4357
|
serializeAttachmentPayloads,
|
|
4138
4358
|
serializeImagePromptTemplateCreate,
|
|
4139
4359
|
serializeImagePromptTemplateUpdate,
|
|
4360
|
+
transmitRootFromApiBaseUrl,
|
|
4140
4361
|
tryFixPhoneNumber,
|
|
4141
4362
|
validatePhoneNumber
|
|
4142
4363
|
};
|