var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __decorateClass = (decorators, target, key, kind) => {
  for (var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target, i = decorators.length - 1, decorator; i >= 0; i--)
    (decorator = decorators[i]) && (result = (kind ? decorator(target, key, result) : decorator(result)) || result);
  return kind && result && __defProp(target, key, result), result;
};

// src/workers/kv/namespace.worker.ts
import assert from "node:assert";
import {
  DeferredPromise,
  DELETE,
  GET,
  HttpError as HttpError2,
  KeyValueStorage,
  maybeApply,
  MiniflareDurableObject,
  POST,
  PUT
} from "miniflare:shared";

// src/workers/kv/constants.ts
import { testRegExps } from "miniflare:shared";
var KVLimits = {
  MIN_CACHE_TTL_SECONDS: 30,
  MIN_EXPIRATION_TTL_SECONDS: 60,
  MAX_LIST_KEYS: 1e3,
  MAX_KEY_SIZE_BYTES: 512,
  MAX_VALUE_SIZE_BYTES: 25 * 1024 * 1024,
  MAX_VALUE_SIZE_TEST_BYTES: 1024,
  MAX_METADATA_SIZE_BYTES: 1024,
  MAX_BULK_SIZE_BYTES: 25 * 1024 * 1024
}, KVParams = {
  URL_ENCODED: "urlencoded",
  CACHE_TTL: "cache_ttl",
  EXPIRATION: "expiration",
  EXPIRATION_TTL: "expiration_ttl",
  LIST_LIMIT: "key_count_limit",
  LIST_PREFIX: "prefix",
  LIST_CURSOR: "cursor"
}, KVHeaders = {
  EXPIRATION: "CF-Expiration",
  METADATA: "CF-KV-Metadata"
};
var MAX_BULK_GET_KEYS = 100;

// src/workers/kv/validator.worker.ts
import { Buffer as Buffer2 } from "node:buffer";
import { HttpError } from "miniflare:shared";
function decodeKey({ key }, query) {
  if (query.get(KVParams.URL_ENCODED)?.toLowerCase() !== "true") return key;
  try {
    return decodeURIComponent(key);
  } catch (e) {
    throw e instanceof URIError ? new HttpError(400, "Could not URL-decode key name") : e;
  }
}
function validateKey(key) {
  if (key === "")
    throw new HttpError(400, "Key names must not be empty");
  if (key === "." || key === "..")
    throw new HttpError(
      400,
      `Illegal key name "${key}". Please use a different name.`
    );
  validateKeyLength(key);
}
function validateKeyLength(key) {
  let keyLength = Buffer2.byteLength(key);
  if (keyLength > KVLimits.MAX_KEY_SIZE_BYTES)
    throw new HttpError(
      414,
      `UTF-8 encoded length of ${keyLength} exceeds key length limit of ${KVLimits.MAX_KEY_SIZE_BYTES}.`
    );
}
function validateGetOptions(key, options) {
  validateKey(key);
  let cacheTtl = options?.cacheTtl;
  if (cacheTtl !== void 0 && (isNaN(cacheTtl) || cacheTtl < KVLimits.MIN_CACHE_TTL_SECONDS))
    throw new HttpError(
      400,
      `Invalid ${KVParams.CACHE_TTL} of ${cacheTtl}. Cache TTL must be at least ${KVLimits.MIN_CACHE_TTL_SECONDS}.`
    );
}
function validatePutOptions(key, options) {
  let { now, rawExpiration, rawExpirationTtl, rawMetadata } = options;
  validateKey(key);
  let expiration;
  if (rawExpirationTtl !== null) {
    let expirationTtl = parseInt(rawExpirationTtl);
    if (Number.isNaN(expirationTtl) || expirationTtl <= 0)
      throw new HttpError(
        400,
        `Invalid ${KVParams.EXPIRATION_TTL} of ${rawExpirationTtl}. Please specify integer greater than 0.`
      );
    if (expirationTtl < KVLimits.MIN_EXPIRATION_TTL_SECONDS)
      throw new HttpError(
        400,
        `Invalid ${KVParams.EXPIRATION_TTL} of ${rawExpirationTtl}. Expiration TTL must be at least ${KVLimits.MIN_EXPIRATION_TTL_SECONDS}.`
      );
    expiration = now + expirationTtl;
  } else if (rawExpiration !== null) {
    if (expiration = parseInt(rawExpiration), Number.isNaN(expiration) || expiration <= now)
      throw new HttpError(
        400,
        `Invalid ${KVParams.EXPIRATION} of ${rawExpiration}. Please specify integer greater than the current number of seconds since the UNIX epoch.`
      );
    if (expiration < now + KVLimits.MIN_EXPIRATION_TTL_SECONDS)
      throw new HttpError(
        400,
        `Invalid ${KVParams.EXPIRATION} of ${rawExpiration}. Expiration times must be at least ${KVLimits.MIN_EXPIRATION_TTL_SECONDS} seconds in the future.`
      );
  }
  let metadata;
  if (rawMetadata !== null) {
    let metadataLength = Buffer2.byteLength(rawMetadata);
    if (metadataLength > KVLimits.MAX_METADATA_SIZE_BYTES)
      throw new HttpError(
        413,
        `Metadata length of ${metadataLength} exceeds limit of ${KVLimits.MAX_METADATA_SIZE_BYTES}.`
      );
    metadata = JSON.parse(rawMetadata);
  }
  return { expiration, metadata };
}
function decodeListOptions(url) {
  let limitParam = url.searchParams.get(KVParams.LIST_LIMIT), limit = limitParam === null ? KVLimits.MAX_LIST_KEYS : parseInt(limitParam), prefix = url.searchParams.get(KVParams.LIST_PREFIX) ?? void 0, cursor = url.searchParams.get(KVParams.LIST_CURSOR) ?? void 0;
  return { limit, prefix, cursor };
}
function validateListOptions(options) {
  let limit = options.limit;
  if (limit !== void 0) {
    if (isNaN(limit) || limit < 1)
      throw new HttpError(
        400,
        `Invalid ${KVParams.LIST_LIMIT} of ${limit}. Please specify an integer greater than 0.`
      );
    if (limit > KVLimits.MAX_LIST_KEYS)
      throw new HttpError(
        400,
        `Invalid ${KVParams.LIST_LIMIT} of ${limit}. Please specify an integer less than ${KVLimits.MAX_LIST_KEYS}.`
      );
  }
  let prefix = options.prefix;
  prefix != null && validateKeyLength(prefix);
}

// src/workers/kv/namespace.worker.ts
function createMaxValueSizeError(length, maxValueSize) {
  return new HttpError2(
    413,
    `Value length of ${length} exceeds limit of ${maxValueSize}.`
  );
}
var MaxLengthStream = class extends TransformStream {
  signal;
  length;
  constructor(maxLength) {
    let abortController = new AbortController(), lengthPromise = new DeferredPromise(), length = 0;
    super({
      transform(chunk, controller) {
        length += chunk.byteLength, length <= maxLength ? controller.enqueue(chunk) : abortController.signal.aborted || abortController.abort();
      },
      flush() {
        lengthPromise.resolve(length);
      }
    }), this.signal = abortController.signal, this.length = lengthPromise;
  }
};
function millisToSeconds(millis) {
  return Math.floor(millis / 1e3);
}
function secondsToMillis(seconds) {
  return seconds * 1e3;
}
async function processKeyValue(obj, type = "text", withMetadata = !1) {
  let decoder = new TextDecoder(), decodedValue = "";
  if (obj?.value) {
    for await (let chunk of obj?.value)
      decodedValue += decoder.decode(chunk, { stream: !0 });
    decodedValue += decoder.decode();
  }
  let val = null, size = decodedValue.length;
  try {
    val = obj?.value ? type === "json" ? JSON.parse(decodedValue) : decodedValue : null;
  } catch {
    throw new HttpError2(
      400,
      `At least one of the requested keys corresponds to a non-${type} value`
    );
  }
  return val && withMetadata ? [
    {
      value: val,
      metadata: obj?.metadata ?? null
    },
    size
  ] : [val, size];
}
var KVNamespaceObject = class extends MiniflareDurableObject {
  #storage;
  get storage() {
    return this.#storage ??= new KeyValueStorage(this);
  }
  get = async (req, params, url) => {
    if (req.method === "POST" && req.body != null) {
      let decodedBody = "", decoder = new TextDecoder();
      for await (let chunk of req.body)
        decodedBody += decoder.decode(chunk, { stream: !0 });
      decodedBody += decoder.decode();
      let parsedBody = JSON.parse(decodedBody), keys = parsedBody.keys, type = parsedBody?.type;
      if (type && type !== "text" && type !== "json") {
        let errorStr = `"${type}" is not a valid type. Use "json" or "text"`;
        return new Response(errorStr, { status: 400, statusText: errorStr });
      }
      let obj = {};
      if (keys.length > MAX_BULK_GET_KEYS) {
        let errorStr = `You can request a maximum of ${MAX_BULK_GET_KEYS} keys`;
        return new Response(errorStr, { status: 400, statusText: errorStr });
      }
      if (keys.length < 1) {
        let errorStr = "You must request a minimum of 1 key";
        return new Response(errorStr, { status: 400, statusText: errorStr });
      }
      let totalBytes = 0;
      for (let key2 of keys) {
        validateGetOptions(key2, { cacheTtl: parsedBody?.cacheTtl });
        let entry2 = await this.storage.get(key2), [value, size] = await processKeyValue(
          entry2,
          parsedBody?.type,
          parsedBody?.withMetadata
        );
        totalBytes += size, obj[key2] = value;
      }
      let maxValueSize = this.beingTested ? KVLimits.MAX_VALUE_SIZE_TEST_BYTES : KVLimits.MAX_BULK_SIZE_BYTES;
      if (totalBytes > maxValueSize)
        throw new HttpError2(
          413,
          `Total size of request exceeds the limit of ${maxValueSize / 1024 / 1024}MB`
        );
      return new Response(JSON.stringify(obj));
    }
    let key = decodeKey(params, url.searchParams), cacheTtlParam = url.searchParams.get(KVParams.CACHE_TTL), cacheTtl = cacheTtlParam === null ? void 0 : parseInt(cacheTtlParam);
    validateGetOptions(key, { cacheTtl });
    let entry = await this.storage.get(key);
    if (entry === null) throw new HttpError2(404, "Not Found");
    let headers = new Headers();
    return entry.expiration !== void 0 && headers.set(
      KVHeaders.EXPIRATION,
      millisToSeconds(entry.expiration).toString()
    ), entry.metadata !== void 0 && headers.set(KVHeaders.METADATA, JSON.stringify(entry.metadata)), new Response(entry.value, { headers });
  };
  put = async (req, params, url) => {
    let key = decodeKey(params, url.searchParams), rawExpiration = url.searchParams.get(KVParams.EXPIRATION), rawExpirationTtl = url.searchParams.get(KVParams.EXPIRATION_TTL), rawMetadata = req.headers.get(KVHeaders.METADATA), now = millisToSeconds(this.timers.now()), { expiration, metadata } = validatePutOptions(key, {
      now,
      rawExpiration,
      rawExpirationTtl,
      rawMetadata
    }), value = req.body, contentLength = parseInt(req.headers.get("Content-Length")), valueLengthHint;
    Number.isNaN(contentLength) ? value === null && (valueLengthHint = 0) : valueLengthHint = contentLength, value ??= new ReadableStream({
      start(controller) {
        controller.close();
      }
    });
    let maxValueSize = this.beingTested ? KVLimits.MAX_VALUE_SIZE_TEST_BYTES : KVLimits.MAX_VALUE_SIZE_BYTES, maxLengthStream;
    if (valueLengthHint !== void 0 && valueLengthHint > maxValueSize)
      throw createMaxValueSizeError(valueLengthHint, maxValueSize);
    maxLengthStream = new MaxLengthStream(maxValueSize), value = value.pipeThrough(maxLengthStream);
    try {
      await this.storage.put({
        key,
        value,
        expiration: maybeApply(secondsToMillis, expiration),
        metadata,
        signal: maxLengthStream?.signal
      });
    } catch (e) {
      if (typeof e == "object" && e !== null && "name" in e && e.name === "AbortError") {
        assert(maxLengthStream !== void 0);
        let length = await maxLengthStream.length;
        throw createMaxValueSizeError(length, maxValueSize);
      } else
        throw e;
    }
    return new Response();
  };
  delete = async (req, params, url) => {
    let key = decodeKey(params, url.searchParams);
    return validateKey(key), await this.storage.delete(key), new Response();
  };
  list = async (req, params, url) => {
    let options = decodeListOptions(url);
    validateListOptions(options);
    let res = await this.storage.list(options), keys = res.keys.map((key) => ({
      name: key.key,
      expiration: maybeApply(millisToSeconds, key.expiration),
      // workerd expects metadata to be a JSON-serialised string
      metadata: maybeApply(JSON.stringify, key.metadata)
    })), result;
    return res.cursor === void 0 ? result = { keys, list_complete: !0, cacheStatus: null } : result = {
      keys,
      list_complete: !1,
      cursor: res.cursor,
      cacheStatus: null
    }, Response.json(result);
  };
};
__decorateClass([
  GET("/:key"),
  POST("/bulk/get")
], KVNamespaceObject.prototype, "get", 2), __decorateClass([
  PUT("/:key")
], KVNamespaceObject.prototype, "put", 2), __decorateClass([
  DELETE("/:key")
], KVNamespaceObject.prototype, "delete", 2), __decorateClass([
  GET("/")
], KVNamespaceObject.prototype, "list", 2);
export {
  KVNamespaceObject
};
//# sourceMappingURL=namespace.worker.js.map
