@superbuilders/primer-tives 2.0.0 → 2.2.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.
Files changed (39) hide show
  1. package/README.md +742 -540
  2. package/dist/client/create.d.ts +22 -6
  3. package/dist/client/create.d.ts.map +1 -1
  4. package/dist/client/index.js +89 -51
  5. package/dist/client/index.js.map +14 -11
  6. package/dist/client/session.d.ts +1 -1
  7. package/dist/client/session.d.ts.map +1 -1
  8. package/dist/client/transport.d.ts +4 -5
  9. package/dist/client/transport.d.ts.map +1 -1
  10. package/dist/contracts/index.d.ts +3 -2
  11. package/dist/contracts/index.d.ts.map +1 -1
  12. package/dist/contracts/index.js +42 -36
  13. package/dist/contracts/index.js.map +6 -5
  14. package/dist/contracts/pci-schemas.d.ts +24 -20
  15. package/dist/contracts/pci-schemas.d.ts.map +1 -1
  16. package/dist/contracts/pci.d.ts +26 -27
  17. package/dist/contracts/pci.d.ts.map +1 -1
  18. package/dist/contracts/validation.d.ts +34 -23
  19. package/dist/contracts/validation.d.ts.map +1 -1
  20. package/dist/errors.d.ts +2 -6
  21. package/dist/errors.d.ts.map +1 -1
  22. package/dist/errors.js +3 -11
  23. package/dist/errors.js.map +3 -3
  24. package/dist/server/create-server.d.ts +3 -33
  25. package/dist/server/create-server.d.ts.map +1 -1
  26. package/dist/server/exchange.d.ts +7 -14
  27. package/dist/server/exchange.d.ts.map +1 -1
  28. package/dist/server/index.d.ts +1 -3
  29. package/dist/server/index.d.ts.map +1 -1
  30. package/dist/server/index.js +31 -407
  31. package/dist/server/index.js.map +6 -9
  32. package/dist/subject-pcis.d.ts +13 -0
  33. package/dist/subject-pcis.d.ts.map +1 -0
  34. package/dist/version.d.ts +1 -1
  35. package/package.json +5 -1
  36. package/dist/server/hints.d.ts +0 -25
  37. package/dist/server/hints.d.ts.map +0 -1
  38. package/dist/server/students.d.ts +0 -12
  39. package/dist/server/students.d.ts.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"create-server.d.ts","sourceRoot":"","sources":["../../src/server/create-server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAA;AACtE,OAAO,EACN,KAAK,YAAY,EACjB,KAAK,eAAe,EAGpB,MAAM,6CAA6C,CAAA;AAEpD,OAAO,EACN,KAAK,cAAc,EACnB,KAAK,oBAAoB,EAEzB,MAAM,0CAA0C,CAAA;AAGjD,UAAU,kBAAkB;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;IACxC,QAAQ,CAAC,KAAK,CAAC,EAAE,eAAe,CAAA;IAChC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAA;CAC7B;AAED,UAAU,YAAY;IACrB;;;;;;;;OAQG;IACH,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;IAEhC;;;;;;OAMG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAA;IAExF;;;;OAIG;IACH,6BAA6B,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IAEvE;;;;OAIG;IACH,qCAAqC,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAA;CAClF;AAED,iBAAS,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,GAAG,YAAY,CAepE;AAED,YAAY,EAAE,YAAY,EAAE,kBAAkB,EAAE,CAAA;AAChD,OAAO,EAAE,kBAAkB,EAAE,CAAA"}
1
+ {"version":3,"file":"create-server.d.ts","sourceRoot":"","sources":["../../src/server/create-server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAA;AACtE,OAAO,EAAE,KAAK,aAAa,EAAY,MAAM,6CAA6C,CAAA;AAE1F,UAAU,kBAAkB;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;IACxC,QAAQ,CAAC,KAAK,CAAC,EAAE,eAAe,CAAA;IAChC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAA;CAC7B;AAED,UAAU,YAAY;IACrB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;CAC/C;AAED,iBAAS,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,GAAG,YAAY,CAMpE;AAED,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,kBAAkB,EAAE,CAAA;AAC/D,OAAO,EAAE,kBAAkB,EAAE,CAAA"}
@@ -1,22 +1,15 @@
1
1
  import type { PrimerLogger } from "../logger";
2
- interface ExchangeConfig {
2
+ interface TokenConfig {
3
3
  readonly origin: string;
4
4
  readonly secretKey: string;
5
5
  readonly fetch?: typeof globalThis.fetch;
6
6
  readonly abort?: AbortController;
7
7
  readonly logger: PrimerLogger;
8
8
  }
9
- interface SessionToken {
10
- readonly accessToken: string;
11
- readonly expiresInSeconds: number;
12
- }
13
- interface TimebackSession {
14
- readonly studentId: string;
15
- readonly accessToken: string;
16
- readonly expiresInSeconds: number;
17
- }
18
- declare function exchangeStudent(config: ExchangeConfig, studentId: string): Promise<SessionToken>;
19
- declare function exchangeTimebackStudent(config: ExchangeConfig, sourcedId: string): Promise<TimebackSession>;
20
- export type { ExchangeConfig, SessionToken, TimebackSession };
21
- export { exchangeStudent, exchangeTimebackStudent };
9
+ type GetTokenInput = {
10
+ readonly verifiedEmail: string;
11
+ };
12
+ declare function getToken(config: TokenConfig, input: GetTokenInput): Promise<string>;
13
+ export type { GetTokenInput, TokenConfig };
14
+ export { getToken };
22
15
  //# sourceMappingURL=exchange.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"exchange.d.ts","sourceRoot":"","sources":["../../src/server/exchange.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAA;AAKtE,UAAU,cAAc;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;IACxC,QAAQ,CAAC,KAAK,CAAC,EAAE,eAAe,CAAA;IAChC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAA;CAC7B;AAED,UAAU,YAAY;IACrB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAA;CACjC;AAED,UAAU,eAAe;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAA;CACjC;AA8OD,iBAAe,eAAe,CAAC,MAAM,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAmB/F;AAED,iBAAe,uBAAuB,CACrC,MAAM,EAAE,cAAc,EACtB,SAAS,EAAE,MAAM,GACf,OAAO,CAAC,eAAe,CAAC,CAmB1B;AAED,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,eAAe,EAAE,CAAA;AAC7D,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,CAAA"}
1
+ {"version":3,"file":"exchange.d.ts","sourceRoot":"","sources":["../../src/server/exchange.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAA;AAItE,UAAU,WAAW;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;IACxC,QAAQ,CAAC,KAAK,CAAC,EAAE,eAAe,CAAA;IAChC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAA;CAC7B;AAED,KAAK,aAAa,GAAG;IACpB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;CAC9B,CAAA;AA4HD,iBAAe,QAAQ,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAclF;AAED,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,CAAA;AAC1C,OAAO,EAAE,QAAQ,EAAE,CAAA"}
@@ -1,5 +1,3 @@
1
1
  export { createPrimerServer } from "./create-server";
2
- export type { PrimerServer, PrimerServerConfig } from "./create-server";
3
- export type { SessionToken, TimebackSession } from "./exchange";
4
- export type { PlacementHints, PlacementHintsResult } from "./hints";
2
+ export type { GetTokenInput, PrimerServer, PrimerServerConfig } from "./create-server";
5
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,kDAAkD,CAAA;AACrF,YAAY,EACX,YAAY,EACZ,kBAAkB,EAClB,MAAM,kDAAkD,CAAA;AAEzD,YAAY,EACX,YAAY,EACZ,eAAe,EACf,MAAM,6CAA6C,CAAA;AAEpD,YAAY,EACX,cAAc,EACd,oBAAoB,EACpB,MAAM,0CAA0C,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,kDAAkD,CAAA;AACrF,YAAY,EACX,aAAa,EACb,YAAY,EACZ,kBAAkB,EAClB,MAAM,kDAAkD,CAAA"}
@@ -3,6 +3,7 @@ import * as errors from "@superbuilders/errors";
3
3
  var ErrNetwork = errors.new("network");
4
4
  var ErrJsonParse = errors.new("json parse");
5
5
  var ErrUnsupportedPci = errors.new("unsupported pci");
6
+ var ErrMissingRequiredPci = errors.new("missing required pci");
6
7
  var ErrInvalidAccessToken = errors.new("invalid access token");
7
8
  var ErrMalformedAccessToken = errors.new("malformed access token");
8
9
  var ErrTokenExpired = errors.new("access token expired");
@@ -12,99 +13,46 @@ var ErrTimeout = errors.new("timeout");
12
13
  var ErrForbidden = errors.new("forbidden");
13
14
  var ErrNotFound = errors.new("not found");
14
15
  var ErrConflict = errors.new("conflict");
15
- var ErrExternalAuthorityRequired = errors.new("external authority required");
16
16
  var ErrRateLimited = errors.new("rate limited");
17
17
  var ErrServiceUnavailable = errors.new("service unavailable");
18
18
  var ErrNotSerializable = errors.new("PrimerState is live in-memory state and must not be serialized or stored");
19
19
  var ErrInvalidSubmission = errors.new("invalid submission");
20
20
  var ErrInvalidSecretKey = errors.new("invalid secret key");
21
- var ErrStudentNotFound = errors.new("student not found");
22
- var ErrUnsupportedGrade = errors.new("unsupported grade");
23
- var ErrTimebackUnavailable = errors.new("timeback unavailable");
24
- var ErrNeedsHints = errors.new("student needs hints set before /advance");
25
21
  var ErrSdkUpgradeRequired = errors.new("sdk upgrade required");
26
- // src/grade-level.ts
27
- var GRADE_LEVELS = ["K", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"];
28
22
  // src/server/exchange.ts
29
23
  import * as errors2 from "@superbuilders/errors";
30
- var STUDENT_EXCHANGE_PATH = "/api/v0/auth/exchange";
31
- var TIMEBACK_EXCHANGE_PATH = "/api/v0/auth/exchange/timeback";
24
+ var TOKEN_PATH = "/api/v0/sessions";
32
25
  function isObjectRecord(value) {
33
26
  return typeof value === "object" && value !== null;
34
27
  }
35
- function isFiniteNumber(value) {
36
- return typeof value === "number" && Number.isFinite(value);
37
- }
38
- function toExchangeErrorBody(data) {
28
+ function toTokenErrorBody(data) {
39
29
  if (!isObjectRecord(data)) {
40
30
  return {};
41
31
  }
42
32
  return typeof data.error === "string" ? { error: data.error } : {};
43
33
  }
44
- function parseSessionTokenSuccessBody(data) {
34
+ function parseTokenSuccessBody(data) {
45
35
  if (!isObjectRecord(data)) {
46
36
  return;
47
37
  }
48
- if (typeof data.access_token !== "string") {
49
- return;
50
- }
51
- if (data.token_type !== "Bearer") {
52
- return;
53
- }
54
- if (!isFiniteNumber(data.expires_in)) {
55
- return;
56
- }
57
- if (typeof data.scope !== "string") {
38
+ if (typeof data.access_token !== "string" || data.token_type !== "Bearer") {
58
39
  return;
59
40
  }
60
41
  return {
61
42
  access_token: data.access_token,
62
- token_type: data.token_type,
63
- expires_in: data.expires_in,
64
- scope: data.scope
65
- };
66
- }
67
- function parseTimebackExchangeSuccessBody(data) {
68
- const session = parseSessionTokenSuccessBody(data);
69
- if (!session || !isObjectRecord(data) || typeof data.student_id !== "string") {
70
- return;
71
- }
72
- return {
73
- student_id: data.student_id,
74
- access_token: session.access_token,
75
- token_type: session.token_type,
76
- expires_in: session.expires_in,
77
- scope: session.scope
43
+ token_type: "Bearer"
78
44
  };
79
45
  }
80
46
  function isAbortError(err) {
81
47
  return err.name === "AbortError" || err.name === "TimeoutError";
82
48
  }
83
- function mapBadRequest(body) {
84
- if (body.error === "unsupported_grade") {
85
- return ErrUnsupportedGrade;
86
- }
87
- return ErrBadRequest;
88
- }
89
- function httpSentinel(status, body) {
49
+ function httpSentinel(status) {
90
50
  if (status === 400) {
91
- return mapBadRequest(body);
51
+ return ErrBadRequest;
92
52
  }
93
53
  if (status === 401) {
94
54
  return ErrInvalidSecretKey;
95
55
  }
96
- if (status === 404) {
97
- return ErrStudentNotFound;
98
- }
99
- if (status === 409) {
100
- if (body.error === "external_authority_required") {
101
- return ErrExternalAuthorityRequired;
102
- }
103
- return ErrConflict;
104
- }
105
- if (status === 502) {
106
- return ErrTimebackUnavailable;
107
- }
108
56
  return ErrServerError;
109
57
  }
110
58
  async function readErrorBody(res) {
@@ -116,20 +64,20 @@ async function readErrorBody(res) {
116
64
  if (!parsed.ok) {
117
65
  return {};
118
66
  }
119
- return toExchangeErrorBody(parsed.data);
67
+ return toTokenErrorBody(parsed.data);
120
68
  }
121
- async function sendExchangeRequest(config, path, body, logContext) {
69
+ async function sendTokenRequest(config, input) {
122
70
  const logger = config.logger;
123
71
  const fetchFn = config.fetch ? config.fetch : globalThis.fetch;
124
72
  const signal = config.abort ? config.abort.signal : undefined;
125
- const url = `${config.origin}${path}`;
73
+ const url = `${config.origin}${TOKEN_PATH}`;
126
74
  const fetchResult = await fetchFn(url, {
127
75
  method: "POST",
128
76
  headers: {
129
77
  "Content-Type": "application/json",
130
78
  Authorization: `Bearer ${config.secretKey}`
131
79
  },
132
- body: JSON.stringify(body),
80
+ body: JSON.stringify({ verified_email: input.verifiedEmail }),
133
81
  signal
134
82
  }).then(function ok(response) {
135
83
  return { ok: true, response };
@@ -138,43 +86,15 @@ async function sendExchangeRequest(config, path, body, logContext) {
138
86
  });
139
87
  if (!fetchResult.ok) {
140
88
  if (isAbortError(fetchResult.error)) {
141
- logger.error("exchange timeout", { path, ...logContext });
89
+ logger.error("token request timeout");
142
90
  throw errors2.wrap(ErrTimeout, fetchResult.error.message);
143
91
  }
144
- logger.error("exchange network error", { path, ...logContext, error: fetchResult.error });
92
+ logger.error("token request network error", { error: fetchResult.error });
145
93
  throw errors2.wrap(ErrNetwork, fetchResult.error.message);
146
94
  }
147
95
  return fetchResult.response;
148
96
  }
149
- async function parseSessionTokenSuccess(config, res, logContext) {
150
- const logger = config.logger;
151
- const jsonResult = await res.json().then(function ok(data) {
152
- return { ok: true, data };
153
- }, function fail(err) {
154
- return { ok: false, error: err };
155
- });
156
- if (!jsonResult.ok) {
157
- logger.error("exchange json parse failed", { ...logContext, error: jsonResult.error });
158
- throw errors2.wrap(ErrJsonParse, jsonResult.error.message);
159
- }
160
- const body = parseSessionTokenSuccessBody(jsonResult.data);
161
- if (!body) {
162
- logger.error("exchange success body had invalid shape", {
163
- ...logContext,
164
- body: jsonResult.data
165
- });
166
- throw errors2.wrap(ErrJsonParse, "exchange success body had invalid shape");
167
- }
168
- logger.debug("exchange success", {
169
- ...logContext,
170
- expiresIn: body.expires_in
171
- });
172
- return {
173
- accessToken: body.access_token,
174
- expiresInSeconds: body.expires_in
175
- };
176
- }
177
- async function parseTimebackSessionSuccess(config, res, sourcedId) {
97
+ async function parseToken(config, res) {
178
98
  const logger = config.logger;
179
99
  const jsonResult = await res.json().then(function ok(data) {
180
100
  return { ok: true, data };
@@ -182,332 +102,36 @@ async function parseTimebackSessionSuccess(config, res, sourcedId) {
182
102
  return { ok: false, error: err };
183
103
  });
184
104
  if (!jsonResult.ok) {
185
- logger.error("timeback exchange json parse failed", { sourcedId, error: jsonResult.error });
105
+ logger.error("token response json parse failed", { error: jsonResult.error });
186
106
  throw errors2.wrap(ErrJsonParse, jsonResult.error.message);
187
107
  }
188
- const body = parseTimebackExchangeSuccessBody(jsonResult.data);
189
- if (!body) {
190
- logger.error("timeback exchange success body had invalid shape", {
191
- sourcedId,
192
- body: jsonResult.data
193
- });
194
- throw errors2.wrap(ErrJsonParse, "timeback exchange success body had invalid shape");
195
- }
196
- logger.debug("timeback exchange success", {
197
- sourcedId,
198
- studentId: body.student_id,
199
- expiresIn: body.expires_in
200
- });
201
- return {
202
- studentId: body.student_id,
203
- accessToken: body.access_token,
204
- expiresInSeconds: body.expires_in
205
- };
206
- }
207
- async function exchangeStudent(config, studentId) {
208
- const logger = config.logger;
209
- logger.debug("exchange student request", { studentId });
210
- const res = await sendExchangeRequest(config, STUDENT_EXCHANGE_PATH, { student_id: studentId }, { studentId, exchangeKind: "student_id" });
211
- if (!res.ok) {
212
- const body = await readErrorBody(res);
213
- const sentinel = httpSentinel(res.status, body);
214
- const detail = body.error ? `${res.status} ${body.error}` : `${res.status}`;
215
- logger.error("exchange student http error", { status: res.status, body, studentId });
216
- throw errors2.wrap(sentinel, detail);
108
+ const body = parseTokenSuccessBody(jsonResult.data);
109
+ if (body === undefined) {
110
+ logger.error("token response had invalid shape", { body: jsonResult.data });
111
+ throw errors2.wrap(ErrJsonParse, "token response had invalid shape");
217
112
  }
218
- return parseSessionTokenSuccess(config, res, { studentId, exchangeKind: "student_id" });
113
+ logger.debug("token request success");
114
+ return body.access_token;
219
115
  }
220
- async function exchangeTimebackStudent(config, sourcedId) {
116
+ async function getToken(config, input) {
221
117
  const logger = config.logger;
222
- logger.debug("exchange timeback student request", { sourcedId });
223
- const res = await sendExchangeRequest(config, TIMEBACK_EXCHANGE_PATH, { sourced_id: sourcedId }, { sourcedId, exchangeKind: "timeback" });
118
+ logger.debug("token request");
119
+ const res = await sendTokenRequest(config, input);
224
120
  if (!res.ok) {
225
121
  const body = await readErrorBody(res);
226
- const sentinel = httpSentinel(res.status, body);
122
+ const sentinel = httpSentinel(res.status);
227
123
  const detail = body.error ? `${res.status} ${body.error}` : `${res.status}`;
228
- logger.error("exchange timeback student http error", { status: res.status, body, sourcedId });
124
+ logger.error("token request http error", { status: res.status, body });
229
125
  throw errors2.wrap(sentinel, detail);
230
126
  }
231
- return parseTimebackSessionSuccess(config, res, sourcedId);
232
- }
233
-
234
- // src/server/hints.ts
235
- import * as errors3 from "@superbuilders/errors";
236
- var STUDENTS_PATH = "/api/v0/students";
237
- function isObjectRecord2(value) {
238
- return typeof value === "object" && value !== null;
239
- }
240
- function toHintsErrorBody(data) {
241
- if (!isObjectRecord2(data)) {
242
- return {};
243
- }
244
- return typeof data.error === "string" ? { error: data.error } : {};
245
- }
246
- function parseHintsSuccess(data) {
247
- if (!isObjectRecord2(data)) {
248
- return;
249
- }
250
- if (typeof data.student_id !== "string") {
251
- return;
252
- }
253
- if (data.grade_level !== null && typeof data.grade_level !== "string") {
254
- return;
255
- }
256
- return {
257
- student_id: data.student_id,
258
- grade_level: data.grade_level
259
- };
260
- }
261
- function isAbortError2(err) {
262
- return err.name === "AbortError" || err.name === "TimeoutError";
263
- }
264
- function httpSentinel2(status) {
265
- if (status === 400) {
266
- return ErrBadRequest;
267
- }
268
- if (status === 401) {
269
- return ErrInvalidSecretKey;
270
- }
271
- if (status === 404) {
272
- return ErrStudentNotFound;
273
- }
274
- return ErrServerError;
275
- }
276
- async function readErrorBody2(res) {
277
- const parsed = await res.json().then(function ok(data) {
278
- return { ok: true, data };
279
- }, function fail() {
280
- return { ok: false };
281
- });
282
- if (!parsed.ok) {
283
- return {};
284
- }
285
- return toHintsErrorBody(parsed.data);
286
- }
287
- function buildRequestBody(hints) {
288
- const body = {};
289
- if (hints.gradeLevel !== undefined) {
290
- body.grade_level = hints.gradeLevel;
291
- }
292
- return JSON.stringify(body);
293
- }
294
- async function sendSetHintsRequest(config, studentId, hints) {
295
- const logger = config.logger;
296
- const fetchFn = config.fetch ? config.fetch : globalThis.fetch;
297
- const signal = config.abort ? config.abort.signal : undefined;
298
- const url = `${config.origin}${STUDENTS_PATH}/${encodeURIComponent(studentId)}/hints`;
299
- const fetchResult = await fetchFn(url, {
300
- method: "PATCH",
301
- headers: {
302
- Authorization: `Bearer ${config.secretKey}`,
303
- "Content-Type": "application/json"
304
- },
305
- body: buildRequestBody(hints),
306
- signal
307
- }).then(function ok(response) {
308
- return { ok: true, response };
309
- }, function fail(err) {
310
- return { ok: false, error: err };
311
- });
312
- if (!fetchResult.ok) {
313
- if (isAbortError2(fetchResult.error)) {
314
- logger.error("set student hints timeout", { studentId });
315
- throw errors3.wrap(ErrTimeout, fetchResult.error.message);
316
- }
317
- logger.error("set student hints network error", {
318
- studentId,
319
- error: fetchResult.error
320
- });
321
- throw errors3.wrap(ErrNetwork, fetchResult.error.message);
322
- }
323
- return fetchResult.response;
324
- }
325
- async function parseHintsSuccessResponse(config, res) {
326
- const logger = config.logger;
327
- const jsonResult = await res.json().then(function ok(data) {
328
- return { ok: true, data };
329
- }, function fail(err) {
330
- return { ok: false, error: err };
331
- });
332
- if (!jsonResult.ok) {
333
- logger.error("set hints response parse failed", { error: jsonResult.error });
334
- throw errors3.wrap(ErrJsonParse, jsonResult.error.message);
335
- }
336
- const body = parseHintsSuccess(jsonResult.data);
337
- if (!body) {
338
- logger.error("set hints response had invalid shape", { body: jsonResult.data });
339
- throw errors3.wrap(ErrJsonParse, "set hints success body had invalid shape");
340
- }
341
- logger.debug("set hints response success", {
342
- studentId: body.student_id,
343
- gradeLevel: body.grade_level
344
- });
345
- return {
346
- studentId: body.student_id,
347
- gradeLevel: coerceGradeLevel(body.grade_level)
348
- };
349
- }
350
- function coerceGradeLevel(value) {
351
- if (value === null) {
352
- return null;
353
- }
354
- for (const g of GRADE_LEVELS) {
355
- if (g === value) {
356
- return g;
357
- }
358
- }
359
- return null;
360
- }
361
- async function setStudentHints(config, studentId, hints) {
362
- const logger = config.logger;
363
- logger.debug("set student hints request", { studentId, hints });
364
- const res = await sendSetHintsRequest(config, studentId, hints);
365
- if (!res.ok) {
366
- const body = await readErrorBody2(res);
367
- const sentinel = httpSentinel2(res.status);
368
- const detail = body.error ? `${res.status} ${body.error}` : `${res.status}`;
369
- logger.error("set student hints http error", {
370
- studentId,
371
- status: res.status,
372
- body
373
- });
374
- throw errors3.wrap(sentinel, detail);
375
- }
376
- return parseHintsSuccessResponse(config, res);
377
- }
378
-
379
- // src/server/students.ts
380
- import * as errors4 from "@superbuilders/errors";
381
- var STUDENTS_PATH2 = "/api/v0/students";
382
- function isObjectRecord3(value) {
383
- return typeof value === "object" && value !== null;
384
- }
385
- function toStudentsErrorBody(data) {
386
- if (!isObjectRecord3(data)) {
387
- return {};
388
- }
389
- return typeof data.error === "string" ? { error: data.error } : {};
390
- }
391
- function parseStudentIdSuccessBody(data) {
392
- if (!isObjectRecord3(data)) {
393
- return;
394
- }
395
- if (typeof data.student_id !== "string") {
396
- return;
397
- }
398
- return { student_id: data.student_id };
399
- }
400
- function isAbortError3(err) {
401
- return err.name === "AbortError" || err.name === "TimeoutError";
402
- }
403
- function httpSentinel3(status) {
404
- if (status === 400) {
405
- return ErrBadRequest;
406
- }
407
- if (status === 401) {
408
- return ErrInvalidSecretKey;
409
- }
410
- if (status === 409) {
411
- return ErrConflict;
412
- }
413
- return ErrServerError;
414
- }
415
- async function readErrorBody3(res) {
416
- const parsed = await res.json().then(function ok(data) {
417
- return { ok: true, data };
418
- }, function fail() {
419
- return { ok: false };
420
- });
421
- if (!parsed.ok) {
422
- return {};
423
- }
424
- return toStudentsErrorBody(parsed.data);
425
- }
426
- async function sendCreateStudentRequest(config) {
427
- const logger = config.logger;
428
- const fetchFn = config.fetch ? config.fetch : globalThis.fetch;
429
- const signal = config.abort ? config.abort.signal : undefined;
430
- const url = `${config.origin}${STUDENTS_PATH2}`;
431
- const fetchResult = await fetchFn(url, {
432
- method: "POST",
433
- headers: {
434
- Authorization: `Bearer ${config.secretKey}`
435
- },
436
- signal
437
- }).then(function ok(response) {
438
- return { ok: true, response };
439
- }, function fail(err) {
440
- return { ok: false, error: err };
441
- });
442
- if (!fetchResult.ok) {
443
- if (isAbortError3(fetchResult.error)) {
444
- logger.error("students timeout", { path: STUDENTS_PATH2 });
445
- throw errors4.wrap(ErrTimeout, fetchResult.error.message);
446
- }
447
- logger.error("students network error", {
448
- path: STUDENTS_PATH2,
449
- error: fetchResult.error
450
- });
451
- throw errors4.wrap(ErrNetwork, fetchResult.error.message);
452
- }
453
- return fetchResult.response;
454
- }
455
- async function parseStudentIdSuccess(config, res) {
456
- const logger = config.logger;
457
- const jsonResult = await res.json().then(function ok(data) {
458
- return { ok: true, data };
459
- }, function fail(err) {
460
- return { ok: false, error: err };
461
- });
462
- if (!jsonResult.ok) {
463
- logger.error("student id response parse failed", {
464
- operation: "create student",
465
- error: jsonResult.error
466
- });
467
- throw errors4.wrap(ErrJsonParse, jsonResult.error.message);
468
- }
469
- const body = parseStudentIdSuccessBody(jsonResult.data);
470
- if (!body) {
471
- logger.error("student id response had invalid shape", {
472
- operation: "create student",
473
- body: jsonResult.data
474
- });
475
- throw errors4.wrap(ErrJsonParse, "create student success body had invalid shape");
476
- }
477
- logger.debug("student id response success", {
478
- operation: "create student",
479
- studentId: body.student_id
480
- });
481
- return body.student_id;
482
- }
483
- async function createStudent(config) {
484
- const logger = config.logger;
485
- logger.debug("create student request");
486
- const res = await sendCreateStudentRequest(config);
487
- if (!res.ok) {
488
- const body = await readErrorBody3(res);
489
- const sentinel = httpSentinel3(res.status);
490
- const detail = body.error ? `${res.status} ${body.error}` : `${res.status}`;
491
- logger.error("create student http error", { status: res.status, body });
492
- throw errors4.wrap(sentinel, detail);
493
- }
494
- return parseStudentIdSuccess(config, res);
127
+ return parseToken(config, res);
495
128
  }
496
129
 
497
130
  // src/server/create-server.ts
498
131
  function createPrimerServer(config) {
499
132
  return {
500
- createStudent() {
501
- return createStudent(config);
502
- },
503
- setStudentHints(studentId, hints) {
504
- return setStudentHints(config, studentId, hints);
505
- },
506
- exchangeStudentForAccessToken(studentId) {
507
- return exchangeStudent(config, studentId);
508
- },
509
- exchangeTimebackStudentForAccessToken(sourcedId) {
510
- return exchangeTimebackStudent(config, sourcedId);
133
+ getToken(input) {
134
+ return getToken(config, input);
511
135
  }
512
136
  };
513
137
  }
@@ -515,4 +139,4 @@ export {
515
139
  createPrimerServer
516
140
  };
517
141
 
518
- //# debugId=ED126AFAB7F1033A64756E2164756E21
142
+ //# debugId=3E72DF6B026E0F7B64756E2164756E21