@aventara/core 0.1.0-pilot.4 → 1.0.0-rc.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 (108) hide show
  1. package/CHANGELOG.md +338 -0
  2. package/README.md +100 -3
  3. package/dist/adapters/adapter-model.d.ts +8 -0
  4. package/dist/adapters/adapter.interface.d.ts +6 -2
  5. package/dist/compiler/contract-compiler-diagnostic.d.ts +6 -0
  6. package/dist/compiler/contract.compiler.js +67 -0
  7. package/dist/config/config.resolver.d.ts +3 -0
  8. package/dist/config/config.resolver.js +1 -0
  9. package/dist/config/config.validator.js +6 -0
  10. package/dist/config/define-fields.d.ts +10 -3
  11. package/dist/config/framework-config.d.ts +4 -1
  12. package/dist/config/framework-fields-config.d.ts +6 -0
  13. package/dist/contracts/client-contract-structure.validator.d.ts +21 -1
  14. package/dist/contracts/client-contract-structure.validator.js +99 -2
  15. package/dist/contracts/contract-canonicalizer.js +32 -0
  16. package/dist/contracts/contract.d.ts +7 -0
  17. package/dist/contracts/field.d.ts +10 -0
  18. package/dist/contracts/json-shape.d.ts +65 -0
  19. package/dist/contracts/json-shape.js +1 -0
  20. package/dist/contracts/reference-write-limit.d.ts +7 -0
  21. package/dist/contracts/reference-write-limit.js +1 -0
  22. package/dist/contracts/scalar-value-type.d.ts +12 -1
  23. package/dist/framework/contract-view.d.ts +126 -21
  24. package/dist/framework/framework.d.ts +5 -3
  25. package/dist/framework/framework.factory.js +56 -13
  26. package/dist/index.d.ts +8 -40
  27. package/dist/index.js +0 -20
  28. package/dist/internal.d.ts +35 -0
  29. package/dist/internal.js +16 -0
  30. package/dist/json/avz.d.ts +96 -0
  31. package/dist/json/avz.js +212 -0
  32. package/dist/json/index.d.ts +6 -0
  33. package/dist/json/index.js +1 -0
  34. package/dist/json/json-shape.registry.d.ts +22 -0
  35. package/dist/json/json-shape.registry.js +16 -0
  36. package/dist/json/zod-issue.translator.d.ts +51 -0
  37. package/dist/json/zod-issue.translator.js +142 -0
  38. package/dist/operations/operation-arguments.d.ts +151 -101
  39. package/dist/operations/operation-identity.d.ts +2 -0
  40. package/dist/operations/operation-identity.js +3 -0
  41. package/dist/operations/operation-validator-core.d.ts +1 -1
  42. package/dist/operations/operation-validator-core.js +28 -10
  43. package/dist/operations/operation-validator.d.ts +20 -5
  44. package/dist/operations/operation-validator.js +26 -10
  45. package/dist/operations/projection-scope.resolver.d.ts +1 -1
  46. package/dist/operations/structural-limits.walker.d.ts +19 -0
  47. package/dist/operations/structural-limits.walker.js +344 -0
  48. package/dist/protocol/absent-route.responder.d.ts +2 -1
  49. package/dist/protocol/absent-route.responder.js +32 -6
  50. package/dist/protocol/http-request.decoder.d.ts +6 -0
  51. package/dist/protocol/http-request.decoder.js +11 -3
  52. package/dist/protocol/protocol.binder.d.ts +4 -2
  53. package/dist/protocol/protocol.binder.js +3 -1
  54. package/dist/protocol/protocol.handler.d.ts +6 -2
  55. package/dist/protocol/protocol.handler.js +14 -4
  56. package/dist/protocol/unmounted-route.error.d.ts +19 -0
  57. package/dist/protocol/unmounted-route.error.js +10 -0
  58. package/dist/runtime/caller-data.copier.d.ts +59 -0
  59. package/dist/runtime/caller-data.copier.js +470 -0
  60. package/dist/runtime/caller-data.door.d.ts +56 -0
  61. package/dist/runtime/caller-data.door.js +65 -0
  62. package/dist/runtime/computed-read-runtime.js +3 -10
  63. package/dist/runtime/computed-value.error.d.ts +13 -0
  64. package/dist/runtime/computed-value.error.js +9 -0
  65. package/dist/runtime/create-requirement.d.ts +43 -7
  66. package/dist/runtime/create-requirement.js +89 -9
  67. package/dist/runtime/diagnostics-reporter.d.ts +20 -4
  68. package/dist/runtime/diagnostics-reporter.js +11 -4
  69. package/dist/runtime/error-boundary.d.ts +8 -0
  70. package/dist/runtime/error-boundary.js +65 -35
  71. package/dist/runtime/field-behavior.d.ts +2 -2
  72. package/dist/runtime/framework-error.d.ts +12 -0
  73. package/dist/runtime/misuse-reporter.d.ts +32 -0
  74. package/dist/runtime/misuse-reporter.js +19 -0
  75. package/dist/runtime/mutation-planner.js +13 -11
  76. package/dist/runtime/operation-request.d.ts +4 -11
  77. package/dist/runtime/operation-runtime.d.ts +14 -7
  78. package/dist/runtime/operation-runtime.js +37 -18
  79. package/dist/runtime/outcome-mapper.js +12 -2
  80. package/dist/runtime/output-validator.d.ts +10 -0
  81. package/dist/runtime/output-validator.js +11 -0
  82. package/dist/runtime/owned-execution.registry.d.ts +13 -0
  83. package/dist/runtime/owned-execution.registry.js +7 -0
  84. package/dist/runtime/pipeline.d.ts +19 -7
  85. package/dist/runtime/referential-action.d.ts +7 -0
  86. package/dist/runtime/request-envelope.validator.d.ts +5 -0
  87. package/dist/runtime/request-envelope.validator.js +118 -0
  88. package/dist/runtime/scalar-runtime.d.ts +30 -2
  89. package/dist/runtime/scalar-runtime.js +162 -79
  90. package/dist/transactions/transaction-plan-assembler.d.ts +5 -1
  91. package/dist/transactions/transaction-plan-assembler.js +74 -75
  92. package/dist/transactions/transaction-plan-validator.d.ts +30 -7
  93. package/dist/transactions/transaction-plan-validator.js +204 -153
  94. package/dist/transactions/transaction-reference-collector.d.ts +30 -0
  95. package/dist/transactions/transaction-reference-collector.js +204 -0
  96. package/dist/transactions/transaction-reference-resolver.js +10 -5
  97. package/dist/transactions/transaction-reference-validator.js +24 -17
  98. package/dist/transactions/transaction-reference.d.ts +23 -51
  99. package/dist/transactions/transaction-request.d.ts +3 -3
  100. package/dist/transactions/transaction-response.d.ts +6 -1
  101. package/dist/transactions/transaction-response.js +24 -1
  102. package/dist/transactions/transaction-runtime.d.ts +4 -2
  103. package/dist/transactions/transaction-runtime.js +4 -3
  104. package/package.json +17 -3
  105. package/dist/operations/operation-boolean-nodes.d.ts +0 -11
  106. package/dist/operations/operation-boolean-nodes.js +0 -283
  107. package/dist/operations/operation-nesting.d.ts +0 -8
  108. package/dist/operations/operation-nesting.js +0 -307
@@ -0,0 +1,96 @@
1
+ import type { JsonSchemaPrimitive, JsonShape, JsonShapeValue } from "../contracts/json-shape.js";
2
+ /** The forms a Json Shape takes. */
3
+ export type AvZKind = "string" | "number" | "int" | "boolean" | "null" | "literal" | "enum" | "object" | "array" | "tuple" | "record" | "union" | "discriminatedUnion" | "optional" | "nullable";
4
+ /**
5
+ * A Json Shape built with {@link AvZ}: opaque, its `kind` the only member. Its
6
+ * TypeScript type is `AvZ.infer<typeof shape>`.
7
+ */
8
+ export interface AvZShape<Value = unknown, Kind extends AvZKind = AvZKind> extends JsonShape<Value> {
9
+ readonly kind: Kind;
10
+ }
11
+ /** The fields of an object shape, by key. */
12
+ export type AvZFields = {
13
+ readonly [key: string]: AvZShape;
14
+ };
15
+ declare const avzOptionalKey: unique symbol;
16
+ /** Marks a shape whose object key may be left out. Type-level only. */
17
+ interface AvZOptionalKey {
18
+ readonly [avzOptionalKey]: true;
19
+ }
20
+ type FieldKeysOptional<Fields extends AvZFields> = {
21
+ [Key in keyof Fields]: Fields[Key] extends AvZOptionalKey ? Key : never;
22
+ }[keyof Fields];
23
+ type Flatten<T> = {
24
+ [Key in keyof T]: T[Key];
25
+ } & {};
26
+ /** The value an object shape describes. */
27
+ type AvZObjectValue<Fields extends AvZFields> = ObjectValueOf<Fields, FieldKeysOptional<Fields>>;
28
+ type ObjectValueOf<Fields extends AvZFields, Optional extends keyof Fields> = Flatten<{
29
+ -readonly [Key in Exclude<keyof Fields, Optional>]: JsonShapeValue<Fields[Key]>;
30
+ } & {
31
+ -readonly [Key in Optional]?: JsonShapeValue<Fields[Key]>;
32
+ }>;
33
+ /** An object shape: closed, so a key it does not declare is refused. */
34
+ export interface AvZObject<Fields extends AvZFields = AvZFields> extends AvZShape<AvZObjectValue<Fields>, "object"> {
35
+ readonly fields: Fields;
36
+ }
37
+ /** A shape whose object key may be left out. */
38
+ export interface AvZOptional<Inner extends AvZShape = AvZShape> extends AvZShape<JsonShapeValue<Inner> | undefined, "optional">, AvZOptionalKey {
39
+ }
40
+ /** A shape that also admits `null`. */
41
+ export type AvZNullable<Inner extends AvZShape = AvZShape> = AvZShape<JsonShapeValue<Inner> | null, "nullable"> & (Inner extends AvZOptionalKey ? AvZOptionalKey : unknown);
42
+ /** A value a literal shape may hold. */
43
+ export type AvZLiteralValue = JsonSchemaPrimitive;
44
+ /** What keys a record shape: any string, an enum's values or one literal. */
45
+ type AvZRecordKey = AvZShape<string, "string"> | AvZShape<string, "enum"> | AvZShape<string, "literal">;
46
+ /** An option of a discriminated union: an object whose discriminator is a literal or an enum. */
47
+ type AvZDiscriminable<Discriminator extends string> = AvZObject<{
48
+ readonly [Key in Discriminator]: AvZShape<AvZLiteralValue, "literal"> | AvZShape<string, "enum">;
49
+ } & AvZFields>;
50
+ type TupleValue<Items extends readonly AvZShape[]> = {
51
+ -readonly [Index in keyof Items]: JsonShapeValue<Items[Index]>;
52
+ };
53
+ /**
54
+ * Builds Json Shapes: the type of a Json field, declared in the configuration as
55
+ * `fields: { Post: { meta: { json: AvZ.object({ … }) } } }`. Types only — no
56
+ * rules, refinements, transforms or defaults. Objects are closed: a key the shape
57
+ * does not declare is refused.
58
+ */
59
+ export declare class AvZ {
60
+ private constructor();
61
+ /** Any string. */
62
+ static readonly string: () => AvZShape<string, "string">;
63
+ /** Any finite number. */
64
+ static readonly number: () => AvZShape<number, "number">;
65
+ /** An integer (a safe one). */
66
+ static readonly int: () => AvZShape<number, "int">;
67
+ /** `true` or `false`. */
68
+ static readonly boolean: () => AvZShape<boolean, "boolean">;
69
+ /** `null`. */
70
+ static readonly null: () => AvZShape<null, "null">;
71
+ /** Exactly `value`: a string, a finite number, a boolean or `null`. */
72
+ static readonly literal: <const Value extends AvZLiteralValue>(value: Value) => AvZShape<Value, "literal">;
73
+ /** One of `values`. */
74
+ static readonly enum: <const Values extends readonly [string, ...string[]]>(values: Values) => AvZShape<Values[number], "enum">;
75
+ /** An object with exactly these keys; `AvZ.optional` marks one that may be left out. */
76
+ static readonly object: <const Fields extends AvZFields>(fields: Fields) => AvZObject<Fields>;
77
+ /** A list of `item`s. */
78
+ static readonly array: <Item extends AvZShape>(item: Item) => AvZShape<JsonShapeValue<Item>[], "array">;
79
+ /** A list of exactly these items, in order. */
80
+ static readonly tuple: <const Items extends readonly [AvZShape, ...AvZShape[]]>(items: Items) => AvZShape<TupleValue<Items>, "tuple">;
81
+ /** An object of `value`s, keyed by any string, an enum's values or a literal. */
82
+ static readonly record: <Key extends AvZRecordKey, Value extends AvZShape>(key: Key, value: Value) => AvZShape<Record<JsonShapeValue<Key>, JsonShapeValue<Value>>, "record">;
83
+ /** Any one of `options`. */
84
+ static readonly union: <const Options extends readonly [AvZShape, AvZShape, ...AvZShape[]]>(options: Options) => AvZShape<JsonShapeValue<Options[number]>, "union">;
85
+ /** One of `options`, told apart by the literal or enum at `discriminator`. */
86
+ static readonly discriminatedUnion: <Discriminator extends string, const Options extends readonly [AvZDiscriminable<Discriminator>, ...AvZDiscriminable<Discriminator>[]]>(discriminator: Discriminator, options: Options) => AvZShape<JsonShapeValue<Options[number]>, "discriminatedUnion">;
87
+ /** An object key that may be left out. */
88
+ static readonly optional: <Inner extends AvZShape>(inner: Inner) => AvZOptional<Inner>;
89
+ /** `inner`, or `null`. */
90
+ static readonly nullable: <Inner extends AvZShape>(inner: Inner) => AvZNullable<Inner>;
91
+ }
92
+ export declare namespace AvZ {
93
+ /** The TypeScript type a shape describes. */
94
+ type infer<Shape extends JsonShape> = JsonShapeValue<Shape>;
95
+ }
96
+ export {};
@@ -0,0 +1,212 @@
1
+ import * as zodMini from "zod/mini";
2
+ import { registerJsonShape } from "./json-shape.registry.js";
3
+ import { translateZodIssues } from "./zod-issue.translator.js";
4
+ const z = zodMini;
5
+ const SHAPE_PROTOTYPE = Object.freeze(Object.create(null));
6
+ const compiled = new WeakMap();
7
+ function compiledOf(builder, value) {
8
+ const found = typeof value === "object" && value !== null
9
+ ? compiled.get(value)
10
+ : undefined;
11
+ if (found === undefined) {
12
+ throw new TypeError(`AvZ.${builder}: every part of a shape must be built with AvZ (a Zod schema or any other value is not accepted).`);
13
+ }
14
+ return found;
15
+ }
16
+ function requiredPart(builder, value) {
17
+ const part = compiledOf(builder, value);
18
+ if (part.optionalKey) {
19
+ throw new TypeError(`AvZ.${builder}: AvZ.optional marks an object key that may be left out; use it only for a field of AvZ.object.`);
20
+ }
21
+ return part;
22
+ }
23
+ function build(node, zod, schema, optionalKey = false) {
24
+ const shape = Object.freeze(Object.assign(Object.create(SHAPE_PROTOTYPE), { kind: node.kind }));
25
+ const frozenSchema = deepFreeze(schema);
26
+ compiled.set(shape, { node, zod, schema: frozenSchema, optionalKey });
27
+ registerJsonShape(shape, {
28
+ schema: frozenSchema,
29
+ validate: (value) => {
30
+ const result = zod.safeParse(value);
31
+ return result.success
32
+ ? []
33
+ : translateZodIssues(node, value, result.error.issues);
34
+ },
35
+ });
36
+ return shape;
37
+ }
38
+ function deepFreeze(value) {
39
+ if (typeof value === "object" && value !== null) {
40
+ for (const child of Object.values(value)) {
41
+ deepFreeze(child);
42
+ }
43
+ Object.freeze(value);
44
+ }
45
+ return value;
46
+ }
47
+ function isLiteralValue(value) {
48
+ return (value === null ||
49
+ typeof value === "string" ||
50
+ typeof value === "boolean" ||
51
+ (typeof value === "number" && Number.isFinite(value)));
52
+ }
53
+ const string = () => build({ kind: "string" }, z.string(), { type: "string" });
54
+ const number = () => build({ kind: "number" }, z.number(), { type: "number" });
55
+ const int = () => build({ kind: "int" }, z.int(), { type: "integer" });
56
+ const boolean = () => build({ kind: "boolean" }, z.boolean(), { type: "boolean" });
57
+ const nullShape = () => build({ kind: "null" }, z.null(), { type: "null" });
58
+ const literal = (value) => {
59
+ if (!isLiteralValue(value)) {
60
+ throw new TypeError("AvZ.literal: the value must be a string, a finite number, a boolean or null.");
61
+ }
62
+ return build({ kind: "literal", value }, z.literal(value), { const: value });
63
+ };
64
+ const enumShape = (values) => {
65
+ if (!Array.isArray(values) ||
66
+ values.length === 0 ||
67
+ values.some((value) => typeof value !== "string") ||
68
+ new Set(values).size !== values.length) {
69
+ throw new TypeError("AvZ.enum: the values must be a non-empty list of distinct strings.");
70
+ }
71
+ const copy = [...values];
72
+ return build({ kind: "enum", values: copy }, z.enum(copy), {
73
+ type: "string",
74
+ enum: copy,
75
+ });
76
+ };
77
+ const object = (fields) => {
78
+ if (typeof fields !== "object" || fields === null || Array.isArray(fields)) {
79
+ throw new TypeError("AvZ.object: the fields must be an object of shapes.");
80
+ }
81
+ const nodes = {};
82
+ const zods = {};
83
+ const properties = {};
84
+ const required = [];
85
+ for (const key of Object.keys(fields)) {
86
+ const part = compiledOf("object", fields[key]);
87
+ nodes[key] = part.node;
88
+ zods[key] = part.zod;
89
+ properties[key] = part.schema;
90
+ if (!part.optionalKey) {
91
+ required.push(key);
92
+ }
93
+ }
94
+ return build({ kind: "object", fields: nodes }, z.strictObject(zods), {
95
+ type: "object",
96
+ properties,
97
+ ...(required.length > 0 ? { required } : {}),
98
+ additionalProperties: false,
99
+ });
100
+ };
101
+ const array = (item) => {
102
+ const part = requiredPart("array", item);
103
+ return build({ kind: "array", item: part.node }, z.array(part.zod), {
104
+ type: "array",
105
+ items: part.schema,
106
+ });
107
+ };
108
+ const tuple = (items) => {
109
+ if (!Array.isArray(items) || items.length === 0) {
110
+ throw new TypeError("AvZ.tuple: the items must be a non-empty list of shapes.");
111
+ }
112
+ const parts = items.map((item) => requiredPart("tuple", item));
113
+ return build({ kind: "tuple", items: parts.map((part) => part.node) }, z.tuple(parts.map((part) => part.zod)), {
114
+ type: "array",
115
+ prefixItems: parts.map((part) => part.schema),
116
+ items: false,
117
+ minItems: parts.length,
118
+ maxItems: parts.length,
119
+ });
120
+ };
121
+ const record = (key, value) => {
122
+ const keyPart = requiredPart("record", key);
123
+ const valuePart = requiredPart("record", value);
124
+ const keyNode = keyPart.node;
125
+ const keys = keyNode.kind === "enum"
126
+ ? keyNode.values
127
+ : keyNode.kind === "literal" && typeof keyNode.value === "string"
128
+ ? [keyNode.value]
129
+ : keyNode.kind === "string"
130
+ ? undefined
131
+ : null;
132
+ if (keys === null) {
133
+ throw new TypeError("AvZ.record: the key must be AvZ.string(), AvZ.enum([...]) or a string AvZ.literal(...).");
134
+ }
135
+ const schema = keys === undefined
136
+ ? { type: "object", additionalProperties: valuePart.schema }
137
+ : {
138
+ type: "object",
139
+ properties: Object.fromEntries(keys.map((entry) => [entry, valuePart.schema])),
140
+ required: [...keys],
141
+ additionalProperties: false,
142
+ };
143
+ return build({ kind: "record", value: valuePart.node }, z.record(keyPart.zod, valuePart.zod), schema);
144
+ };
145
+ const union = (options) => {
146
+ if (!Array.isArray(options) || options.length < 2) {
147
+ throw new TypeError("AvZ.union: the options must be a list of at least two shapes.");
148
+ }
149
+ const parts = options.map((option) => requiredPart("union", option));
150
+ return build({ kind: "union", options: parts.map((part) => part.node) }, z.union(parts.map((part) => part.zod)), {
151
+ anyOf: parts.map((part) => part.schema),
152
+ });
153
+ };
154
+ const discriminatedUnion = (discriminator, options) => {
155
+ if (typeof discriminator !== "string" ||
156
+ !Array.isArray(options) ||
157
+ options.length === 0) {
158
+ throw new TypeError("AvZ.discriminatedUnion: give the discriminator key and a non-empty list of object shapes.");
159
+ }
160
+ const parts = options.map((option) => requiredPart("discriminatedUnion", option));
161
+ const tags = new Map();
162
+ parts.forEach((part, index) => {
163
+ const tag = part.node.kind === "object" ? part.node.fields[discriminator] : undefined;
164
+ const values = tag?.kind === "literal"
165
+ ? [tag.value]
166
+ : tag?.kind === "enum"
167
+ ? tag.values
168
+ : undefined;
169
+ if (values === undefined) {
170
+ throw new TypeError(`AvZ.discriminatedUnion: option ${index} must be an AvZ.object whose "${discriminator}" is an AvZ.literal or an AvZ.enum.`);
171
+ }
172
+ for (const value of values) {
173
+ if (tags.has(value)) {
174
+ throw new TypeError(`AvZ.discriminatedUnion: options ${tags.get(value)} and ${index} both match "${discriminator}" ${JSON.stringify(value)}.`);
175
+ }
176
+ tags.set(value, index);
177
+ }
178
+ });
179
+ return build({
180
+ kind: "discriminatedUnion",
181
+ discriminator,
182
+ options: parts.map((part) => part.node),
183
+ }, z.discriminatedUnion(discriminator, parts.map((part) => part.zod)), {
184
+ oneOf: parts.map((part) => part.schema),
185
+ });
186
+ };
187
+ const optional = (inner) => {
188
+ const part = compiledOf("optional", inner);
189
+ return build({ kind: "optional", inner: part.node }, z.optional(part.zod), part.schema, true);
190
+ };
191
+ const nullable = (inner) => {
192
+ const part = compiledOf("nullable", inner);
193
+ return build({ kind: "nullable", inner: part.node }, z.nullable(part.zod), { anyOf: [part.schema, { type: "null" }] }, part.optionalKey);
194
+ };
195
+ export class AvZ {
196
+ constructor() { }
197
+ static string = string;
198
+ static number = number;
199
+ static int = int;
200
+ static boolean = boolean;
201
+ static null = nullShape;
202
+ static literal = literal;
203
+ static enum = enumShape;
204
+ static object = object;
205
+ static array = array;
206
+ static tuple = tuple;
207
+ static record = record;
208
+ static union = union;
209
+ static discriminatedUnion = discriminatedUnion;
210
+ static optional = optional;
211
+ static nullable = nullable;
212
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * `@aventara/core/json` — `AvZ`, which builds the TypeScript shape of a Json
3
+ * field, and the types of the shapes it builds.
4
+ */
5
+ export type { AvZNullable, AvZObject, AvZOptional, AvZShape } from "./avz.js";
6
+ export { AvZ } from "./avz.js";
@@ -0,0 +1 @@
1
+ export { AvZ } from "./avz.js";
@@ -0,0 +1,22 @@
1
+ import type { Contract } from "../contracts/contract.js";
2
+ import type { ScalarField } from "../contracts/field.js";
3
+ import type { JsonSchema } from "../contracts/json-shape.js";
4
+ import type { ValidationIssue } from "../operations/validation-code.js";
5
+ /** What core may do with a Json Shape: read its Contract form, check a value. */
6
+ export type JsonShapeRuntime = {
7
+ /** The shape's Contract form. */
8
+ readonly schema: JsonSchema;
9
+ /**
10
+ * Every way `value` — a JSON value — does not conform, each at its path inside
11
+ * the value; none when it conforms.
12
+ */
13
+ readonly validate: (value: unknown) => readonly ValidationIssue[];
14
+ };
15
+ /** Records `shape` as a Json Shape. For `@aventara/core/json` only. */
16
+ export declare function registerJsonShape(shape: object, runtime: JsonShapeRuntime): void;
17
+ /** The Json Shape `value` is, or `undefined` when `AvZ` did not build it. */
18
+ export declare function jsonShapeOf(value: unknown): JsonShapeRuntime | undefined;
19
+ /** Binds a compiled Contract field to the shape it enforces. Compiler only. */
20
+ export declare function bindShapedField(field: ScalarField<Contract>, shape: JsonShapeRuntime): void;
21
+ /** The shape a compiled Contract field enforces, or `undefined`. */
22
+ export declare function shapeOfField(field: ScalarField<Contract>): JsonShapeRuntime | undefined;
@@ -0,0 +1,16 @@
1
+ const shapes = new WeakMap();
2
+ const fields = new WeakMap();
3
+ export function registerJsonShape(shape, runtime) {
4
+ shapes.set(shape, runtime);
5
+ }
6
+ export function jsonShapeOf(value) {
7
+ return typeof value === "object" && value !== null
8
+ ? shapes.get(value)
9
+ : undefined;
10
+ }
11
+ export function bindShapedField(field, shape) {
12
+ fields.set(field, shape);
13
+ }
14
+ export function shapeOfField(field) {
15
+ return fields.get(field);
16
+ }
@@ -0,0 +1,51 @@
1
+ import type { JsonSchemaPrimitive } from "../contracts/json-shape.js";
2
+ import type { ValidationIssue } from "../operations/validation-code.js";
3
+ /** A shape as the translator reads it: its form and its parts, no Zod. */
4
+ export type AvZNode = {
5
+ readonly kind: "string" | "number" | "int" | "boolean" | "null";
6
+ } | {
7
+ readonly kind: "literal";
8
+ readonly value: JsonSchemaPrimitive;
9
+ } | {
10
+ readonly kind: "enum";
11
+ readonly values: readonly string[];
12
+ } | {
13
+ readonly kind: "object";
14
+ readonly fields: Readonly<Record<string, AvZNode>>;
15
+ } | {
16
+ readonly kind: "array";
17
+ readonly item: AvZNode;
18
+ } | {
19
+ readonly kind: "tuple";
20
+ readonly items: readonly AvZNode[];
21
+ } | {
22
+ readonly kind: "record";
23
+ readonly value: AvZNode;
24
+ } | {
25
+ readonly kind: "union";
26
+ readonly options: readonly AvZNode[];
27
+ } | {
28
+ readonly kind: "discriminatedUnion";
29
+ readonly discriminator: string;
30
+ readonly options: readonly AvZNode[];
31
+ } | {
32
+ readonly kind: "optional" | "nullable";
33
+ readonly inner: AvZNode;
34
+ };
35
+ /** The members of a Zod issue this module reads. */
36
+ type ZodIssueLike = {
37
+ readonly code: string;
38
+ readonly path: readonly PropertyKey[];
39
+ readonly origin?: string;
40
+ readonly keys?: readonly string[];
41
+ readonly note?: string;
42
+ readonly options?: readonly unknown[];
43
+ readonly minimum?: unknown;
44
+ readonly maximum?: unknown;
45
+ };
46
+ /**
47
+ * Aventara's issues for the ones Zod raised judging `value` against the shape
48
+ * `root`: each at its path inside the value, in Zod's order.
49
+ */
50
+ export declare function translateZodIssues(root: AvZNode, value: unknown, issues: readonly ZodIssueLike[]): readonly ValidationIssue[];
51
+ export {};
@@ -0,0 +1,142 @@
1
+ const MISSING = Symbol("missing");
2
+ function issue(code, path, message) {
3
+ return { code, path, message };
4
+ }
5
+ function valueAt(root, path) {
6
+ let current = root;
7
+ for (const segment of path) {
8
+ if (typeof current !== "object" ||
9
+ current === null ||
10
+ !Object.hasOwn(current, segment)) {
11
+ return MISSING;
12
+ }
13
+ current = current[segment];
14
+ }
15
+ return current;
16
+ }
17
+ function unwrap(node) {
18
+ return node.kind === "optional" || node.kind === "nullable"
19
+ ? unwrap(node.inner)
20
+ : node;
21
+ }
22
+ function selectedOption(node, value) {
23
+ const tag = valueAt(value, [node.discriminator]);
24
+ return node.options.find((option) => {
25
+ const field = option.kind === "object" ? option.fields[node.discriminator] : undefined;
26
+ const inner = field === undefined ? undefined : unwrap(field);
27
+ return inner?.kind === "literal"
28
+ ? Object.is(inner.value, tag)
29
+ : inner?.kind === "enum" && inner.values.includes(tag);
30
+ });
31
+ }
32
+ function nodeAt(root, value, path) {
33
+ let node = root;
34
+ let current = value;
35
+ for (const segment of path) {
36
+ node = node === undefined ? undefined : unwrap(node);
37
+ if (node?.kind === "discriminatedUnion") {
38
+ node = selectedOption(node, current);
39
+ }
40
+ switch (node?.kind) {
41
+ case "object":
42
+ node = node.fields[segment];
43
+ break;
44
+ case "array":
45
+ node = node.item;
46
+ break;
47
+ case "tuple":
48
+ node = typeof segment === "number" ? node.items[segment] : undefined;
49
+ break;
50
+ case "record":
51
+ node = node.value;
52
+ break;
53
+ default:
54
+ return undefined;
55
+ }
56
+ current = valueAt(current, [segment]);
57
+ }
58
+ return node;
59
+ }
60
+ function describe(node) {
61
+ if (node === undefined) {
62
+ return "the field's shape";
63
+ }
64
+ switch (node.kind) {
65
+ case "string":
66
+ return "a string";
67
+ case "number":
68
+ return "a number";
69
+ case "int":
70
+ return "an integer";
71
+ case "boolean":
72
+ return "a boolean";
73
+ case "null":
74
+ return "null";
75
+ case "literal":
76
+ return JSON.stringify(node.value);
77
+ case "enum":
78
+ return `one of ${node.values.map((value) => JSON.stringify(value)).join(", ")}`;
79
+ case "object":
80
+ case "record":
81
+ return "an object";
82
+ case "array":
83
+ return "a list";
84
+ case "tuple":
85
+ return `a list of exactly ${node.items.length} entries`;
86
+ case "union":
87
+ case "discriminatedUnion":
88
+ return [...new Set(node.options.map(describe))].join(" or ");
89
+ case "optional":
90
+ return describe(node.inner);
91
+ case "nullable":
92
+ return `${describe(node.inner)} or null`;
93
+ }
94
+ }
95
+ function pathOf(path) {
96
+ return path.map((segment) => typeof segment === "symbol" ? String(segment) : segment);
97
+ }
98
+ function translateOne(root, value, zodIssue) {
99
+ const path = pathOf(zodIssue.path);
100
+ const node = nodeAt(root, value, path);
101
+ const absent = path.length > 0 && valueAt(value, path) === MISSING;
102
+ if (absent &&
103
+ (zodIssue.code === "invalid_type" || zodIssue.code === "invalid_value")) {
104
+ return [issue("V1000", path, "Required value missing.")];
105
+ }
106
+ switch (zodIssue.code) {
107
+ case "invalid_type":
108
+ return [issue("V1001", path, `Value must be ${describe(node)}.`)];
109
+ case "invalid_value":
110
+ return [issue("V1002", path, `Value must be ${describe(node)}.`)];
111
+ case "unrecognized_keys":
112
+ return (zodIssue.keys ?? []).map((key) => issue("V1005", [...path, key], "Key is not part of the shape."));
113
+ case "invalid_union": {
114
+ if (zodIssue.note !== undefined && Array.isArray(zodIssue.options)) {
115
+ if (absent) {
116
+ return [issue("V1000", path, "Required value missing.")];
117
+ }
118
+ const allowed = zodIssue.options
119
+ .map((option) => JSON.stringify(option))
120
+ .join(", ");
121
+ return [issue("V1002", path, `Value must be one of ${allowed}.`)];
122
+ }
123
+ return [issue("V1001", path, `Value must be ${describe(node)}.`)];
124
+ }
125
+ case "too_big":
126
+ case "too_small":
127
+ if (zodIssue.origin === "array" && node !== undefined) {
128
+ return [issue("V1001", path, `Value must be ${describe(node)}.`)];
129
+ }
130
+ if (zodIssue.origin === "int" || zodIssue.origin === "number") {
131
+ return [
132
+ issue("V1004", path, "Value must be a safe integer (between -9007199254740991 and 9007199254740991)."),
133
+ ];
134
+ }
135
+ return [issue("V1001", path, `Value does not match ${describe(node)}.`)];
136
+ default:
137
+ return [issue("V1001", path, `Value does not match ${describe(node)}.`)];
138
+ }
139
+ }
140
+ export function translateZodIssues(root, value, issues) {
141
+ return issues.flatMap((zodIssue) => translateOne(root, value, zodIssue));
142
+ }