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

streetsidesoftware / cspell / 23342742569

20 Mar 2026 12:25PM UTC coverage: 93.002% (+0.1%) from 92.904%
23342742569

Pull #8680

github

web-flow
Merge ac80a4eef into c1c689764
Pull Request #8680: fix: make flatpack diff friendly

9752 of 11594 branches covered (84.11%)

1187 of 1242 new or added lines in 17 files covered. (95.57%)

7 existing lines in 2 files now uncovered.

19364 of 20821 relevant lines covered (93.0%)

29840.7 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(data: Flatpacked): Unpacked {
35
    const [header] = data;
1,168✔
36
    let stringTable: StringTable | undefined;
37

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

42
    const cache = new Map<number, RawUnpacked>([[0, undefined]]);
1,168✔
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,168✔
49

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

56
    return idxToValue(1);
1,168✔
57

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

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

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

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

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

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

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

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

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

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

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

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

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

146
    function toArr(idx: number, element: ArrayElement): PrimitiveArray {
147
        const placeHolder: Serializable[] = [];
32,759✔
148
        const refs = element.slice(1);
32,759✔
149
        cacheValue(idx, placeHolder);
32,759✔
150
        const arr = refs.map(idxToValue);
32,759✔
151
        // check if the array has been referenced by another object.
152
        if (!referenced.isReferenced(idx)) {
32,759!
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,759✔
158
        return placeHolder;
32,759✔
159
    }
160

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

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

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

212
    function idxToValue(idx: number): Unpacked {
213
        if (!idx) return undefined;
318,559✔
214
        if (idx < 0) {
314,896✔
215
            referenced.add(idx);
131,973✔
216
            return stringTable?.get(-idx);
131,973✔
217
        }
218
        const found = getCachedValue(idx);
182,923✔
219
        if (found !== undefined) {
182,923✔
220
            return annotateUnpacked(found, { meta, index: idx });
44,055✔
221
        }
222

223
        const element = data[idx];
138,868✔
224

225
        if (typeof element === 'object') {
138,868✔
226
            // eslint-disable-next-line unicorn/no-null
227
            if (element === null) return null;
60,892✔
228
            if (Array.isArray(element))
60,847✔
229
                return annotateUnpacked(handleArrayElement(idx, element as ArrayBasedElements), {
60,740✔
230
                    meta,
231
                    index: idx,
232
                });
233
            return annotateUnpacked<PrimitiveObject>({}, { meta, index: idx });
107✔
234
        }
235
        return element;
77,976✔
236
    }
237
}
238

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

243
function isArrayElement(value: FlattenedElement): value is ArrayElement {
244
    return Array.isArray(value) && value[0] === ElementType.Array;
14,387✔
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') {
104,902✔
253
        if (Object.hasOwn(value, symbolFlatpackAnnotation)) {
48,660✔
254
            return value as AnnotateUnpacked<T>;
15,699✔
255
        }
256

257
        return Object.defineProperty(value, symbolFlatpackAnnotation, { value: meta }) as AnnotateUnpacked<T>;
32,961✔
258
    }
259
    return value as AnnotateUnpacked<T>;
56,242✔
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