@htlkg/core 0.0.2 → 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 (35) hide show
  1. package/README.md +51 -0
  2. package/dist/index.d.ts +219 -0
  3. package/dist/index.js +121 -0
  4. package/dist/index.js.map +1 -1
  5. package/package.json +30 -8
  6. package/src/amplify-astro-adapter/amplify-astro-adapter.md +167 -0
  7. package/src/amplify-astro-adapter/createCookieStorageAdapterFromAstroContext.test.ts +296 -0
  8. package/src/amplify-astro-adapter/createCookieStorageAdapterFromAstroContext.ts +97 -0
  9. package/src/amplify-astro-adapter/createRunWithAmplifyServerContext.ts +84 -0
  10. package/src/amplify-astro-adapter/errors.test.ts +115 -0
  11. package/src/amplify-astro-adapter/errors.ts +105 -0
  12. package/src/amplify-astro-adapter/globalSettings.test.ts +78 -0
  13. package/src/amplify-astro-adapter/globalSettings.ts +16 -0
  14. package/src/amplify-astro-adapter/index.ts +14 -0
  15. package/src/amplify-astro-adapter/types.ts +55 -0
  16. package/src/auth/auth.md +178 -0
  17. package/src/auth/index.test.ts +180 -0
  18. package/src/auth/index.ts +294 -0
  19. package/src/constants/constants.md +132 -0
  20. package/src/constants/index.test.ts +116 -0
  21. package/src/constants/index.ts +98 -0
  22. package/src/core-exports.property.test.ts +186 -0
  23. package/src/errors/errors.md +177 -0
  24. package/src/errors/index.test.ts +153 -0
  25. package/src/errors/index.ts +134 -0
  26. package/src/index.ts +65 -0
  27. package/src/routes/index.ts +225 -0
  28. package/src/routes/routes.md +189 -0
  29. package/src/types/index.ts +94 -0
  30. package/src/types/types.md +144 -0
  31. package/src/utils/index.test.ts +257 -0
  32. package/src/utils/index.ts +112 -0
  33. package/src/utils/logger.ts +88 -0
  34. package/src/utils/utils.md +199 -0
  35. package/src/workspace.property.test.ts +235 -0
@@ -0,0 +1,189 @@
1
+ # Routes Module
2
+
3
+ Type-safe route definitions with parameter interpolation. Eliminates string literals scattered across the codebase.
4
+
5
+ ## Installation
6
+
7
+ ```typescript
8
+ import {
9
+ routes,
10
+ adminRoutes,
11
+ portalRoutes,
12
+ authRoutes,
13
+ apiRoutes,
14
+ createRoute,
15
+ matchesRoute,
16
+ extractRouteParams,
17
+ navigateTo,
18
+ buildUrl,
19
+ } from '@htlkg/core/routes';
20
+ ```
21
+
22
+ ## Route Objects
23
+
24
+ ### adminRoutes
25
+
26
+ ```typescript
27
+ adminRoutes.dashboard() // "/admin"
28
+ adminRoutes.accounts() // "/admin/accounts"
29
+ adminRoutes.account({ id: 123 }) // "/admin/accounts/123"
30
+ adminRoutes.accountBrands({ id: 123 }) // "/admin/accounts/123/brands"
31
+ adminRoutes.brands() // "/admin/brands"
32
+ adminRoutes.brand({ id: 456 }) // "/admin/brands/456"
33
+ adminRoutes.brandSettings({ id: 456 }) // "/admin/brands/456/settings"
34
+ adminRoutes.users() // "/admin/users"
35
+ adminRoutes.user({ id: 'abc' }) // "/admin/users/abc"
36
+ adminRoutes.profile() // "/admin/user"
37
+ adminRoutes.analytics() // "/admin/analytics"
38
+ ```
39
+
40
+ ### portalRoutes
41
+
42
+ ```typescript
43
+ portalRoutes.home() // "/"
44
+ portalRoutes.brand({ brandId: 123 }) // "/brands/123"
45
+ portalRoutes.brandSettings({ brandId: 123 }) // "/brands/123/settings"
46
+ portalRoutes.brandDashboard({ brandId: 123 }) // "/brands/123/dashboard"
47
+ portalRoutes.brandAnalytics({ brandId: 123 }) // "/brands/123/analytics"
48
+ ```
49
+
50
+ ### authRoutes
51
+
52
+ ```typescript
53
+ authRoutes.login() // "/login"
54
+ authRoutes.logout() // "/api/auth/logout"
55
+ authRoutes.confirmSignIn() // "/api/auth/confirm-signin"
56
+ ```
57
+
58
+ ### apiRoutes
59
+
60
+ ```typescript
61
+ apiRoutes.auth.logout() // "/api/auth/logout"
62
+ apiRoutes.auth.confirmSignIn() // "/api/auth/confirm-signin"
63
+ ```
64
+
65
+ ## Creating Custom Routes
66
+
67
+ ```typescript
68
+ import { createRoute } from '@htlkg/core/routes';
69
+
70
+ // Simple route
71
+ const homeRoute = createRoute('/home');
72
+ homeRoute(); // "/home"
73
+
74
+ // Route with parameters
75
+ const userRoute = createRoute<{ id: string }>('/users/:id');
76
+ userRoute({ id: '123' }); // "/users/123"
77
+ userRoute.path; // "/users/:id"
78
+
79
+ // Multiple parameters
80
+ const postRoute = createRoute<{ userId: string; postId: string }>(
81
+ '/users/:userId/posts/:postId'
82
+ );
83
+ postRoute({ userId: '1', postId: '42' }); // "/users/1/posts/42"
84
+ ```
85
+
86
+ ## Utility Functions
87
+
88
+ ### matchesRoute
89
+
90
+ Check if a path matches a route pattern.
91
+
92
+ ```typescript
93
+ import { matchesRoute, adminRoutes } from '@htlkg/core/routes';
94
+
95
+ matchesRoute('/admin/accounts/123', adminRoutes.account); // true
96
+ matchesRoute('/admin/users', adminRoutes.account); // false
97
+ ```
98
+
99
+ ### extractRouteParams
100
+
101
+ Extract parameters from a path.
102
+
103
+ ```typescript
104
+ import { extractRouteParams, adminRoutes } from '@htlkg/core/routes';
105
+
106
+ const params = extractRouteParams('/admin/accounts/123', adminRoutes.account);
107
+ // { id: '123' }
108
+
109
+ const noMatch = extractRouteParams('/other/path', adminRoutes.account);
110
+ // null
111
+ ```
112
+
113
+ ### navigateTo
114
+
115
+ Client-side navigation helper.
116
+
117
+ ```typescript
118
+ import { navigateTo, adminRoutes } from '@htlkg/core/routes';
119
+
120
+ // Navigate to account page
121
+ navigateTo(adminRoutes.account({ id: 123 }));
122
+ ```
123
+
124
+ ### buildUrl
125
+
126
+ Build URL with query parameters.
127
+
128
+ ```typescript
129
+ import { buildUrl, adminRoutes } from '@htlkg/core/routes';
130
+
131
+ buildUrl(adminRoutes.users(), { page: 2, status: 'active' });
132
+ // "/admin/users?page=2&status=active"
133
+
134
+ buildUrl(adminRoutes.brands(), { search: null, page: 1 });
135
+ // "/admin/brands?page=1" (null values excluded)
136
+ ```
137
+
138
+ ## Combined Routes Object
139
+
140
+ Access all routes through a single object:
141
+
142
+ ```typescript
143
+ import { routes } from '@htlkg/core/routes';
144
+
145
+ routes.admin.dashboard()
146
+ routes.portal.brand({ brandId: 123 })
147
+ routes.auth.login()
148
+ routes.api.auth.logout()
149
+ ```
150
+
151
+ ## TypeScript Support
152
+
153
+ Routes are fully typed with parameter inference:
154
+
155
+ ```typescript
156
+ import { adminRoutes } from '@htlkg/core/routes';
157
+
158
+ // TypeScript enforces correct parameters
159
+ adminRoutes.account({ id: 123 }); // ✓
160
+ adminRoutes.account({ id: '123' }); // ✓ (string | number)
161
+ adminRoutes.account({}); // ✗ Error: missing 'id'
162
+ adminRoutes.account({ foo: 'bar' }); // ✗ Error: unknown property
163
+ ```
164
+
165
+ ## Usage in Components
166
+
167
+ ```astro
168
+ ---
169
+ import { adminRoutes } from '@htlkg/core/routes';
170
+
171
+ const accountId = 123;
172
+ ---
173
+
174
+ <a href={adminRoutes.account({ id: accountId })}>
175
+ View Account
176
+ </a>
177
+
178
+ <a href={adminRoutes.brands()}>
179
+ All Brands
180
+ </a>
181
+ ```
182
+
183
+ ```typescript
184
+ // In Vue/React components
185
+ import { adminRoutes, buildUrl } from '@htlkg/core/routes';
186
+
187
+ const accountUrl = adminRoutes.account({ id: props.accountId });
188
+ const filteredUrl = buildUrl(adminRoutes.users(), { role: 'admin' });
189
+ ```
@@ -0,0 +1,94 @@
1
+ /**
2
+ * @htlkg/core/types
3
+ * Core TypeScript types and interfaces for Hotelinking applications
4
+ */
5
+
6
+ /**
7
+ * AuthUser interface representing an authenticated user
8
+ * Re-exported from auth module for convenience
9
+ */
10
+ export interface AuthUser {
11
+ username: string;
12
+ email: string;
13
+ brandIds: number[];
14
+ accountIds: number[];
15
+ isAdmin: boolean;
16
+ isSuperAdmin: boolean;
17
+ roles: string[];
18
+ }
19
+
20
+ /**
21
+ * Brand interface representing a hotel brand
22
+ */
23
+ export interface Brand {
24
+ id: string;
25
+ name: string;
26
+ accountId: string;
27
+ logo?: string;
28
+ timezone: string;
29
+ status: "active" | "inactive" | "maintenance" | "suspended";
30
+ settings: Record<string, any>;
31
+ }
32
+
33
+ /**
34
+ * Account interface representing a customer account
35
+ */
36
+ export interface Account {
37
+ id: string;
38
+ name: string;
39
+ logo?: string;
40
+ subscription: Record<string, any>;
41
+ settings: Record<string, any>;
42
+ }
43
+
44
+ /**
45
+ * Product interface representing a product definition
46
+ */
47
+ export interface Product {
48
+ id: string;
49
+ name: string;
50
+ version: string;
51
+ schema: Record<string, any>;
52
+ uiSchema: Record<string, any>;
53
+ defaultConfig: Record<string, any>;
54
+ isActive: boolean;
55
+ }
56
+
57
+ /**
58
+ * ProductInstance interface representing an enabled product for a brand
59
+ */
60
+ export interface ProductInstance {
61
+ id: string;
62
+ productId: string;
63
+ productName: string;
64
+ brandId: string;
65
+ accountId: string;
66
+ enabled: boolean;
67
+ config: Record<string, any>;
68
+ version: string;
69
+ }
70
+
71
+ /**
72
+ * User interface representing a system user
73
+ */
74
+ export interface User {
75
+ id: string;
76
+ cognitoId: string;
77
+ email: string;
78
+ accountId: string;
79
+ brandIds?: string[];
80
+ roles?: string[];
81
+ permissions?: Record<string, any>;
82
+ lastLogin?: string;
83
+ status: "active" | "inactive" | "pending" | "suspended";
84
+ }
85
+
86
+ /**
87
+ * BrandUser interface representing a user associated with a brand
88
+ */
89
+ export interface BrandUser {
90
+ userId: string;
91
+ brandId: string;
92
+ role: string;
93
+ permissions: string[];
94
+ }
@@ -0,0 +1,144 @@
1
+ # Types Module
2
+
3
+ Core TypeScript interfaces for Hotelinking applications.
4
+
5
+ ## Installation
6
+
7
+ ```typescript
8
+ import type {
9
+ AuthUser,
10
+ Brand,
11
+ Account,
12
+ Product,
13
+ ProductInstance,
14
+ User,
15
+ BrandUser,
16
+ } from '@htlkg/core/types';
17
+ ```
18
+
19
+ ## Interfaces
20
+
21
+ ### AuthUser
22
+
23
+ Authenticated user from Cognito.
24
+
25
+ ```typescript
26
+ interface AuthUser {
27
+ username: string;
28
+ email: string;
29
+ brandIds: number[];
30
+ accountIds: number[];
31
+ isAdmin: boolean;
32
+ isSuperAdmin: boolean;
33
+ roles: string[];
34
+ }
35
+ ```
36
+
37
+ ### Brand
38
+
39
+ Hotel brand entity.
40
+
41
+ ```typescript
42
+ interface Brand {
43
+ id: string;
44
+ name: string;
45
+ accountId: string;
46
+ logo?: string;
47
+ timezone: string;
48
+ status: 'active' | 'inactive' | 'maintenance' | 'suspended';
49
+ settings: Record<string, any>;
50
+ }
51
+ ```
52
+
53
+
54
+ ### Account
55
+
56
+ Customer account entity.
57
+
58
+ ```typescript
59
+ interface Account {
60
+ id: string;
61
+ name: string;
62
+ logo?: string;
63
+ subscription: Record<string, any>;
64
+ settings: Record<string, any>;
65
+ }
66
+ ```
67
+
68
+ ### Product
69
+
70
+ Product definition.
71
+
72
+ ```typescript
73
+ interface Product {
74
+ id: string;
75
+ name: string;
76
+ version: string;
77
+ schema: Record<string, any>;
78
+ uiSchema: Record<string, any>;
79
+ defaultConfig: Record<string, any>;
80
+ isActive: boolean;
81
+ }
82
+ ```
83
+
84
+ ### ProductInstance
85
+
86
+ Enabled product for a brand.
87
+
88
+ ```typescript
89
+ interface ProductInstance {
90
+ id: string;
91
+ productId: string;
92
+ productName: string;
93
+ brandId: string;
94
+ accountId: string;
95
+ enabled: boolean;
96
+ config: Record<string, any>;
97
+ version: string;
98
+ }
99
+ ```
100
+
101
+ ### User
102
+
103
+ System user entity.
104
+
105
+ ```typescript
106
+ interface User {
107
+ id: string;
108
+ cognitoId: string;
109
+ email: string;
110
+ accountId: string;
111
+ brandIds?: string[];
112
+ roles?: string[];
113
+ permissions?: Record<string, any>;
114
+ lastLogin?: string;
115
+ status: 'active' | 'inactive' | 'pending' | 'suspended';
116
+ }
117
+ ```
118
+
119
+ ### BrandUser
120
+
121
+ User-brand association.
122
+
123
+ ```typescript
124
+ interface BrandUser {
125
+ userId: string;
126
+ brandId: string;
127
+ role: string;
128
+ permissions: string[];
129
+ }
130
+ ```
131
+
132
+ ## Usage Examples
133
+
134
+ ```typescript
135
+ import type { Brand, User, ProductInstance } from '@htlkg/core/types';
136
+
137
+ function getBrandProducts(brand: Brand): ProductInstance[] {
138
+ // ...
139
+ }
140
+
141
+ function isUserActive(user: User): boolean {
142
+ return user.status === 'active';
143
+ }
144
+ ```
@@ -0,0 +1,257 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import {
3
+ formatDate,
4
+ truncateText,
5
+ groupBy,
6
+ debounce,
7
+ throttle,
8
+ } from "./index";
9
+
10
+ describe("Utility Functions", () => {
11
+ describe("formatDate", () => {
12
+ it("should format Date object", () => {
13
+ const date = new Date("2024-01-15T10:30:00Z");
14
+ const result = formatDate(date);
15
+ expect(result).toContain("Jan");
16
+ expect(result).toContain("15");
17
+ expect(result).toContain("2024");
18
+ });
19
+
20
+ it("should format date string", () => {
21
+ const result = formatDate("2024-01-15");
22
+ expect(result).toContain("Jan");
23
+ expect(result).toContain("15");
24
+ expect(result).toContain("2024");
25
+ });
26
+
27
+ it("should format timestamp", () => {
28
+ const timestamp = new Date("2024-01-15").getTime();
29
+ const result = formatDate(timestamp);
30
+ expect(result).toContain("Jan");
31
+ expect(result).toContain("15");
32
+ expect(result).toContain("2024");
33
+ });
34
+
35
+ it("should use custom locale", () => {
36
+ const date = new Date("2024-01-15");
37
+ const result = formatDate(date, "es-ES");
38
+ // Spanish month abbreviation
39
+ expect(result).toContain("ene");
40
+ });
41
+
42
+ it("should use custom options", () => {
43
+ const date = new Date("2024-01-15T10:30:00Z");
44
+ const result = formatDate(date, "en-US", {
45
+ year: "numeric",
46
+ month: "long",
47
+ day: "numeric",
48
+ hour: "2-digit",
49
+ minute: "2-digit",
50
+ });
51
+ expect(result).toContain("January");
52
+ });
53
+ });
54
+
55
+ describe("truncateText", () => {
56
+ it("should not truncate short text", () => {
57
+ const result = truncateText("Hello", 10);
58
+ expect(result).toBe("Hello");
59
+ });
60
+
61
+ it("should truncate long text", () => {
62
+ const result = truncateText("Hello World", 8);
63
+ expect(result).toBe("Hello...");
64
+ });
65
+
66
+ it("should use custom suffix", () => {
67
+ const result = truncateText("Hello World", 8, "…");
68
+ expect(result).toBe("Hello W…");
69
+ });
70
+
71
+ it("should handle exact length", () => {
72
+ const result = truncateText("Hello", 5);
73
+ expect(result).toBe("Hello");
74
+ });
75
+
76
+ it("should handle empty string", () => {
77
+ const result = truncateText("", 10);
78
+ expect(result).toBe("");
79
+ });
80
+
81
+ it("should account for suffix length", () => {
82
+ const result = truncateText("Hello World", 10, "...");
83
+ expect(result).toBe("Hello W...");
84
+ expect(result.length).toBe(10);
85
+ });
86
+ });
87
+
88
+ describe("groupBy", () => {
89
+ it("should group items by key", () => {
90
+ const items = [
91
+ { id: 1, category: "A" },
92
+ { id: 2, category: "B" },
93
+ { id: 3, category: "A" },
94
+ ];
95
+ const result = groupBy(items, (item) => item.category);
96
+
97
+ expect(result).toEqual({
98
+ A: [
99
+ { id: 1, category: "A" },
100
+ { id: 3, category: "A" },
101
+ ],
102
+ B: [{ id: 2, category: "B" }],
103
+ });
104
+ });
105
+
106
+ it("should handle numeric keys", () => {
107
+ const items = [
108
+ { id: 1, value: 10 },
109
+ { id: 2, value: 20 },
110
+ { id: 3, value: 10 },
111
+ ];
112
+ const result = groupBy(items, (item) => item.value);
113
+
114
+ expect(result).toEqual({
115
+ 10: [
116
+ { id: 1, value: 10 },
117
+ { id: 3, value: 10 },
118
+ ],
119
+ 20: [{ id: 2, value: 20 }],
120
+ });
121
+ });
122
+
123
+ it("should handle empty array", () => {
124
+ const result = groupBy([], (item: any) => item.key);
125
+ expect(result).toEqual({});
126
+ });
127
+
128
+ it("should handle single item", () => {
129
+ const items = [{ id: 1, type: "test" }];
130
+ const result = groupBy(items, (item) => item.type);
131
+
132
+ expect(result).toEqual({
133
+ test: [{ id: 1, type: "test" }],
134
+ });
135
+ });
136
+ });
137
+
138
+ describe("debounce", () => {
139
+ beforeEach(() => {
140
+ vi.useFakeTimers();
141
+ });
142
+
143
+ afterEach(() => {
144
+ vi.restoreAllMocks();
145
+ });
146
+
147
+ it("should delay function execution", () => {
148
+ const fn = vi.fn();
149
+ const debounced = debounce(fn, 100);
150
+
151
+ debounced();
152
+ expect(fn).not.toHaveBeenCalled();
153
+
154
+ vi.advanceTimersByTime(100);
155
+ expect(fn).toHaveBeenCalledTimes(1);
156
+ });
157
+
158
+ it("should cancel previous calls", () => {
159
+ const fn = vi.fn();
160
+ const debounced = debounce(fn, 100);
161
+
162
+ debounced();
163
+ debounced();
164
+ debounced();
165
+
166
+ vi.advanceTimersByTime(100);
167
+ expect(fn).toHaveBeenCalledTimes(1);
168
+ });
169
+
170
+ it("should pass arguments to function", () => {
171
+ const fn = vi.fn();
172
+ const debounced = debounce(fn, 100);
173
+
174
+ debounced("arg1", "arg2");
175
+ vi.advanceTimersByTime(100);
176
+
177
+ expect(fn).toHaveBeenCalledWith("arg1", "arg2");
178
+ });
179
+
180
+ it("should reset timer on each call", () => {
181
+ const fn = vi.fn();
182
+ const debounced = debounce(fn, 100);
183
+
184
+ debounced();
185
+ vi.advanceTimersByTime(50);
186
+ debounced();
187
+ vi.advanceTimersByTime(50);
188
+
189
+ expect(fn).not.toHaveBeenCalled();
190
+
191
+ vi.advanceTimersByTime(50);
192
+ expect(fn).toHaveBeenCalledTimes(1);
193
+ });
194
+ });
195
+
196
+ describe("throttle", () => {
197
+ beforeEach(() => {
198
+ vi.useFakeTimers();
199
+ });
200
+
201
+ afterEach(() => {
202
+ vi.restoreAllMocks();
203
+ });
204
+
205
+ it("should execute function immediately", () => {
206
+ const fn = vi.fn();
207
+ const throttled = throttle(fn, 100);
208
+
209
+ throttled();
210
+ expect(fn).toHaveBeenCalledTimes(1);
211
+ });
212
+
213
+ it("should ignore calls within limit", () => {
214
+ const fn = vi.fn();
215
+ const throttled = throttle(fn, 100);
216
+
217
+ throttled();
218
+ throttled();
219
+ throttled();
220
+
221
+ expect(fn).toHaveBeenCalledTimes(1);
222
+ });
223
+
224
+ it("should allow calls after limit", () => {
225
+ const fn = vi.fn();
226
+ const throttled = throttle(fn, 100);
227
+
228
+ throttled();
229
+ expect(fn).toHaveBeenCalledTimes(1);
230
+
231
+ vi.advanceTimersByTime(100);
232
+ throttled();
233
+ expect(fn).toHaveBeenCalledTimes(2);
234
+ });
235
+
236
+ it("should pass arguments to function", () => {
237
+ const fn = vi.fn();
238
+ const throttled = throttle(fn, 100);
239
+
240
+ throttled("arg1", "arg2");
241
+ expect(fn).toHaveBeenCalledWith("arg1", "arg2");
242
+ });
243
+
244
+ it("should throttle multiple rapid calls", () => {
245
+ const fn = vi.fn();
246
+ const throttled = throttle(fn, 100);
247
+
248
+ throttled();
249
+ vi.advanceTimersByTime(50);
250
+ throttled();
251
+ vi.advanceTimersByTime(50);
252
+ throttled();
253
+
254
+ expect(fn).toHaveBeenCalledTimes(2);
255
+ });
256
+ });
257
+ });