veryfront 0.1.90 → 0.1.91
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/esm/deno.js +1 -1
- package/esm/src/jobs/schemas.d.ts +30 -30
- package/esm/src/platform/adapters/veryfront-api-client/client.d.ts +3 -1
- package/esm/src/platform/adapters/veryfront-api-client/client.d.ts.map +1 -1
- package/esm/src/platform/adapters/veryfront-api-client/client.js +6 -0
- package/esm/src/platform/adapters/veryfront-api-client/index.d.ts +2 -2
- package/esm/src/platform/adapters/veryfront-api-client/index.d.ts.map +1 -1
- package/esm/src/platform/adapters/veryfront-api-client/index.js +1 -1
- package/esm/src/platform/adapters/veryfront-api-client/operations.d.ts +24 -0
- package/esm/src/platform/adapters/veryfront-api-client/operations.d.ts.map +1 -1
- package/esm/src/platform/adapters/veryfront-api-client/operations.js +65 -3
- package/esm/src/platform/adapters/veryfront-api-client/schemas/api.schema.d.ts +28 -0
- package/esm/src/platform/adapters/veryfront-api-client/schemas/api.schema.d.ts.map +1 -1
- package/esm/src/platform/adapters/veryfront-api-client/schemas/api.schema.js +13 -0
- package/esm/src/platform/adapters/veryfront-api-client/schemas/index.d.ts +1 -1
- package/esm/src/platform/adapters/veryfront-api-client/schemas/index.d.ts.map +1 -1
- package/esm/src/platform/adapters/veryfront-api-client/schemas/index.js +1 -1
- package/esm/src/sandbox/index.d.ts +1 -1
- package/esm/src/sandbox/index.d.ts.map +1 -1
- package/esm/src/sandbox/index.js +1 -1
- package/esm/src/sandbox/sandbox.d.ts +58 -0
- package/esm/src/sandbox/sandbox.d.ts.map +1 -1
- package/esm/src/sandbox/sandbox.js +111 -0
- package/esm/src/server/handlers/dev/styles-css.handler.d.ts +5 -0
- package/esm/src/server/handlers/dev/styles-css.handler.d.ts.map +1 -1
- package/esm/src/server/handlers/dev/styles-css.handler.js +121 -1
- package/package.json +1 -1
- package/src/deno.js +1 -1
- package/src/src/platform/adapters/veryfront-api-client/client.ts +17 -0
- package/src/src/platform/adapters/veryfront-api-client/index.ts +6 -0
- package/src/src/platform/adapters/veryfront-api-client/operations.ts +110 -3
- package/src/src/platform/adapters/veryfront-api-client/schemas/api.schema.ts +16 -0
- package/src/src/platform/adapters/veryfront-api-client/schemas/index.ts +2 -0
- package/src/src/sandbox/index.ts +13 -1
- package/src/src/sandbox/sandbox.ts +183 -0
- package/src/src/server/handlers/dev/styles-css.handler.ts +179 -1
|
@@ -41,6 +41,60 @@ export interface ExecStreamEvent {
|
|
|
41
41
|
exitCode?: number;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
/** Status of an async command job. */
|
|
45
|
+
export type CommandJobStatus = "running" | "completed" | "failed" | "canceled";
|
|
46
|
+
|
|
47
|
+
/** Heartbeat health status for a command job. */
|
|
48
|
+
export type CommandJobHeartbeatStatus = "disabled" | "healthy" | "degraded";
|
|
49
|
+
|
|
50
|
+
/** An async command job running in a sandbox. */
|
|
51
|
+
export interface CommandJob {
|
|
52
|
+
id: string;
|
|
53
|
+
status: CommandJobStatus;
|
|
54
|
+
exitCode: number | null;
|
|
55
|
+
signal: string | null;
|
|
56
|
+
startedAt: string;
|
|
57
|
+
finishedAt: string | null;
|
|
58
|
+
heartbeatStatus: CommandJobHeartbeatStatus;
|
|
59
|
+
lastHeartbeatAt: string | null;
|
|
60
|
+
lastHeartbeatError: string | null;
|
|
61
|
+
heartbeatFailureCount: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** A command job with its captured output. */
|
|
65
|
+
export interface CommandJobOutput extends CommandJob {
|
|
66
|
+
stdout: string;
|
|
67
|
+
stderr: string;
|
|
68
|
+
stdoutTruncated: boolean;
|
|
69
|
+
stderrTruncated: boolean;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** A sandbox session summary returned by list. */
|
|
73
|
+
export interface SandboxSession {
|
|
74
|
+
id: string;
|
|
75
|
+
shortId: string;
|
|
76
|
+
endpoint: string;
|
|
77
|
+
status: string;
|
|
78
|
+
createdAt: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Options for listing sandbox sessions. */
|
|
82
|
+
export interface SandboxListOptions extends SandboxOptions {
|
|
83
|
+
cursor?: string;
|
|
84
|
+
limit?: number;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Paginated result of sandbox sessions. */
|
|
88
|
+
export interface SandboxListResult {
|
|
89
|
+
data: SandboxSession[];
|
|
90
|
+
pageInfo: {
|
|
91
|
+
self: string | null;
|
|
92
|
+
first: null;
|
|
93
|
+
next: string | null;
|
|
94
|
+
prev: string | null;
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
44
98
|
/** Client for isolated ephemeral compute environments with command execution and file I/O. */
|
|
45
99
|
export class Sandbox {
|
|
46
100
|
private constructor(
|
|
@@ -117,6 +171,47 @@ export class Sandbox {
|
|
|
117
171
|
return new Sandbox(endpoint, id, authToken, apiUrl);
|
|
118
172
|
}
|
|
119
173
|
|
|
174
|
+
/** List sandbox sessions with optional pagination. */
|
|
175
|
+
static async list(options: SandboxListOptions = {}): Promise<SandboxListResult> {
|
|
176
|
+
const apiUrl = Sandbox.resolveApiUrl(options);
|
|
177
|
+
const authToken = Sandbox.resolveAuthToken(options);
|
|
178
|
+
|
|
179
|
+
const params = new URLSearchParams();
|
|
180
|
+
if (options.cursor) params.set("cursor", options.cursor);
|
|
181
|
+
if (options.limit !== undefined) params.set("limit", String(options.limit));
|
|
182
|
+
|
|
183
|
+
const query = params.toString();
|
|
184
|
+
const url = `${apiUrl}/sandbox-sessions${query ? `?${query}` : ""}`;
|
|
185
|
+
|
|
186
|
+
const res = await dntShim.fetch(url, {
|
|
187
|
+
headers: { Authorization: `Bearer ${authToken}` },
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
if (!res.ok) {
|
|
191
|
+
throw REQUEST_ERROR.create({
|
|
192
|
+
detail: `Failed to list sandboxes: ${res.status} ${await res.text()}`,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const json = await res.json();
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
data: json.data.map((s: Record<string, unknown>) => ({
|
|
200
|
+
id: s.id,
|
|
201
|
+
shortId: s.short_id,
|
|
202
|
+
endpoint: s.endpoint,
|
|
203
|
+
status: s.status,
|
|
204
|
+
createdAt: s.created_at,
|
|
205
|
+
})),
|
|
206
|
+
pageInfo: {
|
|
207
|
+
self: json.page_info?.self ?? null,
|
|
208
|
+
first: null,
|
|
209
|
+
next: json.page_info?.next ?? null,
|
|
210
|
+
prev: json.page_info?.prev ?? null,
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
120
215
|
private static async waitForReady(
|
|
121
216
|
apiUrl: string,
|
|
122
217
|
id: string,
|
|
@@ -247,6 +342,94 @@ export class Sandbox {
|
|
|
247
342
|
}
|
|
248
343
|
}
|
|
249
344
|
|
|
345
|
+
/** Start an async command job in the sandbox. */
|
|
346
|
+
async startCommandJob(command: string): Promise<CommandJob> {
|
|
347
|
+
const res = await dntShim.fetch(`${this.endpoint}/exec/jobs`, {
|
|
348
|
+
method: "POST",
|
|
349
|
+
headers: {
|
|
350
|
+
Authorization: `Bearer ${this.authToken}`,
|
|
351
|
+
"Content-Type": "application/json",
|
|
352
|
+
},
|
|
353
|
+
body: JSON.stringify({ command }),
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
if (!res.ok) {
|
|
357
|
+
throw REQUEST_ERROR.create({
|
|
358
|
+
detail: `Start command job failed: ${res.status} ${await res.text()}`,
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return Sandbox.mapCommandJob(await res.json());
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/** Get the status of an async command job. */
|
|
366
|
+
async getCommandJob(jobId: string): Promise<CommandJob> {
|
|
367
|
+
const res = await dntShim.fetch(`${this.endpoint}/exec/jobs/${jobId}`, {
|
|
368
|
+
headers: { Authorization: `Bearer ${this.authToken}` },
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
if (!res.ok) {
|
|
372
|
+
throw REQUEST_ERROR.create({
|
|
373
|
+
detail: `Get command job failed: ${res.status} ${await res.text()}`,
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return Sandbox.mapCommandJob(await res.json());
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/** Get the output of an async command job. */
|
|
381
|
+
async getCommandJobOutput(jobId: string): Promise<CommandJobOutput> {
|
|
382
|
+
const res = await dntShim.fetch(`${this.endpoint}/exec/jobs/${jobId}/output`, {
|
|
383
|
+
headers: { Authorization: `Bearer ${this.authToken}` },
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
if (!res.ok) {
|
|
387
|
+
throw REQUEST_ERROR.create({
|
|
388
|
+
detail: `Get command job output failed: ${res.status} ${await res.text()}`,
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const json = await res.json();
|
|
393
|
+
return {
|
|
394
|
+
...Sandbox.mapCommandJob(json),
|
|
395
|
+
stdout: json.stdout,
|
|
396
|
+
stderr: json.stderr,
|
|
397
|
+
stdoutTruncated: json.stdout_truncated,
|
|
398
|
+
stderrTruncated: json.stderr_truncated,
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/** Cancel an async command job. */
|
|
403
|
+
async cancelCommandJob(jobId: string): Promise<CommandJob> {
|
|
404
|
+
const res = await dntShim.fetch(`${this.endpoint}/exec/jobs/${jobId}/cancel`, {
|
|
405
|
+
method: "POST",
|
|
406
|
+
headers: { Authorization: `Bearer ${this.authToken}` },
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
if (!res.ok) {
|
|
410
|
+
throw REQUEST_ERROR.create({
|
|
411
|
+
detail: `Cancel command job failed: ${res.status} ${await res.text()}`,
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return Sandbox.mapCommandJob(await res.json());
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
private static mapCommandJob(json: Record<string, unknown>): CommandJob {
|
|
419
|
+
return {
|
|
420
|
+
id: json.id as string,
|
|
421
|
+
status: json.status as CommandJobStatus,
|
|
422
|
+
exitCode: json.exit_code as number | null,
|
|
423
|
+
signal: json.signal as string | null,
|
|
424
|
+
startedAt: json.started_at as string,
|
|
425
|
+
finishedAt: json.finished_at as string | null,
|
|
426
|
+
heartbeatStatus: json.heartbeat_status as CommandJobHeartbeatStatus,
|
|
427
|
+
lastHeartbeatAt: json.last_heartbeat_at as string | null,
|
|
428
|
+
lastHeartbeatError: json.last_heartbeat_error as string | null,
|
|
429
|
+
heartbeatFailureCount: json.heartbeat_failure_count as number,
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
|
|
250
433
|
/** Send a heartbeat to prevent idle timeout. */
|
|
251
434
|
async heartbeat(): Promise<void> {
|
|
252
435
|
await dntShim.fetch(`${this.apiUrl}/sandbox-sessions/${this.sessionId}/heartbeat`, {
|
|
@@ -11,22 +11,33 @@ import { BaseHandler } from "../response/base.js";
|
|
|
11
11
|
import type { HandlerContext, HandlerMetadata, HandlerPriority, HandlerResult } from "../types.js";
|
|
12
12
|
import { HTTP_OK, PRIORITY_HIGH_DEV } from "../../../utils/constants/index.js";
|
|
13
13
|
import { joinPath } from "../../../utils/path-utils.js";
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
formatCSSError,
|
|
16
|
+
getCSSByHashAsync,
|
|
17
|
+
getProjectCSS,
|
|
18
|
+
regenerateCSSByHash,
|
|
19
|
+
} from "../../../html/styles-builder/tailwind-compiler.js";
|
|
15
20
|
import { DEFAULT_STYLESHEET } from "../../../html/styles-builder/css-hash-cache.js";
|
|
16
21
|
import { resolveStyleContentVersion } from "../../../html/styles-builder/content-version.js";
|
|
17
22
|
import {
|
|
18
23
|
createPreparedProjectCSSContext,
|
|
24
|
+
type PreparedProjectCSSRequestContext,
|
|
19
25
|
storePreparedProjectCSS,
|
|
20
26
|
tryGetPreparedProjectCSS,
|
|
21
27
|
} from "../../../html/styles-builder/prepared-project-css-cache.js";
|
|
22
28
|
import { createStyleScopeProfile } from "../../../html/styles-builder/style-scope-profile.js";
|
|
23
29
|
import { serverLogger } from "../../../utils/index.js";
|
|
24
30
|
import type { ResolvedContentContext } from "../../../platform/adapters/fs/veryfront/types.js";
|
|
31
|
+
import type {
|
|
32
|
+
ResolveStyleArtifactInput,
|
|
33
|
+
VeryfrontApiClient,
|
|
34
|
+
} from "../../../platform/adapters/veryfront-api-client/index.js";
|
|
25
35
|
import { extractProjectCandidates } from "./styles-candidate-scanner.js";
|
|
26
36
|
|
|
27
37
|
const logger = serverLogger.component("styles-css-handler");
|
|
28
38
|
|
|
29
39
|
type GeneratedStylesResult = Awaited<ReturnType<typeof getProjectCSS>>;
|
|
40
|
+
type StyleArtifactSelectorContext = Omit<ResolveStyleArtifactInput, "styleProfileHash">;
|
|
30
41
|
|
|
31
42
|
export class StylesCSSHandler extends BaseHandler {
|
|
32
43
|
metadata: HandlerMetadata = {
|
|
@@ -78,6 +89,25 @@ export class StylesCSSHandler extends BaseHandler {
|
|
|
78
89
|
}
|
|
79
90
|
}
|
|
80
91
|
|
|
92
|
+
const remotePrepared = await this.tryResolveRemotePreparedCSS(
|
|
93
|
+
ctx,
|
|
94
|
+
projectScope,
|
|
95
|
+
styleProfile.hash,
|
|
96
|
+
contentContext,
|
|
97
|
+
preparedContext,
|
|
98
|
+
);
|
|
99
|
+
if (remotePrepared) {
|
|
100
|
+
logger.debug("Prepared CSS resolved via style artifact metadata", {
|
|
101
|
+
projectScope,
|
|
102
|
+
styleProfileHash: styleProfile.hash,
|
|
103
|
+
cssHash: remotePrepared.hash,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
return this.respond(
|
|
107
|
+
responseBuilder.withContentType("text/css; charset=utf-8", remotePrepared.css, HTTP_OK),
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
81
111
|
let candidates: Set<string>;
|
|
82
112
|
try {
|
|
83
113
|
candidates = await extractProjectCandidates(ctx);
|
|
@@ -148,6 +178,15 @@ body::before {
|
|
|
148
178
|
});
|
|
149
179
|
}
|
|
150
180
|
|
|
181
|
+
if ("hash" in result) {
|
|
182
|
+
await this.registerPreparedCSSArtifact(
|
|
183
|
+
ctx,
|
|
184
|
+
styleProfile.hash,
|
|
185
|
+
contentContext,
|
|
186
|
+
result.hash,
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
151
190
|
return this.respond(
|
|
152
191
|
responseBuilder.withContentType("text/css; charset=utf-8", result.css, HTTP_OK),
|
|
153
192
|
);
|
|
@@ -212,6 +251,17 @@ body::before {
|
|
|
212
251
|
return typeof fsAdapter.getContentContext === "function" ? fsAdapter.getContentContext() : null;
|
|
213
252
|
}
|
|
214
253
|
|
|
254
|
+
private getVeryfrontApiClient(ctx: HandlerContext): VeryfrontApiClient | null {
|
|
255
|
+
const wrappedFs = ctx.adapter.fs as { getUnderlyingAdapter?: () => unknown };
|
|
256
|
+
if (typeof wrappedFs.getUnderlyingAdapter !== "function") return null;
|
|
257
|
+
|
|
258
|
+
const fsAdapter = wrappedFs.getUnderlyingAdapter() as {
|
|
259
|
+
getClient?: () => VeryfrontApiClient;
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
return typeof fsAdapter.getClient === "function" ? fsAdapter.getClient() : null;
|
|
263
|
+
}
|
|
264
|
+
|
|
215
265
|
private createPreparedCSSContext(
|
|
216
266
|
projectScope: string | undefined,
|
|
217
267
|
rawCss: string,
|
|
@@ -237,4 +287,132 @@ body::before {
|
|
|
237
287
|
},
|
|
238
288
|
);
|
|
239
289
|
}
|
|
290
|
+
|
|
291
|
+
private resolveStyleArtifactSelector(
|
|
292
|
+
contentContext: ResolvedContentContext | null,
|
|
293
|
+
ctx: HandlerContext,
|
|
294
|
+
): StyleArtifactSelectorContext | null {
|
|
295
|
+
if (contentContext?.sourceType === "branch" && contentContext.branch) {
|
|
296
|
+
return {
|
|
297
|
+
branch: contentContext.branch,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (contentContext?.sourceType === "environment" && contentContext.environmentName) {
|
|
302
|
+
return {
|
|
303
|
+
environmentName: contentContext.environmentName,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (contentContext?.sourceType === "release" && contentContext.releaseId) {
|
|
308
|
+
return {
|
|
309
|
+
releaseId: contentContext.releaseId,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (ctx.parsedDomain?.branch) {
|
|
314
|
+
return {
|
|
315
|
+
branch: ctx.parsedDomain.branch,
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (ctx.environmentName) {
|
|
320
|
+
return {
|
|
321
|
+
environmentName: ctx.environmentName,
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (ctx.releaseId) {
|
|
326
|
+
return {
|
|
327
|
+
releaseId: ctx.releaseId,
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
private async tryResolveRemotePreparedCSS(
|
|
335
|
+
ctx: HandlerContext,
|
|
336
|
+
projectScope: string | undefined,
|
|
337
|
+
styleProfileHash: string,
|
|
338
|
+
contentContext: ResolvedContentContext | null,
|
|
339
|
+
preparedContext?: PreparedProjectCSSRequestContext,
|
|
340
|
+
): Promise<{ css: string; hash: string } | undefined> {
|
|
341
|
+
if (!projectScope) return undefined;
|
|
342
|
+
|
|
343
|
+
const selector = this.resolveStyleArtifactSelector(contentContext, ctx);
|
|
344
|
+
if (!selector) return undefined;
|
|
345
|
+
|
|
346
|
+
const client = this.getVeryfrontApiClient(ctx);
|
|
347
|
+
if (!client) return undefined;
|
|
348
|
+
|
|
349
|
+
try {
|
|
350
|
+
const resolved = await client.resolveStyleArtifact({
|
|
351
|
+
...selector,
|
|
352
|
+
styleProfileHash,
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
if (resolved.status !== "ready" || !resolved.artifactHash) {
|
|
356
|
+
return undefined;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const css = await this.getPreparedCSSByHash(resolved.artifactHash, projectScope);
|
|
360
|
+
if (!css) return undefined;
|
|
361
|
+
|
|
362
|
+
if (preparedContext) {
|
|
363
|
+
await storePreparedProjectCSS(preparedContext, {
|
|
364
|
+
css,
|
|
365
|
+
hash: resolved.artifactHash,
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return {
|
|
370
|
+
css,
|
|
371
|
+
hash: resolved.artifactHash,
|
|
372
|
+
};
|
|
373
|
+
} catch (error) {
|
|
374
|
+
logger.debug("Failed to resolve prepared CSS via style artifact metadata", {
|
|
375
|
+
projectScope,
|
|
376
|
+
styleProfileHash,
|
|
377
|
+
error: error instanceof Error ? error.message : String(error),
|
|
378
|
+
});
|
|
379
|
+
return undefined;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
private async getPreparedCSSByHash(
|
|
384
|
+
cssHash: string,
|
|
385
|
+
projectScope: string,
|
|
386
|
+
): Promise<string | undefined> {
|
|
387
|
+
const cached = await getCSSByHashAsync(cssHash);
|
|
388
|
+
if (cached) return cached;
|
|
389
|
+
return regenerateCSSByHash(cssHash, projectScope);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
private async registerPreparedCSSArtifact(
|
|
393
|
+
ctx: HandlerContext,
|
|
394
|
+
styleProfileHash: string,
|
|
395
|
+
contentContext: ResolvedContentContext | null,
|
|
396
|
+
cssHash: string,
|
|
397
|
+
): Promise<void> {
|
|
398
|
+
const selector = this.resolveStyleArtifactSelector(contentContext, ctx);
|
|
399
|
+
if (!selector) return;
|
|
400
|
+
|
|
401
|
+
const client = this.getVeryfrontApiClient(ctx);
|
|
402
|
+
if (!client) return;
|
|
403
|
+
|
|
404
|
+
try {
|
|
405
|
+
await client.upsertStyleArtifact({
|
|
406
|
+
...selector,
|
|
407
|
+
styleProfileHash,
|
|
408
|
+
artifactHash: cssHash,
|
|
409
|
+
});
|
|
410
|
+
} catch (error) {
|
|
411
|
+
logger.debug("Failed to register prepared CSS artifact", {
|
|
412
|
+
cssHash,
|
|
413
|
+
styleProfileHash,
|
|
414
|
+
error: error instanceof Error ? error.message : String(error),
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
}
|
|
240
418
|
}
|