@dropins/mcp 0.1.0
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.md +127 -0
- package/README.md +314 -0
- package/dist/common/project-reader.d.ts +55 -0
- package/dist/common/project-reader.js +173 -0
- package/dist/common/registry-loader.d.ts +101 -0
- package/dist/common/registry-loader.js +386 -0
- package/dist/common/response-handling.d.ts +12 -0
- package/dist/common/response-handling.js +21 -0
- package/dist/common/sanitize.d.ts +8 -0
- package/dist/common/sanitize.js +45 -0
- package/dist/common/synonyms.d.ts +9 -0
- package/dist/common/synonyms.js +127 -0
- package/dist/common/telemetry.d.ts +14 -0
- package/dist/common/telemetry.js +54 -0
- package/dist/common/types.d.ts +308 -0
- package/dist/common/types.js +1 -0
- package/dist/common/version.d.ts +2 -0
- package/dist/common/version.js +14 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +136 -0
- package/dist/operations/analyze-project.d.ts +13 -0
- package/dist/operations/analyze-project.js +125 -0
- package/dist/operations/check-block-health.d.ts +19 -0
- package/dist/operations/check-block-health.js +1149 -0
- package/dist/operations/check-config.d.ts +13 -0
- package/dist/operations/check-config.js +228 -0
- package/dist/operations/explain-event-flow.d.ts +16 -0
- package/dist/operations/explain-event-flow.js +218 -0
- package/dist/operations/get-upgrade-diff.d.ts +13 -0
- package/dist/operations/get-upgrade-diff.js +144 -0
- package/dist/operations/list-api-functions.d.ts +13 -0
- package/dist/operations/list-api-functions.js +53 -0
- package/dist/operations/list-containers.d.ts +13 -0
- package/dist/operations/list-containers.js +44 -0
- package/dist/operations/list-design-tokens.d.ts +13 -0
- package/dist/operations/list-design-tokens.js +47 -0
- package/dist/operations/list-events.d.ts +16 -0
- package/dist/operations/list-events.js +39 -0
- package/dist/operations/list-graphql-queries.d.ts +19 -0
- package/dist/operations/list-graphql-queries.js +84 -0
- package/dist/operations/list-i18n-keys.d.ts +19 -0
- package/dist/operations/list-i18n-keys.js +105 -0
- package/dist/operations/list-models.d.ts +16 -0
- package/dist/operations/list-models.js +80 -0
- package/dist/operations/list-slots.d.ts +16 -0
- package/dist/operations/list-slots.js +81 -0
- package/dist/operations/scaffold-block.d.ts +31 -0
- package/dist/operations/scaffold-block.js +331 -0
- package/dist/operations/scaffold-extension.d.ts +28 -0
- package/dist/operations/scaffold-extension.js +346 -0
- package/dist/operations/scaffold-slot.d.ts +22 -0
- package/dist/operations/scaffold-slot.js +189 -0
- package/dist/operations/search-commerce-docs.d.ts +16 -0
- package/dist/operations/search-commerce-docs.js +101 -0
- package/dist/operations/search-docs.d.ts +23 -0
- package/dist/operations/search-docs.js +298 -0
- package/dist/operations/suggest-event-handler.d.ts +16 -0
- package/dist/operations/suggest-event-handler.js +175 -0
- package/dist/operations/suggest-slot-implementation.d.ts +19 -0
- package/dist/operations/suggest-slot-implementation.js +183 -0
- package/dist/registry/api-functions.json +3045 -0
- package/dist/registry/block-patterns.json +78 -0
- package/dist/registry/containers.json +2003 -0
- package/dist/registry/design-tokens.json +577 -0
- package/dist/registry/docs/boilerplate.json +55 -0
- package/dist/registry/docs/dropins-all.json +97 -0
- package/dist/registry/docs/dropins-b2b.json +607 -0
- package/dist/registry/docs/dropins-cart.json +163 -0
- package/dist/registry/docs/dropins-checkout.json +193 -0
- package/dist/registry/docs/dropins-order.json +139 -0
- package/dist/registry/docs/dropins-payment-services.json +73 -0
- package/dist/registry/docs/dropins-personalization.json +67 -0
- package/dist/registry/docs/dropins-product-details.json +139 -0
- package/dist/registry/docs/dropins-product-discovery.json +85 -0
- package/dist/registry/docs/dropins-recommendations.json +67 -0
- package/dist/registry/docs/dropins-user-account.json +121 -0
- package/dist/registry/docs/dropins-user-auth.json +103 -0
- package/dist/registry/docs/dropins-wishlist.json +85 -0
- package/dist/registry/docs/get-started.json +85 -0
- package/dist/registry/docs/how-tos.json +19 -0
- package/dist/registry/docs/index.json +139 -0
- package/dist/registry/docs/licensing.json +19 -0
- package/dist/registry/docs/merchants.json +523 -0
- package/dist/registry/docs/resources.json +13 -0
- package/dist/registry/docs/sdk.json +139 -0
- package/dist/registry/docs/setup.json +145 -0
- package/dist/registry/docs/troubleshooting.json +19 -0
- package/dist/registry/events.json +2200 -0
- package/dist/registry/examples/index.json +19 -0
- package/dist/registry/examples/storefront-checkout.json +377 -0
- package/dist/registry/examples/storefront-quote-management.json +49 -0
- package/dist/registry/extensions.json +272 -0
- package/dist/registry/graphql.json +3469 -0
- package/dist/registry/i18n.json +1873 -0
- package/dist/registry/models.json +1001 -0
- package/dist/registry/sdk.json +2357 -0
- package/dist/registry/slots.json +2270 -0
- package/dist/registry/tools-components.json +595 -0
- package/dist/resources/guides.d.ts +7 -0
- package/dist/resources/guides.js +625 -0
- package/dist/resources/handlers.d.ts +31 -0
- package/dist/resources/handlers.js +322 -0
- package/package.json +47 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { readFileSync, readdirSync, existsSync, statSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { validateBlockName, validatePathContainment } from "./sanitize.js";
|
|
4
|
+
import { formatSuccessResponse } from "./response-handling.js";
|
|
5
|
+
export function readPackageJson(projectDir) {
|
|
6
|
+
const filePath = join(projectDir, "package.json");
|
|
7
|
+
if (!existsSync(filePath))
|
|
8
|
+
return null;
|
|
9
|
+
try {
|
|
10
|
+
return JSON.parse(readFileSync(filePath, "utf8"));
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export function readConfigJson(projectDir) {
|
|
17
|
+
for (const name of ["config.json", "demo-config.json"]) {
|
|
18
|
+
const filePath = join(projectDir, name);
|
|
19
|
+
if (existsSync(filePath)) {
|
|
20
|
+
try {
|
|
21
|
+
return JSON.parse(readFileSync(filePath, "utf8"));
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
export function listBlocks(projectDir) {
|
|
31
|
+
const blocksDir = join(projectDir, "blocks");
|
|
32
|
+
if (!existsSync(blocksDir))
|
|
33
|
+
return [];
|
|
34
|
+
try {
|
|
35
|
+
return readdirSync(blocksDir)
|
|
36
|
+
.filter((name) => {
|
|
37
|
+
const fullPath = join(blocksDir, name);
|
|
38
|
+
return statSync(fullPath).isDirectory();
|
|
39
|
+
})
|
|
40
|
+
.map((name) => {
|
|
41
|
+
const blockPath = join(blocksDir, name);
|
|
42
|
+
const jsFile = join(blockPath, `${name}.js`);
|
|
43
|
+
const cssFile = join(blockPath, `${name}.css`);
|
|
44
|
+
const hasJs = existsSync(jsFile);
|
|
45
|
+
const hasCss = existsSync(cssFile);
|
|
46
|
+
return {
|
|
47
|
+
name,
|
|
48
|
+
path: blockPath,
|
|
49
|
+
hasJs,
|
|
50
|
+
hasCss,
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export function readBlockFile(projectDir, blockName) {
|
|
59
|
+
const error = validateBlockName(blockName);
|
|
60
|
+
if (error)
|
|
61
|
+
return null;
|
|
62
|
+
const jsFile = join(projectDir, "blocks", blockName, `${blockName}.js`);
|
|
63
|
+
if (!validatePathContainment(projectDir, jsFile))
|
|
64
|
+
return null;
|
|
65
|
+
if (!existsSync(jsFile))
|
|
66
|
+
return null;
|
|
67
|
+
try {
|
|
68
|
+
return readFileSync(jsFile, "utf8");
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
export function listInitializers(projectDir) {
|
|
75
|
+
const initDir = join(projectDir, "scripts", "initializers");
|
|
76
|
+
if (!existsSync(initDir))
|
|
77
|
+
return [];
|
|
78
|
+
try {
|
|
79
|
+
return readdirSync(initDir)
|
|
80
|
+
.filter((name) => name.endsWith(".js") && name !== "index.js")
|
|
81
|
+
.map((name) => ({
|
|
82
|
+
name: name.replace(".js", ""),
|
|
83
|
+
path: join(initDir, name),
|
|
84
|
+
}));
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function projectExists(projectDir) {
|
|
91
|
+
return existsSync(projectDir) && statSync(projectDir).isDirectory();
|
|
92
|
+
}
|
|
93
|
+
export function projectDirGuard(projectDir) {
|
|
94
|
+
if (!projectExists(projectDir)) {
|
|
95
|
+
return formatSuccessResponse("Project directory not found", {
|
|
96
|
+
error: "Directory does not exist",
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
export function extractDropinDependencies(pkg) {
|
|
102
|
+
const results = [];
|
|
103
|
+
for (const [name, version] of Object.entries(pkg.dependencies ?? {})) {
|
|
104
|
+
if (name.startsWith("@dropins/")) {
|
|
105
|
+
results.push({ name, version, isDev: false });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
for (const [name, version] of Object.entries(pkg.devDependencies ?? {})) {
|
|
109
|
+
if (name.startsWith("@dropins/")) {
|
|
110
|
+
results.push({ name, version, isDev: true });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return results;
|
|
114
|
+
}
|
|
115
|
+
export function extractImportsFromBlock(jsContent) {
|
|
116
|
+
const dropinImports = [];
|
|
117
|
+
const containerImports = [];
|
|
118
|
+
const apiImports = [];
|
|
119
|
+
let eventImports = false;
|
|
120
|
+
const importRegex = /import\s+.*?from\s+['"]([^'"]+)['"]/g;
|
|
121
|
+
let match;
|
|
122
|
+
while ((match = importRegex.exec(jsContent)) !== null) {
|
|
123
|
+
const importPath = match[1];
|
|
124
|
+
if (importPath.includes("@dropins/")) {
|
|
125
|
+
dropinImports.push(importPath);
|
|
126
|
+
if (importPath.includes("/containers/")) {
|
|
127
|
+
const containerName = importPath
|
|
128
|
+
.split("/containers/")
|
|
129
|
+
.pop()
|
|
130
|
+
?.replace(".js", "");
|
|
131
|
+
if (containerName)
|
|
132
|
+
containerImports.push(containerName);
|
|
133
|
+
}
|
|
134
|
+
if (importPath.includes("/api")) {
|
|
135
|
+
apiImports.push(importPath);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (importPath.includes("event-bus")) {
|
|
139
|
+
eventImports = true;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return { dropinImports, containerImports, apiImports, eventImports };
|
|
143
|
+
}
|
|
144
|
+
export const INITIALIZER_ALIASES = {
|
|
145
|
+
"storefront-product-discovery": "search",
|
|
146
|
+
};
|
|
147
|
+
export function initializerCandidates(dropinName) {
|
|
148
|
+
const shortName = dropinName.replace("storefront-", "");
|
|
149
|
+
const alias = INITIALIZER_ALIASES[dropinName];
|
|
150
|
+
const candidates = [`${dropinName}.js`, `${shortName}.js`];
|
|
151
|
+
if (alias)
|
|
152
|
+
candidates.push(`${alias}.js`);
|
|
153
|
+
return candidates;
|
|
154
|
+
}
|
|
155
|
+
export function readInitializerFile(projectDir, dropinName) {
|
|
156
|
+
const initDir = join(projectDir, "scripts", "initializers");
|
|
157
|
+
if (!existsSync(initDir))
|
|
158
|
+
return null;
|
|
159
|
+
for (const candidate of initializerCandidates(dropinName)) {
|
|
160
|
+
const filePath = join(initDir, candidate);
|
|
161
|
+
if (!validatePathContainment(projectDir, filePath))
|
|
162
|
+
continue;
|
|
163
|
+
if (existsSync(filePath)) {
|
|
164
|
+
try {
|
|
165
|
+
return readFileSync(filePath, "utf8");
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type { ApiFunctionDropinData, ApiFunctionRegistry, BlockPatternRegistry, ContainerDropinData, ContainerRegistry, DesignTokenCategory, DesignTokenRegistry, DocsIndex, DocsPage, DocsSection, EventCatalog, EventEntry, ExamplesDropinData, ExamplesIndex, ExtensionRegistry, GraphqlDropinData, GraphqlFragment, GraphqlOperation, GraphqlRegistry, I18nRegistry, SdkComponent, SdkFunction, SdkPackage, SdkRegistry, SlotContainerData, SlotDropinData, SlotRegistry } from "./types.js";
|
|
2
|
+
export declare const getSlotRegistry: () => SlotRegistry;
|
|
3
|
+
export declare const getEventCatalog: () => EventCatalog;
|
|
4
|
+
export declare const getContainerRegistry: () => ContainerRegistry;
|
|
5
|
+
export declare const getApiFunctionRegistry: () => ApiFunctionRegistry;
|
|
6
|
+
export declare const getExtensionRegistry: () => ExtensionRegistry;
|
|
7
|
+
export declare const getDesignTokenRegistry: () => DesignTokenRegistry;
|
|
8
|
+
export declare const getBlockPatternRegistry: () => BlockPatternRegistry;
|
|
9
|
+
export interface ModelEntry {
|
|
10
|
+
name: string;
|
|
11
|
+
kind: "interface" | "type";
|
|
12
|
+
definition: string;
|
|
13
|
+
}
|
|
14
|
+
export interface ModelsDropinData {
|
|
15
|
+
version: string;
|
|
16
|
+
models: ModelEntry[];
|
|
17
|
+
}
|
|
18
|
+
export interface ModelsRegistry {
|
|
19
|
+
version: string;
|
|
20
|
+
description: string;
|
|
21
|
+
dropins: Record<string, ModelsDropinData>;
|
|
22
|
+
}
|
|
23
|
+
export declare const getModelsRegistry: () => ModelsRegistry;
|
|
24
|
+
export declare function getModelsByDropin(dropinName: string): ModelEntry[];
|
|
25
|
+
export declare function findModel(modelName: string): Array<ModelEntry & {
|
|
26
|
+
dropin: string;
|
|
27
|
+
}>;
|
|
28
|
+
export declare function getSlotsByDropin(dropinName: string): SlotDropinData | null;
|
|
29
|
+
export declare function getSlotsByContainer(dropinName: string, containerName: string): SlotContainerData | null;
|
|
30
|
+
export declare function getEventsByDropin(dropinName: string, direction?: "emitted" | "consumed" | "all"): EventEntry[];
|
|
31
|
+
export declare function getContainersByDropin(dropinName: string): ContainerDropinData | null;
|
|
32
|
+
export declare function getApiFunctionsByDropin(dropinName: string): ApiFunctionDropinData | null;
|
|
33
|
+
export declare function getDesignTokensByCategory(category: string): DesignTokenCategory | null;
|
|
34
|
+
export declare function getAllDropinNames(): string[];
|
|
35
|
+
export declare const getI18nRegistry: () => I18nRegistry;
|
|
36
|
+
export declare const getGraphqlRegistry: () => GraphqlRegistry;
|
|
37
|
+
export declare function getGraphqlByDropin(dropinName: string): GraphqlDropinData | null;
|
|
38
|
+
interface ScoredField {
|
|
39
|
+
value: string;
|
|
40
|
+
wordWeight: number;
|
|
41
|
+
fullMatchWeight?: number;
|
|
42
|
+
}
|
|
43
|
+
export declare function tokenizeQuery(query: string, extraWords?: string[]): {
|
|
44
|
+
lowerQuery: string;
|
|
45
|
+
words: string[];
|
|
46
|
+
};
|
|
47
|
+
export declare function scoreItem(words: string[], lowerQuery: string, fields: ScoredField[]): number;
|
|
48
|
+
export declare function topResults<T extends {
|
|
49
|
+
score: number;
|
|
50
|
+
}>(results: T[], maxResults: number): T[];
|
|
51
|
+
export interface GraphqlOperationResult {
|
|
52
|
+
operation: Pick<GraphqlOperation, "name" | "type" | "exportName" | "file" | "variables" | "fragmentDependencies">;
|
|
53
|
+
dropin: string;
|
|
54
|
+
score: number;
|
|
55
|
+
}
|
|
56
|
+
export interface GraphqlFragmentResult {
|
|
57
|
+
fragment: Pick<GraphqlFragment, "name" | "exportName" | "file" | "onType" | "exported" | "fragmentDependencies">;
|
|
58
|
+
dropin: string;
|
|
59
|
+
score: number;
|
|
60
|
+
}
|
|
61
|
+
export type GraphqlSearchResult = GraphqlOperationResult | GraphqlFragmentResult;
|
|
62
|
+
export declare function searchGraphql(query: string, maxResults?: number, extraWords?: string[]): GraphqlSearchResult[];
|
|
63
|
+
export interface SdkSearchResult {
|
|
64
|
+
item: (SdkFunction | SdkComponent) & {
|
|
65
|
+
packageName: string;
|
|
66
|
+
importPath?: string;
|
|
67
|
+
};
|
|
68
|
+
packageName: string;
|
|
69
|
+
type: "sdk-function" | "sdk-component";
|
|
70
|
+
score: number;
|
|
71
|
+
}
|
|
72
|
+
export declare const getSdkRegistry: () => SdkRegistry;
|
|
73
|
+
export declare function getSdkPackage(packageName: string): SdkPackage | null;
|
|
74
|
+
export declare function searchSdk(query: string, maxResults?: number, extraWords?: string[]): SdkSearchResult[];
|
|
75
|
+
export interface DocsSearchResult {
|
|
76
|
+
page: Pick<DocsPage, "path" | "title" | "description">;
|
|
77
|
+
section: string;
|
|
78
|
+
sectionLabel: string;
|
|
79
|
+
score: number;
|
|
80
|
+
}
|
|
81
|
+
export declare const getDocsIndex: () => DocsIndex;
|
|
82
|
+
export declare function getDocsSection(sectionKey: string): DocsSection | null;
|
|
83
|
+
export interface ExamplesSearchResult {
|
|
84
|
+
example: {
|
|
85
|
+
id?: string;
|
|
86
|
+
name: string;
|
|
87
|
+
provider?: string;
|
|
88
|
+
description: string;
|
|
89
|
+
hooks?: string[];
|
|
90
|
+
fileNames?: string[];
|
|
91
|
+
sharedFiles?: string[];
|
|
92
|
+
};
|
|
93
|
+
dropin: string;
|
|
94
|
+
type: "extension" | "block" | "guide";
|
|
95
|
+
score: number;
|
|
96
|
+
}
|
|
97
|
+
export declare const getExamplesIndex: () => ExamplesIndex;
|
|
98
|
+
export declare function getExamplesByDropin(dropinName: string): ExamplesDropinData | null;
|
|
99
|
+
export declare function searchExamples(query: string, maxResults?: number, extraWords?: string[]): ExamplesSearchResult[];
|
|
100
|
+
export declare function searchDocs(query: string, maxResults?: number, extraWords?: string[]): DocsSearchResult[];
|
|
101
|
+
export {};
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
import { readFileSync } from "fs";
|
|
2
|
+
import { dirname, join } from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
const registryDir = join(__dirname, "..", "registry");
|
|
6
|
+
function loadJson(filename) {
|
|
7
|
+
const filePath = join(registryDir, filename);
|
|
8
|
+
const content = readFileSync(filePath, "utf8");
|
|
9
|
+
return JSON.parse(content);
|
|
10
|
+
}
|
|
11
|
+
function cachedLoader(filename) {
|
|
12
|
+
let cache = null;
|
|
13
|
+
return () => {
|
|
14
|
+
if (!cache)
|
|
15
|
+
cache = loadJson(filename);
|
|
16
|
+
return cache;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function cachedLoaderWithFallback(filename, fallback) {
|
|
20
|
+
let cache = null;
|
|
21
|
+
let loaded = false;
|
|
22
|
+
return () => {
|
|
23
|
+
if (!loaded) {
|
|
24
|
+
try {
|
|
25
|
+
cache = loadJson(filename);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
cache = fallback;
|
|
29
|
+
}
|
|
30
|
+
loaded = true;
|
|
31
|
+
}
|
|
32
|
+
return cache;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const docsSectionCache = new Map();
|
|
36
|
+
const examplesDropinCache = new Map();
|
|
37
|
+
export const getSlotRegistry = cachedLoader("slots.json");
|
|
38
|
+
export const getEventCatalog = cachedLoader("events.json");
|
|
39
|
+
export const getContainerRegistry = cachedLoader("containers.json");
|
|
40
|
+
export const getApiFunctionRegistry = cachedLoader("api-functions.json");
|
|
41
|
+
export const getExtensionRegistry = cachedLoader("extensions.json");
|
|
42
|
+
export const getDesignTokenRegistry = cachedLoader("design-tokens.json");
|
|
43
|
+
export const getBlockPatternRegistry = cachedLoader("block-patterns.json");
|
|
44
|
+
export const getModelsRegistry = cachedLoader("models.json");
|
|
45
|
+
export function getModelsByDropin(dropinName) {
|
|
46
|
+
const registry = getModelsRegistry();
|
|
47
|
+
return registry.dropins[dropinName]?.models ?? [];
|
|
48
|
+
}
|
|
49
|
+
export function findModel(modelName) {
|
|
50
|
+
const registry = getModelsRegistry();
|
|
51
|
+
const results = [];
|
|
52
|
+
for (const [dropin, data] of Object.entries(registry.dropins)) {
|
|
53
|
+
const found = data.models.find((m) => m.name === modelName);
|
|
54
|
+
if (found)
|
|
55
|
+
results.push({ ...found, dropin });
|
|
56
|
+
}
|
|
57
|
+
return results;
|
|
58
|
+
}
|
|
59
|
+
export function getSlotsByDropin(dropinName) {
|
|
60
|
+
const registry = getSlotRegistry();
|
|
61
|
+
return registry.dropins[dropinName] ?? null;
|
|
62
|
+
}
|
|
63
|
+
export function getSlotsByContainer(dropinName, containerName) {
|
|
64
|
+
const dropin = getSlotsByDropin(dropinName);
|
|
65
|
+
if (!dropin)
|
|
66
|
+
return null;
|
|
67
|
+
return dropin.containers[containerName] ?? null;
|
|
68
|
+
}
|
|
69
|
+
export function getEventsByDropin(dropinName, direction) {
|
|
70
|
+
const catalog = getEventCatalog();
|
|
71
|
+
return catalog.events.filter((event) => {
|
|
72
|
+
const matchesEmit = event.emittedBy?.some((e) => e.dropin === dropinName);
|
|
73
|
+
const matchesConsume = event.consumedBy?.some((c) => c.dropin === dropinName);
|
|
74
|
+
if (direction === "emitted")
|
|
75
|
+
return matchesEmit;
|
|
76
|
+
if (direction === "consumed")
|
|
77
|
+
return matchesConsume;
|
|
78
|
+
return matchesEmit || matchesConsume;
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
export function getContainersByDropin(dropinName) {
|
|
82
|
+
const registry = getContainerRegistry();
|
|
83
|
+
return registry.dropins[dropinName] ?? null;
|
|
84
|
+
}
|
|
85
|
+
export function getApiFunctionsByDropin(dropinName) {
|
|
86
|
+
const registry = getApiFunctionRegistry();
|
|
87
|
+
return registry.dropins[dropinName] ?? null;
|
|
88
|
+
}
|
|
89
|
+
export function getDesignTokensByCategory(category) {
|
|
90
|
+
const registry = getDesignTokenRegistry();
|
|
91
|
+
return registry.categories[category] ?? null;
|
|
92
|
+
}
|
|
93
|
+
export function getAllDropinNames() {
|
|
94
|
+
const registry = getContainerRegistry();
|
|
95
|
+
return Object.keys(registry.dropins);
|
|
96
|
+
}
|
|
97
|
+
export const getI18nRegistry = cachedLoaderWithFallback("i18n.json", { version: "", description: "", dropins: {} });
|
|
98
|
+
export const getGraphqlRegistry = cachedLoaderWithFallback("graphql.json", { version: "", description: "", dropins: {} });
|
|
99
|
+
export function getGraphqlByDropin(dropinName) {
|
|
100
|
+
const registry = getGraphqlRegistry();
|
|
101
|
+
return registry.dropins[dropinName] ?? null;
|
|
102
|
+
}
|
|
103
|
+
export function tokenizeQuery(query, extraWords) {
|
|
104
|
+
const lowerQuery = query.toLowerCase();
|
|
105
|
+
const baseWords = lowerQuery.split(/\s+/).filter((w) => w.length > 2);
|
|
106
|
+
const words = extraWords?.length
|
|
107
|
+
? [
|
|
108
|
+
...new Set([
|
|
109
|
+
...baseWords,
|
|
110
|
+
...extraWords.map((w) => w.toLowerCase()).filter((w) => w.length > 2),
|
|
111
|
+
]),
|
|
112
|
+
]
|
|
113
|
+
: baseWords;
|
|
114
|
+
return { lowerQuery, words };
|
|
115
|
+
}
|
|
116
|
+
export function scoreItem(words, lowerQuery, fields) {
|
|
117
|
+
let score = 0;
|
|
118
|
+
for (const { value, wordWeight, fullMatchWeight } of fields) {
|
|
119
|
+
const lower = (value ?? "").toLowerCase();
|
|
120
|
+
for (const word of words) {
|
|
121
|
+
if (lower.includes(word))
|
|
122
|
+
score += wordWeight;
|
|
123
|
+
}
|
|
124
|
+
if (fullMatchWeight && lower.includes(lowerQuery))
|
|
125
|
+
score += fullMatchWeight;
|
|
126
|
+
}
|
|
127
|
+
return score;
|
|
128
|
+
}
|
|
129
|
+
export function topResults(results, maxResults) {
|
|
130
|
+
return [...results].sort((a, b) => b.score - a.score).slice(0, maxResults);
|
|
131
|
+
}
|
|
132
|
+
export function searchGraphql(query, maxResults = 10, extraWords) {
|
|
133
|
+
const registry = getGraphqlRegistry();
|
|
134
|
+
const { lowerQuery, words } = tokenizeQuery(query, extraWords);
|
|
135
|
+
const results = [];
|
|
136
|
+
for (const [dropinName, dropinData] of Object.entries(registry.dropins ?? {})) {
|
|
137
|
+
for (const op of dropinData.operations ?? []) {
|
|
138
|
+
const score = scoreItem(words, lowerQuery, [
|
|
139
|
+
{ value: op.name ?? "", wordWeight: 15, fullMatchWeight: 25 },
|
|
140
|
+
{ value: op.exportName ?? "", wordWeight: 10 },
|
|
141
|
+
{ value: op.type ?? "", wordWeight: 5 },
|
|
142
|
+
{ value: op.source ?? "", wordWeight: 1 },
|
|
143
|
+
]);
|
|
144
|
+
if (score > 0) {
|
|
145
|
+
results.push({
|
|
146
|
+
operation: {
|
|
147
|
+
name: op.name,
|
|
148
|
+
type: op.type,
|
|
149
|
+
exportName: op.exportName,
|
|
150
|
+
file: op.file,
|
|
151
|
+
variables: op.variables,
|
|
152
|
+
fragmentDependencies: op.fragmentDependencies,
|
|
153
|
+
},
|
|
154
|
+
dropin: dropinName,
|
|
155
|
+
score,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
for (const frag of dropinData.fragments ?? []) {
|
|
160
|
+
const score = scoreItem(words, lowerQuery, [
|
|
161
|
+
{ value: frag.name ?? "", wordWeight: 15, fullMatchWeight: 25 },
|
|
162
|
+
{ value: frag.exportName ?? "", wordWeight: 10 },
|
|
163
|
+
{ value: frag.onType ?? "", wordWeight: 5 },
|
|
164
|
+
{
|
|
165
|
+
value: frag.exported ? "exported extensible override" : "",
|
|
166
|
+
wordWeight: 3,
|
|
167
|
+
},
|
|
168
|
+
{ value: frag.source ?? "", wordWeight: 1 },
|
|
169
|
+
]);
|
|
170
|
+
if (score > 0) {
|
|
171
|
+
results.push({
|
|
172
|
+
fragment: {
|
|
173
|
+
name: frag.name,
|
|
174
|
+
exportName: frag.exportName,
|
|
175
|
+
file: frag.file,
|
|
176
|
+
onType: frag.onType,
|
|
177
|
+
exported: frag.exported,
|
|
178
|
+
fragmentDependencies: frag.fragmentDependencies,
|
|
179
|
+
},
|
|
180
|
+
dropin: dropinName,
|
|
181
|
+
score,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return topResults(results, maxResults);
|
|
187
|
+
}
|
|
188
|
+
export const getSdkRegistry = cachedLoaderWithFallback("sdk.json", { description: "", packages: {} });
|
|
189
|
+
export function getSdkPackage(packageName) {
|
|
190
|
+
const registry = getSdkRegistry();
|
|
191
|
+
return registry.packages[packageName] ?? null;
|
|
192
|
+
}
|
|
193
|
+
export function searchSdk(query, maxResults = 10, extraWords) {
|
|
194
|
+
const registry = getSdkRegistry();
|
|
195
|
+
const { lowerQuery, words } = tokenizeQuery(query, extraWords);
|
|
196
|
+
const results = [];
|
|
197
|
+
for (const [pkgKey, pkg] of Object.entries(registry.packages ?? {})) {
|
|
198
|
+
const pkgFields = [
|
|
199
|
+
{ value: pkg.name ?? "", wordWeight: 3 },
|
|
200
|
+
{ value: pkg.description ?? "", wordWeight: 1 },
|
|
201
|
+
];
|
|
202
|
+
for (const fn of pkg.functions ?? pkg.utilities ?? []) {
|
|
203
|
+
const score = scoreItem(words, lowerQuery, [
|
|
204
|
+
{ value: fn.name ?? "", wordWeight: 15, fullMatchWeight: 25 },
|
|
205
|
+
{ value: fn.description ?? "", wordWeight: 5 },
|
|
206
|
+
{ value: fn.signature ?? "", wordWeight: 2 },
|
|
207
|
+
...pkgFields,
|
|
208
|
+
]);
|
|
209
|
+
if (score > 0) {
|
|
210
|
+
results.push({
|
|
211
|
+
item: { ...fn, packageName: pkg.name },
|
|
212
|
+
packageName: pkgKey,
|
|
213
|
+
type: "sdk-function",
|
|
214
|
+
score,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
for (const comp of pkg.components ?? []) {
|
|
219
|
+
const slotNames = (comp.slots ?? []).join(" ");
|
|
220
|
+
const propNames = (comp.props ?? []).map((p) => p.name).join(" ");
|
|
221
|
+
const score = scoreItem(words, lowerQuery, [
|
|
222
|
+
{ value: comp.name ?? "", wordWeight: 15, fullMatchWeight: 25 },
|
|
223
|
+
{ value: comp.description ?? "", wordWeight: 5 },
|
|
224
|
+
{ value: slotNames, wordWeight: 8 },
|
|
225
|
+
{ value: propNames, wordWeight: 4 },
|
|
226
|
+
...pkgFields,
|
|
227
|
+
]);
|
|
228
|
+
if (score > 0) {
|
|
229
|
+
results.push({
|
|
230
|
+
item: {
|
|
231
|
+
...comp,
|
|
232
|
+
packageName: pkg.name,
|
|
233
|
+
importPath: `${pkg.importPath}/components`,
|
|
234
|
+
},
|
|
235
|
+
packageName: pkgKey,
|
|
236
|
+
type: "sdk-component",
|
|
237
|
+
score,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return topResults(results, maxResults);
|
|
243
|
+
}
|
|
244
|
+
export const getDocsIndex = cachedLoaderWithFallback("docs/index.json", { totalPages: 0, totalSections: 0, priority: "", sections: [] });
|
|
245
|
+
export function getDocsSection(sectionKey) {
|
|
246
|
+
if (docsSectionCache.has(sectionKey)) {
|
|
247
|
+
return docsSectionCache.get(sectionKey);
|
|
248
|
+
}
|
|
249
|
+
const index = getDocsIndex();
|
|
250
|
+
const sectionEntry = index.sections?.find((s) => s.section === sectionKey);
|
|
251
|
+
if (!sectionEntry)
|
|
252
|
+
return null;
|
|
253
|
+
try {
|
|
254
|
+
const data = loadJson(`docs/${sectionEntry.file}`);
|
|
255
|
+
docsSectionCache.set(sectionKey, data);
|
|
256
|
+
return data;
|
|
257
|
+
}
|
|
258
|
+
catch {
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
export const getExamplesIndex = cachedLoaderWithFallback("examples/index.json", { version: "", totalExtensions: 0, totalBlocks: 0, dropins: [] });
|
|
263
|
+
export function getExamplesByDropin(dropinName) {
|
|
264
|
+
if (examplesDropinCache.has(dropinName)) {
|
|
265
|
+
return examplesDropinCache.get(dropinName);
|
|
266
|
+
}
|
|
267
|
+
const index = getExamplesIndex();
|
|
268
|
+
const entry = index.dropins?.find((d) => d.dropin === dropinName);
|
|
269
|
+
if (!entry)
|
|
270
|
+
return null;
|
|
271
|
+
try {
|
|
272
|
+
const data = loadJson(`examples/${entry.file}`);
|
|
273
|
+
examplesDropinCache.set(dropinName, data);
|
|
274
|
+
return data;
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
export function searchExamples(query, maxResults = 10, extraWords) {
|
|
281
|
+
const index = getExamplesIndex();
|
|
282
|
+
const { lowerQuery, words } = tokenizeQuery(query, extraWords);
|
|
283
|
+
const results = [];
|
|
284
|
+
for (const entry of index.dropins ?? []) {
|
|
285
|
+
const data = getExamplesByDropin(entry.dropin);
|
|
286
|
+
if (!data)
|
|
287
|
+
continue;
|
|
288
|
+
for (const ext of data.extensions ?? []) {
|
|
289
|
+
const score = scoreItem(words, lowerQuery, [
|
|
290
|
+
{ value: ext.name ?? "", wordWeight: 15, fullMatchWeight: 25 },
|
|
291
|
+
{ value: ext.id ?? "", wordWeight: 15, fullMatchWeight: 25 },
|
|
292
|
+
{ value: ext.provider ?? "", wordWeight: 15, fullMatchWeight: 25 },
|
|
293
|
+
{ value: ext.description ?? "", wordWeight: 5 },
|
|
294
|
+
{
|
|
295
|
+
value: Object.values(ext.files ?? {}).join("\n"),
|
|
296
|
+
wordWeight: 2,
|
|
297
|
+
},
|
|
298
|
+
]);
|
|
299
|
+
if (score > 0) {
|
|
300
|
+
results.push({
|
|
301
|
+
example: {
|
|
302
|
+
id: ext.id,
|
|
303
|
+
name: ext.name,
|
|
304
|
+
provider: ext.provider,
|
|
305
|
+
description: ext.description,
|
|
306
|
+
hooks: ext.hooks,
|
|
307
|
+
fileNames: Object.keys(ext.files ?? {}),
|
|
308
|
+
sharedFiles: ext.sharedFiles,
|
|
309
|
+
},
|
|
310
|
+
dropin: entry.dropin,
|
|
311
|
+
type: "extension",
|
|
312
|
+
score,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
for (const block of data.blocks ?? []) {
|
|
317
|
+
const score = scoreItem(words, lowerQuery, [
|
|
318
|
+
{ value: block.name ?? "", wordWeight: 15, fullMatchWeight: 25 },
|
|
319
|
+
{ value: block.description ?? "", wordWeight: 5 },
|
|
320
|
+
{
|
|
321
|
+
value: Object.values(block.files ?? {}).join("\n"),
|
|
322
|
+
wordWeight: 2,
|
|
323
|
+
},
|
|
324
|
+
]);
|
|
325
|
+
if (score > 0) {
|
|
326
|
+
results.push({
|
|
327
|
+
example: {
|
|
328
|
+
name: block.name,
|
|
329
|
+
description: block.description,
|
|
330
|
+
fileNames: Object.keys(block.files ?? {}),
|
|
331
|
+
},
|
|
332
|
+
dropin: entry.dropin,
|
|
333
|
+
type: "block",
|
|
334
|
+
score,
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
if (data.extensionGuide) {
|
|
339
|
+
const score = scoreItem(words, lowerQuery, [
|
|
340
|
+
{ value: data.extensionGuide, wordWeight: 1 },
|
|
341
|
+
]);
|
|
342
|
+
if (score > 0) {
|
|
343
|
+
results.push({
|
|
344
|
+
example: {
|
|
345
|
+
name: "Extension Guide",
|
|
346
|
+
description: `Extension system documentation for ${entry.dropin}`,
|
|
347
|
+
},
|
|
348
|
+
dropin: entry.dropin,
|
|
349
|
+
type: "guide",
|
|
350
|
+
score,
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return topResults(results, maxResults);
|
|
356
|
+
}
|
|
357
|
+
export function searchDocs(query, maxResults = 10, extraWords) {
|
|
358
|
+
const index = getDocsIndex();
|
|
359
|
+
const { lowerQuery, words } = tokenizeQuery(query, extraWords);
|
|
360
|
+
const results = [];
|
|
361
|
+
for (const sectionEntry of index.sections ?? []) {
|
|
362
|
+
const section = getDocsSection(sectionEntry.section);
|
|
363
|
+
if (!section)
|
|
364
|
+
continue;
|
|
365
|
+
for (const page of section.pages ?? []) {
|
|
366
|
+
const score = scoreItem(words, lowerQuery, [
|
|
367
|
+
{ value: page.title ?? "", wordWeight: 10, fullMatchWeight: 20 },
|
|
368
|
+
{ value: page.description ?? "", wordWeight: 5, fullMatchWeight: 10 },
|
|
369
|
+
{ value: page.content ?? "", wordWeight: 1 },
|
|
370
|
+
]);
|
|
371
|
+
if (score > 0) {
|
|
372
|
+
results.push({
|
|
373
|
+
page: {
|
|
374
|
+
path: page.path,
|
|
375
|
+
title: page.title,
|
|
376
|
+
description: page.description,
|
|
377
|
+
},
|
|
378
|
+
section: sectionEntry.section,
|
|
379
|
+
sectionLabel: sectionEntry.label,
|
|
380
|
+
score,
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return topResults(results, maxResults);
|
|
386
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare function formatSuccessResponse(message: string, data?: unknown): {
|
|
2
|
+
success: boolean;
|
|
3
|
+
message: string;
|
|
4
|
+
data: unknown;
|
|
5
|
+
};
|
|
6
|
+
declare function formatFailureResponse(message: string, data?: unknown): {
|
|
7
|
+
success: boolean;
|
|
8
|
+
message: string;
|
|
9
|
+
data: unknown;
|
|
10
|
+
};
|
|
11
|
+
export declare function formatExceptionResponse(error: unknown, operation: string): ReturnType<typeof formatFailureResponse>;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { sanitizeErrorMessage } from "./sanitize.js";
|
|
2
|
+
export function formatSuccessResponse(message, data) {
|
|
3
|
+
return {
|
|
4
|
+
success: true,
|
|
5
|
+
message,
|
|
6
|
+
data,
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
function formatFailureResponse(message, data) {
|
|
10
|
+
return {
|
|
11
|
+
success: false,
|
|
12
|
+
message,
|
|
13
|
+
data,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export function formatExceptionResponse(error, operation) {
|
|
17
|
+
const rawMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
18
|
+
process.stderr.write(`${operation} Error: ${rawMessage}\n`);
|
|
19
|
+
const safeMessage = sanitizeErrorMessage(rawMessage);
|
|
20
|
+
return formatFailureResponse(`Error ${operation}: ${safeMessage}`);
|
|
21
|
+
}
|