@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.
Files changed (194) hide show
  1. package/README.md +13 -0
  2. package/lib/adapters/h3.d.ts +51 -0
  3. package/lib/adapters/h3.d.ts.map +1 -0
  4. package/lib/adapters/h3.js +1120 -0
  5. package/lib/adapters/h3.js.map +1 -0
  6. package/lib/codegen/cli.d.ts +3 -0
  7. package/lib/codegen/cli.d.ts.map +1 -0
  8. package/lib/codegen/cli.js +82 -0
  9. package/lib/codegen/cli.js.map +1 -0
  10. package/lib/codegen/emitter.d.ts +7 -0
  11. package/lib/codegen/emitter.d.ts.map +1 -0
  12. package/lib/codegen/emitter.js +453 -0
  13. package/lib/codegen/emitter.js.map +1 -0
  14. package/lib/codegen/init.d.ts +3 -0
  15. package/lib/codegen/init.d.ts.map +1 -0
  16. package/lib/codegen/init.js +90 -0
  17. package/lib/codegen/init.js.map +1 -0
  18. package/lib/codegen/scanner.d.ts +56 -0
  19. package/lib/codegen/scanner.d.ts.map +1 -0
  20. package/lib/codegen/scanner.js +484 -0
  21. package/lib/codegen/scanner.js.map +1 -0
  22. package/lib/codegen/validate.d.ts +14 -0
  23. package/lib/codegen/validate.d.ts.map +1 -0
  24. package/lib/codegen/validate.js +166 -0
  25. package/lib/codegen/validate.js.map +1 -0
  26. package/lib/contracts/auth.d.ts +160 -0
  27. package/lib/contracts/auth.d.ts.map +1 -0
  28. package/lib/contracts/auth.js +123 -0
  29. package/lib/contracts/auth.js.map +1 -0
  30. package/lib/contracts/binding.d.ts +169 -0
  31. package/lib/contracts/binding.d.ts.map +1 -0
  32. package/lib/contracts/binding.js +69 -0
  33. package/lib/contracts/binding.js.map +1 -0
  34. package/lib/contracts/common.d.ts +23 -0
  35. package/lib/contracts/common.d.ts.map +1 -0
  36. package/lib/contracts/common.js +18 -0
  37. package/lib/contracts/common.js.map +1 -0
  38. package/lib/contracts/config.d.ts +93 -0
  39. package/lib/contracts/config.d.ts.map +1 -0
  40. package/lib/contracts/config.js +62 -0
  41. package/lib/contracts/config.js.map +1 -0
  42. package/lib/contracts/controlPlane.d.ts +63 -0
  43. package/lib/contracts/controlPlane.d.ts.map +1 -0
  44. package/lib/contracts/controlPlane.js +2 -0
  45. package/lib/contracts/controlPlane.js.map +1 -0
  46. package/lib/contracts/json.d.ts +9 -0
  47. package/lib/contracts/json.d.ts.map +1 -0
  48. package/lib/contracts/json.js +6 -0
  49. package/lib/contracts/json.js.map +1 -0
  50. package/lib/contracts/manifest.d.ts +158 -0
  51. package/lib/contracts/manifest.d.ts.map +1 -0
  52. package/lib/contracts/manifest.js +40 -0
  53. package/lib/contracts/manifest.js.map +1 -0
  54. package/lib/contracts/observability.d.ts +77 -0
  55. package/lib/contracts/observability.d.ts.map +1 -0
  56. package/lib/contracts/observability.js +99 -0
  57. package/lib/contracts/observability.js.map +1 -0
  58. package/lib/contracts/platformConfig.d.ts +635 -0
  59. package/lib/contracts/platformConfig.d.ts.map +1 -0
  60. package/lib/contracts/platformConfig.js +256 -0
  61. package/lib/contracts/platformConfig.js.map +1 -0
  62. package/lib/contracts/registry.d.ts +104 -0
  63. package/lib/contracts/registry.d.ts.map +1 -0
  64. package/lib/contracts/registry.js +2 -0
  65. package/lib/contracts/registry.js.map +1 -0
  66. package/lib/contracts/route.d.ts +199 -0
  67. package/lib/contracts/route.d.ts.map +1 -0
  68. package/lib/contracts/route.js +26 -0
  69. package/lib/contracts/route.js.map +1 -0
  70. package/lib/contracts/serviceConfig.d.ts +88 -0
  71. package/lib/contracts/serviceConfig.d.ts.map +1 -0
  72. package/lib/contracts/serviceConfig.js +45 -0
  73. package/lib/contracts/serviceConfig.js.map +1 -0
  74. package/lib/contracts/streaming.d.ts +76 -0
  75. package/lib/contracts/streaming.d.ts.map +1 -0
  76. package/lib/contracts/streaming.js +31 -0
  77. package/lib/contracts/streaming.js.map +1 -0
  78. package/lib/contracts/view.d.ts +149 -0
  79. package/lib/contracts/view.d.ts.map +1 -0
  80. package/lib/contracts/view.js +82 -0
  81. package/lib/contracts/view.js.map +1 -0
  82. package/lib/controlPlane/store.d.ts +24 -0
  83. package/lib/controlPlane/store.d.ts.map +1 -0
  84. package/lib/controlPlane/store.js +70 -0
  85. package/lib/controlPlane/store.js.map +1 -0
  86. package/lib/controlPlane/sync.d.ts +8 -0
  87. package/lib/controlPlane/sync.d.ts.map +1 -0
  88. package/lib/controlPlane/sync.js +24 -0
  89. package/lib/controlPlane/sync.js.map +1 -0
  90. package/lib/controlPlane/types.d.ts +15 -0
  91. package/lib/controlPlane/types.d.ts.map +1 -0
  92. package/lib/controlPlane/types.js +2 -0
  93. package/lib/controlPlane/types.js.map +1 -0
  94. package/lib/index.d.ts +40 -0
  95. package/lib/index.d.ts.map +1 -0
  96. package/lib/index.js +40 -0
  97. package/lib/index.js.map +1 -0
  98. package/lib/runtime/auth/envelope.d.ts +32 -0
  99. package/lib/runtime/auth/envelope.d.ts.map +1 -0
  100. package/lib/runtime/auth/envelope.js +123 -0
  101. package/lib/runtime/auth/envelope.js.map +1 -0
  102. package/lib/runtime/auth/issuer.d.ts +44 -0
  103. package/lib/runtime/auth/issuer.d.ts.map +1 -0
  104. package/lib/runtime/auth/issuer.js +82 -0
  105. package/lib/runtime/auth/issuer.js.map +1 -0
  106. package/lib/runtime/auth/jwks.d.ts +7 -0
  107. package/lib/runtime/auth/jwks.d.ts.map +1 -0
  108. package/lib/runtime/auth/jwks.js +69 -0
  109. package/lib/runtime/auth/jwks.js.map +1 -0
  110. package/lib/runtime/auth/keypair.d.ts +21 -0
  111. package/lib/runtime/auth/keypair.d.ts.map +1 -0
  112. package/lib/runtime/auth/keypair.js +50 -0
  113. package/lib/runtime/auth/keypair.js.map +1 -0
  114. package/lib/runtime/auth/tokens.d.ts +25 -0
  115. package/lib/runtime/auth/tokens.d.ts.map +1 -0
  116. package/lib/runtime/auth/tokens.js +137 -0
  117. package/lib/runtime/auth/tokens.js.map +1 -0
  118. package/lib/runtime/auth/verifier.d.ts +45 -0
  119. package/lib/runtime/auth/verifier.d.ts.map +1 -0
  120. package/lib/runtime/auth/verifier.js +76 -0
  121. package/lib/runtime/auth/verifier.js.map +1 -0
  122. package/lib/runtime/bpHeaders.d.ts +10 -0
  123. package/lib/runtime/bpHeaders.d.ts.map +1 -0
  124. package/lib/runtime/bpHeaders.js +53 -0
  125. package/lib/runtime/bpHeaders.js.map +1 -0
  126. package/lib/runtime/configProvider.d.ts +41 -0
  127. package/lib/runtime/configProvider.d.ts.map +1 -0
  128. package/lib/runtime/configProvider.js +232 -0
  129. package/lib/runtime/configProvider.js.map +1 -0
  130. package/lib/runtime/configStore.d.ts +34 -0
  131. package/lib/runtime/configStore.d.ts.map +1 -0
  132. package/lib/runtime/configStore.js +197 -0
  133. package/lib/runtime/configStore.js.map +1 -0
  134. package/lib/runtime/configTicket.d.ts +49 -0
  135. package/lib/runtime/configTicket.d.ts.map +1 -0
  136. package/lib/runtime/configTicket.js +168 -0
  137. package/lib/runtime/configTicket.js.map +1 -0
  138. package/lib/runtime/h3.d.ts +28 -0
  139. package/lib/runtime/h3.d.ts.map +1 -0
  140. package/lib/runtime/h3.js +199 -0
  141. package/lib/runtime/h3.js.map +1 -0
  142. package/lib/runtime/handler.d.ts +55 -0
  143. package/lib/runtime/handler.d.ts.map +1 -0
  144. package/lib/runtime/handler.js +51 -0
  145. package/lib/runtime/handler.js.map +1 -0
  146. package/lib/runtime/http.d.ts +13 -0
  147. package/lib/runtime/http.d.ts.map +1 -0
  148. package/lib/runtime/http.js +114 -0
  149. package/lib/runtime/http.js.map +1 -0
  150. package/lib/runtime/jsonSchema.d.ts +4 -0
  151. package/lib/runtime/jsonSchema.d.ts.map +1 -0
  152. package/lib/runtime/jsonSchema.js +28 -0
  153. package/lib/runtime/jsonSchema.js.map +1 -0
  154. package/lib/runtime/manifest.d.ts +3 -0
  155. package/lib/runtime/manifest.d.ts.map +1 -0
  156. package/lib/runtime/manifest.js +5 -0
  157. package/lib/runtime/manifest.js.map +1 -0
  158. package/lib/runtime/media.d.ts +20 -0
  159. package/lib/runtime/media.d.ts.map +1 -0
  160. package/lib/runtime/media.js +70 -0
  161. package/lib/runtime/media.js.map +1 -0
  162. package/lib/runtime/registry.d.ts +67 -0
  163. package/lib/runtime/registry.d.ts.map +1 -0
  164. package/lib/runtime/registry.js +290 -0
  165. package/lib/runtime/registry.js.map +1 -0
  166. package/lib/runtime/serviceConfig.d.ts +38 -0
  167. package/lib/runtime/serviceConfig.d.ts.map +1 -0
  168. package/lib/runtime/serviceConfig.js +152 -0
  169. package/lib/runtime/serviceConfig.js.map +1 -0
  170. package/lib/runtime/statusViews.d.ts +23 -0
  171. package/lib/runtime/statusViews.d.ts.map +1 -0
  172. package/lib/runtime/statusViews.js +48 -0
  173. package/lib/runtime/statusViews.js.map +1 -0
  174. package/lib/runtime/stream.d.ts +41 -0
  175. package/lib/runtime/stream.d.ts.map +1 -0
  176. package/lib/runtime/stream.js +92 -0
  177. package/lib/runtime/stream.js.map +1 -0
  178. package/lib/runtime/streamHandler.d.ts +48 -0
  179. package/lib/runtime/streamHandler.d.ts.map +1 -0
  180. package/lib/runtime/streamHandler.js +49 -0
  181. package/lib/runtime/streamHandler.js.map +1 -0
  182. package/lib/runtime/tenantResolution.d.ts +4 -0
  183. package/lib/runtime/tenantResolution.d.ts.map +1 -0
  184. package/lib/runtime/tenantResolution.js +19 -0
  185. package/lib/runtime/tenantResolution.js.map +1 -0
  186. package/lib/runtime/uuid.d.ts +6 -0
  187. package/lib/runtime/uuid.d.ts.map +1 -0
  188. package/lib/runtime/uuid.js +27 -0
  189. package/lib/runtime/uuid.js.map +1 -0
  190. package/lib/runtime/view.d.ts +48 -0
  191. package/lib/runtime/view.d.ts.map +1 -0
  192. package/lib/runtime/view.js +111 -0
  193. package/lib/runtime/view.js.map +1 -0
  194. 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,4 @@
1
+ import * as av from "anyvali";
2
+ import { JsonObject } from "../contracts/json.js";
3
+ export declare function toJsonSchemaDocument(schema: av.BaseSchema<unknown, unknown>): JsonObject;
4
+ //# sourceMappingURL=jsonSchema.d.ts.map
@@ -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,3 @@
1
+ import { PluginManifestSchema, type PluginManifest } from "../contracts/manifest.js";
2
+ export declare function createPluginManifest(manifest: Parameters<typeof PluginManifestSchema.parse>[0]): PluginManifest;
3
+ //# sourceMappingURL=manifest.d.ts.map
@@ -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,5 @@
1
+ import { PluginManifestSchema } from "../contracts/manifest.js";
2
+ export function createPluginManifest(manifest) {
3
+ return PluginManifestSchema.parse(manifest);
4
+ }
5
+ //# sourceMappingURL=manifest.js.map
@@ -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