tersejson 0.1.0 → 0.2.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 (38) hide show
  1. package/README.md +47 -4
  2. package/dist/{client-BQAZg7I8.d.mts → client-CFGvusCj.d.mts} +1 -1
  3. package/dist/{client-DOOGwp_p.d.ts → client-hUXNNGcN.d.ts} +1 -1
  4. package/dist/client.d.mts +2 -2
  5. package/dist/client.d.ts +2 -2
  6. package/dist/client.js.map +1 -1
  7. package/dist/client.mjs.map +1 -1
  8. package/dist/{express-LSVylWpN.d.ts → express-Da7WcJtt.d.ts} +1 -1
  9. package/dist/{express-BoL__Ao6.d.mts → express-O5w2NBTf.d.mts} +1 -1
  10. package/dist/express.d.mts +2 -2
  11. package/dist/express.d.ts +2 -2
  12. package/dist/graphql-C3PTnqnZ.d.mts +81 -0
  13. package/dist/graphql-DmtweJgh.d.ts +81 -0
  14. package/dist/graphql-client-2H4FjmRc.d.mts +123 -0
  15. package/dist/graphql-client-BXGtWqe9.d.ts +123 -0
  16. package/dist/graphql-client.d.mts +2 -0
  17. package/dist/graphql-client.d.ts +2 -0
  18. package/dist/graphql-client.js +215 -0
  19. package/dist/graphql-client.js.map +1 -0
  20. package/dist/graphql-client.mjs +207 -0
  21. package/dist/graphql-client.mjs.map +1 -0
  22. package/dist/graphql.d.mts +3 -0
  23. package/dist/graphql.d.ts +3 -0
  24. package/dist/graphql.js +304 -0
  25. package/dist/graphql.js.map +1 -0
  26. package/dist/graphql.mjs +296 -0
  27. package/dist/graphql.mjs.map +1 -0
  28. package/dist/index.d.mts +5 -3
  29. package/dist/index.d.ts +5 -3
  30. package/dist/index.js +400 -3
  31. package/dist/index.js.map +1 -1
  32. package/dist/index.mjs +398 -4
  33. package/dist/index.mjs.map +1 -1
  34. package/dist/integrations.js.map +1 -1
  35. package/dist/integrations.mjs.map +1 -1
  36. package/dist/{types-CzaGQaV7.d.mts → types-BTonKlz8.d.mts} +56 -1
  37. package/dist/{types-CzaGQaV7.d.ts → types-BTonKlz8.d.ts} +56 -1
  38. package/package.json +44 -3
package/dist/index.js CHANGED
@@ -10,6 +10,9 @@ var __export = (target, all) => {
10
10
  function isTersePayload(value) {
11
11
  return typeof value === "object" && value !== null && "__terse__" in value && value.__terse__ === true && "v" in value && "k" in value && "d" in value;
12
12
  }
13
+ function isGraphQLTersePayload(value) {
14
+ return typeof value === "object" && value !== null && "data" in value && "__terse__" in value && typeof value.__terse__ === "object" && value.__terse__ !== null && "v" in value.__terse__ && "k" in value.__terse__ && "paths" in value.__terse__;
15
+ }
13
16
 
14
17
  // src/core.ts
15
18
  function alphaGenerator(index) {
@@ -650,7 +653,7 @@ __export(client_exports, {
650
653
  createFetch: () => createFetch,
651
654
  default: () => client_default,
652
655
  expand: () => expand,
653
- fetch: () => fetch,
656
+ fetch: () => fetch2,
654
657
  isTersePayload: () => isTersePayload,
655
658
  process: () => process2,
656
659
  proxy: () => wrapWithProxy,
@@ -702,7 +705,7 @@ function createFetch(options = {}) {
702
705
  });
703
706
  };
704
707
  }
705
- var fetch = createFetch();
708
+ var fetch2 = createFetch();
706
709
  function useTerseFetch(options = {}) {
707
710
  return createFetch(options);
708
711
  }
@@ -720,7 +723,7 @@ var axiosInterceptor = {
720
723
  return response;
721
724
  }
722
725
  };
723
- var client_default = { fetch, createFetch, expand, proxy: wrapWithProxy, process: process2 };
726
+ var client_default = { fetch: fetch2, createFetch, expand, proxy: wrapWithProxy, process: process2 };
724
727
 
725
728
  // src/integrations.ts
726
729
  var integrations_exports = {};
@@ -982,6 +985,397 @@ var integrations_default = {
982
985
  createQueryFn
983
986
  };
984
987
 
988
+ // src/graphql.ts
989
+ var graphql_exports = {};
990
+ __export(graphql_exports, {
991
+ compressGraphQLResponse: () => compressGraphQLResponse,
992
+ createTerseFormatFn: () => createTerseFormatFn,
993
+ default: () => graphql_default,
994
+ findCompressibleArrays: () => findCompressibleArrays,
995
+ terseGraphQL: () => terseGraphQL
996
+ });
997
+ var DEFAULT_OPTIONS3 = {
998
+ minArrayLength: 2,
999
+ debug: false,
1000
+ minKeyLength: 3,
1001
+ maxDepth: 10,
1002
+ keyPattern: "alpha",
1003
+ nestedHandling: "deep",
1004
+ homogeneousOnly: false,
1005
+ excludeKeys: [],
1006
+ includeKeys: [],
1007
+ excludePaths: []
1008
+ };
1009
+ function findCompressibleArrays(data, basePath = "data", options, currentDepth = 0) {
1010
+ const results = [];
1011
+ if (currentDepth >= options.maxDepth) return results;
1012
+ if (Array.isArray(data)) {
1013
+ if (isCompressibleArray(data) && data.length >= options.minArrayLength && !options.excludePaths.includes(basePath)) {
1014
+ results.push({ path: basePath, data });
1015
+ }
1016
+ data.forEach((item, index) => {
1017
+ if (typeof item === "object" && item !== null) {
1018
+ const nestedResults = findCompressibleArrays(
1019
+ item,
1020
+ `${basePath}[${index}]`,
1021
+ options,
1022
+ currentDepth + 1
1023
+ );
1024
+ results.push(...nestedResults);
1025
+ }
1026
+ });
1027
+ } else if (typeof data === "object" && data !== null) {
1028
+ for (const [key, value] of Object.entries(data)) {
1029
+ const path = `${basePath}.${key}`;
1030
+ const nestedResults = findCompressibleArrays(value, path, options, currentDepth + 1);
1031
+ results.push(...nestedResults);
1032
+ }
1033
+ }
1034
+ return results;
1035
+ }
1036
+ function collectKeysFromArrays(arrays, options) {
1037
+ const allKeys = /* @__PURE__ */ new Set();
1038
+ const { minKeyLength = 3, excludeKeys = [], includeKeys = [] } = options;
1039
+ function collectFromObject(obj, depth = 0) {
1040
+ if (depth >= (options.maxDepth ?? 10)) return;
1041
+ for (const key of Object.keys(obj)) {
1042
+ if (excludeKeys.includes(key)) continue;
1043
+ const shouldInclude = includeKeys.includes(key) || key.length >= minKeyLength;
1044
+ if (shouldInclude) {
1045
+ allKeys.add(key);
1046
+ }
1047
+ const value = obj[key];
1048
+ if (Array.isArray(value)) {
1049
+ for (const item of value) {
1050
+ if (typeof item === "object" && item !== null && !Array.isArray(item)) {
1051
+ collectFromObject(item, depth + 1);
1052
+ }
1053
+ }
1054
+ } else if (typeof value === "object" && value !== null) {
1055
+ collectFromObject(value, depth + 1);
1056
+ }
1057
+ }
1058
+ }
1059
+ for (const { data } of arrays) {
1060
+ for (const item of data) {
1061
+ collectFromObject(item);
1062
+ }
1063
+ }
1064
+ return allKeys;
1065
+ }
1066
+ function compressObjectWithMap(obj, keyToShort, maxDepth, currentDepth = 0) {
1067
+ if (currentDepth >= maxDepth) return obj;
1068
+ const compressed = {};
1069
+ for (const [key, value] of Object.entries(obj)) {
1070
+ const shortKey = keyToShort.get(key) ?? key;
1071
+ if (Array.isArray(value)) {
1072
+ compressed[shortKey] = value.map((item) => {
1073
+ if (typeof item === "object" && item !== null && !Array.isArray(item)) {
1074
+ return compressObjectWithMap(
1075
+ item,
1076
+ keyToShort,
1077
+ maxDepth,
1078
+ currentDepth + 1
1079
+ );
1080
+ }
1081
+ return item;
1082
+ });
1083
+ } else if (typeof value === "object" && value !== null) {
1084
+ compressed[shortKey] = compressObjectWithMap(
1085
+ value,
1086
+ keyToShort,
1087
+ maxDepth,
1088
+ currentDepth + 1
1089
+ );
1090
+ } else {
1091
+ compressed[shortKey] = value;
1092
+ }
1093
+ }
1094
+ return compressed;
1095
+ }
1096
+ function setAtPath(obj, path, value) {
1097
+ const parts = path.split(/\.|\[(\d+)\]/).filter(Boolean);
1098
+ let current = obj;
1099
+ for (let i = 0; i < parts.length - 1; i++) {
1100
+ const part = parts[i];
1101
+ const isIndex = /^\d+$/.test(part);
1102
+ if (isIndex) {
1103
+ current = current[parseInt(part, 10)];
1104
+ } else {
1105
+ current = current[part];
1106
+ }
1107
+ }
1108
+ const lastPart = parts[parts.length - 1];
1109
+ const isLastIndex = /^\d+$/.test(lastPart);
1110
+ if (isLastIndex) {
1111
+ current[parseInt(lastPart, 10)] = value;
1112
+ } else {
1113
+ current[lastPart] = value;
1114
+ }
1115
+ }
1116
+ function compressGraphQLResponse(response, options = {}) {
1117
+ const config = { ...DEFAULT_OPTIONS3, ...options };
1118
+ if (!response.data) {
1119
+ return response;
1120
+ }
1121
+ const arrays = findCompressibleArrays(response.data, "data", {
1122
+ minArrayLength: config.minArrayLength,
1123
+ excludePaths: config.excludePaths,
1124
+ maxDepth: config.maxDepth
1125
+ });
1126
+ const filteredArrays = config.shouldCompress ? arrays.filter(({ path, data }) => config.shouldCompress(data, path)) : arrays;
1127
+ if (filteredArrays.length === 0) {
1128
+ return response;
1129
+ }
1130
+ const allKeys = collectKeysFromArrays(filteredArrays, config);
1131
+ const sortedKeys = Array.from(allKeys).sort();
1132
+ const { generator } = createKeyGenerator(config.keyPattern);
1133
+ const keyToShort = /* @__PURE__ */ new Map();
1134
+ const keyMap = {};
1135
+ sortedKeys.forEach((key, index) => {
1136
+ const shortKey = generator(index);
1137
+ if (shortKey.length < key.length) {
1138
+ keyToShort.set(key, shortKey);
1139
+ keyMap[shortKey] = key;
1140
+ }
1141
+ });
1142
+ if (Object.keys(keyMap).length === 0) {
1143
+ return response;
1144
+ }
1145
+ const clonedData = JSON.parse(JSON.stringify(response.data));
1146
+ const sortedArrays = [...filteredArrays].sort((a, b) => {
1147
+ const depthA = (a.path.match(/\./g) || []).length + (a.path.match(/\[/g) || []).length;
1148
+ const depthB = (b.path.match(/\./g) || []).length + (b.path.match(/\[/g) || []).length;
1149
+ return depthB - depthA;
1150
+ });
1151
+ const paths = [];
1152
+ for (const { path, data } of sortedArrays) {
1153
+ const compressedArray = data.map(
1154
+ (item) => compressObjectWithMap(item, keyToShort, config.maxDepth)
1155
+ );
1156
+ setAtPath({ data: clonedData }, path, compressedArray);
1157
+ paths.push(path);
1158
+ }
1159
+ const terseMeta = {
1160
+ v: 1,
1161
+ k: keyMap,
1162
+ paths
1163
+ };
1164
+ const terseResponse = {
1165
+ data: clonedData,
1166
+ __terse__: terseMeta
1167
+ };
1168
+ if ("errors" in response && response.errors) {
1169
+ terseResponse.errors = response.errors;
1170
+ }
1171
+ if ("extensions" in response && response.extensions) {
1172
+ terseResponse.extensions = response.extensions;
1173
+ }
1174
+ if (config.debug) {
1175
+ const originalSize = JSON.stringify(response).length;
1176
+ const compressedSize = JSON.stringify(terseResponse).length;
1177
+ const savings = ((1 - compressedSize / originalSize) * 100).toFixed(1);
1178
+ console.log(
1179
+ `[tersejson/graphql] Compressed ${originalSize} -> ${compressedSize} bytes (${savings}% savings)`
1180
+ );
1181
+ console.log(`[tersejson/graphql] Compressed paths: ${paths.join(", ")}`);
1182
+ }
1183
+ return terseResponse;
1184
+ }
1185
+ function createTerseFormatFn(options = {}) {
1186
+ return function formatResult(result, _context, _info) {
1187
+ return compressGraphQLResponse(result, options);
1188
+ };
1189
+ }
1190
+ function terseGraphQL(graphqlMiddleware, options = {}) {
1191
+ const config = { ...DEFAULT_OPTIONS3, ...options };
1192
+ return function terseGraphQLMiddleware(req, res, next) {
1193
+ const acceptsTerse = req.headers["accept-terse"] === "true" || req.headers["x-accept-terse"] === "true";
1194
+ if (!acceptsTerse) {
1195
+ graphqlMiddleware(req, res, next);
1196
+ return;
1197
+ }
1198
+ const originalJson = res.json.bind(res);
1199
+ res.json = function terseGraphQLJson(data) {
1200
+ if (typeof data === "object" && data !== null && "data" in data) {
1201
+ try {
1202
+ const compressed = compressGraphQLResponse(
1203
+ data,
1204
+ config
1205
+ );
1206
+ if ("__terse__" in compressed) {
1207
+ res.setHeader("x-terse-json", "graphql");
1208
+ }
1209
+ return originalJson(compressed);
1210
+ } catch (error) {
1211
+ if (config.debug) {
1212
+ console.error("[tersejson/graphql] Compression failed:", error);
1213
+ }
1214
+ return originalJson(data);
1215
+ }
1216
+ }
1217
+ return originalJson(data);
1218
+ };
1219
+ graphqlMiddleware(req, res, next);
1220
+ };
1221
+ }
1222
+ var graphql_default = terseGraphQL;
1223
+
1224
+ // src/graphql-client.ts
1225
+ var graphql_client_exports = {};
1226
+ __export(graphql_client_exports, {
1227
+ createGraphQLFetch: () => createGraphQLFetch,
1228
+ createTerseLink: () => createTerseLink,
1229
+ default: () => graphql_client_default,
1230
+ isGraphQLTersePayload: () => isGraphQLTersePayload,
1231
+ processGraphQLResponse: () => processGraphQLResponse
1232
+ });
1233
+ var DEFAULT_OPTIONS4 = {
1234
+ useProxy: true,
1235
+ debug: false
1236
+ };
1237
+ function getAtPath(obj, path) {
1238
+ const parts = path.split(/\.|\[(\d+)\]/).filter(Boolean);
1239
+ let current = obj;
1240
+ for (const part of parts) {
1241
+ if (current === null || current === void 0) return void 0;
1242
+ const isIndex = /^\d+$/.test(part);
1243
+ if (isIndex) {
1244
+ current = current[parseInt(part, 10)];
1245
+ } else {
1246
+ current = current[part];
1247
+ }
1248
+ }
1249
+ return current;
1250
+ }
1251
+ function setAtPath2(obj, path, value) {
1252
+ const parts = path.split(/\.|\[(\d+)\]/).filter(Boolean);
1253
+ let current = obj;
1254
+ for (let i = 0; i < parts.length - 1; i++) {
1255
+ const part = parts[i];
1256
+ const isIndex = /^\d+$/.test(part);
1257
+ if (isIndex) {
1258
+ current = current[parseInt(part, 10)];
1259
+ } else {
1260
+ current = current[part];
1261
+ }
1262
+ }
1263
+ const lastPart = parts[parts.length - 1];
1264
+ const isLastIndex = /^\d+$/.test(lastPart);
1265
+ if (isLastIndex) {
1266
+ current[parseInt(lastPart, 10)] = value;
1267
+ } else {
1268
+ current[lastPart] = value;
1269
+ }
1270
+ }
1271
+ function expandObject2(obj, keyMap, maxDepth = 10, currentDepth = 0) {
1272
+ if (currentDepth >= maxDepth) return obj;
1273
+ const shortToKey = new Map(
1274
+ Object.entries(keyMap).map(([short, original]) => [short, original])
1275
+ );
1276
+ const expanded = {};
1277
+ for (const [shortKey, value] of Object.entries(obj)) {
1278
+ const originalKey = shortToKey.get(shortKey) ?? shortKey;
1279
+ if (Array.isArray(value)) {
1280
+ expanded[originalKey] = value.map((item) => {
1281
+ if (typeof item === "object" && item !== null && !Array.isArray(item)) {
1282
+ return expandObject2(item, keyMap, maxDepth, currentDepth + 1);
1283
+ }
1284
+ return item;
1285
+ });
1286
+ } else if (typeof value === "object" && value !== null) {
1287
+ expanded[originalKey] = expandObject2(
1288
+ value,
1289
+ keyMap,
1290
+ maxDepth,
1291
+ currentDepth + 1
1292
+ );
1293
+ } else {
1294
+ expanded[originalKey] = value;
1295
+ }
1296
+ }
1297
+ return expanded;
1298
+ }
1299
+ function wrapArrayWithProxies(array, keyMap) {
1300
+ return array.map((item) => createTerseProxy(item, keyMap));
1301
+ }
1302
+ function expandArray(array, keyMap) {
1303
+ return array.map((item) => expandObject2(item, keyMap));
1304
+ }
1305
+ function processGraphQLResponse(response, options = {}) {
1306
+ const config = { ...DEFAULT_OPTIONS4, ...options };
1307
+ if (!isGraphQLTersePayload(response)) {
1308
+ return response;
1309
+ }
1310
+ const terseResponse = response;
1311
+ const { data, __terse__, ...rest } = terseResponse;
1312
+ const { k: keyMap, paths } = __terse__;
1313
+ if (config.debug) {
1314
+ console.log("[tersejson/graphql-client] Processing terse response");
1315
+ console.log("[tersejson/graphql-client] Paths:", paths);
1316
+ console.log("[tersejson/graphql-client] Key map:", keyMap);
1317
+ }
1318
+ const clonedData = JSON.parse(JSON.stringify(data));
1319
+ const result = { data: clonedData, ...rest };
1320
+ for (const path of paths) {
1321
+ const array = getAtPath(result, path);
1322
+ if (!array || !Array.isArray(array)) {
1323
+ if (config.debug) {
1324
+ console.warn(`[tersejson/graphql-client] Path not found or not array: ${path}`);
1325
+ }
1326
+ continue;
1327
+ }
1328
+ if (config.useProxy) {
1329
+ const proxied = wrapArrayWithProxies(array, keyMap);
1330
+ setAtPath2(result, path, proxied);
1331
+ } else {
1332
+ const expanded = expandArray(array, keyMap);
1333
+ setAtPath2(result, path, expanded);
1334
+ }
1335
+ }
1336
+ return result;
1337
+ }
1338
+ function createTerseLink(options = {}) {
1339
+ const config = { ...DEFAULT_OPTIONS4, ...options };
1340
+ return {
1341
+ request(operation, forward) {
1342
+ operation.setContext({
1343
+ ...operation.getContext(),
1344
+ headers: {
1345
+ ...operation.getContext().headers || {},
1346
+ "accept-terse": "true"
1347
+ }
1348
+ });
1349
+ return forward(operation).map((result) => {
1350
+ if (isGraphQLTersePayload(result)) {
1351
+ if (config.debug) {
1352
+ console.log("[tersejson/apollo] Processing terse response");
1353
+ }
1354
+ return processGraphQLResponse(result, config);
1355
+ }
1356
+ return result;
1357
+ });
1358
+ }
1359
+ };
1360
+ }
1361
+ function createGraphQLFetch(options = {}) {
1362
+ const config = { ...DEFAULT_OPTIONS4, ...options };
1363
+ return async function graphqlFetch(url, body, init = {}) {
1364
+ const headers = new Headers(init.headers);
1365
+ headers.set("Content-Type", "application/json");
1366
+ headers.set("accept-terse", "true");
1367
+ const response = await fetch(url, {
1368
+ ...init,
1369
+ method: "POST",
1370
+ headers,
1371
+ body: JSON.stringify(body)
1372
+ });
1373
+ const data = await response.json();
1374
+ return processGraphQLResponse(data, config);
1375
+ };
1376
+ }
1377
+ var graphql_client_default = processGraphQLResponse;
1378
+
985
1379
  exports.TerseAnalytics = TerseAnalytics;
986
1380
  exports.analytics = analytics;
987
1381
  exports.client = client_exports;
@@ -991,9 +1385,12 @@ exports.createTerseProxy = createTerseProxy;
991
1385
  exports.expand = expand;
992
1386
  exports.express = express_exports;
993
1387
  exports.getAnalytics = getAnalytics;
1388
+ exports.graphql = graphql_exports;
1389
+ exports.graphqlClient = graphql_client_exports;
994
1390
  exports.initAnalytics = initAnalytics;
995
1391
  exports.integrations = integrations_exports;
996
1392
  exports.isCompressibleArray = isCompressibleArray;
1393
+ exports.isGraphQLTersePayload = isGraphQLTersePayload;
997
1394
  exports.isTersePayload = isTersePayload;
998
1395
  exports.recordEvent = recordEvent;
999
1396
  exports.wrapWithProxy = wrapWithProxy;