veryfront 0.1.89 → 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.
Files changed (87) hide show
  1. package/esm/cli/commands/knowledge/command.d.ts +3 -0
  2. package/esm/cli/commands/knowledge/command.d.ts.map +1 -1
  3. package/esm/cli/commands/knowledge/command.js +27 -2
  4. package/esm/deno.js +1 -1
  5. package/esm/src/html/styles-builder/candidate-extractor.d.ts +5 -1
  6. package/esm/src/html/styles-builder/candidate-extractor.d.ts.map +1 -1
  7. package/esm/src/html/styles-builder/candidate-extractor.js +6 -1
  8. package/esm/src/html/styles-builder/content-version.d.ts +9 -0
  9. package/esm/src/html/styles-builder/content-version.d.ts.map +1 -0
  10. package/esm/src/html/styles-builder/content-version.js +15 -0
  11. package/esm/src/html/styles-builder/css-pregeneration.d.ts +11 -0
  12. package/esm/src/html/styles-builder/css-pregeneration.d.ts.map +1 -1
  13. package/esm/src/html/styles-builder/css-pregeneration.js +15 -10
  14. package/esm/src/html/styles-builder/prepared-project-css-cache.d.ts +31 -0
  15. package/esm/src/html/styles-builder/prepared-project-css-cache.d.ts.map +1 -0
  16. package/esm/src/html/styles-builder/prepared-project-css-cache.js +165 -0
  17. package/esm/src/html/styles-builder/style-scope-profile.d.ts +14 -0
  18. package/esm/src/html/styles-builder/style-scope-profile.d.ts.map +1 -0
  19. package/esm/src/html/styles-builder/style-scope-profile.js +121 -0
  20. package/esm/src/jobs/schemas.d.ts +30 -30
  21. package/esm/src/platform/adapters/fs/factory.d.ts.map +1 -1
  22. package/esm/src/platform/adapters/fs/factory.js +2 -21
  23. package/esm/src/platform/adapters/fs/veryfront/adapter.d.ts.map +1 -1
  24. package/esm/src/platform/adapters/fs/veryfront/adapter.js +44 -19
  25. package/esm/src/platform/adapters/fs/veryfront/default-invalidation-callbacks.d.ts +3 -0
  26. package/esm/src/platform/adapters/fs/veryfront/default-invalidation-callbacks.d.ts.map +1 -0
  27. package/esm/src/platform/adapters/fs/veryfront/default-invalidation-callbacks.js +25 -0
  28. package/esm/src/platform/adapters/fs/veryfront/proxy-manager.d.ts.map +1 -1
  29. package/esm/src/platform/adapters/fs/veryfront/proxy-manager.js +2 -15
  30. package/esm/src/platform/adapters/fs/veryfront/websocket-manager.d.ts +4 -0
  31. package/esm/src/platform/adapters/fs/veryfront/websocket-manager.d.ts.map +1 -1
  32. package/esm/src/platform/adapters/fs/veryfront/websocket-manager.js +2 -0
  33. package/esm/src/platform/adapters/veryfront-api-client/client.d.ts +3 -1
  34. package/esm/src/platform/adapters/veryfront-api-client/client.d.ts.map +1 -1
  35. package/esm/src/platform/adapters/veryfront-api-client/client.js +6 -0
  36. package/esm/src/platform/adapters/veryfront-api-client/index.d.ts +2 -2
  37. package/esm/src/platform/adapters/veryfront-api-client/index.d.ts.map +1 -1
  38. package/esm/src/platform/adapters/veryfront-api-client/index.js +1 -1
  39. package/esm/src/platform/adapters/veryfront-api-client/operations.d.ts +24 -0
  40. package/esm/src/platform/adapters/veryfront-api-client/operations.d.ts.map +1 -1
  41. package/esm/src/platform/adapters/veryfront-api-client/operations.js +65 -3
  42. package/esm/src/platform/adapters/veryfront-api-client/schemas/api.schema.d.ts +28 -0
  43. package/esm/src/platform/adapters/veryfront-api-client/schemas/api.schema.d.ts.map +1 -1
  44. package/esm/src/platform/adapters/veryfront-api-client/schemas/api.schema.js +13 -0
  45. package/esm/src/platform/adapters/veryfront-api-client/schemas/index.d.ts +1 -1
  46. package/esm/src/platform/adapters/veryfront-api-client/schemas/index.d.ts.map +1 -1
  47. package/esm/src/platform/adapters/veryfront-api-client/schemas/index.js +1 -1
  48. package/esm/src/rendering/orchestrator/css-candidate-manifest.d.ts +3 -0
  49. package/esm/src/rendering/orchestrator/css-candidate-manifest.d.ts.map +1 -1
  50. package/esm/src/rendering/orchestrator/css-candidate-manifest.js +10 -5
  51. package/esm/src/rendering/orchestrator/html.d.ts.map +1 -1
  52. package/esm/src/rendering/orchestrator/html.js +8 -0
  53. package/esm/src/sandbox/index.d.ts +1 -1
  54. package/esm/src/sandbox/index.d.ts.map +1 -1
  55. package/esm/src/sandbox/index.js +1 -1
  56. package/esm/src/sandbox/sandbox.d.ts +58 -0
  57. package/esm/src/sandbox/sandbox.d.ts.map +1 -1
  58. package/esm/src/sandbox/sandbox.js +111 -0
  59. package/esm/src/server/handlers/dev/styles-candidate-scanner.d.ts.map +1 -1
  60. package/esm/src/server/handlers/dev/styles-candidate-scanner.js +14 -16
  61. package/esm/src/server/handlers/dev/styles-css.handler.d.ts +7 -0
  62. package/esm/src/server/handlers/dev/styles-css.handler.d.ts.map +1 -1
  63. package/esm/src/server/handlers/dev/styles-css.handler.js +175 -8
  64. package/package.json +1 -1
  65. package/src/cli/commands/knowledge/command.ts +30 -2
  66. package/src/deno.js +1 -1
  67. package/src/src/html/styles-builder/candidate-extractor.ts +13 -0
  68. package/src/src/html/styles-builder/content-version.ts +20 -0
  69. package/src/src/html/styles-builder/css-pregeneration.ts +49 -12
  70. package/src/src/html/styles-builder/prepared-project-css-cache.ts +228 -0
  71. package/src/src/html/styles-builder/style-scope-profile.ts +164 -0
  72. package/src/src/platform/adapters/fs/factory.ts +2 -27
  73. package/src/src/platform/adapters/fs/veryfront/adapter.ts +49 -20
  74. package/src/src/platform/adapters/fs/veryfront/default-invalidation-callbacks.ts +35 -0
  75. package/src/src/platform/adapters/fs/veryfront/proxy-manager.ts +4 -21
  76. package/src/src/platform/adapters/fs/veryfront/websocket-manager.ts +3 -0
  77. package/src/src/platform/adapters/veryfront-api-client/client.ts +17 -0
  78. package/src/src/platform/adapters/veryfront-api-client/index.ts +6 -0
  79. package/src/src/platform/adapters/veryfront-api-client/operations.ts +110 -3
  80. package/src/src/platform/adapters/veryfront-api-client/schemas/api.schema.ts +16 -0
  81. package/src/src/platform/adapters/veryfront-api-client/schemas/index.ts +2 -0
  82. package/src/src/rendering/orchestrator/css-candidate-manifest.ts +28 -6
  83. package/src/src/rendering/orchestrator/html.ts +11 -0
  84. package/src/src/sandbox/index.ts +13 -1
  85. package/src/src/sandbox/sandbox.ts +183 -0
  86. package/src/src/server/handlers/dev/styles-candidate-scanner.ts +18 -15
  87. package/src/src/server/handlers/dev/styles-css.handler.ts +262 -12
@@ -8,6 +8,8 @@
8
8
  * @module server/handlers/dev/styles-candidate-scanner
9
9
  */
10
10
  import { extractCandidates } from "../../../html/styles-builder/tailwind-compiler.js";
11
+ import { resolveStyleContentVersion } from "../../../html/styles-builder/content-version.js";
12
+ import { createStyleScopeProfile, shouldIncludeStylePath, shouldTraverseStyleDirectory, } from "../../../html/styles-builder/style-scope-profile.js";
11
13
  import { serverLogger } from "../../../utils/index.js";
12
14
  import { createFileSystem } from "../../../platform/compat/fs.js";
13
15
  import { join } from "../../../platform/compat/path/index.js";
@@ -15,22 +17,8 @@ import { getProjectCandidates } from "../../../rendering/orchestrator/css-candid
15
17
  import { FRAMEWORK_CANDIDATES } from "./framework-candidates.generated.js";
16
18
  const logger = serverLogger.component("styles-candidate-scanner");
17
19
  const SOURCE_EXTENSIONS = [".tsx", ".jsx", ".mdx", ".ts", ".js"];
18
- const SKIP_DIRS = new Set(["node_modules", ".cache", ".git", "dist", "build", ".vscode"]);
19
20
  /** De-duplicated set of framework candidates, computed once at import time. */
20
21
  const frameworkCandidates = new Set(FRAMEWORK_CANDIDATES);
21
- function resolveProjectVersion(ctx, contentContext) {
22
- if (contentContext?.releaseId)
23
- return `release:${contentContext.releaseId}`;
24
- if (contentContext?.branch)
25
- return `branch:${contentContext.branch}`;
26
- if (contentContext?.environmentName)
27
- return `environment:${contentContext.environmentName}`;
28
- if (ctx.releaseId)
29
- return `release:${ctx.releaseId}`;
30
- if (ctx.parsedDomain?.branch)
31
- return `branch:${ctx.parsedDomain.branch}`;
32
- return "live";
33
- }
34
22
  /**
35
23
  * Extract Tailwind CSS candidate class names from all project source files.
36
24
  *
@@ -39,6 +27,7 @@ function resolveProjectVersion(ctx, contentContext) {
39
27
  * method is available (local dev mode).
40
28
  */
41
29
  export async function extractProjectCandidates(ctx) {
30
+ const styleProfile = createStyleScopeProfile(ctx.config);
42
31
  const wrappedFs = ctx.adapter.fs;
43
32
  if (typeof wrappedFs.getUnderlyingAdapter !== "function") {
44
33
  logger.debug("[StylesCandidateScanner] No FS adapter wrapper, falling back to local file scanning");
@@ -57,8 +46,13 @@ export async function extractProjectCandidates(ctx) {
57
46
  : null;
58
47
  for (const cls of getProjectCandidates({
59
48
  projectScope: ctx.projectSlug ?? contentContext?.projectSlug ?? ctx.projectDir,
60
- projectVersion: resolveProjectVersion(ctx, contentContext),
49
+ projectVersion: resolveStyleContentVersion(contentContext, {
50
+ releaseId: ctx.releaseId,
51
+ branch: ctx.parsedDomain?.branch,
52
+ environmentName: ctx.environmentName,
53
+ }),
61
54
  projectDir: ctx.projectDir,
55
+ styleProfile,
62
56
  files,
63
57
  developmentMode: contentContext?.sourceType === "branch",
64
58
  })) {
@@ -71,6 +65,7 @@ export async function extractProjectCandidates(ctx) {
71
65
  * Used in local development mode where projects are read directly from disk.
72
66
  */
73
67
  async function scanLocalFiles(projectDir, ctx) {
68
+ const styleProfile = createStyleScopeProfile(ctx.config);
74
69
  const candidates = new Set(frameworkCandidates);
75
70
  const fs = createFileSystem();
76
71
  const scanDir = async (dir) => {
@@ -85,12 +80,15 @@ async function scanLocalFiles(projectDir, ctx) {
85
80
  for await (const entry of entries) {
86
81
  const fullPath = join(dir, entry.name);
87
82
  if (entry.isDirectory) {
88
- if (!SKIP_DIRS.has(entry.name))
83
+ if (shouldTraverseStyleDirectory(styleProfile, fullPath, projectDir)) {
89
84
  await scanDir(fullPath);
85
+ }
90
86
  continue;
91
87
  }
92
88
  if (!entry.isFile)
93
89
  continue;
90
+ if (!shouldIncludeStylePath(styleProfile, fullPath, projectDir))
91
+ continue;
94
92
  if (!SOURCE_EXTENSIONS.some((ext) => entry.name.endsWith(ext)))
95
93
  continue;
96
94
  try {
@@ -12,5 +12,12 @@ export declare class StylesCSSHandler extends BaseHandler {
12
12
  handle(req: dntShim.Request, ctx: HandlerContext): Promise<HandlerResult>;
13
13
  private loadStylesheet;
14
14
  private generateStylesheet;
15
+ private getContentContext;
16
+ private getVeryfrontApiClient;
17
+ private createPreparedCSSContext;
18
+ private resolveStyleArtifactSelector;
19
+ private tryResolveRemotePreparedCSS;
20
+ private getPreparedCSSByHash;
21
+ private registerPreparedCSSArtifact;
15
22
  }
16
23
  //# sourceMappingURL=styles-css.handler.d.ts.map
@@ -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;AACH,OAAO,KAAK,OAAO,MAAM,2BAA2B,CAAC;AAGrD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAmB,aAAa,EAAE,MAAM,aAAa,CAAC;AAkBnG,qBAAa,gBAAiB,SAAQ,WAAW;IAC/C,QAAQ,EAAE,eAAe,CAKvB;IAEI,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;YAkGjE,cAAc;IAkB5B,OAAO,CAAC,kBAAkB;CAe3B"}
1
+ {"version":3,"file":"styles-css.handler.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/dev/styles-css.handler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,OAAO,MAAM,2BAA2B,CAAC;AAGrD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAmB,aAAa,EAAE,MAAM,aAAa,CAAC;AA+BnG,qBAAa,gBAAiB,SAAQ,WAAW;IAC/C,QAAQ,EAAE,eAAe,CAKvB;IAEI,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;YAiKjE,cAAc;IAkB5B,OAAO,CAAC,kBAAkB;IAc1B,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,qBAAqB;IAW7B,OAAO,CAAC,wBAAwB;IA0BhC,OAAO,CAAC,4BAA4B;YA2CtB,2BAA2B;YAiD3B,oBAAoB;YASpB,2BAA2B;CA0B1C"}
@@ -1,8 +1,11 @@
1
1
  import { BaseHandler } from "../response/base.js";
2
2
  import { HTTP_OK, PRIORITY_HIGH_DEV } from "../../../utils/constants/index.js";
3
3
  import { joinPath } from "../../../utils/path-utils.js";
4
- import { formatCSSError, generateTailwindCSS, getProjectCSS, } from "../../../html/styles-builder/tailwind-compiler.js";
4
+ import { formatCSSError, getCSSByHashAsync, getProjectCSS, regenerateCSSByHash, } from "../../../html/styles-builder/tailwind-compiler.js";
5
5
  import { DEFAULT_STYLESHEET } from "../../../html/styles-builder/css-hash-cache.js";
6
+ import { resolveStyleContentVersion } from "../../../html/styles-builder/content-version.js";
7
+ import { createPreparedProjectCSSContext, storePreparedProjectCSS, tryGetPreparedProjectCSS, } from "../../../html/styles-builder/prepared-project-css-cache.js";
8
+ import { createStyleScopeProfile } from "../../../html/styles-builder/style-scope-profile.js";
6
9
  import { serverLogger } from "../../../utils/index.js";
7
10
  import { extractProjectCandidates } from "./styles-candidate-scanner.js";
8
11
  const logger = serverLogger.component("styles-css-handler");
@@ -19,6 +22,9 @@ export class StylesCSSHandler extends BaseHandler {
19
22
  try {
20
23
  return await this.withProxyContext(ctx, async () => {
21
24
  const responseBuilder = this.createResponseBuilder(ctx).withCache("no-cache");
25
+ const projectScope = ctx.projectSlug ?? ctx.projectDir;
26
+ const styleProfile = createStyleScopeProfile(ctx.config);
27
+ const contentContext = this.getContentContext(ctx);
22
28
  let rawCss;
23
29
  try {
24
30
  rawCss = await this.loadStylesheet(ctx);
@@ -29,6 +35,28 @@ export class StylesCSSHandler extends BaseHandler {
29
35
  });
30
36
  rawCss = DEFAULT_STYLESHEET;
31
37
  }
38
+ const preparedContext = this.createPreparedCSSContext(projectScope, rawCss, styleProfile.hash, contentContext, ctx);
39
+ if (preparedContext) {
40
+ const prepared = await tryGetPreparedProjectCSS(preparedContext);
41
+ if (prepared) {
42
+ logger.debug("Prepared CSS cache hit", {
43
+ projectScope,
44
+ projectVersion: preparedContext.projectVersion,
45
+ styleProfileHash: styleProfile.hash,
46
+ cssHash: prepared.hash,
47
+ });
48
+ return this.respond(responseBuilder.withContentType("text/css; charset=utf-8", prepared.css, HTTP_OK));
49
+ }
50
+ }
51
+ const remotePrepared = await this.tryResolveRemotePreparedCSS(ctx, projectScope, styleProfile.hash, contentContext, preparedContext);
52
+ if (remotePrepared) {
53
+ logger.debug("Prepared CSS resolved via style artifact metadata", {
54
+ projectScope,
55
+ styleProfileHash: styleProfile.hash,
56
+ cssHash: remotePrepared.hash,
57
+ });
58
+ return this.respond(responseBuilder.withContentType("text/css; charset=utf-8", remotePrepared.css, HTTP_OK));
59
+ }
32
60
  let candidates;
33
61
  try {
34
62
  candidates = await extractProjectCandidates(ctx);
@@ -39,9 +67,12 @@ export class StylesCSSHandler extends BaseHandler {
39
67
  });
40
68
  candidates = new Set();
41
69
  }
42
- const result = await this.generateStylesheet(ctx, rawCss, candidates);
43
- if ("error" in result && result.error) {
44
- const formatted = formatCSSError(result.error);
70
+ let result;
71
+ try {
72
+ result = await this.generateStylesheet(ctx, rawCss, candidates);
73
+ }
74
+ catch (error) {
75
+ const formatted = formatCSSError(error instanceof Error ? error : String(error));
45
76
  logger.error("Tailwind error", {
46
77
  error: formatted.message,
47
78
  suggestion: formatted.suggestion,
@@ -78,11 +109,21 @@ body::before {
78
109
  });
79
110
  }
80
111
  logger.debug("CSS generated", {
112
+ projectScope,
81
113
  candidates: candidates.size,
82
114
  cssLength: result.css.length,
83
115
  fromCache: "fromCache" in result ? result.fromCache : false,
84
116
  cssHash: "hash" in result ? result.hash : undefined,
85
117
  });
118
+ if (preparedContext && "hash" in result) {
119
+ await storePreparedProjectCSS(preparedContext, {
120
+ css: result.css,
121
+ hash: result.hash,
122
+ });
123
+ }
124
+ if ("hash" in result) {
125
+ await this.registerPreparedCSSArtifact(ctx, styleProfile.hash, contentContext, result.hash);
126
+ }
86
127
  return this.respond(responseBuilder.withContentType("text/css; charset=utf-8", result.css, HTTP_OK));
87
128
  });
88
129
  }
@@ -115,13 +156,139 @@ body::before {
115
156
  }
116
157
  }
117
158
  generateStylesheet(ctx, rawCss, candidates) {
118
- if (!ctx.projectSlug) {
119
- return generateTailwindCSS(rawCss, candidates);
120
- }
121
- return getProjectCSS(ctx.projectSlug, rawCss, candidates, {
159
+ const projectScope = ctx.projectSlug ?? ctx.projectDir;
160
+ return getProjectCSS(projectScope, rawCss, candidates, {
122
161
  minify: true,
123
162
  environment: "preview",
124
163
  buildMode: "production",
125
164
  });
126
165
  }
166
+ getContentContext(ctx) {
167
+ const wrappedFs = ctx.adapter.fs;
168
+ if (typeof wrappedFs.getUnderlyingAdapter !== "function")
169
+ return null;
170
+ const fsAdapter = wrappedFs.getUnderlyingAdapter();
171
+ return typeof fsAdapter.getContentContext === "function" ? fsAdapter.getContentContext() : null;
172
+ }
173
+ getVeryfrontApiClient(ctx) {
174
+ const wrappedFs = ctx.adapter.fs;
175
+ if (typeof wrappedFs.getUnderlyingAdapter !== "function")
176
+ return null;
177
+ const fsAdapter = wrappedFs.getUnderlyingAdapter();
178
+ return typeof fsAdapter.getClient === "function" ? fsAdapter.getClient() : null;
179
+ }
180
+ createPreparedCSSContext(projectScope, rawCss, styleProfileHash, contentContext, ctx) {
181
+ if (!projectScope)
182
+ return undefined;
183
+ return createPreparedProjectCSSContext(projectScope, resolveStyleContentVersion(contentContext, {
184
+ releaseId: ctx.releaseId,
185
+ branch: ctx.parsedDomain?.branch,
186
+ environmentName: ctx.environmentName,
187
+ }), rawCss, styleProfileHash, {
188
+ minify: true,
189
+ environment: "preview",
190
+ buildMode: "production",
191
+ });
192
+ }
193
+ resolveStyleArtifactSelector(contentContext, ctx) {
194
+ if (contentContext?.sourceType === "branch" && contentContext.branch) {
195
+ return {
196
+ branch: contentContext.branch,
197
+ };
198
+ }
199
+ if (contentContext?.sourceType === "environment" && contentContext.environmentName) {
200
+ return {
201
+ environmentName: contentContext.environmentName,
202
+ };
203
+ }
204
+ if (contentContext?.sourceType === "release" && contentContext.releaseId) {
205
+ return {
206
+ releaseId: contentContext.releaseId,
207
+ };
208
+ }
209
+ if (ctx.parsedDomain?.branch) {
210
+ return {
211
+ branch: ctx.parsedDomain.branch,
212
+ };
213
+ }
214
+ if (ctx.environmentName) {
215
+ return {
216
+ environmentName: ctx.environmentName,
217
+ };
218
+ }
219
+ if (ctx.releaseId) {
220
+ return {
221
+ releaseId: ctx.releaseId,
222
+ };
223
+ }
224
+ return null;
225
+ }
226
+ async tryResolveRemotePreparedCSS(ctx, projectScope, styleProfileHash, contentContext, preparedContext) {
227
+ if (!projectScope)
228
+ return undefined;
229
+ const selector = this.resolveStyleArtifactSelector(contentContext, ctx);
230
+ if (!selector)
231
+ return undefined;
232
+ const client = this.getVeryfrontApiClient(ctx);
233
+ if (!client)
234
+ return undefined;
235
+ try {
236
+ const resolved = await client.resolveStyleArtifact({
237
+ ...selector,
238
+ styleProfileHash,
239
+ });
240
+ if (resolved.status !== "ready" || !resolved.artifactHash) {
241
+ return undefined;
242
+ }
243
+ const css = await this.getPreparedCSSByHash(resolved.artifactHash, projectScope);
244
+ if (!css)
245
+ return undefined;
246
+ if (preparedContext) {
247
+ await storePreparedProjectCSS(preparedContext, {
248
+ css,
249
+ hash: resolved.artifactHash,
250
+ });
251
+ }
252
+ return {
253
+ css,
254
+ hash: resolved.artifactHash,
255
+ };
256
+ }
257
+ catch (error) {
258
+ logger.debug("Failed to resolve prepared CSS via style artifact metadata", {
259
+ projectScope,
260
+ styleProfileHash,
261
+ error: error instanceof Error ? error.message : String(error),
262
+ });
263
+ return undefined;
264
+ }
265
+ }
266
+ async getPreparedCSSByHash(cssHash, projectScope) {
267
+ const cached = await getCSSByHashAsync(cssHash);
268
+ if (cached)
269
+ return cached;
270
+ return regenerateCSSByHash(cssHash, projectScope);
271
+ }
272
+ async registerPreparedCSSArtifact(ctx, styleProfileHash, contentContext, cssHash) {
273
+ const selector = this.resolveStyleArtifactSelector(contentContext, ctx);
274
+ if (!selector)
275
+ return;
276
+ const client = this.getVeryfrontApiClient(ctx);
277
+ if (!client)
278
+ return;
279
+ try {
280
+ await client.upsertStyleArtifact({
281
+ ...selector,
282
+ styleProfileHash,
283
+ artifactHash: cssHash,
284
+ });
285
+ }
286
+ catch (error) {
287
+ logger.debug("Failed to register prepared CSS artifact", {
288
+ cssHash,
289
+ styleProfileHash,
290
+ error: error instanceof Error ? error.message : String(error),
291
+ });
292
+ }
293
+ }
127
294
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "veryfront",
3
- "version": "0.1.89",
3
+ "version": "0.1.91",
4
4
  "description": "The simplest way to build AI-powered apps",
5
5
  "keywords": [
6
6
  "react",
@@ -219,6 +219,23 @@ function slugify(value: string): string {
219
219
  return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "document";
220
220
  }
221
221
 
222
+ /**
223
+ * Strip the chat-upload prefix that Studio prepends to uploaded files.
224
+ *
225
+ * Studio stores uploads with a generated prefix like:
226
+ * chat-<uuid>-<timestamp>-<shortid>-<original-filename>
227
+ *
228
+ * This function removes the prefix so knowledge files use the clean
229
+ * original filename (e.g. "agents" instead of
230
+ * "chat-909d3dbc-5a9a-4156-97e4-bcceb5c2ec0d-1773942180291-fv1qg5-agents").
231
+ */
232
+ const CHAT_UPLOAD_PREFIX_RE =
233
+ /^chat-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}-\d+-[a-z0-9]+-/i;
234
+
235
+ export function stripChatUploadPrefix(name: string): string {
236
+ return name.replace(CHAT_UPLOAD_PREFIX_RE, "");
237
+ }
238
+
222
239
  function defaultOutputRoot(): Promise<string> {
223
240
  return dntShim.Deno.makeTempDir({ prefix: "veryfront-knowledge-" });
224
241
  }
@@ -361,7 +378,7 @@ function buildSourceReference(source: KnowledgeSource): string {
361
378
  : source.localPath;
362
379
  }
363
380
 
364
- function buildSuggestedSlug(source: KnowledgeSource, index: number): string {
381
+ export function buildSuggestedSlug(source: KnowledgeSource, index: number): string {
365
382
  const normalized = normalize(
366
383
  source.kind === "upload" ? source.uploadPath : source.localPath,
367
384
  ).replace(/\\/g, "/");
@@ -383,9 +400,20 @@ function buildSuggestedSlug(source: KnowledgeSource, index: number): string {
383
400
  stripped = normalized.replace(/\.[^.]+$/, "");
384
401
  }
385
402
 
403
+ // Strip Studio's chat-upload prefix from the filename portion so that
404
+ // knowledge files use the original clean filename.
405
+ const lastSlash = stripped.lastIndexOf("/");
406
+ if (lastSlash >= 0) {
407
+ const dir = stripped.slice(0, lastSlash + 1);
408
+ const file = stripChatUploadPrefix(stripped.slice(lastSlash + 1));
409
+ stripped = file ? `${dir}${file}` : stripped;
410
+ } else {
411
+ stripped = stripChatUploadPrefix(stripped) || stripped;
412
+ }
413
+
386
414
  return slugify(stripped || basename(normalized, extname(normalized)) || `document-${index + 1}`);
387
415
  }
388
- function ensureUniqueSlugs(sources: KnowledgeSource[]): string[] {
416
+ export function ensureUniqueSlugs(sources: KnowledgeSource[]): string[] {
389
417
  const counts = new Map<string, number>();
390
418
  return sources.map((source, index) => {
391
419
  const baseSlug = buildSuggestedSlug(source, index);
package/src/deno.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  "name": "veryfront",
3
- "version": "0.1.89",
3
+ "version": "0.1.91",
4
4
  "license": "Apache-2.0",
5
5
  "nodeModulesDir": "auto",
6
6
  "exclude": [
@@ -6,6 +6,9 @@
6
6
  * @module html/styles-builder/candidate-extractor
7
7
  */
8
8
 
9
+ import type { StyleScopeProfile } from "./style-scope-profile.js";
10
+ import { shouldIncludeStylePath } from "./style-scope-profile.js";
11
+
9
12
  /**
10
13
  * Extract potential Tailwind class name candidates from source code content.
11
14
  * Uses a comprehensive regex pattern matching Tailwind v4 utility patterns.
@@ -17,12 +20,22 @@ export function extractCandidates(content: string): string[] {
17
20
 
18
21
  export function extractCandidatesFromFiles(
19
22
  files: Array<{ path: string; content?: string }>,
23
+ options: {
24
+ projectDir?: string;
25
+ styleProfile?: StyleScopeProfile;
26
+ } = {},
20
27
  ): Set<string> {
21
28
  const candidates = new Set<string>();
22
29
  const sourceExtensions = [".tsx", ".jsx", ".ts", ".js", ".mdx"];
23
30
 
24
31
  for (const file of files) {
25
32
  if (!file.content) continue;
33
+ if (
34
+ options.styleProfile &&
35
+ !shouldIncludeStylePath(options.styleProfile, file.path, options.projectDir)
36
+ ) {
37
+ continue;
38
+ }
26
39
  if (!sourceExtensions.some((ext) => file.path.endsWith(ext))) continue;
27
40
 
28
41
  for (const candidate of extractCandidates(file.content)) {
@@ -0,0 +1,20 @@
1
+ import type { ResolvedContentContext } from "../../platform/adapters/fs/veryfront/types.js";
2
+
3
+ interface ContentVersionFallback {
4
+ releaseId?: string | null;
5
+ branch?: string | null;
6
+ environmentName?: string | null;
7
+ }
8
+
9
+ export function resolveStyleContentVersion(
10
+ contentContext: ResolvedContentContext | null,
11
+ fallback: ContentVersionFallback = {},
12
+ ): string {
13
+ if (contentContext?.releaseId) return `release:${contentContext.releaseId}`;
14
+ if (contentContext?.branch) return `branch:${contentContext.branch}`;
15
+ if (contentContext?.environmentName) return `environment:${contentContext.environmentName}`;
16
+ if (fallback.releaseId) return `release:${fallback.releaseId}`;
17
+ if (fallback.branch) return `branch:${fallback.branch}`;
18
+ if (fallback.environmentName) return `environment:${fallback.environmentName}`;
19
+ return "live";
20
+ }
@@ -8,20 +8,35 @@
8
8
 
9
9
  import { serverLogger } from "../../utils/index.js";
10
10
  import { extractCandidatesFromFiles, getProjectCSS } from "./tailwind-compiler.js";
11
+ import {
12
+ createPreparedProjectCSSContext,
13
+ storePreparedProjectCSS,
14
+ } from "./prepared-project-css-cache.js";
15
+ import type { StyleScopeProfile } from "./style-scope-profile.js";
11
16
 
12
17
  const logger = serverLogger.component("css-pregeneration");
13
18
 
14
19
  interface CSSPregenerationOptions {
15
20
  /** Project slug for cache keying */
16
21
  projectSlug: string;
22
+ /** Current content version for the prepared stylesheet artifact */
23
+ projectVersion: string;
24
+ /** Project root used for style scope filtering */
25
+ projectDir?: string;
17
26
  /** List of files with content to extract candidates from */
18
27
  files: Array<{ path: string; content?: string }>;
28
+ /** Style scope profile for convention-based filtering */
29
+ styleProfile: StyleScopeProfile;
19
30
  /** Optional custom stylesheet (globals.css content) */
20
31
  stylesheet?: string;
21
32
  /** Optional stylesheet path (from config) to locate content in files */
22
33
  stylesheetPath?: string;
23
34
  /** Enable minification (default: true) */
24
35
  minify?: boolean;
36
+ /** Environment segment used for prepared artifact cache partitioning */
37
+ environment?: string;
38
+ /** Build mode recorded in the prepared artifact profile */
39
+ buildMode?: "development" | "production";
25
40
  }
26
41
 
27
42
  /**
@@ -39,34 +54,56 @@ interface CSSPregenerationOptions {
39
54
  export async function pregenerateCSSFromFiles(
40
55
  options: CSSPregenerationOptions,
41
56
  ): Promise<void> {
42
- const { projectSlug, files, stylesheet, stylesheetPath, minify = true } = options;
57
+ const {
58
+ projectSlug,
59
+ projectVersion,
60
+ projectDir,
61
+ files,
62
+ styleProfile,
63
+ stylesheet,
64
+ stylesheetPath,
65
+ minify = true,
66
+ environment = "preview",
67
+ buildMode = "production",
68
+ } = options;
43
69
  const startTime = performance.now();
44
70
 
45
71
  try {
46
- const candidates = extractCandidatesFromFiles(files);
47
-
48
- if (candidates.size === 0) {
49
- logger.debug("No candidates found, skipping", {
50
- projectSlug,
51
- fileCount: files.length,
52
- });
53
- return;
54
- }
55
-
56
72
  const resolvedStylesheet = stylesheet ?? findStylesheetFromFiles(files, stylesheetPath);
73
+ const candidates = extractCandidatesFromFiles(files, {
74
+ projectDir,
75
+ styleProfile,
76
+ });
57
77
 
58
78
  logger.debug("Starting", {
59
79
  projectSlug,
80
+ projectVersion,
60
81
  fileCount: files.length,
61
82
  candidateCount: candidates.size,
62
83
  hasStylesheet: Boolean(resolvedStylesheet),
84
+ styleProfileHash: styleProfile.hash,
63
85
  });
64
86
 
65
- const result = await getProjectCSS(projectSlug, resolvedStylesheet, candidates, { minify });
87
+ const result = await getProjectCSS(projectSlug, resolvedStylesheet, candidates, {
88
+ minify,
89
+ environment,
90
+ buildMode,
91
+ });
92
+ await storePreparedProjectCSS(
93
+ createPreparedProjectCSSContext(
94
+ projectSlug,
95
+ projectVersion,
96
+ resolvedStylesheet,
97
+ styleProfile.hash,
98
+ { minify, environment, buildMode },
99
+ ),
100
+ { css: result.css, hash: result.hash },
101
+ );
66
102
  const duration = performance.now() - startTime;
67
103
 
68
104
  logger.debug("Complete", {
69
105
  projectSlug,
106
+ projectVersion,
70
107
  candidateCount: candidates.size,
71
108
  cssLength: result.css.length,
72
109
  cssHash: result.hash,