@openstax/ts-utils 1.2.9 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/dist/cjs/assertions.d.ts +75 -0
  2. package/dist/cjs/assertions.js +74 -29
  3. package/dist/cjs/aws/ssmService.d.ts +3 -0
  4. package/dist/cjs/aws/ssmService.js +3 -0
  5. package/dist/cjs/config/awsParameterConfig.d.ts +8 -0
  6. package/dist/cjs/config/awsParameterConfig.js +8 -0
  7. package/dist/cjs/config/envConfig.d.ts +21 -0
  8. package/dist/cjs/config/envConfig.js +20 -14
  9. package/dist/cjs/config/index.d.ts +28 -0
  10. package/dist/cjs/config/index.js +3 -6
  11. package/dist/cjs/config/lambdaParameterConfig.d.ts +10 -0
  12. package/dist/cjs/config/lambdaParameterConfig.js +10 -2
  13. package/dist/cjs/config/replaceConfig.d.ts +10 -0
  14. package/dist/cjs/config/replaceConfig.js +10 -0
  15. package/dist/cjs/config/resolveConfigValue.d.ts +3 -0
  16. package/dist/cjs/config/resolveConfigValue.js +2 -2
  17. package/dist/cjs/errors.d.ts +27 -0
  18. package/dist/cjs/errors.js +29 -2
  19. package/dist/cjs/guards.d.ts +24 -0
  20. package/dist/cjs/guards.js +18 -19
  21. package/dist/cjs/middleware/apiErrorHandler.d.ts +8 -0
  22. package/dist/cjs/middleware/apiErrorHandler.js +8 -0
  23. package/dist/cjs/middleware/apiSlowResponseMiddleware.d.ts +12 -0
  24. package/dist/cjs/middleware/apiSlowResponseMiddleware.js +12 -0
  25. package/dist/cjs/middleware/lambdaCorsResponseMiddleware.d.ts +11 -1
  26. package/dist/cjs/middleware/lambdaCorsResponseMiddleware.js +11 -1
  27. package/dist/cjs/middleware/throwNotFoundMiddleware.d.ts +3 -0
  28. package/dist/cjs/middleware/throwNotFoundMiddleware.js +3 -0
  29. package/dist/cjs/middleware.d.ts +38 -0
  30. package/dist/cjs/middleware.js +26 -16
  31. package/dist/cjs/misc/hashValue.d.ts +5 -0
  32. package/dist/cjs/misc/hashValue.js +3 -4
  33. package/dist/cjs/misc/helpers.d.ts +94 -0
  34. package/dist/cjs/misc/helpers.js +52 -14
  35. package/dist/cjs/misc/merge.d.ts +18 -0
  36. package/dist/cjs/misc/merge.js +10 -3
  37. package/dist/cjs/misc/partitionSequence.d.ts +31 -0
  38. package/dist/cjs/misc/partitionSequence.js +1 -1
  39. package/dist/cjs/pagination.d.ts +28 -0
  40. package/dist/cjs/pagination.js +1 -1
  41. package/dist/cjs/routing/helpers.d.ts +45 -0
  42. package/dist/cjs/routing/helpers.js +40 -22
  43. package/dist/cjs/routing/index.d.ts +169 -0
  44. package/dist/cjs/routing/index.js +127 -45
  45. package/dist/cjs/services/apiGateway/index.d.ts +1 -0
  46. package/dist/cjs/services/apiGateway/index.js +2 -0
  47. package/dist/cjs/services/authProvider/index.d.ts +2 -1
  48. package/dist/cjs/services/authProvider/utils/decryptAndVerify.d.ts +9 -0
  49. package/dist/cjs/services/authProvider/utils/decryptAndVerify.js +9 -0
  50. package/dist/cjs/services/logger/console.d.ts +3 -0
  51. package/dist/cjs/services/logger/console.js +3 -0
  52. package/dist/cjs/services/logger/index.d.ts +25 -0
  53. package/dist/cjs/services/logger/index.js +11 -0
  54. package/dist/cjs/services/lrsGateway/attempt-utils.d.ts +7 -10
  55. package/dist/cjs/services/lrsGateway/attempt-utils.js +19 -40
  56. package/dist/cjs/services/lrsGateway/file-system.js +17 -3
  57. package/dist/cjs/services/lrsGateway/index.d.ts +7 -1
  58. package/dist/cjs/services/lrsGateway/index.js +2 -2
  59. package/dist/cjs/tsconfig.without-specs.cjs.tsbuildinfo +1 -1
  60. package/dist/cjs/types.d.ts +21 -0
  61. package/dist/esm/assertions.d.ts +75 -0
  62. package/dist/esm/assertions.js +74 -29
  63. package/dist/esm/aws/ssmService.d.ts +3 -0
  64. package/dist/esm/aws/ssmService.js +3 -0
  65. package/dist/esm/config/awsParameterConfig.d.ts +8 -0
  66. package/dist/esm/config/awsParameterConfig.js +8 -0
  67. package/dist/esm/config/envConfig.d.ts +21 -0
  68. package/dist/esm/config/envConfig.js +20 -14
  69. package/dist/esm/config/index.d.ts +28 -0
  70. package/dist/esm/config/index.js +3 -6
  71. package/dist/esm/config/lambdaParameterConfig.d.ts +10 -0
  72. package/dist/esm/config/lambdaParameterConfig.js +10 -2
  73. package/dist/esm/config/replaceConfig.d.ts +10 -0
  74. package/dist/esm/config/replaceConfig.js +10 -0
  75. package/dist/esm/config/resolveConfigValue.d.ts +3 -0
  76. package/dist/esm/config/resolveConfigValue.js +2 -2
  77. package/dist/esm/errors.d.ts +27 -0
  78. package/dist/esm/errors.js +27 -0
  79. package/dist/esm/guards.d.ts +24 -0
  80. package/dist/esm/guards.js +18 -19
  81. package/dist/esm/middleware/apiErrorHandler.d.ts +8 -0
  82. package/dist/esm/middleware/apiErrorHandler.js +8 -0
  83. package/dist/esm/middleware/apiSlowResponseMiddleware.d.ts +12 -0
  84. package/dist/esm/middleware/apiSlowResponseMiddleware.js +12 -0
  85. package/dist/esm/middleware/lambdaCorsResponseMiddleware.d.ts +11 -1
  86. package/dist/esm/middleware/lambdaCorsResponseMiddleware.js +11 -1
  87. package/dist/esm/middleware/throwNotFoundMiddleware.d.ts +3 -0
  88. package/dist/esm/middleware/throwNotFoundMiddleware.js +3 -0
  89. package/dist/esm/middleware.d.ts +38 -0
  90. package/dist/esm/middleware.js +26 -16
  91. package/dist/esm/misc/hashValue.d.ts +5 -0
  92. package/dist/esm/misc/hashValue.js +3 -4
  93. package/dist/esm/misc/helpers.d.ts +94 -0
  94. package/dist/esm/misc/helpers.js +48 -10
  95. package/dist/esm/misc/merge.d.ts +18 -0
  96. package/dist/esm/misc/merge.js +10 -3
  97. package/dist/esm/misc/partitionSequence.d.ts +31 -0
  98. package/dist/esm/misc/partitionSequence.js +1 -1
  99. package/dist/esm/pagination.d.ts +28 -0
  100. package/dist/esm/pagination.js +1 -1
  101. package/dist/esm/routing/helpers.d.ts +45 -0
  102. package/dist/esm/routing/helpers.js +40 -22
  103. package/dist/esm/routing/index.d.ts +169 -0
  104. package/dist/esm/routing/index.js +127 -45
  105. package/dist/esm/services/apiGateway/index.d.ts +1 -0
  106. package/dist/esm/services/apiGateway/index.js +2 -0
  107. package/dist/esm/services/authProvider/index.d.ts +2 -1
  108. package/dist/esm/services/authProvider/utils/decryptAndVerify.d.ts +9 -0
  109. package/dist/esm/services/authProvider/utils/decryptAndVerify.js +9 -0
  110. package/dist/esm/services/logger/console.d.ts +3 -0
  111. package/dist/esm/services/logger/console.js +3 -0
  112. package/dist/esm/services/logger/index.d.ts +25 -0
  113. package/dist/esm/services/logger/index.js +11 -0
  114. package/dist/esm/services/lrsGateway/attempt-utils.d.ts +7 -10
  115. package/dist/esm/services/lrsGateway/attempt-utils.js +16 -35
  116. package/dist/esm/services/lrsGateway/file-system.js +17 -3
  117. package/dist/esm/services/lrsGateway/index.d.ts +7 -1
  118. package/dist/esm/services/lrsGateway/index.js +2 -2
  119. package/dist/esm/tsconfig.without-specs.esm.tsbuildinfo +1 -1
  120. package/dist/esm/types.d.ts +21 -0
  121. package/package.json +1 -1
  122. package/script/bin/copy-from-template.bash +1 -1
  123. package/script/.build-dist.swo +0 -0
  124. package/script/.build-dist.swp +0 -0
@@ -3,46 +3,63 @@ import queryString from 'query-string';
3
3
  import { mapFind, memoize } from '../misc/helpers';
4
4
  import { createProfile } from '../profile';
5
5
  import { createConsoleLogger } from '../services/logger/console';
6
- /*
7
- * route definition helper. the only required params of the route are the name, path, and handler. other params
8
- * can be added to the type and then later used in the routeMatcher. when defining the `createRoute` method, only
9
- * the request input format is defined, the result format is derived from the routes.
6
+ /**
7
+ * Makes a createRoute function that can be used to create routes (this is a factory factory). The
8
+ * `makeCreateRoute` function is typically called once in the backend and once in the frontend to
9
+ * set the types for the resulting `createRoute` function -- that latter function is called once
10
+ * per route. E.g. for the backend, the call could look like:
10
11
  *
11
- * eg:
12
- * export const createRoute = makeCreateRoute<AppServices, ApiRouteRequest, {
13
- * method: METHOD;
14
- * }>();
12
+ * ```
13
+ * export const createRoute = makeCreateRoute<AppServices, ApiRouteRequest, {
14
+ * method: METHOD;
15
+ * }>();
16
+ * ```
15
17
  *
16
- * eg when defining requestServiceProvider in line, the types have a hard time, it helps to put in another argument:
17
- * export const exampleRoute = createRoute({name: 'exampleRoute', method: METHOD.GET, path: '/api/example/:key',
18
- * requestServiceProvider: requestServiceProvider({
19
- * cookieAuthMiddleware,
20
- * documentStoreMiddleware,
21
- * }},
22
- * async(params: {key: string}, services) => {
23
- * const result = await services.myDocumentStore.getItem(params.key);
18
+ * Notes:
19
+ * * The `{method: METHOD}` defines the `Ex` extension type; here, the `method` property is only
20
+ * relevant to backend routes.
21
+ * * when defining the `createRoute` method, only the request input format is defined, the output
22
+ * format is derived from the routes.
23
+ *
24
+ * When calling the resulting `createRoute` function, the only required params of the route are the
25
+ * name, path, and handler. Other params can be added to the type and then later used in the
26
+ * routeMatcher.
24
27
  *
25
- * if (!result) {
26
- * throw new NotFoundError('requested item not found');
27
- * }
28
+ * eg when defining requestServiceProvider in line, the types have a hard time, it helps to put in another argument:
29
+ * ```
30
+ * export const exampleRoute = createRoute({
31
+ * name: 'exampleRoute', method: METHOD.GET, path: '/api/example/:key',
32
+ * requestServiceProvider: composeServiceMiddleware({
33
+ * cookieAuthMiddleware,
34
+ * documentStoreMiddleware,
35
+ * }},
36
+ * async(params: {key: string}, services) => {
37
+ * const result = await services.myDocumentStore.getItem(params.key);
28
38
  *
29
- * return apiJsonResponse(200, result);
39
+ * if (!result) {
40
+ * throw new NotFoundError('requested item not found');
30
41
  * }
31
- * );
32
42
  *
43
+ * return apiJsonResponse(200, result);
44
+ * }
45
+ * );
46
+ * ```
33
47
  * eg when using a pre-existing provider variable the types work better:
34
- * export const exampleRoute = createRoute({name: 'exampleRoute', method: METHOD.GET, path: '/api/example/:key',
35
- * requestServiceProvider,
36
- * handler: async(params: {key: string}, services) => {
37
- * const result = await services.myDocumentStore.getItem(params.key);
38
- *
39
- * if (!result) {
40
- * throw new NotFoundError('requested item not found');
41
- * }
48
+ * ```
49
+ * export const exampleRoute = createRoute({
50
+ * name: 'exampleRoute', method: METHOD.GET, path: '/api/example/:key',
51
+ * requestServiceProvider,
52
+ * handler: async(params: {key: string}, services) => {
53
+ * const result = await services.myDocumentStore.getItem(params.key);
42
54
  *
43
- * return apiJsonResponse(200, result);
55
+ * if (!result) {
56
+ * throw new NotFoundError('requested item not found');
44
57
  * }
45
- * });
58
+ *
59
+ * return apiJsonResponse(200, result);
60
+ * }
61
+ * });
62
+ * ```
46
63
  */
47
64
  export const makeCreateRoute = () => (...args) => {
48
65
  return (args.length === 1
@@ -50,12 +67,37 @@ export const makeCreateRoute = () => (...args) => {
50
67
  : { ...args[0], handler: args[1] });
51
68
  };
52
69
  /* begin reverse routing utils */
70
+ /**
71
+ * Makes a renderRouteUrl function that can be used to render route paths (this is a factory
72
+ * factory). The returned function takes a `route`, `params`, and `query` and returns a string
73
+ * with the params and query substituted into the route path.
74
+ *
75
+ * this function is initialized using the Ru type which indicates the specific routes wired into
76
+ * the application, which means that if you try to build a url with a route which is not wired
77
+ * into the router you will get a type error. this is a feature to prevent referencing routes that
78
+ * don't exist or aren't handling requests properly.
79
+ *
80
+ * if you are making a helper function or need to render a route outside your application, you
81
+ * can use the `renderAnyRouteUrl` function
82
+ */
53
83
  export const makeRenderRouteUrl = () => (route, params, query = {}) => {
54
84
  const getPathForParams = pathToRegexp.compile(route.path, { encode: encodeURIComponent });
55
85
  const search = queryString.stringify(query);
56
86
  const path = getPathForParams(params) + (search ? `?${search}` : '');
57
87
  return path;
58
88
  };
89
+ /**
90
+ * A pre-made result from `makeRenderRouteUrl`, this function interpolates parameter and query
91
+ * arguments into a route path.
92
+ *
93
+ * prefer using `renderRouteUrl` initialized with your applications route union
94
+ * when possible to help catch improperly initialized routes.
95
+ *
96
+ * @param route the route that has a `path` to be interpolated
97
+ * @param params the parameters to interpolate into the route path
98
+ * @param query the query parameters to add to the route path
99
+ * @returns the interpolated route path
100
+ */
59
101
  export const renderAnyRouteUrl = makeRenderRouteUrl();
60
102
  const bindRoute = (services, appBinder, pathExtractor, matcher) => (route) => {
61
103
  const getParamsFromPath = pathToRegexp.match(route.path, { decode: decodeURIComponent });
@@ -68,24 +110,35 @@ const bindRoute = (services, appBinder, pathExtractor, matcher) => (route) => {
68
110
  }
69
111
  };
70
112
  };
71
- /*
72
- * here among other things we're specifying a generic response format that the response and error handling middleware can use,
73
- * if any routes have responses that don't adhere to this it'll complain about it.
113
+ /**
114
+ * A factory factory for creating request responders (functions that take a request and return a
115
+ * response -- these functions let us implement Lambda `handler` functions).
74
116
  *
75
- * eg:
76
- * export const getRequestResponder = makeGetRequestResponder<AppServices, TRoutes, ApiRouteRequest, Promise<ApiRouteResponse>>()({
117
+ * Use it in two steps. First, call it with the general business logic that defines routes, logs,
118
+ * errors, etc:
119
+ * ```
120
+ * const getRequestResponder = makeGetRequestResponder<
121
+ * AppServices, TRoutes, ApiRouteRequest, Promise<ApiRouteResponse>
122
+ * >() // this empty invocation helps typescript mix defined and inferred types
123
+ * ({
77
124
  * routes: apiRoutes, // the route definitions
78
- * pathExtractor, // how to get the path out of the request format
79
- * routeMatcher, // logic for matching route (if there is any in addition to the path matching)
80
- * errorHandler, // any special error handling
125
+ * pathExtractor, // how to get the path out of the request format
126
+ * routeMatcher, // logic for matching route (beyond path matching, optional)
127
+ * errorHandler, // any special error handling
81
128
  * });
129
+ * ```
130
+ * Note here that among other things we're specifying a generic response format that the response
131
+ * and error handling middleware can use, if any routes have responses that don't adhere to this
132
+ * it'll complain about it.
82
133
  *
83
- * eg an lambda entrypoint:
84
- * export const handler: (request: APIGatewayProxyEventV2) => Promise<ApiRouteResponse> =
85
- * getRequestResponder(
86
- * lambdaServices, // the AppServices for this entrypoint
87
- * lambdaMiddleware // environment specific response middleware (like cors)
88
- * );
134
+ * Next, we use the `getRequestResponder` to create a responder for a specific lambda entrypoint:
135
+ * ```
136
+ * export const handler: (request: APIGatewayProxyEventV2): Promise<ApiRouteResponse> =>
137
+ * getRequestResponder(
138
+ * lambdaServices, // the AppServices for this entrypoint
139
+ * lambdaMiddleware // environment specific response middleware (like cors)
140
+ * );
141
+ * ```
89
142
  */
90
143
  export const makeGetRequestResponder = () => ({ routes, pathExtractor, routeMatcher, errorHandler, logExtractor }) => (services, responseMiddleware) => {
91
144
  const appBinderImpl = (app, middleware) => middleware(app, appBinder);
@@ -136,9 +189,38 @@ export const makeGetRequestResponder = () => ({ routes, pathExtractor, routeMatc
136
189
  return undefined;
137
190
  };
138
191
  };
192
+ /**
193
+ * Returns a JSON response. Handles serializing the data to JSON and setting the content-type header.
194
+ * @param statusCode e.g. 201
195
+ * @param data the object to be serialized to JSON
196
+ * @param headers HTTP headers
197
+ * @example
198
+ * return apiJsonResponse(
199
+ * 200, {
200
+ * message: "hello, world!",
201
+ * foo: "bar",
202
+ * },
203
+ * { 'X-Frame-Options': 'DENY' }
204
+ * );
205
+ */
139
206
  export const apiJsonResponse = (statusCode, data, headers) => ({ statusCode, data, body: JSON.stringify(data), headers: { ...headers, 'content-type': 'application/json' } });
207
+ /**
208
+ * Returns a plain text response. Handles setting the content-type header.
209
+ * @param statusCode e.g. 201
210
+ * @param data some string
211
+ * @param headers HTTP headers
212
+ * @example return apiTextResponse(200, 'some text')
213
+ */
140
214
  export const apiTextResponse = (statusCode, data, headers) => ({ statusCode, data, body: data, headers: { ...headers, 'content-type': 'text/plain' } });
215
+ /**
216
+ * Returns an HTML response. Handles setting the content-type header.
217
+ * @param statusCode e.g. 201
218
+ * @param data some string
219
+ * @param headers HTTP headers
220
+ * @example return apiHtmlResponse(200, '<b>some text</b>')
221
+ */
141
222
  export const apiHtmlResponse = (statusCode, data, headers) => ({ statusCode, data, body: data, headers: { ...headers, 'content-type': 'text/html' } });
223
+ /** HTTP method enum */
142
224
  export var METHOD;
143
225
  (function (METHOD) {
144
226
  METHOD["GET"] = "GET";
@@ -41,6 +41,7 @@ declare type MapRoutesToConfig<Ru> = [Ru] extends [AnyRoute<Ru>] ? {
41
41
  method: string;
42
42
  };
43
43
  } : never;
44
+ /** Pulls the content out of a response based on the content type */
44
45
  export declare const loadResponse: (response: Response) => () => Promise<any>;
45
46
  interface MakeApiGateway<F> {
46
47
  <Ru>(config: ConfigProviderForConfig<{
@@ -3,6 +3,7 @@ import queryString from 'query-string';
3
3
  import { merge } from '../..';
4
4
  import { resolveConfigValue } from '../../config';
5
5
  import { SessionExpiredError, UnauthorizedError } from '../../errors';
6
+ /** Pulls the content out of a response based on the content type */
6
7
  export const loadResponse = (response) => () => {
7
8
  const [contentType] = (response.headers.get('content-type') || '').split(';');
8
9
  switch (contentType) {
@@ -15,6 +16,7 @@ export const loadResponse = (response) => () => {
15
16
  }
16
17
  };
17
18
  const makeRouteClient = (initializer, config, route, authProvider) => {
19
+ /* TODO this duplicates code with makeRenderRouteUrl, reuse that */
18
20
  const renderUrl = async ({ params, query }) => {
19
21
  const apiBase = await resolveConfigValue(config.apiBase);
20
22
  const getPathForParams = pathToRegexp.compile(route.path, { encode: encodeURIComponent });
@@ -12,13 +12,14 @@ export interface TokenUser {
12
12
  is_administrator: boolean;
13
13
  }
14
14
  export interface ApiUser extends TokenUser {
15
- is_not_gdpr_location: boolean;
16
15
  contact_infos: Array<{
17
16
  type: string;
18
17
  value: string;
19
18
  is_verified: boolean;
20
19
  is_guessed_preferred: boolean;
21
20
  }>;
21
+ external_ids: string[];
22
+ is_not_gdpr_location: boolean;
22
23
  signed_contract_names: string[];
23
24
  }
24
25
  export declare type User = TokenUser | ApiUser;
@@ -1,2 +1,11 @@
1
1
  import { User } from '..';
2
+ /**
3
+ * Decrypts and verifies a SSO cookie.
4
+ *
5
+ * @param token the encrypted token
6
+ * @param encryptionPrivateKey the private key used to encrypt the token
7
+ * @param signaturePublicKey the public key used to verify the decrypted token
8
+ * @throws SessionExpiredError if the token is expired
9
+ * @returns User (success) or undefined (failure)
10
+ */
2
11
  export declare const decryptAndVerify: (token: string, encryptionPrivateKey: string, signaturePublicKey: string) => User | undefined;
@@ -18,6 +18,15 @@ const decrypt = (input, key) => {
18
18
  ]);
19
19
  return result.toString('utf-8');
20
20
  };
21
+ /**
22
+ * Decrypts and verifies a SSO cookie.
23
+ *
24
+ * @param token the encrypted token
25
+ * @param encryptionPrivateKey the private key used to encrypt the token
26
+ * @param signaturePublicKey the public key used to verify the decrypted token
27
+ * @throws SessionExpiredError if the token is expired
28
+ * @returns User (success) or undefined (failure)
29
+ */
21
30
  export const decryptAndVerify = (token, encryptionPrivateKey, signaturePublicKey) => {
22
31
  try {
23
32
  // Decrypt SSO cookie
@@ -1 +1,4 @@
1
+ /**
2
+ * Creates a logger that logs to the console.
3
+ */
1
4
  export declare const createConsoleLogger: () => import(".").Logger;
@@ -1,4 +1,7 @@
1
1
  import { createCoreLogger } from '.';
2
+ /**
3
+ * Creates a logger that logs to the console.
4
+ */
2
5
  export const createConsoleLogger = () => createCoreLogger((level, event) => console[level](JSON.stringify({
3
6
  eventType: level.toUpperCase(),
4
7
  ...event,
@@ -1,14 +1,39 @@
1
1
  import { JsonCompatibleStruct } from '../../routing';
2
+ /**
3
+ * The log level
4
+ */
2
5
  export declare enum Level {
3
6
  Info = "info",
4
7
  Warn = "warn",
5
8
  Error = "error"
6
9
  }
10
+ /**
11
+ * A function that logs an event at a certain level.
12
+ *
13
+ * @param level - log level
14
+ * @param event - event to log
15
+ */
7
16
  export declare type LogEvent = (level: Level, event: JsonCompatibleStruct) => void;
17
+ /**
18
+ * A logger that can be used to log events.
19
+ *
20
+ * @property setContext - sets a context object to be included in all future logged events
21
+ * @property logEvent - logs an arbitrary event at a certain level, without context
22
+ * @property log - logs a message and the context at a certain level
23
+ * @property createSubContext - creates a new logger that inherits the context of this logger
24
+ */
8
25
  export interface Logger {
9
26
  setContext: (obj: JsonCompatibleStruct) => void;
10
27
  logEvent: LogEvent;
11
28
  log: (message: string, level?: Level) => void;
12
29
  createSubContext: () => Logger;
13
30
  }
31
+ /**
32
+ * Creates a logger that logs events using the given driver and context provider.
33
+ *
34
+ * @param driver the driver that logs events
35
+ * @param getParentContext a provider that returns the context to use for this logger
36
+ * (the context is returned when messages are logged with the logger)
37
+ * @returns a Logger
38
+ */
14
39
  export declare const createCoreLogger: (driver: LogEvent, getParentContext?: (() => JsonCompatibleStruct) | undefined) => Logger;
@@ -1,9 +1,20 @@
1
+ /**
2
+ * The log level
3
+ */
1
4
  export var Level;
2
5
  (function (Level) {
3
6
  Level["Info"] = "info";
4
7
  Level["Warn"] = "warn";
5
8
  Level["Error"] = "error";
6
9
  })(Level || (Level = {}));
10
+ /**
11
+ * Creates a logger that logs events using the given driver and context provider.
12
+ *
13
+ * @param driver the driver that logs events
14
+ * @param getParentContext a provider that returns the context to use for this logger
15
+ * (the context is returned when messages are logged with the logger)
16
+ * @returns a Logger
17
+ */
7
18
  export const createCoreLogger = (driver, getParentContext) => {
8
19
  const context = {};
9
20
  const getContext = () => ({ ...getParentContext === null || getParentContext === void 0 ? void 0 : getParentContext(), ...context });
@@ -10,11 +10,15 @@ export declare type ActivityState = {
10
10
  };
11
11
  export declare const matchAttempt: (statement: XapiStatement) => boolean;
12
12
  export declare const matchAttemptCompleted: (attempt: XapiStatement) => (statement: XapiStatement) => boolean;
13
- export declare const resolveActivityAttempts: (statements: XapiStatement[], activityIRI: string, parentActivityAttempt?: string | undefined) => XapiStatement[];
14
- export declare const resolveCompletedForAttempt: (statements: XapiStatement[], activityIRI: string, attempt: XapiStatement) => XapiStatement | undefined;
13
+ export declare const resolveAttempts: (statements: XapiStatement[], options?: {
14
+ activityIRI?: string | undefined;
15
+ parentActivityAttempt?: string | undefined;
16
+ } | undefined) => XapiStatement[];
17
+ export declare const resolveCompletedForAttempt: (statements: XapiStatement[], attempt: XapiStatement, activityIRI?: string | undefined) => XapiStatement | undefined;
15
18
  export declare const oldestStatement: (statements: XapiStatement[]) => XapiStatement | undefined;
16
19
  export declare const mostRecentStatement: (statements: XapiStatement[]) => XapiStatement | undefined;
17
- export declare const resolveActivityAttemptInfo: (statements: XapiStatement[], activityIRI: string, options?: {
20
+ export declare const resolveAttemptInfo: (statements: XapiStatement[], options?: {
21
+ activityIRI?: string | undefined;
18
22
  currentAttempt?: string | undefined;
19
23
  parentActivityAttempt?: string | undefined;
20
24
  currentPreference?: "latest" | "oldest" | undefined;
@@ -23,13 +27,6 @@ export declare const loadStatementsForActivityAndFirstChildren: (gateway: LrsGat
23
27
  attempt?: string | undefined;
24
28
  ensureSync?: boolean | undefined;
25
29
  } | undefined) => Promise<XapiStatement[]>;
26
- export declare const loadStatementsForAttempt: (gateway: LrsGateway, attempt: string, options?: {
27
- ensureSync?: boolean | undefined;
28
- } | undefined) => Promise<XapiStatement[]>;
29
- export declare const loadStatementsForActivity: (gateway: LrsGateway, activityIRI: string, options?: {
30
- attempt?: string | undefined;
31
- ensureSync?: boolean | undefined;
32
- } | undefined) => Promise<XapiStatement[]>;
33
30
  export declare const loadActivityAttemptInfo: (gateway: LrsGateway, activityIRI: string, options?: {
34
31
  currentAttempt?: string | undefined;
35
32
  parentActivityAttempt?: string | undefined;
@@ -24,21 +24,21 @@ export const matchAttemptCompleted = (attempt) => (statement) => {
24
24
  && ((_a = statement.context.statement) === null || _a === void 0 ? void 0 : _a.id) === attempt.id
25
25
  && statement.context.registration === ((_b = attempt.context) === null || _b === void 0 ? void 0 : _b.registration);
26
26
  };
27
- export const resolveActivityAttempts = (statements, activityIRI, parentActivityAttempt) => statements.filter(statement => {
27
+ export const resolveAttempts = (statements, options) => statements.filter(statement => {
28
28
  var _a;
29
29
  return matchAttempt(statement)
30
- && statement.object.id === activityIRI
31
- && (!parentActivityAttempt || ((_a = statement.context) === null || _a === void 0 ? void 0 : _a.registration) === parentActivityAttempt);
30
+ && (!(options === null || options === void 0 ? void 0 : options.activityIRI) || statement.object.id === options.activityIRI)
31
+ && (!(options === null || options === void 0 ? void 0 : options.parentActivityAttempt) || ((_a = statement.context) === null || _a === void 0 ? void 0 : _a.registration) === options.parentActivityAttempt);
32
32
  });
33
- export const resolveCompletedForAttempt = (statements, activityIRI, attempt) => statements.find(statement => matchAttemptCompleted(attempt)(statement)
34
- && statement.object.id === activityIRI);
33
+ export const resolveCompletedForAttempt = (statements, attempt, activityIRI) => statements.find(statement => matchAttemptCompleted(attempt)(statement)
34
+ && (!activityIRI || statement.object.id === activityIRI));
35
35
  export const oldestStatement = (statements) => statements.reduce((result, statement) => result && isBefore(parseISO(result.stored || result.timestamp), parseISO(statement.timestamp)) ? result : statement, statements[0]);
36
36
  export const mostRecentStatement = (statements) => statements.reduce((result, statement) => result && isAfter(parseISO(result.stored || result.timestamp), parseISO(statement.timestamp)) ? result : statement, statements[0]);
37
- export const resolveActivityAttemptInfo = (statements, activityIRI, options) => {
37
+ export const resolveAttemptInfo = (statements, options) => {
38
38
  // TODO optimize. i'm 100% that this could all be done in one iteration but i'm not messing around with that for now.
39
- const attempts = resolveActivityAttempts(statements, activityIRI, options === null || options === void 0 ? void 0 : options.parentActivityAttempt);
39
+ const attempts = resolveAttempts(statements, options);
40
40
  /* attempts that have a completed statement */
41
- const completedAttempts = attempts.filter(attempt => !!resolveCompletedForAttempt(statements, activityIRI, attempt));
41
+ const completedAttempts = attempts.filter(attempt => !!resolveCompletedForAttempt(statements, attempt, options === null || options === void 0 ? void 0 : options.activityIRI));
42
42
  /* the last attempt sorted by timestamp */
43
43
  const currentAttempt = (options === null || options === void 0 ? void 0 : options.currentAttempt)
44
44
  ? attempts.find(attempt => attempt.id === options.currentAttempt)
@@ -48,13 +48,13 @@ export const resolveActivityAttemptInfo = (statements, activityIRI, options) =>
48
48
  /* all statements for the current attempt (doesn't include the attempt or completed statements) */
49
49
  const currentAttemptStatements = currentAttempt ? statements.filter(statement => {
50
50
  var _a;
51
- return statement.object.id === activityIRI
51
+ return (!(options === null || options === void 0 ? void 0 : options.activityIRI) || statement.object.id === options.activityIRI)
52
52
  && ((_a = statement.context) === null || _a === void 0 ? void 0 : _a.registration) === currentAttempt.id;
53
53
  }) : [];
54
- const currentAttemptCompleted = currentAttempt && resolveCompletedForAttempt(statements, activityIRI, currentAttempt);
54
+ const currentAttemptCompleted = currentAttempt && resolveCompletedForAttempt(statements, currentAttempt, options === null || options === void 0 ? void 0 : options.activityIRI);
55
55
  const mostRecentAttemptWithCompleted = completedAttempts.reduce((current, attempt) => current && isAfter(parseISO(current.timestamp), parseISO(attempt.timestamp)) ? current : attempt, completedAttempts[0]);
56
56
  const mostRecentAttemptWithCompletedCompleted = mostRecentAttemptWithCompleted
57
- && resolveCompletedForAttempt(statements, activityIRI, mostRecentAttemptWithCompleted);
57
+ && resolveCompletedForAttempt(statements, mostRecentAttemptWithCompleted, options === null || options === void 0 ? void 0 : options.activityIRI);
58
58
  /*
59
59
  * the structure allows for the possibility of multiple incomplete attempts.
60
60
  * the implementation can choose at its discretion to ignore the currentAttempt
@@ -75,8 +75,9 @@ export const resolveActivityAttemptInfo = (statements, activityIRI, options) =>
75
75
  * loads all statements (for this actor) that have the given activityIRI as the object.id or the context.contextActivities.parent.id
76
76
  *
77
77
  * note: if you filter on attempt you're only gonna get the `Attempted` statements from the child activities, subsequent child activity
78
- * statements would then have to be fetched using `loadStatementsForActivity(gateway, childActivityIRI, childAttemptStatementID)`. this
79
- * is because child activities could have multiple attempts under one attempt on the parent activity.
78
+ * statements would then have to be fetched using
79
+ * `gateway.getAllXapiStatements({ activity: childActivityIRI, registration: childAttemptStatementID })`.
80
+ * this is because child activities could have multiple attempts under one attempt on the parent activity.
80
81
  */
81
82
  export const loadStatementsForActivityAndFirstChildren = (gateway, activityIRI, options) => {
82
83
  const { attempt, ...partialOptions } = options ? options : { attempt: undefined };
@@ -87,30 +88,10 @@ export const loadStatementsForActivityAndFirstChildren = (gateway, activityIRI,
87
88
  ...getOptions,
88
89
  });
89
90
  };
90
- /*
91
- * loads all statements (for this actor) that have the given parent attempt (registration)
92
- */
93
- export const loadStatementsForAttempt = (gateway, attempt, options) => {
94
- return gateway.getAllXapiStatements({
95
- registration: attempt,
96
- ...(options ? options : {})
97
- });
98
- };
99
- /*
100
- * loads all statements (for this actor) that have the given activityIRI as the object.id
101
- */
102
- export const loadStatementsForActivity = (gateway, activityIRI, options) => {
103
- const { attempt, ...partialOptions } = options ? options : { attempt: undefined };
104
- const getOptions = attempt ? { registration: attempt, ...partialOptions } : partialOptions;
105
- return gateway.getAllXapiStatements({
106
- activity: activityIRI,
107
- ...getOptions,
108
- });
109
- };
110
91
  export const loadActivityAttemptInfo = async (gateway, activityIRI, options) => {
111
92
  const { parentActivityAttempt, ...partialOptions } = options ? options : { parentActivityAttempt: undefined };
112
- const loadOptions = parentActivityAttempt ? { attempt: parentActivityAttempt } : partialOptions;
113
- return resolveActivityAttemptInfo(await loadStatementsForActivity(gateway, activityIRI, loadOptions), activityIRI, options);
93
+ const loadOptions = parentActivityAttempt ? { ...partialOptions, registration: parentActivityAttempt } : partialOptions;
94
+ return resolveAttemptInfo(await gateway.getAllXapiStatements({ ...loadOptions, activity: activityIRI }), { ...options, activityIRI });
114
95
  };
115
96
  export const createStatement = (verb, activity, attempt, parentActivityIRI) => {
116
97
  return {
@@ -57,19 +57,33 @@ export const fileSystemLrsGateway = (initializer) => (configProvider) => (authPr
57
57
  await save;
58
58
  return statementsWithDefaults;
59
59
  };
60
- const getAllXapiStatements = async ({ user, anyUser, ...options }) => {
60
+ const getAllXapiStatements = async ({ user, anyUser, fetchUntil, ...options }) => {
61
61
  const authUser = await authProvider.getUser();
62
62
  await load;
63
- return (data || []).filter(statement => {
63
+ let filteredData = (data || []).filter(statement => {
64
64
  var _a, _b, _c, _d;
65
+ const statementDate = new Date(statement.timestamp);
66
+ const sinceDate = options.since ? new Date(options.since) : null;
67
+ const untilDate = options.until ? new Date(options.until) : null;
65
68
  return (anyUser === true || statement.actor.account.name === (user || assertDefined(authUser, new UnauthorizedError()).uuid))
66
69
  && (!options.verb || statement.verb.id === options.verb)
67
70
  && (!options.registration || ((_a = statement.context) === null || _a === void 0 ? void 0 : _a.registration) === options.registration)
68
71
  && (!options.activity || (options.related_activities
69
72
  ? ((statement.object.id === options.activity && statement.object.objectType === 'Activity')
70
73
  || (!!((_d = (_c = (_b = statement.context) === null || _b === void 0 ? void 0 : _b.contextActivities) === null || _c === void 0 ? void 0 : _c.parent) === null || _d === void 0 ? void 0 : _d.find(parent => parent.id === options.activity && parent.objectType === 'Activity'))))
71
- : (statement.object.id === options.activity && statement.object.objectType === 'Activity')));
74
+ : (statement.object.id === options.activity && statement.object.objectType === 'Activity')))
75
+ && (!sinceDate || statementDate >= sinceDate)
76
+ && (!untilDate || statementDate <= untilDate);
72
77
  });
78
+ if (fetchUntil) {
79
+ for (let i = 0; i < filteredData.length; i += pageSize) {
80
+ if (fetchUntil(filteredData.slice(0, i + pageSize))) {
81
+ filteredData = filteredData.slice(0, i + pageSize);
82
+ break;
83
+ }
84
+ }
85
+ }
86
+ return filteredData;
73
87
  };
74
88
  const getMoreXapiStatements = async (more) => {
75
89
  const { args, offset } = JSON.parse(more);
@@ -90,6 +90,8 @@ export declare const lrsGateway: <C extends string = "lrs">(initializer: Initial
90
90
  related_activities?: boolean | undefined;
91
91
  user?: string | undefined;
92
92
  anyUser?: boolean | undefined;
93
+ since?: string | undefined;
94
+ until?: string | undefined;
93
95
  } & {
94
96
  ensureSync?: boolean | undefined;
95
97
  }) => Promise<{
@@ -100,15 +102,19 @@ export declare const lrsGateway: <C extends string = "lrs">(initializer: Initial
100
102
  more: string;
101
103
  statements: XapiStatement[];
102
104
  }>;
103
- getAllXapiStatements: (params: {
105
+ getAllXapiStatements: ({ fetchUntil, ...params }: {
104
106
  verb?: string | undefined;
105
107
  activity?: string | undefined;
106
108
  registration?: string | undefined;
107
109
  related_activities?: boolean | undefined;
108
110
  user?: string | undefined;
109
111
  anyUser?: boolean | undefined;
112
+ since?: string | undefined;
113
+ until?: string | undefined;
110
114
  } & {
111
115
  ensureSync?: boolean | undefined;
116
+ } & {
117
+ fetchUntil?: ((statements: XapiStatement[]) => boolean) | undefined;
112
118
  }) => Promise<XapiStatement[]>;
113
119
  };
114
120
  export {};
@@ -94,9 +94,9 @@ ${await response.text()}`);
94
94
  return formatGetXapiStatementsResponse(fetchXapiStatements(fetchParams));
95
95
  }
96
96
  };
97
- const getAllXapiStatements = async (params) => {
97
+ const getAllXapiStatements = async ({ fetchUntil, ...params }) => {
98
98
  const loadRemaining = async (result) => {
99
- if (!result.more) {
99
+ if (!result.more || (fetchUntil && fetchUntil(result.statements))) {
100
100
  return result.statements;
101
101
  }
102
102
  const { more, statements } = await getMoreXapiStatements(result.more);