@civitai/blocks-react 0.4.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.
Files changed (82) hide show
  1. package/README.md +48 -0
  2. package/dist/hooks/useAppStorage.d.ts +75 -0
  3. package/dist/hooks/useAppStorage.d.ts.map +1 -0
  4. package/dist/hooks/useAppStorage.js +73 -0
  5. package/dist/hooks/useAppStorage.js.map +1 -0
  6. package/dist/hooks/useBlockAnalytics.d.ts +9 -0
  7. package/dist/hooks/useBlockAnalytics.d.ts.map +1 -0
  8. package/dist/hooks/useBlockAnalytics.js +14 -0
  9. package/dist/hooks/useBlockAnalytics.js.map +1 -0
  10. package/dist/hooks/useBlockContext.d.ts +18 -0
  11. package/dist/hooks/useBlockContext.d.ts.map +1 -0
  12. package/dist/hooks/useBlockContext.js +39 -0
  13. package/dist/hooks/useBlockContext.js.map +1 -0
  14. package/dist/hooks/useBlockResize.d.ts +13 -0
  15. package/dist/hooks/useBlockResize.d.ts.map +1 -0
  16. package/dist/hooks/useBlockResize.js +33 -0
  17. package/dist/hooks/useBlockResize.js.map +1 -0
  18. package/dist/hooks/useBlockSettings.d.ts +7 -0
  19. package/dist/hooks/useBlockSettings.d.ts.map +1 -0
  20. package/dist/hooks/useBlockSettings.js +9 -0
  21. package/dist/hooks/useBlockSettings.js.map +1 -0
  22. package/dist/hooks/useBlockToken.d.ts +16 -0
  23. package/dist/hooks/useBlockToken.d.ts.map +1 -0
  24. package/dist/hooks/useBlockToken.js +61 -0
  25. package/dist/hooks/useBlockToken.js.map +1 -0
  26. package/dist/hooks/useBuzzPurchase.d.ts +12 -0
  27. package/dist/hooks/useBuzzPurchase.d.ts.map +1 -0
  28. package/dist/hooks/useBuzzPurchase.js +16 -0
  29. package/dist/hooks/useBuzzPurchase.js.map +1 -0
  30. package/dist/hooks/useBuzzWorkflow.d.ts +21 -0
  31. package/dist/hooks/useBuzzWorkflow.d.ts.map +1 -0
  32. package/dist/hooks/useBuzzWorkflow.js +76 -0
  33. package/dist/hooks/useBuzzWorkflow.js.map +1 -0
  34. package/dist/hooks/useCheckpointPicker.d.ts +34 -0
  35. package/dist/hooks/useCheckpointPicker.d.ts.map +1 -0
  36. package/dist/hooks/useCheckpointPicker.js +41 -0
  37. package/dist/hooks/useCheckpointPicker.js.map +1 -0
  38. package/dist/hooks/useCivitaiNavigate.d.ts +11 -0
  39. package/dist/hooks/useCivitaiNavigate.d.ts.map +1 -0
  40. package/dist/hooks/useCivitaiNavigate.js +16 -0
  41. package/dist/hooks/useCivitaiNavigate.js.map +1 -0
  42. package/dist/index.d.ts +27 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +24 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/internal/detector.d.ts +31 -0
  47. package/dist/internal/detector.d.ts.map +1 -0
  48. package/dist/internal/detector.js +67 -0
  49. package/dist/internal/detector.js.map +1 -0
  50. package/dist/internal/iframeTransport.d.ts +55 -0
  51. package/dist/internal/iframeTransport.d.ts.map +1 -0
  52. package/dist/internal/iframeTransport.js +200 -0
  53. package/dist/internal/iframeTransport.js.map +1 -0
  54. package/dist/internal/inlineTransport.d.ts +18 -0
  55. package/dist/internal/inlineTransport.d.ts.map +1 -0
  56. package/dist/internal/inlineTransport.js +36 -0
  57. package/dist/internal/inlineTransport.js.map +1 -0
  58. package/dist/internal/singleton.d.ts +18 -0
  59. package/dist/internal/singleton.d.ts.map +1 -0
  60. package/dist/internal/singleton.js +28 -0
  61. package/dist/internal/singleton.js.map +1 -0
  62. package/dist/internal/transport.d.ts +100 -0
  63. package/dist/internal/transport.d.ts.map +1 -0
  64. package/dist/internal/transport.js +67 -0
  65. package/dist/internal/transport.js.map +1 -0
  66. package/dist/internal/validate.d.ts +56 -0
  67. package/dist/internal/validate.d.ts.map +1 -0
  68. package/dist/internal/validate.js +202 -0
  69. package/dist/internal/validate.js.map +1 -0
  70. package/dist/testing.d.ts +14 -0
  71. package/dist/testing.d.ts.map +1 -0
  72. package/dist/testing.js +16 -0
  73. package/dist/testing.js.map +1 -0
  74. package/dist/ui/SettingsForm.d.ts +79 -0
  75. package/dist/ui/SettingsForm.d.ts.map +1 -0
  76. package/dist/ui/SettingsForm.js +199 -0
  77. package/dist/ui/SettingsForm.js.map +1 -0
  78. package/dist/ui/index.d.ts +11 -0
  79. package/dist/ui/index.d.ts.map +1 -0
  80. package/dist/ui/index.js +10 -0
  81. package/dist/ui/index.js.map +1 -0
  82. package/package.json +69 -0
package/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # `@civitai/blocks-react`
2
+
3
+ React hooks and iframe transport for [Civitai App Blocks](https://developer.civitai.com/docs/blocks).
4
+
5
+ Pairs with [`@civitai/app-sdk`](https://www.npmjs.com/package/@civitai/app-sdk)'s
6
+ `/blocks` subpath, which carries the framework-agnostic manifest, scope, and
7
+ `postMessage` contract. This package adds the transport that actually moves
8
+ bytes and the React hooks block authors call.
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ pnpm add @civitai/blocks-react @civitai/app-sdk react
14
+ ```
15
+
16
+ `react` and `@civitai/app-sdk` are peer dependencies — bring them yourself so
17
+ your block app and the SDK share a single React tree.
18
+
19
+ ## Quick start
20
+
21
+ ```tsx
22
+ import { useBlockContext, useBuzzWorkflow } from '@civitai/blocks-react';
23
+
24
+ export function App() {
25
+ const { ready, context, viewer } = useBlockContext();
26
+ const { estimate, submit, status, result } = useBuzzWorkflow();
27
+
28
+ if (!ready) return <div>Loading…</div>;
29
+ return (
30
+ <div>
31
+ <p>Block for model {context.modelId} ({viewer.username})</p>
32
+ <button onClick={() => submit({ prompt: 'a cat' })}>Generate</button>
33
+ {status === 'done' && result?.imageUrls?.map((u) => <img key={u} src={u} />)}
34
+ </div>
35
+ );
36
+ }
37
+ ```
38
+
39
+ ## What lives where
40
+
41
+ - [`@civitai/app-sdk/blocks`](https://github.com/civitai/civitai-app-starters/tree/main/packages/civitai-app-sdk/src/blocks): manifest types, `defineBlock`, `BLOCK_SCOPES`, `postMessage` protocol, JSON schema.
42
+ - This package: `IframeTransport`, transport detector, and the eight React hooks (`useBlockContext`, `useBlockToken`, `useBlockSettings`, `useBuzzWorkflow`, `useBlockResize`, `useBuzzPurchase`, `useCivitaiNavigate`, `useBlockAnalytics`).
43
+
44
+ Architecture and contribution notes live in the [in-repo `AGENTS.md`](https://github.com/civitai/civitai-app-starters/blob/main/packages/civitai-blocks-react/AGENTS.md) (not shipped in the published tarball).
45
+
46
+ ## License
47
+
48
+ MIT — see [`LICENSE`](../../LICENSE).
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Public shape of one entry returned by `list()` — `updatedAt` is a real
3
+ * `Date` once the wire's ISO string has been rehydrated.
4
+ */
5
+ export interface AppStorageKeyEntry {
6
+ key: string;
7
+ updatedAt: Date;
8
+ }
9
+ export interface AppStorageListResult {
10
+ keys: AppStorageKeyEntry[];
11
+ /** Opaque, base64-encoded last key — pass back as `cursor` to page forward. */
12
+ nextCursor?: string;
13
+ }
14
+ export interface AppStorageQuota {
15
+ usedBytes: number;
16
+ rowCount: number;
17
+ /** Host-enforced ceiling (bytes). Surface in UI so callers don't hard-code 50MB. */
18
+ limitBytes: number;
19
+ /** Host-enforced row ceiling (~1M today). */
20
+ limitRows: number;
21
+ }
22
+ export interface UseAppStorage {
23
+ /**
24
+ * Read a key for the current (block instance, viewer) tuple. Returns
25
+ * `null` when the key isn't set OR the viewer is anonymous. The generic
26
+ * is for caller convenience; the host stores arbitrary JSON.
27
+ */
28
+ get<T = unknown>(key: string): Promise<T | null>;
29
+ /**
30
+ * Upsert a value. Resolves on host ack. Rejects with the host's
31
+ * `error` string when the value exceeds 64KB, when the per-app 50MB
32
+ * quota would be crossed, or when the viewer is anonymous.
33
+ */
34
+ set<T = unknown>(key: string, value: T): Promise<{
35
+ ok: true;
36
+ sizeBytes?: number;
37
+ }>;
38
+ /**
39
+ * Remove a key. Resolves with `{ deleted: true }` when a row was
40
+ * present, `{ deleted: false }` when the key wasn't set (still treated
41
+ * as success — idempotent delete).
42
+ */
43
+ delete(key: string): Promise<{
44
+ ok: true;
45
+ deleted: boolean;
46
+ }>;
47
+ /**
48
+ * Cursor-paginated key listing. Values aren't returned — call `get(key)`
49
+ * for the ones you care about.
50
+ */
51
+ list(opts?: {
52
+ prefix?: string;
53
+ limit?: number;
54
+ cursor?: string;
55
+ }): Promise<AppStorageListResult>;
56
+ /**
57
+ * Diagnostic: current usage + the v0 ceilings. Build a "X of 50 MB used"
58
+ * settings widget against this.
59
+ */
60
+ getQuota(): Promise<AppStorageQuota>;
61
+ }
62
+ /**
63
+ * Per-(block instance, viewer) KV datastore. Calls flow through the host's
64
+ * postMessage bridge — the block never sees the apps DB credentials.
65
+ *
66
+ * Anon viewers get a no-op read path (`get` resolves `null`, `list` returns
67
+ * empty) and a hard reject on writes; the block decides whether to gate
68
+ * its UI on `useBlockContext().viewer`.
69
+ *
70
+ * The hook is stable across renders — it returns the same object identity
71
+ * once the transport singleton is created, so it's safe to put in
72
+ * dependency arrays of `useEffect` / `useMemo`.
73
+ */
74
+ export declare function useAppStorage(): UseAppStorage;
75
+ //# sourceMappingURL=useAppStorage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAppStorage.d.ts","sourceRoot":"","sources":["../../src/hooks/useAppStorage.ts"],"names":[],"mappings":"AAKA;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,kBAAkB,EAAE,CAAC;IAC3B,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,oFAAoF;IACpF,UAAU,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IACjD;;;;OAIG;IACH,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnF;;;;OAIG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC7D;;;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,oBAAoB,CAAC,CAAC;IAClC;;;OAGG;IACH,QAAQ,IAAI,OAAO,CAAC,eAAe,CAAC,CAAC;CACtC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,IAAI,aAAa,CAyE7C"}
@@ -0,0 +1,73 @@
1
+ import { useMemo } from 'react';
2
+ import { getTransport } from '../internal/singleton.js';
3
+ import { sendTypedRequest } from '../internal/transport.js';
4
+ /**
5
+ * Per-(block instance, viewer) KV datastore. Calls flow through the host's
6
+ * postMessage bridge — the block never sees the apps DB credentials.
7
+ *
8
+ * Anon viewers get a no-op read path (`get` resolves `null`, `list` returns
9
+ * empty) and a hard reject on writes; the block decides whether to gate
10
+ * its UI on `useBlockContext().viewer`.
11
+ *
12
+ * The hook is stable across renders — it returns the same object identity
13
+ * once the transport singleton is created, so it's safe to put in
14
+ * dependency arrays of `useEffect` / `useMemo`.
15
+ */
16
+ export function useAppStorage() {
17
+ return useMemo(() => {
18
+ const transport = getTransport();
19
+ return {
20
+ async get(key) {
21
+ const result = await sendTypedRequest(transport, { type: 'APP_STORAGE_GET', payload: { key } }, 'APP_STORAGE_GET_RESULT');
22
+ if (result.error)
23
+ throw new Error(result.error);
24
+ return (result.value ?? null);
25
+ },
26
+ async set(key, value) {
27
+ const result = await sendTypedRequest(transport, { type: 'APP_STORAGE_SET', payload: { key, value } }, 'APP_STORAGE_SET_RESULT');
28
+ if (!result.ok || result.error) {
29
+ throw new Error(result.error ?? 'storage set failed');
30
+ }
31
+ return { ok: true, sizeBytes: result.sizeBytes };
32
+ },
33
+ async delete(key) {
34
+ const result = await sendTypedRequest(transport, { type: 'APP_STORAGE_DELETE', payload: { key } }, 'APP_STORAGE_DELETE_RESULT');
35
+ if (!result.ok || result.error) {
36
+ throw new Error(result.error ?? 'storage delete failed');
37
+ }
38
+ return { ok: true, deleted: result.deleted };
39
+ },
40
+ async list(opts) {
41
+ const result = await sendTypedRequest(transport, {
42
+ type: 'APP_STORAGE_LIST',
43
+ payload: {
44
+ prefix: opts?.prefix,
45
+ limit: opts?.limit,
46
+ cursor: opts?.cursor,
47
+ },
48
+ }, 'APP_STORAGE_LIST_RESULT');
49
+ if (result.error)
50
+ throw new Error(result.error);
51
+ return {
52
+ keys: result.keys.map((k) => ({
53
+ key: k.key,
54
+ updatedAt: new Date(k.updatedAt),
55
+ })),
56
+ nextCursor: result.nextCursor,
57
+ };
58
+ },
59
+ async getQuota() {
60
+ const result = await sendTypedRequest(transport, { type: 'APP_STORAGE_QUOTA', payload: {} }, 'APP_STORAGE_QUOTA_RESULT');
61
+ if (result.error)
62
+ throw new Error(result.error);
63
+ return {
64
+ usedBytes: result.usedBytes,
65
+ rowCount: result.rowCount,
66
+ limitBytes: result.limitBytes,
67
+ limitRows: result.limitRows,
68
+ };
69
+ },
70
+ };
71
+ }, []);
72
+ }
73
+ //# sourceMappingURL=useAppStorage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAppStorage.js","sourceRoot":"","sources":["../../src/hooks/useAppStorage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Fire-and-forget analytics tracking. The host forwards events to its
3
+ * analytics pipeline (ClickHouse in production); the block doesn't see
4
+ * acknowledgements and shouldn't block on them.
5
+ */
6
+ export declare function useBlockAnalytics(): {
7
+ track: (eventName: string, properties?: Record<string, unknown>) => void;
8
+ };
9
+ //# sourceMappingURL=useBlockAnalytics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBlockAnalytics.d.ts","sourceRoot":"","sources":["../../src/hooks/useBlockAnalytics.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI;IACnC,KAAK,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;CAC1E,CAKA"}
@@ -0,0 +1,14 @@
1
+ import { useCallback } from 'react';
2
+ import { getTransport } from '../internal/singleton.js';
3
+ /**
4
+ * Fire-and-forget analytics tracking. The host forwards events to its
5
+ * analytics pipeline (ClickHouse in production); the block doesn't see
6
+ * acknowledgements and shouldn't block on them.
7
+ */
8
+ export function useBlockAnalytics() {
9
+ const track = useCallback((eventName, properties) => {
10
+ getTransport().sendMessage({ type: 'TRACK_EVENT', payload: { eventName, properties } });
11
+ }, []);
12
+ return { track };
13
+ }
14
+ //# sourceMappingURL=useBlockAnalytics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBlockAnalytics.js","sourceRoot":"","sources":["../../src/hooks/useBlockAnalytics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAEpC,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAExD;;;;GAIG;AACH,MAAM,UAAU,iBAAiB;IAG/B,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,SAAiB,EAAE,UAAoC,EAAE,EAAE;QACpF,YAAY,EAAE,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC;IAC1F,CAAC,EAAE,EAAE,CAAC,CAAC;IACP,OAAO,EAAE,KAAK,EAAE,CAAC;AACnB,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { BlockSnapshot } from '../internal/transport.js';
2
+ /**
3
+ * Subscribe a hook to the singleton transport. All hooks build on this —
4
+ * `useSyncExternalStore` gives concurrent-mode safety and minimal re-renders.
5
+ */
6
+ declare function useTransportSnapshot(): BlockSnapshot;
7
+ /**
8
+ * Primary hook for a block app. Returns the full per-instance context
9
+ * delivered by the host plus a `ready` gate the UI should respect — fields
10
+ * other than `ready` are sentinel-empty before `BLOCK_INIT` lands.
11
+ *
12
+ * The transport detection (iframe vs inline) happens on first call and is
13
+ * cached process-wide; block apps don't branch on render mode.
14
+ */
15
+ export declare function useBlockContext(): Pick<BlockSnapshot, 'ready' | 'renderMode' | 'context' | 'token' | 'settings' | 'viewer' | 'theme' | 'blockId' | 'blockInstanceId' | 'appId'>;
16
+ /** Re-exported so other hooks in this package can share the subscription. */
17
+ export { useTransportSnapshot };
18
+ //# sourceMappingURL=useBlockContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBlockContext.d.ts","sourceRoot":"","sources":["../../src/hooks/useBlockContext.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D;;;GAGG;AACH,iBAAS,oBAAoB,IAAI,aAAa,CAS7C;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,IAAI,IAAI,CACrC,aAAa,EACX,OAAO,GACP,YAAY,GACZ,SAAS,GACT,OAAO,GACP,UAAU,GACV,QAAQ,GACR,OAAO,GACP,SAAS,GACT,iBAAiB,GACjB,OAAO,CACV,CAcA;AAED,6EAA6E;AAC7E,OAAO,EAAE,oBAAoB,EAAE,CAAC"}
@@ -0,0 +1,39 @@
1
+ import { useSyncExternalStore } from 'react';
2
+ import { getTransport } from '../internal/singleton.js';
3
+ /**
4
+ * Subscribe a hook to the singleton transport. All hooks build on this —
5
+ * `useSyncExternalStore` gives concurrent-mode safety and minimal re-renders.
6
+ */
7
+ function useTransportSnapshot() {
8
+ const transport = getTransport();
9
+ return useSyncExternalStore((cb) => transport.subscribe(cb), () => transport.getSnapshot(),
10
+ // Server snapshot: blocks don't SSR meaningful state. Return the
11
+ // pre-init empty snapshot so React's hydration check doesn't warn.
12
+ () => transport.getSnapshot());
13
+ }
14
+ /**
15
+ * Primary hook for a block app. Returns the full per-instance context
16
+ * delivered by the host plus a `ready` gate the UI should respect — fields
17
+ * other than `ready` are sentinel-empty before `BLOCK_INIT` lands.
18
+ *
19
+ * The transport detection (iframe vs inline) happens on first call and is
20
+ * cached process-wide; block apps don't branch on render mode.
21
+ */
22
+ export function useBlockContext() {
23
+ const snap = useTransportSnapshot();
24
+ return {
25
+ ready: snap.ready,
26
+ renderMode: snap.renderMode,
27
+ context: snap.context,
28
+ token: snap.token,
29
+ settings: snap.settings,
30
+ viewer: snap.viewer,
31
+ theme: snap.theme,
32
+ blockId: snap.blockId,
33
+ blockInstanceId: snap.blockInstanceId,
34
+ appId: snap.appId,
35
+ };
36
+ }
37
+ /** Re-exported so other hooks in this package can share the subscription. */
38
+ export { useTransportSnapshot };
39
+ //# sourceMappingURL=useBlockContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBlockContext.js","sourceRoot":"","sources":["../../src/hooks/useBlockContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAE7C,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAGxD;;;GAGG;AACH,SAAS,oBAAoB;IAC3B,MAAM,SAAS,GAAG,YAAY,EAAE,CAAC;IACjC,OAAO,oBAAoB,CACzB,CAAC,EAAE,EAAE,EAAE,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC,EAC/B,GAAG,EAAE,CAAC,SAAS,CAAC,WAAW,EAAE;IAC7B,iEAAiE;IACjE,mEAAmE;IACnE,GAAG,EAAE,CAAC,SAAS,CAAC,WAAW,EAAE,CAC9B,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe;IAa7B,MAAM,IAAI,GAAG,oBAAoB,EAAE,CAAC;IACpC,OAAO;QACL,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,eAAe,EAAE,IAAI,CAAC,eAAe;QACrC,KAAK,EAAE,IAAI,CAAC,KAAK;KAClB,CAAC;AACJ,CAAC;AAED,6EAA6E;AAC7E,OAAO,EAAE,oBAAoB,EAAE,CAAC"}
@@ -0,0 +1,13 @@
1
+ import { type RefObject } from 'react';
2
+ /**
3
+ * Observes the referenced element's height and asks the host to resize on
4
+ * every change. Attach to the block's root DOM element.
5
+ *
6
+ * The ResizeObserver runs on both transports — only the outbound
7
+ * `RESIZE_IFRAME` message differs:
8
+ * - Iframe path: posts `RESIZE_IFRAME` with the integer-rounded height.
9
+ * - Inline path: `InlineTransport.sendMessage` is a no-op (the host DOM
10
+ * reflows naturally), so the observer fires but no message goes out.
11
+ */
12
+ export declare function useBlockResize(ref: RefObject<HTMLElement | null>): void;
13
+ //# sourceMappingURL=useBlockResize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBlockResize.d.ts","sourceRoot":"","sources":["../../src/hooks/useBlockResize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAIlD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,IAAI,CAiBvE"}
@@ -0,0 +1,33 @@
1
+ import { useEffect } from 'react';
2
+ import { getTransport } from '../internal/singleton.js';
3
+ /**
4
+ * Observes the referenced element's height and asks the host to resize on
5
+ * every change. Attach to the block's root DOM element.
6
+ *
7
+ * The ResizeObserver runs on both transports — only the outbound
8
+ * `RESIZE_IFRAME` message differs:
9
+ * - Iframe path: posts `RESIZE_IFRAME` with the integer-rounded height.
10
+ * - Inline path: `InlineTransport.sendMessage` is a no-op (the host DOM
11
+ * reflows naturally), so the observer fires but no message goes out.
12
+ */
13
+ export function useBlockResize(ref) {
14
+ useEffect(() => {
15
+ const el = ref.current;
16
+ if (!el)
17
+ return;
18
+ if (typeof ResizeObserver === 'undefined')
19
+ return;
20
+ const transport = getTransport();
21
+ let lastHeight = -1;
22
+ const observer = new ResizeObserver((entries) => {
23
+ const height = Math.ceil(entries[0]?.contentRect.height ?? el.offsetHeight);
24
+ if (height === lastHeight)
25
+ return;
26
+ lastHeight = height;
27
+ transport.sendMessage({ type: 'RESIZE_IFRAME', payload: { height } });
28
+ });
29
+ observer.observe(el);
30
+ return () => observer.disconnect();
31
+ }, [ref]);
32
+ }
33
+ //# sourceMappingURL=useBlockResize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBlockResize.js","sourceRoot":"","sources":["../../src/hooks/useBlockResize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAkB,MAAM,OAAO,CAAC;AAElD,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAExD;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAAC,GAAkC;IAC/D,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC;QACvB,IAAI,CAAC,EAAE;YAAE,OAAO;QAChB,IAAI,OAAO,cAAc,KAAK,WAAW;YAAE,OAAO;QAElD,MAAM,SAAS,GAAG,YAAY,EAAE,CAAC;QACjC,IAAI,UAAU,GAAG,CAAC,CAAC,CAAC;QACpB,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC,CAAC,OAAO,EAAE,EAAE;YAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,MAAM,IAAI,EAAE,CAAC,YAAY,CAAC,CAAC;YAC5E,IAAI,MAAM,KAAK,UAAU;gBAAE,OAAO;YAClC,UAAU,GAAG,MAAM,CAAC;YACpB,SAAS,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;QACxE,CAAC,CAAC,CAAC;QACH,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACrB,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;IACrC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;AACZ,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { BlockSettings } from '@civitai/app-sdk/blocks';
2
+ /**
3
+ * Shorthand for `useBlockContext().settings`. Returns the publisher- and
4
+ * user-controlled settings the host forwarded at init.
5
+ */
6
+ export declare function useBlockSettings(): BlockSettings;
7
+ //# sourceMappingURL=useBlockSettings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBlockSettings.d.ts","sourceRoot":"","sources":["../../src/hooks/useBlockSettings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAI7D;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,aAAa,CAEhD"}
@@ -0,0 +1,9 @@
1
+ import { useTransportSnapshot } from './useBlockContext.js';
2
+ /**
3
+ * Shorthand for `useBlockContext().settings`. Returns the publisher- and
4
+ * user-controlled settings the host forwarded at init.
5
+ */
6
+ export function useBlockSettings() {
7
+ return useTransportSnapshot().settings;
8
+ }
9
+ //# sourceMappingURL=useBlockSettings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBlockSettings.js","sourceRoot":"","sources":["../../src/hooks/useBlockSettings.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAE5D;;;GAGG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,oBAAoB,EAAE,CAAC,QAAQ,CAAC;AACzC,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { BlockToken } from '@civitai/app-sdk/blocks';
2
+ /**
3
+ * Returns the current block-scoped JWT and keeps it fresh.
4
+ *
5
+ * Iframe path: schedules a `REQUEST_TOKEN` postMessage T-2min before expiry.
6
+ * The host replies with `TOKEN_REFRESH_RESPONSE`, the transport updates its
7
+ * snapshot, and the hook re-renders with the new token.
8
+ *
9
+ * Consumers also get a `refresh()` callable for the 401-retry path. Call it
10
+ * after any API request returns 401 — it forces an immediate token mint and
11
+ * resolves once the new token is applied to the snapshot.
12
+ */
13
+ export declare function useBlockToken(): BlockToken & {
14
+ refresh: () => Promise<void>;
15
+ };
16
+ //# sourceMappingURL=useBlockToken.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBlockToken.d.ts","sourceRoot":"","sources":["../../src/hooks/useBlockToken.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAS1D;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,IAAI,UAAU,GAAG;IAAE,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAwB7E"}
@@ -0,0 +1,61 @@
1
+ import { useEffect } from 'react';
2
+ import { getTransport } from '../internal/singleton.js';
3
+ import { sendTypedRequest } from '../internal/transport.js';
4
+ import { useTransportSnapshot } from './useBlockContext.js';
5
+ /** Refresh fires `REFRESH_LEAD_MS` before the token's `expiresAt`. */
6
+ const REFRESH_LEAD_MS = 2 * 60 * 1000;
7
+ /**
8
+ * Returns the current block-scoped JWT and keeps it fresh.
9
+ *
10
+ * Iframe path: schedules a `REQUEST_TOKEN` postMessage T-2min before expiry.
11
+ * The host replies with `TOKEN_REFRESH_RESPONSE`, the transport updates its
12
+ * snapshot, and the hook re-renders with the new token.
13
+ *
14
+ * Consumers also get a `refresh()` callable for the 401-retry path. Call it
15
+ * after any API request returns 401 — it forces an immediate token mint and
16
+ * resolves once the new token is applied to the snapshot.
17
+ */
18
+ export function useBlockToken() {
19
+ const snap = useTransportSnapshot();
20
+ const token = snap.token;
21
+ const instanceId = snap.blockInstanceId;
22
+ useEffect(() => {
23
+ if (!snap.ready)
24
+ return;
25
+ const msUntilRefresh = token.expiresAt.getTime() - Date.now() - REFRESH_LEAD_MS;
26
+ if (msUntilRefresh <= 0) {
27
+ // Already past the refresh window — refresh now.
28
+ // Background refresh — swallow errors. The next 401 will re-trigger
29
+ // via the explicit refresh() callable below.
30
+ requestRefresh(instanceId).catch(() => { });
31
+ return;
32
+ }
33
+ const id = setTimeout(() => {
34
+ // Background refresh — swallow errors. The next 401 will re-trigger
35
+ // via the explicit refresh() callable below.
36
+ requestRefresh(instanceId).catch(() => { });
37
+ }, msUntilRefresh);
38
+ return () => clearTimeout(id);
39
+ }, [snap.ready, token.expiresAt, instanceId]);
40
+ return { ...token, refresh: () => requestRefresh(instanceId) };
41
+ }
42
+ /**
43
+ * In-flight dedup. Both the scheduled-timeout path and the "already past"
44
+ * synchronous path can fire while a previous refresh is still pending —
45
+ * coalescing into a single round-trip avoids fanning out duplicate
46
+ * REQUEST_TOKEN messages, which would otherwise hit the host once per
47
+ * snapshot update.
48
+ */
49
+ let inFlightRefresh = null;
50
+ async function requestRefresh(blockInstanceId) {
51
+ if (inFlightRefresh)
52
+ return inFlightRefresh;
53
+ const transport = getTransport();
54
+ inFlightRefresh = sendTypedRequest(transport, { type: 'REQUEST_TOKEN', payload: { blockInstanceId } }, 'TOKEN_REFRESH_RESPONSE')
55
+ .then(() => undefined)
56
+ .finally(() => {
57
+ inFlightRefresh = null;
58
+ });
59
+ return inFlightRefresh;
60
+ }
61
+ //# sourceMappingURL=useBlockToken.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBlockToken.js","sourceRoot":"","sources":["../../src/hooks/useBlockToken.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIlC,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAE5D,sEAAsE;AACtE,MAAM,eAAe,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AAEtC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,IAAI,GAAG,oBAAoB,EAAE,CAAC;IACpC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;IACzB,MAAM,UAAU,GAAG,IAAI,CAAC,eAAe,CAAC;IAExC,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,IAAI,CAAC,KAAK;YAAE,OAAO;QACxB,MAAM,cAAc,GAAG,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,eAAe,CAAC;QAChF,IAAI,cAAc,IAAI,CAAC,EAAE,CAAC;YACxB,iDAAiD;YACjD,oEAAoE;YACpE,6CAA6C;YAC7C,cAAc,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YAC3C,OAAO;QACT,CAAC;QACD,MAAM,EAAE,GAAG,UAAU,CAAC,GAAG,EAAE;YACzB,oEAAoE;YACpE,6CAA6C;YAC7C,cAAc,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAC7C,CAAC,EAAE,cAAc,CAAC,CAAC;QACnB,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;IAChC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC;IAE9C,OAAO,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,CAAC;AACjE,CAAC;AAED;;;;;;GAMG;AACH,IAAI,eAAe,GAAyB,IAAI,CAAC;AAEjD,KAAK,UAAU,cAAc,CAAC,eAAuB;IACnD,IAAI,eAAe;QAAE,OAAO,eAAe,CAAC;IAC5C,MAAM,SAAS,GAAG,YAAY,EAAE,CAAC;IACjC,eAAe,GAAG,gBAAgB,CAChC,SAAS,EACT,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,EAAE,eAAe,EAAE,EAAE,EACvD,wBAAwB,CACzB;SACE,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC;SACrB,OAAO,CAAC,GAAG,EAAE;QACZ,eAAe,GAAG,IAAI,CAAC;IACzB,CAAC,CAAC,CAAC;IACL,OAAO,eAAe,CAAC;AACzB,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Opens the Civitai Buzz purchase modal on the host. Resolves with the
3
+ * outcome when the user closes the modal — `purchased: true` means the
4
+ * balance increased; the new balance is included if the host reports it.
5
+ */
6
+ export declare function useBuzzPurchase(): {
7
+ openPurchaseModal: (suggestedAmount?: number) => Promise<{
8
+ purchased: boolean;
9
+ newBalance?: number;
10
+ }>;
11
+ };
12
+ //# sourceMappingURL=useBuzzPurchase.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBuzzPurchase.d.ts","sourceRoot":"","sources":["../../src/hooks/useBuzzPurchase.ts"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,wBAAgB,eAAe,IAAI;IACjC,iBAAiB,EAAE,CAAC,eAAe,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACvG,CAUA"}
@@ -0,0 +1,16 @@
1
+ import { useCallback } from 'react';
2
+ import { getTransport } from '../internal/singleton.js';
3
+ import { sendTypedRequest } from '../internal/transport.js';
4
+ /**
5
+ * Opens the Civitai Buzz purchase modal on the host. Resolves with the
6
+ * outcome when the user closes the modal — `purchased: true` means the
7
+ * balance increased; the new balance is included if the host reports it.
8
+ */
9
+ export function useBuzzPurchase() {
10
+ const openPurchaseModal = useCallback(async (suggestedAmount) => {
11
+ const { purchased, newBalance } = await sendTypedRequest(getTransport(), { type: 'OPEN_BUZZ_PURCHASE', payload: { suggestedAmount } }, 'BUZZ_PURCHASE_RESULT');
12
+ return { purchased, newBalance };
13
+ }, []);
14
+ return { openPurchaseModal };
15
+ }
16
+ //# sourceMappingURL=useBuzzPurchase.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBuzzPurchase.js","sourceRoot":"","sources":["../../src/hooks/useBuzzPurchase.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;AAE5D;;;;GAIG;AACH,MAAM,UAAU,eAAe;IAG7B,MAAM,iBAAiB,GAAG,WAAW,CAAC,KAAK,EAAE,eAAwB,EAAE,EAAE;QACvE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,MAAM,gBAAgB,CACtD,YAAY,EAAE,EACd,EAAE,IAAI,EAAE,oBAAoB,EAAE,OAAO,EAAE,EAAE,eAAe,EAAE,EAAE,EAC5D,sBAAsB,CACvB,CAAC;QACF,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;IACnC,CAAC,EAAE,EAAE,CAAC,CAAC;IACP,OAAO,EAAE,iBAAiB,EAAE,CAAC;AAC/B,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { BlockWorkflowSnapshot, WorkflowBody, WorkflowStatus } from '@civitai/app-sdk/blocks';
2
+ interface UseBuzzWorkflowReturn {
3
+ estimate: (body: WorkflowBody) => Promise<BlockWorkflowSnapshot>;
4
+ submit: (body: WorkflowBody) => Promise<BlockWorkflowSnapshot>;
5
+ poll: (workflowId: string) => Promise<BlockWorkflowSnapshot>;
6
+ status: WorkflowStatus;
7
+ result: BlockWorkflowSnapshot | null;
8
+ error: Error | null;
9
+ }
10
+ /**
11
+ * Orchestrates the estimate → confirm → submit → poll dance through the
12
+ * host-mediated `postMessage` path.
13
+ *
14
+ * The host enforces budget rules (`cost_estimate <= token.buzzBudget`)
15
+ * before forwarding to the orchestrator; submit() will reject if the host
16
+ * refuses. Block apps should call `useBuzzPurchase().openPurchaseModal()`
17
+ * when that happens.
18
+ */
19
+ export declare function useBuzzWorkflow(): UseBuzzWorkflowReturn;
20
+ export {};
21
+ //# sourceMappingURL=useBuzzWorkflow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBuzzWorkflow.d.ts","sourceRoot":"","sources":["../../src/hooks/useBuzzWorkflow.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAiBnG,UAAU,qBAAqB;IAC7B,QAAQ,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACjE,MAAM,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAC/D,IAAI,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAC7D,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,qBAAqB,GAAG,IAAI,CAAC;IACrC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,IAAI,qBAAqB,CAgEvD"}
@@ -0,0 +1,76 @@
1
+ import { useCallback, useState } from 'react';
2
+ import { getTransport } from '../internal/singleton.js';
3
+ import { sendTypedRequest } from '../internal/transport.js';
4
+ /**
5
+ * Snapshot statuses that mean "no further polling is needed."
6
+ * Used by both `submit` (a host can return an instant-fail / cached result)
7
+ * and `poll` so the hook can't get stuck in 'polling' after a terminal reply.
8
+ */
9
+ const TERMINAL_STATUSES = new Set([
10
+ 'succeeded',
11
+ 'failed',
12
+ 'canceled',
13
+ 'expired',
14
+ ]);
15
+ /**
16
+ * Orchestrates the estimate → confirm → submit → poll dance through the
17
+ * host-mediated `postMessage` path.
18
+ *
19
+ * The host enforces budget rules (`cost_estimate <= token.buzzBudget`)
20
+ * before forwarding to the orchestrator; submit() will reject if the host
21
+ * refuses. Block apps should call `useBuzzPurchase().openPurchaseModal()`
22
+ * when that happens.
23
+ */
24
+ export function useBuzzWorkflow() {
25
+ const [status, setStatus] = useState('idle');
26
+ const [result, setResult] = useState(null);
27
+ const [error, setError] = useState(null);
28
+ const estimate = useCallback(async (body) => {
29
+ setError(null);
30
+ setStatus('estimating');
31
+ try {
32
+ const { snapshot } = await sendTypedRequest(getTransport(), { type: 'ESTIMATE_WORKFLOW', payload: { body } }, 'ESTIMATE_RESULT');
33
+ setResult(snapshot);
34
+ setStatus('confirming');
35
+ return snapshot;
36
+ }
37
+ catch (err) {
38
+ setError(err);
39
+ setStatus('error');
40
+ throw err;
41
+ }
42
+ }, []);
43
+ const submit = useCallback(async (body) => {
44
+ setError(null);
45
+ setStatus('submitting');
46
+ try {
47
+ const { snapshot } = await sendTypedRequest(getTransport(), { type: 'SUBMIT_WORKFLOW', payload: { body } }, 'WORKFLOW_SUBMITTED');
48
+ setResult(snapshot);
49
+ setStatus(TERMINAL_STATUSES.has(snapshot.status) ? 'done' : 'polling');
50
+ return snapshot;
51
+ }
52
+ catch (err) {
53
+ setError(err);
54
+ setStatus('error');
55
+ throw err;
56
+ }
57
+ }, []);
58
+ const poll = useCallback(async (workflowId) => {
59
+ setStatus('polling');
60
+ try {
61
+ const { snapshot } = await sendTypedRequest(getTransport(), { type: 'POLL_WORKFLOW', payload: { workflowId } }, 'WORKFLOW_STATUS');
62
+ setResult(snapshot);
63
+ if (TERMINAL_STATUSES.has(snapshot.status)) {
64
+ setStatus('done');
65
+ }
66
+ return snapshot;
67
+ }
68
+ catch (err) {
69
+ setError(err);
70
+ setStatus('error');
71
+ throw err;
72
+ }
73
+ }, []);
74
+ return { estimate, submit, poll, status, result, error };
75
+ }
76
+ //# sourceMappingURL=useBuzzWorkflow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBuzzWorkflow.js","sourceRoot":"","sources":["../../src/hooks/useBuzzWorkflow.ts"],"names":[],"mappings":"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"}