@gusnips/react 0.9.22 → 0.9.24
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/api-error.d.ts +1 -1
- package/dist/api-error.d.ts.map +1 -1
- package/dist/internal-path.d.ts.map +1 -1
- package/dist/internal-path.js +4 -2
- package/dist/internal-path.js.map +1 -1
- package/dist/query.js +1 -1
- package/package.json +2 -2
- package/src/api-error.ts +1 -1
- package/src/error-boundary.test.ts +48 -0
- package/src/internal-path.test.ts +1 -0
- package/src/internal-path.ts +4 -2
- package/src/query.ts +1 -1
package/dist/api-error.d.ts
CHANGED
package/dist/api-error.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-error.d.ts","sourceRoot":"","sources":["../src/api-error.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"api-error.d.ts","sourceRoot":"","sources":["../src/api-error.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD;;;;;;;;;;GAUG;AACH,qBAAa,QAAS,SAAQ,KAAK;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,SAAS,CAAC;IAC7D,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC;;;;;;;;;OASG;IACH,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IAE5C;;;;;;;;OAQG;IACH,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;gBAGzB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,YAAY,CAAC,OAAO,CAAC,GAAG,IAAI,EAClC,IAAI,GAAE;QACJ,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,cAAc,CAAC,EAAE,MAAM,CAAC;KACpB;CAaT;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAEpD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"internal-path.d.ts","sourceRoot":"","sources":["../src/internal-path.ts"],"names":[],"mappings":"AAUA;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAU9D;
|
|
1
|
+
{"version":3,"file":"internal-path.d.ts","sourceRoot":"","sources":["../src/internal-path.ts"],"names":[],"mappings":"AAUA;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAU9D;AAiBD,mGAAmG;AACnG,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,SAAS,GAAG,MAAM,CAKpF"}
|
package/dist/internal-path.js
CHANGED
|
@@ -30,8 +30,9 @@ export function safeInternalPath(value) {
|
|
|
30
30
|
}
|
|
31
31
|
/**
|
|
32
32
|
* The keys an OAuth or OIDC implicit response puts in the fragment that are themselves credentials
|
|
33
|
-
* (RFC 6749 §4.2.2, plus the two a provider token is passed through under
|
|
34
|
-
*
|
|
33
|
+
* (RFC 6749 §4.2.2, plus the two a provider token is passed through under, plus `token_hash`, the
|
|
34
|
+
* single-use hash a Supabase email link carries and `verifyOtp` trades for a session). The rest of
|
|
35
|
+
* that response — `expires_in`, `token_type`, `state`, `type` — is metadata, worthless on its own.
|
|
35
36
|
*/
|
|
36
37
|
const CREDENTIAL_FRAGMENT_KEYS = [
|
|
37
38
|
"access_token",
|
|
@@ -39,6 +40,7 @@ const CREDENTIAL_FRAGMENT_KEYS = [
|
|
|
39
40
|
"id_token",
|
|
40
41
|
"provider_token",
|
|
41
42
|
"provider_refresh_token",
|
|
43
|
+
"token_hash",
|
|
42
44
|
];
|
|
43
45
|
/**
|
|
44
46
|
* Where someone is right now, written down so signing in can bring them back.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"internal-path.js","sourceRoot":"","sources":["../src/internal-path.ts"],"names":[],"mappings":"AAAA,MAAM,eAAe,GAAG,0BAA0B,CAAC;AAEnD,SAAS,eAAe,CAAC,KAAa;IACpC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAChC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;IACjD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,eAAe,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAE/F,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC;QAC5C,IAAI,GAAG,CAAC,MAAM,KAAK,eAAe;YAAE,OAAO,IAAI,CAAC;QAChD,OAAO,GAAG,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IACnD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"internal-path.js","sourceRoot":"","sources":["../src/internal-path.ts"],"names":[],"mappings":"AAAA,MAAM,eAAe,GAAG,0BAA0B,CAAC;AAEnD,SAAS,eAAe,CAAC,KAAa;IACpC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAChC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;IACjD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,eAAe,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAE/F,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC;QAC5C,IAAI,GAAG,CAAC,MAAM,KAAK,eAAe;YAAE,OAAO,IAAI,CAAC;QAChD,OAAO,GAAG,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IACnD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,wBAAwB,GAAG;IAC/B,cAAc;IACd,eAAe;IACf,UAAU;IACV,gBAAgB;IAChB,wBAAwB;IACxB,YAAY;CACb,CAAC;AASF;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,sBAAsB,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAa;IAC1E,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7D,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,QAAQ,CAAC,CAAC;IAC7C,MAAM,iBAAiB,GAAG,wBAAwB,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IAClF,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,iBAAiB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AAChE,CAAC"}
|
package/dist/query.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gusnips/react",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.24",
|
|
4
4
|
"description": "The headless runtime under a Vite + React SPA: typed fetch client with single-flight refresh, auth store, query defaults, route guards, error boundary, stale-tab recovery, i18n bootstrap.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"release:major": "bun pm version major && bun publish --access public"
|
|
81
81
|
},
|
|
82
82
|
"dependencies": {
|
|
83
|
-
"@gusnips/http": "0.
|
|
83
|
+
"@gusnips/http": "0.2.0",
|
|
84
84
|
"clsx": "^2.1.1",
|
|
85
85
|
"tailwind-merge": "^3.6.0"
|
|
86
86
|
},
|
package/src/api-error.ts
CHANGED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { ErrorBoundary } from "./error-boundary.tsx";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The two statics are pure, which is why the reset rule lives in them: it can be checked with no
|
|
6
|
+
* DOM, no renderer and no thrown error.
|
|
7
|
+
*
|
|
8
|
+
* What is worth pinning is that a caught error belongs to the screen it happened on. Wrong one way,
|
|
9
|
+
* navigating away carries the wreckage to every page after it. Wrong the other way, the fallback
|
|
10
|
+
* clears itself at once and re-renders the component that just threw, which throws again: a
|
|
11
|
+
* flicker instead of a state. Lifted from an adopter's copy of this class, deleted when it moved
|
|
12
|
+
* onto the kit's.
|
|
13
|
+
*/
|
|
14
|
+
const props = (resetKey: string | undefined) => ({
|
|
15
|
+
resetKey,
|
|
16
|
+
fallback: () => null,
|
|
17
|
+
children: null,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe("ErrorBoundary", () => {
|
|
21
|
+
it("records the error that was thrown", () => {
|
|
22
|
+
const error = new TypeError("outcome is not a key of TONE");
|
|
23
|
+
expect(ErrorBoundary.getDerivedStateFromError(error)).toEqual({ error });
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("keeps the error while the route stays put", () => {
|
|
27
|
+
const state = { error: new Error("boom"), key: "/jobs/abc" };
|
|
28
|
+
expect(ErrorBoundary.getDerivedStateFromProps(props("/jobs/abc"), state)).toBeNull();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("clears the error when the route changes", () => {
|
|
32
|
+
const state = { error: new Error("boom"), key: "/jobs/abc" };
|
|
33
|
+
expect(ErrorBoundary.getDerivedStateFromProps(props("/keys"), state)).toEqual({
|
|
34
|
+
error: null,
|
|
35
|
+
key: "/keys",
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("tracks the route while nothing has thrown, so the first error is scoped to it", () => {
|
|
40
|
+
// Without this the key would still hold the route the boundary mounted on, and the first
|
|
41
|
+
// error on any later route would clear itself on the next re-render.
|
|
42
|
+
const state = { error: null, key: "/" };
|
|
43
|
+
expect(ErrorBoundary.getDerivedStateFromProps(props("/usage"), state)).toEqual({
|
|
44
|
+
error: null,
|
|
45
|
+
key: "/usage",
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
});
|
package/src/internal-path.ts
CHANGED
|
@@ -30,8 +30,9 @@ export function safeInternalPath(value: unknown): string | null {
|
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* The keys an OAuth or OIDC implicit response puts in the fragment that are themselves credentials
|
|
33
|
-
* (RFC 6749 §4.2.2, plus the two a provider token is passed through under
|
|
34
|
-
*
|
|
33
|
+
* (RFC 6749 §4.2.2, plus the two a provider token is passed through under, plus `token_hash`, the
|
|
34
|
+
* single-use hash a Supabase email link carries and `verifyOtp` trades for a session). The rest of
|
|
35
|
+
* that response — `expires_in`, `token_type`, `state`, `type` — is metadata, worthless on its own.
|
|
35
36
|
*/
|
|
36
37
|
const CREDENTIAL_FRAGMENT_KEYS = [
|
|
37
38
|
"access_token",
|
|
@@ -39,6 +40,7 @@ const CREDENTIAL_FRAGMENT_KEYS = [
|
|
|
39
40
|
"id_token",
|
|
40
41
|
"provider_token",
|
|
41
42
|
"provider_refresh_token",
|
|
43
|
+
"token_hash",
|
|
42
44
|
];
|
|
43
45
|
|
|
44
46
|
/** Anything shaped like the current page: `window.location` and react-router's `useLocation()`. */
|
package/src/query.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { retryDelayMs as delayFor,
|
|
1
|
+
import { retryDelayMs as delayFor, isRetryable as retryRule } from "@gusnips/http/retry";
|
|
2
2
|
import type { DefaultOptions } from "@tanstack/react-query";
|
|
3
3
|
import { ApiError } from "./api-error.ts";
|
|
4
4
|
|