@betterportal/framework 0.0.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.
- package/README.md +13 -0
- package/lib/adapters/h3.d.ts +51 -0
- package/lib/adapters/h3.d.ts.map +1 -0
- package/lib/adapters/h3.js +1120 -0
- package/lib/adapters/h3.js.map +1 -0
- package/lib/codegen/cli.d.ts +3 -0
- package/lib/codegen/cli.d.ts.map +1 -0
- package/lib/codegen/cli.js +82 -0
- package/lib/codegen/cli.js.map +1 -0
- package/lib/codegen/emitter.d.ts +7 -0
- package/lib/codegen/emitter.d.ts.map +1 -0
- package/lib/codegen/emitter.js +453 -0
- package/lib/codegen/emitter.js.map +1 -0
- package/lib/codegen/init.d.ts +3 -0
- package/lib/codegen/init.d.ts.map +1 -0
- package/lib/codegen/init.js +90 -0
- package/lib/codegen/init.js.map +1 -0
- package/lib/codegen/scanner.d.ts +56 -0
- package/lib/codegen/scanner.d.ts.map +1 -0
- package/lib/codegen/scanner.js +484 -0
- package/lib/codegen/scanner.js.map +1 -0
- package/lib/codegen/validate.d.ts +14 -0
- package/lib/codegen/validate.d.ts.map +1 -0
- package/lib/codegen/validate.js +166 -0
- package/lib/codegen/validate.js.map +1 -0
- package/lib/contracts/auth.d.ts +160 -0
- package/lib/contracts/auth.d.ts.map +1 -0
- package/lib/contracts/auth.js +123 -0
- package/lib/contracts/auth.js.map +1 -0
- package/lib/contracts/binding.d.ts +169 -0
- package/lib/contracts/binding.d.ts.map +1 -0
- package/lib/contracts/binding.js +69 -0
- package/lib/contracts/binding.js.map +1 -0
- package/lib/contracts/common.d.ts +23 -0
- package/lib/contracts/common.d.ts.map +1 -0
- package/lib/contracts/common.js +18 -0
- package/lib/contracts/common.js.map +1 -0
- package/lib/contracts/config.d.ts +93 -0
- package/lib/contracts/config.d.ts.map +1 -0
- package/lib/contracts/config.js +62 -0
- package/lib/contracts/config.js.map +1 -0
- package/lib/contracts/controlPlane.d.ts +63 -0
- package/lib/contracts/controlPlane.d.ts.map +1 -0
- package/lib/contracts/controlPlane.js +2 -0
- package/lib/contracts/controlPlane.js.map +1 -0
- package/lib/contracts/json.d.ts +9 -0
- package/lib/contracts/json.d.ts.map +1 -0
- package/lib/contracts/json.js +6 -0
- package/lib/contracts/json.js.map +1 -0
- package/lib/contracts/manifest.d.ts +158 -0
- package/lib/contracts/manifest.d.ts.map +1 -0
- package/lib/contracts/manifest.js +40 -0
- package/lib/contracts/manifest.js.map +1 -0
- package/lib/contracts/observability.d.ts +77 -0
- package/lib/contracts/observability.d.ts.map +1 -0
- package/lib/contracts/observability.js +99 -0
- package/lib/contracts/observability.js.map +1 -0
- package/lib/contracts/platformConfig.d.ts +635 -0
- package/lib/contracts/platformConfig.d.ts.map +1 -0
- package/lib/contracts/platformConfig.js +256 -0
- package/lib/contracts/platformConfig.js.map +1 -0
- package/lib/contracts/registry.d.ts +104 -0
- package/lib/contracts/registry.d.ts.map +1 -0
- package/lib/contracts/registry.js +2 -0
- package/lib/contracts/registry.js.map +1 -0
- package/lib/contracts/route.d.ts +199 -0
- package/lib/contracts/route.d.ts.map +1 -0
- package/lib/contracts/route.js +26 -0
- package/lib/contracts/route.js.map +1 -0
- package/lib/contracts/serviceConfig.d.ts +88 -0
- package/lib/contracts/serviceConfig.d.ts.map +1 -0
- package/lib/contracts/serviceConfig.js +45 -0
- package/lib/contracts/serviceConfig.js.map +1 -0
- package/lib/contracts/streaming.d.ts +76 -0
- package/lib/contracts/streaming.d.ts.map +1 -0
- package/lib/contracts/streaming.js +31 -0
- package/lib/contracts/streaming.js.map +1 -0
- package/lib/contracts/view.d.ts +149 -0
- package/lib/contracts/view.d.ts.map +1 -0
- package/lib/contracts/view.js +82 -0
- package/lib/contracts/view.js.map +1 -0
- package/lib/controlPlane/store.d.ts +24 -0
- package/lib/controlPlane/store.d.ts.map +1 -0
- package/lib/controlPlane/store.js +70 -0
- package/lib/controlPlane/store.js.map +1 -0
- package/lib/controlPlane/sync.d.ts +8 -0
- package/lib/controlPlane/sync.d.ts.map +1 -0
- package/lib/controlPlane/sync.js +24 -0
- package/lib/controlPlane/sync.js.map +1 -0
- package/lib/controlPlane/types.d.ts +15 -0
- package/lib/controlPlane/types.d.ts.map +1 -0
- package/lib/controlPlane/types.js +2 -0
- package/lib/controlPlane/types.js.map +1 -0
- package/lib/index.d.ts +40 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +40 -0
- package/lib/index.js.map +1 -0
- package/lib/runtime/auth/envelope.d.ts +32 -0
- package/lib/runtime/auth/envelope.d.ts.map +1 -0
- package/lib/runtime/auth/envelope.js +123 -0
- package/lib/runtime/auth/envelope.js.map +1 -0
- package/lib/runtime/auth/issuer.d.ts +44 -0
- package/lib/runtime/auth/issuer.d.ts.map +1 -0
- package/lib/runtime/auth/issuer.js +82 -0
- package/lib/runtime/auth/issuer.js.map +1 -0
- package/lib/runtime/auth/jwks.d.ts +7 -0
- package/lib/runtime/auth/jwks.d.ts.map +1 -0
- package/lib/runtime/auth/jwks.js +69 -0
- package/lib/runtime/auth/jwks.js.map +1 -0
- package/lib/runtime/auth/keypair.d.ts +21 -0
- package/lib/runtime/auth/keypair.d.ts.map +1 -0
- package/lib/runtime/auth/keypair.js +50 -0
- package/lib/runtime/auth/keypair.js.map +1 -0
- package/lib/runtime/auth/tokens.d.ts +25 -0
- package/lib/runtime/auth/tokens.d.ts.map +1 -0
- package/lib/runtime/auth/tokens.js +137 -0
- package/lib/runtime/auth/tokens.js.map +1 -0
- package/lib/runtime/auth/verifier.d.ts +45 -0
- package/lib/runtime/auth/verifier.d.ts.map +1 -0
- package/lib/runtime/auth/verifier.js +76 -0
- package/lib/runtime/auth/verifier.js.map +1 -0
- package/lib/runtime/bpHeaders.d.ts +10 -0
- package/lib/runtime/bpHeaders.d.ts.map +1 -0
- package/lib/runtime/bpHeaders.js +53 -0
- package/lib/runtime/bpHeaders.js.map +1 -0
- package/lib/runtime/configProvider.d.ts +41 -0
- package/lib/runtime/configProvider.d.ts.map +1 -0
- package/lib/runtime/configProvider.js +232 -0
- package/lib/runtime/configProvider.js.map +1 -0
- package/lib/runtime/configStore.d.ts +34 -0
- package/lib/runtime/configStore.d.ts.map +1 -0
- package/lib/runtime/configStore.js +197 -0
- package/lib/runtime/configStore.js.map +1 -0
- package/lib/runtime/configTicket.d.ts +49 -0
- package/lib/runtime/configTicket.d.ts.map +1 -0
- package/lib/runtime/configTicket.js +168 -0
- package/lib/runtime/configTicket.js.map +1 -0
- package/lib/runtime/h3.d.ts +28 -0
- package/lib/runtime/h3.d.ts.map +1 -0
- package/lib/runtime/h3.js +199 -0
- package/lib/runtime/h3.js.map +1 -0
- package/lib/runtime/handler.d.ts +55 -0
- package/lib/runtime/handler.d.ts.map +1 -0
- package/lib/runtime/handler.js +51 -0
- package/lib/runtime/handler.js.map +1 -0
- package/lib/runtime/http.d.ts +13 -0
- package/lib/runtime/http.d.ts.map +1 -0
- package/lib/runtime/http.js +114 -0
- package/lib/runtime/http.js.map +1 -0
- package/lib/runtime/jsonSchema.d.ts +4 -0
- package/lib/runtime/jsonSchema.d.ts.map +1 -0
- package/lib/runtime/jsonSchema.js +28 -0
- package/lib/runtime/jsonSchema.js.map +1 -0
- package/lib/runtime/manifest.d.ts +3 -0
- package/lib/runtime/manifest.d.ts.map +1 -0
- package/lib/runtime/manifest.js +5 -0
- package/lib/runtime/manifest.js.map +1 -0
- package/lib/runtime/media.d.ts +20 -0
- package/lib/runtime/media.d.ts.map +1 -0
- package/lib/runtime/media.js +70 -0
- package/lib/runtime/media.js.map +1 -0
- package/lib/runtime/registry.d.ts +67 -0
- package/lib/runtime/registry.d.ts.map +1 -0
- package/lib/runtime/registry.js +290 -0
- package/lib/runtime/registry.js.map +1 -0
- package/lib/runtime/serviceConfig.d.ts +38 -0
- package/lib/runtime/serviceConfig.d.ts.map +1 -0
- package/lib/runtime/serviceConfig.js +152 -0
- package/lib/runtime/serviceConfig.js.map +1 -0
- package/lib/runtime/statusViews.d.ts +23 -0
- package/lib/runtime/statusViews.d.ts.map +1 -0
- package/lib/runtime/statusViews.js +48 -0
- package/lib/runtime/statusViews.js.map +1 -0
- package/lib/runtime/stream.d.ts +41 -0
- package/lib/runtime/stream.d.ts.map +1 -0
- package/lib/runtime/stream.js +92 -0
- package/lib/runtime/stream.js.map +1 -0
- package/lib/runtime/streamHandler.d.ts +48 -0
- package/lib/runtime/streamHandler.d.ts.map +1 -0
- package/lib/runtime/streamHandler.js +49 -0
- package/lib/runtime/streamHandler.js.map +1 -0
- package/lib/runtime/tenantResolution.d.ts +4 -0
- package/lib/runtime/tenantResolution.d.ts.map +1 -0
- package/lib/runtime/tenantResolution.js +19 -0
- package/lib/runtime/tenantResolution.js.map +1 -0
- package/lib/runtime/uuid.d.ts +6 -0
- package/lib/runtime/uuid.d.ts.map +1 -0
- package/lib/runtime/uuid.js +27 -0
- package/lib/runtime/uuid.js.map +1 -0
- package/lib/runtime/view.d.ts +48 -0
- package/lib/runtime/view.d.ts.map +1 -0
- package/lib/runtime/view.js +111 -0
- package/lib/runtime/view.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
function headerLookup(headers, key) {
|
|
2
|
+
if (headers instanceof Headers) {
|
|
3
|
+
return headers.get(key) ?? undefined;
|
|
4
|
+
}
|
|
5
|
+
const value = headers[key] ?? headers[key.toLowerCase()] ?? headers[key.toUpperCase()];
|
|
6
|
+
if (Array.isArray(value)) {
|
|
7
|
+
return value.find((entry) => typeof entry === "string" && entry.trim().length > 0);
|
|
8
|
+
}
|
|
9
|
+
return typeof value === "string" ? value : undefined;
|
|
10
|
+
}
|
|
11
|
+
function firstHeaderValue(headers, candidates) {
|
|
12
|
+
for (const candidate of candidates) {
|
|
13
|
+
const value = headerLookup(headers, candidate);
|
|
14
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
function forwardedHostFromHeaderValue(value) {
|
|
21
|
+
if (!value)
|
|
22
|
+
return undefined;
|
|
23
|
+
const firstEntry = value.split(",")[0]?.trim();
|
|
24
|
+
if (!firstEntry)
|
|
25
|
+
return undefined;
|
|
26
|
+
for (const part of firstEntry.split(";")) {
|
|
27
|
+
const [rawKey, ...rawValueParts] = part.split("=");
|
|
28
|
+
if (rawKey?.trim().toLowerCase() !== "host")
|
|
29
|
+
continue;
|
|
30
|
+
const rawValue = rawValueParts.join("=").trim();
|
|
31
|
+
if (!rawValue)
|
|
32
|
+
return undefined;
|
|
33
|
+
return rawValue.replace(/^"|"$/g, "");
|
|
34
|
+
}
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
export function hostFromHeaderValue(value) {
|
|
38
|
+
if (!value || value.trim().length === 0) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const forwardedHost = forwardedHostFromHeaderValue(value);
|
|
42
|
+
if (forwardedHost) {
|
|
43
|
+
return hostFromHeaderValue(forwardedHost);
|
|
44
|
+
}
|
|
45
|
+
const candidate = value.split(",")[0]?.trim();
|
|
46
|
+
if (!candidate)
|
|
47
|
+
return null;
|
|
48
|
+
try {
|
|
49
|
+
const parsed = new URL(candidate);
|
|
50
|
+
if (parsed.hostname) {
|
|
51
|
+
return normalizedHostFromUrl(parsed);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
/* try as a scheme-less host below */
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
return normalizedHostFromUrl(new URL(`https://${candidate}`));
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function normalizedHostFromUrl(url) {
|
|
65
|
+
const hostname = url.hostname.toLowerCase();
|
|
66
|
+
if (!url.port || url.port === "80" || url.port === "443") {
|
|
67
|
+
return hostname;
|
|
68
|
+
}
|
|
69
|
+
return `${hostname}:${url.port}`;
|
|
70
|
+
}
|
|
71
|
+
function trustedProxyHeaderCandidates(options = {}) {
|
|
72
|
+
const candidates = [];
|
|
73
|
+
if (options.trustedProxyHeaders) {
|
|
74
|
+
candidates.push("forwarded", "x-forwarded-host", "x-original-host", "original-host");
|
|
75
|
+
}
|
|
76
|
+
if (options.cfProxy) {
|
|
77
|
+
candidates.push("cf-connecting-host", "cf-original-host");
|
|
78
|
+
}
|
|
79
|
+
return candidates;
|
|
80
|
+
}
|
|
81
|
+
export function resolveEmbeddedSourceHeader(headers, options = {}) {
|
|
82
|
+
// NOTE: hx-current-url is deliberately NOT trusted - it is set by client-side
|
|
83
|
+
// JS (HTMX) and is fully attacker-controllable, so it must never drive
|
|
84
|
+
// tenant/app resolution. Rely on browser-enforced referer/origin instead, and
|
|
85
|
+
// on proxy headers only when an upstream proxy is explicitly trusted.
|
|
86
|
+
return firstHeaderValue(headers, [
|
|
87
|
+
":referer",
|
|
88
|
+
"referer",
|
|
89
|
+
":origin",
|
|
90
|
+
"origin",
|
|
91
|
+
...trustedProxyHeaderCandidates(options)
|
|
92
|
+
]);
|
|
93
|
+
}
|
|
94
|
+
export function resolveThemeSourceHeader(headers, options = {}) {
|
|
95
|
+
return firstHeaderValue(headers, [
|
|
96
|
+
":origin",
|
|
97
|
+
"origin",
|
|
98
|
+
":referer",
|
|
99
|
+
"referer",
|
|
100
|
+
":authority",
|
|
101
|
+
"authority",
|
|
102
|
+
...trustedProxyHeaderCandidates(options)
|
|
103
|
+
]);
|
|
104
|
+
}
|
|
105
|
+
export function resolveEmbeddedHostname(headers, options = {}) {
|
|
106
|
+
return hostFromHeaderValue(resolveEmbeddedSourceHeader(headers, options));
|
|
107
|
+
}
|
|
108
|
+
export function resolveThemeHostname(headers, options = {}) {
|
|
109
|
+
return hostFromHeaderValue(resolveThemeSourceHeader(headers, options));
|
|
110
|
+
}
|
|
111
|
+
export function toHtmlString(body) {
|
|
112
|
+
return typeof body === "string" ? body : body.toString();
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/runtime/http.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonSchema.d.ts","sourceRoot":"","sources":["../../src/runtime/jsonSchema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,UAAU,EAA+B,MAAM,sBAAsB,CAAC;AA+B/E,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,UAAU,CAExF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as av from "anyvali";
|
|
2
|
+
import { JsonObjectSchema } from "../contracts/json.js";
|
|
3
|
+
function sanitizeJsonValue(value) {
|
|
4
|
+
if (typeof value === "string" ||
|
|
5
|
+
typeof value === "number" ||
|
|
6
|
+
typeof value === "boolean" ||
|
|
7
|
+
value === null) {
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
10
|
+
if (Array.isArray(value)) {
|
|
11
|
+
return value.map((item) => sanitizeJsonValue(item));
|
|
12
|
+
}
|
|
13
|
+
if (typeof value === "object" && value !== null) {
|
|
14
|
+
const output = {};
|
|
15
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
16
|
+
if (typeof entry === "function" || typeof entry === "undefined") {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
output[key] = sanitizeJsonValue(entry);
|
|
20
|
+
}
|
|
21
|
+
return output;
|
|
22
|
+
}
|
|
23
|
+
return String(value);
|
|
24
|
+
}
|
|
25
|
+
export function toJsonSchemaDocument(schema) {
|
|
26
|
+
return JsonObjectSchema.parse(sanitizeJsonValue(av.exportSchema(schema, "portable")));
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=jsonSchema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonSchema.js","sourceRoot":"","sources":["../../src/runtime/jsonSchema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAc,gBAAgB,EAAa,MAAM,sBAAsB,CAAC;AAE/E,SAAS,iBAAiB,CAAC,KAAc;IACvC,IACE,OAAO,KAAK,KAAK,QAAQ;QACzB,OAAO,KAAK,KAAK,QAAQ;QACzB,OAAO,KAAK,KAAK,SAAS;QAC1B,KAAK,KAAK,IAAI,EACd,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC;IACtD,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAChD,MAAM,MAAM,GAAe,EAAE,CAAC;QAC9B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACjD,IAAI,OAAO,KAAK,KAAK,UAAU,IAAI,OAAO,KAAK,KAAK,WAAW,EAAE,CAAC;gBAChE,SAAS;YACX,CAAC;YAED,MAAM,CAAC,GAAG,CAAC,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;QACzC,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,MAAuC;IAC1E,OAAO,gBAAgB,CAAC,KAAK,CAAC,iBAAiB,CAAC,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,UAAU,CAAY,CAAC,CAAC,CAAC;AACnG,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/runtime/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAErF,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,UAAU,CAAC,OAAO,oBAAoB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,cAAc,CAE/G"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/runtime/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAuB,MAAM,0BAA0B,CAAC;AAErF,MAAM,UAAU,oBAAoB,CAAC,QAA0D;IAC7F,OAAO,oBAAoB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;AAC9C,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { RenderMode } from "../contracts/common.js";
|
|
2
|
+
export interface AcceptEntry {
|
|
3
|
+
mediaType: string;
|
|
4
|
+
parameters: Readonly<Record<string, string>>;
|
|
5
|
+
quality: number;
|
|
6
|
+
}
|
|
7
|
+
export type RequestedRepresentation = {
|
|
8
|
+
kind: "json";
|
|
9
|
+
} | {
|
|
10
|
+
kind: "ndjson";
|
|
11
|
+
} | {
|
|
12
|
+
kind: "metadata";
|
|
13
|
+
} | {
|
|
14
|
+
kind: "html";
|
|
15
|
+
theme?: string;
|
|
16
|
+
mode?: RenderMode;
|
|
17
|
+
};
|
|
18
|
+
export declare function parseAcceptHeader(headerValue?: string): AcceptEntry[];
|
|
19
|
+
export declare function resolveRequestedRepresentation(headerValue?: string): RequestedRepresentation;
|
|
20
|
+
//# sourceMappingURL=media.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../../src/runtime/media.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAoB,MAAM,wBAAwB,CAAC;AAEtE,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC7C,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,uBAAuB,GAC/B;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GACpB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,UAAU,CAAA;CAAE,CAAC;AAiBxD,wBAAgB,iBAAiB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,WAAW,EAAE,CAoCrE;AAED,wBAAgB,8BAA8B,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,uBAAuB,CA4B5F"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { RenderModeSchema } from "../contracts/common.js";
|
|
2
|
+
function parseParameter(raw) {
|
|
3
|
+
const separatorIndex = raw.indexOf("=");
|
|
4
|
+
if (separatorIndex <= 0) {
|
|
5
|
+
return null;
|
|
6
|
+
}
|
|
7
|
+
const key = raw.slice(0, separatorIndex).trim().toLowerCase();
|
|
8
|
+
const value = raw.slice(separatorIndex + 1).trim().replace(/^"|"$/g, "");
|
|
9
|
+
if (key.length === 0 || value.length === 0) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
return [key, value];
|
|
13
|
+
}
|
|
14
|
+
export function parseAcceptHeader(headerValue) {
|
|
15
|
+
if (!headerValue || headerValue.trim().length === 0) {
|
|
16
|
+
return [{ mediaType: "application/json", parameters: {}, quality: 1 }];
|
|
17
|
+
}
|
|
18
|
+
return headerValue
|
|
19
|
+
.split(",")
|
|
20
|
+
.map((rawEntry) => rawEntry.trim())
|
|
21
|
+
.filter((rawEntry) => rawEntry.length > 0)
|
|
22
|
+
.map((rawEntry) => {
|
|
23
|
+
const parts = rawEntry.split(";").map((part) => part.trim()).filter((part) => part.length > 0);
|
|
24
|
+
const mediaType = parts[0].toLowerCase();
|
|
25
|
+
const parameters = {};
|
|
26
|
+
let quality = 1;
|
|
27
|
+
for (const parameter of parts.slice(1)) {
|
|
28
|
+
const parsed = parseParameter(parameter);
|
|
29
|
+
if (!parsed) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const [key, value] = parsed;
|
|
33
|
+
if (key === "q") {
|
|
34
|
+
const parsedQuality = Number(value);
|
|
35
|
+
if (Number.isFinite(parsedQuality) && parsedQuality >= 0 && parsedQuality <= 1) {
|
|
36
|
+
quality = parsedQuality;
|
|
37
|
+
}
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
parameters[key] = value;
|
|
41
|
+
}
|
|
42
|
+
return { mediaType, parameters, quality };
|
|
43
|
+
})
|
|
44
|
+
.sort((left, right) => right.quality - left.quality);
|
|
45
|
+
}
|
|
46
|
+
export function resolveRequestedRepresentation(headerValue) {
|
|
47
|
+
const entries = parseAcceptHeader(headerValue);
|
|
48
|
+
for (const entry of entries) {
|
|
49
|
+
if (entry.mediaType === "application/vnd.betterportal.metadata+json") {
|
|
50
|
+
return { kind: "metadata" };
|
|
51
|
+
}
|
|
52
|
+
if (entry.mediaType === "application/x-ndjson") {
|
|
53
|
+
return { kind: "ndjson" };
|
|
54
|
+
}
|
|
55
|
+
if (entry.mediaType === "application/json" || entry.mediaType === "*/*") {
|
|
56
|
+
return { kind: "json" };
|
|
57
|
+
}
|
|
58
|
+
if (entry.mediaType === "text/html") {
|
|
59
|
+
const modeCandidate = entry.parameters.mode;
|
|
60
|
+
const mode = modeCandidate ? RenderModeSchema.parse(modeCandidate) : undefined;
|
|
61
|
+
return {
|
|
62
|
+
kind: "html",
|
|
63
|
+
theme: entry.parameters.theme,
|
|
64
|
+
mode
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return { kind: "json" };
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=media.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media.js","sourceRoot":"","sources":["../../src/runtime/media.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { HttpMethod } from "../contracts/common.js";
|
|
2
|
+
import type { BetterPortalRegistry, RegisteredRoute, RegisteredThemeRenderer } from "../contracts/registry.js";
|
|
3
|
+
import type { PluginManifest } from "../contracts/manifest.js";
|
|
4
|
+
export interface ResolvedRoute {
|
|
5
|
+
readonly route: RegisteredRoute;
|
|
6
|
+
readonly params: Record<string, string>;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Resolve a request path to a registered route.
|
|
10
|
+
* Static segments have priority over dynamic params (Next.js convention).
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveRoute(registry: BetterPortalRegistry, path: string, method: HttpMethod): ResolvedRoute | null;
|
|
13
|
+
export interface ResolvedRenderer {
|
|
14
|
+
readonly renderer: RegisteredThemeRenderer;
|
|
15
|
+
readonly themeId: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Resolve a theme renderer for a route.
|
|
19
|
+
* Method-specific renderers have priority over default (no method).
|
|
20
|
+
*/
|
|
21
|
+
export declare function resolveRenderer(route: RegisteredRoute, themeId: string, type: "page" | "component" | "fragment", method?: HttpMethod, componentId?: string, fragmentKey?: string): ResolvedRenderer | null;
|
|
22
|
+
export interface ManifestBaseFields {
|
|
23
|
+
pluginId: string;
|
|
24
|
+
title: string;
|
|
25
|
+
description: string;
|
|
26
|
+
category?: PluginManifest["category"];
|
|
27
|
+
deploymentModes?: ReadonlyArray<PluginManifest["deploymentModes"][number]>;
|
|
28
|
+
capabilities?: ReadonlyArray<string>;
|
|
29
|
+
configSchemas?: PluginManifest["configSchemas"];
|
|
30
|
+
permissions?: PluginManifest["permissions"];
|
|
31
|
+
adminApis?: PluginManifest["adminApis"];
|
|
32
|
+
webhooks?: PluginManifest["webhooks"];
|
|
33
|
+
cacheHints?: PluginManifest["cacheHints"];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Build a PluginManifest from the registry, auto-deriving:
|
|
37
|
+
* - version (from package.json)
|
|
38
|
+
* - supportedThemes (from route themeRenderers)
|
|
39
|
+
* - supportedRenderModes (from renderer types)
|
|
40
|
+
* - views (from non-fragment routes)
|
|
41
|
+
* - capabilities (from themes + view types)
|
|
42
|
+
*/
|
|
43
|
+
export declare function buildManifestFromRegistry(registry: BetterPortalRegistry, packageJson: {
|
|
44
|
+
version: string;
|
|
45
|
+
}, base: ManifestBaseFields): PluginManifest;
|
|
46
|
+
export interface BpSchemaOutput {
|
|
47
|
+
manifest: PluginManifest;
|
|
48
|
+
routes: Array<{
|
|
49
|
+
viewId: string;
|
|
50
|
+
path: string;
|
|
51
|
+
methods: ReadonlyArray<HttpMethod>;
|
|
52
|
+
paramNames: ReadonlyArray<string>;
|
|
53
|
+
themes: string[];
|
|
54
|
+
hasFragments: boolean;
|
|
55
|
+
fragments: Array<{
|
|
56
|
+
fragmentLocation: string;
|
|
57
|
+
fragmentId: string;
|
|
58
|
+
themes: string[];
|
|
59
|
+
}>;
|
|
60
|
+
components: string[];
|
|
61
|
+
}>;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Build /.well-known/bp/schema.json output.
|
|
65
|
+
*/
|
|
66
|
+
export declare function buildBpSchema(registry: BetterPortalRegistry, manifest: PluginManifest): BpSchemaOutput;
|
|
67
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/runtime/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAc,MAAM,wBAAwB,CAAC;AAErE,OAAO,KAAK,EACV,oBAAoB,EACpB,eAAe,EACf,uBAAuB,EAExB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAsB,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAMnF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzC;AA2BD;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,oBAAoB,EAC9B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,UAAU,GACjB,aAAa,GAAG,IAAI,CAoCtB;AAID,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,QAAQ,EAAE,uBAAuB,CAAC;IAC3C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,eAAe,EACtB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,EACvC,MAAM,CAAC,EAAE,UAAU,EACnB,WAAW,CAAC,EAAE,MAAM,EACpB,WAAW,CAAC,EAAE,MAAM,GACnB,gBAAgB,GAAG,IAAI,CAyCzB;AAID,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;IACtC,eAAe,CAAC,EAAE,aAAa,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3E,YAAY,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACrC,aAAa,CAAC,EAAE,cAAc,CAAC,eAAe,CAAC,CAAC;IAChD,WAAW,CAAC,EAAE,cAAc,CAAC,aAAa,CAAC,CAAC;IAC5C,SAAS,CAAC,EAAE,cAAc,CAAC,WAAW,CAAC,CAAC;IACxC,QAAQ,CAAC,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;IACtC,UAAU,CAAC,EAAE,cAAc,CAAC,YAAY,CAAC,CAAC;CAC3C;AAiBD;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,oBAAoB,EAC9B,WAAW,EAAE;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,EAChC,IAAI,EAAE,kBAAkB,GACvB,cAAc,CAkEhB;AA4FD,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,cAAc,CAAC;IACzB,MAAM,EAAE,KAAK,CAAC;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;QACnC,UAAU,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;QAClC,MAAM,EAAE,MAAM,EAAE,CAAC;QACjB,YAAY,EAAE,OAAO,CAAC;QACtB,SAAS,EAAE,KAAK,CAAC;YAAE,gBAAgB,EAAE,MAAM,CAAC;YAAC,UAAU,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,EAAE,CAAA;SAAE,CAAC,CAAC;QACrF,UAAU,EAAE,MAAM,EAAE,CAAC;KACtB,CAAC,CAAC;CACJ;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,oBAAoB,EAC9B,QAAQ,EAAE,cAAc,GACvB,cAAc,CA8BhB"}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import { toJsonSchemaDocument } from "./jsonSchema.js";
|
|
2
|
+
function buildCandidates(registry) {
|
|
3
|
+
return registry.routes.map((route) => {
|
|
4
|
+
const segments = route.path.split("/").filter(Boolean);
|
|
5
|
+
const paramIndices = new Map();
|
|
6
|
+
let staticCount = 0;
|
|
7
|
+
for (let i = 0; i < segments.length; i++) {
|
|
8
|
+
if (segments[i].startsWith(":")) {
|
|
9
|
+
paramIndices.set(i, segments[i].slice(1));
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
staticCount++;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return { route, segments, paramIndices, staticCount };
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Resolve a request path to a registered route.
|
|
20
|
+
* Static segments have priority over dynamic params (Next.js convention).
|
|
21
|
+
*/
|
|
22
|
+
export function resolveRoute(registry, path, method) {
|
|
23
|
+
const requestSegments = path.split("/").filter(Boolean);
|
|
24
|
+
const candidates = buildCandidates(registry);
|
|
25
|
+
const matches = [];
|
|
26
|
+
for (const candidate of candidates) {
|
|
27
|
+
if (candidate.segments.length !== requestSegments.length)
|
|
28
|
+
continue;
|
|
29
|
+
if (!candidate.route.methods.includes(method))
|
|
30
|
+
continue;
|
|
31
|
+
const params = {};
|
|
32
|
+
let match = true;
|
|
33
|
+
for (let i = 0; i < candidate.segments.length; i++) {
|
|
34
|
+
const paramName = candidate.paramIndices.get(i);
|
|
35
|
+
if (paramName) {
|
|
36
|
+
params[paramName] = requestSegments[i];
|
|
37
|
+
}
|
|
38
|
+
else if (candidate.segments[i] !== requestSegments[i]) {
|
|
39
|
+
match = false;
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (match) {
|
|
44
|
+
matches.push({ candidate, params });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (matches.length === 0)
|
|
48
|
+
return null;
|
|
49
|
+
// Sort: more static segments = higher priority (static > dynamic)
|
|
50
|
+
matches.sort((a, b) => b.candidate.staticCount - a.candidate.staticCount);
|
|
51
|
+
return {
|
|
52
|
+
route: matches[0].candidate.route,
|
|
53
|
+
params: matches[0].params
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Resolve a theme renderer for a route.
|
|
58
|
+
* Method-specific renderers have priority over default (no method).
|
|
59
|
+
*/
|
|
60
|
+
export function resolveRenderer(route, themeId, type, method, componentId, fragmentKey) {
|
|
61
|
+
const themeSet = route.themeRenderers[themeId];
|
|
62
|
+
if (!themeSet)
|
|
63
|
+
return null;
|
|
64
|
+
let pool;
|
|
65
|
+
switch (type) {
|
|
66
|
+
case "page":
|
|
67
|
+
pool = themeSet.pages;
|
|
68
|
+
break;
|
|
69
|
+
case "component":
|
|
70
|
+
pool = themeSet.components;
|
|
71
|
+
break;
|
|
72
|
+
case "fragment":
|
|
73
|
+
pool = themeSet.fragments;
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
// Filter by target
|
|
77
|
+
let candidates;
|
|
78
|
+
if (type === "component" && componentId) {
|
|
79
|
+
candidates = pool.filter((r) => r.rendererId === componentId);
|
|
80
|
+
}
|
|
81
|
+
else if (type === "fragment" && fragmentKey) {
|
|
82
|
+
const [location, id] = fragmentKey.split(".");
|
|
83
|
+
candidates = pool.filter((r) => r.fragmentLocation === location && r.fragmentId === id);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
candidates = pool.filter((r) => r.rendererId === "default");
|
|
87
|
+
}
|
|
88
|
+
if (candidates.length === 0)
|
|
89
|
+
return null;
|
|
90
|
+
// Method-specific wins over generic
|
|
91
|
+
if (method) {
|
|
92
|
+
const methodSpecific = candidates.find((r) => r.method === method);
|
|
93
|
+
if (methodSpecific)
|
|
94
|
+
return { renderer: methodSpecific, themeId };
|
|
95
|
+
}
|
|
96
|
+
// Fallback to no-method renderer
|
|
97
|
+
const generic = candidates.find((r) => r.method === undefined);
|
|
98
|
+
return generic ? { renderer: generic, themeId } : null;
|
|
99
|
+
}
|
|
100
|
+
const CONFIG_ADMIN_APIS = [
|
|
101
|
+
{ id: "config.schema", title: "Config Schema", description: "BetterPortal-managed config schemas for this service.", path: "/.well-known/bp/config/schema", methods: ["GET"], supportsCustomUi: false },
|
|
102
|
+
{ id: "config.values", title: "Config Values", description: "Read and write BetterPortal-managed config values.", path: "/.well-known/bp/config", methods: ["GET", "POST"], supportsCustomUi: false }
|
|
103
|
+
];
|
|
104
|
+
function deriveAdminApis(base) {
|
|
105
|
+
const explicit = base.adminApis ?? [];
|
|
106
|
+
const hasConfigSchemas = (base.configSchemas?.length ?? 0) > 0;
|
|
107
|
+
if (!hasConfigSchemas)
|
|
108
|
+
return [...explicit];
|
|
109
|
+
const explicitIds = new Set(explicit.map((a) => a.id));
|
|
110
|
+
const derived = CONFIG_ADMIN_APIS.filter((a) => !explicitIds.has(a.id));
|
|
111
|
+
return [...explicit, ...derived];
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Build a PluginManifest from the registry, auto-deriving:
|
|
115
|
+
* - version (from package.json)
|
|
116
|
+
* - supportedThemes (from route themeRenderers)
|
|
117
|
+
* - supportedRenderModes (from renderer types)
|
|
118
|
+
* - views (from non-fragment routes)
|
|
119
|
+
* - capabilities (from themes + view types)
|
|
120
|
+
*/
|
|
121
|
+
export function buildManifestFromRegistry(registry, packageJson, base) {
|
|
122
|
+
const themes = new Set();
|
|
123
|
+
const renderModes = new Set();
|
|
124
|
+
const capabilities = new Set();
|
|
125
|
+
for (const capability of base.capabilities ?? []) {
|
|
126
|
+
capabilities.add(capability);
|
|
127
|
+
}
|
|
128
|
+
capabilities.add("view.json");
|
|
129
|
+
capabilities.add("view.metadata");
|
|
130
|
+
for (const route of registry.routes) {
|
|
131
|
+
// Streaming views (spec/streaming.md section 5)
|
|
132
|
+
if (route.schemas.item) {
|
|
133
|
+
capabilities.add("stream.ndjson");
|
|
134
|
+
}
|
|
135
|
+
for (const [themeId, rendererSet] of Object.entries(route.themeRenderers)) {
|
|
136
|
+
themes.add(themeId);
|
|
137
|
+
capabilities.add(`theme.${themeId}`);
|
|
138
|
+
if (rendererSet.pages.length > 0)
|
|
139
|
+
renderModes.add("page");
|
|
140
|
+
if (rendererSet.fragments.length > 0)
|
|
141
|
+
renderModes.add("fragment");
|
|
142
|
+
if (rendererSet.pages.length > 0 || rendererSet.components.length > 0) {
|
|
143
|
+
capabilities.add("view.html");
|
|
144
|
+
}
|
|
145
|
+
if (rendererSet.stream) {
|
|
146
|
+
capabilities.add("view.sse-render");
|
|
147
|
+
capabilities.add("view.html");
|
|
148
|
+
renderModes.add("fragment");
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const seenViewIds = new Set();
|
|
153
|
+
const views = registry.routes
|
|
154
|
+
.filter((route) => {
|
|
155
|
+
if (seenViewIds.has(route.viewId))
|
|
156
|
+
return false;
|
|
157
|
+
seenViewIds.add(route.viewId);
|
|
158
|
+
// Exclude fragment-only routes (routes that have fragment renderers but no pages)
|
|
159
|
+
const hasAnyPage = Object.values(route.themeRenderers).some((set) => set.pages.length > 0 || set.stream !== undefined);
|
|
160
|
+
return hasAnyPage || Object.keys(route.themeRenderers).length === 0;
|
|
161
|
+
})
|
|
162
|
+
.map((route) => routeToViewMetadata(route));
|
|
163
|
+
return {
|
|
164
|
+
pluginId: base.pluginId,
|
|
165
|
+
title: base.title,
|
|
166
|
+
description: base.description,
|
|
167
|
+
version: packageJson.version,
|
|
168
|
+
category: base.category ?? "service",
|
|
169
|
+
deploymentModes: base.deploymentModes ? [...base.deploymentModes] : ["self-hosted"],
|
|
170
|
+
capabilities: [...capabilities],
|
|
171
|
+
supportedThemes: [...themes],
|
|
172
|
+
supportedRenderModes: [...renderModes],
|
|
173
|
+
views,
|
|
174
|
+
configSchemas: base.configSchemas ?? [],
|
|
175
|
+
permissions: base.permissions ?? [],
|
|
176
|
+
adminApis: deriveAdminApis(base),
|
|
177
|
+
webhooks: base.webhooks ?? [],
|
|
178
|
+
cacheHints: base.cacheHints ?? { metadataTtlSeconds: 1800 }
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
function routeToViewMetadata(route) {
|
|
182
|
+
const themeRenderers = {};
|
|
183
|
+
for (const [themeId, set] of Object.entries(route.themeRenderers)) {
|
|
184
|
+
const modes = [];
|
|
185
|
+
const renderers = [];
|
|
186
|
+
if (set.pages.length > 0)
|
|
187
|
+
modes.push("page");
|
|
188
|
+
if (set.fragments.length > 0 || set.stream)
|
|
189
|
+
modes.push("fragment");
|
|
190
|
+
for (const page of set.pages) {
|
|
191
|
+
renderers.push({
|
|
192
|
+
id: page.rendererId,
|
|
193
|
+
title: page.rendererId === "default" ? "Default Content" : page.rendererId,
|
|
194
|
+
slotId: "main",
|
|
195
|
+
renderModes: ["page", "fragment"]
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
for (const fragment of set.fragments) {
|
|
199
|
+
const slotId = fragment.fragmentLocation && fragment.fragmentId
|
|
200
|
+
? `${fragment.fragmentLocation}.${fragment.fragmentId}`
|
|
201
|
+
: fragment.rendererId;
|
|
202
|
+
renderers.push({
|
|
203
|
+
id: fragment.rendererId,
|
|
204
|
+
title: fragment.rendererId,
|
|
205
|
+
slotId,
|
|
206
|
+
renderModes: ["fragment"]
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
themeRenderers[themeId] = {
|
|
210
|
+
defaultRenderer: "default",
|
|
211
|
+
renderModes: modes,
|
|
212
|
+
slots: [...new Set(renderers.map((r) => r.slotId))],
|
|
213
|
+
renderers
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
const renderable = route.raw === true
|
|
217
|
+
? false
|
|
218
|
+
: Object.values(route.themeRenderers).some((set) => set.pages.length > 0 || set.components.length > 0 || set.fragments.length > 0 || Boolean(set.stream));
|
|
219
|
+
return {
|
|
220
|
+
viewId: route.viewId,
|
|
221
|
+
title: route.title,
|
|
222
|
+
description: route.description,
|
|
223
|
+
path: route.path,
|
|
224
|
+
methods: [...route.methods],
|
|
225
|
+
paramsSchema: {},
|
|
226
|
+
querySchema: route.schemas.query ? toJsonSchemaDocument(route.schemas.query) : {},
|
|
227
|
+
headersSchema: route.schemas.headers ? toJsonSchemaDocument(route.schemas.headers) : {},
|
|
228
|
+
bodySchema: route.schemas.multipart
|
|
229
|
+
? toJsonSchemaDocument(route.schemas.multipart)
|
|
230
|
+
: route.schemas.request ? toJsonSchemaDocument(route.schemas.request) : {},
|
|
231
|
+
jsonResponseSchema: route.schemas.response ? toJsonSchemaDocument(route.schemas.response) : {},
|
|
232
|
+
metadataResponseSchema: {},
|
|
233
|
+
renderable,
|
|
234
|
+
...(route.raw === true ? { raw: true } : {}),
|
|
235
|
+
...(route.schemas.item ? {
|
|
236
|
+
streaming: {
|
|
237
|
+
itemSchema: toJsonSchemaDocument(route.schemas.item),
|
|
238
|
+
...(route.schemas.summary ? { summarySchema: toJsonSchemaDocument(route.schemas.summary) } : {})
|
|
239
|
+
}
|
|
240
|
+
} : {}),
|
|
241
|
+
html: { themeRenderers },
|
|
242
|
+
auth: route.auth,
|
|
243
|
+
...(route.role ? { role: route.role } : {}),
|
|
244
|
+
dependencies: [...(route.dependencies ?? [])],
|
|
245
|
+
...(route.chrome ? { chrome: route.chrome } : {}),
|
|
246
|
+
demoScenarios: route.demoScenarios.map((s) => ({
|
|
247
|
+
id: s.id,
|
|
248
|
+
title: s.title,
|
|
249
|
+
...(s.description ? { description: s.description } : {}),
|
|
250
|
+
...(s.match ? { match: s.match } : {}),
|
|
251
|
+
response: s.response
|
|
252
|
+
})),
|
|
253
|
+
cacheHints: route.cacheHints
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Build /.well-known/bp/schema.json output.
|
|
258
|
+
*/
|
|
259
|
+
export function buildBpSchema(registry, manifest) {
|
|
260
|
+
return {
|
|
261
|
+
manifest,
|
|
262
|
+
routes: registry.routes.map((route) => {
|
|
263
|
+
// Aggregate fragments across themes: same (location, fragmentId) groups themes that support it
|
|
264
|
+
const fragMap = new Map();
|
|
265
|
+
for (const [themeId, set] of Object.entries(route.themeRenderers)) {
|
|
266
|
+
for (const f of set.fragments) {
|
|
267
|
+
if (!f.fragmentLocation || !f.fragmentId)
|
|
268
|
+
continue;
|
|
269
|
+
const key = `${f.fragmentLocation}::${f.fragmentId}`;
|
|
270
|
+
const existing = fragMap.get(key);
|
|
271
|
+
if (existing)
|
|
272
|
+
existing.themes.push(themeId);
|
|
273
|
+
else
|
|
274
|
+
fragMap.set(key, { fragmentLocation: f.fragmentLocation, fragmentId: f.fragmentId, themes: [themeId] });
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return {
|
|
278
|
+
viewId: route.viewId,
|
|
279
|
+
path: route.path,
|
|
280
|
+
methods: route.methods,
|
|
281
|
+
paramNames: route.paramNames,
|
|
282
|
+
themes: Object.keys(route.themeRenderers),
|
|
283
|
+
hasFragments: fragMap.size > 0,
|
|
284
|
+
fragments: Array.from(fragMap.values()),
|
|
285
|
+
components: Object.values(route.themeRenderers).flatMap((set) => set.components.map((c) => c.rendererId))
|
|
286
|
+
};
|
|
287
|
+
})
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
//# sourceMappingURL=registry.js.map
|