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
|
@@ -119,4 +119,51 @@ async function resolveProviderSchema(projectRoot, ref) {
|
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
//#endregion
|
|
122
|
-
|
|
122
|
+
//#region src/lib/pulumi/providers.server.ts
|
|
123
|
+
/**
|
|
124
|
+
* The project + provider summaries shown on the index route and by the MCP
|
|
125
|
+
* `list_providers` tool. Plain async functions (no TanStack Start server-fn
|
|
126
|
+
* wrapper) so they can run either inside the web server or in the stdio MCP
|
|
127
|
+
* process, which has no HTTP server at all.
|
|
128
|
+
*/
|
|
129
|
+
async function loadProjectSummary() {
|
|
130
|
+
const targetDir = getTargetDir();
|
|
131
|
+
const project = loadPulumiProject(targetDir);
|
|
132
|
+
if (!project) return {
|
|
133
|
+
targetDir,
|
|
134
|
+
project: null,
|
|
135
|
+
providers: []
|
|
136
|
+
};
|
|
137
|
+
const providers = (await Promise.all(project.packages.map((ref) => resolveProviderSchema(project.root, ref)))).map((entry) => ({
|
|
138
|
+
name: entry.ref.name,
|
|
139
|
+
source: entry.ref.source,
|
|
140
|
+
version: entry.ref.version,
|
|
141
|
+
parameters: entry.ref.parameters,
|
|
142
|
+
resourceCount: Object.keys(entry.schema?.resources ?? {}).length,
|
|
143
|
+
functionCount: Object.keys(entry.schema?.functions ?? {}).length,
|
|
144
|
+
origin: entry.origin,
|
|
145
|
+
error: entry.error
|
|
146
|
+
}));
|
|
147
|
+
return {
|
|
148
|
+
targetDir,
|
|
149
|
+
project: {
|
|
150
|
+
root: project.root,
|
|
151
|
+
name: project.name,
|
|
152
|
+
runtime: project.runtime
|
|
153
|
+
},
|
|
154
|
+
providers
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
/** The fully resolved schema for one local provider, or null if the project or provider isn't found. */
|
|
158
|
+
async function loadProviderDetail(name) {
|
|
159
|
+
const project = loadPulumiProject(getTargetDir());
|
|
160
|
+
if (!project) return null;
|
|
161
|
+
const ref = project.packages.find((p) => p.name === name);
|
|
162
|
+
if (!ref) return null;
|
|
163
|
+
return {
|
|
164
|
+
...await resolveProviderSchema(project.root, ref),
|
|
165
|
+
runtime: project.runtime
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
//#endregion
|
|
169
|
+
export { loadPulumiProject as a, getTargetDir as i, loadProviderDetail as n, resolveProviderSchema as r, loadProjectSummary as t };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as loadPulumiProject, i as getTargetDir, n as loadProviderDetail, r as resolveProviderSchema, t as loadProjectSummary } from "./providers.server-B1ZHZ5jE.js";
|
|
2
2
|
import { n as listProviders, t as getProvider } from "./api-BV6lykkP.js";
|
|
3
|
+
import { a as pickExample, c as resolveDescription, i as extractExamples, n as tokenDisplayName, o as stripExamples, s as summarizeDescription, t as findTokenBySlug } from "./token-DBEFeZIN.js";
|
|
3
4
|
import { useEffect, useState } from "react";
|
|
4
5
|
import { HeadContent, Link, Scripts, createFileRoute, createRootRoute, createRouter, lazyRouteComponent, notFound } from "@tanstack/react-router";
|
|
5
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -328,71 +329,12 @@ function RootDocument({ children }) {
|
|
|
328
329
|
}
|
|
329
330
|
//#endregion
|
|
330
331
|
//#region src/routes/index.tsx
|
|
331
|
-
var $$splitComponentImporter$4 = () => import("./routes-
|
|
332
|
+
var $$splitComponentImporter$4 = () => import("./routes-BUxagg9r.js");
|
|
332
333
|
var Route$5 = createFileRoute("/")({
|
|
333
334
|
loader: () => listProviders(),
|
|
334
335
|
component: lazyRouteComponent($$splitComponentImporter$4, "component")
|
|
335
336
|
});
|
|
336
337
|
//#endregion
|
|
337
|
-
//#region src/lib/pulumi/description.ts
|
|
338
|
-
/**
|
|
339
|
-
* Pulumi schema descriptions embed per-language text as HTML-ish spans, e.g.
|
|
340
|
-
* `<span pulumi-lang-nodejs="`length`" pulumi-lang-python="`length`" ...>`length`</span>`.
|
|
341
|
-
* pulumi.com's docs site picks an attribute based on the selected language tab;
|
|
342
|
-
* we don't have tabs, so we resolve each span to the project's own runtime up
|
|
343
|
-
* front and strip the markup rather than rendering (or leaking) raw HTML.
|
|
344
|
-
*/
|
|
345
|
-
var LANG_SPAN_RE = /<span\s+((?:pulumi-lang-[a-z]+="[^"]*"\s*)+)>([\s\S]*?)<\/span>/g;
|
|
346
|
-
var LANG_ATTR_RE = /pulumi-lang-([a-z]+)="([^"]*)"/g;
|
|
347
|
-
function resolveLangSpans(text, runtime) {
|
|
348
|
-
return text.replace(LANG_SPAN_RE, (_match, attrsSource, fallback) => {
|
|
349
|
-
if (!runtime) return fallback;
|
|
350
|
-
LANG_ATTR_RE.lastIndex = 0;
|
|
351
|
-
let match;
|
|
352
|
-
while (match = LANG_ATTR_RE.exec(attrsSource)) if (match[1] === runtime) return match[2];
|
|
353
|
-
return fallback;
|
|
354
|
-
});
|
|
355
|
-
}
|
|
356
|
-
/** Resolves a schema description's per-language spans for the given Pulumi runtime. */
|
|
357
|
-
function resolveDescription(text, runtime) {
|
|
358
|
-
if (!text) return text;
|
|
359
|
-
return resolveLangSpans(text, runtime);
|
|
360
|
-
}
|
|
361
|
-
//#endregion
|
|
362
|
-
//#region src/lib/pulumi/examples.ts
|
|
363
|
-
var CODE_FENCE_RE = /```([a-zA-Z0-9+#-]+)\n([\s\S]*?)```/g;
|
|
364
|
-
function extractExamples(text) {
|
|
365
|
-
if (!text) return [];
|
|
366
|
-
return [...text.matchAll(CODE_FENCE_RE)].map((match) => ({
|
|
367
|
-
language: match[1].toLowerCase(),
|
|
368
|
-
code: match[2].trimEnd()
|
|
369
|
-
}));
|
|
370
|
-
}
|
|
371
|
-
/** The description text with any fenced code examples removed. */
|
|
372
|
-
function stripExamples(text) {
|
|
373
|
-
if (!text) return text;
|
|
374
|
-
return text.replace(CODE_FENCE_RE, "").trim() || void 0;
|
|
375
|
-
}
|
|
376
|
-
/** A short summary for list views: the first prose paragraph, examples excluded. */
|
|
377
|
-
function summarizeDescription(text) {
|
|
378
|
-
const stripped = stripExamples(text);
|
|
379
|
-
if (!stripped) return void 0;
|
|
380
|
-
return stripped.split(/\n{2,}/)[0]?.trim() || void 0;
|
|
381
|
-
}
|
|
382
|
-
var RUNTIME_LANGUAGES = {
|
|
383
|
-
nodejs: ["typescript", "javascript"],
|
|
384
|
-
python: ["python"],
|
|
385
|
-
go: ["go", "golang"],
|
|
386
|
-
dotnet: ["csharp"],
|
|
387
|
-
java: ["java"],
|
|
388
|
-
yaml: ["yaml"]
|
|
389
|
-
};
|
|
390
|
-
/** Picks the example matching the project's runtime, falling back to the first available one. */
|
|
391
|
-
function pickExample(examples, runtime) {
|
|
392
|
-
const languages = runtime ? RUNTIME_LANGUAGES[runtime] : void 0;
|
|
393
|
-
return (languages ? examples.find((example) => languages.includes(example.language)) : void 0) ?? examples[0];
|
|
394
|
-
}
|
|
395
|
-
//#endregion
|
|
396
338
|
//#region src/lib/pulumi/format.ts
|
|
397
339
|
function formatType(prop) {
|
|
398
340
|
if (!prop) return "any";
|
|
@@ -428,18 +370,6 @@ function formatPropertyName(name, runtime) {
|
|
|
428
370
|
}
|
|
429
371
|
}
|
|
430
372
|
//#endregion
|
|
431
|
-
//#region src/lib/pulumi/token.ts
|
|
432
|
-
/**
|
|
433
|
-
* Schema tokens look like `random:index/randomString:RandomString` (resources)
|
|
434
|
-
* or `random:index/randomInteger:randomInteger` (functions). The segment after
|
|
435
|
-
* the last colon is the name every generated SDK exports for that member, and
|
|
436
|
-
* is unique within the provider, so it makes a readable display name.
|
|
437
|
-
*/
|
|
438
|
-
function tokenDisplayName(token) {
|
|
439
|
-
const short = token.split(":").pop();
|
|
440
|
-
return short && short.length > 0 ? short : token;
|
|
441
|
-
}
|
|
442
|
-
//#endregion
|
|
443
373
|
//#region src/lib/pulumi/mcp.server.ts
|
|
444
374
|
function textResult(data) {
|
|
445
375
|
return { content: [{
|
|
@@ -449,7 +379,7 @@ function textResult(data) {
|
|
|
449
379
|
}
|
|
450
380
|
/** Loads a local provider's resolved schema, throwing a descriptive error a tool call can surface to the client. */
|
|
451
381
|
async function requireProvider(providerName) {
|
|
452
|
-
const detail = await
|
|
382
|
+
const detail = await loadProviderDetail(providerName);
|
|
453
383
|
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.`);
|
|
454
384
|
if (!detail.schema) throw new Error(`Failed to load the schema for provider "${providerName}": ${detail.error ?? "unknown error"}`);
|
|
455
385
|
return {
|
|
@@ -489,8 +419,10 @@ function memberDetail(provider, token, description, runtime, inputs, outputs, de
|
|
|
489
419
|
};
|
|
490
420
|
}
|
|
491
421
|
/**
|
|
492
|
-
* Builds a fresh MCP server with the local-provider-docs tools registered.
|
|
493
|
-
*
|
|
422
|
+
* Builds a fresh MCP server with the local-provider-docs tools registered. Transport
|
|
423
|
+
* agnostic: `src/routes/mcp.ts` calls it once per HTTP request (so concurrent requests
|
|
424
|
+
* never share state), and `src/lib/pulumi/stdio.server.ts` calls it once for the
|
|
425
|
+
* lifetime of a stdio process.
|
|
494
426
|
*/
|
|
495
427
|
function createMcpServer() {
|
|
496
428
|
const server = new McpServer({
|
|
@@ -500,7 +432,7 @@ function createMcpServer() {
|
|
|
500
432
|
server.registerTool("list_providers", {
|
|
501
433
|
title: "List local providers",
|
|
502
434
|
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."
|
|
503
|
-
}, async () => textResult(await
|
|
435
|
+
}, async () => textResult(await loadProjectSummary()));
|
|
504
436
|
server.registerTool("list_resources", {
|
|
505
437
|
title: "List a provider’s resources",
|
|
506
438
|
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.",
|
|
@@ -622,7 +554,7 @@ var Route$3 = createFileRoute("/providers/$name")({ component: lazyRouteComponen
|
|
|
622
554
|
//#endregion
|
|
623
555
|
//#region src/routes/providers.$name.index.tsx
|
|
624
556
|
var $$splitNotFoundComponentImporter$2 = () => import("./providers._name.index-BEwt-7U0.js");
|
|
625
|
-
var $$splitComponentImporter$2 = () => import("./providers._name.index-
|
|
557
|
+
var $$splitComponentImporter$2 = () => import("./providers._name.index-DBkfEjsy.js");
|
|
626
558
|
var Route$2 = createFileRoute("/providers/$name/")({
|
|
627
559
|
loader: async ({ params }) => {
|
|
628
560
|
const entry = await getProvider({ data: params.name });
|
|
@@ -635,15 +567,17 @@ var Route$2 = createFileRoute("/providers/$name/")({
|
|
|
635
567
|
//#endregion
|
|
636
568
|
//#region src/routes/providers.$name.functions.$token.tsx
|
|
637
569
|
var $$splitNotFoundComponentImporter$1 = () => import("./providers._name.functions._token-D8LsoIum.js");
|
|
638
|
-
var $$splitComponentImporter$1 = () => import("./providers._name.functions._token-
|
|
570
|
+
var $$splitComponentImporter$1 = () => import("./providers._name.functions._token-BvkwmQ-L.js");
|
|
639
571
|
var Route$1 = createFileRoute("/providers/$name/functions/$token")({
|
|
640
572
|
loader: async ({ params }) => {
|
|
641
573
|
const entry = await getProvider({ data: params.name });
|
|
642
|
-
const
|
|
643
|
-
|
|
574
|
+
const functions = entry?.schema?.functions;
|
|
575
|
+
const token = functions && findTokenBySlug(Object.keys(functions), params.token);
|
|
576
|
+
const fn = token ? functions[token] : void 0;
|
|
577
|
+
if (!token || !fn) throw notFound();
|
|
644
578
|
return {
|
|
645
579
|
providerName: params.name,
|
|
646
|
-
token
|
|
580
|
+
token,
|
|
647
581
|
fn,
|
|
648
582
|
runtime: entry?.runtime
|
|
649
583
|
};
|
|
@@ -654,15 +588,17 @@ var Route$1 = createFileRoute("/providers/$name/functions/$token")({
|
|
|
654
588
|
//#endregion
|
|
655
589
|
//#region src/routes/providers.$name.resources.$token.tsx
|
|
656
590
|
var $$splitNotFoundComponentImporter = () => import("./providers._name.resources._token-DJPF8nMg.js");
|
|
657
|
-
var $$splitComponentImporter = () => import("./providers._name.resources._token-
|
|
591
|
+
var $$splitComponentImporter = () => import("./providers._name.resources._token-Sl31Qlvd.js");
|
|
658
592
|
var Route = createFileRoute("/providers/$name/resources/$token")({
|
|
659
593
|
loader: async ({ params }) => {
|
|
660
594
|
const entry = await getProvider({ data: params.name });
|
|
661
|
-
const
|
|
662
|
-
|
|
595
|
+
const resources = entry?.schema?.resources;
|
|
596
|
+
const token = resources && findTokenBySlug(Object.keys(resources), params.token);
|
|
597
|
+
const resource = token ? resources[token] : void 0;
|
|
598
|
+
if (!token || !resource) throw notFound();
|
|
663
599
|
return {
|
|
664
600
|
providerName: params.name,
|
|
665
|
-
token
|
|
601
|
+
token,
|
|
666
602
|
resource,
|
|
667
603
|
runtime: entry?.runtime
|
|
668
604
|
};
|
|
@@ -716,10 +652,11 @@ var router_exports = /* @__PURE__ */ __exportAll({ getRouter: () => getRouter })
|
|
|
716
652
|
function getRouter() {
|
|
717
653
|
return createRouter({
|
|
718
654
|
routeTree,
|
|
655
|
+
basepath: "/",
|
|
719
656
|
scrollRestoration: true,
|
|
720
657
|
defaultPreload: "intent",
|
|
721
658
|
defaultPreloadStaleTime: 0
|
|
722
659
|
});
|
|
723
660
|
}
|
|
724
661
|
//#endregion
|
|
725
|
-
export {
|
|
662
|
+
export { formatPropertyName as a, getRouter, Route$2 as i, Route as n, formatType as o, Route$1 as r, Route$5 as s, router_exports as t };
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
//#region src/lib/pulumi/description.ts
|
|
2
|
+
/**
|
|
3
|
+
* Pulumi schema descriptions embed per-language text as HTML-ish spans, e.g.
|
|
4
|
+
* `<span pulumi-lang-nodejs="`length`" pulumi-lang-python="`length`" ...>`length`</span>`.
|
|
5
|
+
* pulumi.com's docs site picks an attribute based on the selected language tab;
|
|
6
|
+
* we don't have tabs, so we resolve each span to the project's own runtime up
|
|
7
|
+
* front and strip the markup rather than rendering (or leaking) raw HTML.
|
|
8
|
+
*/
|
|
9
|
+
var LANG_SPAN_RE = /<span\s+((?:pulumi-lang-[a-z]+="[^"]*"\s*)+)>([\s\S]*?)<\/span>/g;
|
|
10
|
+
var LANG_ATTR_RE = /pulumi-lang-([a-z]+)="([^"]*)"/g;
|
|
11
|
+
function resolveLangSpans(text, runtime) {
|
|
12
|
+
return text.replace(LANG_SPAN_RE, (_match, attrsSource, fallback) => {
|
|
13
|
+
if (!runtime) return fallback;
|
|
14
|
+
LANG_ATTR_RE.lastIndex = 0;
|
|
15
|
+
let match;
|
|
16
|
+
while (match = LANG_ATTR_RE.exec(attrsSource)) if (match[1] === runtime) return match[2];
|
|
17
|
+
return fallback;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
/** Resolves a schema description's per-language spans for the given Pulumi runtime. */
|
|
21
|
+
function resolveDescription(text, runtime) {
|
|
22
|
+
if (!text) return text;
|
|
23
|
+
return resolveLangSpans(text, runtime);
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region src/lib/pulumi/examples.ts
|
|
27
|
+
var CODE_FENCE_RE = /```([a-zA-Z0-9+#-]+)\n([\s\S]*?)```/g;
|
|
28
|
+
function extractExamples(text) {
|
|
29
|
+
if (!text) return [];
|
|
30
|
+
return [...text.matchAll(CODE_FENCE_RE)].map((match) => ({
|
|
31
|
+
language: match[1].toLowerCase(),
|
|
32
|
+
code: match[2].trimEnd()
|
|
33
|
+
}));
|
|
34
|
+
}
|
|
35
|
+
/** The description text with any fenced code examples removed. */
|
|
36
|
+
function stripExamples(text) {
|
|
37
|
+
if (!text) return text;
|
|
38
|
+
return text.replace(CODE_FENCE_RE, "").trim() || void 0;
|
|
39
|
+
}
|
|
40
|
+
/** A short summary for list views: the first prose paragraph, examples excluded. */
|
|
41
|
+
function summarizeDescription(text) {
|
|
42
|
+
const stripped = stripExamples(text);
|
|
43
|
+
if (!stripped) return void 0;
|
|
44
|
+
return stripped.split(/\n{2,}/)[0]?.trim() || void 0;
|
|
45
|
+
}
|
|
46
|
+
var RUNTIME_LANGUAGES = {
|
|
47
|
+
nodejs: ["typescript", "javascript"],
|
|
48
|
+
python: ["python"],
|
|
49
|
+
go: ["go", "golang"],
|
|
50
|
+
dotnet: ["csharp"],
|
|
51
|
+
java: ["java"],
|
|
52
|
+
yaml: ["yaml"]
|
|
53
|
+
};
|
|
54
|
+
/** Picks the example matching the project's runtime, falling back to the first available one. */
|
|
55
|
+
function pickExample(examples, runtime) {
|
|
56
|
+
const languages = runtime ? RUNTIME_LANGUAGES[runtime] : void 0;
|
|
57
|
+
return (languages ? examples.find((example) => languages.includes(example.language)) : void 0) ?? examples[0];
|
|
58
|
+
}
|
|
59
|
+
//#endregion
|
|
60
|
+
//#region src/lib/pulumi/token.ts
|
|
61
|
+
/**
|
|
62
|
+
* Schema tokens look like `random:index/randomString:RandomString` (resources)
|
|
63
|
+
* or `random:index/randomInteger:randomInteger` (functions). The segment after
|
|
64
|
+
* the last colon is the name every generated SDK exports for that member, and
|
|
65
|
+
* is unique within the provider, so it makes a readable display name.
|
|
66
|
+
*/
|
|
67
|
+
function tokenDisplayName(token) {
|
|
68
|
+
const short = token.split(":").pop();
|
|
69
|
+
return short && short.length > 0 ? short : token;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* A URL- and filesystem-safe form of a token, used as the `$token` route param.
|
|
73
|
+
*
|
|
74
|
+
* Tokens contain `:` and `/`, which only survive a URL as percent-escapes —
|
|
75
|
+
* and static hosts (GitHub/GitLab Pages) decode those before matching a file,
|
|
76
|
+
* so `random%3Aindex%2FrandomString%3ARandomString` would never resolve to the
|
|
77
|
+
* page generated for it. Replacing the separators keeps one URL shape that
|
|
78
|
+
* works both when served dynamically and as prerendered files on disk.
|
|
79
|
+
*/
|
|
80
|
+
function tokenSlug(token) {
|
|
81
|
+
return token.replace(/[^a-zA-Z0-9._~-]+/g, "-");
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Resolves a `$token` route param back to the schema token it names. Accepts
|
|
85
|
+
* either a slug (what links produce) or the raw token, so a URL pasted from
|
|
86
|
+
* schema output still works.
|
|
87
|
+
*/
|
|
88
|
+
function findTokenBySlug(tokens, slug) {
|
|
89
|
+
for (const token of tokens) if (token === slug || tokenSlug(token) === slug) return token;
|
|
90
|
+
}
|
|
91
|
+
//#endregion
|
|
92
|
+
export { pickExample as a, resolveDescription as c, extractExamples as i, tokenDisplayName as n, stripExamples as o, tokenSlug as r, summarizeDescription as s, findTokenBySlug as t };
|
package/dist/server/server.js
CHANGED
|
@@ -91,7 +91,7 @@ var HEADERS = { TSS_SHELL: "X-TSS_SHELL" };
|
|
|
91
91
|
* the dev styles URL for route-scoped CSS collection.
|
|
92
92
|
*/
|
|
93
93
|
async function getStartManifest(matchedRoutes) {
|
|
94
|
-
const { tsrStartManifest } = await import("./assets/_tanstack-start-manifest_v-
|
|
94
|
+
const { tsrStartManifest } = await import("./assets/_tanstack-start-manifest_v-DNIp6gp3.js");
|
|
95
95
|
const startManifest = tsrStartManifest();
|
|
96
96
|
let routes = startManifest.routes;
|
|
97
97
|
routes[rootRouteId];
|
|
@@ -115,11 +115,11 @@ async function getStartManifest(matchedRoutes) {
|
|
|
115
115
|
var manifest = {
|
|
116
116
|
"20c32dac0b2237041a59da458130ed4f320ce92dddd59a28b88581987c58d61d": {
|
|
117
117
|
functionName: "getProvider_createServerFn_handler",
|
|
118
|
-
importer: () => import("./assets/api-
|
|
118
|
+
importer: () => import("./assets/api-D49ZDhMh.js")
|
|
119
119
|
},
|
|
120
120
|
"a3ba03544bae9a67523c84875835e6e9b6dfa1b22a99b2c4c57d7ae25dbf7350": {
|
|
121
121
|
functionName: "listProviders_createServerFn_handler",
|
|
122
|
-
importer: () => import("./assets/api-
|
|
122
|
+
importer: () => import("./assets/api-D49ZDhMh.js")
|
|
123
123
|
}
|
|
124
124
|
};
|
|
125
125
|
async function getServerFnById(id, access) {
|
|
@@ -1547,7 +1547,7 @@ var getBaseManifest = getProdBaseManifest;
|
|
|
1547
1547
|
var createEarlyHintsForRequest = createEarlyHintsCollector;
|
|
1548
1548
|
async function loadEntries() {
|
|
1549
1549
|
const [routerEntry, startEntry, pluginAdapters] = await Promise.all([
|
|
1550
|
-
import("./assets/router-
|
|
1550
|
+
import("./assets/router-_zs8P-B2.js").then((n) => n.t),
|
|
1551
1551
|
import("./assets/start-5Z2QO8AU.js"),
|
|
1552
1552
|
import("./assets/empty-plugin-adapters-D9UWiqvJ.js")
|
|
1553
1553
|
]);
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Publishes the local provider docs for this Pulumi project to GitHub Pages.
|
|
2
|
+
# Copy to .github/workflows/docs.yml in your Pulumi project's repo, then enable
|
|
3
|
+
# Pages for the repo with "Source: GitHub Actions" (Settings -> Pages).
|
|
4
|
+
name: Docs
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
branches: [main]
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
pages: write
|
|
14
|
+
id-token: write
|
|
15
|
+
|
|
16
|
+
# One deployment at a time; don't cancel one that's already publishing.
|
|
17
|
+
concurrency:
|
|
18
|
+
group: pages
|
|
19
|
+
cancel-in-progress: false
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
build:
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v7
|
|
26
|
+
|
|
27
|
+
- uses: actions/setup-node@v7
|
|
28
|
+
with:
|
|
29
|
+
node-version: 24
|
|
30
|
+
|
|
31
|
+
# Puts the `pulumi` CLI on PATH; the docs are generated from the schemas
|
|
32
|
+
# it fetches for each package in Pulumi.yaml.
|
|
33
|
+
- uses: pulumi/actions@v7
|
|
34
|
+
|
|
35
|
+
# A project site is served from https://<owner>.github.io/<repo>/, so the
|
|
36
|
+
# site is built for that path. For a user/org site (<owner>.github.io),
|
|
37
|
+
# drop --base.
|
|
38
|
+
- name: Generate docs
|
|
39
|
+
run: npx -y pulumi-package-docs static --out ./site --base "/${GITHUB_REPOSITORY#*/}/"
|
|
40
|
+
|
|
41
|
+
- uses: actions/upload-pages-artifact@v5
|
|
42
|
+
with:
|
|
43
|
+
path: ./site
|
|
44
|
+
# The generated site includes a `.nojekyll` file, and this action
|
|
45
|
+
# drops dotfiles unless asked not to.
|
|
46
|
+
include-hidden-files: true
|
|
47
|
+
|
|
48
|
+
deploy:
|
|
49
|
+
needs: build
|
|
50
|
+
runs-on: ubuntu-latest
|
|
51
|
+
environment:
|
|
52
|
+
name: github-pages
|
|
53
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
54
|
+
steps:
|
|
55
|
+
- id: deployment
|
|
56
|
+
uses: actions/deploy-pages@v5
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Publishes the local provider docs for this Pulumi project to GitLab Pages.
|
|
2
|
+
# Copy this job into your Pulumi project's .gitlab-ci.yml.
|
|
3
|
+
pages:
|
|
4
|
+
image: node:24
|
|
5
|
+
stage: deploy
|
|
6
|
+
before_script:
|
|
7
|
+
# Puts the `pulumi` CLI on PATH; the docs are generated from the schemas
|
|
8
|
+
# it fetches for each package in Pulumi.yaml.
|
|
9
|
+
- curl -fsSL https://get.pulumi.com | sh
|
|
10
|
+
- export PATH="$PATH:$HOME/.pulumi/bin"
|
|
11
|
+
script:
|
|
12
|
+
# CI_PAGES_URL is where this site will be served from — its path is what
|
|
13
|
+
# the docs have to be built for ("/" with unique domains enabled, "/<project>/"
|
|
14
|
+
# otherwise).
|
|
15
|
+
- export DOCS_BASE="$(node -e 'console.log(new URL(process.env.CI_PAGES_URL).pathname)')"
|
|
16
|
+
- npx -y pulumi-package-docs static --out public --base "$DOCS_BASE"
|
|
17
|
+
artifacts:
|
|
18
|
+
paths:
|
|
19
|
+
# GitLab Pages publishes this directory, and only under this name.
|
|
20
|
+
- public
|
|
21
|
+
rules:
|
|
22
|
+
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pulumi-package-docs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Local documentation viewer for the parameterized/local Pulumi providers used by a Pulumi project.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"files": [
|
|
19
19
|
"bin",
|
|
20
20
|
"dist",
|
|
21
|
+
"examples",
|
|
21
22
|
"src",
|
|
22
23
|
"tsconfig.json",
|
|
23
24
|
"vite.config.ts"
|
|
@@ -28,11 +29,14 @@
|
|
|
28
29
|
"scripts": {
|
|
29
30
|
"dev": "vite dev --port 3000",
|
|
30
31
|
"generate-routes": "tsr generate",
|
|
31
|
-
"build": "vite build",
|
|
32
|
+
"build": "vite build && npm run build:mcp",
|
|
33
|
+
"build:mcp": "vite build --config vite.mcp.config.ts",
|
|
32
34
|
"preview": "vite preview",
|
|
33
35
|
"start": "node bin/cli.js",
|
|
36
|
+
"static": "node bin/cli.js static",
|
|
34
37
|
"compile:bun": "node bin/generate-embedded-assets.js && bun build ./bin/cli-bun.js --compile --outfile pulumi-package-docs",
|
|
35
38
|
"test": "vitest run",
|
|
39
|
+
"test:stdio": "node scripts/smoke-test-stdio.js node bin/cli.js --stdio && node scripts/smoke-test-stdio.js ./pulumi-package-docs --stdio",
|
|
36
40
|
"test:watch": "vitest",
|
|
37
41
|
"lint": "oxlint",
|
|
38
42
|
"format": "oxfmt . && oxlint --fix",
|
|
@@ -119,3 +119,40 @@ function MonitorIcon() {
|
|
|
119
119
|
</IconSvg>
|
|
120
120
|
)
|
|
121
121
|
}
|
|
122
|
+
|
|
123
|
+
// Wires the static build's theme switch up without hydration: one delegated
|
|
124
|
+
// click listener, using the same `data-theme` + localStorage contract as
|
|
125
|
+
// ThemeToggle above (and as the pre-hydration script in `__root.tsx`).
|
|
126
|
+
const staticThemeScript = `(function(){var g=document.currentScript.previousElementSibling;if(!g)return;
|
|
127
|
+
function apply(m){try{if(m==='system'){delete document.documentElement.dataset.theme;localStorage.removeItem('theme')}else{document.documentElement.dataset.theme=m;localStorage.setItem('theme',m)}}catch(e){if(m==='system'){delete document.documentElement.dataset.theme}else{document.documentElement.dataset.theme=m}}sync(m)}
|
|
128
|
+
function sync(m){var b=g.querySelectorAll('button[data-theme-mode]');for(var i=0;i<b.length;i++){b[i].setAttribute('aria-pressed',String(b[i].getAttribute('data-theme-mode')===m))}}
|
|
129
|
+
g.addEventListener('click',function(e){var b=e.target.closest('button[data-theme-mode]');if(b){apply(b.getAttribute('data-theme-mode'))}});
|
|
130
|
+
var s=null;try{s=localStorage.getItem('theme')}catch(e){}
|
|
131
|
+
sync(s==='light'||s==='dark'?s:'system')})()`
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* The theme switch for the static build. Same markup as `ThemeToggle`, but
|
|
135
|
+
* driven by an inline script rather than React, since static pages ship
|
|
136
|
+
* without the hydration bundle.
|
|
137
|
+
*/
|
|
138
|
+
export function StaticThemeToggle() {
|
|
139
|
+
return (
|
|
140
|
+
<>
|
|
141
|
+
<div className="theme-switch" role="group" aria-label="Theme">
|
|
142
|
+
{OPTIONS.map(({ mode, label, Icon }) => (
|
|
143
|
+
<button
|
|
144
|
+
key={mode}
|
|
145
|
+
type="button"
|
|
146
|
+
className="theme-switch-option"
|
|
147
|
+
data-theme-mode={mode}
|
|
148
|
+
aria-pressed={mode === 'system'}
|
|
149
|
+
aria-label={label}
|
|
150
|
+
>
|
|
151
|
+
<Icon />
|
|
152
|
+
</button>
|
|
153
|
+
))}
|
|
154
|
+
</div>
|
|
155
|
+
<script dangerouslySetInnerHTML={{ __html: staticThemeScript }} />
|
|
156
|
+
</>
|
|
157
|
+
)
|
|
158
|
+
}
|
package/src/lib/pulumi/api.ts
CHANGED
|
@@ -1,77 +1,26 @@
|
|
|
1
1
|
import { createServerFn } from '@tanstack/react-start'
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface ProjectSummary {
|
|
19
|
-
targetDir: string
|
|
20
|
-
project: Pick<PulumiProject, 'root' | 'name' | 'runtime'> | null
|
|
21
|
-
providers: ProviderSummary[]
|
|
22
|
-
}
|
|
3
|
+
import {
|
|
4
|
+
loadProjectSummary,
|
|
5
|
+
loadProviderDetail,
|
|
6
|
+
} from '@/lib/pulumi/providers.server'
|
|
7
|
+
import type {
|
|
8
|
+
ProjectSummary,
|
|
9
|
+
ProviderDetail,
|
|
10
|
+
} from '@/lib/pulumi/providers.server'
|
|
11
|
+
|
|
12
|
+
export type {
|
|
13
|
+
ProjectSummary,
|
|
14
|
+
ProviderDetail,
|
|
15
|
+
ProviderSummary,
|
|
16
|
+
} from '@/lib/pulumi/providers.server'
|
|
23
17
|
|
|
24
18
|
export const listProviders = createServerFn({ method: 'GET' }).handler(
|
|
25
|
-
|
|
26
|
-
const targetDir = getTargetDir()
|
|
27
|
-
const project = loadPulumiProject(targetDir)
|
|
28
|
-
|
|
29
|
-
if (!project) {
|
|
30
|
-
return { targetDir, project: null, providers: [] }
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const entries = await Promise.all(
|
|
34
|
-
project.packages.map((ref) => resolveProviderSchema(project.root, ref)),
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
const providers: ProviderSummary[] = entries.map((entry) => ({
|
|
38
|
-
name: entry.ref.name,
|
|
39
|
-
source: entry.ref.source,
|
|
40
|
-
version: entry.ref.version,
|
|
41
|
-
parameters: entry.ref.parameters,
|
|
42
|
-
resourceCount: Object.keys(entry.schema?.resources ?? {}).length,
|
|
43
|
-
functionCount: Object.keys(entry.schema?.functions ?? {}).length,
|
|
44
|
-
origin: entry.origin,
|
|
45
|
-
error: entry.error,
|
|
46
|
-
}))
|
|
47
|
-
|
|
48
|
-
return {
|
|
49
|
-
targetDir,
|
|
50
|
-
project: {
|
|
51
|
-
root: project.root,
|
|
52
|
-
name: project.name,
|
|
53
|
-
runtime: project.runtime,
|
|
54
|
-
},
|
|
55
|
-
providers,
|
|
56
|
-
}
|
|
57
|
-
},
|
|
19
|
+
(): Promise<ProjectSummary> => loadProjectSummary(),
|
|
58
20
|
)
|
|
59
21
|
|
|
60
|
-
export interface ProviderDetail extends ProviderEntry {
|
|
61
|
-
/** The Pulumi project's runtime (e.g. "nodejs", "python", "go"), used to render Inputs/Outputs names in that language's casing. */
|
|
62
|
-
runtime?: string
|
|
63
|
-
}
|
|
64
|
-
|
|
65
22
|
export const getProvider = createServerFn({ method: 'GET' })
|
|
66
23
|
.validator((name: string) => name)
|
|
67
|
-
.handler(
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (!project) return null
|
|
71
|
-
|
|
72
|
-
const ref = project.packages.find((p) => p.name === name)
|
|
73
|
-
if (!ref) return null
|
|
74
|
-
|
|
75
|
-
const entry = await resolveProviderSchema(project.root, ref)
|
|
76
|
-
return { ...entry, runtime: project.runtime }
|
|
77
|
-
})
|
|
24
|
+
.handler(({ data: name }): Promise<ProviderDetail | null> =>
|
|
25
|
+
loadProviderDetail(name),
|
|
26
|
+
)
|