@resultdev/sdk 0.3.0 → 0.3.2
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/CHANGELOG.md +20 -0
- package/dist/{client-CJv_awPt.d.cts → client-D5IGLPW4.d.ts} +5 -3
- package/dist/{client-DrsZ1qIm.d.ts → client-N6MW9IE1.d.cts} +5 -3
- package/dist/index.cjs +116 -21
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +116 -21
- package/dist/{middleware-BJl9JRg3.d.ts → middleware-8UNqItQG.d.ts} +1 -1
- package/dist/{middleware-C1Z6aMvn.d.cts → middleware-CGn44ocX.d.cts} +1 -1
- package/dist/ssr/middleware.d.cts +2 -2
- package/dist/ssr/middleware.d.ts +2 -2
- package/dist/ssr.cjs +116 -21
- package/dist/ssr.d.cts +4 -4
- package/dist/ssr.d.ts +4 -4
- package/dist/ssr.js +116 -21
- package/dist/{types-DSVtGaIA.d.cts → types-BTdjI7Yj.d.cts} +4 -2
- package/dist/{types-DSVtGaIA.d.ts → types-BTdjI7Yj.d.ts} +4 -2
- package/package.json +13 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as ResultConfig, o as ResultError } from './types-
|
|
1
|
+
import { a as ResultConfig, o as ResultError } from './types-BTdjI7Yj.js';
|
|
2
2
|
|
|
3
3
|
declare const DEFAULT_ACCESS_TOKEN_COOKIE = "result_access_token";
|
|
4
4
|
declare const DEFAULT_REFRESH_TOKEN_COOKIE = "result_refresh_token";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as ResultConfig, o as ResultError } from './types-
|
|
1
|
+
import { a as ResultConfig, o as ResultError } from './types-BTdjI7Yj.cjs';
|
|
2
2
|
|
|
3
3
|
declare const DEFAULT_ACCESS_TOKEN_COOKIE = "result_access_token";
|
|
4
4
|
declare const DEFAULT_REFRESH_TOKEN_COOKIE = "result_refresh_token";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { b as AuthCookieNames, c as AuthCookieOptions, A as AuthCookieSettings, d as CookieOptions, e as CookieReader, C as CookieStore, a as CookieWriter, D as DEFAULT_ACCESS_TOKEN_COOKIE, f as DEFAULT_REFRESH_TOKEN_COOKIE, U as UpdateSessionOptions, g as UpdateSessionResult, i as clearAuthCookies, j as getAccessTokenCookieName, k as getRefreshTokenCookieName, s as setAuthCookies, u as updateSession } from '../middleware-
|
|
2
|
-
import '../types-
|
|
1
|
+
export { b as AuthCookieNames, c as AuthCookieOptions, A as AuthCookieSettings, d as CookieOptions, e as CookieReader, C as CookieStore, a as CookieWriter, D as DEFAULT_ACCESS_TOKEN_COOKIE, f as DEFAULT_REFRESH_TOKEN_COOKIE, U as UpdateSessionOptions, g as UpdateSessionResult, i as clearAuthCookies, j as getAccessTokenCookieName, k as getRefreshTokenCookieName, s as setAuthCookies, u as updateSession } from '../middleware-CGn44ocX.cjs';
|
|
2
|
+
import '../types-BTdjI7Yj.cjs';
|
package/dist/ssr/middleware.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { b as AuthCookieNames, c as AuthCookieOptions, A as AuthCookieSettings, d as CookieOptions, e as CookieReader, C as CookieStore, a as CookieWriter, D as DEFAULT_ACCESS_TOKEN_COOKIE, f as DEFAULT_REFRESH_TOKEN_COOKIE, U as UpdateSessionOptions, g as UpdateSessionResult, i as clearAuthCookies, j as getAccessTokenCookieName, k as getRefreshTokenCookieName, s as setAuthCookies, u as updateSession } from '../middleware-
|
|
2
|
-
import '../types-
|
|
1
|
+
export { b as AuthCookieNames, c as AuthCookieOptions, A as AuthCookieSettings, d as CookieOptions, e as CookieReader, C as CookieStore, a as CookieWriter, D as DEFAULT_ACCESS_TOKEN_COOKIE, f as DEFAULT_REFRESH_TOKEN_COOKIE, U as UpdateSessionOptions, g as UpdateSessionResult, i as clearAuthCookies, j as getAccessTokenCookieName, k as getRefreshTokenCookieName, s as setAuthCookies, u as updateSession } from '../middleware-8UNqItQG.js';
|
|
2
|
+
import '../types-BTdjI7Yj.js';
|
package/dist/ssr.cjs
CHANGED
|
@@ -690,7 +690,15 @@ var HttpClient = class {
|
|
|
690
690
|
*/
|
|
691
691
|
constructor(config, tokenManager, logger) {
|
|
692
692
|
this.config = config;
|
|
693
|
-
|
|
693
|
+
if (!config.baseUrl) {
|
|
694
|
+
throw new ResultError(
|
|
695
|
+
"createClient needs a baseUrl, but it was undefined at runtime.",
|
|
696
|
+
0,
|
|
697
|
+
"MISSING_BASE_URL",
|
|
698
|
+
"Pass the NEXT_PUBLIC_BACKEND_URL value from .env.local. In browser code, write process.env.NEXT_PUBLIC_BACKEND_URL as a literal expression (never through an imported process module) so the bundler inlines it, and set it as a deployments build variable before deploying."
|
|
699
|
+
);
|
|
700
|
+
}
|
|
701
|
+
this.baseUrl = config.baseUrl;
|
|
694
702
|
this.fetch = config.fetch || (globalThis.fetch ? globalThis.fetch.bind(globalThis) : void 0);
|
|
695
703
|
this.anonKey = config.anonKey;
|
|
696
704
|
this.defaultHeaders = {
|
|
@@ -781,6 +789,7 @@ var HttpClient = class {
|
|
|
781
789
|
}
|
|
782
790
|
let controller;
|
|
783
791
|
let timer;
|
|
792
|
+
let removeCallerAbortListener;
|
|
784
793
|
if (this.timeout > 0 || callerSignal) {
|
|
785
794
|
controller = new AbortController();
|
|
786
795
|
if (this.timeout > 0) {
|
|
@@ -794,16 +803,11 @@ var HttpClient = class {
|
|
|
794
803
|
callerSignal.addEventListener("abort", onCallerAbort, {
|
|
795
804
|
once: true
|
|
796
805
|
});
|
|
797
|
-
|
|
798
|
-
"abort",
|
|
799
|
-
() => {
|
|
800
|
-
callerSignal.removeEventListener("abort", onCallerAbort);
|
|
801
|
-
},
|
|
802
|
-
{ once: true }
|
|
803
|
-
);
|
|
806
|
+
removeCallerAbortListener = () => callerSignal.removeEventListener("abort", onCallerAbort);
|
|
804
807
|
}
|
|
805
808
|
}
|
|
806
809
|
}
|
|
810
|
+
let detachOnExit = true;
|
|
807
811
|
try {
|
|
808
812
|
const response = await this.fetch(url, {
|
|
809
813
|
method,
|
|
@@ -824,14 +828,9 @@ var HttpClient = class {
|
|
|
824
828
|
);
|
|
825
829
|
continue;
|
|
826
830
|
}
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
}
|
|
830
|
-
return response;
|
|
831
|
+
detachOnExit = false;
|
|
832
|
+
return { response, releaseCallerSignal: removeCallerAbortListener };
|
|
831
833
|
} catch (err) {
|
|
832
|
-
if (timer !== void 0) {
|
|
833
|
-
clearTimeout(timer);
|
|
834
|
-
}
|
|
835
834
|
if (err?.name === "AbortError") {
|
|
836
835
|
if (controller?.signal.aborted && this.timeout > 0 && !callerSignal?.aborted) {
|
|
837
836
|
throw new ResultError(
|
|
@@ -851,6 +850,13 @@ var HttpClient = class {
|
|
|
851
850
|
0,
|
|
852
851
|
"NETWORK_ERROR"
|
|
853
852
|
);
|
|
853
|
+
} finally {
|
|
854
|
+
if (timer !== void 0) {
|
|
855
|
+
clearTimeout(timer);
|
|
856
|
+
}
|
|
857
|
+
if (detachOnExit) {
|
|
858
|
+
removeCallerAbortListener?.();
|
|
859
|
+
}
|
|
854
860
|
}
|
|
855
861
|
}
|
|
856
862
|
throw lastError || new ResultError(
|
|
@@ -913,7 +919,7 @@ var HttpClient = class {
|
|
|
913
919
|
});
|
|
914
920
|
}
|
|
915
921
|
this.logger.logRequest(method, url, requestHeaders, processedBody);
|
|
916
|
-
const response = await this.fetchWithRetry({
|
|
922
|
+
const { response, releaseCallerSignal } = await this.fetchWithRetry({
|
|
917
923
|
method,
|
|
918
924
|
url,
|
|
919
925
|
headers: requestHeaders,
|
|
@@ -936,6 +942,8 @@ var HttpClient = class {
|
|
|
936
942
|
);
|
|
937
943
|
}
|
|
938
944
|
throw err;
|
|
945
|
+
} finally {
|
|
946
|
+
releaseCallerSignal?.();
|
|
939
947
|
}
|
|
940
948
|
this.logger.logResponse(
|
|
941
949
|
method,
|
|
@@ -1037,7 +1045,7 @@ var HttpClient = class {
|
|
|
1037
1045
|
const callerSignal = initSignal ?? request?.signal;
|
|
1038
1046
|
const maxAttempts = IDEMPOTENT_METHODS.has(method.toUpperCase()) ? this.retryCount : 0;
|
|
1039
1047
|
this.logger.logRequest(method, url, requestHeaders, body);
|
|
1040
|
-
const response = await this.fetchWithRetry({
|
|
1048
|
+
const { response } = await this.fetchWithRetry({
|
|
1041
1049
|
method,
|
|
1042
1050
|
url,
|
|
1043
1051
|
headers: requestHeaders,
|
|
@@ -1138,6 +1146,10 @@ var HttpClient = class {
|
|
|
1138
1146
|
}
|
|
1139
1147
|
return headers;
|
|
1140
1148
|
}
|
|
1149
|
+
/** Returns the publishable project key used to identify SDK usage reports. */
|
|
1150
|
+
getAnonKey() {
|
|
1151
|
+
return this.anonKey;
|
|
1152
|
+
}
|
|
1141
1153
|
async refreshAccessToken() {
|
|
1142
1154
|
if (this.isRefreshing) {
|
|
1143
1155
|
return this.refreshPromise;
|
|
@@ -1207,6 +1219,47 @@ var HttpClient = class {
|
|
|
1207
1219
|
}
|
|
1208
1220
|
};
|
|
1209
1221
|
|
|
1222
|
+
// src/modules/ai-usage.ts
|
|
1223
|
+
var USAGE_ENDPOINT = "https://beta.result.dev/api/usage/ai";
|
|
1224
|
+
async function reportAiUsage(http, requestId, model, kind, tokens) {
|
|
1225
|
+
const anonKey = http.getAnonKey();
|
|
1226
|
+
if (!anonKey || !Number.isSafeInteger(tokens) || tokens <= 0) {
|
|
1227
|
+
return;
|
|
1228
|
+
}
|
|
1229
|
+
const controller = new AbortController();
|
|
1230
|
+
const timeout = setTimeout(() => controller.abort(), 2e3);
|
|
1231
|
+
try {
|
|
1232
|
+
const request = {
|
|
1233
|
+
method: "POST",
|
|
1234
|
+
headers: { "content-type": "application/json" },
|
|
1235
|
+
body: JSON.stringify({
|
|
1236
|
+
requestId,
|
|
1237
|
+
backendUrl: http.baseUrl,
|
|
1238
|
+
anonKey,
|
|
1239
|
+
model,
|
|
1240
|
+
kind,
|
|
1241
|
+
tokens
|
|
1242
|
+
}),
|
|
1243
|
+
keepalive: true,
|
|
1244
|
+
signal: controller.signal
|
|
1245
|
+
};
|
|
1246
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
1247
|
+
try {
|
|
1248
|
+
const response = await http.fetch(USAGE_ENDPOINT, request);
|
|
1249
|
+
if (response.status < 500) return;
|
|
1250
|
+
} catch {
|
|
1251
|
+
if (controller.signal.aborted) return;
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
} catch {
|
|
1255
|
+
} finally {
|
|
1256
|
+
clearTimeout(timeout);
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
function aiUsageRequestId() {
|
|
1260
|
+
return globalThis.crypto?.randomUUID?.() ?? null;
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1210
1263
|
// src/modules/ai.ts
|
|
1211
1264
|
var SUPPORTED_AUDIO_FORMATS = [
|
|
1212
1265
|
"wav",
|
|
@@ -1294,6 +1347,7 @@ var ChatCompletions = class {
|
|
|
1294
1347
|
}
|
|
1295
1348
|
http;
|
|
1296
1349
|
async create(params) {
|
|
1350
|
+
const usageRequestId = aiUsageRequestId();
|
|
1297
1351
|
const contentSummary = summarizeContent(params.messages ?? []);
|
|
1298
1352
|
assertSupportedContent(params.model, contentSummary);
|
|
1299
1353
|
const backendParams = {
|
|
@@ -1338,7 +1392,7 @@ var ChatCompletions = class {
|
|
|
1338
1392
|
contentSummary
|
|
1339
1393
|
);
|
|
1340
1394
|
}
|
|
1341
|
-
return this.parseSSEStream(response2, params.model);
|
|
1395
|
+
return this.parseSSEStream(response2, params.model, usageRequestId);
|
|
1342
1396
|
}
|
|
1343
1397
|
let response;
|
|
1344
1398
|
try {
|
|
@@ -1347,6 +1401,16 @@ var ChatCompletions = class {
|
|
|
1347
1401
|
throw enrichUpstreamError(error, contentSummary);
|
|
1348
1402
|
}
|
|
1349
1403
|
const content = response.text || "";
|
|
1404
|
+
const totalTokens = response.metadata?.usage?.totalTokens || 0;
|
|
1405
|
+
if (usageRequestId) {
|
|
1406
|
+
await reportAiUsage(
|
|
1407
|
+
this.http,
|
|
1408
|
+
usageRequestId,
|
|
1409
|
+
params.model,
|
|
1410
|
+
"chat",
|
|
1411
|
+
totalTokens
|
|
1412
|
+
);
|
|
1413
|
+
}
|
|
1350
1414
|
return {
|
|
1351
1415
|
id: `chatcmpl-${Date.now()}`,
|
|
1352
1416
|
object: "chat.completion",
|
|
@@ -1373,14 +1437,14 @@ var ChatCompletions = class {
|
|
|
1373
1437
|
usage: {
|
|
1374
1438
|
prompt_tokens: response.metadata?.usage?.promptTokens || 0,
|
|
1375
1439
|
completion_tokens: response.metadata?.usage?.completionTokens || 0,
|
|
1376
|
-
total_tokens:
|
|
1440
|
+
total_tokens: totalTokens
|
|
1377
1441
|
}
|
|
1378
1442
|
};
|
|
1379
1443
|
}
|
|
1380
1444
|
/**
|
|
1381
1445
|
* Parse SSE stream into async iterable of OpenAI-like chunks
|
|
1382
1446
|
*/
|
|
1383
|
-
async *parseSSEStream(response, model) {
|
|
1447
|
+
async *parseSSEStream(response, model, usageRequestId) {
|
|
1384
1448
|
const reader = response.body?.getReader();
|
|
1385
1449
|
if (!reader) {
|
|
1386
1450
|
throw new ResultError(
|
|
@@ -1458,6 +1522,15 @@ var ChatCompletions = class {
|
|
|
1458
1522
|
};
|
|
1459
1523
|
}
|
|
1460
1524
|
if (data.tokenUsage) {
|
|
1525
|
+
if (usageRequestId) {
|
|
1526
|
+
await reportAiUsage(
|
|
1527
|
+
this.http,
|
|
1528
|
+
usageRequestId,
|
|
1529
|
+
model,
|
|
1530
|
+
"chat",
|
|
1531
|
+
data.tokenUsage.totalTokens || 0
|
|
1532
|
+
);
|
|
1533
|
+
}
|
|
1461
1534
|
yield {
|
|
1462
1535
|
id: `chatcmpl-${Date.now()}`,
|
|
1463
1536
|
object: "chat.completion.chunk",
|
|
@@ -1529,17 +1602,28 @@ var Embeddings = class {
|
|
|
1529
1602
|
* ```
|
|
1530
1603
|
*/
|
|
1531
1604
|
async create(params) {
|
|
1605
|
+
const usageRequestId = aiUsageRequestId();
|
|
1532
1606
|
const response = await this.http.post(
|
|
1533
1607
|
"/api/ai/embeddings",
|
|
1534
1608
|
params
|
|
1535
1609
|
);
|
|
1610
|
+
const totalTokens = response.metadata?.usage?.totalTokens || 0;
|
|
1611
|
+
if (usageRequestId) {
|
|
1612
|
+
await reportAiUsage(
|
|
1613
|
+
this.http,
|
|
1614
|
+
usageRequestId,
|
|
1615
|
+
params.model,
|
|
1616
|
+
"embeddings",
|
|
1617
|
+
totalTokens
|
|
1618
|
+
);
|
|
1619
|
+
}
|
|
1536
1620
|
return {
|
|
1537
1621
|
object: response.object,
|
|
1538
1622
|
data: response.data,
|
|
1539
1623
|
model: response.metadata?.model,
|
|
1540
1624
|
usage: response.metadata?.usage ? {
|
|
1541
1625
|
prompt_tokens: response.metadata.usage.promptTokens || 0,
|
|
1542
|
-
total_tokens:
|
|
1626
|
+
total_tokens: totalTokens
|
|
1543
1627
|
} : {
|
|
1544
1628
|
prompt_tokens: 0,
|
|
1545
1629
|
total_tokens: 0
|
|
@@ -1577,6 +1661,7 @@ var Images = class {
|
|
|
1577
1661
|
* ```
|
|
1578
1662
|
*/
|
|
1579
1663
|
async generate(params) {
|
|
1664
|
+
const usageRequestId = aiUsageRequestId();
|
|
1580
1665
|
const response = await this.http.post(
|
|
1581
1666
|
"/api/ai/image/generation",
|
|
1582
1667
|
params
|
|
@@ -1590,6 +1675,16 @@ var Images = class {
|
|
|
1590
1675
|
} else if (response.text) {
|
|
1591
1676
|
data = [{ content: response.text }];
|
|
1592
1677
|
}
|
|
1678
|
+
const totalTokens = response.metadata?.usage?.totalTokens || 0;
|
|
1679
|
+
if (usageRequestId) {
|
|
1680
|
+
await reportAiUsage(
|
|
1681
|
+
this.http,
|
|
1682
|
+
usageRequestId,
|
|
1683
|
+
params.model,
|
|
1684
|
+
"image",
|
|
1685
|
+
totalTokens
|
|
1686
|
+
);
|
|
1687
|
+
}
|
|
1593
1688
|
return {
|
|
1594
1689
|
created: Math.floor(Date.now() / 1e3),
|
|
1595
1690
|
data,
|
package/dist/ssr.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { R as ResultClient } from './client-
|
|
2
|
-
import { o as ResultError, a as ResultConfig, G as AuthRefreshResponse } from './types-
|
|
3
|
-
import { A as AuthCookieSettings, C as CookieStore, a as CookieWriter } from './middleware-
|
|
4
|
-
export { b as AuthCookieNames, c as AuthCookieOptions, d as CookieOptions, e as CookieReader, D as DEFAULT_ACCESS_TOKEN_COOKIE, f as DEFAULT_REFRESH_TOKEN_COOKIE, U as UpdateSessionOptions, g as UpdateSessionResult, h as accessTokenCookieOptions, i as clearAuthCookies, j as getAccessTokenCookieName, k as getRefreshTokenCookieName, r as refreshTokenCookieOptions, s as setAuthCookies, u as updateSession } from './middleware-
|
|
1
|
+
import { R as ResultClient } from './client-N6MW9IE1.cjs';
|
|
2
|
+
import { o as ResultError, a as ResultConfig, G as AuthRefreshResponse } from './types-BTdjI7Yj.cjs';
|
|
3
|
+
import { A as AuthCookieSettings, C as CookieStore, a as CookieWriter } from './middleware-CGn44ocX.cjs';
|
|
4
|
+
export { b as AuthCookieNames, c as AuthCookieOptions, d as CookieOptions, e as CookieReader, D as DEFAULT_ACCESS_TOKEN_COOKIE, f as DEFAULT_REFRESH_TOKEN_COOKIE, U as UpdateSessionOptions, g as UpdateSessionResult, h as accessTokenCookieOptions, i as clearAuthCookies, j as getAccessTokenCookieName, k as getRefreshTokenCookieName, r as refreshTokenCookieOptions, s as setAuthCookies, u as updateSession } from './middleware-CGn44ocX.cjs';
|
|
5
5
|
import '@supabase/postgrest-js';
|
|
6
6
|
|
|
7
7
|
interface CreateAuthActionsOptions extends Omit<ResultConfig, "accessToken" | "edgeFunctionToken" | "isServerMode" | "auth">, AuthCookieSettings {
|
package/dist/ssr.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { R as ResultClient } from './client-
|
|
2
|
-
import { o as ResultError, a as ResultConfig, G as AuthRefreshResponse } from './types-
|
|
3
|
-
import { A as AuthCookieSettings, C as CookieStore, a as CookieWriter } from './middleware-
|
|
4
|
-
export { b as AuthCookieNames, c as AuthCookieOptions, d as CookieOptions, e as CookieReader, D as DEFAULT_ACCESS_TOKEN_COOKIE, f as DEFAULT_REFRESH_TOKEN_COOKIE, U as UpdateSessionOptions, g as UpdateSessionResult, h as accessTokenCookieOptions, i as clearAuthCookies, j as getAccessTokenCookieName, k as getRefreshTokenCookieName, r as refreshTokenCookieOptions, s as setAuthCookies, u as updateSession } from './middleware-
|
|
1
|
+
import { R as ResultClient } from './client-D5IGLPW4.js';
|
|
2
|
+
import { o as ResultError, a as ResultConfig, G as AuthRefreshResponse } from './types-BTdjI7Yj.js';
|
|
3
|
+
import { A as AuthCookieSettings, C as CookieStore, a as CookieWriter } from './middleware-8UNqItQG.js';
|
|
4
|
+
export { b as AuthCookieNames, c as AuthCookieOptions, d as CookieOptions, e as CookieReader, D as DEFAULT_ACCESS_TOKEN_COOKIE, f as DEFAULT_REFRESH_TOKEN_COOKIE, U as UpdateSessionOptions, g as UpdateSessionResult, h as accessTokenCookieOptions, i as clearAuthCookies, j as getAccessTokenCookieName, k as getRefreshTokenCookieName, r as refreshTokenCookieOptions, s as setAuthCookies, u as updateSession } from './middleware-8UNqItQG.js';
|
|
5
5
|
import '@supabase/postgrest-js';
|
|
6
6
|
|
|
7
7
|
interface CreateAuthActionsOptions extends Omit<ResultConfig, "accessToken" | "edgeFunctionToken" | "isServerMode" | "auth">, AuthCookieSettings {
|
package/dist/ssr.js
CHANGED
|
@@ -641,7 +641,15 @@ var HttpClient = class {
|
|
|
641
641
|
*/
|
|
642
642
|
constructor(config, tokenManager, logger) {
|
|
643
643
|
this.config = config;
|
|
644
|
-
|
|
644
|
+
if (!config.baseUrl) {
|
|
645
|
+
throw new ResultError(
|
|
646
|
+
"createClient needs a baseUrl, but it was undefined at runtime.",
|
|
647
|
+
0,
|
|
648
|
+
"MISSING_BASE_URL",
|
|
649
|
+
"Pass the NEXT_PUBLIC_BACKEND_URL value from .env.local. In browser code, write process.env.NEXT_PUBLIC_BACKEND_URL as a literal expression (never through an imported process module) so the bundler inlines it, and set it as a deployments build variable before deploying."
|
|
650
|
+
);
|
|
651
|
+
}
|
|
652
|
+
this.baseUrl = config.baseUrl;
|
|
645
653
|
this.fetch = config.fetch || (globalThis.fetch ? globalThis.fetch.bind(globalThis) : void 0);
|
|
646
654
|
this.anonKey = config.anonKey;
|
|
647
655
|
this.defaultHeaders = {
|
|
@@ -732,6 +740,7 @@ var HttpClient = class {
|
|
|
732
740
|
}
|
|
733
741
|
let controller;
|
|
734
742
|
let timer;
|
|
743
|
+
let removeCallerAbortListener;
|
|
735
744
|
if (this.timeout > 0 || callerSignal) {
|
|
736
745
|
controller = new AbortController();
|
|
737
746
|
if (this.timeout > 0) {
|
|
@@ -745,16 +754,11 @@ var HttpClient = class {
|
|
|
745
754
|
callerSignal.addEventListener("abort", onCallerAbort, {
|
|
746
755
|
once: true
|
|
747
756
|
});
|
|
748
|
-
|
|
749
|
-
"abort",
|
|
750
|
-
() => {
|
|
751
|
-
callerSignal.removeEventListener("abort", onCallerAbort);
|
|
752
|
-
},
|
|
753
|
-
{ once: true }
|
|
754
|
-
);
|
|
757
|
+
removeCallerAbortListener = () => callerSignal.removeEventListener("abort", onCallerAbort);
|
|
755
758
|
}
|
|
756
759
|
}
|
|
757
760
|
}
|
|
761
|
+
let detachOnExit = true;
|
|
758
762
|
try {
|
|
759
763
|
const response = await this.fetch(url, {
|
|
760
764
|
method,
|
|
@@ -775,14 +779,9 @@ var HttpClient = class {
|
|
|
775
779
|
);
|
|
776
780
|
continue;
|
|
777
781
|
}
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
}
|
|
781
|
-
return response;
|
|
782
|
+
detachOnExit = false;
|
|
783
|
+
return { response, releaseCallerSignal: removeCallerAbortListener };
|
|
782
784
|
} catch (err) {
|
|
783
|
-
if (timer !== void 0) {
|
|
784
|
-
clearTimeout(timer);
|
|
785
|
-
}
|
|
786
785
|
if (err?.name === "AbortError") {
|
|
787
786
|
if (controller?.signal.aborted && this.timeout > 0 && !callerSignal?.aborted) {
|
|
788
787
|
throw new ResultError(
|
|
@@ -802,6 +801,13 @@ var HttpClient = class {
|
|
|
802
801
|
0,
|
|
803
802
|
"NETWORK_ERROR"
|
|
804
803
|
);
|
|
804
|
+
} finally {
|
|
805
|
+
if (timer !== void 0) {
|
|
806
|
+
clearTimeout(timer);
|
|
807
|
+
}
|
|
808
|
+
if (detachOnExit) {
|
|
809
|
+
removeCallerAbortListener?.();
|
|
810
|
+
}
|
|
805
811
|
}
|
|
806
812
|
}
|
|
807
813
|
throw lastError || new ResultError(
|
|
@@ -864,7 +870,7 @@ var HttpClient = class {
|
|
|
864
870
|
});
|
|
865
871
|
}
|
|
866
872
|
this.logger.logRequest(method, url, requestHeaders, processedBody);
|
|
867
|
-
const response = await this.fetchWithRetry({
|
|
873
|
+
const { response, releaseCallerSignal } = await this.fetchWithRetry({
|
|
868
874
|
method,
|
|
869
875
|
url,
|
|
870
876
|
headers: requestHeaders,
|
|
@@ -887,6 +893,8 @@ var HttpClient = class {
|
|
|
887
893
|
);
|
|
888
894
|
}
|
|
889
895
|
throw err;
|
|
896
|
+
} finally {
|
|
897
|
+
releaseCallerSignal?.();
|
|
890
898
|
}
|
|
891
899
|
this.logger.logResponse(
|
|
892
900
|
method,
|
|
@@ -988,7 +996,7 @@ var HttpClient = class {
|
|
|
988
996
|
const callerSignal = initSignal ?? request?.signal;
|
|
989
997
|
const maxAttempts = IDEMPOTENT_METHODS.has(method.toUpperCase()) ? this.retryCount : 0;
|
|
990
998
|
this.logger.logRequest(method, url, requestHeaders, body);
|
|
991
|
-
const response = await this.fetchWithRetry({
|
|
999
|
+
const { response } = await this.fetchWithRetry({
|
|
992
1000
|
method,
|
|
993
1001
|
url,
|
|
994
1002
|
headers: requestHeaders,
|
|
@@ -1089,6 +1097,10 @@ var HttpClient = class {
|
|
|
1089
1097
|
}
|
|
1090
1098
|
return headers;
|
|
1091
1099
|
}
|
|
1100
|
+
/** Returns the publishable project key used to identify SDK usage reports. */
|
|
1101
|
+
getAnonKey() {
|
|
1102
|
+
return this.anonKey;
|
|
1103
|
+
}
|
|
1092
1104
|
async refreshAccessToken() {
|
|
1093
1105
|
if (this.isRefreshing) {
|
|
1094
1106
|
return this.refreshPromise;
|
|
@@ -1158,6 +1170,47 @@ var HttpClient = class {
|
|
|
1158
1170
|
}
|
|
1159
1171
|
};
|
|
1160
1172
|
|
|
1173
|
+
// src/modules/ai-usage.ts
|
|
1174
|
+
var USAGE_ENDPOINT = "https://beta.result.dev/api/usage/ai";
|
|
1175
|
+
async function reportAiUsage(http, requestId, model, kind, tokens) {
|
|
1176
|
+
const anonKey = http.getAnonKey();
|
|
1177
|
+
if (!anonKey || !Number.isSafeInteger(tokens) || tokens <= 0) {
|
|
1178
|
+
return;
|
|
1179
|
+
}
|
|
1180
|
+
const controller = new AbortController();
|
|
1181
|
+
const timeout = setTimeout(() => controller.abort(), 2e3);
|
|
1182
|
+
try {
|
|
1183
|
+
const request = {
|
|
1184
|
+
method: "POST",
|
|
1185
|
+
headers: { "content-type": "application/json" },
|
|
1186
|
+
body: JSON.stringify({
|
|
1187
|
+
requestId,
|
|
1188
|
+
backendUrl: http.baseUrl,
|
|
1189
|
+
anonKey,
|
|
1190
|
+
model,
|
|
1191
|
+
kind,
|
|
1192
|
+
tokens
|
|
1193
|
+
}),
|
|
1194
|
+
keepalive: true,
|
|
1195
|
+
signal: controller.signal
|
|
1196
|
+
};
|
|
1197
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
1198
|
+
try {
|
|
1199
|
+
const response = await http.fetch(USAGE_ENDPOINT, request);
|
|
1200
|
+
if (response.status < 500) return;
|
|
1201
|
+
} catch {
|
|
1202
|
+
if (controller.signal.aborted) return;
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
} catch {
|
|
1206
|
+
} finally {
|
|
1207
|
+
clearTimeout(timeout);
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
function aiUsageRequestId() {
|
|
1211
|
+
return globalThis.crypto?.randomUUID?.() ?? null;
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1161
1214
|
// src/modules/ai.ts
|
|
1162
1215
|
var SUPPORTED_AUDIO_FORMATS = [
|
|
1163
1216
|
"wav",
|
|
@@ -1245,6 +1298,7 @@ var ChatCompletions = class {
|
|
|
1245
1298
|
}
|
|
1246
1299
|
http;
|
|
1247
1300
|
async create(params) {
|
|
1301
|
+
const usageRequestId = aiUsageRequestId();
|
|
1248
1302
|
const contentSummary = summarizeContent(params.messages ?? []);
|
|
1249
1303
|
assertSupportedContent(params.model, contentSummary);
|
|
1250
1304
|
const backendParams = {
|
|
@@ -1289,7 +1343,7 @@ var ChatCompletions = class {
|
|
|
1289
1343
|
contentSummary
|
|
1290
1344
|
);
|
|
1291
1345
|
}
|
|
1292
|
-
return this.parseSSEStream(response2, params.model);
|
|
1346
|
+
return this.parseSSEStream(response2, params.model, usageRequestId);
|
|
1293
1347
|
}
|
|
1294
1348
|
let response;
|
|
1295
1349
|
try {
|
|
@@ -1298,6 +1352,16 @@ var ChatCompletions = class {
|
|
|
1298
1352
|
throw enrichUpstreamError(error, contentSummary);
|
|
1299
1353
|
}
|
|
1300
1354
|
const content = response.text || "";
|
|
1355
|
+
const totalTokens = response.metadata?.usage?.totalTokens || 0;
|
|
1356
|
+
if (usageRequestId) {
|
|
1357
|
+
await reportAiUsage(
|
|
1358
|
+
this.http,
|
|
1359
|
+
usageRequestId,
|
|
1360
|
+
params.model,
|
|
1361
|
+
"chat",
|
|
1362
|
+
totalTokens
|
|
1363
|
+
);
|
|
1364
|
+
}
|
|
1301
1365
|
return {
|
|
1302
1366
|
id: `chatcmpl-${Date.now()}`,
|
|
1303
1367
|
object: "chat.completion",
|
|
@@ -1324,14 +1388,14 @@ var ChatCompletions = class {
|
|
|
1324
1388
|
usage: {
|
|
1325
1389
|
prompt_tokens: response.metadata?.usage?.promptTokens || 0,
|
|
1326
1390
|
completion_tokens: response.metadata?.usage?.completionTokens || 0,
|
|
1327
|
-
total_tokens:
|
|
1391
|
+
total_tokens: totalTokens
|
|
1328
1392
|
}
|
|
1329
1393
|
};
|
|
1330
1394
|
}
|
|
1331
1395
|
/**
|
|
1332
1396
|
* Parse SSE stream into async iterable of OpenAI-like chunks
|
|
1333
1397
|
*/
|
|
1334
|
-
async *parseSSEStream(response, model) {
|
|
1398
|
+
async *parseSSEStream(response, model, usageRequestId) {
|
|
1335
1399
|
const reader = response.body?.getReader();
|
|
1336
1400
|
if (!reader) {
|
|
1337
1401
|
throw new ResultError(
|
|
@@ -1409,6 +1473,15 @@ var ChatCompletions = class {
|
|
|
1409
1473
|
};
|
|
1410
1474
|
}
|
|
1411
1475
|
if (data.tokenUsage) {
|
|
1476
|
+
if (usageRequestId) {
|
|
1477
|
+
await reportAiUsage(
|
|
1478
|
+
this.http,
|
|
1479
|
+
usageRequestId,
|
|
1480
|
+
model,
|
|
1481
|
+
"chat",
|
|
1482
|
+
data.tokenUsage.totalTokens || 0
|
|
1483
|
+
);
|
|
1484
|
+
}
|
|
1412
1485
|
yield {
|
|
1413
1486
|
id: `chatcmpl-${Date.now()}`,
|
|
1414
1487
|
object: "chat.completion.chunk",
|
|
@@ -1480,17 +1553,28 @@ var Embeddings = class {
|
|
|
1480
1553
|
* ```
|
|
1481
1554
|
*/
|
|
1482
1555
|
async create(params) {
|
|
1556
|
+
const usageRequestId = aiUsageRequestId();
|
|
1483
1557
|
const response = await this.http.post(
|
|
1484
1558
|
"/api/ai/embeddings",
|
|
1485
1559
|
params
|
|
1486
1560
|
);
|
|
1561
|
+
const totalTokens = response.metadata?.usage?.totalTokens || 0;
|
|
1562
|
+
if (usageRequestId) {
|
|
1563
|
+
await reportAiUsage(
|
|
1564
|
+
this.http,
|
|
1565
|
+
usageRequestId,
|
|
1566
|
+
params.model,
|
|
1567
|
+
"embeddings",
|
|
1568
|
+
totalTokens
|
|
1569
|
+
);
|
|
1570
|
+
}
|
|
1487
1571
|
return {
|
|
1488
1572
|
object: response.object,
|
|
1489
1573
|
data: response.data,
|
|
1490
1574
|
model: response.metadata?.model,
|
|
1491
1575
|
usage: response.metadata?.usage ? {
|
|
1492
1576
|
prompt_tokens: response.metadata.usage.promptTokens || 0,
|
|
1493
|
-
total_tokens:
|
|
1577
|
+
total_tokens: totalTokens
|
|
1494
1578
|
} : {
|
|
1495
1579
|
prompt_tokens: 0,
|
|
1496
1580
|
total_tokens: 0
|
|
@@ -1528,6 +1612,7 @@ var Images = class {
|
|
|
1528
1612
|
* ```
|
|
1529
1613
|
*/
|
|
1530
1614
|
async generate(params) {
|
|
1615
|
+
const usageRequestId = aiUsageRequestId();
|
|
1531
1616
|
const response = await this.http.post(
|
|
1532
1617
|
"/api/ai/image/generation",
|
|
1533
1618
|
params
|
|
@@ -1541,6 +1626,16 @@ var Images = class {
|
|
|
1541
1626
|
} else if (response.text) {
|
|
1542
1627
|
data = [{ content: response.text }];
|
|
1543
1628
|
}
|
|
1629
|
+
const totalTokens = response.metadata?.usage?.totalTokens || 0;
|
|
1630
|
+
if (usageRequestId) {
|
|
1631
|
+
await reportAiUsage(
|
|
1632
|
+
this.http,
|
|
1633
|
+
usageRequestId,
|
|
1634
|
+
params.model,
|
|
1635
|
+
"image",
|
|
1636
|
+
totalTokens
|
|
1637
|
+
);
|
|
1638
|
+
}
|
|
1544
1639
|
return {
|
|
1545
1640
|
created: Math.floor(Date.now() / 1e3),
|
|
1546
1641
|
data,
|
|
@@ -526,8 +526,10 @@ interface SocketMessage {
|
|
|
526
526
|
type ResultErrorCode = ErrorCode | (string & {});
|
|
527
527
|
interface ResultConfig {
|
|
528
528
|
/**
|
|
529
|
-
* The base URL of the Result backend API
|
|
530
|
-
*
|
|
529
|
+
* The base URL of the Result backend API (the NEXT_PUBLIC_BACKEND_URL
|
|
530
|
+
* value from .env.local). Required at runtime: constructing a client
|
|
531
|
+
* without it throws MISSING_BASE_URL. (Kept optional in the type because
|
|
532
|
+
* the SSR entrypoints intentionally fill it from process.env.)
|
|
531
533
|
*/
|
|
532
534
|
baseUrl?: string;
|
|
533
535
|
/**
|
|
@@ -526,8 +526,10 @@ interface SocketMessage {
|
|
|
526
526
|
type ResultErrorCode = ErrorCode | (string & {});
|
|
527
527
|
interface ResultConfig {
|
|
528
528
|
/**
|
|
529
|
-
* The base URL of the Result backend API
|
|
530
|
-
*
|
|
529
|
+
* The base URL of the Result backend API (the NEXT_PUBLIC_BACKEND_URL
|
|
530
|
+
* value from .env.local). Required at runtime: constructing a client
|
|
531
|
+
* without it throws MISSING_BASE_URL. (Kept optional in the type because
|
|
532
|
+
* the SSR entrypoints intentionally fill it from process.env.)
|
|
531
533
|
*/
|
|
532
534
|
baseUrl?: string;
|
|
533
535
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@resultdev/sdk",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Result Backend SDK - authentication, database, storage, functions, realtime, AI, email, web analytics and customer support for apps built on Result.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"homepage": "https://docs.result.dev",
|
|
@@ -59,8 +59,19 @@
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
},
|
|
62
|
+
"scripts": {
|
|
63
|
+
"build": "tsup",
|
|
64
|
+
"test": "vitest run",
|
|
65
|
+
"check-types": "tsc --noEmit -p tsconfig.json"
|
|
66
|
+
},
|
|
62
67
|
"dependencies": {
|
|
63
68
|
"@supabase/postgrest-js": "^1.21.3",
|
|
64
69
|
"socket.io-client": "^4.8.1"
|
|
70
|
+
},
|
|
71
|
+
"devDependencies": {
|
|
72
|
+
"@types/node": "^24.0.0",
|
|
73
|
+
"tsup": "^8.5.0",
|
|
74
|
+
"typescript": "^5.9.2",
|
|
75
|
+
"vitest": "^3.2.4"
|
|
65
76
|
}
|
|
66
|
-
}
|
|
77
|
+
}
|