@albertomarturelo/sii-core 0.13.0 → 0.14.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 (41) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/auth/auth.d.ts.map +1 -1
  3. package/dist/auth/auth.js +7 -5
  4. package/dist/auth/auth.js.map +1 -1
  5. package/dist/index.d.ts +2 -2
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +1 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/portal/f22/index.d.ts +1 -0
  10. package/dist/portal/f22/index.d.ts.map +1 -1
  11. package/dist/portal/f22/index.js +1 -0
  12. package/dist/portal/f22/index.js.map +1 -1
  13. package/dist/portal/f22/shared.d.ts +10 -1
  14. package/dist/portal/f22/shared.d.ts.map +1 -1
  15. package/dist/portal/f22/shared.js +13 -11
  16. package/dist/portal/f22/shared.js.map +1 -1
  17. package/dist/portal/f22/terceros.d.ts +58 -0
  18. package/dist/portal/f22/terceros.d.ts.map +1 -0
  19. package/dist/portal/f22/terceros.js +323 -0
  20. package/dist/portal/f22/terceros.js.map +1 -0
  21. package/dist/portal/f29-rfi-form.d.ts +40 -0
  22. package/dist/portal/f29-rfi-form.d.ts.map +1 -0
  23. package/dist/portal/f29-rfi-form.js +627 -0
  24. package/dist/portal/f29-rfi-form.js.map +1 -0
  25. package/dist/portal/f29-rfi.d.ts +13 -0
  26. package/dist/portal/f29-rfi.d.ts.map +1 -0
  27. package/dist/portal/f29-rfi.js +37 -0
  28. package/dist/portal/f29-rfi.js.map +1 -0
  29. package/dist/portal/gwt.d.ts +99 -0
  30. package/dist/portal/gwt.d.ts.map +1 -1
  31. package/dist/portal/gwt.js +618 -2
  32. package/dist/portal/gwt.js.map +1 -1
  33. package/dist/portal/rfi-schema.d.ts +7 -0
  34. package/dist/portal/rfi-schema.d.ts.map +1 -0
  35. package/dist/portal/rfi-schema.js +429 -0
  36. package/dist/portal/rfi-schema.js.map +1 -0
  37. package/dist/tasks/f22.d.ts +38 -2
  38. package/dist/tasks/f22.d.ts.map +1 -1
  39. package/dist/tasks/f22.js +126 -6
  40. package/dist/tasks/f22.js.map +1 -1
  41. package/package.json +1 -1
@@ -1,5 +1,6 @@
1
- // In-house GWT-RPC codec for the SISPAD peticiones read (ADR-020). No third-party GWT
2
- // library — the request template and the response object-graph reader are derived
1
+ // In-house GWT-RPC codecs (ADR-020). Protocol 5 below serves the SISPAD peticiones read;
2
+ // protocol 7 at the end of the file serves rfiInternet, the F29 submit transport (ADR-029
3
+ // amendment). No third-party GWT library — the request template and the response object-graph reader are derived
3
4
  // first-hand from the compiled permutation (strong-name A477…, module sispadinternet,
4
5
  // GWT 2.0.3, observed 2026-07-03); the per-type field schema lives in `gwt-schema.ts`.
5
6
  // Full wire contract: docs/sii-contract/peticiones.md.
@@ -203,4 +204,619 @@ function extractExceptionMessage(body) {
203
204
  }
204
205
  return 'SII rechazó la consulta de peticiones (error de servicio).';
205
206
  }
207
+ const STRING_CLASS = 'java.lang.String';
208
+ // A primitive parameter or array element, by its JVM code.
209
+ const PRIMITIVE_OP = {
210
+ I: 'i',
211
+ Z: 'z',
212
+ S: 'h',
213
+ B: 'i',
214
+ C: 'i',
215
+ J: 'l',
216
+ D: 'd',
217
+ F: 'd',
218
+ };
219
+ const gwtClass = (sig) => sig.split('/')[0];
220
+ // A Java array's element op. A `String[]` element is a bare string reference, not a String
221
+ // object (observed in SdiAAInfoUsuario's response, 2026-10-19); other `[L…;` / `[[…` elements are
222
+ // objects; a primitive array's elements are leaves.
223
+ const arrayElementOp = (cls) => cls === '[Ljava.lang.String;'
224
+ ? 's'
225
+ : cls[1] === 'L' || cls[1] === '['
226
+ ? 'o'
227
+ : PRIMITIVE_OP[cls[1]];
228
+ const GWT7_ROTO = 'No se pudo interpretar el formulario de SII (scraper roto)';
229
+ /** The protocol-7 codec over one set of layouts and one permutation's signatures. `fail` builds
230
+ * the error the caller's surface raises (the F29 facade passes `F29Error`). */
231
+ export class Gwt7Codec {
232
+ layouts;
233
+ sigs;
234
+ fail;
235
+ constructor(layouts, sigs, fail) {
236
+ this.layouts = layouts;
237
+ this.sigs = sigs;
238
+ this.fail = fail;
239
+ }
240
+ /** Build the `text/x-gwt-rpc` body for `call`. A class missing from the layouts or the
241
+ * permutation, or a value that does not fit its layout, throws: nothing is guessed. */
242
+ encodeRequest(call) {
243
+ const table = [];
244
+ const index = new Map();
245
+ const out = [];
246
+ const seen = new Map();
247
+ // In the client, a JS string IS its value, so GWT's identity map treats two String objects
248
+ // with the same text as one object and writes the second as a back-reference (observed:
249
+ // the browser's validation request shares them; without this the layout re-encodes ~11 KB
250
+ // larger). Other objects keep reference identity.
251
+ const seenStrings = new Map();
252
+ let count = 0;
253
+ const ref = (s) => {
254
+ if (s === null)
255
+ return '0';
256
+ let i = index.get(s);
257
+ if (i === undefined) {
258
+ table.push(s);
259
+ i = table.length;
260
+ index.set(s, i);
261
+ }
262
+ return String(i);
263
+ };
264
+ const where = (cls, k) => `${cls}[${k}]`;
265
+ const leaf = (op, v, at) => {
266
+ if (op === 's') {
267
+ if (v !== null && typeof v !== 'string')
268
+ throw this.err(`${at} debe ser string`);
269
+ out.push(ref(v));
270
+ }
271
+ else if (op === 'l') {
272
+ if (typeof v !== 'string')
273
+ throw this.err(`${at} debe ser long (token base64)`);
274
+ out.push(v);
275
+ }
276
+ else if (op === 'i' || op === 'z' || op === 'h' || op === 'd') {
277
+ if (typeof v !== 'number' || !Number.isFinite(v))
278
+ throw this.err(`${at} debe ser número`);
279
+ out.push(String(v));
280
+ }
281
+ else
282
+ throw this.err(`op desconocida ${op}`);
283
+ };
284
+ const object = (v, at) => {
285
+ if (v === null) {
286
+ out.push('0');
287
+ return;
288
+ }
289
+ if (typeof v !== 'object' || Array.isArray(v))
290
+ throw this.err(`${at} debe ser objeto`);
291
+ const text = v.cls === STRING_CLASS && typeof v.values[0] === 'string' ? v.values[0] : null;
292
+ const back = text !== null ? seenStrings.get(text) : seen.get(v);
293
+ if (back !== undefined) {
294
+ out.push(String(-(back + 1)));
295
+ return;
296
+ }
297
+ if (text !== null)
298
+ seenStrings.set(text, count);
299
+ seen.set(v, count++);
300
+ out.push(ref(this.sig(v.cls)));
301
+ if (v.cls.startsWith('[')) {
302
+ out.push(String(v.values.length));
303
+ const op = arrayElementOp(v.cls);
304
+ if (op === undefined)
305
+ throw this.err(`arreglo desconocido ${v.cls}`);
306
+ v.values.forEach((x, k) => op === 'o' ? object(x, where(v.cls, k)) : leaf(op, x, where(v.cls, k)));
307
+ return;
308
+ }
309
+ const ops = this.layout('write', v.cls);
310
+ if (v.values.length !== ops.length) {
311
+ throw this.err(`${v.cls} tiene ${v.values.length} valores; su esquema, ${ops.length}`);
312
+ }
313
+ [...ops].forEach((op, k) => {
314
+ const x = v.values[k];
315
+ const at = where(v.cls, k);
316
+ if (op === 'o')
317
+ object(x, at);
318
+ else if (op === 'L') {
319
+ const list = this.list(x, at);
320
+ out.push(String(list.length));
321
+ list.forEach((item, j) => object(item, `${at}[${j}]`));
322
+ }
323
+ else if (op === 'M') {
324
+ const entries = this.list(x, at);
325
+ out.push(String(entries.length));
326
+ entries.forEach((e, j) => {
327
+ const pair = this.list(e, `${at}{${j}}`);
328
+ if (pair.length !== 2)
329
+ throw this.err(`${at}{${j}} debe ser [clave, valor]`);
330
+ object(pair[0], `${at}{${j}}.k`);
331
+ object(pair[1], `${at}{${j}}.v`);
332
+ });
333
+ }
334
+ else
335
+ leaf(op, x, at);
336
+ });
337
+ };
338
+ for (const s of [call.moduleBase, call.policy, call.service, call.method])
339
+ out.push(ref(s));
340
+ out.push(String(call.params.length));
341
+ for (const p of call.params)
342
+ out.push(ref(PRIMITIVE_OP[p.type] ? p.type : this.sig(p.type)));
343
+ call.params.forEach((p, k) => {
344
+ const at = `${call.method}#${k}`;
345
+ const prim = PRIMITIVE_OP[p.type];
346
+ if (prim)
347
+ leaf(prim, p.value, at);
348
+ else if (p.type === STRING_CLASS)
349
+ leaf('s', p.value, at);
350
+ else
351
+ object(p.value, at);
352
+ });
353
+ return ['7', '0', String(table.length), ...table.map(escapeGwt7), ...out].join('|') + '|';
354
+ }
355
+ /** Parse a protocol-7 request body back into a call (class names, no CRCs). The inverse of
356
+ * `encodeRequest`; used to verify the encoder, and to read a captured call. */
357
+ decodeRequest(text) {
358
+ const tok = splitGwt7Tokens(text);
359
+ if (tok[0] !== '7')
360
+ throw this.err(`protocolo ${String(tok[0])}, se esperaba 7`);
361
+ const n = Number(tok[2]);
362
+ const strings = tok.slice(3, 3 + n);
363
+ let p = 3 + n;
364
+ const seen = [];
365
+ const next = () => {
366
+ if (p >= tok.length)
367
+ throw this.err('fin de datos inesperado');
368
+ return tok[p++];
369
+ };
370
+ const int = () => {
371
+ const t = next();
372
+ if (!/^-?\d+$/.test(t))
373
+ throw this.err(`se esperaba un entero, llegó "${t.slice(0, 12)}"`);
374
+ return Number(t);
375
+ };
376
+ const str = () => {
377
+ const t = int();
378
+ if (t === 0)
379
+ return null;
380
+ const s = strings[t - 1];
381
+ if (s === undefined)
382
+ throw this.err('referencia de string inválida');
383
+ return s;
384
+ };
385
+ const leaf = (op) => {
386
+ if (op === 's')
387
+ return str();
388
+ if (op === 'l')
389
+ return next();
390
+ if (op === 'd') {
391
+ const d = Number(next());
392
+ if (!Number.isFinite(d))
393
+ throw this.err('se esperaba un double');
394
+ return d;
395
+ }
396
+ return int();
397
+ };
398
+ const object = () => {
399
+ const t = int();
400
+ if (t === 0)
401
+ return null;
402
+ if (t < 0) {
403
+ const back = seen[-t - 1];
404
+ if (back === undefined)
405
+ throw this.err('referencia inválida');
406
+ return back;
407
+ }
408
+ const sig = strings[t - 1];
409
+ if (sig === undefined || !isSig(sig))
410
+ throw this.err('se esperaba una firma de tipo');
411
+ const node = { cls: gwtClass(sig), values: [] };
412
+ seen.push(node);
413
+ this.readInto(node, 'write', object, leaf, int);
414
+ return node;
415
+ };
416
+ const head = [str(), str(), str(), str()];
417
+ if (head.some((h) => h === null))
418
+ throw this.err('cabecera incompleta');
419
+ const types = [];
420
+ const count = int();
421
+ for (let k = 0; k < count; k++) {
422
+ const t = str();
423
+ if (t === null)
424
+ throw this.err('tipo de parámetro nulo');
425
+ types.push(PRIMITIVE_OP[t] ? t : gwtClass(t));
426
+ }
427
+ const params = types.map((type) => {
428
+ const prim = PRIMITIVE_OP[type];
429
+ return { type, value: prim ? leaf(prim) : type === STRING_CLASS ? str() : object() };
430
+ });
431
+ if (p !== tok.length)
432
+ throw this.err(`datos sobrantes (${tok.length - p})`);
433
+ const [moduleBase, policy, service, method] = head;
434
+ return { moduleBase, policy, service, method, params };
435
+ }
436
+ /** Decode a `//OK[…]` response into its root value, with the READ layouts. `returns` is the
437
+ * method's DECLARED return type: GWT writes a `java.lang.String` or primitive return bare
438
+ * (`//OK[1,["…"],0,7]`), a `void` (`V`) as nothing (`//OK[[],0,7]`), anything else as an
439
+ * object. A `//EX[…]` is SII refusing the call: its
440
+ * message is raised verbatim (ADR-004). Anything else is "scraper roto"; never retried. */
441
+ decodeResponse(text, returns) {
442
+ const body = text.trimStart();
443
+ if (body.startsWith('//EX'))
444
+ throw this.fail(gwt7ExceptionMessage(body));
445
+ if (!body.startsWith('//OK'))
446
+ throw this.err('la respuesta no es //OK');
447
+ const { payload, strings } = this.envelope(body);
448
+ let p = payload.length - 1;
449
+ const seen = [];
450
+ const tok = () => {
451
+ if (p < 0)
452
+ throw this.err('fin de datos inesperado');
453
+ return payload[p--];
454
+ };
455
+ const int = () => {
456
+ const t = tok();
457
+ if (typeof t !== 'number')
458
+ throw this.err('se esperaba un número');
459
+ return t;
460
+ };
461
+ const leaf = (op) => {
462
+ if (op === 's') {
463
+ const t = int();
464
+ return t > 0 ? (strings[t - 1] ?? null) : null;
465
+ }
466
+ if (op === 'l') {
467
+ const t = tok();
468
+ if (typeof t !== 'string')
469
+ throw this.err('se esperaba un long');
470
+ return t;
471
+ }
472
+ return int();
473
+ };
474
+ const object = () => {
475
+ const t = int();
476
+ if (t === 0)
477
+ return null;
478
+ if (t < 0) {
479
+ const back = seen[-t - 1];
480
+ if (back === undefined)
481
+ throw this.err('referencia inválida');
482
+ return back;
483
+ }
484
+ const sig = strings[t - 1];
485
+ if (sig === undefined || !isSig(sig))
486
+ throw this.err('se esperaba una firma de tipo');
487
+ const node = { cls: gwtClass(sig), values: [] };
488
+ seen.push(node);
489
+ this.readInto(node, 'read', object, leaf, int);
490
+ return node;
491
+ };
492
+ const prim = returns === undefined ? undefined : PRIMITIVE_OP[returns];
493
+ const root = returns === 'V' ? null : prim ? leaf(prim) : returns === STRING_CLASS ? leaf('s') : object();
494
+ if (p !== -1)
495
+ throw this.err('datos sobrantes');
496
+ return root;
497
+ }
498
+ // Shared by both readers: fill `node` per its class (array, or layout ops).
499
+ readInto(node, side, object, leaf, int) {
500
+ if (node.cls.startsWith('[')) {
501
+ const n = int();
502
+ const op = arrayElementOp(node.cls);
503
+ if (op === undefined)
504
+ throw this.err(`arreglo desconocido ${node.cls}`);
505
+ for (let k = 0; k < n; k++)
506
+ node.values.push(op === 'o' ? object() : leaf(op));
507
+ return;
508
+ }
509
+ for (const op of this.layout(side, node.cls)) {
510
+ if (op === 'o')
511
+ node.values.push(object());
512
+ else if (op === 'L') {
513
+ const n = int();
514
+ const list = [];
515
+ for (let k = 0; k < n; k++)
516
+ list.push(object());
517
+ node.values.push(list);
518
+ }
519
+ else if (op === 'M') {
520
+ const n = int();
521
+ const entries = [];
522
+ for (let k = 0; k < n; k++)
523
+ entries.push([object(), object()]);
524
+ node.values.push(entries);
525
+ }
526
+ else
527
+ node.values.push(leaf(op));
528
+ }
529
+ }
530
+ envelope(body) {
531
+ let arr;
532
+ try {
533
+ arr = parseJsLiteral(body.slice(4));
534
+ }
535
+ catch {
536
+ throw this.err('literal inválido');
537
+ }
538
+ if (!Array.isArray(arr) || arr.length < 3 || arr[arr.length - 1] !== 7) {
539
+ throw this.err('sobre de respuesta inesperado');
540
+ }
541
+ const strings = arr[arr.length - 3];
542
+ if (!Array.isArray(strings) || strings.some((s) => typeof s !== 'string')) {
543
+ throw this.err('sin tabla de strings');
544
+ }
545
+ const payload = arr.slice(0, arr.length - 3);
546
+ if (payload.some((t) => typeof t !== 'number' && typeof t !== 'string')) {
547
+ throw this.err('datos con forma inesperada');
548
+ }
549
+ return { payload: payload, strings: strings };
550
+ }
551
+ layout(side, cls) {
552
+ const ops = this.layouts[side][cls];
553
+ if (ops === undefined)
554
+ throw this.err(`tipo sin esquema: ${cls}`);
555
+ return ops;
556
+ }
557
+ sig(cls) {
558
+ const sig = this.sigs[cls];
559
+ if (sig === undefined)
560
+ throw this.err(`tipo sin firma en la permutación: ${cls}`);
561
+ return sig;
562
+ }
563
+ list(v, at) {
564
+ if (!Array.isArray(v))
565
+ throw this.err(`${at} debe ser una lista`);
566
+ return v;
567
+ }
568
+ err(detail) {
569
+ return this.fail(`${GWT7_ROTO} (${detail})`);
570
+ }
571
+ }
572
+ // The client's string escaping, observed in the permutation (the writer's quoting fns): NUL,
573
+ // '|' and '\' always, plus a user-agent-dependent set of ranges written as \uXXXX. The WebKit
574
+ // set reproduces the captured Chromium requests byte for byte; the server reads any \uXXXX.
575
+ const GWT7_ESCAPE = /[\u0000|\\\u0300-\u03ff\u0590-\u05FF\u0600-\u06ff\u0730-\u074A\u07eb-\u07f3\u0940-\u0963\u0980-\u09ff\u0a00-\u0a7f\u0b00-\u0b7f\u0e00-\u0e7f\u0f00-\u0fff\u1900-\u194f\u1a00-\u1a1f\u1b00-\u1b7f\u1cda-\u1cdc\u1dc0-\u1dff\u1f00-\u1fff\u2000-\u206f\u20d0-\u20ff\u2100-\u214f\u2300-\u23ff\u2a00-\u2aff\u3000-\u303f\uaab2-\uaab4\uD800-\uFFFF]/g;
576
+ function escapeGwt7(s) {
577
+ return s.replace(GWT7_ESCAPE, (ch) => {
578
+ const c = ch.charCodeAt(0);
579
+ if (c === 0)
580
+ return '\\0';
581
+ if (c === 0x5c)
582
+ return '\\\\';
583
+ if (c === 0x7c)
584
+ return '\\!';
585
+ return '\\u' + c.toString(16).padStart(4, '0');
586
+ });
587
+ }
588
+ function splitGwt7Tokens(text) {
589
+ const out = [];
590
+ let cur = '';
591
+ for (let i = 0; i < text.length; i++) {
592
+ const ch = text[i];
593
+ if (ch === '\\') {
594
+ const n = text[++i];
595
+ if (n === 'u') {
596
+ cur += String.fromCharCode(parseInt(text.slice(i + 1, i + 5), 16));
597
+ i += 4;
598
+ }
599
+ else
600
+ cur += n === '!' ? '|' : n === '0' ? '\u0000' : (n ?? '');
601
+ }
602
+ else if (ch === '|') {
603
+ out.push(cur);
604
+ cur = '';
605
+ }
606
+ else
607
+ cur += ch;
608
+ }
609
+ if (cur !== '')
610
+ out.push(cur);
611
+ return out;
612
+ }
613
+ /** Parse the JS literal of a protocol-7 response: arrays, numbers, '…' / "…" strings (with
614
+ * \xNN, \uNNNN and the usual escapes), plus GWT's two splits for large payloads: "…"+"…" and
615
+ * […].concat([…],…). Exported for tests. */
616
+ export function parseJsLiteral(src) {
617
+ let i = 0;
618
+ const ws = () => {
619
+ while (i < src.length && /\s/.test(src[i]))
620
+ i++;
621
+ };
622
+ const fail = (what) => {
623
+ throw new Error(`${what} at ${i}`);
624
+ };
625
+ const ESC = {
626
+ n: '\n',
627
+ t: '\t',
628
+ r: '\r',
629
+ b: '\b',
630
+ f: '\f',
631
+ v: '\v',
632
+ '0': '\0',
633
+ };
634
+ const string = () => {
635
+ const q = src[i++];
636
+ let s = '';
637
+ while (src[i] !== q) {
638
+ if (i >= src.length)
639
+ fail('unterminated string');
640
+ if (src[i] === '\\') {
641
+ const n = src[i + 1];
642
+ if (n === 'x') {
643
+ s += String.fromCharCode(parseInt(src.slice(i + 2, i + 4), 16));
644
+ i += 4;
645
+ }
646
+ else if (n === 'u') {
647
+ s += String.fromCharCode(parseInt(src.slice(i + 2, i + 6), 16));
648
+ i += 6;
649
+ }
650
+ else {
651
+ s += ESC[n] ?? n;
652
+ i += 2;
653
+ }
654
+ }
655
+ else
656
+ s += src[i++];
657
+ }
658
+ i++;
659
+ ws();
660
+ if (src[i] === '+') {
661
+ i++;
662
+ ws();
663
+ if (src[i] !== '"' && src[i] !== "'")
664
+ fail('bad string concatenation');
665
+ return s + string();
666
+ }
667
+ return s;
668
+ };
669
+ const value = () => {
670
+ ws();
671
+ const ch = src[i];
672
+ if (ch === '[') {
673
+ i++;
674
+ let arr = [];
675
+ ws();
676
+ if (src[i] === ']')
677
+ i++;
678
+ else {
679
+ for (;;) {
680
+ arr.push(value());
681
+ ws();
682
+ if (src[i] === ',')
683
+ i++;
684
+ else if (src[i] === ']') {
685
+ i++;
686
+ break;
687
+ }
688
+ else
689
+ fail('bad array');
690
+ }
691
+ }
692
+ ws();
693
+ while (src.startsWith('.concat(', i)) {
694
+ i += 8;
695
+ for (;;) {
696
+ const part = value();
697
+ if (!Array.isArray(part))
698
+ fail('concat of a non-array');
699
+ arr = arr.concat(part);
700
+ ws();
701
+ if (src[i] === ',')
702
+ i++;
703
+ else if (src[i] === ')') {
704
+ i++;
705
+ break;
706
+ }
707
+ else
708
+ fail('bad concat');
709
+ }
710
+ ws();
711
+ }
712
+ return arr;
713
+ }
714
+ if (ch === '"' || ch === "'")
715
+ return string();
716
+ const m = /^-?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/.exec(src.slice(i, i + 40));
717
+ if (!m)
718
+ return fail('bad token');
719
+ i += m[0].length;
720
+ return Number(m[0]);
721
+ };
722
+ const v = value();
723
+ ws();
724
+ if (i !== src.length)
725
+ fail('trailing data');
726
+ return v;
727
+ }
728
+ // A protocol-7 `//EX` carries a serialized exception: its human message is in the string table,
729
+ // among the entries that are not type signatures. Joined verbatim (ADR-004).
730
+ function gwt7ExceptionMessage(body) {
731
+ try {
732
+ const arr = parseJsLiteral(body.slice(4));
733
+ const st = Array.isArray(arr) ? arr[arr.length - 3] : undefined;
734
+ if (Array.isArray(st)) {
735
+ const msgs = st.filter((s) => typeof s === 'string' && s.length > 0 && !isSig(s) && /\s/.test(s));
736
+ if (msgs.length)
737
+ return `SII rechazó la operación: ${msgs.join(' | ')}`;
738
+ }
739
+ }
740
+ catch {
741
+ /* fall through */
742
+ }
743
+ return 'SII rechazó la operación (error de servicio).';
744
+ }
745
+ /** True when a protocol-7 response is GWT refusing the request's serialization policy or a
746
+ * type signature: the trigger to re-read the permutation once (self-heal, ADR-020). The server
747
+ * refuses before invoking the method, so nothing ran. */
748
+ export function isGwt7PolicyError(text) {
749
+ const body = text.trimStart();
750
+ return (body.startsWith('//EX') &&
751
+ body.includes('com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException'));
752
+ }
753
+ // Each service proxy is built as `X.call(this, <moduleBase fn>(), '<entryPoint>', '<policy>', …)`
754
+ // and every type signature is a `<var>='<class>/<crc>'` constant (observed in 543D42AD…,
755
+ // 2026-10-19). Reading both from the JS needs no knowledge of the obfuscated names.
756
+ const PROXY_RE = /\.call\(this,[\w$]+\(\),'([\w$]+)','([0-9A-F]{32})'/g;
757
+ const SIG_RE = /[\w$]+='([a-zA-Z[][\w.$;[]*\/\d+)'/g;
758
+ /** Extract the policy strong-names and the type signatures from a permutation's JS. */
759
+ export function parseGwtPermutation(strongName, js) {
760
+ const policies = {};
761
+ for (const m of js.matchAll(PROXY_RE))
762
+ policies[m[1]] ??= m[2];
763
+ const sigs = {};
764
+ for (const m of js.matchAll(SIG_RE))
765
+ sigs[gwtClass(m[1])] ??= m[1];
766
+ return { strongName, policies, sigs };
767
+ }
768
+ const HEX32 = /\b[0-9A-F]{32}\b/g;
769
+ // Process-wide, like the peticiones hash: the permutation belongs to SII's deploy, not to a user.
770
+ const permutations = new Map();
771
+ /** The module's permutation, from the cache or fetched (nocache.js → each listed permutation's
772
+ * `.cache.html`, the first that declares `ref.entryPoint`). Both are authenticated GETs.
773
+ * `refresh` bypasses the cache: the self-heal after a policy refusal. */
774
+ export async function loadGwtPermutation(session, ref, fail, refresh = false) {
775
+ const key = `${ref.moduleBase}|${ref.entryPoint}`;
776
+ const cached = permutations.get(key);
777
+ if (cached && !refresh)
778
+ return cached;
779
+ const nocache = await session.requestText(`${ref.moduleBase}${ref.nocache}`, { method: 'GET' });
780
+ for (const name of new Set(nocache.body.match(HEX32) ?? [])) {
781
+ const js = await session.requestText(`${ref.moduleBase}${name}.cache.html`, { method: 'GET' });
782
+ const perm = parseGwtPermutation(name, js.body);
783
+ if (perm.policies[ref.entryPoint]) {
784
+ permutations.set(key, perm);
785
+ return perm;
786
+ }
787
+ }
788
+ throw fail(`No se encontró la política de serialización de ${ref.entryPoint} en el JS de SII (scraper roto).`);
789
+ }
790
+ /** Forget every cached permutation (tests). */
791
+ export function clearGwtPermutations() {
792
+ permutations.clear();
793
+ }
794
+ /** POST one protocol-7 call and return the raw response text (the caller decodes it). `build`
795
+ * gets the permutation, so the policy and the signatures always come from the same deploy. On
796
+ * a policy/signature refusal the permutation is re-read ONCE and the call rebuilt and resent;
797
+ * `heal: false` turns that off. */
798
+ export async function postGwt7(session, ref, build, fail, opts = {}) {
799
+ const send = async (perm) => {
800
+ const resp = await session.requestText(`${ref.moduleBase}${ref.entryPoint}`, {
801
+ method: 'POST',
802
+ headers: {
803
+ 'Content-Type': 'text/x-gwt-rpc; charset=utf-8',
804
+ 'X-GWT-Module-Base': ref.moduleBase,
805
+ 'X-GWT-Permutation': perm.strongName,
806
+ },
807
+ body: build(perm),
808
+ });
809
+ return resp.body;
810
+ };
811
+ const text = await send(await loadGwtPermutation(session, ref, fail));
812
+ if (opts.heal === false || !isGwt7PolicyError(text))
813
+ return text;
814
+ const healed = await send(await loadGwtPermutation(session, ref, fail, true));
815
+ // Still refused with a fresh permutation: our layouts or the extraction are stale, not SII's
816
+ // deploy — a broken scraper, never a business message to pass on.
817
+ if (isGwt7PolicyError(healed)) {
818
+ throw fail(`No se pudo usar la política de serialización de ${ref.entryPoint} tras releer la permutación de SII (scraper roto).`);
819
+ }
820
+ return healed;
821
+ }
206
822
  //# sourceMappingURL=gwt.js.map