@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.
- package/README.md +48 -0
- package/dist/hooks/useAppStorage.d.ts +75 -0
- package/dist/hooks/useAppStorage.d.ts.map +1 -0
- package/dist/hooks/useAppStorage.js +73 -0
- package/dist/hooks/useAppStorage.js.map +1 -0
- package/dist/hooks/useBlockAnalytics.d.ts +9 -0
- package/dist/hooks/useBlockAnalytics.d.ts.map +1 -0
- package/dist/hooks/useBlockAnalytics.js +14 -0
- package/dist/hooks/useBlockAnalytics.js.map +1 -0
- package/dist/hooks/useBlockContext.d.ts +18 -0
- package/dist/hooks/useBlockContext.d.ts.map +1 -0
- package/dist/hooks/useBlockContext.js +39 -0
- package/dist/hooks/useBlockContext.js.map +1 -0
- package/dist/hooks/useBlockResize.d.ts +13 -0
- package/dist/hooks/useBlockResize.d.ts.map +1 -0
- package/dist/hooks/useBlockResize.js +33 -0
- package/dist/hooks/useBlockResize.js.map +1 -0
- package/dist/hooks/useBlockSettings.d.ts +7 -0
- package/dist/hooks/useBlockSettings.d.ts.map +1 -0
- package/dist/hooks/useBlockSettings.js +9 -0
- package/dist/hooks/useBlockSettings.js.map +1 -0
- package/dist/hooks/useBlockToken.d.ts +16 -0
- package/dist/hooks/useBlockToken.d.ts.map +1 -0
- package/dist/hooks/useBlockToken.js +61 -0
- package/dist/hooks/useBlockToken.js.map +1 -0
- package/dist/hooks/useBuzzPurchase.d.ts +12 -0
- package/dist/hooks/useBuzzPurchase.d.ts.map +1 -0
- package/dist/hooks/useBuzzPurchase.js +16 -0
- package/dist/hooks/useBuzzPurchase.js.map +1 -0
- package/dist/hooks/useBuzzWorkflow.d.ts +21 -0
- package/dist/hooks/useBuzzWorkflow.d.ts.map +1 -0
- package/dist/hooks/useBuzzWorkflow.js +76 -0
- package/dist/hooks/useBuzzWorkflow.js.map +1 -0
- package/dist/hooks/useCheckpointPicker.d.ts +34 -0
- package/dist/hooks/useCheckpointPicker.d.ts.map +1 -0
- package/dist/hooks/useCheckpointPicker.js +41 -0
- package/dist/hooks/useCheckpointPicker.js.map +1 -0
- package/dist/hooks/useCivitaiNavigate.d.ts +11 -0
- package/dist/hooks/useCivitaiNavigate.d.ts.map +1 -0
- package/dist/hooks/useCivitaiNavigate.js +16 -0
- package/dist/hooks/useCivitaiNavigate.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/detector.d.ts +31 -0
- package/dist/internal/detector.d.ts.map +1 -0
- package/dist/internal/detector.js +67 -0
- package/dist/internal/detector.js.map +1 -0
- package/dist/internal/iframeTransport.d.ts +55 -0
- package/dist/internal/iframeTransport.d.ts.map +1 -0
- package/dist/internal/iframeTransport.js +200 -0
- package/dist/internal/iframeTransport.js.map +1 -0
- package/dist/internal/inlineTransport.d.ts +18 -0
- package/dist/internal/inlineTransport.d.ts.map +1 -0
- package/dist/internal/inlineTransport.js +36 -0
- package/dist/internal/inlineTransport.js.map +1 -0
- package/dist/internal/singleton.d.ts +18 -0
- package/dist/internal/singleton.d.ts.map +1 -0
- package/dist/internal/singleton.js +28 -0
- package/dist/internal/singleton.js.map +1 -0
- package/dist/internal/transport.d.ts +100 -0
- package/dist/internal/transport.d.ts.map +1 -0
- package/dist/internal/transport.js +67 -0
- package/dist/internal/transport.js.map +1 -0
- package/dist/internal/validate.d.ts +56 -0
- package/dist/internal/validate.d.ts.map +1 -0
- package/dist/internal/validate.js +202 -0
- package/dist/internal/validate.js.map +1 -0
- package/dist/testing.d.ts +14 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +16 -0
- package/dist/testing.js.map +1 -0
- package/dist/ui/SettingsForm.d.ts +79 -0
- package/dist/ui/SettingsForm.d.ts.map +1 -0
- package/dist/ui/SettingsForm.js +199 -0
- package/dist/ui/SettingsForm.js.map +1 -0
- package/dist/ui/index.d.ts +11 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +10 -0
- package/dist/ui/index.js.map +1 -0
- 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":"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"}
|
|
@@ -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"}
|
package/dist/testing.js
ADDED
|
@@ -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"}
|