@rajeev02/deeplink 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/lib/index.d.ts +11 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +15 -0
- package/lib/index.js.map +1 -0
- package/lib/router/index.d.ts +70 -0
- package/lib/router/index.d.ts.map +1 -0
- package/lib/router/index.js +204 -0
- package/lib/router/index.js.map +1 -0
- package/package.json +50 -0
- package/src/index.ts +15 -0
- package/src/router/index.ts +271 -0
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @rajeev02/deeplink
|
|
3
|
+
* Universal Deep Linking
|
|
4
|
+
* App links, universal links, deferred deep links, route matching, attribution
|
|
5
|
+
*
|
|
6
|
+
* @author Rajeev Kumar Joshi
|
|
7
|
+
* @license MIT
|
|
8
|
+
*/
|
|
9
|
+
export { DeepLinkRouter, getCommonRoutes } from "./router";
|
|
10
|
+
export type { DeepLinkRoute, DeepLinkMatch, DeferredDeepLink, DeepLinkConfig, } from "./router";
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3D,YAAY,EACV,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,cAAc,GACf,MAAM,UAAU,CAAC"}
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getCommonRoutes = exports.DeepLinkRouter = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* @rajeev02/deeplink
|
|
6
|
+
* Universal Deep Linking
|
|
7
|
+
* App links, universal links, deferred deep links, route matching, attribution
|
|
8
|
+
*
|
|
9
|
+
* @author Rajeev Kumar Joshi
|
|
10
|
+
* @license MIT
|
|
11
|
+
*/
|
|
12
|
+
var router_1 = require("./router");
|
|
13
|
+
Object.defineProperty(exports, "DeepLinkRouter", { enumerable: true, get: function () { return router_1.DeepLinkRouter; } });
|
|
14
|
+
Object.defineProperty(exports, "getCommonRoutes", { enumerable: true, get: function () { return router_1.getCommonRoutes; } });
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA;;;;;;;GAOG;AACH,mCAA2D;AAAlD,wGAAA,cAAc,OAAA;AAAE,yGAAA,eAAe,OAAA"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @rajeev02/deeplink — Router
|
|
3
|
+
* Universal deep link router — pattern matching, parameter extraction,
|
|
4
|
+
* auth guards, deferred deep links, analytics attribution
|
|
5
|
+
*/
|
|
6
|
+
export interface DeepLinkRoute {
|
|
7
|
+
/** Pattern: "/product/:id", "/payment/:orderId/status", "/chat/:roomId" */
|
|
8
|
+
pattern: string;
|
|
9
|
+
/** Screen/page name to navigate to */
|
|
10
|
+
screen: string;
|
|
11
|
+
/** Required auth state ('authenticated' | 'any') */
|
|
12
|
+
authRequired?: boolean;
|
|
13
|
+
/** Handler function */
|
|
14
|
+
handler?: (params: Record<string, string>, query: Record<string, string>) => void;
|
|
15
|
+
}
|
|
16
|
+
export interface DeepLinkMatch {
|
|
17
|
+
route: DeepLinkRoute;
|
|
18
|
+
params: Record<string, string>;
|
|
19
|
+
query: Record<string, string>;
|
|
20
|
+
fullUrl: string;
|
|
21
|
+
}
|
|
22
|
+
export interface DeferredDeepLink {
|
|
23
|
+
url: string;
|
|
24
|
+
timestamp: number;
|
|
25
|
+
source?: string;
|
|
26
|
+
campaign?: string;
|
|
27
|
+
processed: boolean;
|
|
28
|
+
}
|
|
29
|
+
export interface DeepLinkConfig {
|
|
30
|
+
/** App scheme (e.g., "rajeevapp://") */
|
|
31
|
+
scheme: string;
|
|
32
|
+
/** Universal link domains (e.g., ["rajeevapp.com", "link.rajeevapp.com"]) */
|
|
33
|
+
domains: string[];
|
|
34
|
+
/** Route definitions */
|
|
35
|
+
routes: DeepLinkRoute[];
|
|
36
|
+
/** Callback when no route matches */
|
|
37
|
+
onNoMatch?: (url: string) => void;
|
|
38
|
+
/** Callback for attribution tracking */
|
|
39
|
+
onAttribution?: (source: string, campaign?: string, medium?: string) => void;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Deep Link Router
|
|
43
|
+
*/
|
|
44
|
+
export declare class DeepLinkRouter {
|
|
45
|
+
private config;
|
|
46
|
+
private deferredLink;
|
|
47
|
+
private isReady;
|
|
48
|
+
constructor(config: DeepLinkConfig);
|
|
49
|
+
/** Mark the app as ready to handle deep links (after auth check, navigation ready) */
|
|
50
|
+
setReady(): void;
|
|
51
|
+
/** Handle an incoming deep link URL */
|
|
52
|
+
handle(url: string): DeepLinkMatch | null;
|
|
53
|
+
/** Generate a deep link URL */
|
|
54
|
+
generate(pattern: string, params?: Record<string, string>, query?: Record<string, string>): string;
|
|
55
|
+
/** Generate a universal link (HTTPS) */
|
|
56
|
+
generateUniversalLink(pattern: string, params?: Record<string, string>, query?: Record<string, string>): string;
|
|
57
|
+
/** Get deferred deep link (for cold start handling) */
|
|
58
|
+
getDeferredLink(): DeferredDeepLink | null;
|
|
59
|
+
/** Get all registered routes */
|
|
60
|
+
getRoutes(): DeepLinkRoute[];
|
|
61
|
+
/** Add a route dynamically */
|
|
62
|
+
addRoute(route: DeepLinkRoute): void;
|
|
63
|
+
private parseUrl;
|
|
64
|
+
private matchPattern;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Common deep link routes for super apps
|
|
68
|
+
*/
|
|
69
|
+
export declare function getCommonRoutes(): DeepLinkRoute[];
|
|
70
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/router/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,aAAa;IAC5B,2EAA2E;IAC3E,OAAO,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,uBAAuB;IACvB,OAAO,CAAC,EAAE,CACR,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAC1B,IAAI,CAAC;CACX;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,aAAa,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,wCAAwC;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,wBAAwB;IACxB,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,qCAAqC;IACrC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,wCAAwC;IACxC,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9E;AAED;;GAEG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,YAAY,CAAiC;IACrD,OAAO,CAAC,OAAO,CAAkB;gBAErB,MAAM,EAAE,cAAc;IAIlC,sFAAsF;IACtF,QAAQ,IAAI,IAAI;IAQhB,uCAAuC;IACvC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI;IA4CzC,+BAA+B;IAC/B,QAAQ,CACN,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EACnC,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACjC,MAAM;IAYT,wCAAwC;IACxC,qBAAqB,CACnB,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EACnC,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACjC,MAAM;IAaT,uDAAuD;IACvD,eAAe,IAAI,gBAAgB,GAAG,IAAI;IAI1C,gCAAgC;IAChC,SAAS,IAAI,aAAa,EAAE;IAI5B,8BAA8B;IAC9B,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAIpC,OAAO,CAAC,QAAQ;IAwChB,OAAO,CAAC,YAAY;CAmBrB;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,aAAa,EAAE,CA0CjD"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @rajeev02/deeplink — Router
|
|
4
|
+
* Universal deep link router — pattern matching, parameter extraction,
|
|
5
|
+
* auth guards, deferred deep links, analytics attribution
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.DeepLinkRouter = void 0;
|
|
9
|
+
exports.getCommonRoutes = getCommonRoutes;
|
|
10
|
+
/**
|
|
11
|
+
* Deep Link Router
|
|
12
|
+
*/
|
|
13
|
+
class DeepLinkRouter {
|
|
14
|
+
constructor(config) {
|
|
15
|
+
this.deferredLink = null;
|
|
16
|
+
this.isReady = false;
|
|
17
|
+
this.config = config;
|
|
18
|
+
}
|
|
19
|
+
/** Mark the app as ready to handle deep links (after auth check, navigation ready) */
|
|
20
|
+
setReady() {
|
|
21
|
+
this.isReady = true;
|
|
22
|
+
if (this.deferredLink && !this.deferredLink.processed) {
|
|
23
|
+
this.handle(this.deferredLink.url);
|
|
24
|
+
this.deferredLink.processed = true;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** Handle an incoming deep link URL */
|
|
28
|
+
handle(url) {
|
|
29
|
+
const parsed = this.parseUrl(url);
|
|
30
|
+
if (!parsed)
|
|
31
|
+
return null;
|
|
32
|
+
// Extract UTM params for attribution
|
|
33
|
+
if (parsed.query.utm_source && this.config.onAttribution) {
|
|
34
|
+
this.config.onAttribution(parsed.query.utm_source, parsed.query.utm_campaign, parsed.query.utm_medium);
|
|
35
|
+
}
|
|
36
|
+
// If app not ready yet, defer the link
|
|
37
|
+
if (!this.isReady) {
|
|
38
|
+
this.deferredLink = {
|
|
39
|
+
url,
|
|
40
|
+
timestamp: Date.now(),
|
|
41
|
+
source: parsed.query.utm_source,
|
|
42
|
+
processed: false,
|
|
43
|
+
};
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
// Match against routes
|
|
47
|
+
for (const route of this.config.routes) {
|
|
48
|
+
const params = this.matchPattern(route.pattern, parsed.path);
|
|
49
|
+
if (params !== null) {
|
|
50
|
+
const match = {
|
|
51
|
+
route,
|
|
52
|
+
params,
|
|
53
|
+
query: parsed.query,
|
|
54
|
+
fullUrl: url,
|
|
55
|
+
};
|
|
56
|
+
if (route.handler)
|
|
57
|
+
route.handler(params, parsed.query);
|
|
58
|
+
return match;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// No match
|
|
62
|
+
if (this.config.onNoMatch)
|
|
63
|
+
this.config.onNoMatch(url);
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
/** Generate a deep link URL */
|
|
67
|
+
generate(pattern, params = {}, query = {}) {
|
|
68
|
+
let path = pattern;
|
|
69
|
+
for (const [key, value] of Object.entries(params)) {
|
|
70
|
+
path = path.replace(`:${key}`, encodeURIComponent(value));
|
|
71
|
+
}
|
|
72
|
+
const queryStr = Object.entries(query)
|
|
73
|
+
.map(([k, v]) => `${k}=${encodeURIComponent(v)}`)
|
|
74
|
+
.join("&");
|
|
75
|
+
const base = `${this.config.scheme}${path}`;
|
|
76
|
+
return queryStr ? `${base}?${queryStr}` : base;
|
|
77
|
+
}
|
|
78
|
+
/** Generate a universal link (HTTPS) */
|
|
79
|
+
generateUniversalLink(pattern, params = {}, query = {}) {
|
|
80
|
+
let path = pattern;
|
|
81
|
+
for (const [key, value] of Object.entries(params)) {
|
|
82
|
+
path = path.replace(`:${key}`, encodeURIComponent(value));
|
|
83
|
+
}
|
|
84
|
+
const queryStr = Object.entries(query)
|
|
85
|
+
.map(([k, v]) => `${k}=${encodeURIComponent(v)}`)
|
|
86
|
+
.join("&");
|
|
87
|
+
const domain = this.config.domains[0] || "app.example.com";
|
|
88
|
+
const base = `https://${domain}${path}`;
|
|
89
|
+
return queryStr ? `${base}?${queryStr}` : base;
|
|
90
|
+
}
|
|
91
|
+
/** Get deferred deep link (for cold start handling) */
|
|
92
|
+
getDeferredLink() {
|
|
93
|
+
return this.deferredLink;
|
|
94
|
+
}
|
|
95
|
+
/** Get all registered routes */
|
|
96
|
+
getRoutes() {
|
|
97
|
+
return [...this.config.routes];
|
|
98
|
+
}
|
|
99
|
+
/** Add a route dynamically */
|
|
100
|
+
addRoute(route) {
|
|
101
|
+
this.config.routes.push(route);
|
|
102
|
+
}
|
|
103
|
+
parseUrl(url) {
|
|
104
|
+
try {
|
|
105
|
+
let path;
|
|
106
|
+
let queryStr = "";
|
|
107
|
+
if (url.includes("://")) {
|
|
108
|
+
const afterScheme = url.split("://")[1] || "";
|
|
109
|
+
// Remove domain for universal links
|
|
110
|
+
let pathPart = afterScheme;
|
|
111
|
+
for (const domain of this.config.domains) {
|
|
112
|
+
if (pathPart.startsWith(domain)) {
|
|
113
|
+
pathPart = pathPart.substring(domain.length);
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const [p, q] = pathPart.split("?");
|
|
118
|
+
path = p.startsWith("/") ? p : `/${p}`;
|
|
119
|
+
queryStr = q || "";
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
const [p, q] = url.split("?");
|
|
123
|
+
path = p.startsWith("/") ? p : `/${p}`;
|
|
124
|
+
queryStr = q || "";
|
|
125
|
+
}
|
|
126
|
+
const query = {};
|
|
127
|
+
if (queryStr) {
|
|
128
|
+
for (const pair of queryStr.split("&")) {
|
|
129
|
+
const [k, v] = pair.split("=");
|
|
130
|
+
if (k)
|
|
131
|
+
query[decodeURIComponent(k)] = decodeURIComponent(v || "");
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return { path, query };
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
matchPattern(pattern, path) {
|
|
141
|
+
const patternParts = pattern.split("/").filter(Boolean);
|
|
142
|
+
const pathParts = path.split("/").filter(Boolean);
|
|
143
|
+
if (patternParts.length !== pathParts.length)
|
|
144
|
+
return null;
|
|
145
|
+
const params = {};
|
|
146
|
+
for (let i = 0; i < patternParts.length; i++) {
|
|
147
|
+
if (patternParts[i].startsWith(":")) {
|
|
148
|
+
params[patternParts[i].substring(1)] = decodeURIComponent(pathParts[i]);
|
|
149
|
+
}
|
|
150
|
+
else if (patternParts[i] !== pathParts[i]) {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return params;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
exports.DeepLinkRouter = DeepLinkRouter;
|
|
158
|
+
/**
|
|
159
|
+
* Common deep link routes for super apps
|
|
160
|
+
*/
|
|
161
|
+
function getCommonRoutes() {
|
|
162
|
+
return [
|
|
163
|
+
{ pattern: "/home", screen: "HomeScreen" },
|
|
164
|
+
{ pattern: "/product/:id", screen: "ProductScreen" },
|
|
165
|
+
{ pattern: "/product/:id/reviews", screen: "ReviewsScreen" },
|
|
166
|
+
{ pattern: "/cart", screen: "CartScreen", authRequired: true },
|
|
167
|
+
{ pattern: "/checkout", screen: "CheckoutScreen", authRequired: true },
|
|
168
|
+
{
|
|
169
|
+
pattern: "/order/:orderId",
|
|
170
|
+
screen: "OrderDetailScreen",
|
|
171
|
+
authRequired: true,
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
pattern: "/order/:orderId/track",
|
|
175
|
+
screen: "OrderTrackingScreen",
|
|
176
|
+
authRequired: true,
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
pattern: "/payment/:txnId/status",
|
|
180
|
+
screen: "PaymentStatusScreen",
|
|
181
|
+
authRequired: true,
|
|
182
|
+
},
|
|
183
|
+
{ pattern: "/chat/:roomId", screen: "ChatScreen", authRequired: true },
|
|
184
|
+
{ pattern: "/profile", screen: "ProfileScreen", authRequired: true },
|
|
185
|
+
{
|
|
186
|
+
pattern: "/profile/settings",
|
|
187
|
+
screen: "SettingsScreen",
|
|
188
|
+
authRequired: true,
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
pattern: "/notifications",
|
|
192
|
+
screen: "NotificationsScreen",
|
|
193
|
+
authRequired: true,
|
|
194
|
+
},
|
|
195
|
+
{ pattern: "/scan", screen: "QrScanScreen" },
|
|
196
|
+
{ pattern: "/pay", screen: "PaymentScreen", authRequired: true },
|
|
197
|
+
{ pattern: "/pay/:vpa", screen: "PayToVpaScreen", authRequired: true },
|
|
198
|
+
{ pattern: "/refer", screen: "ReferralScreen" },
|
|
199
|
+
{ pattern: "/offers", screen: "OffersScreen" },
|
|
200
|
+
{ pattern: "/help", screen: "HelpScreen" },
|
|
201
|
+
{ pattern: "/kyc", screen: "KycScreen", authRequired: true },
|
|
202
|
+
];
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/router/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rajeev02/deeplink",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Universal deep linking — app links, universal links, deferred deep links, route matching, attribution",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"author": "Rajeev Kumar Joshi <rajeevjoshi91@gmail.com> (https://rajeev02.github.io)",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"types": "lib/index.d.ts",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"clean": "rm -rf lib",
|
|
12
|
+
"prepublishOnly": "npm run build"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"react-native",
|
|
16
|
+
"deeplink",
|
|
17
|
+
"universal-links",
|
|
18
|
+
"routing"
|
|
19
|
+
],
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/Rajeev02/rajeev-sdk",
|
|
23
|
+
"directory": "packages/deeplink"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/Rajeev02/rajeev-sdk#readme",
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/Rajeev02/rajeev-sdk/issues"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"lib/",
|
|
31
|
+
"src/",
|
|
32
|
+
"README.md"
|
|
33
|
+
],
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"react": ">=18.3.0",
|
|
39
|
+
"react-native": ">=0.84.0"
|
|
40
|
+
},
|
|
41
|
+
"peerDependenciesMeta": {
|
|
42
|
+
"react-native": {
|
|
43
|
+
"optional": true
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/react": "^19.0.0",
|
|
48
|
+
"typescript": "^5.4.0"
|
|
49
|
+
}
|
|
50
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @rajeev02/deeplink
|
|
3
|
+
* Universal Deep Linking
|
|
4
|
+
* App links, universal links, deferred deep links, route matching, attribution
|
|
5
|
+
*
|
|
6
|
+
* @author Rajeev Kumar Joshi
|
|
7
|
+
* @license MIT
|
|
8
|
+
*/
|
|
9
|
+
export { DeepLinkRouter, getCommonRoutes } from "./router";
|
|
10
|
+
export type {
|
|
11
|
+
DeepLinkRoute,
|
|
12
|
+
DeepLinkMatch,
|
|
13
|
+
DeferredDeepLink,
|
|
14
|
+
DeepLinkConfig,
|
|
15
|
+
} from "./router";
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @rajeev02/deeplink — Router
|
|
3
|
+
* Universal deep link router — pattern matching, parameter extraction,
|
|
4
|
+
* auth guards, deferred deep links, analytics attribution
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export interface DeepLinkRoute {
|
|
8
|
+
/** Pattern: "/product/:id", "/payment/:orderId/status", "/chat/:roomId" */
|
|
9
|
+
pattern: string;
|
|
10
|
+
/** Screen/page name to navigate to */
|
|
11
|
+
screen: string;
|
|
12
|
+
/** Required auth state ('authenticated' | 'any') */
|
|
13
|
+
authRequired?: boolean;
|
|
14
|
+
/** Handler function */
|
|
15
|
+
handler?: (
|
|
16
|
+
params: Record<string, string>,
|
|
17
|
+
query: Record<string, string>,
|
|
18
|
+
) => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface DeepLinkMatch {
|
|
22
|
+
route: DeepLinkRoute;
|
|
23
|
+
params: Record<string, string>;
|
|
24
|
+
query: Record<string, string>;
|
|
25
|
+
fullUrl: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface DeferredDeepLink {
|
|
29
|
+
url: string;
|
|
30
|
+
timestamp: number;
|
|
31
|
+
source?: string;
|
|
32
|
+
campaign?: string;
|
|
33
|
+
processed: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface DeepLinkConfig {
|
|
37
|
+
/** App scheme (e.g., "rajeevapp://") */
|
|
38
|
+
scheme: string;
|
|
39
|
+
/** Universal link domains (e.g., ["rajeevapp.com", "link.rajeevapp.com"]) */
|
|
40
|
+
domains: string[];
|
|
41
|
+
/** Route definitions */
|
|
42
|
+
routes: DeepLinkRoute[];
|
|
43
|
+
/** Callback when no route matches */
|
|
44
|
+
onNoMatch?: (url: string) => void;
|
|
45
|
+
/** Callback for attribution tracking */
|
|
46
|
+
onAttribution?: (source: string, campaign?: string, medium?: string) => void;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Deep Link Router
|
|
51
|
+
*/
|
|
52
|
+
export class DeepLinkRouter {
|
|
53
|
+
private config: DeepLinkConfig;
|
|
54
|
+
private deferredLink: DeferredDeepLink | null = null;
|
|
55
|
+
private isReady: boolean = false;
|
|
56
|
+
|
|
57
|
+
constructor(config: DeepLinkConfig) {
|
|
58
|
+
this.config = config;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Mark the app as ready to handle deep links (after auth check, navigation ready) */
|
|
62
|
+
setReady(): void {
|
|
63
|
+
this.isReady = true;
|
|
64
|
+
if (this.deferredLink && !this.deferredLink.processed) {
|
|
65
|
+
this.handle(this.deferredLink.url);
|
|
66
|
+
this.deferredLink.processed = true;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Handle an incoming deep link URL */
|
|
71
|
+
handle(url: string): DeepLinkMatch | null {
|
|
72
|
+
const parsed = this.parseUrl(url);
|
|
73
|
+
if (!parsed) return null;
|
|
74
|
+
|
|
75
|
+
// Extract UTM params for attribution
|
|
76
|
+
if (parsed.query.utm_source && this.config.onAttribution) {
|
|
77
|
+
this.config.onAttribution(
|
|
78
|
+
parsed.query.utm_source,
|
|
79
|
+
parsed.query.utm_campaign,
|
|
80
|
+
parsed.query.utm_medium,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// If app not ready yet, defer the link
|
|
85
|
+
if (!this.isReady) {
|
|
86
|
+
this.deferredLink = {
|
|
87
|
+
url,
|
|
88
|
+
timestamp: Date.now(),
|
|
89
|
+
source: parsed.query.utm_source,
|
|
90
|
+
processed: false,
|
|
91
|
+
};
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Match against routes
|
|
96
|
+
for (const route of this.config.routes) {
|
|
97
|
+
const params = this.matchPattern(route.pattern, parsed.path);
|
|
98
|
+
if (params !== null) {
|
|
99
|
+
const match: DeepLinkMatch = {
|
|
100
|
+
route,
|
|
101
|
+
params,
|
|
102
|
+
query: parsed.query,
|
|
103
|
+
fullUrl: url,
|
|
104
|
+
};
|
|
105
|
+
if (route.handler) route.handler(params, parsed.query);
|
|
106
|
+
return match;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// No match
|
|
111
|
+
if (this.config.onNoMatch) this.config.onNoMatch(url);
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Generate a deep link URL */
|
|
116
|
+
generate(
|
|
117
|
+
pattern: string,
|
|
118
|
+
params: Record<string, string> = {},
|
|
119
|
+
query: Record<string, string> = {},
|
|
120
|
+
): string {
|
|
121
|
+
let path = pattern;
|
|
122
|
+
for (const [key, value] of Object.entries(params)) {
|
|
123
|
+
path = path.replace(`:${key}`, encodeURIComponent(value));
|
|
124
|
+
}
|
|
125
|
+
const queryStr = Object.entries(query)
|
|
126
|
+
.map(([k, v]) => `${k}=${encodeURIComponent(v)}`)
|
|
127
|
+
.join("&");
|
|
128
|
+
const base = `${this.config.scheme}${path}`;
|
|
129
|
+
return queryStr ? `${base}?${queryStr}` : base;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Generate a universal link (HTTPS) */
|
|
133
|
+
generateUniversalLink(
|
|
134
|
+
pattern: string,
|
|
135
|
+
params: Record<string, string> = {},
|
|
136
|
+
query: Record<string, string> = {},
|
|
137
|
+
): string {
|
|
138
|
+
let path = pattern;
|
|
139
|
+
for (const [key, value] of Object.entries(params)) {
|
|
140
|
+
path = path.replace(`:${key}`, encodeURIComponent(value));
|
|
141
|
+
}
|
|
142
|
+
const queryStr = Object.entries(query)
|
|
143
|
+
.map(([k, v]) => `${k}=${encodeURIComponent(v)}`)
|
|
144
|
+
.join("&");
|
|
145
|
+
const domain = this.config.domains[0] || "app.example.com";
|
|
146
|
+
const base = `https://${domain}${path}`;
|
|
147
|
+
return queryStr ? `${base}?${queryStr}` : base;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Get deferred deep link (for cold start handling) */
|
|
151
|
+
getDeferredLink(): DeferredDeepLink | null {
|
|
152
|
+
return this.deferredLink;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Get all registered routes */
|
|
156
|
+
getRoutes(): DeepLinkRoute[] {
|
|
157
|
+
return [...this.config.routes];
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Add a route dynamically */
|
|
161
|
+
addRoute(route: DeepLinkRoute): void {
|
|
162
|
+
this.config.routes.push(route);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private parseUrl(
|
|
166
|
+
url: string,
|
|
167
|
+
): { path: string; query: Record<string, string> } | null {
|
|
168
|
+
try {
|
|
169
|
+
let path: string;
|
|
170
|
+
let queryStr: string = "";
|
|
171
|
+
|
|
172
|
+
if (url.includes("://")) {
|
|
173
|
+
const afterScheme = url.split("://")[1] || "";
|
|
174
|
+
// Remove domain for universal links
|
|
175
|
+
let pathPart = afterScheme;
|
|
176
|
+
for (const domain of this.config.domains) {
|
|
177
|
+
if (pathPart.startsWith(domain)) {
|
|
178
|
+
pathPart = pathPart.substring(domain.length);
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const [p, q] = pathPart.split("?");
|
|
183
|
+
path = p.startsWith("/") ? p : `/${p}`;
|
|
184
|
+
queryStr = q || "";
|
|
185
|
+
} else {
|
|
186
|
+
const [p, q] = url.split("?");
|
|
187
|
+
path = p.startsWith("/") ? p : `/${p}`;
|
|
188
|
+
queryStr = q || "";
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const query: Record<string, string> = {};
|
|
192
|
+
if (queryStr) {
|
|
193
|
+
for (const pair of queryStr.split("&")) {
|
|
194
|
+
const [k, v] = pair.split("=");
|
|
195
|
+
if (k) query[decodeURIComponent(k)] = decodeURIComponent(v || "");
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return { path, query };
|
|
200
|
+
} catch {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
private matchPattern(
|
|
206
|
+
pattern: string,
|
|
207
|
+
path: string,
|
|
208
|
+
): Record<string, string> | null {
|
|
209
|
+
const patternParts = pattern.split("/").filter(Boolean);
|
|
210
|
+
const pathParts = path.split("/").filter(Boolean);
|
|
211
|
+
|
|
212
|
+
if (patternParts.length !== pathParts.length) return null;
|
|
213
|
+
|
|
214
|
+
const params: Record<string, string> = {};
|
|
215
|
+
for (let i = 0; i < patternParts.length; i++) {
|
|
216
|
+
if (patternParts[i].startsWith(":")) {
|
|
217
|
+
params[patternParts[i].substring(1)] = decodeURIComponent(pathParts[i]);
|
|
218
|
+
} else if (patternParts[i] !== pathParts[i]) {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return params;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Common deep link routes for super apps
|
|
228
|
+
*/
|
|
229
|
+
export function getCommonRoutes(): DeepLinkRoute[] {
|
|
230
|
+
return [
|
|
231
|
+
{ pattern: "/home", screen: "HomeScreen" },
|
|
232
|
+
{ pattern: "/product/:id", screen: "ProductScreen" },
|
|
233
|
+
{ pattern: "/product/:id/reviews", screen: "ReviewsScreen" },
|
|
234
|
+
{ pattern: "/cart", screen: "CartScreen", authRequired: true },
|
|
235
|
+
{ pattern: "/checkout", screen: "CheckoutScreen", authRequired: true },
|
|
236
|
+
{
|
|
237
|
+
pattern: "/order/:orderId",
|
|
238
|
+
screen: "OrderDetailScreen",
|
|
239
|
+
authRequired: true,
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
pattern: "/order/:orderId/track",
|
|
243
|
+
screen: "OrderTrackingScreen",
|
|
244
|
+
authRequired: true,
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
pattern: "/payment/:txnId/status",
|
|
248
|
+
screen: "PaymentStatusScreen",
|
|
249
|
+
authRequired: true,
|
|
250
|
+
},
|
|
251
|
+
{ pattern: "/chat/:roomId", screen: "ChatScreen", authRequired: true },
|
|
252
|
+
{ pattern: "/profile", screen: "ProfileScreen", authRequired: true },
|
|
253
|
+
{
|
|
254
|
+
pattern: "/profile/settings",
|
|
255
|
+
screen: "SettingsScreen",
|
|
256
|
+
authRequired: true,
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
pattern: "/notifications",
|
|
260
|
+
screen: "NotificationsScreen",
|
|
261
|
+
authRequired: true,
|
|
262
|
+
},
|
|
263
|
+
{ pattern: "/scan", screen: "QrScanScreen" },
|
|
264
|
+
{ pattern: "/pay", screen: "PaymentScreen", authRequired: true },
|
|
265
|
+
{ pattern: "/pay/:vpa", screen: "PayToVpaScreen", authRequired: true },
|
|
266
|
+
{ pattern: "/refer", screen: "ReferralScreen" },
|
|
267
|
+
{ pattern: "/offers", screen: "OffersScreen" },
|
|
268
|
+
{ pattern: "/help", screen: "HelpScreen" },
|
|
269
|
+
{ pattern: "/kyc", screen: "KycScreen", authRequired: true },
|
|
270
|
+
];
|
|
271
|
+
}
|