@learncard/core 9.4.24 → 9.4.26

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.
@@ -0,0 +1,212 @@
1
+ import {
2
+ CredentialRecord,
3
+ StoredCredentialEnvelope,
4
+ VC,
5
+ VP,
6
+ JWKWithPrivateKey,
7
+ } from '@learncard/types';
8
+ import { Query } from 'sift';
9
+ import { Plugin } from './wallet';
10
+ import { OmitNevers } from './helpers';
11
+ import { DeepPartial } from './utilities';
12
+
13
+ export type CacheStrategy = 'cache-only' | 'cache-first' | 'skip-cache';
14
+
15
+ export type PlaneOptions = {
16
+ cache?: CacheStrategy;
17
+ };
18
+
19
+ export type ControlPlane = 'read' | 'store' | 'index' | 'cache' | 'id' | 'context';
20
+
21
+ export type FilterForPlane<Plugins extends Plugin[], Plane extends ControlPlane> = [
22
+ Plugins
23
+ ] extends [1 & Plugins]
24
+ ? any
25
+ : {
26
+ [Index in keyof Plugins]: undefined extends Plugins[Index][Plane]
27
+ ? never
28
+ : Plugins[Index]['name'];
29
+ }[number];
30
+
31
+ export type GetPlanesForPlugins<Plugins extends Plugin[]> = any[] extends Plugins
32
+ ? never
33
+ : {
34
+ [Index in keyof Plugins]: {
35
+ [Key in ControlPlane]: undefined extends Plugins[Index][Key] ? never : Key;
36
+ }[ControlPlane];
37
+ }[number];
38
+
39
+ export type GetPlaneProviders<
40
+ Plugins extends Plugin[],
41
+ Plane extends ControlPlane
42
+ > = any[] extends Plugins
43
+ ? any
44
+ : {
45
+ [Index in keyof Plugins]: undefined extends Plugins[Index][Plane]
46
+ ? never
47
+ : OmitNevers<{
48
+ [Name in Plugins[number]['name']]: Name extends Plugins[Index]['name']
49
+ ? { name: Name; displayName?: string; description?: string }
50
+ : never;
51
+ }>;
52
+ }[number];
53
+
54
+ // --- Read --- \\
55
+
56
+ export type ReadPlane = {
57
+ get: (
58
+ uri?: string,
59
+ options?: PlaneOptions
60
+ ) => Promise<VC | VP | StoredCredentialEnvelope | undefined>;
61
+ };
62
+
63
+ export type PluginReadPlane = ReadPlane;
64
+
65
+ export type LearnCardReadPlane<Plugins extends Plugin[]> = ReadPlane & {
66
+ providers: GetPlaneProviders<Plugins, 'read'>;
67
+ };
68
+
69
+ // --- Store --- \\
70
+
71
+ export type EncryptionParams = {
72
+ recipients: string[];
73
+ };
74
+
75
+ export type StorePlane = {
76
+ upload: (vc: VC | VP | StoredCredentialEnvelope, options?: PlaneOptions) => Promise<string>;
77
+ uploadMany?: (
78
+ vcs: Array<VC | VP | StoredCredentialEnvelope>,
79
+ options?: PlaneOptions
80
+ ) => Promise<string[]>;
81
+ uploadEncrypted?: (
82
+ vc: VC | VP | StoredCredentialEnvelope,
83
+ params?: EncryptionParams,
84
+ options?: PlaneOptions
85
+ ) => Promise<string>;
86
+ delete?: (uri: string, options?: PlaneOptions) => Promise<boolean>;
87
+ };
88
+
89
+ export type PluginStorePlane = StorePlane;
90
+
91
+ export type LearnCardStorePlane<Plugins extends Plugin[]> = Record<
92
+ FilterForPlane<Plugins, 'store'>,
93
+ StorePlane
94
+ > & {
95
+ providers: GetPlaneProviders<Plugins, 'store'>;
96
+ };
97
+
98
+ // --- Index --- \\
99
+
100
+ export type IndexPlane = {
101
+ get: <Metadata extends Record<string, any> = Record<never, never>>(
102
+ query?: Partial<Query<CredentialRecord<Metadata>>>,
103
+ options?: PlaneOptions
104
+ ) => Promise<CredentialRecord<Metadata>[]>;
105
+ getPage?: <Metadata extends Record<string, any> = Record<never, never>>(
106
+ query?: Partial<Query<CredentialRecord<Metadata>>>,
107
+ paginationOptions?: { limit?: number; cursor?: string; sort?: string },
108
+ options?: PlaneOptions
109
+ ) => Promise<{ records: CredentialRecord<Metadata>[]; hasMore: boolean; cursor?: string }>;
110
+ getCount?: <Metadata extends Record<string, any> = Record<never, never>>(
111
+ query?: Partial<Query<CredentialRecord<Metadata>>>,
112
+ options?: PlaneOptions
113
+ ) => Promise<number>;
114
+ add: <Metadata extends Record<string, any> = Record<never, never>>(
115
+ record: CredentialRecord<Metadata>,
116
+ options?: PlaneOptions
117
+ ) => Promise<boolean>;
118
+ addMany?: <Metadata extends Record<string, any> = Record<never, never>>(
119
+ records: CredentialRecord<Metadata>[],
120
+ options?: PlaneOptions
121
+ ) => Promise<boolean>;
122
+ update: <Metadata extends Record<string, any> = Record<never, never>>(
123
+ id: string,
124
+ updates: DeepPartial<CredentialRecord<Metadata>>,
125
+ options?: PlaneOptions
126
+ ) => Promise<boolean>;
127
+ remove: (id: string, options?: PlaneOptions) => Promise<boolean>;
128
+ removeAll?: (options?: PlaneOptions) => Promise<boolean>;
129
+ };
130
+
131
+ export type PluginIndexPlane = IndexPlane;
132
+
133
+ export type LearnCardIndexPlane<Plugins extends Plugin[]> = {
134
+ all: Pick<IndexPlane, 'get'>;
135
+ providers: GetPlaneProviders<Plugins, 'index'>;
136
+ } & Record<FilterForPlane<Plugins, 'index'>, IndexPlane>;
137
+
138
+ // --- Cache --- \\
139
+
140
+ export type CachePlane = {
141
+ getIndex: <Metadata extends Record<string, any> = Record<never, never>>(
142
+ name: string,
143
+ query: Partial<Query<CredentialRecord<Metadata>>>
144
+ ) => Promise<CredentialRecord<Metadata>[] | undefined>;
145
+ setIndex: <Metadata extends Record<string, any> = Record<never, never>>(
146
+ name: string,
147
+ query: Partial<Query<CredentialRecord<Metadata>>>,
148
+ value: CredentialRecord<Metadata>[]
149
+ ) => Promise<boolean>;
150
+ getIndexPage: <Metadata extends Record<string, any> = Record<never, never>>(
151
+ name: string,
152
+ query: Partial<Query<CredentialRecord<Metadata>>>,
153
+ paginationOptions?: { limit?: number; cursor?: string; sort?: string }
154
+ ) => Promise<
155
+ { records: CredentialRecord<Metadata>[]; hasMore: boolean; cursor?: string } | undefined
156
+ >;
157
+ setIndexPage: <Metadata extends Record<string, any> = Record<never, never>>(
158
+ name: string,
159
+ query: Partial<Query<CredentialRecord<Metadata>>>,
160
+ value: { records: CredentialRecord<Metadata>[]; hasMore: boolean; cursor?: string },
161
+ paginationOptions?: { limit?: number; cursor?: string }
162
+ ) => Promise<boolean>;
163
+ getIndexCount?: <Metadata extends Record<string, any> = Record<never, never>>(
164
+ name: string,
165
+ query: Partial<Query<CredentialRecord<Metadata>>>
166
+ ) => Promise<number | undefined>;
167
+ setIndexCount?: <Metadata extends Record<string, any> = Record<never, never>>(
168
+ name: string,
169
+ query: Partial<Query<CredentialRecord<Metadata>>>,
170
+ value: number
171
+ ) => Promise<boolean>;
172
+ flushIndex: () => Promise<boolean>;
173
+ getVc: (uri: string) => Promise<VC | VP | StoredCredentialEnvelope | undefined>;
174
+ setVc: (uri: string, value: VC | VP | StoredCredentialEnvelope | undefined) => Promise<boolean>;
175
+ flushVc: () => Promise<boolean>;
176
+ };
177
+ export type PluginCachePlane = CachePlane;
178
+
179
+ export type LearnCardCachePlane<Plugins extends Plugin[]> = CachePlane & {
180
+ providers: GetPlaneProviders<Plugins, 'cache'>;
181
+ };
182
+
183
+ // --- Identity --- \\
184
+
185
+ export type IdPlane = {
186
+ did: (method?: string, options?: PlaneOptions) => string;
187
+ keypair: (algorithm?: string, options?: PlaneOptions) => JWKWithPrivateKey;
188
+ };
189
+
190
+ export type PluginIdPlane = IdPlane;
191
+
192
+ export type LearnCardIdPlane<Plugins extends Plugin[]> = IdPlane & {
193
+ providers: GetPlaneProviders<Plugins, 'id'>;
194
+ };
195
+
196
+ // --- Contexts --- \\
197
+
198
+ export type ContextPlane = {
199
+ resolveDocument: (
200
+ uri: string,
201
+ allowRemote?: boolean
202
+ ) => Promise<Record<string, any> | undefined>;
203
+ };
204
+
205
+ export type PluginContextPlane = {
206
+ resolveStaticDocument: (uri: string) => Promise<Record<string, any> | undefined>;
207
+ resolveRemoteDocument?: (uri: string) => Promise<Record<string, any> | undefined>;
208
+ };
209
+
210
+ export type LearnCardContextPlane<Plugins extends Plugin[]> = ContextPlane & {
211
+ providers: GetPlaneProviders<Plugins, 'context'>;
212
+ };
@@ -0,0 +1,39 @@
1
+ /** @group Utility Types */
2
+ export type Tail<T extends any[]> = T extends [any, ...infer R] ? R : never;
3
+
4
+ /** @group Utility Types */
5
+ export type Last<T extends any[]> = T extends [...any[], infer R] ? R : never;
6
+
7
+ /** @group Utility Types */
8
+ export type RemoveLast<T extends any[]> = T extends [...infer R, any] ? R : [];
9
+
10
+ /** @group Utility Types */
11
+ export type RemoveFirstArg<T extends (...args: any[]) => any> = (
12
+ args: Tail<Parameters<T>>
13
+ ) => ReturnType<T>;
14
+
15
+ /** @group Utility Types */
16
+ export type RemoveLastArg<T extends (...args: any[]) => any> = (
17
+ args: RemoveLast<Parameters<T>>
18
+ ) => ReturnType<T>;
19
+
20
+ /** @group Utility Types */
21
+ export type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
22
+ k: infer I
23
+ ) => void
24
+ ? I
25
+ : never;
26
+
27
+ /** @group Utility Types */
28
+ export type MergeObjects<Objects extends Record<string, any>[]> = undefined extends Objects[2]
29
+ ? Omit<Objects[0], keyof Objects[1]> & Objects[1]
30
+ : Omit<MergeObjects<RemoveLast<Objects>>, keyof Last<Objects>> & Last<Objects>;
31
+
32
+ /** @group Utility Types */
33
+ export type DeepPartial<T> = T extends object
34
+ ? {
35
+ [P in keyof T]?: DeepPartial<T[P]>;
36
+ }
37
+ : T;
38
+
39
+ export type IsAnyOrNever<T> = [T] extends [1 & T] ? true : false;
@@ -0,0 +1,176 @@
1
+ import {
2
+ ControlPlane,
3
+ GetPlanesForPlugins,
4
+ PluginReadPlane,
5
+ PluginStorePlane,
6
+ PluginIndexPlane,
7
+ PluginCachePlane,
8
+ PluginIdPlane,
9
+ PluginContextPlane,
10
+ LearnCardReadPlane,
11
+ LearnCardStorePlane,
12
+ LearnCardIndexPlane,
13
+ LearnCardCachePlane,
14
+ LearnCardIdPlane,
15
+ LearnCardContextPlane,
16
+ } from './planes';
17
+ import { UnionToIntersection, MergeObjects, IsAnyOrNever } from './utilities';
18
+
19
+ export type GenerateLearnCard<
20
+ NewControlPlanes extends ControlPlane = never,
21
+ NewMethods extends Record<string, (...args: any[]) => any> = Record<never, never>,
22
+ ControlPlanes extends ControlPlane = never,
23
+ Methods extends Record<string, (...args: any[]) => any> = Record<never, never>
24
+ > = LearnCard<
25
+ any,
26
+ IsAnyOrNever<ControlPlanes> extends true
27
+ ? NewControlPlanes
28
+ : IsAnyOrNever<NewControlPlanes> extends true
29
+ ? ControlPlanes
30
+ : ControlPlanes | NewControlPlanes,
31
+ NewMethods & Methods
32
+ >;
33
+
34
+ export type AddImplicitLearnCardArgument<
35
+ Functions extends Record<string, (...args: any[]) => any> = Record<never, never>,
36
+ ControlPlanes extends ControlPlane = never,
37
+ Methods extends Record<string, (...args: any[]) => any> = Record<never, never>,
38
+ DependentControlPlanes extends ControlPlane = never,
39
+ DependentMethods extends Record<string, (...args: any[]) => any> = Record<never, never>
40
+ > = {
41
+ [Key in keyof Functions]: <
42
+ T extends GenerateLearnCard<
43
+ ControlPlanes,
44
+ Methods,
45
+ DependentControlPlanes,
46
+ DependentMethods
47
+ >
48
+ >(
49
+ learnCard: T,
50
+ ...args: Parameters<Functions[Key]>
51
+ ) => ReturnType<Functions[Key]>;
52
+ };
53
+
54
+ /** @group Universal Wallets */
55
+ export type GetPluginMethods<Plugins extends Plugin[]> = undefined extends Plugins[1]
56
+ ? NonNullable<Plugins[0]['_methods']>
57
+ : UnionToIntersection<
58
+ NonNullable<
59
+ MergeObjects<{ [Key in keyof Plugins]: NonNullable<Plugins[Key]['_methods']> }>
60
+ >
61
+ >;
62
+
63
+ /** @group Universal Wallets */
64
+ export type Plugin<
65
+ Name extends string = string,
66
+ ControlPlanes extends ControlPlane = any,
67
+ Methods extends Record<string, (...args: any[]) => any> = Record<never, never>,
68
+ DependentControlPlanes extends ControlPlane = never,
69
+ DependentMethods extends Record<string, (...args: any[]) => any> = Record<never, never>
70
+ > = {
71
+ name: Name;
72
+ displayName?: string;
73
+ description?: string;
74
+ methods: AddImplicitLearnCardArgument<
75
+ Methods,
76
+ ControlPlanes,
77
+ Methods,
78
+ DependentControlPlanes,
79
+ DependentMethods
80
+ >;
81
+ _methods?: Methods;
82
+ read?: {};
83
+ store?: {};
84
+ index?: {};
85
+ cache?: {};
86
+ id?: {};
87
+ context?: {};
88
+ } & (IsAnyOrNever<ControlPlanes> extends true
89
+ ? {}
90
+ : ('read' extends ControlPlanes
91
+ ? {
92
+ read: AddImplicitLearnCardArgument<
93
+ PluginReadPlane,
94
+ ControlPlanes,
95
+ Methods,
96
+ DependentControlPlanes,
97
+ DependentMethods
98
+ >;
99
+ }
100
+ : {}) &
101
+ ('store' extends ControlPlanes
102
+ ? {
103
+ store: AddImplicitLearnCardArgument<
104
+ PluginStorePlane,
105
+ ControlPlanes,
106
+ Methods,
107
+ DependentControlPlanes,
108
+ DependentMethods
109
+ >;
110
+ }
111
+ : {}) &
112
+ ('index' extends ControlPlanes
113
+ ? {
114
+ index: AddImplicitLearnCardArgument<
115
+ PluginIndexPlane,
116
+ ControlPlanes,
117
+ Methods,
118
+ DependentControlPlanes,
119
+ DependentMethods
120
+ >;
121
+ }
122
+ : {}) &
123
+ ('cache' extends ControlPlanes
124
+ ? {
125
+ cache: AddImplicitLearnCardArgument<
126
+ PluginCachePlane,
127
+ ControlPlanes,
128
+ Methods,
129
+ DependentControlPlanes,
130
+ DependentMethods
131
+ >;
132
+ }
133
+ : {}) &
134
+ ('id' extends ControlPlanes
135
+ ? {
136
+ id: AddImplicitLearnCardArgument<
137
+ PluginIdPlane,
138
+ ControlPlanes,
139
+ Methods,
140
+ DependentControlPlanes,
141
+ DependentMethods
142
+ >;
143
+ }
144
+ : {}) &
145
+ ('context' extends ControlPlanes
146
+ ? {
147
+ context: AddImplicitLearnCardArgument<
148
+ PluginContextPlane,
149
+ ControlPlanes,
150
+ Methods,
151
+ DependentControlPlanes,
152
+ DependentMethods
153
+ >;
154
+ }
155
+ : {}));
156
+
157
+ /** @group Universal Wallets */
158
+ export type LearnCard<
159
+ Plugins extends Plugin[] = [],
160
+ ControlPlanes extends ControlPlane = GetPlanesForPlugins<Plugins>,
161
+ PluginMethods = GetPluginMethods<Plugins>
162
+ > = {
163
+ plugins: Plugins;
164
+ invoke: PluginMethods;
165
+ addPlugin: <NewPlugin extends Plugin>(
166
+ plugin: NewPlugin
167
+ ) => Promise<LearnCard<[...Plugins, NewPlugin]>>;
168
+ debug?: typeof console.log;
169
+ } & (IsAnyOrNever<ControlPlanes> extends true
170
+ ? {}
171
+ : ('read' extends ControlPlanes ? { read: LearnCardReadPlane<Plugins> } : {}) &
172
+ ('store' extends ControlPlanes ? { store: LearnCardStorePlane<Plugins> } : {}) &
173
+ ('index' extends ControlPlanes ? { index: LearnCardIndexPlane<Plugins> } : {}) &
174
+ ('cache' extends ControlPlanes ? { cache: LearnCardCachePlane<Plugins> } : {}) &
175
+ ('id' extends ControlPlanes ? { id: LearnCardIdPlane<Plugins> } : {}) &
176
+ ('context' extends ControlPlanes ? { context: LearnCardContextPlane<Plugins> } : {}));
@@ -0,0 +1,5 @@
1
+ import crypto from 'isomorphic-webcrypto';
2
+
3
+ if (typeof window === 'undefined' && !globalThis.crypto) globalThis.crypto = crypto;
4
+
5
+ export default crypto;
@@ -0,0 +1,73 @@
1
+ import { LearnCard, Plugin } from '../../types/wallet';
2
+ import { ControlPlane } from '../../types/planes';
3
+
4
+ /** Type guard for removing null/undefined */
5
+ export const isNotNull = <T>(item?: T | null): item is T => !!item;
6
+
7
+ /** Removes null/undefined from items in an array */
8
+ export const filterNulls = <T>(array: (T | null | undefined)[]) => array.filter(isNotNull);
9
+
10
+ /**
11
+ * Loops through an array, running a callback over it until that callback returns a value. Once the
12
+ * first value is found, it is returned. If no value is found, undefined is returned instead
13
+ */
14
+ export const findFirstResult = <T, U>(
15
+ array: T[],
16
+ callback: (item: T) => U | undefined
17
+ ): U | undefined => {
18
+ return array.reduce<U | undefined>((result, item) => {
19
+ if (result !== undefined) return result;
20
+
21
+ return callback(item);
22
+ }, undefined);
23
+ };
24
+
25
+ export const pluginImplementsPlane = <Plane extends ControlPlane>(
26
+ plugin: Plugin,
27
+ plane: Plane
28
+ ): plugin is Plugin<any, Plane> => {
29
+ if (plane === 'read') return 'get' in (plugin.read ?? {});
30
+ if (plane === 'store') return 'upload' in (plugin.store ?? {});
31
+ if (plane === 'index') return 'get' in (plugin.index ?? {});
32
+ if (plane === 'cache') return 'getIndex' in (plugin.cache ?? {});
33
+ if (plane === 'id') return 'did' in (plugin.id ?? {});
34
+ if (plane === 'context') return 'resolveStaticDocument' in (plugin.context ?? {});
35
+
36
+ return false;
37
+ };
38
+
39
+ export const learnCardImplementsPlane = <Plane extends ControlPlane>(
40
+ learnCard: LearnCard<any, any, any>,
41
+ plane: Plane
42
+ ): learnCard is LearnCard<any, Plane> => {
43
+ if (plane === 'read') return 'read' in learnCard;
44
+ if (plane === 'store') return 'store' in learnCard;
45
+ if (plane === 'index') return 'index' in learnCard;
46
+ if (plane === 'cache') return 'cache' in learnCard;
47
+ if (plane === 'id') return 'id' in learnCard;
48
+ if (plane === 'context') return 'context' in learnCard;
49
+
50
+ return false;
51
+ };
52
+
53
+ export const mapObject = <T extends string, U, V>(
54
+ obj: Record<T, U>,
55
+ callback: (value: U, index: number) => V
56
+ ): Record<T, V> => {
57
+ return Object.fromEntries(
58
+ Object.entries<U>(obj).map(([key, value], index) => [key, callback(value, index)])
59
+ ) as any;
60
+ };
61
+
62
+ export const isFulfilledAndNotEmpty = <T>(
63
+ input: PromiseSettledResult<T>
64
+ ): input is PromiseFulfilledResult<T> => input.status === 'fulfilled' && !!input.value;
65
+
66
+ export const uniqBy = <Obj extends Record<string, any>>(
67
+ array: Obj[],
68
+ key: keyof Obj | ((obj: Obj) => any)
69
+ ) => {
70
+ return [
71
+ ...new Map(array.map(obj => [key instanceof Function ? key(obj) : obj[key], obj])).values(),
72
+ ];
73
+ };
@@ -0,0 +1,2 @@
1
+ export * from './wallet';
2
+ export * from './crypto';