@fitzzero/quickdraw-core 5.1.0 → 5.1.2

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.
Files changed (107) hide show
  1. package/README.md +533 -180
  2. package/dist/{admin-DKwkIh5i.d.ts → admin-DWWrT1h6.d.ts} +2 -2
  3. package/dist/{adminSchemas-2DUA_hZ8.d.ts → adminSchemas-D4bFKsrb.d.ts} +6 -4
  4. package/dist/{auth-BhWqdyb-.d.ts → auth-DXiSJZaf.d.ts} +42 -16
  5. package/dist/{caller-CXTFswBO.d.ts → caller-BT-Mv4bD.d.ts} +117 -22
  6. package/dist/{chunk-RT6OILOI.js → chunk-4D2DOGAW.js} +2 -14
  7. package/dist/chunk-4D2DOGAW.js.map +1 -0
  8. package/dist/chunk-5CGO426C.js +15 -0
  9. package/dist/chunk-5CGO426C.js.map +1 -0
  10. package/dist/{chunk-A6DJBGOG.js → chunk-6K5BY47X.js} +4594 -3481
  11. package/dist/chunk-6K5BY47X.js.map +1 -0
  12. package/dist/{chunk-VJTFXHOK.js → chunk-753VJIUP.js} +13 -15
  13. package/dist/chunk-753VJIUP.js.map +1 -0
  14. package/dist/{chunk-JZBCLNDU.js → chunk-7QCUX4TK.js} +4 -4
  15. package/dist/chunk-7QCUX4TK.js.map +1 -0
  16. package/dist/chunk-AK23XSWD.js +16 -0
  17. package/dist/chunk-AK23XSWD.js.map +1 -0
  18. package/dist/{chunk-3674EIRT.js → chunk-AONAFOYU.js} +14 -5
  19. package/dist/{chunk-3674EIRT.js.map → chunk-AONAFOYU.js.map} +1 -1
  20. package/dist/chunk-DKE6QO26.js +136 -0
  21. package/dist/chunk-DKE6QO26.js.map +1 -0
  22. package/dist/{chunk-H23KEFBA.js → chunk-DUBU3BB3.js} +30 -1
  23. package/dist/chunk-DUBU3BB3.js.map +1 -0
  24. package/dist/{chunk-CDKG4TFA.js → chunk-GKH3LJ7N.js} +1 -1
  25. package/dist/chunk-GKH3LJ7N.js.map +1 -0
  26. package/dist/{chunk-KNDDCVQJ.js → chunk-GSIPUNJL.js} +49 -9
  27. package/dist/chunk-GSIPUNJL.js.map +1 -0
  28. package/dist/{chunk-DD4SKCNO.js → chunk-LCC7HNFP.js} +12 -1
  29. package/dist/chunk-LCC7HNFP.js.map +1 -0
  30. package/dist/{chunk-2PWMUVC7.js → chunk-MBH6IDSM.js} +480 -12
  31. package/dist/chunk-MBH6IDSM.js.map +1 -0
  32. package/dist/{chunk-2UELNHDD.js → chunk-NMXYENH4.js} +10 -10
  33. package/dist/{chunk-2UELNHDD.js.map → chunk-NMXYENH4.js.map} +1 -1
  34. package/dist/chunk-NO4M6PO5.js +101 -0
  35. package/dist/chunk-NO4M6PO5.js.map +1 -0
  36. package/dist/{chunk-TBMKJ26R.js → chunk-NW6U45HX.js} +3 -1
  37. package/dist/chunk-NW6U45HX.js.map +1 -0
  38. package/dist/{chunk-REX5YPHX.js → chunk-RJ5I2VO3.js} +74 -19
  39. package/dist/chunk-RJ5I2VO3.js.map +1 -0
  40. package/dist/{chunk-QEKVYG5F.js → chunk-RL4VKLBX.js} +1 -1
  41. package/dist/{chunk-QEKVYG5F.js.map → chunk-RL4VKLBX.js.map} +1 -1
  42. package/dist/chunk-XH2IZVLT.js +62 -0
  43. package/dist/chunk-XH2IZVLT.js.map +1 -0
  44. package/dist/{chunk-4DIQHASK.js → chunk-Z5ILULF2.js} +26 -7
  45. package/dist/chunk-Z5ILULF2.js.map +1 -0
  46. package/dist/cli/quickdraw-docs.js +5 -3
  47. package/dist/cli/quickdraw-docs.js.map +1 -1
  48. package/dist/client/index.d.ts +30 -18
  49. package/dist/client/index.js +8 -6
  50. package/dist/client/index.js.map +1 -1
  51. package/dist/{clientTypes-Bc-G8n-U.d.ts → clientTypes-hWcPUVS9.d.ts} +3 -3
  52. package/dist/{connection-Cr1BU07W.d.ts → connection-CgijdGpT.d.ts} +8 -4
  53. package/dist/{createServer-CP9eDHZZ.d.ts → createServer-Cp-Wk500.d.ts} +36 -14
  54. package/dist/{createTestApp-UKVGEmOf.d.ts → createTestApp-BB_PkqQ7.d.ts} +16 -10
  55. package/dist/{envelope-Cp3YcVxC.d.ts → envelope-DzWyIXjj.d.ts} +18 -4
  56. package/dist/{http-Djj2Inuu.d.ts → http-DotQ5bj8.d.ts} +68 -17
  57. package/dist/index.d.ts +7 -7
  58. package/dist/index.js +37 -13
  59. package/dist/index.js.map +1 -1
  60. package/dist/{infer-Teo1xpsd.d.ts → infer-BmoZsL9a.d.ts} +43 -10
  61. package/dist/logger-Yz93KzdV.d.ts +30 -0
  62. package/dist/{metrics-BdBu5o8G.d.ts → metrics-DW80Exve.d.ts} +2 -2
  63. package/dist/origins-kvpxnVcW.d.ts +8 -0
  64. package/dist/prisma/index.d.ts +5 -5
  65. package/dist/prisma/index.js +130 -67
  66. package/dist/prisma/index.js.map +1 -1
  67. package/dist/{rateLimit-ZMIZJUZF.js → rateLimit-5RKBFLUR.js} +2 -2
  68. package/dist/server/auth/index.d.ts +82 -33
  69. package/dist/server/auth/index.js +273 -268
  70. package/dist/server/auth/index.js.map +1 -1
  71. package/dist/server/express/index.d.ts +9 -2
  72. package/dist/server/express/index.js +1 -1
  73. package/dist/server/index.d.ts +147 -67
  74. package/dist/server/index.js +520 -381
  75. package/dist/server/index.js.map +1 -1
  76. package/dist/server/mcp/index.d.ts +63 -17
  77. package/dist/server/mcp/index.js +256 -54
  78. package/dist/server/mcp/index.js.map +1 -1
  79. package/dist/server/otel.d.ts +3 -3
  80. package/dist/{sharing-DVa8W-yR.d.ts → sharing-UDLn1049.d.ts} +3 -3
  81. package/dist/testing/client.d.ts +14 -14
  82. package/dist/testing/client.js +9 -7
  83. package/dist/testing/client.js.map +1 -1
  84. package/dist/testing/index.d.ts +12 -12
  85. package/dist/testing/index.js +21 -12
  86. package/dist/testing/index.js.map +1 -1
  87. package/dist/testing/mock.d.ts +5 -5
  88. package/dist/testing/mock.js +8 -6
  89. package/dist/{types-Cu0vmx7c.d.ts → types-CYliTP65.d.ts} +1 -1
  90. package/dist/utils/index.d.ts +1 -1
  91. package/package.json +1 -1
  92. package/dist/chunk-2PWMUVC7.js.map +0 -1
  93. package/dist/chunk-4DIQHASK.js.map +0 -1
  94. package/dist/chunk-A6DJBGOG.js.map +0 -1
  95. package/dist/chunk-CDKG4TFA.js.map +0 -1
  96. package/dist/chunk-DD4SKCNO.js.map +0 -1
  97. package/dist/chunk-H23KEFBA.js.map +0 -1
  98. package/dist/chunk-JZBCLNDU.js.map +0 -1
  99. package/dist/chunk-KNDDCVQJ.js.map +0 -1
  100. package/dist/chunk-REX5YPHX.js.map +0 -1
  101. package/dist/chunk-RT6OILOI.js.map +0 -1
  102. package/dist/chunk-TBMKJ26R.js.map +0 -1
  103. package/dist/chunk-VJTFXHOK.js.map +0 -1
  104. package/dist/chunk-YSWH776Y.js +0 -13
  105. package/dist/chunk-YSWH776Y.js.map +0 -1
  106. package/dist/logger-BY28acW-.d.ts +0 -16
  107. /package/dist/{rateLimit-ZMIZJUZF.js.map → rateLimit-5RKBFLUR.js.map} +0 -0
@@ -1,18 +1,20 @@
1
1
  import {
2
2
  createMockClient
3
- } from "../chunk-3674EIRT.js";
3
+ } from "../chunk-AONAFOYU.js";
4
4
  import {
5
5
  QuickdrawProvider
6
- } from "../chunk-JZBCLNDU.js";
6
+ } from "../chunk-7QCUX4TK.js";
7
7
  import {
8
8
  QuickdrawContext
9
- } from "../chunk-KNDDCVQJ.js";
10
- import "../chunk-CDKG4TFA.js";
11
- import "../chunk-VJTFXHOK.js";
12
- import "../chunk-RT6OILOI.js";
9
+ } from "../chunk-GSIPUNJL.js";
10
+ import "../chunk-GKH3LJ7N.js";
11
+ import "../chunk-753VJIUP.js";
12
+ import "../chunk-5CGO426C.js";
13
+ import "../chunk-4D2DOGAW.js";
14
+ import "../chunk-AK23XSWD.js";
13
15
  import "../chunk-47AEXVDM.js";
14
16
  import "../chunk-Z2LZ7IZ7.js";
15
- import "../chunk-YSWH776Y.js";
17
+ import "../chunk-DKE6QO26.js";
16
18
  import "../chunk-X5CWPX7H.js";
17
19
  import "../chunk-4YO5H5ON.js";
18
20
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/testing/client.tsx","../../src/testing/jsdom.ts"],"sourcesContent":["// Client test helpers: @fitzzero/quickdraw-core/testing/client (RFC 0003\n// section 13). Components that use the typed client are tested against a\n// real in-process server, or against a mock client with no server at all.\n//\n// 4.1's `./client/testing` could not work as documented: `createTestWrapper`\n// filled a context of its own (4.1 `src/client/testing.tsx:185-192`)\n// while the hooks read the provider's unexported one\n// (4.1 `src/client/QuickdrawProvider.tsx:169`), and every hook test\n// mocked the provider, so no test ran a hook against a server. Here:\n//\n// - `renderWithQuickdraw(ui, { app, as, client })` renders `ui` under the\n// real `QuickdrawProvider`, connected over a real socket to a test app\n// (`createTestApp`, which boots the app's services on `createServer`) as\n// the principal `as`. The hooks run as they do in the app: the same\n// connection, calls, change topics, coordinator and live data.\n// - `createMockClient(contracts)` (`mockClient.ts`) is a client of the same\n// type whose members are stubs, for component tests that do not care\n// about the transport. It is its own entry too, `./testing/mock`, which\n// a browser bundle (Storybook) can import: this one names Testing Library.\n//\n// The published package does not depend on Testing Library: it is an\n// optional peer, imported only when `renderWithQuickdraw` runs, so a test\n// that uses `createMockClient` alone loads neither Testing Library nor any\n// server code. No \"use client\" directive: this entry is for tests, never\n// for a bundle that React Server Components split.\n\nimport { QueryClient } from \"@tanstack/react-query\";\nimport type { RenderResult } from \"@testing-library/react\";\nimport * as React from \"react\";\nimport type { QuickdrawClient } from \"../client/clientTypes\";\nimport type { QuickdrawConnection } from \"../client/connection\";\nimport { QuickdrawContext } from \"../client/context\";\nimport { QuickdrawProvider } from \"../client/provider\";\nimport type { ContractMap } from \"../contract/infer\";\nimport type { PrincipalOfServices } from \"../server/dispatcher\";\nimport type { AnyService } from \"../server/service\";\nimport type { TestApp } from \"./createTestApp\";\n\nexport * from \"./mock\";\nexport { installJsdomShims } from \"./jsdom\";\n\n/** Options of {@link renderWithQuickdraw}. */\nexport interface RenderWithQuickdrawOptions<\n S extends readonly AnyService[],\n Contracts extends ContractMap,\n> {\n /** The test app to connect to, from `createTestApp`. */\n readonly app: TestApp<S>;\n /**\n * Who the socket acts for, or `null` for an anonymous socket. It is sent\n * as the handshake's `principal`, which a test app trusts unless it was\n * given an `authenticate` of its own.\n */\n readonly as: PrincipalOfServices<S> | null;\n /** The client the components use, from `createQuickdrawClient`. */\n readonly client: QuickdrawClient<Contracts>;\n /**\n * The cache the hooks use. Default: a fresh one with the provider's\n * defaults (results fresh for 5 minutes, no refetch on window focus) and\n * retries off.\n */\n readonly queryClient?: QueryClient;\n /** Components to render around the provider: the app's own providers, or `React.StrictMode`. */\n readonly wrapper?: React.JSXElementConstructor<{ readonly children: React.ReactNode }>;\n}\n\n/** What {@link renderWithQuickdraw} resolves with: Testing Library's render result, and more. */\nexport interface QuickdrawRenderResult extends RenderResult {\n /** The provider's connection. */\n readonly connection: QuickdrawConnection;\n /** The provider's cache. */\n readonly queryClient: QueryClient;\n /**\n * Drops the socket the way a lost network does, and holds Socket.IO's\n * automatic reconnection until `reconnect()`. The connection is\n * `reconnecting` meanwhile and keeps everything it holds; the server\n * forgets the socket and its rooms. Resolves once the server has dropped it.\n */\n disconnect(): Promise<void>;\n /**\n * Lets a socket dropped by `disconnect()` connect again: as after a real\n * outage, the connection joins its topics again, refetches what may have\n * changed and resumes its live data by revision. Resolves once it is\n * connected and the server's new `qd:hello` has arrived.\n */\n reconnect(): Promise<void>;\n}\n\nconst TIMEOUT_MS = 5000;\n\n/** Testing Library, once `renderWithQuickdraw` has loaded it. */\nlet testingLibrary: typeof import(\"@testing-library/react\") | undefined;\n\n/** True when `RTL_SKIP_AUTO_CLEANUP` turns Testing Library's automatic cleanup off. */\nfunction skipsAutoCleanup(): boolean {\n return typeof process !== \"undefined\" && Boolean(process.env.RTL_SKIP_AUTO_CLEANUP);\n}\n\n// Testing Library unmounts what it rendered after each test when the test\n// runner has a global `afterEach`, but it registers that cleanup when it is\n// first imported. Imported lazily here, that can be inside a test, where it\n// is too late to register; so this module registers the same cleanup when\n// it loads, as Testing Library does, and honors `RTL_SKIP_AUTO_CLEANUP`.\n// Running it twice (Testing Library's own and this one) is harmless.\nconst runnerAfterEach: unknown = (globalThis as { readonly afterEach?: unknown }).afterEach;\nif (typeof runnerAfterEach === \"function\" && !skipsAutoCleanup()) {\n (runnerAfterEach as (cleanup: () => void) => void)(() => {\n testingLibrary?.cleanup();\n });\n}\n\n/**\n * The cache `renderWithQuickdraw` makes: the provider's defaults, with\n * retries off. A quickdraw query hook keeps its own retry policy (one more\n * attempt after `INTERNAL`) unless it is given `retry`; here that attempt\n * follows at once.\n */\nfunction createTestQueryClient(): QueryClient {\n return new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 5 * 60 * 1000,\n refetchOnWindowFocus: false,\n retry: false,\n retryDelay: 0,\n },\n mutations: { retry: false },\n },\n });\n}\n\n/**\n * Resolves once `ready()` holds, checking now and on every `subscribe`\n * notification; rejects after `TIMEOUT_MS` naming what it waited for.\n */\nfunction waitUntil(\n ready: () => boolean,\n subscribe: (notify: () => void) => () => void,\n what: string,\n): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n let stop = (): void => undefined;\n const timer = setTimeout(() => {\n stop();\n reject(new Error(`renderWithQuickdraw: ${what} within ${TIMEOUT_MS} ms`));\n }, TIMEOUT_MS);\n const check = (): void => {\n if (ready()) {\n clearTimeout(timer);\n stop();\n resolve();\n }\n };\n stop = subscribe(check);\n check();\n });\n}\n\n/** The provider's connection, kept by the first render of a component under it. */\ninterface Grabbed {\n connection: QuickdrawConnection | null;\n}\n\n/** Testing Library's `act`: what React does meanwhile is applied before it resolves. */\ntype Act = (callback: () => Promise<void>) => Promise<void>;\n\n/**\n * `disconnect()` and `reconnect()` of one rendered connection. Each runs in\n * `act`, so the re-renders the connection's state changes cause are applied\n * within it, as React expects in a test.\n */\nfunction outages<S extends readonly AnyService[]>(\n app: TestApp<S>,\n connection: QuickdrawConnection,\n act: Act,\n): Pick<QuickdrawRenderResult, \"disconnect\" | \"reconnect\"> {\n const manager = connection.socket.io;\n let reconnection = manager.reconnection();\n const drop = async (id: string): Promise<void> => {\n const dropped = app.server.io.sockets.sockets.get(id);\n reconnection = manager.reconnection();\n manager.reconnection(false);\n manager.engine.close();\n await waitUntil(\n () => !app.server.io.sockets.sockets.has(id),\n (notify) => {\n dropped?.once(\"disconnect\", notify);\n return () => dropped?.off(\"disconnect\", notify);\n },\n \"the server did not drop the socket\",\n );\n };\n const bringBack = async (): Promise<void> => {\n const before = connection.getState().hello;\n manager.reconnection(reconnection);\n connection.socket.connect();\n await waitUntil(\n () => {\n const state = connection.getState();\n return state.status === \"connected\" && state.hello !== null && state.hello !== before;\n },\n connection.subscribe,\n \"the socket did not connect again\",\n );\n };\n return {\n async disconnect() {\n const { id } = connection.socket;\n if (!connection.socket.connected || id === undefined) {\n throw new Error(\"renderWithQuickdraw: disconnect() needs a connected socket\");\n }\n await act(async () => {\n await drop(id);\n });\n },\n async reconnect() {\n if (connection.socket.connected) {\n throw new Error(\"renderWithQuickdraw: reconnect() needs a socket dropped by disconnect()\");\n }\n await act(bringBack);\n },\n };\n}\n\n/**\n * Renders `ui` with Testing Library under a `QuickdrawProvider` for\n * `client`, connected to the test app `app` over a real socket as `as`, with\n * a fresh `QueryClient` (retries off) unless one is given. Resolves with\n * Testing Library's render result plus the provider's `connection` and\n * `queryClient`, and `disconnect()` / `reconnect()` to drop the socket and\n * bring it back. Testing Library unmounts it after each test, as it does\n * everything `render` renders; an unmounted provider closes its connection.\n *\n * Needs a DOM (jsdom or happy-dom) and `@testing-library/react`.\n *\n * @example\n * const app = await createTestApp({ services: [taskService], db });\n * const view = await renderWithQuickdraw(<TaskTitle id={id} />, { app, as: ada, client: qd });\n * await view.findByText(\"Write the spec\");\n * await app.as(bo).taskService.rename({ id, title: \"Ship it\" });\n * await view.findByText(\"Ship it\");\n */\nexport async function renderWithQuickdraw<\n const S extends readonly AnyService[],\n const Contracts extends ContractMap,\n>(\n ui: React.ReactElement,\n options: RenderWithQuickdrawOptions<S, Contracts>,\n): Promise<QuickdrawRenderResult> {\n testingLibrary ??= await import(\"@testing-library/react\");\n const { render, act } = testingLibrary;\n const { app, client } = options;\n const queryClient = options.queryClient ?? createTestQueryClient();\n const auth = options.as === null ? undefined : { principal: options.as };\n const Outer = options.wrapper ?? React.Fragment;\n const grabbed: Grabbed = { connection: null };\n // Reads the context, not `useQuickdraw()`, so it never re-renders when the\n // connection's state changes.\n function Grab(): null {\n const provided = React.useContext(QuickdrawContext);\n grabbed.connection ??= provided?.connection ?? null;\n return null;\n }\n function Wrapper({ children }: { readonly children?: React.ReactNode }): React.ReactElement {\n return (\n <Outer>\n <QuickdrawProvider\n client={client}\n url={app.url}\n auth={auth}\n transports={[\"websocket\"]}\n queryClient={queryClient}\n >\n <Grab />\n {children}\n </QuickdrawProvider>\n </Outer>\n );\n }\n const view = render(ui, { wrapper: Wrapper });\n const { connection } = grabbed;\n if (connection === null) {\n view.unmount();\n throw new Error(\"renderWithQuickdraw: the provider did not render\");\n }\n return { ...view, connection, queryClient, ...outages(app, connection, act) };\n}\n","// What jsdom leaves out that component tests against a real server need\n// (finding F3.11): an app's first run of `renderWithQuickdraw` under jsdom\n// failed on both until it patched them itself.\n//\n// - Scrolling: jsdom lays nothing out, and has no `Element.prototype.scrollTo`,\n// `scrollBy` or `scrollIntoView`, which a list that follows its newest\n// item calls. Here they do nothing.\n// - `Blob.prototype.arrayBuffer`: jsdom's `Blob` has none, and PGlite reads a\n// database dump through it, so a test that boots the API's test database\n// in the same process fails. Here it reads the blob with jsdom's\n// `FileReader`. (`openPgliteFromTemplate` from `./testing/prisma` uses\n// Node's own `Blob` and needs no patch.)\n//\n// Only what is missing is added, so a later jsdom that has them keeps its\n// own. No Node built-in: `./testing/client` is imported by browser bundles'\n// test runners too.\n\ntype Scrolling = \"scrollTo\" | \"scrollBy\" | \"scrollIntoView\";\n\nconst SCROLLING: readonly Scrolling[] = [\"scrollTo\", \"scrollBy\", \"scrollIntoView\"];\n\n/** The blob's bytes, read with the `FileReader` jsdom has. */\nfunction readAsArrayBuffer(blob: Blob): Promise<ArrayBuffer> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.addEventListener(\"load\", () => {\n resolve(reader.result as ArrayBuffer);\n });\n reader.addEventListener(\"error\", () => {\n reject(reader.error ?? new Error(\"The blob could not be read\"));\n });\n reader.readAsArrayBuffer(blob);\n });\n}\n\n/**\n * Adds what jsdom lacks for component tests against a real server: no-op\n * `scrollTo`, `scrollBy` and `scrollIntoView` on elements, and\n * `Blob.prototype.arrayBuffer`. Call it from a `setupFiles` module of the\n * jsdom project; each one already there is kept. Does nothing without a DOM.\n *\n * @example\n * // vitest.setup.ts, listed in the jsdom project's setupFiles\n * import { installJsdomShims } from \"@fitzzero/quickdraw-core/testing/client\";\n * installJsdomShims();\n */\nexport function installJsdomShims(): void {\n const element: unknown = (globalThis as { readonly Element?: unknown }).Element;\n if (typeof element === \"function\") {\n const prototype = (element as { readonly prototype: Partial<Record<Scrolling, unknown>> })\n .prototype;\n for (const name of SCROLLING) {\n if (typeof prototype[name] !== \"function\") {\n Object.defineProperty(prototype, name, {\n value: (): void => undefined,\n configurable: true,\n writable: true,\n });\n }\n }\n }\n const blob: unknown = (globalThis as { readonly Blob?: unknown }).Blob;\n const fileReader: unknown = (globalThis as { readonly FileReader?: unknown }).FileReader;\n if (typeof blob === \"function\" && typeof fileReader === \"function\") {\n const prototype = (blob as { readonly prototype: { arrayBuffer?: unknown } }).prototype;\n if (typeof prototype.arrayBuffer !== \"function\") {\n Object.defineProperty(prototype, \"arrayBuffer\", {\n value(this: Blob): Promise<ArrayBuffer> {\n return readAsArrayBuffer(this);\n },\n configurable: true,\n writable: true,\n });\n }\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/testing/client.tsx","../../src/testing/jsdom.ts"],"sourcesContent":["// Client test helpers: @fitzzero/quickdraw-core/testing/client (RFC 0003\n// section 13). Components that use the typed client are tested against a\n// real in-process server, or against a mock client with no server at all.\n//\n// 4.1's `./client/testing` could not work as documented: `createTestWrapper`\n// filled a context of its own (4.1 `src/client/testing.tsx:185-192`)\n// while the hooks read the provider's unexported one\n// (4.1 `src/client/QuickdrawProvider.tsx:169`), and every hook test\n// mocked the provider, so no test ran a hook against a server. Here:\n//\n// - `renderWithQuickdraw(ui, { app, as, client })` renders `ui` under the\n// real `QuickdrawProvider`, connected over a real socket to a test app\n// (`createTestApp`, which boots the app's services on `createServer`) as\n// the principal `as`. The hooks run as they do in the app: the same\n// connection, calls, change topics, coordinator and live data.\n// - `createMockClient(contracts)` (`mockClient.ts`) is a client of the same\n// type whose members are stubs, for component tests that do not care\n// about the transport. It is its own entry too, `./testing/mock`, which\n// a browser bundle (Storybook) can import: this one names Testing Library.\n//\n// The published package does not depend on Testing Library: it is an\n// optional peer, imported only when `renderWithQuickdraw` runs, so a test\n// that uses `createMockClient` alone loads neither Testing Library nor any\n// server code. No \"use client\" directive: this entry is for tests, never\n// for a bundle that React Server Components split.\n\nimport { QueryClient } from \"@tanstack/react-query\";\nimport type { RenderResult } from \"@testing-library/react\";\nimport * as React from \"react\";\nimport type { QuickdrawClient } from \"../client/clientTypes\";\nimport type { QuickdrawConnection } from \"../client/connection\";\nimport { QuickdrawContext } from \"../client/context\";\nimport { QuickdrawProvider } from \"../client/provider\";\nimport type { ContractMap } from \"../contract/infer\";\nimport type { PrincipalOfServices } from \"../server/dispatcher\";\nimport type { AnyService } from \"../server/service\";\nimport type { TestApp } from \"./createTestApp\";\n\nexport * from \"./mock\";\nexport { installJsdomShims } from \"./jsdom\";\n\n/** Options of {@link renderWithQuickdraw}. */\nexport interface RenderWithQuickdrawOptions<\n S extends readonly AnyService[],\n Contracts extends ContractMap,\n> {\n /** The test app to connect to, from `createTestApp`. */\n readonly app: TestApp<S>;\n /**\n * Who the socket acts for, or `null` for an anonymous socket. It is sent\n * as the handshake's `principal`, which a test app trusts unless it was\n * given an `authenticate` of its own.\n */\n readonly as: PrincipalOfServices<S> | null;\n /** The client the components use, from `createQuickdrawClient`. */\n readonly client: QuickdrawClient<Contracts>;\n /**\n * The cache the hooks use. Default: a fresh one with the provider's\n * defaults (results fresh for 5 minutes, no refetch on window focus) and\n * retries off.\n */\n readonly queryClient?: QueryClient;\n /** Components to render around the provider: the app's own providers, or `React.StrictMode`. */\n readonly wrapper?: React.JSXElementConstructor<{ readonly children: React.ReactNode }>;\n}\n\n/** What {@link renderWithQuickdraw} resolves with: Testing Library's render result, and more. */\nexport interface QuickdrawRenderResult extends RenderResult {\n /** The provider's connection. */\n readonly connection: QuickdrawConnection;\n /** The provider's cache. */\n readonly queryClient: QueryClient;\n /**\n * Drops the socket the way a lost network does, and holds Socket.IO's\n * automatic reconnection until `reconnect()`. The connection is\n * `reconnecting` meanwhile and keeps everything it holds; the server\n * forgets the socket and its rooms. Resolves once the server has dropped it.\n */\n disconnect(): Promise<void>;\n /**\n * Lets a socket dropped by `disconnect()` connect again: as after a real\n * outage, the connection joins its topics again, refetches what may have\n * changed and resumes its live data by revision. Resolves once it is\n * connected and the server's new `qd:hello` has arrived.\n */\n reconnect(): Promise<void>;\n}\n\nconst TIMEOUT_MS = 5000;\n\n/** Testing Library, once `renderWithQuickdraw` has loaded it. */\nlet testingLibrary: typeof import(\"@testing-library/react\") | undefined;\n\n/** True when `RTL_SKIP_AUTO_CLEANUP` turns Testing Library's automatic cleanup off. */\nfunction skipsAutoCleanup(): boolean {\n return typeof process !== \"undefined\" && Boolean(process.env.RTL_SKIP_AUTO_CLEANUP);\n}\n\n// Testing Library unmounts what it rendered after each test when the test\n// runner has a global `afterEach`, but it registers that cleanup when it is\n// first imported. Imported lazily here, that can be inside a test, where it\n// is too late to register; so this module registers the same cleanup when\n// it loads, as Testing Library does, and honors `RTL_SKIP_AUTO_CLEANUP`.\n// Running it twice (Testing Library's own and this one) is harmless.\nconst runnerAfterEach: unknown = (globalThis as { readonly afterEach?: unknown }).afterEach;\nif (typeof runnerAfterEach === \"function\" && !skipsAutoCleanup()) {\n (runnerAfterEach as (cleanup: () => void) => void)(() => {\n testingLibrary?.cleanup();\n });\n}\n\n/**\n * The cache `renderWithQuickdraw` makes: the provider's defaults, with\n * retries off. A quickdraw query hook keeps its own retry policy (one more\n * attempt after `INTERNAL`) unless it is given `retry`; here that attempt\n * follows at once.\n */\nfunction createTestQueryClient(): QueryClient {\n return new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 5 * 60 * 1000,\n refetchOnWindowFocus: false,\n retry: false,\n retryDelay: 0,\n },\n mutations: { retry: false },\n },\n });\n}\n\n/**\n * Resolves once `ready()` holds, checking now and on every `subscribe`\n * notification; rejects after `TIMEOUT_MS` naming what it waited for.\n */\nfunction waitUntil(\n ready: () => boolean,\n subscribe: (notify: () => void) => () => void,\n what: string,\n): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n let stop = (): void => undefined;\n const timer = setTimeout(() => {\n stop();\n reject(new Error(`renderWithQuickdraw: ${what} within ${TIMEOUT_MS} ms`));\n }, TIMEOUT_MS);\n const check = (): void => {\n if (ready()) {\n clearTimeout(timer);\n stop();\n resolve();\n }\n };\n stop = subscribe(check);\n check();\n });\n}\n\n/** The provider's connection, kept by the first render of a component under it. */\ninterface Grabbed {\n connection: QuickdrawConnection | null;\n}\n\n/** Testing Library's `act`: what React does meanwhile is applied before it resolves. */\ntype Act = (callback: () => Promise<void>) => Promise<void>;\n\n/**\n * `disconnect()` and `reconnect()` of one rendered connection. Each runs in\n * `act`, so the re-renders the connection's state changes cause are applied\n * within it, as React expects in a test.\n */\nfunction outages<S extends readonly AnyService[]>(\n app: TestApp<S>,\n connection: QuickdrawConnection,\n act: Act,\n): Pick<QuickdrawRenderResult, \"disconnect\" | \"reconnect\"> {\n const manager = connection.socket.io;\n let reconnection = manager.reconnection();\n const drop = async (id: string): Promise<void> => {\n const dropped = app.server.io.sockets.sockets.get(id);\n reconnection = manager.reconnection();\n manager.reconnection(false);\n manager.engine.close();\n await waitUntil(\n () => !app.server.io.sockets.sockets.has(id),\n (notify) => {\n dropped?.once(\"disconnect\", notify);\n return () => dropped?.off(\"disconnect\", notify);\n },\n \"the server did not drop the socket\",\n );\n };\n const bringBack = async (): Promise<void> => {\n const before = connection.getState().hello;\n manager.reconnection(reconnection);\n connection.socket.connect();\n await waitUntil(\n () => {\n const state = connection.getState();\n return state.status === \"connected\" && state.hello !== null && state.hello !== before;\n },\n connection.subscribe,\n \"the socket did not connect again\",\n );\n };\n return {\n async disconnect() {\n const { id } = connection.socket;\n if (!connection.socket.connected || id === undefined) {\n throw new Error(\"renderWithQuickdraw: disconnect() needs a connected socket\");\n }\n await act(async () => {\n await drop(id);\n });\n },\n async reconnect() {\n if (connection.socket.connected) {\n throw new Error(\"renderWithQuickdraw: reconnect() needs a socket dropped by disconnect()\");\n }\n await act(bringBack);\n },\n };\n}\n\n/**\n * Renders `ui` with Testing Library under a `QuickdrawProvider` for\n * `client`, connected to the test app `app` over a real socket as `as`, with\n * a fresh `QueryClient` (retries off) unless one is given. Resolves with\n * Testing Library's render result plus the provider's `connection` and\n * `queryClient`, and `disconnect()` / `reconnect()` to drop the socket and\n * bring it back. Testing Library unmounts it after each test, as it does\n * everything `render` renders; an unmounted provider closes its connection.\n *\n * Needs a DOM (jsdom or happy-dom) and `@testing-library/react`.\n *\n * @example\n * const app = await createTestApp({ services: [taskService], db });\n * const view = await renderWithQuickdraw(<TaskTitle id={id} />, { app, as: ada, client: qd });\n * await view.findByText(\"Write the spec\");\n * await app.as(bo).taskService.rename({ id, title: \"Ship it\" });\n * await view.findByText(\"Ship it\");\n */\nexport async function renderWithQuickdraw<\n const S extends readonly AnyService[],\n const Contracts extends ContractMap,\n>(\n ui: React.ReactElement,\n options: RenderWithQuickdrawOptions<S, Contracts>,\n): Promise<QuickdrawRenderResult> {\n testingLibrary ??= await import(\"@testing-library/react\");\n const { render, act } = testingLibrary;\n const { app, client } = options;\n const queryClient = options.queryClient ?? createTestQueryClient();\n const auth = options.as === null ? undefined : { principal: options.as };\n const Outer = options.wrapper ?? React.Fragment;\n const grabbed: Grabbed = { connection: null };\n // Reads the context, not `useQuickdraw()`, so it never re-renders when the\n // connection's state changes.\n function Grab(): null {\n const provided = React.useContext(QuickdrawContext);\n grabbed.connection ??= provided?.connection ?? null;\n return null;\n }\n function Wrapper({ children }: { readonly children?: React.ReactNode }): React.ReactElement {\n return (\n <Outer>\n <QuickdrawProvider\n client={client}\n url={app.url}\n auth={auth}\n transports={[\"websocket\"]}\n queryClient={queryClient}\n >\n <Grab />\n {children}\n </QuickdrawProvider>\n </Outer>\n );\n }\n const view = render(ui, { wrapper: Wrapper });\n const { connection } = grabbed;\n if (connection === null) {\n view.unmount();\n throw new Error(\"renderWithQuickdraw: the provider did not render\");\n }\n return { ...view, connection, queryClient, ...outages(app, connection, act) };\n}\n","// What jsdom leaves out that component tests against a real server need\n// (finding F3.11): an app's first run of `renderWithQuickdraw` under jsdom\n// failed on both until it patched them itself.\n//\n// - Scrolling: jsdom lays nothing out, and has no `Element.prototype.scrollTo`,\n// `scrollBy` or `scrollIntoView`, which a list that follows its newest\n// item calls. Here they do nothing.\n// - `Blob.prototype.arrayBuffer`: jsdom's `Blob` has none, and PGlite reads a\n// database dump through it, so a test that boots the API's test database\n// in the same process fails. Here it reads the blob with jsdom's\n// `FileReader`. (`openPgliteFromTemplate` from `./testing/prisma` uses\n// Node's own `Blob` and needs no patch.)\n//\n// Only what is missing is added, so a later jsdom that has them keeps its\n// own. No Node built-in: `./testing/client` is imported by browser bundles'\n// test runners too.\n\ntype Scrolling = \"scrollTo\" | \"scrollBy\" | \"scrollIntoView\";\n\nconst SCROLLING: readonly Scrolling[] = [\"scrollTo\", \"scrollBy\", \"scrollIntoView\"];\n\n/** The blob's bytes, read with the `FileReader` jsdom has. */\nfunction readAsArrayBuffer(blob: Blob): Promise<ArrayBuffer> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.addEventListener(\"load\", () => {\n resolve(reader.result as ArrayBuffer);\n });\n reader.addEventListener(\"error\", () => {\n reject(reader.error ?? new Error(\"The blob could not be read\"));\n });\n reader.readAsArrayBuffer(blob);\n });\n}\n\n/**\n * Adds what jsdom lacks for component tests against a real server: no-op\n * `scrollTo`, `scrollBy` and `scrollIntoView` on elements, and\n * `Blob.prototype.arrayBuffer`. Call it from a `setupFiles` module of the\n * jsdom project; each one already there is kept. Does nothing without a DOM.\n *\n * @example\n * // vitest.setup.ts, listed in the jsdom project's setupFiles\n * import { installJsdomShims } from \"@fitzzero/quickdraw-core/testing/client\";\n * installJsdomShims();\n */\nexport function installJsdomShims(): void {\n const element: unknown = (globalThis as { readonly Element?: unknown }).Element;\n if (typeof element === \"function\") {\n const prototype = (element as { readonly prototype: Partial<Record<Scrolling, unknown>> })\n .prototype;\n for (const name of SCROLLING) {\n if (typeof prototype[name] !== \"function\") {\n Object.defineProperty(prototype, name, {\n value: (): void => undefined,\n configurable: true,\n writable: true,\n });\n }\n }\n }\n const blob: unknown = (globalThis as { readonly Blob?: unknown }).Blob;\n const fileReader: unknown = (globalThis as { readonly FileReader?: unknown }).FileReader;\n if (typeof blob === \"function\" && typeof fileReader === \"function\") {\n const prototype = (blob as { readonly prototype: { arrayBuffer?: unknown } }).prototype;\n if (typeof prototype.arrayBuffer !== \"function\") {\n Object.defineProperty(prototype, \"arrayBuffer\", {\n value(this: Blob): Promise<ArrayBuffer> {\n return readAsArrayBuffer(this);\n },\n configurable: true,\n writable: true,\n });\n }\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,18 +1,18 @@
1
- import { T as TestApp } from '../createTestApp-UKVGEmOf.js';
2
- export { E as EventQuery, F as FrameMatch, d as FrameQuery, f as FrameRecorder, R as RecordedFrame, S as ServerFrameOf, a as TestAppOptions, b as TestConnection, c as createTestApp, g as emitWithAck, e as eventFrames, s as streamFrames, w as waitForEvent } from '../createTestApp-UKVGEmOf.js';
3
- import { e as FlushSink, W as WriteRecord, f as FlushInfo, A as AnyService, g as ServiceMethod, h as Service } from '../caller-CXTFswBO.js';
4
- export { i as DevWarning, D as DevWarningError, j as DevWarningKind } from '../caller-CXTFswBO.js';
5
- import { P as PrincipalOfServices } from '../http-Djj2Inuu.js';
1
+ import { T as TestApp } from '../createTestApp-BB_PkqQ7.js';
2
+ export { E as EventQuery, F as FrameMatch, d as FrameQuery, f as FrameRecorder, R as RecordedFrame, S as ServerFrameOf, a as TestAppOptions, b as TestConnection, c as createTestApp, g as emitWithAck, e as eventFrames, s as streamFrames, w as waitForEvent } from '../createTestApp-BB_PkqQ7.js';
3
+ import { e as FlushSink, W as WriteRecord, f as FlushInfo, A as AnyService, g as ServiceMethod, h as Service } from '../caller-BT-Mv4bD.js';
4
+ export { i as DevWarning, D as DevWarningError, j as DevWarningKind } from '../caller-BT-Mv4bD.js';
5
+ import { P as PrincipalOfServices } from '../http-DotQ5bj8.js';
6
6
  import { a as ErrorCode } from '../errors-S-bxEIBC.js';
7
- import { J as AnyContract, ap as MethodName, al as InputOf } from '../infer-Teo1xpsd.js';
8
- import { Q as QuickdrawTypes, P as Principal } from '../types-Cu0vmx7c.js';
9
- import '../envelope-Cp3YcVxC.js';
10
- import '../createServer-CP9eDHZZ.js';
7
+ import { J as AnyContract, ap as MethodName, al as InputOf } from '../infer-BmoZsL9a.js';
8
+ import { Q as QuickdrawTypes, P as Principal } from '../types-CYliTP65.js';
9
+ import '../envelope-DzWyIXjj.js';
10
+ import '../createServer-Cp-Wk500.js';
11
11
  import 'node:http';
12
- import '../auth-BhWqdyb-.js';
12
+ import '../auth-DXiSJZaf.js';
13
13
  import 'socket.io';
14
- import '../logger-BY28acW-.js';
15
- import '../metrics-BdBu5o8G.js';
14
+ import '../logger-Yz93KzdV.js';
15
+ import '../metrics-DW80Exve.js';
16
16
  import 'socket.io-client';
17
17
 
18
18
  /** One flush a recording sink received. */
@@ -1,33 +1,36 @@
1
1
  import {
2
2
  accessIds,
3
3
  createServer,
4
- inputKeys,
5
4
  isCustomAccess,
6
- runtimeOf
7
- } from "../chunk-A6DJBGOG.js";
5
+ startupChecksOf
6
+ } from "../chunk-6K5BY47X.js";
8
7
  import {
9
8
  DevWarningError,
10
9
  STRICT_WARNINGS,
11
10
  statementsIssued
12
- } from "../chunk-4DIQHASK.js";
13
- import "../chunk-QEKVYG5F.js";
11
+ } from "../chunk-Z5ILULF2.js";
12
+ import "../chunk-RL4VKLBX.js";
14
13
  import {
15
14
  createCaller,
16
- lazyMembers
17
- } from "../chunk-2PWMUVC7.js";
18
- import "../chunk-DD4SKCNO.js";
15
+ inputKeys,
16
+ lazyMembers,
17
+ runtimeOf
18
+ } from "../chunk-MBH6IDSM.js";
19
+ import "../chunk-LCC7HNFP.js";
19
20
  import {
20
21
  isPrincipal,
21
22
  toCallReply
22
- } from "../chunk-REX5YPHX.js";
23
+ } from "../chunk-RJ5I2VO3.js";
23
24
  import {
24
25
  storageOf
25
- } from "../chunk-H23KEFBA.js";
26
+ } from "../chunk-DUBU3BB3.js";
26
27
  import {
27
28
  PROTOCOL_VERSION,
28
29
  QUICKDRAW_VERSION,
29
30
  SERVER_EVENTS
30
- } from "../chunk-VJTFXHOK.js";
31
+ } from "../chunk-753VJIUP.js";
32
+ import "../chunk-4D2DOGAW.js";
33
+ import "../chunk-AK23XSWD.js";
31
34
  import {
32
35
  hasJsonSchema,
33
36
  validate
@@ -37,7 +40,7 @@ import {
37
40
  } from "../chunk-Z2LZ7IZ7.js";
38
41
  import {
39
42
  consoleLogger
40
- } from "../chunk-YSWH776Y.js";
43
+ } from "../chunk-DKE6QO26.js";
41
44
  import {
42
45
  QuickdrawError,
43
46
  fromWire
@@ -637,6 +640,12 @@ async function createTestApp(options) {
637
640
  ...options.auth
638
641
  }
639
642
  });
643
+ try {
644
+ await startupChecksOf(server);
645
+ } catch (error) {
646
+ await server.close().catch(() => void 0);
647
+ throw error;
648
+ }
640
649
  adoptDispatcher(options.services, server.dispatcher);
641
650
  const frames = recordFrames(server.io);
642
651
  const unmeter = meterServer({ io: server.io, db: options.db, storage: options.storage });