@civitai/blocks-react 0.12.4 → 0.13.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/dist/internal/catalog.d.ts +190 -0
- package/dist/internal/catalog.d.ts.map +1 -0
- package/dist/internal/catalog.js +283 -0
- package/dist/internal/catalog.js.map +1 -0
- package/dist/internal/liveHost.d.ts +22 -2
- package/dist/internal/liveHost.d.ts.map +1 -1
- package/dist/internal/liveHost.js +82 -13
- package/dist/internal/liveHost.js.map +1 -1
- package/dist/internal/pickerOverlay.d.ts +90 -0
- package/dist/internal/pickerOverlay.d.ts.map +1 -0
- package/dist/internal/pickerOverlay.js +344 -0
- package/dist/internal/pickerOverlay.js.map +1 -0
- package/dist/testing.d.ts +2 -0
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +2 -0
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SDK-internal catalog client for the LIVE host's in-harness resource picker
|
|
3
|
+
* (Phase 1 of "make `dev:live` a faithful local host").
|
|
4
|
+
*
|
|
5
|
+
* The real platform host opens civitai's OWN model-picker modal when a block
|
|
6
|
+
* sends `OPEN_CHECKPOINT_PICKER` / `OPEN_RESOURCE_PICKER`. Locally, `dev:live`
|
|
7
|
+
* has no civitai chrome — so {@link createLiveHost} serves a PROTOCOL-IDENTICAL
|
|
8
|
+
* equivalent: a tiny in-harness catalog browser backed by this client. The
|
|
9
|
+
* block code (`useCheckpointPicker` / `useResourcePicker`) is byte-identical to
|
|
10
|
+
* production; only the picker chrome differs.
|
|
11
|
+
*
|
|
12
|
+
* TWO READ PATHS (mirrors the reference block-side catalog client), chosen by
|
|
13
|
+
* whether a block token is present:
|
|
14
|
+
*
|
|
15
|
+
* 1. AUTHORITATIVE (the live host always has the dev block token): the
|
|
16
|
+
* block-gated endpoint `/api/v1/blocks/models` with
|
|
17
|
+
* `Authorization: Bearer <token>` (civitai PR #2671). Same response shape as
|
|
18
|
+
* the public `/api/v1/models` but AUTHORITATIVELY CLAMPS the catalog's
|
|
19
|
+
* browsing level to the token's signed `maxBrowsingLevel` ceiling. The
|
|
20
|
+
* client NEVER sends `nsfw`/`browsingLevel` — maturity is server-enforced.
|
|
21
|
+
* The page block token CAN read `/blocks/models` (no extra scope).
|
|
22
|
+
*
|
|
23
|
+
* 2. PUBLIC (graceful fallback): `/api/v1/models` is a MixedAuthEndpoint
|
|
24
|
+
* returning `Access-Control-Allow-Origin: *`. We pass `nsfw=false` for an
|
|
25
|
+
* advisory-SFW view. Used only when the authoritative path isn't deployed /
|
|
26
|
+
* authorized yet (404/401/403) or throws (CORS/network).
|
|
27
|
+
*
|
|
28
|
+
* MONEY-SAFETY: a pick from this catalog is DISCOVERY ONLY. The returned
|
|
29
|
+
* `versionId` is a HINT, never an entitlement — the server re-validates AND
|
|
30
|
+
* prices every id at estimate/submit (the page gate + orchestrator belt). A
|
|
31
|
+
* browse here, authoritative or public, never moves Buzz and never implies the
|
|
32
|
+
* pick is trusted.
|
|
33
|
+
*
|
|
34
|
+
* Everything here is pure + node-testable: the URL/param builders, the
|
|
35
|
+
* response→option mappers (`BlockCheckpointInfo` / `BlockResourceInfo`), and a
|
|
36
|
+
* thin fetch client that takes an injectable `fetch` (tests drive success /
|
|
37
|
+
* empty / non-OK / throw / fallback without a real network).
|
|
38
|
+
*/
|
|
39
|
+
import type { BlockCheckpointInfo, BlockResourceInfo } from '@civitai/app-sdk/blocks';
|
|
40
|
+
/** The public REST base. */
|
|
41
|
+
export declare const CATALOG_API_BASE = "/api/v1/models";
|
|
42
|
+
/** The authoritative, block-token-gated, maturity-clamped catalog base (#2671). */
|
|
43
|
+
export declare const CATALOG_API_BASE_BLOCKS = "/api/v1/blocks/models";
|
|
44
|
+
/** The REST `types` filter value for each picker model type. */
|
|
45
|
+
export type CatalogModelType = 'Checkpoint' | 'LORA';
|
|
46
|
+
/** Default page size — small enough to keep the grid + payload light. */
|
|
47
|
+
export declare const DEFAULT_LIMIT = 24;
|
|
48
|
+
/** Default thumbnail width requested from the image edge (cache-aligned). */
|
|
49
|
+
export declare const THUMB_WIDTH = 320;
|
|
50
|
+
export interface CatalogQuery {
|
|
51
|
+
/** Which model type to browse — maps directly to the REST `types` param. */
|
|
52
|
+
types: CatalogModelType;
|
|
53
|
+
/** Meili text search; empty/whitespace → omitted (default popular view). */
|
|
54
|
+
query?: string;
|
|
55
|
+
/** ≤100 (server cap). Defaults to {@link DEFAULT_LIMIT}. */
|
|
56
|
+
limit?: number;
|
|
57
|
+
cursor?: string;
|
|
58
|
+
}
|
|
59
|
+
export interface BuildUrlOptions {
|
|
60
|
+
base?: string;
|
|
61
|
+
/**
|
|
62
|
+
* Append `nsfw=false` (advisory SFW). Only the PUBLIC endpoint reads this; the
|
|
63
|
+
* authoritative `/blocks/models` IGNORES client maturity (server clamps), so we
|
|
64
|
+
* never send it there. Defaults to `false`.
|
|
65
|
+
*/
|
|
66
|
+
sfwOnly?: boolean;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Build a catalog URL for a query. Pure + deterministic so the request is
|
|
70
|
+
* stable. Empty query is omitted (the default popular view). `limit` is clamped
|
|
71
|
+
* to [1, 100]. `sfwOnly` appends `nsfw=false` — PUBLIC endpoint only.
|
|
72
|
+
*
|
|
73
|
+
* `baseOrOpts` may be a bare base string (back-compat) or a {@link BuildUrlOptions}.
|
|
74
|
+
*/
|
|
75
|
+
export declare function buildCatalogUrl(q: CatalogQuery, baseOrOpts?: string | BuildUrlOptions): string;
|
|
76
|
+
interface RawImage {
|
|
77
|
+
url?: string;
|
|
78
|
+
}
|
|
79
|
+
interface RawModelVersion {
|
|
80
|
+
id?: number;
|
|
81
|
+
name?: string;
|
|
82
|
+
baseModel?: string;
|
|
83
|
+
images?: RawImage[];
|
|
84
|
+
}
|
|
85
|
+
interface RawModel {
|
|
86
|
+
id?: number;
|
|
87
|
+
name?: string;
|
|
88
|
+
type?: string;
|
|
89
|
+
nsfw?: boolean;
|
|
90
|
+
modelVersions?: RawModelVersion[];
|
|
91
|
+
}
|
|
92
|
+
interface RawResponse {
|
|
93
|
+
items?: RawModel[];
|
|
94
|
+
metadata?: {
|
|
95
|
+
nextCursor?: string | null;
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* A normalized card the in-harness grid renders. One per model (its top
|
|
100
|
+
* version). Carries everything {@link cardToCheckpoint} / {@link cardToResource}
|
|
101
|
+
* need to build the protocol-identical picker reply payload.
|
|
102
|
+
*/
|
|
103
|
+
export interface CatalogCard {
|
|
104
|
+
modelId: number;
|
|
105
|
+
versionId: number;
|
|
106
|
+
modelName: string;
|
|
107
|
+
versionName: string;
|
|
108
|
+
baseModel: string;
|
|
109
|
+
modelType: string;
|
|
110
|
+
thumbnailUrl: string | null;
|
|
111
|
+
nsfw: boolean;
|
|
112
|
+
}
|
|
113
|
+
export interface CatalogPage {
|
|
114
|
+
cards: CatalogCard[];
|
|
115
|
+
nextCursor: string | null;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Rewrite a Civitai image edge URL to a small, optimized thumbnail so we hit the
|
|
119
|
+
* CDN-cached variant instead of an origin transcode. Handles `original=true`,
|
|
120
|
+
* `width=NNN`, or no transform segment. Pure + total — never throws.
|
|
121
|
+
*/
|
|
122
|
+
export declare function edgeThumb(url: string, width?: number): string;
|
|
123
|
+
/**
|
|
124
|
+
* Map ONE raw model → a card using its FIRST modelVersion (REST default order
|
|
125
|
+
* puts the latest/primary first) and that version's first image. Returns `null`
|
|
126
|
+
* for an unusable row (no version id). Tolerant of any missing field.
|
|
127
|
+
*/
|
|
128
|
+
export declare function modelToCard(raw: RawModel | null | undefined, imageWidth?: number): CatalogCard | null;
|
|
129
|
+
/** Map a full raw response → a page of cards + the next cursor. Never throws. */
|
|
130
|
+
export declare function responseToPage(raw: RawResponse | null | undefined, imageWidth?: number): CatalogPage;
|
|
131
|
+
/**
|
|
132
|
+
* Map a browsed card → the EXACT `BlockCheckpointInfo` shape the production host
|
|
133
|
+
* returns in `CHECKPOINT_PICKER_RESULT.selected`. Protocol fidelity: the block's
|
|
134
|
+
* `useCheckpointPicker()` consumes this identically in dev:live and in prod.
|
|
135
|
+
*/
|
|
136
|
+
export declare function cardToCheckpoint(card: CatalogCard): BlockCheckpointInfo;
|
|
137
|
+
/**
|
|
138
|
+
* Map a browsed card → the EXACT `BlockResourceInfo` shape the production host
|
|
139
|
+
* returns in `RESOURCE_PICKER_RESULT.selected`. `modelType` is the canonical
|
|
140
|
+
* type the host resolved (e.g. 'Checkpoint' | 'LORA').
|
|
141
|
+
*/
|
|
142
|
+
export declare function cardToResource(card: CatalogCard, modelType: string): BlockResourceInfo;
|
|
143
|
+
/**
|
|
144
|
+
* Client-side family hint: keep only cards whose `baseModel` plausibly matches
|
|
145
|
+
* the requested `baseModelGroup` (ecosystem key like 'Flux1'/'SDXL' OR a
|
|
146
|
+
* baseModel name like 'Flux.1 D'). Loose substring/token match — the REAL family
|
|
147
|
+
* constraint is server-enforced at submit; this is purely a dev-UX convenience
|
|
148
|
+
* that NARROWS the browse, never widens it. An empty/absent group returns the
|
|
149
|
+
* cards unchanged.
|
|
150
|
+
*/
|
|
151
|
+
export declare function filterCardsByFamily(cards: CatalogCard[], baseModelGroup: string | undefined): CatalogCard[];
|
|
152
|
+
export type CatalogResult = {
|
|
153
|
+
kind: 'ok';
|
|
154
|
+
page: CatalogPage;
|
|
155
|
+
authoritative: boolean;
|
|
156
|
+
} | {
|
|
157
|
+
kind: 'empty';
|
|
158
|
+
authoritative: boolean;
|
|
159
|
+
} | {
|
|
160
|
+
kind: 'error';
|
|
161
|
+
status?: number;
|
|
162
|
+
message: string;
|
|
163
|
+
};
|
|
164
|
+
export type FallbackInfo = {
|
|
165
|
+
status: number;
|
|
166
|
+
} | {
|
|
167
|
+
reason: 'network';
|
|
168
|
+
};
|
|
169
|
+
export interface FetchCatalogDeps {
|
|
170
|
+
/** Injectable `fetch` — the live host passes its own `fetchImpl`. */
|
|
171
|
+
fetch: typeof fetch;
|
|
172
|
+
/** Backend origin (e.g. `https://civitai.com`) the relative bases resolve against. */
|
|
173
|
+
baseUrl: string;
|
|
174
|
+
/** Raw block JWT. Present → authoritative path with fallback. Absent → public. */
|
|
175
|
+
token?: string | null;
|
|
176
|
+
imageWidth?: number;
|
|
177
|
+
/** Advisory SFW for the PUBLIC read. Defaults to true (fail-closed SFW). */
|
|
178
|
+
anonSfwOnly?: boolean;
|
|
179
|
+
onFallback?: (info: FallbackInfo) => void;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Fetch one catalog page, choosing the authoritative (token, maturity-clamped)
|
|
183
|
+
* or public (advisory-SFW) endpoint and gracefully falling back from the former
|
|
184
|
+
* to the latter when it isn't deployed / authorized yet (404/401/403) or throws
|
|
185
|
+
* (CORS/preflight/network). A failure on the PUBLIC path is a hard error (no
|
|
186
|
+
* infinite loop). Never throws.
|
|
187
|
+
*/
|
|
188
|
+
export declare function fetchCatalog(q: CatalogQuery, deps: FetchCatalogDeps): Promise<CatalogResult>;
|
|
189
|
+
export {};
|
|
190
|
+
//# sourceMappingURL=catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../src/internal/catalog.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SDK-internal catalog client for the LIVE host's in-harness resource picker
|
|
3
|
+
* (Phase 1 of "make `dev:live` a faithful local host").
|
|
4
|
+
*
|
|
5
|
+
* The real platform host opens civitai's OWN model-picker modal when a block
|
|
6
|
+
* sends `OPEN_CHECKPOINT_PICKER` / `OPEN_RESOURCE_PICKER`. Locally, `dev:live`
|
|
7
|
+
* has no civitai chrome — so {@link createLiveHost} serves a PROTOCOL-IDENTICAL
|
|
8
|
+
* equivalent: a tiny in-harness catalog browser backed by this client. The
|
|
9
|
+
* block code (`useCheckpointPicker` / `useResourcePicker`) is byte-identical to
|
|
10
|
+
* production; only the picker chrome differs.
|
|
11
|
+
*
|
|
12
|
+
* TWO READ PATHS (mirrors the reference block-side catalog client), chosen by
|
|
13
|
+
* whether a block token is present:
|
|
14
|
+
*
|
|
15
|
+
* 1. AUTHORITATIVE (the live host always has the dev block token): the
|
|
16
|
+
* block-gated endpoint `/api/v1/blocks/models` with
|
|
17
|
+
* `Authorization: Bearer <token>` (civitai PR #2671). Same response shape as
|
|
18
|
+
* the public `/api/v1/models` but AUTHORITATIVELY CLAMPS the catalog's
|
|
19
|
+
* browsing level to the token's signed `maxBrowsingLevel` ceiling. The
|
|
20
|
+
* client NEVER sends `nsfw`/`browsingLevel` — maturity is server-enforced.
|
|
21
|
+
* The page block token CAN read `/blocks/models` (no extra scope).
|
|
22
|
+
*
|
|
23
|
+
* 2. PUBLIC (graceful fallback): `/api/v1/models` is a MixedAuthEndpoint
|
|
24
|
+
* returning `Access-Control-Allow-Origin: *`. We pass `nsfw=false` for an
|
|
25
|
+
* advisory-SFW view. Used only when the authoritative path isn't deployed /
|
|
26
|
+
* authorized yet (404/401/403) or throws (CORS/network).
|
|
27
|
+
*
|
|
28
|
+
* MONEY-SAFETY: a pick from this catalog is DISCOVERY ONLY. The returned
|
|
29
|
+
* `versionId` is a HINT, never an entitlement — the server re-validates AND
|
|
30
|
+
* prices every id at estimate/submit (the page gate + orchestrator belt). A
|
|
31
|
+
* browse here, authoritative or public, never moves Buzz and never implies the
|
|
32
|
+
* pick is trusted.
|
|
33
|
+
*
|
|
34
|
+
* Everything here is pure + node-testable: the URL/param builders, the
|
|
35
|
+
* response→option mappers (`BlockCheckpointInfo` / `BlockResourceInfo`), and a
|
|
36
|
+
* thin fetch client that takes an injectable `fetch` (tests drive success /
|
|
37
|
+
* empty / non-OK / throw / fallback without a real network).
|
|
38
|
+
*/
|
|
39
|
+
/** The public REST base. */
|
|
40
|
+
export const CATALOG_API_BASE = '/api/v1/models';
|
|
41
|
+
/** The authoritative, block-token-gated, maturity-clamped catalog base (#2671). */
|
|
42
|
+
export const CATALOG_API_BASE_BLOCKS = '/api/v1/blocks/models';
|
|
43
|
+
/** Default page size — small enough to keep the grid + payload light. */
|
|
44
|
+
export const DEFAULT_LIMIT = 24;
|
|
45
|
+
/** Default thumbnail width requested from the image edge (cache-aligned). */
|
|
46
|
+
export const THUMB_WIDTH = 320;
|
|
47
|
+
function clampLimit(n) {
|
|
48
|
+
if (!Number.isFinite(n))
|
|
49
|
+
return DEFAULT_LIMIT;
|
|
50
|
+
return Math.max(1, Math.min(100, Math.floor(n)));
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Build a catalog URL for a query. Pure + deterministic so the request is
|
|
54
|
+
* stable. Empty query is omitted (the default popular view). `limit` is clamped
|
|
55
|
+
* to [1, 100]. `sfwOnly` appends `nsfw=false` — PUBLIC endpoint only.
|
|
56
|
+
*
|
|
57
|
+
* `baseOrOpts` may be a bare base string (back-compat) or a {@link BuildUrlOptions}.
|
|
58
|
+
*/
|
|
59
|
+
export function buildCatalogUrl(q, baseOrOpts = CATALOG_API_BASE) {
|
|
60
|
+
const opts = typeof baseOrOpts === 'string' ? { base: baseOrOpts } : baseOrOpts;
|
|
61
|
+
const base = opts.base ?? CATALOG_API_BASE;
|
|
62
|
+
const params = new URLSearchParams();
|
|
63
|
+
params.set('types', q.types);
|
|
64
|
+
const query = q.query?.trim();
|
|
65
|
+
if (query)
|
|
66
|
+
params.set('query', query);
|
|
67
|
+
params.set('limit', String(clampLimit(q.limit ?? DEFAULT_LIMIT)));
|
|
68
|
+
if (q.cursor)
|
|
69
|
+
params.set('cursor', q.cursor);
|
|
70
|
+
if (opts.sfwOnly)
|
|
71
|
+
params.set('nsfw', 'false');
|
|
72
|
+
return `${base}?${params.toString()}`;
|
|
73
|
+
}
|
|
74
|
+
function thumbTransformSegment(width) {
|
|
75
|
+
return `width=${width},optimized=true`;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Rewrite a Civitai image edge URL to a small, optimized thumbnail so we hit the
|
|
79
|
+
* CDN-cached variant instead of an origin transcode. Handles `original=true`,
|
|
80
|
+
* `width=NNN`, or no transform segment. Pure + total — never throws.
|
|
81
|
+
*/
|
|
82
|
+
export function edgeThumb(url, width = THUMB_WIDTH) {
|
|
83
|
+
if (!url || typeof url !== 'string')
|
|
84
|
+
return url;
|
|
85
|
+
const w = Math.max(32, Math.floor(width));
|
|
86
|
+
const seg = thumbTransformSegment(w);
|
|
87
|
+
if (/\/original=true\//.test(url))
|
|
88
|
+
return url.replace(/\/original=true\//, `/${seg}/`);
|
|
89
|
+
if (/\/width=\d+(?:,[^/]*)?\//.test(url)) {
|
|
90
|
+
return url.replace(/\/width=\d+(?:,[^/]*)?\//, `/${seg}/`);
|
|
91
|
+
}
|
|
92
|
+
if (isCivitaiImageHost(url)) {
|
|
93
|
+
const lastSlash = url.lastIndexOf('/');
|
|
94
|
+
if (lastSlash > 0)
|
|
95
|
+
return `${url.slice(0, lastSlash)}/${seg}${url.slice(lastSlash)}`;
|
|
96
|
+
}
|
|
97
|
+
return url;
|
|
98
|
+
}
|
|
99
|
+
/** Civitai image-edge hosts whose URLs we rewrite to a cached thumbnail variant. */
|
|
100
|
+
const CIVITAI_IMAGE_HOSTS = new Set(['image.civitai.com', 'imagecache.civitai.com']);
|
|
101
|
+
/**
|
|
102
|
+
* True iff `url` is served by a Civitai image-edge HOST. Parses the URL and
|
|
103
|
+
* checks the exact hostname (not a substring `.includes`, which a crafted URL
|
|
104
|
+
* like `https://evil.example/image.civitai.com/x` would defeat). Total — a
|
|
105
|
+
* malformed/relative URL yields `false` (no rewrite).
|
|
106
|
+
*/
|
|
107
|
+
function isCivitaiImageHost(url) {
|
|
108
|
+
try {
|
|
109
|
+
return CIVITAI_IMAGE_HOSTS.has(new URL(url).hostname.toLowerCase());
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Strip trailing '/' from a base URL in linear time. Avoids the regex
|
|
117
|
+
* `/\/+$/` whose backtracking is polynomial (ReDoS) on a long run of slashes.
|
|
118
|
+
*/
|
|
119
|
+
function stripTrailingSlashes(s) {
|
|
120
|
+
let end = s.length;
|
|
121
|
+
while (end > 0 && s.charCodeAt(end - 1) === 47 /* '/' */)
|
|
122
|
+
end -= 1;
|
|
123
|
+
return s.slice(0, end);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Map ONE raw model → a card using its FIRST modelVersion (REST default order
|
|
127
|
+
* puts the latest/primary first) and that version's first image. Returns `null`
|
|
128
|
+
* for an unusable row (no version id). Tolerant of any missing field.
|
|
129
|
+
*/
|
|
130
|
+
export function modelToCard(raw, imageWidth = THUMB_WIDTH) {
|
|
131
|
+
if (!raw || typeof raw !== 'object')
|
|
132
|
+
return null;
|
|
133
|
+
const version = raw.modelVersions?.[0];
|
|
134
|
+
const versionId = version?.id;
|
|
135
|
+
if (typeof versionId !== 'number' || !Number.isFinite(versionId))
|
|
136
|
+
return null;
|
|
137
|
+
const modelId = typeof raw.id === 'number' ? raw.id : 0;
|
|
138
|
+
const firstImage = version?.images?.find((im) => typeof im?.url === 'string' && im.url);
|
|
139
|
+
return {
|
|
140
|
+
modelId,
|
|
141
|
+
versionId,
|
|
142
|
+
modelName: (raw.name ?? '').trim() || `Model #${modelId || versionId}`,
|
|
143
|
+
versionName: (version?.name ?? '').trim(),
|
|
144
|
+
baseModel: (version?.baseModel ?? '').trim(),
|
|
145
|
+
modelType: (raw.type ?? '').trim(),
|
|
146
|
+
thumbnailUrl: firstImage?.url ? edgeThumb(firstImage.url, imageWidth) : null,
|
|
147
|
+
nsfw: raw.nsfw === true,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
/** Map a full raw response → a page of cards + the next cursor. Never throws. */
|
|
151
|
+
export function responseToPage(raw, imageWidth = THUMB_WIDTH) {
|
|
152
|
+
const items = Array.isArray(raw?.items) ? raw.items : [];
|
|
153
|
+
const cards = [];
|
|
154
|
+
for (const item of items) {
|
|
155
|
+
const card = modelToCard(item, imageWidth);
|
|
156
|
+
if (card)
|
|
157
|
+
cards.push(card);
|
|
158
|
+
}
|
|
159
|
+
const nextCursor = raw?.metadata?.nextCursor ?? null;
|
|
160
|
+
return { cards, nextCursor: typeof nextCursor === 'string' && nextCursor ? nextCursor : null };
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Map a browsed card → the EXACT `BlockCheckpointInfo` shape the production host
|
|
164
|
+
* returns in `CHECKPOINT_PICKER_RESULT.selected`. Protocol fidelity: the block's
|
|
165
|
+
* `useCheckpointPicker()` consumes this identically in dev:live and in prod.
|
|
166
|
+
*/
|
|
167
|
+
export function cardToCheckpoint(card) {
|
|
168
|
+
return {
|
|
169
|
+
versionId: card.versionId,
|
|
170
|
+
modelId: card.modelId,
|
|
171
|
+
modelName: card.modelName,
|
|
172
|
+
versionName: card.versionName,
|
|
173
|
+
baseModel: card.baseModel,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Map a browsed card → the EXACT `BlockResourceInfo` shape the production host
|
|
178
|
+
* returns in `RESOURCE_PICKER_RESULT.selected`. `modelType` is the canonical
|
|
179
|
+
* type the host resolved (e.g. 'Checkpoint' | 'LORA').
|
|
180
|
+
*/
|
|
181
|
+
export function cardToResource(card, modelType) {
|
|
182
|
+
return {
|
|
183
|
+
versionId: card.versionId,
|
|
184
|
+
modelId: card.modelId,
|
|
185
|
+
modelName: card.modelName,
|
|
186
|
+
versionName: card.versionName,
|
|
187
|
+
baseModel: card.baseModel,
|
|
188
|
+
// Prefer the REST-reported type; fall back to the requested picker type.
|
|
189
|
+
modelType: card.modelType || modelType,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Client-side family hint: keep only cards whose `baseModel` plausibly matches
|
|
194
|
+
* the requested `baseModelGroup` (ecosystem key like 'Flux1'/'SDXL' OR a
|
|
195
|
+
* baseModel name like 'Flux.1 D'). Loose substring/token match — the REAL family
|
|
196
|
+
* constraint is server-enforced at submit; this is purely a dev-UX convenience
|
|
197
|
+
* that NARROWS the browse, never widens it. An empty/absent group returns the
|
|
198
|
+
* cards unchanged.
|
|
199
|
+
*/
|
|
200
|
+
export function filterCardsByFamily(cards, baseModelGroup) {
|
|
201
|
+
const group = baseModelGroup?.trim();
|
|
202
|
+
if (!group)
|
|
203
|
+
return cards;
|
|
204
|
+
const norm = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
205
|
+
const needle = norm(group);
|
|
206
|
+
if (!needle)
|
|
207
|
+
return cards;
|
|
208
|
+
const matched = cards.filter((c) => {
|
|
209
|
+
const hay = norm(c.baseModel);
|
|
210
|
+
if (!hay)
|
|
211
|
+
return false;
|
|
212
|
+
return hay.includes(needle) || needle.includes(hay);
|
|
213
|
+
});
|
|
214
|
+
// Don't hand back an empty grid just because the hint was unusual — if nothing
|
|
215
|
+
// matched, fall back to the full set so the dev can still pick. (Server is the
|
|
216
|
+
// real constraint at submit.)
|
|
217
|
+
return matched.length > 0 ? matched : cards;
|
|
218
|
+
}
|
|
219
|
+
/** Statuses that mean "not available yet / not authorized → fall back to public". */
|
|
220
|
+
function isFallbackStatus(status) {
|
|
221
|
+
return status === 404 || status === 401 || status === 403;
|
|
222
|
+
}
|
|
223
|
+
async function toResult(res, authoritative, imageWidth) {
|
|
224
|
+
if (!res.ok) {
|
|
225
|
+
return { kind: 'error', status: res.status, message: `request failed (${res.status})` };
|
|
226
|
+
}
|
|
227
|
+
let body;
|
|
228
|
+
try {
|
|
229
|
+
body = await res.json();
|
|
230
|
+
}
|
|
231
|
+
catch (err) {
|
|
232
|
+
return {
|
|
233
|
+
kind: 'error',
|
|
234
|
+
status: res.status,
|
|
235
|
+
message: err instanceof Error ? err.message : 'bad JSON',
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
const page = responseToPage(body, imageWidth);
|
|
239
|
+
if (page.cards.length === 0)
|
|
240
|
+
return { kind: 'empty', authoritative };
|
|
241
|
+
return { kind: 'ok', page, authoritative };
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Fetch one catalog page, choosing the authoritative (token, maturity-clamped)
|
|
245
|
+
* or public (advisory-SFW) endpoint and gracefully falling back from the former
|
|
246
|
+
* to the latter when it isn't deployed / authorized yet (404/401/403) or throws
|
|
247
|
+
* (CORS/preflight/network). A failure on the PUBLIC path is a hard error (no
|
|
248
|
+
* infinite loop). Never throws.
|
|
249
|
+
*/
|
|
250
|
+
export async function fetchCatalog(q, deps) {
|
|
251
|
+
const baseUrl = stripTrailingSlashes(deps.baseUrl);
|
|
252
|
+
const token = deps.token;
|
|
253
|
+
const anonSfwOnly = deps.anonSfwOnly !== false;
|
|
254
|
+
const fetchPublic = async (sfwOnly) => {
|
|
255
|
+
const url = baseUrl + buildCatalogUrl(q, { base: CATALOG_API_BASE, sfwOnly });
|
|
256
|
+
try {
|
|
257
|
+
const res = await deps.fetch(url);
|
|
258
|
+
return toResult(res, false, deps.imageWidth);
|
|
259
|
+
}
|
|
260
|
+
catch (err) {
|
|
261
|
+
return { kind: 'error', message: err instanceof Error ? err.message : 'network error' };
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
// No token: public endpoint, advisory SFW.
|
|
265
|
+
if (!token)
|
|
266
|
+
return fetchPublic(anonSfwOnly);
|
|
267
|
+
// With token: authoritative, server-maturity-clamped endpoint.
|
|
268
|
+
const authUrl = baseUrl + buildCatalogUrl(q, { base: CATALOG_API_BASE_BLOCKS });
|
|
269
|
+
let authRes;
|
|
270
|
+
try {
|
|
271
|
+
authRes = await deps.fetch(authUrl, { headers: { Authorization: `Bearer ${token}` } });
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
deps.onFallback?.({ reason: 'network' });
|
|
275
|
+
return fetchPublic(true);
|
|
276
|
+
}
|
|
277
|
+
if (!authRes.ok && isFallbackStatus(authRes.status)) {
|
|
278
|
+
deps.onFallback?.({ status: authRes.status });
|
|
279
|
+
return fetchPublic(true);
|
|
280
|
+
}
|
|
281
|
+
return toResult(authRes, true, deps.imageWidth);
|
|
282
|
+
}
|
|
283
|
+
//# sourceMappingURL=catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../src/internal/catalog.ts"],"names":[],"mappings":"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"}
|
|
@@ -31,8 +31,18 @@
|
|
|
31
31
|
* real compute. The token's per-call budget + the per-user daily cap are the
|
|
32
32
|
* server-side bounds (scope doc §4).
|
|
33
33
|
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
34
|
+
* PICKERS (Phase 1 of "make dev:live a faithful local host"): the live host
|
|
35
|
+
* SERVES the resource pickers locally. On `OPEN_CHECKPOINT_PICKER` /
|
|
36
|
+
* `OPEN_RESOURCE_PICKER` it opens an in-harness catalog-browser overlay (real
|
|
37
|
+
* models fetched with the dev block token via `/api/v1/blocks/models`, public
|
|
38
|
+
* fallback), the dev picks one, and the host replies with a REAL
|
|
39
|
+
* `BlockCheckpointInfo` / `BlockResourceInfo` in the EXACT shape the production
|
|
40
|
+
* host returns — so `useCheckpointPicker` / `useResourcePicker` are byte-identical
|
|
41
|
+
* in dev:live and in prod (protocol fidelity, not chrome fidelity). A pick is
|
|
42
|
+
* DISCOVERY ONLY: the server re-validates + prices every id at estimate/submit;
|
|
43
|
+
* nothing the dev clicks here is trusted or spends Buzz. See the picker handlers.
|
|
44
|
+
*
|
|
45
|
+
* SCOPE — live v1 deliberately does NOT support: SET_USER_CHECKPOINT, the
|
|
36
46
|
* App-Storage KV protocol, and an in-band Buzz purchase. Each of those replies
|
|
37
47
|
* with a clearly-labelled "not supported in live v1" outcome (never a fabricated
|
|
38
48
|
* success) and logs once. Use mock mode for those flows, or a later live
|
|
@@ -40,6 +50,7 @@
|
|
|
40
50
|
*/
|
|
41
51
|
import { type BlockContext, type ColorDomain, type Theme, type ViewerInfo } from '@civitai/app-sdk/blocks';
|
|
42
52
|
import type { MockHost } from './mockHost.js';
|
|
53
|
+
import { type PickerOverlayHandle } from './pickerOverlay.js';
|
|
43
54
|
/**
|
|
44
55
|
* Options for {@link createLiveHost}. Mirrors the {@link MockHost}-relevant
|
|
45
56
|
* subset of `MockHostOptions`, plus the live-only `blockToken` / `backendBaseUrl`
|
|
@@ -82,6 +93,15 @@ export interface LiveHostOptions {
|
|
|
82
93
|
* Injectable `fetch` (tests pass a mock). Defaults to the global `fetch`.
|
|
83
94
|
*/
|
|
84
95
|
fetchImpl?: typeof fetch;
|
|
96
|
+
/**
|
|
97
|
+
* TEST SEAM for the in-harness picker overlay. Called with the overlay's
|
|
98
|
+
* {@link PickerOverlayHandle} once its first catalog page has loaded, on every
|
|
99
|
+
* `OPEN_CHECKPOINT_PICKER` / `OPEN_RESOURCE_PICKER`. Tests use it to drive a
|
|
100
|
+
* deterministic selection (`handle.selectFirst()` / `selectByVersionId()`) or
|
|
101
|
+
* a dismissal (`handle.dismiss()`) without synthesizing DOM clicks. In a real
|
|
102
|
+
* `dev:live` run this is omitted — the dev clicks a card in the overlay.
|
|
103
|
+
*/
|
|
104
|
+
onPickerReady?: (handle: PickerOverlayHandle) => void;
|
|
85
105
|
}
|
|
86
106
|
/**
|
|
87
107
|
* Decoded payload of a block-token JWT (the claims `BlockTokenService.sign`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"liveHost.d.ts","sourceRoot":"","sources":["../../src/internal/liveHost.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"liveHost.d.ts","sourceRoot":"","sources":["../../src/internal/liveHost.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AAEH,OAAO,EACL,KAAK,YAAY,EAGjB,KAAK,WAAW,EAChB,KAAK,KAAK,EACV,KAAK,UAAU,EAGhB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,QAAQ,EAAyC,MAAM,eAAe,CAAC;AACrF,OAAO,EAEL,KAAK,mBAAmB,EAEzB,MAAM,oBAAoB,CAAC;AAK5B;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,MAAM,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC3B,wEAAwE;IACxE,KAAK,CAAC,EAAE,KAAK,CAAC;IACd;;;OAGG;IACH,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;IAChE,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,UAAU,CAAC;IACpC;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;CACvD;AAWD;;;;;GAKG;AACH,UAAU,wBAAwB;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oBAAoB;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,wBAAwB,CAgB/E;AA6FD;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,eAAe,GAAG,QAAQ,CAsnBjE"}
|