Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/audience/sdk/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
extends: ['../../../.eslintrc'],
parserOptions: {
project: './tsconfig.eslint.json',
tsconfigRootDir: __dirname,
},
};
15 changes: 15 additions & 0 deletions packages/audience/sdk/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { Config } from 'jest';

const config: Config = {
roots: ['<rootDir>/src'],
moduleDirectories: ['node_modules', 'src'],
testEnvironment: 'jsdom',
transform: {
'^.+\\.(t|j)sx?$': '@swc/jest',
},
moduleNameMapper: {
'^@imtbl/audience-core$': '<rootDir>/../core/src/index.ts',
},
};

export default config;
59 changes: 59 additions & 0 deletions packages/audience/sdk/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"name": "@imtbl/audience-sdk",
"description": "Immutable Audience SDK — consent-aware event tracking and identity management",
"version": "0.0.0",
"author": "Immutable",
"bugs": "https://github.com/immutable/ts-immutable-sdk/issues",
"dependencies": {
"@imtbl/audience-core": "workspace:*"
},
"devDependencies": {
"@swc/core": "^1.4.2",
"@swc/jest": "^0.2.37",
"@types/jest": "^29.5.12",
"@types/node": "^22.10.7",
"eslint": "^8.56.0",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.4.3",
"ts-jest": "^29.1.0",
"tsup": "^8.3.0",
"typescript": "^5.6.2"
},
"engines": {
"node": ">=20.11.0"
},
"exports": {
"development": {
"types": "./src/index.ts",
"browser": "./dist/browser/index.js",
"require": "./dist/node/index.cjs",
"default": "./dist/node/index.js"
},
"default": {
"types": "./dist/types/index.d.ts",
"browser": "./dist/browser/index.js",
"require": "./dist/node/index.cjs",
"default": "./dist/node/index.js"
}
},
"files": ["dist"],
"homepage": "https://github.com/immutable/ts-immutable-sdk#readme",
"main": "dist/node/index.cjs",
"module": "dist/node/index.js",
"browser": "dist/browser/index.js",
"publishConfig": {
"access": "public"
},
"repository": "immutable/ts-immutable-sdk.git",
"scripts": {
"build": "pnpm transpile && pnpm typegen",
"transpile": "tsup src/index.ts --config ../../../tsup.config.js",
"typegen": "tsc --customConditions default --emitDeclarationOnly --outDir dist/types",
"lint": "eslint ./src --ext .ts,.jsx,.tsx --max-warnings=0",
"test": "jest --passWithNoTests",
"test:watch": "jest --watch",
"typecheck": "tsc --customConditions development --noEmit --jsx preserve"
},
"type": "module",
"types": "./dist/types/index.d.ts"
}
6 changes: 6 additions & 0 deletions packages/audience/sdk/src/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SDK-specific constants.
// Backend endpoints and base URLs come from @imtbl/audience-core.

export const LIBRARY_NAME = '@imtbl/audience-sdk';
// Replaced at build time by esbuild replace plugin
export const LIBRARY_VERSION = '__SDK_VERSION__';
11 changes: 11 additions & 0 deletions packages/audience/sdk/src/context.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { collectContext } from './context';
import { LIBRARY_NAME, LIBRARY_VERSION } from './config';

describe('collectContext', () => {
it('should return context with SDK library name and version', () => {
const ctx = collectContext();

expect(ctx.library).toBe(LIBRARY_NAME);
expect(ctx.libraryVersion).toBe(LIBRARY_VERSION);
});
});
7 changes: 7 additions & 0 deletions packages/audience/sdk/src/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { EventContext } from '@imtbl/audience-core';
import { collectContext as coreCollectContext } from '@imtbl/audience-core';
import { LIBRARY_NAME, LIBRARY_VERSION } from './config';

export function collectContext(): EventContext {
return coreCollectContext(LIBRARY_NAME, LIBRARY_VERSION);
}
81 changes: 81 additions & 0 deletions packages/audience/sdk/src/debug.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import type { Message } from '@imtbl/audience-core';
import { DebugLogger } from './debug';

describe('DebugLogger', () => {
let logSpy: jest.SpyInstance;
let warnSpy: jest.SpyInstance;

beforeEach(() => {
logSpy = jest.spyOn(console, 'log').mockImplementation();
warnSpy = jest.spyOn(console, 'warn').mockImplementation();
});

afterEach(() => {
logSpy.mockRestore();
warnSpy.mockRestore();
});

const stubMessage: Message = {
type: 'track',
messageId: 'msg-1',
eventTimestamp: '2026-01-01T00:00:00Z',
anonymousId: 'anon-1',
surface: 'web',
context: { library: 'test', libraryVersion: '0.0.0' },
event: 'click',
properties: {},
};

it('should not log when disabled', () => {
const logger = new DebugLogger(false);
logger.logEvent('track', stubMessage);
logger.logFlush(true, 1);
logger.logConsent('none', 'full');
logger.logWarning('test');

expect(logSpy).not.toHaveBeenCalled();
expect(warnSpy).not.toHaveBeenCalled();
});

it('should log events when enabled', () => {
const logger = new DebugLogger(true);
logger.logEvent('track', stubMessage);

expect(logSpy).toHaveBeenCalledWith(
'[Immutable Audience] track',
stubMessage,
);
});

it('should log flush status', () => {
const logger = new DebugLogger(true);

logger.logFlush(true, 5);
expect(logSpy).toHaveBeenCalledWith(
'[Immutable Audience] flush ok (5 messages)',
);

logger.logFlush(false, 3);
expect(logSpy).toHaveBeenCalledWith(
'[Immutable Audience] flush failed (3 messages)',
);
});

it('should log consent transitions', () => {
const logger = new DebugLogger(true);
logger.logConsent('none', 'full');

expect(logSpy).toHaveBeenCalledWith(
'[Immutable Audience] consent none → full',
);
});

it('should log warnings', () => {
const logger = new DebugLogger(true);
logger.logWarning('something went wrong');

expect(warnSpy).toHaveBeenCalledWith(
'[Immutable Audience] something went wrong',
);
});
});
35 changes: 35 additions & 0 deletions packages/audience/sdk/src/debug.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import type { ConsentLevel, Message } from '@imtbl/audience-core';

const PREFIX = '[Immutable Audience]';

export class DebugLogger {
private enabled: boolean;

constructor(enabled = false) {
this.enabled = enabled;
}

logEvent(method: string, message: Message): void {
if (!this.enabled) return;
// eslint-disable-next-line no-console
console.log(`${PREFIX} ${method}`, message);
}

logFlush(ok: boolean, count: number): void {
if (!this.enabled) return;
// eslint-disable-next-line no-console
console.log(`${PREFIX} flush ${ok ? 'ok' : 'failed'} (${count} messages)`);
}

logConsent(from: ConsentLevel, to: ConsentLevel): void {
if (!this.enabled) return;
// eslint-disable-next-line no-console
console.log(`${PREFIX} consent ${from} → ${to}`);
}

logWarning(msg: string): void {
if (!this.enabled) return;
// eslint-disable-next-line no-console
console.warn(`${PREFIX} ${msg}`);
}
}
3 changes: 3 additions & 0 deletions packages/audience/sdk/src/index.ts
Comment thread
nattb8 marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export type { AudienceSDKConfig } from './types';
export { DebugLogger } from './debug';
export { collectContext } from './context';
13 changes: 13 additions & 0 deletions packages/audience/sdk/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { Environment, ConsentLevel } from '@imtbl/audience-core';

/** Configuration for the Immutable Audience SDK. */
export interface AudienceSDKConfig {
publishableKey: string;
environment: Environment;
/** Defaults to 'none' — no tracking until explicitly opted in. */
consent?: ConsentLevel;
Comment thread
ImmutableJeffrey marked this conversation as resolved.
debug?: boolean;
cookieDomain?: string;
flushInterval?: number;
flushSize?: number;
}
5 changes: 5 additions & 0 deletions packages/audience/sdk/tsconfig.eslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": "./tsconfig.json",
"include": ["src"],
"exclude": []
}
10 changes: 10 additions & 0 deletions packages/audience/sdk/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "./dist",
"rootDirs": ["src"],
"customConditions": ["development"]
},
"include": ["src"],
"exclude": ["dist", "jest.config.ts", "node_modules", "src/**/*.test.ts"]
}
Loading
Loading