@demiurgejs/core 0.2.0-beta.5 → 0.2.0-beta.7
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/README.md +14 -0
- package/bin/demiurge.mjs +6 -0
- package/dist/browser/file-router.d.ts +1 -0
- package/dist/browser/file-router.d.ts.map +1 -1
- package/dist/chunks/{document-shell-9GPwtcTt.js → document-shell-C0aPrkhd.js} +2 -2
- package/dist/chunks/{document-shell-9GPwtcTt.js.map → document-shell-C0aPrkhd.js.map} +1 -1
- package/dist/chunks/image-CRerJ5BY.js +145 -0
- package/dist/chunks/image-CRerJ5BY.js.map +1 -0
- package/dist/chunks/{image-codec-Dh1POFhE.js → image-codec-CQ8Cwxwb.js} +2 -2
- package/dist/chunks/{image-codec-Dh1POFhE.js.map → image-codec-CQ8Cwxwb.js.map} +1 -1
- package/dist/chunks/{images-CG7ZzayG.js → images-QgdmLyQ3.js} +11 -11
- package/dist/chunks/{images-CG7ZzayG.js.map → images-QgdmLyQ3.js.map} +1 -1
- package/dist/chunks/{index-0MNiR_wo.js → index-LYve1rQp.js} +286 -351
- package/dist/chunks/index-LYve1rQp.js.map +1 -0
- package/dist/chunks/links-iU22QkCE.js +68 -0
- package/dist/chunks/links-iU22QkCE.js.map +1 -0
- package/dist/chunks/{plugin-BZauDxRY.js → plugin-bkP6WwrS.js} +13 -12
- package/dist/chunks/{plugin-BZauDxRY.js.map → plugin-bkP6WwrS.js.map} +1 -1
- package/dist/chunks/runtime-BH519j2t.js +294 -0
- package/dist/chunks/runtime-BH519j2t.js.map +1 -0
- package/dist/chunks/streaming-BlKlC3nw.js +427 -0
- package/dist/chunks/streaming-BlKlC3nw.js.map +1 -0
- package/dist/chunks/{testing-CUwGj2ie.js → testing-BEW1nchS.js} +645 -630
- package/dist/chunks/testing-BEW1nchS.js.map +1 -0
- package/dist/chunks/{vite-QoqrGl6f.js → vite-BADZA8Fl.js} +73 -70
- package/dist/chunks/vite-BADZA8Fl.js.map +1 -0
- package/dist/cli.d.ts +8 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +186 -122
- package/dist/cli.js.map +1 -1
- package/dist/config/index.js +2 -2
- package/dist/config/types.d.ts +3 -1
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/validate.d.ts.map +1 -1
- package/dist/data/cache.d.ts +5 -0
- package/dist/data/cache.d.ts.map +1 -1
- package/dist/data/index.d.ts +1 -1
- package/dist/data/index.d.ts.map +1 -1
- package/dist/data/testing.d.ts.map +1 -1
- package/dist/data/testing.js +101 -67
- package/dist/data/testing.js.map +1 -1
- package/dist/data/tiered.d.ts.map +1 -1
- package/dist/edge/index.js +27 -26
- package/dist/edge/index.js.map +1 -1
- package/dist/edge/stores.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +910 -891
- package/dist/index.js.map +1 -1
- package/dist/internal/testing.js +2 -2
- package/dist/kv/index.js +7 -6
- package/dist/kv/index.js.map +1 -1
- package/dist/kv/store.d.ts.map +1 -1
- package/dist/node/http.d.ts +2 -0
- package/dist/node/http.d.ts.map +1 -1
- package/dist/node/index.d.ts +1 -1
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +12 -11
- package/dist/node/index.js.map +1 -1
- package/dist/node/serve.d.ts +4 -0
- package/dist/node/serve.d.ts.map +1 -1
- package/dist/redis/index.js +29 -28
- package/dist/redis/index.js.map +1 -1
- package/dist/redis/store.d.ts.map +1 -1
- package/dist/route/serialization.d.ts +4 -4
- package/dist/route/serialization.d.ts.map +1 -1
- package/dist/static/index.d.ts.map +1 -1
- package/dist/static/index.js +255 -255
- package/dist/static/index.js.map +1 -1
- package/dist/static/testing.d.ts +26 -0
- package/dist/static/testing.d.ts.map +1 -0
- package/dist/static/testing.js +76 -0
- package/dist/static/testing.js.map +1 -0
- package/dist/testing/index.d.ts +47 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +99 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/vercel/build.d.ts +31 -0
- package/dist/vercel/build.d.ts.map +1 -0
- package/dist/vercel/config.d.ts +15 -0
- package/dist/vercel/config.d.ts.map +1 -0
- package/dist/vercel/index.d.ts +7 -0
- package/dist/vercel/index.d.ts.map +1 -0
- package/dist/vercel/index.js +11 -0
- package/dist/vercel/index.js.map +1 -0
- package/dist/vercel/runtime.d.ts +29 -0
- package/dist/vercel/runtime.d.ts.map +1 -0
- package/dist/vite/index.js +1 -1
- package/package.json +13 -1
- package/dist/chunks/index-0MNiR_wo.js.map +0 -1
- package/dist/chunks/streaming-GYIsVTQH.js +0 -560
- package/dist/chunks/streaming-GYIsVTQH.js.map +0 -1
- package/dist/chunks/testing-CUwGj2ie.js.map +0 -1
- package/dist/chunks/vite-QoqrGl6f.js.map +0 -1
package/dist/internal/testing.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { h as u, d as b, L as n, b9 as i, C as _, A as r, ba as c, Z as d, Y as h, z as m, bb as R, bc as g, bd as p, be as f, a as F } from "../chunks/testing-
|
|
1
|
+
import { h as u, d as b, L as n, b9 as i, C as _, A as r, ba as c, Z as d, Y as h, z as m, bb as R, bc as g, bd as p, be as f, a as F } from "../chunks/testing-BEW1nchS.js";
|
|
2
2
|
import "react-dom/server";
|
|
3
|
-
import "../chunks/index-
|
|
3
|
+
import "../chunks/index-LYve1rQp.js";
|
|
4
4
|
import "react";
|
|
5
5
|
import "react/jsx-runtime";
|
|
6
6
|
export {
|
package/dist/kv/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { s as
|
|
1
|
+
import { s as A, p as R } from "../chunks/session-store-ChModVcq.js";
|
|
2
2
|
const M = "demiurge:cache:", O = 1e3;
|
|
3
3
|
function T(r) {
|
|
4
4
|
if (!r.namespace || typeof r.namespace.get != "function")
|
|
@@ -38,6 +38,7 @@ function T(r) {
|
|
|
38
38
|
await t.put(s(e), JSON.stringify(a), { expiration: p }), await y(e, a.tags, p);
|
|
39
39
|
}
|
|
40
40
|
return {
|
|
41
|
+
capabilities: { atomicity: "best-effort" },
|
|
41
42
|
async acquireRefreshLease(e, a, l) {
|
|
42
43
|
return await t.get(n(e)) ? !1 : (await t.put(n(e), a, {
|
|
43
44
|
expiration: $(l)
|
|
@@ -130,7 +131,7 @@ function I(r) {
|
|
|
130
131
|
throw new Error(
|
|
131
132
|
"Demiurge KV session store requires atomic compare-and-swap operations. Adapt the provider transaction API to EdgeKvSessionNamespace.atomic."
|
|
132
133
|
);
|
|
133
|
-
const t = r.store, d =
|
|
134
|
+
const t = r.store, d = A(r.namespace), w = `${r.keyPrefix ?? D}${d}:`, f = (c) => w + c;
|
|
134
135
|
return {
|
|
135
136
|
async create(c) {
|
|
136
137
|
const s = P(c, 0);
|
|
@@ -150,7 +151,7 @@ function I(r) {
|
|
|
150
151
|
return;
|
|
151
152
|
const i = K(o);
|
|
152
153
|
if (i) {
|
|
153
|
-
if (
|
|
154
|
+
if (b(i) <= s) {
|
|
154
155
|
await t.atomic([{ expected: o, key: n }]);
|
|
155
156
|
return;
|
|
156
157
|
}
|
|
@@ -197,14 +198,14 @@ function x(r, t, d) {
|
|
|
197
198
|
expected: t,
|
|
198
199
|
key: r,
|
|
199
200
|
write: {
|
|
200
|
-
options: { expiration: Math.ceil(
|
|
201
|
+
options: { expiration: Math.ceil(b(d) / 1e3) },
|
|
201
202
|
value: JSON.stringify(d)
|
|
202
203
|
}
|
|
203
204
|
};
|
|
204
205
|
}
|
|
205
206
|
function K(r) {
|
|
206
207
|
try {
|
|
207
|
-
return
|
|
208
|
+
return R(JSON.parse(r));
|
|
208
209
|
} catch {
|
|
209
210
|
return;
|
|
210
211
|
}
|
|
@@ -212,7 +213,7 @@ function K(r) {
|
|
|
212
213
|
function P(r, t) {
|
|
213
214
|
return structuredClone({ ...r, version: t });
|
|
214
215
|
}
|
|
215
|
-
function
|
|
216
|
+
function b(r) {
|
|
216
217
|
return Math.min(r.expiresAt, r.idleExpiresAt ?? r.expiresAt);
|
|
217
218
|
}
|
|
218
219
|
export {
|
package/dist/kv/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/kv/store.ts","../../src/kv/rate-limit-store.ts","../../src/kv/session-store.ts"],"sourcesContent":["import type { CacheStore, CacheStoreEntry } from \"../data/cache\";\n\n// This type documents the interface an adapter FOR a KV store needs, not a\n// dependency on any vendor SDK. It is modeled closely on the binding API\n// Cloudflare Workers KV exposes, because that shape is the one most other\n// edge KV providers already copy. The application supplies its own\n// connected client matching this interface, the same way\n// `createRedisCacheStore` takes a connected `ioredis` client rather than\n// owning the connection. A Deno KV or Vercel Edge Config client can satisfy\n// this interface with a small wrapper even when its native method names\n// differ.\nexport type EdgeKvListKey = {\n name: string;\n};\n\nexport type EdgeKvListResult = {\n cursor?: string;\n keys: readonly EdgeKvListKey[];\n list_complete: boolean;\n};\n\nexport type EdgeKvPutOptions = {\n // Absolute expiration, in seconds since the Unix epoch. This store always\n // uses `expiration` rather than `expirationTtl` so an entry's lifetime\n // tracks its `staleUntil` timestamp exactly. Redis gets the same result\n // with `PEXPIREAT` instead of a relative `PEXPIRE`.\n expiration?: number;\n};\n\nexport type EdgeKvNamespace = {\n delete: (key: string) => Promise<void>;\n get: (key: string) => Promise<string | null>;\n list: (options?: {\n cursor?: string;\n limit?: number;\n prefix?: string;\n }) => Promise<EdgeKvListResult>;\n put: (key: string, value: string, options?: EdgeKvPutOptions) => Promise<void>;\n};\n\nexport type KvCacheStoreOptions = {\n // Every consumer of one KV namespace shares one keyspace. This prefix\n // scopes the store's entry, tag, and lease keys away from an\n // application's other use of the same namespace. Two Demiurge\n // deployments that share a namespace still need distinct prefixes, the\n // same way they need distinct cache namespaces.\n keyPrefix?: string;\n // The store issues operations on a client the application already\n // constructed and bound. Which provider, region, and namespace ID back\n // it is the application's decision, not this store's.\n namespace: EdgeKvNamespace;\n};\n\nconst defaultKeyPrefix = \"demiurge:cache:\";\nconst listPageLimit = 1000;\n\n// KV stores give up two things Redis's Lua scripts provided: cross-key\n// atomicity and a compare-and-swap primitive. Every write below is\n// therefore several sequential operations rather than one atomic script.\n//\n// Concretely, `set()`, `delete()`, and `publishRefresh()` each read the\n// previous entry, then write tag membership changes and the entry itself as\n// separate operations. A reader racing with one of these calls can briefly\n// see an entry whose tag memberships have not finished updating. It can\n// also see a tag membership entry whose backing cache entry is already\n// gone. `invalidateTags()` tolerates the latter case. It treats a\n// membership entry with no backing cache entry as already invalid and\n// simply removes it.\n//\n// `acquireRefreshLease()` and `publishRefresh()` implement single-writer\n// coordination with a get-then-write pattern, not a real compare-and-swap.\n// Two isolates that call `acquireRefreshLease()` within the same\n// observation window can both see no lease and both write one. The worst\n// case is redundant refresh work, the same failure mode\n// `staleWhileRevalidate` already tolerates when no coordination exists at\n// all. Do not rely on this store for exclusive-execution correctness\n// beyond that. The Redis store's atomic Lua scripts give a stronger\n// guarantee where that matters.\n//\n// Tag invalidation lists membership keys by prefix and deletes each\n// matching entry. `list()` on a real KV store is typically eventually\n// consistent. A membership entry written just before a matching `list()`\n// call may not appear on that call. A store that needs read-your-writes\n// tag invalidation should use the Redis store instead.\nexport function createKvCacheStore(options: KvCacheStoreOptions): CacheStore {\n if (!options.namespace || typeof options.namespace.get !== \"function\") {\n throw new Error(\n \"Demiurge KV cache store requires an EdgeKvNamespace. Pass a connected KV client matching the get/put/delete/list interface documented on EdgeKvNamespace.\",\n );\n }\n\n const kv = options.namespace;\n const prefix = options.keyPrefix ?? defaultKeyPrefix;\n const entryPrefix = `${prefix}entry:`;\n const tagPrefix = `${prefix}tag:`;\n const leasePrefix = `${prefix}lease:`;\n\n function entryKey(key: string) {\n return entryPrefix + key;\n }\n\n function leaseKey(key: string) {\n return leasePrefix + key;\n }\n\n function tagMemberKey(tagId: string, key: string) {\n return `${tagPrefix}${tagId}:${key}`;\n }\n\n async function readEntry(key: string): Promise<CacheStoreEntry | undefined> {\n const raw = await kv.get(entryKey(key));\n // TYPE-EVIDENCE: the stored raw value is JSON that the store serialized from a cache entry. The cast restores that shape.\n return raw ? (JSON.parse(raw) as CacheStoreEntry) : undefined;\n }\n\n async function removeTagMemberships(key: string, tags: readonly string[]) {\n await Promise.all(\n tags.map((tagId) => kv.delete(tagMemberKey(tagId, key))),\n );\n }\n\n async function addTagMemberships(\n key: string,\n tags: readonly string[],\n expiration: number | undefined,\n ) {\n await Promise.all(\n tags.map((tagId) =>\n kv.put(tagMemberKey(tagId, key), \"1\", { expiration })\n ),\n );\n }\n\n async function writeEntry(key: string, entry: CacheStoreEntry) {\n const previous = await readEntry(key);\n\n if (previous) {\n await removeTagMemberships(key, previous.tags);\n }\n\n const expiration = entryExpiration(entry);\n await kv.put(entryKey(key), JSON.stringify(entry), { expiration });\n await addTagMemberships(key, entry.tags, expiration);\n }\n\n return {\n async acquireRefreshLease(key, token, expiresAt) {\n const existing = await kv.get(leaseKey(key));\n\n if (existing) {\n return false;\n }\n\n await kv.put(leaseKey(key), token, {\n expiration: toExpirationSeconds(expiresAt),\n });\n\n return true;\n },\n async delete(key) {\n const previous = await readEntry(key);\n await kv.delete(leaseKey(key));\n\n if (!previous) {\n return false;\n }\n\n await removeTagMemberships(key, previous.tags);\n await kv.delete(entryKey(key));\n\n return true;\n },\n async get(key) {\n return await readEntry(key);\n },\n async invalidateTags(tags) {\n if (tags.length === 0) {\n return 0;\n }\n\n const seen = new Set<string>();\n let deleted = 0;\n\n for (const tagId of tags) {\n const membershipPrefix = tagMemberKey(tagId, \"\");\n let cursor: string | undefined;\n\n do {\n const page = await kv.list({\n cursor,\n limit: listPageLimit,\n prefix: membershipPrefix,\n });\n\n for (const listedKey of page.keys) {\n const rawKey = listedKey.name.slice(membershipPrefix.length);\n\n if (seen.has(rawKey)) {\n continue;\n }\n\n seen.add(rawKey);\n\n const entry = await readEntry(rawKey);\n\n if (!entry) {\n await kv.delete(listedKey.name);\n continue;\n }\n\n await removeTagMemberships(rawKey, entry.tags);\n await kv.delete(entryKey(rawKey));\n await kv.delete(leaseKey(rawKey));\n deleted += 1;\n }\n\n cursor = page.list_complete ? undefined : page.cursor;\n } while (cursor);\n }\n\n return deleted;\n },\n async publishRefresh(key, token, entry) {\n const owner = await kv.get(leaseKey(key));\n\n if (owner !== token) {\n return false;\n }\n\n await writeEntry(key, entry);\n await kv.delete(leaseKey(key));\n\n return true;\n },\n async releaseRefreshLease(key, token) {\n const owner = await kv.get(leaseKey(key));\n\n if (owner === token) {\n await kv.delete(leaseKey(key));\n }\n },\n async set(key, entry) {\n await writeEntry(key, entry);\n await kv.delete(leaseKey(key));\n },\n };\n}\n\nfunction entryExpiration(entry: CacheStoreEntry) {\n return entry.staleUntil === null\n ? undefined\n : toExpirationSeconds(entry.staleUntil);\n}\n\nfunction toExpirationSeconds(epochMilliseconds: number) {\n return Math.ceil(epochMilliseconds / 1000);\n}\n","import type { RateLimitStore } from \"../security/types\";\nimport type { EdgeKvNamespace } from \"./store\";\n\nexport type KvRateLimitStoreOptions = {\n // Every consumer of one KV namespace shares one keyspace. This prefix\n // scopes the store's counter keys away from an application's other use of\n // the same namespace, the same way `KvCacheStoreOptions.keyPrefix` scopes\n // `createKvCacheStore`'s keys.\n keyPrefix?: string;\n // The store issues operations on a client the application already\n // constructed and bound. Which provider, region, and namespace ID back\n // it is the application's decision, not this store's.\n namespace: EdgeKvNamespace;\n};\n\ntype KvRateLimitEntry = {\n count: number;\n resetAt: number;\n};\n\nconst defaultKeyPrefix = \"demiurge:rate-limit:\";\n\n// KV gives up the same thing here that `createKvCacheStore` gives up:\n// cross-operation atomicity. Plain KV has no compare-and-swap, so\n// `increment()` is necessarily a get-then-write, not a single atomic\n// operation. Two requests that call `increment()` within the same\n// observation window can both read the same prior count. Both then write\n// `count + 1`, so one increment is lost. The store can therefore undercount\n// under concurrent requests in that narrow window. It cannot overcount, and\n// it never blocks a request that should have been limited by more than one\n// request's worth of slack. Treat this as a soft limiter suitable for\n// coarse abuse protection, not an exact one. The Redis store's atomic Lua\n// script gives a stronger guarantee where that matters.\nexport function createKvRateLimitStore(\n options: KvRateLimitStoreOptions,\n): RateLimitStore {\n if (!options.namespace || typeof options.namespace.get !== \"function\") {\n throw new Error(\n \"Demiurge KV rate limit store requires an EdgeKvNamespace. Pass a connected KV client matching the get/put/delete/list interface documented on EdgeKvNamespace.\",\n );\n }\n\n const kv = options.namespace;\n const prefix = options.keyPrefix ?? defaultKeyPrefix;\n\n function counterKey(key: string) {\n return `${prefix}${key}`;\n }\n\n return {\n async increment(key, windowMs, now) {\n const storeKey = counterKey(key);\n const raw = await kv.get(storeKey);\n // TYPE-EVIDENCE: the stored raw value is JSON that the store serialized as a rate limit entry. The cast restores that shape.\n const existing = raw ? (JSON.parse(raw) as KvRateLimitEntry) : undefined;\n\n const next: KvRateLimitEntry = !existing || existing.resetAt <= now\n ? { count: 1, resetAt: now + windowMs }\n : { count: existing.count + 1, resetAt: existing.resetAt };\n\n await kv.put(storeKey, JSON.stringify(next), {\n expiration: toExpirationSeconds(next.resetAt),\n });\n\n return next;\n },\n };\n}\n\nfunction toExpirationSeconds(epochMilliseconds: number) {\n return Math.ceil(epochMilliseconds / 1000);\n}\n","import {\n parseSessionRecord,\n serializeSessionNamespace,\n type SessionData,\n type SessionNamespace,\n type SessionRecord,\n type SessionStore,\n type SessionStoreCandidate,\n} from \"../security/session-store\";\nimport type { EdgeKvNamespace, EdgeKvPutOptions } from \"./store\";\n\nexport type EdgeKvAtomicOperation = {\n expected: string | null;\n key: string;\n write?: {\n options?: EdgeKvPutOptions;\n value: string;\n };\n};\n\nexport type EdgeKvSessionNamespace = EdgeKvNamespace & {\n atomic: (operations: readonly EdgeKvAtomicOperation[]) => Promise<boolean>;\n};\n\nexport type KvSessionStoreOptions = {\n keyPrefix?: string;\n namespace: SessionNamespace;\n store: EdgeKvSessionNamespace;\n};\n\nconst defaultKeyPrefix = \"demiurge:session:\";\n\nexport function createKvSessionStore<\n TData extends SessionData = SessionData,\n>(options: KvSessionStoreOptions): SessionStore<TData> {\n if (!options.store || typeof options.store.get !== \"function\") {\n throw new Error(\n \"Demiurge KV session store requires an EdgeKvSessionNamespace. Pass an application-owned KV client as `store`.\",\n );\n }\n\n if (typeof options.store.atomic !== \"function\") {\n throw new Error(\n \"Demiurge KV session store requires atomic compare-and-swap operations. Adapt the provider transaction API to EdgeKvSessionNamespace.atomic.\",\n );\n }\n\n const store = options.store;\n const namespace = serializeSessionNamespace(options.namespace);\n const prefix = `${options.keyPrefix ?? defaultKeyPrefix}${namespace}:`;\n const key = (id: string) => prefix + id;\n\n return {\n async create(candidate) {\n const record = toRecord(candidate, 0);\n\n try {\n const stored = await store.atomic([putOperation(key(record.id), null, record)]);\n return stored\n ? { record, status: \"stored\" }\n : { status: \"conflict\" };\n } catch {\n return { status: \"unavailable\" };\n }\n },\n async destroy(id) {\n const storageKey = key(id);\n const current = await store.get(storageKey);\n\n if (current === null) {\n return false;\n }\n\n return await store.atomic([{ expected: current, key: storageKey }]);\n },\n async read(id, now) {\n const storageKey = key(id);\n const raw = await store.get(storageKey);\n\n if (raw === null) {\n return undefined;\n }\n\n const record = parseRecord<TData>(raw);\n\n if (!record) {\n return undefined;\n }\n\n if (storeExpiration(record) <= now) {\n await store.atomic([{ expected: raw, key: storageKey }]);\n return undefined;\n }\n\n return record;\n },\n async rotate(currentId, candidate, expectedVersion) {\n const currentKey = key(currentId);\n const nextKey = key(candidate.id);\n const [current, next] = await Promise.all([\n store.get(currentKey),\n currentKey === nextKey ? Promise.resolve(null) : store.get(nextKey),\n ]);\n\n if (!current || (nextKey !== currentKey && next !== null)) {\n return { status: \"conflict\" };\n }\n\n const currentRecord = parseRecord<TData>(current);\n\n if (!currentRecord || currentRecord.version !== expectedVersion) {\n return { status: \"conflict\" };\n }\n\n const record = toRecord(candidate, expectedVersion + 1);\n const operations = currentKey === nextKey\n ? [putOperation(currentKey, current, record)]\n : [\n { expected: current, key: currentKey },\n putOperation(nextKey, null, record),\n ];\n\n try {\n const stored = await store.atomic(operations);\n return stored\n ? { record, status: \"stored\" }\n : { status: \"conflict\" };\n } catch {\n return { status: \"unavailable\" };\n }\n },\n async update(candidate, expectedVersion) {\n const storageKey = key(candidate.id);\n const current = await store.get(storageKey);\n\n const currentRecord = current ? parseRecord<TData>(current) : undefined;\n\n if (!currentRecord || currentRecord.version !== expectedVersion) {\n return { status: \"conflict\" };\n }\n\n const record = toRecord(candidate, expectedVersion + 1);\n\n try {\n const stored = await store.atomic([\n putOperation(storageKey, current, record),\n ]);\n return stored\n ? { record, status: \"stored\" }\n : { status: \"conflict\" };\n } catch {\n return { status: \"unavailable\" };\n }\n },\n };\n}\n\nfunction putOperation(\n key: string,\n expected: string | null,\n record: SessionRecord,\n): EdgeKvAtomicOperation {\n return {\n expected,\n key,\n write: {\n options: { expiration: Math.ceil(storeExpiration(record) / 1000) },\n value: JSON.stringify(record),\n },\n };\n}\n\nfunction parseRecord<TData extends SessionData>(\n raw: string,\n): SessionRecord<TData> | undefined {\n try {\n return parseSessionRecord<TData>(JSON.parse(raw));\n } catch {\n return undefined;\n }\n}\n\nfunction toRecord<TData extends SessionData>(\n candidate: SessionStoreCandidate<TData>,\n version: number,\n): SessionRecord<TData> {\n return structuredClone({ ...candidate, version });\n}\n\nfunction storeExpiration(record: SessionRecord) {\n return Math.min(record.expiresAt, record.idleExpiresAt ?? record.expiresAt);\n}\n"],"names":["defaultKeyPrefix","listPageLimit","createKvCacheStore","options","kv","prefix","entryPrefix","tagPrefix","leasePrefix","entryKey","key","leaseKey","tagMemberKey","tagId","readEntry","raw","removeTagMemberships","tags","addTagMemberships","expiration","writeEntry","entry","previous","entryExpiration","token","expiresAt","toExpirationSeconds","seen","deleted","membershipPrefix","cursor","page","listedKey","rawKey","epochMilliseconds","createKvRateLimitStore","counterKey","windowMs","now","storeKey","existing","next","createKvSessionStore","store","namespace","serializeSessionNamespace","id","candidate","record","toRecord","putOperation","storageKey","current","parseRecord","storeExpiration","currentId","expectedVersion","currentKey","nextKey","currentRecord","operations","expected","parseSessionRecord","version"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/kv/store.ts","../../src/kv/rate-limit-store.ts","../../src/kv/session-store.ts"],"sourcesContent":["import type { CacheStore, CacheStoreEntry } from \"../data/cache\";\n\n// This type documents the interface an adapter FOR a KV store needs, not a\n// dependency on any vendor SDK. It is modeled closely on the binding API\n// Cloudflare Workers KV exposes, because that shape is the one most other\n// edge KV providers already copy. The application supplies its own\n// connected client matching this interface, the same way\n// `createRedisCacheStore` takes a connected `ioredis` client rather than\n// owning the connection. A Deno KV or Vercel Edge Config client can satisfy\n// this interface with a small wrapper even when its native method names\n// differ.\nexport type EdgeKvListKey = {\n name: string;\n};\n\nexport type EdgeKvListResult = {\n cursor?: string;\n keys: readonly EdgeKvListKey[];\n list_complete: boolean;\n};\n\nexport type EdgeKvPutOptions = {\n // Absolute expiration, in seconds since the Unix epoch. This store always\n // uses `expiration` rather than `expirationTtl` so an entry's lifetime\n // tracks its `staleUntil` timestamp exactly. Redis gets the same result\n // with `PEXPIREAT` instead of a relative `PEXPIRE`.\n expiration?: number;\n};\n\nexport type EdgeKvNamespace = {\n delete: (key: string) => Promise<void>;\n get: (key: string) => Promise<string | null>;\n list: (options?: {\n cursor?: string;\n limit?: number;\n prefix?: string;\n }) => Promise<EdgeKvListResult>;\n put: (key: string, value: string, options?: EdgeKvPutOptions) => Promise<void>;\n};\n\nexport type KvCacheStoreOptions = {\n // Every consumer of one KV namespace shares one keyspace. This prefix\n // scopes the store's entry, tag, and lease keys away from an\n // application's other use of the same namespace. Two Demiurge\n // deployments that share a namespace still need distinct prefixes, the\n // same way they need distinct cache namespaces.\n keyPrefix?: string;\n // The store issues operations on a client the application already\n // constructed and bound. Which provider, region, and namespace ID back\n // it is the application's decision, not this store's.\n namespace: EdgeKvNamespace;\n};\n\nconst defaultKeyPrefix = \"demiurge:cache:\";\nconst listPageLimit = 1000;\n\n// KV stores give up two things Redis's Lua scripts provided: cross-key\n// atomicity and a compare-and-swap primitive. Every write below is\n// therefore several sequential operations rather than one atomic script.\n//\n// Concretely, `set()`, `delete()`, and `publishRefresh()` each read the\n// previous entry, then write tag membership changes and the entry itself as\n// separate operations. A reader racing with one of these calls can briefly\n// see an entry whose tag memberships have not finished updating. It can\n// also see a tag membership entry whose backing cache entry is already\n// gone. `invalidateTags()` tolerates the latter case. It treats a\n// membership entry with no backing cache entry as already invalid and\n// simply removes it.\n//\n// `acquireRefreshLease()` and `publishRefresh()` implement single-writer\n// coordination with a get-then-write pattern, not a real compare-and-swap.\n// Two isolates that call `acquireRefreshLease()` within the same\n// observation window can both see no lease and both write one. The worst\n// case is redundant refresh work, the same failure mode\n// `staleWhileRevalidate` already tolerates when no coordination exists at\n// all. Do not rely on this store for exclusive-execution correctness\n// beyond that. The Redis store's atomic Lua scripts give a stronger\n// guarantee where that matters.\n//\n// Tag invalidation lists membership keys by prefix and deletes each\n// matching entry. `list()` on a real KV store is typically eventually\n// consistent. A membership entry written just before a matching `list()`\n// call may not appear on that call. A store that needs read-your-writes\n// tag invalidation should use the Redis store instead.\nexport function createKvCacheStore(options: KvCacheStoreOptions): CacheStore {\n if (!options.namespace || typeof options.namespace.get !== \"function\") {\n throw new Error(\n \"Demiurge KV cache store requires an EdgeKvNamespace. Pass a connected KV client matching the get/put/delete/list interface documented on EdgeKvNamespace.\",\n );\n }\n\n const kv = options.namespace;\n const prefix = options.keyPrefix ?? defaultKeyPrefix;\n const entryPrefix = `${prefix}entry:`;\n const tagPrefix = `${prefix}tag:`;\n const leasePrefix = `${prefix}lease:`;\n\n function entryKey(key: string) {\n return entryPrefix + key;\n }\n\n function leaseKey(key: string) {\n return leasePrefix + key;\n }\n\n function tagMemberKey(tagId: string, key: string) {\n return `${tagPrefix}${tagId}:${key}`;\n }\n\n async function readEntry(key: string): Promise<CacheStoreEntry | undefined> {\n const raw = await kv.get(entryKey(key));\n // TYPE-EVIDENCE: the stored raw value is JSON that the store serialized from a cache entry. The cast restores that shape.\n return raw ? (JSON.parse(raw) as CacheStoreEntry) : undefined;\n }\n\n async function removeTagMemberships(key: string, tags: readonly string[]) {\n await Promise.all(\n tags.map((tagId) => kv.delete(tagMemberKey(tagId, key))),\n );\n }\n\n async function addTagMemberships(\n key: string,\n tags: readonly string[],\n expiration: number | undefined,\n ) {\n await Promise.all(\n tags.map((tagId) =>\n kv.put(tagMemberKey(tagId, key), \"1\", { expiration })\n ),\n );\n }\n\n async function writeEntry(key: string, entry: CacheStoreEntry) {\n const previous = await readEntry(key);\n\n if (previous) {\n await removeTagMemberships(key, previous.tags);\n }\n\n const expiration = entryExpiration(entry);\n await kv.put(entryKey(key), JSON.stringify(entry), { expiration });\n await addTagMemberships(key, entry.tags, expiration);\n }\n\n return {\n capabilities: { atomicity: \"best-effort\" },\n async acquireRefreshLease(key, token, expiresAt) {\n const existing = await kv.get(leaseKey(key));\n\n if (existing) {\n return false;\n }\n\n await kv.put(leaseKey(key), token, {\n expiration: toExpirationSeconds(expiresAt),\n });\n\n return true;\n },\n async delete(key) {\n const previous = await readEntry(key);\n await kv.delete(leaseKey(key));\n\n if (!previous) {\n return false;\n }\n\n await removeTagMemberships(key, previous.tags);\n await kv.delete(entryKey(key));\n\n return true;\n },\n async get(key) {\n return await readEntry(key);\n },\n async invalidateTags(tags) {\n if (tags.length === 0) {\n return 0;\n }\n\n const seen = new Set<string>();\n let deleted = 0;\n\n for (const tagId of tags) {\n const membershipPrefix = tagMemberKey(tagId, \"\");\n let cursor: string | undefined;\n\n do {\n const page = await kv.list({\n cursor,\n limit: listPageLimit,\n prefix: membershipPrefix,\n });\n\n for (const listedKey of page.keys) {\n const rawKey = listedKey.name.slice(membershipPrefix.length);\n\n if (seen.has(rawKey)) {\n continue;\n }\n\n seen.add(rawKey);\n\n const entry = await readEntry(rawKey);\n\n if (!entry) {\n await kv.delete(listedKey.name);\n continue;\n }\n\n await removeTagMemberships(rawKey, entry.tags);\n await kv.delete(entryKey(rawKey));\n await kv.delete(leaseKey(rawKey));\n deleted += 1;\n }\n\n cursor = page.list_complete ? undefined : page.cursor;\n } while (cursor);\n }\n\n return deleted;\n },\n async publishRefresh(key, token, entry) {\n const owner = await kv.get(leaseKey(key));\n\n if (owner !== token) {\n return false;\n }\n\n await writeEntry(key, entry);\n await kv.delete(leaseKey(key));\n\n return true;\n },\n async releaseRefreshLease(key, token) {\n const owner = await kv.get(leaseKey(key));\n\n if (owner === token) {\n await kv.delete(leaseKey(key));\n }\n },\n async set(key, entry) {\n await writeEntry(key, entry);\n await kv.delete(leaseKey(key));\n },\n };\n}\n\nfunction entryExpiration(entry: CacheStoreEntry) {\n return entry.staleUntil === null\n ? undefined\n : toExpirationSeconds(entry.staleUntil);\n}\n\nfunction toExpirationSeconds(epochMilliseconds: number) {\n return Math.ceil(epochMilliseconds / 1000);\n}\n","import type { RateLimitStore } from \"../security/types\";\nimport type { EdgeKvNamespace } from \"./store\";\n\nexport type KvRateLimitStoreOptions = {\n // Every consumer of one KV namespace shares one keyspace. This prefix\n // scopes the store's counter keys away from an application's other use of\n // the same namespace, the same way `KvCacheStoreOptions.keyPrefix` scopes\n // `createKvCacheStore`'s keys.\n keyPrefix?: string;\n // The store issues operations on a client the application already\n // constructed and bound. Which provider, region, and namespace ID back\n // it is the application's decision, not this store's.\n namespace: EdgeKvNamespace;\n};\n\ntype KvRateLimitEntry = {\n count: number;\n resetAt: number;\n};\n\nconst defaultKeyPrefix = \"demiurge:rate-limit:\";\n\n// KV gives up the same thing here that `createKvCacheStore` gives up:\n// cross-operation atomicity. Plain KV has no compare-and-swap, so\n// `increment()` is necessarily a get-then-write, not a single atomic\n// operation. Two requests that call `increment()` within the same\n// observation window can both read the same prior count. Both then write\n// `count + 1`, so one increment is lost. The store can therefore undercount\n// under concurrent requests in that narrow window. It cannot overcount, and\n// it never blocks a request that should have been limited by more than one\n// request's worth of slack. Treat this as a soft limiter suitable for\n// coarse abuse protection, not an exact one. The Redis store's atomic Lua\n// script gives a stronger guarantee where that matters.\nexport function createKvRateLimitStore(\n options: KvRateLimitStoreOptions,\n): RateLimitStore {\n if (!options.namespace || typeof options.namespace.get !== \"function\") {\n throw new Error(\n \"Demiurge KV rate limit store requires an EdgeKvNamespace. Pass a connected KV client matching the get/put/delete/list interface documented on EdgeKvNamespace.\",\n );\n }\n\n const kv = options.namespace;\n const prefix = options.keyPrefix ?? defaultKeyPrefix;\n\n function counterKey(key: string) {\n return `${prefix}${key}`;\n }\n\n return {\n async increment(key, windowMs, now) {\n const storeKey = counterKey(key);\n const raw = await kv.get(storeKey);\n // TYPE-EVIDENCE: the stored raw value is JSON that the store serialized as a rate limit entry. The cast restores that shape.\n const existing = raw ? (JSON.parse(raw) as KvRateLimitEntry) : undefined;\n\n const next: KvRateLimitEntry = !existing || existing.resetAt <= now\n ? { count: 1, resetAt: now + windowMs }\n : { count: existing.count + 1, resetAt: existing.resetAt };\n\n await kv.put(storeKey, JSON.stringify(next), {\n expiration: toExpirationSeconds(next.resetAt),\n });\n\n return next;\n },\n };\n}\n\nfunction toExpirationSeconds(epochMilliseconds: number) {\n return Math.ceil(epochMilliseconds / 1000);\n}\n","import {\n parseSessionRecord,\n serializeSessionNamespace,\n type SessionData,\n type SessionNamespace,\n type SessionRecord,\n type SessionStore,\n type SessionStoreCandidate,\n} from \"../security/session-store\";\nimport type { EdgeKvNamespace, EdgeKvPutOptions } from \"./store\";\n\nexport type EdgeKvAtomicOperation = {\n expected: string | null;\n key: string;\n write?: {\n options?: EdgeKvPutOptions;\n value: string;\n };\n};\n\nexport type EdgeKvSessionNamespace = EdgeKvNamespace & {\n atomic: (operations: readonly EdgeKvAtomicOperation[]) => Promise<boolean>;\n};\n\nexport type KvSessionStoreOptions = {\n keyPrefix?: string;\n namespace: SessionNamespace;\n store: EdgeKvSessionNamespace;\n};\n\nconst defaultKeyPrefix = \"demiurge:session:\";\n\nexport function createKvSessionStore<\n TData extends SessionData = SessionData,\n>(options: KvSessionStoreOptions): SessionStore<TData> {\n if (!options.store || typeof options.store.get !== \"function\") {\n throw new Error(\n \"Demiurge KV session store requires an EdgeKvSessionNamespace. Pass an application-owned KV client as `store`.\",\n );\n }\n\n if (typeof options.store.atomic !== \"function\") {\n throw new Error(\n \"Demiurge KV session store requires atomic compare-and-swap operations. Adapt the provider transaction API to EdgeKvSessionNamespace.atomic.\",\n );\n }\n\n const store = options.store;\n const namespace = serializeSessionNamespace(options.namespace);\n const prefix = `${options.keyPrefix ?? defaultKeyPrefix}${namespace}:`;\n const key = (id: string) => prefix + id;\n\n return {\n async create(candidate) {\n const record = toRecord(candidate, 0);\n\n try {\n const stored = await store.atomic([putOperation(key(record.id), null, record)]);\n return stored\n ? { record, status: \"stored\" }\n : { status: \"conflict\" };\n } catch {\n return { status: \"unavailable\" };\n }\n },\n async destroy(id) {\n const storageKey = key(id);\n const current = await store.get(storageKey);\n\n if (current === null) {\n return false;\n }\n\n return await store.atomic([{ expected: current, key: storageKey }]);\n },\n async read(id, now) {\n const storageKey = key(id);\n const raw = await store.get(storageKey);\n\n if (raw === null) {\n return undefined;\n }\n\n const record = parseRecord<TData>(raw);\n\n if (!record) {\n return undefined;\n }\n\n if (storeExpiration(record) <= now) {\n await store.atomic([{ expected: raw, key: storageKey }]);\n return undefined;\n }\n\n return record;\n },\n async rotate(currentId, candidate, expectedVersion) {\n const currentKey = key(currentId);\n const nextKey = key(candidate.id);\n const [current, next] = await Promise.all([\n store.get(currentKey),\n currentKey === nextKey ? Promise.resolve(null) : store.get(nextKey),\n ]);\n\n if (!current || (nextKey !== currentKey && next !== null)) {\n return { status: \"conflict\" };\n }\n\n const currentRecord = parseRecord<TData>(current);\n\n if (!currentRecord || currentRecord.version !== expectedVersion) {\n return { status: \"conflict\" };\n }\n\n const record = toRecord(candidate, expectedVersion + 1);\n const operations = currentKey === nextKey\n ? [putOperation(currentKey, current, record)]\n : [\n { expected: current, key: currentKey },\n putOperation(nextKey, null, record),\n ];\n\n try {\n const stored = await store.atomic(operations);\n return stored\n ? { record, status: \"stored\" }\n : { status: \"conflict\" };\n } catch {\n return { status: \"unavailable\" };\n }\n },\n async update(candidate, expectedVersion) {\n const storageKey = key(candidate.id);\n const current = await store.get(storageKey);\n\n const currentRecord = current ? parseRecord<TData>(current) : undefined;\n\n if (!currentRecord || currentRecord.version !== expectedVersion) {\n return { status: \"conflict\" };\n }\n\n const record = toRecord(candidate, expectedVersion + 1);\n\n try {\n const stored = await store.atomic([\n putOperation(storageKey, current, record),\n ]);\n return stored\n ? { record, status: \"stored\" }\n : { status: \"conflict\" };\n } catch {\n return { status: \"unavailable\" };\n }\n },\n };\n}\n\nfunction putOperation(\n key: string,\n expected: string | null,\n record: SessionRecord,\n): EdgeKvAtomicOperation {\n return {\n expected,\n key,\n write: {\n options: { expiration: Math.ceil(storeExpiration(record) / 1000) },\n value: JSON.stringify(record),\n },\n };\n}\n\nfunction parseRecord<TData extends SessionData>(\n raw: string,\n): SessionRecord<TData> | undefined {\n try {\n return parseSessionRecord<TData>(JSON.parse(raw));\n } catch {\n return undefined;\n }\n}\n\nfunction toRecord<TData extends SessionData>(\n candidate: SessionStoreCandidate<TData>,\n version: number,\n): SessionRecord<TData> {\n return structuredClone({ ...candidate, version });\n}\n\nfunction storeExpiration(record: SessionRecord) {\n return Math.min(record.expiresAt, record.idleExpiresAt ?? record.expiresAt);\n}\n"],"names":["defaultKeyPrefix","listPageLimit","createKvCacheStore","options","kv","prefix","entryPrefix","tagPrefix","leasePrefix","entryKey","key","leaseKey","tagMemberKey","tagId","readEntry","raw","removeTagMemberships","tags","addTagMemberships","expiration","writeEntry","entry","previous","entryExpiration","token","expiresAt","toExpirationSeconds","seen","deleted","membershipPrefix","cursor","page","listedKey","rawKey","epochMilliseconds","createKvRateLimitStore","counterKey","windowMs","now","storeKey","existing","next","createKvSessionStore","store","namespace","serializeSessionNamespace","id","candidate","record","toRecord","putOperation","storageKey","current","parseRecord","storeExpiration","currentId","expectedVersion","currentKey","nextKey","currentRecord","operations","expected","parseSessionRecord","version"],"mappings":"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"}
|
package/dist/kv/store.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/kv/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAmB,MAAM,eAAe,CAAC;AAWjE,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,SAAS,aAAa,EAAE,CAAC;IAC/B,aAAa,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAK7B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAChC,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChF,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAMhC,SAAS,CAAC,EAAE,MAAM,CAAC;IAInB,SAAS,EAAE,eAAe,CAAC;CAC5B,CAAC;AAiCF,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,UAAU,
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/kv/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAmB,MAAM,eAAe,CAAC;AAWjE,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,SAAS,aAAa,EAAE,CAAC;IAC/B,aAAa,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAK7B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAChC,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChF,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAMhC,SAAS,CAAC,EAAE,MAAM,CAAC;IAInB,SAAS,EAAE,eAAe,CAAC;CAC5B,CAAC;AAiCF,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,UAAU,CAmK3E"}
|
package/dist/node/http.d.ts
CHANGED
package/dist/node/http.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/node/http.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,mBAAmB,EACnB,eAAe,EACf,cAAc,EACf,MAAM,WAAW,CAAC;AAGnB,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC;AAE1C,MAAM,MAAM,gBAAgB,GAAG;IAC7B,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,gBAAgB,GAAG;IAGnD,IAAI,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/node/http.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,mBAAmB,EACnB,eAAe,EACf,cAAc,EACf,MAAM,WAAW,CAAC;AAGnB,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC;AAE1C,MAAM,MAAM,gBAAgB,GAAG;IAC7B,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,gBAAgB,GAAG;IAGnD,IAAI,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;IAGvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,UAAU,GAClB,KAAK,GACL;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC;AAclC,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,MAAM,EAAE,MAAM,CAAC;gBAEH,MAAM,EAAE,MAAM;CAK3B;AAED,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,IAAI,EAAE,MAAM,CAAC;gBAED,IAAI,EAAE,MAAM;CAKzB;AAED,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,MAAM,EAAE,MAAM,CAAC;gBAEH,MAAM,EAAE,MAAM;CAU3B;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,gBAAgB,QA+BhE;AAED,wBAAgB,YAAY,CAC1B,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,mBAAmB,WA0E7B;AAwZD,wBAAgB,mBAAmB,CAAC,cAAc,EAAE,cAAc,QAIjE;AAED,wBAAsB,gBAAgB,CACpC,cAAc,EAAE,cAAc,EAC9B,WAAW,EAAE,QAAQ,iBAiDtB;AAsBD,wBAAgB,SAAS,CAAC,OAAO,EAAE,mBAAmB,WAiBrD"}
|
package/dist/node/index.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export { createImageOptimizer } from "./image";
|
|
|
8
8
|
export type { ImageOptimizerFetch, ImageOptimizerOptions, } from "./image";
|
|
9
9
|
export { createStaticFileHandler } from "./static";
|
|
10
10
|
export { defaultNodeBuildClientDir, defaultNodeBuildReadyPath, serveNodeBuild, } from "./serve";
|
|
11
|
-
export type { NodeBuildContext, NodeBuildPageOptions, ServeNodeBuildEnvironment, ServeNodeBuildOptions, } from "./serve";
|
|
11
|
+
export type { NodeBuildContext, NodeBuildPageOptions, NodeBuildRuntime, ServeNodeBuildEnvironment, ServeNodeBuildOptions, } from "./serve";
|
|
12
12
|
export { renderNodePageResponse, renderStreamingPageResponse, } from "./streaming";
|
|
13
13
|
export type { HttpScheme, NodeOriginPolicy, ToWebRequestOptions, TrustProxy, } from "./http";
|
|
14
14
|
export type { StaticFileHandler, StaticFileHandlerOptions } from "./static";
|
package/dist/node/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/node/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/node/index.ts"],"names":[],"mappings":"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"}
|
package/dist/node/index.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { createServer as H } from "node:http";
|
|
2
|
-
import { d as C } from "../chunks/index-
|
|
3
|
-
import { r as I, v as
|
|
4
|
-
import { C as _e, c as je, d as Be
|
|
2
|
+
import { d as C } from "../chunks/index-LYve1rQp.js";
|
|
3
|
+
import { r as I, v as x, t as L, U as A, w as F, a as M, b as k } from "../chunks/streaming-BlKlC3nw.js";
|
|
4
|
+
import { C as _e, c as je, d as Be } from "../chunks/streaming-BlKlC3nw.js";
|
|
5
5
|
import { constants as S } from "node:fs";
|
|
6
6
|
import { realpath as T, open as W, lstat as D, readFile as $ } from "node:fs/promises";
|
|
7
7
|
import { resolve as P, sep as y, extname as U, relative as _, join as O } from "node:path";
|
|
8
8
|
import { Readable as E } from "node:stream";
|
|
9
9
|
import { I as j, R as B, i as z, c as q } from "../chunks/static-files-ehaD1kE7.js";
|
|
10
|
+
import { c as qe, a as Ge } from "../chunks/image-CRerJ5BY.js";
|
|
10
11
|
import { fileURLToPath as G } from "node:url";
|
|
11
12
|
import { p as V } from "../chunks/manifest-vO6c0G0W.js";
|
|
12
13
|
const Y = /* @__PURE__ */ new Set(["demiurge-manifest.json", "index.html"]);
|
|
@@ -260,7 +261,7 @@ const De = C({
|
|
|
260
261
|
requestTimeout: 3e5
|
|
261
262
|
}, ye = 3e4;
|
|
262
263
|
function pe(e) {
|
|
263
|
-
|
|
264
|
+
x(e);
|
|
264
265
|
const t = Pe(e.static), n = e.onError ?? p;
|
|
265
266
|
return function(a, d) {
|
|
266
267
|
const c = be(a, d);
|
|
@@ -271,7 +272,7 @@ function pe(e) {
|
|
|
271
272
|
async function r(o, a, d) {
|
|
272
273
|
let c;
|
|
273
274
|
try {
|
|
274
|
-
c =
|
|
275
|
+
c = L(o, {
|
|
275
276
|
allowedHosts: e.allowedHosts,
|
|
276
277
|
signal: d,
|
|
277
278
|
trustProxy: e.trustProxy
|
|
@@ -443,8 +444,8 @@ export {
|
|
|
443
444
|
_e as ConsumedRequestBodyError,
|
|
444
445
|
A as UnsupportedMethodError,
|
|
445
446
|
M as UntrustedHostError,
|
|
446
|
-
|
|
447
|
-
|
|
447
|
+
qe as createFontAssetHandler,
|
|
448
|
+
Ge as createImageOptimizer,
|
|
448
449
|
pe as createNodeRequestListener,
|
|
449
450
|
Se as createNodeServer,
|
|
450
451
|
J as createStaticFileHandler,
|
|
@@ -453,11 +454,11 @@ export {
|
|
|
453
454
|
we as defaultNodeServerTimeouts,
|
|
454
455
|
De as nodeAdapter,
|
|
455
456
|
I as renderNodePageResponse,
|
|
456
|
-
|
|
457
|
+
je as renderStreamingPageResponse,
|
|
457
458
|
We as serveNodeBuild,
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
459
|
+
Be as toHeaders,
|
|
460
|
+
L as toWebRequest,
|
|
461
|
+
x as validateNodeOriginPolicy,
|
|
461
462
|
k as writeWebResponse
|
|
462
463
|
};
|
|
463
464
|
//# sourceMappingURL=index.js.map
|
package/dist/node/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/node/static.ts","../../src/node/serve.ts","../../src/node/index.ts"],"sourcesContent":["import { constants } from \"node:fs\";\nimport { lstat, open, realpath } from \"node:fs/promises\";\nimport { extname, join, relative, resolve, sep } from \"node:path\";\nimport { Readable } from \"node:stream\";\nimport {\n contentTypeForExtension,\n IMMUTABLE_FILE_CACHE_CONTROL,\n isContentHashedFileName,\n REVALIDATED_FILE_CACHE_CONTROL,\n} from \"../static-files\";\n\nexport type StaticFileHandler = (request: Request) => Promise<Response | null>;\n\nexport type StaticFileHandlerOptions = {\n // Receives the file's path relative to `root`, always \"/\"-separated, so a\n // predicate can distinguish `index.html` from `docs/index.html`.\n exclude?: (path: string) => boolean;\n immutable?: (fileName: string) => boolean;\n prefix?: string;\n root: string;\n};\n\n// A null result keeps the handler composable. It means that the request does not\n// identify an owned file. The route pipeline owns all other responses, including\n// the 404 body and security headers. A traversal attempt gives the same result.\n// Plain static requests must not access the framework build output. The output\n// includes the SPA shell and the Node configuration manifest. The route pipeline\n// serves the shell and applies the `@policy.ts` headers. The server never serves\n// `demiurge-manifest.json` publicly. This exclusion applies only to the two root\n// files. An application can serve a nested file such as `docs/index.html`.\nconst defaultExcludedPaths = new Set([\"demiurge-manifest.json\", \"index.html\"]);\n\nexport function createStaticFileHandler(\n options: StaticFileHandlerOptions,\n): StaticFileHandler {\n const root = resolve(options.root);\n const realRoot = realpath(root);\n const prefix = normalizePrefix(options.prefix);\n const isExcluded = options.exclude ?? defaultExcluded;\n const isImmutable = options.immutable ?? defaultImmutable;\n\n return async function handleStaticFile(request) {\n if (request.method !== \"GET\" && request.method !== \"HEAD\") {\n return null;\n }\n\n const filePath = resolveFilePath(\n root,\n prefix,\n new URL(request.url).pathname,\n );\n\n if (!filePath || isExcluded(toRelativePath(root, filePath))) {\n return null;\n }\n\n const opened = await openStaticFile(root, await safeRealRoot(realRoot), filePath);\n\n if (!opened) {\n return null;\n }\n\n const { file, stats } = opened;\n\n const etag = createWeakEtag(stats.size, stats.mtimeMs);\n const lastModified = stats.mtime.toUTCString();\n const headers = new Headers({\n \"accept-ranges\": \"bytes\",\n \"cache-control\": isImmutable(fileNameOf(filePath))\n ? IMMUTABLE_FILE_CACHE_CONTROL\n : REVALIDATED_FILE_CACHE_CONTROL,\n \"content-length\": String(stats.size),\n \"content-type\": contentTypeOf(filePath),\n \"cross-origin-resource-policy\": \"same-origin\",\n etag,\n \"last-modified\": lastModified,\n \"x-content-type-options\": \"nosniff\",\n });\n\n if (isNotModified(request, etag, stats.mtimeMs)) {\n await file.close();\n headers.delete(\"content-length\");\n headers.delete(\"content-type\");\n\n return new Response(null, { headers, status: 304 });\n }\n\n const range = canApplyRange(request, stats.mtimeMs)\n ? parseRange(request.headers.get(\"range\"), stats.size)\n : null;\n\n if (range === \"unsatisfiable\") {\n await file.close();\n headers.delete(\"content-length\");\n headers.delete(\"content-type\");\n headers.set(\"content-range\", `bytes */${stats.size}`);\n\n return new Response(null, { headers, status: 416 });\n }\n\n if (range) {\n const contentLength = range.end - range.start + 1;\n headers.set(\"content-length\", String(contentLength));\n headers.set(\"content-range\", `bytes ${range.start}-${range.end}/${stats.size}`);\n\n if (request.method === \"HEAD\") {\n await file.close();\n return new Response(null, { headers, status: 206 });\n }\n\n // TYPE-EVIDENCE: Readable.toWeb returns a web stream that matches the DOM ReadableStream body type.\n return new Response(\n Readable.toWeb(\n file.createReadStream({ end: range.end, start: range.start }),\n ) as ReadableStream,\n { headers, status: 206 },\n );\n }\n\n if (request.method === \"HEAD\") {\n await file.close();\n return new Response(null, { headers });\n }\n\n // TYPE-EVIDENCE: Readable.toWeb returns a web stream that matches the DOM ReadableStream body type.\n return new Response(\n Readable.toWeb(file.createReadStream()) as ReadableStream,\n { headers },\n );\n };\n}\n\nfunction createWeakEtag(size: number, mtimeMs: number) {\n return `W/\"${size.toString(16)}-${Math.trunc(mtimeMs).toString(16)}\"`;\n}\n\nfunction isNotModified(request: Request, etag: string, mtimeMs: number) {\n const ifNoneMatch = request.headers.get(\"if-none-match\");\n\n // If-None-Match takes precedence over If-Modified-Since. Weak comparison is\n // correct for GET/HEAD cache validation, so W/ is removed from both sides.\n if (ifNoneMatch !== null) {\n return ifNoneMatch.trim() === \"*\" || ifNoneMatch.split(\",\").some(\n (candidate) => weakEtagValue(candidate) === weakEtagValue(etag),\n );\n }\n\n const ifModifiedSince = request.headers.get(\"if-modified-since\");\n\n if (ifModifiedSince === null) {\n return false;\n }\n\n const since = Date.parse(ifModifiedSince);\n\n // HTTP dates have one-second precision. Compare the file timestamp at the\n // same precision rather than making a freshly-written file look newer.\n return Number.isFinite(since) && Math.floor(mtimeMs / 1000) * 1000 <= since;\n}\n\nfunction weakEtagValue(value: string) {\n return value.trim().replace(/^W\\//i, \"\");\n}\n\ntype ByteRange = { end: number; start: number };\n\nfunction parseRange(\n header: string | null,\n size: number,\n): ByteRange | \"unsatisfiable\" | null {\n if (header === null) {\n return null;\n }\n\n const match = /^bytes=(\\d*)-(\\d*)$/.exec(header.trim());\n\n // A server that does not support the requested unit, syntax, or multiple\n // ranges ignores Range and returns the complete representation. A 416 is\n // reserved for a valid byte-range set that cannot select this resource.\n if (!match || (!match[1] && !match[2])) {\n return null;\n }\n\n if (size === 0) {\n return \"unsatisfiable\";\n }\n\n if (!match[1]) {\n const suffixLength = Number(match[2]);\n\n if (!Number.isSafeInteger(suffixLength) || suffixLength <= 0) {\n return null;\n }\n\n return { start: Math.max(0, size - suffixLength), end: size - 1 };\n }\n\n const start = Number(match[1]);\n const requestedEnd = match[2] ? Number(match[2]) : size - 1;\n\n if (!Number.isSafeInteger(start) || !Number.isSafeInteger(requestedEnd)) {\n return null;\n }\n\n if (start >= size) {\n return \"unsatisfiable\";\n }\n\n if (match[2] && requestedEnd < start) {\n return null;\n }\n\n return { start, end: Math.min(requestedEnd, size - 1) };\n}\n\nfunction canApplyRange(request: Request, mtimeMs: number) {\n const ifRange = request.headers.get(\"if-range\");\n\n if (ifRange === null) {\n return true;\n }\n\n // If-Range requires strong comparison for an entity-tag. This handler's\n // stat-derived validator is intentionally weak, so only an HTTP date can\n // authorize a partial response.\n if (ifRange.trim().startsWith('\"') || /^W\\//i.test(ifRange.trim())) {\n return false;\n }\n\n const date = Date.parse(ifRange);\n return Number.isFinite(date) && Math.floor(mtimeMs / 1000) * 1000 <= date;\n}\n\nfunction resolveFilePath(root: string, prefix: string, pathname: string) {\n let decoded: string;\n\n try {\n decoded = decodeURIComponent(pathname);\n } catch {\n return null;\n }\n\n // A null byte truncates the path for some system calls. A name such as\n // \"app.js\\0.txt\" could read a file that differs from the checked file.\n if (decoded.includes(\"\\0\")) {\n return null;\n }\n\n if (prefix) {\n if (!decoded.startsWith(prefix)) {\n return null;\n }\n\n decoded = decoded.slice(prefix.length - 1);\n }\n\n if (decoded.endsWith(\"/\")) {\n return null;\n }\n\n const candidate = resolve(root, `.${decoded}`);\n\n return candidate.startsWith(`${root}${sep}`) ? candidate : null;\n}\n\nasync function safeRealRoot(root: Promise<string>) {\n try {\n return await root;\n } catch {\n return null;\n }\n}\n\nasync function openStaticFile(\n root: string,\n realRoot: string | null,\n filePath: string,\n) {\n if (!realRoot || !(await hasNoSymlinkComponents(root, filePath))) {\n return null;\n }\n\n let resolvedTarget: string;\n\n try {\n resolvedTarget = await realpath(filePath);\n } catch {\n return null;\n }\n\n if (!isPathInside(realRoot, resolvedTarget)) {\n return null;\n }\n\n const noFollow =\n typeof constants.O_NOFOLLOW === \"number\" ? constants.O_NOFOLLOW : 0;\n let file: Awaited<ReturnType<typeof open>>;\n\n try {\n // O_NOFOLLOW closes the final-component race on platforms that expose it.\n // The component walk and realpath containment check cover directory links.\n file = await open(filePath, constants.O_RDONLY | noFollow);\n } catch {\n return null;\n }\n\n try {\n const stats = await file.stat();\n\n if (!stats.isFile()) {\n await file.close();\n return null;\n }\n\n return { file, stats };\n } catch {\n await file.close();\n return null;\n }\n}\n\nasync function hasNoSymlinkComponents(root: string, filePath: string) {\n const parts = relative(root, filePath).split(sep);\n let current = root;\n\n try {\n for (const part of parts) {\n current = join(current, part);\n\n if ((await lstat(current)).isSymbolicLink()) {\n return false;\n }\n }\n } catch {\n return false;\n }\n\n return true;\n}\n\nfunction isPathInside(root: string, candidate: string) {\n return candidate.startsWith(`${root}${sep}`);\n}\n\nfunction normalizePrefix(prefix: string | undefined) {\n if (!prefix || prefix === \"/\") {\n return \"\";\n }\n\n return `/${prefix.replace(/^\\/|\\/$/g, \"\")}/`;\n}\n\nfunction defaultImmutable(fileName: string) {\n return isContentHashedFileName(fileName);\n}\n\nfunction defaultExcluded(path: string) {\n return defaultExcludedPaths.has(path);\n}\n\nfunction toRelativePath(root: string, filePath: string) {\n return filePath.slice(root.length + 1).split(sep).join(\"/\");\n}\n\nfunction fileNameOf(filePath: string) {\n return filePath.slice(filePath.lastIndexOf(sep) + 1);\n}\n\nfunction contentTypeOf(filePath: string) {\n return contentTypeForExtension(extname(filePath));\n}\n","import { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { parseClientManifest } from \"../manifest\";\nimport type { PageRenderer, RequestHandler } from \"../server\";\nimport {\n createNodeServer,\n type NodeGracefulShutdownOptions,\n type NodeRequestListenerOptions,\n type NodeServer,\n type NodeServerTimeouts,\n} from \"./index\";\nimport type { TrustProxy } from \"./http\";\nimport { renderNodePageResponse } from \"./streaming\";\nimport type { StaticFileHandler, StaticFileHandlerOptions } from \"./static\";\n\nexport type NodeBuildPageOptions = {\n clientEntry: string;\n renderPage: PageRenderer;\n styles: string[];\n};\n\nexport type NodeBuildContext = {\n page: NodeBuildPageOptions;\n root: string;\n waitUntil: (promise: Promise<unknown>) => void;\n};\n\nexport type ServeNodeBuildEnvironment = Record<string, string | undefined>;\n\nexport type ServeNodeBuildOptions = {\n allowedHosts?: readonly string[];\n base: string | URL;\n clientDir?: string;\n createHandler: (\n context: NodeBuildContext,\n ) => RequestHandler | Promise<RequestHandler>;\n env?: ServeNodeBuildEnvironment;\n host?: string;\n name?: string;\n onError?: NodeRequestListenerOptions[\"onError\"];\n // When set, this replaces the default listen log entirely, so `name` is\n // not used.\n onListen?: (address: { host: string; port: number; server: NodeServer }) => void;\n port?: number;\n readyPath?: string | false;\n shutdown?: NodeGracefulShutdownOptions;\n // A caller that needs a font handler or an image optimizer in front of the\n // plain file handler returns the composed handler from this factory.\n static?:\n | StaticFileHandlerOptions\n | ((context: NodeBuildContext) => StaticFileHandler | StaticFileHandlerOptions);\n timeouts?: Partial<NodeServerTimeouts>;\n trustProxy?: TrustProxy;\n};\n\nexport const defaultNodeBuildClientDir = \"dist/client\";\nexport const defaultNodeBuildReadyPath = \"/.well-known/ready\";\n\n/**\n * Boot a built Demiurge app as one production Node process.\n *\n * The helper reads the browser manifest, resolves the host allowlist and the\n * bind address from the environment, serves hashed assets from the client\n * build, answers the readiness path, and listens.\n */\nexport async function serveNodeBuild(\n options: ServeNodeBuildOptions,\n): Promise<NodeServer> {\n const environment = options.env ?? process.env;\n const root = fileURLToPath(\n new URL(options.clientDir ?? defaultNodeBuildClientDir, options.base),\n );\n const manifest = parseClientManifest(\n await readFile(join(root, \"demiurge-manifest.json\"), \"utf8\"),\n );\n const host = options.host ?? environment.HOST ?? \"127.0.0.1\";\n const port = resolvePort(environment.PORT, options.port);\n const allowedHosts = resolveAllowedHosts(\n options.allowedHosts,\n environment.ALLOWED_HOSTS,\n host,\n );\n const name = options.name ?? \"Demiurge Node server\";\n const readyPath = options.readyPath === false\n ? undefined\n : options.readyPath ?? defaultNodeBuildReadyPath;\n\n // A caller's createHandler may call waitUntil synchronously (for example to\n // warm a cache at startup), before the server exists. Buffer those promises\n // here and flush them into the real server once it is constructed, so an\n // early call just works instead of throwing.\n const pendingWaitUntil: Promise<unknown>[] = [];\n const context: NodeBuildContext = {\n page: {\n clientEntry: manifest.clientEntry,\n renderPage: renderNodePageResponse,\n styles: manifest.styles,\n },\n root,\n waitUntil(promise) {\n pendingWaitUntil.push(promise);\n },\n };\n const handler = await options.createHandler(context);\n\n const server = createNodeServer({\n allowedHosts,\n handler,\n onError: options.onError,\n readyPath,\n shutdown: options.shutdown,\n static: resolveStatic(options.static, context),\n timeouts: options.timeouts,\n trustProxy: options.trustProxy,\n });\n\n context.waitUntil = (promise) => {\n server.waitUntil(promise);\n };\n for (const promise of pendingWaitUntil) {\n server.waitUntil(promise);\n }\n\n await new Promise<void>((resolveListen, rejectListen) => {\n server.once(\"error\", rejectListen);\n server.listen(port, host, () => {\n server.off(\"error\", rejectListen);\n resolveListen();\n });\n });\n\n const address = server.address();\n // Port 0 asks the operating system for a free port. Report the bound port so\n // a caller can reach the server.\n const boundPort = typeof address === \"object\" && address\n ? address.port\n : port;\n\n if (options.onListen) {\n options.onListen({ host, port: boundPort, server });\n } else {\n console.log(`${name} listening on http://${host}:${boundPort}`);\n }\n\n return server;\n}\n\nfunction resolvePort(configured: string | undefined, fallback: number | undefined) {\n const value = configured ?? String(fallback ?? 4173);\n const port = Number(value);\n\n if (!Number.isSafeInteger(port) || port < 0 || port > 65_535) {\n throw new Error(\"Demiurge Node PORT must be an integer from 0 through 65535.\");\n }\n\n return port;\n}\n\nfunction resolveAllowedHosts(\n configured: readonly string[] | undefined,\n fromEnvironment: string | undefined,\n host: string,\n) {\n if (configured) {\n return configured;\n }\n\n if (fromEnvironment) {\n return fromEnvironment\n .split(\",\")\n .map((value) => value.trim())\n .filter(Boolean);\n }\n\n return host === \"localhost\" ? [host] : [host, \"localhost\"];\n}\n\nfunction resolveStatic(\n configured: ServeNodeBuildOptions[\"static\"],\n context: NodeBuildContext,\n) {\n if (!configured) {\n return { root: context.root };\n }\n\n if (typeof configured !== \"function\") {\n return configured;\n }\n\n const resolved = configured(context);\n\n // A factory returns a StaticFileHandler or a StaticFileHandlerOptions\n // object, both synchronously. A StaticFileHandler passed directly instead\n // (the shape createNodeServer's own `static` option accepts) is an async\n // function. Calling it here as a factory always returns a Promise. That\n // catches the mistake early, with a clear message, instead of a failure\n // deep inside the static file handler.\n if (resolved instanceof Promise) {\n throw new TypeError(\n \"The `static` option must be a StaticFileHandlerOptions object or a \" +\n \"factory `(context) => StaticFileHandler | StaticFileHandlerOptions`. \" +\n \"It looks like a StaticFileHandler was passed directly. Wrap it: \" +\n \"`static: () => yourHandler`.\",\n );\n }\n\n return resolved;\n}\n","import { createServer } from \"node:http\";\nimport type { IncomingMessage, Server, ServerResponse } from \"node:http\";\nimport type { Socket } from \"node:net\";\nimport { defineAdapter } from \"../adapter\";\nimport type { RequestHandler } from \"../server\";\nimport {\n UntrustedHostError,\n UnsupportedMethodError,\n toWebRequest,\n validateNodeOriginPolicy,\n writeNotImplemented,\n writeWebResponse,\n} from \"./http\";\nimport { createStaticFileHandler } from \"./static\";\nimport type { StaticFileHandler, StaticFileHandlerOptions } from \"./static\";\n\nexport {\n ConsumedRequestBodyError,\n UntrustedHostError,\n UnsupportedMethodError,\n toHeaders,\n toWebRequest,\n validateNodeOriginPolicy,\n writeWebResponse,\n} from \"./http\";\nexport { createFontAssetHandler } from \"./font\";\nexport type { FontAssetHandlerOptions } from \"./font\";\nexport { createImageOptimizer } from \"./image\";\nexport type {\n ImageOptimizerFetch,\n ImageOptimizerOptions,\n} from \"./image\";\nexport { createStaticFileHandler } from \"./static\";\nexport {\n defaultNodeBuildClientDir,\n defaultNodeBuildReadyPath,\n serveNodeBuild,\n} from \"./serve\";\nexport type {\n NodeBuildContext,\n NodeBuildPageOptions,\n ServeNodeBuildEnvironment,\n ServeNodeBuildOptions,\n} from \"./serve\";\nexport {\n renderNodePageResponse,\n renderStreamingPageResponse,\n} from \"./streaming\";\nexport type {\n HttpScheme,\n NodeOriginPolicy,\n ToWebRequestOptions,\n TrustProxy,\n} from \"./http\";\nexport type { StaticFileHandler, StaticFileHandlerOptions } from \"./static\";\n\n// `requestTimeoutEnforcement` is true because `createNodeServer` sets a real\n// `headersTimeout`, `keepAliveTimeout`, and `requestTimeout` on the\n// `http.Server`. Those bound how long a connection can stay open while the\n// shared pipeline reads a body without a `Content-Length`. The shared\n// pipeline itself does not own that bound.\nexport const nodeAdapter = defineAdapter({\n name: \"node\",\n capabilities: {\n backgroundLifetime: true,\n crossOriginIsolationHeaders: true,\n nonceInjection: true,\n requestTimeoutEnforcement: true,\n streaming: true,\n },\n});\n\nexport type NodeRequestListener = (\n request: IncomingMessage,\n response: ServerResponse,\n) => void;\n\nexport type NodeRequestListenerOptions = import(\"./http\").NodeOriginPolicy & {\n handler: RequestHandler;\n onError?: (error: unknown, request: IncomingMessage) => void;\n static?: StaticFileHandler | StaticFileHandlerOptions;\n};\n\nexport type NodeServerTimeouts = {\n headersTimeout: number;\n keepAliveTimeout: number;\n requestTimeout: number;\n};\n\nexport type NodeShutdownSignal = \"SIGINT\" | \"SIGTERM\";\nexport type NodeServerState = \"draining\" | \"ready\" | \"stopped\";\n\nexport type NodeGracefulShutdownOptions = {\n gracePeriod?: number;\n onBackgroundError?: (error: unknown) => void;\n onStateChange?: (state: NodeServerState) => void;\n signals?: readonly NodeShutdownSignal[];\n};\n\nexport type NodeServerOptions = NodeRequestListenerOptions & {\n readyPath?: string;\n shutdown?: NodeGracefulShutdownOptions;\n timeouts?: Partial<NodeServerTimeouts>;\n};\n\nexport type NodeServer = Server & {\n isReady: () => boolean;\n shutdown: () => Promise<void>;\n waitUntil: (promise: Promise<unknown>) => void;\n};\n\nexport const defaultNodeServerTimeouts = {\n headersTimeout: 66_000,\n keepAliveTimeout: 65_000,\n requestTimeout: 300_000,\n} as const satisfies NodeServerTimeouts;\n\nconst defaultShutdownGracePeriod = 30_000;\n\nexport function createNodeRequestListener(\n options: NodeRequestListenerOptions,\n): NodeRequestListener {\n validateNodeOriginPolicy(options);\n const serveStaticFile = toStaticFileHandler(options.static);\n const onError = options.onError ?? defaultOnError;\n\n return function handleNodeRequest(request, response) {\n const connection = createRequestAbort(request, response);\n\n void respond(request, response, connection.signal)\n .catch((error: unknown) => {\n if (connection.signal.aborted) {\n return;\n }\n\n onError(error, request);\n writeServerError(response);\n })\n .finally(connection.cleanup);\n };\n\n async function respond(\n request: IncomingMessage,\n response: ServerResponse,\n signal: AbortSignal,\n ) {\n let webRequest: Request;\n\n try {\n webRequest = toWebRequest(request, {\n allowedHosts: options.allowedHosts,\n signal,\n trustProxy: options.trustProxy,\n });\n } catch (error) {\n if (error instanceof UnsupportedMethodError) {\n writeNotImplemented(response);\n return;\n }\n\n if (error instanceof UntrustedHostError) {\n writeMisdirectedRequest(response);\n return;\n }\n\n throw error;\n }\n\n const staticResponse = await serveStaticFile?.(webRequest);\n\n await writeWebResponse(\n response,\n staticResponse ?? (await options.handler(webRequest)),\n );\n }\n}\n\nfunction createRequestAbort(\n request: IncomingMessage,\n response: ServerResponse,\n) {\n const controller = new AbortController();\n const abort = () => {\n if (!controller.signal.aborted) {\n controller.abort(new DOMException(\"Client disconnected.\", \"AbortError\"));\n }\n };\n const abortPrematureResponse = () => {\n if (!response.writableFinished) {\n abort();\n }\n };\n\n request.once(\"aborted\", abort);\n response.once(\"close\", abortPrematureResponse);\n\n return {\n cleanup() {\n request.off(\"aborted\", abort);\n response.off(\"close\", abortPrematureResponse);\n },\n signal: controller.signal,\n };\n}\n\nfunction writeMisdirectedRequest(response: ServerResponse) {\n response.statusCode = 421;\n response.setHeader(\"content-type\", \"text/plain; charset=utf-8\");\n response.end(\"Misdirected Request\");\n}\n\nexport function createNodeServer(\n options: NodeServerOptions,\n): NodeServer {\n const timeouts = normalizeNodeServerTimeouts(options.timeouts);\n const readyPath = normalizeReadyPath(options.readyPath);\n // The readiness answer needs the server the framework has not created yet.\n // The closure reads it after listen, so the later binding is always set.\n const handler: RequestHandler = readyPath\n ? async (request) => {\n if (new URL(request.url).pathname !== readyPath) {\n return options.handler(request);\n }\n\n const ready = server.isReady();\n\n return new Response(ready ? \"ready\" : \"draining\", {\n headers: {\n \"cache-control\": \"no-store\",\n \"content-type\": \"text/plain; charset=utf-8\",\n },\n status: ready ? 200 : 503,\n });\n }\n : options.handler;\n // TYPE-EVIDENCE: createServer returns an http.Server that the framework manages as a node server. The cast adds the framework lifecycle type.\n const server = createServer(\n createNodeRequestListener({ ...options, handler }),\n ) as NodeServer;\n server.keepAliveTimeout = timeouts.keepAliveTimeout;\n server.headersTimeout = timeouts.headersTimeout;\n server.requestTimeout = timeouts.requestTimeout;\n\n return attachNodeServerLifecycle(server, options.shutdown);\n}\n\nfunction normalizeReadyPath(configured: string | undefined) {\n if (configured === undefined) {\n return undefined;\n }\n\n if (!configured.startsWith(\"/\") || configured.includes(\"?\")) {\n throw new Error(\n \"Demiurge Node readyPath must be an absolute path without a query string.\",\n );\n }\n\n return configured;\n}\n\nfunction normalizeNodeServerTimeouts(\n configured: Partial<NodeServerTimeouts> | undefined,\n) {\n const timeouts = {\n ...defaultNodeServerTimeouts,\n ...configured,\n } satisfies NodeServerTimeouts;\n\n for (const [name, value] of Object.entries(timeouts)) {\n if (!Number.isSafeInteger(value) || value <= 0) {\n throw new Error(\n `Demiurge Node ${name} must be a positive integer in milliseconds.`,\n );\n }\n }\n\n if (timeouts.headersTimeout <= timeouts.keepAliveTimeout) {\n throw new Error(\n \"Demiurge Node headersTimeout must be greater than keepAliveTimeout.\",\n );\n }\n\n return timeouts;\n}\n\nfunction attachNodeServerLifecycle(\n server: NodeServer,\n options: NodeGracefulShutdownOptions | undefined,\n) {\n const gracePeriod = options?.gracePeriod ?? defaultShutdownGracePeriod;\n\n if (!Number.isSafeInteger(gracePeriod) || gracePeriod < 0) {\n throw new Error(\n \"Demiurge Node shutdown gracePeriod must be a non-negative integer in milliseconds.\",\n );\n }\n\n let draining = false;\n let shutdownPromise: Promise<void> | undefined;\n let checkShutdownCompletion: (() => void) | undefined;\n const signalHandlers = new Map<NodeShutdownSignal, () => void>();\n const activeResponses = new Map<Socket, number>();\n const backgroundTasks = new Set<Promise<void>>();\n\n server.on(\"connection\", (socket) => {\n activeResponses.set(socket, 0);\n socket.once(\"close\", () => activeResponses.delete(socket));\n });\n server.on(\"request\", (_request, response) => {\n const socket = response.socket;\n\n if (!socket) {\n return;\n }\n\n activeResponses.set(socket, (activeResponses.get(socket) ?? 0) + 1);\n let settled = false;\n const settle = () => {\n if (settled) {\n return;\n }\n\n settled = true;\n const remaining = Math.max(0, (activeResponses.get(socket) ?? 1) - 1);\n activeResponses.set(socket, remaining);\n\n if (draining && remaining === 0) {\n socket.destroy();\n }\n };\n response.once(\"finish\", settle);\n response.once(\"close\", settle);\n });\n\n server.isReady = () => server.listening && !draining;\n server.waitUntil = (promise) => {\n const tracked = Promise.resolve(promise)\n .catch((error: unknown) => {\n try {\n (options?.onBackgroundError ?? defaultOnError)(error);\n } catch (reportingError) {\n defaultOnError(reportingError);\n }\n })\n .then(() => undefined)\n .finally(() => {\n backgroundTasks.delete(tracked);\n checkShutdownCompletion?.();\n });\n backgroundTasks.add(tracked);\n };\n server.shutdown = () => {\n if (shutdownPromise) {\n return shutdownPromise;\n }\n\n draining = true;\n options?.onStateChange?.(\"draining\");\n shutdownPromise = new Promise<void>((resolveShutdown, rejectShutdown) => {\n let closeError: Error | undefined;\n let deadlineReached = false;\n let serverClosed = false;\n let settled = false;\n const forceTimer = setTimeout(() => {\n deadlineReached = true;\n server.closeAllConnections();\n completeShutdown();\n }, gracePeriod);\n\n server.close((error) => {\n // TYPE-EVIDENCE: Node close callbacks pass an error with an optional code. The cast reads that code to detect an ordinary shutdown condition.\n if (error && (error as NodeJS.ErrnoException).code !== \"ERR_SERVER_NOT_RUNNING\") {\n closeError = error;\n }\n\n serverClosed = true;\n completeShutdown();\n });\n server.closeIdleConnections();\n closeTrackedIdleConnections();\n // A response can finish in the same turn shutdown begins and transition\n // from active to idle just after the first sweep.\n setImmediate(() => {\n server.closeIdleConnections();\n closeTrackedIdleConnections();\n });\n\n checkShutdownCompletion = completeShutdown;\n\n function completeShutdown() {\n if (\n settled ||\n !serverClosed ||\n (!deadlineReached && backgroundTasks.size > 0)\n ) {\n return;\n }\n\n settled = true;\n checkShutdownCompletion = undefined;\n clearTimeout(forceTimer);\n removeSignalHandlers();\n options?.onStateChange?.(\"stopped\");\n\n if (closeError) {\n rejectShutdown(closeError);\n return;\n }\n\n resolveShutdown();\n }\n });\n\n return shutdownPromise;\n };\n\n server.on(\"listening\", () => options?.onStateChange?.(\"ready\"));\n\n for (const signal of new Set(options?.signals ?? [])) {\n const handler = () => {\n void server.shutdown().catch(defaultOnError);\n };\n signalHandlers.set(signal, handler);\n process.once(signal, handler);\n }\n\n server.once(\"close\", removeSignalHandlers);\n\n return server;\n\n function removeSignalHandlers() {\n for (const [signal, handler] of signalHandlers) {\n process.off(signal, handler);\n }\n\n signalHandlers.clear();\n }\n\n function closeTrackedIdleConnections() {\n for (const [socket, count] of activeResponses) {\n if (count === 0) {\n socket.destroy();\n }\n }\n }\n}\n\nfunction toStaticFileHandler(\n value: StaticFileHandler | StaticFileHandlerOptions | undefined,\n) {\n if (!value) {\n return undefined;\n }\n\n return typeof value === \"function\" ? value : createStaticFileHandler(value);\n}\n\nfunction defaultOnError(error: unknown) {\n console.error(error);\n}\n\n// The body stays generic on purpose. A stack trace here would hand an attacker\n// file paths and framework internals for free.\nfunction writeServerError(response: ServerResponse) {\n if (response.headersSent) {\n response.destroy();\n return;\n }\n\n response.statusCode = 500;\n response.setHeader(\"content-type\", \"text/plain; charset=utf-8\");\n response.end(\"Internal Server Error\");\n}\n"],"names":["defaultExcludedPaths","createStaticFileHandler","options","root","resolve","realRoot","realpath","prefix","normalizePrefix","isExcluded","defaultExcluded","isImmutable","defaultImmutable","request","filePath","resolveFilePath","toRelativePath","opened","openStaticFile","safeRealRoot","file","stats","etag","createWeakEtag","lastModified","headers","fileNameOf","IMMUTABLE_FILE_CACHE_CONTROL","REVALIDATED_FILE_CACHE_CONTROL","contentTypeOf","isNotModified","range","canApplyRange","parseRange","contentLength","Readable","size","mtimeMs","ifNoneMatch","candidate","weakEtagValue","ifModifiedSince","since","value","header","match","suffixLength","start","requestedEnd","ifRange","date","pathname","decoded","sep","hasNoSymlinkComponents","resolvedTarget","isPathInside","noFollow","constants","open","parts","relative","current","part","join","lstat","fileName","isContentHashedFileName","path","contentTypeForExtension","extname","defaultNodeBuildClientDir","defaultNodeBuildReadyPath","serveNodeBuild","environment","fileURLToPath","manifest","parseClientManifest","readFile","host","port","resolvePort","allowedHosts","resolveAllowedHosts","name","readyPath","pendingWaitUntil","context","renderNodePageResponse","promise","handler","server","createNodeServer","resolveStatic","resolveListen","rejectListen","address","boundPort","configured","fallback","fromEnvironment","resolved","nodeAdapter","defineAdapter","defaultNodeServerTimeouts","defaultShutdownGracePeriod","createNodeRequestListener","validateNodeOriginPolicy","serveStaticFile","toStaticFileHandler","onError","defaultOnError","response","connection","createRequestAbort","respond","error","writeServerError","signal","webRequest","toWebRequest","UnsupportedMethodError","writeNotImplemented","UntrustedHostError","writeMisdirectedRequest","staticResponse","writeWebResponse","controller","abort","abortPrematureResponse","timeouts","normalizeNodeServerTimeouts","normalizeReadyPath","ready","createServer","attachNodeServerLifecycle","gracePeriod","draining","shutdownPromise","checkShutdownCompletion","signalHandlers","activeResponses","backgroundTasks","socket","_request","settled","settle","remaining","tracked","reportingError","resolveShutdown","rejectShutdown","closeError","deadlineReached","serverClosed","forceTimer","completeShutdown","closeTrackedIdleConnections","removeSignalHandlers","count"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/node/static.ts","../../src/node/serve.ts","../../src/node/index.ts"],"sourcesContent":["import { constants } from \"node:fs\";\nimport { lstat, open, realpath } from \"node:fs/promises\";\nimport { extname, join, relative, resolve, sep } from \"node:path\";\nimport { Readable } from \"node:stream\";\nimport {\n contentTypeForExtension,\n IMMUTABLE_FILE_CACHE_CONTROL,\n isContentHashedFileName,\n REVALIDATED_FILE_CACHE_CONTROL,\n} from \"../static-files\";\n\nexport type StaticFileHandler = (request: Request) => Promise<Response | null>;\n\nexport type StaticFileHandlerOptions = {\n // Receives the file's path relative to `root`, always \"/\"-separated, so a\n // predicate can distinguish `index.html` from `docs/index.html`.\n exclude?: (path: string) => boolean;\n immutable?: (fileName: string) => boolean;\n prefix?: string;\n root: string;\n};\n\n// A null result keeps the handler composable. It means that the request does not\n// identify an owned file. The route pipeline owns all other responses, including\n// the 404 body and security headers. A traversal attempt gives the same result.\n// Plain static requests must not access the framework build output. The output\n// includes the SPA shell and the Node configuration manifest. The route pipeline\n// serves the shell and applies the `@policy.ts` headers. The server never serves\n// `demiurge-manifest.json` publicly. This exclusion applies only to the two root\n// files. An application can serve a nested file such as `docs/index.html`.\nconst defaultExcludedPaths = new Set([\"demiurge-manifest.json\", \"index.html\"]);\n\nexport function createStaticFileHandler(\n options: StaticFileHandlerOptions,\n): StaticFileHandler {\n const root = resolve(options.root);\n const realRoot = realpath(root);\n const prefix = normalizePrefix(options.prefix);\n const isExcluded = options.exclude ?? defaultExcluded;\n const isImmutable = options.immutable ?? defaultImmutable;\n\n return async function handleStaticFile(request) {\n if (request.method !== \"GET\" && request.method !== \"HEAD\") {\n return null;\n }\n\n const filePath = resolveFilePath(\n root,\n prefix,\n new URL(request.url).pathname,\n );\n\n if (!filePath || isExcluded(toRelativePath(root, filePath))) {\n return null;\n }\n\n const opened = await openStaticFile(root, await safeRealRoot(realRoot), filePath);\n\n if (!opened) {\n return null;\n }\n\n const { file, stats } = opened;\n\n const etag = createWeakEtag(stats.size, stats.mtimeMs);\n const lastModified = stats.mtime.toUTCString();\n const headers = new Headers({\n \"accept-ranges\": \"bytes\",\n \"cache-control\": isImmutable(fileNameOf(filePath))\n ? IMMUTABLE_FILE_CACHE_CONTROL\n : REVALIDATED_FILE_CACHE_CONTROL,\n \"content-length\": String(stats.size),\n \"content-type\": contentTypeOf(filePath),\n \"cross-origin-resource-policy\": \"same-origin\",\n etag,\n \"last-modified\": lastModified,\n \"x-content-type-options\": \"nosniff\",\n });\n\n if (isNotModified(request, etag, stats.mtimeMs)) {\n await file.close();\n headers.delete(\"content-length\");\n headers.delete(\"content-type\");\n\n return new Response(null, { headers, status: 304 });\n }\n\n const range = canApplyRange(request, stats.mtimeMs)\n ? parseRange(request.headers.get(\"range\"), stats.size)\n : null;\n\n if (range === \"unsatisfiable\") {\n await file.close();\n headers.delete(\"content-length\");\n headers.delete(\"content-type\");\n headers.set(\"content-range\", `bytes */${stats.size}`);\n\n return new Response(null, { headers, status: 416 });\n }\n\n if (range) {\n const contentLength = range.end - range.start + 1;\n headers.set(\"content-length\", String(contentLength));\n headers.set(\"content-range\", `bytes ${range.start}-${range.end}/${stats.size}`);\n\n if (request.method === \"HEAD\") {\n await file.close();\n return new Response(null, { headers, status: 206 });\n }\n\n // TYPE-EVIDENCE: Readable.toWeb returns a web stream that matches the DOM ReadableStream body type.\n return new Response(\n Readable.toWeb(\n file.createReadStream({ end: range.end, start: range.start }),\n ) as ReadableStream,\n { headers, status: 206 },\n );\n }\n\n if (request.method === \"HEAD\") {\n await file.close();\n return new Response(null, { headers });\n }\n\n // TYPE-EVIDENCE: Readable.toWeb returns a web stream that matches the DOM ReadableStream body type.\n return new Response(\n Readable.toWeb(file.createReadStream()) as ReadableStream,\n { headers },\n );\n };\n}\n\nfunction createWeakEtag(size: number, mtimeMs: number) {\n return `W/\"${size.toString(16)}-${Math.trunc(mtimeMs).toString(16)}\"`;\n}\n\nfunction isNotModified(request: Request, etag: string, mtimeMs: number) {\n const ifNoneMatch = request.headers.get(\"if-none-match\");\n\n // If-None-Match takes precedence over If-Modified-Since. Weak comparison is\n // correct for GET/HEAD cache validation, so W/ is removed from both sides.\n if (ifNoneMatch !== null) {\n return ifNoneMatch.trim() === \"*\" || ifNoneMatch.split(\",\").some(\n (candidate) => weakEtagValue(candidate) === weakEtagValue(etag),\n );\n }\n\n const ifModifiedSince = request.headers.get(\"if-modified-since\");\n\n if (ifModifiedSince === null) {\n return false;\n }\n\n const since = Date.parse(ifModifiedSince);\n\n // HTTP dates have one-second precision. Compare the file timestamp at the\n // same precision rather than making a freshly-written file look newer.\n return Number.isFinite(since) && Math.floor(mtimeMs / 1000) * 1000 <= since;\n}\n\nfunction weakEtagValue(value: string) {\n return value.trim().replace(/^W\\//i, \"\");\n}\n\ntype ByteRange = { end: number; start: number };\n\nfunction parseRange(\n header: string | null,\n size: number,\n): ByteRange | \"unsatisfiable\" | null {\n if (header === null) {\n return null;\n }\n\n const match = /^bytes=(\\d*)-(\\d*)$/.exec(header.trim());\n\n // A server that does not support the requested unit, syntax, or multiple\n // ranges ignores Range and returns the complete representation. A 416 is\n // reserved for a valid byte-range set that cannot select this resource.\n if (!match || (!match[1] && !match[2])) {\n return null;\n }\n\n if (size === 0) {\n return \"unsatisfiable\";\n }\n\n if (!match[1]) {\n const suffixLength = Number(match[2]);\n\n if (!Number.isSafeInteger(suffixLength) || suffixLength <= 0) {\n return null;\n }\n\n return { start: Math.max(0, size - suffixLength), end: size - 1 };\n }\n\n const start = Number(match[1]);\n const requestedEnd = match[2] ? Number(match[2]) : size - 1;\n\n if (!Number.isSafeInteger(start) || !Number.isSafeInteger(requestedEnd)) {\n return null;\n }\n\n if (start >= size) {\n return \"unsatisfiable\";\n }\n\n if (match[2] && requestedEnd < start) {\n return null;\n }\n\n return { start, end: Math.min(requestedEnd, size - 1) };\n}\n\nfunction canApplyRange(request: Request, mtimeMs: number) {\n const ifRange = request.headers.get(\"if-range\");\n\n if (ifRange === null) {\n return true;\n }\n\n // If-Range requires strong comparison for an entity-tag. This handler's\n // stat-derived validator is intentionally weak, so only an HTTP date can\n // authorize a partial response.\n if (ifRange.trim().startsWith('\"') || /^W\\//i.test(ifRange.trim())) {\n return false;\n }\n\n const date = Date.parse(ifRange);\n return Number.isFinite(date) && Math.floor(mtimeMs / 1000) * 1000 <= date;\n}\n\nfunction resolveFilePath(root: string, prefix: string, pathname: string) {\n let decoded: string;\n\n try {\n decoded = decodeURIComponent(pathname);\n } catch {\n return null;\n }\n\n // A null byte truncates the path for some system calls. A name such as\n // \"app.js\\0.txt\" could read a file that differs from the checked file.\n if (decoded.includes(\"\\0\")) {\n return null;\n }\n\n if (prefix) {\n if (!decoded.startsWith(prefix)) {\n return null;\n }\n\n decoded = decoded.slice(prefix.length - 1);\n }\n\n if (decoded.endsWith(\"/\")) {\n return null;\n }\n\n const candidate = resolve(root, `.${decoded}`);\n\n return candidate.startsWith(`${root}${sep}`) ? candidate : null;\n}\n\nasync function safeRealRoot(root: Promise<string>) {\n try {\n return await root;\n } catch {\n return null;\n }\n}\n\nasync function openStaticFile(\n root: string,\n realRoot: string | null,\n filePath: string,\n) {\n if (!realRoot || !(await hasNoSymlinkComponents(root, filePath))) {\n return null;\n }\n\n let resolvedTarget: string;\n\n try {\n resolvedTarget = await realpath(filePath);\n } catch {\n return null;\n }\n\n if (!isPathInside(realRoot, resolvedTarget)) {\n return null;\n }\n\n const noFollow =\n typeof constants.O_NOFOLLOW === \"number\" ? constants.O_NOFOLLOW : 0;\n let file: Awaited<ReturnType<typeof open>>;\n\n try {\n // O_NOFOLLOW closes the final-component race on platforms that expose it.\n // The component walk and realpath containment check cover directory links.\n file = await open(filePath, constants.O_RDONLY | noFollow);\n } catch {\n return null;\n }\n\n try {\n const stats = await file.stat();\n\n if (!stats.isFile()) {\n await file.close();\n return null;\n }\n\n return { file, stats };\n } catch {\n await file.close();\n return null;\n }\n}\n\nasync function hasNoSymlinkComponents(root: string, filePath: string) {\n const parts = relative(root, filePath).split(sep);\n let current = root;\n\n try {\n for (const part of parts) {\n current = join(current, part);\n\n if ((await lstat(current)).isSymbolicLink()) {\n return false;\n }\n }\n } catch {\n return false;\n }\n\n return true;\n}\n\nfunction isPathInside(root: string, candidate: string) {\n return candidate.startsWith(`${root}${sep}`);\n}\n\nfunction normalizePrefix(prefix: string | undefined) {\n if (!prefix || prefix === \"/\") {\n return \"\";\n }\n\n return `/${prefix.replace(/^\\/|\\/$/g, \"\")}/`;\n}\n\nfunction defaultImmutable(fileName: string) {\n return isContentHashedFileName(fileName);\n}\n\nfunction defaultExcluded(path: string) {\n return defaultExcludedPaths.has(path);\n}\n\nfunction toRelativePath(root: string, filePath: string) {\n return filePath.slice(root.length + 1).split(sep).join(\"/\");\n}\n\nfunction fileNameOf(filePath: string) {\n return filePath.slice(filePath.lastIndexOf(sep) + 1);\n}\n\nfunction contentTypeOf(filePath: string) {\n return contentTypeForExtension(extname(filePath));\n}\n","import { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { parseClientManifest } from \"../manifest\";\nimport type { PageRenderer, RequestHandler } from \"../server\";\nimport {\n createNodeServer,\n type NodeGracefulShutdownOptions,\n type NodeRequestListenerOptions,\n type NodeServer,\n type NodeServerTimeouts,\n} from \"./index\";\nimport type { TrustProxy } from \"./http\";\nimport { renderNodePageResponse } from \"./streaming\";\nimport type { StaticFileHandler, StaticFileHandlerOptions } from \"./static\";\n\nexport type NodeBuildPageOptions = {\n clientEntry: string;\n renderPage: PageRenderer;\n styles: string[];\n};\n\nexport type NodeBuildContext = {\n page: NodeBuildPageOptions;\n root: string;\n waitUntil: (promise: Promise<unknown>) => void;\n};\n\nexport type NodeBuildRuntime = {\n createHandler: (\n context: NodeBuildContext,\n ) => RequestHandler | Promise<RequestHandler>;\n createStatic?: (\n context: NodeBuildContext,\n ) => StaticFileHandler | StaticFileHandlerOptions;\n};\n\nexport type ServeNodeBuildEnvironment = Record<string, string | undefined>;\n\nexport type ServeNodeBuildOptions = {\n allowedHosts?: readonly string[];\n base: string | URL;\n clientDir?: string;\n createHandler: (\n context: NodeBuildContext,\n ) => RequestHandler | Promise<RequestHandler>;\n env?: ServeNodeBuildEnvironment;\n host?: string;\n name?: string;\n onError?: NodeRequestListenerOptions[\"onError\"];\n // When set, this replaces the default listen log entirely, so `name` is\n // not used.\n onListen?: (address: { host: string; port: number; server: NodeServer }) => void;\n port?: number;\n readyPath?: string | false;\n shutdown?: NodeGracefulShutdownOptions;\n // A caller that needs a font handler or an image optimizer in front of the\n // plain file handler returns the composed handler from this factory.\n static?:\n | StaticFileHandlerOptions\n | ((context: NodeBuildContext) => StaticFileHandler | StaticFileHandlerOptions);\n timeouts?: Partial<NodeServerTimeouts>;\n trustProxy?: TrustProxy;\n};\n\nexport const defaultNodeBuildClientDir = \"dist/client\";\nexport const defaultNodeBuildReadyPath = \"/.well-known/ready\";\n\n/**\n * Boot a built Demiurge app as one production Node process.\n *\n * The helper reads the browser manifest, resolves the host allowlist and the\n * bind address from the environment, serves hashed assets from the client\n * build, answers the readiness path, and listens.\n */\nexport async function serveNodeBuild(\n options: ServeNodeBuildOptions,\n): Promise<NodeServer> {\n const environment = options.env ?? process.env;\n const root = fileURLToPath(\n new URL(options.clientDir ?? defaultNodeBuildClientDir, options.base),\n );\n const manifest = parseClientManifest(\n await readFile(join(root, \"demiurge-manifest.json\"), \"utf8\"),\n );\n const host = options.host ?? environment.HOST ?? \"127.0.0.1\";\n const port = resolvePort(environment.PORT, options.port);\n const allowedHosts = resolveAllowedHosts(\n options.allowedHosts,\n environment.ALLOWED_HOSTS,\n host,\n );\n const name = options.name ?? \"Demiurge Node server\";\n const readyPath = options.readyPath === false\n ? undefined\n : options.readyPath ?? defaultNodeBuildReadyPath;\n\n // A caller's createHandler may call waitUntil synchronously (for example to\n // warm a cache at startup), before the server exists. Buffer those promises\n // here and flush them into the real server once it is constructed, so an\n // early call just works instead of throwing.\n const pendingWaitUntil: Promise<unknown>[] = [];\n const context: NodeBuildContext = {\n page: {\n clientEntry: manifest.clientEntry,\n renderPage: renderNodePageResponse,\n styles: manifest.styles,\n },\n root,\n waitUntil(promise) {\n pendingWaitUntil.push(promise);\n },\n };\n const handler = await options.createHandler(context);\n\n const server = createNodeServer({\n allowedHosts,\n handler,\n onError: options.onError,\n readyPath,\n shutdown: options.shutdown,\n static: resolveStatic(options.static, context),\n timeouts: options.timeouts,\n trustProxy: options.trustProxy,\n });\n\n context.waitUntil = (promise) => {\n server.waitUntil(promise);\n };\n for (const promise of pendingWaitUntil) {\n server.waitUntil(promise);\n }\n\n await new Promise<void>((resolveListen, rejectListen) => {\n server.once(\"error\", rejectListen);\n server.listen(port, host, () => {\n server.off(\"error\", rejectListen);\n resolveListen();\n });\n });\n\n const address = server.address();\n // Port 0 asks the operating system for a free port. Report the bound port so\n // a caller can reach the server.\n const boundPort = typeof address === \"object\" && address\n ? address.port\n : port;\n\n if (options.onListen) {\n options.onListen({ host, port: boundPort, server });\n } else {\n console.log(`${name} listening on http://${host}:${boundPort}`);\n }\n\n return server;\n}\n\nfunction resolvePort(configured: string | undefined, fallback: number | undefined) {\n const value = configured ?? String(fallback ?? 4173);\n const port = Number(value);\n\n if (!Number.isSafeInteger(port) || port < 0 || port > 65_535) {\n throw new Error(\"Demiurge Node PORT must be an integer from 0 through 65535.\");\n }\n\n return port;\n}\n\nfunction resolveAllowedHosts(\n configured: readonly string[] | undefined,\n fromEnvironment: string | undefined,\n host: string,\n) {\n if (configured) {\n return configured;\n }\n\n if (fromEnvironment) {\n return fromEnvironment\n .split(\",\")\n .map((value) => value.trim())\n .filter(Boolean);\n }\n\n return host === \"localhost\" ? [host] : [host, \"localhost\"];\n}\n\nfunction resolveStatic(\n configured: ServeNodeBuildOptions[\"static\"],\n context: NodeBuildContext,\n) {\n if (!configured) {\n return { root: context.root };\n }\n\n if (typeof configured !== \"function\") {\n return configured;\n }\n\n const resolved = configured(context);\n\n // A factory returns a StaticFileHandler or a StaticFileHandlerOptions\n // object, both synchronously. A StaticFileHandler passed directly instead\n // (the shape createNodeServer's own `static` option accepts) is an async\n // function. Calling it here as a factory always returns a Promise. That\n // catches the mistake early, with a clear message, instead of a failure\n // deep inside the static file handler.\n if (resolved instanceof Promise) {\n throw new TypeError(\n \"The `static` option must be a StaticFileHandlerOptions object or a \" +\n \"factory `(context) => StaticFileHandler | StaticFileHandlerOptions`. \" +\n \"It looks like a StaticFileHandler was passed directly. Wrap it: \" +\n \"`static: () => yourHandler`.\",\n );\n }\n\n return resolved;\n}\n","import { createServer } from \"node:http\";\nimport type { IncomingMessage, Server, ServerResponse } from \"node:http\";\nimport type { Socket } from \"node:net\";\nimport { defineAdapter } from \"../adapter\";\nimport type { RequestHandler } from \"../server\";\nimport {\n UntrustedHostError,\n UnsupportedMethodError,\n toWebRequest,\n validateNodeOriginPolicy,\n writeNotImplemented,\n writeWebResponse,\n} from \"./http\";\nimport { createStaticFileHandler } from \"./static\";\nimport type { StaticFileHandler, StaticFileHandlerOptions } from \"./static\";\n\nexport {\n ConsumedRequestBodyError,\n UntrustedHostError,\n UnsupportedMethodError,\n toHeaders,\n toWebRequest,\n validateNodeOriginPolicy,\n writeWebResponse,\n} from \"./http\";\nexport { createFontAssetHandler } from \"./font\";\nexport type { FontAssetHandlerOptions } from \"./font\";\nexport { createImageOptimizer } from \"./image\";\nexport type {\n ImageOptimizerFetch,\n ImageOptimizerOptions,\n} from \"./image\";\nexport { createStaticFileHandler } from \"./static\";\nexport {\n defaultNodeBuildClientDir,\n defaultNodeBuildReadyPath,\n serveNodeBuild,\n} from \"./serve\";\nexport type {\n NodeBuildContext,\n NodeBuildPageOptions,\n NodeBuildRuntime,\n ServeNodeBuildEnvironment,\n ServeNodeBuildOptions,\n} from \"./serve\";\nexport {\n renderNodePageResponse,\n renderStreamingPageResponse,\n} from \"./streaming\";\nexport type {\n HttpScheme,\n NodeOriginPolicy,\n ToWebRequestOptions,\n TrustProxy,\n} from \"./http\";\nexport type { StaticFileHandler, StaticFileHandlerOptions } from \"./static\";\n\n// `requestTimeoutEnforcement` is true because `createNodeServer` sets a real\n// `headersTimeout`, `keepAliveTimeout`, and `requestTimeout` on the\n// `http.Server`. Those bound how long a connection can stay open while the\n// shared pipeline reads a body without a `Content-Length`. The shared\n// pipeline itself does not own that bound.\nexport const nodeAdapter = defineAdapter({\n name: \"node\",\n capabilities: {\n backgroundLifetime: true,\n crossOriginIsolationHeaders: true,\n nonceInjection: true,\n requestTimeoutEnforcement: true,\n streaming: true,\n },\n});\n\nexport type NodeRequestListener = (\n request: IncomingMessage,\n response: ServerResponse,\n) => void;\n\nexport type NodeRequestListenerOptions = import(\"./http\").NodeOriginPolicy & {\n handler: RequestHandler;\n onError?: (error: unknown, request: IncomingMessage) => void;\n static?: StaticFileHandler | StaticFileHandlerOptions;\n};\n\nexport type NodeServerTimeouts = {\n headersTimeout: number;\n keepAliveTimeout: number;\n requestTimeout: number;\n};\n\nexport type NodeShutdownSignal = \"SIGINT\" | \"SIGTERM\";\nexport type NodeServerState = \"draining\" | \"ready\" | \"stopped\";\n\nexport type NodeGracefulShutdownOptions = {\n gracePeriod?: number;\n onBackgroundError?: (error: unknown) => void;\n onStateChange?: (state: NodeServerState) => void;\n signals?: readonly NodeShutdownSignal[];\n};\n\nexport type NodeServerOptions = NodeRequestListenerOptions & {\n readyPath?: string;\n shutdown?: NodeGracefulShutdownOptions;\n timeouts?: Partial<NodeServerTimeouts>;\n};\n\nexport type NodeServer = Server & {\n isReady: () => boolean;\n shutdown: () => Promise<void>;\n waitUntil: (promise: Promise<unknown>) => void;\n};\n\nexport const defaultNodeServerTimeouts = {\n headersTimeout: 66_000,\n keepAliveTimeout: 65_000,\n requestTimeout: 300_000,\n} as const satisfies NodeServerTimeouts;\n\nconst defaultShutdownGracePeriod = 30_000;\n\nexport function createNodeRequestListener(\n options: NodeRequestListenerOptions,\n): NodeRequestListener {\n validateNodeOriginPolicy(options);\n const serveStaticFile = toStaticFileHandler(options.static);\n const onError = options.onError ?? defaultOnError;\n\n return function handleNodeRequest(request, response) {\n const connection = createRequestAbort(request, response);\n\n void respond(request, response, connection.signal)\n .catch((error: unknown) => {\n if (connection.signal.aborted) {\n return;\n }\n\n onError(error, request);\n writeServerError(response);\n })\n .finally(connection.cleanup);\n };\n\n async function respond(\n request: IncomingMessage,\n response: ServerResponse,\n signal: AbortSignal,\n ) {\n let webRequest: Request;\n\n try {\n webRequest = toWebRequest(request, {\n allowedHosts: options.allowedHosts,\n signal,\n trustProxy: options.trustProxy,\n });\n } catch (error) {\n if (error instanceof UnsupportedMethodError) {\n writeNotImplemented(response);\n return;\n }\n\n if (error instanceof UntrustedHostError) {\n writeMisdirectedRequest(response);\n return;\n }\n\n throw error;\n }\n\n const staticResponse = await serveStaticFile?.(webRequest);\n\n await writeWebResponse(\n response,\n staticResponse ?? (await options.handler(webRequest)),\n );\n }\n}\n\nfunction createRequestAbort(\n request: IncomingMessage,\n response: ServerResponse,\n) {\n const controller = new AbortController();\n const abort = () => {\n if (!controller.signal.aborted) {\n controller.abort(new DOMException(\"Client disconnected.\", \"AbortError\"));\n }\n };\n const abortPrematureResponse = () => {\n if (!response.writableFinished) {\n abort();\n }\n };\n\n request.once(\"aborted\", abort);\n response.once(\"close\", abortPrematureResponse);\n\n return {\n cleanup() {\n request.off(\"aborted\", abort);\n response.off(\"close\", abortPrematureResponse);\n },\n signal: controller.signal,\n };\n}\n\nfunction writeMisdirectedRequest(response: ServerResponse) {\n response.statusCode = 421;\n response.setHeader(\"content-type\", \"text/plain; charset=utf-8\");\n response.end(\"Misdirected Request\");\n}\n\nexport function createNodeServer(\n options: NodeServerOptions,\n): NodeServer {\n const timeouts = normalizeNodeServerTimeouts(options.timeouts);\n const readyPath = normalizeReadyPath(options.readyPath);\n // The readiness answer needs the server the framework has not created yet.\n // The closure reads it after listen, so the later binding is always set.\n const handler: RequestHandler = readyPath\n ? async (request) => {\n if (new URL(request.url).pathname !== readyPath) {\n return options.handler(request);\n }\n\n const ready = server.isReady();\n\n return new Response(ready ? \"ready\" : \"draining\", {\n headers: {\n \"cache-control\": \"no-store\",\n \"content-type\": \"text/plain; charset=utf-8\",\n },\n status: ready ? 200 : 503,\n });\n }\n : options.handler;\n // TYPE-EVIDENCE: createServer returns an http.Server that the framework manages as a node server. The cast adds the framework lifecycle type.\n const server = createServer(\n createNodeRequestListener({ ...options, handler }),\n ) as NodeServer;\n server.keepAliveTimeout = timeouts.keepAliveTimeout;\n server.headersTimeout = timeouts.headersTimeout;\n server.requestTimeout = timeouts.requestTimeout;\n\n return attachNodeServerLifecycle(server, options.shutdown);\n}\n\nfunction normalizeReadyPath(configured: string | undefined) {\n if (configured === undefined) {\n return undefined;\n }\n\n if (!configured.startsWith(\"/\") || configured.includes(\"?\")) {\n throw new Error(\n \"Demiurge Node readyPath must be an absolute path without a query string.\",\n );\n }\n\n return configured;\n}\n\nfunction normalizeNodeServerTimeouts(\n configured: Partial<NodeServerTimeouts> | undefined,\n) {\n const timeouts = {\n ...defaultNodeServerTimeouts,\n ...configured,\n } satisfies NodeServerTimeouts;\n\n for (const [name, value] of Object.entries(timeouts)) {\n if (!Number.isSafeInteger(value) || value <= 0) {\n throw new Error(\n `Demiurge Node ${name} must be a positive integer in milliseconds.`,\n );\n }\n }\n\n if (timeouts.headersTimeout <= timeouts.keepAliveTimeout) {\n throw new Error(\n \"Demiurge Node headersTimeout must be greater than keepAliveTimeout.\",\n );\n }\n\n return timeouts;\n}\n\nfunction attachNodeServerLifecycle(\n server: NodeServer,\n options: NodeGracefulShutdownOptions | undefined,\n) {\n const gracePeriod = options?.gracePeriod ?? defaultShutdownGracePeriod;\n\n if (!Number.isSafeInteger(gracePeriod) || gracePeriod < 0) {\n throw new Error(\n \"Demiurge Node shutdown gracePeriod must be a non-negative integer in milliseconds.\",\n );\n }\n\n let draining = false;\n let shutdownPromise: Promise<void> | undefined;\n let checkShutdownCompletion: (() => void) | undefined;\n const signalHandlers = new Map<NodeShutdownSignal, () => void>();\n const activeResponses = new Map<Socket, number>();\n const backgroundTasks = new Set<Promise<void>>();\n\n server.on(\"connection\", (socket) => {\n activeResponses.set(socket, 0);\n socket.once(\"close\", () => activeResponses.delete(socket));\n });\n server.on(\"request\", (_request, response) => {\n const socket = response.socket;\n\n if (!socket) {\n return;\n }\n\n activeResponses.set(socket, (activeResponses.get(socket) ?? 0) + 1);\n let settled = false;\n const settle = () => {\n if (settled) {\n return;\n }\n\n settled = true;\n const remaining = Math.max(0, (activeResponses.get(socket) ?? 1) - 1);\n activeResponses.set(socket, remaining);\n\n if (draining && remaining === 0) {\n socket.destroy();\n }\n };\n response.once(\"finish\", settle);\n response.once(\"close\", settle);\n });\n\n server.isReady = () => server.listening && !draining;\n server.waitUntil = (promise) => {\n const tracked = Promise.resolve(promise)\n .catch((error: unknown) => {\n try {\n (options?.onBackgroundError ?? defaultOnError)(error);\n } catch (reportingError) {\n defaultOnError(reportingError);\n }\n })\n .then(() => undefined)\n .finally(() => {\n backgroundTasks.delete(tracked);\n checkShutdownCompletion?.();\n });\n backgroundTasks.add(tracked);\n };\n server.shutdown = () => {\n if (shutdownPromise) {\n return shutdownPromise;\n }\n\n draining = true;\n options?.onStateChange?.(\"draining\");\n shutdownPromise = new Promise<void>((resolveShutdown, rejectShutdown) => {\n let closeError: Error | undefined;\n let deadlineReached = false;\n let serverClosed = false;\n let settled = false;\n const forceTimer = setTimeout(() => {\n deadlineReached = true;\n server.closeAllConnections();\n completeShutdown();\n }, gracePeriod);\n\n server.close((error) => {\n // TYPE-EVIDENCE: Node close callbacks pass an error with an optional code. The cast reads that code to detect an ordinary shutdown condition.\n if (error && (error as NodeJS.ErrnoException).code !== \"ERR_SERVER_NOT_RUNNING\") {\n closeError = error;\n }\n\n serverClosed = true;\n completeShutdown();\n });\n server.closeIdleConnections();\n closeTrackedIdleConnections();\n // A response can finish in the same turn shutdown begins and transition\n // from active to idle just after the first sweep.\n setImmediate(() => {\n server.closeIdleConnections();\n closeTrackedIdleConnections();\n });\n\n checkShutdownCompletion = completeShutdown;\n\n function completeShutdown() {\n if (\n settled ||\n !serverClosed ||\n (!deadlineReached && backgroundTasks.size > 0)\n ) {\n return;\n }\n\n settled = true;\n checkShutdownCompletion = undefined;\n clearTimeout(forceTimer);\n removeSignalHandlers();\n options?.onStateChange?.(\"stopped\");\n\n if (closeError) {\n rejectShutdown(closeError);\n return;\n }\n\n resolveShutdown();\n }\n });\n\n return shutdownPromise;\n };\n\n server.on(\"listening\", () => options?.onStateChange?.(\"ready\"));\n\n for (const signal of new Set(options?.signals ?? [])) {\n const handler = () => {\n void server.shutdown().catch(defaultOnError);\n };\n signalHandlers.set(signal, handler);\n process.once(signal, handler);\n }\n\n server.once(\"close\", removeSignalHandlers);\n\n return server;\n\n function removeSignalHandlers() {\n for (const [signal, handler] of signalHandlers) {\n process.off(signal, handler);\n }\n\n signalHandlers.clear();\n }\n\n function closeTrackedIdleConnections() {\n for (const [socket, count] of activeResponses) {\n if (count === 0) {\n socket.destroy();\n }\n }\n }\n}\n\nfunction toStaticFileHandler(\n value: StaticFileHandler | StaticFileHandlerOptions | undefined,\n) {\n if (!value) {\n return undefined;\n }\n\n return typeof value === \"function\" ? value : createStaticFileHandler(value);\n}\n\nfunction defaultOnError(error: unknown) {\n console.error(error);\n}\n\n// The body stays generic on purpose. A stack trace here would hand an attacker\n// file paths and framework internals for free.\nfunction writeServerError(response: ServerResponse) {\n if (response.headersSent) {\n response.destroy();\n return;\n }\n\n response.statusCode = 500;\n response.setHeader(\"content-type\", \"text/plain; charset=utf-8\");\n response.end(\"Internal Server Error\");\n}\n"],"names":["defaultExcludedPaths","createStaticFileHandler","options","root","resolve","realRoot","realpath","prefix","normalizePrefix","isExcluded","defaultExcluded","isImmutable","defaultImmutable","request","filePath","resolveFilePath","toRelativePath","opened","openStaticFile","safeRealRoot","file","stats","etag","createWeakEtag","lastModified","headers","fileNameOf","IMMUTABLE_FILE_CACHE_CONTROL","REVALIDATED_FILE_CACHE_CONTROL","contentTypeOf","isNotModified","range","canApplyRange","parseRange","contentLength","Readable","size","mtimeMs","ifNoneMatch","candidate","weakEtagValue","ifModifiedSince","since","value","header","match","suffixLength","start","requestedEnd","ifRange","date","pathname","decoded","sep","hasNoSymlinkComponents","resolvedTarget","isPathInside","noFollow","constants","open","parts","relative","current","part","join","lstat","fileName","isContentHashedFileName","path","contentTypeForExtension","extname","defaultNodeBuildClientDir","defaultNodeBuildReadyPath","serveNodeBuild","environment","fileURLToPath","manifest","parseClientManifest","readFile","host","port","resolvePort","allowedHosts","resolveAllowedHosts","name","readyPath","pendingWaitUntil","context","renderNodePageResponse","promise","handler","server","createNodeServer","resolveStatic","resolveListen","rejectListen","address","boundPort","configured","fallback","fromEnvironment","resolved","nodeAdapter","defineAdapter","defaultNodeServerTimeouts","defaultShutdownGracePeriod","createNodeRequestListener","validateNodeOriginPolicy","serveStaticFile","toStaticFileHandler","onError","defaultOnError","response","connection","createRequestAbort","respond","error","writeServerError","signal","webRequest","toWebRequest","UnsupportedMethodError","writeNotImplemented","UntrustedHostError","writeMisdirectedRequest","staticResponse","writeWebResponse","controller","abort","abortPrematureResponse","timeouts","normalizeNodeServerTimeouts","normalizeReadyPath","ready","createServer","attachNodeServerLifecycle","gracePeriod","draining","shutdownPromise","checkShutdownCompletion","signalHandlers","activeResponses","backgroundTasks","socket","_request","settled","settle","remaining","tracked","reportingError","resolveShutdown","rejectShutdown","closeError","deadlineReached","serverClosed","forceTimer","completeShutdown","closeTrackedIdleConnections","removeSignalHandlers","count"],"mappings":"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"}
|
package/dist/node/serve.d.ts
CHANGED
|
@@ -12,6 +12,10 @@ export type NodeBuildContext = {
|
|
|
12
12
|
root: string;
|
|
13
13
|
waitUntil: (promise: Promise<unknown>) => void;
|
|
14
14
|
};
|
|
15
|
+
export type NodeBuildRuntime = {
|
|
16
|
+
createHandler: (context: NodeBuildContext) => RequestHandler | Promise<RequestHandler>;
|
|
17
|
+
createStatic?: (context: NodeBuildContext) => StaticFileHandler | StaticFileHandlerOptions;
|
|
18
|
+
};
|
|
15
19
|
export type ServeNodeBuildEnvironment = Record<string, string | undefined>;
|
|
16
20
|
export type ServeNodeBuildOptions = {
|
|
17
21
|
allowedHosts?: readonly string[];
|
package/dist/node/serve.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/node/serve.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EAEL,KAAK,2BAA2B,EAChC,KAAK,0BAA0B,EAC/B,KAAK,UAAU,EACf,KAAK,kBAAkB,EACxB,MAAM,SAAS,CAAC;AACjB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEzC,OAAO,KAAK,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AAE5E,MAAM,MAAM,oBAAoB,GAAG;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,YAAY,CAAC;IACzB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,oBAAoB,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;CAChD,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAE3E,MAAM,MAAM,qBAAqB,GAAG;IAClC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,CACb,OAAO,EAAE,gBAAgB,KACtB,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IAC9C,GAAG,CAAC,EAAE,yBAAyB,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,0BAA0B,CAAC,SAAS,CAAC,CAAC;IAGhD,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,CAAA;KAAE,KAAK,IAAI,CAAC;IACjF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAC3B,QAAQ,CAAC,EAAE,2BAA2B,CAAC;IAGvC,MAAM,CAAC,EACH,wBAAwB,GACxB,CAAC,CAAC,OAAO,EAAE,gBAAgB,KAAK,iBAAiB,GAAG,wBAAwB,CAAC,CAAC;IAClF,QAAQ,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACvC,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB,CAAC;AAEF,eAAO,MAAM,yBAAyB,gBAAgB,CAAC;AACvD,eAAO,MAAM,yBAAyB,uBAAuB,CAAC;AAE9D;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,UAAU,CAAC,CA8ErB"}
|
|
1
|
+
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/node/serve.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EAEL,KAAK,2BAA2B,EAChC,KAAK,0BAA0B,EAC/B,KAAK,UAAU,EACf,KAAK,kBAAkB,EACxB,MAAM,SAAS,CAAC;AACjB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEzC,OAAO,KAAK,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AAE5E,MAAM,MAAM,oBAAoB,GAAG;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,YAAY,CAAC;IACzB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,oBAAoB,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;CAChD,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,aAAa,EAAE,CACb,OAAO,EAAE,gBAAgB,KACtB,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IAC9C,YAAY,CAAC,EAAE,CACb,OAAO,EAAE,gBAAgB,KACtB,iBAAiB,GAAG,wBAAwB,CAAC;CACnD,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAE3E,MAAM,MAAM,qBAAqB,GAAG;IAClC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,CACb,OAAO,EAAE,gBAAgB,KACtB,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IAC9C,GAAG,CAAC,EAAE,yBAAyB,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,0BAA0B,CAAC,SAAS,CAAC,CAAC;IAGhD,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,CAAA;KAAE,KAAK,IAAI,CAAC;IACjF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAC3B,QAAQ,CAAC,EAAE,2BAA2B,CAAC;IAGvC,MAAM,CAAC,EACH,wBAAwB,GACxB,CAAC,CAAC,OAAO,EAAE,gBAAgB,KAAK,iBAAiB,GAAG,wBAAwB,CAAC,CAAC;IAClF,QAAQ,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACvC,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB,CAAC;AAEF,eAAO,MAAM,yBAAyB,gBAAgB,CAAC;AACvD,eAAO,MAAM,yBAAyB,uBAAuB,CAAC;AAE9D;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,UAAU,CAAC,CA8ErB"}
|