@learncard/types 5.22.0 → 5.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/unsupportedSignInOperation.test.d.ts +2 -0
- package/dist/__tests__/unsupportedSignInOperation.test.d.ts.map +1 -0
- package/dist/auth.d.ts +18 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/clr.d.ts +2 -2
- package/dist/clr.d.ts.map +1 -1
- package/dist/clr.test.d.ts +2 -0
- package/dist/clr.test.d.ts.map +1 -0
- package/dist/inAppMessages.d.ts +10 -10
- package/dist/lcn.d.ts +1632 -9
- package/dist/lcn.d.ts.map +1 -1
- package/dist/share-links.d.ts +104 -0
- package/dist/share-links.d.ts.map +1 -1
- package/dist/types.cjs.development.cjs +352 -12
- package/dist/types.cjs.development.cjs.map +2 -2
- package/dist/types.cjs.production.min.cjs +22 -22
- package/dist/types.cjs.production.min.cjs.map +3 -3
- package/dist/types.esm.js +352 -12
- package/dist/types.esm.js.map +2 -2
- package/package.json +2 -1
- package/src/__tests__/unsupportedSignInOperation.test.ts +13 -0
- package/src/auth.ts +24 -1
- package/src/clr.test.ts +163 -0
- package/src/clr.ts +2 -1
- package/src/lcn.ts +342 -5
- package/src/share-links.ts +75 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@learncard/types",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.23.0",
|
|
4
4
|
"description": "Shared types for learn card",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"scripts": {
|
|
26
26
|
"build": "node ./scripts/build.mjs && shx cp ./scripts/mixedEntypoint.js ./dist/index.cjs && tsc --p tsconfig.json && shx cp ./dist/index.d.ts ./dist/index.d.cts",
|
|
27
27
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
28
|
+
"test": "vitest run",
|
|
28
29
|
"start": "aqu watch"
|
|
29
30
|
},
|
|
30
31
|
"author": "Learning Economy Foundation (www.learningeconomy.io)",
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { UnsupportedSignInOperationError } from '../auth';
|
|
3
|
+
|
|
4
|
+
describe('UnsupportedSignInOperationError', () => {
|
|
5
|
+
it('preserves the operation and provider for callers', () => {
|
|
6
|
+
const error = new UnsupportedSignInOperationError('sendPhoneOtp', 'keycloak');
|
|
7
|
+
expect(error).toBeInstanceOf(Error);
|
|
8
|
+
expect(error.name).toBe('UnsupportedSignInOperationError');
|
|
9
|
+
expect(error.operation).toBe('sendPhoneOtp');
|
|
10
|
+
expect(error.providerType).toBe('keycloak');
|
|
11
|
+
expect(error.message).toContain('sendPhoneOtp');
|
|
12
|
+
});
|
|
13
|
+
});
|
package/src/auth.ts
CHANGED
|
@@ -32,6 +32,17 @@ export class AuthSessionError extends Error {
|
|
|
32
32
|
// Auth Provider
|
|
33
33
|
// ---------------------------------------------------------------------------
|
|
34
34
|
|
|
35
|
+
/** An action that the selected sign-in provider does not support. */
|
|
36
|
+
export class UnsupportedSignInOperationError extends Error {
|
|
37
|
+
constructor(
|
|
38
|
+
public readonly operation: string,
|
|
39
|
+
public readonly providerType: string
|
|
40
|
+
) {
|
|
41
|
+
super(`${operation} is not supported by ${providerType}`);
|
|
42
|
+
this.name = 'UnsupportedSignInOperationError';
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
35
46
|
/**
|
|
36
47
|
* Auth provider identifier. Known values: 'firebase', 'supertokens', 'keycloak', 'oidc'.
|
|
37
48
|
* Use any string to support custom auth providers without modifying this type.
|
|
@@ -288,10 +299,21 @@ export const EscrowPinStatusValidator = z.object({
|
|
|
288
299
|
});
|
|
289
300
|
export type EscrowPinStatus = z.infer<typeof EscrowPinStatusValidator>;
|
|
290
301
|
|
|
302
|
+
/**
|
|
303
|
+
* Reason a stored escrow blob no longer matches the server's active enclave:
|
|
304
|
+
* `mode-mismatch` — blob was sealed for `software`/`nitro` but the server now
|
|
305
|
+
* runs the other backend (e.g. the P6 software→Nitro migration, or a rollback);
|
|
306
|
+
* `key-rotated` — blob's `enclaveKeyId` no longer matches the active
|
|
307
|
+
* attestation's key (e.g. a PCR/measurement rotation).
|
|
308
|
+
*/
|
|
309
|
+
export type EscrowBlobStaleReason = 'mode-mismatch' | 'key-rotated';
|
|
310
|
+
|
|
291
311
|
/** Enrollment details for PIN-aware strategies. Legacy strategies may still return a string. */
|
|
292
312
|
export interface EscrowEnrollmentState {
|
|
293
|
-
state: 'enrolled' | 'not-enrolled' | 'opted-out' | 'disabled';
|
|
313
|
+
state: 'enrolled' | 'not-enrolled' | 'opted-out' | 'disabled' | 'stale';
|
|
294
314
|
escrowPin?: EscrowPinStatus;
|
|
315
|
+
/** Present only when `state === 'stale'`. */
|
|
316
|
+
staleReason?: EscrowBlobStaleReason;
|
|
295
317
|
}
|
|
296
318
|
|
|
297
319
|
/**
|
|
@@ -309,6 +331,7 @@ export interface ServerKeyStatus {
|
|
|
309
331
|
maskedRecoveryEmail?: string | null;
|
|
310
332
|
escrowOptedOut?: boolean;
|
|
311
333
|
escrowPin?: EscrowPinStatus;
|
|
334
|
+
escrowStale?: EscrowBlobStaleReason;
|
|
312
335
|
sssActivationState?: SssActivationState | null;
|
|
313
336
|
}
|
|
314
337
|
|
package/src/clr.test.ts
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { describe, expect, expectTypeOf, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
AssociationValidator,
|
|
5
|
+
ClrCredentialValidator,
|
|
6
|
+
ClrSubjectValidator,
|
|
7
|
+
UnsignedClrCredentialValidator,
|
|
8
|
+
type Association,
|
|
9
|
+
} from './clr';
|
|
10
|
+
|
|
11
|
+
const association = {
|
|
12
|
+
type: 'Association' as const,
|
|
13
|
+
associationType: 'isPartOf',
|
|
14
|
+
sourceId: 'https://example.org/achievements/course',
|
|
15
|
+
targetId: 'https://example.org/achievements/program',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const supportedTypeNames = [
|
|
19
|
+
'Association',
|
|
20
|
+
'https://purl.imsglobal.org/spec/vc/clr/vocab.html#Association',
|
|
21
|
+
'https://example.org/ExtendedAssociation',
|
|
22
|
+
'CustomAssociation',
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
const subject = {
|
|
26
|
+
id: 'did:example:learner',
|
|
27
|
+
type: ['ClrSubject'],
|
|
28
|
+
association: [association],
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const unsignedClr = {
|
|
32
|
+
'@context': [
|
|
33
|
+
'https://www.w3.org/ns/credentials/v2',
|
|
34
|
+
'https://purl.imsglobal.org/spec/clr/v2p0/context.json',
|
|
35
|
+
'https://purl.imsglobal.org/spec/ob/v3p0/context.json',
|
|
36
|
+
],
|
|
37
|
+
id: 'https://example.org/credentials/clr',
|
|
38
|
+
type: ['VerifiableCredential', 'ClrCredential'],
|
|
39
|
+
issuer: { id: 'did:example:issuer', type: ['Profile'], name: 'Example University' },
|
|
40
|
+
validFrom: '2026-01-01T00:00:00Z',
|
|
41
|
+
credentialSubject: subject,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const proof = {
|
|
45
|
+
type: 'DataIntegrityProof',
|
|
46
|
+
cryptosuite: 'eddsa-rdfc-2022',
|
|
47
|
+
created: '2026-01-01T00:00:00Z',
|
|
48
|
+
proofPurpose: 'assertionMethod',
|
|
49
|
+
verificationMethod: 'did:example:issuer#key',
|
|
50
|
+
proofValue: 'zExampleSignature',
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
describe('CLR Association.type', () => {
|
|
54
|
+
it('accepts the canonical scalar without changing it or extension fields', () => {
|
|
55
|
+
// CLR 2.0 $defs.Association.properties.type requires the scalar "Association".
|
|
56
|
+
// https://purl.imsglobal.org/spec/clr/v2p0/schema/json/clr_v2p0_clrcredential_schema.json
|
|
57
|
+
const input = { ...association, 'ext:description': 'Course belongs to program' };
|
|
58
|
+
|
|
59
|
+
expect(AssociationValidator.parse(input)).toEqual(input);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it.each(supportedTypeNames)(
|
|
63
|
+
'preserves scalar and singleton-array type values through full CLR parsing: %s',
|
|
64
|
+
typeName => {
|
|
65
|
+
for (const type of [typeName, [typeName]]) {
|
|
66
|
+
const input = { ...association, type };
|
|
67
|
+
const credential = {
|
|
68
|
+
...unsignedClr,
|
|
69
|
+
credentialSubject: { ...subject, association: [input] },
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// This SDK validator checks structure; the official CLR schema is stricter.
|
|
73
|
+
expect(AssociationValidator.parse(input)).toEqual(input);
|
|
74
|
+
expect(UnsignedClrCredentialValidator.parse(credential)).toEqual(credential);
|
|
75
|
+
expect(ClrCredentialValidator.parse({ ...credential, proof })).toEqual({
|
|
76
|
+
...credential,
|
|
77
|
+
proof,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
it.each(
|
|
84
|
+
[
|
|
85
|
+
['Association'],
|
|
86
|
+
['Association', 'https://example.org/ExtendedAssociation'],
|
|
87
|
+
['https://example.org/LegacyAssociation'],
|
|
88
|
+
[''], // Existing arrays accept every string; preserve that contract.
|
|
89
|
+
].map(type => ({ type }))
|
|
90
|
+
)('preserves supported legacy arrays: $type', ({ type }) => {
|
|
91
|
+
const input = { ...association, type };
|
|
92
|
+
|
|
93
|
+
expect(AssociationValidator.parse(input)).toEqual(input);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it.each(
|
|
97
|
+
[undefined, null, false, 42, '', {}, [], ['Association', 42], [['Association']]].map(
|
|
98
|
+
type => ({ type })
|
|
99
|
+
)
|
|
100
|
+
)('rejects invalid values: $type', ({ type }) => {
|
|
101
|
+
expect(AssociationValidator.safeParse({ ...association, type }).success).toBe(false);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('exposes scalar and legacy array types to callers', () => {
|
|
105
|
+
expectTypeOf<Association['type']>().toEqualTypeOf<string | string[]>();
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
describe('CLR credentials with associations', () => {
|
|
110
|
+
it('parses a subject containing both canonical and legacy associations', () => {
|
|
111
|
+
const input = {
|
|
112
|
+
...subject,
|
|
113
|
+
association: [association, { ...association, type: ['Association'] }],
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
expect(ClrSubjectValidator.parse(input)).toEqual(input);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it.each([false, true])('parses unsigned and signed CLR subject arrays: %s', subjectArray => {
|
|
120
|
+
const input = {
|
|
121
|
+
...unsignedClr,
|
|
122
|
+
credentialSubject: subjectArray ? [subject] : subject,
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
expect(UnsignedClrCredentialValidator.parse(input)).toEqual(input);
|
|
126
|
+
expect(ClrCredentialValidator.parse({ ...input, proof })).toEqual({ ...input, proof });
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('preserves nested CLR content and validates its associations when parsed directly', () => {
|
|
130
|
+
const nested = {
|
|
131
|
+
...unsignedClr,
|
|
132
|
+
id: 'https://example.org/credentials/nested-clr',
|
|
133
|
+
credentialSubject: {
|
|
134
|
+
...subject,
|
|
135
|
+
association: [{ ...association, type: supportedTypeNames[1] }],
|
|
136
|
+
},
|
|
137
|
+
proof,
|
|
138
|
+
};
|
|
139
|
+
const input = {
|
|
140
|
+
...unsignedClr,
|
|
141
|
+
credentialSubject: { ...subject, verifiableCredential: [nested] },
|
|
142
|
+
proof,
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
expect(ClrCredentialValidator.parse(input)).toEqual(input);
|
|
146
|
+
// Embedded verifiableCredential values are intentionally opaque in ClrSubjectValidator.
|
|
147
|
+
expect(ClrCredentialValidator.parse(nested)).toEqual(nested);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it.each([[], '', ['Association', null]].map(type => ({ type })))(
|
|
151
|
+
'rejects invalid associations through full CLR parsing: $type',
|
|
152
|
+
({ type }) => {
|
|
153
|
+
const invalidSubject = { ...subject, association: [{ ...association, type }] };
|
|
154
|
+
|
|
155
|
+
for (const credentialSubject of [invalidSubject, [invalidSubject]]) {
|
|
156
|
+
const input = { ...unsignedClr, credentialSubject };
|
|
157
|
+
|
|
158
|
+
expect(UnsignedClrCredentialValidator.safeParse(input).success).toBe(false);
|
|
159
|
+
expect(ClrCredentialValidator.safeParse({ ...input, proof }).success).toBe(false);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
);
|
|
163
|
+
});
|
package/src/clr.ts
CHANGED
|
@@ -27,7 +27,8 @@ export type AssociationType = z.infer<typeof AssociationTypeValidator>;
|
|
|
27
27
|
|
|
28
28
|
export const AssociationValidator = z
|
|
29
29
|
.object({
|
|
30
|
-
|
|
30
|
+
// Accept compacted scalars while retaining the existing permissive array contract.
|
|
31
|
+
type: z.string().min(1).or(z.string().array().nonempty()),
|
|
31
32
|
associationType: AssociationTypeValidator,
|
|
32
33
|
sourceId: z.string().optional(),
|
|
33
34
|
targetId: z.string(),
|
package/src/lcn.ts
CHANGED
|
@@ -714,12 +714,14 @@ export const ConsentFlowContractDetailsValidator = z.object({
|
|
|
714
714
|
expiresAt: z.string().optional(),
|
|
715
715
|
autoBoosts: z.string().array().optional(),
|
|
716
716
|
writers: z.array(LCNProfileValidator).optional(),
|
|
717
|
+
recipients: z.array(LCNPublicProfileValidator.extend({ did: z.string() })).optional(),
|
|
718
|
+
audienceVersion: z.number().int().nonnegative().optional(),
|
|
717
719
|
});
|
|
718
720
|
export type ConsentFlowContractDetails = z.infer<typeof ConsentFlowContractDetailsValidator>;
|
|
719
721
|
export type ConsentFlowContractDetailsInput = z.input<typeof ConsentFlowContractDetailsValidator>;
|
|
720
722
|
|
|
721
723
|
export const ConsentFlowContractRequestStatusValidator = z
|
|
722
|
-
.enum(['pending', 'accepted', 'denied'])
|
|
724
|
+
.enum(['pending', 'accepted', 'denied', 'cancelled'])
|
|
723
725
|
.nullable();
|
|
724
726
|
export type ConsentFlowContractRequestStatus = z.infer<
|
|
725
727
|
typeof ConsentFlowContractRequestStatusValidator
|
|
@@ -730,7 +732,51 @@ export type ConsentFlowContractRequestReadStatus = z.infer<
|
|
|
730
732
|
typeof ConsentFlowContractRequestReadStatusValidator
|
|
731
733
|
>;
|
|
732
734
|
|
|
733
|
-
|
|
735
|
+
/** Durable referral identity captured on consent history and correlated events. */
|
|
736
|
+
export const ConsentFlowReferralValidator = z.object({
|
|
737
|
+
requestId: z.string(),
|
|
738
|
+
requestedBy: z.string(),
|
|
739
|
+
externalReferenceId: z.string().max(256).optional(),
|
|
740
|
+
});
|
|
741
|
+
export type ConsentFlowReferral = z.infer<typeof ConsentFlowReferralValidator>;
|
|
742
|
+
|
|
743
|
+
export const ContractRequestFieldsValidator = ConsentFlowReferralValidator.partial().extend({
|
|
744
|
+
requestedAt: z.string().optional(),
|
|
745
|
+
message: z.string().max(500).optional(),
|
|
746
|
+
});
|
|
747
|
+
export type ContractRequestFields = z.infer<typeof ContractRequestFieldsValidator>;
|
|
748
|
+
|
|
749
|
+
export const SendContractRequestValidator = z.object({
|
|
750
|
+
contractUri: z.string(),
|
|
751
|
+
targetProfileId: z.string(),
|
|
752
|
+
externalReferenceId: z.string().trim().min(1).max(256).optional(),
|
|
753
|
+
message: z.string().trim().max(500).optional(),
|
|
754
|
+
});
|
|
755
|
+
export type SendContractRequest = z.infer<typeof SendContractRequestValidator>;
|
|
756
|
+
|
|
757
|
+
export const ConsentFlowWebhookMetadataValidator = z.object({
|
|
758
|
+
eventId: z.string(),
|
|
759
|
+
deliveryKey: z.string(),
|
|
760
|
+
event: z.enum([
|
|
761
|
+
'request_sent',
|
|
762
|
+
'request_accepted',
|
|
763
|
+
'request_denied',
|
|
764
|
+
'request_cancelled',
|
|
765
|
+
'consent_created',
|
|
766
|
+
'consent_updated',
|
|
767
|
+
'consent_withdrawn',
|
|
768
|
+
'credentials_synced',
|
|
769
|
+
]),
|
|
770
|
+
contractUri: z.string(),
|
|
771
|
+
termsUri: z.string().optional(),
|
|
772
|
+
requestId: z.string().optional(),
|
|
773
|
+
requestedBy: z.string().optional(),
|
|
774
|
+
externalReferenceId: z.string().optional(),
|
|
775
|
+
recipientRole: z.enum(['owner', 'recipient', 'requester', 'target']),
|
|
776
|
+
});
|
|
777
|
+
export type ConsentFlowWebhookMetadata = z.infer<typeof ConsentFlowWebhookMetadataValidator>;
|
|
778
|
+
|
|
779
|
+
export const ConsentFlowContractRequestForProfileValidator = ContractRequestFieldsValidator.extend({
|
|
734
780
|
profile: LCNProfileValidator,
|
|
735
781
|
status: ConsentFlowContractRequestStatusValidator,
|
|
736
782
|
readStatus: ConsentFlowContractRequestReadStatusValidator.optional(),
|
|
@@ -803,6 +849,7 @@ export const PaginatedConsentFlowTermsValidator = PaginationResponseValidator.ex
|
|
|
803
849
|
expiresAt: z.string().optional(),
|
|
804
850
|
oneTime: z.boolean().optional(),
|
|
805
851
|
terms: ConsentFlowTermsValidator,
|
|
852
|
+
referral: ConsentFlowReferralValidator.optional(),
|
|
806
853
|
contract: ConsentFlowContractDetailsValidator,
|
|
807
854
|
uri: z.string(),
|
|
808
855
|
consenter: LCNProfileValidator,
|
|
@@ -962,6 +1009,7 @@ export const ConsentFlowTransactionValidator = z.object({
|
|
|
962
1009
|
oneTime: z.boolean().optional(),
|
|
963
1010
|
terms: ConsentFlowTermsValidator.optional(),
|
|
964
1011
|
guardianApproval: ConsentFlowGuardianApprovalValidator.optional(),
|
|
1012
|
+
referral: ConsentFlowReferralValidator.optional(),
|
|
965
1013
|
id: z.string(),
|
|
966
1014
|
action: ConsentFlowTransactionActionValidator,
|
|
967
1015
|
date: z.string(),
|
|
@@ -974,6 +1022,7 @@ export const HolderExportConsentRecordValidator = z.object({
|
|
|
974
1022
|
status: ConsentFlowTermsStatusValidator,
|
|
975
1023
|
contract: ConsentFlowContractDetailsValidator,
|
|
976
1024
|
terms: ConsentFlowTermsValidator,
|
|
1025
|
+
referral: ConsentFlowReferralValidator.optional(),
|
|
977
1026
|
transactions: ConsentFlowTransactionValidator.array(),
|
|
978
1027
|
});
|
|
979
1028
|
export type HolderExportConsentRecord = z.infer<typeof HolderExportConsentRecordValidator>;
|
|
@@ -2188,7 +2237,7 @@ export const AppStoreListingSubmitterValidator = z.object({
|
|
|
2188
2237
|
|
|
2189
2238
|
export type AppStoreListingSubmitter = z.infer<typeof AppStoreListingSubmitterValidator>;
|
|
2190
2239
|
|
|
2191
|
-
|
|
2240
|
+
const AppStoreListingFieldsValidator = z.object({
|
|
2192
2241
|
listing_id: z.string(),
|
|
2193
2242
|
slug: z.string().optional(),
|
|
2194
2243
|
display_name: z.string(),
|
|
@@ -2215,12 +2264,35 @@ export const AppStoreListingValidator = z.object({
|
|
|
2215
2264
|
contact_email: z.string().email().optional(),
|
|
2216
2265
|
});
|
|
2217
2266
|
|
|
2267
|
+
export const PendingListingUpdateStatusValidator = z.enum(['DRAFT', 'PENDING_REVIEW']);
|
|
2268
|
+
export type PendingListingUpdateStatus = z.infer<typeof PendingListingUpdateStatusValidator>;
|
|
2269
|
+
|
|
2270
|
+
/** Changes to a live app that only take effect once an admin approves them. */
|
|
2271
|
+
export const PendingListingUpdateValidator = z.object({
|
|
2272
|
+
status: PendingListingUpdateStatusValidator,
|
|
2273
|
+
submitted_at: z.string().optional(),
|
|
2274
|
+
changes: AppStoreListingFieldsValidator.omit({
|
|
2275
|
+
listing_id: true,
|
|
2276
|
+
app_listing_status: true,
|
|
2277
|
+
promotion_level: true,
|
|
2278
|
+
submitted_at: true,
|
|
2279
|
+
submitter: true,
|
|
2280
|
+
}).partial(),
|
|
2281
|
+
manifest_version: z.number().int().min(1).optional(),
|
|
2282
|
+
});
|
|
2283
|
+
export type PendingListingUpdate = z.infer<typeof PendingListingUpdateValidator>;
|
|
2284
|
+
|
|
2285
|
+
export const AppStoreListingValidator = AppStoreListingFieldsValidator.extend({
|
|
2286
|
+
pending_update: PendingListingUpdateValidator.optional(),
|
|
2287
|
+
});
|
|
2288
|
+
|
|
2218
2289
|
export type AppStoreListing = z.infer<typeof AppStoreListingValidator>;
|
|
2219
2290
|
|
|
2220
2291
|
export const AppStoreListingCreateValidator = AppStoreListingValidator.omit({
|
|
2221
2292
|
listing_id: true,
|
|
2222
2293
|
app_listing_status: true,
|
|
2223
2294
|
promotion_level: true,
|
|
2295
|
+
pending_update: true,
|
|
2224
2296
|
});
|
|
2225
2297
|
|
|
2226
2298
|
export type AppStoreListingCreateType = z.infer<typeof AppStoreListingCreateValidator>;
|
|
@@ -2229,6 +2301,7 @@ export const AppStoreListingUpdateValidator = AppStoreListingValidator.partial()
|
|
|
2229
2301
|
listing_id: true,
|
|
2230
2302
|
app_listing_status: true,
|
|
2231
2303
|
promotion_level: true,
|
|
2304
|
+
pending_update: true,
|
|
2232
2305
|
});
|
|
2233
2306
|
|
|
2234
2307
|
export type AppStoreListingUpdateType = z.infer<typeof AppStoreListingUpdateValidator>;
|
|
@@ -2264,13 +2337,116 @@ export const AppBoostValidator = z.object({
|
|
|
2264
2337
|
export type AppBoost = z.infer<typeof AppBoostValidator>;
|
|
2265
2338
|
|
|
2266
2339
|
// App Event Types (discriminated union for type safety)
|
|
2267
|
-
export const
|
|
2340
|
+
export const InlineWalletCategoryValidator = z.enum([
|
|
2341
|
+
'Achievement',
|
|
2342
|
+
'Skill',
|
|
2343
|
+
'ID',
|
|
2344
|
+
'Learning History',
|
|
2345
|
+
'Work History',
|
|
2346
|
+
'Social Badges',
|
|
2347
|
+
'Membership',
|
|
2348
|
+
'Accomplishment',
|
|
2349
|
+
'Experiences',
|
|
2350
|
+
'Accommodation',
|
|
2351
|
+
'Family',
|
|
2352
|
+
]);
|
|
2353
|
+
|
|
2354
|
+
export const InlineAlignmentValidator = z
|
|
2355
|
+
.object({
|
|
2356
|
+
name: z.string(),
|
|
2357
|
+
url: z.string(),
|
|
2358
|
+
framework: z.string().optional(),
|
|
2359
|
+
code: z.string().optional(),
|
|
2360
|
+
})
|
|
2361
|
+
.passthrough();
|
|
2362
|
+
|
|
2363
|
+
export const InlineEvidenceValidator = z
|
|
2364
|
+
.object({
|
|
2365
|
+
id: z.string().optional(),
|
|
2366
|
+
name: z.string().optional(),
|
|
2367
|
+
narrative: z.string().optional(),
|
|
2368
|
+
})
|
|
2369
|
+
.passthrough();
|
|
2370
|
+
|
|
2371
|
+
export const InlineWalletSkillTagValidator = z
|
|
2372
|
+
.object({
|
|
2373
|
+
frameworkId: z.string(),
|
|
2374
|
+
id: z.string(),
|
|
2375
|
+
proficiencyLevel: z.string().optional(),
|
|
2376
|
+
})
|
|
2377
|
+
.passthrough();
|
|
2378
|
+
|
|
2379
|
+
export const SimpleInlineCredentialTemplateValidator = z
|
|
2380
|
+
.object({
|
|
2381
|
+
rawCredential: z.undefined().optional(),
|
|
2382
|
+
name: z.string(),
|
|
2383
|
+
description: z.string().optional(),
|
|
2384
|
+
image: z.string().optional(),
|
|
2385
|
+
issuerName: z.string().optional(),
|
|
2386
|
+
achievementType: z.string().optional(),
|
|
2387
|
+
category: InlineWalletCategoryValidator.optional(),
|
|
2388
|
+
criteria: z
|
|
2389
|
+
.object({
|
|
2390
|
+
narrative: z.string().optional(),
|
|
2391
|
+
url: z.string().optional(),
|
|
2392
|
+
})
|
|
2393
|
+
.passthrough()
|
|
2394
|
+
.optional(),
|
|
2395
|
+
alignments: z.array(InlineAlignmentValidator).optional(),
|
|
2396
|
+
tags: z.array(z.string()).optional(),
|
|
2397
|
+
activity: z
|
|
2398
|
+
.object({
|
|
2399
|
+
startDate: z.string().optional(),
|
|
2400
|
+
endDate: z.string().optional(),
|
|
2401
|
+
})
|
|
2402
|
+
.passthrough()
|
|
2403
|
+
.optional(),
|
|
2404
|
+
credits: z
|
|
2405
|
+
.object({
|
|
2406
|
+
earned: z.union([z.number(), z.string()]).optional(),
|
|
2407
|
+
available: z.union([z.number(), z.string()]).optional(),
|
|
2408
|
+
})
|
|
2409
|
+
.passthrough()
|
|
2410
|
+
.optional(),
|
|
2411
|
+
validUntil: z.string().optional(),
|
|
2412
|
+
evidence: z.array(InlineEvidenceValidator).optional(),
|
|
2413
|
+
walletSkills: z.array(InlineWalletSkillTagValidator).optional(),
|
|
2414
|
+
})
|
|
2415
|
+
.passthrough();
|
|
2416
|
+
|
|
2417
|
+
export const RawInlineCredentialTemplateValidator = z
|
|
2418
|
+
.object({
|
|
2419
|
+
rawCredential: z.record(z.string(), z.unknown()),
|
|
2420
|
+
category: InlineWalletCategoryValidator.optional(),
|
|
2421
|
+
walletSkills: z.array(InlineWalletSkillTagValidator).optional(),
|
|
2422
|
+
})
|
|
2423
|
+
.passthrough();
|
|
2424
|
+
|
|
2425
|
+
export const InlineCredentialTemplateValidator = z.union([
|
|
2426
|
+
RawInlineCredentialTemplateValidator,
|
|
2427
|
+
SimpleInlineCredentialTemplateValidator,
|
|
2428
|
+
]);
|
|
2429
|
+
|
|
2430
|
+
export const LegacySendCredentialEventValidator = z.object({
|
|
2268
2431
|
type: z.literal('send-credential'),
|
|
2269
2432
|
templateAlias: z.string(),
|
|
2270
2433
|
templateData: z.record(z.string(), z.unknown()).optional(),
|
|
2271
2434
|
preventDuplicateClaim: z.boolean().optional(),
|
|
2272
2435
|
});
|
|
2273
2436
|
|
|
2437
|
+
export const InlineSendCredentialEventValidator = z.object({
|
|
2438
|
+
type: z.literal('send-credential'),
|
|
2439
|
+
alias: z.string(),
|
|
2440
|
+
template: InlineCredentialTemplateValidator,
|
|
2441
|
+
templateData: z.record(z.string(), z.unknown()).optional(),
|
|
2442
|
+
preventDuplicateClaim: z.boolean().optional(),
|
|
2443
|
+
});
|
|
2444
|
+
|
|
2445
|
+
export const SendCredentialEventValidator = z.union([
|
|
2446
|
+
LegacySendCredentialEventValidator,
|
|
2447
|
+
InlineSendCredentialEventValidator,
|
|
2448
|
+
]);
|
|
2449
|
+
|
|
2274
2450
|
export type SendCredentialEvent = z.infer<typeof SendCredentialEventValidator>;
|
|
2275
2451
|
|
|
2276
2452
|
export const CheckCredentialEventValidator = z
|
|
@@ -2425,8 +2601,168 @@ export const GetCountersEventValidator = z.object({
|
|
|
2425
2601
|
|
|
2426
2602
|
export type GetCountersEvent = z.infer<typeof GetCountersEventValidator>;
|
|
2427
2603
|
|
|
2604
|
+
export const ConsentRequestWalletCategoryValidator = z.enum([
|
|
2605
|
+
'Achievement',
|
|
2606
|
+
'Skill',
|
|
2607
|
+
'ID',
|
|
2608
|
+
'Learning History',
|
|
2609
|
+
'Work History',
|
|
2610
|
+
'Social Badges',
|
|
2611
|
+
'Membership',
|
|
2612
|
+
'Accomplishment',
|
|
2613
|
+
'Experiences',
|
|
2614
|
+
'Accommodation',
|
|
2615
|
+
'Family',
|
|
2616
|
+
]);
|
|
2617
|
+
|
|
2618
|
+
export const ConsentRequestPersonalFieldValidator = z.enum([
|
|
2619
|
+
'name',
|
|
2620
|
+
'email',
|
|
2621
|
+
'phone',
|
|
2622
|
+
'birthDate',
|
|
2623
|
+
'country',
|
|
2624
|
+
'avatar',
|
|
2625
|
+
]);
|
|
2626
|
+
|
|
2627
|
+
export const ConsentRequestValidator = z.object({
|
|
2628
|
+
read: z
|
|
2629
|
+
.object({
|
|
2630
|
+
credentialCategories: z.array(ConsentRequestWalletCategoryValidator).optional(),
|
|
2631
|
+
personalFields: z.array(ConsentRequestPersonalFieldValidator).optional(),
|
|
2632
|
+
})
|
|
2633
|
+
.optional(),
|
|
2634
|
+
write: z
|
|
2635
|
+
.object({
|
|
2636
|
+
credentialCategories: z.array(ConsentRequestWalletCategoryValidator).optional(),
|
|
2637
|
+
})
|
|
2638
|
+
.optional(),
|
|
2639
|
+
reason: z.string().optional(),
|
|
2640
|
+
});
|
|
2641
|
+
|
|
2642
|
+
export type ConsentRequest = z.infer<typeof ConsentRequestValidator>;
|
|
2643
|
+
|
|
2644
|
+
export const NormalizedConsentScopesValidator = z.object({
|
|
2645
|
+
read: z.object({
|
|
2646
|
+
credentialCategories: z.array(ConsentRequestWalletCategoryValidator).default([]),
|
|
2647
|
+
personalFields: z.array(ConsentRequestPersonalFieldValidator).default([]),
|
|
2648
|
+
}),
|
|
2649
|
+
write: z.object({
|
|
2650
|
+
credentialCategories: z.array(ConsentRequestWalletCategoryValidator).default([]),
|
|
2651
|
+
}),
|
|
2652
|
+
});
|
|
2653
|
+
|
|
2654
|
+
export type NormalizedConsentScopes = z.infer<typeof NormalizedConsentScopesValidator>;
|
|
2655
|
+
|
|
2656
|
+
export const CapturedTemplateRecordValidator = z
|
|
2657
|
+
.object({
|
|
2658
|
+
alias: z.string(),
|
|
2659
|
+
template: InlineCredentialTemplateValidator,
|
|
2660
|
+
version: z.number().int().min(1),
|
|
2661
|
+
lastUsedAt: z.string(),
|
|
2662
|
+
})
|
|
2663
|
+
.passthrough();
|
|
2664
|
+
|
|
2665
|
+
export type CapturedTemplateRecord = z.infer<typeof CapturedTemplateRecordValidator>;
|
|
2666
|
+
|
|
2667
|
+
export const CapturedConsentRecordValidator = z
|
|
2668
|
+
.object({
|
|
2669
|
+
scopes: NormalizedConsentScopesValidator,
|
|
2670
|
+
reason: z.string().optional(),
|
|
2671
|
+
lastUsedAt: z.string(),
|
|
2672
|
+
})
|
|
2673
|
+
.passthrough();
|
|
2674
|
+
|
|
2675
|
+
export type CapturedConsentRecord = z.infer<typeof CapturedConsentRecordValidator>;
|
|
2676
|
+
|
|
2677
|
+
export const AppManifestValidator = z
|
|
2678
|
+
.object({
|
|
2679
|
+
manifestVersion: z.literal(1),
|
|
2680
|
+
appUrl: z.string(),
|
|
2681
|
+
appKey: z.string().max(200).optional(),
|
|
2682
|
+
suggestedName: z.string().optional(),
|
|
2683
|
+
suggestedIconUrl: z.string().optional(),
|
|
2684
|
+
permissions: z.array(z.string()).default([]),
|
|
2685
|
+
templates: z.array(CapturedTemplateRecordValidator).default([]),
|
|
2686
|
+
consentRequests: z.array(CapturedConsentRecordValidator).default([]),
|
|
2687
|
+
usedConsentContract: z.boolean().optional(),
|
|
2688
|
+
featuresLaunched: z.array(z.string()).default([]),
|
|
2689
|
+
counterKeys: z.array(z.string()).default([]),
|
|
2690
|
+
usedLearnerContext: z.boolean(),
|
|
2691
|
+
usedNotifications: z.boolean(),
|
|
2692
|
+
firstCapturedAt: z.string(),
|
|
2693
|
+
lastUpdatedAt: z.string(),
|
|
2694
|
+
})
|
|
2695
|
+
.passthrough();
|
|
2696
|
+
|
|
2697
|
+
export type AppManifest = z.infer<typeof AppManifestValidator>;
|
|
2698
|
+
|
|
2699
|
+
export const AppManifestVersionStatusValidator = z.enum(['draft', 'active', 'superseded']);
|
|
2700
|
+
export type AppManifestVersionStatus = z.infer<typeof AppManifestVersionStatusValidator>;
|
|
2701
|
+
|
|
2702
|
+
export const AppManifestVersionValidator = z.object({
|
|
2703
|
+
id: z.string(),
|
|
2704
|
+
version: z.number().int().min(1),
|
|
2705
|
+
manifestHash: z.string(),
|
|
2706
|
+
manifest: AppManifestValidator,
|
|
2707
|
+
status: AppManifestVersionStatusValidator,
|
|
2708
|
+
createdAt: z.string(),
|
|
2709
|
+
activatedAt: z.string().optional(),
|
|
2710
|
+
});
|
|
2711
|
+
|
|
2712
|
+
export type AppManifestVersion = z.infer<typeof AppManifestVersionValidator>;
|
|
2713
|
+
|
|
2714
|
+
const AppManifestStringDiffValidator = z.object({
|
|
2715
|
+
added: z.array(z.string()),
|
|
2716
|
+
removed: z.array(z.string()),
|
|
2717
|
+
changed: z.array(z.string()),
|
|
2718
|
+
});
|
|
2719
|
+
|
|
2720
|
+
export const AppManifestTemplateRefValidator = z.object({
|
|
2721
|
+
alias: z.string(),
|
|
2722
|
+
version: z.number().int().min(1),
|
|
2723
|
+
});
|
|
2724
|
+
|
|
2725
|
+
export type AppManifestTemplateRef = z.infer<typeof AppManifestTemplateRefValidator>;
|
|
2726
|
+
|
|
2727
|
+
export const AppManifestTemplateChangeValidator = z.object({
|
|
2728
|
+
alias: z.string(),
|
|
2729
|
+
fromVersion: z.number().int().min(1),
|
|
2730
|
+
toVersion: z.number().int().min(1),
|
|
2731
|
+
});
|
|
2732
|
+
|
|
2733
|
+
export type AppManifestTemplateChange = z.infer<typeof AppManifestTemplateChangeValidator>;
|
|
2734
|
+
|
|
2735
|
+
export const AppManifestDiffValidator = z.object({
|
|
2736
|
+
permissions: AppManifestStringDiffValidator,
|
|
2737
|
+
templates: z.object({
|
|
2738
|
+
added: z.array(AppManifestTemplateRefValidator),
|
|
2739
|
+
removed: z.array(AppManifestTemplateRefValidator),
|
|
2740
|
+
changed: z.array(AppManifestTemplateChangeValidator),
|
|
2741
|
+
}),
|
|
2742
|
+
consentScopes: AppManifestStringDiffValidator,
|
|
2743
|
+
featurePaths: AppManifestStringDiffValidator,
|
|
2744
|
+
counterKeys: AppManifestStringDiffValidator,
|
|
2745
|
+
requiresReview: z.boolean(),
|
|
2746
|
+
});
|
|
2747
|
+
|
|
2748
|
+
export type AppManifestDiff = z.infer<typeof AppManifestDiffValidator>;
|
|
2749
|
+
|
|
2750
|
+
export const UpsertConsentContractEventValidator = z.object({
|
|
2751
|
+
type: z.literal('upsert-consent-contract'),
|
|
2752
|
+
scopes: ConsentRequestValidator,
|
|
2753
|
+
});
|
|
2754
|
+
|
|
2755
|
+
export type UpsertConsentContractEvent = z.infer<typeof UpsertConsentContractEventValidator>;
|
|
2756
|
+
|
|
2757
|
+
export const UpsertConsentContractResponseValidator = z.object({
|
|
2758
|
+
contractUri: z.string(),
|
|
2759
|
+
created: z.boolean(),
|
|
2760
|
+
});
|
|
2761
|
+
|
|
2762
|
+
export type UpsertConsentContractResponse = z.infer<typeof UpsertConsentContractResponseValidator>;
|
|
2763
|
+
|
|
2428
2764
|
// Add new event types here as the union grows
|
|
2429
|
-
export const AppEventValidator = z.
|
|
2765
|
+
export const AppEventValidator = z.union([
|
|
2430
2766
|
SendCredentialEventValidator,
|
|
2431
2767
|
CheckCredentialEventValidator,
|
|
2432
2768
|
CheckIssuanceStatusEventValidator,
|
|
@@ -2437,6 +2773,7 @@ export const AppEventValidator = z.discriminatedUnion('type', [
|
|
|
2437
2773
|
IncrementCounterEventValidator,
|
|
2438
2774
|
GetCounterEventValidator,
|
|
2439
2775
|
GetCountersEventValidator,
|
|
2776
|
+
UpsertConsentContractEventValidator,
|
|
2440
2777
|
]);
|
|
2441
2778
|
|
|
2442
2779
|
export type AppEvent = z.infer<typeof AppEventValidator>;
|