@superbuilders/primer-tives 5.0.4 → 6.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.
Files changed (46) hide show
  1. package/README.md +39 -27
  2. package/dist/client/auth-state.d.ts +4 -2
  3. package/dist/client/auth-state.d.ts.map +1 -1
  4. package/dist/client/choice-state.d.ts +2 -2
  5. package/dist/client/choice-state.d.ts.map +1 -1
  6. package/dist/client/extended-text-state.d.ts +8 -3
  7. package/dist/client/extended-text-state.d.ts.map +1 -1
  8. package/dist/client/feedback-state.d.ts +3 -3
  9. package/dist/client/feedback-state.d.ts.map +1 -1
  10. package/dist/client/index.d.ts +2 -2
  11. package/dist/client/index.d.ts.map +1 -1
  12. package/dist/client/index.js +376 -106
  13. package/dist/client/index.js.map +19 -17
  14. package/dist/client/journey.d.ts +13 -0
  15. package/dist/client/journey.d.ts.map +1 -0
  16. package/dist/client/match-state.d.ts +2 -2
  17. package/dist/client/match-state.d.ts.map +1 -1
  18. package/dist/client/mode.d.ts +5 -0
  19. package/dist/client/mode.d.ts.map +1 -0
  20. package/dist/client/observation-state.d.ts +2 -2
  21. package/dist/client/observation-state.d.ts.map +1 -1
  22. package/dist/client/order-state.d.ts +2 -2
  23. package/dist/client/order-state.d.ts.map +1 -1
  24. package/dist/client/pci-state.d.ts +2 -2
  25. package/dist/client/pci-state.d.ts.map +1 -1
  26. package/dist/client/session-context.d.ts +1 -0
  27. package/dist/client/session-context.d.ts.map +1 -1
  28. package/dist/client/session.d.ts +4 -2
  29. package/dist/client/session.d.ts.map +1 -1
  30. package/dist/client/start.d.ts +3 -0
  31. package/dist/client/start.d.ts.map +1 -1
  32. package/dist/client/text-entry-state.d.ts +7 -3
  33. package/dist/client/text-entry-state.d.ts.map +1 -1
  34. package/dist/client/transport.d.ts +9 -2
  35. package/dist/client/transport.d.ts.map +1 -1
  36. package/dist/client/types.d.ts +59 -12
  37. package/dist/client/types.d.ts.map +1 -1
  38. package/dist/contracts/index.d.ts +1 -1
  39. package/dist/contracts/index.d.ts.map +1 -1
  40. package/dist/contracts/index.js +13 -32
  41. package/dist/contracts/index.js.map +3 -3
  42. package/dist/contracts/types.d.ts +3 -20
  43. package/dist/contracts/types.d.ts.map +1 -1
  44. package/dist/contracts/validation.d.ts.map +1 -1
  45. package/dist/version.d.ts +1 -1
  46. package/package.json +1 -1
@@ -6284,6 +6284,8 @@ var ErrNoRoutableContent = errors.new("no routable content");
6284
6284
  var ErrNoActiveFrame = errors.new("no active frame");
6285
6285
  var ErrFrameAlreadyAnswered = errors.new("frame already answered");
6286
6286
  var ErrSessionStateConflict = errors.new("session state conflict");
6287
+ // src/grade-level.ts
6288
+ var GRADE_LEVELS = ["K", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"];
6287
6289
  // src/contracts/content.ts
6288
6290
  function inlinesToPlainText(nodes) {
6289
6291
  const parts = [];
@@ -6594,33 +6596,6 @@ function findUnknownIds(values, choices) {
6594
6596
  return !ids.has(value);
6595
6597
  });
6596
6598
  }
6597
- function countByIdentifier(pairs, side) {
6598
- const counts = new Map;
6599
- for (const pair of pairs) {
6600
- const key = pair[side];
6601
- const currentCount = counts.get(key);
6602
- if (currentCount === undefined) {
6603
- counts.set(key, 1);
6604
- continue;
6605
- }
6606
- counts.set(key, currentCount + 1);
6607
- }
6608
- return counts;
6609
- }
6610
- function validateUsageBounds(choices, counts, side) {
6611
- const issues2 = [];
6612
- for (const choice of choices) {
6613
- const maybeCount = counts.get(choice.identifier);
6614
- const count = maybeCount === undefined ? 0 : maybeCount;
6615
- if (choice.matchMax !== 0 && count > choice.matchMax) {
6616
- issues2.push(`${side} '${choice.identifier}' used ${count} times, max ${choice.matchMax}`);
6617
- }
6618
- if (count < choice.matchMin) {
6619
- issues2.push(`${side} '${choice.identifier}' used ${count} times, min ${choice.matchMin}`);
6620
- }
6621
- }
6622
- return issues2;
6623
- }
6624
6599
  function pciSubmissionSchema(pciId) {
6625
6600
  switch (pciId) {
6626
6601
  case "urn:primer:pci:fraction-input":
@@ -6714,6 +6689,18 @@ function validateMatch(interaction, submission) {
6714
6689
  if (duplicatePairs.length > 0) {
6715
6690
  issues2.push("duplicate associations are not allowed");
6716
6691
  }
6692
+ const duplicateSources = duplicates(submission.pairs, function keyOf(pair) {
6693
+ return pair.source;
6694
+ });
6695
+ if (duplicateSources.length > 0) {
6696
+ issues2.push(`duplicate sources: ${duplicateSources.join(", ")}`);
6697
+ }
6698
+ const duplicateTargets = duplicates(submission.pairs, function keyOf(pair) {
6699
+ return pair.target;
6700
+ });
6701
+ if (duplicateTargets.length > 0) {
6702
+ issues2.push(`duplicate targets: ${duplicateTargets.join(", ")}`);
6703
+ }
6717
6704
  const sourceIds = new Set(interaction.sourceChoices.map(function getId(choice) {
6718
6705
  return choice.identifier;
6719
6706
  }));
@@ -6728,10 +6715,6 @@ function validateMatch(interaction, submission) {
6728
6715
  issues2.push(`unknown target '${pair.target}'`);
6729
6716
  }
6730
6717
  }
6731
- const sourceCounts = countByIdentifier(submission.pairs, "source");
6732
- const targetCounts = countByIdentifier(submission.pairs, "target");
6733
- issues2.push(...validateUsageBounds(interaction.sourceChoices, sourceCounts, "source"));
6734
- issues2.push(...validateUsageBounds(interaction.targetChoices, targetCounts, "target"));
6735
6718
  return issues2;
6736
6719
  }
6737
6720
  function validatePortableCustom(interaction, submission) {
@@ -7263,6 +7246,7 @@ function pendingLogin(config) {
7263
7246
  function signInRequiredState(config) {
7264
7247
  return {
7265
7248
  phase: "sign-in-required",
7249
+ journey: config.journey,
7266
7250
  login: pendingLogin(config),
7267
7251
  toJSON: poisonToJSON
7268
7252
  };
@@ -7270,32 +7254,210 @@ function signInRequiredState(config) {
7270
7254
  function signInFailedState(config) {
7271
7255
  return {
7272
7256
  phase: "sign-in-failed",
7257
+ journey: config.journey,
7273
7258
  error: config.error,
7274
7259
  login: pendingLogin(config),
7275
7260
  toJSON: poisonToJSON
7276
7261
  };
7277
7262
  }
7278
- function authUnavailableState(error) {
7263
+ function authUnavailableState(error, journey) {
7279
7264
  return {
7280
7265
  phase: "auth-unavailable",
7266
+ journey,
7281
7267
  error,
7282
7268
  toJSON: poisonToJSON
7283
7269
  };
7284
7270
  }
7285
- function authConfigInvalidState(error) {
7271
+ function authConfigInvalidState(error, journey) {
7286
7272
  return {
7287
7273
  phase: "auth-config-invalid",
7274
+ journey,
7288
7275
  error,
7289
7276
  toJSON: poisonToJSON
7290
7277
  };
7291
7278
  }
7292
7279
 
7280
+ // src/client/journey.ts
7281
+ var ONBOARDING_JOURNEY = {
7282
+ mode: "onboarding"
7283
+ };
7284
+ var GRADE_LEVEL_SET = new Set(GRADE_LEVELS);
7285
+ var FORBIDDEN_JOURNEY_KEYS = new Set([
7286
+ "id",
7287
+ "Id",
7288
+ "uuid",
7289
+ "handle",
7290
+ "cursor",
7291
+ "delta",
7292
+ "standard",
7293
+ "prerequisite",
7294
+ "theme",
7295
+ "area",
7296
+ "map",
7297
+ "remaining",
7298
+ "completedFrames",
7299
+ "totalFrames",
7300
+ "accuracy",
7301
+ "normalizedProgress",
7302
+ "progressValue",
7303
+ "mastery",
7304
+ "threshold",
7305
+ "placement",
7306
+ "routingTarget",
7307
+ "instructional",
7308
+ "frameId"
7309
+ ]);
7310
+ function isGradeLevel(value) {
7311
+ return GRADE_LEVEL_SET.has(value);
7312
+ }
7313
+ function isRecord2(value) {
7314
+ return typeof value === "object" && value !== null && !Array.isArray(value);
7315
+ }
7316
+ function containsNull(value) {
7317
+ if (value === null) {
7318
+ return true;
7319
+ }
7320
+ if (Array.isArray(value)) {
7321
+ for (const item of value) {
7322
+ if (containsNull(item)) {
7323
+ return true;
7324
+ }
7325
+ }
7326
+ return false;
7327
+ }
7328
+ if (isRecord2(value)) {
7329
+ for (const key in value) {
7330
+ if (containsNull(value[key])) {
7331
+ return true;
7332
+ }
7333
+ }
7334
+ }
7335
+ return false;
7336
+ }
7337
+ function forbiddenJourneyKey(value) {
7338
+ if (Array.isArray(value)) {
7339
+ for (const item of value) {
7340
+ const found = forbiddenJourneyKey(item);
7341
+ if (found !== null) {
7342
+ return found;
7343
+ }
7344
+ }
7345
+ return null;
7346
+ }
7347
+ if (!isRecord2(value)) {
7348
+ return null;
7349
+ }
7350
+ for (const key in value) {
7351
+ if (FORBIDDEN_JOURNEY_KEYS.has(key)) {
7352
+ return key;
7353
+ }
7354
+ const found = forbiddenJourneyKey(value[key]);
7355
+ if (found !== null) {
7356
+ return found;
7357
+ }
7358
+ }
7359
+ return null;
7360
+ }
7361
+ function readString(value, key) {
7362
+ const field = value[key];
7363
+ if (typeof field !== "string") {
7364
+ return null;
7365
+ }
7366
+ if (field.trim().length === 0) {
7367
+ return null;
7368
+ }
7369
+ return field;
7370
+ }
7371
+ function readGradeLevel(value) {
7372
+ const level = readString(value, "level");
7373
+ if (level === null) {
7374
+ return null;
7375
+ }
7376
+ if (!isGradeLevel(level)) {
7377
+ return null;
7378
+ }
7379
+ return level;
7380
+ }
7381
+ function parseProgress(value) {
7382
+ if (!isRecord2(value)) {
7383
+ return null;
7384
+ }
7385
+ const done = value.done;
7386
+ const total = value.total;
7387
+ if (typeof done !== "number" || typeof total !== "number") {
7388
+ return null;
7389
+ }
7390
+ if (!Number.isInteger(done) || !Number.isInteger(total)) {
7391
+ return null;
7392
+ }
7393
+ if (done < 0 || total < 0 || done > total) {
7394
+ return null;
7395
+ }
7396
+ return { done, total };
7397
+ }
7398
+ function parseGradeWithLevel(value) {
7399
+ if (!isRecord2(value)) {
7400
+ return null;
7401
+ }
7402
+ const level = readGradeLevel(value);
7403
+ const progress = parseProgress(value.progress);
7404
+ if (level === null || progress === null) {
7405
+ return null;
7406
+ }
7407
+ return { level, progress };
7408
+ }
7409
+ function parseTitledProgress(value) {
7410
+ if (!isRecord2(value)) {
7411
+ return null;
7412
+ }
7413
+ const title = readString(value, "title");
7414
+ const progress = parseProgress(value.progress);
7415
+ if (title === null || progress === null) {
7416
+ return null;
7417
+ }
7418
+ return { title, progress };
7419
+ }
7420
+ function parseJourney(value) {
7421
+ if (!isRecord2(value)) {
7422
+ return { ok: false, reason: "journey_not_object" };
7423
+ }
7424
+ if (containsNull(value)) {
7425
+ return { ok: false, reason: "journey_contains_null" };
7426
+ }
7427
+ const forbidden = forbiddenJourneyKey(value);
7428
+ if (forbidden !== null) {
7429
+ return { ok: false, reason: `journey_forbidden_key:${forbidden}` };
7430
+ }
7431
+ const mode = value.mode;
7432
+ if (mode === "onboarding") {
7433
+ return { ok: true, journey: ONBOARDING_JOURNEY };
7434
+ }
7435
+ if (mode === "complete") {
7436
+ const grade = parseGradeWithLevel(value.grade);
7437
+ const course = parseTitledProgress(value.course);
7438
+ if (grade === null || course === null) {
7439
+ return { ok: false, reason: "journey_complete_invalid" };
7440
+ }
7441
+ return { ok: true, journey: { mode, grade, course } };
7442
+ }
7443
+ if (mode === "learning" || mode === "practice" || mode === "support") {
7444
+ const grade = parseGradeWithLevel(value.grade);
7445
+ const course = parseTitledProgress(value.course);
7446
+ const lesson = parseTitledProgress(value.lesson);
7447
+ if (grade === null || course === null || lesson === null) {
7448
+ return { ok: false, reason: "journey_active_invalid" };
7449
+ }
7450
+ return { ok: true, journey: { mode, grade, course, lesson } };
7451
+ }
7452
+ return { ok: false, reason: "journey_mode_invalid" };
7453
+ }
7454
+
7293
7455
  // src/client/session.ts
7294
7456
  import * as errors11 from "@superbuilders/errors";
7295
7457
 
7296
7458
  // src/client/choice-state.ts
7297
7459
  import * as errors5 from "@superbuilders/errors";
7298
- function choiceState(ctx, body, stimulus, interaction, options, maxChoices, minChoices, feedback) {
7460
+ function choiceState(ctx, journey, body, stimulus, interaction, options, maxChoices, minChoices, feedback) {
7299
7461
  let submitPending;
7300
7462
  let submitKey;
7301
7463
  let timeoutPending;
@@ -7339,6 +7501,7 @@ function choiceState(ctx, body, stimulus, interaction, options, maxChoices, minC
7339
7501
  return {
7340
7502
  phase: "interaction",
7341
7503
  kind: "choice",
7504
+ journey,
7342
7505
  body,
7343
7506
  stimulus,
7344
7507
  interaction,
@@ -7354,27 +7517,37 @@ function choiceState(ctx, body, stimulus, interaction, options, maxChoices, minC
7354
7517
 
7355
7518
  // src/client/extended-text-state.ts
7356
7519
  import * as errors6 from "@superbuilders/errors";
7357
- function extendedTextState(ctx, body, stimulus, interaction, feedback) {
7520
+ function extendedTextIntent(submission, nextSubmissionRole) {
7521
+ if (nextSubmissionRole === "revision") {
7522
+ return { kind: "interaction", submission, submissionRole: "revision" };
7523
+ }
7524
+ return { kind: "interaction", submission };
7525
+ }
7526
+ function extendedTextState(ctx, journey, body, stimulus, interaction, feedback, options = {}) {
7358
7527
  if (interaction.cardinality === "single") {
7359
7528
  let submitText = function(value) {
7360
- const submission = { type: "extended-text", values: [value] };
7529
+ const submission = {
7530
+ type: "extended-text",
7531
+ values: [value]
7532
+ };
7533
+ const intent = extendedTextIntent(submission, options.nextSubmissionRole);
7361
7534
  const key = JSON.stringify(submission);
7362
7535
  if (timeoutPending2) {
7363
- return Promise.resolve(ctx.errored(errors6.wrap(ErrConflict, "cannot submit while timeout is in flight"), "interaction", { kind: "interaction", submission }));
7536
+ return Promise.resolve(ctx.errored(errors6.wrap(ErrConflict, "cannot submit while timeout is in flight"), "interaction", intent));
7364
7537
  }
7365
7538
  if (submitPending2) {
7366
7539
  if (submitKey2 === key) {
7367
7540
  return submitPending2;
7368
7541
  }
7369
- return Promise.resolve(ctx.errored(errors6.wrap(ErrConflict, "cannot submit a different extended-text payload while submit is in flight"), "interaction", { kind: "interaction", submission }));
7542
+ return Promise.resolve(ctx.errored(errors6.wrap(ErrConflict, "cannot submit a different extended-text payload while submit is in flight"), "interaction", intent));
7370
7543
  }
7371
7544
  const validation = validateSubmissionForInteraction(interaction, submission);
7372
7545
  if (!validation.ok) {
7373
7546
  ctx.logger.error({ value, issues: validation.issues }, "extended-text submit invalid");
7374
- return Promise.resolve(ctx.errored(errors6.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
7547
+ return Promise.resolve(ctx.errored(errors6.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", intent));
7375
7548
  }
7376
7549
  submitKey2 = key;
7377
- submitPending2 = ctx.execute({ kind: "interaction", submission }, "interaction").finally(function clearPending() {
7550
+ submitPending2 = ctx.execute(intent, "interaction").finally(function clearPending() {
7378
7551
  submitPending2 = undefined;
7379
7552
  submitKey2 = undefined;
7380
7553
  });
@@ -7399,10 +7572,12 @@ function extendedTextState(ctx, body, stimulus, interaction, feedback) {
7399
7572
  phase: "interaction",
7400
7573
  kind: "extended-text",
7401
7574
  cardinality: "single",
7575
+ journey,
7402
7576
  body,
7403
7577
  stimulus,
7404
7578
  interaction,
7405
7579
  feedback,
7580
+ initialValue: options.initialValue,
7406
7581
  submitText,
7407
7582
  timeout: timeout2,
7408
7583
  toJSON: poisonToJSON
@@ -7413,24 +7588,28 @@ function extendedTextState(ctx, body, stimulus, interaction, feedback) {
7413
7588
  let submitKey;
7414
7589
  let timeoutPending;
7415
7590
  function submitTexts(values) {
7416
- const submission = { type: "extended-text", values };
7591
+ const submission = {
7592
+ type: "extended-text",
7593
+ values
7594
+ };
7595
+ const intent = extendedTextIntent(submission, options.nextSubmissionRole);
7417
7596
  const key = JSON.stringify(submission);
7418
7597
  if (timeoutPending) {
7419
- return Promise.resolve(ctx.errored(errors6.wrap(ErrConflict, "cannot submit while timeout is in flight"), "interaction", { kind: "interaction", submission }));
7598
+ return Promise.resolve(ctx.errored(errors6.wrap(ErrConflict, "cannot submit while timeout is in flight"), "interaction", intent));
7420
7599
  }
7421
7600
  if (submitPending) {
7422
7601
  if (submitKey === key) {
7423
7602
  return submitPending;
7424
7603
  }
7425
- return Promise.resolve(ctx.errored(errors6.wrap(ErrConflict, "cannot submit a different extended-text payload while submit is in flight"), "interaction", { kind: "interaction", submission }));
7604
+ return Promise.resolve(ctx.errored(errors6.wrap(ErrConflict, "cannot submit a different extended-text payload while submit is in flight"), "interaction", intent));
7426
7605
  }
7427
7606
  const validation = validateSubmissionForInteraction(multi, submission);
7428
7607
  if (!validation.ok) {
7429
7608
  ctx.logger.error({ values, issues: validation.issues }, "extended-text submit invalid");
7430
- return Promise.resolve(ctx.errored(errors6.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
7609
+ return Promise.resolve(ctx.errored(errors6.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", intent));
7431
7610
  }
7432
7611
  submitKey = key;
7433
- submitPending = ctx.execute({ kind: "interaction", submission }, "interaction").finally(function clearPending() {
7612
+ submitPending = ctx.execute(intent, "interaction").finally(function clearPending() {
7434
7613
  submitPending = undefined;
7435
7614
  submitKey = undefined;
7436
7615
  });
@@ -7453,10 +7632,12 @@ function extendedTextState(ctx, body, stimulus, interaction, feedback) {
7453
7632
  phase: "interaction",
7454
7633
  kind: "extended-text",
7455
7634
  cardinality: "multiple",
7635
+ journey,
7456
7636
  body,
7457
7637
  stimulus,
7458
7638
  interaction: multi,
7459
7639
  feedback,
7640
+ initialValues: options.initialValues,
7460
7641
  maxStrings: multi.maxStrings,
7461
7642
  minStrings: multi.minStrings,
7462
7643
  submitTexts,
@@ -7476,25 +7657,27 @@ function createAdvance(ctx) {
7476
7657
  return pending;
7477
7658
  };
7478
7659
  }
7479
- function submittedFeedbackState(ctx, body, stimulus, interaction, submission, assessmentOutcome, feedbackContent, review) {
7660
+ function submittedFeedbackState(ctx, journey, body, stimulus, interaction, submission, isCorrect, feedbackContent, review) {
7480
7661
  return {
7481
7662
  phase: "feedback",
7482
7663
  feedbackKind: "submitted",
7664
+ journey,
7483
7665
  body,
7484
7666
  stimulus,
7485
7667
  interaction,
7486
7668
  submission,
7487
- assessmentOutcome,
7669
+ isCorrect,
7488
7670
  feedbackContent,
7489
7671
  review,
7490
7672
  advance: createAdvance(ctx),
7491
7673
  toJSON: poisonToJSON
7492
7674
  };
7493
7675
  }
7494
- function timedOutFeedbackState(ctx, body, stimulus, interaction, feedbackContent) {
7676
+ function timedOutFeedbackState(ctx, journey, body, stimulus, interaction, feedbackContent) {
7495
7677
  return {
7496
7678
  phase: "feedback",
7497
7679
  feedbackKind: "timedOut",
7680
+ journey,
7498
7681
  body,
7499
7682
  stimulus,
7500
7683
  interaction,
@@ -7506,7 +7689,7 @@ function timedOutFeedbackState(ctx, body, stimulus, interaction, feedbackContent
7506
7689
 
7507
7690
  // src/client/match-state.ts
7508
7691
  import * as errors7 from "@superbuilders/errors";
7509
- function matchState(ctx, body, stimulus, interaction, feedback) {
7692
+ function matchState(ctx, journey, body, stimulus, interaction, feedback) {
7510
7693
  let submitPending;
7511
7694
  let submitKey;
7512
7695
  let timeoutPending;
@@ -7550,6 +7733,7 @@ function matchState(ctx, body, stimulus, interaction, feedback) {
7550
7733
  return {
7551
7734
  phase: "interaction",
7552
7735
  kind: "match",
7736
+ journey,
7553
7737
  body,
7554
7738
  stimulus,
7555
7739
  interaction,
@@ -7565,10 +7749,11 @@ function matchState(ctx, body, stimulus, interaction, feedback) {
7565
7749
  }
7566
7750
 
7567
7751
  // src/client/observation-state.ts
7568
- function observationState(ctx, body, stimulus) {
7752
+ function observationState(ctx, journey, body, stimulus) {
7569
7753
  let pending;
7570
7754
  return {
7571
7755
  phase: "observation",
7756
+ journey,
7572
7757
  body,
7573
7758
  stimulus,
7574
7759
  advance: function advance() {
@@ -7584,7 +7769,7 @@ function observationState(ctx, body, stimulus) {
7584
7769
 
7585
7770
  // src/client/order-state.ts
7586
7771
  import * as errors8 from "@superbuilders/errors";
7587
- function orderState(ctx, body, stimulus, interaction, feedback) {
7772
+ function orderState(ctx, journey, body, stimulus, interaction, feedback) {
7588
7773
  let submitPending;
7589
7774
  let submitKey;
7590
7775
  let timeoutPending;
@@ -7628,6 +7813,7 @@ function orderState(ctx, body, stimulus, interaction, feedback) {
7628
7813
  return {
7629
7814
  phase: "interaction",
7630
7815
  kind: "order",
7816
+ journey,
7631
7817
  body,
7632
7818
  stimulus,
7633
7819
  interaction,
@@ -7643,7 +7829,7 @@ function orderState(ctx, body, stimulus, interaction, feedback) {
7643
7829
 
7644
7830
  // src/client/pci-state.ts
7645
7831
  import * as errors9 from "@superbuilders/errors";
7646
- function pciInteractionState(ctx, body, stimulus, interaction, feedback) {
7832
+ function pciInteractionState(ctx, journey, body, stimulus, interaction, feedback) {
7647
7833
  let submitPending;
7648
7834
  let submitKey;
7649
7835
  let timeoutPending;
@@ -7685,6 +7871,7 @@ function pciInteractionState(ctx, body, stimulus, interaction, feedback) {
7685
7871
  return {
7686
7872
  phase: "interaction",
7687
7873
  kind: "portable-custom",
7874
+ journey,
7688
7875
  body,
7689
7876
  stimulus,
7690
7877
  interaction,
@@ -7699,29 +7886,39 @@ function pciInteractionState(ctx, body, stimulus, interaction, feedback) {
7699
7886
 
7700
7887
  // src/client/text-entry-state.ts
7701
7888
  import * as errors10 from "@superbuilders/errors";
7702
- function textEntryState(ctx, body, stimulus, interaction, feedback) {
7889
+ function textEntryIntent(submission, nextSubmissionRole) {
7890
+ if (nextSubmissionRole === "revision") {
7891
+ return { kind: "interaction", submission, submissionRole: "revision" };
7892
+ }
7893
+ return { kind: "interaction", submission };
7894
+ }
7895
+ function textEntryState(ctx, journey, body, stimulus, interaction, feedback, options = {}) {
7703
7896
  let submitPending;
7704
7897
  let submitKey;
7705
7898
  let timeoutPending;
7706
7899
  function submitText(value) {
7707
- const submission = { type: "text-entry", value };
7900
+ const submission = {
7901
+ type: "text-entry",
7902
+ value
7903
+ };
7904
+ const intent = textEntryIntent(submission, options.nextSubmissionRole);
7708
7905
  const key = JSON.stringify(submission);
7709
7906
  if (timeoutPending) {
7710
- return Promise.resolve(ctx.errored(errors10.wrap(ErrConflict, "cannot submit while timeout is in flight"), "interaction", { kind: "interaction", submission }));
7907
+ return Promise.resolve(ctx.errored(errors10.wrap(ErrConflict, "cannot submit while timeout is in flight"), "interaction", intent));
7711
7908
  }
7712
7909
  if (submitPending) {
7713
7910
  if (submitKey === key) {
7714
7911
  return submitPending;
7715
7912
  }
7716
- return Promise.resolve(ctx.errored(errors10.wrap(ErrConflict, "cannot submit a different text payload while submit is in flight"), "interaction", { kind: "interaction", submission }));
7913
+ return Promise.resolve(ctx.errored(errors10.wrap(ErrConflict, "cannot submit a different text payload while submit is in flight"), "interaction", intent));
7717
7914
  }
7718
7915
  const validation = validateSubmissionForInteraction(interaction, submission);
7719
7916
  if (!validation.ok) {
7720
7917
  ctx.logger.error({ value, issues: validation.issues }, "text-entry submit invalid");
7721
- return Promise.resolve(ctx.errored(errors10.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", { kind: "interaction", submission }));
7918
+ return Promise.resolve(ctx.errored(errors10.wrap(ErrInvalidSubmission, submissionValidationMessage(validation)), "interaction", intent));
7722
7919
  }
7723
7920
  submitKey = key;
7724
- submitPending = ctx.execute({ kind: "interaction", submission }, "interaction").finally(function clearPending() {
7921
+ submitPending = ctx.execute(intent, "interaction").finally(function clearPending() {
7725
7922
  submitPending = undefined;
7726
7923
  submitKey = undefined;
7727
7924
  });
@@ -7743,10 +7940,12 @@ function textEntryState(ctx, body, stimulus, interaction, feedback) {
7743
7940
  return {
7744
7941
  phase: "interaction",
7745
7942
  kind: "text-entry",
7943
+ journey,
7746
7944
  body,
7747
7945
  stimulus,
7748
7946
  interaction,
7749
7947
  feedback,
7948
+ initialValue: options.initialValue,
7750
7949
  submitText,
7751
7950
  timeout,
7752
7951
  toJSON: poisonToJSON
@@ -7781,29 +7980,43 @@ function isRetriableError(err) {
7781
7980
  }
7782
7981
  function makeSession(sc) {
7783
7982
  const logger = sc.logger;
7784
- function resolve(result) {
7983
+ let latestJourney = ONBOARDING_JOURNEY;
7984
+ function advancedPreservedSubmission(result, intent) {
7985
+ if (result.outcome !== "advanced") {
7986
+ return;
7987
+ }
7988
+ if (result.frame.feedback === null) {
7989
+ return;
7990
+ }
7991
+ if (intent.kind !== "interaction") {
7992
+ return;
7993
+ }
7994
+ if (intent.submission.type === "text-entry" || intent.submission.type === "extended-text") {
7995
+ return intent.submission;
7996
+ }
7997
+ return;
7998
+ }
7999
+ function resolve(result, intent) {
8000
+ latestJourney = result.journey;
7785
8001
  switch (result.outcome) {
7786
- case "advanced":
7787
- return fromAdvanced(result.frame.body, result.frame.stimulus, result.frame.interaction, result.frame.feedback);
8002
+ case "advanced": {
8003
+ const preservedSubmission = advancedPreservedSubmission(result, intent);
8004
+ const nextSubmissionRole = preservedSubmission === undefined ? undefined : "revision";
8005
+ return fromAdvanced(result.journey, result.frame.body, result.frame.stimulus, result.frame.interaction, result.frame.feedback, preservedSubmission, nextSubmissionRole);
8006
+ }
7788
8007
  case "submitted": {
7789
8008
  const interaction = result.frame.interaction;
7790
8009
  if (interaction === null) {
7791
8010
  logger.error("submitted result without interaction");
7792
8011
  return {
7793
8012
  phase: "fatal",
8013
+ journey: result.journey,
7794
8014
  error: errors11.wrap(ErrBadRequest, "submitted result missing interaction"),
7795
8015
  retriable: false,
7796
8016
  toJSON: poisonToJSON
7797
8017
  };
7798
8018
  }
7799
- if (result.assessmentOutcome.value === "revisionRequested") {
7800
- const feedback = {
7801
- assessmentOutcome: result.assessmentOutcome,
7802
- feedbackContent: result.feedbackContent
7803
- };
7804
- return fromAdvanced(result.frame.body, result.frame.stimulus, interaction, feedback);
7805
- }
7806
- return submittedFeedbackState(ctx, result.frame.body, result.frame.stimulus, interaction, result.submission, result.assessmentOutcome, result.feedbackContent, result.review);
8019
+ return submittedFeedbackState(ctx, result.journey, result.frame.body, result.frame.stimulus, interaction, result.submission, result.isCorrect, result.feedbackContent, result.review);
7807
8020
  }
7808
8021
  case "timedOut": {
7809
8022
  const interaction = result.frame.interaction;
@@ -7811,15 +8024,16 @@ function makeSession(sc) {
7811
8024
  logger.error("timed out result without interaction");
7812
8025
  return {
7813
8026
  phase: "fatal",
8027
+ journey: result.journey,
7814
8028
  error: errors11.wrap(ErrBadRequest, "timed out result missing interaction"),
7815
8029
  retriable: false,
7816
8030
  toJSON: poisonToJSON
7817
8031
  };
7818
8032
  }
7819
- return timedOutFeedbackState(ctx, result.frame.body, result.frame.stimulus, interaction, result.feedbackContent);
8033
+ return timedOutFeedbackState(ctx, result.journey, result.frame.body, result.frame.stimulus, interaction, result.feedbackContent);
7820
8034
  }
7821
8035
  case "completed":
7822
- return { phase: "completed", toJSON: poisonToJSON };
8036
+ return { phase: "completed", journey: result.journey, toJSON: poisonToJSON };
7823
8037
  }
7824
8038
  }
7825
8039
  function errored(error, failedPhase, intent) {
@@ -7827,6 +8041,7 @@ function makeSession(sc) {
7827
8041
  if (!retriable) {
7828
8042
  return {
7829
8043
  phase: "errored",
8044
+ journey: latestJourney,
7830
8045
  error,
7831
8046
  retriable: false,
7832
8047
  toJSON: poisonToJSON
@@ -7835,6 +8050,7 @@ function makeSession(sc) {
7835
8050
  let pending;
7836
8051
  const state = {
7837
8052
  phase: "errored",
8053
+ journey: latestJourney,
7838
8054
  error,
7839
8055
  retriable: true,
7840
8056
  retry: function retry() {
@@ -7853,17 +8069,19 @@ function makeSession(sc) {
7853
8069
  logger.debug({
7854
8070
  phase,
7855
8071
  intentKind: intent.kind,
7856
- subject: sc.subject
8072
+ subject: sc.subject,
8073
+ mode: sc.mode
7857
8074
  }, "session execute");
7858
8075
  const body = {
7859
8076
  subject: sc.subject,
8077
+ mode: sc.mode,
7860
8078
  intent
7861
8079
  };
7862
8080
  const result = await sc.transport(body);
7863
8081
  if (!result.ok) {
7864
8082
  if ((errors11.is(result.error, ErrTokenExpired) || errors11.is(result.error, ErrInvalidAccessToken)) && sc.reauthenticate !== null) {
7865
8083
  logger.warn({ phase, intentKind: intent.kind }, "session token rejected");
7866
- return sc.reauthenticate(result.error);
8084
+ return sc.reauthenticate(result.error, latestJourney);
7867
8085
  }
7868
8086
  if (isFatalError(result.error)) {
7869
8087
  logger.error({
@@ -7873,6 +8091,7 @@ function makeSession(sc) {
7873
8091
  }, "fatal transport error");
7874
8092
  return {
7875
8093
  phase: "fatal",
8094
+ journey: latestJourney,
7876
8095
  error: result.error,
7877
8096
  retriable: false,
7878
8097
  toJSON: poisonToJSON
@@ -7885,7 +8104,7 @@ function makeSession(sc) {
7885
8104
  }, "retriable transport error");
7886
8105
  return errored(result.error, phase, intent);
7887
8106
  }
7888
- const next = resolve(result.data);
8107
+ const next = resolve(result.data, intent);
7889
8108
  logger.debug({
7890
8109
  phase,
7891
8110
  intentKind: intent.kind,
@@ -7902,50 +8121,75 @@ function makeSession(sc) {
7902
8121
  }
7903
8122
  return false;
7904
8123
  }
7905
- function fromAdvanced(body, stimulus, interaction, feedback) {
8124
+ function fromAdvanced(journey, body, stimulus, interaction, feedback, preservedSubmission, nextSubmissionRole) {
7906
8125
  if (interaction === null) {
7907
- return observationState(ctx, body, stimulus);
8126
+ return observationState(ctx, journey, body, stimulus);
7908
8127
  }
7909
8128
  if (interaction.type === "portable-custom") {
7910
8129
  if (!isPciSupported(interaction.pciId)) {
7911
8130
  logger.error({ pciId: interaction.pciId }, "unsupported pci in frame");
7912
8131
  return {
7913
8132
  phase: "fatal",
8133
+ journey,
7914
8134
  error: errors11.wrap(ErrUnsupportedPci, `pci '${interaction.pciId}'`),
7915
8135
  retriable: false,
7916
8136
  toJSON: poisonToJSON
7917
8137
  };
7918
8138
  }
7919
8139
  }
7920
- return pendingInteractionState(body, stimulus, interaction, feedback);
8140
+ return pendingInteractionState(journey, body, stimulus, interaction, feedback, preservedSubmission, nextSubmissionRole);
7921
8141
  }
7922
- function extendedTextInteractionState(body, stimulus, interaction, feedback) {
8142
+ function extendedTextInteractionState(journey, body, stimulus, interaction, feedback, preservedSubmission, nextSubmissionRole) {
7923
8143
  if (!("cardinality" in interaction)) {
7924
8144
  logger.error("extended-text interaction is unsupported");
7925
8145
  return {
7926
8146
  phase: "fatal",
8147
+ journey,
7927
8148
  error: errors11.wrap(ErrBadRequest, "extended-text interaction unsupported"),
7928
8149
  retriable: false,
7929
8150
  toJSON: poisonToJSON
7930
8151
  };
7931
8152
  }
7932
8153
  const extendedInteraction = interaction;
7933
- return extendedTextState(ctx, body, stimulus, extendedInteraction, feedback);
8154
+ if (preservedSubmission?.type === "extended-text") {
8155
+ if (extendedInteraction.cardinality === "single") {
8156
+ const [initialValue] = preservedSubmission.values;
8157
+ return extendedTextState(ctx, journey, body, stimulus, extendedInteraction, feedback, {
8158
+ initialValue,
8159
+ nextSubmissionRole
8160
+ });
8161
+ }
8162
+ return extendedTextState(ctx, journey, body, stimulus, extendedInteraction, feedback, {
8163
+ initialValues: preservedSubmission.values,
8164
+ nextSubmissionRole
8165
+ });
8166
+ }
8167
+ return extendedTextState(ctx, journey, body, stimulus, extendedInteraction, feedback, {
8168
+ nextSubmissionRole
8169
+ });
7934
8170
  }
7935
- function pendingInteractionState(body, stimulus, interaction, feedback) {
8171
+ function pendingInteractionState(journey, body, stimulus, interaction, feedback, preservedSubmission, nextSubmissionRole) {
7936
8172
  switch (interaction.type) {
7937
8173
  case "choice":
7938
- return choiceState(ctx, body, stimulus, interaction, interaction.options, interaction.maxChoices, interaction.minChoices, feedback);
8174
+ return choiceState(ctx, journey, body, stimulus, interaction, interaction.options, interaction.maxChoices, interaction.minChoices, feedback);
7939
8175
  case "text-entry":
7940
- return textEntryState(ctx, body, stimulus, interaction, feedback);
8176
+ if (preservedSubmission?.type === "text-entry") {
8177
+ return textEntryState(ctx, journey, body, stimulus, interaction, feedback, {
8178
+ initialValue: preservedSubmission.value,
8179
+ nextSubmissionRole
8180
+ });
8181
+ }
8182
+ return textEntryState(ctx, journey, body, stimulus, interaction, feedback, {
8183
+ nextSubmissionRole
8184
+ });
7941
8185
  case "extended-text":
7942
- return extendedTextInteractionState(body, stimulus, interaction, feedback);
8186
+ return extendedTextInteractionState(journey, body, stimulus, interaction, feedback, preservedSubmission, nextSubmissionRole);
7943
8187
  case "order":
7944
- return orderState(ctx, body, stimulus, interaction, feedback);
8188
+ return orderState(ctx, journey, body, stimulus, interaction, feedback);
7945
8189
  case "match":
7946
- return matchState(ctx, body, stimulus, interaction, feedback);
8190
+ return matchState(ctx, journey, body, stimulus, interaction, feedback);
7947
8191
  case "portable-custom":
7948
- return pciInteractionState(ctx, body, stimulus, interaction, feedback);
8192
+ return pciInteractionState(ctx, journey, body, stimulus, interaction, feedback);
7949
8193
  }
7950
8194
  }
7951
8195
  const ctx = { logger, execute, errored };
@@ -7956,11 +8200,21 @@ function makeSession(sc) {
7956
8200
  import * as errors12 from "@superbuilders/errors";
7957
8201
 
7958
8202
  // src/version.ts
7959
- var SDK_VERSION = "5.0.4";
8203
+ var SDK_VERSION = "6.0.0";
7960
8204
  var NPM_PACKAGE_URL = "https://www.npmjs.com/package/@superbuilders/primer-tives";
7961
8205
 
7962
8206
  // src/client/transport.ts
7963
8207
  var ADVANCE_PATH = "/api/v0/advance";
8208
+ function readNumberField(value, key) {
8209
+ if (!(key in value)) {
8210
+ return;
8211
+ }
8212
+ const v = Reflect.get(value, key);
8213
+ if (typeof v !== "number") {
8214
+ return;
8215
+ }
8216
+ return v;
8217
+ }
7964
8218
  function readStringField(value, key) {
7965
8219
  if (!(key in value)) {
7966
8220
  return;
@@ -7994,9 +8248,9 @@ function parseAdvanceErrorBody(body) {
7994
8248
  if (detail !== undefined) {
7995
8249
  result.detail = detail;
7996
8250
  }
7997
- const minimumSdkVersion = readStringField(raw, "minimumSdkVersion");
7998
- if (minimumSdkVersion !== undefined) {
7999
- result.minimumSdkVersion = minimumSdkVersion;
8251
+ const minimumSdkMajor = readNumberField(raw, "minimumSdkMajor");
8252
+ if (minimumSdkMajor !== undefined) {
8253
+ result.minimumSdkMajor = minimumSdkMajor;
8000
8254
  }
8001
8255
  const receivedSdkVersion = readStringField(raw, "receivedSdkVersion");
8002
8256
  if (receivedSdkVersion !== undefined) {
@@ -8069,22 +8323,24 @@ function buildSdkUpgradeRequiredError(sentinel, text, logger) {
8069
8323
  if (parsed === null) {
8070
8324
  logger.error({
8071
8325
  receivedSdkVersion: null,
8072
- minimumSdkVersion: "<unknown>",
8326
+ minimumSdkMajor: "<unknown>",
8073
8327
  upgradeUrl: NPM_PACKAGE_URL
8074
8328
  }, "sdk upgrade required");
8075
- const message2 = `<missing> < <unknown>; bump @superbuilders/primer-tives at ${NPM_PACKAGE_URL}`;
8329
+ const message2 = `major < <unknown>; bump @superbuilders/primer-tives at ${NPM_PACKAGE_URL}`;
8076
8330
  return errors12.wrap(sentinel, message2);
8077
8331
  }
8078
- const minimumSdkVersion = parsed.minimumSdkVersion === undefined ? "<unknown>" : parsed.minimumSdkVersion;
8332
+ let minimumSdkMajor = "<unknown>";
8333
+ if (parsed.minimumSdkMajor !== undefined) {
8334
+ minimumSdkMajor = String(parsed.minimumSdkMajor);
8335
+ }
8079
8336
  const receivedSdkVersion = parsed.receivedSdkVersion === undefined ? null : parsed.receivedSdkVersion;
8080
- const receivedDisplay = receivedSdkVersion === null ? "<missing>" : receivedSdkVersion;
8081
8337
  const upgradeUrl = parsed.upgradeUrl === undefined ? NPM_PACKAGE_URL : parsed.upgradeUrl;
8082
8338
  logger.error({
8083
8339
  receivedSdkVersion,
8084
- minimumSdkVersion,
8340
+ minimumSdkMajor,
8085
8341
  upgradeUrl
8086
8342
  }, "sdk upgrade required");
8087
- const message = `${receivedDisplay} < ${minimumSdkVersion}; bump @superbuilders/primer-tives at ${upgradeUrl}`;
8343
+ const message = `major < ${minimumSdkMajor}; bump @superbuilders/primer-tives at ${upgradeUrl}`;
8088
8344
  return errors12.wrap(sentinel, message);
8089
8345
  }
8090
8346
  function isAbortError(err) {
@@ -8109,7 +8365,8 @@ function createTransport(tc) {
8109
8365
  async function transport(body) {
8110
8366
  logger.debug({
8111
8367
  intentKind: body.intent.kind,
8112
- subject: body.subject
8368
+ subject: body.subject,
8369
+ mode: body.mode
8113
8370
  }, "transport request");
8114
8371
  const fetchResult = await fetchFn(advanceUrl, {
8115
8372
  method: "POST",
@@ -8168,10 +8425,17 @@ function createTransport(tc) {
8168
8425
  }, "transport json parse failed");
8169
8426
  return { ok: false, error: errors12.wrap(ErrJsonParse, jsonResult.error.message) };
8170
8427
  }
8428
+ const journeyResult = parseJourney(jsonResult.data.journey);
8429
+ if (!journeyResult.ok) {
8430
+ logger.error({
8431
+ reason: journeyResult.reason
8432
+ }, "transport journey parse failed");
8433
+ return { ok: false, error: errors12.wrap(ErrBadRequest, journeyResult.reason) };
8434
+ }
8171
8435
  logger.debug({
8172
8436
  intentKind: body.intent.kind
8173
8437
  }, "transport success");
8174
- return { ok: true, data: jsonResult.data };
8438
+ return { ok: true, data: { ...jsonResult.data, journey: journeyResult.journey } };
8175
8439
  }
8176
8440
  return transport;
8177
8441
  }
@@ -8205,15 +8469,16 @@ function primerAuthOrigin(authOrigin, origin) {
8205
8469
  async function startRuntime(config, resolved) {
8206
8470
  let reauthenticate = null;
8207
8471
  if (resolved.clearCachedAccessToken !== undefined) {
8208
- reauthenticate = function reauthenticateManagedAuth(error) {
8472
+ reauthenticate = function reauthenticateManagedAuth(error, journey) {
8209
8473
  resolved.clearCachedAccessToken?.();
8210
- return Promise.resolve(makeSignInFailedState(config, error));
8474
+ return Promise.resolve(makeSignInFailedState(config, error, journey));
8211
8475
  };
8212
8476
  }
8213
8477
  const transport = createTransport({
8214
8478
  accessToken: resolved.accessToken,
8215
8479
  publishableKey: config.publishableKey,
8216
8480
  subject: config.subject,
8481
+ mode: config.mode,
8217
8482
  origin: config.origin,
8218
8483
  fetch: config.fetch,
8219
8484
  abort: config.abort,
@@ -8221,6 +8486,7 @@ async function startRuntime(config, resolved) {
8221
8486
  });
8222
8487
  const session = makeSession({
8223
8488
  subject: config.subject,
8489
+ mode: config.mode,
8224
8490
  supportedPcis: config.supportedPcis,
8225
8491
  logger: config.logger,
8226
8492
  transport,
@@ -8230,13 +8496,15 @@ async function startRuntime(config, resolved) {
8230
8496
  }
8231
8497
  function makeSignInRequiredState(config) {
8232
8498
  return signInRequiredState({
8499
+ journey: ONBOARDING_JOURNEY,
8233
8500
  login: function login() {
8234
8501
  return loginAndStart(config);
8235
8502
  }
8236
8503
  });
8237
8504
  }
8238
- function makeSignInFailedState(config, error) {
8505
+ function makeSignInFailedState(config, error, journey) {
8239
8506
  return signInFailedState({
8507
+ journey,
8240
8508
  error,
8241
8509
  login: function login() {
8242
8510
  return loginAndStart(config);
@@ -8250,13 +8518,13 @@ async function loginAndStart(config) {
8250
8518
  logger: config.logger
8251
8519
  });
8252
8520
  if (result.kind === "auth-unavailable") {
8253
- return authUnavailableState(result.error);
8521
+ return authUnavailableState(result.error, ONBOARDING_JOURNEY);
8254
8522
  }
8255
8523
  if (result.kind === "auth-config-invalid") {
8256
- return authConfigInvalidState(result.error);
8524
+ return authConfigInvalidState(result.error, ONBOARDING_JOURNEY);
8257
8525
  }
8258
8526
  if (result.kind === "sign-in-failed") {
8259
- return makeSignInFailedState(config, result.error);
8527
+ return makeSignInFailedState(config, result.error, ONBOARDING_JOURNEY);
8260
8528
  }
8261
8529
  return startRuntime(config, {
8262
8530
  accessToken: result.accessToken,
@@ -8271,6 +8539,7 @@ async function start(options) {
8271
8539
  publishableKey: options.publishableKey,
8272
8540
  origin,
8273
8541
  authOrigin: primerAuthOrigin(options.authOrigin, origin),
8542
+ mode: options.mode,
8274
8543
  fetch: options.fetch,
8275
8544
  abort: options.abort,
8276
8545
  logger,
@@ -8286,13 +8555,14 @@ async function start(options) {
8286
8555
  if (accessToken.kind === "fatal") {
8287
8556
  return {
8288
8557
  phase: "fatal",
8558
+ journey: ONBOARDING_JOURNEY,
8289
8559
  error: accessToken.error,
8290
8560
  retriable: false,
8291
8561
  toJSON: poisonToJSON
8292
8562
  };
8293
8563
  }
8294
8564
  if (accessToken.kind === "auth-unavailable") {
8295
- return authUnavailableState(accessToken.error);
8565
+ return authUnavailableState(accessToken.error, ONBOARDING_JOURNEY);
8296
8566
  }
8297
8567
  if (accessToken.kind === "missing") {
8298
8568
  return makeSignInRequiredState(config);
@@ -8303,4 +8573,4 @@ export {
8303
8573
  start
8304
8574
  };
8305
8575
 
8306
- //# debugId=B9AA7CC48F32D77C64756E2164756E21
8576
+ //# debugId=E581CF63700F9E9F64756E2164756E21