@okeyamy/lua 5.0.4

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 ADDED
@@ -0,0 +1,552 @@
1
+ <a href="https://github.com/OkeyAmy/Lua-Dynamic-Website-">
2
+ <img src="assets/lua-v2.svg">
3
+ </a>
4
+
5
+ > A progressive, client/server AB testing library.
6
+
7
+ [![npm][npm-image]][npm-url]
8
+ [![bower][bower-image]][bower-url]
9
+
10
+ [npm-image]: https://badge.fury.io/js/lua.svg
11
+ [npm-url]: https://www.npmjs.com/package/lua
12
+ [bower-image]: https://badge.fury.io/bo/lua.svg
13
+ [bower-url]: https://github.com/OkeyAmy/Lua-Dynamic-Website-
14
+
15
+ Lua is an AB testing library designed to be clear, minimal, and flexible. It works in both the server and browser with the use of driver-based persistence layers.
16
+
17
+ You can download the compiled javascript directly [here](/build/lua.js)
18
+
19
+ **[Watch the demo video](https://www.youtube.com/watch?v=e5tgfkSQ1uc)** — UTM personalization and AI-powered content in action.
20
+
21
+ ---
22
+
23
+ ## Problem Statement
24
+
25
+ Enterprises and product teams need to show the right message to the right user at the right time—without maintaining separate pages per campaign or channel. Static copy forces one-size-fits-all experiences: the same headline for a paid ad visitor and an organic social visitor, the same CTA for first-time and returning users. Manual A/B tests scale poorly, and full-stack personalization often requires heavy platforms, server-side logic, and ongoing engineering. Developers and marketers need a lightweight, programmable way to run experiments and personalize content by traffic source, intent, and history while keeping the core site simple and fast.
26
+
27
+ ---
28
+
29
+ ## Solution
30
+
31
+ Lua provides a **progressive, client/server A/B testing and personalization layer** that stays small and optional. You define tests and content variants in code; Lua handles bucketing, persistence, and—optionally—UTM- and AI-driven personalization. No separate CMS or experimentation platform is required. Use the core for classic A/B or multivariate tests; add UTM-based personalization to tailor content by campaign and channel; opt in to AI (OpenAI) to select the best variant or generate dynamic copy from context. Everything can run in the browser with pluggable storage (localStorage, cookies, memory) or on the server, so it fits static sites, SPAs, and full-stack apps. The library is documented, tested, and built so enterprise teams and developers can ship dynamic website experiences without rewriting their stack.
32
+
33
+ ---
34
+
35
+ ## Core Features
36
+
37
+ | Area | Capability |
38
+ |------|------------|
39
+ | **A/B & multivariate testing** | Define tests with weighted buckets (A/B/C/D…), assign users consistently, persist via configurable storage drivers. |
40
+ | **UTM-based personalization** | Infer intent from `utm_source`, `utm_medium`, `utm_campaign`, referrer, and device; show the best-matching content variant automatically. |
41
+ | **AI-powered personalization** | Optional OpenAI integration: **Select** mode picks the best predefined variant; **Generate** mode creates headline, subheadline, and CTA copy from context and brand guidelines. |
42
+ | **Weighted user history** | Store visit context in localStorage with exponential decay; returning users get personalization that considers both current UTM and past behavior. |
43
+ | **Runtime & storage** | Browser and server support; drivers for `localStorage`, cookies, memory, or custom stores. |
44
+ | **Performance & safety** | Lightweight core (~3.8kb); optional AI with caching, timeouts, retries, and fallback to rule-based engine; proxy support for keeping API keys off the client. |
45
+
46
+ ---
47
+
48
+ ## Implementation & Technology
49
+
50
+ - **Integration**: Script tags or bundler; no framework lock-in. Works with static HTML, React, Vue, or any JS environment.
51
+ - **Persistence**: Pluggable store interface; built-in drivers for `localStorage`, cookies, and in-memory; custom drivers for server or hybrid use.
52
+ - **Personalization pipeline**: UTM parsing → intent inference → variant selection (or AI select/generate) → DOM or data application via `data-personalize` and templates.
53
+ - **AI path**: Configurable model (default `gpt-4o-mini`), optional proxy URL for production, structured prompts, and validation of AI responses with fallback.
54
+ - **Testing & quality**: Jest test suites for core, AI, and weighted history; lint and build via npm/pnpm scripts; demo server for local tryout with optional API key injection.
55
+
56
+ ---
57
+
58
+ ## Expected Results (Enterprise, Developers, End Users)
59
+
60
+ | Audience | Outcome |
61
+ |----------|--------|
62
+ | **Enterprise / product teams** | One library for A/B tests and dynamic copy; fewer one-off integrations; control over variants and brand voice; optional AI without vendor lock-in. |
63
+ | **Developers** | Clear API, small bundle, standard JS; add UTM or AI without rewriting the app; run tests and demo locally; deploy to CDN or existing stack. |
64
+ | **Marketers / growth** | Campaign-specific messaging (e.g. Google vs Facebook vs email) and returning-user awareness without separate landing pages or complex tooling. |
65
+ | **End users** | Relevant headlines and CTAs based on where they came from and prior visits; faster, more consistent experience with minimal layout shift. |
66
+
67
+ ---
68
+
69
+ ## Technology Used
70
+
71
+ | Layer | Technology |
72
+ |-------|------------|
73
+ | **Language** | JavaScript (ES5+); Babel for legacy builds. |
74
+ | **Build** | Rollup; UMD + ES module outputs; minification. |
75
+ | **Testing** | Jest; unit and integration tests for core Lua, UTM, AI personalization, weighted history. |
76
+ | **Linting** | ESLint. |
77
+ | **AI / personalization** | OpenAI Chat Completions API (e.g. gpt-4o-mini); fetch with retry and timeout; optional backend proxy. |
78
+ | **Storage (browser)** | localStorage (weighted history, AI cache); configurable cookie or memory stores. |
79
+ | **Runtime** | Browser and Node; no framework dependency. |
80
+
81
+ ---
82
+
83
+ * [Demo video](https://www.youtube.com/watch?v=e5tgfkSQ1uc)
84
+ * [Problem Statement](#problem-statement)
85
+ * [Solution](#solution)
86
+ * [Core Features](#core-features)
87
+ * [Implementation & Technology](#implementation--technology)
88
+ * [Expected Results](#expected-results-enterprise-developers-end-users)
89
+ * [Technology Used](#technology-used)
90
+ * [Features](#features)
91
+ * [Installing](#installing)
92
+ * [Developing](#developing)
93
+ * [Run the demo](#run-the-demo)
94
+ * [Usage](#usage)
95
+ * [API](#api)
96
+ * [Guide/FAQ](#guidefaq)
97
+ * [AI-Powered Personalization](#ai-powered-personalization)
98
+ * [Push to GitHub](#push-to-github)
99
+ * [License](#license)
100
+
101
+ ***
102
+
103
+ ## Features
104
+
105
+ * Powerful, clear API
106
+ * Many variations. ABCD testing
107
+ * Intelligent weighted bucketing
108
+ * Browser & Server support
109
+ * Storage Drivers: `localStorage`, `cookies`, `memory`, or build your own
110
+ * **AI-Powered Personalization** using OpenAI GPT models (opt-in)
111
+ * **UTM-Based Content Personalization** with automatic intent inference
112
+ * **Weighted User History** with exponential decay for returning visitors
113
+ * Well documented, tested, and proven in high production environments
114
+ * Lightweight, weighing in at ~ <span class="size">`3.8kb`</span> (core), with optional AI modules
115
+ * Not tested on animals
116
+
117
+ ## Installing
118
+
119
+ ```bash
120
+ # Via NPM
121
+ npm i lua --save
122
+
123
+ # Via Bower
124
+ bower i lua --save
125
+
126
+ # Via Yarn
127
+ yarn add lua
128
+ ```
129
+
130
+ ## Developing
131
+
132
+ ```bash
133
+ # Install dependencies (use pnpm, npm, or yarn)
134
+ pnpm install
135
+ # OR: npm install
136
+ # OR: yarn install
137
+
138
+ # Build the library
139
+ pnpm run build
140
+ # OR: npm run build
141
+
142
+ # Run linting
143
+ pnpm run lint
144
+ # OR: npm run lint
145
+
146
+ # Run all tests (83 tests across 6 suites)
147
+ pnpm test
148
+ # OR: npm test
149
+ ```
150
+
151
+ ### Run a single test file
152
+
153
+ ```bash
154
+ # Run only core Lua tests
155
+ pnpm test -- src/__tests__/unit.js
156
+
157
+ # Run only AI personalization tests
158
+ pnpm test -- src/__tests__/ai-personalize.test.js
159
+
160
+ # Run only weighted history tests
161
+ pnpm test -- src/__tests__/weighted-history.test.js
162
+ ```
163
+
164
+ ## Run the demo
165
+
166
+ Try the UTM personalization and AI-powered demo locally:
167
+
168
+ ```bash
169
+ # Option 1: Test server with auto-loaded API key (RECOMMENDED)
170
+ pnpm run test:demo
171
+ # OR: node test-demo.js
172
+
173
+ # This will:
174
+ # - Read your OpenAI API key from .env file
175
+ # - Auto-fill the API key in the demo
176
+ # - Enable AI mode by default
177
+ # - Serve at http://localhost:3000/demo
178
+ ```
179
+
180
+ If you don't need AI features, you can use a generic static server instead:
181
+
182
+ ```bash
183
+ # Option 2: Node (npx serve)
184
+ npx serve . -p 3000
185
+
186
+ # Option 3: Python 3
187
+ python3 -m http.server 8000
188
+
189
+ # Option 4: PHP
190
+ php -S localhost:8080
191
+ ```
192
+
193
+ Then open in your browser:
194
+
195
+ - **Option 1**: [http://localhost:3000/demo](http://localhost:3000/demo) (test server — AI auto-configured)
196
+ - **Options 2-4**: [http://localhost:3000/demo/index.html](http://localhost:3000/demo/index.html) (use your port: 3000, 8000, or 8080)
197
+
198
+ On the demo page you can:
199
+
200
+ - Use the **Test UTM Links** in the debug panel to simulate traffic from Google, Facebook, Reddit, etc.
201
+ - Toggle **Enable AI Mode**, add your OpenAI API key, and click **Run AI Personalization** to see AI-driven content selection.
202
+ - Use **Reset & Reload** to clear UTM params, or **Clear AI History & Cache** to reset stored history.
203
+
204
+ **Note:** The demo uses script paths like `../src/utm-personalize.js`, so it must be served from the project root (not by opening `demo/index.html` as a file). The recommended `test-demo.js` handles this automatically.
205
+
206
+ ## Usage
207
+
208
+ ```html
209
+ <script src="lua.js"></script>
210
+ <script>
211
+
212
+ // Set up our test API
213
+ const lua = new Lua({
214
+ store: Lua.stores.local
215
+ });
216
+
217
+ // Define a test
218
+ lua.define({
219
+ name: 'new-homepage',
220
+ buckets: {
221
+ control: { weight: 0.6 },
222
+ versionA: { weight: 0.2 },
223
+ versionB: { weight: 0.2 },
224
+ }
225
+ });
226
+
227
+ // Bucket the user
228
+ lua.assign();
229
+
230
+ // Fetch assignments at a later point
231
+ const info = lua.assignments();
232
+ </script>
233
+ ```
234
+
235
+ ## API
236
+
237
+ > ## `Lua(config)`
238
+
239
+ ```javascript
240
+ const lua = new Lua({
241
+ debug: true,
242
+ store: Lua.stores.local
243
+ });
244
+ ```
245
+
246
+ > This creates a new test API used to defined tests, assign buckets, and retrieve information.
247
+
248
+ **Returns**: `Object`
249
+
250
+ Name | Type | Description | Default
251
+ :--- | :--- | :--- | :---
252
+ `debug` | `Boolean` | _Set to `true` to enable logging of additional information_ | `false`
253
+ `store` | `Object` | _An object with get/set properties that will accept information to help persist and retrieve tests_ | `Lua.stores.local`
254
+
255
+ ***
256
+
257
+ > ## `lua.define(testData)`
258
+
259
+ ```javascript
260
+ // Create your test API
261
+ const lua = new Lua();
262
+
263
+ // Define a test
264
+ lua.define({
265
+ name: 'MyTestName',
266
+ buckets: {
267
+ variantA: { weight: 0.5 },
268
+ variantB: { weight: 0.5 },
269
+ },
270
+ });
271
+ ```
272
+
273
+ > This function defines the tests to be assigned to used during bucket assignment. This function accepts an object with two keys, `name` and `buckets`. Alternatively, you may pass an array of similar objects to define multiple tests at once.
274
+
275
+ > The `name` value is the name of your test. The keys within `bucket` are your bucket names. Each bucket value is an object containing an object with an optional key `weight` that defaults to `1`.
276
+
277
+ > The percent chance a bucket is chosen for any given user is determined by the buckets weight divided by the total amount of all weights provided for an individual test. If you have three buckets with a weight of 2, `2/6 == 0.33` which means each bucket has a weight of `33%`. There is no max for the total weights allowed.
278
+
279
+ **Returns**: `null`
280
+
281
+ Name | Type | Description | Default
282
+ :--- | :--- | :--- | :---
283
+ `data` | `Object/Array` | _An object/array of objects containing test and bucket information_ | `null`
284
+
285
+ ***
286
+
287
+ > ## `lua.assign(testName, bucketName)`
288
+
289
+ ```javascript
290
+ const lua = new Lua();
291
+ lua.define({
292
+ name: 'new-homepage',
293
+ buckets: {
294
+ variantA: { weight: 0.5 },
295
+ variantB: { weight: 0.5 },
296
+ }
297
+ });
298
+
299
+ // Assign buckets from all tests to the user...
300
+ lua.assign();
301
+
302
+ // or assign bucket from the specified test...
303
+ lua.assign('new-homepage');
304
+
305
+ // or specify the bucket from the specified test...
306
+ lua.assign('new-homepage', 'variantB');
307
+
308
+ // or remove the bucketing assignment from the specified test.
309
+ lua.assign('new-homepage', null);
310
+ ```
311
+
312
+ > Calling the `assign` method will assign a bucket for the provided tests to a user and persist them to the `store`. If a user has already been bucketed, they will _not_ be rebucketed unless a `bucketName` is explicitly provided.
313
+
314
+ > If no arguments are provided, all tests will have a bucket assigned to the user. If the first argument provided is a test name, it will attempt to assign a bucket for that test to a user. If a `bucketValue` is provided, it will set that user to the specified bucket. If the `bucketValue` is null, it will remove that users assignment to the bucket.
315
+
316
+ **Returns**: `null`
317
+
318
+ Name | Type | Description | Default
319
+ :--- | :--- | :--- | :---
320
+ `testName` (optional) | `String` | _The name of the test to assign a bucket to_ | `null`
321
+ `bucketName` (optional) | `String` | _The name of the bucket to assign to a user_ | `null`
322
+
323
+ ***
324
+
325
+ > ## `lua.definitions()`
326
+
327
+ ```javascript
328
+ const lua = new Lua();
329
+ lua.define({
330
+ name: 'new-homepage',
331
+ buckets: {
332
+ variantA: { weight: 0.5 },
333
+ variantB: { weight: 0.5 },
334
+ }
335
+ });
336
+
337
+ // Retrieve all of the provided tests
338
+ const tests = lua.definitions();
339
+ ```
340
+
341
+ > This provides the user with all of the tests available.
342
+
343
+ > The returned information will be an array if multiple tests were defined, otherwise, it will be an object of the single test defined. The object will mirror exactly what was provided in the `define` method.
344
+
345
+ **Returns**: `Object|Array`
346
+
347
+ ***
348
+
349
+ > ## `lua.assignments()`
350
+
351
+ ```javascript
352
+ const lua = new Lua();
353
+ lua.define({
354
+ name: 'new-homepage',
355
+ buckets: {
356
+ variantA: { weight: 1 },
357
+ }
358
+ });
359
+
360
+ // Capture assignments
361
+ lua.assign();
362
+
363
+ // Retrieve all of the bucket assignments for the user
364
+ const buckets = lua.assignments();
365
+ assert.strictEqual(buckets['new-homepage'], 'variantA');
366
+ ```
367
+
368
+ > This provides the user with all of the bucket assignments for the current user.
369
+
370
+ > The returned information will be an object whose keys will be test names and values will be the current bucket assigned to the user.
371
+
372
+ ```javascript
373
+ // Example return
374
+ {
375
+ 'new-homepage': 'variantA',
376
+ 'some-test': 'some-bucket',
377
+ }
378
+ ```
379
+
380
+ **Returns**: `Object|Array`
381
+
382
+ ***
383
+
384
+ > ## `lua.extendAssignments`
385
+ >
386
+ > Extending assignments can be a useful way to augment your Lua implementation with third party software.
387
+
388
+ ```javascript
389
+ const lua = new Lua();
390
+
391
+ // Create a function that will modify assignments before you call `assignments`
392
+ lua.extendAssignments =
393
+ (assignments) => Object.assign(assignments, { foo: 'bar' })
394
+
395
+ // Retrieve all of the bucket assignments for the user
396
+ const buckets = lua.assignments();
397
+ assert.strictEqual(buckets['foo'], 'bar');
398
+ ```
399
+
400
+ > A more practical example could be to implement with a third party AB testing platform like Optimizely _(This uses pseudo code for brevity)_
401
+
402
+ ```javascript
403
+ lua.extendAssignments = (assignments) => {
404
+ if (window.optimizely)
405
+ for (const experiment in optimizely.experiments())
406
+ assignments[experiment.name] = experiment.bucket
407
+
408
+ return assignments
409
+ }
410
+ ```
411
+
412
+ **Returns**: `Object`
413
+
414
+ ***
415
+
416
+ ## Guide/FAQ
417
+
418
+ ### CSS Driven Tests
419
+
420
+ Tests logic may be potentially powered on solely CSS. Upon calling `assign`, if the script is running in the browser, a class per test will be added to the `body` tag with the test name and bucket in `BEM` syntax.
421
+
422
+ ```html
423
+ <body class="new-homepage--variantA"> <!-- Could be new-homepage--variantB -->
424
+ ```
425
+
426
+ ```css
427
+ .new-homepage--variantA {
428
+ /* Write custom styles for the new homepage test */
429
+ }
430
+ ```
431
+
432
+ ### Storing metadata associated with tests
433
+
434
+ Each bucket provided may have additional metadata associated with it, and may have its value retrieved by retrieving the assignments and definitions.
435
+
436
+ ```javascript
437
+ const lua = new Lua();
438
+ lua.define({
439
+ name: 'new-homepage',
440
+ buckets: {
441
+ variantA: { weight: 1, foo: 'bar' },
442
+ }
443
+ });
444
+
445
+ lua.assign();
446
+
447
+ const defs = lua.definitions();
448
+ const buckets = lua.assignments();
449
+ const bucket = buckets['new-homepage'];
450
+ const bar = defs.buckets[bucket].foo; // "bar"
451
+ ```
452
+
453
+ ## AI-Powered Personalization
454
+
455
+ Lua includes an optional AI personalization engine that uses OpenAI GPT models to make intelligent content decisions. The AI analyzes UTM parameters, referrer data, device type, and user visit history to select the best content variant or generate new personalized content.
456
+
457
+ ### Quick Setup
458
+
459
+ ```html
460
+ <!-- Include AI modules after the core script -->
461
+ <script src="utm-personalize.js" defer></script>
462
+ <script src="storage/weighted-history.js" defer></script>
463
+ <script src="prompts/personalization-prompts.js" defer></script>
464
+ <script src="ai-personalize.js" defer></script>
465
+ ```
466
+
467
+ ```javascript
468
+ // Enable AI personalization
469
+ LuaUTMPersonalize.personalize({
470
+ templates: {
471
+ 'gaming': { headline: 'Level Up Your Setup', subheadline: '...', ctaLabel: 'Explore Gaming', ctaLink: '/gaming' },
472
+ 'professional': { headline: 'Work Smarter', subheadline: '...', ctaLabel: 'View Collection', ctaLink: '/pro' },
473
+ 'default': { headline: 'Welcome', subheadline: '...', ctaLabel: 'Shop Now', ctaLink: '/shop' }
474
+ },
475
+ enableAI: true,
476
+ aiConfig: {
477
+ apiKey: 'sk-your-openai-key', // Your OpenAI API key
478
+ model: 'gpt-4o-mini', // Default model (configurable)
479
+ mode: 'select' // 'select' or 'generate'
480
+ }
481
+ }).then(function(decision) {
482
+ console.log('AI chose:', decision.intent, 'with confidence:', decision.aiResponse.confidence);
483
+ });
484
+ ```
485
+
486
+ ### Two Modes
487
+
488
+ **Select Mode**: AI chooses the best variant from your predefined templates. Predictable, fast, brand-consistent.
489
+
490
+ **Generate Mode**: AI creates entirely new headline, subheadline, and CTA text based on user context and your brand guidelines.
491
+
492
+ ### Key Features
493
+
494
+ - **Default model**: `gpt-4o-mini` (configurable to any OpenAI model)
495
+ - **Weighted history**: Tracks returning users with exponential decay
496
+ - **Automatic caching**: Caches AI decisions to minimize API calls
497
+ - **Graceful fallback**: Falls back to standard UTM engine if AI fails
498
+ - **Proxy support**: Use `apiUrl` instead of `apiKey` for production security
499
+
500
+ For comprehensive setup instructions, configuration reference, and security best practices, see the [AI Personalization Guide](docs/AI_PERSONALIZATION_GUIDE.md).
501
+
502
+ ***
503
+
504
+ ## Push to GitHub
505
+
506
+ To commit your changes and push to GitHub:
507
+
508
+ ```bash
509
+ # 1. Stage all changes
510
+ git add .
511
+
512
+ # 2. Commit with a descriptive message
513
+ git commit -m "feat: your change description"
514
+
515
+ # 3. Push to your remote (e.g. origin master or main)
516
+ git push origin master
517
+ # OR, if your default branch is main:
518
+ # git push origin main
519
+ ```
520
+
521
+ Before pushing, it’s a good idea to run the build and tests:
522
+
523
+ ```bash
524
+ pnpm run build
525
+ pnpm test
526
+ git add .
527
+ git commit -m "your message"
528
+ git push origin master
529
+ ```
530
+
531
+ To publish the demo to **GitHub Pages** (so others can try it online):
532
+
533
+ ```bash
534
+ pnpm run pages
535
+ # OR: npm run pages
536
+ ```
537
+
538
+ This pushes the `master` branch to the `gh-pages` branch; the demo will be available at `https://<your-username>.github.io/Lua-Dynamic-Website-/` (or your repo’s Pages URL).
539
+
540
+ ***
541
+
542
+ ## License
543
+
544
+ **MIT Licensing**
545
+
546
+ Copyright (c) 2026 Okey Amy
547
+
548
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
549
+
550
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
551
+
552
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.