@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.
- package/CHANGELOG.md +33 -0
- package/dist/ai-check.cjs +77 -3
- package/dist/ai-check.cjs.map +1 -1
- package/dist/ai-check.d.cts +11 -3
- package/dist/ai-check.d.cts.map +1 -1
- package/dist/ai-check.d.ts +11 -3
- package/dist/ai-check.d.ts.map +1 -1
- package/dist/ai-check.js +77 -3
- package/dist/ai-check.js.map +1 -1
- package/dist/{ai-writing-B7oMCt66.js → ai-writing-DQjI9EFN.js} +2514 -136
- package/dist/ai-writing-DQjI9EFN.js.map +1 -0
- package/dist/{ai-writing-DrPTV3R2.cjs → ai-writing-DbpZpGRk.cjs} +2614 -122
- package/dist/ai-writing-DbpZpGRk.cjs.map +1 -0
- package/dist/{ai-B1wYrqcB.d.ts → ai-zhc9yhRj.d.cts} +454 -2
- package/dist/ai-zhc9yhRj.d.cts.map +1 -0
- package/dist/{ai-B1wYrqcB.d.cts → ai-zhc9yhRj.d.ts} +454 -2
- package/dist/ai-zhc9yhRj.d.ts.map +1 -0
- package/dist/{index-XCCoPoTU.d.cts → index-BeC8tAWf.d.cts} +2 -2
- package/dist/{index-XCCoPoTU.d.cts.map → index-BeC8tAWf.d.cts.map} +1 -1
- package/dist/{index-BU99GFTl.d.ts → index-Byar5VVs.d.ts} +2 -2
- package/dist/{index-BU99GFTl.d.ts.map → index-Byar5VVs.d.ts.map} +1 -1
- package/dist/{index-DP9rvyOz.d.cts → index-Ccj8oL_E.d.cts} +2 -2
- package/dist/{index-DP9rvyOz.d.cts.map → index-Ccj8oL_E.d.cts.map} +1 -1
- package/dist/{index-BTkMQtws.d.ts → index-CxkqS62X.d.ts} +89 -7
- package/dist/index-CxkqS62X.d.ts.map +1 -0
- package/dist/{index-BM8LeDWn.d.cts → index-DLsUnGHt.d.cts} +89 -7
- package/dist/index-DLsUnGHt.d.cts.map +1 -0
- package/dist/{index-BdXs4-en.d.ts → index-f4YuMgFb.d.ts} +2 -2
- package/dist/{index-BdXs4-en.d.ts.map → index-f4YuMgFb.d.ts.map} +1 -1
- package/dist/index.cjs +282 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +262 -7
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +262 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +269 -7
- package/dist/index.js.map +1 -1
- package/dist/{item-group-CeDk7mfM.js → item-group-CKZk3txt.js} +2 -2
- package/dist/{item-group-CeDk7mfM.js.map → item-group-CKZk3txt.js.map} +1 -1
- package/dist/{item-group-8P3WFuUK.cjs → item-group-COP5OwEC.cjs} +2 -2
- package/dist/{item-group-8P3WFuUK.cjs.map → item-group-COP5OwEC.cjs.map} +1 -1
- package/dist/{registry-D92hz9iA.js → registry-Bw9grsCN.js} +339 -12
- package/dist/registry-Bw9grsCN.js.map +1 -0
- package/dist/{registry-hzrkTeIT.cjs → registry-C9O9txy5.cjs} +368 -11
- package/dist/registry-C9O9txy5.cjs.map +1 -0
- package/dist/{schemas-C7TViWCg.js → schemas-BJe3W6Xb.js} +3 -3
- package/dist/{schemas-C7TViWCg.js.map → schemas-BJe3W6Xb.js.map} +1 -1
- package/dist/{schemas-BWeskONy.cjs → schemas-CXlemRvB.cjs} +3 -3
- package/dist/{schemas-BWeskONy.cjs.map → schemas-CXlemRvB.cjs.map} +1 -1
- package/dist/schemas.cjs +3 -3
- package/dist/schemas.d.cts +1 -1
- package/dist/schemas.d.ts +1 -1
- package/dist/schemas.js +3 -3
- package/dist/{scoring-DD1GHcLI.js → scoring-BGKdff2i.js} +214 -26
- package/dist/scoring-BGKdff2i.js.map +1 -0
- package/dist/{scoring-CtkCFyFT.cjs → scoring-Db1CZeAn.cjs} +219 -25
- package/dist/scoring-Db1CZeAn.cjs.map +1 -0
- package/dist/scoring.cjs +3 -2
- package/dist/scoring.d.cts +3 -3
- package/dist/scoring.d.ts +3 -3
- package/dist/scoring.js +3 -3
- package/dist/{xapi-Cgicg-zU.js → xapi-BtBm8AWZ.js} +2 -2
- package/dist/{xapi-Cgicg-zU.js.map → xapi-BtBm8AWZ.js.map} +1 -1
- package/dist/{xapi-7btSpx6v.cjs → xapi-DOAqH9__.cjs} +2 -2
- package/dist/{xapi-7btSpx6v.cjs.map → xapi-DOAqH9__.cjs.map} +1 -1
- package/dist/xapi.cjs +1 -1
- package/dist/xapi.d.cts +1 -1
- package/dist/xapi.d.ts +1 -1
- package/dist/xapi.js +1 -1
- package/package.json +2 -2
- package/vectors/README.md +23 -0
- package/vectors/scoring.json +11967 -1
- package/vectors/validation.json +1298 -1
- package/vectors/variants.d.mts +20 -0
- package/vectors/variants.json +5753 -0
- package/vectors/variants.mjs +67 -0
- package/dist/ai-B1wYrqcB.d.cts.map +0 -1
- package/dist/ai-B1wYrqcB.d.ts.map +0 -1
- package/dist/ai-writing-B7oMCt66.js.map +0 -1
- package/dist/ai-writing-DrPTV3R2.cjs.map +0 -1
- package/dist/index-BM8LeDWn.d.cts.map +0 -1
- package/dist/index-BTkMQtws.d.ts.map +0 -1
- package/dist/registry-D92hz9iA.js.map +0 -1
- package/dist/registry-hzrkTeIT.cjs.map +0 -1
- package/dist/scoring-CtkCFyFT.cjs.map +0 -1
- package/dist/scoring-DD1GHcLI.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { r as StimulusSchema, t as ItemGroupSchema } from "./item-group-
|
|
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-
|
|
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-
|
|
2
|
-
const require_item_group = require("./item-group-
|
|
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-
|
|
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-
|
|
3
|
-
const require_item_group = require("./item-group-
|
|
4
|
-
const require_schemas = require("./schemas-
|
|
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;
|
package/dist/schemas.d.cts
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-
|
|
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-
|
|
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 {
|
|
2
|
-
import { i as validateItemGroup } from "./item-group-
|
|
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-
|
|
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 {
|
|
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 !==
|
|
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 +
|
|
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 {
|
|
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-
|
|
2426
|
+
//# sourceMappingURL=scoring-BGKdff2i.js.map
|