tempest-react-sdk 0.82.0 → 0.83.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.
- package/README.md +6 -6
- package/bin/lib/openapi/generate.hostile.test.mjs +91 -0
- package/bin/lib/openapi/generate.mjs +42 -3
- package/dist/auth/create-tempest-auth.cjs +1 -1
- package/dist/auth/create-tempest-auth.cjs.map +1 -1
- package/dist/auth/create-tempest-auth.js +135 -132
- package/dist/auth/create-tempest-auth.js.map +1 -1
- package/dist/http/resumable-upload.cjs +1 -1
- package/dist/http/resumable-upload.cjs.map +1 -1
- package/dist/http/resumable-upload.js +124 -75
- package/dist/http/resumable-upload.js.map +1 -1
- package/dist/http/upload-with-progress.cjs +1 -1
- package/dist/http/upload-with-progress.cjs.map +1 -1
- package/dist/http/upload-with-progress.js +100 -44
- package/dist/http/upload-with-progress.js.map +1 -1
- package/dist/query/offline-persistence.cjs +1 -1
- package/dist/query/offline-persistence.cjs.map +1 -1
- package/dist/query/offline-persistence.js +57 -44
- package/dist/query/offline-persistence.js.map +1 -1
- package/dist/sw/cache.cjs +1 -1
- package/dist/sw/cache.cjs.map +1 -1
- package/dist/sw/cache.js +82 -32
- package/dist/sw/cache.js.map +1 -1
- package/dist/sw.d.ts +21 -1
- package/dist/tempest-react-sdk.d.ts +90 -6
- package/dist/vite/base-url.cjs +1 -1
- package/dist/vite/base-url.cjs.map +1 -1
- package/dist/vite/base-url.js +5 -1
- package/dist/vite/base-url.js.map +1 -1
- package/dist/vite/tempest-pwa-dev-sw.cjs +1 -1
- package/dist/vite/tempest-pwa-dev-sw.cjs.map +1 -1
- package/dist/vite/tempest-pwa-dev-sw.js +96 -31
- package/dist/vite/tempest-pwa-dev-sw.js.map +1 -1
- package/dist/vite/tempest-pwa-manifest.cjs +2 -2
- package/dist/vite/tempest-pwa-manifest.cjs.map +1 -1
- package/dist/vite/tempest-pwa-manifest.js +32 -16
- package/dist/vite/tempest-pwa-manifest.js.map +1 -1
- package/dist/vite.cjs +1 -1
- package/dist/vite.d.ts +63 -12
- package/dist/vite.js +4 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -276,7 +276,7 @@ Every module is re-exported from the package root — `import { Button, useDebou
|
|
|
276
276
|
| `router` _(dep: `react-router`)_ | `defineRoutes`, `AppRouter`, `RouteGuard`, + re-exports of React Router's whole client-side surface (`Link`, `NavLink`, `Outlet`, `useNavigate`, `useParams`, `useSearchParams`, `matchPath`, `generatePath`, `createBrowserRouter`, `useLoaderData`, …; its `Form` as `RouterForm`), types: `TempestRouteObject`, `RouterKind`, `AppRouterProps`, `RouteGuardProps` |
|
|
277
277
|
| `store` _(dep: `zustand`)_ | `createStore`, `createSelectors`, types: `CreateStoreOptions`, `CreateStorePersistOptions`, `WithSelectors` |
|
|
278
278
|
| `app` | `AppProviders` (composes `ErrorBoundary` → `QueryProvider` → `ThemeProvider` → `I18nProvider`), type: `AppProvidersProps` |
|
|
279
|
-
| `vite` _(subpath `tempest-react-sdk/vite`)_ | `createViteConfig`, `tempestPwaManifest` (emits `precache-manifest.json` for offline precache), `tempestPwaIcons` (generates the PNG icon set from one SVG via `sharp`), `tempestPwaDevSw` (serves the SW under `npm run dev`), `tempestCsp` (writes a Content-Security-Policy whose `connect-src` is derived from the app's `VITE_*` URLs, enforced in dev too), `tempestPwaIcons({ appleSplash })` (Apple splash screens), types: `CreateViteConfigOptions`, `ProxyEntry`, `TempestViteConfig`, `TempestPwaManifestOptions`, `TempestPwaIconsOptions`, `TempestPwaDevSwOptions`, `TempestCspOptions`, `TempestCspPolicy`, `TempestCspSource`, `AppleSplashSpec`, `TempestVitePlugin`
|
|
279
|
+
| `vite` _(subpath `tempest-react-sdk/vite`)_ | `createViteConfig`, `tempestPwaManifest` (emits `precache-manifest.json` for offline precache), `tempestPwaIcons` (generates the PNG icon set from one SVG via `sharp`), `tempestPwaDevSw` (serves the SW under `npm run dev` and bundles it into `vite build`), `tempestCsp` (writes a Content-Security-Policy whose `connect-src` is derived from the app's `VITE_*` URLs, enforced in dev too), `tempestPwaIcons({ appleSplash })` (Apple splash screens), types: `CreateViteConfigOptions`, `ProxyEntry`, `TempestViteConfig`, `TempestPwaManifestOptions`, `TempestPwaIconsOptions`, `TempestPwaDevSwOptions`, `TempestCspOptions`, `TempestCspPolicy`, `TempestCspSource`, `AppleSplashSpec`, `TempestVitePlugin` |
|
|
280
280
|
| `forms` _(peer: `zod`, `react-hook-form`)_ | `validateForm`, `zodResolver`, `useZodForm`, `validateCPF`, `validateCNPJ`, `formatCEP`, `formatCNPJ`, `unmask`, `CPFInput`, `CNPJInput`, `PhoneInput`, `CEPInput`, `MoneyInput`, `useViaCEP` |
|
|
281
281
|
| `sse` | `createEventStream`, `useEventStream` (`schema` valida cada frame — zod/valibot/arktype via Standard Schema — e `onParseError` para frame que não é JSON) |
|
|
282
282
|
| `ws` | `createWebSocket` (handshake timeout, silence watchdog, jittered backoff, `navigator.onLine` suspend, close-code classification, `opened` promise), `useWebSocket`, `schema` + `onValidationError` (frame fora do contrato não é entregue; o `pong` do heartbeat continua saindo), `onParseError` (frame que não é JSON: descarta em vez de entregar `string` como `T`), `isRejectionCloseCode`, `HEARTBEAT_CLOSE_CODE` |
|
|
@@ -312,7 +312,7 @@ Every module is re-exported from the package root — `import { Button, useDebou
|
|
|
312
312
|
| `router` _(dep: `react-router`)_ | `defineRoutes`, `AppRouter`, `RouteGuard`, + re-exports of React Router's whole client-side surface (`Link`, `NavLink`, `Outlet`, `useNavigate`, `useParams`, `useSearchParams`, `matchPath`, `generatePath`, `createBrowserRouter`, `useLoaderData`, …; its `Form` as `RouterForm`), types: `TempestRouteObject`, `RouterKind`, `AppRouterProps`, `RouteGuardProps` |
|
|
313
313
|
| `store` _(dep: `zustand`)_ | `createStore`, `createSelectors`, types: `CreateStoreOptions`, `CreateStorePersistOptions`, `WithSelectors` |
|
|
314
314
|
| `app` | `AppProviders` (composes `ErrorBoundary` → `QueryProvider` → `ThemeProvider` → `I18nProvider`), type: `AppProvidersProps` |
|
|
315
|
-
| `vite` _(subpath `tempest-react-sdk/vite`)_ | `createViteConfig`, `tempestPwaManifest` (emits `precache-manifest.json` for offline precache), `tempestPwaIcons` (generates the PNG icon set from one SVG via `sharp`), `tempestPwaDevSw` (serves the SW under `npm run dev`), `tempestPwaIcons({ appleSplash })` (Apple splash screens), types: `CreateViteConfigOptions`, `ProxyEntry`, `TempestViteConfig`, `TempestPwaManifestOptions`, `TempestPwaIconsOptions`, `TempestPwaDevSwOptions`, `AppleSplashSpec`, `TempestVitePlugin`
|
|
315
|
+
| `vite` _(subpath `tempest-react-sdk/vite`)_ | `createViteConfig`, `tempestPwaManifest` (emits `precache-manifest.json` for offline precache), `tempestPwaIcons` (generates the PNG icon set from one SVG via `sharp`), `tempestPwaDevSw` (serves the SW under `npm run dev` and bundles it into `vite build`), `tempestPwaIcons({ appleSplash })` (Apple splash screens), types: `CreateViteConfigOptions`, `ProxyEntry`, `TempestViteConfig`, `TempestPwaManifestOptions`, `TempestPwaIconsOptions`, `TempestPwaDevSwOptions`, `AppleSplashSpec`, `TempestVitePlugin` |
|
|
316
316
|
| `forms` _(peer: `zod`, `react-hook-form`)_ | `validateForm`, `zodResolver`, `useZodForm`, `validateCPF`, `validateCNPJ`, `formatCEP`, `formatCNPJ`, `unmask`, `CPFInput`, `CNPJInput`, `PhoneInput`, `CEPInput`, `MoneyInput`, `useViaCEP` |
|
|
317
317
|
| `sse` | `createEventStream`, `useEventStream` (`schema` valida cada frame — zod/valibot/arktype via Standard Schema — e `onParseError` para frame que não é JSON) |
|
|
318
318
|
| `ws` | `createWebSocket` (handshake timeout, silence watchdog, jittered backoff, `navigator.onLine` suspend, close-code classification, `opened` promise), `useWebSocket`, `schema` + `onValidationError` (frame fora do contrato não é entregue; o `pong` do heartbeat continua saindo), `onParseError` (frame que não é JSON: descarta em vez de entregar `string` como `T`), `isRejectionCloseCode`, `HEARTBEAT_CLOSE_CODE` |
|
|
@@ -343,7 +343,7 @@ Every module is re-exported from the package root — `import { Button, useDebou
|
|
|
343
343
|
|
|
344
344
|
Full per-module docs are published as a bilingual MkDocs site on GitHub Pages — **[Português (BR)](https://mauriciobenjamin700.github.io/tempest-react-sdk/)** / **[English (US)](https://mauriciobenjamin700.github.io/tempest-react-sdk/en/)** (one page per module + draw.io diagrams in [`docs/diagrams/`](./docs/diagrams)). The source markdown lives in [`docs/`](./docs) (PT-BR base files + `.en.md` translations).
|
|
345
345
|
|
|
346
|
-
> **Local preview:** `pip install -r docs/requirements.txt && mkdocs serve` (the published site is built and
|
|
346
|
+
> **Local preview:** `pip install -r docs/requirements.txt && mkdocs serve` (the published site is built and pushed to `gh-pages` by `make docs-deploy`, from the maintainer's machine).
|
|
347
347
|
|
|
348
348
|
A demo app exercising every module lives in [`examples/gallery`](./examples/gallery) — `cd examples/gallery && npm install && npm run dev`.
|
|
349
349
|
|
|
@@ -1321,7 +1321,7 @@ installNotificationClickHandler({
|
|
|
1321
1321
|
});
|
|
1322
1322
|
```
|
|
1323
1323
|
|
|
1324
|
-
The SDK does **not** ship the worker file
|
|
1324
|
+
The SDK does **not** ship the worker file — you write `src/sw.ts`. `tempestPwaDevSw()` from `tempest-react-sdk/vite` bundles it to `dist/sw.js` in `vite build` (and serves it in dev), and `tempestPwaManifest()` fails the build when the manifest would ship without a worker. See [Vite Config › PWA](https://mauriciobenjamin700.github.io/tempest-react-sdk/en/vite-config/#pwa-the-service-worker-in-the-production-build).
|
|
1325
1325
|
|
|
1326
1326
|
### Audio playback recipe
|
|
1327
1327
|
|
|
@@ -1999,10 +1999,10 @@ Two commands, with the release PR's review in between:
|
|
|
1999
1999
|
|
|
2000
2000
|
```bash
|
|
2001
2001
|
make release TAG=X.Y.Z # opens the release PR (no tag, no publish)
|
|
2002
|
-
make release-tag TAG=X.Y.Z # after the merge:
|
|
2002
|
+
make release-tag TAG=X.Y.Z # after the merge: build + smokes + npm publish + tag + GitHub Release
|
|
2003
2003
|
```
|
|
2004
2004
|
|
|
2005
|
-
|
|
2005
|
+
There is no GitHub Actions in this repository: `make release-tag` runs the ref guard, builds and smokes the merged commit, publishes the smoked tarball with the maintainer's npm credential (no provenance — that needs a CI runner's OIDC), pushes the tag and creates the GitHub Release. A bare `npm publish` is refused (`make publish` exits 1). Pipeline and the repository owner's settings checklist: [Release pipeline](https://mauriciobenjamin700.github.io/tempest-react-sdk/en/release/).
|
|
2006
2006
|
|
|
2007
2007
|
Tags follow `vMAJOR.MINOR.PATCH`. CSS tokens (`--tempest-*`) are treated as public API — renames bump the minor (or major, on removal).
|
|
2008
2008
|
|
|
@@ -724,6 +724,97 @@ describe("generate — spec text that would leave its place in the output (#533)
|
|
|
724
724
|
);
|
|
725
725
|
});
|
|
726
726
|
|
|
727
|
+
const KEYWORD_NAMES = [
|
|
728
|
+
"pattern",
|
|
729
|
+
"default",
|
|
730
|
+
"enum",
|
|
731
|
+
"items",
|
|
732
|
+
"properties",
|
|
733
|
+
"additionalProperties",
|
|
734
|
+
"allOf",
|
|
735
|
+
"anyOf",
|
|
736
|
+
"oneOf",
|
|
737
|
+
"required",
|
|
738
|
+
"type",
|
|
739
|
+
"$ref",
|
|
740
|
+
];
|
|
741
|
+
|
|
742
|
+
/**
|
|
743
|
+
* A `Body` schema with one property, named `name`, whose schema is `inner`.
|
|
744
|
+
*
|
|
745
|
+
* @param {string} name - The property name, often a JSON Schema keyword.
|
|
746
|
+
* @param {object} inner - The property's schema.
|
|
747
|
+
* @returns {object} The spec.
|
|
748
|
+
*/
|
|
749
|
+
function keywordNamedProperty(name, inner) {
|
|
750
|
+
return spec(POST_BODY, {
|
|
751
|
+
Body: { type: "object", required: [name], properties: { [name]: inner } },
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
it.each(KEYWORD_NAMES)(
|
|
756
|
+
"a property named %s holding a required __proto__ is refused, naming where it sits (#562)",
|
|
757
|
+
(name) => {
|
|
758
|
+
const inner = JSON.parse(
|
|
759
|
+
'{"type":"object","required":["__proto__"],"properties":{"__proto__":{"type":"string"}}}',
|
|
760
|
+
);
|
|
761
|
+
expect(() => generate(keywordNamedProperty(name, inner))).toThrow(
|
|
762
|
+
`schema "Body" at properties.${name}: a property named "__proto__" cannot be validated`,
|
|
763
|
+
);
|
|
764
|
+
},
|
|
765
|
+
);
|
|
766
|
+
|
|
767
|
+
it.each(KEYWORD_NAMES)(
|
|
768
|
+
"a property named %s whose schema has a __proto__ default is refused at that property (#562)",
|
|
769
|
+
(name) => {
|
|
770
|
+
const inner = JSON.parse('{"type":"object","default":{"__proto__":{"admin":true}}}');
|
|
771
|
+
expect(() => generate(keywordNamedProperty(name, inner))).toThrow(
|
|
772
|
+
`schema "Body" at properties.${name}: the default value has a "__proto__" key`,
|
|
773
|
+
);
|
|
774
|
+
},
|
|
775
|
+
);
|
|
776
|
+
|
|
777
|
+
it.each(KEYWORD_NAMES)(
|
|
778
|
+
"an invalid regex under a property named %s fails generation (#562)",
|
|
779
|
+
(name) => {
|
|
780
|
+
const inner = {
|
|
781
|
+
type: "object",
|
|
782
|
+
properties: { code: { type: "string", pattern: "(" } },
|
|
783
|
+
};
|
|
784
|
+
expect(() => generate(keywordNamedProperty(name, inner))).toThrow(
|
|
785
|
+
`schema "Body" at properties.${name}.properties.code: pattern "(" is not a valid`,
|
|
786
|
+
);
|
|
787
|
+
},
|
|
788
|
+
);
|
|
789
|
+
|
|
790
|
+
it.each(KEYWORD_NAMES)(
|
|
791
|
+
"a nested-quantifier pattern under a property named %s comes back as a warning (#562)",
|
|
792
|
+
(name) => {
|
|
793
|
+
const inner = { type: "string", pattern: "^(a+)+$" };
|
|
794
|
+
const { warnings } = generate(keywordNamedProperty(name, inner));
|
|
795
|
+
expect(warnings).toHaveLength(1);
|
|
796
|
+
expect(warnings[0]).toContain(`schema "Body" at properties.${name}: pattern`);
|
|
797
|
+
},
|
|
798
|
+
);
|
|
799
|
+
|
|
800
|
+
it("properties named after keywords generate, typecheck and are each validated (#562)", () => {
|
|
801
|
+
const properties = Object.fromEntries(
|
|
802
|
+
KEYWORD_NAMES.map((name) => [name, { type: "string", minLength: 2 }]),
|
|
803
|
+
);
|
|
804
|
+
const generated = spec(POST_BODY, {
|
|
805
|
+
Body: { type: "object", required: KEYWORD_NAMES, properties },
|
|
806
|
+
});
|
|
807
|
+
const { files, warnings } = generate(generated);
|
|
808
|
+
expect(warnings).toEqual([]);
|
|
809
|
+
expect(typecheck(files, "t")).toEqual([]);
|
|
810
|
+
const schemas = evaluate(files["t/schemas.ts"], vm.createContext({}), {});
|
|
811
|
+
const valid = Object.fromEntries(KEYWORD_NAMES.map((name) => [name, "ok"]));
|
|
812
|
+
expect(schemas.BodySchema.safeParse(valid).success).toBe(true);
|
|
813
|
+
for (const name of KEYWORD_NAMES) {
|
|
814
|
+
expect(schemas.BodySchema.safeParse({ ...valid, [name]: {} }).success).toBe(false);
|
|
815
|
+
}
|
|
816
|
+
});
|
|
817
|
+
|
|
727
818
|
it("properties called constructor or prototype still generate and are validated", () => {
|
|
728
819
|
const { files } = generate(
|
|
729
820
|
spec(POST_BODY, {
|
|
@@ -175,6 +175,23 @@ function hasProtoKey(value) {
|
|
|
175
175
|
return Object.values(value).some(hasProtoKey);
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
+
/**
|
|
179
|
+
* Keywords whose value maps a name the spec author picked to a sub-schema.
|
|
180
|
+
*
|
|
181
|
+
* Their keys are data, not keywords: a property may be called `pattern`,
|
|
182
|
+
* `default` or `$ref`. {@link checkSchema} walks into every entry as a schema
|
|
183
|
+
* and never reads the map itself as one — it used to, so a property named
|
|
184
|
+
* `pattern` or `default` was skipped as if it were the keyword, and its schema
|
|
185
|
+
* escaped every check below (#562).
|
|
186
|
+
*/
|
|
187
|
+
const SCHEMA_MAPS = new Set([
|
|
188
|
+
"properties",
|
|
189
|
+
"patternProperties",
|
|
190
|
+
"dependentSchemas",
|
|
191
|
+
"$defs",
|
|
192
|
+
"definitions",
|
|
193
|
+
]);
|
|
194
|
+
|
|
178
195
|
/**
|
|
179
196
|
* Check a component schema before it is turned into Zod source.
|
|
180
197
|
*
|
|
@@ -194,6 +211,11 @@ function hasProtoKey(value) {
|
|
|
194
211
|
*
|
|
195
212
|
* Every failure names the schema and where in it the problem sits.
|
|
196
213
|
*
|
|
214
|
+
* The walk reaches every sub-schema. Only a schema's own `pattern` string and
|
|
215
|
+
* `default` value are not walked as schemas — the checks above read them — and
|
|
216
|
+
* the entries of a {@link SCHEMA_MAPS} keyword are always walked, whatever the
|
|
217
|
+
* property is called (#562).
|
|
218
|
+
*
|
|
197
219
|
* @param {unknown} node - The schema (sub)node.
|
|
198
220
|
* @param {string} schema - The component schema name, for the messages.
|
|
199
221
|
* @param {string[]} warnings - Collects the non-fatal findings.
|
|
@@ -233,15 +255,32 @@ function checkSchema(node, schema, warnings, at = []) {
|
|
|
233
255
|
);
|
|
234
256
|
}
|
|
235
257
|
for (const [key, value] of Object.entries(node)) {
|
|
236
|
-
if (key
|
|
258
|
+
if (SCHEMA_MAPS.has(key) && value && typeof value === "object" && !Array.isArray(value)) {
|
|
259
|
+
for (const [name, sub] of Object.entries(value)) {
|
|
260
|
+
checkSchema(sub, schema, warnings, [...at, key, name]);
|
|
261
|
+
}
|
|
262
|
+
} else if (key !== "pattern" && key !== "default") {
|
|
237
263
|
checkSchema(value, schema, warnings, [...at, key]);
|
|
264
|
+
}
|
|
238
265
|
}
|
|
239
266
|
}
|
|
240
267
|
|
|
241
|
-
/**
|
|
268
|
+
/**
|
|
269
|
+
* Collect component-schema names referenced (transitively) by a schema node.
|
|
270
|
+
*
|
|
271
|
+
* Only a string `$ref` is a reference: inside `properties`, `$ref` can be a
|
|
272
|
+
* property name whose value is a schema, and reading it as a reference crashed
|
|
273
|
+
* the run with `ref.lastIndexOf is not a function` (#562).
|
|
274
|
+
*
|
|
275
|
+
* @param {unknown} node - The schema (sub)node.
|
|
276
|
+
* @param {Record<string, object>} schemas - `components.schemas`.
|
|
277
|
+
* @param {Set<string>} [acc] - Collects the referenced names.
|
|
278
|
+
* @param {Set<string>} [seen] - Names already expanded, against cycles.
|
|
279
|
+
* @returns {Set<string>} `acc`.
|
|
280
|
+
*/
|
|
242
281
|
function collectRefs(node, schemas, acc = new Set(), seen = new Set()) {
|
|
243
282
|
if (!node || typeof node !== "object") return acc;
|
|
244
|
-
if (node.$ref) {
|
|
283
|
+
if (typeof node.$ref === "string") {
|
|
245
284
|
const name = refName(node.$ref);
|
|
246
285
|
if (!acc.has(name)) {
|
|
247
286
|
acc.add(name);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../utils/dev-mode.cjs"),t=require("../http/errors.cjs"),n=require("../http/api-client.cjs"),r=require("./session-identity.cjs"),i=require("./create-auth-store.cjs"),a=require("./refresh-queue.cjs");var o=1e4,s=1e4;function c(e,t){return e.length===0?Promise.resolve():new Promise(n=>{let r=setTimeout(n,t);Promise.allSettled(e).then(()=>{clearTimeout(r),n()})})}function
|
|
1
|
+
const e=require("../utils/dev-mode.cjs"),t=require("../http/errors.cjs"),n=require("../http/api-client.cjs"),r=require("./session-identity.cjs"),i=require("./create-auth-store.cjs"),a=require("./refresh-queue.cjs");var o=1e4,s=1e4;function c(e,t){return e.length===0?Promise.resolve():new Promise(n=>{let r=setTimeout(n,t);Promise.allSettled(e).then(()=>{clearTimeout(r),n()})})}function l(){let e=globalThis.crypto;return typeof e?.randomUUID==`function`?e.randomUUID():`${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}var u=new Set;function ee(t){e.isDevBuild()&&!u.has(t)&&(u.add(t),console.warn(`[tempest-react-sdk] createTempestAuth: withCredentials is set without logoutPath. logout() cannot clear the HttpOnly refresh cookie, so the next refresh() — the boot restore — signs the same account back in. Pass logoutPath (the backend's cookie logout route) so the server revokes it.`))}function te(e){let t=e??{};return{token:t.access_token,refreshToken:t.refresh_token}}function d(u){let{baseURL:d,prefix:f,loginPath:ne=`/api/auth/login`,refreshPath:re=`/api/auth/refresh`,logoutPath:p,onLogoutError:ie,mePath:m,storeName:h=`tempest-auth`,storage:g=`local`,withCredentials:_=!1,csrf:ae,fetcher:v,parseTokens:y=te,parseUser:oe,refreshBody:b=e=>e?{refresh_token:e}:void 0,retry:se,redirectTo:x,onSessionEnd:S}=u;_&&!p&&ee(h);let C=i.createAuthStore({name:h,storage:g}),w=`${h}-refresh`;function T(){return typeof window>`u`?null:g===`session`?window.sessionStorage:window.localStorage}function E(){return T()?.getItem(w)??null}let D=E();function O(e){D=e;let t=T();t&&(e?t.setItem(w,e):t.removeItem(w))}let k=`${h}-lineage`,ce=`tempest-react-sdk:${h}:refresh`,A=T()?.getItem(k)??null;function j(e){A=e;let t=T();t&&(e?t.setItem(k,e):t.removeItem(k))}let M=()=>C.getState(),N=()=>M().token;async function le(){if(!m)return M().user;let e=P,n;try{n=await Q.get(m,{skipAuthRetry:!0})}catch(n){throw P===e?n:new t.SessionSupersededError}if(P!==e||!Z().continues)throw new t.SessionSupersededError;return M().setUser(n),n}let P=0,ue=Promise.resolve(),F=0,I=new Set,L=new Set,R=new Set,z=new Set;function B(e){let t=e.then(()=>void 0,()=>void 0);return R.add(t),t.then(()=>R.delete(t)),e}function de(t){e.isDevBuild()&&console.warn(`[tempest-react-sdk] createTempestAuth: logoutPath revocation failed`,t);try{ie?.(t)}catch{return}}function V(e){let t=(async()=>{try{await e()}catch(e){de(e)}})();return L.add(t),t.then(()=>L.delete(t)),t}function H(e,t){p&&!_&&t&&V(()=>U(e).post(p,{body:b(t)}))}function U(e){return n.createApiClient({baseURL:d,prefix:f,withCredentials:_,csrf:ae,fetcher:v,getToken:()=>e})}function W(){P+=1,K=a.createRefreshQueue(G)}async function G(){let e=N(),t=P;z.add(t);try{await fe(()=>pe(e))}finally{z.delete(t)}}async function fe(e){let t=g===`local`&&typeof navigator<`u`?navigator.locks:void 0;if(!t)return e();let n=new AbortController,r=setTimeout(()=>n.abort(),s),i=!1;try{await t.request(ce,{signal:n.signal},async()=>{i=!0,clearTimeout(r),await e()})}catch(t){if(i)throw t;clearTimeout(r),await e()}}async function pe(e){let n=e!==null||M().user!==null||D!==null,r=Z();if(!n&&N()!==null)return;if(!r.continues)throw new t.SessionSupersededError;if(N()!==e)return;let i=P,a;try{a=await B(Q.post(re,{body:b(E()),skipAuth:!0}))}catch(e){throw P===i?e:new t.SessionSupersededError}let{token:o,refreshToken:s}=y(a);if(P!==i||!Z().continues)throw H(o??null,s),new t.SessionSupersededError;n||j(l()),M().setToken(o),s&&O(s)}let K=a.createRefreshQueue(G);function q(e){let t=N();return t===null?!1:e.token===null||e.token!==t}function J(){return N()===null&&E()===null}async function me(){if(!(I.size>0&&F===0&&(await Promise.allSettled([...I]),N()!==null)))throw Error(`anonymous 401 with no session`)}async function he(e){if(e.epoch!==null&&e.epoch!==P)throw new t.SessionSupersededError;if(q(e)){if(e.token===null)throw Error(`anonymous 401 under a live session`)}else return e.token===null&&J()?z.has(P)?K():me():K()}async function ge(e,n){if(n.epoch!==null&&n.epoch!==P||!Z().continues)throw new t.SessionSupersededError;q(n)||n.token===null&&J()||await Se()}async function _e(e){L.size>0&&await c([...L],o);let t=ve(e);I.add(t);try{return await t}finally{I.delete(t)}}async function ve(e){let n=P,r=await B(Q.post(ne,{body:e,skipAuth:!0})),{token:i,refreshToken:a}=y(r);if(P!==n)throw H(i??null,a),new t.SessionSupersededError;H(N(),E()??void 0);let{done:o}=Y(`switch`),s=P;if(await o.catch(()=>void 0),P!==s)throw new t.SessionSupersededError;j(l()),M().setToken(i),O(a??null);let c=oe?.(r);return c==null?le():(M().setUser(c),c)}function Y(e){let{user:t,token:n}=M(),r=n!==null||t!==null||E()!==null;return(r||e!==`expired`)&&W(),M().logout(),O(null),j(null),r?{live:r,done:X({reason:e,user:t,token:n,elsewhere:!1})}:{live:r,done:e===`switch`||F===0?ue:Promise.resolve()}}function X(e){if(!S)return Promise.resolve();let t=U(e.token);F+=1;let n=(async()=>{try{await S({...e,api:t})}finally{--F}})();return ue=n.then(()=>void 0,()=>void 0),n.then(()=>void 0)}function ye(){let e=T();if(!e)return null;let t=e.getItem(w),n=e.getItem(k),r=e.getItem(h);if(!r)return{user:null,token:null,refreshToken:t,lineage:n};try{let e=JSON.parse(r);return{user:e.state?.user??null,token:e.state?.token??null,refreshToken:t,lineage:n}}catch{return{user:null,token:null,refreshToken:t,lineage:n}}}function Z(){let e=ye(),{user:t,token:n}=M();if(e===null)return{continues:!0,done:Promise.resolve()};let r=JSON.stringify(e.user)===JSON.stringify(t);if(e.token===n&&e.refreshToken===D&&e.lineage===A&&r)return{continues:!0,done:Promise.resolve()};let i=e.lineage!==null||A!==null?e.lineage===A:e.token===n||n!==null&&r,a=e.token!==null&&i,o=n!==null||t!==null||D!==null;if(a||W(),D=e.refreshToken,A=e.lineage,C.setState({user:e.user,token:e.token,isAuthenticated:e.token!==null}),a||!o)return{continues:a,done:Promise.resolve()};let s=X({reason:e.token===null?`logout`:`switch`,user:t,token:n,elsewhere:!0});return s.catch(()=>void 0),{continues:a,done:s}}function be(){if(!p)return null;let e=p,{token:t,user:n}=M(),r=E();if(!_&&t===null&&n===null&&r===null)return null;let i=_?[...R]:[];return V(async()=>(await c(i,o),U(t).post(e,{body:b(r)})))}function xe(){let e=Z();if(!e.continues)return e.done;let t=be(),{done:n}=Y(`logout`);return t?Promise.all([t,n]).then(()=>void 0):n}async function Se(){let{live:e,done:t}=Y(`expired`);await t.catch(()=>void 0),e&&x&&typeof window<`u`&&window.location.assign(x)}let Q=n.createApiClient({baseURL:d,prefix:f,withCredentials:_,csrf:ae,fetcher:v,getToken:N,getSessionEpoch:()=>P,refresh:he,retry:se,onUnauthorized:ge}),Ce=()=>K(),$=null,we=()=>N()===null?null:A===null?($?.epoch!==P&&($={epoch:P,key:l()}),$.key):A,Te={current:we,subscribe:e=>C.subscribe(e)};r.bindSessionIdentity(Q,Te),r.bindSessionIdentity(N,Te);function Ee(e){e.storageArea===T()&&(e.key===null||e.key===h||e.key===w||e.key===k)&&Z()}let De=typeof window>`u`?null:window;return De?.addEventListener(`storage`,Ee),{useAuthStore:C,api:Q,login:_e,logout:xe,refresh:Ce,getToken:N,sessionKey:we,dispose:()=>De?.removeEventListener(`storage`,Ee)}}exports.createTempestAuth=d;
|
|
2
2
|
//# sourceMappingURL=create-tempest-auth.cjs.map
|