pulumi-package-docs 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +60 -0
- package/bin/cli.js +80 -0
- package/dist/client/assets/MemberDetail-CX9jiyic.js +1 -0
- package/dist/client/assets/index-CZRUHwIq.js +13 -0
- package/dist/client/assets/providers._name-DpU324kz.js +1 -0
- package/dist/client/assets/providers._name.functions._token-DXrP4mSE.js +1 -0
- package/dist/client/assets/providers._name.functions._token-bvc3vrG2.js +1 -0
- package/dist/client/assets/providers._name.index-CtKE1x7S.js +1 -0
- package/dist/client/assets/providers._name.index-tTXQzRr7.js +1 -0
- package/dist/client/assets/providers._name.resources._token--BZM8RBv.js +1 -0
- package/dist/client/assets/providers._name.resources._token-CbZkRkaE.js +1 -0
- package/dist/client/assets/routes-GAgiQcBX.js +1 -0
- package/dist/client/assets/styles-CWDprFhI.css +1 -0
- package/dist/client/assets/token-oQmZfAP3.js +1 -0
- package/dist/client/assets/useSelector-BIt42tGI.js +1 -0
- package/dist/server/assets/MemberDetail-BiBAI1vw.js +96 -0
- package/dist/server/assets/_tanstack-start-manifest_v-Cl1i6HDY.js +62 -0
- package/dist/server/assets/api-BV6lykkP.js +20 -0
- package/dist/server/assets/api-C55bS4R6.js +64 -0
- package/dist/server/assets/empty-plugin-adapters-D9UWiqvJ.js +5 -0
- package/dist/server/assets/providers._name-DF6sA3DG.js +6 -0
- package/dist/server/assets/providers._name.functions._token-C0A7Y90t.js +21 -0
- package/dist/server/assets/providers._name.functions._token-D8LsoIum.js +13 -0
- package/dist/server/assets/providers._name.index-BEwt-7U0.js +13 -0
- package/dist/server/assets/providers._name.index-BK_ZRd5l.js +97 -0
- package/dist/server/assets/providers._name.resources._token-CbvRkms4.js +22 -0
- package/dist/server/assets/providers._name.resources._token-DJPF8nMg.js +13 -0
- package/dist/server/assets/router-DZSocx9m.js +562 -0
- package/dist/server/assets/routes-B-G_iLTo.js +82 -0
- package/dist/server/assets/schema.server-B-VvU1qb.js +122 -0
- package/dist/server/assets/start-5Z2QO8AU.js +4 -0
- package/dist/server/server.js +2003 -0
- package/package.json +67 -0
- package/src/components/CodeExample.tsx +14 -0
- package/src/components/MemberDetail.tsx +89 -0
- package/src/components/PropertyTable.tsx +49 -0
- package/src/components/PulumiLogo.tsx +86 -0
- package/src/lib/pulumi/api.ts +77 -0
- package/src/lib/pulumi/description.test.ts +54 -0
- package/src/lib/pulumi/description.ts +35 -0
- package/src/lib/pulumi/discovery.server.test.ts +117 -0
- package/src/lib/pulumi/discovery.server.ts +77 -0
- package/src/lib/pulumi/examples.test.ts +97 -0
- package/src/lib/pulumi/examples.ts +55 -0
- package/src/lib/pulumi/format.test.ts +51 -0
- package/src/lib/pulumi/format.ts +11 -0
- package/src/lib/pulumi/mcp.server.ts +343 -0
- package/src/lib/pulumi/naming.test.ts +27 -0
- package/src/lib/pulumi/naming.ts +31 -0
- package/src/lib/pulumi/schema.server.ts +78 -0
- package/src/lib/pulumi/token.test.ts +21 -0
- package/src/lib/pulumi/token.ts +10 -0
- package/src/lib/pulumi/types.ts +62 -0
- package/src/routeTree.gen.ts +187 -0
- package/src/router.tsx +19 -0
- package/src/routes/__root.tsx +59 -0
- package/src/routes/index.tsx +78 -0
- package/src/routes/mcp.ts +30 -0
- package/src/routes/providers.$name.functions.$token.tsx +47 -0
- package/src/routes/providers.$name.index.tsx +129 -0
- package/src/routes/providers.$name.resources.$token.tsx +48 -0
- package/src/routes/providers.$name.tsx +5 -0
- package/src/styles.css +478 -0
- package/tsconfig.json +29 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process'
|
|
2
|
+
import { createHash } from 'node:crypto'
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
4
|
+
import { tmpdir } from 'node:os'
|
|
5
|
+
import { join } from 'node:path'
|
|
6
|
+
import { promisify } from 'node:util'
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
LocalPackageRef,
|
|
10
|
+
ProviderEntry,
|
|
11
|
+
PulumiSchema,
|
|
12
|
+
} from '@/lib/pulumi/types'
|
|
13
|
+
|
|
14
|
+
const execFileAsync = promisify(execFile)
|
|
15
|
+
|
|
16
|
+
const CLI_TIMEOUT_MS = 60_000
|
|
17
|
+
const MAX_BUFFER_BYTES = 64 * 1024 * 1024
|
|
18
|
+
|
|
19
|
+
function cacheFilePath(projectRoot: string, ref: LocalPackageRef): string {
|
|
20
|
+
const projectHash = createHash('sha256')
|
|
21
|
+
.update(projectRoot)
|
|
22
|
+
.digest('hex')
|
|
23
|
+
.slice(0, 16)
|
|
24
|
+
const dir = join(tmpdir(), 'pulumi-package-docs', projectHash)
|
|
25
|
+
mkdirSync(dir, { recursive: true })
|
|
26
|
+
return join(dir, `${ref.name}.json`)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function readCache(path: string): PulumiSchema | null {
|
|
30
|
+
if (!existsSync(path)) return null
|
|
31
|
+
try {
|
|
32
|
+
return JSON.parse(readFileSync(path, 'utf-8')) as PulumiSchema
|
|
33
|
+
} catch {
|
|
34
|
+
return null
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function fetchViaCli(
|
|
39
|
+
projectRoot: string,
|
|
40
|
+
ref: LocalPackageRef,
|
|
41
|
+
): Promise<PulumiSchema> {
|
|
42
|
+
const args = ['package', 'get-schema', ref.source, ...(ref.parameters ?? [])]
|
|
43
|
+
const { stdout } = await execFileAsync('pulumi', args, {
|
|
44
|
+
cwd: projectRoot,
|
|
45
|
+
timeout: CLI_TIMEOUT_MS,
|
|
46
|
+
maxBuffer: MAX_BUFFER_BYTES,
|
|
47
|
+
})
|
|
48
|
+
return JSON.parse(stdout) as PulumiSchema
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Resolves the schema for a local package: try the `pulumi` CLI first (it's the
|
|
53
|
+
* source of truth and works for parameterized providers), and fall back to the
|
|
54
|
+
* last schema we successfully cached for this project if the CLI call fails.
|
|
55
|
+
*/
|
|
56
|
+
export async function resolveProviderSchema(
|
|
57
|
+
projectRoot: string,
|
|
58
|
+
ref: LocalPackageRef,
|
|
59
|
+
): Promise<ProviderEntry> {
|
|
60
|
+
const cachePath = cacheFilePath(projectRoot, ref)
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
const schema = await fetchViaCli(projectRoot, ref)
|
|
64
|
+
writeFileSync(cachePath, JSON.stringify(schema))
|
|
65
|
+
return { ref, schema, origin: 'cli' }
|
|
66
|
+
} catch (cliError) {
|
|
67
|
+
const cached = readCache(cachePath)
|
|
68
|
+
if (cached) {
|
|
69
|
+
return { ref, schema: cached, origin: 'cache' }
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
ref,
|
|
73
|
+
schema: null,
|
|
74
|
+
origin: 'error',
|
|
75
|
+
error: cliError instanceof Error ? cliError.message : String(cliError),
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { tokenDisplayName } from '@/lib/pulumi/token'
|
|
4
|
+
|
|
5
|
+
describe('tokenDisplayName', () => {
|
|
6
|
+
it('returns the segment after the last colon for a resource token', () => {
|
|
7
|
+
expect(tokenDisplayName('random:index/randomString:RandomString')).toBe(
|
|
8
|
+
'RandomString',
|
|
9
|
+
)
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
it('returns the segment after the last colon for a function token', () => {
|
|
13
|
+
expect(
|
|
14
|
+
tokenDisplayName('random:index/randomInteger:getRandomInteger'),
|
|
15
|
+
).toBe('getRandomInteger')
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('returns the token unchanged when it has no colon', () => {
|
|
19
|
+
expect(tokenDisplayName('plainName')).toBe('plainName')
|
|
20
|
+
})
|
|
21
|
+
})
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema tokens look like `random:index/randomString:RandomString` (resources)
|
|
3
|
+
* or `random:index/randomInteger:randomInteger` (functions). The segment after
|
|
4
|
+
* the last colon is the name every generated SDK exports for that member, and
|
|
5
|
+
* is unique within the provider, so it makes a readable display name.
|
|
6
|
+
*/
|
|
7
|
+
export function tokenDisplayName(token: string): string {
|
|
8
|
+
const short = token.split(':').pop()
|
|
9
|
+
return short && short.length > 0 ? short : token
|
|
10
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/** A locally-added package entry from a project's Pulumi.yaml `packages` section. */
|
|
2
|
+
export interface LocalPackageRef {
|
|
3
|
+
/** The local alias the project uses for this package, e.g. "random". */
|
|
4
|
+
name: string
|
|
5
|
+
/** The base provider/plugin this package resolves through, e.g. "terraform-provider". */
|
|
6
|
+
source: string
|
|
7
|
+
version?: string
|
|
8
|
+
/** Parameterization arguments, e.g. ["hashicorp/random"] for a bridged Terraform provider. */
|
|
9
|
+
parameters?: string[]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface PulumiProject {
|
|
13
|
+
root: string
|
|
14
|
+
name?: string
|
|
15
|
+
runtime?: string
|
|
16
|
+
packages: LocalPackageRef[]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface SchemaProperty {
|
|
20
|
+
type?: string
|
|
21
|
+
$ref?: string
|
|
22
|
+
description?: string
|
|
23
|
+
items?: SchemaProperty
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface SchemaResource {
|
|
27
|
+
description?: string
|
|
28
|
+
properties?: Record<string, SchemaProperty>
|
|
29
|
+
required?: string[]
|
|
30
|
+
inputProperties?: Record<string, SchemaProperty>
|
|
31
|
+
requiredInputs?: string[]
|
|
32
|
+
deprecationMessage?: string
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface SchemaFunction {
|
|
36
|
+
description?: string
|
|
37
|
+
inputs?: {
|
|
38
|
+
properties?: Record<string, SchemaProperty>
|
|
39
|
+
required?: string[]
|
|
40
|
+
}
|
|
41
|
+
outputs?: {
|
|
42
|
+
properties?: Record<string, SchemaProperty>
|
|
43
|
+
required?: string[]
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface PulumiSchema {
|
|
48
|
+
name: string
|
|
49
|
+
version?: string
|
|
50
|
+
description?: string
|
|
51
|
+
resources?: Record<string, SchemaResource>
|
|
52
|
+
functions?: Record<string, SchemaFunction>
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export type SchemaOrigin = 'cli' | 'cache'
|
|
56
|
+
|
|
57
|
+
export interface ProviderEntry {
|
|
58
|
+
ref: LocalPackageRef
|
|
59
|
+
schema: PulumiSchema | null
|
|
60
|
+
origin: SchemaOrigin | 'error'
|
|
61
|
+
error?: string
|
|
62
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
|
|
3
|
+
// @ts-nocheck
|
|
4
|
+
|
|
5
|
+
// noinspection JSUnusedGlobalSymbols
|
|
6
|
+
|
|
7
|
+
// This file was automatically generated by TanStack Router.
|
|
8
|
+
// You should NOT make any changes in this file as it will be overwritten.
|
|
9
|
+
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
|
|
10
|
+
|
|
11
|
+
import { Route as rootRouteImport } from './routes/__root'
|
|
12
|
+
import { Route as IndexRouteImport } from './routes/index'
|
|
13
|
+
import { Route as McpRouteImport } from './routes/mcp'
|
|
14
|
+
import { Route as ProvidersNameRouteImport } from './routes/providers.$name'
|
|
15
|
+
import { Route as ProvidersNameIndexRouteImport } from './routes/providers.$name.index'
|
|
16
|
+
import { Route as ProvidersNameFunctionsTokenRouteImport } from './routes/providers.$name.functions.$token'
|
|
17
|
+
import { Route as ProvidersNameResourcesTokenRouteImport } from './routes/providers.$name.resources.$token'
|
|
18
|
+
|
|
19
|
+
const IndexRoute = IndexRouteImport.update({
|
|
20
|
+
id: '/',
|
|
21
|
+
path: '/',
|
|
22
|
+
getParentRoute: () => rootRouteImport,
|
|
23
|
+
} as any)
|
|
24
|
+
const McpRoute = McpRouteImport.update({
|
|
25
|
+
id: '/mcp',
|
|
26
|
+
path: '/mcp',
|
|
27
|
+
getParentRoute: () => rootRouteImport,
|
|
28
|
+
} as any)
|
|
29
|
+
const ProvidersNameRoute = ProvidersNameRouteImport.update({
|
|
30
|
+
id: '/providers/$name',
|
|
31
|
+
path: '/providers/$name',
|
|
32
|
+
getParentRoute: () => rootRouteImport,
|
|
33
|
+
} as any)
|
|
34
|
+
const ProvidersNameIndexRoute = ProvidersNameIndexRouteImport.update({
|
|
35
|
+
id: '/',
|
|
36
|
+
path: '/',
|
|
37
|
+
getParentRoute: () => ProvidersNameRoute,
|
|
38
|
+
} as any)
|
|
39
|
+
const ProvidersNameFunctionsTokenRoute =
|
|
40
|
+
ProvidersNameFunctionsTokenRouteImport.update({
|
|
41
|
+
id: '/functions/$token',
|
|
42
|
+
path: '/functions/$token',
|
|
43
|
+
getParentRoute: () => ProvidersNameRoute,
|
|
44
|
+
} as any)
|
|
45
|
+
const ProvidersNameResourcesTokenRoute =
|
|
46
|
+
ProvidersNameResourcesTokenRouteImport.update({
|
|
47
|
+
id: '/resources/$token',
|
|
48
|
+
path: '/resources/$token',
|
|
49
|
+
getParentRoute: () => ProvidersNameRoute,
|
|
50
|
+
} as any)
|
|
51
|
+
|
|
52
|
+
export interface FileRoutesByFullPath {
|
|
53
|
+
'/': typeof IndexRoute
|
|
54
|
+
'/mcp': typeof McpRoute
|
|
55
|
+
'/providers/$name': typeof ProvidersNameRouteWithChildren
|
|
56
|
+
'/providers/$name/': typeof ProvidersNameIndexRoute
|
|
57
|
+
'/providers/$name/functions/$token': typeof ProvidersNameFunctionsTokenRoute
|
|
58
|
+
'/providers/$name/resources/$token': typeof ProvidersNameResourcesTokenRoute
|
|
59
|
+
}
|
|
60
|
+
export interface FileRoutesByTo {
|
|
61
|
+
'/': typeof IndexRoute
|
|
62
|
+
'/mcp': typeof McpRoute
|
|
63
|
+
'/providers/$name': typeof ProvidersNameIndexRoute
|
|
64
|
+
'/providers/$name/functions/$token': typeof ProvidersNameFunctionsTokenRoute
|
|
65
|
+
'/providers/$name/resources/$token': typeof ProvidersNameResourcesTokenRoute
|
|
66
|
+
}
|
|
67
|
+
export interface FileRoutesById {
|
|
68
|
+
__root__: typeof rootRouteImport
|
|
69
|
+
'/': typeof IndexRoute
|
|
70
|
+
'/mcp': typeof McpRoute
|
|
71
|
+
'/providers/$name': typeof ProvidersNameRouteWithChildren
|
|
72
|
+
'/providers/$name/': typeof ProvidersNameIndexRoute
|
|
73
|
+
'/providers/$name/functions/$token': typeof ProvidersNameFunctionsTokenRoute
|
|
74
|
+
'/providers/$name/resources/$token': typeof ProvidersNameResourcesTokenRoute
|
|
75
|
+
}
|
|
76
|
+
export interface FileRouteTypes {
|
|
77
|
+
fileRoutesByFullPath: FileRoutesByFullPath
|
|
78
|
+
fullPaths:
|
|
79
|
+
| '/'
|
|
80
|
+
| '/mcp'
|
|
81
|
+
| '/providers/$name'
|
|
82
|
+
| '/providers/$name/'
|
|
83
|
+
| '/providers/$name/functions/$token'
|
|
84
|
+
| '/providers/$name/resources/$token'
|
|
85
|
+
fileRoutesByTo: FileRoutesByTo
|
|
86
|
+
to:
|
|
87
|
+
| '/'
|
|
88
|
+
| '/mcp'
|
|
89
|
+
| '/providers/$name'
|
|
90
|
+
| '/providers/$name/functions/$token'
|
|
91
|
+
| '/providers/$name/resources/$token'
|
|
92
|
+
id:
|
|
93
|
+
| '__root__'
|
|
94
|
+
| '/'
|
|
95
|
+
| '/mcp'
|
|
96
|
+
| '/providers/$name'
|
|
97
|
+
| '/providers/$name/'
|
|
98
|
+
| '/providers/$name/functions/$token'
|
|
99
|
+
| '/providers/$name/resources/$token'
|
|
100
|
+
fileRoutesById: FileRoutesById
|
|
101
|
+
}
|
|
102
|
+
export interface RootRouteChildren {
|
|
103
|
+
IndexRoute: typeof IndexRoute
|
|
104
|
+
McpRoute: typeof McpRoute
|
|
105
|
+
ProvidersNameRoute: typeof ProvidersNameRouteWithChildren
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
declare module '@tanstack/react-router' {
|
|
109
|
+
interface FileRoutesByPath {
|
|
110
|
+
'/': {
|
|
111
|
+
id: '/'
|
|
112
|
+
path: '/'
|
|
113
|
+
fullPath: '/'
|
|
114
|
+
preLoaderRoute: typeof IndexRouteImport
|
|
115
|
+
parentRoute: typeof rootRouteImport
|
|
116
|
+
}
|
|
117
|
+
'/mcp': {
|
|
118
|
+
id: '/mcp'
|
|
119
|
+
path: '/mcp'
|
|
120
|
+
fullPath: '/mcp'
|
|
121
|
+
preLoaderRoute: typeof McpRouteImport
|
|
122
|
+
parentRoute: typeof rootRouteImport
|
|
123
|
+
}
|
|
124
|
+
'/providers/$name': {
|
|
125
|
+
id: '/providers/$name'
|
|
126
|
+
path: '/providers/$name'
|
|
127
|
+
fullPath: '/providers/$name'
|
|
128
|
+
preLoaderRoute: typeof ProvidersNameRouteImport
|
|
129
|
+
parentRoute: typeof rootRouteImport
|
|
130
|
+
}
|
|
131
|
+
'/providers/$name/': {
|
|
132
|
+
id: '/providers/$name/'
|
|
133
|
+
path: '/'
|
|
134
|
+
fullPath: '/providers/$name/'
|
|
135
|
+
preLoaderRoute: typeof ProvidersNameIndexRouteImport
|
|
136
|
+
parentRoute: typeof ProvidersNameRoute
|
|
137
|
+
}
|
|
138
|
+
'/providers/$name/functions/$token': {
|
|
139
|
+
id: '/providers/$name/functions/$token'
|
|
140
|
+
path: '/functions/$token'
|
|
141
|
+
fullPath: '/providers/$name/functions/$token'
|
|
142
|
+
preLoaderRoute: typeof ProvidersNameFunctionsTokenRouteImport
|
|
143
|
+
parentRoute: typeof ProvidersNameRoute
|
|
144
|
+
}
|
|
145
|
+
'/providers/$name/resources/$token': {
|
|
146
|
+
id: '/providers/$name/resources/$token'
|
|
147
|
+
path: '/resources/$token'
|
|
148
|
+
fullPath: '/providers/$name/resources/$token'
|
|
149
|
+
preLoaderRoute: typeof ProvidersNameResourcesTokenRouteImport
|
|
150
|
+
parentRoute: typeof ProvidersNameRoute
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
interface ProvidersNameRouteChildren {
|
|
156
|
+
ProvidersNameIndexRoute: typeof ProvidersNameIndexRoute
|
|
157
|
+
ProvidersNameFunctionsTokenRoute: typeof ProvidersNameFunctionsTokenRoute
|
|
158
|
+
ProvidersNameResourcesTokenRoute: typeof ProvidersNameResourcesTokenRoute
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const ProvidersNameRouteChildren: ProvidersNameRouteChildren = {
|
|
162
|
+
ProvidersNameIndexRoute: ProvidersNameIndexRoute,
|
|
163
|
+
ProvidersNameFunctionsTokenRoute: ProvidersNameFunctionsTokenRoute,
|
|
164
|
+
ProvidersNameResourcesTokenRoute: ProvidersNameResourcesTokenRoute,
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const ProvidersNameRouteWithChildren = ProvidersNameRoute._addFileChildren(
|
|
168
|
+
ProvidersNameRouteChildren,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
const rootRouteChildren: RootRouteChildren = {
|
|
172
|
+
IndexRoute: IndexRoute,
|
|
173
|
+
McpRoute: McpRoute,
|
|
174
|
+
ProvidersNameRoute: ProvidersNameRouteWithChildren,
|
|
175
|
+
}
|
|
176
|
+
export const routeTree = rootRouteImport
|
|
177
|
+
._addFileChildren(rootRouteChildren)
|
|
178
|
+
._addFileTypes<FileRouteTypes>()
|
|
179
|
+
|
|
180
|
+
import type { getRouter } from './router.tsx'
|
|
181
|
+
import type { createStart } from '@tanstack/react-start'
|
|
182
|
+
declare module '@tanstack/react-start' {
|
|
183
|
+
interface Register {
|
|
184
|
+
ssr: true
|
|
185
|
+
router: Awaited<ReturnType<typeof getRouter>>
|
|
186
|
+
}
|
|
187
|
+
}
|
package/src/router.tsx
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { createRouter as createTanStackRouter } from '@tanstack/react-router'
|
|
2
|
+
import { routeTree } from './routeTree.gen'
|
|
3
|
+
|
|
4
|
+
export function getRouter() {
|
|
5
|
+
const router = createTanStackRouter({
|
|
6
|
+
routeTree,
|
|
7
|
+
scrollRestoration: true,
|
|
8
|
+
defaultPreload: 'intent',
|
|
9
|
+
defaultPreloadStaleTime: 0,
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
return router
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
declare module '@tanstack/react-router' {
|
|
16
|
+
interface Register {
|
|
17
|
+
router: ReturnType<typeof getRouter>
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HeadContent,
|
|
3
|
+
Link,
|
|
4
|
+
Scripts,
|
|
5
|
+
createRootRoute,
|
|
6
|
+
} from '@tanstack/react-router'
|
|
7
|
+
|
|
8
|
+
import { PulumiLogo } from '@/components/PulumiLogo'
|
|
9
|
+
import appCss from '../styles.css?url'
|
|
10
|
+
|
|
11
|
+
export const Route = createRootRoute({
|
|
12
|
+
head: () => ({
|
|
13
|
+
meta: [
|
|
14
|
+
{
|
|
15
|
+
charSet: 'utf-8',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: 'viewport',
|
|
19
|
+
content: 'width=device-width, initial-scale=1',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
title: 'Local Provider Docs',
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
links: [
|
|
26
|
+
{
|
|
27
|
+
rel: 'stylesheet',
|
|
28
|
+
href: appCss,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
rel: 'icon',
|
|
32
|
+
href: 'https://brand.pulumi.com/media/images/logos/icon-rounded-64w.png',
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
}),
|
|
36
|
+
shellComponent: RootDocument,
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
function RootDocument({ children }: { children: React.ReactNode }) {
|
|
40
|
+
return (
|
|
41
|
+
<html lang="en">
|
|
42
|
+
<head>
|
|
43
|
+
<HeadContent />
|
|
44
|
+
</head>
|
|
45
|
+
<body>
|
|
46
|
+
<header className="site-header">
|
|
47
|
+
<Link to="/" className="site-brand">
|
|
48
|
+
<PulumiLogo height={22} />
|
|
49
|
+
<span className="site-brand-divider" aria-hidden="true" />
|
|
50
|
+
<span className="site-brand-name">Local Provider Docs</span>
|
|
51
|
+
</Link>
|
|
52
|
+
</header>
|
|
53
|
+
{children}
|
|
54
|
+
|
|
55
|
+
<Scripts />
|
|
56
|
+
</body>
|
|
57
|
+
</html>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Link, createFileRoute } from '@tanstack/react-router'
|
|
2
|
+
|
|
3
|
+
import { listProviders } from '@/lib/pulumi/api'
|
|
4
|
+
|
|
5
|
+
export const Route = createFileRoute('/')({
|
|
6
|
+
loader: () => listProviders(),
|
|
7
|
+
component: Home,
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
function Home() {
|
|
11
|
+
const { targetDir, project, providers } = Route.useLoaderData()
|
|
12
|
+
|
|
13
|
+
if (!project) {
|
|
14
|
+
return (
|
|
15
|
+
<main>
|
|
16
|
+
<h1>Local Provider Docs</h1>
|
|
17
|
+
<p className="empty-state">
|
|
18
|
+
No Pulumi project was found at <code>{targetDir}</code> (or any parent
|
|
19
|
+
directory). Run this from inside a Pulumi project directory, or with{' '}
|
|
20
|
+
<code>--dir <path></code> pointing at one.
|
|
21
|
+
</p>
|
|
22
|
+
</main>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<main>
|
|
28
|
+
<h1>Local Provider Docs</h1>
|
|
29
|
+
<p className="subtitle">
|
|
30
|
+
{project.name ?? 'Pulumi project'} · <code>{project.root}</code>
|
|
31
|
+
</p>
|
|
32
|
+
|
|
33
|
+
{providers.length === 0 ? (
|
|
34
|
+
<p className="empty-state">
|
|
35
|
+
This project's <code>Pulumi.yaml</code> has no <code>packages</code>{' '}
|
|
36
|
+
section, so there are no local or parameterized providers to show docs
|
|
37
|
+
for yet. Add one with <code>pulumi package add</code>.
|
|
38
|
+
</p>
|
|
39
|
+
) : (
|
|
40
|
+
<ul className="provider-list">
|
|
41
|
+
{providers.map((provider) => (
|
|
42
|
+
<li key={provider.name} className="provider-card">
|
|
43
|
+
<Link to="/providers/$name" params={{ name: provider.name }}>
|
|
44
|
+
<span className="provider-name">{provider.name}</span>
|
|
45
|
+
<span className="provider-source">
|
|
46
|
+
{provider.source}
|
|
47
|
+
{provider.version ? ` @ ${provider.version}` : ''}
|
|
48
|
+
{provider.parameters?.length
|
|
49
|
+
? ` (${provider.parameters.join(' ')})`
|
|
50
|
+
: ''}
|
|
51
|
+
</span>
|
|
52
|
+
</Link>
|
|
53
|
+
{provider.origin === 'error' ? (
|
|
54
|
+
<p className="banner banner-failure">
|
|
55
|
+
Failed to load schema: {provider.error}
|
|
56
|
+
</p>
|
|
57
|
+
) : (
|
|
58
|
+
<p className="provider-stats">
|
|
59
|
+
{provider.resourceCount} resource
|
|
60
|
+
{provider.resourceCount === 1 ? '' : 's'}
|
|
61
|
+
{' · '}
|
|
62
|
+
{provider.functionCount} function
|
|
63
|
+
{provider.functionCount === 1 ? '' : 's'}
|
|
64
|
+
{provider.origin === 'cache' && (
|
|
65
|
+
<>
|
|
66
|
+
{' · '}
|
|
67
|
+
<span className="badge badge-info">from cache</span>
|
|
68
|
+
</>
|
|
69
|
+
)}
|
|
70
|
+
</p>
|
|
71
|
+
)}
|
|
72
|
+
</li>
|
|
73
|
+
))}
|
|
74
|
+
</ul>
|
|
75
|
+
)}
|
|
76
|
+
</main>
|
|
77
|
+
)
|
|
78
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
2
|
+
|
|
3
|
+
import { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js'
|
|
4
|
+
|
|
5
|
+
import { createMcpServer } from '@/lib/pulumi/mcp.server'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Stateless MCP endpoint: a fresh server + transport per request, so concurrent
|
|
9
|
+
* requests never share state. There's nothing here worth persisting across
|
|
10
|
+
* requests (schemas are re-resolved, cached on disk, each call) and every tool
|
|
11
|
+
* is a plain read, so statelessness keeps this simple.
|
|
12
|
+
*/
|
|
13
|
+
async function handleMcpRequest({ request }: { request: Request }) {
|
|
14
|
+
const server = createMcpServer()
|
|
15
|
+
const transport = new WebStandardStreamableHTTPServerTransport({
|
|
16
|
+
sessionIdGenerator: undefined,
|
|
17
|
+
})
|
|
18
|
+
await server.connect(transport)
|
|
19
|
+
return transport.handleRequest(request)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const Route = createFileRoute('/mcp')({
|
|
23
|
+
server: {
|
|
24
|
+
handlers: {
|
|
25
|
+
GET: handleMcpRequest,
|
|
26
|
+
POST: handleMcpRequest,
|
|
27
|
+
DELETE: handleMcpRequest,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
})
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Link, createFileRoute, notFound } from '@tanstack/react-router'
|
|
2
|
+
|
|
3
|
+
import { MemberDetail } from '@/components/MemberDetail'
|
|
4
|
+
import { getProvider } from '@/lib/pulumi/api'
|
|
5
|
+
import { tokenDisplayName } from '@/lib/pulumi/token'
|
|
6
|
+
|
|
7
|
+
export const Route = createFileRoute('/providers/$name/functions/$token')({
|
|
8
|
+
loader: async ({ params }) => {
|
|
9
|
+
const entry = await getProvider({ data: params.name })
|
|
10
|
+
const fn = entry?.schema?.functions?.[params.token]
|
|
11
|
+
if (!fn) throw notFound()
|
|
12
|
+
return {
|
|
13
|
+
providerName: params.name,
|
|
14
|
+
token: params.token,
|
|
15
|
+
fn,
|
|
16
|
+
runtime: entry?.runtime,
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
component: FunctionDetail,
|
|
20
|
+
notFoundComponent: () => (
|
|
21
|
+
<main>
|
|
22
|
+
<p>
|
|
23
|
+
No function with that name was found.{' '}
|
|
24
|
+
<Link to="/">Back to all providers.</Link>
|
|
25
|
+
</p>
|
|
26
|
+
</main>
|
|
27
|
+
),
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
function FunctionDetail() {
|
|
31
|
+
const { providerName, token, fn, runtime } = Route.useLoaderData()
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<MemberDetail
|
|
35
|
+
providerName={providerName}
|
|
36
|
+
kind="Function"
|
|
37
|
+
displayName={tokenDisplayName(token)}
|
|
38
|
+
token={token}
|
|
39
|
+
description={fn.description}
|
|
40
|
+
runtime={runtime}
|
|
41
|
+
inputProperties={fn.inputs?.properties}
|
|
42
|
+
requiredInputs={fn.inputs?.required}
|
|
43
|
+
outputProperties={fn.outputs?.properties}
|
|
44
|
+
requiredOutputs={fn.outputs?.required}
|
|
45
|
+
/>
|
|
46
|
+
)
|
|
47
|
+
}
|