@linkforty/core 1.4.4 → 1.6.6

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 (39) hide show
  1. package/LICENSE +620 -21
  2. package/README.md +306 -128
  3. package/dist/index.d.ts +1 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +3 -1
  6. package/dist/index.js.map +1 -1
  7. package/dist/lib/database.d.ts.map +1 -1
  8. package/dist/lib/database.js +25 -7
  9. package/dist/lib/database.js.map +1 -1
  10. package/dist/lib/event-emitter.d.ts +1 -1
  11. package/dist/lib/event-emitter.d.ts.map +1 -1
  12. package/dist/lib/fingerprint.js +4 -4
  13. package/dist/lib/fingerprint.js.map +1 -1
  14. package/dist/routes/analytics.d.ts.map +1 -1
  15. package/dist/routes/analytics.js +22 -19
  16. package/dist/routes/analytics.js.map +1 -1
  17. package/dist/routes/debug.d.ts.map +1 -1
  18. package/dist/routes/debug.js +17 -19
  19. package/dist/routes/debug.js.map +1 -1
  20. package/dist/routes/index.d.ts +1 -0
  21. package/dist/routes/index.d.ts.map +1 -1
  22. package/dist/routes/index.js +1 -0
  23. package/dist/routes/index.js.map +1 -1
  24. package/dist/routes/links.d.ts.map +1 -1
  25. package/dist/routes/links.js +75 -38
  26. package/dist/routes/links.js.map +1 -1
  27. package/dist/routes/sdk.js +4 -4
  28. package/dist/routes/sdk.js.map +1 -1
  29. package/dist/routes/templates.d.ts +3 -0
  30. package/dist/routes/templates.d.ts.map +1 -0
  31. package/dist/routes/templates.js +261 -0
  32. package/dist/routes/templates.js.map +1 -0
  33. package/dist/routes/webhooks.d.ts.map +1 -1
  34. package/dist/routes/webhooks.js +40 -24
  35. package/dist/routes/webhooks.js.map +1 -1
  36. package/dist/types/index.d.ts +29 -26
  37. package/dist/types/index.d.ts.map +1 -1
  38. package/llms.txt +763 -0
  39. package/package.json +17 -4
package/README.md CHANGED
@@ -3,9 +3,9 @@
3
3
 
4
4
  # LinkForty Core
5
5
 
6
- **Open-source deeplink management engine with device detection and analytics**
6
+ **Open-source alternative to Branch.io, AppsFlyer OneLink, and Firebase Dynamic Links**
7
7
 
8
- LinkForty Core is a powerful, self-hosted deeplink management system that enables you to create, manage, and track smart links with device-specific routing, analytics, and UTM parameter support. It's the open-source foundation of the LinkForty platform.
8
+ Self-hosted deep linking engine with device detection, analytics, deferred deep linking, and smart routing. No per-click pricing, no vendor lock-in, full data ownership — runs on your own PostgreSQL. Firebase Dynamic Links shut down in August 2025; LinkForty is a production-ready, open-source replacement you can deploy today.
9
9
  </div>
10
10
 
11
11
  [![npm version](https://img.shields.io/npm/v/@linkforty/core.svg)](https://www.npmjs.com/package/@linkforty/core)
@@ -13,18 +13,52 @@
13
13
  [![codecov](https://codecov.io/gh/linkforty/core/branch/main/graph/badge.svg)](https://codecov.io/gh/linkforty/core)
14
14
  [![Docker Pulls](https://img.shields.io/docker/pulls/linkforty/core)](https://hub.docker.com/r/linkforty/core)
15
15
  [![Docker Image Size](https://img.shields.io/docker/image-size/linkforty/core/latest)](https://hub.docker.com/r/linkforty/core)
16
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
17
-
18
- ## Features
19
-
20
- **Smart Link Routing** - Create short links with device-specific URLs for iOS, Android, and web \
21
- **Device Detection** - Automatic detection and routing based on user device \
22
- **Click Analytics** - Track clicks with geolocation, device type, platform, and more \
23
- **UTM Parameters** - Built-in support for UTM campaign tracking \
24
- **Link Expiration** - Set expiration dates for time-sensitive links \
25
- **Redis Caching** - Optional Redis support for high-performance link lookups \
26
- ✅ **PostgreSQL Storage** - Reliable data persistence with full SQL capabilities \
27
- **TypeScript** - Fully typed API for better developer experience
16
+ [![License: AGPL v3](https://img.shields.io/badge/License-AGPL_v3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0)
17
+
18
+ ## Why LinkForty?
19
+
20
+ - **Self-hosted and open-source** AGPL-3.0 licensed, deploy on your own infrastructure
21
+ - **No per-click pricing** — No usage-based fees, no monthly minimums, no enterprise sales calls
22
+ - **Full data ownership** All click data, analytics, and attribution stored in your PostgreSQL database
23
+ - **Privacy-first** No third-party data sharing, no tracking pixels, your users' data stays with you
24
+ - **Drop-in replacement** REST API + mobile SDKs for React Native, Expo, iOS (Swift), and Android (Kotlin)
25
+ - **Firebase Dynamic Links replacement** Google shut down Firebase Dynamic Links in August 2025. LinkForty provides the same capabilities with a self-hosted, open-source stack
26
+
27
+ ### How LinkForty Compares
28
+
29
+ | Feature | LinkForty Core | Branch | AppsFlyer | Firebase Dynamic Links |
30
+ |---------|---------------|--------|-----------|----------------------|
31
+ | **Pricing** | Free (self-hosted) | Starts at $299/mo | Starts at $500/mo | Shut down (Aug 2025) |
32
+ | **Open Source** | Yes (AGPL-3.0) | No | No | No |
33
+ | **Self-Hosted** | Yes | No | No | No |
34
+ | **Data Ownership** | Complete | Vendor-controlled | Vendor-controlled | Was Google-controlled |
35
+ | **Deferred Deep Linking** | Yes | Yes | Yes | Was supported |
36
+ | **Device Detection & Routing** | Yes | Yes | Yes | Was supported |
37
+ | **Click Analytics** | Yes | Yes | Yes | Basic |
38
+ | **QR Code Generation** | Built-in | No | No | No |
39
+ | **Webhooks** | Yes | Enterprise only | Enterprise only | No |
40
+ | **iOS Universal Links** | Yes | Yes | Yes | Was supported |
41
+ | **Android App Links** | Yes | Yes | Yes | Was supported |
42
+ | **UTM Parameter Tracking** | Yes | Yes | Custom params | Was supported |
43
+ | **Custom Domains** | Yes | Enterprise only | Enterprise only | No |
44
+
45
+ ## Features
46
+
47
+ **Smart Link Routing** - Create short links with device-specific URLs for iOS, Android, and web \
48
+ **Device Detection** - Automatic detection and routing based on user device \
49
+ **Click Analytics** - Track clicks with geolocation, device type, platform, and more \
50
+ **UTM Parameters** - Built-in support for UTM campaign tracking \
51
+ **Targeting Rules** - Filter by country, device, and language before redirecting \
52
+ **QR Code Generation** - Generate QR codes (PNG/SVG) for any link \
53
+ **Deferred Deep Linking** - Probabilistic fingerprint matching for install attribution \
54
+ **Webhooks** - Event-driven integrations with HMAC-signed payloads and retry logic \
55
+ **OG Preview Pages** - Social media scraper detection with Open Graph meta tags \
56
+ **iOS Universal Links & Android App Links** - Serve `.well-known` files automatically \
57
+ **Link Expiration** - Set expiration dates for time-sensitive links \
58
+ **Redis Caching** - Optional Redis support for high-performance link lookups \
59
+ **PostgreSQL Storage** - Reliable data persistence with full SQL capabilities \
60
+ **TypeScript** - Fully typed API for better developer experience \
61
+ **No Auth Included** - Bring your own authentication; `userId` is optional for multi-tenant scoping
28
62
 
29
63
  ## Installation
30
64
 
@@ -81,17 +115,21 @@ docker run -d \
81
115
  ```
82
116
 
83
117
  **Features:**
84
- - Pre-built multi-architecture images (AMD64 + ARM64)
85
- - Automatic updates with version tags
86
- - Non-root user for security
87
- - Built-in health checks
88
- - Supply chain attestations (SBOM + Provenance)
118
+ - Pre-built multi-architecture images (AMD64 + ARM64)
119
+ - Automatic updates with version tags
120
+ - Non-root user for security
121
+ - Built-in health checks
122
+ - Supply chain attestations (SBOM + Provenance)
89
123
 
90
124
  See [DOCKER.md](DOCKER.md) for complete deployment guide.
91
125
 
92
126
  ## API Reference
93
127
 
94
- ### Create a Link
128
+ ### Links
129
+
130
+ #### Create a Link
131
+
132
+ `userId` is optional. When provided, the link is scoped to that user (multi-tenant mode). When omitted, the link has no owner (single-tenant mode).
95
133
 
96
134
  ```bash
97
135
  POST /api/links
@@ -101,57 +139,54 @@ Content-Type: application/json
101
139
  "userId": "user-uuid",
102
140
  "originalUrl": "https://example.com",
103
141
  "title": "My Link",
104
- "iosUrl": "myapp://product/123",
105
- "androidUrl": "myapp://product/123",
142
+ "description": "Summer campaign link",
143
+ "iosAppStoreUrl": "https://apps.apple.com/app/id123456",
144
+ "androidAppStoreUrl": "https://play.google.com/store/apps/details?id=com.example",
106
145
  "webFallbackUrl": "https://example.com/product/123",
146
+ "appScheme": "myapp",
147
+ "iosUniversalLink": "https://example.com/app/product/123",
148
+ "androidAppLink": "https://example.com/app/product/123",
149
+ "deepLinkPath": "/product/123",
150
+ "deepLinkParameters": { "ref": "campaign-1" },
107
151
  "utmParameters": {
108
152
  "source": "twitter",
109
153
  "medium": "social",
110
154
  "campaign": "summer-sale"
111
155
  },
156
+ "ogTitle": "Check out this deal",
157
+ "ogDescription": "50% off summer sale",
158
+ "ogImageUrl": "https://example.com/og-image.png",
159
+ "targetingRules": {
160
+ "countries": ["US", "CA"],
161
+ "devices": ["ios", "android"],
162
+ "languages": ["en"]
163
+ },
164
+ "attributionWindowHours": 168,
112
165
  "customCode": "summer-sale",
113
- "expiresAt": "2024-12-31T23:59:59Z"
166
+ "expiresAt": "2026-12-31T23:59:59Z"
114
167
  }
115
168
  ```
116
169
 
117
- **Response:**
170
+ All fields except `originalUrl` are optional.
118
171
 
119
- ```json
120
- {
121
- "id": "link-uuid",
122
- "userId": "user-uuid",
123
- "short_code": "summer-sale",
124
- "original_url": "https://example.com",
125
- "title": "My Link",
126
- "ios_url": "myapp://product/123",
127
- "android_url": "myapp://product/123",
128
- "web_fallback_url": "https://example.com/product/123",
129
- "utmParameters": {
130
- "source": "twitter",
131
- "medium": "social",
132
- "campaign": "summer-sale"
133
- },
134
- "is_active": true,
135
- "expires_at": "2024-12-31T23:59:59Z",
136
- "created_at": "2024-01-01T00:00:00Z",
137
- "updated_at": "2024-01-01T00:00:00Z",
138
- "clickCount": 0
139
- }
140
- ```
141
-
142
- ### Get All Links
172
+ #### Get All Links
143
173
 
144
174
  ```bash
175
+ # Single-tenant (all links)
176
+ GET /api/links
177
+
178
+ # Multi-tenant (scoped to user)
145
179
  GET /api/links?userId=user-uuid
146
180
  ```
147
181
 
148
- ### Get a Specific Link
182
+ #### Get a Specific Link
149
183
 
150
184
  ```bash
185
+ GET /api/links/:id
151
186
  GET /api/links/:id?userId=user-uuid
152
187
  ```
153
188
 
154
- ### Update a Link
189
+ #### Update a Link
155
190
 
156
191
  ```bash
157
192
  PUT /api/links/:id?userId=user-uuid
@@ -163,62 +198,99 @@ Content-Type: application/json
163
198
  }
164
199
  ```
165
200
 
166
- ### Delete a Link
201
+ #### Duplicate a Link
202
+
203
+ ```bash
204
+ POST /api/links/:id/duplicate?userId=user-uuid
205
+ ```
206
+
207
+ #### Delete a Link
167
208
 
168
209
  ```bash
169
210
  DELETE /api/links/:id?userId=user-uuid
170
211
  ```
171
212
 
172
- ### Get Analytics Overview
213
+ ### Analytics
214
+
215
+ #### Get Analytics Overview
173
216
 
174
217
  ```bash
218
+ # All links
219
+ GET /api/analytics/overview?days=30
220
+
221
+ # Scoped to user
175
222
  GET /api/analytics/overview?userId=user-uuid&days=30
176
223
  ```
177
224
 
178
- **Response:**
225
+ Returns: `totalClicks`, `uniqueClicks`, `clicksByDate`, `clicksByCountry`, `clicksByDevice`, `clicksByPlatform`, `topLinks`
179
226
 
180
- ```json
181
- {
182
- "totalClicks": 1234,
183
- "uniqueClicks": 567,
184
- "clicksByDate": [
185
- { "date": "2024-01-01", "clicks": 45 }
186
- ],
187
- "clicksByCountry": [
188
- { "countryCode": "US", "country": "United States", "clicks": 234 }
189
- ],
190
- "clicksByDevice": [
191
- { "device": "mobile", "clicks": 789 }
192
- ],
193
- "clicksByPlatform": [
194
- { "platform": "iOS", "clicks": 456 }
195
- ],
196
- "topLinks": [
197
- {
198
- "id": "link-uuid",
199
- "shortCode": "summer-sale",
200
- "title": "My Link",
201
- "originalUrl": "https://example.com",
202
- "totalClicks": 123,
203
- "uniqueClicks": 67
204
- }
205
- ]
206
- }
227
+ #### Get Link-Specific Analytics
228
+
229
+ ```bash
230
+ GET /api/analytics/links/:linkId?days=30
207
231
  ```
208
232
 
209
- ### Get Link-Specific Analytics
233
+ ### Redirect
210
234
 
211
235
  ```bash
212
- GET /api/analytics/links/:linkId?userId=user-uuid&days=30
236
+ GET /:shortCode
237
+ GET /:templateSlug/:shortCode
213
238
  ```
214
239
 
215
- ### Redirect Short Link
240
+ Automatically redirects users to the appropriate URL based on device type (iOS/Android/web), evaluates targeting rules, and tracks the click asynchronously.
241
+
242
+ ### QR Codes
216
243
 
217
244
  ```bash
218
- GET /:shortCode
245
+ GET /api/links/:id/qr?format=png&size=300
246
+ GET /api/links/:id/qr?format=svg
247
+ ```
248
+
249
+ ### Webhooks
250
+
251
+ ```bash
252
+ GET /api/webhooks?userId=user-uuid
253
+ POST /api/webhooks # Body: { name, url, events, userId? }
254
+ GET /api/webhooks/:id?userId=user-uuid
255
+ PUT /api/webhooks/:id?userId=user-uuid
256
+ DELETE /api/webhooks/:id?userId=user-uuid
257
+ POST /api/webhooks/:id/test?userId=user-uuid
219
258
  ```
220
259
 
221
- This endpoint automatically redirects users to the appropriate URL based on their device type.
260
+ Events: `click_event`, `install_event`, `conversion_event`. Payloads are HMAC SHA-256 signed.
261
+
262
+ ### Mobile SDK Endpoints
263
+
264
+ ```bash
265
+ POST /api/sdk/v1/install # Report app install, get deferred deep link
266
+ GET /api/sdk/v1/attribution/:fingerprint # Debug attribution lookups
267
+ POST /api/sdk/v1/event # Track in-app conversion events
268
+ GET /api/sdk/v1/resolve/:shortCode # Resolve link to deep link data (no redirect)
269
+ GET /api/sdk/v1/health # Health check
270
+ ```
271
+
272
+ ### Debug & Testing
273
+
274
+ ```bash
275
+ POST /api/debug/simulate # Simulate a link click with custom parameters
276
+ WS /api/debug/live?userId=user-uuid # WebSocket live click event stream
277
+ GET /api/debug/user-agents # Common UA strings for testing
278
+ GET /api/debug/countries # Common countries list
279
+ GET /api/debug/languages # Common languages list
280
+ ```
281
+
282
+ ### Well-Known Routes
283
+
284
+ ```bash
285
+ GET /.well-known/apple-app-site-association # iOS Universal Links
286
+ GET /.well-known/assetlinks.json # Android App Links
287
+ ```
288
+
289
+ ### OG Preview
290
+
291
+ ```bash
292
+ GET /:shortCode/preview # OG meta tag page for social scrapers
293
+ ```
222
294
 
223
295
  ## Configuration
224
296
 
@@ -251,39 +323,50 @@ REDIS_URL=redis://localhost:6379
251
323
  PORT=3000
252
324
  NODE_ENV=production
253
325
  CORS_ORIGIN=*
326
+
327
+ # Mobile SDK (optional — for iOS Universal Links and Android App Links)
328
+ IOS_TEAM_ID=ABC123XYZ
329
+ IOS_BUNDLE_ID=com.yourcompany.yourapp
330
+ ANDROID_PACKAGE_NAME=com.yourcompany.yourapp
331
+ ANDROID_SHA256_FINGERPRINTS=AA:BB:CC:DD:...
332
+
333
+ # Custom domain for QR code URLs (optional)
334
+ SHORTLINK_DOMAIN=yourdomain.com
254
335
  ```
255
336
 
256
337
  ## Database Schema
257
338
 
258
- ### Users Table
259
-
260
- | Column | Type | Description |
261
- |---------------|--------------|-----------------------|
262
- | id | UUID | Primary key |
263
- | email | VARCHAR(255) | Unique email |
264
- | name | VARCHAR(255) | User name |
265
- | password_hash | VARCHAR(255) | Hashed password |
266
- | created_at | TIMESTAMP | Creation timestamp |
267
- | updated_at | TIMESTAMP | Last update timestamp |
339
+ Core does not create a `users` table. Authentication and user management are the consumer's responsibility. The `user_id` column on `links` and `webhooks` is optional (nullable, no foreign key) — use it for multi-tenant scoping when your auth layer provides a user identity.
268
340
 
269
341
  ### Links Table
270
342
 
271
- | Column | Type | Description |
272
- |------------------|--------------|-----------------------|
273
- | id | UUID | Primary key |
274
- | user_id | UUID | Foreign key to users |
275
- | short_code | VARCHAR(20) | Unique short code |
276
- | original_url | TEXT | Original URL |
277
- | title | VARCHAR(255) | Link title |
278
- | ios_url | TEXT | iOS-specific URL |
279
- | android_url | TEXT | Android-specific URL |
280
- | web_fallback_url | TEXT | Web fallback URL |
281
- | utm_parameters | JSONB | UTM parameters |
282
- | targeting_rules | JSONB | Targeting rules |
283
- | is_active | BOOLEAN | Active status |
284
- | expires_at | TIMESTAMP | Expiration date |
285
- | created_at | TIMESTAMP | Creation timestamp |
286
- | updated_at | TIMESTAMP | Last update timestamp |
343
+ | Column | Type | Description |
344
+ |-------------------------|--------------|------------------------------------------|
345
+ | id | UUID | Primary key |
346
+ | user_id | UUID | Optional owner/tenant identifier |
347
+ | short_code | VARCHAR(20) | Unique short code |
348
+ | original_url | TEXT | Original URL |
349
+ | title | VARCHAR(255) | Link title |
350
+ | description | TEXT | Link description |
351
+ | ios_app_store_url | TEXT | iOS App Store URL |
352
+ | android_app_store_url | TEXT | Android Play Store URL |
353
+ | web_fallback_url | TEXT | Web fallback URL |
354
+ | app_scheme | VARCHAR(255) | URI scheme (e.g., "myapp") |
355
+ | ios_universal_link | TEXT | iOS Universal Link URL |
356
+ | android_app_link | TEXT | Android App Link URL |
357
+ | deep_link_path | TEXT | In-app destination path |
358
+ | deep_link_parameters | JSONB | Custom app parameters |
359
+ | utm_parameters | JSONB | UTM tracking parameters |
360
+ | targeting_rules | JSONB | Country/device/language targeting |
361
+ | og_title | VARCHAR(255) | Open Graph title |
362
+ | og_description | TEXT | Open Graph description |
363
+ | og_image_url | TEXT | Open Graph image URL |
364
+ | og_type | VARCHAR(50) | Open Graph type (default: "website") |
365
+ | attribution_window_hours| INTEGER | Install attribution window (default: 168)|
366
+ | is_active | BOOLEAN | Active status |
367
+ | expires_at | TIMESTAMP | Expiration date |
368
+ | created_at | TIMESTAMP | Creation timestamp |
369
+ | updated_at | TIMESTAMP | Last update timestamp |
287
370
 
288
371
  ### Click Events Table
289
372
 
@@ -294,7 +377,7 @@ CORS_ORIGIN=*
294
377
  | clicked_at | TIMESTAMP | Click timestamp |
295
378
  | ip_address | INET | User IP address |
296
379
  | user_agent | TEXT | User agent string |
297
- | device_type | VARCHAR(20) | Device type (mobile/desktop) |
380
+ | device_type | VARCHAR(20) | Device type (ios/android/web)|
298
381
  | platform | VARCHAR(20) | Platform (iOS/Android/Web) |
299
382
  | country_code | CHAR(2) | Country code |
300
383
  | country_name | VARCHAR(100) | Country name |
@@ -308,6 +391,68 @@ CORS_ORIGIN=*
308
391
  | utm_campaign | VARCHAR(255) | UTM campaign |
309
392
  | referrer | TEXT | Referrer URL |
310
393
 
394
+ ### Device Fingerprints Table
395
+
396
+ | Column | Type | Description |
397
+ |------------------|-------------|-------------------------------------|
398
+ | id | UUID | Primary key |
399
+ | click_id | UUID | Foreign key to click_events |
400
+ | fingerprint_hash | VARCHAR(64) | SHA-256 hash of fingerprint signals |
401
+ | ip_address | INET | IP address |
402
+ | user_agent | TEXT | User agent string |
403
+ | timezone | VARCHAR(100)| Timezone |
404
+ | language | VARCHAR(10) | Browser language |
405
+ | screen_width | INTEGER | Screen width |
406
+ | screen_height | INTEGER | Screen height |
407
+ | platform | VARCHAR(50) | Platform |
408
+ | platform_version | VARCHAR(50) | Platform version |
409
+ | created_at | TIMESTAMP | Creation timestamp |
410
+
411
+ ### Install Events Table
412
+
413
+ | Column | Type | Description |
414
+ |-------------------------|-------------|------------------------------------|
415
+ | id | UUID | Primary key |
416
+ | link_id | UUID | Attributed link (nullable) |
417
+ | click_id | UUID | Attributed click (nullable) |
418
+ | fingerprint_hash | VARCHAR(64) | Device fingerprint hash |
419
+ | confidence_score | DECIMAL | Match confidence (0-100) |
420
+ | installed_at | TIMESTAMP | Install timestamp |
421
+ | first_open_at | TIMESTAMP | First app open |
422
+ | deep_link_retrieved | BOOLEAN | Whether deferred link was fetched |
423
+ | deep_link_data | JSONB | Deferred deep link data |
424
+ | attribution_window_hours| INTEGER | Attribution window used (default: 168) |
425
+ | device_id | VARCHAR(255)| Optional device identifier |
426
+ | created_at | TIMESTAMP | Creation timestamp |
427
+
428
+ ### In-App Events Table
429
+
430
+ | Column | Type | Description |
431
+ |-----------------|--------------|------------------------------|
432
+ | id | UUID | Primary key |
433
+ | install_id | UUID | Foreign key to install_events|
434
+ | event_name | VARCHAR(255) | Event name |
435
+ | event_data | JSONB | Custom event properties |
436
+ | event_timestamp | TIMESTAMP | When the event occurred |
437
+ | created_at | TIMESTAMP | Creation timestamp |
438
+
439
+ ### Webhooks Table
440
+
441
+ | Column | Type | Description |
442
+ |------------|--------------|----------------------------------|
443
+ | id | UUID | Primary key |
444
+ | user_id | UUID | Optional owner/tenant identifier |
445
+ | name | VARCHAR(255) | Webhook name |
446
+ | url | TEXT | Delivery URL |
447
+ | secret | VARCHAR(255) | HMAC signing secret |
448
+ | events | TEXT[] | Subscribed event types |
449
+ | is_active | BOOLEAN | Active status |
450
+ | retry_count| INTEGER | Max retries (default: 3) |
451
+ | timeout_ms | INTEGER | Request timeout (default: 10000) |
452
+ | headers | JSONB | Custom HTTP headers |
453
+ | created_at | TIMESTAMP | Creation timestamp |
454
+ | updated_at | TIMESTAMP | Last update timestamp |
455
+
311
456
  ## Utilities
312
457
 
313
458
  ### Generate Short Code
@@ -389,7 +534,7 @@ await fastify.listen({ port: 3000 });
389
534
 
390
535
  LinkForty can be deployed in multiple ways depending on your needs:
391
536
 
392
- ### 🚀 Production Deployment (Recommended)
537
+ ### Production Deployment (Recommended)
393
538
 
394
539
  Deploy to managed platforms with minimal DevOps overhead:
395
540
 
@@ -399,11 +544,11 @@ Deploy to managed platforms with minimal DevOps overhead:
399
544
  - Auto-scaling and SSL included
400
545
  - Starting at ~$10-15/month
401
546
 
402
- [View Fly.io deployment guide](infra/fly.io/DEPLOYMENT.md)
547
+ [View Fly.io deployment guide](infra/fly.io/DEPLOYMENT.md)
403
548
 
404
549
  See [`infra/`](infra/) directory for all deployment options and platform-specific guides.
405
550
 
406
- ### 🐳 Docker Deployment (Recommended for Self-Hosting)
551
+ ### Docker Deployment (Recommended for Self-Hosting)
407
552
 
408
553
  **Production-ready Docker images available on Docker Hub:**
409
554
 
@@ -424,7 +569,7 @@ docker compose up -d
424
569
  ```yaml
425
570
  services:
426
571
  linkforty:
427
- image: linkforty/core:v1.4.0 # Pin to specific version
572
+ image: linkforty/core:v1.5.0 # Pin to specific version
428
573
  ```
429
574
 
430
575
  See [DOCKER.md](DOCKER.md) for complete deployment guide including:
@@ -456,8 +601,8 @@ See [`infra/CONTRIBUTING.md`](infra/CONTRIBUTING.md) to add support for addition
456
601
 
457
602
  - **Redis caching**: 5-minute TTL on link lookups reduces database queries by 90%
458
603
  - **Database indexes**: Optimized queries for fast link lookups and analytics
459
- - **Async click tracking**: Non-blocking click event logging
460
- - **Connection pooling**: Efficient database connection management
604
+ - **Async click tracking**: Non-blocking click event logging via `setImmediate()`
605
+ - **Connection pooling**: Efficient database connection management (min 2, max 10)
461
606
 
462
607
  ## Security
463
608
 
@@ -465,6 +610,8 @@ See [`infra/CONTRIBUTING.md`](infra/CONTRIBUTING.md) to add support for addition
465
610
  - **Input validation**: Zod schema validation on all inputs
466
611
  - **CORS configuration**: Configurable CORS for API access control
467
612
  - **Link expiration**: Automatic handling of expired links
613
+ - **Webhook signing**: HMAC SHA-256 signed payloads
614
+ - **No auth included**: Core does not include authentication. The optional `userId` parameter provides data scoping but does not verify identity. Add your own auth middleware as needed.
468
615
 
469
616
  ## Mobile SDK Integration
470
617
 
@@ -556,12 +703,14 @@ LinkForty Core supports iOS Universal Links and Android App Links for seamless d
556
703
 
557
704
  ### Available Mobile SDKs
558
705
 
559
- - **React Native**: `npm install @linkforty/react-native-sdk`
560
- - **iOS**: Coming soon
561
- - **Android**: Coming soon
562
- - **Flutter**: Coming soon
706
+ | Platform | Package | Install |
707
+ |----------|---------|---------|
708
+ | React Native | [`@linkforty/mobile-sdk-react-native`](https://github.com/LinkForty/mobile-sdk-react-native) | `npm install @linkforty/mobile-sdk-react-native` |
709
+ | Expo | [`@linkforty/mobile-sdk-expo`](https://github.com/LinkForty/mobile-sdk-expo) | `npx expo install @linkforty/mobile-sdk-expo` |
710
+ | iOS (Swift) | [LinkFortySDK](https://github.com/LinkForty/mobile-sdk-ios) | Swift Package Manager |
711
+ | Android (Kotlin) | [LinkFortySDK](https://github.com/LinkForty/mobile-sdk-android) | Gradle dependency |
563
712
 
564
- See [SDK Integration Guide](https://docs.linkforty.com/guides/sdk-integration) for detailed documentation.
713
+ See the [SDK documentation](https://docs.linkforty.com/sdks/react-native) for integration guides.
565
714
 
566
715
  ### Testing Domain Verification
567
716
 
@@ -577,18 +726,47 @@ adb shell am start -a android.intent.action.VIEW \
577
726
  ```
578
727
 
579
728
 
729
+ ## Migrate from Another Platform
730
+
731
+ Switching from an existing deep linking provider? LinkForty supports zero-downtime migration via custom domain DNS cutover.
732
+
733
+ - [Migrate from Branch.io](https://docs.linkforty.com/migrations/branch)
734
+ - [Migrate from AppsFlyer OneLink](https://docs.linkforty.com/migrations/appsflyer)
735
+ - [Migrate from Firebase Dynamic Links](https://docs.linkforty.com/comparisons/firebase-dynamic-links-migration) (shut down August 2025)
736
+ - [Migrate from Adjust](https://docs.linkforty.com/migrations/adjust)
737
+ - [Migrate from Kochava](https://docs.linkforty.com/migrations/kochava)
738
+ - [Migration overview and checklist](https://docs.linkforty.com/migrations/overview)
739
+
740
+ ## For AI Tools (llms.txt)
741
+
742
+ LinkForty provides machine-readable documentation for AI coding assistants (Claude, ChatGPT, Cursor, Copilot).
743
+
744
+ - **Quick reference**: [docs.linkforty.com/llms.txt](https://docs.linkforty.com/llms.txt)
745
+ - **Complete integration guide**: [docs.linkforty.com/llms-full.txt](https://docs.linkforty.com/llms-full.txt)
746
+
747
+ Download into your project for AI-assisted integration:
748
+
749
+ ```bash
750
+ curl -o LINKFORTY.md https://docs.linkforty.com/llms-full.txt
751
+ ```
752
+
753
+ The npm package also ships with an `llms.txt` file — AI tools that read from `node_modules` can discover it automatically.
754
+
580
755
  ## Contributing
581
756
 
582
757
  Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details.
583
758
 
584
759
  ## License
585
760
 
586
- MIT License - see [LICENSE](LICENSE) file for details.
761
+ AGPL-3.0 - see [LICENSE](LICENSE) file for details.
587
762
 
588
763
  ## Related Projects
589
764
 
590
- - **@linkforty/ui** - React UI components for link management
591
- - **[LinkForty Cloud](https://linkforty.com)** - Hosted SaaS version with additional features
765
+ - **[@linkforty/mobile-sdk-react-native](https://github.com/LinkForty/mobile-sdk-react-native)** - React Native SDK
766
+ - **[@linkforty/mobile-sdk-expo](https://github.com/LinkForty/mobile-sdk-expo)** - Expo SDK
767
+ - **[mobile-sdk-ios](https://github.com/LinkForty/mobile-sdk-ios)** - iOS SDK (Swift)
768
+ - **[mobile-sdk-android](https://github.com/LinkForty/mobile-sdk-android)** - Android SDK (Kotlin)
769
+ - **[LinkForty Cloud](https://linkforty.com)** - Hosted SaaS version with authentication, teams, billing, and dashboard
592
770
 
593
771
  ## Support
594
772
 
@@ -600,8 +778,8 @@ MIT License - see [LICENSE](LICENSE) file for details.
600
778
  - [Fastify](https://www.fastify.io/) - Fast web framework
601
779
  - [PostgreSQL](https://www.postgresql.org/) - Powerful database
602
780
  - [Redis](https://redis.io/) - In-memory cache
781
+ - [Zod](https://zod.dev/) - TypeScript-first schema validation
603
782
  - [nanoid](https://github.com/ai/nanoid) - Unique ID generation
604
783
  - [geoip-lite](https://github.com/geoip-lite/node-geoip) - IP geolocation
605
784
  - [ua-parser-js](https://github.com/faisalman/ua-parser-js) - User agent parsing
606
-
607
-
785
+ - [qrcode](https://github.com/soldair/node-qrcode) - QR code generation
package/dist/index.d.ts CHANGED
@@ -17,5 +17,5 @@ export * from './lib/fingerprint.js';
17
17
  export * from './lib/webhook.js';
18
18
  export * from './lib/event-emitter.js';
19
19
  export * from './types/index.js';
20
- export { redirectRoutes, linkRoutes, analyticsRoutes, sdkRoutes, webhookRoutes, qrRoutes, previewRoutes, debugRoutes, wellKnownRoutes } from './routes/index.js';
20
+ export { redirectRoutes, linkRoutes, analyticsRoutes, sdkRoutes, webhookRoutes, templateRoutes, qrRoutes, previewRoutes, debugRoutes, wellKnownRoutes } from './routes/index.js';
21
21
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAGnD,OAAO,EAAsB,eAAe,EAAE,MAAM,mBAAmB,CAAC;AASxE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,KAAK,CAAC,EAAE;QACN,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;IACF,IAAI,CAAC,EAAE;QACL,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;KAC3B,CAAC;IACF,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,YAAY,CAAC,OAAO,GAAE,aAAkB,kTA8B7D;AAGD,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,eAAe,EAAE,SAAS,EAAE,aAAa,EAAE,QAAQ,EAAE,aAAa,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAGnD,OAAO,EAAsB,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAUxE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,KAAK,CAAC,EAAE;QACN,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;IACF,IAAI,CAAC,EAAE;QACL,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;KAC3B,CAAC;IACF,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,YAAY,CAAC,OAAO,GAAE,aAAkB,kTA+B7D;AAGD,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,eAAe,EAAE,SAAS,EAAE,aAAa,EAAE,cAAc,EAAE,QAAQ,EAAE,aAAa,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC"}
package/dist/index.js CHANGED
@@ -7,6 +7,7 @@ import { linkRoutes } from './routes/links.js';
7
7
  import { analyticsRoutes } from './routes/analytics.js';
8
8
  import { sdkRoutes } from './routes/sdk.js';
9
9
  import { webhookRoutes } from './routes/webhooks.js';
10
+ import { templateRoutes } from './routes/templates.js';
10
11
  import { qrRoutes } from './routes/qr.js';
11
12
  import { wellKnownRoutes } from './routes/well-known.js';
12
13
  export async function createServer(options = {}) {
@@ -32,6 +33,7 @@ export async function createServer(options = {}) {
32
33
  await fastify.register(analyticsRoutes);
33
34
  await fastify.register(sdkRoutes);
34
35
  await fastify.register(webhookRoutes);
36
+ await fastify.register(templateRoutes);
35
37
  await fastify.register(qrRoutes);
36
38
  return fastify;
37
39
  }
@@ -42,5 +44,5 @@ export * from './lib/fingerprint.js';
42
44
  export * from './lib/webhook.js';
43
45
  export * from './lib/event-emitter.js';
44
46
  export * from './types/index.js';
45
- export { redirectRoutes, linkRoutes, analyticsRoutes, sdkRoutes, webhookRoutes, qrRoutes, previewRoutes, debugRoutes, wellKnownRoutes } from './routes/index.js';
47
+ export { redirectRoutes, linkRoutes, analyticsRoutes, sdkRoutes, webhookRoutes, templateRoutes, qrRoutes, previewRoutes, debugRoutes, wellKnownRoutes } from './routes/index.js';
46
48
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAA4B,MAAM,SAAS,CAAC;AACnD,OAAO,IAAI,MAAM,eAAe,CAAC;AACjC,OAAO,KAAK,MAAM,gBAAgB,CAAC;AACnC,OAAO,EAAE,kBAAkB,EAAmB,MAAM,mBAAmB,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAazD,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,UAAyB,EAAE;IAC5D,MAAM,OAAO,GAAG,OAAO,CAAC;QACtB,MAAM,EAAE,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI;KAC7D,CAAC,CAAC;IAEH,OAAO;IACP,MAAM,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE;QAC3B,MAAM,EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,IAAI,GAAG;KACpC,CAAC,CAAC;IAEH,mBAAmB;IACnB,IAAI,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC;QACvB,MAAM,OAAO,CAAC,QAAQ,CAAC,KAAK,EAAE;YAC5B,GAAG,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG;SACvB,CAAC,CAAC;IACL,CAAC;IAED,WAAW;IACX,MAAM,kBAAkB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAE3C,SAAS;IACT,MAAM,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;IACxC,MAAM,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;IACvC,MAAM,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IACnC,MAAM,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;IACxC,MAAM,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IAClC,MAAM,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;IACtC,MAAM,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAEjC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,gCAAgC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,eAAe,EAAE,SAAS,EAAE,aAAa,EAAE,QAAQ,EAAE,aAAa,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAA4B,MAAM,SAAS,CAAC;AACnD,OAAO,IAAI,MAAM,eAAe,CAAC;AACjC,OAAO,KAAK,MAAM,gBAAgB,CAAC;AACnC,OAAO,EAAE,kBAAkB,EAAmB,MAAM,mBAAmB,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAazD,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,UAAyB,EAAE;IAC5D,MAAM,OAAO,GAAG,OAAO,CAAC;QACtB,MAAM,EAAE,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI;KAC7D,CAAC,CAAC;IAEH,OAAO;IACP,MAAM,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE;QAC3B,MAAM,EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,IAAI,GAAG;KACpC,CAAC,CAAC;IAEH,mBAAmB;IACnB,IAAI,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC;QACvB,MAAM,OAAO,CAAC,QAAQ,CAAC,KAAK,EAAE;YAC5B,GAAG,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG;SACvB,CAAC,CAAC;IACL,CAAC;IAED,WAAW;IACX,MAAM,kBAAkB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAE3C,SAAS;IACT,MAAM,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;IACxC,MAAM,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;IACvC,MAAM,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IACnC,MAAM,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;IACxC,MAAM,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IAClC,MAAM,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;IACtC,MAAM,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;IACvC,MAAM,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAEjC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,gCAAgC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,eAAe,EAAE,SAAS,EAAE,aAAa,EAAE,cAAc,EAAE,QAAQ,EAAE,aAAa,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC"}