@zhijiewang/openharness 2.17.0 → 2.18.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.
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Minimal JSON Schema validator — covers the common subset sufficient for
3
+ * constraining LLM output in headless mode. Supported keywords:
4
+ *
5
+ * - `type`: "string" | "number" | "integer" | "boolean" | "object" | "array" | "null"
6
+ * (or an array of those for union types)
7
+ * - `properties`: object → sub-schema per field
8
+ * - `required`: array of field names that must be present
9
+ * - `items`: sub-schema for array elements
10
+ * - `enum`: array of allowed literal values (compared with strict equality)
11
+ *
12
+ * Anything else is silently accepted. This is intentional — we don't want to
13
+ * ship a full JSON Schema engine. For cases that need more (e.g. `pattern`,
14
+ * `oneOf`, `$ref`), use an external validator.
15
+ */
16
+ export function validateAgainstJsonSchema(value, schema) {
17
+ const errors = [];
18
+ validate(value, schema, "", errors);
19
+ return errors.length === 0 ? { ok: true } : { ok: false, errors };
20
+ }
21
+ function validate(value, schema, path, errors) {
22
+ if (schema.enum !== undefined && Array.isArray(schema.enum)) {
23
+ if (!schema.enum.some((allowed) => deepEqual(allowed, value))) {
24
+ errors.push(`${prefix(path)}value ${JSON.stringify(value)} is not one of the enum values`);
25
+ return;
26
+ }
27
+ }
28
+ if (schema.type !== undefined) {
29
+ const types = Array.isArray(schema.type) ? schema.type : [schema.type];
30
+ if (!types.some((t) => matchesType(value, t))) {
31
+ errors.push(`${prefix(path)}expected ${types.join(" or ")}, got ${describeActual(value)}`);
32
+ return;
33
+ }
34
+ }
35
+ if (matchesType(value, "object") && schema.properties) {
36
+ const properties = schema.properties;
37
+ const required = schema.required ?? [];
38
+ const obj = value;
39
+ for (const field of required) {
40
+ if (!(field in obj)) {
41
+ const fullPath = path ? `${path}.${field}` : field;
42
+ errors.push(`missing required property '${fullPath}'`);
43
+ }
44
+ }
45
+ for (const [field, subSchema] of Object.entries(properties)) {
46
+ if (field in obj) {
47
+ validate(obj[field], subSchema, path ? `${path}.${field}` : field, errors);
48
+ }
49
+ }
50
+ }
51
+ if (matchesType(value, "array") && schema.items) {
52
+ const items = schema.items;
53
+ const arr = value;
54
+ arr.forEach((item, i) => {
55
+ validate(item, items, `${path}[${i}]`, errors);
56
+ });
57
+ }
58
+ }
59
+ function matchesType(value, type) {
60
+ switch (type) {
61
+ case "string":
62
+ return typeof value === "string";
63
+ case "number":
64
+ return typeof value === "number" && Number.isFinite(value);
65
+ case "integer":
66
+ return typeof value === "number" && Number.isInteger(value);
67
+ case "boolean":
68
+ return typeof value === "boolean";
69
+ case "null":
70
+ return value === null;
71
+ case "array":
72
+ return Array.isArray(value);
73
+ case "object":
74
+ return typeof value === "object" && value !== null && !Array.isArray(value);
75
+ default:
76
+ return false;
77
+ }
78
+ }
79
+ function describeActual(value) {
80
+ if (value === null)
81
+ return "null";
82
+ if (Array.isArray(value))
83
+ return "array";
84
+ return typeof value;
85
+ }
86
+ function prefix(path) {
87
+ return path ? `${path}: ` : "";
88
+ }
89
+ function deepEqual(a, b) {
90
+ if (a === b)
91
+ return true;
92
+ if (typeof a !== typeof b)
93
+ return false;
94
+ if (a === null || b === null)
95
+ return a === b;
96
+ if (Array.isArray(a) && Array.isArray(b)) {
97
+ if (a.length !== b.length)
98
+ return false;
99
+ return a.every((x, i) => deepEqual(x, b[i]));
100
+ }
101
+ if (typeof a === "object" && typeof b === "object") {
102
+ const ka = Object.keys(a);
103
+ const kb = Object.keys(b);
104
+ if (ka.length !== kb.length)
105
+ return false;
106
+ return ka.every((k) => deepEqual(a[k], b[k]));
107
+ }
108
+ return false;
109
+ }
110
+ //# sourceMappingURL=json-schema.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhijiewang/openharness",
3
- "version": "2.17.0",
3
+ "version": "2.18.0",
4
4
  "description": "Open-source terminal coding agent. Works with any LLM.",
5
5
  "type": "module",
6
6
  "bin": {