@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.
@@ -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":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAIH,4BAA4B;AAC5B,MAAM,CAAC,MAAM,gBAAgB,GAAG,gBAAgB,CAAC;AAEjD,mFAAmF;AACnF,MAAM,CAAC,MAAM,uBAAuB,GAAG,uBAAuB,CAAC;AAK/D,yEAAyE;AACzE,MAAM,CAAC,MAAM,aAAa,GAAG,EAAE,CAAC;AAEhC,6EAA6E;AAC7E,MAAM,CAAC,MAAM,WAAW,GAAG,GAAG,CAAC;AAsB/B,SAAS,UAAU,CAAC,CAAS;IAC3B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,OAAO,aAAa,CAAC;IAC9C,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACnD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAC7B,CAAe,EACf,aAAuC,gBAAgB;IAEvD,MAAM,IAAI,GAAoB,OAAO,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC;IACjG,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,gBAAgB,CAAC;IAC3C,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;IACrC,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;IAC7B,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC;IAC9B,IAAI,KAAK;QAAE,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACtC,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI,aAAa,CAAC,CAAC,CAAC,CAAC;IAClE,IAAI,CAAC,CAAC,MAAM;QAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;IAC7C,IAAI,IAAI,CAAC,OAAO;QAAE,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9C,OAAO,GAAG,IAAI,IAAI,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;AACxC,CAAC;AAiDD,SAAS,qBAAqB,CAAC,KAAa;IAC1C,OAAO,SAAS,KAAK,iBAAiB,CAAC;AACzC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,GAAW,EAAE,QAAgB,WAAW;IAChE,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC;IAChD,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IAC1C,MAAM,GAAG,GAAG,qBAAqB,CAAC,CAAC,CAAC,CAAC;IACrC,IAAI,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC,OAAO,CAAC,mBAAmB,EAAE,IAAI,GAAG,GAAG,CAAC,CAAC;IACvF,IAAI,0BAA0B,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACzC,OAAO,GAAG,CAAC,OAAO,CAAC,0BAA0B,EAAE,IAAI,GAAG,GAAG,CAAC,CAAC;IAC7D,CAAC;IACD,IAAI,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5B,MAAM,SAAS,GAAG,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QACvC,IAAI,SAAS,GAAG,CAAC;YAAE,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,IAAI,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;IACvF,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,oFAAoF;AACpF,MAAM,mBAAmB,GAAG,IAAI,GAAG,CAAC,CAAC,mBAAmB,EAAE,wBAAwB,CAAC,CAAC,CAAC;AAErF;;;;;GAKG;AACH,SAAS,kBAAkB,CAAC,GAAW;IACrC,IAAI,CAAC;QACH,OAAO,mBAAmB,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;IACtE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,oBAAoB,CAAC,CAAS;IACrC,IAAI,GAAG,GAAG,CAAC,CAAC,MAAM,CAAC;IACnB,OAAO,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,SAAS;QAAE,GAAG,IAAI,CAAC,CAAC;IACnE,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AACzB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CACzB,GAAgC,EAChC,UAAU,GAAG,WAAW;IAExB,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACjD,MAAM,OAAO,GAAG,GAAG,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,CAAC;IACvC,MAAM,SAAS,GAAG,OAAO,EAAE,EAAE,CAAC;IAC9B,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9E,MAAM,OAAO,GAAG,OAAO,GAAG,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACxD,MAAM,UAAU,GAAG,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,OAAO,EAAE,EAAE,GAAG,KAAK,QAAQ,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC;IACxF,OAAO;QACL,OAAO;QACP,SAAS;QACT,SAAS,EAAE,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,UAAU,OAAO,IAAI,SAAS,EAAE;QACtE,WAAW,EAAE,CAAC,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;QACzC,SAAS,EAAE,CAAC,OAAO,EAAE,SAAS,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;QAC5C,SAAS,EAAE,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;QAClC,YAAY,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI;QAC5E,IAAI,EAAE,GAAG,CAAC,IAAI,KAAK,IAAI;KACxB,CAAC;AACJ,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,cAAc,CAC5B,GAAmC,EACnC,UAAU,GAAG,WAAW;IAExB,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,GAAI,CAAC,KAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3D,MAAM,KAAK,GAAkB,EAAE,CAAC;IAChC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;QAC3C,IAAI,IAAI;YAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IACD,MAAM,UAAU,GAAG,GAAG,EAAE,QAAQ,EAAE,UAAU,IAAI,IAAI,CAAC;IACrD,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AACjG,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAiB;IAChD,OAAO;QACL,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,SAAS,EAAE,IAAI,CAAC,SAAS;KAC1B,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,IAAiB,EAAE,SAAiB;IACjE,OAAO;QACL,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,yEAAyE;QACzE,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,SAAS;KACvC,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CACjC,KAAoB,EACpB,cAAkC;IAElC,MAAM,KAAK,GAAG,cAAc,EAAE,IAAI,EAAE,CAAC;IACrC,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,MAAM,IAAI,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;IAC9E,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3B,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC1B,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QACjC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAC9B,IAAI,CAAC,GAAG;YAAE,OAAO,KAAK,CAAC;QACvB,OAAO,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IACH,+EAA+E;IAC/E,+EAA+E;IAC/E,8BAA8B;IAC9B,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;AAC9C,CAAC;AAgBD,qFAAqF;AACrF,SAAS,gBAAgB,CAAC,MAAc;IACtC,OAAO,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG,CAAC;AAC5D,CAAC;AAED,KAAK,UAAU,QAAQ,CACrB,GAAkE,EAClE,aAAsB,EACtB,UAAmB;IAEnB,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,mBAAmB,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC;IAC1F,CAAC;IACD,IAAI,IAAa,CAAC;IAClB,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAC1B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,IAAI,EAAE,OAAO;YACb,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU;SACzD,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAG,cAAc,CAAC,IAAmB,EAAE,UAAU,CAAC,CAAC;IAC7D,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC;IACrE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;AAC7C,CAAC;AAeD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,CAAe,EAAE,IAAsB;IACxE,MAAM,OAAO,GAAG,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;IACzB,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,KAAK,KAAK,CAAC;IAE/C,MAAM,WAAW,GAAG,KAAK,EAAE,OAAgB,EAA0B,EAAE;QACrE,MAAM,GAAG,GAAG,OAAO,GAAG,eAAe,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,CAAC,CAAC;QAC9E,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAClC,OAAO,QAAQ,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QAC/C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC;QAC1F,CAAC;IACH,CAAC,CAAC;IAEF,2CAA2C;IAC3C,IAAI,CAAC,KAAK;QAAE,OAAO,WAAW,CAAC,WAAW,CAAC,CAAC;IAE5C,+DAA+D;IAC/D,MAAM,OAAO,GAAG,OAAO,GAAG,eAAe,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,uBAAuB,EAAE,CAAC,CAAC;IAChF,IAAI,OAA0C,CAAC;IAC/C,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;IACzF,CAAC;IAAC,MAAM,CAAC;QACP,IAAI,CAAC,UAAU,EAAE,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;QACzC,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IAED,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,gBAAgB,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACpD,IAAI,CAAC,UAAU,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QAC9C,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IAED,OAAO,QAAQ,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;AAClD,CAAC"}
@@ -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
- * SCOPE live v1 deliberately does NOT support: pickers
35
- * (OPEN_CHECKPOINT_PICKER / OPEN_RESOURCE_PICKER), SET_USER_CHECKPOINT, the
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;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;AAKrF;;;;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;CAC1B;AAYD;;;;;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,CA6iBjE"}
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"}