veryfront 0.1.83 → 0.1.85

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 (56) hide show
  1. package/esm/cli/commands/knowledge/command.d.ts +10 -13
  2. package/esm/cli/commands/knowledge/command.d.ts.map +1 -1
  3. package/esm/cli/commands/knowledge/command.js +294 -110
  4. package/esm/cli/commands/knowledge/parser-source.d.ts.map +1 -1
  5. package/esm/cli/commands/knowledge/parser-source.js +52 -0
  6. package/esm/cli/commands/knowledge/result.d.ts +54 -0
  7. package/esm/cli/commands/knowledge/result.d.ts.map +1 -0
  8. package/esm/cli/commands/knowledge/result.js +22 -0
  9. package/esm/cli/commands/knowledge/source-policy.d.ts +11 -0
  10. package/esm/cli/commands/knowledge/source-policy.d.ts.map +1 -0
  11. package/esm/cli/commands/knowledge/source-policy.js +135 -0
  12. package/esm/deno.js +1 -1
  13. package/esm/src/jobs/index.d.ts +1 -1
  14. package/esm/src/jobs/index.d.ts.map +1 -1
  15. package/esm/src/jobs/index.js +1 -1
  16. package/esm/src/jobs/schemas.d.ts +1104 -185
  17. package/esm/src/jobs/schemas.d.ts.map +1 -1
  18. package/esm/src/jobs/schemas.js +81 -8
  19. package/esm/src/proxy/error-response.d.ts +7 -0
  20. package/esm/src/proxy/error-response.d.ts.map +1 -0
  21. package/esm/src/proxy/error-response.js +26 -0
  22. package/esm/src/proxy/handler.d.ts.map +1 -1
  23. package/esm/src/proxy/handler.js +25 -0
  24. package/esm/src/proxy/main.js +2 -23
  25. package/esm/src/rendering/orchestrator/css-candidate-manifest.d.ts +11 -0
  26. package/esm/src/rendering/orchestrator/css-candidate-manifest.d.ts.map +1 -1
  27. package/esm/src/rendering/orchestrator/css-candidate-manifest.js +23 -12
  28. package/esm/src/server/handlers/dev/styles-candidate-scanner.d.ts.map +1 -1
  29. package/esm/src/server/handlers/dev/styles-candidate-scanner.js +25 -8
  30. package/esm/src/server/handlers/dev/styles-css.handler.d.ts +1 -0
  31. package/esm/src/server/handlers/dev/styles-css.handler.d.ts.map +1 -1
  32. package/esm/src/server/handlers/dev/styles-css.handler.js +15 -5
  33. package/esm/src/transforms/esm/http-cache-helpers.d.ts.map +1 -1
  34. package/esm/src/transforms/esm/http-cache-helpers.js +7 -1
  35. package/esm/src/transforms/import-rewriter/strategies/bare-strategy.d.ts.map +1 -1
  36. package/esm/src/transforms/import-rewriter/strategies/bare-strategy.js +11 -8
  37. package/esm/src/transforms/shared/package-specifier.d.ts +7 -0
  38. package/esm/src/transforms/shared/package-specifier.d.ts.map +1 -0
  39. package/esm/src/transforms/shared/package-specifier.js +19 -0
  40. package/package.json +1 -1
  41. package/src/cli/commands/knowledge/command.ts +375 -139
  42. package/src/cli/commands/knowledge/parser-source.ts +52 -0
  43. package/src/cli/commands/knowledge/result.ts +88 -0
  44. package/src/cli/commands/knowledge/source-policy.ts +164 -0
  45. package/src/deno.js +1 -1
  46. package/src/src/jobs/index.ts +16 -0
  47. package/src/src/jobs/schemas.ts +105 -8
  48. package/src/src/proxy/error-response.ts +33 -0
  49. package/src/src/proxy/handler.ts +43 -0
  50. package/src/src/proxy/main.ts +2 -27
  51. package/src/src/rendering/orchestrator/css-candidate-manifest.ts +40 -14
  52. package/src/src/server/handlers/dev/styles-candidate-scanner.ts +37 -11
  53. package/src/src/server/handlers/dev/styles-css.handler.ts +25 -4
  54. package/src/src/transforms/esm/http-cache-helpers.ts +12 -1
  55. package/src/src/transforms/import-rewriter/strategies/bare-strategy.ts +11 -12
  56. package/src/src/transforms/shared/package-specifier.ts +29 -0
@@ -14,6 +14,53 @@ def yaml_quote(value: Any) -> str:
14
14
 
15
15
 
16
16
  CODE_FENCE = chr(96) * 3
17
+ TEXT_FILE_EXTENSIONS = {
18
+ ".c",
19
+ ".cc",
20
+ ".conf",
21
+ ".cpp",
22
+ ".cs",
23
+ ".css",
24
+ ".go",
25
+ ".h",
26
+ ".hpp",
27
+ ".ini",
28
+ ".java",
29
+ ".js",
30
+ ".jsonl",
31
+ ".jsx",
32
+ ".kt",
33
+ ".less",
34
+ ".lua",
35
+ ".mjs",
36
+ ".ndjson",
37
+ ".php",
38
+ ".pl",
39
+ ".py",
40
+ ".r",
41
+ ".rb",
42
+ ".rs",
43
+ ".sass",
44
+ ".scala",
45
+ ".scss",
46
+ ".sh",
47
+ ".sql",
48
+ ".swift",
49
+ ".toml",
50
+ ".ts",
51
+ ".tsx",
52
+ ".xml",
53
+ ".yaml",
54
+ ".yml",
55
+ ".zsh",
56
+ }
57
+ TEXT_FILE_NAMES = {
58
+ "dockerfile",
59
+ "makefile",
60
+ "readme",
61
+ "license",
62
+ "changelog",
63
+ }
17
64
 
18
65
 
19
66
  def slugify(value: str) -> str:
@@ -406,6 +453,7 @@ def parse_json(path: str):
406
453
 
407
454
  def select_parser(path: Path):
408
455
  ext = path.suffix.lower()
456
+ name = path.name.lower()
409
457
  if ext == ".pdf":
410
458
  return "pdf", prefer_kreuzberg("pdf", parse_pdf)
411
459
  if ext in {".csv", ".tsv"}:
@@ -424,6 +472,10 @@ def select_parser(path: Path):
424
472
  return ext.lstrip("."), parse_text
425
473
  if ext == ".json":
426
474
  return "json", parse_json
475
+ if ext in TEXT_FILE_EXTENSIONS:
476
+ return ext.lstrip("."), parse_text
477
+ if not ext and name in TEXT_FILE_NAMES:
478
+ return "text", parse_text
427
479
  raise ValueError(f"Unsupported file type: {ext}")
428
480
 
429
481
 
@@ -0,0 +1,88 @@
1
+ export type KnowledgeIngestSkipReason =
2
+ | "hidden_path"
3
+ | "ignored_directory"
4
+ | "unsupported_file_type";
5
+
6
+ export type KnowledgeIngestFailureReason =
7
+ | "parser_error"
8
+ | "upload_error";
9
+
10
+ export interface KnowledgeIngestFileResult {
11
+ source: string;
12
+ localSourcePath: string;
13
+ outputPath: string;
14
+ remotePath: string;
15
+ slug: string;
16
+ sourceType: string;
17
+ summary: string;
18
+ stats: Record<string, unknown>;
19
+ warnings: string[];
20
+ }
21
+
22
+ export interface KnowledgeIngestSkippedFileResult {
23
+ source: string;
24
+ localSourcePath: string | null;
25
+ reason: KnowledgeIngestSkipReason;
26
+ message: string;
27
+ }
28
+
29
+ export interface KnowledgeIngestFailedFileResult {
30
+ source: string;
31
+ localSourcePath: string;
32
+ reason: KnowledgeIngestFailureReason;
33
+ message: string;
34
+ }
35
+
36
+ export interface KnowledgeIngestSummary {
37
+ requested_count: number;
38
+ ingested_count: number;
39
+ skipped_count: number;
40
+ failed_count: number;
41
+ }
42
+
43
+ export interface KnowledgeIngestResultMetadata {
44
+ requested_count: number;
45
+ source_mode: "explicit_sources" | "path_prefix";
46
+ knowledge_path: string;
47
+ }
48
+
49
+ export interface KnowledgeIngestJobResult {
50
+ kind: "knowledge_ingest";
51
+ version: 1;
52
+ metadata: KnowledgeIngestResultMetadata;
53
+ summary: KnowledgeIngestSummary;
54
+ ingested: KnowledgeIngestFileResult[];
55
+ skipped: KnowledgeIngestSkippedFileResult[];
56
+ failed: KnowledgeIngestFailedFileResult[];
57
+ }
58
+
59
+ export function buildKnowledgeIngestJobResult(input: {
60
+ requestedCount: number;
61
+ sourceMode: KnowledgeIngestResultMetadata["source_mode"];
62
+ knowledgePath: string;
63
+ ingested: KnowledgeIngestFileResult[];
64
+ skipped?: KnowledgeIngestSkippedFileResult[];
65
+ failed?: KnowledgeIngestFailedFileResult[];
66
+ }): KnowledgeIngestJobResult {
67
+ const skipped = input.skipped ?? [];
68
+ const failed = input.failed ?? [];
69
+
70
+ return {
71
+ kind: "knowledge_ingest",
72
+ version: 1,
73
+ metadata: {
74
+ requested_count: input.requestedCount,
75
+ source_mode: input.sourceMode,
76
+ knowledge_path: input.knowledgePath,
77
+ },
78
+ summary: {
79
+ requested_count: input.requestedCount,
80
+ ingested_count: input.ingested.length,
81
+ skipped_count: skipped.length,
82
+ failed_count: failed.length,
83
+ },
84
+ ingested: input.ingested,
85
+ skipped,
86
+ failed,
87
+ };
88
+ }
@@ -0,0 +1,164 @@
1
+ import { basename, extname, normalize } from "../../../src/platform/compat/path/index.js";
2
+ import type { KnowledgeIngestSkipReason } from "./result.js";
3
+
4
+ const RICH_FILE_EXTENSIONS = new Set([
5
+ ".pdf",
6
+ ".csv",
7
+ ".tsv",
8
+ ".docx",
9
+ ".xlsx",
10
+ ".xls",
11
+ ".pptx",
12
+ ".html",
13
+ ".htm",
14
+ ".txt",
15
+ ".json",
16
+ ".md",
17
+ ".mdx",
18
+ ]);
19
+
20
+ const TEXT_FALLBACK_EXTENSIONS = new Set([
21
+ ".c",
22
+ ".cc",
23
+ ".conf",
24
+ ".cpp",
25
+ ".cs",
26
+ ".css",
27
+ ".go",
28
+ ".h",
29
+ ".hpp",
30
+ ".ini",
31
+ ".java",
32
+ ".js",
33
+ ".jsonl",
34
+ ".jsx",
35
+ ".kt",
36
+ ".less",
37
+ ".lua",
38
+ ".mjs",
39
+ ".ndjson",
40
+ ".php",
41
+ ".pl",
42
+ ".py",
43
+ ".r",
44
+ ".rb",
45
+ ".rs",
46
+ ".sass",
47
+ ".scala",
48
+ ".scss",
49
+ ".sh",
50
+ ".sql",
51
+ ".swift",
52
+ ".toml",
53
+ ".tsx",
54
+ ".ts",
55
+ ".xml",
56
+ ".yaml",
57
+ ".yml",
58
+ ".zsh",
59
+ ]);
60
+
61
+ const TEXT_FALLBACK_FILENAMES = new Set([
62
+ "dockerfile",
63
+ "makefile",
64
+ "readme",
65
+ "license",
66
+ "changelog",
67
+ ]);
68
+
69
+ const IGNORED_DIRECTORY_NAMES = new Set([
70
+ "node_modules",
71
+ ]);
72
+
73
+ export type KnowledgeIngestSourceDecision =
74
+ | { kind: "ingest" }
75
+ | {
76
+ kind: "skip";
77
+ reason: KnowledgeIngestSkipReason;
78
+ message: string;
79
+ };
80
+
81
+ function normalizePathSegments(value: string): string[] {
82
+ return normalize(value)
83
+ .replaceAll("\\", "/")
84
+ .split("/")
85
+ .map((segment) => segment.trim())
86
+ .filter((segment) => segment.length > 0 && segment !== "." && segment !== "..");
87
+ }
88
+
89
+ function findHiddenSegment(segments: string[]): string | null {
90
+ for (const segment of segments) {
91
+ if (segment.startsWith(".")) {
92
+ return segment;
93
+ }
94
+ }
95
+
96
+ return null;
97
+ }
98
+
99
+ function findIgnoredDirectory(segments: string[]): string | null {
100
+ for (const segment of segments) {
101
+ if (IGNORED_DIRECTORY_NAMES.has(segment.toLowerCase())) {
102
+ return segment;
103
+ }
104
+ }
105
+
106
+ return null;
107
+ }
108
+
109
+ function isSupportedKnowledgeFile(path: string): boolean {
110
+ const extension = extname(path).toLowerCase();
111
+ if (
112
+ extension && (RICH_FILE_EXTENSIONS.has(extension) || TEXT_FALLBACK_EXTENSIONS.has(extension))
113
+ ) {
114
+ return true;
115
+ }
116
+
117
+ const fileName = basename(path).toLowerCase();
118
+ return TEXT_FALLBACK_FILENAMES.has(fileName);
119
+ }
120
+
121
+ function classifyCommonKnowledgePath(path: string): KnowledgeIngestSourceDecision | null {
122
+ const segments = normalizePathSegments(path);
123
+ const hiddenSegment = findHiddenSegment(segments);
124
+ if (hiddenSegment != null) {
125
+ return {
126
+ kind: "skip",
127
+ reason: "hidden_path",
128
+ message: `Hidden file or directory skipped: ${hiddenSegment}`,
129
+ };
130
+ }
131
+
132
+ const ignoredDirectory = findIgnoredDirectory(segments);
133
+ if (ignoredDirectory != null) {
134
+ return {
135
+ kind: "skip",
136
+ reason: "ignored_directory",
137
+ message: `Ignored directory skipped: ${ignoredDirectory}`,
138
+ };
139
+ }
140
+
141
+ return null;
142
+ }
143
+
144
+ export function classifyKnowledgeDirectoryPath(path: string): KnowledgeIngestSourceDecision {
145
+ return classifyCommonKnowledgePath(path) ?? { kind: "ingest" };
146
+ }
147
+
148
+ export function classifyKnowledgeSourcePath(path: string): KnowledgeIngestSourceDecision {
149
+ const commonDecision = classifyCommonKnowledgePath(path);
150
+ if (commonDecision != null) {
151
+ return commonDecision;
152
+ }
153
+
154
+ if (!isSupportedKnowledgeFile(path)) {
155
+ const extension = extname(path).toLowerCase();
156
+ return {
157
+ kind: "skip",
158
+ reason: "unsupported_file_type",
159
+ message: `Unsupported file type: ${extension || basename(path)}`,
160
+ };
161
+ }
162
+
163
+ return { kind: "ingest" };
164
+ }
package/src/deno.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  "name": "veryfront",
3
- "version": "0.1.83",
3
+ "version": "0.1.85",
4
4
  "license": "Apache-2.0",
5
5
  "nodeModulesDir": "auto",
6
6
  "exclude": [
@@ -60,10 +60,16 @@ export {
60
60
  JobEventSchema,
61
61
  type JobEventsResponse,
62
62
  JobEventsResponseSchema,
63
+ type JobKind,
64
+ JobKindSchema,
65
+ type JobListItem,
66
+ JobListItemSchema,
63
67
  type JobLogsResponse,
64
68
  JobLogsResponseSchema,
65
69
  type JobResult,
66
70
  JobResultSchema,
71
+ type JobResultSummary,
72
+ JobResultSummarySchema,
67
73
  JobSchema,
68
74
  type JobStatus,
69
75
  JobStatusSchema,
@@ -73,8 +79,18 @@ export {
73
79
  JobTargetDefinitionsResponseSchema,
74
80
  type KnowledgeIngestBatchSource,
75
81
  KnowledgeIngestBatchSourceSchema,
82
+ type KnowledgeIngestBatchSourceWithMessage,
83
+ KnowledgeIngestBatchSourceWithMessageSchema,
84
+ type KnowledgeIngestFailedFileResult,
85
+ KnowledgeIngestFailedFileResultSchema,
76
86
  type KnowledgeIngestFileResult,
77
87
  KnowledgeIngestFileResultSchema,
88
+ type KnowledgeIngestJobResult,
89
+ KnowledgeIngestJobResultCountsSchema,
90
+ KnowledgeIngestJobResultMetadataSchema,
91
+ KnowledgeIngestJobResultSchema,
92
+ type KnowledgeIngestSkippedFileResult,
93
+ KnowledgeIngestSkippedFileResultSchema,
78
94
  PageInfoSchema,
79
95
  type PaginatedCronJobsResponse,
80
96
  PaginatedCronJobsResponseSchema,
@@ -9,6 +9,7 @@ export const JobStatusSchema = z.enum([
9
9
  ]);
10
10
 
11
11
  export const CronJobStatusSchema = z.enum(["active", "paused", "deleting"]);
12
+ export const JobKindSchema = z.enum(["knowledge_ingest"]).nullable();
12
13
 
13
14
  export const ReservedJobTargetFamilySchema = z.enum(["task:*", "workflow:*", "deploy:*"]);
14
15
 
@@ -20,6 +21,12 @@ export const PageInfoSchema = z.object({
20
21
  });
21
22
 
22
23
  const JsonObjectSchema = z.record(z.unknown());
24
+ const KnowledgeIngestSkipReasonSchema = z.enum([
25
+ "hidden_path",
26
+ "ignored_directory",
27
+ "unsupported_file_type",
28
+ ]);
29
+ const KnowledgeIngestFailureReasonSchema = z.enum(["parser_error", "upload_error"]);
23
30
 
24
31
  export const KnowledgeIngestFileResultSchema = z.object({
25
32
  source: z.string(),
@@ -33,27 +40,82 @@ export const KnowledgeIngestFileResultSchema = z.object({
33
40
  warnings: z.array(z.string()),
34
41
  });
35
42
 
43
+ export const KnowledgeIngestSkippedFileResultSchema = z.object({
44
+ source: z.string(),
45
+ localSourcePath: z.string().nullable(),
46
+ reason: KnowledgeIngestSkipReasonSchema,
47
+ message: z.string(),
48
+ });
49
+
50
+ export const KnowledgeIngestFailedFileResultSchema = z.object({
51
+ source: z.string(),
52
+ localSourcePath: z.string(),
53
+ reason: KnowledgeIngestFailureReasonSchema,
54
+ message: z.string(),
55
+ });
56
+
57
+ export const KnowledgeIngestJobResultMetadataSchema = z.object({
58
+ requested_count: z.number().int().nonnegative(),
59
+ source_mode: z.enum(["explicit_sources", "path_prefix"]),
60
+ knowledge_path: z.string(),
61
+ });
62
+
63
+ export const KnowledgeIngestJobResultCountsSchema = z.object({
64
+ requested_count: z.number().int().nonnegative(),
65
+ ingested_count: z.number().int().nonnegative(),
66
+ skipped_count: z.number().int().nonnegative(),
67
+ failed_count: z.number().int().nonnegative(),
68
+ });
69
+
70
+ export const KnowledgeIngestJobResultSchema = z.object({
71
+ kind: z.literal("knowledge_ingest"),
72
+ version: z.literal(1),
73
+ metadata: KnowledgeIngestJobResultMetadataSchema,
74
+ summary: KnowledgeIngestJobResultCountsSchema,
75
+ ingested: z.array(KnowledgeIngestFileResultSchema),
76
+ skipped: z.array(KnowledgeIngestSkippedFileResultSchema),
77
+ failed: z.array(KnowledgeIngestFailedFileResultSchema),
78
+ });
79
+
36
80
  export const JobResultSchema = z
81
+ .discriminatedUnion("kind", [
82
+ KnowledgeIngestJobResultSchema,
83
+ z.object({
84
+ kind: z.literal("value"),
85
+ value: z.unknown(),
86
+ }),
87
+ z.object({
88
+ kind: z.literal("artifacts"),
89
+ artifacts: z.array(z.unknown()),
90
+ }),
91
+ ])
92
+ .nullable();
93
+
94
+ export const JobResultSummarySchema = z
37
95
  .discriminatedUnion("kind", [
38
96
  z.object({
39
97
  kind: z.literal("knowledge_ingest"),
40
- files: z.array(KnowledgeIngestFileResultSchema),
98
+ state: z.enum(["success", "partial_success", "failed"]),
99
+ requested_count: z.number().int().nonnegative(),
100
+ ingested_count: z.number().int().nonnegative(),
101
+ skipped_count: z.number().int().nonnegative(),
102
+ failed_count: z.number().int().nonnegative(),
41
103
  }),
42
104
  z.object({
43
105
  kind: z.literal("value"),
44
- value: z.unknown(),
45
106
  }),
46
107
  z.object({
47
108
  kind: z.literal("artifacts"),
48
- artifacts: z.array(z.unknown()),
109
+ artifact_count: z.number().int().nonnegative(),
49
110
  }),
50
111
  ])
51
112
  .nullable();
52
113
 
53
- export const JobSchema = z.object({
114
+ const BaseJobSchema = z.object({
54
115
  id: z.string().uuid(),
55
116
  project_id: z.string().uuid(),
56
117
  environment_id: z.string().uuid().nullable(),
118
+ branch_id: z.string().nullable().optional().default(null),
57
119
  cron_job_id: z.string().uuid().nullable(),
58
120
  batch_id: z.string().uuid().nullable(),
59
121
  name: z.string(),
@@ -64,9 +126,6 @@ export const JobSchema = z.object({
64
126
  timeout_seconds: z.number(),
65
127
  backoff_limit: z.number(),
66
128
  exit_code: z.number().nullable(),
67
- failed_reason: z.string().nullable(),
68
- failure_detail: z.string().nullable(),
69
- result: JobResultSchema,
70
129
  started_at: z.string().nullable(),
71
130
  completed_at: z.string().nullable(),
72
131
  created_by: z.string().uuid().nullable(),
@@ -74,8 +133,22 @@ export const JobSchema = z.object({
74
133
  updated_at: z.string(),
75
134
  });
76
135
 
136
+ export const JobSchema = BaseJobSchema.extend({
137
+ failed_reason: z.string().nullable().optional().default(null),
138
+ kind: JobKindSchema.optional().default(null),
139
+ failure_detail: z.string().nullable().optional().default(null),
140
+ result_summary: JobResultSummarySchema.optional().default(null),
141
+ result: JobResultSchema.optional().default(null),
142
+ });
143
+
144
+ export const JobListItemSchema = BaseJobSchema.extend({
145
+ kind: JobKindSchema.optional().default(null),
146
+ failure_detail: z.string().nullable().optional().default(null),
147
+ result_summary: JobResultSummarySchema.optional().default(null),
148
+ });
149
+
77
150
  export const PaginatedJobsResponseSchema = z.object({
78
- data: z.array(JobSchema),
151
+ data: z.array(JobListItemSchema),
79
152
  page_info: PageInfoSchema,
80
153
  });
81
154
 
@@ -111,6 +184,10 @@ export const KnowledgeIngestBatchSourceSchema = z.object({
111
184
  warning_count: z.number().int().nonnegative(),
112
185
  });
113
186
 
187
+ export const KnowledgeIngestBatchSourceWithMessageSchema = KnowledgeIngestBatchSourceSchema.extend({
188
+ message: z.string(),
189
+ });
190
+
114
191
  export const JobBatchStatusCountsSchema = z.object({
115
192
  submitted: z.number().int().nonnegative(),
116
193
  working: z.number().int().nonnegative(),
@@ -125,8 +202,12 @@ export const JobBatchResultSchema = z
125
202
  kind: z.literal("knowledge_ingest"),
126
203
  total_count: z.number().int().nonnegative(),
127
204
  completed_count: z.number().int().nonnegative(),
205
+ skipped_count: z.number().int().nonnegative(),
206
+ failed_count: z.number().int().nonnegative(),
128
207
  processing: z.array(KnowledgeIngestBatchSourceSchema),
129
208
  completed: z.array(KnowledgeIngestBatchSourceSchema),
209
+ skipped: z.array(KnowledgeIngestBatchSourceWithMessageSchema),
210
+ failed: z.array(KnowledgeIngestBatchSourceWithMessageSchema),
130
211
  remaining: z.array(KnowledgeIngestBatchSourceSchema),
131
212
  remaining_label: z.enum(["Remaining Files", "Not Ingested Files"]),
132
213
  }),
@@ -161,6 +242,7 @@ export const CronJobSchema = z.object({
161
242
  id: z.string().uuid(),
162
243
  project_id: z.string().uuid(),
163
244
  environment_id: z.string().uuid().nullable(),
245
+ branch_id: z.string().nullable().optional().default(null),
164
246
  name: z.string(),
165
247
  status: CronJobStatusSchema,
166
248
  target: z.string(),
@@ -184,17 +266,32 @@ export const PaginatedCronJobsResponseSchema = z.object({
184
266
 
185
267
  export type JobStatus = z.infer<typeof JobStatusSchema>;
186
268
  export type CronJobStatus = z.infer<typeof CronJobStatusSchema>;
269
+ export type JobKind = z.infer<typeof JobKindSchema>;
187
270
  export type ReservedJobTargetFamily = z.infer<typeof ReservedJobTargetFamilySchema>;
188
271
 
189
272
  export type KnowledgeIngestFileResult = z.infer<typeof KnowledgeIngestFileResultSchema>;
273
+ export type KnowledgeIngestSkippedFileResult = z.infer<
274
+ typeof KnowledgeIngestSkippedFileResultSchema
275
+ >;
276
+ export type KnowledgeIngestFailedFileResult = z.infer<typeof KnowledgeIngestFailedFileResultSchema>;
277
+ export type KnowledgeIngestJobResultMetadata = z.infer<
278
+ typeof KnowledgeIngestJobResultMetadataSchema
279
+ >;
280
+ export type KnowledgeIngestJobResultCounts = z.infer<typeof KnowledgeIngestJobResultCountsSchema>;
281
+ export type KnowledgeIngestJobResult = z.infer<typeof KnowledgeIngestJobResultSchema>;
190
282
  export type JobResult = z.infer<typeof JobResultSchema>;
283
+ export type JobResultSummary = z.infer<typeof JobResultSummarySchema>;
191
284
  export type Job = z.infer<typeof JobSchema>;
285
+ export type JobListItem = z.infer<typeof JobListItemSchema>;
192
286
  export type PaginatedJobsResponse = z.infer<typeof PaginatedJobsResponseSchema>;
193
287
  export type JobEvent = z.infer<typeof JobEventSchema>;
194
288
  export type JobEventsResponse = z.infer<typeof JobEventsResponseSchema>;
195
289
  export type JobLogsResponse = z.infer<typeof JobLogsResponseSchema>;
196
290
 
197
291
  export type KnowledgeIngestBatchSource = z.infer<typeof KnowledgeIngestBatchSourceSchema>;
292
+ export type KnowledgeIngestBatchSourceWithMessage = z.infer<
293
+ typeof KnowledgeIngestBatchSourceWithMessageSchema
294
+ >;
198
295
  export type JobBatchStatusCounts = z.infer<typeof JobBatchStatusCountsSchema>;
199
296
  export type JobBatchResult = z.infer<typeof JobBatchResultSchema>;
200
297
  export type JobBatch = z.infer<typeof JobBatchSchema>;
@@ -0,0 +1,33 @@
1
+ import * as dntShim from "../../_dnt.shims.js";
2
+ import { ErrorPages } from "../server/utils/error-html.js";
3
+ import type { ProxyContext } from "./handler.js";
4
+
5
+ type ProxyError = NonNullable<ProxyContext["error"]>;
6
+
7
+ export function jsonErrorResponse(status: number, body: Record<string, unknown>): dntShim.Response {
8
+ return new dntShim.Response(JSON.stringify(body), {
9
+ status,
10
+ headers: { "Content-Type": "application/json" },
11
+ });
12
+ }
13
+
14
+ export function createProxyErrorResponse(error: ProxyError): dntShim.Response {
15
+ if (error.redirectUrl) {
16
+ return new dntShim.Response(null, {
17
+ status: 302,
18
+ headers: { Location: error.redirectUrl },
19
+ });
20
+ }
21
+
22
+ if (error.slug === "release-not-found" || error.slug === "project-not-found") {
23
+ return new dntShim.Response(ErrorPages.notFound(), {
24
+ status: 404,
25
+ headers: { "Content-Type": "text/html; charset=utf-8" },
26
+ });
27
+ }
28
+
29
+ return jsonErrorResponse(error.status, {
30
+ error: error.message,
31
+ status: error.status,
32
+ });
33
+ }
@@ -175,6 +175,12 @@ function extractUserToken(cookieHeader: string): string | undefined {
175
175
  return match?.[1] ? decodeURIComponent(match[1]) : undefined;
176
176
  }
177
177
 
178
+ function getStatusFromError(error: unknown): number | null {
179
+ const message = error instanceof Error ? error.message : String(error);
180
+ const match = message.match(/failed: (\d+)/);
181
+ return match ? Number(match[1]) : null;
182
+ }
183
+
178
184
  async function extractUserIdFromToken(
179
185
  token: string,
180
186
  apiBaseUrl: string,
@@ -461,6 +467,7 @@ export function createProxyHandler(options: ProxyHandlerOptions) {
461
467
  const userToken = extractUserToken(cookieHeader);
462
468
 
463
469
  let token: string | undefined;
470
+ let tokenFetchError: unknown;
464
471
 
465
472
  if (isLocalProject) {
466
473
  logger?.debug("Local project, skipping token fetch", { localPath });
@@ -476,6 +483,7 @@ export function createProxyHandler(options: ProxyHandlerOptions) {
476
483
  try {
477
484
  token = await tokenManager.getToken(scope, projectSlug, customDomain);
478
485
  } catch (error) {
486
+ tokenFetchError = error;
479
487
  logger?.error("Token fetch failed", error as Error, { projectSlug, customDomain });
480
488
  }
481
489
  }
@@ -488,6 +496,29 @@ export function createProxyHandler(options: ProxyHandlerOptions) {
488
496
 
489
497
  const base = { scope, host, parsedDomain };
490
498
 
499
+ if (projectSlug && scope === "preview" && !token) {
500
+ const status = getStatusFromError(tokenFetchError);
501
+ if (status === 404) {
502
+ logger?.info("Preview project not found", { projectSlug, host });
503
+ return makeErrorContext(
504
+ base,
505
+ 404,
506
+ "Preview project not found",
507
+ undefined,
508
+ undefined,
509
+ "project-not-found",
510
+ );
511
+ }
512
+
513
+ logger?.warn("Preview request has no usable token", {
514
+ projectSlug,
515
+ host,
516
+ hadUserToken: !!userToken,
517
+ hadTokenFetchError: !!tokenFetchError,
518
+ });
519
+ return makeErrorContext(base, 502, "Failed to authenticate preview request");
520
+ }
521
+
491
522
  if (isCustomDomain && !projectSlug) {
492
523
  if (!token) {
493
524
  logger?.error("Cannot process custom domain without token", undefined, { domain: host });
@@ -590,6 +621,18 @@ export function createProxyHandler(options: ProxyHandlerOptions) {
590
621
  );
591
622
  }
592
623
 
624
+ if (!resolved.projectId) {
625
+ logger?.info("Preview project not found after lookup", { projectSlug, host });
626
+ return makeErrorContext(
627
+ base,
628
+ 404,
629
+ "Preview project not found",
630
+ token,
631
+ undefined,
632
+ "project-not-found",
633
+ );
634
+ }
635
+
593
636
  projectId = resolved.projectId;
594
637
  environmentId = resolved.environmentId;
595
638