@htlkg/core 0.0.1 → 0.0.3

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 (45) hide show
  1. package/README.md +51 -0
  2. package/dist/amplify-astro-adapter/index.d.ts +109 -0
  3. package/dist/amplify-astro-adapter/index.js +295 -0
  4. package/dist/amplify-astro-adapter/index.js.map +1 -0
  5. package/dist/auth/index.d.ts +1 -1
  6. package/dist/auth/index.js +305 -1
  7. package/dist/auth/index.js.map +1 -1
  8. package/dist/index.d.ts +220 -0
  9. package/dist/index.js +426 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/logger-BTW3fOeM.d.ts +45 -0
  12. package/dist/utils/index.d.ts +3 -0
  13. package/dist/utils/index.js +55 -0
  14. package/dist/utils/index.js.map +1 -1
  15. package/package.json +56 -33
  16. package/src/amplify-astro-adapter/amplify-astro-adapter.md +167 -0
  17. package/src/amplify-astro-adapter/createCookieStorageAdapterFromAstroContext.test.ts +296 -0
  18. package/src/amplify-astro-adapter/createCookieStorageAdapterFromAstroContext.ts +97 -0
  19. package/src/amplify-astro-adapter/createRunWithAmplifyServerContext.ts +84 -0
  20. package/src/amplify-astro-adapter/errors.test.ts +115 -0
  21. package/src/amplify-astro-adapter/errors.ts +105 -0
  22. package/src/amplify-astro-adapter/globalSettings.test.ts +78 -0
  23. package/src/amplify-astro-adapter/globalSettings.ts +16 -0
  24. package/src/amplify-astro-adapter/index.ts +14 -0
  25. package/src/amplify-astro-adapter/types.ts +55 -0
  26. package/src/auth/auth.md +178 -0
  27. package/src/auth/index.test.ts +180 -0
  28. package/src/auth/index.ts +294 -0
  29. package/src/constants/constants.md +132 -0
  30. package/src/constants/index.test.ts +116 -0
  31. package/src/constants/index.ts +98 -0
  32. package/src/core-exports.property.test.ts +186 -0
  33. package/src/errors/errors.md +177 -0
  34. package/src/errors/index.test.ts +153 -0
  35. package/src/errors/index.ts +134 -0
  36. package/src/index.ts +65 -0
  37. package/src/routes/index.ts +225 -0
  38. package/src/routes/routes.md +189 -0
  39. package/src/types/index.ts +94 -0
  40. package/src/types/types.md +144 -0
  41. package/src/utils/index.test.ts +257 -0
  42. package/src/utils/index.ts +112 -0
  43. package/src/utils/logger.ts +88 -0
  44. package/src/utils/utils.md +199 -0
  45. package/src/workspace.property.test.ts +235 -0
package/dist/index.js CHANGED
@@ -1,11 +1,315 @@
1
1
  // src/auth/index.ts
2
+ import { Amplify } from "aws-amplify";
2
3
  import { fetchAuthSession } from "aws-amplify/auth";
4
+ import {
5
+ fetchAuthSession as fetchServerAuthSession,
6
+ getCurrentUser as getServerCurrentUser
7
+ } from "aws-amplify/auth/server";
8
+
9
+ // src/amplify-astro-adapter/createRunWithAmplifyServerContext.ts
10
+ import { sharedInMemoryStorage } from "aws-amplify/utils";
11
+ import {
12
+ createAWSCredentialsAndIdentityIdProvider,
13
+ createKeyValueStorageFromCookieStorageAdapter,
14
+ createUserPoolsTokenProvider,
15
+ runWithAmplifyServerContext as coreRunWithContext
16
+ } from "aws-amplify/adapter-core";
17
+
18
+ // src/amplify-astro-adapter/globalSettings.ts
19
+ var globalSettings = /* @__PURE__ */ (() => {
20
+ let runtimeOptions = {};
21
+ let serverSideAuthEnabled = true;
22
+ let sslOrigin = false;
23
+ return {
24
+ setRuntimeOptions(opts) {
25
+ runtimeOptions = opts ?? {};
26
+ },
27
+ getRuntimeOptions() {
28
+ return runtimeOptions;
29
+ },
30
+ enableServerSideAuth() {
31
+ serverSideAuthEnabled = true;
32
+ },
33
+ isServerSideAuthEnabled() {
34
+ return serverSideAuthEnabled;
35
+ },
36
+ setIsSSLOrigin(v) {
37
+ sslOrigin = v;
38
+ },
39
+ isSSLOrigin() {
40
+ return sslOrigin;
41
+ }
42
+ };
43
+ })();
44
+
45
+ // src/utils/logger.ts
46
+ var isDebugEnabled = () => {
47
+ if (typeof process !== "undefined" && process.env) {
48
+ return process.env.DEBUG === "true" || process.env.HTLKG_DEBUG === "true" || process.env.DEBUG === "*";
49
+ }
50
+ try {
51
+ if (typeof import.meta !== "undefined" && import.meta.env) {
52
+ return import.meta.env.DEBUG === "true" || // @ts-ignore
53
+ import.meta.env.HTLKG_DEBUG === "true" || // @ts-ignore
54
+ import.meta.env.DEV === true;
55
+ }
56
+ } catch {
57
+ }
58
+ return false;
59
+ };
60
+ var formatMessage = (namespace, message) => {
61
+ return `[${namespace}] ${message}`;
62
+ };
63
+ var logger = {
64
+ /**
65
+ * Debug log - only shown when DEBUG=true
66
+ */
67
+ debug(namespace, message, ...args) {
68
+ if (isDebugEnabled()) {
69
+ console.log(formatMessage(namespace, message), ...args);
70
+ }
71
+ },
72
+ /**
73
+ * Info log - always shown
74
+ */
75
+ info(namespace, message, ...args) {
76
+ console.info(formatMessage(namespace, message), ...args);
77
+ },
78
+ /**
79
+ * Warning log - always shown
80
+ */
81
+ warn(namespace, message, ...args) {
82
+ console.warn(formatMessage(namespace, message), ...args);
83
+ },
84
+ /**
85
+ * Error log - always shown
86
+ */
87
+ error(namespace, message, ...args) {
88
+ console.error(formatMessage(namespace, message), ...args);
89
+ }
90
+ };
91
+ var createLogger = (namespace) => ({
92
+ debug: (message, ...args) => logger.debug(namespace, message, ...args),
93
+ info: (message, ...args) => logger.info(namespace, message, ...args),
94
+ warn: (message, ...args) => logger.warn(namespace, message, ...args),
95
+ error: (message, ...args) => logger.error(namespace, message, ...args)
96
+ });
97
+
98
+ // src/amplify-astro-adapter/createCookieStorageAdapterFromAstroContext.ts
99
+ var log = createLogger("cookie-storage-adapter");
100
+ function ensureEncodedForJSCookie(name) {
101
+ return encodeURIComponent(name).replace(/%(2[346B]|5E|60|7C)/g, decodeURIComponent);
102
+ }
103
+ function parseCookieHeader(cookieHeader) {
104
+ const out = {};
105
+ if (!cookieHeader) return out;
106
+ for (const part of cookieHeader.split(";")) {
107
+ const [rawName, ...rest] = part.trim().split("=");
108
+ const name = decodeURIComponent(rawName || "");
109
+ const value = decodeURIComponent(rest.join("=") || "");
110
+ if (name) out[name] = value;
111
+ }
112
+ return out;
113
+ }
114
+ function mapSameSite(s) {
115
+ if (s === true) return "strict";
116
+ if (s === false) return "lax";
117
+ if (s === "lax" || s === "strict" || s === "none") return s;
118
+ return void 0;
119
+ }
120
+ async function createCookieStorageAdapterFromAstroContext(astroServerContext) {
121
+ if (astroServerContext === null) {
122
+ log.debug("Context is null, returning no-op adapter");
123
+ return {
124
+ get: () => void 0,
125
+ getAll: () => [],
126
+ set: () => {
127
+ },
128
+ delete: () => {
129
+ }
130
+ };
131
+ }
132
+ const { cookies, request } = astroServerContext;
133
+ const cookieHeader = request?.headers?.get("cookie");
134
+ const headerCookies = parseCookieHeader(cookieHeader ?? null);
135
+ const cookieNames = Object.keys(headerCookies);
136
+ const cognitoCookies = cookieNames.filter((name) => name.includes("CognitoIdentityServiceProvider"));
137
+ log.debug("Available cookies:", cookieNames.length);
138
+ log.debug("Cognito cookies found:", cognitoCookies.length);
139
+ if (cognitoCookies.length > 0) {
140
+ log.debug("Cognito cookie names:", cognitoCookies);
141
+ }
142
+ const adapter = {
143
+ get(name) {
144
+ const encodedName = ensureEncodedForJSCookie(name);
145
+ const v = cookies?.get(encodedName)?.value ?? headerCookies[encodedName] ?? headerCookies[name];
146
+ if (name.includes("accessToken") || name.includes("idToken") || name.includes("refreshToken") || name.includes("LastAuthUser")) {
147
+ log.debug(`get('${name}'): ${v ? "FOUND" : "NOT FOUND"}`);
148
+ }
149
+ return v ? { name, value: v } : void 0;
150
+ },
151
+ getAll() {
152
+ const fromAPI = typeof cookies?.getAll === "function" ? cookies.getAll().map((c) => ({ name: c.name, value: c.value })) : Object.entries(headerCookies).map(([name, value]) => ({ name, value }));
153
+ return fromAPI;
154
+ },
155
+ set(name, value, options) {
156
+ try {
157
+ cookies.set(name, value, {
158
+ ...options ?? {},
159
+ sameSite: mapSameSite(options?.sameSite)
160
+ });
161
+ } catch {
162
+ }
163
+ },
164
+ delete(name) {
165
+ try {
166
+ cookies.delete(name);
167
+ } catch {
168
+ }
169
+ }
170
+ };
171
+ return adapter;
172
+ }
173
+
174
+ // src/amplify-astro-adapter/createRunWithAmplifyServerContext.ts
175
+ var log2 = createLogger("amplify-server-context");
176
+ var createRunWithAmplifyServerContext = ({
177
+ config: resourcesConfig,
178
+ globalSettings: globalSettings2 = globalSettings
179
+ }) => {
180
+ const isServerSideAuthEnabled = globalSettings2.isServerSideAuthEnabled();
181
+ const isSSLOrigin = globalSettings2.isSSLOrigin();
182
+ const setCookieOptions = globalSettings2.getRuntimeOptions().cookies ?? {};
183
+ log2.debug("Settings:", {
184
+ isServerSideAuthEnabled,
185
+ isSSLOrigin,
186
+ hasCookieOptions: Object.keys(setCookieOptions).length > 0
187
+ });
188
+ const mergedSetCookieOptions = {
189
+ ...isServerSideAuthEnabled && { httpOnly: true, sameSite: "lax" },
190
+ ...setCookieOptions,
191
+ ...isServerSideAuthEnabled && { secure: isSSLOrigin },
192
+ path: "/"
193
+ };
194
+ const runWithContext = async ({
195
+ astroServerContext,
196
+ operation
197
+ }) => {
198
+ if (resourcesConfig.Auth) {
199
+ const cookieAdapter = await createCookieStorageAdapterFromAstroContext(astroServerContext);
200
+ const keyValueStorage = astroServerContext === null ? sharedInMemoryStorage : createKeyValueStorageFromCookieStorageAdapter(
201
+ cookieAdapter,
202
+ void 0,
203
+ mergedSetCookieOptions
204
+ );
205
+ const credentialsProvider = createAWSCredentialsAndIdentityIdProvider(
206
+ resourcesConfig.Auth,
207
+ keyValueStorage
208
+ );
209
+ const tokenProvider = createUserPoolsTokenProvider(
210
+ resourcesConfig.Auth,
211
+ keyValueStorage
212
+ );
213
+ return coreRunWithContext(
214
+ resourcesConfig,
215
+ { Auth: { credentialsProvider, tokenProvider } },
216
+ operation
217
+ );
218
+ }
219
+ return coreRunWithContext(resourcesConfig, {}, operation);
220
+ };
221
+ return runWithContext;
222
+ };
223
+
224
+ // src/auth/index.ts
3
225
  function parseIds(ids) {
4
226
  if (!ids) return [];
5
227
  return ids.split(",").map((id) => Number.parseInt(id.trim(), 10)).filter((id) => !Number.isNaN(id));
6
228
  }
229
+ var amplifyConfigured = false;
230
+ function ensureAmplifyConfigured() {
231
+ if (amplifyConfigured) return;
232
+ try {
233
+ const config = Amplify.getConfig();
234
+ if (!config.Auth) {
235
+ throw new Error("Amplify Auth not configured");
236
+ }
237
+ amplifyConfigured = true;
238
+ } catch (error) {
239
+ const errorMsg = error instanceof Error ? error.message : "Unknown error";
240
+ console.error(`[Auth] Amplify not configured: ${errorMsg}`);
241
+ throw error;
242
+ }
243
+ }
7
244
  async function getUser(context) {
8
- return null;
245
+ try {
246
+ ensureAmplifyConfigured();
247
+ const amplifyConfig = Amplify.getConfig();
248
+ const runWithAmplifyServerContext = createRunWithAmplifyServerContext({
249
+ config: amplifyConfig,
250
+ globalSettings
251
+ });
252
+ const astroServerContext = {
253
+ cookies: context.cookies,
254
+ request: context.request
255
+ };
256
+ const user = await runWithAmplifyServerContext({
257
+ astroServerContext,
258
+ operation: async (contextSpec) => {
259
+ try {
260
+ const currentUser = await getServerCurrentUser(contextSpec);
261
+ const session = await fetchServerAuthSession(contextSpec);
262
+ if (!session.tokens?.accessToken) {
263
+ return null;
264
+ }
265
+ const accessPayload = session.tokens.accessToken.payload;
266
+ const idPayload = session.tokens.idToken?.payload;
267
+ const email = idPayload?.email || accessPayload.email || "";
268
+ const brandIds = parseIds(
269
+ accessPayload["custom:brand_ids"]
270
+ );
271
+ const accountIds = parseIds(
272
+ accessPayload["custom:account_ids"]
273
+ );
274
+ const groups = accessPayload["cognito:groups"] || [];
275
+ const isAdmin = groups.includes("admin") || groups.includes("ADMINS") || groups.includes("SUPER_ADMINS");
276
+ const isSuperAdmin = groups.includes("SUPER_ADMINS");
277
+ return {
278
+ username: currentUser.username,
279
+ email,
280
+ brandIds,
281
+ accountIds,
282
+ isAdmin,
283
+ isSuperAdmin,
284
+ roles: groups
285
+ };
286
+ } catch (error) {
287
+ if (error instanceof Error && error.name !== "UserUnAuthenticatedException") {
288
+ console.error(
289
+ "[Auth] Error in server context:",
290
+ error.name,
291
+ "-",
292
+ error.message.replace(/token[=:]\s*[^\s,}]+/gi, "token=***")
293
+ );
294
+ }
295
+ return null;
296
+ }
297
+ }
298
+ });
299
+ return user;
300
+ } catch (error) {
301
+ if (error instanceof Error) {
302
+ console.error(
303
+ "[Auth] Server-side auth error:",
304
+ error.name,
305
+ "-",
306
+ error.message.replace(/token[=:]\s*[^\s,}]+/gi, "token=***")
307
+ );
308
+ } else {
309
+ console.error("[Auth] Server-side auth error: Unknown error");
310
+ }
311
+ return null;
312
+ }
9
313
  }
10
314
  async function getClientUser() {
11
315
  try {
@@ -282,6 +586,117 @@ var NotFoundError = class _NotFoundError extends AppError {
282
586
  };
283
587
  }
284
588
  };
589
+
590
+ // src/routes/index.ts
591
+ function createRoute(path) {
592
+ const routeFn = (params) => {
593
+ if (!params) return path;
594
+ let result = path;
595
+ for (const [key, value] of Object.entries(params)) {
596
+ result = result.replace(`:${key}`, String(value));
597
+ }
598
+ return result;
599
+ };
600
+ Object.defineProperty(routeFn, "path", {
601
+ value: path,
602
+ writable: false,
603
+ enumerable: true
604
+ });
605
+ return routeFn;
606
+ }
607
+ var adminRoutes = {
608
+ /** Dashboard */
609
+ dashboard: createRoute("/admin"),
610
+ /** Accounts list */
611
+ accounts: createRoute("/admin/accounts"),
612
+ /** Account detail */
613
+ account: createRoute("/admin/accounts/:id"),
614
+ /** Account brands */
615
+ accountBrands: createRoute("/admin/accounts/:id/brands"),
616
+ /** Brands list */
617
+ brands: createRoute("/admin/brands"),
618
+ /** Brand detail */
619
+ brand: createRoute("/admin/brands/:id"),
620
+ /** Brand settings */
621
+ brandSettings: createRoute("/admin/brands/:id/settings"),
622
+ /** Users list */
623
+ users: createRoute("/admin/users"),
624
+ /** User detail */
625
+ user: createRoute("/admin/users/:id"),
626
+ /** Current user profile */
627
+ profile: createRoute("/admin/user"),
628
+ /** Analytics */
629
+ analytics: createRoute("/admin/analytics")
630
+ };
631
+ var portalRoutes = {
632
+ /** Portal home */
633
+ home: createRoute("/"),
634
+ /** Brand portal */
635
+ brand: createRoute("/brands/:brandId"),
636
+ /** Brand settings */
637
+ brandSettings: createRoute("/brands/:brandId/settings"),
638
+ /** Brand dashboard */
639
+ brandDashboard: createRoute("/brands/:brandId/dashboard"),
640
+ /** Brand analytics */
641
+ brandAnalytics: createRoute("/brands/:brandId/analytics")
642
+ };
643
+ var authRoutes = {
644
+ /** Login page */
645
+ login: createRoute("/login"),
646
+ /** Logout endpoint */
647
+ logout: createRoute("/api/auth/logout"),
648
+ /** Confirm sign in */
649
+ confirmSignIn: createRoute("/api/auth/confirm-signin")
650
+ };
651
+ var apiRoutes = {
652
+ /** Auth endpoints */
653
+ auth: {
654
+ logout: createRoute("/api/auth/logout"),
655
+ confirmSignIn: createRoute("/api/auth/confirm-signin")
656
+ }
657
+ };
658
+ var routes = {
659
+ admin: adminRoutes,
660
+ portal: portalRoutes,
661
+ auth: authRoutes,
662
+ api: apiRoutes
663
+ };
664
+ function matchesRoute(path, route) {
665
+ const pattern = route.path.replace(/:[^/]+/g, "[^/]+").replace(/\//g, "\\/");
666
+ const regex = new RegExp(`^${pattern}$`);
667
+ return regex.test(path);
668
+ }
669
+ function extractRouteParams(path, route) {
670
+ const paramNames = [];
671
+ const pattern = route.path.replace(/:([^/]+)/g, (_, name) => {
672
+ paramNames.push(name);
673
+ return "([^/]+)";
674
+ });
675
+ const regex = new RegExp(`^${pattern}$`);
676
+ const match = path.match(regex);
677
+ if (!match) return null;
678
+ const params = {};
679
+ paramNames.forEach((name, index) => {
680
+ params[name] = match[index + 1];
681
+ });
682
+ return params;
683
+ }
684
+ function navigateTo(url) {
685
+ if (typeof window !== "undefined") {
686
+ window.location.href = url;
687
+ }
688
+ }
689
+ function buildUrl(path, params) {
690
+ if (!params) return path;
691
+ const searchParams = new URLSearchParams();
692
+ for (const [key, value] of Object.entries(params)) {
693
+ if (value !== null && value !== void 0 && value !== "") {
694
+ searchParams.set(key, String(value));
695
+ }
696
+ }
697
+ const queryString = searchParams.toString();
698
+ return queryString ? `${path}?${queryString}` : path;
699
+ }
285
700
  export {
286
701
  AppError,
287
702
  AuthError,
@@ -291,7 +706,13 @@ export {
291
706
  ROUTES,
292
707
  USER_ROLES,
293
708
  ValidationError,
709
+ adminRoutes,
710
+ apiRoutes,
711
+ authRoutes,
712
+ buildUrl,
713
+ createRoute,
294
714
  debounce,
715
+ extractRouteParams,
295
716
  formatDate,
296
717
  getClientUser,
297
718
  getUser,
@@ -300,9 +721,13 @@ export {
300
721
  hasAccessToBrand,
301
722
  isAdminUser,
302
723
  isSuperAdminUser,
724
+ matchesRoute,
725
+ navigateTo,
726
+ portalRoutes,
303
727
  requireAdminAccess,
304
728
  requireAuth,
305
729
  requireBrandAccess,
730
+ routes,
306
731
  throttle,
307
732
  truncateText
308
733
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/auth/index.ts","../src/utils/index.ts","../src/constants/index.ts","../src/errors/index.ts"],"sourcesContent":["/**\n * @htlkg/core/auth\n * Core authentication functions and utilities\n */\n\nimport type { APIContext } from \"astro\";\nimport { Amplify } from \"aws-amplify\";\nimport { fetchAuthSession } from \"aws-amplify/auth\";\nimport {\n\tfetchAuthSession as fetchServerAuthSession,\n\tgetCurrentUser as getServerCurrentUser,\n} from \"aws-amplify/auth/server\";\n\n// Re-export types\nexport type { APIContext } from \"astro\";\n\n/**\n * User interface representing an authenticated user\n */\nexport interface User {\n\tusername: string;\n\temail: string;\n\tbrandIds: number[];\n\taccountIds: number[];\n\tisAdmin: boolean;\n\tisSuperAdmin: boolean;\n\troles: string[];\n}\n\n/**\n * Parse comma-separated IDs from Cognito custom attributes\n */\nfunction parseIds(ids: string | undefined): number[] {\n\tif (!ids) return [];\n\treturn ids\n\t\t.split(\",\")\n\t\t.map((id) => Number.parseInt(id.trim(), 10))\n\t\t.filter((id) => !Number.isNaN(id));\n}\n\n/**\n * Get current authenticated user (server-side)\n * This is a simplified version that will be enhanced with Amplify server context\n */\nexport async function getUser(context: APIContext): Promise<User | null> {\n\t// This is a placeholder - the full implementation will be in the integration layer\n\t// that has access to the Amplify server context utilities\n\treturn null;\n}\n\n/**\n * Get current authenticated user (client-side)\n */\nexport async function getClientUser(): Promise<User | null> {\n\ttry {\n\t\tconst session = await fetchAuthSession();\n\n\t\tif (!session.tokens?.accessToken) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst accessPayload = session.tokens.accessToken.payload;\n\t\tconst idPayload = session.tokens.idToken?.payload;\n\n\t\tconst email =\n\t\t\t(idPayload?.email as string) || (accessPayload.email as string) || \"\";\n\t\tconst brandIds = parseIds(accessPayload[\"custom:brand_ids\"] as string);\n\t\tconst accountIds = parseIds(accessPayload[\"custom:account_ids\"] as string);\n\n\t\tconst groups = (accessPayload[\"cognito:groups\"] as string[]) || [];\n\t\tconst isAdmin =\n\t\t\tgroups.includes(\"admin\") ||\n\t\t\tgroups.includes(\"ADMINS\") ||\n\t\t\tgroups.includes(\"SUPER_ADMINS\");\n\t\tconst isSuperAdmin = groups.includes(\"SUPER_ADMINS\");\n\n\t\treturn {\n\t\t\tusername: (accessPayload.username as string) || \"\",\n\t\t\temail,\n\t\t\tbrandIds,\n\t\t\taccountIds,\n\t\t\tisAdmin,\n\t\t\tisSuperAdmin,\n\t\t\troles: groups,\n\t\t};\n\t} catch (error) {\n\t\tif (error instanceof Error) {\n\t\t\tconsole.error(\n\t\t\t\t\"[Auth] Client auth error:\",\n\t\t\t\terror.name,\n\t\t\t\t\"-\",\n\t\t\t\terror.message.replace(/token[=:]\\s*[^\\s,}]+/gi, \"token=***\"),\n\t\t\t);\n\t\t}\n\t\treturn null;\n\t}\n}\n\n/**\n * Check if user has access to a specific brand\n */\nexport function hasAccessToBrand(\n\tuser: User | null,\n\tbrandId: number,\n): boolean {\n\tif (!user) return false;\n\tif (user.isAdmin) return true;\n\treturn user.brandIds.includes(brandId);\n}\n\n/**\n * Check if user has access to a specific account\n */\nexport function hasAccessToAccount(\n\tuser: User | null,\n\taccountId: number,\n): boolean {\n\tif (!user) return false;\n\tif (user.isAdmin) return true;\n\treturn user.accountIds.includes(accountId);\n}\n\n/**\n * Check if user is an admin\n */\nexport function isAdminUser(user: User | null): boolean {\n\treturn user?.isAdmin || false;\n}\n\n/**\n * Check if user is a super admin\n */\nexport function isSuperAdminUser(user: User | null): boolean {\n\treturn user?.isSuperAdmin || false;\n}\n\n/**\n * Placeholder for requireAuth - will be implemented in @htlkg/integrations\n * This is here for type exports and documentation\n */\nexport async function requireAuth(\n\tcontext: APIContext,\n\tloginUrl?: string,\n): Promise<{ success: boolean; user?: User; redirectTo?: string }> {\n\tthrow new Error(\n\t\t\"requireAuth should be imported from @htlkg/astro/middleware\",\n\t);\n}\n\n/**\n * Placeholder for requireAdminAccess - will be implemented in @htlkg/integrations\n */\nexport async function requireAdminAccess(\n\tcontext: APIContext,\n\tloginUrl?: string,\n): Promise<{ success: boolean; user?: User; redirectTo?: string }> {\n\tthrow new Error(\n\t\t\"requireAdminAccess should be imported from @htlkg/astro/middleware\",\n\t);\n}\n\n/**\n * Placeholder for requireBrandAccess - will be implemented in @htlkg/integrations\n */\nexport async function requireBrandAccess(\n\tcontext: APIContext,\n\tbrandId: number,\n\tloginUrl?: string,\n): Promise<{ success: boolean; user?: User; redirectTo?: string }> {\n\tthrow new Error(\n\t\t\"requireBrandAccess should be imported from @htlkg/astro/middleware\",\n\t);\n}\n","/**\n * @htlkg/core/utils\n * Core utility functions for Hotelinking applications\n */\n\n/**\n * Format a date to a localized string\n * @param date - Date to format\n * @param locale - Locale string (default: 'en-US')\n * @param options - Intl.DateTimeFormatOptions\n * @returns Formatted date string\n */\nexport function formatDate(\n\tdate: Date | string | number,\n\tlocale = \"en-US\",\n\toptions: Intl.DateTimeFormatOptions = {\n\t\tyear: \"numeric\",\n\t\tmonth: \"short\",\n\t\tday: \"numeric\",\n\t},\n): string {\n\tconst dateObj = typeof date === \"string\" || typeof date === \"number\" ? new Date(date) : date;\n\treturn new Intl.DateTimeFormat(locale, options).format(dateObj);\n}\n\n/**\n * Truncate text to a maximum length with ellipsis\n * @param text - Text to truncate\n * @param maxLength - Maximum length before truncation\n * @param suffix - Suffix to append (default: '...')\n * @returns Truncated text\n */\nexport function truncateText(\n\ttext: string,\n\tmaxLength: number,\n\tsuffix = \"...\",\n): string {\n\tif (text.length <= maxLength) return text;\n\treturn text.slice(0, maxLength - suffix.length) + suffix;\n}\n\n/**\n * Group an array of items by a key\n * @param items - Array of items to group\n * @param keyFn - Function to extract the grouping key\n * @returns Object with keys as group names and values as arrays of items\n */\nexport function groupBy<T>(\n\titems: T[],\n\tkeyFn: (item: T) => string | number,\n): Record<string | number, T[]> {\n\treturn items.reduce(\n\t\t(groups, item) => {\n\t\t\tconst key = keyFn(item);\n\t\t\tif (!groups[key]) {\n\t\t\t\tgroups[key] = [];\n\t\t\t}\n\t\t\tgroups[key].push(item);\n\t\t\treturn groups;\n\t\t},\n\t\t{} as Record<string | number, T[]>,\n\t);\n}\n\n/**\n * Debounce a function call\n * @param fn - Function to debounce\n * @param delay - Delay in milliseconds\n * @returns Debounced function\n */\nexport function debounce<T extends (...args: any[]) => any>(\n\tfn: T,\n\tdelay: number,\n): (...args: Parameters<T>) => void {\n\tlet timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n\treturn function debounced(...args: Parameters<T>) {\n\t\tif (timeoutId !== null) {\n\t\t\tclearTimeout(timeoutId);\n\t\t}\n\t\ttimeoutId = setTimeout(() => {\n\t\t\tfn(...args);\n\t\t\ttimeoutId = null;\n\t\t}, delay);\n\t};\n}\n\n/**\n * Throttle a function call\n * @param fn - Function to throttle\n * @param limit - Minimum time between calls in milliseconds\n * @returns Throttled function\n */\nexport function throttle<T extends (...args: any[]) => any>(\n\tfn: T,\n\tlimit: number,\n): (...args: Parameters<T>) => void {\n\tlet inThrottle = false;\n\n\treturn function throttled(...args: Parameters<T>) {\n\t\tif (!inThrottle) {\n\t\t\tfn(...args);\n\t\t\tinThrottle = true;\n\t\t\tsetTimeout(() => {\n\t\t\t\tinThrottle = false;\n\t\t\t}, limit);\n\t\t}\n\t};\n}\n","/**\n * @htlkg/core/constants\n * Core constants for Hotelinking applications\n */\n\n/**\n * Application routes\n */\nexport const ROUTES = {\n\t// Admin routes\n\tADMIN: {\n\t\tHOME: \"/admin\",\n\t\tBRANDS: \"/admin/brands\",\n\t\tACCOUNTS: \"/admin/accounts\",\n\t\tUSERS: \"/admin/users\",\n\t\tPRODUCTS: \"/admin/products\",\n\t\tSETTINGS: \"/admin/settings\",\n\t},\n\t// Brand routes\n\tBRAND: {\n\t\tHOME: (brandId: string) => `/${brandId}`,\n\t\tADMIN: (brandId: string) => `/${brandId}/admin`,\n\t\tTEMPLATES: (brandId: string) => `/${brandId}/admin/templates`,\n\t\tSETTINGS: (brandId: string) => `/${brandId}/admin/settings`,\n\t},\n\t// Auth routes\n\tAUTH: {\n\t\tLOGIN: \"/login\",\n\t\tLOGOUT: \"/logout\",\n\t\tSIGNUP: \"/signup\",\n\t\tFORGOT_PASSWORD: \"/forgot-password\",\n\t},\n} as const;\n\n/**\n * Product definitions\n */\nexport const PRODUCTS = {\n\tWIFI_PORTAL: {\n\t\tid: \"wifi-portal\",\n\t\tname: \"WiFi Portal\",\n\t\tdescription: \"Captive portal for WiFi authentication\",\n\t},\n\tWHATSAPP_CRM: {\n\t\tid: \"whatsapp-crm\",\n\t\tname: \"WhatsApp CRM\",\n\t\tdescription: \"WhatsApp integration for customer relationship management\",\n\t},\n\tANALYTICS: {\n\t\tid: \"analytics\",\n\t\tname: \"Analytics\",\n\t\tdescription: \"Analytics and reporting dashboard\",\n\t},\n} as const;\n\n/**\n * Permission definitions\n */\nexport const PERMISSIONS = {\n\t// Brand permissions\n\tBRAND_VIEW: \"brand:view\",\n\tBRAND_EDIT: \"brand:edit\",\n\tBRAND_DELETE: \"brand:delete\",\n\tBRAND_CREATE: \"brand:create\",\n\n\t// Account permissions\n\tACCOUNT_VIEW: \"account:view\",\n\tACCOUNT_EDIT: \"account:edit\",\n\tACCOUNT_DELETE: \"account:delete\",\n\tACCOUNT_CREATE: \"account:create\",\n\n\t// User permissions\n\tUSER_VIEW: \"user:view\",\n\tUSER_EDIT: \"user:edit\",\n\tUSER_DELETE: \"user:delete\",\n\tUSER_CREATE: \"user:create\",\n\n\t// Product permissions\n\tPRODUCT_VIEW: \"product:view\",\n\tPRODUCT_EDIT: \"product:edit\",\n\tPRODUCT_ENABLE: \"product:enable\",\n\tPRODUCT_DISABLE: \"product:disable\",\n\n\t// Admin permissions\n\tADMIN_ACCESS: \"admin:access\",\n\tSUPER_ADMIN_ACCESS: \"super_admin:access\",\n} as const;\n\n/**\n * User role definitions\n */\nexport const USER_ROLES = {\n\tSUPER_ADMIN: \"SUPER_ADMINS\",\n\tADMIN: \"ADMINS\",\n\tBRAND_ADMIN: \"BRAND_ADMINS\",\n\tBRAND_USER: \"BRAND_USERS\",\n\tUSER: \"USERS\",\n} as const;\n","/**\n * @htlkg/core/errors\n * Core error classes for Hotelinking applications\n */\n\n/**\n * Base application error class\n */\nexport class AppError extends Error {\n\tpublic readonly code: string;\n\tpublic readonly statusCode: number;\n\tpublic readonly details?: Record<string, any>;\n\n\tconstructor(\n\t\tmessage: string,\n\t\tcode = \"APP_ERROR\",\n\t\tstatusCode = 500,\n\t\tdetails?: Record<string, any>,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"AppError\";\n\t\tthis.code = code;\n\t\tthis.statusCode = statusCode;\n\t\tthis.details = details;\n\n\t\t// Maintains proper stack trace for where our error was thrown (only available on V8)\n\t\tif (Error.captureStackTrace) {\n\t\t\tError.captureStackTrace(this, AppError);\n\t\t}\n\t}\n\n\t/**\n\t * Convert error to JSON representation\n\t */\n\ttoJSON(): Record<string, any> {\n\t\treturn {\n\t\t\tname: this.name,\n\t\t\tmessage: this.message,\n\t\t\tcode: this.code,\n\t\t\tstatusCode: this.statusCode,\n\t\t\tdetails: this.details,\n\t\t};\n\t}\n}\n\n/**\n * Authentication error class\n */\nexport class AuthError extends AppError {\n\tconstructor(\n\t\tmessage: string,\n\t\tcode = \"AUTH_ERROR\",\n\t\tstatusCode = 401,\n\t\tdetails?: Record<string, any>,\n\t) {\n\t\tsuper(message, code, statusCode, details);\n\t\tthis.name = \"AuthError\";\n\n\t\tif (Error.captureStackTrace) {\n\t\t\tError.captureStackTrace(this, AuthError);\n\t\t}\n\t}\n}\n\n/**\n * Validation error class\n */\nexport class ValidationError extends AppError {\n\tpublic readonly errors: Array<{ field: string; message: string }>;\n\n\tconstructor(\n\t\tmessage: string,\n\t\terrors: Array<{ field: string; message: string }> = [],\n\t\tcode = \"VALIDATION_ERROR\",\n\t\tstatusCode = 400,\n\t) {\n\t\tsuper(message, code, statusCode, { errors });\n\t\tthis.name = \"ValidationError\";\n\t\tthis.errors = errors;\n\n\t\tif (Error.captureStackTrace) {\n\t\t\tError.captureStackTrace(this, ValidationError);\n\t\t}\n\t}\n\n\t/**\n\t * Convert error to JSON representation\n\t */\n\ttoJSON(): Record<string, any> {\n\t\treturn {\n\t\t\t...super.toJSON(),\n\t\t\terrors: this.errors,\n\t\t};\n\t}\n}\n\n/**\n * Not found error class\n */\nexport class NotFoundError extends AppError {\n\tpublic readonly resource: string;\n\tpublic readonly resourceId?: string;\n\n\tconstructor(\n\t\tresource: string,\n\t\tresourceId?: string,\n\t\tmessage?: string,\n\t\tcode = \"NOT_FOUND\",\n\t\tstatusCode = 404,\n\t) {\n\t\tconst defaultMessage = resourceId\n\t\t\t? `${resource} with id '${resourceId}' not found`\n\t\t\t: `${resource} not found`;\n\t\tsuper(message || defaultMessage, code, statusCode, { resource, resourceId });\n\t\tthis.name = \"NotFoundError\";\n\t\tthis.resource = resource;\n\t\tthis.resourceId = resourceId;\n\n\t\tif (Error.captureStackTrace) {\n\t\t\tError.captureStackTrace(this, NotFoundError);\n\t\t}\n\t}\n\n\t/**\n\t * Convert error to JSON representation\n\t */\n\ttoJSON(): Record<string, any> {\n\t\treturn {\n\t\t\t...super.toJSON(),\n\t\t\tresource: this.resource,\n\t\t\tresourceId: this.resourceId,\n\t\t};\n\t}\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/auth/index.ts","../src/amplify-astro-adapter/createRunWithAmplifyServerContext.ts","../src/amplify-astro-adapter/globalSettings.ts","../src/utils/logger.ts","../src/amplify-astro-adapter/createCookieStorageAdapterFromAstroContext.ts","../src/utils/index.ts","../src/constants/index.ts","../src/errors/index.ts","../src/routes/index.ts"],"sourcesContent":["/**\n * @htlkg/core/auth\n * Core authentication functions and utilities\n */\n\nimport type { APIContext } from \"astro\";\nimport { Amplify } from \"aws-amplify\";\nimport { fetchAuthSession } from \"aws-amplify/auth\";\nimport {\n\tfetchAuthSession as fetchServerAuthSession,\n\tgetCurrentUser as getServerCurrentUser,\n} from \"aws-amplify/auth/server\";\nimport { createRunWithAmplifyServerContext, globalSettings } from \"../amplify-astro-adapter\";\n\n// Re-export types\nexport type { APIContext } from \"astro\";\n\n/**\n * User interface representing an authenticated user\n */\nexport interface User {\n\tusername: string;\n\temail: string;\n\tbrandIds: number[];\n\taccountIds: number[];\n\tisAdmin: boolean;\n\tisSuperAdmin: boolean;\n\troles: string[];\n}\n\n/**\n * Parse comma-separated IDs from Cognito custom attributes\n */\nfunction parseIds(ids: string | undefined): number[] {\n\tif (!ids) return [];\n\treturn ids\n\t\t.split(\",\")\n\t\t.map((id) => Number.parseInt(id.trim(), 10))\n\t\t.filter((id) => !Number.isNaN(id));\n}\n\n// Track if Amplify has been configured\nlet amplifyConfigured = false;\n\n/**\n * Configure Amplify on first use (server-side)\n * This must be called before any auth operations\n */\nfunction ensureAmplifyConfigured(): void {\n\tif (amplifyConfigured) return;\n\n\ttry {\n\t\t// Amplify should already be configured by the middleware\n\t\t// This is just a safety check\n\t\tconst config = Amplify.getConfig();\n\t\tif (!config.Auth) {\n\t\t\tthrow new Error(\"Amplify Auth not configured\");\n\t\t}\n\t\tamplifyConfigured = true;\n\t} catch (error) {\n\t\tconst errorMsg = error instanceof Error ? error.message : \"Unknown error\";\n\t\tconsole.error(`[Auth] Amplify not configured: ${errorMsg}`);\n\t\tthrow error;\n\t}\n}\n\n/**\n * Get current authenticated user (server-side)\n * Reads Amplify auth cookies from the request and validates the session\n */\nexport async function getUser(context: APIContext): Promise<User | null> {\n\ttry {\n\t\tensureAmplifyConfigured();\n\t\t\n\t\t// Get the current Amplify configuration\n\t\tconst amplifyConfig = Amplify.getConfig();\n\n\t\t// Create the server context runner - pass globalSettings explicitly\n\t\t// to ensure the same instance is used across all modules\n\t\tconst runWithAmplifyServerContext = createRunWithAmplifyServerContext({\n\t\t\tconfig: amplifyConfig,\n\t\t\tglobalSettings,\n\t\t});\n\n\t\t// Create Astro server context\n\t\tconst astroServerContext = {\n\t\t\tcookies: context.cookies,\n\t\t\trequest: context.request,\n\t\t};\n\n\t\t// Run in Amplify server context\n\t\tconst user = await runWithAmplifyServerContext({\n\t\t\tastroServerContext,\n\t\t\toperation: async (contextSpec) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst currentUser = await getServerCurrentUser(contextSpec as any);\n\t\t\t\t\tconst session = await fetchServerAuthSession(contextSpec as any);\n\n\t\t\t\t\tif (!session.tokens?.accessToken) {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst accessPayload = session.tokens.accessToken.payload;\n\t\t\t\t\tconst idPayload = session.tokens.idToken?.payload;\n\n\t\t\t\t\t// Parse user attributes - email is typically in ID token, not access token\n\t\t\t\t\tconst email =\n\t\t\t\t\t\t(idPayload?.email as string) ||\n\t\t\t\t\t\t(accessPayload.email as string) ||\n\t\t\t\t\t\t\"\";\n\t\t\t\t\tconst brandIds = parseIds(\n\t\t\t\t\t\taccessPayload[\"custom:brand_ids\"] as string,\n\t\t\t\t\t);\n\t\t\t\t\tconst accountIds = parseIds(\n\t\t\t\t\t\taccessPayload[\"custom:account_ids\"] as string,\n\t\t\t\t\t);\n\n\t\t\t\t\t// Check admin status\n\t\t\t\t\tconst groups = (accessPayload[\"cognito:groups\"] as string[]) || [];\n\t\t\t\t\tconst isAdmin =\n\t\t\t\t\t\tgroups.includes(\"admin\") ||\n\t\t\t\t\t\tgroups.includes(\"ADMINS\") ||\n\t\t\t\t\t\tgroups.includes(\"SUPER_ADMINS\");\n\t\t\t\t\tconst isSuperAdmin = groups.includes(\"SUPER_ADMINS\");\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\tusername: currentUser.username,\n\t\t\t\t\t\temail,\n\t\t\t\t\t\tbrandIds,\n\t\t\t\t\t\taccountIds,\n\t\t\t\t\t\tisAdmin,\n\t\t\t\t\t\tisSuperAdmin,\n\t\t\t\t\t\troles: groups,\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// UserUnAuthenticatedException is expected when user is not logged in\n\t\t\t\t\t// Only log other errors without sensitive data\n\t\t\t\t\tif (\n\t\t\t\t\t\terror instanceof Error &&\n\t\t\t\t\t\terror.name !== \"UserUnAuthenticatedException\"\n\t\t\t\t\t) {\n\t\t\t\t\t\t// Log error name and message without stack trace or sensitive data\n\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\t\"[Auth] Error in server context:\",\n\t\t\t\t\t\t\terror.name,\n\t\t\t\t\t\t\t\"-\",\n\t\t\t\t\t\t\terror.message.replace(/token[=:]\\s*[^\\s,}]+/gi, \"token=***\"),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\n\t\treturn user;\n\t} catch (error) {\n\t\t// Log error without exposing sensitive information\n\t\tif (error instanceof Error) {\n\t\t\tconsole.error(\n\t\t\t\t\"[Auth] Server-side auth error:\",\n\t\t\t\terror.name,\n\t\t\t\t\"-\",\n\t\t\t\terror.message.replace(/token[=:]\\s*[^\\s,}]+/gi, \"token=***\"),\n\t\t\t);\n\t\t} else {\n\t\t\tconsole.error(\"[Auth] Server-side auth error: Unknown error\");\n\t\t}\n\t\treturn null;\n\t}\n}\n\n/**\n * Get current authenticated user (client-side)\n */\nexport async function getClientUser(): Promise<User | null> {\n\ttry {\n\t\tconst session = await fetchAuthSession();\n\n\t\tif (!session.tokens?.accessToken) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst accessPayload = session.tokens.accessToken.payload;\n\t\tconst idPayload = session.tokens.idToken?.payload;\n\n\t\tconst email =\n\t\t\t(idPayload?.email as string) || (accessPayload.email as string) || \"\";\n\t\tconst brandIds = parseIds(accessPayload[\"custom:brand_ids\"] as string);\n\t\tconst accountIds = parseIds(accessPayload[\"custom:account_ids\"] as string);\n\n\t\tconst groups = (accessPayload[\"cognito:groups\"] as string[]) || [];\n\t\tconst isAdmin =\n\t\t\tgroups.includes(\"admin\") ||\n\t\t\tgroups.includes(\"ADMINS\") ||\n\t\t\tgroups.includes(\"SUPER_ADMINS\");\n\t\tconst isSuperAdmin = groups.includes(\"SUPER_ADMINS\");\n\n\t\treturn {\n\t\t\tusername: (accessPayload.username as string) || \"\",\n\t\t\temail,\n\t\t\tbrandIds,\n\t\t\taccountIds,\n\t\t\tisAdmin,\n\t\t\tisSuperAdmin,\n\t\t\troles: groups,\n\t\t};\n\t} catch (error) {\n\t\tif (error instanceof Error) {\n\t\t\tconsole.error(\n\t\t\t\t\"[Auth] Client auth error:\",\n\t\t\t\terror.name,\n\t\t\t\t\"-\",\n\t\t\t\terror.message.replace(/token[=:]\\s*[^\\s,}]+/gi, \"token=***\"),\n\t\t\t);\n\t\t}\n\t\treturn null;\n\t}\n}\n\n/**\n * Check if user has access to a specific brand\n */\nexport function hasAccessToBrand(\n\tuser: User | null,\n\tbrandId: number,\n): boolean {\n\tif (!user) return false;\n\tif (user.isAdmin) return true;\n\treturn user.brandIds.includes(brandId);\n}\n\n/**\n * Check if user has access to a specific account\n */\nexport function hasAccessToAccount(\n\tuser: User | null,\n\taccountId: number,\n): boolean {\n\tif (!user) return false;\n\tif (user.isAdmin) return true;\n\treturn user.accountIds.includes(accountId);\n}\n\n/**\n * Check if user is an admin\n */\nexport function isAdminUser(user: User | null): boolean {\n\treturn user?.isAdmin || false;\n}\n\n/**\n * Check if user is a super admin\n */\nexport function isSuperAdminUser(user: User | null): boolean {\n\treturn user?.isSuperAdmin || false;\n}\n\n/**\n * Placeholder for requireAuth - will be implemented in @htlkg/integrations\n * This is here for type exports and documentation\n */\nexport async function requireAuth(\n\tcontext: APIContext,\n\tloginUrl?: string,\n): Promise<{ success: boolean; user?: User; redirectTo?: string }> {\n\tthrow new Error(\n\t\t\"requireAuth should be imported from @htlkg/astro/middleware\",\n\t);\n}\n\n/**\n * Placeholder for requireAdminAccess - will be implemented in @htlkg/integrations\n */\nexport async function requireAdminAccess(\n\tcontext: APIContext,\n\tloginUrl?: string,\n): Promise<{ success: boolean; user?: User; redirectTo?: string }> {\n\tthrow new Error(\n\t\t\"requireAdminAccess should be imported from @htlkg/astro/middleware\",\n\t);\n}\n\n/**\n * Placeholder for requireBrandAccess - will be implemented in @htlkg/integrations\n */\nexport async function requireBrandAccess(\n\tcontext: APIContext,\n\tbrandId: number,\n\tloginUrl?: string,\n): Promise<{ success: boolean; user?: User; redirectTo?: string }> {\n\tthrow new Error(\n\t\t\"requireBrandAccess should be imported from @htlkg/astro/middleware\",\n\t);\n}\n","import type { ResourcesConfig } from 'aws-amplify';\nimport { sharedInMemoryStorage } from 'aws-amplify/utils';\nimport {\n createAWSCredentialsAndIdentityIdProvider,\n createKeyValueStorageFromCookieStorageAdapter,\n createUserPoolsTokenProvider,\n runWithAmplifyServerContext as coreRunWithContext,\n} from 'aws-amplify/adapter-core';\n\nimport type { AstroServer } from './types';\nimport { globalSettings as defaultGlobalSettings } from './globalSettings';\nimport { createCookieStorageAdapterFromAstroContext } from './createCookieStorageAdapterFromAstroContext';\nimport { createLogger } from '../utils/logger';\n\nconst log = createLogger('amplify-server-context');\n\n/**\n * Creates a function that runs operations within the Amplify server context.\n *\n * IMPORTANT: Pass globalSettings explicitly to avoid module singleton issues\n * when using linked packages or different bundling contexts.\n */\nexport const createRunWithAmplifyServerContext = ({\n config: resourcesConfig,\n globalSettings = defaultGlobalSettings,\n}: {\n config: ResourcesConfig;\n globalSettings?: AstroServer.GlobalSettings;\n}): AstroServer.RunOperationWithContext => {\n const isServerSideAuthEnabled = globalSettings.isServerSideAuthEnabled();\n const isSSLOrigin = globalSettings.isSSLOrigin();\n const setCookieOptions = globalSettings.getRuntimeOptions().cookies ?? {};\n\n log.debug('Settings:', {\n isServerSideAuthEnabled,\n isSSLOrigin,\n hasCookieOptions: Object.keys(setCookieOptions).length > 0,\n });\n \n const mergedSetCookieOptions = {\n ...(isServerSideAuthEnabled && { httpOnly: true, sameSite: 'lax' as const }),\n ...setCookieOptions,\n ...(isServerSideAuthEnabled && { secure: isSSLOrigin }),\n path: '/',\n };\n\n const runWithContext: AstroServer.RunOperationWithContext = async ({\n astroServerContext,\n operation,\n }) => {\n if (resourcesConfig.Auth) {\n const cookieAdapter = await createCookieStorageAdapterFromAstroContext(astroServerContext);\n \n const keyValueStorage =\n astroServerContext === null\n ? sharedInMemoryStorage\n : createKeyValueStorageFromCookieStorageAdapter(\n cookieAdapter,\n undefined,\n mergedSetCookieOptions\n );\n\n const credentialsProvider = createAWSCredentialsAndIdentityIdProvider(\n resourcesConfig.Auth,\n keyValueStorage\n );\n \n const tokenProvider = createUserPoolsTokenProvider(\n resourcesConfig.Auth,\n keyValueStorage\n );\n\n return coreRunWithContext(\n resourcesConfig,\n { Auth: { credentialsProvider, tokenProvider } },\n operation\n );\n }\n\n return coreRunWithContext(resourcesConfig, {}, operation);\n };\n\n return runWithContext;\n};\n","import type { AstroServer } from './types';\n\nexport const globalSettings: AstroServer.GlobalSettings = (() => {\n let runtimeOptions: AstroServer.RuntimeOptions = {};\n let serverSideAuthEnabled = true;\n let sslOrigin = false;\n\n return {\n setRuntimeOptions(opts) { runtimeOptions = opts ?? {}; },\n getRuntimeOptions() { return runtimeOptions; },\n enableServerSideAuth() { serverSideAuthEnabled = true; },\n isServerSideAuthEnabled() { return serverSideAuthEnabled; },\n setIsSSLOrigin(v: boolean) { sslOrigin = v; },\n isSSLOrigin() { return sslOrigin; },\n };\n})();\n","/**\n * Simple debug logger utility\n * \n * Debug logs are only shown when DEBUG=true or HTLKG_DEBUG=true is set in environment.\n * Info logs are always shown.\n * \n * @example\n * ```typescript\n * import { logger } from '@htlkg/core/utils/logger';\n * \n * logger.debug('server-client', 'Detailed debug info', { data });\n * logger.info('server-client', 'Important info message');\n * logger.warn('server-client', 'Warning message');\n * logger.error('server-client', 'Error occurred', error);\n * ```\n */\n\ntype LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nconst isDebugEnabled = (): boolean => {\n // Check Node.js environment variables\n if (typeof process !== 'undefined' && process.env) {\n return process.env.DEBUG === 'true' || \n process.env.HTLKG_DEBUG === 'true' ||\n process.env.DEBUG === '*';\n }\n // Browser/Vite environment - check for DEV mode\n try {\n // @ts-ignore - import.meta.env is Vite-specific\n if (typeof import.meta !== 'undefined' && import.meta.env) {\n // @ts-ignore\n return import.meta.env.DEBUG === 'true' || \n // @ts-ignore\n import.meta.env.HTLKG_DEBUG === 'true' ||\n // @ts-ignore\n import.meta.env.DEV === true;\n }\n } catch {\n // Ignore errors accessing import.meta\n }\n return false;\n};\n\nconst formatMessage = (namespace: string, message: string): string => {\n return `[${namespace}] ${message}`;\n};\n\nexport const logger = {\n /**\n * Debug log - only shown when DEBUG=true\n */\n debug(namespace: string, message: string, ...args: unknown[]): void {\n if (isDebugEnabled()) {\n console.log(formatMessage(namespace, message), ...args);\n }\n },\n\n /**\n * Info log - always shown\n */\n info(namespace: string, message: string, ...args: unknown[]): void {\n console.info(formatMessage(namespace, message), ...args);\n },\n\n /**\n * Warning log - always shown\n */\n warn(namespace: string, message: string, ...args: unknown[]): void {\n console.warn(formatMessage(namespace, message), ...args);\n },\n\n /**\n * Error log - always shown\n */\n error(namespace: string, message: string, ...args: unknown[]): void {\n console.error(formatMessage(namespace, message), ...args);\n },\n};\n\n/**\n * Create a namespaced logger for a specific module\n */\nexport const createLogger = (namespace: string) => ({\n debug: (message: string, ...args: unknown[]) => logger.debug(namespace, message, ...args),\n info: (message: string, ...args: unknown[]) => logger.info(namespace, message, ...args),\n warn: (message: string, ...args: unknown[]) => logger.warn(namespace, message, ...args),\n error: (message: string, ...args: unknown[]) => logger.error(namespace, message, ...args),\n});\n","import type { CookieStorage } from 'aws-amplify/adapter-core';\nimport type { AstroServer } from './types';\nimport { createLogger } from '../utils/logger';\n\nconst log = createLogger('cookie-storage-adapter');\n\n// Ensures the cookie names are encoded in order to look up the cookie store\n// that is manipulated by js-cookie on the client side.\n// Details of the js-cookie encoding behavior see:\n// https://github.com/js-cookie/js-cookie#encoding\n// The implementation is borrowed from js-cookie without escaping `[()]` as\n// we are not using those chars in the auth keys.\nfunction ensureEncodedForJSCookie(name: string): string {\n return encodeURIComponent(name).replace(/%(2[346B]|5E|60|7C)/g, decodeURIComponent);\n}\n\nfunction parseCookieHeader(cookieHeader: string | null) {\n const out: Record<string, string> = {};\n if (!cookieHeader) return out;\n for (const part of cookieHeader.split(';')) {\n const [rawName, ...rest] = part.trim().split('=');\n const name = decodeURIComponent(rawName || '');\n const value = decodeURIComponent(rest.join('=') || '');\n if (name) out[name] = value;\n }\n return out;\n}\n\nfunction mapSameSite(\n s: CookieStorage.SetCookieOptions['sameSite']\n): 'lax' | 'strict' | 'none' | undefined {\n if (s === true) return 'strict';\n if (s === false) return 'lax';\n if (s === 'lax' || s === 'strict' || s === 'none') return s;\n return undefined;\n}\n\nexport async function createCookieStorageAdapterFromAstroContext(\n astroServerContext: AstroServer.ServerContext\n): Promise<CookieStorage.Adapter> {\n if (astroServerContext === null) {\n log.debug('Context is null, returning no-op adapter');\n return {\n get: () => undefined,\n getAll: () => [],\n set: () => {},\n delete: () => {},\n };\n }\n\n const { cookies, request } = astroServerContext;\n const cookieHeader = request?.headers?.get('cookie');\n const headerCookies = parseCookieHeader(cookieHeader ?? null);\n\n // Debug: Log available cookies (names only for security)\n const cookieNames = Object.keys(headerCookies);\n const cognitoCookies = cookieNames.filter(name => name.includes('CognitoIdentityServiceProvider'));\n log.debug('Available cookies:', cookieNames.length);\n log.debug('Cognito cookies found:', cognitoCookies.length);\n if (cognitoCookies.length > 0) {\n log.debug('Cognito cookie names:', cognitoCookies);\n }\n\n const adapter: CookieStorage.Adapter = {\n get(name) {\n const encodedName = ensureEncodedForJSCookie(name);\n const v = cookies?.get(encodedName)?.value ?? headerCookies[encodedName] ?? headerCookies[name];\n // Debug: Log token lookups (only for auth-related cookies)\n if (name.includes('accessToken') || name.includes('idToken') || name.includes('refreshToken') || name.includes('LastAuthUser')) {\n log.debug(`get('${name}'): ${v ? 'FOUND' : 'NOT FOUND'}`);\n }\n return v ? { name, value: v } : undefined;\n },\n getAll() {\n const fromAPI =\n (typeof (cookies as any)?.getAll === 'function'\n ? (cookies as any).getAll().map((c: any) => ({ name: c.name, value: c.value }))\n : Object.entries(headerCookies).map(([name, value]) => ({ name, value })));\n return fromAPI;\n },\n set(name, value, options) {\n try {\n (cookies as any).set(name, value, {\n ...(options ?? {}),\n sameSite: mapSameSite(options?.sameSite),\n });\n } catch {}\n },\n delete(name: string) {\n try {\n (cookies as any).delete(name);\n } catch {}\n },\n };\n\n return adapter;\n}\n","/**\n * @htlkg/core/utils\n * Core utility functions for Hotelinking applications\n */\n\n// Logger utility\nexport { logger, createLogger } from './logger';\n\n/**\n * Format a date to a localized string\n * @param date - Date to format\n * @param locale - Locale string (default: 'en-US')\n * @param options - Intl.DateTimeFormatOptions\n * @returns Formatted date string\n */\nexport function formatDate(\n\tdate: Date | string | number,\n\tlocale = \"en-US\",\n\toptions: Intl.DateTimeFormatOptions = {\n\t\tyear: \"numeric\",\n\t\tmonth: \"short\",\n\t\tday: \"numeric\",\n\t},\n): string {\n\tconst dateObj = typeof date === \"string\" || typeof date === \"number\" ? new Date(date) : date;\n\treturn new Intl.DateTimeFormat(locale, options).format(dateObj);\n}\n\n/**\n * Truncate text to a maximum length with ellipsis\n * @param text - Text to truncate\n * @param maxLength - Maximum length before truncation\n * @param suffix - Suffix to append (default: '...')\n * @returns Truncated text\n */\nexport function truncateText(\n\ttext: string,\n\tmaxLength: number,\n\tsuffix = \"...\",\n): string {\n\tif (text.length <= maxLength) return text;\n\treturn text.slice(0, maxLength - suffix.length) + suffix;\n}\n\n/**\n * Group an array of items by a key\n * @param items - Array of items to group\n * @param keyFn - Function to extract the grouping key\n * @returns Object with keys as group names and values as arrays of items\n */\nexport function groupBy<T>(\n\titems: T[],\n\tkeyFn: (item: T) => string | number,\n): Record<string | number, T[]> {\n\treturn items.reduce(\n\t\t(groups, item) => {\n\t\t\tconst key = keyFn(item);\n\t\t\tif (!groups[key]) {\n\t\t\t\tgroups[key] = [];\n\t\t\t}\n\t\t\tgroups[key].push(item);\n\t\t\treturn groups;\n\t\t},\n\t\t{} as Record<string | number, T[]>,\n\t);\n}\n\n/**\n * Debounce a function call\n * @param fn - Function to debounce\n * @param delay - Delay in milliseconds\n * @returns Debounced function\n */\nexport function debounce<T extends (...args: any[]) => any>(\n\tfn: T,\n\tdelay: number,\n): (...args: Parameters<T>) => void {\n\tlet timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n\treturn function debounced(...args: Parameters<T>) {\n\t\tif (timeoutId !== null) {\n\t\t\tclearTimeout(timeoutId);\n\t\t}\n\t\ttimeoutId = setTimeout(() => {\n\t\t\tfn(...args);\n\t\t\ttimeoutId = null;\n\t\t}, delay);\n\t};\n}\n\n/**\n * Throttle a function call\n * @param fn - Function to throttle\n * @param limit - Minimum time between calls in milliseconds\n * @returns Throttled function\n */\nexport function throttle<T extends (...args: any[]) => any>(\n\tfn: T,\n\tlimit: number,\n): (...args: Parameters<T>) => void {\n\tlet inThrottle = false;\n\n\treturn function throttled(...args: Parameters<T>) {\n\t\tif (!inThrottle) {\n\t\t\tfn(...args);\n\t\t\tinThrottle = true;\n\t\t\tsetTimeout(() => {\n\t\t\t\tinThrottle = false;\n\t\t\t}, limit);\n\t\t}\n\t};\n}\n","/**\n * @htlkg/core/constants\n * Core constants for Hotelinking applications\n */\n\n/**\n * Application routes\n */\nexport const ROUTES = {\n\t// Admin routes\n\tADMIN: {\n\t\tHOME: \"/admin\",\n\t\tBRANDS: \"/admin/brands\",\n\t\tACCOUNTS: \"/admin/accounts\",\n\t\tUSERS: \"/admin/users\",\n\t\tPRODUCTS: \"/admin/products\",\n\t\tSETTINGS: \"/admin/settings\",\n\t},\n\t// Brand routes\n\tBRAND: {\n\t\tHOME: (brandId: string) => `/${brandId}`,\n\t\tADMIN: (brandId: string) => `/${brandId}/admin`,\n\t\tTEMPLATES: (brandId: string) => `/${brandId}/admin/templates`,\n\t\tSETTINGS: (brandId: string) => `/${brandId}/admin/settings`,\n\t},\n\t// Auth routes\n\tAUTH: {\n\t\tLOGIN: \"/login\",\n\t\tLOGOUT: \"/logout\",\n\t\tSIGNUP: \"/signup\",\n\t\tFORGOT_PASSWORD: \"/forgot-password\",\n\t},\n} as const;\n\n/**\n * Product definitions\n */\nexport const PRODUCTS = {\n\tWIFI_PORTAL: {\n\t\tid: \"wifi-portal\",\n\t\tname: \"WiFi Portal\",\n\t\tdescription: \"Captive portal for WiFi authentication\",\n\t},\n\tWHATSAPP_CRM: {\n\t\tid: \"whatsapp-crm\",\n\t\tname: \"WhatsApp CRM\",\n\t\tdescription: \"WhatsApp integration for customer relationship management\",\n\t},\n\tANALYTICS: {\n\t\tid: \"analytics\",\n\t\tname: \"Analytics\",\n\t\tdescription: \"Analytics and reporting dashboard\",\n\t},\n} as const;\n\n/**\n * Permission definitions\n */\nexport const PERMISSIONS = {\n\t// Brand permissions\n\tBRAND_VIEW: \"brand:view\",\n\tBRAND_EDIT: \"brand:edit\",\n\tBRAND_DELETE: \"brand:delete\",\n\tBRAND_CREATE: \"brand:create\",\n\n\t// Account permissions\n\tACCOUNT_VIEW: \"account:view\",\n\tACCOUNT_EDIT: \"account:edit\",\n\tACCOUNT_DELETE: \"account:delete\",\n\tACCOUNT_CREATE: \"account:create\",\n\n\t// User permissions\n\tUSER_VIEW: \"user:view\",\n\tUSER_EDIT: \"user:edit\",\n\tUSER_DELETE: \"user:delete\",\n\tUSER_CREATE: \"user:create\",\n\n\t// Product permissions\n\tPRODUCT_VIEW: \"product:view\",\n\tPRODUCT_EDIT: \"product:edit\",\n\tPRODUCT_ENABLE: \"product:enable\",\n\tPRODUCT_DISABLE: \"product:disable\",\n\n\t// Admin permissions\n\tADMIN_ACCESS: \"admin:access\",\n\tSUPER_ADMIN_ACCESS: \"super_admin:access\",\n} as const;\n\n/**\n * User role definitions\n */\nexport const USER_ROLES = {\n\tSUPER_ADMIN: \"SUPER_ADMINS\",\n\tADMIN: \"ADMINS\",\n\tBRAND_ADMIN: \"BRAND_ADMINS\",\n\tBRAND_USER: \"BRAND_USERS\",\n\tUSER: \"USERS\",\n} as const;\n","/**\n * @htlkg/core/errors\n * Core error classes for Hotelinking applications\n */\n\n/**\n * Base application error class\n */\nexport class AppError extends Error {\n\tpublic readonly code: string;\n\tpublic readonly statusCode: number;\n\tpublic readonly details?: Record<string, any>;\n\n\tconstructor(\n\t\tmessage: string,\n\t\tcode = \"APP_ERROR\",\n\t\tstatusCode = 500,\n\t\tdetails?: Record<string, any>,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"AppError\";\n\t\tthis.code = code;\n\t\tthis.statusCode = statusCode;\n\t\tthis.details = details;\n\n\t\t// Maintains proper stack trace for where our error was thrown (only available on V8)\n\t\tif (Error.captureStackTrace) {\n\t\t\tError.captureStackTrace(this, AppError);\n\t\t}\n\t}\n\n\t/**\n\t * Convert error to JSON representation\n\t */\n\ttoJSON(): Record<string, any> {\n\t\treturn {\n\t\t\tname: this.name,\n\t\t\tmessage: this.message,\n\t\t\tcode: this.code,\n\t\t\tstatusCode: this.statusCode,\n\t\t\tdetails: this.details,\n\t\t};\n\t}\n}\n\n/**\n * Authentication error class\n */\nexport class AuthError extends AppError {\n\tconstructor(\n\t\tmessage: string,\n\t\tcode = \"AUTH_ERROR\",\n\t\tstatusCode = 401,\n\t\tdetails?: Record<string, any>,\n\t) {\n\t\tsuper(message, code, statusCode, details);\n\t\tthis.name = \"AuthError\";\n\n\t\tif (Error.captureStackTrace) {\n\t\t\tError.captureStackTrace(this, AuthError);\n\t\t}\n\t}\n}\n\n/**\n * Validation error class\n */\nexport class ValidationError extends AppError {\n\tpublic readonly errors: Array<{ field: string; message: string }>;\n\n\tconstructor(\n\t\tmessage: string,\n\t\terrors: Array<{ field: string; message: string }> = [],\n\t\tcode = \"VALIDATION_ERROR\",\n\t\tstatusCode = 400,\n\t) {\n\t\tsuper(message, code, statusCode, { errors });\n\t\tthis.name = \"ValidationError\";\n\t\tthis.errors = errors;\n\n\t\tif (Error.captureStackTrace) {\n\t\t\tError.captureStackTrace(this, ValidationError);\n\t\t}\n\t}\n\n\t/**\n\t * Convert error to JSON representation\n\t */\n\ttoJSON(): Record<string, any> {\n\t\treturn {\n\t\t\t...super.toJSON(),\n\t\t\terrors: this.errors,\n\t\t};\n\t}\n}\n\n/**\n * Not found error class\n */\nexport class NotFoundError extends AppError {\n\tpublic readonly resource: string;\n\tpublic readonly resourceId?: string;\n\n\tconstructor(\n\t\tresource: string,\n\t\tresourceId?: string,\n\t\tmessage?: string,\n\t\tcode = \"NOT_FOUND\",\n\t\tstatusCode = 404,\n\t) {\n\t\tconst defaultMessage = resourceId\n\t\t\t? `${resource} with id '${resourceId}' not found`\n\t\t\t: `${resource} not found`;\n\t\tsuper(message || defaultMessage, code, statusCode, { resource, resourceId });\n\t\tthis.name = \"NotFoundError\";\n\t\tthis.resource = resource;\n\t\tthis.resourceId = resourceId;\n\n\t\tif (Error.captureStackTrace) {\n\t\t\tError.captureStackTrace(this, NotFoundError);\n\t\t}\n\t}\n\n\t/**\n\t * Convert error to JSON representation\n\t */\n\ttoJSON(): Record<string, any> {\n\t\treturn {\n\t\t\t...super.toJSON(),\n\t\t\tresource: this.resource,\n\t\t\tresourceId: this.resourceId,\n\t\t};\n\t}\n}\n","/**\n * Type-Safe Routes\n *\n * Centralized route definitions with type safety and parameter interpolation.\n * Eliminates string literals scattered across the codebase.\n */\n\n/**\n * Route parameters type\n */\nexport type RouteParams = Record<string, string | number>;\n\n/**\n * Route function interface - can be called with params or used as path\n */\nexport interface Route<TParams extends RouteParams = RouteParams> {\n\t/** Base path of the route */\n\treadonly path: string;\n\t/** Generate URL with interpolated parameters */\n\t(params?: TParams): string;\n}\n\n/**\n * Create a route with optional parameters\n *\n * @example\n * ```typescript\n * const userRoute = createRoute<{ id: string }>('/users/:id');\n * userRoute({ id: '123' }); // \"/users/123\"\n * userRoute.path; // \"/users/:id\"\n * ```\n */\nexport function createRoute<TParams extends RouteParams = Record<string, never>>(path: string): Route<TParams> {\n\tconst routeFn = (params?: TParams): string => {\n\t\tif (!params) return path;\n\n\t\tlet result = path;\n\t\tfor (const [key, value] of Object.entries(params)) {\n\t\t\tresult = result.replace(`:${key}`, String(value));\n\t\t}\n\t\treturn result;\n\t};\n\n\t// Add path as a property\n\tObject.defineProperty(routeFn, \"path\", {\n\t\tvalue: path,\n\t\twritable: false,\n\t\tenumerable: true,\n\t});\n\n\treturn routeFn as Route<TParams>;\n}\n\n/**\n * Admin routes\n */\nexport const adminRoutes = {\n\t/** Dashboard */\n\tdashboard: createRoute(\"/admin\"),\n\n\t/** Accounts list */\n\taccounts: createRoute(\"/admin/accounts\"),\n\n\t/** Account detail */\n\taccount: createRoute<{ id: string | number }>(\"/admin/accounts/:id\"),\n\n\t/** Account brands */\n\taccountBrands: createRoute<{ id: string | number }>(\"/admin/accounts/:id/brands\"),\n\n\t/** Brands list */\n\tbrands: createRoute(\"/admin/brands\"),\n\n\t/** Brand detail */\n\tbrand: createRoute<{ id: string | number }>(\"/admin/brands/:id\"),\n\n\t/** Brand settings */\n\tbrandSettings: createRoute<{ id: string | number }>(\"/admin/brands/:id/settings\"),\n\n\t/** Users list */\n\tusers: createRoute(\"/admin/users\"),\n\n\t/** User detail */\n\tuser: createRoute<{ id: string }>(\"/admin/users/:id\"),\n\n\t/** Current user profile */\n\tprofile: createRoute(\"/admin/user\"),\n\n\t/** Analytics */\n\tanalytics: createRoute(\"/admin/analytics\"),\n} as const;\n\n/**\n * Portal routes (customer-facing)\n */\nexport const portalRoutes = {\n\t/** Portal home */\n\thome: createRoute(\"/\"),\n\n\t/** Brand portal */\n\tbrand: createRoute<{ brandId: string | number }>(\"/brands/:brandId\"),\n\n\t/** Brand settings */\n\tbrandSettings: createRoute<{ brandId: string | number }>(\"/brands/:brandId/settings\"),\n\n\t/** Brand dashboard */\n\tbrandDashboard: createRoute<{ brandId: string | number }>(\"/brands/:brandId/dashboard\"),\n\n\t/** Brand analytics */\n\tbrandAnalytics: createRoute<{ brandId: string | number }>(\"/brands/:brandId/analytics\"),\n} as const;\n\n/**\n * Auth routes\n */\nexport const authRoutes = {\n\t/** Login page */\n\tlogin: createRoute(\"/login\"),\n\n\t/** Logout endpoint */\n\tlogout: createRoute(\"/api/auth/logout\"),\n\n\t/** Confirm sign in */\n\tconfirmSignIn: createRoute(\"/api/auth/confirm-signin\"),\n} as const;\n\n/**\n * API routes\n */\nexport const apiRoutes = {\n\t/** Auth endpoints */\n\tauth: {\n\t\tlogout: createRoute(\"/api/auth/logout\"),\n\t\tconfirmSignIn: createRoute(\"/api/auth/confirm-signin\"),\n\t},\n} as const;\n\n/**\n * All routes combined\n */\nexport const routes = {\n\tadmin: adminRoutes,\n\tportal: portalRoutes,\n\tauth: authRoutes,\n\tapi: apiRoutes,\n} as const;\n\n/**\n * Type for all routes\n */\nexport type Routes = typeof routes;\n\n/**\n * Helper to check if a path matches a route pattern\n */\nexport function matchesRoute(path: string, route: Route): boolean {\n\tconst pattern = route.path\n\t\t.replace(/:[^/]+/g, \"[^/]+\") // Replace :param with regex\n\t\t.replace(/\\//g, \"\\\\/\"); // Escape slashes\n\n\tconst regex = new RegExp(`^${pattern}$`);\n\treturn regex.test(path);\n}\n\n/**\n * Extract parameters from a path given a route pattern\n */\nexport function extractRouteParams<TParams extends RouteParams>(\n\tpath: string,\n\troute: Route<TParams>\n): TParams | null {\n\tconst paramNames: string[] = [];\n\tconst pattern = route.path.replace(/:([^/]+)/g, (_, name) => {\n\t\tparamNames.push(name);\n\t\treturn \"([^/]+)\";\n\t});\n\n\tconst regex = new RegExp(`^${pattern}$`);\n\tconst match = path.match(regex);\n\n\tif (!match) return null;\n\n\tconst params: RouteParams = {};\n\tparamNames.forEach((name, index) => {\n\t\tparams[name] = match[index + 1];\n\t});\n\n\treturn params as TParams;\n}\n\n/**\n * Navigate to a route (client-side helper)\n *\n * @example\n * ```typescript\n * navigateTo(routes.admin.account({ id: '123' }));\n * ```\n */\nexport function navigateTo(url: string): void {\n\tif (typeof window !== \"undefined\") {\n\t\twindow.location.href = url;\n\t}\n}\n\n/**\n * Build a URL with query parameters\n *\n * @example\n * ```typescript\n * const url = buildUrl(routes.admin.users(), { page: 2, status: 'active' });\n * // \"/admin/users?page=2&status=active\"\n * ```\n */\nexport function buildUrl(path: string, params?: Record<string, string | number | boolean | null | undefined>): string {\n\tif (!params) return path;\n\n\tconst searchParams = new URLSearchParams();\n\tfor (const [key, value] of Object.entries(params)) {\n\t\tif (value !== null && value !== undefined && value !== \"\") {\n\t\t\tsearchParams.set(key, String(value));\n\t\t}\n\t}\n\n\tconst queryString = searchParams.toString();\n\treturn queryString ? `${path}?${queryString}` : path;\n}\n"],"mappings":"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","names":["log","globalSettings"]}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Simple debug logger utility
3
+ *
4
+ * Debug logs are only shown when DEBUG=true or HTLKG_DEBUG=true is set in environment.
5
+ * Info logs are always shown.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { logger } from '@htlkg/core/utils/logger';
10
+ *
11
+ * logger.debug('server-client', 'Detailed debug info', { data });
12
+ * logger.info('server-client', 'Important info message');
13
+ * logger.warn('server-client', 'Warning message');
14
+ * logger.error('server-client', 'Error occurred', error);
15
+ * ```
16
+ */
17
+ declare const logger: {
18
+ /**
19
+ * Debug log - only shown when DEBUG=true
20
+ */
21
+ debug(namespace: string, message: string, ...args: unknown[]): void;
22
+ /**
23
+ * Info log - always shown
24
+ */
25
+ info(namespace: string, message: string, ...args: unknown[]): void;
26
+ /**
27
+ * Warning log - always shown
28
+ */
29
+ warn(namespace: string, message: string, ...args: unknown[]): void;
30
+ /**
31
+ * Error log - always shown
32
+ */
33
+ error(namespace: string, message: string, ...args: unknown[]): void;
34
+ };
35
+ /**
36
+ * Create a namespaced logger for a specific module
37
+ */
38
+ declare const createLogger: (namespace: string) => {
39
+ debug: (message: string, ...args: unknown[]) => void;
40
+ info: (message: string, ...args: unknown[]) => void;
41
+ warn: (message: string, ...args: unknown[]) => void;
42
+ error: (message: string, ...args: unknown[]) => void;
43
+ };
44
+
45
+ export { createLogger as c, logger as l };