@sproutboat/runtime 0.9.2 → 0.9.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @sproutboat/runtime
2
2
 
3
+ ## 0.9.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 5ee065c: Stream standalone direct R2 byte ranges safely and support ETag conditionals.
8
+
3
9
  ## 0.9.2
4
10
 
5
11
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sproutboat/runtime",
3
- "version": "0.9.2",
3
+ "version": "0.9.3",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "repository": {
@@ -581,7 +581,7 @@ static void sb_r2_delete_blob(const char* path, const char* bucket, const char*
581
581
  typedef struct sb_r2_transfer_ctx {
582
582
  FILE* file;
583
583
  sqlite3* db;
584
- char store[1024], bucket[256], key[512], token[32], expected[65], http[1024], custom[1024], temporary[1200];
584
+ char store[1024], bucket[256], key[512], token[32], expected[65], etag[65], http[1024], custom[1024], temporary[1200];
585
585
  size_t size, max_bytes;
586
586
  } sb_r2_transfer_ctx;
587
587
 
@@ -771,11 +771,14 @@ int sb_r2_transfer_download_open(const char* bucket, const char* token, sb_r2_tr
771
771
  snprintf(ctx->key, sizeof(ctx->key), "%s", key);
772
772
  sqlite3_finalize(st);
773
773
  st = 0;
774
- if (sqlite3_prepare_v2(ctx->db, "SELECT blob_id FROM r2 WHERE bucket=?1 AND key=?2", -1, &st, 0) != 0 || !st) { free(ctx); return 503; }
774
+ if (sqlite3_prepare_v2(ctx->db, "SELECT blob_id, etag FROM r2 WHERE bucket=?1 AND key=?2", -1, &st, 0) != 0 || !st) { free(ctx); return 503; }
775
775
  sqlite3_bind_text(st, 1, bucket, -1, SB_SQLITE_TRANSIENT);
776
776
  sqlite3_bind_text(st, 2, ctx->key, -1, SB_SQLITE_TRANSIENT);
777
777
  if (sqlite3_step(st) != SB_SQLITE_ROW) { sqlite3_finalize(st); free(ctx); return 404; }
778
778
  const char* generation = (const char*)sqlite3_column_text(st, 0);
779
+ const char* etag = (const char*)sqlite3_column_text(st, 1);
780
+ if (!etag || strlen(etag) >= sizeof(ctx->etag)) { sqlite3_finalize(st); free(ctx); return 404; }
781
+ snprintf(ctx->etag, sizeof(ctx->etag), "%s", etag);
779
782
  char blobpath[1024];
780
783
  sb_r2_blob_path(blobpath, sizeof(blobpath), ctx->store, bucket, ctx->key, generation ? generation : "");
781
784
  sqlite3_finalize(st);
@@ -795,6 +798,7 @@ int sb_r2_transfer_download_open(const char* bucket, const char* token, sb_r2_tr
795
798
  }
796
799
 
797
800
  size_t sb_r2_transfer_download_size(sb_r2_transfer_ctx* ctx) { return ctx ? ctx->size : 0; }
801
+ const char* sb_r2_transfer_download_etag(sb_r2_transfer_ctx* ctx) { return ctx ? ctx->etag : ""; }
798
802
  size_t sb_r2_transfer_download_read(sb_r2_transfer_ctx* ctx, size_t offset, char* out, size_t cap) {
799
803
  if (!ctx || !ctx->file || offset >= ctx->size || fseek(ctx->file, (long)offset, SEEK_SET) != 0) return 0;
800
804
  size_t wanted = ctx->size - offset < cap ? ctx->size - offset : cap;