@mittwald/react-ghostmaker 1.5.0-alpha.1 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/esm/index.js CHANGED
@@ -1,7 +1,7 @@
1
- export { makeGhost } from "./makeGhost";
2
- export { registerModelIdentifier } from "./modelIdentifier";
3
- export { getQueryContext, forwardQueryContext } from "./context";
4
- export { invalidateGhosts } from "./invalidate";
5
- export * from "./maybeGhost";
6
- export { GhostMakerModel, ghostMakerModel } from "./metaData";
7
- export { getModelId, getModelName } from "./queries";
1
+ export { makeGhost } from "./makeGhost.js";
2
+ export { registerModelIdentifier } from "./modelIdentifier.js";
3
+ export { getQueryContext, forwardQueryContext } from "./context.js";
4
+ export { invalidateGhosts } from "./invalidate.js";
5
+ export * from "./maybeGhost/index.js";
6
+ export { GhostMakerModel, ghostMakerModel } from "./metaData.js";
7
+ export { getModelId, getModelName } from "./queries.js";
@@ -1,10 +1,6 @@
1
+ import { withEachGhost } from "./withEachGhost.js";
1
2
  export async function invalidateGhosts(queryClient, queryKey) {
2
- for (let i = 1; i <= queryKey.length; i++) {
3
- const partialKey = queryKey.slice(0, i);
4
- const exact = i < queryKey.length;
5
- await queryClient.invalidateQueries({
6
- queryKey: partialKey,
7
- exact,
8
- });
9
- }
3
+ await withEachGhost(queryKey, async (filter) => {
4
+ await queryClient.invalidateQueries(filter);
5
+ });
10
6
  }
@@ -1,10 +1,11 @@
1
1
  import { createElement } from "react";
2
- import { isGhostMarker, transformFnProp, } from "./types";
3
- import { useGhostChain } from "./useGhostChain";
4
- import { RenderComponent } from "./RenderComponent";
5
- import { resolveGhostChain } from "./resolveGhostChain";
6
- import { invalidateGhosts } from "./invalidate";
7
- import { queries } from "./queries";
2
+ import { isGhostMarker, transformFnProp, } from "./types.js";
3
+ import { useGhostChain } from "./useGhostChain.js";
4
+ import { RenderComponent } from "./RenderComponent.js";
5
+ import { resolveGhostChain } from "./resolveGhostChain.js";
6
+ import { invalidateGhosts } from "./invalidate.js";
7
+ import { queries } from "./queries.js";
8
+ import { resetGhosts } from "./reset.js";
8
9
  function buildGhostDeep(target, ghostChain) {
9
10
  const proxyTarget = (() => {
10
11
  // Dummy function to allow ghosting function calls
@@ -28,6 +29,7 @@ function buildGhostDeep(target, ghostChain) {
28
29
  return ghost;
29
30
  },
30
31
  invalidate: (queryClient) => invalidateGhosts(queryClient, queries.ghostChain(target, ghostChain)),
32
+ reset: (queryClient) => resetGhosts(queryClient, queries.ghostChain(target, ghostChain)),
31
33
  };
32
34
  const ghost = new Proxy(proxyTarget, {
33
35
  get: ($, prop) => {
@@ -1,5 +1,5 @@
1
1
  import { expectTypeOf, test } from "vitest";
2
- import { ProjectGhost } from "./testMocks";
2
+ import { ProjectGhost } from "./testMocks.js";
3
3
  test("return type is correct", () => {
4
4
  const customerNameGhost = ProjectGhost.ofId("Project A")
5
5
  .getDetailed()
@@ -1,12 +1,14 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { cleanup, render } from "@testing-library/react";
3
3
  import { describe, expect, test, vitest, beforeEach, afterEach } from "vitest";
4
- import { CustomerDetailed, CustomerGhost, Project, ProjectDetailed, ProjectGhost, advanceSleepTimer, customerMocks, getCustomerNameGhostIds, projectMocks, } from "./testMocks";
4
+ import { CustomerDetailed, CustomerGhost, Project, ProjectDetailed, ProjectGhost, advanceSleepTimer, customerMocks, getCustomerNameGhostIds, projectMocks, } from "./testMocks.js";
5
5
  import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
6
- import { invalidateGhosts } from "./invalidate";
7
- import { makeGhost } from "./makeGhost";
6
+ import { invalidateGhosts } from "./invalidate.js";
7
+ import { makeGhost } from "./makeGhost.js";
8
+ import { cleanupTargetHashes } from "./useGhostChain.js";
8
9
  beforeEach(() => {
9
10
  vitest.useFakeTimers();
11
+ cleanupTargetHashes();
10
12
  });
11
13
  afterEach(() => {
12
14
  vitest.runOnlyPendingTimers();
@@ -181,7 +183,7 @@ describe("Hooks", () => {
181
183
  expect(customerMocks.getDetailed).toHaveBeenCalledTimes(1);
182
184
  expect(customerMocks.getName).toHaveBeenCalledTimes(1);
183
185
  result.current?.invalidate();
184
- await vitest.advanceTimersByTimeAsync(100000);
186
+ await vitest.runOnlyPendingTimersAsync();
185
187
  expect(projectMocks.getDetailed).toHaveBeenCalledTimes(2);
186
188
  expect(customerMocks.getDetailed).toHaveBeenCalledTimes(2);
187
189
  expect(customerMocks.getName).toHaveBeenCalledTimes(2);
@@ -196,7 +198,7 @@ describe("Hooks", () => {
196
198
  expect(customerMocks.getDetailed).toHaveBeenCalledTimes(1);
197
199
  expect(customerMocks.getName).toHaveBeenCalledTimes(1);
198
200
  ghost.invalidate(queryClient);
199
- await vitest.advanceTimersByTimeAsync(10000);
201
+ await vitest.runOnlyPendingTimersAsync();
200
202
  expect(projectMocks.getDetailed).toHaveBeenCalledTimes(2);
201
203
  expect(customerMocks.getDetailed).toHaveBeenCalledTimes(2);
202
204
  expect(customerMocks.getName).toHaveBeenCalledTimes(2);
@@ -211,7 +213,7 @@ describe("Hooks", () => {
211
213
  expect(customerMocks.getDetailed).toHaveBeenCalledTimes(1);
212
214
  expect(customerMocks.getName).toHaveBeenCalledTimes(1);
213
215
  invalidateGhosts(queryClient, getCustomerNameGhostIds.current.queryKey);
214
- await vitest.advanceTimersByTimeAsync(10000);
216
+ await vitest.runOnlyPendingTimersAsync();
215
217
  expect(projectMocks.getDetailed).toHaveBeenCalledTimes(2);
216
218
  expect(customerMocks.getDetailed).toHaveBeenCalledTimes(2);
217
219
  expect(customerMocks.getName).toHaveBeenCalledTimes(2);
@@ -223,7 +225,7 @@ describe("Hooks", () => {
223
225
  expect(projectMocks.getDetailed).toHaveBeenCalledTimes(1);
224
226
  expect(customerMocks.getDetailed).toHaveBeenCalledTimes(1);
225
227
  ghost.invalidate(queryClient);
226
- await vitest.advanceTimersByTimeAsync(10000);
228
+ await vitest.runOnlyPendingTimersAsync();
227
229
  expect(projectMocks.getDetailed).toHaveBeenCalledTimes(2);
228
230
  expect(customerMocks.getDetailed).toHaveBeenCalledTimes(2);
229
231
  });
@@ -234,7 +236,7 @@ describe("Hooks", () => {
234
236
  expect(projectMocks.getDetailed).toHaveBeenCalledTimes(1);
235
237
  expect(customerMocks.getDetailed).toHaveBeenCalledTimes(0);
236
238
  specialGhost.invalidate(queryClient);
237
- await vitest.advanceTimersByTimeAsync(10000);
239
+ await vitest.runOnlyPendingTimersAsync();
238
240
  expect(projectMocks.getDetailed).toHaveBeenCalledTimes(2);
239
241
  expect(customerMocks.getDetailed).toHaveBeenCalledTimes(0);
240
242
  });
@@ -246,7 +248,7 @@ describe("Hooks", () => {
246
248
  .fn()
247
249
  .mockImplementation((id) => new ProjectDetailed(id, `CHANGED! Project ${id}`, "C1"));
248
250
  projectGhost.invalidate(queryClient);
249
- await vitest.advanceTimersByTimeAsync(10000);
251
+ await vitest.runOnlyPendingTimersAsync();
250
252
  expect(projectMocks.getName).toHaveBeenCalledTimes(2);
251
253
  });
252
254
  });
@@ -1,5 +1,5 @@
1
- import { makeGhost } from "../makeGhost";
2
- import { isReactGhost } from "../types";
1
+ import { makeGhost } from "../makeGhost.js";
2
+ import { isReactGhost } from "../types.js";
3
3
  export function asGhost(maybeGhost) {
4
4
  if (isReactGhost(maybeGhost)) {
5
5
  return maybeGhost;
@@ -1,5 +1,5 @@
1
- import { asGhostProps } from "../maybeGhost";
2
- import { isReactGhost } from "../types";
1
+ import { asGhostProps } from "../maybeGhost/index.js";
2
+ import { isReactGhost } from "../types.js";
3
3
  import { expect, test } from "vitest";
4
4
  test("makes Ghosts of all props", () => {
5
5
  const ghostProps = asGhostProps({
@@ -1,2 +1,2 @@
1
- export * from "./asGhost";
2
- export * from "./types";
1
+ export * from "./asGhost.js";
2
+ export * from "./types.js";
@@ -1,5 +1,5 @@
1
1
  import { describe, expectTypeOf, test } from "vitest";
2
- import { asGhostProps } from "./asGhost";
2
+ import { asGhostProps } from "./asGhost.js";
3
3
  describe("fixupMaybeReactGhostProps()", () => {
4
4
  test("works for one prop", () => {
5
5
  expectTypeOf(asGhostProps({})).toEqualTypeOf();
@@ -1,4 +1,4 @@
1
1
  import { getPrototypeListOf } from "polytype";
2
- import { getProtoypes } from "./metaData";
2
+ import { getProtoypes } from "./metaData.js";
3
3
  getProtoypes.current = getPrototypeListOf;
4
- export { GhostMakerModel } from "./metaData";
4
+ export { GhostMakerModel } from "./metaData.js";
@@ -1,7 +1,7 @@
1
1
  import is from "@sindresorhus/is";
2
- import { hashObject } from "./hash";
3
- import { getMetaData } from "./metaData";
4
- import { modelIdentifiers } from "./modelIdentifier";
2
+ import { hashObject } from "./hash.js";
3
+ import { getMetaData } from "./metaData.js";
4
+ import { modelIdentifiers } from "./modelIdentifier.js";
5
5
  export const getModelName = (something) => {
6
6
  return getMetaData(something)?.name;
7
7
  };
@@ -32,7 +32,7 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
32
32
  }
33
33
  return useValue ? value : void 0;
34
34
  };
35
- import { getMetaData, GhostMakerModel } from "./metaData";
35
+ import { getMetaData, GhostMakerModel } from "./metaData.js";
36
36
  import { expect, test } from "vitest";
37
37
  let GrandParent = (() => {
38
38
  let _classDecorators = [GhostMakerModel({
@@ -0,0 +1,6 @@
1
+ import { withEachGhost } from "./withEachGhost.js";
2
+ export async function resetGhosts(queryClient, queryKey) {
3
+ await withEachGhost(queryKey, async (filter) => {
4
+ await queryClient.resetQueries(filter);
5
+ });
6
+ }
@@ -1,6 +1,5 @@
1
1
  import is, { assert } from "@sindresorhus/is";
2
- import { isReactGhost, transformFnProp, } from "./types";
3
- import { getQueryContext, ghostFnContext } from "./context";
2
+ import { isReactGhost, transformFnProp, } from "./types.js";
4
3
  const resolveGhostChainItem = async (target, item) => {
5
4
  const { propName, args } = item;
6
5
  if (propName === transformFnProp) {
@@ -22,19 +21,13 @@ const resolveGhostChainItem = async (target, item) => {
22
21
  return property;
23
22
  }
24
23
  assert.function(property);
25
- return property.call(target, ...args);
24
+ const asyncFn = property.bind(target);
25
+ return await asyncFn(...args);
26
26
  };
27
27
  export const resolveGhostChain = async (target, stack) => {
28
28
  let result = target;
29
- const queryContext = getQueryContext();
30
29
  for (const item of stack) {
31
- if (queryContext) {
32
- const resolveGhostChainItemWithContext = ghostFnContext.bind({ query: queryContext }, resolveGhostChainItem);
33
- result = await resolveGhostChainItemWithContext(result, item);
34
- }
35
- else {
36
- result = await resolveGhostChainItem(result, item);
37
- }
30
+ result = await resolveGhostChainItem(result, item);
38
31
  }
39
32
  return result;
40
33
  };
@@ -33,8 +33,8 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
33
33
  return useValue ? value : void 0;
34
34
  };
35
35
  import { beforeEach, vitest } from "vitest";
36
- import { getQueryContext, makeGhost } from ".";
37
- import { GhostMakerModel } from "./metaData";
36
+ import { getQueryContext, makeGhost } from "./index.js";
37
+ import { GhostMakerModel } from "./metaData.js";
38
38
  const sleep = () => new Promise((resolve) => setTimeout(resolve, 200));
39
39
  export const advanceSleepTimer = async (times = 1) => {
40
40
  for (let i = 0; i < times; i++) {
@@ -1,52 +1,84 @@
1
- import is from "@sindresorhus/is";
2
- import { useEffect, useEffectEvent, useRef } from "react";
1
+ import is, { assert } from "@sindresorhus/is";
2
+ import { useEffect, useEffectEvent, useMemo } from "react";
3
+ import { transformFnProp, } from "./types.js";
3
4
  import { useQueryClient, useSuspenseQuery } from "@tanstack/react-query";
4
- import { queries } from "./queries";
5
- import { ghostFnContext } from "./context";
6
- import { hashObject } from "./hash";
7
- import { resolveGhostChain } from "./resolveGhostChain";
5
+ import { queries } from "./queries.js";
6
+ import { ghostFnContext } from "./context.js";
7
+ import { invalidateGhosts } from "./invalidate.js";
8
+ import { hashObject } from "./hash.js";
9
+ import { resetGhosts } from "./reset.js";
8
10
  export const useGhostChain = (target, chain, options) => {
9
11
  const queryClient = useQueryClient();
10
- const queryKey = queries.ghostChain(target, chain);
11
- const fn = () => resolveGhostChain(target, chain);
12
- const queryResult = useSuspenseQuery({
12
+ const context = {
13
+ queryKey: queries.target(target),
14
+ };
15
+ const value = chain.reduce((currentTarget, item) => useGhostChainItem(currentTarget, item, options, context), target);
16
+ const invalidate = () => invalidateGhosts(queryClient, queries.ghostChain(target, chain));
17
+ const reset = () => resetGhosts(queryClient, queries.ghostChain(target, chain));
18
+ return {
19
+ value,
20
+ invalidate,
21
+ reset,
22
+ };
23
+ };
24
+ const useGhostChainItem = (target, item, options = {}, context) => {
25
+ const { propName, args } = item;
26
+ if (propName === transformFnProp) {
27
+ assert.array(args);
28
+ const transformFn = args[0];
29
+ assert.function(transformFn, "transform requires a mapping function");
30
+ const transformDependencies = args[1] ?? [];
31
+ assert.array(transformDependencies);
32
+ return useMemo(() => transformFn(target), [target, ...transformDependencies]);
33
+ }
34
+ if (is.nullOrUndefined(target)) {
35
+ return target;
36
+ }
37
+ assert.object(target);
38
+ const targetPropertyName = propName;
39
+ const targetProperty = target[targetPropertyName];
40
+ context.queryKey = queries.chainItem(context.queryKey, item);
41
+ if (args === undefined) {
42
+ // no function call, just a property access
43
+ return targetProperty;
44
+ }
45
+ assert.function(targetProperty);
46
+ const targetFn = targetProperty.bind(target);
47
+ const queryKey = [...context.queryKey];
48
+ const query = useSuspenseQuery({
13
49
  ...options,
50
+ queryKey,
14
51
  queryFn: async (ctx) => {
15
- const targetFnWithContext = ghostFnContext.bind({ query: ctx }, fn);
16
- return { result: await targetFnWithContext() };
52
+ const targetFnWithContext = ghostFnContext.bind({ query: ctx }, targetFn);
53
+ return { result: await targetFnWithContext(...args) };
17
54
  },
18
- queryKey,
19
55
  });
20
- if (queryResult.error) {
21
- throw queryResult.error;
22
- }
23
56
  useTargetAutoInvalidate(target, queryKey);
24
- return {
25
- value: queryResult.data.result,
26
- invalidate: () => queryClient.invalidateQueries({
27
- queryKey,
28
- }),
29
- };
57
+ if (query.error) {
58
+ throw query.error;
59
+ }
60
+ return query.data.result;
30
61
  };
62
+ const targetHashes = new Map();
31
63
  const useTargetAutoInvalidate = (target, queryKey) => {
32
64
  const queryClient = useQueryClient();
33
- const invalidate = () => queryClient.invalidateQueries({
34
- queryKey,
35
- });
36
- const targetHash = is.object(target) ? hashObject(target) : target;
37
- const prevTargetHash = useRef(targetHash);
38
- const joinedQueryKey = queryKey.join(".");
39
- const prevQueryId = useRef(joinedQueryKey);
40
- const needsRefresh = prevQueryId.current === joinedQueryKey &&
41
- prevTargetHash.current !== targetHash;
42
- prevTargetHash.current = targetHash;
43
- prevQueryId.current = joinedQueryKey;
44
65
  const onTargetChangeEvent = useEffectEvent(() => {
66
+ const joinedQueryKey = queryKey.join(".");
67
+ const targetHash = hashObject(target);
68
+ const prevTargetHash = targetHashes.get(joinedQueryKey);
69
+ const needsRefresh = prevTargetHash !== undefined && prevTargetHash !== targetHash;
70
+ targetHashes.set(joinedQueryKey, targetHash);
45
71
  if (needsRefresh) {
46
- invalidate();
72
+ queryClient.invalidateQueries({
73
+ queryKey,
74
+ });
47
75
  }
48
76
  });
49
77
  useEffect(() => {
50
78
  onTargetChangeEvent();
51
- }, [needsRefresh]);
79
+ }, [target]);
80
+ };
81
+ /** @internal */
82
+ export const cleanupTargetHashes = () => {
83
+ targetHashes.clear();
52
84
  };
@@ -0,0 +1,10 @@
1
+ export const withEachGhost = async (queryKey, callback) => {
2
+ for (let i = 1; i <= queryKey.length; i++) {
3
+ const partialKey = queryKey.slice(0, i);
4
+ const exact = i < queryKey.length;
5
+ await callback({
6
+ queryKey: partialKey,
7
+ exact,
8
+ });
9
+ }
10
+ };
@@ -1,8 +1,8 @@
1
- export { makeGhost } from "./makeGhost";
2
- export { registerModelIdentifier } from "./modelIdentifier";
3
- export { type ReactGhost, UseGhostReturn } from "./types";
4
- export { getQueryContext, forwardQueryContext } from "./context";
5
- export { invalidateGhosts } from "./invalidate";
6
- export * from "./maybeGhost";
7
- export { GhostMakerModel, ghostMakerModel } from "./metaData";
8
- export { getModelId, getModelName } from "./queries";
1
+ export { makeGhost } from "./makeGhost.ts";
2
+ export { registerModelIdentifier } from "./modelIdentifier.ts";
3
+ export { type ReactGhost, UseGhostReturn } from "./types.ts";
4
+ export { getQueryContext, forwardQueryContext } from "./context.ts";
5
+ export { invalidateGhosts } from "./invalidate.ts";
6
+ export * from "./maybeGhost/index.ts";
7
+ export { GhostMakerModel, ghostMakerModel } from "./metaData.ts";
8
+ export { getModelId, getModelName } from "./queries.ts";
@@ -1,3 +1,3 @@
1
1
  import { type QueryClient } from "@tanstack/react-query";
2
- import type { QueryKey } from "./types";
2
+ import type { QueryKey } from "./types.ts";
3
3
  export declare function invalidateGhosts(queryClient: QueryClient, queryKey: QueryKey): Promise<void>;
@@ -1,2 +1,2 @@
1
- import { type ReactGhost } from "./types";
1
+ import { type ReactGhost } from "./types.ts";
2
2
  export declare function makeGhost<T>(target: T): ReactGhost<T>;
@@ -1,4 +1,4 @@
1
- import { type ReactGhost } from "../types";
2
- import type { MaybeReactGhost, PropsWithFixedUpReactGhosts, PropsWithMaybeReactGhosts } from "./types";
1
+ import { type ReactGhost } from "../types.ts";
2
+ import type { MaybeReactGhost, PropsWithFixedUpReactGhosts, PropsWithMaybeReactGhosts } from "./types.ts";
3
3
  export declare function asGhost<T>(maybeGhost: MaybeReactGhost<T>): ReactGhost<T>;
4
4
  export declare function asGhostProps<T, TProps extends (keyof T)[] = (keyof T)[]>(props: PropsWithMaybeReactGhosts<T, TProps>, keys?: TProps): PropsWithFixedUpReactGhosts<T, TProps>;
@@ -1,2 +1,2 @@
1
- export * from "./asGhost";
2
- export * from "./types";
1
+ export * from "./asGhost.ts";
2
+ export * from "./types.ts";
@@ -1,4 +1,4 @@
1
- import { type OrOptional, type ReactGhost } from "../types";
1
+ import { type OrOptional, type ReactGhost } from "../types.ts";
2
2
  export type MaybeReactGhost<T> = T extends ReactGhost<infer U> ? ReactGhost<U> | U : T | ReactGhost<T>;
3
3
  export type PropsWithMaybeReactGhosts<T, TProps extends (keyof T)[]> = {
4
4
  [K in keyof T]: K extends TProps[number] ? MaybeReactGhost<T[K] | OrOptional<T[K]>> : T[K] | OrOptional<T[K]>;
@@ -1,4 +1,4 @@
1
- import type { ModelIdentifier } from "./types";
1
+ import type { ModelIdentifier } from "./types.ts";
2
2
  export declare const modelIdentifiers: ModelIdentifier[];
3
3
  /** @deprecated Use GhostMakerModel (decorator) instead */
4
4
  export declare const registerModelIdentifier: (identifier: ModelIdentifier) => void;
@@ -1 +1 @@
1
- export { GhostMakerModel } from "./metaData";
1
+ export { GhostMakerModel } from "./metaData.ts";
@@ -1,9 +1,9 @@
1
- import type { GhostChain, GhostChainItem, QueryKey } from "./types";
1
+ import type { GhostChain, GhostChainItem, QueryKey } from "./types.ts";
2
2
  export declare const getModelName: (something: unknown) => string | undefined;
3
3
  export declare const getModelId: (something: unknown) => string | undefined;
4
4
  export declare const queries: {
5
5
  ghostmaker: () => string[];
6
6
  target: (target: unknown) => string[];
7
- chainItem: (prev: QueryKey, chainItem: GhostChainItem) => QueryKey;
8
- ghostChain: (target: unknown, chain: GhostChain) => QueryKey;
7
+ chainItem: (prev: QueryKey, chainItem: GhostChainItem) => unknown[];
8
+ ghostChain: (target: unknown, chain: GhostChain) => unknown[];
9
9
  };
@@ -0,0 +1,3 @@
1
+ import { type QueryClient } from "@tanstack/react-query";
2
+ import type { QueryKey } from "./types.ts";
3
+ export declare function resetGhosts(queryClient: QueryClient, queryKey: QueryKey): Promise<void>;
@@ -1,2 +1,2 @@
1
- import { type GhostChain } from "./types";
1
+ import { type GhostChain } from "./types.ts";
2
2
  export declare const resolveGhostChain: (target: unknown, stack: GhostChain) => Promise<unknown>;
@@ -41,5 +41,5 @@ export declare class ProjectDetailed {
41
41
  getName(): string;
42
42
  getStatus(): Promise<string>;
43
43
  }
44
- export declare const ProjectGhost: import("./types").ReactGhost<typeof Project>;
45
- export declare const CustomerGhost: import("./types").ReactGhost<typeof Customer>;
44
+ export declare const ProjectGhost: import("./types.ts").ReactGhost<typeof Project>;
45
+ export declare const CustomerGhost: import("./types.ts").ReactGhost<typeof Customer>;
@@ -7,13 +7,16 @@ export type AnyFunction = (...args: ExplicitAny[]) => ExplicitAny;
7
7
  export interface UseQueryReturnType<T> {
8
8
  result: T;
9
9
  }
10
- export type useGhostOptions<T = unknown> = Omit<UseQueryOptions<UseQueryReturnType<T>>, "queryKey" | "queryFn">;
10
+ export type UseGhostOptions<T = unknown> = Omit<UseQueryOptions<UseQueryReturnType<T>>, "queryKey" | "queryFn">;
11
+ /** @deprecated Use `UseGhostOptions<unknown>` instead. */
12
+ export type useGhostOptions = UseGhostOptions<unknown>;
11
13
  export interface ReactGhostMethods<T> extends Promise<T> {
12
- useGhost: (options?: useGhostOptions) => UseGhostReturn<T>;
13
- use: (options?: useGhostOptions) => T;
14
+ useGhost: (options?: UseGhostOptions<T>) => UseGhostReturn<T>;
15
+ use: (options?: UseGhostOptions<T>) => T;
14
16
  render: (transform?: (item: T) => ReactNode) => ReactNode;
15
17
  transform: <U, U2 = U>(fn: (item: T extends ReactGhost<infer T2> ? T2 : T) => U2, dependencies?: DependencyList) => ReactGhost<U2>;
16
18
  invalidate: (queryClient: QueryClient) => Promise<void>;
19
+ reset: (queryClient: QueryClient) => Promise<void>;
17
20
  }
18
21
  export declare const transformFnProp: "___transformFn";
19
22
  export declare const isGhostMarker: "___ghostMarker";
@@ -28,12 +31,14 @@ export type GhostChain = GhostChainItem[];
28
31
  export type GhostChainTargetType = UnknownRecord | Class<unknown> | null | undefined;
29
32
  export type QueryKey = readonly unknown[];
30
33
  export type InvalidateGhostFn = () => Promise<void>;
34
+ export type ResetGhostFn = () => Promise<void>;
31
35
  export interface GhostChainContext {
32
36
  queryKey: QueryKey;
33
37
  }
34
38
  export interface UseGhostReturn<T> {
35
39
  value: T;
36
40
  invalidate: InvalidateGhostFn;
41
+ reset: ResetGhostFn;
37
42
  }
38
43
  export declare function isReactGhost<T>(something: unknown): something is ReactGhost<T>;
39
44
  export type ModelIdentifier = (model: unknown) => string | undefined;
@@ -1,2 +1,2 @@
1
- import { type GhostChain, type useGhostOptions, type UseGhostReturn } from "./types";
2
- export declare const useGhostChain: <T>(target: unknown, chain: GhostChain, options?: useGhostOptions) => UseGhostReturn<T>;
1
+ import { type GhostChain, type UseGhostOptions, type UseGhostReturn } from "./types.ts";
2
+ export declare const useGhostChain: <T>(target: unknown, chain: GhostChain, options?: UseGhostOptions<T>) => UseGhostReturn<T>;
@@ -0,0 +1,2 @@
1
+ import type { QueryFilters, QueryKey } from "@tanstack/react-query";
2
+ export declare const withEachGhost: (queryKey: QueryKey, callback: (queryFilters: QueryFilters) => Promise<void>) => Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mittwald/react-ghostmaker",
3
- "version": "1.5.0-alpha.1",
3
+ "version": "1.5.1",
4
4
  "author": "Mittwald CM Service GmbH & Co. KG <opensource@mittwald.de>",
5
5
  "contributors": [
6
6
  "Marco Falkenberg <m.falkenberg@mittwald.de>"
@@ -38,38 +38,38 @@
38
38
  "dist"
39
39
  ],
40
40
  "dependencies": {
41
- "@sindresorhus/is": "^7.1.0",
41
+ "@sindresorhus/is": "^7.2.0",
42
42
  "context": "^3.0.33",
43
- "object-code": "^1.3.4",
44
- "type-fest": "^5.1.0"
43
+ "object-code": "^2.0.0",
44
+ "type-fest": "^5.4.4"
45
45
  },
46
46
  "devDependencies": {
47
- "@eslint/eslintrc": "^3.3.1",
48
- "@eslint/js": "^9.39.1",
49
- "@tanstack/react-query": "^5.90.5",
47
+ "@eslint/eslintrc": "^3.3.3",
48
+ "@eslint/js": "^9.39.2",
49
+ "@tanstack/react-query": "^5.90.20",
50
50
  "@testing-library/dom": "^10.4.1",
51
- "@testing-library/react": "^16.3.0",
52
- "@types/react": "^19.2.2",
53
- "@types/react-dom": "^19.2.2",
54
- "@typescript-eslint/eslint-plugin": "^8.46.3",
55
- "@typescript-eslint/parser": "^8.46.1",
56
- "eslint": "^9.37.0",
51
+ "@testing-library/react": "^16.3.2",
52
+ "@types/react": "^19.2.13",
53
+ "@types/react-dom": "^19.2.3",
54
+ "@typescript-eslint/eslint-plugin": "^8.54.0",
55
+ "@typescript-eslint/parser": "^8.54.0",
56
+ "eslint": "^9.39.2",
57
57
  "eslint-config-prettier": "^10.1.8",
58
58
  "eslint-plugin-json": "^4.0.1",
59
- "eslint-plugin-prettier": "^5.5.4",
59
+ "eslint-plugin-prettier": "^5.5.5",
60
60
  "globals": "^16.5.0",
61
- "happy-dom": "^20.0.2",
61
+ "happy-dom": "^20.5.3",
62
62
  "license-checker-rseidelsohn": "^4.4.2",
63
63
  "polytype": "^0.17.0",
64
- "prettier": "^3.6.2",
65
- "prettier-plugin-jsdoc": "^1.3.3",
66
- "prettier-plugin-pkgsort": "^0.2.1",
67
- "prettier-plugin-sort-json": "^4.1.1",
68
- "react": "^19.2.0",
69
- "react-dom": "^19.2.0",
70
- "rimraf": "^6.0.1",
64
+ "prettier": "^3.8.1",
65
+ "prettier-plugin-jsdoc": "^1.8.0",
66
+ "prettier-plugin-pkgsort": "^0.3.0",
67
+ "prettier-plugin-sort-json": "^4.2.0",
68
+ "react": "^19.2.4",
69
+ "react-dom": "^19.2.4",
70
+ "rimraf": "^6.1.2",
71
71
  "typescript": "^5.9.3",
72
- "vitest": "^3.2.4"
72
+ "vitest": "^4.0.18"
73
73
  },
74
74
  "peerDependencies": {
75
75
  "@tanstack/react-query": "^5",