@posthog/core 1.28.5 → 1.28.7

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/gzip.d.ts CHANGED
@@ -4,6 +4,7 @@
4
4
  */
5
5
  export declare function isGzipSupported(): boolean;
6
6
  export declare const isNativeAsyncGzipReadError: (error: unknown) => boolean;
7
+ export declare const isNativeAsyncGzipError: (error: unknown) => boolean;
7
8
  export type GzipCompressOptions = {
8
9
  /**
9
10
  * By default this helper swallows compression errors and returns null.
@@ -1 +1 @@
1
- {"version":3,"file":"gzip.d.ts","sourceRoot":"","sources":["../src/gzip.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAOzC;AAED,eAAO,MAAM,0BAA0B,GAAI,OAAO,OAAO,KAAG,OAQ3D,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB,CAAA;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,UAAO,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CA8BrH"}
1
+ {"version":3,"file":"gzip.d.ts","sourceRoot":"","sources":["../src/gzip.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAOzC;AAID,eAAO,MAAM,0BAA0B,GAAI,OAAO,OAAO,KAAG,OAQ3D,CAAA;AAED,eAAO,MAAM,sBAAsB,GAAI,OAAO,OAAO,KAAG,OAQvD,CAAA;AA0DD,MAAM,MAAM,mBAAmB,GAAG;IAChC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB,CAAA;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,UAAO,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAgCrH"}
package/dist/gzip.js CHANGED
@@ -26,21 +26,60 @@ __webpack_require__.r(__webpack_exports__);
26
26
  __webpack_require__.d(__webpack_exports__, {
27
27
  gzipCompress: ()=>gzipCompress,
28
28
  isGzipSupported: ()=>isGzipSupported,
29
+ isNativeAsyncGzipError: ()=>isNativeAsyncGzipError,
29
30
  isNativeAsyncGzipReadError: ()=>isNativeAsyncGzipReadError
30
31
  });
31
32
  function isGzipSupported() {
32
33
  return 'CompressionStream' in globalThis && 'TextEncoder' in globalThis && 'Response' in globalThis && 'function' == typeof Response.prototype.blob;
33
34
  }
35
+ const NATIVE_GZIP_VALIDATION_ERROR = 'NativeGzipValidationError';
34
36
  const isNativeAsyncGzipReadError = (error)=>{
35
37
  if (!error || 'object' != typeof error) return false;
36
38
  const name = 'name' in error ? String(error.name) : '';
37
39
  return 'NotReadableError' === name;
38
40
  };
41
+ const isNativeAsyncGzipError = (error)=>{
42
+ if (!error || 'object' != typeof error) return false;
43
+ const name = 'name' in error ? String(error.name) : '';
44
+ return isNativeAsyncGzipReadError(error) || name === NATIVE_GZIP_VALIDATION_ERROR;
45
+ };
46
+ let crc32Table;
47
+ const getCrc32Table = ()=>{
48
+ if (crc32Table) return crc32Table;
49
+ crc32Table = [];
50
+ for(let i = 0; i < 256; i++){
51
+ let crc = i;
52
+ for(let j = 0; j < 8; j++)crc = 1 & crc ? 0xedb88320 ^ crc >>> 1 : crc >>> 1;
53
+ crc32Table[i] = crc >>> 0;
54
+ }
55
+ return crc32Table;
56
+ };
57
+ const crc32 = (bytes)=>{
58
+ const table = getCrc32Table();
59
+ let crc = 0xffffffff;
60
+ for(let i = 0; i < bytes.length; i++)crc = table[(crc ^ bytes[i]) & 0xff] ^ crc >>> 8;
61
+ return (0xffffffff ^ crc) >>> 0;
62
+ };
63
+ const throwNativeGzipValidationError = (reason)=>{
64
+ const error = new Error(`Native gzip produced invalid output: ${reason}`);
65
+ error.name = NATIVE_GZIP_VALIDATION_ERROR;
66
+ throw error;
67
+ };
68
+ const validateNativeGzip = async (compressed, inputBytes)=>{
69
+ if (compressed.size < 18) throwNativeGzipValidationError('too-short');
70
+ const header = new Uint8Array(await compressed.slice(0, 10).arrayBuffer());
71
+ if (0x1f !== header[0] || 0x8b !== header[1] || 0x08 !== header[2]) throwNativeGzipValidationError('invalid-header');
72
+ const trailer = new DataView(await compressed.slice(compressed.size - 8).arrayBuffer());
73
+ if (trailer.getUint32(0, true) !== crc32(inputBytes)) throwNativeGzipValidationError('invalid-crc');
74
+ const inputSize = inputBytes.length >>> 0;
75
+ if (trailer.getUint32(4, true) !== inputSize) throwNativeGzipValidationError('invalid-size');
76
+ };
39
77
  async function gzipCompress(input, isDebug = true, options) {
40
78
  try {
79
+ const inputBytes = new TextEncoder().encode(input);
41
80
  const compressedStream = new CompressionStream('gzip');
42
81
  const writer = compressedStream.writable.getWriter();
43
- const writePromise = writer.write(new TextEncoder().encode(input)).then(()=>writer.close()).catch(async (err)=>{
82
+ const writePromise = writer.write(inputBytes).then(()=>writer.close()).catch(async (err)=>{
44
83
  try {
45
84
  await writer.abort(err);
46
85
  } catch {}
@@ -51,6 +90,7 @@ async function gzipCompress(input, isDebug = true, options) {
51
90
  responsePromise,
52
91
  writePromise
53
92
  ]);
93
+ await validateNativeGzip(compressed, inputBytes);
54
94
  return compressed;
55
95
  } catch (error) {
56
96
  if (options?.rethrow) throw error;
@@ -60,10 +100,12 @@ async function gzipCompress(input, isDebug = true, options) {
60
100
  }
61
101
  exports.gzipCompress = __webpack_exports__.gzipCompress;
62
102
  exports.isGzipSupported = __webpack_exports__.isGzipSupported;
103
+ exports.isNativeAsyncGzipError = __webpack_exports__.isNativeAsyncGzipError;
63
104
  exports.isNativeAsyncGzipReadError = __webpack_exports__.isNativeAsyncGzipReadError;
64
105
  for(var __webpack_i__ in __webpack_exports__)if (-1 === [
65
106
  "gzipCompress",
66
107
  "isGzipSupported",
108
+ "isNativeAsyncGzipError",
67
109
  "isNativeAsyncGzipReadError"
68
110
  ].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
69
111
  Object.defineProperty(exports, '__esModule', {
package/dist/gzip.mjs CHANGED
@@ -1,16 +1,54 @@
1
1
  function isGzipSupported() {
2
2
  return 'CompressionStream' in globalThis && 'TextEncoder' in globalThis && 'Response' in globalThis && 'function' == typeof Response.prototype.blob;
3
3
  }
4
+ const NATIVE_GZIP_VALIDATION_ERROR = 'NativeGzipValidationError';
4
5
  const isNativeAsyncGzipReadError = (error)=>{
5
6
  if (!error || 'object' != typeof error) return false;
6
7
  const name = 'name' in error ? String(error.name) : '';
7
8
  return 'NotReadableError' === name;
8
9
  };
10
+ const isNativeAsyncGzipError = (error)=>{
11
+ if (!error || 'object' != typeof error) return false;
12
+ const name = 'name' in error ? String(error.name) : '';
13
+ return isNativeAsyncGzipReadError(error) || name === NATIVE_GZIP_VALIDATION_ERROR;
14
+ };
15
+ let crc32Table;
16
+ const getCrc32Table = ()=>{
17
+ if (crc32Table) return crc32Table;
18
+ crc32Table = [];
19
+ for(let i = 0; i < 256; i++){
20
+ let crc = i;
21
+ for(let j = 0; j < 8; j++)crc = 1 & crc ? 0xedb88320 ^ crc >>> 1 : crc >>> 1;
22
+ crc32Table[i] = crc >>> 0;
23
+ }
24
+ return crc32Table;
25
+ };
26
+ const crc32 = (bytes)=>{
27
+ const table = getCrc32Table();
28
+ let crc = 0xffffffff;
29
+ for(let i = 0; i < bytes.length; i++)crc = table[(crc ^ bytes[i]) & 0xff] ^ crc >>> 8;
30
+ return (0xffffffff ^ crc) >>> 0;
31
+ };
32
+ const throwNativeGzipValidationError = (reason)=>{
33
+ const error = new Error(`Native gzip produced invalid output: ${reason}`);
34
+ error.name = NATIVE_GZIP_VALIDATION_ERROR;
35
+ throw error;
36
+ };
37
+ const validateNativeGzip = async (compressed, inputBytes)=>{
38
+ if (compressed.size < 18) throwNativeGzipValidationError('too-short');
39
+ const header = new Uint8Array(await compressed.slice(0, 10).arrayBuffer());
40
+ if (0x1f !== header[0] || 0x8b !== header[1] || 0x08 !== header[2]) throwNativeGzipValidationError('invalid-header');
41
+ const trailer = new DataView(await compressed.slice(compressed.size - 8).arrayBuffer());
42
+ if (trailer.getUint32(0, true) !== crc32(inputBytes)) throwNativeGzipValidationError('invalid-crc');
43
+ const inputSize = inputBytes.length >>> 0;
44
+ if (trailer.getUint32(4, true) !== inputSize) throwNativeGzipValidationError('invalid-size');
45
+ };
9
46
  async function gzipCompress(input, isDebug = true, options) {
10
47
  try {
48
+ const inputBytes = new TextEncoder().encode(input);
11
49
  const compressedStream = new CompressionStream('gzip');
12
50
  const writer = compressedStream.writable.getWriter();
13
- const writePromise = writer.write(new TextEncoder().encode(input)).then(()=>writer.close()).catch(async (err)=>{
51
+ const writePromise = writer.write(inputBytes).then(()=>writer.close()).catch(async (err)=>{
14
52
  try {
15
53
  await writer.abort(err);
16
54
  } catch {}
@@ -21,6 +59,7 @@ async function gzipCompress(input, isDebug = true, options) {
21
59
  responsePromise,
22
60
  writePromise
23
61
  ]);
62
+ await validateNativeGzip(compressed, inputBytes);
24
63
  return compressed;
25
64
  } catch (error) {
26
65
  if (options?.rethrow) throw error;
@@ -28,4 +67,4 @@ async function gzipCompress(input, isDebug = true, options) {
28
67
  return null;
29
68
  }
30
69
  }
31
- export { gzipCompress, isGzipSupported, isNativeAsyncGzipReadError };
70
+ export { gzipCompress, isGzipSupported, isNativeAsyncGzipError, isNativeAsyncGzipReadError };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { getFeatureFlagValue } from './featureFlagUtils';
2
- export { gzipCompress, isNativeAsyncGzipReadError } from './gzip';
2
+ export { gzipCompress, isNativeAsyncGzipError, isNativeAsyncGzipReadError } from './gzip';
3
3
  export * from './utils';
4
4
  export * as ErrorTracking from './error-tracking';
5
5
  export { buildOtlpLogRecord, buildOtlpLogsPayload, getOtlpSeverityNumber, getOtlpSeverityText, toOtlpAnyValue, toOtlpKeyValueList, } from './logs/logs-utils';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,EAAE,YAAY,EAAE,0BAA0B,EAAE,MAAM,QAAQ,CAAA;AACjE,cAAc,SAAS,CAAA;AACvB,OAAO,KAAK,aAAa,MAAM,kBAAkB,CAAA;AACjD,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,qBAAqB,EACrB,mBAAmB,EACnB,cAAc,EACd,kBAAkB,GACnB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AACpC,YAAY,EACV,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,yBAAyB,GAC1B,MAAM,cAAc,CAAA;AAIrB,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AACzG,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AACxC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,0BAA0B,CAAA;AACxC,cAAc,mBAAmB,CAAA;AACjC,cAAc,SAAS,CAAA;AACvB,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,MAAM,QAAQ,CAAA;AACzF,cAAc,SAAS,CAAA;AACvB,OAAO,KAAK,aAAa,MAAM,kBAAkB,CAAA;AACjD,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,qBAAqB,EACrB,mBAAmB,EACnB,cAAc,EACd,kBAAkB,GACnB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AACpC,YAAY,EACV,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,yBAAyB,GAC1B,MAAM,cAAc,CAAA;AAIrB,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AACzG,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AACxC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,0BAA0B,CAAA;AACxC,cAAc,mBAAmB,CAAA;AACjC,cAAc,SAAS,CAAA;AACvB,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA"}
package/dist/index.js CHANGED
@@ -92,6 +92,7 @@ var __webpack_exports__ = {};
92
92
  getRequirementsHint: ()=>_surveys_validation__WEBPACK_IMPORTED_MODULE_11__.getRequirementsHint,
93
93
  getValidationError: ()=>_surveys_validation__WEBPACK_IMPORTED_MODULE_11__.getValidationError,
94
94
  gzipCompress: ()=>_gzip__WEBPACK_IMPORTED_MODULE_1__.gzipCompress,
95
+ isNativeAsyncGzipError: ()=>_gzip__WEBPACK_IMPORTED_MODULE_1__.isNativeAsyncGzipError,
95
96
  isNativeAsyncGzipReadError: ()=>_gzip__WEBPACK_IMPORTED_MODULE_1__.isNativeAsyncGzipReadError,
96
97
  toOtlpAnyValue: ()=>_logs_logs_utils__WEBPACK_IMPORTED_MODULE_4__.toOtlpAnyValue,
97
98
  toOtlpKeyValueList: ()=>_logs_logs_utils__WEBPACK_IMPORTED_MODULE_4__.toOtlpKeyValueList,
@@ -104,6 +105,7 @@ var __webpack_exports__ = {};
104
105
  for(var __WEBPACK_IMPORT_KEY__ in _utils__WEBPACK_IMPORTED_MODULE_2__)if ([
105
106
  "toOtlpKeyValueList",
106
107
  "PostHogLogs",
108
+ "isNativeAsyncGzipError",
107
109
  "getOtlpSeverityText",
108
110
  "isNativeAsyncGzipReadError",
109
111
  "getFeatureFlagValue",
@@ -131,6 +133,7 @@ var __webpack_exports__ = {};
131
133
  for(var __WEBPACK_IMPORT_KEY__ in _posthog_core__WEBPACK_IMPORTED_MODULE_7__)if ([
132
134
  "toOtlpKeyValueList",
133
135
  "PostHogLogs",
136
+ "isNativeAsyncGzipError",
134
137
  "getOtlpSeverityText",
135
138
  "isNativeAsyncGzipReadError",
136
139
  "getFeatureFlagValue",
@@ -154,6 +157,7 @@ var __webpack_exports__ = {};
154
157
  for(var __WEBPACK_IMPORT_KEY__ in _posthog_core_stateless__WEBPACK_IMPORTED_MODULE_8__)if ([
155
158
  "toOtlpKeyValueList",
156
159
  "PostHogLogs",
160
+ "isNativeAsyncGzipError",
157
161
  "getOtlpSeverityText",
158
162
  "isNativeAsyncGzipReadError",
159
163
  "getFeatureFlagValue",
@@ -177,6 +181,7 @@ var __webpack_exports__ = {};
177
181
  for(var __WEBPACK_IMPORT_KEY__ in _tracing_headers__WEBPACK_IMPORTED_MODULE_9__)if ([
178
182
  "toOtlpKeyValueList",
179
183
  "PostHogLogs",
184
+ "isNativeAsyncGzipError",
180
185
  "getOtlpSeverityText",
181
186
  "isNativeAsyncGzipReadError",
182
187
  "getFeatureFlagValue",
@@ -200,6 +205,7 @@ var __webpack_exports__ = {};
200
205
  for(var __WEBPACK_IMPORT_KEY__ in _types__WEBPACK_IMPORTED_MODULE_10__)if ([
201
206
  "toOtlpKeyValueList",
202
207
  "PostHogLogs",
208
+ "isNativeAsyncGzipError",
203
209
  "getOtlpSeverityText",
204
210
  "isNativeAsyncGzipReadError",
205
211
  "getFeatureFlagValue",
@@ -231,6 +237,7 @@ exports.getOtlpSeverityText = __webpack_exports__.getOtlpSeverityText;
231
237
  exports.getRequirementsHint = __webpack_exports__.getRequirementsHint;
232
238
  exports.getValidationError = __webpack_exports__.getValidationError;
233
239
  exports.gzipCompress = __webpack_exports__.gzipCompress;
240
+ exports.isNativeAsyncGzipError = __webpack_exports__.isNativeAsyncGzipError;
234
241
  exports.isNativeAsyncGzipReadError = __webpack_exports__.isNativeAsyncGzipReadError;
235
242
  exports.toOtlpAnyValue = __webpack_exports__.toOtlpAnyValue;
236
243
  exports.toOtlpKeyValueList = __webpack_exports__.toOtlpKeyValueList;
@@ -247,6 +254,7 @@ for(var __webpack_i__ in __webpack_exports__)if (-1 === [
247
254
  "getRequirementsHint",
248
255
  "getValidationError",
249
256
  "gzipCompress",
257
+ "isNativeAsyncGzipError",
250
258
  "isNativeAsyncGzipReadError",
251
259
  "toOtlpAnyValue",
252
260
  "toOtlpKeyValueList",
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { getFeatureFlagValue } from "./featureFlagUtils.mjs";
2
- import { gzipCompress, isNativeAsyncGzipReadError } from "./gzip.mjs";
2
+ import { gzipCompress, isNativeAsyncGzipError, isNativeAsyncGzipReadError } from "./gzip.mjs";
3
3
  import { buildOtlpLogRecord, buildOtlpLogsPayload, getOtlpSeverityNumber, getOtlpSeverityText, toOtlpAnyValue, toOtlpKeyValueList } from "./logs/logs-utils.mjs";
4
4
  import { PostHogLogs } from "./logs/index.mjs";
5
5
  import { uuidv7 } from "./vendor/uuidv7.mjs";
@@ -10,4 +10,4 @@ export * from "./posthog-core-stateless.mjs";
10
10
  export * from "./tracing-headers.mjs";
11
11
  export * from "./types.mjs";
12
12
  import * as __WEBPACK_EXTERNAL_MODULE__error_tracking_index_mjs_b3406d6f__ from "./error-tracking/index.mjs";
13
- export { __WEBPACK_EXTERNAL_MODULE__error_tracking_index_mjs_b3406d6f__ as ErrorTracking, PostHogLogs, buildOtlpLogRecord, buildOtlpLogsPayload, getFeatureFlagValue, getLengthFromRules, getOtlpSeverityNumber, getOtlpSeverityText, getRequirementsHint, getValidationError, gzipCompress, isNativeAsyncGzipReadError, toOtlpAnyValue, toOtlpKeyValueList, uuidv7 };
13
+ export { __WEBPACK_EXTERNAL_MODULE__error_tracking_index_mjs_b3406d6f__ as ErrorTracking, PostHogLogs, buildOtlpLogRecord, buildOtlpLogsPayload, getFeatureFlagValue, getLengthFromRules, getOtlpSeverityNumber, getOtlpSeverityText, getRequirementsHint, getValidationError, gzipCompress, isNativeAsyncGzipError, isNativeAsyncGzipReadError, toOtlpAnyValue, toOtlpKeyValueList, uuidv7 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@posthog/core",
3
- "version": "1.28.5",
3
+ "version": "1.28.7",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -67,7 +67,7 @@
67
67
  }
68
68
  },
69
69
  "dependencies": {
70
- "@posthog/types": "1.373.0"
70
+ "@posthog/types": "1.373.2"
71
71
  },
72
72
  "devDependencies": {
73
73
  "@rslib/core": "0.10.6",
package/src/gzip.ts CHANGED
@@ -11,6 +11,8 @@ export function isGzipSupported(): boolean {
11
11
  )
12
12
  }
13
13
 
14
+ const NATIVE_GZIP_VALIDATION_ERROR = 'NativeGzipValidationError'
15
+
14
16
  export const isNativeAsyncGzipReadError = (error: unknown): boolean => {
15
17
  if (!error || typeof error !== 'object') {
16
18
  return false
@@ -21,6 +23,72 @@ export const isNativeAsyncGzipReadError = (error: unknown): boolean => {
21
23
  return name === 'NotReadableError'
22
24
  }
23
25
 
26
+ export const isNativeAsyncGzipError = (error: unknown): boolean => {
27
+ if (!error || typeof error !== 'object') {
28
+ return false
29
+ }
30
+
31
+ const name = 'name' in error ? String(error.name) : ''
32
+
33
+ return isNativeAsyncGzipReadError(error) || name === NATIVE_GZIP_VALIDATION_ERROR
34
+ }
35
+
36
+ type NativeGzipValidationReason = 'too-short' | 'invalid-header' | 'invalid-crc' | 'invalid-size'
37
+
38
+ let crc32Table: number[] | undefined
39
+
40
+ const getCrc32Table = (): number[] => {
41
+ if (crc32Table) {
42
+ return crc32Table
43
+ }
44
+
45
+ crc32Table = []
46
+ for (let i = 0; i < 256; i++) {
47
+ let crc = i
48
+ for (let j = 0; j < 8; j++) {
49
+ crc = crc & 1 ? 0xedb88320 ^ (crc >>> 1) : crc >>> 1
50
+ }
51
+ crc32Table[i] = crc >>> 0
52
+ }
53
+ return crc32Table
54
+ }
55
+
56
+ const crc32 = (bytes: Uint8Array): number => {
57
+ const table = getCrc32Table()
58
+ let crc = 0xffffffff
59
+ for (let i = 0; i < bytes.length; i++) {
60
+ crc = table[(crc ^ bytes[i]) & 0xff] ^ (crc >>> 8)
61
+ }
62
+ return (crc ^ 0xffffffff) >>> 0
63
+ }
64
+
65
+ const throwNativeGzipValidationError = (reason: NativeGzipValidationReason): never => {
66
+ const error = new Error(`Native gzip produced invalid output: ${reason}`)
67
+ error.name = NATIVE_GZIP_VALIDATION_ERROR
68
+ throw error
69
+ }
70
+
71
+ const validateNativeGzip = async (compressed: Blob, inputBytes: Uint8Array): Promise<void> => {
72
+ if (compressed.size < 18) {
73
+ throwNativeGzipValidationError('too-short')
74
+ }
75
+
76
+ const header = new Uint8Array(await compressed.slice(0, 10).arrayBuffer())
77
+ if (header[0] !== 0x1f || header[1] !== 0x8b || header[2] !== 0x08) {
78
+ throwNativeGzipValidationError('invalid-header')
79
+ }
80
+
81
+ const trailer = new DataView(await compressed.slice(compressed.size - 8).arrayBuffer())
82
+ if (trailer.getUint32(0, true) !== crc32(inputBytes)) {
83
+ throwNativeGzipValidationError('invalid-crc')
84
+ }
85
+
86
+ const inputSize = inputBytes.length >>> 0
87
+ if (trailer.getUint32(4, true) !== inputSize) {
88
+ throwNativeGzipValidationError('invalid-size')
89
+ }
90
+ }
91
+
24
92
  export type GzipCompressOptions = {
25
93
  /**
26
94
  * By default this helper swallows compression errors and returns null.
@@ -36,11 +104,12 @@ export type GzipCompressOptions = {
36
104
  */
37
105
  export async function gzipCompress(input: string, isDebug = true, options?: GzipCompressOptions): Promise<Blob | null> {
38
106
  try {
107
+ const inputBytes = new TextEncoder().encode(input)
39
108
  const compressedStream = new CompressionStream('gzip')
40
109
  const writer = compressedStream.writable.getWriter()
41
110
 
42
111
  const writePromise = writer
43
- .write(new TextEncoder().encode(input))
112
+ .write(inputBytes)
44
113
  .then(() => writer.close())
45
114
  .catch(async (err) => {
46
115
  try {
@@ -53,6 +122,7 @@ export async function gzipCompress(input: string, isDebug = true, options?: Gzip
53
122
  const responsePromise = new Response(compressedStream.readable).blob()
54
123
 
55
124
  const [compressed] = await Promise.all([responsePromise, writePromise])
125
+ await validateNativeGzip(compressed, inputBytes)
56
126
  return compressed
57
127
  } catch (error) {
58
128
  if (options?.rethrow) {
package/src/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { getFeatureFlagValue } from './featureFlagUtils'
2
- export { gzipCompress, isNativeAsyncGzipReadError } from './gzip'
2
+ export { gzipCompress, isNativeAsyncGzipError, isNativeAsyncGzipReadError } from './gzip'
3
3
  export * from './utils'
4
4
  export * as ErrorTracking from './error-tracking'
5
5
  export {