@vielzeug/archive 26.11.11 → 26.11.13
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/dist/_server/d1.d.ts +23 -0
- package/dist/_server/d1.d.ts.map +1 -0
- package/dist/_server/execution.d.ts +5 -0
- package/dist/_server/execution.d.ts.map +1 -0
- package/dist/_server/http.cjs +2 -0
- package/dist/_server/http.cjs.map +1 -0
- package/dist/{worker → _server}/http.d.ts +6 -11
- package/dist/_server/http.d.ts.map +1 -0
- package/dist/{worker → _server}/http.js +18 -44
- package/dist/_server/http.js.map +1 -0
- package/dist/{worker/_service.cjs → _server/service.cjs} +1 -1
- package/dist/{worker/_service.js.map → _server/service.cjs.map} +1 -1
- package/dist/{worker/_service.d.ts → _server/service.d.ts} +1 -1
- package/dist/_server/service.d.ts.map +1 -0
- package/dist/{worker/_service.js → _server/service.js} +2 -2
- package/dist/_server/service.js.map +1 -0
- package/dist/_server/uuid.cjs +2 -0
- package/dist/_server/uuid.cjs.map +1 -0
- package/dist/_server/uuid.d.ts +3 -0
- package/dist/_server/uuid.d.ts.map +1 -0
- package/dist/_server/uuid.js +5 -0
- package/dist/_server/uuid.js.map +1 -0
- package/dist/auth/_confirmation.cjs +3 -3
- package/dist/auth/_confirmation.cjs.map +1 -1
- package/dist/auth/_confirmation.js +4 -4
- package/dist/auth/_confirmation.js.map +1 -1
- package/dist/auth/config.cjs.map +1 -1
- package/dist/auth/config.d.ts +1 -1
- package/dist/auth/config.js.map +1 -1
- package/dist/auth/errors.cjs +1 -1
- package/dist/auth/errors.cjs.map +1 -1
- package/dist/auth/errors.d.ts +1 -1
- package/dist/auth/errors.d.ts.map +1 -1
- package/dist/auth/errors.js +1 -1
- package/dist/auth/errors.js.map +1 -1
- package/dist/auth/store.cjs.map +1 -1
- package/dist/auth/store.d.ts +1 -1
- package/dist/auth/store.js.map +1 -1
- package/dist/auth/worker.cjs +1 -1
- package/dist/auth/worker.cjs.map +1 -1
- package/dist/auth/worker.d.ts +1 -1
- package/dist/auth/worker.d.ts.map +1 -1
- package/dist/auth/worker.js +153 -152
- package/dist/auth/worker.js.map +1 -1
- package/dist/errors.cjs +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/worker/_cors.cjs +2 -0
- package/dist/worker/_cors.cjs.map +1 -0
- package/dist/worker/_cors.d.ts +13 -0
- package/dist/worker/_cors.d.ts.map +1 -0
- package/dist/worker/_cors.js +32 -0
- package/dist/worker/_cors.js.map +1 -0
- package/dist/worker/_pagination.cjs +1 -1
- package/dist/worker/_pagination.cjs.map +1 -1
- package/dist/worker/_pagination.js +18 -18
- package/dist/worker/_pagination.js.map +1 -1
- package/dist/worker/auth.cjs +1 -1
- package/dist/worker/auth.cjs.map +1 -1
- package/dist/worker/auth.d.ts.map +1 -1
- package/dist/worker/auth.js +18 -18
- package/dist/worker/auth.js.map +1 -1
- package/dist/worker/env.cjs +1 -1
- package/dist/worker/env.cjs.map +1 -1
- package/dist/worker/env.d.ts +1 -22
- package/dist/worker/env.d.ts.map +1 -1
- package/dist/worker/env.js +1 -1
- package/dist/worker/env.js.map +1 -1
- package/dist/worker/publications.cjs +1 -1
- package/dist/worker/publications.cjs.map +1 -1
- package/dist/worker/publications.d.ts.map +1 -1
- package/dist/worker/publications.js +263 -262
- package/dist/worker/publications.js.map +1 -1
- package/dist/worker/worker.cjs +1 -1
- package/dist/worker/worker.cjs.map +1 -1
- package/dist/worker/worker.d.ts +2 -4
- package/dist/worker/worker.d.ts.map +1 -1
- package/dist/worker/worker.js +124 -122
- package/dist/worker/worker.js.map +1 -1
- package/dist/worker.d.ts +5 -3
- package/dist/worker.d.ts.map +1 -1
- package/package.json +2 -2
- package/dist/worker/_service.cjs.map +0 -1
- package/dist/worker/_service.d.ts.map +0 -1
- package/dist/worker/http.cjs +0 -2
- package/dist/worker/http.cjs.map +0 -1
- package/dist/worker/http.d.ts.map +0 -1
- package/dist/worker/http.js.map +0 -1
package/dist/worker/http.cjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const e=require("../contract.cjs");var ApiError=class extends Error{code;retryAfterSeconds;status;diagnostic;constructor(e,t,n,r){super(n,r),this.name=`ApiError`,this.code=t,this.retryAfterSeconds=r?.retryAfterSeconds,this.status=e,this.diagnostic=r?.diagnostic}},badRequest=(e=`The request is not valid.`)=>new ApiError(400,`bad-request`,e),unauthenticated=()=>new ApiError(401,`unauthenticated`,`Sign-in is required.`),forbidden=(e=`That action is not allowed.`)=>new ApiError(403,`forbidden`,e),notFound=()=>new ApiError(404,`not-found`,`No such entry.`),conflict=(e=`The entry changed since you saw it.`)=>new ApiError(409,`conflict`,e),invalid=(e=`The payload is not valid.`)=>new ApiError(422,`invalid-payload`,e),json=(e,t,n={})=>new Response(JSON.stringify(t),{headers:{"content-type":`application/json; charset=utf-8`,...n},status:e}),t={"cache-control":`no-store`},errorBody=(e,n,r,i)=>json(e,{error:{code:n,message:r,requestId:i}},t);function toErrorResponse(e,n){if(e instanceof ApiError){let r=e.retryAfterSeconds===void 0?t:{...t,"retry-after":String(e.retryAfterSeconds)};return json(e.status,{error:{code:e.code,message:e.message,requestId:n}},r)}return errorBody(500,`internal`,`Something went wrong.`,n)}function queryParams(e,t,n=[]){let r=e.searchParams,i=new Set;for(let e of r.keys()){if(!t.includes(e)||i.has(e)&&!n.includes(e))throw badRequest();i.add(e)}return r}function requireMutationGuard(e,t){let n=e.headers.get(`origin`);if(n===null||!t.allowedOrigins.has(n)||e.headers.get(`x-archive-request`)!==`1`)throw forbidden()}function corsHeaders(e,t){let n=e.headers.get(`origin`);return n===null||!t.allowedOrigins.has(n)?{}:{"access-control-allow-credentials":`true`,"access-control-allow-origin":n,vary:`Origin`}}function preflight(t,n){let r=t.headers.get(`origin`);return r===null||!n.allowedOrigins.has(r)?errorBody(403,`forbidden`,`That action is not allowed.`,crypto.randomUUID()):new Response(null,{headers:{"access-control-allow-credentials":`true`,"access-control-allow-headers":`Content-Type, ${e.PUBLISHER_HEADER}, ${e.REQUEST_MARKER_HEADER}`,"access-control-allow-methods":`GET, POST, PUT, DELETE, OPTIONS`,"access-control-allow-origin":r,"access-control-max-age":`600`,vary:`Origin`},status:204})}var n=1e4,bodyTimeout=()=>new ApiError(408,`timeout`,`The request body took too long.`);async function readJsonBody(t){if((t.headers.get(`content-type`)??``).split(`;`,1)[0].trim().toLowerCase()!==`application/json`)throw new ApiError(415,`unsupported-media`,`JSON bodies only.`);let r=t.body?.getReader();if(!r)throw badRequest();let i=Date.now(),a,o=[],s=0;try{for(;;){let{done:t,value:c}=await Promise.race([r.read(),new Promise((e,t)=>{a=setTimeout(()=>t(bodyTimeout()),Math.max(0,n-(Date.now()-i)))})]);if(clearTimeout(a),a=void 0,t)break;if(s+=c.byteLength,s>e.ARCHIVE_LIMITS.requestBytes)throw new ApiError(413,`too-large`,`The request is too large.`);o.push(c)}}catch(e){throw r.cancel().catch(()=>void 0),e}finally{a!==void 0&&clearTimeout(a),r.releaseLock()}let c=new Uint8Array(s),l=0;for(let e of o)c.set(e,l),l+=e.byteLength;if(s===0)throw badRequest();try{return JSON.parse(new TextDecoder(`utf-8`,{fatal:!0}).decode(c))}catch{throw badRequest()}}var r=4096;function sessionToken(e){let t=e.headers.get(`cookie`);if(t===null)return null;if(t.length>r)throw badRequest();let n=[];for(let e of t.split(`;`)){let t=e.indexOf(`=`);t!==-1&&e.slice(0,t).trim()===`archive_session`&&n.push(e.slice(t+1).trim())}if(n.length>1)throw badRequest();return n.length===1&&n[0].length>0?n[0]:null}var expiredSessionCookie=t=>e.sessionCookieValue(``,{maxAgeSeconds:0,secure:t});exports.ApiError=ApiError,exports.HTTP_BODY_TIMEOUT_MS=n,exports.badRequest=badRequest,exports.conflict=conflict,exports.corsHeaders=corsHeaders,exports.errorBody=errorBody,exports.expiredSessionCookie=expiredSessionCookie,exports.forbidden=forbidden,exports.invalid=invalid,exports.json=json,exports.noStore=t,exports.notFound=notFound,exports.preflight=preflight,exports.queryParams=queryParams,exports.readJsonBody=readJsonBody,exports.requireMutationGuard=requireMutationGuard,exports.sessionToken=sessionToken,exports.toErrorResponse=toErrorResponse,exports.unauthenticated=unauthenticated;
|
|
2
|
-
//# sourceMappingURL=http.cjs.map
|
package/dist/worker/http.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"http.cjs","names":["PUBLISHER_HEADER","REQUEST_MARKER_HEADER","ARCHIVE_LIMITS","sessionCookieValue","require_contract","message","opts","code","status","body","headers","noStore","error","requestId","params","url","seen","key","allowed","repeatable","origin","request","config","HTTP_BODY_TIMEOUT_MS","reader","startedAt","timeoutId","chunks","total","reject","done","value","merged","offset","chunk","SESSION_COOKIE_MAX","header","matches","part","eq"],"sources":["../../src/worker/http.ts"],"sourcesContent":["import {\n ARCHIVE_LIMITS,\n type ArchiveErrorCode,\n PUBLISHER_HEADER,\n REQUEST_MARKER_HEADER,\n SESSION_COOKIE_NAME,\n sessionCookieValue,\n} from '../contract';\nimport type { ApiConfig } from './env';\n\n/**\n * HTTP plumbing shared by every route: the one error shape, bounded body reads, strict\n * query/header guards and the CORS contract. Handlers throw ApiError; the entry point\n * sanitizes — no stack, SQL, body, or PII ever reaches a response. Internal to the\n * Worker: clients parse the wire envelope (`archiveErrorResponseSchema`), never this class.\n */\nexport class ApiError extends Error {\n readonly code: ArchiveErrorCode;\n readonly retryAfterSeconds?: number;\n readonly status: number;\n readonly diagnostic?: ArchiveDiagnostic;\n constructor(\n status: number,\n code: ArchiveErrorCode,\n message: string,\n opts?: ErrorOptions & { retryAfterSeconds?: number; diagnostic?: ArchiveDiagnostic },\n ) {\n super(message, opts);\n this.name = 'ApiError';\n this.code = code;\n this.retryAfterSeconds = opts?.retryAfterSeconds;\n this.status = status;\n this.diagnostic = opts?.diagnostic;\n }\n}\n\nexport type ArchiveDiagnostic =\n | { readonly kind: 'configuration'; readonly field: string }\n | { readonly kind: 'database' | 'record' | 'schema' | 'internal' | 'cache' | 'collection' };\n\nexport const badRequest = (message = 'The request is not valid.'): ApiError =>\n new ApiError(400, 'bad-request', message);\nexport const unauthenticated = (): ApiError => new ApiError(401, 'unauthenticated', 'Sign-in is required.');\nexport const forbidden = (message = 'That action is not allowed.'): ApiError => new ApiError(403, 'forbidden', message);\nexport const notFound = (): ApiError => new ApiError(404, 'not-found', 'No such entry.');\nexport const conflict = (message = 'The entry changed since you saw it.'): ApiError =>\n new ApiError(409, 'conflict', message);\nexport const invalid = (message = 'The payload is not valid.'): ApiError =>\n new ApiError(422, 'invalid-payload', message);\n\nexport const json = (status: number, body: unknown, headers: HeadersInit = {}): Response =>\n new Response(JSON.stringify(body), {\n headers: { 'content-type': 'application/json; charset=utf-8', ...headers },\n status,\n });\n\nexport const noStore = { 'cache-control': 'no-store' } as const;\n\nexport const errorBody = (status: number, code: string, message: string, requestId: string): Response =>\n json(status, { error: { code, message, requestId } }, noStore);\n\n/** Sanitizes any thrown value into the public error shape; unknowns become 500. */\nexport function toErrorResponse(error: unknown, requestId: string): Response {\n if (error instanceof ApiError) {\n const headers: HeadersInit =\n error.retryAfterSeconds === undefined ? noStore : { ...noStore, 'retry-after': String(error.retryAfterSeconds) };\n return json(error.status, { error: { code: error.code, message: error.message, requestId } }, headers);\n }\n return errorBody(500, 'internal', 'Something went wrong.', requestId);\n}\n\n/**\n * Rejects query strings with unknown or repeated parameters. Every accepted route\n * names its full allow-list; anything else is a client bug, not something to ignore.\n * Keys listed in `repeatable` may carry several values (read back with `getAll`);\n * every other key stays single-valued.\n */\nexport function queryParams(url: URL, allowed: readonly string[], repeatable: readonly string[] = []): URLSearchParams {\n const params = url.searchParams;\n const seen = new Set<string>();\n for (const key of params.keys()) {\n if (!allowed.includes(key) || (seen.has(key) && !repeatable.includes(key))) throw badRequest();\n seen.add(key);\n }\n return params;\n}\n\n/** Exact-origin plus request-marker guard for every mutation: no wildcards, no nulls. */\nexport function requireMutationGuard(request: Request, config: ApiConfig): void {\n const origin = request.headers.get('origin');\n if (origin === null || !config.allowedOrigins.has(origin)) throw forbidden();\n if (request.headers.get(REQUEST_MARKER_HEADER) !== '1') throw forbidden();\n}\n\n/** CORS response headers for one already-allowed origin; absent for foreign origins. */\nexport function corsHeaders(request: Request, config: ApiConfig): Record<string, string> {\n const origin = request.headers.get('origin');\n if (origin === null || !config.allowedOrigins.has(origin)) return {};\n return {\n 'access-control-allow-credentials': 'true',\n 'access-control-allow-origin': origin,\n vary: 'Origin',\n };\n}\n\n/** Answers a preflight for an allowed origin; foreign or missing origins get 403. */\nexport function preflight(request: Request, config: ApiConfig): Response {\n const origin = request.headers.get('origin');\n if (origin === null || !config.allowedOrigins.has(origin)) {\n return errorBody(403, 'forbidden', 'That action is not allowed.', crypto.randomUUID());\n }\n return new Response(null, {\n headers: {\n 'access-control-allow-credentials': 'true',\n // Header names are case-insensitive; the values derive from the contract constants.\n 'access-control-allow-headers': `Content-Type, ${PUBLISHER_HEADER}, ${REQUEST_MARKER_HEADER}`,\n 'access-control-allow-methods': 'GET, POST, PUT, DELETE, OPTIONS',\n 'access-control-allow-origin': origin,\n 'access-control-max-age': '600',\n vary: 'Origin',\n },\n status: 204,\n });\n}\n\n/** Hard deadline for reading a request body; slow streams fail as 408. */\nexport const HTTP_BODY_TIMEOUT_MS = 10000;\n\nconst bodyTimeout = (): ApiError => new ApiError(408, 'timeout', 'The request body took too long.');\n\n/**\n * Reads a JSON body with a hard byte ceiling and a wall-clock deadline enforced while\n * streaming — a lying or absent Content-Length and a stalled sender are both cut off.\n * Wrong media types are 415, oversized 413, timed out 408, malformed JSON 400; schema\n * validation happens afterwards at the route boundary.\n */\nexport async function readJsonBody(request: Request): Promise<unknown> {\n // Exactly the application/json media type; parameters (charset etc.) may follow.\n const mediaType = (request.headers.get('content-type') ?? '').split(';', 1)[0].trim().toLowerCase();\n if (mediaType !== 'application/json') {\n throw new ApiError(415, 'unsupported-media', 'JSON bodies only.');\n }\n const reader = request.body?.getReader();\n if (!reader) throw badRequest();\n const startedAt = Date.now();\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n const chunks: Uint8Array[] = [];\n let total = 0;\n try {\n for (;;) {\n const { done, value } = await Promise.race([\n reader.read(),\n new Promise<never>((_r, reject) => {\n timeoutId = setTimeout(\n () => reject(bodyTimeout()),\n Math.max(0, HTTP_BODY_TIMEOUT_MS - (Date.now() - startedAt)),\n );\n }),\n ]);\n clearTimeout(timeoutId);\n timeoutId = undefined;\n if (done) break;\n total += value.byteLength;\n if (total > ARCHIVE_LIMITS.requestBytes) throw new ApiError(413, 'too-large', 'The request is too large.');\n chunks.push(value);\n }\n } catch (error) {\n // Release the remainder so the failure isn't blocked on a stalled sender.\n reader.cancel().catch(() => undefined);\n throw error;\n } finally {\n if (timeoutId !== undefined) clearTimeout(timeoutId);\n reader.releaseLock();\n }\n const merged = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n merged.set(chunk, offset);\n offset += chunk.byteLength;\n }\n if (total === 0) throw badRequest();\n try {\n return JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(merged));\n } catch {\n throw badRequest();\n }\n}\n\nconst SESSION_COOKIE_MAX = 4096;\n\n/**\n * Reads the session cookie. A duplicate cookie name or an oversized header is a\n * malformed request, not a missing session: callers distinguish null (anonymous)\n * from a token to verify.\n */\nexport function sessionToken(request: Request): string | null {\n const header = request.headers.get('cookie');\n if (header === null) return null;\n if (header.length > SESSION_COOKIE_MAX) throw badRequest();\n const matches: string[] = [];\n for (const part of header.split(';')) {\n const eq = part.indexOf('=');\n if (eq === -1) continue;\n if (part.slice(0, eq).trim() === SESSION_COOKIE_NAME) matches.push(part.slice(eq + 1).trim());\n }\n if (matches.length > 1) throw badRequest();\n return matches.length === 1 && matches[0].length > 0 ? matches[0] : null;\n}\n\n/** The expired-session Set-Cookie value the sign-out route and account erasure send. */\nexport const expiredSessionCookie = (secure: boolean): string => sessionCookieValue('', { maxAgeSeconds: 0, secure });\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/worker/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,gBAAgB,EAKtB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC;;;;;GAKG;AACH,qBAAa,QAAS,SAAQ,KAAK;IACjC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,CAAC,EAAE,iBAAiB,CAAC;gBAEtC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,gBAAgB,EACtB,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,YAAY,GAAG;QAAE,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,iBAAiB,CAAA;KAAE;CASvF;AAED,MAAM,MAAM,iBAAiB,GACzB;IAAE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC1D;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,GAAG,YAAY,CAAA;CAAE,CAAC;AAE9F,eAAO,MAAM,UAAU,GAAI,gBAAqC,KAAG,QACxB,CAAC;AAC5C,eAAO,MAAM,eAAe,QAAO,QAAwE,CAAC;AAC5G,eAAO,MAAM,SAAS,GAAI,gBAAuC,KAAG,QAAmD,CAAC;AACxH,eAAO,MAAM,QAAQ,QAAO,QAA4D,CAAC;AACzF,eAAO,MAAM,QAAQ,GAAI,gBAA+C,KAAG,QACnC,CAAC;AACzC,eAAO,MAAM,OAAO,GAAI,gBAAqC,KAAG,QACjB,CAAC;AAEhD,eAAO,MAAM,IAAI,GAAI,QAAQ,MAAM,EAAE,MAAM,OAAO,EAAE,UAAS,WAAgB,KAAG,QAI5E,CAAC;AAEL,eAAO,MAAM,OAAO;;CAA2C,CAAC;AAEhE,eAAO,MAAM,SAAS,GAAI,QAAQ,MAAM,EAAE,MAAM,MAAM,EAAE,SAAS,MAAM,EAAE,WAAW,MAAM,KAAG,QAC7B,CAAC;AAEjE,mFAAmF;AACnF,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,QAAQ,CAO3E;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE,EAAE,UAAU,GAAE,SAAS,MAAM,EAAO,GAAG,eAAe,CAQrH;AAED,yFAAyF;AACzF,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,GAAG,IAAI,CAI9E;AAED,wFAAwF;AACxF,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQvF;AAED,qFAAqF;AACrF,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,GAAG,QAAQ,CAiBvE;AAED,0EAA0E;AAC1E,eAAO,MAAM,oBAAoB,QAAQ,CAAC;AAI1C;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAkDrE;AAID;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAY5D;AAED,wFAAwF;AACxF,eAAO,MAAM,oBAAoB,GAAI,QAAQ,OAAO,KAAG,MAA8D,CAAC"}
|
package/dist/worker/http.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"http.js","names":["message","opts","code","status","body","headers","noStore","error","requestId","params","url","seen","key","allowed","repeatable","origin","request","config","PUBLISHER_HEADER","REQUEST_MARKER_HEADER","HTTP_BODY_TIMEOUT_MS","reader","startedAt","timeoutId","chunks","total","reject","done","value","ARCHIVE_LIMITS","merged","offset","chunk","SESSION_COOKIE_MAX","header","matches","part","eq","sessionCookieValue"],"sources":["../../src/worker/http.ts"],"sourcesContent":["import {\n ARCHIVE_LIMITS,\n type ArchiveErrorCode,\n PUBLISHER_HEADER,\n REQUEST_MARKER_HEADER,\n SESSION_COOKIE_NAME,\n sessionCookieValue,\n} from '../contract';\nimport type { ApiConfig } from './env';\n\n/**\n * HTTP plumbing shared by every route: the one error shape, bounded body reads, strict\n * query/header guards and the CORS contract. Handlers throw ApiError; the entry point\n * sanitizes — no stack, SQL, body, or PII ever reaches a response. Internal to the\n * Worker: clients parse the wire envelope (`archiveErrorResponseSchema`), never this class.\n */\nexport class ApiError extends Error {\n readonly code: ArchiveErrorCode;\n readonly retryAfterSeconds?: number;\n readonly status: number;\n readonly diagnostic?: ArchiveDiagnostic;\n constructor(\n status: number,\n code: ArchiveErrorCode,\n message: string,\n opts?: ErrorOptions & { retryAfterSeconds?: number; diagnostic?: ArchiveDiagnostic },\n ) {\n super(message, opts);\n this.name = 'ApiError';\n this.code = code;\n this.retryAfterSeconds = opts?.retryAfterSeconds;\n this.status = status;\n this.diagnostic = opts?.diagnostic;\n }\n}\n\nexport type ArchiveDiagnostic =\n | { readonly kind: 'configuration'; readonly field: string }\n | { readonly kind: 'database' | 'record' | 'schema' | 'internal' | 'cache' | 'collection' };\n\nexport const badRequest = (message = 'The request is not valid.'): ApiError =>\n new ApiError(400, 'bad-request', message);\nexport const unauthenticated = (): ApiError => new ApiError(401, 'unauthenticated', 'Sign-in is required.');\nexport const forbidden = (message = 'That action is not allowed.'): ApiError => new ApiError(403, 'forbidden', message);\nexport const notFound = (): ApiError => new ApiError(404, 'not-found', 'No such entry.');\nexport const conflict = (message = 'The entry changed since you saw it.'): ApiError =>\n new ApiError(409, 'conflict', message);\nexport const invalid = (message = 'The payload is not valid.'): ApiError =>\n new ApiError(422, 'invalid-payload', message);\n\nexport const json = (status: number, body: unknown, headers: HeadersInit = {}): Response =>\n new Response(JSON.stringify(body), {\n headers: { 'content-type': 'application/json; charset=utf-8', ...headers },\n status,\n });\n\nexport const noStore = { 'cache-control': 'no-store' } as const;\n\nexport const errorBody = (status: number, code: string, message: string, requestId: string): Response =>\n json(status, { error: { code, message, requestId } }, noStore);\n\n/** Sanitizes any thrown value into the public error shape; unknowns become 500. */\nexport function toErrorResponse(error: unknown, requestId: string): Response {\n if (error instanceof ApiError) {\n const headers: HeadersInit =\n error.retryAfterSeconds === undefined ? noStore : { ...noStore, 'retry-after': String(error.retryAfterSeconds) };\n return json(error.status, { error: { code: error.code, message: error.message, requestId } }, headers);\n }\n return errorBody(500, 'internal', 'Something went wrong.', requestId);\n}\n\n/**\n * Rejects query strings with unknown or repeated parameters. Every accepted route\n * names its full allow-list; anything else is a client bug, not something to ignore.\n * Keys listed in `repeatable` may carry several values (read back with `getAll`);\n * every other key stays single-valued.\n */\nexport function queryParams(url: URL, allowed: readonly string[], repeatable: readonly string[] = []): URLSearchParams {\n const params = url.searchParams;\n const seen = new Set<string>();\n for (const key of params.keys()) {\n if (!allowed.includes(key) || (seen.has(key) && !repeatable.includes(key))) throw badRequest();\n seen.add(key);\n }\n return params;\n}\n\n/** Exact-origin plus request-marker guard for every mutation: no wildcards, no nulls. */\nexport function requireMutationGuard(request: Request, config: ApiConfig): void {\n const origin = request.headers.get('origin');\n if (origin === null || !config.allowedOrigins.has(origin)) throw forbidden();\n if (request.headers.get(REQUEST_MARKER_HEADER) !== '1') throw forbidden();\n}\n\n/** CORS response headers for one already-allowed origin; absent for foreign origins. */\nexport function corsHeaders(request: Request, config: ApiConfig): Record<string, string> {\n const origin = request.headers.get('origin');\n if (origin === null || !config.allowedOrigins.has(origin)) return {};\n return {\n 'access-control-allow-credentials': 'true',\n 'access-control-allow-origin': origin,\n vary: 'Origin',\n };\n}\n\n/** Answers a preflight for an allowed origin; foreign or missing origins get 403. */\nexport function preflight(request: Request, config: ApiConfig): Response {\n const origin = request.headers.get('origin');\n if (origin === null || !config.allowedOrigins.has(origin)) {\n return errorBody(403, 'forbidden', 'That action is not allowed.', crypto.randomUUID());\n }\n return new Response(null, {\n headers: {\n 'access-control-allow-credentials': 'true',\n // Header names are case-insensitive; the values derive from the contract constants.\n 'access-control-allow-headers': `Content-Type, ${PUBLISHER_HEADER}, ${REQUEST_MARKER_HEADER}`,\n 'access-control-allow-methods': 'GET, POST, PUT, DELETE, OPTIONS',\n 'access-control-allow-origin': origin,\n 'access-control-max-age': '600',\n vary: 'Origin',\n },\n status: 204,\n });\n}\n\n/** Hard deadline for reading a request body; slow streams fail as 408. */\nexport const HTTP_BODY_TIMEOUT_MS = 10000;\n\nconst bodyTimeout = (): ApiError => new ApiError(408, 'timeout', 'The request body took too long.');\n\n/**\n * Reads a JSON body with a hard byte ceiling and a wall-clock deadline enforced while\n * streaming — a lying or absent Content-Length and a stalled sender are both cut off.\n * Wrong media types are 415, oversized 413, timed out 408, malformed JSON 400; schema\n * validation happens afterwards at the route boundary.\n */\nexport async function readJsonBody(request: Request): Promise<unknown> {\n // Exactly the application/json media type; parameters (charset etc.) may follow.\n const mediaType = (request.headers.get('content-type') ?? '').split(';', 1)[0].trim().toLowerCase();\n if (mediaType !== 'application/json') {\n throw new ApiError(415, 'unsupported-media', 'JSON bodies only.');\n }\n const reader = request.body?.getReader();\n if (!reader) throw badRequest();\n const startedAt = Date.now();\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n const chunks: Uint8Array[] = [];\n let total = 0;\n try {\n for (;;) {\n const { done, value } = await Promise.race([\n reader.read(),\n new Promise<never>((_r, reject) => {\n timeoutId = setTimeout(\n () => reject(bodyTimeout()),\n Math.max(0, HTTP_BODY_TIMEOUT_MS - (Date.now() - startedAt)),\n );\n }),\n ]);\n clearTimeout(timeoutId);\n timeoutId = undefined;\n if (done) break;\n total += value.byteLength;\n if (total > ARCHIVE_LIMITS.requestBytes) throw new ApiError(413, 'too-large', 'The request is too large.');\n chunks.push(value);\n }\n } catch (error) {\n // Release the remainder so the failure isn't blocked on a stalled sender.\n reader.cancel().catch(() => undefined);\n throw error;\n } finally {\n if (timeoutId !== undefined) clearTimeout(timeoutId);\n reader.releaseLock();\n }\n const merged = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n merged.set(chunk, offset);\n offset += chunk.byteLength;\n }\n if (total === 0) throw badRequest();\n try {\n return JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(merged));\n } catch {\n throw badRequest();\n }\n}\n\nconst SESSION_COOKIE_MAX = 4096;\n\n/**\n * Reads the session cookie. A duplicate cookie name or an oversized header is a\n * malformed request, not a missing session: callers distinguish null (anonymous)\n * from a token to verify.\n */\nexport function sessionToken(request: Request): string | null {\n const header = request.headers.get('cookie');\n if (header === null) return null;\n if (header.length > SESSION_COOKIE_MAX) throw badRequest();\n const matches: string[] = [];\n for (const part of header.split(';')) {\n const eq = part.indexOf('=');\n if (eq === -1) continue;\n if (part.slice(0, eq).trim() === SESSION_COOKIE_NAME) matches.push(part.slice(eq + 1).trim());\n }\n if (matches.length > 1) throw badRequest();\n return matches.length === 1 && matches[0].length > 0 ? matches[0] : null;\n}\n\n/** The expired-session Set-Cookie value the sign-out route and account erasure send. */\nexport const expiredSessionCookie = (secure: boolean): string => sessionCookieValue('', { maxAgeSeconds: 0, secure });\n"],"mappings":"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"}
|