@capxul/sdk 0.1.0-alpha.6 → 0.1.0-alpha.8
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 +1 -8
- package/README.md +86 -4
- package/dist/{client-ChfXWMzO.d.ts → client-ByzDfG98.d.ts} +12 -9
- package/dist/{client-DW_fW9DC.d.cts → client-DDAVWtzJ.d.cts} +12 -9
- package/dist/client.cjs +216 -26
- package/dist/client.d.cts +2 -3
- package/dist/client.d.ts +2 -3
- package/dist/client.js +216 -26
- package/dist/index.cjs +216 -26
- package/dist/index.d.cts +9 -10
- package/dist/index.d.ts +9 -10
- package/dist/index.js +216 -26
- package/dist/types-PM4AQRLP.d.ts +1136 -0
- package/dist/types-hfcOE7Oi.d.cts +1136 -0
- package/dist/webhooks.d.cts +1 -2
- package/dist/webhooks.d.ts +1 -2
- package/package.json +6 -6
- package/dist/types-CYvLP5pP.d.ts +0 -213
- package/dist/types-X02RbQ2u.d.cts +0 -213
package/dist/index.js
CHANGED
|
@@ -677,7 +677,12 @@ var Errors = {
|
|
|
677
677
|
"Idempotency key was already used for a different request",
|
|
678
678
|
{ details }
|
|
679
679
|
),
|
|
680
|
-
emailDeliveryFailed: (detail) => new CapxulError2("EMAIL_DELIVERY_FAILED", `Failed to send email: ${detail}
|
|
680
|
+
emailDeliveryFailed: (detail, details) => new CapxulError2("EMAIL_DELIVERY_FAILED", `Failed to send email: ${detail}`, {
|
|
681
|
+
details
|
|
682
|
+
}),
|
|
683
|
+
rateLimited: (details) => new CapxulError2("RATE_LIMITED", "Request was rate limited", {
|
|
684
|
+
details: { ...details }
|
|
685
|
+
}),
|
|
681
686
|
internalError: (reason) => new CapxulError2("INTERNAL_ERROR", `Internal error: ${reason}`),
|
|
682
687
|
/**
|
|
683
688
|
* Verification gate. Surfaced when a request hits a verification
|
|
@@ -750,13 +755,13 @@ function createLifecycle(initial) {
|
|
|
750
755
|
}
|
|
751
756
|
function makeBuildTimeUrlsTransport(config) {
|
|
752
757
|
if (!config.authBaseUrl || config.authBaseUrl.trim().length === 0) {
|
|
753
|
-
throw
|
|
758
|
+
throw invalidConfigError(
|
|
754
759
|
"authBaseUrl",
|
|
755
760
|
"build-time-urls transport requires a non-empty authBaseUrl."
|
|
756
761
|
);
|
|
757
762
|
}
|
|
758
763
|
if (!config.convexUrl || config.convexUrl.trim().length === 0) {
|
|
759
|
-
throw
|
|
764
|
+
throw invalidConfigError(
|
|
760
765
|
"convexUrl",
|
|
761
766
|
"build-time-urls transport requires a non-empty convexUrl."
|
|
762
767
|
);
|
|
@@ -770,6 +775,7 @@ function makeBuildTimeUrlsTransport(config) {
|
|
|
770
775
|
return {
|
|
771
776
|
authBaseUrl,
|
|
772
777
|
convexUrl,
|
|
778
|
+
ensureRuntime: async () => runtime,
|
|
773
779
|
fetch: (path, init) => fetchImpl(resolveUrl(authBaseUrl, path), init),
|
|
774
780
|
getState: lifecycle.getState,
|
|
775
781
|
subscribe: lifecycle.subscribe,
|
|
@@ -785,14 +791,15 @@ function makeBuildTimeUrlsTransport(config) {
|
|
|
785
791
|
};
|
|
786
792
|
}
|
|
787
793
|
function makePublishableKeyTransport(config) {
|
|
788
|
-
|
|
789
|
-
|
|
794
|
+
const publishableKey = config.publishableKey?.trim();
|
|
795
|
+
if (!publishableKey) {
|
|
796
|
+
throw invalidConfigError(
|
|
790
797
|
"publishableKey",
|
|
791
798
|
"publishable-key transport requires a non-empty publishableKey."
|
|
792
799
|
);
|
|
793
800
|
}
|
|
794
801
|
const fetchImpl = config.fetchImpl ?? globalThis.fetch;
|
|
795
|
-
const bootstrapUrl =
|
|
802
|
+
const bootstrapUrl = normalizeBootstrapUrl(
|
|
796
803
|
config.bootstrapUrl ?? `${CAPXUL_API_BASE_URL}/v1/client/bootstrap`
|
|
797
804
|
);
|
|
798
805
|
let authBaseUrl = "";
|
|
@@ -807,23 +814,20 @@ function makePublishableKeyTransport(config) {
|
|
|
807
814
|
const response = await fetchImpl(bootstrapUrl, {
|
|
808
815
|
method: "POST",
|
|
809
816
|
headers: { "content-type": "application/json" },
|
|
810
|
-
body: JSON.stringify({ publishableKey
|
|
817
|
+
body: JSON.stringify({ publishableKey })
|
|
811
818
|
});
|
|
812
819
|
if (!response.ok) {
|
|
813
|
-
throw
|
|
814
|
-
"publishableKey",
|
|
815
|
-
`${bootstrapUrl} failed with HTTP ${response.status}.`
|
|
816
|
-
);
|
|
820
|
+
throw await bootstrapResponseError(response, bootstrapUrl);
|
|
817
821
|
}
|
|
818
|
-
const body = await response
|
|
822
|
+
const body = await readBootstrapSuccessBody(response);
|
|
819
823
|
if (typeof body.authBaseUrl !== "string" || body.authBaseUrl.trim().length === 0) {
|
|
820
|
-
throw
|
|
824
|
+
throw bootstrapContractError(
|
|
821
825
|
"authBaseUrl",
|
|
822
826
|
"/v1/client/bootstrap returned no authBaseUrl."
|
|
823
827
|
);
|
|
824
828
|
}
|
|
825
829
|
if (typeof body.convexUrl !== "string" || body.convexUrl.trim().length === 0) {
|
|
826
|
-
throw
|
|
830
|
+
throw bootstrapContractError(
|
|
827
831
|
"convexUrl",
|
|
828
832
|
"/v1/client/bootstrap returned no convexUrl."
|
|
829
833
|
);
|
|
@@ -835,16 +839,13 @@ function makePublishableKeyTransport(config) {
|
|
|
835
839
|
return runtime;
|
|
836
840
|
})();
|
|
837
841
|
bootstrapPromise = attempt.catch((err) => {
|
|
842
|
+
const error = normalizeBootstrapThrownError(err);
|
|
838
843
|
bootstrapPromise = null;
|
|
839
844
|
lifecycle.setState({
|
|
840
845
|
status: "error",
|
|
841
|
-
error
|
|
842
|
-
code: "UNKNOWN",
|
|
843
|
-
message: "Bootstrap failed without a typed CapxulError.",
|
|
844
|
-
cause: err
|
|
845
|
-
})
|
|
846
|
+
error
|
|
846
847
|
});
|
|
847
|
-
throw
|
|
848
|
+
throw error;
|
|
848
849
|
});
|
|
849
850
|
return await bootstrapPromise;
|
|
850
851
|
}
|
|
@@ -855,6 +856,7 @@ function makePublishableKeyTransport(config) {
|
|
|
855
856
|
get convexUrl() {
|
|
856
857
|
return convexUrl;
|
|
857
858
|
},
|
|
859
|
+
ensureRuntime: ensureBootstrap,
|
|
858
860
|
fetch: async (path, init) => {
|
|
859
861
|
const resolved = await ensureBootstrap();
|
|
860
862
|
return await fetchImpl(resolveUrl(resolved.authBaseUrl, path), init);
|
|
@@ -865,7 +867,7 @@ function makePublishableKeyTransport(config) {
|
|
|
865
867
|
markAuthenticated: ({ dataClient: nextDataClient }) => {
|
|
866
868
|
const current = lifecycle.getState();
|
|
867
869
|
if (current.status !== "ready" && current.status !== "authenticated") {
|
|
868
|
-
throw
|
|
870
|
+
throw internalTransportError(
|
|
869
871
|
`markAuthenticated() called from status="${current.status}". Expected "ready" or "authenticated".`
|
|
870
872
|
);
|
|
871
873
|
}
|
|
@@ -887,6 +889,24 @@ function makePublishableKeyTransport(config) {
|
|
|
887
889
|
function stripTrailingSlash(url) {
|
|
888
890
|
return url.replace(/\/+$/, "");
|
|
889
891
|
}
|
|
892
|
+
function normalizeBootstrapUrl(url) {
|
|
893
|
+
const normalized = stripTrailingSlash(url.trim());
|
|
894
|
+
if (!isAbsoluteHttpUrl(normalized)) {
|
|
895
|
+
throw invalidConfigError(
|
|
896
|
+
"bootstrapUrl",
|
|
897
|
+
"publishable-key transport requires an absolute http(s) bootstrapUrl."
|
|
898
|
+
);
|
|
899
|
+
}
|
|
900
|
+
return normalized;
|
|
901
|
+
}
|
|
902
|
+
function isAbsoluteHttpUrl(url) {
|
|
903
|
+
try {
|
|
904
|
+
const parsed = new URL(url);
|
|
905
|
+
return parsed.protocol === "http:" || parsed.protocol === "https:";
|
|
906
|
+
} catch {
|
|
907
|
+
return false;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
890
910
|
function resolveUrl(authBaseUrl, path) {
|
|
891
911
|
if (path.startsWith("http://") || path.startsWith("https://")) {
|
|
892
912
|
return path;
|
|
@@ -894,10 +914,142 @@ function resolveUrl(authBaseUrl, path) {
|
|
|
894
914
|
return `${authBaseUrl}${path}`;
|
|
895
915
|
}
|
|
896
916
|
function assertNever(value) {
|
|
897
|
-
throw
|
|
917
|
+
throw internalTransportError(
|
|
898
918
|
`Unhandled BrowserCapxulConfig.mode: ${String(value.mode)}.`
|
|
899
919
|
);
|
|
900
920
|
}
|
|
921
|
+
function invalidConfigError(field, reason) {
|
|
922
|
+
return new CapxulError({
|
|
923
|
+
code: "INVALID_INPUT",
|
|
924
|
+
message: `Invalid ${field}: ${reason}`,
|
|
925
|
+
details: { source: "sdk-config", field, reason }
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
function bootstrapContractError(field, message) {
|
|
929
|
+
return new CapxulError({
|
|
930
|
+
code: "INVALID_INPUT",
|
|
931
|
+
message,
|
|
932
|
+
details: {
|
|
933
|
+
source: "backend-bootstrap",
|
|
934
|
+
phase: "publishable-key-bootstrap",
|
|
935
|
+
field,
|
|
936
|
+
reason: message
|
|
937
|
+
}
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
function internalTransportError(reason) {
|
|
941
|
+
return new CapxulError({
|
|
942
|
+
code: "INTERNAL_ERROR",
|
|
943
|
+
message: `Internal error: ${reason}`,
|
|
944
|
+
details: { source: "sdk-transport", reason }
|
|
945
|
+
});
|
|
946
|
+
}
|
|
947
|
+
async function bootstrapResponseError(response, bootstrapUrl) {
|
|
948
|
+
const envelope = await readBootstrapErrorEnvelope(response);
|
|
949
|
+
const wireCode = readNonEmptyString(envelope?.error?.code);
|
|
950
|
+
const normalized = normalizeBootstrapErrorCode(wireCode);
|
|
951
|
+
const message = readNonEmptyString(envelope?.error?.message) ?? `${bootstrapUrl} failed with HTTP ${response.status}.`;
|
|
952
|
+
const backendDetails = readRecord(envelope?.error?.details);
|
|
953
|
+
return new CapxulError({
|
|
954
|
+
code: normalized.code,
|
|
955
|
+
message,
|
|
956
|
+
details: {
|
|
957
|
+
...backendDetails,
|
|
958
|
+
source: "backend-bootstrap",
|
|
959
|
+
phase: "publishable-key-bootstrap",
|
|
960
|
+
httpStatus: response.status,
|
|
961
|
+
...normalized.wireCode ? { wireCode: normalized.wireCode } : {}
|
|
962
|
+
},
|
|
963
|
+
operationId: readNonEmptyString(envelope?.error?.operationId),
|
|
964
|
+
correlationId: readNonEmptyString(envelope?.error?.correlationId),
|
|
965
|
+
retryable: typeof envelope?.error?.retryable === "boolean" ? envelope.error.retryable : void 0
|
|
966
|
+
});
|
|
967
|
+
}
|
|
968
|
+
async function readBootstrapErrorEnvelope(response) {
|
|
969
|
+
try {
|
|
970
|
+
const parsed = await response.json();
|
|
971
|
+
return typeof parsed === "object" && parsed !== null ? parsed : null;
|
|
972
|
+
} catch {
|
|
973
|
+
return null;
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
async function readBootstrapSuccessBody(response) {
|
|
977
|
+
try {
|
|
978
|
+
const parsed = await response.json();
|
|
979
|
+
return typeof parsed === "object" && parsed !== null ? parsed : {};
|
|
980
|
+
} catch {
|
|
981
|
+
throw bootstrapContractError(
|
|
982
|
+
"body",
|
|
983
|
+
"/v1/client/bootstrap returned invalid JSON."
|
|
984
|
+
);
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
function normalizeBootstrapThrownError(error) {
|
|
988
|
+
if (error instanceof CapxulError) return error;
|
|
989
|
+
return new CapxulError({
|
|
990
|
+
code: "NETWORK_ERROR",
|
|
991
|
+
message: "Publishable-key bootstrap network failure.",
|
|
992
|
+
cause: error,
|
|
993
|
+
details: {
|
|
994
|
+
source: "bootstrap-network",
|
|
995
|
+
phase: "publishable-key-bootstrap"
|
|
996
|
+
}
|
|
997
|
+
});
|
|
998
|
+
}
|
|
999
|
+
function normalizeBootstrapErrorCode(wireCode) {
|
|
1000
|
+
if (wireCode === "INTERNAL_SERVER_ERROR") {
|
|
1001
|
+
return { code: "INTERNAL_ERROR", wireCode };
|
|
1002
|
+
}
|
|
1003
|
+
if (wireCode && isCapxulErrorCode(wireCode)) {
|
|
1004
|
+
return { code: wireCode };
|
|
1005
|
+
}
|
|
1006
|
+
return wireCode ? { code: "UNKNOWN", wireCode } : { code: "UNKNOWN" };
|
|
1007
|
+
}
|
|
1008
|
+
var CAPXUL_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
1009
|
+
"NOT_AUTHENTICATED",
|
|
1010
|
+
"EMAIL_DELIVERY_FAILED",
|
|
1011
|
+
"PROFILE_NOT_FOUND",
|
|
1012
|
+
"SMART_ACCOUNT_MISSING",
|
|
1013
|
+
"PLAYER_NOT_FOUND",
|
|
1014
|
+
"ACCOUNT_NOT_FOUND",
|
|
1015
|
+
"PROVIDER_ERROR",
|
|
1016
|
+
"INVALID_INPUT",
|
|
1017
|
+
"ENV_MISSING",
|
|
1018
|
+
"NOT_IMPLEMENTED",
|
|
1019
|
+
"VERIFICATION_REQUIRED",
|
|
1020
|
+
"INSUFFICIENT_BALANCE",
|
|
1021
|
+
"INVALID_RECIPIENT",
|
|
1022
|
+
"TRANSACTION_FAILED",
|
|
1023
|
+
"RATE_LIMITED",
|
|
1024
|
+
"NETWORK_ERROR",
|
|
1025
|
+
"UNKNOWN",
|
|
1026
|
+
"PERMISSION_DENIED",
|
|
1027
|
+
"API_KEY_INVALID",
|
|
1028
|
+
"API_KEY_EXPIRED",
|
|
1029
|
+
"IDEMPOTENCY_CONFLICT",
|
|
1030
|
+
"NOT_FOUND",
|
|
1031
|
+
"OPERATION_CANCELED",
|
|
1032
|
+
"OPERATION_TIMEOUT",
|
|
1033
|
+
"ACTION_REQUIRED",
|
|
1034
|
+
"KYC_REQUIRED",
|
|
1035
|
+
"POLICY_DENIED",
|
|
1036
|
+
"SAFE_NOT_READY",
|
|
1037
|
+
"PROVIDER_UNAVAILABLE",
|
|
1038
|
+
"PROVIDER_REJECTED",
|
|
1039
|
+
"RECONCILIATION_FAILED",
|
|
1040
|
+
"INTERNAL_ERROR",
|
|
1041
|
+
"QUOTE_EXPIRED",
|
|
1042
|
+
"QUOTE_NOT_FOUND"
|
|
1043
|
+
]);
|
|
1044
|
+
function isCapxulErrorCode(value) {
|
|
1045
|
+
return CAPXUL_ERROR_CODES.has(value);
|
|
1046
|
+
}
|
|
1047
|
+
function readRecord(value) {
|
|
1048
|
+
return typeof value === "object" && value !== null ? value : null;
|
|
1049
|
+
}
|
|
1050
|
+
function readNonEmptyString(value) {
|
|
1051
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
1052
|
+
}
|
|
901
1053
|
|
|
902
1054
|
// src/core/auth.ts
|
|
903
1055
|
function createAuthClient(config = {}) {
|
|
@@ -952,13 +1104,16 @@ function createAuthClient(config = {}) {
|
|
|
952
1104
|
email: signIn.user.email,
|
|
953
1105
|
token: signIn.token,
|
|
954
1106
|
convexJwt,
|
|
955
|
-
expiresAt: new Date(
|
|
1107
|
+
expiresAt: new Date(
|
|
1108
|
+
Date.now() + 30 * 24 * 60 * 60 * 1e3
|
|
1109
|
+
).toISOString()
|
|
956
1110
|
};
|
|
957
1111
|
sessionStore.set(session);
|
|
958
1112
|
if (config.auth?.createDataClient) {
|
|
959
1113
|
try {
|
|
960
1114
|
dataClient = await config.auth.createDataClient(session);
|
|
961
1115
|
mutableConfig(config).data = dataClient;
|
|
1116
|
+
transport.markAuthenticated({ dataClient });
|
|
962
1117
|
} catch (cause) {
|
|
963
1118
|
return [
|
|
964
1119
|
new CapxulError({
|
|
@@ -977,6 +1132,8 @@ function createAuthClient(config = {}) {
|
|
|
977
1132
|
sessionStore.clear();
|
|
978
1133
|
dataClient = null;
|
|
979
1134
|
mutableConfig(config).data = void 0;
|
|
1135
|
+
const transport = getTransport();
|
|
1136
|
+
transport?.clearAuth();
|
|
980
1137
|
return [null, void 0];
|
|
981
1138
|
},
|
|
982
1139
|
serviceTokenMint: async () => stub("auth.serviceTokenMint"),
|
|
@@ -1030,16 +1187,19 @@ async function postBetterAuth(transport, path, body, code, signal) {
|
|
|
1030
1187
|
body: JSON.stringify(body),
|
|
1031
1188
|
signal
|
|
1032
1189
|
});
|
|
1190
|
+
const text = await response.text();
|
|
1033
1191
|
if (!response.ok) {
|
|
1192
|
+
const parsedError = parseBetterAuthError(text);
|
|
1034
1193
|
return [
|
|
1035
1194
|
new CapxulError({
|
|
1036
|
-
code,
|
|
1037
|
-
message: `BetterAuth ${path} failed with HTTP ${response.status}
|
|
1195
|
+
code: parsedError.code ?? code,
|
|
1196
|
+
message: parsedError.message ?? `BetterAuth ${path} failed with HTTP ${response.status}.`,
|
|
1197
|
+
details: parsedError.details,
|
|
1198
|
+
retryable: parsedError.retryable
|
|
1038
1199
|
}),
|
|
1039
1200
|
null
|
|
1040
1201
|
];
|
|
1041
1202
|
}
|
|
1042
|
-
const text = await response.text();
|
|
1043
1203
|
return [null, text ? JSON.parse(text) : void 0];
|
|
1044
1204
|
} catch (cause) {
|
|
1045
1205
|
return [
|
|
@@ -1052,6 +1212,36 @@ async function postBetterAuth(transport, path, body, code, signal) {
|
|
|
1052
1212
|
];
|
|
1053
1213
|
}
|
|
1054
1214
|
}
|
|
1215
|
+
function parseBetterAuthError(text) {
|
|
1216
|
+
if (!text.trim()) {
|
|
1217
|
+
return {};
|
|
1218
|
+
}
|
|
1219
|
+
try {
|
|
1220
|
+
const body = JSON.parse(text);
|
|
1221
|
+
if (!body || typeof body !== "object") {
|
|
1222
|
+
return {};
|
|
1223
|
+
}
|
|
1224
|
+
const record = body;
|
|
1225
|
+
const nested = record.error && typeof record.error === "object" ? record.error : record;
|
|
1226
|
+
const code = typeof nested.code === "string" ? nested.code : void 0;
|
|
1227
|
+
const message = typeof nested.message === "string" ? nested.message : void 0;
|
|
1228
|
+
const details = nested.details && typeof nested.details === "object" ? nested.details : void 0;
|
|
1229
|
+
const correlationId = typeof nested.correlationId === "string" ? nested.correlationId : void 0;
|
|
1230
|
+
const retryable = typeof nested.retryable === "boolean" ? nested.retryable : void 0;
|
|
1231
|
+
return {
|
|
1232
|
+
code: isCapxulErrorCode2(code) ? code : void 0,
|
|
1233
|
+
message,
|
|
1234
|
+
details,
|
|
1235
|
+
correlationId,
|
|
1236
|
+
retryable
|
|
1237
|
+
};
|
|
1238
|
+
} catch {
|
|
1239
|
+
return {};
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
function isCapxulErrorCode2(code) {
|
|
1243
|
+
return code === "NOT_AUTHENTICATED" || code === "EMAIL_DELIVERY_FAILED" || code === "INVALID_INPUT" || code === "RATE_LIMITED" || code === "NETWORK_ERROR" || code === "API_KEY_INVALID" || code === "API_KEY_EXPIRED";
|
|
1244
|
+
}
|
|
1055
1245
|
async function exchangeConvexToken(transport, config, token, signal) {
|
|
1056
1246
|
const path = config.auth?.convexTokenUrl ?? "/convex/token";
|
|
1057
1247
|
try {
|