@lokalise/fastify-extras 30.5.0 → 30.6.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 CHANGED
@@ -402,9 +402,11 @@ The plugin decorates your Fastify instance with a `Amplitude`, which you can inj
402
402
  > "@amplitude/analytics-types": "*"
403
403
  > ```
404
404
 
405
- Additionally, you have the option to enhance the safety and accuracy of your events and properties by wrapping your `Amplitude` instance with `AmplitudeAdapter`.
405
+ **Related utilities:**
406
406
 
407
- > 📘Check [`AmplitudeAdapter.spec.ts](./lib/plugins/amplitude/amplitudePlugin.spec.ts) for a practical example
407
+ - `AmplitudeAdapter` - A type-safe wrapper that validates events using Zod schemas before sending them to Amplitude
408
+ - `FakeAmplitude` - A utility class for testing environments that doesn't send events to Amplitude
409
+ See the [Amplitude utilities section](#amplitude) for detailed documentation and examples.
408
410
 
409
411
  ### Strip Trailing Slash Plugin
410
412
 
@@ -487,6 +489,119 @@ When an uncaught exception occurs, the plugin:
487
489
 
488
490
  ## Utilities
489
491
 
492
+ ### amplitude
493
+
494
+ #### FakeAmplitude
495
+
496
+ `FakeAmplitude` is a utility class that extends `Amplitude` but doesn't send any events to Amplitude. This is useful for
497
+ testing environments or when you want to disable event tracking without changing your application code.
498
+
499
+ Example usage:
500
+
501
+ ```typescript
502
+ import { FakeAmplitude } from '@lokalise/fastify-extras'
503
+
504
+ // In your test or development environment
505
+ const amplitude = new FakeAmplitude()
506
+
507
+ // track() will not send any events
508
+ amplitude.track({
509
+ event_type: 'button_clicked',
510
+ user_id: 'user-123',
511
+ })
512
+ ```
513
+
514
+ The `track()` method returns a promise that resolves to `null` immediately, maintaining the same interface as the real
515
+ `Amplitude` class without actually sending data.
516
+
517
+ #### AmplitudeAdapter
518
+
519
+ `AmplitudeAdapter` is a type-safe wrapper around `Amplitude` that uses Zod schemas to validate events before sending them.
520
+ This ensures that all events sent to Amplitude conform to predefined schemas, catching errors at compile-time and runtime.
521
+
522
+ **Key features:**
523
+
524
+ - Type-safe event tracking with TypeScript
525
+ - Automatic validation using Zod schemas
526
+ - Prevents sending malformed events to Amplitude
527
+
528
+ **Example usage:**
529
+
530
+ ```typescript
531
+ import { z } from 'zod'
532
+ import { AmplitudeAdapter, AMPLITUDE_BASE_MESSAGE_SCHEMA, Amplitude } from '@lokalise/fastify-extras'
533
+ import type { AmplitudeMessage } from '@lokalise/fastify-extras'
534
+
535
+ // Define your event schemas
536
+ const eventSchemas = {
537
+ buttonClicked: {
538
+ schema: AMPLITUDE_BASE_MESSAGE_SCHEMA.extend({
539
+ event_type: z.literal('button_clicked'),
540
+ event_properties: z.object({
541
+ button_id: z.string(),
542
+ page: z.string(),
543
+ }),
544
+ }),
545
+ },
546
+ userSignedUp: {
547
+ schema: AMPLITUDE_BASE_MESSAGE_SCHEMA.extend({
548
+ event_type: z.literal('user_signed_up'),
549
+ event_properties: z.object({
550
+ plan: z.enum(['free', 'premium']),
551
+ }),
552
+ }),
553
+ },
554
+ } as const satisfies Record<string, AmplitudeMessage>
555
+
556
+ const eventSchemaValues = Object.values(eventSchemas)
557
+ type SupportedEvents = typeof eventSchemaValues
558
+
559
+ // Create the adapter
560
+ const amplitude = new Amplitude(true)
561
+ const amplitudeAdapter = new AmplitudeAdapter<SupportedEvents>({ amplitude })
562
+
563
+ // Track events with type safety
564
+ amplitudeAdapter.track(eventSchemas.buttonClicked, {
565
+ user_id: 'user-123',
566
+ event_properties: {
567
+ button_id: 'submit-btn',
568
+ page: '/checkout',
569
+ },
570
+ })
571
+
572
+ // With groups
573
+ amplitudeAdapter.track(eventSchemas.userSignedUp, {
574
+ user_id: 'user-456',
575
+ groups: { company: 'acme-corp' },
576
+ event_properties: {
577
+ plan: 'premium',
578
+ },
579
+ })
580
+ ```
581
+
582
+ **Schema validation:**
583
+
584
+ The `AMPLITUDE_BASE_MESSAGE_SCHEMA` provides the base schema that all events must extend. It requires:
585
+
586
+ - `event_type`: A literal string identifying the event
587
+ - `user_id`: A non-empty string or the literal `'SYSTEM'`
588
+ - `groups` (optional): A record of group names to values
589
+
590
+ When defining custom events, extend this base schema with your specific `event_type` and `event_properties`:
591
+
592
+ ```typescript
593
+ const myEventSchema = AMPLITUDE_BASE_MESSAGE_SCHEMA.extend({
594
+ event_type: z.literal('my_custom_event'),
595
+ event_properties: z.object({
596
+ // Your custom properties with validation
597
+ count: z.number().int().positive(),
598
+ status: z.enum(['active', 'inactive']),
599
+ }),
600
+ })
601
+ ```
602
+
603
+ If validation fails, a `ZodError` will be thrown, preventing invalid data from being sent to Amplitude.
604
+
490
605
  ### route-utilities
491
606
 
492
607
  #### authPreHandlers
package/dist/index.d.ts CHANGED
@@ -29,6 +29,7 @@ export { commonSyncHealthcheckPlugin } from './plugins/healthcheck/commonSyncHea
29
29
  export type { CommonSyncHealthcheckPluginOptions } from './plugins/healthcheck/commonSyncHealthcheckPlugin.ts';
30
30
  export { amplitudePlugin, type AmplitudeConfig, type CreateApiTrackingEventFn, } from './plugins/amplitude/amplitudePlugin.js';
31
31
  export { Amplitude } from './plugins/amplitude/Amplitude.js';
32
+ export { FakeAmplitude } from './plugins/amplitude/FakeAmplitude.js';
32
33
  export { AmplitudeAdapter, AMPLITUDE_BASE_MESSAGE_SCHEMA, type AmplitudeMessage, type AmplitudeAdapterDependencies, } from './plugins/amplitude/AmplitudeAdapter.js';
33
34
  export type { FastifyReplyWithPayload } from './types.js';
34
35
  export { stripTrailingSlashPlugin } from './plugins/stripTrailingSlashPlugin.js';
package/dist/index.js CHANGED
@@ -15,6 +15,7 @@ export { startupHealthcheckPlugin } from './plugins/healthcheck/startupHealthche
15
15
  export { commonSyncHealthcheckPlugin } from "./plugins/healthcheck/commonSyncHealthcheckPlugin.js";
16
16
  export { amplitudePlugin, } from './plugins/amplitude/amplitudePlugin.js';
17
17
  export { Amplitude } from './plugins/amplitude/Amplitude.js';
18
+ export { FakeAmplitude } from './plugins/amplitude/FakeAmplitude.js';
18
19
  export { AmplitudeAdapter, AMPLITUDE_BASE_MESSAGE_SCHEMA, } from './plugins/amplitude/AmplitudeAdapter.js';
19
20
  export { stripTrailingSlashPlugin } from './plugins/stripTrailingSlashPlugin.js';
20
21
  export { unhandledExceptionPlugin, commonErrorObjectResolver, } from './plugins/unhandledExceptionPlugin.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,cAAc,GACf,MAAM,4BAA4B,CAAA;AAGnC,OAAO,EACL,4BAA4B,EAC5B,4BAA4B,GAC7B,MAAM,2CAA2C,CAAA;AAGlD,OAAO,EACL,gCAAgC,EAChC,0BAA0B,GAC3B,MAAM,+CAA+C,CAAA;AAGtD,OAAO,EACL,qCAAqC,EACrC,+BAA+B,GAChC,MAAM,oDAAoD,CAAA;AAG3D,OAAO,EACL,2BAA2B,EAC3B,qBAAqB,GACtB,MAAM,0CAA0C,CAAA;AAGjD,OAAO,EACL,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,mDAAmD,CAAA;AAO1D,OAAO,EAAE,mCAAmC,EAAE,MAAM,6DAA6D,CAAA;AAEjH,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAA;AAEtE,OAAO,EACL,yBAAyB,EACzB,kCAAkC,GACnC,MAAM,+CAA+C,CAAA;AAGtD,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAM1D,OAAO,EAAE,uBAAuB,EAAE,MAAM,kDAAkD,CAAA;AAO1F,OAAO,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAA;AAG7E,OAAO,EAAE,uBAAuB,EAAE,MAAM,kDAAkD,CAAA;AAG1F,OAAO,EAAE,wBAAwB,EAAE,MAAM,mDAAmD,CAAA;AAG5F,OAAO,EAAE,2BAA2B,EAAE,MAAM,sDAAsD,CAAA;AAGlG,OAAO,EACL,eAAe,GAGhB,MAAM,wCAAwC,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAA;AAC5D,OAAO,EACL,gBAAgB,EAChB,6BAA6B,GAG9B,MAAM,yCAAyC,CAAA;AAIhD,OAAO,EAAE,wBAAwB,EAAE,MAAM,uCAAuC,CAAA;AAEhF,OAAO,EACL,wBAAwB,EACxB,yBAAyB,GAC1B,MAAM,uCAAuC,CAAA;AAG9C,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAA4B,MAAM,0BAA0B,CAAA;AAGnG,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAE5E,OAAO,EAAE,+BAA+B,EAAE,MAAM,kCAAkC,CAAA;AAClF,OAAO,EACL,0BAA0B,GAG3B,MAAM,4BAA4B,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,cAAc,GACf,MAAM,4BAA4B,CAAA;AAGnC,OAAO,EACL,4BAA4B,EAC5B,4BAA4B,GAC7B,MAAM,2CAA2C,CAAA;AAGlD,OAAO,EACL,gCAAgC,EAChC,0BAA0B,GAC3B,MAAM,+CAA+C,CAAA;AAGtD,OAAO,EACL,qCAAqC,EACrC,+BAA+B,GAChC,MAAM,oDAAoD,CAAA;AAG3D,OAAO,EACL,2BAA2B,EAC3B,qBAAqB,GACtB,MAAM,0CAA0C,CAAA;AAGjD,OAAO,EACL,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,mDAAmD,CAAA;AAO1D,OAAO,EAAE,mCAAmC,EAAE,MAAM,6DAA6D,CAAA;AAEjH,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAA;AAEtE,OAAO,EACL,yBAAyB,EACzB,kCAAkC,GACnC,MAAM,+CAA+C,CAAA;AAGtD,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAM1D,OAAO,EAAE,uBAAuB,EAAE,MAAM,kDAAkD,CAAA;AAO1F,OAAO,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAA;AAG7E,OAAO,EAAE,uBAAuB,EAAE,MAAM,kDAAkD,CAAA;AAG1F,OAAO,EAAE,wBAAwB,EAAE,MAAM,mDAAmD,CAAA;AAG5F,OAAO,EAAE,2BAA2B,EAAE,MAAM,sDAAsD,CAAA;AAGlG,OAAO,EACL,eAAe,GAGhB,MAAM,wCAAwC,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAA;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,sCAAsC,CAAA;AACpE,OAAO,EACL,gBAAgB,EAChB,6BAA6B,GAG9B,MAAM,yCAAyC,CAAA;AAIhD,OAAO,EAAE,wBAAwB,EAAE,MAAM,uCAAuC,CAAA;AAEhF,OAAO,EACL,wBAAwB,EACxB,yBAAyB,GAC1B,MAAM,uCAAuC,CAAA;AAG9C,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAA4B,MAAM,0BAA0B,CAAA;AAGnG,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAE5E,OAAO,EAAE,+BAA+B,EAAE,MAAM,kCAAkC,CAAA;AAClF,OAAO,EACL,0BAA0B,GAG3B,MAAM,4BAA4B,CAAA"}
@@ -0,0 +1,7 @@
1
+ import type { AmplitudeReturn, Result } from '@amplitude/analytics-types';
2
+ import type { BaseEvent } from '@amplitude/analytics-types/lib/esm/base-event.d.ts';
3
+ import { Amplitude } from './Amplitude.ts';
4
+ export declare class FakeAmplitude extends Amplitude {
5
+ constructor();
6
+ track(_: BaseEvent): AmplitudeReturn<Result | null>;
7
+ }
@@ -0,0 +1,12 @@
1
+ import { Amplitude } from "./Amplitude.js";
2
+ export class FakeAmplitude extends Amplitude {
3
+ constructor() {
4
+ super(false);
5
+ }
6
+ track(_) {
7
+ return {
8
+ promise: Promise.resolve(null),
9
+ };
10
+ }
11
+ }
12
+ //# sourceMappingURL=FakeAmplitude.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FakeAmplitude.js","sourceRoot":"","sources":["../../../lib/plugins/amplitude/FakeAmplitude.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE1C,MAAM,OAAO,aAAc,SAAQ,SAAS;IAC1C;QACE,KAAK,CAAC,KAAK,CAAC,CAAA;IACd,CAAC;IAEQ,KAAK,CAAC,CAAY;QACzB,OAAO;YACL,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;SAC/B,CAAA;IACH,CAAC;CACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lokalise/fastify-extras",
3
- "version": "30.5.0",
3
+ "version": "30.6.0",
4
4
  "description": "Opinionated set of fastify plugins, commonly used in Lokalise",
5
5
  "author": {
6
6
  "name": "Lokalise",