@@ -5,91 +5,140 @@ import { asType, attrsOf, validate } from "../../schema";
55import {
66 clientSchema ,
77 fromJsonBody ,
8- rejectServerOnly ,
8+ readonly ,
9+ rejectReadonly ,
10+ responseSchema ,
11+ returned ,
912 serverOnly ,
1013 wireInput ,
1114} from "./attrs" ;
1215
1316describe ( "http field attrs" , ( ) => {
1417 const shape = {
1518 id : v . string ( ) ,
16- role : serverOnly ( v . string ( ) ) ,
19+ role : readonly ( v . string ( ) ) ,
20+ password : returned ( v . string ( ) ) ,
1721 meta : v . object ( {
1822 note : v . string ( ) ,
19- secret : serverOnly ( v . string ( ) ) ,
23+ secret : readonly ( v . string ( ) ) ,
24+ hash : returned ( v . string ( ) ) ,
2025 } ) ,
2126 } ;
2227
23- it ( "serverOnly writes $attrs.http" , ( ) => {
28+ it ( "readonly and returned write $attrs.http" , ( ) => {
29+ expect ( attrsOf ( readonly ( v . string ( ) ) , "http" ) ) . toEqual ( {
30+ readonly : true ,
31+ } ) ;
32+ expect ( attrsOf ( returned ( v . string ( ) ) , "http" ) ) . toEqual ( {
33+ returned : true ,
34+ } ) ;
2435 expect ( attrsOf ( serverOnly ( v . string ( ) ) , "http" ) ) . toEqual ( {
2536 serverOnly : true ,
2637 } ) ;
2738 } ) ;
2839
29- it ( "clientSchema drops server-only fields nested" , ( ) => {
40+ it ( "clientSchema drops readonly fields nested" , ( ) => {
3041 const projected = asType ( clientSchema ( v . object ( shape ) ) ) ;
3142 expect ( Object . keys ( projected . shape as object ) . sort ( ) ) . toEqual ( [
3243 "id" ,
3344 "meta" ,
45+ "password" ,
46+ ] ) ;
47+ const meta = asType ( ( projected . shape as Record < string , unknown > ) . meta ) ;
48+ expect ( Object . keys ( meta . shape as object ) . sort ( ) ) . toEqual ( [ "hash" , "note" ] ) ;
49+ } ) ;
50+
51+ it ( "responseSchema drops returned fields nested" , ( ) => {
52+ const projected = asType ( responseSchema ( v . object ( shape ) ) ) ;
53+ expect ( Object . keys ( projected . shape as object ) . sort ( ) ) . toEqual ( [
54+ "id" ,
55+ "meta" ,
56+ "role" ,
3457 ] ) ;
3558 const meta = asType ( ( projected . shape as Record < string , unknown > ) . meta ) ;
36- expect ( Object . keys ( meta . shape as object ) ) . toEqual ( [ "note" ] ) ;
59+ expect ( Object . keys ( meta . shape as object ) . sort ( ) ) . toEqual ( [
60+ "note" ,
61+ "secret" ,
62+ ] ) ;
3763 } ) ;
3864
39- it ( "rejectServerOnly fails when a server-only key is present" , ( ) => {
65+ it ( "rejectReadonly fails when a readonly key is present" , ( ) => {
4066 expect ( ( ) =>
41- rejectServerOnly ( v . object ( shape ) , {
67+ rejectReadonly ( v . object ( shape ) , {
4268 id : "1" ,
4369 role : "admin" ,
4470 } ) ,
4571 ) . toThrow ( ValidationError ) ;
4672 expect ( ( ) =>
47- rejectServerOnly ( v . object ( shape ) , {
73+ rejectReadonly ( v . object ( shape ) , {
4874 id : "1" ,
4975 meta : { note : "hi" , secret : "x" } ,
5076 } ) ,
51- ) . toThrow ( / s e r v e r - o n l y / ) ;
77+ ) . toThrow ( / r e a d o n l y f i e l d / ) ;
5278 } ) ;
5379
54- it ( "wireInput accepts client fields and rejects smuggled server-only " , ( ) => {
80+ it ( "wireInput accepts client fields and rejects smuggled readonly " , ( ) => {
5581 expect (
5682 wireInput ( v . object ( shape ) , {
5783 id : "1" ,
58- meta : { note : "hi" } ,
84+ password : "secret" ,
85+ meta : { note : "hi" , hash : "h" } ,
5986 } ) ,
60- ) . toEqual ( { id : "1" , meta : { note : "hi" } } ) ;
87+ ) . toEqual ( {
88+ id : "1" ,
89+ password : "secret" ,
90+ meta : { note : "hi" , hash : "h" } ,
91+ } ) ;
6192 expect ( ( ) =>
6293 wireInput ( v . object ( shape ) , { id : "1" , role : "admin" } ) ,
6394 ) . toThrow ( ValidationError ) ;
6495 } ) ;
6596
66- it ( "in-process validate still accepts server-only fields" , ( ) => {
97+ it ( "serverOnly still gates the wire like readonly" , ( ) => {
98+ const schema = v . object ( {
99+ id : v . string ( ) ,
100+ role : serverOnly ( v . string ( ) ) ,
101+ } ) ;
102+ expect ( ( ) => wireInput ( schema , { id : "1" , role : "admin" } ) ) . toThrow (
103+ / r e a d o n l y f i e l d / ,
104+ ) ;
105+ expect ( wireInput ( schema , { id : "1" } ) ) . toEqual ( { id : "1" } ) ;
106+ } ) ;
107+
108+ it ( "in-process validate still accepts readonly fields" , ( ) => {
67109 expect (
68110 validate (
69111 asType ( v . object ( shape ) ) ,
70112 {
71113 id : "1" ,
72114 role : "admin" ,
73- meta : { note : "n" , secret : "s" } ,
115+ password : "p" ,
116+ meta : { note : "n" , secret : "s" , hash : "h" } ,
74117 } ,
75118 "user" ,
76119 ) ,
77120 ) . toEqual ( {
78121 id : "1" ,
79122 role : "admin" ,
80- meta : { note : "n" , secret : "s" } ,
123+ password : "p" ,
124+ meta : { note : "n" , secret : "s" , hash : "h" } ,
81125 } ) ;
82126 } ) ;
83127
84128 it ( "fromJsonBody runs wireInput on the request body" , async ( ) => {
85129 const request = new Request ( "https://example.com" , {
86130 method : "POST" ,
87131 headers : { "content-type" : "application/json" } ,
88- body : JSON . stringify ( { id : "1" , meta : { note : "ok" } } ) ,
132+ body : JSON . stringify ( {
133+ id : "1" ,
134+ password : "p" ,
135+ meta : { note : "ok" , hash : "h" } ,
136+ } ) ,
89137 } ) ;
90138 await expect ( fromJsonBody ( request , v . object ( shape ) ) ) . resolves . toEqual ( {
91139 id : "1" ,
92- meta : { note : "ok" } ,
140+ password : "p" ,
141+ meta : { note : "ok" , hash : "h" } ,
93142 } ) ;
94143
95144 const smuggle = new Request ( "https://example.com" , {
@@ -111,12 +160,12 @@ describe("http field attrs", () => {
111160 ) ;
112161 } ) ;
113162
114- it ( "clientSchema and rejectServerOnly recurse into union arms" , ( ) => {
163+ it ( "clientSchema and rejectReadonly recurse into union arms" , ( ) => {
115164 const schema = v . union ( [
116165 v . object ( {
117166 kind : v . string ( { enum : [ "user" ] } ) ,
118167 id : v . string ( ) ,
119- role : serverOnly ( v . string ( ) ) ,
168+ role : readonly ( v . string ( ) ) ,
120169 } ) ,
121170 v . object ( {
122171 kind : v . string ( { enum : [ "anon" ] } ) ,
@@ -136,8 +185,8 @@ describe("http field attrs", () => {
136185 ] ) ;
137186
138187 expect ( ( ) =>
139- rejectServerOnly ( schema , { kind : "user" , id : "1" , role : "admin" } ) ,
140- ) . toThrow ( / s e r v e r - o n l y / ) ;
188+ rejectReadonly ( schema , { kind : "user" , id : "1" , role : "admin" } ) ,
189+ ) . toThrow ( / r e a d o n l y f i e l d / ) ;
141190 expect ( wireInput ( schema , { kind : "anon" , token : "t" } ) ) . toEqual ( {
142191 kind : "anon" ,
143192 token : "t" ,
0 commit comments