@intellectif/lk-core 1.7.0 → 1.9.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 (86) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/ai-check.cjs +77 -3
  3. package/dist/ai-check.cjs.map +1 -1
  4. package/dist/ai-check.d.cts +11 -3
  5. package/dist/ai-check.d.cts.map +1 -1
  6. package/dist/ai-check.d.ts +11 -3
  7. package/dist/ai-check.d.ts.map +1 -1
  8. package/dist/ai-check.js +77 -3
  9. package/dist/ai-check.js.map +1 -1
  10. package/dist/{ai-writing-B7oMCt66.js → ai-writing-DQjI9EFN.js} +2514 -136
  11. package/dist/ai-writing-DQjI9EFN.js.map +1 -0
  12. package/dist/{ai-writing-DrPTV3R2.cjs → ai-writing-DbpZpGRk.cjs} +2614 -122
  13. package/dist/ai-writing-DbpZpGRk.cjs.map +1 -0
  14. package/dist/{ai-B1wYrqcB.d.ts → ai-zhc9yhRj.d.cts} +454 -2
  15. package/dist/ai-zhc9yhRj.d.cts.map +1 -0
  16. package/dist/{ai-B1wYrqcB.d.cts → ai-zhc9yhRj.d.ts} +454 -2
  17. package/dist/ai-zhc9yhRj.d.ts.map +1 -0
  18. package/dist/{index-XCCoPoTU.d.cts → index-BeC8tAWf.d.cts} +2 -2
  19. package/dist/{index-XCCoPoTU.d.cts.map → index-BeC8tAWf.d.cts.map} +1 -1
  20. package/dist/{index-BU99GFTl.d.ts → index-Byar5VVs.d.ts} +2 -2
  21. package/dist/{index-BU99GFTl.d.ts.map → index-Byar5VVs.d.ts.map} +1 -1
  22. package/dist/{index-DP9rvyOz.d.cts → index-Ccj8oL_E.d.cts} +2 -2
  23. package/dist/{index-DP9rvyOz.d.cts.map → index-Ccj8oL_E.d.cts.map} +1 -1
  24. package/dist/{index-BTkMQtws.d.ts → index-CxkqS62X.d.ts} +89 -7
  25. package/dist/index-CxkqS62X.d.ts.map +1 -0
  26. package/dist/{index-BM8LeDWn.d.cts → index-DLsUnGHt.d.cts} +89 -7
  27. package/dist/index-DLsUnGHt.d.cts.map +1 -0
  28. package/dist/{index-BdXs4-en.d.ts → index-f4YuMgFb.d.ts} +2 -2
  29. package/dist/{index-BdXs4-en.d.ts.map → index-f4YuMgFb.d.ts.map} +1 -1
  30. package/dist/index.cjs +282 -6
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +262 -7
  33. package/dist/index.d.cts.map +1 -1
  34. package/dist/index.d.ts +262 -7
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +269 -7
  37. package/dist/index.js.map +1 -1
  38. package/dist/{item-group-CeDk7mfM.js → item-group-CKZk3txt.js} +2 -2
  39. package/dist/{item-group-CeDk7mfM.js.map → item-group-CKZk3txt.js.map} +1 -1
  40. package/dist/{item-group-8P3WFuUK.cjs → item-group-COP5OwEC.cjs} +2 -2
  41. package/dist/{item-group-8P3WFuUK.cjs.map → item-group-COP5OwEC.cjs.map} +1 -1
  42. package/dist/{registry-D92hz9iA.js → registry-Bw9grsCN.js} +339 -12
  43. package/dist/registry-Bw9grsCN.js.map +1 -0
  44. package/dist/{registry-hzrkTeIT.cjs → registry-C9O9txy5.cjs} +368 -11
  45. package/dist/registry-C9O9txy5.cjs.map +1 -0
  46. package/dist/{schemas-C7TViWCg.js → schemas-BJe3W6Xb.js} +3 -3
  47. package/dist/{schemas-C7TViWCg.js.map → schemas-BJe3W6Xb.js.map} +1 -1
  48. package/dist/{schemas-BWeskONy.cjs → schemas-CXlemRvB.cjs} +3 -3
  49. package/dist/{schemas-BWeskONy.cjs.map → schemas-CXlemRvB.cjs.map} +1 -1
  50. package/dist/schemas.cjs +3 -3
  51. package/dist/schemas.d.cts +1 -1
  52. package/dist/schemas.d.ts +1 -1
  53. package/dist/schemas.js +3 -3
  54. package/dist/{scoring-DD1GHcLI.js → scoring-BGKdff2i.js} +214 -26
  55. package/dist/scoring-BGKdff2i.js.map +1 -0
  56. package/dist/{scoring-CtkCFyFT.cjs → scoring-Db1CZeAn.cjs} +219 -25
  57. package/dist/scoring-Db1CZeAn.cjs.map +1 -0
  58. package/dist/scoring.cjs +3 -2
  59. package/dist/scoring.d.cts +3 -3
  60. package/dist/scoring.d.ts +3 -3
  61. package/dist/scoring.js +3 -3
  62. package/dist/{xapi-Cgicg-zU.js → xapi-BtBm8AWZ.js} +2 -2
  63. package/dist/{xapi-Cgicg-zU.js.map → xapi-BtBm8AWZ.js.map} +1 -1
  64. package/dist/{xapi-7btSpx6v.cjs → xapi-DOAqH9__.cjs} +2 -2
  65. package/dist/{xapi-7btSpx6v.cjs.map → xapi-DOAqH9__.cjs.map} +1 -1
  66. package/dist/xapi.cjs +1 -1
  67. package/dist/xapi.d.cts +1 -1
  68. package/dist/xapi.d.ts +1 -1
  69. package/dist/xapi.js +1 -1
  70. package/package.json +2 -2
  71. package/vectors/README.md +23 -0
  72. package/vectors/scoring.json +11967 -1
  73. package/vectors/validation.json +1298 -1
  74. package/vectors/variants.d.mts +20 -0
  75. package/vectors/variants.json +5753 -0
  76. package/vectors/variants.mjs +67 -0
  77. package/dist/ai-B1wYrqcB.d.cts.map +0 -1
  78. package/dist/ai-B1wYrqcB.d.ts.map +0 -1
  79. package/dist/ai-writing-B7oMCt66.js.map +0 -1
  80. package/dist/ai-writing-DrPTV3R2.cjs.map +0 -1
  81. package/dist/index-BM8LeDWn.d.cts.map +0 -1
  82. package/dist/index-BTkMQtws.d.ts.map +0 -1
  83. package/dist/registry-D92hz9iA.js.map +0 -1
  84. package/dist/registry-hzrkTeIT.cjs.map +0 -1
  85. package/dist/scoring-CtkCFyFT.cjs.map +0 -1
  86. package/dist/scoring-DD1GHcLI.js.map +0 -1
@@ -1,5 +1,5 @@
1
- import { Ct as MediaSchema, D as MultipleChoiceOptionMediaSchema, E as MultipleChoiceDataSchema, St as FillInTheBlanksDataSchema, Y as DictationDataSchema, _ as GapSelectDataSchema, a as readSchema, g as WrittenResponseDataSchema, i as isOwnSchema, o as standardJsonSchemaOf, r as registeredActivityTypes, t as getActivityTypeDescriptor, tt as UnknownActivityTypeError, v as ReadAloudDataSchema } from "./registry-D92hz9iA.js";
2
- import { r as StimulusSchema, t as ItemGroupSchema } from "./item-group-CeDk7mfM.js";
1
+ import { Et as FillInTheBlanksDataSchema, O as MultipleChoiceDataSchema, Ot as MediaSchema, Q as DictationDataSchema, _ as GapSelectDataSchema, a as readSchema, b as ReadAloudDataSchema, g as WrittenResponseDataSchema, i as isOwnSchema, it as UnknownActivityTypeError, k as MultipleChoiceOptionMediaSchema, o as standardJsonSchemaOf, r as registeredActivityTypes, t as getActivityTypeDescriptor } from "./registry-Bw9grsCN.js";
2
+ import { r as StimulusSchema, t as ItemGroupSchema } from "./item-group-CKZk3txt.js";
3
3
  import * as z from "zod/v4";
4
4
  // src/schemas/json-schema.ts
5
5
  /**
@@ -204,4 +204,4 @@ function validateActivity(type, data) {
204
204
  }
205
205
  export { dictationJsonSchema as a, itemGroupJsonSchema as c, readAloudJsonSchema as d, stimulusJsonSchema as f, contentJsonSchema as i, jsonSchemaFor as l, validateMedia as n, fillInTheBlanksJsonSchema as o, writtenResponseJsonSchema as p, validateOptionMedia as r, gapSelectJsonSchema as s, validateActivity as t, multipleChoiceJsonSchema as u };
206
206
 
207
- //# sourceMappingURL=schemas-C7TViWCg.js.map
207
+ //# sourceMappingURL=schemas-BJe3W6Xb.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"schemas-C7TViWCg.js","names":[],"sources":["../src/schemas/json-schema.ts","../src/schemas/index.ts"],"sourcesContent":["import * as z from 'zod/v4';\nimport { UnknownActivityTypeError } from '../errors.js';\nimport { getActivityTypeDescriptor, registeredActivityTypes } 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/** The JSON Schema drafts the SDK writes. */\nexport type JsonSchemaTarget = 'draft-2020-12' | 'draft-7';\n\n/** What the Standard JSON Schema converter calls each draft. */\nconst STANDARD_TARGET = { 'draft-7': 'draft-07', 'draft-2020-12': 'draft-2020-12' } as const;\n\n/**\n * Derives the JSON Schema for any REGISTERED activity type — the live,\n * registry-backed replacement for the static per-type exports above, and the\n * building block for AI generation pipelines (R6.1): pass the result as a\n * structured-output schema so a model can only emit valid items.\n *\n * Draft 7 unless `target` says `'draft-2020-12'`. For a type you registered,\n * it is the descriptor's `jsonSchema` when you gave one, in whatever draft you\n * wrote it, or else what the schema produces through the Standard JSON Schema\n * converter (`'~standard'.jsonSchema`) in the draft asked for — a zod 4.6\n * schema has one. A fresh copy 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(\n type: string,\n options: { target?: JsonSchemaTarget } = {},\n): Record<string, unknown> {\n const target = options.target ?? 'draft-7';\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 }) as Record<string, unknown>;\n }\n if (descriptor.jsonSchema !== undefined) {\n return structuredClone(descriptor.jsonSchema) as Record<string, unknown>;\n }\n const generated = standardJsonSchemaOf(descriptor.schema, STANDARD_TARGET[target]);\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\n/** A schema without its `$schema`, to sit inside another document. */\nfunction body(schema: Record<string, unknown>): Record<string, unknown> {\n const { $schema: _draft, ...rest } = schema;\n return rest;\n}\n\n/**\n * One JSON Schema document for content an outside tool writes: the root is a\n * `SequenceEntry` — an activity of any registered type, or an item group whose\n * items are typed too. Each type, the stimulus and the item group sit under\n * `$defs` (`definitions` in draft 7), so a tool can also point at one of them.\n * Draft 2020-12 unless `target` says `'draft-7'`.\n *\n * Structural, as every exported schema is: rules that span fields (one key in\n * a single-answer question, a blank for each marker, a slow recording that is\n * another file) cannot be written in JSON Schema. On import, `validateDraft`\n * and `validateItemGroupDraft` decide. It includes the author-only fields\n * (`transcript`, `script`), so a tool can write them.\n *\n * ```ts\n * writeFileSync('schema.json', JSON.stringify(contentJsonSchema(), null, 2));\n * ```\n */\nexport function contentJsonSchema(\n options: { target?: JsonSchemaTarget } = {},\n): Record<string, unknown> {\n const target = options.target ?? 'draft-2020-12';\n const definitions = target === 'draft-7' ? 'definitions' : '$defs';\n const ref = (name: string) => ({ $ref: `#/${definitions}/${name}` });\n const types = registeredActivityTypes();\n const anyActivity = { oneOf: types.map(ref) };\n\n const group = body(z.toJSONSchema(ItemGroupSchema, { target }) as Record<string, unknown>);\n const properties = group.properties as Record<string, Record<string, unknown>>;\n group.properties = {\n ...properties,\n stimulus: ref('stimulus'),\n // The container's own rule for an item (a `type`, an `id`, a `slotKey`),\n // and the item's type's whole contract.\n items: { ...properties.items, items: { allOf: [properties.items?.items, anyActivity] } },\n };\n\n return {\n $schema:\n target === 'draft-7'\n ? 'http://json-schema.org/draft-07/schema#'\n : 'https://json-schema.org/draft/2020-12/schema',\n title: 'SequenceEntry',\n description:\n 'One entry of a question set or an exam section: an activity, or an item group. Structural only: validateDraft and validateItemGroupDraft decide.',\n oneOf: [...types.map(ref), ref('item-group')],\n [definitions]: {\n ...Object.fromEntries(types.map((type) => [type, body(jsonSchemaFor(type, { target }))])),\n stimulus: body(z.toJSONSchema(StimulusSchema, { target }) as Record<string, unknown>),\n 'item-group': group,\n },\n };\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 contentJsonSchema,\n dictationJsonSchema,\n fillInTheBlanksJsonSchema,\n gapSelectJsonSchema,\n itemGroupJsonSchema,\n type JsonSchemaTarget,\n jsonSchemaFor,\n multipleChoiceJsonSchema,\n readAloudJsonSchema,\n stimulusJsonSchema,\n writtenResponseJsonSchema,\n} from './json-schema.js';\nexport { SCRIPT_MAX_TURNS } from './media.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":"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"}
1
+ {"version":3,"file":"schemas-BJe3W6Xb.js","names":[],"sources":["../src/schemas/json-schema.ts","../src/schemas/index.ts"],"sourcesContent":["import * as z from 'zod/v4';\nimport { UnknownActivityTypeError } from '../errors.js';\nimport { getActivityTypeDescriptor, registeredActivityTypes } 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/** The JSON Schema drafts the SDK writes. */\nexport type JsonSchemaTarget = 'draft-2020-12' | 'draft-7';\n\n/** What the Standard JSON Schema converter calls each draft. */\nconst STANDARD_TARGET = { 'draft-7': 'draft-07', 'draft-2020-12': 'draft-2020-12' } as const;\n\n/**\n * Derives the JSON Schema for any REGISTERED activity type — the live,\n * registry-backed replacement for the static per-type exports above, and the\n * building block for AI generation pipelines (R6.1): pass the result as a\n * structured-output schema so a model can only emit valid items.\n *\n * Draft 7 unless `target` says `'draft-2020-12'`. For a type you registered,\n * it is the descriptor's `jsonSchema` when you gave one, in whatever draft you\n * wrote it, or else what the schema produces through the Standard JSON Schema\n * converter (`'~standard'.jsonSchema`) in the draft asked for — a zod 4.6\n * schema has one. A fresh copy 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(\n type: string,\n options: { target?: JsonSchemaTarget } = {},\n): Record<string, unknown> {\n const target = options.target ?? 'draft-7';\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 }) as Record<string, unknown>;\n }\n if (descriptor.jsonSchema !== undefined) {\n return structuredClone(descriptor.jsonSchema) as Record<string, unknown>;\n }\n const generated = standardJsonSchemaOf(descriptor.schema, STANDARD_TARGET[target]);\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\n/** A schema without its `$schema`, to sit inside another document. */\nfunction body(schema: Record<string, unknown>): Record<string, unknown> {\n const { $schema: _draft, ...rest } = schema;\n return rest;\n}\n\n/**\n * One JSON Schema document for content an outside tool writes: the root is a\n * `SequenceEntry` — an activity of any registered type, or an item group whose\n * items are typed too. Each type, the stimulus and the item group sit under\n * `$defs` (`definitions` in draft 7), so a tool can also point at one of them.\n * Draft 2020-12 unless `target` says `'draft-7'`.\n *\n * Structural, as every exported schema is: rules that span fields (one key in\n * a single-answer question, a blank for each marker, a slow recording that is\n * another file) cannot be written in JSON Schema. On import, `validateDraft`\n * and `validateItemGroupDraft` decide. It includes the author-only fields\n * (`transcript`, `script`), so a tool can write them.\n *\n * ```ts\n * writeFileSync('schema.json', JSON.stringify(contentJsonSchema(), null, 2));\n * ```\n */\nexport function contentJsonSchema(\n options: { target?: JsonSchemaTarget } = {},\n): Record<string, unknown> {\n const target = options.target ?? 'draft-2020-12';\n const definitions = target === 'draft-7' ? 'definitions' : '$defs';\n const ref = (name: string) => ({ $ref: `#/${definitions}/${name}` });\n const types = registeredActivityTypes();\n const anyActivity = { oneOf: types.map(ref) };\n\n const group = body(z.toJSONSchema(ItemGroupSchema, { target }) as Record<string, unknown>);\n const properties = group.properties as Record<string, Record<string, unknown>>;\n group.properties = {\n ...properties,\n stimulus: ref('stimulus'),\n // The container's own rule for an item (a `type`, an `id`, a `slotKey`),\n // and the item's type's whole contract.\n items: { ...properties.items, items: { allOf: [properties.items?.items, anyActivity] } },\n };\n\n return {\n $schema:\n target === 'draft-7'\n ? 'http://json-schema.org/draft-07/schema#'\n : 'https://json-schema.org/draft/2020-12/schema',\n title: 'SequenceEntry',\n description:\n 'One entry of a question set or an exam section: an activity, or an item group. Structural only: validateDraft and validateItemGroupDraft decide.',\n oneOf: [...types.map(ref), ref('item-group')],\n [definitions]: {\n ...Object.fromEntries(types.map((type) => [type, body(jsonSchemaFor(type, { target }))])),\n stimulus: body(z.toJSONSchema(StimulusSchema, { target }) as Record<string, unknown>),\n 'item-group': group,\n },\n };\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 contentJsonSchema,\n dictationJsonSchema,\n fillInTheBlanksJsonSchema,\n gapSelectJsonSchema,\n itemGroupJsonSchema,\n type JsonSchemaTarget,\n jsonSchemaFor,\n multipleChoiceJsonSchema,\n readAloudJsonSchema,\n stimulusJsonSchema,\n writtenResponseJsonSchema,\n} from './json-schema.js';\nexport { SCRIPT_MAX_TURNS } from './media.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":"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"}
@@ -1,5 +1,5 @@
1
- const require_registry = require("./registry-hzrkTeIT.cjs");
2
- const require_item_group = require("./item-group-8P3WFuUK.cjs");
1
+ const require_registry = require("./registry-C9O9txy5.cjs");
2
+ const require_item_group = require("./item-group-COP5OwEC.cjs");
3
3
  let zod_v4 = require("zod/v4");
4
4
  zod_v4 = require_registry.__toESM(zod_v4, 1);
5
5
  // src/schemas/json-schema.ts
@@ -282,4 +282,4 @@ Object.defineProperty(exports, "writtenResponseJsonSchema", {
282
282
  }
283
283
  });
284
284
 
285
- //# sourceMappingURL=schemas-BWeskONy.cjs.map
285
+ //# sourceMappingURL=schemas-CXlemRvB.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"schemas-BWeskONy.cjs","names":["z","StimulusSchema","ItemGroupSchema","GapSelectDataSchema","DictationDataSchema","ReadAloudDataSchema","MultipleChoiceDataSchema","FillInTheBlanksDataSchema","WrittenResponseDataSchema","getActivityTypeDescriptor","UnknownActivityTypeError","isOwnSchema","standardJsonSchemaOf","registeredActivityTypes","readSchema","MediaSchema","MultipleChoiceOptionMediaSchema","getActivityTypeDescriptor","UnknownActivityTypeError"],"sources":["../src/schemas/json-schema.ts","../src/schemas/index.ts"],"sourcesContent":["import * as z from 'zod/v4';\nimport { UnknownActivityTypeError } from '../errors.js';\nimport { getActivityTypeDescriptor, registeredActivityTypes } 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/** The JSON Schema drafts the SDK writes. */\nexport type JsonSchemaTarget = 'draft-2020-12' | 'draft-7';\n\n/** What the Standard JSON Schema converter calls each draft. */\nconst STANDARD_TARGET = { 'draft-7': 'draft-07', 'draft-2020-12': 'draft-2020-12' } as const;\n\n/**\n * Derives the JSON Schema for any REGISTERED activity type — the live,\n * registry-backed replacement for the static per-type exports above, and the\n * building block for AI generation pipelines (R6.1): pass the result as a\n * structured-output schema so a model can only emit valid items.\n *\n * Draft 7 unless `target` says `'draft-2020-12'`. For a type you registered,\n * it is the descriptor's `jsonSchema` when you gave one, in whatever draft you\n * wrote it, or else what the schema produces through the Standard JSON Schema\n * converter (`'~standard'.jsonSchema`) in the draft asked for — a zod 4.6\n * schema has one. A fresh copy 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(\n type: string,\n options: { target?: JsonSchemaTarget } = {},\n): Record<string, unknown> {\n const target = options.target ?? 'draft-7';\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 }) as Record<string, unknown>;\n }\n if (descriptor.jsonSchema !== undefined) {\n return structuredClone(descriptor.jsonSchema) as Record<string, unknown>;\n }\n const generated = standardJsonSchemaOf(descriptor.schema, STANDARD_TARGET[target]);\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\n/** A schema without its `$schema`, to sit inside another document. */\nfunction body(schema: Record<string, unknown>): Record<string, unknown> {\n const { $schema: _draft, ...rest } = schema;\n return rest;\n}\n\n/**\n * One JSON Schema document for content an outside tool writes: the root is a\n * `SequenceEntry` — an activity of any registered type, or an item group whose\n * items are typed too. Each type, the stimulus and the item group sit under\n * `$defs` (`definitions` in draft 7), so a tool can also point at one of them.\n * Draft 2020-12 unless `target` says `'draft-7'`.\n *\n * Structural, as every exported schema is: rules that span fields (one key in\n * a single-answer question, a blank for each marker, a slow recording that is\n * another file) cannot be written in JSON Schema. On import, `validateDraft`\n * and `validateItemGroupDraft` decide. It includes the author-only fields\n * (`transcript`, `script`), so a tool can write them.\n *\n * ```ts\n * writeFileSync('schema.json', JSON.stringify(contentJsonSchema(), null, 2));\n * ```\n */\nexport function contentJsonSchema(\n options: { target?: JsonSchemaTarget } = {},\n): Record<string, unknown> {\n const target = options.target ?? 'draft-2020-12';\n const definitions = target === 'draft-7' ? 'definitions' : '$defs';\n const ref = (name: string) => ({ $ref: `#/${definitions}/${name}` });\n const types = registeredActivityTypes();\n const anyActivity = { oneOf: types.map(ref) };\n\n const group = body(z.toJSONSchema(ItemGroupSchema, { target }) as Record<string, unknown>);\n const properties = group.properties as Record<string, Record<string, unknown>>;\n group.properties = {\n ...properties,\n stimulus: ref('stimulus'),\n // The container's own rule for an item (a `type`, an `id`, a `slotKey`),\n // and the item's type's whole contract.\n items: { ...properties.items, items: { allOf: [properties.items?.items, anyActivity] } },\n };\n\n return {\n $schema:\n target === 'draft-7'\n ? 'http://json-schema.org/draft-07/schema#'\n : 'https://json-schema.org/draft/2020-12/schema',\n title: 'SequenceEntry',\n description:\n 'One entry of a question set or an exam section: an activity, or an item group. Structural only: validateDraft and validateItemGroupDraft decide.',\n oneOf: [...types.map(ref), ref('item-group')],\n [definitions]: {\n ...Object.fromEntries(types.map((type) => [type, body(jsonSchemaFor(type, { target }))])),\n stimulus: body(z.toJSONSchema(StimulusSchema, { target }) as Record<string, unknown>),\n 'item-group': group,\n },\n };\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 contentJsonSchema,\n dictationJsonSchema,\n fillInTheBlanksJsonSchema,\n gapSelectJsonSchema,\n itemGroupJsonSchema,\n type JsonSchemaTarget,\n jsonSchemaFor,\n multipleChoiceJsonSchema,\n readAloudJsonSchema,\n stimulusJsonSchema,\n writtenResponseJsonSchema,\n} from './json-schema.js';\nexport { SCRIPT_MAX_TURNS } from './media.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":"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"}
1
+ {"version":3,"file":"schemas-CXlemRvB.cjs","names":["z","StimulusSchema","ItemGroupSchema","GapSelectDataSchema","DictationDataSchema","ReadAloudDataSchema","MultipleChoiceDataSchema","FillInTheBlanksDataSchema","WrittenResponseDataSchema","getActivityTypeDescriptor","UnknownActivityTypeError","isOwnSchema","standardJsonSchemaOf","registeredActivityTypes","readSchema","MediaSchema","MultipleChoiceOptionMediaSchema","getActivityTypeDescriptor","UnknownActivityTypeError"],"sources":["../src/schemas/json-schema.ts","../src/schemas/index.ts"],"sourcesContent":["import * as z from 'zod/v4';\nimport { UnknownActivityTypeError } from '../errors.js';\nimport { getActivityTypeDescriptor, registeredActivityTypes } 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/** The JSON Schema drafts the SDK writes. */\nexport type JsonSchemaTarget = 'draft-2020-12' | 'draft-7';\n\n/** What the Standard JSON Schema converter calls each draft. */\nconst STANDARD_TARGET = { 'draft-7': 'draft-07', 'draft-2020-12': 'draft-2020-12' } as const;\n\n/**\n * Derives the JSON Schema for any REGISTERED activity type — the live,\n * registry-backed replacement for the static per-type exports above, and the\n * building block for AI generation pipelines (R6.1): pass the result as a\n * structured-output schema so a model can only emit valid items.\n *\n * Draft 7 unless `target` says `'draft-2020-12'`. For a type you registered,\n * it is the descriptor's `jsonSchema` when you gave one, in whatever draft you\n * wrote it, or else what the schema produces through the Standard JSON Schema\n * converter (`'~standard'.jsonSchema`) in the draft asked for — a zod 4.6\n * schema has one. A fresh copy 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(\n type: string,\n options: { target?: JsonSchemaTarget } = {},\n): Record<string, unknown> {\n const target = options.target ?? 'draft-7';\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 }) as Record<string, unknown>;\n }\n if (descriptor.jsonSchema !== undefined) {\n return structuredClone(descriptor.jsonSchema) as Record<string, unknown>;\n }\n const generated = standardJsonSchemaOf(descriptor.schema, STANDARD_TARGET[target]);\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\n/** A schema without its `$schema`, to sit inside another document. */\nfunction body(schema: Record<string, unknown>): Record<string, unknown> {\n const { $schema: _draft, ...rest } = schema;\n return rest;\n}\n\n/**\n * One JSON Schema document for content an outside tool writes: the root is a\n * `SequenceEntry` — an activity of any registered type, or an item group whose\n * items are typed too. Each type, the stimulus and the item group sit under\n * `$defs` (`definitions` in draft 7), so a tool can also point at one of them.\n * Draft 2020-12 unless `target` says `'draft-7'`.\n *\n * Structural, as every exported schema is: rules that span fields (one key in\n * a single-answer question, a blank for each marker, a slow recording that is\n * another file) cannot be written in JSON Schema. On import, `validateDraft`\n * and `validateItemGroupDraft` decide. It includes the author-only fields\n * (`transcript`, `script`), so a tool can write them.\n *\n * ```ts\n * writeFileSync('schema.json', JSON.stringify(contentJsonSchema(), null, 2));\n * ```\n */\nexport function contentJsonSchema(\n options: { target?: JsonSchemaTarget } = {},\n): Record<string, unknown> {\n const target = options.target ?? 'draft-2020-12';\n const definitions = target === 'draft-7' ? 'definitions' : '$defs';\n const ref = (name: string) => ({ $ref: `#/${definitions}/${name}` });\n const types = registeredActivityTypes();\n const anyActivity = { oneOf: types.map(ref) };\n\n const group = body(z.toJSONSchema(ItemGroupSchema, { target }) as Record<string, unknown>);\n const properties = group.properties as Record<string, Record<string, unknown>>;\n group.properties = {\n ...properties,\n stimulus: ref('stimulus'),\n // The container's own rule for an item (a `type`, an `id`, a `slotKey`),\n // and the item's type's whole contract.\n items: { ...properties.items, items: { allOf: [properties.items?.items, anyActivity] } },\n };\n\n return {\n $schema:\n target === 'draft-7'\n ? 'http://json-schema.org/draft-07/schema#'\n : 'https://json-schema.org/draft/2020-12/schema',\n title: 'SequenceEntry',\n description:\n 'One entry of a question set or an exam section: an activity, or an item group. Structural only: validateDraft and validateItemGroupDraft decide.',\n oneOf: [...types.map(ref), ref('item-group')],\n [definitions]: {\n ...Object.fromEntries(types.map((type) => [type, body(jsonSchemaFor(type, { target }))])),\n stimulus: body(z.toJSONSchema(StimulusSchema, { target }) as Record<string, unknown>),\n 'item-group': group,\n },\n };\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 contentJsonSchema,\n dictationJsonSchema,\n fillInTheBlanksJsonSchema,\n gapSelectJsonSchema,\n itemGroupJsonSchema,\n type JsonSchemaTarget,\n jsonSchemaFor,\n multipleChoiceJsonSchema,\n readAloudJsonSchema,\n stimulusJsonSchema,\n writtenResponseJsonSchema,\n} from './json-schema.js';\nexport { SCRIPT_MAX_TURNS } from './media.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":"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"}
package/dist/schemas.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_registry = require("./registry-hzrkTeIT.cjs");
3
- const require_item_group = require("./item-group-8P3WFuUK.cjs");
4
- const require_schemas = require("./schemas-BWeskONy.cjs");
2
+ const require_registry = require("./registry-C9O9txy5.cjs");
3
+ const require_item_group = require("./item-group-COP5OwEC.cjs");
4
+ const require_schemas = require("./schemas-CXlemRvB.cjs");
5
5
  exports.SCRIPT_MAX_TURNS = require_registry.SCRIPT_MAX_TURNS;
6
6
  exports.contentJsonSchema = require_schemas.contentJsonSchema;
7
7
  exports.dictationJsonSchema = require_schemas.dictationJsonSchema;
@@ -1,2 +1,2 @@
1
- import { A as RedactedStimulus, C as RedactedGapSelectChoice, D as RedactedMultipleChoiceOption, E as RedactedMultipleChoiceData, O as RedactedMultipleChoiceOptionMedia, S as RedactedGapSelectBank, T as RedactedGapSelectGap, _ as RedactedActivity, a as JsonSchemaTarget, b as RedactedDictationSlowMedia, c as fillInTheBlanksJsonSchema, d as jsonSchemaFor, f as multipleChoiceJsonSchema, g as validateItemGroup, h as writtenResponseJsonSchema, i as SCRIPT_MAX_TURNS, j as RedactedWrittenResponseData, k as RedactedReadAloudData, l as gapSelectJsonSchema, m as stimulusJsonSchema, n as validateMedia, o as contentJsonSchema, p as readAloudJsonSchema, r as validateOptionMedia, s as dictationJsonSchema, t as validateActivity, u as itemGroupJsonSchema, v as RedactedBlankConfig, w as RedactedGapSelectData, x as RedactedFillInTheBlanksData, y as RedactedDictationData } from "./index-DP9rvyOz.cjs";
1
+ import { A as RedactedStimulus, C as RedactedGapSelectChoice, D as RedactedMultipleChoiceOption, E as RedactedMultipleChoiceData, O as RedactedMultipleChoiceOptionMedia, S as RedactedGapSelectBank, T as RedactedGapSelectGap, _ as RedactedActivity, a as JsonSchemaTarget, b as RedactedDictationSlowMedia, c as fillInTheBlanksJsonSchema, d as jsonSchemaFor, f as multipleChoiceJsonSchema, g as validateItemGroup, h as writtenResponseJsonSchema, i as SCRIPT_MAX_TURNS, j as RedactedWrittenResponseData, k as RedactedReadAloudData, l as gapSelectJsonSchema, m as stimulusJsonSchema, n as validateMedia, o as contentJsonSchema, p as readAloudJsonSchema, r as validateOptionMedia, s as dictationJsonSchema, t as validateActivity, u as itemGroupJsonSchema, v as RedactedBlankConfig, w as RedactedGapSelectData, x as RedactedFillInTheBlanksData, y as RedactedDictationData } from "./index-Ccj8oL_E.cjs";
2
2
  export { type JsonSchemaTarget, type RedactedActivity, type RedactedBlankConfig, type RedactedDictationData, type RedactedDictationSlowMedia, type RedactedFillInTheBlanksData, type RedactedGapSelectBank, type RedactedGapSelectChoice, type RedactedGapSelectData, type RedactedGapSelectGap, type RedactedMultipleChoiceData, type RedactedMultipleChoiceOption, type RedactedMultipleChoiceOptionMedia, type RedactedReadAloudData, type RedactedStimulus, type RedactedWrittenResponseData, SCRIPT_MAX_TURNS, contentJsonSchema, dictationJsonSchema, fillInTheBlanksJsonSchema, gapSelectJsonSchema, itemGroupJsonSchema, jsonSchemaFor, multipleChoiceJsonSchema, readAloudJsonSchema, stimulusJsonSchema, validateActivity, validateItemGroup, validateMedia, validateOptionMedia, writtenResponseJsonSchema };
package/dist/schemas.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { A as RedactedStimulus, C as RedactedGapSelectChoice, D as RedactedMultipleChoiceOption, E as RedactedMultipleChoiceData, O as RedactedMultipleChoiceOptionMedia, S as RedactedGapSelectBank, T as RedactedGapSelectGap, _ as RedactedActivity, a as JsonSchemaTarget, b as RedactedDictationSlowMedia, c as fillInTheBlanksJsonSchema, d as jsonSchemaFor, f as multipleChoiceJsonSchema, g as validateItemGroup, h as writtenResponseJsonSchema, i as SCRIPT_MAX_TURNS, j as RedactedWrittenResponseData, k as RedactedReadAloudData, l as gapSelectJsonSchema, m as stimulusJsonSchema, n as validateMedia, o as contentJsonSchema, p as readAloudJsonSchema, r as validateOptionMedia, s as dictationJsonSchema, t as validateActivity, u as itemGroupJsonSchema, v as RedactedBlankConfig, w as RedactedGapSelectData, x as RedactedFillInTheBlanksData, y as RedactedDictationData } from "./index-BdXs4-en.js";
1
+ import { A as RedactedStimulus, C as RedactedGapSelectChoice, D as RedactedMultipleChoiceOption, E as RedactedMultipleChoiceData, O as RedactedMultipleChoiceOptionMedia, S as RedactedGapSelectBank, T as RedactedGapSelectGap, _ as RedactedActivity, a as JsonSchemaTarget, b as RedactedDictationSlowMedia, c as fillInTheBlanksJsonSchema, d as jsonSchemaFor, f as multipleChoiceJsonSchema, g as validateItemGroup, h as writtenResponseJsonSchema, i as SCRIPT_MAX_TURNS, j as RedactedWrittenResponseData, k as RedactedReadAloudData, l as gapSelectJsonSchema, m as stimulusJsonSchema, n as validateMedia, o as contentJsonSchema, p as readAloudJsonSchema, r as validateOptionMedia, s as dictationJsonSchema, t as validateActivity, u as itemGroupJsonSchema, v as RedactedBlankConfig, w as RedactedGapSelectData, x as RedactedFillInTheBlanksData, y as RedactedDictationData } from "./index-f4YuMgFb.js";
2
2
  export { type JsonSchemaTarget, type RedactedActivity, type RedactedBlankConfig, type RedactedDictationData, type RedactedDictationSlowMedia, type RedactedFillInTheBlanksData, type RedactedGapSelectBank, type RedactedGapSelectChoice, type RedactedGapSelectData, type RedactedGapSelectGap, type RedactedMultipleChoiceData, type RedactedMultipleChoiceOption, type RedactedMultipleChoiceOptionMedia, type RedactedReadAloudData, type RedactedStimulus, type RedactedWrittenResponseData, SCRIPT_MAX_TURNS, contentJsonSchema, dictationJsonSchema, fillInTheBlanksJsonSchema, gapSelectJsonSchema, itemGroupJsonSchema, jsonSchemaFor, multipleChoiceJsonSchema, readAloudJsonSchema, stimulusJsonSchema, validateActivity, validateItemGroup, validateMedia, validateOptionMedia, writtenResponseJsonSchema };
package/dist/schemas.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Tt as SCRIPT_MAX_TURNS } from "./registry-D92hz9iA.js";
2
- import { i as validateItemGroup } from "./item-group-CeDk7mfM.js";
3
- import { a as dictationJsonSchema, c as itemGroupJsonSchema, d as readAloudJsonSchema, f as stimulusJsonSchema, i as contentJsonSchema, l as jsonSchemaFor, n as validateMedia, o as fillInTheBlanksJsonSchema, p as writtenResponseJsonSchema, r as validateOptionMedia, s as gapSelectJsonSchema, t as validateActivity, u as multipleChoiceJsonSchema } from "./schemas-C7TViWCg.js";
1
+ import { At as SCRIPT_MAX_TURNS } from "./registry-Bw9grsCN.js";
2
+ import { i as validateItemGroup } from "./item-group-CKZk3txt.js";
3
+ import { a as dictationJsonSchema, c as itemGroupJsonSchema, d as readAloudJsonSchema, f as stimulusJsonSchema, i as contentJsonSchema, l as jsonSchemaFor, n as validateMedia, o as fillInTheBlanksJsonSchema, p as writtenResponseJsonSchema, r as validateOptionMedia, s as gapSelectJsonSchema, t as validateActivity, u as multipleChoiceJsonSchema } from "./schemas-BJe3W6Xb.js";
4
4
  export { SCRIPT_MAX_TURNS, contentJsonSchema, dictationJsonSchema, fillInTheBlanksJsonSchema, gapSelectJsonSchema, itemGroupJsonSchema, jsonSchemaFor, multipleChoiceJsonSchema, readAloudJsonSchema, stimulusJsonSchema, validateActivity, validateItemGroup, validateMedia, validateOptionMedia, writtenResponseJsonSchema };
@@ -1,4 +1,4 @@
1
- import { $ as DeferredScoringError, Dt as maxUnits, F as levelKey, I as readRubricLevels, L as countWords, P as levelDistribution, Q as ActivitySchemaError, T as SPEECH_ASSESSMENT_MAX_WORDS, at as dictationReferenceWords, dt as codePointLength, et as RedactedScoringError, ft as normalizeDictationText, it as alignSequences, nt as STEP, t as getActivityTypeDescriptor, tt as UnknownActivityTypeError, v as ReadAloudDataSchema, w as SPEECH_ASSESSMENT_MAX_TEXT_LENGTH, y as CANONICAL_LOCALE_RE } from "./registry-D92hz9iA.js";
1
+ import { B as countWords, D as SPEECH_ASSESSMENT_MAX_WORDS, E as SPEECH_ASSESSMENT_MAX_TEXT_LENGTH, L as levelDistribution, Mt as maxUnits, R as levelKey, at as STEP, b as ReadAloudDataSchema, ct as dictationReferenceWords, ht as normalizeDictationText, it as UnknownActivityTypeError, mt as codePointLength, nt as DeferredScoringError, rt as RedactedScoringError, st as alignSequences, t as getActivityTypeDescriptor, tt as ActivitySchemaError, x as CANONICAL_LOCALE_RE, y as SpeakingResponseDataSchema, z as readRubricLevels } from "./registry-Bw9grsCN.js";
2
2
  import * as z from "zod/v4";
3
3
  // src/scoring/grade-numbers.ts
4
4
  /**
@@ -1406,6 +1406,10 @@ const SpeechAssessmentSchema = z.strictObject({
1406
1406
  overall: Score.optional()
1407
1407
  }),
1408
1408
  recognizedText: maxUnits(z.string(), 8e3).optional(),
1409
+ transcript: z.strictObject({
1410
+ text: maxUnits(z.string().min(1), 8e3),
1411
+ source: AssessorSchema
1412
+ }).optional(),
1409
1413
  miscue: z.enum(["assessor", "none"]),
1410
1414
  phonemeAlphabet: z.enum(["ipa", "sapi"]).optional(),
1411
1415
  words: z.array(WordSchema).max(SPEECH_ASSESSMENT_MAX_WORDS),
@@ -1696,25 +1700,8 @@ function placeInsertions(assessment, entries) {
1696
1700
  });
1697
1701
  });
1698
1702
  }
1699
- // src/scoring/speech/grade.ts
1700
- /**
1701
- * The read-aloud grade: the arithmetic over a pronunciation assessment's
1702
- * dimension scores, and the checks that decide whether that evidence may be
1703
- * graded at all.
1704
- *
1705
- * The SDK calls no assessor and holds no key. It is given the application's
1706
- * assessment and the server's own measurement of the recording, and it refuses
1707
- * evidence it cannot tie to this item, this text and this recording — a refusal
1708
- * is never a zero, because a learner who was not heard has not failed.
1709
- */
1710
- /**
1711
- * Float slack when comparing two measurements of the same recording: voiced
1712
- * time is summed window by window, so it can land a hair above the duration it
1713
- * was measured from without either number being wrong.
1714
- */
1715
- const MEASUREMENT_TOLERANCE = 1e-6;
1716
1703
  /** What every assessor score is out of, and what a criterion declares. */
1717
- const ASSESSMENT_SCALE = 100;
1704
+ const ASSESSMENT_SCALE$1 = 100;
1718
1705
  /** A value named in an error message, without serialising the object it came from. */
1719
1706
  function describeValue$1(value) {
1720
1707
  return typeof value === "number" ? String(value) : `of type ${typeof value}`;
@@ -1731,10 +1718,10 @@ function describeValue$1(value) {
1731
1718
  * recording-key binding. Every field is read once, and what is returned is what
1732
1719
  * was checked.
1733
1720
  */
1734
- function readResponseRecording(value) {
1721
+ function readResponseRecording(value, expected = "read-aloud") {
1735
1722
  if (typeof value !== "object" || value === null) return;
1736
1723
  const { type, recording } = value;
1737
- if (type !== "read-aloud") return;
1724
+ if (type !== expected) return;
1738
1725
  if (recording === null) return { recording: null };
1739
1726
  if (typeof recording !== "object") return;
1740
1727
  const { key, mimeType } = recording;
@@ -1765,7 +1752,7 @@ function spokenWordCount(assessment) {
1765
1752
  */
1766
1753
  function scoreOf(entry) {
1767
1754
  if (entry.state === "omitted") return { score: 0 };
1768
- return entry.accuracy !== void 0 ? { score: entry.accuracy / ASSESSMENT_SCALE } : {};
1755
+ return entry.accuracy !== void 0 ? { score: entry.accuracy / ASSESSMENT_SCALE$1 } : {};
1769
1756
  }
1770
1757
  /**
1771
1758
  * Grades a read-aloud take from a {@link SpeechAssessment} the application
@@ -1836,7 +1823,7 @@ function gradeReadAloud(data, response, assessment, options) {
1836
1823
  const measured = options.measured;
1837
1824
  if (measured === null || measured === void 0) throw new TypeError("gradeReadAloud needs `options.measured`, the server's own measurement of the recording (inspectWav), for a response that carries a recording. A duration the client reported is not evidence.");
1838
1825
  const { durationMs, voicedMs } = measured;
1839
- if (typeof durationMs !== "number" || !Number.isFinite(durationMs) || durationMs < 0 || typeof voicedMs !== "number" || !Number.isFinite(voicedMs) || voicedMs < 0 || voicedMs > durationMs + MEASUREMENT_TOLERANCE) throw new RangeError(`Invalid recording measurement (durationMs ${describeValue$1(durationMs)}, voicedMs ${describeValue$1(voicedMs)}): expected finite millisecond counts, 0 or above, with voicedMs inside durationMs.`);
1826
+ if (typeof durationMs !== "number" || !Number.isFinite(durationMs) || durationMs < 0 || typeof voicedMs !== "number" || !Number.isFinite(voicedMs) || voicedMs < 0 || voicedMs > durationMs + 1e-6) throw new RangeError(`Invalid recording measurement (durationMs ${describeValue$1(durationMs)}, voicedMs ${describeValue$1(voicedMs)}): expected finite millisecond counts, 0 or above, with voicedMs inside durationMs.`);
1840
1827
  const unscorable = (code, reason) => ({
1841
1828
  unscorable: true,
1842
1829
  code,
@@ -1860,7 +1847,7 @@ function gradeReadAloud(data, response, assessment, options) {
1860
1847
  const graded = gradeFromRubric(weighed.map((dimension) => ({
1861
1848
  name: dimension.name,
1862
1849
  score: evidence.scores[dimension.name],
1863
- maxScore: ASSESSMENT_SCALE,
1850
+ maxScore: ASSESSMENT_SCALE$1,
1864
1851
  weight: dimension.weight
1865
1852
  })), item, rounding !== void 0 ? { rounding } : {});
1866
1853
  if ("unscorable" in graded) return unscorable("invalid_assessment", `No weighted total could be computed from the assessment's scores: ${graded.reason}`);
@@ -1887,6 +1874,207 @@ function gradeReadAloud(data, response, assessment, options) {
1887
1874
  function referenceWordsOf(data) {
1888
1875
  return dictationReferenceWords({ transcript: data.referenceText })[0];
1889
1876
  }
1877
+ // src/scoring/speech/grade-speaking.ts
1878
+ /**
1879
+ * The spoken-answer grade: a rubric scored partly by a judge, from the
1880
+ * transcript of what the learner said, and partly from an engine's measures of
1881
+ * how they said it.
1882
+ *
1883
+ * As for a read-aloud, the SDK calls no engine and no model. It is given the
1884
+ * application's unscripted assessment of the take, a judgement of the criteria
1885
+ * a judge scores, and the server's own measurement of the recording, and it
1886
+ * refuses evidence it cannot tie to this item and this recording — a refusal is
1887
+ * never a zero.
1888
+ */
1889
+ /** What every assessor score is out of. */
1890
+ const ASSESSMENT_SCALE = 100;
1891
+ /** The assessment score each measure is read from. */
1892
+ const MEASURE_SCORE = {
1893
+ pronunciation: "accuracy",
1894
+ fluency: "fluency",
1895
+ prosody: "prosody"
1896
+ };
1897
+ /**
1898
+ * How many words were spoken, for the speech-rate check: the transcript's when
1899
+ * the evidence carries one — it is the text the judge grades — and otherwise
1900
+ * what the engine recognised.
1901
+ */
1902
+ function wordsSpoken(evidence) {
1903
+ return evidence.transcript !== void 0 ? countWords(evidence.transcript.text) : spokenWordCount(evidence);
1904
+ }
1905
+ /**
1906
+ * The judged criteria, each read once and matched to the criterion of the
1907
+ * rubric it judges — or why the judgement cannot be graded with this item.
1908
+ */
1909
+ function matchJudgement(judgement, byJudge) {
1910
+ const listed = judgement.criteria;
1911
+ if (!Array.isArray(listed)) return "The judgement carries no list of criteria.";
1912
+ const wanted = new Map(byJudge.map((criterion) => [levelKey(criterion.name), criterion]));
1913
+ const judged = /* @__PURE__ */ new Set();
1914
+ const criteria = [];
1915
+ for (const one of listed) {
1916
+ const { name, score, level, probabilities, comment } = one ?? {};
1917
+ const key = typeof name === "string" ? levelKey(name) : void 0;
1918
+ const criterion = key === void 0 ? void 0 : wanted.get(key);
1919
+ if (key === void 0 || criterion === void 0 || judged.has(key)) return `The judgement names criterion ${typeof name === "string" ? `"${name}"` : describeValue$1(name)}, which is not one the judge scores on this item once.`;
1920
+ if (typeof score !== "number" || !(score >= 0 && score <= 1)) return `The judgement scores "${criterion.name}" ${describeValue$1(score)}, off its 0..1 scale.`;
1921
+ judged.add(key);
1922
+ criteria.push({
1923
+ name: criterion.name,
1924
+ score,
1925
+ weight: criterion.weight,
1926
+ ...level !== void 0 ? { band: level } : {},
1927
+ ...probabilities !== void 0 ? { levelProbabilities: [...probabilities] } : {},
1928
+ ...comment !== void 0 ? { comment } : {}
1929
+ });
1930
+ }
1931
+ const left = byJudge.find((criterion) => !judged.has(levelKey(criterion.name)));
1932
+ if (left !== void 0) return `The judgement leaves out criterion "${left.name}". A total over fewer criteria would grade a different task.`;
1933
+ return criteria;
1934
+ }
1935
+ /**
1936
+ * Grades a spoken answer to a `speaking-response` item (1.9).
1937
+ *
1938
+ * Each criterion of the rubric is scored once:
1939
+ *
1940
+ * - **by the judge** when it names no `measure` — what was said, how it was
1941
+ * organised, the range and accuracy of the language — from
1942
+ * `evidence.judgement`: a model's reply as `checkAiGrading` read it, or a
1943
+ * person's marks in the same shape, each from 0 to 1;
1944
+ * - **from the engine** when it names one: `pronunciation` is the assessment's
1945
+ * accuracy, `fluency` its fluency, `prosody` its prosody, each out of 100. With
1946
+ * `options.judgeMeasures` the judge scores those too, and the engine's scores
1947
+ * are not read.
1948
+ *
1949
+ * The weighted total is `gradeFromRubric`'s, over the item's weights and pass
1950
+ * line. Evidence is refused rather than scored, as a read-aloud's is: an
1951
+ * assessment that is not `unscripted`, not for the item's locale or not for
1952
+ * this recording; one from a model, unless `allowAiAssessor`; a take with no
1953
+ * speech, too little voiced time, or more words in its transcript than the
1954
+ * voiced time holds; a weighted measure the assessment has no score for
1955
+ * (`missing_dimension`); and a judgement that does not score each criterion the
1956
+ * judge scores exactly once, on its 0..1 scale (`judgement_mismatch`). A
1957
+ * judgement is matched whenever one is given, so marks meant for another item,
1958
+ * or for measures the judge was not asked to score, are refused.
1959
+ *
1960
+ * A blank (`response.recording === null`) is a grade of 0: the learner
1961
+ * submitted and said nothing. The evidence and the measurement are not read.
1962
+ *
1963
+ * With a judge, the grade is the judge's, as `gradeFromJudgement` makes one:
1964
+ * its feedback (or the authored feedback, when it gave none), its corrections
1965
+ * and rationale, `requiresHumanReview`, and `grader: { kind: 'ai', model,
1966
+ * promptHash }` — so `routeGrade` routes a spoken answer as it routes a written
1967
+ * one. Without one, the feedback is the authored feedback and the grader is the
1968
+ * engine that measured.
1969
+ *
1970
+ * @throws RangeError when `options.plausibility` or `options.rounding` cannot be
1971
+ * applied, or `options.measured` is not a pair of finite, non-negative
1972
+ * millisecond counts with `voicedMs` inside `durationMs`.
1973
+ * @throws TypeError when `response` is not a speaking-response response, or a
1974
+ * recording is graded without an assessment, a measurement, or — when the
1975
+ * judge scores a criterion — a judgement.
1976
+ * @throws ActivitySchemaError when `data` is not valid speaking-response data.
1977
+ * @throws RedactedScoringError when `data` is a `redact()` projection.
1978
+ */
1979
+ function gradeSpeakingResponse(data, response, evidence, options) {
1980
+ const maxWordsPerSecond = options?.plausibility?.maxWordsPerSecond;
1981
+ if (typeof maxWordsPerSecond !== "number" || !Number.isFinite(maxWordsPerSecond) || maxWordsPerSecond <= 0) throw new RangeError(`Invalid plausibility policy (maxWordsPerSecond ${describeValue$1(maxWordsPerSecond)}): expected a finite number above 0.`);
1982
+ const minVoicedMs = options?.plausibility?.minVoicedMs;
1983
+ if (typeof minVoicedMs !== "number" || !Number.isFinite(minVoicedMs) || minVoicedMs < 0) throw new RangeError(`Invalid plausibility policy (minVoicedMs ${describeValue$1(minVoicedMs)}): expected a finite number of milliseconds, 0 or above.`);
1984
+ const rounding = roundingPolicyOf(options?.rounding);
1985
+ const parsed = SpeakingResponseDataSchema.safeParse(data);
1986
+ if (!parsed.success) throw new ActivitySchemaError("speaking-response", parsed.error.issues.map((issue) => ({
1987
+ path: issue.path.map(String),
1988
+ message: issue.message,
1989
+ code: issue.code
1990
+ })));
1991
+ if (isRedacted(data) || isRedacted(parsed.data)) throw new RedactedScoringError("speaking-response");
1992
+ const item = parsed.data;
1993
+ const checkedResponse = readResponseRecording(response, "speaking-response");
1994
+ if (checkedResponse === void 0) throw new TypeError("gradeSpeakingResponse needs a speaking-response response: { type: \"speaking-response\", recording: null } for a blank, or a recording with a non-empty `key` and a `mimeType`.");
1995
+ const recording = checkedResponse.recording;
1996
+ if (recording === null) {
1997
+ const passed = computePassThreshold(item, 0, rounding);
1998
+ return {
1999
+ score: 0,
2000
+ maxScore: 1,
2001
+ passed,
2002
+ feedback: selectFeedback(item, passed),
2003
+ criteria: []
2004
+ };
2005
+ }
2006
+ const judgeMeasures = options.judgeMeasures === true;
2007
+ const byJudge = item.rubric.criteria.filter((criterion) => judgeMeasures || criterion.measure === void 0);
2008
+ const byEngine = item.rubric.criteria.filter((criterion) => !judgeMeasures && criterion.measure !== void 0);
2009
+ const assessment = evidence?.assessment;
2010
+ if (assessment === null || assessment === void 0) throw new TypeError("gradeSpeakingResponse needs the engine's unscripted assessment of a response that carries a recording. Pass one, or grade the blank (recording: null).");
2011
+ const judgement = evidence.judgement ?? void 0;
2012
+ if (judgement === void 0 && byJudge.length > 0) throw new TypeError(`gradeSpeakingResponse needs a judgement of the criteria a judge scores on this item: ${byJudge.map((criterion) => `"${criterion.name}"`).join(", ")}.`);
2013
+ const measured = options.measured;
2014
+ if (measured === null || measured === void 0) throw new TypeError("gradeSpeakingResponse needs `options.measured`, the server's own measurement of the recording (inspectWav), for a response that carries a recording. A duration the client reported is not evidence.");
2015
+ const { durationMs, voicedMs } = measured;
2016
+ if (typeof durationMs !== "number" || !Number.isFinite(durationMs) || durationMs < 0 || typeof voicedMs !== "number" || !Number.isFinite(voicedMs) || voicedMs < 0 || voicedMs > durationMs + 1e-6) throw new RangeError(`Invalid recording measurement (durationMs ${describeValue$1(durationMs)}, voicedMs ${describeValue$1(voicedMs)}): expected finite millisecond counts, 0 or above, with voicedMs inside durationMs.`);
2017
+ const unscorable = (code, reason) => ({
2018
+ unscorable: true,
2019
+ code,
2020
+ reason
2021
+ });
2022
+ const checked = validateSpeechAssessment(assessment);
2023
+ if (!checked.success) return unscorable("invalid_assessment", `The speech assessment is not well formed (${describeValidationError(checked.errors[0])}).`);
2024
+ const engine = checked.data;
2025
+ if (engine.task !== "unscripted") return unscorable("task_mismatch", `The assessment was made for a "${engine.task}" task; a spoken answer is assessed unscripted, against what the learner said.`);
2026
+ if (engine.locale !== item.locale) return unscorable("locale_mismatch", `The assessment was made for locale "${engine.locale}", and the item is answered in "${item.locale}". Pronunciation is assessed against one locale's speech.`);
2027
+ if (engine.recordingKey !== recording.key) return unscorable("recording_mismatch", engine.recordingKey === void 0 ? `The assessment names no recording, and this response submitted "${recording.key}". Unscripted evidence must carry the key of the recording it measured.` : `The assessment was made for recording "${engine.recordingKey}", and this response submitted "${recording.key}".`);
2028
+ if (engine.assessor.kind === "ai" && options.allowAiAssessor !== true) return unscorable("assessor_not_accepted", "The assessment was produced by a generative model. Pass `allowAiAssessor: true` to accept that as measurement evidence.");
2029
+ if (engine.status === "no_speech") return unscorable("no_speech", "The assessor heard no speech in this recording.");
2030
+ if (voicedMs < minVoicedMs) return unscorable("insufficient_voiced_time", `The recording holds ${voicedMs} ms of voiced time, and the policy asks for at least ${minVoicedMs} ms.`);
2031
+ const words = wordsSpoken(engine);
2032
+ if (words > 0 && (voicedMs === 0 || words / (voicedMs / 1e3) > maxWordsPerSecond)) return unscorable("implausible_speech_rate", `The assessment claims ${words} word(s) in ${voicedMs} ms of voiced time, above the ${maxWordsPerSecond} word(s) per second the policy allows.`);
2033
+ const missing = byEngine.find((criterion) => criterion.weight > 0 && engine.scores[MEASURE_SCORE[criterion.measure]] === void 0);
2034
+ if (missing !== void 0) {
2035
+ const score = MEASURE_SCORE[missing.measure];
2036
+ return unscorable("missing_dimension", `The item scores "${missing.name}" from ${missing.measure}, and the assessment carries no ${score} score. An absent score is not a 0.`);
2037
+ }
2038
+ const criteria = [];
2039
+ for (const criterion of byEngine) {
2040
+ const score = engine.scores[MEASURE_SCORE[criterion.measure]];
2041
+ if (score !== void 0) criteria.push({
2042
+ name: criterion.name,
2043
+ score,
2044
+ maxScore: ASSESSMENT_SCALE,
2045
+ weight: criterion.weight
2046
+ });
2047
+ }
2048
+ if (judgement !== void 0) {
2049
+ const judged = matchJudgement(judgement, byJudge);
2050
+ if (typeof judged === "string") return unscorable("judgement_mismatch", judged);
2051
+ criteria.push(...judged);
2052
+ }
2053
+ const order = new Map(item.rubric.criteria.map((criterion, index) => [levelKey(criterion.name), index]));
2054
+ criteria.sort((a, b) => order.get(levelKey(a.name)) - order.get(levelKey(b.name)));
2055
+ const graded = gradeFromRubric(criteria, item, rounding !== void 0 ? { rounding } : {});
2056
+ if ("unscorable" in graded) return unscorable("invalid_assessment", `No weighted total could be computed from the evidence: ${graded.reason}`);
2057
+ if (byJudge.length === 0 || judgement === void 0) return {
2058
+ ...graded,
2059
+ feedback: selectFeedback(item, graded.passed),
2060
+ grader: { ...engine.assessor }
2061
+ };
2062
+ const { provenance, usage } = judgement;
2063
+ return {
2064
+ ...graded,
2065
+ feedback: judgement.feedback ?? selectFeedback(item, graded.passed),
2066
+ ...judgement.corrections.length > 0 ? { corrections: [...judgement.corrections] } : {},
2067
+ ...judgement.rationale !== void 0 ? { rationale: judgement.rationale } : {},
2068
+ requiresHumanReview: judgement.requiresHumanReview,
2069
+ grader: {
2070
+ kind: "ai",
2071
+ ...provenance?.model !== void 0 ? { model: provenance.model } : {},
2072
+ ...provenance?.promptHash !== void 0 ? { promptHash: provenance.promptHash } : {}
2073
+ },
2074
+ ...usage !== void 0 ? { usage } : {},
2075
+ ...provenance?.generatedAt !== void 0 ? { gradedAt: provenance.generatedAt } : {}
2076
+ };
2077
+ }
1890
2078
  // src/scoring/speech/wav.ts
1891
2079
  /** `RIFF` + the file size + `WAVE`, before the first chunk. */
1892
2080
  const HEADER_BYTES = 12;
@@ -2233,6 +2421,6 @@ function costed(data, opening, graded, rules, rounding) {
2233
2421
  tries: result.tries
2234
2422
  };
2235
2423
  }
2236
- export { DEFAULT_ITEM_SCORING_POLICY as A, contentHash as C, classifyBand as D, computePassThreshold as E, isGradeInRange as F, resolveItemScoringPolicy as M, scoreTries as N, gte as O, validateItemScoringPolicy as P, canonicalJson as S, DEFAULT_PASS_THRESHOLD as T, gradeFromRubric as _, inspectWav as a, calibrationReport as b, validateSpeechAssessment as c, inAuditSample as d, outcomeFromRoutedGrade as f, gradeFromJudgement as g, seededShuffle as h, outcomeFromUnscorable as i, ITEM_SCORING_MAX_RETRIES as j, roundGrade as k, diffDictationChars as l, hashSeed as m, evaluateTries as n, gradeReadAloud as o, routeGrade as p, score as r, alignReadAloud as s, evaluate as t, composeAssessmentScore as u, hasGrade as v, fingerprint as w, graderConfiguration as x, outcomeFromGrade as y };
2424
+ export { roundGrade as A, canonicalJson as C, computePassThreshold as D, DEFAULT_PASS_THRESHOLD as E, validateItemScoringPolicy as F, isGradeInRange as I, ITEM_SCORING_MAX_RETRIES as M, resolveItemScoringPolicy as N, classifyBand as O, scoreTries as P, graderConfiguration as S, fingerprint as T, gradeFromJudgement as _, inspectWav as a, outcomeFromGrade as b, alignReadAloud as c, composeAssessmentScore as d, inAuditSample as f, seededShuffle as g, hashSeed as h, outcomeFromUnscorable as i, DEFAULT_ITEM_SCORING_POLICY as j, gte as k, validateSpeechAssessment as l, routeGrade as m, evaluateTries as n, gradeSpeakingResponse as o, outcomeFromRoutedGrade as p, score as r, gradeReadAloud as s, evaluate as t, diffDictationChars as u, gradeFromRubric as v, contentHash as w, calibrationReport as x, hasGrade as y };
2237
2425
 
2238
- //# sourceMappingURL=scoring-DD1GHcLI.js.map
2426
+ //# sourceMappingURL=scoring-BGKdff2i.js.map