@conform-ed/qti-react 0.0.18 → 0.0.20

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/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export declare const qtiReactPackageName = "@conform-ed/qti-react";
2
2
  export { v0ContentModel, v0InteractionKinds, isAllowedFlowElement, isInteractionKind, sanitizeAttributes, type ContentModel, type V0InteractionKind, } from "./content-model";
3
3
  export { foldString, mapResponse, matchCorrect, mapResponsePoint, scoreResponse } from "./response-processing";
4
+ export { effectiveItemScore, type EffectiveItemScore } from "./item-score";
4
5
  export { assessmentItemViewFromNormalized, assessmentTestViewFromNormalized, stimulusContentFromNormalized, } from "./normalized-item";
5
6
  export { referenceInteractionKinds, reportItemCapability, type ItemCapabilityOptions } from "./item-capability";
6
7
  export { formatPoint, parseCoords, parsePoint, pointInShape, type Point, type QtiShape } from "./graphic";
@@ -15,3 +16,5 @@ export { associateInteraction, choiceInteraction, drawingInteraction, endAttempt
15
16
  export { AssociateReferenceSkin, ChoiceReferenceSkin, DrawingReferenceSkin, EndAttemptReferenceSkin, ExtendedTextReferenceSkin, GapMatchReferenceSkin, GraphicAssociateReferenceSkin, GraphicGapMatchReferenceSkin, GraphicOrderReferenceSkin, GraphicStage, HotspotReferenceSkin, HottextReferenceSkin, InlineChoiceReferenceSkin, MatchReferenceSkin, MediaReferenceSkin, OrderReferenceSkin, PositionObjectReferenceSkin, SelectPointReferenceSkin, SliderReferenceSkin, TextEntryReferenceSkin, UploadReferenceSkin, referenceSkin, textOf, } from "./reference-skin";
16
17
  export type { AreaMapEntryView, AreaMappingView, Cardinality, CorrectResponseView, MapEntryView, MappingView, ResponseDeclarationView, ResponseValue, ScoreResult, } from "./types";
17
18
  export { resolveCatalogSupports, resolvePnpActivation, type CatalogCardEntryView, type CatalogCardView, type CatalogContentView, type CatalogFileHrefView, type CatalogResolution, type CatalogView, type PnpActivation, type PnpAdditionalTestingTimeView, type PnpFeatureSetView, type PnpLanguageModeView, type PnpReplaceAccessModeView, type PnpView, type ResolvedCatalogSupport, } from "./pnp";
19
+ export { cardinalitySchema, interpolationTableSchema, matchTableSchema, outcomeDeclarationSchema, rpExpressionSchema, rpScalarSchema, type OutcomeDeclarationSchema, type RpExpressionSchema, type RpScalarSchema, } from "./rp/schema";
20
+ export { assessmentItemRefViewSchema, assessmentTestViewSchema, branchRuleSchema, itemSessionControlSchema, makeAssessmentTestSchema, orderingSchema, outcomeConditionBranchSchema, outcomeProcessingSchema, outcomeRuleSchema, selectionSchema, testFeedbackSchema, timeLimitsSchema, type AssessmentItemRefViewSchema, type AssessmentSectionNode, type AssessmentTestNode, type AssessmentTestSchemas, type AssessmentTestViewSchema, type BranchRuleSchema, type ItemSessionControlSchema, type OrderingSchema, type SelectionSchema, type TestPartNode, type TimeLimitsSchema, } from "./test/schema";
package/dist/index.js CHANGED
@@ -387,6 +387,23 @@ function scoreResponse(declaration, response, normalize) {
387
387
  correct
388
388
  };
389
389
  }
390
+ // src/item-score.ts
391
+ function numericOutcome(value) {
392
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
393
+ }
394
+ function effectiveItemScore(scores, outcomes) {
395
+ const scoreOutcome = numericOutcome(outcomes["SCORE"]);
396
+ const maxOutcome = numericOutcome(outcomes["MAXSCORE"]);
397
+ const summedMax = scores.reduce((total, score) => total + score.maxScore, 0);
398
+ if (scoreOutcome !== null) {
399
+ return { raw: scoreOutcome, max: maxOutcome ?? summedMax, fromOutcomes: true };
400
+ }
401
+ return {
402
+ raw: scores.reduce((total, score) => total + score.score, 0),
403
+ max: maxOutcome ?? summedMax,
404
+ fromOutcomes: false
405
+ };
406
+ }
390
407
  // src/normalized-item.ts
391
408
  function isRecord(value) {
392
409
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -6783,6 +6800,177 @@ var referenceSkin = {
6783
6800
  textEntryInteraction: TextEntryReferenceSkin,
6784
6801
  uploadInteraction: UploadReferenceSkin
6785
6802
  };
6803
+ // src/rp/schema.ts
6804
+ import { z as z18 } from "zod";
6805
+ var rpScalarSchema = z18.union([z18.string(), z18.number(), z18.boolean()]);
6806
+ var numberOrRef = z18.union([z18.number(), z18.string()]);
6807
+ var categoryFilter = z18.union([z18.string(), z18.array(z18.string())]);
6808
+ var cardinalitySchema = z18.enum(["single", "multiple", "ordered", "record"]);
6809
+ var rpExpressionSchema = z18.lazy(() => z18.object({
6810
+ kind: z18.string(),
6811
+ identifier: z18.string().optional(),
6812
+ baseType: z18.string().optional(),
6813
+ value: rpScalarSchema.optional(),
6814
+ expressions: z18.array(rpExpressionSchema).optional(),
6815
+ min: numberOrRef.optional(),
6816
+ max: numberOrRef.optional(),
6817
+ step: numberOrRef.optional(),
6818
+ toleranceMode: z18.enum(["exact", "absolute", "relative"]).optional(),
6819
+ tolerance: z18.array(numberOrRef).optional(),
6820
+ includeLowerBound: z18.boolean().optional(),
6821
+ includeUpperBound: z18.boolean().optional(),
6822
+ n: numberOrRef.optional(),
6823
+ name: z18.string().optional(),
6824
+ roundingMode: z18.enum(["decimalPlaces", "significantFigures"]).optional(),
6825
+ figures: numberOrRef.optional(),
6826
+ numberRepeats: numberOrRef.optional(),
6827
+ pattern: z18.string().optional(),
6828
+ caseSensitive: z18.boolean().optional(),
6829
+ substring: z18.boolean().optional(),
6830
+ shape: z18.string().optional(),
6831
+ coords: z18.string().optional(),
6832
+ variableIdentifier: z18.string().optional(),
6833
+ outcomeIdentifier: z18.string().optional(),
6834
+ weightIdentifier: z18.string().optional(),
6835
+ sectionIdentifier: z18.string().optional(),
6836
+ includeCategory: categoryFilter.optional(),
6837
+ excludeCategory: categoryFilter.optional(),
6838
+ class: z18.string().optional(),
6839
+ definition: z18.string().optional(),
6840
+ fieldIdentifier: z18.string().optional()
6841
+ }));
6842
+ var matchTableSchema = z18.object({
6843
+ defaultValue: rpScalarSchema.optional(),
6844
+ matchTableEntries: z18.array(z18.object({ sourceValue: z18.number(), targetValue: rpScalarSchema }))
6845
+ });
6846
+ var interpolationTableSchema = z18.object({
6847
+ defaultValue: rpScalarSchema.optional(),
6848
+ interpolationTableEntries: z18.array(z18.object({
6849
+ sourceValue: z18.number(),
6850
+ targetValue: rpScalarSchema,
6851
+ includeBoundary: z18.boolean().optional()
6852
+ }))
6853
+ });
6854
+ var outcomeDeclarationSchema = z18.object({
6855
+ identifier: z18.string(),
6856
+ cardinality: cardinalitySchema,
6857
+ baseType: z18.string().optional(),
6858
+ defaultValue: z18.object({ values: z18.array(z18.object({ value: rpScalarSchema })) }).optional(),
6859
+ matchTable: matchTableSchema.optional(),
6860
+ interpolationTable: interpolationTableSchema.optional(),
6861
+ normalMaximum: z18.number().optional(),
6862
+ normalMinimum: z18.number().optional()
6863
+ });
6864
+ // src/test/schema.ts
6865
+ import { z as z19 } from "zod";
6866
+ var timeLimitsSchema = z19.object({
6867
+ minTime: z19.number().nonnegative().optional(),
6868
+ maxTime: z19.number().nonnegative().optional(),
6869
+ allowLateSubmission: z19.boolean().optional()
6870
+ });
6871
+ var itemSessionControlSchema = z19.object({
6872
+ maxAttempts: z19.number().int().nonnegative().optional(),
6873
+ showFeedback: z19.boolean().optional(),
6874
+ allowReview: z19.boolean().optional(),
6875
+ showSolution: z19.boolean().optional(),
6876
+ allowComment: z19.boolean().optional(),
6877
+ allowSkipping: z19.boolean().optional(),
6878
+ validateResponses: z19.boolean().optional()
6879
+ });
6880
+ var selectionSchema = z19.object({
6881
+ select: z19.number().int().nonnegative(),
6882
+ withReplacement: z19.boolean().optional()
6883
+ });
6884
+ var orderingSchema = z19.object({ shuffle: z19.boolean().optional() });
6885
+ var branchRuleSchema = z19.object({
6886
+ target: z19.string().trim().min(1),
6887
+ expression: rpExpressionSchema
6888
+ });
6889
+ var weightSchema = z19.object({ identifier: z19.string().trim().min(1), value: z19.number() });
6890
+ var templateDefaultSchema = z19.object({
6891
+ templateIdentifier: z19.string().trim().min(1),
6892
+ expression: rpExpressionSchema
6893
+ });
6894
+ var outcomeConditionBranchSchema = z19.lazy(() => z19.object({
6895
+ expression: rpExpressionSchema,
6896
+ rules: z19.array(outcomeRuleSchema)
6897
+ }));
6898
+ var outcomeRuleSchema = z19.lazy(() => z19.object({
6899
+ kind: z19.string(),
6900
+ identifier: z19.string().optional(),
6901
+ expression: rpExpressionSchema.optional(),
6902
+ rules: z19.array(outcomeRuleSchema).optional(),
6903
+ outcomeIf: outcomeConditionBranchSchema.optional(),
6904
+ outcomeElseIfs: z19.array(outcomeConditionBranchSchema).optional(),
6905
+ outcomeElse: z19.object({ rules: z19.array(outcomeRuleSchema) }).optional()
6906
+ }));
6907
+ var outcomeProcessingSchema = z19.object({ rules: z19.array(outcomeRuleSchema) });
6908
+ var testFeedbackSchema = z19.object({
6909
+ access: z19.enum(["atEnd", "during"]).optional(),
6910
+ outcomeIdentifier: z19.string().trim().min(1),
6911
+ identifier: z19.string().trim().min(1),
6912
+ showHide: z19.enum(["show", "hide"]).optional(),
6913
+ content: z19.array(z19.unknown()).optional()
6914
+ });
6915
+ var assessmentItemRefViewSchema = z19.object({
6916
+ kind: z19.literal("assessmentItemRef"),
6917
+ identifier: z19.string().trim().min(1),
6918
+ href: z19.string().optional(),
6919
+ categories: z19.array(z19.string()).optional(),
6920
+ fixed: z19.boolean().optional(),
6921
+ required: z19.boolean().optional(),
6922
+ preConditions: z19.array(rpExpressionSchema).optional(),
6923
+ branchRules: z19.array(branchRuleSchema).optional(),
6924
+ itemSessionControl: itemSessionControlSchema.optional(),
6925
+ timeLimits: timeLimitsSchema.optional(),
6926
+ weights: z19.array(weightSchema).optional(),
6927
+ templateDefaults: z19.array(templateDefaultSchema).optional()
6928
+ });
6929
+ var assessmentSectionFlags = {
6930
+ title: z19.string().trim().min(1).optional(),
6931
+ visible: z19.boolean().optional(),
6932
+ fixed: z19.boolean().optional(),
6933
+ required: z19.boolean().optional(),
6934
+ keepTogether: z19.boolean().optional(),
6935
+ selection: selectionSchema.optional(),
6936
+ ordering: orderingSchema.optional(),
6937
+ preConditions: z19.array(rpExpressionSchema).optional(),
6938
+ branchRules: z19.array(branchRuleSchema).optional(),
6939
+ itemSessionControl: itemSessionControlSchema.optional(),
6940
+ timeLimits: timeLimitsSchema.optional()
6941
+ };
6942
+ var testPartLevel = {
6943
+ preConditions: z19.array(rpExpressionSchema).optional(),
6944
+ branchRules: z19.array(branchRuleSchema).optional(),
6945
+ itemSessionControl: itemSessionControlSchema.optional(),
6946
+ timeLimits: timeLimitsSchema.optional()
6947
+ };
6948
+ function makeAssessmentTestSchema(itemRefSchema) {
6949
+ const assessmentSectionSchema = z19.lazy(() => z19.object({
6950
+ kind: z19.literal("assessmentSection"),
6951
+ identifier: z19.string().trim().min(1),
6952
+ ...assessmentSectionFlags,
6953
+ children: z19.array(z19.union([assessmentSectionSchema, itemRefSchema]))
6954
+ }));
6955
+ const testPartSchema = z19.object({
6956
+ identifier: z19.string().trim().min(1),
6957
+ navigationMode: z19.enum(["linear", "nonlinear"]),
6958
+ submissionMode: z19.enum(["individual", "simultaneous"]),
6959
+ ...testPartLevel,
6960
+ assessmentSections: z19.array(assessmentSectionSchema)
6961
+ });
6962
+ const assessmentTestSchema = z19.object({
6963
+ identifier: z19.string().trim().min(1),
6964
+ title: z19.string().trim().min(1).optional(),
6965
+ outcomeDeclarations: z19.array(outcomeDeclarationSchema).optional(),
6966
+ timeLimits: timeLimitsSchema.optional(),
6967
+ testParts: z19.array(testPartSchema),
6968
+ outcomeProcessing: outcomeProcessingSchema.optional(),
6969
+ testFeedbacks: z19.array(testFeedbackSchema).optional()
6970
+ });
6971
+ return { assessmentSectionSchema, testPartSchema, assessmentTestSchema };
6972
+ }
6973
+ var assessmentTestViewSchema = makeAssessmentTestSchema(assessmentItemRefViewSchema).assessmentTestSchema;
6786
6974
 
6787
6975
  // src/index.ts
6788
6976
  var qtiReactPackageName = "@conform-ed/qti-react";
@@ -6791,14 +6979,19 @@ export {
6791
6979
  v0InteractionKinds,
6792
6980
  v0ContentModel,
6793
6981
  uploadInteraction,
6982
+ timeLimitsSchema,
6794
6983
  textOf,
6795
6984
  textEntryInteraction,
6985
+ testFeedbackSchema,
6796
6986
  stimulusContentFromNormalized,
6797
6987
  sliderInteraction,
6798
6988
  serializePciMarkup,
6989
+ selectionSchema,
6799
6990
  selectPointInteraction,
6800
6991
  scoreResponse,
6801
6992
  sanitizeAttributes,
6993
+ rpScalarSchema,
6994
+ rpExpressionSchema,
6802
6995
  resolveTemplate,
6803
6996
  resolvePnpActivation,
6804
6997
  resolveCatalogSupports,
@@ -6813,16 +7006,25 @@ export {
6813
7006
  pciResponseToValue,
6814
7007
  parsePoint,
6815
7008
  parseCoords,
7009
+ outcomeRuleSchema,
7010
+ outcomeProcessingSchema,
7011
+ outcomeDeclarationSchema,
7012
+ outcomeConditionBranchSchema,
7013
+ orderingSchema,
6816
7014
  orderInteraction,
6817
7015
  mulberry32,
6818
7016
  mountPci,
6819
7017
  mediaInteraction,
7018
+ matchTableSchema,
6820
7019
  matchInteraction,
6821
7020
  matchCorrect,
6822
7021
  mapResponsePoint,
6823
7022
  mapResponse,
7023
+ makeAssessmentTestSchema,
7024
+ itemSessionControlSchema,
6824
7025
  isInteractionKind,
6825
7026
  isAllowedFlowElement,
7027
+ interpolationTableSchema,
6826
7028
  inlineChoiceInteraction,
6827
7029
  hottextInteraction,
6828
7030
  hotspotInteraction,
@@ -6836,6 +7038,7 @@ export {
6836
7038
  executeTemplateProcessing,
6837
7039
  executeResponseProcessing,
6838
7040
  endAttemptInteraction,
7041
+ effectiveItemScore,
6839
7042
  drawingInteraction,
6840
7043
  defineInteraction,
6841
7044
  createTestSessionStore,
@@ -6849,9 +7052,13 @@ export {
6849
7052
  collectResponseViolations,
6850
7053
  collectInteractionConstraints,
6851
7054
  choiceInteraction,
7055
+ cardinalitySchema,
7056
+ branchRuleSchema,
6852
7057
  associateInteraction,
7058
+ assessmentTestViewSchema,
6853
7059
  assessmentTestViewFromNormalized,
6854
7060
  assessmentItemViewFromNormalized,
7061
+ assessmentItemRefViewSchema,
6855
7062
  applyCorrectResponseOverrides,
6856
7063
  UploadReferenceSkin,
6857
7064
  TextEntryReferenceSkin,
@@ -0,0 +1,17 @@
1
+ import type { ScoreResult } from "./types";
2
+ export interface EffectiveItemScore {
3
+ readonly raw: number;
4
+ readonly max: number;
5
+ /** True when SCORE came from the RP outcomes of record rather than per-variable scoring. */
6
+ readonly fromOutcomes: boolean;
7
+ }
8
+ /**
9
+ * The item score of record (QTI): a numeric SCORE outcome from response processing is
10
+ * authoritative — PCI/RP-scored items (e.g. math-entry) have no per-variable
11
+ * correctResponse basis, so their standard scores read 0. Summed per-variable standard
12
+ * scoring is the fallback for items without RP. MAXSCORE follows the same precedence.
13
+ *
14
+ * Pure and framework-light: client and server (authoritative finalize) share it so the
15
+ * grade of record is derived identically on both sides.
16
+ */
17
+ export declare function effectiveItemScore(scores: readonly ScoreResult[], outcomes: Readonly<Record<string, unknown>>): EffectiveItemScore;
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Zod mirrors of the Response-Processing view types (./types.ts). conform-ed owns the
3
+ * QTI view *types*; this is the validation surface that sits beside them — reusable for
4
+ * import/parse validation and kept honest against the types it mirrors. React-free.
5
+ *
6
+ * The expression union is deliberately permissive (`kind: z.string()`), exactly like
7
+ * `RpExpressionView`: kinds the interpreter does not implement still validate structurally
8
+ * and surface as `unsupported-rp` issues at evaluation time rather than being rejected here.
9
+ *
10
+ * The recursive schema is annotated against an explicit interface (`RpExpressionNode`); under
11
+ * `exactOptionalPropertyTypes` zod emits `T | undefined` optionals, so `z.infer` is a
12
+ * superset of the hand-written views (every view value parses — verified in the schema
13
+ * tests against real view samples). Callers that need the exact view shape map explicitly.
14
+ */
15
+ import { z } from "zod";
16
+ import type { RpScalar } from "./types";
17
+ /** string | number | boolean — the QTI scalar value space (NULL is modelled by absence). */
18
+ export declare const rpScalarSchema: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>;
19
+ /**
20
+ * The inferred shape of `rpExpressionSchema` — a faithful, recursive mirror of
21
+ * `RpExpressionView` with zod's `T | undefined` optionals. Declared explicitly (and the
22
+ * schema is annotated with it) because zod's getter-based recursion degrades the recursive
23
+ * `expressions` field to `Record<string, unknown>` under `z.infer`; the `z.lazy` + interface
24
+ * form keeps the type precise (the expression-builder authoring relies on it).
25
+ */
26
+ export interface RpExpressionNode {
27
+ readonly kind: string;
28
+ readonly identifier?: string | undefined;
29
+ readonly baseType?: string | undefined;
30
+ readonly value?: RpScalar | undefined;
31
+ readonly expressions?: readonly RpExpressionNode[] | undefined;
32
+ readonly min?: number | string | undefined;
33
+ readonly max?: number | string | undefined;
34
+ readonly step?: number | string | undefined;
35
+ readonly toleranceMode?: "exact" | "absolute" | "relative" | undefined;
36
+ readonly tolerance?: readonly (number | string)[] | undefined;
37
+ readonly includeLowerBound?: boolean | undefined;
38
+ readonly includeUpperBound?: boolean | undefined;
39
+ readonly n?: number | string | undefined;
40
+ readonly name?: string | undefined;
41
+ readonly roundingMode?: "decimalPlaces" | "significantFigures" | undefined;
42
+ readonly figures?: number | string | undefined;
43
+ readonly numberRepeats?: number | string | undefined;
44
+ readonly pattern?: string | undefined;
45
+ readonly caseSensitive?: boolean | undefined;
46
+ readonly substring?: boolean | undefined;
47
+ readonly shape?: string | undefined;
48
+ readonly coords?: string | undefined;
49
+ readonly variableIdentifier?: string | undefined;
50
+ readonly outcomeIdentifier?: string | undefined;
51
+ readonly weightIdentifier?: string | undefined;
52
+ readonly sectionIdentifier?: string | undefined;
53
+ readonly includeCategory?: string | readonly string[] | undefined;
54
+ readonly excludeCategory?: string | readonly string[] | undefined;
55
+ readonly class?: string | undefined;
56
+ readonly definition?: string | undefined;
57
+ readonly fieldIdentifier?: string | undefined;
58
+ }
59
+ export declare const cardinalitySchema: z.ZodEnum<{
60
+ multiple: "multiple";
61
+ ordered: "ordered";
62
+ record: "record";
63
+ single: "single";
64
+ }>;
65
+ /** The recursive expression schema (`z.lazy` recursion; annotated for a precise `z.infer`). */
66
+ export declare const rpExpressionSchema: z.ZodType<RpExpressionNode>;
67
+ export declare const matchTableSchema: z.ZodObject<{
68
+ defaultValue: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
69
+ matchTableEntries: z.ZodArray<z.ZodObject<{
70
+ sourceValue: z.ZodNumber;
71
+ targetValue: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>;
72
+ }, z.core.$strip>>;
73
+ }, z.core.$strip>;
74
+ export declare const interpolationTableSchema: z.ZodObject<{
75
+ defaultValue: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
76
+ interpolationTableEntries: z.ZodArray<z.ZodObject<{
77
+ sourceValue: z.ZodNumber;
78
+ targetValue: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>;
79
+ includeBoundary: z.ZodOptional<z.ZodBoolean>;
80
+ }, z.core.$strip>>;
81
+ }, z.core.$strip>;
82
+ export declare const outcomeDeclarationSchema: z.ZodObject<{
83
+ identifier: z.ZodString;
84
+ cardinality: z.ZodEnum<{
85
+ multiple: "multiple";
86
+ ordered: "ordered";
87
+ record: "record";
88
+ single: "single";
89
+ }>;
90
+ baseType: z.ZodOptional<z.ZodString>;
91
+ defaultValue: z.ZodOptional<z.ZodObject<{
92
+ values: z.ZodArray<z.ZodObject<{
93
+ value: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>;
94
+ }, z.core.$strip>>;
95
+ }, z.core.$strip>>;
96
+ matchTable: z.ZodOptional<z.ZodObject<{
97
+ defaultValue: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
98
+ matchTableEntries: z.ZodArray<z.ZodObject<{
99
+ sourceValue: z.ZodNumber;
100
+ targetValue: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>;
101
+ }, z.core.$strip>>;
102
+ }, z.core.$strip>>;
103
+ interpolationTable: z.ZodOptional<z.ZodObject<{
104
+ defaultValue: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
105
+ interpolationTableEntries: z.ZodArray<z.ZodObject<{
106
+ sourceValue: z.ZodNumber;
107
+ targetValue: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>;
108
+ includeBoundary: z.ZodOptional<z.ZodBoolean>;
109
+ }, z.core.$strip>>;
110
+ }, z.core.$strip>>;
111
+ normalMaximum: z.ZodOptional<z.ZodNumber>;
112
+ normalMinimum: z.ZodOptional<z.ZodNumber>;
113
+ }, z.core.$strip>;
114
+ export type RpScalarSchema = z.infer<typeof rpScalarSchema>;
115
+ export type RpExpressionSchema = z.infer<typeof rpExpressionSchema>;
116
+ export type OutcomeDeclarationSchema = z.infer<typeof outcomeDeclarationSchema>;
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Zod mirrors of the `assessmentTest` structural views (./types.ts) — the shared QTI
3
+ * atoms (`timeLimits`, `itemSessionControl`, `selection`, `ordering`, `branchRule`,
4
+ * test-level outcome processing/feedback) and the recursive structure
5
+ * (`assessmentSection` / `testPart` / `assessmentTest`).
6
+ *
7
+ * The structure is exposed as a **factory parameterized by the itemRef schema** —
8
+ * `makeAssessmentTestSchema(itemRefSchema)` — because the only thing that varies between
9
+ * delivery (an `href`) and authoring (an `itemVersionId`) is the item reference's identity.
10
+ * The ready `assessmentTestViewSchema` binds the delivery `href` itemRef; emergent binds
11
+ * its authoring itemRef. React-free; see ../rp/schema.ts for the RP-level mirrors.
12
+ */
13
+ import { z } from "zod";
14
+ import { type OutcomeDeclarationSchema, type RpExpressionSchema } from "../rp/schema";
15
+ /** `timeLimits` (seconds); the controller enforces these under its injected clock. */
16
+ export declare const timeLimitsSchema: z.ZodObject<{
17
+ minTime: z.ZodOptional<z.ZodNumber>;
18
+ maxTime: z.ZodOptional<z.ZodNumber>;
19
+ allowLateSubmission: z.ZodOptional<z.ZodBoolean>;
20
+ }, z.core.$strip>;
21
+ /** `itemSessionControl`: per-level overrides cascading testPart → section → itemRef. */
22
+ export declare const itemSessionControlSchema: z.ZodObject<{
23
+ maxAttempts: z.ZodOptional<z.ZodNumber>;
24
+ showFeedback: z.ZodOptional<z.ZodBoolean>;
25
+ allowReview: z.ZodOptional<z.ZodBoolean>;
26
+ showSolution: z.ZodOptional<z.ZodBoolean>;
27
+ allowComment: z.ZodOptional<z.ZodBoolean>;
28
+ allowSkipping: z.ZodOptional<z.ZodBoolean>;
29
+ validateResponses: z.ZodOptional<z.ZodBoolean>;
30
+ }, z.core.$strip>;
31
+ /** `selection`: how many children to draw, optionally with replacement (§4.2.6). */
32
+ export declare const selectionSchema: z.ZodObject<{
33
+ select: z.ZodNumber;
34
+ withReplacement: z.ZodOptional<z.ZodBoolean>;
35
+ }, z.core.$strip>;
36
+ /** `ordering`: whether the surviving children are shuffled (§4.2.7). */
37
+ export declare const orderingSchema: z.ZodObject<{
38
+ shuffle: z.ZodOptional<z.ZodBoolean>;
39
+ }, z.core.$strip>;
40
+ export type TimeLimitsSchema = z.infer<typeof timeLimitsSchema>;
41
+ export type ItemSessionControlSchema = z.infer<typeof itemSessionControlSchema>;
42
+ export type SelectionSchema = z.infer<typeof selectionSchema>;
43
+ export type OrderingSchema = z.infer<typeof orderingSchema>;
44
+ /** `branchRule`: a target identifier (or EXIT_*) gated by a boolean expression. */
45
+ export declare const branchRuleSchema: z.ZodObject<{
46
+ target: z.ZodString;
47
+ expression: z.ZodType<import("../rp/schema").RpExpressionNode, unknown, z.core.$ZodTypeInternals<import("../rp/schema").RpExpressionNode, unknown>>;
48
+ }, z.core.$strip>;
49
+ export type BranchRuleSchema = z.infer<typeof branchRuleSchema>;
50
+ /** A gate plus its nested rules (`outcomeIf` / `outcomeElseIf`); see `OutcomeRuleNode`. */
51
+ export interface OutcomeConditionBranchNode {
52
+ readonly expression: RpExpressionSchema;
53
+ readonly rules: readonly OutcomeRuleNode[];
54
+ }
55
+ /**
56
+ * The recursive outcome-rule shape (a faithful mirror of `OutcomeRuleView` with zod's
57
+ * `T | undefined` optionals). Declared explicitly so `z.infer` stays precise through the
58
+ * recursion (the getter form degrades nested rules to `Record<string, unknown>`).
59
+ */
60
+ export interface OutcomeRuleNode {
61
+ readonly kind: string;
62
+ readonly identifier?: string | undefined;
63
+ readonly expression?: RpExpressionSchema | undefined;
64
+ readonly rules?: readonly OutcomeRuleNode[] | undefined;
65
+ readonly outcomeIf?: OutcomeConditionBranchNode | undefined;
66
+ readonly outcomeElseIfs?: readonly OutcomeConditionBranchNode[] | undefined;
67
+ readonly outcomeElse?: {
68
+ readonly rules: readonly OutcomeRuleNode[];
69
+ } | undefined;
70
+ }
71
+ /** One `outcomeIf` / `outcomeElseIf` branch: a gate plus its nested rules. */
72
+ export declare const outcomeConditionBranchSchema: z.ZodType<OutcomeConditionBranchNode>;
73
+ /** A recursive outcome rule (mirrors `OutcomeRuleView`); `kind` stays permissive. */
74
+ export declare const outcomeRuleSchema: z.ZodType<OutcomeRuleNode>;
75
+ export declare const outcomeProcessingSchema: z.ZodObject<{
76
+ rules: z.ZodArray<z.ZodType<OutcomeRuleNode, unknown, z.core.$ZodTypeInternals<OutcomeRuleNode, unknown>>>;
77
+ }, z.core.$strip>;
78
+ export type OutcomeProcessingSchema = z.infer<typeof outcomeProcessingSchema>;
79
+ /**
80
+ * `testFeedback` — the scalar attributes are mirrored; the `content` body is accepted
81
+ * opaquely (BodyNode is the item runtime's content model, not authored at the test level).
82
+ */
83
+ export declare const testFeedbackSchema: z.ZodObject<{
84
+ access: z.ZodOptional<z.ZodEnum<{
85
+ atEnd: "atEnd";
86
+ during: "during";
87
+ }>>;
88
+ outcomeIdentifier: z.ZodString;
89
+ identifier: z.ZodString;
90
+ showHide: z.ZodOptional<z.ZodEnum<{
91
+ hide: "hide";
92
+ show: "show";
93
+ }>>;
94
+ content: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
95
+ }, z.core.$strip>;
96
+ export type TestFeedbackSchema = z.infer<typeof testFeedbackSchema>;
97
+ /** The delivery `assessmentItemRef` (`href` identity) — the ready view itemRef. */
98
+ export declare const assessmentItemRefViewSchema: z.ZodObject<{
99
+ kind: z.ZodLiteral<"assessmentItemRef">;
100
+ identifier: z.ZodString;
101
+ href: z.ZodOptional<z.ZodString>;
102
+ categories: z.ZodOptional<z.ZodArray<z.ZodString>>;
103
+ fixed: z.ZodOptional<z.ZodBoolean>;
104
+ required: z.ZodOptional<z.ZodBoolean>;
105
+ preConditions: z.ZodOptional<z.ZodArray<z.ZodType<import("../rp/schema").RpExpressionNode, unknown, z.core.$ZodTypeInternals<import("../rp/schema").RpExpressionNode, unknown>>>>;
106
+ branchRules: z.ZodOptional<z.ZodArray<z.ZodObject<{
107
+ target: z.ZodString;
108
+ expression: z.ZodType<import("../rp/schema").RpExpressionNode, unknown, z.core.$ZodTypeInternals<import("../rp/schema").RpExpressionNode, unknown>>;
109
+ }, z.core.$strip>>>;
110
+ itemSessionControl: z.ZodOptional<z.ZodObject<{
111
+ maxAttempts: z.ZodOptional<z.ZodNumber>;
112
+ showFeedback: z.ZodOptional<z.ZodBoolean>;
113
+ allowReview: z.ZodOptional<z.ZodBoolean>;
114
+ showSolution: z.ZodOptional<z.ZodBoolean>;
115
+ allowComment: z.ZodOptional<z.ZodBoolean>;
116
+ allowSkipping: z.ZodOptional<z.ZodBoolean>;
117
+ validateResponses: z.ZodOptional<z.ZodBoolean>;
118
+ }, z.core.$strip>>;
119
+ timeLimits: z.ZodOptional<z.ZodObject<{
120
+ minTime: z.ZodOptional<z.ZodNumber>;
121
+ maxTime: z.ZodOptional<z.ZodNumber>;
122
+ allowLateSubmission: z.ZodOptional<z.ZodBoolean>;
123
+ }, z.core.$strip>>;
124
+ weights: z.ZodOptional<z.ZodArray<z.ZodObject<{
125
+ identifier: z.ZodString;
126
+ value: z.ZodNumber;
127
+ }, z.core.$strip>>>;
128
+ templateDefaults: z.ZodOptional<z.ZodArray<z.ZodObject<{
129
+ templateIdentifier: z.ZodString;
130
+ expression: z.ZodType<import("../rp/schema").RpExpressionNode, unknown, z.core.$ZodTypeInternals<import("../rp/schema").RpExpressionNode, unknown>>;
131
+ }, z.core.$strip>>>;
132
+ }, z.core.$strip>;
133
+ /**
134
+ * The recursive section shape, parameterized by the injected itemRef's inferred type.
135
+ * Optionals carry `| undefined` and arrays are `readonly` so this matches zod's object
136
+ * output exactly — letting the `z.lazy` section schema below be annotated `z.ZodType<…>`
137
+ * (a getter cannot self-infer once a generic itemRef joins the `children` union).
138
+ */
139
+ export interface AssessmentSectionNode<TItemRef> {
140
+ readonly kind: "assessmentSection";
141
+ readonly identifier: string;
142
+ readonly title?: string | undefined;
143
+ readonly visible?: boolean | undefined;
144
+ readonly fixed?: boolean | undefined;
145
+ readonly required?: boolean | undefined;
146
+ readonly keepTogether?: boolean | undefined;
147
+ readonly selection?: SelectionSchema | undefined;
148
+ readonly ordering?: OrderingSchema | undefined;
149
+ readonly preConditions?: readonly RpExpressionSchema[] | undefined;
150
+ readonly branchRules?: readonly BranchRuleSchema[] | undefined;
151
+ readonly itemSessionControl?: ItemSessionControlSchema | undefined;
152
+ readonly timeLimits?: TimeLimitsSchema | undefined;
153
+ readonly children: ReadonlyArray<AssessmentSectionNode<TItemRef> | TItemRef>;
154
+ }
155
+ /** A test part: navigation/submission modes plus the cascade levels and its sections. */
156
+ export interface TestPartNode<TItemRef> {
157
+ readonly identifier: string;
158
+ readonly navigationMode: "linear" | "nonlinear";
159
+ readonly submissionMode: "individual" | "simultaneous";
160
+ readonly preConditions?: readonly RpExpressionSchema[] | undefined;
161
+ readonly branchRules?: readonly BranchRuleSchema[] | undefined;
162
+ readonly itemSessionControl?: ItemSessionControlSchema | undefined;
163
+ readonly timeLimits?: TimeLimitsSchema | undefined;
164
+ readonly assessmentSections: readonly AssessmentSectionNode<TItemRef>[];
165
+ }
166
+ /** The whole `assessmentTest`: declarations/processing/feedback plus the ordered parts. */
167
+ export interface AssessmentTestNode<TItemRef> {
168
+ readonly identifier: string;
169
+ readonly title?: string | undefined;
170
+ readonly outcomeDeclarations?: readonly OutcomeDeclarationSchema[] | undefined;
171
+ readonly timeLimits?: TimeLimitsSchema | undefined;
172
+ readonly testParts: readonly TestPartNode<TItemRef>[];
173
+ readonly outcomeProcessing?: OutcomeProcessingSchema | undefined;
174
+ readonly testFeedbacks?: readonly TestFeedbackSchema[] | undefined;
175
+ }
176
+ /** The schema bundle the factory returns (each level parameterized by the itemRef type). */
177
+ export interface AssessmentTestSchemas<TItemRef> {
178
+ readonly assessmentSectionSchema: z.ZodType<AssessmentSectionNode<TItemRef>>;
179
+ readonly testPartSchema: z.ZodType<TestPartNode<TItemRef>>;
180
+ readonly assessmentTestSchema: z.ZodType<AssessmentTestNode<TItemRef>>;
181
+ }
182
+ /**
183
+ * Build the recursive `assessmentTest` schema around a caller-supplied itemRef schema.
184
+ * Sections nest sections-or-itemRefs through `children` (recursion via `z.lazy`); `z.infer`
185
+ * follows the recursion and carries the injected itemRef's inferred type. The explicit
186
+ * return type keeps the package's `isolatedDeclarations` build able to emit the bundle.
187
+ */
188
+ export declare function makeAssessmentTestSchema<ItemRef extends z.ZodType>(itemRefSchema: ItemRef): AssessmentTestSchemas<z.infer<ItemRef>>;
189
+ /** The ready delivery-view schema: the recursive structure over the `href` itemRef. */
190
+ export declare const assessmentTestViewSchema: z.ZodType<AssessmentTestNode<AssessmentItemRefViewSchema>>;
191
+ export type AssessmentItemRefViewSchema = z.infer<typeof assessmentItemRefViewSchema>;
192
+ export type AssessmentTestViewSchema = z.infer<typeof assessmentTestViewSchema>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@conform-ed/qti-react",
3
- "version": "0.0.18",
3
+ "version": "0.0.20",
4
4
  "files": [
5
5
  "src",
6
6
  "dist"
@@ -30,11 +30,11 @@
30
30
  "xspattern": "^3.1.0"
31
31
  },
32
32
  "devDependencies": {
33
- "@conform-ed/contracts": "0.0.18",
34
- "@conform-ed/qti-xml": "0.0.18",
33
+ "@conform-ed/contracts": "0.0.20",
34
+ "@conform-ed/qti-xml": "0.0.20",
35
35
  "@types/react": "^19.2.17",
36
36
  "@types/react-dom": "^19",
37
- "happy-dom": "^20.10.2",
37
+ "happy-dom": "^20.10.3",
38
38
  "react": "^19.2.7",
39
39
  "react-dom": "^19"
40
40
  },