fastify-ata 0.5.0 → 0.6.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.
package/README.md CHANGED
@@ -75,6 +75,33 @@ app.post('/user', {
75
75
 
76
76
  `ata-validator` falls back to a pure-JS engine where the native addon is not available (Cloudflare Workers, browsers, Bun), so fastify-ata runs in those environments too.
77
77
 
78
+ ### Chainable authoring (TypeBox-style)
79
+
80
+ If you prefer a chainable builder over JSON Schema literals, `ata-validator/t` emits the same plain JSON Schema under the hood, so route schemas, the type provider, and the AOT path all keep working without an adapter. The migration from TypeBox is one import rename:
81
+
82
+ ```ts
83
+ import Fastify from 'fastify'
84
+ import fastifyAta from 'fastify-ata'
85
+ import { t } from 'ata-validator/t'
86
+
87
+ const app = Fastify().withTypeProvider<fastifyAta.AtaTypeProvider>()
88
+ await app.register(fastifyAta)
89
+
90
+ const Body = t.object({
91
+ name: t.string({ minLength: 1 }),
92
+ age: t.integer({ minimum: 0 }),
93
+ email: t.optional(t.string({ format: 'email' })),
94
+ role: t.union([t.literal('admin'), t.literal('user')]),
95
+ })
96
+
97
+ app.post('/users', { schema: { body: Body } }, (req, reply) => {
98
+ req.body.name // string
99
+ req.body.email // string | undefined
100
+ req.body.role // 'admin' | 'user'
101
+ reply.send({ ok: true })
102
+ })
103
+ ```
104
+
78
105
  ## Options
79
106
 
80
107
  ```js
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "fastify-ata",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Fastify plugin for ata-validator. Runtime-competitive with the default, 24x faster serverless cold start, Standard Schema V1.",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
7
7
  "scripts": {
8
- "test": "node test.js && node test-pretty-errors.js && node test-standalone.js && node test-compiler.js && node test-type-provider.js && tsc -p tsconfig.types.json"
8
+ "test": "node test.js && node test-pretty-errors.js && node test-standalone.js && node test-compiler.js && node test-type-provider.js && node test-t-builder.js && tsc -p tsconfig.types.json"
9
9
  },
10
10
  "repository": {
11
11
  "type": "git",
@@ -26,7 +26,7 @@
26
26
  },
27
27
  "homepage": "https://github.com/ata-core/fastify-ata#readme",
28
28
  "dependencies": {
29
- "ata-validator": "^0.18.0",
29
+ "ata-validator": "^0.20.0",
30
30
  "fastify-plugin": "^5.1.0",
31
31
  "sanitize-filename": "^1.6.4"
32
32
  },
@@ -34,6 +34,8 @@
34
34
  "fastify": ">=4.0.0"
35
35
  },
36
36
  "devDependencies": {
37
+ "@fastify/type-provider-typebox": "^6.1.0",
38
+ "@sinclair/typebox": "^0.34.49",
37
39
  "@types/node": "^25.9.1",
38
40
  "autocannon": "^8.0.0",
39
41
  "fastify": "^5.8.4",
@@ -0,0 +1,82 @@
1
+ 'use strict'
2
+
3
+ // Verify that route schemas authored with `ata-validator/t` flow through
4
+ // fastify-ata exactly like plain JSON Schema literals do. The builder is a
5
+ // pure emitter (no runtime adapter), so the validator compiler should not
6
+ // notice the difference; this test pins that invariant so a future bump
7
+ // of ata-validator cannot silently break the TypeBox-style migration path.
8
+
9
+ const fastify = require('fastify')
10
+ const fastifyAta = require('./index')
11
+ const { t } = require('ata-validator/t')
12
+
13
+ let pass = 0
14
+ let fail = 0
15
+ const assert = (cond, msg) => {
16
+ if (cond) { pass++; console.log(` PASS ${msg}`) }
17
+ else { fail++; console.log(` FAIL ${msg}`) }
18
+ }
19
+
20
+ async function run () {
21
+ console.log('\nfastify-ata + ata-validator/t\n')
22
+
23
+ const app = fastify()
24
+ await app.register(fastifyAta)
25
+
26
+ const Body = t.object({
27
+ name: t.string({ minLength: 1 }),
28
+ age: t.integer({ minimum: 0 }),
29
+ email: t.optional(t.string({ format: 'email' })),
30
+ role: t.union([t.literal('admin'), t.literal('user')]),
31
+ })
32
+
33
+ app.post('/users', { schema: { body: Body } }, (req, reply) => {
34
+ reply.send({ ok: true, name: req.body.name, role: req.body.role })
35
+ })
36
+
37
+ await app.ready()
38
+ assert(true, 'route with t-builder schema registers')
39
+
40
+ const ok = await app.inject({
41
+ method: 'POST',
42
+ url: '/users',
43
+ payload: { name: 'Mert', age: 30, role: 'admin' },
44
+ })
45
+ assert(ok.statusCode === 200, 'valid body returns 200')
46
+ assert(JSON.parse(ok.payload).name === 'Mert', 'request body reachable in handler')
47
+
48
+ const optionalOk = await app.inject({
49
+ method: 'POST',
50
+ url: '/users',
51
+ payload: { name: 'Mert', age: 30, email: 'm@example.com', role: 'user' },
52
+ })
53
+ assert(optionalOk.statusCode === 200, 'optional field accepted when present')
54
+
55
+ const missingRequired = await app.inject({
56
+ method: 'POST',
57
+ url: '/users',
58
+ payload: { age: 30, role: 'user' },
59
+ })
60
+ assert(missingRequired.statusCode === 400, 'missing required field rejected')
61
+
62
+ const wrongUnion = await app.inject({
63
+ method: 'POST',
64
+ url: '/users',
65
+ payload: { name: 'Mert', age: 30, role: 'superuser' },
66
+ })
67
+ assert(wrongUnion.statusCode === 400, 'value outside union rejected')
68
+
69
+ const negativeAge = await app.inject({
70
+ method: 'POST',
71
+ url: '/users',
72
+ payload: { name: 'Mert', age: -1, role: 'user' },
73
+ })
74
+ assert(negativeAge.statusCode === 400, 'numeric bound rejected')
75
+
76
+ await app.close()
77
+
78
+ console.log(`\n${pass} passed, ${fail} failed`)
79
+ process.exit(fail > 0 ? 1 : 0)
80
+ }
81
+
82
+ run().catch((e) => { console.error(e); process.exit(1) })