@nospt/backstage-plugin-apigee 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +98 -0
  2. package/config.d.ts +30 -0
  3. package/dist/components/ApiCard.esm.js +105 -0
  4. package/dist/components/ApiCard.esm.js.map +1 -0
  5. package/dist/components/ApiCard.module.css.esm.js +8 -0
  6. package/dist/components/ApiCard.module.css.esm.js.map +1 -0
  7. package/dist/components/ApiCardGrid.esm.js +14 -0
  8. package/dist/components/ApiCardGrid.esm.js.map +1 -0
  9. package/dist/components/ApiDetailsDrawer.esm.js +709 -0
  10. package/dist/components/ApiDetailsDrawer.esm.js.map +1 -0
  11. package/dist/components/ApiDetailsDrawer.module.css.esm.js +8 -0
  12. package/dist/components/ApiDetailsDrawer.module.css.esm.js.map +1 -0
  13. package/dist/components/ApiTable.esm.js +184 -0
  14. package/dist/components/ApiTable.esm.js.map +1 -0
  15. package/dist/components/ApiTable.module.css.esm.js +8 -0
  16. package/dist/components/ApiTable.module.css.esm.js.map +1 -0
  17. package/dist/components/ApigeeApiCatalogPage.esm.js +245 -0
  18. package/dist/components/ApigeeApiCatalogPage.esm.js.map +1 -0
  19. package/dist/components/ApigeeHomePage.esm.js +190 -0
  20. package/dist/components/ApigeeHomePage.esm.js.map +1 -0
  21. package/dist/components/ApigeeHomePage.module.css.esm.js +8 -0
  22. package/dist/components/ApigeeHomePage.module.css.esm.js.map +1 -0
  23. package/dist/components/ApigeeNavContent.esm.js +28 -0
  24. package/dist/components/ApigeeNavContent.esm.js.map +1 -0
  25. package/dist/components/CountCards.esm.js +120 -0
  26. package/dist/components/CountCards.esm.js.map +1 -0
  27. package/dist/components/CountCards.module.css.esm.js +8 -0
  28. package/dist/components/CountCards.module.css.esm.js.map +1 -0
  29. package/dist/components/FilterToolbar.esm.js +131 -0
  30. package/dist/components/FilterToolbar.esm.js.map +1 -0
  31. package/dist/components/FilterToolbar.module.css.esm.js +8 -0
  32. package/dist/components/FilterToolbar.module.css.esm.js.map +1 -0
  33. package/dist/components/SearchBar.esm.js +20 -0
  34. package/dist/components/SearchBar.esm.js.map +1 -0
  35. package/dist/components/apiCardViewModel.esm.js +57 -0
  36. package/dist/components/apiCardViewModel.esm.js.map +1 -0
  37. package/dist/components/apiDetailViewModel.esm.js +98 -0
  38. package/dist/components/apiDetailViewModel.esm.js.map +1 -0
  39. package/dist/components/apiOperations.esm.js +196 -0
  40. package/dist/components/apiOperations.esm.js.map +1 -0
  41. package/dist/components/apigeeFacetFilters.esm.js +62 -0
  42. package/dist/components/apigeeFacetFilters.esm.js.map +1 -0
  43. package/dist/components/apigeeOrgFilter.esm.js +11 -0
  44. package/dist/components/apigeeOrgFilter.esm.js.map +1 -0
  45. package/dist/components/apigeeSearchParams.esm.js +55 -0
  46. package/dist/components/apigeeSearchParams.esm.js.map +1 -0
  47. package/dist/components/brandIcons.esm.js +36 -0
  48. package/dist/components/brandIcons.esm.js.map +1 -0
  49. package/dist/components/codeHighlight.esm.js +71 -0
  50. package/dist/components/codeHighlight.esm.js.map +1 -0
  51. package/dist/components/codeSnippet.esm.js +128 -0
  52. package/dist/components/codeSnippet.esm.js.map +1 -0
  53. package/dist/components/facetModel.esm.js +86 -0
  54. package/dist/components/facetModel.esm.js.map +1 -0
  55. package/dist/components/grouping.esm.js +6 -0
  56. package/dist/components/grouping.esm.js.map +1 -0
  57. package/dist/components/states.esm.js +65 -0
  58. package/dist/components/states.esm.js.map +1 -0
  59. package/dist/components/useApigeeFilters.esm.js +191 -0
  60. package/dist/components/useApigeeFilters.esm.js.map +1 -0
  61. package/dist/components/useDebouncedValue.esm.js +13 -0
  62. package/dist/components/useDebouncedValue.esm.js.map +1 -0
  63. package/dist/constants.esm.js +9 -0
  64. package/dist/constants.esm.js.map +1 -0
  65. package/dist/index.d.ts +203 -0
  66. package/dist/index.esm.js +2 -0
  67. package/dist/index.esm.js.map +1 -0
  68. package/dist/node_modules_dist/style-inject/dist/style-inject.es.esm.js +29 -0
  69. package/dist/node_modules_dist/style-inject/dist/style-inject.es.esm.js.map +1 -0
  70. package/dist/plugin.esm.js +59 -0
  71. package/dist/plugin.esm.js.map +1 -0
  72. package/dist/routes.esm.js +8 -0
  73. package/dist/routes.esm.js.map +1 -0
  74. package/package.json +95 -0
@@ -0,0 +1,196 @@
1
+ import { parse } from 'yaml';
2
+
3
+ const HTTP_METHODS = /* @__PURE__ */ new Set([
4
+ "get",
5
+ "put",
6
+ "post",
7
+ "delete",
8
+ "options",
9
+ "head",
10
+ "patch",
11
+ "trace"
12
+ ]);
13
+ function asRecord(value) {
14
+ return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
15
+ }
16
+ function asArray(value) {
17
+ return Array.isArray(value) ? value : void 0;
18
+ }
19
+ function asString(value) {
20
+ if (value === null || value === void 0) return void 0;
21
+ const s = typeof value === "string" ? value : String(value);
22
+ return s.trim() || void 0;
23
+ }
24
+ function asBool(value) {
25
+ return value === true || value === "true";
26
+ }
27
+ function firstLine(value) {
28
+ return value?.split(/\r?\n/)[0]?.trim() || void 0;
29
+ }
30
+ function resolveRef(root, ref) {
31
+ if (!root || !ref.startsWith("#/")) return void 0;
32
+ let node = root;
33
+ for (const raw of ref.slice(2).split("/")) {
34
+ const key = raw.replace(/~1/g, "/").replace(/~0/g, "~");
35
+ const rec = asRecord(node);
36
+ if (!rec) return void 0;
37
+ node = rec[key];
38
+ }
39
+ return asRecord(node);
40
+ }
41
+ function derefRecord(value, root) {
42
+ let current = value;
43
+ for (let hops = 0; hops < 5 && current; hops++) {
44
+ const ref = asString(current.$ref);
45
+ if (!ref) return current;
46
+ current = resolveRef(root, ref);
47
+ }
48
+ return current;
49
+ }
50
+ function toJsonText(value) {
51
+ if (value === void 0) return void 0;
52
+ try {
53
+ return JSON.stringify(value, null, 2);
54
+ } catch {
55
+ return void 0;
56
+ }
57
+ }
58
+ function deriveExample(schema, root, depth) {
59
+ if (!schema || depth > 8) return void 0;
60
+ const node = derefRecord(schema, root);
61
+ if (!node) return void 0;
62
+ if (node.example !== void 0) return node.example;
63
+ if (node.default !== void 0) return node.default;
64
+ const enumList = asArray(node.enum);
65
+ if (enumList?.length) return enumList[0];
66
+ const allOf = asArray(node.allOf);
67
+ if (allOf) {
68
+ const merged = {};
69
+ for (const part of allOf) {
70
+ const v = deriveExample(asRecord(part), root, depth + 1);
71
+ if (v && typeof v === "object" && !Array.isArray(v)) {
72
+ Object.assign(merged, v);
73
+ }
74
+ }
75
+ if (Object.keys(merged).length > 0) return merged;
76
+ }
77
+ const type = asString(node.type);
78
+ if (type === "object" || !type && asRecord(node.properties)) {
79
+ const out = {};
80
+ for (const [key, sub] of Object.entries(asRecord(node.properties) ?? {})) {
81
+ const v = deriveExample(asRecord(sub), root, depth + 1);
82
+ if (v !== void 0) out[key] = v;
83
+ }
84
+ return out;
85
+ }
86
+ if (type === "array") {
87
+ const item = deriveExample(asRecord(node.items), root, depth + 1);
88
+ return item === void 0 ? [] : [item];
89
+ }
90
+ if (type === "string") return "string";
91
+ if (type === "integer" || type === "number") return 0;
92
+ if (type === "boolean") return true;
93
+ return void 0;
94
+ }
95
+ function extractBody(content, root) {
96
+ const firstKey = Object.keys(content ?? {})[0];
97
+ if (!firstKey) return void 0;
98
+ const media = derefRecord(asRecord(content?.[firstKey]), root);
99
+ const schema = derefRecord(asRecord(media?.schema), root);
100
+ let example = media?.example;
101
+ if (example === void 0) {
102
+ example = asRecord(asArray(media?.examples)?.[0])?.value;
103
+ }
104
+ if (example === void 0) {
105
+ example = schema?.example;
106
+ }
107
+ if (example === void 0) {
108
+ example = deriveExample(schema, root, 0);
109
+ }
110
+ const body = {
111
+ schema: toJsonText(schema),
112
+ example: toJsonText(example)
113
+ };
114
+ return body.schema || body.example ? body : void 0;
115
+ }
116
+ function extractParameters(list) {
117
+ const out = [];
118
+ for (const item of list ?? []) {
119
+ const rec = asRecord(item);
120
+ const name = asString(rec?.name);
121
+ const location = asString(rec?.in);
122
+ if (!rec || !name || !location) continue;
123
+ out.push({
124
+ name,
125
+ in: location,
126
+ required: asBool(rec.required),
127
+ description: firstLine(asString(rec.description))
128
+ });
129
+ }
130
+ return out;
131
+ }
132
+ function extractRequestBody(rec, root) {
133
+ if (!rec) return void 0;
134
+ const content = asRecord(rec.content);
135
+ return {
136
+ required: asBool(rec.required),
137
+ contentTypes: Object.keys(content ?? {}),
138
+ body: extractBody(content, root)
139
+ };
140
+ }
141
+ function extractResponses(rec, root) {
142
+ return Object.entries(rec ?? {}).map(([status, value]) => {
143
+ const respRec = derefRecord(asRecord(value), root);
144
+ const content = asRecord(respRec?.content);
145
+ return {
146
+ status,
147
+ description: firstLine(asString(respRec?.description)),
148
+ contentTypes: Object.keys(content ?? {}),
149
+ body: extractBody(content, root)
150
+ };
151
+ });
152
+ }
153
+ function firstServerUrl(root) {
154
+ const first = asArray(root?.servers)?.[0];
155
+ return typeof first === "string" ? asString(first) : asString(asRecord(first)?.url);
156
+ }
157
+ function operationsFromPaths(paths, root) {
158
+ const server = firstServerUrl(root);
159
+ const ops = [];
160
+ for (const [path, item] of Object.entries(asRecord(paths) ?? {})) {
161
+ const itemRec = asRecord(item);
162
+ if (!itemRec) continue;
163
+ for (const [key, value] of Object.entries(itemRec)) {
164
+ if (!HTTP_METHODS.has(key.toLowerCase())) continue;
165
+ const opRec = asRecord(value);
166
+ if (!opRec) continue;
167
+ ops.push({
168
+ operationId: asString(opRec.operationId),
169
+ path,
170
+ method: key.toUpperCase(),
171
+ summary: firstLine(asString(opRec.summary)) ?? firstLine(asString(opRec.description)),
172
+ server,
173
+ parameters: extractParameters(asArray(opRec.parameters)),
174
+ requestBody: extractRequestBody(asRecord(opRec.requestBody), root),
175
+ responses: extractResponses(asRecord(opRec.responses), root)
176
+ });
177
+ }
178
+ }
179
+ return ops;
180
+ }
181
+ function parseOperations(definition) {
182
+ if (!definition) return [];
183
+ const trimmed = definition.trim();
184
+ if (!trimmed) return [];
185
+ let root;
186
+ try {
187
+ root = trimmed.startsWith("{") ? JSON.parse(trimmed) : parse(trimmed);
188
+ } catch {
189
+ return [];
190
+ }
191
+ const doc = asRecord(root);
192
+ return operationsFromPaths(doc?.paths, doc);
193
+ }
194
+
195
+ export { parseOperations };
196
+ //# sourceMappingURL=apiOperations.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiOperations.esm.js","sources":["../../src/components/apiOperations.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { parse as parseYaml } from 'yaml';\n\n/** One parameter declared on an operation (from `parameters`). */\nexport interface ApiOperationParameter {\n name: string;\n /** `path`, `header`, `query`, or `cookie`. */\n in: string;\n required: boolean;\n description?: string;\n}\n\n/**\n * A request/response body for one media type: the JSON-Schema object and/or an example\n * payload, both pretty-printed as JSON text for display. Either may be absent.\n */\nexport interface ApiOperationBody {\n /** Pretty-printed JSON schema (resolved through local $refs). */\n schema?: string;\n /** Pretty-printed example payload (from `example`, or derived from the schema). */\n example?: string;\n}\n\n/** The operation's request body (from `requestBody`). */\nexport interface ApiOperationRequestBody {\n required: boolean;\n /** Media types declared under `content`, e.g. `application/json`. */\n contentTypes: string[];\n /** Schema + example for the first declared media type; absent when neither is defined. */\n body?: ApiOperationBody;\n}\n\n/** One declared response (from `responses`, keyed by status code or `default`). */\nexport interface ApiOperationResponse {\n status: string;\n description?: string;\n contentTypes: string[];\n /** Schema + example for the first declared media type; absent when neither is defined. */\n body?: ApiOperationBody;\n}\n\n/** One operation parsed from an OpenAPI spec's `paths`. */\nexport interface ApiOperation {\n /** operationId when the spec declares one; otherwise undefined. */\n operationId?: string;\n /** Path template, e.g. `/customer/billingAccount/v1/search`. */\n path: string;\n /** HTTP method, upper-cased (GET, POST, …). */\n method: string;\n /** summary ?? description, first line only; undefined when neither is set. */\n summary?: string;\n /** Base URL from the spec's first `servers[].url`, when declared. */\n server?: string;\n parameters: ApiOperationParameter[];\n requestBody?: ApiOperationRequestBody;\n responses: ApiOperationResponse[];\n}\n\nconst HTTP_METHODS = new Set([\n 'get',\n 'put',\n 'post',\n 'delete',\n 'options',\n 'head',\n 'patch',\n 'trace',\n]);\n\n// --- OpenAPI extraction, shared regardless of whether the source was JSON or YAML ----------\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n return value && typeof value === 'object' && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\nfunction asArray(value: unknown): unknown[] | undefined {\n return Array.isArray(value) ? value : undefined;\n}\n\nfunction asString(value: unknown): string | undefined {\n if (value === null || value === undefined) return undefined;\n const s = typeof value === 'string' ? value : String(value);\n return s.trim() || undefined;\n}\n\nfunction asBool(value: unknown): boolean {\n return value === true || value === 'true';\n}\n\n/** First line of a (possibly multi-line) string, trimmed; undefined when empty/non-string. */\nfunction firstLine(value: string | undefined): string | undefined {\n return value?.split(/\\r?\\n/)[0]?.trim() || undefined;\n}\n\n/** Resolve a local `#/a/b/c` JSON-pointer `$ref` against the full parsed document. */\nfunction resolveRef(\n root: Record<string, unknown> | undefined,\n ref: string,\n): Record<string, unknown> | undefined {\n if (!root || !ref.startsWith('#/')) return undefined;\n let node: unknown = root;\n for (const raw of ref.slice(2).split('/')) {\n const key = raw.replace(/~1/g, '/').replace(/~0/g, '~');\n const rec = asRecord(node);\n if (!rec) return undefined;\n node = rec[key];\n }\n return asRecord(node);\n}\n\n/**\n * Follow a `$ref` chain (e.g. a response entirely defined as `$ref: \"#/components/responses/\n * BadRequest\"`) to the object it points at, bounded to guard against a circular reference.\n * Returns the value unchanged once it isn't a `$ref`.\n */\nfunction derefRecord(\n value: Record<string, unknown> | undefined,\n root: Record<string, unknown> | undefined,\n): Record<string, unknown> | undefined {\n let current = value;\n for (let hops = 0; hops < 5 && current; hops++) {\n const ref = asString(current.$ref);\n if (!ref) return current;\n current = resolveRef(root, ref);\n }\n return current;\n}\n\n// --- Schema/example extraction for request/response bodies -----------------------------\n\n/** Pretty-print a value as JSON text; undefined when it won't serialise. */\nfunction toJsonText(value: unknown): string | undefined {\n if (value === undefined) return undefined;\n try {\n return JSON.stringify(value, null, 2);\n } catch {\n return undefined; // circular structure — leave undefined rather than throw\n }\n}\n\n/**\n * Derive a plausible example value from a JSON-Schema node. Prefers a declared `example`\n * (or `default` / single `enum`); otherwise recurses into `properties` / `items`, resolves\n * local `$ref`s, and folds `allOf` object parts. Falls back to a type placeholder\n * (`\"string\"`, `0`, `true`, `{}`, …). Bounded against circular schemas.\n */\nfunction deriveExample(\n schema: Record<string, unknown> | undefined,\n root: Record<string, unknown> | undefined,\n depth: number,\n): unknown {\n if (!schema || depth > 8) return undefined;\n const node = derefRecord(schema, root);\n if (!node) return undefined;\n if (node.example !== undefined) return node.example;\n if (node.default !== undefined) return node.default;\n const enumList = asArray(node.enum);\n if (enumList?.length) return enumList[0];\n\n // Fold allOf parts (objects merge; a later part's property wins) before the type switch.\n const allOf = asArray(node.allOf);\n if (allOf) {\n const merged: Record<string, unknown> = {};\n for (const part of allOf) {\n const v = deriveExample(asRecord(part), root, depth + 1);\n if (v && typeof v === 'object' && !Array.isArray(v)) {\n Object.assign(merged, v);\n }\n }\n if (Object.keys(merged).length > 0) return merged;\n }\n\n const type = asString(node.type);\n if (type === 'object' || (!type && asRecord(node.properties))) {\n const out: Record<string, unknown> = {};\n for (const [key, sub] of Object.entries(asRecord(node.properties) ?? {})) {\n const v = deriveExample(asRecord(sub), root, depth + 1);\n if (v !== undefined) out[key] = v;\n }\n return out;\n }\n if (type === 'array') {\n const item = deriveExample(asRecord(node.items), root, depth + 1);\n return item === undefined ? [] : [item];\n }\n if (type === 'string') return 'string';\n if (type === 'integer' || type === 'number') return 0;\n if (type === 'boolean') return true;\n return undefined;\n}\n\n/**\n * Build the displayable body for the first media type of an operation/response `content`\n * map: the `$ref`-resolved schema pretty-printed, plus an example — from the media type's\n * `example` / first `examples` entry, the schema's own `example`, or one derived from the\n * schema. Returns undefined when neither a schema nor an example is defined.\n */\nfunction extractBody(\n content: Record<string, unknown> | undefined,\n root: Record<string, unknown> | undefined,\n): ApiOperationBody | undefined {\n const firstKey = Object.keys(content ?? {})[0];\n if (!firstKey) return undefined;\n const media = derefRecord(asRecord(content?.[firstKey]), root);\n const schema = derefRecord(asRecord(media?.schema), root);\n\n let example: unknown = media?.example;\n if (example === undefined) {\n example = asRecord(asArray(media?.examples)?.[0])?.value;\n }\n if (example === undefined) {\n example = schema?.example;\n }\n if (example === undefined) {\n example = deriveExample(schema, root, 0);\n }\n\n const body: ApiOperationBody = {\n schema: toJsonText(schema),\n example: toJsonText(example),\n };\n return body.schema || body.example ? body : undefined;\n}\n\nfunction extractParameters(\n list: unknown[] | undefined,\n): ApiOperationParameter[] {\n const out: ApiOperationParameter[] = [];\n for (const item of list ?? []) {\n const rec = asRecord(item);\n const name = asString(rec?.name);\n const location = asString(rec?.in);\n if (!rec || !name || !location) continue; // skip unresolved $ref-only entries\n out.push({\n name,\n in: location,\n required: asBool(rec.required),\n description: firstLine(asString(rec.description)),\n });\n }\n return out;\n}\n\nfunction extractRequestBody(\n rec: Record<string, unknown> | undefined,\n root: Record<string, unknown> | undefined,\n): ApiOperationRequestBody | undefined {\n if (!rec) return undefined;\n const content = asRecord(rec.content);\n return {\n required: asBool(rec.required),\n contentTypes: Object.keys(content ?? {}),\n body: extractBody(content, root),\n };\n}\n\nfunction extractResponses(\n rec: Record<string, unknown> | undefined,\n root: Record<string, unknown> | undefined,\n): ApiOperationResponse[] {\n return Object.entries(rec ?? {}).map(([status, value]) => {\n const respRec = derefRecord(asRecord(value), root);\n const content = asRecord(respRec?.content);\n return {\n status,\n description: firstLine(asString(respRec?.description)),\n contentTypes: Object.keys(content ?? {}),\n body: extractBody(content, root),\n };\n });\n}\n\n/** First declared `servers[].url` (a plain string or `{url}` object), if any. */\nfunction firstServerUrl(\n root: Record<string, unknown> | undefined,\n): string | undefined {\n const first = asArray(root?.servers)?.[0];\n // Only a genuine string counts as the URL directly — an object must be read via `.url`,\n // otherwise `String(obj)` would coerce it to \"[object Object]\".\n return typeof first === 'string'\n ? asString(first)\n : asString(asRecord(first)?.url);\n}\n\n/** Enumerate operations from a parsed OpenAPI document's `paths` object. */\nfunction operationsFromPaths(\n paths: unknown,\n root: Record<string, unknown> | undefined,\n): ApiOperation[] {\n const server = firstServerUrl(root);\n const ops: ApiOperation[] = [];\n for (const [path, item] of Object.entries(asRecord(paths) ?? {})) {\n const itemRec = asRecord(item);\n if (!itemRec) continue;\n for (const [key, value] of Object.entries(itemRec)) {\n if (!HTTP_METHODS.has(key.toLowerCase())) continue;\n const opRec = asRecord(value);\n if (!opRec) continue;\n ops.push({\n operationId: asString(opRec.operationId),\n path,\n method: key.toUpperCase(),\n summary:\n firstLine(asString(opRec.summary)) ??\n firstLine(asString(opRec.description)),\n server,\n parameters: extractParameters(asArray(opRec.parameters)),\n requestBody: extractRequestBody(asRecord(opRec.requestBody), root),\n responses: extractResponses(asRecord(opRec.responses), root),\n });\n }\n }\n return ops;\n}\n\n/**\n * Extract the operation list (with parameters, request body, and responses) from an OpenAPI\n * spec. JSON is parsed with `JSON.parse`; YAML with the `yaml` package. Returns [] when the\n * spec is absent, unparseable, or declares no operations.\n */\nexport function parseOperations(\n definition: string | undefined,\n): ApiOperation[] {\n if (!definition) return [];\n const trimmed = definition.trim();\n if (!trimmed) return [];\n\n let root: unknown;\n try {\n root = trimmed.startsWith('{') ? JSON.parse(trimmed) : parseYaml(trimmed);\n } catch {\n return [];\n }\n const doc = asRecord(root);\n return operationsFromPaths(doc?.paths, doc);\n}\n"],"names":["parseYaml"],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ import { parseEntityRef } from '@backstage/catalog-model';
2
+ import { humanizeEntityRef } from '@backstage/plugin-catalog-react';
3
+ import { ANNOTATION_APIGEE_ORGS, ANNOTATION_APIGEE_ORG, LABEL_DEPARTMENT } from '../constants.esm.js';
4
+
5
+ function isApigeeEntity(e) {
6
+ const a = e.metadata.annotations ?? {};
7
+ return ANNOTATION_APIGEE_ORGS in a || ANNOTATION_APIGEE_ORG in a;
8
+ }
9
+ const entityLifecycle = (e) => e.spec?.lifecycle?.trim() || void 0;
10
+ const entityOwnerLabel = (e) => {
11
+ const owner = e.spec?.owner;
12
+ return owner ? humanizeEntityRef(
13
+ parseEntityRef(owner, {
14
+ defaultKind: "group",
15
+ defaultNamespace: "default"
16
+ }),
17
+ { defaultKind: "group" }
18
+ ) : void 0;
19
+ };
20
+ const entityOrg = (e) => {
21
+ const a = e.metadata.annotations ?? {};
22
+ return (a[ANNOTATION_APIGEE_ORGS] ?? a[ANNOTATION_APIGEE_ORG])?.split(",")[0]?.trim() || void 0;
23
+ };
24
+ const entityDepartment = (e) => (e.metadata.labels ?? {})[LABEL_DEPARTMENT]?.trim() || void 0;
25
+ class FieldFacetFilter {
26
+ constructor(extract, values) {
27
+ this.extract = extract;
28
+ this.values = values;
29
+ }
30
+ extract;
31
+ values;
32
+ filterEntity(entity) {
33
+ const v = this.extract(entity);
34
+ return v !== void 0 && this.values.includes(v);
35
+ }
36
+ }
37
+ function apiSearchHaystack(e) {
38
+ return [
39
+ e.metadata.title ?? e.metadata.name,
40
+ e.metadata.description ?? "",
41
+ entityOwnerLabel(e) ?? "",
42
+ entityOrg(e) ?? ""
43
+ ].join(" ").toLowerCase();
44
+ }
45
+ class ApiSearchFilter {
46
+ needle;
47
+ constructor(query) {
48
+ this.needle = query.trim().toLowerCase();
49
+ }
50
+ filterEntity(entity) {
51
+ return this.needle === "" || apiSearchHaystack(entity).includes(this.needle);
52
+ }
53
+ }
54
+ class ApiKindFilter {
55
+ value = "API";
56
+ getCatalogFilters() {
57
+ return { kind: this.value };
58
+ }
59
+ }
60
+
61
+ export { ApiKindFilter, ApiSearchFilter, FieldFacetFilter, apiSearchHaystack, entityDepartment, entityLifecycle, entityOrg, entityOwnerLabel, isApigeeEntity };
62
+ //# sourceMappingURL=apigeeFacetFilters.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apigeeFacetFilters.esm.js","sources":["../../src/components/apigeeFacetFilters.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { type Entity, parseEntityRef } from '@backstage/catalog-model';\nimport type { EntityFilter } from '@backstage/plugin-catalog-react';\nimport { humanizeEntityRef } from '@backstage/plugin-catalog-react';\nimport {\n ANNOTATION_APIGEE_ORGS,\n ANNOTATION_APIGEE_ORG,\n LABEL_DEPARTMENT,\n} from '../constants';\n\n/** Dual-key is-Apigee gate (same rule as the existing ApigeeOrgFilter). */\nexport function isApigeeEntity(e: Entity): boolean {\n const a = e.metadata.annotations ?? {};\n return ANNOTATION_APIGEE_ORGS in a || ANNOTATION_APIGEE_ORG in a;\n}\n\n// --- Pure field extractors: the ONE place each facet's value is defined. Reused by\n// deriveFacets(), the facet predicates, and the search haystack. Unit-testable. ---\nexport const entityLifecycle = (e: Entity): string | undefined =>\n (e.spec?.lifecycle as string | undefined)?.trim() || undefined;\n\nexport const entityOwnerLabel = (e: Entity): string | undefined => {\n const owner = e.spec?.owner as string | undefined;\n return owner\n ? humanizeEntityRef(\n parseEntityRef(owner, {\n defaultKind: 'group',\n defaultNamespace: 'default',\n }),\n { defaultKind: 'group' },\n ) // 'group:default/payments-team' -> 'payments-team'\n : undefined;\n};\n\nexport const entityOrg = (e: Entity): string | undefined => {\n const a = e.metadata.annotations ?? {};\n return (\n (a[ANNOTATION_APIGEE_ORGS] ?? a[ANNOTATION_APIGEE_ORG])\n ?.split(',')[0]\n ?.trim() || undefined\n );\n};\n\n/** Department facet value: the `department` label written from the API Hub \"Business unit\". */\nexport const entityDepartment = (e: Entity): string | undefined =>\n (e.metadata.labels ?? {})[LABEL_DEPARTMENT]?.trim() || undefined;\n\n/** Generic single-field multi-value facet. Within-facet OR. No URL seam / getCatalogFilters. */\nexport class FieldFacetFilter implements EntityFilter {\n constructor(\n private readonly extract: (e: Entity) => string | undefined,\n public readonly values: string[],\n ) {}\n filterEntity(entity: Entity): boolean {\n const v = this.extract(entity);\n return v !== undefined && this.values.includes(v); // OR across selected values\n }\n}\n\n/** Broad \"find anything\" search: name + description + owner + org (D-09). */\nexport function apiSearchHaystack(e: Entity): string {\n return [\n e.metadata.title ?? e.metadata.name,\n e.metadata.description ?? '',\n entityOwnerLabel(e) ?? '',\n entityOrg(e) ?? '',\n ]\n .join(' ')\n .toLowerCase();\n}\n\nexport class ApiSearchFilter implements EntityFilter {\n private readonly needle: string;\n constructor(query: string) {\n this.needle = query.trim().toLowerCase();\n }\n filterEntity(entity: Entity): boolean {\n // String.includes ONLY — never build a RegExp from user input (ReDoS guard).\n return (\n this.needle === '' || apiSearchHaystack(entity).includes(this.needle)\n );\n }\n}\n\n/**\n * Replaces EntityKindFilter('api','API'): keeps the backend kind=API filter but exposes no\n * URL seam, so the provider does not serialize `filters[kind]=API` into the URL (D-06).\n */\nexport class ApiKindFilter implements EntityFilter {\n readonly value = 'API';\n getCatalogFilters(): Record<string, string> {\n return { kind: this.value };\n }\n}\n"],"names":[],"mappings":";;;;AAWO,SAAS,eAAe,CAAA,EAAoB;AACjD,EAAA,MAAM,CAAA,GAAI,CAAA,CAAE,QAAA,CAAS,WAAA,IAAe,EAAC;AACrC,EAAA,OAAO,sBAAA,IAA0B,KAAK,qBAAA,IAAyB,CAAA;AACjE;AAIO,MAAM,kBAAkB,CAAC,CAAA,KAC7B,EAAE,IAAA,EAAM,SAAA,EAAkC,MAAK,IAAK;AAEhD,MAAM,gBAAA,GAAmB,CAAC,CAAA,KAAkC;AACjE,EAAA,MAAM,KAAA,GAAQ,EAAE,IAAA,EAAM,KAAA;AACtB,EAAA,OAAO,KAAA,GACH,iBAAA;AAAA,IACE,eAAe,KAAA,EAAO;AAAA,MACpB,WAAA,EAAa,OAAA;AAAA,MACb,gBAAA,EAAkB;AAAA,KACnB,CAAA;AAAA,IACD,EAAE,aAAa,OAAA;AAAQ,GACzB,GACA,MAAA;AACN;AAEO,MAAM,SAAA,GAAY,CAAC,CAAA,KAAkC;AAC1D,EAAA,MAAM,CAAA,GAAI,CAAA,CAAE,QAAA,CAAS,WAAA,IAAe,EAAC;AACrC,EAAA,OAAA,CACG,CAAA,CAAE,sBAAsB,CAAA,IAAK,CAAA,CAAE,qBAAqB,CAAA,GACjD,KAAA,CAAM,GAAG,CAAA,CAAE,CAAC,CAAA,EACZ,IAAA,EAAK,IAAK,MAAA;AAElB;AAGO,MAAM,gBAAA,GAAmB,CAAC,CAAA,KAAA,CAC9B,CAAA,CAAE,QAAA,CAAS,MAAA,IAAU,EAAC,EAAG,gBAAgB,CAAA,EAAG,IAAA,EAAK,IAAK;AAGlD,MAAM,gBAAA,CAAyC;AAAA,EACpD,WAAA,CACmB,SACD,MAAA,EAChB;AAFiB,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AACD,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EACf;AAAA,EAFgB,OAAA;AAAA,EACD,MAAA;AAAA,EAElB,aAAa,MAAA,EAAyB;AACpC,IAAA,MAAM,CAAA,GAAI,IAAA,CAAK,OAAA,CAAQ,MAAM,CAAA;AAC7B,IAAA,OAAO,CAAA,KAAM,MAAA,IAAa,IAAA,CAAK,MAAA,CAAO,SAAS,CAAC,CAAA;AAAA,EAClD;AACF;AAGO,SAAS,kBAAkB,CAAA,EAAmB;AACnD,EAAA,OAAO;AAAA,IACL,CAAA,CAAE,QAAA,CAAS,KAAA,IAAS,CAAA,CAAE,QAAA,CAAS,IAAA;AAAA,IAC/B,CAAA,CAAE,SAAS,WAAA,IAAe,EAAA;AAAA,IAC1B,gBAAA,CAAiB,CAAC,CAAA,IAAK,EAAA;AAAA,IACvB,SAAA,CAAU,CAAC,CAAA,IAAK;AAAA,GAClB,CACG,IAAA,CAAK,GAAG,CAAA,CACR,WAAA,EAAY;AACjB;AAEO,MAAM,eAAA,CAAwC;AAAA,EAClC,MAAA;AAAA,EACjB,YAAY,KAAA,EAAe;AACzB,IAAA,IAAA,CAAK,MAAA,GAAS,KAAA,CAAM,IAAA,EAAK,CAAE,WAAA,EAAY;AAAA,EACzC;AAAA,EACA,aAAa,MAAA,EAAyB;AAEpC,IAAA,OACE,IAAA,CAAK,WAAW,EAAA,IAAM,iBAAA,CAAkB,MAAM,CAAA,CAAE,QAAA,CAAS,KAAK,MAAM,CAAA;AAAA,EAExE;AACF;AAMO,MAAM,aAAA,CAAsC;AAAA,EACxC,KAAA,GAAQ,KAAA;AAAA,EACjB,iBAAA,GAA4C;AAC1C,IAAA,OAAO,EAAE,IAAA,EAAM,IAAA,CAAK,KAAA,EAAM;AAAA,EAC5B;AACF;;;;"}
@@ -0,0 +1,11 @@
1
+ import { ANNOTATION_APIGEE_ORGS, ANNOTATION_APIGEE_ORG } from '../constants.esm.js';
2
+
3
+ class ApigeeOrgFilter {
4
+ filterEntity(entity) {
5
+ const a = entity.metadata.annotations ?? {};
6
+ return ANNOTATION_APIGEE_ORGS in a || ANNOTATION_APIGEE_ORG in a;
7
+ }
8
+ }
9
+
10
+ export { ApigeeOrgFilter };
11
+ //# sourceMappingURL=apigeeOrgFilter.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apigeeOrgFilter.esm.js","sources":["../../src/components/apigeeOrgFilter.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport type { Entity } from '@backstage/catalog-model';\nimport type { EntityFilter } from '@backstage/plugin-catalog-react';\nimport { ANNOTATION_APIGEE_ORGS, ANNOTATION_APIGEE_ORG } from '../constants';\n\n/**\n * Frontend predicate that keeps an entity when it carries EITHER Apigee org key.\n * MUST match both: proxy-derived APIs use the plural key, hub-only APIs the singular\n * (RESEARCH Pitfall 1 / CAT-05). Uses filterEntity (not getCatalogFilters) because a\n * single backend filter record cannot OR two different annotation keys.\n */\nexport class ApigeeOrgFilter implements EntityFilter {\n filterEntity(entity: Entity): boolean {\n const a = entity.metadata.annotations ?? {};\n return ANNOTATION_APIGEE_ORGS in a || ANNOTATION_APIGEE_ORG in a;\n }\n}\n"],"names":[],"mappings":";;AAWO,MAAM,eAAA,CAAwC;AAAA,EACnD,aAAa,MAAA,EAAyB;AACpC,IAAA,MAAM,CAAA,GAAI,MAAA,CAAO,QAAA,CAAS,WAAA,IAAe,EAAC;AAC1C,IAAA,OAAO,sBAAA,IAA0B,KAAK,qBAAA,IAAyB,CAAA;AAAA,EACjE;AACF;;;;"}
@@ -0,0 +1,55 @@
1
+ import { FACET_ORDER } from './facetModel.esm.js';
2
+
3
+ const QKEY = {
4
+ lifecycle: "lifecycle",
5
+ org: "org",
6
+ owner: "owner",
7
+ department: "department"
8
+ };
9
+ function parseSearchParams(sp) {
10
+ const selected = Object.fromEntries(
11
+ FACET_ORDER.map((k) => [k, sp.getAll(QKEY[k])])
12
+ );
13
+ return { selected, q: sp.get("q") ?? "" };
14
+ }
15
+ function parsePageParam(sp) {
16
+ const raw = sp.get("page");
17
+ const n = raw ? Number.parseInt(raw, 10) : NaN;
18
+ return Number.isFinite(n) && n >= 1 ? n : 1;
19
+ }
20
+ function parsePageSizeParam(sp) {
21
+ const raw = sp.get("size");
22
+ const n = raw ? Number.parseInt(raw, 10) : NaN;
23
+ return Number.isFinite(n) && n >= 1 ? n : void 0;
24
+ }
25
+ function parseApiParam(sp) {
26
+ const raw = sp.get("api")?.trim();
27
+ return raw ? raw : void 0;
28
+ }
29
+ function isActive(state) {
30
+ return state.q.trim() !== "" || FACET_ORDER.some((k) => state.selected[k].length > 0);
31
+ }
32
+ function selectOnlyFacetParams(prev, key, value) {
33
+ const current = prev.getAll(QKEY[key]);
34
+ const isSoleSelection = current.length === 1 && current[0] === value && FACET_ORDER.every((k) => k === key || prev.getAll(QKEY[k]).length === 0);
35
+ const next = new URLSearchParams();
36
+ const q = prev.get("q");
37
+ if (q) {
38
+ next.set("q", q);
39
+ }
40
+ const size = prev.get("size");
41
+ if (size) {
42
+ next.set("size", size);
43
+ }
44
+ const apiRef = prev.get("api");
45
+ if (apiRef) {
46
+ next.set("api", apiRef);
47
+ }
48
+ if (!isSoleSelection) {
49
+ next.append(QKEY[key], value);
50
+ }
51
+ return next;
52
+ }
53
+
54
+ export { isActive, parseApiParam, parsePageParam, parsePageSizeParam, parseSearchParams, selectOnlyFacetParams };
55
+ //# sourceMappingURL=apigeeSearchParams.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apigeeSearchParams.esm.js","sources":["../../src/components/apigeeSearchParams.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { FACET_ORDER, type FacetKey } from './facetModel';\n\nexport interface ApigeeFilterState {\n selected: Record<FacetKey, string[]>;\n q: string;\n}\n\n/** Clean URL keys (D-06): one query key per facet, no native `filters[...]` wrapper. */\nconst QKEY: Record<FacetKey, string> = {\n lifecycle: 'lifecycle',\n org: 'org',\n owner: 'owner',\n department: 'department',\n};\n\n/**\n * URL → state. Repeated keys become arrays (D-07) via `getAll`; unknown/extra keys are\n * ignored. Values are treated as opaque strings — no eval, RegExp, or HTML (T-06-02-01);\n * an unknown facet value simply matches nothing downstream (derived allow-list).\n */\nexport function parseSearchParams(sp: URLSearchParams): ApigeeFilterState {\n const selected = Object.fromEntries(\n FACET_ORDER.map(k => [k, sp.getAll(QKEY[k])]),\n ) as Record<FacetKey, string[]>;\n return { selected, q: sp.get('q') ?? '' };\n}\n\n/**\n * URL `page` param → 1-based page number. Missing, non-numeric, or < 1 → 1, so a\n * hand-edited or stale value degrades to the first page instead of throwing.\n */\nexport function parsePageParam(sp: URLSearchParams): number {\n const raw = sp.get('page');\n const n = raw ? Number.parseInt(raw, 10) : NaN;\n return Number.isFinite(n) && n >= 1 ? n : 1;\n}\n\n/**\n * URL `size` param → page size, or undefined when absent/malformed so the caller can fall\n * back to its configured default. Only positive integers are accepted.\n */\nexport function parsePageSizeParam(sp: URLSearchParams): number | undefined {\n const raw = sp.get('size');\n const n = raw ? Number.parseInt(raw, 10) : NaN;\n return Number.isFinite(n) && n >= 1 ? n : undefined;\n}\n\n/**\n * URL `api` param → the `<namespace>/<name>` ref of the API whose details drawer is open, or\n * undefined when absent. Opaque string (an unknown ref resolves to no entity, leaving the drawer\n * closed) — no eval/RegExp/HTML, so no injection surface.\n */\nexport function parseApiParam(sp: URLSearchParams): string | undefined {\n const raw = sp.get('api')?.trim();\n return raw ? raw : undefined;\n}\n\n/**\n * state → URL. Deterministic order (FACET_ORDER, then `q` last) reproduces the exact SC #3\n * shape `?lifecycle=deprecated&q=payment`. Only the known clean keys are appended and empties\n * are omitted (T-06-02-02) — no injection surface. Multi-value facets serialize as repeated\n * keys (D-07) via `append`.\n */\nexport function buildSearchParams(state: ApigeeFilterState): URLSearchParams {\n const sp = new URLSearchParams();\n for (const k of FACET_ORDER) {\n for (const v of state.selected[k]) {\n sp.append(QKEY[k], v);\n }\n }\n if (state.q.trim()) {\n sp.set('q', state.q.trim());\n }\n return sp;\n}\n\n/** True when any facet is selected or the search box holds a non-blank query. */\nexport function isActive(state: ApigeeFilterState): boolean {\n return (\n state.q.trim() !== '' || FACET_ORDER.some(k => state.selected[k].length > 0)\n );\n}\n\n/**\n * Single-select URL update for the count cards: the clicked value becomes the ONLY active facet\n * selection (every other facet is cleared), so each card click filters to exactly that value\n * instead of accumulating like the toolbar's multi-select toggle. Clicking the current sole\n * selection clears it. The `q` search query is always preserved.\n */\nexport function selectOnlyFacetParams(\n prev: URLSearchParams,\n key: FacetKey,\n value: string,\n): URLSearchParams {\n const current = prev.getAll(QKEY[key]);\n const isSoleSelection =\n current.length === 1 &&\n current[0] === value &&\n FACET_ORDER.every(k => k === key || prev.getAll(QKEY[k]).length === 0);\n const next = new URLSearchParams();\n const q = prev.get('q');\n if (q) {\n next.set('q', q);\n }\n // Page size and the open details drawer (`api`) are orthogonal view state, not filters —\n // keep them across a count-card selection.\n const size = prev.get('size');\n if (size) {\n next.set('size', size);\n }\n const apiRef = prev.get('api');\n if (apiRef) {\n next.set('api', apiRef);\n }\n if (!isSoleSelection) {\n next.append(QKEY[key], value);\n }\n return next;\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+
3
+ function BackstageIcon({ size = 16, ...props }) {
4
+ return /* @__PURE__ */ jsx(
5
+ "svg",
6
+ {
7
+ width: size,
8
+ height: size,
9
+ viewBox: "0 0 24 24",
10
+ fill: "currentColor",
11
+ "aria-hidden": true,
12
+ ...props,
13
+ children: /* @__PURE__ */ jsx("path", { d: "M19.5188 9.3003a4.5194 4.5194 0 0 0 .7533-.5808 7.0485 7.0485 0 0 0 .1289-.1288 4.6555 4.6555 0 0 0 .4441-.5254 3.5672 3.5672 0 0 0 .354-.6032 2.721 2.721 0 0 0 .2436-.9185c.0835-1.086-.56-2.166-1.995-3.0369L13.6692 0 6.9335 6.454 2.548 10.6558l6.0826 3.6916a6.25 6.25 0 0 0 3.2351.919c1.3958 0 2.7333-.4928 3.7201-1.4388 1.0732-1.0283 1.4294-2.3591.7674-3.4653a2.7568 2.7568 0 0 0-.3809-.4968 4.9944 4.9944 0 0 0 1.0821.1204h.0084a4.7653 4.7653 0 0 0 1.7363-.3243 4.53 4.53 0 0 0 .7197-.3613Zm-5.6317 3.35c-1.0821 1.0367-2.8229 1.189-4.24.3304l-4.2085-2.5546 3.7779-3.6204 4.2796 2.5977c1.542.9348 1.4574 2.2247.391 3.2469zm.5001-4.6051-3.957-2.4023 3.6075-3.4585 3.8854 2.358c1.4529.8804 1.6416 2.0723.591 3.0805a3.3992 3.3992 0 0 1-4.1269.4223Zm1.6725 10.6418c-1.0961 1.0501-2.586 1.6293-4.194 1.6293a6.93 6.93 0 0 1-3.5902-1.0194L2.548 15.8238v1.3817l6.0826 3.6916a6.2506 6.2506 0 0 0 3.2351.9197c1.3958 0 2.7333-.4935 3.7201-1.439.7214-.691 1.1202-1.5195 1.1023-2.3237v-.1003a4.888 4.888 0 0 1-.6284.7354zm0-2.1844c-1.0961 1.0502-2.586 1.6287-4.194 1.6287a6.9345 6.9345 0 0 1-3.5902-1.0188L2.548 13.64v1.3824l6.0826 3.6915a6.2506 6.2506 0 0 0 3.2351.9197c1.3958 0 2.7333-.4934 3.7201-1.4389.7214-.6911 1.1202-1.5195 1.1023-2.3244v-.0997a4.888 4.888 0 0 1-.6284.7354zm0-2.1844c-1.0961 1.0508-2.586 1.6293-4.194 1.6293a6.9345 6.9345 0 0 1-3.5902-1.0188l-5.7275-3.472v1.3823l6.0826 3.6916a6.25 6.25 0 0 0 3.2351.9191c1.3958 0 2.7333-.4929 3.7201-1.4383.7214-.6917 1.1202-1.5195 1.1023-2.3244v-.102a4.8218 4.8218 0 0 1-.6284.7371zm4.688 1.4417a5.2733 5.2733 0 0 1-3.3718 1.4484v1.4988a4.5704 4.5704 0 0 0 2.898-1.253c.784-.7493 1.184-1.5743 1.1761-2.3882v-.1226a5.3164 5.3164 0 0 1-.7023.8205zm-4.688 5.1086c-1.0961 1.0508-2.586 1.6293-4.194 1.6293a6.93 6.93 0 0 1-3.5902-1.0193L2.548 18.007v1.3823l6.0826 3.691A6.2506 6.2506 0 0 0 11.8657 24c1.3958 0 2.7333-.4929 3.7201-1.4389.7214-.6911 1.1202-1.5195 1.1023-2.3238v-.1003a4.833 4.833 0 0 1-.6284.7354zM20.863 9.0987c-.038.0386-.0756.0773-.1148.1154a5.2856 5.2856 0 0 1-.596.4845 5.1165 5.1165 0 0 1-.6496.392 5.2946 5.2946 0 0 1-1.4697.4879 5.4716 5.4716 0 0 1-.7931.088 3.07 3.07 0 0 1 .07.2744 2.9836 2.9836 0 0 1 .0638.672v.5411h.0032a4.7277 4.7277 0 0 0 1.4198-.3108 4.5368 4.5368 0 0 0 1.478-.9421c.0449-.043.084-.0874.1267-.131a4.5972 4.5972 0 0 0 .4441-.5254 3.5712 3.5712 0 0 0 .354-.6016 2.7193 2.7193 0 0 0 .242-.9213c.0046-.0689.0111-.1384.0106-.2073v-.1131q-.0605.0863-.126.173a5.4178 5.4178 0 0 1-.4627.5243zm0 4.3643c-.0386.0392-.0733.079-.112.1176a5.276 5.276 0 0 1-3.3717 1.4484v1.4938a4.5704 4.5704 0 0 0 2.8979-1.253 4.1172 4.1172 0 0 0 .9219-1.2602 2.7198 2.7198 0 0 0 .242-.9208c.0047-.0694.0111-.1383.0106-.2078v-.1226c-.0414.06-.0818.1204-.1272.1798a5.359 5.359 0 0 1-.4615.5248zm0-2.1844c-.0386.0392-.0733.079-.112.1176a5.219 5.219 0 0 1-.5954.4845 5.1321 5.1321 0 0 1-.6513.392 5.3472 5.3472 0 0 1-2.125.5702v1.4955a4.5732 4.5732 0 0 0 2.8979-1.253c.0448-.0431.084-.0873.1266-.131a4.5972 4.5972 0 0 0 .4441-.5254 3.5712 3.5712 0 0 0 .3512-.6026 2.7198 2.7198 0 0 0 .242-.9208c.0047-.0694.0111-.139.0106-.2078v-.1232c-.0414.0604-.0818.121-.1272.1803a5.359 5.359 0 0 1-.4615.5248z" })
14
+ }
15
+ );
16
+ }
17
+ function ApigeeIcon({ size = 16, ...props }) {
18
+ return /* @__PURE__ */ jsx(
19
+ "svg",
20
+ {
21
+ width: size,
22
+ height: size,
23
+ viewBox: "0 0 24 24",
24
+ fill: "currentColor",
25
+ "aria-hidden": true,
26
+ ...props,
27
+ children: /* @__PURE__ */ jsxs("g", { fillRule: "evenodd", children: [
28
+ /* @__PURE__ */ jsx("path", { d: "m5.2 12l1.32-1.32l-1.79-1.79a2.94 2.94 0 0 1 2.08-5a2.92 2.92 0 0 1 2.07.86l1.8 1.79L12 5.2l-1.79-1.79a4.81 4.81 0 1 0-6.8 6.8Zm13.6 0l-1.32 1.32l1.79 1.8a2.94 2.94 0 0 1-4.16 4.15l-1.79-1.79L12 18.8l1.79 1.79a4.81 4.81 0 1 0 6.8-6.8Z" }),
29
+ /* @__PURE__ */ jsx("path", { d: "M8.89 19.27a2.94 2.94 0 0 1-4.16-4.16l1.79-1.79l4.16 4.16Zm6.23-14.54a2.93 2.93 0 0 1 5 2.08a3 3 0 0 1-.86 2.08l-1.79 1.79l-4.15-4.16ZM12 15.19A3.2 3.2 0 0 1 8.81 12A3.21 3.21 0 0 1 12 8.81A3.21 3.21 0 0 1 15.19 12A3.2 3.2 0 0 1 12 15.19M18.8 12l1.79-1.79a4.81 4.81 0 1 0-6.8-6.8L12 5.2L5.2 12l-1.79 1.79a4.81 4.81 0 1 0 6.8 6.8L12 18.8Z" })
30
+ ] })
31
+ }
32
+ );
33
+ }
34
+
35
+ export { ApigeeIcon, BackstageIcon };
36
+ //# sourceMappingURL=brandIcons.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brandIcons.esm.js","sources":["../../src/components/brandIcons.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport type { SVGProps } from 'react';\n\n// Brand marks for the details-drawer Links section. GitHub uses @remixicon's RiGithubFill;\n// these two have no Remix equivalent so they are inlined: Backstage (simple-icons) and Apigee\n// (Google Cloud icon set), both monochrome so they inherit the link's currentColor.\ntype BrandIconProps = { size?: number } & SVGProps<SVGSVGElement>;\n\nexport function BackstageIcon({ size = 16, ...props }: BrandIconProps) {\n return (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n aria-hidden\n {...props}\n >\n <path d=\"M19.5188 9.3003a4.5194 4.5194 0 0 0 .7533-.5808 7.0485 7.0485 0 0 0 .1289-.1288 4.6555 4.6555 0 0 0 .4441-.5254 3.5672 3.5672 0 0 0 .354-.6032 2.721 2.721 0 0 0 .2436-.9185c.0835-1.086-.56-2.166-1.995-3.0369L13.6692 0 6.9335 6.454 2.548 10.6558l6.0826 3.6916a6.25 6.25 0 0 0 3.2351.919c1.3958 0 2.7333-.4928 3.7201-1.4388 1.0732-1.0283 1.4294-2.3591.7674-3.4653a2.7568 2.7568 0 0 0-.3809-.4968 4.9944 4.9944 0 0 0 1.0821.1204h.0084a4.7653 4.7653 0 0 0 1.7363-.3243 4.53 4.53 0 0 0 .7197-.3613Zm-5.6317 3.35c-1.0821 1.0367-2.8229 1.189-4.24.3304l-4.2085-2.5546 3.7779-3.6204 4.2796 2.5977c1.542.9348 1.4574 2.2247.391 3.2469zm.5001-4.6051-3.957-2.4023 3.6075-3.4585 3.8854 2.358c1.4529.8804 1.6416 2.0723.591 3.0805a3.3992 3.3992 0 0 1-4.1269.4223Zm1.6725 10.6418c-1.0961 1.0501-2.586 1.6293-4.194 1.6293a6.93 6.93 0 0 1-3.5902-1.0194L2.548 15.8238v1.3817l6.0826 3.6916a6.2506 6.2506 0 0 0 3.2351.9197c1.3958 0 2.7333-.4935 3.7201-1.439.7214-.691 1.1202-1.5195 1.1023-2.3237v-.1003a4.888 4.888 0 0 1-.6284.7354zm0-2.1844c-1.0961 1.0502-2.586 1.6287-4.194 1.6287a6.9345 6.9345 0 0 1-3.5902-1.0188L2.548 13.64v1.3824l6.0826 3.6915a6.2506 6.2506 0 0 0 3.2351.9197c1.3958 0 2.7333-.4934 3.7201-1.4389.7214-.6911 1.1202-1.5195 1.1023-2.3244v-.0997a4.888 4.888 0 0 1-.6284.7354zm0-2.1844c-1.0961 1.0508-2.586 1.6293-4.194 1.6293a6.9345 6.9345 0 0 1-3.5902-1.0188l-5.7275-3.472v1.3823l6.0826 3.6916a6.25 6.25 0 0 0 3.2351.9191c1.3958 0 2.7333-.4929 3.7201-1.4383.7214-.6917 1.1202-1.5195 1.1023-2.3244v-.102a4.8218 4.8218 0 0 1-.6284.7371zm4.688 1.4417a5.2733 5.2733 0 0 1-3.3718 1.4484v1.4988a4.5704 4.5704 0 0 0 2.898-1.253c.784-.7493 1.184-1.5743 1.1761-2.3882v-.1226a5.3164 5.3164 0 0 1-.7023.8205zm-4.688 5.1086c-1.0961 1.0508-2.586 1.6293-4.194 1.6293a6.93 6.93 0 0 1-3.5902-1.0193L2.548 18.007v1.3823l6.0826 3.691A6.2506 6.2506 0 0 0 11.8657 24c1.3958 0 2.7333-.4929 3.7201-1.4389.7214-.6911 1.1202-1.5195 1.1023-2.3238v-.1003a4.833 4.833 0 0 1-.6284.7354zM20.863 9.0987c-.038.0386-.0756.0773-.1148.1154a5.2856 5.2856 0 0 1-.596.4845 5.1165 5.1165 0 0 1-.6496.392 5.2946 5.2946 0 0 1-1.4697.4879 5.4716 5.4716 0 0 1-.7931.088 3.07 3.07 0 0 1 .07.2744 2.9836 2.9836 0 0 1 .0638.672v.5411h.0032a4.7277 4.7277 0 0 0 1.4198-.3108 4.5368 4.5368 0 0 0 1.478-.9421c.0449-.043.084-.0874.1267-.131a4.5972 4.5972 0 0 0 .4441-.5254 3.5712 3.5712 0 0 0 .354-.6016 2.7193 2.7193 0 0 0 .242-.9213c.0046-.0689.0111-.1384.0106-.2073v-.1131q-.0605.0863-.126.173a5.4178 5.4178 0 0 1-.4627.5243zm0 4.3643c-.0386.0392-.0733.079-.112.1176a5.276 5.276 0 0 1-3.3717 1.4484v1.4938a4.5704 4.5704 0 0 0 2.8979-1.253 4.1172 4.1172 0 0 0 .9219-1.2602 2.7198 2.7198 0 0 0 .242-.9208c.0047-.0694.0111-.1383.0106-.2078v-.1226c-.0414.06-.0818.1204-.1272.1798a5.359 5.359 0 0 1-.4615.5248zm0-2.1844c-.0386.0392-.0733.079-.112.1176a5.219 5.219 0 0 1-.5954.4845 5.1321 5.1321 0 0 1-.6513.392 5.3472 5.3472 0 0 1-2.125.5702v1.4955a4.5732 4.5732 0 0 0 2.8979-1.253c.0448-.0431.084-.0873.1266-.131a4.5972 4.5972 0 0 0 .4441-.5254 3.5712 3.5712 0 0 0 .3512-.6026 2.7198 2.7198 0 0 0 .242-.9208c.0047-.0694.0111-.139.0106-.2078v-.1232c-.0414.0604-.0818.121-.1272.1803a5.359 5.359 0 0 1-.4615.5248z\" />\n </svg>\n );\n}\n\nexport function ApigeeIcon({ size = 16, ...props }: BrandIconProps) {\n return (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n aria-hidden\n {...props}\n >\n <g fillRule=\"evenodd\">\n <path d=\"m5.2 12l1.32-1.32l-1.79-1.79a2.94 2.94 0 0 1 2.08-5a2.92 2.92 0 0 1 2.07.86l1.8 1.79L12 5.2l-1.79-1.79a4.81 4.81 0 1 0-6.8 6.8Zm13.6 0l-1.32 1.32l1.79 1.8a2.94 2.94 0 0 1-4.16 4.15l-1.79-1.79L12 18.8l1.79 1.79a4.81 4.81 0 1 0 6.8-6.8Z\" />\n <path d=\"M8.89 19.27a2.94 2.94 0 0 1-4.16-4.16l1.79-1.79l4.16 4.16Zm6.23-14.54a2.93 2.93 0 0 1 5 2.08a3 3 0 0 1-.86 2.08l-1.79 1.79l-4.15-4.16ZM12 15.19A3.2 3.2 0 0 1 8.81 12A3.21 3.21 0 0 1 12 8.81A3.21 3.21 0 0 1 15.19 12A3.2 3.2 0 0 1 12 15.19M18.8 12l1.79-1.79a4.81 4.81 0 1 0-6.8-6.8L12 5.2L5.2 12l-1.79 1.79a4.81 4.81 0 1 0 6.8 6.8L12 18.8Z\" />\n </g>\n </svg>\n );\n}\n"],"names":[],"mappings":";;AAQO,SAAS,cAAc,EAAE,IAAA,GAAO,EAAA,EAAI,GAAG,OAAM,EAAmB;AACrE,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,IAAA;AAAA,MACP,MAAA,EAAQ,IAAA;AAAA,MACR,OAAA,EAAQ,WAAA;AAAA,MACR,IAAA,EAAK,cAAA;AAAA,MACL,aAAA,EAAW,IAAA;AAAA,MACV,GAAG,KAAA;AAAA,MAEJ,QAAA,kBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EAAE,slGAAA,EAAulG;AAAA;AAAA,GACjmG;AAEJ;AAEO,SAAS,WAAW,EAAE,IAAA,GAAO,EAAA,EAAI,GAAG,OAAM,EAAmB;AAClE,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,IAAA;AAAA,MACP,MAAA,EAAQ,IAAA;AAAA,MACR,OAAA,EAAQ,WAAA;AAAA,MACR,IAAA,EAAK,cAAA;AAAA,MACL,aAAA,EAAW,IAAA;AAAA,MACV,GAAG,KAAA;AAAA,MAEJ,QAAA,kBAAA,IAAA,CAAC,GAAA,EAAA,EAAE,QAAA,EAAS,SAAA,EACV,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,GAAE,4OAAA,EAA6O,CAAA;AAAA,wBACrP,GAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EAAE,mVAAA,EAAoV;AAAA,OAAA,EAC9V;AAAA;AAAA,GACF;AAEJ;;;;"}
@@ -0,0 +1,71 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import styles from './ApiDetailsDrawer.module.css.esm.js';
3
+
4
+ const KEYWORDS = /* @__PURE__ */ new Set([
5
+ // shared
6
+ "const",
7
+ "let",
8
+ "var",
9
+ "new",
10
+ "await",
11
+ "async",
12
+ "return",
13
+ "import",
14
+ "from",
15
+ "class",
16
+ "function",
17
+ "if",
18
+ "else",
19
+ "for",
20
+ "while",
21
+ "try",
22
+ "catch",
23
+ "throw",
24
+ "null",
25
+ "undefined",
26
+ "true",
27
+ "false",
28
+ "this",
29
+ "None",
30
+ "True",
31
+ "False",
32
+ "print",
33
+ "def"
34
+ ]);
35
+ const TOKEN_CLASS = {
36
+ string: styles.tokString,
37
+ comment: styles.tokComment,
38
+ keyword: styles.tokKeyword,
39
+ number: styles.tokNumber};
40
+ function tokenise(src) {
41
+ const spans = [];
42
+ const re = /(\/\/[^\n]*|#[^\n]*)|("(?:[^"\\\n]|\\.)*"?)|('(?:[^'\\\n]|\\.)*'?)|(\b\d+(?:\.\d+)?\b)|([A-Za-z_$][\w$]*)/g;
43
+ let last = 0;
44
+ let m = re.exec(src);
45
+ while (m !== null) {
46
+ if (m.index > last) spans.push({ text: src.slice(last, m.index) });
47
+ const [full, comment, dq, sq, num, ident] = m;
48
+ if (comment) spans.push({ text: full, cls: TOKEN_CLASS.comment });
49
+ else if (dq || sq) spans.push({ text: full, cls: TOKEN_CLASS.string });
50
+ else if (num) spans.push({ text: full, cls: TOKEN_CLASS.number });
51
+ else if (ident) {
52
+ spans.push({
53
+ text: full,
54
+ cls: KEYWORDS.has(full) ? TOKEN_CLASS.keyword : void 0
55
+ });
56
+ }
57
+ last = re.lastIndex;
58
+ m = re.exec(src);
59
+ }
60
+ if (last < src.length) spans.push({ text: src.slice(last) });
61
+ return spans;
62
+ }
63
+ function HighlightedCode({ code }) {
64
+ const spans = tokenise(code);
65
+ return /* @__PURE__ */ jsx("pre", { className: styles.snippetCode, children: /* @__PURE__ */ jsx("code", { children: spans.map(
66
+ (s, i) => s.cls ? /* @__PURE__ */ jsx("span", { className: s.cls, children: s.text }, i) : s.text
67
+ ) }) });
68
+ }
69
+
70
+ export { HighlightedCode };
71
+ //# sourceMappingURL=codeHighlight.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codeHighlight.esm.js","sources":["../../src/components/codeHighlight.tsx"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { type ReactNode } from 'react';\nimport styles from './ApiDetailsDrawer.module.css';\n\n/**\n * A tiny dependency-free syntax highlighter themed on VS Code's default Dark+ palette.\n * Splits the source into string / comment / keyword / number / plain spans and colours each\n * with a `.tok*` CSS class. Not a full parser — good enough for the short generated request\n * snippets this renders, and adds no syntax-highlighter dependency.\n */\n\nconst KEYWORDS = new Set([\n // shared\n 'const',\n 'let',\n 'var',\n 'new',\n 'await',\n 'async',\n 'return',\n 'import',\n 'from',\n 'class',\n 'function',\n 'if',\n 'else',\n 'for',\n 'while',\n 'try',\n 'catch',\n 'throw',\n 'null',\n 'undefined',\n 'true',\n 'false',\n 'this',\n 'None',\n 'True',\n 'False',\n 'print',\n 'def',\n]);\n\ninterface Span {\n text: string;\n cls?: string;\n}\n\n/** VS Code Dark+ token colours → CSS-module class names defined in the drawer stylesheet. */\nconst TOKEN_CLASS: Record<string, string> = {\n string: styles.tokString,\n comment: styles.tokComment,\n keyword: styles.tokKeyword,\n number: styles.tokNumber,\n fn: styles.tokFunction,\n};\n\n/**\n * Tokenise `src` for display. Order matters: comments and strings are matched first so\n * keywords/numbers inside them are not re-coloured.\n */\nfunction tokenise(src: string): Span[] {\n const spans: Span[] = [];\n // comment | double-quoted | single-quoted | number | identifier/keyword\n const re =\n /(\\/\\/[^\\n]*|#[^\\n]*)|(\"(?:[^\"\\\\\\n]|\\\\.)*\"?)|('(?:[^'\\\\\\n]|\\\\.)*'?)|(\\b\\d+(?:\\.\\d+)?\\b)|([A-Za-z_$][\\w$]*)/g;\n let last = 0;\n let m: RegExpExecArray | null = re.exec(src);\n while (m !== null) {\n if (m.index > last) spans.push({ text: src.slice(last, m.index) });\n const [full, comment, dq, sq, num, ident] = m;\n if (comment) spans.push({ text: full, cls: TOKEN_CLASS.comment });\n else if (dq || sq) spans.push({ text: full, cls: TOKEN_CLASS.string });\n else if (num) spans.push({ text: full, cls: TOKEN_CLASS.number });\n else if (ident) {\n spans.push({\n text: full,\n cls: KEYWORDS.has(full) ? TOKEN_CLASS.keyword : undefined,\n });\n }\n last = re.lastIndex;\n m = re.exec(src);\n }\n if (last < src.length) spans.push({ text: src.slice(last) });\n return spans;\n}\n\n/** Rendered, colourised code block (Dark+ themed) for a generated request snippet. */\nexport function HighlightedCode({ code }: { code: string }): ReactNode {\n const spans = tokenise(code);\n return (\n <pre className={styles.snippetCode}>\n <code>\n {spans.map((s, i) =>\n s.cls ? (\n <span key={i} className={s.cls}>\n {s.text}\n </span>\n ) : (\n s.text\n ),\n )}\n </code>\n </pre>\n );\n}\n"],"names":[],"mappings":"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"}