simple-dynamsoft-mcp 6.3.0 → 7.0.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.
Files changed (47) hide show
  1. package/.env.example +35 -9
  2. package/README.md +156 -497
  3. package/package.json +13 -7
  4. package/scripts/prebuild-rag-index.mjs +1 -1
  5. package/scripts/run-gemini-tests.mjs +1 -1
  6. package/scripts/sync-submodules.mjs +1 -1
  7. package/scripts/verify-doc-resources.mjs +79 -0
  8. package/src/data/bootstrap.js +475 -0
  9. package/src/data/download-utils.js +99 -0
  10. package/src/data/hydration-mode.js +15 -0
  11. package/src/data/hydration-policy.js +39 -0
  12. package/src/data/repo-map.js +149 -0
  13. package/src/{data-root.js → data/root.js} +1 -1
  14. package/src/{submodule-sync.js → data/submodule-sync.js} +1 -1
  15. package/src/index.js +49 -1499
  16. package/src/observability/logging.js +51 -0
  17. package/src/rag/config.js +96 -0
  18. package/src/rag/index.js +266 -0
  19. package/src/rag/lexical-provider.js +170 -0
  20. package/src/rag/logger.js +46 -0
  21. package/src/rag/profile-config.js +48 -0
  22. package/src/rag/providers.js +585 -0
  23. package/src/rag/search-utils.js +166 -0
  24. package/src/rag/vector-cache.js +323 -0
  25. package/src/server/create-server.js +168 -0
  26. package/src/server/helpers/server-helpers.js +33 -0
  27. package/src/{resource-index → server/resource-index}/paths.js +2 -2
  28. package/src/{resource-index → server/resource-index}/samples.js +9 -1
  29. package/src/{resource-index.js → server/resource-index.js} +158 -93
  30. package/src/server/resources/register-resources.js +56 -0
  31. package/src/server/runtime-config.js +66 -0
  32. package/src/server/tools/register-index-tools.js +130 -0
  33. package/src/server/tools/register-project-tools.js +305 -0
  34. package/src/server/tools/register-quickstart-tools.js +572 -0
  35. package/src/server/tools/register-sample-tools.js +333 -0
  36. package/src/server/tools/register-version-tools.js +136 -0
  37. package/src/server/transports/http.js +84 -0
  38. package/src/server/transports/stdio.js +12 -0
  39. package/src/data-bootstrap.js +0 -255
  40. package/src/rag.js +0 -1203
  41. /package/src/{gemini-retry.js → rag/gemini-retry.js} +0 -0
  42. /package/src/{normalizers.js → server/normalizers.js} +0 -0
  43. /package/src/{resource-index → server/resource-index}/builders.js +0 -0
  44. /package/src/{resource-index → server/resource-index}/config.js +0 -0
  45. /package/src/{resource-index → server/resource-index}/docs-loader.js +0 -0
  46. /package/src/{resource-index → server/resource-index}/uri.js +0 -0
  47. /package/src/{resource-index → server/resource-index}/version-policy.js +0 -0
@@ -0,0 +1,572 @@
1
+ import { existsSync, statSync } from "node:fs";
2
+ import { extname, join } from "node:path";
3
+ import { z } from "zod";
4
+
5
+ export function registerQuickstartTools({
6
+ server,
7
+ registry,
8
+ ensureScopeHydrated,
9
+ ensureLatestMajor,
10
+ normalizeProduct,
11
+ normalizePlatform,
12
+ normalizeEdition,
13
+ normalizeApiLevel,
14
+ discoverDcvMobileSamples,
15
+ discoverDcvWebSamples,
16
+ findCodeFilesInSample,
17
+ getMobileSamplePath,
18
+ getDbrServerSamplePath,
19
+ getDcvMobileSamplePath,
20
+ getDcvServerSamplePath,
21
+ getDcvWebSamplePath,
22
+ getDwtSamplePath,
23
+ getDdvSamplePath,
24
+ readCodeFile,
25
+ getMainCodeFile,
26
+ getWebSamplePath
27
+ }) {
28
+ server.registerTool(
29
+ "get_quickstart",
30
+ {
31
+ title: "Get Quickstart",
32
+ description: "Opinionated quickstart for a target product/edition/platform. DCV supports MRZ/VIN/document-normalization/driver-license workflows.",
33
+ inputSchema: {
34
+ product: z.string().describe("Product: dcv, dbr, dwt, or ddv"),
35
+ edition: z.string().optional().describe("Edition: core, mobile, web, server/desktop"),
36
+ platform: z.string().optional().describe("Platform: android, ios, maui, react-native, flutter, js, python, cpp, java, dotnet, nodejs, angular, blazor, capacitor, electron, es6, native-ts, next, nuxt, pwa, react, requirejs, svelte, vue, webview, spm, core"),
37
+ language: z.string().optional().describe("Language hint: kotlin, java, swift, js, ts, python, cpp, csharp, react, vue, angular"),
38
+ version: z.string().optional().describe("Version constraint"),
39
+ api_level: z.string().optional().describe("API level: high-level or low-level (mobile only)"),
40
+ scenario: z.string().optional().describe("Scenario: camera, image, single, multiple, MRZ, VIN, document scan/normalization, driver license, react, etc.")
41
+ }
42
+ },
43
+ async ({ product, edition, platform, language, version, api_level, scenario }) => {
44
+ const normalizedProduct = normalizeProduct(product);
45
+ const normalizedPlatform = normalizePlatform(platform);
46
+ const normalizedEdition = normalizeEdition(edition, normalizedPlatform, normalizedProduct);
47
+
48
+ await ensureScopeHydrated({
49
+ product: normalizedProduct,
50
+ edition: normalizedEdition,
51
+ platform: normalizedPlatform,
52
+ type: "any"
53
+ });
54
+
55
+ const policy = ensureLatestMajor({
56
+ product: normalizedProduct,
57
+ version,
58
+ query: scenario,
59
+ edition: normalizedEdition,
60
+ platform: normalizedPlatform
61
+ });
62
+
63
+ if (!policy.ok) {
64
+ return { isError: true, content: [{ type: "text", text: policy.message }] };
65
+ }
66
+
67
+ if (normalizedProduct === "dcv") {
68
+ const scenarioLower = `${scenario || ""} ${language || ""}`.toLowerCase();
69
+ const effectiveEdition = normalizedEdition || (normalizedPlatform ? normalizeEdition("", normalizedPlatform, "dcv") : "server");
70
+
71
+ function selectDcvServerSample(platformHint, hint) {
72
+ const platformName = normalizePlatform(platformHint) || "python";
73
+ if (platformName === "python") {
74
+ if (hint.includes("mrz")) return "mrz_scanner";
75
+ if (hint.includes("vin")) return "vin_scanner";
76
+ if (hint.includes("driver") || hint.includes("license")) return "driver_license_scanner";
77
+ if (hint.includes("gs1")) return "gs1_ai_scanner";
78
+ return "document_scanner";
79
+ }
80
+ if (platformName === "nodejs") {
81
+ if (hint.includes("lambda")) return "lambda";
82
+ if (hint.includes("pdf")) return "pdf-advanced";
83
+ if (hint.includes("koa")) return "koa";
84
+ return "express";
85
+ }
86
+ if (hint.includes("mrz")) return "MRZScanner";
87
+ if (hint.includes("vin")) return "VINScanner";
88
+ if (hint.includes("driver") || hint.includes("license")) return "DriverLicenseScanner";
89
+ if (hint.includes("gs1")) return "GS1AIScanner";
90
+ return "DocumentScanner";
91
+ }
92
+
93
+ function selectMobileSample(sampleNames, hint) {
94
+ const lowerToName = new Map(sampleNames.map((name) => [String(name).toLowerCase(), name]));
95
+ const candidates = hint.includes("mrz")
96
+ ? ["scanmrz", "mrzscanner"]
97
+ : hint.includes("vin")
98
+ ? ["scanvin", "vinscanner"]
99
+ : (hint.includes("driver") || hint.includes("license"))
100
+ ? ["driverlicensescanner"]
101
+ : ["scandocument", "documentscanner"];
102
+ for (const candidate of candidates) {
103
+ if (lowerToName.has(candidate)) return lowerToName.get(candidate);
104
+ }
105
+ return sampleNames[0] || "";
106
+ }
107
+
108
+ function readBestSampleContent(samplePath) {
109
+ if (!samplePath || !existsSync(samplePath)) return { text: "", fence: "text" };
110
+ const sampleStat = statSync(samplePath);
111
+ if (sampleStat.isFile()) {
112
+ return {
113
+ text: readCodeFile(samplePath),
114
+ fence: extname(samplePath).replace(".", "") || "text"
115
+ };
116
+ }
117
+ const readmePath = join(samplePath, "README.md");
118
+ if (existsSync(readmePath)) return { text: readCodeFile(readmePath), fence: "markdown" };
119
+
120
+ const codeFiles = findCodeFilesInSample(samplePath);
121
+ if (codeFiles.length > 0) {
122
+ const preferredNames = ["index.html", "index.js", "index.ts", "main.dart", "App.tsx", "MainActivity.kt", "MainActivity.java"];
123
+ const preferred = codeFiles.find((file) => preferredNames.includes(file.filename)) || codeFiles[0];
124
+ return {
125
+ text: readCodeFile(preferred.path),
126
+ fence: preferred.extension ? preferred.extension.replace(".", "") : "text"
127
+ };
128
+ }
129
+
130
+ return { text: "Sample found, but no code files detected.", fence: "text" };
131
+ }
132
+
133
+ function formatInstallLines(installation) {
134
+ if (!installation || typeof installation !== "object") return [];
135
+ const lines = [];
136
+ for (const value of Object.values(installation)) {
137
+ if (typeof value === "string" && value.trim()) lines.push(value);
138
+ }
139
+ return lines;
140
+ }
141
+
142
+ if (effectiveEdition === "server") {
143
+ const sdkEntry = registry.sdks["dcv-server"];
144
+ const targetPlatform = normalizePlatform(normalizedPlatform || sdkEntry.default_platform || "python");
145
+ const sampleName = selectDcvServerSample(targetPlatform, scenarioLower);
146
+ const samplePath = getDcvServerSamplePath(targetPlatform, sampleName);
147
+
148
+ if (!samplePath || !existsSync(samplePath)) {
149
+ return { isError: true, content: [{ type: "text", text: `Sample not found: ${sampleName}.` }] };
150
+ }
151
+
152
+ const { text: sampleContent, fence } = readBestSampleContent(samplePath);
153
+ const installLines = formatInstallLines(sdkEntry.platforms?.[targetPlatform]?.installation);
154
+
155
+ return {
156
+ content: [{
157
+ type: "text",
158
+ text: [
159
+ `# Quick Start: DCV Server (${targetPlatform})`,
160
+ "",
161
+ `**SDK Version:** ${sdkEntry.version}`,
162
+ `**Trial License:** \`${registry.trial_license}\``,
163
+ "",
164
+ installLines.length ? "## Install" : "",
165
+ installLines.length ? "```bash" : "",
166
+ ...installLines,
167
+ installLines.length ? "```" : "",
168
+ installLines.length ? "" : "",
169
+ `## ${sampleName}`,
170
+ "```" + fence,
171
+ sampleContent,
172
+ "```",
173
+ "",
174
+ `Docs: ${sdkEntry.platforms?.[targetPlatform]?.docs?.["user-guide"] || "N/A"}`
175
+ ].filter(Boolean).join("\n")
176
+ }]
177
+ };
178
+ }
179
+
180
+ if (effectiveEdition === "web") {
181
+ const sdkEntry = registry.sdks["dcv-web"];
182
+ const available = discoverDcvWebSamples();
183
+ const sampleName = scenarioLower.includes("vin") ? "VINScanner" : (available[0] || "VINScanner");
184
+ const samplePath = getDcvWebSamplePath(sampleName);
185
+ if (!samplePath || !existsSync(samplePath)) {
186
+ return { isError: true, content: [{ type: "text", text: `Sample not found: ${sampleName}.` }] };
187
+ }
188
+ const { text: sampleContent, fence } = readBestSampleContent(samplePath);
189
+ const installLines = formatInstallLines(sdkEntry.platforms?.web?.installation);
190
+
191
+ return {
192
+ content: [{
193
+ type: "text",
194
+ text: [
195
+ "# Quick Start: DCV Web",
196
+ "",
197
+ `**SDK Version:** ${sdkEntry.version}`,
198
+ `**Trial License:** \`${registry.trial_license}\``,
199
+ "",
200
+ installLines.length ? "## Install" : "",
201
+ installLines.length ? "```bash" : "",
202
+ ...installLines,
203
+ installLines.length ? "```" : "",
204
+ installLines.length ? "" : "",
205
+ `## ${sampleName}`,
206
+ "```" + fence,
207
+ sampleContent,
208
+ "```",
209
+ "",
210
+ `Docs: ${sdkEntry.platforms?.web?.docs?.["user-guide"] || "N/A"}`
211
+ ].filter(Boolean).join("\n")
212
+ }]
213
+ };
214
+ }
215
+
216
+ if (effectiveEdition === "mobile") {
217
+ const sdkEntry = registry.sdks["dcv-mobile"];
218
+ const targetPlatform = normalizePlatform(normalizedPlatform || sdkEntry.default_platform || "android");
219
+ const sampleNames = discoverDcvMobileSamples(targetPlatform);
220
+ const sampleName = selectMobileSample(sampleNames, scenarioLower);
221
+ const samplePath = getDcvMobileSamplePath(targetPlatform, sampleName);
222
+
223
+ if (!samplePath || !existsSync(samplePath)) {
224
+ return { isError: true, content: [{ type: "text", text: `Sample not found: ${sampleName || "N/A"}.` }] };
225
+ }
226
+
227
+ const { text: sampleContent, fence } = readBestSampleContent(samplePath);
228
+ const installLines = formatInstallLines(sdkEntry.platforms?.[targetPlatform]?.installation);
229
+
230
+ return {
231
+ content: [{
232
+ type: "text",
233
+ text: [
234
+ `# Quick Start: DCV Mobile (${targetPlatform})`,
235
+ "",
236
+ `**SDK Version:** ${sdkEntry.version}`,
237
+ `**Trial License:** \`${registry.trial_license}\``,
238
+ "",
239
+ installLines.length ? "## Install" : "",
240
+ installLines.length ? "```bash" : "",
241
+ ...installLines,
242
+ installLines.length ? "```" : "",
243
+ installLines.length ? "" : "",
244
+ `## ${sampleName}`,
245
+ "```" + fence,
246
+ sampleContent,
247
+ "```",
248
+ "",
249
+ `Docs: ${sdkEntry.platforms?.[targetPlatform]?.docs?.["user-guide"] || "N/A"}`
250
+ ].filter(Boolean).join("\n")
251
+ }]
252
+ };
253
+ }
254
+
255
+ if (effectiveEdition === "core") {
256
+ const sdkEntry = registry.sdks["dcv-core"];
257
+ return {
258
+ content: [{
259
+ type: "text",
260
+ text: [
261
+ "# Quick Start: DCV Core",
262
+ "",
263
+ `**SDK Version:** ${sdkEntry.version}`,
264
+ "",
265
+ "DCV core docs aggregate architecture, parameters, and cross-product workflows.",
266
+ `Docs: ${sdkEntry.platforms?.core?.docs?.introduction || "https://www.dynamsoft.com/capture-vision/docs/core/"}`
267
+ ].join("\n")
268
+ }]
269
+ };
270
+ }
271
+ }
272
+
273
+ if (normalizedProduct === "dbr" && normalizedEdition === "server") {
274
+ const sdkEntry = registry.sdks["dbr-server"];
275
+ const scenarioLower = (scenario || "").toLowerCase();
276
+ const sampleName = scenarioLower.includes("video") ? "video_decoding" : "read_an_image";
277
+ const samplePath = getDbrServerSamplePath("python", sampleName);
278
+
279
+ if (!existsSync(samplePath)) {
280
+ return { isError: true, content: [{ type: "text", text: `Sample not found: ${sampleName}.` }] };
281
+ }
282
+
283
+ const content = readCodeFile(samplePath);
284
+
285
+ return {
286
+ content: [{
287
+ type: "text",
288
+ text: [
289
+ "# Quick Start: DBR Server (Python)",
290
+ "",
291
+ `**SDK Version:** ${sdkEntry.version}`,
292
+ `**Trial License:** \`${registry.trial_license}\``,
293
+ "",
294
+ "## Install",
295
+ "```bash",
296
+ sdkEntry.platforms.python.installation.pip,
297
+ "```",
298
+ "",
299
+ `## ${sampleName}.py`,
300
+ "```python",
301
+ content,
302
+ "```",
303
+ "",
304
+ `Docs: ${sdkEntry.platforms.python.docs["user-guide"]}`
305
+ ].join("\n")
306
+ }]
307
+ };
308
+ }
309
+
310
+ if (normalizedProduct === "dbr" && normalizedEdition === "web") {
311
+ const sdkEntry = registry.sdks["dbr-web"];
312
+ const scenarioLower = (scenario || "").toLowerCase();
313
+ const sampleName = scenarioLower.includes("image") ? "read-an-image" : "hello-world";
314
+ const samplePath = getWebSamplePath("root", sampleName);
315
+
316
+ if (!samplePath || !existsSync(samplePath)) {
317
+ return { isError: true, content: [{ type: "text", text: `Sample not found: ${sampleName}.` }] };
318
+ }
319
+
320
+ const content = readCodeFile(samplePath);
321
+
322
+ return {
323
+ content: [{
324
+ type: "text",
325
+ text: [
326
+ "# Quick Start: DBR Web",
327
+ "",
328
+ `**SDK Version:** ${sdkEntry.version}`,
329
+ `**Trial License:** \`${registry.trial_license}\``,
330
+ "",
331
+ "## Option 1: CDN",
332
+ "```html",
333
+ `<script src="${sdkEntry.platforms.web.installation.cdn}"></script>`,
334
+ "```",
335
+ "",
336
+ "## Option 2: NPM",
337
+ "```bash",
338
+ sdkEntry.platforms.web.installation.npm,
339
+ "```",
340
+ "",
341
+ `## ${sampleName}.html`,
342
+ "```html",
343
+ content,
344
+ "```",
345
+ "",
346
+ `Docs: ${sdkEntry.platforms.web.docs["user-guide"]}`
347
+ ].join("\n")
348
+ }]
349
+ };
350
+ }
351
+
352
+ if (normalizedProduct === "dbr" && normalizedEdition === "mobile") {
353
+ const sdkEntry = registry.sdks["dbr-mobile"];
354
+ const targetPlatform = normalizedPlatform || "android";
355
+ const level = normalizeApiLevel(api_level || scenario);
356
+ const scenarioLower = (scenario || "").toLowerCase();
357
+
358
+ let sampleName = "ScanSingleBarcode";
359
+ if (scenarioLower.includes("multiple") || scenarioLower.includes("batch")) sampleName = "ScanMultipleBarcodes";
360
+ else if (scenarioLower.includes("image") || scenarioLower.includes("file")) sampleName = "DecodeFromAnImage";
361
+
362
+ if (level === "low-level") {
363
+ if (sampleName === "ScanSingleBarcode" || sampleName === "ScanMultipleBarcodes") {
364
+ sampleName = "DecodeWithCameraEnhancer";
365
+ }
366
+ }
367
+
368
+ const samplePath = getMobileSamplePath(targetPlatform, level, sampleName);
369
+ if (!existsSync(samplePath)) {
370
+ return { isError: true, content: [{ type: "text", text: `Sample not found: ${sampleName}.` }] };
371
+ }
372
+
373
+ const mainFile = getMainCodeFile(targetPlatform, samplePath);
374
+ if (!mainFile) {
375
+ return { isError: true, content: [{ type: "text", text: "Could not find main code file." }] };
376
+ }
377
+
378
+ const content = readCodeFile(mainFile.path);
379
+ const langExt = mainFile.filename.split(".").pop();
380
+
381
+ let deps = "";
382
+ if (targetPlatform === "android") {
383
+ deps = `
384
+ ## Dependencies
385
+
386
+ **Project build.gradle**
387
+ \`\`\`groovy
388
+ allprojects {
389
+ repositories {
390
+ google()
391
+ mavenCentral()
392
+ maven { url "${registry.maven_url}" }
393
+ }
394
+ }
395
+ \`\`\`
396
+
397
+ **App build.gradle**
398
+ \`\`\`groovy
399
+ dependencies {
400
+ implementation 'com.dynamsoft:barcodereaderbundle:${sdkEntry.version}'
401
+ }
402
+ \`\`\`
403
+
404
+ **AndroidManifest.xml**
405
+ \`\`\`xml
406
+ <uses-permission android:name="android.permission.CAMERA" />
407
+ \`\`\``;
408
+ } else {
409
+ deps = `
410
+ ## Dependencies
411
+
412
+ **Podfile**
413
+ \`\`\`ruby
414
+ platform :ios, '11.0'
415
+ use_frameworks!
416
+
417
+ target 'YourApp' do
418
+ pod 'DynamsoftBarcodeReaderBundle'
419
+ end
420
+ \`\`\`
421
+
422
+ **Info.plist**
423
+ \`\`\`xml
424
+ <key>NSCameraUsageDescription</key>
425
+ <string>Camera access for barcode scanning</string>
426
+ \`\`\``;
427
+ }
428
+
429
+ const output = [
430
+ "# Quick Start: DBR Mobile",
431
+ "",
432
+ `**SDK Version:** ${sdkEntry.version}`,
433
+ `**API Level:** ${level}`,
434
+ `**Trial License:** \`${registry.trial_license}\``,
435
+ "",
436
+ deps,
437
+ "",
438
+ `## ${mainFile.filename}`,
439
+ "```" + langExt,
440
+ content,
441
+ "```",
442
+ "",
443
+ `Docs: ${sdkEntry.platforms[targetPlatform]?.docs[level]?.["user-guide"] || "N/A"}`
444
+ ];
445
+
446
+ return { content: [{ type: "text", text: output.join("\n") }] };
447
+ }
448
+
449
+ if (normalizedProduct === "dwt") {
450
+ const sdkEntry = registry.sdks["dwt"];
451
+ const samplePath = getDwtSamplePath("scan", "basic-scan");
452
+
453
+ if (!samplePath || !existsSync(samplePath)) {
454
+ return { isError: true, content: [{ type: "text", text: "Sample not found: basic-scan." }] };
455
+ }
456
+
457
+ const content = readCodeFile(samplePath);
458
+
459
+ return {
460
+ content: [{
461
+ type: "text",
462
+ text: [
463
+ "# Quick Start: Dynamic Web TWAIN",
464
+ "",
465
+ `**SDK Version:** ${sdkEntry.version}`,
466
+ `**Trial License:** \`${registry.trial_license}\``,
467
+ "",
468
+ "## Option 1: CDN",
469
+ "```html",
470
+ `<script src="${sdkEntry.platforms.web.installation.cdn}"></script>`,
471
+ "```",
472
+ "",
473
+ "## Option 2: NPM",
474
+ "```bash",
475
+ sdkEntry.platforms.web.installation.npm,
476
+ "```",
477
+ "",
478
+ "## basic-scan.html",
479
+ "```html",
480
+ content,
481
+ "```",
482
+ "",
483
+ `Docs: ${sdkEntry.platforms.web.docs["user-guide"]}`
484
+ ].join("\n")
485
+ }]
486
+ };
487
+ }
488
+
489
+ if (normalizedProduct === "ddv") {
490
+ const sdkEntry = registry.sdks["ddv"];
491
+ const hint = `${scenario || ""} ${language || ""}`.toLowerCase();
492
+ let sampleName = "hello-world";
493
+
494
+ if (hint.includes("react")) sampleName = "react-vite";
495
+ else if (hint.includes("vue")) sampleName = "vue";
496
+ else if (hint.includes("angular")) sampleName = "angular";
497
+ else if (hint.includes("next")) sampleName = "next";
498
+
499
+ const samplePath = getDdvSamplePath(sampleName);
500
+ if (!samplePath || !existsSync(samplePath)) {
501
+ return { isError: true, content: [{ type: "text", text: `Sample not found: ${sampleName}.` }] };
502
+ }
503
+
504
+ let sampleContent = "";
505
+ let fence = "text";
506
+ const stat = statSync(samplePath);
507
+ if (stat.isDirectory()) {
508
+ const readmePath = join(samplePath, "README.md");
509
+ if (existsSync(readmePath)) {
510
+ sampleContent = readCodeFile(readmePath);
511
+ fence = "markdown";
512
+ } else {
513
+ const codeFiles = findCodeFilesInSample(samplePath);
514
+ if (codeFiles.length > 0) {
515
+ const preferredNames = [
516
+ "main.tsx",
517
+ "main.jsx",
518
+ "main.ts",
519
+ "main.js",
520
+ "App.tsx",
521
+ "App.jsx",
522
+ "App.vue"
523
+ ];
524
+ const preferred = codeFiles.find((file) => preferredNames.includes(file.filename)) || codeFiles[0];
525
+ sampleContent = readCodeFile(preferred.path);
526
+ fence = preferred.extension ? preferred.extension.replace(".", "") : "text";
527
+ } else {
528
+ sampleContent = "Sample found, but no code files detected.";
529
+ }
530
+ }
531
+ } else {
532
+ sampleContent = readCodeFile(samplePath);
533
+ fence = extname(samplePath).replace(".", "") || "text";
534
+ }
535
+
536
+ return {
537
+ content: [{
538
+ type: "text",
539
+ text: [
540
+ "# Quick Start: Dynamsoft Document Viewer",
541
+ "",
542
+ `**SDK Version:** ${sdkEntry.version}`,
543
+ `**Trial License:** \`${registry.trial_license}\``,
544
+ "",
545
+ "## Option 1: CDN",
546
+ "```html",
547
+ `<script src="${sdkEntry.platforms.web.installation.cdn}"></script>`,
548
+ "```",
549
+ "",
550
+ "## Option 2: NPM",
551
+ "```bash",
552
+ sdkEntry.platforms.web.installation.npm,
553
+ "```",
554
+ "",
555
+ `## ${sampleName}`,
556
+ "```" + fence,
557
+ sampleContent,
558
+ "```",
559
+ "",
560
+ `Docs: ${sdkEntry.platforms.web.docs["user-guide"]}`
561
+ ].join("\n")
562
+ }]
563
+ };
564
+ }
565
+
566
+ return {
567
+ isError: true,
568
+ content: [{ type: "text", text: "Unsupported product/edition for quickstart." }]
569
+ };
570
+ }
571
+ );
572
+ }