@schedule1-tools/mixer 0.0.0 → 0.0.2

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 CHANGED
@@ -10,36 +10,42 @@ npm install @schedule1-tools/mixer
10
10
 
11
11
  ## Usage
12
12
 
13
+ ### Calculate a mix
14
+
13
15
  ```typescript
14
- import { mixSubstances, encodeMixState, decodeMixState } from '@schedule1-tools/mixer';
16
+ const result = mixSubstances('OG Kush', ['Cuke', 'Flu Medicine', 'Gasoline']);
15
17
 
16
- // Calculate a mix
17
- const result = mixSubstances('OG Kush', ['A', 'B', 'C']);
18
- console.log(result);
19
18
  /*
20
19
  {
21
- effects: ['En', 'Se', 'To'],
20
+ effects: [ 'Be', 'Eu', 'Se', 'To' ],
22
21
  cost: 12,
23
- sellPrice: 42,
24
- profit: -5,
25
- profitMargin: -0.119
22
+ sellPrice: 64,
23
+ profit: 17,
24
+ profitMargin: 0.27
26
25
  }
27
26
  */
27
+ ```
28
+
29
+ ### Encode a mix state for sharing
28
30
 
29
- // Encode a mix state for sharing
31
+ ```typescript
30
32
  const encoded = encodeMixState({
31
33
  product: 'OG Kush',
32
- substances: ['A', 'B', 'C'],
34
+ substances: ['Cuke', 'Flu Medicine', 'Gasoline'],
33
35
  });
34
- console.log(encoded); // "T0cgS3VzaDpBQkM"
35
36
 
36
- // Decode a mix state
37
+ // "T0cgS3VzaDpBQkM"
38
+ ```
39
+
40
+ ### Decode a mix state
41
+
42
+ ```typescript
37
43
  const decoded = decodeMixState('T0cgS3VzaDpBQkM');
38
- console.log(decoded);
44
+
39
45
  /*
40
46
  {
41
47
  product: 'OG Kush',
42
- substances: ['A', 'B', 'C']
48
+ substances: [ 'Cuke', 'Flu Medicine', 'Gasoline' ]
43
49
  }
44
50
  */
45
51
  ```
@@ -52,3 +58,7 @@ The package also exports the following data objects:
52
58
  - `products`: Information about all products
53
59
  - `substances`: Information about all substances
54
60
  - `effectRulesBySubstance`: Rules for how substances transform effects
61
+
62
+ ## Notice
63
+
64
+ This is a fan-made project and is not affiliated with, authorized, maintained, sponsored, or endorsed by the developers of Schedule I the game. All game-related content, including but not limited to names, trademarks, and copyrights, belong to their respective owners.
@@ -1,7 +1,4 @@
1
- import { EffectCode } from '../types';
2
- /**
3
- * A class for efficiently managing a set of effects using a bitset
4
- */
1
+ import type { EffectCode } from '../types';
5
2
  export declare class EffectSet {
6
3
  private static readonly effectToIndex;
7
4
  private static readonly indexToEffect;
@@ -1,9 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.EffectSet = void 0;
4
- /**
5
- * A class for efficiently managing a set of effects using a bitset
6
- */
7
4
  class EffectSet {
8
5
  /**
9
6
  * Initialize the static mapping between effect codes and bit positions
@@ -113,7 +110,6 @@ class EffectSet {
113
110
  * Get the number of effects in the set
114
111
  */
115
112
  size() {
116
- // Count the number of set bits using Brian Kernighan's algorithm
117
113
  let count = 0;
118
114
  let n = this.bits;
119
115
  while (n) {
@@ -1,4 +1,4 @@
1
- import { MixResult, ProductType, SubstanceCode } from '../types';
1
+ import type { MixResult, ProductType, SubstanceCode } from '../types';
2
2
  /**
3
3
  * Calculate the result of mixing substances with a product
4
4
  */
@@ -3,8 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.mixSubstances = mixSubstances;
4
4
  const effects_1 = require("../data/effects");
5
5
  const products_1 = require("../data/products");
6
- const substances_1 = require("../data/substances");
7
6
  const rules_1 = require("../data/rules");
7
+ const substances_1 = require("../data/substances");
8
8
  const effectSet_1 = require("./effectSet");
9
9
  const MAX_EFFECTS = 8;
10
10
  /**
@@ -1,2 +1,2 @@
1
- import { EffectCode, EffectData } from '../types';
1
+ import type { EffectCode, EffectData } from '../types';
2
2
  export declare const effects: Record<EffectCode, EffectData>;
@@ -1,3 +1,3 @@
1
- import { ProductData, ProductType } from '../types';
1
+ import type { ProductData, ProductType } from '../types';
2
2
  export declare const products: Record<ProductType, ProductData>;
3
3
  export declare const productAbbreviations: Record<string, ProductType>;
@@ -1,2 +1,2 @@
1
- import { EffectRule, SubstanceCode } from '../types';
1
+ import type { EffectRule, SubstanceCode } from '../types';
2
2
  export declare const effectRulesBySubstance: Record<SubstanceCode, EffectRule[]>;
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.effectRulesBySubstance = void 0;
4
4
  exports.effectRulesBySubstance = {
5
- A: [
5
+ Cuke: [
6
6
  { ifPresent: ['Eu'], ifNotPresent: ['La'], replace: { Eu: 'La' } },
7
7
  { ifPresent: ['Fo'], ifNotPresent: ['Cy'], replace: { Fo: 'Cy' } },
8
8
  { ifPresent: ['Gi'], ifNotPresent: ['Tp'], replace: { Gi: 'Tp' } },
@@ -11,7 +11,7 @@ exports.effectRulesBySubstance = {
11
11
  { ifPresent: ['Sn'], ifNotPresent: ['Pa'], replace: { Sn: 'Pa' } },
12
12
  { ifPresent: ['To'], ifNotPresent: ['Eu'], replace: { To: 'Eu' } },
13
13
  ],
14
- B: [
14
+ 'Flu Medicine': [
15
15
  { ifPresent: ['At'], ifNotPresent: ['Mu'], replace: { At: 'Mu' } },
16
16
  { ifPresent: ['Ca'], ifNotPresent: ['Be'], replace: { Ca: 'Be' } },
17
17
  { ifPresent: ['Cy'], ifNotPresent: ['Fo'], replace: { Cy: 'Fo' } },
@@ -23,7 +23,7 @@ exports.effectRulesBySubstance = {
23
23
  { ifPresent: ['Sh'], ifNotPresent: ['Pa'], replace: { Sh: 'Pa' } },
24
24
  { ifPresent: ['Tp'], ifNotPresent: ['Gi'], replace: { Tp: 'Gi' } },
25
25
  ],
26
- C: [
26
+ Gasoline: [
27
27
  { ifPresent: ['Di'], ifNotPresent: ['Gl'], replace: { Di: 'Gl' } },
28
28
  { ifPresent: ['El'], ifNotPresent: ['Di'], replace: { El: 'Di' } },
29
29
  { ifPresent: ['En'], ifNotPresent: ['Eu'], replace: { En: 'Eu' } },
@@ -36,7 +36,7 @@ exports.effectRulesBySubstance = {
36
36
  { ifPresent: ['Sh'], ifNotPresent: ['Fc'], replace: { Sh: 'Fc' } },
37
37
  { ifPresent: ['Sn'], ifNotPresent: ['Tt'], replace: { Sn: 'Tt' } },
38
38
  ],
39
- D: [
39
+ Donut: [
40
40
  { ifPresent: ['Ag'], ifNotPresent: ['Sl'], replace: { Ag: 'Sl' } },
41
41
  { ifPresent: ['Ba'], ifNotPresent: ['Sn'], replace: { Ba: 'Sn' } },
42
42
  { ifPresent: ['Cd'], ifNotPresent: ['Ex'], replace: { Cd: 'Ex' } },
@@ -45,7 +45,7 @@ exports.effectRulesBySubstance = {
45
45
  { ifPresent: ['Mu'], ifNotPresent: ['Ca'], replace: { Mu: 'Ca' } },
46
46
  { ifPresent: ['Sh'], ifNotPresent: ['En'], replace: { Sh: 'En' } },
47
47
  ],
48
- E: [
48
+ 'Energy Drink': [
49
49
  { ifPresent: ['Di'], ifNotPresent: ['El'], replace: { Di: 'El' } },
50
50
  { ifPresent: ['Eu'], ifNotPresent: ['En'], replace: { Eu: 'En' } },
51
51
  { ifPresent: ['Fc'], ifNotPresent: ['Sh'], replace: { Fc: 'Sh' } },
@@ -56,20 +56,20 @@ exports.effectRulesBySubstance = {
56
56
  { ifPresent: ['Sp'], ifNotPresent: ['Eu'], replace: { Sp: 'Eu' } },
57
57
  { ifPresent: ['Tt'], ifNotPresent: ['Sn'], replace: { Tt: 'Sn' } },
58
58
  ],
59
- F: [
59
+ 'Mouth Wash': [
60
60
  { ifPresent: ['Ca'], ifNotPresent: ['Ag'], replace: { Ca: 'Ag' } },
61
61
  { ifPresent: ['Cd'], ifNotPresent: ['Sn'], replace: { Cd: 'Sn' } },
62
62
  { ifPresent: ['Ex'], ifNotPresent: ['Se'], replace: { Ex: 'Se' } },
63
63
  { ifPresent: ['Fc'], ifNotPresent: ['Je'], replace: { Fc: 'Je' } },
64
64
  ],
65
- G: [
65
+ 'Motor Oil': [
66
66
  { ifPresent: ['En'], ifNotPresent: ['Mu'], replace: { En: 'Mu' } },
67
67
  { ifPresent: ['Eu'], ifNotPresent: ['Se'], replace: { Eu: 'Se' } },
68
68
  { ifPresent: ['Fo'], ifNotPresent: ['To'], replace: { Fo: 'To' } },
69
69
  { ifPresent: ['Mu'], ifNotPresent: ['Sc'], replace: { Mu: 'Sc' } },
70
70
  { ifPresent: ['Pa'], ifNotPresent: ['Ag'], replace: { Pa: 'Ag' } },
71
71
  ],
72
- H: [
72
+ Banana: [
73
73
  { ifPresent: ['Ca'], ifNotPresent: ['Sn'], replace: { Ca: 'Sn' } },
74
74
  { ifPresent: ['Cy'], ifNotPresent: ['En'], replace: { Cy: 'En' } },
75
75
  { ifPresent: ['Di'], ifNotPresent: ['Fc'], replace: { Di: 'Fc' } },
@@ -80,7 +80,7 @@ exports.effectRulesBySubstance = {
80
80
  { ifPresent: ['Sm'], ifNotPresent: ['Ag'], replace: { Sm: 'Ag' } },
81
81
  { ifPresent: ['To'], ifNotPresent: ['Sm'], replace: { To: 'Sm' } },
82
82
  ],
83
- I: [
83
+ Chili: [
84
84
  { ifPresent: ['Ag'], ifNotPresent: ['Tt'], replace: { Ag: 'Tt' } },
85
85
  { ifPresent: ['At'], ifNotPresent: ['Eu'], replace: { At: 'Eu' } },
86
86
  { ifPresent: ['La'], ifNotPresent: ['Lf'], replace: { La: 'Lf' } },
@@ -89,7 +89,7 @@ exports.effectRulesBySubstance = {
89
89
  { ifPresent: ['Sn'], ifNotPresent: ['Be'], replace: { Sn: 'Be' } },
90
90
  { ifPresent: ['Tp'], ifNotPresent: ['Fc'], replace: { Tp: 'Fc' } },
91
91
  ],
92
- J: [
92
+ Iodine: [
93
93
  { ifPresent: ['Ca'], ifNotPresent: ['Ba'], replace: { Ca: 'Ba' } },
94
94
  { ifPresent: ['Cd'], ifNotPresent: ['Gi'], replace: { Cd: 'Gi' } },
95
95
  { ifPresent: ['Eu'], ifNotPresent: ['Si'], replace: { Eu: 'Si' } },
@@ -97,7 +97,7 @@ exports.effectRulesBySubstance = {
97
97
  { ifPresent: ['Re'], ifNotPresent: ['Tp'], replace: { Re: 'Tp' } },
98
98
  { ifPresent: ['To'], ifNotPresent: ['Sn'], replace: { To: 'Sn' } },
99
99
  ],
100
- K: [
100
+ Paracetamol: [
101
101
  { ifPresent: ['Ca'], ifNotPresent: ['Sl'], replace: { Ca: 'Sl' } },
102
102
  { ifPresent: ['El'], ifNotPresent: ['At'], replace: { El: 'At' } },
103
103
  { ifPresent: ['En'], ifNotPresent: ['Pa'], replace: { En: 'Pa' } },
@@ -109,20 +109,20 @@ exports.effectRulesBySubstance = {
109
109
  { ifPresent: ['Sp'], ifNotPresent: ['Be'], replace: { Sp: 'Be' } },
110
110
  { ifPresent: ['To'], ifNotPresent: ['Tt'], replace: { To: 'Tt' } },
111
111
  ],
112
- L: [
112
+ Viagra: [
113
113
  { ifPresent: ['At'], ifNotPresent: ['Sn'], replace: { At: 'Sn' } },
114
114
  { ifPresent: ['Di'], ifNotPresent: ['To'], replace: { Di: 'To' } },
115
115
  { ifPresent: ['Eu'], ifNotPresent: ['Be'], replace: { Eu: 'Be' } },
116
116
  { ifPresent: ['La'], ifNotPresent: ['Ca'], replace: { La: 'Ca' } },
117
117
  { ifPresent: ['Sh'], ifNotPresent: ['Gi'], replace: { Sh: 'Gi' } },
118
118
  ],
119
- M: [
119
+ 'Horse Semen': [
120
120
  { ifPresent: ['Ag'], ifNotPresent: ['Ca'], replace: { Ag: 'Ca' } },
121
121
  { ifPresent: ['Gi'], ifNotPresent: ['Re'], replace: { Gi: 'Re' } },
122
122
  { ifPresent: ['Si'], ifNotPresent: ['En'], replace: { Si: 'En' } },
123
123
  { ifPresent: ['Tp'], ifNotPresent: ['El'], replace: { Tp: 'El' } },
124
124
  ],
125
- N: [
125
+ 'Mega Bean': [
126
126
  { ifPresent: ['At'], ifNotPresent: ['La'], replace: { At: 'La' } },
127
127
  { ifPresent: ['Ca'], ifNotPresent: ['Gl'], replace: { Ca: 'Gl' } },
128
128
  { ifPresent: ['En'], ifNotPresent: ['Cy'], replace: { En: 'Cy' } },
@@ -134,14 +134,14 @@ exports.effectRulesBySubstance = {
134
134
  { ifPresent: ['Sn'], ifNotPresent: ['Ca'], replace: { Sn: 'Ca' } },
135
135
  { ifPresent: ['Tp'], ifNotPresent: ['En'], replace: { Tp: 'En' } },
136
136
  ],
137
- O: [
137
+ Addy: [
138
138
  { ifPresent: ['Ex'], ifNotPresent: ['Eu'], replace: { Ex: 'Eu' } },
139
139
  { ifPresent: ['Fo'], ifNotPresent: ['En'], replace: { Fo: 'En' } },
140
140
  { ifPresent: ['Gl'], ifNotPresent: ['Re'], replace: { Gl: 'Re' } },
141
141
  { ifPresent: ['Lf'], ifNotPresent: ['El'], replace: { Lf: 'El' } },
142
142
  { ifPresent: ['Se'], ifNotPresent: ['Gi'], replace: { Se: 'Gi' } },
143
143
  ],
144
- P: [
144
+ Battery: [
145
145
  { ifPresent: ['Cy'], ifNotPresent: ['Gl'], replace: { Cy: 'Gl' } },
146
146
  { ifPresent: ['El'], ifNotPresent: ['Eu'], replace: { El: 'Eu' } },
147
147
  { ifPresent: ['Eu'], ifNotPresent: ['Zo'], replace: { Eu: 'Zo' } },
@@ -1,2 +1,3 @@
1
- import { SubstanceCode, SubstanceData } from '../types';
1
+ import type { SubstanceCode, SubstanceData } from '../types';
2
2
  export declare const substances: Record<SubstanceCode, SubstanceData>;
3
+ export declare const substanceAbbreviations: Record<string, SubstanceCode>;
@@ -1,101 +1,105 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.substances = void 0;
3
+ exports.substanceAbbreviations = exports.substances = void 0;
4
4
  exports.substances = {
5
- A: {
6
- name: 'Cuke',
5
+ Cuke: {
6
+ abbreviation: 'A',
7
7
  rank: '1',
8
8
  price: 2,
9
9
  effect: ['En'],
10
10
  },
11
- B: {
12
- name: 'Flu Medicine',
11
+ 'Flu Medicine': {
12
+ abbreviation: 'B',
13
13
  rank: '4',
14
14
  price: 5,
15
15
  effect: ['Se'],
16
16
  },
17
- C: {
18
- name: 'Gasoline',
17
+ Gasoline: {
18
+ abbreviation: 'C',
19
19
  rank: '5',
20
20
  price: 5,
21
21
  effect: ['To'],
22
22
  },
23
- D: {
24
- name: 'Donut',
23
+ Donut: {
24
+ abbreviation: 'D',
25
25
  rank: '1',
26
26
  price: 3,
27
27
  effect: ['Cd'],
28
28
  },
29
- E: {
30
- name: 'Energy Drink',
29
+ 'Energy Drink': {
30
+ abbreviation: 'E',
31
31
  rank: '6',
32
32
  price: 6,
33
33
  effect: ['At'],
34
34
  },
35
- F: {
36
- name: 'Mouth Wash',
35
+ 'Mouth Wash': {
36
+ abbreviation: 'F',
37
37
  rank: '3',
38
38
  price: 4,
39
39
  effect: ['Ba'],
40
40
  },
41
- G: {
42
- name: 'Motor Oil',
41
+ 'Motor Oil': {
42
+ abbreviation: 'G',
43
43
  rank: '7',
44
44
  price: 6,
45
45
  effect: ['Sl'],
46
46
  },
47
- H: {
48
- name: 'Banana',
47
+ Banana: {
48
+ abbreviation: 'H',
49
49
  rank: '1',
50
50
  price: 2,
51
51
  effect: ['Gi'],
52
52
  },
53
- I: {
54
- name: 'Chili',
53
+ Chili: {
54
+ abbreviation: 'I',
55
55
  rank: '9',
56
56
  price: 7,
57
57
  effect: ['Sp'],
58
58
  },
59
- J: {
60
- name: 'Iodine',
59
+ Iodine: {
60
+ abbreviation: 'J',
61
61
  rank: '11',
62
62
  price: 8,
63
63
  effect: ['Je'],
64
64
  },
65
- K: {
66
- name: 'Paracetamol',
65
+ Paracetamol: {
66
+ abbreviation: 'K',
67
67
  rank: '1',
68
68
  price: 3,
69
69
  effect: ['Sn'],
70
70
  },
71
- L: {
72
- name: 'Viagra',
71
+ Viagra: {
72
+ abbreviation: 'L',
73
73
  rank: '2',
74
74
  price: 4,
75
75
  effect: ['Tt'],
76
76
  },
77
- M: {
78
- name: 'Horse Semen',
77
+ 'Horse Semen': {
78
+ abbreviation: 'M',
79
79
  rank: '13',
80
80
  price: 9,
81
81
  effect: ['Lf'],
82
82
  },
83
- N: {
84
- name: 'Mega Bean',
83
+ 'Mega Bean': {
84
+ abbreviation: 'N',
85
85
  rank: '8',
86
86
  price: 7,
87
87
  effect: ['Fo'],
88
88
  },
89
- O: {
90
- name: 'Addy',
89
+ Addy: {
90
+ abbreviation: 'O',
91
91
  rank: '12',
92
92
  price: 9,
93
93
  effect: ['Tp'],
94
94
  },
95
- P: {
96
- name: 'Battery',
95
+ Battery: {
96
+ abbreviation: 'P',
97
97
  rank: '10',
98
98
  price: 8,
99
99
  effect: ['Be'],
100
100
  },
101
101
  };
102
+ exports.substanceAbbreviations = Object.entries(exports.substances).reduce((acc, [substance, data]) => {
103
+ acc[data.abbreviation] = substance;
104
+ return acc;
105
+ }, {});
package/dist/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export type EffectCode = 'Ag' | 'At' | 'Ba' | 'Be' | 'Ca' | 'Cd' | 'Cy' | 'Di' | 'El' | 'En' | 'Eu' | 'Ex' | 'Fc' | 'Fo' | 'Gi' | 'Gl' | 'Je' | 'La' | 'Lf' | 'Mu' | 'Pa' | 'Re' | 'Sc' | 'Se' | 'Sh' | 'Si' | 'Sl' | 'Sm' | 'Sn' | 'Sp' | 'To' | 'Tp' | 'Tt' | 'Zo';
2
- export type SubstanceCode = 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P';
2
+ export type SubstanceCode = 'Cuke' | 'Flu Medicine' | 'Gasoline' | 'Donut' | 'Energy Drink' | 'Mouth Wash' | 'Motor Oil' | 'Banana' | 'Chili' | 'Iodine' | 'Paracetamol' | 'Viagra' | 'Horse Semen' | 'Mega Bean' | 'Addy' | 'Battery';
3
3
  export type ProductType = 'OG Kush' | 'Sour Diesel' | 'Green Crack' | 'Grandaddy Purple' | 'Meth' | 'Cocaine';
4
4
  export type RankCode = '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '10' | '11' | '12' | '13';
5
5
  export interface EffectData {
@@ -8,7 +8,7 @@ export interface EffectData {
8
8
  color: string;
9
9
  }
10
10
  export interface SubstanceData {
11
- name: string;
11
+ abbreviation: string;
12
12
  rank: RankCode;
13
13
  price: number;
14
14
  effect: EffectCode[];
@@ -1,9 +1,11 @@
1
- import { MixState } from '../types';
1
+ import type { MixState } from '../types';
2
2
  /**
3
3
  * Encode a mix state into a URL-safe string
4
+ * @throws Error if the product type or any substance code is invalid
4
5
  */
5
6
  export declare function encodeMixState(state: MixState): string;
6
7
  /**
7
8
  * Decode a mix state from a URL-safe string
9
+ * @returns The decoded MixState or null if invalid
8
10
  */
9
11
  export declare function decodeMixState(hash: string): MixState | null;
@@ -6,35 +6,54 @@ const products_1 = require("../data/products");
6
6
  const substances_1 = require("../data/substances");
7
7
  /**
8
8
  * Encode a mix state into a URL-safe string
9
+ * @throws Error if the product type or any substance code is invalid
9
10
  */
10
11
  function encodeMixState(state) {
11
- // Validate inputs
12
12
  if (!products_1.products[state.product]) {
13
- throw new Error('Invalid product type');
13
+ throw new Error(`Invalid product type: ${state.product}`);
14
14
  }
15
15
  for (const substance of state.substances) {
16
16
  if (!substances_1.substances[substance]) {
17
17
  throw new Error(`Invalid substance code: ${substance}`);
18
18
  }
19
19
  }
20
- const encoded = `${state.product}:${state.substances.join('')}`;
20
+ const abbreviatedSubstances = state.substances.map((s) => {
21
+ const abbr = substances_1.substances[s].abbreviation;
22
+ if (!abbr) {
23
+ throw new Error(`Missing abbreviation for substance: ${s}`);
24
+ }
25
+ return abbr;
26
+ });
27
+ const encoded = `${state.product}:${abbreviatedSubstances.join('')}`;
21
28
  return toBase64Url(encoded);
22
29
  }
23
30
  /**
24
31
  * Decode a mix state from a URL-safe string
32
+ * @returns The decoded MixState or null if invalid
25
33
  */
26
34
  function decodeMixState(hash) {
35
+ if (!hash || typeof hash !== 'string') {
36
+ return null;
37
+ }
27
38
  try {
28
39
  const decoded = fromBase64Url(hash);
29
- const [product, substancesStr] = decoded.split(':');
30
- if (!product || substancesStr === undefined || !products_1.products[product]) {
40
+ const parts = decoded.split(':');
41
+ if (parts.length !== 2) {
31
42
  return null;
32
43
  }
33
- const substanceCodes = substancesStr.split('');
34
- // Validate substances
35
- for (const code of substanceCodes) {
36
- if (!substances_1.substances[code]) {
37
- return null;
44
+ const [product, substancesStr] = parts;
45
+ if (!product || !products_1.products[product]) {
46
+ return null;
47
+ }
48
+ const substanceCodes = [];
49
+ if (substancesStr) {
50
+ for (let i = 0; i < substancesStr.length; i++) {
51
+ const abbr = substancesStr[i];
52
+ const fullName = substances_1.substanceAbbreviations[abbr];
53
+ if (!fullName || !substances_1.substances[fullName]) {
54
+ return null;
55
+ }
56
+ substanceCodes.push(fullName);
38
57
  }
39
58
  }
40
59
  return {
@@ -42,7 +61,7 @@ function decodeMixState(hash) {
42
61
  substances: substanceCodes,
43
62
  };
44
63
  }
45
- catch (_a) {
64
+ catch (error) {
46
65
  return null;
47
66
  }
48
67
  }
@@ -50,7 +69,7 @@ function decodeMixState(hash) {
50
69
  * Convert a string to a URL-safe base64 string
51
70
  */
52
71
  function toBase64Url(str) {
53
- return Buffer.from(str, 'binary')
72
+ return Buffer.from(str, 'utf-8')
54
73
  .toString('base64')
55
74
  .replace(/\+/g, '-')
56
75
  .replace(/\//g, '_')
@@ -60,8 +79,11 @@ function toBase64Url(str) {
60
79
  * Convert a URL-safe base64 string back to a regular string
61
80
  */
62
81
  function fromBase64Url(str) {
82
+ if (typeof str !== 'string') {
83
+ throw new Error('Input must be a string');
84
+ }
63
85
  const base64 = str.replace(/-/g, '+').replace(/_/g, '/');
64
- const padding = 4 - (base64.length % 4);
65
- const padded = padding < 4 ? base64 + '='.repeat(padding) : base64;
66
- return Buffer.from(padded, 'base64').toString('binary');
86
+ const padding = base64.length % 4;
87
+ const padded = padding ? base64 + '='.repeat(4 - padding) : base64;
88
+ return Buffer.from(padded, 'base64').toString('utf-8');
67
89
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@schedule1-tools/mixer",
3
- "version": "0.0.0",
3
+ "version": "0.0.2",
4
4
  "description": "A package for calculating substance mixes in Schedule 1",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -13,10 +13,11 @@
13
13
  "author": "Schedule1 Tools",
14
14
  "license": "MIT",
15
15
  "devDependencies": {
16
+ "@ianvs/prettier-plugin-sort-imports": "^4.4.1",
16
17
  "@types/jest": "^29.5.14",
17
18
  "@types/node": "^22.14.1",
18
- "prettier": "^3.5.3",
19
19
  "jest": "^29.7.0",
20
+ "prettier": "^3.5.3",
20
21
  "ts-jest": "^29.3.2",
21
22
  "typescript": "^5.8.3"
22
23
  },