@rwdocs/backstage-plugin-rw-backend 0.1.4 → 0.1.5
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/dist/hub.cjs.js +2 -2
- package/dist/hub.cjs.js.map +1 -1
- package/dist/plugin.cjs.js +8 -30
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/router.cjs.js +5 -5
- package/dist/router.cjs.js.map +1 -1
- package/package.json +5 -5
- package/config.d.ts +0 -56
- package/dist/entityPath.cjs.js +0 -12
- package/dist/entityPath.cjs.js.map +0 -1
package/dist/hub.cjs.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var core = require('@rwdocs/core');
|
|
4
|
-
var
|
|
4
|
+
var backstagePluginRwCommon = require('@rwdocs/backstage-plugin-rw-common');
|
|
5
5
|
|
|
6
6
|
class Hub {
|
|
7
7
|
options;
|
|
@@ -10,7 +10,7 @@ class Hub {
|
|
|
10
10
|
constructor(options) {
|
|
11
11
|
this.options = {
|
|
12
12
|
...options,
|
|
13
|
-
entity: options.entity ?
|
|
13
|
+
entity: options.entity ? backstagePluginRwCommon.toEntityPath(options.entity) : void 0
|
|
14
14
|
};
|
|
15
15
|
this.maxSize = options.cacheSize ?? 20;
|
|
16
16
|
}
|
package/dist/hub.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hub.cjs.js","sources":["../src/hub.ts"],"sourcesContent":["import type { LoggerService } from \"@backstage/backend-plugin-api\";\nimport { createSite, type RwSite, type SiteConfig, type DiagramsConfig } from \"@rwdocs/core\";\nimport { toEntityPath } from \"
|
|
1
|
+
{"version":3,"file":"hub.cjs.js","sources":["../src/hub.ts"],"sourcesContent":["import type { LoggerService } from \"@backstage/backend-plugin-api\";\nimport { createSite, type RwSite, type SiteConfig, type DiagramsConfig } from \"@rwdocs/core\";\nimport { toEntityPath, type S3Config } from \"@rwdocs/backstage-plugin-rw-common\";\n\nexport interface HubOptions {\n s3?: S3Config;\n projectDir?: string;\n /** Entity ref in any format accepted by parseEntityRef. Normalized internally. */\n entity?: string;\n diagrams?: DiagramsConfig;\n cacheSize?: number;\n}\n\nexport class Hub {\n private readonly options: HubOptions;\n private readonly cache: Map<string, RwSite> = new Map();\n private readonly maxSize: number;\n\n constructor(options: HubOptions) {\n this.options = {\n ...options,\n entity: options.entity ? toEntityPath(options.entity) : undefined,\n };\n this.maxSize = options.cacheSize ?? 20;\n }\n\n getSite(entityRef: string): RwSite | undefined {\n if (this.options.projectDir) {\n return this.getLocalSite(entityRef);\n }\n return this.getS3Site(entityRef);\n }\n\n private getLocalSite(entityRef: string): RwSite | undefined {\n if (entityRef !== this.options.entity) {\n return undefined;\n }\n\n const cached = this.cache.get(entityRef);\n if (cached) return cached;\n\n const site = createSite({\n projectDir: this.options.projectDir,\n diagrams: this.options.diagrams,\n });\n this.cache.set(entityRef, site);\n return site;\n }\n\n async reloadAll(logger: LoggerService) {\n const entries = [...this.cache.entries()];\n for (const [ref, site] of entries) {\n try {\n const reloaded = await site.reload();\n if (reloaded) {\n logger.info(`Reloaded site: ${ref}`);\n }\n } catch (err) {\n logger.warn(`Failed to reload site ${ref}: ${err}`);\n }\n }\n }\n\n private getS3Site(entityRef: string): RwSite {\n const cached = this.cache.get(entityRef);\n if (cached) {\n this.cache.delete(entityRef);\n this.cache.set(entityRef, cached);\n return cached;\n }\n\n if (this.cache.size >= this.maxSize) {\n const firstKey = this.cache.keys().next().value!;\n this.cache.delete(firstKey);\n }\n\n const s3 = this.options.s3!;\n const config: SiteConfig = {\n s3: { ...s3, entity: entityRef },\n diagrams: this.options.diagrams,\n };\n\n const site = createSite(config);\n this.cache.set(entityRef, site);\n return site;\n }\n}\n"],"names":["toEntityPath","createSite"],"mappings":"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"}
|
package/dist/plugin.cjs.js
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
var backendPluginApi = require('@backstage/backend-plugin-api');
|
|
4
4
|
var config = require('@backstage/config');
|
|
5
|
+
var backstagePluginRwCommon = require('@rwdocs/backstage-plugin-rw-common');
|
|
5
6
|
var router = require('./router.cjs.js');
|
|
6
7
|
var hub = require('./hub.cjs.js');
|
|
7
|
-
var entityPath = require('./entityPath.cjs.js');
|
|
8
8
|
|
|
9
9
|
const rwPlugin = backendPluginApi.createBackendPlugin({
|
|
10
10
|
pluginId: "rw",
|
|
@@ -18,42 +18,20 @@ const rwPlugin = backendPluginApi.createBackendPlugin({
|
|
|
18
18
|
scheduler: backendPluginApi.coreServices.scheduler
|
|
19
19
|
},
|
|
20
20
|
async init({ httpRouter, httpAuth, logger, config: config$1, scheduler }) {
|
|
21
|
-
const
|
|
22
|
-
const entity = config$1.getOptionalString("rw.entity");
|
|
21
|
+
const siteConfig = backstagePluginRwCommon.readRwSiteConfig(config$1);
|
|
23
22
|
const cacheSize = config$1.getOptionalNumber("rw.cacheSize");
|
|
24
|
-
const s3Config = config$1.getOptionalConfig("rw.s3");
|
|
25
|
-
const s3 = s3Config ? {
|
|
26
|
-
bucket: s3Config.getString("bucket"),
|
|
27
|
-
region: s3Config.getOptionalString("region"),
|
|
28
|
-
endpoint: s3Config.getOptionalString("endpoint"),
|
|
29
|
-
bucketRootPath: s3Config.getOptionalString("bucketRootPath"),
|
|
30
|
-
accessKeyId: s3Config.getOptionalString("accessKeyId"),
|
|
31
|
-
secretAccessKey: s3Config.getOptionalString("secretAccessKey")
|
|
32
|
-
} : void 0;
|
|
33
|
-
if (!projectDir && !s3) {
|
|
34
|
-
throw new Error("Either rw.projectDir or rw.s3 must be configured");
|
|
35
|
-
}
|
|
36
|
-
if (projectDir && !entity) {
|
|
37
|
-
throw new Error("rw.entity is required when rw.projectDir is set");
|
|
38
|
-
}
|
|
39
|
-
const diagramsConfig = config$1.getOptionalConfig("rw.diagrams");
|
|
40
|
-
const diagrams = diagramsConfig ? {
|
|
41
|
-
krokiUrl: diagramsConfig.getOptionalString("krokiUrl"),
|
|
42
|
-
dpi: diagramsConfig.getOptionalNumber("dpi")
|
|
43
|
-
} : void 0;
|
|
44
23
|
const hubOptions = {
|
|
45
|
-
|
|
46
|
-
entity,
|
|
47
|
-
s3,
|
|
48
|
-
diagrams,
|
|
24
|
+
...siteConfig,
|
|
49
25
|
cacheSize
|
|
50
26
|
};
|
|
51
27
|
const hub$1 = new hub.Hub(hubOptions);
|
|
52
|
-
if (s3) {
|
|
53
|
-
logger.info(
|
|
28
|
+
if (siteConfig.s3) {
|
|
29
|
+
logger.info(
|
|
30
|
+
`Hub: S3 mode (bucket: ${siteConfig.s3.bucket}, cache size: ${cacheSize ?? 20})`
|
|
31
|
+
);
|
|
54
32
|
} else {
|
|
55
33
|
logger.info(
|
|
56
|
-
`Hub: local mode (${projectDir}, entity: ${entity ?
|
|
34
|
+
`Hub: local mode (${siteConfig.projectDir}, entity: ${siteConfig.entity ? backstagePluginRwCommon.toEntityPath(siteConfig.entity) : siteConfig.entity})`
|
|
57
35
|
);
|
|
58
36
|
}
|
|
59
37
|
if (config$1.has("rw.reloadInterval")) {
|
package/dist/plugin.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.ts"],"sourcesContent":["import { coreServices, createBackendPlugin } from \"@backstage/backend-plugin-api\";\nimport { readDurationFromConfig } from \"@backstage/config\";\nimport {
|
|
1
|
+
{"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.ts"],"sourcesContent":["import { coreServices, createBackendPlugin } from \"@backstage/backend-plugin-api\";\nimport { readDurationFromConfig } from \"@backstage/config\";\nimport { readRwSiteConfig, toEntityPath } from \"@rwdocs/backstage-plugin-rw-common\";\nimport { createRouter } from \"./router\";\nimport { Hub, type HubOptions } from \"./hub\";\n\nexport const rwPlugin = createBackendPlugin({\n pluginId: \"rw\",\n register(env) {\n env.registerInit({\n deps: {\n httpRouter: coreServices.httpRouter,\n httpAuth: coreServices.httpAuth,\n logger: coreServices.logger,\n config: coreServices.rootConfig,\n scheduler: coreServices.scheduler,\n },\n async init({ httpRouter, httpAuth, logger, config, scheduler }) {\n const siteConfig = readRwSiteConfig(config);\n const cacheSize = config.getOptionalNumber(\"rw.cacheSize\");\n\n const hubOptions: HubOptions = {\n ...siteConfig,\n cacheSize,\n };\n\n const hub = new Hub(hubOptions);\n\n if (siteConfig.s3) {\n logger.info(\n `Hub: S3 mode (bucket: ${siteConfig.s3.bucket}, cache size: ${cacheSize ?? 20})`,\n );\n } else {\n logger.info(\n `Hub: local mode (${siteConfig.projectDir}, entity: ${siteConfig.entity ? toEntityPath(siteConfig.entity) : siteConfig.entity})`,\n );\n }\n\n if (config.has(\"rw.reloadInterval\")) {\n const frequency = readDurationFromConfig(config, { key: \"rw.reloadInterval\" });\n logger.info(`Scheduling site reload with interval: ${JSON.stringify(frequency)}`);\n\n await scheduler.scheduleTask({\n id: \"rw-site-reload\",\n frequency,\n timeout: frequency,\n scope: \"local\",\n fn: async () => hub.reloadAll(logger),\n });\n }\n\n const router = await createRouter({ logger, httpAuth, hub });\n httpRouter.use(router);\n httpRouter.addAuthPolicy({\n path: \"/health\",\n allow: \"unauthenticated\",\n });\n },\n });\n },\n});\n"],"names":["createBackendPlugin","coreServices","config","readRwSiteConfig","hub","Hub","toEntityPath","readDurationFromConfig","router","createRouter"],"mappings":";;;;;;;;AAMO,MAAM,WAAWA,oCAAA,CAAoB;AAAA,EAC1C,QAAA,EAAU,IAAA;AAAA,EACV,SAAS,GAAA,EAAK;AACZ,IAAA,GAAA,CAAI,YAAA,CAAa;AAAA,MACf,IAAA,EAAM;AAAA,QACJ,YAAYC,6BAAA,CAAa,UAAA;AAAA,QACzB,UAAUA,6BAAA,CAAa,QAAA;AAAA,QACvB,QAAQA,6BAAA,CAAa,MAAA;AAAA,QACrB,QAAQA,6BAAA,CAAa,UAAA;AAAA,QACrB,WAAWA,6BAAA,CAAa;AAAA,OAC1B;AAAA,MACA,MAAM,KAAK,EAAE,UAAA,EAAY,UAAU,MAAA,UAAQC,QAAA,EAAQ,WAAU,EAAG;AAC9D,QAAA,MAAM,UAAA,GAAaC,yCAAiBD,QAAM,CAAA;AAC1C,QAAA,MAAM,SAAA,GAAYA,QAAA,CAAO,iBAAA,CAAkB,cAAc,CAAA;AAEzD,QAAA,MAAM,UAAA,GAAyB;AAAA,UAC7B,GAAG,UAAA;AAAA,UACH;AAAA,SACF;AAEA,QAAA,MAAME,KAAA,GAAM,IAAIC,OAAA,CAAI,UAAU,CAAA;AAE9B,QAAA,IAAI,WAAW,EAAA,EAAI;AACjB,UAAA,MAAA,CAAO,IAAA;AAAA,YACL,yBAAyB,UAAA,CAAW,EAAA,CAAG,MAAM,CAAA,cAAA,EAAiB,aAAa,EAAE,CAAA,CAAA;AAAA,WAC/E;AAAA,QACF,CAAA,MAAO;AACL,UAAA,MAAA,CAAO,IAAA;AAAA,YACL,CAAA,iBAAA,EAAoB,UAAA,CAAW,UAAU,CAAA,UAAA,EAAa,UAAA,CAAW,MAAA,GAASC,oCAAA,CAAa,UAAA,CAAW,MAAM,CAAA,GAAI,UAAA,CAAW,MAAM,CAAA,CAAA;AAAA,WAC/H;AAAA,QACF;AAEA,QAAA,IAAIJ,QAAA,CAAO,GAAA,CAAI,mBAAmB,CAAA,EAAG;AACnC,UAAA,MAAM,YAAYK,6BAAA,CAAuBL,QAAA,EAAQ,EAAE,GAAA,EAAK,qBAAqB,CAAA;AAC7E,UAAA,MAAA,CAAO,KAAK,CAAA,sCAAA,EAAyC,IAAA,CAAK,SAAA,CAAU,SAAS,CAAC,CAAA,CAAE,CAAA;AAEhF,UAAA,MAAM,UAAU,YAAA,CAAa;AAAA,YAC3B,EAAA,EAAI,gBAAA;AAAA,YACJ,SAAA;AAAA,YACA,OAAA,EAAS,SAAA;AAAA,YACT,KAAA,EAAO,OAAA;AAAA,YACP,EAAA,EAAI,YAAYE,KAAA,CAAI,SAAA,CAAU,MAAM;AAAA,WACrC,CAAA;AAAA,QACH;AAEA,QAAA,MAAMI,WAAS,MAAMC,mBAAA,CAAa,OAAoBL,OAAK,CAAA;AAC3D,QAAA,UAAA,CAAW,IAAII,QAAM,CAAA;AACrB,QAAA,UAAA,CAAW,aAAA,CAAc;AAAA,UACvB,IAAA,EAAM,SAAA;AAAA,UACN,KAAA,EAAO;AAAA,SACR,CAAA;AAAA,MACH;AAAA,KACD,CAAA;AAAA,EACH;AACF,CAAC;;;;"}
|
package/dist/router.cjs.js
CHANGED
|
@@ -26,11 +26,11 @@ async function createRouter(options) {
|
|
|
26
26
|
router.get("/site/:namespace/:kind/:name/config", (_req, res) => {
|
|
27
27
|
res.json({ liveReloadEnabled: false });
|
|
28
28
|
});
|
|
29
|
-
router.get("/site/:namespace/:kind/:name/navigation", (req, res) => {
|
|
29
|
+
router.get("/site/:namespace/:kind/:name/navigation", async (req, res) => {
|
|
30
30
|
const site = res.locals.rwSite;
|
|
31
31
|
const sectionRefParam = req.query.sectionRef;
|
|
32
32
|
const sectionRef = typeof sectionRefParam === "string" ? sectionRefParam : null;
|
|
33
|
-
const nav = getNavigationOrThrow(site, sectionRef);
|
|
33
|
+
const nav = await getNavigationOrThrow(site, sectionRef);
|
|
34
34
|
res.json(nav);
|
|
35
35
|
});
|
|
36
36
|
router.get("/site/:namespace/:kind/:name/pages/", async (req, res) => {
|
|
@@ -39,7 +39,7 @@ async function createRouter(options) {
|
|
|
39
39
|
const sectionRef = typeof sectionRefParam === "string" ? sectionRefParam : void 0;
|
|
40
40
|
let pagePath = "";
|
|
41
41
|
if (sectionRef) {
|
|
42
|
-
const nav = getNavigationOrThrow(site, sectionRef);
|
|
42
|
+
const nav = await getNavigationOrThrow(site, sectionRef);
|
|
43
43
|
if (nav.scope?.path) {
|
|
44
44
|
pagePath = nav.scope.path.replace(/^\//, "");
|
|
45
45
|
}
|
|
@@ -58,9 +58,9 @@ async function createRouter(options) {
|
|
|
58
58
|
});
|
|
59
59
|
return router;
|
|
60
60
|
}
|
|
61
|
-
function getNavigationOrThrow(site, sectionRef) {
|
|
61
|
+
async function getNavigationOrThrow(site, sectionRef) {
|
|
62
62
|
try {
|
|
63
|
-
return site.getNavigation(sectionRef);
|
|
63
|
+
return await site.getNavigation(sectionRef);
|
|
64
64
|
} catch (err) {
|
|
65
65
|
throw toStorageError(err);
|
|
66
66
|
}
|
package/dist/router.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.cjs.js","sources":["../src/router.ts"],"sourcesContent":["import Router from \"express-promise-router\";\nimport type { HttpAuthService, LoggerService } from \"@backstage/backend-plugin-api\";\nimport { InputError, NotFoundError, ServiceUnavailableError } from \"@backstage/errors\";\nimport type { RwSite } from \"@rwdocs/core\";\nimport type { Hub } from \"./hub\";\n\nexport interface RouterOptions {\n logger: LoggerService;\n httpAuth: HttpAuthService;\n hub: Hub;\n}\n\nexport async function createRouter(options: RouterOptions) {\n const { hub } = options;\n const router = Router();\n\n router.get(\"/health\", (_req, res) => {\n res.json({ status: \"ok\" });\n });\n\n router.use(\"/site/:namespace/:kind/:name\", (req, res, next) => {\n const { namespace, kind, name } = req.params;\n const siteRef = `${namespace}/${kind}/${name}`.toLowerCase();\n\n const site = hub.getSite(siteRef);\n if (!site) {\n throw new NotFoundError(`No documentation site found for entity: ${siteRef}`);\n }\n\n res.locals.rwSite = site;\n next();\n });\n\n router.get(\"/site/:namespace/:kind/:name/config\", (_req, res) => {\n res.json({ liveReloadEnabled: false });\n });\n\n router.get(\"/site/:namespace/:kind/:name/navigation\", (req, res) => {\n const site: RwSite = res.locals.rwSite;\n const sectionRefParam = req.query.sectionRef;\n const sectionRef = typeof sectionRefParam === \"string\" ? sectionRefParam : null;\n const nav = getNavigationOrThrow(site, sectionRef);\n res.json(nav);\n });\n\n router.get(\"/site/:namespace/:kind/:name/pages/\", async (req, res) => {\n const site: RwSite = res.locals.rwSite;\n const sectionRefParam = req.query.sectionRef;\n const sectionRef = typeof sectionRefParam === \"string\" ? sectionRefParam : undefined;\n\n let pagePath = \"\";\n if (sectionRef) {\n const nav = getNavigationOrThrow(site, sectionRef);\n if (nav.scope?.path) {\n pagePath = nav.scope.path.replace(/^\\//, \"\");\n }\n }\n\n const page = await renderPageOrThrow(site, pagePath);\n res.json(page);\n });\n\n router.get(\"/site/:namespace/:kind/:name/pages/:path(*)\", async (req, res) => {\n const site: RwSite = res.locals.rwSite;\n const pagePath = req.params.path || \"\";\n if (pagePath.split(\"/\").includes(\"..\")) {\n throw new InputError(\"Invalid path\");\n }\n const page = await renderPageOrThrow(site, pagePath);\n res.json(page);\n });\n\n return router;\n}\n\
|
|
1
|
+
{"version":3,"file":"router.cjs.js","sources":["../src/router.ts"],"sourcesContent":["import Router from \"express-promise-router\";\nimport type { HttpAuthService, LoggerService } from \"@backstage/backend-plugin-api\";\nimport { InputError, NotFoundError, ServiceUnavailableError } from \"@backstage/errors\";\nimport type { RwSite } from \"@rwdocs/core\";\nimport type { Hub } from \"./hub\";\n\nexport interface RouterOptions {\n logger: LoggerService;\n httpAuth: HttpAuthService;\n hub: Hub;\n}\n\nexport async function createRouter(options: RouterOptions) {\n const { hub } = options;\n const router = Router();\n\n router.get(\"/health\", (_req, res) => {\n res.json({ status: \"ok\" });\n });\n\n router.use(\"/site/:namespace/:kind/:name\", (req, res, next) => {\n const { namespace, kind, name } = req.params;\n const siteRef = `${namespace}/${kind}/${name}`.toLowerCase();\n\n const site = hub.getSite(siteRef);\n if (!site) {\n throw new NotFoundError(`No documentation site found for entity: ${siteRef}`);\n }\n\n res.locals.rwSite = site;\n next();\n });\n\n router.get(\"/site/:namespace/:kind/:name/config\", (_req, res) => {\n res.json({ liveReloadEnabled: false });\n });\n\n router.get(\"/site/:namespace/:kind/:name/navigation\", async (req, res) => {\n const site: RwSite = res.locals.rwSite;\n const sectionRefParam = req.query.sectionRef;\n const sectionRef = typeof sectionRefParam === \"string\" ? sectionRefParam : null;\n const nav = await getNavigationOrThrow(site, sectionRef);\n res.json(nav);\n });\n\n router.get(\"/site/:namespace/:kind/:name/pages/\", async (req, res) => {\n const site: RwSite = res.locals.rwSite;\n const sectionRefParam = req.query.sectionRef;\n const sectionRef = typeof sectionRefParam === \"string\" ? sectionRefParam : undefined;\n\n let pagePath = \"\";\n if (sectionRef) {\n const nav = await getNavigationOrThrow(site, sectionRef);\n if (nav.scope?.path) {\n pagePath = nav.scope.path.replace(/^\\//, \"\");\n }\n }\n\n const page = await renderPageOrThrow(site, pagePath);\n res.json(page);\n });\n\n router.get(\"/site/:namespace/:kind/:name/pages/:path(*)\", async (req, res) => {\n const site: RwSite = res.locals.rwSite;\n const pagePath = req.params.path || \"\";\n if (pagePath.split(\"/\").includes(\"..\")) {\n throw new InputError(\"Invalid path\");\n }\n const page = await renderPageOrThrow(site, pagePath);\n res.json(page);\n });\n\n return router;\n}\n\nasync function getNavigationOrThrow(site: RwSite, sectionRef: string | null) {\n try {\n return await site.getNavigation(sectionRef);\n } catch (err) {\n throw toStorageError(err);\n }\n}\n\nasync function renderPageOrThrow(site: RwSite, pagePath: string) {\n try {\n return await site.renderPage(pagePath);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n if (message.includes(\"Content not found\")) {\n throw new NotFoundError(`Page not found: /${pagePath}`);\n }\n // Message prefix comes from @rwdocs/core native addon (RenderError::Storage).\n // Must be updated if the upstream error format changes.\n if (message.includes(\"Storage error\")) {\n throw toStorageError(err);\n }\n throw err;\n }\n}\n\nfunction toStorageError(err: unknown): ServiceUnavailableError {\n const message = err instanceof Error ? err.message : String(err);\n return new ServiceUnavailableError(`Storage unavailable: ${message}`);\n}\n"],"names":["Router","NotFoundError","InputError","ServiceUnavailableError"],"mappings":";;;;;;;;;AAYA,eAAsB,aAAa,OAAA,EAAwB;AACzD,EAAA,MAAM,EAAE,KAAI,GAAI,OAAA;AAChB,EAAA,MAAM,SAASA,uBAAA,EAAO;AAEtB,EAAA,MAAA,CAAO,GAAA,CAAI,SAAA,EAAW,CAAC,IAAA,EAAM,GAAA,KAAQ;AACnC,IAAA,GAAA,CAAI,IAAA,CAAK,EAAE,MAAA,EAAQ,IAAA,EAAM,CAAA;AAAA,EAC3B,CAAC,CAAA;AAED,EAAA,MAAA,CAAO,GAAA,CAAI,8BAAA,EAAgC,CAAC,GAAA,EAAK,KAAK,IAAA,KAAS;AAC7D,IAAA,MAAM,EAAE,SAAA,EAAW,IAAA,EAAM,IAAA,KAAS,GAAA,CAAI,MAAA;AACtC,IAAA,MAAM,OAAA,GAAU,GAAG,SAAS,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA,EAAI,IAAI,GAAG,WAAA,EAAY;AAE3D,IAAA,MAAM,IAAA,GAAO,GAAA,CAAI,OAAA,CAAQ,OAAO,CAAA;AAChC,IAAA,IAAI,CAAC,IAAA,EAAM;AACT,MAAA,MAAM,IAAIC,oBAAA,CAAc,CAAA,wCAAA,EAA2C,OAAO,CAAA,CAAE,CAAA;AAAA,IAC9E;AAEA,IAAA,GAAA,CAAI,OAAO,MAAA,GAAS,IAAA;AACpB,IAAA,IAAA,EAAK;AAAA,EACP,CAAC,CAAA;AAED,EAAA,MAAA,CAAO,GAAA,CAAI,qCAAA,EAAuC,CAAC,IAAA,EAAM,GAAA,KAAQ;AAC/D,IAAA,GAAA,CAAI,IAAA,CAAK,EAAE,iBAAA,EAAmB,KAAA,EAAO,CAAA;AAAA,EACvC,CAAC,CAAA;AAED,EAAA,MAAA,CAAO,GAAA,CAAI,yCAAA,EAA2C,OAAO,GAAA,EAAK,GAAA,KAAQ;AACxE,IAAA,MAAM,IAAA,GAAe,IAAI,MAAA,CAAO,MAAA;AAChC,IAAA,MAAM,eAAA,GAAkB,IAAI,KAAA,CAAM,UAAA;AAClC,IAAA,MAAM,UAAA,GAAa,OAAO,eAAA,KAAoB,QAAA,GAAW,eAAA,GAAkB,IAAA;AAC3E,IAAA,MAAM,GAAA,GAAM,MAAM,oBAAA,CAAqB,IAAA,EAAM,UAAU,CAAA;AACvD,IAAA,GAAA,CAAI,KAAK,GAAG,CAAA;AAAA,EACd,CAAC,CAAA;AAED,EAAA,MAAA,CAAO,GAAA,CAAI,qCAAA,EAAuC,OAAO,GAAA,EAAK,GAAA,KAAQ;AACpE,IAAA,MAAM,IAAA,GAAe,IAAI,MAAA,CAAO,MAAA;AAChC,IAAA,MAAM,eAAA,GAAkB,IAAI,KAAA,CAAM,UAAA;AAClC,IAAA,MAAM,UAAA,GAAa,OAAO,eAAA,KAAoB,QAAA,GAAW,eAAA,GAAkB,MAAA;AAE3E,IAAA,IAAI,QAAA,GAAW,EAAA;AACf,IAAA,IAAI,UAAA,EAAY;AACd,MAAA,MAAM,GAAA,GAAM,MAAM,oBAAA,CAAqB,IAAA,EAAM,UAAU,CAAA;AACvD,MAAA,IAAI,GAAA,CAAI,OAAO,IAAA,EAAM;AACnB,QAAA,QAAA,GAAW,GAAA,CAAI,KAAA,CAAM,IAAA,CAAK,OAAA,CAAQ,OAAO,EAAE,CAAA;AAAA,MAC7C;AAAA,IACF;AAEA,IAAA,MAAM,IAAA,GAAO,MAAM,iBAAA,CAAkB,IAAA,EAAM,QAAQ,CAAA;AACnD,IAAA,GAAA,CAAI,KAAK,IAAI,CAAA;AAAA,EACf,CAAC,CAAA;AAED,EAAA,MAAA,CAAO,GAAA,CAAI,6CAAA,EAA+C,OAAO,GAAA,EAAK,GAAA,KAAQ;AAC5E,IAAA,MAAM,IAAA,GAAe,IAAI,MAAA,CAAO,MAAA;AAChC,IAAA,MAAM,QAAA,GAAW,GAAA,CAAI,MAAA,CAAO,IAAA,IAAQ,EAAA;AACpC,IAAA,IAAI,SAAS,KAAA,CAAM,GAAG,CAAA,CAAE,QAAA,CAAS,IAAI,CAAA,EAAG;AACtC,MAAA,MAAM,IAAIC,kBAAW,cAAc,CAAA;AAAA,IACrC;AACA,IAAA,MAAM,IAAA,GAAO,MAAM,iBAAA,CAAkB,IAAA,EAAM,QAAQ,CAAA;AACnD,IAAA,GAAA,CAAI,KAAK,IAAI,CAAA;AAAA,EACf,CAAC,CAAA;AAED,EAAA,OAAO,MAAA;AACT;AAEA,eAAe,oBAAA,CAAqB,MAAc,UAAA,EAA2B;AAC3E,EAAA,IAAI;AACF,IAAA,OAAO,MAAM,IAAA,CAAK,aAAA,CAAc,UAAU,CAAA;AAAA,EAC5C,SAAS,GAAA,EAAK;AACZ,IAAA,MAAM,eAAe,GAAG,CAAA;AAAA,EAC1B;AACF;AAEA,eAAe,iBAAA,CAAkB,MAAc,QAAA,EAAkB;AAC/D,EAAA,IAAI;AACF,IAAA,OAAO,MAAM,IAAA,CAAK,UAAA,CAAW,QAAQ,CAAA;AAAA,EACvC,SAAS,GAAA,EAAK;AACZ,IAAA,MAAM,UAAU,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,OAAO,GAAG,CAAA;AAC/D,IAAA,IAAI,OAAA,CAAQ,QAAA,CAAS,mBAAmB,CAAA,EAAG;AACzC,MAAA,MAAM,IAAID,oBAAA,CAAc,CAAA,iBAAA,EAAoB,QAAQ,CAAA,CAAE,CAAA;AAAA,IACxD;AAGA,IAAA,IAAI,OAAA,CAAQ,QAAA,CAAS,eAAe,CAAA,EAAG;AACrC,MAAA,MAAM,eAAe,GAAG,CAAA;AAAA,IAC1B;AACA,IAAA,MAAM,GAAA;AAAA,EACR;AACF;AAEA,SAAS,eAAe,GAAA,EAAuC;AAC7D,EAAA,MAAM,UAAU,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,OAAO,GAAG,CAAA;AAC/D,EAAA,OAAO,IAAIE,8BAAA,CAAwB,CAAA,qBAAA,EAAwB,OAAO,CAAA,CAAE,CAAA;AACtE;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rwdocs/backstage-plugin-rw-backend",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"license": "MIT OR Apache-2.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -28,16 +28,15 @@
|
|
|
28
28
|
"pluginId": "rw",
|
|
29
29
|
"pluginPackages": [
|
|
30
30
|
"@rwdocs/backstage-plugin-rw",
|
|
31
|
-
"@rwdocs/backstage-plugin-rw-backend"
|
|
31
|
+
"@rwdocs/backstage-plugin-rw-backend",
|
|
32
|
+
"@rwdocs/backstage-plugin-rw-common"
|
|
32
33
|
],
|
|
33
34
|
"features": {
|
|
34
35
|
".": "@backstage/BackendFeature"
|
|
35
36
|
}
|
|
36
37
|
},
|
|
37
|
-
"configSchema": "config.d.ts",
|
|
38
38
|
"files": [
|
|
39
39
|
"dist",
|
|
40
|
-
"config.d.ts",
|
|
41
40
|
"LICENSE-MIT",
|
|
42
41
|
"LICENSE-APACHE"
|
|
43
42
|
],
|
|
@@ -57,7 +56,8 @@
|
|
|
57
56
|
"@backstage/config": "^1.3.6",
|
|
58
57
|
"@backstage/errors": "^1.2.7",
|
|
59
58
|
"@backstage/types": "^1.2.2",
|
|
60
|
-
"@rwdocs/
|
|
59
|
+
"@rwdocs/backstage-plugin-rw-common": "^0.1.5",
|
|
60
|
+
"@rwdocs/core": "^0.1.23",
|
|
61
61
|
"express": "^4.21.0",
|
|
62
62
|
"express-promise-router": "^4.1.0"
|
|
63
63
|
},
|
package/config.d.ts
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
export interface Config {
|
|
2
|
-
/** @visibility backend */
|
|
3
|
-
rw?: {
|
|
4
|
-
/**
|
|
5
|
-
* Local directory containing documentation source files.
|
|
6
|
-
* Mutually exclusive with `s3`.
|
|
7
|
-
*/
|
|
8
|
-
projectDir?: string;
|
|
9
|
-
/**
|
|
10
|
-
* Entity ref that the local projectDir serves as (required when projectDir is set).
|
|
11
|
-
* Standard Backstage entity ref format: "kind:namespace/name" (e.g. "component:default/my-docs")
|
|
12
|
-
*/
|
|
13
|
-
entity?: string;
|
|
14
|
-
/** Maximum number of cached RwSite instances. Default: 20. */
|
|
15
|
-
cacheSize?: number;
|
|
16
|
-
/**
|
|
17
|
-
* How often to check cached sites for upstream changes and reload if needed.
|
|
18
|
-
* If not set, no periodic reloading is performed.
|
|
19
|
-
*
|
|
20
|
-
* @example
|
|
21
|
-
* ```yaml
|
|
22
|
-
* rw:
|
|
23
|
-
* reloadInterval: { minutes: 5 }
|
|
24
|
-
* ```
|
|
25
|
-
*/
|
|
26
|
-
reloadInterval?: import("@backstage/types").HumanDuration;
|
|
27
|
-
/**
|
|
28
|
-
* S3 storage configuration. Shared across all entity sites.
|
|
29
|
-
* Mutually exclusive with `projectDir`.
|
|
30
|
-
*/
|
|
31
|
-
s3?: {
|
|
32
|
-
/** S3 bucket name. */
|
|
33
|
-
bucket: string;
|
|
34
|
-
/** AWS region. */
|
|
35
|
-
region?: string;
|
|
36
|
-
/** Custom S3 endpoint URL. */
|
|
37
|
-
endpoint?: string;
|
|
38
|
-
/** Root path within the bucket. */
|
|
39
|
-
bucketRootPath?: string;
|
|
40
|
-
/** AWS access key ID. */
|
|
41
|
-
accessKeyId?: string;
|
|
42
|
-
/**
|
|
43
|
-
* AWS secret access key.
|
|
44
|
-
* @visibility secret
|
|
45
|
-
*/
|
|
46
|
-
secretAccessKey?: string;
|
|
47
|
-
};
|
|
48
|
-
/** Diagram rendering configuration. Shared across all sites. */
|
|
49
|
-
diagrams?: {
|
|
50
|
-
/** Kroki server URL for rendering diagrams. */
|
|
51
|
-
krokiUrl?: string;
|
|
52
|
-
/** Diagram rendering DPI. */
|
|
53
|
-
dpi?: number;
|
|
54
|
-
};
|
|
55
|
-
};
|
|
56
|
-
}
|
package/dist/entityPath.cjs.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var catalogModel = require('@backstage/catalog-model');
|
|
4
|
-
require('@backstage/errors');
|
|
5
|
-
|
|
6
|
-
function toEntityPath(entityRef) {
|
|
7
|
-
const ref = catalogModel.parseEntityRef(entityRef);
|
|
8
|
-
return `${ref.namespace}/${ref.kind}/${ref.name}`.toLocaleLowerCase("en-US");
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
exports.toEntityPath = toEntityPath;
|
|
12
|
-
//# sourceMappingURL=entityPath.cjs.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"entityPath.cjs.js","sources":["../src/entityPath.ts"],"sourcesContent":["import { parseEntityRef } from \"@backstage/catalog-model\";\nimport { InputError } from \"@backstage/errors\";\n\n/**\n * Converts a Backstage entity ref (e.g. \"component:default/arch\") to the\n * slash-delimited, lowercased path format used in URLs and cache keys\n * (e.g. \"default/component/arch\").\n *\n * Uses namespace/kind/name ordering to match Backstage catalog URL convention.\n *\n * NOTE: The frontend plugin has a similar utility at\n * plugins/rw/src/components/entityPath.ts — keep in sync if changing logic.\n */\nexport function toEntityPath(entityRef: string): string {\n const ref = parseEntityRef(entityRef);\n return `${ref.namespace}/${ref.kind}/${ref.name}`.toLocaleLowerCase(\"en-US\");\n}\n\n/**\n * Converts a slash-delimited entity path (e.g. \"default/component/arch\")\n * back to the standard Backstage entity ref format (e.g. \"component:default/arch\").\n *\n * This is the inverse of `toEntityPath`. Note that the round-trip always\n * produces lowercased refs since `toEntityPath` lowercases its output.\n */\nexport function fromEntityPath(path: string): string {\n const parts = path.split(\"/\");\n if (parts.length !== 3 || parts.some((p) => !p)) {\n throw new InputError(`Invalid entity path: \"${path}\" (expected \"namespace/kind/name\")`);\n }\n const [namespace, kind, name] = parts;\n return `${kind}:${namespace}/${name}`;\n}\n"],"names":["parseEntityRef"],"mappings":";;;;;AAaO,SAAS,aAAa,SAAA,EAA2B;AACtD,EAAA,MAAM,GAAA,GAAMA,4BAAe,SAAS,CAAA;AACpC,EAAA,OAAO,CAAA,EAAG,GAAA,CAAI,SAAS,CAAA,CAAA,EAAI,GAAA,CAAI,IAAI,CAAA,CAAA,EAAI,GAAA,CAAI,IAAI,CAAA,CAAA,CAAG,iBAAA,CAAkB,OAAO,CAAA;AAC7E;;;;"}
|