@vercel/stega 0.0.3 → 0.0.5

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/README.md ADDED
@@ -0,0 +1,52 @@
1
+ # @vercel/stega
2
+
3
+ A simple [steganography](https://en.wikipedia.org/wiki/Steganography) library for adding hidden JSON data to strings.
4
+
5
+ ## Usage
6
+
7
+ This package exports a few methods for encoding and decoding data. All of these methods have TypeScript type definitions and JSDoc comments explaining their parameters and return values.
8
+
9
+ ### `vercelStegaCombine(string, json, skip = 'auto')`
10
+
11
+ This method combines a `string` with some JSON data and returns the result. The `json` can be any value that can be JSON stringified.
12
+
13
+ When the `skip` property is `true`, the original string will be returned without combining the `json`. It supports `boolean` values and `'auto'`. The default is `'auto'`, which will only skip encoding when the `string` is an ISO date string or a URL.
14
+
15
+ ```js
16
+ vercelStegaCombine('Hello world', { foo: 'bar' });
17
+ // -> 'Hello world' (the JSON data is hidden in the new string)
18
+ ```
19
+
20
+ ### `vercelStegaEncode(json)`
21
+
22
+ This method encodes JSON data as a hidden string and returns the result. The `json` can be any value that can be JSON stringified.
23
+
24
+ ```js
25
+ vercelStegaEncode({ foo: 'bar' });
26
+ // -> '' (the JSON data is hidden)
27
+ ```
28
+
29
+ ### `vercelStegaSplit(string)`
30
+
31
+ This method splits out the original string (cleaned) and the encoded portion of the string.
32
+
33
+ ```js
34
+ // In 'Hello world' (the extra data is hidden)
35
+ vercelStegaSplit('Hello world');
36
+ /*
37
+ * -> {
38
+ * cleaned: 'Hello world', // This doesn't contains the encoded data
39
+ * encoded: '', // This is not an empty string, it contains the encoded data
40
+ * }
41
+ */
42
+ ```
43
+
44
+ ### `vercelStegaDecode(string)`
45
+
46
+ This method attempts to extract encoded JSON data from a `string`.
47
+
48
+ ```js
49
+ // In 'Hello world' (the extra data is hidden)
50
+ vercelStegaDecode('Hello world');
51
+ // -> { foo: 'bar' }
52
+ ```
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.vercelStegaDecode = exports.VERCEL_STEGA_REGEX = void 0;
3
+ exports.vercelStegaDecodeAll = exports.vercelStegaDecode = exports.VERCEL_STEGA_REGEX = void 0;
4
4
  const map_1 = require("./map");
5
5
  const REVERSE_HEX_DIGIT_MAP = Object.fromEntries(Object.entries(map_1.HEX_DIGIT_MAP).map((x) => x.reverse()));
6
6
  const HEX_CHARS = `${Object.values(map_1.HEX_DIGIT_MAP)
@@ -8,18 +8,31 @@ const HEX_CHARS = `${Object.values(map_1.HEX_DIGIT_MAP)
8
8
  .join('')}`;
9
9
  exports.VERCEL_STEGA_REGEX = new RegExp(`(?:[${HEX_CHARS}]{2})+`, 'gu');
10
10
  /**
11
- * Decodes a hidden string back into its original value
12
- * @param encoded - The encoded (hidden) string
11
+ * Decodes the first hidden string that's found in the source string back into its original value
12
+ * @param source - The source string with encoded data
13
13
  * @returns The decoded JSON value
14
14
  */
15
- function vercelStegaDecode(encoded) {
16
- const match = encoded.match(exports.VERCEL_STEGA_REGEX);
17
- if (!match)
15
+ function vercelStegaDecode(source) {
16
+ const matches = source.match(exports.VERCEL_STEGA_REGEX);
17
+ if (!matches)
18
18
  return;
19
- return decode(match[0]);
19
+ return decode(matches[0], true)[0];
20
20
  }
21
21
  exports.vercelStegaDecode = vercelStegaDecode;
22
- function decode(encodedString) {
22
+ /**
23
+ * Decodes every hidden string that's found in the source string back into their original values
24
+ * @param source - The source string with encoded data
25
+ * @returns The decoded JSON values
26
+ */
27
+ function vercelStegaDecodeAll(source) {
28
+ const matches = source.match(exports.VERCEL_STEGA_REGEX);
29
+ if (!matches)
30
+ return;
31
+ return matches.map((match) => decode(match)).flat();
32
+ }
33
+ exports.vercelStegaDecodeAll = vercelStegaDecodeAll;
34
+ function decode(encodedString, firstOnly = false) {
35
+ var _a;
23
36
  const encoded = Array.from(encodedString);
24
37
  if (encoded.length % 2) {
25
38
  throw new Error(`Invalid encoded text length. Expected length to be even, received: ${encoded.length}`);
@@ -29,5 +42,24 @@ function decode(encodedString) {
29
42
  const hex = `${REVERSE_HEX_DIGIT_MAP[encoded[i * 2].codePointAt(0)]}${REVERSE_HEX_DIGIT_MAP[encoded[i * 2 + 1].codePointAt(0)]}`;
30
43
  chars.unshift(String.fromCharCode(parseInt(hex, 16)));
31
44
  }
32
- return JSON.parse(chars.join(''));
45
+ const results = [];
46
+ const queue = [chars.join('')];
47
+ let breakLimit = 10;
48
+ while (queue.length) {
49
+ const string = queue.shift();
50
+ try {
51
+ results.push(JSON.parse(string));
52
+ if (firstOnly)
53
+ return results;
54
+ }
55
+ catch (error) {
56
+ if (!breakLimit--)
57
+ throw error;
58
+ const position = +((_a = error.message.match(/\sposition\s(\d+)$/)) === null || _a === void 0 ? void 0 : _a[1]);
59
+ if (!position)
60
+ throw error;
61
+ queue.unshift(string.substring(0, position), string.substring(position));
62
+ }
63
+ }
64
+ return results;
33
65
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.vercelStegaEncode = void 0;
3
+ exports.vercelStegaCombine = exports.vercelStegaEncode = void 0;
4
4
  const map_1 = require("./map");
5
5
  /**
6
6
  * Encodes JSON as a hidden string
@@ -22,3 +22,29 @@ function vercelStegaEncode(json) {
22
22
  .join('');
23
23
  }
24
24
  exports.vercelStegaEncode = vercelStegaEncode;
25
+ function isDate(string) {
26
+ return Boolean(Date.parse(string));
27
+ }
28
+ function isUrl(string) {
29
+ try {
30
+ new URL(string);
31
+ }
32
+ catch {
33
+ return false;
34
+ }
35
+ return true;
36
+ }
37
+ /**
38
+ * Adds an encoded JSON object to a string as hidden characters
39
+ * @param string - The string the JSON will be added to
40
+ * @param json - The JSON to add to the string
41
+ * @param skip - Whether to skip encoding (default: "auto")
42
+ */
43
+ function vercelStegaCombine(string, json, skip = 'auto') {
44
+ if (skip === true)
45
+ return string;
46
+ if (skip === 'auto' && (isDate(string) || isUrl(string)))
47
+ return string;
48
+ return `${string}${vercelStegaEncode(json)}`;
49
+ }
50
+ exports.vercelStegaCombine = vercelStegaCombine;
package/dist/decode.d.ts CHANGED
@@ -1,7 +1,13 @@
1
1
  export declare const VERCEL_STEGA_REGEX: RegExp;
2
2
  /**
3
- * Decodes a hidden string back into its original value
4
- * @param encoded - The encoded (hidden) string
3
+ * Decodes the first hidden string that's found in the source string back into its original value
4
+ * @param source - The source string with encoded data
5
5
  * @returns The decoded JSON value
6
6
  */
7
- export declare function vercelStegaDecode<T>(encoded: string): T | undefined;
7
+ export declare function vercelStegaDecode<T>(source: string): T | undefined;
8
+ /**
9
+ * Decodes every hidden string that's found in the source string back into their original values
10
+ * @param source - The source string with encoded data
11
+ * @returns The decoded JSON values
12
+ */
13
+ export declare function vercelStegaDecodeAll<T>(source: string): T[];
package/dist/encode.d.ts CHANGED
@@ -4,3 +4,12 @@
4
4
  * @returns The hidden string
5
5
  */
6
6
  export declare function vercelStegaEncode<T>(json: T): string;
7
+ type SkipValue = 'auto' | boolean;
8
+ /**
9
+ * Adds an encoded JSON object to a string as hidden characters
10
+ * @param string - The string the JSON will be added to
11
+ * @param json - The JSON to add to the string
12
+ * @param skip - Whether to skip encoding (default: "auto")
13
+ */
14
+ export declare function vercelStegaCombine<T>(string: string, json: T, skip?: SkipValue): string;
15
+ export {};
@@ -5,17 +5,29 @@ const HEX_CHARS = `${Object.values(HEX_DIGIT_MAP)
5
5
  .join('')}`;
6
6
  export const VERCEL_STEGA_REGEX = new RegExp(`(?:[${HEX_CHARS}]{2})+`, 'gu');
7
7
  /**
8
- * Decodes a hidden string back into its original value
9
- * @param encoded - The encoded (hidden) string
8
+ * Decodes the first hidden string that's found in the source string back into its original value
9
+ * @param source - The source string with encoded data
10
10
  * @returns The decoded JSON value
11
11
  */
12
- export function vercelStegaDecode(encoded) {
13
- const match = encoded.match(VERCEL_STEGA_REGEX);
14
- if (!match)
12
+ export function vercelStegaDecode(source) {
13
+ const matches = source.match(VERCEL_STEGA_REGEX);
14
+ if (!matches)
15
15
  return;
16
- return decode(match[0]);
16
+ return decode(matches[0], true)[0];
17
17
  }
18
- function decode(encodedString) {
18
+ /**
19
+ * Decodes every hidden string that's found in the source string back into their original values
20
+ * @param source - The source string with encoded data
21
+ * @returns The decoded JSON values
22
+ */
23
+ export function vercelStegaDecodeAll(source) {
24
+ const matches = source.match(VERCEL_STEGA_REGEX);
25
+ if (!matches)
26
+ return;
27
+ return matches.map((match) => decode(match)).flat();
28
+ }
29
+ function decode(encodedString, firstOnly = false) {
30
+ var _a;
19
31
  const encoded = Array.from(encodedString);
20
32
  if (encoded.length % 2) {
21
33
  throw new Error(`Invalid encoded text length. Expected length to be even, received: ${encoded.length}`);
@@ -25,5 +37,24 @@ function decode(encodedString) {
25
37
  const hex = `${REVERSE_HEX_DIGIT_MAP[encoded[i * 2].codePointAt(0)]}${REVERSE_HEX_DIGIT_MAP[encoded[i * 2 + 1].codePointAt(0)]}`;
26
38
  chars.unshift(String.fromCharCode(parseInt(hex, 16)));
27
39
  }
28
- return JSON.parse(chars.join(''));
40
+ const results = [];
41
+ const queue = [chars.join('')];
42
+ let breakLimit = 10;
43
+ while (queue.length) {
44
+ const string = queue.shift();
45
+ try {
46
+ results.push(JSON.parse(string));
47
+ if (firstOnly)
48
+ return results;
49
+ }
50
+ catch (error) {
51
+ if (!breakLimit--)
52
+ throw error;
53
+ const position = +((_a = error.message.match(/\sposition\s(\d+)$/)) === null || _a === void 0 ? void 0 : _a[1]);
54
+ if (!position)
55
+ throw error;
56
+ queue.unshift(string.substring(0, position), string.substring(position));
57
+ }
58
+ }
59
+ return results;
29
60
  }
@@ -18,3 +18,28 @@ export function vercelStegaEncode(json) {
18
18
  })
19
19
  .join('');
20
20
  }
21
+ function isDate(string) {
22
+ return Boolean(Date.parse(string));
23
+ }
24
+ function isUrl(string) {
25
+ try {
26
+ new URL(string);
27
+ }
28
+ catch {
29
+ return false;
30
+ }
31
+ return true;
32
+ }
33
+ /**
34
+ * Adds an encoded JSON object to a string as hidden characters
35
+ * @param string - The string the JSON will be added to
36
+ * @param json - The JSON to add to the string
37
+ * @param skip - Whether to skip encoding (default: "auto")
38
+ */
39
+ export function vercelStegaCombine(string, json, skip = 'auto') {
40
+ if (skip === true)
41
+ return string;
42
+ if (skip === 'auto' && (isDate(string) || isUrl(string)))
43
+ return string;
44
+ return `${string}${vercelStegaEncode(json)}`;
45
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/stega",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "Utilities for steganography",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "module": "./dist/esm/index.js",