@superbuilders/primer-tives 0.0.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.
package/README.md ADDED
@@ -0,0 +1,344 @@
1
+ # @superbuilders/primer-tives
2
+
3
+ Client SDK for the Primer adaptive learning engine. Drives a state machine over HTTP POST — the client renders frames, the server controls all routing and assessment.
4
+
5
+ The SDK has no concept of routines, curricula, or backend storage structure. It advances a student through whatever the server decides to serve next.
6
+
7
+ ## Install
8
+
9
+ ```sh
10
+ bun add @superbuilders/primer-tives
11
+ ```
12
+
13
+ Dependency: `@superbuilders/errors` is installed automatically.
14
+
15
+ ## Quick start
16
+
17
+ ```ts
18
+ import { create } from "@superbuilders/primer-tives/client"
19
+ import * as errors from "@superbuilders/errors"
20
+ import { ErrNetwork, ErrRateLimited } from "@superbuilders/primer-tives/errors"
21
+
22
+ const client = create({
23
+ publishableKey: "pk_live_abc123",
24
+ supportedPcis: ["urn:primer:pci:division-remainder"],
25
+ })
26
+
27
+ let state = await client.start("student-uuid")
28
+
29
+ while (state.phase !== "completed" && state.phase !== "fatal") {
30
+ switch (state.phase) {
31
+ case "observation":
32
+ renderStimulus(state.stimulus)
33
+ state = await state.advance()
34
+ break
35
+
36
+ case "interaction":
37
+ switch (state.kind) {
38
+ case "choice":
39
+ state = await state.submitChoice(["option-a"])
40
+ break
41
+ case "text-entry":
42
+ state = await state.submitText("42")
43
+ break
44
+ case "extended-text":
45
+ if (state.cardinality === "single") {
46
+ state = await state.submitText("answer")
47
+ } else {
48
+ state = await state.submitTexts(["a", "b"])
49
+ }
50
+ break
51
+ case "portable-custom":
52
+ state = await state.submit({ quotient: "3", remainder: "1" })
53
+ break
54
+ }
55
+ break
56
+
57
+ case "feedback":
58
+ renderFeedback(state.isCorrect, state.feedbackContent, state.correctAnswer)
59
+ state = await state.advance()
60
+ break
61
+
62
+ case "errored":
63
+ if (errors.is(state.error, ErrRateLimited)) {
64
+ await delay(1000)
65
+ }
66
+ state = await state.retry()
67
+ break
68
+ }
69
+ }
70
+ ```
71
+
72
+ ## Wire protocol
73
+
74
+ Every request is a POST to `https://${origin}/api/v0/advance`:
75
+
76
+ ```
77
+ POST /api/v0/advance
78
+ Header: X-Primer-Publishable-Key: pk_...
79
+ Body: { studentId, supportedPcis, intent }
80
+ ```
81
+
82
+ When `origin` is omitted, the SDK uses a relative path (`/api/v0/advance`) for same-origin requests. For cross-origin integrations, set `origin` to the Primer API host (e.g., `https://sb-primer.vercel.app`). The path constant `ADVANCE_PATH` is exported for reference.
83
+
84
+ The server identifies the student, determines which content to serve, evaluates submissions, and returns the next frame. The client never sends routing information — it only sends:
85
+
86
+ - `studentId` — who is being advanced
87
+ - `supportedPcis` — which custom interaction types the renderer can handle
88
+ - `intent` — `{ kind: "observation" }` or `{ kind: "interaction", submission: ... }`
89
+
90
+ ## Configuration
91
+
92
+ ```ts
93
+ interface Config<Pcis extends PciId = PciId> {
94
+ readonly publishableKey: string
95
+ readonly supportedPcis: readonly Pcis[]
96
+ readonly origin?: string
97
+ readonly fetch?: typeof globalThis.fetch
98
+ readonly abort?: AbortController
99
+ readonly logger?: PrimerLogger
100
+ }
101
+ ```
102
+
103
+ | Field | Required | Description |
104
+ |---|---|---|
105
+ | `publishableKey` | yes | Must start with `pk_`. Sent as `X-Primer-Publishable-Key` header |
106
+ | `supportedPcis` | yes | PCI URNs the renderer handles. `[]` if none |
107
+ | `origin` | no | Origin URL for cross-origin requests. Omit for same-origin (relative fetch). Required for third-party integrations |
108
+ | `fetch` | no | Custom fetch. Defaults to `globalThis.fetch` |
109
+ | `abort` | no | AbortController for cancelling in-flight requests |
110
+ | `logger` | no | Structured logger (`debug`, `info`, `warn`, `error`) |
111
+
112
+ `create()` validates the key prefix immediately — throws `ErrMalformedPublishableKey` if invalid.
113
+
114
+ ## State machine
115
+
116
+ `PrimerState` is a discriminated union on `phase`:
117
+
118
+ ```
119
+ observation → interaction → feedback → observation → ... → completed
120
+ ↓ ↓
121
+ errored errored
122
+
123
+ (retry) → back to failed phase
124
+ ```
125
+
126
+ ### `observation`
127
+
128
+ Display-only frame. Show the stimulus, call `advance()`.
129
+
130
+ ```ts
131
+ state.stimulus // RendererStimulus | null
132
+ state.advance() // Promise<PrimerState>
133
+ ```
134
+
135
+ Stimulus is a discriminated union on `type`:
136
+ - `BodyStimulus` — `{ type: "body", body: ContentBlock[] }` — text-only
137
+ - `ImageStimulus` — `{ type: "image", description: ContentInline[], src: string }` — image-backed
138
+ - `null` — no stimulus
139
+
140
+ ### `interaction`
141
+
142
+ Student must respond. Discriminate on `state.kind`:
143
+
144
+ | `state.kind` | Submit | Key fields |
145
+ |---|---|---|
146
+ | `"choice"` | `submitChoice(keys: string[])` | `options`, `maxChoices`, `minChoices` |
147
+ | `"text-entry"` | `submitText(value: string)` | — |
148
+ | `"extended-text"` (single) | `submitText(value: string)` | `cardinality: "single"` |
149
+ | `"extended-text"` (multiple) | `submitTexts(values: string[])` | `cardinality: "multiple"`, `maxStrings`, `minStrings` |
150
+ | `"portable-custom"` | `submit(value: PciValue<K>)` | `pciId`, `properties` |
151
+
152
+ All submit methods return `Promise<PrimerState>`.
153
+
154
+ ### `feedback`
155
+
156
+ Server evaluated the submission. Show result, call `advance()`.
157
+
158
+ ```ts
159
+ state.stimulus // RendererStimulus | null
160
+ state.interaction // the original interaction
161
+ state.submission // the student's submission
162
+ state.isCorrect // boolean
163
+ state.feedbackContent // ContentInline[]
164
+ state.correctAnswer // RendererCorrectAnswer | null
165
+ state.advance() // Promise<PrimerState>
166
+ ```
167
+
168
+ `correctAnswer` carries the canonical correct response, or `null` when unavailable:
169
+
170
+ ```ts
171
+ type RendererCorrectScalarValue =
172
+ | { kind: "identifier"; value: string }
173
+ | { kind: "string"; value: string }
174
+ | { kind: "integer"; value: number }
175
+ | { kind: "float"; value: number }
176
+
177
+ type RendererCorrectAnswer =
178
+ | { kind: "single"; value: RendererCorrectScalarValue | null }
179
+ | { kind: "multiple"; values: RendererCorrectScalarValue[] }
180
+ | {
181
+ kind: "record"
182
+ fields: Array<{
183
+ fieldIdentifier: string
184
+ baseType: "identifier" | "string" | "integer" | "float"
185
+ value: RendererCorrectScalarValue | null
186
+ }>
187
+ }
188
+ ```
189
+
190
+ For choice interactions, extract correct option identifiers from `kind: "identifier"` scalars. For text-entry, check `kind: "string"` or `kind: "integer"`. For PCI record responses, iterate `fields`. `null` means the server could not determine a correct response.
191
+
192
+ ### `completed`
193
+
194
+ Session finished. No further actions.
195
+
196
+ ### `errored`
197
+
198
+ Retryable error (network, timeout, rate limit, 5xx). Remembers the failed intent.
199
+
200
+ ```ts
201
+ state.error // Error sentinel (use errors.is())
202
+ state.failedPhase // "observation" | "interaction"
203
+ state.retry() // Promise<PrimerState> — replays exact failed intent
204
+ ```
205
+
206
+ ### `fatal`
207
+
208
+ Terminal. Unsupported PCI or unknown interaction type. Session is broken.
209
+
210
+ ## Stimulus & Interaction
211
+
212
+ Every state carries a `stimulus: RendererStimulus | null` directly:
213
+
214
+ ```ts
215
+ state.stimulus // RendererStimulus | null
216
+ ```
217
+
218
+ Stimulus is a discriminated union on `type`:
219
+ - `BodyStimulus` — `{ type: "body", body: ContentBlock[] }` — text-only
220
+ - `ImageStimulus` — `{ type: "image", description: ContentInline[], src: string }` — image-backed
221
+ - `null` — no stimulus
222
+
223
+ Interaction states also carry `interaction` directly:
224
+
225
+ ```ts
226
+ state.interaction.prompt // ContentInline[]
227
+ state.interaction.type // "choice" | "text-entry" | "extended-text" | "portable-custom"
228
+ ```
229
+
230
+ ## Client-side validation
231
+
232
+ The SDK validates submissions before sending them to the server:
233
+
234
+ **Choice:** rejects fewer than `minChoices`, more than `maxChoices`, duplicates, unknown option identifiers.
235
+
236
+ **Extended text (multiple):** rejects fewer than `minStrings`, more than `maxStrings`.
237
+
238
+ Failed validation returns an `errored` state with `ErrInvalidSubmission`.
239
+
240
+ ## Error sentinels
241
+
242
+ ```ts
243
+ import * as errors from "@superbuilders/errors"
244
+ import { ErrNetwork, ErrRateLimited } from "@superbuilders/primer-tives/errors"
245
+
246
+ if (errors.is(state.error, ErrNetwork)) { /* handle */ }
247
+ ```
248
+
249
+ | Sentinel | Cause |
250
+ |---|---|
251
+ | `ErrNetwork` | Fetch failed (offline, DNS, CORS) |
252
+ | `ErrTimeout` | Request aborted or timed out |
253
+ | `ErrJsonParse` | Response not valid JSON |
254
+ | `ErrBadRequest` | 400 |
255
+ | `ErrInvalidPublishableKey` | 401 |
256
+ | `ErrForbidden` | 403 |
257
+ | `ErrNotFound` | 404 |
258
+ | `ErrConflict` | 409 |
259
+ | `ErrUnsupportedPci` | 422 — server sent a PCI the client can't handle |
260
+ | `ErrRateLimited` | 429 |
261
+ | `ErrServerError` | 5xx |
262
+ | `ErrServiceUnavailable` | 502/503/504 |
263
+ | `ErrInvalidSubmission` | Client-side validation failed |
264
+ | `ErrNotSerializable` | Attempted to JSON.stringify PrimerState |
265
+ | `ErrMalformedPublishableKey` | Key doesn't start with `pk_` |
266
+
267
+ **`errored`** = retryable (call `retry()`). **`fatal`** = terminal.
268
+
269
+ ## Submission payloads
270
+
271
+ ```ts
272
+ type RendererSubmission<K extends PciId = PciId> =
273
+ | { type: "choice"; selectedKeys: string[] }
274
+ | { type: "text-entry"; value: string }
275
+ | { type: "extended-text"; values: string[] }
276
+ | { type: "portable-custom"; pciId: K; value: PciValue<K> }
277
+ ```
278
+
279
+ See `/types` for the full type surface.
280
+
281
+ ## Content format
282
+
283
+ Minimal example of the content format:
284
+
285
+ ```ts
286
+ type ContentInline =
287
+ | { type: "text"; value: string }
288
+ | { type: "italic"; children: ContentInline[] }
289
+
290
+ type ContentBlock = { type: "paragraph"; children: ContentInline[] }
291
+ ```
292
+
293
+ `inlinesToPlainText(nodes)` strips inline formatting for accessibility labels. `blocksToPlainText(blocks)` flattens blocks to plain text. See `/content` for the complete type surface.
294
+
295
+ ## PCI system
296
+
297
+ Portable Custom Interactions handle domain-specific input types with full type safety.
298
+
299
+ ### Registry
300
+
301
+ ```ts
302
+ interface PciRegistry {
303
+ "urn:primer:pci:division-remainder": {
304
+ props: { dividend: number; divisor: number }
305
+ value: { quotient: string; remainder: string }
306
+ }
307
+ "urn:primer:pci:fraction-addition": {
308
+ props: {
309
+ left: { numerator: number; denominator: number }
310
+ right: { numerator: number; denominator: number }
311
+ }
312
+ value: { numerator: string; denominator: string }
313
+ }
314
+ }
315
+ ```
316
+
317
+ ### Type helpers
318
+
319
+ ```ts
320
+ type PciId = keyof PciRegistry & string
321
+ type PciProps<K extends PciId> // server-provided props
322
+ type PciValue<K extends PciId> // submission value
323
+ ```
324
+
325
+ ### Adding a PCI
326
+
327
+ 1. Add entry to `PciRegistry` in `src/pci.ts`
328
+ 2. URN format: `urn:primer:pci:<name>`
329
+ 3. Include in `supportedPcis` when calling `create()`
330
+ 4. Build renderer with `PciRenderProps<"urn:primer:pci:your-pci">`
331
+
332
+ ## Subpath exports
333
+
334
+ | Path | JS | Description |
335
+ |---|---|---|
336
+ | `/client` | yes | `create()`, transport, state machine |
337
+ | `/types` | yes | States, interactions, submissions, `PciRenderProps` |
338
+ | `/content` | yes | `ContentInline`, `inlinesToPlainText()` |
339
+ | `/errors` | yes | 15 sentinel error constants |
340
+ | `/pci` | yes | `PciId`, `PciProps`, `PciValue`, `PciRegistry` |
341
+
342
+ ## Non-serializable state
343
+
344
+ `PrimerState` contains closures. Every state object has a poisoned `toJSON()` that throws `ErrNotSerializable`. Do not persist, stringify, or transfer `PrimerState` — it is ephemeral in-memory state.
@@ -0,0 +1,25 @@
1
+ import type { PciId } from "./pci";
2
+ import type { PrimerState } from "./types";
3
+ interface PrimerLogger {
4
+ debug(message: string, attributes?: Record<string, unknown>): void;
5
+ info(message: string, attributes?: Record<string, unknown>): void;
6
+ warn(message: string, attributes?: Record<string, unknown>): void;
7
+ error(message: string, attributes?: Record<string, unknown>): void;
8
+ }
9
+ declare const DEFAULT_ORIGIN = "https://sb-primer.vercel.app";
10
+ declare const ADVANCE_PATH = "/api/v0/advance";
11
+ interface Config<Pcis extends PciId = PciId> {
12
+ readonly publishableKey: string;
13
+ readonly supportedPcis: readonly Pcis[];
14
+ readonly origin?: string;
15
+ readonly fetch?: typeof globalThis.fetch;
16
+ readonly abort?: AbortController;
17
+ readonly logger?: PrimerLogger;
18
+ }
19
+ interface Client<Pcis extends PciId = PciId> {
20
+ start(studentId: string): Promise<PrimerState<Pcis>>;
21
+ }
22
+ declare function create<const Pcis extends PciId>(config: Config<Pcis>): Client<Pcis>;
23
+ export { ADVANCE_PATH, DEFAULT_ORIGIN, create };
24
+ export type { Client, Config, PrimerLogger };
25
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,KAAK,EAAY,MAAM,iCAAiC,CAAA;AACtE,OAAO,KAAK,EAIX,WAAW,EAMX,MAAM,mCAAmC,CAAA;AAG1C,UAAU,YAAY;IACrB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;IAClE,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;IACjE,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;IACjE,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;CAClE;AAiCD,QAAA,MAAM,cAAc,iCAAiC,CAAA;AACrD,QAAA,MAAM,YAAY,oBAAoB,CAAA;AAEtC,UAAU,MAAM,CAAC,IAAI,SAAS,KAAK,GAAG,KAAK;IAC1C,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,aAAa,EAAE,SAAS,IAAI,EAAE,CAAA;IACvC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;IACxC,QAAQ,CAAC,KAAK,CAAC,EAAE,eAAe,CAAA;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,YAAY,CAAA;CAC9B;AAED,UAAU,MAAM,CAAC,IAAI,SAAS,KAAK,GAAG,KAAK;IAC1C,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAA;CACpD;AA+CD,iBAAS,MAAM,CAAC,KAAK,CAAC,IAAI,SAAS,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAmb5E;AAED,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,EAAE,CAAA;AAC/C,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,CAAA"}
package/dist/client.js ADDED
@@ -0,0 +1,383 @@
1
+ // src/errors.ts
2
+ import * as errors from "@superbuilders/errors";
3
+ var ErrNetwork = errors.new("network");
4
+ var ErrJsonParse = errors.new("json parse");
5
+ var ErrUnsupportedPci = errors.new("unsupported pci");
6
+ var ErrInvalidPublishableKey = errors.new("invalid publishable key");
7
+ var ErrBadRequest = errors.new("bad request");
8
+ var ErrServerError = errors.new("server error");
9
+ var ErrTimeout = errors.new("timeout");
10
+ var ErrForbidden = errors.new("forbidden");
11
+ var ErrNotFound = errors.new("not found");
12
+ var ErrConflict = errors.new("conflict");
13
+ var ErrRateLimited = errors.new("rate limited");
14
+ var ErrServiceUnavailable = errors.new("service unavailable");
15
+ var ErrNotSerializable = errors.new("PrimerState is live in-memory state and must not be serialized or stored");
16
+ var ErrInvalidSubmission = errors.new("invalid submission");
17
+ var ErrMalformedPublishableKey = errors.new("malformed publishable key");
18
+ // src/client.ts
19
+ import * as errors2 from "@superbuilders/errors";
20
+ var DEFAULT_ORIGIN = "https://sb-primer.vercel.app";
21
+ var ADVANCE_PATH = "/api/v0/advance";
22
+ function poisonToJSON() {
23
+ throw ErrNotSerializable;
24
+ }
25
+ function httpSentinel(status) {
26
+ if (status === 400) {
27
+ return ErrBadRequest;
28
+ }
29
+ if (status === 401) {
30
+ return ErrInvalidPublishableKey;
31
+ }
32
+ if (status === 403) {
33
+ return ErrForbidden;
34
+ }
35
+ if (status === 404) {
36
+ return ErrNotFound;
37
+ }
38
+ if (status === 409) {
39
+ return ErrConflict;
40
+ }
41
+ if (status === 422) {
42
+ return ErrUnsupportedPci;
43
+ }
44
+ if (status === 429) {
45
+ return ErrRateLimited;
46
+ }
47
+ if (status === 502 || status === 503 || status === 504) {
48
+ return ErrServiceUnavailable;
49
+ }
50
+ return ErrServerError;
51
+ }
52
+ function isAbortError(err) {
53
+ if (err instanceof DOMException && err.name === "AbortError") {
54
+ return true;
55
+ }
56
+ if (err instanceof DOMException && err.name === "TimeoutError") {
57
+ return true;
58
+ }
59
+ return false;
60
+ }
61
+ var PK_PREFIX = "pk_";
62
+ function create(config) {
63
+ const configFetch = config.fetch;
64
+ const fetchFn = configFetch ? configFetch : globalThis.fetch;
65
+ const log = config.logger;
66
+ if (!config.publishableKey.startsWith(PK_PREFIX)) {
67
+ log?.error("malformed publishable key", { prefix: PK_PREFIX });
68
+ throw errors2.wrap(ErrMalformedPublishableKey, `key must start with '${PK_PREFIX}'`);
69
+ }
70
+ function transportSignal() {
71
+ if (config.abort) {
72
+ return config.abort.signal;
73
+ }
74
+ return;
75
+ }
76
+ async function transport(body) {
77
+ log?.debug("transport request", {
78
+ studentId: body.studentId,
79
+ intentKind: body.intent.kind
80
+ });
81
+ const url = config.origin ? `${config.origin}${ADVANCE_PATH}` : ADVANCE_PATH;
82
+ const fetchResult = await fetchFn(url, {
83
+ method: "POST",
84
+ headers: {
85
+ "Content-Type": "application/json",
86
+ "X-Primer-Publishable-Key": config.publishableKey
87
+ },
88
+ body: JSON.stringify(body),
89
+ signal: transportSignal()
90
+ }).then(function ok(r) {
91
+ return { ok: true, response: r };
92
+ }, function fail(err) {
93
+ return { ok: false, error: err };
94
+ });
95
+ if (!fetchResult.ok) {
96
+ if (isAbortError(fetchResult.error)) {
97
+ log?.error("transport timeout", {
98
+ studentId: body.studentId,
99
+ intentKind: body.intent.kind
100
+ });
101
+ return { ok: false, error: errors2.wrap(ErrTimeout, fetchResult.error.message) };
102
+ }
103
+ log?.error("transport network error", {
104
+ studentId: body.studentId,
105
+ error: fetchResult.error
106
+ });
107
+ return { ok: false, error: errors2.wrap(ErrNetwork, fetchResult.error.message) };
108
+ }
109
+ const res = fetchResult.response;
110
+ if (!res.ok) {
111
+ const text = await res.text().catch(function fallback() {
112
+ return "";
113
+ });
114
+ const sentinel = httpSentinel(res.status);
115
+ log?.error("transport http error", {
116
+ studentId: body.studentId,
117
+ status: res.status
118
+ });
119
+ return { ok: false, error: errors2.wrap(sentinel, text) };
120
+ }
121
+ const jsonResult = await res.json().then(function ok(data) {
122
+ return { ok: true, data };
123
+ }, function fail(err) {
124
+ return { ok: false, error: err };
125
+ });
126
+ if (!jsonResult.ok) {
127
+ log?.error("transport json parse failed", {
128
+ studentId: body.studentId,
129
+ error: jsonResult.error
130
+ });
131
+ return { ok: false, error: errors2.wrap(ErrJsonParse, jsonResult.error.message) };
132
+ }
133
+ log?.debug("transport success", {
134
+ studentId: body.studentId,
135
+ intentKind: body.intent.kind
136
+ });
137
+ return { ok: true, data: jsonResult.data };
138
+ }
139
+ function makeSession(studentId) {
140
+ function resolve(result) {
141
+ switch (result.outcome) {
142
+ case "advanced":
143
+ return fromAdvanced(result.stimulus, result.interaction);
144
+ case "submitted":
145
+ return feedbackState(result.stimulus, result.interaction, result.submission, result.isCorrect, result.feedbackContent, result.correctAnswer);
146
+ case "completed":
147
+ return { phase: "completed", toJSON: poisonToJSON };
148
+ }
149
+ }
150
+ function errored(error, failedPhase, intent) {
151
+ return {
152
+ phase: "errored",
153
+ error,
154
+ failedPhase,
155
+ retry: function retry() {
156
+ log?.debug("retrying from errored state", { failedPhase });
157
+ return execute(intent, failedPhase);
158
+ },
159
+ toJSON: poisonToJSON
160
+ };
161
+ }
162
+ async function execute(intent, phase) {
163
+ const body = {
164
+ studentId,
165
+ supportedPcis: config.supportedPcis,
166
+ intent
167
+ };
168
+ const result = await transport(body);
169
+ if (!result.ok) {
170
+ return errored(result.error, phase, intent);
171
+ }
172
+ return resolve(result.data);
173
+ }
174
+ function isPciSupported(pciId) {
175
+ for (const id of config.supportedPcis) {
176
+ if (id === pciId) {
177
+ return true;
178
+ }
179
+ }
180
+ return false;
181
+ }
182
+ function fromAdvanced(stimulus, interaction) {
183
+ if (interaction === null) {
184
+ return observationState(stimulus);
185
+ }
186
+ if (interaction.type === "portable-custom") {
187
+ if (!isPciSupported(interaction.pciId)) {
188
+ log?.error("unsupported pci in frame", { pciId: interaction.pciId });
189
+ return {
190
+ phase: "fatal",
191
+ error: errors2.wrap(ErrUnsupportedPci, `pci '${interaction.pciId}'`),
192
+ toJSON: poisonToJSON
193
+ };
194
+ }
195
+ }
196
+ return pendingInteractionState(stimulus, interaction);
197
+ }
198
+ function observationState(stimulus) {
199
+ return {
200
+ phase: "observation",
201
+ stimulus,
202
+ advance: function advance() {
203
+ return execute({ kind: "observation" }, "observation");
204
+ },
205
+ toJSON: poisonToJSON
206
+ };
207
+ }
208
+ function pendingInteractionState(stimulus, interaction) {
209
+ switch (interaction.type) {
210
+ case "choice":
211
+ return choiceState(stimulus, interaction, interaction.options, interaction.maxChoices, interaction.minChoices);
212
+ case "text-entry":
213
+ return textEntryState(stimulus, interaction);
214
+ case "extended-text":
215
+ return extendedTextState(stimulus, interaction);
216
+ case "portable-custom":
217
+ return pciInteractionState(stimulus, interaction);
218
+ }
219
+ }
220
+ function findUnknownKey(selectedKeys, options) {
221
+ const optionIds = new Set(options.map(function getId(o) {
222
+ return o.identifier;
223
+ }));
224
+ for (const key of selectedKeys) {
225
+ if (!optionIds.has(key)) {
226
+ return key;
227
+ }
228
+ }
229
+ return null;
230
+ }
231
+ function validateChoiceSubmission(selectedKeys, options, maxChoices, minChoices) {
232
+ if (selectedKeys.length < minChoices) {
233
+ log?.error("choice submit below minChoices", { selectedKeys, minChoices });
234
+ return errors2.wrap(ErrInvalidSubmission, `need at least ${minChoices} selections`);
235
+ }
236
+ if (selectedKeys.length > maxChoices) {
237
+ log?.error("choice submit above maxChoices", { selectedKeys, maxChoices });
238
+ return errors2.wrap(ErrInvalidSubmission, `at most ${maxChoices} selections`);
239
+ }
240
+ const uniqueKeys = new Set(selectedKeys);
241
+ if (uniqueKeys.size !== selectedKeys.length) {
242
+ log?.error("choice submit has duplicates", { selectedKeys });
243
+ return errors2.wrap(ErrInvalidSubmission, "duplicate selections");
244
+ }
245
+ const unknownKey = findUnknownKey(selectedKeys, options);
246
+ if (unknownKey) {
247
+ log?.error("choice submit has unknown key", { key: unknownKey });
248
+ return errors2.wrap(ErrInvalidSubmission, `unknown option '${unknownKey}'`);
249
+ }
250
+ return null;
251
+ }
252
+ function choiceState(stimulus, interaction, options, maxChoices, minChoices) {
253
+ return {
254
+ phase: "interaction",
255
+ kind: "choice",
256
+ stimulus,
257
+ interaction,
258
+ options,
259
+ maxChoices,
260
+ minChoices,
261
+ submitChoice: function submitChoice(selectedKeys) {
262
+ const validationError = validateChoiceSubmission(selectedKeys, options, maxChoices, minChoices);
263
+ if (validationError) {
264
+ return Promise.resolve(errored(validationError, "interaction", {
265
+ kind: "interaction",
266
+ submission: { type: "choice", selectedKeys }
267
+ }));
268
+ }
269
+ return execute({ kind: "interaction", submission: { type: "choice", selectedKeys } }, "interaction");
270
+ },
271
+ toJSON: poisonToJSON
272
+ };
273
+ }
274
+ function textEntryState(stimulus, interaction) {
275
+ return {
276
+ phase: "interaction",
277
+ kind: "text-entry",
278
+ stimulus,
279
+ interaction,
280
+ submitText: function submitText(value) {
281
+ return execute({ kind: "interaction", submission: { type: "text-entry", value } }, "interaction");
282
+ },
283
+ toJSON: poisonToJSON
284
+ };
285
+ }
286
+ function validateExtendedTextSubmission(values, minStrings, maxStrings) {
287
+ if (values.length < minStrings) {
288
+ log?.error("extended-text submit below minStrings", { count: values.length, minStrings });
289
+ return errors2.wrap(ErrInvalidSubmission, `need at least ${minStrings} values`);
290
+ }
291
+ if (values.length > maxStrings) {
292
+ log?.error("extended-text submit above maxStrings", { count: values.length, maxStrings });
293
+ return errors2.wrap(ErrInvalidSubmission, `at most ${maxStrings} values`);
294
+ }
295
+ return null;
296
+ }
297
+ function extendedTextState(stimulus, interaction) {
298
+ const maxStrings = interaction.maxStrings;
299
+ const minStrings = interaction.minStrings;
300
+ if (maxStrings === 1) {
301
+ return {
302
+ phase: "interaction",
303
+ kind: "extended-text",
304
+ cardinality: "single",
305
+ stimulus,
306
+ interaction,
307
+ submitText: function submitText(value) {
308
+ return execute({ kind: "interaction", submission: { type: "extended-text", values: [value] } }, "interaction");
309
+ },
310
+ toJSON: poisonToJSON
311
+ };
312
+ }
313
+ return {
314
+ phase: "interaction",
315
+ kind: "extended-text",
316
+ cardinality: "multiple",
317
+ stimulus,
318
+ interaction,
319
+ maxStrings,
320
+ minStrings,
321
+ submitTexts: function submitTexts(values) {
322
+ const validationError = validateExtendedTextSubmission(values, minStrings, maxStrings);
323
+ if (validationError) {
324
+ return Promise.resolve(errored(validationError, "interaction", {
325
+ kind: "interaction",
326
+ submission: { type: "extended-text", values }
327
+ }));
328
+ }
329
+ return execute({ kind: "interaction", submission: { type: "extended-text", values } }, "interaction");
330
+ },
331
+ toJSON: poisonToJSON
332
+ };
333
+ }
334
+ function pciInteractionState(stimulus, interaction) {
335
+ const { pciId, properties } = interaction;
336
+ function submit(value) {
337
+ log?.debug("pci submit", { pciId });
338
+ const submission = { type: "portable-custom", pciId, value };
339
+ return execute({ kind: "interaction", submission }, "interaction");
340
+ }
341
+ return {
342
+ phase: "interaction",
343
+ kind: "portable-custom",
344
+ stimulus,
345
+ interaction,
346
+ pciId,
347
+ properties,
348
+ submit,
349
+ toJSON: poisonToJSON
350
+ };
351
+ }
352
+ function feedbackState(stimulus, interaction, submission, isCorrect, feedbackContent, correctAnswer) {
353
+ return {
354
+ phase: "feedback",
355
+ stimulus,
356
+ interaction,
357
+ submission,
358
+ isCorrect,
359
+ feedbackContent,
360
+ correctAnswer,
361
+ advance: function advance() {
362
+ return execute({ kind: "observation" }, "observation");
363
+ },
364
+ toJSON: poisonToJSON
365
+ };
366
+ }
367
+ return { execute };
368
+ }
369
+ return {
370
+ async start(studentId) {
371
+ log?.debug("start", { studentId });
372
+ const s = makeSession(studentId);
373
+ return s.execute({ kind: "observation" }, "observation");
374
+ }
375
+ };
376
+ }
377
+ export {
378
+ create,
379
+ DEFAULT_ORIGIN,
380
+ ADVANCE_PATH
381
+ };
382
+
383
+ //# debugId=B2799CD48726B75C64756E2164756E21
@@ -0,0 +1,11 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/errors.ts", "../src/client.ts"],
4
+ "sourcesContent": [
5
+ "import * as errors from \"@superbuilders/errors\"\n\nconst ErrNetwork = errors.new(\"network\")\nconst ErrJsonParse = errors.new(\"json parse\")\nconst ErrUnsupportedPci = errors.new(\"unsupported pci\")\nconst ErrInvalidPublishableKey = errors.new(\"invalid publishable key\")\nconst ErrBadRequest = errors.new(\"bad request\")\nconst ErrServerError = errors.new(\"server error\")\nconst ErrTimeout = errors.new(\"timeout\")\nconst ErrForbidden = errors.new(\"forbidden\")\nconst ErrNotFound = errors.new(\"not found\")\nconst ErrConflict = errors.new(\"conflict\")\nconst ErrRateLimited = errors.new(\"rate limited\")\nconst ErrServiceUnavailable = errors.new(\"service unavailable\")\nconst ErrNotSerializable = errors.new(\n\t\"PrimerState is live in-memory state and must not be serialized or stored\"\n)\nconst ErrInvalidSubmission = errors.new(\"invalid submission\")\nconst ErrMalformedPublishableKey = errors.new(\"malformed publishable key\")\n\nexport {\n\tErrBadRequest,\n\tErrConflict,\n\tErrForbidden,\n\tErrInvalidPublishableKey,\n\tErrInvalidSubmission,\n\tErrMalformedPublishableKey,\n\tErrJsonParse,\n\tErrNetwork,\n\tErrNotFound,\n\tErrNotSerializable,\n\tErrRateLimited,\n\tErrServerError,\n\tErrServiceUnavailable,\n\tErrTimeout,\n\tErrUnsupportedPci\n}\n",
6
+ "import * as errors from \"@superbuilders/errors\"\nimport {\n\tErrBadRequest,\n\tErrConflict,\n\tErrForbidden,\n\tErrInvalidPublishableKey,\n\tErrJsonParse,\n\tErrMalformedPublishableKey,\n\tErrNetwork,\n\tErrNotFound,\n\tErrNotSerializable,\n\tErrRateLimited,\n\tErrServerError,\n\tErrServiceUnavailable,\n\tErrTimeout,\n\tErrUnsupportedPci,\n\tErrInvalidSubmission\n} from \"@superbuilders/primer-tives/errors\"\nimport type { PciId, PciValue } from \"@superbuilders/primer-tives/pci\"\nimport type {\n\tErroredState,\n\tPciInteraction,\n\tPciInteractionState,\n\tPrimerState,\n\tRendererChoice,\n\tRendererCorrectAnswer,\n\tRendererInteraction,\n\tRendererStimulus,\n\tRendererSubmission\n} from \"@superbuilders/primer-tives/types\"\nimport type { ContentInline } from \"@superbuilders/primer-tives/content\"\n\ninterface PrimerLogger {\n\tdebug(message: string, attributes?: Record<string, unknown>): void\n\tinfo(message: string, attributes?: Record<string, unknown>): void\n\twarn(message: string, attributes?: Record<string, unknown>): void\n\terror(message: string, attributes?: Record<string, unknown>): void\n}\n\ntype WireIntent<Pcis extends PciId = PciId> =\n\t| { kind: \"observation\" }\n\t| { kind: \"interaction\"; submission: RendererSubmission<Pcis> }\n\ntype WireResult<Pcis extends PciId = PciId> =\n\t| {\n\t\t\toutcome: \"advanced\"\n\t\t\tstimulus: RendererStimulus | null\n\t\t\tinteraction: RendererInteraction<Pcis> | null\n\t }\n\t| {\n\t\t\toutcome: \"submitted\"\n\t\t\tstimulus: RendererStimulus | null\n\t\t\tinteraction: RendererInteraction<Pcis>\n\t\t\tsubmission: RendererSubmission<Pcis>\n\t\t\tisCorrect: boolean\n\t\t\tfeedbackContent: ContentInline[]\n\t\t\tcorrectAnswer: RendererCorrectAnswer | null\n\t }\n\t| { outcome: \"completed\" }\n\ninterface WireRequestBody<Pcis extends PciId = PciId> {\n\tstudentId: string\n\tsupportedPcis: readonly PciId[]\n\tintent: WireIntent<Pcis>\n}\n\ntype TransportResult<Pcis extends PciId = PciId> =\n\t| { ok: true; data: WireResult<Pcis> }\n\t| { ok: false; error: Error }\n\nconst DEFAULT_ORIGIN = \"https://sb-primer.vercel.app\"\nconst ADVANCE_PATH = \"/api/v0/advance\"\n\ninterface Config<Pcis extends PciId = PciId> {\n\treadonly publishableKey: string\n\treadonly supportedPcis: readonly Pcis[]\n\treadonly origin?: string\n\treadonly fetch?: typeof globalThis.fetch\n\treadonly abort?: AbortController\n\treadonly logger?: PrimerLogger\n}\n\ninterface Client<Pcis extends PciId = PciId> {\n\tstart(studentId: string): Promise<PrimerState<Pcis>>\n}\n\nfunction poisonToJSON(): never {\n\t// biome-ignore lint/plugin: intentional toJSON trap — logging here would spam on every accidental serialize attempt\n\tthrow ErrNotSerializable\n}\n\nfunction httpSentinel(status: number): Error {\n\tif (status === 400) {\n\t\treturn ErrBadRequest\n\t}\n\tif (status === 401) {\n\t\treturn ErrInvalidPublishableKey\n\t}\n\tif (status === 403) {\n\t\treturn ErrForbidden\n\t}\n\tif (status === 404) {\n\t\treturn ErrNotFound\n\t}\n\tif (status === 409) {\n\t\treturn ErrConflict\n\t}\n\tif (status === 422) {\n\t\treturn ErrUnsupportedPci\n\t}\n\tif (status === 429) {\n\t\treturn ErrRateLimited\n\t}\n\tif (status === 502 || status === 503 || status === 504) {\n\t\treturn ErrServiceUnavailable\n\t}\n\treturn ErrServerError\n}\n\nfunction isAbortError(err: unknown): boolean {\n\tif (err instanceof DOMException && err.name === \"AbortError\") {\n\t\treturn true\n\t}\n\tif (err instanceof DOMException && err.name === \"TimeoutError\") {\n\t\treturn true\n\t}\n\treturn false\n}\n\nconst PK_PREFIX = \"pk_\"\n\nfunction create<const Pcis extends PciId>(config: Config<Pcis>): Client<Pcis> {\n\tconst configFetch = config.fetch\n\tconst fetchFn = configFetch ? configFetch : globalThis.fetch\n\tconst log = config.logger\n\n\tif (!config.publishableKey.startsWith(PK_PREFIX)) {\n\t\tlog?.error(\"malformed publishable key\", { prefix: PK_PREFIX })\n\t\t// biome-ignore lint/plugin: client SDK — log is optional config.logger, not slog\n\t\tthrow errors.wrap(ErrMalformedPublishableKey, `key must start with '${PK_PREFIX}'`)\n\t}\n\n\tfunction transportSignal(): AbortSignal | undefined {\n\t\tif (config.abort) {\n\t\t\treturn config.abort.signal\n\t\t}\n\t\treturn undefined\n\t}\n\n\tasync function transport(body: WireRequestBody<Pcis>): Promise<TransportResult<Pcis>> {\n\t\tlog?.debug(\"transport request\", {\n\t\t\tstudentId: body.studentId,\n\t\t\tintentKind: body.intent.kind\n\t\t})\n\n\t\tconst url = config.origin ? `${config.origin}${ADVANCE_PATH}` : ADVANCE_PATH\n\t\tconst fetchResult = await fetchFn(url, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\"X-Primer-Publishable-Key\": config.publishableKey\n\t\t\t},\n\t\t\tbody: JSON.stringify(body),\n\t\t\tsignal: transportSignal()\n\t\t}).then(\n\t\t\tfunction ok(r: Response) {\n\t\t\t\treturn { ok: true, response: r } as const\n\t\t\t},\n\t\t\tfunction fail(err: Error) {\n\t\t\t\treturn { ok: false, error: err } as const\n\t\t\t}\n\t\t)\n\n\t\tif (!fetchResult.ok) {\n\t\t\tif (isAbortError(fetchResult.error)) {\n\t\t\t\tlog?.error(\"transport timeout\", {\n\t\t\t\t\tstudentId: body.studentId,\n\t\t\t\t\tintentKind: body.intent.kind\n\t\t\t\t})\n\t\t\t\treturn { ok: false, error: errors.wrap(ErrTimeout, fetchResult.error.message) }\n\t\t\t}\n\t\t\tlog?.error(\"transport network error\", {\n\t\t\t\tstudentId: body.studentId,\n\t\t\t\terror: fetchResult.error\n\t\t\t})\n\t\t\treturn { ok: false, error: errors.wrap(ErrNetwork, fetchResult.error.message) }\n\t\t}\n\n\t\tconst res = fetchResult.response\n\n\t\tif (!res.ok) {\n\t\t\tconst text = await res.text().catch(function fallback() {\n\t\t\t\treturn \"\"\n\t\t\t})\n\t\t\tconst sentinel = httpSentinel(res.status)\n\t\t\tlog?.error(\"transport http error\", {\n\t\t\t\tstudentId: body.studentId,\n\t\t\t\tstatus: res.status\n\t\t\t})\n\t\t\treturn { ok: false, error: errors.wrap(sentinel, text) }\n\t\t}\n\n\t\tconst jsonResult = await res.json().then(\n\t\t\tfunction ok(data: WireResult<Pcis>) {\n\t\t\t\treturn { ok: true, data } as const\n\t\t\t},\n\t\t\tfunction fail(err: Error) {\n\t\t\t\treturn { ok: false, error: err } as const\n\t\t\t}\n\t\t)\n\n\t\tif (!jsonResult.ok) {\n\t\t\tlog?.error(\"transport json parse failed\", {\n\t\t\t\tstudentId: body.studentId,\n\t\t\t\terror: jsonResult.error\n\t\t\t})\n\t\t\treturn { ok: false, error: errors.wrap(ErrJsonParse, jsonResult.error.message) }\n\t\t}\n\n\t\tlog?.debug(\"transport success\", {\n\t\t\tstudentId: body.studentId,\n\t\t\tintentKind: body.intent.kind\n\t\t})\n\n\t\treturn { ok: true, data: jsonResult.data }\n\t}\n\n\tfunction makeSession(studentId: string) {\n\t\tfunction resolve(result: WireResult<Pcis>): PrimerState<Pcis> {\n\t\t\tswitch (result.outcome) {\n\t\t\t\tcase \"advanced\":\n\t\t\t\t\treturn fromAdvanced(result.stimulus, result.interaction)\n\t\t\t\tcase \"submitted\":\n\t\t\t\t\treturn feedbackState(\n\t\t\t\t\t\tresult.stimulus,\n\t\t\t\t\t\tresult.interaction,\n\t\t\t\t\t\tresult.submission,\n\t\t\t\t\t\tresult.isCorrect,\n\t\t\t\t\t\tresult.feedbackContent,\n\t\t\t\t\t\tresult.correctAnswer\n\t\t\t\t\t)\n\t\t\t\tcase \"completed\":\n\t\t\t\t\treturn { phase: \"completed\", toJSON: poisonToJSON }\n\t\t\t}\n\t\t}\n\n\t\tfunction errored(\n\t\t\terror: Error,\n\t\t\tfailedPhase: \"observation\" | \"interaction\",\n\t\t\tintent: WireIntent<Pcis>\n\t\t): ErroredState<Pcis> {\n\t\t\treturn {\n\t\t\t\tphase: \"errored\",\n\t\t\t\terror,\n\t\t\t\tfailedPhase,\n\t\t\t\tretry: function retry() {\n\t\t\t\t\tlog?.debug(\"retrying from errored state\", { failedPhase })\n\t\t\t\t\treturn execute(intent, failedPhase)\n\t\t\t\t},\n\t\t\t\ttoJSON: poisonToJSON\n\t\t\t}\n\t\t}\n\n\t\tasync function execute(\n\t\t\tintent: WireIntent<Pcis>,\n\t\t\tphase: \"observation\" | \"interaction\"\n\t\t): Promise<PrimerState<Pcis>> {\n\t\t\tconst body: WireRequestBody<Pcis> = {\n\t\t\t\tstudentId,\n\t\t\t\tsupportedPcis: config.supportedPcis,\n\t\t\t\tintent\n\t\t\t}\n\n\t\t\tconst result = await transport(body)\n\t\t\tif (!result.ok) {\n\t\t\t\treturn errored(result.error, phase, intent)\n\t\t\t}\n\n\t\t\treturn resolve(result.data)\n\t\t}\n\n\t\tfunction isPciSupported(pciId: string): boolean {\n\t\t\tfor (const id of config.supportedPcis) {\n\t\t\t\tif (id === pciId) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false\n\t\t}\n\n\t\tfunction fromAdvanced(\n\t\t\tstimulus: RendererStimulus | null,\n\t\t\tinteraction: RendererInteraction<Pcis> | null\n\t\t): PrimerState<Pcis> {\n\t\t\tif (interaction === null) {\n\t\t\t\treturn observationState(stimulus)\n\t\t\t}\n\t\t\tif (interaction.type === \"portable-custom\") {\n\t\t\t\tif (!isPciSupported(interaction.pciId)) {\n\t\t\t\t\tlog?.error(\"unsupported pci in frame\", { pciId: interaction.pciId })\n\t\t\t\t\treturn {\n\t\t\t\t\t\tphase: \"fatal\",\n\t\t\t\t\t\terror: errors.wrap(ErrUnsupportedPci, `pci '${interaction.pciId}'`),\n\t\t\t\t\t\ttoJSON: poisonToJSON\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn pendingInteractionState(stimulus, interaction)\n\t\t}\n\n\t\tfunction observationState(stimulus: RendererStimulus | null): PrimerState<Pcis> {\n\t\t\treturn {\n\t\t\t\tphase: \"observation\",\n\t\t\t\tstimulus,\n\t\t\t\tadvance: function advance() {\n\t\t\t\t\treturn execute({ kind: \"observation\" }, \"observation\")\n\t\t\t\t},\n\t\t\t\ttoJSON: poisonToJSON\n\t\t\t}\n\t\t}\n\n\t\tfunction pendingInteractionState(\n\t\t\tstimulus: RendererStimulus | null,\n\t\t\tinteraction: RendererInteraction<Pcis>\n\t\t): PrimerState<Pcis> {\n\t\t\tswitch (interaction.type) {\n\t\t\t\tcase \"choice\":\n\t\t\t\t\treturn choiceState(\n\t\t\t\t\t\tstimulus,\n\t\t\t\t\t\tinteraction,\n\t\t\t\t\t\tinteraction.options,\n\t\t\t\t\t\tinteraction.maxChoices,\n\t\t\t\t\t\tinteraction.minChoices\n\t\t\t\t\t)\n\t\t\t\tcase \"text-entry\":\n\t\t\t\t\treturn textEntryState(stimulus, interaction)\n\t\t\t\tcase \"extended-text\":\n\t\t\t\t\treturn extendedTextState(stimulus, interaction)\n\t\t\t\tcase \"portable-custom\":\n\t\t\t\t\treturn pciInteractionState(stimulus, interaction)\n\t\t\t}\n\t\t}\n\n\t\tfunction findUnknownKey(selectedKeys: string[], options: RendererChoice[]): string | null {\n\t\t\tconst optionIds = new Set(\n\t\t\t\toptions.map(function getId(o) {\n\t\t\t\t\treturn o.identifier\n\t\t\t\t})\n\t\t\t)\n\t\t\tfor (const key of selectedKeys) {\n\t\t\t\tif (!optionIds.has(key)) {\n\t\t\t\t\treturn key\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null\n\t\t}\n\n\t\tfunction validateChoiceSubmission(\n\t\t\tselectedKeys: string[],\n\t\t\toptions: RendererChoice[],\n\t\t\tmaxChoices: number,\n\t\t\tminChoices: number\n\t\t): Error | null {\n\t\t\tif (selectedKeys.length < minChoices) {\n\t\t\t\tlog?.error(\"choice submit below minChoices\", { selectedKeys, minChoices })\n\t\t\t\treturn errors.wrap(ErrInvalidSubmission, `need at least ${minChoices} selections`)\n\t\t\t}\n\t\t\tif (selectedKeys.length > maxChoices) {\n\t\t\t\tlog?.error(\"choice submit above maxChoices\", { selectedKeys, maxChoices })\n\t\t\t\treturn errors.wrap(ErrInvalidSubmission, `at most ${maxChoices} selections`)\n\t\t\t}\n\t\t\tconst uniqueKeys = new Set(selectedKeys)\n\t\t\tif (uniqueKeys.size !== selectedKeys.length) {\n\t\t\t\tlog?.error(\"choice submit has duplicates\", { selectedKeys })\n\t\t\t\treturn errors.wrap(ErrInvalidSubmission, \"duplicate selections\")\n\t\t\t}\n\t\t\tconst unknownKey = findUnknownKey(selectedKeys, options)\n\t\t\tif (unknownKey) {\n\t\t\t\tlog?.error(\"choice submit has unknown key\", { key: unknownKey })\n\t\t\t\treturn errors.wrap(ErrInvalidSubmission, `unknown option '${unknownKey}'`)\n\t\t\t}\n\t\t\treturn null\n\t\t}\n\n\t\tfunction choiceState(\n\t\t\tstimulus: RendererStimulus | null,\n\t\t\tinteraction: Extract<RendererInteraction<Pcis>, { type: \"choice\" }>,\n\t\t\toptions: RendererChoice[],\n\t\t\tmaxChoices: number,\n\t\t\tminChoices: number\n\t\t): PrimerState<Pcis> {\n\t\t\treturn {\n\t\t\t\tphase: \"interaction\",\n\t\t\t\tkind: \"choice\",\n\t\t\t\tstimulus,\n\t\t\t\tinteraction,\n\t\t\t\toptions,\n\t\t\t\tmaxChoices,\n\t\t\t\tminChoices,\n\t\t\t\tsubmitChoice: function submitChoice(selectedKeys: string[]) {\n\t\t\t\t\tconst validationError = validateChoiceSubmission(\n\t\t\t\t\t\tselectedKeys,\n\t\t\t\t\t\toptions,\n\t\t\t\t\t\tmaxChoices,\n\t\t\t\t\t\tminChoices\n\t\t\t\t\t)\n\t\t\t\t\tif (validationError) {\n\t\t\t\t\t\treturn Promise.resolve(\n\t\t\t\t\t\t\terrored(validationError, \"interaction\", {\n\t\t\t\t\t\t\t\tkind: \"interaction\",\n\t\t\t\t\t\t\t\tsubmission: { type: \"choice\", selectedKeys }\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\treturn execute(\n\t\t\t\t\t\t{ kind: \"interaction\", submission: { type: \"choice\", selectedKeys } },\n\t\t\t\t\t\t\"interaction\"\n\t\t\t\t\t)\n\t\t\t\t},\n\t\t\t\ttoJSON: poisonToJSON\n\t\t\t}\n\t\t}\n\n\t\tfunction textEntryState(\n\t\t\tstimulus: RendererStimulus | null,\n\t\t\tinteraction: Extract<RendererInteraction<Pcis>, { type: \"text-entry\" }>\n\t\t): PrimerState<Pcis> {\n\t\t\treturn {\n\t\t\t\tphase: \"interaction\",\n\t\t\t\tkind: \"text-entry\",\n\t\t\t\tstimulus,\n\t\t\t\tinteraction,\n\t\t\t\tsubmitText: function submitText(value: string) {\n\t\t\t\t\treturn execute(\n\t\t\t\t\t\t{ kind: \"interaction\", submission: { type: \"text-entry\", value } },\n\t\t\t\t\t\t\"interaction\"\n\t\t\t\t\t)\n\t\t\t\t},\n\t\t\t\ttoJSON: poisonToJSON\n\t\t\t}\n\t\t}\n\n\t\tfunction validateExtendedTextSubmission(\n\t\t\tvalues: string[],\n\t\t\tminStrings: number,\n\t\t\tmaxStrings: number\n\t\t): Error | null {\n\t\t\tif (values.length < minStrings) {\n\t\t\t\tlog?.error(\"extended-text submit below minStrings\", { count: values.length, minStrings })\n\t\t\t\treturn errors.wrap(ErrInvalidSubmission, `need at least ${minStrings} values`)\n\t\t\t}\n\t\t\tif (values.length > maxStrings) {\n\t\t\t\tlog?.error(\"extended-text submit above maxStrings\", { count: values.length, maxStrings })\n\t\t\t\treturn errors.wrap(ErrInvalidSubmission, `at most ${maxStrings} values`)\n\t\t\t}\n\t\t\treturn null\n\t\t}\n\n\t\tfunction extendedTextState(\n\t\t\tstimulus: RendererStimulus | null,\n\t\t\tinteraction: RendererInteraction<Pcis> & { type: \"extended-text\" }\n\t\t): PrimerState<Pcis> {\n\t\t\tconst maxStrings = interaction.maxStrings\n\t\t\tconst minStrings = interaction.minStrings\n\t\t\tif (maxStrings === 1) {\n\t\t\t\treturn {\n\t\t\t\t\tphase: \"interaction\",\n\t\t\t\t\tkind: \"extended-text\",\n\t\t\t\t\tcardinality: \"single\",\n\t\t\t\t\tstimulus,\n\t\t\t\t\tinteraction,\n\t\t\t\t\tsubmitText: function submitText(value: string) {\n\t\t\t\t\t\treturn execute(\n\t\t\t\t\t\t\t{ kind: \"interaction\", submission: { type: \"extended-text\", values: [value] } },\n\t\t\t\t\t\t\t\"interaction\"\n\t\t\t\t\t\t)\n\t\t\t\t\t},\n\t\t\t\t\ttoJSON: poisonToJSON\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tphase: \"interaction\",\n\t\t\t\tkind: \"extended-text\",\n\t\t\t\tcardinality: \"multiple\",\n\t\t\t\tstimulus,\n\t\t\t\tinteraction,\n\t\t\t\tmaxStrings,\n\t\t\t\tminStrings,\n\t\t\t\tsubmitTexts: function submitTexts(values: string[]) {\n\t\t\t\t\tconst validationError = validateExtendedTextSubmission(values, minStrings, maxStrings)\n\t\t\t\t\tif (validationError) {\n\t\t\t\t\t\treturn Promise.resolve(\n\t\t\t\t\t\t\terrored(validationError, \"interaction\", {\n\t\t\t\t\t\t\t\tkind: \"interaction\",\n\t\t\t\t\t\t\t\tsubmission: { type: \"extended-text\", values }\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\treturn execute(\n\t\t\t\t\t\t{ kind: \"interaction\", submission: { type: \"extended-text\", values } },\n\t\t\t\t\t\t\"interaction\"\n\t\t\t\t\t)\n\t\t\t\t},\n\t\t\t\ttoJSON: poisonToJSON\n\t\t\t}\n\t\t}\n\n\t\t// Safe cast: TS cannot verify mapped-union construction from generic Pcis.\n\t\t// The input `interaction: PciInteraction<Pcis>` guarantees pciId↔properties\n\t\t// correlation; destructuring loses TS tracking but runtime is correct.\n\t\tfunction pciInteractionState(\n\t\t\tstimulus: RendererStimulus | null,\n\t\t\tinteraction: PciInteraction<Pcis>\n\t\t): PciInteractionState<Pcis> {\n\t\t\tconst { pciId, properties } = interaction\n\t\t\tfunction submit(value: PciValue<typeof pciId>): Promise<PrimerState<Pcis>> {\n\t\t\t\tlog?.debug(\"pci submit\", { pciId })\n\t\t\t\tconst submission = { type: \"portable-custom\" as const, pciId, value }\n\t\t\t\treturn execute({ kind: \"interaction\", submission }, \"interaction\")\n\t\t\t}\n\t\t\t// biome-ignore lint/plugin: TS cannot verify mapped-union construction from generic Pcis — pciId↔properties correlation preserved by input\n\t\t\treturn {\n\t\t\t\tphase: \"interaction\",\n\t\t\t\tkind: \"portable-custom\",\n\t\t\t\tstimulus,\n\t\t\t\tinteraction,\n\t\t\t\tpciId,\n\t\t\t\tproperties,\n\t\t\t\tsubmit,\n\t\t\t\ttoJSON: poisonToJSON\n\t\t\t} as PciInteractionState<Pcis>\n\t\t}\n\n\t\tfunction feedbackState(\n\t\t\tstimulus: RendererStimulus | null,\n\t\t\tinteraction: RendererInteraction<Pcis>,\n\t\t\tsubmission: RendererSubmission<Pcis>,\n\t\t\tisCorrect: boolean,\n\t\t\tfeedbackContent: ContentInline[],\n\t\t\tcorrectAnswer: RendererCorrectAnswer | null\n\t\t): PrimerState<Pcis> {\n\t\t\treturn {\n\t\t\t\tphase: \"feedback\",\n\t\t\t\tstimulus,\n\t\t\t\tinteraction,\n\t\t\t\tsubmission,\n\t\t\t\tisCorrect,\n\t\t\t\tfeedbackContent,\n\t\t\t\tcorrectAnswer,\n\t\t\t\tadvance: function advance() {\n\t\t\t\t\treturn execute({ kind: \"observation\" }, \"observation\")\n\t\t\t\t},\n\t\t\t\ttoJSON: poisonToJSON\n\t\t\t}\n\t\t}\n\n\t\treturn { execute }\n\t}\n\n\treturn {\n\t\tasync start(studentId: string): Promise<PrimerState<Pcis>> {\n\t\t\tlog?.debug(\"start\", { studentId })\n\t\t\tconst s = makeSession(studentId)\n\t\t\treturn s.execute({ kind: \"observation\" }, \"observation\")\n\t\t}\n\t}\n}\n\nexport { ADVANCE_PATH, DEFAULT_ORIGIN, create }\nexport type { Client, Config, PrimerLogger }\n"
7
+ ],
8
+ "mappings": "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",
9
+ "debugId": "B2799CD48726B75C64756E2164756E21",
10
+ "names": []
11
+ }
@@ -0,0 +1,16 @@
1
+ type ContentInline = {
2
+ type: "text";
3
+ value: string;
4
+ } | {
5
+ type: "italic";
6
+ children: ContentInline[];
7
+ };
8
+ type ContentBlock = {
9
+ type: "paragraph";
10
+ children: ContentInline[];
11
+ };
12
+ declare function inlinesToPlainText(nodes: ContentInline[]): string;
13
+ declare function blocksToPlainText(blocks: ContentBlock[]): string;
14
+ export type { ContentBlock, ContentInline };
15
+ export { blocksToPlainText, inlinesToPlainText };
16
+ //# sourceMappingURL=content.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../src/content.ts"],"names":[],"mappings":"AAAA,KAAK,aAAa,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,EAAE,aAAa,EAAE,CAAA;CAAE,CAAA;AAEpG,KAAK,YAAY,GAAG;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE,aAAa,EAAE,CAAA;CAAE,CAAA;AAEpE,iBAAS,kBAAkB,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,MAAM,CAa1D;AAED,iBAAS,iBAAiB,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,CAMzD;AAED,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,CAAA;AAC3C,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,CAAA"}
@@ -0,0 +1,27 @@
1
+ // src/content.ts
2
+ function inlinesToPlainText(nodes) {
3
+ const parts = [];
4
+ for (const node of nodes) {
5
+ switch (node.type) {
6
+ case "text":
7
+ parts.push(node.value);
8
+ break;
9
+ case "italic":
10
+ parts.push(inlinesToPlainText(node.children));
11
+ break;
12
+ }
13
+ }
14
+ return parts.join("");
15
+ }
16
+ function blocksToPlainText(blocks) {
17
+ return blocks.map(function blockText(block) {
18
+ return inlinesToPlainText(block.children);
19
+ }).join(`
20
+ `);
21
+ }
22
+ export {
23
+ inlinesToPlainText,
24
+ blocksToPlainText
25
+ };
26
+
27
+ //# debugId=B2FEC3DAAEEB30A564756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/content.ts"],
4
+ "sourcesContent": [
5
+ "type ContentInline = { type: \"text\"; value: string } | { type: \"italic\"; children: ContentInline[] }\n\ntype ContentBlock = { type: \"paragraph\"; children: ContentInline[] }\n\nfunction inlinesToPlainText(nodes: ContentInline[]): string {\n\tconst parts: string[] = []\n\tfor (const node of nodes) {\n\t\tswitch (node.type) {\n\t\t\tcase \"text\":\n\t\t\t\tparts.push(node.value)\n\t\t\t\tbreak\n\t\t\tcase \"italic\":\n\t\t\t\tparts.push(inlinesToPlainText(node.children))\n\t\t\t\tbreak\n\t\t}\n\t}\n\treturn parts.join(\"\")\n}\n\nfunction blocksToPlainText(blocks: ContentBlock[]): string {\n\treturn blocks\n\t\t.map(function blockText(block) {\n\t\t\treturn inlinesToPlainText(block.children)\n\t\t})\n\t\t.join(\"\\n\")\n}\n\nexport type { ContentBlock, ContentInline }\nexport { blocksToPlainText, inlinesToPlainText }\n"
6
+ ],
7
+ "mappings": ";AAIA,SAAS,kBAAkB,CAAC,OAAgC;AAAA,EAC3D,MAAM,QAAkB,CAAC;AAAA,EACzB,WAAW,QAAQ,OAAO;AAAA,IACzB,QAAQ,KAAK;AAAA,WACP;AAAA,QACJ,MAAM,KAAK,KAAK,KAAK;AAAA,QACrB;AAAA,WACI;AAAA,QACJ,MAAM,KAAK,mBAAmB,KAAK,QAAQ,CAAC;AAAA,QAC5C;AAAA;AAAA,EAEH;AAAA,EACA,OAAO,MAAM,KAAK,EAAE;AAAA;AAGrB,SAAS,iBAAiB,CAAC,QAAgC;AAAA,EAC1D,OAAO,OACL,IAAI,SAAS,SAAS,CAAC,OAAO;AAAA,IAC9B,OAAO,mBAAmB,MAAM,QAAQ;AAAA,GACxC,EACA,KAAK;AAAA,CAAI;AAAA;",
8
+ "debugId": "B2FEC3DAAEEB30A564756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,17 @@
1
+ declare const ErrNetwork: Error;
2
+ declare const ErrJsonParse: Error;
3
+ declare const ErrUnsupportedPci: Error;
4
+ declare const ErrInvalidPublishableKey: Error;
5
+ declare const ErrBadRequest: Error;
6
+ declare const ErrServerError: Error;
7
+ declare const ErrTimeout: Error;
8
+ declare const ErrForbidden: Error;
9
+ declare const ErrNotFound: Error;
10
+ declare const ErrConflict: Error;
11
+ declare const ErrRateLimited: Error;
12
+ declare const ErrServiceUnavailable: Error;
13
+ declare const ErrNotSerializable: Error;
14
+ declare const ErrInvalidSubmission: Error;
15
+ declare const ErrMalformedPublishableKey: Error;
16
+ export { ErrBadRequest, ErrConflict, ErrForbidden, ErrInvalidPublishableKey, ErrInvalidSubmission, ErrMalformedPublishableKey, ErrJsonParse, ErrNetwork, ErrNotFound, ErrNotSerializable, ErrRateLimited, ErrServerError, ErrServiceUnavailable, ErrTimeout, ErrUnsupportedPci };
17
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAEA,QAAA,MAAM,UAAU,OAAwB,CAAA;AACxC,QAAA,MAAM,YAAY,OAA2B,CAAA;AAC7C,QAAA,MAAM,iBAAiB,OAAgC,CAAA;AACvD,QAAA,MAAM,wBAAwB,OAAwC,CAAA;AACtE,QAAA,MAAM,aAAa,OAA4B,CAAA;AAC/C,QAAA,MAAM,cAAc,OAA6B,CAAA;AACjD,QAAA,MAAM,UAAU,OAAwB,CAAA;AACxC,QAAA,MAAM,YAAY,OAA0B,CAAA;AAC5C,QAAA,MAAM,WAAW,OAA0B,CAAA;AAC3C,QAAA,MAAM,WAAW,OAAyB,CAAA;AAC1C,QAAA,MAAM,cAAc,OAA6B,CAAA;AACjD,QAAA,MAAM,qBAAqB,OAAoC,CAAA;AAC/D,QAAA,MAAM,kBAAkB,OAEvB,CAAA;AACD,QAAA,MAAM,oBAAoB,OAAmC,CAAA;AAC7D,QAAA,MAAM,0BAA0B,OAA0C,CAAA;AAE1E,OAAO,EACN,aAAa,EACb,WAAW,EACX,YAAY,EACZ,wBAAwB,EACxB,oBAAoB,EACpB,0BAA0B,EAC1B,YAAY,EACZ,UAAU,EACV,WAAW,EACX,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,qBAAqB,EACrB,UAAU,EACV,iBAAiB,EACjB,CAAA"}
package/dist/errors.js ADDED
@@ -0,0 +1,36 @@
1
+ // src/errors.ts
2
+ import * as errors from "@superbuilders/errors";
3
+ var ErrNetwork = errors.new("network");
4
+ var ErrJsonParse = errors.new("json parse");
5
+ var ErrUnsupportedPci = errors.new("unsupported pci");
6
+ var ErrInvalidPublishableKey = errors.new("invalid publishable key");
7
+ var ErrBadRequest = errors.new("bad request");
8
+ var ErrServerError = errors.new("server error");
9
+ var ErrTimeout = errors.new("timeout");
10
+ var ErrForbidden = errors.new("forbidden");
11
+ var ErrNotFound = errors.new("not found");
12
+ var ErrConflict = errors.new("conflict");
13
+ var ErrRateLimited = errors.new("rate limited");
14
+ var ErrServiceUnavailable = errors.new("service unavailable");
15
+ var ErrNotSerializable = errors.new("PrimerState is live in-memory state and must not be serialized or stored");
16
+ var ErrInvalidSubmission = errors.new("invalid submission");
17
+ var ErrMalformedPublishableKey = errors.new("malformed publishable key");
18
+ export {
19
+ ErrUnsupportedPci,
20
+ ErrTimeout,
21
+ ErrServiceUnavailable,
22
+ ErrServerError,
23
+ ErrRateLimited,
24
+ ErrNotSerializable,
25
+ ErrNotFound,
26
+ ErrNetwork,
27
+ ErrMalformedPublishableKey,
28
+ ErrJsonParse,
29
+ ErrInvalidSubmission,
30
+ ErrInvalidPublishableKey,
31
+ ErrForbidden,
32
+ ErrConflict,
33
+ ErrBadRequest
34
+ };
35
+
36
+ //# debugId=DC88F476D6E45CE664756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/errors.ts"],
4
+ "sourcesContent": [
5
+ "import * as errors from \"@superbuilders/errors\"\n\nconst ErrNetwork = errors.new(\"network\")\nconst ErrJsonParse = errors.new(\"json parse\")\nconst ErrUnsupportedPci = errors.new(\"unsupported pci\")\nconst ErrInvalidPublishableKey = errors.new(\"invalid publishable key\")\nconst ErrBadRequest = errors.new(\"bad request\")\nconst ErrServerError = errors.new(\"server error\")\nconst ErrTimeout = errors.new(\"timeout\")\nconst ErrForbidden = errors.new(\"forbidden\")\nconst ErrNotFound = errors.new(\"not found\")\nconst ErrConflict = errors.new(\"conflict\")\nconst ErrRateLimited = errors.new(\"rate limited\")\nconst ErrServiceUnavailable = errors.new(\"service unavailable\")\nconst ErrNotSerializable = errors.new(\n\t\"PrimerState is live in-memory state and must not be serialized or stored\"\n)\nconst ErrInvalidSubmission = errors.new(\"invalid submission\")\nconst ErrMalformedPublishableKey = errors.new(\"malformed publishable key\")\n\nexport {\n\tErrBadRequest,\n\tErrConflict,\n\tErrForbidden,\n\tErrInvalidPublishableKey,\n\tErrInvalidSubmission,\n\tErrMalformedPublishableKey,\n\tErrJsonParse,\n\tErrNetwork,\n\tErrNotFound,\n\tErrNotSerializable,\n\tErrRateLimited,\n\tErrServerError,\n\tErrServiceUnavailable,\n\tErrTimeout,\n\tErrUnsupportedPci\n}\n"
6
+ ],
7
+ "mappings": ";AAAA;AAEA,IAAM,aAAoB,WAAI,SAAS;AACvC,IAAM,eAAsB,WAAI,YAAY;AAC5C,IAAM,oBAA2B,WAAI,iBAAiB;AACtD,IAAM,2BAAkC,WAAI,yBAAyB;AACrE,IAAM,gBAAuB,WAAI,aAAa;AAC9C,IAAM,iBAAwB,WAAI,cAAc;AAChD,IAAM,aAAoB,WAAI,SAAS;AACvC,IAAM,eAAsB,WAAI,WAAW;AAC3C,IAAM,cAAqB,WAAI,WAAW;AAC1C,IAAM,cAAqB,WAAI,UAAU;AACzC,IAAM,iBAAwB,WAAI,cAAc;AAChD,IAAM,wBAA+B,WAAI,qBAAqB;AAC9D,IAAM,qBAA4B,WACjC,0EACD;AACA,IAAM,uBAA8B,WAAI,oBAAoB;AAC5D,IAAM,6BAAoC,WAAI,2BAA2B;",
8
+ "debugId": "DC88F476D6E45CE664756E2164756E21",
9
+ "names": []
10
+ }
package/dist/pci.d.ts ADDED
@@ -0,0 +1,38 @@
1
+ interface DivisionRemainderProps {
2
+ dividend: number;
3
+ divisor: number;
4
+ }
5
+ interface DivisionRemainderSubmission {
6
+ quotient: string;
7
+ remainder: string;
8
+ }
9
+ interface FractionAdditionProps {
10
+ left: {
11
+ numerator: number;
12
+ denominator: number;
13
+ };
14
+ right: {
15
+ numerator: number;
16
+ denominator: number;
17
+ };
18
+ }
19
+ interface FractionAdditionSubmission {
20
+ numerator: string;
21
+ denominator: string;
22
+ }
23
+ type PciUrn = `urn:primer:pci:${string}`;
24
+ interface PciRegistry {
25
+ "urn:primer:pci:division-remainder": {
26
+ props: DivisionRemainderProps;
27
+ value: DivisionRemainderSubmission;
28
+ };
29
+ "urn:primer:pci:fraction-addition": {
30
+ props: FractionAdditionProps;
31
+ value: FractionAdditionSubmission;
32
+ };
33
+ }
34
+ type PciId = keyof PciRegistry & string;
35
+ type PciProps<K extends PciId> = PciRegistry[K]["props"];
36
+ type PciValue<K extends PciId> = PciRegistry[K]["value"];
37
+ export type { DivisionRemainderProps, DivisionRemainderSubmission, FractionAdditionProps, FractionAdditionSubmission, PciId, PciProps, PciRegistry, PciUrn, PciValue };
38
+ //# sourceMappingURL=pci.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pci.d.ts","sourceRoot":"","sources":["../src/pci.ts"],"names":[],"mappings":"AAAA,UAAU,sBAAsB;IAC/B,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;CACf;AAED,UAAU,2BAA2B;IACpC,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;CACjB;AAED,UAAU,qBAAqB;IAC9B,IAAI,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAA;IAChD,KAAK,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAA;CACjD;AAED,UAAU,0BAA0B;IACnC,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;CACnB;AAED,KAAK,MAAM,GAAG,kBAAkB,MAAM,EAAE,CAAA;AAExC,UAAU,WAAW;IACpB,mCAAmC,EAAE;QACpC,KAAK,EAAE,sBAAsB,CAAA;QAC7B,KAAK,EAAE,2BAA2B,CAAA;KAClC,CAAA;IACD,kCAAkC,EAAE;QACnC,KAAK,EAAE,qBAAqB,CAAA;QAC5B,KAAK,EAAE,0BAA0B,CAAA;KACjC,CAAA;CACD;AAMD,KAAK,KAAK,GAAG,MAAM,WAAW,GAAG,MAAM,CAAA;AACvC,KAAK,QAAQ,CAAC,CAAC,SAAS,KAAK,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;AACxD,KAAK,QAAQ,CAAC,CAAC,SAAS,KAAK,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;AAExD,YAAY,EACX,sBAAsB,EACtB,2BAA2B,EAC3B,qBAAqB,EACrB,0BAA0B,EAC1B,KAAK,EACL,QAAQ,EACR,WAAW,EACX,MAAM,EACN,QAAQ,EACR,CAAA"}
package/dist/pci.js ADDED
@@ -0,0 +1,2 @@
1
+
2
+ //# debugId=A38864BFD31DF2F664756E2164756E21
@@ -0,0 +1,9 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [
5
+ ],
6
+ "mappings": "",
7
+ "debugId": "A38864BFD31DF2F664756E2164756E21",
8
+ "names": []
9
+ }
@@ -0,0 +1,188 @@
1
+ import type { ContentBlock, ContentInline } from "./content";
2
+ import type { PciId, PciProps, PciValue } from "./pci";
3
+ type RendererCorrectScalarValue = {
4
+ kind: "identifier";
5
+ value: string;
6
+ } | {
7
+ kind: "string";
8
+ value: string;
9
+ } | {
10
+ kind: "integer";
11
+ value: number;
12
+ } | {
13
+ kind: "float";
14
+ value: number;
15
+ };
16
+ type RendererCorrectAnswer = {
17
+ kind: "single";
18
+ value: RendererCorrectScalarValue | null;
19
+ } | {
20
+ kind: "multiple";
21
+ values: RendererCorrectScalarValue[];
22
+ } | {
23
+ kind: "record";
24
+ fields: Array<{
25
+ fieldIdentifier: string;
26
+ baseType: "identifier" | "string" | "integer" | "float";
27
+ value: RendererCorrectScalarValue | null;
28
+ }>;
29
+ };
30
+ interface NonSerializable {
31
+ toJSON(): never;
32
+ }
33
+ interface BodyStimulus {
34
+ type: "body";
35
+ body: ContentBlock[];
36
+ }
37
+ interface ImageStimulus {
38
+ type: "image";
39
+ description: ContentInline[];
40
+ src: string;
41
+ }
42
+ type RendererStimulus = BodyStimulus | ImageStimulus;
43
+ interface RendererChoice {
44
+ identifier: string;
45
+ content: ContentInline[];
46
+ }
47
+ type PciInteraction<Pcis extends PciId = PciId> = {
48
+ [K in Pcis]: {
49
+ type: "portable-custom";
50
+ prompt: ContentInline[];
51
+ pciId: K;
52
+ properties: PciProps<K>;
53
+ };
54
+ }[Pcis];
55
+ type RendererInteraction<Pcis extends PciId = PciId> = {
56
+ type: "choice";
57
+ prompt: ContentInline[];
58
+ options: RendererChoice[];
59
+ shuffle: boolean;
60
+ maxChoices: number;
61
+ minChoices: number;
62
+ } | {
63
+ type: "text-entry";
64
+ prompt: ContentInline[];
65
+ base: 10;
66
+ expectedLength?: number;
67
+ patternMask?: string;
68
+ placeholderText?: string;
69
+ } | {
70
+ type: "extended-text";
71
+ prompt: ContentInline[];
72
+ format: "plain";
73
+ expectedLines?: number;
74
+ expectedLength?: number;
75
+ patternMask?: string;
76
+ placeholderText?: string;
77
+ maxStrings: number;
78
+ minStrings: number;
79
+ } | PciInteraction<Pcis>;
80
+ type PciSubmission<Pcis extends PciId = PciId> = {
81
+ [K in Pcis]: {
82
+ type: "portable-custom";
83
+ pciId: K;
84
+ value: PciValue<K>;
85
+ };
86
+ }[Pcis];
87
+ type RendererSubmission<Pcis extends PciId = PciId> = {
88
+ type: "choice";
89
+ selectedKeys: string[];
90
+ } | {
91
+ type: "text-entry";
92
+ value: string;
93
+ } | {
94
+ type: "extended-text";
95
+ values: string[];
96
+ } | PciSubmission<Pcis>;
97
+ interface ObservationState<Pcis extends PciId = PciId> extends NonSerializable {
98
+ readonly phase: "observation";
99
+ readonly stimulus: RendererStimulus | null;
100
+ advance(): Promise<PrimerState<Pcis>>;
101
+ }
102
+ interface ChoiceState<Pcis extends PciId = PciId> extends NonSerializable {
103
+ readonly phase: "interaction";
104
+ readonly kind: "choice";
105
+ readonly stimulus: RendererStimulus | null;
106
+ readonly interaction: Extract<RendererInteraction<Pcis>, {
107
+ type: "choice";
108
+ }>;
109
+ readonly options: RendererChoice[];
110
+ readonly maxChoices: number;
111
+ readonly minChoices: number;
112
+ submitChoice(selectedKeys: string[]): Promise<PrimerState<Pcis>>;
113
+ }
114
+ interface TextEntryState<Pcis extends PciId = PciId> extends NonSerializable {
115
+ readonly phase: "interaction";
116
+ readonly kind: "text-entry";
117
+ readonly stimulus: RendererStimulus | null;
118
+ readonly interaction: Extract<RendererInteraction<Pcis>, {
119
+ type: "text-entry";
120
+ }>;
121
+ submitText(value: string): Promise<PrimerState<Pcis>>;
122
+ }
123
+ interface ExtendedTextSingleState<Pcis extends PciId = PciId> extends NonSerializable {
124
+ readonly phase: "interaction";
125
+ readonly kind: "extended-text";
126
+ readonly cardinality: "single";
127
+ readonly stimulus: RendererStimulus | null;
128
+ readonly interaction: Extract<RendererInteraction<Pcis>, {
129
+ type: "extended-text";
130
+ }>;
131
+ submitText(value: string): Promise<PrimerState<Pcis>>;
132
+ }
133
+ interface ExtendedTextMultipleState<Pcis extends PciId = PciId> extends NonSerializable {
134
+ readonly phase: "interaction";
135
+ readonly kind: "extended-text";
136
+ readonly cardinality: "multiple";
137
+ readonly stimulus: RendererStimulus | null;
138
+ readonly interaction: Extract<RendererInteraction<Pcis>, {
139
+ type: "extended-text";
140
+ }>;
141
+ readonly maxStrings: number;
142
+ readonly minStrings: number;
143
+ submitTexts(values: string[]): Promise<PrimerState<Pcis>>;
144
+ }
145
+ type ExtendedTextState<Pcis extends PciId = PciId> = ExtendedTextSingleState<Pcis> | ExtendedTextMultipleState<Pcis>;
146
+ type PciInteractionState<Pcis extends PciId = PciId> = {
147
+ [K in Pcis]: NonSerializable & {
148
+ readonly phase: "interaction";
149
+ readonly kind: "portable-custom";
150
+ readonly stimulus: RendererStimulus | null;
151
+ readonly interaction: PciInteraction<K>;
152
+ readonly pciId: K;
153
+ readonly properties: PciProps<K>;
154
+ submit(value: PciValue<K>): Promise<PrimerState<Pcis>>;
155
+ };
156
+ }[Pcis];
157
+ type InteractionState<Pcis extends PciId = PciId> = ChoiceState<Pcis> | TextEntryState<Pcis> | ExtendedTextState<Pcis> | PciInteractionState<Pcis>;
158
+ interface FeedbackState<Pcis extends PciId = PciId> extends NonSerializable {
159
+ readonly phase: "feedback";
160
+ readonly stimulus: RendererStimulus | null;
161
+ readonly interaction: RendererInteraction<Pcis>;
162
+ readonly submission: RendererSubmission<Pcis>;
163
+ readonly isCorrect: boolean;
164
+ readonly feedbackContent: ContentInline[];
165
+ readonly correctAnswer: RendererCorrectAnswer | null;
166
+ advance(): Promise<PrimerState<Pcis>>;
167
+ }
168
+ interface CompletedState extends NonSerializable {
169
+ readonly phase: "completed";
170
+ }
171
+ interface ErroredState<Pcis extends PciId = PciId> extends NonSerializable {
172
+ readonly phase: "errored";
173
+ readonly error: Error;
174
+ readonly failedPhase: "observation" | "interaction";
175
+ retry(): Promise<PrimerState<Pcis>>;
176
+ }
177
+ interface FatalState extends NonSerializable {
178
+ readonly phase: "fatal";
179
+ readonly error: Error;
180
+ }
181
+ type PrimerState<Pcis extends PciId = PciId> = ObservationState<Pcis> | InteractionState<Pcis> | FeedbackState<Pcis> | CompletedState | ErroredState<Pcis> | FatalState;
182
+ interface PciRenderProps<K extends PciId> {
183
+ properties: PciProps<K>;
184
+ submitted: boolean;
185
+ onValueChange: (value: PciValue<K> | null) => void;
186
+ }
187
+ export type { BodyStimulus, ChoiceState, CompletedState, ErroredState, ExtendedTextMultipleState, ExtendedTextSingleState, ExtendedTextState, FatalState, FeedbackState, ImageStimulus, InteractionState, NonSerializable, ObservationState, PciInteraction, PciInteractionState, PciRenderProps, PciSubmission, PrimerState, RendererChoice, RendererCorrectAnswer, RendererCorrectScalarValue, RendererInteraction, RendererStimulus, RendererSubmission, TextEntryState };
188
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+
2
+ //# debugId=9FCB033A2878230664756E2164756E21
@@ -0,0 +1,9 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [
5
+ ],
6
+ "mappings": "",
7
+ "debugId": "9FCB033A2878230664756E2164756E21",
8
+ "names": []
9
+ }
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@superbuilders/primer-tives",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "exports": {
6
+ "./client": {
7
+ "types": "./dist/client.d.ts",
8
+ "import": "./dist/client.js"
9
+ },
10
+ "./types": {
11
+ "types": "./dist/types.d.ts",
12
+ "import": "./dist/types.js"
13
+ },
14
+ "./content": {
15
+ "types": "./dist/content.d.ts",
16
+ "import": "./dist/content.js"
17
+ },
18
+ "./errors": {
19
+ "types": "./dist/errors.d.ts",
20
+ "import": "./dist/errors.js"
21
+ },
22
+ "./pci": {
23
+ "types": "./dist/pci.d.ts",
24
+ "import": "./dist/pci.js"
25
+ }
26
+ },
27
+ "files": [
28
+ "dist"
29
+ ],
30
+ "dependencies": {
31
+ "@superbuilders/errors": "^3.0.2"
32
+ },
33
+ "scripts": {
34
+ "build": "bun run build.ts",
35
+ "typecheck": "bun --bun tsc --noEmit"
36
+ },
37
+ "devDependencies": {
38
+ "resolve-tspaths": "^0.8.23"
39
+ }
40
+ }