@civitai/blocks-react 0.67.1 → 0.69.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 +520 -8
- package/dist/hooks/useBatchEstimate.d.ts +191 -0
- package/dist/hooks/useBatchEstimate.js +196 -0
- package/dist/hooks/useBlockAnalytics.d.ts +46 -4
- package/dist/hooks/useBlockAnalytics.js +41 -4
- package/dist/hooks/useBuzzWorkflow.d.ts +4 -1
- package/dist/hooks/useBuzzWorkflow.js +4 -1
- package/dist/hooks/useResourcePicker.d.ts +98 -37
- package/dist/hooks/useResourcePicker.js +77 -7
- package/dist/hooks/useSharedStorage.d.ts +24 -0
- package/dist/hooks/useSharedStorage.js +55 -0
- package/dist/hooks/useWorkflowBatch.d.ts +196 -0
- package/dist/hooks/useWorkflowBatch.js +352 -0
- package/dist/index.d.ts +7 -3
- package/dist/index.js +4 -2
- package/dist/internal/liveHost.d.ts +27 -2
- package/dist/internal/liveHost.js +133 -15
- package/dist/internal/mockHost.d.ts +104 -2
- package/dist/internal/mockHost.js +369 -84
- package/dist/internal/pickerOverlay.d.ts +32 -0
- package/dist/internal/pickerOverlay.js +99 -2
- package/dist/internal/trackEventCheck.d.ts +34 -0
- package/dist/internal/trackEventCheck.js +194 -0
- package/dist/transport/requestTimeouts.js +3 -0
- package/dist/transport/validate.d.ts +49 -6
- package/dist/transport/validate.js +138 -28
- package/package.json +2 -2
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import { useBuzzWorkflow } from './useBuzzWorkflow.js';
|
|
3
|
+
/**
|
|
4
|
+
* The most workflows one {@link useWorkflowBatch} may watch.
|
|
5
|
+
*
|
|
6
|
+
* Derived from the host's poll allowance, not picked: one watched workflow makes
|
|
7
|
+
* at most 60 / 1.5 = 40 polls a minute (one poll in flight, `intervalMs` 1,500
|
|
8
|
+
* between polls), and the viewer's poll allowance is 1,200 per 60 s per block
|
|
9
|
+
* instance, so 1,200 / 40 = 30 workflows is the most one viewer can watch before
|
|
10
|
+
* polls start coming back as placeholders. The developer docs' limits table
|
|
11
|
+
* gives the same figure. A list longer than this throws, because an app that
|
|
12
|
+
* needs more cells needs a different plan than "watch them all at once".
|
|
13
|
+
*/
|
|
14
|
+
export const WORKFLOW_BATCH_MAX_CELLS = 30;
|
|
15
|
+
/**
|
|
16
|
+
* How many `cancel()` requests {@link UseWorkflowBatch.cancelAll} keeps in flight.
|
|
17
|
+
*
|
|
18
|
+
* Cancels charge the ESTIMATE allowance (150 per 10 s per block instance). On a
|
|
19
|
+
* page app every viewer of the app shares that allowance, along with the host's
|
|
20
|
+
* other reads for the app (viewer, balance, image and model lookups). Four at a
|
|
21
|
+
* time keeps a 16-cell cancel-all from landing as one burst on an allowance other
|
|
22
|
+
* people are using. Not exported: it is a pacing detail, not a contract.
|
|
23
|
+
*/
|
|
24
|
+
const CANCEL_CONCURRENCY = 4;
|
|
25
|
+
/** Statuses after which a workflow will not change again. */
|
|
26
|
+
const TERMINAL = new Set([
|
|
27
|
+
'succeeded',
|
|
28
|
+
'failed',
|
|
29
|
+
'canceled',
|
|
30
|
+
'expired',
|
|
31
|
+
]);
|
|
32
|
+
/**
|
|
33
|
+
* Placeholder ids a resolved `submit()` can carry when no workflow was created
|
|
34
|
+
* (see `useBuzzWorkflow().submit`). There is nothing behind them to watch.
|
|
35
|
+
*/
|
|
36
|
+
const NON_WORKFLOW_IDS = new Set(['failed', 'whatif']);
|
|
37
|
+
const FRESH = { snapshot: null, phase: 'watching', error: null, cancel: 'idle' };
|
|
38
|
+
function validateIds(ids) {
|
|
39
|
+
if (ids.length > WORKFLOW_BATCH_MAX_CELLS) {
|
|
40
|
+
throw new RangeError(`useWorkflowBatch: ${ids.length} workflow ids, but at most ${WORKFLOW_BATCH_MAX_CELLS} can be watched at once`);
|
|
41
|
+
}
|
|
42
|
+
const seen = new Set();
|
|
43
|
+
for (const id of ids) {
|
|
44
|
+
if (typeof id !== 'string' || id.length === 0) {
|
|
45
|
+
throw new TypeError('useWorkflowBatch: every workflow id must be a non-empty string');
|
|
46
|
+
}
|
|
47
|
+
if (NON_WORKFLOW_IDS.has(id)) {
|
|
48
|
+
throw new TypeError(`useWorkflowBatch: '${id}' is not a workflow id. A submit that resolved with it created nothing to watch`);
|
|
49
|
+
}
|
|
50
|
+
if (seen.has(id)) {
|
|
51
|
+
throw new TypeError(`useWorkflowBatch: workflow id '${id}' appears more than once`);
|
|
52
|
+
}
|
|
53
|
+
seen.add(id);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function summarise(cells) {
|
|
57
|
+
const summary = {
|
|
58
|
+
total: cells.length,
|
|
59
|
+
running: 0,
|
|
60
|
+
succeeded: 0,
|
|
61
|
+
failed: 0,
|
|
62
|
+
canceled: 0,
|
|
63
|
+
expired: 0,
|
|
64
|
+
stopped: 0,
|
|
65
|
+
realisedCost: 0,
|
|
66
|
+
costedCells: 0,
|
|
67
|
+
settled: true,
|
|
68
|
+
};
|
|
69
|
+
for (const cell of cells) {
|
|
70
|
+
const status = cell.snapshot?.status;
|
|
71
|
+
if (status === undefined || !TERMINAL.has(status)) {
|
|
72
|
+
summary.running += 1;
|
|
73
|
+
if (cell.phase === 'stopped')
|
|
74
|
+
summary.stopped += 1;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
summary[status] += 1;
|
|
78
|
+
const total = cell.snapshot?.cost?.total;
|
|
79
|
+
if (typeof total === 'number' && Number.isFinite(total)) {
|
|
80
|
+
summary.realisedCost += total;
|
|
81
|
+
summary.costedCells += 1;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
summary.settled = summary.running === 0;
|
|
85
|
+
return summary;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Watch a set of already-submitted workflows as one batch, with per-cell status,
|
|
89
|
+
* a summary, and cancel-all. For grid apps that submit N workflows one by one
|
|
90
|
+
* with `useBuzzWorkflow().submit()` (each with its own confirmation) and then
|
|
91
|
+
* have to follow all N.
|
|
92
|
+
*
|
|
93
|
+
* 🔴 THE APP OWNS THE LIST, AND THE LIST IS THE BATCH. Pass the workflow ids as
|
|
94
|
+
* an array: append each id as its `submit()` resolves, and persist the same
|
|
95
|
+
* array (it is plain strings) to re-attach after a reload by passing it back.
|
|
96
|
+
* There is no server-side batch id. The host knows nothing about the grouping,
|
|
97
|
+
* so nothing here changes what the host allows: every cell is one ordinary
|
|
98
|
+
* `watch()` and every cancel is one ordinary `cancel()`.
|
|
99
|
+
*
|
|
100
|
+
* What it guarantees, and the host limits behind each:
|
|
101
|
+
*
|
|
102
|
+
* - **One poll in flight per workflow.** Each cell is one `watch()` loop. When a
|
|
103
|
+
* loop is restarted (the list changes, `rewatch()`, a React StrictMode
|
|
104
|
+
* remount), the new loop waits for the old loop's in-flight poll to return
|
|
105
|
+
* first, so two polls for one workflow never overlap.
|
|
106
|
+
* - **At most {@link WORKFLOW_BATCH_MAX_CELLS} workflows.** That is the poll
|
|
107
|
+
* allowance (1,200 per 60 s, per block instance and viewer) divided by one
|
|
108
|
+
* watch's 40 polls a minute. A longer list throws.
|
|
109
|
+
* - **Cancel-all is paced and stops on refusal.** Cancels charge the estimate
|
|
110
|
+
* allowance (150 per 10 s per block instance, shared by every viewer of a page
|
|
111
|
+
* app). At most four are in flight. When the host answers one with a
|
|
112
|
+
* non-terminal snapshot (its answer when that allowance is used up), the rest
|
|
113
|
+
* of the call is not sent and comes back as `notAttempted`. Nothing is retried
|
|
114
|
+
* automatically. Call `cancelAll()` again after a few seconds.
|
|
115
|
+
* - **A host failure placeholder is not a workflow outcome.** When the host
|
|
116
|
+
* fails a poll or a cancel it answers with `{ workflowId: 'failed', status:
|
|
117
|
+
* 'failed' }`. `watch()` and `cancel()` resolve with that, but it says nothing
|
|
118
|
+
* about the workflow, which may still be running. This hook never counts it as
|
|
119
|
+
* a failed cell: a poll placeholder stops the cell (phase `'stopped'`, with
|
|
120
|
+
* `error` set), and a cancel placeholder is a `'failed'` cancel.
|
|
121
|
+
* - **Unmounting stops every poll.** No state updates happen after unmount. A
|
|
122
|
+
* `cancelAll()` already running keeps sending, because the viewer asked for it.
|
|
123
|
+
*
|
|
124
|
+
* Throws (at render) when the list is longer than
|
|
125
|
+
* {@link WORKFLOW_BATCH_MAX_CELLS}, has a duplicate, has an empty id, or has the
|
|
126
|
+
* `'failed'` / `'whatif'` placeholder a refused `submit()` resolves with.
|
|
127
|
+
*
|
|
128
|
+
* Needs the same `ai:write:budgeted` scope as `submit()`. A viewer who submitted
|
|
129
|
+
* already has it.
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* const { submit } = useBuzzWorkflow();
|
|
133
|
+
* const [ids, setIds] = useState<string[]>(() => loadSavedIds());
|
|
134
|
+
* const batch = useWorkflowBatch(ids);
|
|
135
|
+
*
|
|
136
|
+
* async function runCell(body: WorkflowBody, key: string) {
|
|
137
|
+
* const snap = await submit(body, { idempotencyKey: key });
|
|
138
|
+
* if (snap.workflowId !== 'failed') setIds((prev) => [...prev, snap.workflowId]);
|
|
139
|
+
* }
|
|
140
|
+
*
|
|
141
|
+
* useEffect(() => saveIds(ids), [ids]);
|
|
142
|
+
*
|
|
143
|
+
* // batch.summary.running, batch.summary.realisedCost, batch.cells[i].snapshot …
|
|
144
|
+
* const result = await batch.cancelAll();
|
|
145
|
+
* if (!result.complete) showRetryCancel(result);
|
|
146
|
+
*/
|
|
147
|
+
export function useWorkflowBatch(workflowIds, options) {
|
|
148
|
+
validateIds(workflowIds);
|
|
149
|
+
// A private `useBuzzWorkflow()` instance, so the app's own instance's
|
|
150
|
+
// `result`/`status` are not overwritten by 16 cells' updates.
|
|
151
|
+
const { watch, cancel } = useBuzzWorkflow();
|
|
152
|
+
const [states, setStates] = useState(() => new Map());
|
|
153
|
+
// Mirror of `states` for async code, which must not read a stale closure.
|
|
154
|
+
const statesRef = useRef(states);
|
|
155
|
+
statesRef.current = states;
|
|
156
|
+
const idsKey = workflowIds.join('\u0000');
|
|
157
|
+
const idsRef = useRef(workflowIds);
|
|
158
|
+
idsRef.current = workflowIds;
|
|
159
|
+
const optionsRef = useRef(options);
|
|
160
|
+
optionsRef.current = options;
|
|
161
|
+
const mountedRef = useRef(false);
|
|
162
|
+
/** Per workflow: the running loop's controller and a promise that settles when it ends. */
|
|
163
|
+
const loopsRef = useRef(new Map());
|
|
164
|
+
const cancelAllRef = useRef(null);
|
|
165
|
+
const patch = useCallback((workflowId, change) => {
|
|
166
|
+
if (!mountedRef.current)
|
|
167
|
+
return;
|
|
168
|
+
setStates((prev) => {
|
|
169
|
+
const next = new Map(prev);
|
|
170
|
+
next.set(workflowId, { ...(prev.get(workflowId) ?? FRESH), ...change });
|
|
171
|
+
return next;
|
|
172
|
+
});
|
|
173
|
+
}, []);
|
|
174
|
+
const startLoop = useCallback((workflowId) => {
|
|
175
|
+
const loops = loopsRef.current;
|
|
176
|
+
const previous = loops.get(workflowId);
|
|
177
|
+
previous?.controller.abort();
|
|
178
|
+
const controller = new AbortController();
|
|
179
|
+
// 🔴 CHAIN ON THE PREVIOUS LOOP. Aborting a `watch()` does not withdraw a
|
|
180
|
+
// poll already in flight (the host can hold it up to 15 s); the loop only
|
|
181
|
+
// sees the abort when that poll returns. Starting the new loop at once would
|
|
182
|
+
// put two polls for one workflow in flight.
|
|
183
|
+
const done = (previous?.done ?? Promise.resolve()).then(async () => {
|
|
184
|
+
if (controller.signal.aborted)
|
|
185
|
+
return;
|
|
186
|
+
try {
|
|
187
|
+
const final = await watch(workflowId, {
|
|
188
|
+
...optionsRef.current,
|
|
189
|
+
signal: controller.signal,
|
|
190
|
+
onUpdate: (snapshot) => {
|
|
191
|
+
if (controller.signal.aborted || snapshot.workflowId !== workflowId)
|
|
192
|
+
return;
|
|
193
|
+
patch(workflowId, { snapshot });
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
if (controller.signal.aborted)
|
|
197
|
+
return;
|
|
198
|
+
if (final.workflowId !== workflowId) {
|
|
199
|
+
patch(workflowId, {
|
|
200
|
+
phase: 'stopped',
|
|
201
|
+
error: new Error(`useWorkflowBatch: the host answered a poll for ${workflowId} with a failure placeholder, not the workflow's status`),
|
|
202
|
+
});
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
patch(workflowId, {
|
|
206
|
+
snapshot: final,
|
|
207
|
+
phase: TERMINAL.has(final.status) ? 'settled' : 'stopped',
|
|
208
|
+
error: null,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
catch (err) {
|
|
212
|
+
if (controller.signal.aborted)
|
|
213
|
+
return;
|
|
214
|
+
patch(workflowId, { phase: 'stopped', error: err instanceof Error ? err : new Error(String(err)) });
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
loops.set(workflowId, { controller, done });
|
|
218
|
+
}, [watch, patch]);
|
|
219
|
+
useEffect(() => {
|
|
220
|
+
mountedRef.current = true;
|
|
221
|
+
const loops = loopsRef.current;
|
|
222
|
+
return () => {
|
|
223
|
+
mountedRef.current = false;
|
|
224
|
+
for (const loop of loops.values())
|
|
225
|
+
loop.controller.abort();
|
|
226
|
+
};
|
|
227
|
+
}, []);
|
|
228
|
+
// Diff the list against the running loops: start new ids, stop removed ones.
|
|
229
|
+
useEffect(() => {
|
|
230
|
+
const loops = loopsRef.current;
|
|
231
|
+
const wanted = new Set(idsRef.current);
|
|
232
|
+
for (const [id, loop] of loops) {
|
|
233
|
+
// Abort, but KEEP the entry: its `done` is what a re-added id's new loop
|
|
234
|
+
// must wait on, because the aborted loop's poll may still be in flight.
|
|
235
|
+
if (!wanted.has(id))
|
|
236
|
+
loop.controller.abort();
|
|
237
|
+
}
|
|
238
|
+
for (const id of idsRef.current) {
|
|
239
|
+
const loop = loops.get(id);
|
|
240
|
+
if (loop && !loop.controller.signal.aborted)
|
|
241
|
+
continue;
|
|
242
|
+
const state = statesRef.current.get(id);
|
|
243
|
+
// A settled or stopped cell stays as it is until `rewatch()`.
|
|
244
|
+
if (state && state.phase !== 'watching')
|
|
245
|
+
continue;
|
|
246
|
+
startLoop(id);
|
|
247
|
+
}
|
|
248
|
+
setStates((prev) => {
|
|
249
|
+
let changed = false;
|
|
250
|
+
const next = new Map(prev);
|
|
251
|
+
for (const id of prev.keys()) {
|
|
252
|
+
if (!wanted.has(id)) {
|
|
253
|
+
next.delete(id);
|
|
254
|
+
changed = true;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return changed ? next : prev;
|
|
258
|
+
});
|
|
259
|
+
// `idsKey` stands for the list's contents; the list itself is read from the ref.
|
|
260
|
+
}, [idsKey, startLoop]);
|
|
261
|
+
const rewatch = useCallback(() => {
|
|
262
|
+
for (const id of idsRef.current) {
|
|
263
|
+
if (statesRef.current.get(id)?.phase !== 'stopped')
|
|
264
|
+
continue;
|
|
265
|
+
patch(id, { phase: 'watching', error: null });
|
|
266
|
+
startLoop(id);
|
|
267
|
+
}
|
|
268
|
+
}, [patch, startLoop]);
|
|
269
|
+
const cancelAll = useCallback(() => {
|
|
270
|
+
if (cancelAllRef.current)
|
|
271
|
+
return cancelAllRef.current;
|
|
272
|
+
const run = async () => {
|
|
273
|
+
const ids = [...idsRef.current];
|
|
274
|
+
const outcome = new Map();
|
|
275
|
+
const targets = [];
|
|
276
|
+
for (const id of ids) {
|
|
277
|
+
const status = statesRef.current.get(id)?.snapshot?.status;
|
|
278
|
+
if (status !== undefined && TERMINAL.has(status))
|
|
279
|
+
outcome.set(id, 'alreadySettled');
|
|
280
|
+
else
|
|
281
|
+
targets.push(id);
|
|
282
|
+
}
|
|
283
|
+
let refusedSeen = false;
|
|
284
|
+
let next = 0;
|
|
285
|
+
const worker = async () => {
|
|
286
|
+
for (let id = targets[next]; id !== undefined; id = targets[next]) {
|
|
287
|
+
next += 1;
|
|
288
|
+
if (refusedSeen) {
|
|
289
|
+
outcome.set(id, 'notAttempted');
|
|
290
|
+
patch(id, { cancel: 'refused' });
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
patch(id, { cancel: 'pending' });
|
|
294
|
+
let snapshot;
|
|
295
|
+
try {
|
|
296
|
+
snapshot = await cancel(id);
|
|
297
|
+
}
|
|
298
|
+
catch {
|
|
299
|
+
outcome.set(id, 'failed');
|
|
300
|
+
patch(id, { cancel: 'failed' });
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
if (snapshot.workflowId !== id) {
|
|
304
|
+
// The host's failure placeholder: the request failed, the workflow's
|
|
305
|
+
// state is unknown.
|
|
306
|
+
outcome.set(id, 'failed');
|
|
307
|
+
patch(id, { cancel: 'failed' });
|
|
308
|
+
}
|
|
309
|
+
else if (TERMINAL.has(snapshot.status)) {
|
|
310
|
+
outcome.set(id, snapshot.status === 'canceled' ? 'canceled' : 'alreadySettled');
|
|
311
|
+
loopsRef.current.get(id)?.controller.abort();
|
|
312
|
+
patch(id, { cancel: 'done', snapshot, phase: 'settled', error: null });
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
refusedSeen = true;
|
|
316
|
+
outcome.set(id, 'refused');
|
|
317
|
+
patch(id, { cancel: 'refused' });
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
await Promise.all(Array.from({ length: Math.min(CANCEL_CONCURRENCY, targets.length) }, () => worker()));
|
|
322
|
+
const result = {
|
|
323
|
+
canceled: [],
|
|
324
|
+
alreadySettled: [],
|
|
325
|
+
refused: [],
|
|
326
|
+
notAttempted: [],
|
|
327
|
+
failed: [],
|
|
328
|
+
complete: true,
|
|
329
|
+
};
|
|
330
|
+
for (const id of ids) {
|
|
331
|
+
const bucket = outcome.get(id);
|
|
332
|
+
if (bucket)
|
|
333
|
+
result[bucket].push(id);
|
|
334
|
+
}
|
|
335
|
+
result.complete =
|
|
336
|
+
result.refused.length === 0 && result.notAttempted.length === 0 && result.failed.length === 0;
|
|
337
|
+
return result;
|
|
338
|
+
};
|
|
339
|
+
const promise = run().finally(() => {
|
|
340
|
+
cancelAllRef.current = null;
|
|
341
|
+
});
|
|
342
|
+
cancelAllRef.current = promise;
|
|
343
|
+
return promise;
|
|
344
|
+
}, [cancel, patch]);
|
|
345
|
+
const cells = useMemo(() => workflowIds.map((workflowId) => ({ workflowId, ...(states.get(workflowId) ?? FRESH) })),
|
|
346
|
+
// `idsKey` stands for the list's contents, so a new array with the same ids
|
|
347
|
+
// does not rebuild the cells.
|
|
348
|
+
[idsKey, states]);
|
|
349
|
+
const summary = useMemo(() => summarise(cells), [cells]);
|
|
350
|
+
return { cells, summary, cancelAll, rewatch };
|
|
351
|
+
}
|
|
352
|
+
//# sourceMappingURL=useWorkflowBatch.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -27,6 +27,10 @@ export { useHostOrigin } from './hooks/useHostOrigin.js';
|
|
|
27
27
|
export type { UseHostOrigin } from './hooks/useHostOrigin.js';
|
|
28
28
|
export { DEFAULT_WATCH_WAIT_SECONDS, useBuzzWorkflow, WorkflowEstimateError, WorkflowSubmitError, } from './hooks/useBuzzWorkflow.js';
|
|
29
29
|
export type { SubmitWorkflowOptions, UseBuzzWorkflow, WatchWorkflowOptions, WorkflowSubmitErrorCode, } from './hooks/useBuzzWorkflow.js';
|
|
30
|
+
export { BATCH_ESTIMATE_MAX_CELLS, BatchEstimateError, useBatchEstimate, } from './hooks/useBatchEstimate.js';
|
|
31
|
+
export type { BatchEstimateCell, BatchEstimateErrorCode, BatchEstimateResult, BlockEstimateBatchAggregate, EstimateBatchOptions, UseBatchEstimate, } from './hooks/useBatchEstimate.js';
|
|
32
|
+
export { WORKFLOW_BATCH_MAX_CELLS, useWorkflowBatch } from './hooks/useWorkflowBatch.js';
|
|
33
|
+
export type { UseWorkflowBatch, WorkflowBatchCancelResult, WorkflowBatchCancelState, WorkflowBatchCell, WorkflowBatchCellPhase, WorkflowBatchOptions, WorkflowBatchSummary, } from './hooks/useWorkflowBatch.js';
|
|
30
34
|
export { useEntitlements } from './hooks/useEntitlements.js';
|
|
31
35
|
export type { UseEntitlements, Entitlement } from './hooks/useEntitlements.js';
|
|
32
36
|
export { useGoodPurchase, GoodPurchaseRefusal } from './hooks/useGoodPurchase.js';
|
|
@@ -74,8 +78,8 @@ export type { UseRunTraining, QuotedTrainingBody, RunTrainingErrorCode, BlockRun
|
|
|
74
78
|
export type { BlockBuzzTransaction, BlockBuzzAccount, BlockDailyCompensationResource, BlockWildcardPack, BlockWildcardPackErrorCode, AppWorkflow, AppWorkflowImage, AppWorkflowsParams, BlockGatedImage, BlockCollectionFollowErrorCode, BlockCollectionFollowResult, } from '@civitai/app-sdk/blocks';
|
|
75
79
|
export { useCheckpointPicker } from './hooks/useCheckpointPicker.js';
|
|
76
80
|
export type { UseCheckpointPicker } from './hooks/useCheckpointPicker.js';
|
|
77
|
-
export { useResourcePicker } from './hooks/useResourcePicker.js';
|
|
78
|
-
export type { UseResourcePicker } from './hooks/useResourcePicker.js';
|
|
81
|
+
export { useResourcePicker, RESOURCE_PICKER_MULTIPLE_MAX } from './hooks/useResourcePicker.js';
|
|
82
|
+
export type { UseResourcePicker, ResourcePickerOpenOptions, ResourcePickerMultiple, } from './hooks/useResourcePicker.js';
|
|
79
83
|
export { useImageUpload } from './hooks/useImageUpload.js';
|
|
80
84
|
export type { UseImageUpload, UseImageUploadAsyncScan, UseImageUploadGenerationSource, UseImageUploadOptions, } from './hooks/useImageUpload.js';
|
|
81
85
|
export { useUploadImageBytes } from './hooks/useUploadImageBytes.js';
|
|
@@ -108,6 +112,6 @@ export { useNestedDocument } from './hooks/useNestedDocument.js';
|
|
|
108
112
|
export type { NestedDocumentStatus, UseNestedDocument, UseNestedDocumentOptions, } from './hooks/useNestedDocument.js';
|
|
109
113
|
export { useAppStorage } from './hooks/useAppStorage.js';
|
|
110
114
|
export type { AppStorageKeyEntry, AppStorageListResult, AppStorageQuota, UseAppStorage, } from './hooks/useAppStorage.js';
|
|
111
|
-
export { useSharedStorage } from './hooks/useSharedStorage.js';
|
|
115
|
+
export { useSharedStorage, SharedStorageRateLimitError } from './hooks/useSharedStorage.js';
|
|
112
116
|
export type { SharedAppendValue, SharedListItem, SharedListResult, UseSharedStorage, } from './hooks/useSharedStorage.js';
|
|
113
117
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -31,6 +31,8 @@ export { useBlockSettings } from './hooks/useBlockSettings.js';
|
|
|
31
31
|
export { useBlockToken } from './hooks/useBlockToken.js';
|
|
32
32
|
export { useHostOrigin } from './hooks/useHostOrigin.js';
|
|
33
33
|
export { DEFAULT_WATCH_WAIT_SECONDS, useBuzzWorkflow, WorkflowEstimateError, WorkflowSubmitError, } from './hooks/useBuzzWorkflow.js';
|
|
34
|
+
export { BATCH_ESTIMATE_MAX_CELLS, BatchEstimateError, useBatchEstimate, } from './hooks/useBatchEstimate.js';
|
|
35
|
+
export { WORKFLOW_BATCH_MAX_CELLS, useWorkflowBatch } from './hooks/useWorkflowBatch.js';
|
|
34
36
|
export { useEntitlements } from './hooks/useEntitlements.js';
|
|
35
37
|
export { useGoodPurchase, GoodPurchaseRefusal } from './hooks/useGoodPurchase.js';
|
|
36
38
|
export { useTip } from './hooks/useTip.js';
|
|
@@ -62,7 +64,7 @@ export { useCreatePostFromApp, CreatePostError, CREATE_POST_ERROR_CODES, isCreat
|
|
|
62
64
|
export { usePrepareTrainingDataset, PrepareTrainingDatasetError, PREPARE_TRAINING_DATASET_ERROR_CODES, isPrepareTrainingDatasetErrorCode, } from './hooks/usePrepareTrainingDataset.js';
|
|
63
65
|
export { useRunTraining, RunTrainingError, RUN_TRAINING_ERROR_CODES, isRunTrainingErrorCode, } from './hooks/useRunTraining.js';
|
|
64
66
|
export { useCheckpointPicker } from './hooks/useCheckpointPicker.js';
|
|
65
|
-
export { useResourcePicker } from './hooks/useResourcePicker.js';
|
|
67
|
+
export { useResourcePicker, RESOURCE_PICKER_MULTIPLE_MAX } from './hooks/useResourcePicker.js';
|
|
66
68
|
export { useImageUpload } from './hooks/useImageUpload.js';
|
|
67
69
|
export { useUploadImageBytes } from './hooks/useUploadImageBytes.js';
|
|
68
70
|
export { useGenerationResources } from './hooks/useGenerationResources.js';
|
|
@@ -84,5 +86,5 @@ export { hostToRunUrl, DIRECT_LOAD_TIMEOUT_MS } from './transport/directLoad.js'
|
|
|
84
86
|
// `@civitai/app-sdk`'s `src/blocks/nestedDocument.ts` header.
|
|
85
87
|
export { useNestedDocument } from './hooks/useNestedDocument.js';
|
|
86
88
|
export { useAppStorage } from './hooks/useAppStorage.js';
|
|
87
|
-
export { useSharedStorage } from './hooks/useSharedStorage.js';
|
|
89
|
+
export { useSharedStorage, SharedStorageRateLimitError } from './hooks/useSharedStorage.js';
|
|
88
90
|
//# sourceMappingURL=index.js.map
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
* --- BEGIN DERIVED ---
|
|
47
47
|
* fetch chokepoints ..... 2
|
|
48
48
|
* REST endpoints ........ GET /api/v1/blocks/me
|
|
49
|
-
* tRPC procedures .......
|
|
50
|
-
* switch case labels ....
|
|
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)
|
|
51
51
|
* --- END DERIVED ---
|
|
52
52
|
*
|
|
53
53
|
* PICKERS (Phase 1 of "make dev:live a faithful local host"): the live host
|
|
@@ -83,6 +83,12 @@
|
|
|
83
83
|
* the switch's `default: return` with no reply at all, so `useSharedStorage()`
|
|
84
84
|
* hung to the 30s protocol timeout in dev:live for code that worked in dev:mock.
|
|
85
85
|
*
|
|
86
|
+
* TRACK_EVENT: recorded only in production, so the live host forwards nothing
|
|
87
|
+
* and replies nothing (production replies nothing either). What it does is
|
|
88
|
+
* CHECK each event against the manifest passed as `options.manifest` and warn
|
|
89
|
+
* once per distinct problem production would silently drop — see
|
|
90
|
+
* `trackEventCheck.ts`.
|
|
91
|
+
*
|
|
86
92
|
* 🔴 SCOPE — the capabilities live mode still cannot SERVE. Each REFUSES on its
|
|
87
93
|
* own reply channel — `logOnce` (or `console.info`) plus an honest error, never
|
|
88
94
|
* a fabricated success and never silence. Two guards stand behind this list:
|
|
@@ -186,6 +192,25 @@ export interface LiveHostOptions {
|
|
|
186
192
|
* `dev:live` run this is omitted — the dev clicks a card in the overlay.
|
|
187
193
|
*/
|
|
188
194
|
onPickerReady?: (handle: PickerOverlayHandle) => void;
|
|
195
|
+
/**
|
|
196
|
+
* The app's manifest — pass `block.manifest.json` as the harness imported it.
|
|
197
|
+
* Used for one thing: each `TRACK_EVENT` is checked against its
|
|
198
|
+
* `analytics.events` declaration, and the console warns ONCE per distinct
|
|
199
|
+
* problem production would silently drop — an undeclared event, an
|
|
200
|
+
* undeclared property, a value that does not match the declared type or enum.
|
|
201
|
+
* A declared, valid event logs nothing. The check never throws, never blocks
|
|
202
|
+
* the block, and sends no reply (production sends none either). Omit it and
|
|
203
|
+
* the first `TRACK_EVENT` says the check is off.
|
|
204
|
+
*
|
|
205
|
+
* Typed `unknown` because an imported JSON file widens its string literals
|
|
206
|
+
* (`"type": "enum"` reads as `string`), which a stricter type would reject.
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* // `manifest` = your block.manifest.json, imported as JSON — every
|
|
210
|
+
* // `starters/examples/*` harness does this.
|
|
211
|
+
* createLiveHost({ blockToken, manifest });
|
|
212
|
+
*/
|
|
213
|
+
manifest?: unknown;
|
|
189
214
|
}
|
|
190
215
|
/**
|
|
191
216
|
* The reply both training bridges get in dev:live — see their handlers. One
|