Skip to content

Commit 5cc07ae

Browse files
committed
Architecture update
1 parent 1d5f1a1 commit 5cc07ae

File tree

1 file changed

+74
-26
lines changed

1 file changed

+74
-26
lines changed

AGENTS.md

Lines changed: 74 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -21,50 +21,98 @@ When responding:
2121

2222
# Sury Architecture
2323

24-
A schema is a representation of TWO types. Input and output
24+
## Schema Input and Output Types
2525

26-
1. S.string - Sometimes input and output are the same
26+
A schema represents two types: Input and Output.
2727

28-
- Input: string
29-
- Output: string
28+
### Example 1: Same Input and Output
3029

31-
2. S.schema({
32-
foo: S.string.with(S.to, S.number)
33-
}) - In this case, the input and output are different, even though the S.schema won't have .to property itself
34-
- Input: { foo: string }
35-
- Output: { foo: number }
30+
```typescript
31+
S.string
32+
// Input: string
33+
// Output: string
34+
```
3635

37-
When we modify a schema, we modify the output type.
36+
### Example 2: Different Input and Output
3837

39-
```ts
38+
```typescript
39+
S.schema({
40+
foo: S.string.with(S.to, S.number)
41+
})
42+
// Input: { foo: string }
43+
// Output: { foo: number }
44+
```
45+
46+
The input and output differ because nested items have transformations, even though the schema itself does not have a `.to` property.
47+
48+
## Modifying a Schema
49+
50+
When modifying a schema, the modification applies to the output type.
51+
52+
```typescript
4053
S.schema({
4154
foo: S.string.with(S.to, S.number)
4255
}).with(S.refine, () => {...})
4356
```
4457

45-
Since the case doesn't have .to, we MUST deffirentiate between input and output refines to support `S.reverse` - Every schema should be able to be reversed from Input->Output to Output->Input, unless it's explicitly prevented.
58+
Since this schema does not have `.to`, input and output refiners must be stored separately to support `S.reverse`. Every schema should be reversible from Input→Output to Output→Input, unless explicitly prevented.
59+
60+
For modifications like `name` or built-in refinements that do not affect nested items, they apply to both input and output without differentiation.
61+
62+
## Decode Function
63+
64+
The decode function is created from a single schema and transforms the schema's Input to Output. When multiple schemas are joined by the `.to` property, they are automatically combined into a single transformation pipeline.
65+
66+
## Schema Properties and Execution Order
67+
68+
Schema properties are executed in the following order:
69+
70+
1. **decoder** - If input val differs from the schema, decode it to the schema's input type. May skip directly to schema output if there is no inputRefiner.
71+
72+
2. **inputRefiner** - Custom validations on the input part of the schema value.
73+
74+
3. **decoder** - Decodes input to output for the current schema. Typically required to decode nested items such as object fields.
75+
76+
4. **outputRefiner** - Custom validations on the output part of the schema value.
77+
78+
### If Schema Has `.to` Property
79+
80+
5. **parser** - Custom transformation logic to the `.to` schema. The serializer is the reverse of parser.
81+
82+
### If There Is No Parser
83+
84+
5. **encoder** - Transformation logic from the current schema's output to the `.to` schema's input.
85+
86+
6. **.to.decoder** - Starts the cycle from the beginning with the `.to` schema.
4687

47-
We should also try to store every data-point on schema to be able to use them to compile a decode function.
88+
## Reversal with S.reverse
4889

49-
The decode function should be created from a single schema and must transform schema input to output. For multiple schemas it automatically joins them by .to property and turns into a single one.
90+
`S.reverse` swaps:
5091

51-
This makes schema to have the following properties and run them in order:
92+
- `inputRefiner``outputRefiner`
93+
- `parser``serializer`
94+
- Reverses the `.to` chain direction
5295

53-
- inputRefiner - Custom validations to the input part of the schema value
54-
- innerDecoder - Decoding of inner items like object fields
55-
- outputRefiner - Custom validations to the output part of the schema value
96+
## Async Support
5697

57-
If schema has .to property:
98+
Every transformation may return an async value. To continue the transformation chain:
5899

59-
- parser - Custom transformation logic to the .to schema (serializer is a reverse of parser)
100+
1. Append `.then()` and continue the logic in the callback function.
101+
2. For nested items (e.g., object fields, array items), create a promise that collects all inner items with `Promise.all()`.
60102

61-
And if there's no .parser:
103+
## Val
62104

63-
- encoder - Transformation logic from the current schema to the .to schema
64-
- decoder - Transformation logic of the .to schema from any other schema
105+
The `val` represents a value at a specific point in the transformation pipeline during code generation.
65106

66-
After the step either finish the decode function or continue with the inputRefiner.
107+
Key properties:
67108

68-
Additionally for async support we should be aware of that every transformation might return an async value, so to continue the transformation chain we need to append .then and continue the logic in the callback function. For innerDecoder it should create a promise which collects all inner items.
109+
- `schema` - The actual type of the value
110+
- `expected` - The schema of decoder
111+
- `var` - Returns the variable name in generated code
112+
- `inline` - The value as an inline code expression
113+
- `code` - Accumulated generated code (used by all transformation steps)
114+
- `validation` - Built-in type check condition for the decoder (e.g., `typeof x === "string"`). Different from custom refiners.
115+
- `from` - The previous val in the chain (tracks transformation history)
116+
- `path` - Current location in the data structure (for error messages)
69117

70-
Every transformation point is connected by a val
118+
The decoder uses `schema` vs `expected` to skip unnecessary validations when the actual type is already compatible.

0 commit comments

Comments
 (0)