@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.
@@ -0,0 +1,79 @@
1
+ import type { Claim, Configuration, ConsentResult, Options } from "../types.js";
2
+ export declare class ConsentClient {
3
+ private readonly options;
4
+ /** Loads the app configuration. The form starts separately. */
5
+ readonly ready: Promise<Configuration>;
6
+ private readonly api;
7
+ private readonly key;
8
+ private config?;
9
+ private readonly controller;
10
+ private saved?;
11
+ private stored?;
12
+ private preferenceSnapshot;
13
+ private form?;
14
+ private host?;
15
+ private overlay?;
16
+ private mountTarget?;
17
+ private readonly accessibility;
18
+ private destroyed;
19
+ private launchTask?;
20
+ private generation;
21
+ /** Already reported once, so a later mount() or open() that fails on it does not report it again. */
22
+ private loadFailure;
23
+ private readonly reportedErrors;
24
+ constructor(options: Options);
25
+ private load;
26
+ private request;
27
+ private persist;
28
+ private storageChanged;
29
+ private documentStorageChanged;
30
+ private syncSaved;
31
+ private notifyPreference;
32
+ private alive;
33
+ /** A saved choice counts only for the notice now shown. A changed notice or language asks again. */
34
+ private hasChoice;
35
+ /** Returns a current browser preference, or null when a new choice is required. */
36
+ getPreference(): ConsentResult | null;
37
+ /** Returns browser proof for a verified server-side identity claim. */
38
+ getClaim(): Claim | null;
39
+ /**
40
+ * Makes sure a session exists and returns this visitor's saved choices. The same session is kept
41
+ * while it is valid, even after the notice or language changes, so the visitor keeps one record
42
+ * and getClaim() keeps working. Only a session Atlas no longer accepts is replaced.
43
+ */
44
+ private session;
45
+ private reconcileSession;
46
+ /**
47
+ * A visitor who saved and is not asked again never opens the form, so nothing renews their
48
+ * session. When it has under 60 days left, renew it in the background. Failures change nothing.
49
+ */
50
+ private renewIfEnding;
51
+ /** Sends the choices. The form keeps the key, so a retry cannot save twice. */
52
+ private save;
53
+ /** Counts the form as seen. A counter failure must not affect the visitor. */
54
+ private recordView;
55
+ /** Runs a public operation. A failure goes to onError and the error event, and also rejects. */
56
+ private reported;
57
+ /** Inserts the form into an existing container. */
58
+ mount(target: string | HTMLElement): Promise<void>;
59
+ private mountForm;
60
+ /**
61
+ * Shows the form. A panel that is already mounted is scrolled into view and focused. Otherwise
62
+ * the form opens as a dialog, and panel clients use the popup layout for it.
63
+ */
64
+ open(): Promise<void>;
65
+ /** Reloads the current notice and session, replacing stale choices and keeping the same placement. */
66
+ refresh(): Promise<void>;
67
+ private startForm;
68
+ private insertForm;
69
+ private createForm;
70
+ private complete;
71
+ private keydown;
72
+ private pointerdown;
73
+ private assertAlive;
74
+ private fail;
75
+ /** Removes the form and restores page access. The instance can be opened again. */
76
+ close(silent?: boolean): void;
77
+ /** Cancels requests, removes listeners, and permanently ends this instance. */
78
+ destroy(): void;
79
+ }
@@ -0,0 +1,16 @@
1
+ import type { DisplayMode } from "../types.js";
2
+ export declare class DialogAccessibility {
3
+ private previousFocus?;
4
+ private previousOverflow?;
5
+ private pointerFocus?;
6
+ notePointer(event: PointerEvent): void;
7
+ activate(overlay: HTMLDialogElement): void;
8
+ release(): void;
9
+ }
10
+ export declare function createDialog(form: HTMLElement, mode: DisplayMode): HTMLDialogElement;
11
+ interface DialogOwner {
12
+ close(): void;
13
+ }
14
+ export declare function claimDialog(owner: DialogOwner): void;
15
+ export declare function releaseDialog(owner: DialogOwner): void;
16
+ export {};
@@ -0,0 +1,13 @@
1
+ import type { ConsentResult } from "../types.js";
2
+ export interface Saved {
3
+ token: string;
4
+ expires: string;
5
+ result?: ConsentResult;
6
+ }
7
+ /** Stored preferences are untrusted input: keep them only if they have the expected shape. */
8
+ export declare function readSaved(raw: string | null): Saved | undefined;
9
+ /** Undefined means storage is inaccessible; null means the key was removed. */
10
+ export declare function readStorage(key: string): string | null | undefined;
11
+ export declare function storeSaved(key: string, saved: Saved | undefined): boolean;
12
+ /** Same-document clients do not receive native storage events. No credentials go in this event. */
13
+ export declare const STORAGE_EVENT = "atlas-consent:storage";
@@ -0,0 +1 @@
1
+ export declare function request<T>(url: string, lifetime: AbortSignal, init?: RequestInit, fallback?: string): Promise<T>;
@@ -0,0 +1,6 @@
1
+ import { ConsentClient } from "./client/consent-client.js";
2
+ import type { Options } from "./types.js";
3
+ export { ConsentClient };
4
+ export { VERSION } from "./shared/constants.js";
5
+ export * from "./types.js";
6
+ export declare function create(options: Options): ConsentClient;
@@ -0,0 +1,11 @@
1
+ {
2
+ "version": "1.0.0",
3
+ "files": {
4
+ "atlas-consent.js": "1gbGCvUMfC0ainfkRXZbd5RI0y4JxTIq7EVsFKeI3d7t4X+6AH/W8dL90BxXeMnC",
5
+ "atlas-consent.esm.js": "wJJZ22AdV+n/zMfShp14mvapLmaWEiNIdrsXywzxu0SWCYMYfTQsdvEYXQ7bSpY1",
6
+ "atlas-consent-ui.js": "kiHnJlrweigYAue/aKZvCgt+nGoYSw5VAMSa+ZkVYiEonif/vHuBn54RTTSoEu/O",
7
+ "ui.html": "gkXwlOaVjJxvFsh06VvDdbrhZlwvda+y/UOgJpqeNzn8F8j5kGIN7HiUh/PtudLT"
8
+ },
9
+ "environment": "production",
10
+ "api_base_url": "https://play.dashboard.atlaskyc.com/v3/api"
11
+ }
@@ -0,0 +1 @@
1
+ export declare const DEFAULT_API_BASE_URL: string;
@@ -0,0 +1,3 @@
1
+ export declare const VERSION = "1.0.0";
2
+ export declare const PROTOCOL = "atlas-consent:1";
3
+ export declare const REQUEST_TIMEOUT_MS = 15000;
@@ -0,0 +1,3 @@
1
+ import type { Purpose } from "../types.js";
2
+ /** Validate every notice field the renderer reads. Missing fields use complete purpose fallbacks. */
3
+ export declare function validNotice(value: unknown, purposes: Purpose[]): boolean;
@@ -0,0 +1,9 @@
1
+ import type { Configuration, ConsentResult } from "../types.js";
2
+ export declare function randomId(): string;
3
+ export declare function validMessage(value: unknown, channel: string): value is {
4
+ protocol: string;
5
+ channel: string;
6
+ type: string;
7
+ [key: string]: unknown;
8
+ };
9
+ export declare function validResult(value: unknown, config: Configuration): value is ConsentResult;
@@ -0,0 +1,3 @@
1
+ export declare function baseUrl(value: string): string;
2
+ /** Backend base URL implied by the SDK script address, or an error that says how to fix a mismatch. */
3
+ export declare function apiBaseFromScript(src: string): string;
@@ -0,0 +1,4 @@
1
+ import type { Configuration } from "../types.js";
2
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;
3
+ export declare function validChoices(value: unknown, config: Configuration): value is Record<string, boolean>;
4
+ export declare function validConfiguration(value: unknown, appId: string): value is Configuration;
@@ -0,0 +1,64 @@
1
+ export type DisplayMode = "panel" | "popup" | "page";
2
+ export interface Purpose {
3
+ id: number;
4
+ name: string;
5
+ required: boolean;
6
+ description: string;
7
+ data_items: unknown[];
8
+ }
9
+ export interface Appearance {
10
+ theme: "light" | "dark" | "system";
11
+ primary_color: string;
12
+ background_color: string;
13
+ text_color: string;
14
+ radius: "sharp" | "soft" | "round";
15
+ font: "system" | "serif" | "rounded";
16
+ density: "comfortable" | "compact";
17
+ }
18
+ export interface Configuration {
19
+ appearance?: Partial<Appearance>;
20
+ app_id: string;
21
+ name: string;
22
+ description: string | null;
23
+ mode: DisplayMode;
24
+ level_id: string;
25
+ template_id: string | null;
26
+ revision: number;
27
+ sdk_version: string;
28
+ language_code: string;
29
+ config_version: string;
30
+ purposes: Purpose[];
31
+ notice: Record<string, unknown>;
32
+ }
33
+ export interface ConsentResult {
34
+ ref_id: string;
35
+ app_id: string;
36
+ level_id: string;
37
+ config_version: string;
38
+ choices: Record<string, boolean>;
39
+ }
40
+ export interface Claim {
41
+ app_id: string;
42
+ ref_id: string;
43
+ claim_token: string;
44
+ }
45
+ export interface Options {
46
+ appId: string;
47
+ apiBaseUrl?: string;
48
+ mode?: DisplayMode;
49
+ language?: string;
50
+ /** Colors and style. They override the look saved in Atlas. Wording always comes from the consent level. */
51
+ appearance?: Partial<Appearance>;
52
+ target?: string | HTMLElement;
53
+ autoShow?: boolean;
54
+ onComplete?: (result: ConsentResult, claim: Claim) => void;
55
+ /** Observe initial saved consent, changes in this or other tabs, and storage removal. */
56
+ onPreferenceChange?: (result: ConsentResult | null) => void;
57
+ onError?: (error: ConsentError) => void;
58
+ onClose?: () => void;
59
+ }
60
+ export declare class ConsentError extends Error {
61
+ readonly code: string;
62
+ readonly status: number;
63
+ constructor(message: string, code: string, status?: number);
64
+ }
@@ -0,0 +1,6 @@
1
+ import type { Appearance } from "../types.js";
2
+ export interface AppearanceController {
3
+ apply(input: Partial<Appearance> | undefined): void;
4
+ destroy(): void;
5
+ }
6
+ export declare function createAppearance(target: HTMLElement): AppearanceController;
@@ -0,0 +1,5 @@
1
+ import type { DataItem } from "./notice-types.js";
2
+ export declare function summary(items?: DataItem[], categories?: string[]): HTMLElement | null;
3
+ export declare function accordion(label: string, description: string | undefined, items: string[]): HTMLElement;
4
+ /** Section text as the notice writer formatted it: paragraphs, lists, emphasis and links. */
5
+ export declare function paragraphs(value: string | undefined): HTMLElement | null;
@@ -0,0 +1,41 @@
1
+ import type { Appearance, Configuration, ConsentResult, DisplayMode } from "../types.js";
2
+ export interface DecisionPayload {
3
+ config_version: string;
4
+ language_code: string;
5
+ answers: {
6
+ purpose_id: number;
7
+ given: boolean;
8
+ }[];
9
+ }
10
+ export interface ConsentFormOptions {
11
+ host: HTMLElement;
12
+ config: Configuration;
13
+ choices: Record<string, boolean>;
14
+ mode: DisplayMode;
15
+ appearance?: Partial<Appearance>;
16
+ /** Dashboard preview: nothing is saved and focus is left alone. */
17
+ demo?: boolean;
18
+ save: (payload: DecisionPayload, idempotencyKey: string) => Promise<ConsentResult>;
19
+ onComplete: (result: ConsentResult) => void;
20
+ onError: (error: unknown) => void;
21
+ onClose: () => void;
22
+ onRefresh?: () => Promise<void>;
23
+ }
24
+ /** The consent form. It only draws and collects choices; the caller owns every API call. */
25
+ export declare class ConsentForm {
26
+ private readonly options;
27
+ private readonly root;
28
+ private readonly appearance;
29
+ private readonly state;
30
+ private syncChecks;
31
+ private pendingKey;
32
+ private pendingBody;
33
+ private needsRefresh;
34
+ constructor(options: ConsentFormOptions);
35
+ /** Call once the host is in the document, so popup and page modes can move focus inside. */
36
+ render(): void;
37
+ setAppearance(appearance: Partial<Appearance> | undefined): void;
38
+ destroy(): void;
39
+ private message;
40
+ private submit;
41
+ }
@@ -0,0 +1,8 @@
1
+ export declare function element<K extends keyof HTMLElementTagNameMap>(tag: K, text?: string, className?: string): HTMLElementTagNameMap[K];
2
+ /** The text of a notice value, with any markup removed and entities decoded. */
3
+ export declare function plain(value: unknown): string;
4
+ /**
5
+ * Notice text with its formatting. DOMPurify handles parsing, including Trusted Types enforcement.
6
+ * Rebuild the sanitized nodes to apply our heading and link presentation rules.
7
+ */
8
+ export declare function rich(value: unknown): DocumentFragment;
@@ -0,0 +1,16 @@
1
+ import type { Configuration, DisplayMode } from "../types.js";
2
+ export interface FormState {
3
+ choices: Record<string, boolean>;
4
+ busy: boolean;
5
+ }
6
+ export interface FormOptions {
7
+ root: HTMLElement;
8
+ config: Configuration;
9
+ state: FormState;
10
+ mode: DisplayMode;
11
+ demo: boolean;
12
+ onClose: () => void;
13
+ onSubmit: () => Promise<void>;
14
+ onChange: () => void;
15
+ }
16
+ export declare function renderForm({ root, config: configuration, state, mode, demo, onClose, onSubmit, onChange, }: FormOptions): () => void;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,50 @@
1
+ export interface Button {
2
+ button_type: string;
3
+ button_text?: string;
4
+ button_enabled?: boolean;
5
+ button_data?: {
6
+ accept_necessary_text?: string;
7
+ accept_selected_text?: string;
8
+ };
9
+ }
10
+ export interface Block {
11
+ section_type: string;
12
+ section_data: {
13
+ section_title?: string;
14
+ section_body?: string;
15
+ title?: string;
16
+ buttons?: Button[];
17
+ sections?: Block[];
18
+ };
19
+ }
20
+ export interface DataItem {
21
+ data_item_name?: string;
22
+ }
23
+ export interface Notice {
24
+ /** The language of purposes, collected and shared, which Atlas keeps in one language. */
25
+ sections_language_code?: string;
26
+ content?: {
27
+ consent_data?: Block[];
28
+ };
29
+ purposes?: {
30
+ purpose_reason?: string;
31
+ purpose_reason_original?: string;
32
+ desc?: string;
33
+ sub_items?: {
34
+ purpose_id: number;
35
+ purpose_name?: string;
36
+ data_items?: DataItem[];
37
+ data_categories?: string[];
38
+ }[];
39
+ }[];
40
+ collected?: {
41
+ data_category_name?: string;
42
+ desc?: string;
43
+ sub_items?: string[];
44
+ }[];
45
+ shared?: {
46
+ purpose_name?: string;
47
+ data_categories?: string[];
48
+ data_items?: DataItem[];
49
+ }[];
50
+ }
@@ -0,0 +1 @@
1
+ export declare function installStyles(root: ShadowRoot): void;
package/dist/ui.html ADDED
@@ -0,0 +1 @@
1
+ <!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Consent preferences</title><style>html,body,#atlas-root{height:100%;margin:0}</style></head><body><main id="atlas-root">Loading consent…</main><script src="./atlas-consent-ui.js" defer></script></body></html>
package/package.json CHANGED
@@ -1,6 +1,63 @@
1
1
  {
2
2
  "name": "@atlasprivacy/consent-sdk",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.0",
4
+ "description": "Atlas consent UI and browser SDK",
5
+ "type": "module",
6
+ "main": "dist/atlas-consent.esm.js",
7
+ "module": "dist/atlas-consent.esm.js",
8
+ "types": "dist/index.d.ts",
9
+ "files": [
10
+ "dist",
11
+ "README.md",
12
+ "LICENSE",
13
+ "CHANGELOG.md"
14
+ ],
15
+ "sideEffects": [
16
+ "./dist/atlas-consent.js"
17
+ ],
18
+ "scripts": {
19
+ "build": "npm run typecheck && node scripts/build.mjs && tsc --emitDeclarationOnly",
20
+ "test": "node --import tsx --test tests/unit/*.test.* tests/integration/*.test.*",
21
+ "test:browser": "playwright test",
22
+ "sync:backend": "node scripts/sync-backend.mjs",
23
+ "check": "npm run format:check && npm run lint && npm run test && npm run build && npm run test:package",
24
+ "prepack": "npm run build",
25
+ "lint": "eslint src tests scripts playwright.config.ts eslint.config.mjs --max-warnings 0",
26
+ "lint:fix": "npm run lint -- --fix",
27
+ "format": "prettier --write .",
28
+ "format:check": "prettier --check .",
29
+ "typecheck": "tsc --noEmit && tsc -p tsconfig.tests.json",
30
+ "test:package": "node tests/package/consumer.mjs"
31
+ },
32
+ "devDependencies": {
33
+ "@playwright/test": "^1.50.1",
34
+ "@types/node": "^22.0.0",
35
+ "dompurify": "^3.4.17",
36
+ "esbuild": "^0.25.0",
37
+ "eslint": "10.14.0",
38
+ "prettier": "3.9.10",
39
+ "tsx": "^4.20.0",
40
+ "typescript": "^5.8.0",
41
+ "typescript-eslint": "8.72.1"
42
+ },
43
+ "engines": {
44
+ "node": "^20.19.0 || ^22.13.0 || >=24"
45
+ },
46
+ "license": "MIT",
47
+ "jsdelivr": "dist/atlas-consent.js",
48
+ "publishConfig": {
49
+ "access": "public"
50
+ },
51
+ "exports": {
52
+ ".": {
53
+ "types": "./dist/index.d.ts",
54
+ "import": "./dist/atlas-consent.esm.js",
55
+ "default": "./dist/atlas-consent.esm.js"
56
+ },
57
+ "./browser": "./dist/atlas-consent.js"
58
+ },
59
+ "overrides": {
60
+ "minimatch": "11.0.1"
61
+ },
62
+ "gitHead": "e7662a3670dffef99077952b1ddd538722d05d06"
63
+ }