@xylex-group/athena 1.2.0 → 1.3.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.
package/dist/index.cjs CHANGED
@@ -391,7 +391,7 @@ function mergeOptions(...options) {
391
391
  }, void 0);
392
392
  }
393
393
  function createMutationQuery(executor, defaultColumns = DEFAULT_COLUMNS) {
394
- let selectedColumns = defaultColumns;
394
+ let selectedColumns = defaultColumns === null ? void 0 : defaultColumns;
395
395
  let selectedOptions;
396
396
  let promise = null;
397
397
  const run = (columns, options) => {
@@ -802,15 +802,15 @@ function createTableBuilder(tableName, client) {
802
802
  const mergedOptions = mergeOptions(options, selectOptions);
803
803
  const payload = {
804
804
  table_name: tableName,
805
- update_body: values,
805
+ set: values,
806
806
  conditions: filters,
807
- columns,
808
807
  strip_nulls: mergedOptions?.stripNulls ?? true
809
808
  };
809
+ if (columns) payload.columns = columns;
810
810
  const response = await client.updateGateway(payload, mergedOptions);
811
811
  return formatResult(response);
812
812
  };
813
- const mutation = createMutationQuery(executeUpdate);
813
+ const mutation = createMutationQuery(executeUpdate, null);
814
814
  const updateChain = {};
815
815
  const filterMethods2 = createFilterMethods(state, addCondition, updateChain);
816
816
  Object.assign(updateChain, filterMethods2, mutation);
@@ -827,13 +827,13 @@ function createTableBuilder(tableName, client) {
827
827
  const payload = {
828
828
  table_name: tableName,
829
829
  resource_id: resourceId,
830
- conditions: filters,
831
- columns
830
+ conditions: filters
832
831
  };
832
+ if (columns) payload.columns = columns;
833
833
  const response = await client.deleteGateway(payload, mergedOptions);
834
834
  return formatResult(response);
835
835
  };
836
- return createMutationQuery(executeDelete);
836
+ return createMutationQuery(executeDelete, null);
837
837
  },
838
838
  async single(columns, options) {
839
839
  const response = await builder.select(columns, options);
@@ -964,10 +964,334 @@ var Backend = {
964
964
  ScyllaDB: { type: "scylladb" }
965
965
  };
966
966
 
967
+ // src/auxiliaries.ts
968
+ function isRecord2(value) {
969
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
970
+ }
971
+ function safeStringify(value) {
972
+ try {
973
+ const serialized = JSON.stringify(value);
974
+ return serialized ?? String(value);
975
+ } catch {
976
+ return "[unserializable]";
977
+ }
978
+ }
979
+ function contextHint(context) {
980
+ if (!context?.identity) return void 0;
981
+ const identity = typeof context.identity === "string" ? context.identity : safeStringify(context.identity);
982
+ return `Identity: ${identity}`;
983
+ }
984
+ function isAthenaResultLike(value) {
985
+ return isRecord2(value) && "status" in value && "error" in value && "data" in value && typeof value.status === "number";
986
+ }
987
+ function operationFromDetails(details) {
988
+ if (!details?.endpoint) return void 0;
989
+ if (details.endpoint === "/gateway/fetch" || details.endpoint === "/gateway/query") return "select";
990
+ if (details.endpoint === "/gateway/insert") return "insert";
991
+ if (details.endpoint === "/gateway/update") return "update";
992
+ if (details.endpoint === "/gateway/delete") return "delete";
993
+ if (details.endpoint === "/gateway/rpc" || details.endpoint.startsWith("/rpc/")) return "rpc";
994
+ return void 0;
995
+ }
996
+ function matchRegex(input, patterns) {
997
+ for (const pattern of patterns) {
998
+ const match = pattern.exec(input);
999
+ if (match?.[1]) return match[1];
1000
+ }
1001
+ return void 0;
1002
+ }
1003
+ function extractConstraint(message) {
1004
+ return matchRegex(message, [
1005
+ /unique constraint\s+["'`]([^"'`]+)["'`]/i,
1006
+ /constraint\s+["'`]([^"'`]+)["'`]/i
1007
+ ]);
1008
+ }
1009
+ function extractTable(message) {
1010
+ return matchRegex(message, [
1011
+ /(?:table|relation)\s+["'`]([^"'`]+)["'`]/i,
1012
+ /on\s+table\s+([a-zA-Z0-9_.]+)/i
1013
+ ]);
1014
+ }
1015
+ function classifyKind(status, code, message) {
1016
+ const lower = message.toLowerCase();
1017
+ const hasUniquePattern = lower.includes("unique constraint") || lower.includes("duplicate key") || lower.includes("already exists") || lower.includes("duplicate");
1018
+ const hasNotFoundPattern = lower.includes("not found") || lower.includes("no rows");
1019
+ const hasValidationPattern = lower.includes("validation") || lower.includes("invalid") || lower.includes("malformed");
1020
+ const hasAuthPattern = lower.includes("unauthorized") || lower.includes("forbidden") || lower.includes("auth");
1021
+ const hasRateLimitPattern = lower.includes("rate limit") || lower.includes("too many requests");
1022
+ const hasTransientPattern = lower.includes("timeout") || lower.includes("temporar") || lower.includes("connection reset") || lower.includes("socket") || lower.includes("deadlock");
1023
+ if (status === 409 || hasUniquePattern) return "unique_violation";
1024
+ if (status === 404 || hasNotFoundPattern) return "not_found";
1025
+ if (status === 401 || status === 403 || hasAuthPattern) return "auth";
1026
+ if (status === 429 || hasRateLimitPattern) return "rate_limit";
1027
+ if (status === 400 || status === 422 || hasValidationPattern) return "validation";
1028
+ if (code === "NETWORK_ERROR" || status === 0 || status !== void 0 && status >= 500 || hasTransientPattern) {
1029
+ return "transient";
1030
+ }
1031
+ return "unknown";
1032
+ }
1033
+ function toGatewayCode(kind, status) {
1034
+ if (kind === "transient" && (status === 0 || status === void 0)) return "NETWORK_ERROR";
1035
+ if (kind === "validation") return "INVALID_JSON";
1036
+ if (status !== void 0 && status >= 400) return "HTTP_ERROR";
1037
+ return "UNKNOWN_ERROR";
1038
+ }
1039
+ function toAthenaGatewayError(source, fallbackMessage, context) {
1040
+ if (isAthenaGatewayError(source)) {
1041
+ return source;
1042
+ }
1043
+ if (isAthenaResultLike(source) && source.errorDetails) {
1044
+ return new AthenaGatewayError({
1045
+ code: source.errorDetails.code,
1046
+ message: source.error ?? source.errorDetails.message,
1047
+ status: source.status,
1048
+ endpoint: source.errorDetails.endpoint,
1049
+ method: source.errorDetails.method,
1050
+ requestId: source.errorDetails.requestId,
1051
+ hint: source.errorDetails.hint,
1052
+ cause: source.errorDetails.cause
1053
+ });
1054
+ }
1055
+ const normalized = normalizeAthenaError(source, context);
1056
+ const message = isAthenaResultLike(source) && source.error == null && source.errorDetails == null ? fallbackMessage : normalized.message || fallbackMessage;
1057
+ return new AthenaGatewayError({
1058
+ code: toGatewayCode(normalized.kind, normalized.status),
1059
+ message,
1060
+ status: normalized.status ?? 0,
1061
+ hint: normalized.constraint != null ? `Constraint: ${normalized.constraint}` : contextHint(context),
1062
+ cause: typeof normalized.raw === "string" ? normalized.raw : safeStringify(normalized.raw)
1063
+ });
1064
+ }
1065
+ function isOk(result) {
1066
+ return result.error == null && result.status >= 200 && result.status < 300;
1067
+ }
1068
+ function normalizeAthenaError(resultOrError, context) {
1069
+ if (isAthenaResultLike(resultOrError)) {
1070
+ const details = resultOrError.errorDetails;
1071
+ const message2 = resultOrError.error ?? details?.message ?? `Athena ${context?.operation ?? operationFromDetails(details) ?? "request"} failed`;
1072
+ const operation = context?.operation ?? operationFromDetails(details);
1073
+ const table = context?.table ?? extractTable(message2);
1074
+ const constraint = extractConstraint(message2);
1075
+ const kind = classifyKind(resultOrError.status, details?.code, message2);
1076
+ return {
1077
+ kind,
1078
+ status: resultOrError.status,
1079
+ constraint,
1080
+ table,
1081
+ operation,
1082
+ message: message2,
1083
+ raw: resultOrError.raw
1084
+ };
1085
+ }
1086
+ if (isAthenaGatewayError(resultOrError)) {
1087
+ const details = resultOrError.toDetails();
1088
+ const operation = context?.operation ?? operationFromDetails(details);
1089
+ const table = context?.table ?? extractTable(resultOrError.message);
1090
+ const constraint = extractConstraint(resultOrError.message);
1091
+ const kind = classifyKind(resultOrError.status, resultOrError.code, resultOrError.message);
1092
+ return {
1093
+ kind,
1094
+ status: resultOrError.status,
1095
+ constraint,
1096
+ table,
1097
+ operation,
1098
+ message: resultOrError.message,
1099
+ raw: resultOrError
1100
+ };
1101
+ }
1102
+ if (resultOrError instanceof Error) {
1103
+ const maybeStatus = isRecord2(resultOrError) && typeof resultOrError.status === "number" ? resultOrError.status : void 0;
1104
+ return {
1105
+ kind: classifyKind(maybeStatus, void 0, resultOrError.message),
1106
+ status: maybeStatus,
1107
+ constraint: extractConstraint(resultOrError.message),
1108
+ table: context?.table ?? extractTable(resultOrError.message),
1109
+ operation: context?.operation,
1110
+ message: resultOrError.message,
1111
+ raw: resultOrError
1112
+ };
1113
+ }
1114
+ const message = typeof resultOrError === "string" ? resultOrError : "Unknown Athena error";
1115
+ return {
1116
+ kind: classifyKind(void 0, void 0, message),
1117
+ status: void 0,
1118
+ constraint: extractConstraint(message),
1119
+ table: context?.table ?? extractTable(message),
1120
+ operation: context?.operation,
1121
+ message,
1122
+ raw: resultOrError
1123
+ };
1124
+ }
1125
+ function unwrapRows(result, options) {
1126
+ if (!isOk(result)) {
1127
+ throw toAthenaGatewayError(result, "Athena request failed", options?.context);
1128
+ }
1129
+ if (result.data == null) return [];
1130
+ return Array.isArray(result.data) ? result.data : [result.data];
1131
+ }
1132
+ function unwrap(result, options) {
1133
+ if (!isOk(result)) {
1134
+ throw toAthenaGatewayError(result, "Athena request failed", options?.context);
1135
+ }
1136
+ if (result.data == null && !options?.allowNull) {
1137
+ throw toAthenaGatewayError(result, "Expected data but received null", options?.context);
1138
+ }
1139
+ return result.data;
1140
+ }
1141
+ function unwrapOne(result, options) {
1142
+ const rows = unwrapRows(result, options);
1143
+ if (!rows.length) {
1144
+ if (options?.allowNull) return null;
1145
+ throw toAthenaGatewayError(result, "Expected one row but received none", options?.context);
1146
+ }
1147
+ if (options?.requireExactlyOne && rows.length !== 1) {
1148
+ throw toAthenaGatewayError(
1149
+ result,
1150
+ `Expected exactly one row but received ${rows.length}`,
1151
+ options.context
1152
+ );
1153
+ }
1154
+ return rows[0];
1155
+ }
1156
+ function requireSuccess(result, context) {
1157
+ if (!isOk(result)) {
1158
+ throw toAthenaGatewayError(
1159
+ result,
1160
+ `Athena ${context?.operation ?? "request"} failed`,
1161
+ context
1162
+ );
1163
+ }
1164
+ return result;
1165
+ }
1166
+ function requireAffected(result, options, context) {
1167
+ requireSuccess(result, context);
1168
+ const minimum = options?.min ?? 1;
1169
+ const count = result.count;
1170
+ if (count == null) {
1171
+ throw new AthenaGatewayError({
1172
+ code: "UNKNOWN_ERROR",
1173
+ status: result.status,
1174
+ message: "Expected affected row count but response.count is missing",
1175
+ hint: 'Set call option { count: "exact" } for mutation postcondition checks.',
1176
+ cause: safeStringify(result.raw)
1177
+ });
1178
+ }
1179
+ if (count < minimum) {
1180
+ throw new AthenaGatewayError({
1181
+ code: "UNKNOWN_ERROR",
1182
+ status: result.status,
1183
+ message: `Expected at least ${minimum} affected rows but received ${count}`,
1184
+ hint: contextHint(context),
1185
+ cause: safeStringify(result.raw)
1186
+ });
1187
+ }
1188
+ return count;
1189
+ }
1190
+ function applyBounds(value, options) {
1191
+ if (options?.min !== void 0 && value < options.min) return null;
1192
+ if (options?.max !== void 0 && value > options.max) return null;
1193
+ return value;
1194
+ }
1195
+ function parseIntegerString(value) {
1196
+ const normalized = value.trim();
1197
+ if (!/^[-+]?\d+$/.test(normalized)) return null;
1198
+ const parsed = Number(normalized);
1199
+ if (!Number.isFinite(parsed) || !Number.isInteger(parsed)) return null;
1200
+ return parsed;
1201
+ }
1202
+ function coerceInt(value, options) {
1203
+ if (value == null) return null;
1204
+ if (typeof value === "number") {
1205
+ if (!Number.isFinite(value) || !Number.isInteger(value)) return null;
1206
+ return applyBounds(value, options);
1207
+ }
1208
+ if (typeof value === "string") {
1209
+ const parsed = parseIntegerString(value);
1210
+ if (parsed == null) return null;
1211
+ return applyBounds(parsed, options);
1212
+ }
1213
+ if (typeof value === "bigint") {
1214
+ if (options?.strictBigInt) {
1215
+ if (value > BigInt(Number.MAX_SAFE_INTEGER) || value < BigInt(Number.MIN_SAFE_INTEGER)) {
1216
+ return null;
1217
+ }
1218
+ }
1219
+ const parsed = Number(value);
1220
+ if (!Number.isFinite(parsed) || !Number.isInteger(parsed)) return null;
1221
+ return applyBounds(parsed, options);
1222
+ }
1223
+ return null;
1224
+ }
1225
+ function assertInt(value, label = "value", options) {
1226
+ const parsed = coerceInt(value, options);
1227
+ if (parsed == null) {
1228
+ throw new TypeError(`${label} must be a finite integer`);
1229
+ }
1230
+ return parsed;
1231
+ }
1232
+ function defaultShouldRetry(error) {
1233
+ const normalized = normalizeAthenaError(error);
1234
+ return normalized.kind === "transient" || normalized.kind === "rate_limit";
1235
+ }
1236
+ function computeDelayMs(attempt, error, config) {
1237
+ const baseDelay = config.baseDelayMs;
1238
+ const rawDelay = typeof config.backoff === "function" ? config.backoff(attempt, error) : config.backoff === "linear" ? baseDelay * attempt : baseDelay * Math.pow(2, attempt - 1);
1239
+ const safeDelay = Number.isFinite(rawDelay) ? Math.max(0, rawDelay) : 0;
1240
+ const clamped = Math.min(config.maxDelayMs, safeDelay);
1241
+ const jitterFactor = typeof config.jitter === "number" ? Math.max(0, Math.min(1, config.jitter)) : config.jitter ? 0.2 : 0;
1242
+ if (!jitterFactor) return clamped;
1243
+ const deviation = clamped * jitterFactor;
1244
+ const offset = (Math.random() * 2 - 1) * deviation;
1245
+ return Math.max(0, clamped + offset);
1246
+ }
1247
+ function sleep(ms) {
1248
+ if (ms <= 0) return Promise.resolve();
1249
+ return new Promise((resolve) => {
1250
+ setTimeout(resolve, ms);
1251
+ });
1252
+ }
1253
+ async function withRetry(config, fn) {
1254
+ const retries = Math.max(0, Math.trunc(config.retries));
1255
+ const shouldRetry = config.shouldRetry ?? defaultShouldRetry;
1256
+ const resolvedConfig = {
1257
+ baseDelayMs: config.baseDelayMs ?? 100,
1258
+ maxDelayMs: config.maxDelayMs ?? 1e4,
1259
+ backoff: config.backoff ?? "exponential",
1260
+ jitter: config.jitter ?? false
1261
+ };
1262
+ for (let attempts = 0; attempts <= retries; attempts += 1) {
1263
+ try {
1264
+ return await fn();
1265
+ } catch (error) {
1266
+ if (attempts >= retries) {
1267
+ throw error;
1268
+ }
1269
+ const currentAttempt = attempts + 1;
1270
+ const retry = await shouldRetry(error, currentAttempt);
1271
+ if (!retry) {
1272
+ throw error;
1273
+ }
1274
+ const delay = computeDelayMs(currentAttempt, error, resolvedConfig);
1275
+ await sleep(delay);
1276
+ }
1277
+ }
1278
+ throw new Error("withRetry reached an unexpected state");
1279
+ }
1280
+
967
1281
  exports.AthenaClient = AthenaClient;
968
1282
  exports.AthenaGatewayError = AthenaGatewayError;
969
1283
  exports.Backend = Backend;
1284
+ exports.assertInt = assertInt;
1285
+ exports.coerceInt = coerceInt;
970
1286
  exports.createClient = createClient;
971
1287
  exports.isAthenaGatewayError = isAthenaGatewayError;
1288
+ exports.isOk = isOk;
1289
+ exports.normalizeAthenaError = normalizeAthenaError;
1290
+ exports.requireAffected = requireAffected;
1291
+ exports.requireSuccess = requireSuccess;
1292
+ exports.unwrap = unwrap;
1293
+ exports.unwrapOne = unwrapOne;
1294
+ exports.unwrapRows = unwrapRows;
1295
+ exports.withRetry = withRetry;
972
1296
  //# sourceMappingURL=index.cjs.map
973
1297
  //# sourceMappingURL=index.cjs.map