+2
-2
hermes-cli/lib/codegen.ml
+2
-2
hermes-cli/lib/codegen.ml
···
423
423
(* generate output type *)
424
424
( if output_is_bytes then begin
425
425
emitln out " (** raw bytes output with content type *)" ;
426
-
emitln out " type output = string * string" ;
426
+
emitln out " type output = bytes * string" ;
427
427
emit_newline out
428
428
end
429
429
else
···
564
564
(* generate output type *)
565
565
( if output_is_bytes then begin
566
566
emitln out " (** raw bytes output with content type *)" ;
567
-
emitln out " type output = (string * string) option" ;
567
+
emitln out " type output = (bytes * string) option" ;
568
568
emit_newline out
569
569
end
570
570
else
+3
-3
hermes-cli/test/test_codegen.ml
+3
-3
hermes-cli/test/test_codegen.ml
···
110
110
[make_def "result" (Lexicon_types.Union union_spec)]
111
111
in
112
112
let code = Codegen.gen_lexicon_module doc in
113
-
check bool "contains type result" true (contains code "type result =") ;
113
+
check bool "contains type result" true (contains code "type result_ =") ;
114
114
check bool "contains TypeA variant" true (contains code "| TypeA of") ;
115
115
check bool "contains TypeB variant" true (contains code "| TypeB of") ;
116
116
check bool "contains Unknown (open)" true
···
452
452
in
453
453
let code = Codegen.gen_lexicon_module doc in
454
454
check bool "contains module Main" true (contains code "module Main = struct") ;
455
-
check bool "output is string * string tuple" true
456
-
(contains code "type output = string * string") ;
455
+
check bool "output is bytes * string tuple" true
456
+
(contains code "type output = bytes * string") ;
457
457
check bool "calls Hermes.query_bytes" true
458
458
(contains code "Hermes.query_bytes")
459
459
+1
-1
hermes/README.md
+1
-1
hermes/README.md
···
281
281
Endpoints with non-JSON encoding are automatically detected and handled:
282
282
283
283
- **Queries with bytes output** (e.g., `com.atproto.sync.getBlob` with `encoding: "*/*"`):
284
-
- Output type is `string * string` (data, content_type)
284
+
- Output type is `bytes * string` (data, content_type)
285
285
- Generated code uses `Hermes.query_bytes`
286
286
287
287
- **Procedures with bytes input**:
+12
-10
hermes/lib/client.ml
+12
-10
hermes/lib/client.ml
···
36
36
-> (Yojson.Safe.t -> ('a, string) result)
37
37
-> 'a Lwt.t
38
38
39
-
val query_bytes : t -> string -> Yojson.Safe.t -> (string * string) Lwt.t
39
+
val query_bytes : t -> string -> Yojson.Safe.t -> (bytes * string) Lwt.t
40
40
41
41
val procedure_bytes :
42
42
t
43
43
-> string
44
44
-> Yojson.Safe.t
45
-
-> string option
45
+
-> bytes option
46
46
-> content_type:string
47
-
-> (string * string) option Lwt.t
47
+
-> (bytes * string) option Lwt.t
48
48
49
49
val procedure_blob :
50
50
t
···
229
229
Types.raise_xrpc_error ~status payload
230
230
231
231
let query_bytes (t : t) (nsid : string) (params : Yojson.Safe.t) :
232
-
(string * string) Lwt.t =
232
+
(bytes * string) Lwt.t =
233
233
(* call interceptor if present for token refresh *)
234
234
let* () =
235
235
match t.on_request with Some f -> f t | None -> Lwt.return_unit
···
249
249
in
250
250
let status = Cohttp.Response.status resp |> Cohttp.Code.code_of_status in
251
251
let* body_str = Cohttp_lwt.Body.to_string body in
252
+
let body = Bytes.of_string body_str in
252
253
if status >= 200 && status < 300 then
253
254
let content_type =
254
255
Cohttp.Response.headers resp
···
256
257
Cohttp.Header.get h "content-type"
257
258
|> Option.value ~default:"application/octet-stream"
258
259
in
259
-
Lwt.return (body_str, content_type)
260
+
Lwt.return (body, content_type)
260
261
else
261
262
let payload =
262
263
try
···
272
273
273
274
(* execute procedure with raw bytes input, returns raw bytes or none if no output *)
274
275
let procedure_bytes (t : t) (nsid : string) (params : Yojson.Safe.t)
275
-
(input : string option) ~(content_type : string) :
276
-
(string * string) option Lwt.t =
276
+
(input : bytes option) ~(content_type : string) :
277
+
(bytes * string) option Lwt.t =
277
278
(* call interceptor if present for token refresh *)
278
279
let* () =
279
280
match t.on_request with Some f -> f t | None -> Lwt.return_unit
···
286
287
let body =
287
288
match input with
288
289
| Some data ->
289
-
Cohttp_lwt.Body.of_string data
290
+
Cohttp_lwt.Body.of_string (Bytes.to_string data)
290
291
| None ->
291
292
Cohttp_lwt.Body.empty
292
293
in
···
303
304
in
304
305
let status = Cohttp.Response.status resp |> Cohttp.Code.code_of_status in
305
306
let* body_str = Cohttp_lwt.Body.to_string resp_body in
307
+
let body = Bytes.of_string body_str in
306
308
if status >= 200 && status < 300 then
307
-
if String.length body_str = 0 then Lwt.return None
309
+
if Bytes.length body = 0 then Lwt.return None
308
310
else
309
311
let resp_content_type =
310
312
Cohttp.Response.headers resp
···
312
314
Cohttp.Header.get h "content-type"
313
315
|> Option.value ~default:"application/octet-stream"
314
316
in
315
-
Lwt.return (Some (body_str, resp_content_type))
317
+
Lwt.return (Some (body, resp_content_type))
316
318
else
317
319
let payload =
318
320
try
+6
-6
hermes/lib/hermes.mli
+6
-6
hermes/lib/hermes.mli
···
77
77
-> (Yojson.Safe.t -> ('a, string) result)
78
78
-> 'a Lwt.t
79
79
80
-
val query_bytes : client -> string -> Yojson.Safe.t -> (string * string) Lwt.t
80
+
val query_bytes : client -> string -> Yojson.Safe.t -> (bytes * string) Lwt.t
81
81
82
82
val procedure_bytes :
83
83
client
84
84
-> string
85
85
-> Yojson.Safe.t
86
-
-> string option
86
+
-> bytes option
87
87
-> content_type:string
88
-
-> (string * string) option Lwt.t
88
+
-> (bytes * string) option Lwt.t
89
89
90
90
val session_to_yojson : session -> Yojson.Safe.t
91
91
···
158
158
-> (Yojson.Safe.t -> ('a, string) result)
159
159
-> 'a Lwt.t
160
160
161
-
val query_bytes : t -> string -> Yojson.Safe.t -> (string * string) Lwt.t
161
+
val query_bytes : t -> string -> Yojson.Safe.t -> (bytes * string) Lwt.t
162
162
163
163
val procedure_bytes :
164
164
t
165
165
-> string
166
166
-> Yojson.Safe.t
167
-
-> string option
167
+
-> bytes option
168
168
-> content_type:string
169
-
-> (string * string) option Lwt.t
169
+
-> (bytes * string) option Lwt.t
170
170
171
171
val procedure_blob :
172
172
t
+9
-3
hermes/test/test_client.ml
+9
-3
hermes/test/test_client.ml
···
7
7
(* helpers *)
8
8
let test_string = testable Fmt.string String.equal
9
9
10
+
let test_bytes =
11
+
testable
12
+
(Fmt.of_to_string (fun b -> String.sub (Bytes.to_string b) 0 10))
13
+
Bytes.equal
14
+
10
15
(** query tests *)
11
16
12
17
let test_query_success () =
···
105
110
C.query_bytes client "com.atproto.sync.getBlob"
106
111
(`Assoc [("did", `String "did:plc:123"); ("cid", `String "bafyabc")]) )
107
112
in
108
-
check test_string "data" "fake-image-data" data ;
113
+
check test_bytes "data" (Bytes.of_string "fake-image-data") data ;
109
114
check test_string "content_type" "image/jpeg" content_type ;
110
115
let req = List.hd requests in
111
116
Test_utils.assert_request_has_header "accept" "*/*" req ;
···
163
168
let* result, requests =
164
169
Test_utils.with_mock_responses [response] (fun (module C) client ->
165
170
C.procedure_bytes client "com.atproto.repo.importRepo" (`Assoc [])
166
-
(Some "fake-car-data") ~content_type:"application/vnd.ipld.car" )
171
+
(Some (Bytes.of_string "fake-car-data"))
172
+
~content_type:"application/vnd.ipld.car" )
167
173
in
168
-
check (option (pair test_string test_string)) "result" None result ;
174
+
check (option (pair test_bytes test_string)) "result" None result ;
169
175
let req = List.hd requests in
170
176
Test_utils.assert_request_has_header "content-type" "application/vnd.ipld.car"
171
177
req ;
+5
-5
hermes_ppx/test/test_ppx.ml
+5
-5
hermes_ppx/test/test_ppx.ml
···
19
19
check (list string) "single segment" ["Test"] result
20
20
21
21
let test_build_call_expr () =
22
-
let result = Hermes_ppx.build_call_expr ~loc "app.bsky.graph.getProfile" in
23
-
let expected_str = "App.Bsky.Graph.GetProfile.call" in
22
+
let result = Hermes_ppx.build_call_expr ~loc "app.bsky.actor.getProfile" in
23
+
let expected_str = "App.Bsky.Actor.GetProfile.Main.call" in
24
24
check string "call expr" expected_str
25
25
(Ppxlib.Pprintast.string_of_expression result)
26
26
···
38
38
39
39
let test_expand_get_nsid () =
40
40
let actual = expand_xrpc {|[%xrpc get "app.bsky.graph.getRelationships"]|} in
41
-
let expected_str = "App.Bsky.Graph.GetRelationships.call" in
41
+
let expected_str = "App.Bsky.Graph.GetRelationships.Main.call" in
42
42
check string "get expansion" expected_str
43
43
(Ppxlib.Pprintast.string_of_expression actual)
44
44
···
46
46
let actual =
47
47
expand_xrpc {|[%xrpc post "com.atproto.server.createSession"]|}
48
48
in
49
-
let expected_str = "Com.Atproto.Server.CreateSession.call" in
49
+
let expected_str = "Com.Atproto.Server.CreateSession.Main.call" in
50
50
check string "post expansion" expected_str
51
51
(Ppxlib.Pprintast.string_of_expression actual)
52
52
53
53
let test_expand_nsid_only () =
54
54
(* [%xrpc "nsid"] defaults to get *)
55
55
let actual = expand_xrpc {|[%xrpc "app.bsky.actor.getProfile"]|} in
56
-
let expected_str = "App.Bsky.Actor.GetProfile.call" in
56
+
let expected_str = "App.Bsky.Actor.GetProfile.Main.call" in
57
57
check string "nsid only expansion" expected_str
58
58
(Ppxlib.Pprintast.string_of_expression actual)
59
59
+1
-1
pegasus/lexicons/com_atproto_sync_getBlob.ml
+1
-1
pegasus/lexicons/com_atproto_sync_getBlob.ml
+1
-1
pegasus/lexicons/com_atproto_sync_getBlocks.ml
+1
-1
pegasus/lexicons/com_atproto_sync_getBlocks.ml
+1
-1
pegasus/lexicons/com_atproto_sync_getCheckout.ml
+1
-1
pegasus/lexicons/com_atproto_sync_getCheckout.ml
+1
-1
pegasus/lexicons/com_atproto_sync_getRecord.ml
+1
-1
pegasus/lexicons/com_atproto_sync_getRecord.ml
+1
-1
pegasus/lexicons/com_atproto_sync_getRepo.ml
+1
-1
pegasus/lexicons/com_atproto_sync_getRepo.ml
+8
-18
pegasus/lib/api/account_/migrate/migrate.ml
+8
-18
pegasus/lib/api/account_/migrate/migrate.ml
···
417
417
match%lwt Remote.fetch_repo old_client ~did with
418
418
| Error err ->
419
419
render_err ~did ~handle ~old_pds ("Failed to fetch repository: " ^ err)
420
-
| Ok car_data -> (
421
-
match%lwt
422
-
Ops.import_repo ~did ~car_data:(Bytes.of_string (fst car_data))
423
-
with
420
+
| Ok (car_data, _) -> (
421
+
match%lwt Ops.import_repo ~did ~car_data with
424
422
| Error err ->
425
423
render_err ~did ~handle ~old_pds err
426
424
| Ok () ->
···
432
430
match%lwt Remote.fetch_repo old_client ~did with
433
431
| Error e ->
434
432
render_err ("Failed to fetch repository: " ^ e)
435
-
| Ok car_data -> (
436
-
match%lwt
437
-
Ops.import_repo ~did ~car_data:(Bytes.of_string (fst car_data))
438
-
with
433
+
| Ok (car_data, _) -> (
434
+
match%lwt Ops.import_repo ~did ~car_data with
439
435
| Error e ->
440
436
render_err e
441
437
| Ok () -> (
···
784
780
match%lwt Remote.fetch_repo client ~did with
785
781
| Error e ->
786
782
render_err ("Failed to fetch repository: " ^ e)
787
-
| Ok car_data -> (
788
-
match%lwt
789
-
Ops.import_repo ~did
790
-
~car_data:(Bytes.of_string @@ fst car_data)
791
-
with
783
+
| Ok (car_data, _) -> (
784
+
match%lwt Ops.import_repo ~did ~car_data with
792
785
| Error e ->
793
786
render_err e
794
787
| Ok () ->
···
885
878
| Error e ->
886
879
render_err ~did ~handle ~old_pds
887
880
("Failed to fetch repository: " ^ e)
888
-
| Ok car_data -> (
889
-
match%lwt
890
-
Ops.import_repo ~did
891
-
~car_data:(Bytes.of_string @@ fst car_data)
892
-
with
881
+
| Ok (car_data, _) -> (
882
+
match%lwt Ops.import_repo ~did ~car_data with
893
883
| Error e ->
894
884
render_err ~did ~handle ~old_pds e
895
885
| Ok () -> (
+1
-3
pegasus/lib/api/account_/migrate/ops.ml
+1
-3
pegasus/lib/api/account_/migrate/ops.ml