@atlasprivacy/consent-sdk 0.0.0-stage → 1.0.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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,155 @@
1
+ # Changelog
2
+
3
+ ## 1.0.0 (unreleased)
4
+
5
+ First public release, published to npm as `@atlasprivacy/consent-sdk` under the MIT license. The internal builds listed below were never released; their changes are all part of 1.0.0.
6
+
7
+ - Synchronize saved decisions across tabs and SDK instances, reconcile server choices, and expose `onPreferenceChange` and `atlas-consent:change` without credentials.
8
+ - Display every configured purpose when notice sections are incomplete, and reject malformed notice structures before mounting.
9
+ - Keep dialogs dismissible during saves and rendering failures. Use native modality for dynamically added page content and restore focus through nested Shadow DOM.
10
+ - Allow a cancelled launch to be reopened immediately; ignore stale session responses and form callbacks.
11
+ - Sanitize notice HTML with bundled DOMPurify and support enforcing Trusted Types without a permissive default policy.
12
+ - Preserve HTTP status on invalid JSON responses. Add `refresh()` and a reload action for notices changed while editing.
13
+ - Test Chromium, Firefox, WebKit, and mobile viewports; gate CI packages on dependency auditing; reject changed assets at existing published versions.
14
+
15
+ ## Internal builds before 1.0.0
16
+
17
+ These versions were built for testing only and are superseded by 1.0.0.
18
+
19
+ ### Internal 1.8.0
20
+
21
+ - Render the consent form inside the SDK, in the website's page, in Shadow DOM. Websites no longer load an Atlas iframe. The Atlas API is unchanged and still stores every decision.
22
+ - Make all session, decision, and view calls from the SDK. Save failures now report the real error code and HTTP status through `onError`.
23
+ - Remove the `frame_timeout` and `result_mismatch` error codes. An invalid save result now reports `invalid_response`.
24
+ - Keep `ui.html` and `atlas-consent-ui.js` only for the Atlas dashboard preview.
25
+ - Keep one session, and so one consent record, when the notice, its language or the website list changes. Sessions renew on use and last a year from last use once they hold a consent. `getClaim()` keeps working across these changes.
26
+ - Name the main button by what it saves: "Accept Necessary" when no optional purpose is ticked, "Accept Selected" when any is. It previously could read "Accept Necessary" while saving optional purposes.
27
+ - Show notice text as written: names keep their case, and paragraphs, lists, emphasis and links are kept.
28
+ - Mark purposes, collected data and shared data with their own language when it differs from the notice language.
29
+ - `open()` on a mounted panel scrolls the panel into view instead of doing nothing.
30
+ - Report every failure to `onError` and `atlas-consent:error` once, including failures of `mount()` and `open()`. Errors use the specific `code` Atlas now sends, such as `consent_linked` and `notice_changed`.
31
+ - `theme: "dark"` uses dark colours unless colours are set. Unknown theme values fall back to light.
32
+ - Script tags accept `data-auto-show` and `data-appearance`, use the script's own Atlas address before the build default, report `missing_container` for a panel without `data-target`, and fire `atlas-consent:ready` after the configuration loads.
33
+ - Send the SDK version with each save, for the audit trail.
34
+ - CI publishes `shipping` packages as well as `dev` and `staging`; the 1.7.0 note that shipping stays disabled no longer applies.
35
+
36
+ ### Internal 1.7.1
37
+
38
+ - Send the save operation's key in the `Idempotency-Key` header, the same name the Atlas API uses, instead of `submission_id` in the body. Requires a backend that allows this header.
39
+
40
+ ### Internal 1.7.0
41
+
42
+ - Select build-time backend defaults from DEV_URL, STAGING_URL, and PRODUCTION_URL. Explicit API URLs still override the default.
43
+ - Publish dev and staging packages to the private GitLab Generic Package Registry after checks pass. Keep shipping publication disabled.
44
+ - Expire all pipeline artifacts after one week.
45
+ - Use the ci-cogserver runner, fresh checkouts, and npm caches outside the source directory.
46
+
47
+ ### Internal 1.6.2
48
+
49
+ - Remove the docs directory and package references.
50
+ - Cancel automatic startup when the client is closed before configuration loads.
51
+ - Scope saved preferences by backend base URL and app ID. Old app-only preferences require a fresh choice.
52
+ - Validate configuration, session choices, and saved results before use. Report invalid notice rendering.
53
+ - Preserve browser initialization in consumer bundles.
54
+ - Keep Shift+Tab inside the dialog from its initial focus position.
55
+ - Choose button text by contrast and handle non-string appearance overrides safely.
56
+
57
+ ### Internal 1.6.1
58
+
59
+ - Separate client lifecycle, storage, transport, dialog accessibility, protocol, and hosted UI modules.
60
+ - Separate UI styles, theme handling, rendering, and session API calls.
61
+ - Group tests by unit, integration, browser, dashboard, package, and fixtures.
62
+ - Add formatting, lint, import-boundary, and package-consumer checks to the GitLab build.
63
+ - Clean generated output before each build. Preserve public exports and asset filenames.
64
+
65
+ ### Internal 1.6.0
66
+
67
+ - Add System theme. The form follows device light and dark preferences and updates when they change. Brand color stays the same.
68
+
69
+ ### Internal 1.5.5
70
+
71
+ - Cancel pending form launches on close. Reject calls after destroy before sending requests.
72
+ - Restore page access when two clients open dialogs. Open panel clients in a popup when `open()` is called.
73
+ - Close dialogs after completion even when the completion callback throws.
74
+ - Time out form reads and saves after 15 seconds. Keep the submission ID for retries.
75
+ - Disable editable choices during a save. Keep required choices selected.
76
+ - Validate appearance keys and support IPv6 localhost base URLs.
77
+ - Add GitLab build, browser test, and package jobs with distributable artifacts.
78
+ - Verify asset version and integrity before backend sync. Build before package creation.
79
+
80
+ ### Internal 1.5.4
81
+
82
+ - Fix: group tick boxes were cleared after every save.
83
+ - Fix: opening a popup or full page no longer puts a focus ring on the close button. Tab still reaches it first.
84
+ - Script-tag start now reports a clear error when the script address does not contain `/consent-sdk/assets/`, instead of calling a wrong address.
85
+ - The build fails if the SDK version in `src/protocol.ts` differs from `package.json`.
86
+ - Requests time out after 15 seconds. A panel that never loads now reports `frame_timeout`, like the popup.
87
+ - A mismatched or unexpected completion message now raises `onError` (`result_mismatch`) instead of being ignored.
88
+ - `onClose` runs only when a form was open, and not on `destroy()`.
89
+ - The page behind a popup or full-page form is inert while it is open, and restored on close.
90
+ - Saved preferences in storage are checked before use.
91
+
92
+ ### Internal 1.5.3
93
+
94
+ - Embedded panel fills its container, with no grey side bands, and uses a calmer 14px text size.
95
+
96
+ ### Internal 1.5.2
97
+
98
+ - Spacing now changes the whole form: text size, gaps, row padding and button height. Compact is clearly tighter.
99
+ - The rounded font uses a humanist stack that looks different from Sans on every device.
100
+
101
+ ### Internal 1.5.1
102
+
103
+ - Fix: colors and style passed in code were replaced by the saved look once the form loaded.
104
+
105
+ ### Internal 1.5.0
106
+
107
+ - Full page mode opens over the same page instead of a new browser tab, and closes back to it. Show full-page consent in the current tab. Restore website scrolling and focus on close.
108
+
109
+ ### Internal 1.4.0
110
+
111
+ - Match the consent screen used in templates and levels: bordered group cards with a chevron, a tick box per group and per purpose, "+N more" data summaries, a shared-data list, a fade above stacked footer buttons, and the same type sizes.
112
+ - Match its corner size by default. The "soft" corners are 6px.
113
+ - Skip the link section, as the template screen does.
114
+
115
+ ### Internal 1.3.1
116
+
117
+ - Show the three buttons in one row when the form is wide.
118
+ - Remove the in-form preview banner. The dashboard already says nothing is saved.
119
+
120
+ ### Internal 1.3.0
121
+
122
+ - Add the `appearance` option. Colors and style set in code override the look saved in Atlas.
123
+
124
+ ### Internal 1.2.3
125
+
126
+ - Default to the FRS theme: pink `#ae0a60` and text `#1a202c`, the same colors as the consent level screen.
127
+
128
+ ### Internal 1.2.2
129
+
130
+ - Do not move focus into the form in dashboard previews.
131
+
132
+ ### Internal 1.2.1
133
+
134
+ - Do not move focus or scroll when an embedded panel opens.
135
+
136
+ ### Internal 1.2.0
137
+
138
+ - Render the consent form from the consent level: title, sections, data groups and button names.
139
+ - Add collapsible data groups, pinned buttons and "Accept Selected" after a choice is changed.
140
+ - Remove text and button label settings from appearance. They now come from the consent level.
141
+
142
+ ### Internal 1.1.0
143
+
144
+ - Add appearance settings from the dashboard: theme, colors, corner style, font, density, brand label, text and button labels.
145
+ - Apply appearance with CSS variables. The SDK accepts hex colors and fixed choices only.
146
+ - Add a live `appearance` message so the dashboard preview updates while a person edits.
147
+ - Report one anonymous "form shown" count per opened form, for the dashboard funnel.
148
+ - Appearance changes do not change the consent version, so saved choices stay valid.
149
+
150
+ ### Internal 1.0.0
151
+
152
+ - Add panel, automatic popup and button-opened full page modes.
153
+ - Add scoped anonymous sessions, saved choices and verified backend linking.
154
+ - Add shared hosted UI, TypeScript types, lifecycle methods and completion events.
155
+ - Add versioned browser/ESM bundles and asset integrity manifest.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 FRS Labs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,208 @@
1
- # Temporary Holding Version
1
+ # Atlas Consent SDK
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A browser SDK with a built-in consent form and three display modes: embedded panel, automatic popup, and full page opened by a button. The form runs in your page. Atlas provides the API that loads the notice and stores decisions.
4
+
5
+ In Atlas, open **Consents → Integrations**, create an app and select a published consent level. Open the integration to add your website origins and enable it. You can create multiple apps for the same level. Copy the app ID and API base URL from its setup guide. Keep your organization API token on your server.
6
+
7
+ ## Add the SDK
8
+
9
+ Load the script from jsDelivr, or use the versioned asset URL from your Atlas deployment. Pin an exact version; a version URL never changes. Replace the version and app ID below. See [npm](https://www.npmjs.com/package/@atlasprivacy/consent-sdk?activeTab=versions) for the current version.
10
+
11
+ ```html
12
+ <script src="https://cdn.jsdelivr.net/npm/@atlasprivacy/consent-sdk@1.0.0/dist/atlas-consent.js"></script>
13
+ ```
14
+
15
+ With a bundler, install the package and import it:
16
+
17
+ ```sh
18
+ npm install @atlasprivacy/consent-sdk
19
+ ```
20
+
21
+ ```ts
22
+ import { create } from "@atlasprivacy/consent-sdk";
23
+ ```
24
+
25
+ The SDK talks to the production Atlas API by default. To use another Atlas deployment, pass `apiBaseUrl` to `create()` or set `data-api-base-url` on the script tag. A script loaded from an Atlas deployment uses that deployment by default.
26
+
27
+ ```html
28
+ <script src="https://cdn.jsdelivr.net/npm/@atlasprivacy/consent-sdk@1.0.0/dist/atlas-consent.js"></script>
29
+ <script>
30
+ const consent = AtlasConsent.create({
31
+ appId: "ac_app_REPLACE_WITH_YOUR_APP_ID",
32
+ mode: "popup",
33
+ onComplete(result, claim) {
34
+ console.log(result.choices);
35
+ // Send claim to your authenticated backend when the visitor logs in.
36
+ },
37
+ onError(error) {
38
+ console.error(error.code, error.message);
39
+ },
40
+ });
41
+ </script>
42
+ ```
43
+
44
+ An `apiBaseUrl` must include any deployment path prefix. Do not add `/api/v1` or `/consent-sdk` to it. API URLs are public; do not put secrets in them.
45
+
46
+ For a script-only automatic popup:
47
+
48
+ ```html
49
+ <script
50
+ defer
51
+ src="https://cdn.jsdelivr.net/npm/@atlasprivacy/consent-sdk@1.0.0/dist/atlas-consent.js"
52
+ data-atlas-app="ac_app_REPLACE_WITH_YOUR_APP_ID"
53
+ data-mode="popup"
54
+ ></script>
55
+ ```
56
+
57
+ Script tag attributes:
58
+
59
+ | Attribute | Meaning |
60
+ | ------------------- | ----------------------------------------------------------------------- |
61
+ | `data-atlas-app` | Required. The app ID. |
62
+ | `data-mode` | `panel`, `popup` or `page`. Defaults to the mode saved in Atlas. |
63
+ | `data-target` | CSS selector of the panel container. Required when the mode is `panel`. |
64
+ | `data-language` | Notice language. Defaults to `en`. |
65
+ | `data-auto-show` | `false` stops a popup from opening by itself. |
66
+ | `data-appearance` | The `appearance` object as JSON, for example `'{"theme":"dark"}'`. |
67
+ | `data-api-base-url` | The Atlas API base URL. Defaults as described above. |
68
+
69
+ `window` receives `atlas-consent:ready` once the configuration has loaded, with the client in `event.detail.client`. If the mode is `panel` and there is no `data-target`, the SDK reports a `missing_container` error instead of showing nothing. Use either automatic initialization or `create()`, so you do not create duplicate instances.
70
+
71
+ ## Choose a display mode
72
+
73
+ ### Embedded panel
74
+
75
+ Place the container before initialization. The SDK inserts the form into this element.
76
+
77
+ ```html
78
+ <div id="consent-panel"></div>
79
+ <script>
80
+ const consent = AtlasConsent.create({
81
+ appId: "ac_app_REPLACE_WITH_YOUR_APP_ID",
82
+ mode: "panel",
83
+ target: "#consent-panel",
84
+ });
85
+ </script>
86
+ ```
87
+
88
+ ### Popup on page load
89
+
90
+ Use `mode: 'popup'`. The SDK opens it when there is no current saved choice for this app and configuration. To open only on demand, set `autoShow: false` and call `consent.open()` from your button.
91
+
92
+ ### Full page on a button click
93
+
94
+ ```js
95
+ const consent = AtlasConsent.create({
96
+ appId: "ac_app_REPLACE_WITH_YOUR_APP_ID",
97
+ mode: "page",
98
+ });
99
+ document.querySelector("#privacy-choice").addEventListener("click", () => {
100
+ consent.open().catch(console.error);
101
+ });
102
+ ```
103
+
104
+ `open()` shows a full-screen consent page over your current page, so the visitor never leaves your site, and returns to it when done.
105
+
106
+ ## Read and change a choice
107
+
108
+ ```js
109
+ await consent.ready;
110
+ const preference = consent.getPreference(); // null until a current choice is saved
111
+ const claim = consent.getClaim(); // scoped proof; keep private
112
+ await consent.open(); // show the form again
113
+ consent.close(); // close the UI
114
+ await consent.refresh(); // reload the notice and session, keeping an embedded panel in its container
115
+ consent.destroy(); // remove the form, listeners and pending requests
116
+ ```
117
+
118
+ `open()` opens the form as a dialog. Panel clients use the popup layout for it. If a panel is already mounted, `open()` scrolls it into view and focuses it instead of opening a second form.
119
+
120
+ `ready` resolves to the published configuration. `onComplete(result, claim)` runs after a successful save. Results include `ref_id`, `app_id`, `level_id`, `config_version`, and a purpose-ID-to-boolean `choices` map. Necessary purposes stay selected. Saves use the existing Atlas consent history and audit services.
121
+
122
+ Use `onPreferenceChange(preference)` to update optional processing when the SDK observes a changed choice, including a withdrawal in another tab. It receives a preference snapshot or `null` when no current consent exists. It also reports an initially stored choice after configuration loads. The equivalent window event is `atlas-consent:change`, with `{ appId, preference }` in `event.detail`; it contains no session or claim token. `onComplete` remains specific to saves from this instance's active form.
123
+
124
+ Clients synchronize choices through local storage, including multiple SDK instances in the same page. A form with stale choices is closed when another instance or tab changes its saved decision; reopen it to edit the latest choice. Clearing storage invalidates the cached preference. Direct storage edits in the same document are noticed on the next preference or claim read. When storage is blocked, clients retain their own choices in memory for that page only.
125
+
126
+ Session reads reconcile cached choices with the server. Choices from an older notice are kept for editing and claiming, but do not count as consent to a new notice. A `notice_changed` save error provides a **Reload consent form** action, which fetches the current notice before another save. Code integrations can also call `refresh()`.
127
+
128
+ Every configured purpose is displayed, even when the notice omits a purpose group or section. Malformed notice structures are rejected before mounting. Rendering errors retain a dismissal control, and saving leaves Close available. Closing an in-flight save dismisses the form; it does not undo a decision the server may already have recorded.
129
+
130
+ The main button saves exactly the boxes that are ticked. Its name follows them: the level's "Accept Necessary" text when no optional purpose is ticked, and its "Accept Selected" text when any is.
131
+
132
+ Use `appearance` to set colors and style in code: `appearance: { theme: 'light', primary_color: '#ae0a60', background_color: '#ffffff', text_color: '#1a202c', radius: 'soft', font: 'system', density: 'comfortable' }`. Values in code override the look saved in Atlas. Colors must be 6-digit hex values; anything else falls back to the default. Wording and button names always come from the consent level, shown as written, with the notice's paragraphs, lists, emphasis and links.
133
+
134
+ `theme` is `light`, `dark` or `system`. `dark` uses a dark background and light text unless you set your own colors. `system` follows the device's light or dark preference and updates when it changes. Both keep your brand color.
135
+
136
+ Use `language: 'en'` (default), or a language supported by the configured level. The notice's title, sections and buttons follow that language. Atlas keeps purposes, collected data and shared data in one language (English), so those lists are marked with their own `lang` for screen readers. The SDK's few own labels, such as "Your choices are saved.", are English.
137
+
138
+ ## Link a verified customer
139
+
140
+ A browser can save anonymously. Page loads create no consent record; the first decision creates one. A reference ID alone does not authorize access or prove identity.
141
+
142
+ After your application verifies the customer, send `consent.getClaim()` to your own authenticated backend. Your backend must choose the verified customer ID from its login session, not trust a customer ID supplied by the browser. Then call:
143
+
144
+ ```http
145
+ POST /api/v1/consents/records/{ref_id}/claim
146
+ Authorization: Bearer YOUR_ORGANIZATION_API_TOKEN
147
+ Content-Type: application/json
148
+
149
+ {
150
+ "app_id": "ac_app_REPLACE_WITH_YOUR_APP_ID",
151
+ "claim_token": "THE_BROWSER_CLAIM_TOKEN",
152
+ "customer_id": "YOUR_VERIFIED_ID"
153
+ }
154
+ ```
155
+
156
+ You can also supply verified applicant email/mobile fields supported by the applicant API. The token needs `consents.records.applicant.update`. Claiming preserves the anonymous evidence, writes the link to the record's audit trail, and ends browser access to the record. A repeat claim for the same customer is safe; a conflicting customer is rejected. If the customer already has a record on this consent's system, the claim returns 409 and the anonymous record stays unlinked. The error names the existing record, so your backend can save the website choices there with `PUT /api/v1/consents/records/{ref_id}/decisions`. Provide a verified customer flow for later changes after linking. The anonymous SDK cannot read a customer's other records.
157
+
158
+ The claim keeps working when the notice, its language or your website list changes, because the browser keeps the same session and the same record. After linking, opening the form in that browser reports `consent_linked`; send the visitor to your signed-in flow.
159
+
160
+ ## Storage and cookie integrations
161
+
162
+ The SDK stores a backend-and-app-specific session capability and choices in your website's local storage. It sets no cookies. If storage is unavailable, the UI still works but cannot remember a choice after reload.
163
+
164
+ A browser session that never saved a choice lasts 30 days. Once it holds a consent, it lasts a year from its last use. Opening the form renews it, and so does a page load in its last 60 days. A changed notice or language asks the visitor again, with their earlier choices ticked, and saves to the same record. Stored consent evidence remains in Atlas after a session ends. Clearing browser storage, or a session ending, loses the anonymous proof: the next save creates a new record. Treat the claim token as a credential: do not put it in URLs, analytics, logs, or third-party scripts.
165
+
166
+ The popup is a consent form. It does **not** scan cookies or automatically block scripts, trackers, tags, or server-side processing. Your site must keep optional processing disabled until the matching purpose choice is true, and stop it when the visitor changes that choice. Map purpose IDs explicitly. `getPreference()` is a browser convenience, not authorization for backend access.
167
+
168
+ ## Origins and errors
169
+
170
+ Register exact origins, including scheme and non-default port. `https://example.com` and `https://www.example.com` are separate. Wildcards and paths are rejected. HTTP is supported only for local development origins. Deploy the API and assets over HTTPS.
171
+
172
+ Errors are `ConsentError` objects with `code`, `message`, and HTTP `status` (0 when no response arrived). Every failure goes to `onError` and to the `atlas-consent:error` event on `window`, once. `ready`, `open()`, and `mount()` also reject with it.
173
+
174
+ | Code | Meaning |
175
+ | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
176
+ | `session_invalid` | The session expired or is unknown. The SDK starts a new one by itself. |
177
+ | `consent_linked` | This browser's consent is linked to a customer. Use your signed-in flow. |
178
+ | `notice_changed` | The notice changed while the form was open. Open the form again. |
179
+ | `not_allowed` | This website's origin is not allowed, or the integration is disabled. |
180
+ | `not_found` | Unknown app ID. |
181
+ | `invalid_request`, `request_too_large`, `conflict` | Atlas rejected the request. |
182
+ | `rate_limited` | HTTP 429. Wait before trying again. |
183
+ | `service_unavailable`, `internal_error` | HTTP 503 or 500. Try again later. |
184
+ | `request_timeout`, `network_error`, `invalid_response` | Atlas did not answer, or answered with something unusable. |
185
+ | `invalid_app`, `invalid_api_base_url`, `invalid_appearance`, `missing_container` | Integration mistakes on your page. |
186
+ | `form_error` | The notice could not be drawn. |
187
+ | `destroyed` | The client was destroyed. Not reported to `onError`. |
188
+ | `consent_sdk_error` | Sent by Atlas backends older than this SDK, in place of the codes above. |
189
+
190
+ Do not automatically retry writes without a delay. The form keeps the same `Idempotency-Key` when retrying a failed save, so a retry cannot save twice.
191
+
192
+ ## Security notes
193
+
194
+ - Session and claim tokens are kept in the visitor's `localStorage` under `atlas-consent:v2:<encoded-api-base-url>:<appId>`, so they are readable by any script on your site. Keep third-party scripts on pages that use the SDK to a minimum and use a Content Security Policy.
195
+ - The consent form runs in your page, in Shadow DOM. Shadow DOM keeps your CSS and the form's CSS apart. It is not a security boundary: any script on your page can read or change the form, so what Atlas records is what your page submitted. Keep third-party scripts off pages that collect consent.
196
+ - Allow your Atlas API host in your Content Security Policy `connect-src`. The form needs no inline style permission.
197
+ - Notice HTML is sanitized with bundled DOMPurify, then rebuilt using the SDK's restricted formatting and link rules. On sites enforcing Trusted Types, allow the `dompurify` policy name in `trusted-types`; for example, `require-trusted-types-for 'script'; trusted-types dompurify`. No permissive default policy is needed. A CSP that disallows the sanitizer policy fails closed and leaves the dialog dismissible.
198
+ - Atlas writes each save to the record's audit trail with how it arrived: the kind of form (drawn on your website, the older Atlas-hosted form, or no browser origin at all), the SDK version the browser reported, your website's origin, the session, the notice version and language, and the caller's IP address, with a hash of those details. Treat a save as what your page sent, not as proof of what the person saw.
199
+
200
+ ## Browser support
201
+
202
+ The SDK targets current evergreen Chrome/Edge, Firefox, and Safari, using native modal dialogs and Shadow DOM. Automated tests cover Chromium, Firefox, WebKit, and mobile Chromium/WebKit viewports. Mobile projects emulate browser devices; they do not replace testing on physical phones or embedded webviews. Destroy an instance when a framework component unmounts; create a new instance when it mounts again. The ESM entry point can be imported during server rendering, but `create()` must run in the browser.
203
+
204
+ The package contains the script and ESM builds, TypeScript declarations, and `dist/manifest.json` with SHA-384 hashes for Subresource Integrity.
205
+
206
+ ## License
207
+
208
+ MIT
@@ -0,0 +1,202 @@
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright [yyyy] [name of copyright owner]
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.