@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.
- package/README.md +533 -180
- package/dist/{admin-DKwkIh5i.d.ts → admin-DWWrT1h6.d.ts} +2 -2
- package/dist/{adminSchemas-2DUA_hZ8.d.ts → adminSchemas-D4bFKsrb.d.ts} +6 -4
- package/dist/{auth-BhWqdyb-.d.ts → auth-DXiSJZaf.d.ts} +42 -16
- package/dist/{caller-CXTFswBO.d.ts → caller-BT-Mv4bD.d.ts} +117 -22
- package/dist/{chunk-RT6OILOI.js → chunk-4D2DOGAW.js} +2 -14
- package/dist/chunk-4D2DOGAW.js.map +1 -0
- package/dist/chunk-5CGO426C.js +15 -0
- package/dist/chunk-5CGO426C.js.map +1 -0
- package/dist/{chunk-A6DJBGOG.js → chunk-6K5BY47X.js} +4594 -3481
- package/dist/chunk-6K5BY47X.js.map +1 -0
- package/dist/{chunk-VJTFXHOK.js → chunk-753VJIUP.js} +13 -15
- package/dist/chunk-753VJIUP.js.map +1 -0
- package/dist/{chunk-JZBCLNDU.js → chunk-7QCUX4TK.js} +4 -4
- package/dist/chunk-7QCUX4TK.js.map +1 -0
- package/dist/chunk-AK23XSWD.js +16 -0
- package/dist/chunk-AK23XSWD.js.map +1 -0
- package/dist/{chunk-3674EIRT.js → chunk-AONAFOYU.js} +14 -5
- package/dist/{chunk-3674EIRT.js.map → chunk-AONAFOYU.js.map} +1 -1
- package/dist/chunk-DKE6QO26.js +136 -0
- package/dist/chunk-DKE6QO26.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-CDKG4TFA.js → chunk-GKH3LJ7N.js} +1 -1
- package/dist/chunk-GKH3LJ7N.js.map +1 -0
- package/dist/{chunk-KNDDCVQJ.js → chunk-GSIPUNJL.js} +49 -9
- package/dist/chunk-GSIPUNJL.js.map +1 -0
- package/dist/{chunk-DD4SKCNO.js → chunk-LCC7HNFP.js} +12 -1
- package/dist/chunk-LCC7HNFP.js.map +1 -0
- package/dist/{chunk-2PWMUVC7.js → chunk-MBH6IDSM.js} +480 -12
- package/dist/chunk-MBH6IDSM.js.map +1 -0
- package/dist/{chunk-2UELNHDD.js → chunk-NMXYENH4.js} +10 -10
- package/dist/{chunk-2UELNHDD.js.map → chunk-NMXYENH4.js.map} +1 -1
- 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-REX5YPHX.js → chunk-RJ5I2VO3.js} +74 -19
- package/dist/chunk-RJ5I2VO3.js.map +1 -0
- package/dist/{chunk-QEKVYG5F.js → chunk-RL4VKLBX.js} +1 -1
- package/dist/{chunk-QEKVYG5F.js.map → chunk-RL4VKLBX.js.map} +1 -1
- package/dist/chunk-XH2IZVLT.js +62 -0
- package/dist/chunk-XH2IZVLT.js.map +1 -0
- package/dist/{chunk-4DIQHASK.js → chunk-Z5ILULF2.js} +26 -7
- package/dist/chunk-Z5ILULF2.js.map +1 -0
- package/dist/cli/quickdraw-docs.js +5 -3
- package/dist/cli/quickdraw-docs.js.map +1 -1
- package/dist/client/index.d.ts +30 -18
- package/dist/client/index.js +8 -6
- package/dist/client/index.js.map +1 -1
- package/dist/{clientTypes-Bc-G8n-U.d.ts → clientTypes-hWcPUVS9.d.ts} +3 -3
- package/dist/{connection-Cr1BU07W.d.ts → connection-CgijdGpT.d.ts} +8 -4
- package/dist/{createServer-CP9eDHZZ.d.ts → createServer-Cp-Wk500.d.ts} +36 -14
- package/dist/{createTestApp-UKVGEmOf.d.ts → createTestApp-BB_PkqQ7.d.ts} +16 -10
- package/dist/{envelope-Cp3YcVxC.d.ts → envelope-DzWyIXjj.d.ts} +18 -4
- package/dist/{http-Djj2Inuu.d.ts → http-DotQ5bj8.d.ts} +68 -17
- package/dist/index.d.ts +7 -7
- package/dist/index.js +37 -13
- package/dist/index.js.map +1 -1
- package/dist/{infer-Teo1xpsd.d.ts → infer-BmoZsL9a.d.ts} +43 -10
- package/dist/logger-Yz93KzdV.d.ts +30 -0
- package/dist/{metrics-BdBu5o8G.d.ts → metrics-DW80Exve.d.ts} +2 -2
- package/dist/origins-kvpxnVcW.d.ts +8 -0
- package/dist/prisma/index.d.ts +5 -5
- package/dist/prisma/index.js +130 -67
- package/dist/prisma/index.js.map +1 -1
- package/dist/{rateLimit-ZMIZJUZF.js → rateLimit-5RKBFLUR.js} +2 -2
- package/dist/server/auth/index.d.ts +82 -33
- package/dist/server/auth/index.js +273 -268
- 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 +147 -67
- package/dist/server/index.js +520 -381
- package/dist/server/index.js.map +1 -1
- package/dist/server/mcp/index.d.ts +63 -17
- package/dist/server/mcp/index.js +256 -54
- 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-UDLn1049.d.ts} +3 -3
- package/dist/testing/client.d.ts +14 -14
- package/dist/testing/client.js +9 -7
- package/dist/testing/client.js.map +1 -1
- package/dist/testing/index.d.ts +12 -12
- package/dist/testing/index.js +21 -12
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/mock.d.ts +5 -5
- package/dist/testing/mock.js +8 -6
- package/dist/{types-Cu0vmx7c.d.ts → types-CYliTP65.d.ts} +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/package.json +1 -1
- package/dist/chunk-2PWMUVC7.js.map +0 -1
- package/dist/chunk-4DIQHASK.js.map +0 -1
- package/dist/chunk-A6DJBGOG.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-JZBCLNDU.js.map +0 -1
- package/dist/chunk-KNDDCVQJ.js.map +0 -1
- package/dist/chunk-REX5YPHX.js.map +0 -1
- package/dist/chunk-RT6OILOI.js.map +0 -1
- package/dist/chunk-TBMKJ26R.js.map +0 -1
- package/dist/chunk-VJTFXHOK.js.map +0 -1
- package/dist/chunk-YSWH776Y.js +0 -13
- package/dist/chunk-YSWH776Y.js.map +0 -1
- package/dist/logger-BY28acW-.d.ts +0 -16
- /package/dist/{rateLimit-ZMIZJUZF.js.map → rateLimit-5RKBFLUR.js.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/testing/mockClient.ts","../src/testing/mockLive.ts","../src/testing/mockAdmin.ts","../src/testing/mockScope.ts","../src/testing/mockCollections.ts","../src/testing/mockRealtime.ts","../src/testing/mockSearch.ts","../src/testing/mockSession.tsx"],"sourcesContent":["// `createMockClient(contracts)` (RFC 0003 section 13): a client of the same\n// type as `createQuickdrawClient`'s whose members are stubs, for component\n// tests and stories that do not care about the transport. Nothing connects:\n// there is no socket and no server, and the members' hooks need no provider\n// above them. 4.1's `createMockSocket` mocked the socket under the hooks instead\n// (4.1 `src/client/testing.tsx:24-41`), which tested a fake transport.\n//\n// - A query member's `useQuery` is TanStack's own `useQuery`, on the mock's\n// `QueryClient` (retries off), fetching from the method's stub; `call`\n// and `prefetch` answer from the stub too. A mutation's `useMutation` is\n// TanStack's `useMutation` over the stub. Optimistic layers are not shown:\n// the test sets the rows a component reads.\n// - Every method's member carries its stub (`mockResolvedValue`,\n// `mockRejectedValue`, `mockImplementation`, `mockReset`, `calls`). A\n// search kit method's `useSearch` asks its stub at once, with no debounce\n// and no collection cache (`mockSearch.ts`).\n// - `useEntity`, `useEntities` and `useCollection` show what the test sets\n// with `mockRow`, `mockRemoved`, `mockError` and `mockScope`\n// (`mockLive.ts`).\n// - `qd.<service>.admin` holds the same mocked members of the admin kit's\n// methods, and `useAdminServices` asks their `adminMeta` stubs\n// (`mockAdmin.ts`).\n// - `qd.invalidate` invalidates the mock's cache through an invalidation\n// coordinator, as the real client does.\n// - `$Provider` makes the mock the provider of what it renders: the real\n// `useQuickdraw()` and `usePresence(room)` read the mock's session\n// (`$session`) and rooms (`$presence`), with no server (`mockSession.tsx`).\n// - Everything set is forgotten after each test, when the test runner has a\n// global `afterEach` (as Testing Library unmounts after each test), and by\n// `$reset()`.\n\nimport {\n QueryClient,\n useMutation,\n useQuery,\n type QueryKey,\n type Updater,\n} from \"@tanstack/react-query\";\nimport { createBinding, invalidateWith, registerQuery, type Binding } from \"../client/binding\";\nimport type { MethodMutationOptions, MethodQueryOptions } from \"../client/hooks\";\nimport { methodKey, methodKeyPrefix, type MethodQueryKey } from \"../client/keys\";\nimport { buildCaller, type MethodTarget } from \"../client/members\";\nimport type { ContractMap } from \"../contract/infer\";\nimport { createMockStore, mockLiveMembers } from \"./mockLive\";\nimport { mockSearchMember } from \"./mockSearch\";\nimport { mockSessionControls, startingSession } from \"./mockSession\";\nimport type { MethodStub, MockClient, MockClientOptions } from \"./mockTypes\";\n\n/** How a stub answers its calls. */\ntype Answer =\n | { readonly kind: \"pending\" }\n | { readonly kind: \"value\"; readonly value: unknown }\n | { readonly kind: \"error\"; readonly error: unknown }\n | { readonly kind: \"implementation\"; readonly run: (input: unknown) => unknown };\n\n/** A method's stub: its controls, and the call that answers as they say. */\ninterface Stub extends MethodStub<unknown, unknown> {\n invoke(input: unknown): Promise<unknown>;\n /** Forgets its answer and its calls without refetching anything: the test is over. */\n forget(): void;\n}\n\nconst PENDING: Answer = Object.freeze({ kind: \"pending\" });\n\n/** The answer of a call no answer was set for: it never settles. */\nconst NEVER = new Promise<never>(() => {\n // Never settles, as a call the server never answers.\n});\n\n/** A stub that calls `changed` whenever its answer changes. */\nfunction createStub(changed: () => void): Stub {\n let answer = PENDING;\n const calls: unknown[] = [];\n const answerWith = (next: Answer): void => {\n answer = next;\n changed();\n };\n return {\n invoke(input) {\n calls.push(input);\n const current = answer;\n switch (current.kind) {\n case \"value\":\n return Promise.resolve(current.value);\n case \"error\":\n return Promise.reject(current.error);\n case \"implementation\":\n return Promise.resolve().then(() => current.run(input));\n default:\n return NEVER;\n }\n },\n mockResolvedValue: (value) => {\n answerWith({ kind: \"value\", value });\n },\n mockRejectedValue: (error) => {\n answerWith({ kind: \"error\", error });\n },\n mockImplementation: (run) => {\n answerWith({ kind: \"implementation\", run });\n },\n mockReset: () => {\n calls.length = 0;\n answerWith(PENDING);\n },\n forget: () => {\n calls.length = 0;\n answer = PENDING;\n },\n get calls() {\n return [...calls];\n },\n };\n}\n\n/** `member` with `stub`'s controls on it, frozen. */\nfunction withStub<T extends object>(member: T, stub: Stub): T {\n return Object.freeze(\n Object.defineProperties(member, {\n mockResolvedValue: { value: stub.mockResolvedValue, enumerable: true },\n mockRejectedValue: { value: stub.mockRejectedValue, enumerable: true },\n mockImplementation: { value: stub.mockImplementation, enumerable: true },\n mockReset: { value: stub.mockReset, enumerable: true },\n calls: { get: () => stub.calls, enumerable: true },\n }),\n );\n}\n\n/** What the members of one mock client share. */\ninterface MockContext {\n readonly queryClient: QueryClient;\n readonly binding: Binding;\n /** A new stub for the method of `target`. */\n stub(target: MethodTarget): Stub;\n}\n\nfunction useMockQuery(\n queryClient: QueryClient,\n stub: Stub,\n queryKey: MethodQueryKey,\n input: unknown,\n options: MethodQueryOptions<unknown> = {},\n) {\n return useQuery(\n { retry: false, ...options, queryKey, queryFn: () => stub.invoke(input) },\n queryClient,\n );\n}\n\nfunction mockQueryMember(\n context: MockContext,\n target: MethodTarget,\n search: (invoke: Stub[\"invoke\"]) => Readonly<Record<string, unknown>>,\n): object {\n const stub = context.stub(target);\n const key = (input?: unknown): MethodQueryKey => methodKey(target.service, target.method, input);\n const member = withStub(\n {\n ...search(stub.invoke),\n useQuery: (input?: unknown, options?: MethodQueryOptions<unknown>) =>\n useMockQuery(context.queryClient, stub, key(input), input, options),\n call: (input?: unknown): Promise<unknown> => stub.invoke(input),\n key,\n setData: (input: unknown, updater: Updater<unknown, unknown>): unknown =>\n context.queryClient.setQueryData(key(input), updater),\n prefetch: (queryClient: QueryClient, input?: unknown): Promise<void> =>\n queryClient.prefetchQuery({ queryKey: key(input), queryFn: () => stub.invoke(input) }),\n },\n stub,\n );\n registerQuery(context.binding, member, target);\n return member;\n}\n\nfunction useMockMutation(\n queryClient: QueryClient,\n target: MethodTarget,\n stub: Stub,\n options: MethodMutationOptions<unknown, unknown> = {},\n) {\n return useMutation(\n {\n mutationKey: methodKeyPrefix(target.service, target.method),\n ...options,\n mutationFn: (input: unknown) => stub.invoke(input),\n },\n queryClient,\n );\n}\n\nfunction mockMutationMember(context: MockContext, target: MethodTarget): object {\n const stub = context.stub(target);\n return withStub(\n {\n useMutation: (options?: MethodMutationOptions<unknown, unknown>) =>\n useMockMutation(context.queryClient, target, stub, options),\n call: (input?: unknown): Promise<unknown> => stub.invoke(input),\n },\n stub,\n );\n}\n\n/** The cache of a mock client: retries off, results kept fresh. */\nfunction createMockQueryClient(): QueryClient {\n return new QueryClient({\n defaultOptions: {\n queries: { retry: false, staleTime: Number.POSITIVE_INFINITY, refetchOnWindowFocus: false },\n mutations: { retry: false },\n },\n });\n}\n\n/**\n * Fetches the cached results of a query again with its new answer. Reads\n * still waiting on the old one are cancelled first: TanStack keeps a read\n * in flight for a query that has no data yet, and an unanswered read never\n * ends.\n */\nasync function refetchWithNewAnswer(queryClient: QueryClient, queryKey: QueryKey): Promise<void> {\n await queryClient.cancelQueries({ queryKey });\n await queryClient.invalidateQueries({ queryKey });\n}\n\n/** A new stub per method, fetching a query's cached results again when its answer changes. */\nfunction stubMaker(queryClient: QueryClient, stubs: Stub[]): (target: MethodTarget) => Stub {\n return (target) => {\n const queryKey: QueryKey = methodKeyPrefix(target.service, target.method);\n const stub = createStub(() => {\n if (target.kind === \"query\") {\n void refetchWithNewAnswer(queryClient, queryKey);\n }\n });\n stubs.push(stub);\n return stub;\n };\n}\n\n/**\n * Forgets everything set on a mock: answers, calls, rows, scopes and cached\n * results. `quiet` tells no mounted hook, for the reset after a test, which\n * may run before Testing Library unmounts what the test rendered.\n */\nfunction resetMock(\n queryClient: QueryClient,\n store: ReturnType<typeof createMockStore>,\n stubs: readonly Stub[],\n quiet: boolean,\n): void {\n queryClient.clear();\n store.clear(quiet);\n for (const stub of stubs) {\n if (quiet) {\n stub.forget();\n } else {\n stub.mockReset();\n }\n }\n}\n\n/**\n * Runs `reset` after each test when the test runner has a global\n * `afterEach` (vitest with `globals: true`, jest), as Testing Library\n * registers its cleanup. A runner that takes no hook where the mock is made\n * (inside a test) leaves it to `$reset()`.\n */\nfunction resetAfterEachTest(reset: () => void): void {\n const runnerAfterEach: unknown = (globalThis as { readonly afterEach?: unknown }).afterEach;\n if (typeof runnerAfterEach !== \"function\") {\n return;\n }\n try {\n (runnerAfterEach as (hook: () => void) => void)(reset);\n } catch {\n // No hook can be registered here; `$reset()` still forgets everything.\n }\n}\n\n/**\n * Creates a mock client of `contracts`: the type of\n * `createQuickdrawClient(contracts)`, with every member a stub and no\n * transport behind it, for component tests and stories. A call stays\n * pending until the test sets its answer; a row or a scope stays loading\n * until the test sets it. Its members need no provider; components that read\n * `useQuickdraw()` or `usePresence(room)` render inside `$Provider`, which\n * shows the mock's session (`$session`). Everything set is forgotten after\n * each test (see `resetAfterEach`).\n *\n * @example\n * const qd = createMockClient({ task }, { session: { userId: \"ada\" } });\n * vi.mock(\"../lib/qd\", () => ({ qd }));\n * qd.task.get.mockResolvedValue({ id: \"t1\", title: \"Write the spec\" });\n * qd.task.useEntity.mockRow({ id: \"t1\", title: \"Write the spec\" });\n * qd.task.byProject.mockScope(\"p1\", [{ id: \"t1\", title: \"Write the spec\" }]);\n * qd.$session({ serviceAccess: { taskService: \"Admin\" } });\n * render(<TaskCard id=\"t1\" />, { wrapper: qd.$Provider });\n * expect(qd.task.get.calls).toEqual([{ id: \"t1\" }]);\n */\nexport function createMockClient<const Contracts extends ContractMap>(\n contracts: Contracts & { readonly invalidate?: never },\n options: MockClientOptions = {},\n): MockClient<Contracts> {\n const queryClient = options.queryClient ?? createMockQueryClient();\n const base = startingSession(options);\n const store = createMockStore(base);\n const session = mockSessionControls(store, queryClient, base);\n const binding = createBinding();\n binding.coordinator = session.coordinator;\n const stubs: Stub[] = [];\n const context: MockContext = { queryClient, binding, stub: stubMaker(queryClient, stubs) };\n const client = buildCaller(\n \"createMockClient\",\n contracts,\n (target, definition, contract) =>\n target.kind === \"query\"\n ? mockQueryMember(context, target, (invoke) =>\n mockSearchMember(queryClient, invoke, target, definition, contract),\n )\n : mockMutationMember(context, target),\n [\"invalidate\"],\n mockLiveMembers(store, queryClient),\n );\n Object.defineProperties(client, {\n invalidate: { value: invalidateWith(binding) },\n $queryClient: { value: queryClient },\n $Provider: { value: session.Provider },\n $session: { value: session.setSession },\n $presence: { value: session.setPresence },\n $reset: {\n value: (): void => {\n resetMock(queryClient, store, stubs, false);\n },\n },\n });\n if (options.resetAfterEach !== false) {\n resetAfterEachTest(() => {\n resetMock(queryClient, store, stubs, true);\n });\n }\n return Object.freeze(client) as MockClient<Contracts>;\n}\n","// The live members of a mock client (`mockClient.ts`): `useEntity`,\n// `useEntities` and each collection's `useCollection`, showing what the test\n// set through their controls (`mockRow`, `mockScope`, ...) instead of\n// subscribing over a socket, and the stream, channel and event members\n// (`mockRealtime.ts`). They return what the real hooks return\n// (`client/live/useEntity.ts`, `client/live/useCollection.ts`): a row or a\n// scope nobody set is loading, a view runs the contract's predicate over the\n// index rows made from the items, and a component re-renders when the test\n// changes what it shows.\n\nimport { useSyncExternalStore } from \"react\";\nimport type {\n UseEntitiesResult,\n UseEntityOptions,\n UseEntityResult,\n} from \"../client/live/memberTypes\";\nimport type { AnyContract } from \"../contract/defineContract\";\nimport type { QuickdrawError } from \"../protocol/errors\";\nimport { mockAdminNamespace } from \"./mockAdmin\";\nimport { mockCollectionMember } from \"./mockCollections\";\nimport { mockRealtimeMembers } from \"./mockRealtime\";\nimport type { EntityMock, MockScope, SessionState } from \"./mockTypes\";\n\n/** What one mocked row shows: the row, its removal, or an error. */\ntype RowState =\n | { readonly row: { readonly id: string } }\n | { readonly removed: true }\n | { readonly error: QuickdrawError };\n\n/** What one mocked scope shows: its items, or an error. */\nexport type ScopeState =\n | (MockScope & { readonly items: readonly { readonly id: string }[] })\n | { readonly error: QuickdrawError };\n\n/** The rows, scopes and session of one mock client, and the hooks' views of them. */\nexport interface MockStore {\n readonly subscribe: (listener: () => void) => () => void;\n /** The value `build` makes for `key`, the same object until the store changes. */\n read<T>(key: string, build: () => T): T;\n row(service: string, id: string): RowState | undefined;\n setRow(service: string, id: string, state: RowState): void;\n scope(service: string, collection: string, scope: string): ScopeState | undefined;\n setScope(service: string, collection: string, scope: string, state: ScopeState): void;\n /**\n * A mocked value by key: a feed's items, a channel's sent messages\n * (`mockRealtime.ts`), a room's users (`mockSession.tsx`).\n */\n value(key: string): unknown;\n setValue(key: string, value: unknown): void;\n /** Who the mock acts for (`mockSession.tsx`): the same object until it is set again. */\n session(): SessionState;\n setSession(session: SessionState): void;\n /**\n * Forgets every row, scope and value, and puts back the session the mock\n * started with; `quiet` tells no hook that shows them (they are about to\n * unmount).\n */\n clear(quiet?: boolean): void;\n}\n\nfunction keyOf(...parts: readonly string[]): string {\n return parts.join(\"\\u0000\");\n}\n\n/** Creates the store of one mock client, whose session starts as `base`. */\nexport function createMockStore(base: SessionState): MockStore {\n let session = base;\n const rows = new Map<string, RowState>();\n const scopes = new Map<string, ScopeState>();\n const values = new Map<string, unknown>();\n const views = new Map<string, unknown>();\n const listeners = new Set<() => void>();\n const changed = (): void => {\n views.clear();\n for (const listener of [...listeners]) {\n listener();\n }\n };\n return {\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n read<T>(key: string, build: () => T): T {\n if (!views.has(key)) {\n views.set(key, build());\n }\n return views.get(key) as T;\n },\n row: (service, id) => rows.get(keyOf(service, id)),\n setRow(service, id, state) {\n rows.set(keyOf(service, id), state);\n changed();\n },\n scope: (service, collection, scope) => scopes.get(keyOf(service, collection, scope)),\n setScope(service, collection, scope, state) {\n scopes.set(keyOf(service, collection, scope), state);\n changed();\n },\n value: (key) => values.get(key),\n setValue(key, value) {\n values.set(key, value);\n changed();\n },\n session: () => session,\n setSession(next) {\n session = next;\n changed();\n },\n clear(quiet = false) {\n rows.clear();\n scopes.clear();\n values.clear();\n session = base;\n if (quiet) {\n views.clear();\n } else {\n changed();\n }\n },\n };\n}\n\n/** What `useEntity` shows of a mocked row. */\nfunction rowResult(state: RowState | undefined, active: boolean): UseEntityResult<unknown> {\n if (state === undefined) {\n return { data: undefined, isLoading: active, isRemoved: false, error: null };\n }\n if (\"row\" in state) {\n return { data: state.row, isLoading: false, isRemoved: false, error: null };\n }\n if (\"removed\" in state) {\n return { data: undefined, isLoading: false, isRemoved: true, error: null };\n }\n return { data: undefined, isLoading: false, isRemoved: false, error: state.error };\n}\n\n/** What `useEntities` shows of the mocked rows `ids`. */\nfunction rowsResult(\n store: MockStore,\n service: string,\n ids: readonly string[],\n active: boolean,\n): UseEntitiesResult<unknown> {\n const data: unknown[] = [];\n const byId = new Map<string, unknown>();\n const errors = new Map<string, QuickdrawError>();\n let isLoading = false;\n for (const id of ids) {\n const shows = active && id !== \"\";\n const shown = rowResult(shows ? store.row(service, id) : undefined, shows);\n data.push(shown.data);\n if (shown.data !== undefined) {\n byId.set(id, shown.data);\n }\n if (shown.error !== null) {\n errors.set(id, shown.error);\n }\n isLoading ||= shown.isLoading;\n }\n return { data, byId, isLoading, error: errors.values().next().value ?? null, errors };\n}\n\nfunction useMockRow(\n store: MockStore,\n service: string,\n id: string | null | undefined,\n options: UseEntityOptions = {},\n): UseEntityResult<unknown> {\n const rowId = typeof id === \"string\" ? id : \"\";\n const active = options.enabled !== false && rowId !== \"\";\n const read = (): UseEntityResult<unknown> =>\n store.read(keyOf(\"row\", service, rowId, String(active)), () =>\n rowResult(active ? store.row(service, rowId) : undefined, active),\n );\n return useSyncExternalStore(store.subscribe, read, read);\n}\n\nfunction useMockRows(\n store: MockStore,\n service: string,\n ids: readonly string[],\n options: UseEntityOptions = {},\n): UseEntitiesResult<unknown> {\n const rowIds = ids.map((id) => (typeof id === \"string\" ? id : \"\"));\n const active = options.enabled !== false;\n const read = (): UseEntitiesResult<unknown> =>\n store.read(keyOf(\"rows\", service, String(active), ...rowIds), () =>\n rowsResult(store, service, rowIds, active),\n );\n return useSyncExternalStore(store.subscribe, read, read);\n}\n\n/** The id of a row the test passed to `mockRow`. */\nfunction idOf(row: unknown): string {\n const id: unknown = typeof row === \"object\" && row !== null ? (row as { id?: unknown }).id : \"\";\n if (typeof id !== \"string\" || id === \"\") {\n throw new TypeError(\"mockRow: the row needs a string id\");\n }\n return id;\n}\n\n/** The controls of a mocked service's rows. */\nfunction rowControls(store: MockStore, service: string): EntityMock<unknown> {\n return Object.freeze({\n mockRow(row: unknown) {\n store.setRow(service, idOf(row), { row: row as { readonly id: string } });\n },\n mockRemoved(id: string) {\n store.setRow(service, id, { removed: true });\n },\n mockError(id: string, error: QuickdrawError) {\n store.setRow(service, id, { error });\n },\n });\n}\n\n/**\n * The live members of each contract's service on a mock client, as\n * `buildCaller` takes them: the entity hooks for a contract with an entity,\n * each carrying the controls of the rows it shows, one member per\n * collection, and `admin` for a contract with the admin kit (its `adminMeta`\n * queries run on `queryClient`, the mock's cache).\n */\nexport function mockLiveMembers(\n store: MockStore,\n queryClient: Parameters<typeof mockAdminNamespace>[2],\n): (\n contract: AnyContract,\n methods: Readonly<Record<string, object>>,\n) => Readonly<Record<string, object>> {\n return (contract, methods) => {\n const service = contract.name;\n const members: [string, object][] = [];\n if (contract.entity !== undefined) {\n const controls = rowControls(store, service);\n const useEntity = (id: string | null | undefined, options?: UseEntityOptions) =>\n useMockRow(store, service, id, options);\n const useEntities = (ids: readonly string[], options?: UseEntityOptions) =>\n useMockRows(store, service, ids, options);\n members.push([\"useEntity\", Object.freeze(Object.assign(useEntity, controls))]);\n members.push([\"useEntities\", Object.freeze(Object.assign(useEntities, controls))]);\n }\n for (const [collection, def] of Object.entries(contract.collections)) {\n members.push([collection, mockCollectionMember(store, { service, collection, def })]);\n }\n members.push(...mockRealtimeMembers(store, contract));\n members.push(...Object.entries(mockAdminNamespace(contract, methods, queryClient, store)));\n return Object.freeze(Object.fromEntries(members));\n };\n}\n","// The mock client's `qd.<service>.admin` (`mockClient.ts`): the same\n// namespace as the typed client's (`../client/admin.ts`), holding the mocked\n// members of the admin methods, so `qd.task.admin.adminList.mockResolvedValue`\n// sets the same stub as `qd.task.adminList`. `useAdminServices` on a mock\n// client asks each `adminMeta` stub the session's grants allow (`$session`,\n// `serviceAccess`) at once, on the mock's cache, so a component test lists\n// services with `adminMeta.mockResolvedValue(meta)`.\n\nimport { useQueries, type QueryClient, type UseQueryResult } from \"@tanstack/react-query\";\nimport { useSyncExternalStore } from \"react\";\nimport {\n adminNamespace,\n type AdminMetaQuery,\n type AdminMetaState,\n type UseAdminGrants,\n type UseAdminMeta,\n} from \"../client/admin\";\nimport { methodKey } from \"../client/keys\";\nimport type { AnyContract } from \"../contract/defineContract\";\nimport type { MockStore } from \"./mockLive\";\nimport { sessionIn, useSessionScope } from \"./mockScope\";\n\nfunction statesOf(results: UseQueryResult<unknown>[]): AdminMetaState[] {\n return results.map((result) => ({\n data: result.data as AdminMetaState[\"data\"],\n error: result.error,\n }));\n}\n\n/** The mock client's `admin` member of a contract's service, on the mock's cache, with its session's grants. */\nexport function mockAdminNamespace(\n contract: AnyContract,\n methods: Readonly<Record<string, object>>,\n queryClient: QueryClient,\n store: MockStore,\n): Readonly<Record<string, object>> {\n const useMockAdminMeta: UseAdminMeta = (queries: readonly AdminMetaQuery[], enabled: boolean) =>\n useQueries(\n {\n queries: queries.map((query) => ({\n queryKey: methodKey(query.serviceName, query.method, undefined),\n queryFn: () => query.member.call(),\n enabled,\n retry: false,\n })),\n combine: statesOf,\n },\n queryClient,\n );\n const useMockGrants: UseAdminGrants = () => {\n // Under a `$Provider` with a `session` prop, that session's grants (`mockScope.ts`).\n const scope = useSessionScope(store);\n const grants = () => {\n const session = sessionIn(store, scope);\n return session.isKnown ? session.serviceAccess : null;\n };\n return useSyncExternalStore(store.subscribe, grants, grants);\n };\n return adminNamespace(contract, methods, { useMeta: useMockAdminMeta, useGrants: useMockGrants });\n}\n","// The session `<mock.$Provider session={...}>` gives its subtree (finding\n// F6.2 of the quickdraw-chat migration): a Storybook docs page renders every\n// story at once, each inside the mock's provider, and `$session(...)` is one\n// session for the whole mock, so every story showed the last one set. The\n// prop is laid over the mock's own session (`$session`) field by field for\n// what renders inside that provider: the real `useQuickdraw()` and\n// `usePresence(room)` read it through the provider's connection\n// (`mockSession.tsx`), and the mock's own hooks that depend on who the user\n// is (collection views, the admin grants) read it from this context.\n\nimport { createContext, useContext } from \"react\";\nimport type { MockStore } from \"./mockLive\";\nimport type { SessionState } from \"./mockTypes\";\n\n/** Lays a `$Provider`'s `session` prop over the mock's own session: the same object while that one does not change. */\nexport type SessionScope = (session: SessionState) => SessionState;\n\n/** The scope a `$Provider` with a `session` prop set, for one mock's store. */\nexport interface ScopedSession {\n readonly store: MockStore;\n readonly scope: SessionScope;\n}\n\n/** Where a `$Provider` with a `session` prop puts its scope; `undefined` elsewhere. */\nexport const MockSessionScope = createContext<ScopedSession | undefined>(undefined);\n\n/** The scope that applies to `store` where this renders: one of its own providers' `session` prop. */\nexport function useSessionScope(store: MockStore): SessionScope | undefined {\n const scoped = useContext(MockSessionScope);\n return scoped?.store === store ? scoped.scope : undefined;\n}\n\n/** The session `store` shows under `scope`: its own, or the prop over it. */\nexport function sessionIn(store: MockStore, scope: SessionScope | undefined): SessionState {\n const session = store.session();\n return scope === undefined ? session : scope(session);\n}\n","// The collections of a mock client (`mockLive.ts`): each collection's\n// `useCollection`, showing the scope the test set (`mockScope`) instead of\n// subscribing over a socket, as the real hook returns it\n// (`client/live/useCollection.ts`): a scope nobody set is loading, and a\n// view runs the contract's predicate over the index rows made from the\n// items, for the session's user, or under a `$Provider` with a `session`\n// prop for that session's (`mockScope.ts`).\n\nimport { useSyncExternalStore } from \"react\";\nimport { indexRowFromItem, type IndexRow } from \"../client/live/collectionIndex\";\nimport type { UseCollectionOptions, UseCollectionResult } from \"../client/live/memberTypes\";\nimport { viewPredicate } from \"../client/live/views\";\nimport type { CollectionDef, Viewer } from \"../contract/collections\";\nimport type { QuickdrawError } from \"../protocol/errors\";\nimport type { MockStore, ScopeState } from \"./mockLive\";\nimport { sessionIn, useSessionScope } from \"./mockScope\";\nimport type { MockScope, SessionState } from \"./mockTypes\";\n\nfunction keyOf(...parts: readonly string[]): string {\n return parts.join(\"\\u0000\");\n}\n\nconst idle = (): Promise<void> => Promise.resolve();\n\n/** A mock shows no optimistic additions, so none is ever pending. */\nconst NONE_PENDING: ReadonlySet<string> = new Set();\n\n/** A mocked scope with nothing in it yet. */\nfunction emptyScope(active: boolean): UseCollectionResult<unknown, IndexRow> {\n return {\n items: [],\n pending: NONE_PENDING,\n checking: NONE_PENDING,\n // A mock's mutations add nothing, so nothing is refused either.\n refused: [],\n index: undefined,\n byId: new Map(),\n totalCount: null,\n hasMore: false,\n clamped: false,\n indexTruncated: false,\n isLoading: active,\n isLoadingMore: false,\n error: null,\n loadMore: idle,\n loadItems: idle,\n refresh: idle,\n };\n}\n\n/** What `useCollection` shows of a mocked scope, through `view` for `who`. */\nfunction scopeResult(\n def: CollectionDef,\n state: ScopeState | undefined,\n options: { readonly view: string | undefined; readonly who: Viewer; readonly active: boolean },\n): UseCollectionResult<unknown, IndexRow> {\n const empty = emptyScope(options.active);\n if (state === undefined) {\n return empty;\n }\n if (\"error\" in state) {\n return { ...empty, isLoading: false, error: state.error };\n }\n const selects = viewPredicate(def, options.view);\n const keep = (row: Readonly<Record<string, unknown>>): boolean =>\n selects === undefined || selects(row, options.who);\n const index =\n def.index === undefined\n ? undefined\n : state.items.map((item) => indexRowFromItem(def, item)).filter(keep);\n const members = new Set(index?.map((row) => row.id));\n const items = state.items.filter((item) =>\n index === undefined ? keep(item as Readonly<Record<string, unknown>>) : members.has(item.id),\n );\n return {\n ...empty,\n items,\n index,\n byId: new Map(items.map((item) => [item.id, item])),\n totalCount: state.totalCount ?? state.items.length,\n hasMore: state.hasMore ?? false,\n isLoading: false,\n };\n}\n\n/** One collection of a contract, as its mock member shows it. */\ninterface MockCollectionTarget {\n readonly service: string;\n readonly collection: string;\n readonly def: CollectionDef;\n}\n\n/**\n * Who the views select members for: the session's user once known, as the\n * real views read the hello; under a `$Provider` with a `session` prop,\n * that session's (`mockScope.ts`).\n */\nfunction viewerOf(session: SessionState): Viewer {\n return { userId: session.isKnown ? (session.userId ?? \"\") : \"\" };\n}\n\nfunction useMockScope(\n store: MockStore,\n target: MockCollectionTarget,\n scope: string | null | undefined,\n options: UseCollectionOptions = {},\n): UseCollectionResult<unknown, IndexRow> {\n const value = typeof scope === \"string\" ? scope : \"\";\n const active = options.enabled !== false && value !== \"\";\n const { service, collection, def } = target;\n const sessionScope = useSessionScope(store);\n const read = (): UseCollectionResult<unknown, IndexRow> => {\n const who = viewerOf(sessionIn(store, sessionScope));\n // The viewer is part of the key: two providers' sessions may see one scope's views apart.\n const view = options.view ?? \"\";\n const key = keyOf(\"scope\", service, collection, value, view, String(active), who.userId);\n return store.read(key, () =>\n scopeResult(def, active ? store.scope(service, collection, value) : undefined, {\n view: options.view,\n who,\n active,\n }),\n );\n };\n return useSyncExternalStore(store.subscribe, read, read);\n}\n\n/** `qd.<service>.<collection>` of a mock client. */\nexport function mockCollectionMember(store: MockStore, target: MockCollectionTarget): object {\n return Object.freeze({\n useCollection: (scope: string | null | undefined, options?: UseCollectionOptions) =>\n useMockScope(store, target, scope, options),\n mockScope(scope: string, items: readonly { readonly id: string }[], extra: MockScope = {}) {\n store.setScope(target.service, target.collection, scope, { ...extra, items: [...items] });\n },\n mockError(scope: string, error: QuickdrawError) {\n store.setScope(target.service, target.collection, scope, { error });\n },\n });\n}\n","// The stream, channel and event members of a mock client (`mockClient.ts`,\n// RFC 0003 section 12.5), with no socket behind them:\n//\n// - a stream's `useStream` shows what the test set with `mockItems` (the\n// latest `max` of them) or `mockError`; a feed nobody set is loading;\n// - a channel's `useChannel` is always ready, and its `send` records each\n// payload in the member's `sent`;\n// - an event's `useEvent` registers its handler, and `mockEmit(payload)`\n// calls every mounted one (wrap it in Testing Library's `act`).\n//\n// What the test set is forgotten with the rest of the mock (`$reset`, or\n// after each test).\n\nimport { useEffect, useRef, useSyncExternalStore } from \"react\";\nimport { streamMaxOf, type StreamState } from \"../client/live/streams\";\nimport type { UseEventOptions } from \"../client/live/useEvent\";\nimport type { UseStreamOptions, UseStreamResult } from \"../client/live/useStream\";\nimport type { AnyContract } from \"../contract/defineContract\";\nimport { isScopedStream } from \"../contract/realtime\";\nimport type { QuickdrawError } from \"../protocol/errors\";\nimport type { MockStore } from \"./mockLive\";\n\n/** One feed as the test set it: its items, or why it failed. */\ntype FeedState = { readonly items: readonly unknown[] } | { readonly error: QuickdrawError };\n\nfunction keyOf(...parts: readonly string[]): string {\n return parts.join(\"\\u0000\");\n}\n\n/** What `useStream` shows of a mocked feed. */\nfunction feedResult(state: unknown, active: boolean, max: number): StreamState {\n if (!active || state === undefined) {\n return { items: [], isLoading: active, error: null };\n }\n const feed = state as FeedState;\n if (\"error\" in feed) {\n return { items: [], isLoading: false, error: feed.error };\n }\n const items = feed.items.length > max ? feed.items.slice(feed.items.length - max) : feed.items;\n return { items, isLoading: false, error: null };\n}\n\n/** `qd.<service>.<stream>` of a mock client. */\nfunction mockStreamMember(\n store: MockStore,\n service: string,\n stream: string,\n scoped: boolean,\n): object {\n const feedKey = (scope: unknown): string =>\n keyOf(\"stream\", service, stream, scoped && typeof scope === \"string\" ? scope : \"\");\n const useMockStream = (first?: unknown, second?: UseStreamOptions): UseStreamResult<unknown> => {\n const scope = scoped ? first : undefined;\n const options = (scoped ? second : (first as UseStreamOptions | undefined)) ?? {};\n const active =\n options.enabled !== false && (!scoped || (typeof scope === \"string\" && scope !== \"\"));\n const max = streamMaxOf(options.max);\n const read = (): StreamState =>\n store.read(keyOf(\"view\", feedKey(scope), String(active), String(max)), () =>\n feedResult(store.value(feedKey(scope)), active, max),\n );\n return useSyncExternalStore(store.subscribe, read, read);\n };\n // `(scope, value)` for a scoped stream, `(value)` for a global one.\n const set = (args: readonly unknown[], value: (last: unknown) => FeedState): void => {\n store.setValue(feedKey(scoped ? args[0] : undefined), value(args.at(-1)));\n };\n return Object.freeze({\n useStream: useMockStream,\n mockItems: (...args: unknown[]) => {\n set(args, (items) => ({ items: [...(items as readonly unknown[])] }));\n },\n mockError: (...args: unknown[]) => {\n set(args, (error) => ({ error: error as QuickdrawError }));\n },\n });\n}\n\n/** `qd.<service>.<channel>` of a mock client. */\nfunction mockChannelMember(store: MockStore, service: string, channel: string): object {\n const sentKey = keyOf(\"sent\", service, channel);\n const sent = (): readonly unknown[] =>\n (store.value(sentKey) as readonly unknown[] | undefined) ?? [];\n const result = Object.freeze({\n send: (payload: unknown) => {\n store.setValue(sentKey, [...sent(), payload]);\n },\n isReady: true,\n });\n return Object.freeze(\n Object.defineProperties(\n { useChannel: () => result },\n { sent: { get: () => [...sent()], enumerable: true } },\n ),\n );\n}\n\n/** `qd.<service>.<event>` of a mock client. */\nfunction mockEventMember(): object {\n const handlers = new Set<(payload: unknown) => void>();\n return Object.freeze({\n useEvent: (handler: (payload: unknown) => void, options: UseEventOptions = {}) => {\n const latest = useRef(handler);\n useEffect(() => {\n latest.current = handler;\n });\n const enabled = options.enabled !== false;\n useEffect(() => {\n if (!enabled) {\n return undefined;\n }\n const entry = (payload: unknown): void => {\n latest.current(payload);\n };\n handlers.add(entry);\n return () => {\n handlers.delete(entry);\n };\n }, [enabled]);\n },\n mockEmit: (payload: unknown) => {\n for (const handler of [...handlers]) {\n handler(payload);\n }\n },\n });\n}\n\n/** The stream, channel and event members of one contract's service on a mock client. */\nexport function mockRealtimeMembers(store: MockStore, contract: AnyContract): [string, object][] {\n const service = contract.name;\n return [\n ...Object.entries(contract.streams).map(([stream, def]): [string, object] => [\n stream,\n mockStreamMember(store, service, stream, isScopedStream(def)),\n ]),\n ...Object.keys(contract.channels).map((channel): [string, object] => [\n channel,\n mockChannelMember(store, service, channel),\n ]),\n ...Object.keys(contract.events).map((event): [string, object] => [event, mockEventMember()]),\n ];\n}\n","// The mock client's `useSearch` (`mockClient.ts`), on the member of a method\n// the search kit made: the method's stub answers each query at once, with no\n// debounce, server or collection cache, so a component test sets the results\n// it shows with `mockResolvedValue({ items, nextCursor })` and reads what was\n// searched for in `calls`.\n\nimport { useQuery, type QueryClient } from \"@tanstack/react-query\";\nimport { methodKey } from \"../client/keys\";\nimport { searchCall, searchTargetOf, type SearchTarget } from \"../client/live/searchResults\";\nimport type { UseSearchOptions, UseSearchResult } from \"../client/live/searchTypes\";\nimport type { MethodTarget } from \"../client/members\";\nimport type { AnyContract } from \"../contract/defineContract\";\nimport type { SearchPage } from \"../contract/kits/searchSchemas\";\nimport type { MethodDef } from \"../contract/methods\";\nimport type { QuickdrawError } from \"../protocol/errors\";\n\n/** Answers one call of the stubbed method. */\ntype Invoke = (input: unknown) => Promise<unknown>;\n\nconst NO_RESULTS: readonly never[] = Object.freeze([]);\n\nfunction useMockSearch(\n queryClient: QueryClient,\n invoke: Invoke,\n target: SearchTarget,\n q: string,\n options: UseSearchOptions,\n): UseSearchResult<unknown> {\n const typed = typeof q === \"string\" ? q.trim() : \"\";\n const scope = typeof options.scope === \"string\" ? options.scope : \"\";\n const input = searchCall(typed, scope, options.limit);\n const active = options.enabled !== false && typed.length >= target.minLength;\n const result = useQuery(\n {\n queryKey: methodKey(target.service, target.method, input),\n queryFn: () => invoke(input) as Promise<SearchPage<unknown>>,\n enabled: active,\n retry: false,\n },\n queryClient,\n );\n const page = active ? result.data : undefined;\n return {\n items: page?.items ?? NO_RESULTS,\n hasMore: (page?.nextCursor ?? null) !== null,\n isSearching: active && result.isFetching,\n isLoading: active && result.isFetching && page === undefined,\n error: active ? (result.error as QuickdrawError | null) : null,\n };\n}\n\n/** The mock `useSearch` a query member gets when the search kit made its method; nothing otherwise. */\nexport function mockSearchMember(\n queryClient: QueryClient,\n invoke: Invoke,\n target: MethodTarget,\n definition: MethodDef,\n contract: AnyContract,\n): Readonly<Record<string, unknown>> {\n const search = searchTargetOf(target, definition, contract);\n if (search === undefined) {\n return {};\n }\n return {\n useSearch: (q: string, options: UseSearchOptions = {}) =>\n useMockSearch(queryClient, invoke, search, q, options),\n };\n}\n","// The session of a mock client (`mockClient.ts`, RFC 0003 section 13): who\n// the mock acts for, as the real `useQuickdraw()` and `usePresence(room)`\n// read it under the mock's provider (`mock.$Provider`), with no server and\n// no socket. 4.1 had nothing of the kind, and an app on the first 5.0\n// candidates re-exported `useQuickdraw` from the module it mocked, so that a\n// story or a test could fake the connection state (finding F3.1).\n//\n// - The provider fills the same context `QuickdrawProvider` fills, with a\n// connection that never opens: its state is made from the session (a\n// hello naming `userId` with `serviceAccess`, connected or connecting,\n// known or not), and every hook that reads the context reads it, on a\n// server too (`renderOnServerAs`).\n// - `usePresence(room)` shows the users `$presence(room, users)` set, while\n// the session is known (a socket is in no room before its hello).\n// - The views of the mock's collections select members for the session's\n// user, as the real ones do for the user the hello names.\n// - `<mock.$Provider session={...}>` lays its `session` over the mock's for\n// what renders inside it (finding F6.2): its own connection shows that\n// session, and the mock's hooks read it from `mockScope.ts`, so stories\n// rendered side by side (a Storybook docs page) each show their own.\n//\n// Imports nothing of Testing Library, so a browser bundle (Storybook) can\n// take it: `./testing/mock` is its own entry.\n\nimport { QueryClientProvider, type QueryClient } from \"@tanstack/react-query\";\nimport { useMemo, type ReactElement, type ReactNode } from \"react\";\nimport {\n createQuickdrawConnection,\n type ConnectionState,\n type QuickdrawConnection,\n} from \"../client/connection\";\nimport { QuickdrawContext, renderOnServerAs, type QuickdrawContextValue } from \"../client/context\";\nimport { createInvalidationCoordinator, type InvalidationCoordinator } from \"../client/coordinator\";\nimport { inertLiveData } from \"../client/live/liveData\";\nimport type { PresenceStore } from \"../client/live/presence\";\nimport { MAX_SUBSCRIBE_IDS, PROTOCOL_VERSION, type HelloFrame } from \"../protocol/version\";\nimport { QUICKDRAW_VERSION } from \"../version\";\nimport type { MockStore } from \"./mockLive\";\nimport { MockSessionScope, type ScopedSession, type SessionScope } from \"./mockScope\";\nimport type { MockClientOptions, MockSession, SessionState } from \"./mockTypes\";\n\n/** The URL of a mock's connection, which never opens. */\nconst MOCK_URL = \"http://quickdraw-mock.invalid\";\n\nconst NOBODY: readonly string[] = Object.freeze([]);\n\n/** The limits a mock's hello announces: the server's defaults. */\nconst MOCK_LIMITS: HelloFrame[\"limits\"] = Object.freeze({\n maxInFlightQueries: 16,\n maxQueuedQueries: 64,\n maxSubscribeIds: MAX_SUBSCRIBE_IDS,\n callTimeoutMs: 30_000,\n subscriptions: Object.freeze({ maxInFlight: 8, maxQueued: 64 }),\n});\n\nfunction checkUser(owner: string, userId: unknown): string | null | undefined {\n if (userId === undefined || userId === null || (typeof userId === \"string\" && userId !== \"\")) {\n return userId;\n }\n throw new TypeError(`${owner}: userId must be a non-empty string, or null for an anonymous user`);\n}\n\nfunction checkFlag(owner: string, name: string, value: unknown): boolean | undefined {\n if (value === undefined || typeof value === \"boolean\") {\n return value;\n }\n throw new TypeError(`${owner}: ${name} must be true or false`);\n}\n\n/**\n * `session` over `base`: what it sets replaces what `base` has, field by\n * field; a field it leaves out (or sets to `undefined`) keeps `base`'s.\n * Throws a `TypeError` naming `owner` for a field of the wrong kind.\n */\nexport function sessionOver(\n owner: string,\n base: SessionState,\n session: MockSession | undefined,\n): SessionState {\n if (session === undefined) {\n return base;\n }\n if (typeof session !== \"object\" || session === null) {\n throw new TypeError(\n `${owner}: the session is { userId?, serviceAccess?, isConnected?, isKnown? }`,\n );\n }\n const { serviceAccess } = session;\n if (\n serviceAccess !== undefined &&\n (typeof serviceAccess !== \"object\" || serviceAccess === null)\n ) {\n throw new TypeError(`${owner}: serviceAccess maps service names to access levels`);\n }\n const userId = checkUser(owner, session.userId);\n const isConnected = checkFlag(owner, \"isConnected\", session.isConnected);\n const isKnown = checkFlag(owner, \"isKnown\", session.isKnown);\n return Object.freeze({\n userId: userId === undefined ? base.userId : userId,\n serviceAccess: Object.freeze({ ...(serviceAccess ?? base.serviceAccess) }),\n isConnected: isConnected ?? base.isConnected,\n isKnown: isKnown ?? base.isKnown,\n });\n}\n\n/** The id a mock's hello names its server by: one mock server, never restarted. */\nconst MOCK_SERVER_ID = \"mock\";\n\n/** The connection state a session shows: connected or connecting, with a hello once it is known. */\nfunction stateOf(session: SessionState): ConnectionState {\n const hello: HelloFrame | null = session.isKnown\n ? Object.freeze({\n protocol: PROTOCOL_VERSION,\n server: QUICKDRAW_VERSION,\n serverId: MOCK_SERVER_ID,\n limits: MOCK_LIMITS,\n features: Object.freeze([]),\n userId: session.userId,\n serviceAccess: session.serviceAccess,\n })\n : null;\n return Object.freeze({\n status: session.isConnected ? \"connected\" : \"connecting\",\n reconnecting: !session.isConnected && session.isKnown,\n hello,\n serviceAccess: hello === null ? null : session.serviceAccess,\n refusal: null,\n backoff: Object.freeze({}),\n });\n}\n\n/**\n * The connection of a mock client: a real connection that is never opened\n * (its socket, lane and topics stay idle, so a real client's hook rendered\n * under the mock's provider by mistake waits rather than throws), whose\n * state comes from the mock's session. Opening, closing and new credentials\n * do nothing.\n */\nfunction mockConnection(store: MockStore, sessionOf: () => SessionState): QuickdrawConnection {\n const inert = createQuickdrawConnection({ url: MOCK_URL, transports: [\"websocket\"] });\n let shown: { readonly session: SessionState; readonly state: ConnectionState } | undefined;\n const getState = (): ConnectionState => {\n const session = sessionOf();\n if (shown?.session !== session) {\n shown = { session, state: stateOf(session) };\n }\n return shown.state;\n };\n const nothing = (): void => undefined;\n return Object.freeze({\n url: inert.url,\n timeoutMs: inert.timeoutMs,\n socket: inert.socket,\n subscriptionLane: inert.subscriptionLane,\n getState,\n subscribe: store.subscribe,\n open: nothing,\n close: nothing,\n retain: () => nothing,\n setAuth: () => false,\n nextCallId: inert.nextCallId,\n reportRateLimited: nothing,\n backoffRemaining: () => 0,\n onReconnect: () => nothing,\n onHello: (listener: (hello: HelloFrame) => void) => mockHellos(getState, store, listener),\n watch: () => nothing,\n waitForJoin: () => undefined,\n });\n}\n\n/**\n * `onHello` on a mock's connection: each session that is connected and\n * known is a hello (`$session(...)` that sets one is a reconnect), the\n * current one in a microtask, as a real connection gives it.\n */\nfunction mockHellos(\n getState: () => ConnectionState,\n store: MockStore,\n listener: (hello: HelloFrame) => void,\n): () => void {\n let last: HelloFrame | null = null;\n let stopped = false;\n const deliver = (): void => {\n const { status, hello } = getState();\n if (!stopped && status === \"connected\" && hello !== null && hello !== last) {\n last = hello;\n listener(hello);\n }\n };\n queueMicrotask(deliver);\n const stop = store.subscribe(deliver);\n return () => {\n stopped = true;\n stop();\n };\n}\n\n/** The key of a room's users in the mock's store. */\nfunction presenceKey(room: string): string {\n return `presence\\u0000${room}`;\n}\n\n/**\n * The presence `usePresence` reads on a mock: the users the test set per\n * room, nobody while the session is not known. No frame reaches it.\n */\nfunction mockPresence(store: MockStore, sessionOf: () => SessionState): PresenceStore {\n return Object.freeze({\n users(room: string): readonly string[] {\n if (!sessionOf().isKnown) {\n return NOBODY;\n }\n return (store.value(presenceKey(room)) as readonly string[] | undefined) ?? NOBODY;\n },\n // Every change of the store: `users` returns the same array until the room's own changes.\n listen: (_room: string, listener: () => void) => store.subscribe(listener),\n receive: () => undefined,\n clear: () => undefined,\n });\n}\n\n/** What a mock client gives its tests besides its members: its provider, session and presence. */\n/** The props of a mock's provider: what it renders, and the session it shows there. */\nexport interface MockProviderProps {\n readonly children?: ReactNode;\n /**\n * The session the subtree shows, laid over the mock's own (`$session`)\n * field by field: what it leaves out follows the mock's. Each provider\n * has its own, so stories rendered side by side show their own sessions.\n */\n readonly session?: MockSession;\n}\n\nexport interface MockSessionControls {\n readonly Provider: (props: MockProviderProps) => ReactElement;\n /** The invalidation coordinator of the mock's cache, which the provider gives the hooks too. */\n readonly coordinator: InvalidationCoordinator;\n setSession(session: MockSession): void;\n setPresence(room: string, users: readonly string[]): void;\n}\n\n/** The session a mock starts with: its `session` option over `userId`, connected and known. */\nexport function startingSession(options: MockClientOptions): SessionState {\n const fallback: SessionState = Object.freeze({\n userId: null,\n serviceAccess: Object.freeze({}),\n isConnected: true,\n isKnown: true,\n });\n const withUser = sessionOver(\n \"createMockClient\",\n fallback,\n options.userId === undefined ? undefined : { userId: options.userId },\n );\n return sessionOver(\"createMockClient\", withUser, options.session);\n}\n\n/**\n * `session` laid over the session of `store` (`$Provider`'s prop): checked\n * at once, and the same object while the mock's own session does not change.\n */\nfunction scopeOf(store: MockStore, session: MockSession): SessionScope {\n sessionOver(\"$Provider\", store.session(), session);\n let last: { readonly base: SessionState; readonly shown: SessionState } | undefined;\n return (base) => {\n if (last?.base !== base) {\n last = { base, shown: sessionOver(\"$Provider\", base, session) };\n }\n return last.shown;\n };\n}\n\n/**\n * The provider, session and presence of one mock client: `base` is the\n * session it starts with and goes back to on a reset; `$session` sets\n * another over it.\n */\nexport function mockSessionControls(\n store: MockStore,\n queryClient: QueryClient,\n base: SessionState,\n): MockSessionControls {\n const coordinator = createInvalidationCoordinator(queryClient);\n /** The context value of a connection showing `sessionOf`'s session. */\n const contextOver = (sessionOf: () => SessionState): QuickdrawContextValue => {\n const connection = mockConnection(store, sessionOf);\n // A server renders the session too, so a story or a test that renders on one hydrates it.\n renderOnServerAs(connection, connection.getState);\n inertLiveData(connection, queryClient, mockPresence(store, sessionOf));\n return Object.freeze({ connection, queryClient, coordinator });\n };\n const value = contextOver(store.session);\n /** A provider's `session` prop: its own connection, and the scope the mock's hooks read. */\n const scopedBy = (key: string): { value: QuickdrawContextValue; scoped: ScopedSession } => {\n const scope = scopeOf(store, JSON.parse(key) as MockSession);\n return {\n value: contextOver(() => scope(store.session())),\n scoped: Object.freeze({ store, scope }),\n };\n };\n function MockQuickdrawProvider({ children, session }: MockProviderProps): ReactElement {\n // By value: a story passes a new object on every render.\n const key = session === undefined ? undefined : JSON.stringify(session);\n const scoped = useMemo(() => (key === undefined ? undefined : scopedBy(key)), [key]);\n return (\n <QueryClientProvider client={queryClient}>\n <QuickdrawContext.Provider value={scoped?.value ?? value}>\n <MockSessionScope.Provider value={scoped?.scoped}>{children}</MockSessionScope.Provider>\n </QuickdrawContext.Provider>\n </QueryClientProvider>\n );\n }\n return Object.freeze({\n Provider: MockQuickdrawProvider,\n coordinator,\n setSession(session: MockSession): void {\n store.setSession(sessionOver(\"$session\", base, session));\n },\n setPresence(room: string, users: readonly string[]): void {\n if (typeof room !== \"string\" || room === \"\") {\n throw new TypeError(\"$presence: room must be a room's name\");\n }\n if (!Array.isArray(users) || !users.every((user) => typeof user === \"string\")) {\n throw new TypeError(\"$presence: users must be a list of user ids\");\n }\n store.setValue(presenceKey(room), Object.freeze([...new Set(users)]));\n },\n });\n}\n"],"mappings":"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","names":["useQuery","useSyncExternalStore","useSyncExternalStore","useSyncExternalStore","useSyncExternalStore","keyOf","useSyncExternalStore","keyOf","useSyncExternalStore","useQuery"]}
|
|
1
|
+
{"version":3,"sources":["../src/testing/mockClient.ts","../src/testing/mockLive.ts","../src/testing/mockAdmin.ts","../src/testing/mockScope.ts","../src/testing/mockCollections.ts","../src/testing/mockRealtime.ts","../src/testing/mockSearch.ts","../src/testing/mockSession.tsx"],"sourcesContent":["// `createMockClient(contracts)` (RFC 0003 section 13): a client of the same\n// type as `createQuickdrawClient`'s whose members are stubs, for component\n// tests and stories that do not care about the transport. Nothing connects:\n// there is no socket and no server, and the members' hooks need no provider\n// above them. 4.1's `createMockSocket` mocked the socket under the hooks instead\n// (4.1 `src/client/testing.tsx:24-41`), which tested a fake transport.\n//\n// - A query member's `useQuery` is TanStack's own `useQuery`, on the mock's\n// `QueryClient` (retries off), fetching from the method's stub; `call`\n// and `prefetch` answer from the stub too. A mutation's `useMutation` is\n// TanStack's `useMutation` over the stub. Optimistic layers are not shown:\n// the test sets the rows a component reads.\n// - Every method's member carries its stub (`mockResolvedValue`,\n// `mockRejectedValue`, `mockImplementation`, `mockReset`, `calls`). A\n// search kit method's `useSearch` asks its stub at once, with no debounce\n// and no collection cache (`mockSearch.ts`).\n// - `useEntity`, `useEntities` and `useCollection` show what the test sets\n// with `mockRow`, `mockRemoved`, `mockError` and `mockScope`\n// (`mockLive.ts`).\n// - `qd.<service>.admin` holds the same mocked members of the admin kit's\n// methods, and `useAdminServices` asks their `adminMeta` stubs\n// (`mockAdmin.ts`).\n// - `qd.invalidate` invalidates the mock's cache through an invalidation\n// coordinator, as the real client does.\n// - `$Provider` makes the mock the provider of what it renders: the real\n// `useQuickdraw()` and `usePresence(room)` read the mock's session\n// (`$session`) and rooms (`$presence`), with no server (`mockSession.tsx`).\n// - Everything set is forgotten after each test, when the test runner has a\n// global `afterEach` (as Testing Library unmounts after each test), and by\n// `$reset()`.\n\nimport {\n QueryClient,\n useMutation,\n useQuery,\n type QueryKey,\n type Updater,\n} from \"@tanstack/react-query\";\nimport { createBinding, invalidateWith, registerQuery, type Binding } from \"../client/binding\";\nimport type { MethodMutationOptions, MethodQueryOptions } from \"../client/hooks\";\nimport { methodKey, methodKeyPrefix, type MethodQueryKey } from \"../client/keys\";\nimport { buildCaller, type MethodTarget } from \"../client/members\";\nimport type { ContractMap } from \"../contract/infer\";\nimport { createMockStore, mockLiveMembers } from \"./mockLive\";\nimport { mockSearchMember } from \"./mockSearch\";\nimport { mockSessionControls, startingSession } from \"./mockSession\";\nimport type { MethodStub, MockClient, MockClientOptions } from \"./mockTypes\";\n\n/** How a stub answers its calls. */\ntype Answer =\n | { readonly kind: \"pending\" }\n | { readonly kind: \"value\"; readonly value: unknown }\n | { readonly kind: \"error\"; readonly error: unknown }\n | { readonly kind: \"implementation\"; readonly run: (input: unknown) => unknown };\n\n/** A method's stub: its controls, and the call that answers as they say. */\ninterface Stub extends MethodStub<unknown, unknown> {\n invoke(input: unknown): Promise<unknown>;\n /** Forgets its answer and its calls without refetching anything: the test is over. */\n forget(): void;\n}\n\nconst PENDING: Answer = Object.freeze({ kind: \"pending\" });\n\n/** The answer of a call no answer was set for: it never settles. */\nconst NEVER = new Promise<never>(() => {\n // Never settles, as a call the server never answers.\n});\n\n/** A stub that calls `changed` whenever its answer changes. */\nfunction createStub(changed: () => void): Stub {\n let answer = PENDING;\n const calls: unknown[] = [];\n const answerWith = (next: Answer): void => {\n answer = next;\n changed();\n };\n return {\n invoke(input) {\n calls.push(input);\n const current = answer;\n switch (current.kind) {\n case \"value\":\n return Promise.resolve(current.value);\n case \"error\":\n return Promise.reject(current.error);\n case \"implementation\":\n return Promise.resolve().then(() => current.run(input));\n default:\n return NEVER;\n }\n },\n mockResolvedValue: (value) => {\n answerWith({ kind: \"value\", value });\n },\n mockRejectedValue: (error) => {\n answerWith({ kind: \"error\", error });\n },\n mockImplementation: (run) => {\n answerWith({ kind: \"implementation\", run });\n },\n mockReset: () => {\n calls.length = 0;\n answerWith(PENDING);\n },\n forget: () => {\n calls.length = 0;\n answer = PENDING;\n },\n get calls() {\n return [...calls];\n },\n };\n}\n\n/** `member` with `stub`'s controls on it, frozen. */\nfunction withStub<T extends object>(member: T, stub: Stub): T {\n return Object.freeze(\n Object.defineProperties(member, {\n mockResolvedValue: { value: stub.mockResolvedValue, enumerable: true },\n mockRejectedValue: { value: stub.mockRejectedValue, enumerable: true },\n mockImplementation: { value: stub.mockImplementation, enumerable: true },\n mockReset: { value: stub.mockReset, enumerable: true },\n calls: { get: () => stub.calls, enumerable: true },\n }),\n );\n}\n\n/** What the members of one mock client share. */\ninterface MockContext {\n readonly queryClient: QueryClient;\n readonly binding: Binding;\n /** A new stub for the method of `target`. */\n stub(target: MethodTarget): Stub;\n}\n\nfunction useMockQuery(\n queryClient: QueryClient,\n stub: Stub,\n queryKey: MethodQueryKey,\n input: unknown,\n options: MethodQueryOptions<unknown> = {},\n) {\n return useQuery(\n { retry: false, ...options, queryKey, queryFn: () => stub.invoke(input) },\n queryClient,\n );\n}\n\nfunction mockQueryMember(\n context: MockContext,\n target: MethodTarget,\n search: (invoke: Stub[\"invoke\"]) => Readonly<Record<string, unknown>>,\n): object {\n const stub = context.stub(target);\n const key = (input?: unknown): MethodQueryKey => methodKey(target.service, target.method, input);\n const member = withStub(\n {\n ...search(stub.invoke),\n useQuery: (input?: unknown, options?: MethodQueryOptions<unknown>) =>\n useMockQuery(context.queryClient, stub, key(input), input, options),\n call: (input?: unknown): Promise<unknown> => stub.invoke(input),\n key,\n setData: (input: unknown, updater: Updater<unknown, unknown>): unknown =>\n context.queryClient.setQueryData(key(input), updater),\n prefetch: (queryClient: QueryClient, input?: unknown): Promise<void> =>\n queryClient.prefetchQuery({ queryKey: key(input), queryFn: () => stub.invoke(input) }),\n },\n stub,\n );\n registerQuery(context.binding, member, target);\n return member;\n}\n\nfunction useMockMutation(\n queryClient: QueryClient,\n target: MethodTarget,\n stub: Stub,\n options: MethodMutationOptions<unknown, unknown> = {},\n) {\n return useMutation(\n {\n mutationKey: methodKeyPrefix(target.service, target.method),\n ...options,\n mutationFn: (input: unknown) => stub.invoke(input),\n },\n queryClient,\n );\n}\n\nfunction mockMutationMember(context: MockContext, target: MethodTarget): object {\n const stub = context.stub(target);\n return withStub(\n {\n useMutation: (options?: MethodMutationOptions<unknown, unknown>) =>\n useMockMutation(context.queryClient, target, stub, options),\n call: (input?: unknown): Promise<unknown> => stub.invoke(input),\n },\n stub,\n );\n}\n\n/** The cache of a mock client: retries off, results kept fresh. */\nfunction createMockQueryClient(): QueryClient {\n return new QueryClient({\n defaultOptions: {\n queries: { retry: false, staleTime: Number.POSITIVE_INFINITY, refetchOnWindowFocus: false },\n mutations: { retry: false },\n },\n });\n}\n\n/**\n * Fetches the cached results of a query again with its new answer. Reads\n * still waiting on the old one are cancelled first: TanStack keeps a read\n * in flight for a query that has no data yet, and an unanswered read never\n * ends.\n */\nasync function refetchWithNewAnswer(queryClient: QueryClient, queryKey: QueryKey): Promise<void> {\n await queryClient.cancelQueries({ queryKey });\n await queryClient.invalidateQueries({ queryKey });\n}\n\n/** A new stub per method, fetching a query's cached results again when its answer changes. */\nfunction stubMaker(queryClient: QueryClient, stubs: Stub[]): (target: MethodTarget) => Stub {\n return (target) => {\n const queryKey: QueryKey = methodKeyPrefix(target.service, target.method);\n const stub = createStub(() => {\n if (target.kind === \"query\") {\n void refetchWithNewAnswer(queryClient, queryKey);\n }\n });\n stubs.push(stub);\n return stub;\n };\n}\n\n/**\n * Forgets everything set on a mock: answers, calls, rows, scopes and cached\n * results. `quiet` tells no mounted hook, for the reset after a test, which\n * may run before Testing Library unmounts what the test rendered.\n */\nfunction resetMock(\n queryClient: QueryClient,\n store: ReturnType<typeof createMockStore>,\n stubs: readonly Stub[],\n quiet: boolean,\n): void {\n queryClient.clear();\n store.clear(quiet);\n for (const stub of stubs) {\n if (quiet) {\n stub.forget();\n } else {\n stub.mockReset();\n }\n }\n}\n\n/**\n * Runs `reset` after each test when the test runner has a global\n * `afterEach` (vitest with `globals: true`, jest), as Testing Library\n * registers its cleanup. A runner that takes no hook where the mock is made\n * (inside a test) leaves it to `$reset()`.\n */\nfunction resetAfterEachTest(reset: () => void): void {\n const runnerAfterEach: unknown = (globalThis as { readonly afterEach?: unknown }).afterEach;\n if (typeof runnerAfterEach !== \"function\") {\n return;\n }\n try {\n (runnerAfterEach as (hook: () => void) => void)(reset);\n } catch {\n // No hook can be registered here; `$reset()` still forgets everything.\n }\n}\n\n/**\n * Creates a mock client of `contracts`: the type of\n * `createQuickdrawClient(contracts)`, with every member a stub and no\n * transport behind it, for component tests and stories. A call stays\n * pending until the test sets its answer; a row or a scope stays loading\n * until the test sets it. Its members need no provider; components that read\n * `useQuickdraw()` or `usePresence(room)` render inside `$Provider`, which\n * shows the mock's session (`$session`). Everything set is forgotten after\n * each test (see `resetAfterEach`).\n *\n * @example\n * const qd = createMockClient({ task }, { session: { userId: \"ada\" } });\n * vi.mock(\"../lib/qd\", () => ({ qd }));\n * qd.task.get.mockResolvedValue({ id: \"t1\", title: \"Write the spec\" });\n * qd.task.useEntity.mockRow({ id: \"t1\", title: \"Write the spec\" });\n * qd.task.byProject.mockScope(\"p1\", [{ id: \"t1\", title: \"Write the spec\" }]);\n * qd.$session({ serviceAccess: { taskService: \"Admin\" } });\n * render(<TaskCard id=\"t1\" />, { wrapper: qd.$Provider });\n * expect(qd.task.get.calls).toEqual([{ id: \"t1\" }]);\n */\nexport function createMockClient<const Contracts extends ContractMap>(\n contracts: Contracts & { readonly invalidate?: never },\n options: MockClientOptions = {},\n): MockClient<Contracts> {\n const queryClient = options.queryClient ?? createMockQueryClient();\n const base = startingSession(options);\n const store = createMockStore(base);\n const session = mockSessionControls(store, queryClient, base);\n const binding = createBinding();\n binding.coordinator = session.coordinator;\n const stubs: Stub[] = [];\n const context: MockContext = { queryClient, binding, stub: stubMaker(queryClient, stubs) };\n const client = buildCaller(\n \"createMockClient\",\n contracts,\n (target, definition, contract) =>\n target.kind === \"query\"\n ? mockQueryMember(context, target, (invoke) =>\n mockSearchMember(queryClient, invoke, target, definition, contract),\n )\n : mockMutationMember(context, target),\n [\"invalidate\"],\n mockLiveMembers(store, queryClient),\n );\n Object.defineProperties(client, {\n invalidate: { value: invalidateWith(binding) },\n $queryClient: { value: queryClient },\n $Provider: { value: session.Provider },\n $session: { value: session.setSession },\n $presence: { value: session.setPresence },\n $reset: {\n value: (): void => {\n resetMock(queryClient, store, stubs, false);\n },\n },\n });\n if (options.resetAfterEach !== false) {\n resetAfterEachTest(() => {\n resetMock(queryClient, store, stubs, true);\n });\n }\n return Object.freeze(client) as MockClient<Contracts>;\n}\n","// The live members of a mock client (`mockClient.ts`): `useEntity`,\n// `useEntities` and each collection's `useCollection`, showing what the test\n// set through their controls (`mockRow`, `mockScope`, ...) instead of\n// subscribing over a socket, and the stream, channel and event members\n// (`mockRealtime.ts`). They return what the real hooks return\n// (`client/live/useEntity.ts`, `client/live/useCollection.ts`): a row or a\n// scope nobody set is loading, a view runs the contract's predicate over the\n// index rows made from the items, and a component re-renders when the test\n// changes what it shows.\n\nimport { useSyncExternalStore } from \"react\";\nimport type {\n UseEntitiesResult,\n UseEntityOptions,\n UseEntityResult,\n} from \"../client/live/memberTypes\";\nimport type { AnyContract } from \"../contract/defineContract\";\nimport type { QuickdrawError } from \"../protocol/errors\";\nimport { mockAdminNamespace } from \"./mockAdmin\";\nimport { mockCollectionMember } from \"./mockCollections\";\nimport { mockRealtimeMembers } from \"./mockRealtime\";\nimport type { EntityMock, MockScope, SessionState } from \"./mockTypes\";\n\n/** What one mocked row shows: the row, its removal, or an error. */\ntype RowState =\n | { readonly row: { readonly id: string } }\n | { readonly removed: true }\n | { readonly error: QuickdrawError };\n\n/** What one mocked scope shows: its items, or an error. */\nexport type ScopeState =\n | (MockScope & { readonly items: readonly { readonly id: string }[] })\n | { readonly error: QuickdrawError };\n\n/** The rows, scopes and session of one mock client, and the hooks' views of them. */\nexport interface MockStore {\n readonly subscribe: (listener: () => void) => () => void;\n /** The value `build` makes for `key`, the same object until the store changes. */\n read<T>(key: string, build: () => T): T;\n row(service: string, id: string): RowState | undefined;\n setRow(service: string, id: string, state: RowState): void;\n scope(service: string, collection: string, scope: string): ScopeState | undefined;\n setScope(service: string, collection: string, scope: string, state: ScopeState): void;\n /**\n * A mocked value by key: a feed's items, a channel's sent messages\n * (`mockRealtime.ts`), a room's users (`mockSession.tsx`).\n */\n value(key: string): unknown;\n setValue(key: string, value: unknown): void;\n /** Who the mock acts for (`mockSession.tsx`): the same object until it is set again. */\n session(): SessionState;\n setSession(session: SessionState): void;\n /**\n * Forgets every row, scope and value, and puts back the session the mock\n * started with; `quiet` tells no hook that shows them (they are about to\n * unmount).\n */\n clear(quiet?: boolean): void;\n}\n\nfunction keyOf(...parts: readonly string[]): string {\n return parts.join(\"\\u0000\");\n}\n\n/** Creates the store of one mock client, whose session starts as `base`. */\nexport function createMockStore(base: SessionState): MockStore {\n let session = base;\n const rows = new Map<string, RowState>();\n const scopes = new Map<string, ScopeState>();\n const values = new Map<string, unknown>();\n const views = new Map<string, unknown>();\n const listeners = new Set<() => void>();\n const changed = (): void => {\n views.clear();\n for (const listener of [...listeners]) {\n listener();\n }\n };\n return {\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n read<T>(key: string, build: () => T): T {\n if (!views.has(key)) {\n views.set(key, build());\n }\n return views.get(key) as T;\n },\n row: (service, id) => rows.get(keyOf(service, id)),\n setRow(service, id, state) {\n rows.set(keyOf(service, id), state);\n changed();\n },\n scope: (service, collection, scope) => scopes.get(keyOf(service, collection, scope)),\n setScope(service, collection, scope, state) {\n scopes.set(keyOf(service, collection, scope), state);\n changed();\n },\n value: (key) => values.get(key),\n setValue(key, value) {\n values.set(key, value);\n changed();\n },\n session: () => session,\n setSession(next) {\n session = next;\n changed();\n },\n clear(quiet = false) {\n rows.clear();\n scopes.clear();\n values.clear();\n session = base;\n if (quiet) {\n views.clear();\n } else {\n changed();\n }\n },\n };\n}\n\n/** What `useEntity` shows of a mocked row. */\nfunction rowResult(state: RowState | undefined, active: boolean): UseEntityResult<unknown> {\n if (state === undefined) {\n return { data: undefined, isLoading: active, isRemoved: false, error: null };\n }\n if (\"row\" in state) {\n return { data: state.row, isLoading: false, isRemoved: false, error: null };\n }\n if (\"removed\" in state) {\n return { data: undefined, isLoading: false, isRemoved: true, error: null };\n }\n return { data: undefined, isLoading: false, isRemoved: false, error: state.error };\n}\n\n/** What `useEntities` shows of the mocked rows `ids`. */\nfunction rowsResult(\n store: MockStore,\n service: string,\n ids: readonly string[],\n active: boolean,\n): UseEntitiesResult<unknown> {\n const data: unknown[] = [];\n const byId = new Map<string, unknown>();\n const errors = new Map<string, QuickdrawError>();\n let isLoading = false;\n for (const id of ids) {\n const shows = active && id !== \"\";\n const shown = rowResult(shows ? store.row(service, id) : undefined, shows);\n data.push(shown.data);\n if (shown.data !== undefined) {\n byId.set(id, shown.data);\n }\n if (shown.error !== null) {\n errors.set(id, shown.error);\n }\n isLoading ||= shown.isLoading;\n }\n return { data, byId, isLoading, error: errors.values().next().value ?? null, errors };\n}\n\nfunction useMockRow(\n store: MockStore,\n service: string,\n id: string | null | undefined,\n options: UseEntityOptions = {},\n): UseEntityResult<unknown> {\n const rowId = typeof id === \"string\" ? id : \"\";\n const active = options.enabled !== false && rowId !== \"\";\n const read = (): UseEntityResult<unknown> =>\n store.read(keyOf(\"row\", service, rowId, String(active)), () =>\n rowResult(active ? store.row(service, rowId) : undefined, active),\n );\n return useSyncExternalStore(store.subscribe, read, read);\n}\n\nfunction useMockRows(\n store: MockStore,\n service: string,\n ids: readonly string[],\n options: UseEntityOptions = {},\n): UseEntitiesResult<unknown> {\n const rowIds = ids.map((id) => (typeof id === \"string\" ? id : \"\"));\n const active = options.enabled !== false;\n const read = (): UseEntitiesResult<unknown> =>\n store.read(keyOf(\"rows\", service, String(active), ...rowIds), () =>\n rowsResult(store, service, rowIds, active),\n );\n return useSyncExternalStore(store.subscribe, read, read);\n}\n\n/** The id of a row the test passed to `mockRow`. */\nfunction idOf(row: unknown): string {\n const id: unknown = typeof row === \"object\" && row !== null ? (row as { id?: unknown }).id : \"\";\n if (typeof id !== \"string\" || id === \"\") {\n throw new TypeError(\"mockRow: the row needs a string id\");\n }\n return id;\n}\n\n/** The controls of a mocked service's rows. */\nfunction rowControls(store: MockStore, service: string): EntityMock<unknown> {\n return Object.freeze({\n mockRow(row: unknown) {\n store.setRow(service, idOf(row), { row: row as { readonly id: string } });\n },\n mockRemoved(id: string) {\n store.setRow(service, id, { removed: true });\n },\n mockError(id: string, error: QuickdrawError) {\n store.setRow(service, id, { error });\n },\n });\n}\n\n/**\n * The live members of each contract's service on a mock client, as\n * `buildCaller` takes them: the entity hooks for a contract with an entity,\n * each carrying the controls of the rows it shows, one member per\n * collection, and `admin` for a contract with the admin kit (its `adminMeta`\n * queries run on `queryClient`, the mock's cache).\n */\nexport function mockLiveMembers(\n store: MockStore,\n queryClient: Parameters<typeof mockAdminNamespace>[2],\n): (\n contract: AnyContract,\n methods: Readonly<Record<string, object>>,\n) => Readonly<Record<string, object>> {\n return (contract, methods) => {\n const service = contract.name;\n const members: [string, object][] = [];\n if (contract.entity !== undefined) {\n const controls = rowControls(store, service);\n const useEntity = (id: string | null | undefined, options?: UseEntityOptions) =>\n useMockRow(store, service, id, options);\n const useEntities = (ids: readonly string[], options?: UseEntityOptions) =>\n useMockRows(store, service, ids, options);\n members.push([\"useEntity\", Object.freeze(Object.assign(useEntity, controls))]);\n members.push([\"useEntities\", Object.freeze(Object.assign(useEntities, controls))]);\n }\n for (const [collection, def] of Object.entries(contract.collections)) {\n members.push([collection, mockCollectionMember(store, { service, collection, def })]);\n }\n members.push(...mockRealtimeMembers(store, contract));\n members.push(...Object.entries(mockAdminNamespace(contract, methods, queryClient, store)));\n return Object.freeze(Object.fromEntries(members));\n };\n}\n","// The mock client's `qd.<service>.admin` (`mockClient.ts`): the same\n// namespace as the typed client's (`../client/admin.ts`), holding the mocked\n// members of the admin methods, so `qd.task.admin.adminList.mockResolvedValue`\n// sets the same stub as `qd.task.adminList`. `useAdminServices` on a mock\n// client asks each `adminMeta` stub the session's grants allow (`$session`,\n// `serviceAccess`) at once, on the mock's cache, so a component test lists\n// services with `adminMeta.mockResolvedValue(meta)`.\n\nimport { useQueries, type QueryClient, type UseQueryResult } from \"@tanstack/react-query\";\nimport { useSyncExternalStore } from \"react\";\nimport {\n adminNamespace,\n type AdminMetaQuery,\n type AdminMetaState,\n type UseAdminGrants,\n type UseAdminMeta,\n} from \"../client/admin\";\nimport { methodKey } from \"../client/keys\";\nimport type { AnyContract } from \"../contract/defineContract\";\nimport type { MockStore } from \"./mockLive\";\nimport { sessionIn, useSessionScope } from \"./mockScope\";\n\nfunction statesOf(results: UseQueryResult<unknown>[]): AdminMetaState[] {\n return results.map((result) => ({\n data: result.data as AdminMetaState[\"data\"],\n error: result.error,\n }));\n}\n\n/** The mock client's `admin` member of a contract's service, on the mock's cache, with its session's grants. */\nexport function mockAdminNamespace(\n contract: AnyContract,\n methods: Readonly<Record<string, object>>,\n queryClient: QueryClient,\n store: MockStore,\n): Readonly<Record<string, object>> {\n const useMockAdminMeta: UseAdminMeta = (queries: readonly AdminMetaQuery[], enabled: boolean) =>\n useQueries(\n {\n queries: queries.map((query) => ({\n queryKey: methodKey(query.serviceName, query.method, undefined),\n queryFn: () => query.member.call(),\n enabled,\n retry: false,\n })),\n combine: statesOf,\n },\n queryClient,\n );\n const useMockGrants: UseAdminGrants = () => {\n // Under a `$Provider` with a `session` prop, that session's grants (`mockScope.ts`).\n const scope = useSessionScope(store);\n const grants = () => {\n const session = sessionIn(store, scope);\n return session.isKnown ? session.serviceAccess : null;\n };\n return useSyncExternalStore(store.subscribe, grants, grants);\n };\n return adminNamespace(contract, methods, { useMeta: useMockAdminMeta, useGrants: useMockGrants });\n}\n","// The session `<mock.$Provider session={...}>` gives its subtree (finding\n// F6.2 of the quickdraw-chat migration): a Storybook docs page renders every\n// story at once, each inside the mock's provider, and `$session(...)` is one\n// session for the whole mock, so every story showed the last one set. The\n// prop is laid over the mock's own session (`$session`) field by field for\n// what renders inside that provider: the real `useQuickdraw()` and\n// `usePresence(room)` read it through the provider's connection\n// (`mockSession.tsx`), and the mock's own hooks that depend on who the user\n// is (collection views, the admin grants) read it from this context.\n\nimport { createContext, useContext } from \"react\";\nimport type { MockStore } from \"./mockLive\";\nimport type { SessionState } from \"./mockTypes\";\n\n/** Lays a `$Provider`'s `session` prop over the mock's own session: the same object while that one does not change. */\nexport type SessionScope = (session: SessionState) => SessionState;\n\n/** The scope a `$Provider` with a `session` prop set, for one mock's store. */\nexport interface ScopedSession {\n readonly store: MockStore;\n readonly scope: SessionScope;\n}\n\n/** Where a `$Provider` with a `session` prop puts its scope; `undefined` elsewhere. */\nexport const MockSessionScope = createContext<ScopedSession | undefined>(undefined);\n\n/** The scope that applies to `store` where this renders: one of its own providers' `session` prop. */\nexport function useSessionScope(store: MockStore): SessionScope | undefined {\n const scoped = useContext(MockSessionScope);\n return scoped?.store === store ? scoped.scope : undefined;\n}\n\n/** The session `store` shows under `scope`: its own, or the prop over it. */\nexport function sessionIn(store: MockStore, scope: SessionScope | undefined): SessionState {\n const session = store.session();\n return scope === undefined ? session : scope(session);\n}\n","// The collections of a mock client (`mockLive.ts`): each collection's\n// `useCollection`, showing the scope the test set (`mockScope`) instead of\n// subscribing over a socket, as the real hook returns it\n// (`client/live/useCollection.ts`): a scope nobody set is loading, and a\n// view runs the contract's predicate over the index rows made from the\n// items, for the session's user, or under a `$Provider` with a `session`\n// prop for that session's (`mockScope.ts`).\n\nimport { useSyncExternalStore } from \"react\";\nimport { indexRowFromItem, type IndexRow } from \"../client/live/collectionIndex\";\nimport type { UseCollectionOptions, UseCollectionResult } from \"../client/live/memberTypes\";\nimport { viewPredicate } from \"../client/live/views\";\nimport type { CollectionDef, Viewer } from \"../contract/collections\";\nimport type { QuickdrawError } from \"../protocol/errors\";\nimport type { MockStore, ScopeState } from \"./mockLive\";\nimport { sessionIn, useSessionScope } from \"./mockScope\";\nimport type { MockScope, SessionState } from \"./mockTypes\";\n\nfunction keyOf(...parts: readonly string[]): string {\n return parts.join(\"\\u0000\");\n}\n\nconst idle = (): Promise<void> => Promise.resolve();\n\n/** A mock shows no optimistic additions, so none is ever pending. */\nconst NONE_PENDING: ReadonlySet<string> = new Set();\n\n/** A mocked scope with nothing in it yet. */\nfunction emptyScope(active: boolean): UseCollectionResult<unknown, IndexRow> {\n return {\n items: [],\n pending: NONE_PENDING,\n checking: NONE_PENDING,\n // A mock's mutations add nothing, so nothing is refused either.\n refused: [],\n index: undefined,\n byId: new Map(),\n totalCount: null,\n hasMore: false,\n clamped: false,\n indexTruncated: false,\n isLoading: active,\n isLoadingMore: false,\n error: null,\n loadMore: idle,\n loadItems: idle,\n refresh: idle,\n };\n}\n\n/** What `useCollection` shows of a mocked scope, through `view` for `who`. */\nfunction scopeResult(\n def: CollectionDef,\n state: ScopeState | undefined,\n options: { readonly view: string | undefined; readonly who: Viewer; readonly active: boolean },\n): UseCollectionResult<unknown, IndexRow> {\n const empty = emptyScope(options.active);\n if (state === undefined) {\n return empty;\n }\n if (\"error\" in state) {\n return { ...empty, isLoading: false, error: state.error };\n }\n const selects = viewPredicate(def, options.view);\n const keep = (row: Readonly<Record<string, unknown>>): boolean =>\n selects === undefined || selects(row, options.who);\n const index =\n def.index === undefined\n ? undefined\n : state.items.map((item) => indexRowFromItem(def, item)).filter(keep);\n const members = new Set(index?.map((row) => row.id));\n const items = state.items.filter((item) =>\n index === undefined ? keep(item as Readonly<Record<string, unknown>>) : members.has(item.id),\n );\n return {\n ...empty,\n items,\n index,\n byId: new Map(items.map((item) => [item.id, item])),\n totalCount: state.totalCount ?? state.items.length,\n hasMore: state.hasMore ?? false,\n isLoading: false,\n };\n}\n\n/** One collection of a contract, as its mock member shows it. */\ninterface MockCollectionTarget {\n readonly service: string;\n readonly collection: string;\n readonly def: CollectionDef;\n}\n\n/**\n * Who the views select members for: the session's user once known, as the\n * real views read the hello; under a `$Provider` with a `session` prop,\n * that session's (`mockScope.ts`).\n */\nfunction viewerOf(session: SessionState): Viewer {\n return { userId: session.isKnown ? (session.userId ?? \"\") : \"\" };\n}\n\nfunction useMockScope(\n store: MockStore,\n target: MockCollectionTarget,\n scope: string | null | undefined,\n options: UseCollectionOptions = {},\n): UseCollectionResult<unknown, IndexRow> {\n const value = typeof scope === \"string\" ? scope : \"\";\n const active = options.enabled !== false && value !== \"\";\n const { service, collection, def } = target;\n const sessionScope = useSessionScope(store);\n const read = (): UseCollectionResult<unknown, IndexRow> => {\n const who = viewerOf(sessionIn(store, sessionScope));\n // The viewer is part of the key: two providers' sessions may see one scope's views apart.\n const view = options.view ?? \"\";\n const key = keyOf(\"scope\", service, collection, value, view, String(active), who.userId);\n return store.read(key, () =>\n scopeResult(def, active ? store.scope(service, collection, value) : undefined, {\n view: options.view,\n who,\n active,\n }),\n );\n };\n return useSyncExternalStore(store.subscribe, read, read);\n}\n\n/** `qd.<service>.<collection>` of a mock client. */\nexport function mockCollectionMember(store: MockStore, target: MockCollectionTarget): object {\n return Object.freeze({\n useCollection: (scope: string | null | undefined, options?: UseCollectionOptions) =>\n useMockScope(store, target, scope, options),\n mockScope(scope: string, items: readonly { readonly id: string }[], extra: MockScope = {}) {\n store.setScope(target.service, target.collection, scope, { ...extra, items: [...items] });\n },\n mockError(scope: string, error: QuickdrawError) {\n store.setScope(target.service, target.collection, scope, { error });\n },\n });\n}\n","// The stream, channel and event members of a mock client (`mockClient.ts`,\n// RFC 0003 section 12.5), with no socket behind them:\n//\n// - a stream's `useStream` shows what the test set with `mockItems` (the\n// latest `max` of them) or `mockError`; a feed nobody set is loading;\n// - a channel's `useChannel` is always ready, and its `send` records each\n// payload in the member's `sent`;\n// - an event's `useEvent` registers its handler, and `mockEmit(payload)`\n// calls every mounted one (wrap it in Testing Library's `act`).\n//\n// What the test set is forgotten with the rest of the mock (`$reset`, or\n// after each test).\n\nimport { useEffect, useRef, useSyncExternalStore } from \"react\";\nimport { streamMaxOf, type StreamState } from \"../client/live/streams\";\nimport type { UseEventOptions } from \"../client/live/useEvent\";\nimport type { UseStreamOptions, UseStreamResult } from \"../client/live/useStream\";\nimport type { AnyContract } from \"../contract/defineContract\";\nimport { isScopedStream } from \"../contract/realtime\";\nimport type { QuickdrawError } from \"../protocol/errors\";\nimport type { MockStore } from \"./mockLive\";\n\n/** One feed as the test set it: its items, or why it failed. */\ntype FeedState = { readonly items: readonly unknown[] } | { readonly error: QuickdrawError };\n\nfunction keyOf(...parts: readonly string[]): string {\n return parts.join(\"\\u0000\");\n}\n\n/** What `useStream` shows of a mocked feed. */\nfunction feedResult(state: unknown, active: boolean, max: number): StreamState {\n if (!active || state === undefined) {\n return { items: [], isLoading: active, error: null };\n }\n const feed = state as FeedState;\n if (\"error\" in feed) {\n return { items: [], isLoading: false, error: feed.error };\n }\n const items = feed.items.length > max ? feed.items.slice(feed.items.length - max) : feed.items;\n return { items, isLoading: false, error: null };\n}\n\n/** `qd.<service>.<stream>` of a mock client. */\nfunction mockStreamMember(\n store: MockStore,\n service: string,\n stream: string,\n scoped: boolean,\n): object {\n const feedKey = (scope: unknown): string =>\n keyOf(\"stream\", service, stream, scoped && typeof scope === \"string\" ? scope : \"\");\n const useMockStream = (first?: unknown, second?: UseStreamOptions): UseStreamResult<unknown> => {\n const scope = scoped ? first : undefined;\n const options = (scoped ? second : (first as UseStreamOptions | undefined)) ?? {};\n const active =\n options.enabled !== false && (!scoped || (typeof scope === \"string\" && scope !== \"\"));\n const max = streamMaxOf(options.max);\n const read = (): StreamState =>\n store.read(keyOf(\"view\", feedKey(scope), String(active), String(max)), () =>\n feedResult(store.value(feedKey(scope)), active, max),\n );\n return useSyncExternalStore(store.subscribe, read, read);\n };\n // `(scope, value)` for a scoped stream, `(value)` for a global one.\n const set = (args: readonly unknown[], value: (last: unknown) => FeedState): void => {\n store.setValue(feedKey(scoped ? args[0] : undefined), value(args.at(-1)));\n };\n return Object.freeze({\n useStream: useMockStream,\n mockItems: (...args: unknown[]) => {\n set(args, (items) => ({ items: [...(items as readonly unknown[])] }));\n },\n mockError: (...args: unknown[]) => {\n set(args, (error) => ({ error: error as QuickdrawError }));\n },\n });\n}\n\n/** `qd.<service>.<channel>` of a mock client. */\nfunction mockChannelMember(store: MockStore, service: string, channel: string): object {\n const sentKey = keyOf(\"sent\", service, channel);\n const sent = (): readonly unknown[] =>\n (store.value(sentKey) as readonly unknown[] | undefined) ?? [];\n const result = Object.freeze({\n send: (payload: unknown) => {\n store.setValue(sentKey, [...sent(), payload]);\n },\n isReady: true,\n });\n return Object.freeze(\n Object.defineProperties(\n { useChannel: () => result },\n { sent: { get: () => [...sent()], enumerable: true } },\n ),\n );\n}\n\n/** `qd.<service>.<event>` of a mock client. */\nfunction mockEventMember(): object {\n const handlers = new Set<(payload: unknown) => void>();\n return Object.freeze({\n useEvent: (handler: (payload: unknown) => void, options: UseEventOptions = {}) => {\n const latest = useRef(handler);\n useEffect(() => {\n latest.current = handler;\n });\n const enabled = options.enabled !== false;\n useEffect(() => {\n if (!enabled) {\n return undefined;\n }\n const entry = (payload: unknown): void => {\n latest.current(payload);\n };\n handlers.add(entry);\n return () => {\n handlers.delete(entry);\n };\n }, [enabled]);\n },\n mockEmit: (payload: unknown) => {\n for (const handler of [...handlers]) {\n handler(payload);\n }\n },\n });\n}\n\n/** The stream, channel and event members of one contract's service on a mock client. */\nexport function mockRealtimeMembers(store: MockStore, contract: AnyContract): [string, object][] {\n const service = contract.name;\n return [\n ...Object.entries(contract.streams).map(([stream, def]): [string, object] => [\n stream,\n mockStreamMember(store, service, stream, isScopedStream(def)),\n ]),\n ...Object.keys(contract.channels).map((channel): [string, object] => [\n channel,\n mockChannelMember(store, service, channel),\n ]),\n ...Object.keys(contract.events).map((event): [string, object] => [event, mockEventMember()]),\n ];\n}\n","// The mock client's `useSearch` (`mockClient.ts`), on the member of a method\n// the search kit made: the method's stub answers each query at once, with no\n// debounce, server or collection cache, so a component test sets the results\n// it shows with `mockResolvedValue({ items, nextCursor })` and reads what was\n// searched for in `calls`.\n\nimport { useQuery, type QueryClient } from \"@tanstack/react-query\";\nimport { methodKey } from \"../client/keys\";\nimport { searchCall, searchTargetOf, type SearchTarget } from \"../client/live/searchResults\";\nimport type { UseSearchOptions, UseSearchResult } from \"../client/live/searchTypes\";\nimport type { MethodTarget } from \"../client/members\";\nimport type { AnyContract } from \"../contract/defineContract\";\nimport type { SearchPage } from \"../contract/kits/searchSchemas\";\nimport type { MethodDef } from \"../contract/methods\";\nimport type { QuickdrawError } from \"../protocol/errors\";\n\n/** Answers one call of the stubbed method. */\ntype Invoke = (input: unknown) => Promise<unknown>;\n\nconst NO_RESULTS: readonly never[] = Object.freeze([]);\n\nfunction useMockSearch(\n queryClient: QueryClient,\n invoke: Invoke,\n target: SearchTarget,\n q: string,\n options: UseSearchOptions,\n): UseSearchResult<unknown> {\n const typed = typeof q === \"string\" ? q.trim() : \"\";\n const scope = typeof options.scope === \"string\" ? options.scope : \"\";\n const input = searchCall(typed, scope, options.limit);\n const active = options.enabled !== false && typed.length >= target.minLength;\n const result = useQuery(\n {\n queryKey: methodKey(target.service, target.method, input),\n queryFn: () => invoke(input) as Promise<SearchPage<unknown>>,\n enabled: active,\n retry: false,\n },\n queryClient,\n );\n const page = active ? result.data : undefined;\n return {\n items: page?.items ?? NO_RESULTS,\n hasMore: (page?.nextCursor ?? null) !== null,\n isSearching: active && result.isFetching,\n isLoading: active && result.isFetching && page === undefined,\n error: active ? (result.error as QuickdrawError | null) : null,\n };\n}\n\n/** The mock `useSearch` a query member gets when the search kit made its method; nothing otherwise. */\nexport function mockSearchMember(\n queryClient: QueryClient,\n invoke: Invoke,\n target: MethodTarget,\n definition: MethodDef,\n contract: AnyContract,\n): Readonly<Record<string, unknown>> {\n const search = searchTargetOf(target, definition, contract);\n if (search === undefined) {\n return {};\n }\n return {\n useSearch: (q: string, options: UseSearchOptions = {}) =>\n useMockSearch(queryClient, invoke, search, q, options),\n };\n}\n","// The session of a mock client (`mockClient.ts`, RFC 0003 section 13): who\n// the mock acts for, as the real `useQuickdraw()` and `usePresence(room)`\n// read it under the mock's provider (`mock.$Provider`), with no server and\n// no socket. 4.1 had nothing of the kind, and an app on the first 5.0\n// candidates re-exported `useQuickdraw` from the module it mocked, so that a\n// story or a test could fake the connection state (finding F3.1).\n//\n// - The provider fills the same context `QuickdrawProvider` fills, with a\n// connection that never opens: its state is made from the session (a\n// hello naming `userId` with `serviceAccess`, connected or connecting,\n// known or not), and every hook that reads the context reads it, on a\n// server too (`renderOnServerAs`).\n// - `usePresence(room)` shows the users `$presence(room, users)` set, while\n// the session is known (a socket is in no room before its hello).\n// - The views of the mock's collections select members for the session's\n// user, as the real ones do for the user the hello names.\n// - `<mock.$Provider session={...}>` lays its `session` over the mock's for\n// what renders inside it (finding F6.2): its own connection shows that\n// session, and the mock's hooks read it from `mockScope.ts`, so stories\n// rendered side by side (a Storybook docs page) each show their own.\n//\n// Imports nothing of Testing Library, so a browser bundle (Storybook) can\n// take it: `./testing/mock` is its own entry.\n\nimport { QueryClientProvider, type QueryClient } from \"@tanstack/react-query\";\nimport { useMemo, type ReactElement, type ReactNode } from \"react\";\nimport {\n createQuickdrawConnection,\n type ConnectionState,\n type QuickdrawConnection,\n} from \"../client/connection\";\nimport { QuickdrawContext, renderOnServerAs, type QuickdrawContextValue } from \"../client/context\";\nimport { createInvalidationCoordinator, type InvalidationCoordinator } from \"../client/coordinator\";\nimport { inertLiveData } from \"../client/live/liveData\";\nimport type { PresenceStore } from \"../client/live/presence\";\nimport {\n MAX_ENTITY_IDS_PER_SOCKET,\n MAX_SCOPES_PER_SOCKET,\n MAX_SUBSCRIBE_IDS,\n MAX_TOPICS_PER_SOCKET,\n PROTOCOL_VERSION,\n type HelloFrame,\n} from \"../protocol/version\";\nimport { QUICKDRAW_VERSION } from \"../version\";\nimport type { MockStore } from \"./mockLive\";\nimport { MockSessionScope, type ScopedSession, type SessionScope } from \"./mockScope\";\nimport type { MockClientOptions, MockSession, SessionState } from \"./mockTypes\";\n\n/** The URL of a mock's connection, which never opens. */\nconst MOCK_URL = \"http://quickdraw-mock.invalid\";\n\nconst NOBODY: readonly string[] = Object.freeze([]);\n\n/** The limits a mock's hello announces: the server's defaults. */\nconst MOCK_LIMITS: HelloFrame[\"limits\"] = Object.freeze({\n maxInFlightQueries: 16,\n maxQueuedQueries: 64,\n maxSubscribeIds: MAX_SUBSCRIBE_IDS,\n callTimeoutMs: 30_000,\n subscriptions: Object.freeze({\n maxInFlight: 8,\n maxQueued: 64,\n maxEntityIds: MAX_ENTITY_IDS_PER_SOCKET,\n maxScopes: MAX_SCOPES_PER_SOCKET,\n maxTopics: MAX_TOPICS_PER_SOCKET,\n }),\n});\n\nfunction checkUser(owner: string, userId: unknown): string | null | undefined {\n if (userId === undefined || userId === null || (typeof userId === \"string\" && userId !== \"\")) {\n return userId;\n }\n throw new TypeError(`${owner}: userId must be a non-empty string, or null for an anonymous user`);\n}\n\nfunction checkFlag(owner: string, name: string, value: unknown): boolean | undefined {\n if (value === undefined || typeof value === \"boolean\") {\n return value;\n }\n throw new TypeError(`${owner}: ${name} must be true or false`);\n}\n\n/**\n * `session` over `base`: what it sets replaces what `base` has, field by\n * field; a field it leaves out (or sets to `undefined`) keeps `base`'s.\n * Throws a `TypeError` naming `owner` for a field of the wrong kind.\n */\nexport function sessionOver(\n owner: string,\n base: SessionState,\n session: MockSession | undefined,\n): SessionState {\n if (session === undefined) {\n return base;\n }\n if (typeof session !== \"object\" || session === null) {\n throw new TypeError(\n `${owner}: the session is { userId?, serviceAccess?, isConnected?, isKnown? }`,\n );\n }\n const { serviceAccess } = session;\n if (\n serviceAccess !== undefined &&\n (typeof serviceAccess !== \"object\" || serviceAccess === null)\n ) {\n throw new TypeError(`${owner}: serviceAccess maps service names to access levels`);\n }\n const userId = checkUser(owner, session.userId);\n const isConnected = checkFlag(owner, \"isConnected\", session.isConnected);\n const isKnown = checkFlag(owner, \"isKnown\", session.isKnown);\n return Object.freeze({\n userId: userId === undefined ? base.userId : userId,\n serviceAccess: Object.freeze({ ...(serviceAccess ?? base.serviceAccess) }),\n isConnected: isConnected ?? base.isConnected,\n isKnown: isKnown ?? base.isKnown,\n });\n}\n\n/** The id a mock's hello names its server by: one mock server, never restarted. */\nconst MOCK_SERVER_ID = \"mock\";\n\n/** The connection state a session shows: connected or connecting, with a hello once it is known. */\nfunction stateOf(session: SessionState): ConnectionState {\n const hello: HelloFrame | null = session.isKnown\n ? Object.freeze({\n protocol: PROTOCOL_VERSION,\n server: QUICKDRAW_VERSION,\n serverId: MOCK_SERVER_ID,\n limits: MOCK_LIMITS,\n features: Object.freeze([]),\n userId: session.userId,\n serviceAccess: session.serviceAccess,\n })\n : null;\n return Object.freeze({\n status: session.isConnected ? \"connected\" : \"connecting\",\n reconnecting: !session.isConnected && session.isKnown,\n hello,\n serviceAccess: hello === null ? null : session.serviceAccess,\n refusal: null,\n backoff: Object.freeze({}),\n });\n}\n\n/**\n * The connection of a mock client: a real connection that is never opened\n * (its socket, lane and topics stay idle, so a real client's hook rendered\n * under the mock's provider by mistake waits rather than throws), whose\n * state comes from the mock's session. Opening, closing and new credentials\n * do nothing.\n */\nfunction mockConnection(store: MockStore, sessionOf: () => SessionState): QuickdrawConnection {\n const inert = createQuickdrawConnection({ url: MOCK_URL, transports: [\"websocket\"] });\n let shown: { readonly session: SessionState; readonly state: ConnectionState } | undefined;\n const getState = (): ConnectionState => {\n const session = sessionOf();\n if (shown?.session !== session) {\n shown = { session, state: stateOf(session) };\n }\n return shown.state;\n };\n const nothing = (): void => undefined;\n return Object.freeze({\n url: inert.url,\n timeoutMs: inert.timeoutMs,\n socket: inert.socket,\n subscriptionLane: inert.subscriptionLane,\n getState,\n subscribe: store.subscribe,\n open: nothing,\n close: nothing,\n retain: () => nothing,\n setAuth: () => false,\n nextCallId: inert.nextCallId,\n reportRateLimited: nothing,\n backoffRemaining: () => 0,\n onReconnect: () => nothing,\n onHello: (listener: (hello: HelloFrame) => void) => mockHellos(getState, store, listener),\n watch: () => nothing,\n waitForJoin: () => undefined,\n });\n}\n\n/**\n * `onHello` on a mock's connection: each session that is connected and\n * known is a hello (`$session(...)` that sets one is a reconnect), the\n * current one in a microtask, as a real connection gives it.\n */\nfunction mockHellos(\n getState: () => ConnectionState,\n store: MockStore,\n listener: (hello: HelloFrame) => void,\n): () => void {\n let last: HelloFrame | null = null;\n let stopped = false;\n const deliver = (): void => {\n const { status, hello } = getState();\n if (!stopped && status === \"connected\" && hello !== null && hello !== last) {\n last = hello;\n listener(hello);\n }\n };\n queueMicrotask(deliver);\n const stop = store.subscribe(deliver);\n return () => {\n stopped = true;\n stop();\n };\n}\n\n/** The key of a room's users in the mock's store. */\nfunction presenceKey(room: string): string {\n return `presence\\u0000${room}`;\n}\n\n/**\n * The presence `usePresence` reads on a mock: the users the test set per\n * room, nobody while the session is not known. No frame reaches it.\n */\nfunction mockPresence(store: MockStore, sessionOf: () => SessionState): PresenceStore {\n return Object.freeze({\n users(room: string): readonly string[] {\n if (!sessionOf().isKnown) {\n return NOBODY;\n }\n return (store.value(presenceKey(room)) as readonly string[] | undefined) ?? NOBODY;\n },\n // Every change of the store: `users` returns the same array until the room's own changes.\n listen: (_room: string, listener: () => void) => store.subscribe(listener),\n receive: () => undefined,\n clear: () => undefined,\n });\n}\n\n/** What a mock client gives its tests besides its members: its provider, session and presence. */\n/** The props of a mock's provider: what it renders, and the session it shows there. */\nexport interface MockProviderProps {\n readonly children?: ReactNode;\n /**\n * The session the subtree shows, laid over the mock's own (`$session`)\n * field by field: what it leaves out follows the mock's. Each provider\n * has its own, so stories rendered side by side show their own sessions.\n */\n readonly session?: MockSession;\n}\n\nexport interface MockSessionControls {\n readonly Provider: (props: MockProviderProps) => ReactElement;\n /** The invalidation coordinator of the mock's cache, which the provider gives the hooks too. */\n readonly coordinator: InvalidationCoordinator;\n setSession(session: MockSession): void;\n setPresence(room: string, users: readonly string[]): void;\n}\n\n/** The session a mock starts with: its `session` option over `userId`, connected and known. */\nexport function startingSession(options: MockClientOptions): SessionState {\n const fallback: SessionState = Object.freeze({\n userId: null,\n serviceAccess: Object.freeze({}),\n isConnected: true,\n isKnown: true,\n });\n const withUser = sessionOver(\n \"createMockClient\",\n fallback,\n options.userId === undefined ? undefined : { userId: options.userId },\n );\n return sessionOver(\"createMockClient\", withUser, options.session);\n}\n\n/**\n * `session` laid over the session of `store` (`$Provider`'s prop): checked\n * at once, and the same object while the mock's own session does not change.\n */\nfunction scopeOf(store: MockStore, session: MockSession): SessionScope {\n sessionOver(\"$Provider\", store.session(), session);\n let last: { readonly base: SessionState; readonly shown: SessionState } | undefined;\n return (base) => {\n if (last?.base !== base) {\n last = { base, shown: sessionOver(\"$Provider\", base, session) };\n }\n return last.shown;\n };\n}\n\n/**\n * The provider, session and presence of one mock client: `base` is the\n * session it starts with and goes back to on a reset; `$session` sets\n * another over it.\n */\nexport function mockSessionControls(\n store: MockStore,\n queryClient: QueryClient,\n base: SessionState,\n): MockSessionControls {\n const coordinator = createInvalidationCoordinator(queryClient);\n /** The context value of a connection showing `sessionOf`'s session. */\n const contextOver = (sessionOf: () => SessionState): QuickdrawContextValue => {\n const connection = mockConnection(store, sessionOf);\n // A server renders the session too, so a story or a test that renders on one hydrates it.\n renderOnServerAs(connection, connection.getState);\n inertLiveData(connection, queryClient, mockPresence(store, sessionOf));\n return Object.freeze({ connection, queryClient, coordinator });\n };\n const value = contextOver(store.session);\n /** A provider's `session` prop: its own connection, and the scope the mock's hooks read. */\n const scopedBy = (key: string): { value: QuickdrawContextValue; scoped: ScopedSession } => {\n const scope = scopeOf(store, JSON.parse(key) as MockSession);\n return {\n value: contextOver(() => scope(store.session())),\n scoped: Object.freeze({ store, scope }),\n };\n };\n function MockQuickdrawProvider({ children, session }: MockProviderProps): ReactElement {\n // By value: a story passes a new object on every render.\n const key = session === undefined ? undefined : JSON.stringify(session);\n const scoped = useMemo(() => (key === undefined ? undefined : scopedBy(key)), [key]);\n return (\n <QueryClientProvider client={queryClient}>\n <QuickdrawContext.Provider value={scoped?.value ?? value}>\n <MockSessionScope.Provider value={scoped?.scoped}>{children}</MockSessionScope.Provider>\n </QuickdrawContext.Provider>\n </QueryClientProvider>\n );\n }\n return Object.freeze({\n Provider: MockQuickdrawProvider,\n coordinator,\n setSession(session: MockSession): void {\n store.setSession(sessionOver(\"$session\", base, session));\n },\n setPresence(room: string, users: readonly string[]): void {\n if (typeof room !== \"string\" || room === \"\") {\n throw new TypeError(\"$presence: room must be a room's name\");\n }\n if (!Array.isArray(users) || !users.every((user) => typeof user === \"string\")) {\n throw new TypeError(\"$presence: users must be a list of user ids\");\n }\n store.setValue(presenceKey(room), Object.freeze([...new Set(users)]));\n },\n });\n}\n"],"mappings":"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","names":["useQuery","useSyncExternalStore","useSyncExternalStore","useSyncExternalStore","useSyncExternalStore","keyOf","useSyncExternalStore","keyOf","useSyncExternalStore","useQuery"]}
|
|
@@ -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":[]}
|
|
@@ -17,6 +17,34 @@ function storageOf(db) {
|
|
|
17
17
|
function modelKey(model) {
|
|
18
18
|
return model.length === 0 ? model : `${model.charAt(0).toLowerCase()}${model.slice(1)}`;
|
|
19
19
|
}
|
|
20
|
+
function isLink(value) {
|
|
21
|
+
const link = value ?? {};
|
|
22
|
+
return typeof link.model === "string" && link.model.length > 0 && typeof link.entry === "string" && link.entry.length > 0 && Array.isArray(link.columns) && link.columns.every((column) => typeof column === "string");
|
|
23
|
+
}
|
|
24
|
+
function createLinkRegistry() {
|
|
25
|
+
const links = /* @__PURE__ */ new Map();
|
|
26
|
+
const register = (model, link) => {
|
|
27
|
+
if (typeof model !== "string" || model.length === 0) {
|
|
28
|
+
throw new TypeError("registerLinks: model must be a model name");
|
|
29
|
+
}
|
|
30
|
+
if (!isLink(link)) {
|
|
31
|
+
throw new TypeError(
|
|
32
|
+
`registerLinks: the link of ${model} needs { model, entry, columns }: the junction model, its column holding the entry's id, and the columns to read`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
const key = modelKey(model);
|
|
36
|
+
const junction = modelKey(link.model);
|
|
37
|
+
const known = links.get(key) ?? [];
|
|
38
|
+
const same = known.find((other) => other.model === junction && other.entry === link.entry);
|
|
39
|
+
const columns = Object.freeze([.../* @__PURE__ */ new Set([...same?.columns ?? [], ...link.columns])]);
|
|
40
|
+
const merged = Object.freeze({ model: junction, entry: link.entry, columns });
|
|
41
|
+
links.set(key, Object.freeze([...known.filter((other) => other !== same), merged]));
|
|
42
|
+
};
|
|
43
|
+
return Object.freeze({
|
|
44
|
+
register,
|
|
45
|
+
of: (model) => links.get(modelKey(model)) ?? []
|
|
46
|
+
});
|
|
47
|
+
}
|
|
20
48
|
function createInterestRegistry(initial = {}) {
|
|
21
49
|
const columns = /* @__PURE__ */ new Map();
|
|
22
50
|
const register = (model, added) => {
|
|
@@ -47,7 +75,8 @@ export {
|
|
|
47
75
|
STORAGE_KEY,
|
|
48
76
|
storageOf,
|
|
49
77
|
modelKey,
|
|
78
|
+
createLinkRegistry,
|
|
50
79
|
createInterestRegistry,
|
|
51
80
|
ANY_FIELD
|
|
52
81
|
};
|
|
53
|
-
//# sourceMappingURL=chunk-
|
|
82
|
+
//# sourceMappingURL=chunk-DUBU3BB3.js.map
|