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

streetsidesoftware / cspell / 23445217120

23 Mar 2026 03:25PM UTC coverage: 93.06%. First build
23445217120

Pull #8680

github

web-flow
Merge f867167a4 into 72ff88b0a
Pull Request #8680: fix: make flatpack diff friendly

9631 of 11447 branches covered (84.14%)

1340 of 1373 new or added lines in 18 files covered. (97.6%)

19161 of 20590 relevant lines covered (93.06%)

29921.69 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,067✔
36
    let stringTable: StringTable | undefined;
37

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

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

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

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

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

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

69
    function mergeKeysValues<K>(keys: readonly K[], values: PrimitiveArray): [K, Serializable][] {
70
        return keys.map((key, i) => [key, values[i]]);
54,283✔
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[]);
13,846!
113
        cacheValue(idx, s);
13,846✔
114
        return s;
13,846✔
115
    }
116

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

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

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

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

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

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

168
    function handleArrayElement(
169
        idx: number,
170
        element: ArrayBasedElements,
171
    ): PrimitiveArray | Primitive | PrimitiveObject | PrimitiveSet | PrimitiveMap | undefined {
172
        switch (element[0]) {
53,846✔
173
            case ElementType.Array: {
174
                return toArr(idx, element as ArrayElement);
18,352✔
175
            }
176
            case ElementType.Object: {
177
                return toObj(idx, element as ObjectElement);
13,727✔
178
            }
179
            case ElementType.String: {
180
                return toString(idx, element as StringElement);
13,846✔
181
            }
182
            case ElementType.SubString: {
183
                return handleSubStringElement(idx, element as SubStringElement);
6,793✔
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);
517✔
202
                return idxToValue(idx + 1);
517✔
203
            }
204
        }
NEW
205
        return undefined;
×
206
    }
207

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

212
    function idxToValue(idx: number): Unpacked {
213
        if (!idx) return undefined;
306,901✔
214
        if (idx < 0) {
303,238✔
215
            referenced.add(idx);
138,282✔
216
            return stringTable?.get(-idx);
138,282✔
217
        }
218
        const found = getCachedValue(idx);
164,956✔
219
        if (found !== undefined) {
164,956✔
220
            return annotateUnpacked(found, { meta, index: idx });
39,984✔
221
        }
222

223
        const element = data[idx];
124,972✔
224

225
        if (typeof element === 'object') {
124,972✔
226
            // eslint-disable-next-line unicorn/no-null
227
            if (element === null) return null;
54,004✔
228
            if (Array.isArray(element))
53,963✔
229
                return annotateUnpacked(handleArrayElement(idx, element as ArrayBasedElements), {
53,846✔
230
                    meta,
231
                    index: idx,
232
                });
233
            return annotateUnpacked<PrimitiveObject>({}, { meta, index: idx });
117✔
234
        }
235
        return element;
70,968✔
236
    }
237
}
238

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

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

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