pulumi-package-docs 0.8.0 → 0.9.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/README.md +98 -4
- package/bin/args.js +54 -3
- package/bin/args.test.js +50 -0
- package/bin/cli-bun.js +54 -22
- package/bin/cli.js +74 -28
- package/bin/cli.test.js +18 -0
- package/bin/static.js +196 -0
- package/bin/static.test.js +176 -0
- package/dist/client/assets/MemberDetail-D8jkDkVP.js +1 -0
- package/dist/client/assets/{token-oQmZfAP3.js → examples-zkHFEaMt.js} +1 -1
- package/dist/client/assets/index-DM9VsIdh.js +13 -0
- package/dist/client/assets/providers._name-C-FSbmVZ.js +1 -0
- package/dist/client/assets/providers._name.functions._token-B3S8Gcfx.js +1 -0
- package/dist/client/assets/providers._name.functions._token-C4VPHGlG.js +1 -0
- package/dist/client/assets/providers._name.index-CWHPtoBD.js +1 -0
- package/dist/client/assets/providers._name.index-nlnQuJ9Q.js +1 -0
- package/dist/client/assets/providers._name.resources._token-BISpZVHI.js +1 -0
- package/dist/client/assets/providers._name.resources._token-CvMi94a3.js +1 -0
- package/dist/client/assets/{routes-DKNWlgPZ.js → routes-DwhZQHE3.js} +1 -1
- package/dist/mcp/stdio.js +449 -0
- package/dist/server/assets/{MemberDetail-C5sAHNZW.js → MemberDetail-CdHUSmQD.js} +2 -1
- package/dist/server/assets/{_tanstack-start-manifest_v-B0J-S-em.js → _tanstack-start-manifest_v-DNIp6gp3.js} +13 -15
- package/dist/server/assets/api-D49ZDhMh.js +27 -0
- package/dist/server/assets/{providers._name.functions._token-Gkw4zt3M.js → providers._name.functions._token-BvkwmQ-L.js} +3 -2
- package/dist/server/assets/{providers._name.index-CEXkl-76.js → providers._name.index-DBkfEjsy.js} +3 -2
- package/dist/server/assets/{providers._name.resources._token-R_i2MmRl.js → providers._name.resources._token-Sl31Qlvd.js} +3 -2
- package/dist/server/assets/{schema.server-B-VvU1qb.js → providers.server-B1ZHZ5jE.js} +48 -1
- package/dist/server/assets/{router-1INob9fh.js → router-_zs8P-B2.js} +24 -87
- package/dist/server/assets/{routes-4jiXnS1p.js → routes-BUxagg9r.js} +1 -1
- package/dist/server/assets/token-DBEFeZIN.js +92 -0
- package/dist/server/server.js +4 -4
- package/examples/github-pages.yml +56 -0
- package/examples/gitlab-ci.yml +22 -0
- package/package.json +6 -2
- package/src/components/ThemeToggle.tsx +37 -0
- package/src/lib/pulumi/api.ts +18 -69
- package/src/lib/pulumi/mcp.server.test.ts +112 -0
- package/src/lib/pulumi/mcp.server.ts +11 -6
- package/src/lib/pulumi/providers.server.ts +80 -0
- package/src/lib/pulumi/stdio.server.ts +16 -0
- package/src/lib/pulumi/token.test.ts +44 -1
- package/src/lib/pulumi/token.ts +28 -0
- package/src/lib/static-docs.ts +15 -0
- package/src/router.tsx +3 -0
- package/src/routes/__root.tsx +6 -3
- package/src/routes/providers.$name.functions.$token.tsx +7 -4
- package/src/routes/providers.$name.index.tsx +2 -2
- package/src/routes/providers.$name.resources.$token.tsx +7 -4
- package/vite.config.ts +41 -1
- package/dist/client/assets/MemberDetail-BS9DbqEt.js +0 -1
- package/dist/client/assets/index-2FzxAhzN.js +0 -13
- package/dist/client/assets/providers._name-FmJEVkdV.js +0 -1
- package/dist/client/assets/providers._name.functions._token-BzjmNHea.js +0 -1
- package/dist/client/assets/providers._name.functions._token-D768U6I-.js +0 -1
- package/dist/client/assets/providers._name.index-C42yQ95z.js +0 -1
- package/dist/client/assets/providers._name.index-Dm0YiZWP.js +0 -1
- package/dist/client/assets/providers._name.resources._token-CIYPoqyA.js +0 -1
- package/dist/client/assets/providers._name.resources._token-D0OLvgo_.js +0 -1
- package/dist/server/assets/api-C55bS4R6.js +0 -64
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{o as e}from"./useSelector-BIt42tGI.js";import{s as t}from"./index-DM9VsIdh.js";var n=e(),r=()=>(0,n.jsx)(t,{});export{r as component};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{o as e}from"./useSelector-BIt42tGI.js";import{c as t}from"./index-DM9VsIdh.js";var n=e(),r=()=>(0,n.jsx)(`main`,{children:(0,n.jsxs)(`p`,{children:[`No function with that name was found.`,` `,(0,n.jsx)(t,{to:`/`,children:`Back to all providers.`})]})});export{r as notFoundComponent};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{o as e}from"./useSelector-BIt42tGI.js";import{n as t,r as n}from"./index-DM9VsIdh.js";import{t as r}from"./MemberDetail-D8jkDkVP.js";var i=e();function a(){let{providerName:e,token:a,fn:o,runtime:s}=t.useLoaderData();return(0,i.jsx)(r,{providerName:e,kind:`Function`,displayName:n(a),token:a,description:o.description,runtime:s,inputProperties:o.inputs?.properties,requiredInputs:o.inputs?.required,outputProperties:o.outputs?.properties,requiredOutputs:o.outputs?.required})}export{a as component};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{o as e}from"./useSelector-BIt42tGI.js";import{c as t}from"./index-DM9VsIdh.js";var n=e(),r=()=>(0,n.jsx)(`main`,{children:(0,n.jsxs)(`p`,{children:[`No local provider with that name was found.`,` `,(0,n.jsx)(t,{to:`/`,children:`Back to all providers.`})]})});export{r as notFoundComponent};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{o as e}from"./useSelector-BIt42tGI.js";import{a as t,c as n,i as r,r as i}from"./index-DM9VsIdh.js";import{a,i as o}from"./examples-zkHFEaMt.js";var s=e();function c({token:e,member:t,runtime:c,to:l,providerName:u}){let d=o(a(t.description,c)),f=`deprecationMessage`in t&&t.deprecationMessage;return(0,s.jsxs)(`li`,{className:`doc-list-item`,children:[(0,s.jsx)(n,{to:l,params:{name:u,token:r(e)},children:(0,s.jsx)(`code`,{children:i(e)})}),f&&(0,s.jsx)(`span`,{className:`badge badge-warning`,children:`Deprecated`}),d&&(0,s.jsx)(`p`,{className:`doc-summary`,children:d})]})}function l(){let{ref:e,schema:r,origin:i,error:o,runtime:l}=t.useLoaderData(),u=Object.entries(r?.resources??{}),d=Object.entries(r?.functions??{});return(0,s.jsxs)(`main`,{className:`provider-detail`,children:[(0,s.jsx)(n,{to:`/`,className:`back-link`,children:`← All providers`}),(0,s.jsx)(`h1`,{children:e.name}),(0,s.jsxs)(`p`,{className:`subtitle`,children:[e.source,e.version?` @ ${e.version}`:``,e.parameters?.length?` (${e.parameters.join(` `)})`:``]}),i===`error`||!r?(0,s.jsxs)(`p`,{className:`banner banner-failure`,children:[`Failed to load schema: `,o??`unknown error`]}):(0,s.jsxs)(s.Fragment,{children:[i===`cache`&&(0,s.jsxs)(`p`,{className:`banner banner-info`,children:[`Showing a cached schema (the `,(0,s.jsx)(`code`,{children:`pulumi`}),` CLI call failed on this load).`]}),r.description&&(0,s.jsx)(`p`,{children:a(r.description,l)}),(0,s.jsx)(`h2`,{children:`Resources`}),u.length===0?(0,s.jsx)(`p`,{className:`empty-state`,children:`No resources.`}):(0,s.jsx)(`ul`,{className:`doc-list`,children:u.map(([t,n])=>(0,s.jsx)(c,{token:t,member:n,runtime:l,to:`/providers/$name/resources/$token`,providerName:e.name},t))}),(0,s.jsx)(`h2`,{children:`Functions`}),d.length===0?(0,s.jsx)(`p`,{className:`empty-state`,children:`No functions.`}):(0,s.jsx)(`ul`,{className:`doc-list`,children:d.map(([t,n])=>(0,s.jsx)(c,{token:t,member:n,runtime:l,to:`/providers/$name/functions/$token`,providerName:e.name},t))})]})]})}export{l as component};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{o as e}from"./useSelector-BIt42tGI.js";import{r as t,t as n}from"./index-DM9VsIdh.js";import{t as r}from"./MemberDetail-D8jkDkVP.js";var i=e();function a(){let{providerName:e,token:a,resource:o,runtime:s}=n.useLoaderData();return(0,i.jsx)(r,{providerName:e,kind:`Resource`,displayName:t(a),token:a,description:o.description,deprecationMessage:o.deprecationMessage,runtime:s,inputProperties:o.inputProperties,requiredInputs:o.requiredInputs,outputProperties:o.properties,requiredOutputs:o.required})}export{a as component};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{o as e}from"./useSelector-BIt42tGI.js";import{c as t}from"./index-DM9VsIdh.js";var n=e(),r=()=>(0,n.jsx)(`main`,{children:(0,n.jsxs)(`p`,{children:[`No resource with that name was found.`,` `,(0,n.jsx)(t,{to:`/`,children:`Back to all providers.`})]})});export{r as notFoundComponent};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{o as e}from"./useSelector-BIt42tGI.js";import{
|
|
1
|
+
import{o as e}from"./useSelector-BIt42tGI.js";import{c as t,o as n}from"./index-DM9VsIdh.js";var r=e();function i(){let{targetDir:e,project:i,providers:a}=n.useLoaderData();return i?(0,r.jsxs)(`main`,{children:[(0,r.jsx)(`h1`,{children:`Pulumi Package Docs`}),(0,r.jsxs)(`p`,{className:`subtitle`,children:[i.name??`Pulumi project`,` · `,(0,r.jsx)(`code`,{children:i.root})]}),a.length===0?(0,r.jsxs)(`p`,{className:`empty-state`,children:[`This project's `,(0,r.jsx)(`code`,{children:`Pulumi.yaml`}),` has no `,(0,r.jsx)(`code`,{children:`packages`}),` `,`section, so there are no local or parameterized providers to show docs for yet. Add one with `,(0,r.jsx)(`code`,{children:`pulumi package add`}),`.`]}):(0,r.jsx)(`ul`,{className:`provider-list`,children:a.map(e=>(0,r.jsxs)(`li`,{className:`provider-card`,children:[(0,r.jsxs)(t,{to:`/providers/$name`,params:{name:e.name},children:[(0,r.jsx)(`span`,{className:`provider-name`,children:e.name}),(0,r.jsxs)(`span`,{className:`provider-source`,children:[e.source,e.version?` @ ${e.version}`:``,e.parameters?.length?` (${e.parameters.join(` `)})`:``]})]}),e.origin===`error`?(0,r.jsxs)(`p`,{className:`banner banner-failure`,children:[`Failed to load schema: `,e.error]}):(0,r.jsxs)(`p`,{className:`provider-stats`,children:[e.resourceCount,` resource`,e.resourceCount===1?``:`s`,` · `,e.functionCount,` function`,e.functionCount===1?``:`s`,e.origin===`cache`&&(0,r.jsxs)(r.Fragment,{children:[` · `,(0,r.jsx)(`span`,{className:`badge badge-info`,children:`from cache`})]})]})]},e.name))})]}):(0,r.jsxs)(`main`,{children:[(0,r.jsx)(`h1`,{children:`Pulumi Package Docs`}),(0,r.jsxs)(`p`,{className:`empty-state`,children:[`No Pulumi project was found at `,(0,r.jsx)(`code`,{children:e}),` (or any parent directory). Run this from inside a Pulumi project directory, or with`,` `,(0,r.jsx)(`code`,{children:`--dir <path>`}),` pointing at one.`]})]})}export{i as component};
|
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { dirname, join } from "node:path";
|
|
6
|
+
import { parse } from "yaml";
|
|
7
|
+
import { execFile } from "node:child_process";
|
|
8
|
+
import { createHash } from "node:crypto";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
10
|
+
import { promisify } from "node:util";
|
|
11
|
+
//#region src/lib/pulumi/description.ts
|
|
12
|
+
/**
|
|
13
|
+
* Pulumi schema descriptions embed per-language text as HTML-ish spans, e.g.
|
|
14
|
+
* `<span pulumi-lang-nodejs="`length`" pulumi-lang-python="`length`" ...>`length`</span>`.
|
|
15
|
+
* pulumi.com's docs site picks an attribute based on the selected language tab;
|
|
16
|
+
* we don't have tabs, so we resolve each span to the project's own runtime up
|
|
17
|
+
* front and strip the markup rather than rendering (or leaking) raw HTML.
|
|
18
|
+
*/
|
|
19
|
+
var LANG_SPAN_RE = /<span\s+((?:pulumi-lang-[a-z]+="[^"]*"\s*)+)>([\s\S]*?)<\/span>/g;
|
|
20
|
+
var LANG_ATTR_RE = /pulumi-lang-([a-z]+)="([^"]*)"/g;
|
|
21
|
+
function resolveLangSpans(text, runtime) {
|
|
22
|
+
return text.replace(LANG_SPAN_RE, (_match, attrsSource, fallback) => {
|
|
23
|
+
if (!runtime) return fallback;
|
|
24
|
+
LANG_ATTR_RE.lastIndex = 0;
|
|
25
|
+
let match;
|
|
26
|
+
while (match = LANG_ATTR_RE.exec(attrsSource)) if (match[1] === runtime) return match[2];
|
|
27
|
+
return fallback;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/** Resolves a schema description's per-language spans for the given Pulumi runtime. */
|
|
31
|
+
function resolveDescription(text, runtime) {
|
|
32
|
+
if (!text) return text;
|
|
33
|
+
return resolveLangSpans(text, runtime);
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/lib/pulumi/discovery.server.ts
|
|
37
|
+
var PROJECT_FILE_NAMES = ["Pulumi.yaml", "Pulumi.yml"];
|
|
38
|
+
/**
|
|
39
|
+
* The directory of the Pulumi program to inspect. `bin/cli.js` sets this to the
|
|
40
|
+
* directory the user ran `npx pulumi-package-docs` from, since the dev/preview
|
|
41
|
+
* server itself runs with this package's own directory as its cwd.
|
|
42
|
+
*/
|
|
43
|
+
function getTargetDir() {
|
|
44
|
+
return process.env.PULUMI_LOCAL_DOCS_DIR || process.cwd();
|
|
45
|
+
}
|
|
46
|
+
function findProjectFile(startDir) {
|
|
47
|
+
let dir = startDir;
|
|
48
|
+
for (;;) {
|
|
49
|
+
for (const fileName of PROJECT_FILE_NAMES) {
|
|
50
|
+
const candidate = join(dir, fileName);
|
|
51
|
+
if (existsSync(candidate)) return candidate;
|
|
52
|
+
}
|
|
53
|
+
const parent = dirname(dir);
|
|
54
|
+
if (parent === dir) return null;
|
|
55
|
+
dir = parent;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function toLocalPackageRefs(packages) {
|
|
59
|
+
if (!packages || typeof packages !== "object") return [];
|
|
60
|
+
return Object.entries(packages).map(([name, value]) => {
|
|
61
|
+
const entry = value ?? {};
|
|
62
|
+
const parameters = Array.isArray(entry.parameters) ? entry.parameters.filter((p) => typeof p === "string") : void 0;
|
|
63
|
+
return {
|
|
64
|
+
name,
|
|
65
|
+
source: typeof entry.source === "string" ? entry.source : name,
|
|
66
|
+
version: typeof entry.version === "string" ? entry.version : void 0,
|
|
67
|
+
parameters
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Walks up from `startDir` looking for a Pulumi.yaml/Pulumi.yml, then reads its
|
|
73
|
+
* `packages` section to find the local/parameterized providers used by the project.
|
|
74
|
+
*/
|
|
75
|
+
function loadPulumiProject(startDir = getTargetDir()) {
|
|
76
|
+
const projectFile = findProjectFile(startDir);
|
|
77
|
+
if (!projectFile) return null;
|
|
78
|
+
const raw = readFileSync(projectFile, "utf-8");
|
|
79
|
+
const doc = parse(raw);
|
|
80
|
+
return {
|
|
81
|
+
root: dirname(projectFile),
|
|
82
|
+
name: typeof doc?.name === "string" ? doc.name : void 0,
|
|
83
|
+
runtime: typeof doc?.runtime === "string" ? doc.runtime : typeof (doc?.runtime)?.name === "string" ? doc.runtime.name : void 0,
|
|
84
|
+
packages: toLocalPackageRefs(doc?.packages)
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
//#endregion
|
|
88
|
+
//#region src/lib/pulumi/examples.ts
|
|
89
|
+
var CODE_FENCE_RE = /```([a-zA-Z0-9+#-]+)\n([\s\S]*?)```/g;
|
|
90
|
+
function extractExamples(text) {
|
|
91
|
+
if (!text) return [];
|
|
92
|
+
return [...text.matchAll(CODE_FENCE_RE)].map((match) => ({
|
|
93
|
+
language: match[1].toLowerCase(),
|
|
94
|
+
code: match[2].trimEnd()
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
/** The description text with any fenced code examples removed. */
|
|
98
|
+
function stripExamples(text) {
|
|
99
|
+
if (!text) return text;
|
|
100
|
+
return text.replace(CODE_FENCE_RE, "").trim() || void 0;
|
|
101
|
+
}
|
|
102
|
+
/** A short summary for list views: the first prose paragraph, examples excluded. */
|
|
103
|
+
function summarizeDescription(text) {
|
|
104
|
+
const stripped = stripExamples(text);
|
|
105
|
+
if (!stripped) return void 0;
|
|
106
|
+
return stripped.split(/\n{2,}/)[0]?.trim() || void 0;
|
|
107
|
+
}
|
|
108
|
+
var RUNTIME_LANGUAGES = {
|
|
109
|
+
nodejs: ["typescript", "javascript"],
|
|
110
|
+
python: ["python"],
|
|
111
|
+
go: ["go", "golang"],
|
|
112
|
+
dotnet: ["csharp"],
|
|
113
|
+
java: ["java"],
|
|
114
|
+
yaml: ["yaml"]
|
|
115
|
+
};
|
|
116
|
+
/** Picks the example matching the project's runtime, falling back to the first available one. */
|
|
117
|
+
function pickExample(examples, runtime) {
|
|
118
|
+
const languages = runtime ? RUNTIME_LANGUAGES[runtime] : void 0;
|
|
119
|
+
return (languages ? examples.find((example) => languages.includes(example.language)) : void 0) ?? examples[0];
|
|
120
|
+
}
|
|
121
|
+
//#endregion
|
|
122
|
+
//#region src/lib/pulumi/format.ts
|
|
123
|
+
function formatType(prop) {
|
|
124
|
+
if (!prop) return "any";
|
|
125
|
+
if (prop.$ref) {
|
|
126
|
+
const short = prop.$ref.split("/").pop() ?? prop.$ref;
|
|
127
|
+
return decodeURIComponent(short);
|
|
128
|
+
}
|
|
129
|
+
if (prop.type === "array" && prop.items) return `${formatType(prop.items)}[]`;
|
|
130
|
+
return prop.type ?? "any";
|
|
131
|
+
}
|
|
132
|
+
//#endregion
|
|
133
|
+
//#region src/lib/pulumi/naming.ts
|
|
134
|
+
/**
|
|
135
|
+
* Pulumi schema property names are always camelCase; each language's generated
|
|
136
|
+
* SDK renders them in that language's own convention. This approximates that
|
|
137
|
+
* per-runtime rendering for display purposes (it doesn't reproduce Pulumi's
|
|
138
|
+
* codegen exactly, e.g. its acronym handling, but matches the common case).
|
|
139
|
+
*/
|
|
140
|
+
function toSnakeCase(name) {
|
|
141
|
+
return name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").toLowerCase();
|
|
142
|
+
}
|
|
143
|
+
function toPascalCase(name) {
|
|
144
|
+
if (name.length === 0) return name;
|
|
145
|
+
return name.charAt(0).toUpperCase() + name.slice(1);
|
|
146
|
+
}
|
|
147
|
+
/** Renders a schema property name the way it would appear in the given Pulumi runtime's generated SDK. */
|
|
148
|
+
function formatPropertyName(name, runtime) {
|
|
149
|
+
switch (runtime) {
|
|
150
|
+
case "python": return toSnakeCase(name);
|
|
151
|
+
case "go":
|
|
152
|
+
case "dotnet": return toPascalCase(name);
|
|
153
|
+
default: return name;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
//#endregion
|
|
157
|
+
//#region src/lib/pulumi/schema.server.ts
|
|
158
|
+
var execFileAsync = promisify(execFile);
|
|
159
|
+
var CLI_TIMEOUT_MS = 6e4;
|
|
160
|
+
var MAX_BUFFER_BYTES = 67108864;
|
|
161
|
+
function cacheFilePath(projectRoot, ref) {
|
|
162
|
+
const projectHash = createHash("sha256").update(projectRoot).digest("hex").slice(0, 16);
|
|
163
|
+
const dir = join(tmpdir(), "pulumi-package-docs", projectHash);
|
|
164
|
+
mkdirSync(dir, { recursive: true });
|
|
165
|
+
return join(dir, `${ref.name}.json`);
|
|
166
|
+
}
|
|
167
|
+
function readCache(path) {
|
|
168
|
+
if (!existsSync(path)) return null;
|
|
169
|
+
try {
|
|
170
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
171
|
+
} catch {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
async function fetchViaCli(projectRoot, ref) {
|
|
176
|
+
const { stdout } = await execFileAsync("pulumi", [
|
|
177
|
+
"package",
|
|
178
|
+
"get-schema",
|
|
179
|
+
ref.source,
|
|
180
|
+
...ref.parameters ?? []
|
|
181
|
+
], {
|
|
182
|
+
cwd: projectRoot,
|
|
183
|
+
timeout: CLI_TIMEOUT_MS,
|
|
184
|
+
maxBuffer: MAX_BUFFER_BYTES
|
|
185
|
+
});
|
|
186
|
+
return JSON.parse(stdout);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Resolves the schema for a local package: try the `pulumi` CLI first (it's the
|
|
190
|
+
* source of truth and works for parameterized providers), and fall back to the
|
|
191
|
+
* last schema we successfully cached for this project if the CLI call fails.
|
|
192
|
+
*/
|
|
193
|
+
async function resolveProviderSchema(projectRoot, ref) {
|
|
194
|
+
const cachePath = cacheFilePath(projectRoot, ref);
|
|
195
|
+
try {
|
|
196
|
+
const schema = await fetchViaCli(projectRoot, ref);
|
|
197
|
+
writeFileSync(cachePath, JSON.stringify(schema));
|
|
198
|
+
return {
|
|
199
|
+
ref,
|
|
200
|
+
schema,
|
|
201
|
+
origin: "cli"
|
|
202
|
+
};
|
|
203
|
+
} catch (cliError) {
|
|
204
|
+
const cached = readCache(cachePath);
|
|
205
|
+
if (cached) return {
|
|
206
|
+
ref,
|
|
207
|
+
schema: cached,
|
|
208
|
+
origin: "cache"
|
|
209
|
+
};
|
|
210
|
+
return {
|
|
211
|
+
ref,
|
|
212
|
+
schema: null,
|
|
213
|
+
origin: "error",
|
|
214
|
+
error: cliError instanceof Error ? cliError.message : String(cliError)
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
//#endregion
|
|
219
|
+
//#region src/lib/pulumi/providers.server.ts
|
|
220
|
+
/**
|
|
221
|
+
* The project + provider summaries shown on the index route and by the MCP
|
|
222
|
+
* `list_providers` tool. Plain async functions (no TanStack Start server-fn
|
|
223
|
+
* wrapper) so they can run either inside the web server or in the stdio MCP
|
|
224
|
+
* process, which has no HTTP server at all.
|
|
225
|
+
*/
|
|
226
|
+
async function loadProjectSummary() {
|
|
227
|
+
const targetDir = getTargetDir();
|
|
228
|
+
const project = loadPulumiProject(targetDir);
|
|
229
|
+
if (!project) return {
|
|
230
|
+
targetDir,
|
|
231
|
+
project: null,
|
|
232
|
+
providers: []
|
|
233
|
+
};
|
|
234
|
+
const providers = (await Promise.all(project.packages.map((ref) => resolveProviderSchema(project.root, ref)))).map((entry) => ({
|
|
235
|
+
name: entry.ref.name,
|
|
236
|
+
source: entry.ref.source,
|
|
237
|
+
version: entry.ref.version,
|
|
238
|
+
parameters: entry.ref.parameters,
|
|
239
|
+
resourceCount: Object.keys(entry.schema?.resources ?? {}).length,
|
|
240
|
+
functionCount: Object.keys(entry.schema?.functions ?? {}).length,
|
|
241
|
+
origin: entry.origin,
|
|
242
|
+
error: entry.error
|
|
243
|
+
}));
|
|
244
|
+
return {
|
|
245
|
+
targetDir,
|
|
246
|
+
project: {
|
|
247
|
+
root: project.root,
|
|
248
|
+
name: project.name,
|
|
249
|
+
runtime: project.runtime
|
|
250
|
+
},
|
|
251
|
+
providers
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
/** The fully resolved schema for one local provider, or null if the project or provider isn't found. */
|
|
255
|
+
async function loadProviderDetail(name) {
|
|
256
|
+
const project = loadPulumiProject(getTargetDir());
|
|
257
|
+
if (!project) return null;
|
|
258
|
+
const ref = project.packages.find((p) => p.name === name);
|
|
259
|
+
if (!ref) return null;
|
|
260
|
+
return {
|
|
261
|
+
...await resolveProviderSchema(project.root, ref),
|
|
262
|
+
runtime: project.runtime
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
//#endregion
|
|
266
|
+
//#region src/lib/pulumi/token.ts
|
|
267
|
+
/**
|
|
268
|
+
* Schema tokens look like `random:index/randomString:RandomString` (resources)
|
|
269
|
+
* or `random:index/randomInteger:randomInteger` (functions). The segment after
|
|
270
|
+
* the last colon is the name every generated SDK exports for that member, and
|
|
271
|
+
* is unique within the provider, so it makes a readable display name.
|
|
272
|
+
*/
|
|
273
|
+
function tokenDisplayName(token) {
|
|
274
|
+
const short = token.split(":").pop();
|
|
275
|
+
return short && short.length > 0 ? short : token;
|
|
276
|
+
}
|
|
277
|
+
//#endregion
|
|
278
|
+
//#region src/lib/pulumi/mcp.server.ts
|
|
279
|
+
function textResult(data) {
|
|
280
|
+
return { content: [{
|
|
281
|
+
type: "text",
|
|
282
|
+
text: JSON.stringify(data, null, 2)
|
|
283
|
+
}] };
|
|
284
|
+
}
|
|
285
|
+
/** Loads a local provider's resolved schema, throwing a descriptive error a tool call can surface to the client. */
|
|
286
|
+
async function requireProvider(providerName) {
|
|
287
|
+
const detail = await loadProviderDetail(providerName);
|
|
288
|
+
if (!detail) throw new Error(`No local provider named "${providerName}" was found. Call list_providers to see the providers declared in this project's Pulumi.yaml.`);
|
|
289
|
+
if (!detail.schema) throw new Error(`Failed to load the schema for provider "${providerName}": ${detail.error ?? "unknown error"}`);
|
|
290
|
+
return {
|
|
291
|
+
...detail,
|
|
292
|
+
schema: detail.schema
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
function summarizeMember(token, member, runtime) {
|
|
296
|
+
return {
|
|
297
|
+
token,
|
|
298
|
+
name: tokenDisplayName(token),
|
|
299
|
+
deprecated: Boolean("deprecationMessage" in member && member.deprecationMessage),
|
|
300
|
+
summary: summarizeDescription(resolveDescription(member.description, runtime))
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
function formatProperties(properties, required, runtime) {
|
|
304
|
+
const requiredSet = new Set(required ?? []);
|
|
305
|
+
return Object.entries(properties ?? {}).map(([name, prop]) => ({
|
|
306
|
+
name: formatPropertyName(name, runtime),
|
|
307
|
+
type: formatType(prop),
|
|
308
|
+
required: requiredSet.has(name),
|
|
309
|
+
description: resolveDescription(prop.description, runtime)
|
|
310
|
+
}));
|
|
311
|
+
}
|
|
312
|
+
function memberDetail(provider, token, description, runtime, inputs, outputs, deprecationMessage) {
|
|
313
|
+
const resolved = resolveDescription(description, runtime);
|
|
314
|
+
const example = pickExample(extractExamples(resolved), runtime);
|
|
315
|
+
return {
|
|
316
|
+
provider,
|
|
317
|
+
token,
|
|
318
|
+
name: tokenDisplayName(token),
|
|
319
|
+
deprecationMessage,
|
|
320
|
+
description: stripExamples(resolved),
|
|
321
|
+
example: example ?? null,
|
|
322
|
+
inputs,
|
|
323
|
+
outputs
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Builds a fresh MCP server with the local-provider-docs tools registered. Transport
|
|
328
|
+
* agnostic: `src/routes/mcp.ts` calls it once per HTTP request (so concurrent requests
|
|
329
|
+
* never share state), and `src/lib/pulumi/stdio.server.ts` calls it once for the
|
|
330
|
+
* lifetime of a stdio process.
|
|
331
|
+
*/
|
|
332
|
+
function createMcpServer() {
|
|
333
|
+
const server = new McpServer({
|
|
334
|
+
name: "pulumi-package-docs",
|
|
335
|
+
version: "0.1.0"
|
|
336
|
+
});
|
|
337
|
+
server.registerTool("list_providers", {
|
|
338
|
+
title: "List local providers",
|
|
339
|
+
description: "List the local/parameterized Pulumi providers declared in this project's Pulumi.yaml (added with `pulumi package add`), with their source, version, and resource/function counts."
|
|
340
|
+
}, async () => textResult(await loadProjectSummary()));
|
|
341
|
+
server.registerTool("list_resources", {
|
|
342
|
+
title: "List a provider’s resources",
|
|
343
|
+
description: "List the resource tokens for a local provider, each with a display name, a one-line summary, and whether it is deprecated. Use get_resource for full details (inputs/outputs/example) on one of them.",
|
|
344
|
+
inputSchema: { provider: z.string().describe("The provider name as it appears in Pulumi.yaml and list_providers, e.g. \"random\".") }
|
|
345
|
+
}, async ({ provider }) => {
|
|
346
|
+
const detail = await requireProvider(provider);
|
|
347
|
+
const resources = Object.entries(detail.schema.resources ?? {}).map(([token, resource]) => summarizeMember(token, resource, detail.runtime));
|
|
348
|
+
return textResult({
|
|
349
|
+
provider,
|
|
350
|
+
resourceCount: resources.length,
|
|
351
|
+
resources
|
|
352
|
+
});
|
|
353
|
+
});
|
|
354
|
+
server.registerTool("list_functions", {
|
|
355
|
+
title: "List a provider’s functions",
|
|
356
|
+
description: "List the function (data source) tokens for a local provider, each with a display name, a one-line summary, and whether it is deprecated. Use get_function for full details (inputs/outputs/example) on one of them.",
|
|
357
|
+
inputSchema: { provider: z.string().describe("The provider name as it appears in Pulumi.yaml and list_providers, e.g. \"random\".") }
|
|
358
|
+
}, async ({ provider }) => {
|
|
359
|
+
const detail = await requireProvider(provider);
|
|
360
|
+
const functions = Object.entries(detail.schema.functions ?? {}).map(([token, fn]) => summarizeMember(token, fn, detail.runtime));
|
|
361
|
+
return textResult({
|
|
362
|
+
provider,
|
|
363
|
+
functionCount: functions.length,
|
|
364
|
+
functions
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
server.registerTool("get_resource", {
|
|
368
|
+
title: "Get resource details",
|
|
369
|
+
description: "Get the full documentation for one resource of a local provider: its description, a usage example (in the Pulumi project’s own language), and its input and output properties (name, type, required, description). Get the exact token from list_resources first.",
|
|
370
|
+
inputSchema: {
|
|
371
|
+
provider: z.string().describe("The provider name, e.g. \"random\"."),
|
|
372
|
+
token: z.string().describe("The resource’s full schema token from list_resources, e.g. \"random:index/randomString:RandomString\".")
|
|
373
|
+
}
|
|
374
|
+
}, async ({ provider, token }) => {
|
|
375
|
+
const detail = await requireProvider(provider);
|
|
376
|
+
const resource = detail.schema.resources?.[token];
|
|
377
|
+
if (!resource) throw new Error(`No resource with token "${token}" in provider "${provider}". Call list_resources to find the exact token.`);
|
|
378
|
+
return textResult(memberDetail(provider, token, resource.description, detail.runtime, formatProperties(resource.inputProperties, resource.requiredInputs, detail.runtime), formatProperties(resource.properties, resource.required, detail.runtime), resource.deprecationMessage));
|
|
379
|
+
});
|
|
380
|
+
server.registerTool("get_function", {
|
|
381
|
+
title: "Get function details",
|
|
382
|
+
description: "Get the full documentation for one function (data source) of a local provider: its description, a usage example (in the Pulumi project’s own language), and its input and output properties. Get the exact token from list_functions first.",
|
|
383
|
+
inputSchema: {
|
|
384
|
+
provider: z.string().describe("The provider name, e.g. \"random\"."),
|
|
385
|
+
token: z.string().describe("The function’s full schema token from list_functions, e.g. \"random:index/randomInteger:getRandomInteger\".")
|
|
386
|
+
}
|
|
387
|
+
}, async ({ provider, token }) => {
|
|
388
|
+
const detail = await requireProvider(provider);
|
|
389
|
+
const fn = detail.schema.functions?.[token];
|
|
390
|
+
if (!fn) throw new Error(`No function with token "${token}" in provider "${provider}". Call list_functions to find the exact token.`);
|
|
391
|
+
return textResult(memberDetail(provider, token, fn.description, detail.runtime, formatProperties(fn.inputs?.properties, fn.inputs?.required, detail.runtime), formatProperties(fn.outputs?.properties, fn.outputs?.required, detail.runtime)));
|
|
392
|
+
});
|
|
393
|
+
server.registerTool("search_members", {
|
|
394
|
+
title: "Search resources and functions",
|
|
395
|
+
description: "Search resource and function tokens/descriptions across one or all local providers for a keyword. Use this instead of list_resources/list_functions when a provider is large or you only know roughly what you’re looking for (e.g. \"bucket\", \"random string\").",
|
|
396
|
+
inputSchema: {
|
|
397
|
+
query: z.string().describe("Case-insensitive text to search for."),
|
|
398
|
+
provider: z.string().optional().describe("Limit the search to this provider name. Omit to search all local providers.")
|
|
399
|
+
}
|
|
400
|
+
}, async ({ query, provider }) => {
|
|
401
|
+
const project = loadPulumiProject(getTargetDir());
|
|
402
|
+
if (!project) return textResult({
|
|
403
|
+
query,
|
|
404
|
+
matchCount: 0,
|
|
405
|
+
matches: []
|
|
406
|
+
});
|
|
407
|
+
const refs = provider ? project.packages.filter((p) => p.name === provider) : project.packages;
|
|
408
|
+
if (provider && refs.length === 0) throw new Error(`No local provider named "${provider}" was found. Call list_providers to see the providers declared in this project's Pulumi.yaml.`);
|
|
409
|
+
const needle = query.toLowerCase();
|
|
410
|
+
const matches = [];
|
|
411
|
+
for (const ref of refs) {
|
|
412
|
+
const entry = await resolveProviderSchema(project.root, ref);
|
|
413
|
+
if (!entry.schema) continue;
|
|
414
|
+
const groups = [["resource", entry.schema.resources], ["function", entry.schema.functions]];
|
|
415
|
+
for (const [kind, members] of groups) for (const [token, member] of Object.entries(members ?? {})) {
|
|
416
|
+
const name = tokenDisplayName(token);
|
|
417
|
+
const summary = summarizeDescription(resolveDescription(member.description, project.runtime));
|
|
418
|
+
if (`${name} ${token} ${summary ?? ""}`.toLowerCase().includes(needle)) matches.push({
|
|
419
|
+
provider: ref.name,
|
|
420
|
+
kind,
|
|
421
|
+
token,
|
|
422
|
+
name,
|
|
423
|
+
summary
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
return textResult({
|
|
428
|
+
query,
|
|
429
|
+
matchCount: matches.length,
|
|
430
|
+
matches: matches.slice(0, 50)
|
|
431
|
+
});
|
|
432
|
+
});
|
|
433
|
+
return server;
|
|
434
|
+
}
|
|
435
|
+
//#endregion
|
|
436
|
+
//#region src/lib/pulumi/stdio.server.ts
|
|
437
|
+
/**
|
|
438
|
+
* Serves the same provider-docs tools as the `/mcp` HTTP route over stdio, for MCP
|
|
439
|
+
* clients that spawn a server process instead of connecting to a URL.
|
|
440
|
+
*
|
|
441
|
+
* Unlike the HTTP route this is a single long-lived server (one process, one client),
|
|
442
|
+
* and stdout is the protocol channel — nothing on this path may write to it, so any
|
|
443
|
+
* diagnostics belong on stderr.
|
|
444
|
+
*/
|
|
445
|
+
async function runStdioMcpServer() {
|
|
446
|
+
await createMcpServer().connect(new StdioServerTransport());
|
|
447
|
+
}
|
|
448
|
+
//#endregion
|
|
449
|
+
export { runStdioMcpServer };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as pickExample, c as resolveDescription, i as extractExamples, o as stripExamples } from "./token-DBEFeZIN.js";
|
|
2
|
+
import { a as formatPropertyName, o as formatType } from "./router-_zs8P-B2.js";
|
|
2
3
|
import { Link } from "@tanstack/react-router";
|
|
3
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
5
|
//#region src/components/CodeExample.tsx
|
|
@@ -7,17 +7,17 @@ var tsrStartManifest = () => ({ routes: {
|
|
|
7
7
|
"/mcp",
|
|
8
8
|
"/providers/$name"
|
|
9
9
|
],
|
|
10
|
-
preloads: ["/assets/index-
|
|
10
|
+
preloads: ["/assets/index-DM9VsIdh.js", "/assets/useSelector-BIt42tGI.js"],
|
|
11
11
|
scripts: [{ attrs: {
|
|
12
12
|
type: "module",
|
|
13
13
|
async: !0,
|
|
14
|
-
src: "/assets/index-
|
|
14
|
+
src: "/assets/index-DM9VsIdh.js"
|
|
15
15
|
} }]
|
|
16
16
|
},
|
|
17
17
|
"/": {
|
|
18
18
|
filePath: "/home/runner/work/pulumi-package-docs/pulumi-package-docs/src/routes/index.tsx",
|
|
19
19
|
children: void 0,
|
|
20
|
-
preloads: ["/assets/routes-
|
|
20
|
+
preloads: ["/assets/routes-DwhZQHE3.js"]
|
|
21
21
|
},
|
|
22
22
|
"/providers/$name": {
|
|
23
23
|
filePath: "/home/runner/work/pulumi-package-docs/pulumi-package-docs/src/routes/providers.$name.tsx",
|
|
@@ -26,35 +26,33 @@ var tsrStartManifest = () => ({ routes: {
|
|
|
26
26
|
"/providers/$name/functions/$token",
|
|
27
27
|
"/providers/$name/resources/$token"
|
|
28
28
|
],
|
|
29
|
-
preloads: ["/assets/providers._name-
|
|
29
|
+
preloads: ["/assets/providers._name-C-FSbmVZ.js"]
|
|
30
30
|
},
|
|
31
31
|
"/providers/$name/": {
|
|
32
32
|
filePath: "/home/runner/work/pulumi-package-docs/pulumi-package-docs/src/routes/providers.$name.index.tsx",
|
|
33
33
|
children: void 0,
|
|
34
34
|
preloads: [
|
|
35
|
-
"/assets/providers._name.index-
|
|
36
|
-
"/assets/providers._name.index-
|
|
37
|
-
"/assets/
|
|
35
|
+
"/assets/providers._name.index-CWHPtoBD.js",
|
|
36
|
+
"/assets/providers._name.index-nlnQuJ9Q.js",
|
|
37
|
+
"/assets/examples-zkHFEaMt.js"
|
|
38
38
|
]
|
|
39
39
|
},
|
|
40
40
|
"/providers/$name/functions/$token": {
|
|
41
41
|
filePath: "/home/runner/work/pulumi-package-docs/pulumi-package-docs/src/routes/providers.$name.functions.$token.tsx",
|
|
42
42
|
children: void 0,
|
|
43
43
|
preloads: [
|
|
44
|
-
"/assets/providers._name.functions._token-
|
|
45
|
-
"/assets/providers._name.functions._token-
|
|
46
|
-
"/assets/MemberDetail-
|
|
47
|
-
"/assets/token-oQmZfAP3.js"
|
|
44
|
+
"/assets/providers._name.functions._token-B3S8Gcfx.js",
|
|
45
|
+
"/assets/providers._name.functions._token-C4VPHGlG.js",
|
|
46
|
+
"/assets/MemberDetail-D8jkDkVP.js"
|
|
48
47
|
]
|
|
49
48
|
},
|
|
50
49
|
"/providers/$name/resources/$token": {
|
|
51
50
|
filePath: "/home/runner/work/pulumi-package-docs/pulumi-package-docs/src/routes/providers.$name.resources.$token.tsx",
|
|
52
51
|
children: void 0,
|
|
53
52
|
preloads: [
|
|
54
|
-
"/assets/providers._name.resources._token-
|
|
55
|
-
"/assets/MemberDetail-
|
|
56
|
-
"/assets/
|
|
57
|
-
"/assets/providers._name.resources._token-D0OLvgo_.js"
|
|
53
|
+
"/assets/providers._name.resources._token-BISpZVHI.js",
|
|
54
|
+
"/assets/MemberDetail-D8jkDkVP.js",
|
|
55
|
+
"/assets/providers._name.resources._token-CvMi94a3.js"
|
|
58
56
|
]
|
|
59
57
|
}
|
|
60
58
|
} });
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { n as TSS_SERVER_FUNCTION, t as createServerFn } from "../server.js";
|
|
2
|
+
import { n as loadProviderDetail, t as loadProjectSummary } from "./providers.server-B1ZHZ5jE.js";
|
|
3
|
+
//#region node_modules/@tanstack/start-server-core/dist/esm/createServerRpc.js
|
|
4
|
+
var createServerRpc = (serverFnMeta, splitImportFn) => {
|
|
5
|
+
const url = "/_serverFn/" + serverFnMeta.id;
|
|
6
|
+
return Object.assign(splitImportFn, {
|
|
7
|
+
url,
|
|
8
|
+
serverFnMeta,
|
|
9
|
+
[TSS_SERVER_FUNCTION]: true
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/lib/pulumi/api.ts?tss-serverfn-split
|
|
14
|
+
var listProviders_createServerFn_handler = createServerRpc({
|
|
15
|
+
id: "a3ba03544bae9a67523c84875835e6e9b6dfa1b22a99b2c4c57d7ae25dbf7350",
|
|
16
|
+
name: "listProviders",
|
|
17
|
+
filename: "src/lib/pulumi/api.ts"
|
|
18
|
+
}, (opts) => listProviders.__executeServer(opts));
|
|
19
|
+
var listProviders = createServerFn({ method: "GET" }).handler(listProviders_createServerFn_handler, () => loadProjectSummary());
|
|
20
|
+
var getProvider_createServerFn_handler = createServerRpc({
|
|
21
|
+
id: "20c32dac0b2237041a59da458130ed4f320ce92dddd59a28b88581987c58d61d",
|
|
22
|
+
name: "getProvider",
|
|
23
|
+
filename: "src/lib/pulumi/api.ts"
|
|
24
|
+
}, (opts) => getProvider.__executeServer(opts));
|
|
25
|
+
var getProvider = createServerFn({ method: "GET" }).validator((name) => name).handler(getProvider_createServerFn_handler, ({ data: name }) => loadProviderDetail(name));
|
|
26
|
+
//#endregion
|
|
27
|
+
export { getProvider_createServerFn_handler, listProviders_createServerFn_handler };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { n as tokenDisplayName } from "./token-DBEFeZIN.js";
|
|
2
|
+
import { r as Route } from "./router-_zs8P-B2.js";
|
|
3
|
+
import { t as MemberDetail } from "./MemberDetail-CdHUSmQD.js";
|
|
3
4
|
import { jsx } from "react/jsx-runtime";
|
|
4
5
|
//#region src/routes/providers.$name.functions.$token.tsx?tsr-split=component
|
|
5
6
|
function FunctionDetail() {
|
package/dist/server/assets/{providers._name.index-CEXkl-76.js → providers._name.index-DBkfEjsy.js}
RENAMED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { c as resolveDescription, n as tokenDisplayName, r as tokenSlug, s as summarizeDescription } from "./token-DBEFeZIN.js";
|
|
2
|
+
import { i as Route } from "./router-_zs8P-B2.js";
|
|
2
3
|
import { Link } from "@tanstack/react-router";
|
|
3
4
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
5
|
//#region src/routes/providers.$name.index.tsx?tsr-split=component
|
|
@@ -12,7 +13,7 @@ function MemberListItem({ token, member, runtime, to, providerName }) {
|
|
|
12
13
|
to,
|
|
13
14
|
params: {
|
|
14
15
|
name: providerName,
|
|
15
|
-
token
|
|
16
|
+
token: tokenSlug(token)
|
|
16
17
|
},
|
|
17
18
|
children: /* @__PURE__ */ jsx("code", { children: tokenDisplayName(token) })
|
|
18
19
|
}),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { n as tokenDisplayName } from "./token-DBEFeZIN.js";
|
|
2
|
+
import { n as Route } from "./router-_zs8P-B2.js";
|
|
3
|
+
import { t as MemberDetail } from "./MemberDetail-CdHUSmQD.js";
|
|
3
4
|
import { jsx } from "react/jsx-runtime";
|
|
4
5
|
//#region src/routes/providers.$name.resources.$token.tsx?tsr-split=component
|
|
5
6
|
function ResourceDetail() {
|