tempest-react-sdk 0.82.0 → 0.84.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 +7 -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 +201 -153
- 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 +108 -44
- package/dist/http/upload-with-progress.js.map +1 -1
- package/dist/payments/PixCheckout.cjs +2 -0
- package/dist/payments/PixCheckout.cjs.map +1 -0
- package/dist/payments/PixCheckout.js +171 -0
- package/dist/payments/PixCheckout.js.map +1 -0
- package/dist/payments/PixCheckout.module.cjs +3 -0
- package/dist/payments/PixCheckout.module.cjs.map +1 -0
- package/dist/payments/PixCheckout.module.js +22 -0
- package/dist/payments/PixCheckout.module.js.map +1 -0
- package/dist/payments/format.cjs +2 -0
- package/dist/payments/format.cjs.map +1 -0
- package/dist/payments/format.js +9 -0
- package/dist/payments/format.js.map +1 -0
- package/dist/payments/mercado-pago.cjs +2 -0
- package/dist/payments/mercado-pago.cjs.map +1 -0
- package/dist/payments/mercado-pago.js +27 -0
- package/dist/payments/mercado-pago.js.map +1 -0
- package/dist/payments/outcome.cjs +2 -0
- package/dist/payments/outcome.cjs.map +1 -0
- package/dist/payments/outcome.js +108 -0
- package/dist/payments/outcome.js.map +1 -0
- package/dist/payments/types.cjs +2 -0
- package/dist/payments/types.cjs.map +1 -0
- package/dist/payments/types.js +16 -0
- package/dist/payments/types.js.map +1 -0
- package/dist/payments/use-mercado-pago-card.cjs +2 -0
- package/dist/payments/use-mercado-pago-card.cjs.map +1 -0
- package/dist/payments/use-mercado-pago-card.js +120 -0
- package/dist/payments/use-mercado-pago-card.js.map +1 -0
- package/dist/payments/use-pix-charge.cjs +2 -0
- package/dist/payments/use-pix-charge.cjs.map +1 -0
- package/dist/payments/use-pix-charge.js +63 -0
- package/dist/payments/use-pix-charge.js.map +1 -0
- 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/styles/PixCheckout.css +2 -0
- package/dist/styles/component/PixCheckout.css +18 -0
- package/dist/styles/manifest.json +6 -0
- package/dist/styles/payments.css +18 -0
- package/dist/styles.css +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.cjs +1 -1
- package/dist/tempest-react-sdk.d.ts +748 -10
- package/dist/tempest-react-sdk.js +104 -97
- 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
|
@@ -272,11 +272,12 @@ Every module is re-exported from the package root — `import { Button, useDebou
|
|
|
272
272
|
| `http` | `createApiClient`, `buildApiUrl`, `csrfHeaders`, `parseResponse`, `uploadWithProgress`, `createResumableUpload` (tus 1.0.0: chunked + resumable, `pause`/`resume`/`abort`), `createLocalUploadStorage`, `uploadFingerprint`, `retry`, `generateIdempotencyKey`, `usePoll`, types: `ApiClient`, `ApiClientConfig`, `ApiError`, `BuildApiUrlOptions`, `CsrfOptions`, `RequestOptions`, `RetryOptions`, `UploadProgressEvent`, `UploadWithProgressOptions`, `UsePollOptions`, `UsePollResult` |
|
|
273
273
|
| `auth` _(peer: `zustand`)_ | `createAuthStore`, `AuthGuard`, `decodeJWT`, `isJWTExpired`, `lazyWithRetry`, `createRefreshQueue`, **passkeys/WebAuthn**: `createPasskeyClient`, `usePasskeyRegistration`, `usePasskeySignIn`, `usePasskeyCapabilities`, `isPasskeySupported`, `isPlatformAuthenticatorAvailable`, `isConditionalMediationAvailable`, `classifyPasskeyError`, `PasskeyError`, `base64UrlToBytes`, `bytesToBase64Url`, types: `AuthState`, `CreateAuthStoreOptions`, `AuthGuardProps`, `DecodedJWT`, `LazyWithRetryOptions` |
|
|
274
274
|
| `oauth` _(caller injects `@react-oauth/google`)_ | `GoogleSignIn` (normalises Google's credential/error payload), `useOAuthCallback` (runs the callback exchange once, StrictMode-proof), types: `GoogleSignInProps`, `OAuthCredential`, `OAuthError`, `UseOAuthCallbackOptions`, `UseOAuthCallbackResult` |
|
|
275
|
+
| `payments` _(MercadoPago.js loaded from Mercado Pago at runtime, no npm dep)_ | `PixCheckout` (QR + copia-e-cola + countdown, renders the state the backend reports), `usePixCharge` (polls/subscribes until final), `useMercadoPagoCard` (Secure Fields tokenization + installments by BIN; the card never touches the page), `describeCardCharge` + `CARD_STATUS_DETAIL_MESSAGES` (PT-BR decline text), `loadMercadoPago`, `MERCADO_PAGO_SCRIPT_URL`, `isFinalPaymentStatus`, `formatPixRemaining`, types: `PixCharge`, `CardCharge`, `CardChargeRequest`, `PaymentStatus` |
|
|
275
276
|
| `query` _(peer: `@tanstack/react-query`)_ | `QueryProvider`, `createQueryKeys`, `STALE_TIME`, `CACHE_TIME`, `REFETCH_TIME`, `usePaginatedQuery`, `useCursorQuery`, `useOfflineMutation` (optimistic offline mutation + cache rollback), `upsertById`/`removeById` (optimistic list-cache helpers), `persistQueryClientOffline` (IndexedDB cache persistence for offline reads) |
|
|
276
277
|
| `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
278
|
| `store` _(dep: `zustand`)_ | `createStore`, `createSelectors`, types: `CreateStoreOptions`, `CreateStorePersistOptions`, `WithSelectors` |
|
|
278
279
|
| `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`
|
|
280
|
+
| `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
281
|
| `forms` _(peer: `zod`, `react-hook-form`)_ | `validateForm`, `zodResolver`, `useZodForm`, `validateCPF`, `validateCNPJ`, `formatCEP`, `formatCNPJ`, `unmask`, `CPFInput`, `CNPJInput`, `PhoneInput`, `CEPInput`, `MoneyInput`, `useViaCEP` |
|
|
281
282
|
| `sse` | `createEventStream`, `useEventStream` (`schema` valida cada frame — zod/valibot/arktype via Standard Schema — e `onParseError` para frame que não é JSON) |
|
|
282
283
|
| `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 +313,7 @@ Every module is re-exported from the package root — `import { Button, useDebou
|
|
|
312
313
|
| `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
314
|
| `store` _(dep: `zustand`)_ | `createStore`, `createSelectors`, types: `CreateStoreOptions`, `CreateStorePersistOptions`, `WithSelectors` |
|
|
314
315
|
| `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`
|
|
316
|
+
| `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
317
|
| `forms` _(peer: `zod`, `react-hook-form`)_ | `validateForm`, `zodResolver`, `useZodForm`, `validateCPF`, `validateCNPJ`, `formatCEP`, `formatCNPJ`, `unmask`, `CPFInput`, `CNPJInput`, `PhoneInput`, `CEPInput`, `MoneyInput`, `useViaCEP` |
|
|
317
318
|
| `sse` | `createEventStream`, `useEventStream` (`schema` valida cada frame — zod/valibot/arktype via Standard Schema — e `onParseError` para frame que não é JSON) |
|
|
318
319
|
| `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 +344,7 @@ Every module is re-exported from the package root — `import { Button, useDebou
|
|
|
343
344
|
|
|
344
345
|
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
346
|
|
|
346
|
-
> **Local preview:** `pip install -r docs/requirements.txt && mkdocs serve` (the published site is built and
|
|
347
|
+
> **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
348
|
|
|
348
349
|
A demo app exercising every module lives in [`examples/gallery`](./examples/gallery) — `cd examples/gallery && npm install && npm run dev`.
|
|
349
350
|
|
|
@@ -1321,7 +1322,7 @@ installNotificationClickHandler({
|
|
|
1321
1322
|
});
|
|
1322
1323
|
```
|
|
1323
1324
|
|
|
1324
|
-
The SDK does **not** ship the worker file
|
|
1325
|
+
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
1326
|
|
|
1326
1327
|
### Audio playback recipe
|
|
1327
1328
|
|
|
@@ -1999,10 +2000,10 @@ Two commands, with the release PR's review in between:
|
|
|
1999
2000
|
|
|
2000
2001
|
```bash
|
|
2001
2002
|
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:
|
|
2003
|
+
make release-tag TAG=X.Y.Z # after the merge: build + smokes + npm publish + tag + GitHub Release
|
|
2003
2004
|
```
|
|
2004
2005
|
|
|
2005
|
-
|
|
2006
|
+
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
2007
|
|
|
2007
2008
|
Tags follow `vMAJOR.MINOR.PATCH`. CSS tokens (`--tempest-*`) are treated as public API — renames bump the minor (or major, on removal).
|
|
2008
2009
|
|
|
@@ -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
|
|
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("./jwt.cjs"),o=require("./refresh-queue.cjs");var s=1e4,c=1e4;function l(e,t){return e.length===0?Promise.resolve():new Promise(n=>{let r=setTimeout(n,t);Promise.allSettled(e).then(()=>{clearTimeout(r),n()})})}function u(){let e=globalThis.crypto;return typeof e?.randomUUID==`function`?e.randomUUID():`${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}var d=new Set;function ee(t){e.isDevBuild()&&!d.has(t)&&(d.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 f(e){if(!e)return null;try{let{sub:t}=a.decodeJWT(e).payload;return typeof t==`string`&&t!==``?t:null}catch{return null}}function te(e){let t=e??{};return{token:t.access_token,refreshToken:t.refresh_token}}function p(a){let{baseURL:d,prefix:p,loginPath:ne=`/api/auth/login`,refreshPath:m=`/api/auth/refresh`,logoutPath:h,onLogoutError:re,mePath:ie,storeName:g=`tempest-auth`,storage:_=`local`,withCredentials:v=!1,csrf:ae,fetcher:y,parseTokens:b=te,parseUser:oe,refreshBody:x=e=>e?{refresh_token:e}:void 0,retry:se,redirectTo:S,onSessionEnd:ce}=a;v&&!h&&ee(g);let C=i.createAuthStore({name:g,storage:_}),w=`${g}-refresh`;function T(){return typeof window>`u`?null:_===`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=`${g}-lineage`,le=`tempest-react-sdk:${g}: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 ue(){if(!ie)return M().user;let e=P,n;try{n=await Q.get(ie,{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,de=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 fe(t){e.isDevBuild()&&console.warn(`[tempest-react-sdk] createTempestAuth: logoutPath revocation failed`,t);try{re?.(t)}catch{return}}function V(e){let t=(async()=>{try{await e()}catch(e){fe(e)}})();return L.add(t),t.then(()=>L.delete(t)),t}function H(e,t){h&&!v&&t&&V(()=>U(e).post(h,{body:x(t)}))}function U(e){return n.createApiClient({baseURL:d,prefix:p,withCredentials:v,csrf:ae,fetcher:y,getToken:()=>e})}function W(){P+=1,q=o.createRefreshQueue(G)}async function G(){let e=N(),t=P;z.add(t);let n;try{n=await K(()=>pe(e))}finally{z.delete(t)}n&&await J(Y(n))}async function K(e){let t=_===`local`&&typeof navigator<`u`?navigator.locks:void 0;if(!t)return e();let n=new AbortController,r=setTimeout(()=>n.abort(),c),i=!1,a;try{await t.request(le,{signal:n.signal},async()=>{i=!0,clearTimeout(r),a=await e()})}catch(t){if(i)throw t;return clearTimeout(r),e()}return a}async function pe(e){let n=e!==null||M().user!==null||D!==null,r=Z();if(!n&&N()!==null)return null;if(!r.continues)throw new t.SessionSupersededError;if(N()!==e)return null;let i=P,a;try{a=await B(Q.post(m,{body:x(E()),skipAuth:!0}))}catch(e){throw P===i?e:new t.SessionSupersededError}let{token:o,refreshToken:s}=b(a);if(P!==i||!Z().continues)throw H(o??null,s),new t.SessionSupersededError;let c=f(e),l=f(o);return c!==null&&l!==null&&c!==l?{data:a,token:o,refreshToken:s}:(n||j(u()),M().setToken(o),s&&O(s),null)}let q=o.createRefreshQueue(G);function me(e){let t=N();return t===null?!1:e.token===null||e.token!==t}function he(){return N()===null&&E()===null}async function ge(){if(!(I.size>0&&F===0&&(await Promise.allSettled([...I]),N()!==null)))throw Error(`anonymous 401 with no session`)}async function _e(e){if(e.epoch!==null&&e.epoch!==P)throw new t.SessionSupersededError;if(me(e)){if(e.token===null)throw Error(`anonymous 401 under a live session`);return}if(e.token===null&&he())return z.has(P)?q():ge();let n=P;if(await q(),P!==n)throw new t.SessionSupersededError}async function ve(e,n){if(n.epoch!==null&&n.epoch!==P||!Z().continues)throw new t.SessionSupersededError;me(n)||n.token===null&&he()||await De()}async function ye(e){return L.size>0&&await l([...L],s),J(Se(e))}async function be(){return L.size>0&&await l([...L],s),J(xe())}async function J(e){I.add(e);try{return await e}finally{I.delete(e)}}async function xe(){let e=P,n=await K(()=>B(Q.post(m,{body:x(E()),skipAuth:!0}))),{token:r,refreshToken:i}=b(n);if(P!==e)throw H(r??null,i),new t.SessionSupersededError;return Y({data:n,token:r,refreshToken:i})}async function Se(e){let n=P,r=await B(Q.post(ne,{body:e,skipAuth:!0})),{token:i,refreshToken:a}=b(r);if(P!==n)throw H(i??null,a),new t.SessionSupersededError;return H(N(),E()??void 0),Y({data:r,token:i,refreshToken:a})}async function Y(e){let{done:n}=X(`switch`),r=P;if(await n.catch(()=>void 0),P!==r)throw new t.SessionSupersededError;j(u()),M().setToken(e.token),O(e.refreshToken??null);let i=oe?.(e.data);return i==null?ue():(M().setUser(i),i)}function X(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:Ce({reason:e,user:t,token:n,elsewhere:!1})}:{live:r,done:e===`switch`||F===0?de:Promise.resolve()}}function Ce(e){if(!ce)return Promise.resolve();let t=U(e.token);F+=1;let n=(async()=>{try{await ce({...e,api:t})}finally{--F}})();return de=n.then(()=>void 0,()=>void 0),n.then(()=>void 0)}function we(){let e=T();if(!e)return null;let t=e.getItem(w),n=e.getItem(k),r=e.getItem(g);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=we(),{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=Ce({reason:e.token===null?`logout`:`switch`,user:t,token:n,elsewhere:!0});return s.catch(()=>void 0),{continues:a,done:s}}function Te(){if(!h)return null;let e=h,{token:t,user:n}=M(),r=E();if(!v&&t===null&&n===null&&r===null)return null;let i=v?[...R]:[];return V(async()=>(await l(i,s),U(t).post(e,{body:x(r)})))}function Ee(){let e=Z();if(!e.continues)return e.done;let t=Te(),{done:n}=X(`logout`);return t?Promise.all([t,n]).then(()=>void 0):n}async function De(){let{live:e,done:t}=X(`expired`);await t.catch(()=>void 0),e&&S&&typeof window<`u`&&window.location.assign(S)}let Q=n.createApiClient({baseURL:d,prefix:p,withCredentials:v,csrf:ae,fetcher:y,getToken:N,getSessionEpoch:()=>P,refresh:_e,retry:se,onUnauthorized:ve}),Oe=()=>q(),$=null,ke=()=>N()===null?null:A===null?($?.epoch!==P&&($={epoch:P,key:u()}),$.key):A,Ae={current:ke,subscribe:e=>C.subscribe(e)};r.bindSessionIdentity(Q,Ae),r.bindSessionIdentity(N,Ae);function je(e){e.storageArea===T()&&(e.key===null||e.key===g||e.key===w||e.key===k)&&Z()}let Me=typeof window>`u`?null:window;return Me?.addEventListener(`storage`,je),{useAuthStore:C,api:Q,login:ye,logout:Ee,refresh:Oe,restore:be,getToken:N,sessionKey:ke,dispose:()=>Me?.removeEventListener(`storage`,je)}}exports.createTempestAuth=p;
|
|
2
2
|
//# sourceMappingURL=create-tempest-auth.cjs.map
|