veryfront 0.1.1050 → 0.1.1051
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/extensions/ext-llm-anthropic/src/anthropic-provider.d.ts.map +1 -1
- package/esm/extensions/ext-llm-anthropic/src/anthropic-provider.js +6 -2
- package/esm/extensions/ext-llm-anthropic/src/anthropic-stream.d.ts +2 -0
- package/esm/extensions/ext-llm-anthropic/src/anthropic-stream.d.ts.map +1 -1
- package/esm/extensions/ext-llm-anthropic/src/anthropic-stream.js +43 -5
- package/esm/src/html/styles-builder/css-import-extraction.d.ts +40 -0
- package/esm/src/html/styles-builder/css-import-extraction.d.ts.map +1 -0
- package/esm/src/html/styles-builder/css-import-extraction.js +103 -0
- package/esm/src/release-assets/build-executor.d.ts.map +1 -1
- package/esm/src/release-assets/build-executor.js +41 -2
- package/esm/src/server/handlers/dev/styles-css-import-scanner.d.ts +22 -0
- package/esm/src/server/handlers/dev/styles-css-import-scanner.d.ts.map +1 -0
- package/esm/src/server/handlers/dev/styles-css-import-scanner.js +114 -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 +27 -0
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/package.json +5 -5
package/esm/deno.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic-provider.d.ts","sourceRoot":"","sources":["../../../../src/extensions/ext-llm-anthropic/src/anthropic-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AAE3F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EACL,kBAAkB,EAIlB,aAAa,EACb,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,uBAAuB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,oBAAoB,EAKpB,+BAA+B,EAC/B,8BAA8B,EAC/B,MAAM,uCAAuC,CAAC;AAW/C,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,uBAAuB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,oBAAoB,EACpB,+BAA+B,EAC/B,8BAA8B,GAC/B,CAAC;AAEF,MAAM,WAAW,sBAAsB;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACjC;AA4KD,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,sBAAsB,EAC9B,OAAO,EAAE,MAAM,GACd,YAAY,
|
|
1
|
+
{"version":3,"file":"anthropic-provider.d.ts","sourceRoot":"","sources":["../../../../src/extensions/ext-llm-anthropic/src/anthropic-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AAE3F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EACL,kBAAkB,EAIlB,aAAa,EACb,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,uBAAuB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,oBAAoB,EAKpB,+BAA+B,EAC/B,8BAA8B,EAC/B,MAAM,uCAAuC,CAAC;AAW/C,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,uBAAuB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,oBAAoB,EACpB,+BAA+B,EAC/B,8BAA8B,GAC/B,CAAC;AAEF,MAAM,WAAW,sBAAsB;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACjC;AA4KD,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,sBAAsB,EAC9B,OAAO,EAAE,MAAM,GACd,YAAY,CAgFd;AAED,qBAAa,iBAAkB,YAAW,WAAW;IACnD,QAAQ,CAAC,EAAE,eAAe;IAE1B,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,YAAY;CAYtE"}
|
|
@@ -134,8 +134,12 @@ function buildAnthropicGenerateResult(payload) {
|
|
|
134
134
|
}
|
|
135
135
|
export function createAnthropicModelRuntime(config, modelId) {
|
|
136
136
|
const fetchImpl = config.fetch ?? globalThis.fetch;
|
|
137
|
+
const providerName = config.name ?? "anthropic";
|
|
138
|
+
const streamOptions = providerName === "veryfront-cloud"
|
|
139
|
+
? { clientToolUseTrailingUsageTimeoutMode: "drain" }
|
|
140
|
+
: undefined;
|
|
137
141
|
return {
|
|
138
|
-
provider:
|
|
142
|
+
provider: providerName,
|
|
139
143
|
modelId,
|
|
140
144
|
specificationVersion: "v3",
|
|
141
145
|
supportedUrls: {},
|
|
@@ -185,7 +189,7 @@ export function createAnthropicModelRuntime(config, modelId) {
|
|
|
185
189
|
}).then((responseStream) => {
|
|
186
190
|
const drained = warnings.drain();
|
|
187
191
|
return {
|
|
188
|
-
stream: ReadableStream.from(withToolInputStatusTransitions(streamAnthropicCompatibleParts(responseStream))),
|
|
192
|
+
stream: ReadableStream.from(withToolInputStatusTransitions(streamAnthropicCompatibleParts(responseStream, streamOptions))),
|
|
189
193
|
...(drained.length > 0 ? { warnings: drained } : {}),
|
|
190
194
|
};
|
|
191
195
|
});
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { RuntimeUsage } from "../../../src/provider/shared/index.js";
|
|
2
2
|
type AnthropicStreamOptions = {
|
|
3
3
|
clientToolUseTrailingUsageGraceMs?: number;
|
|
4
|
+
clientToolUseTrailingUsageTimeoutMode?: "cancel" | "drain";
|
|
5
|
+
clientToolUseTrailingUsageDrainTimeoutMs?: number;
|
|
4
6
|
};
|
|
5
7
|
export declare function normalizeAnthropicFinishReason(raw: unknown): string | {
|
|
6
8
|
unified: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic-stream.d.ts","sourceRoot":"","sources":["../../../../src/extensions/ext-llm-anthropic/src/anthropic-stream.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AAgB1E,KAAK,sBAAsB,GAAG;IAC5B,iCAAiC,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"anthropic-stream.d.ts","sourceRoot":"","sources":["../../../../src/extensions/ext-llm-anthropic/src/anthropic-stream.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AAgB1E,KAAK,sBAAsB,GAAG;IAC5B,iCAAiC,CAAC,EAAE,MAAM,CAAC;IAC3C,qCAAqC,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC;IAC3D,wCAAwC,CAAC,EAAE,MAAM,CAAC;CACnD,CAAC;AAoBF,wBAAgB,8BAA8B,CAC5C,GAAG,EAAE,OAAO,GACX,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAgBlD;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,YAAY,GAAG,SAAS,CA+DhF;AA+FD,wBAAuB,8BAA8B,CACnD,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,EAClC,OAAO,GAAE,sBAA2B,GACnC,aAAa,CAAC,OAAO,CAAC,CAwUxB"}
|
|
@@ -2,6 +2,7 @@ import * as dntShim from "../../../_dnt.shims.js";
|
|
|
2
2
|
import { mergeUsage, parseSseChunk, readGatewayBillingMode, readRecord, stringifyJsonValue, } from "../../../src/provider/shared/index.js";
|
|
3
3
|
const CLIENT_TOOL_USE_FINISH_REASON = { unified: "tool-calls", raw: "tool_use" };
|
|
4
4
|
const DEFAULT_CLIENT_TOOL_USE_TRAILING_USAGE_GRACE_MS = 100;
|
|
5
|
+
const DEFAULT_CLIENT_TOOL_USE_TRAILING_USAGE_DRAIN_TIMEOUT_MS = 15_000;
|
|
5
6
|
function isEmptyRecord(value) {
|
|
6
7
|
return Boolean(value &&
|
|
7
8
|
typeof value === "object" &&
|
|
@@ -90,7 +91,7 @@ function isToolCallsFinishReason(finishReason) {
|
|
|
90
91
|
return finishReason === "tool-calls" ||
|
|
91
92
|
(typeof finishReason === "object" && finishReason?.unified === "tool-calls");
|
|
92
93
|
}
|
|
93
|
-
async function readStreamChunk(reader, timeoutMs) {
|
|
94
|
+
async function readStreamChunk(reader, timeoutMs, timeoutMode = "cancel", drainTimeoutMs = DEFAULT_CLIENT_TOOL_USE_TRAILING_USAGE_DRAIN_TIMEOUT_MS) {
|
|
94
95
|
if (timeoutMs === undefined) {
|
|
95
96
|
const read = await reader.read();
|
|
96
97
|
return read.done ? { kind: "done" } : { kind: "chunk", chunk: read.value };
|
|
@@ -98,12 +99,17 @@ async function readStreamChunk(reader, timeoutMs) {
|
|
|
98
99
|
let timeoutId;
|
|
99
100
|
const readPromise = reader.read().then((read) => read.done ? { kind: "done" } : { kind: "chunk", chunk: read.value });
|
|
100
101
|
const timeoutPromise = new Promise((resolve) => {
|
|
101
|
-
timeoutId = dntShim.setTimeout(() => resolve({ kind: "timeout" }), Math.max(1, timeoutMs));
|
|
102
|
+
timeoutId = dntShim.setTimeout(() => resolve({ kind: "timeout", readerMode: timeoutMode }), Math.max(1, timeoutMs));
|
|
102
103
|
});
|
|
103
104
|
try {
|
|
104
105
|
const result = await Promise.race([readPromise, timeoutPromise]);
|
|
105
106
|
if (result.kind === "timeout") {
|
|
106
|
-
|
|
107
|
+
if (timeoutMode === "drain") {
|
|
108
|
+
void drainStreamReaderAfterTimeout(reader, readPromise, drainTimeoutMs);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
await cancelStreamReader(reader, "Timed out waiting for trailing Anthropic tool-use usage metadata");
|
|
112
|
+
}
|
|
107
113
|
}
|
|
108
114
|
return result;
|
|
109
115
|
}
|
|
@@ -121,11 +127,40 @@ async function cancelStreamReader(reader, reason) {
|
|
|
121
127
|
// The upstream body may already be closed or canceled by the runtime.
|
|
122
128
|
}
|
|
123
129
|
}
|
|
130
|
+
async function drainStreamReaderAfterTimeout(reader, pendingRead, timeoutMs) {
|
|
131
|
+
const timeoutId = dntShim.setTimeout(() => {
|
|
132
|
+
void cancelStreamReader(reader, "Timed out draining trailing Anthropic tool-use usage metadata");
|
|
133
|
+
}, Math.max(1, timeoutMs));
|
|
134
|
+
try {
|
|
135
|
+
const firstRead = await pendingRead;
|
|
136
|
+
if (firstRead.kind === "done") {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
while (true) {
|
|
140
|
+
const read = await reader.read();
|
|
141
|
+
if (read.done) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
// The caller has already emitted a finish event; late drain failures should
|
|
148
|
+
// not fail the completed tool turn.
|
|
149
|
+
}
|
|
150
|
+
finally {
|
|
151
|
+
clearTimeout(timeoutId);
|
|
152
|
+
reader.releaseLock();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
124
155
|
export async function* streamAnthropicCompatibleParts(stream, options = {}) {
|
|
125
156
|
const decoder = new TextDecoder();
|
|
126
157
|
const reader = stream.getReader();
|
|
127
158
|
const trailingUsageGraceMs = options.clientToolUseTrailingUsageGraceMs ??
|
|
128
159
|
DEFAULT_CLIENT_TOOL_USE_TRAILING_USAGE_GRACE_MS;
|
|
160
|
+
const trailingUsageTimeoutMode = options.clientToolUseTrailingUsageTimeoutMode ?? "cancel";
|
|
161
|
+
const trailingUsageDrainTimeoutMs = options.clientToolUseTrailingUsageDrainTimeoutMs ??
|
|
162
|
+
DEFAULT_CLIENT_TOOL_USE_TRAILING_USAGE_DRAIN_TIMEOUT_MS;
|
|
163
|
+
let readerReleased = false;
|
|
129
164
|
let buffer = "";
|
|
130
165
|
const toolCalls = new Map();
|
|
131
166
|
const reasoningBlocks = new Map();
|
|
@@ -165,8 +200,9 @@ export async function* streamAnthropicCompatibleParts(stream, options = {}) {
|
|
|
165
200
|
});
|
|
166
201
|
try {
|
|
167
202
|
while (true) {
|
|
168
|
-
const read = await readStreamChunk(reader, getClientToolUseReadTimeoutMs());
|
|
203
|
+
const read = await readStreamChunk(reader, getClientToolUseReadTimeoutMs(), trailingUsageTimeoutMode, trailingUsageDrainTimeoutMs);
|
|
169
204
|
if (read.kind === "timeout") {
|
|
205
|
+
readerReleased = read.readerMode === "drain";
|
|
170
206
|
mergeTrailingBufferUsage();
|
|
171
207
|
finishReason ??= CLIENT_TOOL_USE_FINISH_REASON;
|
|
172
208
|
yield buildFinishPart();
|
|
@@ -386,7 +422,9 @@ export async function* streamAnthropicCompatibleParts(stream, options = {}) {
|
|
|
386
422
|
}
|
|
387
423
|
}
|
|
388
424
|
finally {
|
|
389
|
-
|
|
425
|
+
if (!readerReleased) {
|
|
426
|
+
reader.releaseLock();
|
|
427
|
+
}
|
|
390
428
|
}
|
|
391
429
|
mergeTrailingBufferUsage();
|
|
392
430
|
yield buildFinishPart();
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSS Import Extraction
|
|
3
|
+
*
|
|
4
|
+
* Pure helpers for discovering CSS files imported by project source modules
|
|
5
|
+
* (side-effect imports like `import "./styles.css"` in app/layout.tsx, `@/`
|
|
6
|
+
* alias imports, and CSS module imports).
|
|
7
|
+
*
|
|
8
|
+
* The production SSR pipeline collects CSS imports while loading modules and
|
|
9
|
+
* merges them into the page stylesheet. Two other stylesheet producers have no
|
|
10
|
+
* module-loading pass and recover the same information with these helpers:
|
|
11
|
+
* the dev/preview /_vf_styles/styles.css route and the release-asset build
|
|
12
|
+
* executor.
|
|
13
|
+
*
|
|
14
|
+
* Extraction is intentionally text-based (like Tailwind candidate scanning):
|
|
15
|
+
* it must stay cheap enough to run on every stylesheet compile and must not
|
|
16
|
+
* depend on bundler/parser extensions being registered. Unresolvable or
|
|
17
|
+
* unreadable specifiers are skipped downstream, so over-matching is harmless.
|
|
18
|
+
*
|
|
19
|
+
* @module html/styles-builder/css-import-extraction
|
|
20
|
+
*/
|
|
21
|
+
/** Module extensions whose sources can carry CSS imports. */
|
|
22
|
+
export declare const CSS_IMPORTING_SOURCE_EXTENSIONS: string[];
|
|
23
|
+
/** Extract the raw specifiers of all static CSS imports in a source file. */
|
|
24
|
+
export declare function extractCssImportSpecifiers(source: string): string[];
|
|
25
|
+
/**
|
|
26
|
+
* Resolve a CSS import specifier to an absolute project path.
|
|
27
|
+
* Supports `./`/`../` (relative to the importing file) and the `@/` project
|
|
28
|
+
* alias. Bare and URL specifiers are ignored. Returns null when the resolved
|
|
29
|
+
* path would escape the project directory.
|
|
30
|
+
*/
|
|
31
|
+
export declare function resolveCssImportPath(specifier: string, importerPath: string, projectDir: string): string | null;
|
|
32
|
+
/**
|
|
33
|
+
* Collect the resolved absolute paths of all CSS files imported by the given
|
|
34
|
+
* source files, deduplicated and sorted for deterministic output.
|
|
35
|
+
*/
|
|
36
|
+
export declare function collectCssImportPaths(files: Iterable<{
|
|
37
|
+
path: string;
|
|
38
|
+
content: string;
|
|
39
|
+
}>, projectDir: string): string[];
|
|
40
|
+
//# sourceMappingURL=css-import-extraction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"css-import-extraction.d.ts","sourceRoot":"","sources":["../../../../src/src/html/styles-builder/css-import-extraction.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,6DAA6D;AAC7D,eAAO,MAAM,+BAA+B,UAAyC,CAAC;AAUtF,6EAA6E;AAC7E,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAMnE;AAkBD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,GACjB,MAAM,GAAG,IAAI,CAmBf;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,QAAQ,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,EAClD,UAAU,EAAE,MAAM,GACjB,MAAM,EAAE,CAcV"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSS Import Extraction
|
|
3
|
+
*
|
|
4
|
+
* Pure helpers for discovering CSS files imported by project source modules
|
|
5
|
+
* (side-effect imports like `import "./styles.css"` in app/layout.tsx, `@/`
|
|
6
|
+
* alias imports, and CSS module imports).
|
|
7
|
+
*
|
|
8
|
+
* The production SSR pipeline collects CSS imports while loading modules and
|
|
9
|
+
* merges them into the page stylesheet. Two other stylesheet producers have no
|
|
10
|
+
* module-loading pass and recover the same information with these helpers:
|
|
11
|
+
* the dev/preview /_vf_styles/styles.css route and the release-asset build
|
|
12
|
+
* executor.
|
|
13
|
+
*
|
|
14
|
+
* Extraction is intentionally text-based (like Tailwind candidate scanning):
|
|
15
|
+
* it must stay cheap enough to run on every stylesheet compile and must not
|
|
16
|
+
* depend on bundler/parser extensions being registered. Unresolvable or
|
|
17
|
+
* unreadable specifiers are skipped downstream, so over-matching is harmless.
|
|
18
|
+
*
|
|
19
|
+
* @module html/styles-builder/css-import-extraction
|
|
20
|
+
*/
|
|
21
|
+
import { isWithinDirectory, normalizePath } from "../../utils/path-utils.js";
|
|
22
|
+
/** Module extensions whose sources can carry CSS imports. */
|
|
23
|
+
export const CSS_IMPORTING_SOURCE_EXTENSIONS = [".tsx", ".jsx", ".mdx", ".ts", ".js"];
|
|
24
|
+
/**
|
|
25
|
+
* Static ESM import statements whose specifier ends in `.css`:
|
|
26
|
+
* import "./styles.css";
|
|
27
|
+
* import styles from "./button.module.css";
|
|
28
|
+
* `[^'";]*` keeps the match from crossing statement boundaries.
|
|
29
|
+
*/
|
|
30
|
+
const CSS_IMPORT_RE = /import[^'";]*['"]([^'"]+\.css)['"]/g;
|
|
31
|
+
/** Extract the raw specifiers of all static CSS imports in a source file. */
|
|
32
|
+
export function extractCssImportSpecifiers(source) {
|
|
33
|
+
const specifiers = [];
|
|
34
|
+
for (const match of source.matchAll(CSS_IMPORT_RE)) {
|
|
35
|
+
if (match[1])
|
|
36
|
+
specifiers.push(match[1]);
|
|
37
|
+
}
|
|
38
|
+
return specifiers;
|
|
39
|
+
}
|
|
40
|
+
/** Resolve `.`/`..` segments; returns null if the path escapes its root. */
|
|
41
|
+
function collapseSegments(path) {
|
|
42
|
+
const segments = path.split("/");
|
|
43
|
+
const out = [];
|
|
44
|
+
for (const segment of segments) {
|
|
45
|
+
if (segment === "." || (segment === "" && out.length > 0))
|
|
46
|
+
continue;
|
|
47
|
+
if (segment === "..") {
|
|
48
|
+
if (out.length <= 1)
|
|
49
|
+
return null;
|
|
50
|
+
out.pop();
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
out.push(segment);
|
|
54
|
+
}
|
|
55
|
+
return out.join("/") || "/";
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Resolve a CSS import specifier to an absolute project path.
|
|
59
|
+
* Supports `./`/`../` (relative to the importing file) and the `@/` project
|
|
60
|
+
* alias. Bare and URL specifiers are ignored. Returns null when the resolved
|
|
61
|
+
* path would escape the project directory.
|
|
62
|
+
*/
|
|
63
|
+
export function resolveCssImportPath(specifier, importerPath, projectDir) {
|
|
64
|
+
const normalizedImporter = normalizePath(importerPath);
|
|
65
|
+
const normalizedProjectDir = normalizePath(projectDir);
|
|
66
|
+
let candidate;
|
|
67
|
+
if (specifier.startsWith("./") || specifier.startsWith("../")) {
|
|
68
|
+
const dirEnd = normalizedImporter.lastIndexOf("/");
|
|
69
|
+
if (dirEnd <= 0)
|
|
70
|
+
return null;
|
|
71
|
+
candidate = `${normalizedImporter.slice(0, dirEnd)}/${specifier}`;
|
|
72
|
+
}
|
|
73
|
+
else if (specifier.startsWith("@/")) {
|
|
74
|
+
candidate = `${normalizedProjectDir}/${specifier.slice(2)}`;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
const collapsed = collapseSegments(normalizePath(candidate));
|
|
80
|
+
if (!collapsed)
|
|
81
|
+
return null;
|
|
82
|
+
if (!isWithinDirectory(normalizedProjectDir, collapsed))
|
|
83
|
+
return null;
|
|
84
|
+
return collapsed;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Collect the resolved absolute paths of all CSS files imported by the given
|
|
88
|
+
* source files, deduplicated and sorted for deterministic output.
|
|
89
|
+
*/
|
|
90
|
+
export function collectCssImportPaths(files, projectDir) {
|
|
91
|
+
const cssImports = new Set();
|
|
92
|
+
for (const file of files) {
|
|
93
|
+
// Cheap pre-filter before running the regex against large files.
|
|
94
|
+
if (!file.content.includes(".css"))
|
|
95
|
+
continue;
|
|
96
|
+
for (const specifier of extractCssImportSpecifiers(file.content)) {
|
|
97
|
+
const resolved = resolveCssImportPath(specifier, file.path, projectDir);
|
|
98
|
+
if (resolved)
|
|
99
|
+
cssImports.add(resolved);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return [...cssImports].sort();
|
|
103
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-executor.d.ts","sourceRoot":"","sources":["../../../src/src/release-assets/build-executor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;
|
|
1
|
+
{"version":3,"file":"build-executor.d.ts","sourceRoot":"","sources":["../../../src/src/release-assets/build-executor.ts"],"names":[],"mappings":"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"}
|
|
@@ -27,6 +27,7 @@ import { parseImports, replaceSpecifiers } from "../transforms/esm/lexer.js";
|
|
|
27
27
|
import { getReactUrls } from "../transforms/esm/package-registry.js";
|
|
28
28
|
import { PLATFORM_UTILITIES } from "../html/utils.js";
|
|
29
29
|
import { extractCandidatesFromFiles } from "../html/styles-builder/candidate-extractor.js";
|
|
30
|
+
import { collectCssImportPaths, CSS_IMPORTING_SOURCE_EXTENSIONS, } from "../html/styles-builder/css-import-extraction.js";
|
|
30
31
|
import { sha256HexBytes } from "./hash.js";
|
|
31
32
|
import { RELEASE_ASSET_BASE_PATH, RELEASE_ASSET_CONTENT_TYPES, RELEASE_ASSET_DEPENDENCY_IMPORT_MAP_ENV_FLAG, RELEASE_ASSET_MANIFEST_SCHEMA_VERSION, RELEASE_ASSET_MAX_SIZE_BYTES, RELEASE_ASSET_UPLOAD_CONCURRENCY, releaseAssetUrl, } from "./constants.js";
|
|
32
33
|
import { routeForPage } from "./route-path.js";
|
|
@@ -1168,7 +1169,8 @@ async function runBuildInner(input, tempDir, client, transform) {
|
|
|
1168
1169
|
if (client.compileProjectCss) {
|
|
1169
1170
|
try {
|
|
1170
1171
|
const candidates = collectClassCandidates(sourceByPath);
|
|
1171
|
-
const
|
|
1172
|
+
const resolvedStylesheet = resolveProjectStylesheet(sourceByPath, input.stylesheetPath);
|
|
1173
|
+
const stylesheet = mergeModuleCssImports(sourceByPath, resolvedStylesheet);
|
|
1172
1174
|
const compiled = await client.compileProjectCss(candidates, stylesheet);
|
|
1173
1175
|
if (compiled && compiled.css) {
|
|
1174
1176
|
const bytes = new TextEncoder().encode(compiled.css);
|
|
@@ -1300,10 +1302,47 @@ function resolveProjectStylesheet(sourceByPath, stylesheetPath) {
|
|
|
1300
1302
|
for (const path of candidatePaths) {
|
|
1301
1303
|
const content = sourceByPath.get(path);
|
|
1302
1304
|
if (typeof content === "string")
|
|
1303
|
-
return content;
|
|
1305
|
+
return { content, path };
|
|
1304
1306
|
}
|
|
1305
1307
|
return undefined;
|
|
1306
1308
|
}
|
|
1309
|
+
/**
|
|
1310
|
+
* Merge plain CSS files imported by project modules (`import "./styles.css"`
|
|
1311
|
+
* in a layout or component) into the resolved stylesheet. The production SSR
|
|
1312
|
+
* pipeline includes these imports per page at render time; release assets are
|
|
1313
|
+
* compiled once per release, so the merge happens here instead — mirroring the
|
|
1314
|
+
* dev /_vf_styles route.
|
|
1315
|
+
*
|
|
1316
|
+
* `*.module.css` files are skipped: their class names are rewritten per-module
|
|
1317
|
+
* by the runtime, so inlining them raw would leak unscoped selectors.
|
|
1318
|
+
*/
|
|
1319
|
+
function mergeModuleCssImports(sourceByPath, stylesheet) {
|
|
1320
|
+
const sourceFiles = [];
|
|
1321
|
+
for (const [path, content] of sourceByPath) {
|
|
1322
|
+
if (!CSS_IMPORTING_SOURCE_EXTENSIONS.some((ext) => path.endsWith(ext)))
|
|
1323
|
+
continue;
|
|
1324
|
+
// Resolution is rooted at "/" so relative and @/ specifiers resolve
|
|
1325
|
+
// against the release file set's project-relative paths.
|
|
1326
|
+
sourceFiles.push({ path: `/${path}`, content });
|
|
1327
|
+
}
|
|
1328
|
+
const segments = [];
|
|
1329
|
+
for (const importedPath of collectCssImportPaths(sourceFiles, "/")) {
|
|
1330
|
+
const relativePath = importedPath.replace(/^\/+/, "");
|
|
1331
|
+
if (relativePath === stylesheet?.path)
|
|
1332
|
+
continue;
|
|
1333
|
+
if (relativePath.endsWith(".module.css"))
|
|
1334
|
+
continue;
|
|
1335
|
+
const content = sourceByPath.get(relativePath);
|
|
1336
|
+
if (content)
|
|
1337
|
+
segments.push(content);
|
|
1338
|
+
}
|
|
1339
|
+
if (segments.length === 0)
|
|
1340
|
+
return stylesheet?.content;
|
|
1341
|
+
logger.debug("Merged module CSS imports into release stylesheet", {
|
|
1342
|
+
importedCount: segments.length,
|
|
1343
|
+
});
|
|
1344
|
+
return [stylesheet?.content, ...segments].filter(Boolean).join("\n");
|
|
1345
|
+
}
|
|
1307
1346
|
/** Extract Tailwind class candidates from materialized source. */
|
|
1308
1347
|
function collectClassCandidates(sourceByPath) {
|
|
1309
1348
|
return extractCandidatesFromFiles([...sourceByPath.entries()].map(([path, content]) => ({ path, content })));
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Styles CSS Import Scanner
|
|
3
|
+
*
|
|
4
|
+
* Discovers CSS files imported by project source modules (side-effect imports
|
|
5
|
+
* like `import "./styles.css"` in app/layout.tsx, `@/` alias imports, and CSS
|
|
6
|
+
* module imports). The production SSR pipeline collects these imports while
|
|
7
|
+
* loading modules and merges them into the page stylesheet; the page-agnostic
|
|
8
|
+
* /_vf_styles/styles.css dev route has no module-loading pass, so this scanner
|
|
9
|
+
* recovers the same information from project sources using the shared
|
|
10
|
+
* css-import-extraction helpers.
|
|
11
|
+
*
|
|
12
|
+
* @module server/handlers/dev/styles-css-import-scanner
|
|
13
|
+
*/
|
|
14
|
+
import type { HandlerContext } from "../types.js";
|
|
15
|
+
/**
|
|
16
|
+
* Scan project source files for CSS imports and return the resolved absolute
|
|
17
|
+
* paths, deduplicated. Mirrors the file coverage of the Tailwind candidate
|
|
18
|
+
* scanner: the FS adapter's `getAllSourceFiles()` in proxy/remote mode, and a
|
|
19
|
+
* recursive local walk otherwise.
|
|
20
|
+
*/
|
|
21
|
+
export declare function extractProjectCssImports(ctx: HandlerContext): Promise<string[]>;
|
|
22
|
+
//# sourceMappingURL=styles-css-import-scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"styles-css-import-scanner.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/dev/styles-css-import-scanner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAeH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAUlD;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAYrF"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Styles CSS Import Scanner
|
|
3
|
+
*
|
|
4
|
+
* Discovers CSS files imported by project source modules (side-effect imports
|
|
5
|
+
* like `import "./styles.css"` in app/layout.tsx, `@/` alias imports, and CSS
|
|
6
|
+
* module imports). The production SSR pipeline collects these imports while
|
|
7
|
+
* loading modules and merges them into the page stylesheet; the page-agnostic
|
|
8
|
+
* /_vf_styles/styles.css dev route has no module-loading pass, so this scanner
|
|
9
|
+
* recovers the same information from project sources using the shared
|
|
10
|
+
* css-import-extraction helpers.
|
|
11
|
+
*
|
|
12
|
+
* @module server/handlers/dev/styles-css-import-scanner
|
|
13
|
+
*/
|
|
14
|
+
import { serverLogger } from "../../../utils/index.js";
|
|
15
|
+
import { normalizePath } from "../../../utils/path-utils.js";
|
|
16
|
+
import { collectCssImportPaths, CSS_IMPORTING_SOURCE_EXTENSIONS, } from "../../../html/styles-builder/css-import-extraction.js";
|
|
17
|
+
import { createStyleScopeProfile, shouldIncludeStylePath, shouldTraverseStyleDirectory, } from "../../../html/styles-builder/style-scope-profile.js";
|
|
18
|
+
import { createFileSystem } from "../../../platform/compat/fs.js";
|
|
19
|
+
import { join } from "../../../platform/compat/path/index.js";
|
|
20
|
+
const logger = serverLogger.component("styles-css-import-scanner");
|
|
21
|
+
/**
|
|
22
|
+
* Scan project source files for CSS imports and return the resolved absolute
|
|
23
|
+
* paths, deduplicated. Mirrors the file coverage of the Tailwind candidate
|
|
24
|
+
* scanner: the FS adapter's `getAllSourceFiles()` in proxy/remote mode, and a
|
|
25
|
+
* recursive local walk otherwise.
|
|
26
|
+
*/
|
|
27
|
+
export async function extractProjectCssImports(ctx) {
|
|
28
|
+
const files = await collectSourceFiles(ctx);
|
|
29
|
+
const cssImports = collectCssImportPaths(files, ctx.projectDir);
|
|
30
|
+
if (cssImports.length > 0) {
|
|
31
|
+
logger.debug("Discovered module CSS imports", {
|
|
32
|
+
projectDir: ctx.projectDir,
|
|
33
|
+
count: cssImports.length,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return cssImports;
|
|
37
|
+
}
|
|
38
|
+
async function collectSourceFiles(ctx) {
|
|
39
|
+
const wrappedFs = ctx.adapter.fs;
|
|
40
|
+
const fsAdapter = typeof wrappedFs.getUnderlyingAdapter === "function"
|
|
41
|
+
? wrappedFs.getUnderlyingAdapter()
|
|
42
|
+
: undefined;
|
|
43
|
+
if (typeof fsAdapter?.getAllSourceFiles === "function") {
|
|
44
|
+
const files = await fsAdapter.getAllSourceFiles();
|
|
45
|
+
const collected = [];
|
|
46
|
+
for (const file of files) {
|
|
47
|
+
if (!CSS_IMPORTING_SOURCE_EXTENSIONS.some((ext) => file.path.endsWith(ext)))
|
|
48
|
+
continue;
|
|
49
|
+
const absolutePath = file.path.startsWith("/")
|
|
50
|
+
? normalizePath(file.path)
|
|
51
|
+
: normalizePath(join(ctx.projectDir, file.path));
|
|
52
|
+
const content = file.content ?? await readFileOrNull(ctx, absolutePath);
|
|
53
|
+
if (content === null)
|
|
54
|
+
continue;
|
|
55
|
+
collected.push({ path: absolutePath, content });
|
|
56
|
+
}
|
|
57
|
+
return collected;
|
|
58
|
+
}
|
|
59
|
+
return scanLocalSourceFiles(ctx);
|
|
60
|
+
}
|
|
61
|
+
async function readFileOrNull(ctx, path) {
|
|
62
|
+
try {
|
|
63
|
+
return await ctx.adapter.fs.readFile(path);
|
|
64
|
+
}
|
|
65
|
+
catch (_) {
|
|
66
|
+
/* expected: skip files that can't be read */
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/** Fallback for local development mode: walk the project directory on disk. */
|
|
71
|
+
async function scanLocalSourceFiles(ctx) {
|
|
72
|
+
const styleProfile = createStyleScopeProfile(ctx.config);
|
|
73
|
+
const fs = createFileSystem();
|
|
74
|
+
const collected = [];
|
|
75
|
+
const scanDir = async (dir) => {
|
|
76
|
+
let entries;
|
|
77
|
+
try {
|
|
78
|
+
entries = fs.readDir(dir);
|
|
79
|
+
}
|
|
80
|
+
catch (_) {
|
|
81
|
+
/* expected: directory may not exist */
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
for await (const entry of entries) {
|
|
85
|
+
const fullPath = join(dir, entry.name);
|
|
86
|
+
if (entry.isDirectory) {
|
|
87
|
+
if (shouldTraverseStyleDirectory(styleProfile, fullPath, ctx.projectDir)) {
|
|
88
|
+
await scanDir(fullPath);
|
|
89
|
+
}
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (!entry.isFile)
|
|
93
|
+
continue;
|
|
94
|
+
if (!shouldIncludeStylePath(styleProfile, fullPath, ctx.projectDir))
|
|
95
|
+
continue;
|
|
96
|
+
if (!CSS_IMPORTING_SOURCE_EXTENSIONS.some((ext) => entry.name.endsWith(ext)))
|
|
97
|
+
continue;
|
|
98
|
+
const content = await readFileOrNull(ctx, fullPath);
|
|
99
|
+
if (content === null)
|
|
100
|
+
continue;
|
|
101
|
+
collected.push({ path: normalizePath(fullPath), content });
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
try {
|
|
105
|
+
await scanDir(ctx.projectDir);
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
logger.warn("Failed to scan local files for CSS imports", {
|
|
109
|
+
projectDir: ctx.projectDir,
|
|
110
|
+
error: error instanceof Error ? error.message : String(error),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
return collected;
|
|
114
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styles-css.handler.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/dev/styles-css.handler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAmB,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"styles-css.handler.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/dev/styles-css.handler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAmB,aAAa,EAAE,MAAM,aAAa,CAAC;AAmCnG,qBAAa,gBAAiB,SAAQ,WAAW;IAC/C,QAAQ,EAAE,eAAe,CAKvB;IAEI,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;YAgNzD,cAAc;IAkB5B,OAAO,CAAC,kBAAkB;IAc1B,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,qBAAqB;IAW7B,OAAO,CAAC,wBAAwB;IA0BhC,OAAO,CAAC,4BAA4B;YA2CtB,2BAA2B;YAoD3B,oBAAoB;YASpB,2BAA2B;IA2BzC,OAAO,CAAC,oCAAoC;YAI9B,4BAA4B;CAsB3C"}
|
|
@@ -14,6 +14,8 @@ import { createPreparedProjectCSSContext, storePreparedProjectCSS, tryGetPrepare
|
|
|
14
14
|
import { createStyleScopeProfile } from "../../../html/styles-builder/style-scope-profile.js";
|
|
15
15
|
import { serverLogger } from "../../../utils/index.js";
|
|
16
16
|
import { extractProjectCandidates } from "./styles-candidate-scanner.js";
|
|
17
|
+
import { extractProjectCssImports } from "./styles-css-import-scanner.js";
|
|
18
|
+
import { mergeImportedCSS } from "../../../rendering/orchestrator/html-imported-css.js";
|
|
17
19
|
import { profilePhase } from "../../../observability/request-profiler.js";
|
|
18
20
|
const logger = serverLogger.component("styles-css-handler");
|
|
19
21
|
export class StylesCSSHandler extends BaseHandler {
|
|
@@ -42,6 +44,31 @@ export class StylesCSSHandler extends BaseHandler {
|
|
|
42
44
|
});
|
|
43
45
|
rawCss = DEFAULT_STYLESHEET;
|
|
44
46
|
}
|
|
47
|
+
// Production SSR merges CSS imported by modules (`import "./styles.css"`
|
|
48
|
+
// in a layout) into the page stylesheet during module loading. This
|
|
49
|
+
// route has no module-loading pass, so discover those imports from the
|
|
50
|
+
// project sources and merge them here. Runs before the prepared-CSS
|
|
51
|
+
// context is created so cache keys reflect the merged stylesheet.
|
|
52
|
+
try {
|
|
53
|
+
const cssImports = await profilePhase("css.scan_css_imports", () => extractProjectCssImports(ctx));
|
|
54
|
+
if (cssImports.length > 0) {
|
|
55
|
+
const merged = await profilePhase("css.merge_imported_css", () => mergeImportedCSS({
|
|
56
|
+
fs: ctx.adapter.fs,
|
|
57
|
+
logger,
|
|
58
|
+
projectDir: ctx.projectDir,
|
|
59
|
+
globalCSS: rawCss,
|
|
60
|
+
cssImports,
|
|
61
|
+
stylesheetPath: ctx.config?.tailwind?.stylesheet ?? "globals.css",
|
|
62
|
+
}));
|
|
63
|
+
if (merged)
|
|
64
|
+
rawCss = merged;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
logger.error("Failed to merge module CSS imports", {
|
|
69
|
+
error: error instanceof Error ? error.message : String(error),
|
|
70
|
+
});
|
|
71
|
+
}
|
|
45
72
|
const preparedContext = this.createPreparedCSSContext(projectScope, rawCss, styleProfile.hash, contentContext, ctx);
|
|
46
73
|
if (preparedContext) {
|
|
47
74
|
const prepared = await profilePhase("css.prepared_cache_lookup", () => tryGetPreparedProjectCSS(preparedContext));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "veryfront",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1051",
|
|
4
4
|
"description": "The simplest way to build AI-powered apps",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -329,10 +329,10 @@
|
|
|
329
329
|
"@types/react": "19.2.14",
|
|
330
330
|
"@types/react-dom": "19.2.3",
|
|
331
331
|
"ws": "8.21.0",
|
|
332
|
-
"@veryfront/ext-bundler-esbuild": "0.1.
|
|
333
|
-
"@veryfront/ext-content-mdx": "0.1.
|
|
334
|
-
"@veryfront/ext-css-tailwind": "0.1.
|
|
335
|
-
"@veryfront/ext-parser-babel": "0.1.
|
|
332
|
+
"@veryfront/ext-bundler-esbuild": "0.1.1051",
|
|
333
|
+
"@veryfront/ext-content-mdx": "0.1.1051",
|
|
334
|
+
"@veryfront/ext-css-tailwind": "0.1.1051",
|
|
335
|
+
"@veryfront/ext-parser-babel": "0.1.1051"
|
|
336
336
|
},
|
|
337
337
|
"devDependencies": {
|
|
338
338
|
"@types/node": "20.9.0"
|