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