@linkforty/core 1.44.1 → 1.44.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -588
- package/llms.txt +1 -1
- package/package.json +2 -1
- package/dist/index.config.test.d.ts +0 -2
- package/dist/index.config.test.d.ts.map +0 -1
- package/dist/index.config.test.js +0 -87
- package/dist/index.config.test.js.map +0 -1
- package/dist/lib/bot-detection.test.d.ts +0 -2
- package/dist/lib/bot-detection.test.d.ts.map +0 -1
- package/dist/lib/bot-detection.test.js +0 -65
- package/dist/lib/bot-detection.test.js.map +0 -1
- package/dist/lib/client-ip.test.d.ts +0 -7
- package/dist/lib/client-ip.test.d.ts.map +0 -1
- package/dist/lib/client-ip.test.js +0 -114
- package/dist/lib/client-ip.test.js.map +0 -1
- package/dist/lib/config.test.d.ts +0 -2
- package/dist/lib/config.test.d.ts.map +0 -1
- package/dist/lib/config.test.js +0 -126
- package/dist/lib/config.test.js.map +0 -1
- package/dist/lib/database.test.d.ts +0 -2
- package/dist/lib/database.test.d.ts.map +0 -1
- package/dist/lib/database.test.js +0 -180
- package/dist/lib/database.test.js.map +0 -1
- package/dist/lib/deep-link-payload.test.d.ts +0 -2
- package/dist/lib/deep-link-payload.test.d.ts.map +0 -1
- package/dist/lib/deep-link-payload.test.js +0 -202
- package/dist/lib/deep-link-payload.test.js.map +0 -1
- package/dist/lib/event-emitter.test.d.ts +0 -2
- package/dist/lib/event-emitter.test.d.ts.map +0 -1
- package/dist/lib/event-emitter.test.js +0 -162
- package/dist/lib/event-emitter.test.js.map +0 -1
- package/dist/lib/fingerprint.test.d.ts +0 -2
- package/dist/lib/fingerprint.test.d.ts.map +0 -1
- package/dist/lib/fingerprint.test.js +0 -750
- package/dist/lib/fingerprint.test.js.map +0 -1
- package/dist/lib/launchpad.test.d.ts +0 -2
- package/dist/lib/launchpad.test.d.ts.map +0 -1
- package/dist/lib/launchpad.test.js +0 -368
- package/dist/lib/launchpad.test.js.map +0 -1
- package/dist/lib/link-lookup.test.d.ts +0 -2
- package/dist/lib/link-lookup.test.d.ts.map +0 -1
- package/dist/lib/link-lookup.test.js +0 -61
- package/dist/lib/link-lookup.test.js.map +0 -1
- package/dist/lib/link-resolution-cache.test.d.ts +0 -2
- package/dist/lib/link-resolution-cache.test.d.ts.map +0 -1
- package/dist/lib/link-resolution-cache.test.js +0 -45
- package/dist/lib/link-resolution-cache.test.js.map +0 -1
- package/dist/lib/link-safety.test.d.ts +0 -2
- package/dist/lib/link-safety.test.d.ts.map +0 -1
- package/dist/lib/link-safety.test.js +0 -211
- package/dist/lib/link-safety.test.js.map +0 -1
- package/dist/lib/not-found-page.test.d.ts +0 -2
- package/dist/lib/not-found-page.test.d.ts.map +0 -1
- package/dist/lib/not-found-page.test.js +0 -151
- package/dist/lib/not-found-page.test.js.map +0 -1
- package/dist/lib/portable/cli.test.d.ts +0 -2
- package/dist/lib/portable/cli.test.d.ts.map +0 -1
- package/dist/lib/portable/cli.test.js +0 -98
- package/dist/lib/portable/cli.test.js.map +0 -1
- package/dist/lib/portable/portable.integration.test.d.ts +0 -2
- package/dist/lib/portable/portable.integration.test.d.ts.map +0 -1
- package/dist/lib/portable/portable.integration.test.js +0 -139
- package/dist/lib/portable/portable.integration.test.js.map +0 -1
- package/dist/lib/portable/registry.schema.test.d.ts +0 -2
- package/dist/lib/portable/registry.schema.test.d.ts.map +0 -1
- package/dist/lib/portable/registry.schema.test.js +0 -59
- package/dist/lib/portable/registry.schema.test.js.map +0 -1
- package/dist/lib/scanner-detection.test.d.ts +0 -2
- package/dist/lib/scanner-detection.test.d.ts.map +0 -1
- package/dist/lib/scanner-detection.test.js +0 -124
- package/dist/lib/scanner-detection.test.js.map +0 -1
- package/dist/lib/schema-guard.test.d.ts +0 -2
- package/dist/lib/schema-guard.test.d.ts.map +0 -1
- package/dist/lib/schema-guard.test.js +0 -83
- package/dist/lib/schema-guard.test.js.map +0 -1
- package/dist/lib/utils.test.d.ts +0 -2
- package/dist/lib/utils.test.d.ts.map +0 -1
- package/dist/lib/utils.test.js +0 -261
- package/dist/lib/utils.test.js.map +0 -1
- package/dist/lib/webhook.test.d.ts +0 -2
- package/dist/lib/webhook.test.d.ts.map +0 -1
- package/dist/lib/webhook.test.js +0 -340
- package/dist/lib/webhook.test.js.map +0 -1
- package/dist/routes/health.test.d.ts +0 -2
- package/dist/routes/health.test.d.ts.map +0 -1
- package/dist/routes/health.test.js +0 -82
- package/dist/routes/health.test.js.map +0 -1
- package/dist/routes/preview.test.d.ts +0 -2
- package/dist/routes/preview.test.d.ts.map +0 -1
- package/dist/routes/preview.test.js +0 -111
- package/dist/routes/preview.test.js.map +0 -1
- package/dist/routes/qr.test.d.ts +0 -2
- package/dist/routes/qr.test.d.ts.map +0 -1
- package/dist/routes/qr.test.js +0 -67
- package/dist/routes/qr.test.js.map +0 -1
- package/dist/routes/redirect.click-event.test.d.ts +0 -2
- package/dist/routes/redirect.click-event.test.d.ts.map +0 -1
- package/dist/routes/redirect.click-event.test.js +0 -85
- package/dist/routes/redirect.click-event.test.js.map +0 -1
- package/dist/routes/redirect.host-scope.integration.test.d.ts +0 -2
- package/dist/routes/redirect.host-scope.integration.test.d.ts.map +0 -1
- package/dist/routes/redirect.host-scope.integration.test.js +0 -105
- package/dist/routes/redirect.host-scope.integration.test.js.map +0 -1
- package/dist/routes/redirect.interstitial.test.d.ts +0 -2
- package/dist/routes/redirect.interstitial.test.d.ts.map +0 -1
- package/dist/routes/redirect.interstitial.test.js +0 -128
- package/dist/routes/redirect.interstitial.test.js.map +0 -1
- package/dist/routes/redirect.launchpad.test.d.ts +0 -2
- package/dist/routes/redirect.launchpad.test.d.ts.map +0 -1
- package/dist/routes/redirect.launchpad.test.js +0 -515
- package/dist/routes/redirect.launchpad.test.js.map +0 -1
- package/dist/routes/redirect.no-web-destination.test.d.ts +0 -2
- package/dist/routes/redirect.no-web-destination.test.d.ts.map +0 -1
- package/dist/routes/redirect.no-web-destination.test.js +0 -176
- package/dist/routes/redirect.no-web-destination.test.js.map +0 -1
- package/dist/routes/redirect.not-found.test.d.ts +0 -2
- package/dist/routes/redirect.not-found.test.d.ts.map +0 -1
- package/dist/routes/redirect.not-found.test.js +0 -280
- package/dist/routes/redirect.not-found.test.js.map +0 -1
- package/dist/routes/redirect.safety.test.d.ts +0 -2
- package/dist/routes/redirect.safety.test.d.ts.map +0 -1
- package/dist/routes/redirect.safety.test.js +0 -284
- package/dist/routes/redirect.safety.test.js.map +0 -1
- package/dist/routes/redirect.store-url.test.d.ts +0 -2
- package/dist/routes/redirect.store-url.test.d.ts.map +0 -1
- package/dist/routes/redirect.store-url.test.js +0 -239
- package/dist/routes/redirect.store-url.test.js.map +0 -1
- package/dist/routes/redirect.test.d.ts +0 -2
- package/dist/routes/redirect.test.d.ts.map +0 -1
- package/dist/routes/redirect.test.js +0 -158
- package/dist/routes/redirect.test.js.map +0 -1
- package/dist/routes/redirect.web-link.test.d.ts +0 -2
- package/dist/routes/redirect.web-link.test.d.ts.map +0 -1
- package/dist/routes/redirect.web-link.test.js +0 -138
- package/dist/routes/redirect.web-link.test.js.map +0 -1
- package/dist/routes/sdk-cache-bypass.test.d.ts +0 -2
- package/dist/routes/sdk-cache-bypass.test.d.ts.map +0 -1
- package/dist/routes/sdk-cache-bypass.test.js +0 -175
- package/dist/routes/sdk-cache-bypass.test.js.map +0 -1
- package/dist/routes/sdk.device-values.integration.test.d.ts +0 -2
- package/dist/routes/sdk.device-values.integration.test.d.ts.map +0 -1
- package/dist/routes/sdk.device-values.integration.test.js +0 -132
- package/dist/routes/sdk.device-values.integration.test.js.map +0 -1
- package/dist/routes/sdk.event.test.d.ts +0 -2
- package/dist/routes/sdk.event.test.d.ts.map +0 -1
- package/dist/routes/sdk.event.test.js +0 -284
- package/dist/routes/sdk.event.test.js.map +0 -1
- package/dist/routes/sdk.install-certainty.integration.test.d.ts +0 -2
- package/dist/routes/sdk.install-certainty.integration.test.d.ts.map +0 -1
- package/dist/routes/sdk.install-certainty.integration.test.js +0 -104
- package/dist/routes/sdk.install-certainty.integration.test.js.map +0 -1
- package/dist/routes/sdk.install-live-links.integration.test.d.ts +0 -2
- package/dist/routes/sdk.install-live-links.integration.test.d.ts.map +0 -1
- package/dist/routes/sdk.install-live-links.integration.test.js +0 -92
- package/dist/routes/sdk.install-live-links.integration.test.js.map +0 -1
- package/dist/routes/sdk.install-referrer.integration.test.d.ts +0 -2
- package/dist/routes/sdk.install-referrer.integration.test.d.ts.map +0 -1
- package/dist/routes/sdk.install-referrer.integration.test.js +0 -187
- package/dist/routes/sdk.install-referrer.integration.test.js.map +0 -1
- package/dist/routes/sdk.install-window-once.integration.test.d.ts +0 -2
- package/dist/routes/sdk.install-window-once.integration.test.d.ts.map +0 -1
- package/dist/routes/sdk.install-window-once.integration.test.js +0 -147
- package/dist/routes/sdk.install-window-once.integration.test.js.map +0 -1
- package/dist/routes/sdk.install.test.d.ts +0 -2
- package/dist/routes/sdk.install.test.d.ts.map +0 -1
- package/dist/routes/sdk.install.test.js +0 -99
- package/dist/routes/sdk.install.test.js.map +0 -1
- package/dist/routes/sdk.resolve-click.test.d.ts +0 -2
- package/dist/routes/sdk.resolve-click.test.d.ts.map +0 -1
- package/dist/routes/sdk.resolve-click.test.js +0 -79
- package/dist/routes/sdk.resolve-click.test.js.map +0 -1
- package/dist/routes/sdk.resolve-scope.test.d.ts +0 -2
- package/dist/routes/sdk.resolve-scope.test.d.ts.map +0 -1
- package/dist/routes/sdk.resolve-scope.test.js +0 -105
- package/dist/routes/sdk.resolve-scope.test.js.map +0 -1
package/llms.txt
CHANGED
|
@@ -513,7 +513,7 @@ curl "http://localhost:3000/api/links/uuid/qr?format=svg" -o qr.svg
|
|
|
513
513
|
|
|
514
514
|
### Mobile SDK Endpoints (Public)
|
|
515
515
|
|
|
516
|
-
These are called by the LinkForty mobile SDKs (`@linkforty/mobile-sdk-react-native`, `@linkforty/mobile-sdk-expo`, iOS SDK, Android SDK).
|
|
516
|
+
These are called by the LinkForty mobile SDKs (`@linkforty/mobile-sdk-react-native`, `@linkforty/mobile-sdk-expo`, `linkforty_flutter`, iOS SDK, Android SDK).
|
|
517
517
|
|
|
518
518
|
#### POST /api/sdk/v1/install — Report App Install
|
|
519
519
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linkforty/core",
|
|
3
|
-
"version": "1.44.
|
|
3
|
+
"version": "1.44.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Open-source deeplink management engine with device detection and analytics",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -91,6 +91,7 @@
|
|
|
91
91
|
},
|
|
92
92
|
"files": [
|
|
93
93
|
"dist",
|
|
94
|
+
"!dist/**/*.test.*",
|
|
94
95
|
"migrations",
|
|
95
96
|
"README.md",
|
|
96
97
|
"LICENSE",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.config.test.d.ts","sourceRoot":"","sources":["../src/index.config.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* createServer() hands linkforty.config.json to the redirect routes.
|
|
3
|
-
*
|
|
4
|
-
* The real server with only the data layer mocked: what matters is that a
|
|
5
|
-
* config object from loadConfig() reaches the pages a visitor sees, and that
|
|
6
|
-
* options passed in code still take precedence over it.
|
|
7
|
-
*/
|
|
8
|
-
import { describe, it, expect, vi, afterEach } from 'vitest';
|
|
9
|
-
// vi.mock is hoisted above these imports, so index.js still receives the mock.
|
|
10
|
-
import { createServer, parseConfig } from './index.js';
|
|
11
|
-
const query = vi.fn();
|
|
12
|
-
vi.mock('./lib/database.js', () => ({
|
|
13
|
-
SCHEMA_INIT_LOCK_KEY: 0,
|
|
14
|
-
db: { query: (...args) => query(...args) },
|
|
15
|
-
initializeDatabase: vi.fn(async () => undefined),
|
|
16
|
-
}));
|
|
17
|
-
const DESKTOP_UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36';
|
|
18
|
-
function mockLink(row) {
|
|
19
|
-
query.mockReset();
|
|
20
|
-
query.mockImplementation(async (sql) => {
|
|
21
|
-
if (/FROM links l/i.test(sql))
|
|
22
|
-
return { rows: row ? [row] : [], rowCount: row ? 1 : 0 };
|
|
23
|
-
return { rows: [], rowCount: 0 };
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
const link = {
|
|
27
|
-
id: '00000000-0000-0000-0000-0000000000aa',
|
|
28
|
-
short_code: 'abc123',
|
|
29
|
-
original_url: null,
|
|
30
|
-
web_fallback_url: 'https://example.com/page',
|
|
31
|
-
ios_app_store_url: 'https://apps.apple.com/app/id1',
|
|
32
|
-
android_app_store_url: null,
|
|
33
|
-
launchpad_mode: 'inherit',
|
|
34
|
-
is_active: true,
|
|
35
|
-
targeting_rules: null,
|
|
36
|
-
org_settings: null,
|
|
37
|
-
};
|
|
38
|
-
let server;
|
|
39
|
-
afterEach(async () => {
|
|
40
|
-
await new Promise((r) => setImmediate(r));
|
|
41
|
-
await server?.close();
|
|
42
|
-
server = undefined;
|
|
43
|
-
});
|
|
44
|
-
const visit = (url) => server.inject({ method: 'GET', url, headers: { 'user-agent': DESKTOP_UA, accept: 'text/html' } });
|
|
45
|
-
describe('createServer config', () => {
|
|
46
|
-
it('serves the not-found page and Launchpad from the config file', async () => {
|
|
47
|
-
const config = parseConfig({
|
|
48
|
-
notFound: { title: 'Nothing here' },
|
|
49
|
-
launchpad: { desktop: 'always', appName: 'Instance App' },
|
|
50
|
-
});
|
|
51
|
-
server = await createServer({ logger: false, config });
|
|
52
|
-
mockLink(null);
|
|
53
|
-
expect((await visit('/nosuchcode')).body).toContain('<h1>Nothing here</h1>');
|
|
54
|
-
mockLink(link);
|
|
55
|
-
const page = await visit('/abc123');
|
|
56
|
-
expect(page.statusCode).toBe(200);
|
|
57
|
-
expect(page.body).toContain('<span>Instance App</span>');
|
|
58
|
-
});
|
|
59
|
-
it('keeps hooks passed in code alongside the config defaults', async () => {
|
|
60
|
-
const resolveContent = vi.fn().mockReturnValue(null);
|
|
61
|
-
server = await createServer({
|
|
62
|
-
logger: false,
|
|
63
|
-
config: parseConfig({ launchpad: { desktop: 'always' } }),
|
|
64
|
-
redirect: { launchpad: { resolveContent } },
|
|
65
|
-
});
|
|
66
|
-
mockLink(link);
|
|
67
|
-
expect((await visit('/abc123')).statusCode).toBe(200);
|
|
68
|
-
expect(resolveContent).toHaveBeenCalledOnce();
|
|
69
|
-
});
|
|
70
|
-
it('lets options in code take precedence over the file', async () => {
|
|
71
|
-
server = await createServer({
|
|
72
|
-
logger: false,
|
|
73
|
-
config: parseConfig({ notFound: { title: 'From file' }, launchpad: { desktop: 'always' } }),
|
|
74
|
-
redirect: { notFoundDefaults: { title: 'From code' }, launchpad: { defaults: { desktop: 'off' } } },
|
|
75
|
-
});
|
|
76
|
-
mockLink(null);
|
|
77
|
-
expect((await visit('/nosuchcode')).body).toContain('<h1>From code</h1>');
|
|
78
|
-
mockLink(link);
|
|
79
|
-
expect((await visit('/abc123')).statusCode).toBe(302);
|
|
80
|
-
});
|
|
81
|
-
it('behaves as before without a config', async () => {
|
|
82
|
-
server = await createServer({ logger: false });
|
|
83
|
-
mockLink(link);
|
|
84
|
-
expect((await visit('/abc123')).statusCode).toBe(302);
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
//# sourceMappingURL=index.config.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.config.test.js","sourceRoot":"","sources":["../src/index.config.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"bot-detection.test.d.ts","sourceRoot":"","sources":["../../src/lib/bot-detection.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, afterEach } from 'vitest';
|
|
2
|
-
import { classifyBot, edgeBotSignal, isSdkRequest } from './bot-detection.js';
|
|
3
|
-
const CHROME = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36';
|
|
4
|
-
describe('classifyBot', () => {
|
|
5
|
-
it('flags known crawlers/scrapers by user-agent', () => {
|
|
6
|
-
expect(classifyBot('Googlebot/2.1 (+http://www.google.com/bot.html)', 'GET')).toEqual({ isBot: true, reason: 'ua' });
|
|
7
|
-
expect(classifyBot('facebookexternalhit/1.1', 'GET').isBot).toBe(true);
|
|
8
|
-
expect(classifyBot('curl/8.4.0', 'GET').isBot).toBe(true);
|
|
9
|
-
});
|
|
10
|
-
it('does not flag a normal browser', () => {
|
|
11
|
-
expect(classifyBot(CHROME, 'GET')).toEqual({ isBot: false, reason: null });
|
|
12
|
-
});
|
|
13
|
-
it('flags HEAD/OPTIONS as non-human, case-insensitively', () => {
|
|
14
|
-
expect(classifyBot(CHROME, 'HEAD')).toEqual({ isBot: true, reason: 'method' });
|
|
15
|
-
expect(classifyBot(CHROME, 'options')).toEqual({ isBot: true, reason: 'method' });
|
|
16
|
-
});
|
|
17
|
-
it('honors the edge signal with highest authority', () => {
|
|
18
|
-
// Edge wins even for a normal browser UA on a GET.
|
|
19
|
-
expect(classifyBot(CHROME, 'GET', true)).toEqual({ isBot: true, reason: 'edge' });
|
|
20
|
-
// An explicit false edge signal does not override real UA/method detection.
|
|
21
|
-
expect(classifyBot('Googlebot/2.1', 'GET', false)).toEqual({ isBot: true, reason: 'ua' });
|
|
22
|
-
});
|
|
23
|
-
it('does not flag an SDK request by its HTTP library user-agent', () => {
|
|
24
|
-
expect(classifyBot('okhttp/4.12.0', 'GET')).toEqual({ isBot: true, reason: 'ua' });
|
|
25
|
-
expect(classifyBot('okhttp/4.12.0', 'GET', undefined, true)).toEqual({ isBot: false, reason: null });
|
|
26
|
-
expect(classifyBot('okhttp/5.5.0', 'GET', undefined, true).isBot).toBe(false);
|
|
27
|
-
});
|
|
28
|
-
it('still applies the method and edge signals to SDK requests', () => {
|
|
29
|
-
expect(classifyBot('okhttp/4.12.0', 'HEAD', undefined, true)).toEqual({ isBot: true, reason: 'method' });
|
|
30
|
-
expect(classifyBot('okhttp/4.12.0', 'GET', true, true)).toEqual({ isBot: true, reason: 'edge' });
|
|
31
|
-
});
|
|
32
|
-
it('handles a missing/empty user-agent', () => {
|
|
33
|
-
expect(classifyBot(undefined, 'GET')).toEqual({ isBot: false, reason: null });
|
|
34
|
-
expect(classifyBot('', 'GET').isBot).toBe(false);
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
describe('isSdkRequest', () => {
|
|
38
|
-
it('is true when X-LinkForty-SDK names an SDK', () => {
|
|
39
|
-
expect(isSdkRequest({ 'x-linkforty-sdk': 'android/1.4.0' })).toBe(true);
|
|
40
|
-
expect(isSdkRequest({ 'x-linkforty-sdk': ['react-native/1.6.2'] })).toBe(true);
|
|
41
|
-
});
|
|
42
|
-
it('is false when the header is missing or empty', () => {
|
|
43
|
-
expect(isSdkRequest({})).toBe(false);
|
|
44
|
-
expect(isSdkRequest({ 'x-linkforty-sdk': '' })).toBe(false);
|
|
45
|
-
expect(isSdkRequest({ 'x-linkforty-sdk': ' ' })).toBe(false);
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
describe('edgeBotSignal', () => {
|
|
49
|
-
afterEach(() => {
|
|
50
|
-
delete process.env.TRUST_EDGE_BOT_HEADER;
|
|
51
|
-
});
|
|
52
|
-
it('is ignored unless TRUST_EDGE_BOT_HEADER=true', () => {
|
|
53
|
-
expect(edgeBotSignal('1')).toBeUndefined();
|
|
54
|
-
});
|
|
55
|
-
it('parses the header only when trusted', () => {
|
|
56
|
-
process.env.TRUST_EDGE_BOT_HEADER = 'true';
|
|
57
|
-
expect(edgeBotSignal('1')).toBe(true);
|
|
58
|
-
expect(edgeBotSignal('true')).toBe(true);
|
|
59
|
-
expect(edgeBotSignal('TRUE')).toBe(true);
|
|
60
|
-
expect(edgeBotSignal('0')).toBe(false);
|
|
61
|
-
expect(edgeBotSignal(['1'])).toBe(true);
|
|
62
|
-
expect(edgeBotSignal(undefined)).toBeUndefined();
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
//# sourceMappingURL=bot-detection.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"bot-detection.test.js","sourceRoot":"","sources":["../../src/lib/bot-detection.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"client-ip.test.d.ts","sourceRoot":"","sources":["../../src/lib/client-ip.test.ts"],"names":[],"mappings":"AAIA,OAAO,CAAC,MAAM,CAAC;IACb,IAAI,4BAA4B,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CAChE"}
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, afterEach } from 'vitest';
|
|
2
|
-
import { getClientIp } from './client-ip';
|
|
3
|
-
describe('getClientIp', () => {
|
|
4
|
-
it('returns request.ip when set', () => {
|
|
5
|
-
const request = { ip: '192.168.1.1' };
|
|
6
|
-
expect(getClientIp(request)).toBe('192.168.1.1');
|
|
7
|
-
});
|
|
8
|
-
it('returns raw.socket.remoteAddress when request.ip is undefined', () => {
|
|
9
|
-
const request = {
|
|
10
|
-
ip: undefined,
|
|
11
|
-
raw: { socket: { remoteAddress: '10.0.0.2' } },
|
|
12
|
-
};
|
|
13
|
-
expect(getClientIp(request)).toBe('10.0.0.2');
|
|
14
|
-
});
|
|
15
|
-
it('unwraps IPv6-mapped IPv4', () => {
|
|
16
|
-
const request = { ip: '::ffff:192.168.1.1' };
|
|
17
|
-
expect(getClientIp(request)).toBe('192.168.1.1');
|
|
18
|
-
});
|
|
19
|
-
it('returns empty string when neither ip nor socket.remoteAddress is available', () => {
|
|
20
|
-
const request = { ip: undefined, raw: { socket: {} } };
|
|
21
|
-
expect(getClientIp(request)).toBe('');
|
|
22
|
-
});
|
|
23
|
-
it('returns empty string when request.raw has no socket', () => {
|
|
24
|
-
const request = { ip: undefined, raw: {} };
|
|
25
|
-
expect(getClientIp(request)).toBe('');
|
|
26
|
-
});
|
|
27
|
-
});
|
|
28
|
-
describe('getClientIp with TRUSTED_CLIENT_IP_HEADER', () => {
|
|
29
|
-
const ORIGINAL = process.env.TRUSTED_CLIENT_IP_HEADER;
|
|
30
|
-
afterEach(() => {
|
|
31
|
-
if (ORIGINAL === undefined)
|
|
32
|
-
delete process.env.TRUSTED_CLIENT_IP_HEADER;
|
|
33
|
-
else
|
|
34
|
-
process.env.TRUSTED_CLIENT_IP_HEADER = ORIGINAL;
|
|
35
|
-
});
|
|
36
|
-
it('prefers the configured header over request.ip', () => {
|
|
37
|
-
process.env.TRUSTED_CLIENT_IP_HEADER = 'cf-connecting-ip';
|
|
38
|
-
const request = {
|
|
39
|
-
ip: '162.158.23.75', // a Cloudflare edge IP
|
|
40
|
-
headers: { 'cf-connecting-ip': '203.0.113.50' },
|
|
41
|
-
};
|
|
42
|
-
expect(getClientIp(request)).toBe('203.0.113.50');
|
|
43
|
-
});
|
|
44
|
-
it('falls back to request.ip when the configured header is absent', () => {
|
|
45
|
-
process.env.TRUSTED_CLIENT_IP_HEADER = 'cf-connecting-ip';
|
|
46
|
-
const request = { ip: '203.0.113.50', headers: {} };
|
|
47
|
-
expect(getClientIp(request)).toBe('203.0.113.50');
|
|
48
|
-
});
|
|
49
|
-
it('takes the first entry of a comma-separated header value', () => {
|
|
50
|
-
process.env.TRUSTED_CLIENT_IP_HEADER = 'true-client-ip';
|
|
51
|
-
const request = {
|
|
52
|
-
ip: '10.0.0.1',
|
|
53
|
-
headers: { 'true-client-ip': '203.0.113.50, 70.0.0.1' },
|
|
54
|
-
};
|
|
55
|
-
expect(getClientIp(request)).toBe('203.0.113.50');
|
|
56
|
-
});
|
|
57
|
-
it('ignores the header when the env var is unset (default behavior)', () => {
|
|
58
|
-
delete process.env.TRUSTED_CLIENT_IP_HEADER;
|
|
59
|
-
const request = {
|
|
60
|
-
ip: '203.0.113.50',
|
|
61
|
-
headers: { 'cf-connecting-ip': '1.2.3.4' },
|
|
62
|
-
};
|
|
63
|
-
expect(getClientIp(request)).toBe('203.0.113.50');
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
describe('getClientIp with Fastify trustProxy (proxied request)', () => {
|
|
67
|
-
it('uses X-Forwarded-For when trustProxy is true', async () => {
|
|
68
|
-
const Fastify = (await import('fastify')).default;
|
|
69
|
-
const { getClientIp: getIp } = await import('./client-ip.js');
|
|
70
|
-
const app = Fastify({ trustProxy: true });
|
|
71
|
-
app.get('/ip', async (request, reply) => {
|
|
72
|
-
return reply.send({ ip: getIp(request) });
|
|
73
|
-
});
|
|
74
|
-
const res = await app.inject({
|
|
75
|
-
method: 'GET',
|
|
76
|
-
url: '/ip',
|
|
77
|
-
headers: { 'x-forwarded-for': '203.0.113.50' },
|
|
78
|
-
});
|
|
79
|
-
expect(res.statusCode).toBe(200);
|
|
80
|
-
const body = res.json();
|
|
81
|
-
expect(body.ip).toBe('203.0.113.50');
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
vi.mock('./fingerprint.js', async (importOriginal) => {
|
|
85
|
-
const mod = (await importOriginal());
|
|
86
|
-
return {
|
|
87
|
-
...mod,
|
|
88
|
-
recordInstallEvent: vi.fn().mockImplementation(async (data) => {
|
|
89
|
-
globalThis.__capturedInstallFingerprint = data;
|
|
90
|
-
return { installId: 'test-id', match: null, deepLinkData: null };
|
|
91
|
-
}),
|
|
92
|
-
};
|
|
93
|
-
});
|
|
94
|
-
describe('SDK install does not trust client-provided ipAddress', () => {
|
|
95
|
-
it('uses connection/proxy IP for attribution, not body ipAddress', async () => {
|
|
96
|
-
globalThis.__capturedInstallFingerprint = null;
|
|
97
|
-
const Fastify = (await import('fastify')).default;
|
|
98
|
-
const { sdkRoutes } = await import('../routes/sdk.js');
|
|
99
|
-
const app = Fastify({ trustProxy: true });
|
|
100
|
-
await app.register(sdkRoutes);
|
|
101
|
-
const res = await app.inject({
|
|
102
|
-
method: 'POST',
|
|
103
|
-
url: '/api/sdk/v1/install',
|
|
104
|
-
payload: { ipAddress: '1.2.3.4', userAgent: 'Mozilla/5.0 Test' },
|
|
105
|
-
headers: { 'x-forwarded-for': '203.0.113.50', 'content-type': 'application/json' },
|
|
106
|
-
});
|
|
107
|
-
expect(res.statusCode).toBe(200);
|
|
108
|
-
expect(globalThis.__capturedInstallFingerprint).not.toBeNull();
|
|
109
|
-
expect(globalThis.__capturedInstallFingerprint.ipAddress).toBe('203.0.113.50');
|
|
110
|
-
const body = res.json();
|
|
111
|
-
expect(body.clientReportedIp).toBe('1.2.3.4');
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
//# sourceMappingURL=client-ip.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"client-ip.test.js","sourceRoot":"","sources":["../../src/lib/client-ip.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"config.test.d.ts","sourceRoot":"","sources":["../../src/lib/config.test.ts"],"names":[],"mappings":""}
|
package/dist/lib/config.test.js
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { mkdtempSync, rmSync, writeFileSync } from 'fs';
|
|
3
|
-
import { tmpdir } from 'os';
|
|
4
|
-
import { join } from 'path';
|
|
5
|
-
import { ConfigError, loadConfig, parseConfig } from './config.js';
|
|
6
|
-
let dir;
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
dir = mkdtempSync(join(tmpdir(), 'linkforty-config-'));
|
|
9
|
-
});
|
|
10
|
-
afterEach(() => {
|
|
11
|
-
rmSync(dir, { recursive: true, force: true });
|
|
12
|
-
});
|
|
13
|
-
function write(name, contents) {
|
|
14
|
-
const path = join(dir, name);
|
|
15
|
-
writeFileSync(path, typeof contents === 'string' ? contents : JSON.stringify(contents));
|
|
16
|
-
return path;
|
|
17
|
-
}
|
|
18
|
-
function errorFrom(fn) {
|
|
19
|
-
try {
|
|
20
|
-
fn();
|
|
21
|
-
}
|
|
22
|
-
catch (error) {
|
|
23
|
-
expect(error).toBeInstanceOf(ConfigError);
|
|
24
|
-
return error;
|
|
25
|
-
}
|
|
26
|
-
throw new Error('expected a ConfigError');
|
|
27
|
-
}
|
|
28
|
-
describe('loadConfig', () => {
|
|
29
|
-
it('uses the defaults when there is no file', () => {
|
|
30
|
-
expect(loadConfig({ cwd: dir, env: {} })).toEqual({ config: {}, path: null });
|
|
31
|
-
});
|
|
32
|
-
it('reads linkforty.config.json from the working directory', () => {
|
|
33
|
-
const path = write('linkforty.config.json', { notFound: { title: 'Gone' } });
|
|
34
|
-
expect(loadConfig({ cwd: dir, env: {} })).toEqual({ config: { notFound: { title: 'Gone' } }, path });
|
|
35
|
-
});
|
|
36
|
-
it('reads the file named by LINKFORTY_CONFIG', () => {
|
|
37
|
-
const path = write('custom.json', { notFound: { redirectUrl: 'https://example.com/' } });
|
|
38
|
-
expect(loadConfig({ cwd: '/', env: { LINKFORTY_CONFIG: path } }).config).toEqual({
|
|
39
|
-
notFound: { redirectUrl: 'https://example.com/' },
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
it('fails when a named file is missing', () => {
|
|
43
|
-
const error = errorFrom(() => loadConfig({ cwd: dir, env: { LINKFORTY_CONFIG: 'missing.json' } }));
|
|
44
|
-
expect(error.message).toContain('missing.json: file not found');
|
|
45
|
-
});
|
|
46
|
-
it('fails on invalid JSON', () => {
|
|
47
|
-
write('linkforty.config.json', '{ "notFound": { title: "x" } }');
|
|
48
|
-
expect(errorFrom(() => loadConfig({ cwd: dir, env: {} })).message).toContain('not valid JSON');
|
|
49
|
-
});
|
|
50
|
-
it('trims values', () => {
|
|
51
|
-
write('linkforty.config.json', { notFound: { title: ' Gone ' } });
|
|
52
|
-
expect(loadConfig({ cwd: dir, env: {} }).config.notFound?.title).toBe('Gone');
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
describe('parseConfig', () => {
|
|
56
|
-
it('accepts every notFound setting', () => {
|
|
57
|
-
const notFound = {
|
|
58
|
-
redirectUrl: 'https://example.com/',
|
|
59
|
-
title: 'Gone',
|
|
60
|
-
message: 'Try the homepage.',
|
|
61
|
-
logoUrl: 'https://cdn.example.com/logo.png',
|
|
62
|
-
accentColor: '#0f766e',
|
|
63
|
-
backgroundColor: '#f6f7f8',
|
|
64
|
-
buttonLabel: 'Home',
|
|
65
|
-
buttonUrl: 'https://example.com/',
|
|
66
|
-
};
|
|
67
|
-
expect(parseConfig({ notFound })).toEqual({ notFound });
|
|
68
|
-
});
|
|
69
|
-
it.each([
|
|
70
|
-
[{ notFound: { redirectUrl: 'javascript:alert(1)' } }, 'notFound.redirectUrl: must be an http(s) URL'],
|
|
71
|
-
[{ notFound: { accentColor: 'teal' } }, 'notFound.accentColor: must be a hex color'],
|
|
72
|
-
[{ notFound: { buttonLabel: 'Home' } }, 'buttonLabel and buttonUrl must be set together'],
|
|
73
|
-
[{ notFound: { titel: 'Gone' } }, 'notFound: unknown setting "titel"'],
|
|
74
|
-
[{ notfound: {} }, 'unknown setting "notfound"'],
|
|
75
|
-
[{ notFound: { title: 42 } }, 'notFound.title'],
|
|
76
|
-
[{ notFound: { title: 'x'.repeat(121) } }, 'notFound.title'],
|
|
77
|
-
])('names the bad setting: %j', (raw, expected) => {
|
|
78
|
-
const error = errorFrom(() => parseConfig(raw, 'linkforty.config.json'));
|
|
79
|
-
expect(error.message).toContain('linkforty.config.json: ');
|
|
80
|
-
expect(error.message).toContain(expected);
|
|
81
|
-
});
|
|
82
|
-
it('accepts every launchpad setting', () => {
|
|
83
|
-
const launchpad = {
|
|
84
|
-
desktop: 'always',
|
|
85
|
-
mobile: 'page',
|
|
86
|
-
appName: 'Example App',
|
|
87
|
-
appIconUrl: 'https://cdn.example.com/icon.png',
|
|
88
|
-
appWebsiteUrl: 'https://example.com',
|
|
89
|
-
accentColor: '#0f766e',
|
|
90
|
-
backgroundColor: '#101418',
|
|
91
|
-
};
|
|
92
|
-
expect(parseConfig({ launchpad })).toEqual({ launchpad });
|
|
93
|
-
});
|
|
94
|
-
it.each([
|
|
95
|
-
[{ launchpad: { desktop: 'sometimes' } }, 'launchpad.desktop'],
|
|
96
|
-
[{ launchpad: { accentColor: '#abc' } }, 'launchpad.accentColor: must be a six-digit hex color'],
|
|
97
|
-
[{ launchpad: { appIconUrl: 'ftp://cdn.example/icon.png' } }, 'launchpad.appIconUrl: must be an http(s) URL'],
|
|
98
|
-
[{ launchpad: { templateId: 'hero-1' } }, 'launchpad: unknown setting "templateId"'],
|
|
99
|
-
[{ launchpad: { appname: 'x' } }, 'launchpad: unknown setting "appname"'],
|
|
100
|
-
[{ launchpad: { appName: 'x'.repeat(61) } }, 'launchpad.appName'],
|
|
101
|
-
])('names the bad launchpad setting: %j', (raw, expected) => {
|
|
102
|
-
expect(errorFrom(() => parseConfig(raw)).message).toContain(expected);
|
|
103
|
-
});
|
|
104
|
-
it('rejects a file that is not an object', () => {
|
|
105
|
-
expect(() => parseConfig([])).toThrow(ConfigError);
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
describe('botDetection', () => {
|
|
109
|
-
it('accepts a list of scanner networks', () => {
|
|
110
|
-
expect(parseConfig({ botDetection: { emailScannerAsns: [8075, 16509] } })).toEqual({
|
|
111
|
-
botDetection: { emailScannerAsns: [8075, 16509] },
|
|
112
|
-
});
|
|
113
|
-
expect(parseConfig({ botDetection: { emailScannerAsns: [] } })).toEqual({ botDetection: { emailScannerAsns: [] } });
|
|
114
|
-
});
|
|
115
|
-
it('rejects networks iCloud Private Relay exits through', () => {
|
|
116
|
-
const error = errorFrom(() => parseConfig({ botDetection: { emailScannerAsns: [8075, 13335] } }));
|
|
117
|
-
expect(error.message).toContain('botDetection.emailScannerAsns');
|
|
118
|
-
expect(error.message).toContain('Private Relay');
|
|
119
|
-
});
|
|
120
|
-
it('rejects malformed ASNs and unknown keys', () => {
|
|
121
|
-
expect(() => parseConfig({ botDetection: { emailScannerAsns: ['8075'] } })).toThrow(ConfigError);
|
|
122
|
-
expect(() => parseConfig({ botDetection: { emailScannerAsns: [-1] } })).toThrow(ConfigError);
|
|
123
|
-
expect(() => parseConfig({ botDetection: { scanners: true } })).toThrow(ConfigError);
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
//# sourceMappingURL=config.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"config.test.js","sourceRoot":"","sources":["../../src/lib/config.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"database.test.d.ts","sourceRoot":"","sources":["../../src/lib/database.test.ts"],"names":[],"mappings":""}
|