@openstax/ts-utils 1.20.1 → 1.20.3

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.
@@ -80,6 +80,8 @@ export declare const mapFind: <I, R>(array: I[], mapper: (item: I) => R, predica
80
80
  /**
81
81
  * returns a function that will only ever call the given function once, returning the first result for every subsequent call
82
82
  *
83
+ * does not cache rejected promises, to avoid cache poisoning on failed async requests
84
+ *
83
85
  * eg:
84
86
  * const heavyFunction = () => 'hello';
85
87
  * const butOnlyOnce = once(() => 'hello');
@@ -90,6 +92,8 @@ export declare const mapFind: <I, R>(array: I[], mapper: (item: I) => R, predica
90
92
  export declare const once: <F extends (...args: any[]) => any>(fn: F) => F;
91
93
  /**
92
94
  * memoizes a function with any number of arguments
95
+ *
96
+ * does not cache rejected promises, to avoid cache poisoning on failed async requests
93
97
  */
94
98
  export declare const memoize: <F extends (...args: any[]) => any>(fn: F) => F;
95
99
  /**
@@ -107,6 +107,8 @@ exports.mapFind = mapFind;
107
107
  /**
108
108
  * returns a function that will only ever call the given function once, returning the first result for every subsequent call
109
109
  *
110
+ * does not cache rejected promises, to avoid cache poisoning on failed async requests
111
+ *
110
112
  * eg:
111
113
  * const heavyFunction = () => 'hello';
112
114
  * const butOnlyOnce = once(() => 'hello');
@@ -133,6 +135,8 @@ const once = (fn) => {
133
135
  exports.once = once;
134
136
  /**
135
137
  * memoizes a function with any number of arguments
138
+ *
139
+ * does not cache rejected promises, to avoid cache poisoning on failed async requests
136
140
  */
137
141
  const memoize = (fn) => {
138
142
  const cache = {};
@@ -155,7 +159,15 @@ const memoize = (fn) => {
155
159
  };
156
160
  return ((...args) => {
157
161
  const thisCache = resolveCache(cache, args);
158
- return thisCache.result = (thisCache.result || fn(...args));
162
+ if ('result' in thisCache) {
163
+ return thisCache.result;
164
+ }
165
+ thisCache.result = fn(...args);
166
+ if (typeof thisCache.result === 'object' && thisCache.result instanceof Promise) {
167
+ // Clear the result when possible but do not return a Promise that resolves to the initialValue
168
+ thisCache.result.catch(() => delete thisCache.result);
169
+ }
170
+ return thisCache.result;
159
171
  });
160
172
  };
161
173
  exports.memoize = memoize;
@@ -1,5 +1,5 @@
1
- import { XapiStatement, EagerXapiStatement } from ".";
2
- import { User } from "../authProvider";
3
- export declare const addStatementDefaultFields: (statement: Pick<XapiStatement, 'object' | 'verb' | 'context' | 'result'> & {
1
+ import { User } from '../authProvider';
2
+ import { EagerXapiStatement, UXapiStatement, XapiStatement } from '.';
3
+ export declare const addStatementDefaultFields: (statement: (Pick<XapiStatement, 'object' | 'verb' | 'context' | 'result'> & {
4
4
  id?: string;
5
- }, user: User) => EagerXapiStatement;
5
+ }) | UXapiStatement, user: User) => EagerXapiStatement;
@@ -8,7 +8,6 @@ const formatISO_1 = __importDefault(require("date-fns/formatISO"));
8
8
  const uuid_1 = require("uuid");
9
9
  const addStatementDefaultFields = (statement, user) => ({
10
10
  id: (0, uuid_1.v4)(),
11
- ...statement,
12
11
  actor: {
13
12
  account: {
14
13
  homePage: 'https://openstax.org',
@@ -16,6 +15,7 @@ const addStatementDefaultFields = (statement, user) => ({
16
15
  },
17
16
  objectType: 'Agent',
18
17
  },
19
- timestamp: (0, formatISO_1.default)(new Date())
18
+ timestamp: (0, formatISO_1.default)(new Date()),
19
+ ...statement,
20
20
  });
21
21
  exports.addStatementDefaultFields = addStatementDefaultFields;
@@ -1,23 +1,23 @@
1
- import { LrsGateway, XapiStatement } from '.';
1
+ import { LrsGateway, UXapiStatement } from '.';
2
2
  export declare type ActivityState = {
3
3
  attempts: number;
4
4
  completedAttempts: number;
5
- currentAttempt?: XapiStatement;
6
- currentAttemptCompleted?: XapiStatement;
7
- currentAttemptStatements: XapiStatement[];
8
- mostRecentAttemptWithCompleted?: XapiStatement;
9
- mostRecentAttemptWithCompletedCompleted?: XapiStatement;
5
+ currentAttempt?: UXapiStatement;
6
+ currentAttemptCompleted?: UXapiStatement;
7
+ currentAttemptStatements: UXapiStatement[];
8
+ mostRecentAttemptWithCompleted?: UXapiStatement;
9
+ mostRecentAttemptWithCompletedCompleted?: UXapiStatement;
10
10
  };
11
- export declare const matchAttempt: (statement: XapiStatement) => boolean;
12
- export declare const matchAttemptCompleted: (attempt: XapiStatement) => (statement: XapiStatement) => boolean;
13
- export declare const resolveAttempts: (statements: XapiStatement[], options?: {
11
+ export declare const matchAttempt: (statement: UXapiStatement) => boolean;
12
+ export declare const matchAttemptCompleted: (attempt: UXapiStatement) => (statement: UXapiStatement) => boolean;
13
+ export declare const resolveAttempts: (statements: UXapiStatement[], options?: {
14
14
  activityIRI?: string | undefined;
15
15
  parentActivityAttempt?: string | undefined;
16
- } | undefined) => XapiStatement[];
17
- export declare const resolveCompletedForAttempt: (statements: XapiStatement[], attempt: XapiStatement, activityIRI?: string | undefined) => XapiStatement | undefined;
18
- export declare const oldestStatement: (statements: XapiStatement[]) => XapiStatement | undefined;
19
- export declare const mostRecentStatement: (statements: XapiStatement[]) => XapiStatement | undefined;
20
- export declare const resolveAttemptInfo: (statements: XapiStatement[], options?: {
16
+ } | undefined) => UXapiStatement[];
17
+ export declare const resolveCompletedForAttempt: (statements: UXapiStatement[], attempt: UXapiStatement, activityIRI?: string | undefined) => UXapiStatement | undefined;
18
+ export declare const oldestStatement: (statements: UXapiStatement[]) => UXapiStatement | undefined;
19
+ export declare const mostRecentStatement: (statements: UXapiStatement[]) => UXapiStatement | undefined;
20
+ export declare const resolveAttemptInfo: (statements: UXapiStatement[], options?: {
21
21
  activityIRI?: string | undefined;
22
22
  currentAttempt?: string | undefined;
23
23
  parentActivityAttempt?: string | undefined;
@@ -28,20 +28,20 @@ export declare const loadStatementsForActivityAndFirstChildren: (gateway: LrsGat
28
28
  attempt?: string | undefined;
29
29
  ensureSync?: boolean | undefined;
30
30
  user?: string | undefined;
31
- } | undefined) => Promise<XapiStatement[]>;
31
+ } | undefined) => Promise<import(".").XapiStatement[]>;
32
32
  export declare const loadActivityAttemptInfo: (gateway: LrsGateway, activityIRI: string, options?: {
33
33
  currentAttempt?: string | undefined;
34
34
  parentActivityAttempt?: string | undefined;
35
35
  ensureSync?: boolean | undefined;
36
36
  } | undefined) => Promise<ActivityState>;
37
- export declare const createStatement: (verb: XapiStatement['verb'], activity: {
37
+ export declare const createStatement: (verb: UXapiStatement['verb'], activity: {
38
38
  iri: string;
39
39
  type: string;
40
40
  name: string;
41
41
  extensions?: {
42
42
  [key: string]: string;
43
43
  } | undefined;
44
- }, attempt: string, parentActivityIRI?: string | undefined) => Pick<XapiStatement, 'object' | 'verb' | 'context'>;
44
+ }, attempt: string, parentActivityIRI?: string | undefined) => Pick<UXapiStatement, 'object' | 'verb' | 'context'>;
45
45
  export declare const createAttemptStatement: (activity: {
46
46
  iri: string;
47
47
  type: string;
@@ -52,7 +52,7 @@ export declare const createAttemptStatement: (activity: {
52
52
  }, parentActivity?: {
53
53
  iri?: string | undefined;
54
54
  attempt?: string | undefined;
55
- } | undefined) => Pick<XapiStatement, 'object' | 'verb' | 'context'>;
55
+ } | undefined) => Pick<UXapiStatement, 'object' | 'verb' | 'context'>;
56
56
  export declare const putAttemptStatement: (gateway: LrsGateway, activity: {
57
57
  iri: string;
58
58
  type: string;
@@ -64,7 +64,7 @@ export declare const putAttemptStatement: (gateway: LrsGateway, activity: {
64
64
  iri?: string | undefined;
65
65
  attempt?: string | undefined;
66
66
  } | undefined) => Promise<import(".").EagerXapiStatement>;
67
- export declare const createAttemptActivityStatement: (attemptStatement: XapiStatement, verb: XapiStatement['verb'], result?: XapiStatement['result']) => Pick<XapiStatement, 'object' | 'verb' | 'context' | 'result'>;
68
- export declare const putAttemptActivityStatement: (gateway: LrsGateway, attemptStatement: XapiStatement, verb: XapiStatement['verb'], result?: XapiStatement['result']) => Promise<import(".").EagerXapiStatement>;
69
- export declare const createCompletedStatement: (attemptStatement: XapiStatement, result?: XapiStatement['result']) => Pick<XapiStatement, 'object' | 'verb' | 'context' | 'result'>;
70
- export declare const putCompletedStatement: (gateway: LrsGateway, attemptStatement: XapiStatement, result: XapiStatement['result']) => Promise<import(".").EagerXapiStatement>;
67
+ export declare const createAttemptActivityStatement: (attemptStatement: UXapiStatement, verb: UXapiStatement['verb'], result?: UXapiStatement['result']) => Pick<UXapiStatement, 'object' | 'verb' | 'context' | 'result'>;
68
+ export declare const putAttemptActivityStatement: (gateway: LrsGateway, attemptStatement: UXapiStatement, verb: UXapiStatement['verb'], result?: UXapiStatement['result']) => Promise<import(".").EagerXapiStatement>;
69
+ export declare const createCompletedStatement: (attemptStatement: UXapiStatement, result?: UXapiStatement['result']) => Pick<UXapiStatement, 'object' | 'verb' | 'context' | 'result'>;
70
+ export declare const putCompletedStatement: (gateway: LrsGateway, attemptStatement: UXapiStatement, result: UXapiStatement['result']) => Promise<import(".").EagerXapiStatement>;
@@ -42,9 +42,9 @@ exports.resolveAttempts = resolveAttempts;
42
42
  const resolveCompletedForAttempt = (statements, attempt, activityIRI) => statements.find(statement => (0, exports.matchAttemptCompleted)(attempt)(statement)
43
43
  && (!activityIRI || statement.object.id === activityIRI));
44
44
  exports.resolveCompletedForAttempt = resolveCompletedForAttempt;
45
- const oldestStatement = (statements) => statements.reduce((result, statement) => result && (0, isBefore_1.default)((0, parseISO_1.default)(result.stored || result.timestamp), (0, parseISO_1.default)(statement.timestamp)) ? result : statement, statements[0]);
45
+ const oldestStatement = (statements) => statements.reduce((result, statement) => result && (0, isBefore_1.default)((0, parseISO_1.default)('stored' in result && result.stored ? result.stored : result.timestamp), (0, parseISO_1.default)(statement.timestamp)) ? result : statement, statements[0]);
46
46
  exports.oldestStatement = oldestStatement;
47
- const mostRecentStatement = (statements) => statements.reduce((result, statement) => result && (0, isAfter_1.default)((0, parseISO_1.default)(result.stored || result.timestamp), (0, parseISO_1.default)(statement.timestamp)) ? result : statement, statements[0]);
47
+ const mostRecentStatement = (statements) => statements.reduce((result, statement) => result && (0, isAfter_1.default)((0, parseISO_1.default)('stored' in result && result.stored ? result.stored : result.timestamp), (0, parseISO_1.default)(statement.timestamp)) ? result : statement, statements[0]);
48
48
  exports.mostRecentStatement = mostRecentStatement;
49
49
  const resolveAttemptInfo = (statements, options) => {
50
50
  // TODO optimize. i'm 100% that this could all be done in one iteration but i'm not messing around with that for now.
@@ -74,6 +74,7 @@ export interface XapiStatement {
74
74
  }
75
75
  export declare type SavedXapiStatement = WithRequired<XapiStatement, 'stored'>;
76
76
  export declare type EagerXapiStatement = Omit<XapiStatement, 'stored'>;
77
+ export declare type UXapiStatement = XapiStatement | EagerXapiStatement | SavedXapiStatement;
77
78
  export declare type LrsGateway = ReturnType<ReturnType<ReturnType<typeof lrsGateway>>>;
78
79
  export declare type LrsProvider = ReturnType<ReturnType<typeof lrsGateway>>;
79
80
  export declare const lrsGateway: <C extends string = "lrs">(initializer: Initializer<C>) => (configProvider: { [key in C]: {
@@ -82,7 +83,7 @@ export declare const lrsGateway: <C extends string = "lrs">(initializer: Initial
82
83
  }; }) => (authProvider: AuthProvider) => {
83
84
  putXapiStatements: (statements: Array<(Pick<XapiStatement, 'object' | 'verb' | 'context' | 'result'> & {
84
85
  id?: string;
85
- }) | EagerXapiStatement>) => Promise<EagerXapiStatement[]>;
86
+ }) | UXapiStatement>) => Promise<EagerXapiStatement[]>;
86
87
  getXapiStatements: (params: {
87
88
  verb?: string | undefined;
88
89
  activity?: string | undefined;