@intellectif/lk-core 1.2.0 → 1.4.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 (105) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +2 -2
  3. package/dist/ai-CnmLPMzd.d.cts +2417 -0
  4. package/dist/ai-CnmLPMzd.d.cts.map +1 -0
  5. package/dist/ai-CnmLPMzd.d.ts +2417 -0
  6. package/dist/ai-CnmLPMzd.d.ts.map +1 -0
  7. package/dist/ai-check.cjs +845 -620
  8. package/dist/ai-check.cjs.map +1 -1
  9. package/dist/ai-check.d.cts +82 -80
  10. package/dist/ai-check.d.cts.map +1 -0
  11. package/dist/ai-check.d.ts +82 -80
  12. package/dist/ai-check.d.ts.map +1 -0
  13. package/dist/ai-check.js +843 -621
  14. package/dist/ai-check.js.map +1 -1
  15. package/dist/ai-writing-CPpH9Qxq.js +2601 -0
  16. package/dist/ai-writing-CPpH9Qxq.js.map +1 -0
  17. package/dist/ai-writing-G6tnCGzw.cjs +2846 -0
  18. package/dist/ai-writing-G6tnCGzw.cjs.map +1 -0
  19. package/dist/index-2nigSf4N.d.cts +339 -0
  20. package/dist/index-2nigSf4N.d.cts.map +1 -0
  21. package/dist/index-B54baFUA.d.ts +339 -0
  22. package/dist/index-B54baFUA.d.ts.map +1 -0
  23. package/dist/index-BAfEHCnD.d.cts +1071 -0
  24. package/dist/index-BAfEHCnD.d.cts.map +1 -0
  25. package/dist/index-BqMXWuNC.d.cts +153 -0
  26. package/dist/index-BqMXWuNC.d.cts.map +1 -0
  27. package/dist/index-CVWvymVb.d.ts +1071 -0
  28. package/dist/index-CVWvymVb.d.ts.map +1 -0
  29. package/dist/index-CcHDTxIZ.d.ts +153 -0
  30. package/dist/index-CcHDTxIZ.d.ts.map +1 -0
  31. package/dist/index.cjs +1211 -1146
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.d.cts +941 -861
  34. package/dist/index.d.cts.map +1 -0
  35. package/dist/index.d.ts +941 -861
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +1063 -1147
  38. package/dist/index.js.map +1 -1
  39. package/dist/item-group-DXiR8nOr.js +445 -0
  40. package/dist/item-group-DXiR8nOr.js.map +1 -0
  41. package/dist/item-group-DroFXgLb.cjs +505 -0
  42. package/dist/item-group-DroFXgLb.cjs.map +1 -0
  43. package/dist/registry-DVi0LR_i.cjs +5915 -0
  44. package/dist/registry-DVi0LR_i.cjs.map +1 -0
  45. package/dist/registry-Dsx9mNsS.js +5449 -0
  46. package/dist/registry-Dsx9mNsS.js.map +1 -0
  47. package/dist/schemas-Bdw0nGPo.js +152 -0
  48. package/dist/schemas-Bdw0nGPo.js.map +1 -0
  49. package/dist/schemas-DVbAOrtd.cjs +224 -0
  50. package/dist/schemas-DVbAOrtd.cjs.map +1 -0
  51. package/dist/schemas.cjs +16 -34
  52. package/dist/schemas.d.cts +2 -2
  53. package/dist/schemas.d.ts +2 -2
  54. package/dist/schemas.js +3 -34
  55. package/dist/scoring-B2_HEhMT.cjs +1748 -0
  56. package/dist/scoring-B2_HEhMT.cjs.map +1 -0
  57. package/dist/scoring-BZqUkuDF.js +1604 -0
  58. package/dist/scoring-BZqUkuDF.js.map +1 -0
  59. package/dist/scoring.cjs +37 -75
  60. package/dist/scoring.d.cts +3 -1103
  61. package/dist/scoring.d.ts +3 -1103
  62. package/dist/scoring.js +3 -75
  63. package/dist/xapi-CMTph7wG.js +332 -0
  64. package/dist/xapi-CMTph7wG.js.map +1 -0
  65. package/dist/xapi-DNzkSJ0S.cjs +362 -0
  66. package/dist/xapi-DNzkSJ0S.cjs.map +1 -0
  67. package/dist/xapi.cjs +7 -16
  68. package/dist/xapi.d.cts +2 -150
  69. package/dist/xapi.d.ts +2 -150
  70. package/dist/xapi.js +2 -16
  71. package/package.json +4 -4
  72. package/dist/activity-D705CXng.d.cts +0 -2162
  73. package/dist/activity-D705CXng.d.ts +0 -2162
  74. package/dist/chunk-46BXIGLG.cjs +0 -272
  75. package/dist/chunk-46BXIGLG.cjs.map +0 -1
  76. package/dist/chunk-4YANYTFN.cjs +0 -1256
  77. package/dist/chunk-4YANYTFN.cjs.map +0 -1
  78. package/dist/chunk-7KOWU4RO.js +0 -2207
  79. package/dist/chunk-7KOWU4RO.js.map +0 -1
  80. package/dist/chunk-7THXD7AN.cjs +0 -5057
  81. package/dist/chunk-7THXD7AN.cjs.map +0 -1
  82. package/dist/chunk-CYCTIXUN.cjs +0 -118
  83. package/dist/chunk-CYCTIXUN.cjs.map +0 -1
  84. package/dist/chunk-CYPA5BW5.js +0 -118
  85. package/dist/chunk-CYPA5BW5.js.map +0 -1
  86. package/dist/chunk-JTNDIBZO.js +0 -272
  87. package/dist/chunk-JTNDIBZO.js.map +0 -1
  88. package/dist/chunk-MV2N2WNS.js +0 -352
  89. package/dist/chunk-MV2N2WNS.js.map +0 -1
  90. package/dist/chunk-MXI26H62.cjs +0 -2207
  91. package/dist/chunk-MXI26H62.cjs.map +0 -1
  92. package/dist/chunk-QS7B42ZR.js +0 -5057
  93. package/dist/chunk-QS7B42ZR.js.map +0 -1
  94. package/dist/chunk-QX2ERL72.js +0 -1256
  95. package/dist/chunk-QX2ERL72.js.map +0 -1
  96. package/dist/chunk-ZWXQSKCX.cjs +0 -352
  97. package/dist/chunk-ZWXQSKCX.cjs.map +0 -1
  98. package/dist/index-BChYtrEe.d.ts +0 -548
  99. package/dist/index-VXt_v3YI.d.cts +0 -548
  100. package/dist/schemas.cjs.map +0 -1
  101. package/dist/schemas.js.map +0 -1
  102. package/dist/scoring.cjs.map +0 -1
  103. package/dist/scoring.js.map +0 -1
  104. package/dist/xapi.cjs.map +0 -1
  105. package/dist/xapi.js.map +0 -1
@@ -1,118 +0,0 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
-
3
-
4
- var _chunkZWXQSKCXcjs = require('./chunk-ZWXQSKCX.cjs');
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
- var _chunk7THXD7ANcjs = require('./chunk-7THXD7AN.cjs');
20
-
21
- // src/schemas/json-schema.ts
22
- var _v4 = require('zod/v4');
23
- var stimulusJsonSchema = _v4.z.toJSONSchema(_chunkZWXQSKCXcjs.StimulusSchema, {
24
- target: "draft-7"
25
- });
26
- var itemGroupJsonSchema = _v4.z.toJSONSchema(_chunkZWXQSKCXcjs.ItemGroupSchema, {
27
- target: "draft-7"
28
- });
29
- var gapSelectJsonSchema = _v4.z.toJSONSchema(_chunk7THXD7ANcjs.GapSelectDataSchema, {
30
- target: "draft-7"
31
- });
32
- var dictationJsonSchema = _v4.z.toJSONSchema(_chunk7THXD7ANcjs.DictationDataSchema, {
33
- target: "draft-7"
34
- });
35
- var readAloudJsonSchema = _v4.z.toJSONSchema(_chunk7THXD7ANcjs.ReadAloudDataSchema, {
36
- target: "draft-7"
37
- });
38
- var multipleChoiceJsonSchema = _v4.z.toJSONSchema(
39
- _chunk7THXD7ANcjs.MultipleChoiceDataSchema,
40
- {
41
- target: "draft-7"
42
- }
43
- );
44
- var fillInTheBlanksJsonSchema = _v4.z.toJSONSchema(
45
- _chunk7THXD7ANcjs.FillInTheBlanksDataSchema,
46
- {
47
- target: "draft-7"
48
- }
49
- );
50
- var writtenResponseJsonSchema = _v4.z.toJSONSchema(
51
- _chunk7THXD7ANcjs.WrittenResponseDataSchema,
52
- {
53
- target: "draft-7"
54
- }
55
- );
56
- function jsonSchemaFor(type) {
57
- const descriptor = _chunk7THXD7ANcjs.getActivityTypeDescriptor.call(void 0, type);
58
- if (descriptor === void 0) {
59
- throw new (0, _chunk7THXD7ANcjs.UnknownActivityTypeError)(type);
60
- }
61
- if (_chunk7THXD7ANcjs.isOwnSchema.call(void 0, descriptor.schema)) {
62
- return _v4.z.toJSONSchema(descriptor.schema, { target: "draft-7" });
63
- }
64
- if (descriptor.jsonSchema !== void 0) {
65
- return structuredClone(descriptor.jsonSchema);
66
- }
67
- const generated = _chunk7THXD7ANcjs.standardJsonSchemaOf.call(void 0, descriptor.schema);
68
- if (generated === void 0) {
69
- throw new Error(
70
- `Activity type "${descriptor.type}" has no JSON Schema: its schema implements no Standard JSON Schema converter ('~standard'.jsonSchema). Give its descriptor a \`jsonSchema\`.`
71
- );
72
- }
73
- return generated;
74
- }
75
-
76
- // src/schemas/index.ts
77
- function validateMedia(value) {
78
- return asValidation(_chunk7THXD7ANcjs.readSchema.call(void 0, _chunk7THXD7ANcjs.MediaSchema, value, "Media"));
79
- }
80
- function validateOptionMedia(value) {
81
- return asValidation(_chunk7THXD7ANcjs.readSchema.call(void 0, _chunk7THXD7ANcjs.MultipleChoiceOptionMediaSchema, value, "Option media"));
82
- }
83
- function asValidation(reading) {
84
- if (reading.success) {
85
- return { success: true, data: reading.data };
86
- }
87
- return {
88
- success: false,
89
- errors: reading.issues.map((issue) => ({
90
- path: issue.path.map(String),
91
- message: issue.message,
92
- code: issue.code
93
- }))
94
- };
95
- }
96
- function validateActivity(type, data) {
97
- const descriptor = _chunk7THXD7ANcjs.getActivityTypeDescriptor.call(void 0, type);
98
- if (descriptor === void 0) {
99
- throw new (0, _chunk7THXD7ANcjs.UnknownActivityTypeError)(String(type));
100
- }
101
- return asValidation(_chunk7THXD7ANcjs.readSchema.call(void 0, descriptor.schema, data, `Activity type "${descriptor.type}"`));
102
- }
103
-
104
-
105
-
106
-
107
-
108
-
109
-
110
-
111
-
112
-
113
-
114
-
115
-
116
-
117
- exports.stimulusJsonSchema = stimulusJsonSchema; exports.itemGroupJsonSchema = itemGroupJsonSchema; exports.gapSelectJsonSchema = gapSelectJsonSchema; exports.dictationJsonSchema = dictationJsonSchema; exports.readAloudJsonSchema = readAloudJsonSchema; exports.multipleChoiceJsonSchema = multipleChoiceJsonSchema; exports.fillInTheBlanksJsonSchema = fillInTheBlanksJsonSchema; exports.writtenResponseJsonSchema = writtenResponseJsonSchema; exports.jsonSchemaFor = jsonSchemaFor; exports.validateMedia = validateMedia; exports.validateOptionMedia = validateOptionMedia; exports.validateActivity = validateActivity;
118
- //# sourceMappingURL=chunk-CYCTIXUN.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["/home/runner/work/learning-kit/learning-kit/packages/lk-core/dist/chunk-CYCTIXUN.cjs","../src/schemas/json-schema.ts","../src/schemas/index.ts"],"names":[],"mappings":"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","file":"/home/runner/work/learning-kit/learning-kit/packages/lk-core/dist/chunk-CYCTIXUN.cjs","sourcesContent":[null,"import { z } from 'zod/v4';\nimport { UnknownActivityTypeError } from '../errors.js';\nimport { getActivityTypeDescriptor } from '../registry/index.js';\nimport { DictationDataSchema } from './dictation.js';\nimport { FillInTheBlanksDataSchema } from './fill-in-the-blanks.js';\nimport { GapSelectDataSchema } from './gap-select.js';\nimport { ItemGroupSchema, StimulusSchema } from './item-group.js';\nimport { MultipleChoiceDataSchema } from './multiple-choice.js';\nimport { ReadAloudDataSchema } from './read-aloud.js';\nimport { isOwnSchema, standardJsonSchemaOf } from './read-schema.js';\nimport { WrittenResponseDataSchema } from './written-response.js';\n\n/**\n * JSON Schema (Draft 7) for a `Stimulus`. Structural contract only — the\n * kind/media/body consistency guards are Zod-only.\n */\nexport const stimulusJsonSchema: Record<string, unknown> = z.toJSONSchema(StimulusSchema, {\n target: 'draft-7',\n});\n\n/**\n * JSON Schema (Draft 7) for an `ItemGroup` CONTAINER. Items appear as objects\n * with `type` and `id` only; each item's own contract is `jsonSchemaFor(type)`.\n * For an AI generation pipeline, ask for the group and each item separately\n * rather than a single nested schema — that keeps the per-type schema the\n * registry's, not a copy.\n */\nexport const itemGroupJsonSchema: Record<string, unknown> = z.toJSONSchema(ItemGroupSchema, {\n target: 'draft-7',\n});\n\n/**\n * JSON Schema (Draft 7) representation of the Multiple Choice activity data\n * contract, generated natively by Zod 4. Draft 7 is mandated by Requirement\n * 2.2 for the widest AI-prompt / OpenAPI tooling compatibility. The semantic\n * `.refine()` guards are not representable in JSON Schema and are\n * intentionally omitted — the export captures the *structural* contract only.\n * Since v0.3 the source schemas are loose, so these no longer emit\n * `additionalProperties: false` — the JSON Schema and `validateActivity` now\n * agree on unknown-key handling.\n */\n/**\n * JSON Schema (Draft 7) representation of the Gap Select activity data\n * contract. Structural contract only: the six semantic guards — the\n * passage/gap bijection, id uniqueness, the one-choice-source rule, and an\n * answer key that names a choice the gap offers — are Zod-only, as they are\n * for the other types.\n */\nexport const gapSelectJsonSchema: Record<string, unknown> = z.toJSONSchema(GapSelectDataSchema, {\n target: 'draft-7',\n});\n\n/**\n * JSON Schema (Draft 7) representation of the Dictation activity data\n * contract. Structural contract, plus the raw length caps JSON Schema can\n * state in code points as zod counts them — `maxLength` 2000 on a transcript\n * and an accepted transcript, 200 on a rule's `from` and `to`. The twelve\n * semantic guards — a transcript that survives normalisation, the caps after\n * equivalences are applied, distinct candidates, audio only, the\n * slow-recording rules, no captions, no transcript in the title — are\n * Zod-only, as they are for the other types.\n */\nexport const dictationJsonSchema: Record<string, unknown> = z.toJSONSchema(DictationDataSchema, {\n target: 'draft-7',\n});\n\n/**\n * JSON Schema (Draft 7) representation of the Read Aloud activity data\n * contract. Structural contract, plus the raw cap JSON Schema can state in code\n * points as zod counts them — `maxLength` 2000 on the reference text — and the\n * canonical locale pattern. The ten semantic guards — the cap after\n * normalisation, a text that survives it, the spaced-script rule, the\n * model-recording rules, unique dimensions, a weight above 0, and the take\n * bounds — are Zod-only, as they are for the other types.\n */\nexport const readAloudJsonSchema: Record<string, unknown> = z.toJSONSchema(ReadAloudDataSchema, {\n target: 'draft-7',\n});\n\nexport const multipleChoiceJsonSchema: Record<string, unknown> = z.toJSONSchema(\n MultipleChoiceDataSchema,\n {\n target: 'draft-7',\n },\n);\n\n/**\n * JSON Schema (Draft 7) representation of the Fill-in-the-Blanks activity data\n * contract, generated natively by Zod 4. Structural contract only (semantic\n * `.refine()` guards are Zod-only and not representable in JSON Schema).\n */\nexport const fillInTheBlanksJsonSchema: Record<string, unknown> = z.toJSONSchema(\n FillInTheBlanksDataSchema,\n {\n target: 'draft-7',\n },\n);\n\n/**\n * JSON Schema (Draft 7) representation of the Written Response activity data\n * contract. Structural contract only.\n */\nexport const writtenResponseJsonSchema: Record<string, unknown> = z.toJSONSchema(\n WrittenResponseDataSchema,\n {\n target: 'draft-7',\n },\n);\n\n/**\n * Derives the JSON Schema (Draft 7) for any REGISTERED activity type — the\n * live, registry-backed replacement for the static per-type exports above,\n * and the building block for AI generation pipelines (R6.1): pass the result\n * as a structured-output schema so a model can only emit valid items.\n *\n * For a type you registered, it is the descriptor's `jsonSchema` when you gave\n * one, or else what the schema produces through the Standard JSON Schema\n * converter (`'~standard'.jsonSchema`) — a zod 4.6 schema has one. A fresh copy\n * every call.\n *\n * @throws UnknownActivityTypeError when `type` has no registered descriptor.\n * @throws Error when a registered type has neither a `jsonSchema` nor a schema\n * that can produce one.\n */\nexport function jsonSchemaFor(type: string): Record<string, unknown> {\n const descriptor = getActivityTypeDescriptor(type);\n if (descriptor === undefined) {\n throw new UnknownActivityTypeError(type);\n }\n if (isOwnSchema(descriptor.schema)) {\n return z.toJSONSchema(descriptor.schema as never, { target: 'draft-7' }) as Record<\n string,\n unknown\n >;\n }\n if (descriptor.jsonSchema !== undefined) {\n return structuredClone(descriptor.jsonSchema) as Record<string, unknown>;\n }\n const generated = standardJsonSchemaOf(descriptor.schema);\n if (generated === undefined) {\n throw new Error(\n `Activity type \"${descriptor.type}\" has no JSON Schema: its schema implements no Standard JSON Schema converter ('~standard'.jsonSchema). Give its descriptor a \\`jsonSchema\\`.`,\n );\n }\n return generated;\n}\n","import { UnknownActivityTypeError } from '../errors.js';\nimport { getActivityTypeDescriptor } from '../registry/index.js';\nimport type {\n ActivityDataMap,\n ActivityMedia,\n ActivityType,\n MultipleChoiceOptionMedia,\n ValidationResult,\n} from '../types/activity.js';\nimport { MediaSchema } from './media.js';\nimport { MultipleChoiceOptionMediaSchema } from './multiple-choice.js';\nimport { readSchema, type SchemaReading } from './read-schema.js';\n\nexport type {\n RedactedActivity,\n RedactedBlankConfig,\n RedactedDictationData,\n RedactedDictationSlowMedia,\n RedactedFillInTheBlanksData,\n RedactedGapSelectBank,\n RedactedGapSelectChoice,\n RedactedGapSelectData,\n RedactedGapSelectGap,\n RedactedMultipleChoiceData,\n RedactedMultipleChoiceOption,\n RedactedMultipleChoiceOptionMedia,\n RedactedReadAloudData,\n RedactedStimulus,\n RedactedWrittenResponseData,\n} from '../types/redacted.js';\nexport { validateItemGroup } from './item-group.js';\nexport {\n dictationJsonSchema,\n fillInTheBlanksJsonSchema,\n gapSelectJsonSchema,\n itemGroupJsonSchema,\n jsonSchemaFor,\n multipleChoiceJsonSchema,\n readAloudJsonSchema,\n stimulusJsonSchema,\n writtenResponseJsonSchema,\n} from './json-schema.js';\n\n/**\n * Checks a piece of activity media — an item's `media`, a stimulus's, a\n * dictation's `slowMedia` — on its own, the way `validateActivity` checks it\n * inside an item: its kind, an address in the allowed schemes, a playback\n * policy only on audio, captions and tracks only where they can play. For an\n * editor's media picker, which holds the media before the item is whole.\n *\n * @returns `{ success: true, data }`, or `{ success: false, errors }` with the\n * same codes, paths and messages `validateActivity` reports.\n */\nexport function validateMedia(value: unknown): ValidationResult<ActivityMedia> {\n return asValidation(readSchema(MediaSchema as never, value, 'Media'));\n}\n\n/**\n * Checks a multiple-choice option's `media` on its own: a picture or a\n * recording, at an address in the allowed schemes, with no playback policy.\n *\n * @returns `{ success: true, data }`, or `{ success: false, errors }` with the\n * same codes, paths and messages `validateActivity` reports.\n */\nexport function validateOptionMedia(value: unknown): ValidationResult<MultipleChoiceOptionMedia> {\n return asValidation(readSchema(MultipleChoiceOptionMediaSchema as never, value, 'Option media'));\n}\n\nfunction asValidation<T>(reading: SchemaReading<unknown>): ValidationResult<T> {\n if (reading.success) {\n return { success: true, data: reading.data as T };\n }\n return {\n success: false,\n errors: reading.issues.map((issue) => ({\n path: issue.path.map(String),\n message: issue.message,\n code: issue.code,\n })),\n };\n}\n\n/**\n * Validates raw activity data against the schema registered for the given\n * activity type (built-in or consumer-registered via `registerActivityType`).\n *\n * Unknown keys are PRESERVED, not stripped: every built-in schema is loose,\n * so consumer sidecar fields and forward-version fields survive validation\n * verbatim in the returned `data`.\n *\n * @returns `{ success: true, data }` with the typed, validated data, or\n * `{ success: false, errors }` with one entry per failed constraint.\n * @throws UnknownActivityTypeError when `type` has no registered descriptor.\n */\nexport function validateActivity<T extends ActivityType>(\n type: T,\n data: unknown,\n): ValidationResult<ActivityDataMap[T]> {\n const descriptor = getActivityTypeDescriptor(type);\n if (descriptor === undefined) {\n throw new UnknownActivityTypeError(String(type));\n }\n\n return asValidation(readSchema(descriptor.schema, data, `Activity type \"${descriptor.type}\"`));\n}\n"]}
@@ -1,118 +0,0 @@
1
- import {
2
- ItemGroupSchema,
3
- StimulusSchema
4
- } from "./chunk-MV2N2WNS.js";
5
- import {
6
- DictationDataSchema,
7
- FillInTheBlanksDataSchema,
8
- GapSelectDataSchema,
9
- MediaSchema,
10
- MultipleChoiceDataSchema,
11
- MultipleChoiceOptionMediaSchema,
12
- ReadAloudDataSchema,
13
- UnknownActivityTypeError,
14
- WrittenResponseDataSchema,
15
- getActivityTypeDescriptor,
16
- isOwnSchema,
17
- readSchema,
18
- standardJsonSchemaOf
19
- } from "./chunk-QS7B42ZR.js";
20
-
21
- // src/schemas/json-schema.ts
22
- import { z } from "zod/v4";
23
- var stimulusJsonSchema = z.toJSONSchema(StimulusSchema, {
24
- target: "draft-7"
25
- });
26
- var itemGroupJsonSchema = z.toJSONSchema(ItemGroupSchema, {
27
- target: "draft-7"
28
- });
29
- var gapSelectJsonSchema = z.toJSONSchema(GapSelectDataSchema, {
30
- target: "draft-7"
31
- });
32
- var dictationJsonSchema = z.toJSONSchema(DictationDataSchema, {
33
- target: "draft-7"
34
- });
35
- var readAloudJsonSchema = z.toJSONSchema(ReadAloudDataSchema, {
36
- target: "draft-7"
37
- });
38
- var multipleChoiceJsonSchema = z.toJSONSchema(
39
- MultipleChoiceDataSchema,
40
- {
41
- target: "draft-7"
42
- }
43
- );
44
- var fillInTheBlanksJsonSchema = z.toJSONSchema(
45
- FillInTheBlanksDataSchema,
46
- {
47
- target: "draft-7"
48
- }
49
- );
50
- var writtenResponseJsonSchema = z.toJSONSchema(
51
- WrittenResponseDataSchema,
52
- {
53
- target: "draft-7"
54
- }
55
- );
56
- function jsonSchemaFor(type) {
57
- const descriptor = getActivityTypeDescriptor(type);
58
- if (descriptor === void 0) {
59
- throw new UnknownActivityTypeError(type);
60
- }
61
- if (isOwnSchema(descriptor.schema)) {
62
- return z.toJSONSchema(descriptor.schema, { target: "draft-7" });
63
- }
64
- if (descriptor.jsonSchema !== void 0) {
65
- return structuredClone(descriptor.jsonSchema);
66
- }
67
- const generated = standardJsonSchemaOf(descriptor.schema);
68
- if (generated === void 0) {
69
- throw new Error(
70
- `Activity type "${descriptor.type}" has no JSON Schema: its schema implements no Standard JSON Schema converter ('~standard'.jsonSchema). Give its descriptor a \`jsonSchema\`.`
71
- );
72
- }
73
- return generated;
74
- }
75
-
76
- // src/schemas/index.ts
77
- function validateMedia(value) {
78
- return asValidation(readSchema(MediaSchema, value, "Media"));
79
- }
80
- function validateOptionMedia(value) {
81
- return asValidation(readSchema(MultipleChoiceOptionMediaSchema, value, "Option media"));
82
- }
83
- function asValidation(reading) {
84
- if (reading.success) {
85
- return { success: true, data: reading.data };
86
- }
87
- return {
88
- success: false,
89
- errors: reading.issues.map((issue) => ({
90
- path: issue.path.map(String),
91
- message: issue.message,
92
- code: issue.code
93
- }))
94
- };
95
- }
96
- function validateActivity(type, data) {
97
- const descriptor = getActivityTypeDescriptor(type);
98
- if (descriptor === void 0) {
99
- throw new UnknownActivityTypeError(String(type));
100
- }
101
- return asValidation(readSchema(descriptor.schema, data, `Activity type "${descriptor.type}"`));
102
- }
103
-
104
- export {
105
- stimulusJsonSchema,
106
- itemGroupJsonSchema,
107
- gapSelectJsonSchema,
108
- dictationJsonSchema,
109
- readAloudJsonSchema,
110
- multipleChoiceJsonSchema,
111
- fillInTheBlanksJsonSchema,
112
- writtenResponseJsonSchema,
113
- jsonSchemaFor,
114
- validateMedia,
115
- validateOptionMedia,
116
- validateActivity
117
- };
118
- //# sourceMappingURL=chunk-CYPA5BW5.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/schemas/json-schema.ts","../src/schemas/index.ts"],"sourcesContent":["import { z } from 'zod/v4';\nimport { UnknownActivityTypeError } from '../errors.js';\nimport { getActivityTypeDescriptor } from '../registry/index.js';\nimport { DictationDataSchema } from './dictation.js';\nimport { FillInTheBlanksDataSchema } from './fill-in-the-blanks.js';\nimport { GapSelectDataSchema } from './gap-select.js';\nimport { ItemGroupSchema, StimulusSchema } from './item-group.js';\nimport { MultipleChoiceDataSchema } from './multiple-choice.js';\nimport { ReadAloudDataSchema } from './read-aloud.js';\nimport { isOwnSchema, standardJsonSchemaOf } from './read-schema.js';\nimport { WrittenResponseDataSchema } from './written-response.js';\n\n/**\n * JSON Schema (Draft 7) for a `Stimulus`. Structural contract only — the\n * kind/media/body consistency guards are Zod-only.\n */\nexport const stimulusJsonSchema: Record<string, unknown> = z.toJSONSchema(StimulusSchema, {\n target: 'draft-7',\n});\n\n/**\n * JSON Schema (Draft 7) for an `ItemGroup` CONTAINER. Items appear as objects\n * with `type` and `id` only; each item's own contract is `jsonSchemaFor(type)`.\n * For an AI generation pipeline, ask for the group and each item separately\n * rather than a single nested schema — that keeps the per-type schema the\n * registry's, not a copy.\n */\nexport const itemGroupJsonSchema: Record<string, unknown> = z.toJSONSchema(ItemGroupSchema, {\n target: 'draft-7',\n});\n\n/**\n * JSON Schema (Draft 7) representation of the Multiple Choice activity data\n * contract, generated natively by Zod 4. Draft 7 is mandated by Requirement\n * 2.2 for the widest AI-prompt / OpenAPI tooling compatibility. The semantic\n * `.refine()` guards are not representable in JSON Schema and are\n * intentionally omitted — the export captures the *structural* contract only.\n * Since v0.3 the source schemas are loose, so these no longer emit\n * `additionalProperties: false` — the JSON Schema and `validateActivity` now\n * agree on unknown-key handling.\n */\n/**\n * JSON Schema (Draft 7) representation of the Gap Select activity data\n * contract. Structural contract only: the six semantic guards — the\n * passage/gap bijection, id uniqueness, the one-choice-source rule, and an\n * answer key that names a choice the gap offers — are Zod-only, as they are\n * for the other types.\n */\nexport const gapSelectJsonSchema: Record<string, unknown> = z.toJSONSchema(GapSelectDataSchema, {\n target: 'draft-7',\n});\n\n/**\n * JSON Schema (Draft 7) representation of the Dictation activity data\n * contract. Structural contract, plus the raw length caps JSON Schema can\n * state in code points as zod counts them — `maxLength` 2000 on a transcript\n * and an accepted transcript, 200 on a rule's `from` and `to`. The twelve\n * semantic guards — a transcript that survives normalisation, the caps after\n * equivalences are applied, distinct candidates, audio only, the\n * slow-recording rules, no captions, no transcript in the title — are\n * Zod-only, as they are for the other types.\n */\nexport const dictationJsonSchema: Record<string, unknown> = z.toJSONSchema(DictationDataSchema, {\n target: 'draft-7',\n});\n\n/**\n * JSON Schema (Draft 7) representation of the Read Aloud activity data\n * contract. Structural contract, plus the raw cap JSON Schema can state in code\n * points as zod counts them — `maxLength` 2000 on the reference text — and the\n * canonical locale pattern. The ten semantic guards — the cap after\n * normalisation, a text that survives it, the spaced-script rule, the\n * model-recording rules, unique dimensions, a weight above 0, and the take\n * bounds — are Zod-only, as they are for the other types.\n */\nexport const readAloudJsonSchema: Record<string, unknown> = z.toJSONSchema(ReadAloudDataSchema, {\n target: 'draft-7',\n});\n\nexport const multipleChoiceJsonSchema: Record<string, unknown> = z.toJSONSchema(\n MultipleChoiceDataSchema,\n {\n target: 'draft-7',\n },\n);\n\n/**\n * JSON Schema (Draft 7) representation of the Fill-in-the-Blanks activity data\n * contract, generated natively by Zod 4. Structural contract only (semantic\n * `.refine()` guards are Zod-only and not representable in JSON Schema).\n */\nexport const fillInTheBlanksJsonSchema: Record<string, unknown> = z.toJSONSchema(\n FillInTheBlanksDataSchema,\n {\n target: 'draft-7',\n },\n);\n\n/**\n * JSON Schema (Draft 7) representation of the Written Response activity data\n * contract. Structural contract only.\n */\nexport const writtenResponseJsonSchema: Record<string, unknown> = z.toJSONSchema(\n WrittenResponseDataSchema,\n {\n target: 'draft-7',\n },\n);\n\n/**\n * Derives the JSON Schema (Draft 7) for any REGISTERED activity type — the\n * live, registry-backed replacement for the static per-type exports above,\n * and the building block for AI generation pipelines (R6.1): pass the result\n * as a structured-output schema so a model can only emit valid items.\n *\n * For a type you registered, it is the descriptor's `jsonSchema` when you gave\n * one, or else what the schema produces through the Standard JSON Schema\n * converter (`'~standard'.jsonSchema`) — a zod 4.6 schema has one. A fresh copy\n * every call.\n *\n * @throws UnknownActivityTypeError when `type` has no registered descriptor.\n * @throws Error when a registered type has neither a `jsonSchema` nor a schema\n * that can produce one.\n */\nexport function jsonSchemaFor(type: string): Record<string, unknown> {\n const descriptor = getActivityTypeDescriptor(type);\n if (descriptor === undefined) {\n throw new UnknownActivityTypeError(type);\n }\n if (isOwnSchema(descriptor.schema)) {\n return z.toJSONSchema(descriptor.schema as never, { target: 'draft-7' }) as Record<\n string,\n unknown\n >;\n }\n if (descriptor.jsonSchema !== undefined) {\n return structuredClone(descriptor.jsonSchema) as Record<string, unknown>;\n }\n const generated = standardJsonSchemaOf(descriptor.schema);\n if (generated === undefined) {\n throw new Error(\n `Activity type \"${descriptor.type}\" has no JSON Schema: its schema implements no Standard JSON Schema converter ('~standard'.jsonSchema). Give its descriptor a \\`jsonSchema\\`.`,\n );\n }\n return generated;\n}\n","import { UnknownActivityTypeError } from '../errors.js';\nimport { getActivityTypeDescriptor } from '../registry/index.js';\nimport type {\n ActivityDataMap,\n ActivityMedia,\n ActivityType,\n MultipleChoiceOptionMedia,\n ValidationResult,\n} from '../types/activity.js';\nimport { MediaSchema } from './media.js';\nimport { MultipleChoiceOptionMediaSchema } from './multiple-choice.js';\nimport { readSchema, type SchemaReading } from './read-schema.js';\n\nexport type {\n RedactedActivity,\n RedactedBlankConfig,\n RedactedDictationData,\n RedactedDictationSlowMedia,\n RedactedFillInTheBlanksData,\n RedactedGapSelectBank,\n RedactedGapSelectChoice,\n RedactedGapSelectData,\n RedactedGapSelectGap,\n RedactedMultipleChoiceData,\n RedactedMultipleChoiceOption,\n RedactedMultipleChoiceOptionMedia,\n RedactedReadAloudData,\n RedactedStimulus,\n RedactedWrittenResponseData,\n} from '../types/redacted.js';\nexport { validateItemGroup } from './item-group.js';\nexport {\n dictationJsonSchema,\n fillInTheBlanksJsonSchema,\n gapSelectJsonSchema,\n itemGroupJsonSchema,\n jsonSchemaFor,\n multipleChoiceJsonSchema,\n readAloudJsonSchema,\n stimulusJsonSchema,\n writtenResponseJsonSchema,\n} from './json-schema.js';\n\n/**\n * Checks a piece of activity media — an item's `media`, a stimulus's, a\n * dictation's `slowMedia` — on its own, the way `validateActivity` checks it\n * inside an item: its kind, an address in the allowed schemes, a playback\n * policy only on audio, captions and tracks only where they can play. For an\n * editor's media picker, which holds the media before the item is whole.\n *\n * @returns `{ success: true, data }`, or `{ success: false, errors }` with the\n * same codes, paths and messages `validateActivity` reports.\n */\nexport function validateMedia(value: unknown): ValidationResult<ActivityMedia> {\n return asValidation(readSchema(MediaSchema as never, value, 'Media'));\n}\n\n/**\n * Checks a multiple-choice option's `media` on its own: a picture or a\n * recording, at an address in the allowed schemes, with no playback policy.\n *\n * @returns `{ success: true, data }`, or `{ success: false, errors }` with the\n * same codes, paths and messages `validateActivity` reports.\n */\nexport function validateOptionMedia(value: unknown): ValidationResult<MultipleChoiceOptionMedia> {\n return asValidation(readSchema(MultipleChoiceOptionMediaSchema as never, value, 'Option media'));\n}\n\nfunction asValidation<T>(reading: SchemaReading<unknown>): ValidationResult<T> {\n if (reading.success) {\n return { success: true, data: reading.data as T };\n }\n return {\n success: false,\n errors: reading.issues.map((issue) => ({\n path: issue.path.map(String),\n message: issue.message,\n code: issue.code,\n })),\n };\n}\n\n/**\n * Validates raw activity data against the schema registered for the given\n * activity type (built-in or consumer-registered via `registerActivityType`).\n *\n * Unknown keys are PRESERVED, not stripped: every built-in schema is loose,\n * so consumer sidecar fields and forward-version fields survive validation\n * verbatim in the returned `data`.\n *\n * @returns `{ success: true, data }` with the typed, validated data, or\n * `{ success: false, errors }` with one entry per failed constraint.\n * @throws UnknownActivityTypeError when `type` has no registered descriptor.\n */\nexport function validateActivity<T extends ActivityType>(\n type: T,\n data: unknown,\n): ValidationResult<ActivityDataMap[T]> {\n const descriptor = getActivityTypeDescriptor(type);\n if (descriptor === undefined) {\n throw new UnknownActivityTypeError(String(type));\n }\n\n return asValidation(readSchema(descriptor.schema, data, `Activity type \"${descriptor.type}\"`));\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,SAAS;AAgBX,IAAM,qBAA8C,EAAE,aAAa,gBAAgB;AAAA,EACxF,QAAQ;AACV,CAAC;AASM,IAAM,sBAA+C,EAAE,aAAa,iBAAiB;AAAA,EAC1F,QAAQ;AACV,CAAC;AAmBM,IAAM,sBAA+C,EAAE,aAAa,qBAAqB;AAAA,EAC9F,QAAQ;AACV,CAAC;AAYM,IAAM,sBAA+C,EAAE,aAAa,qBAAqB;AAAA,EAC9F,QAAQ;AACV,CAAC;AAWM,IAAM,sBAA+C,EAAE,aAAa,qBAAqB;AAAA,EAC9F,QAAQ;AACV,CAAC;AAEM,IAAM,2BAAoD,EAAE;AAAA,EACjE;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,EACV;AACF;AAOO,IAAM,4BAAqD,EAAE;AAAA,EAClE;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,EACV;AACF;AAMO,IAAM,4BAAqD,EAAE;AAAA,EAClE;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,EACV;AACF;AAiBO,SAAS,cAAc,MAAuC;AACnE,QAAM,aAAa,0BAA0B,IAAI;AACjD,MAAI,eAAe,QAAW;AAC5B,UAAM,IAAI,yBAAyB,IAAI;AAAA,EACzC;AACA,MAAI,YAAY,WAAW,MAAM,GAAG;AAClC,WAAO,EAAE,aAAa,WAAW,QAAiB,EAAE,QAAQ,UAAU,CAAC;AAAA,EAIzE;AACA,MAAI,WAAW,eAAe,QAAW;AACvC,WAAO,gBAAgB,WAAW,UAAU;AAAA,EAC9C;AACA,QAAM,YAAY,qBAAqB,WAAW,MAAM;AACxD,MAAI,cAAc,QAAW;AAC3B,UAAM,IAAI;AAAA,MACR,kBAAkB,WAAW,IAAI;AAAA,IACnC;AAAA,EACF;AACA,SAAO;AACT;;;AC5FO,SAAS,cAAc,OAAiD;AAC7E,SAAO,aAAa,WAAW,aAAsB,OAAO,OAAO,CAAC;AACtE;AASO,SAAS,oBAAoB,OAA6D;AAC/F,SAAO,aAAa,WAAW,iCAA0C,OAAO,cAAc,CAAC;AACjG;AAEA,SAAS,aAAgB,SAAsD;AAC7E,MAAI,QAAQ,SAAS;AACnB,WAAO,EAAE,SAAS,MAAM,MAAM,QAAQ,KAAU;AAAA,EAClD;AACA,SAAO;AAAA,IACL,SAAS;AAAA,IACT,QAAQ,QAAQ,OAAO,IAAI,CAAC,WAAW;AAAA,MACrC,MAAM,MAAM,KAAK,IAAI,MAAM;AAAA,MAC3B,SAAS,MAAM;AAAA,MACf,MAAM,MAAM;AAAA,IACd,EAAE;AAAA,EACJ;AACF;AAcO,SAAS,iBACd,MACA,MACsC;AACtC,QAAM,aAAa,0BAA0B,IAAI;AACjD,MAAI,eAAe,QAAW;AAC5B,UAAM,IAAI,yBAAyB,OAAO,IAAI,CAAC;AAAA,EACjD;AAEA,SAAO,aAAa,WAAW,WAAW,QAAQ,MAAM,kBAAkB,WAAW,IAAI,GAAG,CAAC;AAC/F;","names":[]}
@@ -1,272 +0,0 @@
1
- import {
2
- ActivitySchemaError,
3
- getActivityTypeDescriptor
4
- } from "./chunk-QS7B42ZR.js";
5
-
6
- // src/xapi/validators.ts
7
- import { z } from "zod/v4";
8
- var UUID_V4 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
9
- var IRI = /^[a-z][a-z0-9+.-]*:/i;
10
- var langMap = z.record(z.string(), z.string());
11
- var extensions = z.record(z.string(), z.unknown());
12
- var actorSchema = z.object({
13
- objectType: z.literal("Agent"),
14
- name: z.string().optional(),
15
- mbox: z.string().regex(/^mailto:/, "mbox must be a mailto IRI").optional(),
16
- account: z.object({ homePage: z.string(), name: z.string() }).optional()
17
- }).refine((actor) => actor.mbox !== void 0 || actor.account !== void 0, {
18
- error: "actor must be identified by an mbox or an account"
19
- });
20
- var statementSchema = z.object({
21
- id: z.string().regex(UUID_V4, "id must be a UUID v4"),
22
- actor: actorSchema,
23
- verb: z.object({
24
- id: z.string().regex(IRI, "verb.id must be an IRI"),
25
- display: langMap
26
- }),
27
- object: z.object({
28
- objectType: z.literal("Activity"),
29
- id: z.string().regex(IRI, "object.id must be an IRI"),
30
- definition: z.object({
31
- name: langMap.optional(),
32
- description: langMap.optional(),
33
- type: z.string().optional(),
34
- interactionType: z.string().optional(),
35
- correctResponsesPattern: z.array(z.string()).optional(),
36
- choices: z.array(z.object({ id: z.string(), description: langMap.optional() })).optional(),
37
- extensions: extensions.optional()
38
- }).optional()
39
- }),
40
- result: z.object({
41
- // xAPI 1.0.3 permits scaled in [-1, 1]; the SDK only emits [0, 1].
42
- score: z.object({
43
- scaled: z.number().min(-1).max(1),
44
- raw: z.number().optional(),
45
- min: z.number().optional(),
46
- max: z.number().optional()
47
- }).optional(),
48
- success: z.boolean().optional(),
49
- completion: z.boolean().optional(),
50
- duration: z.string().regex(/^P/, "duration must be an ISO 8601 duration").optional(),
51
- response: z.string().optional(),
52
- extensions: extensions.optional()
53
- }).optional(),
54
- context: z.object({
55
- platform: z.string().optional(),
56
- language: z.string().optional(),
57
- contextActivities: z.object({
58
- parent: z.array(z.object({ objectType: z.literal("Activity"), id: z.string() })).optional(),
59
- grouping: z.array(z.object({ objectType: z.literal("Activity"), id: z.string() })).optional(),
60
- category: z.array(z.object({ objectType: z.literal("Activity"), id: z.string() })).optional(),
61
- other: z.array(z.object({ objectType: z.literal("Activity"), id: z.string() })).optional()
62
- }).optional(),
63
- extensions: extensions.optional()
64
- }).optional(),
65
- timestamp: z.string().refine((value) => !Number.isNaN(Date.parse(value)), "timestamp must be ISO 8601"),
66
- version: z.literal("1.0.3")
67
- });
68
- function isProduction() {
69
- try {
70
- return process.env.NODE_ENV === "production";
71
- } catch {
72
- return false;
73
- }
74
- }
75
- function validateXAPIStatement(statement) {
76
- const result = statementSchema.safeParse(statement);
77
- if (result.success) {
78
- return;
79
- }
80
- const errors = result.error.issues.map((issue) => ({
81
- path: issue.path.map(String),
82
- message: issue.message,
83
- code: issue.code
84
- }));
85
- if (isProduction()) {
86
- console.warn("[learning-kit] Invalid xAPI statement (sent anyway):", errors);
87
- return;
88
- }
89
- throw new ActivitySchemaError("xAPIStatement", errors);
90
- }
91
-
92
- // src/xapi/verbs.ts
93
- var XAPIVerb = {
94
- ANSWERED: "http://adlnet.gov/expapi/verbs/answered",
95
- COMPLETED: "http://adlnet.gov/expapi/verbs/completed",
96
- EXPERIENCED: "http://adlnet.gov/expapi/verbs/experienced",
97
- INTERACTED: "http://adlnet.gov/expapi/verbs/interacted",
98
- ATTEMPTED: "http://adlnet.gov/expapi/verbs/attempted",
99
- PASSED: "http://adlnet.gov/expapi/verbs/passed",
100
- FAILED: "http://adlnet.gov/expapi/verbs/failed",
101
- WATCHED: "https://w3id.org/xapi/video/verbs/watched",
102
- /**
103
- * Emitted for deferred-grading submissions (e.g. written-response): the
104
- * learner has submitted work whose grade does not exist yet, so `answered`
105
- * (which implies a scored response) would be wrong. xAPI 1.0.3 defines no
106
- * canonical "submit" verb; the Activity Streams 1.0 IRI is the established
107
- * community choice (design decision recorded per Task 25.4).
108
- */
109
- SUBMITTED: "http://activitystrea.ms/schema/1.0/submit",
110
- /**
111
- * Emitted when an asynchronous grader produces a grade for work that was
112
- * previously only SUBMITTED. Distinct from `answered`, which asserts the
113
- * grade existed at submission time — here the learner acted earlier and the
114
- * grade arrived later, often from a different actor (an AI or a teacher).
115
- */
116
- SCORED: "http://adlnet.gov/expapi/verbs/scored"
117
- };
118
- var XAPI_VERB_DISPLAY = {
119
- ANSWERED: { "en-US": "answered" },
120
- COMPLETED: { "en-US": "completed" },
121
- EXPERIENCED: { "en-US": "experienced" },
122
- INTERACTED: { "en-US": "interacted" },
123
- ATTEMPTED: { "en-US": "attempted" },
124
- PASSED: { "en-US": "passed" },
125
- FAILED: { "en-US": "failed" },
126
- WATCHED: { "en-US": "watched" },
127
- SUBMITTED: { "en-US": "submitted" },
128
- SCORED: { "en-US": "scored" }
129
- };
130
-
131
- // src/xapi/builder.ts
132
- function msToIsoDuration(ms) {
133
- return `PT${(Math.round(Math.max(0, ms) / 10) / 100).toFixed(2)}S`;
134
- }
135
- function uuidv4() {
136
- const c = globalThis.crypto;
137
- if (c?.randomUUID) {
138
- return c.randomUUID();
139
- }
140
- const bytes = new Uint8Array(16);
141
- if (c?.getRandomValues) {
142
- c.getRandomValues(bytes);
143
- } else {
144
- for (let i = 0; i < 16; i += 1) {
145
- bytes[i] = Math.floor(Math.random() * 256);
146
- }
147
- }
148
- bytes[6] = bytes[6] & 15 | 64;
149
- bytes[8] = bytes[8] & 63 | 128;
150
- const hex = [...bytes].map((b) => b.toString(16).padStart(2, "0")).join("");
151
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
152
- }
153
- function buildObject(params) {
154
- const definition = {
155
- ...params.name !== void 0 ? { name: params.name } : {},
156
- ...params.description !== void 0 ? { description: params.description } : {},
157
- ...params.type !== void 0 ? { type: params.type } : {},
158
- ...params.interactionType !== void 0 ? { interactionType: params.interactionType } : {},
159
- ...params.correctResponsesPattern !== void 0 ? { correctResponsesPattern: params.correctResponsesPattern } : {},
160
- ...params.choices !== void 0 ? { choices: params.choices } : {}
161
- };
162
- return {
163
- objectType: "Activity",
164
- id: params.id,
165
- ...Object.keys(definition).length > 0 ? { definition } : {}
166
- };
167
- }
168
- function buildResult(timeSpentMs, scoringResult, resultExtensions, response) {
169
- return {
170
- completion: true,
171
- duration: msToIsoDuration(timeSpentMs),
172
- ...scoringResult !== void 0 ? {
173
- score: {
174
- scaled: scoringResult.score,
175
- raw: scoringResult.score,
176
- min: 0,
177
- max: scoringResult.maxScore
178
- },
179
- success: scoringResult.passed
180
- } : {},
181
- ...response !== void 0 ? { response } : {},
182
- ...resultExtensions !== void 0 ? { extensions: resultExtensions } : {}
183
- };
184
- }
185
- var xAPIBuilder = {
186
- buildStatement(params) {
187
- const statement = {
188
- id: uuidv4(),
189
- actor: params.actor,
190
- verb: {
191
- id: XAPIVerb[params.verb],
192
- display: XAPI_VERB_DISPLAY[params.verb]
193
- },
194
- object: buildObject(params.object),
195
- ...params.result !== void 0 ? { result: params.result } : {},
196
- ...params.context !== void 0 ? { context: params.context } : {},
197
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
198
- version: "1.0.3"
199
- };
200
- validateXAPIStatement(statement);
201
- return statement;
202
- },
203
- buildAnsweredStatement(params) {
204
- return this.buildStatement({
205
- actor: params.actor,
206
- verb: "ANSWERED",
207
- object: params.object,
208
- result: buildResult(
209
- params.timeSpentMs,
210
- params.scoringResult,
211
- params.resultExtensions,
212
- params.response
213
- ),
214
- ...params.context !== void 0 ? { context: params.context } : {}
215
- });
216
- },
217
- /**
218
- * Builds a statement for a deferred-grading submission (e.g. a written
219
- * response). Verb is SUBMITTED and the result deliberately omits `score`,
220
- * `success`, and `completion` — none of them exist until the asynchronous
221
- * grader runs; only `duration`, `response`, and extensions are recorded.
222
- */
223
- buildSubmittedStatement(params) {
224
- return this.buildStatement({
225
- actor: params.actor,
226
- verb: "SUBMITTED",
227
- object: params.object,
228
- result: {
229
- duration: msToIsoDuration(params.timeSpentMs),
230
- ...params.response !== void 0 ? { response: params.response } : {},
231
- ...params.resultExtensions !== void 0 ? { extensions: params.resultExtensions } : {}
232
- },
233
- ...params.context !== void 0 ? { context: params.context } : {}
234
- });
235
- },
236
- buildCompletedStatement(params) {
237
- return this.buildStatement({
238
- actor: params.actor,
239
- verb: "COMPLETED",
240
- object: params.object,
241
- result: buildResult(
242
- params.timeSpentMs,
243
- params.scoringResult,
244
- params.resultExtensions,
245
- void 0
246
- ),
247
- ...params.context !== void 0 ? { context: params.context } : {}
248
- });
249
- }
250
- };
251
- function xapiDefinitionFor(data) {
252
- const descriptor = getActivityTypeDescriptor(data.type);
253
- const interop = descriptor?.interop;
254
- if (interop === void 0) {
255
- return {};
256
- }
257
- const pattern = interop.correctResponsesPattern?.(data);
258
- return {
259
- ...interop.xapiActivityTypeIri !== void 0 ? { type: interop.xapiActivityTypeIri } : {},
260
- ...interop.xapiInteractionType !== void 0 ? { interactionType: interop.xapiInteractionType } : {},
261
- ...pattern !== void 0 && pattern.length > 0 ? { correctResponsesPattern: pattern } : {}
262
- };
263
- }
264
-
265
- export {
266
- validateXAPIStatement,
267
- XAPIVerb,
268
- XAPI_VERB_DISPLAY,
269
- xAPIBuilder,
270
- xapiDefinitionFor
271
- };
272
- //# sourceMappingURL=chunk-JTNDIBZO.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/xapi/validators.ts","../src/xapi/verbs.ts","../src/xapi/builder.ts"],"sourcesContent":["import { z } from 'zod/v4';\nimport { ActivitySchemaError } from '../errors.js';\nimport type { ValidationError } from '../types/activity.js';\nimport type { XAPIStatement } from '../types/xapi.js';\n\nconst UUID_V4 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n// Minimal IRI/URI check: a scheme followed by ':'.\nconst IRI = /^[a-z][a-z0-9+.-]*:/i;\n\nconst langMap = z.record(z.string(), z.string());\nconst extensions = z.record(z.string(), z.unknown());\n\nconst actorSchema = z\n .object({\n objectType: z.literal('Agent'),\n name: z.string().optional(),\n mbox: z\n .string()\n .regex(/^mailto:/, 'mbox must be a mailto IRI')\n .optional(),\n account: z.object({ homePage: z.string(), name: z.string() }).optional(),\n })\n .refine((actor) => actor.mbox !== undefined || actor.account !== undefined, {\n error: 'actor must be identified by an mbox or an account',\n });\n\nconst statementSchema = z.object({\n id: z.string().regex(UUID_V4, 'id must be a UUID v4'),\n actor: actorSchema,\n verb: z.object({\n id: z.string().regex(IRI, 'verb.id must be an IRI'),\n display: langMap,\n }),\n object: z.object({\n objectType: z.literal('Activity'),\n id: z.string().regex(IRI, 'object.id must be an IRI'),\n definition: z\n .object({\n name: langMap.optional(),\n description: langMap.optional(),\n type: z.string().optional(),\n interactionType: z.string().optional(),\n correctResponsesPattern: z.array(z.string()).optional(),\n choices: z.array(z.object({ id: z.string(), description: langMap.optional() })).optional(),\n extensions: extensions.optional(),\n })\n .optional(),\n }),\n result: z\n .object({\n // xAPI 1.0.3 permits scaled in [-1, 1]; the SDK only emits [0, 1].\n score: z\n .object({\n scaled: z.number().min(-1).max(1),\n raw: z.number().optional(),\n min: z.number().optional(),\n max: z.number().optional(),\n })\n .optional(),\n success: z.boolean().optional(),\n completion: z.boolean().optional(),\n duration: z.string().regex(/^P/, 'duration must be an ISO 8601 duration').optional(),\n response: z.string().optional(),\n extensions: extensions.optional(),\n })\n .optional(),\n context: z\n .object({\n platform: z.string().optional(),\n language: z.string().optional(),\n contextActivities: z\n .object({\n parent: z\n .array(z.object({ objectType: z.literal('Activity'), id: z.string() }))\n .optional(),\n grouping: z\n .array(z.object({ objectType: z.literal('Activity'), id: z.string() }))\n .optional(),\n category: z\n .array(z.object({ objectType: z.literal('Activity'), id: z.string() }))\n .optional(),\n other: z\n .array(z.object({ objectType: z.literal('Activity'), id: z.string() }))\n .optional(),\n })\n .optional(),\n extensions: extensions.optional(),\n })\n .optional(),\n timestamp: z\n .string()\n .refine((value) => !Number.isNaN(Date.parse(value)), 'timestamp must be ISO 8601'),\n version: z.literal('1.0.3'),\n});\n\n/**\n * The one global this module reads by name. Declared here, not globally: this\n * package compiles without Node's types on purpose, and nothing else in it may\n * come to rely on `process`.\n */\ndeclare const process: { env: { NODE_ENV?: string } };\n\n/**\n * Whether this is a production build.\n *\n * **The literal `process.env.NODE_ENV`, spelled exactly so.** It is the one\n * expression bundlers replace at build time, and a browser has no `process` to\n * read at run time. The reading this replaces,\n * `globalThis.process?.env?.NODE_ENV`, is not that expression, so no bundler\n * touched it, and in a browser it found no `process` and answered \"not\n * production\" in every production app — so a malformed statement threw there\n * instead of being logged and sent. Tests running in Node never saw it,\n * because Node has a `process`.\n *\n * This package's own build must leave the literal in its output, for the\n * consumer's bundler to replace. The `try` covers code that reaches a runtime\n * unbundled, with no `process` at all: that is not production, as it always\n * was.\n */\nfunction isProduction(): boolean {\n try {\n return process.env.NODE_ENV === 'production';\n } catch {\n return false;\n }\n}\n\n/**\n * Validates a constructed {@link XAPIStatement} against the xAPI 1.0.3\n * structural contract.\n *\n * In development a structural failure throws (caught early); in production it\n * logs a warning and returns (the statement is still sent — see the design's\n * Error Handling table). Uses a `zod/v4` schema rather than `ajv` + an\n * external JSON Schema to keep `lk-core` dependency-free.\n *\n * NOTE: `ActivitySchemaError` is reused here per the task spec; its\n * `activityType` field carries the sentinel `'xAPIStatement'`. A dedicated\n * `XAPIValidationError` would be semantically cleaner — flagged for review.\n */\nexport function validateXAPIStatement(statement: XAPIStatement): void {\n const result = statementSchema.safeParse(statement);\n if (result.success) {\n return;\n }\n\n const errors: ValidationError[] = result.error.issues.map((issue) => ({\n path: issue.path.map(String),\n message: issue.message,\n code: issue.code,\n }));\n\n if (isProduction()) {\n console.warn('[learning-kit] Invalid xAPI statement (sent anyway):', errors);\n return;\n }\n\n throw new ActivitySchemaError('xAPIStatement', errors);\n}\n","/**\n * The set of xAPI verbs emitted by SDK activities, keyed by a stable\n * identifier mapped to its canonical xAPI verb IRI (xAPI 1.0.3 / ADL registry).\n */\nexport const XAPIVerb = {\n ANSWERED: 'http://adlnet.gov/expapi/verbs/answered',\n COMPLETED: 'http://adlnet.gov/expapi/verbs/completed',\n EXPERIENCED: 'http://adlnet.gov/expapi/verbs/experienced',\n INTERACTED: 'http://adlnet.gov/expapi/verbs/interacted',\n ATTEMPTED: 'http://adlnet.gov/expapi/verbs/attempted',\n PASSED: 'http://adlnet.gov/expapi/verbs/passed',\n FAILED: 'http://adlnet.gov/expapi/verbs/failed',\n WATCHED: 'https://w3id.org/xapi/video/verbs/watched',\n /**\n * Emitted for deferred-grading submissions (e.g. written-response): the\n * learner has submitted work whose grade does not exist yet, so `answered`\n * (which implies a scored response) would be wrong. xAPI 1.0.3 defines no\n * canonical \"submit\" verb; the Activity Streams 1.0 IRI is the established\n * community choice (design decision recorded per Task 25.4).\n */\n SUBMITTED: 'http://activitystrea.ms/schema/1.0/submit',\n /**\n * Emitted when an asynchronous grader produces a grade for work that was\n * previously only SUBMITTED. Distinct from `answered`, which asserts the\n * grade existed at submission time — here the learner acted earlier and the\n * grade arrived later, often from a different actor (an AI or a teacher).\n */\n SCORED: 'http://adlnet.gov/expapi/verbs/scored',\n} as const;\n\n/** Union of the valid `XAPIVerb` keys. */\nexport type XAPIVerbKey = keyof typeof XAPIVerb;\n\n/**\n * Default `en-US` display label for each verb, used to populate\n * `XAPIVerbObject.display` when the builder resolves a verb key.\n */\nexport const XAPI_VERB_DISPLAY: Record<XAPIVerbKey, Record<string, string>> = {\n ANSWERED: { 'en-US': 'answered' },\n COMPLETED: { 'en-US': 'completed' },\n EXPERIENCED: { 'en-US': 'experienced' },\n INTERACTED: { 'en-US': 'interacted' },\n ATTEMPTED: { 'en-US': 'attempted' },\n PASSED: { 'en-US': 'passed' },\n FAILED: { 'en-US': 'failed' },\n WATCHED: { 'en-US': 'watched' },\n SUBMITTED: { 'en-US': 'submitted' },\n SCORED: { 'en-US': 'scored' },\n};\n","import { getActivityTypeDescriptor } from '../registry/index.js';\nimport type { ScoringResult } from '../types/activity.js';\nimport type {\n XAPIActor,\n XAPIContext,\n XAPIObject,\n XAPIResult,\n XAPIStatement,\n} from '../types/xapi.js';\nimport { validateXAPIStatement } from './validators.js';\nimport { XAPI_VERB_DISPLAY, XAPIVerb, type XAPIVerbKey } from './verbs.js';\n\n/** Caller-supplied activity object descriptor (mapped to an xAPI Activity). */\nexport interface XAPIObjectParams {\n /** Activity IRI. */\n id: string;\n name?: Record<string, string>;\n description?: Record<string, string>;\n /** Activity type IRI. */\n type?: string;\n /** xAPI cmi.interaction type (e.g. `choice`, `fill-in`, `long-fill-in`). */\n interactionType?: string;\n /** Correct-responses patterns, per the xAPI CRP format. */\n correctResponsesPattern?: string[];\n /** For choice-family interactions: the available components. */\n choices?: Array<{ id: string; description?: Record<string, string> }>;\n}\n\n/** Parameters for the generic {@link xAPIBuilder.buildStatement}. */\nexport interface XAPIStatementParams {\n actor: XAPIActor;\n verb: XAPIVerbKey;\n object: XAPIObjectParams;\n result?: XAPIResult;\n context?: XAPIContext;\n}\n\n/** Parameters for {@link xAPIBuilder.buildAnsweredStatement} (verb = ANSWERED). */\nexport interface AnsweredStatementParams {\n actor: XAPIActor;\n object: XAPIObjectParams;\n scoringResult: ScoringResult;\n timeSpentMs: number;\n /** Serialized learner response string. */\n response?: string;\n context?: XAPIContext;\n resultExtensions?: Record<string, unknown>;\n}\n\n/**\n * Parameters for {@link xAPIBuilder.buildSubmittedStatement} (verb =\n * SUBMITTED — deferred-grading submissions). Carries NO score, success, or\n * completion: the grade does not exist yet (Req 22.7).\n */\nexport interface SubmittedStatementParams {\n actor: XAPIActor;\n object: XAPIObjectParams;\n timeSpentMs: number;\n /** Serialized learner response string (e.g. the submitted text). */\n response?: string;\n context?: XAPIContext;\n resultExtensions?: Record<string, unknown>;\n}\n\n/** Parameters for {@link xAPIBuilder.buildCompletedStatement} (verb = COMPLETED). */\nexport interface CompletedStatementParams {\n actor: XAPIActor;\n object: XAPIObjectParams;\n scoringResult?: ScoringResult;\n timeSpentMs: number;\n context?: XAPIContext;\n resultExtensions?: Record<string, unknown>;\n}\n\n/**\n * Converts a millisecond duration to an ISO 8601 seconds string with\n * centisecond precision (`PT1.23S`), per the xAPI 1.0.3 recommendation of\n * 0.01-second accuracy. Sub-second durations are preserved (previously any\n * duration under 500 ms collapsed to `PT0S`). Negative inputs (clock skew)\n * clamp to zero — a negative ISO duration is invalid.\n */\nfunction msToIsoDuration(ms: number): string {\n return `PT${(Math.round(Math.max(0, ms) / 10) / 100).toFixed(2)}S`;\n}\n\n/**\n * UUID v4 that works outside secure contexts. `crypto.randomUUID` is\n * SecureContext-gated (undefined on plain-http LAN/staging origins), and a\n * statement is built at SUBMIT time — throwing here would lose the learner's\n * answer. `crypto.getRandomValues` is NOT gated, so the fallback still emits\n * a spec-valid v4 UUID (the statement validator enforces the v4 format).\n */\nfunction uuidv4(): string {\n const c = (\n globalThis as {\n crypto?: { randomUUID?: () => string; getRandomValues?: (a: Uint8Array) => Uint8Array };\n }\n ).crypto;\n if (c?.randomUUID) {\n return c.randomUUID();\n }\n const bytes = new Uint8Array(16);\n if (c?.getRandomValues) {\n c.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 16; i += 1) {\n bytes[i] = Math.floor(Math.random() * 256);\n }\n }\n bytes[6] = ((bytes[6] as number) & 0x0f) | 0x40;\n bytes[8] = ((bytes[8] as number) & 0x3f) | 0x80;\n const hex = [...bytes].map((b) => b.toString(16).padStart(2, '0')).join('');\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n\n/** Builds the xAPI Activity object, including `definition` only when populated. */\nfunction buildObject(params: XAPIObjectParams): XAPIObject {\n const definition: NonNullable<XAPIObject['definition']> = {\n ...(params.name !== undefined ? { name: params.name } : {}),\n ...(params.description !== undefined ? { description: params.description } : {}),\n ...(params.type !== undefined ? { type: params.type } : {}),\n ...(params.interactionType !== undefined ? { interactionType: params.interactionType } : {}),\n ...(params.correctResponsesPattern !== undefined\n ? { correctResponsesPattern: params.correctResponsesPattern }\n : {}),\n ...(params.choices !== undefined ? { choices: params.choices } : {}),\n };\n return {\n objectType: 'Activity',\n id: params.id,\n ...(Object.keys(definition).length > 0 ? { definition } : {}),\n };\n}\n\n/**\n * Maps a {@link ScoringResult} + timing to an {@link XAPIResult} per the\n * normative mapping in the design document.\n */\nfunction buildResult(\n timeSpentMs: number,\n scoringResult: ScoringResult | undefined,\n resultExtensions: Record<string, unknown> | undefined,\n response: string | undefined,\n): XAPIResult {\n return {\n completion: true,\n duration: msToIsoDuration(timeSpentMs),\n ...(scoringResult !== undefined\n ? {\n score: {\n scaled: scoringResult.score,\n raw: scoringResult.score,\n min: 0,\n max: scoringResult.maxScore,\n },\n success: scoringResult.passed,\n }\n : {}),\n ...(response !== undefined ? { response } : {}),\n ...(resultExtensions !== undefined ? { extensions: resultExtensions } : {}),\n };\n}\n\n/**\n * Constructs xAPI 1.0.3 Statements. Every call stamps a fresh UUID v4 `id`,\n * an ISO 8601 `timestamp`, and `version: '1.0.3'`. Not pure by design —\n * statements are unique events.\n */\nexport const xAPIBuilder = {\n buildStatement(params: XAPIStatementParams): XAPIStatement {\n const statement: XAPIStatement = {\n id: uuidv4(),\n actor: params.actor,\n verb: {\n id: XAPIVerb[params.verb],\n display: XAPI_VERB_DISPLAY[params.verb],\n },\n object: buildObject(params.object),\n ...(params.result !== undefined ? { result: params.result } : {}),\n ...(params.context !== undefined ? { context: params.context } : {}),\n timestamp: new Date().toISOString(),\n version: '1.0.3',\n };\n // Design xAPIBuilder step 4: validate every statement (throws in dev,\n // warns in prod). All build*Statement helpers route through here.\n validateXAPIStatement(statement);\n return statement;\n },\n\n buildAnsweredStatement(params: AnsweredStatementParams): XAPIStatement {\n return this.buildStatement({\n actor: params.actor,\n verb: 'ANSWERED',\n object: params.object,\n result: buildResult(\n params.timeSpentMs,\n params.scoringResult,\n params.resultExtensions,\n params.response,\n ),\n ...(params.context !== undefined ? { context: params.context } : {}),\n });\n },\n\n /**\n * Builds a statement for a deferred-grading submission (e.g. a written\n * response). Verb is SUBMITTED and the result deliberately omits `score`,\n * `success`, and `completion` — none of them exist until the asynchronous\n * grader runs; only `duration`, `response`, and extensions are recorded.\n */\n buildSubmittedStatement(params: SubmittedStatementParams): XAPIStatement {\n return this.buildStatement({\n actor: params.actor,\n verb: 'SUBMITTED',\n object: params.object,\n result: {\n duration: msToIsoDuration(params.timeSpentMs),\n ...(params.response !== undefined ? { response: params.response } : {}),\n ...(params.resultExtensions !== undefined ? { extensions: params.resultExtensions } : {}),\n },\n ...(params.context !== undefined ? { context: params.context } : {}),\n });\n },\n\n buildCompletedStatement(params: CompletedStatementParams): XAPIStatement {\n return this.buildStatement({\n actor: params.actor,\n verb: 'COMPLETED',\n object: params.object,\n result: buildResult(\n params.timeSpentMs,\n params.scoringResult,\n params.resultExtensions,\n undefined,\n ),\n ...(params.context !== undefined ? { context: params.context } : {}),\n });\n },\n};\n\n/**\n * Builds the xAPI `object.definition` interop fields for an activity from its\n * REGISTERED descriptor, rather than having every renderer hand-roll them.\n *\n * `ActivityTypeDescriptor.interop` declares the activity-type IRI, the\n * cmi.interaction type and how to derive `correctResponsesPattern`; before\n * this, those fields had no reader anywhere and each component rebuilt the\n * same strings inline — the same \"declared but never used\" defect the SDK\n * fixed elsewhere. Reading them here also means a consumer-registered type\n * gets correct interop for free.\n *\n * Returns an empty object for an unregistered type or one that declares no\n * interop, so it is always safe to spread.\n */\nexport function xapiDefinitionFor(data: { type: string }): Partial<XAPIObjectParams> {\n const descriptor = getActivityTypeDescriptor(data.type);\n const interop = descriptor?.interop;\n if (interop === undefined) {\n return {};\n }\n const pattern = interop.correctResponsesPattern?.(data);\n return {\n ...(interop.xapiActivityTypeIri !== undefined ? { type: interop.xapiActivityTypeIri } : {}),\n ...(interop.xapiInteractionType !== undefined\n ? { interactionType: interop.xapiInteractionType }\n : {}),\n ...(pattern !== undefined && pattern.length > 0 ? { correctResponsesPattern: pattern } : {}),\n };\n}\n"],"mappings":"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","names":[]}