@civitai/blocks-react 0.56.0 → 0.57.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 +34 -2
- package/dist/api/generationResources.d.ts +4 -3
- package/dist/hooks/returnTypeLedger.d.ts +2 -0
- package/dist/hooks/returnTypeLedger.js +2 -0
- package/dist/hooks/useAppStorage.js +2 -2
- package/dist/hooks/useAppWorkflows.d.ts +5 -2
- package/dist/hooks/useAppWorkflows.js +18 -7
- package/dist/hooks/useBlockAnalytics.d.ts +5 -3
- package/dist/hooks/useBlockAnalytics.js +1 -1
- package/dist/hooks/useBlockBreakpoint.d.ts +7 -1
- package/dist/hooks/useBlockContext.d.ts +13 -2
- package/dist/hooks/useBlockContext.js +1 -1
- package/dist/hooks/useBlockResize.d.ts +7 -1
- package/dist/hooks/useBlockResize.js +1 -1
- package/dist/hooks/useBlockSettings.d.ts +6 -1
- package/dist/hooks/useBlockTheme.d.ts +6 -1
- package/dist/hooks/useBlockToken.d.ts +8 -3
- package/dist/hooks/useBlockToken.js +2 -2
- package/dist/hooks/useBuzzAccounts.d.ts +3 -2
- package/dist/hooks/useBuzzAccounts.js +16 -15
- package/dist/hooks/useBuzzBalance.d.ts +3 -2
- package/dist/hooks/useBuzzBalance.js +16 -18
- package/dist/hooks/useBuzzPurchase.d.ts +8 -6
- package/dist/hooks/useBuzzPurchase.js +3 -3
- package/dist/hooks/useBuzzTransactions.d.ts +4 -2
- package/dist/hooks/useBuzzTransactions.js +16 -15
- package/dist/hooks/useBuzzWorkflow.d.ts +71 -28
- package/dist/hooks/useBuzzWorkflow.js +31 -17
- package/dist/hooks/useCheckpointPicker.d.ts +18 -16
- package/dist/hooks/useCheckpointPicker.js +3 -3
- package/dist/hooks/useCivitaiNavigate.d.ts +5 -3
- package/dist/hooks/useCivitaiNavigate.js +1 -1
- package/dist/hooks/useCollectionFollow.js +4 -4
- package/dist/hooks/useConsentUnavailable.js +2 -2
- package/dist/hooks/useCreatePostFromApp.js +4 -4
- package/dist/hooks/useDailyCompensation.d.ts +4 -2
- package/dist/hooks/useDailyCompensation.js +17 -15
- package/dist/hooks/useDirectLoad.d.ts +7 -1
- package/dist/hooks/useDirectLoad.js +1 -1
- package/dist/hooks/useDomainMaturity.d.ts +6 -2
- package/dist/hooks/useGatedImages.d.ts +8 -0
- package/dist/hooks/useGatedImages.js +10 -2
- package/dist/hooks/useGenerationResources.d.ts +5 -3
- package/dist/hooks/useHostOrigin.d.ts +7 -1
- package/dist/hooks/useHostOrigin.js +1 -1
- package/dist/hooks/useImageUpload.d.ts +44 -17
- package/dist/hooks/useImageUpload.js +24 -4
- package/dist/hooks/usePublishGenerationOutputs.js +4 -4
- package/dist/hooks/useRequestConsent.d.ts +7 -5
- package/dist/hooks/useRequestConsent.js +1 -1
- package/dist/hooks/useRequestSequencer.d.ts +71 -0
- package/dist/hooks/useRequestSequencer.js +28 -0
- package/dist/hooks/useRequestSignIn.d.ts +7 -5
- package/dist/hooks/useRequestSignIn.js +1 -1
- package/dist/hooks/useResourcePicker.d.ts +16 -14
- package/dist/hooks/useResourcePicker.js +3 -3
- package/dist/hooks/useSaveImage.js +2 -2
- package/dist/hooks/useSharedStorage.js +2 -2
- package/dist/hooks/useTip.js +1 -1
- package/dist/hooks/useTipAllowance.d.ts +13 -1
- package/dist/hooks/useTipAllowance.js +49 -11
- package/dist/hooks/useViewer.d.ts +3 -2
- package/dist/hooks/useViewer.js +16 -18
- package/dist/hooks/useWildcardPack.d.ts +3 -1
- package/dist/hooks/useWildcardPack.js +19 -14
- package/dist/index.d.ts +30 -14
- package/dist/index.js +7 -7
- package/dist/internal/consentRefusalLatch.d.ts +1 -1
- package/dist/internal/consentRefusalLatch.js +1 -1
- package/dist/internal/liveHost.d.ts +63 -11
- package/dist/internal/liveHost.js +240 -37
- package/dist/internal/mockHost.js +16 -15
- package/dist/internal/pickerOverlay.d.ts +52 -4
- package/dist/internal/pickerOverlay.js +14 -3
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +1 -1
- package/dist/{internal → transport}/iframeTransport.d.ts +43 -5
- package/dist/{internal → transport}/iframeTransport.js +142 -12
- package/dist/transport/originMatcher.d.ts +92 -0
- package/dist/transport/originMatcher.js +222 -0
- package/dist/transport/requestId.d.ts +113 -0
- package/dist/transport/requestId.js +117 -0
- package/dist/{internal → transport}/validate.d.ts +34 -0
- package/dist/{internal → transport}/validate.js +189 -48
- package/dist/ui/BlockGate.js +1 -1
- package/dist/ui/SettingsForm.d.ts +8 -2
- package/dist/ui/SettingsForm.js +49 -3
- package/package.json +7 -148
- package/dist/api/generationResources.d.ts.map +0 -1
- package/dist/api/generationResources.js.map +0 -1
- package/dist/hooks/SfwGate.d.ts.map +0 -1
- package/dist/hooks/SfwGate.js.map +0 -1
- package/dist/hooks/useAppStorage.d.ts.map +0 -1
- package/dist/hooks/useAppStorage.js.map +0 -1
- package/dist/hooks/useAppWorkflows.d.ts.map +0 -1
- package/dist/hooks/useAppWorkflows.js.map +0 -1
- package/dist/hooks/useBlockAnalytics.d.ts.map +0 -1
- package/dist/hooks/useBlockAnalytics.js.map +0 -1
- package/dist/hooks/useBlockBreakpoint.d.ts.map +0 -1
- package/dist/hooks/useBlockBreakpoint.js.map +0 -1
- package/dist/hooks/useBlockContext.d.ts.map +0 -1
- package/dist/hooks/useBlockContext.js.map +0 -1
- package/dist/hooks/useBlockResize.d.ts.map +0 -1
- package/dist/hooks/useBlockResize.js.map +0 -1
- package/dist/hooks/useBlockSettings.d.ts.map +0 -1
- package/dist/hooks/useBlockSettings.js.map +0 -1
- package/dist/hooks/useBlockTheme.d.ts.map +0 -1
- package/dist/hooks/useBlockTheme.js.map +0 -1
- package/dist/hooks/useBlockToken.d.ts.map +0 -1
- package/dist/hooks/useBlockToken.js.map +0 -1
- package/dist/hooks/useBuzzAccounts.d.ts.map +0 -1
- package/dist/hooks/useBuzzAccounts.js.map +0 -1
- package/dist/hooks/useBuzzBalance.d.ts.map +0 -1
- package/dist/hooks/useBuzzBalance.js.map +0 -1
- package/dist/hooks/useBuzzPurchase.d.ts.map +0 -1
- package/dist/hooks/useBuzzPurchase.js.map +0 -1
- package/dist/hooks/useBuzzTransactions.d.ts.map +0 -1
- package/dist/hooks/useBuzzTransactions.js.map +0 -1
- package/dist/hooks/useBuzzWorkflow.d.ts.map +0 -1
- package/dist/hooks/useBuzzWorkflow.js.map +0 -1
- package/dist/hooks/useCheckpointPicker.d.ts.map +0 -1
- package/dist/hooks/useCheckpointPicker.js.map +0 -1
- package/dist/hooks/useCivitaiNavigate.d.ts.map +0 -1
- package/dist/hooks/useCivitaiNavigate.js.map +0 -1
- package/dist/hooks/useCollectionFollow.d.ts.map +0 -1
- package/dist/hooks/useCollectionFollow.js.map +0 -1
- package/dist/hooks/useConsentUnavailable.d.ts.map +0 -1
- package/dist/hooks/useConsentUnavailable.js.map +0 -1
- package/dist/hooks/useCreatePostFromApp.d.ts.map +0 -1
- package/dist/hooks/useCreatePostFromApp.js.map +0 -1
- package/dist/hooks/useDailyCompensation.d.ts.map +0 -1
- package/dist/hooks/useDailyCompensation.js.map +0 -1
- package/dist/hooks/useDirectLoad.d.ts.map +0 -1
- package/dist/hooks/useDirectLoad.js.map +0 -1
- package/dist/hooks/useDomainMaturity.d.ts.map +0 -1
- package/dist/hooks/useDomainMaturity.js.map +0 -1
- package/dist/hooks/useGatedImages.d.ts.map +0 -1
- package/dist/hooks/useGatedImages.js.map +0 -1
- package/dist/hooks/useGenerationResources.d.ts.map +0 -1
- package/dist/hooks/useGenerationResources.js.map +0 -1
- package/dist/hooks/useHostOrigin.d.ts.map +0 -1
- package/dist/hooks/useHostOrigin.js.map +0 -1
- package/dist/hooks/useImageUpload.d.ts.map +0 -1
- package/dist/hooks/useImageUpload.js.map +0 -1
- package/dist/hooks/usePublishGenerationOutputs.d.ts.map +0 -1
- package/dist/hooks/usePublishGenerationOutputs.js.map +0 -1
- package/dist/hooks/useRequestConsent.d.ts.map +0 -1
- package/dist/hooks/useRequestConsent.js.map +0 -1
- package/dist/hooks/useRequestSignIn.d.ts.map +0 -1
- package/dist/hooks/useRequestSignIn.js.map +0 -1
- package/dist/hooks/useResourcePicker.d.ts.map +0 -1
- package/dist/hooks/useResourcePicker.js.map +0 -1
- package/dist/hooks/useSaveImage.d.ts.map +0 -1
- package/dist/hooks/useSaveImage.js.map +0 -1
- package/dist/hooks/useSharedStorage.d.ts.map +0 -1
- package/dist/hooks/useSharedStorage.js.map +0 -1
- package/dist/hooks/useTip.d.ts.map +0 -1
- package/dist/hooks/useTip.js.map +0 -1
- package/dist/hooks/useTipAllowance.d.ts.map +0 -1
- package/dist/hooks/useTipAllowance.js.map +0 -1
- package/dist/hooks/useViewer.d.ts.map +0 -1
- package/dist/hooks/useViewer.js.map +0 -1
- package/dist/hooks/useWildcardPack.d.ts.map +0 -1
- package/dist/hooks/useWildcardPack.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/internal/catalog.d.ts.map +0 -1
- package/dist/internal/catalog.js.map +0 -1
- package/dist/internal/consent.d.ts.map +0 -1
- package/dist/internal/consent.js.map +0 -1
- package/dist/internal/consentRefusalLatch.d.ts.map +0 -1
- package/dist/internal/consentRefusalLatch.js.map +0 -1
- package/dist/internal/detector.d.ts.map +0 -1
- package/dist/internal/detector.js.map +0 -1
- package/dist/internal/directLoad.d.ts.map +0 -1
- package/dist/internal/directLoad.js.map +0 -1
- package/dist/internal/iframeTransport.d.ts.map +0 -1
- package/dist/internal/iframeTransport.js.map +0 -1
- package/dist/internal/inlineTransport.d.ts.map +0 -1
- package/dist/internal/inlineTransport.js.map +0 -1
- package/dist/internal/liveHost.d.ts.map +0 -1
- package/dist/internal/liveHost.js.map +0 -1
- package/dist/internal/mockHost.d.ts.map +0 -1
- package/dist/internal/mockHost.js.map +0 -1
- package/dist/internal/originMatcher.d.ts +0 -28
- package/dist/internal/originMatcher.d.ts.map +0 -1
- package/dist/internal/originMatcher.js +0 -89
- package/dist/internal/originMatcher.js.map +0 -1
- package/dist/internal/pickerOverlay.d.ts.map +0 -1
- package/dist/internal/pickerOverlay.js.map +0 -1
- package/dist/internal/replyError.d.ts.map +0 -1
- package/dist/internal/replyError.js.map +0 -1
- package/dist/internal/requestTimeouts.d.ts.map +0 -1
- package/dist/internal/requestTimeouts.js.map +0 -1
- package/dist/internal/singleton.d.ts.map +0 -1
- package/dist/internal/singleton.js.map +0 -1
- package/dist/internal/transport.d.ts.map +0 -1
- package/dist/internal/transport.js.map +0 -1
- package/dist/internal/validate.d.ts.map +0 -1
- package/dist/internal/validate.js.map +0 -1
- package/dist/live.d.ts.map +0 -1
- package/dist/live.js.map +0 -1
- package/dist/testing.d.ts.map +0 -1
- package/dist/testing.js.map +0 -1
- package/dist/ui/Alert.d.ts.map +0 -1
- package/dist/ui/Alert.js.map +0 -1
- package/dist/ui/Badge.d.ts.map +0 -1
- package/dist/ui/Badge.js.map +0 -1
- package/dist/ui/BlockGate.d.ts.map +0 -1
- package/dist/ui/BlockGate.js.map +0 -1
- package/dist/ui/Button.d.ts.map +0 -1
- package/dist/ui/Button.js.map +0 -1
- package/dist/ui/Card.d.ts.map +0 -1
- package/dist/ui/Card.js.map +0 -1
- package/dist/ui/Collapse.d.ts.map +0 -1
- package/dist/ui/Collapse.js.map +0 -1
- package/dist/ui/FollowButton.d.ts.map +0 -1
- package/dist/ui/FollowButton.js.map +0 -1
- package/dist/ui/Group.d.ts.map +0 -1
- package/dist/ui/Group.js.map +0 -1
- package/dist/ui/Loader.d.ts.map +0 -1
- package/dist/ui/Loader.js.map +0 -1
- package/dist/ui/Modal.d.ts.map +0 -1
- package/dist/ui/Modal.js.map +0 -1
- package/dist/ui/NumberInput.d.ts.map +0 -1
- package/dist/ui/NumberInput.js.map +0 -1
- package/dist/ui/ReportButton.d.ts.map +0 -1
- package/dist/ui/ReportButton.js.map +0 -1
- package/dist/ui/ResourceCard.d.ts.map +0 -1
- package/dist/ui/ResourceCard.js.map +0 -1
- package/dist/ui/SegmentedControl.d.ts.map +0 -1
- package/dist/ui/SegmentedControl.js.map +0 -1
- package/dist/ui/Select.d.ts.map +0 -1
- package/dist/ui/Select.js.map +0 -1
- package/dist/ui/SettingsForm.d.ts.map +0 -1
- package/dist/ui/SettingsForm.js.map +0 -1
- package/dist/ui/Slider.d.ts.map +0 -1
- package/dist/ui/Slider.js.map +0 -1
- package/dist/ui/Stack.d.ts.map +0 -1
- package/dist/ui/Stack.js.map +0 -1
- package/dist/ui/TextInput.d.ts.map +0 -1
- package/dist/ui/TextInput.js.map +0 -1
- package/dist/ui/Textarea.d.ts.map +0 -1
- package/dist/ui/Textarea.js.map +0 -1
- package/dist/ui/TipButton.d.ts.map +0 -1
- package/dist/ui/TipButton.js.map +0 -1
- package/dist/ui/index.d.ts.map +0 -1
- package/dist/ui/index.js.map +0 -1
- package/dist/ui/styles.d.ts.map +0 -1
- package/dist/ui/styles.js.map +0 -1
- /package/dist/{internal → transport}/detector.d.ts +0 -0
- /package/dist/{internal → transport}/detector.js +0 -0
- /package/dist/{internal → transport}/directLoad.d.ts +0 -0
- /package/dist/{internal → transport}/directLoad.js +0 -0
- /package/dist/{internal → transport}/inlineTransport.d.ts +0 -0
- /package/dist/{internal → transport}/inlineTransport.js +0 -0
- /package/dist/{internal → transport}/requestTimeouts.d.ts +0 -0
- /package/dist/{internal → transport}/requestTimeouts.js +0 -0
- /package/dist/{internal → transport}/singleton.d.ts +0 -0
- /package/dist/{internal → transport}/singleton.js +0 -0
- /package/dist/{internal → transport}/transport.d.ts +0 -0
- /package/dist/{internal → transport}/transport.js +0 -0
package/README.md
CHANGED
|
@@ -1313,6 +1313,38 @@ For non-React or advanced use, the transport primitives are exported too:
|
|
|
1313
1313
|
`readAllowedOriginsFromEnv`, `getTransport`, and `sendTypedRequest`. Hooks are the
|
|
1314
1314
|
recommended surface; reach for these only when a hook doesn't fit.
|
|
1315
1315
|
|
|
1316
|
+
### How `allowedParentOrigins` entries are read
|
|
1317
|
+
|
|
1318
|
+
Each entry is canonicalised with the URL parser before it is compared to
|
|
1319
|
+
`event.origin`, so these spellings all mean the same thing and all match a host
|
|
1320
|
+
frame at `https://civitai.com`:
|
|
1321
|
+
|
|
1322
|
+
| Written as | Also matches |
|
|
1323
|
+
|---|---|
|
|
1324
|
+
| `https://civitai.com/` | trailing slash is ignored |
|
|
1325
|
+
| `HTTPS://CIVITAI.COM` | scheme and host are case-insensitive |
|
|
1326
|
+
| `https://civitai.com:443`, `http://x:80` | an explicit **default** port is ignored |
|
|
1327
|
+
| `https://пример.com` | normalised to the punycode a browser reports |
|
|
1328
|
+
|
|
1329
|
+
These stay **significant** — they are different origins, not spellings:
|
|
1330
|
+
|
|
1331
|
+
- a **non-default** port: `https://civitai.com:8443` does not match `https://civitai.com`
|
|
1332
|
+
- the scheme: `http://` never matches `https://`
|
|
1333
|
+
- a trailing-dot host: `https://civitai.com.` is its own origin
|
|
1334
|
+
- any host that merely *contains* an allowed one: `https://civitai.com.evil.com`
|
|
1335
|
+
never matches `https://civitai.com`
|
|
1336
|
+
|
|
1337
|
+
An entry that is not a bare origin **throws at construction** rather than being
|
|
1338
|
+
quietly skipped — no scheme (`civitai.com`), a path/query/fragment
|
|
1339
|
+
(`https://civitai.com/embed`), credentials (`https://u:p@civitai.com`), or a
|
|
1340
|
+
scheme with no origin of its own (`file://…`). A silently dropped entry is the
|
|
1341
|
+
failure this rule exists to prevent: the allowlist would be missing an origin
|
|
1342
|
+
you believe is on it.
|
|
1343
|
+
|
|
1344
|
+
If `BLOCK_INIT` never lands, the 10s timeout error names the origins that
|
|
1345
|
+
actually arrived and were rejected, next to the allowlist it checked them
|
|
1346
|
+
against — start there.
|
|
1347
|
+
|
|
1316
1348
|
## The `/testing` subexport
|
|
1317
1349
|
|
|
1318
1350
|
`@civitai/blocks-react/testing` is the **host-simulation** entry point: it stands
|
|
@@ -1586,8 +1618,8 @@ Runnable, minimal blocks — one per feature, each with its own README:
|
|
|
1586
1618
|
|
|
1587
1619
|
| `@civitai/blocks-react` | pairs with `@civitai/app-sdk` | adds |
|
|
1588
1620
|
|---|---|---|
|
|
1589
|
-
| `0.50.x` | `^0.40.0` | `useCreatePostFromApp()` (`CREATE_POST_FROM_APP`) + the `posts:write:self` scope.
|
|
1590
|
-
| `0.48.x` | `^0.38.0` | `useCollectionFollow()` + `<FollowButton>` / `<TipButton>` (`SET_COLLECTION_FOLLOW`).
|
|
1621
|
+
| `0.50.x` | `^0.40.0` | `useCreatePostFromApp()` (`CREATE_POST_FROM_APP`) + the `posts:write:self` scope. Pairing `0.50.x` with an SDK below `0.40.0` fails at `tsc` with `Cannot find name 'BlockCreatePostHostError'`, not at install. |
|
|
1622
|
+
| `0.48.x` | `^0.38.0` | `useCollectionFollow()` + `<FollowButton>` / `<TipButton>` (`SET_COLLECTION_FOLLOW`). Pairing `0.48.x` with an SDK below `0.38.0` fails at `tsc` with `Cannot find name 'BlockCollectionFollowErrorCode'`, not at install — the floor those releases declared was deliberately wide (#206 — a per-minor floor forced a major on consumers). |
|
|
1591
1623
|
| `0.36.x` | `^0.27.0` | auto-installs the SDK's opaque-origin web-storage shim (`@civitai/app-sdk/safe-storage`) on import |
|
|
1592
1624
|
| `0.29.x` | `^0.24.0` | `useAppWorkflows()` — app generator subqueue read + cancel (`QUERY_APP_WORKFLOWS` / `CANCEL_APP_WORKFLOW`) |
|
|
1593
1625
|
| `0.27.x`–`0.28.x` | `^0.23.0` | async-scan image upload; transport validators for all `SHARED_*` / `APP_STORAGE_*` / picker replies |
|
|
@@ -30,7 +30,8 @@ export declare const MAX_GENERATION_RESOURCE_IDS = 30;
|
|
|
30
30
|
* Pure + deterministic. `base` defaults to {@link GENERATION_RESOURCES_API_BASE}.
|
|
31
31
|
*/
|
|
32
32
|
export declare function buildGenerationResourcesUrl(versionIds: number[], base?: string): string;
|
|
33
|
-
|
|
33
|
+
/** One row of the rehydrate response. See the note above: wire shape, not a contract. */
|
|
34
|
+
export interface RawGenerationResource {
|
|
34
35
|
versionId?: number;
|
|
35
36
|
modelId?: number;
|
|
36
37
|
modelName?: string;
|
|
@@ -43,7 +44,8 @@ interface RawGenerationResource {
|
|
|
43
44
|
trainedWords?: string[];
|
|
44
45
|
clipSkip?: number | null;
|
|
45
46
|
}
|
|
46
|
-
|
|
47
|
+
/** The rehydrate response body — the parameter {@link responseToResources} takes. */
|
|
48
|
+
export interface RawGenerationResourcesResponse {
|
|
47
49
|
items?: RawGenerationResource[];
|
|
48
50
|
maturity?: {
|
|
49
51
|
browsingLevel?: number;
|
|
@@ -58,5 +60,4 @@ interface RawGenerationResourcesResponse {
|
|
|
58
60
|
* `projectSafeGenerationResource`. Never throws.
|
|
59
61
|
*/
|
|
60
62
|
export declare function responseToResources(raw: RawGenerationResourcesResponse | null | undefined): BlockResourceInfo[];
|
|
61
|
-
export {};
|
|
62
63
|
//# sourceMappingURL=generationResources.d.ts.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
|
-
import { getTransport } from '../
|
|
2
|
+
import { getTransport } from '../transport/singleton.js';
|
|
3
3
|
import { throwOnFailedReply, throwOnReplyError } from '../internal/replyError.js';
|
|
4
|
-
import { sendTypedRequest } from '../
|
|
4
|
+
import { sendTypedRequest } from '../transport/transport.js';
|
|
5
5
|
/**
|
|
6
6
|
* Per-(block instance, viewer) KV datastore. Calls flow through the host's
|
|
7
7
|
* postMessage bridge — the block never sees the apps DB credentials.
|
|
@@ -43,8 +43,11 @@ export interface UseAppWorkflows {
|
|
|
43
43
|
*
|
|
44
44
|
* Fetches on mount and whenever `params` change (by value), and exposes `refetch`.
|
|
45
45
|
* A host that never answers surfaces as an `error` after the transport's request
|
|
46
|
-
* timeout — the hook never hangs.
|
|
47
|
-
*
|
|
46
|
+
* timeout — the hook never hangs. Only the LATEST list request may write state:
|
|
47
|
+
* a reply superseded by a newer `refetch` / params change — or one that lands
|
|
48
|
+
* after unmount — is dropped (#392). `cancel` is caller-driven and keeps its own
|
|
49
|
+
* mount guard: its functional `setWorkflows` splice is order-independent, so it
|
|
50
|
+
* is not a sequencing hazard.
|
|
48
51
|
*
|
|
49
52
|
* @example
|
|
50
53
|
* const { workflows, cursor, loading, error, cancel } = useAppWorkflows({ limit: 20 });
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
-
import { getTransport } from '../
|
|
3
|
-
import { sendTypedRequest } from '../
|
|
2
|
+
import { getTransport } from '../transport/singleton.js';
|
|
3
|
+
import { sendTypedRequest } from '../transport/transport.js';
|
|
4
|
+
import { useRequestSequencer } from './useRequestSequencer.js';
|
|
4
5
|
/**
|
|
5
6
|
* Read (and cancel within) the calling app's OWN generator SUBQUEUE — the
|
|
6
7
|
* tag-scoped list of generations THIS app produced for the viewer, newest-first —
|
|
@@ -12,8 +13,11 @@ import { sendTypedRequest } from '../internal/transport.js';
|
|
|
12
13
|
*
|
|
13
14
|
* Fetches on mount and whenever `params` change (by value), and exposes `refetch`.
|
|
14
15
|
* A host that never answers surfaces as an `error` after the transport's request
|
|
15
|
-
* timeout — the hook never hangs.
|
|
16
|
-
*
|
|
16
|
+
* timeout — the hook never hangs. Only the LATEST list request may write state:
|
|
17
|
+
* a reply superseded by a newer `refetch` / params change — or one that lands
|
|
18
|
+
* after unmount — is dropped (#392). `cancel` is caller-driven and keeps its own
|
|
19
|
+
* mount guard: its functional `setWorkflows` splice is order-independent, so it
|
|
20
|
+
* is not a sequencing hazard.
|
|
17
21
|
*
|
|
18
22
|
* @example
|
|
19
23
|
* const { workflows, cursor, loading, error, cancel } = useAppWorkflows({ limit: 20 });
|
|
@@ -24,6 +28,9 @@ export function useAppWorkflows(params) {
|
|
|
24
28
|
const [cursor, setCursor] = useState(null);
|
|
25
29
|
const [loading, setLoading] = useState(true);
|
|
26
30
|
const [error, setError] = useState(null);
|
|
31
|
+
// `cancel` is caller-driven, not auto-issued, and its state write is a
|
|
32
|
+
// functional splice that is safe in any order — so it keeps a plain mount
|
|
33
|
+
// guard rather than joining the list request's sequence.
|
|
27
34
|
const mountedRef = useRef(true);
|
|
28
35
|
useEffect(() => {
|
|
29
36
|
mountedRef.current = true;
|
|
@@ -31,18 +38,22 @@ export function useAppWorkflows(params) {
|
|
|
31
38
|
mountedRef.current = false;
|
|
32
39
|
};
|
|
33
40
|
}, []);
|
|
41
|
+
// Latest-wins + unmount guard for the LIST request (#392): a reply may write
|
|
42
|
+
// state only if it answers the request this hook is CURRENTLY waiting for.
|
|
43
|
+
const seq = useRequestSequencer();
|
|
34
44
|
// Serialize the params to a stable key so `refetch`'s identity only changes
|
|
35
45
|
// when the params VALUE changes (not on every render's fresh object). The
|
|
36
46
|
// callback re-parses the key so it closes over NOTHING but the key.
|
|
37
47
|
const paramsKey = params ? JSON.stringify(params) : '';
|
|
38
48
|
const refetch = useCallback(() => {
|
|
49
|
+
const token = seq.begin();
|
|
39
50
|
setLoading(true);
|
|
40
51
|
setError(null);
|
|
41
52
|
const parsed = paramsKey ? JSON.parse(paramsKey) : undefined;
|
|
42
53
|
const payload = parsed ? { params: parsed } : {};
|
|
43
54
|
sendTypedRequest(getTransport(), { type: 'QUERY_APP_WORKFLOWS', payload }, 'APP_WORKFLOWS_RESULT')
|
|
44
55
|
.then((result) => {
|
|
45
|
-
if (!
|
|
56
|
+
if (!seq.isCurrent(token))
|
|
46
57
|
return;
|
|
47
58
|
if (result.error || !result.result) {
|
|
48
59
|
// `||`, not `??`: the reply validator gates `error` on SHAPE only, so a
|
|
@@ -57,12 +68,12 @@ export function useAppWorkflows(params) {
|
|
|
57
68
|
setLoading(false);
|
|
58
69
|
})
|
|
59
70
|
.catch((err) => {
|
|
60
|
-
if (!
|
|
71
|
+
if (!seq.isCurrent(token))
|
|
61
72
|
return;
|
|
62
73
|
setError(err instanceof Error ? err : new Error(String(err)));
|
|
63
74
|
setLoading(false);
|
|
64
75
|
});
|
|
65
|
-
}, [paramsKey]);
|
|
76
|
+
}, [paramsKey, seq]);
|
|
66
77
|
useEffect(() => {
|
|
67
78
|
refetch();
|
|
68
79
|
}, [refetch]);
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/** What {@link useBlockAnalytics} returns. */
|
|
2
|
+
export interface UseBlockAnalytics {
|
|
3
|
+
track: (eventName: string, properties?: Record<string, unknown>) => void;
|
|
4
|
+
}
|
|
1
5
|
/**
|
|
2
6
|
* Fire-and-forget analytics tracking. The host forwards events to its
|
|
3
7
|
* analytics pipeline (ClickHouse in production); the block doesn't see
|
|
@@ -7,7 +11,5 @@
|
|
|
7
11
|
* const { track } = useBlockAnalytics();
|
|
8
12
|
* track('generate_clicked', { modelId });
|
|
9
13
|
*/
|
|
10
|
-
export declare function useBlockAnalytics():
|
|
11
|
-
track: (eventName: string, properties?: Record<string, unknown>) => void;
|
|
12
|
-
};
|
|
14
|
+
export declare function useBlockAnalytics(): UseBlockAnalytics;
|
|
13
15
|
//# sourceMappingURL=useBlockAnalytics.d.ts.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useCallback } from 'react';
|
|
2
|
-
import { getTransport } from '../
|
|
2
|
+
import { getTransport } from '../transport/singleton.js';
|
|
3
3
|
/**
|
|
4
4
|
* Fire-and-forget analytics tracking. The host forwards events to its
|
|
5
5
|
* analytics pipeline (ClickHouse in production); the block doesn't see
|
|
@@ -23,6 +23,12 @@ export type BlockSizeTier = 'base' | BreakpointKey;
|
|
|
23
23
|
* `display: none` ancestor) resolves to `'base'` — the most conservative tier.
|
|
24
24
|
*/
|
|
25
25
|
export declare function resolveBlockTier(width: number): BlockSizeTier;
|
|
26
|
+
/**
|
|
27
|
+
* What {@link useBlockBreakpoint} returns. An alias for the long-standing
|
|
28
|
+
* {@link BlockBreakpoint} name, which stays exported and unchanged — see
|
|
29
|
+
* `./returnTypeLedger.js`.
|
|
30
|
+
*/
|
|
31
|
+
export type UseBlockBreakpoint = BlockBreakpoint;
|
|
26
32
|
export interface BlockBreakpoint {
|
|
27
33
|
/**
|
|
28
34
|
* The current tier. `'base'` while unmeasured — see `measured`.
|
|
@@ -88,5 +94,5 @@ export interface BlockBreakpoint {
|
|
|
88
94
|
* </div>
|
|
89
95
|
* );
|
|
90
96
|
*/
|
|
91
|
-
export declare function useBlockBreakpoint(ref?: RefObject<HTMLElement | null>):
|
|
97
|
+
export declare function useBlockBreakpoint(ref?: RefObject<HTMLElement | null>): UseBlockBreakpoint;
|
|
92
98
|
//# sourceMappingURL=useBlockBreakpoint.d.ts.map
|
|
@@ -1,9 +1,20 @@
|
|
|
1
|
-
import type { BlockSnapshot } from '../
|
|
1
|
+
import type { BlockSnapshot } from '../transport/transport.js';
|
|
2
2
|
/**
|
|
3
3
|
* Subscribe a hook to the singleton transport. All hooks build on this —
|
|
4
4
|
* `useSyncExternalStore` gives concurrent-mode safety and minimal re-renders.
|
|
5
5
|
*/
|
|
6
6
|
declare function useTransportSnapshot(): BlockSnapshot;
|
|
7
|
+
/**
|
|
8
|
+
* What {@link useBlockContext} returns — the projection of {@link BlockSnapshot}
|
|
9
|
+
* the hook exposes.
|
|
10
|
+
*
|
|
11
|
+
* 🔴 DERIVED FROM `BlockSnapshot`, NOT RETYPED. A hand-written copy of these ten
|
|
12
|
+
* fields would drift silently the first time the snapshot's shape moved; the
|
|
13
|
+
* `Pick` re-resolves against the transport's own type on every build. Before
|
|
14
|
+
* #380 this expression was written inline on the hook's return annotation, so
|
|
15
|
+
* a consumer wrapping `useBlockContext()` had nothing to name.
|
|
16
|
+
*/
|
|
17
|
+
export type UseBlockContext = Pick<BlockSnapshot, 'ready' | 'renderMode' | 'context' | 'token' | 'settings' | 'viewer' | 'theme' | 'blockId' | 'blockInstanceId' | 'appId'>;
|
|
7
18
|
/**
|
|
8
19
|
* Primary hook for a block app. Returns the full per-instance context
|
|
9
20
|
* delivered by the host plus a `ready` gate the UI should respect — fields
|
|
@@ -34,7 +45,7 @@ declare function useTransportSnapshot(): BlockSnapshot;
|
|
|
34
45
|
* // scheduled for removal. This snippet used to do exactly that.
|
|
35
46
|
* return <div data-theme={theme}>{isSignedIn(viewer) ? 'Hi there' : 'Hi anon'}</div>;
|
|
36
47
|
*/
|
|
37
|
-
export declare function useBlockContext():
|
|
48
|
+
export declare function useBlockContext(): UseBlockContext;
|
|
38
49
|
/** Re-exported so other hooks in this package can share the subscription. */
|
|
39
50
|
export { useTransportSnapshot };
|
|
40
51
|
//# sourceMappingURL=useBlockContext.d.ts.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useSyncExternalStore } from 'react';
|
|
2
|
-
import { getTransport } from '../
|
|
2
|
+
import { getTransport } from '../transport/singleton.js';
|
|
3
3
|
/**
|
|
4
4
|
* Subscribe a hook to the singleton transport. All hooks build on this —
|
|
5
5
|
* `useSyncExternalStore` gives concurrent-mode safety and minimal re-renders.
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { type RefObject } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* What {@link useBlockResize} returns: nothing. It is named anyway so the
|
|
4
|
+
* convention has NO exceptions to remember — see `./returnTypeLedger.js`. A wrapper
|
|
5
|
+
* that forwards this hook's result can still annotate it.
|
|
6
|
+
*/
|
|
7
|
+
export type UseBlockResize = void;
|
|
2
8
|
/**
|
|
3
9
|
* Observes the referenced element's height and asks the host to resize on
|
|
4
10
|
* every change. Attach to the block's root DOM element.
|
|
@@ -42,5 +48,5 @@ import { type RefObject } from 'react';
|
|
|
42
48
|
* if (!ready) return <div>Loading…</div>; // no ref needed on this branch
|
|
43
49
|
* return <div ref={rootRef}>…</div>;
|
|
44
50
|
*/
|
|
45
|
-
export declare function useBlockResize(ref: RefObject<HTMLElement | null>):
|
|
51
|
+
export declare function useBlockResize(ref: RefObject<HTMLElement | null>): UseBlockResize;
|
|
46
52
|
//# sourceMappingURL=useBlockResize.d.ts.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useEffect, useRef } from 'react';
|
|
2
|
-
import { getTransport } from '../
|
|
2
|
+
import { getTransport } from '../transport/singleton.js';
|
|
3
3
|
/**
|
|
4
4
|
* Observes the referenced element's height and asks the host to resize on
|
|
5
5
|
* every change. Attach to the block's root DOM element.
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import type { BlockSettings } from '@civitai/app-sdk/blocks';
|
|
2
|
+
/**
|
|
3
|
+
* What {@link useBlockSettings} returns. An alias — see
|
|
4
|
+
* `./returnTypeLedger.js` for why every hook on the entry has one of these.
|
|
5
|
+
*/
|
|
6
|
+
export type UseBlockSettings = BlockSettings;
|
|
2
7
|
/**
|
|
3
8
|
* Shorthand for `useBlockContext().settings`. Returns the publisher- and
|
|
4
9
|
* user-controlled settings the host forwarded at init. Read-only from the
|
|
@@ -8,5 +13,5 @@ import type { BlockSettings } from '@civitai/app-sdk/blocks';
|
|
|
8
13
|
* @example
|
|
9
14
|
* const { publisherSettings, userSettings } = useBlockSettings();
|
|
10
15
|
*/
|
|
11
|
-
export declare function useBlockSettings():
|
|
16
|
+
export declare function useBlockSettings(): UseBlockSettings;
|
|
12
17
|
//# sourceMappingURL=useBlockSettings.d.ts.map
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import type { Theme } from '@civitai/app-sdk/blocks';
|
|
2
|
+
/**
|
|
3
|
+
* What {@link useBlockTheme} returns. An alias — see `./returnTypeLedger.js` for why
|
|
4
|
+
* every hook on the entry has one of these.
|
|
5
|
+
*/
|
|
6
|
+
export type UseBlockTheme = Theme;
|
|
2
7
|
/**
|
|
3
8
|
* The host's CURRENT site theme (`'light' | 'dark'`), kept live for the whole
|
|
4
9
|
* life of the block ON THE IFRAME TRANSPORT.
|
|
@@ -35,5 +40,5 @@ import type { Theme } from '@civitai/app-sdk/blocks';
|
|
|
35
40
|
* const theme = useBlockTheme();
|
|
36
41
|
* return <div data-theme={theme}>…</div>;
|
|
37
42
|
*/
|
|
38
|
-
export declare function useBlockTheme():
|
|
43
|
+
export declare function useBlockTheme(): UseBlockTheme;
|
|
39
44
|
//# sourceMappingURL=useBlockTheme.d.ts.map
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import type { BlockToken } from '@civitai/app-sdk/blocks';
|
|
2
|
+
/**
|
|
3
|
+
* What {@link useBlockToken} returns: the live block token plus a manual
|
|
4
|
+
* `refresh`. See `./returnTypeLedger.js`.
|
|
5
|
+
*/
|
|
6
|
+
export type UseBlockToken = BlockToken & {
|
|
7
|
+
refresh: () => Promise<void>;
|
|
8
|
+
};
|
|
2
9
|
/**
|
|
3
10
|
* Returns the current block-scoped JWT and keeps it fresh.
|
|
4
11
|
*
|
|
@@ -17,9 +24,7 @@ import type { BlockToken } from '@civitai/app-sdk/blocks';
|
|
|
17
24
|
* const { raw, scopes, expiresAt, buzzBudget, refresh } = useBlockToken();
|
|
18
25
|
* // after a 401: await refresh(); then retry the request once with the new `raw`.
|
|
19
26
|
*/
|
|
20
|
-
export declare function useBlockToken():
|
|
21
|
-
refresh: () => Promise<void>;
|
|
22
|
-
};
|
|
27
|
+
export declare function useBlockToken(): UseBlockToken;
|
|
23
28
|
/**
|
|
24
29
|
* Mint/await a token refresh for one block instance, coalescing concurrent calls
|
|
25
30
|
* for the SAME `blockInstanceId`. Exported for the inline-mode multi-instance
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect } from 'react';
|
|
2
|
-
import { getTransport } from '../
|
|
3
|
-
import { sendTypedRequest } from '../
|
|
2
|
+
import { getTransport } from '../transport/singleton.js';
|
|
3
|
+
import { sendTypedRequest } from '../transport/transport.js';
|
|
4
4
|
import { useTransportSnapshot } from './useBlockContext.js';
|
|
5
5
|
/** Refresh fires `REFRESH_LEAD_MS` before the token's `expiresAt`. */
|
|
6
6
|
const REFRESH_LEAD_MS = 2 * 60 * 1000;
|
|
@@ -29,8 +29,9 @@ export interface UseBuzzAccounts {
|
|
|
29
29
|
* the creator payout pools — as `{ accountType, balance }` rows.
|
|
30
30
|
*
|
|
31
31
|
* Fetches once on mount and exposes `refetch`. A host that never answers surfaces
|
|
32
|
-
* as an `error` after the transport timeout
|
|
33
|
-
*
|
|
32
|
+
* as an `error` after the transport timeout. Only the LATEST request may write
|
|
33
|
+
* state: a reply superseded by a newer `refetch` — or one that lands after
|
|
34
|
+
* unmount — is dropped (#392).
|
|
34
35
|
*
|
|
35
36
|
* @example
|
|
36
37
|
* const { accounts, loading, error } = useBuzzAccounts();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { useCallback, useEffect,
|
|
2
|
-
import { getTransport } from '../
|
|
3
|
-
import { sendTypedRequest } from '../
|
|
1
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
2
|
+
import { getTransport } from '../transport/singleton.js';
|
|
3
|
+
import { sendTypedRequest } from '../transport/transport.js';
|
|
4
|
+
import { useRequestSequencer } from './useRequestSequencer.js';
|
|
4
5
|
/**
|
|
5
6
|
* Read the signed-in viewer's ALL-pool Buzz balances through the host-mediated
|
|
6
7
|
* `GET_BUZZ_ACCOUNTS` → `BUZZ_ACCOUNTS_RESULT` bridge. Token-bound: the host
|
|
@@ -10,8 +11,9 @@ import { sendTypedRequest } from '../internal/transport.js';
|
|
|
10
11
|
* the creator payout pools — as `{ accountType, balance }` rows.
|
|
11
12
|
*
|
|
12
13
|
* Fetches once on mount and exposes `refetch`. A host that never answers surfaces
|
|
13
|
-
* as an `error` after the transport timeout
|
|
14
|
-
*
|
|
14
|
+
* as an `error` after the transport timeout. Only the LATEST request may write
|
|
15
|
+
* state: a reply superseded by a newer `refetch` — or one that lands after
|
|
16
|
+
* unmount — is dropped (#392).
|
|
15
17
|
*
|
|
16
18
|
* @example
|
|
17
19
|
* const { accounts, loading, error } = useBuzzAccounts();
|
|
@@ -20,19 +22,18 @@ export function useBuzzAccounts() {
|
|
|
20
22
|
const [accounts, setAccounts] = useState(null);
|
|
21
23
|
const [loading, setLoading] = useState(true);
|
|
22
24
|
const [error, setError] = useState(null);
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
};
|
|
29
|
-
}, []);
|
|
25
|
+
// Latest-wins + unmount guard in one predicate (#392): a reply may write state
|
|
26
|
+
// only if it answers the request this hook is CURRENTLY waiting for. A bare
|
|
27
|
+
// mount check would let a superseded `refetch`'s slow reply overwrite newer
|
|
28
|
+
// state — nothing unmounted, so it passes.
|
|
29
|
+
const seq = useRequestSequencer();
|
|
30
30
|
const refetch = useCallback(() => {
|
|
31
|
+
const token = seq.begin();
|
|
31
32
|
setLoading(true);
|
|
32
33
|
setError(null);
|
|
33
34
|
sendTypedRequest(getTransport(), { type: 'GET_BUZZ_ACCOUNTS', payload: {} }, 'BUZZ_ACCOUNTS_RESULT')
|
|
34
35
|
.then((result) => {
|
|
35
|
-
if (!
|
|
36
|
+
if (!seq.isCurrent(token))
|
|
36
37
|
return;
|
|
37
38
|
if (result.error || !result.result) {
|
|
38
39
|
// `||`, not `??`: the reply validator gates `error` on SHAPE only, so a
|
|
@@ -46,12 +47,12 @@ export function useBuzzAccounts() {
|
|
|
46
47
|
setLoading(false);
|
|
47
48
|
})
|
|
48
49
|
.catch((err) => {
|
|
49
|
-
if (!
|
|
50
|
+
if (!seq.isCurrent(token))
|
|
50
51
|
return;
|
|
51
52
|
setError(err instanceof Error ? err : new Error(String(err)));
|
|
52
53
|
setLoading(false);
|
|
53
54
|
});
|
|
54
|
-
}, []);
|
|
55
|
+
}, [seq]);
|
|
55
56
|
useEffect(() => {
|
|
56
57
|
refetch();
|
|
57
58
|
}, [refetch]);
|
|
@@ -35,8 +35,9 @@ export interface UseBuzzBalance {
|
|
|
35
35
|
*
|
|
36
36
|
* Fetches once on mount and exposes `refetch` for on-demand refreshes (e.g.
|
|
37
37
|
* after a generation debits the balance). A host that never answers surfaces as
|
|
38
|
-
* an `error` after the transport's request timeout — the hook never hangs.
|
|
39
|
-
*
|
|
38
|
+
* an `error` after the transport's request timeout — the hook never hangs. Only
|
|
39
|
+
* the LATEST request may write state: a reply superseded by a newer `refetch` —
|
|
40
|
+
* or one that lands after unmount — is dropped (#392).
|
|
40
41
|
*
|
|
41
42
|
* @example
|
|
42
43
|
* const { balance, loading, error, refetch } = useBuzzBalance();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { useCallback, useEffect,
|
|
2
|
-
import { getTransport } from '../
|
|
3
|
-
import { sendTypedRequest } from '../
|
|
1
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
2
|
+
import { getTransport } from '../transport/singleton.js';
|
|
3
|
+
import { sendTypedRequest } from '../transport/transport.js';
|
|
4
|
+
import { useRequestSequencer } from './useRequestSequencer.js';
|
|
4
5
|
/**
|
|
5
6
|
* Read the signed-in viewer's per-pool Buzz balance ({ blue, green, yellow })
|
|
6
7
|
* through the host-mediated `GET_BUZZ_BALANCE` → `BUZZ_BALANCE_RESULT` bridge.
|
|
@@ -11,8 +12,9 @@ import { sendTypedRequest } from '../internal/transport.js';
|
|
|
11
12
|
*
|
|
12
13
|
* Fetches once on mount and exposes `refetch` for on-demand refreshes (e.g.
|
|
13
14
|
* after a generation debits the balance). A host that never answers surfaces as
|
|
14
|
-
* an `error` after the transport's request timeout — the hook never hangs.
|
|
15
|
-
*
|
|
15
|
+
* an `error` after the transport's request timeout — the hook never hangs. Only
|
|
16
|
+
* the LATEST request may write state: a reply superseded by a newer `refetch` —
|
|
17
|
+
* or one that lands after unmount — is dropped (#392).
|
|
16
18
|
*
|
|
17
19
|
* @example
|
|
18
20
|
* const { balance, loading, error, refetch } = useBuzzBalance();
|
|
@@ -24,22 +26,18 @@ export function useBuzzBalance() {
|
|
|
24
26
|
const [balance, setBalance] = useState(null);
|
|
25
27
|
const [loading, setLoading] = useState(true);
|
|
26
28
|
const [error, setError] = useState(null);
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
mountedRef.current = true;
|
|
33
|
-
return () => {
|
|
34
|
-
mountedRef.current = false;
|
|
35
|
-
};
|
|
36
|
-
}, []);
|
|
29
|
+
// Latest-wins + unmount guard in one predicate (#392): a reply may write state
|
|
30
|
+
// only if it answers the request this hook is CURRENTLY waiting for. A bare
|
|
31
|
+
// mount check would let a superseded `refetch`'s slow reply overwrite newer
|
|
32
|
+
// state — nothing unmounted, so it passes.
|
|
33
|
+
const seq = useRequestSequencer();
|
|
37
34
|
const refetch = useCallback(() => {
|
|
35
|
+
const token = seq.begin();
|
|
38
36
|
setLoading(true);
|
|
39
37
|
setError(null);
|
|
40
38
|
sendTypedRequest(getTransport(), { type: 'GET_BUZZ_BALANCE', payload: {} }, 'BUZZ_BALANCE_RESULT')
|
|
41
39
|
.then((result) => {
|
|
42
|
-
if (!
|
|
40
|
+
if (!seq.isCurrent(token))
|
|
43
41
|
return;
|
|
44
42
|
if (result.error || !result.balance) {
|
|
45
43
|
// `||`, not `??`: the reply validator gates `error` on SHAPE only, so a
|
|
@@ -53,12 +51,12 @@ export function useBuzzBalance() {
|
|
|
53
51
|
setLoading(false);
|
|
54
52
|
})
|
|
55
53
|
.catch((err) => {
|
|
56
|
-
if (!
|
|
54
|
+
if (!seq.isCurrent(token))
|
|
57
55
|
return;
|
|
58
56
|
setError(err instanceof Error ? err : new Error(String(err)));
|
|
59
57
|
setLoading(false);
|
|
60
58
|
});
|
|
61
|
-
}, []);
|
|
59
|
+
}, [seq]);
|
|
62
60
|
useEffect(() => {
|
|
63
61
|
refetch();
|
|
64
62
|
}, [refetch]);
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/** What {@link useBuzzPurchase} returns. */
|
|
2
|
+
export interface UseBuzzPurchase {
|
|
3
|
+
openPurchaseModal: (suggestedAmount?: number) => Promise<{
|
|
4
|
+
purchased: boolean;
|
|
5
|
+
newBalance?: number;
|
|
6
|
+
}>;
|
|
7
|
+
}
|
|
1
8
|
/**
|
|
2
9
|
* Opens the Civitai Buzz purchase modal on the host. Resolves with the
|
|
3
10
|
* outcome when the user closes the modal — `purchased: true` means the
|
|
@@ -15,10 +22,5 @@
|
|
|
15
22
|
* const { purchased, newBalance } = await openPurchaseModal(suggestedAmount);
|
|
16
23
|
* if (purchased) { /* retry the generation *\/ }
|
|
17
24
|
*/
|
|
18
|
-
export declare function useBuzzPurchase():
|
|
19
|
-
openPurchaseModal: (suggestedAmount?: number) => Promise<{
|
|
20
|
-
purchased: boolean;
|
|
21
|
-
newBalance?: number;
|
|
22
|
-
}>;
|
|
23
|
-
};
|
|
25
|
+
export declare function useBuzzPurchase(): UseBuzzPurchase;
|
|
24
26
|
//# sourceMappingURL=useBuzzPurchase.d.ts.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useCallback } from 'react';
|
|
2
|
-
import { HUMAN_INTERACTION_TIMEOUT_MS } from '../
|
|
3
|
-
import { getTransport } from '../
|
|
4
|
-
import { sendTypedRequest } from '../
|
|
2
|
+
import { HUMAN_INTERACTION_TIMEOUT_MS } from '../transport/requestTimeouts.js';
|
|
3
|
+
import { getTransport } from '../transport/singleton.js';
|
|
4
|
+
import { sendTypedRequest } from '../transport/transport.js';
|
|
5
5
|
/**
|
|
6
6
|
* Opens the Civitai Buzz purchase modal on the host. Resolves with the
|
|
7
7
|
* outcome when the user closes the modal — `purchased: true` means the
|
|
@@ -41,8 +41,10 @@ export interface UseBuzzTransactions {
|
|
|
41
41
|
*
|
|
42
42
|
* Fetches on mount and whenever `params` change (by value), and exposes `refetch`.
|
|
43
43
|
* A host that never answers surfaces as an `error` after the transport's request
|
|
44
|
-
* timeout — the hook never hangs.
|
|
45
|
-
*
|
|
44
|
+
* timeout — the hook never hangs. Only the LATEST request may write state: page
|
|
45
|
+
* 1's slow reply cannot repaint (or rewind `cursor` behind) the page 2 a newer
|
|
46
|
+
* request already painted, and a reply that lands after unmount is dropped
|
|
47
|
+
* (#392). Transaction `date`s are rehydrated to `Date`; `cursor` is normalized
|
|
46
48
|
* to an ISO string for round-tripping.
|
|
47
49
|
*
|
|
48
50
|
* @example
|