@marktiderman/genesis-core 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/README.md +223 -0
  2. package/dist/chunk-5NHR6ZXS.js +31 -0
  3. package/dist/chunk-5NHR6ZXS.js.map +1 -0
  4. package/dist/chunk-7NUL32W7.js +14 -0
  5. package/dist/chunk-7NUL32W7.js.map +1 -0
  6. package/dist/chunk-BQOJX67X.js +31 -0
  7. package/dist/chunk-BQOJX67X.js.map +1 -0
  8. package/dist/{chunk-QFTMHOEH.js → chunk-DVW5GXWZ.js} +84 -31
  9. package/dist/chunk-DVW5GXWZ.js.map +1 -0
  10. package/dist/{chunk-6V2TIN3G.js → chunk-GGSXSI6Z.js} +6 -5
  11. package/dist/chunk-GGSXSI6Z.js.map +1 -0
  12. package/dist/chunk-HT43MPKW.js +3 -0
  13. package/dist/{chunk-EL7YGOTY.js.map → chunk-HT43MPKW.js.map} +1 -1
  14. package/dist/chunk-HUNGCTRA.js +33 -0
  15. package/dist/chunk-HUNGCTRA.js.map +1 -0
  16. package/dist/{chunk-5FTUB2M6.js → chunk-IZT2O57C.js} +160 -40
  17. package/dist/chunk-IZT2O57C.js.map +1 -0
  18. package/dist/chunk-QH4P5MI2.js +278 -0
  19. package/dist/chunk-QH4P5MI2.js.map +1 -0
  20. package/dist/chunk-RLY7TZUY.js +444 -0
  21. package/dist/chunk-RLY7TZUY.js.map +1 -0
  22. package/dist/chunk-XQO4OH2N.js +32 -0
  23. package/dist/chunk-XQO4OH2N.js.map +1 -0
  24. package/dist/{chunk-3P4PHPMJ.js → chunk-ZK5C3OOT.js} +3 -3
  25. package/dist/{chunk-3P4PHPMJ.js.map → chunk-ZK5C3OOT.js.map} +1 -1
  26. package/dist/form-types.d.ts +6 -0
  27. package/dist/hooks/index.d.ts +5 -1
  28. package/dist/hooks/index.js +11 -6
  29. package/dist/hooks/mutation-mode.d.ts +109 -0
  30. package/dist/hooks/mutation-mode.js +3 -0
  31. package/dist/hooks/mutation-mode.js.map +1 -0
  32. package/dist/hooks/related-keys.d.ts +12 -0
  33. package/dist/hooks/related-keys.js +3 -0
  34. package/dist/hooks/related-keys.js.map +1 -0
  35. package/dist/hooks/use-capabilities.d.ts +21 -0
  36. package/dist/hooks/use-capabilities.js +5 -0
  37. package/dist/hooks/use-capabilities.js.map +1 -0
  38. package/dist/hooks/use-one.d.ts +2 -1
  39. package/dist/hooks/use-one.js +2 -2
  40. package/dist/hooks/use-resource-form.d.ts +53 -2
  41. package/dist/hooks/use-resource-form.js +7 -3
  42. package/dist/hooks/use-resource.d.ts +42 -5
  43. package/dist/hooks/use-resource.js +4 -2
  44. package/dist/hooks/use-unsaved-changes-guard.d.ts +33 -0
  45. package/dist/hooks/use-unsaved-changes-guard.js +3 -0
  46. package/dist/hooks/use-unsaved-changes-guard.js.map +1 -0
  47. package/dist/index.d.ts +7 -3
  48. package/dist/index.js +14 -9
  49. package/dist/provider/index.d.ts +212 -6
  50. package/dist/provider/index.js +5 -3
  51. package/dist/read-outcome-BQkYcV4t.d.ts +88 -0
  52. package/dist/schema/index.d.ts +4 -3
  53. package/dist/schema/index.js +2 -1
  54. package/dist/{types-DUbRb8qV.d.ts → types-C5bjsbxz.d.ts} +13 -255
  55. package/dist/types-Dsqqr9s7.d.ts +281 -0
  56. package/package.json +2 -2
  57. package/dist/chunk-4W4W7F4I.js +0 -20
  58. package/dist/chunk-4W4W7F4I.js.map +0 -1
  59. package/dist/chunk-5FTUB2M6.js.map +0 -1
  60. package/dist/chunk-6AA4ISUA.js +0 -125
  61. package/dist/chunk-6AA4ISUA.js.map +0 -1
  62. package/dist/chunk-6V2TIN3G.js.map +0 -1
  63. package/dist/chunk-EL7YGOTY.js +0 -3
  64. package/dist/chunk-QFTMHOEH.js.map +0 -1
  65. package/dist/write-outcome-aYToY3dK.d.ts +0 -38
@@ -1,8 +1,12 @@
1
1
  export { UseResourceOptions, UseResourceReturn, useResource } from './use-resource.js';
2
2
  export { UseOneOptions, useOne } from './use-one.js';
3
+ export { useCapabilities } from './use-capabilities.js';
3
4
  export { UseResourceFormOptions, UseResourceFormReturn, buildArrayItemDefaults, useResourceForm } from './use-resource-form.js';
4
5
  export { UseWizardReturn, WizardStep, useWizard } from './use-wizard.js';
6
+ export { DEFAULT_UNDO_DELAY_MS, MutationMode, MutationModeOptions, UndoableWrite, pendingWriteCount } from './mutation-mode.js';
7
+ export { DEFAULT_UNSAVED_CHANGES_MESSAGE, UseUnsavedChangesGuardOptions, UseUnsavedChangesGuardReturn, useUnsavedChangesGuard } from './use-unsaved-changes-guard.js';
5
8
  import '@tanstack/react-query';
6
- import '../types-DUbRb8qV.js';
9
+ import '../types-Dsqqr9s7.js';
10
+ import '../types-C5bjsbxz.js';
7
11
  import '../form-types.js';
8
12
  import 'react-hook-form';
@@ -1,10 +1,15 @@
1
- import '../chunk-EL7YGOTY.js';
2
- export { buildArrayItemDefaults, useResourceForm } from '../chunk-5FTUB2M6.js';
1
+ import '../chunk-HT43MPKW.js';
2
+ export { buildArrayItemDefaults, useResourceForm } from '../chunk-IZT2O57C.js';
3
+ export { useResource } from '../chunk-DVW5GXWZ.js';
4
+ export { DEFAULT_UNSAVED_CHANGES_MESSAGE, useUnsavedChangesGuard } from '../chunk-XQO4OH2N.js';
5
+ export { useWizard } from '../chunk-7KOLYF27.js';
6
+ import '../chunk-5NHR6ZXS.js';
3
7
  import '../chunk-RYEXWQFD.js';
4
- export { useOne } from '../chunk-3P4PHPMJ.js';
5
- export { useResource } from '../chunk-QFTMHOEH.js';
6
- import '../chunk-6V2TIN3G.js';
8
+ export { DEFAULT_UNDO_DELAY_MS, pendingWriteCount } from '../chunk-QH4P5MI2.js';
9
+ import '../chunk-7NUL32W7.js';
10
+ export { useCapabilities } from '../chunk-BQOJX67X.js';
11
+ export { useOne } from '../chunk-ZK5C3OOT.js';
12
+ import '../chunk-GGSXSI6Z.js';
7
13
  import '../chunk-ZYJUEJ4Z.js';
8
- export { useWizard } from '../chunk-7KOLYF27.js';
9
14
  //# sourceMappingURL=index.js.map
10
15
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,109 @@
1
+ import { QueryClient } from '@tanstack/react-query';
2
+ import { I as Identifier, B as BaseRecord, D as DataProvider } from '../types-Dsqqr9s7.js';
3
+ import { A as AuditEntry } from '../types-C5bjsbxz.js';
4
+ import '../form-types.js';
5
+
6
+ type MutationMode = "pessimistic" | "optimistic" | "undoable";
7
+ /** How long an `undoable` write waits for an undo before it is sent. */
8
+ declare const DEFAULT_UNDO_DELAY_MS = 5000;
9
+ /** An undoable write, as handed to `onUndoable` so a UI can offer the undo. */
10
+ interface UndoableWrite {
11
+ resource: string;
12
+ kind: "create" | "update" | "delete";
13
+ /** The record's id; absent for a create (it has none until it is sent). */
14
+ id?: Identifier;
15
+ /**
16
+ * - `delay`: the write has not been sent. `undo()` discards it; `commit()`
17
+ * sends it now instead of waiting.
18
+ * - `provider`: the write was sent at once, because the provider serves
19
+ * `undo`. `undo()` asks the provider to reverse it; `commit()` does
20
+ * nothing.
21
+ */
22
+ strategy: "delay" | "provider";
23
+ /** How long the undo is offered, in ms (the toast's duration). */
24
+ delayMs: number;
25
+ /**
26
+ * Take the write back. Resolves true when it was undone, false when it
27
+ * could not be: already sent under `delay` (the window closed, or the hook
28
+ * unmounted), already undone, or the provider's undo failed (reported
29
+ * through `onError`). Safe to call more than once.
30
+ */
31
+ undo: () => Promise<boolean>;
32
+ /** Send a `delay` write now. */
33
+ commit: () => void;
34
+ }
35
+ interface MutationModeOptions {
36
+ /** Default `"pessimistic"`. */
37
+ mutationMode?: MutationMode;
38
+ /** How long an `undoable` write waits, in ms. Default 5000. */
39
+ undoDelayMs?: number;
40
+ /** Called with every `undoable` write, to show an undo control. */
41
+ onUndoable?: (write: UndoableWrite) => void;
42
+ }
43
+ /** Optimistic or undoable writes on `resource` not yet settled. */
44
+ declare function pendingWriteCount(queryClient: QueryClient, resource: string): number;
45
+ /**
46
+ * Invalidate `[resource]`, unless a patched write on it is still pending
47
+ * (the last one to settle refetches).
48
+ */
49
+ declare function invalidateResource(queryClient: QueryClient, resource: string): Promise<void>;
50
+ /** The record as the cache has it now, from a detail entry or any list. */
51
+ declare function cachedRecord(queryClient: QueryClient, resource: string, id: Identifier): BaseRecord | undefined;
52
+ interface DelayedWrite {
53
+ /** Sends it now, if it is still waiting. */
54
+ flush: () => void;
55
+ }
56
+ /** The delayed writes one hook instance started, flushed when it unmounts. */
57
+ type DelayedWrites = Set<DelayedWrite>;
58
+ /**
59
+ * A hook's own set of delayed writes. When the hook unmounts (the form
60
+ * closes, the route changes) every write still waiting is sent at once,
61
+ * as React Admin does: leaving is not undoing.
62
+ */
63
+ declare function useDelayedWrites(): DelayedWrites;
64
+ interface ExecuteWriteArgs<R extends BaseRecord | void> {
65
+ queryClient: QueryClient;
66
+ provider: DataProvider;
67
+ resource: string;
68
+ kind: "create" | "update" | "delete";
69
+ id?: Identifier;
70
+ /**
71
+ * The values to merge into the cached row (update). Leave out to patch
72
+ * nothing, e.g. when the values are form-shaped and the row is not.
73
+ */
74
+ data?: Record<string, unknown>;
75
+ mode: MutationMode;
76
+ undoDelayMs?: number;
77
+ onUndoable?: (write: UndoableWrite) => void;
78
+ /** The delayed writes of the calling hook instance. */
79
+ delayed: DelayedWrites;
80
+ /** Sends the write. */
81
+ send: () => Promise<{
82
+ record: R;
83
+ audit?: AuditEntry;
84
+ }>;
85
+ /**
86
+ * After the write committed. Invalidates and calls success callbacks; its
87
+ * `[resource]` invalidation should go through `invalidateResource`.
88
+ */
89
+ committed: (record: R) => Promise<void> | void;
90
+ /** A write that failed after the caller was already told it went through. */
91
+ failedLater: (error: Error) => void;
92
+ }
93
+ type ExecuteWriteResult<R> = {
94
+ status: "committed";
95
+ record: R;
96
+ } | {
97
+ status: "applied";
98
+ };
99
+ /**
100
+ * Runs one write in the given mode.
101
+ *
102
+ * - `pessimistic`: sends it and waits; a failure rejects.
103
+ * - `optimistic` / `undoable`: resolves `{ status: "applied" }` as soon as
104
+ * the cache is patched. A later failure rolls the patch back and goes to
105
+ * `failedLater`, never to the returned promise.
106
+ */
107
+ declare function executeWrite<R extends BaseRecord | void>(args: ExecuteWriteArgs<R>): Promise<ExecuteWriteResult<R>>;
108
+
109
+ export { DEFAULT_UNDO_DELAY_MS, type DelayedWrites, type ExecuteWriteArgs, type ExecuteWriteResult, type MutationMode, type MutationModeOptions, type UndoableWrite, cachedRecord, executeWrite, invalidateResource, pendingWriteCount, useDelayedWrites };
@@ -0,0 +1,3 @@
1
+ export { DEFAULT_UNDO_DELAY_MS, cachedRecord, executeWrite, invalidateResource, pendingWriteCount, useDelayedWrites } from '../chunk-QH4P5MI2.js';
2
+ //# sourceMappingURL=mutation-mode.js.map
3
+ //# sourceMappingURL=mutation-mode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"mutation-mode.js"}
@@ -0,0 +1,12 @@
1
+ import { D as DataProvider, W as WriteEvent, j as InvalidationKey } from '../types-Dsqqr9s7.js';
2
+ import '../types-C5bjsbxz.js';
3
+ import '../form-types.js';
4
+
5
+ /**
6
+ * The cache keys a committed write makes stale beyond `[resource]`: the
7
+ * provider's own (`DataProvider.invalidates`) plus the caller's. A provider
8
+ * that throws here is logged and skipped; the write already committed.
9
+ */
10
+ declare function relatedKeys(provider: DataProvider, event: WriteEvent, extra: InvalidationKey[] | undefined): InvalidationKey[];
11
+
12
+ export { relatedKeys };
@@ -0,0 +1,3 @@
1
+ export { relatedKeys } from '../chunk-7NUL32W7.js';
2
+ //# sourceMappingURL=related-keys.js.map
3
+ //# sourceMappingURL=related-keys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"related-keys.js"}
@@ -0,0 +1,21 @@
1
+ import { UseQueryResult } from '@tanstack/react-query';
2
+ import { P as ProviderCapabilities } from '../types-C5bjsbxz.js';
3
+ import '../form-types.js';
4
+
5
+ /**
6
+ * The query key for `provider`'s capabilities for `resource`. It includes
7
+ * the provider's identity: swapping the provider (another tenant, a
8
+ * different store) asks the new one instead of reusing the old answer.
9
+ * @internal
10
+ */
11
+ declare function capabilitiesQueryKey(provider: object, resource: string | undefined): readonly unknown[];
12
+ /**
13
+ * What the data provider can do for `resource` (`provider.capabilities`),
14
+ * cached per provider and resource. `data` stays undefined for a provider
15
+ * that doesn't report capabilities, so a UI keeps its defaults.
16
+ */
17
+ declare function useCapabilities(resource: string | undefined, options?: {
18
+ enabled?: boolean;
19
+ }): UseQueryResult<ProviderCapabilities>;
20
+
21
+ export { capabilitiesQueryKey, useCapabilities };
@@ -0,0 +1,5 @@
1
+ export { capabilitiesQueryKey, useCapabilities } from '../chunk-BQOJX67X.js';
2
+ import '../chunk-GGSXSI6Z.js';
3
+ import '../chunk-ZYJUEJ4Z.js';
4
+ //# sourceMappingURL=use-capabilities.js.map
5
+ //# sourceMappingURL=use-capabilities.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"use-capabilities.js"}
@@ -1,5 +1,6 @@
1
1
  import { UseQueryResult } from '@tanstack/react-query';
2
- import { B as BaseRecord, I as Identifier } from '../types-DUbRb8qV.js';
2
+ import { B as BaseRecord, I as Identifier } from '../types-Dsqqr9s7.js';
3
+ import '../types-C5bjsbxz.js';
3
4
  import '../form-types.js';
4
5
 
5
6
  interface UseOneOptions {
@@ -1,5 +1,5 @@
1
- export { useOne } from '../chunk-3P4PHPMJ.js';
2
- import '../chunk-6V2TIN3G.js';
1
+ export { useOne } from '../chunk-ZK5C3OOT.js';
2
+ import '../chunk-GGSXSI6Z.js';
3
3
  import '../chunk-ZYJUEJ4Z.js';
4
4
  //# sourceMappingURL=use-one.js.map
5
5
  //# sourceMappingURL=use-one.js.map
@@ -1,7 +1,11 @@
1
1
  import { UseFormReturn } from 'react-hook-form';
2
2
  import { ResourceFormFieldDef } from '../form-types.js';
3
+ import { j as InvalidationKey } from '../types-Dsqqr9s7.js';
4
+ import { MutationModeOptions } from './mutation-mode.js';
5
+ import '../types-C5bjsbxz.js';
6
+ import '@tanstack/react-query';
3
7
 
4
- interface UseResourceFormOptions {
8
+ interface UseResourceFormOptions extends MutationModeOptions {
5
9
  resource: string;
6
10
  action: "create" | "edit";
7
11
  id?: string;
@@ -9,16 +13,52 @@ interface UseResourceFormOptions {
9
13
  fields?: ResourceFormFieldDef[];
10
14
  /** Full dataset used to auto-detect select fields from unique values. */
11
15
  listData?: Record<string, unknown>[];
16
+ /**
17
+ * The form is done. After the save in `pessimistic` mode (with the stored
18
+ * row); at once in `optimistic` and `undoable` modes (with the submitted
19
+ * values over the record, and no `id` for a create).
20
+ */
12
21
  onSuccess?: (data: Record<string, unknown>) => void;
22
+ /**
23
+ * The save failed. In `optimistic` and `undoable` modes the form may be
24
+ * gone by then, so show the error from here (genesis-ui's ResourceForm
25
+ * toasts it when no `onError` is given).
26
+ */
13
27
  onError?: (error: Error) => void;
14
- /** Pre-fill create form defaults from URL query params. Default true. */
28
+ /**
29
+ * Warn before unsaved changes are lost. Default false. While the form is
30
+ * dirty, closing or reloading the tab shows the browser's prompt, and
31
+ * `confirmLeave()` asks before in-app navigation (wire it to a router
32
+ * blocker; see SKILL.md "Unsaved changes").
33
+ */
34
+ warnWhenUnsaved?: boolean | {
35
+ message?: string;
36
+ };
37
+ /**
38
+ * Pre-fill create form defaults from URL query params: a param named like a
39
+ * field (`?status=draft`) becomes that field's starting value. Default true.
40
+ * Pass false for a form that should not read the page's URL, e.g. one
41
+ * opened from a list whose own filters live in the query string.
42
+ */
15
43
  prefillFromUrl?: boolean;
44
+ /**
45
+ * Generate a placeholder ("Enter name", "name@example.com") for a field
46
+ * whose `placeholder` is unset. Default true. A placeholder that only
47
+ * repeats the label adds noise without information, so `ResourceForm`
48
+ * turns this off unless asked.
49
+ */
50
+ autoPlaceholder?: boolean;
16
51
  /** Validation trigger mode. Default "onBlur". */
17
52
  validationMode?: "onBlur" | "onChange" | "onSubmit";
18
53
  /** Enable autosave drafts to localStorage for create mode. */
19
54
  autosave?: boolean | {
20
55
  debounceMs?: number;
21
56
  };
57
+ /**
58
+ * Other query keys to invalidate after a save, on top of `[resource]` and
59
+ * the provider's own `invalidates`.
60
+ */
61
+ invalidates?: InvalidationKey[];
22
62
  }
23
63
  interface UseResourceFormReturn {
24
64
  form: UseFormReturn<Record<string, unknown>>;
@@ -30,6 +70,17 @@ interface UseResourceFormReturn {
30
70
  handleSave: (data: Record<string, unknown>) => Promise<void>;
31
71
  isSaving: boolean;
32
72
  isLoading: boolean;
73
+ /**
74
+ * The form has changes that have not been saved. False again once a save
75
+ * succeeds (or, in optimistic / undoable modes, is handed off).
76
+ */
77
+ isDirty: boolean;
78
+ /**
79
+ * For in-app navigation: true when it is fine to leave (nothing unsaved,
80
+ * `warnWhenUnsaved` off, or the user confirmed). Asks with
81
+ * `window.confirm` otherwise.
82
+ */
83
+ confirmLeave: () => boolean;
33
84
  }
34
85
  /** Build default values for a single array item from its sub-field definitions */
35
86
  declare function buildArrayItemDefaults(arrayFields: ResourceFormFieldDef[]): Record<string, unknown>;
@@ -1,7 +1,11 @@
1
- export { buildArrayItemDefaults, useResourceForm } from '../chunk-5FTUB2M6.js';
1
+ export { buildArrayItemDefaults, useResourceForm } from '../chunk-IZT2O57C.js';
2
+ import '../chunk-XQO4OH2N.js';
3
+ import '../chunk-5NHR6ZXS.js';
2
4
  import '../chunk-RYEXWQFD.js';
3
- import '../chunk-3P4PHPMJ.js';
4
- import '../chunk-6V2TIN3G.js';
5
+ import '../chunk-QH4P5MI2.js';
6
+ import '../chunk-7NUL32W7.js';
7
+ import '../chunk-ZK5C3OOT.js';
8
+ import '../chunk-GGSXSI6Z.js';
5
9
  import '../chunk-ZYJUEJ4Z.js';
6
10
  //# sourceMappingURL=use-resource-form.js.map
7
11
  //# sourceMappingURL=use-resource-form.js.map
@@ -1,8 +1,10 @@
1
1
  import { UseQueryResult, UseMutationResult } from '@tanstack/react-query';
2
- import { z as SortParam, k as FilterParam, p as ListResult, L as ListParams, I as Identifier, B as BaseRecord } from '../types-DUbRb8qV.js';
2
+ import { m as SortParam, F as FilterParam, j as InvalidationKey, k as ListResult, L as ListParams, I as Identifier, B as BaseRecord } from '../types-Dsqqr9s7.js';
3
+ import { MutationModeOptions } from './mutation-mode.js';
4
+ import '../types-C5bjsbxz.js';
3
5
  import '../form-types.js';
4
6
 
5
- interface UseResourceOptions<T> {
7
+ interface UseResourceOptions<T> extends MutationModeOptions {
6
8
  defaultSort?: SortParam;
7
9
  /**
8
10
  * Re-synced whenever its value changes (deep-equal, order-independent),
@@ -15,15 +17,42 @@ interface UseResourceOptions<T> {
15
17
  */
16
18
  defaultFilters?: FilterParam[];
17
19
  defaultPerPage?: number;
20
+ /** The page to start on (from 1), e.g. restored from the URL. Default 1. */
21
+ defaultPage?: number;
22
+ /** The search to start with, e.g. restored from the URL. Default "". */
23
+ defaultSearch?: string;
18
24
  /** Supabase select/join syntax, e.g. "*, author:profiles(name)". */
19
25
  select?: string;
20
26
  /** Fields to search across when setSearch is called. Required for server-side search. */
21
27
  searchFields?: string[];
22
28
  /** Disable auto-fetching. */
23
29
  enabled?: boolean;
30
+ /**
31
+ * Other query keys to invalidate after a create, update or delete, on top
32
+ * of `[resource]` and the provider's own `invalidates` (e.g. `[["me"]]`
33
+ * when a write changes the signed-in user's roles).
34
+ */
35
+ invalidates?: InvalidationKey[];
36
+ /**
37
+ * While the next page / sort / search is loading, keep returning the
38
+ * previous result (`list.isPlaceholderData` is true meanwhile) instead of
39
+ * dropping to `undefined`. A paged list then stays on screen between pages
40
+ * rather than flashing a skeleton. Default false.
41
+ */
42
+ keepPreviousData?: boolean;
43
+ /**
44
+ * After the write commits, with the provider's row. In `optimistic` and
45
+ * `undoable` modes that is after `create` / `update` / `remove` have
46
+ * already resolved.
47
+ */
24
48
  onCreateSuccess?: (data: T) => void;
25
49
  onUpdateSuccess?: (data: T) => void;
26
50
  onDeleteSuccess?: () => void;
51
+ /**
52
+ * A write failed. In `optimistic` and `undoable` modes this is the only
53
+ * place a failure is reported (the cache has already been put back), so
54
+ * show it to the user here.
55
+ */
27
56
  onError?: (error: Error) => void;
28
57
  }
29
58
  interface UseResourceReturn<T> {
@@ -36,11 +65,19 @@ interface UseResourceReturn<T> {
36
65
  setPage: (page: number) => void;
37
66
  setPerPage: (perPage: number) => void;
38
67
  setSearch: (query: string) => void;
39
- /** Create a new record. Returns the created item. */
68
+ /**
69
+ * Create a new record. Returns the created item. In `optimistic` and
70
+ * `undoable` modes it resolves at once with the values given (no `id` yet;
71
+ * `onCreateSuccess` gets the stored row).
72
+ */
40
73
  create: (data: Partial<T>) => Promise<T>;
41
- /** Update an existing record by id. Returns the updated item. */
74
+ /**
75
+ * Update an existing record by id. Returns the updated item. In
76
+ * `optimistic` and `undoable` modes it resolves at once with the cached
77
+ * row merged with `data`.
78
+ */
42
79
  update: (id: Identifier, data: Partial<T>) => Promise<T>;
43
- /** Delete a record by id. */
80
+ /** Delete a record by id. Resolves at once in `optimistic` / `undoable` modes. */
44
81
  remove: (id: Identifier) => Promise<void>;
45
82
  /** True when any mutation is in-flight. */
46
83
  isMutating: boolean;
@@ -1,5 +1,7 @@
1
- export { useResource } from '../chunk-QFTMHOEH.js';
2
- import '../chunk-6V2TIN3G.js';
1
+ export { useResource } from '../chunk-DVW5GXWZ.js';
2
+ import '../chunk-QH4P5MI2.js';
3
+ import '../chunk-7NUL32W7.js';
4
+ import '../chunk-GGSXSI6Z.js';
3
5
  import '../chunk-ZYJUEJ4Z.js';
4
6
  //# sourceMappingURL=use-resource.js.map
5
7
  //# sourceMappingURL=use-resource.js.map
@@ -0,0 +1,33 @@
1
+ declare const DEFAULT_UNSAVED_CHANGES_MESSAGE = "You have unsaved changes. Leave without saving?";
2
+ interface UseUnsavedChangesGuardOptions {
3
+ /** Guard while this is true (e.g. a form is dirty). */
4
+ when: boolean;
5
+ /** The question `confirmLeave` asks. */
6
+ message?: string;
7
+ /**
8
+ * How to ask. Default `window.confirm`. Must answer synchronously, since
9
+ * router blockers and `beforeunload` cannot wait.
10
+ */
11
+ confirm?: (message: string) => boolean;
12
+ }
13
+ interface UseUnsavedChangesGuardReturn {
14
+ /** True while leaving would lose changes. */
15
+ isBlocking: boolean;
16
+ /**
17
+ * Call before leaving in-app (closing a dialog, a router blocker). Returns
18
+ * true when it is fine to leave: nothing to lose, or the user said so.
19
+ */
20
+ confirmLeave: () => boolean;
21
+ }
22
+ /**
23
+ * Warns before unsaved changes are lost, without depending on a router.
24
+ *
25
+ * - While `when` is true, closing or reloading the tab shows the browser's
26
+ * own "Leave site?" prompt (`beforeunload`).
27
+ * - `confirmLeave()` is for in-app navigation: wire it to your router's
28
+ * blocker (React Router `useBlocker`, TanStack Router `useBlocker`), or call
29
+ * it before closing a dialog. See SKILL.md "Unsaved changes".
30
+ */
31
+ declare function useUnsavedChangesGuard(options: UseUnsavedChangesGuardOptions): UseUnsavedChangesGuardReturn;
32
+
33
+ export { DEFAULT_UNSAVED_CHANGES_MESSAGE, type UseUnsavedChangesGuardOptions, type UseUnsavedChangesGuardReturn, useUnsavedChangesGuard };
@@ -0,0 +1,3 @@
1
+ export { DEFAULT_UNSAVED_CHANGES_MESSAGE, useUnsavedChangesGuard } from '../chunk-XQO4OH2N.js';
2
+ //# sourceMappingURL=use-unsaved-changes-guard.js.map
3
+ //# sourceMappingURL=use-unsaved-changes-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"use-unsaved-changes-guard.js"}
package/dist/index.d.ts CHANGED
@@ -1,13 +1,17 @@
1
- export { A as ArrayFilter, a as AuditEntry, B as BaseRecord, b as BulkChange, c as BulkResult, C as CreateParams, d as CreateResult, D as DataProvider, e as DataRow, f as DeleteParams, g as DeleteResult, F as FieldPermissions, h as FieldReference, i as FieldSchema, j as FilterOperator, k as FilterParam, G as GetManyParams, l as GetManyResult, m as GetOneParams, n as GetOneResult, H as HistoryParams, I as Identifier, o as ImportParams, L as ListParams, p as ListResult, N as NullFilter, P as PaginationParam, q as ProviderCapabilities, R as RangeFilter, r as RecordComment, s as ResourceAccess, t as ResourceContract, u as ResourceRelation, v as ResourceSchema, w as ResourceSummary, x as RowAction, y as RowFailure, S as ScalarFilter, z as SortParam, E as StorageType, U as UpdateParams, J as UpdateResult, V as VersionField } from './types-DUbRb8qV.js';
2
- export { D as DataWriteError, a as DataWriteErrorOptions, W as WriteOutcome, i as isDataWriteError } from './write-outcome-aYToY3dK.js';
3
- export { DataProviderRoot, DataProviderRootProps, GenesisProvider, GenesisProviderProps, MockProviderOptions, SupabaseClient, SupabaseClientInput, createMockProvider, createSupabaseProvider, useDataProvider, useStorage } from './provider/index.js';
1
+ export { A as ArrayFilter, B as BaseRecord, a as BulkChange, b as BulkResult, C as CreateParams, c as CreateResult, D as DataProvider, d as DeleteParams, e as DeleteResult, F as FilterParam, G as GetManyParams, f as GetManyResult, g as GetOneParams, h as GetOneResult, H as HistoryParams, I as Identifier, i as ImportParams, j as InvalidationKey, L as ListParams, k as ListResult, N as NullFilter, P as PaginationParam, R as RangeFilter, l as RowFailure, S as ScalarFilter, m as SortParam, U as UpdateParams, n as UpdateResult, W as WriteEvent } from './types-Dsqqr9s7.js';
2
+ export { D as DataReadError, a as DataReadErrorOptions, b as DataWriteError, c as DataWriteErrorOptions, R as ReadOutcome, W as WriteOutcome, i as isDataReadError, d as isDataWriteError, r as readFailureMessage } from './read-outcome-BQkYcV4t.js';
3
+ export { DataProviderRoot, DataProviderRootProps, GenesisProvider, GenesisProviderProps, MockProviderOptions, PostgrestFieldErrors, SupabaseClient, SupabaseClientInput, SupabaseProviderOptions, SupabaseResourceConfig, SupabaseWriteContext, createMockProvider, createSupabaseProvider, fieldErrorsFromPostgrest, readOutcomeFromCode, toDataWriteError, useDataProvider, useStorage, writeOutcomeFromCode } from './provider/index.js';
4
4
  export { S as StorageAdapter } from './types-DKcxHXxq.js';
5
5
  export { createLocalStorageAdapter, createMemoryStorageAdapter, createNoopStorageAdapter } from './storage/index.js';
6
6
  export { UseResourceOptions, UseResourceReturn, useResource } from './hooks/use-resource.js';
7
7
  export { UseOneOptions, useOne } from './hooks/use-one.js';
8
+ export { useCapabilities } from './hooks/use-capabilities.js';
8
9
  export { UseResourceFormOptions, UseResourceFormReturn, buildArrayItemDefaults, useResourceForm } from './hooks/use-resource-form.js';
9
10
  export { UseWizardReturn, WizardStep, useWizard } from './hooks/use-wizard.js';
11
+ export { DEFAULT_UNDO_DELAY_MS, MutationMode, MutationModeOptions, UndoableWrite, pendingWriteCount } from './hooks/mutation-mode.js';
12
+ export { DEFAULT_UNSAVED_CHANGES_MESSAGE, UseUnsavedChangesGuardOptions, UseUnsavedChangesGuardReturn, useUnsavedChangesGuard } from './hooks/use-unsaved-changes-guard.js';
10
13
  export { FieldType, ResourceFormFieldDef, SelectOption, FieldType as WidgetHint } from './form-types.js';
14
+ export { A as AuditEntry, D as DataRow, F as FieldPermissions, a as FieldReference, b as FieldSchema, c as FilterOperator, P as ProviderCapabilities, R as RecordComment, d as ResourceAccess, e as ResourceContract, f as ResourceRelation, g as ResourceSchema, h as ResourceSummary, i as RowAction, S as StorageType, V as VersionField } from './types-C5bjsbxz.js';
11
15
  export { decodeRecordKey, encodeRecordKey, keyFields } from './schema/index.js';
12
16
  export { titleCase } from './utils.js';
13
17
  import 'react';
package/dist/index.js CHANGED
@@ -1,15 +1,20 @@
1
- export { createSupabaseProvider } from './chunk-6AA4ISUA.js';
2
- import './chunk-XCX2TRBL.js';
1
+ export { createSupabaseProvider, fieldErrorsFromPostgrest, readOutcomeFromCode, toDataWriteError, writeOutcomeFromCode } from './chunk-RLY7TZUY.js';
3
2
  export { decodeRecordKey, encodeRecordKey, keyFields } from './chunk-KV2HW4G4.js';
4
- export { DataWriteError, isDataWriteError } from './chunk-4W4W7F4I.js';
3
+ export { DataReadError, isDataReadError, readFailureMessage } from './chunk-HUNGCTRA.js';
4
+ import './chunk-XCX2TRBL.js';
5
5
  import './chunk-65SXNHWE.js';
6
- import './chunk-EL7YGOTY.js';
7
- export { buildArrayItemDefaults, useResourceForm } from './chunk-5FTUB2M6.js';
6
+ import './chunk-HT43MPKW.js';
7
+ export { buildArrayItemDefaults, useResourceForm } from './chunk-IZT2O57C.js';
8
+ export { useResource } from './chunk-DVW5GXWZ.js';
9
+ export { DEFAULT_UNSAVED_CHANGES_MESSAGE, useUnsavedChangesGuard } from './chunk-XQO4OH2N.js';
10
+ export { useWizard } from './chunk-7KOLYF27.js';
11
+ export { DataWriteError, isDataWriteError } from './chunk-5NHR6ZXS.js';
8
12
  export { titleCase } from './chunk-RYEXWQFD.js';
9
- export { useOne } from './chunk-3P4PHPMJ.js';
10
- export { useResource } from './chunk-QFTMHOEH.js';
11
- export { DataProviderRoot, GenesisProvider, createMockProvider, useDataProvider, useStorage } from './chunk-6V2TIN3G.js';
13
+ export { DEFAULT_UNDO_DELAY_MS, pendingWriteCount } from './chunk-QH4P5MI2.js';
14
+ import './chunk-7NUL32W7.js';
15
+ export { useCapabilities } from './chunk-BQOJX67X.js';
16
+ export { useOne } from './chunk-ZK5C3OOT.js';
17
+ export { DataProviderRoot, GenesisProvider, createMockProvider, useDataProvider, useStorage } from './chunk-GGSXSI6Z.js';
12
18
  export { createLocalStorageAdapter, createMemoryStorageAdapter, createNoopStorageAdapter } from './chunk-ZYJUEJ4Z.js';
13
- export { useWizard } from './chunk-7KOLYF27.js';
14
19
  //# sourceMappingURL=index.js.map
15
20
  //# sourceMappingURL=index.js.map