@omega.js/client 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/LICENSE +98 -0
  2. package/README.md +874 -0
  3. package/dist/index.js +999 -0
  4. package/dist/modules/analytics.js +584 -0
  5. package/dist/modules/auth.js +469 -0
  6. package/dist/modules/bindings.js +319 -0
  7. package/dist/modules/device.js +282 -0
  8. package/dist/modules/dom.js +96 -0
  9. package/dist/modules/features.js +30 -0
  10. package/dist/modules/firestore.js +313 -0
  11. package/dist/modules/form-manager.js +1577 -0
  12. package/dist/modules/icon-core.js +226 -0
  13. package/dist/modules/icon-renderer.js +149 -0
  14. package/dist/modules/live-page.js +235 -0
  15. package/dist/modules/logger.js +36 -0
  16. package/dist/modules/motion.js +853 -0
  17. package/dist/modules/notifications.js +433 -0
  18. package/dist/modules/path-prefix.js +22 -0
  19. package/dist/modules/request.js +223 -0
  20. package/dist/modules/sentry.js +108 -0
  21. package/dist/modules/service-worker.js +237 -0
  22. package/dist/modules/storage.js +133 -0
  23. package/dist/modules/triggers.js +117 -0
  24. package/dist/modules/utilities.js +479 -0
  25. package/dist/modules/vert-document.js +354 -0
  26. package/dist/modules/verts.js +1133 -0
  27. package/dist/vendor/account/engine.js +182 -0
  28. package/dist/vendor/account/features.js +220 -0
  29. package/dist/vendor/account/index.js +53 -0
  30. package/dist/vendor/account/schema.js +272 -0
  31. package/dist/vendor/account/subscription.js +38 -0
  32. package/dist/vendor/analytics/adapters/ga4.js +26 -0
  33. package/dist/vendor/analytics/adapters/meta.js +26 -0
  34. package/dist/vendor/analytics/adapters/resolve.js +130 -0
  35. package/dist/vendor/analytics/adapters/tiktok.js +27 -0
  36. package/dist/vendor/analytics/catalog.js +908 -0
  37. package/dist/vendor/analytics/consent.js +49 -0
  38. package/dist/vendor/analytics/core.js +141 -0
  39. package/dist/vendor/analytics/identity.js +136 -0
  40. package/dist/vendor/analytics/index.js +170 -0
  41. package/dist/vendor/analytics/logger.js +40 -0
  42. package/dist/vendor/analytics/transports/browser.js +110 -0
  43. package/dist/vendor/monitoring/browser.js +207 -0
  44. package/dist/vendor/monitoring/core.js +180 -0
  45. package/dist/vendor/monitoring/logger.js +39 -0
  46. package/docs/architecture.md +59 -0
  47. package/docs/bindings.md +235 -0
  48. package/docs/build-system.md +32 -0
  49. package/docs/cdp-debugging.md +29 -0
  50. package/docs/code-patterns.md +96 -0
  51. package/docs/common-tasks.md +36 -0
  52. package/docs/dependencies.md +19 -0
  53. package/docs/index.md +159 -0
  54. package/docs/modules.md +180 -0
  55. package/docs/shared/agent-docs.md +89 -0
  56. package/docs/shared/analytics.md +612 -0
  57. package/docs/shared/brands.md +51 -0
  58. package/docs/shared/breaking-changes.md +497 -0
  59. package/docs/shared/config.md +1387 -0
  60. package/docs/shared/deploys.md +215 -0
  61. package/docs/shared/icons.md +201 -0
  62. package/docs/shared/local-dev.md +147 -0
  63. package/docs/shared/logging.md +202 -0
  64. package/docs/shared/monitoring.md +153 -0
  65. package/docs/shared/publishing.md +183 -0
  66. package/docs/shared/rulings.md +34 -0
  67. package/docs/shared/testing.md +147 -0
  68. package/docs/shared/theming.md +604 -0
  69. package/docs/shared/translation.md +291 -0
  70. package/docs/shared/updates.md +61 -0
  71. package/docs/testing.md +9 -0
  72. package/package.json +65 -0
@@ -0,0 +1,235 @@
1
+ # Bindings (`data-omega-bind`)
2
+
3
+ The `data-omega-bind` attribute declaratively binds DOM elements to state data (auth, plan, roles, usage, custom state) managed by `omega.bindings()`. **Always prefer omega-bindings over manual JS class toggling** for anything based on user/auth state — if an element's visibility or content depends on the user object, use `data-omega-bind` in HTML, not `classList.toggle('d-none', ...)` or `.hidden` from JS.
4
+
5
+ ## HTML Syntax
6
+
7
+ ```html
8
+ <element data-omega-bind="@action path.to.data"></element>
9
+ ```
10
+
11
+ ### Multiple Bindings: MUST Use Commas
12
+
13
+ Multiple bindings are **comma-separated**. The parser splits by comma first, then parses each part as `@action expression`.
14
+
15
+ ```html
16
+ <!-- CORRECT: comma-separated -->
17
+ <element data-omega-bind="@show auth.user, @attr src auth.user.photoURL"></element>
18
+
19
+ <!-- WRONG: space-separated — gets parsed as ONE binding with action=@show, expression="auth.user @attr src auth.user.photoURL" -->
20
+ <element data-omega-bind="@show auth.user @attr src auth.user.photoURL"></element>
21
+ ```
22
+
23
+ **Why this matters:** The parser splits on `,` then finds the first space within each part to separate `@action` from `expression`. Without commas, everything after the first `@action` is treated as a single expression string, producing broken behavior with no error.
24
+
25
+ ## Supported Actions
26
+
27
+ | Action | Syntax | Description |
28
+ |--------|--------|-------------|
29
+ | `@text` | `@text path` | Set text content |
30
+ | `@value` | `@value path` | Set input/textarea value |
31
+ | `@show` | `@show condition` | Show element if truthy |
32
+ | `@hide` | `@hide condition` | Hide element if truthy |
33
+ | `@attr` | `@attr name path` | Set HTML attribute value |
34
+ | `@style` | `@style property path` | Set CSS property or CSS variable |
35
+
36
+ ## Condition Operators
37
+
38
+ ```html
39
+ <!-- Truthy check -->
40
+ <div data-omega-bind="@show auth.user">Visible when logged in</div>
41
+
42
+ <!-- Negation (!) -->
43
+ <div data-omega-bind="@show !auth.user">Visible when NOT logged in</div>
44
+
45
+ <!-- Comparisons (===, !==, ==, !=, >, <, >=, <=) -->
46
+ <div data-omega-bind="@show auth.account.plan.id === 'premium'">Premium only</div>
47
+ <div data-omega-bind="@show checkout.errorCount > 0">Has errors</div>
48
+ ```
49
+
50
+ No logic operators (`&&`, `||`) in conditions — keep conditions simple. Right-side comparison values are auto-parsed: quoted strings, numbers, booleans, null.
51
+
52
+ ## Common Auth Patterns
53
+
54
+ ```html
55
+ <!-- Show for anonymous users -->
56
+ <div data-omega-bind="@show !auth.user">
57
+ <a href="/signup">Create free account</a>
58
+ </div>
59
+
60
+ <!-- Show for signed-in users -->
61
+ <div data-omega-bind="@show auth.user">
62
+ <a href="/pricing">Upgrade your plan</a>
63
+ </div>
64
+
65
+ <!-- Admin-only elements -->
66
+ <div data-omega-bind="@show auth.account.roles.admin">Admin panel</div>
67
+
68
+ <!-- User data binding -->
69
+ <img data-omega-bind="@show auth.user, @attr src auth.user.photoURL, @attr alt auth.user.displayName">
70
+ <span data-omega-bind="@text auth.user.displayName">Loading...</span>
71
+ <input data-omega-bind="@value auth.user.email">
72
+ ```
73
+
74
+ ## Available State Paths
75
+
76
+ ### Auth paths (automatically populated by @omega.js/client)
77
+
78
+ ```
79
+ auth.user # Firebase user object (truthy = signed in)
80
+ auth.user.uid # User ID
81
+ auth.user.email # Email
82
+ auth.user.displayName # Display name
83
+ auth.user.photoURL # Avatar URL
84
+ auth.user.emailVerified # Boolean
85
+ auth.account.plan.id # Plan ID (e.g. 'basic', 'premium')
86
+ auth.account.roles.admin # Boolean
87
+ auth.account.roles.betaTester # Boolean
88
+ ```
89
+
90
+ ### Usage paths (server usage — auto-populated by @omega.js/client)
91
+
92
+ ```
93
+ usage.{feature}.monthly # Current monthly usage count
94
+ usage.{feature}.daily # Current daily usage count
95
+ usage.{feature}.limit # Plan limit for this feature
96
+ ```
97
+
98
+ Example: `usage.credits.monthly`, `usage.credits.limit`
99
+
100
+ Seeded on auth settle from `account.usage` + the site's payment plan config. Refreshed after every `omega.request()` call from the `omega-properties` response header — the backend attaches fresh usage counters to every response, so bound elements stay current automatically.
101
+
102
+ ### Device paths (local stats — auto-populated on initialize)
103
+
104
+ ```
105
+ device.installed # First-seen timestamp (ms)
106
+ device.session.count # Session count on this device
107
+ device.version.current # App version currently running
108
+ device.version.isNew # True on the first run after an update
109
+ device.duration.total.days # Time since install (also: session.*, other units)
110
+ ```
111
+
112
+ From the `device` module (localStorage / extension storage) — LOCAL device stats, deliberately a different key than the server-derived `usage` above.
113
+
114
+ ### Custom state (set via JS)
115
+
116
+ Any custom paths set via `omega.bindings().update(stateObject)`.
117
+
118
+ ## JavaScript API
119
+
120
+ ```javascript
121
+ // Update bindings with state data
122
+ omega.bindings().update({
123
+ checkout: {
124
+ product: { name: 'Pro Plan' },
125
+ error: { show: false, message: '' },
126
+ },
127
+ });
128
+
129
+ // Get current binding context
130
+ const context = omega.bindings().getContext();
131
+
132
+ // Clear all bindings
133
+ omega.bindings().clear();
134
+ ```
135
+
136
+ ## Skeleton Loaders
137
+
138
+ ### How Skeletons Work
139
+
140
+ The `omega-binding-skeleton` class shows a shimmer animation until data loads. Skeletons resolve **only when at least one of the element's bindings is actually processed** — meaning the binding's root key must be in the `updatedKeys` for that `update()` call.
141
+
142
+ When `_updateBindings` processes an element:
143
+
144
+ 1. Executes each binding action (`@text`, `@show`, `@attr`, etc.)
145
+ 2. Each action returns `true` (processed) or `false` (skipped because root key wasn't updated)
146
+ 3. **Only if at least one action was processed:** adds `omega-bound` class (triggers CSS fade-out transition)
147
+ 4. After 300ms, removes `omega-binding-skeleton` class (shimmer disappears)
148
+
149
+ **Root key scoping matters for skeletons.** If an element is bound to `checkout.pricing.total` and `update({ auth: ... })` fires, that element's skeleton is NOT resolved — the binding is skipped entirely because `checkout` is not in `updatedKeys`.
150
+
151
+ ```html
152
+ <!-- Skeleton resolves when 'auth' key is updated -->
153
+ <span class="omega-binding-skeleton" data-omega-bind="@text auth.user.displayName">&nbsp;</span>
154
+
155
+ <!-- Skeleton resolves when 'checkout' key is updated (NOT when 'auth' updates) -->
156
+ <span class="omega-binding-skeleton" data-omega-bind="@text checkout.pricing.total">&nbsp;</span>
157
+ ```
158
+
159
+ ### Multi-phase binding example (e.g., checkout page)
160
+
161
+ When bindings fire in phases, skeletons resolve independently per root key:
162
+
163
+ ```javascript
164
+ // Phase 1: Global auth bindings fire
165
+ omega.bindings().update({ auth: { user: {...} } });
166
+ // → Only elements bound to 'auth.*' resolve their skeletons
167
+ // → Elements bound to 'checkout.*' keep their skeletons
168
+
169
+ // Phase 2: After API fetches complete
170
+ omega.bindings().update({ checkout: { pricing: {...} } });
171
+ // → Now elements bound to 'checkout.*' resolve their skeletons
172
+ ```
173
+
174
+ This prevents checkout skeletons from disappearing prematurely when global auth bindings fire before checkout data is available.
175
+
176
+ ### Skeleton Pattern
177
+
178
+ Use `&nbsp;` as placeholder content (prevents zero-width collapse so the shimmer is visible):
179
+
180
+ ```html
181
+ <span class="omega-binding-skeleton" data-omega-bind="@text auth.user.displayName">&nbsp;</span>
182
+ ```
183
+
184
+ **Do NOT use text like "Loading..." as placeholder** — it flashes visible text before the shimmer kicks in. Use `&nbsp;` for a clean shimmer-only experience.
185
+
186
+ ### Composite Text in Skeletons
187
+
188
+ For composite text (e.g., "$0.00 due today"), do NOT mix static text with a binding span inside a skeleton div. Instead, create a dedicated pre-formatted value in the state and bind with a single `@text`:
189
+
190
+ ```javascript
191
+ // CORRECT: compose the text in JS, bind as single value
192
+ omega.bindings().update({
193
+ checkout: {
194
+ totalDueText: `${formatCurrency(prices.total)} due today`,
195
+ },
196
+ });
197
+ ```
198
+
199
+ ```html
200
+ <!-- CORRECT: single binding for composite text -->
201
+ <span class="omega-binding-skeleton" data-omega-bind="@text checkout.totalDueText">&nbsp;</span>
202
+
203
+ <!-- WRONG: mixing static text with binding inside skeleton -->
204
+ <span class="omega-binding-skeleton">
205
+ <span data-omega-bind="@text checkout.pricing.total"></span> due today
206
+ </span>
207
+ ```
208
+
209
+ ## Implementation Notes
210
+
211
+ - Uses the `hidden` attribute for show/hide (`[hidden] { display: none !important; }`)
212
+ - Queries `[data-omega-bind]` on each `update()` call — handles dynamic elements
213
+ - Auth bindings are auto-populated when `omega.auth().listen()` fires
214
+ - When `updatedKeys` is `null` (e.g., from `clear()`), ALL bindings fire
215
+
216
+ ### Root Key Update Filtering
217
+
218
+ `_shouldUpdatePath` checks the **root key** (first segment before `.`) of each binding's expression path against the `updatedKeys` from the `update()` call. A binding only fires when its root key was updated.
219
+
220
+ ```javascript
221
+ // This update ONLY triggers bindings whose expression starts with 'checkout'
222
+ omega.bindings().update({
223
+ checkout: { pricing: { total: 9.99 } },
224
+ });
225
+ // Fires: @text checkout.pricing.total, @show checkout.active
226
+ // Skips: @text auth.user.displayName (root key is 'auth', not updated)
227
+ ```
228
+
229
+ Negation (`!`) is stripped before root-key checking, so `@show !auth.user` fires when `auth` is updated.
230
+
231
+ ## See also
232
+
233
+ - [modules.md](modules.md) — quick reference for all nine modules
234
+ - [architecture.md](architecture.md) — module dependency graph
235
+ - `src/modules/bindings.js` — the implementation
@@ -0,0 +1,32 @@
1
+ # Build System
2
+
3
+ ## prepare-package
4
+
5
+ The library uses `prepare-package` for ES5 transpilation:
6
+
7
+ ```json
8
+ {
9
+ "preparePackage": {
10
+ "input": "./src",
11
+ "output": "./dist"
12
+ }
13
+ }
14
+ ```
15
+
16
+ **Commands**:
17
+ - `npm run prepare` — Build once
18
+ - `npm start` — Watch mode
19
+ - `npm test` — Run Mocha tests
20
+
21
+ ## Package Exports
22
+
23
+ ```json
24
+ {
25
+ "main": "dist/index.js",
26
+ "module": "src/index.js",
27
+ "exports": {
28
+ ".": "./dist/index.js",
29
+ "./modules/*": "./dist/modules/*"
30
+ }
31
+ }
32
+ ```
@@ -0,0 +1,29 @@
1
+ # CDP Debugging (driving a live browser)
2
+
3
+ How to drive a browser you can CONTROL — see a consuming site live, screenshot it, click, type, read console logs, inspect network requests — for agents (Claude via MCP/CDP) and humans. @omega.js/client has no dev server of its own; it runs INSIDE consumers (@omega.js/web sites, @omega.js/extension extensions, @omega.js/desktop renderers), so browser verification means driving a consumer.
4
+
5
+ > Mirrored across the five sister frameworks (UJM / @omega.js/backend / @omega.js/extension / @omega.js/desktop / @omega.js/client) — same core section, framework-flavored. Edit all five together.
6
+
7
+ ## The browser: your Claude session owns one
8
+
9
+ Browser work runs through the **`chrome-devtools` MCP** (via mcp-router). There is NO launch procedure anymore — no ports, no profile dirs, no curl checks:
10
+
11
+ - **Just call the tools** — `new_page`, `navigate_page`, `take_screenshot`, `click`, `fill`, `evaluate_script`, `list_console_messages`, `list_network_requests`. The browser auto-launches on the first call.
12
+ - **Each Claude session gets its OWN private Chrome** (`--isolated`): temp profile, CDP over an internal pipe. Parallel sessions cannot see or touch each other's pages — open and close pages freely, the whole browser is yours.
13
+ - **It dies with the session.** No orphans, no cleanup, nothing to kill.
14
+ - **Ephemeral profile** — cookies/logins do NOT persist between sessions. If a flow needs auth, log in during the task.
15
+ - **Self-signed HTTPS is pre-accepted** (`--acceptInsecureCerts` in the upstream) — dev servers load without certificate interstitials.
16
+ - **NEVER quit/kill Chrome by app name** (`killall "Google Chrome"`, osascript) — that's the user's personal browser, not yours.
17
+
18
+ Humans: the agent's Chrome window is visible — you can watch it drive. Full reference: `~/.claude/mcp-server/servers/chrome-devtools/CLAUDE.md`.
19
+
20
+ ## Electron apps are the exception (attach, don't launch)
21
+
22
+ An Electron dev app is a running singleton — you ATTACH to it instead of launching a browser: the `chrome-devtools-electron` MCP upstream (reads `OMEGA_CDP_PORT`, default 9222, expanded once at session start) or @omega.js/desktop's per-invocation `npx omega cdp`. See @omega.js/desktop's `docs/cdp-debugging.md`.
23
+
24
+ ## @omega.js/client specifics
25
+
26
+ - **Verify @omega.js/client behavior through a consumer.** The usual host is a UJM site's dev server: **`https://localhost:4000` — NEVER the LAN IP** (`https://192.168.x.x:...`); port 4000 by default, increments (4001, …) when multiple sites run — exact port in the WEBSITE project's `.temp/_config_browsersync.yml`. To test uncommitted @omega.js/client changes, link the local @omega.js/client into the consumer first (see the consumer framework's dev-install flow), then drive the site.
27
+ - What to exercise from the browser: auth flows (`omega.auth()` states, the Settler Pattern), `data-omega-bind` bindings reacting to state changes (`evaluate_script` to mutate state, `take_snapshot`/`take_screenshot` to verify DOM), Firestore reads/writes on the network tab, and console cleanliness (@omega.js/client logs its module lifecycle).
28
+ - Ephemeral profile ⇒ auth'd testing means logging in through the consumer's real UI at the start of the session (test creds).
29
+ - @omega.js/client inside a @omega.js/extension extension or @omega.js/desktop renderer: drive those through their own surfaces — @omega.js/extension's `chrome-devtools-extension` upstream, @omega.js/desktop's `chrome-devtools-electron`/`mgr cdp` (see those repos' `docs/cdp-debugging.md`).
@@ -0,0 +1,96 @@
1
+ # Key Patterns
2
+
3
+ ## 1. Early Return (Short-Circuit)
4
+
5
+ Always use early returns instead of nested conditionals:
6
+
7
+ ```javascript
8
+ // CORRECT
9
+ function doSomething() {
10
+ if (!condition) {
11
+ return;
12
+ }
13
+ // Long code block...
14
+ }
15
+
16
+ // WRONG
17
+ function doSomething() {
18
+ if (condition) {
19
+ // Long code block...
20
+ }
21
+ }
22
+ ```
23
+
24
+ ## 2. DOM Element Naming
25
+
26
+ Prefix DOM element variables with `$`:
27
+
28
+ ```javascript
29
+ const $button = document.querySelector('.submit-btn');
30
+ const $input = document.getElementById('email');
31
+ ```
32
+
33
+ ## 3. Logical Operator Formatting
34
+
35
+ Place operators at the START of continuation lines:
36
+
37
+ ```javascript
38
+ // CORRECT
39
+ const result = conditionA
40
+ || conditionB
41
+ || conditionC;
42
+
43
+ // WRONG
44
+ const result = conditionA ||
45
+ conditionB ||
46
+ conditionC;
47
+ ```
48
+
49
+ ## 4. Firestore Path Syntax
50
+
51
+ Prefer path syntax over collection/doc chaining:
52
+
53
+ ```javascript
54
+ // PREFERRED
55
+ db.doc('users/userId')
56
+
57
+ // ALSO SUPPORTED
58
+ db.doc('users', 'userId')
59
+ ```
60
+
61
+ ## 5. Dynamic Imports
62
+
63
+ Firebase modules are dynamically imported to reduce bundle size:
64
+
65
+ ```javascript
66
+ const { initializeApp } = await import('firebase/app');
67
+ const { getAuth } = await import('firebase/auth');
68
+ ```
69
+
70
+ ## 6. Configuration Deep Merge
71
+
72
+ User config is deep-merged with defaults in `_processConfiguration()`. Only override what you need:
73
+
74
+ ```javascript
75
+ // Defaults defined in _processConfiguration()
76
+ const defaults = {
77
+ environment: 'production',
78
+ firebase: { app: { enabled: true, config: {} } },
79
+ // ...
80
+ };
81
+ ```
82
+
83
+ ## 7. Click Triggers
84
+
85
+ Click-driven UI never hand-rolls a listener: it registers on the shared trigger
86
+ registry (`modules/triggers.js`), which owns the ONE delegated `document` click
87
+ listener. The class is always `omega-<name>` — callers never spell it:
88
+
89
+ ```javascript
90
+ import { registerTrigger } from '@omega.js/client/modules/triggers.js';
91
+
92
+ // A click on `.omega-signout` (or anything inside one) runs this
93
+ registerTrigger('signout', async (event, element) => {
94
+ // Handle signout
95
+ });
96
+ ```
@@ -0,0 +1,36 @@
1
+ # Common Tasks
2
+
3
+ ## Adding a New Utility Function
4
+
5
+ 1. Add function to `src/modules/utilities.js`
6
+ 2. Export it: `export function myFunction() { ... }`
7
+ 3. Update README.md with documentation
8
+ 4. Run `npm run prepare` to build
9
+
10
+ ## Adding a New Module
11
+
12
+ 1. Create `src/modules/my-module.js`
13
+ 2. Export class: `export default class MyModule { constructor(manager) { ... } }`
14
+ 3. Import in `src/index.js`: `import MyModule from './modules/my-module.js'`
15
+ 4. Add to Manager constructor: `this._myModule = new MyModule(this)`
16
+ 5. Add getter: `myModule() { return this._myModule; }`
17
+ 6. Update README.md
18
+ 7. Run `npm run prepare`
19
+
20
+ ## Modifying Configuration Defaults
21
+
22
+ 1. Edit `_processConfiguration()` in `src/index.js`
23
+ 2. Add to `defaults` object (e.g., `payment: { providers: {}, products: [] }`)
24
+ 3. Document in README.md Configuration section
25
+
26
+ ## Payment Configuration
27
+
28
+ Payment config shape mirrors OMEGA (the SSOT) — same key names used in @omega.js/backend, UJM, and @omega.js/desktop:
29
+ - `providers`: Stripe, PayPal, Chargebee, Coinbase (publishable keys / client IDs)
30
+ - `products`: Array of `{ id, name, type, limits: { feature: N }, prices, trial, paypal, stripe, chargebee }` — used to resolve usage limits on the frontend AND drive checkout flows
31
+
32
+ ## Adding a Data Binding Action
33
+
34
+ 1. Edit `_executeAction()` in `src/modules/bindings.js`
35
+ 2. Add case for new action (e.g., `@class`)
36
+ 3. Document in README.md Data Binding section
@@ -0,0 +1,19 @@
1
+ # Dependencies & Important Notes
2
+
3
+ ## Dependencies
4
+
5
+ | Package | Purpose |
6
+ |---------|---------|
7
+ | `firebase` (^12.x) | Auth, Firestore, Messaging |
8
+ | `@sentry/browser` (^10.x) | Error tracking |
9
+ | `lodash` (^4.x) | get/set for path-based access |
10
+ | `itwcw-package-analytics` | Analytics (internal) |
11
+
12
+ ## Important Notes
13
+
14
+ 1. **DO NOT MODIFY `_legacy/`** — Reference only for historical context
15
+ 2. **Backwards compatibility is NOT required** — Just change to the new way
16
+ 3. **Prefer `fs-jetpack`** over `fs` for any file operations in tests/scripts
17
+ 4. **No TypeScript** — This is a pure JavaScript library
18
+ 5. **Template strings** — Use backticks for string interpolation
19
+ 6. **Modular design** — Keep modules focused and small
package/docs/index.md ADDED
@@ -0,0 +1,159 @@
1
+ # OMEGA Client (@omega.js/client)
2
+
3
+ > **Note for contributors and Claude:** This file is the guide for `@omega.js/client` — identity, top-level conventions, and a map to the deep references. It lives in the monorepo's `docs/` tree and is loaded on demand (the omega Claude plugin's hooks inject it by context; the repo-root AGENTS.md map is the one agent entry — packages carry no agent docs). The **meat** (module APIs, patterns, behavior tables) lives in the package's own [`docs/<topic>.md`](../docs) files. When extending or adding content, write it in the matching `docs/*.md` file and cross-link from here — do NOT inline it. If a topic doesn't have a doc yet, create one.
4
+
5
+ ## Identity
6
+
7
+ OMEGA Client is a modern JavaScript utility library for web applications with Firebase integration. It runs in the browser, in Electron's renderer process, and inside browser extensions (content scripts, popups, background pages). Provides:
8
+
9
+ - A singleton `Manager` instance exposing authentication, reactive DOM data binding, Firestore, storage, push notifications, error tracking (Sentry), service-worker helpers, DOM/utility functions, and `omega.request()` — the harmonized API-fetch layer (fresh Bearer token, automatic `omega-properties` processing with server usage synced into bindings)
10
+ - Lazy Firebase imports to keep consumer bundles small
11
+ - Reactive `data-omega-bind` DOM directives wired to auth + usage state
12
+ - A `resolveSubscription()` helper unified with @omega.js/backend's `User.resolveSubscription()` so subscription-state logic is identical across frontend and backend
13
+
14
+ ### Consumed by the frontend Manager family
15
+
16
+ OMEGA Client is the runtime singleton powering **@omega.js/web**, **@omega.js/extension**, and **@omega.js/desktop**. Each framework initializes the singleton once and exposes it as `manager.omega`. Any consumer of those frameworks gets a fully-wired @omega.js/client via `import omega from '@omega.js/client'`.
17
+
18
+ ## Recommended skills
19
+
20
+ - **`omega:client`** — the router skill from the omega Claude plugin. The inject hook loads it automatically when the session works inside `packages/client` (own-name match only — a dependency on the runtime says nothing about the session); it points back to this guide + `docs/` (the SSOT).
21
+ - **`js:patterns`** — JavaScript/Node.js conventions: file structure, JSDoc, defensive coding (`?.` usage), template literals, `package.json` conventions. Auto-loads when creating new `.js` files or touching JS module structure.
22
+
23
+ ## Quick Start
24
+
25
+ ### For Consuming Projects
26
+
27
+ OMEGA Client is consumed indirectly through @omega.js/web, @omega.js/extension, or @omega.js/desktop — those frameworks initialize the singleton for you. Inside any consuming code:
28
+
29
+ ```javascript
30
+ import omega from '@omega.js/client';
31
+
32
+ omega.auth().listen({ once: true }, async () => { /* auth settled */ });
33
+ omega.utilities().escapeHTML(untrustedText);
34
+ omega.firestore().doc('users/abc').get();
35
+ ```
36
+
37
+ ### For Framework Development (This Repository)
38
+
39
+ 1. `npm install` — install OMEGA Client's own deps
40
+ 2. `npm run prepare` — build once: copies `src/` → `dist/` via prepare-package (ES5 transpile)
41
+ 3. `npm start` — watch mode (rebuild on change)
42
+ 4. `npm test` — run Mocha tests
43
+
44
+ > **Important:** OMEGA Client is a library, not an app. There is no `npm run build` / `npm run serve` here. Consume it from inside an @omega.js/web / @omega.js/extension / @omega.js/desktop project for end-to-end behavior.
45
+
46
+ ## Architecture
47
+
48
+ OMEGA Client exports a singleton `Manager` instance from `src/index.js`. Every `import omega from '@omega.js/client'` returns the same already-initialized object — do NOT call `new Manager()`, and do NOT pass `omega` through function params or module-level variables.
49
+
50
+ The singleton owns thirteen feature modules under `src/modules/`: `storage`, `auth`, `bindings`, `firestore`, `notifications`, `service-worker`, `sentry`, `dom`, `utilities` (the untrusted-text surface — `escapeHTML`, `sanitizeURL`, and the escape-first `renderMarkdown` that composes them, plus clipboard/notification/platform helpers), `device` (local install/session stats — binds the `device` key), `request` (harmonized API fetch — `omega.request()`; also consumed standalone by desktop main + the extension service worker via `createRequest`), `verts` (the fallback-ladder ad engine, adblock-safe naming — AdSense provider lane + in-house/company units, shared by web/desktop/extension), and `analytics` (runtime event tracking on every runtime, built on the shared `@omega.js/analytics` core — the one home of GA4 event-name and Measurement Protocol semantics; the extension posts through the Measurement Protocol, web hands the event to the page's own `gtag` so the api_secret never reaches a page, and desktop's renderer forwards over the preload's IPC bridge so the main process is the one sender ([#411](https://github.com/Omega-JS-Stack/omega/issues/411))). Firebase modules are dynamically imported to keep the bundle small. Alongside them live the transport-free standalone modules (`icon-core`, `icon-renderer`, `motion`, `live-page`, `vert-document` — the ONE vert unit document renderer, consumed by the verts module's promo lane and by `@omega.js/backend`'s serve route ([docs/web/ads-system.md](../web/ads-system.md)), `triggers` — the click-trigger registry below, `form-manager` — lightweight form state: initializing → ready ⇄ submitting) that embedding frameworks import by subpath and boot themselves — `motion` is the shared animation engine behind the `data-omega-*` attributes (classy v2), and `live-page` is the self-refreshing-page kit (`swap` writes a section only when its markup changed, `loading` is the first-paint spinner, `createFeedPoller` owns the declared feed table with its in-flight count and keep-last-good-on-failure rule, taking an `omega.request`-shaped fetcher as an argument). See [docs/architecture.md](../docs/architecture.md) for the directory structure and module dependency graph, and [docs/modules.md](../docs/modules.md) for the API reference of each module.
51
+
52
+ Pages built on this runtime follow the **page paint contract** ([docs/web/page-contract.md](../web/page-contract.md)): the client fills the `auth`, `usage`, `config` and `device` binding roots at auth settle so page code never hides its DOM waiting for a user, `bindings.update()` filters by root key so a spot that must wait for a server answer lives under a root the early paint does not publish, and `FormManager`'s `addGate()` / `resolveGate()` hold a form's submit controls disabled until every async answer it depends on has landed.
53
+
54
+ ### The wakeup ping (`omega.request(url, { wakeup: true })`)
55
+
56
+ A fire-and-forget GET that warms a cold backend and nothing else. @omega.js/backend's middleware sees `wakeup` in the request data and answers it BEFORE it loads a route or authenticates ([docs/backend/index.md](../backend/index.md)), so any route warms the same function at the same cost and none of them runs. The call mints no ID token, reads no response body, and resolves rather than throwing when the network is down, so a caller can fire it and move on:
57
+
58
+ ```javascript
59
+ import { WAKEUP_ROUTE } from '@omega.js/client/modules/request.js';
60
+
61
+ omega.request(WAKEUP_ROUTE, { wakeup: true });
62
+ ```
63
+
64
+ **One route, every surface.** `WAKEUP_ROUTE` (`/omega/health`) is exported by `modules/request.js` and named by every caller instead of "the route I am about to need": a wakeup never runs a route, so a per-caller route would be a dozen spellings of one warm function — and desktop main and the extension cannot reach a web-side constant anyway ([#644](https://github.com/Omega-JS-Stack/omega/issues/644)).
65
+
66
+ **Where it fires.** Every entry point whose first user action is a backend call warms it on load, never awaited:
67
+
68
+ | Surface | Site | The call it is warming for |
69
+ |---|---|---|
70
+ | @omega.js/web | `/pricing` | the checkout the plan buttons lead to |
71
+ | @omega.js/web | `/payment/checkout` | `/omega/payments/intent` |
72
+ | @omega.js/web | `/signup` | `/omega/user/signup` — the 14-second cold start measured live 2026-08-27 |
73
+ | @omega.js/web | `/signin` | `/omega/user/signup` behind a first-time OAuth signin |
74
+ | @omega.js/web | `/account` | the billing portal, plan switch, cancel, refund, API key, data request, delete |
75
+ | @omega.js/web | `/token` | `/omega/user/token` (also where a desktop app and an extension sign in) |
76
+ | @omega.js/web | `/connections/callback` | `/omega/user/connections` |
77
+ | @omega.js/web | `/feedback` | `/omega/user/feedback` |
78
+ | @omega.js/web | `/portal/email-preferences` | `/omega/marketing/email-preferences` |
79
+ | @omega.js/web | `/download`, only where the notify-me form renders | `/omega/general/email` |
80
+ | @omega.js/web | the `newsletter-cta` band — on first FOCUS, not on load | `/omega/marketing/contact` |
81
+ | @omega.js/desktop | the renderer's auth bridge (`_wireAuthBridge`) | `/omega/user/token`, via main's sync-request |
82
+ | @omega.js/extension | every surface's `syncWithBackground()` | `/omega/user/token`, via background's sync |
83
+
84
+ The newsletter band is the one that waits for an interaction: it rides most pages, so a load-time ping would warm a function for every passive visitor scrolling past. A focus is the intent.
85
+
86
+ Deliberately NOT pinging: the payment confirmation page (its check is a Firestore read, not a backend call), the contact form (it posts to Slapform, a third party), and the admin dashboard (internal tooling, whose seven load-time fetches are their own warm-up).
87
+
88
+ ### The session probe (`omega.auth().probeSession()`)
89
+
90
+ One forced token refresh at a moment of doubt, and nothing else ([#798](https://github.com/Omega-JS-Stack/omega/issues/798)). Firebase asks the Auth server about a persisted session at page load and at the hourly refresh and at no other moment, so a revoked, disabled or deleted account keeps an open tab signed in until a reload, and a dev stack whose auth emulator restarted leaves the page holding a session the server no longer has. The probe never asks OUR backend, so dev and production run the same code.
91
+
92
+ `probeSession()` exchanges the refresh token with the Auth server (`getIdToken(user, true)`) and classifies the answer:
93
+
94
+ | Result | When | What the client does |
95
+ |---|---|---|
96
+ | `signed-out` | no `currentUser` | nothing; a probe on a signed-out client is a no-op |
97
+ | `alive` | the refresh succeeded | nothing |
98
+ | `gone` | a DEFINITE `auth/*` verdict, meaning any `auth/*` code that is not one of the three transient ones below (`auth/user-token-expired`, `auth/user-disabled`, `auth/user-not-found`, `auth/invalid-refresh-token`, …) | `signOut()`, whose `onAuthStateChanged` emission drives each surface's own policy listener |
99
+ | `unknown` | the three TRANSIENT codes (`auth/network-request-failed`, `auth/too-many-requests`, `auth/internal-error`), or an error carrying no auth code | keeps the user: a bad connection, a throttle and a failing Auth server are no verdict on the session, and neither is the "Backend starting" window |
100
+
101
+ **Three moments, no timer.** The tab coming back into view (`visibilitychange` to visible) and the network coming back (`online`) are wired once per manager instance beside the auth state listener, guarded for a host with no `document` (the extension's background service worker). The third is a 401 on an authenticated `omega.request()`: the request layer calls its optional `onUnauthorized` dep WITHOUT awaiting it and throws the caller's error unchanged, so nothing waits on a token refresh and a failed probe is never the request's failure. Probes coalesce, one in flight per Auth instance, because focus, online and a 401 arrive together all the time and are all asking the same question. A periodic ping would be a request per open tab for nothing; page load and the hourly refresh stay Firebase's own.
102
+
103
+ Web, desktop and extension get every bit of this from the client, with no framework code of their own. Web's page auth policy is what redirects on the resulting signed-out state ([docs/web/page-contract.md](../web/page-contract.md)).
104
+
105
+ ### Click triggers (`modules/triggers.js`)
106
+
107
+ The ONE click-trigger registry every surface shares ([#16](https://github.com/Omega-JS-Stack/omega/issues/16)). A trigger is markup wiring — a class means "clicking this runs that action", with no per-page JS — and the client owns the single delegated `document` click listener behind all of them.
108
+
109
+ ```javascript
110
+ import { registerTrigger } from '@omega.js/client/modules/triggers.js';
111
+
112
+ registerTrigger('signout', async (event, element) => { /* ... */ });
113
+ ```
114
+
115
+ - **The class is always `omega-<name>`** — `registerTrigger('signout', …)` answers `.omega-signout`. Callers never spell the class, so the naming can never drift (this replaced three conventions: `.auth-signout-btn`, `.auth-signin-btn`, `.uj-password-toggle`, with no aliases kept).
116
+ - **A click anywhere inside a trigger element counts** (`closest()`), which is what makes icon-only and label-wrapped buttons work. The INNERMOST trigger wins when triggers nest.
117
+ - **A trigger class means the framework owns the click**: the registry calls `preventDefault()` + `stopPropagation()` before the handler, so no default navigation and no page-level handler fires behind it.
118
+ - **Registration arms the listener**, so a surface may register before or after `omega.initialize()` — order never matters. Re-registering a name REPLACES the handler with a warning; it never stacks, so a double boot cannot double-fire.
119
+ - **Who registers what**: the client registers the GENERIC actions (`omega-signout` — confirm, sign out, notify — from `auth.setupEventListeners()`), and each surface registers its own. Today: @omega.js/extension registers `omega-signin` (opens the website's `/token` page in a tab) and `omega-account` (opens the website's `/account` page in a tab) from `setupAuthEventListeners()`; @omega.js/web registers `omega-password-toggle` (the password eye) from its global module.
120
+ - Transport-free and DOM-only like `motion` / `icon-renderer`: inert where there is no document (desktop main, the extension service worker).
121
+
122
+ Web's `data-shell-toggle` / `data-shell-dismiss` are NOT triggers — they are @omega.js/web's attribute-driven app-shell contract and stay there.
123
+
124
+ ## File Conventions
125
+
126
+ - **CommonJS-friendly ES6+** in `src/`. `prepare-package` transpiles to ES5 in `dist/`.
127
+ - **`fs-jetpack`** over `fs` / `fs-extra` for any file operations in tests/scripts.
128
+ - **No TypeScript** — pure JavaScript library.
129
+ - **Template strings** — use backticks for string interpolation.
130
+ - **DO NOT modify `_legacy/`** — reference only, frozen for historical context.
131
+ - **No backwards compatibility** unless explicitly requested — just change to the new way.
132
+ - **Early-return / short-circuit** style throughout — see [docs/code-patterns.md](../docs/code-patterns.md) for the full code-pattern checklist (`$`-prefixed DOM vars, operators at start of continuation lines, Firestore path syntax, dynamic imports, config deep-merge, event delegation).
133
+
134
+ ## Doc-update parity
135
+
136
+ Whenever you make a behavioral change (new module, new method, new pattern, removed feature), update:
137
+
138
+ 1. **`README.md`** — user-facing summary
139
+ 2. **`docs/client/index.md`** (this file) — architecture overview, one paragraph or cross-link
140
+ 3. **`docs/<topic>.md`** — the meat. If a topic doesn't have a doc yet, create one.
141
+ 4. **`CHANGELOG.md`** — if the project keeps one
142
+
143
+ Don't ship behavioral changes with stale docs. Validate first, then document — write docs that describe shipped reality, not intentions.
144
+
145
+ **The four framework guides are structurally MIRRORED** — this guide follows the library subset of that skeleton (the scaffolding frameworks [web](../web/index.md), [backend](../backend/index.md), [desktop](../desktop/index.md), and [extension](../extension/index.md) carry the full skeleton + a consumer template). Never add, rename, or reorder a section here without checking the sibling guides.
146
+
147
+ ## Documentation
148
+
149
+ Deep references live in `docs/`. Treat docs as a first-class deliverable. **Whenever you make a behavioral change, update both this overview AND the relevant `docs/*.md` deep reference.**
150
+
151
+ - [docs/architecture.md](../docs/architecture.md) — singleton pattern, directory structure, module dependency graph
152
+ - [docs/code-patterns.md](../docs/code-patterns.md) — early returns, `$`-prefixed DOM vars, logical operator placement, Firestore path syntax, dynamic imports, config deep-merge, event delegation
153
+ - [docs/modules.md](../docs/modules.md) — full module quick reference (Storage, Auth + `resolveSubscription` + Settler Pattern, Bindings, Firestore, Notifications, ServiceWorker, Sentry, DOM, Utilities)
154
+ - [docs/bindings.md](../docs/bindings.md) — `data-omega-bind` deep reference: actions, comma syntax, condition operators, state paths, skeleton loaders, root-key update filtering
155
+ - [docs/build-system.md](../docs/build-system.md) — `prepare-package` ES5 transpile, build commands, package exports
156
+ - [docs/testing.md](../docs/testing.md) — Mocha test setup
157
+ - [docs/cdp-debugging.md](../docs/cdp-debugging.md) — driving a live browser (per-session isolated Chrome via the `chrome-devtools` MCP) to verify @omega.js/client inside a consuming site
158
+ - [docs/common-tasks.md](../docs/common-tasks.md) — adding a utility, adding a module, modifying config defaults, payment config (OMEGA SSOT shape), adding a binding action
159
+ - [docs/dependencies.md](../docs/dependencies.md) — dependencies table + important notes (no TypeScript, prefer fs-jetpack, no backwards-compat requirement, etc.)