@civitai/blocks-react 0.43.0 → 0.44.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 +116 -22
- package/dist/hooks/useAppStorage.d.ts.map +1 -1
- package/dist/hooks/useAppStorage.js +17 -4
- package/dist/hooks/useAppStorage.js.map +1 -1
- package/dist/hooks/useBuzzWorkflow.d.ts +303 -39
- package/dist/hooks/useBuzzWorkflow.d.ts.map +1 -1
- package/dist/hooks/useBuzzWorkflow.js +350 -39
- package/dist/hooks/useBuzzWorkflow.js.map +1 -1
- package/dist/hooks/useCheckpointPicker.d.ts.map +1 -1
- package/dist/hooks/useCheckpointPicker.js +7 -2
- package/dist/hooks/useCheckpointPicker.js.map +1 -1
- package/dist/hooks/useSaveImage.d.ts.map +1 -1
- package/dist/hooks/useSaveImage.js +6 -2
- package/dist/hooks/useSaveImage.js.map +1 -1
- package/dist/hooks/useSharedStorage.d.ts.map +1 -1
- package/dist/hooks/useSharedStorage.js +20 -4
- package/dist/hooks/useSharedStorage.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/liveHost.d.ts.map +1 -1
- package/dist/internal/liveHost.js +17 -0
- package/dist/internal/liveHost.js.map +1 -1
- package/dist/internal/mockHost.d.ts +48 -5
- package/dist/internal/mockHost.d.ts.map +1 -1
- package/dist/internal/mockHost.js +65 -3
- package/dist/internal/mockHost.js.map +1 -1
- package/dist/internal/validate.d.ts +66 -16
- package/dist/internal/validate.d.ts.map +1 -1
- package/dist/internal/validate.js +89 -27
- package/dist/internal/validate.js.map +1 -1
- package/package.json +2 -2
|
@@ -112,46 +112,57 @@ function sleep(ms, signal) {
|
|
|
112
112
|
* reproduces the bug one layer down: a caller who logs `message` expecting the
|
|
113
113
|
* server's words gets a constant and is exactly as stuck as before.
|
|
114
114
|
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
115
|
+
* 🔴 THREE FIELDS, THREE AUDIENCES — AND **NONE OF THEM IS VIEWER-FACING COPY**.
|
|
116
|
+
* The viewer-facing string is one the APP owns; this error carries nothing you
|
|
117
|
+
* may render as-is. Two apps migrating to `0.43.0` each had a `catch` that piped
|
|
118
|
+
* `err.message` straight into rendered UI, and each would have shipped
|
|
119
|
+
* `estimate did not return a usable price (failed) — reason on .snapshot.error`
|
|
120
|
+
* to end users; both were caught only by a test asserting that exact string.
|
|
121
|
+
*
|
|
122
|
+
* - {@link WorkflowEstimateError.snapshot}`.error` — **the DIAGNOSTIC read.**
|
|
123
|
+
* The server's own words, verbatim. **Server-authored and UNSANITISED**:
|
|
124
|
+
* civitai's `errorHandling.ts` documents that raw upstream text —
|
|
125
|
+
* Prisma/`pg` column and constraint names among it — can reach this field.
|
|
126
|
+
* Log it, or show it in a developer-facing error surface. **NEVER render it
|
|
127
|
+
* verbatim into markup**, and think before shipping it to a third-party
|
|
128
|
+
* error tracker.
|
|
129
|
+
* - {@link WorkflowEstimateError.message} — **DEVELOPER-FACING.** A CONSTANT
|
|
130
|
+
* template with only `code` interpolated. It contains no server text and
|
|
131
|
+
* nothing to sanitise, so it is safe to LOG and safe to let a stack trace
|
|
132
|
+
* print — that is deliberate, because `message` is what an uncaught
|
|
123
133
|
* rejection prints and what a third-party block's error reporter ships
|
|
124
134
|
* upstream by default, and this is a package consumed by third-party code.
|
|
125
|
-
*
|
|
126
|
-
*
|
|
135
|
+
* **It is NOT intended for display to viewers**: it names an internal field
|
|
136
|
+
* path, it is not localised, and **its exact wording is NOT a contract** —
|
|
137
|
+
* it can change in any release, so a UI built on it silently rots.
|
|
138
|
+
* - {@link WorkflowEstimateError.code} — **the BRANCH TARGET**, and the only
|
|
139
|
+
* stable one. Switch on it to pick a viewer-facing string YOUR APP owns
|
|
140
|
+
* (see the mapper in the `@example` on {@link useBuzzWorkflow}).
|
|
127
141
|
*
|
|
128
142
|
* 🔴 DO NOT "SIMPLIFY" THIS BY PUTTING `snapshot.error` BACK ON `message`. The
|
|
129
143
|
* two fields are split on purpose and the split is what keeps database internals
|
|
130
144
|
* off a third party's default-printed surface. `test/useBuzzWorkflow.test.tsx`
|
|
131
145
|
* pins it with a realistic `Unique constraint failed: Key (email)=(…)` fixture.
|
|
132
146
|
*
|
|
133
|
-
* 🔴
|
|
134
|
-
* `WORKFLOW_SUBMITTED`
|
|
135
|
-
* `status
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
* own blast radius on the recovery path (a fix MUST keep the budget-rejection arm
|
|
141
|
-
* RESOLVING, or the top-up flow breaks) and is deliberately NOT attempted here —
|
|
142
|
-
* so `submit` is left resolving BOTH, and the accompanying test pins only the
|
|
143
|
-
* budget-rejection producer, not a claim that submit is correct.
|
|
144
|
-
*
|
|
145
|
-
* 🔴 TRACKED, NOT FORGOTTEN: civitai/civitai-app-starters#251. Read it before
|
|
146
|
-
* touching `submit`'s failure handling.
|
|
147
|
+
* 🔴 `submit` HAS ITS OWN CLASS — {@link WorkflowSubmitError} — BECAUSE ITS
|
|
148
|
+
* DISCRIMINATOR IS DIFFERENT. On `WORKFLOW_SUBMITTED` both producers report
|
|
149
|
+
* `status:'failed'`, so `status` separates nothing there and `cost` presence is
|
|
150
|
+
* what does. Do not merge the two classes or reuse this one for submit: the
|
|
151
|
+
* question each answers is different ("was a usable price returned?" vs "was a
|
|
152
|
+
* workflow actually queued?") and so is the arm that must keep resolving.
|
|
153
|
+
* (civitai/civitai-app-starters#251, the `submit` half of civitai/civitai#4159.)
|
|
147
154
|
*/
|
|
148
155
|
export class WorkflowEstimateError extends Error {
|
|
149
156
|
/**
|
|
150
157
|
* WHICH PRODUCER this was, structurally — so a caller branches on an enum
|
|
151
158
|
* rather than on prose. It is the ONLY stable branch target here:
|
|
152
159
|
* `snapshot.error` is server-authored and can change without notice, and
|
|
153
|
-
* {@link WorkflowEstimateError.message} is a generic summary
|
|
154
|
-
* wording is not a contract either.
|
|
160
|
+
* {@link WorkflowEstimateError.message} is a generic developer-facing summary
|
|
161
|
+
* whose exact wording is not a contract either.
|
|
162
|
+
*
|
|
163
|
+
* 🔴 THIS IS WHAT A VIEWER-FACING MESSAGE MUST BE DERIVED FROM. Branch on
|
|
164
|
+
* `code` and return a string your app owns and localises; never render
|
|
165
|
+
* `message` or `snapshot.error` to a viewer.
|
|
155
166
|
*
|
|
156
167
|
* - `'failed'` — the reply's `status` is `'failed'`. USUALLY that means
|
|
157
168
|
* `blocks.estimateWorkflow` threw server-side and the host posted its
|
|
@@ -176,8 +187,8 @@ export class WorkflowEstimateError extends Error {
|
|
|
176
187
|
* will not price — the whole point of civitai/civitai#4159), but civitai's
|
|
177
188
|
* `errorHandling.ts` documents that raw upstream text — Prisma/`pg` column and
|
|
178
189
|
* constraint names among it — can reach that field. Log it, show it in a
|
|
179
|
-
* developer-facing error surface,
|
|
180
|
-
* ship it to a third-party error tracker without thinking about it.
|
|
190
|
+
* developer-facing error surface, **NEVER render it verbatim into markup**,
|
|
191
|
+
* and do not ship it to a third-party error tracker without thinking about it.
|
|
181
192
|
*/
|
|
182
193
|
snapshot;
|
|
183
194
|
constructor(snapshot, code) {
|
|
@@ -190,20 +201,245 @@ export class WorkflowEstimateError extends Error {
|
|
|
190
201
|
// wire), so routing the raw text to `.snapshot.error` alone costs a
|
|
191
202
|
// deliberate debugger nothing and changes only the DISPOSITION. `code` is
|
|
192
203
|
// carried IN the message so an uncaught rejection is still self-describing.
|
|
204
|
+
//
|
|
205
|
+
// 🔴 IT IS DEVELOPER-FACING, NOT VIEWER-FACING — it names an internal field
|
|
206
|
+
// path and is not localised, so it must not reach rendered UI. Its exact
|
|
207
|
+
// wording is NOT a contract: it may change in any release. A viewer-facing
|
|
208
|
+
// string is derived from `code` by the app (see the class docs).
|
|
193
209
|
super(`estimate did not return a usable price (${code}) — reason on .snapshot.error`);
|
|
194
210
|
this.name = 'WorkflowEstimateError';
|
|
195
211
|
this.code = code;
|
|
196
212
|
this.snapshot = snapshot;
|
|
197
213
|
}
|
|
198
214
|
}
|
|
215
|
+
/**
|
|
216
|
+
* The `workflowId` the host stamps on a snapshot it SYNTHESISED itself, rather
|
|
217
|
+
* than one derived from a real orchestrator workflow.
|
|
218
|
+
*
|
|
219
|
+
* 🔴 THIS IS A REAL WIRE INVARIANT, NOT A HEURISTIC. Every reply the host builds
|
|
220
|
+
* itself goes through `failureSnapshot()` (civitai
|
|
221
|
+
* `src/components/AppBlocks/failureSnapshot.ts`), which hardcodes this exact
|
|
222
|
+
* literal — from its `catch` arms AND from non-catch short-circuits such as the
|
|
223
|
+
* moderator-review nack. The server's `snapshotFromWorkflow` instead returns
|
|
224
|
+
* `workflow.id ?? 'whatif'`.
|
|
225
|
+
*
|
|
226
|
+
* 🔴 COMPARE IT WITH `===` — EXACT AND CASE-SENSITIVE. Every looser comparison
|
|
227
|
+
* quietly reclassifies a reply the host did NOT synthesise into the reassuring
|
|
228
|
+
* "nothing to report" arm, and each direction has to be pinned separately:
|
|
229
|
+
* `.startsWith()` accepts `failed-x`, `.endsWith()` accepts `x-failed`, and a
|
|
230
|
+
* case-folding compare accepts `FAILED`. All three are WIDENINGS, which a
|
|
231
|
+
* delete-only mutation sweep cannot see. Pinned by three near-miss fixtures in
|
|
232
|
+
* `test/useBuzzWorkflow.test.tsx`; an earlier version of this comment said only
|
|
233
|
+
* "never a prefix test" while the tests pinned only that one direction — a
|
|
234
|
+
* description wider than its coverage.
|
|
235
|
+
*
|
|
236
|
+
* 🔴 THE FAIL-SAFETY IS ONE-DIRECTIONAL, AND SAYING OTHERWISE WAS A BUG IN AN
|
|
237
|
+
* EARLIER REVISION OF THIS COMMENT. An id that is NOT this literal is treated as
|
|
238
|
+
* possibly-charged, so an unrecognised id never buys the reassuring reading —
|
|
239
|
+
* that direction holds. The converse does NOT: this sentinel is also what the
|
|
240
|
+
* host stamps when a submit threw for reasons that may still have created and
|
|
241
|
+
* charged a workflow (a lost response, an in-progress idempotency conflict). So
|
|
242
|
+
* the sentinel arm means "the host had no workflow to report", never "nothing
|
|
243
|
+
* happened". See {@link WorkflowSubmitError.code}.
|
|
244
|
+
*/
|
|
245
|
+
const HOST_SYNTHESISED_WORKFLOW_ID = 'failed';
|
|
246
|
+
/**
|
|
247
|
+
* Thrown by {@link UseBuzzWorkflowReturn.submit} when the host's reply carries no
|
|
248
|
+
* usable workflow outcome — either the submit ERRORED before anything was queued,
|
|
249
|
+
* or a workflow-shaped reply came back already failed with no price.
|
|
250
|
+
*
|
|
251
|
+
* 🔴 READ {@link WorkflowSubmitError.code} BEFORE SAYING ANYTHING ABOUT MONEY.
|
|
252
|
+
* The two codes differ on exactly that, and getting it wrong is expensive in both
|
|
253
|
+
* directions — see the per-code notes below.
|
|
254
|
+
*
|
|
255
|
+
* 🔴 WHY THIS EXISTS — civitai/civitai-app-starters#251, the `submit` half of
|
|
256
|
+
* civitai/civitai#4159. A server-side `blocks.submitWorkflow` throw cannot reach
|
|
257
|
+
* the block as a rejection; the reply crosses `postMessage`. The host instead
|
|
258
|
+
* posts a well-formed `WORKFLOW_SUBMITTED` carrying its `failureSnapshot(err)` —
|
|
259
|
+
* `{ workflowId: 'failed', status: 'failed', error: '<server message>' }`, with
|
|
260
|
+
* **no `cost`**. That snapshot is perfectly legal, so `submit()` resolved it and
|
|
261
|
+
* moved the hook to `'done'`, handing the caller a "workflow" that does not
|
|
262
|
+
* exist.
|
|
263
|
+
*
|
|
264
|
+
* 🔴 TWO PRODUCERS, ONE `status` — AND HERE `status` DISCRIMINATES NOTHING. This
|
|
265
|
+
* is what makes submit's version of the defect different from estimate's, and
|
|
266
|
+
* why {@link WorkflowEstimateError}'s guard could not simply be copied:
|
|
267
|
+
*
|
|
268
|
+
* - **budget / spend-cap REJECTION** — a legitimate OUTCOME. The server quotes
|
|
269
|
+
* the price it refused to charge, so the snapshot carries a numeric
|
|
270
|
+
* `cost.total`. Blocks recover from it (open a top-up flow), so it MUST keep
|
|
271
|
+
* RESOLVING. Every such exit on the server attaches a cost: the per-call
|
|
272
|
+
* `buzzBudget` gate, the per-user daily Buzz cap, the per-app aggregate and
|
|
273
|
+
* velocity caps, and the dev-tunnel session cap — on all three body kinds.
|
|
274
|
+
* - **caught server EXCEPTION** — `failureSnapshot(err)`, no `cost`, and the
|
|
275
|
+
* `'failed'` sentinel id. The host had no workflow to report — usually
|
|
276
|
+
* nothing was queued, but a lost response or an in-progress idempotency
|
|
277
|
+
* conflict lands here too. Rejects as `'exception'`.
|
|
278
|
+
* - **a reply that came back failed and unpriced with a NON-sentinel id** —
|
|
279
|
+
* normally a genuine orchestrator id, no `cost` (the server's
|
|
280
|
+
* `snapshotFromWorkflow` omits the key whenever `workflow.cost?.total` is not
|
|
281
|
+
* numeric). Rejects as `'workflow-failed'`, and **money may already be
|
|
282
|
+
* committed** — see that code's note.
|
|
283
|
+
*
|
|
284
|
+
* So the rule is `cost` presence, and ONLY among failure-shaped replies: an
|
|
285
|
+
* ordinary in-flight submit (`{ workflowId:'wf_…', status:'pending' }`) is
|
|
286
|
+
* cost-less too and must keep resolving, which is why the guard is not keyed on
|
|
287
|
+
* `cost` alone either. Both clauses are load-bearing; each has its own control
|
|
288
|
+
* in `test/useBuzzWorkflow.test.tsx`.
|
|
289
|
+
*
|
|
290
|
+
* 🔴 THE GUARD IS DELIBERATELY NOT WIDENED TO ALL TERMINAL STATUSES. A cost-less
|
|
291
|
+
* `succeeded` / `canceled` / `expired` reply is a legitimate outcome and must
|
|
292
|
+
* RESOLVE; only `'failed'` is unusable. Swapping the clause for
|
|
293
|
+
* `TERMINAL_STATUSES.has(...)` is a silent WIDENING that a delete-only mutation
|
|
294
|
+
* sweep cannot see — `test/useBuzzWorkflow.test.tsx` pins each of those three
|
|
295
|
+
* statuses explicitly for exactly that reason.
|
|
296
|
+
*
|
|
297
|
+
* 🔴 THREE FIELDS, THREE AUDIENCES — AND **NONE OF THEM IS VIEWER-FACING COPY**,
|
|
298
|
+
* exactly as on {@link WorkflowEstimateError}. Two apps migrating to `0.43.0`
|
|
299
|
+
* piped that class's `err.message` straight into rendered UI
|
|
300
|
+
* (civitai/civitai-app-starters#253); the same split applies here so the same
|
|
301
|
+
* mistake is not re-enabled on the submit path.
|
|
302
|
+
*
|
|
303
|
+
* - {@link WorkflowSubmitError.snapshot}`.error` — **the DIAGNOSTIC read.** The
|
|
304
|
+
* server's own words, verbatim. **Server-authored and UNSANITISED**:
|
|
305
|
+
* civitai's `errorHandling.ts` documents that raw upstream text —
|
|
306
|
+
* Prisma/`pg` column and constraint names among it — can reach this field.
|
|
307
|
+
* Log it, or show it in a developer-facing error surface. **NEVER render it
|
|
308
|
+
* verbatim into markup**, and think before shipping it to a third-party
|
|
309
|
+
* error tracker.
|
|
310
|
+
* - {@link WorkflowSubmitError.message} — **DEVELOPER-FACING.** A CONSTANT
|
|
311
|
+
* template with only `code` interpolated. It contains no server text, so it
|
|
312
|
+
* is safe to LOG and safe to let a stack trace print — deliberate, because
|
|
313
|
+
* `message` is what an uncaught rejection prints and what a third-party
|
|
314
|
+
* block's error reporter ships upstream by default. **It is NOT intended for
|
|
315
|
+
* display to viewers** and **its exact wording is NOT a contract.**
|
|
316
|
+
* - {@link WorkflowSubmitError.code} — **the BRANCH TARGET**, and the only
|
|
317
|
+
* stable one.
|
|
318
|
+
*
|
|
319
|
+
* 🔴 DO NOT "SIMPLIFY" THIS BY PUTTING `snapshot.error` BACK ON `message`, and do
|
|
320
|
+
* not widen the guard to reject a budget rejection. The first re-opens #253; the
|
|
321
|
+
* second breaks the top-up recovery flow, which is the one thing #251 says must
|
|
322
|
+
* not break.
|
|
323
|
+
*/
|
|
324
|
+
export class WorkflowSubmitError extends Error {
|
|
325
|
+
/**
|
|
326
|
+
* WHICH PRODUCER this was, structurally — so a caller branches on an enum
|
|
327
|
+
* rather than on prose. It is the ONLY stable branch target here:
|
|
328
|
+
* `snapshot.error` is server-authored and can change without notice, and
|
|
329
|
+
* {@link WorkflowSubmitError.message} is a generic developer-facing summary
|
|
330
|
+
* whose exact wording is not a contract either.
|
|
331
|
+
*
|
|
332
|
+
* 🔴 THE TWO CODES DIFFER ON WHETHER MONEY MOVED. Do not collapse them, and do
|
|
333
|
+
* not write recovery copy that ignores the distinction.
|
|
334
|
+
*
|
|
335
|
+
* - `'exception'` — the host built this reply itself
|
|
336
|
+
* (`failureSnapshot(err)`, id {@link HOST_SYNTHESISED_WORKFLOW_ID}), from a
|
|
337
|
+
* `catch` OR from a non-catch short-circuit such as the moderator-review
|
|
338
|
+
* nack. **It means the host had no workflow to report — NOT that nothing
|
|
339
|
+
* happened.**
|
|
340
|
+
*
|
|
341
|
+
* 🔴 USUALLY nothing was queued and nothing was charged, and a retry is the
|
|
342
|
+
* sensible recovery. But the same shape is reachable when a workflow MAY have
|
|
343
|
+
* been created and charged, because the failure happened after the request
|
|
344
|
+
* left the block:
|
|
345
|
+
* - a **lost response** — the server's own catch concedes that "the
|
|
346
|
+
* orchestrator externalId dedupe still protects a retry that DID create a
|
|
347
|
+
* workflow server-side despite a lost response". What that path refunds
|
|
348
|
+
* is its own reservation and cap counters; the Buzz debit happens inside
|
|
349
|
+
* the orchestrator's submit.
|
|
350
|
+
* - an **in-progress idempotency CONFLICT** — a concurrent first attempt is
|
|
351
|
+
* still in flight and may be committing right now. Retrying blind is the
|
|
352
|
+
* worst option there.
|
|
353
|
+
* - a **transient transport failure** (5xx / 408 / 429 / 401), which the
|
|
354
|
+
* `dev:live` host collapses into this same shape.
|
|
355
|
+
*
|
|
356
|
+
* So prefer **reusing the same {@link SubmitWorkflowOptions.idempotencyKey}**
|
|
357
|
+
* on retry rather than letting a fresh one be minted, and do not render
|
|
358
|
+
* "nothing was charged" to a viewer as a certainty. There is no workflow id
|
|
359
|
+
* on this arm to poll.
|
|
360
|
+
*
|
|
361
|
+
* - `'workflow-failed'` — the id was NOT the host's sentinel, so the reply came
|
|
362
|
+
* from the server's `snapshotFromWorkflow`: normally a real orchestrator id,
|
|
363
|
+
* already `'failed'` and with no price.
|
|
364
|
+
*
|
|
365
|
+
* 🔴 **DO NOT TELL THE VIEWER NOTHING WAS CHARGED, AND DO NOT BLIND-RETRY.**
|
|
366
|
+
* Server-side, `blocks.submitWorkflow` treats *any resolved* submit as
|
|
367
|
+
* money-COMMITTED: its own comment is "A resolved submit is money-COMMITTED
|
|
368
|
+
* (the reservation is kept regardless of snapshot status)… we do NOT refund
|
|
369
|
+
* on a non-throwing failed snapshot", and `finalizeGenIdempotency` runs on
|
|
370
|
+
* that path. So Buzz may already be spent for this call.
|
|
371
|
+
*
|
|
372
|
+
* A retry is a SECOND reservation unless you reuse the same
|
|
373
|
+
* {@link SubmitWorkflowOptions.idempotencyKey} — `submit()` mints a fresh key
|
|
374
|
+
* per call by default, so an automatic retry double-reserves.
|
|
375
|
+
*
|
|
376
|
+
* 🔴 `err.snapshot.workflowId` IS USUALLY POLLABLE, BUT CHECK IT FIRST — this
|
|
377
|
+
* arm is defined by what the id is NOT. The server emits `workflow.id ??
|
|
378
|
+
* 'whatif'`, and it treats **both** `'failed'` and `'whatif'` as non-workflow
|
|
379
|
+
* sentinels (it skips its own persistence and settle steps on either). So
|
|
380
|
+
* `'whatif'` lands here — correctly, because the cautious money reading still
|
|
381
|
+
* applies — but there is nothing to poll. Guard with
|
|
382
|
+
* `err.snapshot.workflowId !== 'whatif'` before calling
|
|
383
|
+
* {@link UseBuzzWorkflowReturn.watch} / {@link UseBuzzWorkflowReturn.poll} to
|
|
384
|
+
* learn the workflow's actual fate before spending again.
|
|
385
|
+
*
|
|
386
|
+
* A union rather than a boolean so a future producer gets its own code without
|
|
387
|
+
* breaking a caller's `switch`.
|
|
388
|
+
*
|
|
389
|
+
* 🔴 A BUDGET REJECTION NEVER ARRIVES HERE. It resolves, and is read off the
|
|
390
|
+
* returned snapshot as `status === 'failed'` with a numeric `cost.total`.
|
|
391
|
+
*/
|
|
392
|
+
code;
|
|
393
|
+
/**
|
|
394
|
+
* The snapshot the host replied with, VERBATIM — including `snapshot.error`,
|
|
395
|
+
* the server's own words, when there are any.
|
|
396
|
+
*
|
|
397
|
+
* 🔴 `snapshot.error` IS SERVER-AUTHORED AND UNSANITISED. It is where the
|
|
398
|
+
* reason lives, but raw upstream text — Prisma/`pg` column and constraint
|
|
399
|
+
* names among it — can reach that field. Log it, show it in a developer-facing
|
|
400
|
+
* error surface, **NEVER render it verbatim into markup**.
|
|
401
|
+
*/
|
|
402
|
+
snapshot;
|
|
403
|
+
constructor(snapshot, code) {
|
|
404
|
+
// 🔴 GENERIC, AND DELIBERATELY NOT `snapshot.error` — see the class docs and
|
|
405
|
+
// civitai/civitai-app-starters#253. `message` is the field an uncaught
|
|
406
|
+
// rejection PRINTS and the field a third-party block's error reporter ships
|
|
407
|
+
// upstream by default, so putting the raw server string here would put
|
|
408
|
+
// database internals on the default-printed surface of somebody else's code.
|
|
409
|
+
// Exposure to the block is identical either way (`snapshot.error` was always
|
|
410
|
+
// on the wire); only the DISPOSITION changes. `code` is carried IN the
|
|
411
|
+
// message so an uncaught rejection is still self-describing.
|
|
412
|
+
//
|
|
413
|
+
// 🔴 IT MAKES NO CLAIM ABOUT MONEY, AND THAT IS THE POINT. An earlier draft
|
|
414
|
+
// read "submit did not queue a workflow", which is FALSE for
|
|
415
|
+
// `'workflow-failed'` — that arm has a workflow-shaped reply and
|
|
416
|
+
// possibly-committed
|
|
417
|
+
// spend. A single template that is true for BOTH codes cannot mislead a
|
|
418
|
+
// developer reading a stack trace; the money semantics live on `code`.
|
|
419
|
+
super(`submit did not return a usable workflow (${code}) — reason on .snapshot.error`);
|
|
420
|
+
this.name = 'WorkflowSubmitError';
|
|
421
|
+
this.code = code;
|
|
422
|
+
this.snapshot = snapshot;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
199
425
|
/**
|
|
200
426
|
* Orchestrates the estimate → confirm → submit → poll dance through the
|
|
201
427
|
* host-mediated `postMessage` path.
|
|
202
428
|
*
|
|
203
|
-
* The host enforces budget rules (`cost_estimate <= token.buzzBudget`)
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
429
|
+
* The host enforces budget rules (`cost_estimate <= token.buzzBudget`) before
|
|
430
|
+
* forwarding to the orchestrator.
|
|
431
|
+
*
|
|
432
|
+
* 🔴 A BUDGET REFUSAL DOES **NOT** REJECT — IT RESOLVES. It comes back as a
|
|
433
|
+
* snapshot with `status: 'failed'`, an `error` string and the `cost` the server
|
|
434
|
+
* declined to charge, and THAT resolved shape is the cue to call
|
|
435
|
+
* `useBuzzPurchase().openPurchaseModal()`. What DOES reject is a submit with no
|
|
436
|
+
* usable outcome — see {@link WorkflowSubmitError}. Routing a rejection into a
|
|
437
|
+
* top-up sells Buzz for a failure Buzz cannot fix.
|
|
438
|
+
*
|
|
439
|
+
* 🔴 NOR IS EVERY RESOLVED `'failed'` AN AFFORDABILITY PROBLEM. The per-app
|
|
440
|
+
* velocity limit, the per-app aggregate daily cap, a fail-closed "temporarily
|
|
441
|
+
* unavailable" deny and a missing price quote are all priced, resolving outcomes
|
|
442
|
+
* too. Branch on the message/your own policy before offering to sell anything.
|
|
207
443
|
*
|
|
208
444
|
* AFTER `submit` FLIPS `status` TO `'polling'`, USE `watch(workflowId)`. It owns
|
|
209
445
|
* the loop, resolves on the terminal snapshot, and pushes every intermediate
|
|
@@ -246,21 +482,58 @@ export class WorkflowEstimateError extends Error {
|
|
|
246
482
|
* modelVersionId,
|
|
247
483
|
* params: { prompt: 'a cat' },
|
|
248
484
|
* };
|
|
485
|
+
* // A viewer-facing string YOUR APP owns, chosen by `code`. Neither
|
|
486
|
+
* // `err.message` (developer-facing, wording not a contract) nor
|
|
487
|
+
* // `err.snapshot.error` (server-authored, unsanitised) may be rendered.
|
|
488
|
+
* const estimateFailureMessage = (err: WorkflowEstimateError) =>
|
|
489
|
+
* err.code === 'no-cost'
|
|
490
|
+
* ? 'We could not get a price for this configuration. Try adjusting it.'
|
|
491
|
+
* : 'Pricing is unavailable right now. Please try again shortly.';
|
|
492
|
+
*
|
|
249
493
|
* // 🔴 estimate() REJECTS when the reply carries no usable price — an errored
|
|
250
494
|
* // estimate, or one that came back with no numeric cost. ALWAYS catch it.
|
|
251
495
|
* try {
|
|
252
496
|
* await estimate(body); // status 'estimating' → 'confirming' (cost in result.cost.total)
|
|
253
497
|
* } catch (err) {
|
|
254
498
|
* // status is now 'error'; `result` holds the unusable snapshot, never a
|
|
255
|
-
* // STALE priced one.
|
|
256
|
-
* // reason is on err.snapshot.error (unsanitised — log it, don't render it as
|
|
257
|
-
* // trusted copy). See WorkflowEstimateError.
|
|
499
|
+
* // STALE priced one. See WorkflowEstimateError for the three-audience split.
|
|
258
500
|
* if (!(err instanceof WorkflowEstimateError)) throw err;
|
|
259
|
-
* logForDebugging(err.snapshot.error);
|
|
260
|
-
* showError(
|
|
501
|
+
* logForDebugging(err.message, err.snapshot.error); // developer-facing: LOG only
|
|
502
|
+
* showError(estimateFailureMessage(err)); // viewer-facing: app-owned
|
|
503
|
+
* }
|
|
504
|
+
* // 🔴 submit() REJECTS when the reply carries no usable workflow outcome, so it
|
|
505
|
+
* // needs a catch too — and the two codes differ on whether MONEY MOVED.
|
|
506
|
+
* try {
|
|
507
|
+
* const snap = await submit(body); // status 'submitting' → 'polling'
|
|
508
|
+
* if (snap.status === 'failed') {
|
|
509
|
+
* // RESOLVED + priced = a server outcome (affordability, a cap, a velocity
|
|
510
|
+
* // limit, a transient deny). Only some of those are fixable by buying Buzz.
|
|
511
|
+
* showError(submitOutcomeMessage(snap));
|
|
512
|
+
* } else {
|
|
513
|
+
* const done = await watch(snap.workflowId, { onUpdate: render }); // → terminal
|
|
514
|
+
* if (done.status === 'succeeded') setImages(done.imageUrls ?? []);
|
|
515
|
+
* }
|
|
516
|
+
* } catch (err) {
|
|
517
|
+
* if (!(err instanceof WorkflowSubmitError)) throw err;
|
|
518
|
+
* logForDebugging(err.message, err.snapshot.error); // developer-facing: LOG only
|
|
519
|
+
* // 🔴 TWO SEPARATE QUESTIONS: `code` decides what you may say about MONEY,
|
|
520
|
+
* // the id decides only whether there is anything to POLL. Conjoining them
|
|
521
|
+
* // sends a 'whatif' id into the reassuring arm.
|
|
522
|
+
* if (err.code === 'workflow-failed') {
|
|
523
|
+
* // Spend may already be committed. Do NOT retry blindly (that mints a new
|
|
524
|
+
* // idempotency key = a second reservation) and do NOT claim nothing was
|
|
525
|
+
* // charged.
|
|
526
|
+
* showError('The generation may have started but did not complete.');
|
|
527
|
+
* // 'whatif' is a non-workflow sentinel — nothing behind it to poll.
|
|
528
|
+
* if (err.snapshot.workflowId !== 'whatif') {
|
|
529
|
+
* await watch(err.snapshot.workflowId, { onUpdate: render });
|
|
530
|
+
* }
|
|
531
|
+
* } else {
|
|
532
|
+
* // 'exception' — usually nothing was queued. Retry, but reuse the SAME
|
|
533
|
+
* // idempotencyKey: a lost response can also land here.
|
|
534
|
+
* showError('Could not start the generation. Please try again.');
|
|
535
|
+
* }
|
|
261
536
|
* }
|
|
262
|
-
* const snap = await submit(body); // status 'submitting' → 'polling'; returns a workflowId
|
|
263
|
-
* const done = await watch(snap.workflowId, { onUpdate: render }); // → terminal
|
|
264
537
|
*/
|
|
265
538
|
export function useBuzzWorkflow() {
|
|
266
539
|
const [status, setStatus] = useState('idle');
|
|
@@ -324,7 +597,45 @@ export function useBuzzWorkflow() {
|
|
|
324
597
|
const idempotencyKey = options?.idempotencyKey ?? generateIdempotencyKey();
|
|
325
598
|
try {
|
|
326
599
|
const { snapshot } = await sendTypedRequest(getTransport(), { type: 'SUBMIT_WORKFLOW', payload: { body, idempotencyKey } }, 'WORKFLOW_SUBMITTED', { timeoutMs: WORKFLOW_REQUEST_TIMEOUT_MS });
|
|
600
|
+
// 🔴 PUBLISH THE SNAPSHOT BEFORE THE REJECTION BELOW, for the same reason
|
|
601
|
+
// `estimate` does: a block may render from `result` rather than from the
|
|
602
|
+
// returned value, and jumping over this line would leave the PREVIOUS
|
|
603
|
+
// submit's snapshot in place — a live control pointing at a workflow THIS
|
|
604
|
+
// submit did not queue.
|
|
327
605
|
setResult(snapshot);
|
|
606
|
+
// 🔴 AN ERRORED SUBMIT MUST REJECT (civitai/civitai-app-starters#251, the
|
|
607
|
+
// `submit` half of civitai/civitai#4159). Two producers report
|
|
608
|
+
// `status:'failed'` and `status` separates neither:
|
|
609
|
+
//
|
|
610
|
+
// - a budget / spend-cap REJECTION is an OUTCOME the block recovers from
|
|
611
|
+
// (open a top-up flow). The server quotes the price it refused to
|
|
612
|
+
// charge, so `cost.total` is present. It RESOLVES — turning this arm
|
|
613
|
+
// into a throw is the one change that would break the recovery path.
|
|
614
|
+
// - a failure-shaped reply with NO `cost` is not a usable outcome. It
|
|
615
|
+
// REJECTS — and the `code` says which kind, because they differ on
|
|
616
|
+
// whether money moved (see WorkflowSubmitError.code).
|
|
617
|
+
//
|
|
618
|
+
// BOTH clauses are load-bearing. Dropping `status === 'failed'` would
|
|
619
|
+
// reject every ordinary in-flight reply (`{status:'pending'}` is cost-less
|
|
620
|
+
// too); dropping the cost test would reject the budget rejection. And the
|
|
621
|
+
// test is `typeof … !== 'number'`, never `!snapshot.cost?.total`: `0` is a
|
|
622
|
+
// real price and falsy.
|
|
623
|
+
//
|
|
624
|
+
// 🔴 `status === 'failed'`, NOT `TERMINAL_STATUSES.has(status)`. Widening it
|
|
625
|
+
// would reject cost-less `succeeded`/`canceled`/`expired` replies, which are
|
|
626
|
+
// legitimate outcomes the server really does emit without a price
|
|
627
|
+
// (`snapshotFromWorkflow` omits `cost` on any non-numeric total). That
|
|
628
|
+
// WIDENING is invisible to a mutation sweep that only deletes clauses, so
|
|
629
|
+
// all three statuses are pinned by their own fixtures in the test file.
|
|
630
|
+
if (snapshot.status === 'failed' && typeof snapshot.cost?.total !== 'number') {
|
|
631
|
+
// 🔴 WHICH ARM: the host stamps its own synthesised failures with the
|
|
632
|
+
// `'failed'` sentinel, while a server-built reply carries `workflow.id`
|
|
633
|
+
// (or the `'whatif'` sentinel).
|
|
634
|
+
// Anything unrecognised falls to `'workflow-failed'`, the arm that assumes
|
|
635
|
+
// money MAY be committed — an unknown id must never buy the reassuring
|
|
636
|
+
// "nothing was charged" reading.
|
|
637
|
+
throw new WorkflowSubmitError(snapshot, snapshot.workflowId === HOST_SYNTHESISED_WORKFLOW_ID ? 'exception' : 'workflow-failed');
|
|
638
|
+
}
|
|
328
639
|
setStatus(TERMINAL_STATUSES.has(snapshot.status) ? 'done' : 'polling');
|
|
329
640
|
return snapshot;
|
|
330
641
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useBuzzWorkflow.js","sourceRoot":"","sources":["../../src/hooks/useBuzzWorkflow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAI9C,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAEpF;;;;GAIG;AACH,MAAM,iBAAiB,GAAiD,IAAI,GAAG,CAAC;IAC9E,WAAW;IACX,QAAQ;IACR,UAAU;IACV,SAAS;CACV,CAAC,CAAC;AAEH;;;;;;;;;;GAUG;AACH,MAAM,2BAA2B,GAAG,OAAO,CAAC;AAE5C;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,EAAE,CAAC;AAE7C,qFAAqF;AACrF,MAAM,yBAAyB,GAAG,KAAK,CAAC;AAExC,6EAA6E;AAC7E,MAAM,wBAAwB,GAAG,EAAE,GAAG,MAAM,CAAC;AAE7C,gFAAgF;AAChF,MAAM,yBAAyB,GAAG,CAAC,CAAC;AAwDpC;;;;;;;;;GASG;AACH,SAAS,KAAK,CAAC,EAAU,EAAE,MAAoB;IAC7C,IAAI,MAAM,EAAE,OAAO;QAAE,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC9C,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QACnC,MAAM,IAAI,GAAG,GAAG,EAAE;YAChB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YAC3C,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC;QACF,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACnC,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;AACL,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"useBuzzWorkflow.js","sourceRoot":"","sources":["../../src/hooks/useBuzzWorkflow.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCheckpointPicker.d.ts","sourceRoot":"","sources":["../../src/hooks/useCheckpointPicker.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAMnE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,mBAAmB,IAAI;IACrC,IAAI,EAAE,CAAC,IAAI,EAAE;QACX;;;;;WAKG;QACH,cAAc,EAAE,MAAM,CAAC;QACvB,uEAAuE;QACvE,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B,KAAK,OAAO,CAAC;QAAE,QAAQ,CAAC,EAAE,mBAAmB,CAAA;KAAE,CAAC,CAAC;IAClD,OAAO,EAAE,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACtD,
|
|
1
|
+
{"version":3,"file":"useCheckpointPicker.d.ts","sourceRoot":"","sources":["../../src/hooks/useCheckpointPicker.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAMnE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,mBAAmB,IAAI;IACrC,IAAI,EAAE,CAAC,IAAI,EAAE;QACX;;;;;WAKG;QACH,cAAc,EAAE,MAAM,CAAC;QACvB,uEAAuE;QACvE,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B,KAAK,OAAO,CAAC;QAAE,QAAQ,CAAC,EAAE,mBAAmB,CAAA;KAAE,CAAC,CAAC;IAClD,OAAO,EAAE,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACtD,CAuCA"}
|
|
@@ -38,8 +38,13 @@ export function useCheckpointPicker() {
|
|
|
38
38
|
}, []);
|
|
39
39
|
const persist = useCallback(async (versionId) => {
|
|
40
40
|
const { ok, error } = await sendTypedRequest(getTransport(), { type: 'SET_USER_CHECKPOINT', payload: { versionId } }, 'USER_CHECKPOINT_SET');
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
// A PRESENT `error` is the reject signal, not a TRUTHY one: the reply
|
|
42
|
+
// validator early-accepts anything carrying an `error` key, so `error: ''`
|
|
43
|
+
// reaches here having skipped the success-field checks. This site tested
|
|
44
|
+
// `!ok` ALONE before, so the `error` clause is new, not merely retyped.
|
|
45
|
+
// `||` (not `??`) so an empty error string still yields readable copy.
|
|
46
|
+
if (!ok || error !== undefined) {
|
|
47
|
+
throw new Error(error || 'failed to persist checkpoint');
|
|
43
48
|
}
|
|
44
49
|
}, []);
|
|
45
50
|
return { open, persist };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCheckpointPicker.js","sourceRoot":"","sources":["../../src/hooks/useCheckpointPicker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAIpC,OAAO,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAE5D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,mBAAmB;IAcjC,MAAM,IAAI,GAAG,WAAW,CACtB,KAAK,EAAE,IAA2D,EAAE,EAAE;QACpE,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,gBAAgB,CACzC,YAAY,EAAE,EACd;YACE,IAAI,EAAE,wBAAwB;YAC9B,OAAO,EAAE;gBACP,cAAc,EAAE,IAAI,CAAC,cAAc;gBACnC,GAAG,CAAC,IAAI,CAAC,gBAAgB,IAAI,IAAI;oBAC/B,CAAC,CAAC,EAAE,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,EAAE;oBAC7C,CAAC,CAAC,EAAE,CAAC;aACR;SACF,EACD,0BAA0B,EAC1B,EAAE,SAAS,EAAE,4BAA4B,EAAE,CAC5C,CAAC;QACF,OAAO,EAAE,QAAQ,EAAE,CAAC;IACtB,CAAC,EACD,EAAE,CACH,CAAC;IAEF,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,EAAE,SAAwB,EAAE,EAAE;QAC7D,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,MAAM,gBAAgB,CAC1C,YAAY,EAAE,EACd,EAAE,IAAI,EAAE,qBAAqB,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,EAAE,EACvD,qBAAqB,CACtB,CAAC;QACF,IAAI,CAAC,EAAE,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"useCheckpointPicker.js","sourceRoot":"","sources":["../../src/hooks/useCheckpointPicker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAIpC,OAAO,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAE5D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,mBAAmB;IAcjC,MAAM,IAAI,GAAG,WAAW,CACtB,KAAK,EAAE,IAA2D,EAAE,EAAE;QACpE,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,gBAAgB,CACzC,YAAY,EAAE,EACd;YACE,IAAI,EAAE,wBAAwB;YAC9B,OAAO,EAAE;gBACP,cAAc,EAAE,IAAI,CAAC,cAAc;gBACnC,GAAG,CAAC,IAAI,CAAC,gBAAgB,IAAI,IAAI;oBAC/B,CAAC,CAAC,EAAE,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,EAAE;oBAC7C,CAAC,CAAC,EAAE,CAAC;aACR;SACF,EACD,0BAA0B,EAC1B,EAAE,SAAS,EAAE,4BAA4B,EAAE,CAC5C,CAAC;QACF,OAAO,EAAE,QAAQ,EAAE,CAAC;IACtB,CAAC,EACD,EAAE,CACH,CAAC;IAEF,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,EAAE,SAAwB,EAAE,EAAE;QAC7D,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,MAAM,gBAAgB,CAC1C,YAAY,EAAE,EACd,EAAE,IAAI,EAAE,qBAAqB,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,EAAE,EACvD,qBAAqB,CACtB,CAAC;QACF,sEAAsE;QACtE,2EAA2E;QAC3E,yEAAyE;QACzE,wEAAwE;QACxE,uEAAuE;QACvE,IAAI,CAAC,EAAE,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YAC/B,MAAM,IAAI,KAAK,CAAC,KAAK,IAAI,8BAA8B,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AAC3B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSaveImage.d.ts","sourceRoot":"","sources":["../../src/hooks/useSaveImage.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,cAAc,GACtB;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GACnD;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAExD,MAAM,WAAW,YAAY;IAC3B;;;;;;;;;OASG;IACH,SAAS,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,IAAI,YAAY,
|
|
1
|
+
{"version":3,"file":"useSaveImage.d.ts","sourceRoot":"","sources":["../../src/hooks/useSaveImage.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,cAAc,GACtB;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GACnD;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAExD,MAAM,WAAW,YAAY;IAC3B;;;;;;;;;OASG;IACH,SAAS,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,IAAI,YAAY,CAqB3C"}
|
|
@@ -18,8 +18,12 @@ export function useSaveImage() {
|
|
|
18
18
|
? { url: input.url, filename: input.filename }
|
|
19
19
|
: { imageId: input.imageId, filename: input.filename };
|
|
20
20
|
const reply = await sendTypedRequest(getTransport(), { type: 'SAVE_IMAGE', payload }, 'SAVE_IMAGE_RESULT');
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
// A PRESENT `error` is the reject signal, not a TRUTHY one: the reply
|
|
22
|
+
// validator early-accepts anything carrying an `error` key, so `error: ''`
|
|
23
|
+
// reaches here having skipped the success-field checks. `||` (not `??`) so
|
|
24
|
+
// an empty error string still yields readable copy.
|
|
25
|
+
if (!reply.ok || reply.error !== undefined) {
|
|
26
|
+
throw new Error(reply.error || 'failed to save image');
|
|
23
27
|
}
|
|
24
28
|
}, []);
|
|
25
29
|
return { saveImage };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSaveImage.js","sourceRoot":"","sources":["../../src/hooks/useSaveImage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAEpC,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAgC5D;;;;;;;;;;GAUG;AACH,MAAM,UAAU,YAAY;IAC1B,MAAM,SAAS,GAAG,WAAW,CAAC,KAAK,EAAE,KAAqB,EAAiB,EAAE;QAC3E,MAAM,OAAO,GACX,KAAK,IAAI,KAAK,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS;YACvC,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE;YAC9C,CAAC,CAAC,EAAE,OAAO,EAAG,KAA6B,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC;QACpF,MAAM,KAAK,GAAG,MAAM,gBAAgB,CAClC,YAAY,EAAE,EACd,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,EAC/B,mBAAmB,CACpB,CAAC;QACF,IAAI,CAAC,KAAK,CAAC,EAAE,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"useSaveImage.js","sourceRoot":"","sources":["../../src/hooks/useSaveImage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAEpC,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAgC5D;;;;;;;;;;GAUG;AACH,MAAM,UAAU,YAAY;IAC1B,MAAM,SAAS,GAAG,WAAW,CAAC,KAAK,EAAE,KAAqB,EAAiB,EAAE;QAC3E,MAAM,OAAO,GACX,KAAK,IAAI,KAAK,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS;YACvC,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE;YAC9C,CAAC,CAAC,EAAE,OAAO,EAAG,KAA6B,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC;QACpF,MAAM,KAAK,GAAG,MAAM,gBAAgB,CAClC,YAAY,EAAE,EACd,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,EAC/B,mBAAmB,CACpB,CAAC;QACF,sEAAsE;QACtE,2EAA2E;QAC3E,2EAA2E;QAC3E,oDAAoD;QACpD,IAAI,CAAC,KAAK,CAAC,EAAE,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YAC3C,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,IAAI,sBAAsB,CAAC,CAAC;QACzD,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,EAAE,SAAS,EAAE,CAAC;AACvB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSharedStorage.d.ts","sourceRoot":"","sources":["../../src/hooks/useSharedStorage.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAyB,MAAM,yBAAyB,CAAC;AAKzF;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GAAG,kBAAkB,CAAC;AAEnD;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,iBAAiB,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB;;;;;;OAMG;IACH,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,sFAAsF;IACtF,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,IAAI,CAAC,IAAI,CAAC,EAAE;QACV,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC9B;;;;;;;OAOG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IACjD;;;;;;;OAOG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,8EAA8E;IAC9E,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACvC;;;OAGG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC3D;;;;;;OAMG;IACH,MAAM,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC3D;;;;;;;;;OASG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D;;;OAGG;IACH,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACnC;;;OAGG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACrC;;;OAGG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CACnE;AA8CD,wBAAgB,gBAAgB,IAAI,gBAAgB,
|
|
1
|
+
{"version":3,"file":"useSharedStorage.d.ts","sourceRoot":"","sources":["../../src/hooks/useSharedStorage.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAyB,MAAM,yBAAyB,CAAC;AAKzF;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GAAG,kBAAkB,CAAC;AAEnD;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,iBAAiB,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB;;;;;;OAMG;IACH,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,sFAAsF;IACtF,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,IAAI,CAAC,IAAI,CAAC,EAAE;QACV,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC9B;;;;;;;OAOG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IACjD;;;;;;;OAOG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,8EAA8E;IAC9E,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACvC;;;OAGG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC3D;;;;;;OAMG;IACH,MAAM,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC3D;;;;;;;;;OASG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D;;;OAGG;IACH,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACnC;;;OAGG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACrC;;;OAGG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CACnE;AA8CD,wBAAgB,gBAAgB,IAAI,gBAAgB,CA8HnD"}
|
|
@@ -72,8 +72,12 @@ export function useSharedStorage() {
|
|
|
72
72
|
},
|
|
73
73
|
async report(key, reason) {
|
|
74
74
|
const result = await sendTypedRequest(transport, { type: 'SHARED_REPORT', payload: { key, reason } }, 'SHARED_REPORT_RESULT');
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
// A PRESENT `error` is the reject signal, not a TRUTHY one: the reply
|
|
76
|
+
// validator early-accepts anything carrying an `error` key, so
|
|
77
|
+
// `error: ''` reaches here having skipped the success-field checks.
|
|
78
|
+
// `||` (not `??`) so an empty error string still yields readable copy.
|
|
79
|
+
if (!result.ok || result.error !== undefined) {
|
|
80
|
+
throw new Error(result.error || 'shared report failed');
|
|
77
81
|
}
|
|
78
82
|
},
|
|
79
83
|
async getCount(key) {
|
|
@@ -96,8 +100,9 @@ export function useSharedStorage() {
|
|
|
96
100
|
},
|
|
97
101
|
async update(key, value) {
|
|
98
102
|
const result = await sendTypedRequest(transport, { type: 'SHARED_UPDATE', payload: { key, value } }, 'SHARED_UPDATE_RESULT');
|
|
99
|
-
|
|
100
|
-
|
|
103
|
+
// PRESENT, not truthy — see `report()` above.
|
|
104
|
+
if (!result.ok || result.error !== undefined) {
|
|
105
|
+
throw new Error(result.error || 'shared update failed');
|
|
101
106
|
}
|
|
102
107
|
},
|
|
103
108
|
async vote(key) {
|
|
@@ -117,6 +122,17 @@ export function useSharedStorage() {
|
|
|
117
122
|
if (!result.ok || result.error) {
|
|
118
123
|
throw new Error(result.error ?? 'shared withdraw failed');
|
|
119
124
|
}
|
|
125
|
+
// `deleted` is optional on the wire type because an error reply omits
|
|
126
|
+
// it. On the success path the validator requires a boolean, so this is
|
|
127
|
+
// defence in depth rather than a reachable branch through the real
|
|
128
|
+
// transport — but it is what makes the `boolean` we return honest,
|
|
129
|
+
// instead of a cast asserting a guarantee this function cannot see.
|
|
130
|
+
// NOTE: this site still tests `error` for TRUTHINESS. It is not one of
|
|
131
|
+
// the six this change normalized (it early-accepted before PR #273);
|
|
132
|
+
// the remaining truthiness sites are a tracked follow-up.
|
|
133
|
+
if (typeof result.deleted !== 'boolean') {
|
|
134
|
+
throw new Error('shared withdraw failed: reply carried no `deleted` flag');
|
|
135
|
+
}
|
|
120
136
|
return { ok: true, deleted: result.deleted };
|
|
121
137
|
},
|
|
122
138
|
};
|