@fitzzero/quickdraw-core 5.1.1 → 5.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1308 -219
- package/dist/{admin-DKwkIh5i.d.ts → admin-CS7JB_UH.d.ts} +2 -2
- package/dist/{adminSchemas-2DUA_hZ8.d.ts → adminSchemas-29cGgrg5.d.ts} +6 -4
- package/dist/{auth-BhWqdyb-.d.ts → auth-DtdYjrZ9.d.ts} +51 -21
- package/dist/{caller-CXTFswBO.d.ts → caller-BTwGl7vm.d.ts} +296 -33
- package/dist/chunk-2FDSKLBO.js +82 -0
- package/dist/chunk-2FDSKLBO.js.map +1 -0
- package/dist/{chunk-RT6OILOI.js → chunk-4D2DOGAW.js} +2 -14
- package/dist/chunk-4D2DOGAW.js.map +1 -0
- package/dist/{chunk-OECZPRPW.js → chunk-64I6MYBC.js} +162 -775
- package/dist/chunk-64I6MYBC.js.map +1 -0
- package/dist/chunk-7473HK24.js +17 -0
- package/dist/chunk-7473HK24.js.map +1 -0
- package/dist/chunk-7Y5ROPXL.js +33 -0
- package/dist/chunk-7Y5ROPXL.js.map +1 -0
- package/dist/chunk-A2RBQD66.js +7 -0
- package/dist/chunk-A2RBQD66.js.map +1 -0
- package/dist/chunk-AK23XSWD.js +16 -0
- package/dist/chunk-AK23XSWD.js.map +1 -0
- package/dist/chunk-ASAMUGHG.js +651 -0
- package/dist/chunk-ASAMUGHG.js.map +1 -0
- package/dist/{chunk-B2P7AZ25.js → chunk-CYE4UBOB.js} +6 -4
- package/dist/chunk-CYE4UBOB.js.map +1 -0
- package/dist/chunk-DKE6QO26.js +136 -0
- package/dist/chunk-DKE6QO26.js.map +1 -0
- package/dist/chunk-DNP65TD5.js +832 -0
- package/dist/chunk-DNP65TD5.js.map +1 -0
- package/dist/{chunk-H23KEFBA.js → chunk-DUBU3BB3.js} +30 -1
- package/dist/chunk-DUBU3BB3.js.map +1 -0
- package/dist/chunk-GYLWEJHW.js +19 -0
- package/dist/chunk-GYLWEJHW.js.map +1 -0
- package/dist/{chunk-SHEGLZEP.js → chunk-I3JCFS7H.js} +34 -13
- package/dist/chunk-I3JCFS7H.js.map +1 -0
- package/dist/{chunk-X5CWPX7H.js → chunk-IIQ3WMJK.js} +13 -2
- package/dist/chunk-IIQ3WMJK.js.map +1 -0
- package/dist/{chunk-RZTP4IEW.js → chunk-JVTONQFI.js} +6339 -4226
- package/dist/chunk-JVTONQFI.js.map +1 -0
- package/dist/{chunk-QEKVYG5F.js → chunk-KKQANRLG.js} +2 -2
- package/dist/chunk-KKQANRLG.js.map +1 -0
- package/dist/{chunk-CDKG4TFA.js → chunk-MPQAT55F.js} +38 -205
- package/dist/chunk-MPQAT55F.js.map +1 -0
- package/dist/chunk-NO4M6PO5.js +101 -0
- package/dist/chunk-NO4M6PO5.js.map +1 -0
- package/dist/{chunk-TBMKJ26R.js → chunk-NW6U45HX.js} +3 -1
- package/dist/chunk-NW6U45HX.js.map +1 -0
- package/dist/chunk-NX2V6SIL.js +202 -0
- package/dist/chunk-NX2V6SIL.js.map +1 -0
- package/dist/{chunk-ZIVPHYRG.js → chunk-NZHA4TZD.js} +72 -175
- package/dist/chunk-NZHA4TZD.js.map +1 -0
- package/dist/{chunk-2PWMUVC7.js → chunk-OCZJ4EYV.js} +518 -27
- package/dist/chunk-OCZJ4EYV.js.map +1 -0
- package/dist/{chunk-REX5YPHX.js → chunk-PHCURHC7.js} +76 -21
- package/dist/chunk-PHCURHC7.js.map +1 -0
- package/dist/{chunk-4YO5H5ON.js → chunk-Q4OGQM5I.js} +4 -10
- package/dist/chunk-Q4OGQM5I.js.map +1 -0
- package/dist/chunk-QSU47XEX.js +13 -0
- package/dist/chunk-QSU47XEX.js.map +1 -0
- package/dist/{chunk-DD4SKCNO.js → chunk-RZXJTDH5.js} +13 -2
- package/dist/chunk-RZXJTDH5.js.map +1 -0
- package/dist/{chunk-Z2LZ7IZ7.js → chunk-SN3OZVEQ.js} +5 -31
- package/dist/chunk-SN3OZVEQ.js.map +1 -0
- package/dist/{chunk-4DIQHASK.js → chunk-SQ5VUG77.js} +28 -8
- package/dist/chunk-SQ5VUG77.js.map +1 -0
- package/dist/chunk-STH5DLQP.js +1192 -0
- package/dist/chunk-STH5DLQP.js.map +1 -0
- package/dist/{chunk-QCHKWIRS.js → chunk-VZFJTKIW.js} +13 -13
- package/dist/{chunk-QCHKWIRS.js.map → chunk-VZFJTKIW.js.map} +1 -1
- package/dist/{chunk-VSQBHPWH.js → chunk-X4ZU4QH5.js} +54 -22
- package/dist/chunk-X4ZU4QH5.js.map +1 -0
- package/dist/chunk-XD7SRDUY.js +188 -0
- package/dist/chunk-XD7SRDUY.js.map +1 -0
- package/dist/chunk-XH2IZVLT.js +62 -0
- package/dist/chunk-XH2IZVLT.js.map +1 -0
- package/dist/chunk-Z7I6SONO.js +825 -0
- package/dist/chunk-Z7I6SONO.js.map +1 -0
- package/dist/cli/quickdraw-docs.js +132 -112
- package/dist/cli/quickdraw-docs.js.map +1 -1
- package/dist/client/core.d.ts +387 -0
- package/dist/client/core.js +109 -0
- package/dist/client/index.d.ts +52 -203
- package/dist/client/index.js +59 -218
- package/dist/client/index.js.map +1 -1
- package/dist/{clientTypes-Bc-G8n-U.d.ts → clientTypes-CUUNJwWs.d.ts} +3 -3
- package/dist/{connection-Cr1BU07W.d.ts → connection-2EeQ_Et0.d.ts} +22 -11
- package/dist/{createServer-CP9eDHZZ.d.ts → createServer-Bx5HOcYK.d.ts} +67 -17
- package/dist/{createTestApp-UKVGEmOf.d.ts → createTestApp-DAMANT79.d.ts} +46 -13
- package/dist/{envelope-Cp3YcVxC.d.ts → envelope-BMBaxppV.d.ts} +216 -10
- package/dist/{http-Djj2Inuu.d.ts → http-YzLAar3w.d.ts} +76 -60
- package/dist/index.d.ts +8 -7
- package/dist/index.js +82 -25
- package/dist/index.js.map +1 -1
- package/dist/{infer-Teo1xpsd.d.ts → infer-Br62cnyW.d.ts} +82 -11
- package/dist/logger-Yz93KzdV.d.ts +30 -0
- package/dist/{metrics-BdBu5o8G.d.ts → metrics-CTVpw0gu.d.ts} +2 -2
- package/dist/origins-kvpxnVcW.d.ts +8 -0
- package/dist/parser.js +2 -1
- package/dist/prisma/index.d.ts +14 -7
- package/dist/prisma/index.js +279 -70
- package/dist/prisma/index.js.map +1 -1
- package/dist/{rateLimit-ZMIZJUZF.js → rateLimit-5RKBFLUR.js} +2 -2
- package/dist/rateLimit-5RKBFLUR.js.map +1 -0
- package/dist/remote-N7OUAGOW.js +15 -0
- package/dist/remote-N7OUAGOW.js.map +1 -0
- package/dist/server/auth/index.d.ts +82 -33
- package/dist/server/auth/index.js +277 -270
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/express/index.d.ts +9 -2
- package/dist/server/express/index.js +1 -1
- package/dist/server/index.d.ts +268 -84
- package/dist/server/index.js +664 -418
- package/dist/server/index.js.map +1 -1
- package/dist/server/mcp/index.d.ts +89 -27
- package/dist/server/mcp/index.js +281 -62
- package/dist/server/mcp/index.js.map +1 -1
- package/dist/server/otel.d.ts +3 -3
- package/dist/{sharing-DVa8W-yR.d.ts → sharing-BrsmlDv_.d.ts} +3 -3
- package/dist/testing/client.d.ts +14 -14
- package/dist/testing/client.js +19 -10
- package/dist/testing/client.js.map +1 -1
- package/dist/testing/index.d.ts +50 -13
- package/dist/testing/index.js +113 -21
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/mock.d.ts +5 -5
- package/dist/testing/mock.js +17 -9
- package/dist/tracked-BR5b2O-U.d.ts +43 -0
- package/dist/{types-Cu0vmx7c.d.ts → types-CLRCQGnO.d.ts} +4 -3
- package/dist/types-tMagHbjy.d.ts +151 -0
- package/dist/utils/index.d.ts +16 -4
- package/dist/utils/index.js +6 -3
- package/package.json +5 -1
- package/dist/chunk-2PWMUVC7.js.map +0 -1
- package/dist/chunk-4DIQHASK.js.map +0 -1
- package/dist/chunk-4YO5H5ON.js.map +0 -1
- package/dist/chunk-B2P7AZ25.js.map +0 -1
- package/dist/chunk-CDKG4TFA.js.map +0 -1
- package/dist/chunk-DD4SKCNO.js.map +0 -1
- package/dist/chunk-H23KEFBA.js.map +0 -1
- package/dist/chunk-OECZPRPW.js.map +0 -1
- package/dist/chunk-QEKVYG5F.js.map +0 -1
- package/dist/chunk-REX5YPHX.js.map +0 -1
- package/dist/chunk-RT6OILOI.js.map +0 -1
- package/dist/chunk-RZTP4IEW.js.map +0 -1
- package/dist/chunk-SHEGLZEP.js.map +0 -1
- package/dist/chunk-TBMKJ26R.js.map +0 -1
- package/dist/chunk-VSQBHPWH.js.map +0 -1
- package/dist/chunk-X5CWPX7H.js.map +0 -1
- package/dist/chunk-YSWH776Y.js +0 -13
- package/dist/chunk-YSWH776Y.js.map +0 -1
- package/dist/chunk-Z2LZ7IZ7.js.map +0 -1
- package/dist/chunk-ZIVPHYRG.js.map +0 -1
- package/dist/logger-BY28acW-.d.ts +0 -16
- /package/dist/{rateLimit-ZMIZJUZF.js.map → client/core.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client/peer/clock.ts","../src/client/peer/codec.ts","../src/client/peer/lag.ts","../src/client/peer/core.ts"],"sourcesContent":["// A peer link's clock (RFC 0003 section 12.5): each side pings the other on\n// `fast` and reads the peer's clock from the pong. A pong of a ping sent at\n// `t` (this side's clock), answered at `u` (the peer's) and heard at `at`\n// gives a round trip of `at - t` and an offset of `u + rtt / 2 - at`, the\n// peer's clock minus this one's, when the two halves of the trip took as\n// long. The offset taken is the one of the fastest of the last\n// `CLOCK_WINDOW` round trips (the latest of them on a tie), whose halves are\n// the likeliest to match: until\n// `CLOCK_SYNC_SAMPLES` pongs arrived it is taken as it comes; after, it\n// moves toward a new one by at most `CLOCK_SLEW_MS` a pong, so `now()`\n// never jumps, unless they differ by more than `CLOCK_JUMP_MS` (the peer's\n// clock changed). `rtt` and `jitter` are smoothed as TCP smooths its round\n// trip and its deviation.\n//\n// React-free.\n\n/** A peer's clock, as a link estimates it. */\nexport interface PeerClock {\n /** The peer's clock now, in milliseconds: this side's `performance.now()` plus `offset`. */\n now(): number;\n /** The peer's clock minus this side's, in milliseconds; 0 before the first pong. */\n readonly offset: number;\n /** The round trip, smoothed, in milliseconds; 0 before the first pong. */\n readonly rtt: number;\n /** How much round trips vary, smoothed, in milliseconds. */\n readonly jitter: number;\n /** True once enough pongs arrived for `offset` to hold: about half a second after the link opens. */\n readonly synced: boolean;\n}\n\n/** A clock and how a link feeds it. */\nexport interface ClockState extends PeerClock {\n /** A pong of the ping sent at `t`, answered at the peer's `u`, heard at `at`. */\n pong(t: number, u: number, at: number): void;\n}\n\n/** The round trips the offset is chosen from. */\nexport const CLOCK_WINDOW = 24;\n\n/** Pongs before the clock is synced; until then the offset is taken as it comes. */\nexport const CLOCK_SYNC_SAMPLES = 5;\n\n/** How far a synced offset moves toward a new estimate in one pong. */\nexport const CLOCK_SLEW_MS = 5;\n\n/** A new estimate this far from the offset is taken at once. */\nexport const CLOCK_JUMP_MS = 250;\n\n/** A round trip longer than this is not read. */\nconst MAX_RTT_MS = 10_000;\n\ninterface Sample {\n readonly rtt: number;\n readonly offset: number;\n}\n\n/** The offset of the fastest round trip; of the latest of them, when several tie. */\nfunction fastest(samples: readonly Sample[]): number {\n let best: Sample | undefined;\n for (const sample of samples) {\n if (best === undefined || sample.rtt <= best.rtt) {\n best = sample;\n }\n }\n return best?.offset ?? 0;\n}\n\n/** A clock reading this side's time from `local` (`performance.now()` by default). */\nexport function createClock(local: () => number = () => performance.now()): ClockState {\n const samples: Sample[] = [];\n let pongs = 0;\n let offset = 0;\n let rtt = 0;\n let jitter = 0;\n return {\n now: () => local() + offset,\n get offset() {\n return offset;\n },\n get rtt() {\n return rtt;\n },\n get jitter() {\n return jitter;\n },\n get synced() {\n return pongs >= CLOCK_SYNC_SAMPLES;\n },\n pong(t: number, u: number, at: number): void {\n const trip = at - t;\n if (!(trip >= 0 && trip <= MAX_RTT_MS)) {\n return;\n }\n samples.push({ rtt: trip, offset: u + trip / 2 - at });\n if (samples.length > CLOCK_WINDOW) {\n samples.shift();\n }\n pongs += 1;\n if (pongs === 1) {\n rtt = trip;\n jitter = trip / 2;\n } else {\n jitter += (Math.abs(trip - rtt) - jitter) / 4;\n rtt += (trip - rtt) / 8;\n }\n const target = fastest(samples);\n const step = target - offset;\n offset =\n pongs <= CLOCK_SYNC_SAMPLES || Math.abs(step) > CLOCK_JUMP_MS\n ? target\n : offset + Math.max(-CLOCK_SLEW_MS, Math.min(CLOCK_SLEW_MS, step));\n },\n };\n}\n","// Peer link messages (RFC 0003 section 12.5): the JSON text one data channel\n// message or one relayed `text` holds, alike on both paths, so a link can\n// change paths without either side reading a message differently.\n// `[0, data]` carries the app's data, `[1, t]` is a clock ping and\n// `[2, t, u]` its pong (`clock.ts`); kinds 3 to 8 are host mode's\n// (`HostMessage`: a channel message, a subscribe and an unsubscribe, a\n// seed's chunk, an item, a refused subscribe), whose service, member and\n// scope are each 1 to `MAX_SCOPE_LENGTH` characters, so an answer that names\n// them back always fits a message. A message whose kind this client does\n// not know, whose members are not what its kind says, or that is not JSON,\n// is ignored.\n//\n// React-free.\n\nimport type { HostMessage, PeerMessage } from \"../../protocol/envelope\";\nimport type { WireError } from \"../../protocol/errors\";\nimport { isName, isRecord } from \"../../protocol/guards\";\nimport { utf8ByteLength } from \"../../protocol/utf8\";\nimport { MAX_SCOPE_LENGTH } from \"../../protocol/version\";\n\n/** The text a link message travels as. */\nexport function encodeMessage(message: PeerMessage): string {\n return JSON.stringify(message);\n}\n\n/** The UTF-8 size of a message's text, which `limits.peers.maxMessageBytes` caps. */\nexport const messageBytes = utf8ByteLength;\n\nlet decoder: TextDecoder | undefined;\n\n/** A data channel message as text: a string, or the UTF-8 bytes a peer sent in binary mode. */\nfunction textOf(data: unknown): string | undefined {\n if (typeof data === \"string\") {\n return data;\n }\n if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) {\n decoder ??= new TextDecoder();\n return decoder.decode(data);\n }\n return undefined;\n}\n\nfunction isTime(value: unknown): value is number {\n return typeof value === \"number\" && Number.isFinite(value);\n}\n\n/** A service's, a member's or a scope's name in a host mode message: 1 to `MAX_SCOPE_LENGTH` characters. */\nfunction isHostName(value: unknown): value is string {\n return isName(value) && value.length <= MAX_SCOPE_LENGTH;\n}\n\n/** A host mode message's members after its kind, read: `undefined` when they are not its kind's. */\nfunction readHostMessage(value: readonly unknown[]): HostMessage | undefined {\n const [kind, service, name, third, fourth, fifth] = value;\n if (!isHostName(service) || !isHostName(name)) {\n return undefined;\n }\n if (kind === 3) {\n return value.length >= 4 ? [3, service, name, third] : undefined;\n }\n if (!isHostName(third)) {\n return undefined;\n }\n switch (kind) {\n case 4:\n case 5:\n return [kind, service, name, third];\n case 6:\n return Array.isArray(fourth) && typeof fifth === \"boolean\"\n ? [6, service, name, third, fourth as unknown[], fifth]\n : undefined;\n case 7:\n return value.length >= 5 ? [7, service, name, third, fourth] : undefined;\n case 8:\n return isRecord(fourth) && isName(fourth.code) && typeof fourth.message === \"string\"\n ? [8, service, name, third, { code: fourth.code, message: fourth.message } as WireError]\n : undefined;\n default:\n return undefined;\n }\n}\n\n/** The link message `data` holds, or `undefined` for what this client does not read. */\nexport function decodeMessage(data: unknown): PeerMessage | undefined {\n const text = textOf(data);\n if (text === undefined) {\n return undefined;\n }\n let value: unknown;\n try {\n value = JSON.parse(text);\n } catch {\n return undefined;\n }\n if (!Array.isArray(value)) {\n return undefined;\n }\n const [kind, first, second] = value as readonly unknown[];\n if (kind === 0 && value.length >= 2) {\n return [0, first];\n }\n if (kind === 1 && isTime(first)) {\n return [1, first];\n }\n if (kind === 2) {\n return isTime(first) && isTime(second) ? [2, first, second] : undefined;\n }\n return readHostMessage(value as readonly unknown[]);\n}\n","// The lag a peer link simulates (RFC 0003 section 12.5): `PeerLink.setLag`\n// holds each message of the link, both ways, `ms` plus up to `jitter`\n// milliseconds, and loses a `fast` one with probability `loss`, so a test or\n// a bench plays over a bad network on the real path, and a host can give\n// itself its peers' lag. A `LagQueue` holds one direction's messages. A\n// `rel` message is never lost and never goes before the one held ahead of\n// it, whatever its jitter; one whose path refuses it as it comes due waits,\n// with the ones after it, and is tried again after `LAG_RETRY_MS`, doubling\n// up to `MAX_RETRY_MS`. A `fast` message goes on its own time. Without a lag\n// the queue lets what it holds go on the next task, in the order it was\n// pushed. The loopback double of `./testing` carries its messages on the\n// same queues.\n//\n// React-free.\n\nimport type { PeerLabel } from \"../../protocol/envelope\";\nimport type { PeerLinkLag, PeerLinkLagOptions } from \"./types\";\n\n/** A held `rel` message its path refused is tried again after this long, doubling up to a second. */\nexport const LAG_RETRY_MS = 50;\n\nconst MAX_RETRY_MS = 1000;\n\n/**\n * `options` as a lag, checked: a `TypeError` that names `owner` for a part\n * that is not a number in range, or a `random` that is not a function.\n */\nexport function checkLag(options: PeerLinkLagOptions, owner: string): PeerLinkLag {\n const part = (name: \"ms\" | \"jitter\" | \"loss\", max = Number.POSITIVE_INFINITY): number => {\n const value = options[name] ?? 0;\n if (typeof value !== \"number\" || !Number.isFinite(value) || value < 0 || value > max) {\n throw new TypeError(`${owner}: ${name} must be a number from 0${max === 1 ? \" to 1\" : \"\"}`);\n }\n return value;\n };\n const lag = Object.freeze({ ms: part(\"ms\"), jitter: part(\"jitter\"), loss: part(\"loss\", 1) });\n if (options.random !== undefined && typeof options.random !== \"function\") {\n throw new TypeError(`${owner}: random must be a function returning numbers from 0 to 1`);\n }\n return lag;\n}\n\n/**\n * Hands a held message on as it comes due. False when its path could not\n * take it: a `rel` message then waits and is tried again, a `fast` one is\n * gone.\n */\nexport type LagDeliver = () => boolean | void;\n\n/** One direction's messages, held for a lag. */\nexport interface LagQueue {\n /** No lag and nothing held: a message may go at once, past the queue. */\n readonly idle: boolean;\n /** A held `rel` message waits for its path to take it. */\n readonly blocked: boolean;\n /**\n * Holds a message on `label` and calls `deliver` once it is due. False\n * when the lag lost it (a `fast` message): `deliver` is never called.\n */\n push(label: PeerLabel, deliver: LagDeliver): boolean;\n /**\n * Takes `lag` for the messages pushed from now on; those held keep their\n * time. With `null`, what is held goes on the next task, in order.\n */\n set(lag: PeerLinkLag | null, random?: () => number): void;\n /** Delivers what is held now, in the order it was pushed. */\n flush(): void;\n /** Drops what is held, for good: nothing is delivered after. */\n close(): void;\n}\n\ninterface Held {\n readonly label: PeerLabel;\n readonly seq: number;\n readonly deliver: LagDeliver;\n /** When it is due, by `performance.now()`, the clock the timers keep. */\n readonly at: number;\n /** Due: its time came, or the time of a `rel` message held after it did. */\n ready: boolean;\n timer: ReturnType<typeof setTimeout> | undefined;\n}\n\n/** The state one queue keeps. */\ninterface Queue {\n lag: PeerLinkLag | null;\n random: () => number;\n /** The held `rel` messages, in order. */\n readonly rel: Held[];\n /** The held `fast` messages, in the order they were pushed. */\n readonly fast: Set<Held>;\n seq: number;\n /** The next try of the first `rel` message, which its path refused. */\n retry: ReturnType<typeof setTimeout> | undefined;\n retryMs: number;\n /** The flush on the next task, after `set(null)`. */\n flushing: ReturnType<typeof setTimeout> | undefined;\n /** Handing messages on: a call from a `deliver` hands nothing on. */\n delivering: boolean;\n closed: boolean;\n}\n\n/** Hands one message on; one whose `deliver` throws is gone, and its error is thrown on its own. */\nfunction handOn(item: Held): boolean | void {\n try {\n return item.deliver();\n } catch (error) {\n queueMicrotask(() => {\n throw error;\n });\n return true;\n }\n}\n\n/** Hands the first `rel` message on: false when its path refused it, so it waits. */\nfunction deliverRel(queue: Queue, item: Held): boolean {\n if (handOn(item) === false && !queue.closed) {\n queue.retry = setTimeout(() => {\n queue.retry = undefined;\n drain(queue);\n }, queue.retryMs);\n queue.retryMs = Math.min(MAX_RETRY_MS, queue.retryMs * 2);\n return false;\n }\n queue.retryMs = LAG_RETRY_MS;\n if (queue.rel[0] === item) {\n queue.rel.shift();\n clearTimeout(item.timer);\n }\n return true;\n}\n\nfunction deliverFast(queue: Queue, item: Held): void {\n if (queue.fast.delete(item)) {\n clearTimeout(item.timer);\n handOn(item);\n }\n}\n\n/** Hands on the `rel` messages that are due, in order, up to one that waits for its path. */\nfunction drain(queue: Queue): void {\n if (queue.delivering || queue.retry !== undefined) {\n return;\n }\n queue.delivering = true;\n try {\n for (let item = queue.rel[0]; item?.ready === true; item = queue.rel[0]) {\n if (queue.closed || !deliverRel(queue, item)) {\n return;\n }\n }\n } finally {\n queue.delivering = false;\n }\n}\n\n/** A `rel` message's time came: each one ahead of it is due too. */\nfunction due(queue: Queue, item: Held): void {\n item.timer = undefined;\n const at = queue.rel.indexOf(item);\n for (const ahead of queue.rel.slice(0, at + 1)) {\n ahead.ready = true;\n }\n drain(queue);\n}\n\n/**\n * Hands the message on once its time came, by `performance.now()`: a timer\n * may fire a little early by the event loop's clock.\n */\nfunction arm(queue: Queue, item: Held): void {\n item.timer = setTimeout(\n () => {\n if (item.at > performance.now()) {\n arm(queue, item);\n } else if (item.label === \"rel\") {\n due(queue, item);\n } else {\n deliverFast(queue, item);\n }\n },\n Math.max(0, Math.ceil(item.at - performance.now())),\n );\n}\n\n/** The next message to flush, in the order they were pushed: `rel` ones up to one that waits. */\nfunction nextHeld(queue: Queue): Held | undefined {\n const [first] = queue.fast;\n const rel = queue.retry === undefined ? queue.rel[0] : undefined;\n return rel !== undefined && (first === undefined || rel.seq < first.seq) ? rel : first;\n}\n\nfunction flush(queue: Queue): void {\n clearTimeout(queue.flushing);\n queue.flushing = undefined;\n if (queue.delivering) {\n return;\n }\n // What is held now; what a `deliver` pushes waits for its time.\n const until = queue.seq;\n for (const item of queue.rel) {\n item.ready = true;\n }\n queue.delivering = true;\n try {\n for (let item = nextHeld(queue); item !== undefined; item = nextHeld(queue)) {\n if (queue.closed || item.seq >= until) {\n return;\n }\n if (item.label === \"rel\") {\n deliverRel(queue, item);\n } else {\n deliverFast(queue, item);\n }\n }\n } finally {\n queue.delivering = false;\n }\n}\n\nfunction push(queue: Queue, label: PeerLabel, deliver: LagDeliver): boolean {\n const { lag } = queue;\n if (queue.closed) {\n return true;\n }\n if (lag !== null && label === \"fast\" && queue.random() < lag.loss) {\n return false;\n }\n let at = performance.now();\n if (lag !== null) {\n at += lag.ms + queue.random() * lag.jitter;\n }\n if (label === \"rel\") {\n // Due no sooner than the `rel` message held ahead of it.\n at = Math.max(at, queue.rel.at(-1)?.at ?? at);\n }\n const item: Held = { label, seq: queue.seq, deliver, at, ready: false, timer: undefined };\n queue.seq += 1;\n if (label === \"rel\") {\n queue.rel.push(item);\n } else {\n queue.fast.add(item);\n }\n arm(queue, item);\n return true;\n}\n\nfunction close(queue: Queue): void {\n queue.closed = true;\n for (const item of [...queue.rel, ...queue.fast]) {\n clearTimeout(item.timer);\n }\n queue.rel.length = 0;\n queue.fast.clear();\n clearTimeout(queue.retry);\n clearTimeout(queue.flushing);\n queue.retry = undefined;\n queue.flushing = undefined;\n}\n\n/** A queue that holds messages for `lag` (`null`: none), drawing jitter and loss from `random`. */\nexport function createLagQueue(\n lag: PeerLinkLag | null,\n random: () => number = Math.random,\n): LagQueue {\n const queue: Queue = {\n lag,\n random,\n rel: [],\n fast: new Set(),\n seq: 0,\n retry: undefined,\n retryMs: LAG_RETRY_MS,\n flushing: undefined,\n delivering: false,\n closed: false,\n };\n return Object.freeze({\n get idle() {\n return queue.lag === null && queue.rel.length === 0 && queue.fast.size === 0;\n },\n get blocked() {\n return queue.retry !== undefined;\n },\n push: (label: PeerLabel, deliver: LagDeliver) => push(queue, label, deliver),\n set(next: PeerLinkLag | null, nextRandom: () => number = Math.random): void {\n if (queue.closed) {\n return;\n }\n queue.lag = next;\n queue.random = nextRandom;\n if (next === null && (queue.rel.length > 0 || queue.fast.size > 0)) {\n queue.flushing ??= setTimeout(() => {\n flush(queue);\n }, 0);\n }\n },\n flush: () => {\n flush(queue);\n },\n close: () => {\n close(queue);\n },\n });\n}\n","// What every peer link does whatever carries it (RFC 0003 section 12.5): its\n// state and events, its stats, the clock pings and the watchdog. Once open,\n// a link pings on `fast` (`QUICK_PINGS` pings `QUICK_PING_MS` apart, then\n// one every `PING_MS`) and answers the peer's pings, so the two clocks sync\n// and each side hears the other at least that often: a link that hears\n// nothing for `WATCHDOG_MS` closes as `lost`. `createPeerLink` drives a core\n// from signals and paths (`link.ts`); the loopback double of `./testing`\n// drives two from an in-process pair, and host mode's in-process link\n// (`../host/local.ts`) two more. Host mode's messages travel beside the\n// app's, apart from them: `controlOf(link)` sends and hears them, and a\n// link's `send` and `message` event never do. `setLag` holds every message\n// on its way out and in (`lag.ts`), the pings and host mode's included.\n//\n// React-free.\n\nimport type { HostMessage, PeerLabel, PeerMessage } from \"../../protocol/envelope\";\nimport { createClock, type ClockState, type PeerClock } from \"./clock\";\nimport { decodeMessage, encodeMessage, messageBytes } from \"./codec\";\nimport { checkLag, createLagQueue, type LagQueue } from \"./lag\";\nimport type {\n PeerCloseReason,\n PeerLink,\n PeerLinkEvents,\n PeerLinkLag,\n PeerLinkLagOptions,\n PeerLinkState,\n PeerLinkStats,\n PeerPath,\n} from \"./types\";\n\n/** Clock pings a link sends `QUICK_PING_MS` apart as it opens, so its clock syncs at once. */\nexport const QUICK_PINGS = 6;\n\n/** The time between the clock pings a link sends as it opens. */\nexport const QUICK_PING_MS = 80;\n\n/** The time between the clock pings of an open link, after its first ones. */\nexport const PING_MS = 500;\n\n/** A link that hears nothing from its peer for this long closes as `lost`. */\nexport const WATCHDOG_MS = 8000;\n\n/** What carries a core's messages, and what the core tells its owner. */\nexport interface CoreHooks {\n /** Sends one message's text on `label`; false when it was dropped. */\n send(label: PeerLabel, text: string): boolean;\n /**\n * The link closed, once: take its paths down. `fromHere` when this side\n * closed it (`close()`, the watchdog, `fail`), so the peer must be told.\n */\n closed(reason: PeerCloseReason, fromHere: boolean): void;\n}\n\n/** What a core is made from. */\nexport interface CoreOptions {\n readonly room: string;\n readonly peer: string;\n readonly hooks: CoreHooks;\n /** The longest message's text, in UTF-8 bytes: the server's `limits.peers.maxMessageBytes`. */\n readonly maxMessageBytes: () => number;\n /** This side's clock, in milliseconds. Default `performance.now()`. */\n readonly now?: () => number;\n /**\n * False for a link that cannot be lost (in process): it never closes as\n * `lost`, since its pings run on this side's timers alone, which a hidden\n * tab throttles. Default true.\n */\n readonly watchdog?: boolean;\n}\n\n/** A link, and how its owner drives it. */\nexport interface LinkCore {\n readonly link: PeerLink;\n /** A message from the peer, on `label`, from either path. */\n receive(data: unknown, label: PeerLabel): void;\n /** A path carries the link: it opens, or moves to that path. */\n carry(path: PeerPath): void;\n /** The link ended from outside (the peer's bye, the socket's disconnect): nothing is sent. */\n end(reason: PeerCloseReason): void;\n /** This side ends the link for `reason` (a timeout, a failed path): the owner tells the peer. */\n fail(reason: PeerCloseReason): void;\n /** The peer's user, as the server named it in the peer's first signal. */\n identify(userId: string): void;\n}\n\n/** Throws `error` on its own, after the caller went on: one failing listener stops no other. */\nexport function throwLater(error: unknown): void {\n queueMicrotask(() => {\n throw error;\n });\n}\n\ntype Listeners = { readonly [E in keyof PeerLinkEvents]: Set<PeerLinkEvents[E]> };\n\n/** The state one core keeps. */\ninterface Core {\n readonly options: CoreOptions;\n readonly now: () => number;\n readonly clock: ClockState;\n readonly listeners: Listeners;\n state: PeerLinkState;\n path: PeerPath | undefined;\n closeReason: PeerCloseReason | undefined;\n userId: string | undefined;\n lastHeard: number;\n pingsLeft: number;\n timer: ReturnType<typeof setTimeout> | undefined;\n readonly stats: { sent: number; received: number; dropped: number; simulatedLoss: number };\n /** Who hears host mode's messages (`controlOf`). */\n readonly control: Set<(message: HostMessage, label: PeerLabel) => void>;\n /** The lag `setLag` set; `null` for none. */\n lag: PeerLinkLag | null;\n /** This side's messages on their way out, held for the lag. */\n readonly outgoing: LagQueue;\n /** The peer's messages on their way in, held for the lag. */\n readonly incoming: LagQueue;\n}\n\n/** Calls each listener of `event`; one that throws is reported apart, and the others still run. */\nfunction emit<E extends keyof PeerLinkEvents>(\n core: Core,\n event: E,\n ...args: Parameters<PeerLinkEvents[E]>\n): void {\n for (const listener of [...core.listeners[event]]) {\n try {\n (listener as (...values: Parameters<PeerLinkEvents[E]>) => void)(...args);\n } catch (error) {\n throwLater(error);\n }\n }\n}\n\nfunction finish(core: Core, reason: PeerCloseReason, fromHere: boolean): void {\n if (core.state === \"closed\") {\n return;\n }\n core.state = \"closed\";\n core.path = undefined;\n core.closeReason = reason;\n clearTimeout(core.timer);\n core.outgoing.close();\n core.incoming.close();\n core.options.hooks.closed(reason, fromHere);\n emit(core, \"state\", \"closed\", undefined);\n emit(core, \"close\", reason);\n for (const set of Object.values(core.listeners) as Set<unknown>[]) {\n set.clear();\n }\n core.control.clear();\n}\n\n/** A ping, then the next one: the watchdog's beat too. */\nfunction tick(core: Core): void {\n core.timer = undefined;\n if (core.state !== \"open\") {\n return;\n }\n if (core.options.watchdog !== false && core.now() - core.lastHeard > WATCHDOG_MS) {\n finish(core, \"lost\", true);\n return;\n }\n transmit(core, \"fast\", encodeMessage([1, core.now()]), false);\n core.pingsLeft = Math.max(0, core.pingsLeft - 1);\n core.timer = setTimeout(\n () => {\n tick(core);\n },\n core.pingsLeft > 0 ? QUICK_PING_MS : PING_MS,\n );\n}\n\n/** A message's text, checked against the size limit: a `RangeError` over it. */\nfunction textOf(core: Core, message: Parameters<typeof encodeMessage>[0], what: string): string {\n const text = encodeMessage(message);\n const bytes = messageBytes(text);\n const max = core.options.maxMessageBytes();\n if (bytes > max) {\n throw new RangeError(\n `${what}: a message holds at most ${max} bytes as JSON; this one has ${bytes}: split it`,\n );\n }\n return text;\n}\n\n/**\n * Sends one message's text on `label`, on the path at once, or once the lag\n * held it (which may lose a `fast` one). False when it was dropped: the path\n * refused it, or a held `rel` message waits for the path. An app message\n * (`app`) counts in `stats`: `sent` once a path took it, `dropped` for one\n * dropped, `simulatedLoss` for one the lag lost.\n */\nfunction transmit(core: Core, label: PeerLabel, text: string, app: boolean): boolean {\n const { stats, outgoing } = core;\n const put = (): boolean => {\n const sent = core.state === \"open\" && core.options.hooks.send(label, text);\n if (app && sent) {\n stats.sent += 1;\n }\n return sent;\n };\n if (outgoing.idle) {\n const sent = put();\n if (app && !sent) {\n stats.dropped += 1;\n }\n return sent;\n }\n if (label === \"rel\" && outgoing.blocked) {\n if (app) {\n stats.dropped += 1;\n }\n return false;\n }\n const held = outgoing.push(label, () => {\n const sent = put();\n // A `rel` message the path refused waits and goes again: not dropped.\n if (app && !sent && label === \"fast\") {\n stats.dropped += 1;\n }\n return sent;\n });\n if (app && !held) {\n stats.simulatedLoss += 1;\n }\n return true;\n}\n\nfunction send(core: Core, label: PeerLabel, data: unknown): boolean {\n if (label !== \"rel\" && label !== \"fast\") {\n throw new TypeError(`PeerLink.send: label must be \"rel\" or \"fast\", not ${String(label)}`);\n }\n const text = textOf(core, [0, data], \"PeerLink.send\");\n if (core.state !== \"open\") {\n core.stats.dropped += 1;\n return false;\n }\n return transmit(core, label, text, true);\n}\n\n/** A message from the peer, once the lag held it (at once without one). */\nfunction hear(core: Core, message: PeerMessage, label: PeerLabel): void {\n if (core.state === \"closed\") {\n return;\n }\n core.lastHeard = core.now();\n if (message[0] === 0) {\n core.stats.received += 1;\n emit(core, \"message\", message[1], label);\n } else if (message[0] === 1) {\n if (core.state === \"open\") {\n transmit(core, \"fast\", encodeMessage([2, message[1], core.now()]), false);\n }\n } else if (message[0] === 2) {\n core.clock.pong(message[1], message[2], core.now());\n } else {\n for (const listener of [...core.control]) {\n try {\n listener(message, label);\n } catch (error) {\n throwLater(error);\n }\n }\n }\n}\n\nfunction receive(core: Core, data: unknown, label: PeerLabel): void {\n const message = core.state === \"closed\" ? undefined : decodeMessage(data);\n if (message === undefined) {\n return;\n }\n if (core.incoming.idle) {\n hear(core, message, label);\n return;\n }\n const held = core.incoming.push(label, () => {\n hear(core, message, label);\n });\n if (!held && message[0] === 0) {\n core.stats.simulatedLoss += 1;\n }\n}\n\nfunction setLag(core: Core, options: PeerLinkLagOptions | null): void {\n if (options !== null && typeof options !== \"object\") {\n throw new TypeError(\"PeerLink.setLag: pass { ms, jitter, loss }, or null for none\");\n }\n const lag = options === null ? null : checkLag(options, \"PeerLink.setLag\");\n if (core.state === \"closed\") {\n return;\n }\n core.lag = lag === null || (lag.ms === 0 && lag.jitter === 0 && lag.loss === 0) ? null : lag;\n const random = options?.random ?? Math.random;\n core.outgoing.set(core.lag, random);\n core.incoming.set(core.lag, random);\n}\n\nfunction carry(core: Core, path: PeerPath): void {\n if (core.state === \"closed\" || (core.state === \"open\" && core.path === path)) {\n return;\n }\n const opening = core.state === \"connecting\";\n core.state = \"open\";\n core.path = path;\n if (opening) {\n core.lastHeard = core.now();\n core.pingsLeft = QUICK_PINGS;\n emit(core, \"open\", path);\n }\n emit(core, \"state\", \"open\", path);\n if (opening && core.state === \"open\") {\n tick(core);\n }\n}\n\n/** The clock as a link shows it: read-only. */\nfunction clockView(clock: PeerClock): PeerClock {\n return Object.freeze({\n now: () => clock.now(),\n get offset() {\n return clock.offset;\n },\n get rtt() {\n return clock.rtt;\n },\n get jitter() {\n return clock.jitter;\n },\n get synced() {\n return clock.synced;\n },\n });\n}\n\n/** The link the app holds. */\nfunction linkOf(core: Core): PeerLink {\n const { listeners } = core;\n return Object.freeze({\n room: core.options.room,\n peer: core.options.peer,\n get userId() {\n return core.userId;\n },\n get state() {\n return core.state;\n },\n get path() {\n return core.path;\n },\n get closeReason() {\n return core.closeReason;\n },\n send: (label: PeerLabel, data: unknown) => send(core, label, data),\n on<E extends keyof PeerLinkEvents>(event: E, listener: PeerLinkEvents[E]): () => void {\n if (!Object.hasOwn(listeners, event)) {\n throw new TypeError(`PeerLink.on: no event \"${String(event)}\"`);\n }\n listeners[event].add(listener);\n return () => {\n listeners[event].delete(listener);\n };\n },\n clock: clockView(core.clock),\n get stats(): PeerLinkStats {\n return Object.freeze({ ...core.stats });\n },\n setLag: (options: PeerLinkLagOptions | null) => {\n setLag(core, options);\n },\n get lag() {\n return core.lag;\n },\n close: () => {\n finish(core, \"closed\", true);\n },\n });\n}\n\n/** Creates a link's core, connecting. */\nconst CORES = new WeakMap<PeerLink, Core>();\n\n/** What host mode reaches of a link: its own messages, kept apart from the app's. */\nexport interface LinkControl {\n /**\n * Sends a host mode message; false when the link is not open or its path\n * dropped it. A `RangeError` for one over the message limit.\n */\n send(label: PeerLabel, message: HostMessage): boolean;\n /** Calls `listener` for each host mode message from the peer; returns its removal. */\n on(listener: (message: HostMessage, label: PeerLabel) => void): () => void;\n}\n\n/** The host mode side of a link made by `createPeerLink` or the loopback double; `undefined` for any other object. */\nexport function controlOf(link: PeerLink): LinkControl | undefined {\n const core = CORES.get(link);\n if (core === undefined) {\n return undefined;\n }\n return {\n send: (label, message) =>\n core.state === \"open\" && transmit(core, label, textOf(core, message, \"host mode\"), false),\n on(listener) {\n if (core.state === \"closed\") {\n return () => undefined;\n }\n core.control.add(listener);\n return () => {\n core.control.delete(listener);\n };\n },\n };\n}\n\nexport function createLinkCore(options: CoreOptions): LinkCore {\n const now = options.now ?? (() => performance.now());\n const core: Core = {\n options,\n now,\n clock: createClock(now),\n listeners: { message: new Set(), open: new Set(), state: new Set(), close: new Set() },\n state: \"connecting\",\n path: undefined,\n closeReason: undefined,\n userId: undefined,\n lastHeard: 0,\n pingsLeft: 0,\n timer: undefined,\n stats: { sent: 0, received: 0, dropped: 0, simulatedLoss: 0 },\n control: new Set(),\n lag: null,\n outgoing: createLagQueue(null),\n incoming: createLagQueue(null),\n };\n const link = linkOf(core);\n CORES.set(link, core);\n return {\n link,\n receive: (data, label) => {\n receive(core, data, label);\n },\n carry: (path) => {\n carry(core, path);\n },\n end: (reason) => {\n finish(core, reason, false);\n },\n fail: (reason) => {\n finish(core, reason, true);\n },\n identify: (userId) => {\n core.userId ??= userId;\n },\n };\n}\n"],"mappings":"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","names":["textOf"]}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildCaller,
|
|
3
3
|
methodKey
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-IIQ3WMJK.js";
|
|
5
5
|
import {
|
|
6
6
|
QuickdrawError,
|
|
7
|
-
fromWire
|
|
7
|
+
fromWire
|
|
8
|
+
} from "./chunk-Q4OGQM5I.js";
|
|
9
|
+
import {
|
|
8
10
|
isRecord
|
|
9
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-QSU47XEX.js";
|
|
10
12
|
|
|
11
13
|
// src/utils/formatting.ts
|
|
12
14
|
function formatCurrency(value, currency = "USD", locale = "en-US") {
|
|
@@ -272,4 +274,4 @@ export {
|
|
|
272
274
|
parseJWTPayload,
|
|
273
275
|
createServerCaller
|
|
274
276
|
};
|
|
275
|
-
//# sourceMappingURL=chunk-
|
|
277
|
+
//# sourceMappingURL=chunk-CYE4UBOB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/formatting.ts","../src/utils/navigation.ts","../src/utils/jwt.ts","../src/client/serverCaller.ts"],"sourcesContent":["// Display formatting, carried over unchanged from 4.1\n// (4.1 `src/client/utils/formatting.ts`) except that the `value == null`\n// checks are spelled out. Pure and DOM-free: the `./utils` entry exports them\n// for React server components as well as the browser.\n\n/**\n * Format a currency value for display.\n */\nexport function formatCurrency(\n value: string | number | null | undefined,\n currency = \"USD\",\n locale = \"en-US\",\n): string {\n if (value === null || value === undefined) return \"-\";\n const num = typeof value === \"string\" ? parseFloat(value) : value;\n if (isNaN(num)) return \"-\";\n return new Intl.NumberFormat(locale, {\n style: \"currency\",\n currency,\n }).format(num);\n}\n\n/**\n * Format a number for display with optional decimal places.\n */\nexport function formatNumber(\n value: string | number | null | undefined,\n options?: Intl.NumberFormatOptions,\n locale = \"en-US\",\n): string {\n if (value === null || value === undefined) return \"-\";\n const num = typeof value === \"string\" ? parseFloat(value) : value;\n if (isNaN(num)) return \"-\";\n return new Intl.NumberFormat(locale, options).format(num);\n}\n\n/**\n * Format a date for display.\n */\nexport function formatDate(\n value: string | Date | null | undefined,\n options?: Intl.DateTimeFormatOptions,\n locale = \"en-US\",\n): string {\n if (!value) return \"-\";\n const date = typeof value === \"string\" ? new Date(value) : value;\n if (isNaN(date.getTime())) return \"-\";\n return date.toLocaleDateString(\n locale,\n options ?? {\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n },\n );\n}\n\n/**\n * Format a date with time for display.\n */\nexport function formatDateTime(\n value: string | Date | null | undefined,\n options?: Intl.DateTimeFormatOptions,\n locale = \"en-US\",\n): string {\n if (!value) return \"-\";\n const date = typeof value === \"string\" ? new Date(value) : value;\n if (isNaN(date.getTime())) return \"-\";\n return date.toLocaleString(\n locale,\n options ?? {\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n },\n );\n}\n\n/**\n * Truncate a string to a maximum length.\n */\nexport function truncate(value: string | null | undefined, maxLength = 50): string {\n if (!value) return \"-\";\n if (value.length <= maxLength) return value;\n return `${value.slice(0, maxLength - 3)}...`;\n}\n\n/**\n * Format a percentage for display.\n */\nexport function formatPercent(\n value: string | number | null | undefined,\n decimals = 1,\n locale = \"en-US\",\n): string {\n if (value === null || value === undefined) return \"-\";\n const num = typeof value === \"string\" ? parseFloat(value) : value;\n if (isNaN(num)) return \"-\";\n return new Intl.NumberFormat(locale, {\n style: \"percent\",\n minimumFractionDigits: decimals,\n maximumFractionDigits: decimals,\n }).format(num / 100);\n}\n","// Navigation helpers, carried over unchanged from 4.1\n// (4.1 `src/client/utils/navigation.ts`). Pure and DOM-free: the `./utils`\n// entry exports them for React server components as well as the browser.\n\nexport interface NavItem {\n id: string;\n label: string;\n href: string;\n icon?: unknown;\n children?: NavItem[];\n defaultExpanded?: boolean;\n requireAuth?: boolean;\n dynamicChildren?: boolean;\n serviceName?: string;\n minAccessLevel?: string;\n}\n\nexport interface BreadcrumbItem {\n label: string;\n href: string;\n siblings?: { label: string; href: string }[];\n}\n\n/**\n * Find a nav item by its href (exact match, searching children recursively).\n */\nexport function findNavItemByHref(items: NavItem[], href: string): NavItem | undefined {\n for (const item of items) {\n if (item.href === href) return item;\n if (item.children) {\n const found = findNavItemByHref(item.children, href);\n if (found) return found;\n }\n }\n return undefined;\n}\n\n/**\n * Find the parent nav item containing a child with the given href.\n */\nexport function findParentNavItem(items: NavItem[], href: string): NavItem | undefined {\n for (const item of items) {\n if (item.children) {\n const childMatch = item.children.find((c) => c.href === href);\n if (childMatch) return item;\n const nested = findParentNavItem(item.children, href);\n if (nested) return nested;\n }\n }\n return undefined;\n}\n\n/**\n * Build breadcrumb items from a pathname using the provided navigation config.\n */\nexport function buildBreadcrumbs(pathname: string, navigation: NavItem[]): BreadcrumbItem[] {\n const breadcrumbs: BreadcrumbItem[] = [];\n\n if (pathname === \"/\") {\n breadcrumbs.push({\n label: \"Home\",\n href: \"/\",\n siblings: navigation\n .filter((item) => item.href === \"/\" || !item.requireAuth)\n .map((item) => ({ label: item.label, href: item.href })),\n });\n return breadcrumbs;\n }\n\n const segments = pathname.split(\"/\").filter(Boolean);\n let currentPath = \"\";\n\n for (const segment of segments) {\n currentPath += `/${segment}`;\n const navItem = findNavItemByHref(navigation, currentPath);\n\n if (navItem) {\n const parent = findParentNavItem(navigation, currentPath);\n const siblingItems = parent?.children ?? navigation;\n\n breadcrumbs.push({\n label: navItem.label,\n href: navItem.href,\n siblings: siblingItems.map((item) => ({\n label: item.label,\n href: item.href,\n })),\n });\n } else {\n breadcrumbs.push({\n label: segment,\n href: currentPath,\n });\n }\n }\n\n return breadcrumbs;\n}\n\n/**\n * Check if a route requires authentication by searching the navigation config.\n */\nexport function routeRequiresAuth(\n pathname: string,\n navigation: NavItem[],\n userMenuItems?: NavItem[],\n): boolean {\n const navItem = findNavItemByHref(navigation, pathname);\n if (navItem) return navItem.requireAuth ?? false;\n\n if (userMenuItems) {\n const userItem = findNavItemByHref(userMenuItems, pathname);\n if (userItem) return userItem.requireAuth ?? false;\n }\n\n const segments = pathname.split(\"/\").filter(Boolean);\n let currentPath = \"\";\n for (const segment of segments) {\n currentPath += `/${segment}`;\n const item = findNavItemByHref(navigation, currentPath);\n if (item?.requireAuth) return true;\n }\n\n return false;\n}\n","// Reading a JWT's payload without verifying it, carried over from 4.1\n// (4.1 `src/client/utils/auth.ts:29-63`). Pure and DOM-free (`atob` is a\n// global in browsers, Node and React Native), so the `./utils` entry exports\n// it for React server components too. The helpers that keep the token in\n// `localStorage` stay on `./client` (`../client/auth.ts`).\n//\n// A JWT's segments are base64url (RFC 7515): `-` and `_` where base64 has\n// `+` and `/`, and no padding. 4.1 handed them to `atob` as they were, which\n// refuses those characters and a length that is not a multiple of 4; here a\n// segment is turned into padded base64 first, and its bytes read as UTF-8.\n\nexport interface JWTPayload {\n userId: string;\n email?: string;\n}\n\n/** The text of a base64url segment: its bytes as UTF-8 where `TextDecoder` exists, else as Latin-1. */\nfunction decodeSegment(segment: string): string {\n const base64 = segment.replaceAll(\"-\", \"+\").replaceAll(\"_\", \"/\");\n const binary = atob(base64.padEnd(base64.length + ((4 - (base64.length % 4)) % 4), \"=\"));\n if (typeof TextDecoder === \"undefined\") {\n return binary;\n }\n return new TextDecoder().decode(Uint8Array.from(binary, (char) => char.charCodeAt(0)));\n}\n\n/**\n * Parse JWT payload client-side (not verified, for display purposes only).\n */\nexport function parseJWTPayload(token: string): JWTPayload | null {\n try {\n const parts = token.split(\".\");\n if (parts.length !== 3 || !parts[1]) return null;\n\n const decoded = decodeSegment(parts[1]);\n const payload: unknown = JSON.parse(decoded);\n\n if (\n typeof payload === \"object\" &&\n payload !== null &&\n \"userId\" in payload &&\n typeof (payload as { userId: unknown }).userId === \"string\"\n ) {\n const typedPayload = payload as { userId: string; email?: string };\n return {\n userId: typedPayload.userId,\n email: typedPayload.email,\n };\n }\n return null;\n } catch {\n return null;\n }\n}\n","// Calls over the HTTP transport (RFC 0003 section 10), for code that has no\n// socket: React server components prefetching for hydration, route handlers,\n// scripts. `POST {url}/qd/{service}/{method}` with the input as the JSON body,\n// answered `{ ok: true, d }` or `{ ok: false, e }` (`server/transports/http.ts`).\n// Every request says `Content-Type: application/json`, even without a body:\n// the server refuses any other, so a cross-site page cannot call a method\n// with a user's session cookie without a CORS preflight.\n//\n// `prefetch` fills a TanStack `QueryClient` under the key the hooks read\n// (`keys.ts`), so `dehydrate` and `HydrationBoundary` hand the result to the\n// browser's `useQuery`.\n//\n// React-free and dependency-free (it uses the global `fetch`), so the\n// isomorphic `./utils` entry exports it: a module that begins with\n// \"use client\", as `./client` does, cannot be called from a server component.\n// `prefetch` takes the part of a `QueryClient` it calls (`QueryClientLike`),\n// so neither `./utils` nor `./client/core`, which re-exports it, names\n// TanStack in its types either.\n\nimport type { AnyContract } from \"../contract/defineContract\";\nimport type { ContractMap, InputOf, KindOf, MethodName, OutputOf } from \"../contract/infer\";\nimport { QuickdrawError, fromWire } from \"../protocol/errors\";\nimport { isRecord } from \"../protocol/guards\";\nimport { methodKey, type MethodQueryKey } from \"./keys\";\nimport { buildCaller, type MethodTarget } from \"./members\";\n\n/** Headers sent with every call, or a function that returns them for each call. */\nexport type ServerCallerHeaders = HeadersInit | (() => HeadersInit | Promise<HeadersInit>);\n\n/** Options of {@link createServerCaller}. */\nexport interface ServerCallerOptions {\n /** The server's URL: `\"http://api:4000\"`. */\n readonly url: string;\n /** The path the server serves calls under, as its `http.path`. Default `\"/qd\"`. */\n readonly path?: string;\n /**\n * Sent with every call: the credentials the server's `authenticate` reads,\n * such as the incoming request's `cookie` or an `authorization` bearer\n * token. `Content-Type` is always `application/json`.\n */\n readonly headers?: ServerCallerHeaders;\n /** The `fetch` to call with. Default: the global one. */\n readonly fetch?: typeof fetch;\n}\n\n/**\n * The part of a TanStack `QueryClient` that `prefetch` calls, which any\n * `QueryClient` is: `prefetchQuery` with the method's key and a function\n * that calls it.\n */\nexport interface QueryClientLike {\n prefetchQuery(options: {\n readonly queryKey: MethodQueryKey;\n readonly queryFn: (context: { readonly signal: AbortSignal }) => Promise<unknown>;\n }): Promise<void>;\n}\n\n/** Options of one HTTP call. */\nexport interface ServerCallOptions {\n /** Aborting it cancels the request; the call rejects with `CANCELLED`. */\n readonly signal?: AbortSignal;\n}\n\ntype InputArgs<C extends AnyContract, M extends MethodName<C>, Rest extends unknown[]> =\n undefined extends InputOf<C, M>\n ? [input?: InputOf<C, M>, ...rest: Rest]\n : [input: InputOf<C, M>, ...rest: Rest];\n\n/** A query of the server caller. */\nexport interface ServerQuery<C extends AnyContract, M extends MethodName<C>> {\n /** Calls the query; resolves with its data or rejects with its `QuickdrawError`. */\n call(...args: InputArgs<C, M, [options?: ServerCallOptions]>): Promise<OutputOf<C, M>>;\n /** The key the query's result is cached under, the one the client's hooks read. */\n key(...args: InputArgs<C, M, []>): MethodQueryKey<InputOf<C, M>>;\n /** Calls the query into `queryClient`, as `queryClient.prefetchQuery` does: it never rejects. */\n prefetch(queryClient: QueryClientLike, ...args: InputArgs<C, M, []>): Promise<void>;\n}\n\n/** A mutation of the server caller. */\nexport interface ServerMutation<C extends AnyContract, M extends MethodName<C>> {\n /** Calls the mutation; resolves with its data or rejects with its `QuickdrawError`. */\n call(...args: InputArgs<C, M, [options?: ServerCallOptions]>): Promise<OutputOf<C, M>>;\n}\n\n/** One method of the server caller, by its kind. */\nexport type ServerMethod<C extends AnyContract, M extends MethodName<C>> =\n KindOf<C, M> extends \"query\" ? ServerQuery<C, M> : ServerMutation<C, M>;\n\n/** The server caller of a map of contracts: `caller.task.get.call(input)`. */\nexport type ServerCaller<Contracts extends ContractMap> = {\n readonly [Key in keyof Contracts]: {\n readonly [M in MethodName<Contracts[Key]>]: ServerMethod<Contracts[Key], M>;\n };\n};\n\ninterface Settings {\n readonly endpoint: string;\n readonly headers: ServerCallerHeaders | undefined;\n readonly fetch: typeof fetch;\n}\n\nfunction settingsOf(options: ServerCallerOptions): Settings {\n if (typeof options.url !== \"string\" || options.url === \"\") {\n throw new TypeError(\"createServerCaller: url must be the server's URL\");\n }\n const path = (options.path ?? \"/qd\").replace(/^\\/+|\\/+$/g, \"\");\n const base = options.url.replace(/\\/+$/, \"\");\n const fetchImpl = options.fetch ?? globalThis.fetch;\n if (typeof fetchImpl !== \"function\") {\n throw new TypeError(\"createServerCaller: there is no global fetch; pass options.fetch\");\n }\n return {\n endpoint: path === \"\" ? base : `${base}/${path}`,\n headers: options.headers,\n fetch: fetchImpl,\n };\n}\n\nasync function headersOf(settings: Settings): Promise<Headers> {\n const given =\n typeof settings.headers === \"function\" ? await settings.headers() : settings.headers;\n const headers = new Headers(given);\n headers.set(\"content-type\", \"application/json\");\n return headers;\n}\n\nfunction withCause(error: QuickdrawError, cause: unknown): QuickdrawError {\n error.cause = cause;\n return error;\n}\n\nasync function readReply(response: Response, target: MethodTarget): Promise<unknown> {\n const name = `${target.service}.${target.method}`;\n let reply: unknown;\n try {\n reply = await response.json();\n } catch (error) {\n const message = `${name} answered HTTP ${response.status} without a JSON reply`;\n throw withCause(new QuickdrawError(\"INTERNAL\", message), error);\n }\n if (isRecord(reply) && reply.ok === true) {\n return reply.d;\n }\n if (isRecord(reply) && reply.ok === false) {\n throw fromWire(reply.e);\n }\n throw new QuickdrawError(\n \"INTERNAL\",\n `${name} answered HTTP ${response.status} with no call reply`,\n );\n}\n\nasync function post(\n settings: Settings,\n target: MethodTarget,\n input: unknown,\n signal: AbortSignal | undefined,\n): Promise<unknown> {\n const url = `${settings.endpoint}/${encodeURIComponent(target.service)}/${encodeURIComponent(target.method)}`;\n let response: Response;\n try {\n response = await settings.fetch(url, {\n method: \"POST\",\n headers: await headersOf(settings),\n body: input === undefined ? undefined : JSON.stringify(input),\n signal,\n });\n } catch (error) {\n if (signal?.aborted === true) {\n throw withCause(new QuickdrawError(\"CANCELLED\", \"The call was cancelled\"), error);\n }\n const message = `${target.service}.${target.method}: the HTTP request failed`;\n throw withCause(new QuickdrawError(\"INTERNAL\", message), error);\n }\n return readReply(response, target);\n}\n\nfunction memberOf(settings: Settings, target: MethodTarget): object {\n const callMethod = (input?: unknown, options?: ServerCallOptions): Promise<unknown> =>\n post(settings, target, input, options?.signal);\n if (target.kind !== \"query\") {\n return Object.freeze({ call: callMethod });\n }\n const key = (input?: unknown): MethodQueryKey => methodKey(target.service, target.method, input);\n return Object.freeze({\n call: callMethod,\n key,\n prefetch: (queryClient: QueryClientLike, input?: unknown): Promise<void> =>\n queryClient.prefetchQuery({\n queryKey: key(input),\n queryFn: ({ signal }) => post(settings, target, input, signal),\n }),\n });\n}\n\n/**\n * Creates a caller that calls the server's methods over HTTP:\n * `caller.task.get.call(input)`, and for queries `key(input)` and\n * `prefetch(queryClient, input)`. Use it where there is no socket, such as a\n * React server component; import it from `@fitzzero/quickdraw-core/utils`\n * there, since `./client` is client code.\n *\n * @example\n * const server = createServerCaller({ task }, {\n * url: process.env.API_URL,\n * headers: { cookie: (await cookies()).toString() },\n * });\n * const queryClient = new QueryClient();\n * await server.task.get.prefetch(queryClient, { id });\n * return <HydrationBoundary state={dehydrate(queryClient)}><Task id={id} /></HydrationBoundary>;\n */\nexport function createServerCaller<const Contracts extends ContractMap>(\n contracts: Contracts,\n options: ServerCallerOptions,\n): ServerCaller<Contracts> {\n const settings = settingsOf(options);\n return Object.freeze(\n buildCaller(\"createServerCaller\", contracts, (target) => memberOf(settings, target)),\n ) as ServerCaller<Contracts>;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// src/contract/logger.ts
|
|
2
|
+
var LEVELS = ["debug", "info", "warn", "error"];
|
|
3
|
+
function inProduction() {
|
|
4
|
+
try {
|
|
5
|
+
return process.env.NODE_ENV === "production";
|
|
6
|
+
} catch {
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function createConsoleLogger(options = {}) {
|
|
11
|
+
const { level } = options;
|
|
12
|
+
if (level !== void 0 && !LEVELS.includes(level)) {
|
|
13
|
+
throw new TypeError(
|
|
14
|
+
`createConsoleLogger: level must be "debug", "info", "warn" or "error", not ${JSON.stringify(level)}`
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
const writes = (at) => LEVELS.indexOf(at) >= LEVELS.indexOf(level ?? (inProduction() ? "info" : "debug"));
|
|
18
|
+
const logger = {
|
|
19
|
+
info: (message, meta) => {
|
|
20
|
+
if (writes("info")) {
|
|
21
|
+
console.log(`[INFO] ${message}`, meta ?? "");
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
warn: (message, meta) => {
|
|
25
|
+
if (writes("warn")) {
|
|
26
|
+
console.warn(`[WARN] ${message}`, meta ?? "");
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
error: (message, meta) => {
|
|
30
|
+
if (writes("error")) {
|
|
31
|
+
console.error(`[ERROR] ${message}`, meta ?? "");
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
debug: (message, meta) => {
|
|
35
|
+
if (writes("debug")) {
|
|
36
|
+
console.debug(`[DEBUG] ${message}`, meta ?? "");
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
child: () => logger
|
|
40
|
+
};
|
|
41
|
+
return logger;
|
|
42
|
+
}
|
|
43
|
+
var consoleLogger = createConsoleLogger();
|
|
44
|
+
function isPrismaError(error) {
|
|
45
|
+
return /^PrismaClient\w*Error$/u.test(error.name);
|
|
46
|
+
}
|
|
47
|
+
var META_KEYS = /* @__PURE__ */ new Set([
|
|
48
|
+
"modelName",
|
|
49
|
+
"model_name",
|
|
50
|
+
"operation",
|
|
51
|
+
"target",
|
|
52
|
+
"field_name",
|
|
53
|
+
"column_name",
|
|
54
|
+
"argument_name",
|
|
55
|
+
"relation_name",
|
|
56
|
+
"model_a_name",
|
|
57
|
+
"model_b_name",
|
|
58
|
+
"object_name",
|
|
59
|
+
"constraint",
|
|
60
|
+
"table",
|
|
61
|
+
"column",
|
|
62
|
+
"path",
|
|
63
|
+
"code"
|
|
64
|
+
]);
|
|
65
|
+
var ADAPTER_CAUSE_KEYS = /* @__PURE__ */ new Set([
|
|
66
|
+
"kind",
|
|
67
|
+
"originalCode",
|
|
68
|
+
"code",
|
|
69
|
+
"constraint",
|
|
70
|
+
"column",
|
|
71
|
+
"table"
|
|
72
|
+
]);
|
|
73
|
+
function isObject(value) {
|
|
74
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
75
|
+
}
|
|
76
|
+
function picked(value, keys) {
|
|
77
|
+
return Object.fromEntries(Object.entries(value).filter(([key]) => keys.has(key)));
|
|
78
|
+
}
|
|
79
|
+
function metaOf(meta) {
|
|
80
|
+
const kept = picked(meta, META_KEYS);
|
|
81
|
+
const adapter = meta.driverAdapterError;
|
|
82
|
+
if (isObject(adapter)) {
|
|
83
|
+
kept.driverAdapterError = {
|
|
84
|
+
...typeof adapter.name === "string" ? { name: adapter.name } : {},
|
|
85
|
+
...isObject(adapter.cause) ? { cause: picked(adapter.cause, ADAPTER_CAUSE_KEYS) } : {}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
return kept;
|
|
89
|
+
}
|
|
90
|
+
var MAX_REASON = 300;
|
|
91
|
+
function prismaSummary(error) {
|
|
92
|
+
const { code, errorCode, meta, clientVersion } = error;
|
|
93
|
+
const described = { name: error.name };
|
|
94
|
+
const kept = typeof code === "string" ? code : errorCode;
|
|
95
|
+
if (typeof kept === "string") {
|
|
96
|
+
described.code = kept;
|
|
97
|
+
}
|
|
98
|
+
if (isObject(meta)) {
|
|
99
|
+
described.meta = metaOf(meta);
|
|
100
|
+
}
|
|
101
|
+
if (typeof clientVersion === "string") {
|
|
102
|
+
described.clientVersion = clientVersion;
|
|
103
|
+
}
|
|
104
|
+
if (error.name === "PrismaClientValidationError" || error.name === "PrismaClientInitializationError") {
|
|
105
|
+
const lines = error.message.split("\n").filter((line) => line.trim() !== "");
|
|
106
|
+
const reason = lines.at(-1)?.trim();
|
|
107
|
+
if (reason !== void 0) {
|
|
108
|
+
described.reason = reason.slice(0, MAX_REASON);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const frames = error.stack?.split("\n").filter((line) => line.startsWith(" at "));
|
|
112
|
+
if (frames !== void 0) {
|
|
113
|
+
described.stack = frames.join("\n");
|
|
114
|
+
}
|
|
115
|
+
return described;
|
|
116
|
+
}
|
|
117
|
+
function errorText(error) {
|
|
118
|
+
if (!(error instanceof Error)) {
|
|
119
|
+
return String(error);
|
|
120
|
+
}
|
|
121
|
+
if (!isPrismaError(error)) {
|
|
122
|
+
return error.message;
|
|
123
|
+
}
|
|
124
|
+
const { code, reason } = prismaSummary(error);
|
|
125
|
+
const coded = typeof code === "string" ? `${error.name} ${code}` : error.name;
|
|
126
|
+
return typeof reason === "string" ? `${coded}: ${reason}` : coded;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export {
|
|
130
|
+
createConsoleLogger,
|
|
131
|
+
consoleLogger,
|
|
132
|
+
isPrismaError,
|
|
133
|
+
prismaSummary,
|
|
134
|
+
errorText
|
|
135
|
+
};
|
|
136
|
+
//# sourceMappingURL=chunk-DKE6QO26.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/contract/logger.ts"],"sourcesContent":["// The logger contract and its console default (4.1 `src/shared/types.ts:525-545`),\n// and how an error is written into a log (the 5.1 audit's CLIENT-5). The\n// default writes every level outside production, and from `info` up when\n// `NODE_ENV` is \"production\" (a call's record logs at debug with its caller's\n// user id); `createConsoleLogger({ level })` names the lowest level written\n// instead. A Prisma client error's message renders the call it failed,\n// values included, and its `meta` can hold the database's own text, which\n// quotes the row, so a log gets its summary (`prismaSummary`, `errorText`),\n// never its message or that text. Isomorphic: `process` may not exist, and NODE_ENV is\n// read at each call.\n\n/**\n * Logger interface compatible with Winston and other loggers\n */\nexport interface Logger {\n info(message: string, meta?: Record<string, unknown>): void;\n warn(message: string, meta?: Record<string, unknown>): void;\n error(message: string, meta?: Record<string, unknown>): void;\n debug(message: string, meta?: Record<string, unknown>): void;\n child(options: Record<string, unknown>): Logger;\n}\n\n/** A logger's levels, lowest first. */\nexport type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nconst LEVELS: readonly string[] = [\"debug\", \"info\", \"warn\", \"error\"];\n\n/** Options of {@link createConsoleLogger}. */\nexport interface ConsoleLoggerOptions {\n /**\n * The lowest level written. Default `\"info\"` when `NODE_ENV` is\n * `\"production\"` (read at each call), else `\"debug\"`.\n */\n readonly level?: LogLevel;\n}\n\n/** True when `process.env.NODE_ENV` is \"production\", or cannot be read. */\nfunction inProduction(): boolean {\n try {\n // Written out whole, so a bundler can inline it.\n return process.env.NODE_ENV === \"production\";\n } catch {\n // No `process` here (a bundler that provides none): treated as production.\n return true;\n }\n}\n\n/**\n * A logger that writes to the console from `level` up: by default every\n * level, but `debug` when `NODE_ENV` is \"production\". `child` returns the\n * same logger.\n */\nexport function createConsoleLogger(options: ConsoleLoggerOptions = {}): Logger {\n const { level } = options;\n if (level !== undefined && !LEVELS.includes(level)) {\n throw new TypeError(\n `createConsoleLogger: level must be \"debug\", \"info\", \"warn\" or \"error\", not ${JSON.stringify(level)}`,\n );\n }\n const writes = (at: LogLevel): boolean =>\n LEVELS.indexOf(at) >= LEVELS.indexOf(level ?? (inProduction() ? \"info\" : \"debug\"));\n const logger: Logger = {\n info: (message, meta) => {\n if (writes(\"info\")) {\n console.log(`[INFO] ${message}`, meta ?? \"\");\n }\n },\n warn: (message, meta) => {\n if (writes(\"warn\")) {\n console.warn(`[WARN] ${message}`, meta ?? \"\");\n }\n },\n error: (message, meta) => {\n if (writes(\"error\")) {\n console.error(`[ERROR] ${message}`, meta ?? \"\");\n }\n },\n debug: (message, meta) => {\n if (writes(\"debug\")) {\n console.debug(`[DEBUG] ${message}`, meta ?? \"\");\n }\n },\n child: () => logger,\n };\n return logger;\n}\n\n/** The default console logger: every level, but `debug` in production. */\nexport const consoleLogger: Logger = createConsoleLogger();\n\n/** True for an error of the Prisma client, whose message renders the call it failed, values included. */\nexport function isPrismaError(error: Error): boolean {\n return /^PrismaClient\\w*Error$/u.test(error.name);\n}\n\n/**\n * The keys of a Prisma error's `meta` a log keeps: each names the schema (a\n * model, field, constraint, relation or operation) or a code, never a value.\n * Every other key is dropped: `argument_value`, `field_value`, `details`,\n * `database_error` and `message` quote the row or the database's own text.\n */\nconst META_KEYS: ReadonlySet<string> = new Set([\n \"modelName\",\n \"model_name\",\n \"operation\",\n \"target\",\n \"field_name\",\n \"column_name\",\n \"argument_name\",\n \"relation_name\",\n \"model_a_name\",\n \"model_b_name\",\n \"object_name\",\n \"constraint\",\n \"table\",\n \"column\",\n \"path\",\n \"code\",\n]);\n\n/**\n * The keys of a driver adapter error's cause a log keeps (Prisma 7 puts it\n * in every database error's `meta.driverAdapterError`): its kind, its codes\n * and the schema it names. Its `originalMessage`, `message`, `detail` and\n * `cause` are the database's own text, which quotes values (\"Failing row\n * contains (...)\").\n */\nconst ADAPTER_CAUSE_KEYS: ReadonlySet<string> = new Set([\n \"kind\",\n \"originalCode\",\n \"code\",\n \"constraint\",\n \"column\",\n \"table\",\n]);\n\nfunction isObject(value: unknown): value is Readonly<Record<string, unknown>> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** The entries of `value` whose keys `keys` holds. */\nfunction picked(\n value: Readonly<Record<string, unknown>>,\n keys: ReadonlySet<string>,\n): Record<string, unknown> {\n return Object.fromEntries(Object.entries(value).filter(([key]) => keys.has(key)));\n}\n\n/** A Prisma error's `meta` as a log keeps it: the schema it names and its codes, never a value. */\nfunction metaOf(meta: Readonly<Record<string, unknown>>): Record<string, unknown> {\n const kept = picked(meta, META_KEYS);\n const adapter = meta.driverAdapterError;\n if (isObject(adapter)) {\n kept.driverAdapterError = {\n ...(typeof adapter.name === \"string\" ? { name: adapter.name } : {}),\n ...(isObject(adapter.cause) ? { cause: picked(adapter.cause, ADAPTER_CAUSE_KEYS) } : {}),\n };\n }\n return kept;\n}\n\n/** The longest `reason` kept of a Prisma error's message. */\nconst MAX_REASON = 300;\n\n/**\n * A Prisma error, summarized (the 5.1 audit's CLIENT-5): its class, code,\n * the schema names and codes of its `meta` (`metaOf`), client version and\n * stack frames;\n * for a validation or initialization error, its message's last line, which\n * names arguments and never their values. Never the message: it renders the\n * call, values included, and the stack's first lines repeat it.\n */\nexport function prismaSummary(error: Error): Record<string, unknown> {\n const { code, errorCode, meta, clientVersion } = error as Error &\n Readonly<Record<\"code\" | \"errorCode\" | \"meta\" | \"clientVersion\", unknown>>;\n const described: Record<string, unknown> = { name: error.name };\n const kept = typeof code === \"string\" ? code : errorCode;\n if (typeof kept === \"string\") {\n described.code = kept;\n }\n if (isObject(meta)) {\n described.meta = metaOf(meta);\n }\n if (typeof clientVersion === \"string\") {\n described.clientVersion = clientVersion;\n }\n if (\n error.name === \"PrismaClientValidationError\" ||\n error.name === \"PrismaClientInitializationError\"\n ) {\n const lines = error.message.split(\"\\n\").filter((line) => line.trim() !== \"\");\n const reason = lines.at(-1)?.trim();\n if (reason !== undefined) {\n described.reason = reason.slice(0, MAX_REASON);\n }\n }\n const frames = error.stack?.split(\"\\n\").filter((line) => line.startsWith(\" at \"));\n if (frames !== undefined) {\n described.stack = frames.join(\"\\n\");\n }\n return described;\n}\n\n/**\n * One line for an error in a log message or a reply's text: a Prisma error\n * summarized as `\"<class> <code>: <reason>\"` (the parts it has; never the\n * call its message renders), another error's message, anything else as a\n * string.\n */\nexport function errorText(error: unknown): string {\n if (!(error instanceof Error)) {\n return String(error);\n }\n if (!isPrismaError(error)) {\n return error.message;\n }\n const { code, reason } = prismaSummary(error);\n const coded = typeof code === \"string\" ? `${error.name} ${code}` : error.name;\n return typeof reason === \"string\" ? `${coded}: ${reason}` : coded;\n}\n"],"mappings":"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","names":[]}
|