@kb-labs/shared-command-kit 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,111 @@
1
+ import { IAnalytics } from '@kb-labs/core-platform';
2
+
3
+ /**
4
+ * Analytics Wrapper
5
+ *
6
+ * Optional helper for wrapping operations with analytics events.
7
+ * You can always use ctx.platform.analytics.track() directly - this is just convenience.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { withAnalytics } from '@kb-labs/shared-command-kit';
12
+ *
13
+ * const result = await withAnalytics(ctx, 'mind.query', {
14
+ * started: { queryId, text, mode },
15
+ * completed: (result) => ({ tokensIn: result.tokensIn, tokensOut: result.tokensOut }),
16
+ * failed: (error) => ({ errorMessage: error.message }),
17
+ * }, async () => {
18
+ * return await executeQuery(...);
19
+ * });
20
+ * ```
21
+ */
22
+
23
+ /**
24
+ * Context with optional analytics
25
+ */
26
+ interface AnalyticsContext {
27
+ platform?: {
28
+ analytics?: IAnalytics;
29
+ };
30
+ }
31
+ /**
32
+ * Analytics event configuration
33
+ */
34
+ interface AnalyticsEvents<TResult> {
35
+ /** Event properties when operation starts */
36
+ started?: Record<string, unknown>;
37
+ /** Event properties when operation completes (can be function of result) */
38
+ completed?: Record<string, unknown> | ((result: TResult) => Record<string, unknown>);
39
+ /** Event properties when operation fails (can be function of error) */
40
+ failed?: Record<string, unknown> | ((error: Error) => Record<string, unknown>);
41
+ }
42
+ /**
43
+ * Wrap an async operation with analytics tracking
44
+ *
45
+ * Automatically tracks:
46
+ * - `{eventName}.started` when operation begins
47
+ * - `{eventName}.completed` when operation succeeds
48
+ * - `{eventName}.failed` when operation fails
49
+ *
50
+ * @param ctx - Context with platform.analytics
51
+ * @param eventName - Base event name (e.g., 'mind.query', 'workflow.run')
52
+ * @param events - Event properties for started/completed/failed events
53
+ * @param operation - Async operation to execute
54
+ * @returns Result of the operation
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * const result = await withAnalytics(ctx, 'mind.query', {
59
+ * started: { queryId: '123', text: 'search query' },
60
+ * completed: (result) => ({
61
+ * tokensIn: result.tokensIn,
62
+ * tokensOut: result.tokensOut,
63
+ * resultsCount: result.results.length,
64
+ * }),
65
+ * failed: (error) => ({
66
+ * errorCode: error.code,
67
+ * errorMessage: error.message,
68
+ * }),
69
+ * }, async () => {
70
+ * return await executeQuery('search query');
71
+ * });
72
+ * ```
73
+ */
74
+ declare function withAnalytics<TResult>(ctx: AnalyticsContext, eventName: string, events: AnalyticsEvents<TResult>, operation: () => Promise<TResult>): Promise<TResult>;
75
+ /**
76
+ * Create a reusable analytics wrapper for a specific event
77
+ *
78
+ * @param eventName - Base event name
79
+ * @param defaultEvents - Default event properties
80
+ * @returns Function that wraps operations with analytics
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * const trackQuery = createAnalyticsWrapper('mind.query', {
85
+ * started: { source: 'cli' },
86
+ * completed: (result) => ({ resultsCount: result.results.length }),
87
+ * });
88
+ *
89
+ * // Use multiple times
90
+ * const result1 = await trackQuery(ctx, { text: 'query 1' }, async () => {...});
91
+ * const result2 = await trackQuery(ctx, { text: 'query 2' }, async () => {...});
92
+ * ```
93
+ */
94
+ declare function createAnalyticsWrapper<TResult>(eventName: string, defaultEvents: AnalyticsEvents<TResult>): (ctx: AnalyticsContext, additionalEvents: Partial<AnalyticsEvents<TResult>>, operation: () => Promise<TResult>) => Promise<TResult>;
95
+ /**
96
+ * Track a simple event (no wrapping)
97
+ *
98
+ * Convenience helper for tracking single events without wrapping an operation.
99
+ *
100
+ * @param ctx - Context with platform.analytics
101
+ * @param eventName - Event name
102
+ * @param properties - Event properties
103
+ *
104
+ * @example
105
+ * ```typescript
106
+ * await trackEvent(ctx, 'button.clicked', { buttonId: 'submit', page: 'settings' });
107
+ * ```
108
+ */
109
+ declare function trackEvent(ctx: AnalyticsContext, eventName: string, properties?: Record<string, unknown>): Promise<void>;
110
+
111
+ export { type AnalyticsContext, type AnalyticsEvents, createAnalyticsWrapper, trackEvent, withAnalytics };
@@ -0,0 +1,56 @@
1
+ // src/analytics/with-analytics.ts
2
+ async function withAnalytics(ctx, eventName, events, operation) {
3
+ const analytics = ctx.platform?.analytics;
4
+ const startTime = Date.now();
5
+ if (analytics && events.started) {
6
+ await analytics.track(`${eventName}.started`, {
7
+ ...events.started,
8
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
9
+ });
10
+ }
11
+ try {
12
+ const result = await operation();
13
+ if (analytics && events.completed) {
14
+ const completedProps = typeof events.completed === "function" ? events.completed(result) : events.completed;
15
+ await analytics.track(`${eventName}.completed`, {
16
+ ...completedProps,
17
+ durationMs: Date.now() - startTime,
18
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
19
+ });
20
+ }
21
+ return result;
22
+ } catch (error) {
23
+ if (analytics && events.failed) {
24
+ const failedProps = typeof events.failed === "function" ? events.failed(error) : events.failed;
25
+ await analytics.track(`${eventName}.failed`, {
26
+ ...failedProps,
27
+ durationMs: Date.now() - startTime,
28
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
29
+ });
30
+ }
31
+ throw error;
32
+ }
33
+ }
34
+ function createAnalyticsWrapper(eventName, defaultEvents) {
35
+ return async (ctx, additionalEvents, operation) => {
36
+ const mergedEvents = {
37
+ started: { ...defaultEvents.started, ...additionalEvents.started },
38
+ completed: additionalEvents.completed || defaultEvents.completed,
39
+ failed: additionalEvents.failed || defaultEvents.failed
40
+ };
41
+ return withAnalytics(ctx, eventName, mergedEvents, operation);
42
+ };
43
+ }
44
+ async function trackEvent(ctx, eventName, properties) {
45
+ const analytics = ctx.platform?.analytics;
46
+ if (analytics) {
47
+ await analytics.track(eventName, {
48
+ ...properties,
49
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
50
+ });
51
+ }
52
+ }
53
+
54
+ export { createAnalyticsWrapper, trackEvent, withAnalytics };
55
+ //# sourceMappingURL=index.js.map
56
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/analytics/with-analytics.ts"],"names":[],"mappings":";AA2EA,eAAsB,aAAA,CACpB,GAAA,EACA,SAAA,EACA,MAAA,EACA,SAAA,EACkB;AAClB,EAAA,MAAM,SAAA,GAAY,IAAI,QAAA,EAAU,SAAA;AAChC,EAAA,MAAM,SAAA,GAAY,KAAK,GAAA,EAAI;AAG3B,EAAA,IAAI,SAAA,IAAa,OAAO,OAAA,EAAS;AAC/B,IAAA,MAAM,SAAA,CAAU,KAAA,CAAM,CAAA,EAAG,SAAS,CAAA,QAAA,CAAA,EAAY;AAAA,MAC5C,GAAG,MAAA,CAAO,OAAA;AAAA,MACV,SAAA,EAAA,iBAAW,IAAI,IAAA,EAAK,EAAE,WAAA;AAAY,KACnC,CAAA;AAAA,EACH;AAEA,EAAA,IAAI;AAEF,IAAA,MAAM,MAAA,GAAS,MAAM,SAAA,EAAU;AAG/B,IAAA,IAAI,SAAA,IAAa,OAAO,SAAA,EAAW;AACjC,MAAA,MAAM,cAAA,GAAiB,OAAO,MAAA,CAAO,SAAA,KAAc,aAC/C,MAAA,CAAO,SAAA,CAAU,MAAM,CAAA,GACvB,MAAA,CAAO,SAAA;AAEX,MAAA,MAAM,SAAA,CAAU,KAAA,CAAM,CAAA,EAAG,SAAS,CAAA,UAAA,CAAA,EAAc;AAAA,QAC9C,GAAG,cAAA;AAAA,QACH,UAAA,EAAY,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA;AAAA,QACzB,SAAA,EAAA,iBAAW,IAAI,IAAA,EAAK,EAAE,WAAA;AAAY,OACnC,CAAA;AAAA,IACH;AAEA,IAAA,OAAO,MAAA;AAAA,EACT,SAAS,KAAA,EAAY;AAEnB,IAAA,IAAI,SAAA,IAAa,OAAO,MAAA,EAAQ;AAC9B,MAAA,MAAM,WAAA,GAAc,OAAO,MAAA,CAAO,MAAA,KAAW,aACzC,MAAA,CAAO,MAAA,CAAO,KAAK,CAAA,GACnB,MAAA,CAAO,MAAA;AAEX,MAAA,MAAM,SAAA,CAAU,KAAA,CAAM,CAAA,EAAG,SAAS,CAAA,OAAA,CAAA,EAAW;AAAA,QAC3C,GAAG,WAAA;AAAA,QACH,UAAA,EAAY,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA;AAAA,QACzB,SAAA,EAAA,iBAAW,IAAI,IAAA,EAAK,EAAE,WAAA;AAAY,OACnC,CAAA;AAAA,IACH;AAGA,IAAA,MAAM,KAAA;AAAA,EACR;AACF;AAqBO,SAAS,sBAAA,CACd,WACA,aAAA,EACA;AACA,EAAA,OAAO,OACL,GAAA,EACA,gBAAA,EACA,SAAA,KACqB;AACrB,IAAA,MAAM,YAAA,GAAyC;AAAA,MAC7C,SAAS,EAAE,GAAG,cAAc,OAAA,EAAS,GAAG,iBAAiB,OAAA,EAAQ;AAAA,MACjE,SAAA,EAAW,gBAAA,CAAiB,SAAA,IAAa,aAAA,CAAc,SAAA;AAAA,MACvD,MAAA,EAAQ,gBAAA,CAAiB,MAAA,IAAU,aAAA,CAAc;AAAA,KACnD;AAEA,IAAA,OAAO,aAAA,CAAc,GAAA,EAAK,SAAA,EAAW,YAAA,EAAc,SAAS,CAAA;AAAA,EAC9D,CAAA;AACF;AAgBA,eAAsB,UAAA,CACpB,GAAA,EACA,SAAA,EACA,UAAA,EACe;AACf,EAAA,MAAM,SAAA,GAAY,IAAI,QAAA,EAAU,SAAA;AAEhC,EAAA,IAAI,SAAA,EAAW;AACb,IAAA,MAAM,SAAA,CAAU,MAAM,SAAA,EAAW;AAAA,MAC/B,GAAG,UAAA;AAAA,MACH,SAAA,EAAA,iBAAW,IAAI,IAAA,EAAK,EAAE,WAAA;AAAY,KACnC,CAAA;AAAA,EACH;AACF","file":"index.js","sourcesContent":["/**\n * Analytics Wrapper\n *\n * Optional helper for wrapping operations with analytics events.\n * You can always use ctx.platform.analytics.track() directly - this is just convenience.\n *\n * @example\n * ```typescript\n * import { withAnalytics } from '@kb-labs/shared-command-kit';\n *\n * const result = await withAnalytics(ctx, 'mind.query', {\n * started: { queryId, text, mode },\n * completed: (result) => ({ tokensIn: result.tokensIn, tokensOut: result.tokensOut }),\n * failed: (error) => ({ errorMessage: error.message }),\n * }, async () => {\n * return await executeQuery(...);\n * });\n * ```\n */\n\nimport type { IAnalytics } from '@kb-labs/core-platform';\n\n/**\n * Context with optional analytics\n */\nexport interface AnalyticsContext {\n platform?: {\n analytics?: IAnalytics;\n };\n}\n\n/**\n * Analytics event configuration\n */\nexport interface AnalyticsEvents<TResult> {\n /** Event properties when operation starts */\n started?: Record<string, unknown>;\n /** Event properties when operation completes (can be function of result) */\n completed?: Record<string, unknown> | ((result: TResult) => Record<string, unknown>);\n /** Event properties when operation fails (can be function of error) */\n failed?: Record<string, unknown> | ((error: Error) => Record<string, unknown>);\n}\n\n/**\n * Wrap an async operation with analytics tracking\n *\n * Automatically tracks:\n * - `{eventName}.started` when operation begins\n * - `{eventName}.completed` when operation succeeds\n * - `{eventName}.failed` when operation fails\n *\n * @param ctx - Context with platform.analytics\n * @param eventName - Base event name (e.g., 'mind.query', 'workflow.run')\n * @param events - Event properties for started/completed/failed events\n * @param operation - Async operation to execute\n * @returns Result of the operation\n *\n * @example\n * ```typescript\n * const result = await withAnalytics(ctx, 'mind.query', {\n * started: { queryId: '123', text: 'search query' },\n * completed: (result) => ({\n * tokensIn: result.tokensIn,\n * tokensOut: result.tokensOut,\n * resultsCount: result.results.length,\n * }),\n * failed: (error) => ({\n * errorCode: error.code,\n * errorMessage: error.message,\n * }),\n * }, async () => {\n * return await executeQuery('search query');\n * });\n * ```\n */\nexport async function withAnalytics<TResult>(\n ctx: AnalyticsContext,\n eventName: string,\n events: AnalyticsEvents<TResult>,\n operation: () => Promise<TResult>\n): Promise<TResult> {\n const analytics = ctx.platform?.analytics;\n const startTime = Date.now();\n\n // Track started event\n if (analytics && events.started) {\n await analytics.track(`${eventName}.started`, {\n ...events.started,\n timestamp: new Date().toISOString(),\n });\n }\n\n try {\n // Execute operation\n const result = await operation();\n\n // Track completed event\n if (analytics && events.completed) {\n const completedProps = typeof events.completed === 'function'\n ? events.completed(result)\n : events.completed;\n\n await analytics.track(`${eventName}.completed`, {\n ...completedProps,\n durationMs: Date.now() - startTime,\n timestamp: new Date().toISOString(),\n });\n }\n\n return result;\n } catch (error: any) {\n // Track failed event\n if (analytics && events.failed) {\n const failedProps = typeof events.failed === 'function'\n ? events.failed(error)\n : events.failed;\n\n await analytics.track(`${eventName}.failed`, {\n ...failedProps,\n durationMs: Date.now() - startTime,\n timestamp: new Date().toISOString(),\n });\n }\n\n // Re-throw error\n throw error;\n }\n}\n\n/**\n * Create a reusable analytics wrapper for a specific event\n *\n * @param eventName - Base event name\n * @param defaultEvents - Default event properties\n * @returns Function that wraps operations with analytics\n *\n * @example\n * ```typescript\n * const trackQuery = createAnalyticsWrapper('mind.query', {\n * started: { source: 'cli' },\n * completed: (result) => ({ resultsCount: result.results.length }),\n * });\n *\n * // Use multiple times\n * const result1 = await trackQuery(ctx, { text: 'query 1' }, async () => {...});\n * const result2 = await trackQuery(ctx, { text: 'query 2' }, async () => {...});\n * ```\n */\nexport function createAnalyticsWrapper<TResult>(\n eventName: string,\n defaultEvents: AnalyticsEvents<TResult>\n) {\n return async (\n ctx: AnalyticsContext,\n additionalEvents: Partial<AnalyticsEvents<TResult>>,\n operation: () => Promise<TResult>\n ): Promise<TResult> => {\n const mergedEvents: AnalyticsEvents<TResult> = {\n started: { ...defaultEvents.started, ...additionalEvents.started },\n completed: additionalEvents.completed || defaultEvents.completed,\n failed: additionalEvents.failed || defaultEvents.failed,\n };\n\n return withAnalytics(ctx, eventName, mergedEvents, operation);\n };\n}\n\n/**\n * Track a simple event (no wrapping)\n *\n * Convenience helper for tracking single events without wrapping an operation.\n *\n * @param ctx - Context with platform.analytics\n * @param eventName - Event name\n * @param properties - Event properties\n *\n * @example\n * ```typescript\n * await trackEvent(ctx, 'button.clicked', { buttonId: 'submit', page: 'settings' });\n * ```\n */\nexport async function trackEvent(\n ctx: AnalyticsContext,\n eventName: string,\n properties?: Record<string, unknown>\n): Promise<void> {\n const analytics = ctx.platform?.analytics;\n\n if (analytics) {\n await analytics.track(eventName, {\n ...properties,\n timestamp: new Date().toISOString(),\n });\n }\n}\n"]}
@@ -0,0 +1,227 @@
1
+ /**
2
+ * @module @kb-labs/shared-command-kit/errors/types
3
+ * Types for error formatting
4
+ */
5
+ /**
6
+ * Formatted error result
7
+ */
8
+ interface FormattedError {
9
+ /** Human-readable error message */
10
+ message: string;
11
+ /** JSON representation */
12
+ json: {
13
+ ok: false;
14
+ error: string;
15
+ timingMs?: number;
16
+ stack?: string;
17
+ };
18
+ }
19
+ /**
20
+ * Error formatting options
21
+ */
22
+ interface FormatErrorOptions {
23
+ /** Output in JSON format */
24
+ jsonMode?: boolean;
25
+ /** Include stack trace */
26
+ showStack?: boolean;
27
+ /** Timing information */
28
+ timingMs?: number;
29
+ }
30
+
31
+ /**
32
+ * @module @kb-labs/shared-command-kit/errors/format
33
+ * Error formatting utilities
34
+ */
35
+
36
+ /**
37
+ * Format error for display
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * const formatted = formatError(error, {
42
+ * jsonMode: Boolean(flags.json),
43
+ * showStack: Boolean(flags.debug),
44
+ * timingMs: tracker.total(),
45
+ * });
46
+ *
47
+ * if (flags.json) {
48
+ * ctx.output?.json(formatted.json);
49
+ * } else {
50
+ * ctx.output?.error(formatted.message);
51
+ * }
52
+ * ```
53
+ */
54
+ declare function formatError(error: unknown, options?: FormatErrorOptions): FormattedError;
55
+
56
+ /**
57
+ * Error Factory for KB Labs Plugins
58
+ *
59
+ * Optional helper for defining plugin errors without boilerplate.
60
+ * You can always use standard Error classes - this is just convenience.
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * import { defineError } from '@kb-labs/shared-command-kit';
65
+ *
66
+ * export const MindError = defineError('MIND', {
67
+ * ValidationFailed: { code: 400, message: 'Validation failed' },
68
+ * IndexNotFound: { code: 404, message: (scope: string) => `Index '${scope}' not found` },
69
+ * QueryFailed: { code: 500, message: 'Query execution failed' },
70
+ * });
71
+ *
72
+ * // Usage:
73
+ * throw new MindError.IndexNotFound('default');
74
+ * throw new MindError.ValidationFailed({ details: { field: 'cwd' } });
75
+ * ```
76
+ */
77
+ /**
78
+ * Error definition with HTTP code and message
79
+ */
80
+ interface ErrorDefinition {
81
+ /** HTTP status code (400, 404, 500, etc.) */
82
+ code: number;
83
+ /** Error message - can be string or function for parameterized messages */
84
+ message: string | ((...args: any[]) => string);
85
+ /** Optional additional details */
86
+ details?: Record<string, unknown>;
87
+ }
88
+ /**
89
+ * Error definitions map
90
+ */
91
+ type ErrorDefinitions = Record<string, ErrorDefinition>;
92
+ /**
93
+ * Base error class with HTTP status code support
94
+ */
95
+ declare class PluginError extends Error {
96
+ readonly statusCode: number;
97
+ readonly errorCode: string;
98
+ readonly details?: Record<string, unknown>;
99
+ constructor(errorCode: string, message: string, statusCode: number, details?: Record<string, unknown>);
100
+ /**
101
+ * Convert error to JSON for logging/serialization
102
+ */
103
+ toJSON(): {
104
+ name: string;
105
+ errorCode: string;
106
+ message: string;
107
+ statusCode: number;
108
+ details: Record<string, unknown> | undefined;
109
+ stack: string | undefined;
110
+ };
111
+ /**
112
+ * Check if error is a PluginError
113
+ */
114
+ static isPluginError(error: unknown): error is PluginError;
115
+ }
116
+ /**
117
+ * Type for error constructor created by defineError
118
+ */
119
+ type ErrorConstructor<TArgs extends any[] = any[]> = {
120
+ new (details?: Record<string, unknown>): PluginError;
121
+ new (...args: TArgs): PluginError;
122
+ };
123
+ /**
124
+ * Type for error namespace created by defineError
125
+ */
126
+ type ErrorNamespace<TDefs extends ErrorDefinitions> = {
127
+ [K in keyof TDefs]: ErrorConstructor;
128
+ } & {
129
+ /** Check if error is from this namespace */
130
+ is(error: unknown): error is PluginError;
131
+ /** Check if error has specific error code */
132
+ hasCode(error: unknown, code: keyof TDefs): boolean;
133
+ };
134
+ /**
135
+ * Define a namespace of plugin errors
136
+ *
137
+ * @param prefix - Error code prefix (e.g., 'MIND', 'WORKFLOW')
138
+ * @param definitions - Error definitions map
139
+ * @returns Error namespace with error constructors
140
+ *
141
+ * @example
142
+ * ```typescript
143
+ * export const MindError = defineError('MIND', {
144
+ * IndexNotFound: {
145
+ * code: 404,
146
+ * message: (scope: string) => `Index '${scope}' not found`
147
+ * },
148
+ * QueryFailed: {
149
+ * code: 500,
150
+ * message: 'Query execution failed'
151
+ * },
152
+ * });
153
+ *
154
+ * // Throw with template params
155
+ * throw new MindError.IndexNotFound('default');
156
+ * // Error message: "Index 'default' not found"
157
+ *
158
+ * // Throw with details
159
+ * throw new MindError.QueryFailed({
160
+ * details: { query: 'test', reason: 'timeout' }
161
+ * });
162
+ * ```
163
+ */
164
+ declare function defineError<TDefs extends ErrorDefinitions>(prefix: string, definitions: TDefs): ErrorNamespace<TDefs>;
165
+ /**
166
+ * Common error definitions that can be reused across plugins
167
+ */
168
+ declare const commonErrors: {
169
+ /**
170
+ * Validation error (400)
171
+ */
172
+ ValidationFailed: {
173
+ code: number;
174
+ message: string;
175
+ };
176
+ /**
177
+ * Resource not found (404)
178
+ */
179
+ NotFound: {
180
+ code: number;
181
+ message: (resource: string) => string;
182
+ };
183
+ /**
184
+ * Unauthorized access (401)
185
+ */
186
+ Unauthorized: {
187
+ code: number;
188
+ message: string;
189
+ };
190
+ /**
191
+ * Forbidden access (403)
192
+ */
193
+ Forbidden: {
194
+ code: number;
195
+ message: string;
196
+ };
197
+ /**
198
+ * Internal server error (500)
199
+ */
200
+ InternalError: {
201
+ code: number;
202
+ message: string;
203
+ };
204
+ /**
205
+ * Service unavailable (503)
206
+ */
207
+ ServiceUnavailable: {
208
+ code: number;
209
+ message: (service: string) => string;
210
+ };
211
+ /**
212
+ * Timeout error (504)
213
+ */
214
+ Timeout: {
215
+ code: number;
216
+ message: (operation: string) => string;
217
+ };
218
+ /**
219
+ * Conflict error (409)
220
+ */
221
+ Conflict: {
222
+ code: number;
223
+ message: (resource: string) => string;
224
+ };
225
+ };
226
+
227
+ export { type ErrorDefinition, type ErrorDefinitions, type FormatErrorOptions, type FormattedError, PluginError, commonErrors, defineError, formatError };
@@ -0,0 +1,276 @@
1
+ // src/flags/types.ts
2
+ var FlagValidationError = class extends Error {
3
+ constructor(flag, message, value, schema, commandName) {
4
+ super(message);
5
+ this.flag = flag;
6
+ this.value = value;
7
+ this.schema = schema;
8
+ this.commandName = commandName;
9
+ this.name = "FlagValidationError";
10
+ }
11
+ };
12
+
13
+ // src/errors/format-validation.ts
14
+ function formatValidationError(error, options = {}) {
15
+ const { commandName, schema } = options;
16
+ const lines = [];
17
+ const errorMsg = error.message;
18
+ if (errorMsg.includes("is required")) {
19
+ lines.push(`\u274C Missing required flag: --${error.flag}`);
20
+ } else if (errorMsg.includes("must be one of")) {
21
+ const valueStr = error.value !== void 0 ? ` ${error.value}` : "";
22
+ lines.push(`\u274C Invalid value for --${error.flag}:${valueStr}`);
23
+ } else if (errorMsg.includes("must be a")) {
24
+ lines.push(`\u274C Invalid type for --${error.flag}`);
25
+ } else if (errorMsg.includes("conflicts with")) {
26
+ lines.push(`\u274C Flag conflict: --${error.flag}`);
27
+ } else if (errorMsg.includes("depends on")) {
28
+ lines.push(`\u274C Missing dependency for --${error.flag}`);
29
+ } else {
30
+ lines.push(`\u274C ${errorMsg}`);
31
+ }
32
+ lines.push("");
33
+ if (commandName && schema) {
34
+ const usageLine = generateUsageLine(commandName, schema, error.flag);
35
+ if (usageLine) {
36
+ lines.push(`Usage: ${usageLine}`);
37
+ }
38
+ }
39
+ if (commandName) {
40
+ lines.push(`Hint: Try ${commandName} --help`);
41
+ } else {
42
+ lines.push("Hint: Try --help for more information");
43
+ }
44
+ return lines.join("\n");
45
+ }
46
+ function generateUsageLine(commandName, schema, errorFlag) {
47
+ const parts = [commandName];
48
+ const errorFlagSchema = schema[errorFlag];
49
+ if (errorFlagSchema) {
50
+ const flagStr = formatFlagForUsage(errorFlag, errorFlagSchema);
51
+ parts.push(flagStr);
52
+ }
53
+ const otherRequiredFlags = Object.entries(schema).filter(
54
+ ([name, flagSchema]) => name !== errorFlag && flagSchema.required
55
+ );
56
+ if (otherRequiredFlags.length > 0) {
57
+ for (const [name, flagSchema] of otherRequiredFlags) {
58
+ parts.push(formatFlagForUsage(name, flagSchema));
59
+ }
60
+ }
61
+ const hasOptionalFlags = Object.values(schema).some((s) => !s.required);
62
+ if (hasOptionalFlags) {
63
+ parts.push("[options]");
64
+ }
65
+ return parts.join(" ");
66
+ }
67
+ function formatFlagForUsage(name, schema) {
68
+ let valueHint;
69
+ if ("choices" in schema && schema.choices && schema.choices.length > 0) {
70
+ valueHint = `<${schema.choices.join("|")}>`;
71
+ } else if (schema.type === "boolean") {
72
+ return schema.required ? `--${name}` : `[--${name}]`;
73
+ } else {
74
+ valueHint = `<${name}>`;
75
+ }
76
+ const flagPart = `--${name} ${valueHint}`;
77
+ return schema.required ? flagPart : `[${flagPart}]`;
78
+ }
79
+
80
+ // src/errors/format.ts
81
+ function formatError(error, options = {}) {
82
+ const { showStack = false, timingMs } = options;
83
+ if (error instanceof FlagValidationError) {
84
+ const friendlyMessage = formatValidationError(error, {
85
+ commandName: error.commandName,
86
+ schema: error.schema
87
+ });
88
+ const json2 = {
89
+ ok: false,
90
+ error: error.message
91
+ };
92
+ if (timingMs !== void 0) {
93
+ json2.timingMs = timingMs;
94
+ }
95
+ if (showStack && error.stack) {
96
+ json2.stack = error.stack;
97
+ }
98
+ let message2 = friendlyMessage;
99
+ if (showStack && error.stack) {
100
+ message2 = `${friendlyMessage}
101
+
102
+ Stack trace:
103
+ ${error.stack}`;
104
+ }
105
+ return {
106
+ message: message2,
107
+ json: json2
108
+ };
109
+ }
110
+ const errorMessage = error instanceof Error ? error.message : String(error);
111
+ const errorStack = error instanceof Error ? error.stack : void 0;
112
+ const json = {
113
+ ok: false,
114
+ error: errorMessage
115
+ };
116
+ if (timingMs !== void 0) {
117
+ json.timingMs = timingMs;
118
+ }
119
+ if (showStack && errorStack) {
120
+ json.stack = errorStack;
121
+ }
122
+ let message = errorMessage;
123
+ if (showStack && errorStack) {
124
+ message = `${errorMessage}
125
+
126
+ ${errorStack}`;
127
+ }
128
+ return {
129
+ message,
130
+ json
131
+ };
132
+ }
133
+
134
+ // src/errors/factory.ts
135
+ var PluginError = class _PluginError extends Error {
136
+ statusCode;
137
+ errorCode;
138
+ details;
139
+ constructor(errorCode, message, statusCode, details) {
140
+ super(message);
141
+ this.name = "PluginError";
142
+ this.errorCode = errorCode;
143
+ this.statusCode = statusCode;
144
+ this.details = details;
145
+ if (Error.captureStackTrace) {
146
+ Error.captureStackTrace(this, _PluginError);
147
+ }
148
+ }
149
+ /**
150
+ * Convert error to JSON for logging/serialization
151
+ */
152
+ toJSON() {
153
+ return {
154
+ name: this.name,
155
+ errorCode: this.errorCode,
156
+ message: this.message,
157
+ statusCode: this.statusCode,
158
+ details: this.details,
159
+ stack: this.stack
160
+ };
161
+ }
162
+ /**
163
+ * Check if error is a PluginError
164
+ */
165
+ static isPluginError(error) {
166
+ return error instanceof _PluginError;
167
+ }
168
+ };
169
+ function defineError(prefix, definitions) {
170
+ const errorNamespace = {};
171
+ for (const [key, def] of Object.entries(definitions)) {
172
+ const errorCode = `${prefix}_${key.toUpperCase()}`;
173
+ class DefinedError extends PluginError {
174
+ constructor(...args) {
175
+ const { message, details } = buildMessageAndDetails(def, args);
176
+ super(errorCode, message, def.code, details);
177
+ this.name = `${prefix}Error`;
178
+ }
179
+ }
180
+ errorNamespace[key] = DefinedError;
181
+ }
182
+ errorNamespace.is = (error) => {
183
+ return PluginError.isPluginError(error) && error.errorCode.startsWith(prefix + "_");
184
+ };
185
+ errorNamespace.hasCode = (error, code) => {
186
+ const errorCode = `${prefix}_${String(code).toUpperCase()}`;
187
+ return PluginError.isPluginError(error) && error.errorCode === errorCode;
188
+ };
189
+ return errorNamespace;
190
+ }
191
+ function buildMessageAndDetails(def, args) {
192
+ let message;
193
+ let details;
194
+ if (typeof def.message === "function") {
195
+ const lastArg = args[args.length - 1];
196
+ const hasDetails = lastArg && typeof lastArg === "object" && "details" in lastArg;
197
+ if (hasDetails) {
198
+ const templateArgs = args.slice(0, -1);
199
+ message = def.message(...templateArgs);
200
+ details = { ...def.details, ...lastArg.details };
201
+ } else {
202
+ message = def.message(...args);
203
+ details = def.details;
204
+ }
205
+ } else {
206
+ message = def.message;
207
+ if (args.length > 0 && typeof args[0] === "object" && args[0] !== null) {
208
+ details = { ...def.details, ...args[0].details };
209
+ } else {
210
+ details = def.details;
211
+ }
212
+ }
213
+ return { message, details };
214
+ }
215
+ var commonErrors = {
216
+ /**
217
+ * Validation error (400)
218
+ */
219
+ ValidationFailed: {
220
+ code: 400,
221
+ message: "Validation failed"
222
+ },
223
+ /**
224
+ * Resource not found (404)
225
+ */
226
+ NotFound: {
227
+ code: 404,
228
+ message: (resource) => `${resource} not found`
229
+ },
230
+ /**
231
+ * Unauthorized access (401)
232
+ */
233
+ Unauthorized: {
234
+ code: 401,
235
+ message: "Unauthorized"
236
+ },
237
+ /**
238
+ * Forbidden access (403)
239
+ */
240
+ Forbidden: {
241
+ code: 403,
242
+ message: "Forbidden"
243
+ },
244
+ /**
245
+ * Internal server error (500)
246
+ */
247
+ InternalError: {
248
+ code: 500,
249
+ message: "Internal server error"
250
+ },
251
+ /**
252
+ * Service unavailable (503)
253
+ */
254
+ ServiceUnavailable: {
255
+ code: 503,
256
+ message: (service) => `Service '${service}' is unavailable`
257
+ },
258
+ /**
259
+ * Timeout error (504)
260
+ */
261
+ Timeout: {
262
+ code: 504,
263
+ message: (operation) => `Operation '${operation}' timed out`
264
+ },
265
+ /**
266
+ * Conflict error (409)
267
+ */
268
+ Conflict: {
269
+ code: 409,
270
+ message: (resource) => `${resource} already exists`
271
+ }
272
+ };
273
+
274
+ export { PluginError, commonErrors, defineError, formatError };
275
+ //# sourceMappingURL=index.js.map
276
+ //# sourceMappingURL=index.js.map