@vedangiitb/qwintly-core 1.4.4 → 1.4.6
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/dist/image/unsplash.service.d.ts.map +1 -1
- package/dist/image/unsplash.service.js +6 -3
- package/dist/image/unsplash.service.js.map +1 -1
- package/dist/indexer/projectInfoIndex.d.ts.map +1 -1
- package/dist/indexer/projectInfoIndex.js +70 -203
- package/dist/indexer/projectInfoIndex.js.map +1 -1
- package/dist/tests/projectInfoIndex.test.d.ts +2 -0
- package/dist/tests/projectInfoIndex.test.d.ts.map +1 -0
- package/dist/tests/projectInfoIndex.test.js +75 -0
- package/dist/tests/projectInfoIndex.test.js.map +1 -0
- package/dist/tests/unsplash.service.test.d.ts +2 -0
- package/dist/tests/unsplash.service.test.d.ts.map +1 -0
- package/dist/tests/unsplash.service.test.js +94 -0
- package/dist/tests/unsplash.service.test.js.map +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unsplash.service.d.ts","sourceRoot":"","sources":["../../src/image/unsplash.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,KAAK,cAAc,GAAG;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;
|
|
1
|
+
{"version":3,"file":"unsplash.service.d.ts","sourceRoot":"","sources":["../../src/image/unsplash.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,KAAK,cAAc,GAAG;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAsBF,wBAAgB,YAAY,CAC1B,MAAM,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,IAAI,GAAG,SAAS,QAcnD;AAoBD,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CA6E/B;AAED,eAAO,MAAM,8BAA8B,GACzC,IAAI,cAAc,EAClB,KAAK,MAAM,KACV,OAAO,CAAC,IAAI,CAuBd,CAAC;AAEF,eAAO,MAAM,yBAAyB,GACpC,IAAI,cAAc,KACjB,OAAO,CAAC,IAAI,CAcd,CAAC"}
|
|
@@ -106,15 +106,18 @@ export const resolveUnsplashImageForElement = async (el, alt) => {
|
|
|
106
106
|
}
|
|
107
107
|
try {
|
|
108
108
|
const resolved = await searchUnsplashImage(query);
|
|
109
|
-
if (!resolved?.imageUrl)
|
|
109
|
+
if (!resolved?.imageUrl) {
|
|
110
|
+
console.log(resolved);
|
|
110
111
|
return;
|
|
112
|
+
}
|
|
111
113
|
const anyEl = el;
|
|
112
114
|
if (!anyEl.props || typeof anyEl.props !== "object")
|
|
113
115
|
anyEl.props = {};
|
|
114
116
|
anyEl.props.src = resolved.imageUrl;
|
|
115
117
|
anyEl.props.alt = query;
|
|
116
118
|
}
|
|
117
|
-
catch {
|
|
119
|
+
catch (error) {
|
|
120
|
+
console.error(error);
|
|
118
121
|
// Ignore Unsplash lookup errors and still allow prop updates.
|
|
119
122
|
}
|
|
120
123
|
};
|
|
@@ -122,7 +125,7 @@ export const resolveUnsplashImagesDeep = async (el) => {
|
|
|
122
125
|
const anyEl = el;
|
|
123
126
|
if (anyEl?.type === "image") {
|
|
124
127
|
const alt = String(anyEl?.props?.alt ?? "").trim();
|
|
125
|
-
resolveUnsplashImageForElement(el, alt);
|
|
128
|
+
await resolveUnsplashImageForElement(el, alt);
|
|
126
129
|
}
|
|
127
130
|
const kids = anyEl?.children;
|
|
128
131
|
if (Array.isArray(kids)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unsplash.service.js","sourceRoot":"","sources":["../../src/image/unsplash.service.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAoB5B,IAAI,cAAc,GAA0B,IAAI,CAAC;AAEjD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAyB,CAAC;AAE/C,KAAK,UAAU,gBAAgB,
|
|
1
|
+
{"version":3,"file":"unsplash.service.js","sourceRoot":"","sources":["../../src/image/unsplash.service.ts"],"names":[],"mappings":"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","sourcesContent":["import crypto from \"crypto\";\r\nimport { BuilderElement } from \"../types/elements.js\";\r\n\r\nexport interface ResolvedImage {\r\n id: string;\r\n alt: string;\r\n imageUrl: string;\r\n thumbUrl: string;\r\n photographer: string;\r\n photographerUrl: string;\r\n unsplashUrl: string;\r\n width: number;\r\n height: number;\r\n}\r\n\r\ntype UnsplashConfig = {\r\n url: string;\r\n accessKey: string;\r\n};\r\n\r\nlet unsplashConfig: UnsplashConfig | null = null;\r\n\r\nconst cache = new Map<string, ResolvedImage>();\r\n\r\nasync function fetchWithTimeout(\r\n url: string,\r\n options: RequestInit = {},\r\n): Promise<Response> {\r\n const controller = new AbortController();\r\n const id = setTimeout(() => controller.abort(), 5000);\r\n try {\r\n return await fetch(url, {\r\n ...options,\r\n signal: controller.signal,\r\n });\r\n } finally {\r\n clearTimeout(id);\r\n }\r\n}\r\n\r\nexport function initUnsplash(\r\n config: Partial<UnsplashConfig> | null | undefined,\r\n) {\r\n const url = String(config?.url ?? \"\").trim();\r\n const accessKey = String(config?.accessKey ?? \"\").trim();\r\n\r\n if (!url || !accessKey) {\r\n unsplashConfig = null;\r\n return;\r\n }\r\n\r\n unsplashConfig = {\r\n url: url.replace(/\\/+$/, \"\"),\r\n accessKey,\r\n };\r\n}\r\n\r\nfunction hashIntent(imgQuery: string) {\r\n return crypto.createHash(\"sha256\").update(imgQuery).digest(\"hex\");\r\n}\r\n\r\nfunction scoreImage(photo: any) {\r\n let score = 0;\r\n\r\n score += (photo.likes || 0) * 0.3;\r\n\r\n if (photo.width > photo.height) {\r\n score += 50;\r\n }\r\n\r\n score += (photo.user.total_photos || 0) * 0.05;\r\n\r\n return score;\r\n}\r\n\r\nexport async function searchUnsplashImage(\r\n imgQuery: string,\r\n): Promise<ResolvedImage | null> {\r\n console.log(\"searchUnsplashImage\", imgQuery);\r\n const cfg = unsplashConfig;\r\n if (!cfg) throw new Error(\"Unsplash not configured\");\r\n\r\n const cacheKey = hashIntent(imgQuery);\r\n\r\n const cached = cache.get(cacheKey);\r\n\r\n if (cached) {\r\n return cached;\r\n }\r\n\r\n const params = new URLSearchParams({\r\n imgQuery,\r\n per_page: \"10\",\r\n });\r\n\r\n const response = await fetchWithTimeout(\r\n `${cfg.url}/search/photos?${params.toString()}`,\r\n {\r\n headers: {\r\n Authorization: `Client-ID ${cfg.accessKey}`,\r\n },\r\n },\r\n );\r\n\r\n if (!response.ok) {\r\n throw new Error(`Unsplash API failed: ${response.status}`);\r\n }\r\n\r\n const data = await response.json();\r\n\r\n const photos = data.results;\r\n\r\n if (!photos.length) {\r\n return null;\r\n }\r\n\r\n const ranked = [...photos].sort((a, b) => {\r\n return scoreImage(b) - scoreImage(a);\r\n });\r\n\r\n const top3 = ranked.slice(0, 3);\r\n\r\n const selected = top3[Math.floor(Math.random() * top3.length)];\r\n\r\n // IMPORTANT:\r\n // Required by Unsplash API terms\r\n // Tracks image download usage\r\n if (selected.links.download_location) {\r\n await fetchWithTimeout(selected.links.download_location, {\r\n headers: {\r\n Authorization: `Client-ID ${cfg.accessKey}`,\r\n },\r\n });\r\n }\r\n\r\n const resolved: ResolvedImage = {\r\n id: selected.id,\r\n alt: imgQuery,\r\n\r\n imageUrl: selected.urls.regular,\r\n thumbUrl: selected.urls.small,\r\n\r\n photographer: selected.user.name,\r\n photographerUrl: selected.user.links.html,\r\n\r\n unsplashUrl: selected.links.html,\r\n\r\n width: selected.width,\r\n height: selected.height,\r\n };\r\n\r\n cache.set(cacheKey, resolved);\r\n\r\n return resolved;\r\n}\r\n\r\nexport const resolveUnsplashImageForElement = async (\r\n el: BuilderElement,\r\n alt: string,\r\n): Promise<void> => {\r\n if (!unsplashConfig) return;\r\n const query = String(alt ?? \"\").trim();\r\n if (!query) return;\r\n if (el.props?.src) {\r\n console.log(\"src already exists, skipping\");\r\n return;\r\n }\r\n try {\r\n const resolved = await searchUnsplashImage(query);\r\n if (!resolved?.imageUrl) {\r\n console.log(resolved);\r\n return;\r\n }\r\n\r\n const anyEl = el as any;\r\n if (!anyEl.props || typeof anyEl.props !== \"object\") anyEl.props = {};\r\n anyEl.props.src = resolved.imageUrl;\r\n anyEl.props.alt = query;\r\n } catch (error) {\r\n console.error(error);\r\n // Ignore Unsplash lookup errors and still allow prop updates.\r\n }\r\n};\r\n\r\nexport const resolveUnsplashImagesDeep = async (\r\n el: BuilderElement,\r\n): Promise<void> => {\r\n const anyEl = el as any;\r\n\r\n if (anyEl?.type === \"image\") {\r\n const alt = String(anyEl?.props?.alt ?? \"\").trim();\r\n await resolveUnsplashImageForElement(el, alt);\r\n }\r\n\r\n const kids = anyEl?.children;\r\n if (Array.isArray(kids)) {\r\n for (const child of kids) {\r\n await resolveUnsplashImagesDeep(child);\r\n }\r\n }\r\n};\r\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"projectInfoIndex.d.ts","sourceRoot":"","sources":["../../src/indexer/projectInfoIndex.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"projectInfoIndex.d.ts","sourceRoot":"","sources":["../../src/indexer/projectInfoIndex.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAyF5D,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,WAAW,CAAC,CAkDtB"}
|
|
@@ -1,19 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { getAvailableRoutes, getPageConfigJsonPath, } from "../ai/tools/helpers/pageConfigJson.helpers.js";
|
|
2
2
|
import { readFile, safeReadDir } from "../utils/workspace.js";
|
|
3
|
-
const isRouteGroup = (segment) => segment.startsWith("(") && segment.endsWith(")");
|
|
4
|
-
const isParallelRoute = (segment) => segment.startsWith("@");
|
|
5
|
-
const toPosixPath = (p) => p.replace(/\\/g, "/");
|
|
6
|
-
const extractQuotedString = (match) => {
|
|
7
|
-
if (!match)
|
|
8
|
-
return "";
|
|
9
|
-
return String(match[1] ?? match[2] ?? match[3] ?? "").trim();
|
|
10
|
-
};
|
|
11
|
-
const extractPropString = (objText, prop) => {
|
|
12
|
-
const re = prop === "id"
|
|
13
|
-
? /\bid\s*:\s*(?:"([^"]+)"|'([^']+)'|`([^`]+)`)/
|
|
14
|
-
: /\btype\s*:\s*(?:"([^"]+)"|'([^']+)'|`([^`]+)`)/;
|
|
15
|
-
return extractQuotedString(objText.match(re));
|
|
16
|
-
};
|
|
17
3
|
const sectionNameFromId = (id) => {
|
|
18
4
|
if (!id)
|
|
19
5
|
return "";
|
|
@@ -23,216 +9,97 @@ const sectionNameFromId = (id) => {
|
|
|
23
9
|
return id.slice(0, -"-container".length);
|
|
24
10
|
return id;
|
|
25
11
|
};
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const objectStartStack = [];
|
|
31
|
-
let inString = null;
|
|
32
|
-
for (let i = 0; i < arrayText.length; i++) {
|
|
33
|
-
const ch = arrayText[i];
|
|
34
|
-
if (inString) {
|
|
35
|
-
if (inString !== "`" && ch === "\\") {
|
|
36
|
-
i += 1;
|
|
37
|
-
continue;
|
|
38
|
-
}
|
|
39
|
-
if (ch === inString)
|
|
40
|
-
inString = null;
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
if (ch === '"' || ch === "'" || ch === "`") {
|
|
44
|
-
inString = ch;
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
if (ch === "[") {
|
|
48
|
-
bracketDepth += 1;
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
if (ch === "]") {
|
|
52
|
-
bracketDepth -= 1;
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
if (ch === "{") {
|
|
56
|
-
if (bracketDepth >= 1)
|
|
57
|
-
objectStartStack.push(i);
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
if (ch === "}") {
|
|
61
|
-
const start = objectStartStack.pop();
|
|
62
|
-
if (start === undefined)
|
|
63
|
-
continue;
|
|
64
|
-
// only consider objects that are direct children of the current array
|
|
65
|
-
if (bracketDepth !== 1 || objectStartStack.length !== 0)
|
|
66
|
-
continue;
|
|
67
|
-
const objText = arrayText.slice(start, i + 1);
|
|
68
|
-
const id = extractPropString(objText, "id");
|
|
69
|
-
const type = extractPropString(objText, "type");
|
|
70
|
-
if (type === "div" && id && id !== "root" && !seen.has(id)) {
|
|
71
|
-
seen.add(id);
|
|
72
|
-
results.push(id);
|
|
73
|
-
}
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return results;
|
|
12
|
+
const computePageNameFromRoute = (pageRoute) => {
|
|
13
|
+
if (pageRoute === "/")
|
|
14
|
+
return "root";
|
|
15
|
+
return pageRoute.slice(1).split("/").join("-");
|
|
78
16
|
};
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
if (childrenKey < 0)
|
|
17
|
+
const extractSectionNamesFromParsedConfig = (config) => {
|
|
18
|
+
if (!config || !Array.isArray(config.elements))
|
|
82
19
|
return [];
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
20
|
+
const elements = config.elements;
|
|
21
|
+
// Let's find the root element (where type === "div" && id === "root") recursively
|
|
22
|
+
let rootElement = null;
|
|
23
|
+
const findRoot = (els) => {
|
|
24
|
+
for (const el of els) {
|
|
25
|
+
if (el && typeof el === "object") {
|
|
26
|
+
if (el.type === "div" && el.id === "root") {
|
|
27
|
+
rootElement = el;
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (Array.isArray(el.children)) {
|
|
31
|
+
findRoot(el.children);
|
|
32
|
+
if (rootElement)
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
95
35
|
}
|
|
96
|
-
if (ch === inString)
|
|
97
|
-
inString = null;
|
|
98
|
-
continue;
|
|
99
|
-
}
|
|
100
|
-
if (ch === '"' || ch === "'" || ch === "`") {
|
|
101
|
-
inString = ch;
|
|
102
|
-
continue;
|
|
103
36
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
37
|
+
};
|
|
38
|
+
findRoot(elements);
|
|
39
|
+
if (rootElement && Array.isArray(rootElement.children)) {
|
|
40
|
+
const rootSections = [];
|
|
41
|
+
for (const child of rootElement.children) {
|
|
42
|
+
if (child &&
|
|
43
|
+
typeof child === "object" &&
|
|
44
|
+
child.type === "div" &&
|
|
45
|
+
child.id &&
|
|
46
|
+
child.id !== "root") {
|
|
47
|
+
const name = sectionNameFromId(child.id);
|
|
48
|
+
if (name) {
|
|
49
|
+
rootSections.push(name);
|
|
50
|
+
}
|
|
112
51
|
}
|
|
113
52
|
}
|
|
53
|
+
if (rootSections.length > 0) {
|
|
54
|
+
return rootSections;
|
|
55
|
+
}
|
|
114
56
|
}
|
|
115
|
-
|
|
116
|
-
};
|
|
117
|
-
const extractSectionNamesFromConfig = (content) => {
|
|
118
|
-
if (!content)
|
|
119
|
-
return [];
|
|
120
|
-
const elementsKey = content.indexOf("elements");
|
|
121
|
-
if (elementsKey < 0)
|
|
122
|
-
return [];
|
|
123
|
-
const arrayStart = content.indexOf("[", elementsKey);
|
|
124
|
-
if (arrayStart < 0)
|
|
125
|
-
return [];
|
|
57
|
+
// Fallback: collect all divs with id ending in "-section" or "-container"
|
|
126
58
|
const seen = new Set();
|
|
127
59
|
const results = [];
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
continue;
|
|
137
|
-
}
|
|
138
|
-
if (ch === inString) {
|
|
139
|
-
inString = null;
|
|
140
|
-
}
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
if (ch === '"' || ch === "'" || ch === "`") {
|
|
144
|
-
inString = ch;
|
|
145
|
-
continue;
|
|
146
|
-
}
|
|
147
|
-
if (ch === "[") {
|
|
148
|
-
bracketDepth += 1;
|
|
149
|
-
continue;
|
|
150
|
-
}
|
|
151
|
-
if (ch === "]") {
|
|
152
|
-
bracketDepth -= 1;
|
|
153
|
-
if (bracketDepth <= 0)
|
|
154
|
-
break;
|
|
155
|
-
continue;
|
|
156
|
-
}
|
|
157
|
-
if (ch === "{") {
|
|
158
|
-
if (bracketDepth >= 1) {
|
|
159
|
-
objectStartStack.push(i);
|
|
160
|
-
}
|
|
161
|
-
continue;
|
|
162
|
-
}
|
|
163
|
-
if (ch === "}") {
|
|
164
|
-
const objectStart = objectStartStack.pop();
|
|
165
|
-
if (bracketDepth >= 1 && objectStart !== undefined) {
|
|
166
|
-
const objText = content.slice(objectStart, i + 1);
|
|
167
|
-
const id = extractPropString(objText, "id");
|
|
168
|
-
const type = extractPropString(objText, "type");
|
|
169
|
-
if (type === "div" && id === "root") {
|
|
170
|
-
const rootSections = extractRootChildSectionNames(objText);
|
|
171
|
-
if (rootSections.length > 0) {
|
|
172
|
-
return rootSections;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
if (type === "div" &&
|
|
176
|
-
id &&
|
|
177
|
-
id !== "root" &&
|
|
178
|
-
(id.endsWith("-section") || id.endsWith("-container"))) {
|
|
179
|
-
const sectionName = sectionNameFromId(id);
|
|
60
|
+
const collectFallback = (els) => {
|
|
61
|
+
for (const el of els) {
|
|
62
|
+
if (el && typeof el === "object") {
|
|
63
|
+
if (el.type === "div" &&
|
|
64
|
+
el.id &&
|
|
65
|
+
el.id !== "root" &&
|
|
66
|
+
(el.id.endsWith("-section") || el.id.endsWith("-container"))) {
|
|
67
|
+
const sectionName = sectionNameFromId(el.id);
|
|
180
68
|
if (sectionName && !seen.has(sectionName)) {
|
|
181
69
|
seen.add(sectionName);
|
|
182
70
|
results.push(sectionName);
|
|
183
71
|
}
|
|
184
72
|
}
|
|
73
|
+
if (Array.isArray(el.children)) {
|
|
74
|
+
collectFallback(el.children);
|
|
75
|
+
}
|
|
185
76
|
}
|
|
186
|
-
continue;
|
|
187
77
|
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
};
|
|
191
|
-
const computePageRouteFromSegments = (segments) => {
|
|
192
|
-
const filtered = segments.filter((s) => s && !isRouteGroup(s) && !isParallelRoute(s));
|
|
193
|
-
if (filtered.length === 0)
|
|
194
|
-
return "/";
|
|
195
|
-
return `/${filtered.join("/")}`;
|
|
196
|
-
};
|
|
197
|
-
const computePageNameFromRoute = (pageRoute) => {
|
|
198
|
-
if (pageRoute === "/")
|
|
199
|
-
return "root";
|
|
200
|
-
return pageRoute.slice(1).split("/").join("-");
|
|
201
|
-
};
|
|
202
|
-
const findPageConfigFiles = async (dir) => {
|
|
203
|
-
const entries = await safeReadDir(dir);
|
|
204
|
-
const results = [];
|
|
205
|
-
for (const entry of entries) {
|
|
206
|
-
if (!entry.isDirectory() && !entry.isFile())
|
|
207
|
-
continue;
|
|
208
|
-
const fullPath = path.join(dir, entry.name);
|
|
209
|
-
if (entry.isDirectory()) {
|
|
210
|
-
results.push(...(await findPageConfigFiles(fullPath)));
|
|
211
|
-
continue;
|
|
212
|
-
}
|
|
213
|
-
if (entry.name === "page.config.ts") {
|
|
214
|
-
results.push(fullPath);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
78
|
+
};
|
|
79
|
+
collectFallback(elements);
|
|
217
80
|
return results;
|
|
218
81
|
};
|
|
219
82
|
export async function computeProjectInfo(rootDir) {
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
83
|
+
const routes = await getAvailableRoutes({
|
|
84
|
+
workspaceRoot: rootDir,
|
|
85
|
+
fs: { safeReadDir },
|
|
86
|
+
});
|
|
223
87
|
const uiPages = [];
|
|
224
|
-
for (const
|
|
225
|
-
const relFromApp = toPosixPath(path.relative(appDir, filePath));
|
|
226
|
-
const relDir = path.posix.dirname(relFromApp);
|
|
227
|
-
const segments = relDir === "." ? [] : relDir.split("/").filter(Boolean);
|
|
228
|
-
if (segments.some(isParallelRoute)) {
|
|
229
|
-
continue;
|
|
230
|
-
}
|
|
231
|
-
const pageRoute = computePageRouteFromSegments(segments);
|
|
88
|
+
for (const pageRoute of routes) {
|
|
232
89
|
const pageName = computePageNameFromRoute(pageRoute);
|
|
233
90
|
const description = `${pageName} page for this project`;
|
|
234
|
-
|
|
235
|
-
|
|
91
|
+
let sectionNames = [];
|
|
92
|
+
try {
|
|
93
|
+
const configPath = getPageConfigJsonPath(rootDir, pageRoute);
|
|
94
|
+
const content = await readFile(configPath);
|
|
95
|
+
if (content) {
|
|
96
|
+
const config = JSON.parse(content);
|
|
97
|
+
sectionNames = extractSectionNamesFromParsedConfig(config);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
// Ignore reading/parsing errors, treat as no sections
|
|
102
|
+
}
|
|
236
103
|
const page = {
|
|
237
104
|
pageRoute,
|
|
238
105
|
pageName,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"projectInfoIndex.js","sourceRoot":"","sources":["../../src/indexer/projectInfoIndex.ts"],"names":[],"mappings":"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","sourcesContent":["import path from \"node:path\";\nimport { ContextRepository } from \"../repository/context.repository.js\";\nimport { ProjectInfo } from \"../types/projectInfo.types.js\";\nimport { readFile, safeReadDir } from \"../utils/workspace.js\";\n\ntype UiPage = ProjectInfo[\"uiPages\"][number];\n\nconst isRouteGroup = (segment: string) =>\n segment.startsWith(\"(\") && segment.endsWith(\")\");\n\nconst isParallelRoute = (segment: string) => segment.startsWith(\"@\");\n\nconst toPosixPath = (p: string) => p.replace(/\\\\/g, \"/\");\n\nconst extractQuotedString = (match: RegExpMatchArray | null): string => {\n if (!match) return \"\";\n return String(match[1] ?? match[2] ?? match[3] ?? \"\").trim();\n};\n\nconst extractPropString = (objText: string, prop: \"id\" | \"type\"): string => {\n const re =\n prop === \"id\"\n ? /\\bid\\s*:\\s*(?:\"([^\"]+)\"|'([^']+)'|`([^`]+)`)/\n : /\\btype\\s*:\\s*(?:\"([^\"]+)\"|'([^']+)'|`([^`]+)`)/;\n\n return extractQuotedString(objText.match(re));\n};\n\nconst sectionNameFromId = (id: string): string => {\n if (!id) return \"\";\n if (id.endsWith(\"-section\")) return id.slice(0, -\"-section\".length);\n if (id.endsWith(\"-container\")) return id.slice(0, -\"-container\".length);\n return id;\n};\n\nconst extractDirectContainerIdsFromArrayText = (\n arrayText: string,\n): string[] => {\n const seen = new Set<string>();\n const results: string[] = [];\n\n let bracketDepth = 0;\n const objectStartStack: number[] = [];\n let inString: '\"' | \"'\" | \"`\" | null = null;\n\n for (let i = 0; i < arrayText.length; i++) {\n const ch = arrayText[i]!;\n\n if (inString) {\n if (inString !== \"`\" && ch === \"\\\\\") {\n i += 1;\n continue;\n }\n if (ch === inString) inString = null;\n continue;\n }\n\n if (ch === '\"' || ch === \"'\" || ch === \"`\") {\n inString = ch;\n continue;\n }\n\n if (ch === \"[\") {\n bracketDepth += 1;\n continue;\n }\n if (ch === \"]\") {\n bracketDepth -= 1;\n continue;\n }\n\n if (ch === \"{\") {\n if (bracketDepth >= 1) objectStartStack.push(i);\n continue;\n }\n\n if (ch === \"}\") {\n const start = objectStartStack.pop();\n if (start === undefined) continue;\n\n // only consider objects that are direct children of the current array\n if (bracketDepth !== 1 || objectStartStack.length !== 0) continue;\n\n const objText = arrayText.slice(start, i + 1);\n const id = extractPropString(objText, \"id\");\n const type = extractPropString(objText, \"type\");\n\n if (type === \"div\" && id && id !== \"root\" && !seen.has(id)) {\n seen.add(id);\n results.push(id);\n }\n\n continue;\n }\n }\n\n return results;\n};\n\nconst extractRootChildSectionNames = (rootObjText: string): string[] => {\n const childrenKey = rootObjText.indexOf(\"children\");\n if (childrenKey < 0) return [];\n\n const arrayStart = rootObjText.indexOf(\"[\", childrenKey);\n if (arrayStart < 0) return [];\n\n // isolate the children array text (including brackets)\n let bracketDepth = 0;\n let inString: '\"' | \"'\" | \"`\" | null = null;\n\n for (let i = arrayStart; i < rootObjText.length; i++) {\n const ch = rootObjText[i]!;\n\n if (inString) {\n if (inString !== \"`\" && ch === \"\\\\\") {\n i += 1;\n continue;\n }\n if (ch === inString) inString = null;\n continue;\n }\n\n if (ch === '\"' || ch === \"'\" || ch === \"`\") {\n inString = ch;\n continue;\n }\n\n if (ch === \"[\") bracketDepth += 1;\n if (ch === \"]\") {\n bracketDepth -= 1;\n if (bracketDepth === 0) {\n const arrayText = rootObjText.slice(arrayStart, i + 1);\n const ids = extractDirectContainerIdsFromArrayText(arrayText);\n return ids.map(sectionNameFromId).filter(Boolean);\n }\n }\n }\n\n return [];\n};\n\nconst extractSectionNamesFromConfig = (content: string): string[] => {\n if (!content) return [];\n\n const elementsKey = content.indexOf(\"elements\");\n if (elementsKey < 0) return [];\n\n const arrayStart = content.indexOf(\"[\", elementsKey);\n if (arrayStart < 0) return [];\n\n const seen = new Set<string>();\n const results: string[] = [];\n\n let bracketDepth = 0;\n const objectStartStack: number[] = [];\n let inString: '\"' | \"'\" | \"`\" | null = null;\n\n for (let i = arrayStart; i < content.length; i++) {\n const ch = content[i]!;\n\n if (inString) {\n if (inString !== \"`\" && ch === \"\\\\\") {\n i += 1;\n continue;\n }\n\n if (ch === inString) {\n inString = null;\n }\n\n continue;\n }\n\n if (ch === '\"' || ch === \"'\" || ch === \"`\") {\n inString = ch;\n continue;\n }\n\n if (ch === \"[\") {\n bracketDepth += 1;\n continue;\n }\n\n if (ch === \"]\") {\n bracketDepth -= 1;\n if (bracketDepth <= 0) break;\n continue;\n }\n\n if (ch === \"{\") {\n if (bracketDepth >= 1) {\n objectStartStack.push(i);\n }\n continue;\n }\n\n if (ch === \"}\") {\n const objectStart = objectStartStack.pop();\n if (bracketDepth >= 1 && objectStart !== undefined) {\n const objText = content.slice(objectStart, i + 1);\n const id = extractPropString(objText, \"id\");\n const type = extractPropString(objText, \"type\");\n\n if (type === \"div\" && id === \"root\") {\n const rootSections = extractRootChildSectionNames(objText);\n if (rootSections.length > 0) {\n return rootSections;\n }\n }\n\n if (\n type === \"div\" &&\n id &&\n id !== \"root\" &&\n (id.endsWith(\"-section\") || id.endsWith(\"-container\"))\n ) {\n const sectionName = sectionNameFromId(id);\n if (sectionName && !seen.has(sectionName)) {\n seen.add(sectionName);\n results.push(sectionName);\n }\n }\n }\n continue;\n }\n }\n\n return results;\n};\n\nconst computePageRouteFromSegments = (segments: string[]): string => {\n const filtered = segments.filter(\n (s) => s && !isRouteGroup(s) && !isParallelRoute(s),\n );\n\n if (filtered.length === 0) return \"/\";\n return `/${filtered.join(\"/\")}`;\n};\n\nconst computePageNameFromRoute = (pageRoute: string): string => {\n if (pageRoute === \"/\") return \"root\";\n return pageRoute.slice(1).split(\"/\").join(\"-\");\n};\n\nconst findPageConfigFiles = async (dir: string): Promise<string[]> => {\n const entries = await safeReadDir(dir);\n const results: string[] = [];\n\n for (const entry of entries) {\n if (!entry.isDirectory() && !entry.isFile()) continue;\n\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n results.push(...(await findPageConfigFiles(fullPath)));\n continue;\n }\n\n if (entry.name === \"page.config.ts\") {\n results.push(fullPath);\n }\n }\n\n return results;\n};\n\nexport async function computeProjectInfo(\n rootDir: string,\n): Promise<ProjectInfo> {\n const effectiveRoot = rootDir;\n\n const appDir = path.join(effectiveRoot, \"app\");\n const pageConfigFiles = await findPageConfigFiles(appDir);\n\n const uiPages: UiPage[] = [];\n\n for (const filePath of pageConfigFiles) {\n const relFromApp = toPosixPath(path.relative(appDir, filePath));\n const relDir = path.posix.dirname(relFromApp);\n const segments = relDir === \".\" ? [] : relDir.split(\"/\").filter(Boolean);\n\n if (segments.some(isParallelRoute)) {\n continue;\n }\n\n const pageRoute = computePageRouteFromSegments(segments);\n const pageName = computePageNameFromRoute(pageRoute);\n const description = `${pageName} page for this project`;\n\n const content = await readFile(filePath);\n const sectionNames = extractSectionNamesFromConfig(content);\n\n const page: UiPage = {\n pageRoute,\n pageName,\n description,\n };\n\n if (sectionNames.length > 0) {\n page.sections = sectionNames.map((sectionName) => ({\n sectionName,\n description: `${sectionName} section for this page`,\n }));\n }\n\n uiPages.push(page);\n }\n\n uiPages.sort((a, b) =>\n a.pageRoute.localeCompare(b.pageRoute, undefined, {\n sensitivity: \"base\",\n }),\n );\n\n return {\n uiPages,\n lastUpdatedPlanVersion: 1,\n };\n}"]}
|
|
1
|
+
{"version":3,"file":"projectInfoIndex.js","sourceRoot":"","sources":["../../src/indexer/projectInfoIndex.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n getAvailableRoutes,\n getPageConfigJsonPath,\n} from \"../ai/tools/helpers/pageConfigJson.helpers.js\";\nimport { ProjectInfo } from \"../types/projectInfo.types.js\";\nimport { readFile, safeReadDir } from \"../utils/workspace.js\";\n\ntype UiPage = ProjectInfo[\"uiPages\"][number];\n\nconst sectionNameFromId = (id: string): string => {\n if (!id) return \"\";\n if (id.endsWith(\"-section\")) return id.slice(0, -\"-section\".length);\n if (id.endsWith(\"-container\")) return id.slice(0, -\"-container\".length);\n return id;\n};\n\nconst computePageNameFromRoute = (pageRoute: string): string => {\n if (pageRoute === \"/\") return \"root\";\n return pageRoute.slice(1).split(\"/\").join(\"-\");\n};\n\nconst extractSectionNamesFromParsedConfig = (config: any): string[] => {\n if (!config || !Array.isArray(config.elements)) return [];\n\n const elements = config.elements;\n\n // Let's find the root element (where type === \"div\" && id === \"root\") recursively\n let rootElement: any = null;\n const findRoot = (els: any[]) => {\n for (const el of els) {\n if (el && typeof el === \"object\") {\n if (el.type === \"div\" && el.id === \"root\") {\n rootElement = el;\n return;\n }\n if (Array.isArray(el.children)) {\n findRoot(el.children);\n if (rootElement) return;\n }\n }\n }\n };\n findRoot(elements);\n\n if (rootElement && Array.isArray(rootElement.children)) {\n const rootSections: string[] = [];\n for (const child of rootElement.children) {\n if (\n child &&\n typeof child === \"object\" &&\n child.type === \"div\" &&\n child.id &&\n child.id !== \"root\"\n ) {\n const name = sectionNameFromId(child.id);\n if (name) {\n rootSections.push(name);\n }\n }\n }\n if (rootSections.length > 0) {\n return rootSections;\n }\n }\n\n // Fallback: collect all divs with id ending in \"-section\" or \"-container\"\n const seen = new Set<string>();\n const results: string[] = [];\n const collectFallback = (els: any[]) => {\n for (const el of els) {\n if (el && typeof el === \"object\") {\n if (\n el.type === \"div\" &&\n el.id &&\n el.id !== \"root\" &&\n (el.id.endsWith(\"-section\") || el.id.endsWith(\"-container\"))\n ) {\n const sectionName = sectionNameFromId(el.id);\n if (sectionName && !seen.has(sectionName)) {\n seen.add(sectionName);\n results.push(sectionName);\n }\n }\n if (Array.isArray(el.children)) {\n collectFallback(el.children);\n }\n }\n }\n };\n collectFallback(elements);\n return results;\n};\n\nexport async function computeProjectInfo(\n rootDir: string,\n): Promise<ProjectInfo> {\n const routes = await getAvailableRoutes({\n workspaceRoot: rootDir,\n fs: { safeReadDir },\n });\n\n const uiPages: UiPage[] = [];\n\n for (const pageRoute of routes) {\n const pageName = computePageNameFromRoute(pageRoute);\n const description = `${pageName} page for this project`;\n\n let sectionNames: string[] = [];\n try {\n const configPath = getPageConfigJsonPath(rootDir, pageRoute);\n const content = await readFile(configPath);\n if (content) {\n const config = JSON.parse(content);\n sectionNames = extractSectionNamesFromParsedConfig(config);\n }\n } catch (err) {\n // Ignore reading/parsing errors, treat as no sections\n }\n\n const page: UiPage = {\n pageRoute,\n pageName,\n description,\n };\n\n if (sectionNames.length > 0) {\n page.sections = sectionNames.map((sectionName) => ({\n sectionName,\n description: `${sectionName} section for this page`,\n }));\n }\n\n uiPages.push(page);\n }\n\n uiPages.sort((a, b) =>\n a.pageRoute.localeCompare(b.pageRoute, undefined, {\n sensitivity: \"base\",\n }),\n );\n\n return {\n uiPages,\n lastUpdatedPlanVersion: 1,\n };\n}"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projectInfoIndex.test.d.ts","sourceRoot":"","sources":["../../src/tests/projectInfoIndex.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import fs from "node:fs/promises";
|
|
6
|
+
import { computeProjectInfo } from "../indexer/projectInfoIndex.js";
|
|
7
|
+
test("computeProjectInfo: scans routes and sections from pageConfig.json files", async () => {
|
|
8
|
+
const workspaceRoot = await fs.mkdtemp(path.join(os.tmpdir(), "qwintly-core-project-info-"));
|
|
9
|
+
try {
|
|
10
|
+
// 1. Create a root pageConfig.json with a div root and two sections
|
|
11
|
+
const rootDir = path.join(workspaceRoot, "app");
|
|
12
|
+
await fs.mkdir(rootDir, { recursive: true });
|
|
13
|
+
await fs.writeFile(path.join(rootDir, "pageConfig.json"), JSON.stringify({
|
|
14
|
+
elements: [
|
|
15
|
+
{
|
|
16
|
+
id: "root",
|
|
17
|
+
type: "div",
|
|
18
|
+
children: [
|
|
19
|
+
{ id: "hero-section", type: "div", children: [] },
|
|
20
|
+
{ id: "footer-container", type: "div", children: [] },
|
|
21
|
+
{ id: "some-text", type: "text", props: { text: "hello" } }
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}));
|
|
26
|
+
// 2. Create a nested pageConfig.json under /dashboard/settings with a fallback section
|
|
27
|
+
const settingsDir = path.join(workspaceRoot, "app", "dashboard", "settings");
|
|
28
|
+
await fs.mkdir(settingsDir, { recursive: true });
|
|
29
|
+
await fs.writeFile(path.join(settingsDir, "pageConfig.json"), JSON.stringify({
|
|
30
|
+
elements: [
|
|
31
|
+
{
|
|
32
|
+
id: "settings-container",
|
|
33
|
+
type: "div",
|
|
34
|
+
children: []
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
}));
|
|
38
|
+
// 3. Create a nested pageConfig.json under /dashboard with no sections
|
|
39
|
+
const dashboardDir = path.join(workspaceRoot, "app", "dashboard");
|
|
40
|
+
await fs.mkdir(dashboardDir, { recursive: true });
|
|
41
|
+
await fs.writeFile(path.join(dashboardDir, "pageConfig.json"), JSON.stringify({
|
|
42
|
+
elements: []
|
|
43
|
+
}));
|
|
44
|
+
const projectInfo = await computeProjectInfo(workspaceRoot);
|
|
45
|
+
assert.equal(projectInfo.uiPages.length, 3);
|
|
46
|
+
assert.equal(projectInfo.lastUpdatedPlanVersion, 1);
|
|
47
|
+
// / route
|
|
48
|
+
const rootPage = projectInfo.uiPages.find(p => p.pageRoute === "/");
|
|
49
|
+
assert.ok(rootPage);
|
|
50
|
+
assert.equal(rootPage.pageName, "root");
|
|
51
|
+
assert.equal(rootPage.description, "root page for this project");
|
|
52
|
+
assert.deepEqual(rootPage.sections, [
|
|
53
|
+
{ sectionName: "hero", description: "hero section for this page" },
|
|
54
|
+
{ sectionName: "footer", description: "footer section for this page" }
|
|
55
|
+
]);
|
|
56
|
+
// /dashboard route
|
|
57
|
+
const dashboardPage = projectInfo.uiPages.find(p => p.pageRoute === "/dashboard");
|
|
58
|
+
assert.ok(dashboardPage);
|
|
59
|
+
assert.equal(dashboardPage.pageName, "dashboard");
|
|
60
|
+
assert.equal(dashboardPage.description, "dashboard page for this project");
|
|
61
|
+
assert.equal(dashboardPage.sections, undefined);
|
|
62
|
+
// /dashboard/settings route
|
|
63
|
+
const settingsPage = projectInfo.uiPages.find(p => p.pageRoute === "/dashboard/settings");
|
|
64
|
+
assert.ok(settingsPage);
|
|
65
|
+
assert.equal(settingsPage.pageName, "dashboard-settings");
|
|
66
|
+
assert.equal(settingsPage.description, "dashboard-settings page for this project");
|
|
67
|
+
assert.deepEqual(settingsPage.sections, [
|
|
68
|
+
{ sectionName: "settings", description: "settings section for this page" }
|
|
69
|
+
]);
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
await fs.rm(workspaceRoot, { recursive: true, force: true });
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
//# sourceMappingURL=projectInfoIndex.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projectInfoIndex.test.js","sourceRoot":"","sources":["../../src/tests/projectInfoIndex.test.ts"],"names":[],"mappings":"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","sourcesContent":["import assert from \"node:assert/strict\";\nimport test from \"node:test\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport fs from \"node:fs/promises\";\nimport { computeProjectInfo } from \"../indexer/projectInfoIndex.js\";\n\ntest(\"computeProjectInfo: scans routes and sections from pageConfig.json files\", async () => {\n const workspaceRoot = await fs.mkdtemp(path.join(os.tmpdir(), \"qwintly-core-project-info-\"));\n try {\n // 1. Create a root pageConfig.json with a div root and two sections\n const rootDir = path.join(workspaceRoot, \"app\");\n await fs.mkdir(rootDir, { recursive: true });\n await fs.writeFile(\n path.join(rootDir, \"pageConfig.json\"),\n JSON.stringify({\n elements: [\n {\n id: \"root\",\n type: \"div\",\n children: [\n { id: \"hero-section\", type: \"div\", children: [] },\n { id: \"footer-container\", type: \"div\", children: [] },\n { id: \"some-text\", type: \"text\", props: { text: \"hello\" } }\n ]\n }\n ]\n })\n );\n\n // 2. Create a nested pageConfig.json under /dashboard/settings with a fallback section\n const settingsDir = path.join(workspaceRoot, \"app\", \"dashboard\", \"settings\");\n await fs.mkdir(settingsDir, { recursive: true });\n await fs.writeFile(\n path.join(settingsDir, \"pageConfig.json\"),\n JSON.stringify({\n elements: [\n {\n id: \"settings-container\",\n type: \"div\",\n children: []\n }\n ]\n })\n );\n\n // 3. Create a nested pageConfig.json under /dashboard with no sections\n const dashboardDir = path.join(workspaceRoot, \"app\", \"dashboard\");\n await fs.mkdir(dashboardDir, { recursive: true });\n await fs.writeFile(\n path.join(dashboardDir, \"pageConfig.json\"),\n JSON.stringify({\n elements: []\n })\n );\n\n const projectInfo = await computeProjectInfo(workspaceRoot);\n\n assert.equal(projectInfo.uiPages.length, 3);\n assert.equal(projectInfo.lastUpdatedPlanVersion, 1);\n\n // / route\n const rootPage = projectInfo.uiPages.find(p => p.pageRoute === \"/\");\n assert.ok(rootPage);\n assert.equal(rootPage.pageName, \"root\");\n assert.equal(rootPage.description, \"root page for this project\");\n assert.deepEqual(rootPage.sections, [\n { sectionName: \"hero\", description: \"hero section for this page\" },\n { sectionName: \"footer\", description: \"footer section for this page\" }\n ]);\n\n // /dashboard route\n const dashboardPage = projectInfo.uiPages.find(p => p.pageRoute === \"/dashboard\");\n assert.ok(dashboardPage);\n assert.equal(dashboardPage.pageName, \"dashboard\");\n assert.equal(dashboardPage.description, \"dashboard page for this project\");\n assert.equal(dashboardPage.sections, undefined);\n\n // /dashboard/settings route\n const settingsPage = projectInfo.uiPages.find(p => p.pageRoute === \"/dashboard/settings\");\n assert.ok(settingsPage);\n assert.equal(settingsPage.pageName, \"dashboard-settings\");\n assert.equal(settingsPage.description, \"dashboard-settings page for this project\");\n assert.deepEqual(settingsPage.sections, [\n { sectionName: \"settings\", description: \"settings section for this page\" }\n ]);\n\n } finally {\n await fs.rm(workspaceRoot, { recursive: true, force: true });\n }\n});\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unsplash.service.test.d.ts","sourceRoot":"","sources":["../../src/tests/unsplash.service.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { initUnsplash, resolveUnsplashImagesDeep } from "../image/unsplash.service.js";
|
|
4
|
+
test("resolveUnsplashImagesDeep: resolves and inserts src for images", async () => {
|
|
5
|
+
const originalFetch = globalThis.fetch;
|
|
6
|
+
// Setup fetch mock
|
|
7
|
+
const calls = [];
|
|
8
|
+
globalThis.fetch = async (url, init) => {
|
|
9
|
+
const urlString = String(url);
|
|
10
|
+
calls.push(urlString);
|
|
11
|
+
if (urlString.includes("/search/photos")) {
|
|
12
|
+
return {
|
|
13
|
+
ok: true,
|
|
14
|
+
status: 200,
|
|
15
|
+
json: async () => ({
|
|
16
|
+
results: [
|
|
17
|
+
{
|
|
18
|
+
id: "photo_1",
|
|
19
|
+
likes: 100,
|
|
20
|
+
width: 800,
|
|
21
|
+
height: 600,
|
|
22
|
+
user: {
|
|
23
|
+
total_photos: 10,
|
|
24
|
+
name: "Photographer Name",
|
|
25
|
+
links: {
|
|
26
|
+
html: "https://unsplash.com/@photographer"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
links: {
|
|
30
|
+
download_location: "https://api.unsplash.com/photos/photo_1/download",
|
|
31
|
+
html: "https://unsplash.com/photos/photo_1"
|
|
32
|
+
},
|
|
33
|
+
urls: {
|
|
34
|
+
regular: "https://images.unsplash.com/photo_1_regular",
|
|
35
|
+
small: "https://images.unsplash.com/photo_1_small"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
})
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
if (urlString.includes("/download")) {
|
|
43
|
+
return {
|
|
44
|
+
ok: true,
|
|
45
|
+
status: 200,
|
|
46
|
+
json: async () => ({})
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
ok: false,
|
|
51
|
+
status: 404
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
try {
|
|
55
|
+
initUnsplash({
|
|
56
|
+
url: "https://api.unsplash.com",
|
|
57
|
+
accessKey: "fake_key",
|
|
58
|
+
});
|
|
59
|
+
const element = {
|
|
60
|
+
type: "div",
|
|
61
|
+
children: [
|
|
62
|
+
{
|
|
63
|
+
type: "image",
|
|
64
|
+
props: {
|
|
65
|
+
alt: "rustic sourdough bread"
|
|
66
|
+
},
|
|
67
|
+
className: "w-full h-64"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
type: "text",
|
|
71
|
+
props: {
|
|
72
|
+
text: "Artisanal Breads, Baked Fresh Daily"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
};
|
|
77
|
+
// Before calling, image prop has no src
|
|
78
|
+
const imageElement = element.children[0];
|
|
79
|
+
assert.ok(!imageElement.props.src);
|
|
80
|
+
// Call and await resolution
|
|
81
|
+
await resolveUnsplashImagesDeep(element);
|
|
82
|
+
// After calling, image prop must have the resolved src
|
|
83
|
+
assert.equal(imageElement.props.src, "https://images.unsplash.com/photo_1_regular");
|
|
84
|
+
assert.equal(imageElement.props.alt, "rustic sourdough bread");
|
|
85
|
+
// Make sure we have fetch calls
|
|
86
|
+
assert.ok(calls.some(c => c.includes("/search/photos")));
|
|
87
|
+
assert.ok(calls.some(c => c.includes("/download")));
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
globalThis.fetch = originalFetch;
|
|
91
|
+
initUnsplash(null);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
//# sourceMappingURL=unsplash.service.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unsplash.service.test.js","sourceRoot":"","sources":["../../src/tests/unsplash.service.test.ts"],"names":[],"mappings":"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","sourcesContent":["import assert from \"node:assert/strict\";\nimport test from \"node:test\";\nimport { initUnsplash, resolveUnsplashImagesDeep } from \"../image/unsplash.service.js\";\n\ntest(\"resolveUnsplashImagesDeep: resolves and inserts src for images\", async () => {\n const originalFetch = globalThis.fetch;\n \n // Setup fetch mock\n const calls: string[] = [];\n globalThis.fetch = async (url: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\n const urlString = String(url);\n calls.push(urlString);\n \n if (urlString.includes(\"/search/photos\")) {\n return {\n ok: true,\n status: 200,\n json: async () => ({\n results: [\n {\n id: \"photo_1\",\n likes: 100,\n width: 800,\n height: 600,\n user: {\n total_photos: 10,\n name: \"Photographer Name\",\n links: {\n html: \"https://unsplash.com/@photographer\"\n }\n },\n links: {\n download_location: \"https://api.unsplash.com/photos/photo_1/download\",\n html: \"https://unsplash.com/photos/photo_1\"\n },\n urls: {\n regular: \"https://images.unsplash.com/photo_1_regular\",\n small: \"https://images.unsplash.com/photo_1_small\"\n }\n }\n ]\n })\n } as Response;\n }\n \n if (urlString.includes(\"/download\")) {\n return {\n ok: true,\n status: 200,\n json: async () => ({})\n } as Response;\n }\n \n return {\n ok: false,\n status: 404\n } as Response;\n };\n\n try {\n initUnsplash({\n url: \"https://api.unsplash.com\",\n accessKey: \"fake_key\",\n });\n\n const element = {\n type: \"div\",\n children: [\n {\n type: \"image\",\n props: {\n alt: \"rustic sourdough bread\"\n },\n className: \"w-full h-64\"\n },\n {\n type: \"text\",\n props: {\n text: \"Artisanal Breads, Baked Fresh Daily\"\n }\n }\n ]\n };\n\n // Before calling, image prop has no src\n const imageElement = element.children[0] as any;\n assert.ok(!imageElement.props.src);\n\n // Call and await resolution\n await resolveUnsplashImagesDeep(element as any);\n\n // After calling, image prop must have the resolved src\n assert.equal(imageElement.props.src, \"https://images.unsplash.com/photo_1_regular\");\n assert.equal(imageElement.props.alt, \"rustic sourdough bread\");\n\n // Make sure we have fetch calls\n assert.ok(calls.some(c => c.includes(\"/search/photos\")));\n assert.ok(calls.some(c => c.includes(\"/download\")));\n\n } finally {\n globalThis.fetch = originalFetch;\n initUnsplash(null);\n }\n});\n"]}
|