@particle/esim-tooling 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.
Files changed (77) hide show
  1. package/README.md +187 -0
  2. package/dist/activation-code.d.ts +11 -0
  3. package/dist/activation-code.d.ts.map +1 -0
  4. package/dist/activation-code.js +56 -0
  5. package/dist/activation-code.js.map +1 -0
  6. package/dist/apdu.d.ts +73 -0
  7. package/dist/apdu.d.ts.map +1 -0
  8. package/dist/apdu.js +357 -0
  9. package/dist/apdu.js.map +1 -0
  10. package/dist/errors.d.ts +32 -0
  11. package/dist/errors.d.ts.map +1 -0
  12. package/dist/errors.js +52 -0
  13. package/dist/errors.js.map +1 -0
  14. package/dist/es10/es10b-notifications.d.ts +30 -0
  15. package/dist/es10/es10b-notifications.d.ts.map +1 -0
  16. package/dist/es10/es10b-notifications.js +294 -0
  17. package/dist/es10/es10b-notifications.js.map +1 -0
  18. package/dist/es10/es10b.d.ts +34 -0
  19. package/dist/es10/es10b.d.ts.map +1 -0
  20. package/dist/es10/es10b.js +108 -0
  21. package/dist/es10/es10b.js.map +1 -0
  22. package/dist/es10/es10c.d.ts +12 -0
  23. package/dist/es10/es10c.d.ts.map +1 -0
  24. package/dist/es10/es10c.js +133 -0
  25. package/dist/es10/es10c.js.map +1 -0
  26. package/dist/es10/iccid.d.ts +9 -0
  27. package/dist/es10/iccid.d.ts.map +1 -0
  28. package/dist/es10/iccid.js +31 -0
  29. package/dist/es10/iccid.js.map +1 -0
  30. package/dist/es10/index.d.ts +5 -0
  31. package/dist/es10/index.d.ts.map +1 -0
  32. package/dist/es10/index.js +4 -0
  33. package/dist/es10/index.js.map +1 -0
  34. package/dist/es10/tags.d.ts +55 -0
  35. package/dist/es10/tags.d.ts.map +1 -0
  36. package/dist/es10/tags.js +63 -0
  37. package/dist/es10/tags.js.map +1 -0
  38. package/dist/es9plus.d.ts +52 -0
  39. package/dist/es9plus.d.ts.map +1 -0
  40. package/dist/es9plus.js +227 -0
  41. package/dist/es9plus.js.map +1 -0
  42. package/dist/gsma-rsp2-root-ci1.d.ts +38 -0
  43. package/dist/gsma-rsp2-root-ci1.d.ts.map +1 -0
  44. package/dist/gsma-rsp2-root-ci1.js +52 -0
  45. package/dist/gsma-rsp2-root-ci1.js.map +1 -0
  46. package/dist/index.d.ts +6 -0
  47. package/dist/index.d.ts.map +1 -0
  48. package/dist/index.js +5 -0
  49. package/dist/index.js.map +1 -0
  50. package/dist/lpa.d.ts +15 -0
  51. package/dist/lpa.d.ts.map +1 -0
  52. package/dist/lpa.js +283 -0
  53. package/dist/lpa.js.map +1 -0
  54. package/dist/tlv.d.ts +14 -0
  55. package/dist/tlv.d.ts.map +1 -0
  56. package/dist/tlv.js +132 -0
  57. package/dist/tlv.js.map +1 -0
  58. package/dist/types.d.ts +230 -0
  59. package/dist/types.d.ts.map +1 -0
  60. package/dist/types.js +108 -0
  61. package/dist/types.js.map +1 -0
  62. package/package.json +50 -0
  63. package/src/activation-code.ts +64 -0
  64. package/src/apdu.ts +419 -0
  65. package/src/errors.ts +69 -0
  66. package/src/es10/es10b-notifications.ts +331 -0
  67. package/src/es10/es10b.ts +163 -0
  68. package/src/es10/es10c.ts +168 -0
  69. package/src/es10/iccid.ts +32 -0
  70. package/src/es10/index.ts +42 -0
  71. package/src/es10/tags.ts +69 -0
  72. package/src/es9plus.ts +331 -0
  73. package/src/gsma-rsp2-root-ci1.ts +53 -0
  74. package/src/index.ts +43 -0
  75. package/src/lpa.ts +346 -0
  76. package/src/tlv.ts +137 -0
  77. package/src/types.ts +264 -0
package/src/apdu.ts ADDED
@@ -0,0 +1,419 @@
1
+ import type { DeviceAdapter } from './types.js';
2
+ import { DeviceError } from './errors.js';
3
+ import { tlvDecode, tlvFind, tlvConcat, type TlvNode } from './tlv.js';
4
+
5
+ // ISD-R AID per SGP.22
6
+ const ISD_R_AID = new Uint8Array([
7
+ 0xa0, 0x00, 0x00, 0x05, 0x59, 0x10, 0x10,
8
+ 0xff, 0xff, 0xff, 0xff, 0x89, 0x00, 0x00, 0x01, 0x00,
9
+ ]);
10
+
11
+ const STORE_DATA_MSS = 255; // max APDU data size per SGP.22
12
+
13
+ // BPP segment tags (children of BF36 BoundProfilePackage)
14
+ const TAG_BPP_INIT_SECURE_CHANNEL = 0xbf23;
15
+ const TAG_BPP_FIRST_SEQ_OF_87 = 0xa0;
16
+ const TAG_BPP_SEQ_OF_88 = 0xa1;
17
+ const TAG_BPP_SECOND_SEQ_OF_87 = 0xa2;
18
+ const TAG_BPP_SEQ_OF_86 = 0xa3;
19
+
20
+ /**
21
+ * APDU transport layer — handles logical channel management, STORE DATA
22
+ * chunking, GET RESPONSE chaining, and BPP segmentation over a DeviceAdapter
23
+ * that sends individual APDUs.
24
+ */
25
+ export class ApduTransport {
26
+ private device: DeviceAdapter;
27
+ private channel = -1;
28
+
29
+ constructor(device: DeviceAdapter) {
30
+ this.device = device;
31
+ }
32
+
33
+ /**
34
+ * Send a complete SGP.22 TLV payload to the eUICC and return the complete
35
+ * TLV response. Handles STORE DATA chunking and GET RESPONSE chaining.
36
+ */
37
+ async sendCommand(data: Uint8Array): Promise<Uint8Array> {
38
+ await this.ensureChannel();
39
+ return this.storeDataAndGetResponse(data);
40
+ }
41
+
42
+ /**
43
+ * Load BPP per SGP.22 Section 2.5.5 Segmented BPP loading.
44
+ *
45
+ * Segmentation order:
46
+ * 1. BF36 header + BF23 (InitialiseSecureChannel)
47
+ * 2. A0 complete (firstSequenceOf87)
48
+ * 3. A1 header only (sequenceOf88)
49
+ * 4. Each 88 TLV individually
50
+ * 5. A2 complete if present (secondSequenceOf87)
51
+ * 6. A3 header only (sequenceOf86)
52
+ * 7. Each 86 TLV individually
53
+ *
54
+ * Critical: P1=0x91 only on the VERY LAST APDU of entire BPP.
55
+ * Block number (P2) resets at each segment boundary.
56
+ */
57
+ async loadBoundProfilePackage(bpp: Uint8Array): Promise<Uint8Array | undefined> {
58
+ await this.ensureChannel();
59
+
60
+ const bppNode = tlvDecode(bpp);
61
+ if (bppNode.tag !== 0xbf36) {
62
+ throw new DeviceError(-1, `Expected BPP tag BF36, got ${bppNode.tag.toString(16)}`);
63
+ }
64
+ if (!bppNode.children || bppNode.children.length === 0) {
65
+ throw new DeviceError(-1, 'BPP has no children');
66
+ }
67
+
68
+ // Extract required children
69
+ const initSecureChannel = tlvFind(bppNode, TAG_BPP_INIT_SECURE_CHANNEL);
70
+ const firstSeq87 = tlvFind(bppNode, TAG_BPP_FIRST_SEQ_OF_87);
71
+ const seq88 = tlvFind(bppNode, TAG_BPP_SEQ_OF_88);
72
+ const secondSeq87 = tlvFind(bppNode, TAG_BPP_SECOND_SEQ_OF_87);
73
+ const seq86 = tlvFind(bppNode, TAG_BPP_SEQ_OF_86);
74
+
75
+ if (!initSecureChannel || !firstSeq87 || !seq88 || !seq86) {
76
+ throw new DeviceError(-1, 'Invalid BPP structure: missing required segments');
77
+ }
78
+
79
+ // Build list of segments per SGP.22 Section 2.5.5
80
+ const segments: Uint8Array[] = [];
81
+
82
+ // Segment 1: BF36 header + BF23 (InitialiseSecureChannel)
83
+ segments.push(this.buildBppSegment1(bppNode, initSecureChannel));
84
+
85
+ // Segment 2: A0 complete (firstSequenceOf87)
86
+ if (firstSeq87.raw) {
87
+ segments.push(firstSeq87.raw);
88
+ }
89
+
90
+ // Segment 3: A1 header only (sequenceOf88)
91
+ segments.push(this.buildSequenceHeader(seq88));
92
+
93
+ // Segment 4-N: Each 88 TLV individually
94
+ if (seq88.children) {
95
+ for (const child of seq88.children) {
96
+ if (child.raw) {
97
+ segments.push(child.raw);
98
+ }
99
+ }
100
+ }
101
+
102
+ // Segment N+1: A2 complete if present (secondSequenceOf87)
103
+ if (secondSeq87?.raw) {
104
+ segments.push(secondSeq87.raw);
105
+ }
106
+
107
+ // Segment N+2: A3 header only (sequenceOf86)
108
+ segments.push(this.buildSequenceHeader(seq86));
109
+
110
+ // Remaining segments: Each 86 TLV individually
111
+ if (seq86.children) {
112
+ for (const child of seq86.children) {
113
+ if (child.raw) {
114
+ segments.push(child.raw);
115
+ }
116
+ }
117
+ }
118
+
119
+ // Send all segments with proper P1 handling
120
+ // Returns ProfileInstallationResult (BF37) from the last APDU
121
+ return await this.sendBppSegments(segments);
122
+ }
123
+
124
+ /**
125
+ * Build Segment 1: BF36 header (tag+length) + complete BF23
126
+ */
127
+ private buildBppSegment1(bppNode: TlvNode, initSecureChannel: TlvNode): Uint8Array {
128
+ if (!bppNode.raw || !initSecureChannel.raw) {
129
+ throw new DeviceError(-1, 'BPP or InitialiseSecureChannel missing raw data');
130
+ }
131
+
132
+ // BF36 header = raw minus value
133
+ const headerSize = bppNode.raw.length - bppNode.value.length;
134
+ const bppHeader = bppNode.raw.slice(0, headerSize);
135
+
136
+ return tlvConcat(bppHeader, initSecureChannel.raw);
137
+ }
138
+
139
+ /**
140
+ * Build sequence header (tag+length only, no children)
141
+ */
142
+ private buildSequenceHeader(node: TlvNode): Uint8Array {
143
+ if (!node.raw) {
144
+ throw new DeviceError(-1, 'Node missing raw data');
145
+ }
146
+ const headerSize = node.raw.length - node.value.length;
147
+ return node.raw.slice(0, headerSize);
148
+ }
149
+
150
+ /**
151
+ * Send BPP segments with correct P1 handling:
152
+ * - P1=0x11 for all APDUs except the very last
153
+ * - P1=0x91 only on the VERY LAST APDU of the entire BPP
154
+ * - P2 resets at each segment boundary
155
+ *
156
+ * Returns the ProfileInstallationResult (BF37) from the last APDU response.
157
+ */
158
+ private async sendBppSegments(segments: Uint8Array[]): Promise<Uint8Array | undefined> {
159
+ const cla = this.gpCla();
160
+ let installationResult: Uint8Array | undefined;
161
+
162
+ for (let segIdx = 0; segIdx < segments.length; segIdx++) {
163
+ const segment = segments[segIdx];
164
+ const isLastSegment = segIdx === segments.length - 1;
165
+ const numChunks = Math.max(1, Math.ceil(segment.length / STORE_DATA_MSS));
166
+
167
+ for (let chunkIdx = 0; chunkIdx < numChunks; chunkIdx++) {
168
+ const offset = chunkIdx * STORE_DATA_MSS;
169
+ const chunkLen = Math.min(STORE_DATA_MSS, segment.length - offset);
170
+ const isLastChunk = chunkIdx === numChunks - 1;
171
+ const isVeryLastApdu = isLastSegment && isLastChunk;
172
+
173
+ const apdu = new Uint8Array(5 + chunkLen);
174
+ apdu[0] = cla;
175
+ apdu[1] = 0xe2; // STORE DATA
176
+ apdu[2] = isVeryLastApdu ? 0x91 : 0x11; // P1: only 0x91 on very last APDU
177
+ apdu[3] = chunkIdx & 0xff; // P2: resets at each segment
178
+ apdu[4] = chunkLen;
179
+ apdu.set(segment.subarray(offset, offset + chunkLen), 5);
180
+
181
+ const resp = await this.device.sendApdu(apdu);
182
+ if (resp.length < 2) {
183
+ throw new DeviceError(-1, `BPP STORE DATA: response too short (segment ${segIdx}, chunk ${chunkIdx})`);
184
+ }
185
+
186
+ let sw1 = resp[resp.length - 2];
187
+ let sw2 = resp[resp.length - 1];
188
+
189
+ // Collect response data for ProfileInstallationResult
190
+ const responseChunks: Uint8Array[] = [];
191
+ const initialData = resp.subarray(0, resp.length - 2);
192
+ if (initialData.length > 0) {
193
+ responseChunks.push(initialData);
194
+ }
195
+
196
+ // Handle SW=61XX (more data available) by draining with GET RESPONSE
197
+ while (sw1 === 0x61) {
198
+ const getResp = await this.device.sendApdu(new Uint8Array([cla, 0xc0, 0x00, 0x00, sw2]));
199
+ if (getResp.length < 2) {
200
+ throw new DeviceError(-1, `BPP GET RESPONSE too short (segment ${segIdx}, chunk ${chunkIdx})`);
201
+ }
202
+
203
+ const respData = getResp.subarray(0, getResp.length - 2);
204
+ if (respData.length > 0) {
205
+ responseChunks.push(respData);
206
+ }
207
+
208
+ sw1 = getResp[getResp.length - 2];
209
+ sw2 = getResp[getResp.length - 1];
210
+ }
211
+
212
+ // Per SGP.22: accept SW 90XX or 91XX
213
+ if (sw1 !== 0x90 && sw1 !== 0x91) {
214
+ throw new DeviceError(-1, `BPP segment ${segIdx} chunk ${chunkIdx} failed: SW=${sw1.toString(16)}${sw2.toString(16)}`);
215
+ }
216
+
217
+ // Collect response data if present
218
+ if (responseChunks.length > 0) {
219
+ const totalLen = responseChunks.reduce((sum, c) => sum + c.length, 0);
220
+ installationResult = new Uint8Array(totalLen);
221
+ let pos = 0;
222
+ for (const chunk of responseChunks) {
223
+ installationResult.set(chunk, pos);
224
+ pos += chunk.length;
225
+ }
226
+
227
+ // If we got a ProfileInstallationResult (BF37), stop sending - eUICC has responded
228
+ // This handles both early errors and final success/failure
229
+ if (this.isBppResult(installationResult)) {
230
+ return installationResult;
231
+ }
232
+ }
233
+ }
234
+ }
235
+
236
+ return installationResult;
237
+ }
238
+
239
+ /**
240
+ * Check if BPP response data contains a ProfileInstallationResult (BF37).
241
+ * Returns true if BF37 is detected (caller should stop sending and return the data).
242
+ */
243
+ private isBppResult(data: Uint8Array): boolean {
244
+ // Look for BF37 tag (ProfileInstallationResult)
245
+ // BF37 = 0xBF 0x37
246
+ if (data.length < 4) return false;
247
+ return data[0] === 0xbf && data[1] === 0x37;
248
+ }
249
+
250
+ /**
251
+ * Close the logical channel if open.
252
+ */
253
+ async close(): Promise<void> {
254
+ if (this.channel >= 0) {
255
+ await this.closeChannel(this.channel);
256
+ this.channel = -1;
257
+ }
258
+ }
259
+
260
+ // ---------------------------------------------------------------------------
261
+ // Logical channel management
262
+ // ---------------------------------------------------------------------------
263
+
264
+ private async ensureChannel(): Promise<void> {
265
+ if (this.channel >= 0) return;
266
+
267
+ let ch = await this.openChannel();
268
+ if (ch < 0) {
269
+ // Stale channels from a previous run — close all and retry
270
+ await this.closeAllChannels();
271
+ ch = await this.openChannel();
272
+ if (ch < 0) {
273
+ throw new DeviceError(-1, 'Failed to open logical channel');
274
+ }
275
+ }
276
+
277
+ await this.selectIsdR(ch);
278
+ this.channel = ch;
279
+ }
280
+
281
+ private async openChannel(): Promise<number> {
282
+ // MANAGE CHANNEL OPEN: 00 70 00 00 01
283
+ const resp = await this.device.sendApdu(new Uint8Array([0x00, 0x70, 0x00, 0x00, 0x01]));
284
+ if (resp.length < 3) return -1;
285
+
286
+ const sw1 = resp[resp.length - 2];
287
+ const sw2 = resp[resp.length - 1];
288
+ if (sw1 !== 0x90 || sw2 !== 0x00) return -1;
289
+
290
+ return resp[0];
291
+ }
292
+
293
+ private async selectIsdR(channel: number): Promise<void> {
294
+ // SELECT: CLA=channel, INS=A4, P1=04, P2=00, Lc=AID_len, Data=AID, Le=00
295
+ const cmd = new Uint8Array(5 + ISD_R_AID.length + 1);
296
+ cmd[0] = channel <= 3 ? (0x00 | channel) : (0x40 | (channel - 4));
297
+ cmd[1] = 0xa4;
298
+ cmd[2] = 0x04;
299
+ cmd[3] = 0x00;
300
+ cmd[4] = ISD_R_AID.length;
301
+ cmd.set(ISD_R_AID, 5);
302
+ cmd[5 + ISD_R_AID.length] = 0x00; // Le
303
+
304
+ const resp = await this.device.sendApdu(cmd);
305
+ if (resp.length < 2) {
306
+ throw new DeviceError(-1, 'SELECT ISD-R: response too short');
307
+ }
308
+
309
+ const sw1 = resp[resp.length - 2];
310
+ if (sw1 !== 0x90 && sw1 !== 0x61) {
311
+ const sw2 = resp[resp.length - 1];
312
+ throw new DeviceError(-1, `SELECT ISD-R failed: SW=${sw1.toString(16)}${sw2.toString(16)}`);
313
+ }
314
+ }
315
+
316
+ private async closeChannel(channel: number): Promise<void> {
317
+ // MANAGE CHANNEL CLOSE
318
+ await this.device.sendApdu(new Uint8Array([0x00, 0x70, 0x80, channel, 0x00]));
319
+ }
320
+
321
+ private async closeAllChannels(): Promise<void> {
322
+ for (let ch = 1; ch <= 3; ch++) {
323
+ try {
324
+ await this.closeChannel(ch);
325
+ } catch {
326
+ // Ignore errors — channel may not be open
327
+ }
328
+ }
329
+ }
330
+
331
+ // ---------------------------------------------------------------------------
332
+ // STORE DATA + GET RESPONSE
333
+ // ---------------------------------------------------------------------------
334
+
335
+ /**
336
+ * GlobalPlatform CLA byte for the current logical channel.
337
+ */
338
+ private gpCla(): number {
339
+ return this.channel <= 3 ? (0x80 | this.channel) : (0xc0 | (this.channel - 4));
340
+ }
341
+
342
+ /**
343
+ * Send data via STORE DATA chunking and collect the response via
344
+ * GET RESPONSE chaining.
345
+ */
346
+ private async storeDataAndGetResponse(data: Uint8Array): Promise<Uint8Array> {
347
+ const cla = this.gpCla();
348
+ const numChunks = Math.max(1, Math.ceil(data.length / STORE_DATA_MSS));
349
+
350
+ let lastResp: Uint8Array = new Uint8Array(0);
351
+
352
+ for (let i = 0; i < numChunks; i++) {
353
+ const offset = i * STORE_DATA_MSS;
354
+ const chunkLen = Math.min(STORE_DATA_MSS, data.length - offset);
355
+ const isLast = i === numChunks - 1;
356
+
357
+ const apdu = new Uint8Array(5 + chunkLen);
358
+ apdu[0] = cla;
359
+ apdu[1] = 0xe2; // STORE DATA
360
+ apdu[2] = isLast ? 0x91 : 0x11; // P1: last or continue
361
+ apdu[3] = i & 0xff; // P2: sequence number
362
+ apdu[4] = chunkLen;
363
+ apdu.set(data.subarray(offset, offset + chunkLen), 5);
364
+
365
+ lastResp = await this.device.sendApdu(apdu);
366
+ if (lastResp.length < 2) {
367
+ throw new DeviceError(-1, 'STORE DATA: response too short');
368
+ }
369
+
370
+ if (!isLast) {
371
+ const sw1 = lastResp[lastResp.length - 2];
372
+ const sw2 = lastResp[lastResp.length - 1];
373
+ if (sw1 !== 0x90 || sw2 !== 0x00) {
374
+ throw new DeviceError(-1, `STORE DATA chunk ${i} failed: SW=${sw1.toString(16)}${sw2.toString(16)}`);
375
+ }
376
+ }
377
+ }
378
+
379
+ // Collect response data from last STORE DATA and GET RESPONSE chains
380
+ const parts: Uint8Array[] = [];
381
+
382
+ // Data from last STORE DATA (excluding SW)
383
+ if (lastResp.length > 2) {
384
+ parts.push(lastResp.subarray(0, lastResp.length - 2));
385
+ }
386
+
387
+ let sw1 = lastResp[lastResp.length - 2];
388
+ let sw2 = lastResp[lastResp.length - 1];
389
+
390
+ // GET RESPONSE chaining
391
+ while (sw1 === 0x61) {
392
+ const getResp = await this.device.sendApdu(new Uint8Array([cla, 0xc0, 0x00, 0x00, sw2]));
393
+ if (getResp.length < 2) {
394
+ throw new DeviceError(-1, 'GET RESPONSE: response too short');
395
+ }
396
+
397
+ if (getResp.length > 2) {
398
+ parts.push(getResp.subarray(0, getResp.length - 2));
399
+ }
400
+
401
+ sw1 = getResp[getResp.length - 2];
402
+ sw2 = getResp[getResp.length - 1];
403
+ }
404
+
405
+ if (sw1 !== 0x90) {
406
+ throw new DeviceError(-1, `Final SW=${sw1.toString(16)}${sw2.toString(16)}`);
407
+ }
408
+
409
+ // Concatenate all response parts
410
+ const totalLen = parts.reduce((sum, p) => sum + p.length, 0);
411
+ const result = new Uint8Array(totalLen);
412
+ let pos = 0;
413
+ for (const part of parts) {
414
+ result.set(part, pos);
415
+ pos += part.length;
416
+ }
417
+ return result;
418
+ }
419
+ }
package/src/errors.ts ADDED
@@ -0,0 +1,69 @@
1
+ export class EsimError extends Error {
2
+ readonly code: string;
3
+
4
+ constructor(code: string, message: string) {
5
+ super(message);
6
+ this.name = 'EsimError';
7
+ this.code = code;
8
+ }
9
+ }
10
+
11
+ export class DeviceError extends EsimError {
12
+ readonly result: number;
13
+
14
+ constructor(result: number, message?: string) {
15
+ super('DEVICE_ERROR', message ?? `Device error: ${result}`);
16
+ this.name = 'DeviceError';
17
+ this.result = result;
18
+ }
19
+ }
20
+
21
+ export class Es10Error extends EsimError {
22
+ readonly resultCode: number;
23
+
24
+ constructor(resultCode: number, message?: string) {
25
+ super('ES10_ERROR', message ?? `eUICC error: ${resultCode}`);
26
+ this.name = 'Es10Error';
27
+ this.resultCode = resultCode;
28
+ }
29
+ }
30
+
31
+ export class Es9PlusError extends EsimError {
32
+ readonly statusCode: number;
33
+ readonly serverStatus?: string;
34
+ readonly subjectCode?: string;
35
+ readonly reasonCode?: string;
36
+
37
+ constructor(params: {
38
+ statusCode: number;
39
+ serverStatus?: string;
40
+ subjectCode?: string;
41
+ reasonCode?: string;
42
+ message?: string;
43
+ }) {
44
+ super(
45
+ 'ES9PLUS_ERROR',
46
+ params.message ??
47
+ `SM-DP+ error: HTTP ${params.statusCode}${params.serverStatus ? ` (${params.serverStatus})` : ''}`,
48
+ );
49
+ this.name = 'Es9PlusError';
50
+ this.statusCode = params.statusCode;
51
+ this.serverStatus = params.serverStatus;
52
+ this.subjectCode = params.subjectCode;
53
+ this.reasonCode = params.reasonCode;
54
+ }
55
+ }
56
+
57
+ export class TlvError extends EsimError {
58
+ constructor(message: string) {
59
+ super('TLV_ERROR', message);
60
+ this.name = 'TlvError';
61
+ }
62
+ }
63
+
64
+ export class ActivationCodeError extends EsimError {
65
+ constructor(message: string) {
66
+ super('ACTIVATION_CODE_ERROR', message);
67
+ this.name = 'ActivationCodeError';
68
+ }
69
+ }