Next.js website for Rocky Mountain Vending company featuring: - Product catalog with Stripe integration - Service areas and parts pages - Admin dashboard with Clerk authentication - SEO optimized pages with JSON-LD structured data Co-authored-by: Cursor <cursoragent@cursor.com>
258 lines
9.5 KiB
Text
258 lines
9.5 KiB
Text
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,
|
|
PUT
|
|
} from "miniflare:shared";
|
|
|
|
// src/workers/kv/constants.ts
|
|
import { testRegExps } from "miniflare:shared";
|
|
var KVLimits = {
|
|
MIN_CACHE_TTL: 60,
|
|
MAX_LIST_KEYS: 1e3,
|
|
MAX_KEY_SIZE: 512,
|
|
MAX_VALUE_SIZE: 25 * 1024 * 1024,
|
|
MAX_VALUE_SIZE_TEST: 1024,
|
|
MAX_METADATA_SIZE: 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"
|
|
};
|
|
|
|
// 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)
|
|
throw new HttpError(
|
|
414,
|
|
`UTF-8 encoded length of ${keyLength} exceeds key length limit of ${KVLimits.MAX_KEY_SIZE}.`
|
|
);
|
|
}
|
|
function validateGetOptions(key, options) {
|
|
validateKey(key);
|
|
let cacheTtl = options?.cacheTtl;
|
|
if (cacheTtl !== void 0 && (isNaN(cacheTtl) || cacheTtl < KVLimits.MIN_CACHE_TTL))
|
|
throw new HttpError(
|
|
400,
|
|
`Invalid ${KVParams.CACHE_TTL} of ${cacheTtl}. Cache TTL must be at least ${KVLimits.MIN_CACHE_TTL}.`
|
|
);
|
|
}
|
|
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_CACHE_TTL)
|
|
throw new HttpError(
|
|
400,
|
|
`Invalid ${KVParams.EXPIRATION_TTL} of ${rawExpirationTtl}. Expiration TTL must be at least ${KVLimits.MIN_CACHE_TTL}.`
|
|
);
|
|
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_CACHE_TTL)
|
|
throw new HttpError(
|
|
400,
|
|
`Invalid ${KVParams.EXPIRATION} of ${rawExpiration}. Expiration times must be at least ${KVLimits.MIN_CACHE_TTL} seconds in the future.`
|
|
);
|
|
}
|
|
let metadata;
|
|
if (rawMetadata !== null) {
|
|
let metadataLength = Buffer2.byteLength(rawMetadata);
|
|
if (metadataLength > KVLimits.MAX_METADATA_SIZE)
|
|
throw new HttpError(
|
|
413,
|
|
`Metadata length of ${metadataLength} exceeds limit of ${KVLimits.MAX_METADATA_SIZE}.`
|
|
);
|
|
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;
|
|
}
|
|
var KVNamespaceObject = class extends MiniflareDurableObject {
|
|
#storage;
|
|
get storage() {
|
|
return this.#storage ??= new KeyValueStorage(this);
|
|
}
|
|
get = async (req, params, url) => {
|
|
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 : KVLimits.MAX_VALUE_SIZE, 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")
|
|
], 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
|