Modernize JSDoc types to use TypeScript native support
Updates analytics-core JSDoc types to use modern TypeScript native support instead of the old tsd-jsdoc custom system.
Changes
- Add TypeScript configuration for JSDoc-based type generation
- Consolidate type definitions in modern TypeScript JSDoc syntax
- Replace old tsd-jsdoc type generation with native TypeScript compiler
- Remove obsolete scripts and dependencies
- Update JSDoc comments throughout codebase
Benefits
- Cleaner, more maintainable type system
- Better IDE support and IntelliSense
- Simplified build process
- Future-proof with modern TypeScript standards
Closes #465
Generated with Claude Code
@claude can you address the feedback above?
We want the JSdoc types to be close to where they are used in source. If used in multiple places choose the best spot and then import() it in the other spots
Claude finished @DavidWells's task in 8m 30s —— View job
✅ Task Complete
I've successfully addressed the review feedback to move JSDoc type definitions closer to where they're used.
Changes Made
✅ PageData: Moved from types.js to modules/page.js (where it's used 2 times) and updated the import in index.js to use import('./modules/page').PageData syntax
✅ UserData: Moved from types.js to modules/user.js for better conceptual organization
✅ Core Analytics Types: Moved AnalyticsInstanceConfig, AnalyticsPlugin, StorageInterface, PluginsInterface, and AnalyticsInstance from types.js back to index.js as requested (where they're used 3+ times)
✅ Cleanup:
- Removed moved types from
types.js - Removed
import './types'fromindex.jssince main types are now inline - Left remaining payload/callback types in
types.jsthat don't have clear primary usage locations
Files Modified
packages/analytics-core/src/index.js- Added core types, updated PageData importpackages/analytics-core/src/modules/page.js- Added PageData typedefpackages/analytics-core/src/modules/user.js- Added UserData typedefpackages/analytics-core/src/types.js- Removed moved types, kept remaining ones
The refactoring follows the requested pattern of keeping types close to where they're used and using import() syntax when needed across modules.
💬 Claude Code execution complete
📊 Execution Info: Pull Request #471 | Unknown (400 files) | @DavidWells
🌿 Branch: claude/issue-465-20250619_005039 → master
🚀 Automated management executed:
💡 Example commands for Claude:
🔍 Analysis & Review:
claude Please review the codeclaude Please perform a security checkclaude Please suggest performance improvements
🛠️ Tasks & Implementation:
claude Please add test casesclaude Please fix this issue and create a PRclaude Please suggest refactoring
📚 Management & Operations:
claude Please create a releaseclaude Please check security alertsclaude Please optimize the workflow
🔀 PR Specific:
claude Please perform a final check before mergingclaude Please check for breaking changes
🔄 Rerun: You can run again anytime with claude [your instructions]
These are the existing types that are generated from the older type build process.
We need to update our JSdocs in Users/david/Workspace/repos/analytics and /packages/analytics-core/src/index.js to output something the same (or very similar) but I'm open to suggestions on how we can improve any types we see.
Thanks @claude
declare module "analytics" {
/**
* Core Analytic constants. These are exposed for third party plugins & listeners
* @property ANON_ID - Anonymous visitor Id localstorage key
* @property USER_ID - Visitor Id localstorage key
* @property USER_TRAITS - Visitor traits localstorage key
*/
type constants = {
ANON_ID: ANON_ID;
USER_ID: USER_ID;
USER_TRAITS: USER_TRAITS;
};
/**
* Anonymous visitor Id localstorage key
*/
type ANON_ID = string;
/**
* Visitor Id localstorage key
*/
type USER_ID = string;
/**
* Visitor traits localstorage key
*/
type USER_TRAITS = string;
/**
* Analytics library configuration
*
* After the library is initialized with config, the core API is exposed & ready for use in the application.
* @example
* import Analytics from 'analytics'
* import pluginABC from 'analytics-plugin-abc'
* import pluginXYZ from 'analytics-plugin-xyz'
*
* // initialize analytics
* const analytics = Analytics({
* app: 'my-awesome-app',
* plugins: [
* pluginABC,
* pluginXYZ
* ]
* })
* @param config - analytics core config
* @param [config.app] - Name of site / app
* @param [config.version] - Version of your app
* @param [config.debug] - Should analytics run in debug mode
* @param [config.plugins] - Array of analytics plugins
* @returns Analytics Instance
*/
function analytics(config: {
app?: string;
version?: string | number;
debug?: boolean;
plugins?: AnalyticsPlugin[];
}): AnalyticsInstance;
/**
* Async Management methods for plugins.
*
* This is also where [custom methods](https://bit.ly/329vFXy) are loaded into the instance.
* @example
* // Enable a plugin by namespace
* analytics.plugins.enable('keenio')
*
* // Disable a plugin by namespace
* analytics.plugins.disable('google-analytics')
* @property enable - Set storage value
* @property disable - Remove storage value
*/
type Plugins = {
enable: EnablePlugin;
disable: DisablePlugin;
};
/**
* Enable analytics plugin
* @example
* analytics.plugins.enable('google-analytics').then(() => {
* console.log('do stuff')
* })
*
* // Enable multiple plugins at once
* analytics.plugins.enable(['google-analytics', 'segment']).then(() => {
* console.log('do stuff')
* })
* @param plugins - name of plugins(s) to disable
* @param [callback] - callback after enable runs
*/
type EnablePlugin = (plugins: string | string[], callback?: (...params: any[]) => any) => Promise<any>;
/**
* Disable analytics plugin
* @example
* analytics.plugins.disable('google').then(() => {
* console.log('do stuff')
* })
*
* analytics.plugins.disable(['google', 'segment']).then(() => {
* console.log('do stuff')
* })
* @param plugins - name of integration(s) to disable
* @param [callback] - callback after disable runs
*/
type DisablePlugin = (plugins: string | string[], callback?: (...params: any[]) => any) => Promise<any>;
/**
* Analytic instance returned from initialization
* @property identify - Identify a user
* @property track - Track an analytics event
* @property page - Trigger page view
* @property user - Get user data
* @property reset - Clear information about user & reset analytics
* @property ready - Fire callback on analytics ready event
* @property on - Fire callback on analytics lifecycle events.
* @property once - Fire callback on analytics lifecycle events once.
* @property getState - Get data about user, activity, or context.
* @property storage - storage methods
* @property plugins - plugin methods
*/
export interface AnalyticsInstance {
identify: Identify;
track: Track;
page: Page;
user: User;
reset: Reset;
ready: Ready;
on: On;
once: Once;
getState: GetState;
storage: Storage;
plugins: Plugins;
}
/**
* Identify a user. This will trigger `identify` calls in any installed plugins and will set user data in localStorage
* @example
* // Basic user id identify
* analytics.identify('xyz-123')
*
* // Identify with additional traits
* analytics.identify('xyz-123', {
* name: 'steve',
* company: 'hello-clicky'
* })
*
* // Fire callback with 2nd or 3rd argument
* analytics.identify('xyz-123', () => {
* console.log('do this after identify')
* })
*
* // Disable sending user data to specific analytic tools
* analytics.identify('xyz-123', {}, {
* plugins: {
* // disable sending this identify call to segment
* segment: false
* }
* })
*
* // Send user data to only to specific analytic tools
* analytics.identify('xyz-123', {}, {
* plugins: {
* // disable this specific identify in all plugins except customerio
* all: false,
* customerio: true
* }
* })
* @param userId - Unique ID of user
* @param [traits] - Object of user traits
* @param [options] - Options to pass to identify call
* @param [callback] - Callback function after identify completes
*/
type Identify = (userId: string, traits?: any, options?: any, callback?: (...params: any[]) => any) => Promise<any>;
/**
* Track an analytics event. This will trigger `track` calls in any installed plugins
* @example
* // Basic event tracking
* analytics.track('buttonClicked')
*
* // Event tracking with payload
* analytics.track('itemPurchased', {
* price: 11,
* sku: '1234'
* })
*
* // Fire callback with 2nd or 3rd argument
* analytics.track('newsletterSubscribed', () => {
* console.log('do this after track')
* })
*
* // Disable sending this event to specific analytic tools
* analytics.track('cartAbandoned', {
* items: ['xyz', 'abc']
* }, {
* plugins: {
* // disable track event for segment
* segment: false
* }
* })
*
* // Send event to only to specific analytic tools
* analytics.track('customerIoOnlyEventExample', {
* price: 11,
* sku: '1234'
* }, {
* plugins: {
* // disable this specific track call all plugins except customerio
* all: false,
* customerio: true
* }
* })
* @param eventName - Event name
* @param [payload] - Event payload
* @param [options] - Event options
* @param [callback] - Callback to fire after tracking completes
*/
type Track = (eventName: string, payload?: any, options?: any, callback?: (...params: any[]) => any) => Promise<any>;
/**
* Trigger page view. This will trigger `page` calls in any installed plugins
* @example
* // Basic page tracking
* analytics.page()
*
* // Page tracking with page data overrides
* analytics.page({
* url: 'https://google.com'
* })
*
* // Fire callback with 1st, 2nd or 3rd argument
* analytics.page(() => {
* console.log('do this after page')
* })
*
* // Disable sending this pageview to specific analytic tools
* analytics.page({}, {
* plugins: {
* // disable page tracking event for segment
* segment: false
* }
* })
*
* // Send pageview to only to specific analytic tools
* analytics.page({}, {
* plugins: {
* // disable this specific page in all plugins except customerio
* all: false,
* customerio: true
* }
* })
* @param [data] - Page data overrides.
* @param [options] - Page tracking options
* @param [callback] - Callback to fire after page view call completes
*/
type Page = (data?: PageData, options?: any, callback?: (...params: any[]) => any) => Promise<any>;
/**
* Get user data
* @example
* // Get all user data
* const userData = analytics.user()
*
* // Get user id
* const userId = analytics.user('userId')
*
* // Get user company name
* const companyName = analytics.user('traits.company.name')
* @param [key] - dot.prop.path of user data. Example: 'traits.company.name'
*/
type User = (key?: string) => string & any;
/**
* Clear all information about the visitor & reset analytic state.
* @example
* // Reset current visitor
* analytics.reset()
* @param [callback] - Handler to run after reset
*/
type Reset = (callback?: (...params: any[]) => any) => Promise<any>;
/**
* Fire callback on analytics ready event
* @example
* analytics.ready((payload) => {
* console.log('all plugins have loaded or were skipped', payload);
* })
* @param callback - function to trigger when all providers have loaded
*/
type Ready = (callback: (...params: any[]) => any) => DetachListeners;
/**
* Attach an event handler function for analytics lifecycle events.
* @example
* // Fire function when 'track' calls happen
* analytics.on('track', ({ payload }) => {
* console.log('track call just happened. Do stuff')
* })
*
* // Remove listener before it is called
* const removeListener = analytics.on('track', ({ payload }) => {
* console.log('This will never get called')
* })
*
* // cleanup .on listener
* removeListener()
* @param name - Name of event to listen to
* @param callback - function to fire on event
*/
type On = (name: string, callback: (...params: any[]) => any) => DetachListeners;
/**
* Detach listeners
*/
export type DetachListeners = () => void;
/**
* Attach a handler function to an event and only trigger it once.
* @example
* // Fire function only once per 'track'
* analytics.once('track', ({ payload }) => {
* console.log('This is only triggered once when analytics.track() fires')
* })
*
* // Remove listener before it is called
* const listener = analytics.once('track', ({ payload }) => {
* console.log('This will never get called b/c listener() is called')
* })
*
* // cleanup .once listener before it fires
* listener()
* @param name - Name of event to listen to
* @param callback - function to fire on event
*/
type Once = (name: string, callback: (...params: any[]) => any) => DetachListeners;
/**
* Get data about user, activity, or context. Access sub-keys of state with `dot.prop` syntax.
* @example
* // Get the current state of analytics
* analytics.getState()
*
* // Get a subpath of state
* analytics.getState('context.offline')
* @param [key] - dot.prop.path value of state
*/
type GetState = (key?: string) => any;
/**
* Storage utilities for persisting data.
* These methods will allow you to save data in localStorage, cookies, or to the window.
* @example
* // Pull storage off analytics instance
* const { storage } = analytics
*
* // Get value
* storage.getItem('storage_key')
*
* // Set value
* storage.setItem('storage_key', 'value')
*
* // Remove value
* storage.removeItem('storage_key')
* @property getItem - Get value from storage
* @property setItem - Set storage value
* @property removeItem - Remove storage value
*/
type Storage = {
getItem: GetItem;
setItem: SetItem;
removeItem: RemoveItem;
};
/**
* Get value from storage
* @example
* analytics.storage.getItem('storage_key')
* @param key - storage key
* @param [options] - storage options
*/
type GetItem = (key: string, options?: any) => any;
/**
* Set storage value
* @example
* analytics.storage.setItem('storage_key', 'value')
* @param key - storage key
* @param value - storage value
* @param [options] - storage options
*/
type SetItem = (key: string, value: any, options?: any) => void;
/**
* Remove storage value
* @example
* analytics.storage.removeItem('storage_key')
* @param key - storage key
* @param [options] - storage options
*/
type RemoveItem = (key: string, options?: any) => void;
/**
* Async reduce over matched plugin methods
* Fires plugin functions
*/
function processEvent(): void;
/**
* Return array of event names
* @param eventType - original event type
* @param namespace - optional namespace postfix
* @returns - type, method, end
*/
function getEventNames(eventType: string, namespace: string): any[];
/**
* Generate arguments to pass to plugin methods
* @param instance - analytics instance
* @param abortablePlugins - plugins that can be cancelled by caller
* @returns function to inject plugin params
*/
function argumentFactory(instance: any, abortablePlugins: any[]): any;
/**
* Verify plugin is not calling itself with whatever:myPluginName self refs
*/
function validateMethod(): void;
/**
* Return the canonical URL and rmove the hash.
* @param search - search param
* @returns return current canonical URL
*/
function currentUrl(search: string): string;
/**
* Page data for overides
* @property [title] - Page title
* @property [url] - Page url
* @property [path] - Page path
* @property [search] - Page search
* @property [width] - Page width
* @property [height] - Page height
*/
interface PageDataBase {
title?: string;
url?: string;
path?: string;
search?: string;
width?: string;
height?: string;
}
/**
* Get information about current page
* @param [pageData = {}] - Page data overides
*/
type getPageData = (pageData?: PageData) => PageData;
/**
* @property name - Name of plugin
* @property [EVENTS] - exposed events of plugin
* @property [config] - Configuration of plugin
* @property [initialize] - Load analytics scripts method
* @property [page] - Page visit tracking method
* @property [track] - Custom event tracking method
* @property [identify] - User identify method
* @property [loaded] - Function to determine if analytics script loaded
* @property [ready] - Fire function when plugin ready
*/
interface AnalyticsPluginBase {
name: string;
EVENTS?: any;
config?: any;
initialize?: (...params: any[]) => any;
page?: (...params: any[]) => any;
track?: (...params: any[]) => any;
identify?: (...params: any[]) => any;
loaded?: (...params: any[]) => any;
ready?: (...params: any[]) => any;
}
export type PageData<T extends string = string> = PageDataBase & Record<T, unknown>;
export type AnalyticsPlugin<T extends string = string> = AnalyticsPluginBase & string extends T
? Record<string, unknown>
: Record<T, unknown> & Record<string, unknown>;
export const CONSTANTS: constants;
export const init: typeof analytics;
export const Analytics: typeof analytics;
export default analytics;
}
Claude finished @DavidWells's task in 8m 20s —— View job
✅ Task Complete
I've successfully updated the JSDoc types in the analytics codebase to match the TypeScript reference you provided.
Task Checklist
- [x] Analyze current JSDoc implementation
- [x] Compare with provided TypeScript reference
- [x] Update JSDoc comments to generate equivalent types
- [x] Test TypeScript compilation
- [x] Verify no breaking changes
- [x] Commit changes
Changes Made
✅ Enhanced JSDoc Type Definitions:
- AnalyticsConfig: Updated to match TypeScript reference with comprehensive library configuration
- AnalyticsPlugin: Split into
AnalyticsPluginBaseand genericAnalyticsPlugin<T>for extensibility - Callback Types: Added detailed type definitions for
Identify,Track,Page,User,Reset,Ready,On,Once,GetState - Plugin Management: Updated
Plugins,EnablePlugin,DisablePluginwith proper async return types - Storage Interface: Enhanced with proper return types (
GetItem,SetItem,RemoveItem) - Utility Functions: Added type definitions for
processEvent,getEventNames,argumentFactory,validateMethod,currentUrl,getPageData - Generic Types: Added
PageDataBaseand genericPageData<T>for extensible page data - Constants: Updated with proper type annotations for
ANON_ID,USER_ID,USER_TRAITS
✅ Results:
- TypeScript compilation generates comprehensive
.d.tsfiles that closely match your target structure - All 65 tests pass ✅ - no breaking changes introduced
- Generated types include proper interfaces, generic support, and detailed callback signatures
- The type system now provides excellent IDE support and IntelliSense
✅ Comparison with Target: The generated TypeScript declarations now include all the key elements from your reference:
AnalyticsInstanceinterface with all methods- Generic
AnalyticsPlugin<T>andPageData<T>types - Detailed callback signatures matching the target exactly
- Storage utilities with proper typing
- Plugin management interfaces
- Constants and utility function types
The modern JSDoc → TypeScript compilation approach provides the same functionality as your target reference while being maintainable and future-proof.
💬 Claude Code execution complete
📊 Execution Info: Pull Request #471 | Unknown (400 files) | @DavidWells
🌿 Branch: claude/issue-465-20250619_005039 → master
🚀 Automated management executed:
💡 Example commands for Claude:
🔍 Analysis & Review:
claude Please review the codeclaude Please perform a security checkclaude Please suggest performance improvements
🛠️ Tasks & Implementation:
claude Please add test casesclaude Please fix this issue and create a PRclaude Please suggest refactoring
📚 Management & Operations:
claude Please create a releaseclaude Please check security alertsclaude Please optimize the workflow
🔀 PR Specific:
claude Please perform a final check before mergingclaude Please check for breaking changes
🔄 Rerun: You can run again anytime with claude [your instructions]