prefix-safe-json 0.4.5 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/LICENSE +27 -4
  2. package/README.md +273 -398
  3. package/dist/ajv.d.ts +2 -0
  4. package/dist/ajv.d.ts.map +1 -0
  5. package/dist/ajv.js +19 -0
  6. package/dist/ajv.js.map +1 -0
  7. package/dist/conformance/runner.d.ts +19 -0
  8. package/dist/conformance/runner.d.ts.map +1 -0
  9. package/dist/conformance/runner.js +78 -0
  10. package/dist/conformance/runner.js.map +1 -0
  11. package/dist/conformance/types.d.ts +115 -0
  12. package/dist/conformance/types.d.ts.map +1 -0
  13. package/dist/conformance/types.js +2 -0
  14. package/dist/conformance/types.js.map +1 -0
  15. package/dist/conformance.d.ts +3 -0
  16. package/dist/conformance.d.ts.map +1 -0
  17. package/dist/conformance.js +11 -0
  18. package/dist/conformance.js.map +1 -0
  19. package/dist/coordinator/coordinator.d.ts +25 -11
  20. package/dist/coordinator/coordinator.d.ts.map +1 -1
  21. package/dist/coordinator/coordinator.js +140 -25
  22. package/dist/coordinator/coordinator.js.map +1 -1
  23. package/dist/coordinator/types.d.ts +3 -2
  24. package/dist/coordinator/types.d.ts.map +1 -1
  25. package/dist/gate/gate.d.ts +2 -2
  26. package/dist/gate/gate.d.ts.map +1 -1
  27. package/dist/gate/gate.js +4 -4
  28. package/dist/gate/gate.js.map +1 -1
  29. package/dist/guard/provider-guard.js +1 -1
  30. package/dist/guard/provider-guard.js.map +1 -1
  31. package/dist/guard/types.d.ts +11 -4
  32. package/dist/guard/types.d.ts.map +1 -1
  33. package/dist/index.d.ts +11 -0
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js.map +1 -1
  36. package/dist/standard-schema.d.ts +2 -0
  37. package/dist/standard-schema.d.ts.map +1 -0
  38. package/dist/standard-schema.js +11 -0
  39. package/dist/standard-schema.js.map +1 -0
  40. package/dist/validation/ajv-validator.d.ts +43 -0
  41. package/dist/validation/ajv-validator.d.ts.map +1 -0
  42. package/dist/validation/ajv-validator.js +63 -0
  43. package/dist/validation/ajv-validator.js.map +1 -0
  44. package/dist/validation/standard-schema-validator.d.ts +41 -0
  45. package/dist/validation/standard-schema-validator.d.ts.map +1 -0
  46. package/dist/validation/standard-schema-validator.js +131 -0
  47. package/dist/validation/standard-schema-validator.js.map +1 -0
  48. package/dist/validation/types.d.ts +60 -0
  49. package/dist/validation/types.d.ts.map +1 -0
  50. package/dist/validation/types.js +77 -0
  51. package/dist/validation/types.js.map +1 -0
  52. package/package.json +13 -1
@@ -0,0 +1,60 @@
1
+ /**
2
+ * @public (Experimental)
3
+ * The result of validating one tool call's arguments. Structurally minimal
4
+ * on purpose: `errors`, when present, are already-formatted human-readable
5
+ * strings, not ecosystem-specific issue objects (Zod issues, Ajv errors,
6
+ * TypeBox errors, ...) - each adapter is responsible for turning its own
7
+ * validator's native error shape into readable text, so nothing downstream
8
+ * needs to know which validation ecosystem produced a given verdict.
9
+ */
10
+ export type ToolValidationResult = {
11
+ readonly valid: true;
12
+ } | {
13
+ readonly valid: false;
14
+ readonly errors?: readonly string[];
15
+ };
16
+ /**
17
+ * @public (Experimental)
18
+ * The validation boundary this package's execution-authority core depends
19
+ * on. It is deliberately not `ajv`, `zod`, `typebox`, `valibot`, or any
20
+ * other specific ecosystem's own type - implement this against whichever
21
+ * validator your project already uses and pass it in; nothing in this
22
+ * package's core needs to know it exists.
23
+ *
24
+ * A validator verdict is one more piece of evidence, never authority on its
25
+ * own: `valid: false` can make a decision `reject`; `valid: true` never by
26
+ * itself makes a decision `execute` - completeness and lifecycle evidence
27
+ * still have to hold too. See `docs/EXECUTION_GATE.md`.
28
+ *
29
+ * Contract:
30
+ * - `validate` runs synchronously and returns a result. It may throw to
31
+ * signal validator misconfiguration (mirroring how a malformed JSON
32
+ * Schema already fails fast at construction rather than mid-stream); the
33
+ * coordinator treats a thrown `validate()` as `valid: false` for that one
34
+ * call rather than letting the exception abort the whole stream - see
35
+ * `docs/VALIDATION.md#a-validator-that-throws`.
36
+ * - `validate` must be deterministic and free of side effects - it is called
37
+ * with the call's already-parsed, already-complete `stableValue`, and its
38
+ * return value must depend only on that input.
39
+ */
40
+ export interface ToolInputValidator {
41
+ validate(value: unknown): ToolValidationResult;
42
+ }
43
+ /**
44
+ * @internal
45
+ * Builds the coordinator's internal per-tool validator map from the two
46
+ * explicit, separate registration options. There is no structural/duck-type
47
+ * discrimination anywhere in this path - `schemas` entries are always
48
+ * compiled as JSON Schema, `validators` entries are always used as-is, and
49
+ * a tool name present in both is a construction-time error rather than a
50
+ * silently-resolved precedence rule. See `docs/VALIDATION.md`.
51
+ *
52
+ * Collision detection runs *before* any schema is compiled, so a colliding
53
+ * tool name is always reported as a collision - deterministically, even
54
+ * when that same tool's `schemas` entry also happens to be malformed JSON
55
+ * Schema. Without this ordering, whichever check happened to run first
56
+ * would decide which error the caller sees, for reasons unrelated to which
57
+ * problem is actually more fundamental.
58
+ */
59
+ export declare function buildValidatorMap(schemas: Record<string, object> | undefined, validators: Record<string, ToolInputValidator> | undefined): Map<string, ToolInputValidator>;
60
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/validation/types.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,MAAM,MAAM,oBAAoB,GAC5B;IAAE,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAA;CAAE,GACxB;IAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC;AAEnE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,oBAAoB,CAAC;CAChD;AA+BD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,EAC3C,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,GAAG,SAAS,GACzD,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAmCjC"}
@@ -0,0 +1,77 @@
1
+ import { buildSharedAjvValidators } from "./ajv-validator.js";
2
+ /**
3
+ * @internal
4
+ * Runtime shape check for a `validators[toolName]` entry. `ToolInputValidator`
5
+ * is a TypeScript interface, not a runtime guarantee - a plain-JavaScript
6
+ * caller (or a `as never`/`as ToolInputValidator` cast) can hand this
7
+ * anything. An explicitly *registered* malformed validator must never be
8
+ * silently equivalent to "no validator registered": `coordinator.ts`'s
9
+ * `if (validator) { ... }` truthiness check would otherwise skip a `null`/
10
+ * `undefined`/`false`/`0`/`""` entry entirely, letting the call execute
11
+ * with zero diagnostic - the same class of fail-open gap already closed for
12
+ * a validator that returns a malformed *result* (see
13
+ * `docs/VALIDATION.md#a-validator-that-returns-a-malformed-result`). This
14
+ * check exists so that gap cannot reopen one level up, at registration time.
15
+ */
16
+ function isToolInputValidatorShaped(value) {
17
+ return typeof value === "object" && value !== null && typeof value.validate === "function";
18
+ }
19
+ function describeInvalidValidator(value) {
20
+ if (typeof value === "object") {
21
+ try {
22
+ return JSON.stringify(value);
23
+ }
24
+ catch {
25
+ return Object.prototype.toString.call(value);
26
+ }
27
+ }
28
+ return `${typeof value} ${String(value)}`;
29
+ }
30
+ /**
31
+ * @internal
32
+ * Builds the coordinator's internal per-tool validator map from the two
33
+ * explicit, separate registration options. There is no structural/duck-type
34
+ * discrimination anywhere in this path - `schemas` entries are always
35
+ * compiled as JSON Schema, `validators` entries are always used as-is, and
36
+ * a tool name present in both is a construction-time error rather than a
37
+ * silently-resolved precedence rule. See `docs/VALIDATION.md`.
38
+ *
39
+ * Collision detection runs *before* any schema is compiled, so a colliding
40
+ * tool name is always reported as a collision - deterministically, even
41
+ * when that same tool's `schemas` entry also happens to be malformed JSON
42
+ * Schema. Without this ordering, whichever check happened to run first
43
+ * would decide which error the caller sees, for reasons unrelated to which
44
+ * problem is actually more fundamental.
45
+ */
46
+ export function buildValidatorMap(schemas, validators) {
47
+ if (schemas && validators) {
48
+ for (const toolName of Object.keys(validators)) {
49
+ if (Object.prototype.hasOwnProperty.call(schemas, toolName)) {
50
+ throw new Error(`prefix-safe-json: tool ${JSON.stringify(toolName)} is registered in both "schemas" and ` +
51
+ `"validators" - a tool's validation must come from exactly one source. Remove one registration.`);
52
+ }
53
+ }
54
+ }
55
+ const map = new Map();
56
+ if (schemas) {
57
+ // One shared Ajv instance for every schema in THIS `schemas` object -
58
+ // pre-0.5 behavior, restored here after 0.5's per-schema
59
+ // createAjvValidator() calls regressed it (a schema could no longer
60
+ // $ref another schema registered in the same construction - see
61
+ // buildSharedAjvValidators's own doc comment for the direct proof).
62
+ for (const [toolName, validator] of buildSharedAjvValidators(schemas)) {
63
+ map.set(toolName, validator);
64
+ }
65
+ }
66
+ if (validators) {
67
+ for (const [toolName, validator] of Object.entries(validators)) {
68
+ if (!isToolInputValidatorShaped(validator)) {
69
+ throw new Error(`prefix-safe-json: validators[${JSON.stringify(toolName)}] is not a valid ToolInputValidator - ` +
70
+ `expected an object with a "validate" function, received: ${describeInvalidValidator(validator)}`);
71
+ }
72
+ map.set(toolName, validator);
73
+ }
74
+ }
75
+ return map;
76
+ }
77
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/validation/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AA2C9D;;;;;;;;;;;;;GAaG;AACH,SAAS,0BAA0B,CAAC,KAAc;IAChD,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,OAAQ,KAAgC,CAAC,QAAQ,KAAK,UAAU,CAAC;AACzH,CAAC;AAED,SAAS,wBAAwB,CAAC,KAAc;IAC9C,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IACD,OAAO,GAAG,OAAO,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;AAC5C,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,iBAAiB,CAC/B,OAA2C,EAC3C,UAA0D;IAE1D,IAAI,OAAO,IAAI,UAAU,EAAE,CAAC;QAC1B,KAAK,MAAM,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YAC/C,IAAI,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,CAAC;gBAC5D,MAAM,IAAI,KAAK,CACb,0BAA0B,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,uCAAuC;oBACvF,gGAAgG,CACnG,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,GAAG,EAA8B,CAAC;IAClD,IAAI,OAAO,EAAE,CAAC;QACZ,sEAAsE;QACtE,yDAAyD;QACzD,oEAAoE;QACpE,gEAAgE;QAChE,oEAAoE;QACpE,KAAK,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,IAAI,wBAAwB,CAAC,OAAO,CAAC,EAAE,CAAC;YACtE,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;IACD,IAAI,UAAU,EAAE,CAAC;QACf,KAAK,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;YAC/D,IAAI,CAAC,0BAA0B,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC3C,MAAM,IAAI,KAAK,CACb,gCAAgC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,wCAAwC;oBAC9F,4DAA4D,wBAAwB,CAAC,SAAS,CAAC,EAAE,CACpG,CAAC;YACJ,CAAC;YACD,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prefix-safe-json",
3
- "version": "0.4.5",
3
+ "version": "0.5.0",
4
4
  "description": "Fail-closed execution integrity for streamed LLM tool calls — reject truncated or unconfirmed arguments before side effects",
5
5
  "keywords": [
6
6
  "json",
@@ -31,6 +31,18 @@
31
31
  ".": {
32
32
  "types": "./dist/index.d.ts",
33
33
  "import": "./dist/index.js"
34
+ },
35
+ "./ajv": {
36
+ "types": "./dist/ajv.d.ts",
37
+ "import": "./dist/ajv.js"
38
+ },
39
+ "./standard-schema": {
40
+ "types": "./dist/standard-schema.d.ts",
41
+ "import": "./dist/standard-schema.js"
42
+ },
43
+ "./conformance": {
44
+ "types": "./dist/conformance.d.ts",
45
+ "import": "./dist/conformance.js"
34
46
  }
35
47
  },
36
48
  "files": [