@exercode/problem-utils 5.0.4 → 5.1.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.
@@ -1,2 +1,2 @@
1
- "use strict";const e=require("../types/language.cjs"),t=require("../types/problem.cjs");let n=require("zod"),r=require("@willbooster/shared-lib");const i=/^[0-9_a-z-]+$/,a=n.z.string().regex(i),o=Object.keys(e.languageIdToDefinition),s=n.z.enum(o),c=n.z.string().regex(/^\w+$/),l=n.z.record(n.z.string(),n.z.unknown()),u=n.z.number().int().min(100).max(599),d=n.z.string().startsWith(`/`).refine(e=>!e.includes(`?`)&&!e.includes(`#`),`path must not carry a query or fragment; use query`).refine(e=>e.split(`/`).every(e=>!/[{}]/.test(e)||/^\{[^{}]+\}$/.test(e)),`a {name} placeholder must be one whole path segment`),f=l.refine(e=>k(e),`header names in properties and required must be lower case`),p=n.z.strictObject({playgrounds:n.z.array(n.z.enum([`bbs`,`backlog`,`hosting`])).min(1),expectedRequest:n.z.strictObject({method:n.z.enum([`GET`,`POST`,`PUT`,`PATCH`,`DELETE`]),path:d,status:n.z.union([u,n.z.array(u).min(1)]).optional(),query:l.optional(),headers:f.optional(),body:l.optional()})}),m=n.z.strictObject({name:n.z.string().min(1),type:n.z.enum([`prompt_study`]).optional(),timeLimitMs:n.z.number().int().min(0).optional(),memoryLimitByte:n.z.number().int().min(0).optional(),requiredRegExpsInCode:t.codeRuleSchema.array().default([]),forbiddenRegExpsInCode:t.codeRuleSchema.array().default([]),forbiddenTextsInCode:t.codeRuleSchema.array().default([]),canCreateFiles:n.z.boolean().optional(),isEditorDisabled:n.z.boolean().optional(),isAttachedFileRequired:n.z.boolean().optional(),isManualScoringRequired:n.z.boolean().optional(),isVotable:n.z.boolean().optional(),requiredEnvironmentVariables:n.z.array(c).default([]),requiredOutputFilePaths:n.z.string().min(1).array().default([]),requiredSubmissionFilePaths:n.z.string().min(1).array().default([]),agentChallenge:p.optional()}),h={availableLanguageIds:s.array().optional(),areTestCasesHidden:n.z.boolean().optional(),isProblemGradingResultHidden:n.z.boolean().optional(),isAutoFormatDisabled:n.z.boolean().optional(),isCopyAndPasteDisabled:n.z.boolean().optional(),isDebugHintDisabled:n.z.boolean().optional(),isFixHintDisabled:n.z.boolean().optional(),isDiffHintDisabled:n.z.boolean().optional(),debugHintWaitingSeconds:n.z.number().min(0).optional(),fixHintWaitingSeconds:n.z.number().min(0).optional(),diffHintWaitingSeconds:n.z.number().min(0).optional(),submissionOpenedAt:n.z.iso.datetime({offset:!0}).optional(),submissionSoftClosedAt:n.z.iso.datetime({offset:!0}).optional(),submissionHardClosedAt:n.z.iso.datetime({offset:!0}).optional(),isAutoTranslationDisabled:n.z.boolean().optional(),isModelAnswerShownAfterDeadline:n.z.boolean().optional(),isVotable:n.z.boolean().optional(),isMaterialChatDisabled:n.z.boolean().optional()},g=n.z.strictObject({...h,name:n.z.string().min(1),description:n.z.string(),author:n.z.string().min(1).optional(),isMotivationFeatureEnabled:n.z.boolean().optional(),isPublic:n.z.boolean().optional(),lectures:n.z.array(n.z.object({id:a,name:n.z.string().min(1),description:n.z.string()})).optional()}),_=n.z.string().array().min(1).refine(e=>e.length===new Set(e).size,{error:`options must contain unique values`}),v=n.z.string().regex(/^\d+$/,{error:`must be a non-negative integer index (quoted numbers such as "0" are accepted)`}).transform(Number),y={id:a,isResubmittable:n.z.boolean().optional(),isSurvey:n.z.boolean().optional(),explanation:n.z.string().optional(),hint:n.z.string().optional()},b={...y,type:n.z.literal(`select`),options:_,answerIndex:n.z.union([n.z.number().int().nonnegative(),v,n.z.number().int().nonnegative().array(),v.array()]).optional()},x={...y,type:n.z.literal(`select_multiple`),options:_,answerIndices:n.z.union([n.z.number().int().nonnegative().array(),v.array()]).optional()},S={...y,type:n.z.literal(`text`),answerPattern:n.z.string().min(1).optional(),modelAnswer:n.z.string().optional()},C=n.z.discriminatedUnion(`type`,[n.z.strictObject({...b,question:n.z.string()}),n.z.strictObject({...x,question:n.z.string()}),n.z.strictObject({...S,question:n.z.string()})]),w=n.z.discriminatedUnion(`type`,[n.z.strictObject({...b,question:n.z.string().optional()}),n.z.strictObject({...x,question:n.z.string().optional()}),n.z.strictObject({...S,question:n.z.string().optional()})]),T=n.z.strictObject({...h,name:n.z.string().min(1),problems:n.z.strictObject({id:a}).array().optional(),questions:n.z.array(w).optional(),turtleGraphicsQuestions:n.z.object({id:a}).array().optional(),isExamination:n.z.boolean().optional(),isMockExamination:n.z.boolean().optional(),isRealtimeSurvey:n.z.boolean().optional()}),E=n.z.strictObject({id:a,name:n.z.string().min(1),openedAt:n.z.iso.datetime({offset:!0}),closedAt:n.z.iso.datetime({offset:!0}),password:n.z.string().min(1).optional()}).refine(e=>new Date(e.openedAt)<new Date(e.closedAt),{error:`openedAt must be earlier than closedAt`}),D=n.z.strictObject({id:a,score:n.z.number().int().nonnegative(),areTestCasesHidden:h.areTestCasesHidden,isDebugHintDisabled:h.isDebugHintDisabled,isFixHintDisabled:h.isFixHintDisabled,isDiffHintDisabled:h.isDiffHintDisabled,debugHintWaitingSeconds:h.debugHintWaitingSeconds,fixHintWaitingSeconds:h.fixHintWaitingSeconds,diffHintWaitingSeconds:h.diffHintWaitingSeconds,isMaterialChatDisabled:h.isMaterialChatDisabled}),O=n.z.strictObject({...h,name:n.z.string().min(1),description:n.z.string().optional(),adminEmails:n.z.array(n.z.string().trim().email()).optional(),showsProblemsAfterClose:n.z.boolean().optional(),divisions:n.z.array(E).min(1),problems:n.z.array(D).min(1)});function k(e,t=/* @__PURE__ */ new WeakSet){if(typeof e!=`object`||!e||t.has(e))return!0;if(t.add(e),Array.isArray(e))return e.every(e=>k(e,t));if(!(0,r.isRecord)(e))return!0;let n=(0,r.isRecord)(e.properties)?Object.keys(e.properties):[],i=Array.isArray(e.required)?e.required:[];return[...n,...i].every(e=>typeof e!=`string`||e===e.toLowerCase())&&Object.values(e).every(e=>k(e,t))}exports.CONTEST_MATERIAL_FILE_SUFFIX=`.contest.yaml`,exports.LEARNING_MATERIAL_ID_REGEX=i,exports.agentChallengeSchema=p,exports.availableLanguageIds=o,exports.contestFileSchema=O,exports.courseFileSchema=g,exports.learningMaterialIdSchema=a,exports.materialFrontmatterSchema=T,exports.problemFrontmatterSchema=m,exports.questionInCodeBlockSchema=C;
1
+ "use strict";const e=require("../types/language.cjs"),t=require("../types/problem.cjs");let n=require("zod"),r=require("@willbooster/shared-lib");const i=/^[0-9_a-z-]+$/,a=n.z.string().regex(i),o=Object.keys(e.languageIdToDefinition),s=n.z.enum(o),c=n.z.string().regex(/^\w+$/),l=n.z.record(n.z.string(),n.z.unknown()),u=n.z.number().int().min(100).max(599),d=n.z.string().startsWith(`/`).refine(e=>!e.includes(`?`)&&!e.includes(`#`),`path must not carry a query or fragment; use query`).refine(e=>e.split(`/`).every(e=>!/[{}]/.test(e)||/^\{[^{}]+\}$/.test(e)),`a {name} placeholder must be one whole path segment`),f=l.refine(e=>k(e),`header names in properties and required must be lower case`),p=n.z.strictObject({playgrounds:n.z.array(n.z.enum([`bbs`,`backlog`,`hosting`])).min(1),expectedRequest:n.z.strictObject({method:n.z.enum([`GET`,`POST`,`PUT`,`PATCH`,`DELETE`]),path:d,status:n.z.union([u,n.z.array(u).min(1)]).optional(),query:l.optional(),headers:f.optional(),body:l.optional()})}),m=n.z.strictObject({name:n.z.string().min(1),type:n.z.enum([`prompt_study`]).optional(),timeLimitMs:n.z.number().int().min(0).optional(),memoryLimitByte:n.z.number().int().min(0).optional(),requiredRegExpsInCode:t.codeRuleSchema.array().default([]),forbiddenRegExpsInCode:t.codeRuleSchema.array().default([]),forbiddenTextsInCode:t.codeRuleSchema.array().default([]),canCreateFiles:n.z.boolean().optional(),isEditorDisabled:n.z.boolean().optional(),isAttachedFileRequired:n.z.boolean().optional(),isManualScoringRequired:n.z.boolean().optional(),isVotable:n.z.boolean().optional(),requiredEnvironmentVariables:n.z.array(c).default([]),requiredOutputFilePaths:n.z.string().min(1).array().default([]),requiredSubmissionFilePaths:n.z.string().min(1).array().default([]),agentChallenge:p.optional()}),h={availableLanguageIds:s.array().optional(),areTestCasesHidden:n.z.boolean().optional(),isProblemGradingResultHidden:n.z.boolean().optional(),isAutoFormatDisabled:n.z.boolean().optional(),isCopyAndPasteDisabled:n.z.boolean().optional(),isDebugHintDisabled:n.z.boolean().optional(),isFixHintDisabled:n.z.boolean().optional(),isDiffHintDisabled:n.z.boolean().optional(),debugHintWaitingSeconds:n.z.number().min(0).optional(),fixHintWaitingSeconds:n.z.number().min(0).optional(),diffHintWaitingSeconds:n.z.number().min(0).optional(),submissionOpenedAt:n.z.iso.datetime({offset:!0}).optional(),submissionSoftClosedAt:n.z.iso.datetime({offset:!0}).optional(),submissionHardClosedAt:n.z.iso.datetime({offset:!0}).optional(),isAutoTranslationDisabled:n.z.boolean().optional(),isModelAnswerShownAfterDeadline:n.z.boolean().optional(),isVotable:n.z.boolean().optional(),isMaterialChatDisabled:n.z.boolean().optional()},g=n.z.strictObject({...h,name:n.z.string().min(1),description:n.z.string(),author:n.z.string().min(1).optional(),isMotivationFeatureEnabled:n.z.boolean().optional(),isPublic:n.z.boolean().optional(),startsAt:n.z.iso.datetime({offset:!0}).optional(),lectures:n.z.array(n.z.object({id:a,name:n.z.string().min(1),description:n.z.string()})).optional()}),_=n.z.string().array().min(1).refine(e=>e.length===new Set(e).size,{error:`options must contain unique values`}),v=n.z.string().regex(/^\d+$/,{error:`must be a non-negative integer index (quoted numbers such as "0" are accepted)`}).transform(Number),y={id:a,isResubmittable:n.z.boolean().optional(),isSurvey:n.z.boolean().optional(),explanation:n.z.string().optional(),hint:n.z.string().optional()},b={...y,type:n.z.literal(`select`),options:_,answerIndex:n.z.union([n.z.number().int().nonnegative(),v,n.z.number().int().nonnegative().array(),v.array()]).optional()},x={...y,type:n.z.literal(`select_multiple`),options:_,answerIndices:n.z.union([n.z.number().int().nonnegative().array(),v.array()]).optional()},S={...y,type:n.z.literal(`text`),answerPattern:n.z.string().min(1).optional(),modelAnswer:n.z.string().optional()},C=n.z.discriminatedUnion(`type`,[n.z.strictObject({...b,question:n.z.string()}),n.z.strictObject({...x,question:n.z.string()}),n.z.strictObject({...S,question:n.z.string()})]),w=n.z.discriminatedUnion(`type`,[n.z.strictObject({...b,question:n.z.string().optional()}),n.z.strictObject({...x,question:n.z.string().optional()}),n.z.strictObject({...S,question:n.z.string().optional()})]),T=n.z.strictObject({...h,name:n.z.string().min(1),problems:n.z.strictObject({id:a}).array().optional(),questions:n.z.array(w).optional(),turtleGraphicsQuestions:n.z.object({id:a}).array().optional(),isExamination:n.z.boolean().optional(),isMockExamination:n.z.boolean().optional(),isRealtimeSurvey:n.z.boolean().optional()}),E=n.z.strictObject({id:a,name:n.z.string().min(1),openedAt:n.z.iso.datetime({offset:!0}),closedAt:n.z.iso.datetime({offset:!0}),password:n.z.string().min(1).optional()}).refine(e=>new Date(e.openedAt)<new Date(e.closedAt),{error:`openedAt must be earlier than closedAt`}),D=n.z.strictObject({id:a,score:n.z.number().int().nonnegative(),areTestCasesHidden:h.areTestCasesHidden,isDebugHintDisabled:h.isDebugHintDisabled,isFixHintDisabled:h.isFixHintDisabled,isDiffHintDisabled:h.isDiffHintDisabled,debugHintWaitingSeconds:h.debugHintWaitingSeconds,fixHintWaitingSeconds:h.fixHintWaitingSeconds,diffHintWaitingSeconds:h.diffHintWaitingSeconds,isMaterialChatDisabled:h.isMaterialChatDisabled}),O=n.z.strictObject({...h,name:n.z.string().min(1),description:n.z.string().optional(),adminEmails:n.z.array(n.z.string().trim().email()).optional(),showsProblemsAfterClose:n.z.boolean().optional(),divisions:n.z.array(E).min(1),problems:n.z.array(D).min(1)});function k(e,t=/* @__PURE__ */ new WeakSet){if(typeof e!=`object`||!e||t.has(e))return!0;if(t.add(e),Array.isArray(e))return e.every(e=>k(e,t));if(!(0,r.isRecord)(e))return!0;let n=(0,r.isRecord)(e.properties)?Object.keys(e.properties):[],i=Array.isArray(e.required)?e.required:[];return[...n,...i].every(e=>typeof e!=`string`||e===e.toLowerCase())&&Object.values(e).every(e=>k(e,t))}exports.CONTEST_MATERIAL_FILE_SUFFIX=`.contest.yaml`,exports.LEARNING_MATERIAL_ID_REGEX=i,exports.agentChallengeSchema=p,exports.availableLanguageIds=o,exports.contestFileSchema=O,exports.courseFileSchema=g,exports.learningMaterialIdSchema=a,exports.materialFrontmatterSchema=T,exports.problemFrontmatterSchema=m,exports.questionInCodeBlockSchema=C;
2
2
  //# sourceMappingURL=schemas.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"schemas.cjs","names":["z","languageIdToDefinition","codeRuleSchema","isRecord","require_language","require_problem","zod","_willbooster_shared_lib","LEARNING_MATERIAL_ID_REGEX","learningMaterialIdSchema","availableLanguageIds","availableLanguageIdSchema","environmentVariableNameSchema","jsonSchemaSchema","httpStatusSchema","pathTemplateSchema","path","segment","headersJsonSchemaSchema","declaresOnlyLowerCaseNames","schema","agentChallengeSchema","problemFrontmatterSchema","materialConfigShape","courseFileSchema","uniqueOptionsSchema","options","answerIndexStringSchema","questionBaseShape","selectQuestionShape","selectMultipleQuestionShape","textQuestionShape","questionInCodeBlockSchema","questionInFrontmatterSchema","materialFrontmatterSchema","contestDivisionSchema","division","contestProblemSchema","contestFileSchema","visited","item","propertyNames","requiredNames","name","value"],"sources":["../../src/learningMaterial/schemas.ts"],"sourcesContent":["import { isRecord } from '@willbooster/shared-lib';\nimport { z } from 'zod';\n\nimport { languageIdToDefinition } from '../types/language.js';\nimport { codeRuleSchema } from '../types/problem.js';\n\n// Schemas in this file mirror the Exercode judge importer so that generated materials validate\n// exactly like the importer would parse them.\n\nexport const LEARNING_MATERIAL_ID_REGEX = /^[0-9_a-z-]+$/;\n\nexport const learningMaterialIdSchema = z.string().regex(LEARNING_MATERIAL_ID_REGEX);\n\nexport const availableLanguageIds = Object.keys(languageIdToDefinition);\nconst availableLanguageIdSchema = z.enum(availableLanguageIds);\n\nconst environmentVariableNameSchema = z.string().regex(/^\\w+$/);\n\nconst jsonSchemaSchema = z.record(z.string(), z.unknown());\nconst httpStatusSchema = z.number().int().min(100).max(599);\nconst pathTemplateSchema = z\n .string()\n .startsWith('/')\n .refine((path) => !path.includes('?') && !path.includes('#'), 'path must not carry a query or fragment; use query')\n .refine(\n (path) => path.split('/').every((segment) => !/[{}]/.test(segment) || /^\\{[^{}]+\\}$/.test(segment)),\n 'a {name} placeholder must be one whole path segment'\n );\n// Header names are lower-cased before matching, so a capitalized name could never match.\nconst headersJsonSchemaSchema = jsonSchemaSchema.refine(\n (schema) => declaresOnlyLowerCaseNames(schema),\n 'header names in properties and required must be lower case'\n);\n\n// Mirrors the judge server's `agentChallengeSchema`.\nexport const agentChallengeSchema = z.strictObject({\n playgrounds: z.array(z.enum(['bbs', 'backlog', 'hosting'])).min(1),\n expectedRequest: z.strictObject({\n method: z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']),\n path: pathTemplateSchema,\n status: z.union([httpStatusSchema, z.array(httpStatusSchema).min(1)]).optional(),\n query: jsonSchemaSchema.optional(),\n headers: headersJsonSchemaSchema.optional(),\n body: jsonSchemaSchema.optional(),\n }),\n});\n\n// Mirrors the judge server's strict problem frontmatter schema; keys the server removed with the\n// v1 judge (judgeEnvironmentId, generalJudgeEnvironmentConfigOverrides, testCases, isGui) are\n// rejected here as unknown keys as well.\nexport const problemFrontmatterSchema = z.strictObject({\n name: z.string().min(1),\n type: z.enum(['prompt_study']).optional(),\n timeLimitMs: z.number().int().min(0).optional(),\n memoryLimitByte: z.number().int().min(0).optional(),\n requiredRegExpsInCode: codeRuleSchema.array().default([]),\n forbiddenRegExpsInCode: codeRuleSchema.array().default([]),\n forbiddenTextsInCode: codeRuleSchema.array().default([]),\n canCreateFiles: z.boolean().optional(),\n isEditorDisabled: z.boolean().optional(),\n isAttachedFileRequired: z.boolean().optional(),\n isManualScoringRequired: z.boolean().optional(),\n isVotable: z.boolean().optional(),\n requiredEnvironmentVariables: z.array(environmentVariableNameSchema).default([]),\n requiredOutputFilePaths: z.string().min(1).array().default([]),\n requiredSubmissionFilePaths: z.string().min(1).array().default([]),\n agentChallenge: agentChallengeSchema.optional(),\n});\n\nexport type ProblemFrontmatter = z.infer<typeof problemFrontmatterSchema>;\n\nconst materialConfigShape = {\n availableLanguageIds: availableLanguageIdSchema.array().optional(),\n areTestCasesHidden: z.boolean().optional(),\n isProblemGradingResultHidden: z.boolean().optional(),\n isAutoFormatDisabled: z.boolean().optional(),\n isCopyAndPasteDisabled: z.boolean().optional(),\n isDebugHintDisabled: z.boolean().optional(),\n isFixHintDisabled: z.boolean().optional(),\n isDiffHintDisabled: z.boolean().optional(),\n debugHintWaitingSeconds: z.number().min(0).optional(),\n fixHintWaitingSeconds: z.number().min(0).optional(),\n diffHintWaitingSeconds: z.number().min(0).optional(),\n // z.iso.datetime is the zod v4 API (this repo pins zod 4); z.string().datetime is the v3 form.\n submissionOpenedAt: z.iso.datetime({ offset: true }).optional(),\n submissionSoftClosedAt: z.iso.datetime({ offset: true }).optional(),\n submissionHardClosedAt: z.iso.datetime({ offset: true }).optional(),\n isAutoTranslationDisabled: z.boolean().optional(),\n isModelAnswerShownAfterDeadline: z.boolean().optional(),\n isVotable: z.boolean().optional(),\n isMaterialChatDisabled: z.boolean().optional(),\n} as const;\n\nexport const courseFileSchema = z.strictObject({\n ...materialConfigShape,\n name: z.string().min(1),\n description: z.string(),\n author: z.string().min(1).optional(),\n isMotivationFeatureEnabled: z.boolean().optional(),\n isPublic: z.boolean().optional(),\n // The importer strips unknown keys of a lecture entry (plain object) while the course object is strict.\n lectures: z\n .array(\n z.object({\n id: learningMaterialIdSchema,\n name: z.string().min(1),\n description: z.string(),\n })\n )\n .optional(),\n});\n\nexport type CourseFile = z.infer<typeof courseFileSchema>;\n\nconst uniqueOptionsSchema = z\n .string()\n .array()\n .min(1)\n // zod 4 names the custom-message option `error` (`message` is the deprecated zod 3 spelling).\n .refine((options) => options.length === new Set(options).size, { error: 'options must contain unique values' });\n\n// A quoted index is coerced like the judge does, but a non-numeric string is a schema error rather\n// than a NaN that would surface as an out-of-bounds index.\nconst answerIndexStringSchema = z\n .string()\n .regex(/^\\d+$/, { error: 'must be a non-negative integer index (quoted numbers such as \"0\" are accepted)' })\n .transform(Number);\n\nconst questionBaseShape = {\n id: learningMaterialIdSchema,\n isResubmittable: z.boolean().optional(),\n isSurvey: z.boolean().optional(),\n explanation: z.string().optional(),\n hint: z.string().optional(),\n} as const;\n\nconst selectQuestionShape = {\n ...questionBaseShape,\n type: z.literal('select'),\n options: uniqueOptionsSchema,\n answerIndex: z\n .union([\n z.number().int().nonnegative(),\n answerIndexStringSchema,\n z.number().int().nonnegative().array(),\n answerIndexStringSchema.array(),\n ])\n .optional(),\n} as const;\n\nconst selectMultipleQuestionShape = {\n ...questionBaseShape,\n type: z.literal('select_multiple'),\n options: uniqueOptionsSchema,\n answerIndices: z.union([z.number().int().nonnegative().array(), answerIndexStringSchema.array()]).optional(),\n} as const;\n\nconst textQuestionShape = {\n ...questionBaseShape,\n type: z.literal('text'),\n answerPattern: z.string().min(1).optional(),\n modelAnswer: z.string().optional(),\n} as const;\n\nexport const questionInCodeBlockSchema = z.discriminatedUnion('type', [\n z.strictObject({ ...selectQuestionShape, question: z.string() }),\n z.strictObject({ ...selectMultipleQuestionShape, question: z.string() }),\n z.strictObject({ ...textQuestionShape, question: z.string() }),\n]);\n\n// The judge still supports question definitions in material frontmatter where `question` is optional.\nconst questionInFrontmatterSchema = z.discriminatedUnion('type', [\n z.strictObject({ ...selectQuestionShape, question: z.string().optional() }),\n z.strictObject({ ...selectMultipleQuestionShape, question: z.string().optional() }),\n z.strictObject({ ...textQuestionShape, question: z.string().optional() }),\n]);\n\nexport type MaterialQuestion = z.infer<typeof questionInFrontmatterSchema>;\n\nexport const materialFrontmatterSchema = z.strictObject({\n ...materialConfigShape,\n name: z.string().min(1),\n problems: z.strictObject({ id: learningMaterialIdSchema }).array().optional(),\n questions: z.array(questionInFrontmatterSchema).optional(),\n turtleGraphicsQuestions: z.object({ id: learningMaterialIdSchema }).array().optional(),\n isExamination: z.boolean().optional(),\n isMockExamination: z.boolean().optional(),\n isRealtimeSurvey: z.boolean().optional(),\n});\n\nexport const CONTEST_MATERIAL_FILE_SUFFIX = '.contest.yaml';\n\nconst contestDivisionSchema = z\n .strictObject({\n id: learningMaterialIdSchema,\n name: z.string().min(1),\n openedAt: z.iso.datetime({ offset: true }),\n closedAt: z.iso.datetime({ offset: true }),\n password: z.string().min(1).optional(),\n })\n .refine((division) => new Date(division.openedAt) < new Date(division.closedAt), {\n error: 'openedAt must be earlier than closedAt',\n });\n\nconst contestProblemSchema = z.strictObject({\n id: learningMaterialIdSchema,\n score: z.number().int().nonnegative(),\n // Per-problem overrides reuse the shared material-config fields, mirroring the judge schema.\n areTestCasesHidden: materialConfigShape.areTestCasesHidden,\n isDebugHintDisabled: materialConfigShape.isDebugHintDisabled,\n isFixHintDisabled: materialConfigShape.isFixHintDisabled,\n isDiffHintDisabled: materialConfigShape.isDiffHintDisabled,\n debugHintWaitingSeconds: materialConfigShape.debugHintWaitingSeconds,\n fixHintWaitingSeconds: materialConfigShape.fixHintWaitingSeconds,\n diffHintWaitingSeconds: materialConfigShape.diffHintWaitingSeconds,\n isMaterialChatDisabled: materialConfigShape.isMaterialChatDisabled,\n});\n\nexport const contestFileSchema = z.strictObject({\n ...materialConfigShape,\n name: z.string().min(1),\n description: z.string().optional(),\n // Exercode reads the contest administrators itself and strips the key before the judge's strict schema.\n adminEmails: z.array(z.string().trim().email()).optional(),\n showsProblemsAfterClose: z.boolean().optional(),\n divisions: z.array(contestDivisionSchema).min(1),\n problems: z.array(contestProblemSchema).min(1),\n});\n\n/** Checks every `properties` key and `required` item at any depth, so composed schemas (allOf, $defs, if/then) count too. */\nfunction declaresOnlyLowerCaseNames(schema: unknown, visited = new WeakSet<object>()): boolean {\n // A YAML alias can make the schema cyclic.\n if (typeof schema !== 'object' || schema === null || visited.has(schema)) return true;\n visited.add(schema);\n if (Array.isArray(schema)) return schema.every((item) => declaresOnlyLowerCaseNames(item, visited));\n if (!isRecord(schema)) return true;\n const propertyNames = isRecord(schema.properties) ? Object.keys(schema.properties) : [];\n const requiredNames: unknown[] = Array.isArray(schema.required) ? schema.required : [];\n return (\n [...propertyNames, ...requiredNames].every((name) => typeof name !== 'string' || name === name.toLowerCase()) &&\n Object.values(schema).every((value) => declaresOnlyLowerCaseNames(value, visited))\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"schemas.cjs","names":["z","languageIdToDefinition","codeRuleSchema","isRecord","require_language","require_problem","zod","_willbooster_shared_lib","LEARNING_MATERIAL_ID_REGEX","learningMaterialIdSchema","availableLanguageIds","availableLanguageIdSchema","environmentVariableNameSchema","jsonSchemaSchema","httpStatusSchema","pathTemplateSchema","path","segment","headersJsonSchemaSchema","declaresOnlyLowerCaseNames","schema","agentChallengeSchema","problemFrontmatterSchema","materialConfigShape","courseFileSchema","uniqueOptionsSchema","options","answerIndexStringSchema","questionBaseShape","selectQuestionShape","selectMultipleQuestionShape","textQuestionShape","questionInCodeBlockSchema","questionInFrontmatterSchema","materialFrontmatterSchema","contestDivisionSchema","division","contestProblemSchema","contestFileSchema","visited","item","propertyNames","requiredNames","name","value"],"sources":["../../src/learningMaterial/schemas.ts"],"sourcesContent":["import { isRecord } from '@willbooster/shared-lib';\nimport { z } from 'zod';\n\nimport { languageIdToDefinition } from '../types/language.js';\nimport { codeRuleSchema } from '../types/problem.js';\n\n// Schemas in this file mirror the Exercode judge importer so that generated materials validate\n// exactly like the importer would parse them.\n\nexport const LEARNING_MATERIAL_ID_REGEX = /^[0-9_a-z-]+$/;\n\nexport const learningMaterialIdSchema = z.string().regex(LEARNING_MATERIAL_ID_REGEX);\n\nexport const availableLanguageIds = Object.keys(languageIdToDefinition);\nconst availableLanguageIdSchema = z.enum(availableLanguageIds);\n\nconst environmentVariableNameSchema = z.string().regex(/^\\w+$/);\n\nconst jsonSchemaSchema = z.record(z.string(), z.unknown());\nconst httpStatusSchema = z.number().int().min(100).max(599);\nconst pathTemplateSchema = z\n .string()\n .startsWith('/')\n .refine((path) => !path.includes('?') && !path.includes('#'), 'path must not carry a query or fragment; use query')\n .refine(\n (path) => path.split('/').every((segment) => !/[{}]/.test(segment) || /^\\{[^{}]+\\}$/.test(segment)),\n 'a {name} placeholder must be one whole path segment'\n );\n// Header names are lower-cased before matching, so a capitalized name could never match.\nconst headersJsonSchemaSchema = jsonSchemaSchema.refine(\n (schema) => declaresOnlyLowerCaseNames(schema),\n 'header names in properties and required must be lower case'\n);\n\n// Mirrors the judge server's `agentChallengeSchema`.\nexport const agentChallengeSchema = z.strictObject({\n playgrounds: z.array(z.enum(['bbs', 'backlog', 'hosting'])).min(1),\n expectedRequest: z.strictObject({\n method: z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']),\n path: pathTemplateSchema,\n status: z.union([httpStatusSchema, z.array(httpStatusSchema).min(1)]).optional(),\n query: jsonSchemaSchema.optional(),\n headers: headersJsonSchemaSchema.optional(),\n body: jsonSchemaSchema.optional(),\n }),\n});\n\n// Mirrors the judge server's strict problem frontmatter schema; keys the server removed with the\n// v1 judge (judgeEnvironmentId, generalJudgeEnvironmentConfigOverrides, testCases, isGui) are\n// rejected here as unknown keys as well.\nexport const problemFrontmatterSchema = z.strictObject({\n name: z.string().min(1),\n type: z.enum(['prompt_study']).optional(),\n timeLimitMs: z.number().int().min(0).optional(),\n memoryLimitByte: z.number().int().min(0).optional(),\n requiredRegExpsInCode: codeRuleSchema.array().default([]),\n forbiddenRegExpsInCode: codeRuleSchema.array().default([]),\n forbiddenTextsInCode: codeRuleSchema.array().default([]),\n canCreateFiles: z.boolean().optional(),\n isEditorDisabled: z.boolean().optional(),\n isAttachedFileRequired: z.boolean().optional(),\n isManualScoringRequired: z.boolean().optional(),\n isVotable: z.boolean().optional(),\n requiredEnvironmentVariables: z.array(environmentVariableNameSchema).default([]),\n requiredOutputFilePaths: z.string().min(1).array().default([]),\n requiredSubmissionFilePaths: z.string().min(1).array().default([]),\n agentChallenge: agentChallengeSchema.optional(),\n});\n\nexport type ProblemFrontmatter = z.infer<typeof problemFrontmatterSchema>;\n\nconst materialConfigShape = {\n availableLanguageIds: availableLanguageIdSchema.array().optional(),\n areTestCasesHidden: z.boolean().optional(),\n isProblemGradingResultHidden: z.boolean().optional(),\n isAutoFormatDisabled: z.boolean().optional(),\n isCopyAndPasteDisabled: z.boolean().optional(),\n isDebugHintDisabled: z.boolean().optional(),\n isFixHintDisabled: z.boolean().optional(),\n isDiffHintDisabled: z.boolean().optional(),\n debugHintWaitingSeconds: z.number().min(0).optional(),\n fixHintWaitingSeconds: z.number().min(0).optional(),\n diffHintWaitingSeconds: z.number().min(0).optional(),\n // z.iso.datetime is the zod v4 API (this repo pins zod 4); z.string().datetime is the v3 form.\n submissionOpenedAt: z.iso.datetime({ offset: true }).optional(),\n submissionSoftClosedAt: z.iso.datetime({ offset: true }).optional(),\n submissionHardClosedAt: z.iso.datetime({ offset: true }).optional(),\n isAutoTranslationDisabled: z.boolean().optional(),\n isModelAnswerShownAfterDeadline: z.boolean().optional(),\n isVotable: z.boolean().optional(),\n isMaterialChatDisabled: z.boolean().optional(),\n} as const;\n\nexport const courseFileSchema = z.strictObject({\n ...materialConfigShape,\n name: z.string().min(1),\n description: z.string(),\n author: z.string().min(1).optional(),\n isMotivationFeatureEnabled: z.boolean().optional(),\n isPublic: z.boolean().optional(),\n startsAt: z.iso.datetime({ offset: true }).optional(),\n // The importer strips unknown keys of a lecture entry (plain object) while the course object is strict.\n lectures: z\n .array(\n z.object({\n id: learningMaterialIdSchema,\n name: z.string().min(1),\n description: z.string(),\n })\n )\n .optional(),\n});\n\nexport type CourseFile = z.infer<typeof courseFileSchema>;\n\nconst uniqueOptionsSchema = z\n .string()\n .array()\n .min(1)\n // zod 4 names the custom-message option `error` (`message` is the deprecated zod 3 spelling).\n .refine((options) => options.length === new Set(options).size, { error: 'options must contain unique values' });\n\n// A quoted index is coerced like the judge does, but a non-numeric string is a schema error rather\n// than a NaN that would surface as an out-of-bounds index.\nconst answerIndexStringSchema = z\n .string()\n .regex(/^\\d+$/, { error: 'must be a non-negative integer index (quoted numbers such as \"0\" are accepted)' })\n .transform(Number);\n\nconst questionBaseShape = {\n id: learningMaterialIdSchema,\n isResubmittable: z.boolean().optional(),\n isSurvey: z.boolean().optional(),\n explanation: z.string().optional(),\n hint: z.string().optional(),\n} as const;\n\nconst selectQuestionShape = {\n ...questionBaseShape,\n type: z.literal('select'),\n options: uniqueOptionsSchema,\n answerIndex: z\n .union([\n z.number().int().nonnegative(),\n answerIndexStringSchema,\n z.number().int().nonnegative().array(),\n answerIndexStringSchema.array(),\n ])\n .optional(),\n} as const;\n\nconst selectMultipleQuestionShape = {\n ...questionBaseShape,\n type: z.literal('select_multiple'),\n options: uniqueOptionsSchema,\n answerIndices: z.union([z.number().int().nonnegative().array(), answerIndexStringSchema.array()]).optional(),\n} as const;\n\nconst textQuestionShape = {\n ...questionBaseShape,\n type: z.literal('text'),\n answerPattern: z.string().min(1).optional(),\n modelAnswer: z.string().optional(),\n} as const;\n\nexport const questionInCodeBlockSchema = z.discriminatedUnion('type', [\n z.strictObject({ ...selectQuestionShape, question: z.string() }),\n z.strictObject({ ...selectMultipleQuestionShape, question: z.string() }),\n z.strictObject({ ...textQuestionShape, question: z.string() }),\n]);\n\n// The judge still supports question definitions in material frontmatter where `question` is optional.\nconst questionInFrontmatterSchema = z.discriminatedUnion('type', [\n z.strictObject({ ...selectQuestionShape, question: z.string().optional() }),\n z.strictObject({ ...selectMultipleQuestionShape, question: z.string().optional() }),\n z.strictObject({ ...textQuestionShape, question: z.string().optional() }),\n]);\n\nexport type MaterialQuestion = z.infer<typeof questionInFrontmatterSchema>;\n\nexport const materialFrontmatterSchema = z.strictObject({\n ...materialConfigShape,\n name: z.string().min(1),\n problems: z.strictObject({ id: learningMaterialIdSchema }).array().optional(),\n questions: z.array(questionInFrontmatterSchema).optional(),\n turtleGraphicsQuestions: z.object({ id: learningMaterialIdSchema }).array().optional(),\n isExamination: z.boolean().optional(),\n isMockExamination: z.boolean().optional(),\n isRealtimeSurvey: z.boolean().optional(),\n});\n\nexport const CONTEST_MATERIAL_FILE_SUFFIX = '.contest.yaml';\n\nconst contestDivisionSchema = z\n .strictObject({\n id: learningMaterialIdSchema,\n name: z.string().min(1),\n openedAt: z.iso.datetime({ offset: true }),\n closedAt: z.iso.datetime({ offset: true }),\n password: z.string().min(1).optional(),\n })\n .refine((division) => new Date(division.openedAt) < new Date(division.closedAt), {\n error: 'openedAt must be earlier than closedAt',\n });\n\nconst contestProblemSchema = z.strictObject({\n id: learningMaterialIdSchema,\n score: z.number().int().nonnegative(),\n // Per-problem overrides reuse the shared material-config fields, mirroring the judge schema.\n areTestCasesHidden: materialConfigShape.areTestCasesHidden,\n isDebugHintDisabled: materialConfigShape.isDebugHintDisabled,\n isFixHintDisabled: materialConfigShape.isFixHintDisabled,\n isDiffHintDisabled: materialConfigShape.isDiffHintDisabled,\n debugHintWaitingSeconds: materialConfigShape.debugHintWaitingSeconds,\n fixHintWaitingSeconds: materialConfigShape.fixHintWaitingSeconds,\n diffHintWaitingSeconds: materialConfigShape.diffHintWaitingSeconds,\n isMaterialChatDisabled: materialConfigShape.isMaterialChatDisabled,\n});\n\nexport const contestFileSchema = z.strictObject({\n ...materialConfigShape,\n name: z.string().min(1),\n description: z.string().optional(),\n // Exercode reads the contest administrators itself and strips the key before the judge's strict schema.\n adminEmails: z.array(z.string().trim().email()).optional(),\n showsProblemsAfterClose: z.boolean().optional(),\n divisions: z.array(contestDivisionSchema).min(1),\n problems: z.array(contestProblemSchema).min(1),\n});\n\n/** Checks every `properties` key and `required` item at any depth, so composed schemas (allOf, $defs, if/then) count too. */\nfunction declaresOnlyLowerCaseNames(schema: unknown, visited = new WeakSet<object>()): boolean {\n // A YAML alias can make the schema cyclic.\n if (typeof schema !== 'object' || schema === null || visited.has(schema)) return true;\n visited.add(schema);\n if (Array.isArray(schema)) return schema.every((item) => declaresOnlyLowerCaseNames(item, visited));\n if (!isRecord(schema)) return true;\n const propertyNames = isRecord(schema.properties) ? Object.keys(schema.properties) : [];\n const requiredNames: unknown[] = Array.isArray(schema.required) ? schema.required : [];\n return (\n [...propertyNames, ...requiredNames].every((name) => typeof name !== 'string' || name === name.toLowerCase()) &&\n Object.values(schema).every((value) => declaresOnlyLowerCaseNames(value, visited))\n );\n}\n"],"mappings":"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"}
@@ -99,6 +99,7 @@ export declare const courseFileSchema: z.ZodObject<{
99
99
  author: z.ZodOptional<z.ZodString>;
100
100
  isMotivationFeatureEnabled: z.ZodOptional<z.ZodBoolean>;
101
101
  isPublic: z.ZodOptional<z.ZodBoolean>;
102
+ startsAt: z.ZodOptional<z.ZodISODateTime>;
102
103
  lectures: z.ZodOptional<z.ZodArray<z.ZodObject<{
103
104
  id: z.ZodString;
104
105
  name: z.ZodString;
@@ -1,2 +1,2 @@
1
- import{languageIdToDefinition as e}from"../types/language.js";import{codeRuleSchema as t}from"../types/problem.js";import{z as n}from"zod";import{isRecord as r}from"@willbooster/shared-lib";const i=/^[0-9_a-z-]+$/,a=n.string().regex(i),o=Object.keys(e),s=n.enum(o),c=n.string().regex(/^\w+$/),l=n.record(n.string(),n.unknown()),u=n.number().int().min(100).max(599),d=n.string().startsWith(`/`).refine(e=>!e.includes(`?`)&&!e.includes(`#`),`path must not carry a query or fragment; use query`).refine(e=>e.split(`/`).every(e=>!/[{}]/.test(e)||/^\{[^{}]+\}$/.test(e)),`a {name} placeholder must be one whole path segment`),f=l.refine(e=>A(e),`header names in properties and required must be lower case`),p=n.strictObject({playgrounds:n.array(n.enum([`bbs`,`backlog`,`hosting`])).min(1),expectedRequest:n.strictObject({method:n.enum([`GET`,`POST`,`PUT`,`PATCH`,`DELETE`]),path:d,status:n.union([u,n.array(u).min(1)]).optional(),query:l.optional(),headers:f.optional(),body:l.optional()})}),m=n.strictObject({name:n.string().min(1),type:n.enum([`prompt_study`]).optional(),timeLimitMs:n.number().int().min(0).optional(),memoryLimitByte:n.number().int().min(0).optional(),requiredRegExpsInCode:t.array().default([]),forbiddenRegExpsInCode:t.array().default([]),forbiddenTextsInCode:t.array().default([]),canCreateFiles:n.boolean().optional(),isEditorDisabled:n.boolean().optional(),isAttachedFileRequired:n.boolean().optional(),isManualScoringRequired:n.boolean().optional(),isVotable:n.boolean().optional(),requiredEnvironmentVariables:n.array(c).default([]),requiredOutputFilePaths:n.string().min(1).array().default([]),requiredSubmissionFilePaths:n.string().min(1).array().default([]),agentChallenge:p.optional()}),h={availableLanguageIds:s.array().optional(),areTestCasesHidden:n.boolean().optional(),isProblemGradingResultHidden:n.boolean().optional(),isAutoFormatDisabled:n.boolean().optional(),isCopyAndPasteDisabled:n.boolean().optional(),isDebugHintDisabled:n.boolean().optional(),isFixHintDisabled:n.boolean().optional(),isDiffHintDisabled:n.boolean().optional(),debugHintWaitingSeconds:n.number().min(0).optional(),fixHintWaitingSeconds:n.number().min(0).optional(),diffHintWaitingSeconds:n.number().min(0).optional(),submissionOpenedAt:n.iso.datetime({offset:!0}).optional(),submissionSoftClosedAt:n.iso.datetime({offset:!0}).optional(),submissionHardClosedAt:n.iso.datetime({offset:!0}).optional(),isAutoTranslationDisabled:n.boolean().optional(),isModelAnswerShownAfterDeadline:n.boolean().optional(),isVotable:n.boolean().optional(),isMaterialChatDisabled:n.boolean().optional()},g=n.strictObject({...h,name:n.string().min(1),description:n.string(),author:n.string().min(1).optional(),isMotivationFeatureEnabled:n.boolean().optional(),isPublic:n.boolean().optional(),lectures:n.array(n.object({id:a,name:n.string().min(1),description:n.string()})).optional()}),_=n.string().array().min(1).refine(e=>e.length===new Set(e).size,{error:`options must contain unique values`}),v=n.string().regex(/^\d+$/,{error:`must be a non-negative integer index (quoted numbers such as "0" are accepted)`}).transform(Number),y={id:a,isResubmittable:n.boolean().optional(),isSurvey:n.boolean().optional(),explanation:n.string().optional(),hint:n.string().optional()},b={...y,type:n.literal(`select`),options:_,answerIndex:n.union([n.number().int().nonnegative(),v,n.number().int().nonnegative().array(),v.array()]).optional()},x={...y,type:n.literal(`select_multiple`),options:_,answerIndices:n.union([n.number().int().nonnegative().array(),v.array()]).optional()},S={...y,type:n.literal(`text`),answerPattern:n.string().min(1).optional(),modelAnswer:n.string().optional()},C=n.discriminatedUnion(`type`,[n.strictObject({...b,question:n.string()}),n.strictObject({...x,question:n.string()}),n.strictObject({...S,question:n.string()})]),w=n.discriminatedUnion(`type`,[n.strictObject({...b,question:n.string().optional()}),n.strictObject({...x,question:n.string().optional()}),n.strictObject({...S,question:n.string().optional()})]),T=n.strictObject({...h,name:n.string().min(1),problems:n.strictObject({id:a}).array().optional(),questions:n.array(w).optional(),turtleGraphicsQuestions:n.object({id:a}).array().optional(),isExamination:n.boolean().optional(),isMockExamination:n.boolean().optional(),isRealtimeSurvey:n.boolean().optional()}),E=`.contest.yaml`,D=n.strictObject({id:a,name:n.string().min(1),openedAt:n.iso.datetime({offset:!0}),closedAt:n.iso.datetime({offset:!0}),password:n.string().min(1).optional()}).refine(e=>new Date(e.openedAt)<new Date(e.closedAt),{error:`openedAt must be earlier than closedAt`}),O=n.strictObject({id:a,score:n.number().int().nonnegative(),areTestCasesHidden:h.areTestCasesHidden,isDebugHintDisabled:h.isDebugHintDisabled,isFixHintDisabled:h.isFixHintDisabled,isDiffHintDisabled:h.isDiffHintDisabled,debugHintWaitingSeconds:h.debugHintWaitingSeconds,fixHintWaitingSeconds:h.fixHintWaitingSeconds,diffHintWaitingSeconds:h.diffHintWaitingSeconds,isMaterialChatDisabled:h.isMaterialChatDisabled}),k=n.strictObject({...h,name:n.string().min(1),description:n.string().optional(),adminEmails:n.array(n.string().trim().email()).optional(),showsProblemsAfterClose:n.boolean().optional(),divisions:n.array(D).min(1),problems:n.array(O).min(1)});function A(e,t=/* @__PURE__ */ new WeakSet){if(typeof e!=`object`||!e||t.has(e))return!0;if(t.add(e),Array.isArray(e))return e.every(e=>A(e,t));if(!r(e))return!0;let n=r(e.properties)?Object.keys(e.properties):[],i=Array.isArray(e.required)?e.required:[];return[...n,...i].every(e=>typeof e!=`string`||e===e.toLowerCase())&&Object.values(e).every(e=>A(e,t))}export{E as CONTEST_MATERIAL_FILE_SUFFIX,i as LEARNING_MATERIAL_ID_REGEX,p as agentChallengeSchema,o as availableLanguageIds,k as contestFileSchema,g as courseFileSchema,a as learningMaterialIdSchema,T as materialFrontmatterSchema,m as problemFrontmatterSchema,C as questionInCodeBlockSchema};
1
+ import{languageIdToDefinition as e}from"../types/language.js";import{codeRuleSchema as t}from"../types/problem.js";import{z as n}from"zod";import{isRecord as r}from"@willbooster/shared-lib";const i=/^[0-9_a-z-]+$/,a=n.string().regex(i),o=Object.keys(e),s=n.enum(o),c=n.string().regex(/^\w+$/),l=n.record(n.string(),n.unknown()),u=n.number().int().min(100).max(599),d=n.string().startsWith(`/`).refine(e=>!e.includes(`?`)&&!e.includes(`#`),`path must not carry a query or fragment; use query`).refine(e=>e.split(`/`).every(e=>!/[{}]/.test(e)||/^\{[^{}]+\}$/.test(e)),`a {name} placeholder must be one whole path segment`),f=l.refine(e=>A(e),`header names in properties and required must be lower case`),p=n.strictObject({playgrounds:n.array(n.enum([`bbs`,`backlog`,`hosting`])).min(1),expectedRequest:n.strictObject({method:n.enum([`GET`,`POST`,`PUT`,`PATCH`,`DELETE`]),path:d,status:n.union([u,n.array(u).min(1)]).optional(),query:l.optional(),headers:f.optional(),body:l.optional()})}),m=n.strictObject({name:n.string().min(1),type:n.enum([`prompt_study`]).optional(),timeLimitMs:n.number().int().min(0).optional(),memoryLimitByte:n.number().int().min(0).optional(),requiredRegExpsInCode:t.array().default([]),forbiddenRegExpsInCode:t.array().default([]),forbiddenTextsInCode:t.array().default([]),canCreateFiles:n.boolean().optional(),isEditorDisabled:n.boolean().optional(),isAttachedFileRequired:n.boolean().optional(),isManualScoringRequired:n.boolean().optional(),isVotable:n.boolean().optional(),requiredEnvironmentVariables:n.array(c).default([]),requiredOutputFilePaths:n.string().min(1).array().default([]),requiredSubmissionFilePaths:n.string().min(1).array().default([]),agentChallenge:p.optional()}),h={availableLanguageIds:s.array().optional(),areTestCasesHidden:n.boolean().optional(),isProblemGradingResultHidden:n.boolean().optional(),isAutoFormatDisabled:n.boolean().optional(),isCopyAndPasteDisabled:n.boolean().optional(),isDebugHintDisabled:n.boolean().optional(),isFixHintDisabled:n.boolean().optional(),isDiffHintDisabled:n.boolean().optional(),debugHintWaitingSeconds:n.number().min(0).optional(),fixHintWaitingSeconds:n.number().min(0).optional(),diffHintWaitingSeconds:n.number().min(0).optional(),submissionOpenedAt:n.iso.datetime({offset:!0}).optional(),submissionSoftClosedAt:n.iso.datetime({offset:!0}).optional(),submissionHardClosedAt:n.iso.datetime({offset:!0}).optional(),isAutoTranslationDisabled:n.boolean().optional(),isModelAnswerShownAfterDeadline:n.boolean().optional(),isVotable:n.boolean().optional(),isMaterialChatDisabled:n.boolean().optional()},g=n.strictObject({...h,name:n.string().min(1),description:n.string(),author:n.string().min(1).optional(),isMotivationFeatureEnabled:n.boolean().optional(),isPublic:n.boolean().optional(),startsAt:n.iso.datetime({offset:!0}).optional(),lectures:n.array(n.object({id:a,name:n.string().min(1),description:n.string()})).optional()}),_=n.string().array().min(1).refine(e=>e.length===new Set(e).size,{error:`options must contain unique values`}),v=n.string().regex(/^\d+$/,{error:`must be a non-negative integer index (quoted numbers such as "0" are accepted)`}).transform(Number),y={id:a,isResubmittable:n.boolean().optional(),isSurvey:n.boolean().optional(),explanation:n.string().optional(),hint:n.string().optional()},b={...y,type:n.literal(`select`),options:_,answerIndex:n.union([n.number().int().nonnegative(),v,n.number().int().nonnegative().array(),v.array()]).optional()},x={...y,type:n.literal(`select_multiple`),options:_,answerIndices:n.union([n.number().int().nonnegative().array(),v.array()]).optional()},S={...y,type:n.literal(`text`),answerPattern:n.string().min(1).optional(),modelAnswer:n.string().optional()},C=n.discriminatedUnion(`type`,[n.strictObject({...b,question:n.string()}),n.strictObject({...x,question:n.string()}),n.strictObject({...S,question:n.string()})]),w=n.discriminatedUnion(`type`,[n.strictObject({...b,question:n.string().optional()}),n.strictObject({...x,question:n.string().optional()}),n.strictObject({...S,question:n.string().optional()})]),T=n.strictObject({...h,name:n.string().min(1),problems:n.strictObject({id:a}).array().optional(),questions:n.array(w).optional(),turtleGraphicsQuestions:n.object({id:a}).array().optional(),isExamination:n.boolean().optional(),isMockExamination:n.boolean().optional(),isRealtimeSurvey:n.boolean().optional()}),E=`.contest.yaml`,D=n.strictObject({id:a,name:n.string().min(1),openedAt:n.iso.datetime({offset:!0}),closedAt:n.iso.datetime({offset:!0}),password:n.string().min(1).optional()}).refine(e=>new Date(e.openedAt)<new Date(e.closedAt),{error:`openedAt must be earlier than closedAt`}),O=n.strictObject({id:a,score:n.number().int().nonnegative(),areTestCasesHidden:h.areTestCasesHidden,isDebugHintDisabled:h.isDebugHintDisabled,isFixHintDisabled:h.isFixHintDisabled,isDiffHintDisabled:h.isDiffHintDisabled,debugHintWaitingSeconds:h.debugHintWaitingSeconds,fixHintWaitingSeconds:h.fixHintWaitingSeconds,diffHintWaitingSeconds:h.diffHintWaitingSeconds,isMaterialChatDisabled:h.isMaterialChatDisabled}),k=n.strictObject({...h,name:n.string().min(1),description:n.string().optional(),adminEmails:n.array(n.string().trim().email()).optional(),showsProblemsAfterClose:n.boolean().optional(),divisions:n.array(D).min(1),problems:n.array(O).min(1)});function A(e,t=/* @__PURE__ */ new WeakSet){if(typeof e!=`object`||!e||t.has(e))return!0;if(t.add(e),Array.isArray(e))return e.every(e=>A(e,t));if(!r(e))return!0;let n=r(e.properties)?Object.keys(e.properties):[],i=Array.isArray(e.required)?e.required:[];return[...n,...i].every(e=>typeof e!=`string`||e===e.toLowerCase())&&Object.values(e).every(e=>A(e,t))}export{E as CONTEST_MATERIAL_FILE_SUFFIX,i as LEARNING_MATERIAL_ID_REGEX,p as agentChallengeSchema,o as availableLanguageIds,k as contestFileSchema,g as courseFileSchema,a as learningMaterialIdSchema,T as materialFrontmatterSchema,m as problemFrontmatterSchema,C as questionInCodeBlockSchema};
2
2
  //# sourceMappingURL=schemas.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"schemas.js","names":["LEARNING_MATERIAL_ID_REGEX","learningMaterialIdSchema","z","availableLanguageIds","languageIdToDefinition","availableLanguageIdSchema","environmentVariableNameSchema","jsonSchemaSchema","httpStatusSchema","pathTemplateSchema","path","segment","headersJsonSchemaSchema","declaresOnlyLowerCaseNames","schema","agentChallengeSchema","problemFrontmatterSchema","codeRuleSchema","materialConfigShape","courseFileSchema","uniqueOptionsSchema","options","answerIndexStringSchema","questionBaseShape","selectQuestionShape","selectMultipleQuestionShape","textQuestionShape","questionInCodeBlockSchema","questionInFrontmatterSchema","materialFrontmatterSchema","CONTEST_MATERIAL_FILE_SUFFIX","contestDivisionSchema","division","contestProblemSchema","contestFileSchema","visited","item","isRecord","propertyNames","requiredNames","name","value"],"sources":["../../src/learningMaterial/schemas.ts"],"sourcesContent":["import { isRecord } from '@willbooster/shared-lib';\nimport { z } from 'zod';\n\nimport { languageIdToDefinition } from '../types/language.js';\nimport { codeRuleSchema } from '../types/problem.js';\n\n// Schemas in this file mirror the Exercode judge importer so that generated materials validate\n// exactly like the importer would parse them.\n\nexport const LEARNING_MATERIAL_ID_REGEX = /^[0-9_a-z-]+$/;\n\nexport const learningMaterialIdSchema = z.string().regex(LEARNING_MATERIAL_ID_REGEX);\n\nexport const availableLanguageIds = Object.keys(languageIdToDefinition);\nconst availableLanguageIdSchema = z.enum(availableLanguageIds);\n\nconst environmentVariableNameSchema = z.string().regex(/^\\w+$/);\n\nconst jsonSchemaSchema = z.record(z.string(), z.unknown());\nconst httpStatusSchema = z.number().int().min(100).max(599);\nconst pathTemplateSchema = z\n .string()\n .startsWith('/')\n .refine((path) => !path.includes('?') && !path.includes('#'), 'path must not carry a query or fragment; use query')\n .refine(\n (path) => path.split('/').every((segment) => !/[{}]/.test(segment) || /^\\{[^{}]+\\}$/.test(segment)),\n 'a {name} placeholder must be one whole path segment'\n );\n// Header names are lower-cased before matching, so a capitalized name could never match.\nconst headersJsonSchemaSchema = jsonSchemaSchema.refine(\n (schema) => declaresOnlyLowerCaseNames(schema),\n 'header names in properties and required must be lower case'\n);\n\n// Mirrors the judge server's `agentChallengeSchema`.\nexport const agentChallengeSchema = z.strictObject({\n playgrounds: z.array(z.enum(['bbs', 'backlog', 'hosting'])).min(1),\n expectedRequest: z.strictObject({\n method: z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']),\n path: pathTemplateSchema,\n status: z.union([httpStatusSchema, z.array(httpStatusSchema).min(1)]).optional(),\n query: jsonSchemaSchema.optional(),\n headers: headersJsonSchemaSchema.optional(),\n body: jsonSchemaSchema.optional(),\n }),\n});\n\n// Mirrors the judge server's strict problem frontmatter schema; keys the server removed with the\n// v1 judge (judgeEnvironmentId, generalJudgeEnvironmentConfigOverrides, testCases, isGui) are\n// rejected here as unknown keys as well.\nexport const problemFrontmatterSchema = z.strictObject({\n name: z.string().min(1),\n type: z.enum(['prompt_study']).optional(),\n timeLimitMs: z.number().int().min(0).optional(),\n memoryLimitByte: z.number().int().min(0).optional(),\n requiredRegExpsInCode: codeRuleSchema.array().default([]),\n forbiddenRegExpsInCode: codeRuleSchema.array().default([]),\n forbiddenTextsInCode: codeRuleSchema.array().default([]),\n canCreateFiles: z.boolean().optional(),\n isEditorDisabled: z.boolean().optional(),\n isAttachedFileRequired: z.boolean().optional(),\n isManualScoringRequired: z.boolean().optional(),\n isVotable: z.boolean().optional(),\n requiredEnvironmentVariables: z.array(environmentVariableNameSchema).default([]),\n requiredOutputFilePaths: z.string().min(1).array().default([]),\n requiredSubmissionFilePaths: z.string().min(1).array().default([]),\n agentChallenge: agentChallengeSchema.optional(),\n});\n\nexport type ProblemFrontmatter = z.infer<typeof problemFrontmatterSchema>;\n\nconst materialConfigShape = {\n availableLanguageIds: availableLanguageIdSchema.array().optional(),\n areTestCasesHidden: z.boolean().optional(),\n isProblemGradingResultHidden: z.boolean().optional(),\n isAutoFormatDisabled: z.boolean().optional(),\n isCopyAndPasteDisabled: z.boolean().optional(),\n isDebugHintDisabled: z.boolean().optional(),\n isFixHintDisabled: z.boolean().optional(),\n isDiffHintDisabled: z.boolean().optional(),\n debugHintWaitingSeconds: z.number().min(0).optional(),\n fixHintWaitingSeconds: z.number().min(0).optional(),\n diffHintWaitingSeconds: z.number().min(0).optional(),\n // z.iso.datetime is the zod v4 API (this repo pins zod 4); z.string().datetime is the v3 form.\n submissionOpenedAt: z.iso.datetime({ offset: true }).optional(),\n submissionSoftClosedAt: z.iso.datetime({ offset: true }).optional(),\n submissionHardClosedAt: z.iso.datetime({ offset: true }).optional(),\n isAutoTranslationDisabled: z.boolean().optional(),\n isModelAnswerShownAfterDeadline: z.boolean().optional(),\n isVotable: z.boolean().optional(),\n isMaterialChatDisabled: z.boolean().optional(),\n} as const;\n\nexport const courseFileSchema = z.strictObject({\n ...materialConfigShape,\n name: z.string().min(1),\n description: z.string(),\n author: z.string().min(1).optional(),\n isMotivationFeatureEnabled: z.boolean().optional(),\n isPublic: z.boolean().optional(),\n // The importer strips unknown keys of a lecture entry (plain object) while the course object is strict.\n lectures: z\n .array(\n z.object({\n id: learningMaterialIdSchema,\n name: z.string().min(1),\n description: z.string(),\n })\n )\n .optional(),\n});\n\nexport type CourseFile = z.infer<typeof courseFileSchema>;\n\nconst uniqueOptionsSchema = z\n .string()\n .array()\n .min(1)\n // zod 4 names the custom-message option `error` (`message` is the deprecated zod 3 spelling).\n .refine((options) => options.length === new Set(options).size, { error: 'options must contain unique values' });\n\n// A quoted index is coerced like the judge does, but a non-numeric string is a schema error rather\n// than a NaN that would surface as an out-of-bounds index.\nconst answerIndexStringSchema = z\n .string()\n .regex(/^\\d+$/, { error: 'must be a non-negative integer index (quoted numbers such as \"0\" are accepted)' })\n .transform(Number);\n\nconst questionBaseShape = {\n id: learningMaterialIdSchema,\n isResubmittable: z.boolean().optional(),\n isSurvey: z.boolean().optional(),\n explanation: z.string().optional(),\n hint: z.string().optional(),\n} as const;\n\nconst selectQuestionShape = {\n ...questionBaseShape,\n type: z.literal('select'),\n options: uniqueOptionsSchema,\n answerIndex: z\n .union([\n z.number().int().nonnegative(),\n answerIndexStringSchema,\n z.number().int().nonnegative().array(),\n answerIndexStringSchema.array(),\n ])\n .optional(),\n} as const;\n\nconst selectMultipleQuestionShape = {\n ...questionBaseShape,\n type: z.literal('select_multiple'),\n options: uniqueOptionsSchema,\n answerIndices: z.union([z.number().int().nonnegative().array(), answerIndexStringSchema.array()]).optional(),\n} as const;\n\nconst textQuestionShape = {\n ...questionBaseShape,\n type: z.literal('text'),\n answerPattern: z.string().min(1).optional(),\n modelAnswer: z.string().optional(),\n} as const;\n\nexport const questionInCodeBlockSchema = z.discriminatedUnion('type', [\n z.strictObject({ ...selectQuestionShape, question: z.string() }),\n z.strictObject({ ...selectMultipleQuestionShape, question: z.string() }),\n z.strictObject({ ...textQuestionShape, question: z.string() }),\n]);\n\n// The judge still supports question definitions in material frontmatter where `question` is optional.\nconst questionInFrontmatterSchema = z.discriminatedUnion('type', [\n z.strictObject({ ...selectQuestionShape, question: z.string().optional() }),\n z.strictObject({ ...selectMultipleQuestionShape, question: z.string().optional() }),\n z.strictObject({ ...textQuestionShape, question: z.string().optional() }),\n]);\n\nexport type MaterialQuestion = z.infer<typeof questionInFrontmatterSchema>;\n\nexport const materialFrontmatterSchema = z.strictObject({\n ...materialConfigShape,\n name: z.string().min(1),\n problems: z.strictObject({ id: learningMaterialIdSchema }).array().optional(),\n questions: z.array(questionInFrontmatterSchema).optional(),\n turtleGraphicsQuestions: z.object({ id: learningMaterialIdSchema }).array().optional(),\n isExamination: z.boolean().optional(),\n isMockExamination: z.boolean().optional(),\n isRealtimeSurvey: z.boolean().optional(),\n});\n\nexport const CONTEST_MATERIAL_FILE_SUFFIX = '.contest.yaml';\n\nconst contestDivisionSchema = z\n .strictObject({\n id: learningMaterialIdSchema,\n name: z.string().min(1),\n openedAt: z.iso.datetime({ offset: true }),\n closedAt: z.iso.datetime({ offset: true }),\n password: z.string().min(1).optional(),\n })\n .refine((division) => new Date(division.openedAt) < new Date(division.closedAt), {\n error: 'openedAt must be earlier than closedAt',\n });\n\nconst contestProblemSchema = z.strictObject({\n id: learningMaterialIdSchema,\n score: z.number().int().nonnegative(),\n // Per-problem overrides reuse the shared material-config fields, mirroring the judge schema.\n areTestCasesHidden: materialConfigShape.areTestCasesHidden,\n isDebugHintDisabled: materialConfigShape.isDebugHintDisabled,\n isFixHintDisabled: materialConfigShape.isFixHintDisabled,\n isDiffHintDisabled: materialConfigShape.isDiffHintDisabled,\n debugHintWaitingSeconds: materialConfigShape.debugHintWaitingSeconds,\n fixHintWaitingSeconds: materialConfigShape.fixHintWaitingSeconds,\n diffHintWaitingSeconds: materialConfigShape.diffHintWaitingSeconds,\n isMaterialChatDisabled: materialConfigShape.isMaterialChatDisabled,\n});\n\nexport const contestFileSchema = z.strictObject({\n ...materialConfigShape,\n name: z.string().min(1),\n description: z.string().optional(),\n // Exercode reads the contest administrators itself and strips the key before the judge's strict schema.\n adminEmails: z.array(z.string().trim().email()).optional(),\n showsProblemsAfterClose: z.boolean().optional(),\n divisions: z.array(contestDivisionSchema).min(1),\n problems: z.array(contestProblemSchema).min(1),\n});\n\n/** Checks every `properties` key and `required` item at any depth, so composed schemas (allOf, $defs, if/then) count too. */\nfunction declaresOnlyLowerCaseNames(schema: unknown, visited = new WeakSet<object>()): boolean {\n // A YAML alias can make the schema cyclic.\n if (typeof schema !== 'object' || schema === null || visited.has(schema)) return true;\n visited.add(schema);\n if (Array.isArray(schema)) return schema.every((item) => declaresOnlyLowerCaseNames(item, visited));\n if (!isRecord(schema)) return true;\n const propertyNames = isRecord(schema.properties) ? Object.keys(schema.properties) : [];\n const requiredNames: unknown[] = Array.isArray(schema.required) ? schema.required : [];\n return (\n [...propertyNames, ...requiredNames].every((name) => typeof name !== 'string' || name === name.toLowerCase()) &&\n Object.values(schema).every((value) => declaresOnlyLowerCaseNames(value, visited))\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"schemas.js","names":["LEARNING_MATERIAL_ID_REGEX","learningMaterialIdSchema","z","availableLanguageIds","languageIdToDefinition","availableLanguageIdSchema","environmentVariableNameSchema","jsonSchemaSchema","httpStatusSchema","pathTemplateSchema","path","segment","headersJsonSchemaSchema","declaresOnlyLowerCaseNames","schema","agentChallengeSchema","problemFrontmatterSchema","codeRuleSchema","materialConfigShape","courseFileSchema","uniqueOptionsSchema","options","answerIndexStringSchema","questionBaseShape","selectQuestionShape","selectMultipleQuestionShape","textQuestionShape","questionInCodeBlockSchema","questionInFrontmatterSchema","materialFrontmatterSchema","CONTEST_MATERIAL_FILE_SUFFIX","contestDivisionSchema","division","contestProblemSchema","contestFileSchema","visited","item","isRecord","propertyNames","requiredNames","name","value"],"sources":["../../src/learningMaterial/schemas.ts"],"sourcesContent":["import { isRecord } from '@willbooster/shared-lib';\nimport { z } from 'zod';\n\nimport { languageIdToDefinition } from '../types/language.js';\nimport { codeRuleSchema } from '../types/problem.js';\n\n// Schemas in this file mirror the Exercode judge importer so that generated materials validate\n// exactly like the importer would parse them.\n\nexport const LEARNING_MATERIAL_ID_REGEX = /^[0-9_a-z-]+$/;\n\nexport const learningMaterialIdSchema = z.string().regex(LEARNING_MATERIAL_ID_REGEX);\n\nexport const availableLanguageIds = Object.keys(languageIdToDefinition);\nconst availableLanguageIdSchema = z.enum(availableLanguageIds);\n\nconst environmentVariableNameSchema = z.string().regex(/^\\w+$/);\n\nconst jsonSchemaSchema = z.record(z.string(), z.unknown());\nconst httpStatusSchema = z.number().int().min(100).max(599);\nconst pathTemplateSchema = z\n .string()\n .startsWith('/')\n .refine((path) => !path.includes('?') && !path.includes('#'), 'path must not carry a query or fragment; use query')\n .refine(\n (path) => path.split('/').every((segment) => !/[{}]/.test(segment) || /^\\{[^{}]+\\}$/.test(segment)),\n 'a {name} placeholder must be one whole path segment'\n );\n// Header names are lower-cased before matching, so a capitalized name could never match.\nconst headersJsonSchemaSchema = jsonSchemaSchema.refine(\n (schema) => declaresOnlyLowerCaseNames(schema),\n 'header names in properties and required must be lower case'\n);\n\n// Mirrors the judge server's `agentChallengeSchema`.\nexport const agentChallengeSchema = z.strictObject({\n playgrounds: z.array(z.enum(['bbs', 'backlog', 'hosting'])).min(1),\n expectedRequest: z.strictObject({\n method: z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']),\n path: pathTemplateSchema,\n status: z.union([httpStatusSchema, z.array(httpStatusSchema).min(1)]).optional(),\n query: jsonSchemaSchema.optional(),\n headers: headersJsonSchemaSchema.optional(),\n body: jsonSchemaSchema.optional(),\n }),\n});\n\n// Mirrors the judge server's strict problem frontmatter schema; keys the server removed with the\n// v1 judge (judgeEnvironmentId, generalJudgeEnvironmentConfigOverrides, testCases, isGui) are\n// rejected here as unknown keys as well.\nexport const problemFrontmatterSchema = z.strictObject({\n name: z.string().min(1),\n type: z.enum(['prompt_study']).optional(),\n timeLimitMs: z.number().int().min(0).optional(),\n memoryLimitByte: z.number().int().min(0).optional(),\n requiredRegExpsInCode: codeRuleSchema.array().default([]),\n forbiddenRegExpsInCode: codeRuleSchema.array().default([]),\n forbiddenTextsInCode: codeRuleSchema.array().default([]),\n canCreateFiles: z.boolean().optional(),\n isEditorDisabled: z.boolean().optional(),\n isAttachedFileRequired: z.boolean().optional(),\n isManualScoringRequired: z.boolean().optional(),\n isVotable: z.boolean().optional(),\n requiredEnvironmentVariables: z.array(environmentVariableNameSchema).default([]),\n requiredOutputFilePaths: z.string().min(1).array().default([]),\n requiredSubmissionFilePaths: z.string().min(1).array().default([]),\n agentChallenge: agentChallengeSchema.optional(),\n});\n\nexport type ProblemFrontmatter = z.infer<typeof problemFrontmatterSchema>;\n\nconst materialConfigShape = {\n availableLanguageIds: availableLanguageIdSchema.array().optional(),\n areTestCasesHidden: z.boolean().optional(),\n isProblemGradingResultHidden: z.boolean().optional(),\n isAutoFormatDisabled: z.boolean().optional(),\n isCopyAndPasteDisabled: z.boolean().optional(),\n isDebugHintDisabled: z.boolean().optional(),\n isFixHintDisabled: z.boolean().optional(),\n isDiffHintDisabled: z.boolean().optional(),\n debugHintWaitingSeconds: z.number().min(0).optional(),\n fixHintWaitingSeconds: z.number().min(0).optional(),\n diffHintWaitingSeconds: z.number().min(0).optional(),\n // z.iso.datetime is the zod v4 API (this repo pins zod 4); z.string().datetime is the v3 form.\n submissionOpenedAt: z.iso.datetime({ offset: true }).optional(),\n submissionSoftClosedAt: z.iso.datetime({ offset: true }).optional(),\n submissionHardClosedAt: z.iso.datetime({ offset: true }).optional(),\n isAutoTranslationDisabled: z.boolean().optional(),\n isModelAnswerShownAfterDeadline: z.boolean().optional(),\n isVotable: z.boolean().optional(),\n isMaterialChatDisabled: z.boolean().optional(),\n} as const;\n\nexport const courseFileSchema = z.strictObject({\n ...materialConfigShape,\n name: z.string().min(1),\n description: z.string(),\n author: z.string().min(1).optional(),\n isMotivationFeatureEnabled: z.boolean().optional(),\n isPublic: z.boolean().optional(),\n startsAt: z.iso.datetime({ offset: true }).optional(),\n // The importer strips unknown keys of a lecture entry (plain object) while the course object is strict.\n lectures: z\n .array(\n z.object({\n id: learningMaterialIdSchema,\n name: z.string().min(1),\n description: z.string(),\n })\n )\n .optional(),\n});\n\nexport type CourseFile = z.infer<typeof courseFileSchema>;\n\nconst uniqueOptionsSchema = z\n .string()\n .array()\n .min(1)\n // zod 4 names the custom-message option `error` (`message` is the deprecated zod 3 spelling).\n .refine((options) => options.length === new Set(options).size, { error: 'options must contain unique values' });\n\n// A quoted index is coerced like the judge does, but a non-numeric string is a schema error rather\n// than a NaN that would surface as an out-of-bounds index.\nconst answerIndexStringSchema = z\n .string()\n .regex(/^\\d+$/, { error: 'must be a non-negative integer index (quoted numbers such as \"0\" are accepted)' })\n .transform(Number);\n\nconst questionBaseShape = {\n id: learningMaterialIdSchema,\n isResubmittable: z.boolean().optional(),\n isSurvey: z.boolean().optional(),\n explanation: z.string().optional(),\n hint: z.string().optional(),\n} as const;\n\nconst selectQuestionShape = {\n ...questionBaseShape,\n type: z.literal('select'),\n options: uniqueOptionsSchema,\n answerIndex: z\n .union([\n z.number().int().nonnegative(),\n answerIndexStringSchema,\n z.number().int().nonnegative().array(),\n answerIndexStringSchema.array(),\n ])\n .optional(),\n} as const;\n\nconst selectMultipleQuestionShape = {\n ...questionBaseShape,\n type: z.literal('select_multiple'),\n options: uniqueOptionsSchema,\n answerIndices: z.union([z.number().int().nonnegative().array(), answerIndexStringSchema.array()]).optional(),\n} as const;\n\nconst textQuestionShape = {\n ...questionBaseShape,\n type: z.literal('text'),\n answerPattern: z.string().min(1).optional(),\n modelAnswer: z.string().optional(),\n} as const;\n\nexport const questionInCodeBlockSchema = z.discriminatedUnion('type', [\n z.strictObject({ ...selectQuestionShape, question: z.string() }),\n z.strictObject({ ...selectMultipleQuestionShape, question: z.string() }),\n z.strictObject({ ...textQuestionShape, question: z.string() }),\n]);\n\n// The judge still supports question definitions in material frontmatter where `question` is optional.\nconst questionInFrontmatterSchema = z.discriminatedUnion('type', [\n z.strictObject({ ...selectQuestionShape, question: z.string().optional() }),\n z.strictObject({ ...selectMultipleQuestionShape, question: z.string().optional() }),\n z.strictObject({ ...textQuestionShape, question: z.string().optional() }),\n]);\n\nexport type MaterialQuestion = z.infer<typeof questionInFrontmatterSchema>;\n\nexport const materialFrontmatterSchema = z.strictObject({\n ...materialConfigShape,\n name: z.string().min(1),\n problems: z.strictObject({ id: learningMaterialIdSchema }).array().optional(),\n questions: z.array(questionInFrontmatterSchema).optional(),\n turtleGraphicsQuestions: z.object({ id: learningMaterialIdSchema }).array().optional(),\n isExamination: z.boolean().optional(),\n isMockExamination: z.boolean().optional(),\n isRealtimeSurvey: z.boolean().optional(),\n});\n\nexport const CONTEST_MATERIAL_FILE_SUFFIX = '.contest.yaml';\n\nconst contestDivisionSchema = z\n .strictObject({\n id: learningMaterialIdSchema,\n name: z.string().min(1),\n openedAt: z.iso.datetime({ offset: true }),\n closedAt: z.iso.datetime({ offset: true }),\n password: z.string().min(1).optional(),\n })\n .refine((division) => new Date(division.openedAt) < new Date(division.closedAt), {\n error: 'openedAt must be earlier than closedAt',\n });\n\nconst contestProblemSchema = z.strictObject({\n id: learningMaterialIdSchema,\n score: z.number().int().nonnegative(),\n // Per-problem overrides reuse the shared material-config fields, mirroring the judge schema.\n areTestCasesHidden: materialConfigShape.areTestCasesHidden,\n isDebugHintDisabled: materialConfigShape.isDebugHintDisabled,\n isFixHintDisabled: materialConfigShape.isFixHintDisabled,\n isDiffHintDisabled: materialConfigShape.isDiffHintDisabled,\n debugHintWaitingSeconds: materialConfigShape.debugHintWaitingSeconds,\n fixHintWaitingSeconds: materialConfigShape.fixHintWaitingSeconds,\n diffHintWaitingSeconds: materialConfigShape.diffHintWaitingSeconds,\n isMaterialChatDisabled: materialConfigShape.isMaterialChatDisabled,\n});\n\nexport const contestFileSchema = z.strictObject({\n ...materialConfigShape,\n name: z.string().min(1),\n description: z.string().optional(),\n // Exercode reads the contest administrators itself and strips the key before the judge's strict schema.\n adminEmails: z.array(z.string().trim().email()).optional(),\n showsProblemsAfterClose: z.boolean().optional(),\n divisions: z.array(contestDivisionSchema).min(1),\n problems: z.array(contestProblemSchema).min(1),\n});\n\n/** Checks every `properties` key and `required` item at any depth, so composed schemas (allOf, $defs, if/then) count too. */\nfunction declaresOnlyLowerCaseNames(schema: unknown, visited = new WeakSet<object>()): boolean {\n // A YAML alias can make the schema cyclic.\n if (typeof schema !== 'object' || schema === null || visited.has(schema)) return true;\n visited.add(schema);\n if (Array.isArray(schema)) return schema.every((item) => declaresOnlyLowerCaseNames(item, visited));\n if (!isRecord(schema)) return true;\n const propertyNames = isRecord(schema.properties) ? Object.keys(schema.properties) : [];\n const requiredNames: unknown[] = Array.isArray(schema.required) ? schema.required : [];\n return (\n [...propertyNames, ...requiredNames].every((name) => typeof name !== 'string' || name === name.toLowerCase()) &&\n Object.values(schema).every((value) => declaresOnlyLowerCaseNames(value, visited))\n );\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exercode/problem-utils",
3
- "version": "5.0.4",
3
+ "version": "5.1.0",
4
4
  "description": ":100: A set of utilities for judging programs on Exercode (https://exercode.willbooster.com/).",
5
5
  "keywords": [
6
6
  "exercode",