@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
@@ -0,0 +1,18 @@
1
+ import { type DetectOptions } from './detector.js';
2
+ import type { BlockTransport } from './transport.js';
3
+ /**
4
+ * Returns the process-wide transport, instantiating it on first call.
5
+ *
6
+ * Hooks call this with no arguments — they get whatever the detector chose
7
+ * (iframe vs inline) based on the runtime environment. Tests and starter
8
+ * dev harnesses can pass explicit `DetectOptions` once before the first
9
+ * hook call to override origin allowlists or inject a mock window.
10
+ *
11
+ * After the first call with options, later calls without options return
12
+ * the same instance. A later call WITH different options is ignored — call
13
+ * `__resetTransport()` (from `@civitai/blocks-react/testing`) first.
14
+ */
15
+ export declare function getTransport(opts?: DetectOptions): BlockTransport;
16
+ /** Test-only. Production hooks never call this. */
17
+ export declare function __resetTransport(): void;
18
+ //# sourceMappingURL=singleton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"singleton.d.ts","sourceRoot":"","sources":["../../src/internal/singleton.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAC3E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAIrD;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,IAAI,CAAC,EAAE,aAAa,GAAG,cAAc,CAIjE;AAED,mDAAmD;AACnD,wBAAgB,gBAAgB,IAAI,IAAI,CAKvC"}
@@ -0,0 +1,28 @@
1
+ import { BlockTransportDetector } from './detector.js';
2
+ let cached = null;
3
+ /**
4
+ * Returns the process-wide transport, instantiating it on first call.
5
+ *
6
+ * Hooks call this with no arguments — they get whatever the detector chose
7
+ * (iframe vs inline) based on the runtime environment. Tests and starter
8
+ * dev harnesses can pass explicit `DetectOptions` once before the first
9
+ * hook call to override origin allowlists or inject a mock window.
10
+ *
11
+ * After the first call with options, later calls without options return
12
+ * the same instance. A later call WITH different options is ignored — call
13
+ * `__resetTransport()` (from `@civitai/blocks-react/testing`) first.
14
+ */
15
+ export function getTransport(opts) {
16
+ if (cached)
17
+ return cached;
18
+ cached = BlockTransportDetector.detect(opts);
19
+ return cached;
20
+ }
21
+ /** Test-only. Production hooks never call this. */
22
+ export function __resetTransport() {
23
+ // Some transports allocate listeners; dispose if possible.
24
+ const disposable = cached;
25
+ disposable?.dispose?.();
26
+ cached = null;
27
+ }
28
+ //# sourceMappingURL=singleton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"singleton.js","sourceRoot":"","sources":["../../src/internal/singleton.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAsB,MAAM,eAAe,CAAC;AAG3E,IAAI,MAAM,GAA0B,IAAI,CAAC;AAEzC;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,YAAY,CAAC,IAAoB;IAC/C,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAC1B,MAAM,GAAG,sBAAsB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC7C,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,gBAAgB;IAC9B,2DAA2D;IAC3D,MAAM,UAAU,GAAG,MAA4D,CAAC;IAChF,UAAU,EAAE,OAAO,EAAE,EAAE,CAAC;IACxB,MAAM,GAAG,IAAI,CAAC;AAChB,CAAC"}
@@ -0,0 +1,100 @@
1
+ import type { BlockContext, BlockInitPayload, BlockSettings, BlockToken, Theme, ViewerInfo, WrappedToken, ParentToBlockMessage, ParentToBlockMessageType, BlockToParentMessage, BlockToParentMessageType } from '@civitai/app-sdk/blocks';
2
+ /**
3
+ * Synchronous snapshot the hooks read via `useSyncExternalStore`.
4
+ *
5
+ * Before `BLOCK_INIT` lands, `ready === false`, `viewer === null`, and the
6
+ * per-field values are sentinel empties. Hooks that render UI must gate on
7
+ * `ready`; non-UI hooks (e.g. `useBuzzWorkflow`) rely on the outbound queue
8
+ * in `IframeTransport` instead of gating.
9
+ */
10
+ export interface BlockSnapshot {
11
+ ready: boolean;
12
+ renderMode: 'iframe' | 'inline';
13
+ context: BlockContext;
14
+ token: BlockToken;
15
+ settings: BlockSettings;
16
+ /** `null` for anonymous viewers, matching `BlockInitPayload.viewer`. */
17
+ viewer: ViewerInfo | null;
18
+ theme: Theme;
19
+ blockInstanceId: string;
20
+ blockId: string;
21
+ appId: string;
22
+ }
23
+ /**
24
+ * Outbound message shape, without the auto-assigned `requestId` field.
25
+ * Callers of `sendRequest` describe the message they want sent; the transport
26
+ * appends the `requestId`.
27
+ */
28
+ export type OutboundRequest = {
29
+ [K in BlockToParentMessageType]: Extract<BlockToParentMessage, {
30
+ type: K;
31
+ }> extends {
32
+ payload: {
33
+ requestId: string;
34
+ };
35
+ } ? {
36
+ type: K;
37
+ payload: Omit<Extract<BlockToParentMessage, {
38
+ type: K;
39
+ }>['payload'], 'requestId'>;
40
+ } : never;
41
+ }[BlockToParentMessageType];
42
+ /**
43
+ * Contract every transport (iframe v1, inline v2) implements. Hooks consume
44
+ * this through the singleton in `./singleton.ts` so block apps stay unaware
45
+ * of which path is active.
46
+ *
47
+ * Messages flow as full discriminated-union values rather than (type, payload)
48
+ * tuples — this avoids generic-variance pitfalls when the implementation
49
+ * has to widen back to the union.
50
+ *
51
+ * `sendRequest` here is intentionally untyped on the return; the typed view
52
+ * lives in the free function {@link sendTypedRequest}. Putting the generic
53
+ * on a free wrapper sidesteps the "interface method with generic return
54
+ * depending on a parameter" variance problem (TS can't prove `Promise<Extract<..., TRes>>`
55
+ * covariance across implementations).
56
+ */
57
+ export interface BlockTransport {
58
+ /** Current snapshot; cheap to call (no allocation). */
59
+ getSnapshot(): BlockSnapshot;
60
+ /** Subscribe to snapshot changes. Returns an unsubscribe function. */
61
+ subscribe(listener: () => void): () => void;
62
+ /** Fire-and-forget message to the peer. */
63
+ sendMessage(message: BlockToParentMessage): void;
64
+ /** Untyped — use {@link sendTypedRequest} for the type-narrowed view. */
65
+ sendRequest(request: OutboundRequest, responseType: ParentToBlockMessageType, opts?: {
66
+ timeoutMs?: number;
67
+ }): Promise<unknown>;
68
+ }
69
+ /**
70
+ * Type-safe wrapper around `transport.sendRequest`. Hooks always go through
71
+ * this so the response payload narrows based on `responseType`.
72
+ *
73
+ * Implemented as `async` + `await` instead of a single `as` cast: `Promise<T>`
74
+ * is invariant in `T`, so casting `Promise<unknown>` directly to
75
+ * `Promise<Extract<..., TRes>>` trips TS variance checking. Awaiting first
76
+ * yields a plain `unknown` we can cast synchronously, and the `async` keyword
77
+ * re-wraps it in the correctly-typed Promise.
78
+ */
79
+ export declare function sendTypedRequest<TRes extends ParentToBlockMessageType>(transport: BlockTransport, request: OutboundRequest, responseType: TRes, opts?: {
80
+ timeoutMs?: number;
81
+ }): Promise<Extract<ParentToBlockMessage, {
82
+ type: TRes;
83
+ }>['payload']>;
84
+ export declare const EMPTY_SNAPSHOT: BlockSnapshot;
85
+ /** Convert a wire `BlockInitPayload` (ISO string expiresAt) into a `BlockSnapshot`. */
86
+ export declare function snapshotFromInit(payload: BlockInitPayload): BlockSnapshot;
87
+ /**
88
+ * Rehydrate a wire `WrappedToken` (ISO `expiresAt`) into the runtime
89
+ * `BlockToken` shape (`Date` `expiresAt`). Shared by `snapshotFromInit`
90
+ * and the `TOKEN_REFRESH` / `TOKEN_REFRESH_RESPONSE` handlers so the
91
+ * snapshot's token always reflects every wrapped field — `scopes` and
92
+ * `buzzBudget` included — not just `raw`/`expiresAt`.
93
+ */
94
+ export declare function tokenFromWrapped(wrapped: WrappedToken): BlockToken;
95
+ /**
96
+ * Monotonic request ID with a random prefix so concurrent block instances
97
+ * sharing the dev console don't collide in logs.
98
+ */
99
+ export declare function nextRequestId(): string;
100
+ //# sourceMappingURL=transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/internal/transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,gBAAgB,EAChB,aAAa,EACb,UAAU,EACV,KAAK,EACL,UAAU,EACV,YAAY,EACZ,oBAAoB,EACpB,wBAAwB,EACxB,oBAAoB,EACpB,wBAAwB,EACzB,MAAM,yBAAyB,CAAC;AAEjC;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAChC,OAAO,EAAE,YAAY,CAAC;IACtB,KAAK,EAAE,UAAU,CAAC;IAClB,QAAQ,EAAE,aAAa,CAAC;IACxB,wEAAwE;IACxE,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;IAC1B,KAAK,EAAE,KAAK,CAAC;IACb,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG;KAC3B,CAAC,IAAI,wBAAwB,GAAG,OAAO,CAAC,oBAAoB,EAAE;QAAE,IAAI,EAAE,CAAC,CAAA;KAAE,CAAC,SAAS;QAClF,OAAO,EAAE;YAAE,SAAS,EAAE,MAAM,CAAA;SAAE,CAAC;KAChC,GACG;QACE,IAAI,EAAE,CAAC,CAAC;QACR,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,oBAAoB,EAAE;YAAE,IAAI,EAAE,CAAC,CAAA;SAAE,CAAC,CAAC,SAAS,CAAC,EAAE,WAAW,CAAC,CAAC;KACnF,GACD,KAAK;CACV,CAAC,wBAAwB,CAAC,CAAC;AAE5B;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,cAAc;IAC7B,uDAAuD;IACvD,WAAW,IAAI,aAAa,CAAC;IAC7B,sEAAsE;IACtE,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;IAC5C,2CAA2C;IAC3C,WAAW,CAAC,OAAO,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACjD,yEAAyE;IACzE,WAAW,CACT,OAAO,EAAE,eAAe,EACxB,YAAY,EAAE,wBAAwB,EACtC,IAAI,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAC5B,OAAO,CAAC,OAAO,CAAC,CAAC;CACrB;AAED;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,SAAS,wBAAwB,EAC1E,SAAS,EAAE,cAAc,EACzB,OAAO,EAAE,eAAe,EACxB,YAAY,EAAE,IAAI,EAClB,IAAI,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5B,OAAO,CAAC,OAAO,CAAC,oBAAoB,EAAE;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAInE;AAED,eAAO,MAAM,cAAc,EAAE,aAW5B,CAAC;AAEF,uFAAuF;AACvF,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,gBAAgB,GAAG,aAAa,CAazE;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,YAAY,GAAG,UAAU,CAOlE;AAGD;;;GAGG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAGtC"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Type-safe wrapper around `transport.sendRequest`. Hooks always go through
3
+ * this so the response payload narrows based on `responseType`.
4
+ *
5
+ * Implemented as `async` + `await` instead of a single `as` cast: `Promise<T>`
6
+ * is invariant in `T`, so casting `Promise<unknown>` directly to
7
+ * `Promise<Extract<..., TRes>>` trips TS variance checking. Awaiting first
8
+ * yields a plain `unknown` we can cast synchronously, and the `async` keyword
9
+ * re-wraps it in the correctly-typed Promise.
10
+ */
11
+ export async function sendTypedRequest(transport, request, responseType, opts) {
12
+ const payload = await transport.sendRequest(request, responseType, opts);
13
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- variance escape; see fn jsdoc
14
+ return payload;
15
+ }
16
+ export const EMPTY_SNAPSHOT = {
17
+ ready: false,
18
+ renderMode: 'iframe',
19
+ context: { slotId: '' },
20
+ token: { raw: '', scopes: [], expiresAt: new Date(0) },
21
+ settings: { publisherSettings: {}, userSettings: {} },
22
+ viewer: null,
23
+ theme: 'light',
24
+ blockInstanceId: '',
25
+ blockId: '',
26
+ appId: '',
27
+ };
28
+ /** Convert a wire `BlockInitPayload` (ISO string expiresAt) into a `BlockSnapshot`. */
29
+ export function snapshotFromInit(payload) {
30
+ return {
31
+ ready: true,
32
+ renderMode: payload.renderMode,
33
+ context: payload.context,
34
+ token: tokenFromWrapped(payload.token),
35
+ settings: payload.settings,
36
+ viewer: payload.viewer,
37
+ theme: payload.theme,
38
+ blockInstanceId: payload.blockInstanceId,
39
+ blockId: payload.blockId,
40
+ appId: payload.appId,
41
+ };
42
+ }
43
+ /**
44
+ * Rehydrate a wire `WrappedToken` (ISO `expiresAt`) into the runtime
45
+ * `BlockToken` shape (`Date` `expiresAt`). Shared by `snapshotFromInit`
46
+ * and the `TOKEN_REFRESH` / `TOKEN_REFRESH_RESPONSE` handlers so the
47
+ * snapshot's token always reflects every wrapped field — `scopes` and
48
+ * `buzzBudget` included — not just `raw`/`expiresAt`.
49
+ */
50
+ export function tokenFromWrapped(wrapped) {
51
+ return {
52
+ raw: wrapped.raw,
53
+ scopes: wrapped.scopes,
54
+ expiresAt: new Date(wrapped.expiresAt),
55
+ buzzBudget: wrapped.buzzBudget,
56
+ };
57
+ }
58
+ let requestIdCounter = 0;
59
+ /**
60
+ * Monotonic request ID with a random prefix so concurrent block instances
61
+ * sharing the dev console don't collide in logs.
62
+ */
63
+ export function nextRequestId() {
64
+ requestIdCounter += 1;
65
+ return `${Math.random().toString(36).slice(2, 8)}-${requestIdCounter}`;
66
+ }
67
+ //# sourceMappingURL=transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.js","sourceRoot":"","sources":["../../src/internal/transport.ts"],"names":[],"mappings":"AAkFA;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,SAAyB,EACzB,OAAwB,EACxB,YAAkB,EAClB,IAA6B;IAE7B,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,OAAO,EAAE,YAAY,EAAE,IAAI,CAAC,CAAC;IACzE,+FAA+F;IAC/F,OAAO,OAAc,CAAC;AACxB,CAAC;AAED,MAAM,CAAC,MAAM,cAAc,GAAkB;IAC3C,KAAK,EAAE,KAAK;IACZ,UAAU,EAAE,QAAQ;IACpB,OAAO,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;IACvB,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE;IACtD,QAAQ,EAAE,EAAE,iBAAiB,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE;IACrD,MAAM,EAAE,IAAI;IACZ,KAAK,EAAE,OAAO;IACd,eAAe,EAAE,EAAE;IACnB,OAAO,EAAE,EAAE;IACX,KAAK,EAAE,EAAE;CACV,CAAC;AAEF,uFAAuF;AACvF,MAAM,UAAU,gBAAgB,CAAC,OAAyB;IACxD,OAAO;QACL,KAAK,EAAE,IAAI;QACX,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,KAAK,EAAE,gBAAgB,CAAC,OAAO,CAAC,KAAK,CAAC;QACtC,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,eAAe,EAAE,OAAO,CAAC,eAAe;QACxC,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,KAAK,EAAE,OAAO,CAAC,KAAK;KACrB,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAqB;IACpD,OAAO;QACL,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,SAAS,EAAE,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC;QACtC,UAAU,EAAE,OAAO,CAAC,UAAU;KAC/B,CAAC;AACJ,CAAC;AAED,IAAI,gBAAgB,GAAG,CAAC,CAAC;AACzB;;;GAGG;AACH,MAAM,UAAU,aAAa;IAC3B,gBAAgB,IAAI,CAAC,CAAC;IACtB,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,gBAAgB,EAAE,CAAC;AACzE,CAAC"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Trust-boundary shape validation for inbound `postMessage` payloads.
3
+ *
4
+ * Origin allowlisting in `IframeTransport.handleMessage` gates *who* can
5
+ * send — these guards gate *what* the message contains. They're shape
6
+ * checks, not schema validation: each guard asserts the fields downstream
7
+ * code will dereference, nothing more. Anything that fails drops with a
8
+ * `console.warn` instead of crashing, so a malformed message degrades the
9
+ * affected feature without breaking the rest of the block.
10
+ *
11
+ * Keep these in lockstep with `BlockInitPayload` / `ParentToBlockMessage`
12
+ * in `@civitai/app-sdk/blocks` AND with the host implementation in
13
+ * civitai/civitai's `src/components/AppBlocks/IframeHost.tsx`. A new field
14
+ * downstream code reads → a new check here.
15
+ */
16
+ import type { BlockInitPayload, BlockWorkflowSnapshot, WrappedToken } from '@civitai/app-sdk/blocks';
17
+ /**
18
+ * Shape check for the wrapped-token shape carried by `BLOCK_INIT.token`,
19
+ * `TOKEN_REFRESH.payload.token`, and `TOKEN_REFRESH_RESPONSE.payload.token`.
20
+ */
21
+ export declare function isValidWrappedToken(t: unknown): t is WrappedToken;
22
+ export declare function isValidBlockInitPayload(p: unknown): p is BlockInitPayload;
23
+ export declare function isValidWorkflowSnapshot(s: unknown): s is BlockWorkflowSnapshot;
24
+ /**
25
+ * Host-pushed token rotation. No `requestId` field; the host is the initiator.
26
+ * Carries the same wrapped-token shape as the reply path.
27
+ */
28
+ export declare function isValidTokenRefresh(p: unknown): p is {
29
+ token: WrappedToken;
30
+ };
31
+ /**
32
+ * Reply to a block-initiated `REQUEST_TOKEN`. `requestId` is optional — the
33
+ * platform's IframeHost.tsx echoes it back when supplied, omits it otherwise.
34
+ */
35
+ export declare function isValidTokenRefreshResponse(p: unknown): p is {
36
+ token: WrappedToken;
37
+ requestId?: string;
38
+ };
39
+ export declare function isValidWorkflowReply(p: unknown): p is {
40
+ snapshot: BlockWorkflowSnapshot;
41
+ requestId?: string;
42
+ };
43
+ export declare function isValidBuzzPurchaseResult(p: unknown): p is {
44
+ purchased: boolean;
45
+ newBalance?: number;
46
+ requestId?: string;
47
+ };
48
+ /**
49
+ * Returns the validator for an inbound message type, or `null` for types
50
+ * that don't carry a payload requiring shape checks (SUSPEND/RESUME).
51
+ *
52
+ * Falsy result from the validator means "drop the message"; `iframeTransport`
53
+ * pairs that with a `console.warn` carrying the type name.
54
+ */
55
+ export declare function payloadValidatorFor(type: string): ((payload: unknown) => boolean) | null;
56
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/internal/validate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EACV,gBAAgB,EAChB,qBAAqB,EACrB,YAAY,EACb,MAAM,yBAAyB,CAAC;AAejC;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,YAAY,CAQjE;AAED,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,gBAAgB,CA6BzE;AAcD,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,qBAAqB,CA4B9E;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,CAAC,EAAE,OAAO,GACT,CAAC,IAAI;IAAE,KAAK,EAAE,YAAY,CAAA;CAAE,CAI9B;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CACzC,CAAC,EAAE,OAAO,GACT,CAAC,IAAI;IAAE,KAAK,EAAE,YAAY,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAKlD;AAED,wBAAgB,oBAAoB,CAClC,CAAC,EAAE,OAAO,GACT,CAAC,IAAI;IAAE,QAAQ,EAAE,qBAAqB,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAK9D;AAED,wBAAgB,yBAAyB,CACvC,CAAC,EAAE,OAAO,GACT,CAAC,IAAI;IAAE,SAAS,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAMtE;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,GACX,CAAC,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,GAAG,IAAI,CAsBxC"}
@@ -0,0 +1,202 @@
1
+ /**
2
+ * Trust-boundary shape validation for inbound `postMessage` payloads.
3
+ *
4
+ * Origin allowlisting in `IframeTransport.handleMessage` gates *who* can
5
+ * send — these guards gate *what* the message contains. They're shape
6
+ * checks, not schema validation: each guard asserts the fields downstream
7
+ * code will dereference, nothing more. Anything that fails drops with a
8
+ * `console.warn` instead of crashing, so a malformed message degrades the
9
+ * affected feature without breaking the rest of the block.
10
+ *
11
+ * Keep these in lockstep with `BlockInitPayload` / `ParentToBlockMessage`
12
+ * in `@civitai/app-sdk/blocks` AND with the host implementation in
13
+ * civitai/civitai's `src/components/AppBlocks/IframeHost.tsx`. A new field
14
+ * downstream code reads → a new check here.
15
+ */
16
+ const isObject = (v) => v !== null && typeof v === 'object';
17
+ const isNonEmptyString = (v) => typeof v === 'string' && v.length > 0;
18
+ /** Accepts an ISO 8601 string that `new Date()` can parse to a finite timestamp. */
19
+ function isParseableDateString(v) {
20
+ if (typeof v !== 'string' || v.length === 0)
21
+ return false;
22
+ const ts = Date.parse(v);
23
+ return Number.isFinite(ts);
24
+ }
25
+ /**
26
+ * Shape check for the wrapped-token shape carried by `BLOCK_INIT.token`,
27
+ * `TOKEN_REFRESH.payload.token`, and `TOKEN_REFRESH_RESPONSE.payload.token`.
28
+ */
29
+ export function isValidWrappedToken(t) {
30
+ if (!isObject(t))
31
+ return false;
32
+ if (!isNonEmptyString(t.raw))
33
+ return false;
34
+ if (!Array.isArray(t.scopes))
35
+ return false;
36
+ if (!t.scopes.every((s) => typeof s === 'string'))
37
+ return false;
38
+ if (!isParseableDateString(t.expiresAt))
39
+ return false;
40
+ if (t.buzzBudget !== undefined && typeof t.buzzBudget !== 'number')
41
+ return false;
42
+ return true;
43
+ }
44
+ export function isValidBlockInitPayload(p) {
45
+ if (!isObject(p))
46
+ return false;
47
+ if (!isNonEmptyString(p.blockId))
48
+ return false;
49
+ if (!isNonEmptyString(p.blockInstanceId))
50
+ return false;
51
+ if (!isNonEmptyString(p.appId))
52
+ return false;
53
+ if (p.renderMode !== 'iframe' && p.renderMode !== 'inline')
54
+ return false;
55
+ if (!isValidWrappedToken(p.token))
56
+ return false;
57
+ if (!isObject(p.context))
58
+ return false;
59
+ if (!isNonEmptyString(p.context.slotId))
60
+ return false;
61
+ if (!isObject(p.settings))
62
+ return false;
63
+ if (!isObject(p.settings.publisherSettings))
64
+ return false;
65
+ if (!isObject(p.settings.userSettings))
66
+ return false;
67
+ // `null` for anonymous viewers; otherwise { id, username, status }.
68
+ if (p.viewer !== null) {
69
+ if (!isObject(p.viewer))
70
+ return false;
71
+ if (typeof p.viewer.id !== 'number')
72
+ return false;
73
+ if (p.viewer.username !== null && typeof p.viewer.username !== 'string')
74
+ return false;
75
+ if (p.viewer.status !== 'active' && p.viewer.status !== 'banned' && p.viewer.status !== 'muted') {
76
+ return false;
77
+ }
78
+ }
79
+ if (p.theme !== 'light' && p.theme !== 'dark')
80
+ return false;
81
+ return true;
82
+ }
83
+ const WORKFLOW_STATUSES = new Set([
84
+ 'pending',
85
+ 'processing',
86
+ 'succeeded',
87
+ 'failed',
88
+ 'expired',
89
+ 'canceled',
90
+ ]);
91
+ const AUTO_CLAIM_TYPES = new Set(['dailyBoost']);
92
+ const AUTO_CLAIM_ACCOUNT_TYPES = new Set(['yellow', 'blue', 'red', 'green']);
93
+ export function isValidWorkflowSnapshot(s) {
94
+ if (!isObject(s))
95
+ return false;
96
+ if (!isNonEmptyString(s.workflowId))
97
+ return false;
98
+ if (typeof s.status !== 'string' || !WORKFLOW_STATUSES.has(s.status))
99
+ return false;
100
+ if (s.cost !== undefined) {
101
+ if (!isObject(s.cost) || typeof s.cost.total !== 'number')
102
+ return false;
103
+ }
104
+ if (s.imageUrls !== undefined) {
105
+ if (!Array.isArray(s.imageUrls))
106
+ return false;
107
+ if (!s.imageUrls.every((u) => typeof u === 'string'))
108
+ return false;
109
+ }
110
+ if (s.error !== undefined && typeof s.error !== 'string')
111
+ return false;
112
+ if (s.autoClaim !== undefined) {
113
+ if (!isObject(s.autoClaim))
114
+ return false;
115
+ if (typeof s.autoClaim.type !== 'string' || !AUTO_CLAIM_TYPES.has(s.autoClaim.type)) {
116
+ return false;
117
+ }
118
+ if (typeof s.autoClaim.amount !== 'number' || !Number.isFinite(s.autoClaim.amount)) {
119
+ return false;
120
+ }
121
+ if (typeof s.autoClaim.accountType !== 'string' ||
122
+ !AUTO_CLAIM_ACCOUNT_TYPES.has(s.autoClaim.accountType)) {
123
+ return false;
124
+ }
125
+ }
126
+ return true;
127
+ }
128
+ /**
129
+ * Host-pushed token rotation. No `requestId` field; the host is the initiator.
130
+ * Carries the same wrapped-token shape as the reply path.
131
+ */
132
+ export function isValidTokenRefresh(p) {
133
+ if (!isObject(p))
134
+ return false;
135
+ if (!isValidWrappedToken(p.token))
136
+ return false;
137
+ return true;
138
+ }
139
+ /**
140
+ * Reply to a block-initiated `REQUEST_TOKEN`. `requestId` is optional — the
141
+ * platform's IframeHost.tsx echoes it back when supplied, omits it otherwise.
142
+ */
143
+ export function isValidTokenRefreshResponse(p) {
144
+ if (!isObject(p))
145
+ return false;
146
+ if (!isValidWrappedToken(p.token))
147
+ return false;
148
+ if (p.requestId !== undefined && typeof p.requestId !== 'string')
149
+ return false;
150
+ return true;
151
+ }
152
+ export function isValidWorkflowReply(p) {
153
+ if (!isObject(p))
154
+ return false;
155
+ if (!isValidWorkflowSnapshot(p.snapshot))
156
+ return false;
157
+ if (p.requestId !== undefined && typeof p.requestId !== 'string')
158
+ return false;
159
+ return true;
160
+ }
161
+ export function isValidBuzzPurchaseResult(p) {
162
+ if (!isObject(p))
163
+ return false;
164
+ if (typeof p.purchased !== 'boolean')
165
+ return false;
166
+ if (p.newBalance !== undefined && typeof p.newBalance !== 'number')
167
+ return false;
168
+ if (p.requestId !== undefined && typeof p.requestId !== 'string')
169
+ return false;
170
+ return true;
171
+ }
172
+ /**
173
+ * Returns the validator for an inbound message type, or `null` for types
174
+ * that don't carry a payload requiring shape checks (SUSPEND/RESUME).
175
+ *
176
+ * Falsy result from the validator means "drop the message"; `iframeTransport`
177
+ * pairs that with a `console.warn` carrying the type name.
178
+ */
179
+ export function payloadValidatorFor(type) {
180
+ switch (type) {
181
+ case 'BLOCK_INIT':
182
+ return isValidBlockInitPayload;
183
+ case 'TOKEN_REFRESH':
184
+ return isValidTokenRefresh;
185
+ case 'TOKEN_REFRESH_RESPONSE':
186
+ return isValidTokenRefreshResponse;
187
+ case 'ESTIMATE_RESULT':
188
+ case 'WORKFLOW_SUBMITTED':
189
+ case 'WORKFLOW_STATUS':
190
+ return isValidWorkflowReply;
191
+ case 'BUZZ_PURCHASE_RESULT':
192
+ return isValidBuzzPurchaseResult;
193
+ case 'SUSPEND':
194
+ case 'RESUME':
195
+ return null;
196
+ default:
197
+ // Unknown type names get a structural pass; handleMessage's earlier
198
+ // `isMessage` branches won't match them anyway.
199
+ return null;
200
+ }
201
+ }
202
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/internal/validate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAQH,MAAM,QAAQ,GAAG,CAAC,CAAU,EAAgC,EAAE,CAC5D,CAAC,KAAK,IAAI,IAAI,OAAO,CAAC,KAAK,QAAQ,CAAC;AAEtC,MAAM,gBAAgB,GAAG,CAAC,CAAU,EAAe,EAAE,CACnD,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;AAExC,oFAAoF;AACpF,SAAS,qBAAqB,CAAC,CAAU;IACvC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1D,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACzB,OAAO,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AAC7B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,CAAU;IAC5C,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IAC/B,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAC3C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IAC3C,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7E,IAAI,CAAC,qBAAqB,CAAC,CAAC,CAAC,SAAS,CAAC;QAAE,OAAO,KAAK,CAAC;IACtD,IAAI,CAAC,CAAC,UAAU,KAAK,SAAS,IAAI,OAAO,CAAC,CAAC,UAAU,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACjF,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,CAAU;IAChD,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IAC/B,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAC;IAC/C,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,eAAe,CAAC;QAAE,OAAO,KAAK,CAAC;IACvD,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7C,IAAI,CAAC,CAAC,UAAU,KAAK,QAAQ,IAAI,CAAC,CAAC,UAAU,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAEzE,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAEhD,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAC;IACvC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IAEtD,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;QAAE,OAAO,KAAK,CAAC;IACxC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,iBAAiB,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1D,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC;QAAE,OAAO,KAAK,CAAC;IAErD,oEAAoE;IACpE,IAAI,CAAC,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;QACtB,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC;YAAE,OAAO,KAAK,CAAC;QACtC,IAAI,OAAO,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAC;QAClD,IAAI,CAAC,CAAC,MAAM,CAAC,QAAQ,KAAK,IAAI,IAAI,OAAO,CAAC,CAAC,MAAM,CAAC,QAAQ,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAC;QACtF,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;YAChG,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,IAAI,CAAC,CAAC,KAAK,KAAK,OAAO,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM;QAAE,OAAO,KAAK,CAAC;IAE5D,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAS;IACxC,SAAS;IACT,YAAY;IACZ,WAAW;IACX,QAAQ;IACR,SAAS;IACT,UAAU;CACX,CAAC,CAAC;AAEH,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAS,CAAC,YAAY,CAAC,CAAC,CAAC;AACzD,MAAM,wBAAwB,GAAG,IAAI,GAAG,CAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;AAErF,MAAM,UAAU,uBAAuB,CAAC,CAAU;IAChD,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IAC/B,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,UAAU,CAAC;QAAE,OAAO,KAAK,CAAC;IAClD,IAAI,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IACnF,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QACzB,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAC;IAC1E,CAAC;IACD,IAAI,CAAC,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QAC9B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;YAAE,OAAO,KAAK,CAAC;QAC9C,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC;YAAE,OAAO,KAAK,CAAC;IAClF,CAAC;IACD,IAAI,CAAC,CAAC,KAAK,KAAK,SAAS,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACvE,IAAI,CAAC,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QAC9B,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;YAAE,OAAO,KAAK,CAAC;QACzC,IAAI,OAAO,CAAC,CAAC,SAAS,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;YACpF,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,OAAO,CAAC,CAAC,SAAS,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;YACnF,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IACE,OAAO,CAAC,CAAC,SAAS,CAAC,WAAW,KAAK,QAAQ;YAC3C,CAAC,wBAAwB,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,EACtD,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CACjC,CAAU;IAEV,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IAC/B,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAChD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,2BAA2B,CACzC,CAAU;IAEV,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IAC/B,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAChD,IAAI,CAAC,CAAC,SAAS,KAAK,SAAS,IAAI,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC/E,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,CAAU;IAEV,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IAC/B,IAAI,CAAC,uBAAuB,CAAC,CAAC,CAAC,QAAQ,CAAC;QAAE,OAAO,KAAK,CAAC;IACvD,IAAI,CAAC,CAAC,SAAS,KAAK,SAAS,IAAI,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC/E,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,yBAAyB,CACvC,CAAU;IAEV,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IAC/B,IAAI,OAAO,CAAC,CAAC,SAAS,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACnD,IAAI,CAAC,CAAC,UAAU,KAAK,SAAS,IAAI,OAAO,CAAC,CAAC,UAAU,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACjF,IAAI,CAAC,CAAC,SAAS,KAAK,SAAS,IAAI,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC/E,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CACjC,IAAY;IAEZ,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,YAAY;YACf,OAAO,uBAAuB,CAAC;QACjC,KAAK,eAAe;YAClB,OAAO,mBAAmB,CAAC;QAC7B,KAAK,wBAAwB;YAC3B,OAAO,2BAA2B,CAAC;QACrC,KAAK,iBAAiB,CAAC;QACvB,KAAK,oBAAoB,CAAC;QAC1B,KAAK,iBAAiB;YACpB,OAAO,oBAAoB,CAAC;QAC9B,KAAK,sBAAsB;YACzB,OAAO,yBAAyB,CAAC;QACnC,KAAK,SAAS,CAAC;QACf,KAAK,QAAQ;YACX,OAAO,IAAI,CAAC;QACd;YACE,oEAAoE;YACpE,gDAAgD;YAChD,OAAO,IAAI,CAAC;IAChB,CAAC;AACH,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Test-only helpers for `@civitai/blocks-react`. Not part of the runtime
3
+ * surface — block apps should never import from here.
4
+ *
5
+ * Subpath-exported so accidental production imports show up in code review.
6
+ */
7
+ import { __resetTransport } from './internal/singleton.js';
8
+ export { __resetTransport as resetTransport };
9
+ /**
10
+ * Builds a `MessageEvent` that mimics a parent-frame postMessage so tests can
11
+ * exercise `IframeTransport.handleMessage` without a real cross-frame setup.
12
+ */
13
+ export declare function mockParentMessage(data: unknown, origin: string): MessageEvent;
14
+ //# sourceMappingURL=testing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3D,OAAO,EAAE,gBAAgB,IAAI,cAAc,EAAE,CAAC;AAE9C;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,OAAO,EACb,MAAM,EAAE,MAAM,GACb,YAAY,CAEd"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Test-only helpers for `@civitai/blocks-react`. Not part of the runtime
3
+ * surface — block apps should never import from here.
4
+ *
5
+ * Subpath-exported so accidental production imports show up in code review.
6
+ */
7
+ import { __resetTransport } from './internal/singleton.js';
8
+ export { __resetTransport as resetTransport };
9
+ /**
10
+ * Builds a `MessageEvent` that mimics a parent-frame postMessage so tests can
11
+ * exercise `IframeTransport.handleMessage` without a real cross-frame setup.
12
+ */
13
+ export function mockParentMessage(data, origin) {
14
+ return new MessageEvent('message', { data, origin, source: null });
15
+ }
16
+ //# sourceMappingURL=testing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3D,OAAO,EAAE,gBAAgB,IAAI,cAAc,EAAE,CAAC;AAE9C;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAC/B,IAAa,EACb,MAAc;IAEd,OAAO,IAAI,YAAY,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;AACrE,CAAC"}
@@ -0,0 +1,79 @@
1
+ import type { ManifestSettings, ManifestSettingField, SettingScope } from '@civitai/app-sdk/blocks';
2
+ /**
3
+ * W3 v0 — generic, manifest-driven settings form.
4
+ *
5
+ * Renders one of seven primitive inputs per field based on the manifest's
6
+ * widget hint, filters by `forScope` + `requires_scope`, and posts the
7
+ * collected values back via `onSubmit`. Designed to be used from:
8
+ * 1. Platform-side `/apps/installed` settings modal (publisher slice).
9
+ * 2. Same page (viewer slice).
10
+ * 3. Per-app settings page (`/apps/[appBlockId]/settings`).
11
+ * 4. Model edit page banner (publisher slice).
12
+ *
13
+ * Intentionally headless-styled. No design tokens, no Mantine, no CSS
14
+ * imports — the host page applies CSS via wrapper / className. The W6
15
+ * component pack (when it lands) will own theming for `<Button>` etc.;
16
+ * until then this form ships unstyled native controls so the platform
17
+ * pages can theme them inline.
18
+ *
19
+ * Validation note: the form does client-side type / range checks only
20
+ * (NaN, out-of-bounds). Server-side `validateBlockSettings` is the
21
+ * authoritative gate; this layer just prevents obvious typos before the
22
+ * round trip. Server errors surface inline via `onSubmit` rejecting with
23
+ * `{ fieldErrors }`.
24
+ */
25
+ export interface SettingsFormProps {
26
+ /** App's manifest.settings declaration (already validated server-side). */
27
+ manifestSettings: ManifestSettings;
28
+ /** App's declared scopes — drives `requires_scope` field gating. */
29
+ declaredScopes: string[];
30
+ /** Which slice of fields to render. */
31
+ forScope: SettingScope;
32
+ /**
33
+ * Initial values keyed by field name. Missing keys fall back to the
34
+ * manifest field's `default`. Unknown keys are ignored.
35
+ */
36
+ initialValues: Record<string, unknown>;
37
+ /**
38
+ * Persist the form's collected values. May throw a SettingsFormError
39
+ * with per-field messages — the form surfaces them inline.
40
+ */
41
+ onSubmit: (values: Record<string, unknown>) => Promise<void>;
42
+ /**
43
+ * Resource picker integration. Required when the manifest declares any
44
+ * `widget: 'resource_picker'` field. Pass the host-mediated picker —
45
+ * typically a thin wrapper around `useCheckpointPicker()` for the
46
+ * Checkpoint case, or a future `useResourcePicker()` hook.
47
+ *
48
+ * The callback receives the field's `widget_options` (e.g.
49
+ * `{ resource_type: 'Checkpoint', filter_by_ecosystem: true }`) plus
50
+ * the current value, and returns the picked resource's id (or null if
51
+ * the user dismissed without picking).
52
+ */
53
+ resourcePicker?: (opts: {
54
+ fieldKey: string;
55
+ widgetOptions: Record<string, unknown>;
56
+ currentValue: number | null;
57
+ }) => Promise<number | null>;
58
+ /** Button label. Defaults to 'Save'. */
59
+ submitLabel?: string;
60
+ /** Optional className applied to the form root for the host to style. */
61
+ className?: string;
62
+ }
63
+ /**
64
+ * Thrown by `onSubmit` callers to surface per-field server errors back to
65
+ * the form. The form renders the message under the named field.
66
+ */
67
+ export declare class SettingsFormError extends Error {
68
+ readonly fieldErrors: Record<string, string>;
69
+ readonly name = "SettingsFormError";
70
+ constructor(fieldErrors: Record<string, string>);
71
+ }
72
+ /**
73
+ * Test if a field should render for the current scope + declared-scopes
74
+ * combination. Exposed for callers that want to count visible fields
75
+ * before rendering (e.g. "no settings to configure" empty state).
76
+ */
77
+ export declare function isFieldVisible(field: ManifestSettingField, forScope: SettingScope, declaredScopes: string[]): boolean;
78
+ export declare function SettingsForm(props: SettingsFormProps): React.JSX.Element;
79
+ //# sourceMappingURL=SettingsForm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SettingsForm.d.ts","sourceRoot":"","sources":["../../src/ui/SettingsForm.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,gBAAgB,EAChB,oBAAoB,EAIpB,YAAY,EACb,MAAM,yBAAyB,CAAC;AAEjC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,MAAM,WAAW,iBAAiB;IAChC,2EAA2E;IAC3E,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,oEAAoE;IACpE,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,uCAAuC;IACvC,QAAQ,EAAE,YAAY,CAAC;IACvB;;;OAGG;IACH,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC;;;OAGG;IACH,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D;;;;;;;;;;OAUG;IACH,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE;QACtB,QAAQ,EAAE,MAAM,CAAC;QACjB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACvC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;KAC7B,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7B,wCAAwC;IACxC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;aAEd,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAD/D,SAAkB,IAAI,uBAAuB;gBACjB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;CAGhE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,oBAAoB,EAC3B,QAAQ,EAAE,YAAY,EACtB,cAAc,EAAE,MAAM,EAAE,GACvB,OAAO,CAIT;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAqHxE"}