@openpond/evals 0.7.0 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/LEARNING.md +14 -0
  2. package/dist/javascript-verifier-worker-source.js +1 -1
  3. package/dist/learning/authoring-service.js +49 -0
  4. package/dist/learning/authoring.js +37 -0
  5. package/dist/learning/index.js +1 -0
  6. package/dist/learning/operations.js +7 -3
  7. package/dist/learning/repository.js +2 -0
  8. package/dist/learning/service.js +22 -0
  9. package/dist/learning/transport.js +10 -0
  10. package/dist/task-schema-meta-validator.js +2 -0
  11. package/dist/task-schema-validation.js +113 -0
  12. package/dist/task-schema.js +4 -142
  13. package/dist/task-value-validator.js +37 -0
  14. package/dist/types/learning/authoring-service.d.ts +25 -0
  15. package/dist/types/learning/authoring-service.d.ts.map +1 -0
  16. package/dist/types/learning/authoring.d.ts +672 -0
  17. package/dist/types/learning/authoring.d.ts.map +1 -0
  18. package/dist/types/learning/index.d.ts +1 -0
  19. package/dist/types/learning/index.d.ts.map +1 -1
  20. package/dist/types/learning/operations.d.ts +557 -1
  21. package/dist/types/learning/operations.d.ts.map +1 -1
  22. package/dist/types/learning/repository.d.ts +192 -0
  23. package/dist/types/learning/repository.d.ts.map +1 -1
  24. package/dist/types/learning/service.d.ts +14 -0
  25. package/dist/types/learning/service.d.ts.map +1 -1
  26. package/dist/types/learning/transport.d.ts +505 -2
  27. package/dist/types/learning/transport.d.ts.map +1 -1
  28. package/dist/types/task-schema-meta-validator.d.ts +3 -0
  29. package/dist/types/task-schema-validation.d.ts +18 -0
  30. package/dist/types/task-schema-validation.d.ts.map +1 -0
  31. package/dist/types/task-schema.d.ts +2 -17
  32. package/dist/types/task-schema.d.ts.map +1 -1
  33. package/dist/types/task-value-validator.d.ts +3 -0
  34. package/dist/types/task-value-validator.d.ts.map +1 -0
  35. package/package.json +2 -2
  36. package/schemas/learning/v1/command-request.schema.json +757 -366
  37. package/schemas/learning/v1/draft.schema.json +562 -0
  38. package/schemas/learning/v1/evidence-inspection.schema.json +106 -0
  39. package/schemas/learning/v1/read-request.schema.json +42 -0
@@ -0,0 +1,49 @@
1
+ import { assertBoundedTaskJson } from "../task-schema.js";
2
+ import { AuthoringDraftSchema } from "./authoring.js";
3
+ import { LearningDomainError } from "./errors.js";
4
+ import { learningRef, sameLearningRef, sealLearningContent } from "./contracts.js";
5
+ import { requireLearningRelease } from "./repository.js";
6
+ export async function saveAuthoringDraft(tx, input, now) {
7
+ // A 100-record page remains below the common 16 MiB response limit.
8
+ assertBoundedTaskJson(input.draft, 131_072);
9
+ const previous = await tx.get("draft", input.draft.id);
10
+ if (previous && (previous.status !== "draft" || previous.targetId !== input.draft.targetId || previous.targetKind !== input.draft.targetKind || (previous.baseRelease === null ? input.draft.baseRelease !== null : input.draft.baseRelease === null || !sameLearningRef(previous.baseRelease, input.draft.baseRelease))))
11
+ throw new LearningDomainError("authoring_draft_identity_conflict", 409);
12
+ if (input.draft.baseRelease) {
13
+ if (input.draft.baseRelease.id !== input.draft.targetId)
14
+ throw new LearningDomainError("authoring_draft_base_mismatch", 422);
15
+ await requireLearningRelease(tx, input.draft.targetKind, input.draft.baseRelease);
16
+ }
17
+ const draft = AuthoringDraftSchema.parse(sealLearningContent({ ...input.draft, schemaVersion: "openpond.authoringDraft.v1", revision: input.expectedRevision + 1, status: "draft", publishedRelease: null, createdAt: previous?.createdAt ?? now, updatedAt: now }));
18
+ await tx.put("draft", draft, input.expectedRevision, { parentId: draft.targetKind, status: draft.status });
19
+ return { kind: "draft", id: draft.id, revision: draft.revision };
20
+ }
21
+ export async function archiveAuthoringDraft(tx, ref, now) {
22
+ const draft = await currentDraft(tx, ref);
23
+ const { contentHash: _hash, ...content } = draft;
24
+ const archived = AuthoringDraftSchema.parse(sealLearningContent({ ...content, revision: draft.revision + 1, status: "archived", updatedAt: now }));
25
+ await tx.put("draft", archived, draft.revision, { parentId: draft.targetKind, status: archived.status });
26
+ return { kind: "draft", id: archived.id, revision: archived.revision };
27
+ }
28
+ export async function finalizeAuthoringDraft(tx, input, published, now) {
29
+ const finalization = input.finalizeDraft;
30
+ if (!finalization)
31
+ return null;
32
+ const draft = await currentDraft(tx, finalization.draft);
33
+ if (draft.targetKind !== finalization.targetKind || draft.targetId !== finalization.release.id || finalization.release.revision !== (draft.baseRelease?.revision ?? 0) + 1 || !published.some(value => value.kind === finalization.targetKind && value.id === finalization.release.id && value.revision === finalization.release.revision))
34
+ throw new LearningDomainError("authoring_draft_publication_mismatch", 422);
35
+ await requireLearningRelease(tx, finalization.targetKind, finalization.release);
36
+ const { contentHash: _hash, ...content } = draft;
37
+ const completed = AuthoringDraftSchema.parse(sealLearningContent({ ...content, revision: draft.revision + 1, status: "published", publishedRelease: finalization.release, updatedAt: now }));
38
+ await tx.put("draft", completed, draft.revision, { parentId: draft.targetKind, status: completed.status });
39
+ return { kind: "draft", id: completed.id, revision: completed.revision };
40
+ }
41
+ async function currentDraft(tx, ref) {
42
+ const draft = await requireLearningRelease(tx, "draft", ref);
43
+ const current = await tx.get("draft", ref.id);
44
+ if (!current || !sameLearningRef(learningRef(current), ref))
45
+ throw new LearningDomainError("authoring_draft_revision_stale", 409);
46
+ if (draft.status !== "draft")
47
+ throw new LearningDomainError("authoring_draft_closed", 409);
48
+ return draft;
49
+ }
@@ -0,0 +1,37 @@
1
+ import { z } from "zod";
2
+ import { ReleaseHashSchema, ReleaseIdSchema, ReleaseTimestampSchema } from "@openpond/harness";
3
+ import { RewardBindingSourceSchema, RewardReleaseRefSchema } from "../rewards.js";
4
+ import { LearningRevisionRefSchema } from "./contracts.js";
5
+ /** Strings intentionally preserve incomplete JSON/code until explicit publication. */
6
+ export const RewardAuthoringFieldsSchema = z.object({
7
+ name: z.string().max(500), description: z.string().max(10_000),
8
+ kind: z.enum(["custom_verifier", "state", "content", "schema", "artifact", "runtime_event", "model_judge", "learned_model", "human"]),
9
+ fields: z.string(), outputField: z.string(), expectedField: z.string(), expectedValue: z.string(),
10
+ schema: z.string(), reference: z.string(), events: z.string(), code: z.string(), exportName: z.string(), timeout: z.string(),
11
+ rubric: z.string(), providerId: z.string(), modelId: z.string(), modelRevision: z.string(), temperature: z.string(),
12
+ reviewerRole: z.string(), learnedId: z.string(), learnedHash: z.string(), inputContract: z.string(), minimum: z.string(), maximum: z.string(),
13
+ }).strict();
14
+ export const TaskFormatAuthoringFieldsSchema = z.object({
15
+ name: z.string().max(500), description: z.string().max(10_000), instructions: z.string().max(20_000),
16
+ input: z.string(), output: z.string(), familyNamespace: z.string(),
17
+ sources: z.array(RewardBindingSourceSchema).max(100), recipeRef: RewardReleaseRefSchema.optional(),
18
+ }).strict();
19
+ export const CombinedRewardAuthoringFieldsSchema = z.object({
20
+ name: z.string().max(500), description: z.string().max(10_000), sources: z.array(RewardBindingSourceSchema).max(100),
21
+ }).strict();
22
+ export const AuthoringTargetKindSchema = z.enum(["definition", "reward", "binding"]);
23
+ const base = z.object({ id: ReleaseIdSchema, targetId: ReleaseIdSchema, baseRelease: LearningRevisionRefSchema.nullable(), editorVersion: z.literal("openpond.modelsEditor.v1") });
24
+ export const AuthoringDraftInputSchema = z.discriminatedUnion("targetKind", [
25
+ base.extend({ targetKind: z.literal("definition"), fields: TaskFormatAuthoringFieldsSchema }).strict(),
26
+ base.extend({ targetKind: z.literal("reward"), fields: RewardAuthoringFieldsSchema }).strict(),
27
+ base.extend({ targetKind: z.literal("binding"), fields: CombinedRewardAuthoringFieldsSchema }).strict(),
28
+ ]);
29
+ const history = {
30
+ schemaVersion: z.literal("openpond.authoringDraft.v1"), revision: z.number().int().positive(),
31
+ status: z.enum(["draft", "archived", "published"]), publishedRelease: LearningRevisionRefSchema.nullable(),
32
+ createdAt: ReleaseTimestampSchema, updatedAt: ReleaseTimestampSchema,
33
+ };
34
+ export const AuthoringDraftContentSchema = z.discriminatedUnion("targetKind", [AuthoringDraftInputSchema.options[0].extend(history).strict(), AuthoringDraftInputSchema.options[1].extend(history).strict(), AuthoringDraftInputSchema.options[2].extend(history).strict()]);
35
+ export const AuthoringDraftSchema = z.discriminatedUnion("targetKind", [AuthoringDraftContentSchema.options[0].extend({ contentHash: ReleaseHashSchema }).strict(), AuthoringDraftContentSchema.options[1].extend({ contentHash: ReleaseHashSchema }).strict(), AuthoringDraftContentSchema.options[2].extend({ contentHash: ReleaseHashSchema }).strict()]);
36
+ /** Exact draft and resulting release are finalized in the publication transaction. */
37
+ export const AuthoringDraftFinalizationSchema = z.object({ draft: LearningRevisionRefSchema, targetKind: AuthoringTargetKindSchema, release: LearningRevisionRefSchema }).strict();
@@ -7,3 +7,4 @@ export * from "./service.js";
7
7
  export * from "./grade-worker.js";
8
8
  export * from "./transport.js";
9
9
  export * from "./errors.js";
10
+ export * from "./authoring.js";
@@ -2,9 +2,10 @@ import { z } from "zod";
2
2
  import { ReleaseIdSchema } from "@openpond/harness";
3
3
  import { LearningJsonObjectSchema, LearningRevisionRefSchema, LearningSourceContentSchema, TaskDefinitionContentSchema, TaskExampleSubmissionSchema, TaskFeedbackSubmissionSchema, LearningPolicyContentSchema } from "./contracts.js";
4
4
  import { RewardBindingContentSchema, RewardReleaseContentSchema } from "../rewards.js";
5
+ import { AuthoringDraftInputSchema, AuthoringDraftFinalizationSchema } from "./authoring.js";
5
6
  import { LearningTextAssetContentSchema } from "./assets.js";
6
7
  const command = z.object({ operationId: ReleaseIdSchema });
7
- const publish = command.extend({ action: z.literal("publish"), expectedRevision: z.number().int().nonnegative() });
8
+ const publish = command.extend({ action: z.literal("publish"), expectedRevision: z.number().int().nonnegative(), finalizeDraft: AuthoringDraftFinalizationSchema.optional() });
8
9
  export const PublishLearningResourceCommandSchema = z.discriminatedUnion("kind", [
9
10
  publish.extend({ kind: z.literal("asset"), content: LearningTextAssetContentSchema }).strict(),
10
11
  publish.extend({ kind: z.literal("definition"), content: TaskDefinitionContentSchema }).strict(),
@@ -15,7 +16,8 @@ export const PublishLearningResourceCommandSchema = z.discriminatedUnion("kind",
15
16
  ]);
16
17
  export const PublishLearningResourcesCommandSchema = command.extend({
17
18
  action: z.literal("publish_resources"),
18
- resources: z.array(z.union(PublishLearningResourceCommandSchema.options.map((schema) => schema.omit({ operationId: true, action: true })))).min(1).max(20),
19
+ finalizeDraft: AuthoringDraftFinalizationSchema.optional(),
20
+ resources: z.array(z.union(PublishLearningResourceCommandSchema.options.map((schema) => schema.omit({ operationId: true, action: true, finalizeDraft: true })))).min(1).max(20),
19
21
  }).strict();
20
22
  export const SubmitTaskExampleCommandSchema = command.extend({ action: z.literal("submit_example"), example: TaskExampleSubmissionSchema }).strict();
21
23
  export const SubmitTaskFeedbackCommandSchema = command.extend({ action: z.literal("submit_feedback"), feedback: TaskFeedbackSubmissionSchema }).strict();
@@ -30,4 +32,6 @@ export const ReviewTaskEvidenceCommandSchema = command.extend({
30
32
  }).strict();
31
33
  export const SealTaskBatchCommandSchema = command.extend({ action: z.literal("seal_batch"), batchId: ReleaseIdSchema, taskDefinition: LearningRevisionRefSchema, purpose: z.enum(["supervised_training", "reward_training", "evaluation"]), evidence: z.array(LearningRevisionRefSchema).min(1).max(10_000), decisions: z.array(LearningRevisionRefSchema).min(1).max(10_000) }).strict();
32
34
  export const CancelTaskGradeCommandSchema = command.extend({ action: z.literal("cancel_grade"), gradeId: ReleaseIdSchema, expectedRevision: z.number().int().positive() }).strict();
33
- export const LearningCommandSchema = z.union([PublishLearningResourceCommandSchema, PublishLearningResourcesCommandSchema, SubmitTaskExampleCommandSchema, SubmitTaskFeedbackCommandSchema, ApplyTaskCorrectionCommandSchema, ResolveTaskFeedbackCommandSchema, QueueTaskGradeCommandSchema, ReviewTaskEvidenceCommandSchema, SealTaskBatchCommandSchema, CancelTaskGradeCommandSchema]);
35
+ export const SaveAuthoringDraftCommandSchema = command.extend({ action: z.literal("save_draft"), expectedRevision: z.number().int().nonnegative(), draft: AuthoringDraftInputSchema }).strict();
36
+ export const ArchiveAuthoringDraftCommandSchema = command.extend({ action: z.literal("archive_draft"), draft: LearningRevisionRefSchema }).strict();
37
+ export const LearningCommandSchema = z.union([SaveAuthoringDraftCommandSchema, ArchiveAuthoringDraftCommandSchema, PublishLearningResourceCommandSchema, PublishLearningResourcesCommandSchema, SubmitTaskExampleCommandSchema, SubmitTaskFeedbackCommandSchema, ApplyTaskCorrectionCommandSchema, ResolveTaskFeedbackCommandSchema, QueueTaskGradeCommandSchema, ReviewTaskEvidenceCommandSchema, SealTaskBatchCommandSchema, CancelTaskGradeCommandSchema]);
@@ -4,8 +4,10 @@ import { assertLearningContentHash, LearningSourceSchema, TaskAdmissionDecisionS
4
4
  import { RewardBindingSchema, RewardReleaseSchema } from "../rewards.js";
5
5
  import { TasksetReleaseSchema } from "../tasksets.js";
6
6
  import { LearningDomainError } from "./errors.js";
7
+ import { AuthoringDraftSchema } from "./authoring.js";
7
8
  import { LearningTextAssetSchema } from "./assets.js";
8
9
  export const learningResourceSchemas = {
10
+ draft: AuthoringDraftSchema,
9
11
  asset: LearningTextAssetSchema,
10
12
  definition: TaskDefinitionSchema, reward: RewardReleaseSchema, binding: RewardBindingSchema,
11
13
  source: LearningSourceSchema, evidence: TaskEvidenceSchema, feedback: TaskFeedbackSchema,
@@ -1,3 +1,4 @@
1
+ import { saveAuthoringDraft, archiveAuthoringDraft, finalizeAuthoringDraft } from "./authoring-service.js";
1
2
  import { LearningDomainError } from "./errors.js";
2
3
  import { contentHash } from "@openpond/harness";
3
4
  import { createRewardBinding, createRewardRelease, resolveBoundRewards } from "../rewards.js";
@@ -8,6 +9,7 @@ import { LearningCommandSchema } from "./operations.js";
8
9
  import { LearningConflictError, learningEvidenceId, learningOperationId, requireLearningRelease, requireLearningResource } from "./repository.js";
9
10
  import { validateSourceSubmission } from "./admission.js";
10
11
  import { LearningTextAssetSchema, verifyLearningTextAsset } from "./assets.js";
12
+ import { LearningRevisionRefSchema } from "./contracts.js";
11
13
  export function createLearningService(repository, options = {}) {
12
14
  const now = options.now ?? (() => new Date().toISOString());
13
15
  async function command(context, raw) {
@@ -26,6 +28,12 @@ export function createLearningService(repository, options = {}) {
26
28
  }
27
29
  let pointers;
28
30
  switch (input.action) {
31
+ case "save_draft":
32
+ pointers = [await saveAuthoringDraft(transaction, input, now())];
33
+ break;
34
+ case "archive_draft":
35
+ pointers = [await archiveAuthoringDraft(transaction, input.draft, now())];
36
+ break;
29
37
  case "publish":
30
38
  pointers = [await publish(transaction, input)];
31
39
  break;
@@ -60,6 +68,11 @@ export function createLearningService(repository, options = {}) {
60
68
  pointers = await seal(transaction, input, context.actor.id);
61
69
  break;
62
70
  }
71
+ if (input.action === "publish" || input.action === "publish_resources") {
72
+ const finalized = await finalizeAuthoringDraft(transaction, input, pointers, now());
73
+ if (finalized)
74
+ pointers.push(finalized);
75
+ }
63
76
  await transaction.saveOperation(operationId, { requestHash, resources: pointers });
64
77
  return { operationId: input.operationId, resources: await Promise.all(pointers.map((pointer) => requireLearningResource(transaction, pointer.kind, pointer.id, pointer.revision))) };
65
78
  });
@@ -321,6 +334,15 @@ export function createLearningService(repository, options = {}) {
321
334
  }
322
335
  return {
323
336
  command,
337
+ async inspectEvidence(context, reference) {
338
+ authorizeRead(context);
339
+ const ref = LearningRevisionRefSchema.parse(reference);
340
+ return repository.transaction(context.scope, async (transaction) => {
341
+ const evidence = await requireLearningRelease(transaction, "evidence", ref);
342
+ const definition = await requireLearningRelease(transaction, "definition", evidence.submission.taskDefinition);
343
+ return { evidence: learningRef(evidence), definition: learningRef(definition), inspection: inspectTaskEvidence(evidence, definition) };
344
+ });
345
+ },
324
346
  async get(context, kind, id, revision) {
325
347
  authorizeRead(context);
326
348
  return repository.transaction(context.scope, (transaction) => requireLearningResource(transaction, kind, id, revision));
@@ -4,6 +4,7 @@ import { LearningCommandSchema } from "./operations.js";
4
4
  import { LearningResourceKindSchema, learningResourceSchemas } from "./repository.js";
5
5
  import { assertBoundedTaskJson } from "../task-schema.js";
6
6
  import { LearningDomainError } from "./errors.js";
7
+ import { LearningRevisionRefSchema } from "./contracts.js";
7
8
  /** Run before recursive schema parsing, including for non-TypeScript producers. */
8
9
  export function assertLearningRequestJson(raw) {
9
10
  try {
@@ -16,9 +17,18 @@ export function assertLearningRequestJson(raw) {
16
17
  /** Scope selection is never authorization; each host resolves its authenticated owner. */
17
18
  export const LearningCommandRequestSchema = z.object({ scope: ReleaseIdSchema, command: LearningCommandSchema }).strict();
18
19
  export const LearningReadRequestSchema = z.discriminatedUnion("action", [
20
+ z.object({ action: z.literal("inspect_evidence"), scope: ReleaseIdSchema, evidence: LearningRevisionRefSchema }).strict(),
19
21
  z.object({ action: z.literal("get"), scope: ReleaseIdSchema, kind: LearningResourceKindSchema, id: ReleaseIdSchema, revision: z.number().int().positive().optional() }).strict(),
20
22
  z.object({ action: z.literal("list"), scope: ReleaseIdSchema, kind: LearningResourceKindSchema, parentId: ReleaseIdSchema.optional(), status: z.string().min(1).max(200).optional(), afterId: ReleaseIdSchema.optional(), limit: z.number().int().min(1).max(100).default(50) }).strict(),
21
23
  ]);
24
+ export const TaskEvidenceInspectionResultSchema = z.object({
25
+ evidence: LearningRevisionRefSchema,
26
+ definition: LearningRevisionRefSchema,
27
+ inspection: z.object({
28
+ evidenceValidity: z.enum(["valid", "invalid"]), taskReady: z.boolean(), observedOutputValid: z.boolean().nullable(),
29
+ issues: z.array(z.object({ path: z.string(), code: z.string(), message: z.string() }).strict()).max(100),
30
+ }).strict(),
31
+ }).strict();
22
32
  export const LearningOperationResultSchema = z.object({
23
33
  operationId: ReleaseIdSchema,
24
34
  resources: z.array(z.union(Object.values(learningResourceSchemas))).max(100),
@@ -0,0 +1,2 @@
1
+ // Generated by scripts/generate-task-schema-validator.ts. Do not edit.
2
+ var E=(e,i)=>()=>{try{return i||e((i={exports:{}}).exports,i),i.exports}catch(m){throw i=0,m}};var C=E((z,N)=>{"use strict";N.exports=function e(i,m){if(i===m)return!0;if(i&&m&&typeof i=="object"&&typeof m=="object"){if(i.constructor!==m.constructor)return!1;var d,a,l;if(Array.isArray(i)){if(d=i.length,d!=m.length)return!1;for(a=d;a--!==0;)if(!e(i[a],m[a]))return!1;return!0}if(i.constructor===RegExp)return i.source===m.source&&i.flags===m.flags;if(i.valueOf!==Object.prototype.valueOf)return i.valueOf()===m.valueOf();if(i.toString!==Object.prototype.toString)return i.toString()===m.toString();if(l=Object.keys(i),d=l.length,d!==Object.keys(m).length)return!1;for(a=d;a--!==0;)if(!Object.prototype.hasOwnProperty.call(m,l[a]))return!1;for(a=d;a--!==0;){var t=l[a];if(!e(i[t],m[t]))return!1}return!0}return i!==i&&m!==m}});var S=E(j=>{"use strict";Object.defineProperty(j,"__esModule",{value:!0});var L=C();L.code='require("ajv/dist/runtime/equal").default';j.default=L});var G=p,H=p,T={$defs:{schemaMap:{type:"object",additionalProperties:{$ref:"#"}},schemaArray:{type:"array",items:{$ref:"#"},minItems:1,maxItems:32},stringArray:{type:"array",items:{type:"string"},uniqueItems:!0},natural:{type:"integer",minimum:0},schemaType:{enum:["array","boolean","integer","null","number","object","string"]}},type:["object","boolean"],additionalProperties:!1,properties:{$schema:{const:"https://json-schema.org/draft/2020-12/schema"},$ref:{type:"string"},$comment:{type:"string"},$defs:{$ref:"#/$defs/schemaMap"},properties:{$ref:"#/$defs/schemaMap"},dependentSchemas:{$ref:"#/$defs/schemaMap"},allOf:{$ref:"#/$defs/schemaArray"},anyOf:{$ref:"#/$defs/schemaArray"},oneOf:{$ref:"#/$defs/schemaArray"},prefixItems:{$ref:"#/$defs/schemaArray"},items:{$ref:"#"},additionalProperties:{$ref:"#"},not:{$ref:"#"},if:{$ref:"#"},then:{$ref:"#"},else:{$ref:"#"},contains:{$ref:"#"},unevaluatedProperties:{$ref:"#"},unevaluatedItems:{$ref:"#"},propertyNames:{$ref:"#"},type:{anyOf:[{$ref:"#/$defs/schemaType"},{type:"array",items:{$ref:"#/$defs/schemaType"},minItems:1,uniqueItems:!0}]},enum:{type:"array",minItems:1},const:!0,default:!0,title:{type:"string"},description:{type:"string"},examples:{type:"array"},readOnly:{type:"boolean"},writeOnly:{type:"boolean"},deprecated:{type:"boolean"},multipleOf:{type:"number",exclusiveMinimum:0},maximum:{type:"number"},exclusiveMaximum:{type:"number"},minimum:{type:"number"},exclusiveMinimum:{type:"number"},maxLength:{$ref:"#/$defs/natural"},minLength:{$ref:"#/$defs/natural"},maxItems:{$ref:"#/$defs/natural"},minItems:{$ref:"#/$defs/natural"},maxContains:{$ref:"#/$defs/natural"},minContains:{$ref:"#/$defs/natural"},maxProperties:{$ref:"#/$defs/natural"},minProperties:{$ref:"#/$defs/natural"},uniqueItems:{type:"boolean"},required:{$ref:"#/$defs/stringArray"},dependentRequired:{type:"object",additionalProperties:{$ref:"#/$defs/stringArray"}},contentEncoding:{type:"string"},contentMediaType:{type:"string"}}},F=Object.prototype.hasOwnProperty,R=S().default;var o={validate:p};function h(e,{instancePath:i="",parentData:m,parentDataProperty:d,rootData:a=e,dynamicAnchors:l={}}={}){let t=null,r=0,f=h.evaluated;if(f.dynamicProps&&(f.props=void 0),f.dynamicItems&&(f.items=void 0),r===0)if(e&&typeof e=="object"&&!Array.isArray(e))for(let g in e){let y=r;o.validate(e[g],{instancePath:i+"/"+g.replace(/~/g,"~0").replace(/\//g,"~1"),parentData:e,parentDataProperty:g,rootData:a,dynamicAnchors:l})||(t=t===null?o.validate.errors:t.concat(o.validate.errors),r=t.length);var n=y===r;if(!n)break}else return h.errors=[{instancePath:i,schemaPath:"#/type",keyword:"type",params:{type:"object"}}],!1;return h.errors=t,r===0}h.evaluated={props:!0,dynamicProps:!1,dynamicItems:!1};function v(e,{instancePath:i="",parentData:m,parentDataProperty:d,rootData:a=e,dynamicAnchors:l={}}={}){let t=null,r=0,f=v.evaluated;if(f.dynamicProps&&(f.props=void 0),f.dynamicItems&&(f.items=void 0),r===0)if(Array.isArray(e)){if(e.length>32)return v.errors=[{instancePath:i,schemaPath:"#/maxItems",keyword:"maxItems",params:{limit:32}}],!1;if(e.length<1)return v.errors=[{instancePath:i,schemaPath:"#/minItems",keyword:"minItems",params:{limit:1}}],!1;{var n=!0;let g=e.length;for(let y=0;y<g;y++){let b=r;o.validate(e[y],{instancePath:i+"/"+y,parentData:e,parentDataProperty:y,rootData:a,dynamicAnchors:l})||(t=t===null?o.validate.errors:t.concat(o.validate.errors),r=t.length);var n=b===r;if(!n)break}}}else return v.errors=[{instancePath:i,schemaPath:"#/type",keyword:"type",params:{type:"array"}}],!1;return v.errors=t,r===0}v.evaluated={items:!0,dynamicProps:!1,dynamicItems:!1};var A={enum:["array","boolean","integer","null","number","object","string"]};function I(e,{instancePath:i="",parentData:m,parentDataProperty:d,rootData:a=e,dynamicAnchors:l={}}={}){let t=null,r=0,f=I.evaluated;return f.dynamicProps&&(f.props=void 0),f.dynamicItems&&(f.items=void 0),e==="array"||e==="boolean"||e==="integer"||e==="null"||e==="number"||e==="object"||e==="string"?(I.errors=t,r===0):(I.errors=[{instancePath:i,schemaPath:"#/enum",keyword:"enum",params:{allowedValues:A.enum}}],!1)}I.evaluated={dynamicProps:!1,dynamicItems:!1};function u(e,{instancePath:i="",parentData:m,parentDataProperty:d,rootData:a=e,dynamicAnchors:l={}}={}){let t=null,r=0,f=u.evaluated;return f.dynamicProps&&(f.props=void 0),f.dynamicItems&&(f.items=void 0),typeof e=="number"&&!(e%1)&&!isNaN(e)&&isFinite(e)?r===0&&typeof e=="number"&&isFinite(e)&&(e<0||isNaN(e))?(u.errors=[{instancePath:i,schemaPath:"#/minimum",keyword:"minimum",params:{comparison:">=",limit:0}}],!1):(u.errors=t,r===0):(u.errors=[{instancePath:i,schemaPath:"#/type",keyword:"type",params:{type:"integer"}}],!1)}u.evaluated={dynamicProps:!1,dynamicItems:!1};function P(e,{instancePath:i="",parentData:m,parentDataProperty:d,rootData:a=e,dynamicAnchors:l={}}={}){let t=null,r=0,f=P.evaluated;if(f.dynamicProps&&(f.props=void 0),f.dynamicItems&&(f.items=void 0),r===0)if(Array.isArray(e)){var n=!0;let g=e.length;for(let y=0;y<g;y++){let b=r;if(typeof e[y]!="string")return P.errors=[{instancePath:i+"/"+y,schemaPath:"#/items/type",keyword:"type",params:{type:"string"}}],!1;var n=b===r;if(!n)break}if(n){let y=e.length,b;if(y>1){let D={};for(;y--;){let s=e[y];if(typeof s=="string"){if(typeof D[s]=="number"){return b=D[s],P.errors=[{instancePath:i,schemaPath:"#/uniqueItems",keyword:"uniqueItems",params:{i:y,j:b}}],!1;break}D[s]=y}}}}}else return P.errors=[{instancePath:i,schemaPath:"#/type",keyword:"type",params:{type:"array"}}],!1;return P.errors=t,r===0}P.evaluated={items:!0,dynamicProps:!1,dynamicItems:!1};function p(e,{instancePath:i="",parentData:m,parentDataProperty:d,rootData:a=e,dynamicAnchors:l={}}={}){let t=null,r=0,f=p.evaluated;if(f.dynamicProps&&(f.props=void 0),f.dynamicItems&&(f.items=void 0),!(e&&typeof e=="object"&&!Array.isArray(e))&&typeof e!="boolean")return p.errors=[{instancePath:i,schemaPath:"#/type",keyword:"type",params:{type:T.type}}],!1;if(r===0&&e&&typeof e=="object"&&!Array.isArray(e)){let D=r;for(let s in e)if(!F.call(T.properties,s)){return p.errors=[{instancePath:i,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty:s}}],!1;break}if(D===r){if(e.$schema!==void 0){let s=r;if(e.$schema!=="https://json-schema.org/draft/2020-12/schema")return p.errors=[{instancePath:i+"/$schema",schemaPath:"#/properties/%24schema/const",keyword:"const",params:{allowedValue:"https://json-schema.org/draft/2020-12/schema"}}],!1;var n=s===r}else var n=!0;if(n){if(e.$ref!==void 0){let s=r;if(typeof e.$ref!="string")return p.errors=[{instancePath:i+"/$ref",schemaPath:"#/properties/%24ref/type",keyword:"type",params:{type:"string"}}],!1;var n=s===r}else var n=!0;if(n){if(e.$comment!==void 0){let s=r;if(typeof e.$comment!="string")return p.errors=[{instancePath:i+"/$comment",schemaPath:"#/properties/%24comment/type",keyword:"type",params:{type:"string"}}],!1;var n=s===r}else var n=!0;if(n){if(e.$defs!==void 0){let s=r;h(e.$defs,{instancePath:i+"/$defs",parentData:e,parentDataProperty:"$defs",rootData:a,dynamicAnchors:l})||(t=t===null?h.errors:t.concat(h.errors),r=t.length);var n=s===r}else var n=!0;if(n){if(e.properties!==void 0){let s=r;h(e.properties,{instancePath:i+"/properties",parentData:e,parentDataProperty:"properties",rootData:a,dynamicAnchors:l})||(t=t===null?h.errors:t.concat(h.errors),r=t.length);var n=s===r}else var n=!0;if(n){if(e.dependentSchemas!==void 0){let s=r;h(e.dependentSchemas,{instancePath:i+"/dependentSchemas",parentData:e,parentDataProperty:"dependentSchemas",rootData:a,dynamicAnchors:l})||(t=t===null?h.errors:t.concat(h.errors),r=t.length);var n=s===r}else var n=!0;if(n){if(e.allOf!==void 0){let s=r;v(e.allOf,{instancePath:i+"/allOf",parentData:e,parentDataProperty:"allOf",rootData:a,dynamicAnchors:l})||(t=t===null?v.errors:t.concat(v.errors),r=t.length);var n=s===r}else var n=!0;if(n){if(e.anyOf!==void 0){let s=r;v(e.anyOf,{instancePath:i+"/anyOf",parentData:e,parentDataProperty:"anyOf",rootData:a,dynamicAnchors:l})||(t=t===null?v.errors:t.concat(v.errors),r=t.length);var n=s===r}else var n=!0;if(n){if(e.oneOf!==void 0){let s=r;v(e.oneOf,{instancePath:i+"/oneOf",parentData:e,parentDataProperty:"oneOf",rootData:a,dynamicAnchors:l})||(t=t===null?v.errors:t.concat(v.errors),r=t.length);var n=s===r}else var n=!0;if(n){if(e.prefixItems!==void 0){let s=r;v(e.prefixItems,{instancePath:i+"/prefixItems",parentData:e,parentDataProperty:"prefixItems",rootData:a,dynamicAnchors:l})||(t=t===null?v.errors:t.concat(v.errors),r=t.length);var n=s===r}else var n=!0;if(n){if(e.items!==void 0){let s=r;o.validate(e.items,{instancePath:i+"/items",parentData:e,parentDataProperty:"items",rootData:a,dynamicAnchors:l})||(t=t===null?o.validate.errors:t.concat(o.validate.errors),r=t.length);var n=s===r}else var n=!0;if(n){if(e.additionalProperties!==void 0){let s=r;o.validate(e.additionalProperties,{instancePath:i+"/additionalProperties",parentData:e,parentDataProperty:"additionalProperties",rootData:a,dynamicAnchors:l})||(t=t===null?o.validate.errors:t.concat(o.validate.errors),r=t.length);var n=s===r}else var n=!0;if(n){if(e.not!==void 0){let s=r;o.validate(e.not,{instancePath:i+"/not",parentData:e,parentDataProperty:"not",rootData:a,dynamicAnchors:l})||(t=t===null?o.validate.errors:t.concat(o.validate.errors),r=t.length);var n=s===r}else var n=!0;if(n){if(e.if!==void 0){let s=r;o.validate(e.if,{instancePath:i+"/if",parentData:e,parentDataProperty:"if",rootData:a,dynamicAnchors:l})||(t=t===null?o.validate.errors:t.concat(o.validate.errors),r=t.length);var n=s===r}else var n=!0;if(n){if(e.then!==void 0){let s=r;o.validate(e.then,{instancePath:i+"/then",parentData:e,parentDataProperty:"then",rootData:a,dynamicAnchors:l})||(t=t===null?o.validate.errors:t.concat(o.validate.errors),r=t.length);var n=s===r}else var n=!0;if(n){if(e.else!==void 0){let s=r;o.validate(e.else,{instancePath:i+"/else",parentData:e,parentDataProperty:"else",rootData:a,dynamicAnchors:l})||(t=t===null?o.validate.errors:t.concat(o.validate.errors),r=t.length);var n=s===r}else var n=!0;if(n){if(e.contains!==void 0){let s=r;o.validate(e.contains,{instancePath:i+"/contains",parentData:e,parentDataProperty:"contains",rootData:a,dynamicAnchors:l})||(t=t===null?o.validate.errors:t.concat(o.validate.errors),r=t.length);var n=s===r}else var n=!0;if(n){if(e.unevaluatedProperties!==void 0){let s=r;o.validate(e.unevaluatedProperties,{instancePath:i+"/unevaluatedProperties",parentData:e,parentDataProperty:"unevaluatedProperties",rootData:a,dynamicAnchors:l})||(t=t===null?o.validate.errors:t.concat(o.validate.errors),r=t.length);var n=s===r}else var n=!0;if(n){if(e.unevaluatedItems!==void 0){let s=r;o.validate(e.unevaluatedItems,{instancePath:i+"/unevaluatedItems",parentData:e,parentDataProperty:"unevaluatedItems",rootData:a,dynamicAnchors:l})||(t=t===null?o.validate.errors:t.concat(o.validate.errors),r=t.length);var n=s===r}else var n=!0;if(n){if(e.propertyNames!==void 0){let s=r;o.validate(e.propertyNames,{instancePath:i+"/propertyNames",parentData:e,parentDataProperty:"propertyNames",rootData:a,dynamicAnchors:l})||(t=t===null?o.validate.errors:t.concat(o.validate.errors),r=t.length);var n=s===r}else var n=!0;if(n){if(e.type!==void 0){let s=e.type,c=r,w=r,_=!1,k=r;I(s,{instancePath:i+"/type",parentData:e,parentDataProperty:"type",rootData:a,dynamicAnchors:l})||(t=t===null?I.errors:t.concat(I.errors),r=t.length);var y=k===r;_=_||y;let q=r;if(r===q)if(Array.isArray(s))if(s.length<1){let x={instancePath:i+"/type",schemaPath:"#/properties/type/anyOf/1/minItems",keyword:"minItems",params:{limit:1}};t===null?t=[x]:t.push(x),r++}else{var g=!0;let x=s.length;for(let $=0;$<x;$++){let O=r;I(s[$],{instancePath:i+"/type/"+$,parentData:s,parentDataProperty:$,rootData:a,dynamicAnchors:l})||(t=t===null?I.errors:t.concat(I.errors),r=t.length);var g=O===r;if(!g)break}if(g){let $=s.length,O;if($>1){e:for(;$--;)for(O=$;O--;)if(R(s[$],s[O])){let M={instancePath:i+"/type",schemaPath:"#/properties/type/anyOf/1/uniqueItems",keyword:"uniqueItems",params:{i:$,j:O}};t===null?t=[M]:t.push(M),r++;break e}}}}else{let x={instancePath:i+"/type",schemaPath:"#/properties/type/anyOf/1/type",keyword:"type",params:{type:"array"}};t===null?t=[x]:t.push(x),r++}var y=q===r;if(_=_||y,_)r=w,t!==null&&(w?t.length=w:t=null);else{let x={instancePath:i+"/type",schemaPath:"#/properties/type/anyOf",keyword:"anyOf",params:{}};return t===null?t=[x]:t.push(x),r++,p.errors=t,!1}var n=c===r}else var n=!0;if(n){if(e.enum!==void 0){let s=e.enum,c=r;if(r===c)if(Array.isArray(s)){if(s.length<1)return p.errors=[{instancePath:i+"/enum",schemaPath:"#/properties/enum/minItems",keyword:"minItems",params:{limit:1}}],!1}else return p.errors=[{instancePath:i+"/enum",schemaPath:"#/properties/enum/type",keyword:"type",params:{type:"array"}}],!1;var n=c===r}else var n=!0;if(n){if(e.title!==void 0){let s=r;if(typeof e.title!="string")return p.errors=[{instancePath:i+"/title",schemaPath:"#/properties/title/type",keyword:"type",params:{type:"string"}}],!1;var n=s===r}else var n=!0;if(n){if(e.description!==void 0){let s=r;if(typeof e.description!="string")return p.errors=[{instancePath:i+"/description",schemaPath:"#/properties/description/type",keyword:"type",params:{type:"string"}}],!1;var n=s===r}else var n=!0;if(n){if(e.examples!==void 0){let s=r;if(!Array.isArray(e.examples))return p.errors=[{instancePath:i+"/examples",schemaPath:"#/properties/examples/type",keyword:"type",params:{type:"array"}}],!1;var n=s===r}else var n=!0;if(n){if(e.readOnly!==void 0){let s=r;if(typeof e.readOnly!="boolean")return p.errors=[{instancePath:i+"/readOnly",schemaPath:"#/properties/readOnly/type",keyword:"type",params:{type:"boolean"}}],!1;var n=s===r}else var n=!0;if(n){if(e.writeOnly!==void 0){let s=r;if(typeof e.writeOnly!="boolean")return p.errors=[{instancePath:i+"/writeOnly",schemaPath:"#/properties/writeOnly/type",keyword:"type",params:{type:"boolean"}}],!1;var n=s===r}else var n=!0;if(n){if(e.deprecated!==void 0){let s=r;if(typeof e.deprecated!="boolean")return p.errors=[{instancePath:i+"/deprecated",schemaPath:"#/properties/deprecated/type",keyword:"type",params:{type:"boolean"}}],!1;var n=s===r}else var n=!0;if(n){if(e.multipleOf!==void 0){let s=e.multipleOf,c=r;if(r===c)if(typeof s=="number"&&isFinite(s)){if(s<=0||isNaN(s))return p.errors=[{instancePath:i+"/multipleOf",schemaPath:"#/properties/multipleOf/exclusiveMinimum",keyword:"exclusiveMinimum",params:{comparison:">",limit:0}}],!1}else return p.errors=[{instancePath:i+"/multipleOf",schemaPath:"#/properties/multipleOf/type",keyword:"type",params:{type:"number"}}],!1;var n=c===r}else var n=!0;if(n){if(e.maximum!==void 0){let s=e.maximum,c=r;if(!(typeof s=="number"&&isFinite(s)))return p.errors=[{instancePath:i+"/maximum",schemaPath:"#/properties/maximum/type",keyword:"type",params:{type:"number"}}],!1;var n=c===r}else var n=!0;if(n){if(e.exclusiveMaximum!==void 0){let s=e.exclusiveMaximum,c=r;if(!(typeof s=="number"&&isFinite(s)))return p.errors=[{instancePath:i+"/exclusiveMaximum",schemaPath:"#/properties/exclusiveMaximum/type",keyword:"type",params:{type:"number"}}],!1;var n=c===r}else var n=!0;if(n){if(e.minimum!==void 0){let s=e.minimum,c=r;if(!(typeof s=="number"&&isFinite(s)))return p.errors=[{instancePath:i+"/minimum",schemaPath:"#/properties/minimum/type",keyword:"type",params:{type:"number"}}],!1;var n=c===r}else var n=!0;if(n){if(e.exclusiveMinimum!==void 0){let s=e.exclusiveMinimum,c=r;if(!(typeof s=="number"&&isFinite(s)))return p.errors=[{instancePath:i+"/exclusiveMinimum",schemaPath:"#/properties/exclusiveMinimum/type",keyword:"type",params:{type:"number"}}],!1;var n=c===r}else var n=!0;if(n){if(e.maxLength!==void 0){let s=r;u(e.maxLength,{instancePath:i+"/maxLength",parentData:e,parentDataProperty:"maxLength",rootData:a,dynamicAnchors:l})||(t=t===null?u.errors:t.concat(u.errors),r=t.length);var n=s===r}else var n=!0;if(n){if(e.minLength!==void 0){let s=r;u(e.minLength,{instancePath:i+"/minLength",parentData:e,parentDataProperty:"minLength",rootData:a,dynamicAnchors:l})||(t=t===null?u.errors:t.concat(u.errors),r=t.length);var n=s===r}else var n=!0;if(n){if(e.maxItems!==void 0){let s=r;u(e.maxItems,{instancePath:i+"/maxItems",parentData:e,parentDataProperty:"maxItems",rootData:a,dynamicAnchors:l})||(t=t===null?u.errors:t.concat(u.errors),r=t.length);var n=s===r}else var n=!0;if(n){if(e.minItems!==void 0){let s=r;u(e.minItems,{instancePath:i+"/minItems",parentData:e,parentDataProperty:"minItems",rootData:a,dynamicAnchors:l})||(t=t===null?u.errors:t.concat(u.errors),r=t.length);var n=s===r}else var n=!0;if(n){if(e.maxContains!==void 0){let s=r;u(e.maxContains,{instancePath:i+"/maxContains",parentData:e,parentDataProperty:"maxContains",rootData:a,dynamicAnchors:l})||(t=t===null?u.errors:t.concat(u.errors),r=t.length);var n=s===r}else var n=!0;if(n){if(e.minContains!==void 0){let s=r;u(e.minContains,{instancePath:i+"/minContains",parentData:e,parentDataProperty:"minContains",rootData:a,dynamicAnchors:l})||(t=t===null?u.errors:t.concat(u.errors),r=t.length);var n=s===r}else var n=!0;if(n){if(e.maxProperties!==void 0){let s=r;u(e.maxProperties,{instancePath:i+"/maxProperties",parentData:e,parentDataProperty:"maxProperties",rootData:a,dynamicAnchors:l})||(t=t===null?u.errors:t.concat(u.errors),r=t.length);var n=s===r}else var n=!0;if(n){if(e.minProperties!==void 0){let s=r;u(e.minProperties,{instancePath:i+"/minProperties",parentData:e,parentDataProperty:"minProperties",rootData:a,dynamicAnchors:l})||(t=t===null?u.errors:t.concat(u.errors),r=t.length);var n=s===r}else var n=!0;if(n){if(e.uniqueItems!==void 0){let s=r;if(typeof e.uniqueItems!="boolean")return p.errors=[{instancePath:i+"/uniqueItems",schemaPath:"#/properties/uniqueItems/type",keyword:"type",params:{type:"boolean"}}],!1;var n=s===r}else var n=!0;if(n){if(e.required!==void 0){let s=r;P(e.required,{instancePath:i+"/required",parentData:e,parentDataProperty:"required",rootData:a,dynamicAnchors:l})||(t=t===null?P.errors:t.concat(P.errors),r=t.length);var n=s===r}else var n=!0;if(n){if(e.dependentRequired!==void 0){let s=e.dependentRequired,c=r;if(r===c)if(s&&typeof s=="object"&&!Array.isArray(s))for(let _ in s){let k=r;P(s[_],{instancePath:i+"/dependentRequired/"+_.replace(/~/g,"~0").replace(/\//g,"~1"),parentData:s,parentDataProperty:_,rootData:a,dynamicAnchors:l})||(t=t===null?P.errors:t.concat(P.errors),r=t.length);var b=k===r;if(!b)break}else return p.errors=[{instancePath:i+"/dependentRequired",schemaPath:"#/properties/dependentRequired/type",keyword:"type",params:{type:"object"}}],!1;var n=c===r}else var n=!0;if(n){if(e.contentEncoding!==void 0){let s=r;if(typeof e.contentEncoding!="string")return p.errors=[{instancePath:i+"/contentEncoding",schemaPath:"#/properties/contentEncoding/type",keyword:"type",params:{type:"string"}}],!1;var n=s===r}else var n=!0;if(n)if(e.contentMediaType!==void 0){let s=r;if(typeof e.contentMediaType!="string")return p.errors=[{instancePath:i+"/contentMediaType",schemaPath:"#/properties/contentMediaType/type",keyword:"type",params:{type:"string"}}],!1;var n=s===r}else var n=!0}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}return p.errors=t,r===0}p.evaluated={props:!0,dynamicProps:!1,dynamicItems:!1};export{H as default,G as validate};
@@ -0,0 +1,113 @@
1
+ import validateMetaSchema from "./task-schema-meta-validator.js";
2
+ export const TASK_JSON_SCHEMA_DIALECT = "https://json-schema.org/draft/2020-12/schema";
3
+ export const TASK_VALUE_MAX_BYTES = 1_048_576;
4
+ export const TASK_SCHEMA_MAX_BYTES = 32_768;
5
+ const schemaMaps = new Set(["properties", "$defs", "dependentSchemas"]);
6
+ const schemaArrays = new Set(["allOf", "anyOf", "oneOf", "prefixItems"]);
7
+ const schemaChildren = new Set(["items", "additionalProperties", "not", "if", "then", "else", "contains", "unevaluatedProperties", "unevaluatedItems", "propertyNames"]);
8
+ /** Bound producer-controlled JSON before hashing, schema compilation or validation. */
9
+ export function assertBoundedTaskJson(value, maxBytes = TASK_VALUE_MAX_BYTES) {
10
+ const pending = [{ value, depth: 0 }];
11
+ const seen = new Set();
12
+ let nodes = 0;
13
+ while (pending.length) {
14
+ const item = pending.pop();
15
+ if (item.exit) {
16
+ seen.delete(item.value);
17
+ continue;
18
+ }
19
+ if (++nodes > 100_000 || item.depth > 48)
20
+ throw new Error("task_json_complexity_exceeded");
21
+ if (item.value === null || typeof item.value === "string" || typeof item.value === "boolean")
22
+ continue;
23
+ if (typeof item.value === "number" && Number.isFinite(item.value))
24
+ continue;
25
+ if (typeof item.value !== "object")
26
+ throw new Error("task_json_value_invalid");
27
+ if (seen.has(item.value))
28
+ throw new Error("task_json_cycle");
29
+ seen.add(item.value);
30
+ pending.push({ ...item, exit: true });
31
+ const prototype = Object.getPrototypeOf(item.value);
32
+ if (!Array.isArray(item.value) && prototype !== Object.prototype && prototype !== null)
33
+ throw new Error("task_json_object_invalid");
34
+ if (Object.getOwnPropertySymbols(item.value).length)
35
+ throw new Error("task_json_symbol_invalid");
36
+ if (Object.hasOwn(item.value, "toJSON"))
37
+ throw new Error("task_json_serializer_invalid");
38
+ if (Array.isArray(item.value) && (item.value.length > 10_000 || Object.keys(item.value).length !== item.value.length))
39
+ throw new Error("task_json_array_invalid");
40
+ for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(item.value))) {
41
+ if (descriptor.get || descriptor.set)
42
+ throw new Error("task_json_accessor_invalid");
43
+ if (Array.isArray(item.value) && key !== "length" && (!/^(0|[1-9]\d*)$/u.test(key) || Number(key) >= item.value.length))
44
+ throw new Error("task_json_array_invalid");
45
+ if (descriptor.enumerable)
46
+ pending.push({ value: descriptor.value, depth: item.depth + 1 });
47
+ }
48
+ }
49
+ if (new TextEncoder().encode(JSON.stringify(value)).byteLength > maxBytes)
50
+ throw new Error("task_json_size_exceeded");
51
+ }
52
+ /** No remote resolution, custom code, data mutation, regex or format plugins. */
53
+ export function validateTaskSchema(schema) {
54
+ try {
55
+ assertTaskSchema(schema);
56
+ return { valid: true, issues: [] };
57
+ }
58
+ catch (error) {
59
+ return { valid: false, issues: [{ path: "", code: "task_schema_invalid", message: error instanceof Error ? error.message : "Task schema is invalid." }] };
60
+ }
61
+ }
62
+ export function assertTaskSchema(schema) {
63
+ assertBoundedTaskJson(schema, TASK_SCHEMA_MAX_BYTES);
64
+ inspectSchema(schema, schema, new Set(), 0, { remaining: 2_048 });
65
+ if (!validateMetaSchema(schema)) {
66
+ const error = validateMetaSchema.errors?.[0];
67
+ throw new Error(`Task schema ${error?.instancePath || "/"} violates ${error?.keyword ?? "schema"}.`);
68
+ }
69
+ }
70
+ function inspectSchema(value, root, ancestors, depth, budget) {
71
+ if (--budget.remaining < 0)
72
+ throw new Error("Task schema reference expansion exceeds the validation budget.");
73
+ if (typeof value === "boolean")
74
+ return;
75
+ if (!value || typeof value !== "object" || Array.isArray(value))
76
+ throw new Error("A task schema must be an object or boolean.");
77
+ if (depth > 24 || ancestors.has(value))
78
+ throw new Error("Recursive or deeply nested task schemas are not supported.");
79
+ const next = new Set(ancestors).add(value);
80
+ const schema = value;
81
+ if (schema.$schema !== undefined && schema.$schema !== TASK_JSON_SCHEMA_DIALECT)
82
+ throw new Error(`Task schemas must use ${TASK_JSON_SCHEMA_DIALECT}.`);
83
+ for (const [key, child] of Object.entries(schema)) {
84
+ if (key === "$ref") {
85
+ if (typeof child !== "string" || !child.startsWith("#/$defs/"))
86
+ throw new Error("Task schema references must be local JSON pointers into $defs.");
87
+ let target = root;
88
+ for (const segment of child.slice(2).split("/")) {
89
+ if (/~(?![01])/u.test(segment))
90
+ throw new Error("Invalid task schema JSON pointer.");
91
+ const decoded = segment.replaceAll("~1", "/").replaceAll("~0", "~");
92
+ if (!target || typeof target !== "object" || !Object.hasOwn(target, decoded))
93
+ throw new Error(`Task schema reference ${child} is missing.`);
94
+ target = target[decoded];
95
+ }
96
+ inspectSchema(target, root, next, depth + 1, budget);
97
+ }
98
+ else if (schemaMaps.has(key)) {
99
+ if (!child || typeof child !== "object" || Array.isArray(child))
100
+ throw new Error(`Invalid schema map ${key}.`);
101
+ for (const entry of Object.values(child))
102
+ inspectSchema(entry, root, next, depth + 1, budget);
103
+ }
104
+ else if (schemaArrays.has(key)) {
105
+ if (!Array.isArray(child) || child.length > 32)
106
+ throw new Error(`Schema ${key} must contain at most 32 alternatives.`);
107
+ for (const entry of child)
108
+ inspectSchema(entry, root, next, depth + 1, budget);
109
+ }
110
+ else if (schemaChildren.has(key))
111
+ inspectSchema(child, root, next, depth + 1, budget);
112
+ }
113
+ }
@@ -1,142 +1,4 @@
1
- import { Ajv2020 } from "ajv/dist/2020.js";
2
- import { contentHash } from "@openpond/harness";
3
- export const TASK_JSON_SCHEMA_DIALECT = "https://json-schema.org/draft/2020-12/schema";
4
- export const TASK_VALUE_MAX_BYTES = 1_048_576;
5
- export const TASK_SCHEMA_MAX_BYTES = 32_768;
6
- const validators = new Map();
7
- const schemaMaps = new Set(["properties", "$defs", "dependentSchemas"]);
8
- const schemaArrays = new Set(["allOf", "anyOf", "oneOf", "prefixItems"]);
9
- const schemaChildren = new Set(["items", "additionalProperties", "not", "if", "then", "else", "contains", "unevaluatedProperties", "unevaluatedItems", "propertyNames"]);
10
- const unsupported = new Set(["pattern", "patternProperties", "format", "$id", "$anchor", "$dynamicAnchor", "$dynamicRef", "$recursiveRef", "$recursiveAnchor", "$async", "$data", "contentSchema"]);
11
- /** Bound producer-controlled JSON before hashing, schema compilation or validation. */
12
- export function assertBoundedTaskJson(value, maxBytes = TASK_VALUE_MAX_BYTES) {
13
- const pending = [{ value, depth: 0 }];
14
- const seen = new Set();
15
- let nodes = 0;
16
- while (pending.length) {
17
- const item = pending.pop();
18
- if (item.exit) {
19
- seen.delete(item.value);
20
- continue;
21
- }
22
- if (++nodes > 100_000 || item.depth > 48)
23
- throw new Error("task_json_complexity_exceeded");
24
- if (item.value === null || typeof item.value === "string" || typeof item.value === "boolean")
25
- continue;
26
- if (typeof item.value === "number" && Number.isFinite(item.value))
27
- continue;
28
- if (typeof item.value !== "object")
29
- throw new Error("task_json_value_invalid");
30
- if (seen.has(item.value))
31
- throw new Error("task_json_cycle");
32
- seen.add(item.value);
33
- pending.push({ ...item, exit: true });
34
- const prototype = Object.getPrototypeOf(item.value);
35
- if (!Array.isArray(item.value) && prototype !== Object.prototype && prototype !== null)
36
- throw new Error("task_json_object_invalid");
37
- if (Object.getOwnPropertySymbols(item.value).length)
38
- throw new Error("task_json_symbol_invalid");
39
- if (Object.hasOwn(item.value, "toJSON"))
40
- throw new Error("task_json_serializer_invalid");
41
- if (Array.isArray(item.value) && (item.value.length > 10_000 || Object.keys(item.value).length !== item.value.length))
42
- throw new Error("task_json_array_invalid");
43
- for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(item.value))) {
44
- if (descriptor.get || descriptor.set)
45
- throw new Error("task_json_accessor_invalid");
46
- if (Array.isArray(item.value) && key !== "length" && (!/^(0|[1-9]\d*)$/u.test(key) || Number(key) >= item.value.length))
47
- throw new Error("task_json_array_invalid");
48
- if (descriptor.enumerable)
49
- pending.push({ value: descriptor.value, depth: item.depth + 1 });
50
- }
51
- }
52
- if (new TextEncoder().encode(JSON.stringify(value)).byteLength > maxBytes)
53
- throw new Error("task_json_size_exceeded");
54
- }
55
- /** No remote resolution, custom code, data mutation, regex or format plugins. */
56
- export function validateTaskSchema(schema) {
57
- try {
58
- validator(schema);
59
- return { valid: true, issues: [] };
60
- }
61
- catch (error) {
62
- return { valid: false, issues: [{ path: "", code: "task_schema_invalid", message: error instanceof Error ? error.message : "Task schema is invalid." }] };
63
- }
64
- }
65
- export function validateTaskValue(schema, value) {
66
- try {
67
- assertBoundedTaskJson(value);
68
- const validate = validator(schema);
69
- const valid = validate(value);
70
- return {
71
- valid,
72
- issues: valid ? [] : (validate.errors ?? []).slice(0, 20).map((error) => ({
73
- path: error.instancePath,
74
- code: error.keyword,
75
- message: error.message ?? "Value does not match the task schema.",
76
- })),
77
- };
78
- }
79
- catch (error) {
80
- return { valid: false, issues: [{ path: "", code: "task_validation_unavailable", message: error instanceof Error ? error.message : "Task validation failed." }] };
81
- }
82
- }
83
- function validator(schema) {
84
- assertBoundedTaskJson(schema, TASK_SCHEMA_MAX_BYTES);
85
- inspectSchema(schema, schema, new Set(), 0, { remaining: 2_048 });
86
- const hash = contentHash(schema);
87
- const cached = validators.get(hash);
88
- if (cached)
89
- return cached;
90
- const ajv = new Ajv2020({ allErrors: false, ownProperties: true, strict: true, strictTypes: false, strictTuples: false, strictRequired: false, validateFormats: false, inlineRefs: false, loopRequired: 32, loopEnum: 32 });
91
- const compiled = ajv.compile(schema);
92
- if (validators.size >= 64)
93
- validators.delete(validators.keys().next().value);
94
- validators.set(hash, compiled);
95
- return compiled;
96
- }
97
- function inspectSchema(value, root, ancestors, depth, budget) {
98
- if (--budget.remaining < 0)
99
- throw new Error("Task schema reference expansion exceeds the validation budget.");
100
- if (typeof value === "boolean")
101
- return;
102
- if (!value || typeof value !== "object" || Array.isArray(value))
103
- throw new Error("A task schema must be an object or boolean.");
104
- if (depth > 24 || ancestors.has(value))
105
- throw new Error("Recursive or deeply nested task schemas are not supported.");
106
- const next = new Set(ancestors).add(value);
107
- const schema = value;
108
- if (schema.$schema !== undefined && schema.$schema !== TASK_JSON_SCHEMA_DIALECT)
109
- throw new Error(`Task schemas must use ${TASK_JSON_SCHEMA_DIALECT}.`);
110
- for (const [key, child] of Object.entries(schema)) {
111
- if (unsupported.has(key))
112
- throw new Error(`Task schema keyword ${key} is not supported by the bounded validator.`);
113
- if (key === "$ref") {
114
- if (typeof child !== "string" || !child.startsWith("#/$defs/"))
115
- throw new Error("Task schema references must be local JSON pointers into $defs.");
116
- let target = root;
117
- for (const segment of child.slice(2).split("/")) {
118
- if (/~(?![01])/u.test(segment))
119
- throw new Error("Invalid task schema JSON pointer.");
120
- const decoded = segment.replaceAll("~1", "/").replaceAll("~0", "~");
121
- if (!target || typeof target !== "object" || !Object.hasOwn(target, decoded))
122
- throw new Error(`Task schema reference ${child} is missing.`);
123
- target = target[decoded];
124
- }
125
- inspectSchema(target, root, next, depth + 1, budget);
126
- }
127
- else if (schemaMaps.has(key)) {
128
- if (!child || typeof child !== "object" || Array.isArray(child))
129
- throw new Error(`Invalid schema map ${key}.`);
130
- for (const entry of Object.values(child))
131
- inspectSchema(entry, root, next, depth + 1, budget);
132
- }
133
- else if (schemaArrays.has(key)) {
134
- if (!Array.isArray(child) || child.length > 32)
135
- throw new Error(`Schema ${key} must contain at most 32 alternatives.`);
136
- for (const entry of child)
137
- inspectSchema(entry, root, next, depth + 1, budget);
138
- }
139
- else if (schemaChildren.has(key))
140
- inspectSchema(child, root, next, depth + 1, budget);
141
- }
142
- }
1
+ // Keep schema authoring separate from the execution owner's runtime compiler so
2
+ // browsers importing portable contracts can omit compiler code entirely.
3
+ export { TASK_JSON_SCHEMA_DIALECT, TASK_VALUE_MAX_BYTES, TASK_SCHEMA_MAX_BYTES, assertBoundedTaskJson, validateTaskSchema, } from "./task-schema-validation.js";
4
+ export { validateTaskValue } from "./task-value-validator.js";
@@ -0,0 +1,37 @@
1
+ import { Ajv2020 } from "ajv/dist/2020.js";
2
+ import { contentHash } from "@openpond/harness";
3
+ import { assertBoundedTaskJson, assertTaskSchema } from "./task-schema-validation.js";
4
+ const validators = new Map();
5
+ export function validateTaskValue(schema, value) {
6
+ try {
7
+ assertBoundedTaskJson(value);
8
+ const validate = validator(schema);
9
+ const valid = validate(value);
10
+ return {
11
+ valid,
12
+ issues: valid ? [] : (validate.errors ?? []).slice(0, 20).map((error) => ({
13
+ path: error.instancePath,
14
+ code: error.keyword,
15
+ message: error.message ?? "Value does not match the task schema.",
16
+ })),
17
+ };
18
+ }
19
+ catch (error) {
20
+ return { valid: false, issues: [{ path: "", code: "task_validation_unavailable", message: error instanceof Error ? error.message : "Task validation failed." }] };
21
+ }
22
+ }
23
+ function validator(schema) {
24
+ assertTaskSchema(schema);
25
+ const hash = contentHash(schema);
26
+ const cached = validators.get(hash);
27
+ if (cached)
28
+ return cached;
29
+ // The precompiled meta-schema and bounded keyword profile are authoritative
30
+ // for schema validity. Ajv compiles values only on the execution owner.
31
+ const ajv = new Ajv2020({ allErrors: false, ownProperties: true, strict: true, strictSchema: false, strictTypes: false, strictTuples: false, strictRequired: false, validateFormats: false, validateSchema: false, inlineRefs: false, loopRequired: 32, loopEnum: 32 });
32
+ const compiled = ajv.compile(schema);
33
+ if (validators.size >= 64)
34
+ validators.delete(validators.keys().next().value);
35
+ validators.set(hash, compiled);
36
+ return compiled;
37
+ }
@@ -0,0 +1,25 @@
1
+ import { type AuthoringDraftInput } from "./authoring.js";
2
+ import { type LearningRevisionRef } from "./contracts.js";
3
+ import { type LearningResourcePointer, type LearningTransaction } from "./repository.js";
4
+ import type { LearningCommand } from "./operations.js";
5
+ export declare function saveAuthoringDraft(tx: LearningTransaction, input: {
6
+ draft: AuthoringDraftInput;
7
+ expectedRevision: number;
8
+ }, now: string): Promise<{
9
+ kind: "draft";
10
+ id: string;
11
+ revision: number;
12
+ }>;
13
+ export declare function archiveAuthoringDraft(tx: LearningTransaction, ref: LearningRevisionRef, now: string): Promise<{
14
+ kind: "draft";
15
+ id: string;
16
+ revision: number;
17
+ }>;
18
+ export declare function finalizeAuthoringDraft(tx: LearningTransaction, input: Extract<LearningCommand, {
19
+ action: "publish" | "publish_resources";
20
+ }>, published: LearningResourcePointer[], now: string): Promise<{
21
+ kind: "draft";
22
+ id: string;
23
+ revision: number;
24
+ } | null>;
25
+ //# sourceMappingURL=authoring-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authoring-service.d.ts","sourceRoot":"","sources":["../../../../../src/learning/authoring-service.ts"],"names":[],"mappings":"AACA,OAAO,EAAwB,KAAK,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAEhF,OAAO,EAAqD,KAAK,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC7G,OAAO,EAA0B,KAAK,uBAAuB,EAAE,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACjH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD,wBAAsB,kBAAkB,CAAC,EAAE,EAAE,mBAAmB,EAAE,KAAK,EAAE;IAAE,KAAK,EAAE,mBAAmB,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,EAAE,MAAM;;;;GAY7I;AAED,wBAAsB,qBAAqB,CAAC,EAAE,EAAE,mBAAmB,EAAE,GAAG,EAAE,mBAAmB,EAAE,GAAG,EAAE,MAAM;;;;GAMzG;AAED,wBAAsB,sBAAsB,CAAC,EAAE,EAAE,mBAAmB,EAAE,KAAK,EAAE,OAAO,CAAC,eAAe,EAAE;IAAE,MAAM,EAAE,SAAS,GAAG,mBAAmB,CAAA;CAAE,CAAC,EAAE,SAAS,EAAE,uBAAuB,EAAE,EAAE,GAAG,EAAE,MAAM;;;;UAUpM"}