diff --git a/AI_prompt.txt b/AI_prompt.txt index 3595c6c..5120027 100644 --- a/AI_prompt.txt +++ b/AI_prompt.txt @@ -33,3 +33,21 @@ All API should be semantically consistent and naming should be consistent across + + + + + +Task: Update NATSBridge.js to reflect recent changes in NATSBridge.jl and docs + +Context: NATSBridge.jl and docs has been updated. + +Requirements: + +Source of Truth: Treat the updated NATSBridge.jl and docs as the definitive source. +API Consistency: Ensure the Main Package API (e.g., smartsend(), publish_message()) uses consistent naming across all three supported languages. +Ecosystem Variance: Low-level native functions (e.g., NATS.connect(), JSON.read()) should follow the conventions of the specific language ecosystem and do not require cross-language consistency. + + + + diff --git a/docs/architecture.md b/docs/architecture.md index 3275141..4a5f29b 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -388,6 +388,12 @@ graph TD - **Julia:** Uses `NATS_connection` keyword parameter with function overloading - **JavaScript/Python:** Achieved by creating NATS client outside the function and reusing it in custom handlers +**Note on `is_publish`:** +- `is_publish` is simply a switch to control automatic publishing +- When `true` (default): Message is published to NATS automatically +- When `false`: Returns `(env, env_json_str)` without publishing, allowing manual publishing +- Connection reuse is achieved separately by creating NATS client outside the function + ### Julia Implementation #### Dependencies diff --git a/docs/implementation.md b/docs/implementation.md index 8da6ce3..304877b 100644 --- a/docs/implementation.md +++ b/docs/implementation.md @@ -338,6 +338,7 @@ node test/scenario3_julia_to_julia.js **Why the Difference?** - Julia supports function overloading and keyword arguments, allowing `NATS_connection` to be passed as an optional parameter - JavaScript/Python use a simpler `is_publish` option to control automatic publishing +- `is_publish` is simply a switch: when `true`, publish automatically; when `false`, return `(env, env_json_str)` without publishing - For connection reuse in JavaScript/Python, create a NATS client once and reuse it in your custom `fileserver_upload_handler` or custom publish logic ## Usage diff --git a/src/NATSBridge.js b/src/NATSBridge.js index 64b1c91..2bb776d 100644 --- a/src/NATSBridge.js +++ b/src/NATSBridge.js @@ -9,16 +9,16 @@ * File Server Handler Architecture: * The system uses handler functions to abstract file server operations, allowing support * for different file server implementations (e.g., Plik, AWS S3, custom HTTP server). - * + * * Handler Function Signatures: - * + * * ```javascript * // Upload handler - uploads data to file server and returns URL * // The handler is passed to smartsend as fileserverUploadHandler parameter * // It receives: (fileserver_url, dataname, data) * // Returns: { status, uploadid, fileid, url } - * async function fileserverUploadHandler(fileserver_url, dataname, data) { ... } - * + * async function plik_oneshot_upload(fileserver_url, dataname, data) { ... } + * * // Download handler - fetches data from file server URL with exponential backoff * // The handler is passed to smartreceive as fileserverDownloadHandler parameter * // It receives: (url, max_retries, base_delay, max_delay, correlation_id) @@ -213,73 +213,16 @@ function _deserialize_data(data, type, correlation_id) { } // Helper: Upload data to file server +// Internal wrapper that adds correlation_id logging for smartsend async function _upload_to_fileserver(fileserver_url, dataname, data, correlation_id) { /** - * Upload data to HTTP file server (plik-like API) - * - * This function implements the plik one-shot upload mode: - * 1. Creates a one-shot upload session by sending POST request with {"OneShot": true} - * 2. Uploads the file data as multipart form data - * 3. Returns identifiers and download URL for the uploaded file + * Internal upload helper - wraps plik_oneshot_upload to add correlation_id logging + * This allows smartsend to pass correlation_id for tracing without changing the handler signature */ log_trace(correlation_id, `Uploading ${dataname} to fileserver: ${fileserver_url}`); - - // Step 1: Get upload ID and token - const url_getUploadID = `${fileserver_url}/upload`; - const headers = { - "Content-Type": "application/json" - }; - const body = JSON.stringify({ OneShot: true }); - - let response = await fetch(url_getUploadID, { - method: "POST", - headers: headers, - body: body - }); - - if (!response.ok) { - throw new Error(`Failed to get upload ID: ${response.status} ${response.statusText}`); - } - - const responseJson = await response.json(); - const uploadid = responseJson.id; - const uploadtoken = responseJson.uploadToken; - - // Step 2: Upload file data - const url_upload = `${fileserver_url}/file/${uploadid}`; - - // Create multipart form data - const formData = new FormData(); - // Create a Blob from the Uint8Array - const blob = new Blob([data], { type: "application/octet-stream" }); - formData.append("file", blob, dataname); - - response = await fetch(url_upload, { - method: "POST", - headers: { - "X-UploadToken": uploadtoken - }, - body: formData - }); - - if (!response.ok) { - throw new Error(`Failed to upload file: ${response.status} ${response.statusText}`); - } - - const fileResponseJson = await response.json(); - const fileid = fileResponseJson.id; - - // Build the download URL - const url = `${fileserver_url}/file/${uploadid}/${fileid}/${encodeURIComponent(dataname)}`; - - log_trace(correlation_id, `Uploaded to URL: ${url}`); - - return { - status: response.status, - uploadid: uploadid, - fileid: fileid, - url: url - }; + const result = await plik_oneshot_upload(fileserver_url, dataname, data); + log_trace(correlation_id, `Uploaded to URL: ${result.url}`); + return result; } // Helper: Fetch data from URL with exponential backoff @@ -481,11 +424,14 @@ async function smartsend(subject, data, options = {}) { * @param {string} options.receiver_name - Name of the receiver (default: "") * @param {string} options.receiver_id - UUID of the receiver (default: "") * @param {string} options.reply_to - Topic to reply to (default: "") - * @param {string} options.reply_to_msg_id - Message ID this message is replying to (default: "") - * @param {boolean} options.is_publish - Whether to automatically publish the message to NATS (default: true) - * - * @returns {Promise} - A tuple-like object with { env: MessageEnvelope, env_json_str: string } - */ + * @param {string} options.reply_to_msg_id - Message ID this message is replying to (default: "") + * @param {boolean} options.is_publish - Whether to automatically publish the message to NATS (default: true) + * - When true: Message is published to NATS automatically + * - When false: Returns (env, env_json_str) without publishing, allowing manual publishing + * @returns {Promise} - A tuple-like object with { env: MessageEnvelope, env_json_str: string } + * - env: MessageEnvelope object with all metadata and payloads + * - env_json_str: JSON string representation of the envelope for manual publishing + */ const { broker_url = DEFAULT_NATS_URL, fileserver_url = DEFAULT_FILESERVER_URL, @@ -541,8 +487,8 @@ async function smartsend(subject, data, options = {}) { // Link path - Upload to HTTP server, send URL via NATS log_trace(correlation_id, `Using link transport, uploading to fileserver`); - // Upload to HTTP server - const response = await fileserver_upload_handler(fileserver_url, dataname, payloadBytes, correlation_id); + // Upload to HTTP server using plik_oneshot_upload handler + const response = await fileserver_upload_handler(fileserver_url, dataname, payloadBytes); if (response.status !== 200) { throw new Error(`Failed to upload data to fileserver: ${response.status}`); @@ -705,13 +651,18 @@ async function smartreceive(msg, options = {}) { } // plik_oneshot_upload - matches Julia plik_oneshot_upload function +// Upload handler signature: plik_oneshot_upload(fileserver_url, dataname, data) +// Returns: { status, uploadid, fileid, url } async function plik_oneshot_upload(file_server_url, dataname, data) { /** * Upload a single file to a plik server using one-shot mode * This function uploads raw byte array to a plik server in one-shot mode (no upload session). * It first creates a one-shot upload session by sending a POST request with {"OneShot": true}, * retrieves an upload ID and token, then uploads the file data as multipart form data using the token. - * + * + * This is the default upload handler used by smartsend. + * Custom handlers can be passed via the fileserver_upload_handler option. + * * @param {string} file_server_url - Base URL of the plik server (e.g., "http://localhost:8080") * @param {string} dataname - Name of the file being uploaded * @param {Uint8Array} data - Raw byte data of the file content