@xapps-platform/backend-kit 0.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 (142) hide show
  1. package/README.md +196 -0
  2. package/dist/backend/modes/gateway-managed/payment.d.ts +23 -0
  3. package/dist/backend/modes/gateway-managed/payment.d.ts.map +1 -0
  4. package/dist/backend/modes/gateway-managed/payment.js +49 -0
  5. package/dist/backend/modes/gateway-managed/payment.js.map +7 -0
  6. package/dist/backend/modes/gateway-managed/paymentSession.d.ts +4 -0
  7. package/dist/backend/modes/gateway-managed/paymentSession.d.ts.map +1 -0
  8. package/dist/backend/modes/gateway-managed/paymentSession.js +16 -0
  9. package/dist/backend/modes/gateway-managed/paymentSession.js.map +7 -0
  10. package/dist/backend/modes/gateway-managed/policy.d.ts +68 -0
  11. package/dist/backend/modes/gateway-managed/policy.d.ts.map +1 -0
  12. package/dist/backend/modes/gateway-managed/policy.js +53 -0
  13. package/dist/backend/modes/gateway-managed/policy.js.map +7 -0
  14. package/dist/backend/modes/gateway-managed/policyContext.d.ts +5 -0
  15. package/dist/backend/modes/gateway-managed/policyContext.d.ts.map +1 -0
  16. package/dist/backend/modes/gateway-managed/policyContext.js +22 -0
  17. package/dist/backend/modes/gateway-managed/policyContext.js.map +7 -0
  18. package/dist/backend/modes/index.d.ts +72 -0
  19. package/dist/backend/modes/index.d.ts.map +1 -0
  20. package/dist/backend/modes/index.js +159 -0
  21. package/dist/backend/modes/index.js.map +7 -0
  22. package/dist/backend/modes/owner-managed/payment.d.ts +34 -0
  23. package/dist/backend/modes/owner-managed/payment.d.ts.map +1 -0
  24. package/dist/backend/modes/owner-managed/payment.js +75 -0
  25. package/dist/backend/modes/owner-managed/payment.js.map +7 -0
  26. package/dist/backend/modes/owner-managed/paymentAssets.d.ts +4 -0
  27. package/dist/backend/modes/owner-managed/paymentAssets.d.ts.map +1 -0
  28. package/dist/backend/modes/owner-managed/paymentAssets.js +11 -0
  29. package/dist/backend/modes/owner-managed/paymentAssets.js.map +7 -0
  30. package/dist/backend/modes/owner-managed/paymentPageApi.d.ts +4 -0
  31. package/dist/backend/modes/owner-managed/paymentPageApi.d.ts.map +1 -0
  32. package/dist/backend/modes/owner-managed/paymentPageApi.js +11 -0
  33. package/dist/backend/modes/owner-managed/paymentPageApi.js.map +7 -0
  34. package/dist/backend/modes/owner-managed/paymentSession.d.ts +4 -0
  35. package/dist/backend/modes/owner-managed/paymentSession.d.ts.map +1 -0
  36. package/dist/backend/modes/owner-managed/paymentSession.js +18 -0
  37. package/dist/backend/modes/owner-managed/paymentSession.js.map +7 -0
  38. package/dist/backend/modes/owner-managed/policy.d.ts +68 -0
  39. package/dist/backend/modes/owner-managed/policy.d.ts.map +1 -0
  40. package/dist/backend/modes/owner-managed/policy.js +53 -0
  41. package/dist/backend/modes/owner-managed/policy.js.map +7 -0
  42. package/dist/backend/modes/owner-managed/policyContext.d.ts +5 -0
  43. package/dist/backend/modes/owner-managed/policyContext.d.ts.map +1 -0
  44. package/dist/backend/modes/owner-managed/policyContext.js +26 -0
  45. package/dist/backend/modes/owner-managed/policyContext.js.map +7 -0
  46. package/dist/backend/modes/publisher-delegated/payment.d.ts +23 -0
  47. package/dist/backend/modes/publisher-delegated/payment.d.ts.map +1 -0
  48. package/dist/backend/modes/publisher-delegated/payment.js +50 -0
  49. package/dist/backend/modes/publisher-delegated/payment.js.map +7 -0
  50. package/dist/backend/modes/publisher-delegated/paymentSession.d.ts +4 -0
  51. package/dist/backend/modes/publisher-delegated/paymentSession.d.ts.map +1 -0
  52. package/dist/backend/modes/publisher-delegated/paymentSession.js +16 -0
  53. package/dist/backend/modes/publisher-delegated/paymentSession.js.map +7 -0
  54. package/dist/backend/modes/publisher-delegated/policy.d.ts +68 -0
  55. package/dist/backend/modes/publisher-delegated/policy.d.ts.map +1 -0
  56. package/dist/backend/modes/publisher-delegated/policy.js +53 -0
  57. package/dist/backend/modes/publisher-delegated/policy.js.map +7 -0
  58. package/dist/backend/modes/publisher-delegated/policyContext.d.ts +5 -0
  59. package/dist/backend/modes/publisher-delegated/policyContext.d.ts.map +1 -0
  60. package/dist/backend/modes/publisher-delegated/policyContext.js +22 -0
  61. package/dist/backend/modes/publisher-delegated/policyContext.js.map +7 -0
  62. package/dist/backend/modes/tenant-delegated/payment.d.ts +23 -0
  63. package/dist/backend/modes/tenant-delegated/payment.d.ts.map +1 -0
  64. package/dist/backend/modes/tenant-delegated/payment.js +50 -0
  65. package/dist/backend/modes/tenant-delegated/payment.js.map +7 -0
  66. package/dist/backend/modes/tenant-delegated/paymentSession.d.ts +4 -0
  67. package/dist/backend/modes/tenant-delegated/paymentSession.d.ts.map +1 -0
  68. package/dist/backend/modes/tenant-delegated/paymentSession.js +16 -0
  69. package/dist/backend/modes/tenant-delegated/paymentSession.js.map +7 -0
  70. package/dist/backend/modes/tenant-delegated/policy.d.ts +68 -0
  71. package/dist/backend/modes/tenant-delegated/policy.d.ts.map +1 -0
  72. package/dist/backend/modes/tenant-delegated/policy.js +53 -0
  73. package/dist/backend/modes/tenant-delegated/policy.js.map +7 -0
  74. package/dist/backend/modes/tenant-delegated/policyContext.d.ts +5 -0
  75. package/dist/backend/modes/tenant-delegated/policyContext.d.ts.map +1 -0
  76. package/dist/backend/modes/tenant-delegated/policyContext.js +22 -0
  77. package/dist/backend/modes/tenant-delegated/policyContext.js.map +7 -0
  78. package/dist/backend/modules.d.ts +33 -0
  79. package/dist/backend/modules.d.ts.map +1 -0
  80. package/dist/backend/modules.js +100 -0
  81. package/dist/backend/modules.js.map +7 -0
  82. package/dist/backend/options.d.ts +78 -0
  83. package/dist/backend/options.d.ts.map +1 -0
  84. package/dist/backend/options.js +153 -0
  85. package/dist/backend/options.js.map +7 -0
  86. package/dist/backend/paymentRuntime.d.ts +31 -0
  87. package/dist/backend/paymentRuntime.d.ts.map +1 -0
  88. package/dist/backend/paymentRuntime.js +231 -0
  89. package/dist/backend/paymentRuntime.js.map +7 -0
  90. package/dist/backend/policies/common.d.ts +102 -0
  91. package/dist/backend/policies/common.d.ts.map +1 -0
  92. package/dist/backend/policies/common.js +226 -0
  93. package/dist/backend/policies/common.js.map +7 -0
  94. package/dist/backend/routes/gateway/guard.d.ts +7 -0
  95. package/dist/backend/routes/gateway/guard.d.ts.map +1 -0
  96. package/dist/backend/routes/gateway/guard.js +89 -0
  97. package/dist/backend/routes/gateway/guard.js.map +7 -0
  98. package/dist/backend/routes/gateway/hostApi.d.ts +8 -0
  99. package/dist/backend/routes/gateway/hostApi.d.ts.map +1 -0
  100. package/dist/backend/routes/gateway/hostApi.js +45 -0
  101. package/dist/backend/routes/gateway/hostApi.js.map +7 -0
  102. package/dist/backend/routes/gateway/hostApiBridge.d.ts +5 -0
  103. package/dist/backend/routes/gateway/hostApiBridge.d.ts.map +1 -0
  104. package/dist/backend/routes/gateway/hostApiBridge.js +61 -0
  105. package/dist/backend/routes/gateway/hostApiBridge.js.map +7 -0
  106. package/dist/backend/routes/gateway/hostApiCore.d.ts +5 -0
  107. package/dist/backend/routes/gateway/hostApiCore.d.ts.map +1 -0
  108. package/dist/backend/routes/gateway/hostApiCore.js +95 -0
  109. package/dist/backend/routes/gateway/hostApiCore.js.map +7 -0
  110. package/dist/backend/routes/gateway/hostApiLifecycle.d.ts +5 -0
  111. package/dist/backend/routes/gateway/hostApiLifecycle.d.ts.map +1 -0
  112. package/dist/backend/routes/gateway/hostApiLifecycle.js +80 -0
  113. package/dist/backend/routes/gateway/hostApiLifecycle.js.map +7 -0
  114. package/dist/backend/routes/gateway/hostContractBoundary.d.ts +25 -0
  115. package/dist/backend/routes/gateway/hostContractBoundary.d.ts.map +1 -0
  116. package/dist/backend/routes/gateway/hostContractBoundary.js +43 -0
  117. package/dist/backend/routes/gateway/hostContractBoundary.js.map +7 -0
  118. package/dist/backend/routes/gateway/payment.d.ts +4 -0
  119. package/dist/backend/routes/gateway/payment.d.ts.map +1 -0
  120. package/dist/backend/routes/gateway/payment.js +10 -0
  121. package/dist/backend/routes/gateway/payment.js.map +7 -0
  122. package/dist/backend/routes/gateway/shared.d.ts +36 -0
  123. package/dist/backend/routes/gateway/shared.d.ts.map +1 -0
  124. package/dist/backend/routes/gateway/shared.js +208 -0
  125. package/dist/backend/routes/gateway/shared.js.map +7 -0
  126. package/dist/backend/routes/gateway/subjectProfiles.d.ts +2 -0
  127. package/dist/backend/routes/gateway/subjectProfiles.d.ts.map +1 -0
  128. package/dist/backend/routes/gateway/subjectProfiles.js +150 -0
  129. package/dist/backend/routes/gateway/subjectProfiles.js.map +7 -0
  130. package/dist/backend/routes/health.d.ts +2 -0
  131. package/dist/backend/routes/health.d.ts.map +1 -0
  132. package/dist/backend/routes/health.js +20 -0
  133. package/dist/backend/routes/health.js.map +7 -0
  134. package/dist/backend/routes/reference.d.ts +2 -0
  135. package/dist/backend/routes/reference.d.ts.map +1 -0
  136. package/dist/backend/routes/reference.js +414 -0
  137. package/dist/backend/routes/reference.js.map +7 -0
  138. package/dist/index.d.ts +10 -0
  139. package/dist/index.d.ts.map +1 -0
  140. package/dist/index.js +81 -0
  141. package/dist/index.js.map +7 -0
  142. package/package.json +42 -0
@@ -0,0 +1,208 @@
1
+ import {
2
+ EmbedHostProxyInputError,
3
+ EmbedHostProxyNotConfiguredError,
4
+ GatewayApiClientError
5
+ } from "@xapps/server-sdk";
6
+ import crypto from "node:crypto";
7
+ const HOST_BOOTSTRAP_HEADER = "x-xapps-host-bootstrap";
8
+ function toBase64Url(input) {
9
+ return Buffer.from(String(input), "utf8").toString("base64url");
10
+ }
11
+ function fromBase64UrlJson(input) {
12
+ const raw = Buffer.from(String(input || ""), "base64url").toString("utf8");
13
+ return JSON.parse(raw);
14
+ }
15
+ function normalizeOrigin(value) {
16
+ const raw = String(value || "").trim();
17
+ return raw ? raw.replace(/\/+$/, "") : "";
18
+ }
19
+ function normalizeAllowedOrigins(allowedOrigins = []) {
20
+ return Array.isArray(allowedOrigins) ? allowedOrigins.map((entry) => normalizeOrigin(entry)).filter(Boolean) : [];
21
+ }
22
+ function readBodyRecord(value) {
23
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
24
+ }
25
+ function requireHostProxyService(hostProxyService) {
26
+ if (!hostProxyService || typeof hostProxyService.getHostConfig !== "function") {
27
+ throw new EmbedHostProxyNotConfiguredError("Host proxy service is not configured");
28
+ }
29
+ return hostProxyService;
30
+ }
31
+ function readRequestOrigin(request) {
32
+ return normalizeOrigin(request?.headers?.origin);
33
+ }
34
+ function readHostBootstrapToken(request) {
35
+ return String(request?.headers?.[HOST_BOOTSTRAP_HEADER] || "").trim();
36
+ }
37
+ function issueHostBootstrapToken(payload, signingSecret) {
38
+ const encodedPayload = toBase64Url(JSON.stringify(payload));
39
+ const signature = crypto.createHmac("sha256", String(signingSecret)).update(encodedPayload).digest("base64url");
40
+ return `${encodedPayload}.${signature}`;
41
+ }
42
+ function verifyHostBootstrapToken(token, signingSecret) {
43
+ const parts = String(token || "").trim().split(".");
44
+ if (parts.length !== 2) {
45
+ throw new EmbedHostProxyInputError("host bootstrap token is malformed", 401);
46
+ }
47
+ const [encodedPayload, receivedSignature] = parts;
48
+ const expectedSignature = crypto.createHmac("sha256", String(signingSecret)).update(encodedPayload).digest("base64url");
49
+ const received = Buffer.from(receivedSignature, "utf8");
50
+ const expected = Buffer.from(expectedSignature, "utf8");
51
+ if (received.length !== expected.length || !crypto.timingSafeEqual(received, expected)) {
52
+ throw new EmbedHostProxyInputError("host bootstrap token is invalid", 401);
53
+ }
54
+ const payload = fromBase64UrlJson(encodedPayload);
55
+ if (!payload || typeof payload !== "object") {
56
+ throw new EmbedHostProxyInputError("host bootstrap token payload is invalid", 401);
57
+ }
58
+ const now = Math.floor(Date.now() / 1e3);
59
+ if (Number(payload.exp || 0) <= now) {
60
+ throw new EmbedHostProxyInputError("host bootstrap token expired", 401);
61
+ }
62
+ return payload;
63
+ }
64
+ function readHostBootstrapContext(request, bootstrap = {}) {
65
+ const signingSecret = String(bootstrap?.signingSecret || "").trim();
66
+ const token = readHostBootstrapToken(request);
67
+ if (!token || !signingSecret) return null;
68
+ const payload = verifyHostBootstrapToken(token, signingSecret);
69
+ const requestOrigin = readRequestOrigin(request);
70
+ const tokenOrigin = normalizeOrigin(payload.origin);
71
+ if (tokenOrigin && requestOrigin && tokenOrigin !== requestOrigin) {
72
+ throw new EmbedHostProxyInputError("host bootstrap token origin mismatch", 401);
73
+ }
74
+ return {
75
+ subjectId: String(payload.subjectId || "").trim() || null,
76
+ email: String(payload.email || "").trim() || null,
77
+ name: String(payload.name || "").trim() || null,
78
+ origin: tokenOrigin || null,
79
+ token
80
+ };
81
+ }
82
+ function requireHostBootstrapRequest(request, bootstrap = {}) {
83
+ const allowedApiKeys = Array.isArray(bootstrap?.apiKeys) ? bootstrap.apiKeys.map((entry) => String(entry || "").trim()).filter(Boolean) : [];
84
+ const signingSecret = String(bootstrap?.signingSecret || "").trim();
85
+ if (allowedApiKeys.length === 0 || !signingSecret) {
86
+ throw new EmbedHostProxyNotConfiguredError("Host bootstrap is not configured");
87
+ }
88
+ const apiKey = String(request?.headers?.["x-api-key"] || "").trim();
89
+ if (!apiKey || !allowedApiKeys.includes(apiKey)) {
90
+ throw new EmbedHostProxyInputError("Invalid host bootstrap api key", 401);
91
+ }
92
+ return {
93
+ apiKey,
94
+ signingSecret,
95
+ ttlSeconds: Number.isFinite(Number(bootstrap?.ttlSeconds)) && Number(bootstrap?.ttlSeconds) > 0 ? Math.floor(Number(bootstrap.ttlSeconds)) : 300
96
+ };
97
+ }
98
+ function buildHostBootstrapResult({
99
+ subjectId,
100
+ email,
101
+ name,
102
+ origin,
103
+ signingSecret,
104
+ ttlSeconds
105
+ }) {
106
+ const now = Math.floor(Date.now() / 1e3);
107
+ const expiresIn = Number(ttlSeconds) > 0 ? Math.floor(Number(ttlSeconds)) : 300;
108
+ const bootstrapToken = issueHostBootstrapToken(
109
+ {
110
+ v: 1,
111
+ type: "host_bootstrap",
112
+ subjectId: String(subjectId || "").trim(),
113
+ email: String(email || "").trim(),
114
+ name: String(name || "").trim(),
115
+ origin: normalizeOrigin(origin),
116
+ iat: now,
117
+ exp: now + expiresIn
118
+ },
119
+ signingSecret
120
+ );
121
+ return {
122
+ subjectId: String(subjectId || "").trim(),
123
+ email: String(email || "").trim(),
124
+ name: String(name || "").trim() || null,
125
+ bootstrapToken,
126
+ expiresIn
127
+ };
128
+ }
129
+ function isOriginAllowed(origin, allowedOrigins = []) {
130
+ const normalizedOrigin = normalizeOrigin(origin);
131
+ if (!normalizedOrigin) return true;
132
+ const normalizedAllowedOrigins = normalizeAllowedOrigins(allowedOrigins);
133
+ if (normalizedAllowedOrigins.length === 0) {
134
+ return true;
135
+ }
136
+ return normalizedAllowedOrigins.includes(normalizedOrigin);
137
+ }
138
+ function applyHostApiCorsHeaders(reply, request, allowedOrigins = []) {
139
+ const origin = readRequestOrigin(request);
140
+ if (!origin) return;
141
+ const normalizedAllowedOrigins = normalizeAllowedOrigins(allowedOrigins);
142
+ if (normalizedAllowedOrigins.length === 0) return;
143
+ if (!normalizedAllowedOrigins.includes(origin)) return;
144
+ reply.header("Access-Control-Allow-Origin", origin);
145
+ reply.header("Vary", "Origin");
146
+ }
147
+ function ensureHostApiOriginAllowed(request, reply, allowedOrigins = []) {
148
+ const origin = readRequestOrigin(request);
149
+ if (!origin) return true;
150
+ if (!isOriginAllowed(origin, allowedOrigins)) {
151
+ reply.code(403).send({ message: "Origin is not allowed" });
152
+ return false;
153
+ }
154
+ applyHostApiCorsHeaders(reply, request, allowedOrigins);
155
+ return true;
156
+ }
157
+ function sendHostApiPreflight(request, reply, allowedOrigins = []) {
158
+ const origin = readRequestOrigin(request);
159
+ if (origin && !isOriginAllowed(origin, allowedOrigins)) {
160
+ return reply.code(403).send({ message: "Origin is not allowed" });
161
+ }
162
+ applyHostApiCorsHeaders(reply, request, allowedOrigins);
163
+ const requestedHeaders = String(
164
+ request?.headers?.["access-control-request-headers"] || ""
165
+ ).trim();
166
+ reply.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
167
+ reply.header(
168
+ "Access-Control-Allow-Headers",
169
+ requestedHeaders || "Content-Type, Authorization, X-Xapps-Host-Bootstrap"
170
+ );
171
+ reply.header("Access-Control-Max-Age", "600");
172
+ return reply.code(204).send();
173
+ }
174
+ function applyNoStoreHeaders(reply, hostProxyService) {
175
+ const service = requireHostProxyService(hostProxyService);
176
+ const headers = service.getNoStoreHeaders();
177
+ reply.header("Cache-Control", headers["Cache-Control"]);
178
+ reply.header("Pragma", headers.Pragma);
179
+ reply.header("Expires", headers.Expires);
180
+ return reply;
181
+ }
182
+ function sendServiceError(request, reply, err, fallbackMessage) {
183
+ if (err instanceof EmbedHostProxyInputError || err instanceof EmbedHostProxyNotConfiguredError) {
184
+ return reply.code(err.status).send({ message: err.message });
185
+ }
186
+ if (err instanceof GatewayApiClientError) {
187
+ return reply.code(err.status || 502).send(
188
+ err.details && typeof err.details === "object" ? err.details : { message: err.message }
189
+ );
190
+ }
191
+ request.log.error({ err }, fallbackMessage);
192
+ return reply.code(500).send({ message: fallbackMessage });
193
+ }
194
+ export {
195
+ applyHostApiCorsHeaders,
196
+ applyNoStoreHeaders,
197
+ buildHostBootstrapResult,
198
+ ensureHostApiOriginAllowed,
199
+ isOriginAllowed,
200
+ readBodyRecord,
201
+ readHostBootstrapContext,
202
+ readRequestOrigin,
203
+ requireHostBootstrapRequest,
204
+ requireHostProxyService,
205
+ sendHostApiPreflight,
206
+ sendServiceError
207
+ };
208
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/backend/routes/gateway/shared.ts"],
4
+ "sourcesContent": ["// @ts-nocheck\nimport {\n EmbedHostProxyInputError,\n EmbedHostProxyNotConfiguredError,\n GatewayApiClientError,\n} from \"@xapps/server-sdk\";\nimport crypto from \"node:crypto\";\n\nconst HOST_BOOTSTRAP_HEADER = \"x-xapps-host-bootstrap\";\n\nfunction toBase64Url(input) {\n return Buffer.from(String(input), \"utf8\").toString(\"base64url\");\n}\n\nfunction fromBase64UrlJson(input) {\n const raw = Buffer.from(String(input || \"\"), \"base64url\").toString(\"utf8\");\n return JSON.parse(raw);\n}\n\nfunction normalizeOrigin(value) {\n const raw = String(value || \"\").trim();\n return raw ? raw.replace(/\\/+$/, \"\") : \"\";\n}\n\nfunction normalizeAllowedOrigins(allowedOrigins = []) {\n return Array.isArray(allowedOrigins)\n ? allowedOrigins.map((entry) => normalizeOrigin(entry)).filter(Boolean)\n : [];\n}\n\nexport function readBodyRecord(value) {\n return value && typeof value === \"object\" && !Array.isArray(value) ? value : {};\n}\n\nexport function requireHostProxyService(hostProxyService) {\n if (!hostProxyService || typeof hostProxyService.getHostConfig !== \"function\") {\n throw new EmbedHostProxyNotConfiguredError(\"Host proxy service is not configured\");\n }\n return hostProxyService;\n}\n\nexport function readRequestOrigin(request) {\n return normalizeOrigin(request?.headers?.origin);\n}\n\nfunction readHostBootstrapToken(request) {\n return String(request?.headers?.[HOST_BOOTSTRAP_HEADER] || \"\").trim();\n}\n\nfunction issueHostBootstrapToken(payload, signingSecret) {\n const encodedPayload = toBase64Url(JSON.stringify(payload));\n const signature = crypto\n .createHmac(\"sha256\", String(signingSecret))\n .update(encodedPayload)\n .digest(\"base64url\");\n return `${encodedPayload}.${signature}`;\n}\n\nfunction verifyHostBootstrapToken(token, signingSecret) {\n const parts = String(token || \"\")\n .trim()\n .split(\".\");\n if (parts.length !== 2) {\n throw new EmbedHostProxyInputError(\"host bootstrap token is malformed\", 401);\n }\n const [encodedPayload, receivedSignature] = parts;\n const expectedSignature = crypto\n .createHmac(\"sha256\", String(signingSecret))\n .update(encodedPayload)\n .digest(\"base64url\");\n const received = Buffer.from(receivedSignature, \"utf8\");\n const expected = Buffer.from(expectedSignature, \"utf8\");\n if (received.length !== expected.length || !crypto.timingSafeEqual(received, expected)) {\n throw new EmbedHostProxyInputError(\"host bootstrap token is invalid\", 401);\n }\n const payload = fromBase64UrlJson(encodedPayload);\n if (!payload || typeof payload !== \"object\") {\n throw new EmbedHostProxyInputError(\"host bootstrap token payload is invalid\", 401);\n }\n const now = Math.floor(Date.now() / 1000);\n if (Number(payload.exp || 0) <= now) {\n throw new EmbedHostProxyInputError(\"host bootstrap token expired\", 401);\n }\n return payload;\n}\n\nexport function readHostBootstrapContext(request, bootstrap = {}) {\n const signingSecret = String(bootstrap?.signingSecret || \"\").trim();\n const token = readHostBootstrapToken(request);\n if (!token || !signingSecret) return null;\n const payload = verifyHostBootstrapToken(token, signingSecret);\n const requestOrigin = readRequestOrigin(request);\n const tokenOrigin = normalizeOrigin(payload.origin);\n if (tokenOrigin && requestOrigin && tokenOrigin !== requestOrigin) {\n throw new EmbedHostProxyInputError(\"host bootstrap token origin mismatch\", 401);\n }\n return {\n subjectId: String(payload.subjectId || \"\").trim() || null,\n email: String(payload.email || \"\").trim() || null,\n name: String(payload.name || \"\").trim() || null,\n origin: tokenOrigin || null,\n token,\n };\n}\n\nexport function requireHostBootstrapRequest(request, bootstrap = {}) {\n const allowedApiKeys = Array.isArray(bootstrap?.apiKeys)\n ? bootstrap.apiKeys.map((entry) => String(entry || \"\").trim()).filter(Boolean)\n : [];\n const signingSecret = String(bootstrap?.signingSecret || \"\").trim();\n if (allowedApiKeys.length === 0 || !signingSecret) {\n throw new EmbedHostProxyNotConfiguredError(\"Host bootstrap is not configured\");\n }\n const apiKey = String(request?.headers?.[\"x-api-key\"] || \"\").trim();\n if (!apiKey || !allowedApiKeys.includes(apiKey)) {\n throw new EmbedHostProxyInputError(\"Invalid host bootstrap api key\", 401);\n }\n return {\n apiKey,\n signingSecret,\n ttlSeconds:\n Number.isFinite(Number(bootstrap?.ttlSeconds)) && Number(bootstrap?.ttlSeconds) > 0\n ? Math.floor(Number(bootstrap.ttlSeconds))\n : 300,\n };\n}\n\nexport function buildHostBootstrapResult({\n subjectId,\n email,\n name,\n origin,\n signingSecret,\n ttlSeconds,\n}) {\n const now = Math.floor(Date.now() / 1000);\n const expiresIn = Number(ttlSeconds) > 0 ? Math.floor(Number(ttlSeconds)) : 300;\n const bootstrapToken = issueHostBootstrapToken(\n {\n v: 1,\n type: \"host_bootstrap\",\n subjectId: String(subjectId || \"\").trim(),\n email: String(email || \"\").trim(),\n name: String(name || \"\").trim(),\n origin: normalizeOrigin(origin),\n iat: now,\n exp: now + expiresIn,\n },\n signingSecret,\n );\n return {\n subjectId: String(subjectId || \"\").trim(),\n email: String(email || \"\").trim(),\n name: String(name || \"\").trim() || null,\n bootstrapToken,\n expiresIn,\n };\n}\n\nexport function isOriginAllowed(origin, allowedOrigins = []) {\n const normalizedOrigin = normalizeOrigin(origin);\n if (!normalizedOrigin) return true;\n const normalizedAllowedOrigins = normalizeAllowedOrigins(allowedOrigins);\n if (normalizedAllowedOrigins.length === 0) {\n return true;\n }\n return normalizedAllowedOrigins.includes(normalizedOrigin);\n}\n\nexport function applyHostApiCorsHeaders(reply, request, allowedOrigins = []) {\n const origin = readRequestOrigin(request);\n if (!origin) return;\n const normalizedAllowedOrigins = normalizeAllowedOrigins(allowedOrigins);\n if (normalizedAllowedOrigins.length === 0) return;\n if (!normalizedAllowedOrigins.includes(origin)) return;\n reply.header(\"Access-Control-Allow-Origin\", origin);\n reply.header(\"Vary\", \"Origin\");\n}\n\nexport function ensureHostApiOriginAllowed(request, reply, allowedOrigins = []) {\n const origin = readRequestOrigin(request);\n if (!origin) return true;\n if (!isOriginAllowed(origin, allowedOrigins)) {\n reply.code(403).send({ message: \"Origin is not allowed\" });\n return false;\n }\n applyHostApiCorsHeaders(reply, request, allowedOrigins);\n return true;\n}\n\nexport function sendHostApiPreflight(request, reply, allowedOrigins = []) {\n const origin = readRequestOrigin(request);\n if (origin && !isOriginAllowed(origin, allowedOrigins)) {\n return reply.code(403).send({ message: \"Origin is not allowed\" });\n }\n applyHostApiCorsHeaders(reply, request, allowedOrigins);\n const requestedHeaders = String(\n request?.headers?.[\"access-control-request-headers\"] || \"\",\n ).trim();\n reply.header(\"Access-Control-Allow-Methods\", \"GET, POST, OPTIONS\");\n reply.header(\n \"Access-Control-Allow-Headers\",\n requestedHeaders || \"Content-Type, Authorization, X-Xapps-Host-Bootstrap\",\n );\n reply.header(\"Access-Control-Max-Age\", \"600\");\n return reply.code(204).send();\n}\n\nexport function applyNoStoreHeaders(reply, hostProxyService) {\n const service = requireHostProxyService(hostProxyService);\n const headers = service.getNoStoreHeaders();\n reply.header(\"Cache-Control\", headers[\"Cache-Control\"]);\n reply.header(\"Pragma\", headers.Pragma);\n reply.header(\"Expires\", headers.Expires);\n return reply;\n}\n\nexport function sendServiceError(request, reply, err, fallbackMessage) {\n if (err instanceof EmbedHostProxyInputError || err instanceof EmbedHostProxyNotConfiguredError) {\n return reply.code(err.status).send({ message: err.message });\n }\n if (err instanceof GatewayApiClientError) {\n return reply\n .code(err.status || 502)\n .send(\n err.details && typeof err.details === \"object\" ? err.details : { message: err.message },\n );\n }\n request.log.error({ err }, fallbackMessage);\n return reply.code(500).send({ message: fallbackMessage });\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,2 @@
1
+ export default function subjectProfileRoutes(fastify: any, options?: {}): Promise<void>;
2
+ //# sourceMappingURL=subjectProfiles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subjectProfiles.d.ts","sourceRoot":"","sources":["../../../../src/backend/routes/gateway/subjectProfiles.ts"],"names":[],"mappings":"AAyKA,wBAA8B,oBAAoB,CAAC,OAAO,KAAA,EAAE,OAAO,KAAK,iBAMvE"}
@@ -0,0 +1,150 @@
1
+ function asObject(value) {
2
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
3
+ }
4
+ function asArray(value) {
5
+ return Array.isArray(value) ? value : [];
6
+ }
7
+ function readBool(value) {
8
+ if (typeof value === "boolean") return value;
9
+ return ["1", "true", "yes", "y", "on"].includes(
10
+ String(value ?? "").trim().toLowerCase()
11
+ );
12
+ }
13
+ function readString(...values) {
14
+ for (const value of values) {
15
+ const normalized = String(value ?? "").trim();
16
+ if (normalized) return normalized;
17
+ }
18
+ return "";
19
+ }
20
+ function readCatalogProfiles(rawCatalog) {
21
+ if (!rawCatalog) return [];
22
+ try {
23
+ const parsed = JSON.parse(rawCatalog);
24
+ if (Array.isArray(parsed)) return parsed;
25
+ const root = asObject(parsed);
26
+ const directProfiles = [...asArray(root.profiles), ...asArray(root.items)];
27
+ const scoped = [];
28
+ const bySubject = asObject(root.by_subject || root.bySubject || root.subjects);
29
+ for (const [subjectId, value] of Object.entries(bySubject)) {
30
+ for (const entry of asArray(value)) {
31
+ scoped.push({
32
+ ...asObject(entry) || {},
33
+ subject_id: readString(asObject(entry).subject_id, asObject(entry).subjectId, subjectId)
34
+ });
35
+ }
36
+ }
37
+ return [...directProfiles, ...scoped];
38
+ } catch {
39
+ return [];
40
+ }
41
+ }
42
+ function normalizeSubjectProfileCandidate(raw, index, requestedFamily, workspace) {
43
+ const record = asObject(raw);
44
+ const nestedData = asObject(
45
+ record.data ?? record.profile ?? record.customerProfile ?? record.customer_profile
46
+ );
47
+ const inferredFamily = readString(record.profile_family, record.profileFamily, nestedData.profile_family) || (() => {
48
+ const hasBusinessIdentity = readString(
49
+ nestedData.company_name,
50
+ nestedData.companyName,
51
+ nestedData.company_identification_number,
52
+ nestedData.vat_code,
53
+ nestedData.company_registration_number
54
+ );
55
+ if (hasBusinessIdentity) return "billing_business";
56
+ const hasIdentityOnly = readString(nestedData.name, nestedData.email, nestedData.phone) && !readString(nestedData.address, nestedData.city, nestedData.country);
57
+ return hasIdentityOnly ? "identity_basic" : "billing_individual";
58
+ })();
59
+ const data = Object.keys(nestedData).length > 0 ? { ...nestedData, profile_family: inferredFamily } : {
60
+ profile_family: inferredFamily,
61
+ company_name: readString(record.company_name, record.companyName) || null,
62
+ company_identification_number: readString(record.company_identification_number) || null,
63
+ vat_code: readString(record.vat_code) || null,
64
+ company_registration_number: readString(record.company_registration_number) || null,
65
+ address: readString(record.address) || null,
66
+ city: readString(record.city) || null,
67
+ country: readString(record.country) || null,
68
+ email: readString(record.email) || null,
69
+ phone: readString(record.phone) || null,
70
+ name: readString(record.name) || null,
71
+ linked_profiles: asArray(record.linked_profiles || record.linkedProfiles)
72
+ };
73
+ const candidate = {
74
+ id: readString(record.id, record.profile_id, record.profileId) || `${workspace}_profile_${index + 1}`,
75
+ label: readString(
76
+ record.label,
77
+ record.profile_label,
78
+ record.profileLabel,
79
+ data.company_name,
80
+ data.name
81
+ ) || `${workspace} profile ${index + 1}`,
82
+ profile_family: inferredFamily,
83
+ is_default: readBool(record.is_default ?? record.isDefault) || index === 0,
84
+ data,
85
+ subject_id: readString(record.subject_id, record.subjectId) || null
86
+ };
87
+ if (requestedFamily && candidate.profile_family !== requestedFamily) return null;
88
+ return candidate;
89
+ }
90
+ async function buildSubjectProfileCandidateEnvelope(payload, options = {}) {
91
+ const guardContext = asObject(payload.guard_context || payload.guardContext);
92
+ const subjectId = readString(payload.subjectId, payload.subject_id, guardContext.subjectId);
93
+ const requestedFamily = readString(payload.profile_family, payload.profileFamily) || null;
94
+ const xappSlug = readString(payload.xapp_slug, payload.xappSlug);
95
+ const toolName = readString(payload.tool_name, payload.toolName);
96
+ const workspace = readString(options.workspace) || "tenant";
97
+ const source = readString(options.source) || "tenant_subject_profile";
98
+ const configuredProfiles = readCatalogProfiles(readString(options.catalogJson));
99
+ let resolvedProfiles = [];
100
+ if (typeof options.resolveCandidates === "function") {
101
+ const resolved = await options.resolveCandidates({
102
+ payload,
103
+ subjectId: subjectId || null,
104
+ requestedFamily,
105
+ xappSlug: xappSlug || null,
106
+ toolName: toolName || null
107
+ });
108
+ resolvedProfiles = Array.isArray(resolved) ? resolved : [];
109
+ }
110
+ const providedDefaultProfiles = Array.isArray(options.defaultProfiles) ? options.defaultProfiles : [];
111
+ const effectiveCatalog = resolvedProfiles.length > 0 ? resolvedProfiles : configuredProfiles.length > 0 ? configuredProfiles : providedDefaultProfiles;
112
+ const profiles = effectiveCatalog.map(
113
+ (entry, index) => normalizeSubjectProfileCandidate(entry, index, requestedFamily, workspace)
114
+ ).filter(Boolean).filter((entry) => {
115
+ const scopedSubjectId = readString(entry.subject_id);
116
+ return !subjectId || !scopedSubjectId || scopedSubjectId === subjectId;
117
+ });
118
+ const selected = profiles.find((entry) => entry.is_default) || profiles[0] || null;
119
+ return {
120
+ ok: true,
121
+ selected_profile_id: selected?.id || null,
122
+ profiles: profiles.map((entry) => {
123
+ const { subject_id, ...rest } = entry;
124
+ void subject_id;
125
+ return rest;
126
+ }),
127
+ source,
128
+ metadata: {
129
+ workspace,
130
+ subject_id: subjectId || null,
131
+ requested_family: requestedFamily,
132
+ xapp_slug: xappSlug || null,
133
+ tool_name: toolName || null,
134
+ profile_count: profiles.length,
135
+ configured_catalog: configuredProfiles.length > 0,
136
+ default_catalog: configuredProfiles.length === 0 && providedDefaultProfiles.length > 0
137
+ }
138
+ };
139
+ }
140
+ async function subjectProfileRoutes(fastify, options = {}) {
141
+ fastify.post("/guard/subject-profiles/tenant-candidates", async (request, reply) => {
142
+ const body = request.body && typeof request.body === "object" ? request.body : {};
143
+ const payload = body.payload && typeof body.payload === "object" ? body.payload : body;
144
+ return reply.send(await buildSubjectProfileCandidateEnvelope(payload, options));
145
+ });
146
+ }
147
+ export {
148
+ subjectProfileRoutes as default
149
+ };
150
+ //# sourceMappingURL=subjectProfiles.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/backend/routes/gateway/subjectProfiles.ts"],
4
+ "sourcesContent": ["// @ts-nocheck\nfunction asObject(value) {\n return value && typeof value === \"object\" && !Array.isArray(value) ? value : {};\n}\n\nfunction asArray(value) {\n return Array.isArray(value) ? value : [];\n}\n\nfunction readBool(value) {\n if (typeof value === \"boolean\") return value;\n return [\"1\", \"true\", \"yes\", \"y\", \"on\"].includes(\n String(value ?? \"\")\n .trim()\n .toLowerCase(),\n );\n}\n\nfunction readString(...values) {\n for (const value of values) {\n const normalized = String(value ?? \"\").trim();\n if (normalized) return normalized;\n }\n return \"\";\n}\n\nfunction readCatalogProfiles(rawCatalog) {\n if (!rawCatalog) return [];\n try {\n const parsed = JSON.parse(rawCatalog);\n if (Array.isArray(parsed)) return parsed;\n const root = asObject(parsed);\n const directProfiles = [...asArray(root.profiles), ...asArray(root.items)];\n const scoped = [];\n const bySubject = asObject(root.by_subject || root.bySubject || root.subjects);\n for (const [subjectId, value] of Object.entries(bySubject)) {\n for (const entry of asArray(value)) {\n scoped.push({\n ...(asObject(entry) || {}),\n subject_id: readString(asObject(entry).subject_id, asObject(entry).subjectId, subjectId),\n });\n }\n }\n return [...directProfiles, ...scoped];\n } catch {\n return [];\n }\n}\n\nfunction normalizeSubjectProfileCandidate(raw, index, requestedFamily, workspace) {\n const record = asObject(raw);\n const nestedData = asObject(\n record.data ?? record.profile ?? record.customerProfile ?? record.customer_profile,\n );\n const inferredFamily =\n readString(record.profile_family, record.profileFamily, nestedData.profile_family) ||\n (() => {\n const hasBusinessIdentity = readString(\n nestedData.company_name,\n nestedData.companyName,\n nestedData.company_identification_number,\n nestedData.vat_code,\n nestedData.company_registration_number,\n );\n if (hasBusinessIdentity) return \"billing_business\";\n const hasIdentityOnly =\n readString(nestedData.name, nestedData.email, nestedData.phone) &&\n !readString(nestedData.address, nestedData.city, nestedData.country);\n return hasIdentityOnly ? \"identity_basic\" : \"billing_individual\";\n })();\n const data =\n Object.keys(nestedData).length > 0\n ? { ...nestedData, profile_family: inferredFamily }\n : {\n profile_family: inferredFamily,\n company_name: readString(record.company_name, record.companyName) || null,\n company_identification_number: readString(record.company_identification_number) || null,\n vat_code: readString(record.vat_code) || null,\n company_registration_number: readString(record.company_registration_number) || null,\n address: readString(record.address) || null,\n city: readString(record.city) || null,\n country: readString(record.country) || null,\n email: readString(record.email) || null,\n phone: readString(record.phone) || null,\n name: readString(record.name) || null,\n linked_profiles: asArray(record.linked_profiles || record.linkedProfiles),\n };\n const candidate = {\n id:\n readString(record.id, record.profile_id, record.profileId) ||\n `${workspace}_profile_${index + 1}`,\n label:\n readString(\n record.label,\n record.profile_label,\n record.profileLabel,\n data.company_name,\n data.name,\n ) || `${workspace} profile ${index + 1}`,\n profile_family: inferredFamily,\n is_default: readBool(record.is_default ?? record.isDefault) || index === 0,\n data,\n subject_id: readString(record.subject_id, record.subjectId) || null,\n };\n if (requestedFamily && candidate.profile_family !== requestedFamily) return null;\n return candidate;\n}\n\nasync function buildSubjectProfileCandidateEnvelope(payload, options = {}) {\n const guardContext = asObject(payload.guard_context || payload.guardContext);\n const subjectId = readString(payload.subjectId, payload.subject_id, guardContext.subjectId);\n const requestedFamily = readString(payload.profile_family, payload.profileFamily) || null;\n const xappSlug = readString(payload.xapp_slug, payload.xappSlug);\n const toolName = readString(payload.tool_name, payload.toolName);\n const workspace = readString(options.workspace) || \"tenant\";\n const source = readString(options.source) || \"tenant_subject_profile\";\n const configuredProfiles = readCatalogProfiles(readString(options.catalogJson));\n let resolvedProfiles = [];\n if (typeof options.resolveCandidates === \"function\") {\n const resolved = await options.resolveCandidates({\n payload,\n subjectId: subjectId || null,\n requestedFamily,\n xappSlug: xappSlug || null,\n toolName: toolName || null,\n });\n resolvedProfiles = Array.isArray(resolved) ? resolved : [];\n }\n const providedDefaultProfiles = Array.isArray(options.defaultProfiles)\n ? options.defaultProfiles\n : [];\n const effectiveCatalog =\n resolvedProfiles.length > 0\n ? resolvedProfiles\n : configuredProfiles.length > 0\n ? configuredProfiles\n : providedDefaultProfiles;\n const profiles = effectiveCatalog\n .map((entry, index) =>\n normalizeSubjectProfileCandidate(entry, index, requestedFamily, workspace),\n )\n .filter(Boolean)\n .filter((entry) => {\n const scopedSubjectId = readString(entry.subject_id);\n return !subjectId || !scopedSubjectId || scopedSubjectId === subjectId;\n });\n const selected = profiles.find((entry) => entry.is_default) || profiles[0] || null;\n return {\n ok: true,\n selected_profile_id: selected?.id || null,\n profiles: profiles.map((entry) => {\n const { subject_id, ...rest } = entry;\n void subject_id;\n return rest;\n }),\n source,\n metadata: {\n workspace,\n subject_id: subjectId || null,\n requested_family: requestedFamily,\n xapp_slug: xappSlug || null,\n tool_name: toolName || null,\n profile_count: profiles.length,\n configured_catalog: configuredProfiles.length > 0,\n default_catalog: configuredProfiles.length === 0 && providedDefaultProfiles.length > 0,\n },\n };\n}\n\nexport default async function subjectProfileRoutes(fastify, options = {}) {\n fastify.post(\"/guard/subject-profiles/tenant-candidates\", async (request, reply) => {\n const body = request.body && typeof request.body === \"object\" ? request.body : {};\n const payload = body.payload && typeof body.payload === \"object\" ? body.payload : body;\n return reply.send(await buildSubjectProfileCandidateEnvelope(payload, options));\n });\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,2 @@
1
+ export default function healthRoutes(fastify: any, options?: {}): Promise<void>;
2
+ //# sourceMappingURL=health.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../../src/backend/routes/health.ts"],"names":[],"mappings":"AAKA,wBAA8B,YAAY,CAAC,OAAO,KAAA,EAAE,OAAO,KAAK,iBAe/D"}
@@ -0,0 +1,20 @@
1
+ function nowIso() {
2
+ return (/* @__PURE__ */ new Date()).toISOString();
3
+ }
4
+ async function healthRoutes(fastify, options = {}) {
5
+ const branding = options.branding && typeof options.branding === "object" ? options.branding : {};
6
+ const reference = options.reference && typeof options.reference === "object" ? options.reference : {};
7
+ const tools = Array.isArray(options.tools) ? options.tools.filter(Boolean) : [];
8
+ fastify.get("/health", async () => ({
9
+ ok: true,
10
+ service: String(branding.serviceName || "").trim() || "tenant-backend",
11
+ mode: String(reference.mode || "").trim() || "reference-minimum",
12
+ ...String(branding.stackLabel || "").trim() ? { stack: String(branding.stackLabel).trim() } : {},
13
+ ...tools.length > 0 ? { tools } : {},
14
+ time: nowIso()
15
+ }));
16
+ }
17
+ export {
18
+ healthRoutes as default
19
+ };
20
+ //# sourceMappingURL=health.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/backend/routes/health.ts"],
4
+ "sourcesContent": ["// @ts-nocheck\nfunction nowIso() {\n return new Date().toISOString();\n}\n\nexport default async function healthRoutes(fastify, options = {}) {\n const branding = options.branding && typeof options.branding === \"object\" ? options.branding : {};\n const reference =\n options.reference && typeof options.reference === \"object\" ? options.reference : {};\n const tools = Array.isArray(options.tools) ? options.tools.filter(Boolean) : [];\n fastify.get(\"/health\", async () => ({\n ok: true,\n service: String(branding.serviceName || \"\").trim() || \"tenant-backend\",\n mode: String(reference.mode || \"\").trim() || \"reference-minimum\",\n ...(String(branding.stackLabel || \"\").trim()\n ? { stack: String(branding.stackLabel).trim() }\n : {}),\n ...(tools.length > 0 ? { tools } : {}),\n time: nowIso(),\n }));\n}\n"],
5
+ "mappings": "AACA,SAAS,SAAS;AAChB,UAAO,oBAAI,KAAK,GAAE,YAAY;AAChC;AAEA,eAAO,aAAoC,SAAS,UAAU,CAAC,GAAG;AAChE,QAAM,WAAW,QAAQ,YAAY,OAAO,QAAQ,aAAa,WAAW,QAAQ,WAAW,CAAC;AAChG,QAAM,YACJ,QAAQ,aAAa,OAAO,QAAQ,cAAc,WAAW,QAAQ,YAAY,CAAC;AACpF,QAAM,QAAQ,MAAM,QAAQ,QAAQ,KAAK,IAAI,QAAQ,MAAM,OAAO,OAAO,IAAI,CAAC;AAC9E,UAAQ,IAAI,WAAW,aAAa;AAAA,IAClC,IAAI;AAAA,IACJ,SAAS,OAAO,SAAS,eAAe,EAAE,EAAE,KAAK,KAAK;AAAA,IACtD,MAAM,OAAO,UAAU,QAAQ,EAAE,EAAE,KAAK,KAAK;AAAA,IAC7C,GAAI,OAAO,SAAS,cAAc,EAAE,EAAE,KAAK,IACvC,EAAE,OAAO,OAAO,SAAS,UAAU,EAAE,KAAK,EAAE,IAC5C,CAAC;AAAA,IACL,GAAI,MAAM,SAAS,IAAI,EAAE,MAAM,IAAI,CAAC;AAAA,IACpC,MAAM,OAAO;AAAA,EACf,EAAE;AACJ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,2 @@
1
+ export default function referenceRoutes(fastify: any, options?: {}): Promise<void>;
2
+ //# sourceMappingURL=reference.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reference.d.ts","sourceRoot":"","sources":["../../../src/backend/routes/reference.ts"],"names":[],"mappings":"AA8XA,wBAA8B,eAAe,CAAC,OAAO,KAAA,EAAE,OAAO,KAAK,iBA2ElE"}