@zeroad.network/token 0.13.6 → 0.13.8
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 +79 -67
- package/dist/browser-BiNZ2c6t.d.cts +47 -0
- package/dist/browser-BiNZ2c6t.d.mts +47 -0
- package/dist/browser-CMCq-bkd.cjs +132 -0
- package/dist/browser-Citg0bh9.mjs +116 -0
- package/dist/browser.d.mts +1 -54
- package/dist/browser.mjs +1 -1
- package/dist/index.cjs +80 -81
- package/dist/index.d.cts +29 -18
- package/dist/index.d.mts +29 -18
- package/dist/index.mjs +78 -82
- package/package.json +2 -2
- package/dist/browser-C2zgnAdK.cjs +0 -171
- package/dist/browser-DDeUUPbU.mjs +0 -156
- package/dist/browser.d.cts +0 -54
package/README.md
CHANGED
|
@@ -1,75 +1,82 @@
|
|
|
1
1
|
# Introduction
|
|
2
2
|
|
|
3
|
-
This NPM module is
|
|
3
|
+
This NPM module is designed for sites running Node.js, Bun, or Deno that participate in the [Zero Ad Network](https://zeroad.network) program.
|
|
4
4
|
|
|
5
|
-
The `@zeroad.network/token` module
|
|
5
|
+
The `@zeroad.network/token` module is a lightweight, TypeScript-ready, open-source, and fully tested HTTP-header-based "access/entitlement token" library with no production dependencies.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
For detailed guides and implementation instructions, see the [official Zero Ad Network documentation](https://docs.zeroad.network).
|
|
8
8
|
|
|
9
|
-
## Runtime
|
|
9
|
+
## Runtime Compatibility
|
|
10
10
|
|
|
11
|
-
| Runtime |
|
|
12
|
-
| :------ |
|
|
13
|
-
| Node.js | 16+
|
|
14
|
-
| Bun | 1.1.0+
|
|
15
|
-
| Deno | 2.0.0+
|
|
11
|
+
| Runtime | Version | ESM | CJS |
|
|
12
|
+
| :------ | :------ | :-: | :-: |
|
|
13
|
+
| Node.js | 16+ | ✅ | ✅ |
|
|
14
|
+
| Bun | 1.1.0+ | ✅ | ✅ |
|
|
15
|
+
| Deno | 2.0.0+ | ✅ | ✅ |
|
|
16
16
|
|
|
17
17
|
## Purpose
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
The module helps developers to:
|
|
20
|
+
|
|
21
|
+
- Generate a valid "Welcome Header" when `clientId` and `features` are provided.
|
|
22
|
+
- Inject a valid site's HTTP Response Header (**Welcome Header**) into every endpoint. Example:
|
|
20
23
|
|
|
21
|
-
- Inject a valid site's HTTP Response Header known as "Welcome Header" to every endpoint. An example:
|
|
22
24
|
```http
|
|
23
|
-
X-Better-Web-Welcome: "
|
|
25
|
+
X-Better-Web-Welcome: "Z2CclA8oXIT1e0QmqTWF8w^1^3"
|
|
24
26
|
```
|
|
25
|
-
|
|
27
|
+
|
|
28
|
+
- Detect and parse Zero Ad Network user tokens sent via HTTP Request Header. Example:
|
|
29
|
+
|
|
26
30
|
```http
|
|
27
31
|
X-Better-Web-Hello: "Aav2IXRoh0oKBw==.2yZfC2/pM9DWfgX+von4IgWLmN9t67HJHLiee/gx4+pFIHHurwkC3PCHT1Kaz0yUhx3crUaxST+XLlRtJYacAQ=="
|
|
28
32
|
```
|
|
29
|
-
- If found, parse the token from the HTTP Request Header value and verify its integrity.
|
|
30
|
-
- (Optionally) Generate a valid "Welcome Header" value when `siteId` UUID and site `features` array are provided.
|
|
31
33
|
|
|
32
|
-
|
|
34
|
+
- Verify client token integrity locally.
|
|
33
35
|
|
|
34
|
-
|
|
36
|
+
## Implementation Details
|
|
35
37
|
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
38
|
+
- Uses `node:crypto` to verify token signatures with Zero Ad Network's public ED25519 key.
|
|
39
|
+
- Decodes token payload to extract protocol version, expiration timestamp, and site features.
|
|
40
|
+
- Generates a feature map; expired tokens produce all flags as `false`.
|
|
39
41
|
|
|
40
|
-
Parsed token
|
|
42
|
+
Parsed token example:
|
|
41
43
|
|
|
42
44
|
```js
|
|
43
45
|
{
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
HIDE_ADVERTISEMENTS: boolean,
|
|
47
|
+
HIDE_COOKIE_CONSENT_SCREEN: boolean,
|
|
48
|
+
HIDE_MARKETING_DIALOGS: boolean,
|
|
49
|
+
DISABLE_NON_FUNCTIONAL_TRACKING: boolean,
|
|
50
|
+
DISABLE_CONTENT_PAYWALL: boolean,
|
|
51
|
+
ENABLE_SUBSCRIPTION_ACCESS: boolean,
|
|
49
52
|
};
|
|
50
53
|
```
|
|
51
54
|
|
|
52
|
-
|
|
55
|
+
- Verification occurs locally; no data leaves your server.
|
|
56
|
+
- Parsing and verification adds roughly 0.06ms–0.6ms to endpoint execution time (tested on M1 MacBook Pro). Performance may vary.
|
|
57
|
+
- Redis caching tests show local verification is faster than retrieving cached results.
|
|
53
58
|
|
|
54
|
-
|
|
59
|
+
## Benefits of Joining
|
|
55
60
|
|
|
56
|
-
|
|
61
|
+
Partnering with Zero Ad Network allows your site to:
|
|
57
62
|
|
|
58
|
-
|
|
63
|
+
- Generate a new revenue stream by:
|
|
64
|
+
- Providing a clean, unobstructed user experience
|
|
65
|
+
- Removing paywalls and enabling free access to your base subscription plan
|
|
66
|
+
- Or both combined
|
|
67
|
+
- Contribute to a truly joyful, user-friendly internet experience
|
|
59
68
|
|
|
60
|
-
|
|
69
|
+
## Onboarding Your Site
|
|
61
70
|
|
|
62
|
-
|
|
71
|
+
1. [Sign up](https://zeroad.network/login) with Zero Ad Network.
|
|
72
|
+
2. [Register your site](https://zeroad.network/publisher/sites/add) to receive your unique `X-Better-Web-Welcome` header.
|
|
63
73
|
|
|
64
|
-
|
|
74
|
+
Your site must include this header on all publicly accessible HTML or RESTful endpoints so that Zero Ad Network users’ browser extensions can recognize participation.
|
|
65
75
|
|
|
66
|
-
|
|
76
|
+
## Module Installation
|
|
67
77
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
## Module installation
|
|
71
|
-
|
|
72
|
-
Great news for TypeScript enjoyers, the module is written purely in TypeScript, hence all types and interfaces are readily available. The module works well in EcmaScript (`import {} from ""`) and CommonJS `const {} = require("")` environments. If unsure - prefer ESM when possible.
|
|
78
|
+
- Written entirely in TypeScript with full types and interfaces.
|
|
79
|
+
- Supports both ESM (`import`) and CommonJS (`require`). ESM is recommended when possible.
|
|
73
80
|
|
|
74
81
|
To install the module use your favourite package manager:
|
|
75
82
|
|
|
@@ -92,15 +99,15 @@ deno add npm:@zeroad.network/token
|
|
|
92
99
|
|
|
93
100
|
## Examples
|
|
94
101
|
|
|
95
|
-
|
|
102
|
+
For more example implementations using `Express.js` (JavaScript), `Hono`, and `Fastify` (TypeScript), visit the [examples section on our GitHub repository](https://github.com/laurynas-karvelis/zeroad-token-typescript/tree/main/examples/).
|
|
96
103
|
|
|
97
|
-
|
|
104
|
+
The following JavaScript example provides a quick reference, demonstrating how to:
|
|
98
105
|
|
|
99
|
-
- Inject the "Welcome Header" into
|
|
100
|
-
- Parse user's token from
|
|
101
|
-
- Use the `tokenContext`
|
|
106
|
+
- Inject the "Welcome Header" into responses
|
|
107
|
+
- Parse the user's token from the request header
|
|
108
|
+
- Use the `tokenContext` in controllers and templates
|
|
102
109
|
|
|
103
|
-
|
|
110
|
+
Minimal Express.js v5 app example:
|
|
104
111
|
|
|
105
112
|
```js
|
|
106
113
|
import express from "express";
|
|
@@ -108,42 +115,47 @@ import { Site } from "@zeroad.network/token";
|
|
|
108
115
|
|
|
109
116
|
const app = express();
|
|
110
117
|
|
|
111
|
-
// Initialize
|
|
112
|
-
//
|
|
113
|
-
const
|
|
114
|
-
const site = Site(
|
|
118
|
+
// Initialize the Zero Ad Network module at app startup.
|
|
119
|
+
// Your site's `clientId` value is obtained during site registration on the Zero Ad Network platform (https://zeroad.network).
|
|
120
|
+
const ZERO_AD_NETWORK_CLIENT_ID = "Z2CclA8oXIT1e0QmqTWF8w";
|
|
121
|
+
const site = Site({
|
|
122
|
+
clientId: ZERO_AD_NETWORK_CLIENT_ID,
|
|
123
|
+
features: [FEATURES.CLEAN_WEB, FEATURES.ONE_PASS],
|
|
124
|
+
});
|
|
115
125
|
|
|
116
126
|
app
|
|
117
|
-
//
|
|
127
|
+
// Apply middleware for all routes
|
|
118
128
|
.use((req, res, next) => {
|
|
119
|
-
// Inject "X-Better-Web-Welcome" header
|
|
129
|
+
// Inject the "X-Better-Web-Welcome" header into the response
|
|
120
130
|
res.set(site.SERVER_HEADER_NAME, site.SERVER_HEADER_VALUE);
|
|
121
131
|
|
|
122
|
-
// Parse the
|
|
123
|
-
//
|
|
124
|
-
req.tokenContext = site.
|
|
132
|
+
// Parse the incoming user token from the request header
|
|
133
|
+
// Attach the parsed token data to the request object for downstream use
|
|
134
|
+
req.tokenContext = site.parseClientToken(req.get(site.CLIENT_HEADER_NAME));
|
|
125
135
|
|
|
126
136
|
next();
|
|
127
137
|
})
|
|
128
138
|
.get("/", (req, res) => {
|
|
129
|
-
//
|
|
139
|
+
// Access parsed token data for this request
|
|
130
140
|
console.log(req.tokenContext);
|
|
131
141
|
|
|
132
|
-
//
|
|
142
|
+
// Example structure of tokenContext:
|
|
133
143
|
req.tokenContext = {
|
|
134
|
-
// If
|
|
135
|
-
|
|
136
|
-
// If
|
|
137
|
-
|
|
138
|
-
// If
|
|
139
|
-
|
|
140
|
-
// If
|
|
141
|
-
|
|
142
|
-
// If
|
|
143
|
-
|
|
144
|
+
// If `true`: Hide advertisements on the page
|
|
145
|
+
HIDE_ADVERTISEMENTS: boolean,
|
|
146
|
+
// If `true`: Disable all Cookie Consent screens (headers, footers, or dialogs)
|
|
147
|
+
HIDE_COOKIE_CONSENT_SCREEN: boolean,
|
|
148
|
+
// If `true`: Disable all marketing dialogs or popups (newsletters, promotions, etc.)
|
|
149
|
+
HIDE_MARKETING_DIALOGS: boolean,
|
|
150
|
+
// If `true`: Fully opt out the user of non-functional trackers
|
|
151
|
+
DISABLE_NON_FUNCTIONAL_TRACKING: boolean,
|
|
152
|
+
// If `true`: Provide Free access to content behind a paywall (news, articles, etc.)
|
|
153
|
+
DISABLE_CONTENT_PAYWALL: boolean,
|
|
154
|
+
// If `true`: Provide Free access to your base subscription plan (if subscription model is present)
|
|
155
|
+
ENABLE_SUBSCRIPTION_ACCESS: boolean,
|
|
144
156
|
};
|
|
145
157
|
|
|
146
|
-
//
|
|
158
|
+
// Adjust content in your templates based on tokenContext values
|
|
147
159
|
res.render("index.ejs", { tokenContext });
|
|
148
160
|
});
|
|
149
161
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Official Zero Ad Network public key.
|
|
3
|
+
* Used to verify that `X-Better-Web-Hello` header values are authentic
|
|
4
|
+
* and have not been tampered with.
|
|
5
|
+
*/
|
|
6
|
+
declare const ZEROAD_NETWORK_PUBLIC_KEY: string;
|
|
7
|
+
/**
|
|
8
|
+
* IMPORTANT: Requirements listed for each feature class MUST be fulfilled fully.
|
|
9
|
+
* Failure to comply will result in the site getting banned from Zero Ad Network platform.
|
|
10
|
+
*/
|
|
11
|
+
declare enum FEATURES {
|
|
12
|
+
/**
|
|
13
|
+
* Feature requirements:
|
|
14
|
+
* - Disable all advertisements on the page;
|
|
15
|
+
* - Disable all Cookie Consent screens (headers, footers, or dialogs);
|
|
16
|
+
* - Fully opt out the user of non-functional trackers;
|
|
17
|
+
* - Disable all marketing dialogs or popups (newsletters, promotions, etc.).
|
|
18
|
+
*/
|
|
19
|
+
CLEAN_WEB = 1,
|
|
20
|
+
/**
|
|
21
|
+
* Feature requirements:
|
|
22
|
+
* - Provide Free access to content behind a paywall (news, articles, etc.);
|
|
23
|
+
* - Provide Free access to your base subscription plan (if subscription model is present).
|
|
24
|
+
*/
|
|
25
|
+
ONE_PASS = 2
|
|
26
|
+
}
|
|
27
|
+
declare enum SERVER_HEADERS {
|
|
28
|
+
WELCOME = "X-Better-Web-Welcome"
|
|
29
|
+
}
|
|
30
|
+
declare enum CLIENT_HEADERS {
|
|
31
|
+
HELLO = "X-Better-Web-Hello"
|
|
32
|
+
}
|
|
33
|
+
declare enum PROTOCOL_VERSION {
|
|
34
|
+
V_1 = 1
|
|
35
|
+
}
|
|
36
|
+
declare const CURRENT_PROTOCOL_VERSION = PROTOCOL_VERSION.V_1;
|
|
37
|
+
|
|
38
|
+
declare function encodeServerHeader(clientId: string, features: FEATURES[]): string;
|
|
39
|
+
type WelcomeHeader = {
|
|
40
|
+
clientId: string;
|
|
41
|
+
version: PROTOCOL_VERSION;
|
|
42
|
+
features: (keyof typeof FEATURES)[];
|
|
43
|
+
};
|
|
44
|
+
declare function decodeServerHeader(headerValue: string | null | undefined): WelcomeHeader | undefined;
|
|
45
|
+
|
|
46
|
+
export { CLIENT_HEADERS as C, FEATURES as F, PROTOCOL_VERSION as P, SERVER_HEADERS as S, ZEROAD_NETWORK_PUBLIC_KEY as Z, CURRENT_PROTOCOL_VERSION as a, decodeServerHeader as d, encodeServerHeader as e };
|
|
47
|
+
export type { WelcomeHeader as W };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Official Zero Ad Network public key.
|
|
3
|
+
* Used to verify that `X-Better-Web-Hello` header values are authentic
|
|
4
|
+
* and have not been tampered with.
|
|
5
|
+
*/
|
|
6
|
+
declare const ZEROAD_NETWORK_PUBLIC_KEY: string;
|
|
7
|
+
/**
|
|
8
|
+
* IMPORTANT: Requirements listed for each feature class MUST be fulfilled fully.
|
|
9
|
+
* Failure to comply will result in the site getting banned from Zero Ad Network platform.
|
|
10
|
+
*/
|
|
11
|
+
declare enum FEATURES {
|
|
12
|
+
/**
|
|
13
|
+
* Feature requirements:
|
|
14
|
+
* - Disable all advertisements on the page;
|
|
15
|
+
* - Disable all Cookie Consent screens (headers, footers, or dialogs);
|
|
16
|
+
* - Fully opt out the user of non-functional trackers;
|
|
17
|
+
* - Disable all marketing dialogs or popups (newsletters, promotions, etc.).
|
|
18
|
+
*/
|
|
19
|
+
CLEAN_WEB = 1,
|
|
20
|
+
/**
|
|
21
|
+
* Feature requirements:
|
|
22
|
+
* - Provide Free access to content behind a paywall (news, articles, etc.);
|
|
23
|
+
* - Provide Free access to your base subscription plan (if subscription model is present).
|
|
24
|
+
*/
|
|
25
|
+
ONE_PASS = 2
|
|
26
|
+
}
|
|
27
|
+
declare enum SERVER_HEADERS {
|
|
28
|
+
WELCOME = "X-Better-Web-Welcome"
|
|
29
|
+
}
|
|
30
|
+
declare enum CLIENT_HEADERS {
|
|
31
|
+
HELLO = "X-Better-Web-Hello"
|
|
32
|
+
}
|
|
33
|
+
declare enum PROTOCOL_VERSION {
|
|
34
|
+
V_1 = 1
|
|
35
|
+
}
|
|
36
|
+
declare const CURRENT_PROTOCOL_VERSION = PROTOCOL_VERSION.V_1;
|
|
37
|
+
|
|
38
|
+
declare function encodeServerHeader(clientId: string, features: FEATURES[]): string;
|
|
39
|
+
type WelcomeHeader = {
|
|
40
|
+
clientId: string;
|
|
41
|
+
version: PROTOCOL_VERSION;
|
|
42
|
+
features: (keyof typeof FEATURES)[];
|
|
43
|
+
};
|
|
44
|
+
declare function decodeServerHeader(headerValue: string | null | undefined): WelcomeHeader | undefined;
|
|
45
|
+
|
|
46
|
+
export { CLIENT_HEADERS as C, FEATURES as F, PROTOCOL_VERSION as P, SERVER_HEADERS as S, ZEROAD_NETWORK_PUBLIC_KEY as Z, CURRENT_PROTOCOL_VERSION as a, decodeServerHeader as d, encodeServerHeader as e };
|
|
47
|
+
export type { WelcomeHeader as W };
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const levels = {
|
|
4
|
+
error: 0,
|
|
5
|
+
warn: 1,
|
|
6
|
+
info: 2,
|
|
7
|
+
debug: 3
|
|
8
|
+
};
|
|
9
|
+
let currentLevel = "error";
|
|
10
|
+
function setLogLevel(level) {
|
|
11
|
+
if (levels[level] !== void 0) {
|
|
12
|
+
currentLevel = level;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function log(level, ...args) {
|
|
16
|
+
if (levels[level] <= levels[currentLevel]) {
|
|
17
|
+
console.log(`[${level.toUpperCase()}]`, ...args);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const ZEROAD_NETWORK_PUBLIC_KEY = "MCowBQYDK2VwAyEAignXRaTQtxEDl4ThULucKNQKEEO2Lo5bEO8qKwjSDVs=";
|
|
22
|
+
var FEATURES = /* @__PURE__ */ ((FEATURES2) => {
|
|
23
|
+
FEATURES2[FEATURES2["CLEAN_WEB"] = 1] = "CLEAN_WEB";
|
|
24
|
+
FEATURES2[FEATURES2["ONE_PASS"] = 2] = "ONE_PASS";
|
|
25
|
+
return FEATURES2;
|
|
26
|
+
})(FEATURES || {});
|
|
27
|
+
var SERVER_HEADERS = /* @__PURE__ */ ((SERVER_HEADERS2) => {
|
|
28
|
+
SERVER_HEADERS2["WELCOME"] = "X-Better-Web-Welcome";
|
|
29
|
+
return SERVER_HEADERS2;
|
|
30
|
+
})(SERVER_HEADERS || {});
|
|
31
|
+
var CLIENT_HEADERS = /* @__PURE__ */ ((CLIENT_HEADERS2) => {
|
|
32
|
+
CLIENT_HEADERS2["HELLO"] = "X-Better-Web-Hello";
|
|
33
|
+
return CLIENT_HEADERS2;
|
|
34
|
+
})(CLIENT_HEADERS || {});
|
|
35
|
+
var PROTOCOL_VERSION = /* @__PURE__ */ ((PROTOCOL_VERSION2) => {
|
|
36
|
+
PROTOCOL_VERSION2[PROTOCOL_VERSION2["V_1"] = 1] = "V_1";
|
|
37
|
+
return PROTOCOL_VERSION2;
|
|
38
|
+
})(PROTOCOL_VERSION || {});
|
|
39
|
+
const CURRENT_PROTOCOL_VERSION = 1 /* V_1 */;
|
|
40
|
+
|
|
41
|
+
let SITE_FEATURES_NATIVE$1;
|
|
42
|
+
const getSiteFeaturesNative = () => {
|
|
43
|
+
if (SITE_FEATURES_NATIVE$1?.length) return SITE_FEATURES_NATIVE$1;
|
|
44
|
+
return SITE_FEATURES_NATIVE$1 = Object.entries(FEATURES).filter(([key]) => isNaN(Number(key)));
|
|
45
|
+
};
|
|
46
|
+
const toBase64 = (data) => {
|
|
47
|
+
if (typeof data.toBase64 === "function") return data.toBase64();
|
|
48
|
+
if (typeof Buffer !== "undefined") return Buffer.from(data).toString("base64");
|
|
49
|
+
if (typeof btoa === "function") {
|
|
50
|
+
let binary = "";
|
|
51
|
+
for (let i = 0; i < data.length; i++) {
|
|
52
|
+
binary += String.fromCharCode(data[i]);
|
|
53
|
+
}
|
|
54
|
+
return btoa(binary);
|
|
55
|
+
}
|
|
56
|
+
throw new Error("Base64 encoding not supported in this environment");
|
|
57
|
+
};
|
|
58
|
+
const fromBase64 = (input) => {
|
|
59
|
+
if (typeof Uint8Array.fromBase64 === "function") return Uint8Array.fromBase64(input);
|
|
60
|
+
if (typeof Buffer !== "undefined") return new Uint8Array(Buffer.from(input, "base64"));
|
|
61
|
+
if (typeof atob === "function") {
|
|
62
|
+
const binary = atob(input);
|
|
63
|
+
const bytes = new Uint8Array(binary.length);
|
|
64
|
+
for (let i = 0; i < binary.length; i++) {
|
|
65
|
+
bytes[i] = binary.charCodeAt(i);
|
|
66
|
+
}
|
|
67
|
+
return bytes;
|
|
68
|
+
}
|
|
69
|
+
throw new Error("Base64 decoding not supported in this environment");
|
|
70
|
+
};
|
|
71
|
+
const assert = (value, message) => {
|
|
72
|
+
if (!value) throw new Error(message);
|
|
73
|
+
};
|
|
74
|
+
const hasFlag = (flag, flags) => Boolean(flag & flags);
|
|
75
|
+
const setFlags = (features = []) => features.reduce((acc, feature) => acc | feature, 0);
|
|
76
|
+
|
|
77
|
+
const SEPARATOR = "^";
|
|
78
|
+
const SITE_FEATURES_NATIVE = getSiteFeaturesNative();
|
|
79
|
+
const validFeatureValues = Object.values(FEATURES).filter((key) => !isNaN(Number(key)));
|
|
80
|
+
const validFeatureKeys = Object.values(FEATURES).filter((key) => isNaN(Number(key)));
|
|
81
|
+
function encodeServerHeader(clientId, features) {
|
|
82
|
+
if (!clientId?.length) {
|
|
83
|
+
throw new Error("The provided `clientId` value cannot be an empty string");
|
|
84
|
+
}
|
|
85
|
+
if (!features?.length) {
|
|
86
|
+
throw new Error("At least one Site feature must be provided");
|
|
87
|
+
}
|
|
88
|
+
if (features.filter((feature) => validFeatureValues.includes(feature)).length !== features.length) {
|
|
89
|
+
throw new Error(`Only valid Site features are allowed: ${validFeatureKeys.join(" | ")}`);
|
|
90
|
+
}
|
|
91
|
+
return [clientId, CURRENT_PROTOCOL_VERSION, setFlags(features)].join(SEPARATOR);
|
|
92
|
+
}
|
|
93
|
+
function decodeServerHeader(headerValue) {
|
|
94
|
+
if (!headerValue?.length) return;
|
|
95
|
+
try {
|
|
96
|
+
const parts = headerValue.split(SEPARATOR);
|
|
97
|
+
assert(parts.length === 3, "Invalid header value format");
|
|
98
|
+
const [clientId, protocolVersion, flags] = parts;
|
|
99
|
+
assert(
|
|
100
|
+
Object.values(PROTOCOL_VERSION).includes(Number(protocolVersion)),
|
|
101
|
+
"Invalid or unsupported protocol version"
|
|
102
|
+
);
|
|
103
|
+
assert(Number(flags).toFixed(0).toString() === flags, "Invalid flags number");
|
|
104
|
+
let features = [];
|
|
105
|
+
for (const [feature, shift] of SITE_FEATURES_NATIVE) {
|
|
106
|
+
if (hasFlag(Number(flags), shift)) features.push(feature);
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
version: Number(protocolVersion),
|
|
110
|
+
clientId,
|
|
111
|
+
features
|
|
112
|
+
};
|
|
113
|
+
} catch (err) {
|
|
114
|
+
log("warn", "Could not decode server header value", { reason: err?.message });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
exports.CLIENT_HEADERS = CLIENT_HEADERS;
|
|
119
|
+
exports.CURRENT_PROTOCOL_VERSION = CURRENT_PROTOCOL_VERSION;
|
|
120
|
+
exports.FEATURES = FEATURES;
|
|
121
|
+
exports.PROTOCOL_VERSION = PROTOCOL_VERSION;
|
|
122
|
+
exports.SERVER_HEADERS = SERVER_HEADERS;
|
|
123
|
+
exports.ZEROAD_NETWORK_PUBLIC_KEY = ZEROAD_NETWORK_PUBLIC_KEY;
|
|
124
|
+
exports.decodeServerHeader = decodeServerHeader;
|
|
125
|
+
exports.encodeServerHeader = encodeServerHeader;
|
|
126
|
+
exports.fromBase64 = fromBase64;
|
|
127
|
+
exports.getSiteFeaturesNative = getSiteFeaturesNative;
|
|
128
|
+
exports.hasFlag = hasFlag;
|
|
129
|
+
exports.log = log;
|
|
130
|
+
exports.setFlags = setFlags;
|
|
131
|
+
exports.setLogLevel = setLogLevel;
|
|
132
|
+
exports.toBase64 = toBase64;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
const levels = {
|
|
2
|
+
error: 0,
|
|
3
|
+
warn: 1,
|
|
4
|
+
info: 2,
|
|
5
|
+
debug: 3
|
|
6
|
+
};
|
|
7
|
+
let currentLevel = "error";
|
|
8
|
+
function setLogLevel(level) {
|
|
9
|
+
if (levels[level] !== void 0) {
|
|
10
|
+
currentLevel = level;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function log(level, ...args) {
|
|
14
|
+
if (levels[level] <= levels[currentLevel]) {
|
|
15
|
+
console.log(`[${level.toUpperCase()}]`, ...args);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const ZEROAD_NETWORK_PUBLIC_KEY = "MCowBQYDK2VwAyEAignXRaTQtxEDl4ThULucKNQKEEO2Lo5bEO8qKwjSDVs=";
|
|
20
|
+
var FEATURES = /* @__PURE__ */ ((FEATURES2) => {
|
|
21
|
+
FEATURES2[FEATURES2["CLEAN_WEB"] = 1] = "CLEAN_WEB";
|
|
22
|
+
FEATURES2[FEATURES2["ONE_PASS"] = 2] = "ONE_PASS";
|
|
23
|
+
return FEATURES2;
|
|
24
|
+
})(FEATURES || {});
|
|
25
|
+
var SERVER_HEADERS = /* @__PURE__ */ ((SERVER_HEADERS2) => {
|
|
26
|
+
SERVER_HEADERS2["WELCOME"] = "X-Better-Web-Welcome";
|
|
27
|
+
return SERVER_HEADERS2;
|
|
28
|
+
})(SERVER_HEADERS || {});
|
|
29
|
+
var CLIENT_HEADERS = /* @__PURE__ */ ((CLIENT_HEADERS2) => {
|
|
30
|
+
CLIENT_HEADERS2["HELLO"] = "X-Better-Web-Hello";
|
|
31
|
+
return CLIENT_HEADERS2;
|
|
32
|
+
})(CLIENT_HEADERS || {});
|
|
33
|
+
var PROTOCOL_VERSION = /* @__PURE__ */ ((PROTOCOL_VERSION2) => {
|
|
34
|
+
PROTOCOL_VERSION2[PROTOCOL_VERSION2["V_1"] = 1] = "V_1";
|
|
35
|
+
return PROTOCOL_VERSION2;
|
|
36
|
+
})(PROTOCOL_VERSION || {});
|
|
37
|
+
const CURRENT_PROTOCOL_VERSION = 1 /* V_1 */;
|
|
38
|
+
|
|
39
|
+
let SITE_FEATURES_NATIVE$1;
|
|
40
|
+
const getSiteFeaturesNative = () => {
|
|
41
|
+
if (SITE_FEATURES_NATIVE$1?.length) return SITE_FEATURES_NATIVE$1;
|
|
42
|
+
return SITE_FEATURES_NATIVE$1 = Object.entries(FEATURES).filter(([key]) => isNaN(Number(key)));
|
|
43
|
+
};
|
|
44
|
+
const toBase64 = (data) => {
|
|
45
|
+
if (typeof data.toBase64 === "function") return data.toBase64();
|
|
46
|
+
if (typeof Buffer !== "undefined") return Buffer.from(data).toString("base64");
|
|
47
|
+
if (typeof btoa === "function") {
|
|
48
|
+
let binary = "";
|
|
49
|
+
for (let i = 0; i < data.length; i++) {
|
|
50
|
+
binary += String.fromCharCode(data[i]);
|
|
51
|
+
}
|
|
52
|
+
return btoa(binary);
|
|
53
|
+
}
|
|
54
|
+
throw new Error("Base64 encoding not supported in this environment");
|
|
55
|
+
};
|
|
56
|
+
const fromBase64 = (input) => {
|
|
57
|
+
if (typeof Uint8Array.fromBase64 === "function") return Uint8Array.fromBase64(input);
|
|
58
|
+
if (typeof Buffer !== "undefined") return new Uint8Array(Buffer.from(input, "base64"));
|
|
59
|
+
if (typeof atob === "function") {
|
|
60
|
+
const binary = atob(input);
|
|
61
|
+
const bytes = new Uint8Array(binary.length);
|
|
62
|
+
for (let i = 0; i < binary.length; i++) {
|
|
63
|
+
bytes[i] = binary.charCodeAt(i);
|
|
64
|
+
}
|
|
65
|
+
return bytes;
|
|
66
|
+
}
|
|
67
|
+
throw new Error("Base64 decoding not supported in this environment");
|
|
68
|
+
};
|
|
69
|
+
const assert = (value, message) => {
|
|
70
|
+
if (!value) throw new Error(message);
|
|
71
|
+
};
|
|
72
|
+
const hasFlag = (flag, flags) => Boolean(flag & flags);
|
|
73
|
+
const setFlags = (features = []) => features.reduce((acc, feature) => acc | feature, 0);
|
|
74
|
+
|
|
75
|
+
const SEPARATOR = "^";
|
|
76
|
+
const SITE_FEATURES_NATIVE = getSiteFeaturesNative();
|
|
77
|
+
const validFeatureValues = Object.values(FEATURES).filter((key) => !isNaN(Number(key)));
|
|
78
|
+
const validFeatureKeys = Object.values(FEATURES).filter((key) => isNaN(Number(key)));
|
|
79
|
+
function encodeServerHeader(clientId, features) {
|
|
80
|
+
if (!clientId?.length) {
|
|
81
|
+
throw new Error("The provided `clientId` value cannot be an empty string");
|
|
82
|
+
}
|
|
83
|
+
if (!features?.length) {
|
|
84
|
+
throw new Error("At least one Site feature must be provided");
|
|
85
|
+
}
|
|
86
|
+
if (features.filter((feature) => validFeatureValues.includes(feature)).length !== features.length) {
|
|
87
|
+
throw new Error(`Only valid Site features are allowed: ${validFeatureKeys.join(" | ")}`);
|
|
88
|
+
}
|
|
89
|
+
return [clientId, CURRENT_PROTOCOL_VERSION, setFlags(features)].join(SEPARATOR);
|
|
90
|
+
}
|
|
91
|
+
function decodeServerHeader(headerValue) {
|
|
92
|
+
if (!headerValue?.length) return;
|
|
93
|
+
try {
|
|
94
|
+
const parts = headerValue.split(SEPARATOR);
|
|
95
|
+
assert(parts.length === 3, "Invalid header value format");
|
|
96
|
+
const [clientId, protocolVersion, flags] = parts;
|
|
97
|
+
assert(
|
|
98
|
+
Object.values(PROTOCOL_VERSION).includes(Number(protocolVersion)),
|
|
99
|
+
"Invalid or unsupported protocol version"
|
|
100
|
+
);
|
|
101
|
+
assert(Number(flags).toFixed(0).toString() === flags, "Invalid flags number");
|
|
102
|
+
let features = [];
|
|
103
|
+
for (const [feature, shift] of SITE_FEATURES_NATIVE) {
|
|
104
|
+
if (hasFlag(Number(flags), shift)) features.push(feature);
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
version: Number(protocolVersion),
|
|
108
|
+
clientId,
|
|
109
|
+
features
|
|
110
|
+
};
|
|
111
|
+
} catch (err) {
|
|
112
|
+
log("warn", "Could not decode server header value", { reason: err?.message });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export { CLIENT_HEADERS as C, FEATURES as F, PROTOCOL_VERSION as P, SERVER_HEADERS as S, ZEROAD_NETWORK_PUBLIC_KEY as Z, setLogLevel as a, CURRENT_PROTOCOL_VERSION as b, decodeServerHeader as d, encodeServerHeader as e, fromBase64 as f, getSiteFeaturesNative as g, hasFlag as h, log as l, setFlags as s, toBase64 as t };
|
package/dist/browser.d.mts
CHANGED
|
@@ -1,54 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* This is an official ZeroAd Network public key.
|
|
3
|
-
* Used to verify `X-Better-Web-User` header values are not tampered with.
|
|
4
|
-
*/
|
|
5
|
-
declare const ZEROAD_NETWORK_PUBLIC_KEY: string;
|
|
6
|
-
declare enum FEATURES {
|
|
7
|
-
/** Render no advertisements anywhere on the page */
|
|
8
|
-
ADS_OFF = 1,
|
|
9
|
-
/** Render no Cookie Consent screens (headers, footers or dialogs) on the page with complete OPT-OUT for non-functional trackers */
|
|
10
|
-
COOKIE_CONSENT_OFF = 2,
|
|
11
|
-
/** Render no marketing dialogs or popups such as newsletter, promotion etc. on the page */
|
|
12
|
-
MARKETING_DIALOG_OFF = 4,
|
|
13
|
-
/** Provide automatic access to otherwise paywalled content such as articles, news etc. */
|
|
14
|
-
CONTENT_PAYWALL_OFF = 8,
|
|
15
|
-
/** Provide automatic access to site features provided behind a SaaS at least the basic subscription plan */
|
|
16
|
-
SUBSCRIPTION_ACCESS_ON = 16
|
|
17
|
-
}
|
|
18
|
-
type UUID = string;
|
|
19
|
-
declare enum SERVER_HEADERS {
|
|
20
|
-
WELCOME = "X-Better-Web-Welcome"
|
|
21
|
-
}
|
|
22
|
-
declare enum CLIENT_HEADERS {
|
|
23
|
-
HELLO = "X-Better-Web-Hello"
|
|
24
|
-
}
|
|
25
|
-
declare enum PROTOCOL_VERSION {
|
|
26
|
-
V_1 = 1
|
|
27
|
-
}
|
|
28
|
-
declare const CURRENT_PROTOCOL_VERSION = PROTOCOL_VERSION.V_1;
|
|
29
|
-
type ServerHeaderExtendedOptions = {
|
|
30
|
-
siteId: UUID;
|
|
31
|
-
features: FEATURES[];
|
|
32
|
-
};
|
|
33
|
-
type ServerHeaderOptions = NonNullable<string | ServerHeaderExtendedOptions>;
|
|
34
|
-
type WelcomeHeader = {
|
|
35
|
-
version: PROTOCOL_VERSION;
|
|
36
|
-
features: (keyof typeof FEATURES)[];
|
|
37
|
-
siteId: UUID;
|
|
38
|
-
};
|
|
39
|
-
type ClientParsedHeader = {
|
|
40
|
-
version: PROTOCOL_VERSION;
|
|
41
|
-
expiresAt: Date;
|
|
42
|
-
flags: number;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
declare class ServerHeader {
|
|
46
|
-
NAME: SERVER_HEADERS;
|
|
47
|
-
VALUE: string;
|
|
48
|
-
constructor(options: ServerHeaderOptions);
|
|
49
|
-
encode(siteId: UUID, features: FEATURES[]): string;
|
|
50
|
-
static decode(headerValue: string | undefined): WelcomeHeader | undefined;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export { CLIENT_HEADERS, CURRENT_PROTOCOL_VERSION, FEATURES, PROTOCOL_VERSION, SERVER_HEADERS, ServerHeader, ZEROAD_NETWORK_PUBLIC_KEY };
|
|
54
|
-
export type { ClientParsedHeader, ServerHeaderExtendedOptions, ServerHeaderOptions, UUID, WelcomeHeader };
|
|
1
|
+
export { C as CLIENT_HEADERS, a as CURRENT_PROTOCOL_VERSION, F as FEATURES, P as PROTOCOL_VERSION, S as SERVER_HEADERS, Z as ZEROAD_NETWORK_PUBLIC_KEY, d as decodeServerHeader } from './browser-BiNZ2c6t.mjs';
|
package/dist/browser.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { C as CLIENT_HEADERS,
|
|
1
|
+
export { C as CLIENT_HEADERS, b as CURRENT_PROTOCOL_VERSION, F as FEATURES, P as PROTOCOL_VERSION, S as SERVER_HEADERS, Z as ZEROAD_NETWORK_PUBLIC_KEY, d as decodeServerHeader } from './browser-Citg0bh9.mjs';
|