@htlkg/core 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,72 @@
1
+ import { APIContext } from 'astro';
2
+ export { APIContext } from 'astro';
3
+
4
+ /**
5
+ * @htlkg/core/auth
6
+ * Core authentication functions and utilities
7
+ */
8
+
9
+ /**
10
+ * User interface representing an authenticated user
11
+ */
12
+ interface User {
13
+ username: string;
14
+ email: string;
15
+ brandIds: number[];
16
+ accountIds: number[];
17
+ isAdmin: boolean;
18
+ isSuperAdmin: boolean;
19
+ roles: string[];
20
+ }
21
+ /**
22
+ * Get current authenticated user (server-side)
23
+ * This is a simplified version that will be enhanced with Amplify server context
24
+ */
25
+ declare function getUser(context: APIContext): Promise<User | null>;
26
+ /**
27
+ * Get current authenticated user (client-side)
28
+ */
29
+ declare function getClientUser(): Promise<User | null>;
30
+ /**
31
+ * Check if user has access to a specific brand
32
+ */
33
+ declare function hasAccessToBrand(user: User | null, brandId: number): boolean;
34
+ /**
35
+ * Check if user has access to a specific account
36
+ */
37
+ declare function hasAccessToAccount(user: User | null, accountId: number): boolean;
38
+ /**
39
+ * Check if user is an admin
40
+ */
41
+ declare function isAdminUser(user: User | null): boolean;
42
+ /**
43
+ * Check if user is a super admin
44
+ */
45
+ declare function isSuperAdminUser(user: User | null): boolean;
46
+ /**
47
+ * Placeholder for requireAuth - will be implemented in @htlkg/integrations
48
+ * This is here for type exports and documentation
49
+ */
50
+ declare function requireAuth(context: APIContext, loginUrl?: string): Promise<{
51
+ success: boolean;
52
+ user?: User;
53
+ redirectTo?: string;
54
+ }>;
55
+ /**
56
+ * Placeholder for requireAdminAccess - will be implemented in @htlkg/integrations
57
+ */
58
+ declare function requireAdminAccess(context: APIContext, loginUrl?: string): Promise<{
59
+ success: boolean;
60
+ user?: User;
61
+ redirectTo?: string;
62
+ }>;
63
+ /**
64
+ * Placeholder for requireBrandAccess - will be implemented in @htlkg/integrations
65
+ */
66
+ declare function requireBrandAccess(context: APIContext, brandId: number, loginUrl?: string): Promise<{
67
+ success: boolean;
68
+ user?: User;
69
+ redirectTo?: string;
70
+ }>;
71
+
72
+ export { type User, getClientUser, getUser, hasAccessToAccount, hasAccessToBrand, isAdminUser, isSuperAdminUser, requireAdminAccess, requireAuth, requireBrandAccess };
@@ -0,0 +1,87 @@
1
+ // src/auth/index.ts
2
+ import { fetchAuthSession } from "aws-amplify/auth";
3
+ function parseIds(ids) {
4
+ if (!ids) return [];
5
+ return ids.split(",").map((id) => Number.parseInt(id.trim(), 10)).filter((id) => !Number.isNaN(id));
6
+ }
7
+ async function getUser(context) {
8
+ return null;
9
+ }
10
+ async function getClientUser() {
11
+ try {
12
+ const session = await fetchAuthSession();
13
+ if (!session.tokens?.accessToken) {
14
+ return null;
15
+ }
16
+ const accessPayload = session.tokens.accessToken.payload;
17
+ const idPayload = session.tokens.idToken?.payload;
18
+ const email = idPayload?.email || accessPayload.email || "";
19
+ const brandIds = parseIds(accessPayload["custom:brand_ids"]);
20
+ const accountIds = parseIds(accessPayload["custom:account_ids"]);
21
+ const groups = accessPayload["cognito:groups"] || [];
22
+ const isAdmin = groups.includes("admin") || groups.includes("ADMINS") || groups.includes("SUPER_ADMINS");
23
+ const isSuperAdmin = groups.includes("SUPER_ADMINS");
24
+ return {
25
+ username: accessPayload.username || "",
26
+ email,
27
+ brandIds,
28
+ accountIds,
29
+ isAdmin,
30
+ isSuperAdmin,
31
+ roles: groups
32
+ };
33
+ } catch (error) {
34
+ if (error instanceof Error) {
35
+ console.error(
36
+ "[Auth] Client auth error:",
37
+ error.name,
38
+ "-",
39
+ error.message.replace(/token[=:]\s*[^\s,}]+/gi, "token=***")
40
+ );
41
+ }
42
+ return null;
43
+ }
44
+ }
45
+ function hasAccessToBrand(user, brandId) {
46
+ if (!user) return false;
47
+ if (user.isAdmin) return true;
48
+ return user.brandIds.includes(brandId);
49
+ }
50
+ function hasAccessToAccount(user, accountId) {
51
+ if (!user) return false;
52
+ if (user.isAdmin) return true;
53
+ return user.accountIds.includes(accountId);
54
+ }
55
+ function isAdminUser(user) {
56
+ return user?.isAdmin || false;
57
+ }
58
+ function isSuperAdminUser(user) {
59
+ return user?.isSuperAdmin || false;
60
+ }
61
+ async function requireAuth(context, loginUrl) {
62
+ throw new Error(
63
+ "requireAuth should be imported from @htlkg/astro/middleware"
64
+ );
65
+ }
66
+ async function requireAdminAccess(context, loginUrl) {
67
+ throw new Error(
68
+ "requireAdminAccess should be imported from @htlkg/astro/middleware"
69
+ );
70
+ }
71
+ async function requireBrandAccess(context, brandId, loginUrl) {
72
+ throw new Error(
73
+ "requireBrandAccess should be imported from @htlkg/astro/middleware"
74
+ );
75
+ }
76
+ export {
77
+ getClientUser,
78
+ getUser,
79
+ hasAccessToAccount,
80
+ hasAccessToBrand,
81
+ isAdminUser,
82
+ isSuperAdminUser,
83
+ requireAdminAccess,
84
+ requireAuth,
85
+ requireBrandAccess
86
+ };
87
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/auth/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"],"mappings":";AAOA,SAAS,wBAAwB;AAyBjC,SAAS,SAAS,KAAmC;AACpD,MAAI,CAAC,IAAK,QAAO,CAAC;AAClB,SAAO,IACL,MAAM,GAAG,EACT,IAAI,CAAC,OAAO,OAAO,SAAS,GAAG,KAAK,GAAG,EAAE,CAAC,EAC1C,OAAO,CAAC,OAAO,CAAC,OAAO,MAAM,EAAE,CAAC;AACnC;AAMA,eAAsB,QAAQ,SAA2C;AAGxE,SAAO;AACR;AAKA,eAAsB,gBAAsC;AAC3D,MAAI;AACH,UAAM,UAAU,MAAM,iBAAiB;AAEvC,QAAI,CAAC,QAAQ,QAAQ,aAAa;AACjC,aAAO;AAAA,IACR;AAEA,UAAM,gBAAgB,QAAQ,OAAO,YAAY;AACjD,UAAM,YAAY,QAAQ,OAAO,SAAS;AAE1C,UAAM,QACJ,WAAW,SAAqB,cAAc,SAAoB;AACpE,UAAM,WAAW,SAAS,cAAc,kBAAkB,CAAW;AACrE,UAAM,aAAa,SAAS,cAAc,oBAAoB,CAAW;AAEzE,UAAM,SAAU,cAAc,gBAAgB,KAAkB,CAAC;AACjE,UAAM,UACL,OAAO,SAAS,OAAO,KACvB,OAAO,SAAS,QAAQ,KACxB,OAAO,SAAS,cAAc;AAC/B,UAAM,eAAe,OAAO,SAAS,cAAc;AAEnD,WAAO;AAAA,MACN,UAAW,cAAc,YAAuB;AAAA,MAChD;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAO;AAAA,IACR;AAAA,EACD,SAAS,OAAO;AACf,QAAI,iBAAiB,OAAO;AAC3B,cAAQ;AAAA,QACP;AAAA,QACA,MAAM;AAAA,QACN;AAAA,QACA,MAAM,QAAQ,QAAQ,0BAA0B,WAAW;AAAA,MAC5D;AAAA,IACD;AACA,WAAO;AAAA,EACR;AACD;AAKO,SAAS,iBACf,MACA,SACU;AACV,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI,KAAK,QAAS,QAAO;AACzB,SAAO,KAAK,SAAS,SAAS,OAAO;AACtC;AAKO,SAAS,mBACf,MACA,WACU;AACV,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI,KAAK,QAAS,QAAO;AACzB,SAAO,KAAK,WAAW,SAAS,SAAS;AAC1C;AAKO,SAAS,YAAY,MAA4B;AACvD,SAAO,MAAM,WAAW;AACzB;AAKO,SAAS,iBAAiB,MAA4B;AAC5D,SAAO,MAAM,gBAAgB;AAC9B;AAMA,eAAsB,YACrB,SACA,UACkE;AAClE,QAAM,IAAI;AAAA,IACT;AAAA,EACD;AACD;AAKA,eAAsB,mBACrB,SACA,UACkE;AAClE,QAAM,IAAI;AAAA,IACT;AAAA,EACD;AACD;AAKA,eAAsB,mBACrB,SACA,SACA,UACkE;AAClE,QAAM,IAAI;AAAA,IACT;AAAA,EACD;AACD;","names":[]}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * @htlkg/core/constants
3
+ * Core constants for Hotelinking applications
4
+ */
5
+ /**
6
+ * Application routes
7
+ */
8
+ declare const ROUTES: {
9
+ readonly ADMIN: {
10
+ readonly HOME: "/admin";
11
+ readonly BRANDS: "/admin/brands";
12
+ readonly ACCOUNTS: "/admin/accounts";
13
+ readonly USERS: "/admin/users";
14
+ readonly PRODUCTS: "/admin/products";
15
+ readonly SETTINGS: "/admin/settings";
16
+ };
17
+ readonly BRAND: {
18
+ readonly HOME: (brandId: string) => string;
19
+ readonly ADMIN: (brandId: string) => string;
20
+ readonly TEMPLATES: (brandId: string) => string;
21
+ readonly SETTINGS: (brandId: string) => string;
22
+ };
23
+ readonly AUTH: {
24
+ readonly LOGIN: "/login";
25
+ readonly LOGOUT: "/logout";
26
+ readonly SIGNUP: "/signup";
27
+ readonly FORGOT_PASSWORD: "/forgot-password";
28
+ };
29
+ };
30
+ /**
31
+ * Product definitions
32
+ */
33
+ declare const PRODUCTS: {
34
+ readonly WIFI_PORTAL: {
35
+ readonly id: "wifi-portal";
36
+ readonly name: "WiFi Portal";
37
+ readonly description: "Captive portal for WiFi authentication";
38
+ };
39
+ readonly WHATSAPP_CRM: {
40
+ readonly id: "whatsapp-crm";
41
+ readonly name: "WhatsApp CRM";
42
+ readonly description: "WhatsApp integration for customer relationship management";
43
+ };
44
+ readonly ANALYTICS: {
45
+ readonly id: "analytics";
46
+ readonly name: "Analytics";
47
+ readonly description: "Analytics and reporting dashboard";
48
+ };
49
+ };
50
+ /**
51
+ * Permission definitions
52
+ */
53
+ declare const PERMISSIONS: {
54
+ readonly BRAND_VIEW: "brand:view";
55
+ readonly BRAND_EDIT: "brand:edit";
56
+ readonly BRAND_DELETE: "brand:delete";
57
+ readonly BRAND_CREATE: "brand:create";
58
+ readonly ACCOUNT_VIEW: "account:view";
59
+ readonly ACCOUNT_EDIT: "account:edit";
60
+ readonly ACCOUNT_DELETE: "account:delete";
61
+ readonly ACCOUNT_CREATE: "account:create";
62
+ readonly USER_VIEW: "user:view";
63
+ readonly USER_EDIT: "user:edit";
64
+ readonly USER_DELETE: "user:delete";
65
+ readonly USER_CREATE: "user:create";
66
+ readonly PRODUCT_VIEW: "product:view";
67
+ readonly PRODUCT_EDIT: "product:edit";
68
+ readonly PRODUCT_ENABLE: "product:enable";
69
+ readonly PRODUCT_DISABLE: "product:disable";
70
+ readonly ADMIN_ACCESS: "admin:access";
71
+ readonly SUPER_ADMIN_ACCESS: "super_admin:access";
72
+ };
73
+ /**
74
+ * User role definitions
75
+ */
76
+ declare const USER_ROLES: {
77
+ readonly SUPER_ADMIN: "SUPER_ADMINS";
78
+ readonly ADMIN: "ADMINS";
79
+ readonly BRAND_ADMIN: "BRAND_ADMINS";
80
+ readonly BRAND_USER: "BRAND_USERS";
81
+ readonly USER: "USERS";
82
+ };
83
+
84
+ export { PERMISSIONS, PRODUCTS, ROUTES, USER_ROLES };
@@ -0,0 +1,82 @@
1
+ // src/constants/index.ts
2
+ var ROUTES = {
3
+ // Admin routes
4
+ ADMIN: {
5
+ HOME: "/admin",
6
+ BRANDS: "/admin/brands",
7
+ ACCOUNTS: "/admin/accounts",
8
+ USERS: "/admin/users",
9
+ PRODUCTS: "/admin/products",
10
+ SETTINGS: "/admin/settings"
11
+ },
12
+ // Brand routes
13
+ BRAND: {
14
+ HOME: (brandId) => `/${brandId}`,
15
+ ADMIN: (brandId) => `/${brandId}/admin`,
16
+ TEMPLATES: (brandId) => `/${brandId}/admin/templates`,
17
+ SETTINGS: (brandId) => `/${brandId}/admin/settings`
18
+ },
19
+ // Auth routes
20
+ AUTH: {
21
+ LOGIN: "/login",
22
+ LOGOUT: "/logout",
23
+ SIGNUP: "/signup",
24
+ FORGOT_PASSWORD: "/forgot-password"
25
+ }
26
+ };
27
+ var PRODUCTS = {
28
+ WIFI_PORTAL: {
29
+ id: "wifi-portal",
30
+ name: "WiFi Portal",
31
+ description: "Captive portal for WiFi authentication"
32
+ },
33
+ WHATSAPP_CRM: {
34
+ id: "whatsapp-crm",
35
+ name: "WhatsApp CRM",
36
+ description: "WhatsApp integration for customer relationship management"
37
+ },
38
+ ANALYTICS: {
39
+ id: "analytics",
40
+ name: "Analytics",
41
+ description: "Analytics and reporting dashboard"
42
+ }
43
+ };
44
+ var PERMISSIONS = {
45
+ // Brand permissions
46
+ BRAND_VIEW: "brand:view",
47
+ BRAND_EDIT: "brand:edit",
48
+ BRAND_DELETE: "brand:delete",
49
+ BRAND_CREATE: "brand:create",
50
+ // Account permissions
51
+ ACCOUNT_VIEW: "account:view",
52
+ ACCOUNT_EDIT: "account:edit",
53
+ ACCOUNT_DELETE: "account:delete",
54
+ ACCOUNT_CREATE: "account:create",
55
+ // User permissions
56
+ USER_VIEW: "user:view",
57
+ USER_EDIT: "user:edit",
58
+ USER_DELETE: "user:delete",
59
+ USER_CREATE: "user:create",
60
+ // Product permissions
61
+ PRODUCT_VIEW: "product:view",
62
+ PRODUCT_EDIT: "product:edit",
63
+ PRODUCT_ENABLE: "product:enable",
64
+ PRODUCT_DISABLE: "product:disable",
65
+ // Admin permissions
66
+ ADMIN_ACCESS: "admin:access",
67
+ SUPER_ADMIN_ACCESS: "super_admin:access"
68
+ };
69
+ var USER_ROLES = {
70
+ SUPER_ADMIN: "SUPER_ADMINS",
71
+ ADMIN: "ADMINS",
72
+ BRAND_ADMIN: "BRAND_ADMINS",
73
+ BRAND_USER: "BRAND_USERS",
74
+ USER: "USERS"
75
+ };
76
+ export {
77
+ PERMISSIONS,
78
+ PRODUCTS,
79
+ ROUTES,
80
+ USER_ROLES
81
+ };
82
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/constants/index.ts"],"sourcesContent":["/**\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"],"mappings":";AAQO,IAAM,SAAS;AAAA;AAAA,EAErB,OAAO;AAAA,IACN,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,OAAO;AAAA,IACP,UAAU;AAAA,IACV,UAAU;AAAA,EACX;AAAA;AAAA,EAEA,OAAO;AAAA,IACN,MAAM,CAAC,YAAoB,IAAI,OAAO;AAAA,IACtC,OAAO,CAAC,YAAoB,IAAI,OAAO;AAAA,IACvC,WAAW,CAAC,YAAoB,IAAI,OAAO;AAAA,IAC3C,UAAU,CAAC,YAAoB,IAAI,OAAO;AAAA,EAC3C;AAAA;AAAA,EAEA,MAAM;AAAA,IACL,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,iBAAiB;AAAA,EAClB;AACD;AAKO,IAAM,WAAW;AAAA,EACvB,aAAa;AAAA,IACZ,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,aAAa;AAAA,EACd;AAAA,EACA,cAAc;AAAA,IACb,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,aAAa;AAAA,EACd;AAAA,EACA,WAAW;AAAA,IACV,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,aAAa;AAAA,EACd;AACD;AAKO,IAAM,cAAc;AAAA;AAAA,EAE1B,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,cAAc;AAAA;AAAA,EAGd,cAAc;AAAA,EACd,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB,gBAAgB;AAAA;AAAA,EAGhB,WAAW;AAAA,EACX,WAAW;AAAA,EACX,aAAa;AAAA,EACb,aAAa;AAAA;AAAA,EAGb,cAAc;AAAA,EACd,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB,iBAAiB;AAAA;AAAA,EAGjB,cAAc;AAAA,EACd,oBAAoB;AACrB;AAKO,IAAM,aAAa;AAAA,EACzB,aAAa;AAAA,EACb,OAAO;AAAA,EACP,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,MAAM;AACP;","names":[]}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * @htlkg/core/errors
3
+ * Core error classes for Hotelinking applications
4
+ */
5
+ /**
6
+ * Base application error class
7
+ */
8
+ declare class AppError extends Error {
9
+ readonly code: string;
10
+ readonly statusCode: number;
11
+ readonly details?: Record<string, any>;
12
+ constructor(message: string, code?: string, statusCode?: number, details?: Record<string, any>);
13
+ /**
14
+ * Convert error to JSON representation
15
+ */
16
+ toJSON(): Record<string, any>;
17
+ }
18
+ /**
19
+ * Authentication error class
20
+ */
21
+ declare class AuthError extends AppError {
22
+ constructor(message: string, code?: string, statusCode?: number, details?: Record<string, any>);
23
+ }
24
+ /**
25
+ * Validation error class
26
+ */
27
+ declare class ValidationError extends AppError {
28
+ readonly errors: Array<{
29
+ field: string;
30
+ message: string;
31
+ }>;
32
+ constructor(message: string, errors?: Array<{
33
+ field: string;
34
+ message: string;
35
+ }>, code?: string, statusCode?: number);
36
+ /**
37
+ * Convert error to JSON representation
38
+ */
39
+ toJSON(): Record<string, any>;
40
+ }
41
+ /**
42
+ * Not found error class
43
+ */
44
+ declare class NotFoundError extends AppError {
45
+ readonly resource: string;
46
+ readonly resourceId?: string;
47
+ constructor(resource: string, resourceId?: string, message?: string, code?: string, statusCode?: number);
48
+ /**
49
+ * Convert error to JSON representation
50
+ */
51
+ toJSON(): Record<string, any>;
52
+ }
53
+
54
+ export { AppError, AuthError, NotFoundError, ValidationError };
@@ -0,0 +1,88 @@
1
+ // src/errors/index.ts
2
+ var AppError = class _AppError extends Error {
3
+ code;
4
+ statusCode;
5
+ details;
6
+ constructor(message, code = "APP_ERROR", statusCode = 500, details) {
7
+ super(message);
8
+ this.name = "AppError";
9
+ this.code = code;
10
+ this.statusCode = statusCode;
11
+ this.details = details;
12
+ if (Error.captureStackTrace) {
13
+ Error.captureStackTrace(this, _AppError);
14
+ }
15
+ }
16
+ /**
17
+ * Convert error to JSON representation
18
+ */
19
+ toJSON() {
20
+ return {
21
+ name: this.name,
22
+ message: this.message,
23
+ code: this.code,
24
+ statusCode: this.statusCode,
25
+ details: this.details
26
+ };
27
+ }
28
+ };
29
+ var AuthError = class _AuthError extends AppError {
30
+ constructor(message, code = "AUTH_ERROR", statusCode = 401, details) {
31
+ super(message, code, statusCode, details);
32
+ this.name = "AuthError";
33
+ if (Error.captureStackTrace) {
34
+ Error.captureStackTrace(this, _AuthError);
35
+ }
36
+ }
37
+ };
38
+ var ValidationError = class _ValidationError extends AppError {
39
+ errors;
40
+ constructor(message, errors = [], code = "VALIDATION_ERROR", statusCode = 400) {
41
+ super(message, code, statusCode, { errors });
42
+ this.name = "ValidationError";
43
+ this.errors = errors;
44
+ if (Error.captureStackTrace) {
45
+ Error.captureStackTrace(this, _ValidationError);
46
+ }
47
+ }
48
+ /**
49
+ * Convert error to JSON representation
50
+ */
51
+ toJSON() {
52
+ return {
53
+ ...super.toJSON(),
54
+ errors: this.errors
55
+ };
56
+ }
57
+ };
58
+ var NotFoundError = class _NotFoundError extends AppError {
59
+ resource;
60
+ resourceId;
61
+ constructor(resource, resourceId, message, code = "NOT_FOUND", statusCode = 404) {
62
+ const defaultMessage = resourceId ? `${resource} with id '${resourceId}' not found` : `${resource} not found`;
63
+ super(message || defaultMessage, code, statusCode, { resource, resourceId });
64
+ this.name = "NotFoundError";
65
+ this.resource = resource;
66
+ this.resourceId = resourceId;
67
+ if (Error.captureStackTrace) {
68
+ Error.captureStackTrace(this, _NotFoundError);
69
+ }
70
+ }
71
+ /**
72
+ * Convert error to JSON representation
73
+ */
74
+ toJSON() {
75
+ return {
76
+ ...super.toJSON(),
77
+ resource: this.resource,
78
+ resourceId: this.resourceId
79
+ };
80
+ }
81
+ };
82
+ export {
83
+ AppError,
84
+ AuthError,
85
+ NotFoundError,
86
+ ValidationError
87
+ };
88
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/errors/index.ts"],"sourcesContent":["/**\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":";AAQO,IAAM,WAAN,MAAM,kBAAiB,MAAM;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EAEhB,YACC,SACA,OAAO,aACP,aAAa,KACb,SACC;AACD,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,aAAa;AAClB,SAAK,UAAU;AAGf,QAAI,MAAM,mBAAmB;AAC5B,YAAM,kBAAkB,MAAM,SAAQ;AAAA,IACvC;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,SAA8B;AAC7B,WAAO;AAAA,MACN,MAAM,KAAK;AAAA,MACX,SAAS,KAAK;AAAA,MACd,MAAM,KAAK;AAAA,MACX,YAAY,KAAK;AAAA,MACjB,SAAS,KAAK;AAAA,IACf;AAAA,EACD;AACD;AAKO,IAAM,YAAN,MAAM,mBAAkB,SAAS;AAAA,EACvC,YACC,SACA,OAAO,cACP,aAAa,KACb,SACC;AACD,UAAM,SAAS,MAAM,YAAY,OAAO;AACxC,SAAK,OAAO;AAEZ,QAAI,MAAM,mBAAmB;AAC5B,YAAM,kBAAkB,MAAM,UAAS;AAAA,IACxC;AAAA,EACD;AACD;AAKO,IAAM,kBAAN,MAAM,yBAAwB,SAAS;AAAA,EAC7B;AAAA,EAEhB,YACC,SACA,SAAoD,CAAC,GACrD,OAAO,oBACP,aAAa,KACZ;AACD,UAAM,SAAS,MAAM,YAAY,EAAE,OAAO,CAAC;AAC3C,SAAK,OAAO;AACZ,SAAK,SAAS;AAEd,QAAI,MAAM,mBAAmB;AAC5B,YAAM,kBAAkB,MAAM,gBAAe;AAAA,IAC9C;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,SAA8B;AAC7B,WAAO;AAAA,MACN,GAAG,MAAM,OAAO;AAAA,MAChB,QAAQ,KAAK;AAAA,IACd;AAAA,EACD;AACD;AAKO,IAAM,gBAAN,MAAM,uBAAsB,SAAS;AAAA,EAC3B;AAAA,EACA;AAAA,EAEhB,YACC,UACA,YACA,SACA,OAAO,aACP,aAAa,KACZ;AACD,UAAM,iBAAiB,aACpB,GAAG,QAAQ,aAAa,UAAU,gBAClC,GAAG,QAAQ;AACd,UAAM,WAAW,gBAAgB,MAAM,YAAY,EAAE,UAAU,WAAW,CAAC;AAC3E,SAAK,OAAO;AACZ,SAAK,WAAW;AAChB,SAAK,aAAa;AAElB,QAAI,MAAM,mBAAmB;AAC5B,YAAM,kBAAkB,MAAM,cAAa;AAAA,IAC5C;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,SAA8B;AAC7B,WAAO;AAAA,MACN,GAAG,MAAM,OAAO;AAAA,MAChB,UAAU,KAAK;AAAA,MACf,YAAY,KAAK;AAAA,IAClB;AAAA,EACD;AACD;","names":[]}
@@ -0,0 +1,6 @@
1
+ export { User, getClientUser, getUser, hasAccessToAccount, hasAccessToBrand, isAdminUser, isSuperAdminUser, requireAdminAccess, requireAuth, requireBrandAccess } from './auth/index.js';
2
+ export { Account, Brand, BrandUser, Product, ProductInstance } from './types/index.js';
3
+ export { debounce, formatDate, groupBy, throttle, truncateText } from './utils/index.js';
4
+ export { PERMISSIONS, PRODUCTS, ROUTES, USER_ROLES } from './constants/index.js';
5
+ export { AppError, AuthError, NotFoundError, ValidationError } from './errors/index.js';
6
+ export { APIContext } from 'astro';
package/dist/index.js ADDED
@@ -0,0 +1,309 @@
1
+ // src/auth/index.ts
2
+ import { fetchAuthSession } from "aws-amplify/auth";
3
+ function parseIds(ids) {
4
+ if (!ids) return [];
5
+ return ids.split(",").map((id) => Number.parseInt(id.trim(), 10)).filter((id) => !Number.isNaN(id));
6
+ }
7
+ async function getUser(context) {
8
+ return null;
9
+ }
10
+ async function getClientUser() {
11
+ try {
12
+ const session = await fetchAuthSession();
13
+ if (!session.tokens?.accessToken) {
14
+ return null;
15
+ }
16
+ const accessPayload = session.tokens.accessToken.payload;
17
+ const idPayload = session.tokens.idToken?.payload;
18
+ const email = idPayload?.email || accessPayload.email || "";
19
+ const brandIds = parseIds(accessPayload["custom:brand_ids"]);
20
+ const accountIds = parseIds(accessPayload["custom:account_ids"]);
21
+ const groups = accessPayload["cognito:groups"] || [];
22
+ const isAdmin = groups.includes("admin") || groups.includes("ADMINS") || groups.includes("SUPER_ADMINS");
23
+ const isSuperAdmin = groups.includes("SUPER_ADMINS");
24
+ return {
25
+ username: accessPayload.username || "",
26
+ email,
27
+ brandIds,
28
+ accountIds,
29
+ isAdmin,
30
+ isSuperAdmin,
31
+ roles: groups
32
+ };
33
+ } catch (error) {
34
+ if (error instanceof Error) {
35
+ console.error(
36
+ "[Auth] Client auth error:",
37
+ error.name,
38
+ "-",
39
+ error.message.replace(/token[=:]\s*[^\s,}]+/gi, "token=***")
40
+ );
41
+ }
42
+ return null;
43
+ }
44
+ }
45
+ function hasAccessToBrand(user, brandId) {
46
+ if (!user) return false;
47
+ if (user.isAdmin) return true;
48
+ return user.brandIds.includes(brandId);
49
+ }
50
+ function hasAccessToAccount(user, accountId) {
51
+ if (!user) return false;
52
+ if (user.isAdmin) return true;
53
+ return user.accountIds.includes(accountId);
54
+ }
55
+ function isAdminUser(user) {
56
+ return user?.isAdmin || false;
57
+ }
58
+ function isSuperAdminUser(user) {
59
+ return user?.isSuperAdmin || false;
60
+ }
61
+ async function requireAuth(context, loginUrl) {
62
+ throw new Error(
63
+ "requireAuth should be imported from @htlkg/astro/middleware"
64
+ );
65
+ }
66
+ async function requireAdminAccess(context, loginUrl) {
67
+ throw new Error(
68
+ "requireAdminAccess should be imported from @htlkg/astro/middleware"
69
+ );
70
+ }
71
+ async function requireBrandAccess(context, brandId, loginUrl) {
72
+ throw new Error(
73
+ "requireBrandAccess should be imported from @htlkg/astro/middleware"
74
+ );
75
+ }
76
+
77
+ // src/utils/index.ts
78
+ function formatDate(date, locale = "en-US", options = {
79
+ year: "numeric",
80
+ month: "short",
81
+ day: "numeric"
82
+ }) {
83
+ const dateObj = typeof date === "string" || typeof date === "number" ? new Date(date) : date;
84
+ return new Intl.DateTimeFormat(locale, options).format(dateObj);
85
+ }
86
+ function truncateText(text, maxLength, suffix = "...") {
87
+ if (text.length <= maxLength) return text;
88
+ return text.slice(0, maxLength - suffix.length) + suffix;
89
+ }
90
+ function groupBy(items, keyFn) {
91
+ return items.reduce(
92
+ (groups, item) => {
93
+ const key = keyFn(item);
94
+ if (!groups[key]) {
95
+ groups[key] = [];
96
+ }
97
+ groups[key].push(item);
98
+ return groups;
99
+ },
100
+ {}
101
+ );
102
+ }
103
+ function debounce(fn, delay) {
104
+ let timeoutId = null;
105
+ return function debounced(...args) {
106
+ if (timeoutId !== null) {
107
+ clearTimeout(timeoutId);
108
+ }
109
+ timeoutId = setTimeout(() => {
110
+ fn(...args);
111
+ timeoutId = null;
112
+ }, delay);
113
+ };
114
+ }
115
+ function throttle(fn, limit) {
116
+ let inThrottle = false;
117
+ return function throttled(...args) {
118
+ if (!inThrottle) {
119
+ fn(...args);
120
+ inThrottle = true;
121
+ setTimeout(() => {
122
+ inThrottle = false;
123
+ }, limit);
124
+ }
125
+ };
126
+ }
127
+
128
+ // src/constants/index.ts
129
+ var ROUTES = {
130
+ // Admin routes
131
+ ADMIN: {
132
+ HOME: "/admin",
133
+ BRANDS: "/admin/brands",
134
+ ACCOUNTS: "/admin/accounts",
135
+ USERS: "/admin/users",
136
+ PRODUCTS: "/admin/products",
137
+ SETTINGS: "/admin/settings"
138
+ },
139
+ // Brand routes
140
+ BRAND: {
141
+ HOME: (brandId) => `/${brandId}`,
142
+ ADMIN: (brandId) => `/${brandId}/admin`,
143
+ TEMPLATES: (brandId) => `/${brandId}/admin/templates`,
144
+ SETTINGS: (brandId) => `/${brandId}/admin/settings`
145
+ },
146
+ // Auth routes
147
+ AUTH: {
148
+ LOGIN: "/login",
149
+ LOGOUT: "/logout",
150
+ SIGNUP: "/signup",
151
+ FORGOT_PASSWORD: "/forgot-password"
152
+ }
153
+ };
154
+ var PRODUCTS = {
155
+ WIFI_PORTAL: {
156
+ id: "wifi-portal",
157
+ name: "WiFi Portal",
158
+ description: "Captive portal for WiFi authentication"
159
+ },
160
+ WHATSAPP_CRM: {
161
+ id: "whatsapp-crm",
162
+ name: "WhatsApp CRM",
163
+ description: "WhatsApp integration for customer relationship management"
164
+ },
165
+ ANALYTICS: {
166
+ id: "analytics",
167
+ name: "Analytics",
168
+ description: "Analytics and reporting dashboard"
169
+ }
170
+ };
171
+ var PERMISSIONS = {
172
+ // Brand permissions
173
+ BRAND_VIEW: "brand:view",
174
+ BRAND_EDIT: "brand:edit",
175
+ BRAND_DELETE: "brand:delete",
176
+ BRAND_CREATE: "brand:create",
177
+ // Account permissions
178
+ ACCOUNT_VIEW: "account:view",
179
+ ACCOUNT_EDIT: "account:edit",
180
+ ACCOUNT_DELETE: "account:delete",
181
+ ACCOUNT_CREATE: "account:create",
182
+ // User permissions
183
+ USER_VIEW: "user:view",
184
+ USER_EDIT: "user:edit",
185
+ USER_DELETE: "user:delete",
186
+ USER_CREATE: "user:create",
187
+ // Product permissions
188
+ PRODUCT_VIEW: "product:view",
189
+ PRODUCT_EDIT: "product:edit",
190
+ PRODUCT_ENABLE: "product:enable",
191
+ PRODUCT_DISABLE: "product:disable",
192
+ // Admin permissions
193
+ ADMIN_ACCESS: "admin:access",
194
+ SUPER_ADMIN_ACCESS: "super_admin:access"
195
+ };
196
+ var USER_ROLES = {
197
+ SUPER_ADMIN: "SUPER_ADMINS",
198
+ ADMIN: "ADMINS",
199
+ BRAND_ADMIN: "BRAND_ADMINS",
200
+ BRAND_USER: "BRAND_USERS",
201
+ USER: "USERS"
202
+ };
203
+
204
+ // src/errors/index.ts
205
+ var AppError = class _AppError extends Error {
206
+ code;
207
+ statusCode;
208
+ details;
209
+ constructor(message, code = "APP_ERROR", statusCode = 500, details) {
210
+ super(message);
211
+ this.name = "AppError";
212
+ this.code = code;
213
+ this.statusCode = statusCode;
214
+ this.details = details;
215
+ if (Error.captureStackTrace) {
216
+ Error.captureStackTrace(this, _AppError);
217
+ }
218
+ }
219
+ /**
220
+ * Convert error to JSON representation
221
+ */
222
+ toJSON() {
223
+ return {
224
+ name: this.name,
225
+ message: this.message,
226
+ code: this.code,
227
+ statusCode: this.statusCode,
228
+ details: this.details
229
+ };
230
+ }
231
+ };
232
+ var AuthError = class _AuthError extends AppError {
233
+ constructor(message, code = "AUTH_ERROR", statusCode = 401, details) {
234
+ super(message, code, statusCode, details);
235
+ this.name = "AuthError";
236
+ if (Error.captureStackTrace) {
237
+ Error.captureStackTrace(this, _AuthError);
238
+ }
239
+ }
240
+ };
241
+ var ValidationError = class _ValidationError extends AppError {
242
+ errors;
243
+ constructor(message, errors = [], code = "VALIDATION_ERROR", statusCode = 400) {
244
+ super(message, code, statusCode, { errors });
245
+ this.name = "ValidationError";
246
+ this.errors = errors;
247
+ if (Error.captureStackTrace) {
248
+ Error.captureStackTrace(this, _ValidationError);
249
+ }
250
+ }
251
+ /**
252
+ * Convert error to JSON representation
253
+ */
254
+ toJSON() {
255
+ return {
256
+ ...super.toJSON(),
257
+ errors: this.errors
258
+ };
259
+ }
260
+ };
261
+ var NotFoundError = class _NotFoundError extends AppError {
262
+ resource;
263
+ resourceId;
264
+ constructor(resource, resourceId, message, code = "NOT_FOUND", statusCode = 404) {
265
+ const defaultMessage = resourceId ? `${resource} with id '${resourceId}' not found` : `${resource} not found`;
266
+ super(message || defaultMessage, code, statusCode, { resource, resourceId });
267
+ this.name = "NotFoundError";
268
+ this.resource = resource;
269
+ this.resourceId = resourceId;
270
+ if (Error.captureStackTrace) {
271
+ Error.captureStackTrace(this, _NotFoundError);
272
+ }
273
+ }
274
+ /**
275
+ * Convert error to JSON representation
276
+ */
277
+ toJSON() {
278
+ return {
279
+ ...super.toJSON(),
280
+ resource: this.resource,
281
+ resourceId: this.resourceId
282
+ };
283
+ }
284
+ };
285
+ export {
286
+ AppError,
287
+ AuthError,
288
+ NotFoundError,
289
+ PERMISSIONS,
290
+ PRODUCTS,
291
+ ROUTES,
292
+ USER_ROLES,
293
+ ValidationError,
294
+ debounce,
295
+ formatDate,
296
+ getClientUser,
297
+ getUser,
298
+ groupBy,
299
+ hasAccessToAccount,
300
+ hasAccessToBrand,
301
+ isAdminUser,
302
+ isSuperAdminUser,
303
+ requireAdminAccess,
304
+ requireAuth,
305
+ requireBrandAccess,
306
+ throttle,
307
+ truncateText
308
+ };
309
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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":[]}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * @htlkg/core/types
3
+ * Core TypeScript types and interfaces for Hotelinking applications
4
+ */
5
+ /**
6
+ * AuthUser interface representing an authenticated user
7
+ * Re-exported from auth module for convenience
8
+ */
9
+ interface AuthUser {
10
+ username: string;
11
+ email: string;
12
+ brandIds: number[];
13
+ accountIds: number[];
14
+ isAdmin: boolean;
15
+ isSuperAdmin: boolean;
16
+ roles: string[];
17
+ }
18
+ /**
19
+ * Brand interface representing a hotel brand
20
+ */
21
+ interface Brand {
22
+ id: string;
23
+ name: string;
24
+ accountId: string;
25
+ logo?: string;
26
+ timezone: string;
27
+ status: "active" | "inactive" | "maintenance" | "suspended";
28
+ settings: Record<string, any>;
29
+ }
30
+ /**
31
+ * Account interface representing a customer account
32
+ */
33
+ interface Account {
34
+ id: string;
35
+ name: string;
36
+ logo?: string;
37
+ subscription: Record<string, any>;
38
+ settings: Record<string, any>;
39
+ }
40
+ /**
41
+ * Product interface representing a product definition
42
+ */
43
+ interface Product {
44
+ id: string;
45
+ name: string;
46
+ version: string;
47
+ schema: Record<string, any>;
48
+ uiSchema: Record<string, any>;
49
+ defaultConfig: Record<string, any>;
50
+ isActive: boolean;
51
+ }
52
+ /**
53
+ * ProductInstance interface representing an enabled product for a brand
54
+ */
55
+ interface ProductInstance {
56
+ id: string;
57
+ productId: string;
58
+ productName: string;
59
+ brandId: string;
60
+ accountId: string;
61
+ enabled: boolean;
62
+ config: Record<string, any>;
63
+ version: string;
64
+ }
65
+ /**
66
+ * User interface representing a system user
67
+ */
68
+ interface User {
69
+ id: string;
70
+ cognitoId: string;
71
+ email: string;
72
+ accountId: string;
73
+ brandIds?: string[];
74
+ roles?: string[];
75
+ permissions?: Record<string, any>;
76
+ lastLogin?: string;
77
+ status: "active" | "inactive" | "pending" | "suspended";
78
+ }
79
+ /**
80
+ * BrandUser interface representing a user associated with a brand
81
+ */
82
+ interface BrandUser {
83
+ userId: string;
84
+ brandId: string;
85
+ role: string;
86
+ permissions: string[];
87
+ }
88
+
89
+ export type { Account, AuthUser, Brand, BrandUser, Product, ProductInstance, User };
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * @htlkg/core/utils
3
+ * Core utility functions for Hotelinking applications
4
+ */
5
+ /**
6
+ * Format a date to a localized string
7
+ * @param date - Date to format
8
+ * @param locale - Locale string (default: 'en-US')
9
+ * @param options - Intl.DateTimeFormatOptions
10
+ * @returns Formatted date string
11
+ */
12
+ declare function formatDate(date: Date | string | number, locale?: string, options?: Intl.DateTimeFormatOptions): string;
13
+ /**
14
+ * Truncate text to a maximum length with ellipsis
15
+ * @param text - Text to truncate
16
+ * @param maxLength - Maximum length before truncation
17
+ * @param suffix - Suffix to append (default: '...')
18
+ * @returns Truncated text
19
+ */
20
+ declare function truncateText(text: string, maxLength: number, suffix?: string): string;
21
+ /**
22
+ * Group an array of items by a key
23
+ * @param items - Array of items to group
24
+ * @param keyFn - Function to extract the grouping key
25
+ * @returns Object with keys as group names and values as arrays of items
26
+ */
27
+ declare function groupBy<T>(items: T[], keyFn: (item: T) => string | number): Record<string | number, T[]>;
28
+ /**
29
+ * Debounce a function call
30
+ * @param fn - Function to debounce
31
+ * @param delay - Delay in milliseconds
32
+ * @returns Debounced function
33
+ */
34
+ declare function debounce<T extends (...args: any[]) => any>(fn: T, delay: number): (...args: Parameters<T>) => void;
35
+ /**
36
+ * Throttle a function call
37
+ * @param fn - Function to throttle
38
+ * @param limit - Minimum time between calls in milliseconds
39
+ * @returns Throttled function
40
+ */
41
+ declare function throttle<T extends (...args: any[]) => any>(fn: T, limit: number): (...args: Parameters<T>) => void;
42
+
43
+ export { debounce, formatDate, groupBy, throttle, truncateText };
@@ -0,0 +1,58 @@
1
+ // src/utils/index.ts
2
+ function formatDate(date, locale = "en-US", options = {
3
+ year: "numeric",
4
+ month: "short",
5
+ day: "numeric"
6
+ }) {
7
+ const dateObj = typeof date === "string" || typeof date === "number" ? new Date(date) : date;
8
+ return new Intl.DateTimeFormat(locale, options).format(dateObj);
9
+ }
10
+ function truncateText(text, maxLength, suffix = "...") {
11
+ if (text.length <= maxLength) return text;
12
+ return text.slice(0, maxLength - suffix.length) + suffix;
13
+ }
14
+ function groupBy(items, keyFn) {
15
+ return items.reduce(
16
+ (groups, item) => {
17
+ const key = keyFn(item);
18
+ if (!groups[key]) {
19
+ groups[key] = [];
20
+ }
21
+ groups[key].push(item);
22
+ return groups;
23
+ },
24
+ {}
25
+ );
26
+ }
27
+ function debounce(fn, delay) {
28
+ let timeoutId = null;
29
+ return function debounced(...args) {
30
+ if (timeoutId !== null) {
31
+ clearTimeout(timeoutId);
32
+ }
33
+ timeoutId = setTimeout(() => {
34
+ fn(...args);
35
+ timeoutId = null;
36
+ }, delay);
37
+ };
38
+ }
39
+ function throttle(fn, limit) {
40
+ let inThrottle = false;
41
+ return function throttled(...args) {
42
+ if (!inThrottle) {
43
+ fn(...args);
44
+ inThrottle = true;
45
+ setTimeout(() => {
46
+ inThrottle = false;
47
+ }, limit);
48
+ }
49
+ };
50
+ }
51
+ export {
52
+ debounce,
53
+ formatDate,
54
+ groupBy,
55
+ throttle,
56
+ truncateText
57
+ };
58
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/index.ts"],"sourcesContent":["/**\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"],"mappings":";AAYO,SAAS,WACf,MACA,SAAS,SACT,UAAsC;AAAA,EACrC,MAAM;AAAA,EACN,OAAO;AAAA,EACP,KAAK;AACN,GACS;AACT,QAAM,UAAU,OAAO,SAAS,YAAY,OAAO,SAAS,WAAW,IAAI,KAAK,IAAI,IAAI;AACxF,SAAO,IAAI,KAAK,eAAe,QAAQ,OAAO,EAAE,OAAO,OAAO;AAC/D;AASO,SAAS,aACf,MACA,WACA,SAAS,OACA;AACT,MAAI,KAAK,UAAU,UAAW,QAAO;AACrC,SAAO,KAAK,MAAM,GAAG,YAAY,OAAO,MAAM,IAAI;AACnD;AAQO,SAAS,QACf,OACA,OAC+B;AAC/B,SAAO,MAAM;AAAA,IACZ,CAAC,QAAQ,SAAS;AACjB,YAAM,MAAM,MAAM,IAAI;AACtB,UAAI,CAAC,OAAO,GAAG,GAAG;AACjB,eAAO,GAAG,IAAI,CAAC;AAAA,MAChB;AACA,aAAO,GAAG,EAAE,KAAK,IAAI;AACrB,aAAO;AAAA,IACR;AAAA,IACA,CAAC;AAAA,EACF;AACD;AAQO,SAAS,SACf,IACA,OACmC;AACnC,MAAI,YAAkD;AAEtD,SAAO,SAAS,aAAa,MAAqB;AACjD,QAAI,cAAc,MAAM;AACvB,mBAAa,SAAS;AAAA,IACvB;AACA,gBAAY,WAAW,MAAM;AAC5B,SAAG,GAAG,IAAI;AACV,kBAAY;AAAA,IACb,GAAG,KAAK;AAAA,EACT;AACD;AAQO,SAAS,SACf,IACA,OACmC;AACnC,MAAI,aAAa;AAEjB,SAAO,SAAS,aAAa,MAAqB;AACjD,QAAI,CAAC,YAAY;AAChB,SAAG,GAAG,IAAI;AACV,mBAAa;AACb,iBAAW,MAAM;AAChB,qBAAa;AAAA,MACd,GAAG,KAAK;AAAA,IACT;AAAA,EACD;AACD;","names":[]}
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@htlkg/core",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": "./dist/index.js",
7
+ "./auth": "./dist/auth/index.js",
8
+ "./types": "./dist/types/index.js",
9
+ "./utils": "./dist/utils/index.js",
10
+ "./constants": "./dist/constants/index.js",
11
+ "./errors": "./dist/errors/index.js"
12
+ },
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "scripts": {
17
+ "build": "tsup",
18
+ "dev": "tsup --watch",
19
+ "test": "vitest run",
20
+ "test:watch": "vitest"
21
+ },
22
+ "dependencies": {
23
+ "aws-amplify": "^6.15.7"
24
+ },
25
+ "devDependencies": {
26
+ "astro": "^5.14.7",
27
+ "tsup": "^8.0.0",
28
+ "typescript": "^5.9.2",
29
+ "vitest": "^3.2.4"
30
+ },
31
+ "publishConfig": {
32
+ "access": "restricted"
33
+ }
34
+ }