From dede6b14498ef4a3c824635cdefe4b579cebe046 Mon Sep 17 00:00:00 2001 From: <> Date: Mon, 27 Jan 2025 09:59:05 +0000 Subject: [PATCH] Deployed 3a4b2a5 with MkDocs version: 1.6.1 --- .nojekyll | 0 404.html | 1 + Atna/index.html | 37 + AuthenticateUser/index.html | 339 + GetXAssertion/index.html | 128 + IdPRenew/index.html | 183 + ImmunizationAdministrationDocument/index.html | 383 + ImmunizationAdministrationSections/index.html | 342 + MedicationCardDocument/index.html | 906 +++ PDQ/index.html | 254 + PIXFeed/index.html | 229 + PIXQuery/index.html | 224 + ProvideAndRegister/index.html | 272 + ProvideXAssertion/index.html | 32 + Questionnaire/index.html | 852 +++ RegistryStoredQuery/index.html | 449 ++ RetrieveDocumentSet/index.html | 219 + SSOLogout/index.html | 143 + assets/images/favicon.png | Bin 0 -> 1870 bytes assets/javascripts/bundle.cd18aaf1.min.js | 29 + assets/javascripts/bundle.cd18aaf1.min.js.map | 7 + assets/javascripts/lunr/min/lunr.ar.min.js | 1 + assets/javascripts/lunr/min/lunr.da.min.js | 18 + assets/javascripts/lunr/min/lunr.de.min.js | 18 + assets/javascripts/lunr/min/lunr.du.min.js | 18 + assets/javascripts/lunr/min/lunr.el.min.js | 1 + assets/javascripts/lunr/min/lunr.es.min.js | 18 + assets/javascripts/lunr/min/lunr.fi.min.js | 18 + assets/javascripts/lunr/min/lunr.fr.min.js | 18 + assets/javascripts/lunr/min/lunr.he.min.js | 1 + assets/javascripts/lunr/min/lunr.hi.min.js | 1 + assets/javascripts/lunr/min/lunr.hu.min.js | 18 + assets/javascripts/lunr/min/lunr.hy.min.js | 1 + assets/javascripts/lunr/min/lunr.it.min.js | 18 + assets/javascripts/lunr/min/lunr.ja.min.js | 1 + assets/javascripts/lunr/min/lunr.jp.min.js | 1 + assets/javascripts/lunr/min/lunr.kn.min.js | 1 + assets/javascripts/lunr/min/lunr.ko.min.js | 1 + assets/javascripts/lunr/min/lunr.multi.min.js | 1 + assets/javascripts/lunr/min/lunr.nl.min.js | 18 + assets/javascripts/lunr/min/lunr.no.min.js | 18 + assets/javascripts/lunr/min/lunr.pt.min.js | 18 + assets/javascripts/lunr/min/lunr.ro.min.js | 18 + assets/javascripts/lunr/min/lunr.ru.min.js | 18 + assets/javascripts/lunr/min/lunr.sa.min.js | 1 + .../lunr/min/lunr.stemmer.support.min.js | 1 + assets/javascripts/lunr/min/lunr.sv.min.js | 18 + assets/javascripts/lunr/min/lunr.ta.min.js | 1 + assets/javascripts/lunr/min/lunr.te.min.js | 1 + assets/javascripts/lunr/min/lunr.th.min.js | 1 + assets/javascripts/lunr/min/lunr.tr.min.js | 18 + assets/javascripts/lunr/min/lunr.vi.min.js | 1 + assets/javascripts/lunr/min/lunr.zh.min.js | 1 + assets/javascripts/lunr/tinyseg.js | 206 + assets/javascripts/lunr/wordcut.js | 6708 +++++++++++++++++ .../workers/search.f886a092.min.js | 42 + .../workers/search.f886a092.min.js.map | 7 + assets/stylesheets/main.fad675c6.min.css | 1 + assets/stylesheets/main.fad675c6.min.css.map | 1 + assets/stylesheets/palette.356b1318.min.css | 1 + .../stylesheets/palette.356b1318.min.css.map | 1 + css/timeago.css | 15 + gazelle/index.html | 1 + index.html | 1 + js/timeago.min.js | 2 + js/timeago_mkdocs_material.js | 33 + media/SAML-Artifact-flow.png | Bin 0 -> 40104 bytes media/SAML-Logout.png | Bin 0 -> 22522 bytes media/ehealthsuisse.css | 87 + media/favicon.png | Bin 0 -> 1434 bytes media/logo.png | Bin 0 -> 15784 bytes media/logo.svg | 5 + playground/index.html | 1 + search/search_index.json | 1 + sitemap.xml | 79 + sitemap.xml.gz | Bin 0 -> 393 bytes 76 files changed, 12508 insertions(+) create mode 100644 .nojekyll create mode 100644 404.html create mode 100644 Atna/index.html create mode 100644 AuthenticateUser/index.html create mode 100644 GetXAssertion/index.html create mode 100644 IdPRenew/index.html create mode 100644 ImmunizationAdministrationDocument/index.html create mode 100644 ImmunizationAdministrationSections/index.html create mode 100644 MedicationCardDocument/index.html create mode 100644 PDQ/index.html create mode 100644 PIXFeed/index.html create mode 100644 PIXQuery/index.html create mode 100644 ProvideAndRegister/index.html create mode 100644 ProvideXAssertion/index.html create mode 100644 Questionnaire/index.html create mode 100644 RegistryStoredQuery/index.html create mode 100644 RetrieveDocumentSet/index.html create mode 100644 SSOLogout/index.html create mode 100644 assets/images/favicon.png create mode 100644 assets/javascripts/bundle.cd18aaf1.min.js create mode 100644 assets/javascripts/bundle.cd18aaf1.min.js.map create mode 100644 assets/javascripts/lunr/min/lunr.ar.min.js create mode 100644 assets/javascripts/lunr/min/lunr.da.min.js create mode 100644 assets/javascripts/lunr/min/lunr.de.min.js create mode 100644 assets/javascripts/lunr/min/lunr.du.min.js create mode 100644 assets/javascripts/lunr/min/lunr.el.min.js create mode 100644 assets/javascripts/lunr/min/lunr.es.min.js create mode 100644 assets/javascripts/lunr/min/lunr.fi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.fr.min.js create mode 100644 assets/javascripts/lunr/min/lunr.he.min.js create mode 100644 assets/javascripts/lunr/min/lunr.hi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.hu.min.js create mode 100644 assets/javascripts/lunr/min/lunr.hy.min.js create mode 100644 assets/javascripts/lunr/min/lunr.it.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ja.min.js create mode 100644 assets/javascripts/lunr/min/lunr.jp.min.js create mode 100644 assets/javascripts/lunr/min/lunr.kn.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ko.min.js create mode 100644 assets/javascripts/lunr/min/lunr.multi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.nl.min.js create mode 100644 assets/javascripts/lunr/min/lunr.no.min.js create mode 100644 assets/javascripts/lunr/min/lunr.pt.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ro.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ru.min.js create mode 100644 assets/javascripts/lunr/min/lunr.sa.min.js create mode 100644 assets/javascripts/lunr/min/lunr.stemmer.support.min.js create mode 100644 assets/javascripts/lunr/min/lunr.sv.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ta.min.js create mode 100644 assets/javascripts/lunr/min/lunr.te.min.js create mode 100644 assets/javascripts/lunr/min/lunr.th.min.js create mode 100644 assets/javascripts/lunr/min/lunr.tr.min.js create mode 100644 assets/javascripts/lunr/min/lunr.vi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.zh.min.js create mode 100644 assets/javascripts/lunr/tinyseg.js create mode 100644 assets/javascripts/lunr/wordcut.js create mode 100644 assets/javascripts/workers/search.f886a092.min.js create mode 100644 assets/javascripts/workers/search.f886a092.min.js.map create mode 100644 assets/stylesheets/main.fad675c6.min.css create mode 100644 assets/stylesheets/main.fad675c6.min.css.map create mode 100644 assets/stylesheets/palette.356b1318.min.css create mode 100644 assets/stylesheets/palette.356b1318.min.css.map create mode 100644 css/timeago.css create mode 100644 gazelle/index.html create mode 100644 index.html create mode 100644 js/timeago.min.js create mode 100644 js/timeago_mkdocs_material.js create mode 100644 media/SAML-Artifact-flow.png create mode 100644 media/SAML-Logout.png create mode 100644 media/ehealthsuisse.css create mode 100644 media/favicon.png create mode 100644 media/logo.png create mode 100644 media/logo.svg create mode 100644 playground/index.html create mode 100644 search/search_index.json create mode 100644 sitemap.xml create mode 100644 sitemap.xml.gz diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/404.html b/404.html new file mode 100644 index 0000000..b5cad30 --- /dev/null +++ b/404.html @@ -0,0 +1 @@ + EPR by example

404 - Not found

\ No newline at end of file diff --git a/Atna/index.html b/Atna/index.html new file mode 100644 index 0000000..ebb39cf --- /dev/null +++ b/Atna/index.html @@ -0,0 +1,37 @@ + Audit logs - EPR by example
Skip to content

Audit logs

When executing most of the transactions with the EPR, there is a requirement to send an audit log to the community describing who did what and when, on the client's side. The community also creates such an audit log, and both can be compared if the need arises.

Since the audit logs may contain information about the transaction outcome, you have to wait for the community response before creating the audit log.

Warning

You have to do your best efforts to send the audit logs, even if the transaction has failed (network issue or error 500 for example).

Specifications

The base specification of the <AuditMessage> format is given in DICOM PS3.15. Additional requirements are described in:

Basic structure

An audit log is simply wrapped in a <AuditMessage> element. No namespace is needed.

The first child is a EventIdentification element that describes the transaction:

<EventIdentification EventActionCode="★action_code" EventDateTime="★date_time" EventOutcomeIndicator="★outcome">
+  <EventID csd-code="★transaction_code" codeSystemName="DCM" originalText="★transaction_display"/>
+  <EventTypeCode csd-code="★type_code" codeSystemName="IHE Transactions" originalText="★type_display"/>
+  <PurposeOfUse csd-code="★puo_code" codeSystemName="2.16.756.5.30.1.127.3.10.5" originalText="★puo_display" />
+</EventIdentification>
+
where:

  • action_code, transaction_code, transaction_display,type_code and type_display depend on the transaction type and are defined in the IHE transaction profile, in the 'Security Considerations' section;
  • date_time is the date and time at which the transaction was made. It shall contain a timezone, and shall follow the xsd:dateTime format;
  • outcome describes whether the transaction was successful or not: 0 is a success, 4 is a minor failure, 8 is a serious failure and 12 is a major failure. The choice of the failure type is left to the implementers;
  • puo_code and puo_display are the Purpose of Use code and display name, respectively. It is mandatory when the transaction is secured by XUA; otherwise, the whole <PurposeOfUse> element shall be omitted.

Then, two ActiveParticipants describe the source and destination participants.

The first ActiveParticipant describes the source participant, which often is the one that has sent the transaction (but not always). It has the following content:

<ActiveParticipant UserID="★user_id" AlternativeUserID="★alt_user_id" NetworkAccessPointID="★nap_id" NetworkAccessPointTypeCode="★nap_type" UserIsRequest="true">
+  <RoleIDCode csd-code="110153" codeSystemName="DCM" originalText="Source Role ID"/>
+</ActiveParticipant>
+
where:

  • user_id is required and can be filled as you want. It can be a process ID, a login name, or other identifiers;
  • alt_user_id is the process ID as used within the local operating system in the local system logs;
  • nap_id is the DNS name or IP address of the source;
  • nap_type is 1 for machine (DNS) name, 2 for IP address.

Other attributes are optional.

The second ActiveParticipant describes the destination participant, which often is the one that has received the transaction (but not always). It has the following content:

<ActiveParticipant UserID="★user_id" NetworkAccessPointID="★nap_id" NetworkAccessPointTypeCode="★nap_type" UserIsRequest="false">
+  <RoleIDCode csd-code="110152" codeSystemName="DCM" originalText="Destination Role ID"/>
+</ActiveParticipant>
+
where:

  • user_id is the SOAP endpoint URI;
  • nap_id is the DNS name or IP address of the destination;
  • nap_type is 1 for machine (DNS) name, 2 for IP address.

Other attributes are optional.

Warning

In some transactions such as ITI-43, the source and destination participants are reversed. Always check the IHE specifications for the correct order. The participant "server" is the one having the "SOAP endpoint URI", the participant "client" is the one having the "process ID".

Following that is the <AuditSourceIdentification> element. It contains the following:

<AuditSourceIdentification AuditSourceID="★source" AuditEnterpriseSiteID="★oid" />
+
where oid is required by the Amendment 1 to Annex 5 (§1.5.2) and must be an OID of your OID hierarchy. Please ask your community if it has requirements about the use of this OID. The source value is required by the standard, but its value choice is left to the implementers; it should contain an "Identifier of the source". You can copy the oid value if you want.

Requirements for transactions secured by XUA

If the transaction is secured by XUA (like ITI-18, ITI-41, ITI-43), then additional ActiveParticipants shall be specified to describe the authenticated participant of the transaction.

The first additional ActiveParticipant is described in IHE ITI-40, §3.40.4.2. It is required for all roles. It shall contain:

<ActiveParticipant UserID="★user_id" UserName="★alias&lt;★user@★issuer&gt;" UserIsRequest="false" />
+
where:

  • user_id is required and can be filled as you want. It can be a process ID, a login name, or other identifiers;
  • alias (optional) is the SAML Assertion's Subject/NameID/@SPProvidedID attribute;
  • user (required) is the SAML Assertion's Subject/NameID content;
  • issuer (required) is the SAML Assertion's Issuer content.

The other attributes and subject role specification are optional.

The second additional ActiveParticipant is described in the Amendment 1 to Annex 5, §1.6.4.3.5.1. It is required for all roles. It shall contain:

<ActiveParticipant UserID="★user_id" UserName="★user_name" UserIsRequestor="false">
+    <RoleIDCode csd-code="★code" codeSystemName="2.16.756.5.30.1.127.3.10.6" originalText="★display"/>
+</ActiveParticipant>
+
where:

  • user_id (required) is the SAML Assertion's Subject/NameID content;
  • user_name (required) is the SAML Assertion's AttributeStatement/Attribute[@Name="urn:oasis:names:tc:xspa:1.0:subject:subject-id"]/AttributeValue content;
  • code (required) is the SAML Assertion's AttributeStatement/Attribute[@Name="urn:oasis:names:tc:xacml:2.0:subject:role"]/AttributeValue/Role/@code attribute.

Other attributes are optional.

The third additional ActiveParticipant is described in the Amendment 1 to Annex 5, §1.6.4.3.5.1. It is only required for assistants and technical users. It shall contain:

<ActiveParticipant UserID="★user_id" UserName="★user_name" UserIsRequest="false">
+    <RoleIDCode csd-code="★code" codeSystemName="2.16.756.5.30.1.127.3.10.6" originalText="★display" />
+</ActiveParticipant>
+
where:

  • user_id (required) is the SAML Assertion's Subject/SubjectConfirmation/NameID content;
  • user_name (required) is the SAML Assertion's Subject/SubjectConfirmation/SubjectConfirmationData/AttributeStatement/Attribute[@Name="urn:oasis:names:tc:xspa:1.0:subject:subject-id"]/AttributeValue content;
  • code (required) is either TCU or ASS.

Special parts

Some ParticipantObjectIdentification elements may be used to include additional details about the query content.

Query

For ITI-18 transaction, the query content has to be included in the <ParticipantObjectIdentification> element:

<ParticipantObjectIdentification ParticipantObjectID="★participant_object_id" ParticipantObjectTypeCode="2" ParticipantObjectTypeCodeRole="24">
+  <ParticipantObjectIDTypeCode csd-code="★transaction_code" codeSystemName="IHE Transactions" originalText="★transaction_display" />
+  <ParticipantObjectQuery>★query_b64</ParticipantObjectQuery>
+  <ParticipantObjectDetail type="QueryEncoding" value="★query_encoding_b64" />
+</ParticipantObjectIdentification>
+
where:

  • participant_object_id is the Stored Query ID for ITI-18, or optional for ITI-45 and ITI-47;
  • transaction_code and transaction_display are the same as in EventIdentification/EventID;
  • query_b64 is the XML representation of a part of the query, base64-encoded:
    • For ITI-18, the AdhocQueryRequest;
    • For ITI-45 and ITI-47, the QueryByParameter segment of the query;
  • query_encoding_b64 is the query encoding, encoded in base64. Usually VVRGLTg= (UTF-8).

For ITI-18 transactions, an additional <ParticipantObjectIdentification> is required inside, if the homeCommunityID is specified in the query:

<ParticipantObjectDetail type="urn:ihe:iti:xca:2010:homeCommunityId" value="★home_community_id_b64" />
+
where home_community_id_b64 is the value of the homeCommunityId, base64-encoded.

Patient

<ParticipantObjectIdentification ParticipantObjectID="★patient_id" ParticipantObjectTypeCode="1" ParticipantObjectTypeCodeRole="1">
+  <ParticipantObjectIDTypeCode csd-code="2" codeSystemName="RFC-3881" originalText="Patient Number" />
+</ParticipantObjectIdentification>
+
where patient_id is the patient identifier encoded in HL7 CX format (e.g. value^^^&1.2.3&ISO).

Submission set

For ITI-41 requests, a <ParticipantObjectIdentification> is required to describe the Submission Set:

<ParticipantObjectIdentification ParticipantObjectID="★submission_set_unique_id" ParticipantObjectTypeCode="2" ParticipantObjectTypeCodeRole="20">
+  <ParticipantObjectIDTypeCode csd-code="urn:uuid:a54d6aa5-d40d-43f9-88c5-b4633d873bdd" originalText="submission set classificationNode" codeSystemName="IHE XDS Metadata"/>
+</ParticipantObjectIdentification>
+
where submission_set_unique_id is the URN-encoded Submission Set unique ID.

Document

For ITI-43 transactions, a <ParticipantObjectIdentification> is required to describe the document to be retrieved:

<ParticipantObjectIdentification ParticipantObjectID="★document_unique_id" ParticipantObjectTypeCode="2" ParticipantObjectTypeCodeRole="3" ParticipantObjectSensitivity="★confidentiality_code">
+  <ParticipantObjectIDTypeCode csd-code="9" codeSystemName="RFC-3881" originalText="Report Number"/>
+  <ParticipantObjectDetail type="Repository Unique Id" value="★repo_unique_id_b64"/>
+</ParticipantObjectIdentification>
+
where:

  • document_unique_id is the document unique ID;
  • repo_unique_id_b64 is the repository unique ID;
  • confidentiality_code is the document confidentiality code, if known. The format is HL7v2 CE with the code system OID as the code (e.g.: 1051000195109^normal^2.16.840.1.113883.6.96).

If the home community ID is known, another ParticipantObjectDetail is required inside: <ParticipantObjectDetail type="ihe:homeCommunityID" value="★home_community_id_b64"/>. The value must be base64-encoded.

Requirements summary

ActiveParticipant ParticipantObjectIdentification
Transaction EventIdentification Source Destination XUA Query Patient Submission Set Document
ITI-18
ITI-41
ITI-43
ITI-44
ITI-45
ITI-47
ITI-57

Sending an audit log

The audit log shall be sent to the community as a syslog message either over TCP (with TLS). The relevant specifications are:

  • RFC 5424: The Syslog Protocol for information about the syslog message format;
  • RFC 5425: Transport Layer Security (TLS) Transport Mapping for Syslog which formalizes sending Syslog messages over TCP;

The choice of values in the Syslog headers is left to the implementers.

An example of a Syslog message sent over TCP is:

2027 <85>1 2024-06-25T13:47:57.600Z mag-cara-695f6f7f49-zsxxw IPF 1 IHE+RFC-3881 - <?xml version="1.0" encoding="UTF-8"?><AuditMessage><EventIdentification EventActionCode="E" EventDateTime="2024-06-25T13:47:57.598829760Z" EventOutcomeIndicator="12"><EventID csd-code="110112" codeSystemName="DCM" originalText="Query" /><EventTypeCode csd-code="ITI-67" codeSystemName="IHE Transactions" originalText="Mobile Document Reference Query" /></EventIdentification><ActiveParticipant UserID="/mag-cara/fhir/DocumentReference" UserIsRequestor="true" NetworkAccessPointID="147.87.210.77" NetworkAccessPointTypeCode="2"><RoleIDCode csd-code="110153" codeSystemName="DCM" originalText="Source Role ID" /></ActiveParticipant><ActiveParticipant UserID="https://test.ahdis.ch/mag-cara/fhir/DocumentReference" AlternativeUserID="1" UserIsRequestor="false" NetworkAccessPointID="10.28.2.28" NetworkAccessPointTypeCode="2"><RoleIDCode csd-code="110152" codeSystemName="DCM" originalText="Destination Role ID" /></ActiveParticipant><AuditSourceIdentification AuditEnterpriseSiteID="1.3.6.1.4.1.21367.2017.2.7.109" AuditSourceID="IPF"><AuditSourceTypeCode csd-code="9" codeSystemName="DCM" originalText="Other" /></AuditSourceIdentification><ParticipantObjectIdentification ParticipantObjectID="urn:oid:1.1.1.99.1|215503a0-11d2-4197-822a-053791ab5a8e" ParticipantObjectTypeCode="1" ParticipantObjectTypeCodeRole="1"><ParticipantObjectIDTypeCode csd-code="2" codeSystemName="RFC-3881" originalText="Patient Number" /></ParticipantObjectIdentification><ParticipantObjectIdentification ParticipantObjectID="MobileDocumentReferenceQuery" ParticipantObjectTypeCode="2" ParticipantObjectTypeCodeRole="24"><ParticipantObjectIDTypeCode csd-code="ITI-67" codeSystemName="IHE Transactions" originalText="Mobile Document Reference Query" /><ParticipantObjectQuery>c3RhdHVzPWN1cnJlbnQmcGF0aWVudC5pZGVudGlmaWVyPXVybjpvaWQ6MS4xLjEuOTkuMXwyMTU1MDNhMC0xMWQyLTQxOTctODIyYS0wNTM3OTFhYjVhOGU=</ParticipantObjectQuery></ParticipantObjectIdentification></AuditMessage>
+

Note

Notice the UTF-8 BOM at the beginning of the payload (just before <?xml). It is required by the syslog protocol for UTF-8 messages. 2027 is the number of bytes in the following message (2025 characters + 2 extra bytes for the BOM).


Last update: 2025-01-27
\ No newline at end of file diff --git a/AuthenticateUser/index.html b/AuthenticateUser/index.html new file mode 100644 index 0000000..3637cbc --- /dev/null +++ b/AuthenticateUser/index.html @@ -0,0 +1,339 @@ + Authenticate User - EPR by example
Skip to content

Authenticate User

Transaction to authenticate a user at a identity provider certified for the Swiss EPR. Primary systems shall use this transaction to retrieve a IdP assertion. The IdP assertion is required to retrieve the XUA Assertion to be used with EPR transactions.

Overview

Primary systems shall use this transaction to retrieve an IdP assertion authentication the user for the access to the Swiss EPR.

The requirements for the transaction are defined in Annex 8 of the ordinances of the Swiss EPR.

The EPR requires primary systems to implement authentication as described in the SAML 2.0 specification family, i.e.,

Primary systems do need implement all the bindings and profiles supported by the SAML 2.0 specification family.

In the Swiss EPR the following bindings are required:

  • HTTP POST Binding.
  • HTTP Artifact Binding.
  • SAML SOAP Binding.

In the Swiss EPR the following profiles are required:

  • Web Browser SSO Profile.
  • Single Logout Profile.

The usage of the profiles and binding used to authenticate user for the Swiss EPR is described in the following sections.

Transactions

The transaction to authenticate a user for the access to the Swiss EPR is a multi-step flow consisting of HTTP Post and SOAP Web Service calls, as displayed in the following figure:

Authentication Sequence

Figure 1: Authentication Sequence Diagram

The sequence consists of the following steps, each using assigned transaction messages:

  • [01 .. 04] The user (claimant) is redirected to the identity provider via the user agent (browser) with a SAML 2.0 AuthnRequest message.
  • [05] The user (claimant) authenticates at the identity provider with her authentication means.
  • [06 .. 07] The identity provider responds a SAML 2.0 artifact in a HTTP redirect to the primary system (relying party).
  • [08] The primary system sends a ArtifactResolve message to resolve the SAML artifact to the SAML 2 IdP Assertion via the SOAP backchannel.
  • [09] The IdP responds the IdP Assertion in the ArtifactResponse message.

Authentication Request

The transaction shall be performed by the primary system when the user aims to access the EPR. The primary system shall redirect the user agent (browser) to the IdP authentication endpoint with a AuthnRequest message as defined in Assertions and Protocols for the OASIS Security Assertion Markup Language (SAML) V2.0.

When the user is authenticated by the IdP, the IdP responds with a HTTP redirect to the registered endpoint of the primary system as specified in Bindings for the OASIS Security Assertion Markup Language (SAML) V2.0.

Message Semantics

Request Message

The following snippet is taken from a sample request recorded during the EPR projectathon in September 2020. Some elements were ommitted to increase readability. The raw request file may be found here.

The AuthnRequest conveys the following information to be set by the primary system:

  • ID: A unique ID of the request message (line 7 in the example below).
  • Issuer: A ID of the primary system as URL (line 9 in the example below).
  • SignedInfo: Signature metadata and the digest value used for the signature.
  • SignatureValue: The signature of the request (line 23 in the example below).
  • X509Certificate: The X509 certificate used to sign the request (line 26 in the example below).
04_AuthnRequest.xml
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
<AuthnRequest
+ xmlns="urn:oasis:names:tc:SAML:2.0:protocol"
+ AssertionConsumerServiceURL="https://epdtest.mycompany.local:8549/ACS"
+ IssueInstant="2020-09-24T13:19:25.208+02:00"
+ Destination="https://fed.idp.ch:443/saml/3.0/idp/"
+ ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"
+ ID="SAML-CD88202A-FE57-11EA-800A-ACB5C93CFFF0"
+ Version="2.0">
+ <Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">https://epdtest.mycompany.local</Issuer>
+ <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
+  <SignedInfo>
+   <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+   <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
+   <Reference URI="#SAML-CD88202A-FE57-11EA-800A-ACB5C93CFFF0">
+    <Transforms>
+     <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
+     <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+    </Transforms>
+    <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
+    <DigestValue>TdN8cZ5mvY7pOsrpOK0h+YlnvlhOOHYecaBN59yH4w0=</DigestValue>
+   </Reference>
+  </SignedInfo>
+  <SignatureValue><!-- omitted for brevity --></SignatureValue>
+  <KeyInfo>
+   <X509Data>
+    <X509Certificate><!-- omitted for brevity --></X509Certificate>
+   </X509Data>
+  </KeyInfo>
+ </Signature>
+ <NameIDPolicy Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" AllowCreate="true"/>
+</AuthnRequest>
+
Response Message

The following snippet is taken from a sample request recorded during the EPR projectathon in September 2020. It conveys two parameter to be used by the primary system:

  • SAMLart: The SAML artifact to be used in the ArtifactResolve request (see section below).
  • RelayState: A unique identifier of the conversation, the primary system initially sent with the Authentication Request.
https://epdtest.mycompany.local:8549/ACS?SAMLart=AAQAAOjXNPPr%2Fr7FO5WpiZ%2B2vAl5KMFibkRaAGwIkwXh%2Bo7DgsG2LMDE58c%3D&RelayState=idp%23468
+

Transport Protocol

The transaction uses front channel HTTP communication via the user agent (browser).

Security Requirements

The transactions shall use TLS secured transports (HTTPS) to ensure data privacy and with server authentication.

Artifact Resolve

The transaction shall be performed by the primary system to exchange the artifact to a SAML 2.0 IdP Assertion.

The primary system shall use the SOAP backchannel with an ArtifactResolve request message as defined in Assertions and Protocols for the OASIS Security Assertion Markup Language (SAML) V2.0.

The IdP server responds the SAML 2.0 IdP Assertion of the authenticated user.

Message Semantics

Request Message

The following snippet is taken from a sample request recorded during the EPR projectathon in September 2020. Some elements are omitted to increase readability. The raw request file may be found here.

The ArtifactResolve conveys the following information to be set by the primary system:

  • Issuer: A ID of the primary system as URL (line 4 in the example below).
  • SignedInfo: Signature metadata and the digest value used for the signature.
  • SignatureValue: The signature of the request (line 18 in the example below).
  • X509Certificate: The X509 certificate used to sign the request (line 21 in the example below).
  • Artifact: The artifact as retrieved from the Authentication Request transaction (line 25).
08_ArtifactResolve_raw.xml
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
+  <Body>
+   <ArtifactResolve xmlns="urn:oasis:names:tc:SAML:2.0:protocol" ID="SAML-D76F77F0-FE57-11EA-8007-9DB4CDFD82EF" Version="2.0" IssueInstant="2020-09-24T13:19:41.822+02:00" Destination="https://fed.idp.ch/nevisauth/services/artifactresolution">
+    <Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">https://epdtest.mycompany.local</Issuer>
+    <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
+     <SignedInfo>
+      <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+      <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
+      <Reference URI="#SAML-D76F77F0-FE57-11EA-8007-9DB4CDFD82EF">
+       <Transforms>
+        <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
+        <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+       </Transforms>
+       <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
+       <DigestValue>R+bMs3semvJBgae/3wnNVZp0eTpIhQQK4+S2KqRj1bM=</DigestValue>
+      </Reference>
+     </SignedInfo>s
+     <SignatureValue> <!-- omitted for brevity --> </SignatureValue>
+     <KeyInfo>
+      <X509Data>
+       <X509Certificate> <!-- omitted for brevity --> </X509Certificate>
+      </X509Data>
+     </KeyInfo>
+    </Signature>
+    <Artifact>AAQAAOjXNPPr/r7FO5WpiZ+2vAl5KMFibkRaAGwIkwXh+o7DgsG2LMDE58c=</Artifact>
+   </ArtifactResolve>
+  </Body>
+ </Envelope>
+
Response Message

The following snippet is taken from a sample response recorded during the EPR projectathon in September 2020. Some elements are omitted to increase readability. The raw version may be found here.

The ArtifactResponse conveys the following information which shall be evaluated by the primary system:

  • Issuer: A ID of the primary system as URL (line 6 in the example below).
  • SignedInfo: Signature metadata and the digest value used for the signature.
  • SignatureValue: The signature of the request (line 26 in the example below) which shall be validated by the primary system.
  • X509Certificate: The X509 certificate used to sign the request (line 29 in the example below).
  • assertion: The IdP assertion conveying the attributes of the authenticated user.
09_ArtifactResponse_raw.xml
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
+  <SOAP-ENV:Body>
+    <saml2p:ArtifactResponse
+      xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"
+      xmlns:xs="http://www.w3.org/2001/XMLSchema"
+      ID="ArtifactResponse_bf738b81c935d2c2f47604e01388553c2407ac40"
+      InResponseTo="SAML-D76F77F0-FE57-11EA-8007-9DB4CDFD82EF"
+      IssueInstant="2020-09-24T11:19:42.030Z"
+      Version="2.0">
+      <saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">fed.idp.ch</saml2:Issuer>
+      <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+        <ds:SignedInfo>
+          <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+          <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
+          <ds:Reference URI="#ArtifactResponse_bf738b81c935d2c2f47604e01388553c2407ac40">
+            <ds:Transforms>
+              <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
+              <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
+                <ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="xs"/>
+              </ds:Transform>
+            </ds:Transforms>
+            <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+            <ds:DigestValue>1RZPbh508clAQoKhxnQZEenNFyg=</ds:DigestValue>
+          </ds:Reference>
+        </ds:SignedInfo>
+        <ds:SignatureValue><!-- omitted for brevity --></ds:SignatureValue>
+        <ds:KeyInfo>
+          <ds:X509Data>
+            <ds:X509SKI>gYMVdgdR5LG/983GRTJIch0a+zU=</ds:X509SKI>
+          </ds:X509Data>
+        </ds:KeyInfo>
+      </ds:Signature>
+      <saml2p:Status>
+        <saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
+      </saml2p:Status>
+      <saml2p:Response
+        xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"
+        Destination="https://epdtest.mycompany.com"
+        ID="Response_ef5597568f1bc48233cac63c5a51ca3026566d59"
+        InResponseTo="SAML-CD88202A-FE57-11EA-800A-ACB5C93CFFF0"
+        IssueInstant="2020-09-24T11:19:41.634Z"
+        Version="2.0">
+        <saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">fed.idp.ch</saml2:Issuer>
+        <saml2p:Status>
+          <saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
+        </saml2p:Status>
+        <saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xs="http://www.w3.org/2001/XMLSchema" ID="Assertion_4f962f0ff6d14c9ea77726da3c2c88bb76fcae67" IssueInstant="2020-09-24T11:19:41.633Z" Version="2.0">
+

The following snippet shows an example of a IdP Assertion conveyed with the response.

The primary system must keep the IdP Assertion in memory to use it to authenticate the Get X-User Assertion transaction.

The primary system is not required to analyze the IdP Assertion further, but may extract the following information from the assertion:

  • NameID : This element conveys the user ID assigned by IdP. Primary systems may use it to locally authenticate the user in the primary system.
  • NotBefore and NotOnOrAfter: The lifetime of the IdP assertion.
  • AttributeStatement: IdP shall provide user attributes in the AttributeStatement child elements. At least the givenname, surname, gender and dateofbirth must be provided. Optional fileds may be provided. The optional fields cover the GLN of the healthcare professional and other attributes, the primary system may use internally to identify the authenticated user.
        <saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xs="http://www.w3.org/2001/XMLSchema" ID="Assertion_4f962f0ff6d14c9ea77726da3c2c88bb76fcae67" IssueInstant="2020-09-24T11:19:41.633Z" Version="2.0">
+          <saml2:Issuer>fed.idp.ch</saml2:Issuer>
+          <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+            <ds:SignedInfo>
+              <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+              <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
+              <ds:Reference URI="#Assertion_4f962f0ff6d14c9ea77726da3c2c88bb76fcae67">
+                <ds:Transforms>
+                  <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
+                  <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
+                    <ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="xs"/>
+                  </ds:Transform>
+                </ds:Transforms>
+                <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
+                <ds:DigestValue>P/I9Ym+p/Zzs0ANXHmAjqZcuBp2FJ75j6oCM6Gd0bVg=</ds:DigestValue>
+              </ds:Reference>
+            </ds:SignedInfo>
+            <ds:SignatureValue><!-- omitted for brevity --></ds:SignatureValue>
+            <ds:KeyInfo>
+              <ds:X509Data>
+                <ds:X509Certificate><!-- omitted for brevity --></ds:X509Certificate>
+              </ds:X509Data>
+            </ds:KeyInfo>
+          </ds:Signature>
+          <saml2:Subject>
+            <saml2:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">pjtt31</saml2:NameID>
+            <saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
+              <saml2:SubjectConfirmationData InResponseTo="SAML-CD88202A-FE57-11EA-800A-ACB5C93CFFF0" NotOnOrAfter="2020-09-24T12:19:41.634Z" Recipient="https://epdtest.mycompany.com"/>
+            </saml2:SubjectConfirmation>
+          </saml2:Subject>
+          <saml2:Conditions NotBefore="2020-09-24T11:19:41.633Z" NotOnOrAfter="2020-09-24T12:09:41.633Z">
+            <saml2:AudienceRestriction>
+              <saml2:Audience>https://epdtest.mycompany.com</saml2:Audience>
+            </saml2:AudienceRestriction>
+          </saml2:Conditions>
+          <saml2:AuthnStatement AuthnInstant="2020-09-24T11:19:41.633Z" SessionNotOnOrAfter="2020-09-24T13:19:41.633Z">
+            <saml2:AuthnContext>
+              <saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml2:AuthnContextClassRef>
+            </saml2:AuthnContext>
+          </saml2:AuthnStatement>
+          <saml2:AttributeStatement>
+            <!-- other vendor specific attributes omitted for brevity -->
+            <saml2:Attribute Name="GLN" NameFormat="urn:oasis:names:tc:ebcore:partyid-type:DataUniversalNumberingSystem:0060">
+             <saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:token">9801000050702</saml2:AttributeValue>
+            </saml2:Attribute>
+            <saml2:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
+              <saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">Martina</saml2:AttributeValue>
+            </saml2:Attribute>
+            <saml2:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
+              <saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">Musterarzt</saml2:AttributeValue>
+            </saml2:Attribute>
+            <saml2:Attribute Name="gender" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
+              <saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:token">F</saml2:AttributeValue>
+            </saml2:Attribute>
+            <saml2:Attribute Name="dateofbirth" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
+              <saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:date">1990-09-06</saml2:AttributeValue>
+            </saml2:Attribute>
+          </saml2:AttributeStatement>
+        </saml2:Assertion>
+

Transport Protocol

The primary system shall send the request messages to the IdP of the community using the http POST binding as defined in the W3C SOAP specification. It may look like:

1
+2
+3
+4
+5
+6
POST /IdPAuthenticationService HTTP/1.1
+Host: idp.example.org
+Accept-Encoding: gzip, deflate
+Connection: Keep-Alive
+Content-Type: application/soap+xml; charset="utf-8"
+Content-Length: nnnn  
+

Security Requirements

The Artifact Resolve transaction shall be secured by using the SOAP backchannel with TLS and mutual authentication with client and server certificate validation. The certificates shall be exchanged during the client registration process.

Audit Log

Primary systems shall protocol the transaction in their logs to ensure traceability. No further requirements are defined in the ordinances of the Swiss EPR.

Test Opportunity

The transaction can be tested with the test suite of the EPR reference environment, test systems of the EPR communities or the EPR Playground.


Last update: 2025-01-27
\ No newline at end of file diff --git a/GetXAssertion/index.html b/GetXAssertion/index.html new file mode 100644 index 0000000..985fdd7 --- /dev/null +++ b/GetXAssertion/index.html @@ -0,0 +1,128 @@ + Get X-User Assertion - EPR by example
Skip to content

Get X-User Assertion

Transaction to retrieve a SAML 2.0 assertion for authorization of transactions in the Swiss EPR. Primary systems shall use this transaction to retrieve a SAML 2 assertions to be used with EPR transactions, which require authorization.

Overview

Primary systems shall use this transaction to retrieve a SAML 2 assertions to be used with the Provide X-User Assertion with XDS.b transactions as defined in the IHE XUA profile with Swiss specific extensions defined in
Amendment 1 to Annex 5.

The primary system shall provide claims (e.g., user role, purpose of use) with the request as defined in Amendment 1 to Annex 5.

The community verifies the claims and responds with a XUA compliant SAML 2.0 Assertion defined in Amendment 1 to Annex 5.

Transaction

Message Semantics

Messages are encoded as described in the WS Trust standard, with restrictions defined in the IHE profile and the ordinances to the Swiss EPR.

Request Message

The following snippet is taken from a sample request recorded during the EPR projectathon in September 2020. Some elements are omitted to increase readability. The raw request file may be found here.

The snippet shows a request performed by a healthcare professional performing a the normal access. For other roles and situations the claims are different. Other examples may be found at XUA examples.

The request message shall be a XML SOAP envelope with the query embedded in the Body element of the SOAP envelope. The SOAP Header element conveys the following information:

  • MessageID element: a UUID of the message.
  • Action element: The SOAP action identifier of the query as defined in the IHE ITI Technical Framework.
  • Security element: The Web Service Security header as defined in the WS Security specification. This element must contain the IdP Assertion taken from user authentication (see Authenticate User).
SOAP header
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
+ <env:Header>
+  <wsa:Action xmlns:wsa="http://www.w3.org/2005/08/addressing">http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue</wsa:Action>
+  <wsa:MessageID xmlns:wsa="http://www.w3.org/2005/08/addressing">urn:uuid:005300f3-c686-4960-8ae8-f8c1720eda41</wsa:MessageID>
+  <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
+   <saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xs="http://www.w3.org/2001/XMLSchema" ID="Assertion_dbce1232740fcad9e020f927fd25a5d04779b4cc" IssueInstant="2020-09-21T13:38:43.857Z" Version="2.0">
+   <!-- IdP Assertion omitted -->
+   </saml2:Assertion>
+  </wsse:Security>
+ </env:Header>
+

The SOAP Body element contains the RequestSecurityToken element with the following claims to be set by the primary system. It's child element read:

AppliesTo the URL of the community endpoint the XUA assertion shall be used for authorization, whose value is set in the Address child element.

56
+57
+58
+59
+60
   <wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
+    <wsa:EndpointReference xmlns:wsa="http://www.w3.org/2005/08/addressing">
+     <wsa:Address>https://sp.communilty.ch</wsa:Address>
+    </wsa:EndpointReference>
+   </wsp:AppliesTo>
+

resourceID conveying the EPR-SPID of the patient EPR to access in CX format (see see PIXFeed)

62
+63
+64
    <saml2:Attribute xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" Name="urn:oasis:names:tc:xacml:2.0:resource:resource-id">
+     <saml2:AttributeValue xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:token">761337610411353650^^^&amp;2.16.756.5.30.1.127.3.10.3&amp;ISO</saml2:AttributeValue>
+    </saml2:Attribute>
+

purposeOfUse conveying the purpose of use of the request, which must be taken from the EPR value set defined in Annex 3 of the ordinances of the Swiss electronic patient dossier.

65
+66
+67
+68
+69
    <saml2:Attribute xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" Name="urn:oasis:names:tc:xspa:1.0:subject:purposeofuse">
+     <saml2:AttributeValue xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:anyType">
+      <PurposeOfUse xmlns="urn:hl7-org:v3" code="NORM" codeSystem="2.16.756.5.30.1.127.3.10.5" codeSystemName="eHealth Suisse Verwendungszweck" displayName="Normalzugriff" xsi:type="CE"/>
+     </saml2:AttributeValue>
+    </saml2:Attribute>
+

role conveying the EPR role of the user, which must be taken from the EPR value set defined in Annex 3.

70
+71
+72
+73
+74
    <saml2:Attribute xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" Name="urn:oasis:names:tc:xacml:2.0:subject:role">
+     <saml2:AttributeValue xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:anyType">
+      <Role xmlns="urn:hl7-org:v3" code="HCP" codeSystem="2.16.756.5.30.1.127.3.10.6" codeSystemName="eHealth Suisse EPR Akteure" displayName="Behandelnde(r)" xsi:type="CE"/>
+     </saml2:AttributeValue>
+    </saml2:Attribute>
+

Response Message

The following snippet is taken from a sample response recorded during the EPR projectathon in September 2020. Some elements were ommitted to increase readability. The raw file may be found here.

The response message is a XML SOAP envelope with the XUA Assertion embedded in the the Body element of the SOAP envelope (see example below, lines 21 to 23). Primary systems shall extract the XUA Assertion to use the im the security header of the XDS.b transactions, which require authorization. For primary systems, there is no need to extract information from the XUA assertion.

The XUA Assertion is omitted in the snippet below. For examples of see here.

GetXAssertion_response.xml
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
<?xml version='1.0' encoding='utf-8'?>
+<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
+ <soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">
+  <wsa:Action>http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTRC/IssueFinal</wsa:Action>
+  <wsa:RelatesTo>urn:uuid:005300f3-c686-4960-8ae8-f8c1720eda41</wsa:RelatesTo>
+ </soapenv:Header>
+ <soapenv:Body>
+  <wst:RequestSecurityTokenResponseCollection xmlns:wst="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
+   <wst:RequestSecurityTokenResponse>
+    <wst:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0</wst:TokenType>
+    <wst:Lifetime>
+     <wsu:Created xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2020-09-21T13:39:23.200Z</wsu:Created>
+     <wsu:Expires xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2020-09-21T13:54:23.200Z</wsu:Expires>
+    </wst:Lifetime>
+    <wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
+     <wsa:EndpointReference xmlns:wsa="http://www.w3.org/2005/08/addressing">
+      <wsa:Address>https://sp.communilty.ch</wsa:Address>
+     </wsa:EndpointReference>
+    </wsp:AppliesTo>
+    <wst:RequestedSecurityToken>
+     <saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xsd="http://www.w3.org/2001/XMLSchema" ID="_96189571-c72c-4a10-8f1c-6d5b27efa797" IssueInstant="2020-09-21T13:39:23.200Z" Version="2.0">
+      <!-- assertion omitted for brevity -->
+     </saml2:Assertion>
+    </wst:RequestedSecurityToken>
+    <wst:RequestedAttachedReference>
+     <wsse:SecurityTokenReference xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
+      <wsse:Reference URI="_96189571-c72c-4a10-8f1c-6d5b27efa797" ValueType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0"/>
+     </wsse:SecurityTokenReference>
+    </wst:RequestedAttachedReference>
+   </wst:RequestSecurityTokenResponse>
+  </wst:RequestSecurityTokenResponseCollection>
+ </soapenv:Body>
+</soapenv:Envelope>
+

Transport Protocol

The primary system shall send the request messages to the X-Assertion Provider of the community using the http POST binding as defined in the W3C SOAP specification. It may look like:

1
+2
+3
+4
+5
+6
POST /RegistryStoredQueryService HTTP/1.1
+Host: company.example.org
+Accept-Encoding: gzip, deflate
+Connection: Keep-Alive
+Content-Type: application/soap+xml; charset="utf-8"
+Content-Length: nnnn  
+

Audit Log

Primary systems shall protocol the request and response for traceability. There are no further requirements on protocols defined in the ordinances of the Swiss EPR.

Security Requirements

To ensure privacy the transaction must be secured using https with mutual authentication, using X.509 certificates (extended validation required) and client and server side certificate validation.

The retrieval of a XUA assertion requires user authentication by providing the IdP assertion in the Security header of the SOAP envelope (see Message Semantics). The IdP assertion shall be retrieved by the primary system by using the Authenticate User transaction.

Test Opportunity

The transaction can be tested with the test suite of the EPR reference environment or the test systems of the EPR communities.


Last update: 2025-01-27
\ No newline at end of file diff --git a/IdPRenew/index.html b/IdPRenew/index.html new file mode 100644 index 0000000..8c5eb8b --- /dev/null +++ b/IdPRenew/index.html @@ -0,0 +1,183 @@ + IdP Renew - EPR by example
Skip to content

IdP Renew

Transaction to renew a IdP Assertion. Primary systems may use this transaction to retrieve a new IdP Assertion, without requiring the user to enter her credentials.

Overview

Primary systems shall use this transaction retrieve a new IdP Assertion by sending an assertion retrieved beforehand.

Transaction

Message Semantics

Messages are encoded as described in Assertions and Protocols for the OASIS Security Assertion Markup Language (SAML) V2.0 and the ordinances to the Swiss EPR.

Request Message

Primary systems shall use this transaction to renew a assertion whose lifetime is exceeded.

The following snippet is adapted from a sample request recorded during the EPR projectathon in September 2020. Some elements and namespaces were omitted to increase readability. The raw request file may be found here.

The Header element of the SOAP envelope contains the data used to sign the request message, as required from the ordinances of the Swiss EPR in Annex 8 and specified in the Web Service Security: SOAP Message Security 1.1. specification.

SOAP header
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
<Envelope>
+ <Header>
+  <Security mustUnderstand="1">
+   <Timestamp Id="TS-15277e04-85e0-4b9c-9692-76c3e7be17bc">
+    <Created>2019-03-26T15:13:15.144Z</Created>
+    <Expires>2019-03-26T15:18:15.144Z</Expires>
+   </Timestamp>
+   <BinarySecurityToken
+    EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"
+    ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"
+    Id="X509-6dfe58da-6804-48ba-ad52-e871c63455df">
+    MIIDPTCCAiWgAwIBAgIEPVbC1zANBgkqhkiG9w0BAQUFADBPMQswCQYDVQQGEqSqEb/3VB3ITUav3DIo2o2mRCKyfHV471QUNt4qNFmEwRxpsoGst/UYoTqW8/buv4A=
+   </BinarySecurityToken>
+   <Signature Id="SIG-98f468bf-4022-4e31-9886-6f30f1c676bc">
+    <SignedInfo>
+     <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
+      <InclusiveNamespaces PrefixList="soap"/>
+     </CanonicalizationMethod>
+     <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256" />
+     <Reference URI="#TS-15277e04-85e0-4b9c-9692-76c3e7be17bc" >
+      <Transforms>
+       <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" >
+        <InclusiveNamespaces PrefixList="soap wsse"/>
+       </Transform>
+      </Transforms>
+      <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
+      <DigestValue >LCxW9EORpApnpju2Q17b0MB1LGt8CMCuvoOqCtlhFx0=</DigestValue>
+     </Reference>
+     <Reference URI="#_33c9f0c5-c7d2-4d53-ad2f-944320637754" >
+      <Transforms>
+       <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
+      </Transforms>
+      <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
+      <DigestValue >TwKUz3SxOx1NaFVvy55AbbpWXbUJmfn+mreDpkNa/pg=</ds:DigestValue>
+     </Reference>
+    </SignedInfo>
+    <SignatureValue>
+     <!-- signature value omitted -->
+    </SignatureValue>
+    <KeyInfo Id="KI-2c6438fa-738a-4ffb-aa52-379bd9380b1a" >
+     <SecurityTokenReference Id="STR-76ccd654-581d-446e-a00b-8ed529bcc4ab">
+      <X509Data>
+       <X509IssuerSerial>
+        <X509IssuerName >CN=lk,OU=lk,O=lk,L=lsn,ST=vd,C=ch</X509IssuerName>
+        <X509SerialNumber >1029096151</X509SerialNumber>
+       </X509IssuerSerial>
+      </X509Data>
+     </SecurityTokenReference>
+    </KeyInfo>
+   </Signature>
+  </Security>
+ </Header>
+

Primary systems shall embed the SAML Assertion in the the Body of the SOAP envelope (see lines 58 .. 60 in example below).

SOAP body
53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
 <Body Id="_33c9f0c5-c7d2-4d53-ad2f-944320637754">
+  <RequestSecurityToken>
+   <RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Renew</RequestType>
+   <TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0</TokenType>
+   <RenewTarget>
+    <Assertion ID="ID_6ff2590e-8df8-4f04-9ae0-cfd16d816c49" IssueInstant="2019-03-26T15:12:13.246Z" Version="2.0">
+     <!-- assertion omitted -->
+    </Assertion>
+   </RenewTarget>
+   <Renewing/>
+  </RequestSecurityToken>
+ </Body>
+</Envelope>
+

Response Message

The community responds with a SAML 2.0 IdP assertion whose lifetime is updated.

The following snippet is adapted from a sample request recorded during the EPR projectathon in September 2020. Some elements and namespaces were ommitted to increase readability. The raw request file may be found here.

A SAML 2.0 IdP Assertion with identical attributes but updated lifetime is conveyed in the the Body of the SOAP envelope (see lines 67 .. 69). Examples of Swiss EPR compliant XUA assertions may be found here.

Response
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
<Envelope>
+ <Header/>
+ <Body>
+  <RequestSecurityTokenResponse Context="">
+   <TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0</TokenType>
+   <Lifetime>
+    <wsu:Created>2019-03-26T15:13:13.378Z</wsu:Created>
+    <wsu:Expires>2019-03-26T15:18:13.378Z</wsu:Expires>
+   </Lifetime>
+   <RequestedSecurityToken>
+    <Assertion>
+     <!-- Assertion returned by the IdP ommitted for brevity -->
+    </Assertion>
+   </RequestedSecurityToken>
+   <RequestedAttachedReference>
+    <SecurityTokenReference TokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0">
+     <KeyIdentifier ValueType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLID">#ID_cf6f7da6-c670-4c35-b413-39182b5672f2</KeyIdentifier>
+    </SecurityTokenReference>
+   </RequestedAttachedReference>
+  </RequestSecurityTokenResponse>
+ </Body>
+</Envelope>
+

Transport Protocol

The primary system shall send the request messages to the registry of the community using the http POST binding as defined in the W3C SOAP specification. It may look like:

1
+2
+3
+4
+5
+6
POST /RegistryStoredQueryService HTTP/1.1
+Host: company.example.org
+Accept-Encoding: gzip, deflate
+Connection: Keep-Alive
+Content-Type: application/soap+xml; charset="utf-8"
+Content-Length: nnnn  
+

Audit Log

Primary systems shall protocol the request and response for traceability. There are no further requirements on protocols defined in the ordinances of the Swiss EPR.

Security Requirements

To ensure privacy the transaction must be secured using the TLS SOAP backchannel with mutual authentication by server and client certificate validation.

Test Opportunity

The transaction can be tested with the Gazelle test suite of the EPR reference environment, or test systems of the EPR communities.


Last update: 2025-01-27
\ No newline at end of file diff --git a/ImmunizationAdministrationDocument/index.html b/ImmunizationAdministrationDocument/index.html new file mode 100644 index 0000000..c5a1edd --- /dev/null +++ b/ImmunizationAdministrationDocument/index.html @@ -0,0 +1,383 @@ + Immunization Administration Document - EPR by example
Skip to content

Immunization Administration Document

In a primary system, different vaccinations are documented in a treatment context. Primary systems can use this Exchange Format to generate a Immunization Administration document.

Bundle

Profile information

In the resource "Bundle", all resources are collected in a container.

  • id: Local id of the resource (line 3)
  • meta: Shows the metadata of the document (line 4 to 8).
  • identifier: Unique identifier for the bundle - fixed Value: "urn:ietf:rfc:3986" (line 9 to 12).
  • type: Indicates the purpose of this bundle - fixed Value: "document"; binding: BundleType (line 13).
  • timestamp: The date/time the bundle was composed (line 14).
  • entry: The list to add all resources belonging to the document (line 15-17).
{
+  "resourceType" : "Bundle",
+  "id" : "A-D2-HCP1-C1",
+  "meta" : {
+    "profile" : [
+      "http://fhir.ch/ig/ch-vacd/StructureDefinition/ch-vacd-document-immunization-administration"
+    ]
+  },
+  "identifier" : {
+    "system" : "urn:ietf:rfc:3986",
+    "value" : "urn:uuid:bef441e1-58b1-48e3-aa51-61237a3c20cd"
+  },
+  "type" : "document",
+  "timestamp" : "2021-06-15T00:00:00.390+02:00",
+  "entry" : [
+    ....
+  ]
+}
+

Composition

Profile information

In the resource "Composition", general information about the document is specified. It should be the first entry in the list.

  • entry: An entry in a bundle resource - will either contain a resource or information about a resource (line 1)
  • id: Local id of the resource (line 6).
  • meta: Shows the metadata of the composition (line 7 to 11).
  • language: Specifies the language of the document - binding: CommonLanguages (line 12).
  • text: Presents the narrative text of the resource (line 13 to 16).
  • extension: Extension to version number (line 17 to 23).
  • identifier: A version-independent identifier for the Composition with a fixed system value "urn:ietf:rfc:3986" (line 24 to 27).
  • status: The status of the document - fixed Value: "final"; binding: CompositionStatus (line 28).
  • type: Specifies the particular kind of composition. Fixed coding value (line 29 to 37).
"entry" : [
+    {
+      "fullUrl" : "http://test.fhir.ch/r4/Composition/A-D2-HCP1-C1-Composition",
+      "resource" : {
+        "resourceType" : "Composition",
+        "id" : "A-D2-HCP1-C1-Composition",
+        "meta" : {
+          "profile" : [
+            "http://fhir.ch/ig/ch-vacd/StructureDefinition/ch-vacd-composition-immunization-administration"
+          ]
+        },
+        "language" : "en-US",
+        "text" : {
+          "status" : "generated",
+          "div" : "<div xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en-US\" lang=\"en-US\"><h3>Immunization Administration</h3><p><b>Id: </b>A-D2-HCP1-C1-Composition</p><p><b>Identifier: </b><span>urn:ietf:rfc:3986#urn:uuid:ab0add6e-0049-4567-8609-8d3ffdd84af0</span></p><p><b>Status: </b>Final</p><p><b>Code: </b><span>Immunization record (http://snomed.info/sct#41000179103)</span></p><p><b>Patient: </b><a href=\"Patient-TC-patient.html\">Patient/TC-patient</a> Wegmueller Monika</p><p><b>Date: </b>June 15, 2021</p><p><b>Authors:</b></p><table><tr><td><p><a href=\"Practitioner-TC-HCP1-C1.html\">Practitioner/TC-HCP1-C1</a> Bereit Allzeit</p><p><a href=\"Organization-TC-ORG1.html\">Organization/TC-ORG1</a> Gruppenpraxis CH</p></td></tr></table><p><b>Confidentiality: </b>null<span> Normal (qualifier value) (http://snomed.info/sct#17621005)</span></p><p><b>Sections:</b></p><table><tr><td>Immunization Administration</td></tr></table></div>"
+        },
+        "extension" : [
+          {
+            "id" : "versionNumber",
+            "url" : "http://fhir.ch/ig/ch-core/StructureDefinition/ch-ext-epr-versionnumber",
+            "valueUnsignedInt" : 1
+          }
+        ],
+        "identifier" : {
+         "system" : "urn:ietf:rfc:3986",
+         "value" : "urn:uuid:ab0add6e-0049-4567-8609-8d3ffdd84af0"
+        },
+        "status" : "final",
+        "type" : {
+         "coding" : [
+           {
+             "system" : "http://snomed.info/sct",
+             "code" : "41000179103",
+             "display" : "Immunization record"
+           }
+         ]
+       },
+
  • subject: Who the composition is about. Its a reference to the resource contained as entry in the bundle (line 38 to 40).
  • date: The date of the composition creation (line 41).
  • author: Identifies who is responsible for the information in the composition (line 42 to 46).
  • title: The title of the document. Fixed Value. (line 47).
       "subject" : {
+          "reference" : "Patient/TC-patient"
+        },
+        "date" : "2021-06-15T00:00:00+02:00",
+        "author" : [
+          {
+            "reference" : "PractitionerRole/TC-HCP1-ORG1-ROLE-author"
+          }
+        ],
+       "title" : "Immunization Administration",
+
       "confidentiality" : "N",
+        "_confidentiality" : {
+          "extension" : [
+            {
+              "url" : "http://fhir.ch/ig/ch-core/StructureDefinition/ch-ext-epr-confidentialitycode",
+               "valueCodeableConcept" : {
+                "coding" : [
+                  {
+                    "system" : "http://snomed.info/sct",
+                    "code" : "17621005",
+                    "display" : "Normal (qualifier value)"
+                  }
+                ]
+              }
+            }
+         ]
+       },
+       "custodian" : {
+         "reference" : "Organization/TC-ORG1"
+       },
+
  • section: The root of the sections that make up the composition. There can be multiple sections (in minimum one of these sections has to be declared and an entry has to be defined) - See Immunization Administration Sections:
       "section" : [
+          {
+            "id" : "administration",
+            "title" : "Immunization Administration",
+            "code" : {
+              "coding" : [
+                {
+                  "system" : "http://loinc.org",
+                  "code" : "11369-6",
+                  "display" : "Hx of Immunization"
+                }
+              ]
+            },
+            "text" : {
+              "status" : "generated",
+              "div" : "<div xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en-US\" lang=\"en-US\"><p><b>Code: </b><span>Hx of Immunization (http://loinc.org#11369-6)</span></p><p><b>Entries:</b></p><table><tr><td><a href=\"Immunization-TCA01-IMMUN2-HCP1-ORG1-ROLE.html\">Immunization/TCA01-IMMUN2-HCP1-ORG1-ROLE</a></td></tr></table></div>"
+            },
+            "entry" : [
+              {
+                "reference" : "Immunization/TCA01-IMMUN2-HCP1-ORG1-ROLE"
+              }
+            ]
+          }
+        ]
+

Information about the patient

In the "Patient" resource, the demographic and administrative data of a patient are specified.

Profile information

 {
+      "fullUrl" : "http://test.fhir.ch/r4/Patient/TC-patient",
+      "resource" : {
+        "resourceType" : "Patient",
+        "id" : "TC-patient",
+        "meta" : {
+          "profile" : [
+            "http://fhir.ch/ig/ch-core/StructureDefinition/ch-core-patient-epr"
+          ]
+        },
+        "text" : {
+          "status" : "generated",
+          "div" : "<div xmlns=\"http://www.w3.org/1999/xhtml\"><p><b>Generated Narrative</b></p><div style=\"display: inline-block; background-color: #d9e0e7; padding: 6px; margin: 4px; border: 1px solid #8da1b4; border-radius: 5px; line-height: 60%\"><p style=\"margin-bottom: 0px\">Resource \"TC-patient\" </p><p style=\"margin-bottom: 0px\">Profile: <a href=\"http://fhir.ch/ig/ch-core/StructureDefinition-ch-core-patient-epr.html\">CH Core Patient Profile EPR</a></p></div><p><b>identifier</b>: id: 123.71.332.115, id: 8077560000000000000000</p><p><b>name</b>: Monika Wegmueller </p><p><b>telecom</b>: ph: tel:+41.32.685.12.34(HOME)</p><p><b>gender</b>: female</p><p><b>birthDate</b>: 1967-02-10</p><p><b>address</b>: Leidensweg 10 Specimendorf 9876 CH </p></div>"
+        },
+        "identifier" : [
+          {
+           "system" : "urn:oid:2.16.756.5.31",
+           "value" : "123.71.332.115"
+         },
+         {
+           "system" : "urn:oid:2.16.756.5.30.1.123.100.1.1.1",
+           "value" : "8077560000000000000000"
+         }
+       ],
+       "name" : [
+         {
+            "family" : "Wegmueller",
+            "given" : [
+              "Monika"
+            ]
+          }
+        ],
+        "telecom" : [
+          {
+            "system" : "phone",
+            "value" : "tel:+41.32.685.12.34",
+            "use" : "home"
+          }
+        ],
+        "gender" : "female",
+        "birthDate" : "1967-02-10",
+        "address" : [
+          {
+            "type" : "both",
+            "line" : [
+              "Leidensweg 10"
+            ],
+            "city" : "Specimendorf",
+            "postalCode" : "9876",
+            "country" : "CH"
+          }
+        ]
+      }
+    },
+

Information about the practitioner

The resource Practitioner indicates which practitioner has given a vaccination.

Profile information

{
+      "fullUrl" : "http://test.fhir.ch/r4/Practitioner/TC-HCP1-C1",
+      "resource" : {
+        "resourceType" : "Practitioner",
+        "id" : "TC-HCP1-C1",
+        "meta" : {
+          "profile" : [
+            "http://fhir.ch/ig/ch-core/StructureDefinition/ch-core-practitioner-epr"
+          ]
+        },
+        "text" : {
+          "status" : "generated",
+          "div" : "<div xmlns=\"http://www.w3.org/1999/xhtml\"><p><b>Generated Narrative</b></p><div style=\"display: inline-block; background-color: #d9e0e7; padding: 6px; margin: 4px; border: 1px solid #8da1b4; border-radius: 5px; line-height: 60%\"><p style=\"margin-bottom: 0px\">Resource \"TC-HCP1-C1\" </p><p style=\"margin-bottom: 0px\">Profile: <a href=\"http://fhir.ch/ig/ch-core/StructureDefinition-ch-core-practitioner-epr.html\">CH Core Practitioner Profile EPR</a></p></div><p><b>identifier</b>: id: 7608888888888</p><p><b>active</b>: true</p><p><b>name</b>: Allzeit Bereit </p><p><b>telecom</b>: ph: tel:+41.32.234.55.66(WORK), fax: fax:+41.32.234.55.67(WORK), <a href=\"mailto:mailto:allzeit.bereit@gruppenpraxis.ch\">mailto:allzeit.bereit@gruppenpraxis.ch</a>, <a href=\"http://www.gruppenpraxis.ch\">http://www.gruppenpraxis.ch</a></p><p><b>address</b>: Doktorgasse 2 Musterhausen 8888 CH </p></div>"
+        },
+        "identifier" : [
+          {
+            "system" : "urn:oid:2.51.1.3",
+            "value" : "7608888888888"
+          }
+        ],
+        "active" : true,
+        "name" : [
+          {
+            "family" : "Bereit",
+            "given" : [
+              "Allzeit"
+            ],
+            "prefix" : [
+              "Dr. med."
+            ]
+          }
+        ],
+        "telecom" : [
+          {
+            "system" : "phone",
+            "value" : "tel:+41.32.234.55.66",
+            "use" : "work"
+          },
+          {
+            "system" : "fax",
+            "value" : "fax:+41.32.234.55.67",
+            "use" : "work"
+          },
+          {
+            "system" : "email",
+            "value" : "mailto:allzeit.bereit@gruppenpraxis.ch",
+            "use" : "work"
+          },
+          {
+            "system" : "url",
+            "value" : "http://www.gruppenpraxis.ch",
+            "use" : "work"
+          }
+        ],
+        "address" : [
+          {
+            "type" : "physical",
+            "line" : [
+              "Doktorgasse 2"
+            ],
+            "city" : "Musterhausen",
+            "postalCode" : "8888",
+            "country" : "CH"
+          }
+        ]
+      }
+    },
+

Information about the organization

The resource stores the information about the organization that creates the Immunization Administration Document

Profile information

{
+      "fullUrl" : "http://test.fhir.ch/r4/Organization/TC-ORG1",
+      "resource" : {
+        "resourceType" : "Organization",
+        "id" : "TC-ORG1",
+        "meta" : {
+          "profile" : [
+            "http://fhir.ch/ig/ch-core/StructureDefinition/ch-core-organization-epr"
+          ]
+        },
+        "text" : {
+          "status" : "generated",
+          "div" : "<div xmlns=\"http://www.w3.org/1999/xhtml\"><p><b>Generated Narrative</b></p><div style=\"display: inline-block; background-color: #d9e0e7; padding: 6px; margin: 4px; border: 1px solid #8da1b4; border-radius: 5px; line-height: 60%\"><p style=\"margin-bottom: 0px\">Resource \"TC-ORG1\" </p><p style=\"margin-bottom: 0px\">Profile: <a href=\"http://fhir.ch/ig/ch-core/StructureDefinition-ch-core-organization-epr.html\">CH Core Organization Profile EPR</a></p></div><p><b>identifier</b>: id: 7608888888888</p><p><b>name</b>: Gruppenpraxis CH</p><p><b>telecom</b>: ph: tel:+41.32.234.55.66(WORK), fax: fax:+41.32.234.55.67(WORK), <a href=\"mailto:mailto:bereit@gruppenpraxis.ch\">mailto:bereit@gruppenpraxis.ch</a>, <a href=\"http://www.gruppenpraxis.ch\">http://www.gruppenpraxis.ch</a></p><p><b>address</b>: Doktorgasse 2 Musterhausen ZH 8888 CH </p></div>"
+        },
+        "identifier" : [
+          {
+            "system" : "urn:oid:2.51.1.3",
+            "value" : "7608888888888"
+          }
+        ],
+        "name" : "Gruppenpraxis CH",
+        "telecom" : [
+          {
+            "system" : "phone",
+            "value" : "tel:+41.32.234.55.66",
+            "use" : "work"
+          },
+          {
+            "system" : "fax",
+            "value" : "fax:+41.32.234.55.67",
+            "use" : "work"
+          },
+          {
+            "system" : "email",
+            "value" : "mailto:bereit@gruppenpraxis.ch",
+            "use" : "work"
+          },
+          {
+            "system" : "url",
+            "value" : "http://www.gruppenpraxis.ch",
+            "use" : "work"
+          }
+        ],
+        "address" : [
+          {
+            "line" : [
+              "Doktorgasse 2"
+            ],
+            "city" : "Musterhausen",
+            "state" : "ZH",
+            "postalCode" : "8888",
+            "country" : "CH"
+          }
+        ]
+      }
+    },
+

Immunization

In the resource "Immunization" the data of the patientes vaccination is defined.

Profile information

 {
+   "fullUrl" : "http://test.fhir.ch/r4/Immunization/TCA01-IMMUN2-HCP1-ORG1-ROLE",
+   "resource" : {
+     "resourceType" : "Immunization",
+     "id" : "TCA01-IMMUN2-HCP1-ORG1-ROLE",
+     "meta" : {
+       "profile" : [
+         "http://fhir.ch/ig/ch-vacd/StructureDefinition/ch-vacd-immunization"
+       ]
+     },
+     "text" : {
+       "status" : "extensions",
+       "div" : "<div xmlns=\"http://www.w3.org/1999/xhtml\"><p><b>Generated Narrative</b></p><div style=\"display: inline-block; background-color: #d9e0e7; padding: 6px; margin: 4px; border: 1px solid #8da1b4; border-radius: 5px; line-height: 60%\"><p style=\"margin-bottom: 0px\">Resource \"TCA01-IMMUN2-HCP1-ORG1-ROLE\" </p><p style=\"margin-bottom: 0px\">Profile: <a href=\"StructureDefinition-ch-vacd-immunization.html\">CH VACD Immunization Profile</a></p></div><p><b>CH VACD Extension Immunization Recorder Reference</b>: <a href=\"#PractitionerRole_TC-HCP1-ORG1-ROLE-author\">See above (PractitionerRole/TC-HCP1-ORG1-ROLE-author)</a></p><p><b>identifier</b>: id: 11853642-8ff4-45ae-af98-44c58b3bf0b7</p><p><b>status</b>: completed</p><p><b>vaccineCode</b>: Boostrix <span style=\"background: LightGoldenRodYellow; margin: 4px; border: 1px solid khaki\"> (<a href=\"CodeSystem-ch-vacd-swissmedic-cs.html\">Swiss Medic Authorized Vaccines Codesystem</a>#637)</span></p><p><b>patient</b>: <a href=\"#Patient_TC-patient\">See above (Patient/TC-patient)</a></p><p><b>occurrence</b>: 2021-06-15</p><p><b>recorded</b>: 2021-06-15T00:00:00.39+02:00</p><p><b>lotNumber</b>: 14-34218</p><p><b>route</b>: Intramuscular use <span style=\"background: LightGoldenRodYellow; margin: 4px; border: 1px solid khaki\"> (standardterms.edqm.eu#20035000)</span></p><h3>Performers</h3><table class=\"grid\"><tr><td>-</td><td><b>Actor</b></td></tr><tr><td>*</td><td><a href=\"#PractitionerRole_TC-HCP1-ORG1-ROLE-performer\">See above (PractitionerRole/TC-HCP1-ORG1-ROLE-performer)</a></td></tr></table><h3>ProtocolApplieds</h3><table class=\"grid\"><tr><td>-</td><td><b>TargetDisease</b></td><td><b>DoseNumber[x]</b></td></tr><tr><td>*</td><td>Diphtheria caused by Corynebacterium diphtheriae (disorder) <span style=\"background: LightGoldenRodYellow; margin: 4px; border: 1px solid khaki\"> (<a href=\"https://browser.ihtsdotools.org/\">SNOMED CT</a>#397430003)</span>, Tetanus (disorder) <span style=\"background: LightGoldenRodYellow; margin: 4px; border: 1px solid khaki\"> (<a href=\"https://browser.ihtsdotools.org/\">SNOMED CT</a>#76902006)</span>, Pertussis (disorder) <span style=\"background: LightGoldenRodYellow; margin: 4px; border: 1px solid khaki\"> (<a href=\"https://browser.ihtsdotools.org/\">SNOMED CT</a>#27836007)</span></td><td>1</td></tr></table></div>"
+     },
+     "extension" : [
+       {
+         "url" : "http://fhir.ch/ig/ch-vacd/StructureDefinition/ch-vacd-ext-immunization-recorder-reference",
+         "valueReference" : {
+           "reference" : "PractitionerRole/TC-HCP1-ORG1-ROLE-author"
+         }
+       }
+     ],
+     "identifier" : [
+       {
+         "system" : "urn:oid:2.16.756.5.30.1.402.1.3.1.1.1",
+         "value" : "11853642-8ff4-45ae-af98-44c58b3bf0b7"
+       }
+     ],
+     "status" : "completed",
+     "vaccineCode" : {
+       "coding" : [
+         {
+           "system" : "http://fhir.ch/ig/ch-vacd/CodeSystem/ch-vacd-swissmedic-cs",
+           "code" : "637",
+           "display" : "Boostrix"
+         }
+       ]
+     },
+     "patient" : {
+       "reference" : "Patient/TC-patient"
+     },
+     "occurrenceDateTime" : "2021-06-15",
+     "recorded" : "2021-06-15T00:00:00.390+02:00",
+     "lotNumber" : "14-34218",
+     "route" : {
+       "coding" : [
+         {
+           "system" : "http://standardterms.edqm.eu",
+           "code" : "20035000",
+           "display" : "Intramuscular use"
+         }
+       ]
+     },
+     "performer" : [
+       {
+         "actor" : {
+           "reference" : "PractitionerRole/TC-HCP1-ORG1-ROLE-performer"
+         }
+       }
+     ],
+     "protocolApplied" : [
+       {
+         "targetDisease" : [
+           {
+             "coding" : [
+               {
+                 "system" : "http://snomed.info/sct",
+                 "code" : "397430003",
+                 "display" : "Diphtheria caused by Corynebacterium diphtheriae (disorder)"
+               }
+             ]
+           },
+           {
+             "coding" : [
+               {
+                 "system" : "http://snomed.info/sct",
+                 "code" : "76902006",
+                 "display" : "Tetanus (disorder)"
+               }
+             ]
+           },
+           {
+             "coding" : [
+               {
+                 "system" : "http://snomed.info/sct",
+                 "code" : "27836007",
+                 "display" : "Pertussis (disorder)"
+               }
+             ]
+           }
+         ],
+         "doseNumberPositiveInt" : 1
+       }
+     ]
+   }
+ }
+

Last update: 2025-01-27
\ No newline at end of file diff --git a/ImmunizationAdministrationSections/index.html b/ImmunizationAdministrationSections/index.html new file mode 100644 index 0000000..a663349 --- /dev/null +++ b/ImmunizationAdministrationSections/index.html @@ -0,0 +1,342 @@ + Immunization Administration Sections - EPR by example
Skip to content

Immunization Administration Sections

A Immunization Administration document contains a compositions with different possible sections inside. In minimum one section (optionality: C) next to the optional sections (optionality: O) has to be defined.

Possible Sections

The following sections can be defined:

Name id System Code Display Optionality
Immunization Administration administration http://loinc.org 11369-6 Hx of Immunization C
Medical Problems medicalproblems http://loinc.org 11450-4 Problem list Reported C
Past Illnesses pastillnesses http://loinc.org 11348-0 Hx of Past illness C
Allergies and Intolerences allergyintolerances http://loinc.org 48765-2 Allergies and adverse reactions Document C
Other Relevant Observatons otherrelevantobservations http://loinc.org 30954-2 Relevant diagnostic tests/laboratory data Narrative C
Laboratory-Serology laboratory-serology http://loinc.org 18727-8 Serology studies (set) C
Pregnancy pregnancy http://loinc.org 10162-6 Pregnancies Hx C
Annotation annotation http://loinc.org 48767-8 Annotation comment Imp O
Original representation originalRepresentation http://loinc.org 55108-5 Clinical presentation O

Immunization Administration

  • title: 'Liste der verabreichten Impfungen' in german or 'Liste Vaccin administré' in french or 'Lista Vaccinazione somministrata' in italian or 'List Immunization Administration' in english or titles in other languages are also allowed
  • reference:
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
  "section" : [
+    {
+      "id" : "administration",
+      "title" : "Immunization Administration",
+      "code" : {
+        "coding" : [
+          {
+            "system" : "http://loinc.org",
+            "code" : "11369-6",
+            "display" : "Hx of Immunization"
+          }
+       ],
+     },
+     "text": {},
+     "entry" : [
+       {
+         "reference" : "Immunization/<id>"
+       }
+     ]
+  }
+

Medical Problems

  • title: 'Liste der Medizinischen Problemen' in german or 'Liste Vaccin administré' in french or 'Lista Vaccinazione somministrata' in italian or 'List Immunization Administration' in english or titles in other languages are also allowed
  • reference:
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
  "section" : [
+    {
+      "id" : "medicalproblems",
+      "title" : "Liste der Medizinischen Problemen",
+      "code" : {
+        "coding" : [
+          {
+            "system" : "http://loinc.org",
+            "code" : "11450-4",
+            "display" : "Problem list Reported"
+          }
+       ],
+     },
+     "text": {},
+     "entry" : [
+       {
+         "reference" : "Condition/<id>"
+       }
+     ]
+  }
+

Past Illnesses

  • titel: 'Bisherige Krankheiten' in german or 'Maladies antérieures' in french or 'Malattie precedenti' in italian or 'Previous illnesses' in english or titles in other languages are also allowed
  • reference: A list of Condition resources declaring past illnesses the patient is recovered from - see CH VACD Past Illness Profile
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
  "section" : [
+    {
+      "id" : "pastillnesses",
+      "title" : "Bisherige Krankheiten",
+      "code" : {
+        "coding" : [
+          {
+            "system" : "http://loinc.org",
+            "code" : "11348-0",
+            "display" : "Hx of Past illness"
+          }
+       ],
+     },
+     "text": {},
+     "entry" : [
+       {
+         "reference" : "Condition/<id>"
+       }
+     ]
+  }
+

Allergies and Intolerences

  • title: 'Allergien' in german or 'Les allergies' in french or 'Allergie' in italian or 'Allergies' in english or titles in other languages are also allowed
  • reference: A list of AllergyIntolerance resources defining the allergies and intolerances - see CH VACD AllergyIntolerance Profile
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
  "section" : [
+    {
+      "id" : "allergyintolerances",
+      "title" : "Allergies",
+      "code" : {
+        "coding" : [
+          {
+            "system" : "http://loinc.org",
+            "code" : "48765-2",
+            "display" : "Allergies and adverse reactions Document"
+          }
+       ],
+     },
+     "text": {},
+     "entry" : [
+       {
+         "reference" : "AllergyIntolerance/<id>"
+       }
+     ]
+  }
+

Other Relevant Observatons

  • title: 'Weiter relevante Beobachtungen' in german or 'Autres observations pertinentes' in french or 'Altre osservazioni rilevanti' in italian or 'Other Relevant Observations' in english or titles in other languages are also allowed
  • reference: The Condition resource defining the gestational age - see CH VACD Other Relevant Observations
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
  "section" : [
+    {
+      "id" : "otherrelevantobservations",
+      "title" : "Altre osservazioni rilevant",
+      "code" : {
+        "coding" : [
+          {
+            "system" : "http://loinc.org",
+            "code" : "30954-2",
+            "display" : "Relevant diagnostic tests/laboratory data Narrative"
+          }
+       ],
+     },
+     "text": {},
+     "entry" : [
+       {
+         "reference" : "Condition/<id>"
+       }
+     ]
+  }
+

Laboratory-Serology

  • title: 'Laborbefund - Serologie' in german or 'Résultats de laboratoire - Sérologie' in french or 'Risultati di laboratorio - Sierologia' in italian or 'Laboratory findings - Serology' in english or titles in other languages are also allowed
  • reference: A list of Condition resources defining the lab results - see CH VACD Laboratory And Serology Profile
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
  "section" : [
+    {
+      "id" : "laboratory-serology",
+      "title" : "Résultats de laboratoire - Sérologie",
+      "code" : {
+        "coding" : [
+          {
+            "system" : "http://loinc.org",
+            "code" : "18727-8",
+            "display" : "Serology studies (set)"
+          }
+       ],
+     },
+     "text": {},
+     "entry" : [
+       {
+         "reference" : "Observation/<id>"
+       }
+     ]
+  }
+

Pregnancy

  • title: 'Schwangerschaft' in german or 'Grossesse' in french or 'Gravidanza' in italian or 'Pregnancy' in english or titles in other languages are also allowed
  • reference: the Condition resource for pregnancy - see CH VACD Pregnancy Profile
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
  "section" : [
+    {
+      "id" : "pregnancy",
+      "title" : "Gravidanza",
+      "code" : {
+        "coding" : [
+          {
+            "system" : "http://loinc.org",
+            "code" : "10162-6",
+            "display" : "Pregnancies Hx"
+          }
+       ],
+     },
+     "text": {},
+     "entry" : [
+       {
+         "reference" : "Condition/<id>"
+       }
+     ]
+  }
+

Annotation

  • title: 'Kommentar' in german or 'Commentaire' in french or 'Osservazione' in italian or 'Comment' in english or titles in other languages are also allowed
  • text: The annotation comment can be added here as narrative.
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
  "section" : [
+    {
+      "id" : "annotation",
+      "title" : "Commentaire",
+      "code" : {
+        "coding" : [
+          {
+            "system" : "http://loinc.org",
+            "code" : "48767-8",
+            "display" : "Annotation comment Imp"
+          }
+       ],
+     },
+     "text": {}
+  }
+

Original representation

  • title: Original representation
  • reference: Binary resource containing the original representation of the content as PDF.
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
  "section" : [
+    {
+      "id" : "originalRepresentation",
+      "title" : "Original representation",
+      "code" : {
+        "coding" : [
+          {
+            "system" : "http://loinc.org",
+            "code" : "55108-5",
+            "display" : "Clinical presentation"
+          }
+       ],
+     },
+     "text": {},
+     "entry" : [
+       {
+         "reference" : "Binary/<id>"
+       }
+     ]
+  }
+

Last update: 2025-01-27
\ No newline at end of file diff --git a/MedicationCardDocument/index.html b/MedicationCardDocument/index.html new file mode 100644 index 0000000..d42527e --- /dev/null +++ b/MedicationCardDocument/index.html @@ -0,0 +1,906 @@ + Medication Card document - EPR by example
Skip to content

Medication Card document

In a primary system, different medications are documented in a treatment context. Primary systems can use this Exchange Format to generate a Medication Card document with the help of the Content Creator.

Bundle

Profile information

In the resource "Bundle", all resources are collected in a container.

  • id: Local id of the resource (line 3)
  • meta: Shows the metadata of the document (line 4 to 9).
  • identifier: Unique identifier for the bundle - fixed Value: "urn:ietf:rfc:3986" (line 10 to 13).
  • type: Indicates the purpose of this bundle - fixed Value: "document"; binding: BundleType (line 14).
  • timestamp: The date/time the bundle was composed (line 15).
  • entry: The list to add all resources belonging to the document (line 16-18).
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
  {
+    "resourceType" : "Bundle",
+    "id" : "2-7-MedicationCard",
+    "meta" : {
+      "lastUpdated" : "2020-02-21T12:31:59.738+00:00",
+      "profile" : [
+        "http://fhir.ch/ig/ch-emed/StructureDefinition/ch-emed-document-medicationcard"
+     ]
+   },
+  "identifier" : {
+    "system" : "urn:ietf:rfc:3986",
+    "value" : "urn:uuid:6b6ed376-a7da-44cb-92d1-e75ce1ae73b0"
+  },
+  "type" : "document",
+  "timestamp" : "2012-02-04T14:05:00+01:00",
+  "entry" : [
+    {
+      "fullUrl" : "http://test.fhir.ch/r4/Composition/2-7-MedicationCard",
+

Composition

Profile information

In the resource "Composition", general information about the document is specified.

  • entry: An entry in a bundle resource - will either contain a resource or information about a resource (line 1)
  • id: Local id of the resource (line 6).
  • language: Specifies the language of the document - binding: CommonLanguages (line 7).
  • text: Presents the narrative text of the resource (line 8 to 11).
  • extension: Extension to Information Recipient (line 12 to 19).
  • identifier: A version-independent identifier for the Composition (line 20 to 23).
  • status: The status of the document - fixed Value: "final"; binding: CompositionStatus (line 24).
  • type: Specifies the particular kind of composition - binding: DocumentEntry.typeCode(line 25 to 38).
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
  "entry" : [
+    {
+      "fullUrl" : "http://test.fhir.ch/r4/Composition/2-7-MedicationCard",  
+      "resource" : {
+        "resourceType" : "Composition",
+        "id" : "2-7-MedicationCard",
+        "language" : "de-CH",
+        "text" : {
+          "status" : "extensions",
+         "div" : "<div xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"de-CH\" lang=\"de-CH\"><p><b>Generated Narrative</b></p><div style=\"display: inline-block; background-color: #d9e0e7; padding: 6px; margin: 4px; border: 1px solid #8da1b4; border-radius: 5px; line-height: 60%\"><p style=\"margin-bottom: 0px\">Resource \"2-7-MedicationCard\"  (Language \"de-CH\") </p></div><p><b>EPR Information Recipient</b>: <a href=\"#Patient_MonikaWegmuellerRecipient\">See above (Patient/MonikaWegmuellerRecipient)</a></p><p><b>identifier</b>: id: urn:uuid:6b6ed376-a7da-44cb-92d1-e75ce1ae73b0</p><p><b>status</b>: final</p><p><b>type</b>: Medication summary <span style=\"background: LightGoldenRodYellow; margin: 4px; border: 1px solid khaki\"> (<a href=\"https://loinc.org/\">LOINC</a>#56445-0; <a href=\"https://browser.ihtsdotools.org/\">SNOMED CT</a>#721912009\"Medication summary document (record artifact)\")</span></p><p><b>date</b>: 2012-02-04T14:05:00+01:00</p><p><b>author</b>: </p><ul><li><a href=\"#Practitioner_FamilienHausarzt\">See above (Practitioner/FamilienHausarzt)</a></li><li><a href=\"#Organization_Hausarzt\">See above (Organization/Hausarzt)</a></li></ul><p><b>title</b>: Medikationsplan</p><p><b>confidentiality</b>: N</p><p><b>custodian</b>: <a href=\"#Organization_Custodian\">See above (Organization/Custodian)</a></p></div>"
+        },
+        "extension" : [
+          {
+            "url" : "http://fhir.ch/ig/ch-core/StructureDefinition/ch-ext-epr-informationrecipient",
+            "valueReference" : {
+             "reference" : "Patient/MonikaWegmuellerRecipient"
+            }
+          }
+        ],
+        "identifier" : {
+          "system" : "urn:ietf:rfc:3986",
+          "value" : "urn:uuid:6b6ed376-a7da-44cb-92d1-e75ce1ae73b0"
+        },
+        "status" : "final",
+        "type" : {
+          "coding" : [
+            {
+              "system" : "http://loinc.org",
+              "code" : "56445-0",
+              "display" : "Medication summary"
+            },
+            {
+              "system" : "http://snomed.info/sct",
+              "code" : "721912009",
+              "display" : "Medication summary document (record artifact)"
+            }
+          ]
+        },
+
  • subject: Who the composition is about. Its a reference to the resource contained as entry in the bundle (line 39 to 44).
  • date The date of the composition creation (line 42).
  • author: Identifies who is responsible for the information in the composition (line 43 to 56).
  • extension: Extension to EPR Time (line 45 to 50).
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
      "subject": {
+         "reference": "Patient/MonikaWegmueller"
+       },
+       "date": "2012-02-04T14:05:00+01:00",
+       "author": [
+         {
+           "extension": [
+             {
+               "url": "http://fhir.ch/ig/ch-core/StructureDefinition/ch-ext-epr-time",
+               "valueDateTime": "2012-02-04T13:55:00+01:00"
+             }
+           ],
+           "reference": "Practitioner/FamilienHausarzt"
+         },
+         {
+           "reference": "Organization/Hausarzt"
+         }
+       ],
+
  • title: Human readable label for the composition.
       "title": "Medikationsplan",
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
       "confidentiality": "N",
+       "_confidentiality": {
+         "extension": [
+           {
+             "url": "http://fhir.ch/ig/ch-core/StructureDefinition/ch-ext-epr-confidentialitycode",
+             "valueCodeableConcept": {
+               "coding": [
+                 {
+                   "system": "http://snomed.info/sct",
+                   "code": "17621005",
+                   "display": "Normally accessible"
+                 }
+               ]
+             }
+           }
+         ]
+       },
+
  • custodian: Organization who is responsible for the document and access.
1
+2
+3
       "custodian": {
+         "reference": "Organization/Custodian"
+       },
+
  • section: The root of the sections that make up the composition.
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
       "section": [
+         {
+           "title": "Medikamentenliste",
+           "code": {
+             "coding": [
+               {
+                 "system": "http://loinc.org",
+                 "code": "10160-0",
+                "display": "History of medication use"
+               }
+             ]
+           },
+           "entry": [
+             {
+               "reference": "MedicationStatement/2-7-MedStatBeloczok"
+             },
+             {
+               "reference": "MedicationStatement/2-7-MedStatNorvasc"
+             }
+           ]
+         },
+         {
+           "title": "Kommentar",
+           "code": {
+             "coding": [
+               {
+                 "system": "http://loinc.org",
+                 "code": "48767-8",
+                 "display": "Annotation comment"
+               }
+             ]
+           },
+           "text": {
+             "status": "generated",
+             "div": "<div xmlns=\"http://www.w3.org/1999/xhtml\">\n              <span id=\"co1\">\n                  Kommentar zu Medication Treatment\n              </span>\n            </div>"
+           }
+         },
+         {
+           "title": "Original Darstellung",
+           "code": {
+             "coding": [
+               {
+                 "system": "http://loinc.org",
+                 "code": "55108-5",
+                 "display": "Clinical presentation"
+               }
+             ]
+           },
+           "text": {
+             "status": "generated",
+             "div": "<div xmlns=\"http://www.w3.org/1999/xhtml\">\n              <a href=\"Binary/2-7-pdf\">Representation of the original view</a>\n            </div>"
+           },
+           "entry": [
+             {
+               "reference": "Binary/2-7-pdf"
+             }
+           ]
+         }
+       ]
+     }
+   },
+

Information about the patient

In the "Patient" resource, the demographic and administrative data of a patient are specified.

Profile information

 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
  "resource": {
+      "resourceType": "Patient",
+      "id": "MonikaWegmueller",
+      "text": {
+        "status": "generated",
+        "div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"><p><b>Generated Narrative</b></p><p><b>identifier</b>: Medical record number: 11111111</p><p><b>name</b>: Monika Wegmüller </p><p><b>gender</b>: female</p><p><b>birthDate</b>: 1943-05-15</p><p><b>address</b>: Wiesenstr. 12 Zürich 8003 CH </p></div>"
+      },
+     "identifier": [
+        {
+          "type": {
+            "coding": [
+              {
+                "system": "http://terminology.hl7.org/CodeSystem/v2-0203",
+                "code": "MR"
+              }
+            ]
+          },
+          "system": "urn:oid:2.999",
+          "value": "11111111"
+        }
+      ],
+      "name": [
+        {
+          "family": "Wegmüller",
+          "given": [
+            "Monika"
+          ]
+        }
+      ],
+      "gender": "female",
+      "birthDate": "1943-05-15",
+      "address": [
+        {
+          "line": [
+            "Wiesenstr. 12"
+          ],
+          "city": "Zürich",
+          "postalCode": "8003",
+          "country": "CH"
+        }
+      ]
+    }
+  },
+

Information about the practitioner

The resource Practitioner indicates which practitioner has prescribed a medication.

Profile information

 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
 "resource" : {
+      "resourceType" : "Practitioner",
+      "id" : "FamilienHausarzt",
+      "text" : {
+        "status" : "generated",
+        "div" : "<div xmlns=\"http://www.w3.org/1999/xhtml\"><p><b>Generated Narrative</b></p><div style=\"display: inline-block; background-color: #d9e0e7; padding: 6px; margin: 4px; border: 1px solid #8da1b4; border-radius: 5px; line-height: 60%\"><p style=\"margin-bottom: 0px\">Resource \"FamilienHausarzt\" </p></div><p><b>identifier</b>: id: 7601000234438</p><p><b>name</b>: Familien Hausarzt </p><p><b>address</b>: Krankenstrasse 2 Zürich 8005 CH </p></div>"
+      },
+      "identifier" : [
+        {
+          "system" : "urn:oid:2.51.1.3",
+          "value" : "7601000234438"
+        }
+      ],
+      "name" : [
+        {
+          "family" : "Hausarzt",
+          "given" : [
+            "Familien"
+          ]
+        }
+      ],
+      "address" : [
+        {
+          "line" : [
+            "Krankenstrasse 2"
+          ],
+          "city" : "Zürich",
+          "postalCode" : "8005",
+          "country" : "CH"
+        }
+      ]
+    }
+

Information about the organization

The resource stores the information about the organization that create the Medication Card document.

Profile information

 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
 "resource" : {
+      "resourceType" : "Organization",
+      "id" : "Hausarzt",
+      "text" : {
+        "status" : "generated",
+        "div" : "<div xmlns=\"http://www.w3.org/1999/xhtml\"><p><b>Generated Narrative</b></p><div style=\"display: inline-block; background-color: #d9e0e7; padding: 6px; margin: 4px; border: 1px solid #8da1b4; border-radius: 5px; line-height: 60%\"><p style=\"margin-bottom: 0px\">Resource \"Hausarzt\" </p></div><p><b>identifier</b>: id: 7601000234438</p><p><b>name</b>: Hausarzt</p><p><b>address</b>: Krankenstrasse 2 Zürich 8005 CH </p></div>"
+      },
+      "identifier" : [
+        {
+          "system" : "urn:oid:2.51.1.3",
+          "value" : "7601000234438"
+        }
+      ],
+      "name" : "Hausarzt",
+      "address" : [
+        {
+          "line" : [
+            "Krankenstrasse 2"
+          ],
+          "city" : "Zürich",
+          "postalCode" : "8005",
+          "country" : "CH"
+        }
+      ]
+    }
+

Medication

In the resource "MedicationStatement" the data of the patient's medication are specified.

Profile information

Medication information

  • id: Local id of the resource (line 2).
  • text: Presents the narrative text of the resource (line 3, 19, 54).
  • contained: The resource cannot be identified independently and therefore cannot exist independently apart from the resource it contains (line 7 to 73).
  • code: A code (or set of codes) that specify this medication, or a textual description if no code is available. (line 11, 25, 35, 41, 50, 67).
  • coding A reference to a code defined by a terminology system. (line 12,22, 47).
  • form Describes the form of the medication. Powder; tablets; capsule. (line 21 to 29).
  • amount: A relationship of two Quantity values - expressed as a numerator and a denominator. (line 30 to 36).
  • ingredient: Particular ingredient of a medication (line 44 to 71).
  • strength: A relationship of two Quantity values - expressed as a numerator and a denominator (line 56 to 69).
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
 "resourceType": "MedicationStatement",
+        "id": "2-7-MedStatBeloczok",
+        "text": {
+          "status": "extensions",
+          "div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"><p><b>Generated Narrative</b></p><blockquote><p><b>CH EMED Extension Treatment Plan</b></p><h3>Urls</h3><table class=\"grid\"><tr><td>-</td></tr><tr><td>*</td></tr></table><p><b>value</b>: id: urn:uuid:5712fffe-20c6-11e6-b67b-9e71128cae77</p><h3>Urls</h3><table class=\"grid\"><tr><td>-</td></tr><tr><td>*</td></tr></table><p><b>value</b>: id: urn:uuid:5712fffe-20c6-11e6-b67b-9e71128cae77</p></blockquote><p><b>identifier</b>: id: urn:uuid:d0f885ca-afa6-4e7e-905d-f7698f9607aa</p><p><b>status</b>: completed</p><p><b>medication</b>: <a name=\"med\"> </a></p><blockquote><p><b>code</b>: <span title=\"Codes: {urn:oid:2.51.1.1 7680521101306}\">BELOC ZOK Ret Tabl 50 mg</span></p><p><b>form</b>: <span title=\"Codes: {urn:oid:0.4.0.127.0.16.1.1.2.1 10219000}\">Tablet</span></p><p><b>amount</b>: 30 Tablet (unit of presentation)/1 Package</p><h3>Ingredients</h3><table class=\"grid\"><tr><td>-</td><td><b>Item[x]</b></td><td><b>Strength</b></td></tr><tr><td>*</td><td><span title=\"Codes: {http://snomed.info/sct 372826007}\">Metoprolol</span></td><td>50 milligram/1 Tablet (unit of presentation)</td></tr></table></blockquote><p><b>subject</b>: <a href=\"#Patient_MonikaWegmueller\">See above (Patient/MonikaWegmueller)</a></p><p><b>reasonCode</b>: <span title=\"Codes: \">Bluthochdruck</span></p><p><b>note</b>: -</p><h3>Dosages</h3><table class=\"grid\"><tr><td>-</td></tr><tr><td>*</td></tr><tr><td>*</td></tr><tr><td>*</td></tr></table></div>"
+        },
+        "contained": [
+          {
+            "resourceType": "Medication",
+           "id": "med",
+           "code": {
+             "coding": [
+               {
+                 "system": "urn:oid:2.51.1.1",
+                 "code": "7680521101306",
+               "display": "BELOC ZOK Ret Tabl 50 mg 30 Stk"
+             }
+           ],
+           "text": "BELOC ZOK Ret Tabl 50 mg"
+         },
+         "form": {
+           "coding": [
+             {
+               "system": "urn:oid:0.4.0.127.0.16.1.1.2.1",
+               "code": "10219000",
+               "display": "Tablet"
+             }
+           ]
+         },
+         "amount": {
+           "numerator": {
+             "value": 30,
+             "unit": "Tablet (unit of presentation)",
+             "system": "http://snomed.info/sct",
+             "code": "732936001"
+           },
+           "denominator": {
+             "value": 1,
+             "unit": "Package",
+             "system": "http://unitsofmeasure.org",
+             "code": "{Package}"
+             }
+           },
+           "ingredient": [
+             {
+               "itemCodeableConcept": {
+                 "coding": [
+                   {
+                     "system": "http://snomed.info/sct",
+                     "code": "372826007",
+                     "display": "Metoprolol (substance)"
+                   }
+                 ],
+                 "text": "Metoprolol"
+               },
+               "strength": {
+                 "numerator": {
+                   "value": 50,
+                   "unit": "milligram",
+                   "system": "http://unitsofmeasure.org",
+                   "code": "mg"
+                 },
+                 "denominator": {
+                   "value": 1,
+                   "unit": "Tablet (unit of presentation)",
+                   "system": "http://snomed.info/sct",
+                   "code": "732936001"
+                 }
+               }
+             }
+           ]
+         }
+       ],
+
  • indentifier: Identifiers associated with this Medication Statement that are defined by business processes and/or used to refer to it when a direct URL reference to the resource itself is not appropriate -fixed Value "urn:ietf:rfc:3986".
1
+2
+3
+4
+5
+6
 "identifier": [
+        {
+          "system": "urn:ietf:rfc:3986",
+          "value": "urn:uuid:d0f885ca-afa6-4e7e-905d-f7698f9607aa"
+        }
+      ],
+
  • status: A code representing the status of the use of the medication - fixed Value: "completed"; binding: Medication Status Codes.
 "status": "completed",
+
  • subject: The person who is taking the medication.
1
+2
+3
 "subject": {
+                    "reference": "Patient/69d661eb-e3ed-4e86-b34c-b5e747c13021"
+                },
+
1
+2
+3
 "reasonCode": [{
+                        "text": "Bluthochdruck/Herz"
+                    }
+
  • note: Provides extra information about the medication statement that is not conveyed by the other attributes.
1
+2
+3
+4
+5
 "note": [
+         {
+           "text": "-"
+         }
+       ],
+

Dosage

  • text: Non-structured dosage element (line 3).
  • timing: when the medication should be taken (line 6 to 16).
  • route: Indicates the route of administration - binding: SNOMEDCTRouteCodes; EDQM - RouteOfAdministration (line 17 to 25).
  • doseAndRate: The amount of medication administered (line 26 to 36).

Example normal Dosing (incl. Dosage Non-Structured):

Profile information (normal Dosing)

Profile information (Dosage Non-Structured)

 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
 "dosage" : [
+    {
+      "text" : "Morgens und abends je 1 Tablette einnehmen"
+    },
+    {
+      "timing" : {
+          "repeat" : {
+          "boundsPeriod" : {
+              "start" : "2012-02-04"
+          },
+          "when" : [
+              "MORN",
+              "EVE"
+          ]
+        }
+      },
+      "route" : {
+          "coding" : [
+          {
+              "system" : "urn:oid:0.4.0.127.0.16.1.1.2.1",
+              "code" : "20053000",
+              "display" : "Oral use"
+          }
+        ]
+      },
+      "doseAndRate" : [
+          {
+          "doseQuantity" : {
+              "value" : 1,
+              "unit" : "Tablet (unit of presentation)",
+              "system" : "http://snomed.info/sct",
+              "code" : "732936001"
+          }
+        }
+      ]
+    }
+  ]
+
  • text: Non-structured dosage element (line 3).
  • timing: when the medication should be taken (line 7 to 16).
  • route: Indicates the route of administration - binding: SNOMEDCTRouteCodes; EDQM - RouteOfAdministration (line 17 to 25).
  • doseAndRate: The amount of medication administered (line 26 to 36).

Example split Dosing (incl. Dosage Non-Structured):

Profile information (split Dosing)

Profile information (Dosage Non-Structured)

 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
 "dosage": [
+          {
+            "text": "Morgens 1 und abends 1/2 Tablette nehmen"
+          },
+          {
+            "sequence": 1,
+            "timing": {
+              "repeat": {
+                "boundsPeriod": {
+                 "start": "2012-02-04"
+               },
+               "when": [
+                 "MORN"
+               ]
+             }
+           },
+           "route": {
+             "coding": [
+               {
+                 "system": "urn:oid:0.4.0.127.0.16.1.1.2.1",
+                 "code": "20053000",
+                 "display": "Oral use"
+               }
+             ]
+           },
+           "doseAndRate": [
+             {
+               "doseQuantity": {
+                 "value": 1,
+                 "unit": "Tablet (unit of presentation)",
+                 "system": "http://snomed.info/sct",
+                 "code": "732936001"
+               }
+             }
+           ]
+         },
+         {
+           "sequence": 2,
+           "timing": {
+             "repeat": {
+               "boundsPeriod": {
+                 "start": "2012-02-04"
+               },
+               "when": [
+                 "EVE"
+               ]
+             }
+           },
+           "route": {
+             "coding": [
+               {
+                 "system": "urn:oid:0.4.0.127.0.16.1.1.2.1",
+                 "code": "20053000",
+                 "display": "Oral use"
+               }
+             ]
+           },
+           "doseAndRate": [
+             {
+               "doseQuantity": {
+                 "value": 0.5,
+                 "unit": "Tablet (unit of presentation)",
+                 "system": "http://snomed.info/sct",
+                 "code": "732936001"
+               }
+             }
+           ]
+         }
+       ]
+     }
+   },
+

The resource "Binary" represents the original representation of the Medication Card document.

1
+2
+3
+4
+5
+6
+7
 "resource": {
+                "resourceType": "Binary",
+                "id": "2-7-pdf",
+                "language": "de-CH",
+                "contentType": "application/pdf",
+                "data": "JVBERi0xLjQKMSAwIG9iago8PAovVGl0bGU..."
+            }
+

Last update: 2025-01-27
\ No newline at end of file diff --git a/PDQ/index.html b/PDQ/index.html new file mode 100644 index 0000000..9e80a0a --- /dev/null +++ b/PDQ/index.html @@ -0,0 +1,254 @@ + Patient Demographics Query - EPR by example
Skip to content

Patient Demographics Query

Transaction to search for patient identities and data from a community using the patient demographic data as search criteria. Primary systems may use this transaction to verify if a patient uses a Swiss EPR and is already registered in the community.

Overview

Primary systems may use this transaction to search for patients which are already registered in the community, either because the patient opened the Swiss EPR in the community or because the patient opened the Swiss EPR in a remote community and was already registered by another primary system to store documents. In the Swiss EPR the IHE PDQV3 profile and transactions shall be used to search for patients by demographic data.

To search for patients the primary system shall perform a Patient Demographic Query [ITI-47]. Within the query request the primary system shall provide the demographic data as search criteria. In the Swiss EPR each community must support the name, birthdate, gender and nationality. Individual communities may support other demographic data (e.g., address and other contact data).

The community sends a response with all patient data sets matching the search criteria. Each patient data set contains the known demographic data, the EPR-SPID and the assigned ID. The response contains the master data set as well as all known patient data sets, as registered by other primary systems.

Transaction

Message Semantics

Messages are encoded as described in the HL7 V3 standard with restrictions defined in the IHE PDQ V3 profile and the ordinances to the Swiss EPR.

Request Message

Since the HL7 V3 standard is very generic, the request message is quite lengthy and needs some background information to interpret. The raw version of a request message may be found here. For a step by step interpretation of the request message, see section below.

Message Interpretation

The request message is not very complex, but lengthy due to the genericity of the HL7 V3 standard. Therefore the following step by step interpretation may be of help to interpret the response.

The SOAP Header element conveys the following information:

  • To element: The URL of the provide an register document set service.
  • MessageID element: a UUID of the message.
  • Action element: The SOAP action identifier of the request as defined in the IHE ITI Technical Framework.
SOAP header
 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
 <soap:Header>
+  <Action soap:mustUnderstand="true"
+   xmlns="http://www.w3.org/2005/08/addressing">urn:hl7-org:v3:PRPA_IN201305UV02</Action>
+  <MessageID xmlns="http://www.w3.org/2005/08/addressing">urn:uuid:9fe7246b-8fab-4dd7-976e-c81bc1955575</MessageID>
+  <To xmlns="http://www.w3.org/2005/08/addressing">https://epd-test.ith-icoserve.com:7443/PIXPDQ/services/PIXPDQV3ManagerService</To>
+  <ReplyTo xmlns="http://www.w3.org/2005/08/addressing">
+   <Address>http://www.w3.org/2005/08/addressing/anonymous</Address>
+  </ReplyTo>
+ </soap:Header>
+

For the patient demographic query no Security header element is required, since in the Swiss EPR the access to the patient data is authorized for all applications, which are registered and authenticate with a client certificate (see section Security Requirements).

The SOAP Body element conveys the administrative information required for a PRPA_IN201305UV02 message in HL7 V3 syntax in which primary systems must set the following values:

  • creationTime: A timestamp in unix time format.
  • sender : The OID of the sender application initiating the request.
  • receiver: The OID of the receiver application which shall respond to the request.
PRPA_IN201305UV02 message
13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
   <id root="1.2.3.4"/>
+   <creationTime value="20230911143411"/>
+   <interactionId extension="PRPA_IN201305UV02" root="2.16.840.1.113883.1.6"/>
+   <processingCode code="P"/>
+   <processingModeCode code="T"/>
+   <acceptAckCode code="AL"/>
+   <receiver typeCode="RCV">
+    <device classCode="DEV" determinerCode="INSTANCE">
+     <id root="1.3.6.1.4.1.21367.2017.2.5.97"/>
+     <asAgent classCode="AGNT">
+      <representedOrganization classCode="ORG" determinerCode="INSTANCE">
+       <id root="1.3.6.1.4.1.21367.2017.2.7.127"/>
+      </representedOrganization>
+     </asAgent>
+    </device>
+   </receiver>
+   <sender typeCode="SND">
+    <device classCode="DEV" determinerCode="INSTANCE">
+     <id root="1.2.3.4"/>
+    </device>
+   </sender>
+

The query is encoded in a HL7 V3 controlAct object as follows:

PRPA_IN201305UV02 message
34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
   <controlActProcess classCode="CACT" moodCode="EVN">
+    <code code="PRPA_TE201305UV02" codeSystem="2.16.840.1.113883.1.6"/>
+    <queryByParameter>
+     <queryId extension="16944356511831" root="1.2.840.114350.1.13.28.1.18.5.999"/>
+     <statusCode code="new"/>
+     <responseModalityCode code="R"/>
+     <responsePriorityCode code="I"/>
+     <parameterList>
+      <livingSubjectId>
+       <value extension="08242eb8-dd47-4298-8d2f-25d60114f137" root="1.1.1.2.2"/>
+       <semanticsText>LivingSubject.id</semanticsText>
+      </livingSubjectId>
+      <otherIDsScopingOrganization>
+       <value root="1.3.6.1.4.1.21367.2017.2.5.93"/>
+       <semanticsText>OtherIDs.scopingOrganization.id</semanticsText>
+      </otherIDsScopingOrganization>
+      <otherIDsScopingOrganization>
+       <value root="2.16.756.5.30.1.127.3.10.3"/>
+       <semanticsText>OtherIDs.scopingOrganization.id</semanticsText>
+      </otherIDsScopingOrganization>
+     </parameterList>
+    </queryByParameter>
+   </controlActProcess>
+

The HL7 controlAct object conveys the query search parameter in a HL7 V3 parameterList element.

In the above example these are

  • the livingSubjectId conveying the local ID in the primary system of the patient data to search for,
  • the otherIDsScopingOrganization to match with the registered patient data,
parameterList element
41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
     <parameterList>
+      <livingSubjectId>
+       <value extension="08242eb8-dd47-4298-8d2f-25d60114f137" root="1.1.1.2.2"/>
+       <semanticsText>LivingSubject.id</semanticsText>
+      </livingSubjectId>
+      <otherIDsScopingOrganization>
+       <value root="1.3.6.1.4.1.21367.2017.2.5.93"/>
+       <semanticsText>OtherIDs.scopingOrganization.id</semanticsText>
+      </otherIDsScopingOrganization>
+      <otherIDsScopingOrganization>
+       <value root="2.16.756.5.30.1.127.3.10.3"/>
+       <semanticsText>OtherIDs.scopingOrganization.id</semanticsText>
+      </otherIDsScopingOrganization>
+     </parameterList>
+

The query supports many more search options and filter parameter. For a documentation of the options see IHE PDQ V3.

Response Message

Since the HL7 V3 standard is very generic, the response message is quite lengthy and needs some background information to interpret. The raw version of a response message may be found here. For a step by step interpretation of the message, see section below.

Message Interpretation

The PDQV3 service responds with a list of patient data which match the search parameter in a HL7 V3 subject child element of the controlAct object. The subject child element conveys the following information:

  • id: the XAD PID, which identifies the patient in the community (line 50) and the EPR-SPID (line 51).
  • name: conveying the given and the family names of the matching patient data (line 54).
  • administrativeGenderCode: conveying the coded value of patient gender (line 58), taken from the value sets defined in Annex 3.
  • birthTime: the data of birth of the matching patient data (line 59).
  • addr: The address data of the patient (line 60).
patient element
49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
       <ns1:patient classCode="PAT">
+        <ns1:id xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns1:II" root="1.3.6.1.4.1.21367.2017.2.5.93" extension="25f98b34-0e01-48b7-a06c-f706eb4c485f" assigningAuthorityName="XDS Affinity Domain"/>
+        <ns1:id xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns1:II" root="2.16.756.5.30.1.127.3.10.3" extension="761337610411353650" assigningAuthorityName="SPID"/>
+        <ns1:statusCode code="active"/>
+        <ns1:patientPerson classCode="PSN" determinerCode="INSTANCE">
+         <ns1:name xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns1:PN">
+          <ns1:given>Dylan Jose</ns1:given>
+          <ns1:family>Dell</ns1:family>
+         </ns1:name>
+         <ns1:administrativeGenderCode code="F"/>
+         <ns1:birthTime xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns1:TS" value="19890622"/>
+         <ns1:addr xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns1:AD" use="HP">
+          <ns1:city>Pontarlier</ns1:city>
+          <ns1:postalCode>25300</ns1:postalCode>
+          <ns1:streetName>Ruelle de la Tour</ns1:streetName>
+         </ns1:addr>
+        </ns1:patientPerson>
+        <ns1:providerOrganization classCode="ORG" determinerCode="INSTANCE">
+         <ns1:id xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns1:II" root="1.3.6.1.4.1.21367.13.20.2000"/>
+         <ns1:contactParty classCode="CON"/>
+        </ns1:providerOrganization>
+        <ns1:subjectOf1>
+         <ns1:queryMatchObservation classCode="COND" moodCode="EVN">
+          <ns1:code xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns1:CD" code="IHE_PDQ"/>
+          <ns1:value xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns1:INT" value="100"/>
+         </ns1:queryMatchObservation>
+        </ns1:subjectOf1>
+       </ns1:patient>
+

Transport Protocol

The primary system shall send the request messages to the registry of the community using the http POST binding as defined in the W3C SOAP specification. It may look like:

1
+2
+3
+4
+5
+6
POST /PDQV3Service HTTP/1.1
+Host: company.example.org
+Accept-Encoding: gzip, deflate
+Connection: Keep-Alive
+Content-Type: application/soap+xml; charset="utf-8"
+Content-Length: nnnn  
+

Audit Log

Primary systems shall store syslog messages to the audit record repository of the community using TLS transport protocol. The audit message uses XML formatting as specified in RFC 3881 with restrictions specified in the IHE ITI TF and the Extension 1 to Annex5 in the ordinances of the Swiss electronic patient record (see Section 1.5 "Requirements on ATNA").

iti-47-log.xml
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
<?xml version='1.0' encoding='utf-8'?>
+<AuditMessage>
+ <EventIdentification EventActionCode="E" EventDateTime="2020-09-30T19:27:29.386Z" EventOutcomeIndicator="0">
+  <EventID csd-code="110112" codeSystemName="DCM" originalText="Query"></EventID>
+  <EventTypeCode csd-code="ITI-47" codeSystemName="IHE Transactions" originalText="Patient Demographics Query"></EventTypeCode>
+ </EventIdentification>
+ <ActiveParticipant UserID="https://my_primary_system.com/PDQConsumer" AlternativeUserID="201809" UserIsRequestor="true" NetworkAccessPointID="0045e6d09dd0" NetworkAccessPointTypeCode="1">
+  <RoleIDCode csd-code="110153" codeSystemName="DCM" originalText="Source Role ID"></RoleIDCode>
+ </ActiveParticipant>
+ <ActiveParticipant UserID="_SYSTEM" UserIsRequestor="true" NetworkAccessPointID="0045e6d09dd0" NetworkAccessPointTypeCode="1">
+  <RoleIDCode csd-code="%All" codeSystemName="HealthShare" originalText="%All"></RoleIDCode>
+ </ActiveParticipant>
+ <ActiveParticipant UserID="https://ehealthsuisse.ihe-europe.net/PDQService" UserIsRequestor="false" NetworkAccessPointID="ehealthsuisse.ihe-europe.net" NetworkAccessPointTypeCode="1">
+  <RoleIDCode csd-code="110152" codeSystemName="DCM" originalText="Destination Role ID"></RoleIDCode>
+ </ActiveParticipant>
+ <AuditSourceIdentification AuditEnterpriseSiteID="2.16.756.5.30.1.109.6.1.3.1.1" AuditSourceID="my.primary.system.ID">
+  <AuditSourceTypeCode csd-code="4"></AuditSourceTypeCode>
+ </AuditSourceIdentification>
+ <ParticipantObjectIdentification ParticipantObjectID="CHPAM34^^^&amp;1.3.6.1.4.1.12559.11.20.1&amp;ISO" ParticipantObjectTypeCode="1" ParticipantObjectTypeCodeRole="1">
+  <ParticipantObjectIDTypeCode csd-code="2" codeSystemName="RFC-3881" originalText="Patient Number"></ParticipantObjectIDTypeCode>
+  <ParticipantObjectName>^Neil^Mellisa</ParticipantObjectName>
+  <ParticipantObjectDetail type="II" value="RjlENjJBNjgtMDM1Mi0xMUVCLUE2RTgtMDI0MkFDMTQwMDAy"></ParticipantObjectDetail>
+ </ParticipantObjectIdentification>
+ <ParticipantObjectIdentification ParticipantObjectID="1^^^&amp;F9D62A4A-0352-11EB-A6E8-0242AC140002&amp;ISO" ParticipantObjectTypeCode="2" ParticipantObjectTypeCodeRole="24">
+  <ParticipantObjectIDTypeCode csd-code="ITI-47" codeSystemName="IHE Transactions" originalText="Patient Demographics Query"></ParticipantObjectIDTypeCode>
+  <ParticipantObjectQuery> <!-- BASE-64 encoded copy of the query message --> </ParticipantObjectQuery>
+  <ParticipantObjectDetail type="MSH-10" value="MV5eXiZGOUQ2MkE2OC0wMzUyLTExRUItQTZFOC0wMjQyQUMxNDAwMDImSVNP"></ParticipantObjectDetail>
+ </ParticipantObjectIdentification>
+</AuditMessage>
+

The message is made of the following blocks:

  • EventIdentification: Event related information including the timestamp (line 3 .. 6).
  • ActiveParticipant: Information related to the primary system performing the query (line 7 .. 9).
  • ActiveParticipant: Information on the user initiating the transaction (line 10 .. 12).
  • ActiveParticipant: Information on the responding service endpoint (line 13 .. 15).
  • AuditSourceIdentification: Information related to the primary system performing the query (line 16 .. 18)
  • ParticipantObjectIdentification: Information on the patients EPR accessed (line 19 .. 23)
  • ParticipantObjectIdentification: Request message related information including a UBASE-64 encoded copy of the query (line 24 .. 28).

Security Requirements

To ensure privacy the transaction must be secured using https with mutual authentication, with X.509 certificates (extended validation required) and client and server side certificate validation.

Note

  • Some test environments dropped the mutual authentication or TLS for testing purposes. Please contact your test system provider on the details.

Test Opportunity

The transaction can be tested with the test suite of the EPR reference environment, test systems of the EPR communities or the EPR Playground.


Last update: 2025-01-27
\ No newline at end of file diff --git a/PIXFeed/index.html b/PIXFeed/index.html new file mode 100644 index 0000000..4383256 --- /dev/null +++ b/PIXFeed/index.html @@ -0,0 +1,229 @@ + PIX Feed - EPR by example
Skip to content

PIX Feed

Transaction to register a patient in a community. Primary systems shall use this transaction to register patient data to be able to provide and retrieve documents to the patients EPR.

Overview

Primary systems shall use this transaction to register patient data with the local ID, the patient is registered in the primary system. In the Swiss EPR the IHE PIX V3 profile and transactions shall be used to register the patient data.

To register the patient data the primary system shall perform a Patient Identity Feed HL7 V3 [ITI-44] transaction. In the feed request the primary system must provide the demographic data as provided by the ZAS central service, which includes the name, birthdate, gender, nationality and the EPR-SPID. Primary systems may provide other demographic data (e.g., address and other contact data).

The community response includes a success confirmation, or a error code in the case an error occurred in the community during registration. In case of success the community stores the patient data provided by the primary system, matches the data set to other patient data set registered by other primary systems and assigns the patient data set to a master patient record and the master patient ID (XAD-PID).

To perform the PIX V3 feed fo the EPR, primary systems must retrieve the demographic data and the EPR-SPID from the ZAS central service. While the interface to be used by the communities is specified in the ordinances to the Swiss electronic patient dossier, the interface for primary systems is not, since communities provide simplified interfaces for primary systems to retrieve the data or included the interface in the registration workflow. Please contact the community you want to connect to on implementation details.

Transaction

Message Semantics

Messages are encoded as described in the HL7 V3 standard with restrictions defined in the IHE Patient Identity Feed HL7 V3 profile and the ordinances to the Swiss EPR.

Request Message

Due to the genericity of the underlying HL7 V3 standard, the request message is quite lengthy. A raw version of a request message may be found here.

For a step by step interpretation of the request message, see section below.

Message Interpretation

The request message is not very complex, but lengthy due to the genericity of the HL7 V3 standard.

The SOAP Header element shall convey the following information:

  • To element: The URL of the provide and register document set service.
  • MessageID element: a UUID of the message.
  • Action element: The SOAP action identifier of the request as defined in the IHE ITI Technical Framework.

Optional elements may be included according to the specification in the W3C SOAP specification.

SOAP header
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
+  <env:Header>
+    <wsa:To xmlns:wsa="http://www.w3.org/2005/08/addressing">
+   https://epd-test.ith-icoserve.com:7443/PIXPDQ/services/PIXPDQV3ManagerService
+    </wsa:To>
+    <wsa:MessageID xmlns:wsa="http://www.w3.org/2005/08/addressing">urn:uuid:c12e1f14-c2c9-4a94-ba27-6511e8c90b78</wsa:MessageID>
+    <wsa:ReplyTo xmlns:wsa="http://www.w3.org/2005/08/addressing">
+      <wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address>
+    </wsa:ReplyTo>
+    <wsa:Action xmlns:wsa="http://www.w3.org/2005/08/addressing" env:mustUnderstand="1">urn:hl7-org:v3:PRPA_IN201301UV02</wsa:Action>
+  </env:Header>
+

For the patient identity feed no Security header element is required, since in the Swiss EPR the access to the patient data is authorized for all applications, which are registered in the community and authenticate with a client certificate (see section Security Requirements).

The SOAP Body element conveys the administrative information required for a PRPA_IN201305UV02 message in HL7 V3 syntax.

Primary systems shall set the following values:

  • creationTime: A timestamp in unix time format.
  • sender : The OID of the sender application initiating the request.
  • receiver: The OID of the receiver application which shall respond to the request.
PRPA_IN201301UV02 message
13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
    <PRPA_IN201301UV02 xmlns="urn:hl7-org:v3" ITSVersion="XML_1.0">
+      <id extension="1694431245655" root="1.3.6.1.4.1.21367.2017.2.7.141"/>
+      <creationTime value="20230911115902"/>
+      <interactionId extension="PRPA_IN201301UV02" root="2.16.840.1.113883.1.6"/>
+      <processingCode code="T"/>
+      <processingModeCode code="T"/>
+      <acceptAckCode code="AL"/>
+      <receiver typeCode="RCV">
+        <device classCode="DEV" determinerCode="INSTANCE">
+          <id root="1.3.6.1.4.1.21367.2017.2.4.136"/>
+        </device>
+      </receiver>
+      <sender typeCode="SND">
+        <device classCode="DEV" determinerCode="INSTANCE">
+          <id root="1.3.6.1.4.1.21367.2017.2.2.140"/>
+        </device>
+      </sender>
+

The patient data are encoded in a HL7 V3 controlActProcess object as follows:

controlActProcess element
30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
      <controlActProcess classCode="CACT" moodCode="EVN">
+        <code code="PRPA_TE201301UV02" codeSystem="2.16.840.1.113883.1.6"/>
+        <subject contextConductionInd="false" typeCode="SUBJ">
+          <registrationEvent classCode="REG" moodCode="EVN">
+            <statusCode code="active"/>
+            <subject1 typeCode="SBJ">
+              <patient classCode="PAT">
+                <id assigningAuthorityName="MyPrimarySystem" extension="TestSystemId" root="1.3.6.1.4.1.21367.2017.2.5.89"/>
+                <id assigningAuthorityName="ZAS" extension="761337610435201235" root="2.16.756.5.30.1.127.3.10.3"/>
+                <statusCode code="active"/>
+                <patientPerson classCode="PSN" determinerCode="INSTANCE">
+                  <name>
+                    <family>Muster</family>
+                    <given>Maja</given>
+                  </name>
+                  <name>
+                    <family qualifier="BR">Tauxe</family>
+                    <given>Maja</given>
+                  </name>
+                  <administrativeGenderCode code="F" codeSystem="2.16.840.1.113883.12.1" displayName="Female"/>
+                  <birthTime value="19600618"/>
+                  <addr>
+                    <city>Wettingen</city>
+                    <country>CH</country>
+                    <postalCode>5430</postalCode>
+                    <streetAddressLine>Imfeldstrasse 24b</streetAddressLine>
+                  </addr>
+                </patientPerson>
+                <providerOrganization classCode="ORG" determinerCode="INSTANCE">
+                  <id root="1.3.6.1.4.1.21367.2017.2.5.89"/>
+                  <id root="2.16.756.5.30.1.127.3.10.3"/>
+                  <name>MyCompany</name>
+                  <contactParty classCode="CON">
+                    <contactPerson classCode="PSN" determinerCode="INSTANCE">
+                      <name xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="PN">
+                        <family>Administrator</family>
+                        <given>Max</given>
+                      </name>
+                    </contactPerson>
+                  </contactParty>
+                </providerOrganization>
+              </patient>
+            </subject1>
+

The subject child element conveys the following information in its child elements.

The patient child element conveys the patients identifiers and patient demographics:

  • id: the local ID of the patient in the primary system (line 25) with the OID of the primary system in the root attribute.
  • id: the EPR-SPID of the patient provided by the ZAS (line 26) with the OID of the ZAS in the root attribute.

The patients demographic data are conveyed in the patientPerson child element:

  • name: conveying the given and the family name of the patient.
  • administrativeGenderCode: conveying the coded value of patient gender, taken from the value sets defined in Annex 3.
  • birthTime: the data of birth of the matching patient data.
  • addr : The address data of the patient.
  • custodian: Information on the provider organization.

The custodian element shall convey the OID of the provider organization in the id child element:

custodian element
73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
            <custodian typeCode="CST">
+              <assignedEntity classCode="ASSIGNED">
+                <id root="1.3.6.1.4.1.21367.2017.2.5.108"/>
+                <assignedOrganization classCode="ORG" determinerCode="INSTANCE">
+                  <name xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ON">MyCompany</name>
+                </assignedOrganization>
+              </assignedEntity>
+            </custodian>
+          </registrationEvent>
+        </subject>
+      </controlActProcess>
+    </PRPA_IN201301UV02>
+

Response Message

The PIX V3 Feed service responds with a message indicating the success of the transaction. A raw version of a response message may be found here.

Transport Protocol

The primary system shall send the request messages to the registry of the community using the http POST binding as defined in the W3C SOAP specification. It may look like:

1
+2
+3
+4
+5
+6
POST /PIXV3FeedService HTTP/1.1
+Host: company.example.org
+Accept-Encoding: gzip, deflate
+Connection: Keep-Alive
+Content-Type: application/soap+xml; charset="utf-8"
+Content-Length: nnn  
+

Audit Log

Primary systems shall store syslog messages to the audit record repository of the community using TLS transport protocol. The audit message uses XML formatting as specified in RFC 3881 with restrictions specified in the IHE ITI TF and the Extension 1 to Annex5 in the ordinances of the Swiss electronic patient record (see Section 1.5 "Requirements on ATNA").

The following snippet shows a example audit message to be written by the primary system:

iti-44-log.xml
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
<?xml version='1.0' encoding='utf-8'?>
+<AuditMessage>
+ <EventIdentification EventDateTime="2020-09-21T15:25:53.616+02:00" EventOutcomeIndicator="0" EventActionCode="C">
+  <EventID csd-code="110110" codeSystemName="DCM" originalText="Patient Record"/>
+  <EventTypeCode csd-code="ITI-44" codeSystemName="IHE Transactions" originalText="Patient Identity Feed"/>
+  <EventOutcomeDescription/>
+ </EventIdentification>
+ <ActiveParticipant AlternativeUserID="primary.system.alt.ID" UserID="primary.system.ID" UserIsRequestor="true" NetworkAccessPointID="https://my_primary_system.com/" NetworkAccessPointTypeCode="1">
+  <RoleIDCode csd-code="110153" codeSystemName="DCM" originalText="Source"/>
+ </ActiveParticipant>
+ <ActiveParticipant AlternativeUserID="plattform.mpi.alt.ID" UserID="plattform.mpi.ID" UserIsRequestor="false" NetworkAccessPointID="https://platform.com/mock/patientRegister" NetworkAccessPointTypeCode="1">
+  <RoleIDCode csd-code="110152" codeSystemName="DCM" originalText="Destination"/>
+ </ActiveParticipant>
+ <AuditSourceIdentification AuditSourceID="primary.system.alt.ID" AuditEnterpriseSiteID="2.16.756.5.30.1.174.1.5.1">
+  <AuditSourceTypeCode csd-code="4" codeSystemName="DCM" originalText="Client Process"/>
+ </AuditSourceIdentification>
+ <ParticipantObjectIdentification ParticipantObjectID="11234^^^&amp;2.16.756.5.30.1.174.1.9999.1&amp;ISO" ParticipantObjectTypeCode="1" ParticipantObjectTypeCodeRole="1">
+  <ParticipantObjectIDTypeCode csd-code="2" codeSystemName="RFC-3881" originalText="Patient Number"/>
+  <ParticipantObjectDetail type="II" value="ZGVmYXVsdA=="/>
+ </ParticipantObjectIdentification>
+ <ParticipantObjectIdentification ParticipantObjectID="urn:uuid:4d42d62d-30b7-4ad4-a9e2-c3f0dd1d50f9" ParticipantObjectTypeCode="2" ParticipantObjectTypeCodeRole="24">
+  <ParticipantObjectIDTypeCode csd-code="ITI-44" codeSystemName="IHE Transactions" originalText="Patient Identity Feed"/>
+  <ParticipantObjectQuery>
+   <!-- omitted for brevity -->
+  </ParticipantObjectQuery>
+  <ParticipantObjectDetail type="II" value="ZGVmYXVsdA=="/>
+ </ParticipantObjectIdentification>
+</AuditMessage>
+

The message is made of the following blocks:

  • EventIdentification: Event related information including the timestamp (line 3 .. 7).
  • ActiveParticipant: Information related to the primary system performing the query (line 8 .. 10).
  • ActiveParticipant: Information on the responding service endpoint (line 11 .. 13).
  • AuditSourceIdentification: Information related to the primary system performing the query (line 14 .. 16)
  • ParticipantObjectIdentification: Information on the patients EPR accessed (line 17 .. 20)
  • ParticipantObjectIdentification: Request message related information including a BASE-64 encoded copy of the query (line 21 .. 27).

Security Requirements

To ensure privacy the transaction must be secured using https with mutual authentication, with X.509 certificates (extended validation required) and client and server side certificate validation.

Note

  • Some test environments dropped the mutual authentication or TLS for testing purposes. Please contact your test system provider on the details.

Test Opportunity

The transaction can be tested with the test suite of the EPR reference environment, test systems of the EPR communities or the **EPR Playground.


Last update: 2025-01-27
\ No newline at end of file diff --git a/PIXQuery/index.html b/PIXQuery/index.html new file mode 100644 index 0000000..314e7d3 --- /dev/null +++ b/PIXQuery/index.html @@ -0,0 +1,224 @@ + PIX Query - EPR by example
Skip to content

PIX Query

Transaction to get the master patient ID of a patient in a community using the local ID.

Overview

Primary systems shall use this transaction to retrieve the master patient ID (XAD-SPID) for patients the primary systems wants to retrieve or provide documents for. In the Swiss EPR the IHE PIX V3 profile and transactions shall be used to retrieve the master patient ID.

To retrieve the master patient ID for the patient to access the patients EPR, the the primary system shall perform a Patient V3 Query [ITI-45]. Within the query request the primary system shall provide the local ID of the patient in the primary system, as well as the data source parameter of the assigning authority of the community and the the assigning authority EPR-SPID. The local ID must match the local ID the primary system registered the patient with (see PIX Feed).

If the patient is registered in the community, the community sends a response with the master patient ID (XAD-PID) and the EPR-SPID.

Transaction

Message Semantics

Messages are encoded as described in the HL7 V3 standard with restrictions defined in the IHE PIX V3 Query profile and the ordinances to the Swiss EPR.

Request Message

Due to the genericity of the underlying HL7 V3 standard, the request message is quite lengthy. A raw version of a request message may be found here.

For a step by step interpretation of the request message, see section below.

Message Interpretation

The request message is not complex in nature, but quite lengthy due to the genericity of the HL7 V3 standard.

The SOAP Header element shall convey the following information:

  • To element: The URL of the provide and register document set service.
  • MessageID element: a UUID of the message.
  • Action element: The SOAP action identifier of the request as defined in the IHE ITI Technical Framework.

Optional elements may be included according to the specification in the W3C SOAP specification.

SOAP header
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
+ <env:Header>
+  <wsa:To xmlns:wsa="http://www.w3.org/2005/08/addressing">
+   https://epd-test.ith-icoserve.com:7443/PIXPDQ/services/PIXPDQV3ManagerService
+  </wsa:To>
+  <wsa:MessageID xmlns:wsa="http://www.w3.org/2005/08/addressing">urn:uuid:c12e1f14-c2c9-4a94-ba27-6411e8c90b75</wsa:MessageID>
+  <wsa:ReplyTo xmlns:wsa="http://www.w3.org/2005/08/addressing">
+   <wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address>
+  </wsa:ReplyTo>
+  <wsa:Action xmlns:wsa="http://www.w3.org/2005/08/addressing" env:mustUnderstand="1">urn:hl7-org:v3:PRPA_IN201309UV02</wsa:Action>
+ </env:Header>
+

For the PIX query no Security header element is required, since in the Swiss EPR the access to the patient data is authorized for all applications, which are registered in the community and authenticate with a client certificate (see section Security Requirements).

The SOAP Body element conveys the administrative information required for a HL7 V3 PRPA_IN201310UV02 message in HL7 V3 syntax.

Primary systems shall set the following values:

  • creationTime: A timestamp in unix time format.
  • sender : The OID of the sender application initiating the request.
  • receiver: The OID of the receiver application which shall respond to the request.
PRPA_IN201309UV02 message
13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
  <PRPA_IN201309UV02 xmlns="urn:hl7-org:v3" ITSVersion="XML_1.0">
+   <id extension="1694523036420" root="1.3.6.1.4.1.21367.2017.2.7.141"/>
+   <creationTime value="20230912131951"/>
+   <interactionId extension="PRPA_IN201309UV02" root="2.16.840.1.113883.1.6"/>
+   <processingCode code="T"/>
+   <processingModeCode code="T"/>
+   <acceptAckCode code="AL"/>
+   <receiver typeCode="RCV">
+    <device classCode="DEV" determinerCode="INSTANCE">
+     <id root="1.3.6.1.4.1.21367.2017.2.4.136"/>
+    </device>
+   </receiver>
+   <sender typeCode="SND">
+    <device classCode="DEV" determinerCode="INSTANCE">
+     <id root="1.3.6.1.4.1.21367.2017.2.2.140"/>
+    </device>
+   </sender>
+

The query parameter are encoded in a HL7 V3 controlActProcess element of the message. Primary systems shall set the following query attributes:

  • authorOrPerformer: The OID of the primary system in the id element.
controlActProcess element
30
+31
+32
+33
+34
+35
+36
   <controlActProcess classCode="CACT" moodCode="EVN">
+    <code code="PRPA_TE201309UV02" displayName="2.16.840.1.113883.1.6"/>
+    <authorOrPerformer typeCode="AUT">
+     <assignedPerson classCode="ASSIGNED">
+      <id root="1.3.6.1.4.1.21367.2017.2.5.108"/>
+     </assignedPerson>
+    </authorOrPerformer>
+

The query parameter are conveyed in the queryByParameter child element:

  • queryId: A unique ID of the query.
  • dataSource: The OID of the assigning authority of the community (line 43).
  • dataSource: The OID of the assigning authority of the EPR-SPID (line 46).
  • patientIdentifier: The ID of the patient data in the primary system, with the OID of the primary system in the root element and the local ID in the extension element.
controlActProcess element
37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
    <queryByParameter>
+     <queryId extension="1694523036421" root="1.3.6.1.4.1.21367.2017.2.7.141"/>
+     <statusCode code="new"/>
+     <responsePriorityCode code="I"/>
+     <parameterList>
+      <dataSource>
+       <value root="1.3.6.1.4.1.21367.2017.2.5.93"/>
+       <semanticsText>DataSource.id</semanticsText>
+      </dataSource>
+      <dataSource>
+       <value root="2.16.756.5.30.1.127.3.10.3"/>
+       <semanticsText>DataSource.id</semanticsText>
+      </dataSource>
+      <patientIdentifier>
+       <value extension="900010" root="1.3.6.1.4.1.21367.2017.2.5.103"/>
+       <semanticsText>Patient.id</semanticsText>
+      </patientIdentifier>
+     </parameterList>
+    </queryByParameter>
+

Response Message

The PIX V3 Feed service responds with the master patient ID (XAD-PID) and the EPR-SPID, the patient is registered with in the community.

The request message is not very complex, but quite lengthy due to the genericity of the HL7 V3 standard. A raw version of a response message may be found here.

Message Interpretation

The SOAP Header element shall conveys the following information:

  • Action element: The SOAP action identifier of the request as defined in the IHE ITI Technical Framework.
  • RelatesTo element: A copy of the unique ID of the query request.
SOAP header
1
+2
+3
+4
+5
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
+  <soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">
+    <wsa:Action xmlns:mustUnderstand="http://www.w3.org/2003/05/soap-envelope" mustUnderstand:mustUnderstand="1">urn:hl7-org:v3:PRPA_IN201310UV02</wsa:Action>
+    <wsa:RelatesTo>urn:uuid:c12e1f14-c2c9-4a94-ba27-6411e8c90b75</wsa:RelatesTo>
+  </soapenv:Header>
+

The SOAP Body element conveys the administrative information required for a PRPA_IN201310UV02 message in HL7 V3 syntax and the query result encoded in the controlActProcess element.

The controlActProcess element conveys the following information for the primary system in the subject child element:

The patient child element conveys the master patient ID (XAD-SPID) and the EPR-SPID as follows:

  • id : The master patient ID (XAD-SPID), with the community OID as the assigning authority in the root and the ID in the extension attribute (line 49).
  • id : The EPR-SPID, with the OID of the ZAS as assigning authority in the root and the ID in the extension attribute (line 50).
registrationEvent element
44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
          <ns1:registrationEvent classCode="REG" moodCode="EVN">
+            <ns1:id xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" nullFlavor="NA" xsi:type="ns1:II"/>
+            <ns1:statusCode code="active"/>
+            <ns1:subject1 typeCode="SBJ">
+              <ns1:patient classCode="PAT">
+                <ns1:id xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" assigningAuthorityName="XDS Affinity Domain" extension="7f2e05e6-673e-44b6-8b76-e17ac58ea80f" root="1.3.6.1.4.1.21367.2017.2.5.93" xsi:type="ns1:II"/>
+                <ns1:id xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" assigningAuthorityName="SPID" extension="761337610435209810" root="2.16.756.5.30.1.127.3.10.3" xsi:type="ns1:II"/>
+                <ns1:statusCode code="active"/>
+                <ns1:patientPerson classCode="PSN" determinerCode="INSTANCE">
+                  <ns1:name xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" nullFlavor="NA" xsi:type="ns1:PN"/>
+                </ns1:patientPerson>
+              </ns1:patient>
+            </ns1:subject1>
+

The custodian child element conveys information on the responding system with the the OID of the provider organization in the id child element as follows:

custodian element
57
+58
+59
+60
+61
+62
+63
+64
+65
+66
            <ns1:custodian typeCode="CST">
+              <ns1:assignedEntity classCode="ASSIGNED">
+                <ns1:id xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" extension="MyOrgID" root="1.3.6.1.4.1.21367.2010.1.2.600" xsi:type="ns1:II"/>
+                <ns1:assignedOrganization classCode="ORG" determinerCode="INSTANCE">
+                  <ns1:name xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns1:EN">
+                    <ns1:given>MyOrganisation</ns1:given>
+                  </ns1:name>
+                </ns1:assignedOrganization>
+              </ns1:assignedEntity>
+            </ns1:custodian>
+

Transport Protocol

The primary system shall send the request messages to the registry of the community using the http POST binding as defined in the W3C SOAP specification. It may look like:

1
+2
+3
+4
+5
+6
POST /PIXV3QueryService HTTP/1.1
+Host: company.example.org
+Accept-Encoding: gzip, deflate
+Connection: Keep-Alive
+Content-Type: application/soap+xml; charset="utf-8"
+Content-Length: nnnn    
+

Audit Log

Primary systems shall store syslog messages to the audit record repository of the community using TLS transport protocol. The audit message uses XML formatting as specified in RFC 3881 with restrictions specified in the IHE ITI TF and the Extension 1 to Annex5 in the ordinances of the Swiss electronic patient record (see Section 1.5 "Requirements on ATNA").

The following snippet shows a example audit message to be written by the primary system:

iti-45-log.xml
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
<?xml version='1.0' encoding='utf-8'?>
+<AuditMessage>
+ <EventIdentification EventActionCode="E" EventDateTime="2020-09-30T19:32:55.368Z" EventOutcomeIndicator="0">
+  <EventID csd-code="110112" codeSystemName="DCM" originalText="Query"></EventID>
+  <EventTypeCode csd-code="ITI-45" codeSystemName="IHE Transactions" originalText="PIX Query"></EventTypeCode>
+ </EventIdentification>
+ <ActiveParticipant UserID="https://my_primary_system.com/PIXConsumer" AlternativeUserID="201818" UserIsRequestor="true" NetworkAccessPointID="0045e6d09dd0" NetworkAccessPointTypeCode="1">
+  <RoleIDCode csd-code="110153" codeSystemName="DCM" originalText="Source Role ID"></RoleIDCode>
+ </ActiveParticipant>
+ <ActiveParticipant UserID="_SYSTEM" UserIsRequestor="true" NetworkAccessPointID="0045e6d09dd0" NetworkAccessPointTypeCode="1">
+  <RoleIDCode csd-code="%All" codeSystemName="HealthShare" originalText="%All"></RoleIDCode>
+ </ActiveParticipant>
+ <ActiveParticipant UserID="https://ehealthsuisse.ihe-europe.net/PIXManagerService" UserIsRequestor="false" NetworkAccessPointID="ehealthsuisse.ihe-europe.net" NetworkAccessPointTypeCode="1">
+  <RoleIDCode csd-code="110152" codeSystemName="DCM" originalText="Destination Role ID"></RoleIDCode>
+ </ActiveParticipant>
+ <AuditSourceIdentification AuditEnterpriseSiteID="2.16.756.5.30.1.109.6.1.3.1.1" AuditSourceID="my.primary.system.ID">
+  <AuditSourceTypeCode csd-code="4"></AuditSourceTypeCode>
+ </AuditSourceIdentification>
+ <ParticipantObjectIdentification ParticipantObjectID="CHFACILITY22332^^^&amp;1.3.6.1.4.1.12559.11.25.1.19&amp;ISO" ParticipantObjectTypeCode="1" ParticipantObjectTypeCodeRole="1">
+  <ParticipantObjectIDTypeCode csd-code="2" codeSystemName="RFC-3881" originalText="Patient Number"></ParticipantObjectIDTypeCode>
+  <ParticipantObjectDetail type="II" value="QkMzMjJDQjAtMDM1My0xMUVCLTk5OTQtMDI0MkFDMTQwMDAy"></ParticipantObjectDetail>
+ </ParticipantObjectIdentification>
+ <ParticipantObjectIdentification ParticipantObjectID="1^^^&amp;BC322C7E-0353-11EB-9994-0242AC140002&amp;ISO" ParticipantObjectTypeCode="2" ParticipantObjectTypeCodeRole="24">
+  <ParticipantObjectIDTypeCode csd-code="ITI-45" codeSystemName="IHE Transactions" originalText="PIX Query"></ParticipantObjectIDTypeCode>
+  <ParticipantObjectQuery> <!-- omitted for brevity --> </ParticipantObjectQuery>
+  <ParticipantObjectDetail type="MSH-10" value="Xl5eJkJDMzIyQ0IwLTAzNTMtMTFFQi05OTk0LTAyNDJBQzE0MDAwMiZJU08="></ParticipantObjectDetail>
+ </ParticipantObjectIdentification>
+</AuditMessage>
+

The message is made of the following blocks:

  • EventIdentification: Event related information including the timestamp (line 3 .. 6).
  • ActiveParticipant: Information related to the primary system performing the query (line 7 .. 9).
  • ActiveParticipant: Information on the user initiating the transaction (line 10 .. 12).
  • ActiveParticipant: Information on the responding service endpoint (line 13 .. 15).
  • AuditSourceIdentification: Information related to the primary system performing the query (line 16 .. 18)
  • ParticipantObjectIdentification: Information on the patients EPR accessed (line 19 .. 22)
  • ParticipantObjectIdentification: Request message related information including a BASE-64 encoded copy of the query (line 23 .. 27).

Security Requirements

To ensure privacy the transaction must be secured using https with mutual authentication, with X.509 certificates (extended validation required) and client and server side certificate validation.

Note

  • Some test environments dropped the mutual authentication or TLS for testing purposes. Please contact your test system provider on the details.

Test Opportunity

The transaction can be tested with the test suite of the EPR reference environment, test systems of the EPR communities or the EPR Playground.


Last update: 2025-01-27
\ No newline at end of file diff --git a/ProvideAndRegister/index.html b/ProvideAndRegister/index.html new file mode 100644 index 0000000..e2e8285 --- /dev/null +++ b/ProvideAndRegister/index.html @@ -0,0 +1,272 @@ + Provide and Register Document Set - EPR by example
Skip to content

Provide and Register Document Set

Transaction to store one or more documents to a community. Primary systems shall use this transaction to export documents and the to a community repository to add it to a patients EPR.

Overview

Primary systems shall use this transaction to provide documents and the related document metadata to a patient EPR. In the Swiss EPR the IHE XDS.b profile and transactions shall be used.

To store the document metadata of the document, the primary system shall perform a Provide And Register Document Set [ITI-41] transaction. Within the request, the primary systems shall provide the master patient ID as retrieved from the PIX Query, the document metadata as defined in the ordinances of the Swiss EPR and the binary data of the document.

The community responds with a code indicating the successful registration of the document.

Transaction

Message Semantics

Messages are encoded as described in the ebXML standard with restrictions defined in the IHE profile and the ordinances to the Swiss EPR.

Request Message

Since the ebXML standard is very generic, the request message is quite lengthy and needs some background information to interpret.

The structure of the result set is as follows (see example below):

  • The metadata of the individual documents are bundled in a ExtrinsicObject element.
  • The metadata attributes are encoded as Slot, as Classification or as ExternalIdentifier elements.
  • Metadata attributes encoded as Slots can be identified and interpreted by the slot's name attribute.
  • Metadata attributes encoded as Classification can be identified and interpreted by the classification's classificationScheme attribute.
  • The unique ID of the document is encoded as ExternalIdentifier, which has an identificationScheme attribute with a fixed value.

The table of the identifier used to indicate the metadata attributes is defined by the metadata model used by IHE XDS.b in IHE ITI Technical Framework Vol. 3, Section 4.2.5.1 and IHE ITI Technical Framework Vol. 3, Section 4.2.5.2 .

The corresponding interpretation of the metadata attributes in the Swiss EPR and the supported value sets may be found in Annex 3 of the ordinances of the Swiss electronic patient dossier.

A request message is quite lengthy. A listing with abrevations used in the step by step interpretation below is found here. The raw version of the request message may be found here.

Message Interpretation

The request message is not very complex, but lengthy due to the genericity of the ebXML standard. Therefore the following step by step interpretation may be of help to interpret the response.

The SOAP Header element conveys the following information:

  • To element: The URL of the provide and register document set service.
  • MessageID element: a UUID of the message.
  • Action element: The SOAP action identifier of the request as defined in the IHE ITI Technical Framework.
  • Security element: The Web Service Security header as defined in the WS Security specification. This element conveys the XUA Assertion used for authorization (see Provide X-User Assertion).
SOAP header
28
+29
+30
+31
+32
+33
+34
+35
+36
    <soap:Header>
+      <Action soap:mustUnderstand="true"
+        xmlns="http://www.w3.org/2005/08/addressing">urn:ihe:iti:2007:ProvideAndRegisterDocumentSet-b</Action>
+      <MessageID xmlns="http://www.w3.org/2005/08/addressing">urn:uuid:073be420-d838-47c9-b35f-c59af5b147a2</MessageID>
+      <To xmlns="http://www.w3.org/2005/08/addressing">http://ehealthsuisse.ihe-europe.net:8280/xdstools7/sim/epr-testing__for_init_gw_testing/rep/prb</To>
+      <ReplyTo xmlns="http://www.w3.org/2005/08/addressing">
+        <Address>http://www.w3.org/2005/08/addressing/anonymous</Address>
+      </ReplyTo>
+          <wsse:Security>
+

The SOAP Body element conveys the following objects in ebXML syntax:

  • RegistryPackage defining the submission set and its metadata.
  • ExtrinsicObject defining the document metadata (matches the document metadata interpretation in Registry Stored Query).
  • Association linking the document metadata to the submission set.

We will explain the RegistryPackage object defining the submission set first. For the other elements, see below.

Submission Set

The structure of the RegistryPackage object defining the submission set is as follows (see example below):

  • The metadata attributes are encoded as Slot, as Classification or as ExternalIdentifier elements.
  • Metadata attributes encoded as Slots can be identified and interpreted by the slot's name attribute.
  • Metadata attributes encoded as Classification can be identified and interpreted by the classification's classificationScheme attribute.
  • The unique ID of the document is encoded as ExternalIdentifier, which has an identificationScheme attribute with a fixed value.

The RegistryPackage object defining the submission set has one Slot child elements with name submissionTime which conveys the request timestamp, and a Name element to convey the display name of the submission set (see lines 17 to 25 below).

RegistryPackage element
            <RegistryPackage id="urn:uuid:a459a58b-1c47-4b43-b7db-82eb1b340168">
+              <Slot name="submissionTime">
+                <ValueList>
+                  <Value>20231219102116</Value>
+                </ValueList>
+              </Slot>
+

The RegistryPackage object defining the submission set has three Classification child elements conveying the submission set metadata:

  • Content Type Code: The submission set content type code attribute, indicated by the value of the classificationScheme equal to urn:uuid:aa543740-bdda-424e-8c96-df4873be8500. The value conveyed with the nodeRepresentation attribute and the codingScheme value must match one of the supported values in the Swiss EPR as defined in Annex 3.
  • submission author: The submission set author element, indicated by the value of the classificationScheme equal to urn:uuid:a7058bb9-b4e4-4307-ba5b-e3f0ab85e12d. The author element is optional in the EPR. If present, it shall convey the information on the person, which initiated the request.
  • submission set identifier: An element with classification scheme urn:uuid:a54d6aa5-d40d-43f9-88c5-b4633d873bdd required to identify the RegistryPackage object as a XDS.b submission set.
RegistryPackage element
              <Classification classificationScheme="urn:uuid:a7058bb9-b4e4-4307-ba5b-e3f0ab85e12d" classifiedObject="urn:uuid:a459a58b-1c47-4b43-b7db-82eb1b340168" nodeRepresentation="" id="urn:uuid:6c2e8937-3650-4f7b-a98d-b1739c0798e1">
+                <Slot name="authorPerson">
+                  <ValueList>
+                    <Value>^Max^Mustermann^^^Dr.Med</Value>
+                  </ValueList>
+                </Slot>
+                <Slot name="authorRole">
+                  <ValueList>
+                    <Value>HCP^^^&amp;2.16.756.5.30.1.127.3.10.6&amp;ISO</Value>
+                  </ValueList>
+                </Slot>
+                <Slot name="authorSpecialty">
+                  <ValueList>
+                    <Value>1050^^^&amp;2.16.756.5.30.1.127.3.5&amp;ISO</Value>
+                  </ValueList>
+                </Slot>
+              </Classification>
+              <Classification classificationScheme="urn:uuid:aa543740-bdda-424e-8c96-df4873be8500" classifiedObject="urn:uuid:a459a58b-1c47-4b43-b7db-82eb1b340168" nodeRepresentation="71388002" id="urn:uuid:3e3b7544-d7d8-498b-9bc1-6f16cce2ad38">
+                <Slot name="codingScheme">
+                  <ValueList>
+                    <Value>2.16.840.1.113883.6.96</Value>
+                  </ValueList>
+                </Slot>
+                <Name>
+                  <LocalizedString xml:lang="en-US" charset="UTF-8" value="Procedure (procedure)"/>
+                </Name>
+              </Classification>
+

The RegistryPackage object defining the submission set has three ExternalIdentifier child elements:

  • XDSSubmissionSet.sourceId: Conveys the OID of the primary system performing the request.
  • XDSSubmissionSet.uniqueId: Conveys a UUID of the submission set.
  • XDSSubmissionSet.patientId: The master patient ID (XAD-PID) of the patient in CX format (see PIX Feed).
RegistryPackage element
              <ExternalIdentifier registryObject="urn:uuid:a459a58b-1c47-4b43-b7db-82eb1b340168" identificationScheme="urn:uuid:6b5aea1a-874d-4603-a4bc-96a0a7b38446" value="CHPAM3946^^^&amp;1.3.6.1.4.1.12559.11.20.1&amp;ISO" id="urn:uuid:b155cad3-b415-47a7-854f-0d8a0a29cfd2">
+                <Name>
+                  <LocalizedString value="XDSSubmissionSet.patientId"/>
+                </Name>
+              </ExternalIdentifier>
+              <ExternalIdentifier registryObject="urn:uuid:a459a58b-1c47-4b43-b7db-82eb1b340168" identificationScheme="urn:uuid:96fdda7c-d067-4183-912e-bf5ee74998a8" value="2.25.194301908197721326796925171598754063498" id="urn:uuid:b19fdbbd-f545-43e6-82c1-2b7b60560635">
+                <Name>
+                  <LocalizedString value="XDSSubmissionSet.uniqueId"/>
+                </Name>
+              </ExternalIdentifier>
+              <ExternalIdentifier registryObject="urn:uuid:a459a58b-1c47-4b43-b7db-82eb1b340168" identificationScheme="urn:uuid:554ac39e-e3fe-47fe-b233-965d2a147832" value="2.16.756.5.30.1.139.1.1.11" id="urn:uuid:c85aeade-175b-47ac-803e-efe2d1556895">
+                <Name>
+                  <LocalizedString value="XDSSubmissionSet.sourceId"/>
+                </Name>
+              </ExternalIdentifier>
+
Document Metadata

The request contains 1..N ExtrinsicObject representing the document metadata for each document. The interpretation of the document metadata matches the document metadata interpretation, which is explained in detail in step by step example in the Registry Stored Query page and will not be reproduced here. Please see Registry Stored Query for the interpretation of the document metadata.

Association

The request contains one Association object linking the document and document metadata to a submission set defined in the RegistryPackage (see Submission Set).

The Association object thus conveys two parameter to link the objects:

  • sourceObject: The attribute value must match the id attribute of the submission set RegistryPackage.
  • targetObject: The attribute value must match the id attribute value of the document metadata ExtrinsicObject.

In addition the Association object conveys a status indicator, which must take the value Original (see snippet below).

Association element
            <Association associationType="urn:oasis:names:tc:ebxml-regrep:AssociationType:HasMember" sourceObject="urn:uuid:a459a58b-1c47-4b43-b7db-82eb1b340168" targetObject="urn:uuid:af516d8d-c449-4a8b-bbb4-9e36489d474d" id="urn:uuid:9aabf9f9-1a16-47d8-b280-281edd2fc3fc">
+              <Slot name="SubmissionSetStatus">
+                <ValueList>
+                  <Value>Original</Value>
+                </ValueList>
+              </Slot>
+            </Association>
+

Response Message

The provide and register service responds with a message indicating the success of the transaction. The outcome indicator is encoded in the Body element of the SOAP envelope as follows:

<ns2:RegistryResponse xmlns="namespace omitted" status="urn:oasis:names:tc:ebxml-regrep:ResponseStatusType:Success"/>
+

The raw version of a response message may be found here.

Transport Protocol

The system shall send the request messages to the repository service of the community using the MIME Multipart/Related binding as specified in the SOAP MTOM specification of the W3C.

The request in MTOM format may look as follows:

 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
POST /XDSDocumentRepositoryService HTTP/1.1
+Host: 10.2.101.10:11076
+Content-Type: multipart/related; boundary="MIMEBoundary_05b39a33e8effeb90c1ccb1c58c4b93b5af2935a13853149"; type="application/xop+xml"; start="<0.15b39a33e8effeb90c1ccb1c58c4b93b5af2935a13853149@apache.org>"; start-info="application/soap+xml"; action="urn:ihe:iti:2007:ProvideAndRegisterDocumentSet-b"
+Connection: Keep-Alive
+Content-Length: 181931
+
+--MIMEBoundary_05b39a33e8effeb90c1ccb1c58c4b93b5af2935a13853149
+Content-Type: application/xop+xml; charset=utf-8; type="application/soap+xml"
+Content-Transfer-Encoding: binary
+Content-ID: <0.15b39a33e8effeb90c1ccb1c58c4b93b5af2935a13853149@apache.org>
+
+<!-- message omittedd -->
+
+--MIMEBoundary_05b39a33e8effeb90c1ccb1c58c4b93b5af2935a13853149
+Content-Type: application/octet-stream
+Content-Transfer-Encoding: binary
+Content-ID: <1.c5b39a33e8effeb94a97121c58c4b93b53d2935a13853149@apache.org>
+
+<!-- binary document data omitted -->
+
+--MIMEBoundary_05b39a33e8effeb90c1ccb1c58c4b93b5af2935a13853149--
+

The provide and register service sends the response message in the MIME Multipart/Related binding as specified in the SOAP MTOM specification of the W3C.

The response in MTOM format may look as follows:

 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
DefaultHttpResponse(chunked: false)
+HTTP/1.1 200 OK
+Connection: keep-alive
+Content-Type: multipart/related; type="application/xop+xml"; boundary="uuid:2a1acced-0234-4a54-8dd3-b9f9b753169c"; start="<root.message@cxf.apache.org>"; start-info="application/soap+xml"
+Date: Thu, 24 Sep 2020 15:43:11 GMT
+Content-Length: 1136
+
+--uuid:2a1acced-0234-4a54-8dd3-b9f9b753169c
+Content-Type: application/xop+xml; charset=UTF-8; type="application/soap+xml"
+Content-Transfer-Encoding: binary
+Content-ID: <root.message@cxf.apache.org>
+
+<?xml version='1.0' encoding='utf-8'?>
+<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
+ <soap:Header>
+  <Action xmlns="http://www.w3.org/2005/08/addressing" soap:mustUnderstand="true">urn:ihe:iti:2007:ProvideAndRegisterDocumentSet-bResponse</Action>
+  <MessageID xmlns="http://www.w3.org/2005/08/addressing">urn:uuid:3dbfa2b7-3cd6-46b3-b642-9569bb3b43fb</MessageID>
+  <To xmlns="http://www.w3.org/2005/08/addressing">http://www.w3.org/2005/08/addressing/anonymous</To>
+  <RelatesTo xmlns="http://www.w3.org/2005/08/addressing">d22ebb69-8368-4eb6-929b-b382f1b37c72</RelatesTo>
+ </soap:Header>
+ <soap:Body>
+  <ns2:RegistryResponse
+  xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0"
+  xmlns:ns2="urn:oasis:names:tc:ebxml-regrep:xsd:rs:3.0"
+  xmlns:ns3="urn:oasis:names:tc:ebxml-regrep:xsd:lcm:3.0"
+  xmlns:ns4="urn:ihe:iti:xds-b:2007"
+  status="urn:oasis:names:tc:ebxml-regrep:ResponseStatusType:Success"/>
+ </soap:Body>
+</soap:Envelope>
+--uuid:2a1acced-0234-4a54-8dd3-b9f9b753169c--
+

Audit Log

Primary systems shall store syslog messages to the audit record repository of the community using TLS transport protocol. The audit message uses XML formatting as specified in RFC 3881 with restrictions specified in the IHE ITI TF and the Extension 1 to Annex5 in the ordinances of the Swiss electronic patient record (see Section 1.5 "Requirements on ATNA").

The following snippet shows an example audit message to be written by the primary system:

iti-41-log.xml
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
<?xml version="1.0"?>
+<AuditMessage>
+ <EventIdentification EventActionCode="R" EventDateTime="2020-11-17T18:39:39+01:00" EventOutcomeIndicator="0">
+  <EventID csd-code="110106" originalText="Export" codeSystemName="DCM"/>
+  <EventTypeCode csd-code="ITI-41" originalText="Provide and Register Document Set-b" codeSystemName="IHE Transactions"/>
+ </EventIdentification>
+ <ActiveParticipant UserID="pma@gnt.com" UserName="JD&lt;pma@gnt.com&gt;"/>
+ <ActiveParticipant UserID="2000000090108" UserName="Dr. med. John Doe" UserIsRequestor="true">
+  <RoleIDCode csd-code="HCP" codeSystemName="2.16.756.5.30.1.127.3.10.6" originalText="Healthcare professional"/>
+ </ActiveParticipant>
+ <ActiveParticipant UserID="https://repositoryService.com" AlternativeUserID="1" UserIsRequestor="false" NetworkAccessPointID="172.18.0.49" NetworkAccessPointTypeCode="2">
+  <RoleIDCode csd-code="110153" codeSystemName="DCM" originalText="Source Role ID"/>
+ </ActiveParticipant>
+ <ActiveParticipant UserID="https://primarySystem.com" AlternativeUserID="UNKNOWN" UserIsRequestor="true" NetworkAccessPointID="hcohcdemo01-app06-icwpxs01.net.swisscom-health.it" NetworkAccessPointTypeCode="1">
+  <RoleIDCode csd-code="110152" codeSystemName="DCM" originalText="Destination Role ID"/>
+ </ActiveParticipant>
+ <AuditSourceIdentification code="1" AuditSourceID="connectathon"/>
+ <ParticipantObjectIdentification ParticipantObjectID="752343^^^&amp;2.16.840.1.113883.3.37.4.1.1.2.1.1&amp;ISO" ParticipantObjectTypeCode="1" ParticipantObjectTypeCodeRole="1">
+  <ParticipantObjectIDTypeCode csd-code="2" originalText="Patient Number" codeSystemName="RFC-3881"/>
+ </ParticipantObjectIdentification>
+ <ParticipantObjectIdentification ParticipantObjectID="urn:uuid:6b948daf-ab4a-4d51-a1a4-e9f4b2e05ff7" ParticipantObjectTypeCode="2" ParticipantObjectTypeCodeRole="20">
+  <ParticipantObjectIDTypeCode csd-code="urn:uuid:a54d6aa5-d40d-43f9-88c5-b4633d873bdd" originalText="submission set classificationNode" codeSystemName="IHE XDS Metadata"/>
+ </ParticipantObjectIdentification>
+</AuditMessage>
+

The message is made of the following blocks:

  • EventIdentification: Element with event related information including the timestamp.
  • ActiveParticipant: Information related to the community repository which is the source of the documents.
  • ActiveParticipant: Information on the user as required by the IHE XUA profile.
  • ActiveParticipant: Element with information on the authenticated user initiating the request.
  • ActiveParticipant: Element with information on the primary system performing which is the destination of the documents.
  • ParticipantObjectIdentification: Element conveying the master patient ID (XAD-PID) in CX format (see PIX Feed).
  • ParticipantObjectIdentification: Element with request message related information.

TODO Update with gazelle example

Security Requirements

To ensure privacy the transaction must be secured using https with mutual authentication, with X.509 certificates (extended validation required) and client and server side certificate validation.

To enable authorization, the transaction must convey the XUA Assertion for authorization in the security header of the SOAP envelope. See Provide X-User Assertion for the implementation details.

Note

  • Some test environments dropped the mutual authentication or TLS for testing purposes. Please contact your test system provider on the details.
  • Some test environments may also drop authorization for testing purposes. Please contact your test system provider on the details.

Test Opportunity

The transaction can be tested with the test suite of the EPR reference environment, test systems of the EPR communities or the EPR Playground.


Last update: 2025-01-27
\ No newline at end of file diff --git a/ProvideXAssertion/index.html b/ProvideXAssertion/index.html new file mode 100644 index 0000000..1caf310 --- /dev/null +++ b/ProvideXAssertion/index.html @@ -0,0 +1,32 @@ + Provide X-User Assertion - EPR by example
Skip to content

Provide X-User Assertion

Method to provide a SAML 2.0 assertion in the Web Service Security header to authorize transactions. Primary systems shall use this transaction to provide a SAML Assertion to authorize transactions.

Overview

Primary systems shall use this transaction to provide SAML 2 assertions retrieved by the Get X-User Assertion with XDS.b transactions as defined in the IHE XUA profile with Swiss specific extensions defined in
Amendment 1 to Annex 5.

Transaction

This transaction is not used standalone and shall be used in conjunction with other transactions which require authorization. These are:

Message Semantics

Primary systems shall use a Get X-User Assertion transaction to retrieve XUA SAML Assertion for authorization, before performing transactions which require authorization. The XUA SAML Assertion for authorization shall be added to the Security header of the SOAP envelope used for the transaction, which requires authorization.

The following snippet shows an abbreviated example message with a SAML Assertion:

 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
<?xml version="1.0" encoding="UTF-8"?>
+<soap:Envelope xmlns=" !-- namespaces omitted -- ">
+ <soap:Header>
+   <wsa:To soap:mustUnderstand="1"> <!-- id of transaction used in conjunction --></wsa:To>
+   <wsa:MessageID soap:mustUnderstand="1">urn:uuid:31D7E4B5-C117-481E-9EE1-F32849E81BF8</wsa:MessageID>
+   <wsa:Action soap:mustUnderstand="1">urn:ihe:iti:2007:RegistryStoredQuery</wsa:Action>
+   <wsse:Security>
+     <saml2:Assertion>
+       <!-- assertion content omitted for brevity -->
+     </saml2:Assertion>
+   </wsse:Security>
+ </soap:Header>
+ <soap:Body>
+  <!-- body elements of transaction used in conjunction omitted for brevity -->
+ </soap:Body>
+</soap:Envelope>
+

For the details on the Assertion content, please see the step by step example in Get X-User Assertion .

Audit Log

This transaction does not require separate ATNA audit log messages, but adds requirements to the transactions used with, as described in section 1.6.4.3.5 of Amendment 1 to Annex 5.

Test Opportunity

The transaction can be tested with the test suite of the EPR reference environment or test systems of the EPR communities.


Last update: 2025-01-27
\ No newline at end of file diff --git a/Questionnaire/index.html b/Questionnaire/index.html new file mode 100644 index 0000000..9b41211 --- /dev/null +++ b/Questionnaire/index.html @@ -0,0 +1,852 @@ + How to build a FHIR-Questionnaire - EPR by example
Skip to content

How to build a FHIR-Questionnaire

This guide aims to explain how to build a FHIR Questionnaire, and in particular how to use the extensions necessary to make full use of the @i4mi/fhir-questionnaire-renderer library. You can use the Questionnaire Prototype from the mHealth prototype project to render the questionnaire.

Minimal Example

Your are free to set whatever fields you want inside your Questionnaire resource (note that the status field is mandatory), however only the following fields will be rendered by the Questionnaire Renderer:

  • title the title of the questionnaire
  • description the description as a markdown string
  • item the array that contains the questions
Minimal example
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
{
+  "resourceType": "Questionnaire",
+  "status": "draft",
+  "title": "Basic Data Questionnaire",
+  "description": "# How it works?\n\n Simply enter your date of birth and your name and press save!",
+  "item": [
+    {
+      "linkId": "Q1",
+      "type": "dateTime",
+      "required": true,
+      "text": "Date of Birth"
+    },
+    {
+      "linkId": "Q2",
+      "type": "boolean",
+      "required": true,
+      "text": "Vaccinated against polio"
+    }
+  ]
+}
+

For a more detailed and commented example, see the last chapter of this guide.

Adding a question

To add a question, add an object inside the item array. Every question should have a unique linkId and a type. The rendering library supports the following subset of the questionnaire item types:

  • group
  • display
  • boolean
  • decimal
  • integer
  • date
  • datetime
  • time
  • string
  • text
  • url

Required questions

To specify if a questionnaire item is required to be answered, set the field required to true.

For details, please see the official documentation for enableWhen.

Answer options for choice questions

For choice questions, you can specify the potential answer options in the answerOptions field. Usually, it makes the most sense to use the valueCoding type here, but in this documentation we chose valueString for keeping it simpler.

Choice question
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
{
+  "linkId": "Q4",
+  "type": "choice",
+  "text": "Eye color",
+  "repeats": false,
+  "answerOptions": [
+    {
+      "valueString": "green"
+    },
+    {
+      "valueString": "brown"
+    },
+    {
+      "valueString": "blue"
+    },
+    {
+      "valueString": "other"
+    }
+  ]
+}
+

The repeats field declares if a choice question can have multiple answers or not. Set it to true to have a multiple choice question.

As an alternative to answerOptions, you can link to a ValueSet in the answerValueSet field with an # and the id of the contained value-set. The valueSet resource must be in the contained of the Questionnaire, external ValueSets are not supported as of now.

Linking a ValueSet
1
+2
+3
+4
{
+  ...,
+  "answerValueSet": "#ORGAN-DONATION-LIST-ValueSet"
+}
+

Subquestions

In a FHIR questionnaire, it is possible to have subquestions for a question. Just add the subquestion in the item field of the parent question. Of course, a parent item can have multiple subquestions, and the subquestions themselves can have subquestions again. This is rendered in a hierarchical way by the questionnaire renderer. If you want to display the subquestions depending from the answer given to the parent question, you have to declare them as depending questions (see next chapter).

Sub-question
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
{
+  "linkId": "parent",
+  "type": "boolean",
+  "text": "Do you have children?",
+  "item": [
+    {
+      "linkId": "subQuestion",
+      "type": "integer",
+      "text": "How many children do you have?"
+    }
+  ]
+}
+

Depending questions

You can also dynamically enable or not some questions, depending on the status of other questions.

Depending question
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
{
+  "linkId": "Q3",
+  "type": "date",
+  "text": "Date of vaccination",
+  "enableWhen": [
+    {
+      "question": "Q2",
+      "operator": "=",
+      "answerBoolean": true
+    }
+  ]
+}
+

Initial values

You can also define an initial answer, that is preset to a question and can be changed by the user.

Initial value
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
{
+  "linkId": "initial",
+  "type": "boolean",
+  "text": "Do you want to receive our amazing newsletter?",
+  "initial": [
+    {
+      "valueBoolean": true
+    }
+  ]
+}
+

Questionnaire Item UI Control Codes

It is possible to specify how questions should be rendered by adding an extension to an element or to its child (see next chapters for more information).

Currently only the Help-Button, Prompt and Unit codes are supported. To see which items do support these codes, please refer on documentation of fhir-questionnaire-renderer.

For choice questions you can specify if the question should be rendered with radio control (one answer) or with checkboxes (multiple answer allowed) by adding an extension directly to the question. If you do not use this extension on choice questions, they will be rendered as dropdown control and the repeats will be used to determine if it is or not a multiple answers question.

Help Button

If you want to add explanatory/help text to a question, you can do so by adding an item with display element with the help code and questionnaire-itemControl extension as described below.

The help element will take the form of an icon with a question mark which will display the text when hovering over it on a computer or pressing it on a mobile device.

Help button
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
{
+  "linkId": "Q1-with-help",
+  "text": "Where did you put your advanced directives?",
+  "type": "text",
+  "item": [
+    {
+      "extension": [
+        {
+          "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl",
+          "valueCodeableConcept": {
+            "coding": [
+              {
+                "system": "http://hl7.org/fhir/questionnaire-item-control",
+                "code": "help",
+                "display": "Help-Button"
+              }
+            ],
+            "text": "Help-Button"
+          }
+        }
+      ],
+      "linkId": "H-Q1",
+      "text": "Please, be precise e.g ”In the middle drawer of my desk at my house.”",
+      "type": "display"
+    }
+  ]
+}
+

Prompt

If you want to add prompt/placeholder to an input question, you can do so by adding an item with display element with the prompt code and questionnaire-itemControl extension as described below.

The prompt element will take the form of a text inside an input to give information about what should be entered.

Prompt
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
{
+  "extension": [
+    {
+      "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl",
+      "valueCodeableConcept": {
+        "coding": [
+          {
+            "system": "http://hl7.org/fhir/questionnaire-item-control",
+            "code": "prompt",
+            "display": "Prompt"
+          }
+        ],
+        "text": "Prompt"
+      }
+    }
+  ],
+  "linkId": "P-Q1",
+  "text": "Your advanced directive is ...",
+  "type": "display"
+}
+

Unit

If you want to add unit to an input question, you can do so by adding an item with display element with the unit code and questionnaire-itemControl extension as described below.

The unit element will be displayed at the end of the input field as an indication of the unit of what the user should enter. Please not that the unit is only cosmetic, if you want to have an answer corresponding to an input with its unit, you should use a quantity item, but its not supported by this project.

Unit
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
{
+  "extension": [
+    {
+      "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl",
+      "valueCodeableConcept": {
+        "coding": [
+          {
+            "system": "http://hl7.org/fhir/questionnaire-item-control",
+            "code": "unit",
+            "display": "Unit"
+          }
+        ],
+        "text": "Unit"
+      }
+    }
+  ],
+  "linkId": "U-Q1",
+  "text": "m / s ^ 2",
+  "type": "display"
+}
+

Check-box

If you want to render choice question with checkbox control, you can do so by adding the check-box code and questionnaire-itemControl extension as described below.

Checkbox
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
{
+  "extension": [
+    {
+      "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl",
+      "valueCodeableConcept": {
+        "coding": [
+          {
+            "system": "http://hl7.org/fhir/questionnaire-item-control",
+            "code": "check-box",
+            "display": "Check-box"
+          }
+        ],
+        "text": "Check-box"
+      }
+    }
+  ],
+  "linkId": "Q1-",
+  "type": "choice",
+  "required": true,
+  "repeats": true,
+  "text": "I CONSENT to the removal of the following organs, tissues or cells, and to the associated preparatory medical measures:",
+  "answerValueSet": "#ORGAN-DONATION-LIST-ValueSet"
+}
+

Radio Button

If you want to render choice question with radio control, you can do so by adding the radio-button code and questionnaire-itemControl extension as described below.

Radio button
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
{
+      "extension": [
+        {
+          "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl",
+          "valueCodeableConcept": {
+            "coding": [
+              {
+                "system": "http://hl7.org/fhir/questionnaire-item-control",
+                "code": "radio-button",
+                "display": "Radio Button"
+              }
+            ],
+            "text": "Radio Button"
+          }
+        }
+      ],
+      "linkId": "Q1",
+      "text": "In the event that removal of organs, tissues or cells is possible after my death, my wishes are as follows:",
+      "type": "choice",
+      "required": true,
+      "repeats": false,
+      "answerValueSet": "#ORGAN-DONATION-RESPONSE-ValueSet",
+

Internationalisation

To add internationalisation to a questionnaire, use the FHIR extension translation. The following is applicable for most text elements of a questionnaire (text, title, description, ...). Please note, that for choice questions with answerOptions, internationalisation is not supported. In this case, you need to use an answerValueSet with designation.

Internationalisation
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
{
+  "title": "Digital Organ Donation Card",
+  "_title": {
+    "extension": [
+      {
+        "url": "http://hl7.org/fhir/StructureDefinition/translation",
+        "extension": [
+          {
+            "url": "lang",
+            "valueCode": "de"
+          },
+          {
+            "url": "content",
+            "valueString": "Digitale Organspende-Karte"
+          }
+        ]
+      },
+      {
+        "url": "http://hl7.org/fhir/StructureDefinition/translation",
+        "extension": [
+          {
+            "url": "lang",
+            "valueCode": "fr"
+          },
+          {
+            "url": "content",
+            "valueString": "Carte numérique de don d'organes"
+          }
+        ]
+      },
+      {
+        "url": "http://hl7.org/fhir/StructureDefinition/translation",
+        "extension": [
+          {
+            "url": "lang",
+            "valueCode": "it"
+          },
+          {
+            "url": "content",
+            "valueString": "Carta digitale per la donazione di organi"
+          }
+        ]
+      }
+    ]
+  }
+}
+

The @i4mi/fhir_questionnaire library will process the extension, see the documentation for more information.

Prepopulation

Besides the initial value (see above), it is also possible to prepopulate the answers to the questions, using another FHIR resource with the information contained. The difference between initial values and prepopulation is that, with initial values, the preset answers are the same for every user, while with prepopulation, the answers can be set dynamically for every user, depending on the resource that is passed to the Questionnaire library. How the information is to be extracted from the passed resource is described in FHIRPath.

For this, you have to have the initialExpression extension in every item that should be prepopulated, with the FHIRPath expression that extracts the wanted value from the passed resource.

Prepopulation
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
{
+  "linkId": "populateExample",
+  "text": "Birthdate",
+  "type": "date",
+  "extension": [
+    {
+      "url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
+      "valueExpression": {
+        "language": "text/fhirpath",
+        "expression": "%patient.birthDate"
+      }
+    }
+  ]
+}
+

Of course, you also have to pass the resource containing the information to the QuestionnaireData library. You can do this like this:

QuestionnaireData library
1
+2
+3
+4
+5
const fhirQuestionnaire = new QuestionnaireData(myQuestionnaire, languages); // fhirQuestionnaire is the instance of your QuestionnaireData
+fhirQuestionnaire.populate(
+  [myPatientResource], // the resources containing the needed information; you can only provide one resource of every ResourceType (e.g. Patient)
+  true // true if you want to overwrite potentially already existing answers
+);
+

More information on prepopulating are found on the @i4mi/fhir-questionnaire README.

Detailed example with comments

This is a detailled example using many of the concepts explained above. It is based on the organ donation questionnaire used in this app, however some redundancies are stripped out for better readability.

Detailed example
  1
+  2
+  3
+  4
+  5
+  6
+  7
+  8
+  9
+ 10
+ 11
+ 12
+ 13
+ 14
+ 15
+ 16
+ 17
+ 18
+ 19
+ 20
+ 21
+ 22
+ 23
+ 24
+ 25
+ 26
+ 27
+ 28
+ 29
+ 30
+ 31
+ 32
+ 33
+ 34
+ 35
+ 36
+ 37
+ 38
+ 39
+ 40
+ 41
+ 42
+ 43
+ 44
+ 45
+ 46
+ 47
+ 48
+ 49
+ 50
+ 51
+ 52
+ 53
+ 54
+ 55
+ 56
+ 57
+ 58
+ 59
+ 60
+ 61
+ 62
+ 63
+ 64
+ 65
+ 66
+ 67
+ 68
+ 69
+ 70
+ 71
+ 72
+ 73
+ 74
+ 75
+ 76
+ 77
+ 78
+ 79
+ 80
+ 81
+ 82
+ 83
+ 84
+ 85
+ 86
+ 87
+ 88
+ 89
+ 90
+ 91
+ 92
+ 93
+ 94
+ 95
+ 96
+ 97
+ 98
+ 99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
{
+  "resourceType": "Questionnaire",
+  "url": "https://github.com/mHealth-Prototyp/Questionnaires/blob/main/resources/Questionnaire/organ-donation.json",
+  "version": "0.0.1", // the questionnaire needs a version number so the QuestionnaireResponse resource can be matched with the exact version of the questionnaire
+  "name": "OrganDonation", // the technical name of the questionnaire, which is usually not displayed to the user
+  "title": "Digital Organ Donation Card", // the title of the questionnaire as it is displayed to the user
+  "_title": {
+    "extension": [
+      {
+        // for every language translation of the title, we need an extension of the title field, which are placed in the "_title" field
+        "url": "http://hl7.org/fhir/StructureDefinition/translation",
+        "extension": [
+          {
+            "url": "lang",
+            "valueCode": "de"
+          },
+          {
+            "url": "content",
+            "valueString": "Digitale Organspende-Karte"
+          }
+        ]
+      },
+      {
+        "url": "http://hl7.org/fhir/StructureDefinition/translation",
+        "extension": [
+          {
+            "url": "lang",
+            "valueCode": "fr"
+          },
+          {
+            "url": "content",
+            "valueString": "Carte numérique de don d'organes"
+          }
+        ]
+      }
+    ]
+  },
+  "status": "draft", // the status of the questionnaire
+  "date": "2023-03-20", // when the questionnaire was published
+  "description": "**Here's how it works**  \n1. Fill in the online form completely.  \n2. Register it in your EPR.  \n3. Inform your relatives of your wishes and inform them of the presence of this form on your EPD. If you change your mind, fill in the form again, and inform your relatives of this new decision.",
+  "_description": {
+    "extension": [
+      // internationalisation for the description works the same as for the title and is stripped out for better readability
+    ]
+  },
+  "code": [
+    // this code represents the whole questionnaire
+    {
+      "system": "urn:prototype-4-codes", // in lack of a better alternative, we use an own code system
+      "code": "organ-donation",
+      "display": "Organ Donation"
+    }
+  ],
+  "item": [
+    {
+      "linkId": "Dgroup", // a question group is also a question...
+      "type": "group", // ... of type group
+      "item": [
+        // the actual subquestions of the group are in the "item" field
+        {
+          "linkId": "D1", // the unique ID of the question
+          "text": "First and familiy name", // text displayed for the question
+          "_text": {
+            "extension": [
+              // internationalisation for the item texts works the same as for the title and is stripped out for better readability
+            ]
+          },
+          "type": "string", // the name of the user is a string
+          "required": "true", // this question is mandatory
+          "repeats": false, // but only one answer is allowed
+          "readOnly": true, // because we populate it from the Patient resource, in this case the name is read only
+          "extension": [
+            {
+              // with this extension, we can define with a FHIRPath expression, how the value of the question should be prepopulated
+              "url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
+              "valueExpression": {
+                "language": "text/fhirpath",
+                "expression": "%patient.name.given.first() + ' ' + %patient.name.family.first()"
+              }
+            }
+          ]
+        },
+        {
+          "linkId": "D2",
+          "text": "Birthdate",
+          "_text": {
+            "extension": [
+              // internationalisation for the item texts works the same as for the title and is stripped out for better readability
+            ]
+          },
+          "type": "date", // type of birth date is a date
+          "required": "true", // this question is mandatory
+          "repeats": false, // but only one answer is allowed
+          "readOnly": true, // because we populate it from the Patient resource, in this case the name is read only
+          "extension": [
+            {
+              // another example how to prepopulate the answer with the birthdate from the patient resource
+              "url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
+              "valueExpression": {
+                "language": "text/fhirpath",
+                "expression": "%patient.birthDate"
+              }
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "linkId": "Q1",
+      "text": "In the event that removal of organs, tissues or cells is possible after my death, my wishes are as follows:",
+      "_text": {
+        "extension": [
+          // internationalisation for the item texts works the same as for the title and is stripped out for better readability
+        ]
+      },
+      "type": "choice", // this is a choice question
+      "required": true, // that is also required
+      "repeats": false, // but only one answer can be selected
+      "answerValueSet": "#ORGAN-DONATION-RESPONSE-ValueSet", // this refers to the answerValueset, where the answers options are defined
+      // the valueSet can be found in the "contained" field of the Questionnaire resource
+      "item": [
+        // the choice question has some sub-questions
+        {
+          "linkId": "Q1-SQ1",
+          "type": "choice",
+          "required": true,
+          "repeats": true, // other than the parent question, multiple answers are allowed here
+          "text": "I CONSENT to the removal of the following organs, tissues or cells, and to the associated preparatory medical measures:",
+          "_text": {
+            "extension": [
+              // internationalisation for the item texts works the same as for the title and is stripped out for better readability
+            ]
+          },
+          "enableWhen": [
+            {
+              // here we can define when the question should be enabled and thus displayed to the user
+              "question": "Q1", // this declares on which question this question depends
+              "operator": "=", // we want to enable it when the answer to Q1 equals the following answer (could also be not equal, or bigger / smaller for numeric answers)
+              "answerCoding": {
+                // and here we define the answer that must be set in Q1 to enable this question
+                "system": "https://github.com/mHealth-Prototyp/Questionnaires/blob/main/resources/ValueSet/organ-donation-response.json",
+                "code": "yes_following"
+              }
+            }
+          ],
+          "answerValueSet": "#ORGAN-DONATION-LIST-ValueSet" // this refers to the answerValueset, where the answers options are defined
+          // the valueSet can be found in the "contained" field of the Questionnaire resource
+        }
+      ],
+      "extension": [
+        {
+          // this itemControl extensions defines that the answer options should be displayed as radio buttons
+          "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl",
+          "valueCodeableConcept": {
+            "coding": [
+              {
+                "system": "http://hl7.org/fhir/questionnaire-item-control",
+                "code": "radio-button",
+                "display": "Radio Button"
+              }
+            ],
+            "text": "Radio Button"
+          }
+        }
+      ]
+    },
+    {
+      "linkId": "I1",
+      "text": "The information you provide here is only recorded on your EPR.",
+      "type": "display", // the final question is only for displaying the information, and does not take any answers
+      "_text": {
+        "extension": [
+          // internationalisation for the item texts works the same as for the title and is stripped out for better readability
+        ]
+      }
+    }
+  ]
+}
+

Last update: 2025-01-27
\ No newline at end of file diff --git a/RegistryStoredQuery/index.html b/RegistryStoredQuery/index.html new file mode 100644 index 0000000..82a9518 --- /dev/null +++ b/RegistryStoredQuery/index.html @@ -0,0 +1,449 @@ + Registry Stored Query - EPR by example
Skip to content

Registry Stored Query

Transaction to lookup the document metadata for the documents stored in a patient's EPR. Primary systems shall use this transaction to view the metadata of the available documents for to display the data in the UI.

Overview

Primary systems shall use this transaction to retrieve the document metadata for the documents stored in a patients EPR. In the Swiss EPR the IHE XDS.b profile and transactions shall be used to retrieve the document metadata.

To retrieve the document metadata of the document stored in a patients EPR, the primary system shall perform a Registry Stored Query [ITI-18]. The Registry Stored Query [ITI-18] supports various query parameter as search and filter parameter. The most common parameter used in the Swiss EPR are the patient ID in CX format and the status information, which must be Approved.

The community responds with the metadata sets of all documents registered in the patient's EPR, which match the search and filter parameter of the query. The profile is based upon the ebXML standard. Due to the genericity of the ebXML standard, the response is not human readable and needs without background information given below.

Transaction

Message Semantics

Messages are encoded as described in the ebXML standard with restrictions defined in the IHE profile and the ordinances to the Swiss EPR.

Request Message

The following snippet is taken from a sample request recorded during the EPR projectathon in September 2020. Some elements were omitted to increase readability. The raw request file may be found here.

The request message shall be a XML SOAP envelope with the query embedded in the Body element of the SOAP envelope. The SOAP Header element conveys the following information:

  • To element: The URL of the registry stored query service.
  • MessageID element: a UUID of the message.
  • Action element: The SOAP action identifier of the query as defined in the IHE ITI Technical Framework.
  • Security element: The Web Service Security header as defined in the WS Security specification. This element conveys the XUA Assertion used for authorization (see Provide X-User Assertion).

The SOAP Body element conveys the AdhocQuery (lines 15 to 26 below) with the following information:

  • Slot element with name $XDSDocumentEntryStatus: The status filter parameter, which must take the value urn:oasis:names🇹🇨ebxml-regrep:StatusType:Approved (line 18).
  • Slot element with name $XDSDocumentEntryPatientId: The master patient ID (XAD-PID) of the patient in CX format (see PIX Feed) (line 23).
ITI-18_request.xml
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
<soapenv:Envelope xmlns="!-- namespaces ommitted -->">
+ <soapenv:Header>
+  <wsa:To soapenv:mustUnderstand="1">https://epd-test.com/Registry/services/RegistryService</wsa:To>
+  <wsa:MessageID soapenv:mustUnderstand="1">urn:uuid:31D7E4B5-C117-481E-9EE1-F32849E81BF8</wsa:MessageID>
+  <wsa:Action soapenv:mustUnderstand="1">urn:ihe:iti:2007:RegistryStoredQuery</wsa:Action>
+  <wsse:Security>
+   <saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
+    <!-- CH:XUA Assertion omitted -->
+   </saml2:Assertion>
+  </wsse:Security>
+ </soapenv:Header>
+ <soapenv:Body>
+  <ns0:AdhocQueryRequest>
+   <ns0:ResponseOption returnType="ObjectRef" returnComposedObjects="true"/>
+   <rim:AdhocQuery id="urn:uuid:14d4debf-8f97-4251-9a74-a90016b0af0d">
+    <rim:Slot name="$XDSDocumentEntryStatus">
+     <rim:ValueList>
+      <rim:Value>('urn:oasis:names:tc:ebxml-regrep:StatusType:Approved')</rim:Value>
+     </rim:ValueList>
+    </rim:Slot>
+    <rim:Slot name="$XDSDocumentEntryPatientId">
+     <rim:ValueList>
+      <rim:Value>'7e1c6e78-58f1-4a43-ae88-0d5a5c4ab43e^^^&amp;1.3.6.1.4.1.21367.2017.2.5.45&amp;ISO'</rim:Value>
+     </rim:ValueList>
+    </rim:Slot>
+   </rim:AdhocQuery>
+  </ns0:AdhocQueryRequest>
+ </soapenv:Body>
+</soapenv:Envelope>
+

Response Message

TODO add the originalProvider to the response message.

Since the ebXML standard is very generic, the response message is quite lengthy and needs some background information to interpret.

The structure of the result set is as follows (see example below):

  • The metadata of the individual documents are bundled in a ExtrinsicObject element.
  • The metadata attributes are encoded as Slot, as Classification or as ExternalIdentifier elements.
  • Metadata attributes encoded as Slots can be identified and interpreted by the slot's name attribute.
  • Metadata attributes encoded as Classification can be identified and interpreted by the classification's classificationScheme attribute.
  • The unique ID of the document is encoded as ExternalIdentifier, which has an identificationScheme attribute with a fixed value.

The table of the identifier used to indicate the metadata attributes is defined by the metadata model used by IHE XDS.b in IHE ITI Technical Framework Vol. 3, Section 4.2.5.2.

The corresponding interpretation of the metadata attributes in the Swiss EPR and the supported value sets may be found in Annex 3 of the ordinances of the Swiss electronic patient dossier.

A request message is quite lengthy. A listing with abrevations used in the step by step interpretation below is found here. The raw version of the message may be found here.

Message Interpretation

The response message is not very complex, but quite lengthy due to the genericity of the ebXML standard. Therefore the following step by step interpretation may be of help to interpret the response.

The SOAP Header element conveys the following information:

  • Action element: The SOAP action identifier of the response as defined in the IHE ITI Technical Framework.
  • RelatesTo element: The messageID of the query request (see above).
ITI-18_response.xml
2
+3
+4
+5
+6
+7
 <S:Header>
+  <wsa:Action s:mustUnderstand="1"
+   xmlns:s="http://www.w3.org/2003/05/soap-envelope"
+   xmlns:wsa="http://www.w3.org/2005/08/addressing">urn:ihe:iti:2007:RegistryStoredQueryResponse</wsa:Action>
+  <wsa:RelatesTo xmlns:wsa="http://www.w3.org/2005/08/addressing">urn:uuid:a8313b99-aad5-4880-94d2-4b02197cb650</wsa:RelatesTo>
+ </S:Header>
+

The SOAP body element conveys 0..N ExtrinsicObject elements, each conveying the metadata of a single document.

    <ExtrinsicObject mimeType="application/fhir+json" objectType="urn:uuid:7edca82f-054d-47f2-a032-9b2a5b5186c1" id="urn:uuid:1415538d-41bc-41b2-9ae6-8b785f7f3aa6" lid="urn:uuid:1415538d-41bc-41b2-9ae6-8b785f7f3aa6" status="urn:oasis:names:tc:ebxml-regrep:StatusType:Approved" home="urn:oid:1.1.4567334.1.6"
+

The element has fixed attributes defined in the IHE ITI Technical Framework. Beyond these, the ExtrinsicObject conveys the following information for the primary system:

  • mimeType attribute: The document mime type. It's value must match a mime type supported by the Swiss EPR as defined in Annex 3.
  • status attribute: The status of the document, which should be Approved.

As explained above, a subset of the relevant metadata are defined in ebXML slot elements. These are:

14
+15
+16
+17
+18
+19
     <Slot name="repositoryUniqueId"
+      xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+      <ValueList xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+       <Value xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">1.1.4567332.1.75</Value>
+      </ValueList>
+     </Slot>
+
  • repositoryUniqueId: The unique ID of the repository the document is stored. This value must be used when retrieving documents to display (see Retrieve Document Set).
20
+21
+22
+23
+24
+25
     <Slot name="hash"
+      xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+      <ValueList xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+       <Value xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">a72f1824ea1d57af00faf5dd5ccb9aea0b0ce390</Value>
+      </ValueList>
+     </Slot>
+
  • hash: The hash value of the binary.
26
+27
+28
+29
+30
+31
     <Slot name="size"
+      xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+      <ValueList xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+       <Value xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">5375</Value>
+      </ValueList>
+     </Slot>
+
  • size: The size of the binary.
32
+33
+34
+35
+36
+37
     <Slot name="creationTime"
+      xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+      <ValueList xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+       <Value xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">20231219095121</Value>
+      </ValueList>
+     </Slot>
+
  • creationTime: The timestamp the document was uploaded to the patient EPR (or last modified).
38
+39
+40
+41
+42
+43
     <Slot name="languageCode"
+      xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+      <ValueList xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+       <Value xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">en</Value>
+      </ValueList>
+     </Slot>
+
  • languageCode: The coded value of the documents language. It's value must match one code value supported by the Swiss EPR as defined in Annex 3.
44
+45
+46
+47
+48
+49
     <Slot name="sourcePatientId"
+      xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+      <ValueList xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+       <Value xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">CHPAM3946^^^&amp;1.3.6.1.4.1.12559.11.20.1&amp;ISO</Value>
+      </ValueList>
+     </Slot>
+
  • sourcePatientId: The local ID of the patient in the document source system which uploaded the document.
50
+51
+52
+53
+54
+55
     <Slot name="urn:e-health-suisse:2020:originalProviderRole"
+      xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+      <ValueList xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+       <Value xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">HCP^^^&amp;2.16.756.5.30.1.127.3.10.6&amp;ISO</Value>
+      </ValueList>
+     </Slot>
+
  • urn:e-health-suisse:2020:originalProviderRole: The Role of original uploader who uploaded the initial version of the document as defined in Attachment 1 to Annex 5. This attribute is used to track the initial uploader role which shall never be modified by metadat update transactions.
56
+57
+58
+59
     <Name xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+      <LocalizedString charset="UTF-8" value="Vaccination - FSME-Immun 0.25 ml Junior"
+       xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0"/>
+     </Name>
+
  • Name: The document name to display in the UI.
     <VersionInfo versionName="1"
+      xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0"/>
+
  • the version info to track upadated by metadata update transactions and document replacements.

A subset of the relevant metadata are defined in ebXML Classification elements. These are:

62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
     <Classification classificationScheme="urn:uuid:93606bcf-9494-43ec-9b4e-a7748d1a838d" classifiedObject="urn:uuid:1415538d-41bc-41b2-9ae6-8b785f7f3aa6" nodeRepresentation="" id="urn:uuid:e0fcf0a2-f44c-46c5-bc4a-26ac0743f193"
+      xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+      <Slot name="authorPerson"
+       xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+       <ValueList xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+        <Value xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">^Max^Mustermann^^^Dr.Med</Value>
+       </ValueList>
+      </Slot>
+      <Slot name="authorRole"
+       xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+       <ValueList xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+        <Value xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">HCP^^^&amp;2.16.756.5.30.1.127.3.10.6&amp;ISO</Value>
+       </ValueList>
+      </Slot>
+      <Slot name="authorSpecialty"
+       xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+       <ValueList xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+        <Value xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">1050^^^&amp;2.16.756.5.30.1.127.3.5&amp;ISO</Value>
+       </ValueList>
+      </Slot>
+      <VersionInfo versionName="-1"
+       xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0"/>
+     </Classification>
+
85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
     <Classification classificationScheme="urn:uuid:41a5887f-8865-4c09-adf7-e362475b143a" classifiedObject="urn:uuid:1415538d-41bc-41b2-9ae6-8b785f7f3aa6" nodeRepresentation="184216000" id="urn:uuid:b6a1074e-c92e-4648-9f08-f011b6453689"
+      xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+      <Slot name="codingScheme"
+       xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+       <ValueList xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+        <Value xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">2.16.840.1.113883.6.96</Value>
+       </ValueList>
+      </Slot>
+      <Name xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+       <LocalizedString charset="UTF-8" value="Patient record type (record artifact)"
+        xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0"/>
+      </Name>
+      <VersionInfo versionName="-1"
+       xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0"/>
+     </Classification>
+
  • Class Code: The document Class Code metadata attribute, indicated by the value of the classificationScheme equal to urn:uuid:41a5887f-8865-4c09-adf7-e362475b143a as defined in IHE ITI Technical Framework Vol. 3, Section 4.2.5.2. The value conveyed with the nodeRepresentation attribute and the codingScheme value must match one of the supported values in the Swiss EPR as defined in Annex 3.
  • Name : The human readable display name of the document class.
     <Classification classificationScheme="urn:uuid:f33fb8ac-18af-42cc-ae0e-ed0b0bdb91e1" classifiedObject="urn:uuid:1415538d-41bc-41b2-9ae6-8b785f7f3aa6" nodeRepresentation="43741000" id="urn:uuid:84594dc4-814a-495f-94d7-08ee5741bbe3"
+      xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+      <Slot name="codingScheme"
+       xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+       <ValueList xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+        <Value xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">2.16.840.1.113883.6.96</Value>
+       </ValueList>
+      </Slot>
+      <Name xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+       <LocalizedString charset="UTF-8" value="Site of Care (environment)"
+        xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0"/>
+      </Name>
+      <VersionInfo versionName="-1"
+       xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0"/>
+     </Classification>
+
  • Healthcare Facility Type Code: The type of the healthcare facility from which the document was registered. The value conveyed with the nodeRepresentation attribute and the codingScheme value must match one of the supported values in the Swiss EPR as defined in Annex 3.
  • Name : The human readable display name of the healthcare facility type code.
     <Classification classificationScheme="urn:uuid:cccf5598-8b07-4b77-a05e-ae952c785ead" classifiedObject="urn:uuid:1415538d-41bc-41b2-9ae6-8b785f7f3aa6" nodeRepresentation="394802001" id="urn:uuid:56fd7e42-192f-4c6b-a2ca-ad0cb9f93e42"
+      xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+      <Slot name="codingScheme"
+       xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+       <ValueList xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+        <Value xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">2.16.840.1.113883.6.96</Value>
+       </ValueList>
+      </Slot>
+      <Name xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+       <LocalizedString charset="UTF-8" value="General medicine (qualifier value)"
+        xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0"/>
+      </Name>
+      <VersionInfo versionName="-1"
+       xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0"/>
+     </Classification>
+
  • Practice Setting Code: The practice setting code the document is registered with. The value conveyed with the nodeRepresentation attribute and the codingScheme value must match one of the supported values in the Swiss EPR as defined in Annex 3.
  • Name : The human readable display name of the practice setting code.
     <Classification classificationScheme="urn:uuid:f0306f51-975f-434e-a61c-c59651d33983" classifiedObject="urn:uuid:1415538d-41bc-41b2-9ae6-8b785f7f3aa6" nodeRepresentation="41000179103" id="urn:uuid:bb58016b-ec14-498f-9969-a9a8e6b1ab03"
+      xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+      <Slot name="codingScheme"
+       xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+       <ValueList xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+        <Value xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">2.16.840.1.113883.6.96</Value>
+       </ValueList>
+      </Slot>
+      <Name xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+       <LocalizedString charset="UTF-8" value="Immunization Record (record artifact)"
+        xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0"/>
+      </Name>
+      <VersionInfo versionName="-1"
+       xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0"/>
+     </Classification>
+
  • Document Type Code: The type code of the document. The value conveyed with the nodeRepresentation attribute and the codingScheme value must match one of the supported values in the Swiss EPR as defined in Annex 3.
  • Name : The human readable display name of the document type code.
     <Classification classificationScheme="urn:uuid:f4f85eac-e6cb-4883-b524-f2705394840f" classifiedObject="urn:uuid:1415538d-41bc-41b2-9ae6-8b785f7f3aa6" nodeRepresentation="17621005" id="urn:uuid:4819ee10-581a-49f0-b144-6f85be87b38b"
+      xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+      <Slot name="codingScheme"
+       xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+       <ValueList xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+        <Value xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">2.16.840.1.113883.6.96</Value>
+       </ValueList>
+      </Slot>
+      <Name xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+       <LocalizedString xml:lang="en-US" charset="UTF-8" value="Normal"
+        xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0"/>
+      </Name>
+      <VersionInfo versionName="-1"
+       xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0"/>
+     </Classification>
+
  • confidentiality code: The confidentiality of the the document. The value conveyed with the nodeRepresentation attribute and the codingScheme value must match one of the supported values in the Swiss EPR as defined in Annex 3.
  • Name : The human readable display name of the document confidentiality.

A subset of the relevant metadata are defined in ebXML ExternalIdentifier elements. These are:

     <ExternalIdentifier registryObject="urn:uuid:1415538d-41bc-41b2-9ae6-8b785f7f3aa6" identificationScheme="urn:uuid:58a6f841-87b3-4a3e-92fd-a8ffeff98427" value="CHPAM3946^^^&amp;1.3.6.1.4.1.12559.11.20.1&amp;ISO" id="urn:uuid:a019bc95-f172-42cf-af20-38406d848c5b"
+      xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+      <Name xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+       <LocalizedString value="XDSDocumentEntry.patientId"
+        xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0"/>
+      </Name>
+      <VersionInfo versionName="-1"
+       xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0"/>
+     </ExternalIdentifier>
+
  • The master patient ID (XAD-SPID): The value conveyed with the value attribute conveys the master patient ID (XAD-SPID) in the repository. Its value must be used when retrieving documents to display (see Retrieve Document Set).
     <ExternalIdentifier registryObject="urn:uuid:1415538d-41bc-41b2-9ae6-8b785f7f3aa6" identificationScheme="urn:uuid:2e82c1f6-a085-4c72-9da3-8640a32e42ab" value="2.25.306443472873218838784535217290635593269" id="urn:uuid:b37c4f57-e554-43fb-ab34-8f74dfc5376a"
+      xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+      <Name xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0">
+       <LocalizedString value="XDSDocumentEntry.uniqueId"
+        xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0"/>
+      </Name>
+      <VersionInfo versionName="-1"
+       xmlns="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0"/>
+     </ExternalIdentifier>
+
  • The document unique ID, indicated by the value of the identificationScheme equal to urn:uuid:2e82c1f6-a085-4c72-9da3-8640a32e42ab as defined in IHE ITI Technical Framework Vol. 3, Section 4.2.5.2. The value conveyed with the id attribute uniquely identifies the document in the repository. It's value must be used when retrieving documents to display (see Retrieve Document Set).

Transport Protocol

The primary system shall send the request messages to the registry of the community using the http POST binding as defined in the W3C SOAP specification. It may look like:

1
+2
+3
+4
+5
+6
POST /RegistryStoredQueryService HTTP/1.1
+Host: company.example.org
+Accept-Encoding: gzip, deflate
+Connection: Keep-Alive
+Content-Type: application/soap+xml; charset="utf-8"
+Content-Length: nnnn  
+

Audit Log

Primary systems shall store syslog messages to the audit record repository of the community using TLS transport protocol. The audit message uses XML formatting as specified in RFC 3881 with restrictions specified in the IHE ITI TF and the Extension 1 to Annex5 in the ordinances of the Swiss electronic patient record (see Section 1.5 "Requirements on ATNA").

The following snippet shows a example audit message to be written by the primary system:

iti-18-log.xml
55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
<AuditMessage>
+  <EventIdentification EventActionCode="E" EventDateTime="2023-09-11T14:18:27.579+02:00" EventOutcomeIndicator="0">
+    <EventID csd-code="110112" codeSystemName="DCM" originalText="Query" />
+    <EventTypeCode csd-code="ITI-18" codeSystemName="IHE Transactions" originalText="Registry Stored Query" />
+    <PurposeOfUse csd-code="NORM" codeSystemName="2.16.756.5.30.1.127.3.10.5" originalText="Normaler Zugriff" />
+  </EventIdentification>
+  <ActiveParticipant UserID="761337610410035724" UserName="&lt;761337610410035724@http://ith-icoserve.com/eHealthSolutionsSTS&gt;"/>
+  <ActiveParticipant UserID="761337610410035724" UserName="Andreas Friederich TANNER-WELTI" UserIsRequestor="true">
+    <RoleIDCode csd-code="PAT" codeSystemName="2.16.756.5.30.1.127.3.10.6" originalText="Patient" />
+  </ActiveParticipant>
+  <ActiveParticipant UserID="http://www.w3.org/2005/08/addressing/anonymous" AlternativeUserID="20559@epd-test.ith-icoserve.com.ForkJoinPool-5-worker-3" UserIsRequestor="true" NetworkAccessPointID="81.223.215.43" NetworkAccessPointTypeCode="2">
+    <RoleIDCode csd-code="110153" codeSystemName="DCM" originalText="Source" />
+  </ActiveParticipant>
+  <ActiveParticipant UserID="https://localhost:7443/Registry/services/RegistryService" UserIsRequestor="false" NetworkAccessPointID="localhost" NetworkAccessPointTypeCode="1">
+    <RoleIDCode csd-code="110152" codeSystemName="DCM" originalText="Destination" />
+  </ActiveParticipant>
+  <AuditSourceIdentification AuditEnterpriseSiteID="1.3.6.1.4.1.21367.2017.2.6.19" AuditSourceID="1.3.6.1.4.1.12559.11.20.1">
+    <AuditSourceTypeCode csd-code="4" codeSystemName="1.3.6.1.4.1.9784.999200" originalText="ITH icoserve information technology for healthcare sense (tm)" />
+  </AuditSourceIdentification>
+  <ParticipantObjectIdentification ParticipantObjectID="d5e42fed-5962-4bb9-b8b6-5d9e8afb0f2a^^^&amp;1.3.6.1.4.1.21367.2017.2.5.93&amp;ISO" ParticipantObjectTypeCode="1" ParticipantObjectTypeCodeRole="1">
+    <ParticipantObjectIDTypeCode csd-code="2" codeSystemName="RFC-3881" originalText="Patient Number" />
+  </ParticipantObjectIdentification>
+  <ParticipantObjectIdentification ParticipantObjectID="urn:uuid:10b545ea-725c-446d-9b95-8aeb444eddf3" ParticipantObjectTypeCode="2" ParticipantObjectTypeCodeRole="24">
+    <ParticipantObjectIDTypeCode csd-code="ITI-18" codeSystemName="IHE Transactions" originalText="Registry Stored Query" />
+    <ParticipantObjectQuery>PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9InllcyI/PjxBZGhvY1F1ZXJ5UmVxdWVzdCB4bWxucz0idXJuOm9hc2lzOm5hbWVzOnRjOmVieG1sLXJlZ3JlcDp4c2Q6cXVlcnk6My4wIiB4bWxuczpuczY9InVybjpvYXNpczpuYW1lczp0YzplYnhtbC1yZWdyZXA6eHNkOmxjbTozLjAiIHhtbG5zOm5zNT0idXJuOm9hc2lzOm5hbWVzOnRjOmVieG1sLXJlZ3JlcDp4c2Q6cnM6My4wIiB4bWxuczpuczc9InVybjppaGU6aXRpOnJtZDoyMDE3IiB4bWxuczpuczI9InVybjppaGU6aXRpOnhkcy1iOjIwMDciIHhtbG5zOm5zND0idXJuOm9hc2lzOm5hbWVzOnRjOmVieG1sLXJlZ3JlcDp4c2Q6cmltOjMuMCIgeG1sbnM6bnMzPSJ1cm46aWhlOnJhZDp4ZHNpLWI6MjAwOSI+PFJlc3BvbnNlT3B0aW9uIHJldHVyblR5cGU9IkxlYWZDbGFzcyIgcmV0dXJuQ29tcG9zZWRPYmplY3RzPSJ0cnVlIi8+PG5zNDpBZGhvY1F1ZXJ5IGlkPSJ1cm46dXVpZDoxMGI1NDVlYS03MjVjLTQ0NmQtOWI5NS04YWViNDQ0ZWRkZjMiPjxuczQ6U2xvdCBuYW1lPSIkcGF0aWVudElkIj48bnM0OlZhbHVlTGlzdD48bnM0OlZhbHVlPidkNWU0MmZlZC01OTYyLTRiYjktYjhiNi01ZDllOGFmYjBmMmFeXl4mYW1wOzEuMy42LjEuNC4xLjIxMzY3LjIwMTcuMi41LjkzJmFtcDtJU08nPC9uczQ6VmFsdWU+PC9uczQ6VmFsdWVMaXN0PjwvbnM0OlNsb3Q+PG5zNDpTbG90IG5hbWU9IiRYRFNEb2N1bWVudEVudHJ5U3RhdHVzIj48bnM0OlZhbHVlTGlzdD48bnM0OlZhbHVlPigndXJuOm9hc2lzOm5hbWVzOnRjOmVieG1sLXJlZ3JlcDpTdGF0dXNUeXBlOkFwcHJvdmVkJyk8L25zNDpWYWx1ZT48bnM0OlZhbHVlPigndXJuOm9hc2lzOm5hbWVzOnRjOmVieG1sLXJlZ3JlcDpTdGF0dXNUeXBlOkRlcHJlY2F0ZWQnKTwvbnM0OlZhbHVlPjwvbnM0OlZhbHVlTGlzdD48L25zNDpTbG90PjxuczQ6U2xvdCBuYW1lPSIkWERTRm9sZGVyU3RhdHVzIj48bnM0OlZhbHVlTGlzdD48bnM0OlZhbHVlPigndXJuOm9hc2lzOm5hbWVzOnRjOmVieG1sLXJlZ3JlcDpTdGF0dXNUeXBlOkFwcHJvdmVkJyk8L25zNDpWYWx1ZT48L25zNDpWYWx1ZUxpc3Q+PC9uczQ6U2xvdD48bnM0OlNsb3QgbmFtZT0iJFhEU1N1Ym1pc3Npb25TZXRTdGF0dXMiPjxuczQ6VmFsdWVMaXN0PjxuczQ6VmFsdWU+KCd1cm46b2FzaXM6bmFtZXM6dGM6ZWJ4bWwtcmVncmVwOlN0YXR1c1R5cGU6QXBwcm92ZWQnKTwvbnM0OlZhbHVlPjwvbnM0OlZhbHVlTGlzdD48L25zNDpTbG90PjxuczQ6U2xvdCBuYW1lPSIkWERTRG9jdW1lbnRFbnRyeVR5cGUiPjxuczQ6VmFsdWVMaXN0PjxuczQ6VmFsdWU+KCd1cm46dXVpZDozNDI2OGU0Ny1mZGY1LTQxYTYtYmEzMy04MjEzM2M0NjUyNDgnKTwvbnM0OlZhbHVlPjxuczQ6VmFsdWU+KCd1cm46dXVpZDo3ZWRjYTgyZi0wNTRkLTQ3ZjItYTAzMi05YjJhNWI1MTg2YzEnKTwvbnM0OlZhbHVlPjwvbnM0OlZhbHVlTGlzdD48L25zNDpTbG90PjwvbnM0OkFkaG9jUXVlcnk+PC9BZGhvY1F1ZXJ5UmVxdWVzdD4=</ParticipantObjectQuery>
+    <ParticipantObjectDetail type="QueryEncoding" value="VVRGLTg=" />
+    <ParticipantObjectDetail type="urn:ihe:iti:xca:2010:homeCommunityId" value="MS4zLjYuMS40LjEuMjEzNjcuMjAxNy4yLjYuMTk=" />
+  </ParticipantObjectIdentification>
+</AuditMessage>
+

The message is made of the following blocks:

  • EventIdentification: Event related information including the timestamp.
  • ActiveParticipant: Information related to the portal or primary system performing the query.
  • ActiveParticipant: Information on the authenticated user as required by IHE XUA profile.
  • ActiveParticipant: Information on the authenticated user including the user name who initiated the request.
  • ActiveParticipant: Information on the responding service endpoint.
  • AuditSourceIdentification: Information related to the primary system performing the query.
  • ParticipantObjectIdentification: Request message related information including a BASE-64 encoded copy of the query.
  • ParticipantObjectIdentification: Information on the patients EPR accessed.

Security Requirements

To ensure privacy the transaction must be secured using https with mutual authentication, using X.509 certificates (extended validation required) and client and server side certificate validation.

To enable authorization, the transaction must convey the XUA Assertion for authorization in the security header of the SOAP envelope. See Provide X-User Assertion for the implementation details.

Note

  • Some test environments dropped the mutual authentication or TLS for testing purposes. Please contact your test system provider on the details.
  • Some test environments may also drop authorization for testing purposes. Please contact your test system provider on the details.

Test Opportunity

The transaction can be tested with the test suite of the EPR reference environment, test systems of the EPR communities or the EPR Playground.


Last update: 2025-01-27
\ No newline at end of file diff --git a/RetrieveDocumentSet/index.html b/RetrieveDocumentSet/index.html new file mode 100644 index 0000000..a5b919c --- /dev/null +++ b/RetrieveDocumentSet/index.html @@ -0,0 +1,219 @@ + Retrieve Document Set - EPR by example
Skip to content

Retrieve Document Set

Transaction to retrieve one or more documents from a community. Primary systems shall use this transaction to read documents from the EPR and integrate to the primary system or to display the document in the UI.

Overview

Primary systems shall use this transaction to retrieve documents from a patients EPR. In the Swiss EPR the IHE XDS.b profile and transactions shall be used.

To retrieve the document metadata of the document, the the primary system shall perform a Retrieve Document Set [ITI-43] transaction. Within the request, the primary systems shall provide the master patient ID as retrieved from the PIX Query, and the repository as well as the documents unique IDs taken from the response of the Registry Stored Query. In the Swiss EPR currently only supports the synchronous exchange option is supported.

The community responds the set of documents.

Transaction

Message Semantics

Messages are encoded as described in the ebXML standard with restrictions defined in the IHE profile and the ordinances to the Swiss EPR.

Request Message

The following snippet displays a sample request recorded during the EPR projectathon in September 2020, with abrevations to increase readability. The raw request file may be found here.

The request message shall be a XML SOAP envelope with the query embedded in the Body element of the SOAP envelope. The SOAP Header element conveys the following information:

  • To element: The URL of the repository service.
  • MessageID element: a UUID of the message.
  • Action element: The SOAP action identifier of the query as defined in the IHE ITI Technical Framework.
  • Security element: The Web Service Security header as defined in the WS Security specification. This element conveys the XUA Assertion used for authorization (see Provide X-User Assertion).

The SOAP Body element conveys the ebXML RetrieveDocumentSetRequest which shall convey 1..N DocumentRequest elements (lines 12 to 16 below) with the following information:

  • HomeCommunityId : Unique ID of the community.
  • RepositoryUniqueId: Unique ID of repository taken from a Registry Stored Query response.
  • DocumentUniqueId: Unique ID of the document taken from a Registry Stored Query response.
ITI-43_request.xml
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
<?xml version="1.0" encoding="UTF-8"?>
+<soapenv:Envelope xmlns=" !-- namespaces omitted -- ">
+  <soapenv:Header>
+    <wsa:To soapenv:mustUnderstand="1">https://epd-test.com/Repository/services/RepositoryService</wsa:To>
+    <wsa:MessageID soapenv:mustUnderstand="1">urn:uuid:1EB10F67-6562-46D5-9B6B-5DC42EB2B4A6</wsa:MessageID>
+    <wsa:Action soapenv:mustUnderstand="1">urn:ihe:iti:2007:RetrieveDocumentSet</wsa:Action>
+    <wsse:Security>
+      <saml2:Assertion> <!-- XUA assertion omitted --></saml2:Assertion>
+    </wsse:Security>
+  </soapenv:Header>
+  <soapenv:Body>
+    <xsdb:RetrieveDocumentSetRequest>
+      <xsdb:DocumentRequest>
+        <xsdb:HomeCommunityId>urn:oid:1.3.6.1.4.1.21367.2017.2.6.19</xsdb:HomeCommunityId>
+        <xsdb:RepositoryUniqueId>1.3.6.1.4.1.21367.2017.2.3.54</xsdb:RepositoryUniqueId>
+        <xsdb:DocumentUniqueId>1.3.6.1.4.1.21367.2017.2.1.75.20200922130227623</xsdb:DocumentUniqueId>
+      </xsdb:DocumentRequest>
+    </xsdb:RetrieveDocumentSetRequest>
+  </soapenv:Body>
+</soapenv:Envelope>
+

Response Message

The following snippet displays a sample response recorded during the EPR projectathon in September 2020, with abrevations to increase readability. The raw request file may be found here.

The SOAP Header element of the response conveys the following information:

  • Action element: The SOAP action identifier of the response as defined in the IHE ITI Technical Framework.
  • RelatesTo element: The messageID of the request (see above).
ITI-43_response.xml
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
<?xml version='1.0' encoding='utf-8'?>
+<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
+  <soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">
+    <wsa:Action>urn:ihe:iti:2007:RetrieveDocumentSetResponse</wsa:Action>
+    <wsa:RelatesTo>urn:uuid:1EB10F67-6562-46D5-9B6B-5DC42EB2B4A6</wsa:RelatesTo>
+  </soapenv:Header>
+  <soapenv:Body>
+    <ns3:RetrieveDocumentSetResponse xmlns=" !-- namespaces omitted -- ">
+      <ns6:RegistryResponse status="urn:oasis:names:tc:ebxml-regrep:ResponseStatusType:Success"/>
+      <ns3:DocumentResponse>
+        <ns3:HomeCommunityId>urn:oid:1.3.6.1.4.1.21367.2017.2.6.19</ns3:HomeCommunityId>
+        <ns3:RepositoryUniqueId>1.3.6.1.4.1.21367.2017.2.3.54</ns3:RepositoryUniqueId>
+        <ns3:DocumentUniqueId>1.3.6.1.4.1.21367.2017.2.1.75.20200922130227623</ns3:DocumentUniqueId>
+        <ns3:mimeType>application/pdf</ns3:mimeType>
+        <ns3:Document xmlns:xop="http://www.w3.org/2004/08/xop/include">
+          <xop:Include href="cid:72f7c587daaacb8b81212de4e80e442e5f43394482e12edd@apache.org"/>
+        </ns3:Document>
+      </ns3:DocumentResponse>
+    </ns3:RetrieveDocumentSetResponse>
+  </soapenv:Body>
+</soapenv:Envelope>
+

The SOAP Body element conveys the ebXML RetrieveDocumentSetResponse which conveys 1..N DocumentResponse elements (lines 9 to 17 below) with the following information:

  • HomeCommunityId : Unique ID of the community.
  • RepositoryUniqueId: Unique ID of repository.
  • DocumentUniqueId: Unique ID of the document.
  • Document element (line 14 to 16): It's Include element conveys the content-id reference of the attached document in the MTOM response (see below).

Transport Protocol

The system shall send the request messages to the repository service of the community using the MIME Multipart/Related binding as specified in the SOAP MTOM specification of the W3C.

The repository responds the documents using the MIME Multipart/Related binding as specified in the SOAP MTOM specification of the W3C. A full message may look like:

 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
DefaultHttpResponse(chunked: false)
+HTTP/1.1 200 OK
+Connection: Keep-Alive
+Content-Length: nnnn  
+Content-Type: multipart/related; boundary=MIMEBoundary4A7AE55984E7438034;type="application/xop+xml"; start="<0.09BC7F4BE2E4D3EF1B@apache.org>";start-info="text/xml; charset=utf-8"
+
+--MIMEBoundary4A7AE55984E7438034
+content-type: application/xop+xml; charset=utf-8; type="application/soap+xml;"
+content-transfer-encoding: binary
+content-id: <0.09BC7F4BE2E4D3EF1B@apache.org>
+
+<?xml version='1.0' encoding='utf-8'?>
+<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
+  <soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">
+    <wsa:Action>urn:ihe:iti:2007:RetrieveDocumentSetResponse</wsa:Action>
+    <wsa:RelatesTo>urn:uuid:1EB10F67-6562-46D5-9B6B-5DC42EB2B4A6</wsa:RelatesTo>
+  </soapenv:Header>
+  <soapenv:Body>
+    <ns3:RetrieveDocumentSetResponse xmlns=" !-- namespaces omitted -- ">
+      <ns6:RegistryResponse status="urn:oasis:names:tc:ebxml-regrep:ResponseStatusType:Success"/>
+      <ns3:DocumentResponse>
+        <ns3:HomeCommunityId>urn:oid:1.3.6.1.4.1.21367.2017.2.6.19</ns3:HomeCommunityId>
+        <ns3:RepositoryUniqueId>1.3.6.1.4.1.21367.2017.2.3.54</ns3:RepositoryUniqueId>
+        <ns3:DocumentUniqueId>1.3.6.1.4.1.21367.2017.2.1.75.20200922130227623</ns3:DocumentUniqueId>
+        <ns3:mimeType>application/pdf</ns3:mimeType>
+        <ns3:Document xmlns:xop="http://www.w3.org/2004/08/xop/include">
+          <xop:Include href="cid:72f7c587daaacb8b81212de4e80e442e5f43394482e12edd@apache.org"/>
+        </ns3:Document>
+      </ns3:DocumentResponse>
+    </ns3:RetrieveDocumentSetResponse>
+  </soapenv:Body>
+</soapenv:Envelope>
+
+--MIMEBoundary4A7AE55984E7438034
+content-type: application/octet-stream
+content-transfer-encoding: binary
+content-id: <72f7c587daaacb8b81212de4e80e442e5f43394482e12edd@apache.org>
+
+!-- Binary Data omitted --
+
+--MIMEBoundary4A7AE55984E7438034--
+

Audit Log

Primary systems shall store syslog messages to the audit record repository of the community using TLS transport protocol. The audit message uses XML formatting as specified in RFC 3881 with restrictions specified in the IHE ITI TF and the Extension 1 to Annex5 in the ordinances of the Swiss electronic patient record (see Section 1.5 "Requirements on ATNA").

The following snippet shows a example audit message to be written by the primary system:

iti-43-log.xml
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
<?xml version='1.0' encoding='utf-8'?>
+<AuditMessage>
+ <EventIdentification EventActionCode="C" EventDateTime="2020-06-04T10:54:39.571Z" EventOutcomeIndicator="0">
+  <EventID csd-code="110107" codeSystemName="DCM" originalText="Import"/>
+  <EventTypeCode csd-code="ITI-43" codeSystemName="IHE Transactions" originalText="Retrieve Document Set"/>
+  <PurposeOfUse csd-code="NORM" codeSystemName="2.16.756.5.30.1.127.3.10.5" originalText="Normal"/>
+ </EventIdentification>
+ <ActiveParticipant UserID="pma@gnt.com" UserName="JD&lt;pma@gnt.com&gt;"/>
+ <ActiveParticipant UserID="2000000090108" UserName="Dr. med. John Doe" UserIsRequestor="true">
+  <RoleIDCode csd-code="HCP" codeSystemName="2.16.756.5.30.1.127.3.10.6" originalText="Healthcare professional"/>
+ </ActiveParticipant>
+ <ActiveParticipant UserID="https://repositoryService.com" AlternativeUserID="1" UserIsRequestor="false" NetworkAccessPointID="172.18.0.49" NetworkAccessPointTypeCode="2">
+  <RoleIDCode csd-code="110153" codeSystemName="DCM" originalText="Source Role ID"/>
+ </ActiveParticipant>
+ <ActiveParticipant UserID="https://primarySystem.com" AlternativeUserID="UNKNOWN" UserIsRequestor="true" NetworkAccessPointID="hcohcdemo01-app06-icwpxs01.net.swisscom-health.it" NetworkAccessPointTypeCode="1">
+  <RoleIDCode csd-code="110152" codeSystemName="DCM" originalText="Destination Role ID"/>
+ </ActiveParticipant>
+ <AuditSourceIdentification AuditEnterpriseSiteID="2.16.756.5.30.1.194" AuditSourceID="LE-Portal">
+  <AuditSourceTypeCode csd-code="9" codeSystemName="DCM" originalText="Other"/>
+ </AuditSourceIdentification>
+ <ParticipantObjectIdentification ParticipantObjectID="761337615343338300^^^&amp;2.16.756.5.30.1.127.3.10.3&amp;ISO" ParticipantObjectTypeCode="1" ParticipantObjectTypeCodeRole="1">
+  <ParticipantObjectIDTypeCode csd-code="2" codeSystemName="RFC-3881" originalText="Patient Number"/>
+ </ParticipantObjectIdentification>
+ <ParticipantObjectIdentification ParticipantObjectID="2.16.756.5.30.1.194.130880.1591258526941" ParticipantObjectTypeCode="2" ParticipantObjectTypeCodeRole="3">
+  <ParticipantObjectIDTypeCode csd-code="9" codeSystemName="RFC-3881" originalText="Report Number"/>
+  <ParticipantObjectDetail type="Repository Unique Id" value="Mi4xNi43NTYuNS4zMC4xLjE5NC4zLjMuMQ=="/>
+  <ParticipantObjectDetail type="ihe:homeCommunityID" value="dXJuOm9pZDoyLjE2Ljc1Ni41LjMwLjEuMTk0"/>
+ </ParticipantObjectIdentification>
+</AuditMessage>
+

The message is made of the following blocks:

  • EventIdentification: Event related information including the timestamp and purpose of use (line 3 .. 7).
  • ActiveParticipant: Information related to the primary system performing the query (line 8 .. 10).
  • ActiveParticipant: Information on the user initiating the transaction (line 11 .. 13).
  • ActiveParticipant: Additional information on the user initiating the transaction (line 14 .. 16).
  • ActiveParticipant: Information on the responding service endpoint (line 17 .. 19).
  • AuditSourceIdentification: Information related to the primary system performing the query (line 20 .. 22).
  • ParticipantObjectIdentification: Additional information on the patient local ID (line 23 .. 25).
  • ParticipantObjectIdentification: Request message related information (line 26 .. 30).
  • ParticipantObjectIdentification: Information on the patients EPR accessed (line 31 .. 33).

Security Requirements

To ensure privacy the transaction must be secured using https with mutual authentication, with X.509 certificates (extended validation required) and client and server side certificate validation.

To enable authorization, the transaction must convey the XUA Assertion for authorization in the security header of the SOAP envelope. See Provide X-User Assertion for the implementation details.

Note

  • Some test environments dropped the mutual authentication or TLS for testing purposes. Please contact your test system provider on the details.
  • Some test environments may also drop authorization for testing purposes. Please contact your test system provider on the details.

Test Opportunity

The transaction can be tested with the test suite of the EPR reference environment, test systems of the EPR communities or the EPR Playground.


Last update: 2025-01-27
\ No newline at end of file diff --git a/SSOLogout/index.html b/SSOLogout/index.html new file mode 100644 index 0000000..86f6c4a --- /dev/null +++ b/SSOLogout/index.html @@ -0,0 +1,143 @@ + SSO Logout - EPR by example
Skip to content

SSO Logout

Transaction to log out a user from the IdP and all session participants. Primary systems shall perform this transaction to notify the IdP, when a user logs out, or to receive a notification from the IdP, when the user logged out from another application sharing the same IdP session.

Overview

Primary systems shall perform this transaction to notify the IdP, when a user logs out, or to receive a notification from the IdP, when the user logged from another relying party sharing the same session.

Transaction

Message Semantics

Messages are encoded as described in Assertions and Protocols for the OASIS Security Assertion Markup Language (SAML) V2.0 and the ordinances to the Swiss EPR.

Request Message

The following snippet shows the content of a logout request, with some elements omitted to increase readability.

The major content (lines 9..32) of the message is required for to sign the message compliant with the SAML 2.0 specification. Apart from that, the message conveys the following information:

  • ID: A unique ID of the request message (line 4).
  • Destination: An identifier of the receiver endpoint. For primary systems sending the request, this shall be the URL of IdP logout endpoint (line 7).
  • NameID: The electronic ID used to authenticate the user (line 33). It's value shall match the value provided in the IdP Assertion (see Authenticate User).
  • SessionIndex: The unique ID of the session used to identify all systems sharing the same session. It's value shall match the Session Index provided in the IdP Assertion (see Authenticate User).
2_Logout Request.xml
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
<samlp:LogoutRequest
+    xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
+    xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
+    ID="pfxd4d369e8-9ea1-780c-aff8-a1d11a9862a1"
+    Version="2.0"
+    IssueInstant="2020-07-18T01:13:06Z"
+    Destination="http://idp.example.com/SSOLogoutService">
+    <saml:Issuer>http://sp.application.com</saml:Issuer>
+    <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+        <ds:SignedInfo>
+            <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+            <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
+            <ds:Reference URI="#pfxd4d369e8-9ea1-780c-aff8-a1d11a9862a1">
+                <ds:Transforms>
+                    <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
+                    <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+                </ds:Transforms>
+                <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+                <ds:DigestValue>Q9PRlugQZKSBt+Ed9i6bKUGWND0=</ds:DigestValue>
+            </ds:Reference>
+        </ds:SignedInfo>
+        <ds:SignatureValue>
+            <!-- omitted for brevity -->
+        </ds:SignatureValue>
+        <ds:KeyInfo>
+            <ds:X509Data>
+                <ds:X509Certificate>
+                    <!-- omitted for brevity -->
+                </ds:X509Certificate>
+            </ds:X509Data>
+        </ds:KeyInfo>
+    </ds:Signature>
+    <saml:NameID SPNameQualifier="http://sp.application.com" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient">IdP_User_ID_f92cc183</saml:NameID>
+    <saml:SessionIndex>bdfe3302-3ed8-11eb-b378-0242ac130002</saml:SessionIndex>
+</samlp:LogoutRequest>
+

Response Message

The following snippet shows the content of a logout response, with some elements omitted to increase readability.

The major content (lines 9..32) of the message is required for to sign the message compliant with the SAML 2.0 specification. Apart from that, the message conveys the following information:

  • ID: A unique ID of the request message (line 4).
  • Destination: An identifier of the receiver endpoint as URL (line 7).
  • InResponseTo: The unique ID of the initial request (line 8).
  • Status: The return status of the request, indicating success or failure (line 34 .. 36).
2_Logout Response.xml
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
<samlp:LogoutResponse
+    xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
+    xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
+    ID="pfxe335499f-e73b-80bd-60c4-1628984aed4f"
+    Version="2.0"
+    IssueInstant="2020-07-18T01:13:06Z"
+    Destination="http://sp.example.com/demo1/index.php?acs"
+    InResponseTo="pfxd4d369e8-9ea1-780c-aff8-a1d11a9862a1">
+    <saml:Issuer>http://idp.example.com/SSOLogoutService</saml:Issuer>
+    <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+        <ds:SignedInfo>
+            <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+            <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
+            <ds:Reference URI="#pfxe335499f-e73b-80bd-60c4-1628984aed4f">
+                <ds:Transforms>
+                    <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
+                    <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+                </ds:Transforms>
+                <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+                <ds:DigestValue>PusFPAn+RUZV+fBvwPffNMOENwE=</ds:DigestValue>
+            </ds:Reference>
+        </ds:SignedInfo>
+        <ds:SignatureValue>
+            <!-- omitted for brevity -->
+        </ds:SignatureValue>
+        <ds:KeyInfo>
+            <ds:X509Data>
+                <ds:X509Certificate>
+                    <!-- omitted for brevity -->
+                </ds:X509Certificate>
+            </ds:X509Data>
+        </ds:KeyInfo>
+    </ds:Signature>
+    <samlp:Status>
+        <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
+    </samlp:Status>
+</samlp:LogoutResponse>
+

Transport Protocol

The LogoutRequest may be send by primary systems to the IdP using one of the following bindings:

  • SAML 2.0 HTTP POST binding via the frontchannel (involving the browser).
  • SAML 2.0 SOAP binding via the backchannel.

Audit Log

Primary systems shall protocol the transaction in their logs to ensure traceability. No further requirements are defined in the ordinances of the Swiss EPR.

Security Requirements

Communication via the SOAP backchannel shall be secured with TLS and mutual authentication, using client and server certificate validation. The certificates used, shall be exchanged during the client registration process.

Communication via the frontchannel (involving the browser) shall be secured with HTTPS and mutual authentication, using server certificate validation.

Test Opportunity

The transaction can be tested with the Gazelle test suite of the EPR reference environment, or test systems of the EPR communities.


Last update: 2025-01-27
\ No newline at end of file diff --git a/assets/images/favicon.png b/assets/images/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..1cf13b9f9d978896599290a74f77d5dbe7d1655c GIT binary patch literal 1870 zcmV-U2eJ5xP)Gc)JR9QMau)O=X#!i9;T z37kk-upj^(fsR36MHs_+1RCI)NNu9}lD0S{B^g8PN?Ww(5|~L#Ng*g{WsqleV}|#l zz8@ri&cTzw_h33bHI+12+kK6WN$h#n5cD8OQt`5kw6p~9H3()bUQ8OS4Q4HTQ=1Ol z_JAocz`fLbT2^{`8n~UAo=#AUOf=SOq4pYkt;XbC&f#7lb$*7=$na!mWCQ`dBQsO0 zLFBSPj*N?#u5&pf2t4XjEGH|=pPQ8xh7tpx;US5Cx_Ju;!O`ya-yF`)b%TEt5>eP1ZX~}sjjA%FJF?h7cX8=b!DZl<6%Cv z*G0uvvU+vmnpLZ2paivG-(cd*y3$hCIcsZcYOGh{$&)A6*XX&kXZd3G8m)G$Zz-LV z^GF3VAW^Mdv!)4OM8EgqRiz~*Cji;uzl2uC9^=8I84vNp;ltJ|q-*uQwGp2ma6cY7 z;`%`!9UXO@fr&Ebapfs34OmS9^u6$)bJxrucutf>`dKPKT%%*d3XlFVKunp9 zasduxjrjs>f8V=D|J=XNZp;_Zy^WgQ$9WDjgY=z@stwiEBm9u5*|34&1Na8BMjjgf3+SHcr`5~>oz1Y?SW^=K z^bTyO6>Gar#P_W2gEMwq)ot3; zREHn~U&Dp0l6YT0&k-wLwYjb?5zGK`W6S2v+K>AM(95m2C20L|3m~rN8dprPr@t)5lsk9Hu*W z?pS990s;Ez=+Rj{x7p``4>+c0G5^pYnB1^!TL=(?HLHZ+HicG{~4F1d^5Awl_2!1jICM-!9eoLhbbT^;yHcefyTAaqRcY zmuctDopPT!%k+}x%lZRKnzykr2}}XfG_ne?nRQO~?%hkzo;@RN{P6o`&mMUWBYMTe z6i8ChtjX&gXl`nvrU>jah)2iNM%JdjqoaeaU%yVn!^70x-flljp6Q5tK}5}&X8&&G zX3fpb3E(!rH=zVI_9Gjl45w@{(ITqngWFe7@9{mX;tO25Z_8 zQHEpI+FkTU#4xu>RkN>b3Tnc3UpWzPXWm#o55GKF09j^Mh~)K7{QqbO_~(@CVq! zS<8954|P8mXN2MRs86xZ&Q4EfM@JB94b=(YGuk)s&^jiSF=t3*oNK3`rD{H`yQ?d; ztE=laAUoZx5?RC8*WKOj`%LXEkgDd>&^Q4M^z`%u0rg-It=hLCVsq!Z%^6eB-OvOT zFZ28TN&cRmgU}Elrnk43)!>Z1FCPL2K$7}gwzIc48NX}#!A1BpJP?#v5wkNprhV** z?Cpalt1oH&{r!o3eSKc&ap)iz2BTn_VV`4>9M^b3;(YY}4>#ML6{~(4mH+?%07*qo IM6N<$f(jP3KmY&$ literal 0 HcmV?d00001 diff --git a/assets/javascripts/bundle.cd18aaf1.min.js b/assets/javascripts/bundle.cd18aaf1.min.js new file mode 100644 index 0000000..07521cc --- /dev/null +++ b/assets/javascripts/bundle.cd18aaf1.min.js @@ -0,0 +1,29 @@ +"use strict";(()=>{var Si=Object.create;var ur=Object.defineProperty;var Ti=Object.getOwnPropertyDescriptor;var Oi=Object.getOwnPropertyNames,kt=Object.getOwnPropertySymbols,Mi=Object.getPrototypeOf,dr=Object.prototype.hasOwnProperty,Zr=Object.prototype.propertyIsEnumerable;var Xr=(e,t,r)=>t in e?ur(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,R=(e,t)=>{for(var r in t||(t={}))dr.call(t,r)&&Xr(e,r,t[r]);if(kt)for(var r of kt(t))Zr.call(t,r)&&Xr(e,r,t[r]);return e};var eo=(e,t)=>{var r={};for(var o in e)dr.call(e,o)&&t.indexOf(o)<0&&(r[o]=e[o]);if(e!=null&&kt)for(var o of kt(e))t.indexOf(o)<0&&Zr.call(e,o)&&(r[o]=e[o]);return r};var hr=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Li=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of Oi(t))!dr.call(e,n)&&n!==r&&ur(e,n,{get:()=>t[n],enumerable:!(o=Ti(t,n))||o.enumerable});return e};var Ht=(e,t,r)=>(r=e!=null?Si(Mi(e)):{},Li(t||!e||!e.__esModule?ur(r,"default",{value:e,enumerable:!0}):r,e));var to=(e,t,r)=>new Promise((o,n)=>{var i=c=>{try{a(r.next(c))}catch(p){n(p)}},s=c=>{try{a(r.throw(c))}catch(p){n(p)}},a=c=>c.done?o(c.value):Promise.resolve(c.value).then(i,s);a((r=r.apply(e,t)).next())});var oo=hr((br,ro)=>{(function(e,t){typeof br=="object"&&typeof ro!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(br,function(){"use strict";function e(r){var o=!0,n=!1,i=null,s={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function a(C){return!!(C&&C!==document&&C.nodeName!=="HTML"&&C.nodeName!=="BODY"&&"classList"in C&&"contains"in C.classList)}function c(C){var it=C.type,Ue=C.tagName;return!!(Ue==="INPUT"&&s[it]&&!C.readOnly||Ue==="TEXTAREA"&&!C.readOnly||C.isContentEditable)}function p(C){C.classList.contains("focus-visible")||(C.classList.add("focus-visible"),C.setAttribute("data-focus-visible-added",""))}function l(C){C.hasAttribute("data-focus-visible-added")&&(C.classList.remove("focus-visible"),C.removeAttribute("data-focus-visible-added"))}function f(C){C.metaKey||C.altKey||C.ctrlKey||(a(r.activeElement)&&p(r.activeElement),o=!0)}function u(C){o=!1}function d(C){a(C.target)&&(o||c(C.target))&&p(C.target)}function v(C){a(C.target)&&(C.target.classList.contains("focus-visible")||C.target.hasAttribute("data-focus-visible-added"))&&(n=!0,window.clearTimeout(i),i=window.setTimeout(function(){n=!1},100),l(C.target))}function b(C){document.visibilityState==="hidden"&&(n&&(o=!0),z())}function z(){document.addEventListener("mousemove",G),document.addEventListener("mousedown",G),document.addEventListener("mouseup",G),document.addEventListener("pointermove",G),document.addEventListener("pointerdown",G),document.addEventListener("pointerup",G),document.addEventListener("touchmove",G),document.addEventListener("touchstart",G),document.addEventListener("touchend",G)}function K(){document.removeEventListener("mousemove",G),document.removeEventListener("mousedown",G),document.removeEventListener("mouseup",G),document.removeEventListener("pointermove",G),document.removeEventListener("pointerdown",G),document.removeEventListener("pointerup",G),document.removeEventListener("touchmove",G),document.removeEventListener("touchstart",G),document.removeEventListener("touchend",G)}function G(C){C.target.nodeName&&C.target.nodeName.toLowerCase()==="html"||(o=!1,K())}document.addEventListener("keydown",f,!0),document.addEventListener("mousedown",u,!0),document.addEventListener("pointerdown",u,!0),document.addEventListener("touchstart",u,!0),document.addEventListener("visibilitychange",b,!0),z(),r.addEventListener("focus",d,!0),r.addEventListener("blur",v,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)})});var Vr=hr((Ot,Dr)=>{/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */(function(t,r){typeof Ot=="object"&&typeof Dr=="object"?Dr.exports=r():typeof define=="function"&&define.amd?define([],r):typeof Ot=="object"?Ot.ClipboardJS=r():t.ClipboardJS=r()})(Ot,function(){return function(){var e={686:function(o,n,i){"use strict";i.d(n,{default:function(){return wi}});var s=i(279),a=i.n(s),c=i(370),p=i.n(c),l=i(817),f=i.n(l);function u(W){try{return document.execCommand(W)}catch(O){return!1}}var d=function(O){var S=f()(O);return u("cut"),S},v=d;function b(W){var O=document.documentElement.getAttribute("dir")==="rtl",S=document.createElement("textarea");S.style.fontSize="12pt",S.style.border="0",S.style.padding="0",S.style.margin="0",S.style.position="absolute",S.style[O?"right":"left"]="-9999px";var $=window.pageYOffset||document.documentElement.scrollTop;return S.style.top="".concat($,"px"),S.setAttribute("readonly",""),S.value=W,S}var z=function(O,S){var $=b(O);S.container.appendChild($);var F=f()($);return u("copy"),$.remove(),F},K=function(O){var S=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},$="";return typeof O=="string"?$=z(O,S):O instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(O==null?void 0:O.type)?$=z(O.value,S):($=f()(O),u("copy")),$},G=K;function C(W){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?C=function(S){return typeof S}:C=function(S){return S&&typeof Symbol=="function"&&S.constructor===Symbol&&S!==Symbol.prototype?"symbol":typeof S},C(W)}var it=function(){var O=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},S=O.action,$=S===void 0?"copy":S,F=O.container,Q=O.target,_e=O.text;if($!=="copy"&&$!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(Q!==void 0)if(Q&&C(Q)==="object"&&Q.nodeType===1){if($==="copy"&&Q.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if($==="cut"&&(Q.hasAttribute("readonly")||Q.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(_e)return G(_e,{container:F});if(Q)return $==="cut"?v(Q):G(Q,{container:F})},Ue=it;function Pe(W){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?Pe=function(S){return typeof S}:Pe=function(S){return S&&typeof Symbol=="function"&&S.constructor===Symbol&&S!==Symbol.prototype?"symbol":typeof S},Pe(W)}function di(W,O){if(!(W instanceof O))throw new TypeError("Cannot call a class as a function")}function Jr(W,O){for(var S=0;S0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof F.action=="function"?F.action:this.defaultAction,this.target=typeof F.target=="function"?F.target:this.defaultTarget,this.text=typeof F.text=="function"?F.text:this.defaultText,this.container=Pe(F.container)==="object"?F.container:document.body}},{key:"listenClick",value:function(F){var Q=this;this.listener=p()(F,"click",function(_e){return Q.onClick(_e)})}},{key:"onClick",value:function(F){var Q=F.delegateTarget||F.currentTarget,_e=this.action(Q)||"copy",Ct=Ue({action:_e,container:this.container,target:this.target(Q),text:this.text(Q)});this.emit(Ct?"success":"error",{action:_e,text:Ct,trigger:Q,clearSelection:function(){Q&&Q.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(F){return fr("action",F)}},{key:"defaultTarget",value:function(F){var Q=fr("target",F);if(Q)return document.querySelector(Q)}},{key:"defaultText",value:function(F){return fr("text",F)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(F){var Q=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return G(F,Q)}},{key:"cut",value:function(F){return v(F)}},{key:"isSupported",value:function(){var F=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],Q=typeof F=="string"?[F]:F,_e=!!document.queryCommandSupported;return Q.forEach(function(Ct){_e=_e&&!!document.queryCommandSupported(Ct)}),_e}}]),S}(a()),wi=Ei},828:function(o){var n=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function s(a,c){for(;a&&a.nodeType!==n;){if(typeof a.matches=="function"&&a.matches(c))return a;a=a.parentNode}}o.exports=s},438:function(o,n,i){var s=i(828);function a(l,f,u,d,v){var b=p.apply(this,arguments);return l.addEventListener(u,b,v),{destroy:function(){l.removeEventListener(u,b,v)}}}function c(l,f,u,d,v){return typeof l.addEventListener=="function"?a.apply(null,arguments):typeof u=="function"?a.bind(null,document).apply(null,arguments):(typeof l=="string"&&(l=document.querySelectorAll(l)),Array.prototype.map.call(l,function(b){return a(b,f,u,d,v)}))}function p(l,f,u,d){return function(v){v.delegateTarget=s(v.target,f),v.delegateTarget&&d.call(l,v)}}o.exports=c},879:function(o,n){n.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},n.nodeList=function(i){var s=Object.prototype.toString.call(i);return i!==void 0&&(s==="[object NodeList]"||s==="[object HTMLCollection]")&&"length"in i&&(i.length===0||n.node(i[0]))},n.string=function(i){return typeof i=="string"||i instanceof String},n.fn=function(i){var s=Object.prototype.toString.call(i);return s==="[object Function]"}},370:function(o,n,i){var s=i(879),a=i(438);function c(u,d,v){if(!u&&!d&&!v)throw new Error("Missing required arguments");if(!s.string(d))throw new TypeError("Second argument must be a String");if(!s.fn(v))throw new TypeError("Third argument must be a Function");if(s.node(u))return p(u,d,v);if(s.nodeList(u))return l(u,d,v);if(s.string(u))return f(u,d,v);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function p(u,d,v){return u.addEventListener(d,v),{destroy:function(){u.removeEventListener(d,v)}}}function l(u,d,v){return Array.prototype.forEach.call(u,function(b){b.addEventListener(d,v)}),{destroy:function(){Array.prototype.forEach.call(u,function(b){b.removeEventListener(d,v)})}}}function f(u,d,v){return a(document.body,u,d,v)}o.exports=c},817:function(o){function n(i){var s;if(i.nodeName==="SELECT")i.focus(),s=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var a=i.hasAttribute("readonly");a||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),a||i.removeAttribute("readonly"),s=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var c=window.getSelection(),p=document.createRange();p.selectNodeContents(i),c.removeAllRanges(),c.addRange(p),s=c.toString()}return s}o.exports=n},279:function(o){function n(){}n.prototype={on:function(i,s,a){var c=this.e||(this.e={});return(c[i]||(c[i]=[])).push({fn:s,ctx:a}),this},once:function(i,s,a){var c=this;function p(){c.off(i,p),s.apply(a,arguments)}return p._=s,this.on(i,p,a)},emit:function(i){var s=[].slice.call(arguments,1),a=((this.e||(this.e={}))[i]||[]).slice(),c=0,p=a.length;for(c;c{"use strict";/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * Copyright(c) 2015 Andreas Lubbe + * Copyright(c) 2015 Tiancheng "Timothy" Gu + * MIT Licensed + */var $a=/["'&<>]/;Un.exports=Ra;function Ra(e){var t=""+e,r=$a.exec(t);if(!r)return t;var o,n="",i=0,s=0;for(i=r.index;i0&&i[i.length-1])&&(p[0]===6||p[0]===2)){r=0;continue}if(p[0]===3&&(!i||p[1]>i[0]&&p[1]=e.length&&(e=void 0),{value:e&&e[o++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function N(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var o=r.call(e),n,i=[],s;try{for(;(t===void 0||t-- >0)&&!(n=o.next()).done;)i.push(n.value)}catch(a){s={error:a}}finally{try{n&&!n.done&&(r=o.return)&&r.call(o)}finally{if(s)throw s.error}}return i}function D(e,t,r){if(r||arguments.length===2)for(var o=0,n=t.length,i;o1||a(u,d)})})}function a(u,d){try{c(o[u](d))}catch(v){f(i[0][3],v)}}function c(u){u.value instanceof Ze?Promise.resolve(u.value.v).then(p,l):f(i[0][2],u)}function p(u){a("next",u)}function l(u){a("throw",u)}function f(u,d){u(d),i.shift(),i.length&&a(i[0][0],i[0][1])}}function ao(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof we=="function"?we(e):e[Symbol.iterator](),r={},o("next"),o("throw"),o("return"),r[Symbol.asyncIterator]=function(){return this},r);function o(i){r[i]=e[i]&&function(s){return new Promise(function(a,c){s=e[i](s),n(a,c,s.done,s.value)})}}function n(i,s,a,c){Promise.resolve(c).then(function(p){i({value:p,done:a})},s)}}function k(e){return typeof e=="function"}function at(e){var t=function(o){Error.call(o),o.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var Rt=at(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: +`+r.map(function(o,n){return n+1+") "+o.toString()}).join(` + `):"",this.name="UnsubscriptionError",this.errors=r}});function De(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var Ie=function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,o,n,i;if(!this.closed){this.closed=!0;var s=this._parentage;if(s)if(this._parentage=null,Array.isArray(s))try{for(var a=we(s),c=a.next();!c.done;c=a.next()){var p=c.value;p.remove(this)}}catch(b){t={error:b}}finally{try{c&&!c.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}else s.remove(this);var l=this.initialTeardown;if(k(l))try{l()}catch(b){i=b instanceof Rt?b.errors:[b]}var f=this._finalizers;if(f){this._finalizers=null;try{for(var u=we(f),d=u.next();!d.done;d=u.next()){var v=d.value;try{so(v)}catch(b){i=i!=null?i:[],b instanceof Rt?i=D(D([],N(i)),N(b.errors)):i.push(b)}}}catch(b){o={error:b}}finally{try{d&&!d.done&&(n=u.return)&&n.call(u)}finally{if(o)throw o.error}}}if(i)throw new Rt(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)so(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&De(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&De(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=function(){var t=new e;return t.closed=!0,t}(),e}();var gr=Ie.EMPTY;function Pt(e){return e instanceof Ie||e&&"closed"in e&&k(e.remove)&&k(e.add)&&k(e.unsubscribe)}function so(e){k(e)?e():e.unsubscribe()}var Ae={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var st={setTimeout:function(e,t){for(var r=[],o=2;o0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var o=this,n=this,i=n.hasError,s=n.isStopped,a=n.observers;return i||s?gr:(this.currentObservers=null,a.push(r),new Ie(function(){o.currentObservers=null,De(a,r)}))},t.prototype._checkFinalizedStatuses=function(r){var o=this,n=o.hasError,i=o.thrownError,s=o.isStopped;n?r.error(i):s&&r.complete()},t.prototype.asObservable=function(){var r=new P;return r.source=this,r},t.create=function(r,o){return new bo(r,o)},t}(P);var bo=function(e){ie(t,e);function t(r,o){var n=e.call(this)||this;return n.destination=r,n.source=o,n}return t.prototype.next=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.next)===null||n===void 0||n.call(o,r)},t.prototype.error=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.error)===null||n===void 0||n.call(o,r)},t.prototype.complete=function(){var r,o;(o=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||o===void 0||o.call(r)},t.prototype._subscribe=function(r){var o,n;return(n=(o=this.source)===null||o===void 0?void 0:o.subscribe(r))!==null&&n!==void 0?n:gr},t}(x);var yt={now:function(){return(yt.delegate||Date).now()},delegate:void 0};var Et=function(e){ie(t,e);function t(r,o,n){r===void 0&&(r=1/0),o===void 0&&(o=1/0),n===void 0&&(n=yt);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=o,i._timestampProvider=n,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=o===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,o),i}return t.prototype.next=function(r){var o=this,n=o.isStopped,i=o._buffer,s=o._infiniteTimeWindow,a=o._timestampProvider,c=o._windowTime;n||(i.push(r),!s&&i.push(a.now()+c)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var o=this._innerSubscribe(r),n=this,i=n._infiniteTimeWindow,s=n._buffer,a=s.slice(),c=0;c0?e.prototype.requestAsyncId.call(this,r,o,n):(r.actions.push(this),r._scheduled||(r._scheduled=lt.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,o,n){var i;if(n===void 0&&(n=0),n!=null?n>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,o,n);var s=r.actions;o!=null&&((i=s[s.length-1])===null||i===void 0?void 0:i.id)!==o&&(lt.cancelAnimationFrame(o),r._scheduled=void 0)},t}(jt);var xo=function(e){ie(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var o=this._scheduled;this._scheduled=void 0;var n=this.actions,i;r=r||n.shift();do if(i=r.execute(r.state,r.delay))break;while((r=n[0])&&r.id===o&&n.shift());if(this._active=!1,i){for(;(r=n[0])&&r.id===o&&n.shift();)r.unsubscribe();throw i}},t}(Wt);var Oe=new xo(go);var L=new P(function(e){return e.complete()});function Nt(e){return e&&k(e.schedule)}function Or(e){return e[e.length-1]}function Qe(e){return k(Or(e))?e.pop():void 0}function Me(e){return Nt(Or(e))?e.pop():void 0}function Ut(e,t){return typeof Or(e)=="number"?e.pop():t}var mt=function(e){return e&&typeof e.length=="number"&&typeof e!="function"};function Dt(e){return k(e==null?void 0:e.then)}function Vt(e){return k(e[pt])}function zt(e){return Symbol.asyncIterator&&k(e==null?void 0:e[Symbol.asyncIterator])}function qt(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function Ii(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var Kt=Ii();function Qt(e){return k(e==null?void 0:e[Kt])}function Yt(e){return io(this,arguments,function(){var r,o,n,i;return $t(this,function(s){switch(s.label){case 0:r=e.getReader(),s.label=1;case 1:s.trys.push([1,,9,10]),s.label=2;case 2:return[4,Ze(r.read())];case 3:return o=s.sent(),n=o.value,i=o.done,i?[4,Ze(void 0)]:[3,5];case 4:return[2,s.sent()];case 5:return[4,Ze(n)];case 6:return[4,s.sent()];case 7:return s.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function Bt(e){return k(e==null?void 0:e.getReader)}function I(e){if(e instanceof P)return e;if(e!=null){if(Vt(e))return Fi(e);if(mt(e))return ji(e);if(Dt(e))return Wi(e);if(zt(e))return yo(e);if(Qt(e))return Ni(e);if(Bt(e))return Ui(e)}throw qt(e)}function Fi(e){return new P(function(t){var r=e[pt]();if(k(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function ji(e){return new P(function(t){for(var r=0;r=2;return function(o){return o.pipe(e?M(function(n,i){return e(n,i,o)}):ue,xe(1),r?He(t):Fo(function(){return new Jt}))}}function jo(){for(var e=[],t=0;t=2,!0))}function le(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new x}:t,o=e.resetOnError,n=o===void 0?!0:o,i=e.resetOnComplete,s=i===void 0?!0:i,a=e.resetOnRefCountZero,c=a===void 0?!0:a;return function(p){var l,f,u,d=0,v=!1,b=!1,z=function(){f==null||f.unsubscribe(),f=void 0},K=function(){z(),l=u=void 0,v=b=!1},G=function(){var C=l;K(),C==null||C.unsubscribe()};return g(function(C,it){d++,!b&&!v&&z();var Ue=u=u!=null?u:r();it.add(function(){d--,d===0&&!b&&!v&&(f=Hr(G,c))}),Ue.subscribe(it),!l&&d>0&&(l=new tt({next:function(Pe){return Ue.next(Pe)},error:function(Pe){b=!0,z(),f=Hr(K,n,Pe),Ue.error(Pe)},complete:function(){v=!0,z(),f=Hr(K,s),Ue.complete()}}),I(C).subscribe(l))})(p)}}function Hr(e,t){for(var r=[],o=2;oe.next(document)),e}function q(e,t=document){return Array.from(t.querySelectorAll(e))}function U(e,t=document){let r=se(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function se(e,t=document){return t.querySelector(e)||void 0}function Re(){return document.activeElement instanceof HTMLElement&&document.activeElement||void 0}var ia=_(h(document.body,"focusin"),h(document.body,"focusout")).pipe(ke(1),V(void 0),m(()=>Re()||document.body),J(1));function Zt(e){return ia.pipe(m(t=>e.contains(t)),X())}function Je(e){return{x:e.offsetLeft,y:e.offsetTop}}function Do(e){return _(h(window,"load"),h(window,"resize")).pipe(Ce(0,Oe),m(()=>Je(e)),V(Je(e)))}function er(e){return{x:e.scrollLeft,y:e.scrollTop}}function dt(e){return _(h(e,"scroll"),h(window,"resize")).pipe(Ce(0,Oe),m(()=>er(e)),V(er(e)))}function Vo(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)Vo(e,r)}function T(e,t,...r){let o=document.createElement(e);if(t)for(let n of Object.keys(t))typeof t[n]!="undefined"&&(typeof t[n]!="boolean"?o.setAttribute(n,t[n]):o.setAttribute(n,""));for(let n of r)Vo(o,n);return o}function tr(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function ht(e){let t=T("script",{src:e});return H(()=>(document.head.appendChild(t),_(h(t,"load"),h(t,"error").pipe(E(()=>Mr(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(m(()=>{}),A(()=>document.head.removeChild(t)),xe(1))))}var zo=new x,aa=H(()=>typeof ResizeObserver=="undefined"?ht("https://unpkg.com/resize-observer-polyfill"):j(void 0)).pipe(m(()=>new ResizeObserver(e=>{for(let t of e)zo.next(t)})),E(e=>_(Ve,j(e)).pipe(A(()=>e.disconnect()))),J(1));function he(e){return{width:e.offsetWidth,height:e.offsetHeight}}function ye(e){return aa.pipe(w(t=>t.observe(e)),E(t=>zo.pipe(M(({target:r})=>r===e),A(()=>t.unobserve(e)),m(()=>he(e)))),V(he(e)))}function bt(e){return{width:e.scrollWidth,height:e.scrollHeight}}function qo(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}var Ko=new x,sa=H(()=>j(new IntersectionObserver(e=>{for(let t of e)Ko.next(t)},{threshold:0}))).pipe(E(e=>_(Ve,j(e)).pipe(A(()=>e.disconnect()))),J(1));function rr(e){return sa.pipe(w(t=>t.observe(e)),E(t=>Ko.pipe(M(({target:r})=>r===e),A(()=>t.unobserve(e)),m(({isIntersecting:r})=>r))))}function Qo(e,t=16){return dt(e).pipe(m(({y:r})=>{let o=he(e),n=bt(e);return r>=n.height-o.height-t}),X())}var or={drawer:U("[data-md-toggle=drawer]"),search:U("[data-md-toggle=search]")};function Yo(e){return or[e].checked}function Ke(e,t){or[e].checked!==t&&or[e].click()}function We(e){let t=or[e];return h(t,"change").pipe(m(()=>t.checked),V(t.checked))}function ca(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function pa(){return _(h(window,"compositionstart").pipe(m(()=>!0)),h(window,"compositionend").pipe(m(()=>!1))).pipe(V(!1))}function Bo(){let e=h(window,"keydown").pipe(M(t=>!(t.metaKey||t.ctrlKey)),m(t=>({mode:Yo("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),M(({mode:t,type:r})=>{if(t==="global"){let o=Re();if(typeof o!="undefined")return!ca(o,r)}return!0}),le());return pa().pipe(E(t=>t?L:e))}function pe(){return new URL(location.href)}function ot(e,t=!1){if(te("navigation.instant")&&!t){let r=T("a",{href:e.href});document.body.appendChild(r),r.click(),r.remove()}else location.href=e.href}function Go(){return new x}function Jo(){return location.hash.slice(1)}function nr(e){let t=T("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function la(e){return _(h(window,"hashchange"),e).pipe(m(Jo),V(Jo()),M(t=>t.length>0),J(1))}function Xo(e){return la(e).pipe(m(t=>se(`[id="${t}"]`)),M(t=>typeof t!="undefined"))}function Fr(e){let t=matchMedia(e);return Xt(r=>t.addListener(()=>r(t.matches))).pipe(V(t.matches))}function Zo(){let e=matchMedia("print");return _(h(window,"beforeprint").pipe(m(()=>!0)),h(window,"afterprint").pipe(m(()=>!1))).pipe(V(e.matches))}function jr(e,t){return e.pipe(E(r=>r?t():L))}function ir(e,t){return new P(r=>{let o=new XMLHttpRequest;o.open("GET",`${e}`),o.responseType="blob",o.addEventListener("load",()=>{o.status>=200&&o.status<300?(r.next(o.response),r.complete()):r.error(new Error(o.statusText))}),o.addEventListener("error",()=>{r.error(new Error("Network Error"))}),o.addEventListener("abort",()=>{r.error(new Error("Request aborted"))}),typeof(t==null?void 0:t.progress$)!="undefined"&&(o.addEventListener("progress",n=>{if(n.lengthComputable)t.progress$.next(n.loaded/n.total*100);else{let i=Number(o.getResponseHeader("Content-Length"))||0;t.progress$.next(n.loaded/i*100)}}),t.progress$.next(5)),o.send()})}function Ne(e,t){return ir(e,t).pipe(E(r=>r.text()),m(r=>JSON.parse(r)),J(1))}function en(e,t){let r=new DOMParser;return ir(e,t).pipe(E(o=>o.text()),m(o=>r.parseFromString(o,"text/xml")),J(1))}function tn(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function rn(){return _(h(window,"scroll",{passive:!0}),h(window,"resize",{passive:!0})).pipe(m(tn),V(tn()))}function on(){return{width:innerWidth,height:innerHeight}}function nn(){return h(window,"resize",{passive:!0}).pipe(m(on),V(on()))}function an(){return B([rn(),nn()]).pipe(m(([e,t])=>({offset:e,size:t})),J(1))}function ar(e,{viewport$:t,header$:r}){let o=t.pipe(ee("size")),n=B([o,r]).pipe(m(()=>Je(e)));return B([r,t,n]).pipe(m(([{height:i},{offset:s,size:a},{x:c,y:p}])=>({offset:{x:s.x-c,y:s.y-p+i},size:a})))}function ma(e){return h(e,"message",t=>t.data)}function fa(e){let t=new x;return t.subscribe(r=>e.postMessage(r)),t}function sn(e,t=new Worker(e)){let r=ma(t),o=fa(t),n=new x;n.subscribe(o);let i=o.pipe(Z(),re(!0));return n.pipe(Z(),qe(r.pipe(Y(i))),le())}var ua=U("#__config"),vt=JSON.parse(ua.textContent);vt.base=`${new URL(vt.base,pe())}`;function me(){return vt}function te(e){return vt.features.includes(e)}function be(e,t){return typeof t!="undefined"?vt.translations[e].replace("#",t.toString()):vt.translations[e]}function Ee(e,t=document){return U(`[data-md-component=${e}]`,t)}function oe(e,t=document){return q(`[data-md-component=${e}]`,t)}function da(e){let t=U(".md-typeset > :first-child",e);return h(t,"click",{once:!0}).pipe(m(()=>U(".md-typeset",e)),m(r=>({hash:__md_hash(r.innerHTML)})))}function cn(e){if(!te("announce.dismiss")||!e.childElementCount)return L;if(!e.hidden){let t=U(".md-typeset",e);__md_hash(t.innerHTML)===__md_get("__announce")&&(e.hidden=!0)}return H(()=>{let t=new x;return t.subscribe(({hash:r})=>{e.hidden=!0,__md_set("__announce",r)}),da(e).pipe(w(r=>t.next(r)),A(()=>t.complete()),m(r=>R({ref:e},r)))})}function ha(e,{target$:t}){return t.pipe(m(r=>({hidden:r!==e})))}function pn(e,t){let r=new x;return r.subscribe(({hidden:o})=>{e.hidden=o}),ha(e,t).pipe(w(o=>r.next(o)),A(()=>r.complete()),m(o=>R({ref:e},o)))}function ba(e,t){let r=H(()=>B([Do(e),dt(t)])).pipe(m(([{x:o,y:n},i])=>{let{width:s,height:a}=he(e);return{x:o-i.x+s/2,y:n-i.y+a/2}}));return Zt(e).pipe(E(o=>r.pipe(m(n=>({active:o,offset:n})),xe(+!o||1/0))))}function ln(e,t,{target$:r}){let[o,n]=Array.from(e.children);return H(()=>{let i=new x,s=i.pipe(Z(),re(!0));return i.subscribe({next({offset:a}){e.style.setProperty("--md-tooltip-x",`${a.x}px`),e.style.setProperty("--md-tooltip-y",`${a.y}px`)},complete(){e.style.removeProperty("--md-tooltip-x"),e.style.removeProperty("--md-tooltip-y")}}),rr(e).pipe(Y(s)).subscribe(a=>{e.toggleAttribute("data-md-visible",a)}),_(i.pipe(M(({active:a})=>a)),i.pipe(ke(250),M(({active:a})=>!a))).subscribe({next({active:a}){a?e.prepend(o):o.remove()},complete(){e.prepend(o)}}),i.pipe(Ce(16,Oe)).subscribe(({active:a})=>{o.classList.toggle("md-tooltip--active",a)}),i.pipe(Pr(125,Oe),M(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:a})=>a)).subscribe({next(a){a?e.style.setProperty("--md-tooltip-0",`${-a}px`):e.style.removeProperty("--md-tooltip-0")},complete(){e.style.removeProperty("--md-tooltip-0")}}),h(n,"click").pipe(Y(s),M(a=>!(a.metaKey||a.ctrlKey))).subscribe(a=>{a.stopPropagation(),a.preventDefault()}),h(n,"mousedown").pipe(Y(s),ne(i)).subscribe(([a,{active:c}])=>{var p;if(a.button!==0||a.metaKey||a.ctrlKey)a.preventDefault();else if(c){a.preventDefault();let l=e.parentElement.closest(".md-annotation");l instanceof HTMLElement?l.focus():(p=Re())==null||p.blur()}}),r.pipe(Y(s),M(a=>a===o),ze(125)).subscribe(()=>e.focus()),ba(e,t).pipe(w(a=>i.next(a)),A(()=>i.complete()),m(a=>R({ref:e},a)))})}function Wr(e){return T("div",{class:"md-tooltip",id:e},T("div",{class:"md-tooltip__inner md-typeset"}))}function mn(e,t){if(t=t?`${t}_annotation_${e}`:void 0,t){let r=t?`#${t}`:void 0;return T("aside",{class:"md-annotation",tabIndex:0},Wr(t),T("a",{href:r,class:"md-annotation__index",tabIndex:-1},T("span",{"data-md-annotation-id":e})))}else return T("aside",{class:"md-annotation",tabIndex:0},Wr(t),T("span",{class:"md-annotation__index",tabIndex:-1},T("span",{"data-md-annotation-id":e})))}function fn(e){return T("button",{class:"md-clipboard md-icon",title:be("clipboard.copy"),"data-clipboard-target":`#${e} > code`})}function Nr(e,t){let r=t&2,o=t&1,n=Object.keys(e.terms).filter(c=>!e.terms[c]).reduce((c,p)=>[...c,T("del",null,p)," "],[]).slice(0,-1),i=me(),s=new URL(e.location,i.base);te("search.highlight")&&s.searchParams.set("h",Object.entries(e.terms).filter(([,c])=>c).reduce((c,[p])=>`${c} ${p}`.trim(),""));let{tags:a}=me();return T("a",{href:`${s}`,class:"md-search-result__link",tabIndex:-1},T("article",{class:"md-search-result__article md-typeset","data-md-score":e.score.toFixed(2)},r>0&&T("div",{class:"md-search-result__icon md-icon"}),r>0&&T("h1",null,e.title),r<=0&&T("h2",null,e.title),o>0&&e.text.length>0&&e.text,e.tags&&e.tags.map(c=>{let p=a?c in a?`md-tag-icon md-tag--${a[c]}`:"md-tag-icon":"";return T("span",{class:`md-tag ${p}`},c)}),o>0&&n.length>0&&T("p",{class:"md-search-result__terms"},be("search.result.term.missing"),": ",...n)))}function un(e){let t=e[0].score,r=[...e],o=me(),n=r.findIndex(l=>!`${new URL(l.location,o.base)}`.includes("#")),[i]=r.splice(n,1),s=r.findIndex(l=>l.scoreNr(l,1)),...c.length?[T("details",{class:"md-search-result__more"},T("summary",{tabIndex:-1},T("div",null,c.length>0&&c.length===1?be("search.result.more.one"):be("search.result.more.other",c.length))),...c.map(l=>Nr(l,1)))]:[]];return T("li",{class:"md-search-result__item"},p)}function dn(e){return T("ul",{class:"md-source__facts"},Object.entries(e).map(([t,r])=>T("li",{class:`md-source__fact md-source__fact--${t}`},typeof r=="number"?tr(r):r)))}function Ur(e){let t=`tabbed-control tabbed-control--${e}`;return T("div",{class:t,hidden:!0},T("button",{class:"tabbed-button",tabIndex:-1,"aria-hidden":"true"}))}function hn(e){return T("div",{class:"md-typeset__scrollwrap"},T("div",{class:"md-typeset__table"},e))}function va(e){let t=me(),r=new URL(`../${e.version}/`,t.base);return T("li",{class:"md-version__item"},T("a",{href:`${r}`,class:"md-version__link"},e.title))}function bn(e,t){return T("div",{class:"md-version"},T("button",{class:"md-version__current","aria-label":be("select.version")},t.title),T("ul",{class:"md-version__list"},e.map(va)))}function ga(e){return e.tagName==="CODE"?q(".c, .c1, .cm",e):[e]}function xa(e){let t=[];for(let r of ga(e)){let o=[],n=document.createNodeIterator(r,NodeFilter.SHOW_TEXT);for(let i=n.nextNode();i;i=n.nextNode())o.push(i);for(let i of o){let s;for(;s=/(\(\d+\))(!)?/.exec(i.textContent);){let[,a,c]=s;if(typeof c=="undefined"){let p=i.splitText(s.index);i=p.splitText(a.length),t.push(p)}else{i.textContent=a,t.push(i);break}}}}return t}function vn(e,t){t.append(...Array.from(e.childNodes))}function sr(e,t,{target$:r,print$:o}){let n=t.closest("[id]"),i=n==null?void 0:n.id,s=new Map;for(let a of xa(t)){let[,c]=a.textContent.match(/\((\d+)\)/);se(`:scope > li:nth-child(${c})`,e)&&(s.set(c,mn(c,i)),a.replaceWith(s.get(c)))}return s.size===0?L:H(()=>{let a=new x,c=a.pipe(Z(),re(!0)),p=[];for(let[l,f]of s)p.push([U(".md-typeset",f),U(`:scope > li:nth-child(${l})`,e)]);return o.pipe(Y(c)).subscribe(l=>{e.hidden=!l,e.classList.toggle("md-annotation-list",l);for(let[f,u]of p)l?vn(f,u):vn(u,f)}),_(...[...s].map(([,l])=>ln(l,t,{target$:r}))).pipe(A(()=>a.complete()),le())})}function gn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return gn(t)}}function xn(e,t){return H(()=>{let r=gn(e);return typeof r!="undefined"?sr(r,e,t):L})}var En=Ht(Vr());var ya=0;function wn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return wn(t)}}function yn(e){return ye(e).pipe(m(({width:t})=>({scrollable:bt(e).width>t})),ee("scrollable"))}function Sn(e,t){let{matches:r}=matchMedia("(hover)"),o=H(()=>{let n=new x;if(n.subscribe(({scrollable:s})=>{s&&r?e.setAttribute("tabindex","0"):e.removeAttribute("tabindex")}),En.default.isSupported()&&(e.closest(".copy")||te("content.code.copy")&&!e.closest(".no-copy"))){let s=e.closest("pre");s.id=`__code_${ya++}`,s.insertBefore(fn(s.id),e)}let i=e.closest(".highlight");if(i instanceof HTMLElement){let s=wn(i);if(typeof s!="undefined"&&(i.classList.contains("annotate")||te("content.code.annotate"))){let a=sr(s,e,t);return yn(e).pipe(w(c=>n.next(c)),A(()=>n.complete()),m(c=>R({ref:e},c)),qe(ye(i).pipe(m(({width:c,height:p})=>c&&p),X(),E(c=>c?a:L))))}}return yn(e).pipe(w(s=>n.next(s)),A(()=>n.complete()),m(s=>R({ref:e},s)))});return te("content.lazy")?rr(e).pipe(M(n=>n),xe(1),E(()=>o)):o}function Ea(e,{target$:t,print$:r}){let o=!0;return _(t.pipe(m(n=>n.closest("details:not([open])")),M(n=>e===n),m(()=>({action:"open",reveal:!0}))),r.pipe(M(n=>n||!o),w(()=>o=e.open),m(n=>({action:n?"open":"close"}))))}function Tn(e,t){return H(()=>{let r=new x;return r.subscribe(({action:o,reveal:n})=>{e.toggleAttribute("open",o==="open"),n&&e.scrollIntoView()}),Ea(e,t).pipe(w(o=>r.next(o)),A(()=>r.complete()),m(o=>R({ref:e},o)))})}var On=".node circle,.node ellipse,.node path,.node polygon,.node rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}marker{fill:var(--md-mermaid-edge-color)!important}.edgeLabel .label rect{fill:#0000}.label{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.label foreignObject{line-height:normal;overflow:visible}.label div .edgeLabel{color:var(--md-mermaid-label-fg-color)}.edgeLabel,.edgeLabel rect,.label div .edgeLabel{background-color:var(--md-mermaid-label-bg-color)}.edgeLabel,.edgeLabel rect{fill:var(--md-mermaid-label-bg-color);color:var(--md-mermaid-edge-color)}.edgePath .path,.flowchart-link{stroke:var(--md-mermaid-edge-color);stroke-width:.05rem}.edgePath .arrowheadPath{fill:var(--md-mermaid-edge-color);stroke:none}.cluster rect{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}.cluster span{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}g #flowchart-circleEnd,g #flowchart-circleStart,g #flowchart-crossEnd,g #flowchart-crossStart,g #flowchart-pointEnd,g #flowchart-pointStart{stroke:none}g.classGroup line,g.classGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.classGroup text{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.classLabel .box{fill:var(--md-mermaid-label-bg-color);background-color:var(--md-mermaid-label-bg-color);opacity:1}.classLabel .label{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.node .divider{stroke:var(--md-mermaid-node-fg-color)}.relation{stroke:var(--md-mermaid-edge-color)}.cardinality{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.cardinality text{fill:inherit!important}defs #classDiagram-compositionEnd,defs #classDiagram-compositionStart,defs #classDiagram-dependencyEnd,defs #classDiagram-dependencyStart,defs #classDiagram-extensionEnd,defs #classDiagram-extensionStart{fill:var(--md-mermaid-edge-color)!important;stroke:var(--md-mermaid-edge-color)!important}defs #classDiagram-aggregationEnd,defs #classDiagram-aggregationStart{fill:var(--md-mermaid-label-bg-color)!important;stroke:var(--md-mermaid-edge-color)!important}g.stateGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.stateGroup .state-title{fill:var(--md-mermaid-label-fg-color)!important;font-family:var(--md-mermaid-font-family)}g.stateGroup .composit{fill:var(--md-mermaid-label-bg-color)}.nodeLabel{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.node circle.state-end,.node circle.state-start,.start-state{fill:var(--md-mermaid-edge-color);stroke:none}.end-state-inner,.end-state-outer{fill:var(--md-mermaid-edge-color)}.end-state-inner,.node circle.state-end{stroke:var(--md-mermaid-label-bg-color)}.transition{stroke:var(--md-mermaid-edge-color)}[id^=state-fork] rect,[id^=state-join] rect{fill:var(--md-mermaid-edge-color)!important;stroke:none!important}.statediagram-cluster.statediagram-cluster .inner{fill:var(--md-default-bg-color)}.statediagram-cluster rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.statediagram-state rect.divider{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}defs #statediagram-barbEnd{stroke:var(--md-mermaid-edge-color)}.attributeBoxEven,.attributeBoxOdd{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.entityBox{fill:var(--md-mermaid-label-bg-color);stroke:var(--md-mermaid-node-fg-color)}.entityLabel{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.relationshipLabelBox{fill:var(--md-mermaid-label-bg-color);fill-opacity:1;background-color:var(--md-mermaid-label-bg-color);opacity:1}.relationshipLabel{fill:var(--md-mermaid-label-fg-color)}.relationshipLine{stroke:var(--md-mermaid-edge-color)}defs #ONE_OR_MORE_END *,defs #ONE_OR_MORE_START *,defs #ONLY_ONE_END *,defs #ONLY_ONE_START *,defs #ZERO_OR_MORE_END *,defs #ZERO_OR_MORE_START *,defs #ZERO_OR_ONE_END *,defs #ZERO_OR_ONE_START *{stroke:var(--md-mermaid-edge-color)!important}defs #ZERO_OR_MORE_END circle,defs #ZERO_OR_MORE_START circle{fill:var(--md-mermaid-label-bg-color)}.actor{fill:var(--md-mermaid-sequence-actor-bg-color);stroke:var(--md-mermaid-sequence-actor-border-color)}text.actor>tspan{fill:var(--md-mermaid-sequence-actor-fg-color);font-family:var(--md-mermaid-font-family)}line{stroke:var(--md-mermaid-sequence-actor-line-color)}.actor-man circle,.actor-man line{fill:var(--md-mermaid-sequence-actorman-bg-color);stroke:var(--md-mermaid-sequence-actorman-line-color)}.messageLine0,.messageLine1{stroke:var(--md-mermaid-sequence-message-line-color)}.note{fill:var(--md-mermaid-sequence-note-bg-color);stroke:var(--md-mermaid-sequence-note-border-color)}.loopText,.loopText>tspan,.messageText,.noteText>tspan{stroke:none;font-family:var(--md-mermaid-font-family)!important}.messageText{fill:var(--md-mermaid-sequence-message-fg-color)}.loopText,.loopText>tspan{fill:var(--md-mermaid-sequence-loop-fg-color)}.noteText>tspan{fill:var(--md-mermaid-sequence-note-fg-color)}#arrowhead path{fill:var(--md-mermaid-sequence-message-line-color);stroke:none}.loopLine{fill:var(--md-mermaid-sequence-loop-bg-color);stroke:var(--md-mermaid-sequence-loop-border-color)}.labelBox{fill:var(--md-mermaid-sequence-label-bg-color);stroke:none}.labelText,.labelText>span{fill:var(--md-mermaid-sequence-label-fg-color);font-family:var(--md-mermaid-font-family)}.sequenceNumber{fill:var(--md-mermaid-sequence-number-fg-color)}rect.rect{fill:var(--md-mermaid-sequence-box-bg-color);stroke:none}rect.rect+text.text{fill:var(--md-mermaid-sequence-box-fg-color)}defs #sequencenumber{fill:var(--md-mermaid-sequence-number-bg-color)!important}";var zr,Sa=0;function Ta(){return typeof mermaid=="undefined"||mermaid instanceof Element?ht("https://unpkg.com/mermaid@10.6.1/dist/mermaid.min.js"):j(void 0)}function Mn(e){return e.classList.remove("mermaid"),zr||(zr=Ta().pipe(w(()=>mermaid.initialize({startOnLoad:!1,themeCSS:On,sequence:{actorFontSize:"16px",messageFontSize:"16px",noteFontSize:"16px"}})),m(()=>{}),J(1))),zr.subscribe(()=>to(this,null,function*(){e.classList.add("mermaid");let t=`__mermaid_${Sa++}`,r=T("div",{class:"mermaid"}),o=e.textContent,{svg:n,fn:i}=yield mermaid.render(t,o),s=r.attachShadow({mode:"closed"});s.innerHTML=n,e.replaceWith(r),i==null||i(s)})),zr.pipe(m(()=>({ref:e})))}var Ln=T("table");function _n(e){return e.replaceWith(Ln),Ln.replaceWith(hn(e)),j({ref:e})}function Oa(e){let t=q(":scope > input",e),r=t.find(o=>o.checked)||t[0];return _(...t.map(o=>h(o,"change").pipe(m(()=>U(`label[for="${o.id}"]`))))).pipe(V(U(`label[for="${r.id}"]`)),m(o=>({active:o})))}function An(e,{viewport$:t}){let r=Ur("prev");e.append(r);let o=Ur("next");e.append(o);let n=U(".tabbed-labels",e);return H(()=>{let i=new x,s=i.pipe(Z(),re(!0));return B([i,ye(e)]).pipe(Ce(1,Oe),Y(s)).subscribe({next([{active:a},c]){let p=Je(a),{width:l}=he(a);e.style.setProperty("--md-indicator-x",`${p.x}px`),e.style.setProperty("--md-indicator-width",`${l}px`);let f=er(n);(p.xf.x+c.width)&&n.scrollTo({left:Math.max(0,p.x-16),behavior:"smooth"})},complete(){e.style.removeProperty("--md-indicator-x"),e.style.removeProperty("--md-indicator-width")}}),B([dt(n),ye(n)]).pipe(Y(s)).subscribe(([a,c])=>{let p=bt(n);r.hidden=a.x<16,o.hidden=a.x>p.width-c.width-16}),_(h(r,"click").pipe(m(()=>-1)),h(o,"click").pipe(m(()=>1))).pipe(Y(s)).subscribe(a=>{let{width:c}=he(n);n.scrollBy({left:c*a,behavior:"smooth"})}),te("content.tabs.link")&&i.pipe(je(1),ne(t)).subscribe(([{active:a},{offset:c}])=>{let p=a.innerText.trim();if(a.hasAttribute("data-md-switching"))a.removeAttribute("data-md-switching");else{let l=e.offsetTop-c.y;for(let u of q("[data-tabs]"))for(let d of q(":scope > input",u)){let v=U(`label[for="${d.id}"]`);if(v!==a&&v.innerText.trim()===p){v.setAttribute("data-md-switching",""),d.click();break}}window.scrollTo({top:e.offsetTop-l});let f=__md_get("__tabs")||[];__md_set("__tabs",[...new Set([p,...f])])}}),i.pipe(Y(s)).subscribe(()=>{for(let a of q("audio, video",e))a.pause()}),Oa(e).pipe(w(a=>i.next(a)),A(()=>i.complete()),m(a=>R({ref:e},a)))}).pipe(rt(ae))}function Cn(e,{viewport$:t,target$:r,print$:o}){return _(...q(".annotate:not(.highlight)",e).map(n=>xn(n,{target$:r,print$:o})),...q("pre:not(.mermaid) > code",e).map(n=>Sn(n,{target$:r,print$:o})),...q("pre.mermaid",e).map(n=>Mn(n)),...q("table:not([class])",e).map(n=>_n(n)),...q("details",e).map(n=>Tn(n,{target$:r,print$:o})),...q("[data-tabs]",e).map(n=>An(n,{viewport$:t})))}function Ma(e,{alert$:t}){return t.pipe(E(r=>_(j(!0),j(!1).pipe(ze(2e3))).pipe(m(o=>({message:r,active:o})))))}function kn(e,t){let r=U(".md-typeset",e);return H(()=>{let o=new x;return o.subscribe(({message:n,active:i})=>{e.classList.toggle("md-dialog--active",i),r.textContent=n}),Ma(e,t).pipe(w(n=>o.next(n)),A(()=>o.complete()),m(n=>R({ref:e},n)))})}function La({viewport$:e}){if(!te("header.autohide"))return j(!1);let t=e.pipe(m(({offset:{y:n}})=>n),Le(2,1),m(([n,i])=>[nMath.abs(i-n.y)>100),m(([,[n]])=>n),X()),o=We("search");return B([e,o]).pipe(m(([{offset:n},i])=>n.y>400&&!i),X(),E(n=>n?r:j(!1)),V(!1))}function Hn(e,t){return H(()=>B([ye(e),La(t)])).pipe(m(([{height:r},o])=>({height:r,hidden:o})),X((r,o)=>r.height===o.height&&r.hidden===o.hidden),J(1))}function $n(e,{header$:t,main$:r}){return H(()=>{let o=new x,n=o.pipe(Z(),re(!0));return o.pipe(ee("active"),Ge(t)).subscribe(([{active:i},{hidden:s}])=>{e.classList.toggle("md-header--shadow",i&&!s),e.hidden=s}),r.subscribe(o),t.pipe(Y(n),m(i=>R({ref:e},i)))})}function _a(e,{viewport$:t,header$:r}){return ar(e,{viewport$:t,header$:r}).pipe(m(({offset:{y:o}})=>{let{height:n}=he(e);return{active:o>=n}}),ee("active"))}function Rn(e,t){return H(()=>{let r=new x;r.subscribe({next({active:n}){e.classList.toggle("md-header__title--active",n)},complete(){e.classList.remove("md-header__title--active")}});let o=se(".md-content h1");return typeof o=="undefined"?L:_a(o,t).pipe(w(n=>r.next(n)),A(()=>r.complete()),m(n=>R({ref:e},n)))})}function Pn(e,{viewport$:t,header$:r}){let o=r.pipe(m(({height:i})=>i),X()),n=o.pipe(E(()=>ye(e).pipe(m(({height:i})=>({top:e.offsetTop,bottom:e.offsetTop+i})),ee("bottom"))));return B([o,n,t]).pipe(m(([i,{top:s,bottom:a},{offset:{y:c},size:{height:p}}])=>(p=Math.max(0,p-Math.max(0,s-c,i)-Math.max(0,p+c-a)),{offset:s-i,height:p,active:s-i<=c})),X((i,s)=>i.offset===s.offset&&i.height===s.height&&i.active===s.active))}function Aa(e){let t=__md_get("__palette")||{index:e.findIndex(r=>matchMedia(r.getAttribute("data-md-color-media")).matches)};return j(...e).pipe(ce(r=>h(r,"change").pipe(m(()=>r))),V(e[Math.max(0,t.index)]),m(r=>({index:e.indexOf(r),color:{scheme:r.getAttribute("data-md-color-scheme"),primary:r.getAttribute("data-md-color-primary"),accent:r.getAttribute("data-md-color-accent")}})),J(1))}function In(e){let t=T("meta",{name:"theme-color"});document.head.appendChild(t);let r=T("meta",{name:"color-scheme"});return document.head.appendChild(r),H(()=>{let o=new x;o.subscribe(i=>{document.body.setAttribute("data-md-color-switching","");for(let[s,a]of Object.entries(i.color))document.body.setAttribute(`data-md-color-${s}`,a);for(let s=0;s{let i=Ee("header"),s=window.getComputedStyle(i);return r.content=s.colorScheme,s.backgroundColor.match(/\d+/g).map(a=>(+a).toString(16).padStart(2,"0")).join("")})).subscribe(i=>t.content=`#${i}`),o.pipe(Se(ae)).subscribe(()=>{document.body.removeAttribute("data-md-color-switching")});let n=q("input",e);return Aa(n).pipe(w(i=>o.next(i)),A(()=>o.complete()),m(i=>R({ref:e},i)))})}function Fn(e,{progress$:t}){return H(()=>{let r=new x;return r.subscribe(({value:o})=>{e.style.setProperty("--md-progress-value",`${o}`)}),t.pipe(w(o=>r.next({value:o})),A(()=>r.complete()),m(o=>({ref:e,value:o})))})}var qr=Ht(Vr());function Ca(e){e.setAttribute("data-md-copying","");let t=e.closest("[data-copy]"),r=t?t.getAttribute("data-copy"):e.innerText;return e.removeAttribute("data-md-copying"),r.trimEnd()}function jn({alert$:e}){qr.default.isSupported()&&new P(t=>{new qr.default("[data-clipboard-target], [data-clipboard-text]",{text:r=>r.getAttribute("data-clipboard-text")||Ca(U(r.getAttribute("data-clipboard-target")))}).on("success",r=>t.next(r))}).pipe(w(t=>{t.trigger.focus()}),m(()=>be("clipboard.copied"))).subscribe(e)}function ka(e){if(e.length<2)return[""];let[t,r]=[...e].sort((n,i)=>n.length-i.length).map(n=>n.replace(/[^/]+$/,"")),o=0;if(t===r)o=t.length;else for(;t.charCodeAt(o)===r.charCodeAt(o);)o++;return e.map(n=>n.replace(t.slice(0,o),""))}function cr(e){let t=__md_get("__sitemap",sessionStorage,e);if(t)return j(t);{let r=me();return en(new URL("sitemap.xml",e||r.base)).pipe(m(o=>ka(q("loc",o).map(n=>n.textContent))),de(()=>L),He([]),w(o=>__md_set("__sitemap",o,sessionStorage,e)))}}function Wn(e){let t=se("[rel=canonical]",e);typeof t!="undefined"&&(t.href=t.href.replace("//localhost:","//127.0.0.1:"));let r=new Map;for(let o of q(":scope > *",e)){let n=o.outerHTML;for(let i of["href","src"]){let s=o.getAttribute(i);if(s===null)continue;let a=new URL(s,t==null?void 0:t.href),c=o.cloneNode();c.setAttribute(i,`${a}`),n=c.outerHTML;break}r.set(n,o)}return r}function Nn({location$:e,viewport$:t,progress$:r}){let o=me();if(location.protocol==="file:")return L;let n=cr().pipe(m(l=>l.map(f=>`${new URL(f,o.base)}`))),i=h(document.body,"click").pipe(ne(n),E(([l,f])=>{if(!(l.target instanceof Element))return L;let u=l.target.closest("a");if(u===null)return L;if(u.target||l.metaKey||l.ctrlKey)return L;let d=new URL(u.href);return d.search=d.hash="",f.includes(`${d}`)?(l.preventDefault(),j(new URL(u.href))):L}),le());i.pipe(xe(1)).subscribe(()=>{let l=se("link[rel=icon]");typeof l!="undefined"&&(l.href=l.href)}),h(window,"beforeunload").subscribe(()=>{history.scrollRestoration="auto"}),i.pipe(ne(t)).subscribe(([l,{offset:f}])=>{history.scrollRestoration="manual",history.replaceState(f,""),history.pushState(null,"",l)}),i.subscribe(e);let s=e.pipe(V(pe()),ee("pathname"),je(1),E(l=>ir(l,{progress$:r}).pipe(de(()=>(ot(l,!0),L))))),a=new DOMParser,c=s.pipe(E(l=>l.text()),E(l=>{let f=a.parseFromString(l,"text/html");for(let b of["[data-md-component=announce]","[data-md-component=container]","[data-md-component=header-topic]","[data-md-component=outdated]","[data-md-component=logo]","[data-md-component=skip]",...te("navigation.tabs.sticky")?["[data-md-component=tabs]"]:[]]){let z=se(b),K=se(b,f);typeof z!="undefined"&&typeof K!="undefined"&&z.replaceWith(K)}let u=Wn(document.head),d=Wn(f.head);for(let[b,z]of d)z.getAttribute("rel")==="stylesheet"||z.hasAttribute("src")||(u.has(b)?u.delete(b):document.head.appendChild(z));for(let b of u.values())b.getAttribute("rel")==="stylesheet"||b.hasAttribute("src")||b.remove();let v=Ee("container");return Fe(q("script",v)).pipe(E(b=>{let z=f.createElement("script");if(b.src){for(let K of b.getAttributeNames())z.setAttribute(K,b.getAttribute(K));return b.replaceWith(z),new P(K=>{z.onload=()=>K.complete()})}else return z.textContent=b.textContent,b.replaceWith(z),L}),Z(),re(f))}),le());return h(window,"popstate").pipe(m(pe)).subscribe(e),e.pipe(V(pe()),Le(2,1),M(([l,f])=>l.pathname===f.pathname&&l.hash!==f.hash),m(([,l])=>l)).subscribe(l=>{var f,u;history.state!==null||!l.hash?window.scrollTo(0,(u=(f=history.state)==null?void 0:f.y)!=null?u:0):(history.scrollRestoration="auto",nr(l.hash),history.scrollRestoration="manual")}),e.pipe(Cr(i),V(pe()),Le(2,1),M(([l,f])=>l.pathname===f.pathname&&l.hash===f.hash),m(([,l])=>l)).subscribe(l=>{history.scrollRestoration="auto",nr(l.hash),history.scrollRestoration="manual",history.back()}),c.pipe(ne(e)).subscribe(([,l])=>{var f,u;history.state!==null||!l.hash?window.scrollTo(0,(u=(f=history.state)==null?void 0:f.y)!=null?u:0):nr(l.hash)}),t.pipe(ee("offset"),ke(100)).subscribe(({offset:l})=>{history.replaceState(l,"")}),c}var Vn=Ht(Dn());function zn(e){let t=e.separator.split("|").map(n=>n.replace(/(\(\?[!=<][^)]+\))/g,"").length===0?"\uFFFD":n).join("|"),r=new RegExp(t,"img"),o=(n,i,s)=>`${i}${s}`;return n=>{n=n.replace(/[\s*+\-:~^]+/g," ").trim();let i=new RegExp(`(^|${e.separator}|)(${n.replace(/[|\\{}()[\]^$+*?.-]/g,"\\$&").replace(r,"|")})`,"img");return s=>(0,Vn.default)(s).replace(i,o).replace(/<\/mark>(\s+)]*>/img,"$1")}}function Mt(e){return e.type===1}function pr(e){return e.type===3}function qn(e,t){let r=sn(e);return _(j(location.protocol!=="file:"),We("search")).pipe($e(o=>o),E(()=>t)).subscribe(({config:o,docs:n})=>r.next({type:0,data:{config:o,docs:n,options:{suggest:te("search.suggest")}}})),r}function Kn({document$:e}){let t=me(),r=Ne(new URL("../versions.json",t.base)).pipe(de(()=>L)),o=r.pipe(m(n=>{let[,i]=t.base.match(/([^/]+)\/?$/);return n.find(({version:s,aliases:a})=>s===i||a.includes(i))||n[0]}));r.pipe(m(n=>new Map(n.map(i=>[`${new URL(`../${i.version}/`,t.base)}`,i]))),E(n=>h(document.body,"click").pipe(M(i=>!i.metaKey&&!i.ctrlKey),ne(o),E(([i,s])=>{if(i.target instanceof Element){let a=i.target.closest("a");if(a&&!a.target&&n.has(a.href)){let c=a.href;return!i.target.closest(".md-version")&&n.get(c)===s?L:(i.preventDefault(),j(c))}}return L}),E(i=>{let{version:s}=n.get(i);return cr(new URL(i)).pipe(m(a=>{let p=pe().href.replace(t.base,"");return a.includes(p.split("#")[0])?new URL(`../${s}/${p}`,t.base):new URL(i)}))})))).subscribe(n=>ot(n,!0)),B([r,o]).subscribe(([n,i])=>{U(".md-header__topic").appendChild(bn(n,i))}),e.pipe(E(()=>o)).subscribe(n=>{var s;let i=__md_get("__outdated",sessionStorage);if(i===null){i=!0;let a=((s=t.version)==null?void 0:s.default)||"latest";Array.isArray(a)||(a=[a]);e:for(let c of a)for(let p of n.aliases.concat(n.version))if(new RegExp(c,"i").test(p)){i=!1;break e}__md_set("__outdated",i,sessionStorage)}if(i)for(let a of oe("outdated"))a.hidden=!1})}function Ia(e,{worker$:t}){let{searchParams:r}=pe();r.has("q")&&(Ke("search",!0),e.value=r.get("q"),e.focus(),We("search").pipe($e(i=>!i)).subscribe(()=>{let i=pe();i.searchParams.delete("q"),history.replaceState({},"",`${i}`)}));let o=Zt(e),n=_(t.pipe($e(Mt)),h(e,"keyup"),o).pipe(m(()=>e.value),X());return B([n,o]).pipe(m(([i,s])=>({value:i,focus:s})),J(1))}function Qn(e,{worker$:t}){let r=new x,o=r.pipe(Z(),re(!0));B([t.pipe($e(Mt)),r],(i,s)=>s).pipe(ee("value")).subscribe(({value:i})=>t.next({type:2,data:i})),r.pipe(ee("focus")).subscribe(({focus:i})=>{i&&Ke("search",i)}),h(e.form,"reset").pipe(Y(o)).subscribe(()=>e.focus());let n=U("header [for=__search]");return h(n,"click").subscribe(()=>e.focus()),Ia(e,{worker$:t}).pipe(w(i=>r.next(i)),A(()=>r.complete()),m(i=>R({ref:e},i)),J(1))}function Yn(e,{worker$:t,query$:r}){let o=new x,n=Qo(e.parentElement).pipe(M(Boolean)),i=e.parentElement,s=U(":scope > :first-child",e),a=U(":scope > :last-child",e);We("search").subscribe(l=>a.setAttribute("role",l?"list":"presentation")),o.pipe(ne(r),$r(t.pipe($e(Mt)))).subscribe(([{items:l},{value:f}])=>{switch(l.length){case 0:s.textContent=f.length?be("search.result.none"):be("search.result.placeholder");break;case 1:s.textContent=be("search.result.one");break;default:let u=tr(l.length);s.textContent=be("search.result.other",u)}});let c=o.pipe(w(()=>a.innerHTML=""),E(({items:l})=>_(j(...l.slice(0,10)),j(...l.slice(10)).pipe(Le(4),Ir(n),E(([f])=>f)))),m(un),le());return c.subscribe(l=>a.appendChild(l)),c.pipe(ce(l=>{let f=se("details",l);return typeof f=="undefined"?L:h(f,"toggle").pipe(Y(o),m(()=>f))})).subscribe(l=>{l.open===!1&&l.offsetTop<=i.scrollTop&&i.scrollTo({top:l.offsetTop})}),t.pipe(M(pr),m(({data:l})=>l)).pipe(w(l=>o.next(l)),A(()=>o.complete()),m(l=>R({ref:e},l)))}function Fa(e,{query$:t}){return t.pipe(m(({value:r})=>{let o=pe();return o.hash="",r=r.replace(/\s+/g,"+").replace(/&/g,"%26").replace(/=/g,"%3D"),o.search=`q=${r}`,{url:o}}))}function Bn(e,t){let r=new x,o=r.pipe(Z(),re(!0));return r.subscribe(({url:n})=>{e.setAttribute("data-clipboard-text",e.href),e.href=`${n}`}),h(e,"click").pipe(Y(o)).subscribe(n=>n.preventDefault()),Fa(e,t).pipe(w(n=>r.next(n)),A(()=>r.complete()),m(n=>R({ref:e},n)))}function Gn(e,{worker$:t,keyboard$:r}){let o=new x,n=Ee("search-query"),i=_(h(n,"keydown"),h(n,"focus")).pipe(Se(ae),m(()=>n.value),X());return o.pipe(Ge(i),m(([{suggest:a},c])=>{let p=c.split(/([\s-]+)/);if(a!=null&&a.length&&p[p.length-1]){let l=a[a.length-1];l.startsWith(p[p.length-1])&&(p[p.length-1]=l)}else p.length=0;return p})).subscribe(a=>e.innerHTML=a.join("").replace(/\s/g," ")),r.pipe(M(({mode:a})=>a==="search")).subscribe(a=>{switch(a.type){case"ArrowRight":e.innerText.length&&n.selectionStart===n.value.length&&(n.value=e.innerText);break}}),t.pipe(M(pr),m(({data:a})=>a)).pipe(w(a=>o.next(a)),A(()=>o.complete()),m(()=>({ref:e})))}function Jn(e,{index$:t,keyboard$:r}){let o=me();try{let n=qn(o.search,t),i=Ee("search-query",e),s=Ee("search-result",e);h(e,"click").pipe(M(({target:c})=>c instanceof Element&&!!c.closest("a"))).subscribe(()=>Ke("search",!1)),r.pipe(M(({mode:c})=>c==="search")).subscribe(c=>{let p=Re();switch(c.type){case"Enter":if(p===i){let l=new Map;for(let f of q(":first-child [href]",s)){let u=f.firstElementChild;l.set(f,parseFloat(u.getAttribute("data-md-score")))}if(l.size){let[[f]]=[...l].sort(([,u],[,d])=>d-u);f.click()}c.claim()}break;case"Escape":case"Tab":Ke("search",!1),i.blur();break;case"ArrowUp":case"ArrowDown":if(typeof p=="undefined")i.focus();else{let l=[i,...q(":not(details) > [href], summary, details[open] [href]",s)],f=Math.max(0,(Math.max(0,l.indexOf(p))+l.length+(c.type==="ArrowUp"?-1:1))%l.length);l[f].focus()}c.claim();break;default:i!==Re()&&i.focus()}}),r.pipe(M(({mode:c})=>c==="global")).subscribe(c=>{switch(c.type){case"f":case"s":case"/":i.focus(),i.select(),c.claim();break}});let a=Qn(i,{worker$:n});return _(a,Yn(s,{worker$:n,query$:a})).pipe(qe(...oe("search-share",e).map(c=>Bn(c,{query$:a})),...oe("search-suggest",e).map(c=>Gn(c,{worker$:n,keyboard$:r}))))}catch(n){return e.hidden=!0,Ve}}function Xn(e,{index$:t,location$:r}){return B([t,r.pipe(V(pe()),M(o=>!!o.searchParams.get("h")))]).pipe(m(([o,n])=>zn(o.config)(n.searchParams.get("h"))),m(o=>{var s;let n=new Map,i=document.createNodeIterator(e,NodeFilter.SHOW_TEXT);for(let a=i.nextNode();a;a=i.nextNode())if((s=a.parentElement)!=null&&s.offsetHeight){let c=a.textContent,p=o(c);p.length>c.length&&n.set(a,p)}for(let[a,c]of n){let{childNodes:p}=T("span",null,c);a.replaceWith(...Array.from(p))}return{ref:e,nodes:n}}))}function ja(e,{viewport$:t,main$:r}){let o=e.closest(".md-grid"),n=o.offsetTop-o.parentElement.offsetTop;return B([r,t]).pipe(m(([{offset:i,height:s},{offset:{y:a}}])=>(s=s+Math.min(n,Math.max(0,a-i))-n,{height:s,locked:a>=i+n})),X((i,s)=>i.height===s.height&&i.locked===s.locked))}function Kr(e,o){var n=o,{header$:t}=n,r=eo(n,["header$"]);let i=U(".md-sidebar__scrollwrap",e),{y:s}=Je(i);return H(()=>{let a=new x,c=a.pipe(Z(),re(!0)),p=a.pipe(Ce(0,Oe));return p.pipe(ne(t)).subscribe({next([{height:l},{height:f}]){i.style.height=`${l-2*s}px`,e.style.top=`${f}px`},complete(){i.style.height="",e.style.top=""}}),p.pipe($e()).subscribe(()=>{for(let l of q(".md-nav__link--active[href]",e)){if(!l.clientHeight)continue;let f=l.closest(".md-sidebar__scrollwrap");if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:d}=he(f);f.scrollTo({top:u-d/2})}}}),ge(q("label[tabindex]",e)).pipe(ce(l=>h(l,"click").pipe(Se(ae),m(()=>l),Y(c)))).subscribe(l=>{let f=U(`[id="${l.htmlFor}"]`);U(`[aria-labelledby="${l.id}"]`).setAttribute("aria-expanded",`${f.checked}`)}),ja(e,r).pipe(w(l=>a.next(l)),A(()=>a.complete()),m(l=>R({ref:e},l)))})}function Zn(e,t){if(typeof t!="undefined"){let r=`https://api.github.com/repos/${e}/${t}`;return St(Ne(`${r}/releases/latest`).pipe(de(()=>L),m(o=>({version:o.tag_name})),He({})),Ne(r).pipe(de(()=>L),m(o=>({stars:o.stargazers_count,forks:o.forks_count})),He({}))).pipe(m(([o,n])=>R(R({},o),n)))}else{let r=`https://api.github.com/users/${e}`;return Ne(r).pipe(m(o=>({repositories:o.public_repos})),He({}))}}function ei(e,t){let r=`https://${e}/api/v4/projects/${encodeURIComponent(t)}`;return Ne(r).pipe(de(()=>L),m(({star_count:o,forks_count:n})=>({stars:o,forks:n})),He({}))}function ti(e){let t=e.match(/^.+github\.com\/([^/]+)\/?([^/]+)?/i);if(t){let[,r,o]=t;return Zn(r,o)}if(t=e.match(/^.+?([^/]*gitlab[^/]+)\/(.+?)\/?$/i),t){let[,r,o]=t;return ei(r,o)}return L}var Wa;function Na(e){return Wa||(Wa=H(()=>{let t=__md_get("__source",sessionStorage);if(t)return j(t);if(oe("consent").length){let o=__md_get("__consent");if(!(o&&o.github))return L}return ti(e.href).pipe(w(o=>__md_set("__source",o,sessionStorage)))}).pipe(de(()=>L),M(t=>Object.keys(t).length>0),m(t=>({facts:t})),J(1)))}function ri(e){let t=U(":scope > :last-child",e);return H(()=>{let r=new x;return r.subscribe(({facts:o})=>{t.appendChild(dn(o)),t.classList.add("md-source__repository--active")}),Na(e).pipe(w(o=>r.next(o)),A(()=>r.complete()),m(o=>R({ref:e},o)))})}function Ua(e,{viewport$:t,header$:r}){return ye(document.body).pipe(E(()=>ar(e,{header$:r,viewport$:t})),m(({offset:{y:o}})=>({hidden:o>=10})),ee("hidden"))}function oi(e,t){return H(()=>{let r=new x;return r.subscribe({next({hidden:o}){e.hidden=o},complete(){e.hidden=!1}}),(te("navigation.tabs.sticky")?j({hidden:!1}):Ua(e,t)).pipe(w(o=>r.next(o)),A(()=>r.complete()),m(o=>R({ref:e},o)))})}function Da(e,{viewport$:t,header$:r}){let o=new Map,n=q("[href^=\\#]",e);for(let a of n){let c=decodeURIComponent(a.hash.substring(1)),p=se(`[id="${c}"]`);typeof p!="undefined"&&o.set(a,p)}let i=r.pipe(ee("height"),m(({height:a})=>{let c=Ee("main"),p=U(":scope > :first-child",c);return a+.8*(p.offsetTop-c.offsetTop)}),le());return ye(document.body).pipe(ee("height"),E(a=>H(()=>{let c=[];return j([...o].reduce((p,[l,f])=>{for(;c.length&&o.get(c[c.length-1]).tagName>=f.tagName;)c.pop();let u=f.offsetTop;for(;!u&&f.parentElement;)f=f.parentElement,u=f.offsetTop;let d=f.offsetParent;for(;d;d=d.offsetParent)u+=d.offsetTop;return p.set([...c=[...c,l]].reverse(),u)},new Map))}).pipe(m(c=>new Map([...c].sort(([,p],[,l])=>p-l))),Ge(i),E(([c,p])=>t.pipe(kr(([l,f],{offset:{y:u},size:d})=>{let v=u+d.height>=Math.floor(a.height);for(;f.length;){let[,b]=f[0];if(b-p=u&&!v)f=[l.pop(),...f];else break}return[l,f]},[[],[...c]]),X((l,f)=>l[0]===f[0]&&l[1]===f[1])))))).pipe(m(([a,c])=>({prev:a.map(([p])=>p),next:c.map(([p])=>p)})),V({prev:[],next:[]}),Le(2,1),m(([a,c])=>a.prev.length{let i=new x,s=i.pipe(Z(),re(!0));if(i.subscribe(({prev:a,next:c})=>{for(let[p]of c)p.classList.remove("md-nav__link--passed"),p.classList.remove("md-nav__link--active");for(let[p,[l]]of a.entries())l.classList.add("md-nav__link--passed"),l.classList.toggle("md-nav__link--active",p===a.length-1)}),te("toc.follow")){let a=_(t.pipe(ke(1),m(()=>{})),t.pipe(ke(250),m(()=>"smooth")));i.pipe(M(({prev:c})=>c.length>0),Ge(o.pipe(Se(ae))),ne(a)).subscribe(([[{prev:c}],p])=>{let[l]=c[c.length-1];if(l.offsetHeight){let f=qo(l);if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:d}=he(f);f.scrollTo({top:u-d/2,behavior:p})}}})}return te("navigation.tracking")&&t.pipe(Y(s),ee("offset"),ke(250),je(1),Y(n.pipe(je(1))),Tt({delay:250}),ne(i)).subscribe(([,{prev:a}])=>{let c=pe(),p=a[a.length-1];if(p&&p.length){let[l]=p,{hash:f}=new URL(l.href);c.hash!==f&&(c.hash=f,history.replaceState({},"",`${c}`))}else c.hash="",history.replaceState({},"",`${c}`)}),Da(e,{viewport$:t,header$:r}).pipe(w(a=>i.next(a)),A(()=>i.complete()),m(a=>R({ref:e},a)))})}function Va(e,{viewport$:t,main$:r,target$:o}){let n=t.pipe(m(({offset:{y:s}})=>s),Le(2,1),m(([s,a])=>s>a&&a>0),X()),i=r.pipe(m(({active:s})=>s));return B([i,n]).pipe(m(([s,a])=>!(s&&a)),X(),Y(o.pipe(je(1))),re(!0),Tt({delay:250}),m(s=>({hidden:s})))}function ii(e,{viewport$:t,header$:r,main$:o,target$:n}){let i=new x,s=i.pipe(Z(),re(!0));return i.subscribe({next({hidden:a}){e.hidden=a,a?(e.setAttribute("tabindex","-1"),e.blur()):e.removeAttribute("tabindex")},complete(){e.style.top="",e.hidden=!0,e.removeAttribute("tabindex")}}),r.pipe(Y(s),ee("height")).subscribe(({height:a})=>{e.style.top=`${a+16}px`}),h(e,"click").subscribe(a=>{a.preventDefault(),window.scrollTo({top:0})}),Va(e,{viewport$:t,main$:o,target$:n}).pipe(w(a=>i.next(a)),A(()=>i.complete()),m(a=>R({ref:e},a)))}function ai({document$:e,tablet$:t}){e.pipe(E(()=>q(".md-toggle--indeterminate")),w(r=>{r.indeterminate=!0,r.checked=!1}),ce(r=>h(r,"change").pipe(Rr(()=>r.classList.contains("md-toggle--indeterminate")),m(()=>r))),ne(t)).subscribe(([r,o])=>{r.classList.remove("md-toggle--indeterminate"),o&&(r.checked=!1)})}function za(){return/(iPad|iPhone|iPod)/.test(navigator.userAgent)}function si({document$:e}){e.pipe(E(()=>q("[data-md-scrollfix]")),w(t=>t.removeAttribute("data-md-scrollfix")),M(za),ce(t=>h(t,"touchstart").pipe(m(()=>t)))).subscribe(t=>{let r=t.scrollTop;r===0?t.scrollTop=1:r+t.offsetHeight===t.scrollHeight&&(t.scrollTop=r-1)})}function ci({viewport$:e,tablet$:t}){B([We("search"),t]).pipe(m(([r,o])=>r&&!o),E(r=>j(r).pipe(ze(r?400:100))),ne(e)).subscribe(([r,{offset:{y:o}}])=>{if(r)document.body.setAttribute("data-md-scrolllock",""),document.body.style.top=`-${o}px`;else{let n=-1*parseInt(document.body.style.top,10);document.body.removeAttribute("data-md-scrolllock"),document.body.style.top="",n&&window.scrollTo(0,n)}})}Object.entries||(Object.entries=function(e){let t=[];for(let r of Object.keys(e))t.push([r,e[r]]);return t});Object.values||(Object.values=function(e){let t=[];for(let r of Object.keys(e))t.push(e[r]);return t});typeof Element!="undefined"&&(Element.prototype.scrollTo||(Element.prototype.scrollTo=function(e,t){typeof e=="object"?(this.scrollLeft=e.left,this.scrollTop=e.top):(this.scrollLeft=e,this.scrollTop=t)}),Element.prototype.replaceWith||(Element.prototype.replaceWith=function(...e){let t=this.parentNode;if(t){e.length===0&&t.removeChild(this);for(let r=e.length-1;r>=0;r--){let o=e[r];typeof o=="string"?o=document.createTextNode(o):o.parentNode&&o.parentNode.removeChild(o),r?t.insertBefore(this.previousSibling,o):t.replaceChild(o,this)}}}));function qa(){return location.protocol==="file:"?ht(`${new URL("search/search_index.js",Qr.base)}`).pipe(m(()=>__index),J(1)):Ne(new URL("search/search_index.json",Qr.base))}document.documentElement.classList.remove("no-js");document.documentElement.classList.add("js");var nt=Uo(),_t=Go(),gt=Xo(_t),Yr=Bo(),Te=an(),lr=Fr("(min-width: 960px)"),li=Fr("(min-width: 1220px)"),mi=Zo(),Qr=me(),fi=document.forms.namedItem("search")?qa():Ve,Br=new x;jn({alert$:Br});var Gr=new x;te("navigation.instant")&&Nn({location$:_t,viewport$:Te,progress$:Gr}).subscribe(nt);var pi;((pi=Qr.version)==null?void 0:pi.provider)==="mike"&&Kn({document$:nt});_(_t,gt).pipe(ze(125)).subscribe(()=>{Ke("drawer",!1),Ke("search",!1)});Yr.pipe(M(({mode:e})=>e==="global")).subscribe(e=>{switch(e.type){case"p":case",":let t=se("link[rel=prev]");typeof t!="undefined"&&ot(t);break;case"n":case".":let r=se("link[rel=next]");typeof r!="undefined"&&ot(r);break;case"Enter":let o=Re();o instanceof HTMLLabelElement&&o.click()}});ai({document$:nt,tablet$:lr});si({document$:nt});ci({viewport$:Te,tablet$:lr});var Xe=Hn(Ee("header"),{viewport$:Te}),Lt=nt.pipe(m(()=>Ee("main")),E(e=>Pn(e,{viewport$:Te,header$:Xe})),J(1)),Ka=_(...oe("consent").map(e=>pn(e,{target$:gt})),...oe("dialog").map(e=>kn(e,{alert$:Br})),...oe("header").map(e=>$n(e,{viewport$:Te,header$:Xe,main$:Lt})),...oe("palette").map(e=>In(e)),...oe("progress").map(e=>Fn(e,{progress$:Gr})),...oe("search").map(e=>Jn(e,{index$:fi,keyboard$:Yr})),...oe("source").map(e=>ri(e))),Qa=H(()=>_(...oe("announce").map(e=>cn(e)),...oe("content").map(e=>Cn(e,{viewport$:Te,target$:gt,print$:mi})),...oe("content").map(e=>te("search.highlight")?Xn(e,{index$:fi,location$:_t}):L),...oe("header-title").map(e=>Rn(e,{viewport$:Te,header$:Xe})),...oe("sidebar").map(e=>e.getAttribute("data-md-type")==="navigation"?jr(li,()=>Kr(e,{viewport$:Te,header$:Xe,main$:Lt})):jr(lr,()=>Kr(e,{viewport$:Te,header$:Xe,main$:Lt}))),...oe("tabs").map(e=>oi(e,{viewport$:Te,header$:Xe})),...oe("toc").map(e=>ni(e,{viewport$:Te,header$:Xe,main$:Lt,target$:gt})),...oe("top").map(e=>ii(e,{viewport$:Te,header$:Xe,main$:Lt,target$:gt})))),ui=nt.pipe(E(()=>Qa),qe(Ka),J(1));ui.subscribe();window.document$=nt;window.location$=_t;window.target$=gt;window.keyboard$=Yr;window.viewport$=Te;window.tablet$=lr;window.screen$=li;window.print$=mi;window.alert$=Br;window.progress$=Gr;window.component$=ui;})(); +//# sourceMappingURL=bundle.cd18aaf1.min.js.map + diff --git a/assets/javascripts/bundle.cd18aaf1.min.js.map b/assets/javascripts/bundle.cd18aaf1.min.js.map new file mode 100644 index 0000000..8bfddbb --- /dev/null +++ b/assets/javascripts/bundle.cd18aaf1.min.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["node_modules/focus-visible/dist/focus-visible.js", "node_modules/clipboard/dist/clipboard.js", "node_modules/escape-html/index.js", "src/templates/assets/javascripts/bundle.ts", "node_modules/rxjs/node_modules/tslib/tslib.es6.js", "node_modules/rxjs/src/internal/util/isFunction.ts", "node_modules/rxjs/src/internal/util/createErrorClass.ts", "node_modules/rxjs/src/internal/util/UnsubscriptionError.ts", "node_modules/rxjs/src/internal/util/arrRemove.ts", "node_modules/rxjs/src/internal/Subscription.ts", "node_modules/rxjs/src/internal/config.ts", "node_modules/rxjs/src/internal/scheduler/timeoutProvider.ts", "node_modules/rxjs/src/internal/util/reportUnhandledError.ts", "node_modules/rxjs/src/internal/util/noop.ts", "node_modules/rxjs/src/internal/NotificationFactories.ts", "node_modules/rxjs/src/internal/util/errorContext.ts", "node_modules/rxjs/src/internal/Subscriber.ts", "node_modules/rxjs/src/internal/symbol/observable.ts", "node_modules/rxjs/src/internal/util/identity.ts", "node_modules/rxjs/src/internal/util/pipe.ts", "node_modules/rxjs/src/internal/Observable.ts", "node_modules/rxjs/src/internal/util/lift.ts", "node_modules/rxjs/src/internal/operators/OperatorSubscriber.ts", "node_modules/rxjs/src/internal/scheduler/animationFrameProvider.ts", "node_modules/rxjs/src/internal/util/ObjectUnsubscribedError.ts", "node_modules/rxjs/src/internal/Subject.ts", "node_modules/rxjs/src/internal/scheduler/dateTimestampProvider.ts", "node_modules/rxjs/src/internal/ReplaySubject.ts", "node_modules/rxjs/src/internal/scheduler/Action.ts", "node_modules/rxjs/src/internal/scheduler/intervalProvider.ts", "node_modules/rxjs/src/internal/scheduler/AsyncAction.ts", "node_modules/rxjs/src/internal/Scheduler.ts", "node_modules/rxjs/src/internal/scheduler/AsyncScheduler.ts", "node_modules/rxjs/src/internal/scheduler/async.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameAction.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameScheduler.ts", "node_modules/rxjs/src/internal/scheduler/animationFrame.ts", "node_modules/rxjs/src/internal/observable/empty.ts", "node_modules/rxjs/src/internal/util/isScheduler.ts", "node_modules/rxjs/src/internal/util/args.ts", "node_modules/rxjs/src/internal/util/isArrayLike.ts", "node_modules/rxjs/src/internal/util/isPromise.ts", "node_modules/rxjs/src/internal/util/isInteropObservable.ts", "node_modules/rxjs/src/internal/util/isAsyncIterable.ts", "node_modules/rxjs/src/internal/util/throwUnobservableError.ts", "node_modules/rxjs/src/internal/symbol/iterator.ts", "node_modules/rxjs/src/internal/util/isIterable.ts", "node_modules/rxjs/src/internal/util/isReadableStreamLike.ts", "node_modules/rxjs/src/internal/observable/innerFrom.ts", "node_modules/rxjs/src/internal/util/executeSchedule.ts", "node_modules/rxjs/src/internal/operators/observeOn.ts", "node_modules/rxjs/src/internal/operators/subscribeOn.ts", "node_modules/rxjs/src/internal/scheduled/scheduleObservable.ts", "node_modules/rxjs/src/internal/scheduled/schedulePromise.ts", "node_modules/rxjs/src/internal/scheduled/scheduleArray.ts", "node_modules/rxjs/src/internal/scheduled/scheduleIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleAsyncIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleReadableStreamLike.ts", "node_modules/rxjs/src/internal/scheduled/scheduled.ts", "node_modules/rxjs/src/internal/observable/from.ts", "node_modules/rxjs/src/internal/observable/of.ts", "node_modules/rxjs/src/internal/observable/throwError.ts", "node_modules/rxjs/src/internal/util/EmptyError.ts", "node_modules/rxjs/src/internal/util/isDate.ts", "node_modules/rxjs/src/internal/operators/map.ts", "node_modules/rxjs/src/internal/util/mapOneOrManyArgs.ts", "node_modules/rxjs/src/internal/util/argsArgArrayOrObject.ts", "node_modules/rxjs/src/internal/util/createObject.ts", "node_modules/rxjs/src/internal/observable/combineLatest.ts", "node_modules/rxjs/src/internal/operators/mergeInternals.ts", "node_modules/rxjs/src/internal/operators/mergeMap.ts", "node_modules/rxjs/src/internal/operators/mergeAll.ts", "node_modules/rxjs/src/internal/operators/concatAll.ts", "node_modules/rxjs/src/internal/observable/concat.ts", "node_modules/rxjs/src/internal/observable/defer.ts", "node_modules/rxjs/src/internal/observable/fromEvent.ts", "node_modules/rxjs/src/internal/observable/fromEventPattern.ts", "node_modules/rxjs/src/internal/observable/timer.ts", "node_modules/rxjs/src/internal/observable/merge.ts", "node_modules/rxjs/src/internal/observable/never.ts", "node_modules/rxjs/src/internal/util/argsOrArgArray.ts", "node_modules/rxjs/src/internal/operators/filter.ts", "node_modules/rxjs/src/internal/observable/zip.ts", "node_modules/rxjs/src/internal/operators/audit.ts", "node_modules/rxjs/src/internal/operators/auditTime.ts", "node_modules/rxjs/src/internal/operators/bufferCount.ts", "node_modules/rxjs/src/internal/operators/catchError.ts", "node_modules/rxjs/src/internal/operators/scanInternals.ts", "node_modules/rxjs/src/internal/operators/combineLatest.ts", "node_modules/rxjs/src/internal/operators/combineLatestWith.ts", "node_modules/rxjs/src/internal/operators/debounceTime.ts", "node_modules/rxjs/src/internal/operators/defaultIfEmpty.ts", "node_modules/rxjs/src/internal/operators/take.ts", "node_modules/rxjs/src/internal/operators/ignoreElements.ts", "node_modules/rxjs/src/internal/operators/mapTo.ts", "node_modules/rxjs/src/internal/operators/delayWhen.ts", "node_modules/rxjs/src/internal/operators/delay.ts", "node_modules/rxjs/src/internal/operators/distinctUntilChanged.ts", "node_modules/rxjs/src/internal/operators/distinctUntilKeyChanged.ts", "node_modules/rxjs/src/internal/operators/throwIfEmpty.ts", "node_modules/rxjs/src/internal/operators/endWith.ts", "node_modules/rxjs/src/internal/operators/finalize.ts", "node_modules/rxjs/src/internal/operators/first.ts", "node_modules/rxjs/src/internal/operators/merge.ts", "node_modules/rxjs/src/internal/operators/mergeWith.ts", "node_modules/rxjs/src/internal/operators/repeat.ts", "node_modules/rxjs/src/internal/operators/sample.ts", "node_modules/rxjs/src/internal/operators/scan.ts", "node_modules/rxjs/src/internal/operators/share.ts", "node_modules/rxjs/src/internal/operators/shareReplay.ts", "node_modules/rxjs/src/internal/operators/skip.ts", "node_modules/rxjs/src/internal/operators/skipUntil.ts", "node_modules/rxjs/src/internal/operators/startWith.ts", "node_modules/rxjs/src/internal/operators/switchMap.ts", "node_modules/rxjs/src/internal/operators/takeUntil.ts", "node_modules/rxjs/src/internal/operators/takeWhile.ts", "node_modules/rxjs/src/internal/operators/tap.ts", "node_modules/rxjs/src/internal/operators/throttle.ts", "node_modules/rxjs/src/internal/operators/throttleTime.ts", "node_modules/rxjs/src/internal/operators/withLatestFrom.ts", "node_modules/rxjs/src/internal/operators/zip.ts", "node_modules/rxjs/src/internal/operators/zipWith.ts", "src/templates/assets/javascripts/browser/document/index.ts", "src/templates/assets/javascripts/browser/element/_/index.ts", "src/templates/assets/javascripts/browser/element/focus/index.ts", "src/templates/assets/javascripts/browser/element/offset/_/index.ts", "src/templates/assets/javascripts/browser/element/offset/content/index.ts", "src/templates/assets/javascripts/utilities/h/index.ts", "src/templates/assets/javascripts/utilities/round/index.ts", "src/templates/assets/javascripts/browser/script/index.ts", "src/templates/assets/javascripts/browser/element/size/_/index.ts", "src/templates/assets/javascripts/browser/element/size/content/index.ts", "src/templates/assets/javascripts/browser/element/visibility/index.ts", "src/templates/assets/javascripts/browser/toggle/index.ts", "src/templates/assets/javascripts/browser/keyboard/index.ts", "src/templates/assets/javascripts/browser/location/_/index.ts", "src/templates/assets/javascripts/browser/location/hash/index.ts", "src/templates/assets/javascripts/browser/media/index.ts", "src/templates/assets/javascripts/browser/request/index.ts", "src/templates/assets/javascripts/browser/viewport/offset/index.ts", "src/templates/assets/javascripts/browser/viewport/size/index.ts", "src/templates/assets/javascripts/browser/viewport/_/index.ts", "src/templates/assets/javascripts/browser/viewport/at/index.ts", "src/templates/assets/javascripts/browser/worker/index.ts", "src/templates/assets/javascripts/_/index.ts", "src/templates/assets/javascripts/components/_/index.ts", "src/templates/assets/javascripts/components/announce/index.ts", "src/templates/assets/javascripts/components/consent/index.ts", "src/templates/assets/javascripts/components/content/annotation/_/index.ts", "src/templates/assets/javascripts/templates/tooltip/index.tsx", "src/templates/assets/javascripts/templates/annotation/index.tsx", "src/templates/assets/javascripts/templates/clipboard/index.tsx", "src/templates/assets/javascripts/templates/search/index.tsx", "src/templates/assets/javascripts/templates/source/index.tsx", "src/templates/assets/javascripts/templates/tabbed/index.tsx", "src/templates/assets/javascripts/templates/table/index.tsx", "src/templates/assets/javascripts/templates/version/index.tsx", "src/templates/assets/javascripts/components/content/annotation/list/index.ts", "src/templates/assets/javascripts/components/content/annotation/block/index.ts", "src/templates/assets/javascripts/components/content/code/_/index.ts", "src/templates/assets/javascripts/components/content/details/index.ts", "src/templates/assets/javascripts/components/content/mermaid/index.css", "src/templates/assets/javascripts/components/content/mermaid/index.ts", "src/templates/assets/javascripts/components/content/table/index.ts", "src/templates/assets/javascripts/components/content/tabs/index.ts", "src/templates/assets/javascripts/components/content/_/index.ts", "src/templates/assets/javascripts/components/dialog/index.ts", "src/templates/assets/javascripts/components/header/_/index.ts", "src/templates/assets/javascripts/components/header/title/index.ts", "src/templates/assets/javascripts/components/main/index.ts", "src/templates/assets/javascripts/components/palette/index.ts", "src/templates/assets/javascripts/components/progress/index.ts", "src/templates/assets/javascripts/integrations/clipboard/index.ts", "src/templates/assets/javascripts/integrations/sitemap/index.ts", "src/templates/assets/javascripts/integrations/instant/index.ts", "src/templates/assets/javascripts/integrations/search/highlighter/index.ts", "src/templates/assets/javascripts/integrations/search/worker/message/index.ts", "src/templates/assets/javascripts/integrations/search/worker/_/index.ts", "src/templates/assets/javascripts/integrations/version/index.ts", "src/templates/assets/javascripts/components/search/query/index.ts", "src/templates/assets/javascripts/components/search/result/index.ts", "src/templates/assets/javascripts/components/search/share/index.ts", "src/templates/assets/javascripts/components/search/suggest/index.ts", "src/templates/assets/javascripts/components/search/_/index.ts", "src/templates/assets/javascripts/components/search/highlight/index.ts", "src/templates/assets/javascripts/components/sidebar/index.ts", "src/templates/assets/javascripts/components/source/facts/github/index.ts", "src/templates/assets/javascripts/components/source/facts/gitlab/index.ts", "src/templates/assets/javascripts/components/source/facts/_/index.ts", "src/templates/assets/javascripts/components/source/_/index.ts", "src/templates/assets/javascripts/components/tabs/index.ts", "src/templates/assets/javascripts/components/toc/index.ts", "src/templates/assets/javascripts/components/top/index.ts", "src/templates/assets/javascripts/patches/indeterminate/index.ts", "src/templates/assets/javascripts/patches/scrollfix/index.ts", "src/templates/assets/javascripts/patches/scrolllock/index.ts", "src/templates/assets/javascripts/polyfills/index.ts"], + "sourcesContent": ["(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (factory());\n}(this, (function () { 'use strict';\n\n /**\n * Applies the :focus-visible polyfill at the given scope.\n * A scope in this case is either the top-level Document or a Shadow Root.\n *\n * @param {(Document|ShadowRoot)} scope\n * @see https://github.com/WICG/focus-visible\n */\n function applyFocusVisiblePolyfill(scope) {\n var hadKeyboardEvent = true;\n var hadFocusVisibleRecently = false;\n var hadFocusVisibleRecentlyTimeout = null;\n\n var inputTypesAllowlist = {\n text: true,\n search: true,\n url: true,\n tel: true,\n email: true,\n password: true,\n number: true,\n date: true,\n month: true,\n week: true,\n time: true,\n datetime: true,\n 'datetime-local': true\n };\n\n /**\n * Helper function for legacy browsers and iframes which sometimes focus\n * elements like document, body, and non-interactive SVG.\n * @param {Element} el\n */\n function isValidFocusTarget(el) {\n if (\n el &&\n el !== document &&\n el.nodeName !== 'HTML' &&\n el.nodeName !== 'BODY' &&\n 'classList' in el &&\n 'contains' in el.classList\n ) {\n return true;\n }\n return false;\n }\n\n /**\n * Computes whether the given element should automatically trigger the\n * `focus-visible` class being added, i.e. whether it should always match\n * `:focus-visible` when focused.\n * @param {Element} el\n * @return {boolean}\n */\n function focusTriggersKeyboardModality(el) {\n var type = el.type;\n var tagName = el.tagName;\n\n if (tagName === 'INPUT' && inputTypesAllowlist[type] && !el.readOnly) {\n return true;\n }\n\n if (tagName === 'TEXTAREA' && !el.readOnly) {\n return true;\n }\n\n if (el.isContentEditable) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Add the `focus-visible` class to the given element if it was not added by\n * the author.\n * @param {Element} el\n */\n function addFocusVisibleClass(el) {\n if (el.classList.contains('focus-visible')) {\n return;\n }\n el.classList.add('focus-visible');\n el.setAttribute('data-focus-visible-added', '');\n }\n\n /**\n * Remove the `focus-visible` class from the given element if it was not\n * originally added by the author.\n * @param {Element} el\n */\n function removeFocusVisibleClass(el) {\n if (!el.hasAttribute('data-focus-visible-added')) {\n return;\n }\n el.classList.remove('focus-visible');\n el.removeAttribute('data-focus-visible-added');\n }\n\n /**\n * If the most recent user interaction was via the keyboard;\n * and the key press did not include a meta, alt/option, or control key;\n * then the modality is keyboard. Otherwise, the modality is not keyboard.\n * Apply `focus-visible` to any current active element and keep track\n * of our keyboard modality state with `hadKeyboardEvent`.\n * @param {KeyboardEvent} e\n */\n function onKeyDown(e) {\n if (e.metaKey || e.altKey || e.ctrlKey) {\n return;\n }\n\n if (isValidFocusTarget(scope.activeElement)) {\n addFocusVisibleClass(scope.activeElement);\n }\n\n hadKeyboardEvent = true;\n }\n\n /**\n * If at any point a user clicks with a pointing device, ensure that we change\n * the modality away from keyboard.\n * This avoids the situation where a user presses a key on an already focused\n * element, and then clicks on a different element, focusing it with a\n * pointing device, while we still think we're in keyboard modality.\n * @param {Event} e\n */\n function onPointerDown(e) {\n hadKeyboardEvent = false;\n }\n\n /**\n * On `focus`, add the `focus-visible` class to the target if:\n * - the target received focus as a result of keyboard navigation, or\n * - the event target is an element that will likely require interaction\n * via the keyboard (e.g. a text box)\n * @param {Event} e\n */\n function onFocus(e) {\n // Prevent IE from focusing the document or HTML element.\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {\n addFocusVisibleClass(e.target);\n }\n }\n\n /**\n * On `blur`, remove the `focus-visible` class from the target.\n * @param {Event} e\n */\n function onBlur(e) {\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (\n e.target.classList.contains('focus-visible') ||\n e.target.hasAttribute('data-focus-visible-added')\n ) {\n // To detect a tab/window switch, we look for a blur event followed\n // rapidly by a visibility change.\n // If we don't see a visibility change within 100ms, it's probably a\n // regular focus change.\n hadFocusVisibleRecently = true;\n window.clearTimeout(hadFocusVisibleRecentlyTimeout);\n hadFocusVisibleRecentlyTimeout = window.setTimeout(function() {\n hadFocusVisibleRecently = false;\n }, 100);\n removeFocusVisibleClass(e.target);\n }\n }\n\n /**\n * If the user changes tabs, keep track of whether or not the previously\n * focused element had .focus-visible.\n * @param {Event} e\n */\n function onVisibilityChange(e) {\n if (document.visibilityState === 'hidden') {\n // If the tab becomes active again, the browser will handle calling focus\n // on the element (Safari actually calls it twice).\n // If this tab change caused a blur on an element with focus-visible,\n // re-apply the class when the user switches back to the tab.\n if (hadFocusVisibleRecently) {\n hadKeyboardEvent = true;\n }\n addInitialPointerMoveListeners();\n }\n }\n\n /**\n * Add a group of listeners to detect usage of any pointing devices.\n * These listeners will be added when the polyfill first loads, and anytime\n * the window is blurred, so that they are active when the window regains\n * focus.\n */\n function addInitialPointerMoveListeners() {\n document.addEventListener('mousemove', onInitialPointerMove);\n document.addEventListener('mousedown', onInitialPointerMove);\n document.addEventListener('mouseup', onInitialPointerMove);\n document.addEventListener('pointermove', onInitialPointerMove);\n document.addEventListener('pointerdown', onInitialPointerMove);\n document.addEventListener('pointerup', onInitialPointerMove);\n document.addEventListener('touchmove', onInitialPointerMove);\n document.addEventListener('touchstart', onInitialPointerMove);\n document.addEventListener('touchend', onInitialPointerMove);\n }\n\n function removeInitialPointerMoveListeners() {\n document.removeEventListener('mousemove', onInitialPointerMove);\n document.removeEventListener('mousedown', onInitialPointerMove);\n document.removeEventListener('mouseup', onInitialPointerMove);\n document.removeEventListener('pointermove', onInitialPointerMove);\n document.removeEventListener('pointerdown', onInitialPointerMove);\n document.removeEventListener('pointerup', onInitialPointerMove);\n document.removeEventListener('touchmove', onInitialPointerMove);\n document.removeEventListener('touchstart', onInitialPointerMove);\n document.removeEventListener('touchend', onInitialPointerMove);\n }\n\n /**\n * When the polfyill first loads, assume the user is in keyboard modality.\n * If any event is received from a pointing device (e.g. mouse, pointer,\n * touch), turn off keyboard modality.\n * This accounts for situations where focus enters the page from the URL bar.\n * @param {Event} e\n */\n function onInitialPointerMove(e) {\n // Work around a Safari quirk that fires a mousemove on whenever the\n // window blurs, even if you're tabbing out of the page. \u00AF\\_(\u30C4)_/\u00AF\n if (e.target.nodeName && e.target.nodeName.toLowerCase() === 'html') {\n return;\n }\n\n hadKeyboardEvent = false;\n removeInitialPointerMoveListeners();\n }\n\n // For some kinds of state, we are interested in changes at the global scope\n // only. For example, global pointer input, global key presses and global\n // visibility change should affect the state at every scope:\n document.addEventListener('keydown', onKeyDown, true);\n document.addEventListener('mousedown', onPointerDown, true);\n document.addEventListener('pointerdown', onPointerDown, true);\n document.addEventListener('touchstart', onPointerDown, true);\n document.addEventListener('visibilitychange', onVisibilityChange, true);\n\n addInitialPointerMoveListeners();\n\n // For focus and blur, we specifically care about state changes in the local\n // scope. This is because focus / blur events that originate from within a\n // shadow root are not re-dispatched from the host element if it was already\n // the active element in its own scope:\n scope.addEventListener('focus', onFocus, true);\n scope.addEventListener('blur', onBlur, true);\n\n // We detect that a node is a ShadowRoot by ensuring that it is a\n // DocumentFragment and also has a host property. This check covers native\n // implementation and polyfill implementation transparently. If we only cared\n // about the native implementation, we could just check if the scope was\n // an instance of a ShadowRoot.\n if (scope.nodeType === Node.DOCUMENT_FRAGMENT_NODE && scope.host) {\n // Since a ShadowRoot is a special kind of DocumentFragment, it does not\n // have a root element to add a class to. So, we add this attribute to the\n // host element instead:\n scope.host.setAttribute('data-js-focus-visible', '');\n } else if (scope.nodeType === Node.DOCUMENT_NODE) {\n document.documentElement.classList.add('js-focus-visible');\n document.documentElement.setAttribute('data-js-focus-visible', '');\n }\n }\n\n // It is important to wrap all references to global window and document in\n // these checks to support server-side rendering use cases\n // @see https://github.com/WICG/focus-visible/issues/199\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n // Make the polyfill helper globally available. This can be used as a signal\n // to interested libraries that wish to coordinate with the polyfill for e.g.,\n // applying the polyfill to a shadow root:\n window.applyFocusVisiblePolyfill = applyFocusVisiblePolyfill;\n\n // Notify interested libraries of the polyfill's presence, in case the\n // polyfill was loaded lazily:\n var event;\n\n try {\n event = new CustomEvent('focus-visible-polyfill-ready');\n } catch (error) {\n // IE11 does not support using CustomEvent as a constructor directly:\n event = document.createEvent('CustomEvent');\n event.initCustomEvent('focus-visible-polyfill-ready', false, false, {});\n }\n\n window.dispatchEvent(event);\n }\n\n if (typeof document !== 'undefined') {\n // Apply the polyfill to the global document, so that no JavaScript\n // coordination is required to use the polyfill in the top-level document:\n applyFocusVisiblePolyfill(document);\n }\n\n})));\n", "/*!\n * clipboard.js v2.0.11\n * https://clipboardjs.com/\n *\n * Licensed MIT \u00A9 Zeno Rocha\n */\n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ClipboardJS\"] = factory();\n\telse\n\t\troot[\"ClipboardJS\"] = factory();\n})(this, function() {\nreturn /******/ (function() { // webpackBootstrap\n/******/ \tvar __webpack_modules__ = ({\n\n/***/ 686:\n/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n\n// EXPORTS\n__webpack_require__.d(__webpack_exports__, {\n \"default\": function() { return /* binding */ clipboard; }\n});\n\n// EXTERNAL MODULE: ./node_modules/tiny-emitter/index.js\nvar tiny_emitter = __webpack_require__(279);\nvar tiny_emitter_default = /*#__PURE__*/__webpack_require__.n(tiny_emitter);\n// EXTERNAL MODULE: ./node_modules/good-listener/src/listen.js\nvar listen = __webpack_require__(370);\nvar listen_default = /*#__PURE__*/__webpack_require__.n(listen);\n// EXTERNAL MODULE: ./node_modules/select/src/select.js\nvar src_select = __webpack_require__(817);\nvar select_default = /*#__PURE__*/__webpack_require__.n(src_select);\n;// CONCATENATED MODULE: ./src/common/command.js\n/**\n * Executes a given operation type.\n * @param {String} type\n * @return {Boolean}\n */\nfunction command(type) {\n try {\n return document.execCommand(type);\n } catch (err) {\n return false;\n }\n}\n;// CONCATENATED MODULE: ./src/actions/cut.js\n\n\n/**\n * Cut action wrapper.\n * @param {String|HTMLElement} target\n * @return {String}\n */\n\nvar ClipboardActionCut = function ClipboardActionCut(target) {\n var selectedText = select_default()(target);\n command('cut');\n return selectedText;\n};\n\n/* harmony default export */ var actions_cut = (ClipboardActionCut);\n;// CONCATENATED MODULE: ./src/common/create-fake-element.js\n/**\n * Creates a fake textarea element with a value.\n * @param {String} value\n * @return {HTMLElement}\n */\nfunction createFakeElement(value) {\n var isRTL = document.documentElement.getAttribute('dir') === 'rtl';\n var fakeElement = document.createElement('textarea'); // Prevent zooming on iOS\n\n fakeElement.style.fontSize = '12pt'; // Reset box model\n\n fakeElement.style.border = '0';\n fakeElement.style.padding = '0';\n fakeElement.style.margin = '0'; // Move element out of screen horizontally\n\n fakeElement.style.position = 'absolute';\n fakeElement.style[isRTL ? 'right' : 'left'] = '-9999px'; // Move element to the same position vertically\n\n var yPosition = window.pageYOffset || document.documentElement.scrollTop;\n fakeElement.style.top = \"\".concat(yPosition, \"px\");\n fakeElement.setAttribute('readonly', '');\n fakeElement.value = value;\n return fakeElement;\n}\n;// CONCATENATED MODULE: ./src/actions/copy.js\n\n\n\n/**\n * Create fake copy action wrapper using a fake element.\n * @param {String} target\n * @param {Object} options\n * @return {String}\n */\n\nvar fakeCopyAction = function fakeCopyAction(value, options) {\n var fakeElement = createFakeElement(value);\n options.container.appendChild(fakeElement);\n var selectedText = select_default()(fakeElement);\n command('copy');\n fakeElement.remove();\n return selectedText;\n};\n/**\n * Copy action wrapper.\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @return {String}\n */\n\n\nvar ClipboardActionCopy = function ClipboardActionCopy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n var selectedText = '';\n\n if (typeof target === 'string') {\n selectedText = fakeCopyAction(target, options);\n } else if (target instanceof HTMLInputElement && !['text', 'search', 'url', 'tel', 'password'].includes(target === null || target === void 0 ? void 0 : target.type)) {\n // If input type doesn't support `setSelectionRange`. Simulate it. https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange\n selectedText = fakeCopyAction(target.value, options);\n } else {\n selectedText = select_default()(target);\n command('copy');\n }\n\n return selectedText;\n};\n\n/* harmony default export */ var actions_copy = (ClipboardActionCopy);\n;// CONCATENATED MODULE: ./src/actions/default.js\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\n\n\n/**\n * Inner function which performs selection from either `text` or `target`\n * properties and then executes copy or cut operations.\n * @param {Object} options\n */\n\nvar ClipboardActionDefault = function ClipboardActionDefault() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n // Defines base properties passed from constructor.\n var _options$action = options.action,\n action = _options$action === void 0 ? 'copy' : _options$action,\n container = options.container,\n target = options.target,\n text = options.text; // Sets the `action` to be performed which can be either 'copy' or 'cut'.\n\n if (action !== 'copy' && action !== 'cut') {\n throw new Error('Invalid \"action\" value, use either \"copy\" or \"cut\"');\n } // Sets the `target` property using an element that will be have its content copied.\n\n\n if (target !== undefined) {\n if (target && _typeof(target) === 'object' && target.nodeType === 1) {\n if (action === 'copy' && target.hasAttribute('disabled')) {\n throw new Error('Invalid \"target\" attribute. Please use \"readonly\" instead of \"disabled\" attribute');\n }\n\n if (action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {\n throw new Error('Invalid \"target\" attribute. You can\\'t cut text from elements with \"readonly\" or \"disabled\" attributes');\n }\n } else {\n throw new Error('Invalid \"target\" value, use a valid Element');\n }\n } // Define selection strategy based on `text` property.\n\n\n if (text) {\n return actions_copy(text, {\n container: container\n });\n } // Defines which selection strategy based on `target` property.\n\n\n if (target) {\n return action === 'cut' ? actions_cut(target) : actions_copy(target, {\n container: container\n });\n }\n};\n\n/* harmony default export */ var actions_default = (ClipboardActionDefault);\n;// CONCATENATED MODULE: ./src/clipboard.js\nfunction clipboard_typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { clipboard_typeof = function _typeof(obj) { return typeof obj; }; } else { clipboard_typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return clipboard_typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (clipboard_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n\n\n\n\n\n/**\n * Helper function to retrieve attribute value.\n * @param {String} suffix\n * @param {Element} element\n */\n\nfunction getAttributeValue(suffix, element) {\n var attribute = \"data-clipboard-\".concat(suffix);\n\n if (!element.hasAttribute(attribute)) {\n return;\n }\n\n return element.getAttribute(attribute);\n}\n/**\n * Base class which takes one or more elements, adds event listeners to them,\n * and instantiates a new `ClipboardAction` on each click.\n */\n\n\nvar Clipboard = /*#__PURE__*/function (_Emitter) {\n _inherits(Clipboard, _Emitter);\n\n var _super = _createSuper(Clipboard);\n\n /**\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n * @param {Object} options\n */\n function Clipboard(trigger, options) {\n var _this;\n\n _classCallCheck(this, Clipboard);\n\n _this = _super.call(this);\n\n _this.resolveOptions(options);\n\n _this.listenClick(trigger);\n\n return _this;\n }\n /**\n * Defines if attributes would be resolved using internal setter functions\n * or custom functions that were passed in the constructor.\n * @param {Object} options\n */\n\n\n _createClass(Clipboard, [{\n key: \"resolveOptions\",\n value: function resolveOptions() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n this.action = typeof options.action === 'function' ? options.action : this.defaultAction;\n this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;\n this.text = typeof options.text === 'function' ? options.text : this.defaultText;\n this.container = clipboard_typeof(options.container) === 'object' ? options.container : document.body;\n }\n /**\n * Adds a click event listener to the passed trigger.\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n */\n\n }, {\n key: \"listenClick\",\n value: function listenClick(trigger) {\n var _this2 = this;\n\n this.listener = listen_default()(trigger, 'click', function (e) {\n return _this2.onClick(e);\n });\n }\n /**\n * Defines a new `ClipboardAction` on each click event.\n * @param {Event} e\n */\n\n }, {\n key: \"onClick\",\n value: function onClick(e) {\n var trigger = e.delegateTarget || e.currentTarget;\n var action = this.action(trigger) || 'copy';\n var text = actions_default({\n action: action,\n container: this.container,\n target: this.target(trigger),\n text: this.text(trigger)\n }); // Fires an event based on the copy operation result.\n\n this.emit(text ? 'success' : 'error', {\n action: action,\n text: text,\n trigger: trigger,\n clearSelection: function clearSelection() {\n if (trigger) {\n trigger.focus();\n }\n\n window.getSelection().removeAllRanges();\n }\n });\n }\n /**\n * Default `action` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultAction\",\n value: function defaultAction(trigger) {\n return getAttributeValue('action', trigger);\n }\n /**\n * Default `target` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultTarget\",\n value: function defaultTarget(trigger) {\n var selector = getAttributeValue('target', trigger);\n\n if (selector) {\n return document.querySelector(selector);\n }\n }\n /**\n * Allow fire programmatically a copy action\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @returns Text copied.\n */\n\n }, {\n key: \"defaultText\",\n\n /**\n * Default `text` lookup function.\n * @param {Element} trigger\n */\n value: function defaultText(trigger) {\n return getAttributeValue('text', trigger);\n }\n /**\n * Destroy lifecycle.\n */\n\n }, {\n key: \"destroy\",\n value: function destroy() {\n this.listener.destroy();\n }\n }], [{\n key: \"copy\",\n value: function copy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n return actions_copy(target, options);\n }\n /**\n * Allow fire programmatically a cut action\n * @param {String|HTMLElement} target\n * @returns Text cutted.\n */\n\n }, {\n key: \"cut\",\n value: function cut(target) {\n return actions_cut(target);\n }\n /**\n * Returns the support of the given action, or all actions if no action is\n * given.\n * @param {String} [action]\n */\n\n }, {\n key: \"isSupported\",\n value: function isSupported() {\n var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];\n var actions = typeof action === 'string' ? [action] : action;\n var support = !!document.queryCommandSupported;\n actions.forEach(function (action) {\n support = support && !!document.queryCommandSupported(action);\n });\n return support;\n }\n }]);\n\n return Clipboard;\n}((tiny_emitter_default()));\n\n/* harmony default export */ var clipboard = (Clipboard);\n\n/***/ }),\n\n/***/ 828:\n/***/ (function(module) {\n\nvar DOCUMENT_NODE_TYPE = 9;\n\n/**\n * A polyfill for Element.matches()\n */\nif (typeof Element !== 'undefined' && !Element.prototype.matches) {\n var proto = Element.prototype;\n\n proto.matches = proto.matchesSelector ||\n proto.mozMatchesSelector ||\n proto.msMatchesSelector ||\n proto.oMatchesSelector ||\n proto.webkitMatchesSelector;\n}\n\n/**\n * Finds the closest parent that matches a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @return {Function}\n */\nfunction closest (element, selector) {\n while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {\n if (typeof element.matches === 'function' &&\n element.matches(selector)) {\n return element;\n }\n element = element.parentNode;\n }\n}\n\nmodule.exports = closest;\n\n\n/***/ }),\n\n/***/ 438:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar closest = __webpack_require__(828);\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction _delegate(element, selector, type, callback, useCapture) {\n var listenerFn = listener.apply(this, arguments);\n\n element.addEventListener(type, listenerFn, useCapture);\n\n return {\n destroy: function() {\n element.removeEventListener(type, listenerFn, useCapture);\n }\n }\n}\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element|String|Array} [elements]\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction delegate(elements, selector, type, callback, useCapture) {\n // Handle the regular Element usage\n if (typeof elements.addEventListener === 'function') {\n return _delegate.apply(null, arguments);\n }\n\n // Handle Element-less usage, it defaults to global delegation\n if (typeof type === 'function') {\n // Use `document` as the first parameter, then apply arguments\n // This is a short way to .unshift `arguments` without running into deoptimizations\n return _delegate.bind(null, document).apply(null, arguments);\n }\n\n // Handle Selector-based usage\n if (typeof elements === 'string') {\n elements = document.querySelectorAll(elements);\n }\n\n // Handle Array-like based usage\n return Array.prototype.map.call(elements, function (element) {\n return _delegate(element, selector, type, callback, useCapture);\n });\n}\n\n/**\n * Finds closest match and invokes callback.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Function}\n */\nfunction listener(element, selector, type, callback) {\n return function(e) {\n e.delegateTarget = closest(e.target, selector);\n\n if (e.delegateTarget) {\n callback.call(element, e);\n }\n }\n}\n\nmodule.exports = delegate;\n\n\n/***/ }),\n\n/***/ 879:\n/***/ (function(__unused_webpack_module, exports) {\n\n/**\n * Check if argument is a HTML element.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.node = function(value) {\n return value !== undefined\n && value instanceof HTMLElement\n && value.nodeType === 1;\n};\n\n/**\n * Check if argument is a list of HTML elements.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.nodeList = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return value !== undefined\n && (type === '[object NodeList]' || type === '[object HTMLCollection]')\n && ('length' in value)\n && (value.length === 0 || exports.node(value[0]));\n};\n\n/**\n * Check if argument is a string.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.string = function(value) {\n return typeof value === 'string'\n || value instanceof String;\n};\n\n/**\n * Check if argument is a function.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.fn = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return type === '[object Function]';\n};\n\n\n/***/ }),\n\n/***/ 370:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar is = __webpack_require__(879);\nvar delegate = __webpack_require__(438);\n\n/**\n * Validates all params and calls the right\n * listener function based on its target type.\n *\n * @param {String|HTMLElement|HTMLCollection|NodeList} target\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listen(target, type, callback) {\n if (!target && !type && !callback) {\n throw new Error('Missing required arguments');\n }\n\n if (!is.string(type)) {\n throw new TypeError('Second argument must be a String');\n }\n\n if (!is.fn(callback)) {\n throw new TypeError('Third argument must be a Function');\n }\n\n if (is.node(target)) {\n return listenNode(target, type, callback);\n }\n else if (is.nodeList(target)) {\n return listenNodeList(target, type, callback);\n }\n else if (is.string(target)) {\n return listenSelector(target, type, callback);\n }\n else {\n throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList');\n }\n}\n\n/**\n * Adds an event listener to a HTML element\n * and returns a remove listener function.\n *\n * @param {HTMLElement} node\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNode(node, type, callback) {\n node.addEventListener(type, callback);\n\n return {\n destroy: function() {\n node.removeEventListener(type, callback);\n }\n }\n}\n\n/**\n * Add an event listener to a list of HTML elements\n * and returns a remove listener function.\n *\n * @param {NodeList|HTMLCollection} nodeList\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNodeList(nodeList, type, callback) {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.addEventListener(type, callback);\n });\n\n return {\n destroy: function() {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.removeEventListener(type, callback);\n });\n }\n }\n}\n\n/**\n * Add an event listener to a selector\n * and returns a remove listener function.\n *\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenSelector(selector, type, callback) {\n return delegate(document.body, selector, type, callback);\n}\n\nmodule.exports = listen;\n\n\n/***/ }),\n\n/***/ 817:\n/***/ (function(module) {\n\nfunction select(element) {\n var selectedText;\n\n if (element.nodeName === 'SELECT') {\n element.focus();\n\n selectedText = element.value;\n }\n else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {\n var isReadOnly = element.hasAttribute('readonly');\n\n if (!isReadOnly) {\n element.setAttribute('readonly', '');\n }\n\n element.select();\n element.setSelectionRange(0, element.value.length);\n\n if (!isReadOnly) {\n element.removeAttribute('readonly');\n }\n\n selectedText = element.value;\n }\n else {\n if (element.hasAttribute('contenteditable')) {\n element.focus();\n }\n\n var selection = window.getSelection();\n var range = document.createRange();\n\n range.selectNodeContents(element);\n selection.removeAllRanges();\n selection.addRange(range);\n\n selectedText = selection.toString();\n }\n\n return selectedText;\n}\n\nmodule.exports = select;\n\n\n/***/ }),\n\n/***/ 279:\n/***/ (function(module) {\n\nfunction E () {\n // Keep this empty so it's easier to inherit from\n // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)\n}\n\nE.prototype = {\n on: function (name, callback, ctx) {\n var e = this.e || (this.e = {});\n\n (e[name] || (e[name] = [])).push({\n fn: callback,\n ctx: ctx\n });\n\n return this;\n },\n\n once: function (name, callback, ctx) {\n var self = this;\n function listener () {\n self.off(name, listener);\n callback.apply(ctx, arguments);\n };\n\n listener._ = callback\n return this.on(name, listener, ctx);\n },\n\n emit: function (name) {\n var data = [].slice.call(arguments, 1);\n var evtArr = ((this.e || (this.e = {}))[name] || []).slice();\n var i = 0;\n var len = evtArr.length;\n\n for (i; i < len; i++) {\n evtArr[i].fn.apply(evtArr[i].ctx, data);\n }\n\n return this;\n },\n\n off: function (name, callback) {\n var e = this.e || (this.e = {});\n var evts = e[name];\n var liveEvents = [];\n\n if (evts && callback) {\n for (var i = 0, len = evts.length; i < len; i++) {\n if (evts[i].fn !== callback && evts[i].fn._ !== callback)\n liveEvents.push(evts[i]);\n }\n }\n\n // Remove event from queue to prevent memory leak\n // Suggested by https://github.com/lazd\n // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910\n\n (liveEvents.length)\n ? e[name] = liveEvents\n : delete e[name];\n\n return this;\n }\n};\n\nmodule.exports = E;\nmodule.exports.TinyEmitter = E;\n\n\n/***/ })\n\n/******/ \t});\n/************************************************************************/\n/******/ \t// The module cache\n/******/ \tvar __webpack_module_cache__ = {};\n/******/ \t\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(__webpack_module_cache__[moduleId]) {\n/******/ \t\t\treturn __webpack_module_cache__[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = __webpack_module_cache__[moduleId] = {\n/******/ \t\t\t// no module.id needed\n/******/ \t\t\t// no module.loaded needed\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/ \t\n/******/ \t\t// Execute the module function\n/******/ \t\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n/******/ \t\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/ \t\n/************************************************************************/\n/******/ \t/* webpack/runtime/compat get default export */\n/******/ \t!function() {\n/******/ \t\t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t\t__webpack_require__.n = function(module) {\n/******/ \t\t\tvar getter = module && module.__esModule ?\n/******/ \t\t\t\tfunction() { return module['default']; } :\n/******/ \t\t\t\tfunction() { return module; };\n/******/ \t\t\t__webpack_require__.d(getter, { a: getter });\n/******/ \t\t\treturn getter;\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/define property getters */\n/******/ \t!function() {\n/******/ \t\t// define getter functions for harmony exports\n/******/ \t\t__webpack_require__.d = function(exports, definition) {\n/******/ \t\t\tfor(var key in definition) {\n/******/ \t\t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n/******/ \t\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n/******/ \t\t\t\t}\n/******/ \t\t\t}\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/hasOwnProperty shorthand */\n/******/ \t!function() {\n/******/ \t\t__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }\n/******/ \t}();\n/******/ \t\n/************************************************************************/\n/******/ \t// module exports must be returned from runtime so entry inlining is disabled\n/******/ \t// startup\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(686);\n/******/ })()\n.default;\n});", "/*!\n * escape-html\n * Copyright(c) 2012-2013 TJ Holowaychuk\n * Copyright(c) 2015 Andreas Lubbe\n * Copyright(c) 2015 Tiancheng \"Timothy\" Gu\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module variables.\n * @private\n */\n\nvar matchHtmlRegExp = /[\"'&<>]/;\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = escapeHtml;\n\n/**\n * Escape special characters in the given string of html.\n *\n * @param {string} string The string to escape for inserting into HTML\n * @return {string}\n * @public\n */\n\nfunction escapeHtml(string) {\n var str = '' + string;\n var match = matchHtmlRegExp.exec(str);\n\n if (!match) {\n return str;\n }\n\n var escape;\n var html = '';\n var index = 0;\n var lastIndex = 0;\n\n for (index = match.index; index < str.length; index++) {\n switch (str.charCodeAt(index)) {\n case 34: // \"\n escape = '"';\n break;\n case 38: // &\n escape = '&';\n break;\n case 39: // '\n escape = ''';\n break;\n case 60: // <\n escape = '<';\n break;\n case 62: // >\n escape = '>';\n break;\n default:\n continue;\n }\n\n if (lastIndex !== index) {\n html += str.substring(lastIndex, index);\n }\n\n lastIndex = index + 1;\n html += escape;\n }\n\n return lastIndex !== index\n ? html + str.substring(lastIndex, index)\n : html;\n}\n", "/*\n * Copyright (c) 2016-2023 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport \"focus-visible\"\n\nimport {\n EMPTY,\n NEVER,\n Observable,\n Subject,\n defer,\n delay,\n filter,\n map,\n merge,\n mergeWith,\n shareReplay,\n switchMap\n} from \"rxjs\"\n\nimport { configuration, feature } from \"./_\"\nimport {\n at,\n getActiveElement,\n getOptionalElement,\n requestJSON,\n setLocation,\n setToggle,\n watchDocument,\n watchKeyboard,\n watchLocation,\n watchLocationTarget,\n watchMedia,\n watchPrint,\n watchScript,\n watchViewport\n} from \"./browser\"\nimport {\n getComponentElement,\n getComponentElements,\n mountAnnounce,\n mountBackToTop,\n mountConsent,\n mountContent,\n mountDialog,\n mountHeader,\n mountHeaderTitle,\n mountPalette,\n mountProgress,\n mountSearch,\n mountSearchHiglight,\n mountSidebar,\n mountSource,\n mountTableOfContents,\n mountTabs,\n watchHeader,\n watchMain\n} from \"./components\"\nimport {\n SearchIndex,\n setupClipboardJS,\n setupInstantNavigation,\n setupVersionSelector\n} from \"./integrations\"\nimport {\n patchIndeterminate,\n patchScrollfix,\n patchScrolllock\n} from \"./patches\"\nimport \"./polyfills\"\n\n/* ----------------------------------------------------------------------------\n * Functions - @todo refactor\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch search index\n *\n * @returns Search index observable\n */\nfunction fetchSearchIndex(): Observable {\n if (location.protocol === \"file:\") {\n return watchScript(\n `${new URL(\"search/search_index.js\", config.base)}`\n )\n .pipe(\n // @ts-ignore - @todo fix typings\n map(() => __index),\n shareReplay(1)\n )\n } else {\n return requestJSON(\n new URL(\"search/search_index.json\", config.base)\n )\n }\n}\n\n/* ----------------------------------------------------------------------------\n * Application\n * ------------------------------------------------------------------------- */\n\n/* Yay, JavaScript is available */\ndocument.documentElement.classList.remove(\"no-js\")\ndocument.documentElement.classList.add(\"js\")\n\n/* Set up navigation observables and subjects */\nconst document$ = watchDocument()\nconst location$ = watchLocation()\nconst target$ = watchLocationTarget(location$)\nconst keyboard$ = watchKeyboard()\n\n/* Set up media observables */\nconst viewport$ = watchViewport()\nconst tablet$ = watchMedia(\"(min-width: 960px)\")\nconst screen$ = watchMedia(\"(min-width: 1220px)\")\nconst print$ = watchPrint()\n\n/* Retrieve search index, if search is enabled */\nconst config = configuration()\nconst index$ = document.forms.namedItem(\"search\")\n ? fetchSearchIndex()\n : NEVER\n\n/* Set up Clipboard.js integration */\nconst alert$ = new Subject()\nsetupClipboardJS({ alert$ })\n\n/* Set up progress indicator */\nconst progress$ = new Subject()\n\n/* Set up instant navigation, if enabled */\nif (feature(\"navigation.instant\"))\n setupInstantNavigation({ location$, viewport$, progress$ })\n .subscribe(document$)\n\n/* Set up version selector */\nif (config.version?.provider === \"mike\")\n setupVersionSelector({ document$ })\n\n/* Always close drawer and search on navigation */\nmerge(location$, target$)\n .pipe(\n delay(125)\n )\n .subscribe(() => {\n setToggle(\"drawer\", false)\n setToggle(\"search\", false)\n })\n\n/* Set up global keyboard handlers */\nkeyboard$\n .pipe(\n filter(({ mode }) => mode === \"global\")\n )\n .subscribe(key => {\n switch (key.type) {\n\n /* Go to previous page */\n case \"p\":\n case \",\":\n const prev = getOptionalElement(\"link[rel=prev]\")\n if (typeof prev !== \"undefined\")\n setLocation(prev)\n break\n\n /* Go to next page */\n case \"n\":\n case \".\":\n const next = getOptionalElement(\"link[rel=next]\")\n if (typeof next !== \"undefined\")\n setLocation(next)\n break\n\n /* Expand navigation, see https://bit.ly/3ZjG5io */\n case \"Enter\":\n const active = getActiveElement()\n if (active instanceof HTMLLabelElement)\n active.click()\n }\n })\n\n/* Set up patches */\npatchIndeterminate({ document$, tablet$ })\npatchScrollfix({ document$ })\npatchScrolllock({ viewport$, tablet$ })\n\n/* Set up header and main area observable */\nconst header$ = watchHeader(getComponentElement(\"header\"), { viewport$ })\nconst main$ = document$\n .pipe(\n map(() => getComponentElement(\"main\")),\n switchMap(el => watchMain(el, { viewport$, header$ })),\n shareReplay(1)\n )\n\n/* Set up control component observables */\nconst control$ = merge(\n\n /* Consent */\n ...getComponentElements(\"consent\")\n .map(el => mountConsent(el, { target$ })),\n\n /* Dialog */\n ...getComponentElements(\"dialog\")\n .map(el => mountDialog(el, { alert$ })),\n\n /* Header */\n ...getComponentElements(\"header\")\n .map(el => mountHeader(el, { viewport$, header$, main$ })),\n\n /* Color palette */\n ...getComponentElements(\"palette\")\n .map(el => mountPalette(el)),\n\n /* Progress bar */\n ...getComponentElements(\"progress\")\n .map(el => mountProgress(el, { progress$ })),\n\n /* Search */\n ...getComponentElements(\"search\")\n .map(el => mountSearch(el, { index$, keyboard$ })),\n\n /* Repository information */\n ...getComponentElements(\"source\")\n .map(el => mountSource(el))\n)\n\n/* Set up content component observables */\nconst content$ = defer(() => merge(\n\n /* Announcement bar */\n ...getComponentElements(\"announce\")\n .map(el => mountAnnounce(el)),\n\n /* Content */\n ...getComponentElements(\"content\")\n .map(el => mountContent(el, { viewport$, target$, print$ })),\n\n /* Search highlighting */\n ...getComponentElements(\"content\")\n .map(el => feature(\"search.highlight\")\n ? mountSearchHiglight(el, { index$, location$ })\n : EMPTY\n ),\n\n /* Header title */\n ...getComponentElements(\"header-title\")\n .map(el => mountHeaderTitle(el, { viewport$, header$ })),\n\n /* Sidebar */\n ...getComponentElements(\"sidebar\")\n .map(el => el.getAttribute(\"data-md-type\") === \"navigation\"\n ? at(screen$, () => mountSidebar(el, { viewport$, header$, main$ }))\n : at(tablet$, () => mountSidebar(el, { viewport$, header$, main$ }))\n ),\n\n /* Navigation tabs */\n ...getComponentElements(\"tabs\")\n .map(el => mountTabs(el, { viewport$, header$ })),\n\n /* Table of contents */\n ...getComponentElements(\"toc\")\n .map(el => mountTableOfContents(el, {\n viewport$, header$, main$, target$\n })),\n\n /* Back-to-top button */\n ...getComponentElements(\"top\")\n .map(el => mountBackToTop(el, { viewport$, header$, main$, target$ }))\n))\n\n/* Set up component observables */\nconst component$ = document$\n .pipe(\n switchMap(() => content$),\n mergeWith(control$),\n shareReplay(1)\n )\n\n/* Subscribe to all components */\ncomponent$.subscribe()\n\n/* ----------------------------------------------------------------------------\n * Exports\n * ------------------------------------------------------------------------- */\n\nwindow.document$ = document$ /* Document observable */\nwindow.location$ = location$ /* Location subject */\nwindow.target$ = target$ /* Location target observable */\nwindow.keyboard$ = keyboard$ /* Keyboard observable */\nwindow.viewport$ = viewport$ /* Viewport observable */\nwindow.tablet$ = tablet$ /* Media tablet observable */\nwindow.screen$ = screen$ /* Media screen observable */\nwindow.print$ = print$ /* Media print observable */\nwindow.alert$ = alert$ /* Alert subject */\nwindow.progress$ = progress$ /* Progress indicator subject */\nwindow.component$ = component$ /* Component observable */\n", "/*! *****************************************************************************\r\nCopyright (c) Microsoft Corporation.\r\n\r\nPermission to use, copy, modify, and/or distribute this software for any\r\npurpose with or without fee is hereby granted.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\r\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\r\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\r\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\r\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\r\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\r\nPERFORMANCE OF THIS SOFTWARE.\r\n***************************************************************************** */\r\n/* global Reflect, Promise */\r\n\r\nvar extendStatics = function(d, b) {\r\n extendStatics = Object.setPrototypeOf ||\r\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\r\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\r\n return extendStatics(d, b);\r\n};\r\n\r\nexport function __extends(d, b) {\r\n if (typeof b !== \"function\" && b !== null)\r\n throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\r\n extendStatics(d, b);\r\n function __() { this.constructor = d; }\r\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\r\n}\r\n\r\nexport var __assign = function() {\r\n __assign = Object.assign || function __assign(t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\r\n }\r\n return t;\r\n }\r\n return __assign.apply(this, arguments);\r\n}\r\n\r\nexport function __rest(s, e) {\r\n var t = {};\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\r\n t[p] = s[p];\r\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\r\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\r\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\r\n t[p[i]] = s[p[i]];\r\n }\r\n return t;\r\n}\r\n\r\nexport function __decorate(decorators, target, key, desc) {\r\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\r\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\r\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\r\n return c > 3 && r && Object.defineProperty(target, key, r), r;\r\n}\r\n\r\nexport function __param(paramIndex, decorator) {\r\n return function (target, key) { decorator(target, key, paramIndex); }\r\n}\r\n\r\nexport function __metadata(metadataKey, metadataValue) {\r\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\r\n}\r\n\r\nexport function __awaiter(thisArg, _arguments, P, generator) {\r\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\r\n return new (P || (P = Promise))(function (resolve, reject) {\r\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\r\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\r\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\r\n step((generator = generator.apply(thisArg, _arguments || [])).next());\r\n });\r\n}\r\n\r\nexport function __generator(thisArg, body) {\r\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;\r\n return g = { next: verb(0), \"throw\": verb(1), \"return\": verb(2) }, typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\r\n function verb(n) { return function (v) { return step([n, v]); }; }\r\n function step(op) {\r\n if (f) throw new TypeError(\"Generator is already executing.\");\r\n while (_) try {\r\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\r\n if (y = 0, t) op = [op[0] & 2, t.value];\r\n switch (op[0]) {\r\n case 0: case 1: t = op; break;\r\n case 4: _.label++; return { value: op[1], done: false };\r\n case 5: _.label++; y = op[1]; op = [0]; continue;\r\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\r\n default:\r\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\r\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\r\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\r\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\r\n if (t[2]) _.ops.pop();\r\n _.trys.pop(); continue;\r\n }\r\n op = body.call(thisArg, _);\r\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\r\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\r\n }\r\n}\r\n\r\nexport var __createBinding = Object.create ? (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });\r\n}) : (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n o[k2] = m[k];\r\n});\r\n\r\nexport function __exportStar(m, o) {\r\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);\r\n}\r\n\r\nexport function __values(o) {\r\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\r\n if (m) return m.call(o);\r\n if (o && typeof o.length === \"number\") return {\r\n next: function () {\r\n if (o && i >= o.length) o = void 0;\r\n return { value: o && o[i++], done: !o };\r\n }\r\n };\r\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\r\n}\r\n\r\nexport function __read(o, n) {\r\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\r\n if (!m) return o;\r\n var i = m.call(o), r, ar = [], e;\r\n try {\r\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\r\n }\r\n catch (error) { e = { error: error }; }\r\n finally {\r\n try {\r\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\r\n }\r\n finally { if (e) throw e.error; }\r\n }\r\n return ar;\r\n}\r\n\r\n/** @deprecated */\r\nexport function __spread() {\r\n for (var ar = [], i = 0; i < arguments.length; i++)\r\n ar = ar.concat(__read(arguments[i]));\r\n return ar;\r\n}\r\n\r\n/** @deprecated */\r\nexport function __spreadArrays() {\r\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\r\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\r\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\r\n r[k] = a[j];\r\n return r;\r\n}\r\n\r\nexport function __spreadArray(to, from, pack) {\r\n if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {\r\n if (ar || !(i in from)) {\r\n if (!ar) ar = Array.prototype.slice.call(from, 0, i);\r\n ar[i] = from[i];\r\n }\r\n }\r\n return to.concat(ar || Array.prototype.slice.call(from));\r\n}\r\n\r\nexport function __await(v) {\r\n return this instanceof __await ? (this.v = v, this) : new __await(v);\r\n}\r\n\r\nexport function __asyncGenerator(thisArg, _arguments, generator) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\r\n return i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i;\r\n function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }\r\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\r\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\r\n function fulfill(value) { resume(\"next\", value); }\r\n function reject(value) { resume(\"throw\", value); }\r\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\r\n}\r\n\r\nexport function __asyncDelegator(o) {\r\n var i, p;\r\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\r\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === \"return\" } : f ? f(v) : v; } : f; }\r\n}\r\n\r\nexport function __asyncValues(o) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var m = o[Symbol.asyncIterator], i;\r\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\r\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\r\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\r\n}\r\n\r\nexport function __makeTemplateObject(cooked, raw) {\r\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\r\n return cooked;\r\n};\r\n\r\nvar __setModuleDefault = Object.create ? (function(o, v) {\r\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\r\n}) : function(o, v) {\r\n o[\"default\"] = v;\r\n};\r\n\r\nexport function __importStar(mod) {\r\n if (mod && mod.__esModule) return mod;\r\n var result = {};\r\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\r\n __setModuleDefault(result, mod);\r\n return result;\r\n}\r\n\r\nexport function __importDefault(mod) {\r\n return (mod && mod.__esModule) ? mod : { default: mod };\r\n}\r\n\r\nexport function __classPrivateFieldGet(receiver, state, kind, f) {\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a getter\");\r\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot read private member from an object whose class did not declare it\");\r\n return kind === \"m\" ? f : kind === \"a\" ? f.call(receiver) : f ? f.value : state.get(receiver);\r\n}\r\n\r\nexport function __classPrivateFieldSet(receiver, state, value, kind, f) {\r\n if (kind === \"m\") throw new TypeError(\"Private method is not writable\");\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a setter\");\r\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot write private member to an object whose class did not declare it\");\r\n return (kind === \"a\" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;\r\n}\r\n", "/**\n * Returns true if the object is a function.\n * @param value The value to check\n */\nexport function isFunction(value: any): value is (...args: any[]) => any {\n return typeof value === 'function';\n}\n", "/**\n * Used to create Error subclasses until the community moves away from ES5.\n *\n * This is because compiling from TypeScript down to ES5 has issues with subclassing Errors\n * as well as other built-in types: https://github.com/Microsoft/TypeScript/issues/12123\n *\n * @param createImpl A factory function to create the actual constructor implementation. The returned\n * function should be a named function that calls `_super` internally.\n */\nexport function createErrorClass(createImpl: (_super: any) => any): T {\n const _super = (instance: any) => {\n Error.call(instance);\n instance.stack = new Error().stack;\n };\n\n const ctorFunc = createImpl(_super);\n ctorFunc.prototype = Object.create(Error.prototype);\n ctorFunc.prototype.constructor = ctorFunc;\n return ctorFunc;\n}\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface UnsubscriptionError extends Error {\n readonly errors: any[];\n}\n\nexport interface UnsubscriptionErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (errors: any[]): UnsubscriptionError;\n}\n\n/**\n * An error thrown when one or more errors have occurred during the\n * `unsubscribe` of a {@link Subscription}.\n */\nexport const UnsubscriptionError: UnsubscriptionErrorCtor = createErrorClass(\n (_super) =>\n function UnsubscriptionErrorImpl(this: any, errors: (Error | string)[]) {\n _super(this);\n this.message = errors\n ? `${errors.length} errors occurred during unsubscription:\n${errors.map((err, i) => `${i + 1}) ${err.toString()}`).join('\\n ')}`\n : '';\n this.name = 'UnsubscriptionError';\n this.errors = errors;\n }\n);\n", "/**\n * Removes an item from an array, mutating it.\n * @param arr The array to remove the item from\n * @param item The item to remove\n */\nexport function arrRemove(arr: T[] | undefined | null, item: T) {\n if (arr) {\n const index = arr.indexOf(item);\n 0 <= index && arr.splice(index, 1);\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { UnsubscriptionError } from './util/UnsubscriptionError';\nimport { SubscriptionLike, TeardownLogic, Unsubscribable } from './types';\nimport { arrRemove } from './util/arrRemove';\n\n/**\n * Represents a disposable resource, such as the execution of an Observable. A\n * Subscription has one important method, `unsubscribe`, that takes no argument\n * and just disposes the resource held by the subscription.\n *\n * Additionally, subscriptions may be grouped together through the `add()`\n * method, which will attach a child Subscription to the current Subscription.\n * When a Subscription is unsubscribed, all its children (and its grandchildren)\n * will be unsubscribed as well.\n *\n * @class Subscription\n */\nexport class Subscription implements SubscriptionLike {\n /** @nocollapse */\n public static EMPTY = (() => {\n const empty = new Subscription();\n empty.closed = true;\n return empty;\n })();\n\n /**\n * A flag to indicate whether this Subscription has already been unsubscribed.\n */\n public closed = false;\n\n private _parentage: Subscription[] | Subscription | null = null;\n\n /**\n * The list of registered finalizers to execute upon unsubscription. Adding and removing from this\n * list occurs in the {@link #add} and {@link #remove} methods.\n */\n private _finalizers: Exclude[] | null = null;\n\n /**\n * @param initialTeardown A function executed first as part of the finalization\n * process that is kicked off when {@link #unsubscribe} is called.\n */\n constructor(private initialTeardown?: () => void) {}\n\n /**\n * Disposes the resources held by the subscription. May, for instance, cancel\n * an ongoing Observable execution or cancel any other type of work that\n * started when the Subscription was created.\n * @return {void}\n */\n unsubscribe(): void {\n let errors: any[] | undefined;\n\n if (!this.closed) {\n this.closed = true;\n\n // Remove this from it's parents.\n const { _parentage } = this;\n if (_parentage) {\n this._parentage = null;\n if (Array.isArray(_parentage)) {\n for (const parent of _parentage) {\n parent.remove(this);\n }\n } else {\n _parentage.remove(this);\n }\n }\n\n const { initialTeardown: initialFinalizer } = this;\n if (isFunction(initialFinalizer)) {\n try {\n initialFinalizer();\n } catch (e) {\n errors = e instanceof UnsubscriptionError ? e.errors : [e];\n }\n }\n\n const { _finalizers } = this;\n if (_finalizers) {\n this._finalizers = null;\n for (const finalizer of _finalizers) {\n try {\n execFinalizer(finalizer);\n } catch (err) {\n errors = errors ?? [];\n if (err instanceof UnsubscriptionError) {\n errors = [...errors, ...err.errors];\n } else {\n errors.push(err);\n }\n }\n }\n }\n\n if (errors) {\n throw new UnsubscriptionError(errors);\n }\n }\n }\n\n /**\n * Adds a finalizer to this subscription, so that finalization will be unsubscribed/called\n * when this subscription is unsubscribed. If this subscription is already {@link #closed},\n * because it has already been unsubscribed, then whatever finalizer is passed to it\n * will automatically be executed (unless the finalizer itself is also a closed subscription).\n *\n * Closed Subscriptions cannot be added as finalizers to any subscription. Adding a closed\n * subscription to a any subscription will result in no operation. (A noop).\n *\n * Adding a subscription to itself, or adding `null` or `undefined` will not perform any\n * operation at all. (A noop).\n *\n * `Subscription` instances that are added to this instance will automatically remove themselves\n * if they are unsubscribed. Functions and {@link Unsubscribable} objects that you wish to remove\n * will need to be removed manually with {@link #remove}\n *\n * @param teardown The finalization logic to add to this subscription.\n */\n add(teardown: TeardownLogic): void {\n // Only add the finalizer if it's not undefined\n // and don't add a subscription to itself.\n if (teardown && teardown !== this) {\n if (this.closed) {\n // If this subscription is already closed,\n // execute whatever finalizer is handed to it automatically.\n execFinalizer(teardown);\n } else {\n if (teardown instanceof Subscription) {\n // We don't add closed subscriptions, and we don't add the same subscription\n // twice. Subscription unsubscribe is idempotent.\n if (teardown.closed || teardown._hasParent(this)) {\n return;\n }\n teardown._addParent(this);\n }\n (this._finalizers = this._finalizers ?? []).push(teardown);\n }\n }\n }\n\n /**\n * Checks to see if a this subscription already has a particular parent.\n * This will signal that this subscription has already been added to the parent in question.\n * @param parent the parent to check for\n */\n private _hasParent(parent: Subscription) {\n const { _parentage } = this;\n return _parentage === parent || (Array.isArray(_parentage) && _parentage.includes(parent));\n }\n\n /**\n * Adds a parent to this subscription so it can be removed from the parent if it\n * unsubscribes on it's own.\n *\n * NOTE: THIS ASSUMES THAT {@link _hasParent} HAS ALREADY BEEN CHECKED.\n * @param parent The parent subscription to add\n */\n private _addParent(parent: Subscription) {\n const { _parentage } = this;\n this._parentage = Array.isArray(_parentage) ? (_parentage.push(parent), _parentage) : _parentage ? [_parentage, parent] : parent;\n }\n\n /**\n * Called on a child when it is removed via {@link #remove}.\n * @param parent The parent to remove\n */\n private _removeParent(parent: Subscription) {\n const { _parentage } = this;\n if (_parentage === parent) {\n this._parentage = null;\n } else if (Array.isArray(_parentage)) {\n arrRemove(_parentage, parent);\n }\n }\n\n /**\n * Removes a finalizer from this subscription that was previously added with the {@link #add} method.\n *\n * Note that `Subscription` instances, when unsubscribed, will automatically remove themselves\n * from every other `Subscription` they have been added to. This means that using the `remove` method\n * is not a common thing and should be used thoughtfully.\n *\n * If you add the same finalizer instance of a function or an unsubscribable object to a `Subscription` instance\n * more than once, you will need to call `remove` the same number of times to remove all instances.\n *\n * All finalizer instances are removed to free up memory upon unsubscription.\n *\n * @param teardown The finalizer to remove from this subscription\n */\n remove(teardown: Exclude): void {\n const { _finalizers } = this;\n _finalizers && arrRemove(_finalizers, teardown);\n\n if (teardown instanceof Subscription) {\n teardown._removeParent(this);\n }\n }\n}\n\nexport const EMPTY_SUBSCRIPTION = Subscription.EMPTY;\n\nexport function isSubscription(value: any): value is Subscription {\n return (\n value instanceof Subscription ||\n (value && 'closed' in value && isFunction(value.remove) && isFunction(value.add) && isFunction(value.unsubscribe))\n );\n}\n\nfunction execFinalizer(finalizer: Unsubscribable | (() => void)) {\n if (isFunction(finalizer)) {\n finalizer();\n } else {\n finalizer.unsubscribe();\n }\n}\n", "import { Subscriber } from './Subscriber';\nimport { ObservableNotification } from './types';\n\n/**\n * The {@link GlobalConfig} object for RxJS. It is used to configure things\n * like how to react on unhandled errors.\n */\nexport const config: GlobalConfig = {\n onUnhandledError: null,\n onStoppedNotification: null,\n Promise: undefined,\n useDeprecatedSynchronousErrorHandling: false,\n useDeprecatedNextContext: false,\n};\n\n/**\n * The global configuration object for RxJS, used to configure things\n * like how to react on unhandled errors. Accessible via {@link config}\n * object.\n */\nexport interface GlobalConfig {\n /**\n * A registration point for unhandled errors from RxJS. These are errors that\n * cannot were not handled by consuming code in the usual subscription path. For\n * example, if you have this configured, and you subscribe to an observable without\n * providing an error handler, errors from that subscription will end up here. This\n * will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onUnhandledError: ((err: any) => void) | null;\n\n /**\n * A registration point for notifications that cannot be sent to subscribers because they\n * have completed, errored or have been explicitly unsubscribed. By default, next, complete\n * and error notifications sent to stopped subscribers are noops. However, sometimes callers\n * might want a different behavior. For example, with sources that attempt to report errors\n * to stopped subscribers, a caller can configure RxJS to throw an unhandled error instead.\n * This will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onStoppedNotification: ((notification: ObservableNotification, subscriber: Subscriber) => void) | null;\n\n /**\n * The promise constructor used by default for {@link Observable#toPromise toPromise} and {@link Observable#forEach forEach}\n * methods.\n *\n * @deprecated As of version 8, RxJS will no longer support this sort of injection of a\n * Promise constructor. If you need a Promise implementation other than native promises,\n * please polyfill/patch Promise as you see appropriate. Will be removed in v8.\n */\n Promise?: PromiseConstructorLike;\n\n /**\n * If true, turns on synchronous error rethrowing, which is a deprecated behavior\n * in v6 and higher. This behavior enables bad patterns like wrapping a subscribe\n * call in a try/catch block. It also enables producer interference, a nasty bug\n * where a multicast can be broken for all observers by a downstream consumer with\n * an unhandled error. DO NOT USE THIS FLAG UNLESS IT'S NEEDED TO BUY TIME\n * FOR MIGRATION REASONS.\n *\n * @deprecated As of version 8, RxJS will no longer support synchronous throwing\n * of unhandled errors. All errors will be thrown on a separate call stack to prevent bad\n * behaviors described above. Will be removed in v8.\n */\n useDeprecatedSynchronousErrorHandling: boolean;\n\n /**\n * If true, enables an as-of-yet undocumented feature from v5: The ability to access\n * `unsubscribe()` via `this` context in `next` functions created in observers passed\n * to `subscribe`.\n *\n * This is being removed because the performance was severely problematic, and it could also cause\n * issues when types other than POJOs are passed to subscribe as subscribers, as they will likely have\n * their `this` context overwritten.\n *\n * @deprecated As of version 8, RxJS will no longer support altering the\n * context of next functions provided as part of an observer to Subscribe. Instead,\n * you will have access to a subscription or a signal or token that will allow you to do things like\n * unsubscribe and test closed status. Will be removed in v8.\n */\n useDeprecatedNextContext: boolean;\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetTimeoutFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearTimeoutFunction = (handle: TimerHandle) => void;\n\ninterface TimeoutProvider {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n delegate:\n | {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n }\n | undefined;\n}\n\nexport const timeoutProvider: TimeoutProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setTimeout(handler: () => void, timeout?: number, ...args) {\n const { delegate } = timeoutProvider;\n if (delegate?.setTimeout) {\n return delegate.setTimeout(handler, timeout, ...args);\n }\n return setTimeout(handler, timeout, ...args);\n },\n clearTimeout(handle) {\n const { delegate } = timeoutProvider;\n return (delegate?.clearTimeout || clearTimeout)(handle as any);\n },\n delegate: undefined,\n};\n", "import { config } from '../config';\nimport { timeoutProvider } from '../scheduler/timeoutProvider';\n\n/**\n * Handles an error on another job either with the user-configured {@link onUnhandledError},\n * or by throwing it on that new job so it can be picked up by `window.onerror`, `process.on('error')`, etc.\n *\n * This should be called whenever there is an error that is out-of-band with the subscription\n * or when an error hits a terminal boundary of the subscription and no error handler was provided.\n *\n * @param err the error to report\n */\nexport function reportUnhandledError(err: any) {\n timeoutProvider.setTimeout(() => {\n const { onUnhandledError } = config;\n if (onUnhandledError) {\n // Execute the user-configured error handler.\n onUnhandledError(err);\n } else {\n // Throw so it is picked up by the runtime's uncaught error mechanism.\n throw err;\n }\n });\n}\n", "/* tslint:disable:no-empty */\nexport function noop() { }\n", "import { CompleteNotification, NextNotification, ErrorNotification } from './types';\n\n/**\n * A completion object optimized for memory use and created to be the\n * same \"shape\" as other notifications in v8.\n * @internal\n */\nexport const COMPLETE_NOTIFICATION = (() => createNotification('C', undefined, undefined) as CompleteNotification)();\n\n/**\n * Internal use only. Creates an optimized error notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function errorNotification(error: any): ErrorNotification {\n return createNotification('E', undefined, error) as any;\n}\n\n/**\n * Internal use only. Creates an optimized next notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function nextNotification(value: T) {\n return createNotification('N', value, undefined) as NextNotification;\n}\n\n/**\n * Ensures that all notifications created internally have the same \"shape\" in v8.\n *\n * TODO: This is only exported to support a crazy legacy test in `groupBy`.\n * @internal\n */\nexport function createNotification(kind: 'N' | 'E' | 'C', value: any, error: any) {\n return {\n kind,\n value,\n error,\n };\n}\n", "import { config } from '../config';\n\nlet context: { errorThrown: boolean; error: any } | null = null;\n\n/**\n * Handles dealing with errors for super-gross mode. Creates a context, in which\n * any synchronously thrown errors will be passed to {@link captureError}. Which\n * will record the error such that it will be rethrown after the call back is complete.\n * TODO: Remove in v8\n * @param cb An immediately executed function.\n */\nexport function errorContext(cb: () => void) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n const isRoot = !context;\n if (isRoot) {\n context = { errorThrown: false, error: null };\n }\n cb();\n if (isRoot) {\n const { errorThrown, error } = context!;\n context = null;\n if (errorThrown) {\n throw error;\n }\n }\n } else {\n // This is the general non-deprecated path for everyone that\n // isn't crazy enough to use super-gross mode (useDeprecatedSynchronousErrorHandling)\n cb();\n }\n}\n\n/**\n * Captures errors only in super-gross mode.\n * @param err the error to capture\n */\nexport function captureError(err: any) {\n if (config.useDeprecatedSynchronousErrorHandling && context) {\n context.errorThrown = true;\n context.error = err;\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { Observer, ObservableNotification } from './types';\nimport { isSubscription, Subscription } from './Subscription';\nimport { config } from './config';\nimport { reportUnhandledError } from './util/reportUnhandledError';\nimport { noop } from './util/noop';\nimport { nextNotification, errorNotification, COMPLETE_NOTIFICATION } from './NotificationFactories';\nimport { timeoutProvider } from './scheduler/timeoutProvider';\nimport { captureError } from './util/errorContext';\n\n/**\n * Implements the {@link Observer} interface and extends the\n * {@link Subscription} class. While the {@link Observer} is the public API for\n * consuming the values of an {@link Observable}, all Observers get converted to\n * a Subscriber, in order to provide Subscription-like capabilities such as\n * `unsubscribe`. Subscriber is a common type in RxJS, and crucial for\n * implementing operators, but it is rarely used as a public API.\n *\n * @class Subscriber\n */\nexport class Subscriber extends Subscription implements Observer {\n /**\n * A static factory for a Subscriber, given a (potentially partial) definition\n * of an Observer.\n * @param next The `next` callback of an Observer.\n * @param error The `error` callback of an\n * Observer.\n * @param complete The `complete` callback of an\n * Observer.\n * @return A Subscriber wrapping the (partially defined)\n * Observer represented by the given arguments.\n * @nocollapse\n * @deprecated Do not use. Will be removed in v8. There is no replacement for this\n * method, and there is no reason to be creating instances of `Subscriber` directly.\n * If you have a specific use case, please file an issue.\n */\n static create(next?: (x?: T) => void, error?: (e?: any) => void, complete?: () => void): Subscriber {\n return new SafeSubscriber(next, error, complete);\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected isStopped: boolean = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected destination: Subscriber | Observer; // this `any` is the escape hatch to erase extra type param (e.g. R)\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * There is no reason to directly create an instance of Subscriber. This type is exported for typings reasons.\n */\n constructor(destination?: Subscriber | Observer) {\n super();\n if (destination) {\n this.destination = destination;\n // Automatically chain subscriptions together here.\n // if destination is a Subscription, then it is a Subscriber.\n if (isSubscription(destination)) {\n destination.add(this);\n }\n } else {\n this.destination = EMPTY_OBSERVER;\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `next` from\n * the Observable, with a value. The Observable may call this method 0 or more\n * times.\n * @param {T} [value] The `next` value.\n * @return {void}\n */\n next(value?: T): void {\n if (this.isStopped) {\n handleStoppedNotification(nextNotification(value), this);\n } else {\n this._next(value!);\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `error` from\n * the Observable, with an attached `Error`. Notifies the Observer that\n * the Observable has experienced an error condition.\n * @param {any} [err] The `error` exception.\n * @return {void}\n */\n error(err?: any): void {\n if (this.isStopped) {\n handleStoppedNotification(errorNotification(err), this);\n } else {\n this.isStopped = true;\n this._error(err);\n }\n }\n\n /**\n * The {@link Observer} callback to receive a valueless notification of type\n * `complete` from the Observable. Notifies the Observer that the Observable\n * has finished sending push-based notifications.\n * @return {void}\n */\n complete(): void {\n if (this.isStopped) {\n handleStoppedNotification(COMPLETE_NOTIFICATION, this);\n } else {\n this.isStopped = true;\n this._complete();\n }\n }\n\n unsubscribe(): void {\n if (!this.closed) {\n this.isStopped = true;\n super.unsubscribe();\n this.destination = null!;\n }\n }\n\n protected _next(value: T): void {\n this.destination.next(value);\n }\n\n protected _error(err: any): void {\n try {\n this.destination.error(err);\n } finally {\n this.unsubscribe();\n }\n }\n\n protected _complete(): void {\n try {\n this.destination.complete();\n } finally {\n this.unsubscribe();\n }\n }\n}\n\n/**\n * This bind is captured here because we want to be able to have\n * compatibility with monoid libraries that tend to use a method named\n * `bind`. In particular, a library called Monio requires this.\n */\nconst _bind = Function.prototype.bind;\n\nfunction bind any>(fn: Fn, thisArg: any): Fn {\n return _bind.call(fn, thisArg);\n}\n\n/**\n * Internal optimization only, DO NOT EXPOSE.\n * @internal\n */\nclass ConsumerObserver implements Observer {\n constructor(private partialObserver: Partial>) {}\n\n next(value: T): void {\n const { partialObserver } = this;\n if (partialObserver.next) {\n try {\n partialObserver.next(value);\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n\n error(err: any): void {\n const { partialObserver } = this;\n if (partialObserver.error) {\n try {\n partialObserver.error(err);\n } catch (error) {\n handleUnhandledError(error);\n }\n } else {\n handleUnhandledError(err);\n }\n }\n\n complete(): void {\n const { partialObserver } = this;\n if (partialObserver.complete) {\n try {\n partialObserver.complete();\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n}\n\nexport class SafeSubscriber extends Subscriber {\n constructor(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((e?: any) => void) | null,\n complete?: (() => void) | null\n ) {\n super();\n\n let partialObserver: Partial>;\n if (isFunction(observerOrNext) || !observerOrNext) {\n // The first argument is a function, not an observer. The next\n // two arguments *could* be observers, or they could be empty.\n partialObserver = {\n next: (observerOrNext ?? undefined) as (((value: T) => void) | undefined),\n error: error ?? undefined,\n complete: complete ?? undefined,\n };\n } else {\n // The first argument is a partial observer.\n let context: any;\n if (this && config.useDeprecatedNextContext) {\n // This is a deprecated path that made `this.unsubscribe()` available in\n // next handler functions passed to subscribe. This only exists behind a flag\n // now, as it is *very* slow.\n context = Object.create(observerOrNext);\n context.unsubscribe = () => this.unsubscribe();\n partialObserver = {\n next: observerOrNext.next && bind(observerOrNext.next, context),\n error: observerOrNext.error && bind(observerOrNext.error, context),\n complete: observerOrNext.complete && bind(observerOrNext.complete, context),\n };\n } else {\n // The \"normal\" path. Just use the partial observer directly.\n partialObserver = observerOrNext;\n }\n }\n\n // Wrap the partial observer to ensure it's a full observer, and\n // make sure proper error handling is accounted for.\n this.destination = new ConsumerObserver(partialObserver);\n }\n}\n\nfunction handleUnhandledError(error: any) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n captureError(error);\n } else {\n // Ideal path, we report this as an unhandled error,\n // which is thrown on a new call stack.\n reportUnhandledError(error);\n }\n}\n\n/**\n * An error handler used when no error handler was supplied\n * to the SafeSubscriber -- meaning no error handler was supplied\n * do the `subscribe` call on our observable.\n * @param err The error to handle\n */\nfunction defaultErrorHandler(err: any) {\n throw err;\n}\n\n/**\n * A handler for notifications that cannot be sent to a stopped subscriber.\n * @param notification The notification being sent\n * @param subscriber The stopped subscriber\n */\nfunction handleStoppedNotification(notification: ObservableNotification, subscriber: Subscriber) {\n const { onStoppedNotification } = config;\n onStoppedNotification && timeoutProvider.setTimeout(() => onStoppedNotification(notification, subscriber));\n}\n\n/**\n * The observer used as a stub for subscriptions where the user did not\n * pass any arguments to `subscribe`. Comes with the default error handling\n * behavior.\n */\nexport const EMPTY_OBSERVER: Readonly> & { closed: true } = {\n closed: true,\n next: noop,\n error: defaultErrorHandler,\n complete: noop,\n};\n", "/**\n * Symbol.observable or a string \"@@observable\". Used for interop\n *\n * @deprecated We will no longer be exporting this symbol in upcoming versions of RxJS.\n * Instead polyfill and use Symbol.observable directly *or* use https://www.npmjs.com/package/symbol-observable\n */\nexport const observable: string | symbol = (() => (typeof Symbol === 'function' && Symbol.observable) || '@@observable')();\n", "/**\n * This function takes one parameter and just returns it. Simply put,\n * this is like `(x: T): T => x`.\n *\n * ## Examples\n *\n * This is useful in some cases when using things like `mergeMap`\n *\n * ```ts\n * import { interval, take, map, range, mergeMap, identity } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(5));\n *\n * const result$ = source$.pipe(\n * map(i => range(i)),\n * mergeMap(identity) // same as mergeMap(x => x)\n * );\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * Or when you want to selectively apply an operator\n *\n * ```ts\n * import { interval, take, identity } from 'rxjs';\n *\n * const shouldLimit = () => Math.random() < 0.5;\n *\n * const source$ = interval(1000);\n *\n * const result$ = source$.pipe(shouldLimit() ? take(5) : identity);\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * @param x Any value that is returned by this function\n * @returns The value passed as the first parameter to this function\n */\nexport function identity(x: T): T {\n return x;\n}\n", "import { identity } from './identity';\nimport { UnaryFunction } from '../types';\n\nexport function pipe(): typeof identity;\nexport function pipe(fn1: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction, fn3: UnaryFunction): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction,\n ...fns: UnaryFunction[]\n): UnaryFunction;\n\n/**\n * pipe() can be called on one or more functions, each of which can take one argument (\"UnaryFunction\")\n * and uses it to return a value.\n * It returns a function that takes one argument, passes it to the first UnaryFunction, and then\n * passes the result to the next one, passes that result to the next one, and so on. \n */\nexport function pipe(...fns: Array>): UnaryFunction {\n return pipeFromArray(fns);\n}\n\n/** @internal */\nexport function pipeFromArray(fns: Array>): UnaryFunction {\n if (fns.length === 0) {\n return identity as UnaryFunction;\n }\n\n if (fns.length === 1) {\n return fns[0];\n }\n\n return function piped(input: T): R {\n return fns.reduce((prev: any, fn: UnaryFunction) => fn(prev), input as any);\n };\n}\n", "import { Operator } from './Operator';\nimport { SafeSubscriber, Subscriber } from './Subscriber';\nimport { isSubscription, Subscription } from './Subscription';\nimport { TeardownLogic, OperatorFunction, Subscribable, Observer } from './types';\nimport { observable as Symbol_observable } from './symbol/observable';\nimport { pipeFromArray } from './util/pipe';\nimport { config } from './config';\nimport { isFunction } from './util/isFunction';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A representation of any set of values over any amount of time. This is the most basic building block\n * of RxJS.\n *\n * @class Observable\n */\nexport class Observable implements Subscribable {\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n source: Observable | undefined;\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n operator: Operator | undefined;\n\n /**\n * @constructor\n * @param {Function} subscribe the function that is called when the Observable is\n * initially subscribed to. This function is given a Subscriber, to which new values\n * can be `next`ed, or an `error` method can be called to raise an error, or\n * `complete` can be called to notify of a successful completion.\n */\n constructor(subscribe?: (this: Observable, subscriber: Subscriber) => TeardownLogic) {\n if (subscribe) {\n this._subscribe = subscribe;\n }\n }\n\n // HACK: Since TypeScript inherits static properties too, we have to\n // fight against TypeScript here so Subject can have a different static create signature\n /**\n * Creates a new Observable by calling the Observable constructor\n * @owner Observable\n * @method create\n * @param {Function} subscribe? the subscriber function to be passed to the Observable constructor\n * @return {Observable} a new observable\n * @nocollapse\n * @deprecated Use `new Observable()` instead. Will be removed in v8.\n */\n static create: (...args: any[]) => any = (subscribe?: (subscriber: Subscriber) => TeardownLogic) => {\n return new Observable(subscribe);\n };\n\n /**\n * Creates a new Observable, with this Observable instance as the source, and the passed\n * operator defined as the new observable's operator.\n * @method lift\n * @param operator the operator defining the operation to take on the observable\n * @return a new observable with the Operator applied\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * If you have implemented an operator using `lift`, it is recommended that you create an\n * operator by simply returning `new Observable()` directly. See \"Creating new operators from\n * scratch\" section here: https://rxjs.dev/guide/operators\n */\n lift(operator?: Operator): Observable {\n const observable = new Observable();\n observable.source = this;\n observable.operator = operator;\n return observable;\n }\n\n subscribe(observerOrNext?: Partial> | ((value: T) => void)): Subscription;\n /** @deprecated Instead of passing separate callback arguments, use an observer argument. Signatures taking separate callback arguments will be removed in v8. Details: https://rxjs.dev/deprecations/subscribe-arguments */\n subscribe(next?: ((value: T) => void) | null, error?: ((error: any) => void) | null, complete?: (() => void) | null): Subscription;\n /**\n * Invokes an execution of an Observable and registers Observer handlers for notifications it will emit.\n *\n * Use it when you have all these Observables, but still nothing is happening.\n *\n * `subscribe` is not a regular operator, but a method that calls Observable's internal `subscribe` function. It\n * might be for example a function that you passed to Observable's constructor, but most of the time it is\n * a library implementation, which defines what will be emitted by an Observable, and when it be will emitted. This means\n * that calling `subscribe` is actually the moment when Observable starts its work, not when it is created, as it is often\n * the thought.\n *\n * Apart from starting the execution of an Observable, this method allows you to listen for values\n * that an Observable emits, as well as for when it completes or errors. You can achieve this in two\n * of the following ways.\n *\n * The first way is creating an object that implements {@link Observer} interface. It should have methods\n * defined by that interface, but note that it should be just a regular JavaScript object, which you can create\n * yourself in any way you want (ES6 class, classic function constructor, object literal etc.). In particular, do\n * not attempt to use any RxJS implementation details to create Observers - you don't need them. Remember also\n * that your object does not have to implement all methods. If you find yourself creating a method that doesn't\n * do anything, you can simply omit it. Note however, if the `error` method is not provided and an error happens,\n * it will be thrown asynchronously. Errors thrown asynchronously cannot be caught using `try`/`catch`. Instead,\n * use the {@link onUnhandledError} configuration option or use a runtime handler (like `window.onerror` or\n * `process.on('error)`) to be notified of unhandled errors. Because of this, it's recommended that you provide\n * an `error` method to avoid missing thrown errors.\n *\n * The second way is to give up on Observer object altogether and simply provide callback functions in place of its methods.\n * This means you can provide three functions as arguments to `subscribe`, where the first function is equivalent\n * of a `next` method, the second of an `error` method and the third of a `complete` method. Just as in case of an Observer,\n * if you do not need to listen for something, you can omit a function by passing `undefined` or `null`,\n * since `subscribe` recognizes these functions by where they were placed in function call. When it comes\n * to the `error` function, as with an Observer, if not provided, errors emitted by an Observable will be thrown asynchronously.\n *\n * You can, however, subscribe with no parameters at all. This may be the case where you're not interested in terminal events\n * and you also handled emissions internally by using operators (e.g. using `tap`).\n *\n * Whichever style of calling `subscribe` you use, in both cases it returns a Subscription object.\n * This object allows you to call `unsubscribe` on it, which in turn will stop the work that an Observable does and will clean\n * up all resources that an Observable used. Note that cancelling a subscription will not call `complete` callback\n * provided to `subscribe` function, which is reserved for a regular completion signal that comes from an Observable.\n *\n * Remember that callbacks provided to `subscribe` are not guaranteed to be called asynchronously.\n * It is an Observable itself that decides when these functions will be called. For example {@link of}\n * by default emits all its values synchronously. Always check documentation for how given Observable\n * will behave when subscribed and if its default behavior can be modified with a `scheduler`.\n *\n * #### Examples\n *\n * Subscribe with an {@link guide/observer Observer}\n *\n * ```ts\n * import { of } from 'rxjs';\n *\n * const sumObserver = {\n * sum: 0,\n * next(value) {\n * console.log('Adding: ' + value);\n * this.sum = this.sum + value;\n * },\n * error() {\n * // We actually could just remove this method,\n * // since we do not really care about errors right now.\n * },\n * complete() {\n * console.log('Sum equals: ' + this.sum);\n * }\n * };\n *\n * of(1, 2, 3) // Synchronously emits 1, 2, 3 and then completes.\n * .subscribe(sumObserver);\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Subscribe with functions ({@link deprecations/subscribe-arguments deprecated})\n *\n * ```ts\n * import { of } from 'rxjs'\n *\n * let sum = 0;\n *\n * of(1, 2, 3).subscribe(\n * value => {\n * console.log('Adding: ' + value);\n * sum = sum + value;\n * },\n * undefined,\n * () => console.log('Sum equals: ' + sum)\n * );\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Cancel a subscription\n *\n * ```ts\n * import { interval } from 'rxjs';\n *\n * const subscription = interval(1000).subscribe({\n * next(num) {\n * console.log(num)\n * },\n * complete() {\n * // Will not be called, even when cancelling subscription.\n * console.log('completed!');\n * }\n * });\n *\n * setTimeout(() => {\n * subscription.unsubscribe();\n * console.log('unsubscribed!');\n * }, 2500);\n *\n * // Logs:\n * // 0 after 1s\n * // 1 after 2s\n * // 'unsubscribed!' after 2.5s\n * ```\n *\n * @param {Observer|Function} observerOrNext (optional) Either an observer with methods to be called,\n * or the first of three possible handlers, which is the handler for each value emitted from the subscribed\n * Observable.\n * @param {Function} error (optional) A handler for a terminal event resulting from an error. If no error handler is provided,\n * the error will be thrown asynchronously as unhandled.\n * @param {Function} complete (optional) A handler for a terminal event resulting from successful completion.\n * @return {Subscription} a subscription reference to the registered handlers\n * @method subscribe\n */\n subscribe(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((error: any) => void) | null,\n complete?: (() => void) | null\n ): Subscription {\n const subscriber = isSubscriber(observerOrNext) ? observerOrNext : new SafeSubscriber(observerOrNext, error, complete);\n\n errorContext(() => {\n const { operator, source } = this;\n subscriber.add(\n operator\n ? // We're dealing with a subscription in the\n // operator chain to one of our lifted operators.\n operator.call(subscriber, source)\n : source\n ? // If `source` has a value, but `operator` does not, something that\n // had intimate knowledge of our API, like our `Subject`, must have\n // set it. We're going to just call `_subscribe` directly.\n this._subscribe(subscriber)\n : // In all other cases, we're likely wrapping a user-provided initializer\n // function, so we need to catch errors and handle them appropriately.\n this._trySubscribe(subscriber)\n );\n });\n\n return subscriber;\n }\n\n /** @internal */\n protected _trySubscribe(sink: Subscriber): TeardownLogic {\n try {\n return this._subscribe(sink);\n } catch (err) {\n // We don't need to return anything in this case,\n // because it's just going to try to `add()` to a subscription\n // above.\n sink.error(err);\n }\n }\n\n /**\n * Used as a NON-CANCELLABLE means of subscribing to an observable, for use with\n * APIs that expect promises, like `async/await`. You cannot unsubscribe from this.\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * #### Example\n *\n * ```ts\n * import { interval, take } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(4));\n *\n * async function getTotal() {\n * let total = 0;\n *\n * await source$.forEach(value => {\n * total += value;\n * console.log('observable -> ' + value);\n * });\n *\n * return total;\n * }\n *\n * getTotal().then(\n * total => console.log('Total: ' + total)\n * );\n *\n * // Expected:\n * // 'observable -> 0'\n * // 'observable -> 1'\n * // 'observable -> 2'\n * // 'observable -> 3'\n * // 'Total: 6'\n * ```\n *\n * @param next a handler for each value emitted by the observable\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n */\n forEach(next: (value: T) => void): Promise;\n\n /**\n * @param next a handler for each value emitted by the observable\n * @param promiseCtor a constructor function used to instantiate the Promise\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n * @deprecated Passing a Promise constructor will no longer be available\n * in upcoming versions of RxJS. This is because it adds weight to the library, for very\n * little benefit. If you need this functionality, it is recommended that you either\n * polyfill Promise, or you create an adapter to convert the returned native promise\n * to whatever promise implementation you wanted. Will be removed in v8.\n */\n forEach(next: (value: T) => void, promiseCtor: PromiseConstructorLike): Promise;\n\n forEach(next: (value: T) => void, promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n const subscriber = new SafeSubscriber({\n next: (value) => {\n try {\n next(value);\n } catch (err) {\n reject(err);\n subscriber.unsubscribe();\n }\n },\n error: reject,\n complete: resolve,\n });\n this.subscribe(subscriber);\n }) as Promise;\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): TeardownLogic {\n return this.source?.subscribe(subscriber);\n }\n\n /**\n * An interop point defined by the es7-observable spec https://github.com/zenparsing/es-observable\n * @method Symbol.observable\n * @return {Observable} this instance of the observable\n */\n [Symbol_observable]() {\n return this;\n }\n\n /* tslint:disable:max-line-length */\n pipe(): Observable;\n pipe(op1: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction, op3: OperatorFunction): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction,\n ...operations: OperatorFunction[]\n ): Observable;\n /* tslint:enable:max-line-length */\n\n /**\n * Used to stitch together functional operators into a chain.\n * @method pipe\n * @return {Observable} the Observable result of all of the operators having\n * been called in the order they were passed in.\n *\n * ## Example\n *\n * ```ts\n * import { interval, filter, map, scan } from 'rxjs';\n *\n * interval(1000)\n * .pipe(\n * filter(x => x % 2 === 0),\n * map(x => x + x),\n * scan((acc, x) => acc + x)\n * )\n * .subscribe(x => console.log(x));\n * ```\n */\n pipe(...operations: OperatorFunction[]): Observable {\n return pipeFromArray(operations)(this);\n }\n\n /* tslint:disable:max-line-length */\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: typeof Promise): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: PromiseConstructorLike): Promise;\n /* tslint:enable:max-line-length */\n\n /**\n * Subscribe to this Observable and get a Promise resolving on\n * `complete` with the last emission (if any).\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * @method toPromise\n * @param [promiseCtor] a constructor function used to instantiate\n * the Promise\n * @return A Promise that resolves with the last value emit, or\n * rejects on an error. If there were no emissions, Promise\n * resolves with undefined.\n * @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise\n */\n toPromise(promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n let value: T | undefined;\n this.subscribe(\n (x: T) => (value = x),\n (err: any) => reject(err),\n () => resolve(value)\n );\n }) as Promise;\n }\n}\n\n/**\n * Decides between a passed promise constructor from consuming code,\n * A default configured promise constructor, and the native promise\n * constructor and returns it. If nothing can be found, it will throw\n * an error.\n * @param promiseCtor The optional promise constructor to passed by consuming code\n */\nfunction getPromiseCtor(promiseCtor: PromiseConstructorLike | undefined) {\n return promiseCtor ?? config.Promise ?? Promise;\n}\n\nfunction isObserver(value: any): value is Observer {\n return value && isFunction(value.next) && isFunction(value.error) && isFunction(value.complete);\n}\n\nfunction isSubscriber(value: any): value is Subscriber {\n return (value && value instanceof Subscriber) || (isObserver(value) && isSubscription(value));\n}\n", "import { Observable } from '../Observable';\nimport { Subscriber } from '../Subscriber';\nimport { OperatorFunction } from '../types';\nimport { isFunction } from './isFunction';\n\n/**\n * Used to determine if an object is an Observable with a lift function.\n */\nexport function hasLift(source: any): source is { lift: InstanceType['lift'] } {\n return isFunction(source?.lift);\n}\n\n/**\n * Creates an `OperatorFunction`. Used to define operators throughout the library in a concise way.\n * @param init The logic to connect the liftedSource to the subscriber at the moment of subscription.\n */\nexport function operate(\n init: (liftedSource: Observable, subscriber: Subscriber) => (() => void) | void\n): OperatorFunction {\n return (source: Observable) => {\n if (hasLift(source)) {\n return source.lift(function (this: Subscriber, liftedSource: Observable) {\n try {\n return init(liftedSource, this);\n } catch (err) {\n this.error(err);\n }\n });\n }\n throw new TypeError('Unable to lift unknown Observable type');\n };\n}\n", "import { Subscriber } from '../Subscriber';\n\n/**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional teardown logic here. This will only be called on teardown if the\n * subscriber itself is not already closed. This is called after all other teardown logic is executed.\n */\nexport function createOperatorSubscriber(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n onFinalize?: () => void\n): Subscriber {\n return new OperatorSubscriber(destination, onNext, onComplete, onError, onFinalize);\n}\n\n/**\n * A generic helper for allowing operators to be created with a Subscriber and\n * use closures to capture necessary state from the operator function itself.\n */\nexport class OperatorSubscriber extends Subscriber {\n /**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional finalization logic here. This will only be called on finalization if the\n * subscriber itself is not already closed. This is called after all other finalization logic is executed.\n * @param shouldUnsubscribe An optional check to see if an unsubscribe call should truly unsubscribe.\n * NOTE: This currently **ONLY** exists to support the strange behavior of {@link groupBy}, where unsubscription\n * to the resulting observable does not actually disconnect from the source if there are active subscriptions\n * to any grouped observable. (DO NOT EXPOSE OR USE EXTERNALLY!!!)\n */\n constructor(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n private onFinalize?: () => void,\n private shouldUnsubscribe?: () => boolean\n ) {\n // It's important - for performance reasons - that all of this class's\n // members are initialized and that they are always initialized in the same\n // order. This will ensure that all OperatorSubscriber instances have the\n // same hidden class in V8. This, in turn, will help keep the number of\n // hidden classes involved in property accesses within the base class as\n // low as possible. If the number of hidden classes involved exceeds four,\n // the property accesses will become megamorphic and performance penalties\n // will be incurred - i.e. inline caches won't be used.\n //\n // The reasons for ensuring all instances have the same hidden class are\n // further discussed in this blog post from Benedikt Meurer:\n // https://benediktmeurer.de/2018/03/23/impact-of-polymorphism-on-component-based-frameworks-like-react/\n super(destination);\n this._next = onNext\n ? function (this: OperatorSubscriber, value: T) {\n try {\n onNext(value);\n } catch (err) {\n destination.error(err);\n }\n }\n : super._next;\n this._error = onError\n ? function (this: OperatorSubscriber, err: any) {\n try {\n onError(err);\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._error;\n this._complete = onComplete\n ? function (this: OperatorSubscriber) {\n try {\n onComplete();\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._complete;\n }\n\n unsubscribe() {\n if (!this.shouldUnsubscribe || this.shouldUnsubscribe()) {\n const { closed } = this;\n super.unsubscribe();\n // Execute additional teardown if we have any and we didn't already do so.\n !closed && this.onFinalize?.();\n }\n }\n}\n", "import { Subscription } from '../Subscription';\n\ninterface AnimationFrameProvider {\n schedule(callback: FrameRequestCallback): Subscription;\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n delegate:\n | {\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n }\n | undefined;\n}\n\nexport const animationFrameProvider: AnimationFrameProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n schedule(callback) {\n let request = requestAnimationFrame;\n let cancel: typeof cancelAnimationFrame | undefined = cancelAnimationFrame;\n const { delegate } = animationFrameProvider;\n if (delegate) {\n request = delegate.requestAnimationFrame;\n cancel = delegate.cancelAnimationFrame;\n }\n const handle = request((timestamp) => {\n // Clear the cancel function. The request has been fulfilled, so\n // attempting to cancel the request upon unsubscription would be\n // pointless.\n cancel = undefined;\n callback(timestamp);\n });\n return new Subscription(() => cancel?.(handle));\n },\n requestAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.requestAnimationFrame || requestAnimationFrame)(...args);\n },\n cancelAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.cancelAnimationFrame || cancelAnimationFrame)(...args);\n },\n delegate: undefined,\n};\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface ObjectUnsubscribedError extends Error {}\n\nexport interface ObjectUnsubscribedErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (): ObjectUnsubscribedError;\n}\n\n/**\n * An error thrown when an action is invalid because the object has been\n * unsubscribed.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n *\n * @class ObjectUnsubscribedError\n */\nexport const ObjectUnsubscribedError: ObjectUnsubscribedErrorCtor = createErrorClass(\n (_super) =>\n function ObjectUnsubscribedErrorImpl(this: any) {\n _super(this);\n this.name = 'ObjectUnsubscribedError';\n this.message = 'object unsubscribed';\n }\n);\n", "import { Operator } from './Operator';\nimport { Observable } from './Observable';\nimport { Subscriber } from './Subscriber';\nimport { Subscription, EMPTY_SUBSCRIPTION } from './Subscription';\nimport { Observer, SubscriptionLike, TeardownLogic } from './types';\nimport { ObjectUnsubscribedError } from './util/ObjectUnsubscribedError';\nimport { arrRemove } from './util/arrRemove';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A Subject is a special type of Observable that allows values to be\n * multicasted to many Observers. Subjects are like EventEmitters.\n *\n * Every Subject is an Observable and an Observer. You can subscribe to a\n * Subject, and you can call next to feed values as well as error and complete.\n */\nexport class Subject extends Observable implements SubscriptionLike {\n closed = false;\n\n private currentObservers: Observer[] | null = null;\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n observers: Observer[] = [];\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n isStopped = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n hasError = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n thrownError: any = null;\n\n /**\n * Creates a \"subject\" by basically gluing an observer to an observable.\n *\n * @nocollapse\n * @deprecated Recommended you do not use. Will be removed at some point in the future. Plans for replacement still under discussion.\n */\n static create: (...args: any[]) => any = (destination: Observer, source: Observable): AnonymousSubject => {\n return new AnonymousSubject(destination, source);\n };\n\n constructor() {\n // NOTE: This must be here to obscure Observable's constructor.\n super();\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n lift(operator: Operator): Observable {\n const subject = new AnonymousSubject(this, this);\n subject.operator = operator as any;\n return subject as any;\n }\n\n /** @internal */\n protected _throwIfClosed() {\n if (this.closed) {\n throw new ObjectUnsubscribedError();\n }\n }\n\n next(value: T) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n if (!this.currentObservers) {\n this.currentObservers = Array.from(this.observers);\n }\n for (const observer of this.currentObservers) {\n observer.next(value);\n }\n }\n });\n }\n\n error(err: any) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.hasError = this.isStopped = true;\n this.thrownError = err;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.error(err);\n }\n }\n });\n }\n\n complete() {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.isStopped = true;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.complete();\n }\n }\n });\n }\n\n unsubscribe() {\n this.isStopped = this.closed = true;\n this.observers = this.currentObservers = null!;\n }\n\n get observed() {\n return this.observers?.length > 0;\n }\n\n /** @internal */\n protected _trySubscribe(subscriber: Subscriber): TeardownLogic {\n this._throwIfClosed();\n return super._trySubscribe(subscriber);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._checkFinalizedStatuses(subscriber);\n return this._innerSubscribe(subscriber);\n }\n\n /** @internal */\n protected _innerSubscribe(subscriber: Subscriber) {\n const { hasError, isStopped, observers } = this;\n if (hasError || isStopped) {\n return EMPTY_SUBSCRIPTION;\n }\n this.currentObservers = null;\n observers.push(subscriber);\n return new Subscription(() => {\n this.currentObservers = null;\n arrRemove(observers, subscriber);\n });\n }\n\n /** @internal */\n protected _checkFinalizedStatuses(subscriber: Subscriber) {\n const { hasError, thrownError, isStopped } = this;\n if (hasError) {\n subscriber.error(thrownError);\n } else if (isStopped) {\n subscriber.complete();\n }\n }\n\n /**\n * Creates a new Observable with this Subject as the source. You can do this\n * to create custom Observer-side logic of the Subject and conceal it from\n * code that uses the Observable.\n * @return {Observable} Observable that the Subject casts to\n */\n asObservable(): Observable {\n const observable: any = new Observable();\n observable.source = this;\n return observable;\n }\n}\n\n/**\n * @class AnonymousSubject\n */\nexport class AnonymousSubject extends Subject {\n constructor(\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n public destination?: Observer,\n source?: Observable\n ) {\n super();\n this.source = source;\n }\n\n next(value: T) {\n this.destination?.next?.(value);\n }\n\n error(err: any) {\n this.destination?.error?.(err);\n }\n\n complete() {\n this.destination?.complete?.();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n return this.source?.subscribe(subscriber) ?? EMPTY_SUBSCRIPTION;\n }\n}\n", "import { TimestampProvider } from '../types';\n\ninterface DateTimestampProvider extends TimestampProvider {\n delegate: TimestampProvider | undefined;\n}\n\nexport const dateTimestampProvider: DateTimestampProvider = {\n now() {\n // Use the variable rather than `this` so that the function can be called\n // without being bound to the provider.\n return (dateTimestampProvider.delegate || Date).now();\n },\n delegate: undefined,\n};\n", "import { Subject } from './Subject';\nimport { TimestampProvider } from './types';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * A variant of {@link Subject} that \"replays\" old values to new subscribers by emitting them when they first subscribe.\n *\n * `ReplaySubject` has an internal buffer that will store a specified number of values that it has observed. Like `Subject`,\n * `ReplaySubject` \"observes\" values by having them passed to its `next` method. When it observes a value, it will store that\n * value for a time determined by the configuration of the `ReplaySubject`, as passed to its constructor.\n *\n * When a new subscriber subscribes to the `ReplaySubject` instance, it will synchronously emit all values in its buffer in\n * a First-In-First-Out (FIFO) manner. The `ReplaySubject` will also complete, if it has observed completion; and it will\n * error if it has observed an error.\n *\n * There are two main configuration items to be concerned with:\n *\n * 1. `bufferSize` - This will determine how many items are stored in the buffer, defaults to infinite.\n * 2. `windowTime` - The amount of time to hold a value in the buffer before removing it from the buffer.\n *\n * Both configurations may exist simultaneously. So if you would like to buffer a maximum of 3 values, as long as the values\n * are less than 2 seconds old, you could do so with a `new ReplaySubject(3, 2000)`.\n *\n * ### Differences with BehaviorSubject\n *\n * `BehaviorSubject` is similar to `new ReplaySubject(1)`, with a couple of exceptions:\n *\n * 1. `BehaviorSubject` comes \"primed\" with a single value upon construction.\n * 2. `ReplaySubject` will replay values, even after observing an error, where `BehaviorSubject` will not.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n * @see {@link shareReplay}\n */\nexport class ReplaySubject extends Subject {\n private _buffer: (T | number)[] = [];\n private _infiniteTimeWindow = true;\n\n /**\n * @param bufferSize The size of the buffer to replay on subscription\n * @param windowTime The amount of time the buffered items will stay buffered\n * @param timestampProvider An object with a `now()` method that provides the current timestamp. This is used to\n * calculate the amount of time something has been buffered.\n */\n constructor(\n private _bufferSize = Infinity,\n private _windowTime = Infinity,\n private _timestampProvider: TimestampProvider = dateTimestampProvider\n ) {\n super();\n this._infiniteTimeWindow = _windowTime === Infinity;\n this._bufferSize = Math.max(1, _bufferSize);\n this._windowTime = Math.max(1, _windowTime);\n }\n\n next(value: T): void {\n const { isStopped, _buffer, _infiniteTimeWindow, _timestampProvider, _windowTime } = this;\n if (!isStopped) {\n _buffer.push(value);\n !_infiniteTimeWindow && _buffer.push(_timestampProvider.now() + _windowTime);\n }\n this._trimBuffer();\n super.next(value);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._trimBuffer();\n\n const subscription = this._innerSubscribe(subscriber);\n\n const { _infiniteTimeWindow, _buffer } = this;\n // We use a copy here, so reentrant code does not mutate our array while we're\n // emitting it to a new subscriber.\n const copy = _buffer.slice();\n for (let i = 0; i < copy.length && !subscriber.closed; i += _infiniteTimeWindow ? 1 : 2) {\n subscriber.next(copy[i] as T);\n }\n\n this._checkFinalizedStatuses(subscriber);\n\n return subscription;\n }\n\n private _trimBuffer() {\n const { _bufferSize, _timestampProvider, _buffer, _infiniteTimeWindow } = this;\n // If we don't have an infinite buffer size, and we're over the length,\n // use splice to truncate the old buffer values off. Note that we have to\n // double the size for instances where we're not using an infinite time window\n // because we're storing the values and the timestamps in the same array.\n const adjustedBufferSize = (_infiniteTimeWindow ? 1 : 2) * _bufferSize;\n _bufferSize < Infinity && adjustedBufferSize < _buffer.length && _buffer.splice(0, _buffer.length - adjustedBufferSize);\n\n // Now, if we're not in an infinite time window, remove all values where the time is\n // older than what is allowed.\n if (!_infiniteTimeWindow) {\n const now = _timestampProvider.now();\n let last = 0;\n // Search the array for the first timestamp that isn't expired and\n // truncate the buffer up to that point.\n for (let i = 1; i < _buffer.length && (_buffer[i] as number) <= now; i += 2) {\n last = i;\n }\n last && _buffer.splice(0, last + 1);\n }\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Subscription } from '../Subscription';\nimport { SchedulerAction } from '../types';\n\n/**\n * A unit of work to be executed in a `scheduler`. An action is typically\n * created from within a {@link SchedulerLike} and an RxJS user does not need to concern\n * themselves about creating and manipulating an Action.\n *\n * ```ts\n * class Action extends Subscription {\n * new (scheduler: Scheduler, work: (state?: T) => void);\n * schedule(state?: T, delay: number = 0): Subscription;\n * }\n * ```\n *\n * @class Action\n */\nexport class Action extends Subscription {\n constructor(scheduler: Scheduler, work: (this: SchedulerAction, state?: T) => void) {\n super();\n }\n /**\n * Schedules this action on its parent {@link SchedulerLike} for execution. May be passed\n * some context object, `state`. May happen at some point in the future,\n * according to the `delay` parameter, if specified.\n * @param {T} [state] Some contextual data that the `work` function uses when\n * called by the Scheduler.\n * @param {number} [delay] Time to wait before executing the work, where the\n * time unit is implicit and defined by the Scheduler.\n * @return {void}\n */\n public schedule(state?: T, delay: number = 0): Subscription {\n return this;\n }\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetIntervalFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearIntervalFunction = (handle: TimerHandle) => void;\n\ninterface IntervalProvider {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n delegate:\n | {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n }\n | undefined;\n}\n\nexport const intervalProvider: IntervalProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setInterval(handler: () => void, timeout?: number, ...args) {\n const { delegate } = intervalProvider;\n if (delegate?.setInterval) {\n return delegate.setInterval(handler, timeout, ...args);\n }\n return setInterval(handler, timeout, ...args);\n },\n clearInterval(handle) {\n const { delegate } = intervalProvider;\n return (delegate?.clearInterval || clearInterval)(handle as any);\n },\n delegate: undefined,\n};\n", "import { Action } from './Action';\nimport { SchedulerAction } from '../types';\nimport { Subscription } from '../Subscription';\nimport { AsyncScheduler } from './AsyncScheduler';\nimport { intervalProvider } from './intervalProvider';\nimport { arrRemove } from '../util/arrRemove';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncAction extends Action {\n public id: TimerHandle | undefined;\n public state?: T;\n // @ts-ignore: Property has no initializer and is not definitely assigned\n public delay: number;\n protected pending: boolean = false;\n\n constructor(protected scheduler: AsyncScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (this.closed) {\n return this;\n }\n\n // Always replace the current state with the new state.\n this.state = state;\n\n const id = this.id;\n const scheduler = this.scheduler;\n\n //\n // Important implementation note:\n //\n // Actions only execute once by default, unless rescheduled from within the\n // scheduled callback. This allows us to implement single and repeat\n // actions via the same code path, without adding API surface area, as well\n // as mimic traditional recursion but across asynchronous boundaries.\n //\n // However, JS runtimes and timers distinguish between intervals achieved by\n // serial `setTimeout` calls vs. a single `setInterval` call. An interval of\n // serial `setTimeout` calls can be individually delayed, which delays\n // scheduling the next `setTimeout`, and so on. `setInterval` attempts to\n // guarantee the interval callback will be invoked more precisely to the\n // interval period, regardless of load.\n //\n // Therefore, we use `setInterval` to schedule single and repeat actions.\n // If the action reschedules itself with the same delay, the interval is not\n // canceled. If the action doesn't reschedule, or reschedules with a\n // different delay, the interval will be canceled after scheduled callback\n // execution.\n //\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, delay);\n }\n\n // Set the pending flag indicating that this action has been scheduled, or\n // has recursively rescheduled itself.\n this.pending = true;\n\n this.delay = delay;\n // If this action has already an async Id, don't request a new one.\n this.id = this.id ?? this.requestAsyncId(scheduler, this.id, delay);\n\n return this;\n }\n\n protected requestAsyncId(scheduler: AsyncScheduler, _id?: TimerHandle, delay: number = 0): TimerHandle {\n return intervalProvider.setInterval(scheduler.flush.bind(scheduler, this), delay);\n }\n\n protected recycleAsyncId(_scheduler: AsyncScheduler, id?: TimerHandle, delay: number | null = 0): TimerHandle | undefined {\n // If this action is rescheduled with the same delay time, don't clear the interval id.\n if (delay != null && this.delay === delay && this.pending === false) {\n return id;\n }\n // Otherwise, if the action's delay time is different from the current delay,\n // or the action has been rescheduled before it's executed, clear the interval id\n if (id != null) {\n intervalProvider.clearInterval(id);\n }\n\n return undefined;\n }\n\n /**\n * Immediately executes this action and the `work` it contains.\n * @return {any}\n */\n public execute(state: T, delay: number): any {\n if (this.closed) {\n return new Error('executing a cancelled action');\n }\n\n this.pending = false;\n const error = this._execute(state, delay);\n if (error) {\n return error;\n } else if (this.pending === false && this.id != null) {\n // Dequeue if the action didn't reschedule itself. Don't call\n // unsubscribe(), because the action could reschedule later.\n // For example:\n // ```\n // scheduler.schedule(function doWork(counter) {\n // /* ... I'm a busy worker bee ... */\n // var originalAction = this;\n // /* wait 100ms before rescheduling the action */\n // setTimeout(function () {\n // originalAction.schedule(counter + 1);\n // }, 100);\n // }, 1000);\n // ```\n this.id = this.recycleAsyncId(this.scheduler, this.id, null);\n }\n }\n\n protected _execute(state: T, _delay: number): any {\n let errored: boolean = false;\n let errorValue: any;\n try {\n this.work(state);\n } catch (e) {\n errored = true;\n // HACK: Since code elsewhere is relying on the \"truthiness\" of the\n // return here, we can't have it return \"\" or 0 or false.\n // TODO: Clean this up when we refactor schedulers mid-version-8 or so.\n errorValue = e ? e : new Error('Scheduled action threw falsy error');\n }\n if (errored) {\n this.unsubscribe();\n return errorValue;\n }\n }\n\n unsubscribe() {\n if (!this.closed) {\n const { id, scheduler } = this;\n const { actions } = scheduler;\n\n this.work = this.state = this.scheduler = null!;\n this.pending = false;\n\n arrRemove(actions, this);\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, null);\n }\n\n this.delay = null!;\n super.unsubscribe();\n }\n }\n}\n", "import { Action } from './scheduler/Action';\nimport { Subscription } from './Subscription';\nimport { SchedulerLike, SchedulerAction } from './types';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * An execution context and a data structure to order tasks and schedule their\n * execution. Provides a notion of (potentially virtual) time, through the\n * `now()` getter method.\n *\n * Each unit of work in a Scheduler is called an `Action`.\n *\n * ```ts\n * class Scheduler {\n * now(): number;\n * schedule(work, delay?, state?): Subscription;\n * }\n * ```\n *\n * @class Scheduler\n * @deprecated Scheduler is an internal implementation detail of RxJS, and\n * should not be used directly. Rather, create your own class and implement\n * {@link SchedulerLike}. Will be made internal in v8.\n */\nexport class Scheduler implements SchedulerLike {\n public static now: () => number = dateTimestampProvider.now;\n\n constructor(private schedulerActionCtor: typeof Action, now: () => number = Scheduler.now) {\n this.now = now;\n }\n\n /**\n * A getter method that returns a number representing the current time\n * (at the time this function was called) according to the scheduler's own\n * internal clock.\n * @return {number} A number that represents the current time. May or may not\n * have a relation to wall-clock time. May or may not refer to a time unit\n * (e.g. milliseconds).\n */\n public now: () => number;\n\n /**\n * Schedules a function, `work`, for execution. May happen at some point in\n * the future, according to the `delay` parameter, if specified. May be passed\n * some context object, `state`, which will be passed to the `work` function.\n *\n * The given arguments will be processed an stored as an Action object in a\n * queue of actions.\n *\n * @param {function(state: ?T): ?Subscription} work A function representing a\n * task, or some unit of work to be executed by the Scheduler.\n * @param {number} [delay] Time to wait before executing the work, where the\n * time unit is implicit and defined by the Scheduler itself.\n * @param {T} [state] Some contextual data that the `work` function uses when\n * called by the Scheduler.\n * @return {Subscription} A subscription in order to be able to unsubscribe\n * the scheduled work.\n */\n public schedule(work: (this: SchedulerAction, state?: T) => void, delay: number = 0, state?: T): Subscription {\n return new this.schedulerActionCtor(this, work).schedule(state, delay);\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Action } from './Action';\nimport { AsyncAction } from './AsyncAction';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncScheduler extends Scheduler {\n public actions: Array> = [];\n /**\n * A flag to indicate whether the Scheduler is currently executing a batch of\n * queued actions.\n * @type {boolean}\n * @internal\n */\n public _active: boolean = false;\n /**\n * An internal ID used to track the latest asynchronous task such as those\n * coming from `setTimeout`, `setInterval`, `requestAnimationFrame`, and\n * others.\n * @type {any}\n * @internal\n */\n public _scheduled: TimerHandle | undefined;\n\n constructor(SchedulerAction: typeof Action, now: () => number = Scheduler.now) {\n super(SchedulerAction, now);\n }\n\n public flush(action: AsyncAction): void {\n const { actions } = this;\n\n if (this._active) {\n actions.push(action);\n return;\n }\n\n let error: any;\n this._active = true;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions.shift()!)); // exhaust the scheduler queue\n\n this._active = false;\n\n if (error) {\n while ((action = actions.shift()!)) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\n/**\n *\n * Async Scheduler\n *\n * Schedule task as if you used setTimeout(task, duration)\n *\n * `async` scheduler schedules tasks asynchronously, by putting them on the JavaScript\n * event loop queue. It is best used to delay tasks in time or to schedule tasks repeating\n * in intervals.\n *\n * If you just want to \"defer\" task, that is to perform it right after currently\n * executing synchronous code ends (commonly achieved by `setTimeout(deferredTask, 0)`),\n * better choice will be the {@link asapScheduler} scheduler.\n *\n * ## Examples\n * Use async scheduler to delay task\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * const task = () => console.log('it works!');\n *\n * asyncScheduler.schedule(task, 2000);\n *\n * // After 2 seconds logs:\n * // \"it works!\"\n * ```\n *\n * Use async scheduler to repeat task in intervals\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * function task(state) {\n * console.log(state);\n * this.schedule(state + 1, 1000); // `this` references currently executing Action,\n * // which we reschedule with new state and delay\n * }\n *\n * asyncScheduler.schedule(task, 3000, 0);\n *\n * // Logs:\n * // 0 after 3s\n * // 1 after 4s\n * // 2 after 5s\n * // 3 after 6s\n * ```\n */\n\nexport const asyncScheduler = new AsyncScheduler(AsyncAction);\n\n/**\n * @deprecated Renamed to {@link asyncScheduler}. Will be removed in v8.\n */\nexport const async = asyncScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\nimport { SchedulerAction } from '../types';\nimport { animationFrameProvider } from './animationFrameProvider';\nimport { TimerHandle } from './timerHandle';\n\nexport class AnimationFrameAction extends AsyncAction {\n constructor(protected scheduler: AnimationFrameScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n protected requestAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay is greater than 0, request as an async action.\n if (delay !== null && delay > 0) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n // Push the action to the end of the scheduler queue.\n scheduler.actions.push(this);\n // If an animation frame has already been requested, don't request another\n // one. If an animation frame hasn't been requested yet, request one. Return\n // the current animation frame request id.\n return scheduler._scheduled || (scheduler._scheduled = animationFrameProvider.requestAnimationFrame(() => scheduler.flush(undefined)));\n }\n\n protected recycleAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle | undefined {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n if (delay != null ? delay > 0 : this.delay > 0) {\n return super.recycleAsyncId(scheduler, id, delay);\n }\n // If the scheduler queue has no remaining actions with the same async id,\n // cancel the requested animation frame and set the scheduled flag to\n // undefined so the next AnimationFrameAction will request its own.\n const { actions } = scheduler;\n if (id != null && actions[actions.length - 1]?.id !== id) {\n animationFrameProvider.cancelAnimationFrame(id as number);\n scheduler._scheduled = undefined;\n }\n // Return undefined so the action knows to request a new async id if it's rescheduled.\n return undefined;\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\nexport class AnimationFrameScheduler extends AsyncScheduler {\n public flush(action?: AsyncAction): void {\n this._active = true;\n // The async id that effects a call to flush is stored in _scheduled.\n // Before executing an action, it's necessary to check the action's async\n // id to determine whether it's supposed to be executed in the current\n // flush.\n // Previous implementations of this method used a count to determine this,\n // but that was unsound, as actions that are unsubscribed - i.e. cancelled -\n // are removed from the actions array and that can shift actions that are\n // scheduled to be executed in a subsequent flush into positions at which\n // they are executed within the current flush.\n const flushId = this._scheduled;\n this._scheduled = undefined;\n\n const { actions } = this;\n let error: any;\n action = action || actions.shift()!;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions[0]) && action.id === flushId && actions.shift());\n\n this._active = false;\n\n if (error) {\n while ((action = actions[0]) && action.id === flushId && actions.shift()) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AnimationFrameAction } from './AnimationFrameAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\n\n/**\n *\n * Animation Frame Scheduler\n *\n * Perform task when `window.requestAnimationFrame` would fire\n *\n * When `animationFrame` scheduler is used with delay, it will fall back to {@link asyncScheduler} scheduler\n * behaviour.\n *\n * Without delay, `animationFrame` scheduler can be used to create smooth browser animations.\n * It makes sure scheduled task will happen just before next browser content repaint,\n * thus performing animations as efficiently as possible.\n *\n * ## Example\n * Schedule div height animation\n * ```ts\n * // html:
\n * import { animationFrameScheduler } from 'rxjs';\n *\n * const div = document.querySelector('div');\n *\n * animationFrameScheduler.schedule(function(height) {\n * div.style.height = height + \"px\";\n *\n * this.schedule(height + 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * }, 0, 0);\n *\n * // You will see a div element growing in height\n * ```\n */\n\nexport const animationFrameScheduler = new AnimationFrameScheduler(AnimationFrameAction);\n\n/**\n * @deprecated Renamed to {@link animationFrameScheduler}. Will be removed in v8.\n */\nexport const animationFrame = animationFrameScheduler;\n", "import { Observable } from '../Observable';\nimport { SchedulerLike } from '../types';\n\n/**\n * A simple Observable that emits no items to the Observer and immediately\n * emits a complete notification.\n *\n * Just emits 'complete', and nothing else.\n *\n * ![](empty.png)\n *\n * A simple Observable that only emits the complete notification. It can be used\n * for composing with other Observables, such as in a {@link mergeMap}.\n *\n * ## Examples\n *\n * Log complete notification\n *\n * ```ts\n * import { EMPTY } from 'rxjs';\n *\n * EMPTY.subscribe({\n * next: () => console.log('Next'),\n * complete: () => console.log('Complete!')\n * });\n *\n * // Outputs\n * // Complete!\n * ```\n *\n * Emit the number 7, then complete\n *\n * ```ts\n * import { EMPTY, startWith } from 'rxjs';\n *\n * const result = EMPTY.pipe(startWith(7));\n * result.subscribe(x => console.log(x));\n *\n * // Outputs\n * // 7\n * ```\n *\n * Map and flatten only odd numbers to the sequence `'a'`, `'b'`, `'c'`\n *\n * ```ts\n * import { interval, mergeMap, of, EMPTY } from 'rxjs';\n *\n * const interval$ = interval(1000);\n * const result = interval$.pipe(\n * mergeMap(x => x % 2 === 1 ? of('a', 'b', 'c') : EMPTY),\n * );\n * result.subscribe(x => console.log(x));\n *\n * // Results in the following to the console:\n * // x is equal to the count on the interval, e.g. (0, 1, 2, 3, ...)\n * // x will occur every 1000ms\n * // if x % 2 is equal to 1, print a, b, c (each on its own)\n * // if x % 2 is not equal to 1, nothing will be output\n * ```\n *\n * @see {@link Observable}\n * @see {@link NEVER}\n * @see {@link of}\n * @see {@link throwError}\n */\nexport const EMPTY = new Observable((subscriber) => subscriber.complete());\n\n/**\n * @param scheduler A {@link SchedulerLike} to use for scheduling\n * the emission of the complete notification.\n * @deprecated Replaced with the {@link EMPTY} constant or {@link scheduled} (e.g. `scheduled([], scheduler)`). Will be removed in v8.\n */\nexport function empty(scheduler?: SchedulerLike) {\n return scheduler ? emptyScheduled(scheduler) : EMPTY;\n}\n\nfunction emptyScheduled(scheduler: SchedulerLike) {\n return new Observable((subscriber) => scheduler.schedule(() => subscriber.complete()));\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport function isScheduler(value: any): value is SchedulerLike {\n return value && isFunction(value.schedule);\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\nimport { isScheduler } from './isScheduler';\n\nfunction last(arr: T[]): T | undefined {\n return arr[arr.length - 1];\n}\n\nexport function popResultSelector(args: any[]): ((...args: unknown[]) => unknown) | undefined {\n return isFunction(last(args)) ? args.pop() : undefined;\n}\n\nexport function popScheduler(args: any[]): SchedulerLike | undefined {\n return isScheduler(last(args)) ? args.pop() : undefined;\n}\n\nexport function popNumber(args: any[], defaultValue: number): number {\n return typeof last(args) === 'number' ? args.pop()! : defaultValue;\n}\n", "export const isArrayLike = ((x: any): x is ArrayLike => x && typeof x.length === 'number' && typeof x !== 'function');", "import { isFunction } from \"./isFunction\";\n\n/**\n * Tests to see if the object is \"thennable\".\n * @param value the object to test\n */\nexport function isPromise(value: any): value is PromiseLike {\n return isFunction(value?.then);\n}\n", "import { InteropObservable } from '../types';\nimport { observable as Symbol_observable } from '../symbol/observable';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being Observable (but not necessary an Rx Observable) */\nexport function isInteropObservable(input: any): input is InteropObservable {\n return isFunction(input[Symbol_observable]);\n}\n", "import { isFunction } from './isFunction';\n\nexport function isAsyncIterable(obj: any): obj is AsyncIterable {\n return Symbol.asyncIterator && isFunction(obj?.[Symbol.asyncIterator]);\n}\n", "/**\n * Creates the TypeError to throw if an invalid object is passed to `from` or `scheduled`.\n * @param input The object that was passed.\n */\nexport function createInvalidObservableTypeError(input: any) {\n // TODO: We should create error codes that can be looked up, so this can be less verbose.\n return new TypeError(\n `You provided ${\n input !== null && typeof input === 'object' ? 'an invalid object' : `'${input}'`\n } where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.`\n );\n}\n", "export function getSymbolIterator(): symbol {\n if (typeof Symbol !== 'function' || !Symbol.iterator) {\n return '@@iterator' as any;\n }\n\n return Symbol.iterator;\n}\n\nexport const iterator = getSymbolIterator();\n", "import { iterator as Symbol_iterator } from '../symbol/iterator';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being an Iterable */\nexport function isIterable(input: any): input is Iterable {\n return isFunction(input?.[Symbol_iterator]);\n}\n", "import { ReadableStreamLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport async function* readableStreamLikeToAsyncGenerator(readableStream: ReadableStreamLike): AsyncGenerator {\n const reader = readableStream.getReader();\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) {\n return;\n }\n yield value!;\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nexport function isReadableStreamLike(obj: any): obj is ReadableStreamLike {\n // We don't want to use instanceof checks because they would return\n // false for instances from another Realm, like an