mirror of
https://github.com/multipleof4/lynchmark.git
synced 2026-01-14 16:47:55 +00:00
Docs: Update benchmark results
This commit is contained in:
84
results.json
84
results.json
@@ -1,62 +1,32 @@
|
|||||||
{
|
{
|
||||||
"openrouter/polaris-alpha": {
|
"openai/gpt-5.1-codex": {
|
||||||
"1_dijkstra": 5.663508702,
|
"1_dijkstra": 12.73643722,
|
||||||
"2_convex_hull": 5.853618222000001,
|
"2_convex_hull": 20.551442495,
|
||||||
"3_lis": 2.7803642009999985,
|
"3_lis": 6.9065933989999975,
|
||||||
"4_determinant": 2.2549030789999995,
|
"4_determinant": 3.340895964000003,
|
||||||
"5_markdown_parser": 2.428845373,
|
"5_markdown_parser": 3.5223763349999935,
|
||||||
"6_csv_processor": 7.004516176000001,
|
"6_csv_processor": 16.396112775000002,
|
||||||
"7_scheduler": 14.370537171,
|
"7_scheduler": 85.407877246,
|
||||||
"8_json_validator": 4.488872165000001
|
"8_json_validator": 9.581871734999993
|
||||||
},
|
},
|
||||||
"google/gemini-2.5-pro TEMP:0.7": {
|
"google/gemini-2.5-pro": {
|
||||||
"1_dijkstra": 44.600899471,
|
"1_dijkstra": 52.880149714,
|
||||||
"2_convex_hull": 37.66833377400001,
|
"2_convex_hull": 53.18372889399997,
|
||||||
"3_lis": 15.942395212000017,
|
"3_lis": 28.706411030000016,
|
||||||
"4_determinant": 14.880486881999998,
|
"4_determinant": 12.91266780300002,
|
||||||
"5_markdown_parser": 22.373131437000005,
|
"5_markdown_parser": 19.207484959000023,
|
||||||
"6_csv_processor": 44.242416465999995,
|
"6_csv_processor": 37.37156615500001,
|
||||||
"7_scheduler": 59.66216159599999,
|
"7_scheduler": 108.64825142700003,
|
||||||
"8_json_validator": 56.66983818199998
|
"8_json_validator": 25.360757163000002
|
||||||
},
|
|
||||||
"anthropic/claude-sonnet-4.5": {
|
|
||||||
"1_dijkstra": 4.15588515399996,
|
|
||||||
"2_convex_hull": 4.806273176999995,
|
|
||||||
"3_lis": 2.2488794699999852,
|
|
||||||
"4_determinant": 1.442708507000003,
|
|
||||||
"5_markdown_parser": 2.1533864889999967,
|
|
||||||
"6_csv_processor": 4.5494161339999994,
|
|
||||||
"7_scheduler": 13.169441607000016,
|
|
||||||
"8_json_validator": 2.83180839000002
|
|
||||||
},
|
},
|
||||||
"anthropic/claude-sonnet-4.5 TEMP:0.7": {
|
"anthropic/claude-sonnet-4.5 TEMP:0.7": {
|
||||||
"1_dijkstra": 4.402104449000035,
|
"1_dijkstra": 3.8821057339999823,
|
||||||
"2_convex_hull": 5.400227270999982,
|
"2_convex_hull": 4.63132335000002,
|
||||||
"3_lis": 4.495708809000033,
|
"3_lis": 2.5366414799999912,
|
||||||
"4_determinant": 1.8576504829999758,
|
"4_determinant": 1.642024412000028,
|
||||||
"5_markdown_parser": 1.8857627109999884,
|
"5_markdown_parser": 2.235130207999959,
|
||||||
"6_csv_processor": 4.558546455000004,
|
"6_csv_processor": 4.568896986000007,
|
||||||
"7_scheduler": 11.452500546000024,
|
"7_scheduler": 15.728725427000084,
|
||||||
"8_json_validator": 2.654919869999983
|
"8_json_validator": 3.0146192130000564
|
||||||
},
|
|
||||||
"anthropic/claude-sonnet-4.5 TEMP:0.4": {
|
|
||||||
"1_dijkstra": 4.231384166999953,
|
|
||||||
"2_convex_hull": 4.297011561000021,
|
|
||||||
"3_lis": 2.270648061999993,
|
|
||||||
"4_determinant": 1.5894071249999688,
|
|
||||||
"5_markdown_parser": 1.8096978460000246,
|
|
||||||
"6_csv_processor": 4.622239387999987,
|
|
||||||
"7_scheduler": 12.332949335000013,
|
|
||||||
"8_json_validator": 2.6777597769999995
|
|
||||||
},
|
|
||||||
"openai/gpt-5-codex": {
|
|
||||||
"1_dijkstra": 86.38025543199991,
|
|
||||||
"2_convex_hull": 37.86684929500008,
|
|
||||||
"3_lis": 9.727075501000042,
|
|
||||||
"4_determinant": 8.948688077000087,
|
|
||||||
"5_markdown_parser": 8.000620244999999,
|
|
||||||
"6_csv_processor": 31.738115685999976,
|
|
||||||
"7_scheduler": 155.32837211300003,
|
|
||||||
"8_json_validator": 27.551844137000035
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
async function findShortestPath(graph, start, end) {
|
|
||||||
const { default: PriorityQueue } = await import('https://cdn.skypack.dev/js-priority-queue');
|
|
||||||
|
|
||||||
const dist = Object.keys(graph).reduce((acc, node) => ({ ...acc, [node]: Infinity }), {});
|
|
||||||
dist[start] = 0;
|
|
||||||
|
|
||||||
const pq = new PriorityQueue({ comparator: (a, b) => a[1] - b[1] });
|
|
||||||
pq.queue([start, 0]);
|
|
||||||
|
|
||||||
const visited = new Set();
|
|
||||||
|
|
||||||
while (pq.length) {
|
|
||||||
const [node, d] = pq.dequeue();
|
|
||||||
|
|
||||||
if (visited.has(node)) continue;
|
|
||||||
visited.add(node);
|
|
||||||
|
|
||||||
if (node === end) return d;
|
|
||||||
|
|
||||||
for (const [neighbor, weight] of Object.entries(graph[node] || {})) {
|
|
||||||
const newDist = d + weight;
|
|
||||||
if (newDist < dist[neighbor]) {
|
|
||||||
dist[neighbor] = newDist;
|
|
||||||
pq.queue([neighbor, newDist]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dist[end];
|
|
||||||
}
|
|
||||||
export default findShortestPath;
|
|
||||||
@@ -1,25 +1,28 @@
|
|||||||
async function findShortestPath(graph, start, end) {
|
async function findShortestPath(graph, start, end) {
|
||||||
const { default: PQ } = await import('https://cdn.jsdelivr.net/npm/js-priority-queue@0.1.5/+esm');
|
const { default: PriorityQueue } = await import('https://cdn.skypack.dev/js-priority-queue');
|
||||||
|
|
||||||
const dist = { [start]: 0 };
|
const distances = {};
|
||||||
const pq = new PQ({ comparator: (a, b) => a[1] - b[1] });
|
const pq = new PriorityQueue({ comparator: (a, b) => a.dist - b.dist });
|
||||||
pq.queue([start, 0]);
|
|
||||||
|
for (const node in graph) distances[node] = Infinity;
|
||||||
|
distances[start] = 0;
|
||||||
|
pq.queue({ node: start, dist: 0 });
|
||||||
|
|
||||||
while (pq.length) {
|
while (pq.length) {
|
||||||
const [node, d] = pq.dequeue();
|
const { node, dist } = pq.dequeue();
|
||||||
|
|
||||||
if (node === end) return d;
|
if (node === end) return dist;
|
||||||
if (d > (dist[node] ?? Infinity)) continue;
|
if (dist > distances[node]) continue;
|
||||||
|
|
||||||
for (const [neighbor, weight] of Object.entries(graph[node] || {})) {
|
for (const neighbor in graph[node]) {
|
||||||
const newDist = d + weight;
|
const newDist = dist + graph[node][neighbor];
|
||||||
if (newDist < (dist[neighbor] ?? Infinity)) {
|
if (newDist < distances[neighbor]) {
|
||||||
dist[neighbor] = newDist;
|
distances[neighbor] = newDist;
|
||||||
pq.queue([neighbor, newDist]);
|
pq.queue({ node: neighbor, dist: newDist });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Infinity;
|
return distances[end];
|
||||||
}
|
}
|
||||||
export default findShortestPath;
|
export default findShortestPath;
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
async function findShortestPath(graph, start, end) {
|
|
||||||
const { default: PriorityQueue } = await import('https://cdn.skypack.dev/js-priority-queue');
|
|
||||||
|
|
||||||
const distances = Object.keys(graph).reduce((acc, node) => ({ ...acc, [node]: Infinity }), {});
|
|
||||||
distances[start] = 0;
|
|
||||||
|
|
||||||
const pq = new PriorityQueue({ comparator: (a, b) => a.dist - b.dist });
|
|
||||||
pq.queue({ node: start, dist: 0 });
|
|
||||||
|
|
||||||
const visited = new Set();
|
|
||||||
|
|
||||||
while (pq.length) {
|
|
||||||
const { node, dist } = pq.dequeue();
|
|
||||||
|
|
||||||
if (visited.has(node)) continue;
|
|
||||||
visited.add(node);
|
|
||||||
|
|
||||||
if (node === end) return dist;
|
|
||||||
|
|
||||||
for (const [neighbor, weight] of Object.entries(graph[node] || {})) {
|
|
||||||
const newDist = dist + weight;
|
|
||||||
if (newDist < distances[neighbor]) {
|
|
||||||
distances[neighbor] = newDist;
|
|
||||||
pq.queue({ node: neighbor, dist: newDist });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return distances[end];
|
|
||||||
}
|
|
||||||
export default findShortestPath;
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
async function findShortestPath(graph, startNode, endNode) {
|
|
||||||
const { default: PriorityQueue } = await import('https://cdn.jsdelivr.net/npm/js-priority-queue@0.1.5/priority-queue.min.js');
|
|
||||||
|
|
||||||
const dist = {};
|
|
||||||
const pq = new PriorityQueue({ comparator: (a, b) => dist[a] - dist[b] });
|
|
||||||
|
|
||||||
for (const vertex in graph) {
|
|
||||||
dist[vertex] = Infinity;
|
|
||||||
}
|
|
||||||
dist[startNode] = 0;
|
|
||||||
|
|
||||||
pq.queue(startNode);
|
|
||||||
|
|
||||||
while (pq.length) {
|
|
||||||
const u = pq.dequeue();
|
|
||||||
|
|
||||||
if (u === endNode) break;
|
|
||||||
if (!graph[u] || dist[u] === Infinity) continue;
|
|
||||||
|
|
||||||
for (const v in graph[u]) {
|
|
||||||
const newDist = dist[u] + graph[u][v];
|
|
||||||
|
|
||||||
if (newDist < dist[v]) {
|
|
||||||
dist[v] = newDist;
|
|
||||||
pq.queue(v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dist[endNode] ?? Infinity;
|
|
||||||
}
|
|
||||||
export default findShortestPath;
|
|
||||||
33
tests/1_dijkstra/outputs/google_gemini-2.5-pro.js
Normal file
33
tests/1_dijkstra/outputs/google_gemini-2.5-pro.js
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
async function findShortestPath(graph, startNode, endNode) {
|
||||||
|
const { default: PriorityQueue } = await import('https://esm.sh/js-priority-queue');
|
||||||
|
|
||||||
|
const distances = { [startNode]: 0 };
|
||||||
|
const queue = new PriorityQueue({ comparator: (a, b) => a.priority - b.priority });
|
||||||
|
|
||||||
|
queue.queue({ node: startNode, priority: 0 });
|
||||||
|
|
||||||
|
while (queue.length) {
|
||||||
|
const { node: currentNode, priority: currentDist } = queue.dequeue();
|
||||||
|
|
||||||
|
if (currentDist > (distances[currentNode] ?? Infinity)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentNode === endNode) {
|
||||||
|
return currentDist;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const neighbor in graph[currentNode] || {}) {
|
||||||
|
const weight = graph[currentNode][neighbor];
|
||||||
|
const newDist = currentDist + weight;
|
||||||
|
|
||||||
|
if (newDist < (distances[neighbor] ?? Infinity)) {
|
||||||
|
distances[neighbor] = newDist;
|
||||||
|
queue.queue({ node: neighbor, priority: newDist });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Infinity;
|
||||||
|
}
|
||||||
|
export default findShortestPath;
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
let pq;
|
|
||||||
const loadPQ=()=>pq||(pq=import('https://cdn.skypack.dev/js-priority-queue').then(m=>m.default));
|
|
||||||
|
|
||||||
async function findShortestPath(graph,start,end){
|
|
||||||
const PQ=await loadPQ();
|
|
||||||
if(start===end) return 0;
|
|
||||||
if(!graph||typeof graph!=='object') return Infinity;
|
|
||||||
const d=new Map([[start,0]]);
|
|
||||||
const q=new PQ({comparator:(a,b)=>a[0]-b[0]});
|
|
||||||
q.queue([0,start]);
|
|
||||||
while(q.length){
|
|
||||||
const [w,n]=q.dequeue();
|
|
||||||
if(w>(d.get(n)??Infinity)) continue;
|
|
||||||
if(n===end) return w;
|
|
||||||
const edges=graph[n];
|
|
||||||
if(!edges||typeof edges!=='object') continue;
|
|
||||||
for(const k of Object.keys(edges)){
|
|
||||||
const c=edges[k];
|
|
||||||
if(typeof c!=='number'||c<0||!Number.isFinite(c)) continue;
|
|
||||||
const nw=w+c;
|
|
||||||
if(nw<(d.get(k)??Infinity)){
|
|
||||||
d.set(k,nw);
|
|
||||||
q.queue([nw,k]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Infinity;
|
|
||||||
}
|
|
||||||
export default findShortestPath;
|
|
||||||
19
tests/1_dijkstra/outputs/openai_gpt-5.1-codex.js
Normal file
19
tests/1_dijkstra/outputs/openai_gpt-5.1-codex.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
async function findShortestPath(g,s,e){
|
||||||
|
const {default:Q}=await import('https://cdn.skypack.dev/js-priority-queue')
|
||||||
|
const d={[s]:0},q=new Q({comparator:(a,b)=>a.w-b.w})
|
||||||
|
q.queue({n:s,w:0})
|
||||||
|
while(q.length){
|
||||||
|
const {n,w}=q.dequeue()
|
||||||
|
if(w>(d[n]??Infinity))continue
|
||||||
|
if(n===e)return w
|
||||||
|
for(const k in g[n]||{}){
|
||||||
|
const nw=w+g[n][k]
|
||||||
|
if(nw<(d[k]??Infinity)){
|
||||||
|
d[k]=nw
|
||||||
|
q.queue({n:k,w:nw})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Infinity
|
||||||
|
}
|
||||||
|
export default findShortestPath;
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
async function findShortestPath(g,s,e){
|
|
||||||
const {default:PriorityQueue} = await import('https://cdn.jsdelivr.net/npm/js-priority-queue@0.1.5/jsprioryqueue.min.js').catch(()=>({default:class{
|
|
||||||
constructor(o){this.c=o.comparator||((a,b)=>a-b);this.q=[]}
|
|
||||||
queue(v){this.q.push(v);this.q.sort(this.c)}
|
|
||||||
dequeue(){return this.q.shift()}
|
|
||||||
peek(){return this.q[0]}
|
|
||||||
get length(){return this.q.length}
|
|
||||||
}}));
|
|
||||||
if(!g||!g[s]||!g[e])return Infinity;
|
|
||||||
const d={},v={},pq=new PriorityQueue({comparator:(a,b)=>a.w-b.w});
|
|
||||||
for(const n in g)d[n]=Infinity;
|
|
||||||
d[s]=0;
|
|
||||||
pq.queue({n:s,w:0});
|
|
||||||
while(pq.length){
|
|
||||||
const {n,w}=pq.dequeue();
|
|
||||||
if(v[n])continue;
|
|
||||||
v[n]=1;
|
|
||||||
if(n===e)return w;
|
|
||||||
const nb=g[n];
|
|
||||||
for(const k in nb){
|
|
||||||
const nw=w+nb[k];
|
|
||||||
if(nw<d[k]){
|
|
||||||
d[k]=nw;
|
|
||||||
pq.queue({n:k,w:nw});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Infinity;
|
|
||||||
}
|
|
||||||
export default findShortestPath;
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
async function findConvexHull(points) {
|
|
||||||
const _ = await import('https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lodash.min.js').then(m => m.default);
|
|
||||||
|
|
||||||
if (points.length < 3) return points;
|
|
||||||
|
|
||||||
const sorted = _.sortBy(_.uniqWith(points, _.isEqual), ['x', 'y']);
|
|
||||||
|
|
||||||
if (sorted.length < 3) return sorted;
|
|
||||||
|
|
||||||
const cross = (o, a, b) => (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
|
|
||||||
|
|
||||||
const buildHull = pts => {
|
|
||||||
const hull = [];
|
|
||||||
for (const p of pts) {
|
|
||||||
while (hull.length >= 2 && cross(hull[hull.length - 2], hull[hull.length - 1], p) <= 0) {
|
|
||||||
hull.pop();
|
|
||||||
}
|
|
||||||
hull.push(p);
|
|
||||||
}
|
|
||||||
hull.pop();
|
|
||||||
return hull;
|
|
||||||
};
|
|
||||||
|
|
||||||
return [...buildHull(sorted), ...buildHull(sorted.slice().reverse())];
|
|
||||||
}
|
|
||||||
export default findConvexHull;
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
async function findConvexHull(points) {
|
async function findConvexHull(points) {
|
||||||
const { sortBy, uniqWith } = await import('https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lodash.js');
|
const _ = await import('https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lodash.min.js').then(m => m.default);
|
||||||
|
|
||||||
if (points.length < 3) return points;
|
if (points.length < 3) return points;
|
||||||
|
|
||||||
const sorted = sortBy(uniqWith(points, (a, b) => a.x === b.x && a.y === b.y), ['x', 'y']);
|
const sorted = _.sortBy(_.uniqWith(points, _.isEqual), ['x', 'y']);
|
||||||
|
|
||||||
if (sorted.length < 3) return sorted;
|
if (sorted.length < 3) return sorted;
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
async function findConvexHull(points) {
|
|
||||||
const _ = await import('https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lodash.min.js').then(m => m.default);
|
|
||||||
|
|
||||||
if (points.length < 3) return points;
|
|
||||||
|
|
||||||
const sorted = _.sortBy(_.uniqWith(points, _.isEqual), ['x', 'y']);
|
|
||||||
|
|
||||||
if (sorted.length < 3) return sorted;
|
|
||||||
|
|
||||||
const cross = (o, a, b) => (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
|
|
||||||
|
|
||||||
const buildHull = pts => {
|
|
||||||
const hull = [];
|
|
||||||
for (const p of pts) {
|
|
||||||
while (hull.length >= 2 && cross(hull[hull.length - 2], hull[hull.length - 1], p) <= 0) {
|
|
||||||
hull.pop();
|
|
||||||
}
|
|
||||||
hull.push(p);
|
|
||||||
}
|
|
||||||
hull.pop();
|
|
||||||
return hull;
|
|
||||||
};
|
|
||||||
|
|
||||||
return [...buildHull(sorted), ...buildHull(sorted.slice().reverse())];
|
|
||||||
}
|
|
||||||
export default findConvexHull;
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
async function findConvexHull(points) {
|
|
||||||
const { sortBy, uniqWith, isEqual } = await import('https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/+esm');
|
|
||||||
|
|
||||||
if (points.length < 3) {
|
|
||||||
return [...points];
|
|
||||||
}
|
|
||||||
|
|
||||||
const crossProduct = (p1, p2, p3) =>
|
|
||||||
(p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x);
|
|
||||||
|
|
||||||
const sortedPoints = sortBy(points, ['x', 'y']);
|
|
||||||
|
|
||||||
const buildHull = (pts) =>
|
|
||||||
pts.reduce((hull, p) => {
|
|
||||||
while (hull.length >= 2 && crossProduct(hull[hull.length - 2], hull.at(-1), p) <= 0) {
|
|
||||||
hull.pop();
|
|
||||||
}
|
|
||||||
hull.push(p);
|
|
||||||
return hull;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const lowerHull = buildHull(sortedPoints);
|
|
||||||
const upperHull = buildHull([...sortedPoints].reverse());
|
|
||||||
|
|
||||||
const combinedHull = [...lowerHull, ...upperHull];
|
|
||||||
|
|
||||||
return uniqWith(combinedHull, isEqual);
|
|
||||||
}
|
|
||||||
export default findConvexHull;
|
|
||||||
27
tests/2_convex_hull/outputs/google_gemini-2.5-pro.js
Normal file
27
tests/2_convex_hull/outputs/google_gemini-2.5-pro.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
async function findConvexHull(points) {
|
||||||
|
const { sortBy, uniqWith, isEqual } = await import('https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lodash.min.js');
|
||||||
|
|
||||||
|
const uniquePoints = uniqWith(points, isEqual);
|
||||||
|
if (uniquePoints.length < 3) {
|
||||||
|
return uniquePoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sortedPoints = sortBy(uniquePoints, ['x', 'y']);
|
||||||
|
|
||||||
|
const crossProduct = (p1, p2, p3) =>
|
||||||
|
(p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x);
|
||||||
|
|
||||||
|
const buildHull = pts => pts.reduce((hull, p) => {
|
||||||
|
while (hull.length >= 2 && crossProduct(hull.at(-2), hull.at(-1), p) <= 0) {
|
||||||
|
hull.pop();
|
||||||
|
}
|
||||||
|
hull.push(p);
|
||||||
|
return hull;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const lower = buildHull(sortedPoints);
|
||||||
|
const upper = buildHull([...sortedPoints].reverse());
|
||||||
|
|
||||||
|
return [...lower.slice(0, -1), ...upper.slice(0, -1)];
|
||||||
|
}
|
||||||
|
export default findConvexHull;
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
let h
|
|
||||||
const findConvexHull=async v=>{
|
|
||||||
const {sortBy,uniqWith,isEqual}=await(h??=import('https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/+esm'))
|
|
||||||
const s=uniqWith(sortBy(v,['x','y']),isEqual)
|
|
||||||
if(s.length<3)return s.slice()
|
|
||||||
const r=(o,a,b)=>(a.x-o.x)*(b.y-o.y)-(a.y-o.y)*(b.x-o.x)
|
|
||||||
const l=[]
|
|
||||||
for(const p of s){while(l.length>1&&r(l.at(-2),l.at(-1),p)<=0)l.pop();l.push(p)}
|
|
||||||
const u=[]
|
|
||||||
for(let i=s.length;i--;){const p=s[i];while(u.length>1&&r(u.at(-2),u.at(-1),p)<=0)u.pop();u.push(p)}
|
|
||||||
l.pop()
|
|
||||||
u.pop()
|
|
||||||
return l.concat(u)
|
|
||||||
}
|
|
||||||
export default findConvexHull;
|
|
||||||
23
tests/2_convex_hull/outputs/openai_gpt-5.1-codex.js
Normal file
23
tests/2_convex_hull/outputs/openai_gpt-5.1-codex.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
let cache
|
||||||
|
const loader=()=>cache??=import('https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lodash.js')
|
||||||
|
|
||||||
|
export const findConvexHull=async pts=>{
|
||||||
|
const {sortBy:s,uniqWith:u,isEqual:e}=await loader()
|
||||||
|
const data=u(s(pts,[v=>v.x,v=>v.y]),e)
|
||||||
|
if(data.length<2)return data.slice()
|
||||||
|
const cross=(o,a,b)=>(a.x-o.x)*(b.y-o.y)-(a.y-o.y)*(b.x-o.x)
|
||||||
|
const lower=[],upper=[]
|
||||||
|
for(const p of data){
|
||||||
|
while(lower.length>1&&cross(lower.at(-2),lower.at(-1),p)<=0)lower.pop()
|
||||||
|
lower.push(p)
|
||||||
|
}
|
||||||
|
for(let i=data.length;i--;){
|
||||||
|
const p=data[i]
|
||||||
|
while(upper.length>1&&cross(upper.at(-2),upper.at(-1),p)<=0)upper.pop()
|
||||||
|
upper.push(p)
|
||||||
|
}
|
||||||
|
lower.pop()
|
||||||
|
upper.pop()
|
||||||
|
return lower.concat(upper)
|
||||||
|
}
|
||||||
|
export default findConvexHull;
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
async function findConvexHull(points) {
|
|
||||||
if (!Array.isArray(points) || points.length < 1) return [];
|
|
||||||
const { default: _ } = await import('https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lodash.default.min.js');
|
|
||||||
const s = _.uniqBy(points, p => `${p.x},${p.y}`);
|
|
||||||
if (s.length < 2) return s.slice();
|
|
||||||
const pts = _.sortBy(s, ['x', 'y']);
|
|
||||||
const c = (o, a, b) => (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
|
|
||||||
const l = [];
|
|
||||||
for (let p of pts) {
|
|
||||||
for (; l.length > 1 && c(l[l.length - 2], l[l.length - 1], p) <= 0;) l.pop();
|
|
||||||
l.push(p);
|
|
||||||
}
|
|
||||||
const u = [];
|
|
||||||
for (let i = pts.length - 1; i >= 0; i--) {
|
|
||||||
const p = pts[i];
|
|
||||||
for (; u.length > 1 && c(u[u.length - 2], u[u.length - 1], p) <= 0;) u.pop();
|
|
||||||
u.push(p);
|
|
||||||
}
|
|
||||||
l.pop();
|
|
||||||
u.pop();
|
|
||||||
return l.concat(u);
|
|
||||||
}
|
|
||||||
export default findConvexHull;
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
const findLISLength = async (arr) => {
|
|
||||||
if (!arr?.length) return 0;
|
|
||||||
|
|
||||||
const { bisectLeft } = await import('https://cdn.jsdelivr.net/npm/d3-array@3/+esm');
|
|
||||||
|
|
||||||
const tails = [];
|
|
||||||
|
|
||||||
for (const num of arr) {
|
|
||||||
const pos = bisectLeft(tails, num);
|
|
||||||
tails[pos] = num;
|
|
||||||
}
|
|
||||||
|
|
||||||
return tails.length;
|
|
||||||
};
|
|
||||||
export default findLISLength;
|
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
const findLISLength = async (nums) => {
|
const findLISLength = async (arr) => {
|
||||||
const { bisectLeft } = await import('https://cdn.jsdelivr.net/npm/d3-array@3/+esm');
|
const { bisectLeft } = await import('https://cdn.jsdelivr.net/npm/d3-array@3/+esm');
|
||||||
|
|
||||||
if (!nums?.length) return 0;
|
if (!arr?.length) return 0;
|
||||||
|
|
||||||
const tails = [];
|
const tails = [];
|
||||||
|
|
||||||
for (const num of nums) {
|
for (const num of arr) {
|
||||||
const pos = bisectLeft(tails, num);
|
const idx = bisectLeft(tails, num);
|
||||||
tails[pos] = num;
|
tails[idx] = num;
|
||||||
}
|
}
|
||||||
|
|
||||||
return tails.length;
|
return tails.length;
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
const findLISLength = async (nums) => {
|
|
||||||
const { bisectLeft } = await import('https://cdn.jsdelivr.net/npm/d3-array@3/+esm');
|
|
||||||
|
|
||||||
if (!nums?.length) return 0;
|
|
||||||
|
|
||||||
const tails = [];
|
|
||||||
|
|
||||||
for (const num of nums) {
|
|
||||||
const idx = bisectLeft(tails, num);
|
|
||||||
tails[idx] = num;
|
|
||||||
}
|
|
||||||
|
|
||||||
return tails.length;
|
|
||||||
};
|
|
||||||
export default findLISLength;
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
const findLISLength = async (nums) => {
|
|
||||||
if (!nums?.length) return 0;
|
|
||||||
|
|
||||||
const { bisectLeft } = await import('https://cdn.jsdelivr.net/npm/d3-array@3/+esm');
|
|
||||||
|
|
||||||
const tails = [];
|
|
||||||
|
|
||||||
for (const num of nums) {
|
|
||||||
const i = bisectLeft(tails, num);
|
|
||||||
tails[i] = num;
|
|
||||||
}
|
|
||||||
|
|
||||||
return tails.length;
|
|
||||||
};
|
|
||||||
export default findLISLength;
|
|
||||||
16
tests/3_lis/outputs/google_gemini-2.5-pro.js
Normal file
16
tests/3_lis/outputs/google_gemini-2.5-pro.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
const findLISLength = async (nums) => {
|
||||||
|
if (!nums?.length) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { bisectLeft } = await import('https://esm.sh/d3-array');
|
||||||
|
|
||||||
|
const tails = [];
|
||||||
|
|
||||||
|
for (const num of nums) {
|
||||||
|
tails[bisectLeft(tails, num)] = num;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tails.length;
|
||||||
|
};
|
||||||
|
export default findLISLength;
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
let l;
|
|
||||||
const m=()=>l??=import('https://cdn.jsdelivr.net/npm/d3-array@3/+esm').then(({bisectLeft})=>bisectLeft);
|
|
||||||
export async function findLISLength(a){
|
|
||||||
if(!Array.isArray(a)) throw new TypeError('Expected array');
|
|
||||||
const b=await m();
|
|
||||||
const t=[];
|
|
||||||
for(const n of a){
|
|
||||||
if(typeof n!=='number'||!Number.isFinite(n)) throw new TypeError('Expected finite numbers');
|
|
||||||
const i=b(t,n);
|
|
||||||
t[i]=n;
|
|
||||||
}
|
|
||||||
return t.length;
|
|
||||||
}
|
|
||||||
export default findLISLength;
|
|
||||||
9
tests/3_lis/outputs/openai_gpt-5.1-codex.js
Normal file
9
tests/3_lis/outputs/openai_gpt-5.1-codex.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
const m = import('https://cdn.jsdelivr.net/npm/d3-array@3/+esm');
|
||||||
|
|
||||||
|
async function findLISLength(nums) {
|
||||||
|
const { bisectLeft: b } = await m;
|
||||||
|
const tails = [];
|
||||||
|
for (const n of nums) tails[b(tails, n)] = n;
|
||||||
|
return tails.length;
|
||||||
|
}
|
||||||
|
export default findLISLength;
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
async function findLISLength(a){
|
|
||||||
if(!a||!a.length)return 0
|
|
||||||
const{bisectLeft}=await import('https://cdn.jsdelivr.net/npm/d3-array@3/+esm')
|
|
||||||
const t=[]
|
|
||||||
for(const x of a){
|
|
||||||
const i=bisectLeft(t,x)
|
|
||||||
if(i===t.length)t.push(x)
|
|
||||||
else t[i]=x
|
|
||||||
}
|
|
||||||
return t.length
|
|
||||||
}
|
|
||||||
export default findLISLength;
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
const calculateDeterminant = async (matrix) => {
|
|
||||||
const { det } = await import('https://cdn.jsdelivr.net/npm/mathjs@11.11.0/+esm');
|
|
||||||
return det(matrix);
|
|
||||||
};
|
|
||||||
export default calculateDeterminant;
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
const calculateDeterminant = async (matrix) => {
|
|
||||||
const { det } = await import('https://cdn.jsdelivr.net/npm/mathjs@13.0.0/es/index.js');
|
|
||||||
return det(matrix);
|
|
||||||
};
|
|
||||||
export default calculateDeterminant;
|
|
||||||
5
tests/4_determinant/outputs/google_gemini-2.5-pro.js
Normal file
5
tests/4_determinant/outputs/google_gemini-2.5-pro.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
const calculateDeterminant = async m => {
|
||||||
|
const { det } = await import('https://cdn.jsdelivr.net/npm/mathjs/+esm');
|
||||||
|
return det(m);
|
||||||
|
};
|
||||||
|
export default calculateDeterminant;
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
let c
|
|
||||||
const m=()=>c||(c=import('https://cdn.jsdelivr.net/npm/mathjs@11.11.1/+esm'))
|
|
||||||
async function calculateDeterminant(a){
|
|
||||||
const{det}=await m()
|
|
||||||
return det(a)
|
|
||||||
}
|
|
||||||
export default calculateDeterminant;
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
const calculateDeterminant = async (matrix) => {
|
export const calculateDeterminant = async matrix => {
|
||||||
const { det } = await import('https://cdn.jsdelivr.net/npm/mathjs@11.11.0/+esm');
|
const { det } = await import('https://cdn.jsdelivr.net/npm/mathjs@11.11.0/+esm');
|
||||||
return det(matrix);
|
return det(matrix);
|
||||||
};
|
};
|
||||||
export default calculateDeterminant;
|
export default calculateDeterminant;
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
async function calculateDeterminant(m){
|
|
||||||
if(!Array.isArray(m)||!m.length||!m.every(r=>Array.isArray(r)&&r.length===m.length))throw new Error('Matrix must be square')
|
|
||||||
const {create,all} = await import('https://cdn.jsdelivr.net/npm/mathjs@11.11.0/+esm')
|
|
||||||
const math = create(all)
|
|
||||||
return math.det(m)
|
|
||||||
}
|
|
||||||
export default calculateDeterminant;
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
async function parseMarkdown(md) {
|
|
||||||
const { marked } = await import('https://cdn.jsdelivr.net/npm/marked@11.1.1/+esm');
|
|
||||||
return marked.parse(md);
|
|
||||||
}
|
|
||||||
export default parseMarkdown;
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
async function parseMarkdown(md) {
|
|
||||||
const { marked } = await import('https://cdn.jsdelivr.net/npm/marked@11.1.1/+esm');
|
|
||||||
return marked.parse(md);
|
|
||||||
}
|
|
||||||
export default parseMarkdown;
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
async function parseMarkdown(markdown) {
|
|
||||||
const cdn = 'https://cdn.jsdelivr.net/npm/marked@12/lib/marked.esm.js';
|
|
||||||
const { marked } = await import(cdn);
|
|
||||||
return marked.parse(markdown);
|
|
||||||
}
|
|
||||||
export default parseMarkdown;
|
|
||||||
5
tests/5_markdown_parser/outputs/google_gemini-2.5-pro.js
Normal file
5
tests/5_markdown_parser/outputs/google_gemini-2.5-pro.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
const parseMarkdown = async (md) => {
|
||||||
|
const { marked } = await import('https://cdn.jsdelivr.net/npm/marked@12.0.2/+esm');
|
||||||
|
return marked.parse(md);
|
||||||
|
};
|
||||||
|
export default parseMarkdown;
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
const parseMarkdown=async t=>{
|
|
||||||
parseMarkdown.l??=import('https://cdn.jsdelivr.net/npm/marked@12.0.2/lib/marked.esm.js');
|
|
||||||
const{marked}=await parseMarkdown.l;
|
|
||||||
return marked.parse(t||'');
|
|
||||||
};
|
|
||||||
export default parseMarkdown;
|
|
||||||
7
tests/5_markdown_parser/outputs/openai_gpt-5.1-codex.js
Normal file
7
tests/5_markdown_parser/outputs/openai_gpt-5.1-codex.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
let m
|
||||||
|
export const parseMarkdown=async t=>{
|
||||||
|
m||(m=import('https://cdn.jsdelivr.net/npm/marked@12.0.2/lib/marked.esm.js'))
|
||||||
|
const {marked}=await m
|
||||||
|
return marked.parse(t)
|
||||||
|
}
|
||||||
|
export default parseMarkdown;
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
async function parseMarkdown(md) {
|
|
||||||
if (typeof md !== "string") md = String(md ?? "");
|
|
||||||
const [{ default: marked }] = await Promise.all([
|
|
||||||
import("https://cdn.jsdelivr.net/npm/marked@12.0.2/lib/marked.esm.js")
|
|
||||||
]);
|
|
||||||
marked.setOptions({
|
|
||||||
gfm: true,
|
|
||||||
breaks: false,
|
|
||||||
headerIds: true,
|
|
||||||
mangle: false
|
|
||||||
});
|
|
||||||
return marked.parse(md);
|
|
||||||
}
|
|
||||||
export default parseMarkdown;
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
const processCSV = async (csv, config) => {
|
|
||||||
const { parse } = await import('https://cdn.jsdelivr.net/npm/papaparse@5.4.1/+esm');
|
|
||||||
|
|
||||||
const { data } = parse(csv, { header: true, skipEmptyLines: true });
|
|
||||||
|
|
||||||
const filtered = data.filter(row =>
|
|
||||||
String(row[config.filterColumn]).trim() === String(config.filterValue).trim()
|
|
||||||
);
|
|
||||||
|
|
||||||
const grouped = filtered.reduce((acc, row) => {
|
|
||||||
const key = row[config.groupBy];
|
|
||||||
if (!acc[key]) acc[key] = [];
|
|
||||||
acc[key].push(row);
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
const aggregate = (rows) => {
|
|
||||||
const values = rows.map(r => parseFloat(r[config.aggregateColumn]) || 0);
|
|
||||||
if (config.operation === 'sum') return values.reduce((a, b) => a + b, 0);
|
|
||||||
if (config.operation === 'avg') return values.reduce((a, b) => a + b, 0) / values.length;
|
|
||||||
return values.length;
|
|
||||||
};
|
|
||||||
|
|
||||||
return Object.entries(grouped).map(([key, rows]) => ({
|
|
||||||
[config.groupBy]: key,
|
|
||||||
result: aggregate(rows)
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
export default processCSV;
|
|
||||||
@@ -1,9 +1,15 @@
|
|||||||
const processCSV = async (csvString, config) => {
|
const processCSV = async (csvString, config) => {
|
||||||
const { parse } = await import('https://cdn.skypack.dev/papaparse');
|
const { parse } = await import('https://cdn.jsdelivr.net/npm/papaparse@5.4.1/+esm');
|
||||||
|
|
||||||
const { data } = parse(csvString, { header: true, skipEmptyLines: true });
|
const { data } = parse(csvString, {
|
||||||
|
header: true,
|
||||||
|
skipEmptyLines: true,
|
||||||
|
dynamicTyping: true
|
||||||
|
});
|
||||||
|
|
||||||
const filtered = data.filter(row => row[config.filterColumn] == config.filterValue);
|
const filtered = data.filter(row =>
|
||||||
|
row[config.filterColumn] == config.filterValue
|
||||||
|
);
|
||||||
|
|
||||||
const grouped = filtered.reduce((acc, row) => {
|
const grouped = filtered.reduce((acc, row) => {
|
||||||
const key = row[config.groupBy];
|
const key = row[config.groupBy];
|
||||||
@@ -12,17 +18,15 @@ const processCSV = async (csvString, config) => {
|
|||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
return Object.entries(grouped).map(([key, rows]) => {
|
const operations = {
|
||||||
let result;
|
sum: arr => arr.reduce((s, r) => s + (Number(r[config.aggregateColumn]) || 0), 0),
|
||||||
if (config.operation === 'count') {
|
avg: arr => operations.sum(arr) / arr.length || 0,
|
||||||
result = rows.length;
|
count: arr => arr.length
|
||||||
} else {
|
};
|
||||||
const values = rows.map(r => parseFloat(r[config.aggregateColumn]) || 0);
|
|
||||||
result = config.operation === 'sum'
|
return Object.entries(grouped).map(([key, rows]) => ({
|
||||||
? values.reduce((a, b) => a + b, 0)
|
[config.groupBy]: key,
|
||||||
: values.reduce((a, b) => a + b, 0) / values.length;
|
result: operations[config.operation](rows)
|
||||||
}
|
}));
|
||||||
return { [config.groupBy]: key, result };
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
export default processCSV;
|
export default processCSV;
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
async function processCSV(csv, config) {
|
|
||||||
const { parse } = await import('https://esm.sh/papaparse@5.4.1');
|
|
||||||
const { groupBy, sumBy, meanBy, countBy } = await import('https://esm.sh/lodash-es@4.17.21');
|
|
||||||
|
|
||||||
const { data } = parse(csv, { header: true, skipEmptyLines: true });
|
|
||||||
|
|
||||||
const filtered = data.filter(row =>
|
|
||||||
String(row[config.filterColumn]) === String(config.filterValue)
|
|
||||||
);
|
|
||||||
|
|
||||||
const grouped = Object.entries(
|
|
||||||
groupBy(filtered, config.groupBy)
|
|
||||||
);
|
|
||||||
|
|
||||||
const ops = {
|
|
||||||
sum: arr => sumBy(arr, r => parseFloat(r[config.aggregateColumn]) || 0),
|
|
||||||
avg: arr => meanBy(arr, r => parseFloat(r[config.aggregateColumn]) || 0),
|
|
||||||
count: arr => arr.length
|
|
||||||
};
|
|
||||||
|
|
||||||
return grouped.map(([key, rows]) => ({
|
|
||||||
[config.groupBy]: key,
|
|
||||||
result: ops[config.operation](rows)
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
export default processCSV;
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
const processCSV = async (
|
|
||||||
csvString, {
|
|
||||||
filterColumn,
|
|
||||||
filterValue,
|
|
||||||
groupBy,
|
|
||||||
aggregateColumn,
|
|
||||||
operation
|
|
||||||
}
|
|
||||||
) => {
|
|
||||||
const [Papa, {
|
|
||||||
default: _
|
|
||||||
}] = await Promise.all([
|
|
||||||
import('https://cdn.jsdelivr.net/npm/papaparse@5.4.1/papaparse.min.js'),
|
|
||||||
import('https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/+esm'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
const {
|
|
||||||
data
|
|
||||||
} = Papa.parse(csvString, {
|
|
||||||
header: true,
|
|
||||||
dynamicTyping: true,
|
|
||||||
skipEmptyLines: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const aggregators = {
|
|
||||||
sum: g => _.sumBy(g, aggregateColumn),
|
|
||||||
avg: g => _.meanBy(g, aggregateColumn),
|
|
||||||
count: g => g.length,
|
|
||||||
};
|
|
||||||
|
|
||||||
return _.chain(data)
|
|
||||||
.filter({
|
|
||||||
[filterColumn]: filterValue
|
|
||||||
})
|
|
||||||
.groupBy(groupBy)
|
|
||||||
.map((rows, key) => ({
|
|
||||||
[groupBy]: Number.isNaN(+key) ? key : +key,
|
|
||||||
result: aggregators[operation](rows),
|
|
||||||
}))
|
|
||||||
.value();
|
|
||||||
};
|
|
||||||
export default processCSV;
|
|
||||||
38
tests/6_csv_processor/outputs/google_gemini-2.5-pro.js
Normal file
38
tests/6_csv_processor/outputs/google_gemini-2.5-pro.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
const processCSV = async (csvString, config) => {
|
||||||
|
const {
|
||||||
|
filterColumn,
|
||||||
|
filterValue,
|
||||||
|
groupBy,
|
||||||
|
aggregateColumn,
|
||||||
|
operation,
|
||||||
|
} = config;
|
||||||
|
|
||||||
|
const [Papa, _] = await Promise.all([
|
||||||
|
import('https://cdn.jsdelivr.net/npm/papaparse@5.4.1/+esm'),
|
||||||
|
import('https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/+esm'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const aggregators = {
|
||||||
|
sum: items => _.sumBy(items, aggregateColumn),
|
||||||
|
count: items => items.length,
|
||||||
|
avg: items => _.sumBy(items, aggregateColumn) / items.length || 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const { data } = await new Promise(resolve =>
|
||||||
|
Papa.parse(csvString, {
|
||||||
|
header: true,
|
||||||
|
dynamicTyping: true,
|
||||||
|
skipEmptyLines: true,
|
||||||
|
complete: resolve,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const filteredData = data.filter(row => row[filterColumn] === filterValue);
|
||||||
|
const groupedData = _.groupBy(filteredData, groupBy);
|
||||||
|
|
||||||
|
return Object.entries(groupedData).map(([key, group]) => ({
|
||||||
|
[groupBy]: isNaN(Number(key)) ? key : Number(key),
|
||||||
|
result: aggregators[operation](group),
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
export default processCSV;
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
let cache;
|
|
||||||
const load=()=>cache||(cache=Promise.all([
|
|
||||||
import('https://cdn.jsdelivr.net/npm/papaparse@5.4.1/papaparse.min.mjs'),
|
|
||||||
import('https://cdn.jsdelivr.net/npm/d3-array@3/+esm')
|
|
||||||
]));
|
|
||||||
const processCSV=async(csv,cfg)=>{
|
|
||||||
const[{default:Papa},{rollup}]=await load();
|
|
||||||
const{filterColumn:f,filterValue:v,groupBy:g,aggregateColumn:a,operation:o}=cfg;
|
|
||||||
const rows=Papa.parse(csv,{header:1,dynamicTyping:1,skipEmptyLines:1}).data.filter(r=>r[f]===v);
|
|
||||||
if(!rows.length)return[];
|
|
||||||
const map=rollup(rows,s=>{
|
|
||||||
if(o==='count')return s.length;
|
|
||||||
const t=s.reduce((p,c)=>p+(Number(c[a])||0),0);
|
|
||||||
return o==='avg'?t/s.length:t;
|
|
||||||
},r=>r[g]);
|
|
||||||
return Array.from(map,([k,val])=>({[g]:k,result:val}));
|
|
||||||
};
|
|
||||||
export default processCSV;
|
|
||||||
16
tests/6_csv_processor/outputs/openai_gpt-5.1-codex.js
Normal file
16
tests/6_csv_processor/outputs/openai_gpt-5.1-codex.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
export const processCSV=async(csv,cfg)=>{
|
||||||
|
const [{parse},{groupBy,sumBy}]=await Promise.all([
|
||||||
|
import('https://cdn.jsdelivr.net/npm/papaparse@5.4.1/+esm'),
|
||||||
|
import('https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/+esm')
|
||||||
|
]);
|
||||||
|
const {filterColumn:fc,filterValue:fv,groupBy:gb,aggregateColumn:ac,operation:op}=cfg;
|
||||||
|
const rows=parse(csv,{header:!0,skipEmptyLines:!0}).data.filter(r=>r[fc]==fv);
|
||||||
|
const grouped=groupBy(rows,r=>r[gb]);
|
||||||
|
const agg=v=>{
|
||||||
|
if(op=='count')return v.length;
|
||||||
|
const total=sumBy(v,x=>+x[ac]||0);
|
||||||
|
return op=='avg'&&v.length?total/v.length:total;
|
||||||
|
};
|
||||||
|
return Object.entries(grouped).map(([k,v])=>({[gb]:k,result:agg(v)}));
|
||||||
|
};
|
||||||
|
export default processCSV;
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
async function processCSV(csv, c) {
|
|
||||||
if (typeof csv !== 'string') throw new TypeError('csv must be string')
|
|
||||||
if (!c || typeof c !== 'object') throw new TypeError('config required')
|
|
||||||
let { filterColumn, filterValue, groupBy, aggregateColumn, operation } = c
|
|
||||||
if (!filterColumn || !groupBy || !operation) throw new Error('missing config')
|
|
||||||
if (!['sum', 'avg', 'count'].includes(operation)) throw new Error('bad op')
|
|
||||||
|
|
||||||
const { parse } = await import('https://cdn.skypack.dev/papaparse@5.4.1')
|
|
||||||
const _ = (await import('https://cdn.skypack.dev/lodash-es@4.17.21')).default
|
|
||||||
|
|
||||||
const { data, errors } = parse(csv, { header: true, dynamicTyping: true, skipEmptyLines: true })
|
|
||||||
if (errors && errors.length) throw new Error('csv parse error')
|
|
||||||
|
|
||||||
const rows = _.filter(data, r => r && r[filterColumn] === filterValue)
|
|
||||||
if (!rows.length) return []
|
|
||||||
|
|
||||||
const grouped = _.groupBy(rows, r => r[groupBy])
|
|
||||||
const out = []
|
|
||||||
|
|
||||||
_.forOwn(grouped, (items, k) => {
|
|
||||||
let result
|
|
||||||
if (operation === 'count') {
|
|
||||||
result = items.length
|
|
||||||
} else {
|
|
||||||
const nums = _.map(items, i => Number(i[aggregateColumn])).filter(v => Number.isFinite(v))
|
|
||||||
if (!nums.length) return
|
|
||||||
const sum = _.sum(nums)
|
|
||||||
result = operation === 'sum' ? sum : sum / nums.length
|
|
||||||
}
|
|
||||||
out.push({ [groupBy]: k, result })
|
|
||||||
})
|
|
||||||
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
export default processCSV;
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
const findAvailableSlots = async (cal1, cal2, constraints) => {
|
|
||||||
const { default: dayjs } = await import('https://cdn.jsdelivr.net/npm/dayjs@1.11.10/+esm');
|
|
||||||
const [{ default: utc }, { default: customParseFormat }, { default: isBetween }] = await Promise.all([
|
|
||||||
import('https://cdn.jsdelivr.net/npm/dayjs@1.11.10/plugin/utc.js/+esm'),
|
|
||||||
import('https://cdn.jsdelivr.net/npm/dayjs@1.11.10/plugin/customParseFormat.js/+esm'),
|
|
||||||
import('https://cdn.jsdelivr.net/npm/dayjs@1.11.10/plugin/isBetween.js/+esm')
|
|
||||||
]);
|
|
||||||
|
|
||||||
dayjs.extend(utc);
|
|
||||||
dayjs.extend(customParseFormat);
|
|
||||||
dayjs.extend(isBetween);
|
|
||||||
|
|
||||||
const { durationMinutes, searchRange, workHours } = constraints;
|
|
||||||
const [whStart, whEnd] = [workHours.start.split(':'), workHours.end.split(':')];
|
|
||||||
|
|
||||||
const allBusy = [...cal1, ...cal2]
|
|
||||||
.map(({ start, end }) => ({ start: dayjs(start), end: dayjs(end) }))
|
|
||||||
.sort((a, b) => a.start - b.start);
|
|
||||||
|
|
||||||
const merged = allBusy.reduce((acc, curr) => {
|
|
||||||
if (!acc.length || acc[acc.length - 1].end < curr.start) {
|
|
||||||
acc.push(curr);
|
|
||||||
} else {
|
|
||||||
acc[acc.length - 1].end = dayjs.max(acc[acc.length - 1].end, curr.end);
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const slots = [];
|
|
||||||
let current = dayjs(searchRange.start);
|
|
||||||
const searchEnd = dayjs(searchRange.end);
|
|
||||||
|
|
||||||
while (current < searchEnd) {
|
|
||||||
const dayStart = current.hour(+whStart[0]).minute(+whStart[1]).second(0);
|
|
||||||
const dayEnd = current.hour(+whEnd[0]).minute(+whEnd[1]).second(0);
|
|
||||||
|
|
||||||
let pointer = dayStart > current ? dayStart : current;
|
|
||||||
|
|
||||||
for (const busy of merged) {
|
|
||||||
if (busy.start >= dayEnd) break;
|
|
||||||
if (busy.end <= pointer) continue;
|
|
||||||
|
|
||||||
while (pointer.add(durationMinutes, 'minute') <= dayjs.min(busy.start, dayEnd)) {
|
|
||||||
const slotEnd = pointer.add(durationMinutes, 'minute');
|
|
||||||
if (pointer >= dayStart && slotEnd <= dayEnd && pointer >= dayjs(searchRange.start)) {
|
|
||||||
slots.push({
|
|
||||||
start: pointer.toISOString(),
|
|
||||||
end: slotEnd.toISOString()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
pointer = pointer.add(durationMinutes, 'minute');
|
|
||||||
}
|
|
||||||
pointer = dayjs.max(pointer, busy.end);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (pointer.add(durationMinutes, 'minute') <= dayEnd) {
|
|
||||||
const slotEnd = pointer.add(durationMinutes, 'minute');
|
|
||||||
if (pointer >= dayStart && slotEnd <= searchEnd) {
|
|
||||||
slots.push({
|
|
||||||
start: pointer.toISOString(),
|
|
||||||
end: slotEnd.toISOString()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
pointer = pointer.add(durationMinutes, 'minute');
|
|
||||||
}
|
|
||||||
|
|
||||||
current = current.add(1, 'day').startOf('day');
|
|
||||||
}
|
|
||||||
|
|
||||||
return slots;
|
|
||||||
};
|
|
||||||
export default findAvailableSlots;
|
|
||||||
@@ -1,85 +1,108 @@
|
|||||||
const findAvailableSlots = async (cal1, cal2, constraints) => {
|
const findAvailableSlots = async (cal1, cal2, constraints) => {
|
||||||
const { DateTime, Interval } = await import('https://cdn.skypack.dev/luxon');
|
const { default: dayjs } = await import('https://cdn.jsdelivr.net/npm/dayjs@1.11.10/+esm');
|
||||||
|
const [{ default: utc }, { default: customParseFormat }, { default: isBetween }] = await Promise.all([
|
||||||
|
import('https://cdn.jsdelivr.net/npm/dayjs@1.11.10/plugin/utc.js'),
|
||||||
|
import('https://cdn.jsdelivr.net/npm/dayjs@1.11.10/plugin/customParseFormat.js'),
|
||||||
|
import('https://cdn.jsdelivr.net/npm/dayjs@1.11.10/plugin/isBetween.js')
|
||||||
|
]);
|
||||||
|
|
||||||
const { durationMinutes: dur, searchRange: range, workHours: wh } = constraints;
|
dayjs.extend(utc);
|
||||||
const [rangeStart, rangeEnd] = [DateTime.fromISO(range.start), DateTime.fromISO(range.end)];
|
dayjs.extend(customParseFormat);
|
||||||
const [whStart, whEnd] = wh.start.split(':').map(Number);
|
dayjs.extend(isBetween);
|
||||||
const [whStartMin, whEndMin] = [whStart * 60 + (wh.start.split(':')[1] || 0),
|
|
||||||
whEnd.split(':').map(Number).reduce((h, m) => h * 60 + m)];
|
const { durationMinutes, searchRange, workHours } = constraints;
|
||||||
|
const allBusy = [...cal1, ...cal2].map(s => ({
|
||||||
const mergeIntervals = (intervals) => {
|
start: dayjs(s.start),
|
||||||
if (!intervals.length) return [];
|
end: dayjs(s.end)
|
||||||
const sorted = intervals.sort((a, b) => a.start - b.start);
|
})).sort((a, b) => a.start - b.start);
|
||||||
const merged = [sorted[0]];
|
|
||||||
for (let i = 1; i < sorted.length; i++) {
|
const merged = allBusy.reduce((acc, curr) => {
|
||||||
const last = merged[merged.length - 1];
|
if (!acc.length || acc[acc.length - 1].end < curr.start) {
|
||||||
if (sorted[i].start <= last.end) {
|
acc.push(curr);
|
||||||
last.end = last.end > sorted[i].end ? last.end : sorted[i].end;
|
|
||||||
} else {
|
|
||||||
merged.push(sorted[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return merged;
|
|
||||||
};
|
|
||||||
|
|
||||||
const toBusy = (cal) => cal.map(({ start, end }) => ({
|
|
||||||
start: DateTime.fromISO(start),
|
|
||||||
end: DateTime.fromISO(end)
|
|
||||||
}));
|
|
||||||
|
|
||||||
const allBusy = mergeIntervals([...toBusy(cal1), ...toBusy(cal2)]);
|
|
||||||
|
|
||||||
const isWorkHour = (dt) => {
|
|
||||||
const min = dt.hour * 60 + dt.minute;
|
|
||||||
return min >= whStartMin && min < whEndMin;
|
|
||||||
};
|
|
||||||
|
|
||||||
const slots = [];
|
|
||||||
let cursor = rangeStart;
|
|
||||||
|
|
||||||
while (cursor < rangeEnd) {
|
|
||||||
if (!isWorkHour(cursor)) {
|
|
||||||
cursor = cursor.plus({ minutes: 1 });
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const slotEnd = cursor.plus({ minutes: dur });
|
|
||||||
|
|
||||||
if (slotEnd > rangeEnd) break;
|
|
||||||
|
|
||||||
let validSlot = true;
|
|
||||||
let tempCursor = cursor;
|
|
||||||
while (tempCursor < slotEnd) {
|
|
||||||
if (!isWorkHour(tempCursor)) {
|
|
||||||
validSlot = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
tempCursor = tempCursor.plus({ minutes: 1 });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!validSlot) {
|
|
||||||
cursor = cursor.plus({ minutes: 1 });
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const overlaps = allBusy.some(busy =>
|
|
||||||
(cursor >= busy.start && cursor < busy.end) ||
|
|
||||||
(slotEnd > busy.start && slotEnd <= busy.end) ||
|
|
||||||
(cursor <= busy.start && slotEnd >= busy.end)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!overlaps) {
|
|
||||||
slots.push({
|
|
||||||
start: cursor.toISO(),
|
|
||||||
end: slotEnd.toISO()
|
|
||||||
});
|
|
||||||
cursor = slotEnd;
|
|
||||||
} else {
|
} else {
|
||||||
const nextBusy = allBusy.find(b => b.end > cursor && b.start <= cursor);
|
acc[acc.length - 1].end = dayjs.max(acc[acc.length - 1].end, curr.end);
|
||||||
cursor = nextBusy ? nextBusy.end : cursor.plus({ minutes: 1 });
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const slots = [];
|
||||||
|
const searchStart = dayjs(searchRange.start);
|
||||||
|
const searchEnd = dayjs(searchRange.end);
|
||||||
|
const [whStart, whEnd] = [
|
||||||
|
dayjs(workHours.start, 'HH:mm'),
|
||||||
|
dayjs(workHours.end, 'HH:mm')
|
||||||
|
];
|
||||||
|
|
||||||
|
const isInWorkHours = (dt) => {
|
||||||
|
const time = dayjs().hour(dt.hour()).minute(dt.minute());
|
||||||
|
return time.isBetween(whStart, whEnd, null, '[)');
|
||||||
|
};
|
||||||
|
|
||||||
|
const addSlot = (start, end) => {
|
||||||
|
let curr = start;
|
||||||
|
while (curr.add(durationMinutes, 'minute') <= end) {
|
||||||
|
if (isInWorkHours(curr) && isInWorkHours(curr.add(durationMinutes, 'minute').subtract(1, 'second'))) {
|
||||||
|
const slotEnd = curr.add(durationMinutes, 'minute');
|
||||||
|
const dayEnd = curr.hour(whEnd.hour()).minute(whEnd.minute());
|
||||||
|
if (slotEnd <= dayEnd) {
|
||||||
|
slots.push({
|
||||||
|
start: curr.toISOString(),
|
||||||
|
end: slotEnd.toISOString()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
curr = curr.add(15, 'minute');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let prevEnd = searchStart.hour(whStart.hour()).minute(whStart.minute());
|
||||||
|
if (prevEnd < searchStart) prevEnd = searchStart;
|
||||||
|
|
||||||
|
merged.forEach(busy => {
|
||||||
|
if (busy.start > prevEnd) {
|
||||||
|
let gapStart = prevEnd;
|
||||||
|
let gapEnd = busy.start;
|
||||||
|
|
||||||
|
let day = gapStart.startOf('day');
|
||||||
|
while (day <= gapEnd) {
|
||||||
|
const dayStart = dayjs.max(
|
||||||
|
gapStart,
|
||||||
|
day.hour(whStart.hour()).minute(whStart.minute())
|
||||||
|
);
|
||||||
|
const dayEnd = dayjs.min(
|
||||||
|
gapEnd,
|
||||||
|
day.hour(whEnd.hour()).minute(whEnd.minute())
|
||||||
|
);
|
||||||
|
|
||||||
|
if (dayStart < dayEnd && dayStart >= searchStart && dayEnd <= searchEnd) {
|
||||||
|
addSlot(dayStart, dayEnd);
|
||||||
|
}
|
||||||
|
day = day.add(1, 'day');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prevEnd = dayjs.max(prevEnd, busy.end);
|
||||||
|
});
|
||||||
|
|
||||||
|
const finalEnd = searchEnd.hour(whEnd.hour()).minute(whEnd.minute());
|
||||||
|
if (prevEnd < finalEnd) {
|
||||||
|
let day = prevEnd.startOf('day');
|
||||||
|
while (day <= finalEnd) {
|
||||||
|
const dayStart = dayjs.max(
|
||||||
|
prevEnd,
|
||||||
|
day.hour(whStart.hour()).minute(whStart.minute())
|
||||||
|
);
|
||||||
|
const dayEnd = dayjs.min(
|
||||||
|
finalEnd,
|
||||||
|
day.hour(whEnd.hour()).minute(whEnd.minute())
|
||||||
|
);
|
||||||
|
|
||||||
|
if (dayStart < dayEnd && dayStart >= searchStart && dayEnd <= searchEnd) {
|
||||||
|
addSlot(dayStart, dayEnd);
|
||||||
|
}
|
||||||
|
day = day.add(1, 'day');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return slots;
|
return slots;
|
||||||
};
|
};
|
||||||
export default findAvailableSlots;
|
export default findAvailableSlots;
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
async function findAvailableSlots(cal1, cal2, constraints) {
|
|
||||||
const { default: dayjs } = await import('https://cdn.jsdelivr.net/npm/dayjs@1.11.10/+esm');
|
|
||||||
const [{ default: utc }, { default: isBetween }, { default: customParseFormat }] = await Promise.all([
|
|
||||||
import('https://cdn.jsdelivr.net/npm/dayjs@1.11.10/esm/plugin/utc/+esm'),
|
|
||||||
import('https://cdn.jsdelivr.net/npm/dayjs@1.11.10/esm/plugin/isBetween/+esm'),
|
|
||||||
import('https://cdn.jsdelivr.net/npm/dayjs@1.11.10/esm/plugin/customParseFormat/+esm')
|
|
||||||
]);
|
|
||||||
|
|
||||||
dayjs.extend(utc);
|
|
||||||
dayjs.extend(isBetween);
|
|
||||||
dayjs.extend(customParseFormat);
|
|
||||||
|
|
||||||
const { durationMinutes, searchRange, workHours } = constraints;
|
|
||||||
const allBusy = [...cal1, ...cal2]
|
|
||||||
.map(s => ({ start: dayjs(s.start), end: dayjs(s.end) }))
|
|
||||||
.sort((a, b) => a.start.valueOf() - b.start.valueOf());
|
|
||||||
|
|
||||||
const merged = allBusy.reduce((acc, curr) => {
|
|
||||||
if (!acc.length || acc[acc.length - 1].end.isBefore(curr.start)) {
|
|
||||||
acc.push(curr);
|
|
||||||
} else {
|
|
||||||
acc[acc.length - 1].end = dayjs.max(acc[acc.length - 1].end, curr.end);
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const slots = [];
|
|
||||||
const rangeStart = dayjs(searchRange.start);
|
|
||||||
const rangeEnd = dayjs(searchRange.end);
|
|
||||||
const [workStart, workEnd] = [workHours.start, workHours.end];
|
|
||||||
|
|
||||||
const getWorkBounds = (date) => {
|
|
||||||
const [sh, sm] = workStart.split(':').map(Number);
|
|
||||||
const [eh, em] = workEnd.split(':').map(Number);
|
|
||||||
return {
|
|
||||||
start: date.hour(sh).minute(sm).second(0).millisecond(0),
|
|
||||||
end: date.hour(eh).minute(em).second(0).millisecond(0)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
let currentDay = rangeStart.startOf('day');
|
|
||||||
while (currentDay.isBefore(rangeEnd) || currentDay.isSame(rangeEnd, 'day')) {
|
|
||||||
const { start: dayWorkStart, end: dayWorkEnd } = getWorkBounds(currentDay);
|
|
||||||
const dayStart = dayjs.max(dayWorkStart, rangeStart);
|
|
||||||
const dayEnd = dayjs.min(dayWorkEnd, rangeEnd);
|
|
||||||
|
|
||||||
if (dayStart.isBefore(dayEnd)) {
|
|
||||||
let cursor = dayStart;
|
|
||||||
|
|
||||||
for (const busy of merged) {
|
|
||||||
if (busy.end.isBefore(dayStart) || busy.start.isAfter(dayEnd)) continue;
|
|
||||||
|
|
||||||
const gapEnd = dayjs.min(busy.start, dayEnd);
|
|
||||||
while (cursor.add(durationMinutes, 'minute').isSameOrBefore(gapEnd)) {
|
|
||||||
slots.push({
|
|
||||||
start: cursor.toISOString(),
|
|
||||||
end: cursor.add(durationMinutes, 'minute').toISOString()
|
|
||||||
});
|
|
||||||
cursor = cursor.add(durationMinutes, 'minute');
|
|
||||||
}
|
|
||||||
cursor = dayjs.max(cursor, busy.end);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (cursor.add(durationMinutes, 'minute').isSameOrBefore(dayEnd)) {
|
|
||||||
slots.push({
|
|
||||||
start: cursor.toISOString(),
|
|
||||||
end: cursor.add(durationMinutes, 'minute').toISOString()
|
|
||||||
});
|
|
||||||
cursor = cursor.add(durationMinutes, 'minute');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
currentDay = currentDay.add(1, 'day');
|
|
||||||
}
|
|
||||||
|
|
||||||
return slots;
|
|
||||||
}
|
|
||||||
export default findAvailableSlots;
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
async function findAvailableSlots(calendar1, calendar2, constraints) {
|
|
||||||
const [dayjsModule, durationModule] = await Promise.all([
|
|
||||||
import('https://cdn.jsdelivr.net/npm/dayjs@1/dayjs.min.js'),
|
|
||||||
import('https://cdn.jsdelivr.net/npm/dayjs@1/plugin/duration.js')
|
|
||||||
]);
|
|
||||||
const dayjs = dayjsModule.default;
|
|
||||||
dayjs.extend(durationModule.default);
|
|
||||||
|
|
||||||
const { durationMinutes: duration, searchRange, workHours } = constraints;
|
|
||||||
const searchStart = dayjs(searchRange.start);
|
|
||||||
const searchEnd = dayjs(searchRange.end);
|
|
||||||
const [workStartH, workStartM] = workHours.start.split(':').map(Number);
|
|
||||||
const [workEndH, workEndM] = workHours.end.split(':').map(Number);
|
|
||||||
|
|
||||||
const toDayjs = ({ start, end }) => ({ start: dayjs(start), end: dayjs(end) });
|
|
||||||
const allBusy = [...calendar1, ...calendar2].map(toDayjs);
|
|
||||||
|
|
||||||
for (let day = searchStart.clone().startOf('day'); day.isBefore(searchEnd); day = day.add(1, 'day')) {
|
|
||||||
allBusy.push({
|
|
||||||
start: day,
|
|
||||||
end: day.hour(workStartH).minute(workStartM)
|
|
||||||
});
|
|
||||||
allBusy.push({
|
|
||||||
start: day.hour(workEndH).minute(workEndM),
|
|
||||||
end: day.endOf('day')
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const mergedBusy = allBusy
|
|
||||||
.sort((a, b) => a.start - b.start)
|
|
||||||
.reduce((acc, slot) => {
|
|
||||||
const last = acc.at(-1);
|
|
||||||
if (last && slot.start <= last.end) {
|
|
||||||
if (slot.end > last.end) last.end = slot.end;
|
|
||||||
} else {
|
|
||||||
acc.push({ ...slot });
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const availableSlots = [];
|
|
||||||
let nextFreeStart = searchStart;
|
|
||||||
|
|
||||||
const findSlotsInGap = (start, end) => {
|
|
||||||
let slotStart = start;
|
|
||||||
while (slotStart.add(duration, 'minute') <= end) {
|
|
||||||
const slotEnd = slotStart.add(duration, 'minute');
|
|
||||||
availableSlots.push({ start: slotStart.toISOString(), end: slotEnd.toISOString() });
|
|
||||||
slotStart = slotEnd;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
mergedBusy.forEach(busySlot => {
|
|
||||||
if (busySlot.start > nextFreeStart) {
|
|
||||||
findSlotsInGap(nextFreeStart, busySlot.start);
|
|
||||||
}
|
|
||||||
if (busySlot.end > nextFreeStart) {
|
|
||||||
nextFreeStart = busySlot.end;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (searchEnd > nextFreeStart) {
|
|
||||||
findSlotsInGap(nextFreeStart, searchEnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
return availableSlots;
|
|
||||||
}
|
|
||||||
export default findAvailableSlots;
|
|
||||||
69
tests/7_scheduler/outputs/google_gemini-2.5-pro.js
Normal file
69
tests/7_scheduler/outputs/google_gemini-2.5-pro.js
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
async function findAvailableSlots(cal1, cal2, c) {
|
||||||
|
const {
|
||||||
|
parseISO, formatISO, addMinutes, isAfter, max, startOfDay,
|
||||||
|
setHours, setMinutes, setSeconds, setMilliseconds, eachDayOfInterval
|
||||||
|
} = await import('https://cdn.jsdelivr.net/npm/date-fns@3.6.0/index.mjs');
|
||||||
|
|
||||||
|
const { durationMinutes: dur, searchRange: sr, workHours: wh } = c;
|
||||||
|
const sStart = parseISO(sr.start);
|
||||||
|
const sEnd = parseISO(sr.end);
|
||||||
|
const toDate = s => parseISO(s);
|
||||||
|
|
||||||
|
const [whsH, whsM] = wh.start.split(':').map(Number);
|
||||||
|
const [wheH, wheM] = wh.end.split(':').map(Number);
|
||||||
|
|
||||||
|
const applyTime = (d, h, m) =>
|
||||||
|
setMilliseconds(setSeconds(setMinutes(setHours(startOfDay(d), h), m), 0), 0);
|
||||||
|
|
||||||
|
const busy = [...cal1, ...cal2].map(({ start, end }) => ({
|
||||||
|
start: toDate(start),
|
||||||
|
end: toDate(end),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const nonWork = [];
|
||||||
|
if (sStart < sEnd) {
|
||||||
|
for (const day of eachDayOfInterval({ start: sStart, end: sEnd })) {
|
||||||
|
nonWork.push({
|
||||||
|
start: startOfDay(day),
|
||||||
|
end: applyTime(day, whsH, whsM)
|
||||||
|
});
|
||||||
|
nonWork.push({
|
||||||
|
start: applyTime(day, wheH, wheM),
|
||||||
|
end: addMinutes(startOfDay(day), 1440)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const unavailable = [...busy, ...nonWork].sort((a, b) => a.start - b.start);
|
||||||
|
|
||||||
|
const merged = unavailable.reduce((acc, slot) => {
|
||||||
|
const last = acc[acc.length - 1];
|
||||||
|
if (last && slot.start <= last.end) {
|
||||||
|
last.end = max(last.end, slot.end);
|
||||||
|
} else {
|
||||||
|
acc.push({ ...slot });
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const results = [];
|
||||||
|
let ptr = sStart;
|
||||||
|
const allBlocks = [...merged, { start: sEnd, end: sEnd }];
|
||||||
|
|
||||||
|
for (const block of allBlocks) {
|
||||||
|
let slotStart = ptr;
|
||||||
|
while (true) {
|
||||||
|
const slotEnd = addMinutes(slotStart, dur);
|
||||||
|
if (isAfter(slotEnd, block.start)) break;
|
||||||
|
results.push({ start: slotStart, end: slotEnd });
|
||||||
|
slotStart = slotEnd;
|
||||||
|
}
|
||||||
|
ptr = max(ptr, block.end);
|
||||||
|
}
|
||||||
|
|
||||||
|
return results.map(({ start, end }) => ({
|
||||||
|
start: formatISO(start),
|
||||||
|
end: formatISO(end),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
export default findAvailableSlots;
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
let cache
|
|
||||||
const load=()=>cache||(cache=import('https://cdn.skypack.dev/date-fns@2.30.0?min'))
|
|
||||||
async function findAvailableSlots(calA,calB,rules){
|
|
||||||
const {parseISO:iso,addMinutes:plus,differenceInMinutes:gap,eachDayOfInterval:days,set:setTime,max:maxDate,min:minDate}=await load()
|
|
||||||
const parseCal=c=>c.map(({start,end})=>({start:iso(start),end:iso(end)}))
|
|
||||||
const [sh,sm]=rules.workHours.start.split(':').map(Number)
|
|
||||||
const [eh,em]=rules.workHours.end.split(':').map(Number)
|
|
||||||
const rangeStart=iso(rules.searchRange.start),rangeEnd=iso(rules.searchRange.end)
|
|
||||||
const entries=[...parseCal(calA),...parseCal(calB)].filter(b=>b.end>rangeStart&&b.start<rangeEnd).sort((a,b)=>a.start-b.start)
|
|
||||||
const slots=[]
|
|
||||||
const addFree=(s,e)=>{
|
|
||||||
let cursor=s
|
|
||||||
while(gap(e,cursor)>=rules.durationMinutes){
|
|
||||||
const stop=plus(cursor,rules.durationMinutes)
|
|
||||||
slots.push({start:cursor.toISOString(),end:stop.toISOString()})
|
|
||||||
cursor=stop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(const day of days({start:rangeStart,end:rangeEnd})){
|
|
||||||
const workStart=setTime(day,{hours:sh,minutes:sm,seconds:0,milliseconds:0})
|
|
||||||
const workEnd=setTime(day,{hours:eh,minutes:em,seconds:0,milliseconds:0})
|
|
||||||
let spanStart=maxDate([workStart,rangeStart]),spanEnd=minDate([workEnd,rangeEnd])
|
|
||||||
if(spanStart>=spanEnd)continue
|
|
||||||
const busy=entries.filter(b=>b.end>spanStart&&b.start<spanEnd).map(b=>({start:maxDate([b.start,spanStart]),end:minDate([b.end,spanEnd])})).sort((a,b)=>a.start-b.start)
|
|
||||||
const merged=[]
|
|
||||||
for(const block of busy){
|
|
||||||
const last=merged[merged.length-1]
|
|
||||||
if(!last||block.start>last.end)merged.push({start:block.start,end:block.end})
|
|
||||||
else if(block.end>last.end)last.end=block.end
|
|
||||||
}
|
|
||||||
let cursor=spanStart
|
|
||||||
for(const block of merged){
|
|
||||||
if(block.start>cursor)addFree(cursor,block.start)
|
|
||||||
if(block.end>cursor)cursor=block.end
|
|
||||||
}
|
|
||||||
if(cursor<spanEnd)addFree(cursor,spanEnd)
|
|
||||||
}
|
|
||||||
return slots
|
|
||||||
}
|
|
||||||
export default findAvailableSlots;
|
|
||||||
59
tests/7_scheduler/outputs/openai_gpt-5.1-codex.js
Normal file
59
tests/7_scheduler/outputs/openai_gpt-5.1-codex.js
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
const findAvailableSlots = async (a, b, o) => {
|
||||||
|
const { parseISO, formatISO, addDays, startOfDay } = await import('https://cdn.skypack.dev/date-fns@2.30.0');
|
||||||
|
const dur = o.durationMinutes * 6e4;
|
||||||
|
const rng = [parseISO(o.searchRange.start), parseISO(o.searchRange.end)];
|
||||||
|
const split = t => t.split(':').map(Number);
|
||||||
|
const hrs = { s: split(o.workHours.start), e: split(o.workHours.end) };
|
||||||
|
const busy = [...a, ...b]
|
||||||
|
.map(v => ({ start: parseISO(v.start), end: parseISO(v.end) }))
|
||||||
|
.map(v => {
|
||||||
|
const s = new Date(Math.max(v.start, rng[0]));
|
||||||
|
const e = new Date(Math.min(v.end, rng[1]));
|
||||||
|
return s < e ? { start: s, end: e } : null;
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
|
.sort((x, y) => x.start - y.start)
|
||||||
|
.reduce((m, v) => {
|
||||||
|
const last = m[m.length - 1];
|
||||||
|
if (!last || v.start > last.end) m.push({ start: new Date(v.start), end: new Date(v.end) });
|
||||||
|
else if (v.end > last.end) last.end = new Date(v.end);
|
||||||
|
return m;
|
||||||
|
}, []);
|
||||||
|
const slots = [];
|
||||||
|
const collect = (s, e) => {
|
||||||
|
let a = s.getTime(), b = e.getTime();
|
||||||
|
while (a + dur <= b) {
|
||||||
|
const r = a + dur;
|
||||||
|
slots.push({ start: formatISO(new Date(a)), end: formatISO(new Date(r)) });
|
||||||
|
a = r;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let idx = 0;
|
||||||
|
for (let d = startOfDay(rng[0]); d <= rng[1]; d = addDays(d, 1)) {
|
||||||
|
const ws = new Date(d);
|
||||||
|
ws.setHours(hrs.s[0], hrs.s[1], 0, 0);
|
||||||
|
const we = new Date(d);
|
||||||
|
we.setHours(hrs.e[0], hrs.e[1], 0, 0);
|
||||||
|
const winStart = new Date(Math.max(ws, rng[0]));
|
||||||
|
const winEnd = new Date(Math.min(we, rng[1]));
|
||||||
|
if (winStart >= winEnd) continue;
|
||||||
|
let cursor = winStart;
|
||||||
|
while (idx < busy.length && busy[idx].end <= winStart) idx++;
|
||||||
|
let view = idx;
|
||||||
|
let done = false;
|
||||||
|
while (view < busy.length && busy[view].start < winEnd) {
|
||||||
|
const gapEnd = new Date(Math.min(busy[view].start, winEnd));
|
||||||
|
if (gapEnd > cursor) collect(cursor, gapEnd);
|
||||||
|
cursor = new Date(Math.max(cursor, busy[view].end));
|
||||||
|
if (cursor >= winEnd) {
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
view++;
|
||||||
|
}
|
||||||
|
if (!done && cursor < winEnd) collect(cursor, winEnd);
|
||||||
|
idx = view;
|
||||||
|
}
|
||||||
|
return slots;
|
||||||
|
};
|
||||||
|
export default findAvailableSlots;
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
async function findAvailableSlots(cal1, cal2, c) {
|
|
||||||
const { DateTime, Interval } = await import('https://cdn.skypack.dev/luxon')
|
|
||||||
const d = c.durationMinutes
|
|
||||||
const sR = c.searchRange
|
|
||||||
const wH = c.workHours
|
|
||||||
const z = 'UTC'
|
|
||||||
const p = (x, y) => DateTime.fromISO(x, { zone: y })
|
|
||||||
const b = c => c.map(v => Interval.fromDateTimes(p(v.start, z), p(v.end, z)))
|
|
||||||
const m = (a, b) => [...a, ...b].sort((x, y) => x.start - y.start)
|
|
||||||
const n = i => {
|
|
||||||
const r = []
|
|
||||||
let c = i[0]
|
|
||||||
for (let k = 1; k < i.length; k++) {
|
|
||||||
const v = i[k]
|
|
||||||
if (c.overlaps(v) || c.abutsStart(v)) c = Interval.fromDateTimes(c.start, c.end > v.end ? c.end : v.end)
|
|
||||||
else r.push(c), (c = v)
|
|
||||||
}
|
|
||||||
r.push(c)
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
const iA = b(cal1)
|
|
||||||
const iB = b(cal2)
|
|
||||||
const all = m(iA, iB)
|
|
||||||
const busy = all.length ? n(all) : []
|
|
||||||
const srI = Interval.fromDateTimes(p(sR.start, z), p(sR.end, z))
|
|
||||||
const whS = wH.start.split(':').map(Number)
|
|
||||||
const whE = wH.end.split(':').map(Number)
|
|
||||||
|
|
||||||
const dayI = []
|
|
||||||
let cur = srI.start.startOf('day')
|
|
||||||
const endDay = srI.end.startOf('day')
|
|
||||||
while (cur <= endDay) {
|
|
||||||
const ws = cur.set({ hour: whS[0], minute: whS[1] })
|
|
||||||
const we = cur.set({ hour: whE[0], minute: whE[1] })
|
|
||||||
const w = Interval.fromDateTimes(ws, we)
|
|
||||||
const clipped = srI.intersection(w)
|
|
||||||
if (clipped && clipped.isValid && clipped.length('minutes') >= d) dayI.push(clipped)
|
|
||||||
cur = cur.plus({ days: 1 })
|
|
||||||
}
|
|
||||||
|
|
||||||
const inv = []
|
|
||||||
let pS = null
|
|
||||||
for (const x of busy) {
|
|
||||||
const xs = x.start < srI.start ? srI.start : x.start
|
|
||||||
const xe = x.end > srI.end ? srI.end : x.end
|
|
||||||
if (!pS) {
|
|
||||||
if (xs > srI.start) inv.push(Interval.fromDateTimes(srI.start, xs))
|
|
||||||
pS = xe
|
|
||||||
} else {
|
|
||||||
if (xs > pS) inv.push(Interval.fromDateTimes(pS, xs))
|
|
||||||
if (xe > pS) pS = xe
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!busy.length) inv.push(srI)
|
|
||||||
else if (pS < srI.end) inv.push(Interval.fromDateTimes(pS, srI.end))
|
|
||||||
|
|
||||||
const freeWithinRange = inv
|
|
||||||
.map(v => dayI.map(dv => dv.intersection(v)).filter(x => x && x.isValid))
|
|
||||||
.flat()
|
|
||||||
.filter(x => x.length('minutes') >= d)
|
|
||||||
|
|
||||||
const res = []
|
|
||||||
for (const f of freeWithinRange) {
|
|
||||||
let st = f.start
|
|
||||||
const mins = f.length('minutes')
|
|
||||||
const steps = Math.floor(mins / d)
|
|
||||||
for (let i = 0; i < steps; i++) {
|
|
||||||
const en = st.plus({ minutes: d })
|
|
||||||
res.push({ start: st.toISO(), end: en.toISO() })
|
|
||||||
st = en
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
export default findAvailableSlots;
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
const validateJSON = async (data, schema) => {
|
|
||||||
const { default: Ajv } = await import('https://esm.sh/ajv@8.12.0');
|
|
||||||
const ajv = new Ajv({ allErrors: true });
|
|
||||||
const validate = ajv.compile(schema);
|
|
||||||
const valid = validate(data);
|
|
||||||
|
|
||||||
return {
|
|
||||||
valid,
|
|
||||||
errors: valid ? [] : validate.errors.map(e =>
|
|
||||||
`${e.instancePath || '/'} ${e.message}`.trim()
|
|
||||||
)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
export default validateJSON;
|
|
||||||
@@ -1,14 +1,18 @@
|
|||||||
const validateJSON = async (data, schema) => {
|
const validateJSON = async (data, schema) => {
|
||||||
const { default: Ajv } = await import('https://esm.sh/ajv@8.12.0');
|
const { default: Ajv } = await import('https://cdn.jsdelivr.net/npm/ajv@8.12.0/dist/2020.min.js');
|
||||||
const ajv = new Ajv({ allErrors: true });
|
|
||||||
|
const ajv = new Ajv({ allErrors: true, verbose: true });
|
||||||
const validate = ajv.compile(schema);
|
const validate = ajv.compile(schema);
|
||||||
const valid = validate(data);
|
const valid = validate(data);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
valid,
|
valid,
|
||||||
errors: valid ? [] : validate.errors.map(e =>
|
errors: valid ? [] : validate.errors.map(e => ({
|
||||||
`${e.instancePath || '/'} ${e.message}`.trim()
|
path: e.instancePath || '/',
|
||||||
)
|
message: e.message,
|
||||||
|
keyword: e.keyword,
|
||||||
|
params: e.params
|
||||||
|
}))
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
export default validateJSON;
|
export default validateJSON;
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
const validateJSON = async (data, schema) => {
|
|
||||||
const { default: Ajv } = await import('https://esm.sh/ajv@8.12.0');
|
|
||||||
const ajv = new Ajv({ allErrors: true });
|
|
||||||
const validate = ajv.compile(schema);
|
|
||||||
const valid = validate(data);
|
|
||||||
|
|
||||||
return {
|
|
||||||
valid,
|
|
||||||
errors: valid ? [] : validate.errors.map(err =>
|
|
||||||
`${err.instancePath || '/'} ${err.message}`.trim()
|
|
||||||
)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
export default validateJSON;
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
const validateJSON = (() => {
|
|
||||||
let ajvPromise;
|
|
||||||
const validatorCache = new WeakMap();
|
|
||||||
|
|
||||||
return async (json, schema) => {
|
|
||||||
ajvPromise ||= import('https://cdn.jsdelivr.net/npm/ajv@8/dist/ajv2020.min.js')
|
|
||||||
.then(({ default: Ajv }) => new Ajv({ allErrors: true }));
|
|
||||||
|
|
||||||
const ajv = await ajvPromise;
|
|
||||||
|
|
||||||
let validate = validatorCache.get(schema);
|
|
||||||
if (!validate) {
|
|
||||||
validate = ajv.compile(schema);
|
|
||||||
validatorCache.set(schema, validate);
|
|
||||||
}
|
|
||||||
|
|
||||||
const valid = validate(json);
|
|
||||||
const errors = valid ? [] : validate.errors.map(
|
|
||||||
({ instancePath, message }) => `${instancePath || 'object'} ${message}`
|
|
||||||
);
|
|
||||||
|
|
||||||
return { valid, errors };
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
export default validateJSON;
|
|
||||||
16
tests/8_json_validator/outputs/google_gemini-2.5-pro.js
Normal file
16
tests/8_json_validator/outputs/google_gemini-2.5-pro.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
let ajvPromise;
|
||||||
|
|
||||||
|
const validateJSON = async (data, schema) => {
|
||||||
|
const { default: Ajv } = await (ajvPromise ||= import('https://cdn.jsdelivr.net/npm/ajv@8.12.0/+esm'));
|
||||||
|
|
||||||
|
const ajv = new Ajv({ allErrors: true });
|
||||||
|
const validate = ajv.compile(schema);
|
||||||
|
const valid = validate(data);
|
||||||
|
|
||||||
|
const errors = valid ? [] : validate.errors.map(
|
||||||
|
e => (`${e.instancePath || 'root'} ${e.message}`).trim()
|
||||||
|
);
|
||||||
|
|
||||||
|
return { valid, errors };
|
||||||
|
};
|
||||||
|
export default validateJSON;
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
let ajv
|
|
||||||
const cache=new WeakMap()
|
|
||||||
const ensureAjv=async()=>ajv??=new (await import('https://cdn.jsdelivr.net/npm/ajv@8.12.0/+esm')).default({allErrors:true,strict:false})
|
|
||||||
const getValidator=async schema=>{
|
|
||||||
if(cache.has(schema))return cache.get(schema)
|
|
||||||
const v=(await ensureAjv()).compile(schema)
|
|
||||||
cache.set(schema,v)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
async function validateJSON(data,schema){
|
|
||||||
try{
|
|
||||||
const v=await getValidator(schema)
|
|
||||||
const valid=v(data)
|
|
||||||
const errors=valid?[]:(v.errors||[]).map(e=>{
|
|
||||||
const path=e.instancePath||'/'
|
|
||||||
const msg=e.message||'Invalid value'
|
|
||||||
const meta=e.params&&Object.keys(e.params).length?` ${JSON.stringify(e.params)}`:''
|
|
||||||
return`${path} ${msg}${meta}`.trim()
|
|
||||||
})
|
|
||||||
return{valid,errors}
|
|
||||||
}catch(err){
|
|
||||||
return{valid:false,errors:[err?.message||String(err)]}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export default validateJSON;
|
|
||||||
10
tests/8_json_validator/outputs/openai_gpt-5.1-codex.js
Normal file
10
tests/8_json_validator/outputs/openai_gpt-5.1-codex.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
const ajvP=import('https://cdn.skypack.dev/ajv@8?min').then(m=>m.default||m)
|
||||||
|
const ajvI=ajvP.then(Ajv=>new Ajv({allErrors:true}))
|
||||||
|
export async function validateJSON(data,schema){
|
||||||
|
const ajv=await ajvI
|
||||||
|
let validate
|
||||||
|
try{validate=ajv.compile(schema)}catch(e){return{valid:false,errors:[e.message]}}
|
||||||
|
const valid=validate(data)
|
||||||
|
return valid?{valid:true,errors:[]}:{valid:false,errors:validate.errors.map(e=>(e.instancePath||'/')+' '+e.message)}
|
||||||
|
}
|
||||||
|
export default validateJSON;
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
async function validateJSON(data, schema) {
|
|
||||||
const { default: Ajv } = await import('https://cdn.skypack.dev/ajv@8?min');
|
|
||||||
const { default: addFormats } = await import('https://cdn.skypack.dev/ajv-formats@2?min');
|
|
||||||
const ajv = new Ajv({ allErrors: true, strict: false });
|
|
||||||
addFormats(ajv);
|
|
||||||
const compile = s => {
|
|
||||||
const c = ajv.compile(s);
|
|
||||||
return d => ({ ok: c(d), errs: c.errors || [] });
|
|
||||||
};
|
|
||||||
const run = compile(schema);
|
|
||||||
const r = run(data);
|
|
||||||
if (r.ok) return { valid: true, errors: [] };
|
|
||||||
const errors = r.errs.map(e => {
|
|
||||||
const p = e.instancePath || e.dataPath || '';
|
|
||||||
const loc = p || e.schemaPath || '';
|
|
||||||
const msg = e.message || 'Invalid value';
|
|
||||||
return loc ? loc + ' ' + msg : msg;
|
|
||||||
});
|
|
||||||
return { valid: false, errors };
|
|
||||||
}
|
|
||||||
export default validateJSON;
|
|
||||||
Reference in New Issue
Block a user