@civitai/blocks-react 0.71.0 → 0.72.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +308 -15
  2. package/dist/hooks/useAppStorage.js +2 -0
  3. package/dist/hooks/useAppWorkflows.js +2 -0
  4. package/dist/hooks/useBatchEstimate.d.ts +4 -0
  5. package/dist/hooks/useBatchEstimate.js +6 -0
  6. package/dist/hooks/useBlockAnalytics.js +2 -0
  7. package/dist/hooks/useBlockBreakpoint.js +2 -0
  8. package/dist/hooks/useBlockContext.js +2 -0
  9. package/dist/hooks/useBlockResize.js +2 -0
  10. package/dist/hooks/useBlockSettings.js +2 -0
  11. package/dist/hooks/useBlockTheme.js +2 -0
  12. package/dist/hooks/useBlockToken.js +2 -0
  13. package/dist/hooks/useBuzzAccounts.js +2 -0
  14. package/dist/hooks/useBuzzBalance.js +2 -0
  15. package/dist/hooks/useBuzzPurchase.js +2 -0
  16. package/dist/hooks/useBuzzTransactions.js +2 -0
  17. package/dist/hooks/useBuzzWorkflow.d.ts +35 -3
  18. package/dist/hooks/useBuzzWorkflow.js +32 -3
  19. package/dist/hooks/useCheckpointPicker.d.ts +2 -0
  20. package/dist/hooks/useCheckpointPicker.js +4 -0
  21. package/dist/hooks/useCivitaiNavigate.js +2 -0
  22. package/dist/hooks/useCivitaiRoute.js +2 -0
  23. package/dist/hooks/useCollectionFollow.js +2 -0
  24. package/dist/hooks/useConsentUnavailable.js +2 -0
  25. package/dist/hooks/useCreatePostFromApp.js +2 -0
  26. package/dist/hooks/useDailyCompensation.js +2 -0
  27. package/dist/hooks/useDirectLoad.js +2 -0
  28. package/dist/hooks/useDomainMaturity.js +2 -0
  29. package/dist/hooks/useEntitlements.js +2 -0
  30. package/dist/hooks/useGatedImages.js +2 -0
  31. package/dist/hooks/useGenerationResources.js +2 -0
  32. package/dist/hooks/useGoodPurchase.js +2 -0
  33. package/dist/hooks/useHostOrigin.js +2 -0
  34. package/dist/hooks/useImageUpload.js +2 -0
  35. package/dist/hooks/useNestedDocument.js +2 -0
  36. package/dist/hooks/usePrepareTrainingDataset.js +2 -0
  37. package/dist/hooks/usePublishGenerationOutputs.js +2 -0
  38. package/dist/hooks/useRequestConsent.js +2 -0
  39. package/dist/hooks/useRequestSignIn.js +2 -0
  40. package/dist/hooks/useResourcePicker.js +2 -0
  41. package/dist/hooks/useRunConfirmedStep.d.ts +209 -0
  42. package/dist/hooks/useRunConfirmedStep.js +289 -0
  43. package/dist/hooks/useRunTraining.js +2 -0
  44. package/dist/hooks/useSaveImage.js +2 -0
  45. package/dist/hooks/useSharedStorage.js +2 -0
  46. package/dist/hooks/useTip.js +2 -0
  47. package/dist/hooks/useTipAllowance.js +2 -0
  48. package/dist/hooks/useTransportConnection.d.ts +43 -0
  49. package/dist/hooks/useTransportConnection.js +59 -0
  50. package/dist/hooks/useUploadImageBytes.js +2 -0
  51. package/dist/hooks/useViewer.js +2 -0
  52. package/dist/hooks/useWildcardPack.js +2 -0
  53. package/dist/hooks/useWorkflowBatch.js +2 -0
  54. package/dist/index.d.ts +2 -0
  55. package/dist/index.js +1 -0
  56. package/dist/internal/hostSurface.d.ts +37 -0
  57. package/dist/internal/hostSurface.js +38 -0
  58. package/dist/internal/liveHost.d.ts +15 -6
  59. package/dist/internal/liveHost.js +151 -69
  60. package/dist/internal/mockHost.d.ts +123 -10
  61. package/dist/internal/mockHost.js +377 -14
  62. package/dist/internal/mockWorkflowGraph.d.ts +23 -10
  63. package/dist/internal/mockWorkflowGraph.js +34 -11
  64. package/dist/testing.d.ts +1 -1
  65. package/dist/transport/iframeTransport.js +13 -0
  66. package/dist/transport/requestTimeouts.js +6 -0
  67. package/dist/transport/singleton.d.ts +6 -0
  68. package/dist/transport/singleton.js +20 -0
  69. package/dist/transport/validate.d.ts +13 -0
  70. package/dist/transport/validate.js +76 -0
  71. package/package.json +4 -4
@@ -0,0 +1,59 @@
1
+ import { useEffect } from 'react';
2
+ import { claimConnectFailureReport, getTransport } from '../transport/singleton.js';
3
+ /**
4
+ * Connects the block to its host at MOUNT. Internal — every public hook calls
5
+ * it, and it is deliberately not exported from the package entry.
6
+ *
7
+ * 🔴 WHY EVERY HOOK, AND WHY AT MOUNT. The host waits 10 s for the block's
8
+ * handshake — `BLOCK_HELLO` on transport construction, `BLOCK_READY` on the
9
+ * first valid `BLOCK_INIT` — and then shows "This app didn't load in time",
10
+ * retries twice and gives up (`bootSkeleton` hides the cover, not the
11
+ * deadline). Both messages are sent by the transport, and the transport is a
12
+ * lazy singleton: nothing goes out until something calls `getTransport()`.
13
+ * Request-only hooks (`useBuzzWorkflow`, `useTip`, …) used to call it only
14
+ * inside their callbacks, so an app whose only SDK use was one of them never
15
+ * answered the host and was torn down before anyone could click. Calling this
16
+ * from every public hook makes "uses any SDK hook" imply "is connected".
17
+ * `test/connectsAtMount.test.tsx` pins it over the whole exported hook set.
18
+ *
19
+ * 🔴 IT IS CALLED EVEN WHERE IT IS REDUNDANT, ON PURPOSE. About half the hooks
20
+ * already reach `getTransport()` at render or in a mount fetch; they call this
21
+ * anyway so the rule is "every public hook", which needs no case analysis. A
22
+ * mount fetch can be conditional (`useWildcardPack` skips it for an invalid id),
23
+ * so "it already fetches on mount" is not a safe reason to leave one out.
24
+ *
25
+ * 🔴 WHY AN EFFECT, NOT RENDER. `useBlockContext` constructs during render, and
26
+ * that is fine for it — but a render-time call throws during SSR (the iframe
27
+ * transport refuses to construct without a `window`), and these hooks have
28
+ * always been safe to render on a server. An effect never runs there, and on
29
+ * the client it runs at mount, long before the host's deadline.
30
+ *
31
+ * 🔴 WHY NOT AT MODULE LOAD. `getTransport(opts)` honours only its FIRST call;
32
+ * dev harnesses and tests inject their allowlist (`installDevTransport()`,
33
+ * `getTransport({ allowedParentOrigins })`) after importing this package and
34
+ * before the first hook runs. Constructing on import would win that race and
35
+ * silently discard their options.
36
+ *
37
+ * 🔴 WHY A FAILURE IS LOGGED, NOT THROWN. Construction throws when no parent
38
+ * allowlist is configured. Before this hook existed, such an app still rendered
39
+ * and only failed on its first bridge call — and it still does: `getTransport()`
40
+ * caches nothing on a throw, so that call throws the same error as before. This
41
+ * hook only ADDS the connection; it must not turn a misconfigured block that
42
+ * renders into one that crashes on mount.
43
+ */
44
+ export function useTransportConnection() {
45
+ useEffect(() => {
46
+ try {
47
+ getTransport();
48
+ }
49
+ catch (err) {
50
+ // One console line per transport lifecycle, not one per hook instance:
51
+ // a block calling ten hooks would otherwise log ten identical errors.
52
+ if (claimConnectFailureReport()) {
53
+ // eslint-disable-next-line no-console -- developer-facing diagnostic; the host will time this block out
54
+ console.error('@civitai/blocks-react: could not connect to the host at mount.', err);
55
+ }
56
+ }
57
+ }, []);
58
+ }
59
+ //# sourceMappingURL=useTransportConnection.js.map
@@ -4,6 +4,7 @@ import { UPLOAD_BYTES_MAX_BYTES, UPLOAD_BYTES_TOO_LARGE_ERROR } from '../interna
4
4
  import { HUMAN_INTERACTION_TIMEOUT_MS } from '../transport/requestTimeouts.js';
5
5
  import { getTransport } from '../transport/singleton.js';
6
6
  import { sendTypedRequest } from '../transport/transport.js';
7
+ import { useTransportConnection } from './useTransportConnection.js';
7
8
  /**
8
9
  * The SDK's own rejection when a reply carries neither an image nor an
9
10
  * `error`. A host that predates the `bytes` variant opens its picker instead,
@@ -43,6 +44,7 @@ export const UPLOAD_IMAGE_BYTES_NO_IMAGE_ERROR = 'the host returned no uploaded
43
44
  * await createPost({ sources: [{ kind: 'published', imageIds: [image.imageId] }] });
44
45
  */
45
46
  export function useUploadImageBytes() {
47
+ useTransportConnection();
46
48
  const upload = useCallback(async (bytes, options) => {
47
49
  // The ONLY client-side refusal, mirroring `useSaveImage`'s cap pre-check:
48
50
  // everything else is forwarded and judged by the host.
@@ -2,6 +2,7 @@ import { useCallback, useEffect, useState } from 'react';
2
2
  import { getTransport } from '../transport/singleton.js';
3
3
  import { sendTypedRequest } from '../transport/transport.js';
4
4
  import { useRequestSequencer } from './useRequestSequencer.js';
5
+ import { useTransportConnection } from './useTransportConnection.js';
5
6
  /**
6
7
  * Read the signed-in viewer (id / username / status + optional buzzBudget)
7
8
  * through the host-mediated `GET_VIEWER` → `VIEWER_RESULT` bridge. Token-bound:
@@ -29,6 +30,7 @@ import { useRequestSequencer } from './useRequestSequencer.js';
29
30
  * return <span>{viewer?.username}</span>;
30
31
  */
31
32
  export function useViewer() {
33
+ useTransportConnection();
32
34
  const [viewer, setViewer] = useState(null);
33
35
  const [loading, setLoading] = useState(true);
34
36
  const [error, setError] = useState(null);
@@ -2,6 +2,7 @@ import { useCallback, useEffect, useState } from 'react';
2
2
  import { getTransport } from '../transport/singleton.js';
3
3
  import { sendTypedRequest } from '../transport/transport.js';
4
4
  import { useRequestSequencer } from './useRequestSequencer.js';
5
+ import { useTransportConnection } from './useTransportConnection.js';
5
6
  /**
6
7
  * A wildcard-pack import failure. Unlike the buzz bridges' free-text errors, the
7
8
  * host returns a DISCRIMINATED code — this error carries it on `.code` so a block
@@ -37,6 +38,7 @@ export class WildcardPackError extends Error {
37
38
  * if (error instanceof WildcardPackError && error.code === 'busy') return <RetryLater />;
38
39
  */
39
40
  export function useWildcardPack(modelVersionId) {
41
+ useTransportConnection();
40
42
  const [pack, setPack] = useState(null);
41
43
  const [loading, setLoading] = useState(true);
42
44
  const [error, setError] = useState(null);
@@ -1,5 +1,6 @@
1
1
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
2
  import { useBuzzWorkflow } from './useBuzzWorkflow.js';
3
+ import { useTransportConnection } from './useTransportConnection.js';
3
4
  /**
4
5
  * The most workflows one {@link useWorkflowBatch} may watch.
5
6
  *
@@ -145,6 +146,7 @@ function summarise(cells) {
145
146
  * if (!result.complete) showRetryCancel(result);
146
147
  */
147
148
  export function useWorkflowBatch(workflowIds, options) {
149
+ useTransportConnection();
148
150
  validateIds(workflowIds);
149
151
  // A private `useBuzzWorkflow()` instance, so the app's own instance's
150
152
  // `result`/`status` are not overwritten by 16 cells' updates.
package/dist/index.d.ts CHANGED
@@ -75,6 +75,8 @@ export { usePrepareTrainingDataset, PrepareTrainingDatasetError, PREPARE_TRAININ
75
75
  export type { UsePrepareTrainingDataset, BlockPrepareTrainingDatasetHostError, BlockTrainingDatasetItem, BlockTrainingDatasetResult, BlockTrainingRejectionReason, } from './hooks/usePrepareTrainingDataset.js';
76
76
  export { useRunTraining, RunTrainingError, RUN_TRAINING_ERROR_CODES, isRunTrainingErrorCode, } from './hooks/useRunTraining.js';
77
77
  export type { UseRunTraining, QuotedTrainingBody, RunTrainingErrorCode, BlockRunTrainingHostError, BlockTrainingQuote, WorkflowBodyTraining, } from './hooks/useRunTraining.js';
78
+ export { useRunConfirmedStep, RunConfirmedStepError, RUN_CONFIRMED_STEP_ERROR_CODES, isRunConfirmedStepErrorCode, isStepQuoteId, } from './hooks/useRunConfirmedStep.js';
79
+ export type { UseRunConfirmedStep, QuotedStepBody, RunConfirmedStepErrorCode, BlockRunConfirmedStepHostError, BlockStepQuote, WorkflowBodyPassThroughStep, } from './hooks/useRunConfirmedStep.js';
78
80
  export type { BlockBuzzTransaction, BlockBuzzAccount, BlockDailyCompensationResource, BlockWildcardPack, BlockWildcardPackErrorCode, AppWorkflow, AppWorkflowImage, AppWorkflowsParams, BlockGatedImage, BlockCollectionFollowErrorCode, BlockCollectionFollowResult, } from '@civitai/app-sdk/blocks';
79
81
  export { useCheckpointPicker } from './hooks/useCheckpointPicker.js';
80
82
  export type { UseCheckpointPicker } from './hooks/useCheckpointPicker.js';
package/dist/index.js CHANGED
@@ -63,6 +63,7 @@ export { useCollectionFollow, CollectionFollowError, COLLECTION_FOLLOW_ERROR_COD
63
63
  export { useCreatePostFromApp, CreatePostError, CREATE_POST_ERROR_CODES, isCreatePostErrorCode, } from './hooks/useCreatePostFromApp.js';
64
64
  export { usePrepareTrainingDataset, PrepareTrainingDatasetError, PREPARE_TRAINING_DATASET_ERROR_CODES, isPrepareTrainingDatasetErrorCode, } from './hooks/usePrepareTrainingDataset.js';
65
65
  export { useRunTraining, RunTrainingError, RUN_TRAINING_ERROR_CODES, isRunTrainingErrorCode, } from './hooks/useRunTraining.js';
66
+ export { useRunConfirmedStep, RunConfirmedStepError, RUN_CONFIRMED_STEP_ERROR_CODES, isRunConfirmedStepErrorCode, isStepQuoteId, } from './hooks/useRunConfirmedStep.js';
66
67
  export { useCheckpointPicker } from './hooks/useCheckpointPicker.js';
67
68
  export { useResourcePicker, RESOURCE_PICKER_MULTIPLE_MAX } from './hooks/useResourcePicker.js';
68
69
  export { useImageUpload } from './hooks/useImageUpload.js';
@@ -0,0 +1,37 @@
1
+ /**
2
+ * hostSurface.ts — which production host a dev host stands in for.
3
+ *
4
+ * civitai.com has two hosts. A full-page app (`app.page`) is mounted by the page
5
+ * host; a block in a `model.*` slot is mounted by the model-slot host. They
6
+ * answer a few messages differently, so a dev host has to know which of the two
7
+ * it is imitating. Both dev hosts (`createMockHost`, `createLiveHost`) read it
8
+ * from the one place they already model it: the `BLOCK_INIT` context.
9
+ */
10
+ import type { BlockContext } from '@civitai/app-sdk/blocks';
11
+ /** The slot id of a full-page app. */
12
+ export declare const PAGE_SLOT_ID = "app.page";
13
+ /**
14
+ * Is this dev host standing in for the PAGE host?
15
+ *
16
+ * `context` is the `options.context` the harness passed. `undefined` is a page:
17
+ * both dev hosts default to a complete page context when none is given.
18
+ *
19
+ * Keyed on the slot id alone, not on `isPageSlotContext`. Production picks the
20
+ * host by the slot the app is mounted in, so an incomplete `{ slotId:
21
+ * 'app.page' }` context is still a page.
22
+ */
23
+ export declare function emulatesPageHost(context: BlockContext | undefined): boolean;
24
+ /**
25
+ * The `error` the page host answers `SET_USER_CHECKPOINT` with, verbatim.
26
+ *
27
+ * A page token is bound to no model, and the server writes a checkpoint override
28
+ * into the settings row of a model-bound install. The page host therefore
29
+ * refuses every such request with `USER_CHECKPOINT_SET { requestId, ok: false,
30
+ * error }`, whatever `versionId` was sent, and makes no server call.
31
+ *
32
+ * Read from `civitai/civitai` `main` at `c1fffa4d1f`
33
+ * (`src/components/AppBlocks/PageBlockHost.tsx`). It is host-authored text: show
34
+ * it or log it, do not branch on it.
35
+ */
36
+ export declare const PAGE_CHECKPOINT_PERSIST_ERROR = "page blocks cannot persist a checkpoint override (no model binding)";
37
+ //# sourceMappingURL=hostSurface.d.ts.map
@@ -0,0 +1,38 @@
1
+ /**
2
+ * hostSurface.ts — which production host a dev host stands in for.
3
+ *
4
+ * civitai.com has two hosts. A full-page app (`app.page`) is mounted by the page
5
+ * host; a block in a `model.*` slot is mounted by the model-slot host. They
6
+ * answer a few messages differently, so a dev host has to know which of the two
7
+ * it is imitating. Both dev hosts (`createMockHost`, `createLiveHost`) read it
8
+ * from the one place they already model it: the `BLOCK_INIT` context.
9
+ */
10
+ /** The slot id of a full-page app. */
11
+ export const PAGE_SLOT_ID = 'app.page';
12
+ /**
13
+ * Is this dev host standing in for the PAGE host?
14
+ *
15
+ * `context` is the `options.context` the harness passed. `undefined` is a page:
16
+ * both dev hosts default to a complete page context when none is given.
17
+ *
18
+ * Keyed on the slot id alone, not on `isPageSlotContext`. Production picks the
19
+ * host by the slot the app is mounted in, so an incomplete `{ slotId:
20
+ * 'app.page' }` context is still a page.
21
+ */
22
+ export function emulatesPageHost(context) {
23
+ return (context?.slotId ?? PAGE_SLOT_ID) === PAGE_SLOT_ID;
24
+ }
25
+ /**
26
+ * The `error` the page host answers `SET_USER_CHECKPOINT` with, verbatim.
27
+ *
28
+ * A page token is bound to no model, and the server writes a checkpoint override
29
+ * into the settings row of a model-bound install. The page host therefore
30
+ * refuses every such request with `USER_CHECKPOINT_SET { requestId, ok: false,
31
+ * error }`, whatever `versionId` was sent, and makes no server call.
32
+ *
33
+ * Read from `civitai/civitai` `main` at `c1fffa4d1f`
34
+ * (`src/components/AppBlocks/PageBlockHost.tsx`). It is host-authored text: show
35
+ * it or log it, do not branch on it.
36
+ */
37
+ export const PAGE_CHECKPOINT_PERSIST_ERROR = 'page blocks cannot persist a checkpoint override (no model binding)';
38
+ //# sourceMappingURL=hostSurface.js.map
@@ -47,7 +47,7 @@
47
47
  * fetch chokepoints ..... 2
48
48
  * REST endpoints ........ GET /api/v1/blocks/me
49
49
  * tRPC procedures ....... 29 = blocks.* (14) + apps.shared.* (10) + apps.storage.* (5)
50
- * switch case labels .... 48, of which 10 REFUSE (enumerated under SCOPE below)
50
+ * switch case labels .... 49, of which 11 REFUSE (enumerated under SCOPE below)
51
51
  * --- END DERIVED ---
52
52
  *
53
53
  * PICKERS (Phase 1 of "make dev:live a faithful local host"): the live host
@@ -70,11 +70,12 @@
70
70
  * and row budgets enforced per (app, user) — with the REAL ceilings, so a
71
71
  * write this host accepts is one production would accept.
72
72
  *
73
- * SET_USER_CHECKPOINT (Phase 4): FORWARDED (faithful) to the block-token
74
- * `blocks.updateUserSettings` mutation — never fabricated. The default page
75
- * setup mints a PAGE token which lacks `ctx.modelId`, so the backend returns the
76
- * honest error "block token lacks modelId context"; that real outcome is
77
- * surfaced to the block. With a model-slot token it persists for real.
73
+ * SET_USER_CHECKPOINT (Phase 4): answered as the production host for the slot
74
+ * would. On a PAGE context (the default; dev-token mints page tokens) it is
75
+ * refused locally with the page host's own reply — `ok: false` and the same
76
+ * error text — and no backend call, because the page host makes none. On a
77
+ * model-slot context it is FORWARDED to the block-token
78
+ * `blocks.updateUserSettings` mutation, never fabricated.
78
79
  *
79
80
  * SHARED STORAGE (#386): the live host SERVES all TEN `SHARED_*` bridges —
80
81
  * `apps.shared.{list,get,getCount,getCounts,append,update,vote,unvote,withdraw,
@@ -113,6 +114,10 @@
113
114
  * • RUN_TRAINING — the same dev-token refusal, plus the host-chrome consent
114
115
  * dialog a run is confirmed in, which this harness does not have. Replies
115
116
  * with a refusal.
117
+ * • RUN_CONFIRMED_STEP — the server refuses a confirmed step from a dev token
118
+ * and the estimate never returns a `stepQuote` for one, and the price is
119
+ * confirmed in host chrome this harness does not have. Replies with a
120
+ * refusal.
116
121
  * • PUBLISH_GENERATION_OUTPUTS — publishing requires the viewer's signed-in
117
122
  * civitai.com session, which the local harness does not have; test
118
123
  * publishing against the mock host — `createMockHost` or `Harness` from
@@ -217,6 +222,10 @@ export interface LiveHostOptions {
217
222
  * string so the two cannot drift.
218
223
  */
219
224
  export declare const TRAINING_UNSUPPORTED_ERROR = "training is not supported in dev:live \u2014 use dev:mock";
225
+ /**
226
+ * The reply `RUN_CONFIRMED_STEP` gets in dev:live — see its handler.
227
+ */
228
+ export declare const CONFIRMED_STEP_UNSUPPORTED_ERROR = "confirmed steps are not supported in dev:live \u2014 use dev:mock";
220
229
  /**
221
230
  * Decoded payload of a block-token JWT (the claims `BlockTokenService.sign`
222
231
  * stamps — see civitai `block-token.service.ts`). Everything is optional/unknown