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

LukaJCB / ts-mls / 20979592964

14 Jan 2026 02:00AM UTC coverage: 95.626% (-0.03%) from 95.658%
20979592964

Pull #214

github

web-flow
Merge df6cc819b into 6455ce4ec
Pull Request #214: Rename decodeX to xDecoder

412 of 419 branches covered (98.33%)

Branch coverage included in aggregate %.

165 of 169 new or added lines in 50 files covered. (97.63%)

3 existing lines in 2 files now uncovered.

2277 of 2393 relevant lines covered (95.15%)

73672.72 hits per line

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

96.97
/src/leafNode.ts
1
import { Capabilities, capabilitiesEncoder, capabilitiesDecoder } from "./capabilities.js"
2
import { uint32Decoder, uint32Encoder } from "./codec/number.js"
3
import { Decoder, mapDecoders, flatMapDecoder, mapDecoderOption, mapDecoder } from "./codec/tlsDecoder.js"
4
import { BufferEncoder, contramapBufferEncoders, encode } from "./codec/tlsEncoder.js"
5
import { varLenDataEncoder, varLenDataDecoder, varLenTypeEncoder, varLenTypeDecoder } from "./codec/variableLength.js"
6
import { credentialEncoder, credentialDecoder, Credential } from "./credential.js"
7
import { Signature, signWithLabel, verifyWithLabel } from "./crypto/signature.js"
8
import { Extension, extensionEncoder, extensionDecoder } from "./extension.js"
9
import { leafNodeSources, leafNodeSourceValueDecoder, leafNodeSourceValueEncoder } from "./leafNodeSource.js"
10
import { Lifetime, lifetimeEncoder, lifetimeDecoder } from "./lifetime.js"
11

12
/** @public */
13
export interface LeafNodeData {
14
  hpkePublicKey: Uint8Array
15
  signaturePublicKey: Uint8Array
16
  credential: Credential
17
  capabilities: Capabilities
18
}
19

20
export const leafNodeDataEncoder: BufferEncoder<LeafNodeData> = contramapBufferEncoders(
3✔
21
  [varLenDataEncoder, varLenDataEncoder, credentialEncoder, capabilitiesEncoder],
22
  (data) => [data.hpkePublicKey, data.signaturePublicKey, data.credential, data.capabilities] as const,
62,086✔
23
)
24

25
export const leafNodeDataDecoder: Decoder<LeafNodeData> = mapDecoders(
3✔
26
  [varLenDataDecoder, varLenDataDecoder, credentialDecoder, capabilitiesDecoder],
27
  (hpkePublicKey, signaturePublicKey, credential, capabilities) => ({
4,986✔
28
    hpkePublicKey,
29
    signaturePublicKey,
30
    credential,
31
    capabilities,
32
  }),
33
)
34

35
/** @public */
36
export type LeafNodeInfoOmitted = LeafNodeInfoKeyPackage | LeafNodeInfoUpdateOmitted | LeafNodeInfoCommitOmitted
37

38
/** @public */
39
export interface LeafNodeInfoUpdateOmitted {
40
  leafNodeSource: typeof leafNodeSources.update
41
  extensions: Extension[]
42
}
43

44
/** @public */
45
export interface LeafNodeInfoCommitOmitted {
46
  leafNodeSource: typeof leafNodeSources.commit
47
  parentHash: Uint8Array
48
  extensions: Extension[]
49
}
50

51
/** @public */
52
export interface LeafNodeInfoKeyPackage {
53
  leafNodeSource: typeof leafNodeSources.key_package
54
  lifetime: Lifetime
55
  extensions: Extension[]
56
}
57

58
export const leafNodeInfoKeyPackageEncoder: BufferEncoder<LeafNodeInfoKeyPackage> = contramapBufferEncoders(
3✔
59
  [leafNodeSourceValueEncoder, lifetimeEncoder, varLenTypeEncoder(extensionEncoder)],
60
  (info) => [leafNodeSources.key_package, info.lifetime, info.extensions] as const,
54,691✔
61
)
62

63
export const leafNodeInfoUpdateOmittedEncoder: BufferEncoder<LeafNodeInfoUpdateOmitted> = contramapBufferEncoders(
3✔
64
  [leafNodeSourceValueEncoder, varLenTypeEncoder(extensionEncoder)],
65
  (i) => [i.leafNodeSource, i.extensions] as const,
401✔
66
)
67

68
export const leafNodeInfoCommitOmittedEncoder: BufferEncoder<LeafNodeInfoCommitOmitted> = contramapBufferEncoders(
3✔
69
  [leafNodeSourceValueEncoder, varLenDataEncoder, varLenTypeEncoder(extensionEncoder)],
70
  (info) => [info.leafNodeSource, info.parentHash, info.extensions] as const,
69,175✔
71
)
72

73
export const leafNodeInfoOmittedEncoder: BufferEncoder<LeafNodeInfoOmitted> = (info) => {
3✔
74
  switch (info.leafNodeSource) {
130,621✔
75
    case leafNodeSources.key_package:
76
      return leafNodeInfoKeyPackageEncoder(info)
65,049✔
77
    case leafNodeSources.update:
78
      return leafNodeInfoUpdateOmittedEncoder(info)
387✔
79
    case leafNodeSources.commit:
80
      return leafNodeInfoCommitOmittedEncoder(info)
65,185✔
81
  }
82
}
83

84
export const leafNodeInfoKeyPackageDecoder: Decoder<LeafNodeInfoKeyPackage> = mapDecoders(
3✔
85
  [lifetimeDecoder, varLenTypeDecoder(extensionDecoder)],
86
  (lifetime, extensions) => ({
5,510✔
87
    leafNodeSource: leafNodeSources.key_package,
88
    lifetime,
89
    extensions,
90
  }),
91
)
92

93
export const leafNodeInfoUpdateOmittedDecoder: Decoder<LeafNodeInfoUpdateOmitted> = mapDecoder(
3✔
94
  varLenTypeDecoder(extensionDecoder),
95
  (extensions) => ({
316✔
96
    leafNodeSource: leafNodeSources.update,
97
    extensions,
98
  }),
99
)
100

101
export const leafNodeInfoCommitOmittedDecoder: Decoder<LeafNodeInfoCommitOmitted> = mapDecoders(
3✔
102
  [varLenDataDecoder, varLenTypeDecoder(extensionDecoder)],
103
  (parentHash, extensions) => ({
2,006✔
104
    leafNodeSource: leafNodeSources.commit,
105
    parentHash,
106
    extensions,
107
  }),
108
)
109

110
export const leafNodeInfoOmittedDecoder: Decoder<LeafNodeInfoOmitted> = flatMapDecoder(
3✔
111
  leafNodeSourceValueDecoder,
112
  (leafNodeSource): Decoder<LeafNodeInfoOmitted> => {
113
    switch (leafNodeSource) {
11,213✔
114
      case leafNodeSources.key_package:
115
        return leafNodeInfoKeyPackageDecoder
6,343✔
116
      case leafNodeSources.update:
117
        return leafNodeInfoUpdateOmittedDecoder
316✔
118
      case leafNodeSources.commit:
119
        return leafNodeInfoCommitOmittedDecoder
4,554✔
120
    }
121
  },
122
)
123

124
export type LeafNodeInfo = LeafNodeInfoKeyPackage | LeafNodeInfoUpdate | LeafNodeInfoCommit
125

126
export type LeafNodeInfoUpdate = LeafNodeInfoUpdateOmitted & {
127
  groupId: Uint8Array
128
  leafIndex: number
129
}
130
export type LeafNodeInfoCommit = LeafNodeInfoCommitOmitted & {
131
  groupId: Uint8Array
132
  leafIndex: number
133
}
134

135
export const leafNodeInfoUpdateEncoder: BufferEncoder<LeafNodeInfoUpdate> = contramapBufferEncoders(
3✔
136
  [leafNodeInfoUpdateOmittedEncoder, varLenDataEncoder, uint32Encoder],
137
  (i) => [i, i.groupId, i.leafIndex] as const,
14✔
138
)
139

140
export const leafNodeInfoCommitEncoder: BufferEncoder<LeafNodeInfoCommit> = contramapBufferEncoders(
3✔
141
  [leafNodeInfoCommitOmittedEncoder, varLenDataEncoder, uint32Encoder],
142
  (info) => [info, info.groupId, info.leafIndex] as const,
4,590✔
143
)
144

145
export const leafNodeInfoEncoder: BufferEncoder<LeafNodeInfo> = (info) => {
3✔
146
  switch (info.leafNodeSource) {
14,317✔
147
    case leafNodeSources.key_package:
148
      return leafNodeInfoKeyPackageEncoder(info)
9,713✔
149
    case leafNodeSources.update:
150
      return leafNodeInfoUpdateEncoder(info)
14✔
151
    case leafNodeSources.commit:
152
      return leafNodeInfoCommitEncoder(info)
4,590✔
153
  }
154
}
155

156
export const leafNodeInfoUpdateDecoder: Decoder<LeafNodeInfoUpdate> = mapDecoders(
3✔
157
  [leafNodeInfoUpdateOmittedDecoder, varLenDataDecoder, uint32Decoder],
UNCOV
158
  (ln, groupId, leafIndex) => ({
×
159
    ...ln,
160
    groupId,
161
    leafIndex,
162
  }),
163
)
164

165
export const leafNodeInfoCommitDecoder: Decoder<LeafNodeInfoCommit> = mapDecoders(
3✔
166
  [leafNodeInfoCommitOmittedDecoder, varLenDataDecoder, uint32Decoder],
UNCOV
167
  (ln, groupId, leafIndex) => ({
×
168
    ...ln,
169
    groupId,
170
    leafIndex,
171
  }),
172
)
173

174
export type LeafNodeTBS = LeafNodeData & LeafNodeInfo
175

176
export type LeafNodeTBSCommit = LeafNodeData & LeafNodeInfoCommit
177

178
export type LeafNodeTBSKeyPackage = LeafNodeData & LeafNodeInfoKeyPackage
179

180
export const leafNodeTBSEncoder: BufferEncoder<LeafNodeTBS> = contramapBufferEncoders(
3✔
181
  [leafNodeDataEncoder, leafNodeInfoEncoder],
182
  (tbs) => [tbs, tbs] as const,
2,494✔
183
)
184

185
/** @public */
186
export type LeafNode = LeafNodeData & LeafNodeInfoOmitted & { signature: Uint8Array }
187

188
export const leafNodeEncoder: BufferEncoder<LeafNode> = contramapBufferEncoders(
3✔
189
  [leafNodeDataEncoder, leafNodeInfoOmittedEncoder, varLenDataEncoder],
190
  (leafNode) => [leafNode, leafNode, leafNode.signature] as const,
53,957✔
191
)
192

193
export const leafNodeDecoder: Decoder<LeafNode> = mapDecoders(
3✔
194
  [leafNodeDataDecoder, leafNodeInfoOmittedDecoder, varLenDataDecoder],
195
  (data, info, signature) => ({
3,804✔
196
    ...data,
197
    ...info,
198
    signature,
199
  }),
200
)
201

202
/** @public */
203
export type LeafNodeKeyPackage = LeafNode & { leafNodeSource: typeof leafNodeSources.key_package }
204

205
export const leafNodeKeyPackageDecoder: Decoder<LeafNodeKeyPackage> = mapDecoderOption(leafNodeDecoder, (ln) =>
3✔
206
  ln.leafNodeSource === leafNodeSources.key_package ? ln : undefined,
3,094✔
207
)
208

209
/** @public */
210
export type LeafNodeCommit = LeafNode & { leafNodeSource: typeof leafNodeSources.commit }
211

212
export const leafNodeCommitDecoder: Decoder<LeafNodeCommit> = mapDecoderOption(leafNodeDecoder, (ln) =>
3✔
213
  ln.leafNodeSource === leafNodeSources.commit ? ln : undefined,
2,987✔
214
)
215

216
/** @public */
217
export type LeafNodeUpdate = LeafNode & { leafNodeSource: typeof leafNodeSources.update }
218

219
export const leafNodeUpdateDecoder: Decoder<LeafNodeUpdate> = mapDecoderOption(leafNodeDecoder, (ln) =>
3✔
220
  ln.leafNodeSource === leafNodeSources.update ? ln : undefined,
315✔
221
)
222

223
function toTbs(leafNode: LeafNode, groupId: Uint8Array, leafIndex: number): LeafNodeTBS {
224
  switch (leafNode.leafNodeSource) {
3,584✔
225
    case leafNodeSources.key_package:
226
      return { ...leafNode, leafNodeSource: leafNode.leafNodeSource }
119✔
227
    case leafNodeSources.update:
228
      return { ...leafNode, leafNodeSource: leafNode.leafNodeSource, groupId, leafIndex }
14✔
229
    case leafNodeSources.commit:
230
      return { ...leafNode, leafNodeSource: leafNode.leafNodeSource, groupId, leafIndex }
3,451✔
231
  }
232
}
233

234
export async function signLeafNodeCommit(
235
  tbs: LeafNodeTBSCommit,
236
  signaturePrivateKey: Uint8Array,
237
  sig: Signature,
238
): Promise<LeafNodeCommit> {
239
  return {
1,139✔
240
    ...tbs,
241
    signature: await signWithLabel(signaturePrivateKey, "LeafNodeTBS", encode(leafNodeTBSEncoder, tbs), sig),
242
  }
243
}
244

245
export async function signLeafNodeKeyPackage(
246
  tbs: LeafNodeTBSKeyPackage,
247
  signaturePrivateKey: Uint8Array,
248
  sig: Signature,
249
): Promise<LeafNodeKeyPackage> {
250
  return {
1,216✔
251
    ...tbs,
252
    signature: await signWithLabel(signaturePrivateKey, "LeafNodeTBS", encode(leafNodeTBSEncoder, tbs), sig),
253
  }
254
}
255

256
export function verifyLeafNodeSignature(
257
  leaf: LeafNode,
258
  groupId: Uint8Array,
259
  leafIndex: number,
260
  sig: Signature,
261
): Promise<boolean> {
262
  return verifyWithLabel(
3,584✔
263
    leaf.signaturePublicKey,
264
    "LeafNodeTBS",
265
    encode(leafNodeTBSEncoder, toTbs(leaf, groupId, leafIndex)),
266
    leaf.signature,
267
    sig,
268
  )
269
}
270

271
export function verifyLeafNodeSignatureKeyPackage(leaf: LeafNodeKeyPackage, sig: Signature): Promise<boolean> {
272
  return verifyWithLabel(leaf.signaturePublicKey, "LeafNodeTBS", encode(leafNodeTBSEncoder, leaf), leaf.signature, sig)
5,493✔
273
}
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