• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

streetsidesoftware / cspell / 23410982485

22 Mar 2026 07:42PM UTC coverage: 93.033% (+0.1%) from 92.904%
23410982485

Pull #8680

github

web-flow
Merge 0a1157d35 into fc7b60b0f
Pull Request #8680: fix: make flatpack diff friendly

9600 of 11415 branches covered (84.1%)

1299 of 1336 new or added lines in 17 files covered. (97.23%)

8 existing lines in 3 files now uncovered.

19121 of 20553 relevant lines covered (93.03%)

30216.63 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.19
/packages/flatpack-json/src/unpack.mts
1
import assert from 'node:assert';
2

3
import { getFlatpackedRootIdx } from './flatpacked.mjs';
4
import { RefCounter } from './RefCounter.mjs';
5
import { StringTable } from './stringTable.mjs';
6
import type {
7
    AnnotateUnpacked,
8
    ArrayBasedElements,
9
    ArrayElement,
10
    BigIntElement,
11
    DateElement,
12
    Flatpacked,
13
    FlattenedElement,
14
    MapElement,
15
    ObjectElement,
16
    Primitive,
17
    PrimitiveArray,
18
    PrimitiveMap,
19
    PrimitiveObject,
20
    PrimitiveSet,
21
    RawUnpacked,
22
    RegExpElement,
23
    Serializable,
24
    SetElement,
25
    StringElement,
26
    StringTableElement,
27
    SubStringElement,
28
    Unpacked,
29
    UnpackedAnnotation,
30
    UnpackMetaData,
31
} from './types.mjs';
32
import { ElementType, supportedHeaders, symbolFlatpackAnnotation } from './types.mjs';
33

34
export function fromJSON<T = Unpacked>(data: Flatpacked): T {
35
    const [header] = data;
1,065✔
36
    let stringTable: StringTable | undefined;
37

38
    if (!supportedHeaders.has(header)) {
1,065!
39
        throw new Error('Invalid header');
×
40
    }
41

42
    const cache = new Map<number, RawUnpacked>([[0, undefined]]);
1,065✔
43

44
    /**
45
     * indexes that have been referenced by multiple objects.
46
     * A count of 1 means that there is only 1 reference.
47
     */
48
    const referenced = new RefCounter<number>();
1,065✔
49

50
    const meta: UnpackMetaData = {
1,065✔
51
        flatpack: data,
52
        referenced,
53
        rootIndex: getFlatpackedRootIdx(data),
54
    };
55

56
    return idxToValue(1) as T;
1,065✔
57

58
    function cacheValue(idx: number, value: RawUnpacked): RawUnpacked {
59
        assert(!cache.has(idx), `Index ${idx} already exists in cache`);
71,961✔
60
        cache.set(idx, value);
71,961✔
61
        return value;
71,961✔
62
    }
63

64
    function getCachedValue(idx: number): RawUnpacked | undefined {
65
        referenced.add(idx);
204,089✔
66
        return cache.get(idx);
204,089✔
67
    }
68

69
    function mergeKeysValues<K>(keys: readonly K[], values: PrimitiveArray): [K, Serializable][] {
70
        return keys.map((key, i) => [key, values[i]]);
54,276✔
71
    }
72

73
    function toSet(idx: number, elem: SetElement): PrimitiveSet {
74
        const [_, k] = elem;
135✔
75
        const s: PrimitiveSet = k ? (new Set(idxToArr(k)) as PrimitiveSet) : new Set();
135!
76
        cacheValue(idx, s);
135✔
77
        return s;
135✔
78
    }
79

80
    function toMap(idx: number, elem: MapElement): PrimitiveMap {
81
        const [_, k, v] = elem;
79✔
82
        const m: PrimitiveMap =
83
            !k || !v ? new Map() : (new Map(mergeKeysValues(idxToArr(k), idxToArr(v))) as PrimitiveMap);
79!
84
        cacheValue(idx, m);
79✔
85
        return m;
79✔
86
    }
87

88
    function toRegExp(idx: number, elem: RegExpElement): RegExp {
89
        const [_, pattern, flags] = elem;
155✔
90
        const p = idxToValue(pattern) as string;
155✔
91
        const f = idxToValue(flags) as string;
155✔
92
        const r = new RegExp(p, f);
155✔
93
        cacheValue(idx, r);
155✔
94
        return r;
155✔
95
    }
96

97
    function toBigInt(idx: number, elem: BigIntElement): bigint {
98
        const [_, vIdx] = elem;
182✔
99
        const r = BigInt(idxToValue(vIdx) as string | number);
182✔
100
        cacheValue(idx, r);
182✔
101
        return r;
182✔
102
    }
103

104
    function toDate(idx: number, elem: DateElement): Date {
105
        const [_, value] = elem;
60✔
106
        const r = new Date(value);
60✔
107
        cacheValue(idx, r);
60✔
108
        return r;
60✔
109
    }
110

111
    function toString(idx: number, elem: StringElement | string): string {
112
        const s = typeof elem === 'string' ? elem : idxToString(elem.slice(1) as number[]);
16,544!
113
        cacheValue(idx, s);
16,544✔
114
        return s;
16,544✔
115
    }
116

117
    function toObj(idx: number, elem: ObjectElement): PrimitiveObject {
118
        const [_, k, v] = elem;
13,722✔
119
        // Object Wrapper
120
        if (!k && v) {
13,722✔
121
            const obj = Object(idxToValue(v));
57✔
122
            cacheValue(idx, obj);
57✔
123
            return obj as PrimitiveObject;
57✔
124
        }
125

126
        const obj = {};
13,665✔
127
        cacheValue(idx, obj);
13,665✔
128

129
        if (!k || !v) return obj;
13,665✔
130
        const keys = idxToArr(k) as string[];
13,647✔
131
        const values = idxToArr(v);
13,647✔
132
        Object.assign(obj, Object.fromEntries(mergeKeysValues(keys, values)));
13,647✔
133
        return obj;
13,647✔
134
    }
135

136
    function idxToArr(idx: number): PrimitiveArray {
137
        const found = getCachedValue(idx);
27,587✔
138
        if (found !== undefined) {
27,587✔
139
            return found as PrimitiveArray;
13,342✔
140
        }
141
        const element = data[idx];
14,245✔
142
        assert(isArrayElement(element));
14,245✔
143
        return toArr(idx, element);
14,245✔
144
    }
145

146
    function toArr(idx: number, element: ArrayElement): PrimitiveArray {
147
        const placeHolder: Serializable[] = [];
32,579✔
148
        const refs = element.slice(1);
32,579✔
149
        cacheValue(idx, placeHolder);
32,579✔
150
        const arr = refs.map(idxToValue);
32,579✔
151
        // check if the array has been referenced by another object.
152
        if (!referenced.isReferenced(idx)) {
32,579!
153
            // It has not, just replace the placeholder with the array.
NEW
154
            cacheValue(idx, arr);
×
UNCOV
155
            return arr;
×
156
        }
157
        placeHolder.push(...arr);
32,579✔
158
        return placeHolder;
32,579✔
159
    }
160

161
    function handleSubStringElement(idx: number, refs: SubStringElement): string {
162
        const [_t, sIdx, len, offset = 0] = refs;
8,505✔
163
        const s = `${idxToValue(sIdx)}`.slice(offset, offset + len);
8,505✔
164
        cacheValue(idx, s);
8,505✔
165
        return s;
8,505✔
166
    }
167

168
    function handleArrayElement(
169
        idx: number,
170
        element: ArrayBasedElements,
171
    ): PrimitiveArray | Primitive | PrimitiveObject | PrimitiveSet | PrimitiveMap | undefined {
172
        switch (element[0]) {
58,088✔
173
            case ElementType.Array: {
174
                return toArr(idx, element as ArrayElement);
18,334✔
175
            }
176
            case ElementType.Object: {
177
                return toObj(idx, element as ObjectElement);
13,722✔
178
            }
179
            case ElementType.String: {
180
                return toString(idx, element as StringElement);
16,544✔
181
            }
182
            case ElementType.SubString: {
183
                return handleSubStringElement(idx, element as SubStringElement);
8,505✔
184
            }
185
            case ElementType.Set: {
186
                return toSet(idx, element as SetElement);
135✔
187
            }
188
            case ElementType.Map: {
189
                return toMap(idx, element as MapElement);
79✔
190
            }
191
            case ElementType.RegExp: {
192
                return toRegExp(idx, element as RegExpElement);
155✔
193
            }
194
            case ElementType.Date: {
195
                return toDate(idx, element as DateElement);
60✔
196
            }
197
            case ElementType.BigInt: {
198
                return toBigInt(idx, element as BigIntElement);
182✔
199
            }
200
            case ElementType.StringTable: {
201
                stringTable = new StringTable(element as StringTableElement);
372✔
202
                return idxToValue(idx + 1);
372✔
203
            }
204
        }
NEW
205
        return undefined;
×
206
    }
207

208
    function idxToString(idx: number[]): string {
209
        return joinToString(idx.map((i) => idxToValue(i)));
33,367✔
210
    }
211

212
    function idxToValue(idx: number): Unpacked {
213
        if (!idx) return undefined;
313,802✔
214
        if (idx < 0) {
310,139✔
215
            referenced.add(idx);
133,637✔
216
            return stringTable?.get(-idx);
133,637✔
217
        }
218
        const found = getCachedValue(idx);
176,502✔
219
        if (found !== undefined) {
176,502✔
220
            return annotateUnpacked(found, { meta, index: idx });
42,574✔
221
        }
222

223
        const element = data[idx];
133,928✔
224

225
        if (typeof element === 'object') {
133,928✔
226
            // eslint-disable-next-line unicorn/no-null
227
            if (element === null) return null;
58,246✔
228
            if (Array.isArray(element))
58,205✔
229
                return annotateUnpacked(handleArrayElement(idx, element as ArrayBasedElements), {
58,088✔
230
                    meta,
231
                    index: idx,
232
                });
233
            return annotateUnpacked<PrimitiveObject>({}, { meta, index: idx });
117✔
234
        }
235
        return element;
75,682✔
236
    }
237
}
238

239
function joinToString(parts: PrimitiveArray): string {
240
    return parts.flat().join('');
16,544✔
241
}
242

243
function isArrayElement(value: FlattenedElement): value is ArrayElement {
244
    return Array.isArray(value) && value[0] === ElementType.Array;
14,245✔
245
}
246

247
export function parse(data: string): Unpacked {
248
    return fromJSON(JSON.parse(data));
5✔
249
}
250

251
function annotateUnpacked<T extends RawUnpacked>(value: T, meta: UnpackedAnnotation): AnnotateUnpacked<T> {
252
    if (value && typeof value === 'object') {
100,779✔
253
        if (Object.hasOwn(value, symbolFlatpackAnnotation)) {
48,452✔
254
            return value as AnnotateUnpacked<T>;
15,722✔
255
        }
256

257
        return Object.defineProperty(value, symbolFlatpackAnnotation, { value: meta }) as AnnotateUnpacked<T>;
32,730✔
258
    }
259
    return value as AnnotateUnpacked<T>;
52,327✔
260
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc