@sphereon/ssi-sdk.vc-status-list 0.34.1-feature.SSISDK.17.bitstring.sl.11 → 0.34.1-feature.SSISDK.17.bitstring.sl.14

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.
@@ -1,16 +1,15 @@
1
- import type { IAgentContext, ICredentialPlugin } from '@veramo/core'
1
+ import type { IAgentContext } from '@veramo/core'
2
2
  import type { IIdentifierResolution } from '@sphereon/ssi-sdk-ext.identifier-resolution'
3
3
  import {
4
- BitstringStatus,
5
- BitstringStatusListEntryCredentialStatus,
6
4
  CheckStatusIndexArgs,
7
5
  CreateStatusListArgs,
6
+ IMergeDetailsWithEntityArgs,
7
+ IToDetailsFromCredentialArgs,
8
8
  Status2021,
9
9
  StatusList2021EntryCredentialStatus,
10
10
  StatusListOAuthEntryCredentialStatus,
11
11
  StatusListResult,
12
12
  StatusOAuth,
13
- ToStatusListDetailsArgs,
14
13
  UpdateStatusListFromEncodedListArgs,
15
14
  UpdateStatusListIndexArgs,
16
15
  } from '../types'
@@ -24,37 +23,70 @@ import {
24
23
  StatusListType,
25
24
  StatusPurpose2021,
26
25
  } from '@sphereon/ssi-types'
27
- import { IBitstringStatusListEntryEntity, IStatusListEntryEntity, StatusListEntity } from '@sphereon/ssi-sdk.data-store'
26
+ import {
27
+ BitstringStatusListEntryCredentialStatus,
28
+ IBitstringStatusListEntryEntity,
29
+ IStatusListEntryEntity,
30
+ StatusListEntity,
31
+ } from '@sphereon/ssi-sdk.data-store'
32
+ import { IVcdmCredentialPlugin } from '@sphereon/ssi-sdk.credential-vcdm'
33
+ import { DecodedStatusListPayload } from './encoding/common'
34
+
35
+ export interface IExtractedCredentialDetails {
36
+ id: string
37
+ issuer: string | IIssuer
38
+ encodedList: string
39
+ decodedPayload?: DecodedStatusListPayload
40
+ }
28
41
 
29
42
  export interface IStatusList {
30
43
  /**
31
44
  * Creates a new status list of the specific type
32
45
  */
33
- createNewStatusList(args: CreateStatusListArgs, context: IAgentContext<ICredentialPlugin & IIdentifierResolution>): Promise<StatusListResult>
46
+ createNewStatusList(args: CreateStatusListArgs, context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>): Promise<StatusListResult>
34
47
 
35
48
  /**
36
49
  * Updates a status at the given index in the status list
37
50
  */
38
- updateStatusListIndex(args: UpdateStatusListIndexArgs, context: IAgentContext<ICredentialPlugin & IIdentifierResolution>): Promise<StatusListResult>
51
+ updateStatusListIndex(
52
+ args: UpdateStatusListIndexArgs,
53
+ context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
54
+ ): Promise<StatusListResult>
39
55
 
40
56
  /**
41
57
  * Updates a status list using a base64 encoded list of statuses
42
58
  */
43
59
  updateStatusListFromEncodedList(
44
60
  args: UpdateStatusListFromEncodedListArgs,
45
- context: IAgentContext<ICredentialPlugin & IIdentifierResolution>,
61
+ context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
46
62
  ): Promise<StatusListResult>
47
63
 
48
64
  /**
49
65
  * Checks the status at a given index in the status list
50
66
  */
51
- checkStatusIndex(args: CheckStatusIndexArgs): Promise<number | Status2021 | StatusOAuth | BitstringStatus>
67
+ checkStatusIndex(args: CheckStatusIndexArgs): Promise<number | Status2021 | StatusOAuth>
68
+
69
+ /**
70
+ * Performs the initial parsing of a StatusListCredential.
71
+ * This method handles expensive operations like JWT/CWT decoding once.
72
+ * It extracts all details available from the credential payload itself.
73
+ */
74
+ extractCredentialDetails(credential: StatusListCredential): Promise<IExtractedCredentialDetails>
75
+
76
+ /**
77
+ * Converts a credential and its known metadata into a full StatusListResult.
78
+ */
79
+ toStatusListDetails(
80
+ args: IToDetailsFromCredentialArgs,
81
+ ): Promise<
82
+ StatusListResult & (IStatusList2021ImplementationResult | IOAuthStatusListImplementationResult | IBitstringStatusListImplementationResult)
83
+ >
52
84
 
53
85
  /**
54
- * Collects the status list details - returns flattened entity data ready for storage
86
+ * Merges pre-parsed details from a new credential with an existing database entity.
55
87
  */
56
88
  toStatusListDetails(
57
- args: ToStatusListDetailsArgs,
89
+ args: IMergeDetailsWithEntityArgs,
58
90
  ): Promise<
59
91
  StatusListResult & (IStatusList2021ImplementationResult | IOAuthStatusListImplementationResult | IBitstringStatusListImplementationResult)
60
92
  >
@@ -1,26 +1,28 @@
1
- import type { IAgentContext, ICredentialPlugin, IKeyManager } from '@veramo/core'
1
+ import type { IAgentContext, IKeyManager } from '@veramo/core'
2
2
  import { type CompactJWT, type CredentialProofFormat, type CWT, StatusListType } from '@sphereon/ssi-types'
3
3
  import type {
4
4
  CheckStatusIndexArgs,
5
5
  CreateStatusListArgs,
6
+ IMergeDetailsWithEntityArgs,
7
+ IToDetailsFromCredentialArgs,
6
8
  SignedStatusListData,
7
9
  StatusListOAuthEntryCredentialStatus,
8
10
  StatusListResult,
9
11
  StatusOAuth,
10
- ToStatusListDetailsArgs,
11
12
  UpdateStatusListFromEncodedListArgs,
12
13
  UpdateStatusListIndexArgs,
13
14
  } from '../types'
14
15
  import { determineProofFormat, ensureDate, getAssertedValue, getAssertedValues } from '../utils'
15
- import type { IOAuthStatusListImplementationResult, IStatusList } from './IStatusList'
16
+ import type { IExtractedCredentialDetails, IOAuthStatusListImplementationResult, IStatusList } from './IStatusList'
16
17
  import { StatusList } from '@sd-jwt/jwt-status-list'
17
18
  import type { IJwtService } from '@sphereon/ssi-sdk-ext.jwt-service'
18
19
  import type { IIdentifierResolution } from '@sphereon/ssi-sdk-ext.identifier-resolution'
19
20
  import { createSignedJwt, decodeStatusListJWT } from './encoding/jwt'
20
21
  import { createSignedCbor, decodeStatusListCWT } from './encoding/cbor'
21
22
  import { IBitstringStatusListEntryEntity, IStatusListEntryEntity, OAuthStatusListEntity, StatusListEntity } from '@sphereon/ssi-sdk.data-store'
23
+ import { IVcdmCredentialPlugin } from '@sphereon/ssi-sdk.credential-vcdm'
22
24
 
23
- type IRequiredContext = IAgentContext<ICredentialPlugin & IJwtService & IIdentifierResolution & IKeyManager>
25
+ type IRequiredContext = IAgentContext<IVcdmCredentialPlugin & IJwtService & IIdentifierResolution & IKeyManager>
24
26
 
25
27
  export const DEFAULT_BITS_PER_STATUS = 1 // 1 bit is sufficient for 0x00 - "VALID" 0x01 - "INVALID" saving space in the process
26
28
  export const DEFAULT_LIST_LENGTH = 250000
@@ -163,41 +165,87 @@ export class OAuthStatusListImplementation implements IStatusList {
163
165
  return statusList.getStatus(index)
164
166
  }
165
167
 
166
- async toStatusListDetails(args: ToStatusListDetailsArgs): Promise<StatusListResult & IOAuthStatusListImplementationResult> {
167
- const { statusListPayload } = args as { statusListPayload: CompactJWT | CWT }
168
- const proofFormat = determineProofFormat(statusListPayload)
169
- const decoded = proofFormat === 'jwt' ? decodeStatusListJWT(statusListPayload) : decodeStatusListCWT(statusListPayload)
170
- const { statusList, issuer, id, exp } = decoded
168
+ async extractCredentialDetails(credential: CompactJWT | CWT): Promise<IExtractedCredentialDetails> {
169
+ if (typeof credential !== 'string') {
170
+ return Promise.reject('statusListCredential must be a JWT or CWT string')
171
+ }
171
172
 
172
- const bitsPerStatus = statusList.getBitsPerStatus()
173
- const expiresAt = exp ? new Date(exp * 1000) : undefined
173
+ const proofFormat = determineProofFormat(credential)
174
+ const decoded = proofFormat === 'jwt' ? decodeStatusListJWT(credential) : decodeStatusListCWT(credential)
174
175
 
175
176
  return {
176
- // Base implementation fields
177
- id,
178
- encodedList: statusList.compressStatusList(),
179
- issuer,
180
- type: StatusListType.OAuthStatusList,
181
- proofFormat,
182
- length: statusList.statusList.length,
183
- statusListCredential: statusListPayload,
184
- statuslistContentType: this.buildContentType(proofFormat),
185
- correlationId: args.correlationId, // FIXME these do not need to be inside the impl
186
- driverType: args.driverType, // FIXME these do not need to be inside the impl
177
+ id: decoded.id,
178
+ issuer: decoded.issuer,
179
+ encodedList: decoded.statusList.compressStatusList(),
180
+ decodedPayload: decoded,
181
+ }
182
+ }
187
183
 
188
- // Flattened OAuth-specific fields
189
- bitsPerStatus,
190
- ...(expiresAt && { expiresAt }),
184
+ // For CREATE and READ contexts
185
+ async toStatusListDetails(args: IToDetailsFromCredentialArgs): Promise<StatusListResult & IOAuthStatusListImplementationResult>
186
+ // For UPDATE contexts
187
+ async toStatusListDetails(args: IMergeDetailsWithEntityArgs): Promise<StatusListResult & IOAuthStatusListImplementationResult>
188
+ async toStatusListDetails(
189
+ args: IToDetailsFromCredentialArgs | IMergeDetailsWithEntityArgs,
190
+ ): Promise<StatusListResult & IOAuthStatusListImplementationResult> {
191
+ if ('statusListCredential' in args) {
192
+ // CREATE/READ context
193
+ const { statusListCredential, bitsPerStatus, correlationId, driverType } = args
194
+ if (!bitsPerStatus || bitsPerStatus < 1) {
195
+ return Promise.reject(Error('bitsPerStatus must be set for OAuth status lists and must be 1 or higher'))
196
+ }
191
197
 
192
- // Legacy nested structure for backward compatibility
193
- oauthStatusList: {
198
+ const proofFormat = determineProofFormat(statusListCredential as string)
199
+ const decoded =
200
+ proofFormat === 'jwt' ? decodeStatusListJWT(statusListCredential as string) : decodeStatusListCWT(statusListCredential as string)
201
+ const { statusList, issuer, id, exp } = decoded
202
+ const expiresAt = exp ? new Date(exp * 1000) : undefined
203
+
204
+ return {
205
+ id,
206
+ encodedList: statusList.compressStatusList(),
207
+ issuer,
208
+ type: StatusListType.OAuthStatusList,
209
+ proofFormat,
210
+ length: statusList.statusList.length,
211
+ statusListCredential: statusListCredential as CompactJWT | CWT,
212
+ statuslistContentType: this.buildContentType(proofFormat),
213
+ correlationId,
214
+ driverType,
194
215
  bitsPerStatus,
195
216
  ...(expiresAt && { expiresAt }),
196
- },
197
-
198
- // Optional fields from args
199
- ...(args.correlationId && { correlationId: args.correlationId }),
200
- ...(args.driverType && { driverType: args.driverType }),
217
+ oauthStatusList: {
218
+ bitsPerStatus,
219
+ ...(expiresAt && { expiresAt }),
220
+ },
221
+ }
222
+ } else {
223
+ // UPDATE context
224
+ const { extractedDetails, statusListEntity } = args
225
+ const oauthEntity = statusListEntity as OAuthStatusListEntity
226
+ const decoded = extractedDetails.decodedPayload as { statusList: StatusList; exp?: number }
227
+
228
+ const proofFormat = determineProofFormat(statusListEntity.statusListCredential as string)
229
+ const expiresAt = decoded.exp ? new Date(decoded.exp * 1000) : undefined
230
+
231
+ return {
232
+ id: extractedDetails.id,
233
+ encodedList: extractedDetails.encodedList,
234
+ issuer: extractedDetails.issuer,
235
+ type: StatusListType.OAuthStatusList,
236
+ proofFormat,
237
+ length: decoded.statusList.statusList.length,
238
+ statusListCredential: statusListEntity.statusListCredential!,
239
+ statuslistContentType: this.buildContentType(proofFormat),
240
+ correlationId: statusListEntity.correlationId,
241
+ driverType: statusListEntity.driverType,
242
+ bitsPerStatus: oauthEntity.bitsPerStatus,
243
+ ...(expiresAt && { expiresAt }),
244
+ oauthStatusList: {
245
+ bitsPerStatus: oauthEntity.bitsPerStatus,
246
+ ...(expiresAt && { expiresAt }),
247
+ },
248
+ }
201
249
  }
202
250
  }
203
251
 
@@ -227,7 +275,7 @@ export class OAuthStatusListImplementation implements IStatusList {
227
275
 
228
276
  private async createSignedStatusList(
229
277
  proofFormat: CredentialProofFormat,
230
- context: IAgentContext<ICredentialPlugin & IJwtService & IIdentifierResolution & IKeyManager>,
278
+ context: IAgentContext<IVcdmCredentialPlugin & IJwtService & IIdentifierResolution & IKeyManager>,
231
279
  statusList: StatusList,
232
280
  issuerString: string,
233
281
  id: string,
@@ -1,4 +1,4 @@
1
- import type { IAgentContext, ICredentialPlugin, ProofFormat as VeramoProofFormat } from '@veramo/core'
1
+ import type { IAgentContext, ProofFormat as VeramoProofFormat } from '@veramo/core'
2
2
  import type { IIdentifierResolution } from '@sphereon/ssi-sdk-ext.identifier-resolution'
3
3
  import {
4
4
  CredentialMapper,
@@ -10,18 +10,20 @@ import {
10
10
  } from '@sphereon/ssi-types'
11
11
 
12
12
  import { StatusList } from '@sphereon/vc-status-list'
13
- import type { IStatusList, IStatusList2021ImplementationResult } from './IStatusList'
13
+ import type { IExtractedCredentialDetails, IStatusList, IStatusList2021ImplementationResult } from './IStatusList'
14
14
  import type {
15
15
  CheckStatusIndexArgs,
16
16
  CreateStatusListArgs,
17
+ IMergeDetailsWithEntityArgs,
18
+ IToDetailsFromCredentialArgs,
17
19
  StatusListResult,
18
- ToStatusListDetailsArgs,
19
20
  UpdateStatusListFromEncodedListArgs,
20
21
  UpdateStatusListIndexArgs,
21
22
  } from '../types'
22
23
  import { Status2021, StatusList2021EntryCredentialStatus } from '../types'
23
24
  import { assertValidProofType, getAssertedProperty, getAssertedValue, getAssertedValues } from '../utils'
24
25
  import { IBitstringStatusListEntryEntity, IStatusListEntryEntity, StatusList2021Entity, StatusListEntity } from '@sphereon/ssi-sdk.data-store'
26
+ import { IVcdmCredentialPlugin } from '@sphereon/ssi-sdk.credential-vcdm'
25
27
 
26
28
  export const DEFAULT_LIST_LENGTH = 250000
27
29
  export const DEFAULT_PROOF_FORMAT = 'lds' as CredentialProofFormat
@@ -29,7 +31,7 @@ export const DEFAULT_PROOF_FORMAT = 'lds' as CredentialProofFormat
29
31
  export class StatusList2021Implementation implements IStatusList {
30
32
  async createNewStatusList(
31
33
  args: CreateStatusListArgs,
32
- context: IAgentContext<ICredentialPlugin & IIdentifierResolution>,
34
+ context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
33
35
  ): Promise<StatusListResult> {
34
36
  const length = args?.length ?? DEFAULT_LIST_LENGTH
35
37
  const proofFormat: CredentialProofFormat = args?.proofFormat ?? DEFAULT_PROOF_FORMAT
@@ -71,7 +73,7 @@ export class StatusList2021Implementation implements IStatusList {
71
73
 
72
74
  async updateStatusListIndex(
73
75
  args: UpdateStatusListIndexArgs,
74
- context: IAgentContext<ICredentialPlugin & IIdentifierResolution>,
76
+ context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
75
77
  ): Promise<StatusListResult> {
76
78
  const credential = args.statusListCredential
77
79
  const uniform = CredentialMapper.toUniformCredential(credential)
@@ -118,7 +120,7 @@ export class StatusList2021Implementation implements IStatusList {
118
120
 
119
121
  async updateStatusListFromEncodedList(
120
122
  args: UpdateStatusListFromEncodedListArgs,
121
- context: IAgentContext<ICredentialPlugin & IIdentifierResolution>,
123
+ context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
122
124
  ): Promise<StatusListResult> {
123
125
  if (!args.statusList2021) {
124
126
  throw new Error('statusList2021 options required for type StatusList2021')
@@ -170,44 +172,82 @@ export class StatusList2021Implementation implements IStatusList {
170
172
  return status ? Status2021.Invalid : Status2021.Valid
171
173
  }
172
174
 
173
- async toStatusListDetails(args: ToStatusListDetailsArgs): Promise<StatusListResult & IStatusList2021ImplementationResult> {
174
- const { statusListPayload } = args
175
- const uniform = CredentialMapper.toUniformCredential(statusListPayload)
175
+ async extractCredentialDetails(credential: StatusListCredential): Promise<IExtractedCredentialDetails> {
176
+ const uniform = CredentialMapper.toUniformCredential(credential)
176
177
  const { issuer, credentialSubject } = uniform
177
- const id = getAssertedValue('id', uniform.id)
178
- const encodedList = getAssertedProperty('encodedList', credentialSubject)
179
- const proofFormat: CredentialProofFormat = CredentialMapper.detectDocumentType(statusListPayload) === DocumentFormat.JWT ? 'jwt' : 'lds'
180
-
181
- const statusPurpose = getAssertedProperty('statusPurpose', credentialSubject)
182
- const indexingDirection = 'rightToLeft'
183
- const list = await StatusList.decode({ encodedList })
178
+ const subject = Array.isArray(credentialSubject) ? credentialSubject[0] : credentialSubject
184
179
 
185
180
  return {
186
- // Base implementation fields
187
- id,
188
- encodedList,
181
+ id: getAssertedValue('id', uniform.id),
189
182
  issuer,
190
- type: StatusListType.StatusList2021,
191
- proofFormat,
192
- length: list.length,
193
- statusListCredential: statusListPayload,
194
- statuslistContentType: this.buildContentType(proofFormat),
195
- correlationId: args.correlationId, // FIXME these do not need to be inside the impl
196
- driverType: args.driverType, // FIXME these do not need to be inside the impl
197
-
198
- // Flattened StatusList2021-specific fields
199
- indexingDirection,
200
- statusPurpose,
183
+ encodedList: getAssertedProperty('encodedList', subject),
184
+ }
185
+ }
201
186
 
202
- // Legacy nested structure for backward compatibility
203
- statusList2021: {
204
- indexingDirection,
187
+ async toStatusListDetails(args: IToDetailsFromCredentialArgs): Promise<StatusListResult & IStatusList2021ImplementationResult>
188
+ // For UPDATE contexts
189
+ async toStatusListDetails(args: IMergeDetailsWithEntityArgs): Promise<StatusListResult & IStatusList2021ImplementationResult>
190
+ async toStatusListDetails(
191
+ args: IToDetailsFromCredentialArgs | IMergeDetailsWithEntityArgs,
192
+ ): Promise<StatusListResult & IStatusList2021ImplementationResult> {
193
+ if ('statusListCredential' in args) {
194
+ // CREATE/READ context
195
+ const { statusListCredential, correlationId, driverType } = args
196
+ const uniform = CredentialMapper.toUniformCredential(statusListCredential)
197
+ const { issuer, credentialSubject } = uniform
198
+ const subject = Array.isArray(credentialSubject) ? credentialSubject[0] : credentialSubject
199
+
200
+ const id = getAssertedValue('id', uniform.id)
201
+ const encodedList = getAssertedProperty('encodedList', subject)
202
+ const statusPurpose = getAssertedProperty('statusPurpose', subject)
203
+ const proofFormat: CredentialProofFormat = CredentialMapper.detectDocumentType(statusListCredential) === DocumentFormat.JWT ? 'jwt' : 'lds'
204
+ const list = await StatusList.decode({ encodedList })
205
+
206
+ return {
207
+ id,
208
+ encodedList,
209
+ issuer,
210
+ type: StatusListType.StatusList2021,
211
+ proofFormat,
212
+ length: list.length,
213
+ statusListCredential,
214
+ statuslistContentType: this.buildContentType(proofFormat),
215
+ correlationId,
216
+ driverType,
217
+ indexingDirection: 'rightToLeft',
205
218
  statusPurpose,
206
-
207
- // Optional fields from args
208
- ...(args.correlationId && { correlationId: args.correlationId }),
209
- ...(args.driverType && { driverType: args.driverType }),
210
- },
219
+ statusList2021: {
220
+ indexingDirection: 'rightToLeft',
221
+ statusPurpose,
222
+ },
223
+ }
224
+ } else {
225
+ // UPDATE context
226
+ const { extractedDetails, statusListEntity } = args
227
+ const statusList2021Entity = statusListEntity as StatusList2021Entity
228
+
229
+ const proofFormat: CredentialProofFormat =
230
+ CredentialMapper.detectDocumentType(statusListEntity.statusListCredential!) === DocumentFormat.JWT ? 'jwt' : 'lds'
231
+ const list = await StatusList.decode({ encodedList: extractedDetails.encodedList })
232
+
233
+ return {
234
+ id: extractedDetails.id,
235
+ encodedList: extractedDetails.encodedList,
236
+ issuer: extractedDetails.issuer,
237
+ type: StatusListType.StatusList2021,
238
+ proofFormat,
239
+ length: list.length,
240
+ statusListCredential: statusListEntity.statusListCredential!,
241
+ statuslistContentType: this.buildContentType(proofFormat),
242
+ correlationId: statusListEntity.correlationId,
243
+ driverType: statusListEntity.driverType,
244
+ indexingDirection: statusList2021Entity.indexingDirection,
245
+ statusPurpose: statusList2021Entity.statusPurpose,
246
+ statusList2021: {
247
+ indexingDirection: statusList2021Entity.indexingDirection,
248
+ statusPurpose: statusList2021Entity.statusPurpose,
249
+ },
250
+ }
211
251
  }
212
252
  }
213
253
 
@@ -238,7 +278,7 @@ export class StatusList2021Implementation implements IStatusList {
238
278
  proofFormat: VeramoProofFormat
239
279
  keyRef?: string
240
280
  },
241
- context: IAgentContext<ICredentialPlugin & IIdentifierResolution>,
281
+ context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
242
282
  ): Promise<StatusListCredential> {
243
283
  const identifier = await context.agent.identifierManagedGet({
244
284
  identifier: typeof args.issuer === 'string' ? args.issuer : args.issuer.id,
package/src/index.ts CHANGED
@@ -3,3 +3,4 @@
3
3
 
4
4
  export * from './types'
5
5
  export * from './functions'
6
+ export { determineStatusListType } from './utils'
@@ -12,20 +12,15 @@ import {
12
12
  StatusListType,
13
13
  type StatusPurpose2021,
14
14
  } from '@sphereon/ssi-types'
15
- import type {
16
- CredentialPayload,
17
- IAgentContext,
18
- ICredentialIssuer,
19
- ICredentialPlugin,
20
- ICredentialVerifier,
21
- IKeyManager,
22
- IPluginMethodMap,
23
- } from '@veramo/core'
15
+ import type { CredentialPayload, IAgentContext, ICredentialIssuer, ICredentialVerifier, IKeyManager, IPluginMethodMap } from '@veramo/core'
24
16
  import { DataSource } from 'typeorm'
25
17
  import type { BitsPerStatus } from '@sd-jwt/jwt-status-list'
26
18
  import type { SdJwtVcPayload } from '@sd-jwt/sd-jwt-vc'
27
19
  import type { StatusListOpts } from '@sphereon/oid4vci-common'
28
20
  import { BitstringStatusPurpose } from '@4sure-tech/vc-bitstring-status-lists'
21
+ import { IVcdmCredentialPlugin } from '@sphereon/ssi-sdk.credential-vcdm'
22
+ import { IExtractedCredentialDetails } from '../impl/IStatusList'
23
+ import { BitstringStatusListArgs, IStatusListEntity } from '@sphereon/ssi-sdk.data-store'
29
24
 
30
25
  export enum StatusOAuth {
31
26
  Valid = 0,
@@ -38,8 +33,6 @@ export enum Status2021 {
38
33
  Invalid = 1,
39
34
  }
40
35
 
41
- export type BitstringStatus = number
42
-
43
36
  export type StatusList2021Args = {
44
37
  indexingDirection: StatusListIndexingDirection
45
38
  statusPurpose?: StatusPurpose2021
@@ -51,14 +44,6 @@ export type OAuthStatusListArgs = {
51
44
  expiresAt?: Date
52
45
  }
53
46
 
54
- export type BitstringStatusListArgs = {
55
- statusPurpose: BitstringStatusPurpose
56
- bitsPerStatus: number
57
- ttl?: number
58
- validFrom?: Date
59
- validUntil?: Date
60
- }
61
-
62
47
  export type BaseCreateNewStatusListArgs = {
63
48
  type: StatusListType
64
49
  id: string
@@ -109,7 +94,7 @@ export interface UpdateStatusListFromStatusListCredentialArgs {
109
94
  statusListCredential: StatusListCredential // | CompactJWT
110
95
  keyRef?: string
111
96
  statusListIndex: number | string
112
- value: number | Status2021 | StatusOAuth | BitstringStatus
97
+ value: number | Status2021 | StatusOAuth
113
98
  }
114
99
 
115
100
  export interface StatusListResult {
@@ -170,16 +155,6 @@ export interface StatusListOAuthEntryCredentialStatus extends ICredentialStatus
170
155
  expiresAt?: Date
171
156
  }
172
157
 
173
- export interface BitstringStatusListEntryCredentialStatus extends ICredentialStatus {
174
- type: 'BitstringStatusListEntry'
175
- statusPurpose: BitstringStatusPurpose | BitstringStatusPurpose[]
176
- statusListIndex: string
177
- statusListCredential: string
178
- bitsPerStatus?: number
179
- statusMessage?: Array<BitstringStatus>
180
- statusReference?: string | string[]
181
- }
182
-
183
158
  export interface StatusList2021ToVerifiableCredentialArgs {
184
159
  issuer: string | IIssuer
185
160
  id: string
@@ -205,7 +180,7 @@ export interface CreateStatusListArgs {
205
180
  export interface UpdateStatusListIndexArgs {
206
181
  statusListCredential: StatusListCredential // | CompactJWT
207
182
  statusListIndex: number | string
208
- value: number | Status2021 | StatusOAuth | BitstringStatus
183
+ value: number | Status2021 | StatusOAuth
209
184
  bitsPerStatus?: number
210
185
  keyRef?: string
211
186
  expiresAt?: Date
@@ -217,11 +192,22 @@ export interface CheckStatusIndexArgs {
217
192
  bitsPerStatus?: number
218
193
  }
219
194
 
220
- export interface ToStatusListDetailsArgs {
221
- statusListPayload: StatusListCredential
195
+ // For the CREATE and READ contexts
196
+ export interface IToDetailsFromCredentialArgs {
197
+ // The source credential we are converting
198
+ statusListCredential: StatusListCredential
199
+
200
+ // The required metadata that is NOT in the credential itself
201
+ statusListType: StatusListType
202
+ bitsPerStatus?: number
222
203
  correlationId?: string
223
204
  driverType?: StatusListDriverType
224
- bitsPerStatus?: number
205
+ }
206
+
207
+ // For the UPDATE context
208
+ export interface IMergeDetailsWithEntityArgs {
209
+ extractedDetails: IExtractedCredentialDetails
210
+ statusListEntity: IStatusListEntity
225
211
  }
226
212
 
227
213
  /**
@@ -309,5 +295,5 @@ export type SignedStatusListData = {
309
295
  encodedList: string
310
296
  }
311
297
 
312
- export type IRequiredPlugins = ICredentialPlugin & IIdentifierResolution
313
- export type IRequiredContext = IAgentContext<ICredentialIssuer & ICredentialVerifier & IIdentifierResolution & IKeyManager & ICredentialPlugin>
298
+ export type IRequiredPlugins = IVcdmCredentialPlugin & IIdentifierResolution
299
+ export type IRequiredContext = IAgentContext<ICredentialIssuer & ICredentialVerifier & IIdentifierResolution & IKeyManager & IVcdmCredentialPlugin>
package/src/utils.ts CHANGED
@@ -41,7 +41,7 @@ export function getAssertedProperty<T extends object>(propertyName: string, obj:
41
41
  const ValidProofTypeMap = new Map<StatusListType, CredentialProofFormat[]>([
42
42
  [StatusListType.StatusList2021, ['jwt', 'lds']],
43
43
  [StatusListType.OAuthStatusList, ['jwt', 'cbor']],
44
- [StatusListType.BitstringStatusList, ['lds']],
44
+ [StatusListType.BitstringStatusList, ['lds', 'vc+jwt']],
45
45
  ])
46
46
 
47
47
  export function assertValidProofType(type: StatusListType, proofFormat: CredentialProofFormat) {
@@ -53,31 +53,60 @@ export function assertValidProofType(type: StatusListType, proofFormat: Credenti
53
53
 
54
54
  export function determineStatusListType(credential: StatusListCredential): StatusListType {
55
55
  const proofFormat = determineProofFormat(credential)
56
+
56
57
  switch (proofFormat) {
57
58
  case 'jwt':
58
- const payload: StatusListCredential = jwtDecode(credential as string)
59
- const keys = Object.keys(payload)
60
- if (keys.includes('status_list')) {
61
- return StatusListType.OAuthStatusList
62
- } else if (keys.includes('vc')) {
63
- return StatusListType.StatusList2021
64
- }
65
- break
59
+ return determineJwtStatusListType(credential as string)
66
60
  case 'lds':
67
- const uniform = CredentialMapper.toUniformCredential(credential)
68
- const type = uniform.type.find((t) => {
69
- return Object.values(StatusListType).some((statusType) => t.includes(statusType))
70
- })
71
- if (!type) {
72
- throw new Error('Invalid status list credential type')
73
- }
74
- return type.replace('Credential', '') as StatusListType
75
-
61
+ return determineLdsStatusListType(credential)
76
62
  case 'cbor':
77
63
  return StatusListType.OAuthStatusList
64
+ default:
65
+ throw new Error('Cannot determine status list type from credential payload')
66
+ }
67
+ }
68
+
69
+ function determineJwtStatusListType(credential: string): StatusListType {
70
+ const payload: any = jwtDecode(credential)
71
+
72
+ // OAuth status list format
73
+ if ('status_list' in payload) {
74
+ return StatusListType.OAuthStatusList
75
+ }
76
+
77
+ // Direct credential subject
78
+ if ('credentialSubject' in payload) {
79
+ return getStatusListTypeFromSubject(payload.credentialSubject)
80
+ }
81
+
82
+ // Wrapped VC format
83
+ if ('vc' in payload && 'credentialSubject' in payload.vc) {
84
+ return getStatusListTypeFromSubject(payload.vc.credentialSubject)
85
+ }
86
+
87
+ throw new Error('Invalid status list credential: credentialSubject not found')
88
+ }
89
+
90
+ function determineLdsStatusListType(credential: StatusListCredential): StatusListType {
91
+ const uniform = CredentialMapper.toUniformCredential(credential)
92
+ const statusListType = uniform.type.find((type) => Object.values(StatusListType).some((statusType) => type.includes(statusType)))
93
+
94
+ if (!statusListType) {
95
+ throw new Error('Invalid status list credential type')
78
96
  }
79
97
 
80
- throw new Error('Cannot determine status list type from credential payload')
98
+ return statusListType.replace('Credential', '') as StatusListType
99
+ }
100
+
101
+ function getStatusListTypeFromSubject(credentialSubject: any): StatusListType {
102
+ switch (credentialSubject.type) {
103
+ case 'StatusList2021':
104
+ return StatusListType.StatusList2021
105
+ case 'BitstringStatusList':
106
+ return StatusListType.BitstringStatusList
107
+ default:
108
+ throw new Error(`Unknown credential subject type: ${credentialSubject.type}`)
109
+ }
81
110
  }
82
111
 
83
112
  export function determineProofFormat(credential: StatusListCredential): CredentialProofFormat {