@civitai/blocks-react 0.65.2 → 0.67.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 +170 -2
- package/dist/hooks/useCreatePostFromApp.d.ts +2 -1
- package/dist/hooks/useCreatePostFromApp.js +2 -1
- package/dist/hooks/useSaveImage.d.ts +53 -12
- package/dist/hooks/useSaveImage.js +31 -5
- package/dist/hooks/useUploadImageBytes.d.ts +84 -0
- package/dist/hooks/useUploadImageBytes.js +77 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/internal/liveHost.js +22 -4
- package/dist/internal/mockHost.d.ts +198 -38
- package/dist/internal/mockHost.js +197 -40
- package/dist/internal/saveBytes.d.ts +102 -0
- package/dist/internal/saveBytes.js +187 -0
- package/dist/internal/uploadBytes.d.ts +89 -0
- package/dist/internal/uploadBytes.js +102 -0
- package/dist/transport/validate.d.ts +2 -1
- package/dist/transport/validate.js +8 -1
- package/package.json +4 -4
|
@@ -40,14 +40,17 @@
|
|
|
40
40
|
import { type BlockContext, type BlockResourceInfo, type BlockResourcePickerType, type BlockUploadedImageInfo, type BlockGenerationSourceImageInfo, type BuzzAccountType, type BlockBuzzTransaction, type BlockBuzzAccount, type BlockDailyCompensationResource, type BlockViewer, type BlockWildcardPack, type BlockWildcardPackErrorCode, type AppWorkflow, type BlockPublishedModel, type BlockGatedImage, type BlockCollectionFollowErrorCode, type BlockCreatePostHostError, type BlockCreatePostResult, type BlockPrepareTrainingDatasetHostError, type BlockRunTrainingHostError, type BlockTrainingRejectionReason, type ColorDomain, type SharedStorageValue, type Theme, type ViewerInfo, type WorkflowBody } from '@civitai/app-sdk/blocks';
|
|
41
41
|
/**
|
|
42
42
|
* How submits behave. `'none'` = everything succeeds; `'all'` / `'insufficient'`
|
|
43
|
-
* = every submit
|
|
44
|
-
*
|
|
43
|
+
* = every submit fails as if the viewer were OUT OF BUZZ, until an
|
|
44
|
+
* `OPEN_BUZZ_PURCHASE` resets the mode to `'none'`; `'some'` = ~1 in 3
|
|
45
|
+
* submits fail with a generic error.
|
|
45
46
|
*
|
|
46
|
-
* 🔴
|
|
47
|
-
* `'
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
47
|
+
* 🔴 EVERY FAILING MODE **REJECTS** with `WorkflowSubmitError` code
|
|
48
|
+
* `'exception'` — each emits the host's cost-less `failureSnapshot(err)`. That
|
|
49
|
+
* includes `'all'`/`'insufficient'`, which until this was fixed RESOLVED a
|
|
50
|
+
* priced `failed` snapshot. Production never does that for an empty wallet: the
|
|
51
|
+
* orchestrator refuses the debit, the server throws, and the host replies with
|
|
52
|
+
* `failureSnapshot(err)` (no `cost`). See {@link MockBuzzScenario}. A priced,
|
|
53
|
+
* RESOLVING refusal is a spend CAP — {@link MockGenerationScenario.submitCapRefusal}.
|
|
51
54
|
*/
|
|
52
55
|
export type MockHostFailMode = 'none' | 'some' | 'all' | 'insufficient';
|
|
53
56
|
/**
|
|
@@ -152,15 +155,15 @@ export interface MockGenerationScenario {
|
|
|
152
155
|
* error }` with **no `cost`**. `useBuzzWorkflow().submit()` rejects with a
|
|
153
156
|
* `WorkflowSubmitError` whose `code` is `'exception'`.
|
|
154
157
|
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
158
|
+
* This knob was added for civitai/civitai-app-starters#251, when no other submit
|
|
159
|
+
* knob produced a rejection at all. The out-of-Buzz knobs
|
|
160
|
+
* ({@link MockBuzzScenario}) now emit this same shape, because production does;
|
|
161
|
+
* this one stays as the way to inject an arbitrary server message with the
|
|
162
|
+
* wallet untouched. The `failEstimate` knob is the estimate-side twin of this
|
|
163
|
+
* one.
|
|
161
164
|
*
|
|
162
|
-
* Checked FIRST, before the disallowed-account /
|
|
163
|
-
* paths: a host-side throw pre-empts every server-side decision.
|
|
165
|
+
* Checked FIRST, before the disallowed-account / spend-cap / out-of-Buzz /
|
|
166
|
+
* generic paths: a host-side throw pre-empts every server-side decision.
|
|
164
167
|
*
|
|
165
168
|
* Default: unset (submits behave normally).
|
|
166
169
|
*/
|
|
@@ -171,6 +174,28 @@ export interface MockGenerationScenario {
|
|
|
171
174
|
* check how your block's developer-facing error surface renders one.
|
|
172
175
|
*/
|
|
173
176
|
failSubmitExceptionMessage?: string;
|
|
177
|
+
/**
|
|
178
|
+
* Make every SUBMIT come back as a server SPEND-CAP refusal — the priced,
|
|
179
|
+
* RESOLVING shape `{ workflowId:'failed', status:'failed', cost:{ total }, error }`,
|
|
180
|
+
* where `total` is this generation's cost. `useBuzzWorkflow().submit()`
|
|
181
|
+
* RESOLVES it (no rejection); the block reads `status === 'failed'`.
|
|
182
|
+
*
|
|
183
|
+
* This is what the real submit returns when a cap stops the run before
|
|
184
|
+
* anything is spent: the app's per-generation budget, the viewer's daily (or
|
|
185
|
+
* private-run) cap, the per-app consent budget, the app's spend or rate limit,
|
|
186
|
+
* or a dev-session cap. 🔴 **Buying Buzz fixes none of them**, so a block must
|
|
187
|
+
* not offer a top-up for this reply. Running OUT of Buzz is a different shape —
|
|
188
|
+
* a rejection; see {@link MockBuzzScenario}.
|
|
189
|
+
*
|
|
190
|
+
* Pass the server's text (e.g. `'insufficient buzz budget: estimate 600 exceeds
|
|
191
|
+
* budget 200'`), or `true` for a default message. Checked after the
|
|
192
|
+
* disallowed-account path and before the out-of-Buzz path, matching the real
|
|
193
|
+
* ordering: the caps are checked before the orchestrator is asked to debit.
|
|
194
|
+
* Live-tunable via `setScenario({ generation: { submitCapRefusal } })`; set it
|
|
195
|
+
* to `undefined` there to clear it. URL: `?capRefusal=1` (or `=<text>`).
|
|
196
|
+
* Default: unset.
|
|
197
|
+
*/
|
|
198
|
+
submitCapRefusal?: string | true;
|
|
174
199
|
/** A single result image url (or `(body) => url`). */
|
|
175
200
|
image?: ImageSpec;
|
|
176
201
|
/**
|
|
@@ -248,24 +273,43 @@ export declare const MOCK_TRAINING_DATASET_GONE_ERROR = "training dataset not fo
|
|
|
248
273
|
export declare const MOCK_TRAINING_NONE_ELIGIBLE_ERROR = "none of the requested images can be used for training";
|
|
249
274
|
export declare const MOCK_TRAINING_NONE_IMPORTED_ERROR = "none of the requested images could be prepared for training";
|
|
250
275
|
/**
|
|
251
|
-
* BUZZ scenario controls — simulate a
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
276
|
+
* BUZZ scenario controls — simulate a viewer who runs out of Buzz. The mock host
|
|
277
|
+
* treats `balance` as a spendable wallet: each succeeding generation DEBITS its
|
|
278
|
+
* cost, a submit whose cost would exceed the remaining balance fails as
|
|
279
|
+
* OUT OF BUZZ, and `OPEN_BUZZ_PURCHASE` REFILLS the balance.
|
|
280
|
+
*
|
|
281
|
+
* 🔴 OUT OF BUZZ **REJECTS** — `useBuzzWorkflow().submit()` throws a
|
|
282
|
+
* `WorkflowSubmitError` with code `'exception'`, the reason on
|
|
283
|
+
* `err.snapshot.error`. That is what production does. The orchestrator refuses
|
|
284
|
+
* the debit (HTTP 403), the server turns that into a thrown `BAD_REQUEST`, and
|
|
285
|
+
* the host replies with its cost-less `failureSnapshot(err)`. These knobs used to
|
|
286
|
+
* RESOLVE a priced `failed` snapshot instead, so a block tested against the mock
|
|
287
|
+
* could build a "resolved refusal → top-up" flow that production never reaches.
|
|
288
|
+
*
|
|
289
|
+
* Nothing structural tells this rejection apart from other `'exception'`s (the
|
|
290
|
+
* server message is upstream text, not a contract). To decide whether to offer
|
|
291
|
+
* a top-up, compare the viewer's spendable balance (`useBuzzBalance()`) with the
|
|
292
|
+
* quoted cost — never the error text. In the mock, that balance is
|
|
293
|
+
* {@link MockHostOptions.buzzBalance}, which is separate from `balance` below.
|
|
294
|
+
*
|
|
295
|
+
* A priced, RESOLVING refusal is a spend CAP, which a top-up cannot fix — see
|
|
296
|
+
* {@link MockGenerationScenario.submitCapRefusal}.
|
|
256
297
|
*/
|
|
257
298
|
export interface MockBuzzScenario {
|
|
258
299
|
/**
|
|
259
300
|
* Simulated spendable balance. When set, generations debit against it and a
|
|
260
|
-
* gen that would exceed it
|
|
261
|
-
* `undefined`, balance is NOT simulated (back-compat: only the legacy
|
|
262
|
-
* `failMode`
|
|
301
|
+
* gen that would exceed it is refused as out of Buzz (a REJECTION, see above).
|
|
302
|
+
* When `undefined`, balance is NOT simulated (back-compat: only the legacy
|
|
303
|
+
* `failMode` and {@link insufficient} drive the out-of-Buzz path).
|
|
263
304
|
*/
|
|
264
305
|
balance?: number;
|
|
265
306
|
/**
|
|
266
|
-
* Force every submit down the
|
|
267
|
-
*
|
|
268
|
-
* insufficient
|
|
307
|
+
* Force every submit down the out-of-Buzz path regardless of balance — it
|
|
308
|
+
* REJECTS with code `'exception'` (see above). Equivalent to the legacy
|
|
309
|
+
* `failMode: 'insufficient'`; provided here so the path is reachable from the
|
|
310
|
+
* `buzz` group alone. `OPEN_BUZZ_PURCHASE` clears it — and clears
|
|
311
|
+
* `failMode: 'insufficient' | 'all'` too (back to `'none'`), so a top-up
|
|
312
|
+
* followed by a retry succeeds whichever knob forced the out-of-Buzz state.
|
|
269
313
|
*/
|
|
270
314
|
insufficient?: boolean;
|
|
271
315
|
}
|
|
@@ -423,8 +467,8 @@ export interface MockHostOptions {
|
|
|
423
467
|
consentGrantable?: boolean;
|
|
424
468
|
/**
|
|
425
469
|
* How submits behave. Default `'none'` (all succeed). See
|
|
426
|
-
* {@link MockHostFailMode} —
|
|
427
|
-
*
|
|
470
|
+
* {@link MockHostFailMode} — every failing mode REJECTS with code
|
|
471
|
+
* `'exception'`; `'all'`/`'insufficient'` model an out-of-Buzz viewer.
|
|
428
472
|
*/
|
|
429
473
|
failMode?: MockHostFailMode;
|
|
430
474
|
/**
|
|
@@ -476,8 +520,8 @@ export interface MockHostOptions {
|
|
|
476
520
|
*/
|
|
477
521
|
generation?: MockGenerationScenario;
|
|
478
522
|
/**
|
|
479
|
-
* BUZZ scenario: simulated balance + force-
|
|
480
|
-
* {@link MockBuzzScenario}.
|
|
523
|
+
* BUZZ scenario: simulated balance + force-out-of-Buzz. Both REJECT the
|
|
524
|
+
* submit, as production does. See {@link MockBuzzScenario}.
|
|
481
525
|
*/
|
|
482
526
|
buzz?: MockBuzzScenario;
|
|
483
527
|
/**
|
|
@@ -485,8 +529,9 @@ export interface MockHostOptions {
|
|
|
485
529
|
* block via the host-mediated `GET_BUZZ_BALANCE` → `BUZZ_BALANCE_RESULT`
|
|
486
530
|
* bridge (what the `useBuzzBalance` hook reads). Distinct from the
|
|
487
531
|
* {@link MockBuzzScenario.balance} spendable-wallet knob, which only drives
|
|
488
|
-
* the
|
|
489
|
-
*
|
|
532
|
+
* the out-of-Buzz SUBMIT rejection — this is the displayable per-pool balance,
|
|
533
|
+
* and the number a block should compare with the quoted cost to decide on a
|
|
534
|
+
* top-up. Set both when exercising that flow. Absent → {@link DEFAULT_BUZZ_BALANCE} (a plausible
|
|
490
535
|
* non-zero wallet, so a block shows a balance out of the box).
|
|
491
536
|
*/
|
|
492
537
|
buzzBalance?: MockBuzzBalance;
|
|
@@ -574,8 +619,9 @@ export interface MockHostOptions {
|
|
|
574
619
|
/**
|
|
575
620
|
* The bare (post-less) scanned `Image` row ids returned from
|
|
576
621
|
* `PUBLISH_GENERATION_OUTPUTS` (what `usePublishGenerationOutputs().publish()`
|
|
577
|
-
* resolves with). Absent → {@link DEFAULT_PUBLISH_IMAGE_IDS}.
|
|
578
|
-
*
|
|
622
|
+
* resolves with). Absent → {@link DEFAULT_PUBLISH_IMAGE_IDS}. Each reply makes
|
|
623
|
+
* these ids postable once in a `CREATE_POST_FROM_APP` `published` source (see
|
|
624
|
+
* {@link createPostResult}). Live-tunable via {@link MockHost.setScenario}.
|
|
579
625
|
*/
|
|
580
626
|
publishImageIds?: number[];
|
|
581
627
|
/**
|
|
@@ -636,6 +682,21 @@ export interface MockHostOptions {
|
|
|
636
682
|
* `useCreatePostFromApp().createPost()` resolves with). Absent →
|
|
637
683
|
* {@link DEFAULT_CREATE_POST_RESULT}. Ignored when {@link createPostError} is
|
|
638
684
|
* set. Live-tunable via {@link MockHost.setScenario}.
|
|
685
|
+
*
|
|
686
|
+
* A `{ kind: 'published', imageIds }` source is checked as the server's
|
|
687
|
+
* `resolveAppPublishedImages` checks it (civitai/civitai#5639). Production
|
|
688
|
+
* accepts the viewer's own images that this app stamped and that are not in a
|
|
689
|
+
* post yet, from this session or an earlier one. The mock has no earlier
|
|
690
|
+
* sessions, so it accepts ONLY ids issued as postable on this mock instance
|
|
691
|
+
* (an accepted `OPEN_IMAGE_UPLOAD { bytes }` reply,
|
|
692
|
+
* {@link uploadImageBytesResult}, or a `PUBLISH_GENERATION_OUTPUTS` reply,
|
|
693
|
+
* {@link publishImageIds}) plus the ids seeded with {@link postableImageIds}.
|
|
694
|
+
* Any other id, a picked `useImageUpload()` id (production leaves it
|
|
695
|
+
* unstamped) or an id a previous post already adopted is refused with
|
|
696
|
+
* `an image is not available to post`, and a source with no positive integer
|
|
697
|
+
* id with `no valid image ids in a published source`. A refused post adopts
|
|
698
|
+
* nothing. `workflow` sources are not checked. The mock does not model the
|
|
699
|
+
* scan wait, the viewer's ceiling or the server's input schema.
|
|
639
700
|
*/
|
|
640
701
|
createPostResult?: BlockCreatePostResult;
|
|
641
702
|
/**
|
|
@@ -731,6 +792,104 @@ export interface MockHostOptions {
|
|
|
731
792
|
* `setScenario({ runTrainingCapRefusal: undefined })` clears it.
|
|
732
793
|
*/
|
|
733
794
|
runTrainingCapRefusal?: string;
|
|
795
|
+
/**
|
|
796
|
+
* Force `SAVE_IMAGE` to reply with this `error` instead of saving — any of
|
|
797
|
+
* the host's free-text refusals (`'image url is not allowed'`,
|
|
798
|
+
* `'image is not available'`, `'busy'`, …), or
|
|
799
|
+
* `'invalid save-image request'` to model a host that PREDATES the `bytes`
|
|
800
|
+
* variant (what production replies to `saveImage({ bytes })` until the host
|
|
801
|
+
* ships it). Applies to every variant; checked after the request-shape gate,
|
|
802
|
+
* so an invalid request still gets `invalid save-image request`. Absent →
|
|
803
|
+
* the save succeeds (subject to the `bytes` classification below).
|
|
804
|
+
* Live-tunable via {@link MockHost.setScenario}; `undefined` clears it.
|
|
805
|
+
*
|
|
806
|
+
* The `bytes` variant is classified exactly as the host's contract says
|
|
807
|
+
* (civitai/civitai-app-starters#583): PNG / WebP / JPEG by magic bytes, else
|
|
808
|
+
* UTF-8 text with no NUL (JSON when it parses and the CLEANED `filename` —
|
|
809
|
+
* `?`/`#` replaced with `_`, path dropped — ends `.json`, case-insensitive),
|
|
810
|
+
* else `file type is not allowed`; an empty buffer,
|
|
811
|
+
* `invalid save-image request`; over the host's byte cap,
|
|
812
|
+
* `file exceeds the maximum save size`. 🔴 What the mock cannot model: that
|
|
813
|
+
* the variant is PAGE-ONLY (it does not know which surface the block renders
|
|
814
|
+
* on; a slot host has no `SAVE_IMAGE` handler and rejects it with
|
|
815
|
+
* `unsupported on this host`),
|
|
816
|
+
* the host's concurrency and per-window rate limits (`busy` — force it
|
|
817
|
+
* here), and the real browser
|
|
818
|
+
* download. {@link onSaveBytes} reports what would have been saved.
|
|
819
|
+
*/
|
|
820
|
+
saveImageError?: string;
|
|
821
|
+
/**
|
|
822
|
+
* Called with each `bytes` save the mock ACCEPTS — the classified `mimeType`
|
|
823
|
+
* (one of `image/png`, `image/webp`, `image/jpeg`, `application/json`,
|
|
824
|
+
* `text/plain`), the download `filename` exactly as the host cleans it (the
|
|
825
|
+
* same character rules, then the extension forced from that type), and the
|
|
826
|
+
* `bytes` themselves.
|
|
827
|
+
* The mock cannot trigger a browser download, so this is the observable a
|
|
828
|
+
* test or harness asserts on.
|
|
829
|
+
*/
|
|
830
|
+
onSaveBytes?: (file: {
|
|
831
|
+
bytes: ArrayBuffer;
|
|
832
|
+
mimeType: string;
|
|
833
|
+
filename: string;
|
|
834
|
+
}) => void;
|
|
835
|
+
/**
|
|
836
|
+
* The moderated image an ACCEPTED `OPEN_IMAGE_UPLOAD { bytes }` replies with
|
|
837
|
+
* (what `useUploadImageBytes().upload()` resolves with). Absent →
|
|
838
|
+
* {@link DEFAULT_IMAGE_BYTES_UPLOAD}, whose `imageId` differs from
|
|
839
|
+
* {@link DEFAULT_IMAGE_UPLOAD}'s so a block that confuses a picked upload
|
|
840
|
+
* with an uploaded one is visible. Each accepted upload makes its `imageId`
|
|
841
|
+
* postable ONCE in a `CREATE_POST_FROM_APP` `{ kind: 'published', imageIds }`
|
|
842
|
+
* source on this same mock instance, as the real host allows for the app that
|
|
843
|
+
* uploaded it; see {@link createPostResult} for what the mock refuses.
|
|
844
|
+
* Live-tunable via {@link MockHost.setScenario}.
|
|
845
|
+
*
|
|
846
|
+
* The `bytes` variant runs the host's admission rules
|
|
847
|
+
* (civitai/civitai#5639, `imageUploadBytes.ts`) in the host's order: not a
|
|
848
|
+
* non-empty `ArrayBuffer`, or `purpose: 'generationSource'` →
|
|
849
|
+
* `invalid image-upload request`; over 40 MiB →
|
|
850
|
+
* `file exceeds the maximum upload size` (checked FIRST); a full rolling
|
|
851
|
+
* window (3 uploads or 80 MiB per 60 s, per install) → `busy`; not PNG /
|
|
852
|
+
* WebP / JPEG by magic bytes → `file type is not allowed`; then
|
|
853
|
+
* {@link uploadImageBytesError}, standing in for the server persist and the
|
|
854
|
+
* scan. 🔴 What the mock cannot model: that the variant is PAGE-ONLY, the
|
|
855
|
+
* `posts:write:self` scope check (force `block lacks posts:write:self scope`
|
|
856
|
+
* with {@link uploadImageBytesError}), the real scan, the no-reply for a
|
|
857
|
+
* `requestId` reused while in flight (the mock replies at once), and that a
|
|
858
|
+
* host OLDER than the variant opens its picker instead.
|
|
859
|
+
*/
|
|
860
|
+
uploadImageBytesResult?: BlockUploadedImageInfo;
|
|
861
|
+
/**
|
|
862
|
+
* Force an `OPEN_IMAGE_UPLOAD { bytes }` that passed the admission rules
|
|
863
|
+
* above to reply with this `error`: a server refusal the real host forwards
|
|
864
|
+
* (`block lacks posts:write:self scope`,
|
|
865
|
+
* `Rate limit exceeded, please retry shortly.`, a scan refusal, …), or
|
|
866
|
+
* `busy` / `no block token`. The upload still counts against the window, as
|
|
867
|
+
* on the host. Absent → it succeeds. Live-tunable via
|
|
868
|
+
* {@link MockHost.setScenario}; `undefined` clears it.
|
|
869
|
+
*/
|
|
870
|
+
uploadImageBytesError?: string;
|
|
871
|
+
/**
|
|
872
|
+
* Called with each `bytes` upload the mock ACCEPTS: the sniffed `mimeType`
|
|
873
|
+
* (`image/png`, `image/webp` or `image/jpeg`), the `filename` as the host
|
|
874
|
+
* stores it (the `useSaveImage()` bytes cleaning, the sniffed extension, at
|
|
875
|
+
* most 255 characters), and a copy of the `bytes`.
|
|
876
|
+
*/
|
|
877
|
+
onUploadImageBytes?: (file: {
|
|
878
|
+
bytes: ArrayBuffer;
|
|
879
|
+
mimeType: string;
|
|
880
|
+
filename: string;
|
|
881
|
+
}) => void;
|
|
882
|
+
/**
|
|
883
|
+
* Image ids the mock treats as already issued and postable when the host is
|
|
884
|
+
* created, as production treats an image this app stamped for the viewer in
|
|
885
|
+
* an EARLIER session that is not in a post yet. Each seeded id may be posted
|
|
886
|
+
* ONCE in a `CREATE_POST_FROM_APP` `{ kind: 'published', imageIds }` source;
|
|
887
|
+
* see {@link createPostResult} for what the mock refuses. Use it for a test
|
|
888
|
+
* that posts an image the block uploaded before the test started, instead of
|
|
889
|
+
* driving an upload or `PUBLISH_GENERATION_OUTPUTS` first. Absent → none.
|
|
890
|
+
* Read once at creation; not live-tunable.
|
|
891
|
+
*/
|
|
892
|
+
postableImageIds?: number[];
|
|
734
893
|
/**
|
|
735
894
|
* Buzz pools a `SUBMIT_WORKFLOW` must REJECT when named in `body.accountType`
|
|
736
895
|
* — simulates the real backend's content-rating clamp. The real host throws a
|
|
@@ -895,7 +1054,7 @@ export interface MockHostOptions {
|
|
|
895
1054
|
* {@link MockHost.setTheme}, which pushes a real `THEME_CHANGE` — the mock of
|
|
896
1055
|
* the viewer toggling dark mode with the block already mounted.)
|
|
897
1056
|
*/
|
|
898
|
-
export type MockHostScenarioPatch = Pick<MockHostOptions, 'consentGrantable' | 'failMode' | 'cost' | 'pollsUntilDone' | 'cannedPicks' | 'cannedImageUpload' | 'cannedGenerationSourceUpload' | 'cannedImageScan' | 'generation' | 'buzz' | 'storage' | 'shared' | 'buzzBalanceError' | 'viewerResult' | 'viewerError' | 'buzzTransactions' | 'buzzAccounts' | 'dailyCompensation' | 'buzzReadError' | 'wildcardPack' | 'wildcardPackError' | 'collectionFollowError' | 'createPostResult' | 'createPostError' | 'trainingDatasetRejected' | 'trainingDatasetError' | 'trainingQuoteTotal' | 'runTrainingError' | 'runTrainingCapRefusal' | 'appWorkflows' | 'appWorkflowsError' | 'publishImageIds' | 'publishError' | 'gatedImages' | 'gatedImagesError' | 'disallowedAccountTypes'>;
|
|
1057
|
+
export type MockHostScenarioPatch = Pick<MockHostOptions, 'consentGrantable' | 'failMode' | 'cost' | 'pollsUntilDone' | 'cannedPicks' | 'cannedImageUpload' | 'cannedGenerationSourceUpload' | 'cannedImageScan' | 'generation' | 'buzz' | 'storage' | 'shared' | 'buzzBalanceError' | 'viewerResult' | 'viewerError' | 'buzzTransactions' | 'buzzAccounts' | 'dailyCompensation' | 'buzzReadError' | 'wildcardPack' | 'wildcardPackError' | 'collectionFollowError' | 'createPostResult' | 'createPostError' | 'trainingDatasetRejected' | 'trainingDatasetError' | 'trainingQuoteTotal' | 'runTrainingError' | 'runTrainingCapRefusal' | 'appWorkflows' | 'appWorkflowsError' | 'publishImageIds' | 'publishError' | 'gatedImages' | 'gatedImagesError' | 'disallowedAccountTypes' | 'saveImageError' | 'uploadImageBytesResult' | 'uploadImageBytesError'>;
|
|
899
1058
|
/** Runtime Buzz-balance handle exposed on {@link MockHost.buzz}. */
|
|
900
1059
|
export interface MockBuzzHandle {
|
|
901
1060
|
/** Current simulated balance, or `undefined` when balance isn't simulated. */
|
|
@@ -941,8 +1100,9 @@ export declare function disallowedAccountError(accountType: BuzzAccountType): st
|
|
|
941
1100
|
* Reads the URL query toggles the gen-matrix dev harness uses, so a starter's
|
|
942
1101
|
* dev harness keeps working with `?viewer/?consent/?fail/?theme/?pick/?pickCkpt`.
|
|
943
1102
|
* Layer-1 additions: `?balance/?latency/?costPerGen/?failNext/?failRate/?seed`
|
|
944
|
-
* map onto the new scenario groups so a dev can flip
|
|
945
|
-
* failures / latency without editing code.
|
|
1103
|
+
* map onto the new scenario groups so a dev can flip out-of-Buzz /
|
|
1104
|
+
* failures / latency without editing code. `?capRefusal=1` (or `=<text>`) sets
|
|
1105
|
+
* {@link MockGenerationScenario.submitCapRefusal}.
|
|
946
1106
|
*
|
|
947
1107
|
* Returns a partial overlay applied ON TOP of explicit {@link MockHostOptions}
|
|
948
1108
|
* (URL wins — it's the interactive dev knob). No-op outside a browser.
|
|
@@ -987,7 +1147,7 @@ export declare function readMockHostUrlOptions(win?: (Window & typeof globalThis
|
|
|
987
1147
|
* const host = createMockHost({ generation: { failNext: 1, latencyMs: 1500 }, buzz: { balance: 5 } });
|
|
988
1148
|
* const uninstall = host.install();
|
|
989
1149
|
* // … drive the block / assertions …
|
|
990
|
-
* host.buzz.setBalance(0); //
|
|
1150
|
+
* host.buzz.setBalance(0); // out of Buzz mid-session: submit() now rejects ('exception')
|
|
991
1151
|
* host.setScenario({ generation: { failRate: 1 } });
|
|
992
1152
|
* uninstall();
|
|
993
1153
|
*/
|