-
Notifications
You must be signed in to change notification settings - Fork 207
Introduce theme analytics component to improve o11y #6184
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Coverage report
Show new covered files 🐣
Show files with reduced coverage 🔻
Test suite run success3211 tests passing in 1348 suites. Report generated by 🧪jest coverage report action from a744dda |
3145c0f
to
08fa145
Compare
08fa145
to
894d78c
Compare
This stack of pull requests is managed by Graphite. Learn more about stacking. |
894d78c
to
a3ff7b3
Compare
543d986
to
6e1a174
Compare
We detected some changes at Caution DO NOT create changesets for features which you do not wish to be included in the public changelog of the next CLI release. |
packages/cli-kit/src/public/node/themes/analytics/error-categorizer.ts
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the PR and the solid tests to go with it @karreiro!
6e1a174
to
89d0d41
Compare
89d0d41
to
a744dda
Compare
Differences in type declarationsWe detected differences in the type declarations generated by Typescript for this branch compared to the baseline ('main' branch). Please, review them to ensure they are backward-compatible. Here are some important things to keep in mind:
New type declarationspackages/cli-kit/dist/public/node/themes/analytics.d.ts/**
* Records timing data for performance monitoring. Call twice with the same
* event name to start and stop timing. First call starts the timer, second
* call stops it and records the duration.
*
* @example
* ```ts
* recordTiming('theme-upload') // Start timing
* // ... do work ...
* recordTiming('theme-upload') // Stop timing and record duration
* ```
*
* @param eventName - Unique identifier for the timing event
*/
export declare function recordTiming(eventName: string): void;
/**
* Records error information for debugging and monitoring. Use this to track
* any exceptions or error conditions that occur during theme operations.
* Errors are automatically categorized for easier analysis.
*
* @example
* ```ts
* try {
* // ... risky operation ...
* } catch (error) {
* recordError(error)
* }
* ```
*
* @param error - Error object or message to record
*/
export declare function recordError<T>(error: T): T;
/**
* Records retry attempts for network operations. Use this to track when
* operations are retried due to transient failures. Helps identify
* problematic endpoints or operations that frequently fail.
*
* @example
* ```ts
* recordRetry('https://api.shopify.com/themes', 'upload')
* ```
*
* @param url - The URL or endpoint being retried
* @param operation - Description of the operation being retried
*/
export declare function recordRetry(url: string, operation: string): void;
/**
* Records custom events for tracking specific user actions or system events.
* Use this for important milestones, user interactions, or significant
* state changes in the application.
*
* @example
* ```ts
* recordEvent('theme-dev-started')
* recordEvent('file-watcher-connected')
* ```
*
* @param eventName - Descriptive name for the event
*/
export declare function recordEvent(eventName: string): void;
packages/cli-kit/dist/public/node/themes/analytics/bounded-collections.d.ts/**
* A bounded array that automatically maintains a maximum size by removing
* the oldest entries when new items are added beyond the limit.
*
* Extends the native Array class to provide all standard array methods
* while enforcing a fixed maximum size of MAX_ARRAY_SIZE (1000 entries).
*
* When the size limit is exceeded, the oldest entries (at the beginning
* of the array) are automatically removed to make room for new ones.
*
* @example
* const commands = new BArray()
* commands.push(entry) // Automatically removes oldest if over 1000
*/
export declare class BArray<T> extends Array<T> {
push(...items: T[]): number;
clear(): void;
toArray(): T[];
private enforceLimit;
}
/**
* A bounded map that automatically maintains a maximum number of keys by
* removing the oldest entries when new keys are added beyond the limit.
*
* Extends the native Map class to provide all standard map methods while
* enforcing a fixed maximum size of MAX_MAP_KEYS (1000 entries).
*
* Tracks insertion order to ensure the oldest keys are removed first when
* the limit is exceeded. This provides LRU-like behavior based on insertion
* time rather than access time.
*
* @example
* const events = new BMap()
* events.set('event', 1) // Automatically removes oldest if over 1000
*/
export declare class BMap<TKey, TValue> extends Map<TKey, TValue> {
private insertionOrder;
set(key: TKey, value: TValue): this;
delete(key: TKey): boolean;
clear(): void;
toObject(): {
[key: string]: TValue;
};
private enforceLimit;
}
packages/cli-kit/dist/public/node/themes/analytics/error-categorizer.d.tsexport declare enum ErrorCategory {
ThemeCheck = "THEME_CHECK",
Network = "NETWORK",
FileSystem = "FILE_SYSTEM",
Authentication = "AUTHENTICATION",
Validation = "VALIDATION",
Permission = "PERMISSION",
RateLimit = "RATE_LIMIT",
Parsing = "PARSING",
Unknown = "UNKNOWN"
}
export declare function categorizeError(error: unknown): ErrorCategory;
packages/cli-kit/dist/public/node/themes/analytics/storage.d.tsimport { ErrorCategory } from './error-categorizer.js';
interface TimingEntry {
event: string;
duration: number;
}
interface ErrorEntry {
category: ErrorCategory;
message: string;
timestamp: number;
}
interface RetryEntry {
url: string;
operation: string;
attempts: number;
timestamp: number;
}
interface EventEntry {
name: string;
timestamp: number;
}
export interface RuntimeData {
timings: TimingEntry[];
errors: ErrorEntry[];
retries: RetryEntry[];
events: EventEntry[];
}
export declare function recordTiming(eventName: string): void;
export declare function recordError(error: unknown): void;
export declare function recordRetry(url: string, operation: string): void;
export declare function recordEvent(eventName: string): void;
export declare function compileData(): RuntimeData;
export declare function reset(): void;
export {};
Existing type declarationsWe found no diffs with existing type declarations |
👋 @Shopify/app-inner-loop could you please take a look at this one? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't want to block on this, but shouldn't you use the existing analytics component? If you need any additional feature, you could add it. But it feels weird to use a different system for themes, no?
WHY are these changes introduced?
This PR introduces a component to collect runtime data about theme commands. The goal is to improve the quality of the signals we have around theme commands and help us better understand any friction users might be facing.
WHAT is this pull request doing?
This PR adds a new analytics module at
themes/analytics.ts
. This module is responsible for recording events in a small, bounded JavaScript object, which helps avoid performance issues from large objects and prevents sending oversized data to monorail.The main features of this module include:
This analytics module will help us identify performance bottlenecks, common errors, and usage patterns, giving us clearer insights to improve the theme development experience.
How to test your changes?
N/A — This component is not user-facing and does not require user-level testing (this will happen on following PRs using this component).
Post-release steps
N/A
Measuring impact
How do we know this change was effective? Please choose one:
Checklist