@pigmilcom/a11y 1.0.9 → 1.1.1

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.

Potentially problematic release.


This version of @pigmilcom/a11y might be problematic. Click here for more details.

package/README.md CHANGED
@@ -61,6 +61,54 @@ PigmilA11y.unmount(); // remove the widget and clean up
61
61
 
62
62
  ---
63
63
 
64
+ ## Plans & domain validation
65
+
66
+ Starting from v1.1.0, the widget validates the hosting domain against the **PIGMIL API** before rendering. This enables usage-based quotas and per-domain access control.
67
+
68
+ ### Tiers
69
+
70
+ | Plan | Requests / day | Requests / month | PIGMIL branding |
71
+ | ------ | -------------- | ---------------- | --------------- |
72
+ | Free | 1,000 | 30,000 | Shown in footer |
73
+ | Pro | Unlimited | Unlimited | Hidden |
74
+
75
+ A "request" is one widget initialisation (page load / route change).
76
+
77
+ ### How validation works
78
+
79
+ 1. On widget mount the browser makes a single `GET` request to the PIGMIL API.
80
+ 2. The API checks whether the domain is registered and within quota.
81
+ 3. The response controls whether the widget renders:
82
+ - **`valid`** — widget renders normally.
83
+ - **`blocked`** — quota exceeded; widget does not render until the day / month resets or the plan is upgraded.
84
+ - **`error`** / API unreachable — widget does not render (fail-closed on non-local domains).
85
+ 4. The result is cached in `sessionStorage` for 30 minutes to avoid redundant calls.
86
+ 5. Cached and returned result objects are `Object.freeze()`d — their `status` and `plan` properties cannot be mutated after resolution.
87
+
88
+ ### Local / development environments
89
+
90
+ Validation is **automatically skipped** when the page runs on:
91
+ - `localhost`, `127.x.x.x`, `::1`, `0.0.0.0`
92
+ - Any non-standard port (e.g. `:3000`, `:5173`, `:8080`)
93
+
94
+ The widget always renders in these environments with a free-plan appearance. No API key or sign-up is required during development.
95
+
96
+ ### Security layers
97
+
98
+ | Layer | What it does |
99
+ |---|---|
100
+ | **Server-side enforcement** (primary) | The PIGMIL API only validates registered domains, increments per-domain counters, and returns `blocked:true` when quotas are exceeded. This is the real gate — no client-side bypass can defeat it. |
101
+ | **Frozen result objects** | `Object.freeze()` on the validation result prevents callers from mutating `{ status, plan }` to escalate their own access level. |
102
+ | **CDN bundle obfuscation** | `dist/a11y.cdn.js` is post-processed by `javascript-obfuscator` with Base64 string-array encoding and self-defending code. The self-defending wrapper causes the bundle to break at runtime if it has been reformatted / prettified before execution, deterring the "prettify → grep → patch → re-minify" bypass workflow. The npm package source remains intentionally readable (open source). |
103
+ | **XOR-encoded endpoint** | The API URL is stored as a numeric XOR array in source and decoded at runtime, making it non-trivial to locate in the minified + obfuscated CDN bundle. |
104
+ | **Daily-rotating request signature** | Each API call includes an FNV-1a hash of `domain + YYYY-MM-DD` — a per-domain-per-day token the server can optionally verify to detect replayed or forged requests. |
105
+
106
+ ### Current status (mock mode)
107
+
108
+ The API endpoint is being implemented in the PIGMIL backend. Until it is live, `src/license.js` uses `MOCK_MODE = true`, which always returns `{ ok: true, plan: 'free', blocked: false }`. To switch to the real API, set `MOCK_MODE = false` in that file — the fetch code is already written and ready.
109
+
110
+ ---
111
+
64
112
  ## Features
65
113
 
66
114
  - Text size — Normal / Large / X-Large
@@ -120,13 +168,71 @@ class rules applied to `<html>`.
120
168
  ### 2 — Drop in the widget
121
169
 
122
170
  ```jsx
123
- import A11yWidget from '@pigmilcom/a11y';
171
+ import a11y from '@pigmilcom/a11y';
172
+
173
+ // Assign to a capitalized variable to use in JSX
174
+ const A11y = a11y;
175
+
176
+ <A11y className="fixed bottom-4 right-4 rounded" />
177
+ ```
178
+
179
+ The `className` prop is merged onto the trigger button — use Tailwind classes,
180
+ custom classes, or anything your bundler supports to position the widget.
181
+
182
+ ---
183
+
184
+ ## Customising the widget with CSS
185
+
186
+ The widget exposes stable **`a11y-widget-*`** class names on its key elements
187
+ as a public API. Target them from a `<style>` tag (CDN / self-hosted) or your
188
+ own stylesheet (React):
189
+
190
+ ```html
191
+ <style>
192
+ /* Trigger button */
193
+ .a11y-widget-btn {
194
+ border-radius: 8px !important;
195
+ background: rgba(15, 15, 40, 0.9) !important;
196
+ }
197
+
198
+ /* Dialog panel */
199
+ .a11y-widget-dialog {
200
+ border-color: rgba(99, 102, 241, 0.4) !important;
201
+ }
202
+
203
+ /* Toggle option rows */
204
+ .a11y-widget-toggle-btn:hover {
205
+ background: rgba(99, 102, 241, 0.06) !important;
206
+ }
207
+ </style>
208
+ ```
209
+
210
+ | Public class | Element |
211
+ | --------------------------- | -------------------- |
212
+ | `a11y-widget-btn` | Trigger button |
213
+ | `a11y-widget-dialog` | Dialog panel |
214
+ | `a11y-widget-toggle-btn` | Each toggle option |
215
+
216
+ > Place your `<style>` tag **after** the widget `<script>` tag so it takes
217
+ > precedence in the cascade. Use `!important` where the widget's base styles
218
+ > have higher specificity.
219
+
220
+ ---
221
+
222
+ ## Tailwind CSS support
223
+
224
+ Tailwind utility classes work transparently via the `className` prop:
225
+
226
+ ```jsx
227
+ import a11y from '@pigmilcom/a11y';
228
+ const A11y = a11y;
124
229
 
125
- <A11yWidget className="fixed bottom-4 right-4 rounded" />
230
+ // Tailwind classes are passed straight through to the trigger button
231
+ <A11y className="fixed bottom-6 right-6 rounded-full shadow-xl" />
126
232
  ```
127
233
 
128
- The `className` prop is forwarded to the trigger button use it to position
129
- the widget wherever suits your layout.
234
+ Because the classes come from *your* source files, Tailwind's JIT scanner
235
+ picks them up automatically. No purge exceptions or safelist entries needed.
130
236
 
131
237
  ---
132
238
 
@@ -137,14 +243,16 @@ the widget wherever suits your layout.
137
243
  ```jsx
138
244
  // app/layout.jsx
139
245
  import '@pigmilcom/a11y/styles';
140
- import A11yWidget from '@pigmilcom/a11y';
246
+ import a11y from '@pigmilcom/a11y';
247
+
248
+ const A11y = a11y;
141
249
 
142
250
  export default function RootLayout({ children }) {
143
251
  return (
144
252
  <html lang="en">
145
253
  <body>
146
254
  {children}
147
- <A11yWidget className="fixed bottom-4 right-4 rounded" />
255
+ <A11y className="fixed bottom-4 right-4 rounded" />
148
256
  </body>
149
257
  </html>
150
258
  );
@@ -155,25 +263,17 @@ export default function RootLayout({ children }) {
155
263
 
156
264
  ### Vite + React
157
265
 
158
- ```jsx
159
- // src/main.jsx
160
- import '@pigmilcom/a11y/styles';
161
- import React from 'react';
162
- import ReactDOM from 'react-dom/client';
163
- import App from './App';
164
-
165
- ReactDOM.createRoot(document.getElementById('root')).render(<App />);
166
- ```
167
-
168
266
  ```jsx
169
267
  // src/App.jsx
170
- import A11yWidget from '@pigmilcom/a11y';
268
+ import a11y from '@pigmilcom/a11y';
269
+
270
+ const A11y = a11y;
171
271
 
172
272
  export default function App() {
173
273
  return (
174
274
  <>
175
275
  {/* …your content… */}
176
- <A11yWidget className="fixed bottom-4 right-4 rounded" />
276
+ <A11y className="fixed bottom-4 right-4 rounded" />
177
277
  </>
178
278
  );
179
279
  }