@superbuilders/primer-tives 0.8.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +244 -68
- package/dist/client/index.js +7 -29
- package/dist/client/index.js.map +5 -5
- package/dist/client/session-context.d.ts +0 -2
- package/dist/client/session-context.d.ts.map +1 -1
- package/dist/client/session.d.ts.map +1 -1
- package/dist/client/transport.d.ts +0 -1
- package/dist/client/transport.d.ts.map +1 -1
- package/dist/errors.d.ts +2 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/server/create-server.d.ts +21 -19
- package/dist/server/create-server.d.ts.map +1 -1
- package/dist/server/exchange.d.ts +10 -5
- package/dist/server/exchange.d.ts.map +1 -1
- package/dist/server/hints.d.ts +25 -0
- package/dist/server/hints.d.ts.map +1 -0
- package/dist/server/index.d.ts +4 -3
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +354 -79
- package/dist/server/index.js.map +9 -8
- package/dist/server/students.d.ts +3 -5
- package/dist/server/students.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,64 +1,161 @@
|
|
|
1
1
|
# @superbuilders/primer-tives
|
|
2
2
|
|
|
3
|
-
TypeScript SDK for the Primer adaptive learning engine.
|
|
3
|
+
TypeScript SDK for the Primer adaptive learning engine.
|
|
4
|
+
|
|
5
|
+
The package exposes **two explicit subpaths**:
|
|
6
|
+
|
|
7
|
+
- `@superbuilders/primer-tives/server` — runs on your backend, authenticates with your Primer `sk_...` secret key, and starts student sessions.
|
|
8
|
+
- `@superbuilders/primer-tives/client` — runs in the browser and drives the Primer lesson state machine.
|
|
9
|
+
|
|
10
|
+
There is **no root export**. You must choose the side of the wire you are on.
|
|
4
11
|
|
|
5
12
|
```sh
|
|
6
13
|
bun add @superbuilders/primer-tives
|
|
7
14
|
```
|
|
8
15
|
|
|
9
|
-
Dependency: `@superbuilders/errors` is installed automatically.
|
|
16
|
+
Dependency note: `@superbuilders/errors` is installed automatically and is used for `errors.try()` / `errors.is()`.
|
|
10
17
|
|
|
11
|
-
##
|
|
12
|
-
|
|
13
|
-
The package ships two subpaths. There is no root export — you must pick the side of the wire you're on:
|
|
18
|
+
## Entrypoints
|
|
14
19
|
|
|
15
20
|
| Import | Runs on | Wraps |
|
|
16
21
|
|---|---|---|
|
|
17
|
-
| `@superbuilders/primer-tives/server` | your backend | `POST /api/v0/
|
|
18
|
-
| `@superbuilders/primer-tives/client` | the browser | `POST /api/v0/advance`
|
|
22
|
+
| `@superbuilders/primer-tives/server` | your backend | `POST /api/v0/students`, `POST /api/v0/auth/exchange`, `POST /api/v0/auth/exchange/timeback` |
|
|
23
|
+
| `@superbuilders/primer-tives/client` | the browser | `POST /api/v0/advance` |
|
|
24
|
+
|
|
25
|
+
## Supported student flows
|
|
26
|
+
|
|
27
|
+
Primer intentionally supports **two** backend-authenticated student flows.
|
|
28
|
+
|
|
29
|
+
### 1. Primer-native / manual students
|
|
30
|
+
|
|
31
|
+
Use this when **your system** owns the learner identity.
|
|
32
|
+
|
|
33
|
+
1. Call `createStudent()` once to mint a stable Primer `studentId`.
|
|
34
|
+
2. Persist that `studentId` in your own database alongside your user record.
|
|
35
|
+
3. At each session start, call `exchangeStudentForAccessToken(studentId)`.
|
|
36
|
+
4. Hand the returned `accessToken` to the browser SDK.
|
|
37
|
+
|
|
38
|
+
### 2. Live-authoritative Timeback students
|
|
39
|
+
|
|
40
|
+
Use this when **Timeback / OneRoster** remains the live authority for each login.
|
|
19
41
|
|
|
20
|
-
|
|
42
|
+
1. At each session start, call `exchangeTimebackStudentForAccessToken(sourcedId)`.
|
|
43
|
+
2. Primer verifies the learner against Timeback on **every call**.
|
|
44
|
+
3. Primer resolves or provisions the frontend-owned Primer student row behind the scenes.
|
|
45
|
+
4. The call returns both the stable Primer `studentId` and a short-lived `accessToken`.
|
|
46
|
+
5. Hand the returned `accessToken` to the browser SDK.
|
|
21
47
|
|
|
22
|
-
|
|
48
|
+
Persisting the returned `studentId` is fine if you want a stable local foreign key. Session startup for this flow still begins from `sourcedId`, and the returned `accessToken` is the session credential you pass to the browser SDK.
|
|
23
49
|
|
|
24
|
-
|
|
50
|
+
## End-to-end examples
|
|
51
|
+
|
|
52
|
+
### Native/manual flow
|
|
25
53
|
|
|
26
54
|
```ts
|
|
27
55
|
// ── your backend ─────────────────────────────────────────────────────
|
|
28
56
|
import * as errors from "@superbuilders/errors"
|
|
29
|
-
import {
|
|
57
|
+
import {
|
|
58
|
+
createPrimerServer,
|
|
59
|
+
ErrConflict,
|
|
60
|
+
ErrInvalidSecretKey,
|
|
61
|
+
ErrStudentNotFound
|
|
62
|
+
} from "@superbuilders/primer-tives/server"
|
|
30
63
|
|
|
31
64
|
const primer = createPrimerServer({
|
|
32
65
|
origin: "https://sb-primer.vercel.app",
|
|
33
|
-
secretKey: process.env.PRIMER_CLIENT_SECRET_KEY_DEV
|
|
66
|
+
secretKey: process.env.PRIMER_CLIENT_SECRET_KEY_DEV!,
|
|
67
|
+
logger: console
|
|
34
68
|
})
|
|
35
69
|
|
|
36
|
-
// One
|
|
37
|
-
const
|
|
38
|
-
|
|
70
|
+
// One time per user.
|
|
71
|
+
const createResult = await errors.try(primer.createStudent())
|
|
72
|
+
if (createResult.error) {
|
|
73
|
+
if (errors.is(createResult.error, ErrInvalidSecretKey)) {
|
|
74
|
+
throw new Error("Primer secret key is invalid")
|
|
75
|
+
}
|
|
76
|
+
if (errors.is(createResult.error, ErrConflict)) {
|
|
77
|
+
throw new Error("This Primer frontend is not provisioned with routable content")
|
|
78
|
+
}
|
|
79
|
+
throw createResult.error
|
|
80
|
+
}
|
|
81
|
+
const studentId = createResult.data
|
|
82
|
+
// persist `studentId` alongside your own user record
|
|
39
83
|
|
|
40
|
-
// Every session
|
|
41
|
-
const
|
|
42
|
-
primer.
|
|
84
|
+
// Every session start.
|
|
85
|
+
const tokenResult = await errors.try(
|
|
86
|
+
primer.exchangeStudentForAccessToken(studentId)
|
|
43
87
|
)
|
|
44
|
-
if (
|
|
45
|
-
|
|
46
|
-
|
|
88
|
+
if (tokenResult.error) {
|
|
89
|
+
if (errors.is(tokenResult.error, ErrInvalidSecretKey)) {
|
|
90
|
+
throw new Error("Primer secret key is invalid")
|
|
91
|
+
}
|
|
92
|
+
if (errors.is(tokenResult.error, ErrStudentNotFound)) {
|
|
93
|
+
throw new Error("Stored Primer student id no longer exists on this frontend")
|
|
94
|
+
}
|
|
95
|
+
throw tokenResult.error
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const { accessToken, expiresInSeconds } = tokenResult.data
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Live-authoritative Timeback flow
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
// ── your backend ─────────────────────────────────────────────────────
|
|
105
|
+
import * as errors from "@superbuilders/errors"
|
|
106
|
+
import {
|
|
107
|
+
createPrimerServer,
|
|
108
|
+
ErrConflict,
|
|
109
|
+
ErrInvalidSecretKey,
|
|
110
|
+
ErrStudentNotFound,
|
|
111
|
+
ErrTimebackUnavailable,
|
|
112
|
+
ErrUnsupportedGrade
|
|
113
|
+
} from "@superbuilders/primer-tives/server"
|
|
114
|
+
|
|
115
|
+
const primer = createPrimerServer({
|
|
116
|
+
origin: "https://sb-primer.vercel.app",
|
|
117
|
+
secretKey: process.env.PRIMER_CLIENT_SECRET_KEY_DEV!,
|
|
118
|
+
logger: console
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
const sessionResult = await errors.try(
|
|
122
|
+
primer.exchangeTimebackStudentForAccessToken("student-123")
|
|
123
|
+
)
|
|
124
|
+
if (sessionResult.error) {
|
|
125
|
+
if (errors.is(sessionResult.error, ErrInvalidSecretKey)) {
|
|
126
|
+
throw new Error("Primer secret key is invalid")
|
|
127
|
+
}
|
|
128
|
+
if (errors.is(sessionResult.error, ErrStudentNotFound)) {
|
|
129
|
+
throw new Error("Timeback sourcedId was not found upstream")
|
|
130
|
+
}
|
|
131
|
+
if (errors.is(sessionResult.error, ErrUnsupportedGrade)) {
|
|
132
|
+
throw new Error("Timeback returned a grade Primer does not support")
|
|
133
|
+
}
|
|
134
|
+
if (errors.is(sessionResult.error, ErrConflict)) {
|
|
135
|
+
throw new Error("This Primer frontend is not provisioned with routable content")
|
|
136
|
+
}
|
|
137
|
+
if (errors.is(sessionResult.error, ErrTimebackUnavailable)) {
|
|
138
|
+
throw new Error("Timeback is temporarily unavailable")
|
|
139
|
+
}
|
|
140
|
+
throw sessionResult.error
|
|
47
141
|
}
|
|
48
|
-
|
|
49
|
-
|
|
142
|
+
|
|
143
|
+
const { studentId, accessToken, expiresInSeconds } = sessionResult.data
|
|
144
|
+
// persist `studentId` if you want a stable Primer foreign key
|
|
145
|
+
// use `accessToken` for this session only
|
|
50
146
|
```
|
|
51
147
|
|
|
148
|
+
### Browser flow
|
|
149
|
+
|
|
52
150
|
```ts
|
|
53
151
|
// ── your frontend ────────────────────────────────────────────────────
|
|
54
152
|
import {
|
|
55
153
|
create,
|
|
56
|
-
ErrRateLimited,
|
|
57
154
|
type PrimerState
|
|
58
155
|
} from "@superbuilders/primer-tives/client"
|
|
59
156
|
|
|
60
157
|
const client = create({
|
|
61
|
-
accessToken,
|
|
158
|
+
accessToken,
|
|
62
159
|
origin: "https://sb-primer.vercel.app",
|
|
63
160
|
subject: "math",
|
|
64
161
|
supportedPcis: [
|
|
@@ -103,8 +200,19 @@ import {
|
|
|
103
200
|
type PrimerServer,
|
|
104
201
|
type PrimerServerConfig,
|
|
105
202
|
type SessionToken,
|
|
106
|
-
type
|
|
107
|
-
|
|
203
|
+
type TimebackSession,
|
|
204
|
+
type PrimerLogger,
|
|
205
|
+
ErrBadRequest,
|
|
206
|
+
ErrConflict,
|
|
207
|
+
ErrExternalAuthorityRequired,
|
|
208
|
+
ErrInvalidSecretKey,
|
|
209
|
+
ErrJsonParse,
|
|
210
|
+
ErrNetwork,
|
|
211
|
+
ErrServerError,
|
|
212
|
+
ErrStudentNotFound,
|
|
213
|
+
ErrTimebackUnavailable,
|
|
214
|
+
ErrTimeout,
|
|
215
|
+
ErrUnsupportedGrade
|
|
108
216
|
} from "@superbuilders/primer-tives/server"
|
|
109
217
|
```
|
|
110
218
|
|
|
@@ -112,100 +220,168 @@ import {
|
|
|
112
220
|
|
|
113
221
|
```ts
|
|
114
222
|
interface PrimerServerConfig {
|
|
115
|
-
readonly origin: string
|
|
116
|
-
readonly secretKey: string
|
|
117
|
-
readonly fetch?: typeof globalThis.fetch
|
|
118
|
-
readonly abort?: AbortController
|
|
119
|
-
readonly logger
|
|
223
|
+
readonly origin: string // e.g. https://sb-primer.vercel.app (no trailing slash)
|
|
224
|
+
readonly secretKey: string // your sk_… key
|
|
225
|
+
readonly fetch?: typeof globalThis.fetch // override (tests, proxies, instrumentation)
|
|
226
|
+
readonly abort?: AbortController // wired into every request signal
|
|
227
|
+
readonly logger: PrimerLogger // required debug/info/warn/error logger (console works)
|
|
120
228
|
}
|
|
121
229
|
|
|
122
230
|
function createPrimerServer(config: PrimerServerConfig): PrimerServer
|
|
123
231
|
```
|
|
124
232
|
|
|
125
|
-
Returns a `PrimerServer` with
|
|
126
|
-
|
|
127
|
-
### `createNativeStudent(gradeLevel): Promise<string>`
|
|
128
|
-
|
|
129
|
-
Provision a new Primer-owned student. Returns the `studentId` string — persist it in your own database keyed by your user. Call this **once per user**.
|
|
233
|
+
Returns a `PrimerServer` with exactly **three** methods:
|
|
130
234
|
|
|
131
235
|
```ts
|
|
132
|
-
|
|
236
|
+
interface PrimerServer {
|
|
237
|
+
createStudent(): Promise<string>
|
|
238
|
+
exchangeStudentForAccessToken(studentId: string): Promise<SessionToken>
|
|
239
|
+
exchangeTimebackStudentForAccessToken(sourcedId: string): Promise<TimebackSession>
|
|
240
|
+
}
|
|
133
241
|
```
|
|
134
242
|
|
|
135
|
-
|
|
243
|
+
## Method reference
|
|
244
|
+
|
|
245
|
+
### `createStudent(): Promise<string>`
|
|
136
246
|
|
|
137
|
-
|
|
247
|
+
Provision a new **frontend-owned Primer student** and return its stable `studentId`.
|
|
138
248
|
|
|
139
|
-
|
|
249
|
+
Use this only for the native/manual flow.
|
|
140
250
|
|
|
141
251
|
```ts
|
|
142
|
-
await primer.
|
|
252
|
+
const studentId = await primer.createStudent()
|
|
143
253
|
```
|
|
144
254
|
|
|
145
|
-
|
|
255
|
+
Notes:
|
|
146
256
|
|
|
147
|
-
|
|
257
|
+
- Call this when your system is the source of truth for learner identity.
|
|
258
|
+
- Persist the returned `studentId` in your own database.
|
|
259
|
+
- Use that stored `studentId` with `exchangeStudentForAccessToken(studentId)` at each session start.
|
|
260
|
+
- `logger` is required. Pass any object implementing `debug/info/warn/error`; `console` is acceptable for basic integrations.
|
|
148
261
|
|
|
149
|
-
|
|
262
|
+
### `exchangeStudentForAccessToken(studentId): Promise<SessionToken>`
|
|
263
|
+
|
|
264
|
+
Mint a short-lived access token for an existing **native/manual** Primer student.
|
|
150
265
|
|
|
151
266
|
```ts
|
|
152
|
-
const { accessToken, expiresInSeconds } =
|
|
267
|
+
const { accessToken, expiresInSeconds } =
|
|
268
|
+
await primer.exchangeStudentForAccessToken(studentId)
|
|
153
269
|
```
|
|
154
270
|
|
|
155
|
-
|
|
271
|
+
Call this at **every session start**.
|
|
272
|
+
|
|
273
|
+
Important:
|
|
274
|
+
|
|
275
|
+
- This is for **native/manual** students only.
|
|
276
|
+
- If `studentId` belongs to a Timeback-linked student, the method throws `ErrExternalAuthorityRequired`.
|
|
277
|
+
- Tokens are short-lived (typically 15 minutes).
|
|
278
|
+
|
|
279
|
+
### `exchangeTimebackStudentForAccessToken(sourcedId): Promise<TimebackSession>`
|
|
280
|
+
|
|
281
|
+
Perform a **live-authoritative Timeback session start**.
|
|
156
282
|
|
|
157
|
-
|
|
283
|
+
Primer verifies the learner against Timeback on every call, then resolves or provisions the corresponding frontend-owned Primer student and returns:
|
|
284
|
+
|
|
285
|
+
- the stable Primer `studentId`
|
|
286
|
+
- a short-lived `accessToken`
|
|
287
|
+
- `expiresInSeconds`
|
|
158
288
|
|
|
159
289
|
```ts
|
|
160
|
-
const { accessToken, expiresInSeconds } =
|
|
290
|
+
const { studentId, accessToken, expiresInSeconds } =
|
|
291
|
+
await primer.exchangeTimebackStudentForAccessToken(sourcedId)
|
|
161
292
|
```
|
|
162
293
|
|
|
163
|
-
|
|
294
|
+
Use this at **every Timeback session start**.
|
|
295
|
+
|
|
296
|
+
Operational rule:
|
|
297
|
+
|
|
298
|
+
- Call this at every Timeback session start.
|
|
299
|
+
- Persist `studentId` if you need a stable Primer foreign key in your own system.
|
|
300
|
+
- Use the returned `accessToken` for the active browser session.
|
|
301
|
+
|
|
302
|
+
## Return types
|
|
303
|
+
|
|
304
|
+
### `SessionToken`
|
|
164
305
|
|
|
165
306
|
```ts
|
|
166
307
|
interface SessionToken {
|
|
167
|
-
readonly accessToken: string
|
|
168
|
-
readonly expiresInSeconds: number
|
|
308
|
+
readonly accessToken: string
|
|
309
|
+
readonly expiresInSeconds: number
|
|
169
310
|
}
|
|
170
311
|
```
|
|
171
312
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
## `GradeLevel`
|
|
313
|
+
### `TimebackSession`
|
|
175
314
|
|
|
176
315
|
```ts
|
|
177
|
-
|
|
316
|
+
interface TimebackSession {
|
|
317
|
+
readonly studentId: string
|
|
318
|
+
readonly accessToken: string
|
|
319
|
+
readonly expiresInSeconds: number
|
|
320
|
+
}
|
|
178
321
|
```
|
|
179
322
|
|
|
180
|
-
Exported as both `GradeLevel` and the `GRADE_LEVELS` readonly tuple.
|
|
181
|
-
|
|
182
323
|
## Error sentinels (`/server`)
|
|
183
324
|
|
|
325
|
+
All `/server` methods throw sentinel-wrapped `Error`s. Use `errors.try()` and `errors.is()` from `@superbuilders/errors`.
|
|
326
|
+
|
|
184
327
|
| Sentinel | Raised when |
|
|
185
328
|
|---|---|
|
|
186
329
|
| `ErrInvalidSecretKey` | HTTP 401 — missing, malformed, or unknown `sk_` |
|
|
187
|
-
| `ErrStudentNotFound` | HTTP 404 —
|
|
188
|
-
| `ErrUnsupportedGrade` | HTTP 400 — Timeback
|
|
189
|
-
| `ErrTimebackUnavailable` | HTTP 502 — Timeback OneRoster endpoint failed |
|
|
190
|
-
| `
|
|
330
|
+
| `ErrStudentNotFound` | HTTP 404 — native `studentId` unknown on this frontend, or Timeback `sourcedId` unknown upstream |
|
|
331
|
+
| `ErrUnsupportedGrade` | HTTP 400 — Timeback returned a grade outside Primer's supported range |
|
|
332
|
+
| `ErrTimebackUnavailable` | HTTP 502 — Timeback OneRoster endpoint failed during live exchange |
|
|
333
|
+
| `ErrExternalAuthorityRequired` | HTTP 409 — attempted native/manual exchange for a Timeback-linked student |
|
|
334
|
+
| `ErrConflict` | HTTP 409 — frontend is not provisioned for routing/content |
|
|
335
|
+
| `ErrBadRequest` | HTTP 400 — validation failure |
|
|
191
336
|
| `ErrServerError` | HTTP 5xx |
|
|
192
|
-
| `ErrJsonParse` | Success response body
|
|
193
|
-
| `ErrNetwork` | fetch() rejected (DNS, connection, TLS) |
|
|
337
|
+
| `ErrJsonParse` | Success response body was not valid JSON or had the wrong shape |
|
|
338
|
+
| `ErrNetwork` | fetch() rejected (DNS, connection, TLS, etc.) |
|
|
194
339
|
| `ErrTimeout` | fetch() aborted (your `AbortController` or `TimeoutError`) |
|
|
195
340
|
|
|
196
|
-
|
|
341
|
+
### Recommended error-handling pattern
|
|
197
342
|
|
|
198
343
|
```ts
|
|
199
|
-
|
|
344
|
+
import * as errors from "@superbuilders/errors"
|
|
345
|
+
import {
|
|
346
|
+
createPrimerServer,
|
|
347
|
+
ErrExternalAuthorityRequired,
|
|
348
|
+
ErrInvalidSecretKey,
|
|
349
|
+
ErrStudentNotFound
|
|
350
|
+
} from "@superbuilders/primer-tives/server"
|
|
351
|
+
|
|
352
|
+
const primer = createPrimerServer({
|
|
353
|
+
origin: "https://sb-primer.vercel.app",
|
|
354
|
+
secretKey: process.env.PRIMER_CLIENT_SECRET_KEY_DEV!,
|
|
355
|
+
logger: console
|
|
356
|
+
})
|
|
357
|
+
|
|
358
|
+
const result = await errors.try(
|
|
359
|
+
primer.exchangeStudentForAccessToken(studentId)
|
|
360
|
+
)
|
|
200
361
|
if (result.error) {
|
|
201
362
|
if (errors.is(result.error, ErrInvalidSecretKey)) {
|
|
202
|
-
// rotate
|
|
363
|
+
// rotate or fix your sk_
|
|
364
|
+
}
|
|
365
|
+
if (errors.is(result.error, ErrStudentNotFound)) {
|
|
366
|
+
// your stored studentId is stale or wrong for this frontend
|
|
367
|
+
}
|
|
368
|
+
if (errors.is(result.error, ErrExternalAuthorityRequired)) {
|
|
369
|
+
// this student must authenticate through live Timeback exchange
|
|
203
370
|
}
|
|
204
371
|
throw result.error
|
|
205
372
|
}
|
|
206
|
-
|
|
373
|
+
|
|
374
|
+
const { accessToken } = result.data
|
|
207
375
|
```
|
|
208
376
|
|
|
377
|
+
## Rules of the road
|
|
378
|
+
|
|
379
|
+
- **Native/manual flow:** `createStudent()` once, then `exchangeStudentForAccessToken(studentId)` every session.
|
|
380
|
+
- **Timeback flow:** `exchangeTimebackStudentForAccessToken(sourcedId)` every session.
|
|
381
|
+
- **Student ids are stable identifiers, not browser credentials.** Always hand the browser a fresh `accessToken` from your backend.
|
|
382
|
+
- **Server-only secrets.** Keep `secretKey` on your backend; never ship it to the browser.
|
|
383
|
+
- **Explicit subpaths only.** Import from `/server` or `/client`, never a package root.
|
|
384
|
+
|
|
209
385
|
---
|
|
210
386
|
|
|
211
387
|
# `/client`
|
package/dist/client/index.js
CHANGED
|
@@ -15,6 +15,7 @@ var ErrTimeout = errors.new("timeout");
|
|
|
15
15
|
var ErrForbidden = errors.new("forbidden");
|
|
16
16
|
var ErrNotFound = errors.new("not found");
|
|
17
17
|
var ErrConflict = errors.new("conflict");
|
|
18
|
+
var ErrExternalAuthorityRequired = errors.new("external authority required");
|
|
18
19
|
var ErrRateLimited = errors.new("rate limited");
|
|
19
20
|
var ErrServiceUnavailable = errors.new("service unavailable");
|
|
20
21
|
var ErrNotSerializable = errors.new("PrimerState is live in-memory state and must not be serialized or stored");
|
|
@@ -735,11 +736,9 @@ function isRetriableError(err) {
|
|
|
735
736
|
}
|
|
736
737
|
function makeSession(sc) {
|
|
737
738
|
const log = sc.log;
|
|
738
|
-
let currentFrameContext = null;
|
|
739
739
|
function resolve(result) {
|
|
740
740
|
switch (result.outcome) {
|
|
741
741
|
case "advanced":
|
|
742
|
-
currentFrameContext = result.frameContext;
|
|
743
742
|
return fromAdvanced(result.stimulus, result.interaction);
|
|
744
743
|
case "submitted":
|
|
745
744
|
return feedbackState(ctx, result.stimulus, result.interaction, result.submission, result.isCorrect, result.feedbackContent, result.review);
|
|
@@ -771,32 +770,11 @@ function makeSession(sc) {
|
|
|
771
770
|
return state;
|
|
772
771
|
}
|
|
773
772
|
async function execute(intent, phase) {
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
subject: sc.subject
|
|
780
|
-
};
|
|
781
|
-
} else {
|
|
782
|
-
if (currentFrameContext === null) {
|
|
783
|
-
log?.error("missing frame context for non-observation intent", {
|
|
784
|
-
phase,
|
|
785
|
-
intentKind: intent.kind
|
|
786
|
-
});
|
|
787
|
-
return {
|
|
788
|
-
phase: "fatal",
|
|
789
|
-
error: ErrBadRequest,
|
|
790
|
-
retriable: false,
|
|
791
|
-
toJSON: poisonToJSON
|
|
792
|
-
};
|
|
793
|
-
}
|
|
794
|
-
body = {
|
|
795
|
-
supportedPcis: sc.supportedPcis,
|
|
796
|
-
intent: { ...intent, frameContext: currentFrameContext },
|
|
797
|
-
subject: sc.subject
|
|
798
|
-
};
|
|
799
|
-
}
|
|
773
|
+
const body = {
|
|
774
|
+
supportedPcis: sc.supportedPcis,
|
|
775
|
+
intent,
|
|
776
|
+
subject: sc.subject
|
|
777
|
+
};
|
|
800
778
|
const result = await sc.transport(body);
|
|
801
779
|
if (!result.ok) {
|
|
802
780
|
if (isFatalError(result.error)) {
|
|
@@ -946,4 +924,4 @@ export {
|
|
|
946
924
|
ErrBadRequest
|
|
947
925
|
};
|
|
948
926
|
|
|
949
|
-
//# debugId=
|
|
927
|
+
//# debugId=03C66069BFA6BE2F64756E2164756E21
|