browserscale-ts 1.4.0 → 1.7.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.
- package/README.md +34 -3
- package/dist/auth-session.d.ts +37 -0
- package/dist/auth-session.js +1 -0
- package/dist/browser.d.ts +5 -1
- package/dist/browser.js +5 -0
- package/dist/browserscale.browser.js +1662 -42
- package/dist/client.d.ts +350 -7
- package/dist/client.js +637 -8
- package/dist/dom-mirror.d.ts +271 -0
- package/dist/dom-mirror.js +613 -0
- package/dist/gen/wrc_pb.d.ts +1490 -168
- package/dist/gen/wrc_pb.js +234 -39
- package/dist/index.d.ts +32 -1
- package/dist/index.js +61 -0
- package/dist/internal/convert.d.ts +11 -2
- package/dist/internal/convert.js +84 -1
- package/dist/network-capture.d.ts +84 -0
- package/dist/network-capture.js +107 -0
- package/dist/scripts.d.ts +205 -0
- package/dist/scripts.js +234 -0
- package/dist/types.d.ts +157 -0
- package/dist/ws-transport.d.ts +15 -1
- package/dist/ws-transport.js +155 -10
- package/package.json +1 -1
|
@@ -3799,7 +3799,7 @@ function serviceDesc(file, path, ...paths) {
|
|
|
3799
3799
|
}
|
|
3800
3800
|
|
|
3801
3801
|
// src/gen/wrc_pb.ts
|
|
3802
|
-
var file_wrc = /* @__PURE__ */ fileDesc("Cgl3cmMucHJvdG8SD2Jyb3dzZXJzY2FsZS52MSI7CgRSZWN0EgkKAXgYASABKAESCQoBeRgCIAEoARINCgV3aWR0aBgDIAEoARIOCgZoZWlnaHQYBCABKAEihgIKCUZyYW1lSW5mbxIQCghmcmFtZV9pZBgBIAEoCRILCgN1cmwYAiABKAkSEAoIaXNfb29waWYYAyABKAgSFgoOaGFzX2pzX2NvbnRleHQYBCABKAgSEgoKaXNfbG9hZGluZxgFIAEoCBISCgppc192aXNpYmxlGAYgASgIEiwKDWFic29sdXRlX3JlY3QYByABKAsyFS5icm93c2Vyc2NhbGUudjEuUmVjdBIsCg1yZWxhdGl2ZV9yZWN0GAggASgLMhUuYnJvd3NlcnNjYWxlLnYxLlJlY3QSLAoIY2hpbGRyZW4YCSADKAsyGi5icm93c2Vyc2NhbGUudjEuRnJhbWVJbmZvIqwBCghQYWdlSW5mbxIPCgdwYWdlX2lkGAEgASgJEhoKEmJyb3dzZXJfY29udGV4dF9pZBgCIAEoCRILCgN1cmwYAyABKAkSDQoFdGl0bGUYBCABKAkSJwoIdmlld3BvcnQYBSABKAsyFS5icm93c2Vyc2NhbGUudjEuUmVjdBIuCgpmcmFtZV90cmVlGAYgASgLMhouYnJvd3NlcnNjYWxlLnYxLkZyYW1lSW5mbyIlCgZIZWFkZXISDAoEbmFtZRgBIAEoCRINCgV2YWx1ZRgCIAEoCSKAAQoSSW50ZXJjZXB0ZWRSZXF1ZXN0Eg4KBm1ldGhvZBgBIAEoCRILCgN1cmwYAiABKAkSKAoHaGVhZGVycxgDIAMoCzIXLmJyb3dzZXJzY2FsZS52MS5IZWFkZXISDAoEYm9keRgEIAEoCRIVCg1yZXNvdXJjZV90eXBlGAUgASgJIm8KE0ludGVyY2VwdGVkUmVzcG9uc2USCwoDdXJsGAEgASgJEhMKC3N0YXR1c19jb2RlGAIgASgFEigKB2hlYWRlcnMYAyADKAsyFy5icm93c2Vyc2NhbGUudjEuSGVhZGVyEgwKBGJvZHkYBCABKAkijgEKEkhlYWRlck1vZGlmaWNhdGlvbhIMCgRuYW1lGAEgASgJEhIKBXZhbHVlGAIgASgJSACIAQESDgoGYWN0aW9uGAMgASgJEhMKBmJlZm9yZRgEIAEoCUgBiAEBEhIKBWFmdGVyGAUgASgJSAKIAQFCCAoGX3ZhbHVlQgkKB19iZWZvcmVCCAoGX2FmdGVyIk0KEkNvb2tpZVBhcnRpdGlvbktleRIWCg50b3BfbGV2ZWxfc2l0ZRgBIAEoCRIfChdoYXNfY3Jvc3Nfc2l0ZV9hbmNlc3RvchgCIAEoCCK/AwoLQ29va2llUGFyYW0SDAoEbmFtZRgBIAEoCRINCgV2YWx1ZRgCIAEoCRIQCgN1cmwYAyABKAlIAIgBARIOCgZkb21haW4YBCABKAkSDAoEcGF0aBgFIAEoCRITCgZzZWN1cmUYBiABKAhIAYgBARIWCglodHRwX29ubHkYByABKAhIAogBARIWCglzYW1lX3NpdGUYCCABKAlIA4gBARIUCgdleHBpcmVzGAkgASgBSASIAQESFQoIcHJpb3JpdHkYCiABKAlIBYgBARIaCg1zb3VyY2Vfc2NoZW1lGAsgASgJSAaIAQESGAoLc291cmNlX3BvcnQYDCABKAVIB4gBARI/Cg1wYXJ0aXRpb25fa2V5GA0gASgLMiMuYnJvd3NlcnNjYWxlLnYxLkNvb2tpZVBhcnRpdGlvbktleUgIiAEBQgYKBF91cmxCCQoHX3NlY3VyZUIMCgpfaHR0cF9vbmx5QgwKCl9zYW1lX3NpdGVCCgoIX2V4cGlyZXNCCwoJX3ByaW9yaXR5QhAKDl9zb3VyY2Vfc2NoZW1lQg4KDF9zb3VyY2VfcG9ydEIQCg5fcGFydGl0aW9uX2tleSKtAQoNV2FpdENvbmRpdGlvbhIVCghzZWxlY3RvchgBIAEoCUgAiAEBEhoKDWpzX2V4cHJlc3Npb24YAiABKAlIAYgBARIUCgd2aXNpYmxlGAMgASgISAKIAQESGAoLc3RlYWR5X3RpbWUYBCABKAFIA4gBAUILCglfc2VsZWN0b3JCEAoOX2pzX2V4cHJlc3Npb25CCgoIX3Zpc2libGVCDgoMX3N0ZWFkeV90aW1lIrsBCgpXYWl0UmVzdWx0Eg0KBWluZGV4GAEgASgFEhAKCGZyYW1lX2lkGAIgASgJEhcKD2JhY2tlbmRfbm9kZV9pZBgDIAEoBRISCgppc192aXNpYmxlGAQgASgIEiUKBmJvdW5kcxgFIAEoCzIVLmJyb3dzZXJzY2FsZS52MS5SZWN0Ei4KBWVycm9yGAYgASgLMhouYnJvd3NlcnNjYWxlLnYxLldhaXRFcnJvckgAiAEBQggKBl9lcnJvciLsAQoTV2FpdENvbmRpdGlvblN0YXR1cxINCgVpbmRleBgBIAEoBRINCgVzdGF0ZRgCIAEoCRIXCg9iYWNrZW5kX25vZGVfaWQYAyABKAUSEAoIZnJhbWVfaWQYBCABKAkSEgoKaXNfdmlzaWJsZRgFIAEoCBIqCgZib3VuZHMYBiABKAsyFS5icm93c2Vyc2NhbGUudjEuUmVjdEgAiAEBEjQKCG9jY2x1ZGVyGAcgASgLMh0uYnJvd3NlcnNjYWxlLnYxLk9jY2x1ZGVySW5mb0gBiAEBQgkKB19ib3VuZHNCCwoJX29jY2x1ZGVyImQKCVdhaXRFcnJvchIMCgRjb2RlGAEgASgJEg8KB21lc3NhZ2UYAiABKAkSOAoKY29uZGl0aW9ucxgDIAMoCzIkLmJyb3dzZXJzY2FsZS52MS5XYWl0Q29uZGl0aW9uU3RhdHVzIqYBCg1FbGVtZW50UmVzdWx0Eg8KB3N1Y2Nlc3MYASABKAgSEAoIZnJhbWVfaWQYAiABKAkSFwoPYmFja2VuZF9ub2RlX2lkGAMgASgFEhIKCmlzX3Zpc2libGUYBCABKAgSJQoGYm91bmRzGAUgASgLMhUuYnJvd3NlcnNjYWxlLnYxLlJlY3QSDgoGcm9vdF94GAYgASgBEg4KBnJvb3RfeRgHIAEoASLSAwoMT2NjbHVkZXJJbmZvEhcKD2JhY2tlbmRfbm9kZV9pZBgBIAEoBRIQCghmcmFtZV9pZBgCIAEoCRIQCgh0YWdfbmFtZRgDIAEoCRIPCgJpZBgEIAEoCUgAiAEBEhcKCmNsYXNzX25hbWUYBSABKAlIAYgBARIRCgR0ZXh0GAYgASgJSAKIAQESJQoGYm91bmRzGAcgASgLMhUuYnJvd3NlcnNjYWxlLnYxLlJlY3QSGwoOcG9pbnRlcl9ldmVudHMYCCABKAlIA4gBARIXCgp2aXNpYmlsaXR5GAkgASgJSASIAQESFAoHb3BhY2l0eRgKIAEoAUgFiAEBEhQKB3pfaW5kZXgYCyABKAlIBogBARIlChhoaXR0YWJsZV93aGlsZV9pbnZpc2libGUYDCABKAhIB4gBARIVCghwb3NpdGlvbhgNIAEoCUgIiAEBQgUKA19pZEINCgtfY2xhc3NfbmFtZUIHCgVfdGV4dEIRCg9fcG9pbnRlcl9ldmVudHNCDQoLX3Zpc2liaWxpdHlCCgoIX29wYWNpdHlCCgoIX3pfaW5kZXhCGwoZX2hpdHRhYmxlX3doaWxlX2ludmlzaWJsZUILCglfcG9zaXRpb24ioAEKCkNsaWNrRXJyb3ISDAoEY29kZRgBIAEoCRIPCgdtZXNzYWdlGAIgASgJEjQKCG9jY2x1ZGVyGAMgASgLMh0uYnJvd3NlcnNjYWxlLnYxLk9jY2x1ZGVySW5mb0gAiAEBEhwKD2V2YWRlX2F0dGVtcHRlZBgEIAEoCEgBiAEBQgsKCV9vY2NsdWRlckISChBfZXZhZGVfYXR0ZW1wdGVkIt8BCgtDbGlja1Jlc3VsdBIPCgdzdWNjZXNzGAEgASgIEhAKCGZyYW1lX2lkGAIgASgJEhcKD2JhY2tlbmRfbm9kZV9pZBgDIAEoBRISCgppc192aXNpYmxlGAQgASgIEiUKBmJvdW5kcxgFIAEoCzIVLmJyb3dzZXJzY2FsZS52MS5SZWN0Eg4KBnJvb3RfeBgGIAEoARIOCgZyb290X3kYByABKAESLwoFZXJyb3IYCCABKAsyGy5icm93c2Vyc2NhbGUudjEuQ2xpY2tFcnJvckgAiAEBQggKBl9lcnJvciKeAgoMUmVhY3Rpb25JbmZvEhMKC3JlYWN0aW9uX2lkGAEgASgJEhsKDm1hdGNoX3NlbGVjdG9yGAIgASgJSACIAQESIAoTbWF0Y2hfanNfZXhwcmVzc2lvbhgDIAEoCUgBiAEBEhwKD2FjdGlvbl9zZWxlY3RvchgEIAEoCUgCiAEBEiEKFGFjdGlvbl9qc19leHByZXNzaW9uGAUgASgJSAOIAQESEAoIZnJhbWVfaWQYBiABKAkSDwoHdmlzaWJsZRgHIAEoCEIRCg9fbWF0Y2hfc2VsZWN0b3JCFgoUX21hdGNoX2pzX2V4cHJlc3Npb25CEgoQX2FjdGlvbl9zZWxlY3RvckIXChVfYWN0aW9uX2pzX2V4cHJlc3Npb24i1gMKEkFkZFJlYWN0aW9uUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSDwoHcGFnZV9pZBgDIAEoCRIbCg5tYXRjaF9zZWxlY3RvchgEIAEoCUgAiAEBEiAKE21hdGNoX2pzX2V4cHJlc3Npb24YBSABKAlIAYgBARIVCghmcmFtZV9pZBgGIAEoCUgCiAEBEhQKB3Zpc2libGUYByABKAhIA4gBARIcCg9hY3Rpb25fc2VsZWN0b3IYCCABKAlIBIgBARIhChRhY3Rpb25fanNfZXhwcmVzc2lvbhgJIAEoCUgFiAEBEhMKBmJ1dHRvbhgKIAEoCUgGiAEBEhgKC2NsaWNrX2NvdW50GAsgASgFSAeIAQESFQoIaW50ZXJ2YWwYDCABKAFICIgBAUIRCg9fbWF0Y2hfc2VsZWN0b3JCFgoUX21hdGNoX2pzX2V4cHJlc3Npb25CCwoJX2ZyYW1lX2lkQgoKCF92aXNpYmxlQhIKEF9hY3Rpb25fc2VsZWN0b3JCFwoVX2FjdGlvbl9qc19leHByZXNzaW9uQgkKB19idXR0b25CDgoMX2NsaWNrX2NvdW50QgsKCV9pbnRlcnZhbCIqChNBZGRSZWFjdGlvblJlc3BvbnNlEhMKC3JlYWN0aW9uX2lkGAEgASgJIlEKFVJlbW92ZVJlYWN0aW9uUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSEwoLcmVhY3Rpb25faWQYAyABKAkiKQoWUmVtb3ZlUmVhY3Rpb25SZXNwb25zZRIPCgdyZW1vdmVkGAEgASgIIkwKFExpc3RSZWFjdGlvbnNSZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCRIPCgdwYWdlX2lkGAMgASgJIkkKFUxpc3RSZWFjdGlvbnNSZXNwb25zZRIwCglyZWFjdGlvbnMYASADKAsyHS5icm93c2Vyc2NhbGUudjEuUmVhY3Rpb25JbmZvIukBCgpFbGVtZW50UmVmEhcKD2JhY2tlbmRfbm9kZV9pZBgBIAEoBRIQCgh0YWdfbmFtZRgCIAEoCRIPCgJpZBgDIAEoCUgAiAEBEhEKBG5hbWUYBCABKAlIAYgBARIXCgpjbGFzc19uYW1lGAUgASgJSAKIAQESFwoKaW5wdXRfdHlwZRgGIAEoCUgDiAEBEhEKBHRleHQYByABKAlIBIgBARIQCghlZGl0YWJsZRgIIAEoCEIFCgNfaWRCBwoFX25hbWVCDQoLX2NsYXNzX25hbWVCDQoLX2lucHV0X3R5cGVCBwoFX3RleHQi7gIKCUZpbGxFcnJvchIMCgRjb2RlGAEgASgJEg8KB21lc3NhZ2UYAiABKAkSNQoLY2xpY2tfZXJyb3IYAyABKAsyGy5icm93c2Vyc2NhbGUudjEuQ2xpY2tFcnJvckgAiAEBEiQKF2ZvY3VzZWRfYmFja2VuZF9ub2RlX2lkGAQgASgFSAGIAQESOQoPZm9jdXNlZF9lbGVtZW50GAUgASgLMhsuYnJvd3NlcnNjYWxlLnYxLkVsZW1lbnRSZWZIAogBARIcCg90YXJnZXRfZWRpdGFibGUYBiABKAhIA4gBARIgChN0YXJnZXRfdmFsdWVfbGVuZ3RoGAcgASgFSASIAQFCDgoMX2NsaWNrX2Vycm9yQhoKGF9mb2N1c2VkX2JhY2tlbmRfbm9kZV9pZEISChBfZm9jdXNlZF9lbGVtZW50QhIKEF90YXJnZXRfZWRpdGFibGVCFgoUX3RhcmdldF92YWx1ZV9sZW5ndGgiogEKCkZpbGxSZXN1bHQSDwoHc3VjY2VzcxgBIAEoCBIQCghmcmFtZV9pZBgCIAEoCRIXCg9iYWNrZW5kX25vZGVfaWQYAyABKAUSDgoGcm9vdF94GAYgASgBEg4KBnJvb3RfeRgHIAEoARIuCgVlcnJvchgIIAEoCzIaLmJyb3dzZXJzY2FsZS52MS5GaWxsRXJyb3JIAIgBAUIICgZfZXJyb3IiwgEKCkRyYWdSZXN1bHQSDwoHc3VjY2VzcxgBIAEoCBIQCghmcmFtZV9pZBgCIAEoCRIXCg9iYWNrZW5kX25vZGVfaWQYAyABKAUSDwoHc3RhcnRfeBgEIAEoARIPCgdzdGFydF95GAUgASgBEg0KBWVuZF94GAYgASgBEg0KBWVuZF95GAcgASgBEi4KBWVycm9yGAggASgLMhouYnJvd3NlcnNjYWxlLnYxLkRyYWdFcnJvckgAiAEBQggKBl9lcnJvciJxCglEcmFnRXJyb3ISDAoEY29kZRgBIAEoCRIPCgdtZXNzYWdlGAIgASgJEjUKC2NsaWNrX2Vycm9yGAMgASgLMhsuYnJvd3NlcnNjYWxlLnYxLkNsaWNrRXJyb3JIAIgBAUIOCgxfY2xpY2tfZXJyb3IirgEKElNlbGVjdE9wdGlvblJlc3VsdBIWCg5zZWxlY3RlZF9pbmRleBgBIAEoBRIWCg5zZWxlY3RlZF92YWx1ZRgCIAEoCRIVCg1zZWxlY3RlZF90ZXh0GAMgASgJEg8KB3N1Y2Nlc3MYBCABKAgSNgoFZXJyb3IYBSABKAsyIi5icm93c2Vyc2NhbGUudjEuU2VsZWN0T3B0aW9uRXJyb3JIAIgBAUIICgZfZXJyb3IiMgoRU2VsZWN0T3B0aW9uRXJyb3ISDAoEY29kZRgBIAEoCRIPCgdtZXNzYWdlGAIgASgJIsEBCgxTY3JvbGxSZXN1bHQSDwoHc3VjY2VzcxgBIAEoCBIQCghmcmFtZV9pZBgCIAEoCRIXCg9iYWNrZW5kX25vZGVfaWQYAyABKAUSEgoKaXNfdmlzaWJsZRgEIAEoCBIlCgZib3VuZHMYBSABKAsyFS5icm93c2Vyc2NhbGUudjEuUmVjdBIwCgVlcnJvchgIIAEoCzIcLmJyb3dzZXJzY2FsZS52MS5TY3JvbGxFcnJvckgAiAEBQggKBl9lcnJvciIsCgtTY3JvbGxFcnJvchIMCgRjb2RlGAEgASgJEg8KB21lc3NhZ2UYAiABKAki3QEKCk1vdmVSZXN1bHQSDwoHc3VjY2VzcxgBIAEoCBIQCghmcmFtZV9pZBgCIAEoCRIXCg9iYWNrZW5kX25vZGVfaWQYAyABKAUSEgoKaXNfdmlzaWJsZRgEIAEoCBIlCgZib3VuZHMYBSABKAsyFS5icm93c2Vyc2NhbGUudjEuUmVjdBIOCgZyb290X3gYBiABKAESDgoGcm9vdF95GAcgASgBEi4KBWVycm9yGAggASgLMhouYnJvd3NlcnNjYWxlLnYxLk1vdmVFcnJvckgAiAEBQggKBl9lcnJvciIqCglNb3ZlRXJyb3ISDAoEY29kZRgBIAEoCRIPCgdtZXNzYWdlGAIgASgJIuYBCg9TZXRQcm94eVJlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCRIPCgdhcGlfa2V5GAIgASgJEhcKCnByb3h5X2hvc3QYAyABKAlIAIgBARIXCgpwcm94eV9wb3J0GAQgASgFSAGIAQESGwoOcHJveHlfdXNlcm5hbWUYBSABKAlIAogBARIbCg5wcm94eV9wYXNzd29yZBgGIAEoCUgDiAEBQg0KC19wcm94eV9ob3N0Qg0KC19wcm94eV9wb3J0QhEKD19wcm94eV91c2VybmFtZUIRCg9fcHJveHlfcGFzc3dvcmQiNgoPR2V0UGFnZXNSZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCSI8ChBHZXRQYWdlc1Jlc3BvbnNlEigKBXBhZ2VzGAEgAygLMhkuYnJvd3NlcnNjYWxlLnYxLlBhZ2VJbmZvIpoBCg9OYXZpZ2F0ZVJlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCRIPCgdhcGlfa2V5GAIgASgJEg8KB3BhZ2VfaWQYAyABKAkSCwoDdXJsGAQgASgJEhUKCHJlZmVycmVyGAUgASgJSACIAQESFAoHdGltZW91dBgGIAEoAUgBiAEBQgsKCV9yZWZlcnJlckIKCghfdGltZW91dCIxChBOYXZpZ2F0ZVJlc3BvbnNlEhAKCGZyYW1lX2lkGAEgASgJEgsKA3VybBgCIAEoCSK2AQoPTG9hZEhUTUxSZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCRIPCgdwYWdlX2lkGAMgASgJEgsKA3VybBgEIAEoCRIMCgRodG1sGAUgASgJEigKB2hlYWRlcnMYBiADKAsyFy5icm93c2Vyc2NhbGUudjEuSGVhZGVyEhgKC3N0YXR1c19jb2RlGAcgASgFSACIAQFCDgoMX3N0YXR1c19jb2RlIn8KD0V2YWx1YXRlUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSDwoHcGFnZV9pZBgDIAEoCRISCgpleHByZXNzaW9uGAQgASgJEhUKCGZyYW1lX2lkGAUgASgJSACIAQFCCwoJX2ZyYW1lX2lkInYKEEV2YWx1YXRlUmVzcG9uc2USDgoGcmVzdWx0GAEgASgJEhcKD2JhY2tlbmRfbm9kZV9pZBgCIAEoBRISCgppc192aXNpYmxlGAMgASgIEiUKBmJvdW5kcxgEIAEoCzIVLmJyb3dzZXJzY2FsZS52MS5SZWN0ImMKClJ1blJlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCRIPCgdhcGlfa2V5GAIgASgJEg4KBnNvdXJjZRgDIAEoCRIUCgdwYWdlX2lkGAQgASgJSACIAQFCCgoIX3BhZ2VfaWQiLgoLUnVuUmVzcG9uc2USDwoHc3VjY2VzcxgBIAEoCBIOCgZyZXN1bHQYAiABKAkiwgEKEFdhaXRGb3JBbnlQYXJhbXMSEgoKc2Vzc2lvbl9pZBgBIAEoCRIPCgdhcGlfa2V5GAIgASgJEg8KB3BhZ2VfaWQYAyABKAkSMgoKY29uZGl0aW9ucxgEIAMoCzIeLmJyb3dzZXJzY2FsZS52MS5XYWl0Q29uZGl0aW9uEhUKCGZyYW1lX2lkGAUgASgJSACIAQESFAoHdGltZW91dBgGIAEoAUgBiAEBQgsKCV9mcmFtZV9pZEIKCghfdGltZW91dCL1AgoTU2VsZWN0T3B0aW9uUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSDwoHcGFnZV9pZBgDIAEoCRIcCg9iYWNrZW5kX25vZGVfaWQYBCABKAVIAIgBARIVCghzZWxlY3RvchgFIAEoCUgBiAEBEhoKDWpzX2V4cHJlc3Npb24YBiABKAlIAogBARIVCghmcmFtZV9pZBgHIAEoCUgDiAEBEhIKBWluZGV4GAggASgFSASIAQESEgoFdmFsdWUYCSABKAlIBYgBARIRCgR0ZXh0GAogASgJSAaIAQESGAoLZmlyZV9ldmVudHMYCyABKAhIB4gBAUISChBfYmFja2VuZF9ub2RlX2lkQgsKCV9zZWxlY3RvckIQCg5fanNfZXhwcmVzc2lvbkILCglfZnJhbWVfaWRCCAoGX2luZGV4QggKBl92YWx1ZUIHCgVfdGV4dEIOCgxfZmlyZV9ldmVudHMi7wEKD1Njcm9sbFRvUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSDwoHcGFnZV9pZBgDIAEoCRIcCg9iYWNrZW5kX25vZGVfaWQYBCABKAVIAIgBARIVCghzZWxlY3RvchgFIAEoCUgBiAEBEhoKDWpzX2V4cHJlc3Npb24YBiABKAlIAogBARIVCghmcmFtZV9pZBgHIAEoCUgDiAEBQhIKEF9iYWNrZW5kX25vZGVfaWRCCwoJX3NlbGVjdG9yQhAKDl9qc19leHByZXNzaW9uQgsKCV9mcmFtZV9pZCKZAgoNTW92ZVRvUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSDwoHcGFnZV9pZBgDIAEoCRIcCg9iYWNrZW5kX25vZGVfaWQYBCABKAVIAIgBARIVCghzZWxlY3RvchgFIAEoCUgBiAEBEhoKDWpzX2V4cHJlc3Npb24YBiABKAlIAogBARIVCghmcmFtZV9pZBgHIAEoCUgDiAEBEg4KAXgYCCABKAFIBIgBARIOCgF5GAkgASgBSAWIAQFCEgoQX2JhY2tlbmRfbm9kZV9pZEILCglfc2VsZWN0b3JCEAoOX2pzX2V4cHJlc3Npb25CCwoJX2ZyYW1lX2lkQgQKAl94QgQKAl95IoIDCgxDbGlja1JlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCRIPCgdhcGlfa2V5GAIgASgJEg8KB3BhZ2VfaWQYAyABKAkSHAoPYmFja2VuZF9ub2RlX2lkGAQgASgFSACIAQESFQoIc2VsZWN0b3IYBSABKAlIAYgBARIaCg1qc19leHByZXNzaW9uGAYgASgJSAKIAQESFQoIZnJhbWVfaWQYByABKAlIA4gBARITCgZidXR0b24YCCABKAlIBIgBARIYCgtjbGlja19jb3VudBgJIAEoBUgFiAEBEg4KAXgYCiABKAFIBogBARIOCgF5GAsgASgBSAeIAQESEwoGYWN0aW9uGAwgASgJSAiIAQFCEgoQX2JhY2tlbmRfbm9kZV9pZEILCglfc2VsZWN0b3JCEAoOX2pzX2V4cHJlc3Npb25CCwoJX2ZyYW1lX2lkQgkKB19idXR0b25CDgoMX2NsaWNrX2NvdW50QgQKAl94QgQKAl95QgkKB19hY3Rpb24igwMKC0RyYWdSZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCRIPCgdwYWdlX2lkGAMgASgJEhwKD2JhY2tlbmRfbm9kZV9pZBgEIAEoBUgAiAEBEhUKCHNlbGVjdG9yGAUgASgJSAGIAQESGgoNanNfZXhwcmVzc2lvbhgGIAEoCUgCiAEBEhUKCGZyYW1lX2lkGAcgASgJSAOIAQESFQoIb2Zmc2V0X3gYCCABKAFIBIgBARIVCghvZmZzZXRfeRgJIAEoAUgFiAEBEhcKCmFic29sdXRlX3gYCiABKAFIBogBARIXCgphYnNvbHV0ZV95GAsgASgBSAeIAQFCEgoQX2JhY2tlbmRfbm9kZV9pZEILCglfc2VsZWN0b3JCEAoOX2pzX2V4cHJlc3Npb25CCwoJX2ZyYW1lX2lkQgsKCV9vZmZzZXRfeEILCglfb2Zmc2V0X3lCDQoLX2Fic29sdXRlX3hCDQoLX2Fic29sdXRlX3ki7wIKC0ZpbGxSZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCRIPCgdwYWdlX2lkGAMgASgJEhwKD2JhY2tlbmRfbm9kZV9pZBgEIAEoBUgAiAEBEhUKCHNlbGVjdG9yGAUgASgJSAGIAQESGgoNanNfZXhwcmVzc2lvbhgGIAEoCUgCiAEBEhUKCGZyYW1lX2lkGAcgASgJSAOIAQESDAoEdGV4dBgIIAEoCRIYCgtjbGVhcl9maXJzdBgJIAEoCEgEiAEBEhQKB3RpbWVvdXQYCiABKAFIBYgBARIYCgtzdGVhZHlfdGltZRgLIAEoAUgGiAEBQhIKEF9iYWNrZW5kX25vZGVfaWRCCwoJX3NlbGVjdG9yQhAKDl9qc19leHByZXNzaW9uQgsKCV9mcmFtZV9pZEIOCgxfY2xlYXJfZmlyc3RCCgoIX3RpbWVvdXRCDgoMX3N0ZWFkeV90aW1lIkwKE1NldEJsb2NrTGlzdFJlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCRIPCgdhcGlfa2V5GAIgASgJEhAKCHBhdHRlcm5zGAMgAygJImEKFVNldFN0YXRpY1BhdGhzUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSEAoIcGF0dGVybnMYAyADKAkSEQoJYmxvYl9uYW1lGAQgASgJIogBChhXYWl0Rm9yQW55UmVxdWVzdFJlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCRIPCgdhcGlfa2V5GAIgASgJEhAKCHBhdHRlcm5zGAMgAygJEhMKC2Fib3J0X2ZsYWdzGAQgAygFEhQKB3RpbWVvdXQYBSABKAFIAIgBAUIKCghfdGltZW91dCJgChlXYWl0Rm9yQW55UmVxdWVzdFJlc3BvbnNlEg0KBWluZGV4GAEgASgFEjQKB3JlcXVlc3QYAiABKAsyIy5icm93c2Vyc2NhbGUudjEuSW50ZXJjZXB0ZWRSZXF1ZXN0IokBChlXYWl0Rm9yQW55UmVzcG9uc2VSZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCRIQCghwYXR0ZXJucxgDIAMoCRITCgthYm9ydF9mbGFncxgEIAMoBRIUCgd0aW1lb3V0GAUgASgBSACIAQFCCgoIX3RpbWVvdXQiYwoaV2FpdEZvckFueVJlc3BvbnNlUmVzcG9uc2USDQoFaW5kZXgYASABKAUSNgoIcmVzcG9uc2UYAiABKAsyJC5icm93c2Vyc2NhbGUudjEuSW50ZXJjZXB0ZWRSZXNwb25zZSLKAQoUTW9kaWZ5UmVxdWVzdFJlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCRIPCgdhcGlfa2V5GAIgASgJEhMKC3VybF9wYXR0ZXJuGAMgASgJEjoKDW1vZGlmaWNhdGlvbnMYBCADKAsyIy5icm93c2Vyc2NhbGUudjEuSGVhZGVyTW9kaWZpY2F0aW9uEhEKBGJvZHkYBSABKAlIAIgBARIUCgd0aW1lb3V0GAYgASgBSAGIAQFCBwoFX2JvZHlCCgoIX3RpbWVvdXQiTQoVTW9kaWZ5UmVxdWVzdFJlc3BvbnNlEjQKB3JlcXVlc3QYASABKAsyIy5icm93c2Vyc2NhbGUudjEuSW50ZXJjZXB0ZWRSZXF1ZXN0IjgKEUdldENvb2tpZXNSZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCSJDChJHZXRDb29raWVzUmVzcG9uc2USLQoHY29va2llcxgBIAMoCzIcLmJyb3dzZXJzY2FsZS52MS5Db29raWVQYXJhbSJnChFTZXRDb29raWVzUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSLQoHY29va2llcxgDIAMoCzIcLmJyb3dzZXJzY2FsZS52MS5Db29raWVQYXJhbSI6ChNDbGVhckNvb2tpZXNSZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCSIpCgtTdG9yYWdlSXRlbRILCgNrZXkYASABKAkSDQoFdmFsdWUYAiABKAkiUQoSU3RvcmFnZU9yaWdpbkVudHJ5Eg4KBm9yaWdpbhgBIAEoCRIrCgVpdGVtcxgCIAMoCzIcLmJyb3dzZXJzY2FsZS52MS5TdG9yYWdlSXRlbSJYChFHZXRTdG9yYWdlUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSEwoGb3JpZ2luGAMgASgJSACIAQFCCQoHX29yaWdpbiJKChJHZXRTdG9yYWdlUmVzcG9uc2USNAoHc3RvcmFnZRgBIAMoCzIjLmJyb3dzZXJzY2FsZS52MS5TdG9yYWdlT3JpZ2luRW50cnkimAEKEVNldFN0b3JhZ2VSZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCRI0CgdzdG9yYWdlGAMgAygLMiMuYnJvd3NlcnNjYWxlLnYxLlN0b3JhZ2VPcmlnaW5FbnRyeRIYCgtjbGVhcl9maXJzdBgEIAEoCEgAiAEBQg4KDF9jbGVhcl9maXJzdCJaChNDbGVhclN0b3JhZ2VSZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCRITCgZvcmlnaW4YAyABKAlIAIgBAUIJCgdfb3JpZ2luIocBCg1HZXRET01SZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCRIPCgdwYWdlX2lkGAMgASgJEhUKCGZyYW1lX2lkGAQgASgJSACIAQESEgoFZGVwdGgYBSABKAVIAYgBAUILCglfZnJhbWVfaWRCCAoGX2RlcHRoIh0KDkdldERPTVJlc3BvbnNlEgsKA2RvbRgBIAEoCSKZBAoVR2V0T2JzZXJ2YXRpb25SZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCRIPCgdwYWdlX2lkGAMgASgJEiMKFm1heF9lbGVtZW50c19wZXJfZnJhbWUYBCABKAVIAIgBARIcCg9tYXhfdGV4dF9sZW5ndGgYBSABKAVIAYgBARITCgZmb3JtYXQYBiABKAlIAogBARIdChBtYXhfdG90YWxfdG9rZW5zGAcgASgFSAOIAQESGwoOaW5jbHVkZV9ib3VuZHMYCCABKAhIBIgBARIaCg12aWV3cG9ydF9vbmx5GAkgASgISAWIAQESHAoPYmFja2VuZF9ub2RlX2lkGAogASgFSAaIAQESFQoIc2VsZWN0b3IYCyABKAlIB4gBARIaCg1qc19leHByZXNzaW9uGAwgASgJSAiIAQESFQoIZnJhbWVfaWQYDSABKAlICYgBAUIZChdfbWF4X2VsZW1lbnRzX3Blcl9mcmFtZUISChBfbWF4X3RleHRfbGVuZ3RoQgkKB19mb3JtYXRCEwoRX21heF90b3RhbF90b2tlbnNCEQoPX2luY2x1ZGVfYm91bmRzQhAKDl92aWV3cG9ydF9vbmx5QhIKEF9iYWNrZW5kX25vZGVfaWRCCwoJX3NlbGVjdG9yQhAKDl9qc19leHByZXNzaW9uQgsKCV9mcmFtZV9pZCJFChZHZXRPYnNlcnZhdGlvblJlc3BvbnNlEhMKC29ic2VydmF0aW9uGAEgASgJSgQIAhADUhBvYnNlcnZhdGlvbl9qc29uIm0KEUdldERPTUhhc2hSZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCRIPCgdwYWdlX2lkGAMgASgJEhUKCGZyYW1lX2lkGAQgASgJSACIAQFCCwoJX2ZyYW1lX2lkIiIKEkdldERPTUhhc2hSZXNwb25zZRIMCgRoYXNoGAEgASgJImYKGEluc3BlY3RBdFBvc2l0aW9uUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSDwoHcGFnZV9pZBgDIAEoCRIJCgF4GAQgASgBEgkKAXkYBSABKAEiqQEKGUluc3BlY3RBdFBvc2l0aW9uUmVzcG9uc2USFwoPYmFja2VuZF9ub2RlX2lkGAEgASgFEhAKCGZyYW1lX2lkGAIgASgJEhAKCHRhZ19uYW1lGAMgASgJEhQKDHRleHRfY29udGVudBgEIAEoCRISCgppc192aXNpYmxlGAUgASgIEiUKBmJvdW5kcxgGIAEoCzIVLmJyb3dzZXJzY2FsZS52MS5SZWN0IokBChRIaWdobGlnaHROb2RlUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSDwoHcGFnZV9pZBgDIAEoCRIXCg9iYWNrZW5kX25vZGVfaWQYBCABKAUSFQoIZnJhbWVfaWQYBSABKAlIAIgBAUILCglfZnJhbWVfaWQiVwoRSW5zZXJ0VGV4dFJlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCRIPCgdhcGlfa2V5GAIgASgJEg8KB3BhZ2VfaWQYAyABKAkSDAoEdGV4dBgEIAEoCSJ7CgtUeXBlUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSDwoHcGFnZV9pZBgDIAEoCRIMCgR0ZXh0GAQgASgJEhgKC2NsZWFyX2ZpcnN0GAUgASgISACIAQFCDgoMX2NsZWFyX2ZpcnN0IroBCg9QcmVzc0tleVJlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCRIPCgdhcGlfa2V5GAIgASgJEg8KB3BhZ2VfaWQYAyABKAkSCwoDa2V5GAQgASgJEhEKBGNvZGUYBSABKAlIAIgBARIWCgltb2RpZmllcnMYBiABKAVIAYgBARIVCghsb2NhdGlvbhgHIAEoBUgCiAEBQgcKBV9jb2RlQgwKCl9tb2RpZmllcnNCCwoJX2xvY2F0aW9uIrwBChFSZWxlYXNlS2V5UmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSDwoHcGFnZV9pZBgDIAEoCRILCgNrZXkYBCABKAkSEQoEY29kZRgFIAEoCUgAiAEBEhYKCW1vZGlmaWVycxgGIAEoBUgBiAEBEhUKCGxvY2F0aW9uGAcgASgFSAKIAQFCBwoFX2NvZGVCDAoKX21vZGlmaWVyc0ILCglfbG9jYXRpb24iSwoTR2V0U2VsZWN0aW9uUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSDwoHcGFnZV9pZBgDIAEoCSIkChRHZXRTZWxlY3Rpb25SZXNwb25zZRIMCgR0ZXh0GAEgASgJIosBChFTY3JlZW5zaG90UmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSDwoHcGFnZV9pZBgDIAEoCRITCgZmb3JtYXQYBCABKAlIAIgBARIUCgdxdWFsaXR5GAUgASgFSAGIAQFCCQoHX2Zvcm1hdEIKCghfcXVhbGl0eSJIChJTY3JlZW5zaG90UmVzcG9uc2USEwoLZGF0YV9iYXNlNjQYASABKAkSDQoFd2lkdGgYAiABKAUSDgoGaGVpZ2h0GAMgASgFIpMDChFSZWFkQ2FudmFzUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSDwoHcGFnZV9pZBgDIAEoCRIcCg9iYWNrZW5kX25vZGVfaWQYBCABKAVIAIgBARIVCghzZWxlY3RvchgFIAEoCUgBiAEBEhoKDWpzX2V4cHJlc3Npb24YBiABKAlIAogBARIVCghmcmFtZV9pZBgHIAEoCUgDiAEBEhMKBmZvcm1hdBgIIAEoCUgEiAEBEhQKB3F1YWxpdHkYCSABKAVIBYgBARIPCgJzeBgKIAEoBUgGiAEBEg8KAnN5GAsgASgFSAeIAQESDwoCc3cYDCABKAVICIgBARIPCgJzaBgNIAEoBUgJiAEBQhIKEF9iYWNrZW5kX25vZGVfaWRCCwoJX3NlbGVjdG9yQhAKDl9qc19leHByZXNzaW9uQgsKCV9mcmFtZV9pZEIJCgdfZm9ybWF0QgoKCF9xdWFsaXR5QgUKA19zeEIFCgNfc3lCBQoDX3N3QgUKA19zaCKaAQoSUmVhZENhbnZhc1Jlc3BvbnNlEg8KB3N1Y2Nlc3MYASABKAgSEAoIZnJhbWVfaWQYAiABKAkSFwoPYmFja2VuZF9ub2RlX2lkGAMgASgFEhMKC2RhdGFfYmFzZTY0GAQgASgJEg0KBXdpZHRoGAUgASgFEg4KBmhlaWdodBgGIAEoBRIUCgxvcmlnaW5fY2xlYW4YByABKAgiZAoTU29sdmVDYXB0Y2hhUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSEgoKdGltZW91dF9tcxgDIAEoBRIUCgxyZXRyeV9hbW91bnQYBCABKAUiJgoUU29sdmVDYXB0Y2hhUmVzcG9uc2USDgoGcmVzdWx0GAEgASgJImUKCUljZVNlcnZlchIMCgR1cmxzGAEgAygJEhUKCHVzZXJuYW1lGAIgASgJSACIAQESFwoKY3JlZGVudGlhbBgDIAEoCUgBiAEBQgsKCV91c2VybmFtZUINCgtfY3JlZGVudGlhbCI9ChZHZXRTdHJlYW1Db25maWdSZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCSJKChdHZXRTdHJlYW1Db25maWdSZXNwb25zZRIvCgtpY2Vfc2VydmVycxgBIAMoCzIaLmJyb3dzZXJzY2FsZS52MS5JY2VTZXJ2ZXIiTAoSU3RhcnRTdHJlYW1SZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCRIRCglvZmZlcl9zZHAYAyABKAkiKQoTU3RhcnRTdHJlYW1SZXNwb25zZRISCgphbnN3ZXJfc2RwGAEgASgJIjgKEVN0b3BTdHJlYW1SZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCSIUChJTdG9wU3RyZWFtUmVzcG9uc2UygRwKB0Jyb3dzZXISRAoIU2V0UHJveHkSIC5icm93c2Vyc2NhbGUudjEuU2V0UHJveHlSZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5Ek8KCEdldFBhZ2VzEiAuYnJvd3NlcnNjYWxlLnYxLkdldFBhZ2VzUmVxdWVzdBohLmJyb3dzZXJzY2FsZS52MS5HZXRQYWdlc1Jlc3BvbnNlEk8KCE5hdmlnYXRlEiAuYnJvd3NlcnNjYWxlLnYxLk5hdmlnYXRlUmVxdWVzdBohLmJyb3dzZXJzY2FsZS52MS5OYXZpZ2F0ZVJlc3BvbnNlEkQKCExvYWRIVE1MEiAuYnJvd3NlcnNjYWxlLnYxLkxvYWRIVE1MUmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eRJPCghFdmFsdWF0ZRIgLmJyb3dzZXJzY2FsZS52MS5FdmFsdWF0ZVJlcXVlc3QaIS5icm93c2Vyc2NhbGUudjEuRXZhbHVhdGVSZXNwb25zZRJACgNSdW4SGy5icm93c2Vyc2NhbGUudjEuUnVuUmVxdWVzdBocLmJyb3dzZXJzY2FsZS52MS5SdW5SZXNwb25zZRJMCgpXYWl0Rm9yQW55EiEuYnJvd3NlcnNjYWxlLnYxLldhaXRGb3JBbnlQYXJhbXMaGy5icm93c2Vyc2NhbGUudjEuV2FpdFJlc3VsdBJZCgxTZWxlY3RPcHRpb24SJC5icm93c2Vyc2NhbGUudjEuU2VsZWN0T3B0aW9uUmVxdWVzdBojLmJyb3dzZXJzY2FsZS52MS5TZWxlY3RPcHRpb25SZXN1bHQSSwoIU2Nyb2xsVG8SIC5icm93c2Vyc2NhbGUudjEuU2Nyb2xsVG9SZXF1ZXN0Gh0uYnJvd3NlcnNjYWxlLnYxLlNjcm9sbFJlc3VsdBJFCgZNb3ZlVG8SHi5icm93c2Vyc2NhbGUudjEuTW92ZVRvUmVxdWVzdBobLmJyb3dzZXJzY2FsZS52MS5Nb3ZlUmVzdWx0EkQKBUNsaWNrEh0uYnJvd3NlcnNjYWxlLnYxLkNsaWNrUmVxdWVzdBocLmJyb3dzZXJzY2FsZS52MS5DbGlja1Jlc3VsdBJBCgREcmFnEhwuYnJvd3NlcnNjYWxlLnYxLkRyYWdSZXF1ZXN0GhsuYnJvd3NlcnNjYWxlLnYxLkRyYWdSZXN1bHQSQQoERmlsbBIcLmJyb3dzZXJzY2FsZS52MS5GaWxsUmVxdWVzdBobLmJyb3dzZXJzY2FsZS52MS5GaWxsUmVzdWx0ElgKC0FkZFJlYWN0aW9uEiMuYnJvd3NlcnNjYWxlLnYxLkFkZFJlYWN0aW9uUmVxdWVzdBokLmJyb3dzZXJzY2FsZS52MS5BZGRSZWFjdGlvblJlc3BvbnNlEmEKDlJlbW92ZVJlYWN0aW9uEiYuYnJvd3NlcnNjYWxlLnYxLlJlbW92ZVJlYWN0aW9uUmVxdWVzdBonLmJyb3dzZXJzY2FsZS52MS5SZW1vdmVSZWFjdGlvblJlc3BvbnNlEl4KDUxpc3RSZWFjdGlvbnMSJS5icm93c2Vyc2NhbGUudjEuTGlzdFJlYWN0aW9uc1JlcXVlc3QaJi5icm93c2Vyc2NhbGUudjEuTGlzdFJlYWN0aW9uc1Jlc3BvbnNlEkwKDFNldEJsb2NrTGlzdBIkLmJyb3dzZXJzY2FsZS52MS5TZXRCbG9ja0xpc3RSZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5ElAKDlNldFN0YXRpY1BhdGhzEiYuYnJvd3NlcnNjYWxlLnYxLlNldFN0YXRpY1BhdGhzUmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eRJqChFXYWl0Rm9yQW55UmVxdWVzdBIpLmJyb3dzZXJzY2FsZS52MS5XYWl0Rm9yQW55UmVxdWVzdFJlcXVlc3QaKi5icm93c2Vyc2NhbGUudjEuV2FpdEZvckFueVJlcXVlc3RSZXNwb25zZRJtChJXYWl0Rm9yQW55UmVzcG9uc2USKi5icm93c2Vyc2NhbGUudjEuV2FpdEZvckFueVJlc3BvbnNlUmVxdWVzdBorLmJyb3dzZXJzY2FsZS52MS5XYWl0Rm9yQW55UmVzcG9uc2VSZXNwb25zZRJeCg1Nb2RpZnlSZXF1ZXN0EiUuYnJvd3NlcnNjYWxlLnYxLk1vZGlmeVJlcXVlc3RSZXF1ZXN0GiYuYnJvd3NlcnNjYWxlLnYxLk1vZGlmeVJlcXVlc3RSZXNwb25zZRJVCgpHZXRDb29raWVzEiIuYnJvd3NlcnNjYWxlLnYxLkdldENvb2tpZXNSZXF1ZXN0GiMuYnJvd3NlcnNjYWxlLnYxLkdldENvb2tpZXNSZXNwb25zZRJICgpTZXRDb29raWVzEiIuYnJvd3NlcnNjYWxlLnYxLlNldENvb2tpZXNSZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5EkwKDENsZWFyQ29va2llcxIkLmJyb3dzZXJzY2FsZS52MS5DbGVhckNvb2tpZXNSZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5ElUKCkdldFN0b3JhZ2USIi5icm93c2Vyc2NhbGUudjEuR2V0U3RvcmFnZVJlcXVlc3QaIy5icm93c2Vyc2NhbGUudjEuR2V0U3RvcmFnZVJlc3BvbnNlEkgKClNldFN0b3JhZ2USIi5icm93c2Vyc2NhbGUudjEuU2V0U3RvcmFnZVJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkSTAoMQ2xlYXJTdG9yYWdlEiQuYnJvd3NlcnNjYWxlLnYxLkNsZWFyU3RvcmFnZVJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkSSQoGR2V0RE9NEh4uYnJvd3NlcnNjYWxlLnYxLkdldERPTVJlcXVlc3QaHy5icm93c2Vyc2NhbGUudjEuR2V0RE9NUmVzcG9uc2USVQoKR2V0RE9NSGFzaBIiLmJyb3dzZXJzY2FsZS52MS5HZXRET01IYXNoUmVxdWVzdBojLmJyb3dzZXJzY2FsZS52MS5HZXRET01IYXNoUmVzcG9uc2USYQoOR2V0T2JzZXJ2YXRpb24SJi5icm93c2Vyc2NhbGUudjEuR2V0T2JzZXJ2YXRpb25SZXF1ZXN0GicuYnJvd3NlcnNjYWxlLnYxLkdldE9ic2VydmF0aW9uUmVzcG9uc2USagoRSW5zcGVjdEF0UG9zaXRpb24SKS5icm93c2Vyc2NhbGUudjEuSW5zcGVjdEF0UG9zaXRpb25SZXF1ZXN0GiouYnJvd3NlcnNjYWxlLnYxLkluc3BlY3RBdFBvc2l0aW9uUmVzcG9uc2USTgoNSGlnaGxpZ2h0Tm9kZRIlLmJyb3dzZXJzY2FsZS52MS5IaWdobGlnaHROb2RlUmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eRJVCgpTY3JlZW5zaG90EiIuYnJvd3NlcnNjYWxlLnYxLlNjcmVlbnNob3RSZXF1ZXN0GiMuYnJvd3NlcnNjYWxlLnYxLlNjcmVlbnNob3RSZXNwb25zZRJVCgpSZWFkQ2FudmFzEiIuYnJvd3NlcnNjYWxlLnYxLlJlYWRDYW52YXNSZXF1ZXN0GiMuYnJvd3NlcnNjYWxlLnYxLlJlYWRDYW52YXNSZXNwb25zZRJICgpJbnNlcnRUZXh0EiIuYnJvd3NlcnNjYWxlLnYxLkluc2VydFRleHRSZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5EjwKBFR5cGUSHC5icm93c2Vyc2NhbGUudjEuVHlwZVJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkSRAoIUHJlc3NLZXkSIC5icm93c2Vyc2NhbGUudjEuUHJlc3NLZXlSZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5EkgKClJlbGVhc2VLZXkSIi5icm93c2Vyc2NhbGUudjEuUmVsZWFzZUtleVJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkSWwoMR2V0U2VsZWN0aW9uEiQuYnJvd3NlcnNjYWxlLnYxLkdldFNlbGVjdGlvblJlcXVlc3QaJS5icm93c2Vyc2NhbGUudjEuR2V0U2VsZWN0aW9uUmVzcG9uc2USWwoMU29sdmVDYXB0Y2hhEiQuYnJvd3NlcnNjYWxlLnYxLlNvbHZlQ2FwdGNoYVJlcXVlc3QaJS5icm93c2Vyc2NhbGUudjEuU29sdmVDYXB0Y2hhUmVzcG9uc2USZAoPR2V0U3RyZWFtQ29uZmlnEicuYnJvd3NlcnNjYWxlLnYxLkdldFN0cmVhbUNvbmZpZ1JlcXVlc3QaKC5icm93c2Vyc2NhbGUudjEuR2V0U3RyZWFtQ29uZmlnUmVzcG9uc2USWAoLU3RhcnRTdHJlYW0SIy5icm93c2Vyc2NhbGUudjEuU3RhcnRTdHJlYW1SZXF1ZXN0GiQuYnJvd3NlcnNjYWxlLnYxLlN0YXJ0U3RyZWFtUmVzcG9uc2USVQoKU3RvcFN0cmVhbRIiLmJyb3dzZXJzY2FsZS52MS5TdG9wU3RyZWFtUmVxdWVzdBojLmJyb3dzZXJzY2FsZS52MS5TdG9wU3RyZWFtUmVzcG9uc2VCM1oxZ2l0aHViLmNvbS9icm93c2Vyc2NhbGUvYnJvd3NlcnNjYWxlLWdvL2dlbmVyYXRlZGIGcHJvdG8z", [file_google_protobuf_empty]);
|
|
3802
|
+
var file_wrc = /* @__PURE__ */ fileDesc("Cgl3cmMucHJvdG8SD2Jyb3dzZXJzY2FsZS52MSI7CgRSZWN0EgkKAXgYASABKAESCQoBeRgCIAEoARINCgV3aWR0aBgDIAEoARIOCgZoZWlnaHQYBCABKAEihgIKCUZyYW1lSW5mbxIQCghmcmFtZV9pZBgBIAEoCRILCgN1cmwYAiABKAkSEAoIaXNfb29waWYYAyABKAgSFgoOaGFzX2pzX2NvbnRleHQYBCABKAgSEgoKaXNfbG9hZGluZxgFIAEoCBISCgppc192aXNpYmxlGAYgASgIEiwKDWFic29sdXRlX3JlY3QYByABKAsyFS5icm93c2Vyc2NhbGUudjEuUmVjdBIsCg1yZWxhdGl2ZV9yZWN0GAggASgLMhUuYnJvd3NlcnNjYWxlLnYxLlJlY3QSLAoIY2hpbGRyZW4YCSADKAsyGi5icm93c2Vyc2NhbGUudjEuRnJhbWVJbmZvIqwBCghQYWdlSW5mbxIPCgdwYWdlX2lkGAEgASgJEhoKEmJyb3dzZXJfY29udGV4dF9pZBgCIAEoCRILCgN1cmwYAyABKAkSDQoFdGl0bGUYBCABKAkSJwoIdmlld3BvcnQYBSABKAsyFS5icm93c2Vyc2NhbGUudjEuUmVjdBIuCgpmcmFtZV90cmVlGAYgASgLMhouYnJvd3NlcnNjYWxlLnYxLkZyYW1lSW5mbyIlCgZIZWFkZXISDAoEbmFtZRgBIAEoCRINCgV2YWx1ZRgCIAEoCSKAAQoSSW50ZXJjZXB0ZWRSZXF1ZXN0Eg4KBm1ldGhvZBgBIAEoCRILCgN1cmwYAiABKAkSKAoHaGVhZGVycxgDIAMoCzIXLmJyb3dzZXJzY2FsZS52MS5IZWFkZXISDAoEYm9keRgEIAEoCRIVCg1yZXNvdXJjZV90eXBlGAUgASgJIm8KE0ludGVyY2VwdGVkUmVzcG9uc2USCwoDdXJsGAEgASgJEhMKC3N0YXR1c19jb2RlGAIgASgFEigKB2hlYWRlcnMYAyADKAsyFy5icm93c2Vyc2NhbGUudjEuSGVhZGVyEgwKBGJvZHkYBCABKAkijgEKEkhlYWRlck1vZGlmaWNhdGlvbhIMCgRuYW1lGAEgASgJEhIKBXZhbHVlGAIgASgJSACIAQESDgoGYWN0aW9uGAMgASgJEhMKBmJlZm9yZRgEIAEoCUgBiAEBEhIKBWFmdGVyGAUgASgJSAKIAQFCCAoGX3ZhbHVlQgkKB19iZWZvcmVCCAoGX2FmdGVyIk0KEkNvb2tpZVBhcnRpdGlvbktleRIWCg50b3BfbGV2ZWxfc2l0ZRgBIAEoCRIfChdoYXNfY3Jvc3Nfc2l0ZV9hbmNlc3RvchgCIAEoCCK/AwoLQ29va2llUGFyYW0SDAoEbmFtZRgBIAEoCRINCgV2YWx1ZRgCIAEoCRIQCgN1cmwYAyABKAlIAIgBARIOCgZkb21haW4YBCABKAkSDAoEcGF0aBgFIAEoCRITCgZzZWN1cmUYBiABKAhIAYgBARIWCglodHRwX29ubHkYByABKAhIAogBARIWCglzYW1lX3NpdGUYCCABKAlIA4gBARIUCgdleHBpcmVzGAkgASgBSASIAQESFQoIcHJpb3JpdHkYCiABKAlIBYgBARIaCg1zb3VyY2Vfc2NoZW1lGAsgASgJSAaIAQESGAoLc291cmNlX3BvcnQYDCABKAVIB4gBARI/Cg1wYXJ0aXRpb25fa2V5GA0gASgLMiMuYnJvd3NlcnNjYWxlLnYxLkNvb2tpZVBhcnRpdGlvbktleUgIiAEBQgYKBF91cmxCCQoHX3NlY3VyZUIMCgpfaHR0cF9vbmx5QgwKCl9zYW1lX3NpdGVCCgoIX2V4cGlyZXNCCwoJX3ByaW9yaXR5QhAKDl9zb3VyY2Vfc2NoZW1lQg4KDF9zb3VyY2VfcG9ydEIQCg5fcGFydGl0aW9uX2tleSKtAQoNV2FpdENvbmRpdGlvbhIVCghzZWxlY3RvchgBIAEoCUgAiAEBEhoKDWpzX2V4cHJlc3Npb24YAiABKAlIAYgBARIUCgd2aXNpYmxlGAMgASgISAKIAQESGAoLc3RlYWR5X3RpbWUYBCABKAFIA4gBAUILCglfc2VsZWN0b3JCEAoOX2pzX2V4cHJlc3Npb25CCgoIX3Zpc2libGVCDgoMX3N0ZWFkeV90aW1lIrsBCgpXYWl0UmVzdWx0Eg0KBWluZGV4GAEgASgFEhAKCGZyYW1lX2lkGAIgASgJEhcKD2JhY2tlbmRfbm9kZV9pZBgDIAEoBRISCgppc192aXNpYmxlGAQgASgIEiUKBmJvdW5kcxgFIAEoCzIVLmJyb3dzZXJzY2FsZS52MS5SZWN0Ei4KBWVycm9yGAYgASgLMhouYnJvd3NlcnNjYWxlLnYxLldhaXRFcnJvckgAiAEBQggKBl9lcnJvciLsAQoTV2FpdENvbmRpdGlvblN0YXR1cxINCgVpbmRleBgBIAEoBRINCgVzdGF0ZRgCIAEoCRIXCg9iYWNrZW5kX25vZGVfaWQYAyABKAUSEAoIZnJhbWVfaWQYBCABKAkSEgoKaXNfdmlzaWJsZRgFIAEoCBIqCgZib3VuZHMYBiABKAsyFS5icm93c2Vyc2NhbGUudjEuUmVjdEgAiAEBEjQKCG9jY2x1ZGVyGAcgASgLMh0uYnJvd3NlcnNjYWxlLnYxLk9jY2x1ZGVySW5mb0gBiAEBQgkKB19ib3VuZHNCCwoJX29jY2x1ZGVyImQKCVdhaXRFcnJvchIMCgRjb2RlGAEgASgJEg8KB21lc3NhZ2UYAiABKAkSOAoKY29uZGl0aW9ucxgDIAMoCzIkLmJyb3dzZXJzY2FsZS52MS5XYWl0Q29uZGl0aW9uU3RhdHVzIqYBCg1FbGVtZW50UmVzdWx0Eg8KB3N1Y2Nlc3MYASABKAgSEAoIZnJhbWVfaWQYAiABKAkSFwoPYmFja2VuZF9ub2RlX2lkGAMgASgFEhIKCmlzX3Zpc2libGUYBCABKAgSJQoGYm91bmRzGAUgASgLMhUuYnJvd3NlcnNjYWxlLnYxLlJlY3QSDgoGcm9vdF94GAYgASgBEg4KBnJvb3RfeRgHIAEoASLSAwoMT2NjbHVkZXJJbmZvEhcKD2JhY2tlbmRfbm9kZV9pZBgBIAEoBRIQCghmcmFtZV9pZBgCIAEoCRIQCgh0YWdfbmFtZRgDIAEoCRIPCgJpZBgEIAEoCUgAiAEBEhcKCmNsYXNzX25hbWUYBSABKAlIAYgBARIRCgR0ZXh0GAYgASgJSAKIAQESJQoGYm91bmRzGAcgASgLMhUuYnJvd3NlcnNjYWxlLnYxLlJlY3QSGwoOcG9pbnRlcl9ldmVudHMYCCABKAlIA4gBARIXCgp2aXNpYmlsaXR5GAkgASgJSASIAQESFAoHb3BhY2l0eRgKIAEoAUgFiAEBEhQKB3pfaW5kZXgYCyABKAlIBogBARIlChhoaXR0YWJsZV93aGlsZV9pbnZpc2libGUYDCABKAhIB4gBARIVCghwb3NpdGlvbhgNIAEoCUgIiAEBQgUKA19pZEINCgtfY2xhc3NfbmFtZUIHCgVfdGV4dEIRCg9fcG9pbnRlcl9ldmVudHNCDQoLX3Zpc2liaWxpdHlCCgoIX29wYWNpdHlCCgoIX3pfaW5kZXhCGwoZX2hpdHRhYmxlX3doaWxlX2ludmlzaWJsZUILCglfcG9zaXRpb24ioAEKCkNsaWNrRXJyb3ISDAoEY29kZRgBIAEoCRIPCgdtZXNzYWdlGAIgASgJEjQKCG9jY2x1ZGVyGAMgASgLMh0uYnJvd3NlcnNjYWxlLnYxLk9jY2x1ZGVySW5mb0gAiAEBEhwKD2V2YWRlX2F0dGVtcHRlZBgEIAEoCEgBiAEBQgsKCV9vY2NsdWRlckISChBfZXZhZGVfYXR0ZW1wdGVkIt8BCgtDbGlja1Jlc3VsdBIPCgdzdWNjZXNzGAEgASgIEhAKCGZyYW1lX2lkGAIgASgJEhcKD2JhY2tlbmRfbm9kZV9pZBgDIAEoBRISCgppc192aXNpYmxlGAQgASgIEiUKBmJvdW5kcxgFIAEoCzIVLmJyb3dzZXJzY2FsZS52MS5SZWN0Eg4KBnJvb3RfeBgGIAEoARIOCgZyb290X3kYByABKAESLwoFZXJyb3IYCCABKAsyGy5icm93c2Vyc2NhbGUudjEuQ2xpY2tFcnJvckgAiAEBQggKBl9lcnJvciKeAgoMUmVhY3Rpb25JbmZvEhMKC3JlYWN0aW9uX2lkGAEgASgJEhsKDm1hdGNoX3NlbGVjdG9yGAIgASgJSACIAQESIAoTbWF0Y2hfanNfZXhwcmVzc2lvbhgDIAEoCUgBiAEBEhwKD2FjdGlvbl9zZWxlY3RvchgEIAEoCUgCiAEBEiEKFGFjdGlvbl9qc19leHByZXNzaW9uGAUgASgJSAOIAQESEAoIZnJhbWVfaWQYBiABKAkSDwoHdmlzaWJsZRgHIAEoCEIRCg9fbWF0Y2hfc2VsZWN0b3JCFgoUX21hdGNoX2pzX2V4cHJlc3Npb25CEgoQX2FjdGlvbl9zZWxlY3RvckIXChVfYWN0aW9uX2pzX2V4cHJlc3Npb24i1gMKEkFkZFJlYWN0aW9uUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSDwoHcGFnZV9pZBgDIAEoCRIbCg5tYXRjaF9zZWxlY3RvchgEIAEoCUgAiAEBEiAKE21hdGNoX2pzX2V4cHJlc3Npb24YBSABKAlIAYgBARIVCghmcmFtZV9pZBgGIAEoCUgCiAEBEhQKB3Zpc2libGUYByABKAhIA4gBARIcCg9hY3Rpb25fc2VsZWN0b3IYCCABKAlIBIgBARIhChRhY3Rpb25fanNfZXhwcmVzc2lvbhgJIAEoCUgFiAEBEhMKBmJ1dHRvbhgKIAEoCUgGiAEBEhgKC2NsaWNrX2NvdW50GAsgASgFSAeIAQESFQoIaW50ZXJ2YWwYDCABKAFICIgBAUIRCg9fbWF0Y2hfc2VsZWN0b3JCFgoUX21hdGNoX2pzX2V4cHJlc3Npb25CCwoJX2ZyYW1lX2lkQgoKCF92aXNpYmxlQhIKEF9hY3Rpb25fc2VsZWN0b3JCFwoVX2FjdGlvbl9qc19leHByZXNzaW9uQgkKB19idXR0b25CDgoMX2NsaWNrX2NvdW50QgsKCV9pbnRlcnZhbCIqChNBZGRSZWFjdGlvblJlc3BvbnNlEhMKC3JlYWN0aW9uX2lkGAEgASgJIlEKFVJlbW92ZVJlYWN0aW9uUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSEwoLcmVhY3Rpb25faWQYAyABKAkiKQoWUmVtb3ZlUmVhY3Rpb25SZXNwb25zZRIPCgdyZW1vdmVkGAEgASgIIkwKFExpc3RSZWFjdGlvbnNSZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCRIPCgdwYWdlX2lkGAMgASgJIkkKFUxpc3RSZWFjdGlvbnNSZXNwb25zZRIwCglyZWFjdGlvbnMYASADKAsyHS5icm93c2Vyc2NhbGUudjEuUmVhY3Rpb25JbmZvIukBCgpFbGVtZW50UmVmEhcKD2JhY2tlbmRfbm9kZV9pZBgBIAEoBRIQCgh0YWdfbmFtZRgCIAEoCRIPCgJpZBgDIAEoCUgAiAEBEhEKBG5hbWUYBCABKAlIAYgBARIXCgpjbGFzc19uYW1lGAUgASgJSAKIAQESFwoKaW5wdXRfdHlwZRgGIAEoCUgDiAEBEhEKBHRleHQYByABKAlIBIgBARIQCghlZGl0YWJsZRgIIAEoCEIFCgNfaWRCBwoFX25hbWVCDQoLX2NsYXNzX25hbWVCDQoLX2lucHV0X3R5cGVCBwoFX3RleHQi7gIKCUZpbGxFcnJvchIMCgRjb2RlGAEgASgJEg8KB21lc3NhZ2UYAiABKAkSNQoLY2xpY2tfZXJyb3IYAyABKAsyGy5icm93c2Vyc2NhbGUudjEuQ2xpY2tFcnJvckgAiAEBEiQKF2ZvY3VzZWRfYmFja2VuZF9ub2RlX2lkGAQgASgFSAGIAQESOQoPZm9jdXNlZF9lbGVtZW50GAUgASgLMhsuYnJvd3NlcnNjYWxlLnYxLkVsZW1lbnRSZWZIAogBARIcCg90YXJnZXRfZWRpdGFibGUYBiABKAhIA4gBARIgChN0YXJnZXRfdmFsdWVfbGVuZ3RoGAcgASgFSASIAQFCDgoMX2NsaWNrX2Vycm9yQhoKGF9mb2N1c2VkX2JhY2tlbmRfbm9kZV9pZEISChBfZm9jdXNlZF9lbGVtZW50QhIKEF90YXJnZXRfZWRpdGFibGVCFgoUX3RhcmdldF92YWx1ZV9sZW5ndGgiogEKCkZpbGxSZXN1bHQSDwoHc3VjY2VzcxgBIAEoCBIQCghmcmFtZV9pZBgCIAEoCRIXCg9iYWNrZW5kX25vZGVfaWQYAyABKAUSDgoGcm9vdF94GAYgASgBEg4KBnJvb3RfeRgHIAEoARIuCgVlcnJvchgIIAEoCzIaLmJyb3dzZXJzY2FsZS52MS5GaWxsRXJyb3JIAIgBAUIICgZfZXJyb3IiwgEKCkRyYWdSZXN1bHQSDwoHc3VjY2VzcxgBIAEoCBIQCghmcmFtZV9pZBgCIAEoCRIXCg9iYWNrZW5kX25vZGVfaWQYAyABKAUSDwoHc3RhcnRfeBgEIAEoARIPCgdzdGFydF95GAUgASgBEg0KBWVuZF94GAYgASgBEg0KBWVuZF95GAcgASgBEi4KBWVycm9yGAggASgLMhouYnJvd3NlcnNjYWxlLnYxLkRyYWdFcnJvckgAiAEBQggKBl9lcnJvciJxCglEcmFnRXJyb3ISDAoEY29kZRgBIAEoCRIPCgdtZXNzYWdlGAIgASgJEjUKC2NsaWNrX2Vycm9yGAMgASgLMhsuYnJvd3NlcnNjYWxlLnYxLkNsaWNrRXJyb3JIAIgBAUIOCgxfY2xpY2tfZXJyb3IirgEKElNlbGVjdE9wdGlvblJlc3VsdBIWCg5zZWxlY3RlZF9pbmRleBgBIAEoBRIWCg5zZWxlY3RlZF92YWx1ZRgCIAEoCRIVCg1zZWxlY3RlZF90ZXh0GAMgASgJEg8KB3N1Y2Nlc3MYBCABKAgSNgoFZXJyb3IYBSABKAsyIi5icm93c2Vyc2NhbGUudjEuU2VsZWN0T3B0aW9uRXJyb3JIAIgBAUIICgZfZXJyb3IiMgoRU2VsZWN0T3B0aW9uRXJyb3ISDAoEY29kZRgBIAEoCRIPCgdtZXNzYWdlGAIgASgJIsEBCgxTY3JvbGxSZXN1bHQSDwoHc3VjY2VzcxgBIAEoCBIQCghmcmFtZV9pZBgCIAEoCRIXCg9iYWNrZW5kX25vZGVfaWQYAyABKAUSEgoKaXNfdmlzaWJsZRgEIAEoCBIlCgZib3VuZHMYBSABKAsyFS5icm93c2Vyc2NhbGUudjEuUmVjdBIwCgVlcnJvchgIIAEoCzIcLmJyb3dzZXJzY2FsZS52MS5TY3JvbGxFcnJvckgAiAEBQggKBl9lcnJvciIsCgtTY3JvbGxFcnJvchIMCgRjb2RlGAEgASgJEg8KB21lc3NhZ2UYAiABKAki3QEKCk1vdmVSZXN1bHQSDwoHc3VjY2VzcxgBIAEoCBIQCghmcmFtZV9pZBgCIAEoCRIXCg9iYWNrZW5kX25vZGVfaWQYAyABKAUSEgoKaXNfdmlzaWJsZRgEIAEoCBIlCgZib3VuZHMYBSABKAsyFS5icm93c2Vyc2NhbGUudjEuUmVjdBIOCgZyb290X3gYBiABKAESDgoGcm9vdF95GAcgASgBEi4KBWVycm9yGAggASgLMhouYnJvd3NlcnNjYWxlLnYxLk1vdmVFcnJvckgAiAEBQggKBl9lcnJvciIqCglNb3ZlRXJyb3ISDAoEY29kZRgBIAEoCRIPCgdtZXNzYWdlGAIgASgJIuYBCg9TZXRQcm94eVJlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCRIPCgdhcGlfa2V5GAIgASgJEhcKCnByb3h5X2hvc3QYAyABKAlIAIgBARIXCgpwcm94eV9wb3J0GAQgASgFSAGIAQESGwoOcHJveHlfdXNlcm5hbWUYBSABKAlIAogBARIbCg5wcm94eV9wYXNzd29yZBgGIAEoCUgDiAEBQg0KC19wcm94eV9ob3N0Qg0KC19wcm94eV9wb3J0QhEKD19wcm94eV91c2VybmFtZUIRCg9fcHJveHlfcGFzc3dvcmQiNgoPR2V0UGFnZXNSZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCSI8ChBHZXRQYWdlc1Jlc3BvbnNlEigKBXBhZ2VzGAEgAygLMhkuYnJvd3NlcnNjYWxlLnYxLlBhZ2VJbmZvIpoBCg9OYXZpZ2F0ZVJlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCRIPCgdhcGlfa2V5GAIgASgJEg8KB3BhZ2VfaWQYAyABKAkSCwoDdXJsGAQgASgJEhUKCHJlZmVycmVyGAUgASgJSACIAQESFAoHdGltZW91dBgGIAEoAUgBiAEBQgsKCV9yZWZlcnJlckIKCghfdGltZW91dCIxChBOYXZpZ2F0ZVJlc3BvbnNlEhAKCGZyYW1lX2lkGAEgASgJEgsKA3VybBgCIAEoCSK2AQoPTG9hZEhUTUxSZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCRIPCgdwYWdlX2lkGAMgASgJEgsKA3VybBgEIAEoCRIMCgRodG1sGAUgASgJEigKB2hlYWRlcnMYBiADKAsyFy5icm93c2Vyc2NhbGUudjEuSGVhZGVyEhgKC3N0YXR1c19jb2RlGAcgASgFSACIAQFCDgoMX3N0YXR1c19jb2RlIn8KD0V2YWx1YXRlUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSDwoHcGFnZV9pZBgDIAEoCRISCgpleHByZXNzaW9uGAQgASgJEhUKCGZyYW1lX2lkGAUgASgJSACIAQFCCwoJX2ZyYW1lX2lkInYKEEV2YWx1YXRlUmVzcG9uc2USDgoGcmVzdWx0GAEgASgJEhcKD2JhY2tlbmRfbm9kZV9pZBgCIAEoBRISCgppc192aXNpYmxlGAMgASgIEiUKBmJvdW5kcxgEIAEoCzIVLmJyb3dzZXJzY2FsZS52MS5SZWN0ImMKClJ1blJlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCRIPCgdhcGlfa2V5GAIgASgJEg4KBnNvdXJjZRgDIAEoCRIUCgdwYWdlX2lkGAQgASgJSACIAQFCCgoIX3BhZ2VfaWQiLgoLUnVuUmVzcG9uc2USDwoHc3VjY2VzcxgBIAEoCBIOCgZyZXN1bHQYAiABKAkiwgEKEFdhaXRGb3JBbnlQYXJhbXMSEgoKc2Vzc2lvbl9pZBgBIAEoCRIPCgdhcGlfa2V5GAIgASgJEg8KB3BhZ2VfaWQYAyABKAkSMgoKY29uZGl0aW9ucxgEIAMoCzIeLmJyb3dzZXJzY2FsZS52MS5XYWl0Q29uZGl0aW9uEhUKCGZyYW1lX2lkGAUgASgJSACIAQESFAoHdGltZW91dBgGIAEoAUgBiAEBQgsKCV9mcmFtZV9pZEIKCghfdGltZW91dCL1AgoTU2VsZWN0T3B0aW9uUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSDwoHcGFnZV9pZBgDIAEoCRIcCg9iYWNrZW5kX25vZGVfaWQYBCABKAVIAIgBARIVCghzZWxlY3RvchgFIAEoCUgBiAEBEhoKDWpzX2V4cHJlc3Npb24YBiABKAlIAogBARIVCghmcmFtZV9pZBgHIAEoCUgDiAEBEhIKBWluZGV4GAggASgFSASIAQESEgoFdmFsdWUYCSABKAlIBYgBARIRCgR0ZXh0GAogASgJSAaIAQESGAoLZmlyZV9ldmVudHMYCyABKAhIB4gBAUISChBfYmFja2VuZF9ub2RlX2lkQgsKCV9zZWxlY3RvckIQCg5fanNfZXhwcmVzc2lvbkILCglfZnJhbWVfaWRCCAoGX2luZGV4QggKBl92YWx1ZUIHCgVfdGV4dEIOCgxfZmlyZV9ldmVudHMi7wEKD1Njcm9sbFRvUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSDwoHcGFnZV9pZBgDIAEoCRIcCg9iYWNrZW5kX25vZGVfaWQYBCABKAVIAIgBARIVCghzZWxlY3RvchgFIAEoCUgBiAEBEhoKDWpzX2V4cHJlc3Npb24YBiABKAlIAogBARIVCghmcmFtZV9pZBgHIAEoCUgDiAEBQhIKEF9iYWNrZW5kX25vZGVfaWRCCwoJX3NlbGVjdG9yQhAKDl9qc19leHByZXNzaW9uQgsKCV9mcmFtZV9pZCKZAgoNTW92ZVRvUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSDwoHcGFnZV9pZBgDIAEoCRIcCg9iYWNrZW5kX25vZGVfaWQYBCABKAVIAIgBARIVCghzZWxlY3RvchgFIAEoCUgBiAEBEhoKDWpzX2V4cHJlc3Npb24YBiABKAlIAogBARIVCghmcmFtZV9pZBgHIAEoCUgDiAEBEg4KAXgYCCABKAFIBIgBARIOCgF5GAkgASgBSAWIAQFCEgoQX2JhY2tlbmRfbm9kZV9pZEILCglfc2VsZWN0b3JCEAoOX2pzX2V4cHJlc3Npb25CCwoJX2ZyYW1lX2lkQgQKAl94QgQKAl95IoIDCgxDbGlja1JlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCRIPCgdhcGlfa2V5GAIgASgJEg8KB3BhZ2VfaWQYAyABKAkSHAoPYmFja2VuZF9ub2RlX2lkGAQgASgFSACIAQESFQoIc2VsZWN0b3IYBSABKAlIAYgBARIaCg1qc19leHByZXNzaW9uGAYgASgJSAKIAQESFQoIZnJhbWVfaWQYByABKAlIA4gBARITCgZidXR0b24YCCABKAlIBIgBARIYCgtjbGlja19jb3VudBgJIAEoBUgFiAEBEg4KAXgYCiABKAFIBogBARIOCgF5GAsgASgBSAeIAQESEwoGYWN0aW9uGAwgASgJSAiIAQFCEgoQX2JhY2tlbmRfbm9kZV9pZEILCglfc2VsZWN0b3JCEAoOX2pzX2V4cHJlc3Npb25CCwoJX2ZyYW1lX2lkQgkKB19idXR0b25CDgoMX2NsaWNrX2NvdW50QgQKAl94QgQKAl95QgkKB19hY3Rpb24igwMKC0RyYWdSZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCRIPCgdwYWdlX2lkGAMgASgJEhwKD2JhY2tlbmRfbm9kZV9pZBgEIAEoBUgAiAEBEhUKCHNlbGVjdG9yGAUgASgJSAGIAQESGgoNanNfZXhwcmVzc2lvbhgGIAEoCUgCiAEBEhUKCGZyYW1lX2lkGAcgASgJSAOIAQESFQoIb2Zmc2V0X3gYCCABKAFIBIgBARIVCghvZmZzZXRfeRgJIAEoAUgFiAEBEhcKCmFic29sdXRlX3gYCiABKAFIBogBARIXCgphYnNvbHV0ZV95GAsgASgBSAeIAQFCEgoQX2JhY2tlbmRfbm9kZV9pZEILCglfc2VsZWN0b3JCEAoOX2pzX2V4cHJlc3Npb25CCwoJX2ZyYW1lX2lkQgsKCV9vZmZzZXRfeEILCglfb2Zmc2V0X3lCDQoLX2Fic29sdXRlX3hCDQoLX2Fic29sdXRlX3ki7wIKC0ZpbGxSZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCRIPCgdwYWdlX2lkGAMgASgJEhwKD2JhY2tlbmRfbm9kZV9pZBgEIAEoBUgAiAEBEhUKCHNlbGVjdG9yGAUgASgJSAGIAQESGgoNanNfZXhwcmVzc2lvbhgGIAEoCUgCiAEBEhUKCGZyYW1lX2lkGAcgASgJSAOIAQESDAoEdGV4dBgIIAEoCRIYCgtjbGVhcl9maXJzdBgJIAEoCEgEiAEBEhQKB3RpbWVvdXQYCiABKAFIBYgBARIYCgtzdGVhZHlfdGltZRgLIAEoAUgGiAEBQhIKEF9iYWNrZW5kX25vZGVfaWRCCwoJX3NlbGVjdG9yQhAKDl9qc19leHByZXNzaW9uQgsKCV9mcmFtZV9pZEIOCgxfY2xlYXJfZmlyc3RCCgoIX3RpbWVvdXRCDgoMX3N0ZWFkeV90aW1lIkwKE1NldEJsb2NrTGlzdFJlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCRIPCgdhcGlfa2V5GAIgASgJEhAKCHBhdHRlcm5zGAMgAygJImEKFVNldFN0YXRpY1BhdGhzUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSEAoIcGF0dGVybnMYAyADKAkSEQoJYmxvYl9uYW1lGAQgASgJIogBChhXYWl0Rm9yQW55UmVxdWVzdFJlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCRIPCgdhcGlfa2V5GAIgASgJEhAKCHBhdHRlcm5zGAMgAygJEhMKC2Fib3J0X2ZsYWdzGAQgAygFEhQKB3RpbWVvdXQYBSABKAFIAIgBAUIKCghfdGltZW91dCJgChlXYWl0Rm9yQW55UmVxdWVzdFJlc3BvbnNlEg0KBWluZGV4GAEgASgFEjQKB3JlcXVlc3QYAiABKAsyIy5icm93c2Vyc2NhbGUudjEuSW50ZXJjZXB0ZWRSZXF1ZXN0IokBChlXYWl0Rm9yQW55UmVzcG9uc2VSZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCRIQCghwYXR0ZXJucxgDIAMoCRITCgthYm9ydF9mbGFncxgEIAMoBRIUCgd0aW1lb3V0GAUgASgBSACIAQFCCgoIX3RpbWVvdXQiYwoaV2FpdEZvckFueVJlc3BvbnNlUmVzcG9uc2USDQoFaW5kZXgYASABKAUSNgoIcmVzcG9uc2UYAiABKAsyJC5icm93c2Vyc2NhbGUudjEuSW50ZXJjZXB0ZWRSZXNwb25zZSLKAQoUTW9kaWZ5UmVxdWVzdFJlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCRIPCgdhcGlfa2V5GAIgASgJEhMKC3VybF9wYXR0ZXJuGAMgASgJEjoKDW1vZGlmaWNhdGlvbnMYBCADKAsyIy5icm93c2Vyc2NhbGUudjEuSGVhZGVyTW9kaWZpY2F0aW9uEhEKBGJvZHkYBSABKAlIAIgBARIUCgd0aW1lb3V0GAYgASgBSAGIAQFCBwoFX2JvZHlCCgoIX3RpbWVvdXQiTQoVTW9kaWZ5UmVxdWVzdFJlc3BvbnNlEjQKB3JlcXVlc3QYASABKAsyIy5icm93c2Vyc2NhbGUudjEuSW50ZXJjZXB0ZWRSZXF1ZXN0IoYBChpTdGFydE5ldHdvcmtDYXB0dXJlUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSEAoIcGF0dGVybnMYAyADKAkSDgoGYm9kaWVzGAQgASgJEhUKDWJvZHlfcGF0dGVybnMYBSADKAlKBAgGEAdKBAgHEAgiQAoZU3RvcE5ldHdvcmtDYXB0dXJlUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkiLQoaU3RvcE5ldHdvcmtDYXB0dXJlUmVzcG9uc2USDwoHc3RvcHBlZBgBIAEoCCJECh1TdHJlYW1OZXR3b3JrRXhjaGFuZ2VzUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkiWwoUTmV0d29ya0V4Y2hhbmdlRXZlbnQSMgoIZXhjaGFuZ2UYASABKAsyIC5icm93c2Vyc2NhbGUudjEuTmV0d29ya0V4Y2hhbmdlEg8KB2Ryb3BwZWQYAiABKAQitAUKD05ldHdvcmtFeGNoYW5nZRISCgpyZXF1ZXN0X2lkGAEgASgJEhAKCGNoYWluX2lkGAIgASgJEhYKDnJlZGlyZWN0X2luZGV4GAMgASgFEhAKCGZyYW1lX2lkGAQgASgJEhAKCGlzX29vcGlmGAUgASgIEhUKDXJlc291cmNlX3R5cGUYBiABKAkSDgoGbWV0aG9kGAcgASgJEgsKA3VybBgIIAEoCRIVCg1pbml0aWF0b3JfdXJsGAkgASgJEjAKD3JlcXVlc3RfaGVhZGVycxgKIAMoCzIXLmJyb3dzZXJzY2FsZS52MS5IZWFkZXISIAoYcmVxdWVzdF9oZWFkZXJzX2FyZV93aXJlGAsgASgIEhQKDHJlcXVlc3RfYm9keRgMIAEoDBIeChZyZXF1ZXN0X2JvZHlfdHJ1bmNhdGVkGA0gASgIEhQKDGhhc19yZXNwb25zZRgOIAEoCBITCgtzdGF0dXNfY29kZRgPIAEoBRITCgtzdGF0dXNfdGV4dBgQIAEoCRIRCgltaW1lX3R5cGUYESABKAkSEAoIcHJvdG9jb2wYEiABKAkSFgoOcmVtb3RlX2FkZHJlc3MYEyABKAkSEwoLc2VydmVkX2Zyb20YFCABKAkSMQoQcmVzcG9uc2VfaGVhZGVycxgVIAMoCzIXLmJyb3dzZXJzY2FsZS52MS5IZWFkZXISIQoZcmVzcG9uc2VfaGVhZGVyc19hcmVfd2lyZRgWIAEoCBIVCg1yZXNwb25zZV9ib2R5GBcgASgMEh8KF3Jlc3BvbnNlX2JvZHlfdHJ1bmNhdGVkGBggASgIEh4KFnJlc3BvbnNlX2JvZHlfY2FwdHVyZWQYGSABKAgSGwoTZW5jb2RlZF9kYXRhX2xlbmd0aBgaIAEoAxINCgVlcnJvchgbIAEoCSI4ChFHZXRDb29raWVzUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkiQwoSR2V0Q29va2llc1Jlc3BvbnNlEi0KB2Nvb2tpZXMYASADKAsyHC5icm93c2Vyc2NhbGUudjEuQ29va2llUGFyYW0iZwoRU2V0Q29va2llc1JlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCRIPCgdhcGlfa2V5GAIgASgJEi0KB2Nvb2tpZXMYAyADKAsyHC5icm93c2Vyc2NhbGUudjEuQ29va2llUGFyYW0iOgoTQ2xlYXJDb29raWVzUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkiKQoLU3RvcmFnZUl0ZW0SCwoDa2V5GAEgASgJEg0KBXZhbHVlGAIgASgJIlEKElN0b3JhZ2VPcmlnaW5FbnRyeRIOCgZvcmlnaW4YASABKAkSKwoFaXRlbXMYAiADKAsyHC5icm93c2Vyc2NhbGUudjEuU3RvcmFnZUl0ZW0iWAoRR2V0U3RvcmFnZVJlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCRIPCgdhcGlfa2V5GAIgASgJEhMKBm9yaWdpbhgDIAEoCUgAiAEBQgkKB19vcmlnaW4iSgoSR2V0U3RvcmFnZVJlc3BvbnNlEjQKB3N0b3JhZ2UYASADKAsyIy5icm93c2Vyc2NhbGUudjEuU3RvcmFnZU9yaWdpbkVudHJ5IpgBChFTZXRTdG9yYWdlUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSNAoHc3RvcmFnZRgDIAMoCzIjLmJyb3dzZXJzY2FsZS52MS5TdG9yYWdlT3JpZ2luRW50cnkSGAoLY2xlYXJfZmlyc3QYBCABKAhIAIgBAUIOCgxfY2xlYXJfZmlyc3QiWgoTQ2xlYXJTdG9yYWdlUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSEwoGb3JpZ2luGAMgASgJSACIAQFCCQoHX29yaWdpbiIsCgtEYnNjU2Vzc2lvbhIMCgRzaXRlGAEgASgJEg8KB3Nlc3Npb24YAiABKAki2AIKC0F1dGhTZXNzaW9uEhQKB2dhaWFfaWQYASABKAlIAIgBARISCgVlbWFpbBgCIAEoCUgBiAEBEhoKDXJlZnJlc2hfdG9rZW4YAyABKAlIAogBARIgChN3cmFwcGVkX2JpbmRpbmdfa2V5GAQgASgJSAOIAQESJAoXc2lnbmluX3Njb3BlZF9kZXZpY2VfaWQYBSABKAlIBIgBARIZCgxzeW5jX2NvbnNlbnQYBiABKAhIBYgBARIzCg1kYnNjX3Nlc3Npb25zGAcgAygLMhwuYnJvd3NlcnNjYWxlLnYxLkRic2NTZXNzaW9uQgoKCF9nYWlhX2lkQggKBl9lbWFpbEIQCg5fcmVmcmVzaF90b2tlbkIWChRfd3JhcHBlZF9iaW5kaW5nX2tleUIaChhfc2lnbmluX3Njb3BlZF9kZXZpY2VfaWRCDwoNX3N5bmNfY29uc2VudCI8ChVHZXRBdXRoU2Vzc2lvblJlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCRIPCgdhcGlfa2V5GAIgASgJIlgKFkdldEF1dGhTZXNzaW9uUmVzcG9uc2USMgoHc2Vzc2lvbhgBIAEoCzIcLmJyb3dzZXJzY2FsZS52MS5BdXRoU2Vzc2lvbkgAiAEBQgoKCF9zZXNzaW9uImsKFVNldEF1dGhTZXNzaW9uUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSLQoHc2Vzc2lvbhgDIAEoCzIcLmJyb3dzZXJzY2FsZS52MS5BdXRoU2Vzc2lvbiKHAQoNR2V0RE9NUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSDwoHcGFnZV9pZBgDIAEoCRIVCghmcmFtZV9pZBgEIAEoCUgAiAEBEhIKBWRlcHRoGAUgASgFSAGIAQFCCwoJX2ZyYW1lX2lkQggKBl9kZXB0aCIdCg5HZXRET01SZXNwb25zZRILCgNkb20YASABKAkimQQKFUdldE9ic2VydmF0aW9uUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSDwoHcGFnZV9pZBgDIAEoCRIjChZtYXhfZWxlbWVudHNfcGVyX2ZyYW1lGAQgASgFSACIAQESHAoPbWF4X3RleHRfbGVuZ3RoGAUgASgFSAGIAQESEwoGZm9ybWF0GAYgASgJSAKIAQESHQoQbWF4X3RvdGFsX3Rva2VucxgHIAEoBUgDiAEBEhsKDmluY2x1ZGVfYm91bmRzGAggASgISASIAQESGgoNdmlld3BvcnRfb25seRgJIAEoCEgFiAEBEhwKD2JhY2tlbmRfbm9kZV9pZBgKIAEoBUgGiAEBEhUKCHNlbGVjdG9yGAsgASgJSAeIAQESGgoNanNfZXhwcmVzc2lvbhgMIAEoCUgIiAEBEhUKCGZyYW1lX2lkGA0gASgJSAmIAQFCGQoXX21heF9lbGVtZW50c19wZXJfZnJhbWVCEgoQX21heF90ZXh0X2xlbmd0aEIJCgdfZm9ybWF0QhMKEV9tYXhfdG90YWxfdG9rZW5zQhEKD19pbmNsdWRlX2JvdW5kc0IQCg5fdmlld3BvcnRfb25seUISChBfYmFja2VuZF9ub2RlX2lkQgsKCV9zZWxlY3RvckIQCg5fanNfZXhwcmVzc2lvbkILCglfZnJhbWVfaWQiRQoWR2V0T2JzZXJ2YXRpb25SZXNwb25zZRITCgtvYnNlcnZhdGlvbhgBIAEoCUoECAIQA1IQb2JzZXJ2YXRpb25fanNvbiJtChFHZXRET01IYXNoUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSDwoHcGFnZV9pZBgDIAEoCRIVCghmcmFtZV9pZBgEIAEoCUgAiAEBQgsKCV9mcmFtZV9pZCIiChJHZXRET01IYXNoUmVzcG9uc2USDAoEaGFzaBgBIAEoCSKAAQoVU3RhcnREb21NaXJyb3JSZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCRISCgVkZXB0aBgEIAEoBUgAiAEBEhMKBnBpZXJjZRgFIAEoCEgBiAEBQggKBl9kZXB0aEIJCgdfcGllcmNlSgQIAxAEIksKFlN0YXJ0RG9tTWlycm9yUmVzcG9uc2USDAoEcm9vdBgBIAEoCRIQCghmcmFtZV9pZBgCIAEoCRILCgNzZXEYAyABKARKBAgEEAUiQQoUU3RvcERvbU1pcnJvclJlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCRIPCgdhcGlfa2V5GAIgASgJSgQIAxAEIpcBChVHZXREb21DaGlsZHJlblJlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCRIPCgdhcGlfa2V5GAIgASgJEhcKD2JhY2tlbmRfbm9kZV9pZBgDIAEoBRIVCghmcmFtZV9pZBgEIAEoCUgAiAEBEhIKBWRlcHRoGAUgASgFSAGIAQFCCwoJX2ZyYW1lX2lkQggKBl9kZXB0aCI3ChZHZXREb21DaGlsZHJlblJlc3BvbnNlEhAKCGNoaWxkcmVuGAEgASgJEgsKA3NlcRgCIAEoBCJ8ChhSZWxlYXNlRG9tU3VidHJlZVJlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCRIPCgdhcGlfa2V5GAIgASgJEhcKD2JhY2tlbmRfbm9kZV9pZBgDIAEoBRIVCghmcmFtZV9pZBgEIAEoCUgAiAEBQgsKCV9mcmFtZV9pZCJ4ChRSZXZlYWxEb21Ob2RlUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSFwoPYmFja2VuZF9ub2RlX2lkGAMgASgFEhUKCGZyYW1lX2lkGAQgASgJSACIAQFCCwoJX2ZyYW1lX2lkIjIKFVJldmVhbERvbU5vZGVSZXNwb25zZRIMCgRwYXRoGAEgASgJEgsKA3NlcRgCIAEoBCJgChVHZXREb21SZXZpc2lvblJlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCRIPCgdhcGlfa2V5GAIgASgJEhUKCGZyYW1lX2lkGAMgASgJSACIAQFCCwoJX2ZyYW1lX2lkIioKFkdldERvbVJldmlzaW9uUmVzcG9uc2USEAoIcmV2aXNpb24YASABKAQiPQoWU3RyZWFtRG9tRXZlbnRzUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkiOQoJRG9tVXBkYXRlEhAKCGZyYW1lX2lkGAEgASgJEgsKA3NlcRgCIAEoBBINCgVlZGl0cxgDIAEoCSIhCglEb21SZXN5bmMSDgoGcmVhc29uGAIgASgJSgQIARACInsKCERvbUV2ZW50EiwKBnVwZGF0ZRgBIAEoCzIaLmJyb3dzZXJzY2FsZS52MS5Eb21VcGRhdGVIABIsCgZyZXN5bmMYAiABKAsyGi5icm93c2Vyc2NhbGUudjEuRG9tUmVzeW5jSABCBwoFZXZlbnRKBAgDEARKBAgEEAUiZgoYSW5zcGVjdEF0UG9zaXRpb25SZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCRIPCgdwYWdlX2lkGAMgASgJEgkKAXgYBCABKAESCQoBeRgFIAEoASKpAQoZSW5zcGVjdEF0UG9zaXRpb25SZXNwb25zZRIXCg9iYWNrZW5kX25vZGVfaWQYASABKAUSEAoIZnJhbWVfaWQYAiABKAkSEAoIdGFnX25hbWUYAyABKAkSFAoMdGV4dF9jb250ZW50GAQgASgJEhIKCmlzX3Zpc2libGUYBSABKAgSJQoGYm91bmRzGAYgASgLMhUuYnJvd3NlcnNjYWxlLnYxLlJlY3QiiQEKFEhpZ2hsaWdodE5vZGVSZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCRIPCgdwYWdlX2lkGAMgASgJEhcKD2JhY2tlbmRfbm9kZV9pZBgEIAEoBRIVCghmcmFtZV9pZBgFIAEoCUgAiAEBQgsKCV9mcmFtZV9pZCJXChFJbnNlcnRUZXh0UmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSDwoHcGFnZV9pZBgDIAEoCRIMCgR0ZXh0GAQgASgJInsKC1R5cGVSZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCRIPCgdwYWdlX2lkGAMgASgJEgwKBHRleHQYBCABKAkSGAoLY2xlYXJfZmlyc3QYBSABKAhIAIgBAUIOCgxfY2xlYXJfZmlyc3QiugEKD1ByZXNzS2V5UmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSDwoHcGFnZV9pZBgDIAEoCRILCgNrZXkYBCABKAkSEQoEY29kZRgFIAEoCUgAiAEBEhYKCW1vZGlmaWVycxgGIAEoBUgBiAEBEhUKCGxvY2F0aW9uGAcgASgFSAKIAQFCBwoFX2NvZGVCDAoKX21vZGlmaWVyc0ILCglfbG9jYXRpb24ivAEKEVJlbGVhc2VLZXlSZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCRIPCgdwYWdlX2lkGAMgASgJEgsKA2tleRgEIAEoCRIRCgRjb2RlGAUgASgJSACIAQESFgoJbW9kaWZpZXJzGAYgASgFSAGIAQESFQoIbG9jYXRpb24YByABKAVIAogBAUIHCgVfY29kZUIMCgpfbW9kaWZpZXJzQgsKCV9sb2NhdGlvbiJLChNHZXRTZWxlY3Rpb25SZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCRIPCgdwYWdlX2lkGAMgASgJIiQKFEdldFNlbGVjdGlvblJlc3BvbnNlEgwKBHRleHQYASABKAkiiwEKEVNjcmVlbnNob3RSZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCRIPCgdwYWdlX2lkGAMgASgJEhMKBmZvcm1hdBgEIAEoCUgAiAEBEhQKB3F1YWxpdHkYBSABKAVIAYgBAUIJCgdfZm9ybWF0QgoKCF9xdWFsaXR5IkgKElNjcmVlbnNob3RSZXNwb25zZRITCgtkYXRhX2Jhc2U2NBgBIAEoCRINCgV3aWR0aBgCIAEoBRIOCgZoZWlnaHQYAyABKAUikwMKEVJlYWRDYW52YXNSZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCRIPCgdwYWdlX2lkGAMgASgJEhwKD2JhY2tlbmRfbm9kZV9pZBgEIAEoBUgAiAEBEhUKCHNlbGVjdG9yGAUgASgJSAGIAQESGgoNanNfZXhwcmVzc2lvbhgGIAEoCUgCiAEBEhUKCGZyYW1lX2lkGAcgASgJSAOIAQESEwoGZm9ybWF0GAggASgJSASIAQESFAoHcXVhbGl0eRgJIAEoBUgFiAEBEg8KAnN4GAogASgFSAaIAQESDwoCc3kYCyABKAVIB4gBARIPCgJzdxgMIAEoBUgIiAEBEg8KAnNoGA0gASgFSAmIAQFCEgoQX2JhY2tlbmRfbm9kZV9pZEILCglfc2VsZWN0b3JCEAoOX2pzX2V4cHJlc3Npb25CCwoJX2ZyYW1lX2lkQgkKB19mb3JtYXRCCgoIX3F1YWxpdHlCBQoDX3N4QgUKA19zeUIFCgNfc3dCBQoDX3NoIpoBChJSZWFkQ2FudmFzUmVzcG9uc2USDwoHc3VjY2VzcxgBIAEoCBIQCghmcmFtZV9pZBgCIAEoCRIXCg9iYWNrZW5kX25vZGVfaWQYAyABKAUSEwoLZGF0YV9iYXNlNjQYBCABKAkSDQoFd2lkdGgYBSABKAUSDgoGaGVpZ2h0GAYgASgFEhQKDG9yaWdpbl9jbGVhbhgHIAEoCCJkChNTb2x2ZUNhcHRjaGFSZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCRISCgp0aW1lb3V0X21zGAMgASgFEhQKDHJldHJ5X2Ftb3VudBgEIAEoBSImChRTb2x2ZUNhcHRjaGFSZXNwb25zZRIOCgZyZXN1bHQYASABKAkiZQoJSWNlU2VydmVyEgwKBHVybHMYASADKAkSFQoIdXNlcm5hbWUYAiABKAlIAIgBARIXCgpjcmVkZW50aWFsGAMgASgJSAGIAQFCCwoJX3VzZXJuYW1lQg0KC19jcmVkZW50aWFsIj0KFkdldFN0cmVhbUNvbmZpZ1JlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCRIPCgdhcGlfa2V5GAIgASgJIkoKF0dldFN0cmVhbUNvbmZpZ1Jlc3BvbnNlEi8KC2ljZV9zZXJ2ZXJzGAEgAygLMhouYnJvd3NlcnNjYWxlLnYxLkljZVNlcnZlciJMChJTdGFydFN0cmVhbVJlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCRIPCgdhcGlfa2V5GAIgASgJEhEKCW9mZmVyX3NkcBgDIAEoCSJSChNTdGFydFN0cmVhbVJlc3BvbnNlEhIKCmFuc3dlcl9zZHAYASABKAkSJwoIdmlld3BvcnQYAiABKAsyFS5icm93c2Vyc2NhbGUudjEuUmVjdCI4ChFTdG9wU3RyZWFtUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkiFAoSU3RvcFN0cmVhbVJlc3BvbnNlIkMKDlNjcmlwdExvZ0VudHJ5Eg0KBWxldmVsGAEgASgJEg8KB21lc3NhZ2UYAiABKAkSEQoJdGltZXN0YW1wGAMgASgDIkcKEFJ1blNjcmlwdFJlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCRIPCgdhcGlfa2V5GAIgASgJEg4KBnNvdXJjZRgDIAEoCSKFAQoRUnVuU2NyaXB0UmVzcG9uc2USDwoHc3VjY2VzcxgBIAEoCBIOCgZyZXN1bHQYAiABKAkSDgoGcnVuX2lkGAMgASgJEiwKA2xvZxgEIAMoCzIfLmJyb3dzZXJzY2FsZS52MS5TY3JpcHRMb2dFbnRyeRIRCgl0cnVuY2F0ZWQYBSABKAgiSQoSU3RhcnRTY3JpcHRSZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCRIOCgZzb3VyY2UYAyABKAkiJQoTU3RhcnRTY3JpcHRSZXNwb25zZRIOCgZydW5faWQYASABKAkiSQoSU3RvcFNjcmlwdHNSZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSDwoHYXBpX2tleRgCIAEoCRIOCgZydW5faWQYAyABKAkiJgoTU3RvcFNjcmlwdHNSZXNwb25zZRIPCgdzdG9wcGVkGAEgASgFIi8KCVNjcmlwdFJ1bhIOCgZydW5faWQYASABKAkSEgoKcnVubmluZ19tcxgCIAEoAyI8ChVMaXN0U2NyaXB0UnVuc1JlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCRIPCgdhcGlfa2V5GAIgASgJIkIKFkxpc3RTY3JpcHRSdW5zUmVzcG9uc2USKAoEcnVucxgBIAMoCzIaLmJyb3dzZXJzY2FsZS52MS5TY3JpcHRSdW4iUAoZU3RyZWFtU2NyaXB0RXZlbnRzUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2FwaV9rZXkYAiABKAkSDgoGcnVuX2lkGAMgASgJIkoKCVNjcmlwdExvZxIOCgZydW5faWQYASABKAkSLQoEbGluZRgCIAEoCzIfLmJyb3dzZXJzY2FsZS52MS5TY3JpcHRMb2dFbnRyeSJSCg5TY3JpcHRGaW5pc2hlZBIOCgZydW5faWQYASABKAkSDwoHc3VjY2VzcxgCIAEoCBIOCgZyZXN1bHQYAyABKAkSDwoHc3RvcHBlZBgEIAEoCCKHAQoLU2NyaXB0RXZlbnQSKQoDbG9nGAEgASgLMhouYnJvd3NlcnNjYWxlLnYxLlNjcmlwdExvZ0gAEjMKCGZpbmlzaGVkGAIgASgLMh8uYnJvd3NlcnNjYWxlLnYxLlNjcmlwdEZpbmlzaGVkSAASDwoHZHJvcHBlZBgDIAEoBEIHCgVldmVudDLLKAoHQnJvd3NlchJECghTZXRQcm94eRIgLmJyb3dzZXJzY2FsZS52MS5TZXRQcm94eVJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkSTwoIR2V0UGFnZXMSIC5icm93c2Vyc2NhbGUudjEuR2V0UGFnZXNSZXF1ZXN0GiEuYnJvd3NlcnNjYWxlLnYxLkdldFBhZ2VzUmVzcG9uc2USTwoITmF2aWdhdGUSIC5icm93c2Vyc2NhbGUudjEuTmF2aWdhdGVSZXF1ZXN0GiEuYnJvd3NlcnNjYWxlLnYxLk5hdmlnYXRlUmVzcG9uc2USRAoITG9hZEhUTUwSIC5icm93c2Vyc2NhbGUudjEuTG9hZEhUTUxSZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5Ek8KCEV2YWx1YXRlEiAuYnJvd3NlcnNjYWxlLnYxLkV2YWx1YXRlUmVxdWVzdBohLmJyb3dzZXJzY2FsZS52MS5FdmFsdWF0ZVJlc3BvbnNlEkAKA1J1bhIbLmJyb3dzZXJzY2FsZS52MS5SdW5SZXF1ZXN0GhwuYnJvd3NlcnNjYWxlLnYxLlJ1blJlc3BvbnNlEkwKCldhaXRGb3JBbnkSIS5icm93c2Vyc2NhbGUudjEuV2FpdEZvckFueVBhcmFtcxobLmJyb3dzZXJzY2FsZS52MS5XYWl0UmVzdWx0ElkKDFNlbGVjdE9wdGlvbhIkLmJyb3dzZXJzY2FsZS52MS5TZWxlY3RPcHRpb25SZXF1ZXN0GiMuYnJvd3NlcnNjYWxlLnYxLlNlbGVjdE9wdGlvblJlc3VsdBJLCghTY3JvbGxUbxIgLmJyb3dzZXJzY2FsZS52MS5TY3JvbGxUb1JlcXVlc3QaHS5icm93c2Vyc2NhbGUudjEuU2Nyb2xsUmVzdWx0EkUKBk1vdmVUbxIeLmJyb3dzZXJzY2FsZS52MS5Nb3ZlVG9SZXF1ZXN0GhsuYnJvd3NlcnNjYWxlLnYxLk1vdmVSZXN1bHQSRAoFQ2xpY2sSHS5icm93c2Vyc2NhbGUudjEuQ2xpY2tSZXF1ZXN0GhwuYnJvd3NlcnNjYWxlLnYxLkNsaWNrUmVzdWx0EkEKBERyYWcSHC5icm93c2Vyc2NhbGUudjEuRHJhZ1JlcXVlc3QaGy5icm93c2Vyc2NhbGUudjEuRHJhZ1Jlc3VsdBJBCgRGaWxsEhwuYnJvd3NlcnNjYWxlLnYxLkZpbGxSZXF1ZXN0GhsuYnJvd3NlcnNjYWxlLnYxLkZpbGxSZXN1bHQSWAoLQWRkUmVhY3Rpb24SIy5icm93c2Vyc2NhbGUudjEuQWRkUmVhY3Rpb25SZXF1ZXN0GiQuYnJvd3NlcnNjYWxlLnYxLkFkZFJlYWN0aW9uUmVzcG9uc2USYQoOUmVtb3ZlUmVhY3Rpb24SJi5icm93c2Vyc2NhbGUudjEuUmVtb3ZlUmVhY3Rpb25SZXF1ZXN0GicuYnJvd3NlcnNjYWxlLnYxLlJlbW92ZVJlYWN0aW9uUmVzcG9uc2USXgoNTGlzdFJlYWN0aW9ucxIlLmJyb3dzZXJzY2FsZS52MS5MaXN0UmVhY3Rpb25zUmVxdWVzdBomLmJyb3dzZXJzY2FsZS52MS5MaXN0UmVhY3Rpb25zUmVzcG9uc2USTAoMU2V0QmxvY2tMaXN0EiQuYnJvd3NlcnNjYWxlLnYxLlNldEJsb2NrTGlzdFJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkSUAoOU2V0U3RhdGljUGF0aHMSJi5icm93c2Vyc2NhbGUudjEuU2V0U3RhdGljUGF0aHNSZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5EmoKEVdhaXRGb3JBbnlSZXF1ZXN0EikuYnJvd3NlcnNjYWxlLnYxLldhaXRGb3JBbnlSZXF1ZXN0UmVxdWVzdBoqLmJyb3dzZXJzY2FsZS52MS5XYWl0Rm9yQW55UmVxdWVzdFJlc3BvbnNlEm0KEldhaXRGb3JBbnlSZXNwb25zZRIqLmJyb3dzZXJzY2FsZS52MS5XYWl0Rm9yQW55UmVzcG9uc2VSZXF1ZXN0GisuYnJvd3NlcnNjYWxlLnYxLldhaXRGb3JBbnlSZXNwb25zZVJlc3BvbnNlEl4KDU1vZGlmeVJlcXVlc3QSJS5icm93c2Vyc2NhbGUudjEuTW9kaWZ5UmVxdWVzdFJlcXVlc3QaJi5icm93c2Vyc2NhbGUudjEuTW9kaWZ5UmVxdWVzdFJlc3BvbnNlEloKE1N0YXJ0TmV0d29ya0NhcHR1cmUSKy5icm93c2Vyc2NhbGUudjEuU3RhcnROZXR3b3JrQ2FwdHVyZVJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkSbQoSU3RvcE5ldHdvcmtDYXB0dXJlEiouYnJvd3NlcnNjYWxlLnYxLlN0b3BOZXR3b3JrQ2FwdHVyZVJlcXVlc3QaKy5icm93c2Vyc2NhbGUudjEuU3RvcE5ldHdvcmtDYXB0dXJlUmVzcG9uc2UScQoWU3RyZWFtTmV0d29ya0V4Y2hhbmdlcxIuLmJyb3dzZXJzY2FsZS52MS5TdHJlYW1OZXR3b3JrRXhjaGFuZ2VzUmVxdWVzdBolLmJyb3dzZXJzY2FsZS52MS5OZXR3b3JrRXhjaGFuZ2VFdmVudDABElUKCkdldENvb2tpZXMSIi5icm93c2Vyc2NhbGUudjEuR2V0Q29va2llc1JlcXVlc3QaIy5icm93c2Vyc2NhbGUudjEuR2V0Q29va2llc1Jlc3BvbnNlEkgKClNldENvb2tpZXMSIi5icm93c2Vyc2NhbGUudjEuU2V0Q29va2llc1JlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkSTAoMQ2xlYXJDb29raWVzEiQuYnJvd3NlcnNjYWxlLnYxLkNsZWFyQ29va2llc1JlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkSVQoKR2V0U3RvcmFnZRIiLmJyb3dzZXJzY2FsZS52MS5HZXRTdG9yYWdlUmVxdWVzdBojLmJyb3dzZXJzY2FsZS52MS5HZXRTdG9yYWdlUmVzcG9uc2USSAoKU2V0U3RvcmFnZRIiLmJyb3dzZXJzY2FsZS52MS5TZXRTdG9yYWdlUmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eRJMCgxDbGVhclN0b3JhZ2USJC5icm93c2Vyc2NhbGUudjEuQ2xlYXJTdG9yYWdlUmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eRJhCg5HZXRBdXRoU2Vzc2lvbhImLmJyb3dzZXJzY2FsZS52MS5HZXRBdXRoU2Vzc2lvblJlcXVlc3QaJy5icm93c2Vyc2NhbGUudjEuR2V0QXV0aFNlc3Npb25SZXNwb25zZRJQCg5TZXRBdXRoU2Vzc2lvbhImLmJyb3dzZXJzY2FsZS52MS5TZXRBdXRoU2Vzc2lvblJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkSSQoGR2V0RE9NEh4uYnJvd3NlcnNjYWxlLnYxLkdldERPTVJlcXVlc3QaHy5icm93c2Vyc2NhbGUudjEuR2V0RE9NUmVzcG9uc2USVQoKR2V0RE9NSGFzaBIiLmJyb3dzZXJzY2FsZS52MS5HZXRET01IYXNoUmVxdWVzdBojLmJyb3dzZXJzY2FsZS52MS5HZXRET01IYXNoUmVzcG9uc2USYQoOR2V0T2JzZXJ2YXRpb24SJi5icm93c2Vyc2NhbGUudjEuR2V0T2JzZXJ2YXRpb25SZXF1ZXN0GicuYnJvd3NlcnNjYWxlLnYxLkdldE9ic2VydmF0aW9uUmVzcG9uc2USagoRSW5zcGVjdEF0UG9zaXRpb24SKS5icm93c2Vyc2NhbGUudjEuSW5zcGVjdEF0UG9zaXRpb25SZXF1ZXN0GiouYnJvd3NlcnNjYWxlLnYxLkluc3BlY3RBdFBvc2l0aW9uUmVzcG9uc2USTgoNSGlnaGxpZ2h0Tm9kZRIlLmJyb3dzZXJzY2FsZS52MS5IaWdobGlnaHROb2RlUmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eRJhCg5TdGFydERvbU1pcnJvchImLmJyb3dzZXJzY2FsZS52MS5TdGFydERvbU1pcnJvclJlcXVlc3QaJy5icm93c2Vyc2NhbGUudjEuU3RhcnREb21NaXJyb3JSZXNwb25zZRJOCg1TdG9wRG9tTWlycm9yEiUuYnJvd3NlcnNjYWxlLnYxLlN0b3BEb21NaXJyb3JSZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5EmEKDkdldERvbUNoaWxkcmVuEiYuYnJvd3NlcnNjYWxlLnYxLkdldERvbUNoaWxkcmVuUmVxdWVzdBonLmJyb3dzZXJzY2FsZS52MS5HZXREb21DaGlsZHJlblJlc3BvbnNlElYKEVJlbGVhc2VEb21TdWJ0cmVlEikuYnJvd3NlcnNjYWxlLnYxLlJlbGVhc2VEb21TdWJ0cmVlUmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eRJeCg1SZXZlYWxEb21Ob2RlEiUuYnJvd3NlcnNjYWxlLnYxLlJldmVhbERvbU5vZGVSZXF1ZXN0GiYuYnJvd3NlcnNjYWxlLnYxLlJldmVhbERvbU5vZGVSZXNwb25zZRJhCg5HZXREb21SZXZpc2lvbhImLmJyb3dzZXJzY2FsZS52MS5HZXREb21SZXZpc2lvblJlcXVlc3QaJy5icm93c2Vyc2NhbGUudjEuR2V0RG9tUmV2aXNpb25SZXNwb25zZRJXCg9TdHJlYW1Eb21FdmVudHMSJy5icm93c2Vyc2NhbGUudjEuU3RyZWFtRG9tRXZlbnRzUmVxdWVzdBoZLmJyb3dzZXJzY2FsZS52MS5Eb21FdmVudDABElUKClNjcmVlbnNob3QSIi5icm93c2Vyc2NhbGUudjEuU2NyZWVuc2hvdFJlcXVlc3QaIy5icm93c2Vyc2NhbGUudjEuU2NyZWVuc2hvdFJlc3BvbnNlElUKClJlYWRDYW52YXMSIi5icm93c2Vyc2NhbGUudjEuUmVhZENhbnZhc1JlcXVlc3QaIy5icm93c2Vyc2NhbGUudjEuUmVhZENhbnZhc1Jlc3BvbnNlEkgKCkluc2VydFRleHQSIi5icm93c2Vyc2NhbGUudjEuSW5zZXJ0VGV4dFJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkSPAoEVHlwZRIcLmJyb3dzZXJzY2FsZS52MS5UeXBlUmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eRJECghQcmVzc0tleRIgLmJyb3dzZXJzY2FsZS52MS5QcmVzc0tleVJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkSSAoKUmVsZWFzZUtleRIiLmJyb3dzZXJzY2FsZS52MS5SZWxlYXNlS2V5UmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eRJbCgxHZXRTZWxlY3Rpb24SJC5icm93c2Vyc2NhbGUudjEuR2V0U2VsZWN0aW9uUmVxdWVzdBolLmJyb3dzZXJzY2FsZS52MS5HZXRTZWxlY3Rpb25SZXNwb25zZRJbCgxTb2x2ZUNhcHRjaGESJC5icm93c2Vyc2NhbGUudjEuU29sdmVDYXB0Y2hhUmVxdWVzdBolLmJyb3dzZXJzY2FsZS52MS5Tb2x2ZUNhcHRjaGFSZXNwb25zZRJkCg9HZXRTdHJlYW1Db25maWcSJy5icm93c2Vyc2NhbGUudjEuR2V0U3RyZWFtQ29uZmlnUmVxdWVzdBooLmJyb3dzZXJzY2FsZS52MS5HZXRTdHJlYW1Db25maWdSZXNwb25zZRJYCgtTdGFydFN0cmVhbRIjLmJyb3dzZXJzY2FsZS52MS5TdGFydFN0cmVhbVJlcXVlc3QaJC5icm93c2Vyc2NhbGUudjEuU3RhcnRTdHJlYW1SZXNwb25zZRJVCgpTdG9wU3RyZWFtEiIuYnJvd3NlcnNjYWxlLnYxLlN0b3BTdHJlYW1SZXF1ZXN0GiMuYnJvd3NlcnNjYWxlLnYxLlN0b3BTdHJlYW1SZXNwb25zZRJSCglSdW5TY3JpcHQSIS5icm93c2Vyc2NhbGUudjEuUnVuU2NyaXB0UmVxdWVzdBoiLmJyb3dzZXJzY2FsZS52MS5SdW5TY3JpcHRSZXNwb25zZRJYCgtTdGFydFNjcmlwdBIjLmJyb3dzZXJzY2FsZS52MS5TdGFydFNjcmlwdFJlcXVlc3QaJC5icm93c2Vyc2NhbGUudjEuU3RhcnRTY3JpcHRSZXNwb25zZRJYCgtTdG9wU2NyaXB0cxIjLmJyb3dzZXJzY2FsZS52MS5TdG9wU2NyaXB0c1JlcXVlc3QaJC5icm93c2Vyc2NhbGUudjEuU3RvcFNjcmlwdHNSZXNwb25zZRJhCg5MaXN0U2NyaXB0UnVucxImLmJyb3dzZXJzY2FsZS52MS5MaXN0U2NyaXB0UnVuc1JlcXVlc3QaJy5icm93c2Vyc2NhbGUudjEuTGlzdFNjcmlwdFJ1bnNSZXNwb25zZRJgChJTdHJlYW1TY3JpcHRFdmVudHMSKi5icm93c2Vyc2NhbGUudjEuU3RyZWFtU2NyaXB0RXZlbnRzUmVxdWVzdBocLmJyb3dzZXJzY2FsZS52MS5TY3JpcHRFdmVudDABQjNaMWdpdGh1Yi5jb20vYnJvd3NlcnNjYWxlL2Jyb3dzZXJzY2FsZS1nby9nZW5lcmF0ZWRiBnByb3RvMw", [file_google_protobuf_empty]);
|
|
3803
3803
|
var HeaderSchema = /* @__PURE__ */ messageDesc(file_wrc, 3);
|
|
3804
3804
|
var HeaderModificationSchema = /* @__PURE__ */ messageDesc(file_wrc, 6);
|
|
3805
3805
|
var CookiePartitionKeySchema = /* @__PURE__ */ messageDesc(file_wrc, 7);
|
|
@@ -3825,30 +3825,49 @@ var SetStaticPathsRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 53);
|
|
|
3825
3825
|
var WaitForAnyRequestRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 54);
|
|
3826
3826
|
var WaitForAnyResponseRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 56);
|
|
3827
3827
|
var ModifyRequestRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 58);
|
|
3828
|
-
var
|
|
3829
|
-
var
|
|
3830
|
-
var
|
|
3831
|
-
var
|
|
3832
|
-
var
|
|
3833
|
-
var
|
|
3834
|
-
var
|
|
3835
|
-
var
|
|
3836
|
-
var
|
|
3837
|
-
var
|
|
3838
|
-
var
|
|
3839
|
-
var
|
|
3840
|
-
var
|
|
3841
|
-
var
|
|
3842
|
-
var
|
|
3843
|
-
var
|
|
3844
|
-
var
|
|
3845
|
-
var
|
|
3846
|
-
var
|
|
3847
|
-
var
|
|
3848
|
-
var
|
|
3849
|
-
var
|
|
3850
|
-
var
|
|
3851
|
-
var
|
|
3828
|
+
var StartNetworkCaptureRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 60);
|
|
3829
|
+
var StopNetworkCaptureRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 61);
|
|
3830
|
+
var StreamNetworkExchangesRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 63);
|
|
3831
|
+
var GetCookiesRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 66);
|
|
3832
|
+
var SetCookiesRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 68);
|
|
3833
|
+
var ClearCookiesRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 69);
|
|
3834
|
+
var StorageItemSchema = /* @__PURE__ */ messageDesc(file_wrc, 70);
|
|
3835
|
+
var StorageOriginEntrySchema = /* @__PURE__ */ messageDesc(file_wrc, 71);
|
|
3836
|
+
var GetStorageRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 72);
|
|
3837
|
+
var SetStorageRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 74);
|
|
3838
|
+
var ClearStorageRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 75);
|
|
3839
|
+
var DbscSessionSchema = /* @__PURE__ */ messageDesc(file_wrc, 76);
|
|
3840
|
+
var AuthSessionSchema = /* @__PURE__ */ messageDesc(file_wrc, 77);
|
|
3841
|
+
var GetAuthSessionRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 78);
|
|
3842
|
+
var SetAuthSessionRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 80);
|
|
3843
|
+
var GetDOMRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 81);
|
|
3844
|
+
var GetObservationRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 83);
|
|
3845
|
+
var GetDOMHashRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 85);
|
|
3846
|
+
var StartDomMirrorRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 87);
|
|
3847
|
+
var StopDomMirrorRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 89);
|
|
3848
|
+
var GetDomChildrenRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 90);
|
|
3849
|
+
var ReleaseDomSubtreeRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 92);
|
|
3850
|
+
var RevealDomNodeRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 93);
|
|
3851
|
+
var GetDomRevisionRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 95);
|
|
3852
|
+
var StreamDomEventsRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 97);
|
|
3853
|
+
var InspectAtPositionRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 101);
|
|
3854
|
+
var HighlightNodeRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 103);
|
|
3855
|
+
var InsertTextRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 104);
|
|
3856
|
+
var TypeRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 105);
|
|
3857
|
+
var PressKeyRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 106);
|
|
3858
|
+
var ReleaseKeyRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 107);
|
|
3859
|
+
var GetSelectionRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 108);
|
|
3860
|
+
var ScreenshotRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 110);
|
|
3861
|
+
var ReadCanvasRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 112);
|
|
3862
|
+
var SolveCaptchaRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 114);
|
|
3863
|
+
var GetStreamConfigRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 117);
|
|
3864
|
+
var StartStreamRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 119);
|
|
3865
|
+
var StopStreamRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 121);
|
|
3866
|
+
var RunScriptRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 124);
|
|
3867
|
+
var StartScriptRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 126);
|
|
3868
|
+
var StopScriptsRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 128);
|
|
3869
|
+
var ListScriptRunsRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 131);
|
|
3870
|
+
var StreamScriptEventsRequestSchema = /* @__PURE__ */ messageDesc(file_wrc, 133);
|
|
3852
3871
|
var Browser = /* @__PURE__ */ serviceDesc(file_wrc, 0);
|
|
3853
3872
|
|
|
3854
3873
|
// src/defaults.ts
|
|
@@ -4302,6 +4321,37 @@ function interceptedRequestFromProto(r) {
|
|
|
4302
4321
|
resourceType: r.resourceType
|
|
4303
4322
|
};
|
|
4304
4323
|
}
|
|
4324
|
+
function networkExchangeFromProto(e) {
|
|
4325
|
+
return {
|
|
4326
|
+
requestId: e.requestId,
|
|
4327
|
+
chainId: e.chainId,
|
|
4328
|
+
redirectIndex: e.redirectIndex,
|
|
4329
|
+
frameId: e.frameId,
|
|
4330
|
+
isOopif: e.isOopif,
|
|
4331
|
+
resourceType: e.resourceType,
|
|
4332
|
+
method: e.method,
|
|
4333
|
+
url: e.url,
|
|
4334
|
+
initiatorUrl: e.initiatorUrl,
|
|
4335
|
+
requestHeaders: e.requestHeaders.map(headerFromProto),
|
|
4336
|
+
requestHeadersAreWire: e.requestHeadersAreWire,
|
|
4337
|
+
requestBody: e.requestBody,
|
|
4338
|
+
requestBodyTruncated: e.requestBodyTruncated,
|
|
4339
|
+
hasResponse: e.hasResponse,
|
|
4340
|
+
statusCode: e.statusCode,
|
|
4341
|
+
statusText: e.statusText,
|
|
4342
|
+
mimeType: e.mimeType,
|
|
4343
|
+
protocol: e.protocol,
|
|
4344
|
+
remoteAddress: e.remoteAddress,
|
|
4345
|
+
servedFrom: e.servedFrom,
|
|
4346
|
+
responseHeaders: e.responseHeaders.map(headerFromProto),
|
|
4347
|
+
responseHeadersAreWire: e.responseHeadersAreWire,
|
|
4348
|
+
responseBody: e.responseBody,
|
|
4349
|
+
responseBodyTruncated: e.responseBodyTruncated,
|
|
4350
|
+
responseBodyCaptured: e.responseBodyCaptured,
|
|
4351
|
+
encodedDataLength: Number(e.encodedDataLength),
|
|
4352
|
+
error: e.error
|
|
4353
|
+
};
|
|
4354
|
+
}
|
|
4305
4355
|
function interceptedResponseFromProto(r) {
|
|
4306
4356
|
if (!r) return null;
|
|
4307
4357
|
return {
|
|
@@ -4373,6 +4423,35 @@ function storageEntriesToProto(storage) {
|
|
|
4373
4423
|
return msg;
|
|
4374
4424
|
});
|
|
4375
4425
|
}
|
|
4426
|
+
function authSessionFromProto(s) {
|
|
4427
|
+
const out = {};
|
|
4428
|
+
if (s.gaiaId !== void 0) out.gaiaId = s.gaiaId;
|
|
4429
|
+
if (s.email !== void 0) out.email = s.email;
|
|
4430
|
+
if (s.refreshToken !== void 0) out.refreshToken = s.refreshToken;
|
|
4431
|
+
if (s.wrappedBindingKey !== void 0) out.wrappedBindingKey = s.wrappedBindingKey;
|
|
4432
|
+
if (s.signinScopedDeviceId !== void 0) out.signinScopedDeviceId = s.signinScopedDeviceId;
|
|
4433
|
+
if (s.syncConsent !== void 0) out.syncConsent = s.syncConsent;
|
|
4434
|
+
if (s.dbscSessions.length > 0) {
|
|
4435
|
+
out.dbscSessions = s.dbscSessions.map((d) => ({ site: d.site, session: d.session }));
|
|
4436
|
+
}
|
|
4437
|
+
return out;
|
|
4438
|
+
}
|
|
4439
|
+
function authSessionToProto(s) {
|
|
4440
|
+
const msg = create(AuthSessionSchema);
|
|
4441
|
+
if (s.gaiaId !== void 0) msg.gaiaId = s.gaiaId;
|
|
4442
|
+
if (s.email !== void 0) msg.email = s.email;
|
|
4443
|
+
if (s.refreshToken !== void 0) msg.refreshToken = s.refreshToken;
|
|
4444
|
+
if (s.wrappedBindingKey !== void 0) msg.wrappedBindingKey = s.wrappedBindingKey;
|
|
4445
|
+
if (s.signinScopedDeviceId !== void 0) msg.signinScopedDeviceId = s.signinScopedDeviceId;
|
|
4446
|
+
if (s.syncConsent !== void 0) msg.syncConsent = s.syncConsent;
|
|
4447
|
+
msg.dbscSessions = (s.dbscSessions ?? []).map((d) => {
|
|
4448
|
+
const item = create(DbscSessionSchema);
|
|
4449
|
+
item.site = d.site;
|
|
4450
|
+
item.session = d.session;
|
|
4451
|
+
return item;
|
|
4452
|
+
});
|
|
4453
|
+
return msg;
|
|
4454
|
+
}
|
|
4376
4455
|
function splitRequestPatterns(patterns) {
|
|
4377
4456
|
const urls = new Array(patterns.length);
|
|
4378
4457
|
let any = false;
|
|
@@ -4400,6 +4479,793 @@ function headerModsToProto(mods) {
|
|
|
4400
4479
|
});
|
|
4401
4480
|
}
|
|
4402
4481
|
|
|
4482
|
+
// src/network-capture.ts
|
|
4483
|
+
var NetworkCapture = class {
|
|
4484
|
+
/** @internal Constructed by CloudBrowser; not part of the public API. */
|
|
4485
|
+
constructor(init) {
|
|
4486
|
+
this.armed = false;
|
|
4487
|
+
this.stopped = false;
|
|
4488
|
+
this.failure = null;
|
|
4489
|
+
this.droppedCount = 0;
|
|
4490
|
+
this.abort = init.abort;
|
|
4491
|
+
this.disarm = init.disarm;
|
|
4492
|
+
this.finished = this.pump(init.stream, init.onExchange);
|
|
4493
|
+
}
|
|
4494
|
+
/**
|
|
4495
|
+
* @internal Marks the capture as armed by this handle, so stop() disarms it
|
|
4496
|
+
* server-side. Views that only attached to someone else's capture stay
|
|
4497
|
+
* unarmed and merely detach.
|
|
4498
|
+
*/
|
|
4499
|
+
arm() {
|
|
4500
|
+
this.armed = true;
|
|
4501
|
+
}
|
|
4502
|
+
/**
|
|
4503
|
+
* Reads the stream and calls the handler for each exchange. It never rejects:
|
|
4504
|
+
* a failure is recorded for {@link NetworkCapture.error} instead, so nothing
|
|
4505
|
+
* surfaces as an unhandled rejection.
|
|
4506
|
+
*/
|
|
4507
|
+
async pump(stream, onExchange) {
|
|
4508
|
+
try {
|
|
4509
|
+
for await (const event of stream) {
|
|
4510
|
+
if (event.dropped > 0n) {
|
|
4511
|
+
this.droppedCount = Number(event.dropped);
|
|
4512
|
+
}
|
|
4513
|
+
if (!event.exchange) continue;
|
|
4514
|
+
onExchange(networkExchangeFromProto(event.exchange));
|
|
4515
|
+
}
|
|
4516
|
+
} catch (err) {
|
|
4517
|
+
if (!this.stopped) {
|
|
4518
|
+
this.failure = err instanceof Error ? err : new Error(String(err));
|
|
4519
|
+
}
|
|
4520
|
+
}
|
|
4521
|
+
}
|
|
4522
|
+
/**
|
|
4523
|
+
* Why the capture ended: null while it is still running, and after a clean
|
|
4524
|
+
* stop or the session ending normally.
|
|
4525
|
+
*/
|
|
4526
|
+
get error() {
|
|
4527
|
+
return this.failure;
|
|
4528
|
+
}
|
|
4529
|
+
/**
|
|
4530
|
+
* How many exchanges the server discarded because this reader fell behind.
|
|
4531
|
+
* Anything above zero means the log has holes: make the handler cheaper,
|
|
4532
|
+
* narrow `patterns`, or stop capturing bodies.
|
|
4533
|
+
*/
|
|
4534
|
+
get dropped() {
|
|
4535
|
+
return this.droppedCount;
|
|
4536
|
+
}
|
|
4537
|
+
/**
|
|
4538
|
+
* Resolves once the capture ends — {@link NetworkCapture.stop}, a dead
|
|
4539
|
+
* session or a transport failure.
|
|
4540
|
+
*
|
|
4541
|
+
* Use it to capture for as long as the session lives. It is not needed when
|
|
4542
|
+
* you drive the browser yourself and call stop when done.
|
|
4543
|
+
*
|
|
4544
|
+
* @throws the transport failure that ended the capture, if any
|
|
4545
|
+
*/
|
|
4546
|
+
async wait() {
|
|
4547
|
+
await this.finished;
|
|
4548
|
+
if (this.failure) throw this.failure;
|
|
4549
|
+
}
|
|
4550
|
+
/**
|
|
4551
|
+
* Ends the capture. Idempotent, and safe to call from a `finally`.
|
|
4552
|
+
*
|
|
4553
|
+
* Once it resolves the handler is no longer running, so data it collected is
|
|
4554
|
+
* complete. Views from {@link CloudBrowser.streamNetworkExchanges} only
|
|
4555
|
+
* detach — they never disarm a capture other readers may share.
|
|
4556
|
+
*
|
|
4557
|
+
* To stop from inside the handler, call
|
|
4558
|
+
* {@link CloudBrowser.stopNetworkCapture} instead: stop awaits the reader,
|
|
4559
|
+
* which cannot finish while the handler it called is still running.
|
|
4560
|
+
*
|
|
4561
|
+
* @throws UNKNOWN_ERROR - the capture could not be disarmed; the local reader
|
|
4562
|
+
* is shut down regardless
|
|
4563
|
+
*/
|
|
4564
|
+
async stop() {
|
|
4565
|
+
if (this.stopped) return;
|
|
4566
|
+
this.stopped = true;
|
|
4567
|
+
try {
|
|
4568
|
+
if (this.armed) await this.disarm();
|
|
4569
|
+
} finally {
|
|
4570
|
+
this.abort.abort();
|
|
4571
|
+
await this.finished;
|
|
4572
|
+
}
|
|
4573
|
+
}
|
|
4574
|
+
};
|
|
4575
|
+
|
|
4576
|
+
// src/scripts.ts
|
|
4577
|
+
function scriptLogEntryFromProto(e) {
|
|
4578
|
+
return {
|
|
4579
|
+
level: e.level,
|
|
4580
|
+
message: e.message,
|
|
4581
|
+
timestamp: new Date(Number(e.timestamp))
|
|
4582
|
+
};
|
|
4583
|
+
}
|
|
4584
|
+
function scriptEventFromProto(event) {
|
|
4585
|
+
const payload = event.event;
|
|
4586
|
+
if (payload.case === "log") {
|
|
4587
|
+
if (!payload.value.line) return null;
|
|
4588
|
+
return {
|
|
4589
|
+
runId: payload.value.runId,
|
|
4590
|
+
log: scriptLogEntryFromProto(payload.value.line)
|
|
4591
|
+
};
|
|
4592
|
+
}
|
|
4593
|
+
if (payload.case === "finished") {
|
|
4594
|
+
return {
|
|
4595
|
+
runId: payload.value.runId,
|
|
4596
|
+
finished: {
|
|
4597
|
+
success: payload.value.success,
|
|
4598
|
+
result: payload.value.result,
|
|
4599
|
+
stopped: payload.value.stopped
|
|
4600
|
+
}
|
|
4601
|
+
};
|
|
4602
|
+
}
|
|
4603
|
+
return null;
|
|
4604
|
+
}
|
|
4605
|
+
var ScriptRun = class {
|
|
4606
|
+
/** @internal Constructed by CloudBrowser; not part of the public API. */
|
|
4607
|
+
constructor(init) {
|
|
4608
|
+
this.detached = false;
|
|
4609
|
+
this.failure = null;
|
|
4610
|
+
this.droppedCount = 0;
|
|
4611
|
+
this.result = null;
|
|
4612
|
+
this.id = init.runId;
|
|
4613
|
+
this.abort = init.abort;
|
|
4614
|
+
this.cancel = init.cancel;
|
|
4615
|
+
this.endedPromise = new Promise((resolve) => {
|
|
4616
|
+
this.ended = resolve;
|
|
4617
|
+
});
|
|
4618
|
+
this.finished = this.pump(init.stream, init.onEvent);
|
|
4619
|
+
}
|
|
4620
|
+
/**
|
|
4621
|
+
* The id the browser gave this run. Pass it to
|
|
4622
|
+
* {@link CloudBrowser.stopScripts} to cancel the run from elsewhere, or to
|
|
4623
|
+
* {@link CloudBrowser.followScript} to watch it from another tab.
|
|
4624
|
+
*/
|
|
4625
|
+
get runId() {
|
|
4626
|
+
return this.id;
|
|
4627
|
+
}
|
|
4628
|
+
/**
|
|
4629
|
+
* Reads the stream and calls the handler for each event belonging to this
|
|
4630
|
+
* run. Filtering happens here rather than server-side because the
|
|
4631
|
+
* subscription had to exist before the run id did.
|
|
4632
|
+
*
|
|
4633
|
+
* It never rejects: a failure is recorded for {@link ScriptRun.error}
|
|
4634
|
+
* instead, so nothing surfaces as an unhandled rejection.
|
|
4635
|
+
*/
|
|
4636
|
+
async pump(stream, onEvent) {
|
|
4637
|
+
try {
|
|
4638
|
+
for await (const message of stream) {
|
|
4639
|
+
if (message.dropped > 0n) {
|
|
4640
|
+
this.droppedCount = Number(message.dropped);
|
|
4641
|
+
}
|
|
4642
|
+
const event = scriptEventFromProto(message);
|
|
4643
|
+
if (!event || event.runId !== this.id) continue;
|
|
4644
|
+
onEvent(event);
|
|
4645
|
+
if (event.finished) {
|
|
4646
|
+
this.result = event.finished;
|
|
4647
|
+
this.ended();
|
|
4648
|
+
return;
|
|
4649
|
+
}
|
|
4650
|
+
}
|
|
4651
|
+
} catch (err) {
|
|
4652
|
+
if (!this.detached) {
|
|
4653
|
+
this.failure = err instanceof Error ? err : new Error(String(err));
|
|
4654
|
+
}
|
|
4655
|
+
} finally {
|
|
4656
|
+
this.ended();
|
|
4657
|
+
}
|
|
4658
|
+
}
|
|
4659
|
+
/**
|
|
4660
|
+
* Why the stream ended: null while it is still open, and after a clean stop
|
|
4661
|
+
* or the session ending normally.
|
|
4662
|
+
*/
|
|
4663
|
+
get error() {
|
|
4664
|
+
return this.failure;
|
|
4665
|
+
}
|
|
4666
|
+
/**
|
|
4667
|
+
* How many events the server discarded because this reader fell behind.
|
|
4668
|
+
* Anything above zero means the log has holes: make the handler cheaper, or
|
|
4669
|
+
* have the script print less.
|
|
4670
|
+
*/
|
|
4671
|
+
get dropped() {
|
|
4672
|
+
return this.droppedCount;
|
|
4673
|
+
}
|
|
4674
|
+
/**
|
|
4675
|
+
* Resolves once the run ends, with how it ended.
|
|
4676
|
+
*
|
|
4677
|
+
* A script that threw is an outcome, not an error: it resolves with
|
|
4678
|
+
* `success: false`. It rejects when the run's fate is unknown — the stream
|
|
4679
|
+
* broke or the session died before the script finished.
|
|
4680
|
+
*
|
|
4681
|
+
* @returns how the script ended
|
|
4682
|
+
*
|
|
4683
|
+
* @throws UNKNOWN_ERROR - the outcome could not be observed
|
|
4684
|
+
*/
|
|
4685
|
+
async wait() {
|
|
4686
|
+
await this.endedPromise;
|
|
4687
|
+
if (this.result) return this.result;
|
|
4688
|
+
if (this.failure) throw this.failure;
|
|
4689
|
+
throw new BrowserScaleError(
|
|
4690
|
+
"browserscale.ScriptRun.wait: stream ended before the run did"
|
|
4691
|
+
);
|
|
4692
|
+
}
|
|
4693
|
+
/**
|
|
4694
|
+
* Cancels the run and detaches this reader. Idempotent, and safe to call from
|
|
4695
|
+
* a `finally`.
|
|
4696
|
+
*
|
|
4697
|
+
* A script that is executing is interrupted; one parked on an await unwinds
|
|
4698
|
+
* at its next operation in the page. Either way the handler sees a `finished`
|
|
4699
|
+
* with `stopped` set, unless the local reader is torn down first.
|
|
4700
|
+
*
|
|
4701
|
+
* @throws UNKNOWN_ERROR - the run could not be cancelled server-side; the
|
|
4702
|
+
* local reader is detached regardless
|
|
4703
|
+
*/
|
|
4704
|
+
async stop() {
|
|
4705
|
+
if (this.detached) return;
|
|
4706
|
+
this.detached = true;
|
|
4707
|
+
try {
|
|
4708
|
+
await this.cancel(this.id);
|
|
4709
|
+
} finally {
|
|
4710
|
+
this.abort.abort();
|
|
4711
|
+
await this.finished;
|
|
4712
|
+
}
|
|
4713
|
+
}
|
|
4714
|
+
/**
|
|
4715
|
+
* Stops reading this run's output without cancelling the run. The script
|
|
4716
|
+
* keeps going with nobody watching, which is what makes a detached run
|
|
4717
|
+
* outlive the page that started it.
|
|
4718
|
+
*/
|
|
4719
|
+
async detach() {
|
|
4720
|
+
if (this.detached) return;
|
|
4721
|
+
this.detached = true;
|
|
4722
|
+
this.abort.abort();
|
|
4723
|
+
await this.finished;
|
|
4724
|
+
}
|
|
4725
|
+
};
|
|
4726
|
+
var ScriptFollow = class {
|
|
4727
|
+
/** @internal Constructed by CloudBrowser; not part of the public API. */
|
|
4728
|
+
constructor(init) {
|
|
4729
|
+
this.stopped = false;
|
|
4730
|
+
this.failure = null;
|
|
4731
|
+
this.droppedCount = 0;
|
|
4732
|
+
this.abort = init.abort;
|
|
4733
|
+
this.finished = this.pump(init.stream, init.onEvent);
|
|
4734
|
+
}
|
|
4735
|
+
async pump(stream, onEvent) {
|
|
4736
|
+
try {
|
|
4737
|
+
for await (const message of stream) {
|
|
4738
|
+
if (message.dropped > 0n) {
|
|
4739
|
+
this.droppedCount = Number(message.dropped);
|
|
4740
|
+
}
|
|
4741
|
+
const event = scriptEventFromProto(message);
|
|
4742
|
+
if (event) onEvent(event);
|
|
4743
|
+
}
|
|
4744
|
+
} catch (err) {
|
|
4745
|
+
if (!this.stopped) {
|
|
4746
|
+
this.failure = err instanceof Error ? err : new Error(String(err));
|
|
4747
|
+
}
|
|
4748
|
+
}
|
|
4749
|
+
}
|
|
4750
|
+
/**
|
|
4751
|
+
* Why the subscription ended: null while it is still open and after a clean
|
|
4752
|
+
* stop.
|
|
4753
|
+
*/
|
|
4754
|
+
get error() {
|
|
4755
|
+
return this.failure;
|
|
4756
|
+
}
|
|
4757
|
+
/** How many events the server discarded because this reader fell behind. */
|
|
4758
|
+
get dropped() {
|
|
4759
|
+
return this.droppedCount;
|
|
4760
|
+
}
|
|
4761
|
+
/**
|
|
4762
|
+
* Resolves once the subscription ends — {@link ScriptFollow.stop}, a dead
|
|
4763
|
+
* session or a transport failure.
|
|
4764
|
+
*
|
|
4765
|
+
* @throws the transport failure that ended the subscription, if any
|
|
4766
|
+
*/
|
|
4767
|
+
async wait() {
|
|
4768
|
+
await this.finished;
|
|
4769
|
+
if (this.failure) throw this.failure;
|
|
4770
|
+
}
|
|
4771
|
+
/**
|
|
4772
|
+
* Ends the subscription. Idempotent, and safe to call from a `finally`. It
|
|
4773
|
+
* never cancels a run: other readers, and the script itself, are unaffected.
|
|
4774
|
+
*/
|
|
4775
|
+
async stop() {
|
|
4776
|
+
if (this.stopped) return;
|
|
4777
|
+
this.stopped = true;
|
|
4778
|
+
this.abort.abort();
|
|
4779
|
+
await this.finished;
|
|
4780
|
+
}
|
|
4781
|
+
};
|
|
4782
|
+
|
|
4783
|
+
// src/dom-mirror.ts
|
|
4784
|
+
var keyOf = (frameId, backendNodeId) => `${frameId}#${backendNodeId}`;
|
|
4785
|
+
var RESYNC_ATTEMPTS = 4;
|
|
4786
|
+
var RESYNC_RETRY_MS = 150;
|
|
4787
|
+
var DomMirror = class {
|
|
4788
|
+
/** @internal Constructed by CloudBrowser; not part of the public API. */
|
|
4789
|
+
constructor(init) {
|
|
4790
|
+
this.nodes = /* @__PURE__ */ new Map();
|
|
4791
|
+
this.slots = /* @__PURE__ */ new Map();
|
|
4792
|
+
this.expandedKeys = /* @__PURE__ */ new Set();
|
|
4793
|
+
/**
|
|
4794
|
+
* Per node: the page sequence its current state was defined at, by a read
|
|
4795
|
+
* payload or by an edit.
|
|
4796
|
+
*
|
|
4797
|
+
* Reads and events reach a client over two different channels — a unary call
|
|
4798
|
+
* and a stream — so an event can turn up that the read reply already folded
|
|
4799
|
+
* in. Applying it twice would duplicate an insertion, which is the one entry
|
|
4800
|
+
* type that is not idempotent. Comparing against the node's own watermark
|
|
4801
|
+
* rather than a single page-wide one keeps that from silently discarding a
|
|
4802
|
+
* change to an unrelated part of the tree.
|
|
4803
|
+
*/
|
|
4804
|
+
this.asOf = /* @__PURE__ */ new Map();
|
|
4805
|
+
this.seqValue = 0;
|
|
4806
|
+
this.rootNode = null;
|
|
4807
|
+
this.mainFrame = "";
|
|
4808
|
+
/**
|
|
4809
|
+
* Events are held until a snapshot exists to apply them to, and dropped if
|
|
4810
|
+
* they predate it. Without this a batch that lands between subscribing and
|
|
4811
|
+
* the snapshot arriving would either be applied to nothing or applied twice.
|
|
4812
|
+
*/
|
|
4813
|
+
this.ready = false;
|
|
4814
|
+
this.pendingEvents = [];
|
|
4815
|
+
this.stopped = false;
|
|
4816
|
+
this.failure = null;
|
|
4817
|
+
this.transport = init.transport;
|
|
4818
|
+
this.options = init.options;
|
|
4819
|
+
this.onChange = init.onChange;
|
|
4820
|
+
this.onResync = init.onResync;
|
|
4821
|
+
this.abort = init.abort;
|
|
4822
|
+
this.finished = this.pump(init.stream);
|
|
4823
|
+
}
|
|
4824
|
+
// ── public surface ────────────────────────────────────────────────────
|
|
4825
|
+
/** The main frame's document, or null before the first snapshot arrived. */
|
|
4826
|
+
get root() {
|
|
4827
|
+
return this.rootNode;
|
|
4828
|
+
}
|
|
4829
|
+
/** The page's main frame. */
|
|
4830
|
+
get mainFrameId() {
|
|
4831
|
+
return this.mainFrame;
|
|
4832
|
+
}
|
|
4833
|
+
/**
|
|
4834
|
+
* Every frame with a document in the tree, main frame first. A frame whose
|
|
4835
|
+
* `<iframe>` has not been expanded is not mirrored and not listed.
|
|
4836
|
+
*/
|
|
4837
|
+
get frameIds() {
|
|
4838
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4839
|
+
if (this.mainFrame) seen.add(this.mainFrame);
|
|
4840
|
+
for (const node2 of this.nodes.values()) {
|
|
4841
|
+
if (node2.nodeType === 9) seen.add(node2.frameId);
|
|
4842
|
+
}
|
|
4843
|
+
return [...seen];
|
|
4844
|
+
}
|
|
4845
|
+
/**
|
|
4846
|
+
* The page sequence of the last change applied. One clock for the whole
|
|
4847
|
+
* page: a change in an out-of-process iframe and one in the main document
|
|
4848
|
+
* are ordered against each other.
|
|
4849
|
+
*/
|
|
4850
|
+
get seq() {
|
|
4851
|
+
return this.seqValue;
|
|
4852
|
+
}
|
|
4853
|
+
/** Looks up a node by its address. */
|
|
4854
|
+
getNode(frameId, backendNodeId) {
|
|
4855
|
+
return this.nodes.get(keyOf(frameId, backendNodeId));
|
|
4856
|
+
}
|
|
4857
|
+
/**
|
|
4858
|
+
* Whether this node's children are known. Changes inside a node that is not
|
|
4859
|
+
* expanded arrive only as an updated `childNodeCount`.
|
|
4860
|
+
*/
|
|
4861
|
+
isExpanded(node2) {
|
|
4862
|
+
return this.expandedKeys.has(keyOf(node2.frameId, node2.backendNodeId));
|
|
4863
|
+
}
|
|
4864
|
+
/** Why the mirror ended: null while running and after a clean stop. */
|
|
4865
|
+
get error() {
|
|
4866
|
+
return this.failure;
|
|
4867
|
+
}
|
|
4868
|
+
/**
|
|
4869
|
+
* Fetches a node's children and starts reporting changes inside them. This
|
|
4870
|
+
* is what a tree view calls when the user opens a node.
|
|
4871
|
+
*
|
|
4872
|
+
* On an `<iframe>` the one child is the document it hosts, and this call is
|
|
4873
|
+
* what starts mirroring that frame. Nothing about the result says a process
|
|
4874
|
+
* boundary was crossed; it is a child list like any other.
|
|
4875
|
+
*
|
|
4876
|
+
* @param depth levels below the node, default 1
|
|
4877
|
+
*/
|
|
4878
|
+
async expand(node2, depth) {
|
|
4879
|
+
if (this.stopped) return;
|
|
4880
|
+
const frame = node2.frameId;
|
|
4881
|
+
const key = keyOf(frame, node2.backendNodeId);
|
|
4882
|
+
const reply = await this.transport.children(node2.backendNodeId, frame, depth);
|
|
4883
|
+
if (!this.nodes.has(key)) return;
|
|
4884
|
+
const kids = JSON.parse(reply.children || "[]");
|
|
4885
|
+
const fresh = this.touch(key);
|
|
4886
|
+
this.dropChildren(fresh);
|
|
4887
|
+
fresh.children = kids.map(
|
|
4888
|
+
(k) => this.adopt(k, { parent: key, kind: "children" }, frame, reply.seq)
|
|
4889
|
+
);
|
|
4890
|
+
fresh.childNodeCount = fresh.children.length;
|
|
4891
|
+
this.expandedKeys.add(key);
|
|
4892
|
+
this.mark(key, reply.seq);
|
|
4893
|
+
this.onChange(this);
|
|
4894
|
+
}
|
|
4895
|
+
/**
|
|
4896
|
+
* Stops reporting changes inside a node, called when the user closes it. The
|
|
4897
|
+
* node itself stays in the tree and keeps reporting its child count. A child
|
|
4898
|
+
* frame below it stops being mirrored too.
|
|
4899
|
+
*
|
|
4900
|
+
* Skipping this is not an error, it is a slow leak: the browser's revealed
|
|
4901
|
+
* set only grows, and eventually it is no longer filtering anything.
|
|
4902
|
+
*/
|
|
4903
|
+
async collapse(node2) {
|
|
4904
|
+
if (this.stopped) return;
|
|
4905
|
+
const key = keyOf(node2.frameId, node2.backendNodeId);
|
|
4906
|
+
await this.transport.release(node2.backendNodeId, node2.frameId);
|
|
4907
|
+
if (!this.nodes.has(key)) return;
|
|
4908
|
+
const fresh = this.touch(key);
|
|
4909
|
+
this.dropChildren(fresh);
|
|
4910
|
+
delete fresh.children;
|
|
4911
|
+
this.expandedKeys.delete(key);
|
|
4912
|
+
this.onChange(this);
|
|
4913
|
+
}
|
|
4914
|
+
/**
|
|
4915
|
+
* Brings a node into the tree together with its ancestors and their
|
|
4916
|
+
* siblings, and starts reporting changes along that path.
|
|
4917
|
+
*
|
|
4918
|
+
* Use it to focus a node you do not hold — an `inspectAtPosition` hit, say.
|
|
4919
|
+
* You cannot walk up to it yourself: it is not in your tree, so there is
|
|
4920
|
+
* nothing to walk from.
|
|
4921
|
+
*
|
|
4922
|
+
* The node may be in a frame nobody opened, and that works: the chain comes
|
|
4923
|
+
* back crossing the frame boundaries it has to, and those frames start being
|
|
4924
|
+
* mirrored, exactly as if you had expanded your way there by hand.
|
|
4925
|
+
*
|
|
4926
|
+
* @returns the ancestor chain, the main document first, or an empty array if
|
|
4927
|
+
* the node is not on the page
|
|
4928
|
+
*/
|
|
4929
|
+
async reveal(backendNodeId, frameId) {
|
|
4930
|
+
if (this.stopped) return [];
|
|
4931
|
+
const frame = frameId || this.mainFrame;
|
|
4932
|
+
const reply = await this.transport.reveal(backendNodeId, frame);
|
|
4933
|
+
const path = JSON.parse(reply.path || "[]");
|
|
4934
|
+
if (path.length === 0) return [];
|
|
4935
|
+
const keys = [];
|
|
4936
|
+
let space = this.mainFrame;
|
|
4937
|
+
let above = null;
|
|
4938
|
+
for (const step of path) {
|
|
4939
|
+
if (step.nodeType === 9 && step.frameId) space = step.frameId;
|
|
4940
|
+
const key = keyOf(space, step.backendNodeId);
|
|
4941
|
+
if (!this.nodes.has(key)) {
|
|
4942
|
+
if (above === null || !this.nodes.has(above)) break;
|
|
4943
|
+
const owner = this.touch(above);
|
|
4944
|
+
this.dropChildren(owner);
|
|
4945
|
+
owner.children = [
|
|
4946
|
+
this.adopt(step, { parent: above, kind: "children" }, space, reply.seq)
|
|
4947
|
+
];
|
|
4948
|
+
owner.childNodeCount = 1;
|
|
4949
|
+
this.expandedKeys.add(above);
|
|
4950
|
+
this.mark(above, reply.seq);
|
|
4951
|
+
}
|
|
4952
|
+
this.mergeChildren(key, step, space, reply.seq);
|
|
4953
|
+
keys.push(key);
|
|
4954
|
+
above = key;
|
|
4955
|
+
}
|
|
4956
|
+
this.onChange(this);
|
|
4957
|
+
return keys.map((k) => this.nodes.get(k)).filter((n) => n !== void 0);
|
|
4958
|
+
}
|
|
4959
|
+
/**
|
|
4960
|
+
* Throws away the local copy of the whole page and fetches a fresh one.
|
|
4961
|
+
* Happens automatically whenever the browser says the copy is void, so you
|
|
4962
|
+
* rarely need to call it.
|
|
4963
|
+
*/
|
|
4964
|
+
async resync(reason = "manual") {
|
|
4965
|
+
if (this.stopped) return;
|
|
4966
|
+
this.ready = false;
|
|
4967
|
+
let snapshot = null;
|
|
4968
|
+
let last;
|
|
4969
|
+
for (let attempt = 0; attempt < RESYNC_ATTEMPTS; attempt++) {
|
|
4970
|
+
if (this.stopped) return;
|
|
4971
|
+
if (attempt > 0) await new Promise((r) => setTimeout(r, RESYNC_RETRY_MS));
|
|
4972
|
+
try {
|
|
4973
|
+
snapshot = await this.transport.start(this.options);
|
|
4974
|
+
break;
|
|
4975
|
+
} catch (err) {
|
|
4976
|
+
last = err;
|
|
4977
|
+
}
|
|
4978
|
+
}
|
|
4979
|
+
if (!snapshot) {
|
|
4980
|
+
this.failure = last instanceof Error ? last : new Error(String(last));
|
|
4981
|
+
void this.stop();
|
|
4982
|
+
return;
|
|
4983
|
+
}
|
|
4984
|
+
this.install(snapshot);
|
|
4985
|
+
this.onResync?.(reason);
|
|
4986
|
+
this.onChange(this);
|
|
4987
|
+
}
|
|
4988
|
+
/** Resolves once the mirror ends — stop(), a dead session, a transport failure. */
|
|
4989
|
+
async wait() {
|
|
4990
|
+
await this.finished;
|
|
4991
|
+
if (this.failure) throw this.failure;
|
|
4992
|
+
}
|
|
4993
|
+
/** Stops mirroring and detaches the reader. Idempotent, safe in a `finally`. */
|
|
4994
|
+
async stop() {
|
|
4995
|
+
if (this.stopped) return;
|
|
4996
|
+
this.stopped = true;
|
|
4997
|
+
try {
|
|
4998
|
+
await this.transport.stop();
|
|
4999
|
+
} catch {
|
|
5000
|
+
} finally {
|
|
5001
|
+
this.abort.abort();
|
|
5002
|
+
await this.finished;
|
|
5003
|
+
}
|
|
5004
|
+
}
|
|
5005
|
+
// ── snapshot installation ─────────────────────────────────────────────
|
|
5006
|
+
/** @internal Called by CloudBrowser with the opening snapshot. */
|
|
5007
|
+
install(snapshot) {
|
|
5008
|
+
this.reset();
|
|
5009
|
+
this.mainFrame = snapshot.frameId || this.mainFrame;
|
|
5010
|
+
this.seqValue = snapshot.seq;
|
|
5011
|
+
const parsed = snapshot.root ? JSON.parse(snapshot.root) : null;
|
|
5012
|
+
this.rootNode = parsed ? this.adopt(parsed, null, this.mainFrame, snapshot.seq) : null;
|
|
5013
|
+
this.ready = true;
|
|
5014
|
+
const buffered = this.pendingEvents;
|
|
5015
|
+
this.pendingEvents = [];
|
|
5016
|
+
for (const event of buffered) this.handle(event);
|
|
5017
|
+
}
|
|
5018
|
+
reset() {
|
|
5019
|
+
this.nodes = /* @__PURE__ */ new Map();
|
|
5020
|
+
this.slots = /* @__PURE__ */ new Map();
|
|
5021
|
+
this.expandedKeys = /* @__PURE__ */ new Set();
|
|
5022
|
+
this.asOf = /* @__PURE__ */ new Map();
|
|
5023
|
+
this.rootNode = null;
|
|
5024
|
+
}
|
|
5025
|
+
// ── event stream ──────────────────────────────────────────────────────
|
|
5026
|
+
async pump(stream) {
|
|
5027
|
+
try {
|
|
5028
|
+
for await (const event of stream) {
|
|
5029
|
+
if (event.event.case === "resync") {
|
|
5030
|
+
await this.resync(event.event.value.reason);
|
|
5031
|
+
continue;
|
|
5032
|
+
}
|
|
5033
|
+
this.handle(event);
|
|
5034
|
+
}
|
|
5035
|
+
} catch (err) {
|
|
5036
|
+
if (!this.stopped) {
|
|
5037
|
+
this.failure = err instanceof Error ? err : new Error(String(err));
|
|
5038
|
+
}
|
|
5039
|
+
}
|
|
5040
|
+
}
|
|
5041
|
+
handle(event) {
|
|
5042
|
+
if (!this.ready) {
|
|
5043
|
+
if (event.event.case !== void 0) this.pendingEvents.push(event);
|
|
5044
|
+
return;
|
|
5045
|
+
}
|
|
5046
|
+
if (event.event.case !== "update") return;
|
|
5047
|
+
const batch = event.event.value;
|
|
5048
|
+
const frame = batch.frameId || this.mainFrame;
|
|
5049
|
+
const seq = Number(batch.seq);
|
|
5050
|
+
let entries;
|
|
5051
|
+
try {
|
|
5052
|
+
entries = JSON.parse(batch.edits || "[]");
|
|
5053
|
+
} catch {
|
|
5054
|
+
void this.resync("overflow");
|
|
5055
|
+
return;
|
|
5056
|
+
}
|
|
5057
|
+
const keep = [];
|
|
5058
|
+
const touched = [];
|
|
5059
|
+
for (const entry of entries) {
|
|
5060
|
+
const key = keyOf(frame, targetOf(entry));
|
|
5061
|
+
if ((this.asOf.get(key) ?? -1) >= seq) continue;
|
|
5062
|
+
keep.push(entry);
|
|
5063
|
+
touched.push(key);
|
|
5064
|
+
}
|
|
5065
|
+
for (const entry of keep) this.apply(entry, frame, seq);
|
|
5066
|
+
for (const key of touched) this.mark(key, seq);
|
|
5067
|
+
if (seq > this.seqValue) this.seqValue = seq;
|
|
5068
|
+
this.onChange(this);
|
|
5069
|
+
}
|
|
5070
|
+
// ── applying one entry ────────────────────────────────────────────────
|
|
5071
|
+
apply(entry, frame, seq) {
|
|
5072
|
+
switch (entry.type) {
|
|
5073
|
+
case "childNodeInserted": {
|
|
5074
|
+
const parentKey = keyOf(frame, entry.parentId);
|
|
5075
|
+
if (!this.nodes.has(parentKey)) return;
|
|
5076
|
+
const fresh = this.touch(parentKey);
|
|
5077
|
+
const kids = fresh.children ?? [];
|
|
5078
|
+
const node2 = this.adopt(
|
|
5079
|
+
entry.node,
|
|
5080
|
+
{ parent: parentKey, kind: "children" },
|
|
5081
|
+
frame,
|
|
5082
|
+
seq
|
|
5083
|
+
);
|
|
5084
|
+
const at2 = entry.previousNodeId === 0 ? 0 : kids.findIndex((c) => c.backendNodeId === entry.previousNodeId) + 1;
|
|
5085
|
+
kids.splice(at2 <= 0 ? 0 : at2, 0, node2);
|
|
5086
|
+
fresh.children = kids;
|
|
5087
|
+
fresh.childNodeCount = kids.length;
|
|
5088
|
+
this.expandedKeys.add(parentKey);
|
|
5089
|
+
return;
|
|
5090
|
+
}
|
|
5091
|
+
case "childNodeRemoved": {
|
|
5092
|
+
const parentKey = keyOf(frame, entry.parentId);
|
|
5093
|
+
const parent = this.nodes.get(parentKey);
|
|
5094
|
+
if (!parent?.children) return;
|
|
5095
|
+
const fresh = this.touch(parentKey);
|
|
5096
|
+
const kids = fresh.children ?? [];
|
|
5097
|
+
const at2 = kids.findIndex((c) => c.backendNodeId === entry.nodeId);
|
|
5098
|
+
if (at2 < 0) return;
|
|
5099
|
+
this.forget(kids[at2]);
|
|
5100
|
+
kids.splice(at2, 1);
|
|
5101
|
+
fresh.children = kids;
|
|
5102
|
+
fresh.childNodeCount = kids.length;
|
|
5103
|
+
return;
|
|
5104
|
+
}
|
|
5105
|
+
case "childNodeCountUpdated": {
|
|
5106
|
+
const key = keyOf(frame, entry.nodeId);
|
|
5107
|
+
if (!this.nodes.has(key)) return;
|
|
5108
|
+
this.touch(key).childNodeCount = entry.count;
|
|
5109
|
+
return;
|
|
5110
|
+
}
|
|
5111
|
+
case "childListReordered": {
|
|
5112
|
+
const key = keyOf(frame, entry.nodeId);
|
|
5113
|
+
const parent = this.nodes.get(key);
|
|
5114
|
+
if (!parent?.children) return;
|
|
5115
|
+
const fresh = this.touch(key);
|
|
5116
|
+
const have = new Map(
|
|
5117
|
+
(fresh.children ?? []).map((c) => [c.backendNodeId, c])
|
|
5118
|
+
);
|
|
5119
|
+
const next = [];
|
|
5120
|
+
for (const id of entry.order) {
|
|
5121
|
+
const child = have.get(id);
|
|
5122
|
+
if (child) next.push(child);
|
|
5123
|
+
}
|
|
5124
|
+
if (next.length !== have.size) {
|
|
5125
|
+
void this.resync("overflow");
|
|
5126
|
+
return;
|
|
5127
|
+
}
|
|
5128
|
+
fresh.children = next;
|
|
5129
|
+
return;
|
|
5130
|
+
}
|
|
5131
|
+
case "attributesUpdated": {
|
|
5132
|
+
const key = keyOf(frame, entry.nodeId);
|
|
5133
|
+
if (!this.nodes.has(key)) return;
|
|
5134
|
+
this.touch(key).attributes = entry.attributes.slice();
|
|
5135
|
+
return;
|
|
5136
|
+
}
|
|
5137
|
+
case "characterDataModified": {
|
|
5138
|
+
const key = keyOf(frame, entry.nodeId);
|
|
5139
|
+
if (!this.nodes.has(key)) return;
|
|
5140
|
+
this.touch(key).nodeValue = entry.value;
|
|
5141
|
+
return;
|
|
5142
|
+
}
|
|
5143
|
+
}
|
|
5144
|
+
}
|
|
5145
|
+
// ── tree bookkeeping ──────────────────────────────────────────────────
|
|
5146
|
+
/** Records that a node's state is current as of `seq`. Never moves back. */
|
|
5147
|
+
mark(key, seq) {
|
|
5148
|
+
if (seq > (this.asOf.get(key) ?? -1)) this.asOf.set(key, seq);
|
|
5149
|
+
if (seq > this.seqValue) this.seqValue = seq;
|
|
5150
|
+
}
|
|
5151
|
+
/**
|
|
5152
|
+
* Replaces `key` and every ancestor with copies, so the path from the root
|
|
5153
|
+
* to the changed node has new identities and nothing else does. Returns the
|
|
5154
|
+
* fresh copy of `key`, which the caller then edits in place.
|
|
5155
|
+
*
|
|
5156
|
+
* The walk crosses frame boundaries without noticing them: a document is a
|
|
5157
|
+
* child of the `<iframe>` hosting it like any other, so a change deep inside
|
|
5158
|
+
* an out-of-process frame still produces a new `root`.
|
|
5159
|
+
*/
|
|
5160
|
+
touch(key) {
|
|
5161
|
+
const original = this.nodes.get(key);
|
|
5162
|
+
if (!original) throw new Error(`dom mirror: node ${key} is not in the tree`);
|
|
5163
|
+
const clone = { ...original };
|
|
5164
|
+
if (original.children) clone.children = original.children.slice();
|
|
5165
|
+
if (original.shadowRoots) clone.shadowRoots = original.shadowRoots.slice();
|
|
5166
|
+
this.nodes.set(key, clone);
|
|
5167
|
+
let childKey = key;
|
|
5168
|
+
let childNode = clone;
|
|
5169
|
+
for (; ; ) {
|
|
5170
|
+
const slot = this.slots.get(childKey);
|
|
5171
|
+
if (!slot) {
|
|
5172
|
+
this.rootNode = childNode;
|
|
5173
|
+
return clone;
|
|
5174
|
+
}
|
|
5175
|
+
const parent = this.nodes.get(slot.parent);
|
|
5176
|
+
if (!parent) return clone;
|
|
5177
|
+
const parentClone = { ...parent };
|
|
5178
|
+
const list = (parent[slot.kind] ?? []).slice();
|
|
5179
|
+
const at2 = list.findIndex(
|
|
5180
|
+
(n) => n.backendNodeId === childNode.backendNodeId && n.frameId === childNode.frameId
|
|
5181
|
+
);
|
|
5182
|
+
if (at2 >= 0) list[at2] = childNode;
|
|
5183
|
+
parentClone[slot.kind] = list;
|
|
5184
|
+
this.nodes.set(slot.parent, parentClone);
|
|
5185
|
+
childKey = slot.parent;
|
|
5186
|
+
childNode = parentClone;
|
|
5187
|
+
}
|
|
5188
|
+
}
|
|
5189
|
+
/**
|
|
5190
|
+
* Registers a payload subtree and returns the copy that lives in the tree.
|
|
5191
|
+
*
|
|
5192
|
+
* `frame` is the id space the payload's ids belong to, and it changes here
|
|
5193
|
+
* and nowhere else: a document node names its own frame, and everything
|
|
5194
|
+
* below it counts in that frame. That is the whole of what crossing into an
|
|
5195
|
+
* iframe means to a client.
|
|
5196
|
+
*/
|
|
5197
|
+
adopt(node2, slot, frame, seq) {
|
|
5198
|
+
const isDocument = node2.nodeType === 9;
|
|
5199
|
+
const own = isDocument && node2.frameId ? node2.frameId : frame;
|
|
5200
|
+
const key = keyOf(own, node2.backendNodeId);
|
|
5201
|
+
const copy = { ...node2, frameId: own };
|
|
5202
|
+
if (!isDocument && node2.frameId && node2.frameId !== own) {
|
|
5203
|
+
copy.contentFrameId = node2.frameId;
|
|
5204
|
+
} else {
|
|
5205
|
+
delete copy.contentFrameId;
|
|
5206
|
+
}
|
|
5207
|
+
this.nodes.set(key, copy);
|
|
5208
|
+
this.mark(key, seq);
|
|
5209
|
+
if (slot) this.slots.set(key, slot);
|
|
5210
|
+
else this.slots.delete(key);
|
|
5211
|
+
if (node2.children) {
|
|
5212
|
+
copy.children = node2.children.map(
|
|
5213
|
+
(c) => this.adopt(c, { parent: key, kind: "children" }, own, seq)
|
|
5214
|
+
);
|
|
5215
|
+
this.expandedKeys.add(key);
|
|
5216
|
+
} else {
|
|
5217
|
+
delete copy.children;
|
|
5218
|
+
this.expandedKeys.delete(key);
|
|
5219
|
+
}
|
|
5220
|
+
if (node2.shadowRoots) {
|
|
5221
|
+
copy.shadowRoots = node2.shadowRoots.map(
|
|
5222
|
+
(r) => this.adopt(r, { parent: key, kind: "shadowRoots" }, own, seq)
|
|
5223
|
+
);
|
|
5224
|
+
} else {
|
|
5225
|
+
delete copy.shadowRoots;
|
|
5226
|
+
}
|
|
5227
|
+
return copy;
|
|
5228
|
+
}
|
|
5229
|
+
/** Replaces a node's child list from a fresh payload for the same node. */
|
|
5230
|
+
mergeChildren(key, payload, frame, seq) {
|
|
5231
|
+
if (!payload.children) return;
|
|
5232
|
+
const fresh = this.touch(key);
|
|
5233
|
+
this.dropChildren(fresh);
|
|
5234
|
+
fresh.children = payload.children.map(
|
|
5235
|
+
(c) => this.adopt(c, { parent: key, kind: "children" }, frame, seq)
|
|
5236
|
+
);
|
|
5237
|
+
fresh.childNodeCount = fresh.children.length;
|
|
5238
|
+
this.expandedKeys.add(key);
|
|
5239
|
+
this.mark(key, seq);
|
|
5240
|
+
}
|
|
5241
|
+
dropChildren(node2) {
|
|
5242
|
+
for (const child of node2.children ?? []) this.forget(child);
|
|
5243
|
+
}
|
|
5244
|
+
/**
|
|
5245
|
+
* Removes a subtree from the index. It descends through hosted documents
|
|
5246
|
+
* like through anything else — they are children, and a frame stops being
|
|
5247
|
+
* mirrored exactly when the element hosting it stops being expanded.
|
|
5248
|
+
*/
|
|
5249
|
+
forget(node2) {
|
|
5250
|
+
const key = keyOf(node2.frameId, node2.backendNodeId);
|
|
5251
|
+
this.nodes.delete(key);
|
|
5252
|
+
this.slots.delete(key);
|
|
5253
|
+
this.expandedKeys.delete(key);
|
|
5254
|
+
this.asOf.delete(key);
|
|
5255
|
+
for (const child of node2.children ?? []) this.forget(child);
|
|
5256
|
+
for (const root of node2.shadowRoots ?? []) this.forget(root);
|
|
5257
|
+
}
|
|
5258
|
+
};
|
|
5259
|
+
function targetOf(entry) {
|
|
5260
|
+
switch (entry.type) {
|
|
5261
|
+
case "childNodeInserted":
|
|
5262
|
+
case "childNodeRemoved":
|
|
5263
|
+
return entry.parentId;
|
|
5264
|
+
default:
|
|
5265
|
+
return entry.nodeId;
|
|
5266
|
+
}
|
|
5267
|
+
}
|
|
5268
|
+
|
|
4403
5269
|
// src/client.ts
|
|
4404
5270
|
var CloudBrowser = class {
|
|
4405
5271
|
constructor(transport, sessionId, apiKey, fingerprint, stopFn) {
|
|
@@ -5399,6 +6265,336 @@ var CloudBrowser = class {
|
|
|
5399
6265
|
return interceptedRequestFromProto(resp.request);
|
|
5400
6266
|
}
|
|
5401
6267
|
// ──────────────────────────────────────────────────────────────────
|
|
6268
|
+
// Network capture
|
|
6269
|
+
// ──────────────────────────────────────────────────────────────────
|
|
6270
|
+
/**
|
|
6271
|
+
* Starts capturing the session's network traffic and returns a live view of
|
|
6272
|
+
* it.
|
|
6273
|
+
*
|
|
6274
|
+
* Every request matching `opts.patterns` is reported once it completes, and
|
|
6275
|
+
* "every request" is literal: capture sits in the browser process rather than
|
|
6276
|
+
* in a page, so cross-process iframes, workers and service workers are
|
|
6277
|
+
* included, the headers are the ones actually put on the wire (Cookie and
|
|
6278
|
+
* Sec-* included), and each hop of a redirect chain arrives as its own
|
|
6279
|
+
* exchange. Requests are never paused, so the page loads at full speed.
|
|
6280
|
+
*
|
|
6281
|
+
* This resolves as soon as the capture is running; onExchange then fires in
|
|
6282
|
+
* the background while you drive the browser. The capture is armed only after
|
|
6283
|
+
* the subscription exists, so nothing that happens after this resolves is
|
|
6284
|
+
* missed. Call {@link NetworkCapture.stop} when done — it disarms the capture
|
|
6285
|
+
* server-side, which an aborted transport alone does not.
|
|
6286
|
+
*
|
|
6287
|
+
* @param opts - which requests to capture and whether to keep bodies
|
|
6288
|
+
* @param onExchange - called per exchange; see {@link NetworkExchangeHandler}
|
|
6289
|
+
* for the ordering and blocking rules
|
|
6290
|
+
*
|
|
6291
|
+
* @returns NetworkCapture handle for stopping the capture and inspecting how
|
|
6292
|
+
* it ended
|
|
6293
|
+
*
|
|
6294
|
+
* @throws UNKNOWN_ERROR - the capture could not be started
|
|
6295
|
+
*
|
|
6296
|
+
* @example
|
|
6297
|
+
* const capture = await browser.captureNetwork(
|
|
6298
|
+
* { patterns: ["*\/api/*"], bodies: "text" },
|
|
6299
|
+
* (ex) => console.log(ex.statusCode, ex.method, ex.url),
|
|
6300
|
+
* );
|
|
6301
|
+
* try {
|
|
6302
|
+
* await browser.navigate("https://example.com");
|
|
6303
|
+
* } finally {
|
|
6304
|
+
* await capture.stop();
|
|
6305
|
+
* }
|
|
6306
|
+
*/
|
|
6307
|
+
async captureNetwork(opts, onExchange) {
|
|
6308
|
+
const capture = await this.subscribeNetworkExchanges(onExchange);
|
|
6309
|
+
try {
|
|
6310
|
+
await this.startNetworkCapture(opts);
|
|
6311
|
+
} catch (err) {
|
|
6312
|
+
await capture.stop();
|
|
6313
|
+
throw err;
|
|
6314
|
+
}
|
|
6315
|
+
capture.arm();
|
|
6316
|
+
return capture;
|
|
6317
|
+
}
|
|
6318
|
+
/**
|
|
6319
|
+
* Arms a capture without subscribing to it.
|
|
6320
|
+
*
|
|
6321
|
+
* Use it when the reader lives somewhere else — another tab, or a later
|
|
6322
|
+
* {@link createWebSocketBrowser} against the same session. Most callers want
|
|
6323
|
+
* {@link CloudBrowser.captureNetwork} instead, which arms and subscribes
|
|
6324
|
+
* together. Calling this again replaces the running capture.
|
|
6325
|
+
*
|
|
6326
|
+
* @param opts - which requests to capture and whether to keep bodies
|
|
6327
|
+
*
|
|
6328
|
+
* @throws UNKNOWN_ERROR - the capture could not be started
|
|
6329
|
+
*/
|
|
6330
|
+
async startNetworkCapture(opts) {
|
|
6331
|
+
await this.client.startNetworkCapture(
|
|
6332
|
+
create(StartNetworkCaptureRequestSchema, {
|
|
6333
|
+
sessionId: this.sessionId,
|
|
6334
|
+
apiKey: this.apiKey,
|
|
6335
|
+
patterns: opts.patterns ?? [],
|
|
6336
|
+
bodies: opts.bodies ?? "none",
|
|
6337
|
+
bodyPatterns: opts.bodyPatterns ?? []
|
|
6338
|
+
})
|
|
6339
|
+
);
|
|
6340
|
+
}
|
|
6341
|
+
/**
|
|
6342
|
+
* Disarms the session's capture.
|
|
6343
|
+
*
|
|
6344
|
+
* @returns whether a capture was running
|
|
6345
|
+
*
|
|
6346
|
+
* @throws UNKNOWN_ERROR - the capture could not be stopped
|
|
6347
|
+
*/
|
|
6348
|
+
async stopNetworkCapture() {
|
|
6349
|
+
const resp = await this.client.stopNetworkCapture(
|
|
6350
|
+
create(StopNetworkCaptureRequestSchema, {
|
|
6351
|
+
sessionId: this.sessionId,
|
|
6352
|
+
apiKey: this.apiKey
|
|
6353
|
+
})
|
|
6354
|
+
);
|
|
6355
|
+
return resp.stopped;
|
|
6356
|
+
}
|
|
6357
|
+
/**
|
|
6358
|
+
* Subscribes to the session's capture without arming one, for reading a
|
|
6359
|
+
* capture that {@link CloudBrowser.startNetworkCapture} armed elsewhere.
|
|
6360
|
+
* Several readers can watch the same capture, each with its own buffer.
|
|
6361
|
+
*
|
|
6362
|
+
* Stopping the returned view detaches this reader and leaves the capture
|
|
6363
|
+
* running, since other readers may still be attached.
|
|
6364
|
+
*
|
|
6365
|
+
* @param onExchange - called per exchange; see {@link NetworkExchangeHandler}
|
|
6366
|
+
* for the ordering and blocking rules
|
|
6367
|
+
*
|
|
6368
|
+
* @returns NetworkCapture attached to whatever capture is running; onExchange
|
|
6369
|
+
* simply never fires when none is
|
|
6370
|
+
*
|
|
6371
|
+
* @throws UNKNOWN_ERROR - the subscription could not be opened
|
|
6372
|
+
*/
|
|
6373
|
+
async streamNetworkExchanges(onExchange) {
|
|
6374
|
+
return this.subscribeNetworkExchanges(onExchange);
|
|
6375
|
+
}
|
|
6376
|
+
/**
|
|
6377
|
+
* Opens the stream and waits for the server to acknowledge the subscription
|
|
6378
|
+
* before resolving.
|
|
6379
|
+
*
|
|
6380
|
+
* Merely calling the streaming method does not wait for the server to start
|
|
6381
|
+
* handling it, so arming a capture straight after could outrun the
|
|
6382
|
+
* subscription and lose the first exchanges. The response headers arrive once
|
|
6383
|
+
* the handler is subscribed, and onHeader reports exactly that — over native
|
|
6384
|
+
* gRPC as well as over the WebSocket transport, which forwards the event as
|
|
6385
|
+
* its own frame.
|
|
6386
|
+
*/
|
|
6387
|
+
async subscribeNetworkExchanges(onExchange) {
|
|
6388
|
+
const abort = new AbortController();
|
|
6389
|
+
let subscribed;
|
|
6390
|
+
const ready = new Promise((resolve) => {
|
|
6391
|
+
subscribed = resolve;
|
|
6392
|
+
});
|
|
6393
|
+
const stream = this.client.streamNetworkExchanges(
|
|
6394
|
+
create(StreamNetworkExchangesRequestSchema, {
|
|
6395
|
+
sessionId: this.sessionId,
|
|
6396
|
+
apiKey: this.apiKey
|
|
6397
|
+
}),
|
|
6398
|
+
{ signal: abort.signal, onHeader: () => subscribed() }
|
|
6399
|
+
);
|
|
6400
|
+
const capture = new NetworkCapture({
|
|
6401
|
+
stream,
|
|
6402
|
+
onExchange,
|
|
6403
|
+
abort,
|
|
6404
|
+
disarm: () => this.stopNetworkCapture()
|
|
6405
|
+
});
|
|
6406
|
+
const ended = capture.wait().then(() => {
|
|
6407
|
+
throw new BrowserScaleError(
|
|
6408
|
+
"browserscale.captureNetwork: stream closed before the subscription was established"
|
|
6409
|
+
);
|
|
6410
|
+
});
|
|
6411
|
+
ended.catch(() => {
|
|
6412
|
+
});
|
|
6413
|
+
await Promise.race([ready, ended]);
|
|
6414
|
+
return capture;
|
|
6415
|
+
}
|
|
6416
|
+
// ──────────────────────────────────────────────────────────────────
|
|
6417
|
+
// DOM mirror
|
|
6418
|
+
// ──────────────────────────────────────────────────────────────────
|
|
6419
|
+
/**
|
|
6420
|
+
* Starts a live copy of a frame's DOM and keeps it up to date.
|
|
6421
|
+
*
|
|
6422
|
+
* The browser sends the top of the tree once, then reports only what changed
|
|
6423
|
+
* in the part you expanded. Everything else costs a child count per batch, no
|
|
6424
|
+
* matter how much churns inside it — which is what makes this usable on a
|
|
6425
|
+
* page that rewrites a list sixty times a second, where re-fetching the
|
|
6426
|
+
* document on a timer is not.
|
|
6427
|
+
*
|
|
6428
|
+
* Expand and collapse as the user opens and closes nodes; that is what moves
|
|
6429
|
+
* the boundary of what gets reported. The returned {@link DomMirror} holds
|
|
6430
|
+
* the tree and exposes `expand`, `collapse` and `reveal`.
|
|
6431
|
+
*
|
|
6432
|
+
* The handler runs after each applied batch. `mirror.root` is a new object
|
|
6433
|
+
* whenever anything under it changed and the untouched parts keep their
|
|
6434
|
+
* identity, so rendering straight from it with memoized components is cheap.
|
|
6435
|
+
*
|
|
6436
|
+
* One mirror covers the whole page as ONE tree. An `<iframe>` is an
|
|
6437
|
+
* ordinary element whose single child is the document it hosts; expanding it
|
|
6438
|
+
* fetches that document and starts mirroring the frame, however deeply
|
|
6439
|
+
* nested and whether or not it is cross-origin. Unlike the inlining
|
|
6440
|
+
* {@link CloudBrowser.getDOM} does, these regions stay live — and frames
|
|
6441
|
+
* nobody opened cost nothing.
|
|
6442
|
+
*
|
|
6443
|
+
* ```ts
|
|
6444
|
+
* const mirror = await browser.mirrorDom({ pierce: true }, () => {
|
|
6445
|
+
* render(mirror.root);
|
|
6446
|
+
* });
|
|
6447
|
+
* await mirror.expand(bodyNode);
|
|
6448
|
+
* // ...
|
|
6449
|
+
* await mirror.stop();
|
|
6450
|
+
* ```
|
|
6451
|
+
*
|
|
6452
|
+
* @param opts initial depth and whether to pierce shadow roots
|
|
6453
|
+
* @param onChange called after every change, including the first snapshot
|
|
6454
|
+
* @param onResync called when the copy had to be rebuilt, after the new tree
|
|
6455
|
+
* is in place. Rebuilding is automatic; this is for telling the user why
|
|
6456
|
+
* their expanded nodes collapsed.
|
|
6457
|
+
* @throws UNKNOWN_ERROR - the mirror could not be started
|
|
6458
|
+
*/
|
|
6459
|
+
async mirrorDom(opts, onChange, onResync) {
|
|
6460
|
+
const abort = new AbortController();
|
|
6461
|
+
let subscribed;
|
|
6462
|
+
const ready = new Promise((resolve) => {
|
|
6463
|
+
subscribed = resolve;
|
|
6464
|
+
});
|
|
6465
|
+
const stream = this.client.streamDomEvents(
|
|
6466
|
+
create(StreamDomEventsRequestSchema, {
|
|
6467
|
+
sessionId: this.sessionId,
|
|
6468
|
+
apiKey: this.apiKey
|
|
6469
|
+
}),
|
|
6470
|
+
{ signal: abort.signal, onHeader: () => subscribed() }
|
|
6471
|
+
);
|
|
6472
|
+
const mirror = new DomMirror({
|
|
6473
|
+
stream,
|
|
6474
|
+
transport: {
|
|
6475
|
+
start: (o) => this.startDomMirror(o),
|
|
6476
|
+
stop: () => this.stopDomMirror(),
|
|
6477
|
+
children: (id, frameId, depth) => this.getDomChildren(id, frameId, depth),
|
|
6478
|
+
release: (id, frameId) => this.releaseDomSubtree(id, frameId),
|
|
6479
|
+
reveal: (id, frameId) => this.revealDomNode(id, frameId)
|
|
6480
|
+
},
|
|
6481
|
+
options: opts,
|
|
6482
|
+
onChange,
|
|
6483
|
+
onResync,
|
|
6484
|
+
abort
|
|
6485
|
+
});
|
|
6486
|
+
const ended = mirror.wait().then(() => {
|
|
6487
|
+
throw new BrowserScaleError(
|
|
6488
|
+
"browserscale.mirrorDom: stream closed before the subscription was established"
|
|
6489
|
+
);
|
|
6490
|
+
});
|
|
6491
|
+
ended.catch(() => {
|
|
6492
|
+
});
|
|
6493
|
+
await Promise.race([ready, ended]);
|
|
6494
|
+
try {
|
|
6495
|
+
mirror.install(await this.startDomMirror(opts));
|
|
6496
|
+
} catch (err) {
|
|
6497
|
+
abort.abort();
|
|
6498
|
+
throw err;
|
|
6499
|
+
}
|
|
6500
|
+
onChange(mirror);
|
|
6501
|
+
return mirror;
|
|
6502
|
+
}
|
|
6503
|
+
/**
|
|
6504
|
+
* Starts (or restarts) the page's mirror and returns the main document,
|
|
6505
|
+
* without subscribing to changes. {@link CloudBrowser.mirrorDom} is what you
|
|
6506
|
+
* normally want; this is the raw command.
|
|
6507
|
+
*/
|
|
6508
|
+
async startDomMirror(opts = {}) {
|
|
6509
|
+
const req = create(StartDomMirrorRequestSchema, {
|
|
6510
|
+
sessionId: this.sessionId,
|
|
6511
|
+
apiKey: this.apiKey
|
|
6512
|
+
});
|
|
6513
|
+
if (opts.depth !== void 0) req.depth = opts.depth;
|
|
6514
|
+
if (opts.pierce !== void 0) req.pierce = opts.pierce;
|
|
6515
|
+
const resp = await this.client.startDomMirror(req);
|
|
6516
|
+
return {
|
|
6517
|
+
root: resp.root,
|
|
6518
|
+
frameId: resp.frameId,
|
|
6519
|
+
seq: Number(resp.seq)
|
|
6520
|
+
};
|
|
6521
|
+
}
|
|
6522
|
+
/** Stops the page's mirror, every frame of it. Idempotent. */
|
|
6523
|
+
async stopDomMirror() {
|
|
6524
|
+
await this.client.stopDomMirror(
|
|
6525
|
+
create(StopDomMirrorRequestSchema, {
|
|
6526
|
+
sessionId: this.sessionId,
|
|
6527
|
+
apiKey: this.apiKey
|
|
6528
|
+
})
|
|
6529
|
+
);
|
|
6530
|
+
}
|
|
6531
|
+
/**
|
|
6532
|
+
* Fetches a node's children and starts reporting changes inside them.
|
|
6533
|
+
* {@link DomMirror.expand} calls this and folds the result into the tree.
|
|
6534
|
+
*
|
|
6535
|
+
* On an `<iframe>` the one child is the document it hosts, and this call is
|
|
6536
|
+
* what starts mirroring that frame.
|
|
6537
|
+
*/
|
|
6538
|
+
async getDomChildren(backendNodeId, frameId = "", depth) {
|
|
6539
|
+
const req = create(GetDomChildrenRequestSchema, {
|
|
6540
|
+
sessionId: this.sessionId,
|
|
6541
|
+
apiKey: this.apiKey,
|
|
6542
|
+
backendNodeId
|
|
6543
|
+
});
|
|
6544
|
+
if (frameId) req.frameId = frameId;
|
|
6545
|
+
if (depth !== void 0) req.depth = depth;
|
|
6546
|
+
const resp = await this.client.getDomChildren(req);
|
|
6547
|
+
return { children: resp.children, seq: Number(resp.seq) };
|
|
6548
|
+
}
|
|
6549
|
+
/**
|
|
6550
|
+
* Stops reporting changes inside a node, and inside any frame below it.
|
|
6551
|
+
* {@link DomMirror.collapse} calls this.
|
|
6552
|
+
*/
|
|
6553
|
+
async releaseDomSubtree(backendNodeId, frameId = "") {
|
|
6554
|
+
const req = create(ReleaseDomSubtreeRequestSchema, {
|
|
6555
|
+
sessionId: this.sessionId,
|
|
6556
|
+
apiKey: this.apiKey,
|
|
6557
|
+
backendNodeId
|
|
6558
|
+
});
|
|
6559
|
+
if (frameId) req.frameId = frameId;
|
|
6560
|
+
await this.client.releaseDomSubtree(req);
|
|
6561
|
+
}
|
|
6562
|
+
/**
|
|
6563
|
+
* Returns the chain from the main document down to a node, each ancestor
|
|
6564
|
+
* with its own children, crossing into frames where it has to and starting
|
|
6565
|
+
* the ones it passes through. {@link DomMirror.reveal} calls this and
|
|
6566
|
+
* splices it in.
|
|
6567
|
+
*/
|
|
6568
|
+
async revealDomNode(backendNodeId, frameId = "") {
|
|
6569
|
+
const req = create(RevealDomNodeRequestSchema, {
|
|
6570
|
+
sessionId: this.sessionId,
|
|
6571
|
+
apiKey: this.apiKey,
|
|
6572
|
+
backendNodeId
|
|
6573
|
+
});
|
|
6574
|
+
if (frameId) req.frameId = frameId;
|
|
6575
|
+
const resp = await this.client.revealDomNode(req);
|
|
6576
|
+
return { path: resp.path, seq: Number(resp.seq) };
|
|
6577
|
+
}
|
|
6578
|
+
/**
|
|
6579
|
+
* A frame's mutation counter, incremented on every change the document sees.
|
|
6580
|
+
* O(1) in the browser and the change detector to poll if you are not
|
|
6581
|
+
* consuming mirror events.
|
|
6582
|
+
*
|
|
6583
|
+
* Prefer this over {@link CloudBrowser.getDOMHash}, which serializes the
|
|
6584
|
+
* whole tree just to hash it. The two answer different questions: a hash
|
|
6585
|
+
* compares content, a revision only says whether this document moved since
|
|
6586
|
+
* you last asked.
|
|
6587
|
+
*/
|
|
6588
|
+
async getDomRevision(frameId = "") {
|
|
6589
|
+
const req = create(GetDomRevisionRequestSchema, {
|
|
6590
|
+
sessionId: this.sessionId,
|
|
6591
|
+
apiKey: this.apiKey
|
|
6592
|
+
});
|
|
6593
|
+
if (frameId) req.frameId = frameId;
|
|
6594
|
+
const resp = await this.client.getDomRevision(req);
|
|
6595
|
+
return Number(resp.revision);
|
|
6596
|
+
}
|
|
6597
|
+
// ──────────────────────────────────────────────────────────────────
|
|
5402
6598
|
// Cookies
|
|
5403
6599
|
// ──────────────────────────────────────────────────────────────────
|
|
5404
6600
|
/**
|
|
@@ -5551,6 +6747,58 @@ var CloudBrowser = class {
|
|
|
5551
6747
|
await this.client.clearStorage(req);
|
|
5552
6748
|
}
|
|
5553
6749
|
// ──────────────────────────────────────────────────────────────────
|
|
6750
|
+
// Auth / DBSC (portable signed-in persona)
|
|
6751
|
+
// ──────────────────────────────────────────────────────────────────
|
|
6752
|
+
/**
|
|
6753
|
+
* Exports the signed-in primary account and DBSC sessions of this
|
|
6754
|
+
* browser context.
|
|
6755
|
+
*
|
|
6756
|
+
* State is read in the browser process, so no page needs to be open.
|
|
6757
|
+
* Returns undefined when the context has neither a signed-in account
|
|
6758
|
+
* nor DBSC sessions.
|
|
6759
|
+
*
|
|
6760
|
+
* @returns AuthSession, or undefined when there is nothing to export
|
|
6761
|
+
*
|
|
6762
|
+
* @throws UNKNOWN_ERROR - the auth session could not be read
|
|
6763
|
+
*
|
|
6764
|
+
* @example
|
|
6765
|
+
* const auth = await browser.getAuthSession();
|
|
6766
|
+
* if (auth) await fs.writeFile("auth.json", JSON.stringify(auth));
|
|
6767
|
+
*/
|
|
6768
|
+
async getAuthSession() {
|
|
6769
|
+
const resp = await this.client.getAuthSession(
|
|
6770
|
+
create(GetAuthSessionRequestSchema, {
|
|
6771
|
+
sessionId: this.sessionId,
|
|
6772
|
+
apiKey: this.apiKey
|
|
6773
|
+
})
|
|
6774
|
+
);
|
|
6775
|
+
return resp.session ? authSessionFromProto(resp.session) : void 0;
|
|
6776
|
+
}
|
|
6777
|
+
/**
|
|
6778
|
+
* Imports an auth session so the context comes up signed in (and syncing
|
|
6779
|
+
* if syncConsent) with its DBSC sessions restored.
|
|
6780
|
+
*
|
|
6781
|
+
* Call it before navigating. Pair with setCookies() / setStorage() to
|
|
6782
|
+
* restore a full persona.
|
|
6783
|
+
*
|
|
6784
|
+
* @param session - session as returned by getAuthSession()
|
|
6785
|
+
*
|
|
6786
|
+
* @throws UNKNOWN_ERROR - the auth session could not be written
|
|
6787
|
+
*
|
|
6788
|
+
* @example
|
|
6789
|
+
* await browser.setAuthSession(saved);
|
|
6790
|
+
* await browser.navigate("https://mail.google.com");
|
|
6791
|
+
*/
|
|
6792
|
+
async setAuthSession(session) {
|
|
6793
|
+
await this.client.setAuthSession(
|
|
6794
|
+
create(SetAuthSessionRequestSchema, {
|
|
6795
|
+
sessionId: this.sessionId,
|
|
6796
|
+
apiKey: this.apiKey,
|
|
6797
|
+
session: authSessionToProto(session)
|
|
6798
|
+
})
|
|
6799
|
+
);
|
|
6800
|
+
}
|
|
6801
|
+
// ──────────────────────────────────────────────────────────────────
|
|
5554
6802
|
// Devtools / live-UI helpers
|
|
5555
6803
|
// ──────────────────────────────────────────────────────────────────
|
|
5556
6804
|
/**
|
|
@@ -5824,20 +7072,20 @@ var CloudBrowser = class {
|
|
|
5824
7072
|
}
|
|
5825
7073
|
/**
|
|
5826
7074
|
* Answers your WebRTC SDP offer and starts streaming the page as a video
|
|
5827
|
-
* track
|
|
5828
|
-
* The browser is the answerer; you are the offerer (see
|
|
7075
|
+
* track. The browser is the answerer; you are the offerer (see
|
|
5829
7076
|
* {@link getStreamConfig} for the credentials to build the offer).
|
|
5830
7077
|
*
|
|
5831
7078
|
* @param offerSdp - your `RTCPeerConnection`'s SDP offer
|
|
5832
7079
|
*
|
|
5833
|
-
* @returns the SDP answer to apply as the remote description
|
|
7080
|
+
* @returns the SDP answer to apply as the remote description, plus the
|
|
7081
|
+
* viewport to map input coordinates into
|
|
5834
7082
|
*
|
|
5835
7083
|
* @throws UNKNOWN_ERROR - the offer was empty, TURN is unconfigured, or the
|
|
5836
7084
|
* browser could not negotiate the stream
|
|
5837
7085
|
*
|
|
5838
7086
|
* @example
|
|
5839
|
-
* const
|
|
5840
|
-
* await pc.setRemoteDescription({ type: "answer", sdp:
|
|
7087
|
+
* const { answerSdp, viewport } = await browser.startStream(offer.sdp);
|
|
7088
|
+
* await pc.setRemoteDescription({ type: "answer", sdp: answerSdp });
|
|
5841
7089
|
*/
|
|
5842
7090
|
async startStream(offerSdp) {
|
|
5843
7091
|
const resp = await this.client.startStream(
|
|
@@ -5847,7 +7095,10 @@ var CloudBrowser = class {
|
|
|
5847
7095
|
offerSdp
|
|
5848
7096
|
})
|
|
5849
7097
|
);
|
|
5850
|
-
return
|
|
7098
|
+
return {
|
|
7099
|
+
answerSdp: resp.answerSdp,
|
|
7100
|
+
viewport: resp.viewport ? { width: resp.viewport.width, height: resp.viewport.height } : null
|
|
7101
|
+
};
|
|
5851
7102
|
}
|
|
5852
7103
|
/**
|
|
5853
7104
|
* Tears down the live video stream for the session's page. Safe to call even
|
|
@@ -5972,6 +7223,239 @@ var CloudBrowser = class {
|
|
|
5972
7223
|
visible: r.visible
|
|
5973
7224
|
}));
|
|
5974
7225
|
}
|
|
7226
|
+
// ──────────────────────────────────────────────────────────────────
|
|
7227
|
+
// Scripts
|
|
7228
|
+
// ──────────────────────────────────────────────────────────────────
|
|
7229
|
+
/**
|
|
7230
|
+
* Runs `source` in the session's browser and waits for it to finish.
|
|
7231
|
+
*
|
|
7232
|
+
* The script executes in a V8 isolate inside the browser process, not in the
|
|
7233
|
+
* page, and reaches the same operations this SDK exposes through a `browser`
|
|
7234
|
+
* object it is handed. The difference is cost: each call is a function call in
|
|
7235
|
+
* the browser rather than a network round trip, so work that is chatty by
|
|
7236
|
+
* nature — polling for a selector, walking a list, following pagination —
|
|
7237
|
+
* runs in microseconds per step instead of tens of milliseconds.
|
|
7238
|
+
*
|
|
7239
|
+
* This waits for as long as the script runs, and cannot be bounded: the run
|
|
7240
|
+
* id needed to cancel only arrives with the reply. Use
|
|
7241
|
+
* {@link CloudBrowser.startScript} when the script may outlive the caller's
|
|
7242
|
+
* patience, or {@link CloudBrowser.stopScripts} to abandon what this session
|
|
7243
|
+
* is running.
|
|
7244
|
+
*
|
|
7245
|
+
* @param source - JavaScript to execute; its return value comes back as JSON
|
|
7246
|
+
*
|
|
7247
|
+
* @returns the return value and the script's whole console output. A script
|
|
7248
|
+
* that threw is reported as `success: false`, not as a rejection
|
|
7249
|
+
*
|
|
7250
|
+
* @throws UNKNOWN_ERROR - the script could not be delivered to the browser
|
|
7251
|
+
*
|
|
7252
|
+
* @example
|
|
7253
|
+
* const result = await browser.runScript(`
|
|
7254
|
+
* await browser.navigate("https://example.com");
|
|
7255
|
+
* const items = [];
|
|
7256
|
+
* for (const el of await browser.getDOM().querySelectorAll("h1")) {
|
|
7257
|
+
* items.push(el.textContent);
|
|
7258
|
+
* }
|
|
7259
|
+
* return items;
|
|
7260
|
+
* `);
|
|
7261
|
+
* console.log(result.success, result.result);
|
|
7262
|
+
*/
|
|
7263
|
+
async runScript(source) {
|
|
7264
|
+
const resp = await this.client.runScript(
|
|
7265
|
+
create(RunScriptRequestSchema, {
|
|
7266
|
+
sessionId: this.sessionId,
|
|
7267
|
+
apiKey: this.apiKey,
|
|
7268
|
+
source
|
|
7269
|
+
})
|
|
7270
|
+
);
|
|
7271
|
+
return {
|
|
7272
|
+
success: resp.success,
|
|
7273
|
+
result: resp.result,
|
|
7274
|
+
runId: resp.runId,
|
|
7275
|
+
log: resp.log.map(scriptLogEntryFromProto),
|
|
7276
|
+
truncated: resp.truncated
|
|
7277
|
+
};
|
|
7278
|
+
}
|
|
7279
|
+
/**
|
|
7280
|
+
* Launches `source` in the session's browser and resolves as soon as the run
|
|
7281
|
+
* is under way.
|
|
7282
|
+
*
|
|
7283
|
+
* The counterpart to {@link CloudBrowser.runScript}, for scripts that are not
|
|
7284
|
+
* worth waiting on: a watcher that runs for the life of the session, work
|
|
7285
|
+
* that should survive this page. Output arrives at `onEvent` while the caller
|
|
7286
|
+
* gets on with something else, and {@link ScriptRun.wait} collects the
|
|
7287
|
+
* outcome if it is wanted.
|
|
7288
|
+
*
|
|
7289
|
+
* Subscribing has to happen before the launch, because a detached run's
|
|
7290
|
+
* output is not kept anywhere — the browser rejects a start with nobody
|
|
7291
|
+
* listening rather than discard the script's log and result. This call does
|
|
7292
|
+
* both in that order, so nothing the script prints is missed.
|
|
7293
|
+
*
|
|
7294
|
+
* @param source - JavaScript to execute
|
|
7295
|
+
* @param onEvent - called per log line and once for the outcome; see
|
|
7296
|
+
* {@link ScriptEventHandler} for the ordering and blocking rules
|
|
7297
|
+
*
|
|
7298
|
+
* @returns ScriptRun handle for awaiting or cancelling the run
|
|
7299
|
+
*
|
|
7300
|
+
* @throws UNKNOWN_ERROR - the run could not be started
|
|
7301
|
+
*
|
|
7302
|
+
* @example
|
|
7303
|
+
* const run = await browser.startScript(source, (ev) => {
|
|
7304
|
+
* if (ev.log) console.log(ev.log.level, ev.log.message);
|
|
7305
|
+
* });
|
|
7306
|
+
* const outcome = await run.wait();
|
|
7307
|
+
*/
|
|
7308
|
+
async startScript(source, onEvent) {
|
|
7309
|
+
const abort = new AbortController();
|
|
7310
|
+
let subscribed;
|
|
7311
|
+
const ready = new Promise((resolve) => {
|
|
7312
|
+
subscribed = resolve;
|
|
7313
|
+
});
|
|
7314
|
+
const stream = this.client.streamScriptEvents(
|
|
7315
|
+
create(StreamScriptEventsRequestSchema, {
|
|
7316
|
+
sessionId: this.sessionId,
|
|
7317
|
+
apiKey: this.apiKey
|
|
7318
|
+
}),
|
|
7319
|
+
{ signal: abort.signal, onHeader: () => subscribed() }
|
|
7320
|
+
);
|
|
7321
|
+
const iterator = stream[Symbol.asyncIterator]();
|
|
7322
|
+
const first = iterator.next();
|
|
7323
|
+
const ended = first.then((result) => {
|
|
7324
|
+
if (result.done) {
|
|
7325
|
+
throw new BrowserScaleError(
|
|
7326
|
+
"browserscale.startScript: stream closed before the subscription was established"
|
|
7327
|
+
);
|
|
7328
|
+
}
|
|
7329
|
+
});
|
|
7330
|
+
ended.catch(() => {
|
|
7331
|
+
});
|
|
7332
|
+
try {
|
|
7333
|
+
await Promise.race([ready, ended]);
|
|
7334
|
+
} catch (err) {
|
|
7335
|
+
abort.abort();
|
|
7336
|
+
throw err;
|
|
7337
|
+
}
|
|
7338
|
+
let runId;
|
|
7339
|
+
try {
|
|
7340
|
+
const resp = await this.client.startScript(
|
|
7341
|
+
create(StartScriptRequestSchema, {
|
|
7342
|
+
sessionId: this.sessionId,
|
|
7343
|
+
apiKey: this.apiKey,
|
|
7344
|
+
source
|
|
7345
|
+
})
|
|
7346
|
+
);
|
|
7347
|
+
runId = resp.runId;
|
|
7348
|
+
} catch (err) {
|
|
7349
|
+
abort.abort();
|
|
7350
|
+
throw err;
|
|
7351
|
+
}
|
|
7352
|
+
const resumed = resumeStream(iterator, first);
|
|
7353
|
+
return new ScriptRun({
|
|
7354
|
+
runId,
|
|
7355
|
+
stream: resumed,
|
|
7356
|
+
onEvent,
|
|
7357
|
+
abort,
|
|
7358
|
+
cancel: (id) => this.stopScripts(id)
|
|
7359
|
+
});
|
|
7360
|
+
}
|
|
7361
|
+
/**
|
|
7362
|
+
* Watches script output in this session without starting anything.
|
|
7363
|
+
*
|
|
7364
|
+
* For the case {@link CloudBrowser.startScript} cannot cover: a run somebody
|
|
7365
|
+
* else launched, or one this page started before it reloaded. Several readers
|
|
7366
|
+
* can watch the same session, each with its own buffer.
|
|
7367
|
+
*
|
|
7368
|
+
* Only output produced from now on arrives — lines printed before the
|
|
7369
|
+
* subscription existed are not kept. A run that has already finished is
|
|
7370
|
+
* therefore invisible here; {@link CloudBrowser.listScriptRuns} is how you
|
|
7371
|
+
* tell that apart from a run that is merely quiet.
|
|
7372
|
+
*
|
|
7373
|
+
* @param runId - run to follow, or `""` to follow every run in the session
|
|
7374
|
+
* @param onEvent - called per event; see {@link ScriptEventHandler} for the
|
|
7375
|
+
* ordering and blocking rules
|
|
7376
|
+
*
|
|
7377
|
+
* @returns ScriptFollow handle for stopping the subscription
|
|
7378
|
+
*
|
|
7379
|
+
* @throws UNKNOWN_ERROR - the subscription could not be opened
|
|
7380
|
+
*
|
|
7381
|
+
* @example
|
|
7382
|
+
* const follow = await browser.followScript(runId, (ev) => {
|
|
7383
|
+
* if (ev.log) console.log(ev.log.message);
|
|
7384
|
+
* });
|
|
7385
|
+
* try {
|
|
7386
|
+
* await follow.wait();
|
|
7387
|
+
* } finally {
|
|
7388
|
+
* await follow.stop();
|
|
7389
|
+
* }
|
|
7390
|
+
*/
|
|
7391
|
+
async followScript(runId, onEvent) {
|
|
7392
|
+
const abort = new AbortController();
|
|
7393
|
+
const stream = this.client.streamScriptEvents(
|
|
7394
|
+
create(StreamScriptEventsRequestSchema, {
|
|
7395
|
+
sessionId: this.sessionId,
|
|
7396
|
+
apiKey: this.apiKey,
|
|
7397
|
+
runId
|
|
7398
|
+
}),
|
|
7399
|
+
{ signal: abort.signal }
|
|
7400
|
+
);
|
|
7401
|
+
return new ScriptFollow({ stream, onEvent, abort });
|
|
7402
|
+
}
|
|
7403
|
+
/**
|
|
7404
|
+
* Cancels runs in this session and reports how many it ended.
|
|
7405
|
+
*
|
|
7406
|
+
* An empty `runId` cancels every run in the session, which is the only form
|
|
7407
|
+
* available to a caller that never learned an id — notably one abandoning a
|
|
7408
|
+
* {@link CloudBrowser.runScript}.
|
|
7409
|
+
*
|
|
7410
|
+
* @param runId - run to cancel, or `""` for all of them
|
|
7411
|
+
*
|
|
7412
|
+
* @returns how many runs were cancelled; 0 when the id named nothing in
|
|
7413
|
+
* flight
|
|
7414
|
+
*
|
|
7415
|
+
* @throws UNKNOWN_ERROR - the cancel could not be delivered
|
|
7416
|
+
*
|
|
7417
|
+
* @example
|
|
7418
|
+
* await browser.stopScripts(""); // abandon everything running
|
|
7419
|
+
*/
|
|
7420
|
+
async stopScripts(runId) {
|
|
7421
|
+
const resp = await this.client.stopScripts(
|
|
7422
|
+
create(StopScriptsRequestSchema, {
|
|
7423
|
+
sessionId: this.sessionId,
|
|
7424
|
+
apiKey: this.apiKey,
|
|
7425
|
+
runId
|
|
7426
|
+
})
|
|
7427
|
+
);
|
|
7428
|
+
return resp.stopped;
|
|
7429
|
+
}
|
|
7430
|
+
/**
|
|
7431
|
+
* Reports the scripts still running in this session.
|
|
7432
|
+
*
|
|
7433
|
+
* Only runs in flight — a finished run is reported once on the event stream
|
|
7434
|
+
* and then forgotten, so this is not a history. Its use is finding work this
|
|
7435
|
+
* caller did not start: a script a previous page left behind, which
|
|
7436
|
+
* {@link CloudBrowser.stopScripts} needs an id to name.
|
|
7437
|
+
*
|
|
7438
|
+
* @returns one entry per run still executing
|
|
7439
|
+
*
|
|
7440
|
+
* @throws UNKNOWN_ERROR - the session could not be queried
|
|
7441
|
+
*
|
|
7442
|
+
* @example
|
|
7443
|
+
* for (const run of await browser.listScriptRuns()) {
|
|
7444
|
+
* console.log(run.runId, run.runningMs);
|
|
7445
|
+
* }
|
|
7446
|
+
*/
|
|
7447
|
+
async listScriptRuns() {
|
|
7448
|
+
const resp = await this.client.listScriptRuns(
|
|
7449
|
+
create(ListScriptRunsRequestSchema, {
|
|
7450
|
+
sessionId: this.sessionId,
|
|
7451
|
+
apiKey: this.apiKey
|
|
7452
|
+
})
|
|
7453
|
+
);
|
|
7454
|
+
return resp.runs.map((run) => ({
|
|
7455
|
+
runId: run.runId,
|
|
7456
|
+
runningMs: Number(run.runningMs)
|
|
7457
|
+
}));
|
|
7458
|
+
}
|
|
5975
7459
|
/**
|
|
5976
7460
|
* @internal — a reaction match/action must be a css()/js() locator: node()
|
|
5977
7461
|
* and at() are rejected (a reaction watches for a condition, like a wait).
|
|
@@ -5989,13 +7473,36 @@ var CloudBrowser = class {
|
|
|
5989
7473
|
}
|
|
5990
7474
|
}
|
|
5991
7475
|
};
|
|
7476
|
+
async function* resumeStream(iterator, first) {
|
|
7477
|
+
let result = await first;
|
|
7478
|
+
while (!result.done) {
|
|
7479
|
+
yield result.value;
|
|
7480
|
+
result = await iterator.next();
|
|
7481
|
+
}
|
|
7482
|
+
}
|
|
5992
7483
|
|
|
5993
7484
|
// src/ws-transport.ts
|
|
5994
7485
|
var nextId = 0;
|
|
5995
|
-
var
|
|
7486
|
+
var STATUS_OK = 0;
|
|
7487
|
+
var STATUS_STREAM_MSG = 2;
|
|
7488
|
+
var STATUS_STREAM_END = 3;
|
|
7489
|
+
var STATUS_STREAM_READY = 4;
|
|
7490
|
+
var CANCEL = "cancel";
|
|
7491
|
+
function wake(stream) {
|
|
7492
|
+
const pending = stream.wake;
|
|
7493
|
+
stream.wake = null;
|
|
7494
|
+
pending?.();
|
|
7495
|
+
}
|
|
7496
|
+
function endStream(stream, err) {
|
|
7497
|
+
stream.done = true;
|
|
7498
|
+
stream.error = err;
|
|
7499
|
+
wake(stream);
|
|
7500
|
+
}
|
|
7501
|
+
var WebSocketTransport = class _WebSocketTransport {
|
|
5996
7502
|
constructor(url) {
|
|
5997
7503
|
this.ws = null;
|
|
5998
7504
|
this.pending = /* @__PURE__ */ new Map();
|
|
7505
|
+
this.streams = /* @__PURE__ */ new Map();
|
|
5999
7506
|
this.queue = [];
|
|
6000
7507
|
this.connected = false;
|
|
6001
7508
|
this.url = url;
|
|
@@ -6018,10 +7525,15 @@ var WebSocketTransport = class {
|
|
|
6018
7525
|
const id = view.getUint32(0, true);
|
|
6019
7526
|
const status = buf[4];
|
|
6020
7527
|
const payload = buf.slice(5);
|
|
7528
|
+
const stream = this.streams.get(id);
|
|
7529
|
+
if (stream) {
|
|
7530
|
+
this.dispatchStreamFrame(id, stream, status, payload);
|
|
7531
|
+
return;
|
|
7532
|
+
}
|
|
6021
7533
|
const call = this.pending.get(id);
|
|
6022
7534
|
if (!call) return;
|
|
6023
7535
|
this.pending.delete(id);
|
|
6024
|
-
if (status ===
|
|
7536
|
+
if (status === STATUS_OK) {
|
|
6025
7537
|
call.resolve(payload);
|
|
6026
7538
|
} else {
|
|
6027
7539
|
call.reject(new Error(new TextDecoder().decode(payload)));
|
|
@@ -6033,12 +7545,44 @@ var WebSocketTransport = class {
|
|
|
6033
7545
|
call.reject(new Error("WebSocket closed"));
|
|
6034
7546
|
}
|
|
6035
7547
|
this.pending.clear();
|
|
7548
|
+
const closed = new Error("WebSocket closed");
|
|
7549
|
+
for (const [, stream] of this.streams) {
|
|
7550
|
+
stream.onReadyFailed?.(closed);
|
|
7551
|
+
endStream(stream, closed);
|
|
7552
|
+
}
|
|
7553
|
+
this.streams.clear();
|
|
6036
7554
|
setTimeout(() => this.connect(), 1e3);
|
|
6037
7555
|
};
|
|
6038
7556
|
this.ws.onerror = () => {
|
|
6039
7557
|
this.ws?.close();
|
|
6040
7558
|
};
|
|
6041
7559
|
}
|
|
7560
|
+
dispatchStreamFrame(id, stream, status, payload) {
|
|
7561
|
+
switch (status) {
|
|
7562
|
+
case STATUS_STREAM_READY: {
|
|
7563
|
+
const ready = stream.onReady;
|
|
7564
|
+
stream.onReady = null;
|
|
7565
|
+
stream.onReadyFailed = null;
|
|
7566
|
+
ready?.();
|
|
7567
|
+
return;
|
|
7568
|
+
}
|
|
7569
|
+
case STATUS_STREAM_MSG:
|
|
7570
|
+
stream.queue.push(payload);
|
|
7571
|
+
wake(stream);
|
|
7572
|
+
return;
|
|
7573
|
+
case STATUS_STREAM_END:
|
|
7574
|
+
this.streams.delete(id);
|
|
7575
|
+
endStream(stream, null);
|
|
7576
|
+
return;
|
|
7577
|
+
default: {
|
|
7578
|
+
const err = new Error(new TextDecoder().decode(payload));
|
|
7579
|
+
this.streams.delete(id);
|
|
7580
|
+
stream.onReadyFailed?.(err);
|
|
7581
|
+
endStream(stream, err);
|
|
7582
|
+
return;
|
|
7583
|
+
}
|
|
7584
|
+
}
|
|
7585
|
+
}
|
|
6042
7586
|
send(data) {
|
|
6043
7587
|
if (this.connected && this.ws?.readyState === WebSocket.OPEN) {
|
|
6044
7588
|
this.ws.send(data);
|
|
@@ -6046,21 +7590,25 @@ var WebSocketTransport = class {
|
|
|
6046
7590
|
this.queue.push(data);
|
|
6047
7591
|
}
|
|
6048
7592
|
}
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
const
|
|
6052
|
-
const inputMsg = create(method.input, input);
|
|
6053
|
-
const payload = toBinary(method.input, inputMsg);
|
|
6054
|
-
const methodBytes = new TextEncoder().encode(methodName);
|
|
7593
|
+
/** Builds a request frame: [4B id LE][2B method_len LE][method][payload]. */
|
|
7594
|
+
static frame(id, method, payload) {
|
|
7595
|
+
const methodBytes = new TextEncoder().encode(method);
|
|
6055
7596
|
const frame = new Uint8Array(6 + methodBytes.length + payload.length);
|
|
6056
7597
|
const view = new DataView(frame.buffer);
|
|
6057
7598
|
view.setUint32(0, id, true);
|
|
6058
7599
|
view.setUint16(4, methodBytes.length, true);
|
|
6059
7600
|
frame.set(methodBytes, 6);
|
|
6060
7601
|
frame.set(payload, 6 + methodBytes.length);
|
|
7602
|
+
return frame;
|
|
7603
|
+
}
|
|
7604
|
+
async unary(method, signal, _timeoutMs, _header, input, _contextValues) {
|
|
7605
|
+
const id = ++nextId;
|
|
7606
|
+
const methodName = `${method.parent.typeName}/${method.name}`;
|
|
7607
|
+
const inputMsg = create(method.input, input);
|
|
7608
|
+
const payload = toBinary(method.input, inputMsg);
|
|
6061
7609
|
const responsePayload = await new Promise((resolve, reject) => {
|
|
6062
7610
|
this.pending.set(id, { resolve, reject });
|
|
6063
|
-
this.send(frame);
|
|
7611
|
+
this.send(_WebSocketTransport.frame(id, methodName, payload));
|
|
6064
7612
|
if (signal) {
|
|
6065
7613
|
signal.addEventListener("abort", () => {
|
|
6066
7614
|
this.pending.delete(id);
|
|
@@ -6078,8 +7626,76 @@ var WebSocketTransport = class {
|
|
|
6078
7626
|
message: output
|
|
6079
7627
|
};
|
|
6080
7628
|
}
|
|
6081
|
-
|
|
6082
|
-
|
|
7629
|
+
/**
|
|
7630
|
+
* Runs a server-streaming call. Client-streaming is not supported: the frame
|
|
7631
|
+
* format carries exactly one request message, and no RPC needs more.
|
|
7632
|
+
*
|
|
7633
|
+
* Resolving means the server confirmed the subscription, so a caller may act
|
|
7634
|
+
* on it — arm a network capture, say — without racing the first message.
|
|
7635
|
+
*/
|
|
7636
|
+
async stream(method, signal, _timeoutMs, _header, input, _contextValues) {
|
|
7637
|
+
let request;
|
|
7638
|
+
for await (const msg of input) {
|
|
7639
|
+
request = msg;
|
|
7640
|
+
break;
|
|
7641
|
+
}
|
|
7642
|
+
if (request === void 0) {
|
|
7643
|
+
throw new Error("WebSocket transport: server streaming needs one request message");
|
|
7644
|
+
}
|
|
7645
|
+
const id = ++nextId;
|
|
7646
|
+
const methodName = `${method.parent.typeName}/${method.name}`;
|
|
7647
|
+
const payload = toBinary(method.input, create(method.input, request));
|
|
7648
|
+
const stream = {
|
|
7649
|
+
queue: [],
|
|
7650
|
+
wake: null,
|
|
7651
|
+
done: false,
|
|
7652
|
+
error: null,
|
|
7653
|
+
onReady: null,
|
|
7654
|
+
onReadyFailed: null
|
|
7655
|
+
};
|
|
7656
|
+
this.streams.set(id, stream);
|
|
7657
|
+
const ready = new Promise((resolve, reject) => {
|
|
7658
|
+
stream.onReady = resolve;
|
|
7659
|
+
stream.onReadyFailed = reject;
|
|
7660
|
+
});
|
|
7661
|
+
this.send(_WebSocketTransport.frame(id, methodName, payload));
|
|
7662
|
+
signal?.addEventListener("abort", () => this.cancelStream(id), { once: true });
|
|
7663
|
+
await ready;
|
|
7664
|
+
return {
|
|
7665
|
+
stream: true,
|
|
7666
|
+
service: method.parent,
|
|
7667
|
+
method,
|
|
7668
|
+
header: new Headers(),
|
|
7669
|
+
trailer: new Headers(),
|
|
7670
|
+
message: this.readStream(method, id, stream)
|
|
7671
|
+
};
|
|
7672
|
+
}
|
|
7673
|
+
async *readStream(method, id, stream) {
|
|
7674
|
+
try {
|
|
7675
|
+
for (; ; ) {
|
|
7676
|
+
while (stream.queue.length === 0 && !stream.done) {
|
|
7677
|
+
await new Promise((resolve) => {
|
|
7678
|
+
stream.wake = resolve;
|
|
7679
|
+
});
|
|
7680
|
+
}
|
|
7681
|
+
const buf = stream.queue.shift();
|
|
7682
|
+
if (buf === void 0) {
|
|
7683
|
+
if (stream.error) throw stream.error;
|
|
7684
|
+
return;
|
|
7685
|
+
}
|
|
7686
|
+
yield fromBinary(method.output, buf);
|
|
7687
|
+
}
|
|
7688
|
+
} finally {
|
|
7689
|
+
this.cancelStream(id);
|
|
7690
|
+
}
|
|
7691
|
+
}
|
|
7692
|
+
/** Drops a stream locally and asks the server to stop producing. */
|
|
7693
|
+
cancelStream(id) {
|
|
7694
|
+
const stream = this.streams.get(id);
|
|
7695
|
+
if (!stream) return;
|
|
7696
|
+
this.streams.delete(id);
|
|
7697
|
+
endStream(stream, null);
|
|
7698
|
+
this.send(_WebSocketTransport.frame(id, CANCEL, new Uint8Array(0)));
|
|
6083
7699
|
}
|
|
6084
7700
|
close() {
|
|
6085
7701
|
this.ws?.close();
|
|
@@ -6171,10 +7787,14 @@ export {
|
|
|
6171
7787
|
DefaultSteadyMs,
|
|
6172
7788
|
DefaultVisible,
|
|
6173
7789
|
DefaultWaitTimeoutMs,
|
|
7790
|
+
DomMirror,
|
|
6174
7791
|
DragError,
|
|
6175
7792
|
FillError,
|
|
6176
7793
|
Locator,
|
|
6177
7794
|
MoveError,
|
|
7795
|
+
NetworkCapture,
|
|
7796
|
+
ScriptFollow,
|
|
7797
|
+
ScriptRun,
|
|
6178
7798
|
ScrollError,
|
|
6179
7799
|
SelectOptionError,
|
|
6180
7800
|
WaitError,
|