@groundbrick/sveltekit-adapter 0.1.0
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.
- package/README.md +554 -0
- package/dist/analytics/components/GoogleAnalytics.svelte +96 -0
- package/dist/analytics/config/analytics.config.d.ts +6 -0
- package/dist/analytics/config/analytics.config.d.ts.map +1 -0
- package/dist/analytics/config/analytics.config.js +27 -0
- package/dist/analytics/config/analytics.config.js.map +1 -0
- package/dist/analytics/index.d.ts +7 -0
- package/dist/analytics/index.d.ts.map +1 -0
- package/dist/analytics/index.js +9 -0
- package/dist/analytics/index.js.map +1 -0
- package/dist/analytics/interfaces/IAnalyticsService.d.ts +40 -0
- package/dist/analytics/interfaces/IAnalyticsService.d.ts.map +1 -0
- package/dist/analytics/interfaces/IAnalyticsService.js +2 -0
- package/dist/analytics/interfaces/IAnalyticsService.js.map +1 -0
- package/dist/analytics/services/AnalyticsService.d.ts +37 -0
- package/dist/analytics/services/AnalyticsService.d.ts.map +1 -0
- package/dist/analytics/services/AnalyticsService.js +261 -0
- package/dist/analytics/services/AnalyticsService.js.map +1 -0
- package/dist/analytics/utils/consent.d.ts +52 -0
- package/dist/analytics/utils/consent.d.ts.map +1 -0
- package/dist/analytics/utils/consent.js +138 -0
- package/dist/analytics/utils/consent.js.map +1 -0
- package/dist/auth/index.d.ts +10 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +49 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/client/ApiClient.d.ts +17 -0
- package/dist/client/ApiClient.d.ts.map +1 -0
- package/dist/client/ApiClient.js +184 -0
- package/dist/client/ApiClient.js.map +1 -0
- package/dist/client/NotificationComponent.d.ts +7 -0
- package/dist/client/NotificationComponent.d.ts.map +1 -0
- package/dist/client/NotificationComponent.js +131 -0
- package/dist/client/NotificationComponent.js.map +1 -0
- package/dist/client/apiWrapper.d.ts +324 -0
- package/dist/client/apiWrapper.d.ts.map +1 -0
- package/dist/client/apiWrapper.js +257 -0
- package/dist/client/apiWrapper.js.map +1 -0
- package/dist/client/authErrorHandler.d.ts +19 -0
- package/dist/client/authErrorHandler.d.ts.map +1 -0
- package/dist/client/authErrorHandler.js +40 -0
- package/dist/client/authErrorHandler.js.map +1 -0
- package/dist/client/hooks.d.ts +11 -0
- package/dist/client/hooks.d.ts.map +1 -0
- package/dist/client/hooks.js +28 -0
- package/dist/client/hooks.js.map +1 -0
- package/dist/client/index.d.ts +8 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +13 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/jwtDecoder.d.ts +56 -0
- package/dist/client/jwtDecoder.d.ts.map +1 -0
- package/dist/client/jwtDecoder.js +114 -0
- package/dist/client/jwtDecoder.js.map +1 -0
- package/dist/client/notifications.d.ts +55 -0
- package/dist/client/notifications.d.ts.map +1 -0
- package/dist/client/notifications.js +160 -0
- package/dist/client/notifications.js.map +1 -0
- package/dist/client/stores.d.ts +26 -0
- package/dist/client/stores.d.ts.map +1 -0
- package/dist/client/stores.js +196 -0
- package/dist/client/stores.js.map +1 -0
- package/dist/consent/components/CookieConsent.svelte +580 -0
- package/dist/consent/config/consent.config.d.ts +7 -0
- package/dist/consent/config/consent.config.d.ts.map +1 -0
- package/dist/consent/config/consent.config.js +194 -0
- package/dist/consent/config/consent.config.js.map +1 -0
- package/dist/consent/index.d.ts +5 -0
- package/dist/consent/index.d.ts.map +1 -0
- package/dist/consent/index.js +7 -0
- package/dist/consent/index.js.map +1 -0
- package/dist/consent/interfaces/IConsentService.d.ts +121 -0
- package/dist/consent/interfaces/IConsentService.d.ts.map +1 -0
- package/dist/consent/interfaces/IConsentService.js +2 -0
- package/dist/consent/interfaces/IConsentService.js.map +1 -0
- package/dist/consent/services/ConsentService.d.ts +39 -0
- package/dist/consent/services/ConsentService.d.ts.map +1 -0
- package/dist/consent/services/ConsentService.js +302 -0
- package/dist/consent/services/ConsentService.js.map +1 -0
- package/dist/consent/utils/analytics.d.ts +52 -0
- package/dist/consent/utils/analytics.d.ts.map +1 -0
- package/dist/consent/utils/analytics.js +181 -0
- package/dist/consent/utils/analytics.js.map +1 -0
- package/dist/hooks/index.d.ts +14 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +218 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/types/index.d.ts +88 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +87 -0
package/README.md
ADDED
|
@@ -0,0 +1,554 @@
|
|
|
1
|
+
# @groundbrick/sveltekit-adapter
|
|
2
|
+
|
|
3
|
+
SvelteKit integration adapter para o microframework TypeScript - fornece integração client-side com APIs Express, gerenciamento de estado reativo, autenticação JWT, hooks e sistema de consentimento de cookies.
|
|
4
|
+
|
|
5
|
+
## Features Implementadas
|
|
6
|
+
|
|
7
|
+
- **🔧 ApiClient** - Cliente HTTP com autenticação, retry, timeout e suporte a cookies
|
|
8
|
+
- **🔐 Gerenciamento de Autenticação** - Autenticação segura usando HttpOnly cookies
|
|
9
|
+
- **📊 Stores Reativas** - Stores Svelte para estado de auth e API com suporte a runes
|
|
10
|
+
- **� Sistema de Notificações** - Sistema completo de notificações toast com auto-clear na navegação
|
|
11
|
+
- **�🛡️ Error Handling** - Hooks abrangentes de tratamento de erros
|
|
12
|
+
- **📝 Request Logging** - Log automático de request/response com timing
|
|
13
|
+
- **🎣 SvelteKit Hooks** - Hooks pré-construídos para autenticação e logging
|
|
14
|
+
- **🔄 Retry Logic** - Lógica de retry automática com exponential backoff
|
|
15
|
+
- **⏱️ Timeout Handling** - Controle de timeout configurável para requests
|
|
16
|
+
- **🍪 Cookie Consent** - Sistema GDPR-compliant de consentimento com Google Analytics
|
|
17
|
+
|
|
18
|
+
## Instalação
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install @groundbrick/sveltekit-adapter
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Peer Dependencies
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install @sveltejs/kit svelte
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
### 1. Configuração de Hooks
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
// src/hooks.server.ts
|
|
36
|
+
import { sequence } from '@sveltejs/kit/hooks';
|
|
37
|
+
import { createSvelteKitHooks } from '@groundbrick/sveltekit-adapter';
|
|
38
|
+
|
|
39
|
+
const { hooks, handleError } = createSvelteKitHooks({
|
|
40
|
+
logger: {
|
|
41
|
+
logRequests: true,
|
|
42
|
+
logResponses: true,
|
|
43
|
+
includeHeaders: false,
|
|
44
|
+
excludePaths: ['/favicon.ico', '/robots.txt', '/health'],
|
|
45
|
+
context: 'my-app'
|
|
46
|
+
},
|
|
47
|
+
auth: {
|
|
48
|
+
tokenKey: 'auth_token',
|
|
49
|
+
publicPaths: ['/login', '/register', '/', '/api/public/*'],
|
|
50
|
+
redirectPath: '/login',
|
|
51
|
+
onAuthRequired: () => {
|
|
52
|
+
console.log('Authentication required');
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
errorHandler: {
|
|
56
|
+
logErrors: true,
|
|
57
|
+
context: 'my-app',
|
|
58
|
+
onError: (error, context) => {
|
|
59
|
+
// Custom error handling
|
|
60
|
+
console.error('App error:', error, context);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
export const handle = sequence(...hooks);
|
|
66
|
+
export { handleError };
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 2. Configuração do API Client
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
// src/lib/api.ts
|
|
73
|
+
import { createApiClient } from '@groundbrick/sveltekit-adapter';
|
|
74
|
+
|
|
75
|
+
export const apiClient = createApiClient({
|
|
76
|
+
baseUrl: 'http://localhost:3000/api',
|
|
77
|
+
timeout: 10000,
|
|
78
|
+
retries: 3,
|
|
79
|
+
defaultHeaders: {
|
|
80
|
+
'X-App-Version': '1.0.0',
|
|
81
|
+
'X-Client': 'sveltekit'
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### 3. Cookie Consent Setup
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
// src/routes/+layout.svelte
|
|
90
|
+
<script lang="ts">
|
|
91
|
+
import {
|
|
92
|
+
CookieConsent,
|
|
93
|
+
ConsentService,
|
|
94
|
+
createConsentAwareAnalytics
|
|
95
|
+
} from '@groundbrick/sveltekit-adapter/consent';
|
|
96
|
+
|
|
97
|
+
// Initialize consent service
|
|
98
|
+
const consentService = ConsentService.getInstance();
|
|
99
|
+
|
|
100
|
+
// Setup analytics with consent awareness
|
|
101
|
+
const analytics = createConsentAwareAnalytics(consentService, {
|
|
102
|
+
gaId: 'G-XXXXXXXXXX',
|
|
103
|
+
enableDebug: true
|
|
104
|
+
});
|
|
105
|
+
</script>
|
|
106
|
+
|
|
107
|
+
<!-- Your app content -->
|
|
108
|
+
<slot />
|
|
109
|
+
|
|
110
|
+
<!-- Cookie consent banner -->
|
|
111
|
+
<CookieConsent
|
|
112
|
+
theme="light"
|
|
113
|
+
position="bottom"
|
|
114
|
+
onConsentChange={(state) => {
|
|
115
|
+
console.log('Consent updated:', state);
|
|
116
|
+
}}
|
|
117
|
+
/>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 4. Using Stores
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
// src/routes/+page.svelte
|
|
124
|
+
<script lang="ts">
|
|
125
|
+
import {
|
|
126
|
+
authStore,
|
|
127
|
+
apiStore,
|
|
128
|
+
isAuthenticated,
|
|
129
|
+
currentUser,
|
|
130
|
+
initializeStores
|
|
131
|
+
} from '@groundbrick/sveltekit-adapter';
|
|
132
|
+
|
|
133
|
+
// Initialize stores on app start
|
|
134
|
+
initializeStores();
|
|
135
|
+
|
|
136
|
+
// Reactive to authentication state
|
|
137
|
+
$: if ($isAuthenticated) {
|
|
138
|
+
console.log('User is logged in:', $currentUser);
|
|
139
|
+
}
|
|
140
|
+
</script>
|
|
141
|
+
|
|
142
|
+
{#if $isAuthenticated}
|
|
143
|
+
<h1>Welcome, {$currentUser?.name}!</h1>
|
|
144
|
+
{:else}
|
|
145
|
+
<a href="/login">Please log in</a>
|
|
146
|
+
{/if}
|
|
147
|
+
|
|
148
|
+
{#if $apiStore.loading}
|
|
149
|
+
<div class="spinner">Loading...</div>
|
|
150
|
+
{/if}
|
|
151
|
+
|
|
152
|
+
{#if $apiStore.error}
|
|
153
|
+
<div class="error">{$apiStore.error}</div>
|
|
154
|
+
{/if}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Module Exports
|
|
158
|
+
|
|
159
|
+
### Main Package
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
// Everything from client and hooks
|
|
163
|
+
import {
|
|
164
|
+
createApiClient,
|
|
165
|
+
authStore,
|
|
166
|
+
createSvelteKitHooks
|
|
167
|
+
} from '@groundbrick/sveltekit-adapter';
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Client Module
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
// Just client functionality
|
|
174
|
+
import {
|
|
175
|
+
ApiClient,
|
|
176
|
+
createApiClient,
|
|
177
|
+
authStore,
|
|
178
|
+
apiStore,
|
|
179
|
+
isAuthenticated,
|
|
180
|
+
currentUser,
|
|
181
|
+
isLoading,
|
|
182
|
+
currentError,
|
|
183
|
+
initializeStores,
|
|
184
|
+
clearErrors
|
|
185
|
+
} from '@groundbrick/sveltekit-adapter/client';
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Hooks Module
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
// Just hooks functionality
|
|
192
|
+
import {
|
|
193
|
+
createSvelteKitHooks,
|
|
194
|
+
createAuthHook,
|
|
195
|
+
createLoggerHook,
|
|
196
|
+
createErrorHandlerHook
|
|
197
|
+
} from '@groundbrick/sveltekit-adapter/hooks';
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Consent Module
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
// Cookie consent system
|
|
204
|
+
import {
|
|
205
|
+
CookieConsent,
|
|
206
|
+
ConsentService,
|
|
207
|
+
createConsentAwareAnalytics,
|
|
208
|
+
GDPR_CONFIG,
|
|
209
|
+
CCPA_CONFIG
|
|
210
|
+
} from '@groundbrick/sveltekit-adapter/consent';
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## API Client Features
|
|
214
|
+
|
|
215
|
+
### Basic Usage
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
import { createApiClient } from '@groundbrick/sveltekit-adapter';
|
|
219
|
+
|
|
220
|
+
const client = createApiClient({
|
|
221
|
+
baseUrl: 'https://api.example.com',
|
|
222
|
+
timeout: 10000,
|
|
223
|
+
retries: 3
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// GET request
|
|
227
|
+
const users = await client.get('/users');
|
|
228
|
+
|
|
229
|
+
// POST request with data
|
|
230
|
+
const newUser = await client.post('/users', {
|
|
231
|
+
name: 'John Doe',
|
|
232
|
+
email: 'john@example.com'
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// Request with auth token
|
|
236
|
+
const profile = await client.get('/profile', {
|
|
237
|
+
headers: { Authorization: 'Bearer token' }
|
|
238
|
+
});
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Advanced Configuration
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
const client = createApiClient({
|
|
245
|
+
baseUrl: 'https://api.example.com',
|
|
246
|
+
timeout: 15000,
|
|
247
|
+
retries: 5,
|
|
248
|
+
retryDelay: 1000,
|
|
249
|
+
defaultHeaders: {
|
|
250
|
+
'Content-Type': 'application/json',
|
|
251
|
+
'X-API-Version': '2024-01-01'
|
|
252
|
+
},
|
|
253
|
+
interceptors: {
|
|
254
|
+
request: (config) => {
|
|
255
|
+
// Add timestamp to all requests
|
|
256
|
+
config.headers['X-Timestamp'] = new Date().toISOString();
|
|
257
|
+
return config;
|
|
258
|
+
},
|
|
259
|
+
response: (response) => {
|
|
260
|
+
// Log all successful responses
|
|
261
|
+
console.log('Response received:', response.status);
|
|
262
|
+
return response;
|
|
263
|
+
},
|
|
264
|
+
error: (error) => {
|
|
265
|
+
// Custom error handling
|
|
266
|
+
if (error.status === 401) {
|
|
267
|
+
// Redirect to login
|
|
268
|
+
window.location.href = '/login';
|
|
269
|
+
}
|
|
270
|
+
throw error;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Authentication Flow
|
|
277
|
+
|
|
278
|
+
The adapter provides secure authentication using HttpOnly cookies:
|
|
279
|
+
|
|
280
|
+
#### How Authentication Works:
|
|
281
|
+
|
|
282
|
+
1. **Server-side**: Auth hook validates JWT tokens from HttpOnly cookies (no client-side token handling)
|
|
283
|
+
2. **Client-side**: API Client automatically includes cookies with all requests
|
|
284
|
+
3. **Automatic cleanup**: Invalid tokens are cleared through HttpOnly cookie management
|
|
285
|
+
4. **Smart redirects**: Users are redirected to login with current page as return URL
|
|
286
|
+
5. **No performance impact**: No unnecessary API calls on page loads
|
|
287
|
+
|
|
288
|
+
#### Auth Hook Configuration:
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
// src/hooks.server.ts
|
|
292
|
+
const { hooks, handleError } = createSvelteKitHooks({
|
|
293
|
+
auth: {
|
|
294
|
+
tokenKey: 'auth_token',
|
|
295
|
+
redirectPath: '/login',
|
|
296
|
+
publicPaths: [
|
|
297
|
+
'/login',
|
|
298
|
+
'/register',
|
|
299
|
+
'/',
|
|
300
|
+
'/landing',
|
|
301
|
+
'/api/*', // Allow API calls to pass through
|
|
302
|
+
'/favicon.ico',
|
|
303
|
+
'/robots.txt'
|
|
304
|
+
],
|
|
305
|
+
validateExpiration: true // Enable JWT expiration checking (default: true)
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
## Cookie Consent System
|
|
311
|
+
|
|
312
|
+
### GDPR Compliance
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
import {
|
|
316
|
+
ConsentService,
|
|
317
|
+
GDPR_CONFIG,
|
|
318
|
+
createConsentAwareAnalytics
|
|
319
|
+
} from '@groundbrick/sveltekit-adapter/consent';
|
|
320
|
+
|
|
321
|
+
// GDPR-compliant setup
|
|
322
|
+
const consentService = ConsentService.getInstance(GDPR_CONFIG);
|
|
323
|
+
|
|
324
|
+
// Analytics only loads with explicit consent
|
|
325
|
+
const analytics = createConsentAwareAnalytics(consentService, {
|
|
326
|
+
gaId: 'G-XXXXXXXXXX',
|
|
327
|
+
anonymizeIp: true
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// Track events (respects consent automatically)
|
|
331
|
+
analytics.trackEvent({
|
|
332
|
+
action: 'button_click',
|
|
333
|
+
category: 'UI',
|
|
334
|
+
label: 'signup'
|
|
335
|
+
});
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Custom Configuration
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
const customConfig = {
|
|
342
|
+
banner: {
|
|
343
|
+
title: '🍪 Cookies',
|
|
344
|
+
message: 'We use cookies to improve your experience.',
|
|
345
|
+
acceptAllText: 'Accept All',
|
|
346
|
+
rejectAllText: 'Reject All'
|
|
347
|
+
},
|
|
348
|
+
categories: {
|
|
349
|
+
functional: {
|
|
350
|
+
required: true,
|
|
351
|
+
name: 'Essential',
|
|
352
|
+
description: 'Required for basic functionality'
|
|
353
|
+
},
|
|
354
|
+
analytics: {
|
|
355
|
+
required: false,
|
|
356
|
+
name: 'Analytics',
|
|
357
|
+
description: 'Help us improve our website'
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
const consentService = ConsentService.getInstance(customConfig);
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
## Stores System
|
|
366
|
+
|
|
367
|
+
### Authentication Store
|
|
368
|
+
|
|
369
|
+
```typescript
|
|
370
|
+
import { authStore, isAuthenticated, currentUser } from '@groundbrick/sveltekit-adapter';
|
|
371
|
+
|
|
372
|
+
// Reactive authentication state
|
|
373
|
+
$: if ($isAuthenticated) {
|
|
374
|
+
console.log('User:', $currentUser);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Login
|
|
378
|
+
authStore.login({
|
|
379
|
+
token: 'jwt-token',
|
|
380
|
+
user: { id: 1, name: 'John', email: 'john@example.com' }
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
// Logout
|
|
384
|
+
authStore.logout();
|
|
385
|
+
|
|
386
|
+
// Check if user has specific role
|
|
387
|
+
if ($currentUser?.roles?.includes('admin')) {
|
|
388
|
+
// Show admin content
|
|
389
|
+
}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
### API Store
|
|
393
|
+
|
|
394
|
+
```typescript
|
|
395
|
+
import { apiStore, isLoading, currentError } from '@groundbrick/sveltekit-adapter';
|
|
396
|
+
|
|
397
|
+
// Loading states
|
|
398
|
+
$: if ($isLoading) {
|
|
399
|
+
// Show spinner
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Error handling
|
|
403
|
+
$: if ($currentError) {
|
|
404
|
+
// Show error message
|
|
405
|
+
console.error('API Error:', $currentError);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Manual error clearing
|
|
409
|
+
apiStore.clearError();
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### Notification System
|
|
413
|
+
|
|
414
|
+
```typescript
|
|
415
|
+
import {
|
|
416
|
+
showSuccess,
|
|
417
|
+
showError,
|
|
418
|
+
showWarning,
|
|
419
|
+
showInfo,
|
|
420
|
+
clearAllNotifications,
|
|
421
|
+
useNavigationNotificationClear
|
|
422
|
+
} from '@groundbrick/sveltekit-adapter';
|
|
423
|
+
|
|
424
|
+
// Enable auto-clearing on navigation (add to your root layout)
|
|
425
|
+
useNavigationNotificationClear();
|
|
426
|
+
|
|
427
|
+
// Show different types of notifications
|
|
428
|
+
showSuccess('Data saved successfully!');
|
|
429
|
+
showError('Something went wrong!');
|
|
430
|
+
showWarning('This action cannot be undone!');
|
|
431
|
+
showInfo('New feature available!');
|
|
432
|
+
|
|
433
|
+
// With options
|
|
434
|
+
showSuccess('Operation complete!', {
|
|
435
|
+
title: 'Success',
|
|
436
|
+
duration: 3000,
|
|
437
|
+
persistent: false // won't clear on navigation if true
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
// Clear notifications
|
|
441
|
+
clearAllNotifications();
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
Add notification component to your layout:
|
|
445
|
+
|
|
446
|
+
```svelte
|
|
447
|
+
<script>
|
|
448
|
+
import { useNavigationNotificationClear } from '@groundbrick/sveltekit-adapter';
|
|
449
|
+
import Notifications from '$lib/components/Notifications.svelte';
|
|
450
|
+
|
|
451
|
+
// Enable auto-clearing
|
|
452
|
+
useNavigationNotificationClear();
|
|
453
|
+
</script>
|
|
454
|
+
|
|
455
|
+
<main>
|
|
456
|
+
<slot />
|
|
457
|
+
</main>
|
|
458
|
+
|
|
459
|
+
<!-- Notifications -->
|
|
460
|
+
<Notifications />
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
### Logging Estruturado
|
|
464
|
+
|
|
465
|
+
- **Request/Response**: Log automático com timing
|
|
466
|
+
```typescript
|
|
467
|
+
// Configuração no hooks.server.ts
|
|
468
|
+
const loggerHook = createLoggerHook({
|
|
469
|
+
logRequests: true, // GET /api/users [timing: 245ms]
|
|
470
|
+
logResponses: true // GET /api/users 200 OK [duration: 245ms]
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
// Logs automáticos no console/arquivo
|
|
474
|
+
// INFO: Incoming request { method: 'GET', url: '/api/users', duration: 245 }
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
- **Context preservation**: Mantém contexto através da aplicação
|
|
478
|
+
```typescript
|
|
479
|
+
// Contexto propagado automaticamente
|
|
480
|
+
const logger = createLogger().child('user-service');
|
|
481
|
+
// user-service: Processing user creation
|
|
482
|
+
// user-service: Database query completed
|
|
483
|
+
// user-service: User created successfully
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
- **Structured data**: Logs estruturados com metadados
|
|
487
|
+
```typescript
|
|
488
|
+
// Metadados automáticos em cada log
|
|
489
|
+
logger.info('User login', {
|
|
490
|
+
userId: 123,
|
|
491
|
+
ip: '192.168.1.1',
|
|
492
|
+
userAgent: 'Chrome/120.0',
|
|
493
|
+
timestamp: '2024-01-15T10:30:00Z',
|
|
494
|
+
requestId: 'req_abc123'
|
|
495
|
+
});
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
- **Performance tracking**: Tracking de performance automático
|
|
499
|
+
```typescript
|
|
500
|
+
// Timing automático de requests
|
|
501
|
+
const startTime = Date.now();
|
|
502
|
+
await apiClient.get('/heavy-endpoint');
|
|
503
|
+
// Log: Request completed { duration: 1247ms, status: 200 }
|
|
504
|
+
|
|
505
|
+
// Timing de hooks
|
|
506
|
+
// Log: Request processed { url: '/dashboard', duration: 89ms }
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
## Melhores Práticas
|
|
510
|
+
|
|
511
|
+
1. **Inicialize stores cedo** no ciclo de vida da aplicação
|
|
512
|
+
2. **Use stores reativas** em vez de gerenciamento manual de estado
|
|
513
|
+
3. **Configure hooks apropriadamente** para as necessidades da sua aplicação
|
|
514
|
+
4. **Use TypeScript** para melhor experiência de desenvolvimento
|
|
515
|
+
5. **Trate erros graciosamente** com os error stores
|
|
516
|
+
6. **Configure paths públicos** no hook de autenticação
|
|
517
|
+
7. **Use retry mechanism** para endpoints instáveis
|
|
518
|
+
8. **Implemente consent** desde o início para compliance
|
|
519
|
+
9. **Configure analytics** com consent awareness
|
|
520
|
+
10. **Use presets** de configuração para diferentes regiões
|
|
521
|
+
|
|
522
|
+
## Compatibilidade
|
|
523
|
+
|
|
524
|
+
- **Svelte 5.x** com runes support
|
|
525
|
+
- **SvelteKit 2.x** com app directory structure
|
|
526
|
+
- **Node.js 18+** para funcionalidades de servidor
|
|
527
|
+
- **TypeScript 5.x** para tipagem completa
|
|
528
|
+
|
|
529
|
+
## Dependencies
|
|
530
|
+
|
|
531
|
+
### Required
|
|
532
|
+
- `@groundbrick/logger` - Sistema de logging
|
|
533
|
+
|
|
534
|
+
### Peer Dependencies
|
|
535
|
+
- `@sveltejs/kit` - SvelteKit framework (^2.0.0)
|
|
536
|
+
- `svelte` - Svelte framework (^5.0.0)
|
|
537
|
+
|
|
538
|
+
## License
|
|
539
|
+
|
|
540
|
+
MIT
|
|
541
|
+
|
|
542
|
+
## Packaging & build notes
|
|
543
|
+
|
|
544
|
+
- During the package build we copy some source assets (for example, `.svelte` components) from `src/` into the published `dist/` folder. This is handled by the script `scripts/copy-assets.js` which runs after `tsc` in the package `build` script.
|
|
545
|
+
|
|
546
|
+
- Why: some modules are intentionally re-exported as source Svelte components (for example `analytics/components/GoogleAnalytics.svelte`). The compiled TypeScript output in `dist` contains runtime imports to those `.svelte` files. To make the package consumable by apps that will compile Svelte components in their own build (SvelteKit apps), we ship the `.svelte` files alongside the compiled JS so imports resolve correctly.
|
|
547
|
+
|
|
548
|
+
- Why not rsync: earlier we used `rsync` to copy files, but that requires `rsync` to be present on the build server. To make builds portable across environments we now use a small cross-platform Node script.
|
|
549
|
+
|
|
550
|
+
- Alternatives:
|
|
551
|
+
- Precompile Svelte components into JavaScript during the package build (using Rollup/Vite or `svelte/compiler`). This produces a distribution with JS only (no `.svelte` sources), which is cleaner for non-Svelte consumers but requires adding a Svelte compilation step to the package build.
|
|
552
|
+
- Keep shipping `.svelte` files (the current approach). This is the simplest and lets SvelteKit apps compile components during app build time.
|
|
553
|
+
|
|
554
|
+
- If you prefer a precompiled distribution, open an issue or request and we can add a Rollup/Vite build step (low-risk, slightly larger change).
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { browser } from '$app/environment';
|
|
3
|
+
import { page } from '$app/stores';
|
|
4
|
+
import { AnalyticsService } from '../services/AnalyticsService.js';
|
|
5
|
+
import type { AnalyticsConfig } from '../interfaces/IAnalyticsService.js';
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
measurementId: string;
|
|
9
|
+
debug?: boolean;
|
|
10
|
+
anonymizeIp?: boolean;
|
|
11
|
+
cookieDomain?: string;
|
|
12
|
+
cookieExpires?: number;
|
|
13
|
+
customParameters?: Record<string, any>;
|
|
14
|
+
trackPageViews?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let {
|
|
18
|
+
measurementId,
|
|
19
|
+
debug = false,
|
|
20
|
+
anonymizeIp = false,
|
|
21
|
+
cookieDomain,
|
|
22
|
+
cookieExpires,
|
|
23
|
+
customParameters,
|
|
24
|
+
trackPageViews = true,
|
|
25
|
+
}: Props = $props();
|
|
26
|
+
|
|
27
|
+
const analytics = AnalyticsService.getInstance();
|
|
28
|
+
let initialized = $state(false);
|
|
29
|
+
let isReady = $state(false);
|
|
30
|
+
|
|
31
|
+
// Initialize analytics service
|
|
32
|
+
$effect(() => {
|
|
33
|
+
if (browser && measurementId && !initialized) {
|
|
34
|
+
const config: AnalyticsConfig = {
|
|
35
|
+
measurementId,
|
|
36
|
+
debug,
|
|
37
|
+
anonymizeIp,
|
|
38
|
+
cookieDomain,
|
|
39
|
+
cookieExpires,
|
|
40
|
+
customParameters,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
analytics.initialize(config);
|
|
44
|
+
|
|
45
|
+
// Integração com o consent se disponível
|
|
46
|
+
try {
|
|
47
|
+
// @ts-ignore
|
|
48
|
+
import('@groundbrick/sveltekit-adapter/consent').then(({ ConsentService }) => {
|
|
49
|
+
const consentService = ConsentService.getInstance();
|
|
50
|
+
analytics.setConsentService(consentService);
|
|
51
|
+
}).catch(() => {
|
|
52
|
+
// Consent não disponível, continue sem ele
|
|
53
|
+
if (debug) {
|
|
54
|
+
console.log('[GoogleAnalytics] Consent system not available');
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
} catch {
|
|
58
|
+
// Ignore se consent não estiver disponível
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
initialized = true;
|
|
62
|
+
|
|
63
|
+
// Listen for when analytics is ready
|
|
64
|
+
const unsubscribe = analytics.onStateChange((state) => {
|
|
65
|
+
isReady = state.ready;
|
|
66
|
+
if (debug && state.ready) {
|
|
67
|
+
console.log("[GoogleAnalytics] Analytics is ready");
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
return unsubscribe;
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Track page views only when ready
|
|
76
|
+
$effect(() => {
|
|
77
|
+
if (browser && trackPageViews && isReady && $page.url) {
|
|
78
|
+
const url = $page.url.pathname + $page.url.search;
|
|
79
|
+
|
|
80
|
+
// Wait a bit longer to ensure everything is ready
|
|
81
|
+
setTimeout(() => {
|
|
82
|
+
if (analytics.isEnabled()) {
|
|
83
|
+
analytics.trackPageView({
|
|
84
|
+
page_path: url,
|
|
85
|
+
page_title: document.title,
|
|
86
|
+
page_location: window.location.href,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
if (debug) {
|
|
90
|
+
console.log("[GoogleAnalytics] Page view tracked:", url);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}, 300);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
</script>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { AnalyticsConfig } from '../interfaces/IAnalyticsService.js';
|
|
2
|
+
export declare const DEFAULT_ANALYTICS_CONFIG: Partial<AnalyticsConfig>;
|
|
3
|
+
export declare const GDPR_ANALYTICS_CONFIG: Partial<AnalyticsConfig>;
|
|
4
|
+
export declare const DEBUG_ANALYTICS_CONFIG: Partial<AnalyticsConfig>;
|
|
5
|
+
export declare const MINIMAL_ANALYTICS_CONFIG: Partial<AnalyticsConfig>;
|
|
6
|
+
//# sourceMappingURL=analytics.config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytics.config.d.ts","sourceRoot":"","sources":["../../../src/analytics/config/analytics.config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAE1E,eAAO,MAAM,wBAAwB,EAAE,OAAO,CAAC,eAAe,CAI7D,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,OAAO,CAAC,eAAe,CAQ1D,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,OAAO,CAAC,eAAe,CAG3D,CAAC;AAEF,eAAO,MAAM,wBAAwB,EAAE,OAAO,CAAC,eAAe,CAO7D,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export const DEFAULT_ANALYTICS_CONFIG = {
|
|
2
|
+
debug: false,
|
|
3
|
+
anonymizeIp: false,
|
|
4
|
+
cookieExpires: 63072000 // 2 years in seconds
|
|
5
|
+
};
|
|
6
|
+
export const GDPR_ANALYTICS_CONFIG = {
|
|
7
|
+
debug: false,
|
|
8
|
+
anonymizeIp: true,
|
|
9
|
+
cookieExpires: 31536000, // 1 year for GDPR compliance
|
|
10
|
+
customParameters: {
|
|
11
|
+
'allow_google_signals': false,
|
|
12
|
+
'allow_ad_personalization_signals': false
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
export const DEBUG_ANALYTICS_CONFIG = {
|
|
16
|
+
debug: true,
|
|
17
|
+
anonymizeIp: false
|
|
18
|
+
};
|
|
19
|
+
export const MINIMAL_ANALYTICS_CONFIG = {
|
|
20
|
+
debug: false,
|
|
21
|
+
anonymizeIp: true,
|
|
22
|
+
customParameters: {
|
|
23
|
+
'send_page_view': false, // Manual page view tracking only
|
|
24
|
+
'allow_google_signals': false
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=analytics.config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytics.config.js","sourceRoot":"","sources":["../../../src/analytics/config/analytics.config.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,wBAAwB,GAA6B;IAC9D,KAAK,EAAE,KAAK;IACZ,WAAW,EAAE,KAAK;IAClB,aAAa,EAAE,QAAQ,CAAC,qBAAqB;CAChD,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAA6B;IAC3D,KAAK,EAAE,KAAK;IACZ,WAAW,EAAE,IAAI;IACjB,aAAa,EAAE,QAAQ,EAAE,6BAA6B;IACtD,gBAAgB,EAAE;QACd,sBAAsB,EAAE,KAAK;QAC7B,kCAAkC,EAAE,KAAK;KAC5C;CACJ,CAAC;AAEF,MAAM,CAAC,MAAM,sBAAsB,GAA6B;IAC5D,KAAK,EAAE,IAAI;IACX,WAAW,EAAE,KAAK;CACrB,CAAC;AAEF,MAAM,CAAC,MAAM,wBAAwB,GAA6B;IAC9D,KAAK,EAAE,KAAK;IACZ,WAAW,EAAE,IAAI;IACjB,gBAAgB,EAAE;QACd,gBAAgB,EAAE,KAAK,EAAE,iCAAiC;QAC1D,sBAAsB,EAAE,KAAK;KAChC;CACJ,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { AnalyticsService } from './services/AnalyticsService.js';
|
|
2
|
+
export type { IAnalyticsService, AnalyticsConfig, AnalyticsState, GAEvent, GAPageView } from './interfaces/IAnalyticsService.js';
|
|
3
|
+
export { DEFAULT_ANALYTICS_CONFIG, GDPR_ANALYTICS_CONFIG, DEBUG_ANALYTICS_CONFIG, MINIMAL_ANALYTICS_CONFIG } from './config/analytics.config.js';
|
|
4
|
+
export { createConsentAwareAnalytics, createConsentWrapper, createConsentAwarePixel, AnalyticsEventBuilder, AnalyticsEvents } from './utils/consent.js';
|
|
5
|
+
export type { ConsentProvider, PixelConfig, PixelEvent } from './utils/consent.js';
|
|
6
|
+
export { default as GoogleAnalytics } from './components/GoogleAnalytics.svelte';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/analytics/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAGlE,YAAY,EACR,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,OAAO,EACP,UAAU,EACb,MAAM,mCAAmC,CAAC;AAG3C,OAAO,EACH,wBAAwB,EACxB,qBAAqB,EACrB,sBAAsB,EACtB,wBAAwB,EAC3B,MAAM,8BAA8B,CAAC;AAGtC,OAAO,EACH,2BAA2B,EAC3B,oBAAoB,EACpB,uBAAuB,EACvB,qBAAqB,EACrB,eAAe,EAClB,MAAM,oBAAoB,CAAC;AAE5B,YAAY,EACR,eAAe,EACf,WAAW,EACX,UAAU,EACb,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,qCAAqC,CAAC"}
|