From 14cd4c007629fe3d9e569edff4c07dde981debf0 Mon Sep 17 00:00:00 2001 From: narawat Date: Tue, 24 Mar 2026 08:55:06 +0700 Subject: [PATCH] update doc --- Manifest.toml | 72 ++++++++++--------- README.md | 187 +++++++++++++++++++++++++++++++++++++++++++------- etc.jl | 56 +++++++++++++++ 3 files changed, 258 insertions(+), 57 deletions(-) create mode 100644 etc.jl diff --git a/Manifest.toml b/Manifest.toml index a23f797..0acef47 100644 --- a/Manifest.toml +++ b/Manifest.toml @@ -52,9 +52,9 @@ version = "1.2.2" [[deps.CSV]] deps = ["CodecZlib", "Dates", "FilePathsBase", "InlineStrings", "Mmap", "Parsers", "PooledArrays", "PrecompileTools", "SentinelArrays", "Tables", "Unicode", "WeakRefStrings", "WorkerUtilities"] -git-tree-sha1 = "deddd8725e5e1cc49ee205a1964256043720a6c3" +git-tree-sha1 = "8d8e0b0f350b8e1c91420b5e64e5de774c2f0f4d" uuid = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" -version = "0.10.15" +version = "0.10.16" [[deps.CodeTracking]] deps = ["InteractiveUtils", "UUIDs"] @@ -108,9 +108,9 @@ version = "1.3.0+1" [[deps.ConcurrentUtilities]] deps = ["Serialization", "Sockets"] -git-tree-sha1 = "d9d26935a0bcffc87d2613ce14c527c99fc543fd" +git-tree-sha1 = "21d088c496ea22914fe80906eb5bce65755e5ec8" uuid = "f0e56b4a-5159-44fe-b623-3e5288b988bb" -version = "2.5.0" +version = "2.5.1" [[deps.Crayons]] git-tree-sha1 = "249fe38abf76d48563e2f4556bebd215aa317e15" @@ -171,9 +171,9 @@ uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" version = "1.7.0" [[deps.EnumX]] -git-tree-sha1 = "7bebc8aad6ee6217c78c5ddcf7ed289d65d0263e" +git-tree-sha1 = "c49898e8438c828577f04b92fc9368c388ac783c" uuid = "4e289a0a-7415-4d19-859d-a7e5c4648b56" -version = "1.0.6" +version = "1.0.7" [[deps.ExceptionUnwrapping]] deps = ["Test"] @@ -234,9 +234,9 @@ version = "0.3.1" [[deps.HTTP]] deps = ["Base64", "CodecZlib", "ConcurrentUtilities", "Dates", "ExceptionUnwrapping", "Logging", "LoggingExtras", "MbedTLS", "NetworkOptions", "OpenSSL", "PrecompileTools", "Random", "SimpleBufferStream", "Sockets", "URIs", "UUIDs"] -git-tree-sha1 = "5e6fe50ae7f23d171f44e311c2960294aaa0beb5" +git-tree-sha1 = "51059d23c8bb67911a2e6fd5130229113735fc7e" uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3" -version = "1.10.19" +version = "1.11.0" [[deps.HashArrayMappedTries]] git-tree-sha1 = "2eaa69a7cab70a52b9687c8bf950a5a93ec895ae" @@ -307,9 +307,9 @@ weakdeps = ["ArrowTypes"] [[deps.JuliaInterpreter]] deps = ["CodeTracking", "InteractiveUtils", "Random", "UUIDs"] -git-tree-sha1 = "80580012d4ed5a3e8b18c7cd86cebe4b816d17a6" +git-tree-sha1 = "3d3b79166e2a0afcf875df20db110af91ad3ab61" uuid = "aa1ae85d-cabe-5617-a682-6adf51b2e16a" -version = "0.10.9" +version = "0.10.11" [[deps.JuliaSyntaxHighlighting]] deps = ["StyledStrings"] @@ -383,9 +383,9 @@ version = "1.2.0" [[deps.LoweredCodeUtils]] deps = ["CodeTracking", "Compiler", "JuliaInterpreter"] -git-tree-sha1 = "65ae3db6ab0e5b1b5f217043c558d9d1d33cc88d" +git-tree-sha1 = "5d4278f755440f70648d80cc6225f51e78e94094" uuid = "6f1432cf-f94c-5a45-995e-cdbf5db27b0b" -version = "3.5.0" +version = "3.5.1" [[deps.Lz4_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -400,9 +400,9 @@ version = "1.11.0" [[deps.MbedTLS]] deps = ["Dates", "MbedTLS_jll", "MozillaCACerts_jll", "NetworkOptions", "Random", "Sockets"] -git-tree-sha1 = "c067a280ddc25f196b5e7df3877c6b226d390aaf" +git-tree-sha1 = "8785729fa736197687541f7053f6d8ab7fc44f92" uuid = "739be429-bea8-5141-9913-cc70e7f3736d" -version = "1.1.9" +version = "1.1.10" [[deps.MbedTLS_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -437,10 +437,10 @@ uuid = "55e73f9c-eeeb-467f-b4cc-a633fde63d2a" version = "0.1.0" [[deps.NATSBridge]] -deps = ["Arrow", "DataFrames", "Dates", "GeneralUtils", "HTTP", "JSON", "NATS", "PrettyPrinting", "Revise", "UUIDs"] +deps = ["Arrow", "Base64", "DataFrames", "Dates", "GeneralUtils", "HTTP", "JSON", "NATS", "PrettyPrinting", "Revise", "UUIDs"] path = "." uuid = "f2724d33-f338-4a57-b9f8-1be882570d10" -version = "0.4.1" +version = "0.5.6" [[deps.NanoDates]] deps = ["Dates", "Parsers"] @@ -514,9 +514,9 @@ version = "1.3.3" [[deps.Preferences]] deps = ["TOML"] -git-tree-sha1 = "522f093a29b31a93e34eaea17ba055d850edea28" +git-tree-sha1 = "8b770b60760d4451834fe79dd483e318eee709c4" uuid = "21216c6a-2e73-6563-6e65-726566657250" -version = "1.5.1" +version = "1.5.2" [[deps.PrettyPrinting]] git-tree-sha1 = "142ee93724a9c5d04d78df7006670a93ed1b244e" @@ -525,9 +525,15 @@ version = "0.4.2" [[deps.PrettyTables]] deps = ["Crayons", "LaTeXStrings", "Markdown", "PrecompileTools", "Printf", "REPL", "Reexport", "StringManipulation", "Tables"] -git-tree-sha1 = "c5a07210bd060d6a8491b0ccdee2fa0235fc00bf" +git-tree-sha1 = "211530a7dc76ab59087f4d4d1fc3f086fbe87594" uuid = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d" -version = "3.1.2" +version = "3.2.3" + + [deps.PrettyTables.extensions] + PrettyTablesTypstryExt = "Typstry" + + [deps.PrettyTables.weakdeps] + Typstry = "f0ed7684-a786-439e-b1e3-3b82803b501e" [[deps.Printf]] deps = ["Unicode"] @@ -535,9 +541,9 @@ uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" version = "1.11.0" [[deps.PtrArrays]] -git-tree-sha1 = "1d36ef11a9aaf1e8b74dacc6a731dd1de8fd493d" +git-tree-sha1 = "4fbbafbc6251b883f4d2705356f3641f3652a7fe" uuid = "43287f4e-b6f4-7ad1-bb20-aadabca52c3d" -version = "1.3.0" +version = "1.4.0" [[deps.QuadGK]] deps = ["DataStructures", "LinearAlgebra"] @@ -568,9 +574,9 @@ version = "1.2.2" [[deps.Revise]] deps = ["CodeTracking", "FileWatching", "InteractiveUtils", "JuliaInterpreter", "LibGit2", "LoweredCodeUtils", "OrderedCollections", "Preferences", "REPL", "UUIDs"] -git-tree-sha1 = "14d1bfb0a30317edc77e11094607ace3c800f193" +git-tree-sha1 = "d97d78d4fc5f858d8ce44f6b88bc972f2023f51d" uuid = "295af30f-e4ad-537b-8983-00126c2a3abe" -version = "3.13.2" +version = "3.14.0" [deps.Revise.extensions] DistributedExt = "Distributed" @@ -596,9 +602,9 @@ version = "0.7.0" [[deps.ScopedValues]] deps = ["HashArrayMappedTries", "Logging"] -git-tree-sha1 = "c3b2323466378a2ba15bea4b2f73b081e022f473" +git-tree-sha1 = "ac4b837d89a58c848e85e698e2a2514e9d59d8f6" uuid = "7e506255-f358-4e82-b7e4-beb19740aa63" -version = "1.5.0" +version = "1.6.0" [[deps.Scratch]] deps = ["Dates"] @@ -644,9 +650,9 @@ version = "1.12.0" [[deps.SpecialFunctions]] deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] -git-tree-sha1 = "f2685b435df2613e25fc10ad8c26dddb8640f547" +git-tree-sha1 = "5acc6a41b3082920f79ca3c759acbcecf18a8d78" uuid = "276daf66-3868-5448-9aa4-cd146d93841b" -version = "2.6.1" +version = "2.7.1" [deps.SpecialFunctions.extensions] SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" @@ -692,9 +698,9 @@ version = "1.5.2" [[deps.StringManipulation]] deps = ["PrecompileTools"] -git-tree-sha1 = "a3c1536470bf8c5e02096ad4853606d7c8f62721" +git-tree-sha1 = "d05693d339e37d6ab134c5ab53c29fce5ee5d7d5" uuid = "892a3eda-7b42-436c-8928-eab12a02cf0e" -version = "0.4.2" +version = "0.4.4" [[deps.StringViews]] git-tree-sha1 = "f2dcb92855b31ad92fe8f079d4f75ac57c93e4b8" @@ -709,16 +715,18 @@ version = "1.11.0" [[deps.StructUtils]] deps = ["Dates", "UUIDs"] -git-tree-sha1 = "9297459be9e338e546f5c4bedb59b3b5674da7f1" +git-tree-sha1 = "fa95b3b097bcef5845c142ea2e085f1b2591e92c" uuid = "ec057cc2-7a8d-4b58-b3b3-92acb9f63b42" -version = "2.6.2" +version = "2.7.1" [deps.StructUtils.extensions] StructUtilsMeasurementsExt = ["Measurements"] + StructUtilsStaticArraysCoreExt = ["StaticArraysCore"] StructUtilsTablesExt = ["Tables"] [deps.StructUtils.weakdeps] Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7" + StaticArraysCore = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" [[deps.StyledStrings]] diff --git a/README.md b/README.md index ab0b711..42eeb9d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # NATSBridge - Cross-Platform Bi-Directional Data Bridge -A high-performance, bi-directional data bridge for **Julia**, **JavaScript**, **Python**, and **MicroPython** applications using NATS (Core & JetStream), implementing the Claim-Check pattern for large payloads. +A high-performance, bi-directional data bridge for **Julia**, **JavaScript**, **Python**, **Dart**, and **MicroPython** applications using NATS (Core & JetStream), implementing the Claim-Check pattern for large payloads. [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![NATS](https://img.shields.io/badge/NATS-Enabled-green.svg)](https://nats.io) @@ -48,33 +48,35 @@ NATSBridge enables seamless communication across multiple platforms through NATS | **JavaScript (Node.js)** | [`src/natsbridge_ssr.js`](src/natsbridge_ssr.js) | Node.js, async/await, Arrow IPC | | **JavaScript (Browser)** | [`src/natsbridge_csr.js`](src/natsbridge_csr.js) | Browser, WebSocket NATS, async/await, JSON table only | | **Python** | [`src/natsbridge.py`](src/natsbridge.py) | Desktop Python, asyncio, type hints, Arrow IPC | +| **Dart (Desktop/Flutter)** | [`src/natsbridge.dart`](src/natsbridge.dart) | Desktop/Flutter, async/await, Arrow IPC | +| **Dart Web** | [`src/natsbridge.dart`](src/natsbridge.dart) | Web, WebSocket NATS, JSON table only | | **MicroPython** | [`src/natsbridge_mpy.py`](src/natsbridge_mpy.py) | Memory-constrained, synchronous API | ### Platform Comparison -| Feature | Julia | JavaScript | JavaScript (Browser) | Python | MicroPython | -|---------|-------|------------|----------------------|--------|-------------| -| Multiple Dispatch | ✅ Native | ❌ | ❌ | ❌ | ❌ | -| Async/Await | ❌ | ✅ Native | ✅ Native | ✅ Native | ⚠️ (uasyncio) | -| Type Safety | ✅ Strong | ⚠️ (TypeScript) | ⚠️ (TypeScript) | ✅ (Type hints) | ❌ | -| Arrow IPC | ✅ Native | ✅ Native | ❌ (Browser incompatible) | ✅ Native | ❌ | -| JSON Table | ✅ | ✅ | ✅ (Only table type) | ✅ | ⚠️ (Limited) | -| Direct Transport | ✅ | ✅ | ✅ | ✅ | ✅ | -| Link Transport | ✅ | ✅ | ✅ | ✅ | ⚠️ (Limited) | -| Handler Functions | ✅ | ✅ | ✅ | ✅ | ✅ | -| Cross-Platform API | ✅ | ✅ | ✅ | ✅ | ✅ | -| WebSocket NATS | ❌ | ❌ | ✅ | ❌ | ❌ | +| Feature | Julia | JavaScript | JavaScript (Browser) | Python | Dart | Dart Web | MicroPython | +|---------|-------|------------|----------------------|--------|------|----------|-------------| +| Multiple Dispatch | ✅ Native | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| Async/Await | ❌ | ✅ Native | ✅ Native | ✅ Native | ✅ Native | ✅ Native | ⚠️ (uasyncio) | +| Type Safety | ✅ Strong | ⚠️ (TypeScript) | ⚠️ (TypeScript) | ✅ (Type hints) | ✅ Strong | ✅ Strong | ❌ | +| Arrow IPC | ✅ Native | ✅ Native | ❌ (Browser incompatible) | ✅ Native | ✅ Native | ❌ (Browser incompatible) | ❌ | +| JSON Table | ✅ | ✅ | ✅ (Only table type) | ✅ | ✅ | ✅ | ⚠️ (Limited) | +| Direct Transport | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| Link Transport | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⚠️ (Limited) | +| Handler Functions | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| Cross-Platform API | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| WebSocket NATS | ❌ | ❌ | ✅ | ❌ | ✅ | ✅ | ❌ | --- ## Features -- ✅ **Cross-platform messaging** for Julia, JavaScript, Python, and MicroPython applications +- ✅ **Cross-platform messaging** for Julia, JavaScript, Python, Dart, and MicroPython applications - ✅ **Bi-directional messaging** with request-reply patterns - ✅ **Multi-payload support** - send multiple payloads with different types in one message - ✅ **Automatic transport selection** - direct vs link based on payload size - ✅ **Claim-Check pattern** for payloads ≥ 500KB -- ✅ **Apache Arrow IPC** support for tabular data (Desktop: Julia/Python/Node.js) +- ✅ **Apache Arrow IPC** support for tabular data (Desktop: Julia/Python/Node.js/Dart) - ✅ **JSON Table** support for tabular data (All platforms including Browser) - ✅ **Exponential backoff** for reliable file server downloads - ✅ **Correlation ID tracking** for message tracing @@ -171,6 +173,38 @@ env, env_json_str = smartsend( print("Message sent!") ``` +#### Dart (Desktop/Flutter) + +```dart +import 'package:natsbridge/natsbridge.dart'; + +final data = [ + ['message', 'Hello World', 'text'] +]; +final [env, envJsonStr] = await NATSBridge.send( + '/chat/room1', + data, + brokerUrl: 'nats://localhost:4222', +); +print('Message sent!'); +``` + +#### Dart Web + +```dart +import 'package:natsbridge/natsbridge.dart'; + +final data = [ + ['message', 'Hello World', 'text'] +]; +final [env, envJsonStr] = await NATSBridge.send( + '/chat/room1', + data, + brokerUrl: 'ws://localhost:4222', // WebSocket for browser +); +print('Message sent!'); +``` + --- ## API Reference @@ -335,6 +369,54 @@ env, env_json_str = NATSBridge.smartsend( # Returns: Tuple[Dict, str] ``` +#### Dart (Desktop/Flutter) + +```dart +import 'package:natsbridge/natsbridge.dart'; + +final env, envJsonStr = await NATSBridge.send( + subject, + data, // List of [dataname, data, type] lists + brokerUrl: 'nats://localhost:4222', + fileserverUrl: 'http://localhost:8080', + fileserverUploadHandler: plikOneshotUpload, + sizeThreshold: 500000, + correlationId: uuid.v4(), + msgPurpose: 'chat', + senderName: 'NATSBridge', + receiverName: '', + receiverId: '', + replyTo: '', + replyToMsgId: '', + isPublish: true, +); +// Returns: Future> [env, env_json_str] +``` + +#### Dart Web + +```dart +import 'package:natsbridge/natsbridge.dart'; + +final env, envJsonStr = await NATSBridge.send( + subject, + data, // List of [dataname, data, type] lists + brokerUrl: 'ws://localhost:4222', // WebSocket for browser + fileserverUrl: 'http://localhost:8080', + fileserverUploadHandler: plikOneshotUpload, + sizeThreshold: 500000, + correlationId: uuid.v4(), + msgPurpose: 'chat', + senderName: 'NATSBridge', + receiverName: '', + receiverId: '', + replyTo: '', + replyToMsgId: '', + isPublish: true, +); +// Returns: Future> [env, env_json_str] +``` + ### smartreceive Receives and processes messages from NATS, handling both direct and link transport. @@ -418,20 +500,50 @@ env = NATSBridge.smartreceive( # Returns: Dict with "payloads" key ``` +#### Dart (Desktop/Flutter) + +```dart +import 'package:natsbridge/natsbridge.dart'; + +final env = await NATSBridge.receive( + msg, + fileserverDownloadHandler: fetchWithBackoff, + maxRetries: 5, + baseDelay: 100, + maxDelay: 5000, +); +// Returns: Future> with "payloads" key +``` + +#### Dart Web + +```dart +import 'package:natsbridge/natsbridge.dart'; + +final env = await NATSBridge.receive( + msg, + fileserverDownloadHandler: fetchWithBackoff, + maxRetries: 5, + baseDelay: 100, + maxDelay: 5000, +); +// Returns: Future> with "payloads" key +``` + --- ## Payload Types -| Type | Julia | JavaScript | Python | MicroPython | Description | -|------|-------|------------|--------|-------------|-------------| -| `text` | `String` | `string` | `str` | `str` | Plain text strings | -| `dictionary` | `Dict`, `NamedTuple` | `Object`, `Array` | `dict`, `list` | `dict` | JSON-serializable dictionaries | -| `arrowtable` | `DataFrame`, `Arrow.Table` | ❌ (Browser), ✅ (Node.js) | `pandas.DataFrame` | ❌ | Tabular data (Arrow IPC) | -| `jsontable` | `DataFrame`, `Vector{NamedTuple}` | `Array` | `list[dict]` | ⚠️ | Tabular data (JSON) - **Only table type in Browser** | -| `image` | `Vector{UInt8}` | `Uint8Array`, `Buffer` | `bytes` | `bytearray` | Image data (PNG, JPG) | -| `audio` | `Vector{UInt8}` | `Uint8Array`, `Buffer` | `bytes` | `bytearray` | Audio data (WAV, MP3) | -| `video` | `Vector{UInt8}` | `Uint8Array`, `Buffer` | `bytes` | `bytearray` | Video data (MP4, AVI) | -| `binary` | `Vector{UInt8}`, `IOBuffer` | `Uint8Array`, `Buffer` | `bytes`, `bytearray` | `bytearray` | Generic binary data | +| Type | Julia | JavaScript | Python | Dart | Dart Web | MicroPython | Description | +|------|-------|------------|--------|------|----------|-------------|-------------| +| `text` | `String` | `string` | `str` | `String` | `String` | `str` | Plain text strings | +| `dictionary` | `Dict`, `NamedTuple` | `Object`, `Array` | `dict`, `list` | `Map` | `Map` | `dict` | JSON-serializable dictionaries | +| `arrowtable` | `DataFrame`, `Arrow.Table` | ❌ (Browser), ✅ (Node.js) | `pandas.DataFrame` | `List` (Desktop/Flutter) | ❌ (Browser incompatible) | ❌ | Tabular data (Arrow IPC) | +| `jsontable` | `DataFrame`, `Vector{NamedTuple}` | `Array` | `list[dict]` | `List` | `List` | ⚠️ | Tabular data (JSON) - **Only table type in Browser/Dart Web** | +| `image` | `Vector{UInt8}` | `Uint8Array`, `Buffer` | `bytes` | `Uint8List` | `Uint8List` | `bytearray` | Image data (PNG, JPG) | +| `audio` | `Vector{UInt8}` | `Uint8Array`, `Buffer` | `bytes` | `Uint8List` | `Uint8List` | `bytearray` | Audio data (WAV, MP3) | +| `video` | `Vector{UInt8}` | `Uint8Array`, `Buffer` | `bytes` | `Uint8List` | `Uint8List` | `bytearray` | Video data (MP4, AVI) | +| `binary` | `Vector{UInt8}`, `IOBuffer` | `Uint8Array`, `Buffer` | `bytes`, `bytearray` | `Uint8List` | `Uint8List` | `bytearray` | Generic binary data | --- @@ -721,6 +833,7 @@ env, env_json_str = await NATSBridge.smartsend( | **Julia** | `test/test_julia_*_sender.jl` | `test/test_julia_*_receiver.jl` | | **JavaScript** | `test/test_js_*_sender.js` | `test/test_js_*_receiver.js` | | **Python** | `test/test_py_*_sender.py` | `test/test_py_*_receiver.py` | +| **Dart** | `test/test_dart_*_sender.dart` | `test/test_dart_*_receiver.dart` | ### Run Tests @@ -788,6 +901,30 @@ python3 test/test_py_table_sender.py python3 test/test_py_table_receiver.py ``` +#### Dart + +```bash +# Text message exchange +dart test/test_dart_text_sender.dart +dart test/test_dart_text_receiver.dart + +# Dictionary exchange +dart test/test_dart_dictionary_sender.dart +dart test/test_dart_dictionary_receiver.dart + +# Binary transfer +dart test/test_dart_binary_sender.dart +dart test/test_dart_binary_receiver.dart + +# Mixed payload types +dart test/test_dart_mix_payloads_sender.dart +dart test/test_dart_mix_payloads_receiver.dart + +# Table exchange +dart test/test_dart_table_sender.dart +dart test/test_dart_table_receiver.dart +``` + --- ## Browser Deployment diff --git a/etc.jl b/etc.jl new file mode 100644 index 0000000..5ca735e --- /dev/null +++ b/etc.jl @@ -0,0 +1,56 @@ +using HTTP, Arrow, JSON, DataFrames + +df = DataFrame(id = 1:10_000, val = rand(10_000)) + +file_server_url = "http://192.168.88.104:8080" +url_getUploadID = "$file_server_url/api/upload" + +function upload_to_plik(url, df) + # 1. Build the Request object manually + headers = [ + "X-Plik-TTL" => "5m", + "Content-Type" => "application/octet-stream", + "Transfer-Encoding" => "chunked" + ] + + # We create a request with an empty body, but we'll stream into it + req = HTTP.Request("POST", url, headers) + + # 2. Open the connection manually to get a raw Stream + local_url = "" + HTTP.open("POST", url, headers) do stream + # WRITE PHASE + # Arrow.write handles the 'chunked' encoding automatically + Arrow.write(stream, df; file=false) + + # CLOSE WRITE / START READ + # This is the critical hand-off. + # We tell the kernel we are done sending. + HTTP.closewrite(stream) + + # Now we wait for the server's response + resp = HTTP.startread(stream) + + # Handle the body + if resp.status == 200 || resp.status == 201 + payload = read(stream, String) + # Depending on Plik version, it might return the URL directly + # or a JSON object. Adjust accordingly: + try + local_url = JSON.parse(payload)["url"] + catch + local_url = payload # Fallback if it's a raw string + end + else + error("Plik rejected upload with status: $(resp.status)") + end + end + + return local_url +end + +url = upload_to_plik(url_getUploadID, df) + + + +