lubba-encoder 1.0.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.
- package/README.md +5 -0
- package/dist/lubba-encoder.cjs +279 -0
- package/dist/lubba-encoder.cjs.map +1 -0
- package/package.json +28 -0
- package/rollup.config.js +12 -0
- package/src/index.js +246 -0
- package/src/index.test.js +220 -0
package/README.md
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var encoding = require('lib0/dist/encoding.cjs');
|
|
6
|
+
var decoding = require('lib0/dist/decoding.cjs');
|
|
7
|
+
var yaml = require('yaml');
|
|
8
|
+
|
|
9
|
+
function _interopNamespace(e) {
|
|
10
|
+
if (e && e.__esModule) return e;
|
|
11
|
+
var n = Object.create(null);
|
|
12
|
+
if (e) {
|
|
13
|
+
Object.keys(e).forEach(function (k) {
|
|
14
|
+
if (k !== 'default') {
|
|
15
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
16
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
17
|
+
enumerable: true,
|
|
18
|
+
get: function () { return e[k]; }
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
n["default"] = e;
|
|
24
|
+
return Object.freeze(n);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
var encoding__namespace = /*#__PURE__*/_interopNamespace(encoding);
|
|
28
|
+
var decoding__namespace = /*#__PURE__*/_interopNamespace(decoding);
|
|
29
|
+
|
|
30
|
+
/* global btoa atob */
|
|
31
|
+
|
|
32
|
+
const reverseObject = (object) => (
|
|
33
|
+
Object.keys(object).reduce((res, key) => { res[object[key]] = key; return res }, {})
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const partTitleMap = {
|
|
37
|
+
Warmup: 1,
|
|
38
|
+
Activity: 2,
|
|
39
|
+
Fast: 3,
|
|
40
|
+
Recover: 4,
|
|
41
|
+
Rest: 5,
|
|
42
|
+
Cooldown: 6,
|
|
43
|
+
Ski: 7,
|
|
44
|
+
Run: 8,
|
|
45
|
+
Skate: 9,
|
|
46
|
+
Walk: 10
|
|
47
|
+
};
|
|
48
|
+
const layoutMap = {
|
|
49
|
+
1: 1,
|
|
50
|
+
'2+1': 2,
|
|
51
|
+
'2+2': 3,
|
|
52
|
+
'2+3': 4
|
|
53
|
+
};
|
|
54
|
+
const valueGoalTypeMap = {
|
|
55
|
+
duration: 1,
|
|
56
|
+
distance: 2,
|
|
57
|
+
distanceTest: 3,
|
|
58
|
+
paceCurrent: 4,
|
|
59
|
+
paceTest: 5,
|
|
60
|
+
paceTest2: 6,
|
|
61
|
+
paceAverageDuration: 7,
|
|
62
|
+
bpm: 8,
|
|
63
|
+
bpmAverageDuration: 9,
|
|
64
|
+
intervalLeft: 10,
|
|
65
|
+
durationInterval: 11,
|
|
66
|
+
distanceInterval: 12,
|
|
67
|
+
desiredAccuracy: 13,
|
|
68
|
+
pace: 14, // used for target type
|
|
69
|
+
paceSplits: 15
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const partTitleMapReverse = reverseObject(partTitleMap);
|
|
73
|
+
const layoutMapReverse = reverseObject(layoutMap);
|
|
74
|
+
const valueGoalTypeMapReverse = reverseObject(valueGoalTypeMap);
|
|
75
|
+
|
|
76
|
+
function getBit (number, bitPosition) {
|
|
77
|
+
return (number & (1 << bitPosition)) === 0 ? 0 : 1
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function updateBit (number, bitPosition, bitValue) {
|
|
81
|
+
const bitValueNormalized = bitValue ? 1 : 0;
|
|
82
|
+
const clearMask = ~(1 << bitPosition);
|
|
83
|
+
return (number & clearMask) | (bitValueNormalized << bitPosition)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function encodePart (part, encoder) {
|
|
87
|
+
// Set flags for nil values
|
|
88
|
+
let flagsA = 0;
|
|
89
|
+
let flagsB = 0;
|
|
90
|
+
|
|
91
|
+
flagsA = updateBit(flagsA, 0, part.title != null);
|
|
92
|
+
flagsA = updateBit(flagsA, 1, part.layout != null);
|
|
93
|
+
flagsA = updateBit(flagsA, 2, part.layoutValues != null && part.layoutValues.length > 0);
|
|
94
|
+
flagsA = updateBit(flagsA, 3, part.goalType != null);
|
|
95
|
+
flagsA = updateBit(flagsA, 4, part.goalValue != null);
|
|
96
|
+
flagsA = updateBit(flagsA, 5, part.goalIsVariable === true);
|
|
97
|
+
flagsA = updateBit(flagsA, 6, part.targetType != null);
|
|
98
|
+
flagsA = updateBit(flagsA, 7, part.targetMin != null);
|
|
99
|
+
|
|
100
|
+
flagsB = updateBit(flagsB, 0, part.targetMax != null);
|
|
101
|
+
flagsB = updateBit(flagsB, 1, part.repeats != null && part.repeats > 0);
|
|
102
|
+
flagsB = updateBit(flagsB, 2, part.repeatParts != null && part.repeatParts.length > 0);
|
|
103
|
+
|
|
104
|
+
encoding__namespace.writeUint8(encoder, flagsA);
|
|
105
|
+
encoding__namespace.writeUint8(encoder, flagsB);
|
|
106
|
+
|
|
107
|
+
// Use part title map, or custom title
|
|
108
|
+
if (part.title != null) {
|
|
109
|
+
const titleIndex = partTitleMap[part.title];
|
|
110
|
+
if (titleIndex) {
|
|
111
|
+
encoding__namespace.writeUint8(encoder, titleIndex);
|
|
112
|
+
} else {
|
|
113
|
+
encoding__namespace.writeUint8(encoder, 0); // 0 indicates custom string
|
|
114
|
+
encoding__namespace.writeVarString(encoder, part.title);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (part.layout != null) encoding__namespace.writeUint8(encoder, layoutMap[part.layout]);
|
|
119
|
+
if (part.layoutValues != null && part.layoutValues.length > 0) {
|
|
120
|
+
encoding__namespace.writeUint8(encoder, part.layoutValues.length);
|
|
121
|
+
|
|
122
|
+
for (const value of part.layoutValues) {
|
|
123
|
+
encoding__namespace.writeUint8(encoder, valueGoalTypeMap[value]);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (part.goalType != null) encoding__namespace.writeUint8(encoder, valueGoalTypeMap[part.goalType]);
|
|
128
|
+
if (part.goalValue != null) encoding__namespace.writeFloat64(encoder, part.goalValue);
|
|
129
|
+
|
|
130
|
+
if (part.targetType != null) encoding__namespace.writeUint8(encoder, valueGoalTypeMap[part.targetType]);
|
|
131
|
+
if (part.targetMin != null) encoding__namespace.writeFloat64(encoder, part.targetMin);
|
|
132
|
+
if (part.targetMax != null) encoding__namespace.writeFloat64(encoder, part.targetMax);
|
|
133
|
+
|
|
134
|
+
if (part.repeats != null && part.repeats > 0) encoding__namespace.writeUint8(encoder, part.repeats);
|
|
135
|
+
|
|
136
|
+
if (part.repeatParts != null && part.repeatParts.length > 0) {
|
|
137
|
+
encoding__namespace.writeUint8(encoder, part.repeatParts.length);
|
|
138
|
+
|
|
139
|
+
for (const subPart of part.repeatParts) {
|
|
140
|
+
encodePart(subPart, encoder);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function decodePart (decoder) {
|
|
146
|
+
const part = {};
|
|
147
|
+
|
|
148
|
+
// Some flags to indicate what values is non-nil
|
|
149
|
+
const flagsA = decoding__namespace.readUint8(decoder);
|
|
150
|
+
const flagsB = decoding__namespace.readUint8(decoder);
|
|
151
|
+
|
|
152
|
+
const hasTitle = getBit(flagsA, 0);
|
|
153
|
+
const hasLayout = getBit(flagsA, 1);
|
|
154
|
+
const hasLayoutValues = getBit(flagsA, 2);
|
|
155
|
+
const hasGoalType = getBit(flagsA, 3);
|
|
156
|
+
const hasGoalValue = getBit(flagsA, 4);
|
|
157
|
+
const hasGoalIsVariable = getBit(flagsA, 5);
|
|
158
|
+
const hasTargetType = getBit(flagsA, 6);
|
|
159
|
+
const hasTargetMin = getBit(flagsA, 7);
|
|
160
|
+
|
|
161
|
+
const hasTargetMax = getBit(flagsB, 0);
|
|
162
|
+
const hasRepeats = getBit(flagsB, 1);
|
|
163
|
+
const hasRepeatParts = getBit(flagsB, 2);
|
|
164
|
+
|
|
165
|
+
// Use part title map, or custom title
|
|
166
|
+
if (hasTitle) {
|
|
167
|
+
const titleIndex = decoding__namespace.readUint8(decoder);
|
|
168
|
+
const titleIsCustomString = titleIndex === 0;
|
|
169
|
+
part.title = titleIsCustomString ? decoding__namespace.readVarString(decoder) : partTitleMapReverse[titleIndex];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (hasLayout) part.layout = layoutMapReverse[decoding__namespace.readUint8(decoder)];
|
|
173
|
+
if (hasLayoutValues) {
|
|
174
|
+
const amountValues = decoding__namespace.readUint8(decoder);
|
|
175
|
+
part.layoutValues = [];
|
|
176
|
+
|
|
177
|
+
for (let j = 0; j < amountValues; j++) {
|
|
178
|
+
part.layoutValues.push(valueGoalTypeMapReverse[decoding__namespace.readUint8(decoder)]);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (hasGoalType) part.goalType = valueGoalTypeMapReverse[decoding__namespace.readUint8(decoder)];
|
|
183
|
+
if (hasGoalValue) part.goalValue = decoding__namespace.readFloat64(decoder);
|
|
184
|
+
if (hasGoalIsVariable) part.goalIsVariable = true;
|
|
185
|
+
|
|
186
|
+
if (hasTargetType) part.targetType = valueGoalTypeMapReverse[decoding__namespace.readUint8(decoder)];
|
|
187
|
+
if (hasTargetMin) part.targetMin = decoding__namespace.readFloat64(decoder);
|
|
188
|
+
if (hasTargetMax) part.targetMax = decoding__namespace.readFloat64(decoder);
|
|
189
|
+
|
|
190
|
+
if (hasRepeats) part.repeats = decoding__namespace.readUint8(decoder);
|
|
191
|
+
if (hasRepeatParts) {
|
|
192
|
+
const amountParts = decoding__namespace.readUint8(decoder);
|
|
193
|
+
part.repeatParts = [];
|
|
194
|
+
|
|
195
|
+
for (let j = 0; j < amountParts; j++) {
|
|
196
|
+
part.repeatParts.push(decodePart(decoder));
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return part
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function encodeYamlToLubbaData (yaml$1, encodingVersion) {
|
|
204
|
+
const object = yaml.parse(yaml$1);
|
|
205
|
+
|
|
206
|
+
return encodeJsonObjectToLubbaData(object, encodingVersion)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function encodeJsonObjectToLubbaData (object, encodingVersion) {
|
|
210
|
+
const encoder = encoding__namespace.createEncoder();
|
|
211
|
+
|
|
212
|
+
encoding__namespace.writeUint8(encoder, encodingVersion);
|
|
213
|
+
encoding__namespace.writeUint8(encoder, object.isMetric ? 1 : 0);
|
|
214
|
+
encoding__namespace.writeVarString(encoder, object.title);
|
|
215
|
+
encoding__namespace.writeUint32(encoder, object.activityType);
|
|
216
|
+
encoding__namespace.writeUint8(encoder, object.locationType);
|
|
217
|
+
|
|
218
|
+
// Parts
|
|
219
|
+
const amountParts = (object.parts && object.parts.length) || 0;
|
|
220
|
+
encoding__namespace.writeUint8(encoder, amountParts);
|
|
221
|
+
|
|
222
|
+
for (const part of object.parts) {
|
|
223
|
+
encodePart(part, encoder);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return encoding__namespace.toUint8Array(encoder)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function decodeLubbaData (data) {
|
|
230
|
+
const decoder = decoding__namespace.createDecoder(data);
|
|
231
|
+
const encodingVersion = decoding__namespace.readUint8(decoder);
|
|
232
|
+
|
|
233
|
+
if (encodingVersion === 1) {
|
|
234
|
+
const decoded = {
|
|
235
|
+
isCustom: true // Assume all are custom
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
decoded.isMetric = decoding__namespace.readUint8(decoder) === 1;
|
|
239
|
+
decoded.title = decoding__namespace.readVarString(decoder);
|
|
240
|
+
decoded.activityType = decoding__namespace.readUint32(decoder);
|
|
241
|
+
decoded.locationType = decoding__namespace.readUint8(decoder);
|
|
242
|
+
|
|
243
|
+
// Parts
|
|
244
|
+
const amountParts = decoding__namespace.readUint8(decoder);
|
|
245
|
+
decoded.parts = [];
|
|
246
|
+
for (let i = 0; i < amountParts; i++) {
|
|
247
|
+
decoded.parts.push(decodePart(decoder));
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return decoded
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return null
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Some short encoding (base64)
|
|
257
|
+
function encodeLubbaDataToHash (data) {
|
|
258
|
+
let s = '';
|
|
259
|
+
for (const byte of data) {
|
|
260
|
+
s += String.fromCharCode(byte);
|
|
261
|
+
}
|
|
262
|
+
return btoa(s)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function decodeLubbaDataFromHash (hash) {
|
|
266
|
+
const a = atob(hash);
|
|
267
|
+
const bytes = new Uint8Array(a.length);
|
|
268
|
+
for (let i = 0; i < a.length; i++) {
|
|
269
|
+
bytes[i] = a.charCodeAt(i);
|
|
270
|
+
}
|
|
271
|
+
return bytes
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
exports.decodeLubbaData = decodeLubbaData;
|
|
275
|
+
exports.decodeLubbaDataFromHash = decodeLubbaDataFromHash;
|
|
276
|
+
exports.encodeJsonObjectToLubbaData = encodeJsonObjectToLubbaData;
|
|
277
|
+
exports.encodeLubbaDataToHash = encodeLubbaDataToHash;
|
|
278
|
+
exports.encodeYamlToLubbaData = encodeYamlToLubbaData;
|
|
279
|
+
//# sourceMappingURL=lubba-encoder.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lubba-encoder.cjs","sources":["../src/index.js"],"sourcesContent":["/* global btoa atob */\nimport * as encoding from 'lib0/dist/encoding.cjs'\nimport * as decoding from 'lib0/dist/decoding.cjs'\nimport { parse as parseYaml } from 'yaml'\n\nconst reverseObject = (object) => (\n Object.keys(object).reduce((res, key) => { res[object[key]] = key; return res }, {})\n)\n\nconst partTitleMap = {\n Warmup: 1,\n Activity: 2,\n Fast: 3,\n Recover: 4,\n Rest: 5,\n Cooldown: 6,\n Ski: 7,\n Run: 8,\n Skate: 9,\n Walk: 10\n}\nconst layoutMap = {\n 1: 1,\n '2+1': 2,\n '2+2': 3,\n '2+3': 4\n}\nconst valueGoalTypeMap = {\n duration: 1,\n distance: 2,\n distanceTest: 3,\n paceCurrent: 4,\n paceTest: 5,\n paceTest2: 6,\n paceAverageDuration: 7,\n bpm: 8,\n bpmAverageDuration: 9,\n intervalLeft: 10,\n durationInterval: 11,\n distanceInterval: 12,\n desiredAccuracy: 13,\n pace: 14, // used for target type\n paceSplits: 15\n}\n\nconst partTitleMapReverse = reverseObject(partTitleMap)\nconst layoutMapReverse = reverseObject(layoutMap)\nconst valueGoalTypeMapReverse = reverseObject(valueGoalTypeMap)\n\nfunction getBit (number, bitPosition) {\n return (number & (1 << bitPosition)) === 0 ? 0 : 1\n}\n\nfunction updateBit (number, bitPosition, bitValue) {\n const bitValueNormalized = bitValue ? 1 : 0\n const clearMask = ~(1 << bitPosition)\n return (number & clearMask) | (bitValueNormalized << bitPosition)\n}\n\nfunction encodePart (part, encoder) {\n // Set flags for nil values\n let flagsA = 0\n let flagsB = 0\n\n flagsA = updateBit(flagsA, 0, part.title != null)\n flagsA = updateBit(flagsA, 1, part.layout != null)\n flagsA = updateBit(flagsA, 2, part.layoutValues != null && part.layoutValues.length > 0)\n flagsA = updateBit(flagsA, 3, part.goalType != null)\n flagsA = updateBit(flagsA, 4, part.goalValue != null)\n flagsA = updateBit(flagsA, 5, part.goalIsVariable === true)\n flagsA = updateBit(flagsA, 6, part.targetType != null)\n flagsA = updateBit(flagsA, 7, part.targetMin != null)\n\n flagsB = updateBit(flagsB, 0, part.targetMax != null)\n flagsB = updateBit(flagsB, 1, part.repeats != null && part.repeats > 0)\n flagsB = updateBit(flagsB, 2, part.repeatParts != null && part.repeatParts.length > 0)\n\n encoding.writeUint8(encoder, flagsA)\n encoding.writeUint8(encoder, flagsB)\n\n // Use part title map, or custom title\n if (part.title != null) {\n const titleIndex = partTitleMap[part.title]\n if (titleIndex) {\n encoding.writeUint8(encoder, titleIndex)\n } else {\n encoding.writeUint8(encoder, 0) // 0 indicates custom string\n encoding.writeVarString(encoder, part.title)\n }\n }\n\n if (part.layout != null) encoding.writeUint8(encoder, layoutMap[part.layout])\n if (part.layoutValues != null && part.layoutValues.length > 0) {\n encoding.writeUint8(encoder, part.layoutValues.length)\n\n for (const value of part.layoutValues) {\n encoding.writeUint8(encoder, valueGoalTypeMap[value])\n }\n }\n\n if (part.goalType != null) encoding.writeUint8(encoder, valueGoalTypeMap[part.goalType])\n if (part.goalValue != null) encoding.writeFloat64(encoder, part.goalValue)\n\n if (part.targetType != null) encoding.writeUint8(encoder, valueGoalTypeMap[part.targetType])\n if (part.targetMin != null) encoding.writeFloat64(encoder, part.targetMin)\n if (part.targetMax != null) encoding.writeFloat64(encoder, part.targetMax)\n\n if (part.repeats != null && part.repeats > 0) encoding.writeUint8(encoder, part.repeats)\n\n if (part.repeatParts != null && part.repeatParts.length > 0) {\n encoding.writeUint8(encoder, part.repeatParts.length)\n\n for (const subPart of part.repeatParts) {\n encodePart(subPart, encoder)\n }\n }\n}\n\nfunction decodePart (decoder) {\n const part = {}\n\n // Some flags to indicate what values is non-nil\n const flagsA = decoding.readUint8(decoder)\n const flagsB = decoding.readUint8(decoder)\n\n const hasTitle = getBit(flagsA, 0)\n const hasLayout = getBit(flagsA, 1)\n const hasLayoutValues = getBit(flagsA, 2)\n const hasGoalType = getBit(flagsA, 3)\n const hasGoalValue = getBit(flagsA, 4)\n const hasGoalIsVariable = getBit(flagsA, 5)\n const hasTargetType = getBit(flagsA, 6)\n const hasTargetMin = getBit(flagsA, 7)\n\n const hasTargetMax = getBit(flagsB, 0)\n const hasRepeats = getBit(flagsB, 1)\n const hasRepeatParts = getBit(flagsB, 2)\n\n // Use part title map, or custom title\n if (hasTitle) {\n const titleIndex = decoding.readUint8(decoder)\n const titleIsCustomString = titleIndex === 0\n part.title = titleIsCustomString ? decoding.readVarString(decoder) : partTitleMapReverse[titleIndex]\n }\n\n if (hasLayout) part.layout = layoutMapReverse[decoding.readUint8(decoder)]\n if (hasLayoutValues) {\n const amountValues = decoding.readUint8(decoder)\n part.layoutValues = []\n\n for (let j = 0; j < amountValues; j++) {\n part.layoutValues.push(valueGoalTypeMapReverse[decoding.readUint8(decoder)])\n }\n }\n\n if (hasGoalType) part.goalType = valueGoalTypeMapReverse[decoding.readUint8(decoder)]\n if (hasGoalValue) part.goalValue = decoding.readFloat64(decoder)\n if (hasGoalIsVariable) part.goalIsVariable = true\n\n if (hasTargetType) part.targetType = valueGoalTypeMapReverse[decoding.readUint8(decoder)]\n if (hasTargetMin) part.targetMin = decoding.readFloat64(decoder)\n if (hasTargetMax) part.targetMax = decoding.readFloat64(decoder)\n\n if (hasRepeats) part.repeats = decoding.readUint8(decoder)\n if (hasRepeatParts) {\n const amountParts = decoding.readUint8(decoder)\n part.repeatParts = []\n\n for (let j = 0; j < amountParts; j++) {\n part.repeatParts.push(decodePart(decoder))\n }\n }\n\n return part\n}\n\nexport function encodeYamlToLubbaData (yaml, encodingVersion) {\n const object = parseYaml(yaml)\n\n return encodeJsonObjectToLubbaData(object, encodingVersion)\n}\n\nexport function encodeJsonObjectToLubbaData (object, encodingVersion) {\n const encoder = encoding.createEncoder()\n\n encoding.writeUint8(encoder, encodingVersion)\n encoding.writeUint8(encoder, object.isMetric ? 1 : 0)\n encoding.writeVarString(encoder, object.title)\n encoding.writeUint32(encoder, object.activityType)\n encoding.writeUint8(encoder, object.locationType)\n\n // Parts\n const amountParts = (object.parts && object.parts.length) || 0\n encoding.writeUint8(encoder, amountParts)\n\n for (const part of object.parts) {\n encodePart(part, encoder)\n }\n\n return encoding.toUint8Array(encoder)\n}\n\nexport function decodeLubbaData (data) {\n const decoder = decoding.createDecoder(data)\n const encodingVersion = decoding.readUint8(decoder)\n\n if (encodingVersion === 1) {\n const decoded = {\n isCustom: true // Assume all are custom\n }\n\n decoded.isMetric = decoding.readUint8(decoder) === 1\n decoded.title = decoding.readVarString(decoder)\n decoded.activityType = decoding.readUint32(decoder)\n decoded.locationType = decoding.readUint8(decoder)\n\n // Parts\n const amountParts = decoding.readUint8(decoder)\n decoded.parts = []\n for (let i = 0; i < amountParts; i++) {\n decoded.parts.push(decodePart(decoder))\n }\n\n return decoded\n }\n\n return null\n}\n\n// Some short encoding (base64)\nexport function encodeLubbaDataToHash (data) {\n let s = ''\n for (const byte of data) {\n s += String.fromCharCode(byte)\n }\n return btoa(s)\n}\n\nexport function decodeLubbaDataFromHash (hash) {\n const a = atob(hash)\n const bytes = new Uint8Array(a.length)\n for (let i = 0; i < a.length; i++) {\n bytes[i] = a.charCodeAt(i)\n }\n return bytes\n}\n"],"names":["encoding","decoding","yaml","parseYaml"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "lubba-encoder",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"main": "./dist/lubba-encoder.cjs",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"exports": {
|
|
7
|
+
"./lubba-encoder": "./dist/lubba-encoder.cjs"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"dist": "rm -rf dist && rollup -c",
|
|
11
|
+
"prepare": "npm run dist",
|
|
12
|
+
"test": "jest",
|
|
13
|
+
"lint": "standard"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"jest": "27.5.1",
|
|
17
|
+
"rollup": "^2.58.3",
|
|
18
|
+
"standard": "16.0.4"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"lib0": "0.2.49",
|
|
22
|
+
"yaml": "2.0.0"
|
|
23
|
+
},
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "https://github.com/hesselbom/lubba-encoder"
|
|
27
|
+
}
|
|
28
|
+
}
|
package/rollup.config.js
ADDED
package/src/index.js
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/* global btoa atob */
|
|
2
|
+
import * as encoding from 'lib0/dist/encoding.cjs'
|
|
3
|
+
import * as decoding from 'lib0/dist/decoding.cjs'
|
|
4
|
+
import { parse as parseYaml } from 'yaml'
|
|
5
|
+
|
|
6
|
+
const reverseObject = (object) => (
|
|
7
|
+
Object.keys(object).reduce((res, key) => { res[object[key]] = key; return res }, {})
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
const partTitleMap = {
|
|
11
|
+
Warmup: 1,
|
|
12
|
+
Activity: 2,
|
|
13
|
+
Fast: 3,
|
|
14
|
+
Recover: 4,
|
|
15
|
+
Rest: 5,
|
|
16
|
+
Cooldown: 6,
|
|
17
|
+
Ski: 7,
|
|
18
|
+
Run: 8,
|
|
19
|
+
Skate: 9,
|
|
20
|
+
Walk: 10
|
|
21
|
+
}
|
|
22
|
+
const layoutMap = {
|
|
23
|
+
1: 1,
|
|
24
|
+
'2+1': 2,
|
|
25
|
+
'2+2': 3,
|
|
26
|
+
'2+3': 4
|
|
27
|
+
}
|
|
28
|
+
const valueGoalTypeMap = {
|
|
29
|
+
duration: 1,
|
|
30
|
+
distance: 2,
|
|
31
|
+
distanceTest: 3,
|
|
32
|
+
paceCurrent: 4,
|
|
33
|
+
paceTest: 5,
|
|
34
|
+
paceTest2: 6,
|
|
35
|
+
paceAverageDuration: 7,
|
|
36
|
+
bpm: 8,
|
|
37
|
+
bpmAverageDuration: 9,
|
|
38
|
+
intervalLeft: 10,
|
|
39
|
+
durationInterval: 11,
|
|
40
|
+
distanceInterval: 12,
|
|
41
|
+
desiredAccuracy: 13,
|
|
42
|
+
pace: 14, // used for target type
|
|
43
|
+
paceSplits: 15
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const partTitleMapReverse = reverseObject(partTitleMap)
|
|
47
|
+
const layoutMapReverse = reverseObject(layoutMap)
|
|
48
|
+
const valueGoalTypeMapReverse = reverseObject(valueGoalTypeMap)
|
|
49
|
+
|
|
50
|
+
function getBit (number, bitPosition) {
|
|
51
|
+
return (number & (1 << bitPosition)) === 0 ? 0 : 1
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function updateBit (number, bitPosition, bitValue) {
|
|
55
|
+
const bitValueNormalized = bitValue ? 1 : 0
|
|
56
|
+
const clearMask = ~(1 << bitPosition)
|
|
57
|
+
return (number & clearMask) | (bitValueNormalized << bitPosition)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function encodePart (part, encoder) {
|
|
61
|
+
// Set flags for nil values
|
|
62
|
+
let flagsA = 0
|
|
63
|
+
let flagsB = 0
|
|
64
|
+
|
|
65
|
+
flagsA = updateBit(flagsA, 0, part.title != null)
|
|
66
|
+
flagsA = updateBit(flagsA, 1, part.layout != null)
|
|
67
|
+
flagsA = updateBit(flagsA, 2, part.layoutValues != null && part.layoutValues.length > 0)
|
|
68
|
+
flagsA = updateBit(flagsA, 3, part.goalType != null)
|
|
69
|
+
flagsA = updateBit(flagsA, 4, part.goalValue != null)
|
|
70
|
+
flagsA = updateBit(flagsA, 5, part.goalIsVariable === true)
|
|
71
|
+
flagsA = updateBit(flagsA, 6, part.targetType != null)
|
|
72
|
+
flagsA = updateBit(flagsA, 7, part.targetMin != null)
|
|
73
|
+
|
|
74
|
+
flagsB = updateBit(flagsB, 0, part.targetMax != null)
|
|
75
|
+
flagsB = updateBit(flagsB, 1, part.repeats != null && part.repeats > 0)
|
|
76
|
+
flagsB = updateBit(flagsB, 2, part.repeatParts != null && part.repeatParts.length > 0)
|
|
77
|
+
|
|
78
|
+
encoding.writeUint8(encoder, flagsA)
|
|
79
|
+
encoding.writeUint8(encoder, flagsB)
|
|
80
|
+
|
|
81
|
+
// Use part title map, or custom title
|
|
82
|
+
if (part.title != null) {
|
|
83
|
+
const titleIndex = partTitleMap[part.title]
|
|
84
|
+
if (titleIndex) {
|
|
85
|
+
encoding.writeUint8(encoder, titleIndex)
|
|
86
|
+
} else {
|
|
87
|
+
encoding.writeUint8(encoder, 0) // 0 indicates custom string
|
|
88
|
+
encoding.writeVarString(encoder, part.title)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (part.layout != null) encoding.writeUint8(encoder, layoutMap[part.layout])
|
|
93
|
+
if (part.layoutValues != null && part.layoutValues.length > 0) {
|
|
94
|
+
encoding.writeUint8(encoder, part.layoutValues.length)
|
|
95
|
+
|
|
96
|
+
for (const value of part.layoutValues) {
|
|
97
|
+
encoding.writeUint8(encoder, valueGoalTypeMap[value])
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (part.goalType != null) encoding.writeUint8(encoder, valueGoalTypeMap[part.goalType])
|
|
102
|
+
if (part.goalValue != null) encoding.writeFloat64(encoder, part.goalValue)
|
|
103
|
+
|
|
104
|
+
if (part.targetType != null) encoding.writeUint8(encoder, valueGoalTypeMap[part.targetType])
|
|
105
|
+
if (part.targetMin != null) encoding.writeFloat64(encoder, part.targetMin)
|
|
106
|
+
if (part.targetMax != null) encoding.writeFloat64(encoder, part.targetMax)
|
|
107
|
+
|
|
108
|
+
if (part.repeats != null && part.repeats > 0) encoding.writeUint8(encoder, part.repeats)
|
|
109
|
+
|
|
110
|
+
if (part.repeatParts != null && part.repeatParts.length > 0) {
|
|
111
|
+
encoding.writeUint8(encoder, part.repeatParts.length)
|
|
112
|
+
|
|
113
|
+
for (const subPart of part.repeatParts) {
|
|
114
|
+
encodePart(subPart, encoder)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function decodePart (decoder) {
|
|
120
|
+
const part = {}
|
|
121
|
+
|
|
122
|
+
// Some flags to indicate what values is non-nil
|
|
123
|
+
const flagsA = decoding.readUint8(decoder)
|
|
124
|
+
const flagsB = decoding.readUint8(decoder)
|
|
125
|
+
|
|
126
|
+
const hasTitle = getBit(flagsA, 0)
|
|
127
|
+
const hasLayout = getBit(flagsA, 1)
|
|
128
|
+
const hasLayoutValues = getBit(flagsA, 2)
|
|
129
|
+
const hasGoalType = getBit(flagsA, 3)
|
|
130
|
+
const hasGoalValue = getBit(flagsA, 4)
|
|
131
|
+
const hasGoalIsVariable = getBit(flagsA, 5)
|
|
132
|
+
const hasTargetType = getBit(flagsA, 6)
|
|
133
|
+
const hasTargetMin = getBit(flagsA, 7)
|
|
134
|
+
|
|
135
|
+
const hasTargetMax = getBit(flagsB, 0)
|
|
136
|
+
const hasRepeats = getBit(flagsB, 1)
|
|
137
|
+
const hasRepeatParts = getBit(flagsB, 2)
|
|
138
|
+
|
|
139
|
+
// Use part title map, or custom title
|
|
140
|
+
if (hasTitle) {
|
|
141
|
+
const titleIndex = decoding.readUint8(decoder)
|
|
142
|
+
const titleIsCustomString = titleIndex === 0
|
|
143
|
+
part.title = titleIsCustomString ? decoding.readVarString(decoder) : partTitleMapReverse[titleIndex]
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (hasLayout) part.layout = layoutMapReverse[decoding.readUint8(decoder)]
|
|
147
|
+
if (hasLayoutValues) {
|
|
148
|
+
const amountValues = decoding.readUint8(decoder)
|
|
149
|
+
part.layoutValues = []
|
|
150
|
+
|
|
151
|
+
for (let j = 0; j < amountValues; j++) {
|
|
152
|
+
part.layoutValues.push(valueGoalTypeMapReverse[decoding.readUint8(decoder)])
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (hasGoalType) part.goalType = valueGoalTypeMapReverse[decoding.readUint8(decoder)]
|
|
157
|
+
if (hasGoalValue) part.goalValue = decoding.readFloat64(decoder)
|
|
158
|
+
if (hasGoalIsVariable) part.goalIsVariable = true
|
|
159
|
+
|
|
160
|
+
if (hasTargetType) part.targetType = valueGoalTypeMapReverse[decoding.readUint8(decoder)]
|
|
161
|
+
if (hasTargetMin) part.targetMin = decoding.readFloat64(decoder)
|
|
162
|
+
if (hasTargetMax) part.targetMax = decoding.readFloat64(decoder)
|
|
163
|
+
|
|
164
|
+
if (hasRepeats) part.repeats = decoding.readUint8(decoder)
|
|
165
|
+
if (hasRepeatParts) {
|
|
166
|
+
const amountParts = decoding.readUint8(decoder)
|
|
167
|
+
part.repeatParts = []
|
|
168
|
+
|
|
169
|
+
for (let j = 0; j < amountParts; j++) {
|
|
170
|
+
part.repeatParts.push(decodePart(decoder))
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return part
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function encodeYamlToLubbaData (yaml, encodingVersion) {
|
|
178
|
+
const object = parseYaml(yaml)
|
|
179
|
+
|
|
180
|
+
return encodeJsonObjectToLubbaData(object, encodingVersion)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function encodeJsonObjectToLubbaData (object, encodingVersion) {
|
|
184
|
+
const encoder = encoding.createEncoder()
|
|
185
|
+
|
|
186
|
+
encoding.writeUint8(encoder, encodingVersion)
|
|
187
|
+
encoding.writeUint8(encoder, object.isMetric ? 1 : 0)
|
|
188
|
+
encoding.writeVarString(encoder, object.title)
|
|
189
|
+
encoding.writeUint32(encoder, object.activityType)
|
|
190
|
+
encoding.writeUint8(encoder, object.locationType)
|
|
191
|
+
|
|
192
|
+
// Parts
|
|
193
|
+
const amountParts = (object.parts && object.parts.length) || 0
|
|
194
|
+
encoding.writeUint8(encoder, amountParts)
|
|
195
|
+
|
|
196
|
+
for (const part of object.parts) {
|
|
197
|
+
encodePart(part, encoder)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return encoding.toUint8Array(encoder)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function decodeLubbaData (data) {
|
|
204
|
+
const decoder = decoding.createDecoder(data)
|
|
205
|
+
const encodingVersion = decoding.readUint8(decoder)
|
|
206
|
+
|
|
207
|
+
if (encodingVersion === 1) {
|
|
208
|
+
const decoded = {
|
|
209
|
+
isCustom: true // Assume all are custom
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
decoded.isMetric = decoding.readUint8(decoder) === 1
|
|
213
|
+
decoded.title = decoding.readVarString(decoder)
|
|
214
|
+
decoded.activityType = decoding.readUint32(decoder)
|
|
215
|
+
decoded.locationType = decoding.readUint8(decoder)
|
|
216
|
+
|
|
217
|
+
// Parts
|
|
218
|
+
const amountParts = decoding.readUint8(decoder)
|
|
219
|
+
decoded.parts = []
|
|
220
|
+
for (let i = 0; i < amountParts; i++) {
|
|
221
|
+
decoded.parts.push(decodePart(decoder))
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return decoded
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return null
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Some short encoding (base64)
|
|
231
|
+
export function encodeLubbaDataToHash (data) {
|
|
232
|
+
let s = ''
|
|
233
|
+
for (const byte of data) {
|
|
234
|
+
s += String.fromCharCode(byte)
|
|
235
|
+
}
|
|
236
|
+
return btoa(s)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function decodeLubbaDataFromHash (hash) {
|
|
240
|
+
const a = atob(hash)
|
|
241
|
+
const bytes = new Uint8Array(a.length)
|
|
242
|
+
for (let i = 0; i < a.length; i++) {
|
|
243
|
+
bytes[i] = a.charCodeAt(i)
|
|
244
|
+
}
|
|
245
|
+
return bytes
|
|
246
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
/* eslint-env jest */
|
|
5
|
+
import {
|
|
6
|
+
encodeYamlToLubbaData,
|
|
7
|
+
encodeJsonObjectToLubbaData,
|
|
8
|
+
decodeLubbaData,
|
|
9
|
+
encodeLubbaDataToHash,
|
|
10
|
+
decodeLubbaDataFromHash
|
|
11
|
+
} from './index'
|
|
12
|
+
|
|
13
|
+
const yaml = `id: 123abc-dsa
|
|
14
|
+
isMetric: true
|
|
15
|
+
title: Runcoach Speed 04/13
|
|
16
|
+
isCustom: true
|
|
17
|
+
activityType: 37
|
|
18
|
+
locationType: 3
|
|
19
|
+
parts:
|
|
20
|
+
- title: Warmup
|
|
21
|
+
goalType: distance
|
|
22
|
+
goalValue: 2e+3
|
|
23
|
+
- title: Drills
|
|
24
|
+
- title: Custom layout
|
|
25
|
+
layout: 2+2
|
|
26
|
+
layoutValues:
|
|
27
|
+
- distance
|
|
28
|
+
- duration
|
|
29
|
+
- bpm
|
|
30
|
+
- paceCurrent
|
|
31
|
+
- repeats: 4
|
|
32
|
+
repeatParts:
|
|
33
|
+
- title: Run
|
|
34
|
+
goalType: duration
|
|
35
|
+
goalValue: 3e+2
|
|
36
|
+
targetType: pace
|
|
37
|
+
targetMin: 2.56e+2
|
|
38
|
+
targetMax: 2.76e+2
|
|
39
|
+
- title: Recover
|
|
40
|
+
goalType: duration
|
|
41
|
+
goalValue: 1.8e+2
|
|
42
|
+
- title: Pace Splits
|
|
43
|
+
goalType: distance
|
|
44
|
+
goalValue: 2e+3
|
|
45
|
+
targetType: paceSplits
|
|
46
|
+
targetMin: 3e+2
|
|
47
|
+
targetMax: -1
|
|
48
|
+
- title: Cooldown
|
|
49
|
+
goalType: distance
|
|
50
|
+
goalIsVariable: true
|
|
51
|
+
targetType: bpm
|
|
52
|
+
targetMin: 0
|
|
53
|
+
targetMax: 3e+2
|
|
54
|
+
`
|
|
55
|
+
|
|
56
|
+
const jsonObject = {
|
|
57
|
+
id: '123abc-dsa',
|
|
58
|
+
isMetric: true,
|
|
59
|
+
title: 'Runcoach Speed 04/13',
|
|
60
|
+
isCustom: true,
|
|
61
|
+
activityType: 37,
|
|
62
|
+
locationType: 3,
|
|
63
|
+
parts: [
|
|
64
|
+
{
|
|
65
|
+
title: 'Warmup',
|
|
66
|
+
goalType: 'distance',
|
|
67
|
+
goalValue: 2000
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
title: 'Drills'
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
title: 'Custom layout',
|
|
74
|
+
layout: '2+2',
|
|
75
|
+
layoutValues: [
|
|
76
|
+
'distance',
|
|
77
|
+
'duration',
|
|
78
|
+
'bpm',
|
|
79
|
+
'paceCurrent'
|
|
80
|
+
]
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
repeats: 4,
|
|
84
|
+
repeatParts: [
|
|
85
|
+
{
|
|
86
|
+
title: 'Run',
|
|
87
|
+
goalType: 'duration',
|
|
88
|
+
goalValue: 300,
|
|
89
|
+
targetType: 'pace',
|
|
90
|
+
targetMin: 256,
|
|
91
|
+
targetMax: 276
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
title: 'Recover',
|
|
95
|
+
goalType: 'duration',
|
|
96
|
+
goalValue: 180
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
title: 'Pace Splits',
|
|
100
|
+
goalType: 'distance',
|
|
101
|
+
goalValue: 2000,
|
|
102
|
+
targetType: 'paceSplits',
|
|
103
|
+
targetMin: 300,
|
|
104
|
+
targetMax: -1
|
|
105
|
+
}
|
|
106
|
+
]
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
title: 'Cooldown',
|
|
110
|
+
goalType: 'distance',
|
|
111
|
+
goalIsVariable: true,
|
|
112
|
+
targetType: 'bpm',
|
|
113
|
+
targetMin: 0,
|
|
114
|
+
targetMax: 300
|
|
115
|
+
}
|
|
116
|
+
]
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const result = {
|
|
120
|
+
isMetric: true,
|
|
121
|
+
title: 'Runcoach Speed 04/13',
|
|
122
|
+
isCustom: true,
|
|
123
|
+
activityType: 37,
|
|
124
|
+
locationType: 3,
|
|
125
|
+
parts: [
|
|
126
|
+
{ title: 'Warmup', goalType: 'distance', goalValue: 2000 },
|
|
127
|
+
{ title: 'Drills' },
|
|
128
|
+
{
|
|
129
|
+
title: 'Custom layout',
|
|
130
|
+
layout: '2+2',
|
|
131
|
+
layoutValues: ['distance', 'duration', 'bpm', 'paceCurrent']
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
repeats: 4,
|
|
135
|
+
repeatParts: [
|
|
136
|
+
{
|
|
137
|
+
title: 'Run',
|
|
138
|
+
goalType: 'duration',
|
|
139
|
+
goalValue: 300,
|
|
140
|
+
targetType: 'pace',
|
|
141
|
+
targetMin: 256,
|
|
142
|
+
targetMax: 276
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
title: 'Recover',
|
|
146
|
+
goalType: 'duration',
|
|
147
|
+
goalValue: 180
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
title: 'Pace Splits',
|
|
151
|
+
goalType: 'distance',
|
|
152
|
+
goalValue: 2000,
|
|
153
|
+
targetType: 'paceSplits',
|
|
154
|
+
targetMin: 300,
|
|
155
|
+
targetMax: -1
|
|
156
|
+
}
|
|
157
|
+
]
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
title: 'Cooldown',
|
|
161
|
+
goalType: 'distance',
|
|
162
|
+
goalIsVariable: true,
|
|
163
|
+
targetType: 'bpm',
|
|
164
|
+
targetMin: 0,
|
|
165
|
+
targetMax: 300
|
|
166
|
+
}
|
|
167
|
+
]
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
test('works encoding and then decoding with yaml', () => {
|
|
171
|
+
const encodingVersion = 1
|
|
172
|
+
const encoded = encodeYamlToLubbaData(yaml, encodingVersion)
|
|
173
|
+
const decoded = decodeLubbaData(encoded)
|
|
174
|
+
|
|
175
|
+
// Expect first byte to be version
|
|
176
|
+
expect(encoded[0]).toBe(encodingVersion)
|
|
177
|
+
|
|
178
|
+
expect(decoded).toEqual(result)
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
test('works encoding and then decoding with json object', () => {
|
|
182
|
+
const encodingVersion = 1
|
|
183
|
+
const encoded = encodeJsonObjectToLubbaData(jsonObject, encodingVersion)
|
|
184
|
+
const decoded = decodeLubbaData(encoded)
|
|
185
|
+
|
|
186
|
+
// Expect first byte to be version
|
|
187
|
+
expect(encoded[0]).toBe(encodingVersion)
|
|
188
|
+
|
|
189
|
+
expect(decoded).toEqual(result)
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
test('works encoding for url hash', () => {
|
|
193
|
+
/*
|
|
194
|
+
https://stackoverflow.com/a/44532746/511949
|
|
195
|
+
Max hash size:
|
|
196
|
+
|
|
197
|
+
Chrome: 50K+
|
|
198
|
+
Firefox: 50K+
|
|
199
|
+
Safari (iOS): 50K+
|
|
200
|
+
Internet Explorer 11: Fails between 2,025 and 2,050
|
|
201
|
+
Microsoft Edge: Fails between 2,025 and 2,050
|
|
202
|
+
*/
|
|
203
|
+
const MAX_HASH_SIZE = 2020
|
|
204
|
+
const encodingVersion = 1
|
|
205
|
+
const encoded = encodeYamlToLubbaData(yaml, encodingVersion)
|
|
206
|
+
const hash = encodeLubbaDataToHash(encoded)
|
|
207
|
+
const decodedFromHash = decodeLubbaDataFromHash(hash)
|
|
208
|
+
const decoded = decodeLubbaData(decodedFromHash)
|
|
209
|
+
|
|
210
|
+
// No need to check exact hash, but just that we get a string
|
|
211
|
+
expect(typeof hash).toBe('string')
|
|
212
|
+
expect(hash.length < MAX_HASH_SIZE).toBe(true)
|
|
213
|
+
|
|
214
|
+
// Log hash to be able to test against other implementations
|
|
215
|
+
console.log('Encoded bytes:', encoded.join(','))
|
|
216
|
+
console.log('Encoded hash:', hash)
|
|
217
|
+
|
|
218
|
+
expect(decodedFromHash).toEqual(encoded)
|
|
219
|
+
expect(decoded).toEqual(result)
|
|
220
|
+
})
|