@superbuilders/primer-tives 1.1.4 → 2.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 +20 -11
- package/dist/client/choice-state.d.ts +2 -1
- package/dist/client/choice-state.d.ts.map +1 -1
- package/dist/client/create.d.ts +1 -1
- package/dist/client/create.d.ts.map +1 -1
- package/dist/client/extended-text-state.d.ts +2 -1
- package/dist/client/extended-text-state.d.ts.map +1 -1
- package/dist/client/feedback-state.d.ts +2 -2
- package/dist/client/feedback-state.d.ts.map +1 -1
- package/dist/client/index.js +145 -61
- package/dist/client/index.js.map +16 -15
- package/dist/client/match-state.d.ts +2 -1
- package/dist/client/match-state.d.ts.map +1 -1
- package/dist/client/observation-state.d.ts +2 -1
- package/dist/client/observation-state.d.ts.map +1 -1
- package/dist/client/order-state.d.ts +2 -1
- package/dist/client/order-state.d.ts.map +1 -1
- package/dist/client/pci-state.d.ts +2 -1
- package/dist/client/pci-state.d.ts.map +1 -1
- package/dist/client/session-context.d.ts +1 -1
- package/dist/client/session-context.d.ts.map +1 -1
- package/dist/client/session.d.ts +1 -1
- package/dist/client/session.d.ts.map +1 -1
- package/dist/client/text-entry-state.d.ts +2 -1
- package/dist/client/text-entry-state.d.ts.map +1 -1
- package/dist/client/transport.d.ts +9 -6
- package/dist/client/transport.d.ts.map +1 -1
- package/dist/client/types.d.ts +10 -1
- package/dist/client/types.d.ts.map +1 -1
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/index.d.ts.map +1 -1
- package/dist/contracts/types.d.ts +5 -9
- package/dist/contracts/types.d.ts.map +1 -1
- package/dist/errors.d.ts +3 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +5 -1
- package/dist/errors.js.map +3 -3
- package/dist/server/create-server.d.ts +6 -4
- package/dist/server/create-server.d.ts.map +1 -1
- package/dist/server/index.js +3 -1
- package/dist/server/index.js.map +4 -4
- package/dist/version.d.ts +4 -0
- package/dist/version.d.ts.map +1 -0
- package/package.json +1 -1
package/dist/client/index.js
CHANGED
|
@@ -21,6 +21,8 @@ var ErrInvalidSecretKey = errors.new("invalid secret key");
|
|
|
21
21
|
var ErrStudentNotFound = errors.new("student not found");
|
|
22
22
|
var ErrUnsupportedGrade = errors.new("unsupported grade");
|
|
23
23
|
var ErrTimebackUnavailable = errors.new("timeback unavailable");
|
|
24
|
+
var ErrNeedsHints = errors.new("student needs hints set before /advance");
|
|
25
|
+
var ErrSdkUpgradeRequired = errors.new("sdk upgrade required");
|
|
24
26
|
// src/contracts/content.ts
|
|
25
27
|
function inlinesToPlainText(nodes) {
|
|
26
28
|
const parts = [];
|
|
@@ -335,18 +337,22 @@ import * as errors10 from "@superbuilders/errors";
|
|
|
335
337
|
|
|
336
338
|
// src/client/transport.ts
|
|
337
339
|
import * as errors2 from "@superbuilders/errors";
|
|
340
|
+
|
|
341
|
+
// src/version.ts
|
|
342
|
+
var SDK_VERSION = "2.0.0";
|
|
343
|
+
var NPM_PACKAGE_URL = "https://www.npmjs.com/package/@superbuilders/primer-tives";
|
|
344
|
+
|
|
345
|
+
// src/client/transport.ts
|
|
338
346
|
var ADVANCE_PATH = "/api/v0/advance";
|
|
339
|
-
function
|
|
340
|
-
if (
|
|
341
|
-
return
|
|
342
|
-
}
|
|
343
|
-
if ("error" in value && value.error !== undefined && typeof value.error !== "string") {
|
|
344
|
-
return false;
|
|
347
|
+
function readStringField(value, key) {
|
|
348
|
+
if (!(key in value)) {
|
|
349
|
+
return;
|
|
345
350
|
}
|
|
346
|
-
|
|
347
|
-
|
|
351
|
+
const v = Reflect.get(value, key);
|
|
352
|
+
if (typeof v !== "string") {
|
|
353
|
+
return;
|
|
348
354
|
}
|
|
349
|
-
return
|
|
355
|
+
return v;
|
|
350
356
|
}
|
|
351
357
|
function parseAdvanceErrorBody(body) {
|
|
352
358
|
if (body.length === 0) {
|
|
@@ -358,13 +364,39 @@ function parseAdvanceErrorBody(body) {
|
|
|
358
364
|
if (parsed.error) {
|
|
359
365
|
return null;
|
|
360
366
|
}
|
|
361
|
-
|
|
367
|
+
const raw = parsed.data;
|
|
368
|
+
if (typeof raw !== "object" || raw === null) {
|
|
362
369
|
return null;
|
|
363
370
|
}
|
|
364
|
-
|
|
371
|
+
const result = {};
|
|
372
|
+
const error = readStringField(raw, "error");
|
|
373
|
+
if (error !== undefined) {
|
|
374
|
+
result.error = error;
|
|
375
|
+
}
|
|
376
|
+
const detail = readStringField(raw, "detail");
|
|
377
|
+
if (detail !== undefined) {
|
|
378
|
+
result.detail = detail;
|
|
379
|
+
}
|
|
380
|
+
const minimumSdkVersion = readStringField(raw, "minimumSdkVersion");
|
|
381
|
+
if (minimumSdkVersion !== undefined) {
|
|
382
|
+
result.minimumSdkVersion = minimumSdkVersion;
|
|
383
|
+
}
|
|
384
|
+
const receivedSdkVersion = readStringField(raw, "receivedSdkVersion");
|
|
385
|
+
if (receivedSdkVersion !== undefined) {
|
|
386
|
+
result.receivedSdkVersion = receivedSdkVersion;
|
|
387
|
+
}
|
|
388
|
+
const upgradeUrl = readStringField(raw, "upgradeUrl");
|
|
389
|
+
if (upgradeUrl !== undefined) {
|
|
390
|
+
result.upgradeUrl = upgradeUrl;
|
|
391
|
+
}
|
|
392
|
+
return result;
|
|
365
393
|
}
|
|
366
394
|
function httpSentinel(status, body) {
|
|
367
395
|
if (status === 400) {
|
|
396
|
+
const parsed = parseAdvanceErrorBody(body);
|
|
397
|
+
if (parsed?.error === "sdk_upgrade_required") {
|
|
398
|
+
return ErrSdkUpgradeRequired;
|
|
399
|
+
}
|
|
368
400
|
return ErrBadRequest;
|
|
369
401
|
}
|
|
370
402
|
if (status === 401) {
|
|
@@ -383,6 +415,9 @@ function httpSentinel(status, body) {
|
|
|
383
415
|
if (status === 409) {
|
|
384
416
|
return ErrConflict;
|
|
385
417
|
}
|
|
418
|
+
if (status === 412) {
|
|
419
|
+
return ErrNeedsHints;
|
|
420
|
+
}
|
|
386
421
|
if (status === 429) {
|
|
387
422
|
return ErrRateLimited;
|
|
388
423
|
}
|
|
@@ -391,6 +426,29 @@ function httpSentinel(status, body) {
|
|
|
391
426
|
}
|
|
392
427
|
return ErrServerError;
|
|
393
428
|
}
|
|
429
|
+
function buildSdkUpgradeRequiredError(sentinel, text, logger) {
|
|
430
|
+
const parsed = parseAdvanceErrorBody(text);
|
|
431
|
+
if (parsed === null) {
|
|
432
|
+
logger.error("sdk upgrade required", {
|
|
433
|
+
receivedSdkVersion: null,
|
|
434
|
+
minimumSdkVersion: "<unknown>",
|
|
435
|
+
upgradeUrl: NPM_PACKAGE_URL
|
|
436
|
+
});
|
|
437
|
+
const message2 = `<missing> < <unknown>; bump @superbuilders/primer-tives at ${NPM_PACKAGE_URL}`;
|
|
438
|
+
return errors2.wrap(sentinel, message2);
|
|
439
|
+
}
|
|
440
|
+
const minimumSdkVersion = parsed.minimumSdkVersion === undefined ? "<unknown>" : parsed.minimumSdkVersion;
|
|
441
|
+
const receivedSdkVersion = parsed.receivedSdkVersion === undefined ? null : parsed.receivedSdkVersion;
|
|
442
|
+
const receivedDisplay = receivedSdkVersion === null ? "<missing>" : receivedSdkVersion;
|
|
443
|
+
const upgradeUrl = parsed.upgradeUrl === undefined ? NPM_PACKAGE_URL : parsed.upgradeUrl;
|
|
444
|
+
logger.error("sdk upgrade required", {
|
|
445
|
+
receivedSdkVersion,
|
|
446
|
+
minimumSdkVersion,
|
|
447
|
+
upgradeUrl
|
|
448
|
+
});
|
|
449
|
+
const message = `${receivedDisplay} < ${minimumSdkVersion}; bump @superbuilders/primer-tives at ${upgradeUrl}`;
|
|
450
|
+
return errors2.wrap(sentinel, message);
|
|
451
|
+
}
|
|
394
452
|
function isAbortError(err) {
|
|
395
453
|
if (err instanceof DOMException && err.name === "AbortError") {
|
|
396
454
|
return true;
|
|
@@ -402,7 +460,7 @@ function isAbortError(err) {
|
|
|
402
460
|
}
|
|
403
461
|
function createTransport(tc) {
|
|
404
462
|
const fetchFn = tc.fetch ? tc.fetch : globalThis.fetch;
|
|
405
|
-
const
|
|
463
|
+
const logger = tc.logger;
|
|
406
464
|
function transportSignal() {
|
|
407
465
|
if (tc.abort) {
|
|
408
466
|
return tc.abort.signal;
|
|
@@ -410,7 +468,7 @@ function createTransport(tc) {
|
|
|
410
468
|
return;
|
|
411
469
|
}
|
|
412
470
|
async function transport(body) {
|
|
413
|
-
|
|
471
|
+
logger.debug("transport request", {
|
|
414
472
|
intentKind: body.intent.kind,
|
|
415
473
|
subject: body.subject
|
|
416
474
|
});
|
|
@@ -419,7 +477,8 @@ function createTransport(tc) {
|
|
|
419
477
|
method: "POST",
|
|
420
478
|
headers: {
|
|
421
479
|
"Content-Type": "application/json",
|
|
422
|
-
Authorization: `Bearer ${tc.accessToken}
|
|
480
|
+
Authorization: `Bearer ${tc.accessToken}`,
|
|
481
|
+
"X-Primer-SDK-Version": SDK_VERSION
|
|
423
482
|
},
|
|
424
483
|
body: JSON.stringify(body),
|
|
425
484
|
signal: transportSignal()
|
|
@@ -430,12 +489,12 @@ function createTransport(tc) {
|
|
|
430
489
|
});
|
|
431
490
|
if (!fetchResult.ok) {
|
|
432
491
|
if (isAbortError(fetchResult.error)) {
|
|
433
|
-
|
|
492
|
+
logger.error("transport timeout", {
|
|
434
493
|
intentKind: body.intent.kind
|
|
435
494
|
});
|
|
436
495
|
return { ok: false, error: errors2.wrap(ErrTimeout, fetchResult.error.message) };
|
|
437
496
|
}
|
|
438
|
-
|
|
497
|
+
logger.error("transport network error", {
|
|
439
498
|
error: fetchResult.error
|
|
440
499
|
});
|
|
441
500
|
return { ok: false, error: errors2.wrap(ErrNetwork, fetchResult.error.message) };
|
|
@@ -446,7 +505,10 @@ function createTransport(tc) {
|
|
|
446
505
|
return "";
|
|
447
506
|
});
|
|
448
507
|
const sentinel = res.status === 422 ? ErrUnsupportedPci : httpSentinel(res.status, text);
|
|
449
|
-
|
|
508
|
+
if (errors2.is(sentinel, ErrSdkUpgradeRequired)) {
|
|
509
|
+
return { ok: false, error: buildSdkUpgradeRequiredError(sentinel, text, logger) };
|
|
510
|
+
}
|
|
511
|
+
logger.error("transport http error", {
|
|
450
512
|
status: res.status
|
|
451
513
|
});
|
|
452
514
|
return { ok: false, error: errors2.wrap(sentinel, text) };
|
|
@@ -457,12 +519,12 @@ function createTransport(tc) {
|
|
|
457
519
|
return { ok: false, error: err };
|
|
458
520
|
});
|
|
459
521
|
if (!jsonResult.ok) {
|
|
460
|
-
|
|
522
|
+
logger.error("transport json parse failed", {
|
|
461
523
|
error: jsonResult.error
|
|
462
524
|
});
|
|
463
525
|
return { ok: false, error: errors2.wrap(ErrJsonParse, jsonResult.error.message) };
|
|
464
526
|
}
|
|
465
|
-
|
|
527
|
+
logger.debug("transport success", {
|
|
466
528
|
intentKind: body.intent.kind
|
|
467
529
|
});
|
|
468
530
|
return { ok: true, data: jsonResult.data };
|
|
@@ -480,7 +542,7 @@ function poisonToJSON() {
|
|
|
480
542
|
|
|
481
543
|
// src/client/choice-state.ts
|
|
482
544
|
import * as errors3 from "@superbuilders/errors";
|
|
483
|
-
function choiceState(ctx, stimulus, interaction, options, maxChoices, minChoices) {
|
|
545
|
+
function choiceState(ctx, body, stimulus, interaction, options, maxChoices, minChoices) {
|
|
484
546
|
let submitPending;
|
|
485
547
|
let submitKey;
|
|
486
548
|
let timeoutPending;
|
|
@@ -498,7 +560,7 @@ function choiceState(ctx, stimulus, interaction, options, maxChoices, minChoices
|
|
|
498
560
|
}
|
|
499
561
|
const validation = validateSubmissionForInteraction(interaction, submission);
|
|
500
562
|
if (!validation.ok) {
|
|
501
|
-
ctx.
|
|
563
|
+
ctx.logger.error("choice submit invalid", { selectedKeys, issues: validation.issues });
|
|
502
564
|
return Promise.resolve(ctx.errored(errors3.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
|
|
503
565
|
}
|
|
504
566
|
submitKey = key;
|
|
@@ -524,6 +586,7 @@ function choiceState(ctx, stimulus, interaction, options, maxChoices, minChoices
|
|
|
524
586
|
return {
|
|
525
587
|
phase: "interaction",
|
|
526
588
|
kind: "choice",
|
|
589
|
+
body,
|
|
527
590
|
stimulus,
|
|
528
591
|
interaction,
|
|
529
592
|
options,
|
|
@@ -537,7 +600,7 @@ function choiceState(ctx, stimulus, interaction, options, maxChoices, minChoices
|
|
|
537
600
|
|
|
538
601
|
// src/client/extended-text-state.ts
|
|
539
602
|
import * as errors4 from "@superbuilders/errors";
|
|
540
|
-
function extendedTextState(ctx, stimulus, interaction) {
|
|
603
|
+
function extendedTextState(ctx, body, stimulus, interaction) {
|
|
541
604
|
if (interaction.cardinality === "single") {
|
|
542
605
|
let submitText = function(value) {
|
|
543
606
|
const submission = { type: "extended-text", values: [value] };
|
|
@@ -553,7 +616,7 @@ function extendedTextState(ctx, stimulus, interaction) {
|
|
|
553
616
|
}
|
|
554
617
|
const validation = validateSubmissionForInteraction(interaction, submission);
|
|
555
618
|
if (!validation.ok) {
|
|
556
|
-
ctx.
|
|
619
|
+
ctx.logger.error("extended-text submit invalid", { value, issues: validation.issues });
|
|
557
620
|
return Promise.resolve(ctx.errored(errors4.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
|
|
558
621
|
}
|
|
559
622
|
submitKey2 = key;
|
|
@@ -582,6 +645,7 @@ function extendedTextState(ctx, stimulus, interaction) {
|
|
|
582
645
|
phase: "interaction",
|
|
583
646
|
kind: "extended-text",
|
|
584
647
|
cardinality: "single",
|
|
648
|
+
body,
|
|
585
649
|
stimulus,
|
|
586
650
|
interaction,
|
|
587
651
|
submitText,
|
|
@@ -607,7 +671,7 @@ function extendedTextState(ctx, stimulus, interaction) {
|
|
|
607
671
|
}
|
|
608
672
|
const validation = validateSubmissionForInteraction(multi, submission);
|
|
609
673
|
if (!validation.ok) {
|
|
610
|
-
ctx.
|
|
674
|
+
ctx.logger.error("extended-text submit invalid", { values, issues: validation.issues });
|
|
611
675
|
return Promise.resolve(ctx.errored(errors4.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
|
|
612
676
|
}
|
|
613
677
|
submitKey = key;
|
|
@@ -634,6 +698,7 @@ function extendedTextState(ctx, stimulus, interaction) {
|
|
|
634
698
|
phase: "interaction",
|
|
635
699
|
kind: "extended-text",
|
|
636
700
|
cardinality: "multiple",
|
|
701
|
+
body,
|
|
637
702
|
stimulus,
|
|
638
703
|
interaction: multi,
|
|
639
704
|
maxStrings: multi.maxStrings,
|
|
@@ -645,10 +710,11 @@ function extendedTextState(ctx, stimulus, interaction) {
|
|
|
645
710
|
}
|
|
646
711
|
|
|
647
712
|
// src/client/feedback-state.ts
|
|
648
|
-
function feedbackState(ctx, stimulus, interaction, submission, isCorrect, feedbackContent, review) {
|
|
713
|
+
function feedbackState(ctx, body, stimulus, interaction, submission, isCorrect, feedbackContent, review) {
|
|
649
714
|
let pending;
|
|
650
715
|
return {
|
|
651
716
|
phase: "feedback",
|
|
717
|
+
body,
|
|
652
718
|
stimulus,
|
|
653
719
|
interaction,
|
|
654
720
|
submission,
|
|
@@ -668,7 +734,7 @@ function feedbackState(ctx, stimulus, interaction, submission, isCorrect, feedba
|
|
|
668
734
|
|
|
669
735
|
// src/client/match-state.ts
|
|
670
736
|
import * as errors5 from "@superbuilders/errors";
|
|
671
|
-
function matchState(ctx, stimulus, interaction) {
|
|
737
|
+
function matchState(ctx, body, stimulus, interaction) {
|
|
672
738
|
let submitPending;
|
|
673
739
|
let submitKey;
|
|
674
740
|
let timeoutPending;
|
|
@@ -686,7 +752,7 @@ function matchState(ctx, stimulus, interaction) {
|
|
|
686
752
|
}
|
|
687
753
|
const validation = validateSubmissionForInteraction(interaction, submission);
|
|
688
754
|
if (!validation.ok) {
|
|
689
|
-
ctx.
|
|
755
|
+
ctx.logger.error("match submit invalid", { pairs, issues: validation.issues });
|
|
690
756
|
return Promise.resolve(ctx.errored(errors5.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
|
|
691
757
|
}
|
|
692
758
|
submitKey = key;
|
|
@@ -712,6 +778,7 @@ function matchState(ctx, stimulus, interaction) {
|
|
|
712
778
|
return {
|
|
713
779
|
phase: "interaction",
|
|
714
780
|
kind: "match",
|
|
781
|
+
body,
|
|
715
782
|
stimulus,
|
|
716
783
|
interaction,
|
|
717
784
|
sourceChoices: interaction.sourceChoices,
|
|
@@ -725,10 +792,11 @@ function matchState(ctx, stimulus, interaction) {
|
|
|
725
792
|
}
|
|
726
793
|
|
|
727
794
|
// src/client/observation-state.ts
|
|
728
|
-
function observationState(ctx, stimulus) {
|
|
795
|
+
function observationState(ctx, body, stimulus) {
|
|
729
796
|
let pending;
|
|
730
797
|
return {
|
|
731
798
|
phase: "observation",
|
|
799
|
+
body,
|
|
732
800
|
stimulus,
|
|
733
801
|
advance: function advance() {
|
|
734
802
|
if (pending) {
|
|
@@ -743,7 +811,7 @@ function observationState(ctx, stimulus) {
|
|
|
743
811
|
|
|
744
812
|
// src/client/order-state.ts
|
|
745
813
|
import * as errors6 from "@superbuilders/errors";
|
|
746
|
-
function orderState(ctx, stimulus, interaction) {
|
|
814
|
+
function orderState(ctx, body, stimulus, interaction) {
|
|
747
815
|
let submitPending;
|
|
748
816
|
let submitKey;
|
|
749
817
|
let timeoutPending;
|
|
@@ -761,7 +829,7 @@ function orderState(ctx, stimulus, interaction) {
|
|
|
761
829
|
}
|
|
762
830
|
const validation = validateSubmissionForInteraction(interaction, submission);
|
|
763
831
|
if (!validation.ok) {
|
|
764
|
-
ctx.
|
|
832
|
+
ctx.logger.error("order submit invalid", { orderedKeys, issues: validation.issues });
|
|
765
833
|
return Promise.resolve(ctx.errored(errors6.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
|
|
766
834
|
}
|
|
767
835
|
submitKey = key;
|
|
@@ -787,6 +855,7 @@ function orderState(ctx, stimulus, interaction) {
|
|
|
787
855
|
return {
|
|
788
856
|
phase: "interaction",
|
|
789
857
|
kind: "order",
|
|
858
|
+
body,
|
|
790
859
|
stimulus,
|
|
791
860
|
interaction,
|
|
792
861
|
choices: interaction.choices,
|
|
@@ -800,7 +869,7 @@ function orderState(ctx, stimulus, interaction) {
|
|
|
800
869
|
|
|
801
870
|
// src/client/pci-state.ts
|
|
802
871
|
import * as errors7 from "@superbuilders/errors";
|
|
803
|
-
function pciInteractionState(ctx, stimulus, interaction) {
|
|
872
|
+
function pciInteractionState(ctx, body, stimulus, interaction) {
|
|
804
873
|
let submitPending;
|
|
805
874
|
let submitKey;
|
|
806
875
|
let timeoutPending;
|
|
@@ -817,7 +886,7 @@ function pciInteractionState(ctx, stimulus, interaction) {
|
|
|
817
886
|
}
|
|
818
887
|
return Promise.resolve(ctx.errored(errors7.wrap(ErrConflict, "cannot submit a different pci payload while submit is in flight"), "interaction", { kind: "interaction", submission }));
|
|
819
888
|
}
|
|
820
|
-
ctx.
|
|
889
|
+
ctx.logger.debug("pci submit", { pciId });
|
|
821
890
|
submitKey = key;
|
|
822
891
|
submitPending = ctx.execute({ kind: "interaction", submission }, "interaction").finally(function clearPending() {
|
|
823
892
|
submitPending = undefined;
|
|
@@ -833,7 +902,7 @@ function pciInteractionState(ctx, stimulus, interaction) {
|
|
|
833
902
|
if (timeoutPending) {
|
|
834
903
|
return timeoutPending;
|
|
835
904
|
}
|
|
836
|
-
ctx.
|
|
905
|
+
ctx.logger.debug("pci timeout", { pciId });
|
|
837
906
|
timeoutPending = ctx.execute(intent, "timeout").finally(function clearPending() {
|
|
838
907
|
timeoutPending = undefined;
|
|
839
908
|
});
|
|
@@ -842,6 +911,7 @@ function pciInteractionState(ctx, stimulus, interaction) {
|
|
|
842
911
|
return {
|
|
843
912
|
phase: "interaction",
|
|
844
913
|
kind: "portable-custom",
|
|
914
|
+
body,
|
|
845
915
|
stimulus,
|
|
846
916
|
interaction,
|
|
847
917
|
pciId,
|
|
@@ -854,7 +924,7 @@ function pciInteractionState(ctx, stimulus, interaction) {
|
|
|
854
924
|
|
|
855
925
|
// src/client/text-entry-state.ts
|
|
856
926
|
import * as errors8 from "@superbuilders/errors";
|
|
857
|
-
function textEntryState(ctx, stimulus, interaction) {
|
|
927
|
+
function textEntryState(ctx, body, stimulus, interaction) {
|
|
858
928
|
let submitPending;
|
|
859
929
|
let submitKey;
|
|
860
930
|
let timeoutPending;
|
|
@@ -872,7 +942,7 @@ function textEntryState(ctx, stimulus, interaction) {
|
|
|
872
942
|
}
|
|
873
943
|
const validation = validateSubmissionForInteraction(interaction, submission);
|
|
874
944
|
if (!validation.ok) {
|
|
875
|
-
ctx.
|
|
945
|
+
ctx.logger.error("text-entry submit invalid", { value, issues: validation.issues });
|
|
876
946
|
return Promise.resolve(ctx.errored(errors8.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
|
|
877
947
|
}
|
|
878
948
|
submitKey = key;
|
|
@@ -898,6 +968,7 @@ function textEntryState(ctx, stimulus, interaction) {
|
|
|
898
968
|
return {
|
|
899
969
|
phase: "interaction",
|
|
900
970
|
kind: "text-entry",
|
|
971
|
+
body,
|
|
901
972
|
stimulus,
|
|
902
973
|
interaction,
|
|
903
974
|
submitText,
|
|
@@ -913,6 +984,8 @@ var FATAL_SENTINELS = [
|
|
|
913
984
|
ErrTokenExpired,
|
|
914
985
|
ErrForbidden,
|
|
915
986
|
ErrNotFound,
|
|
987
|
+
ErrNeedsHints,
|
|
988
|
+
ErrSdkUpgradeRequired,
|
|
916
989
|
ErrUnsupportedPci
|
|
917
990
|
];
|
|
918
991
|
function isFatalError(err) {
|
|
@@ -927,13 +1000,24 @@ function isRetriableError(err) {
|
|
|
927
1000
|
return !errors9.is(err, ErrInvalidSubmission);
|
|
928
1001
|
}
|
|
929
1002
|
function makeSession(sc) {
|
|
930
|
-
const
|
|
1003
|
+
const logger = sc.logger;
|
|
931
1004
|
function resolve(result) {
|
|
932
1005
|
switch (result.outcome) {
|
|
933
1006
|
case "advanced":
|
|
934
|
-
return fromAdvanced(result.stimulus, result.interaction);
|
|
935
|
-
case "submitted":
|
|
936
|
-
|
|
1007
|
+
return fromAdvanced(result.frame.body, result.frame.stimulus, result.frame.interaction);
|
|
1008
|
+
case "submitted": {
|
|
1009
|
+
const interaction = result.frame.interaction;
|
|
1010
|
+
if (interaction === null) {
|
|
1011
|
+
logger.error("submitted result without interaction");
|
|
1012
|
+
return {
|
|
1013
|
+
phase: "fatal",
|
|
1014
|
+
error: errors9.wrap(ErrBadRequest, "submitted result missing interaction"),
|
|
1015
|
+
retriable: false,
|
|
1016
|
+
toJSON: poisonToJSON
|
|
1017
|
+
};
|
|
1018
|
+
}
|
|
1019
|
+
return feedbackState(ctx, result.frame.body, result.frame.stimulus, interaction, result.submission, result.isCorrect, result.feedbackContent, result.review);
|
|
1020
|
+
}
|
|
937
1021
|
case "completed":
|
|
938
1022
|
return { phase: "completed", toJSON: poisonToJSON };
|
|
939
1023
|
}
|
|
@@ -947,13 +1031,13 @@ function makeSession(sc) {
|
|
|
947
1031
|
retriable,
|
|
948
1032
|
retry: function retry() {
|
|
949
1033
|
if (!retriable) {
|
|
950
|
-
|
|
1034
|
+
logger.debug("retry ignored for non-retriable error", { failedPhase });
|
|
951
1035
|
return Promise.resolve(state);
|
|
952
1036
|
}
|
|
953
1037
|
if (pending) {
|
|
954
1038
|
return pending;
|
|
955
1039
|
}
|
|
956
|
-
|
|
1040
|
+
logger.debug("retrying from errored state", { failedPhase });
|
|
957
1041
|
pending = execute(intent, failedPhase);
|
|
958
1042
|
return pending;
|
|
959
1043
|
},
|
|
@@ -970,7 +1054,7 @@ function makeSession(sc) {
|
|
|
970
1054
|
const result = await sc.transport(body);
|
|
971
1055
|
if (!result.ok) {
|
|
972
1056
|
if (isFatalError(result.error)) {
|
|
973
|
-
|
|
1057
|
+
logger.error("fatal transport error", { error: result.error, phase });
|
|
974
1058
|
return { phase: "fatal", error: result.error, retriable: false, toJSON: poisonToJSON };
|
|
975
1059
|
}
|
|
976
1060
|
return errored(result.error, phase, intent);
|
|
@@ -985,13 +1069,13 @@ function makeSession(sc) {
|
|
|
985
1069
|
}
|
|
986
1070
|
return false;
|
|
987
1071
|
}
|
|
988
|
-
function fromAdvanced(stimulus, interaction) {
|
|
1072
|
+
function fromAdvanced(body, stimulus, interaction) {
|
|
989
1073
|
if (interaction === null) {
|
|
990
|
-
return observationState(ctx, stimulus);
|
|
1074
|
+
return observationState(ctx, body, stimulus);
|
|
991
1075
|
}
|
|
992
1076
|
if (interaction.type === "portable-custom") {
|
|
993
1077
|
if (!isPciSupported(interaction.pciId)) {
|
|
994
|
-
|
|
1078
|
+
logger.error("unsupported pci in frame", { pciId: interaction.pciId });
|
|
995
1079
|
return {
|
|
996
1080
|
phase: "fatal",
|
|
997
1081
|
error: errors9.wrap(ErrUnsupportedPci, `pci '${interaction.pciId}'`),
|
|
@@ -1000,25 +1084,25 @@ function makeSession(sc) {
|
|
|
1000
1084
|
};
|
|
1001
1085
|
}
|
|
1002
1086
|
}
|
|
1003
|
-
return pendingInteractionState(stimulus, interaction);
|
|
1087
|
+
return pendingInteractionState(body, stimulus, interaction);
|
|
1004
1088
|
}
|
|
1005
|
-
function pendingInteractionState(stimulus, interaction) {
|
|
1089
|
+
function pendingInteractionState(body, stimulus, interaction) {
|
|
1006
1090
|
switch (interaction.type) {
|
|
1007
1091
|
case "choice":
|
|
1008
|
-
return choiceState(ctx, stimulus, interaction, interaction.options, interaction.maxChoices, interaction.minChoices);
|
|
1092
|
+
return choiceState(ctx, body, stimulus, interaction, interaction.options, interaction.maxChoices, interaction.minChoices);
|
|
1009
1093
|
case "text-entry":
|
|
1010
|
-
return textEntryState(ctx, stimulus, interaction);
|
|
1094
|
+
return textEntryState(ctx, body, stimulus, interaction);
|
|
1011
1095
|
case "extended-text":
|
|
1012
|
-
return extendedTextState(ctx, stimulus, interaction);
|
|
1096
|
+
return extendedTextState(ctx, body, stimulus, interaction);
|
|
1013
1097
|
case "order":
|
|
1014
|
-
return orderState(ctx, stimulus, interaction);
|
|
1098
|
+
return orderState(ctx, body, stimulus, interaction);
|
|
1015
1099
|
case "match":
|
|
1016
|
-
return matchState(ctx, stimulus, interaction);
|
|
1100
|
+
return matchState(ctx, body, stimulus, interaction);
|
|
1017
1101
|
case "portable-custom":
|
|
1018
|
-
return pciInteractionState(ctx, stimulus, interaction);
|
|
1102
|
+
return pciInteractionState(ctx, body, stimulus, interaction);
|
|
1019
1103
|
}
|
|
1020
1104
|
}
|
|
1021
|
-
const ctx = {
|
|
1105
|
+
const ctx = { logger, execute, errored };
|
|
1022
1106
|
return { execute };
|
|
1023
1107
|
}
|
|
1024
1108
|
|
|
@@ -1032,9 +1116,9 @@ function isMalformedJws(token) {
|
|
|
1032
1116
|
return dotCount !== 2;
|
|
1033
1117
|
}
|
|
1034
1118
|
function create(config) {
|
|
1035
|
-
const
|
|
1119
|
+
const logger = config.logger;
|
|
1036
1120
|
if (isMalformedJws(config.accessToken)) {
|
|
1037
|
-
|
|
1121
|
+
logger.error("malformed access token", { prefix: ACCESS_TOKEN_PREFIX });
|
|
1038
1122
|
throw errors10.wrap(ErrMalformedAccessToken, `token must start with '${ACCESS_TOKEN_PREFIX}' and contain two dots`);
|
|
1039
1123
|
}
|
|
1040
1124
|
const transport = createTransport({
|
|
@@ -1043,15 +1127,15 @@ function create(config) {
|
|
|
1043
1127
|
origin: config.origin,
|
|
1044
1128
|
fetch: config.fetch,
|
|
1045
1129
|
abort: config.abort,
|
|
1046
|
-
|
|
1130
|
+
logger
|
|
1047
1131
|
});
|
|
1048
1132
|
let startPromise;
|
|
1049
1133
|
async function doStart() {
|
|
1050
|
-
|
|
1134
|
+
logger.debug("start", { subject: config.subject });
|
|
1051
1135
|
const s = makeSession({
|
|
1052
1136
|
supportedPcis: config.supportedPcis,
|
|
1053
1137
|
subject: config.subject,
|
|
1054
|
-
|
|
1138
|
+
logger,
|
|
1055
1139
|
transport
|
|
1056
1140
|
});
|
|
1057
1141
|
return s.execute({ kind: "observation" }, "observation");
|
|
@@ -1059,7 +1143,7 @@ function create(config) {
|
|
|
1059
1143
|
return {
|
|
1060
1144
|
start() {
|
|
1061
1145
|
if (startPromise) {
|
|
1062
|
-
|
|
1146
|
+
logger.debug("start already called");
|
|
1063
1147
|
return startPromise;
|
|
1064
1148
|
}
|
|
1065
1149
|
startPromise = doStart();
|
|
@@ -1071,4 +1155,4 @@ export {
|
|
|
1071
1155
|
create
|
|
1072
1156
|
};
|
|
1073
1157
|
|
|
1074
|
-
//# debugId=
|
|
1158
|
+
//# debugId=5A77D78E02028F9D64756E2164756E21
|