antifraud-sdk-core-ts 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,23 @@
1
+ # Changelog
2
+
3
+ ## Unreleased
4
+
5
+ ## 0.1.1 - 2026-10-17
6
+
7
+ - Add runtime-neutral authoritative credential issuance orchestration using
8
+ canonical data-utils contracts, opaque polling continuity and terminal-state
9
+ enforcement.
10
+
11
+ - Add runtime-neutral employer qualification-evidence orchestration, issuer
12
+ discovery, privacy-minimized confirmation requests, issuer-owned decisions
13
+ and reverse decision notifications.
14
+ - Document canonical Schema.org `MediaObject` evidence, its OpenID Identity
15
+ Assurance attachment projection, SHA3-384 CID anchoring, tenant-to-tenant
16
+ delivery, ledger recovery and W3C credential rendering.
17
+
18
+ - Add runtime-neutral OpenID4VP DCQL profiles, opaque presentation-session storage and presentation orchestration.
19
+ - Document reusable workforce authorization as active group membership plus a
20
+ controller-managed group authorization, with optional subject authorization
21
+ kept separate and no employee-specific Consent.
22
+ - Keep awarding-organization resolution, employer attestation, and
23
+ source-issued qualification proof as distinct orchestration states.
package/LICENSE ADDED
@@ -0,0 +1,167 @@
1
+ Copyright 2026 Antifraud Services Inc.
2
+
3
+ Apache License
4
+ Version 2.0, January 2004
5
+ http://www.apache.org/licenses/
6
+
7
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
8
+
9
+ 1. Definitions.
10
+
11
+ "License" shall mean the terms and conditions for use, reproduction, and
12
+ distribution as defined by Sections 1 through 9 of this document.
13
+
14
+ "Licensor" shall mean the copyright owner or entity authorized by the
15
+ copyright owner that is granting the License.
16
+
17
+ "Legal Entity" shall mean the union of the acting entity and all other
18
+ entities that control, are controlled by, or are under common control with
19
+ that entity. For the purposes of this definition, "control" means (i) the
20
+ power, direct or indirect, to cause the direction or management of such
21
+ entity, whether by contract or otherwise, or (ii) ownership of fifty percent
22
+ (50%) or more of the outstanding shares, or (iii) beneficial ownership of
23
+ such entity.
24
+
25
+ "You" (or "Your") shall mean an individual or Legal Entity exercising
26
+ permissions granted by this License.
27
+
28
+ "Source" form shall mean the preferred form for making modifications,
29
+ including but not limited to software source code, documentation source,
30
+ and configuration files.
31
+
32
+ "Object" form shall mean any form resulting from mechanical transformation
33
+ or translation of a Source form, including but not limited to compiled
34
+ object code, generated documentation, and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or Object form,
37
+ made available under the License, as indicated by a copyright notice that is
38
+ included in or attached to the work (an example is provided in the Appendix
39
+ below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object form,
42
+ that is based on (or derived from) the Work and for which the editorial
43
+ revisions, annotations, elaborations, or other modifications represent, as
44
+ a whole, an original work of authorship. For the purposes of this License,
45
+ Derivative Works shall not include works that remain separable from, or
46
+ merely link (or bind by name) to the interfaces of, the Work and Derivative
47
+ Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including the original
50
+ version of the Work and any modifications or additions to that Work or
51
+ Derivative Works thereof, that is intentionally submitted to Licensor for
52
+ inclusion in the Work by the copyright owner or by an individual or Legal
53
+ Entity authorized to submit on behalf of the copyright owner. For the
54
+ purposes of this definition, "submitted" means any form of electronic,
55
+ verbal, or written communication sent to the Licensor or its representatives,
56
+ including but not limited to communication on electronic mailing lists,
57
+ source code control systems, and issue tracking systems that are managed by,
58
+ or on behalf of, the Licensor for the purpose of discussing and improving
59
+ the Work, but excluding communication that is conspicuously marked or
60
+ otherwise designated in writing by the copyright owner as "Not a
61
+ Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity on
64
+ behalf of whom a Contribution has been received by Licensor and subsequently
65
+ incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of this
68
+ License, each Contributor hereby grants to You a perpetual, worldwide,
69
+ non-exclusive, no-charge, royalty-free, irrevocable copyright license to
70
+ reproduce, prepare Derivative Works of, publicly display, publicly perform,
71
+ sublicense, and distribute the Work and such Derivative Works in Source or
72
+ Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of this
75
+ License, each Contributor hereby grants to You a perpetual, worldwide,
76
+ non-exclusive, no-charge, royalty-free, irrevocable (except as stated in
77
+ this section) patent license to make, have made, use, offer to sell, sell,
78
+ import, and otherwise transfer the Work, where such license applies only to
79
+ those patent claims licensable by such Contributor that are necessarily
80
+ infringed by their Contribution(s) alone or by combination of their
81
+ Contribution(s) with the Work to which such Contribution(s) was submitted.
82
+ If You institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work or a
84
+ Contribution incorporated within the Work constitutes direct or
85
+ contributory patent infringement, then any patent licenses granted to You
86
+ under this License for that Work shall terminate as of the date such
87
+ litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the Work or
90
+ Derivative Works thereof in any medium, with or without modifications, and
91
+ in Source or Object form, provided that You meet the following conditions:
92
+
93
+ (a) You must give any other recipients of the Work or Derivative Works a copy
94
+ of this License; and
95
+
96
+ (b) You must cause any modified files to carry prominent notices stating that
97
+ You changed the files; and
98
+
99
+ (c) You must retain, in the Source form of any Derivative Works that You
100
+ distribute, all copyright, patent, trademark, and attribution notices from
101
+ the Source form of the Work, excluding those notices that do not pertain to
102
+ any part of the Derivative Works; and
103
+
104
+ (d) If the Work includes a "NOTICE" text file as part of its distribution,
105
+ then any Derivative Works that You distribute must include a readable copy of
106
+ the attribution notices contained within such NOTICE file, excluding those
107
+ notices that do not pertain to any part of the Derivative Works, in at least
108
+ one of the following places: within a NOTICE text file distributed as part
109
+ of the Derivative Works; within the Source form or documentation, if provided
110
+ along with the Derivative Works; or, within a display generated by the
111
+ Derivative Works, if and wherever such third-party notices normally appear.
112
+ The contents of the NOTICE file are for informational purposes only and do
113
+ not modify the License. You may add Your own attribution notices within
114
+ Derivative Works that You distribute, alongside or as an addendum to the
115
+ NOTICE text from the Work, provided that such additional attribution notices
116
+ cannot be construed as modifying the License.
117
+
118
+ You may add Your own copyright statement to Your modifications and may
119
+ provide additional or different license terms and conditions for use,
120
+ reproduction, or distribution of Your modifications, or for any such
121
+ Derivative Works as a whole, provided Your use, reproduction, and
122
+ distribution of the Work otherwise complies with the conditions stated in
123
+ this License.
124
+
125
+ 5. Submission of Contributions. Unless You explicitly state otherwise, any
126
+ Contribution intentionally submitted for inclusion in the Work by You to the
127
+ Licensor shall be under the terms and conditions of this License, without
128
+ any additional terms or conditions. Notwithstanding the above, nothing
129
+ herein shall supersede or modify the terms of any separate license agreement
130
+ you may have executed with Licensor regarding such Contributions.
131
+
132
+ 6. Trademarks. This License does not grant permission to use the trade names,
133
+ trademarks, service marks, or product names of the Licensor, except as
134
+ required for reasonable and customary use in describing the origin of the
135
+ Work and reproducing the content of the NOTICE file.
136
+
137
+ 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in
138
+ writing, Licensor provides the Work (and each Contributor provides its
139
+ Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
140
+ KIND, either express or implied, including, without limitation, any
141
+ warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or
142
+ FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining
143
+ the appropriateness of using or redistributing the Work and assume any risks
144
+ associated with Your exercise of permissions under this License.
145
+
146
+ 8. Limitation of Liability. In no event and under no legal theory, whether
147
+ in tort (including negligence), contract, or otherwise, unless required by
148
+ applicable law (such as deliberate and grossly negligent acts) or agreed to
149
+ in writing, shall any Contributor be liable to You for damages, including any
150
+ direct, indirect, special, incidental, or consequential damages of any
151
+ character arising as a result of this License or out of the use or inability
152
+ to use the Work (including but not limited to damages for loss of goodwill,
153
+ work stoppage, computer failure or malfunction, or any and all other
154
+ commercial damages or losses), even if such Contributor has been advised of
155
+ the possibility of such damages.
156
+
157
+ 9. Accepting Warranty or Additional Liability. While redistributing the Work
158
+ or Derivative Works thereof, You may choose to offer, and charge a fee for,
159
+ acceptance of support, warranty, indemnity, or other liability obligations
160
+ and/or rights consistent with this License. However, in accepting such
161
+ obligations, You may act only on Your own behalf and on Your sole
162
+ responsibility, not on behalf of any other Contributor, and only if You agree
163
+ to indemnify, defend, and hold each Contributor harmless for any liability
164
+ incurred by, or claims asserted against, such Contributor by reason of your
165
+ accepting any such warranty or additional liability.
166
+
167
+ END OF TERMS AND CONDITIONS
package/README.md CHANGED
@@ -1,3 +1,92 @@
1
- # Temporary Holding Version
1
+ # Antifraud SDK Core
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Runtime-neutral orchestration contracts for identity verification, subject and relationship attestation, and provider-issued credentials.
4
+
5
+ ## Status
6
+
7
+ Public implementation foundation. No package has been published and no production readiness is claimed yet.
8
+
9
+ ## Responsibility
10
+
11
+ This package will coordinate application flows without depending on Node.js, React, a database, a ledger, a tenant, or a product.
12
+
13
+ It will reuse the canonical claims, JSON Schemas, mappings, and capability identifiers from [antifraud-data-utils-ts](https://github.com/antifraudservices/antifraud-data-utils-ts). It must not duplicate those literals.
14
+
15
+ The intended flow is:
16
+
17
+ 1. A user interface or backend for frontend starts an identity presentation.
18
+ 2. A trusted verifier validates the presentation and returns an opaque transaction result.
19
+ 3. An authorized index provider reads the subject or relationship again from its authoritative source.
20
+ 4. The index provider issues and signs the resulting credential.
21
+ 5. The caller receives the credential or a stable reference, but never the provider signing key.
22
+
23
+ OpenID for Verifiable Presentations starts and verifies a wallet presentation. Credential issuance is a separate provider operation and may use OpenID for Verifiable Credential Issuance or another explicit issuer protocol. The two operations must never be described as one.
24
+
25
+ ## Planned public API
26
+
27
+ - verification and attestation use-case ports;
28
+ - wallet-presentation session orchestration;
29
+ - authoritative-readback request and result types;
30
+ - credential-issuance request orchestration;
31
+ - deterministic flow states and errors;
32
+ - no transport, storage, framework, or signing implementation.
33
+
34
+ ## Security boundary
35
+
36
+ No secrets, tenant configuration, trust anchors, personal data, signing keys, or operational endpoints belong in this repository.
37
+
38
+ The browser and a generic backend for frontend are untrusted for credential signing. Only the configured credential provider may sign after an authoritative readback and authorization check.
39
+
40
+ ## Workforce group authorization
41
+
42
+ An employee does not receive a separate Consent merely by joining an
43
+ operational group. Orchestration must receive and keep distinct:
44
+
45
+ 1. the employee's active assignment and active membership in the exact group;
46
+ 2. the controller-managed authorization for that group and its exact
47
+ capabilities;
48
+ 3. when the use case requires it, a separate authorization from the subject or
49
+ lawful representative.
50
+
51
+ Adding or removing one employee changes membership only. It must not recreate
52
+ or revoke the group authorization for the remaining members. Invitation,
53
+ acceptance, department or group reassignment, suspension, and reactivation are
54
+ separate assignment events that applications notify through Communication.
55
+ Those notifications are not authorization records.
56
+
57
+ The same orchestration contract can serve education, insurance, human health,
58
+ animal care, or other domains. Product applications select their own group
59
+ purposes and subject-authorization rules; this package must not embed them.
60
+
61
+ For employee qualification evidence, orchestration keeps organization
62
+ resolution, employer attestation, and source-issued credential proof as
63
+ separate states. An awarding organization is selected by legal identifier and
64
+ jurisdiction. If no source credential exists, the employer may sign its own
65
+ attestation but must not be represented as the awarding organization's issuer.
66
+
67
+ The implemented qualification-evidence controller records the employer's proof
68
+ through a trusted port, resolves the claimed issuer by legal identifier and
69
+ jurisdiction, anchors only opaque continuity, and queues a tenant-to-tenant
70
+ confirmation request. See [Qualification evidence confirmation prototype](docs/QUALIFICATION_EVIDENCE_CONFIRMATION.md)
71
+ for attachment, issuer notification, ledger recovery and credential-rendering
72
+ boundaries.
73
+
74
+ ## Dependency direction
75
+
76
+ ```text
77
+ antifraud-data-utils-ts
78
+ ↓
79
+ antifraud-sdk-core-ts
80
+ ↓
81
+ antifraud-sdk-node-ts / antifraud-portal-components-react
82
+ ↓
83
+ applications and services
84
+ ```
85
+
86
+ ## Implementation gate
87
+
88
+ Implementation begins with executable contract tests and proceeds in dependency order. A repository, type name, mock, or successful HTTP response is not evidence that a credential was verified or issued.
89
+
90
+ ## License
91
+
92
+ Apache-2.0.
@@ -0,0 +1,18 @@
1
+ import { type CredentialIssuanceReceipt, type CredentialIssuanceRequest } from 'antifraud-data-utils-ts';
2
+ export interface CredentialIssuanceProviderPort {
3
+ requestIssuance(request: CredentialIssuanceRequest): Promise<CredentialIssuanceReceipt>;
4
+ getIssuance(issuanceId: string): Promise<CredentialIssuanceReceipt>;
5
+ }
6
+ export interface CredentialIssuanceControllerOptions {
7
+ provider: CredentialIssuanceProviderPort;
8
+ wait(milliseconds: number): Promise<void>;
9
+ }
10
+ export interface CredentialIssuancePollingOptions {
11
+ maxPollAttempts?: number;
12
+ pollIntervalMilliseconds?: number;
13
+ }
14
+ export interface CredentialIssuanceController {
15
+ issue(request: CredentialIssuanceRequest & Record<string, unknown>, polling?: CredentialIssuancePollingOptions): Promise<CredentialIssuanceReceipt>;
16
+ }
17
+ export declare function createCredentialIssuanceController(options: CredentialIssuanceControllerOptions): CredentialIssuanceController;
18
+ //# sourceMappingURL=credential-issuance-controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credential-issuance-controller.d.ts","sourceRoot":"","sources":["../src/credential-issuance-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,yBAAyB,EAC9B,KAAK,yBAAyB,EAC/B,MAAM,yBAAyB,CAAA;AAEhC,MAAM,WAAW,8BAA8B;IAC7C,eAAe,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAAA;IACvF,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAAA;CACpE;AAED,MAAM,WAAW,mCAAmC;IAClD,QAAQ,EAAE,8BAA8B,CAAA;IACxC,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1C;AAED,MAAM,WAAW,gCAAgC;IAC/C,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,wBAAwB,CAAC,EAAE,MAAM,CAAA;CAClC;AAED,MAAM,WAAW,4BAA4B;IAC3C,KAAK,CACH,OAAO,EAAE,yBAAyB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5D,OAAO,CAAC,EAAE,gCAAgC,GACzC,OAAO,CAAC,yBAAyB,CAAC,CAAA;CACtC;AAwBD,wBAAgB,kCAAkC,CAChD,OAAO,EAAE,mCAAmC,GAC3C,4BAA4B,CAgC9B"}
@@ -0,0 +1,42 @@
1
+ import { CredentialIssuanceStatus, defineCredentialIssuanceRequest, } from 'antifraud-data-utils-ts';
2
+ function isTerminal(receipt) {
3
+ return receipt.status === CredentialIssuanceStatus.Issued
4
+ || receipt.status === CredentialIssuanceStatus.Denied
5
+ || receipt.status === CredentialIssuanceStatus.Failed;
6
+ }
7
+ function positiveInteger(value, fallback, name) {
8
+ const resolved = value ?? fallback;
9
+ if (!Number.isSafeInteger(resolved) || resolved <= 0) {
10
+ throw new TypeError(`${name} must be a positive safe integer`);
11
+ }
12
+ return resolved;
13
+ }
14
+ function nonNegativeInteger(value, fallback) {
15
+ const resolved = value ?? fallback;
16
+ if (!Number.isSafeInteger(resolved) || resolved < 0) {
17
+ throw new TypeError('pollIntervalMilliseconds must be a non-negative safe integer');
18
+ }
19
+ return resolved;
20
+ }
21
+ export function createCredentialIssuanceController(options) {
22
+ return Object.freeze({
23
+ async issue(request, polling = {}) {
24
+ const canonicalRequest = defineCredentialIssuanceRequest(request);
25
+ const initial = await options.provider.requestIssuance(canonicalRequest);
26
+ if (isTerminal(initial))
27
+ return initial;
28
+ const maxPollAttempts = positiveInteger(polling.maxPollAttempts, 8, 'maxPollAttempts');
29
+ const pollIntervalMilliseconds = nonNegativeInteger(polling.pollIntervalMilliseconds, 1_000);
30
+ for (let attempt = 0; attempt < maxPollAttempts; attempt += 1) {
31
+ await options.wait(pollIntervalMilliseconds);
32
+ const current = await options.provider.getIssuance(initial.issuanceId);
33
+ if (current.issuanceId !== initial.issuanceId) {
34
+ throw new Error('Credential provider changed issuance identifier during polling');
35
+ }
36
+ if (isTerminal(current))
37
+ return current;
38
+ }
39
+ throw new Error('Credential issuance remained pending after the configured poll limit');
40
+ },
41
+ });
42
+ }
@@ -0,0 +1,6 @@
1
+ export * from './openid4vp.js';
2
+ export * from './credential-issuance-controller.js';
3
+ export * from './presentation-controller.js';
4
+ export * from './presentation-session.js';
5
+ export * from './qualification-evidence-controller.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,qCAAqC,CAAA;AACnD,cAAc,8BAA8B,CAAA;AAC5C,cAAc,2BAA2B,CAAA;AACzC,cAAc,wCAAwC,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export * from './openid4vp.js';
2
+ export * from './credential-issuance-controller.js';
3
+ export * from './presentation-controller.js';
4
+ export * from './presentation-session.js';
5
+ export * from './qualification-evidence-controller.js';
@@ -0,0 +1,58 @@
1
+ export declare const OPENID4VP_FORMAT: {
2
+ readonly msoMdoc: "mso_mdoc";
3
+ readonly sdJwtVc: "dc+sd-jwt";
4
+ };
5
+ export declare const EUDI_PID: {
6
+ readonly mdocType: "eu.europa.ec.eudi.pid.1";
7
+ readonly sdJwtVcType: "urn:eudi:pid:1";
8
+ readonly givenName: "given_name";
9
+ readonly familyName: "family_name";
10
+ readonly birthDate: "birth_date";
11
+ };
12
+ export declare const ISO_MDL: {
13
+ readonly documentType: "org.iso.18013.5.1.mDL";
14
+ readonly namespace: "org.iso.18013.5.1";
15
+ readonly givenName: "given_name";
16
+ readonly familyName: "family_name";
17
+ readonly birthDate: "birth_date";
18
+ };
19
+ export type OpenId4VpProfileId = 'eudi-pid' | 'mdl';
20
+ export interface OpenId4VpProfile {
21
+ id: OpenId4VpProfileId;
22
+ label: string;
23
+ claimLabels: readonly string[];
24
+ }
25
+ export declare const OPENID4VP_PROFILES: {
26
+ readonly eudiPid: {
27
+ readonly id: "eudi-pid";
28
+ readonly label: "EUDI Personal Identification Data";
29
+ readonly claimLabels: readonly ["Given name", "Family name", "Date of birth"];
30
+ };
31
+ readonly mobileDrivingLicence: {
32
+ readonly id: "mdl";
33
+ readonly label: "Mobile Driving Licence";
34
+ readonly claimLabels: readonly ["Given name", "Family name", "Date of birth"];
35
+ };
36
+ };
37
+ export interface DcqlClaim {
38
+ path: readonly string[];
39
+ intent_to_retain?: false;
40
+ }
41
+ export interface DcqlCredential {
42
+ id: string;
43
+ format: (typeof OPENID4VP_FORMAT)[keyof typeof OPENID4VP_FORMAT];
44
+ meta: {
45
+ doctype_value: string;
46
+ } | {
47
+ vct_values: readonly string[];
48
+ };
49
+ claims: readonly DcqlClaim[];
50
+ }
51
+ export interface DcqlQuery {
52
+ credentials: readonly DcqlCredential[];
53
+ credential_sets?: readonly {
54
+ options: readonly (readonly string[])[];
55
+ }[];
56
+ }
57
+ export declare function buildDcqlQuery(profile: OpenId4VpProfile): DcqlQuery;
58
+ //# sourceMappingURL=openid4vp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openid4vp.d.ts","sourceRoot":"","sources":["../src/openid4vp.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB;;;CAGnB,CAAA;AAEV,eAAO,MAAM,QAAQ;;;;;;CAMX,CAAA;AAEV,eAAO,MAAM,OAAO;;;;;;CAMV,CAAA;AAEV,MAAM,MAAM,kBAAkB,GAAG,UAAU,GAAG,KAAK,CAAA;AAEnD,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,kBAAkB,CAAA;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;CAC/B;AAED,eAAO,MAAM,kBAAkB;;;;;;;;;;;CAWsB,CAAA;AAErD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;IACvB,gBAAgB,CAAC,EAAE,KAAK,CAAA;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,OAAO,gBAAgB,CAAC,CAAA;IAChE,IAAI,EAAE;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,UAAU,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,CAAA;IACnE,MAAM,EAAE,SAAS,SAAS,EAAE,CAAA;CAC7B;AAED,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,SAAS,cAAc,EAAE,CAAA;IACtC,eAAe,CAAC,EAAE,SAAS;QAAE,OAAO,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAA;KAAE,EAAE,CAAA;CACzE;AAMD,wBAAgB,cAAc,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,CA2CnE"}
@@ -0,0 +1,76 @@
1
+ export const OPENID4VP_FORMAT = {
2
+ msoMdoc: 'mso_mdoc',
3
+ sdJwtVc: 'dc+sd-jwt',
4
+ };
5
+ export const EUDI_PID = {
6
+ mdocType: 'eu.europa.ec.eudi.pid.1',
7
+ sdJwtVcType: 'urn:eudi:pid:1',
8
+ givenName: 'given_name',
9
+ familyName: 'family_name',
10
+ birthDate: 'birth_date',
11
+ };
12
+ export const ISO_MDL = {
13
+ documentType: 'org.iso.18013.5.1.mDL',
14
+ namespace: 'org.iso.18013.5.1',
15
+ givenName: 'given_name',
16
+ familyName: 'family_name',
17
+ birthDate: 'birth_date',
18
+ };
19
+ export const OPENID4VP_PROFILES = {
20
+ eudiPid: {
21
+ id: 'eudi-pid',
22
+ label: 'EUDI Personal Identification Data',
23
+ claimLabels: ['Given name', 'Family name', 'Date of birth'],
24
+ },
25
+ mobileDrivingLicence: {
26
+ id: 'mdl',
27
+ label: 'Mobile Driving Licence',
28
+ claimLabels: ['Given name', 'Family name', 'Date of birth'],
29
+ },
30
+ };
31
+ function mdocClaim(namespace, claim) {
32
+ return { path: [namespace, claim], intent_to_retain: false };
33
+ }
34
+ export function buildDcqlQuery(profile) {
35
+ if (profile.id === 'eudi-pid') {
36
+ return {
37
+ credentials: [
38
+ {
39
+ id: 'pid_mdoc',
40
+ format: OPENID4VP_FORMAT.msoMdoc,
41
+ meta: { doctype_value: EUDI_PID.mdocType },
42
+ claims: [
43
+ mdocClaim(EUDI_PID.mdocType, EUDI_PID.givenName),
44
+ mdocClaim(EUDI_PID.mdocType, EUDI_PID.familyName),
45
+ mdocClaim(EUDI_PID.mdocType, EUDI_PID.birthDate),
46
+ ],
47
+ },
48
+ {
49
+ id: 'pid_sd_jwt',
50
+ format: OPENID4VP_FORMAT.sdJwtVc,
51
+ meta: { vct_values: [EUDI_PID.sdJwtVcType] },
52
+ claims: [
53
+ { path: [EUDI_PID.givenName] },
54
+ { path: [EUDI_PID.familyName] },
55
+ { path: [EUDI_PID.birthDate] },
56
+ ],
57
+ },
58
+ ],
59
+ credential_sets: [{ options: [['pid_mdoc'], ['pid_sd_jwt']] }],
60
+ };
61
+ }
62
+ return {
63
+ credentials: [
64
+ {
65
+ id: 'mdl',
66
+ format: OPENID4VP_FORMAT.msoMdoc,
67
+ meta: { doctype_value: ISO_MDL.documentType },
68
+ claims: [
69
+ mdocClaim(ISO_MDL.namespace, ISO_MDL.givenName),
70
+ mdocClaim(ISO_MDL.namespace, ISO_MDL.familyName),
71
+ mdocClaim(ISO_MDL.namespace, ISO_MDL.birthDate),
72
+ ],
73
+ },
74
+ ],
75
+ };
76
+ }
@@ -0,0 +1,57 @@
1
+ import type { OpenId4VpProfile } from './openid4vp.js';
2
+ import type { PresentationSession } from './presentation-session.js';
3
+ export interface PresentationTransaction {
4
+ transaction_id: string;
5
+ client_id: string;
6
+ request_uri: string;
7
+ request_uri_method: 'get' | 'post' | 'post_get';
8
+ }
9
+ export type PresentationResult = {
10
+ outcome: 'pending';
11
+ } | {
12
+ outcome: 'verified';
13
+ } | {
14
+ outcome: 'denied';
15
+ reason?: string;
16
+ } | {
17
+ outcome: 'failed';
18
+ reason?: string;
19
+ };
20
+ interface PresentationVerifier {
21
+ initialize(profile: OpenId4VpProfile): Promise<PresentationTransaction>;
22
+ getResult(transactionId: string): Promise<PresentationResult>;
23
+ }
24
+ interface SessionRepository {
25
+ save(session: PresentationSession): Promise<void>;
26
+ clear(): Promise<void>;
27
+ }
28
+ interface PresentationControllerOptions {
29
+ verifier: PresentationVerifier;
30
+ sessionRepository: SessionRepository;
31
+ openWallet(uri: string): Promise<void>;
32
+ now(): string;
33
+ }
34
+ export type PresentationState = {
35
+ phase: 'idle';
36
+ } | {
37
+ phase: 'waiting';
38
+ transactionId: string;
39
+ } | {
40
+ phase: 'verified';
41
+ transactionId: string;
42
+ } | {
43
+ phase: 'denied';
44
+ transactionId: string;
45
+ reason?: string;
46
+ } | {
47
+ phase: 'failed';
48
+ transactionId: string;
49
+ reason?: string;
50
+ };
51
+ export declare function buildHaipAuthorizationUri(transaction: PresentationTransaction): string;
52
+ export declare function createPresentationController(options: PresentationControllerOptions): {
53
+ start(profile: OpenId4VpProfile): Promise<PresentationState>;
54
+ refresh(transactionId: string): Promise<PresentationState>;
55
+ };
56
+ export {};
57
+ //# sourceMappingURL=presentation-controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presentation-controller.d.ts","sourceRoot":"","sources":["../src/presentation-controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAA;AAEpE,MAAM,WAAW,uBAAuB;IACtC,cAAc,EAAE,MAAM,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,kBAAkB,EAAE,KAAK,GAAG,MAAM,GAAG,UAAU,CAAA;CAChD;AAED,MAAM,MAAM,kBAAkB,GAC1B;IAAE,OAAO,EAAE,SAAS,CAAA;CAAE,GACtB;IAAE,OAAO,EAAE,UAAU,CAAA;CAAE,GACvB;IAAE,OAAO,EAAE,QAAQ,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,OAAO,EAAE,QAAQ,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAE1C,UAAU,oBAAoB;IAC5B,UAAU,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAA;IACvE,SAAS,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;CAC9D;AAED,UAAU,iBAAiB;IACzB,IAAI,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB;AAED,UAAU,6BAA6B;IACrC,QAAQ,EAAE,oBAAoB,CAAA;IAC9B,iBAAiB,EAAE,iBAAiB,CAAA;IACpC,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACtC,GAAG,IAAI,MAAM,CAAA;CACd;AAED,MAAM,MAAM,iBAAiB,GACzB;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GACjB;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GAC3C;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GAC5C;IAAE,KAAK,EAAE,QAAQ,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC3D;IAAE,KAAK,EAAE,QAAQ,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAE/D,wBAAgB,yBAAyB,CAAC,WAAW,EAAE,uBAAuB,GAAG,MAAM,CAOtF;AAED,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,6BAA6B;mBAE1D,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC;2BAWrC,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;EAiBnE"}
@@ -0,0 +1,37 @@
1
+ export function buildHaipAuthorizationUri(transaction) {
2
+ const parameters = new URLSearchParams({
3
+ client_id: transaction.client_id,
4
+ request_uri: transaction.request_uri,
5
+ request_uri_method: transaction.request_uri_method,
6
+ });
7
+ return `haip-vp://?${parameters.toString()}`;
8
+ }
9
+ export function createPresentationController(options) {
10
+ return {
11
+ async start(profile) {
12
+ const transaction = await options.verifier.initialize(profile);
13
+ await options.sessionRepository.save({
14
+ transactionId: transaction.transaction_id,
15
+ requestUri: transaction.request_uri,
16
+ createdAt: options.now(),
17
+ });
18
+ await options.openWallet(buildHaipAuthorizationUri(transaction));
19
+ return { phase: 'waiting', transactionId: transaction.transaction_id };
20
+ },
21
+ async refresh(transactionId) {
22
+ const result = await options.verifier.getResult(transactionId);
23
+ if (result.outcome === 'pending') {
24
+ return { phase: 'waiting', transactionId };
25
+ }
26
+ await options.sessionRepository.clear();
27
+ if (result.outcome === 'verified') {
28
+ return { phase: 'verified', transactionId };
29
+ }
30
+ return {
31
+ phase: result.outcome,
32
+ transactionId,
33
+ ...(result.reason === undefined ? {} : { reason: result.reason }),
34
+ };
35
+ },
36
+ };
37
+ }
@@ -0,0 +1,16 @@
1
+ export interface PresentationSessionStorage {
2
+ getItem(key: string): Promise<string | null>;
3
+ setItem(key: string, value: string): Promise<void>;
4
+ removeItem(key: string): Promise<void>;
5
+ }
6
+ export interface PresentationSession {
7
+ transactionId: string;
8
+ requestUri: string;
9
+ createdAt: string;
10
+ }
11
+ export declare function createPresentationSessionRepository(storage: PresentationSessionStorage): {
12
+ load(): Promise<PresentationSession | null>;
13
+ save(session: PresentationSession): Promise<void>;
14
+ clear(): Promise<void>;
15
+ };
16
+ //# sourceMappingURL=presentation-session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presentation-session.d.ts","sourceRoot":"","sources":["../src/presentation-session.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,0BAA0B;IACzC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IAC5C,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAClD,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACvC;AAED,MAAM,WAAW,mBAAmB;IAClC,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;CAClB;AAeD,wBAAgB,mCAAmC,CAAC,OAAO,EAAE,0BAA0B;YAErE,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC;kBAU7B,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;aAGxC,OAAO,CAAC,IAAI,CAAC;EAI/B"}
@@ -0,0 +1,32 @@
1
+ const SESSION_KEY = 'openid4vp.presentation.session';
2
+ function isPresentationSession(value) {
3
+ if (!value || typeof value !== 'object')
4
+ return false;
5
+ const candidate = value;
6
+ return typeof candidate.transactionId === 'string'
7
+ && typeof candidate.requestUri === 'string'
8
+ && typeof candidate.createdAt === 'string'
9
+ && Object.keys(candidate).every((key) => (key === 'transactionId' || key === 'requestUri' || key === 'createdAt'));
10
+ }
11
+ export function createPresentationSessionRepository(storage) {
12
+ return {
13
+ async load() {
14
+ const serialized = await storage.getItem(SESSION_KEY);
15
+ if (!serialized)
16
+ return null;
17
+ try {
18
+ const value = JSON.parse(serialized);
19
+ return isPresentationSession(value) ? value : null;
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ },
25
+ async save(session) {
26
+ await storage.setItem(SESSION_KEY, JSON.stringify(session));
27
+ },
28
+ async clear() {
29
+ await storage.removeItem(SESSION_KEY);
30
+ },
31
+ };
32
+ }
@@ -0,0 +1,121 @@
1
+ export type QualificationIssuerQuery = Readonly<{
2
+ legalIdentifier: string;
3
+ jurisdiction: string;
4
+ }>;
5
+ export type ResolvedQualificationIssuer = QualificationIssuerQuery & Readonly<{
6
+ issuerTenantReference: string;
7
+ issuerOrganizationReference: string;
8
+ confirmationServiceReference: string;
9
+ }>;
10
+ export type QualificationEvidenceRequest = Readonly<{
11
+ trustedContext: Readonly<{
12
+ controllerAuthorizationReference: string;
13
+ employerTenantReference: string;
14
+ employerOrganizationReference: string;
15
+ employeeReference: string;
16
+ groupAuthorizationReference: string;
17
+ }>;
18
+ qualificationClaimReference: string;
19
+ claimedIssuer: QualificationIssuerQuery;
20
+ evidence: Readonly<{
21
+ reference: string;
22
+ contentId: string;
23
+ mediaType: 'application/pdf' | 'image/png' | 'image/jpeg' | 'image/tiff';
24
+ fileName: string;
25
+ }>;
26
+ }>;
27
+ export type QualificationConfirmationRequestMessage = Readonly<{
28
+ messageType: 'qualification-evidence-confirmation-request';
29
+ messageId: string;
30
+ fromTenantReference: string;
31
+ toTenantReference: string;
32
+ serviceReference: string;
33
+ evidenceReference: string;
34
+ qualificationClaimReference: string;
35
+ employerOrganizationReference: string;
36
+ claimedIssuerOrganizationReference: string;
37
+ evidenceContentId: string;
38
+ evidenceMediaType: QualificationEvidenceRequest['evidence']['mediaType'];
39
+ createdAt: string;
40
+ }>;
41
+ export type QualificationEvidenceControllerPorts = Readonly<{
42
+ issuerDirectory: Readonly<{
43
+ resolve(query: QualificationIssuerQuery): Promise<ResolvedQualificationIssuer | null>;
44
+ }>;
45
+ evidenceRecorder: Readonly<{
46
+ recordEmployerAttestation(request: QualificationEvidenceRequest): Promise<Readonly<{
47
+ evidenceReference: string;
48
+ employerProofReference: string;
49
+ anchor: Readonly<{
50
+ anchorReference: string;
51
+ contentHash: string;
52
+ }>;
53
+ }>>;
54
+ }>;
55
+ ledger: Readonly<{
56
+ recordAnchor(anchor: Readonly<{
57
+ anchorReference: string;
58
+ evidenceReference: string;
59
+ contentHash: string;
60
+ issuerTenantReference: string;
61
+ lifecycleStatus: 'awaiting-issuer-confirmation';
62
+ }>): Promise<void>;
63
+ }>;
64
+ outbox: Readonly<{
65
+ queue(message: QualificationConfirmationRequestMessage): Promise<Readonly<{
66
+ messageReference: string;
67
+ }>>;
68
+ }>;
69
+ now(): string;
70
+ }>;
71
+ export declare function createQualificationEvidenceController(ports: QualificationEvidenceControllerPorts): {
72
+ submit(request: QualificationEvidenceRequest): Promise<Readonly<{
73
+ phase: "issuer-not-resolved";
74
+ }> | Readonly<{
75
+ phase: "issuer-confirmation-queued";
76
+ evidenceReference: string;
77
+ employerProofReference: string;
78
+ ledgerAnchorReference: string;
79
+ issuerTenantReference: string;
80
+ issuerOrganizationReference: string;
81
+ requestMessageReference: string;
82
+ }>>;
83
+ };
84
+ export type QualificationIssuerDecision = Readonly<{
85
+ evidenceReference: string;
86
+ issuerTenantReference: string;
87
+ issuerOrganizationReference: string;
88
+ decision: 'confirmed' | 'rejected' | 'revoked';
89
+ issuerProofReference: string;
90
+ decidedAt: string;
91
+ }>;
92
+ export type QualificationIssuerOutcome = Readonly<{
93
+ evidenceReference: string;
94
+ status: 'issuer-confirmed' | 'issuer-rejected' | 'issuer-revoked';
95
+ issuerProofReference: string;
96
+ decidedAt: string;
97
+ }>;
98
+ export declare function applyQualificationIssuerDecision(current: Readonly<{
99
+ evidenceReference: string;
100
+ expectedIssuerTenantReference: string;
101
+ expectedIssuerOrganizationReference: string;
102
+ }>, decision: QualificationIssuerDecision): QualificationIssuerOutcome;
103
+ export type QualificationIssuerDecisionNotice = Readonly<{
104
+ messageType: 'qualification-evidence-issuer-decision';
105
+ messageId: string;
106
+ fromTenantReference: string;
107
+ toTenantReference: string;
108
+ serviceReference: string;
109
+ evidenceReference: string;
110
+ status: QualificationIssuerOutcome['status'];
111
+ issuerProofReference: string;
112
+ createdAt: string;
113
+ }>;
114
+ export declare function buildQualificationIssuerDecisionNotice(input: Readonly<{
115
+ outcome: QualificationIssuerOutcome;
116
+ employerTenantReference: string;
117
+ issuerTenantReference: string;
118
+ employerNotificationServiceReference: string;
119
+ createdAt: string;
120
+ }>): QualificationIssuerDecisionNotice;
121
+ //# sourceMappingURL=qualification-evidence-controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qualification-evidence-controller.d.ts","sourceRoot":"","sources":["../src/qualification-evidence-controller.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,126 @@
1
+ // Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
2
+ function requireReference(value, path) {
3
+ if (value.trim().length === 0)
4
+ throw new TypeError(`${path} must not be empty`);
5
+ }
6
+ const BASE58BTC_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
7
+ function decodeBase58btc(value) {
8
+ let decoded = 0n;
9
+ for (const character of value) {
10
+ const index = BASE58BTC_ALPHABET.indexOf(character);
11
+ if (index < 0)
12
+ throw new TypeError('Invalid base58btc character');
13
+ decoded = decoded * 58n + BigInt(index);
14
+ }
15
+ const bytes = [];
16
+ while (decoded > 0n) {
17
+ bytes.unshift(Number(decoded & 0xffn));
18
+ decoded >>= 8n;
19
+ }
20
+ for (const character of value) {
21
+ if (character !== '1')
22
+ break;
23
+ bytes.unshift(0);
24
+ }
25
+ return Uint8Array.from(bytes);
26
+ }
27
+ function assertRawSha3_384CidV1(contentId) {
28
+ if (!/^z[1-9A-HJ-NP-Za-km-z]+$/u.test(contentId)) {
29
+ throw new TypeError('evidence.contentId must be a base58btc multibase CID');
30
+ }
31
+ const bytes = decodeBase58btc(contentId.slice(1));
32
+ if (bytes.length !== 52
33
+ || bytes[0] !== 0x01
34
+ || bytes[1] !== 0x55
35
+ || bytes[2] !== 0x15
36
+ || bytes[3] !== 0x30) {
37
+ throw new TypeError('evidence.contentId must be CIDv1 raw with a SHA3-384 multihash');
38
+ }
39
+ }
40
+ function validateRequest(request) {
41
+ requireReference(request.trustedContext.controllerAuthorizationReference, 'controllerAuthorizationReference');
42
+ requireReference(request.trustedContext.employerTenantReference, 'employerTenantReference');
43
+ requireReference(request.trustedContext.employerOrganizationReference, 'employerOrganizationReference');
44
+ requireReference(request.trustedContext.employeeReference, 'employeeReference');
45
+ requireReference(request.trustedContext.groupAuthorizationReference, 'groupAuthorizationReference');
46
+ requireReference(request.qualificationClaimReference, 'qualificationClaimReference');
47
+ requireReference(request.claimedIssuer.legalIdentifier, 'claimedIssuer.legalIdentifier');
48
+ requireReference(request.claimedIssuer.jurisdiction, 'claimedIssuer.jurisdiction');
49
+ requireReference(request.evidence.reference, 'evidence.reference');
50
+ assertRawSha3_384CidV1(request.evidence.contentId);
51
+ }
52
+ export function createQualificationEvidenceController(ports) {
53
+ return {
54
+ async submit(request) {
55
+ validateRequest(request);
56
+ const issuer = await ports.issuerDirectory.resolve(request.claimedIssuer);
57
+ if (!issuer) {
58
+ return Object.freeze({ phase: 'issuer-not-resolved' });
59
+ }
60
+ const recorded = await ports.evidenceRecorder.recordEmployerAttestation(request);
61
+ await ports.ledger.recordAnchor({
62
+ anchorReference: recorded.anchor.anchorReference,
63
+ evidenceReference: recorded.evidenceReference,
64
+ contentHash: recorded.anchor.contentHash,
65
+ issuerTenantReference: issuer.issuerTenantReference,
66
+ lifecycleStatus: 'awaiting-issuer-confirmation',
67
+ });
68
+ const message = Object.freeze({
69
+ messageType: 'qualification-evidence-confirmation-request',
70
+ messageId: `urn:message:qualification-evidence:${recorded.evidenceReference}`,
71
+ fromTenantReference: request.trustedContext.employerTenantReference,
72
+ toTenantReference: issuer.issuerTenantReference,
73
+ serviceReference: issuer.confirmationServiceReference,
74
+ evidenceReference: recorded.evidenceReference,
75
+ qualificationClaimReference: request.qualificationClaimReference,
76
+ employerOrganizationReference: request.trustedContext.employerOrganizationReference,
77
+ claimedIssuerOrganizationReference: issuer.issuerOrganizationReference,
78
+ evidenceContentId: request.evidence.contentId,
79
+ evidenceMediaType: request.evidence.mediaType,
80
+ createdAt: ports.now(),
81
+ });
82
+ const delivery = await ports.outbox.queue(message);
83
+ return Object.freeze({
84
+ phase: 'issuer-confirmation-queued',
85
+ evidenceReference: recorded.evidenceReference,
86
+ employerProofReference: recorded.employerProofReference,
87
+ ledgerAnchorReference: recorded.anchor.anchorReference,
88
+ issuerTenantReference: issuer.issuerTenantReference,
89
+ issuerOrganizationReference: issuer.issuerOrganizationReference,
90
+ requestMessageReference: delivery.messageReference,
91
+ });
92
+ },
93
+ };
94
+ }
95
+ export function applyQualificationIssuerDecision(current, decision) {
96
+ if (decision.evidenceReference !== current.evidenceReference) {
97
+ throw new TypeError('Issuer decision evidence does not match');
98
+ }
99
+ if (decision.issuerTenantReference !== current.expectedIssuerTenantReference) {
100
+ throw new TypeError('Issuer tenant does not match');
101
+ }
102
+ if (decision.issuerOrganizationReference !== current.expectedIssuerOrganizationReference) {
103
+ throw new TypeError('Issuer organization does not match');
104
+ }
105
+ requireReference(decision.issuerProofReference, 'issuerProofReference');
106
+ requireReference(decision.decidedAt, 'decidedAt');
107
+ return Object.freeze({
108
+ evidenceReference: decision.evidenceReference,
109
+ status: `issuer-${decision.decision}`,
110
+ issuerProofReference: decision.issuerProofReference,
111
+ decidedAt: decision.decidedAt,
112
+ });
113
+ }
114
+ export function buildQualificationIssuerDecisionNotice(input) {
115
+ return Object.freeze({
116
+ messageType: 'qualification-evidence-issuer-decision',
117
+ messageId: `urn:message:qualification-evidence-decision:${input.outcome.evidenceReference}:${input.outcome.status}`,
118
+ fromTenantReference: input.issuerTenantReference,
119
+ toTenantReference: input.employerTenantReference,
120
+ serviceReference: input.employerNotificationServiceReference,
121
+ evidenceReference: input.outcome.evidenceReference,
122
+ status: input.outcome.status,
123
+ issuerProofReference: input.outcome.issuerProofReference,
124
+ createdAt: input.createdAt,
125
+ });
126
+ }
package/package.json CHANGED
@@ -1,6 +1,39 @@
1
1
  {
2
2
  "name": "antifraud-sdk-core-ts",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.1",
4
+ "description": "Runtime-neutral orchestration for identity verification and provider-issued credentials",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md",
17
+ "LICENSE",
18
+ "CHANGELOG.md"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsc -p tsconfig.json",
22
+ "test": "npm run build && node --test test/*.test.mjs",
23
+ "typecheck": "tsc -p tsconfig.json --noEmit"
24
+ },
25
+ "engines": {
26
+ "node": ">=24.0.0"
27
+ },
28
+ "license": "Apache-2.0",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/antifraudservices/antifraud-sdk-core-ts.git"
32
+ },
33
+ "devDependencies": {
34
+ "typescript": "^5.9.3"
35
+ },
36
+ "dependencies": {
37
+ "antifraud-data-utils-ts": "0.1.1"
38
+ }
39
+ }