gemi 0.71.0 → 0.72.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.
Files changed (64) hide show
  1. package/dist/ai/index.js +1 -1
  2. package/dist/{chunk-qb1x1r58.js → chunk-0j8v3dd3.js} +1 -1
  3. package/dist/{chunk-ybtwp81a.js → chunk-akbawnmg.js} +1 -1
  4. package/dist/{chunk-ksxes55e.js → chunk-ebdtwzzr.js} +1 -1
  5. package/dist/{chunk-ep1jte2d.js → chunk-t2t2qcj4.js} +2 -2
  6. package/dist/{chunk-ep1jte2d.js.map → chunk-t2t2qcj4.js.map} +1 -1
  7. package/dist/chunk-t48gchew.js +5 -0
  8. package/dist/{chunk-3zraw16t.js.map → chunk-t48gchew.js.map} +5 -5
  9. package/dist/{chunk-0x9a9yha.js → chunk-vqxpknyg.js} +1 -1
  10. package/dist/chunks/{ThemeProvider-Bz-y1g2i.js → ThemeProvider-D9v0VAbY.js} +53 -4
  11. package/dist/chunks/ThemeProvider-D9v0VAbY.js.map +1 -0
  12. package/dist/client/Mutation.d.ts +8 -1
  13. package/dist/client/Mutation.d.ts.map +1 -1
  14. package/dist/client/MutationError.d.ts +109 -0
  15. package/dist/client/MutationError.d.ts.map +1 -0
  16. package/dist/client/QueryResource.d.ts +24 -0
  17. package/dist/client/QueryResource.d.ts.map +1 -1
  18. package/dist/client/auth/useForgotPassword.d.ts +1 -1
  19. package/dist/client/auth/useResetPassword.d.ts +1 -1
  20. package/dist/client/auth/useSignIn.d.ts +1 -1
  21. package/dist/client/auth/useSignOut.d.ts +1 -1
  22. package/dist/client/auth/useSignUp.d.ts +1 -1
  23. package/dist/client/index.d.ts +4 -0
  24. package/dist/client/index.d.ts.map +1 -1
  25. package/dist/client/index.js +284 -16
  26. package/dist/client/index.js.map +1 -1
  27. package/dist/client/useInfiniteQuery.d.ts +59 -0
  28. package/dist/client/useInfiniteQuery.d.ts.map +1 -0
  29. package/dist/client/useMutate.d.ts +7 -1
  30. package/dist/client/useMutate.d.ts.map +1 -1
  31. package/dist/client/useMutation.d.ts +6 -21
  32. package/dist/client/useMutation.d.ts.map +1 -1
  33. package/dist/client/useQuery.d.ts +4 -4
  34. package/dist/client/useQuery.d.ts.map +1 -1
  35. package/dist/facades/Storage.d.ts +6 -2
  36. package/dist/facades/Storage.d.ts.map +1 -1
  37. package/dist/facades/index.js +1 -1
  38. package/dist/http/index.js +1 -1
  39. package/dist/i18n/index.js +1 -1
  40. package/dist/kernel/index.js +1 -1
  41. package/dist/server/index.js +1 -1
  42. package/dist/services/file-storage/drivers/AzureBlobDriver.d.ts +2 -2
  43. package/dist/services/file-storage/drivers/AzureBlobDriver.d.ts.map +1 -1
  44. package/dist/services/file-storage/drivers/FileStorageDriver.d.ts +11 -1
  45. package/dist/services/file-storage/drivers/FileStorageDriver.d.ts.map +1 -1
  46. package/dist/services/file-storage/drivers/FileSystemDriver.d.ts +2 -1
  47. package/dist/services/file-storage/drivers/FileSystemDriver.d.ts.map +1 -1
  48. package/dist/services/file-storage/drivers/S3Driver.d.ts +2 -1
  49. package/dist/services/file-storage/drivers/S3Driver.d.ts.map +1 -1
  50. package/dist/services/file-storage/drivers/types.d.ts +9 -0
  51. package/dist/services/file-storage/drivers/types.d.ts.map +1 -1
  52. package/dist/services/index.d.ts +1 -1
  53. package/dist/services/index.d.ts.map +1 -1
  54. package/dist/services/index.js +5 -5
  55. package/dist/services/index.js.map +4 -4
  56. package/dist/testing/index.js +1 -1
  57. package/package.json +1 -1
  58. package/skills/gemi-react-best-practices/rules/controller-throw-framework-errors.md +3 -3
  59. package/dist/chunk-3zraw16t.js +0 -5
  60. package/dist/chunks/ThemeProvider-Bz-y1g2i.js.map +0 -1
  61. /package/dist/{chunk-qb1x1r58.js.map → chunk-0j8v3dd3.js.map} +0 -0
  62. /package/dist/{chunk-ybtwp81a.js.map → chunk-akbawnmg.js.map} +0 -0
  63. /package/dist/{chunk-ksxes55e.js.map → chunk-ebdtwzzr.js.map} +0 -0
  64. /package/dist/{chunk-0x9a9yha.js.map → chunk-vqxpknyg.js.map} +0 -0
@@ -0,0 +1,109 @@
1
+ /**
2
+ * What a failed mutation hands `onError` and puts in `error` — `useMutation`
3
+ * and its `usePost`/`usePut`/`usePatch`/`useDelete` shorthands, `useUpload`,
4
+ * and `<Form onError>`.
5
+ *
6
+ * It was typed `any` on `<Form>` and on the hooks' `error`, and as a union of
7
+ * `kind`s the server never sent (`not_authorized`, `insufficient_permissions`)
8
+ * on the hooks' `onError`. Apps sniffed the shape themselves, and got it wrong
9
+ * as easily as right. This is the shape the framework actually produces,
10
+ * member by member, read off the server's error payloads and the client's
11
+ * fetch code (issue #626).
12
+ *
13
+ * The value is exactly what the response's `error` field held, unchanged — a
14
+ * bare string for most refusals — so it is not tagged with the status it came
15
+ * with. Use the guards below rather than comparing strings, and
16
+ * `mutationErrorKind` to branch on all of them at once.
17
+ */
18
+ /** A server-side `ValidationError` (400): messages keyed by field. */
19
+ export interface MutationValidationError {
20
+ kind: "validation_error";
21
+ messages: Record<string, string[]>;
22
+ }
23
+ /**
24
+ * A single form-level message, which `<FormError>` renders. gemi never sends
25
+ * one itself; a `RequestBreakerError` of the app's own can.
26
+ */
27
+ export interface MutationFormError {
28
+ kind: "form_error";
29
+ message: string;
30
+ }
31
+ /** `useUpload` only: an upload that failed with a body that was not JSON. */
32
+ export interface MutationServerError {
33
+ kind: "server_error";
34
+ message: string;
35
+ }
36
+ /**
37
+ * A `{ message }` body: a missing record, file or route (404, `"Not found"`),
38
+ * a policy denial (403, `"Forbidden"`), a rate limit (429,
39
+ * `"Rate limit exceeded"`) or an unsatisfiable range (416).
40
+ */
41
+ export interface MutationMessageError {
42
+ kind?: undefined;
43
+ message: string;
44
+ }
45
+ /**
46
+ * Every value `onError` receives and `error` holds for a request gemi's server
47
+ * answered, or failed to reach:
48
+ *
49
+ * - `MutationValidationError`, `MutationFormError`, `MutationServerError` —
50
+ * tagged with `kind`.
51
+ * - `MutationMessageError` — `{ message }`, see above.
52
+ * - `string` — a refusal or failure answered with a bare string:
53
+ * `"Authentication error"` (401), `"Not authorized"` (401), `"Insufficient
54
+ * permissions"` (403), `"Invalid CSRF token"` (403), `"Internal Server
55
+ * Error"` (500; the exception's message under `gemi dev`). A refusal thrown
56
+ * with a message of its own arrives as that message.
57
+ * - `Error` — raised in the browser: a `TypeError` when the request never got
58
+ * an answer (offline, DNS, CORS), a `SyntaxError` when the answer was not
59
+ * JSON (a proxy's HTML error page, say). A throw from an `onSuccess` of
60
+ * yours is reported through `onError` as well, as whatever it threw.
61
+ *
62
+ * A cancelled request is not an error: `onCanceled` runs and `error` stays
63
+ * `null`. A route that answers an error body of its own shape is outside this
64
+ * type.
65
+ */
66
+ export type MutationError = MutationValidationError | MutationFormError | MutationServerError | MutationMessageError | string | Error;
67
+ /** What `mutationErrorKind` answers, one per guard below. */
68
+ export type MutationErrorKind = "validation" | "form" | "authentication" | "permission" | "csrf" | "not_found" | "rate_limit" | "server" | "network" | "unknown";
69
+ declare const AUTHENTICATION = "Authentication error";
70
+ declare const CSRF = "Invalid CSRF token";
71
+ declare const SERVER = "Internal Server Error";
72
+ type PermissionRefusal = "Insufficient permissions" | "Not authorized";
73
+ type MessageOf<M extends string> = MutationMessageError & {
74
+ message: M;
75
+ };
76
+ /** Field messages, as `<ValidationErrors>` renders them. */
77
+ export declare function isValidationError(error: unknown): error is MutationValidationError;
78
+ /** A form-level message, as `<FormError>` renders it. */
79
+ export declare function isFormError(error: unknown): error is MutationFormError;
80
+ /** No signed-in user: `AuthenticationError` (401). */
81
+ export declare function isAuthenticationError(error: unknown): error is typeof AUTHENTICATION;
82
+ /**
83
+ * A signed-in user who may not do this: `InsufficientPermissionsError` (403),
84
+ * `AuthorizationError` (401) or a policy denial (403, `{ message:
85
+ * "Forbidden" }`). Recognised by the framework's default messages.
86
+ */
87
+ export declare function isPermissionError(error: unknown): error is PermissionRefusal | MessageOf<"Forbidden">;
88
+ /** The CSRF cookie is missing or stale (403). A reload fixes it. */
89
+ export declare function isCsrfError(error: unknown): error is typeof CSRF;
90
+ /** A missing record, file or route (404). */
91
+ export declare function isNotFoundError(error: unknown): error is MessageOf<"Not found">;
92
+ /** `RateLimitMiddleware` refused the request (429). */
93
+ export declare function isRateLimitError(error: unknown): error is MessageOf<"Rate limit exceeded">;
94
+ /**
95
+ * The server failed (a production 500), or answered something that is not
96
+ * JSON. Under `gemi dev` a 500 carries the exception's message instead, which
97
+ * is `"unknown"`.
98
+ */
99
+ export declare function isServerError(error: unknown): error is typeof SERVER | MutationServerError | SyntaxError;
100
+ /** The request got no answer: offline, DNS, CORS, a dropped connection. */
101
+ export declare function isNetworkError(error: unknown): error is TypeError;
102
+ /**
103
+ * Which of the guards above `error` passes, for a `switch` over all of them.
104
+ * `"unknown"` is everything else: a refusal thrown with a message of its own,
105
+ * a 500 under `gemi dev`, an error body of the app's own shape.
106
+ */
107
+ export declare function mutationErrorKind(error: unknown): MutationErrorKind;
108
+ export {};
109
+ //# sourceMappingURL=MutationError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MutationError.d.ts","sourceRoot":"","sources":["../../client/MutationError.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,sEAAsE;AACtE,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,kBAAkB,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CACpC;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,YAAY,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,6EAA6E;AAC7E,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,MAAM,aAAa,GACrB,uBAAuB,GACvB,iBAAiB,GACjB,mBAAmB,GACnB,oBAAoB,GACpB,MAAM,GACN,KAAK,CAAC;AAEV,6DAA6D;AAC7D,MAAM,MAAM,iBAAiB,GACzB,YAAY,GACZ,MAAM,GACN,gBAAgB,GAChB,YAAY,GACZ,MAAM,GACN,WAAW,GACX,YAAY,GACZ,QAAQ,GACR,SAAS,GACT,SAAS,CAAC;AAUd,QAAA,MAAM,cAAc,yBAAyB,CAAC;AAK9C,QAAA,MAAM,IAAI,uBAAuB,CAAC;AAClC,QAAA,MAAM,MAAM,0BAA0B,CAAC;AAEvC,KAAK,iBAAiB,GAAG,0BAA0B,GAAG,gBAAgB,CAAC;AACvE,KAAK,SAAS,CAAC,CAAC,SAAS,MAAM,IAAI,oBAAoB,GAAG;IAAE,OAAO,EAAE,CAAC,CAAA;CAAE,CAAC;AAczE,4DAA4D;AAC5D,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,uBAAuB,CAElC;AAED,yDAAyD;AACzD,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,iBAAiB,CAEtE;AAED,sDAAsD;AACtD,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,OAAO,cAAc,CAEhC;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,iBAAiB,GAAG,SAAS,CAAC,WAAW,CAAC,CAKrD;AAED,oEAAoE;AACpE,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,OAAO,IAAI,CAEhE;AAED,6CAA6C;AAC7C,wBAAgB,eAAe,CAC7B,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,SAAS,CAAC,WAAW,CAAC,CAEjC;AAED,uDAAuD;AACvD,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,SAAS,CAAC,qBAAqB,CAAC,CAE3C;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,OAAO,MAAM,GAAG,mBAAmB,GAAG,WAAW,CAM5D;AAED,2EAA2E;AAC3E,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,SAAS,CAEjE;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,iBAAiB,CAWnE"}
@@ -33,6 +33,14 @@ export declare class QueryResource {
33
33
  * without waiting on the wire.
34
34
  */
35
35
  private pending;
36
+ /**
37
+ * How many mounted readers are rendering each variant. A mounted variant is
38
+ * on screen, so an invalidation refetches it now; one nobody renders is only
39
+ * marked stale and revalidates when something reads it next — invalidating
40
+ * every search variant a user browsed through must not put each of them on
41
+ * the wire at once.
42
+ */
43
+ private watchers;
36
44
  constructor(key: string, initialState: Record<string, any>);
37
45
  /**
38
46
  * Adopt server-prefetched data into the cache.
@@ -90,6 +98,22 @@ export declare class QueryResource {
90
98
  * re-throwing the stored failure.
91
99
  */
92
100
  clearError(variantKey?: string): void;
101
+ /**
102
+ * Mark a variant as rendered by a mounted reader until the returned release
103
+ * runs. `useQuery` and `useInfiniteQuery` hold one for every variant they
104
+ * put on screen.
105
+ */
106
+ retain(variantKey: string): () => void;
107
+ /** Every variant key this resource holds state for, in insertion order. */
108
+ variantKeys(): string[];
109
+ /**
110
+ * The many-variant counterpart of `mutate`. The update (when given) is
111
+ * applied to the variant's cached data right away, but only a variant a
112
+ * mounted reader is rendering refetches now; any other one is marked stale
113
+ * and revalidates — bypassing the HTTP cache — the next time it is read.
114
+ */
115
+ invalidate(variantKey: string, fn?: (data: any) => any): void;
116
+ private dropHttpCache;
93
117
  mutate(variantKey: string, fn?: (data: any) => any): void;
94
118
  refetch(variantKey: string): void;
95
119
  private resolveVariant;
@@ -1 +1 @@
1
- {"version":3,"file":"QueryResource.d.ts","sourceRoot":"","sources":["../../client/QueryResource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAG3C,KAAK,KAAK,GAAG;IACX,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,GAAG,CAAC;IACV;;;;;;OAMG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,GAAG,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAIF,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAEvC,qBAAa,aAAa;IACxB,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;IACnC,aAAa,cAAqB;IAClC,eAAe,sBAA6B;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,CAAqB;IACrC;;;;;OAKG;IACH,OAAO,CAAC,OAAO,CAA+B;gBAElC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAM1D;;;;;;;OAOG;IACH,OAAO,CAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,GAAG,SAAS;IAsC5D,OAAO,CAAC,UAAU;IAalB,OAAO,CAAC,MAAM;IAQd,OAAO,CAAC,OAAO;IAQf;;;;;;;;;;OAUG;IACH,IAAI,CAAC,UAAU,EAAE,MAAM;IAIvB;;;;;;;;OAQG;IACH,IAAI,CACF,UAAU,EAAE,MAAM,EAClB,SAAS,GAAE,MAA2B,GACrC;QAAE,KAAK,CAAC,EAAE,KAAK,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE;IA+B7C,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,GAAE,MAA2B;IA2BrE;;;;;;;;;;OAUG;IACH,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,GAAE,MAA2B;IAcrE;;;;OAIG;IACH,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM;IAe9B,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,GAAE,CAAC,IAAI,EAAE,GAAG,KAAK,GAAoB;IAwClE,OAAO,CAAC,UAAU,EAAE,MAAM;YAIZ,cAAc;CA6E7B"}
1
+ {"version":3,"file":"QueryResource.d.ts","sourceRoot":"","sources":["../../client/QueryResource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAG3C,KAAK,KAAK,GAAG;IACX,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,GAAG,CAAC;IACV;;;;;;OAMG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,GAAG,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAIF,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAEvC,qBAAa,aAAa;IACxB,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;IACnC,aAAa,cAAqB;IAClC,eAAe,sBAA6B;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,CAAqB;IACrC;;;;;OAKG;IACH,OAAO,CAAC,OAAO,CAA+B;IAC9C;;;;;;OAMG;IACH,OAAO,CAAC,QAAQ,CAA6B;gBAEjC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAM1D;;;;;;;OAOG;IACH,OAAO,CAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,GAAG,SAAS;IAsC5D,OAAO,CAAC,UAAU;IAalB,OAAO,CAAC,MAAM;IAQd,OAAO,CAAC,OAAO;IAQf;;;;;;;;;;OAUG;IACH,IAAI,CAAC,UAAU,EAAE,MAAM;IAIvB;;;;;;;;OAQG;IACH,IAAI,CACF,UAAU,EAAE,MAAM,EAClB,SAAS,GAAE,MAA2B,GACrC;QAAE,KAAK,CAAC,EAAE,KAAK,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE;IA+B7C,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,GAAE,MAA2B;IA2BrE;;;;;;;;;;OAUG;IACH,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,GAAE,MAA2B;IAcrE;;;;OAIG;IACH,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM;IAe9B;;;;OAIG;IACH,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,IAAI;IAetC,2EAA2E;IAC3E,WAAW,IAAI,MAAM,EAAE;IAIvB;;;;;OAKG;IACH,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,GAAG;IAetD,OAAO,CAAC,aAAa;IAiBrB,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,GAAE,CAAC,IAAI,EAAE,GAAG,KAAK,GAAoB;IA2BlE,OAAO,CAAC,UAAU,EAAE,MAAM;YAIZ,cAAc;CAgF7B"}
@@ -3,7 +3,7 @@ interface UseForgotPasswordArgs {
3
3
  }
4
4
  export declare function useForgotPassword(args?: UseForgotPasswordArgs): {
5
5
  data: {};
6
- error: any;
6
+ error: import("gemi/client").MutationError;
7
7
  loading: boolean;
8
8
  formData: FormData;
9
9
  cancel: () => void;
@@ -3,7 +3,7 @@ interface UseResetPasswordArgs {
3
3
  }
4
4
  export declare function useResetPassword(args?: UseResetPasswordArgs): {
5
5
  data: {};
6
- error: any;
6
+ error: import("gemi/client").MutationError;
7
7
  loading: boolean;
8
8
  formData: FormData;
9
9
  cancel: () => void;
@@ -12,7 +12,7 @@ export declare function useSignIn(args?: UseSignInArgs): {
12
12
  accounts: import("../../auth/types").Account[];
13
13
  extension: Record<string, any>;
14
14
  };
15
- error: any;
15
+ error: import("gemi/client").MutationError;
16
16
  loading: boolean;
17
17
  formData: FormData;
18
18
  cancel: () => void;
@@ -3,7 +3,7 @@ interface UseSignOutArgs {
3
3
  }
4
4
  export declare function useSignOut(args?: UseSignOutArgs): {
5
5
  data: {};
6
- error: any;
6
+ error: import("gemi/client").MutationError;
7
7
  loading: boolean;
8
8
  formData: FormData;
9
9
  cancel: () => void;
@@ -1,6 +1,6 @@
1
1
  export declare function useSignUp(): {
2
2
  data: import("../../auth/types").User;
3
- error: any;
3
+ error: import("gemi/client").MutationError;
4
4
  loading: boolean;
5
5
  formData: FormData;
6
6
  cancel: () => void;
@@ -1,9 +1,13 @@
1
1
  /// <reference path="../gemi.d.ts" />
2
2
  export { useQuery } from "./useQuery";
3
3
  export type { QueryResult, GemiQueryDefaults } from "./useQuery";
4
+ export { useInfiniteQuery } from "./useInfiniteQuery";
5
+ export type { InfiniteQueryConfig, InfiniteQueryReturn, } from "./useInfiniteQuery";
4
6
  export { QueryError } from "./QueryError";
5
7
  export { useMutation, useDelete, usePatch, usePost, usePut, useUpload } from "./useMutation";
6
8
  export { useMutate } from "./useMutate";
9
+ export { isValidationError, isFormError, isAuthenticationError, isPermissionError, isCsrfError, isNotFoundError, isRateLimitError, isServerError, isNetworkError, mutationErrorKind, } from "./MutationError";
10
+ export type { MutationError, MutationErrorKind, MutationValidationError, MutationFormError, MutationServerError, MutationMessageError, } from "./MutationError";
7
11
  export { Form, FormError, useMutationStatus, useFormStatus, useFormData, FormFieldContainer, ValidationErrors, } from "./Mutation";
8
12
  export { QueryManagerProvider } from "./QueryManagerContext";
9
13
  export type { QueryConfig } from "./QueryManagerContext";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../client/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../client/index.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { A as isAbsoluteUrl, B as QueryError, C as useNavigate, D as ServerDataProvider, E as ServerDataContext, F as applyParams$1, I as QueryConfigContext, L as QueryManagerContext, M as Action, O as useLocation, P as toVariantKey, R as QueryManagerProvider, S as useSearchParams, T as I18nProvider, V as Subject, _ as registerDictionary, a as m, b as routeDataUrl, c as ClientRouterContext, d as ComponentsProvider, f as initialViewModulesReady, g as loadDictionaryForRender, h as loadDictionary, i as WebSocketContextProvider, k as domainUrl, l as ClientRouterProvider, m as subscribeViewModules, n as useTheme, o as RouteTransitionProvider, p as loadViewModule, r as WebSocketContext, s as useRouteTransition, t as ThemeProvider, u as ComponentsContext, v as setActiveLocale, w as I18nContext, x as useRoute, y as readRoutePayload, z as DEFAULT_STALE_TIME } from "../chunks/ThemeProvider-Bz-y1g2i.js";
1
+ import { A as isAbsoluteUrl, B as QueryError, C as useNavigate, D as ServerDataProvider, E as ServerDataContext, F as applyParams$1, I as QueryConfigContext, L as QueryManagerContext, M as Action, O as useLocation, P as toVariantKey, R as QueryManagerProvider, S as useSearchParams, T as I18nProvider, V as Subject, _ as registerDictionary, a as m, b as routeDataUrl, c as ClientRouterContext, d as ComponentsProvider, f as initialViewModulesReady, g as loadDictionaryForRender, h as loadDictionary, i as WebSocketContextProvider, k as domainUrl, l as ClientRouterProvider, m as subscribeViewModules, n as useTheme, o as RouteTransitionProvider, p as loadViewModule, r as WebSocketContext, s as useRouteTransition, t as ThemeProvider, u as ComponentsContext, v as setActiveLocale, w as I18nContext, x as useRoute, y as readRoutePayload, z as DEFAULT_STALE_TIME } from "../chunks/ThemeProvider-D9v0VAbY.js";
2
2
  import { n as RouteStateContext, r as RouteStateProvider, t as useParams } from "../chunks/useParams-BN3XXfmG.js";
3
3
  import { Fragment, StrictMode, Suspense, createContext, createElement, isValidElement, memo, use, useCallback, useContext, useDeferredValue, useEffect, useMemo, useRef, useState, useSyncExternalStore, useTransition } from "react";
4
4
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -110,7 +110,14 @@ function useFrameworkQuery(url, ...args) {
110
110
  const focusEligibleRef = useRef(!lazy);
111
111
  const wasAwayRef = useRef(false);
112
112
  const lastFocusRevalidationRef = useRef(0);
113
- const subscribe = useCallback((onStoreChange) => resource.store.subscribe(onStoreChange), [resource]);
113
+ const subscribe = useCallback((onStoreChange) => {
114
+ const release = resource.retain(variantKey);
115
+ const unsubscribe = resource.store.subscribe(onStoreChange);
116
+ return () => {
117
+ unsubscribe();
118
+ release();
119
+ };
120
+ }, [resource, variantKey]);
114
121
  const getSnapshot = useCallback(() => resource.peek(variantKey), [resource, variantKey]);
115
122
  const snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
116
123
  const lastDataRef = useRef(snapshot?.hasData ? snapshot : null);
@@ -301,6 +308,179 @@ function useFrameworkQuery(url, ...args) {
301
308
  };
302
309
  }
303
310
  //#endregion
311
+ //#region client/useInfiniteQuery.ts
312
+ /**
313
+ * Follows the cursor chain through the cache: the first page, then
314
+ * `getNextPage` of it, and so on, up to `count` pages. Stops at the first page
315
+ * that has no data yet — that one is loading (or failed).
316
+ */
317
+ function walkPages(resource, firstKey, search, pageParam, count, getNextPage) {
318
+ const keys = [firstKey];
319
+ const states = [resource.peek(firstKey)];
320
+ const pages = [];
321
+ let next = null;
322
+ for (let i = 0; i < count; i++) {
323
+ const state = states[i];
324
+ if (!state?.hasData) break;
325
+ pages.push(state.data);
326
+ next = getNextPage(state.data, pages.slice());
327
+ if (next === null || next === void 0 || next === false) break;
328
+ if (i + 1 === count) break;
329
+ const key = toVariantKey({
330
+ ...search,
331
+ [pageParam]: next
332
+ });
333
+ keys.push(key);
334
+ states.push(resource.peek(key));
335
+ }
336
+ return {
337
+ keys,
338
+ states,
339
+ pages,
340
+ next
341
+ };
342
+ }
343
+ /**
344
+ * A paged list whose pages live in the query cache: page 1 is a plain
345
+ * `useQuery` (so it suspends, SSR-renders and `Query.prefetch`es exactly like
346
+ * one), and each further page is the same path with `pageParam` added to the
347
+ * search — its own cached variant, which `useMutate` updates and invalidates
348
+ * like any other. Changing `params` or `search` starts over from page 1.
349
+ */
350
+ function useInfiniteQuery(url, options, config) {
351
+ const { getNextPage, pageParam = "page", getItems, getKey, ...queryConfig } = config;
352
+ const first = useQuery(url, options, queryConfig);
353
+ const routeParams = useParams();
354
+ const params = options && "params" in options ? {
355
+ ...routeParams,
356
+ ...options.params
357
+ } : routeParams;
358
+ const search = options?.search ?? {};
359
+ const path = applyParams$1(url, params);
360
+ const firstKey = toVariantKey(search);
361
+ const listKey = `${path}?${firstKey}`;
362
+ const { getResource } = useContext(QueryManagerContext);
363
+ const appConfig = useContext(QueryConfigContext);
364
+ const { prefetchedData } = useRouteData();
365
+ const resource = getResource(path, prefetchedData?.[path] ?? void 0);
366
+ const [requested, setRequested] = useState({
367
+ listKey,
368
+ count: 1
369
+ });
370
+ const count = requested.listKey === listKey ? requested.count : 1;
371
+ const getNextPageRef = useRef(getNextPage);
372
+ getNextPageRef.current = getNextPage;
373
+ const snapshotRef = useRef(null);
374
+ const getSnapshot = useCallback(() => {
375
+ const walked = walkPages(resource, firstKey, search, pageParam, count, getNextPageRef.current);
376
+ const previous = snapshotRef.current;
377
+ if (previous && previous.keys.length === walked.keys.length && previous.keys.every((key, i) => key === walked.keys[i]) && previous.states.every((state, i) => state === walked.states[i])) return previous;
378
+ snapshotRef.current = walked;
379
+ return walked;
380
+ }, [
381
+ resource,
382
+ firstKey,
383
+ pageParam,
384
+ count
385
+ ]);
386
+ const chain = useSyncExternalStore(resource.store.subscribe, getSnapshot, getSnapshot);
387
+ const laterKeysId = chain.keys.slice(1).join("\n");
388
+ const staleTime = queryConfig.staleTime ?? appConfig?.staleTime;
389
+ const staleTimeRef = useRef(staleTime);
390
+ staleTimeRef.current = staleTime;
391
+ const ensuredRef = useRef({
392
+ listKey,
393
+ keys: /* @__PURE__ */ new Set()
394
+ });
395
+ useEffect(() => {
396
+ if (!laterKeysId) return;
397
+ if (ensuredRef.current.listKey !== listKey) ensuredRef.current = {
398
+ listKey,
399
+ keys: /* @__PURE__ */ new Set()
400
+ };
401
+ const ensured = ensuredRef.current.keys;
402
+ const keys = laterKeysId.split("\n");
403
+ const releases = keys.map((key) => resource.retain(key));
404
+ for (const key of keys) {
405
+ if (ensured.has(key)) continue;
406
+ ensured.add(key);
407
+ resource.getVariant(key, staleTimeRef.current);
408
+ }
409
+ return () => releases.forEach((release) => release());
410
+ }, [
411
+ resource,
412
+ listKey,
413
+ laterKeysId
414
+ ]);
415
+ const ownFirstPage = chain.states[0]?.hasData === true;
416
+ const fallbackPage = ownFirstPage ? void 0 : first.data;
417
+ const keepingPrevious = !ownFirstPage && fallbackPage !== void 0 && first.loading;
418
+ const shownRef = useRef(null);
419
+ const pages = useMemo(() => ownFirstPage ? chain.pages : keepingPrevious && shownRef.current ? shownRef.current : fallbackPage !== void 0 ? [fallbackPage] : [], [
420
+ ownFirstPage,
421
+ chain,
422
+ keepingPrevious,
423
+ fallbackPage
424
+ ]);
425
+ useEffect(() => {
426
+ if (ownFirstPage) shownRef.current = pages;
427
+ }, [ownFirstPage, pages]);
428
+ const pending = chain.states[chain.pages.length];
429
+ const hasMore = ownFirstPage && chain.pages.length === chain.keys.length && chain.next !== null && chain.next !== void 0 && chain.next !== false;
430
+ const isFetchingNextPage = ownFirstPage && chain.pages.length < chain.keys.length && !pending?.error;
431
+ const pageError = ownFirstPage && chain.pages.length < chain.keys.length ? pending?.error ?? null : null;
432
+ const items = useMemo(() => {
433
+ const out = [];
434
+ const seen = /* @__PURE__ */ new Set();
435
+ for (const page of pages) {
436
+ const rows = getItems ? getItems(page) : Array.isArray(page) ? page : [];
437
+ for (const row of rows) {
438
+ if (getKey) {
439
+ const key = getKey(row);
440
+ if (seen.has(key)) continue;
441
+ seen.add(key);
442
+ }
443
+ out.push(row);
444
+ }
445
+ }
446
+ return out;
447
+ }, [
448
+ pages,
449
+ getItems,
450
+ getKey
451
+ ]);
452
+ const fetchNextPage = useCallback(() => {
453
+ if (pageError) {
454
+ resource.refetch(chain.keys[chain.keys.length - 1]);
455
+ return;
456
+ }
457
+ if (!hasMore) return;
458
+ setRequested((current) => ({
459
+ listKey,
460
+ count: (current.listKey === listKey ? current.count : 1) + 1
461
+ }));
462
+ }, [
463
+ pageError,
464
+ hasMore,
465
+ resource,
466
+ chain,
467
+ listKey
468
+ ]);
469
+ const refetch = useCallback(() => {
470
+ for (const key of chain.keys) resource.refetch(key);
471
+ }, [resource, chain]);
472
+ return {
473
+ pages,
474
+ items,
475
+ hasMore,
476
+ fetchNextPage,
477
+ isFetchingNextPage,
478
+ loading: first.loading,
479
+ error: first.error ?? pageError,
480
+ refetch
481
+ };
482
+ }
483
+ //#endregion
304
484
  //#region client/useMutation.ts
305
485
  function applyParams(url, params = {}) {
306
486
  let out = url;
@@ -516,12 +696,37 @@ function useUpload(url, ...args) {
516
696
  }
517
697
  //#endregion
518
698
  //#region client/useMutate.ts
699
+ /**
700
+ * The shape guard both `mutate` forms apply: the callback's return value
701
+ * *replaces* the cached data, so it has to keep the data's shape — a stray
702
+ * value must not corrupt the cache. It does not merge or append.
703
+ */
704
+ function applyUpdate(data, fn) {
705
+ const updatedData = typeof fn === "function" ? fn(data) : fn;
706
+ if (isPlainObject(data)) {
707
+ if (isPlainObject(updatedData)) return updatedData;
708
+ throw new Error("Mutate function must return an object when the current data is an object.");
709
+ }
710
+ if (Array.isArray(data)) {
711
+ if (Array.isArray(updatedData)) return updatedData;
712
+ throw new Error("Mutate function must return an array when the current data is an array.");
713
+ }
714
+ if (typeof data !== typeof updatedData) throw new Error("Mutate function must return the same type as the current data.");
715
+ return updatedData;
716
+ }
519
717
  function useMutate() {
520
718
  const { getResource } = useContext(QueryManagerContext);
521
719
  return function mutate(options, fn) {
522
720
  const { path, params = {}, search = {} } = options ?? {};
523
721
  const normalPath = applyParams$1(path, params);
524
722
  const resource = getResource(normalPath);
723
+ if (typeof search === "function") {
724
+ for (const variantKey of resource.variantKeys()) {
725
+ if (!search(new URLSearchParams(variantKey))) continue;
726
+ resource.invalidate(variantKey, fn === void 0 ? void 0 : (data) => data === null ? data : applyUpdate(data, fn));
727
+ }
728
+ return;
729
+ }
525
730
  const variantKey = toVariantKey(search);
526
731
  return resource.mutate.call(resource, variantKey, (data) => {
527
732
  if (data === void 0 || data === null) {
@@ -529,21 +734,84 @@ function useMutate() {
529
734
  return data;
530
735
  }
531
736
  if (!fn) return data;
532
- const updatedData = typeof fn === "function" ? fn(data) : fn;
533
- if (isPlainObject(data)) {
534
- if (isPlainObject(updatedData)) return updatedData;
535
- throw new Error("Mutate function must return an object when the current data is an object.");
536
- }
537
- if (Array.isArray(data)) {
538
- if (Array.isArray(updatedData)) return updatedData;
539
- throw new Error("Mutate function must return an array when the current data is an array.");
540
- }
541
- if (typeof data !== typeof updatedData) throw new Error("Mutate function must return the same type as the current data.");
542
- return updatedData;
737
+ return applyUpdate(data, fn);
543
738
  });
544
739
  };
545
740
  }
546
741
  //#endregion
742
+ //#region client/MutationError.ts
743
+ var AUTHENTICATION = "Authentication error";
744
+ var PERMISSION = ["Insufficient permissions", "Not authorized"];
745
+ var CSRF = "Invalid CSRF token";
746
+ var SERVER = "Internal Server Error";
747
+ function isObject(error) {
748
+ return typeof error === "object" && error !== null;
749
+ }
750
+ function bodyMessage(error) {
751
+ return isObject(error) && !(error instanceof Error) && !("kind" in error) ? error.message : void 0;
752
+ }
753
+ /** Field messages, as `<ValidationErrors>` renders them. */
754
+ function isValidationError(error) {
755
+ return isObject(error) && error.kind === "validation_error";
756
+ }
757
+ /** A form-level message, as `<FormError>` renders it. */
758
+ function isFormError(error) {
759
+ return isObject(error) && error.kind === "form_error";
760
+ }
761
+ /** No signed-in user: `AuthenticationError` (401). */
762
+ function isAuthenticationError(error) {
763
+ return error === AUTHENTICATION;
764
+ }
765
+ /**
766
+ * A signed-in user who may not do this: `InsufficientPermissionsError` (403),
767
+ * `AuthorizationError` (401) or a policy denial (403, `{ message:
768
+ * "Forbidden" }`). Recognised by the framework's default messages.
769
+ */
770
+ function isPermissionError(error) {
771
+ return typeof error === "string" && PERMISSION.includes(error) || bodyMessage(error) === "Forbidden";
772
+ }
773
+ /** The CSRF cookie is missing or stale (403). A reload fixes it. */
774
+ function isCsrfError(error) {
775
+ return error === CSRF;
776
+ }
777
+ /** A missing record, file or route (404). */
778
+ function isNotFoundError(error) {
779
+ return bodyMessage(error) === "Not found";
780
+ }
781
+ /** `RateLimitMiddleware` refused the request (429). */
782
+ function isRateLimitError(error) {
783
+ return bodyMessage(error) === "Rate limit exceeded";
784
+ }
785
+ /**
786
+ * The server failed (a production 500), or answered something that is not
787
+ * JSON. Under `gemi dev` a 500 carries the exception's message instead, which
788
+ * is `"unknown"`.
789
+ */
790
+ function isServerError(error) {
791
+ return error === SERVER || isObject(error) && error.kind === "server_error" || error instanceof SyntaxError;
792
+ }
793
+ /** The request got no answer: offline, DNS, CORS, a dropped connection. */
794
+ function isNetworkError(error) {
795
+ return error instanceof TypeError;
796
+ }
797
+ /**
798
+ * Which of the guards above `error` passes, for a `switch` over all of them.
799
+ * `"unknown"` is everything else: a refusal thrown with a message of its own,
800
+ * a 500 under `gemi dev`, an error body of the app's own shape.
801
+ */
802
+ function mutationErrorKind(error) {
803
+ if (isValidationError(error)) return "validation";
804
+ if (isFormError(error)) return "form";
805
+ if (isAuthenticationError(error)) return "authentication";
806
+ if (isPermissionError(error)) return "permission";
807
+ if (isCsrfError(error)) return "csrf";
808
+ if (isNotFoundError(error)) return "not_found";
809
+ if (isRateLimitError(error)) return "rate_limit";
810
+ if (isServerError(error)) return "server";
811
+ if (isNetworkError(error)) return "network";
812
+ return "unknown";
813
+ }
814
+ //#endregion
547
815
  //#region client/Mutation.tsx
548
816
  var MutationContext = createContext({
549
817
  isPending: false,
@@ -623,8 +891,8 @@ function Form(props) {
623
891
  if (onSubmitStart?.(formData, formRef.current) === false) return;
624
892
  trigger(formData);
625
893
  };
626
- const validationErrors = error?.kind === "validation_error" ? error.messages : {};
627
- const formError = error?.kind === "form_error" ? error.message : null;
894
+ const validationErrors = isValidationError(error) ? error.messages : {};
895
+ const formError = isFormError(error) ? error.message : null;
628
896
  return /* @__PURE__ */ jsx(MutationContext.Provider, {
629
897
  value: {
630
898
  isPending: loading,
@@ -2058,6 +2326,6 @@ function useAppIdMissmatch() {
2058
2326
  return current !== next;
2059
2327
  }
2060
2328
  //#endregion
2061
- export { Form, FormError, FormFieldContainer, Head, Image, Link, OpenGraphImage, QueryError, QueryManagerProvider, Redirect, ValidationErrors, create, createRoot, defineDictionary, init, useAppIdMissmatch, useBreadcrumbs, useBroadcast, useDelete, useDictionary, useDomain, useFeature, useFeatures, useForgotPassword, useFormData, useFormStatus, useIntendedUrl, useIsNavigationPending, useLocale, useLocation, useMutate, useMutation, useMutationStatus, useNavigate, useNavigationProgress, useParams, usePatch, usePost, usePrefetch, usePut, useQuery, useResetPassword, useRoute, useRouteTransition, useSearchParams, useSignIn, useSignOut, useSignUp, useSubscription, useTheme, useTranslator, useUpload, useUser };
2329
+ export { Form, FormError, FormFieldContainer, Head, Image, Link, OpenGraphImage, QueryError, QueryManagerProvider, Redirect, ValidationErrors, create, createRoot, defineDictionary, init, isAuthenticationError, isCsrfError, isFormError, isNetworkError, isNotFoundError, isPermissionError, isRateLimitError, isServerError, isValidationError, mutationErrorKind, useAppIdMissmatch, useBreadcrumbs, useBroadcast, useDelete, useDictionary, useDomain, useFeature, useFeatures, useForgotPassword, useFormData, useFormStatus, useInfiniteQuery, useIntendedUrl, useIsNavigationPending, useLocale, useLocation, useMutate, useMutation, useMutationStatus, useNavigate, useNavigationProgress, useParams, usePatch, usePost, usePrefetch, usePut, useQuery, useResetPassword, useRoute, useRouteTransition, useSearchParams, useSignIn, useSignOut, useSignUp, useSubscription, useTheme, useTranslator, useUpload, useUser };
2062
2330
 
2063
2331
  //# sourceMappingURL=index.js.map