@ace-grid/mcp 1.0.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/LICENSE +21 -0
- package/README.md +94 -0
- package/data/apiSpecCoverage.json +408 -0
- package/data/apiSpecQuality.json +15 -0
- package/data/docsIndex.json +4632 -0
- package/data/formulaFunctionSnapshot.json +1137 -0
- package/data/gridApiSnapshot.json +9162 -0
- package/dist/catalog.d.ts +229 -0
- package/dist/catalog.d.ts.map +1 -0
- package/dist/catalog.js +518 -0
- package/dist/demoServer.d.ts +2 -0
- package/dist/demoServer.d.ts.map +1 -0
- package/dist/demoServer.js +324 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +79 -0
- package/dist/portalApi.d.ts +18 -0
- package/dist/portalApi.d.ts.map +1 -0
- package/dist/portalApi.js +34 -0
- package/dist/tools.d.ts +78 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +154 -0
- package/package.json +58 -0
package/dist/catalog.js
ADDED
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
const TIER_RANK = {
|
|
3
|
+
Community: 0,
|
|
4
|
+
Pro: 1,
|
|
5
|
+
Enterprise: 2,
|
|
6
|
+
};
|
|
7
|
+
const PRO_FEATURE_KEYS = new Set([
|
|
8
|
+
"formula",
|
|
9
|
+
"rowGrouping",
|
|
10
|
+
"spanning",
|
|
11
|
+
"sparkline",
|
|
12
|
+
"treeData",
|
|
13
|
+
"validation",
|
|
14
|
+
]);
|
|
15
|
+
const ENTERPRISE_FEATURE_KEYS = new Set([
|
|
16
|
+
"charts",
|
|
17
|
+
"masterDetail",
|
|
18
|
+
"pivot",
|
|
19
|
+
"serverRowModel",
|
|
20
|
+
]);
|
|
21
|
+
const FEATURE_ALIASES = {
|
|
22
|
+
charts: ["chart", "charts", "integrated chart", "visualization"],
|
|
23
|
+
formula: ["formula", "formulas", "spreadsheet formula"],
|
|
24
|
+
masterDetail: ["master detail", "master-detail", "detail panel", "expand row"],
|
|
25
|
+
pivot: ["pivot", "pivoting", "cross tab", "crosstab"],
|
|
26
|
+
rowGrouping: ["row grouping", "group rows", "grouping"],
|
|
27
|
+
serverRowModel: ["server row model", "server-side", "ssrm", "lazy load", "remote rows"],
|
|
28
|
+
spanning: ["cell spanning", "span", "merge cells", "merged cells"],
|
|
29
|
+
sparkline: ["sparkline", "mini chart", "inline chart"],
|
|
30
|
+
treeData: ["tree data", "hierarchy", "hierarchical", "nested rows"],
|
|
31
|
+
validation: ["validation", "validate", "validator", "rules", "invalid cell"],
|
|
32
|
+
};
|
|
33
|
+
const FRAMEWORK_PACKAGES = {
|
|
34
|
+
angular: {
|
|
35
|
+
Community: ["@ace-grid/angular", "@ace-grid/core"],
|
|
36
|
+
Enterprise: ["@ace-grid/angular", "@ace-grid/enterprise"],
|
|
37
|
+
Pro: ["@ace-grid/angular", "@ace-grid/pro"],
|
|
38
|
+
},
|
|
39
|
+
react: {
|
|
40
|
+
Community: ["@ace-grid/core"],
|
|
41
|
+
Enterprise: ["@ace-grid/enterprise"],
|
|
42
|
+
Pro: ["@ace-grid/pro"],
|
|
43
|
+
},
|
|
44
|
+
svelte: {
|
|
45
|
+
Community: ["@ace-grid/svelte", "@ace-grid/core"],
|
|
46
|
+
Enterprise: ["@ace-grid/svelte", "@ace-grid/enterprise"],
|
|
47
|
+
Pro: ["@ace-grid/svelte", "@ace-grid/pro"],
|
|
48
|
+
},
|
|
49
|
+
vue: {
|
|
50
|
+
Community: ["@ace-grid/vue", "@ace-grid/core"],
|
|
51
|
+
Enterprise: ["@ace-grid/vue", "@ace-grid/enterprise"],
|
|
52
|
+
Pro: ["@ace-grid/vue", "@ace-grid/pro"],
|
|
53
|
+
},
|
|
54
|
+
"web-components": {
|
|
55
|
+
Community: ["@ace-grid/wc", "@ace-grid/core"],
|
|
56
|
+
Enterprise: ["@ace-grid/wc", "@ace-grid/enterprise"],
|
|
57
|
+
Pro: ["@ace-grid/wc", "@ace-grid/pro"],
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
function readJson(relativePath) {
|
|
61
|
+
return JSON.parse(readFileSync(new URL(relativePath, import.meta.url), "utf8"));
|
|
62
|
+
}
|
|
63
|
+
export const gridSnapshot = readJson("../data/gridApiSnapshot.json");
|
|
64
|
+
export const formulaSnapshot = readJson("../data/formulaFunctionSnapshot.json");
|
|
65
|
+
export const docsIndex = readJson("../data/docsIndex.json");
|
|
66
|
+
const propEntries = gridSnapshot.featureGroups.flatMap((group) => group.props.map((prop) => ({
|
|
67
|
+
group,
|
|
68
|
+
prop,
|
|
69
|
+
})));
|
|
70
|
+
export const propsByPath = new Map(propEntries.map((entry) => [entry.prop.path, entry]));
|
|
71
|
+
export const groupsByKey = new Map(gridSnapshot.featureGroups.map((group) => [group.key, group]));
|
|
72
|
+
export const docsBySlug = new Map(docsIndex.entries.map((entry) => [entry.slug, entry]));
|
|
73
|
+
export const docsByPath = new Map(docsIndex.entries.map((entry) => [entry.path, entry]));
|
|
74
|
+
export const examplesByFramework = new Map(docsIndex.examples.map((example) => [example.framework, example]));
|
|
75
|
+
function normalizeSearchText(value) {
|
|
76
|
+
return value.toLowerCase().replace(/[^a-z0-9.]+/g, " ").trim();
|
|
77
|
+
}
|
|
78
|
+
function scoreText(haystack, terms) {
|
|
79
|
+
return terms.reduce((score, term) => {
|
|
80
|
+
if (haystack.includes(term)) {
|
|
81
|
+
return score + (haystack.startsWith(term) ? 3 : 1);
|
|
82
|
+
}
|
|
83
|
+
return score;
|
|
84
|
+
}, 0);
|
|
85
|
+
}
|
|
86
|
+
export function searchCatalog(query, limit = 10) {
|
|
87
|
+
const terms = normalizeSearchText(query).split(/\s+/).filter(Boolean);
|
|
88
|
+
if (terms.length === 0)
|
|
89
|
+
return [];
|
|
90
|
+
const groupResults = gridSnapshot.featureGroups.map((group) => {
|
|
91
|
+
const text = normalizeSearchText(`${group.key} ${group.label} ${group.description} ${group.typeName}`);
|
|
92
|
+
return {
|
|
93
|
+
description: group.description,
|
|
94
|
+
featureGroup: group.key,
|
|
95
|
+
label: group.label,
|
|
96
|
+
score: scoreText(text, terms) + 1,
|
|
97
|
+
};
|
|
98
|
+
});
|
|
99
|
+
const propResults = propEntries.map(({ group, prop }) => {
|
|
100
|
+
const text = normalizeSearchText(`${prop.path} ${prop.name} ${prop.type} ${prop.description ?? ""} ${group.label}`);
|
|
101
|
+
return {
|
|
102
|
+
description: prop.description ?? "",
|
|
103
|
+
featureGroup: group.key,
|
|
104
|
+
label: prop.path,
|
|
105
|
+
path: prop.path,
|
|
106
|
+
score: scoreText(text, terms) + (prop.path.toLowerCase() === query.toLowerCase() ? 8 : 0),
|
|
107
|
+
type: prop.type,
|
|
108
|
+
};
|
|
109
|
+
});
|
|
110
|
+
return [...groupResults, ...propResults]
|
|
111
|
+
.filter((result) => result.score > 0)
|
|
112
|
+
.sort((a, b) => b.score - a.score || a.label.localeCompare(b.label))
|
|
113
|
+
.slice(0, Math.max(1, Math.min(limit, 50)));
|
|
114
|
+
}
|
|
115
|
+
export function searchDocs(query, limit = 10) {
|
|
116
|
+
const terms = normalizeSearchText(query).split(/\s+/).filter(Boolean);
|
|
117
|
+
if (terms.length === 0)
|
|
118
|
+
return [];
|
|
119
|
+
return docsIndex.entries
|
|
120
|
+
.map((entry) => {
|
|
121
|
+
const text = normalizeSearchText(`${entry.title} ${entry.summary} ${entry.category} ${entry.content}`);
|
|
122
|
+
const exactBoost = entry.slug.toLowerCase() === query.toLowerCase() ||
|
|
123
|
+
entry.path.toLowerCase() === query.toLowerCase()
|
|
124
|
+
? 20
|
|
125
|
+
: 0;
|
|
126
|
+
const propBoost = entry.relevantProps.some((prop) => prop.path.toLowerCase().includes(query.toLowerCase()))
|
|
127
|
+
? 8
|
|
128
|
+
: 0;
|
|
129
|
+
return {
|
|
130
|
+
category: entry.category,
|
|
131
|
+
key: entry.key,
|
|
132
|
+
path: entry.path,
|
|
133
|
+
relevantProps: entry.relevantProps,
|
|
134
|
+
score: scoreText(text, terms) + exactBoost + propBoost,
|
|
135
|
+
slug: entry.slug,
|
|
136
|
+
summary: entry.summary,
|
|
137
|
+
title: entry.title,
|
|
138
|
+
type: entry.type,
|
|
139
|
+
};
|
|
140
|
+
})
|
|
141
|
+
.filter((result) => result.score > 0)
|
|
142
|
+
.sort((a, b) => b.score - a.score || a.title.localeCompare(b.title))
|
|
143
|
+
.slice(0, Math.max(1, Math.min(limit, 50)));
|
|
144
|
+
}
|
|
145
|
+
export function searchEverything(query, limit = 10) {
|
|
146
|
+
const perSourceLimit = Math.max(1, Math.min(limit, 50));
|
|
147
|
+
const apiResults = searchCatalog(query, perSourceLimit).map((result) => ({
|
|
148
|
+
...result,
|
|
149
|
+
source: "api",
|
|
150
|
+
}));
|
|
151
|
+
const docsResults = searchDocs(query, perSourceLimit).map((result) => ({
|
|
152
|
+
...result,
|
|
153
|
+
source: "docs",
|
|
154
|
+
}));
|
|
155
|
+
return [...docsResults, ...apiResults]
|
|
156
|
+
.sort((a, b) => b.score - a.score || resultLabel(a).localeCompare(resultLabel(b)))
|
|
157
|
+
.slice(0, perSourceLimit);
|
|
158
|
+
}
|
|
159
|
+
function resultLabel(result) {
|
|
160
|
+
return "label" in result ? result.label : result.title;
|
|
161
|
+
}
|
|
162
|
+
export function listDocsPages() {
|
|
163
|
+
return docsIndex.pages;
|
|
164
|
+
}
|
|
165
|
+
export function getDocsPage(slugOrPath) {
|
|
166
|
+
const normalizedPath = slugOrPath.startsWith("/") ? slugOrPath : `/docs/${slugOrPath}`;
|
|
167
|
+
return docsBySlug.get(slugOrPath) ?? docsByPath.get(normalizedPath);
|
|
168
|
+
}
|
|
169
|
+
export function listFrameworkExamples() {
|
|
170
|
+
return docsIndex.examples.map((example) => ({
|
|
171
|
+
actionsTitle: example.actionsTitle,
|
|
172
|
+
framework: example.framework,
|
|
173
|
+
hostPackage: example.hostPackage,
|
|
174
|
+
label: example.label,
|
|
175
|
+
summary: example.summary,
|
|
176
|
+
}));
|
|
177
|
+
}
|
|
178
|
+
export function getFrameworkExample(framework) {
|
|
179
|
+
return examplesByFramework.get(framework);
|
|
180
|
+
}
|
|
181
|
+
export function listFeatureGroups() {
|
|
182
|
+
return gridSnapshot.featureGroups.map((group) => ({
|
|
183
|
+
description: group.description,
|
|
184
|
+
key: group.key,
|
|
185
|
+
label: group.label,
|
|
186
|
+
optional: group.optional,
|
|
187
|
+
propCount: group.propCount,
|
|
188
|
+
typeName: group.typeName,
|
|
189
|
+
}));
|
|
190
|
+
}
|
|
191
|
+
export function getProp(path) {
|
|
192
|
+
const entry = propsByPath.get(path);
|
|
193
|
+
if (!entry) {
|
|
194
|
+
return undefined;
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
description: entry.prop.description ?? "",
|
|
198
|
+
featureGroup: entry.group.key,
|
|
199
|
+
optional: entry.prop.optional,
|
|
200
|
+
path: entry.prop.path,
|
|
201
|
+
type: entry.prop.type,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function collectObjectPaths(value, prefix = "") {
|
|
205
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
206
|
+
return prefix ? [prefix] : [];
|
|
207
|
+
}
|
|
208
|
+
const paths = [];
|
|
209
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
210
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
211
|
+
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
212
|
+
paths.push(path);
|
|
213
|
+
paths.push(...collectObjectPaths(nested, path));
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
paths.push(path);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return paths;
|
|
220
|
+
}
|
|
221
|
+
function valueAtPath(value, path) {
|
|
222
|
+
return path.split(".").reduce((current, part) => {
|
|
223
|
+
if (!current || typeof current !== "object" || Array.isArray(current)) {
|
|
224
|
+
return undefined;
|
|
225
|
+
}
|
|
226
|
+
return current[part];
|
|
227
|
+
}, value);
|
|
228
|
+
}
|
|
229
|
+
export function validateGridConfig(config) {
|
|
230
|
+
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
231
|
+
return {
|
|
232
|
+
ok: false,
|
|
233
|
+
errors: ["Config must be a JSON object."],
|
|
234
|
+
missingRequiredProps: [],
|
|
235
|
+
unknownProps: [],
|
|
236
|
+
warnings: [],
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
const knownGroups = new Set(gridSnapshot.featureGroups.map((group) => group.key));
|
|
240
|
+
const knownProps = new Set(propEntries.map((entry) => entry.prop.path));
|
|
241
|
+
const allPaths = collectObjectPaths(config);
|
|
242
|
+
const unknownProps = allPaths.filter((path) => {
|
|
243
|
+
if (!path.includes("."))
|
|
244
|
+
return !knownGroups.has(path);
|
|
245
|
+
return !knownProps.has(path);
|
|
246
|
+
});
|
|
247
|
+
const minimalRequiredProps = ["data.rows", "data.columns"];
|
|
248
|
+
const missingRequiredProps = minimalRequiredProps.filter((path) => valueAtPath(config, path) === undefined);
|
|
249
|
+
const warnings = gridSnapshot.featureGroups
|
|
250
|
+
.filter((group) => group.key !== "license" && group.key !== "data" && valueAtPath(config, group.key))
|
|
251
|
+
.filter((group) => group.optional && group.props.length === 0)
|
|
252
|
+
.map((group) => `${group.key} is present but has no generated prop metadata.`);
|
|
253
|
+
return {
|
|
254
|
+
ok: unknownProps.length === 0 && missingRequiredProps.length === 0,
|
|
255
|
+
errors: unknownProps.map((path) => `Unknown Ace Grid prop: ${path}`),
|
|
256
|
+
missingRequiredProps,
|
|
257
|
+
unknownProps,
|
|
258
|
+
warnings,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
function normalizeFramework(value) {
|
|
262
|
+
const framework = value ?? "react";
|
|
263
|
+
if (framework === "angular" ||
|
|
264
|
+
framework === "vue" ||
|
|
265
|
+
framework === "svelte" ||
|
|
266
|
+
framework === "web-components") {
|
|
267
|
+
return framework;
|
|
268
|
+
}
|
|
269
|
+
return "react";
|
|
270
|
+
}
|
|
271
|
+
function tierForFeature(key) {
|
|
272
|
+
if (ENTERPRISE_FEATURE_KEYS.has(key))
|
|
273
|
+
return "Enterprise";
|
|
274
|
+
if (PRO_FEATURE_KEYS.has(key))
|
|
275
|
+
return "Pro";
|
|
276
|
+
return "Community";
|
|
277
|
+
}
|
|
278
|
+
function maxTier(a, b) {
|
|
279
|
+
return TIER_RANK[a] >= TIER_RANK[b] ? a : b;
|
|
280
|
+
}
|
|
281
|
+
function installCommand(framework, tier) {
|
|
282
|
+
return `npm install ${FRAMEWORK_PACKAGES[framework][tier].join(" ")}`;
|
|
283
|
+
}
|
|
284
|
+
function uniqueBy(values, getKey) {
|
|
285
|
+
const seen = new Set();
|
|
286
|
+
return values.filter((value) => {
|
|
287
|
+
const key = getKey(value);
|
|
288
|
+
if (seen.has(key))
|
|
289
|
+
return false;
|
|
290
|
+
seen.add(key);
|
|
291
|
+
return true;
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
function queryMatchesFeature(query, key) {
|
|
295
|
+
const normalized = normalizeSearchText(query);
|
|
296
|
+
const group = groupsByKey.get(key);
|
|
297
|
+
const aliases = FEATURE_ALIASES[key] ?? [];
|
|
298
|
+
const candidates = [key, group?.label ?? "", group?.description ?? "", ...aliases];
|
|
299
|
+
return candidates.some((candidate) => {
|
|
300
|
+
const term = normalizeSearchText(candidate);
|
|
301
|
+
return term.length > 0 && normalized.includes(term);
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
function matchedFeatureKeys(query, docsMatches) {
|
|
305
|
+
const directKeys = gridSnapshot.featureGroups
|
|
306
|
+
.filter((group) => queryMatchesFeature(query, group.key))
|
|
307
|
+
.map((group) => group.key);
|
|
308
|
+
const docKeys = docsMatches.map((entry) => entry.key).filter((key) => groupsByKey.has(key));
|
|
309
|
+
const apiKeys = searchCatalog(query, 12).map((result) => result.featureGroup);
|
|
310
|
+
return uniqueBy([...directKeys, ...docKeys, ...apiKeys], (key) => key).slice(0, 6);
|
|
311
|
+
}
|
|
312
|
+
function relevantPropsForFeatures(keys, docsMatches) {
|
|
313
|
+
return uniqueBy([
|
|
314
|
+
...docsMatches.flatMap((entry) => entry.relevantProps),
|
|
315
|
+
...keys.flatMap((key) => groupsByKey.get(key)?.props.slice(0, 8) ?? []),
|
|
316
|
+
].map((prop) => ({
|
|
317
|
+
description: prop.description,
|
|
318
|
+
path: prop.path,
|
|
319
|
+
type: prop.type,
|
|
320
|
+
})), (prop) => prop.path).slice(0, 24);
|
|
321
|
+
}
|
|
322
|
+
function setNestedValue(target, path, value) {
|
|
323
|
+
const parts = path.split(".");
|
|
324
|
+
let current = target;
|
|
325
|
+
for (const part of parts.slice(0, -1)) {
|
|
326
|
+
const existing = current[part];
|
|
327
|
+
if (!existing || typeof existing !== "object" || Array.isArray(existing)) {
|
|
328
|
+
current[part] = {};
|
|
329
|
+
}
|
|
330
|
+
current = current[part];
|
|
331
|
+
}
|
|
332
|
+
current[parts[parts.length - 1]] = value;
|
|
333
|
+
}
|
|
334
|
+
function serializableGridConfig(input, plan) {
|
|
335
|
+
const config = {
|
|
336
|
+
data: {
|
|
337
|
+
rows: [
|
|
338
|
+
{ id: "row-1", account: "Acme", status: "Active", revenue: 1200 },
|
|
339
|
+
{ id: "row-2", account: "Globex", status: "Trial", revenue: 860 },
|
|
340
|
+
{ id: "row-3", account: "Initech", status: "Active", revenue: 2140 },
|
|
341
|
+
],
|
|
342
|
+
columns: [
|
|
343
|
+
{ key: "account", header: "Account" },
|
|
344
|
+
{ key: "status", header: "Status" },
|
|
345
|
+
{ key: "revenue", header: "Revenue", type: "number" },
|
|
346
|
+
],
|
|
347
|
+
},
|
|
348
|
+
layout: {
|
|
349
|
+
height: 520,
|
|
350
|
+
},
|
|
351
|
+
};
|
|
352
|
+
if (plan.requiredTier !== "Community") {
|
|
353
|
+
config.license = {
|
|
354
|
+
appId: input.appId ?? "app_...",
|
|
355
|
+
apiBaseUrl: "https://api.ace-grid.com",
|
|
356
|
+
licenseKey: input.licenseKey ?? "ag_key_...",
|
|
357
|
+
...(input.domain ? { domain: input.domain } : {}),
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
for (const feature of plan.matchedFeatures) {
|
|
361
|
+
const enabledPath = `${feature.key}.enabled`;
|
|
362
|
+
if (propsByPath.has(enabledPath)) {
|
|
363
|
+
setNestedValue(config, enabledPath, true);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
if (propsByPath.has("validation.mode") && "validation" in config) {
|
|
367
|
+
setNestedValue(config, "validation.mode", "blocking");
|
|
368
|
+
}
|
|
369
|
+
if (propsByPath.has("validation.validateOn") && "validation" in config) {
|
|
370
|
+
setNestedValue(config, "validation.validateOn", "change");
|
|
371
|
+
}
|
|
372
|
+
if (propsByPath.has("charts.defaultChartType") && "charts" in config) {
|
|
373
|
+
setNestedValue(config, "charts.defaultChartType", "bar");
|
|
374
|
+
}
|
|
375
|
+
if (propsByPath.has("serverRowModel.blockSize") && "serverRowModel" in config) {
|
|
376
|
+
setNestedValue(config, "serverRowModel.blockSize", 200);
|
|
377
|
+
}
|
|
378
|
+
if (propsByPath.has("pivot.pivotMode") && "pivot" in config) {
|
|
379
|
+
setNestedValue(config, "pivot.pivotMode", true);
|
|
380
|
+
}
|
|
381
|
+
return config;
|
|
382
|
+
}
|
|
383
|
+
function serializeCode(value, indent = 0) {
|
|
384
|
+
const pad = " ".repeat(indent);
|
|
385
|
+
const nextPad = " ".repeat(indent + 2);
|
|
386
|
+
if (typeof value === "string")
|
|
387
|
+
return JSON.stringify(value);
|
|
388
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
389
|
+
return String(value);
|
|
390
|
+
if (value === null || value === undefined)
|
|
391
|
+
return "undefined";
|
|
392
|
+
if (Array.isArray(value)) {
|
|
393
|
+
if (value.length === 0)
|
|
394
|
+
return "[]";
|
|
395
|
+
return `[\n${value.map((item) => `${nextPad}${serializeCode(item, indent + 2)}`).join(",\n")}\n${pad}]`;
|
|
396
|
+
}
|
|
397
|
+
if (typeof value === "object") {
|
|
398
|
+
const entries = Object.entries(value);
|
|
399
|
+
if (entries.length === 0)
|
|
400
|
+
return "{}";
|
|
401
|
+
return `{\n${entries
|
|
402
|
+
.map(([key, nested]) => `${nextPad}${/^[a-zA-Z_$][\w$]*$/.test(key) ? key : JSON.stringify(key)}: ${serializeCode(nested, indent + 2)}`)
|
|
403
|
+
.join(",\n")}\n${pad}}`;
|
|
404
|
+
}
|
|
405
|
+
return "undefined";
|
|
406
|
+
}
|
|
407
|
+
function frameworkImport(framework, tier) {
|
|
408
|
+
const packageName = tier === "Enterprise" ? "@ace-grid/enterprise" : tier === "Pro" ? "@ace-grid/pro" : "@ace-grid/core";
|
|
409
|
+
const tierPath = tier === "Enterprise" ? "enterprise" : tier === "Pro" ? "pro" : "core";
|
|
410
|
+
if (framework === "angular")
|
|
411
|
+
return `import { AceGridComponent } from "@ace-grid/angular/${tierPath}";`;
|
|
412
|
+
if (framework === "vue")
|
|
413
|
+
return `import AceGrid from "@ace-grid/vue/${tierPath}";`;
|
|
414
|
+
if (framework === "svelte")
|
|
415
|
+
return `import AceGrid from "@ace-grid/svelte/${tierPath}";`;
|
|
416
|
+
if (framework === "web-components") {
|
|
417
|
+
return `import { defineAceGridElement } from "@ace-grid/wc";
|
|
418
|
+
import { Grid } from "${packageName}";
|
|
419
|
+
|
|
420
|
+
defineAceGridElement("ace-grid", { Grid });`;
|
|
421
|
+
}
|
|
422
|
+
return `import { Grid } from "${packageName}";`;
|
|
423
|
+
}
|
|
424
|
+
function implementationNotes(plan) {
|
|
425
|
+
const matched = plan.matchedFeatures
|
|
426
|
+
.map((feature) => `${feature.label}${feature.docsPath ? ` (${feature.docsPath})` : ""}`)
|
|
427
|
+
.join(", ");
|
|
428
|
+
return matched ? `// Matched Ace Grid features: ${matched}\n` : "";
|
|
429
|
+
}
|
|
430
|
+
function generateCodeForFramework(input, plan) {
|
|
431
|
+
const gridConfig = serializableGridConfig(input, plan);
|
|
432
|
+
const configCode = serializeCode(gridConfig);
|
|
433
|
+
const notes = implementationNotes(plan);
|
|
434
|
+
if (plan.framework === "angular") {
|
|
435
|
+
return `${frameworkImport(plan.framework, plan.requiredTier)}\nimport { Component } from "@angular/core";\n\n${notes}@Component({\n selector: "app-ace-grid",\n standalone: true,\n imports: [AceGridComponent],\n template: '<ace-grid-angular [props]="gridProps"></ace-grid-angular>',\n})\nexport class AceGridExampleComponent {\n gridProps = ${configCode};\n}\n`;
|
|
436
|
+
}
|
|
437
|
+
if (plan.framework === "vue") {
|
|
438
|
+
return `<script setup lang="ts">\n${frameworkImport(plan.framework, plan.requiredTier)}\n\n${notes}const gridProps = ${configCode};\n</script>\n\n<template>\n <AceGrid :props="gridProps" />\n</template>\n`;
|
|
439
|
+
}
|
|
440
|
+
if (plan.framework === "svelte") {
|
|
441
|
+
return `<script lang="ts">\n ${frameworkImport(plan.framework, plan.requiredTier)}\n\n${notes
|
|
442
|
+
.split("\n")
|
|
443
|
+
.filter(Boolean)
|
|
444
|
+
.map((line) => ` ${line}`)
|
|
445
|
+
.join("\n")}\n const gridProps = ${configCode.replace(/\n/g, "\n ")};\n</script>\n\n<AceGrid props={gridProps} />\n`;
|
|
446
|
+
}
|
|
447
|
+
if (plan.framework === "web-components") {
|
|
448
|
+
return `${frameworkImport(plan.framework, plan.requiredTier)}\n\n${notes}const grid = document.querySelector("ace-grid");\nif (!grid) throw new Error("Missing <ace-grid> element.");\n\ngrid.props = ${configCode};\n\n// HTML:\n// <ace-grid></ace-grid>\n`;
|
|
449
|
+
}
|
|
450
|
+
return `${frameworkImport(plan.framework, plan.requiredTier)}\n\n${notes}const gridProps = ${configCode};\n\nexport function AceGridExample() {\n return <Grid {...gridProps} />;\n}\n`;
|
|
451
|
+
}
|
|
452
|
+
export function planImplementation(input) {
|
|
453
|
+
const framework = normalizeFramework(input.framework);
|
|
454
|
+
const docs = searchDocs(input.query, 8);
|
|
455
|
+
const keys = matchedFeatureKeys(input.query, docs);
|
|
456
|
+
const matchedFeatures = keys.map((key) => {
|
|
457
|
+
const group = groupsByKey.get(key);
|
|
458
|
+
const doc = docs.find((entry) => entry.key === key) ?? docsBySlug.get(key);
|
|
459
|
+
return {
|
|
460
|
+
docsPath: doc?.path,
|
|
461
|
+
key,
|
|
462
|
+
label: group?.label ?? doc?.title ?? key,
|
|
463
|
+
tier: tierForFeature(key),
|
|
464
|
+
};
|
|
465
|
+
});
|
|
466
|
+
const requiredTier = matchedFeatures.reduce((tier, feature) => maxTier(tier, feature.tier), input.tier ?? "Community");
|
|
467
|
+
const warnings = input.tier && TIER_RANK[input.tier] < TIER_RANK[requiredTier]
|
|
468
|
+
? [`Requested ${input.tier}, but matched features require ${requiredTier}.`]
|
|
469
|
+
: [];
|
|
470
|
+
const notes = [
|
|
471
|
+
"Use this as a starting implementation and keep business-specific row loading, editing, and event handlers in app code.",
|
|
472
|
+
requiredTier === "Community"
|
|
473
|
+
? "No license config is needed for Community usage."
|
|
474
|
+
: "Paid tiers need licenseKey and appId. The browser domain is auto-detected when license.domain is omitted.",
|
|
475
|
+
];
|
|
476
|
+
return {
|
|
477
|
+
docs,
|
|
478
|
+
framework,
|
|
479
|
+
installCommand: installCommand(framework, requiredTier),
|
|
480
|
+
matchedFeatures,
|
|
481
|
+
notes,
|
|
482
|
+
requestedTier: input.tier,
|
|
483
|
+
requiredTier,
|
|
484
|
+
relevantProps: relevantPropsForFeatures(keys, docs),
|
|
485
|
+
warnings,
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
export function generateImplementation(input) {
|
|
489
|
+
const plan = planImplementation(input);
|
|
490
|
+
const config = serializableGridConfig(input, plan);
|
|
491
|
+
return {
|
|
492
|
+
code: generateCodeForFramework(input, plan),
|
|
493
|
+
config,
|
|
494
|
+
plan,
|
|
495
|
+
validation: validateGridConfig(config),
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
export function generateReactExample(options) {
|
|
499
|
+
const plan = options.plan ?? "Community";
|
|
500
|
+
const packageName = plan === "Enterprise" ? "@ace-grid/enterprise" : plan === "Pro" ? "@ace-grid/pro" : "ace-grid";
|
|
501
|
+
const licenseBlock = plan === "Community"
|
|
502
|
+
? ""
|
|
503
|
+
: `\n license: {\n licenseKey: "${options.licenseKey ?? "ag_key_..."}",\n appId: "${options.appId ?? "app_..."}",\n apiBaseUrl: "https://api.ace-grid.com",${options.domain ? `\n domain: "${options.domain}",` : ""}\n },`;
|
|
504
|
+
return `import { Grid } from "${packageName}";\n\nconst rows = [\n { id: "r1", product: "Starter", revenue: 1200 },\n { id: "r2", product: "Pro", revenue: 4200 },\n];\n\nconst columns = [\n { key: "product", header: "Product" },\n { key: "revenue", header: "Revenue", type: "number" },\n];\n\nexport function RevenueGrid() {\n return (\n <Grid\n data={{ rows, columns }}\n layout={{ height: 520 }}${licenseBlock}\n />\n );\n}\n`;
|
|
505
|
+
}
|
|
506
|
+
export function licenseSetupGuide() {
|
|
507
|
+
return {
|
|
508
|
+
defaultApiBaseUrl: "https://api.ace-grid.com",
|
|
509
|
+
notes: [
|
|
510
|
+
"Users configure licenseKey and appId from the Ace Grid portal.",
|
|
511
|
+
"The runtime automatically detects the browser hostname when license.domain is omitted.",
|
|
512
|
+
"The runtime can auto-fetch the public lease signing key from /v1/license/public-key.",
|
|
513
|
+
"Set license.leaseSigningPublicKey only when you need pinned/offline public-key verification.",
|
|
514
|
+
"Signed license leases are cached locally and verified before reuse.",
|
|
515
|
+
],
|
|
516
|
+
snippet: `const aceGridLicense = {\n licenseKey: "ag_key_...",\n appId: "app_...",\n apiBaseUrl: "https://api.ace-grid.com"\n};`,
|
|
517
|
+
};
|
|
518
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"demoServer.d.ts","sourceRoot":"","sources":["../src/demoServer.ts"],"names":[],"mappings":""}
|