@zapier/zapier-sdk 0.13.2 → 0.13.4
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.
- package/CHANGELOG.md +12 -0
- package/dist/api/debug.d.ts.map +1 -1
- package/dist/api/debug.js +36 -1
- package/dist/api/schemas.d.ts +174 -174
- package/dist/constants.d.ts +4 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +4 -0
- package/dist/index.cjs +475 -9
- package/dist/index.d.mts +381 -157
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.mjs +446 -13
- package/dist/plugins/api/index.d.ts +1 -3
- package/dist/plugins/api/index.d.ts.map +1 -1
- package/dist/plugins/eventEmission/builders.d.ts +13 -0
- package/dist/plugins/eventEmission/builders.d.ts.map +1 -0
- package/dist/plugins/eventEmission/builders.js +78 -0
- package/dist/plugins/eventEmission/index.d.ts +34 -0
- package/dist/plugins/eventEmission/index.d.ts.map +1 -0
- package/dist/plugins/eventEmission/index.js +216 -0
- package/dist/plugins/eventEmission/index.test.d.ts +5 -0
- package/dist/plugins/eventEmission/index.test.d.ts.map +1 -0
- package/dist/plugins/eventEmission/index.test.js +143 -0
- package/dist/plugins/eventEmission/transport.d.ts +37 -0
- package/dist/plugins/eventEmission/transport.d.ts.map +1 -0
- package/dist/plugins/eventEmission/transport.js +96 -0
- package/dist/plugins/eventEmission/transport.test.d.ts +5 -0
- package/dist/plugins/eventEmission/transport.test.d.ts.map +1 -0
- package/dist/plugins/eventEmission/transport.test.js +153 -0
- package/dist/plugins/eventEmission/types.d.ts +53 -0
- package/dist/plugins/eventEmission/types.d.ts.map +1 -0
- package/dist/plugins/eventEmission/types.js +1 -0
- package/dist/plugins/eventEmission/utils.d.ts +45 -0
- package/dist/plugins/eventEmission/utils.d.ts.map +1 -0
- package/dist/plugins/eventEmission/utils.js +114 -0
- package/dist/plugins/fetch/schemas.d.ts +4 -4
- package/dist/plugins/getAction/index.d.ts.map +1 -1
- package/dist/plugins/getAction/index.js +3 -2
- package/dist/plugins/getAction/schemas.d.ts +2 -2
- package/dist/plugins/listActions/schemas.d.ts +2 -2
- package/dist/plugins/listInputFieldChoices/schemas.d.ts +4 -4
- package/dist/plugins/listInputFields/index.d.ts +2 -1
- package/dist/plugins/listInputFields/index.d.ts.map +1 -1
- package/dist/plugins/listInputFields/index.js +7 -2
- package/dist/plugins/listInputFields/index.test.js +36 -5
- package/dist/plugins/listInputFields/schemas.d.ts +2 -2
- package/dist/plugins/request/schemas.d.ts +4 -4
- package/dist/plugins/runAction/index.d.ts.map +1 -1
- package/dist/plugins/runAction/index.js +6 -1
- package/dist/plugins/runAction/schemas.d.ts +2 -2
- package/dist/resolvers/actionType.d.ts.map +1 -1
- package/dist/resolvers/actionType.js +2 -3
- package/dist/resolvers/authenticationId.d.ts.map +1 -1
- package/dist/schemas/Action.d.ts +2 -2
- package/dist/schemas/App.d.ts +30 -30
- package/dist/sdk.d.ts +2 -2
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js +4 -1
- package/dist/types/sdk.d.ts +5 -1
- package/dist/types/sdk.d.ts.map +1 -1
- package/dist/types/telemetry-events.d.ts +76 -0
- package/dist/types/telemetry-events.d.ts.map +1 -0
- package/dist/types/telemetry-events.js +8 -0
- package/package.json +1 -1
- package/src/api/debug.ts +44 -1
- package/src/constants.ts +6 -0
- package/src/index.ts +24 -0
- package/src/plugins/api/index.ts +1 -5
- package/src/plugins/eventEmission/builders.ts +115 -0
- package/src/plugins/eventEmission/index.test.ts +169 -0
- package/src/plugins/eventEmission/index.ts +294 -0
- package/src/plugins/eventEmission/transport.test.ts +214 -0
- package/src/plugins/eventEmission/transport.ts +135 -0
- package/src/plugins/eventEmission/types.ts +58 -0
- package/src/plugins/eventEmission/utils.ts +121 -0
- package/src/plugins/getAction/index.ts +5 -2
- package/src/plugins/listInputFields/index.test.ts +37 -5
- package/src/plugins/listInputFields/index.ts +10 -3
- package/src/plugins/runAction/index.ts +9 -0
- package/src/resolvers/actionType.ts +4 -3
- package/src/resolvers/authenticationId.ts +2 -1
- package/src/sdk.ts +5 -1
- package/src/types/sdk.ts +7 -1
- package/src/types/telemetry-events.ts +85 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event builder utilities for creating telemetry events
|
|
3
|
+
*
|
|
4
|
+
* Provides builder functions that auto-populate common fields and ensure
|
|
5
|
+
* schema compliance for all event types.
|
|
6
|
+
*/
|
|
7
|
+
import { generateEventId, getCurrentTimestamp, getReleaseId, getOsInfo, getPlatformVersions, getMemoryUsage, getCpuTime, isCi, getCiPlatform, } from "./utils";
|
|
8
|
+
import sdkPackageJson from "../../../package.json";
|
|
9
|
+
// Create base event with auto-populated common fields
|
|
10
|
+
// Kept for backward compatibility but can be replaced with direct construction
|
|
11
|
+
export function createBaseEvent(context = {}) {
|
|
12
|
+
return {
|
|
13
|
+
event_id: generateEventId(),
|
|
14
|
+
timestamp_ms: getCurrentTimestamp(),
|
|
15
|
+
release_id: getReleaseId(),
|
|
16
|
+
customuser_id: context.customuser_id,
|
|
17
|
+
account_id: context.account_id,
|
|
18
|
+
identity_id: context.identity_id,
|
|
19
|
+
visitor_id: context.visitor_id,
|
|
20
|
+
correlation_id: context.correlation_id,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export function buildErrorEvent(data, context = {}) {
|
|
24
|
+
return {
|
|
25
|
+
...createBaseEvent(context),
|
|
26
|
+
zap_id: context.zap_id,
|
|
27
|
+
node_id: context.node_id,
|
|
28
|
+
selected_api: context.selected_api,
|
|
29
|
+
app_id: context.app_id,
|
|
30
|
+
app_version_id: context.app_version_id,
|
|
31
|
+
environment: context.environment,
|
|
32
|
+
sdk_version: sdkPackageJson.version,
|
|
33
|
+
...data,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export function buildApplicationLifecycleEvent(data, context = {}) {
|
|
37
|
+
const osInfo = getOsInfo();
|
|
38
|
+
const platformVersions = getPlatformVersions();
|
|
39
|
+
return {
|
|
40
|
+
...createBaseEvent(context),
|
|
41
|
+
selected_api: context.selected_api,
|
|
42
|
+
app_id: context.app_id,
|
|
43
|
+
app_version_id: context.app_version_id,
|
|
44
|
+
sdk_version: sdkPackageJson.version,
|
|
45
|
+
cli_version: null,
|
|
46
|
+
memory_usage_bytes: data.memory_usage_bytes ?? getMemoryUsage(),
|
|
47
|
+
peak_memory_usage_bytes: data.peak_memory_usage_bytes ?? getMemoryUsage(),
|
|
48
|
+
cpu_time_ms: data.cpu_time_ms ?? getCpuTime(),
|
|
49
|
+
os_platform: osInfo.platform,
|
|
50
|
+
os_release: osInfo.release,
|
|
51
|
+
os_architecture: osInfo.architecture,
|
|
52
|
+
platform_versions: platformVersions,
|
|
53
|
+
environment: context.environment ?? (process.env.NODE_ENV || null),
|
|
54
|
+
is_ci_environment: isCi(),
|
|
55
|
+
ci_platform: getCiPlatform(),
|
|
56
|
+
session_id: null,
|
|
57
|
+
metadata: null,
|
|
58
|
+
process_argv: process.argv || null,
|
|
59
|
+
...data,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export function buildErrorEventWithContext(data, context = {}) {
|
|
63
|
+
const executionTime = data.execution_start_time
|
|
64
|
+
? Date.now() - data.execution_start_time
|
|
65
|
+
: null;
|
|
66
|
+
return {
|
|
67
|
+
...createBaseEvent(context),
|
|
68
|
+
zap_id: context.zap_id,
|
|
69
|
+
node_id: context.node_id,
|
|
70
|
+
selected_api: context.selected_api,
|
|
71
|
+
app_id: context.app_id,
|
|
72
|
+
app_version_id: context.app_version_id,
|
|
73
|
+
environment: context.environment ?? (process.env.NODE_ENV || null),
|
|
74
|
+
sdk_version: sdkPackageJson.version,
|
|
75
|
+
execution_time_before_error_ms: executionTime,
|
|
76
|
+
...data,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event Emission Plugin for Zapier SDK
|
|
3
|
+
*
|
|
4
|
+
* Provides silent telemetry event emission capability to the SDK.
|
|
5
|
+
* All events are emitted asynchronously and failures are silently handled.
|
|
6
|
+
*/
|
|
7
|
+
import type { Plugin } from "../../types/plugin";
|
|
8
|
+
import type { BaseEvent } from "../../types/telemetry-events";
|
|
9
|
+
import type { EventTransport, TransportConfig } from "./transport";
|
|
10
|
+
export interface EventEmissionConfig {
|
|
11
|
+
enabled?: boolean;
|
|
12
|
+
transport?: TransportConfig;
|
|
13
|
+
}
|
|
14
|
+
export interface EventEmissionContext {
|
|
15
|
+
eventEmission: {
|
|
16
|
+
transport: EventTransport;
|
|
17
|
+
config: EventEmissionConfig;
|
|
18
|
+
emit<T extends any>(subject: string, event: T): void;
|
|
19
|
+
createBaseEvent(): BaseEvent;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export interface EventEmissionProvides {
|
|
23
|
+
context: EventEmissionContext;
|
|
24
|
+
}
|
|
25
|
+
export declare const eventEmissionPlugin: Plugin<{}, {
|
|
26
|
+
options: {
|
|
27
|
+
eventEmission?: EventEmissionConfig;
|
|
28
|
+
};
|
|
29
|
+
}, EventEmissionProvides>;
|
|
30
|
+
export type { EventContext, ApplicationLifecycleEventData, EnhancedErrorEventData, } from "./types";
|
|
31
|
+
export { buildApplicationLifecycleEvent, buildErrorEventWithContext, buildErrorEvent, createBaseEvent, } from "./builders";
|
|
32
|
+
export type { BaseEvent } from "../../types/telemetry-events";
|
|
33
|
+
export * from "./utils";
|
|
34
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugins/eventEmission/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAUnE,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,eAAe,CAAC;CAC7B;AAGD,MAAM,WAAW,oBAAoB;IACnC,aAAa,EAAE;QACb,SAAS,EAAE,cAAc,CAAC;QAC1B,MAAM,EAAE,mBAAmB,CAAC;QAE5B,IAAI,CAAC,CAAC,SAAS,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;QAErD,eAAe,IAAI,SAAS,CAAC;KAC9B,CAAC;CACH;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,oBAAoB,CAAC;CAC/B;AA4CD,eAAO,MAAM,mBAAmB,EAAE,MAAM,CACtC,EAAE,EACF;IAAE,OAAO,EAAE;QAAE,aAAa,CAAC,EAAE,mBAAmB,CAAA;KAAE,CAAA;CAAE,EACpD,qBAAqB,CAiMtB,CAAC;AAGF,YAAY,EACV,YAAY,EACZ,6BAA6B,EAC7B,sBAAsB,GACvB,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,8BAA8B,EAC9B,0BAA0B,EAC1B,eAAe,EACf,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAC9D,cAAc,SAAS,CAAC"}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event Emission Plugin for Zapier SDK
|
|
3
|
+
*
|
|
4
|
+
* Provides silent telemetry event emission capability to the SDK.
|
|
5
|
+
* All events are emitted asynchronously and failures are silently handled.
|
|
6
|
+
*/
|
|
7
|
+
import { createTransport } from "./transport";
|
|
8
|
+
import { generateEventId, getCurrentTimestamp, getReleaseId } from "./utils";
|
|
9
|
+
import { buildApplicationLifecycleEvent, buildErrorEventWithContext, } from "./builders";
|
|
10
|
+
import { TRACKING_API_ENDPOINT } from "../../constants";
|
|
11
|
+
const APPLICATION_LIFECYCLE_EVENT_SUBJECT = "platform.sdk.ApplicationLifecycleEvent";
|
|
12
|
+
const ERROR_OCCURRED_EVENT_SUBJECT = "platform.sdk.ErrorOccurredEvent";
|
|
13
|
+
// Silent emission wrapper - ensures events never disrupt SDK operation
|
|
14
|
+
async function silentEmit(transport, subject, event) {
|
|
15
|
+
try {
|
|
16
|
+
// Fire and forget - don't await the transport
|
|
17
|
+
transport.emit(subject, event).catch(() => {
|
|
18
|
+
// Silently ignore transport failures
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// Silently ignore all errors
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// Helper to get transport config from environment or defaults
|
|
26
|
+
function getTransportConfig() {
|
|
27
|
+
const envTransport = process?.env?.ZAPIER_SDK_TELEMETRY_TRANSPORT;
|
|
28
|
+
if (envTransport === "noop" || envTransport === "disabled") {
|
|
29
|
+
return { type: "noop" };
|
|
30
|
+
}
|
|
31
|
+
if (envTransport === "console") {
|
|
32
|
+
return { type: "console" };
|
|
33
|
+
}
|
|
34
|
+
// Default to HTTP transport
|
|
35
|
+
const endpoint = process?.env?.ZAPIER_SDK_TELEMETRY_ENDPOINT || TRACKING_API_ENDPOINT;
|
|
36
|
+
return {
|
|
37
|
+
type: "http",
|
|
38
|
+
endpoint,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export const eventEmissionPlugin = ({ context }) => {
|
|
42
|
+
const defaultTransport = getTransportConfig();
|
|
43
|
+
// Merge config: env var takes precedence over options, options take precedence over defaults
|
|
44
|
+
const config = {
|
|
45
|
+
enabled: context.options.eventEmission?.enabled ?? true,
|
|
46
|
+
transport:
|
|
47
|
+
// If env var is set, use it (defaultTransport will be from env)
|
|
48
|
+
process?.env?.ZAPIER_SDK_TELEMETRY_TRANSPORT
|
|
49
|
+
? defaultTransport
|
|
50
|
+
: // Otherwise, use option transport or default
|
|
51
|
+
(context.options.eventEmission?.transport ?? defaultTransport),
|
|
52
|
+
};
|
|
53
|
+
const startupTime = Date.now();
|
|
54
|
+
let shutdownStartTime = null;
|
|
55
|
+
// If disabled, return noop implementations
|
|
56
|
+
if (!config.enabled) {
|
|
57
|
+
return {
|
|
58
|
+
context: {
|
|
59
|
+
eventEmission: {
|
|
60
|
+
transport: createTransport({ type: "noop" }),
|
|
61
|
+
config,
|
|
62
|
+
emit: () => { },
|
|
63
|
+
createBaseEvent: () => ({
|
|
64
|
+
event_id: generateEventId(),
|
|
65
|
+
timestamp_ms: getCurrentTimestamp(),
|
|
66
|
+
release_id: getReleaseId(),
|
|
67
|
+
customuser_id: null,
|
|
68
|
+
account_id: null,
|
|
69
|
+
identity_id: null,
|
|
70
|
+
visitor_id: null,
|
|
71
|
+
correlation_id: null,
|
|
72
|
+
}),
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
let transport;
|
|
78
|
+
try {
|
|
79
|
+
transport = createTransport(config.transport || { type: "noop" });
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// If transport creation fails, fallback to noop to maintain silent operation
|
|
83
|
+
transport = createTransport({ type: "noop" });
|
|
84
|
+
}
|
|
85
|
+
// Helper to create base event
|
|
86
|
+
const createBaseEventHelper = () => ({
|
|
87
|
+
event_id: generateEventId(),
|
|
88
|
+
timestamp_ms: getCurrentTimestamp(),
|
|
89
|
+
release_id: getReleaseId(),
|
|
90
|
+
customuser_id: null,
|
|
91
|
+
account_id: null,
|
|
92
|
+
identity_id: null,
|
|
93
|
+
visitor_id: null,
|
|
94
|
+
correlation_id: null,
|
|
95
|
+
});
|
|
96
|
+
// Register lifecycle event handlers if enabled
|
|
97
|
+
if (config.enabled) {
|
|
98
|
+
// Emit startup event
|
|
99
|
+
const startupEvent = buildApplicationLifecycleEvent({
|
|
100
|
+
lifecycle_event_type: "startup",
|
|
101
|
+
});
|
|
102
|
+
silentEmit(transport, APPLICATION_LIFECYCLE_EVENT_SUBJECT, startupEvent);
|
|
103
|
+
// Register process event handlers (Node.js only)
|
|
104
|
+
if (typeof process?.on === "function") {
|
|
105
|
+
// Handle normal process exit
|
|
106
|
+
process.on("exit", (code) => {
|
|
107
|
+
const uptime = Date.now() - startupTime;
|
|
108
|
+
const shutdownDuration = shutdownStartTime
|
|
109
|
+
? Date.now() - shutdownStartTime
|
|
110
|
+
: null;
|
|
111
|
+
const exitEvent = buildApplicationLifecycleEvent({
|
|
112
|
+
lifecycle_event_type: "exit",
|
|
113
|
+
exit_code: code,
|
|
114
|
+
uptime_ms: uptime,
|
|
115
|
+
is_graceful_shutdown: code === 0,
|
|
116
|
+
shutdown_duration_ms: shutdownDuration,
|
|
117
|
+
});
|
|
118
|
+
silentEmit(transport, APPLICATION_LIFECYCLE_EVENT_SUBJECT, exitEvent);
|
|
119
|
+
});
|
|
120
|
+
// Handle uncaught exceptions
|
|
121
|
+
process.on("uncaughtException", async (error) => {
|
|
122
|
+
const errorEvent = buildErrorEventWithContext({
|
|
123
|
+
error_message: error.message || "Unknown error",
|
|
124
|
+
error_type: "UncaughtException",
|
|
125
|
+
error_stack_trace: error.stack || null,
|
|
126
|
+
error_severity: "critical",
|
|
127
|
+
is_user_facing: false,
|
|
128
|
+
is_recoverable: false,
|
|
129
|
+
execution_start_time: startupTime,
|
|
130
|
+
});
|
|
131
|
+
// Wait up to 300ms for telemetry to send before allowing process to exit
|
|
132
|
+
try {
|
|
133
|
+
await Promise.race([
|
|
134
|
+
transport.emit(ERROR_OCCURRED_EVENT_SUBJECT, errorEvent),
|
|
135
|
+
new Promise((resolve) => setTimeout(resolve, 300)),
|
|
136
|
+
]);
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
// Silently ignore telemetry failures
|
|
140
|
+
}
|
|
141
|
+
// Let Node.js exit naturally after all handlers complete
|
|
142
|
+
});
|
|
143
|
+
// Handle unhandled promise rejections
|
|
144
|
+
process.on("unhandledRejection", async (reason, promise) => {
|
|
145
|
+
const errorMessage = reason instanceof Error
|
|
146
|
+
? reason.message
|
|
147
|
+
: typeof reason === "string"
|
|
148
|
+
? reason
|
|
149
|
+
: "Unhandled promise rejection";
|
|
150
|
+
const errorStack = reason instanceof Error ? reason.stack : null;
|
|
151
|
+
const errorEvent = buildErrorEventWithContext({
|
|
152
|
+
error_message: errorMessage,
|
|
153
|
+
error_type: "UnhandledRejection",
|
|
154
|
+
error_stack_trace: errorStack,
|
|
155
|
+
error_severity: "critical",
|
|
156
|
+
is_user_facing: false,
|
|
157
|
+
is_recoverable: false,
|
|
158
|
+
execution_start_time: startupTime,
|
|
159
|
+
error_metadata: {
|
|
160
|
+
promise: String(promise),
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
// Wait up to 300ms for telemetry to send
|
|
164
|
+
try {
|
|
165
|
+
await Promise.race([
|
|
166
|
+
transport.emit(ERROR_OCCURRED_EVENT_SUBJECT, errorEvent),
|
|
167
|
+
new Promise((resolve) => setTimeout(resolve, 300)),
|
|
168
|
+
]);
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
// Silently ignore telemetry failures
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
// Handle termination signals
|
|
175
|
+
const handleSignal = async (signal) => {
|
|
176
|
+
shutdownStartTime = Date.now();
|
|
177
|
+
const uptime = Date.now() - startupTime;
|
|
178
|
+
const signalEvent = buildApplicationLifecycleEvent({
|
|
179
|
+
lifecycle_event_type: "signal_termination",
|
|
180
|
+
signal_name: signal,
|
|
181
|
+
uptime_ms: uptime,
|
|
182
|
+
is_graceful_shutdown: true,
|
|
183
|
+
});
|
|
184
|
+
// Wait up to 300ms for telemetry to send
|
|
185
|
+
try {
|
|
186
|
+
await Promise.race([
|
|
187
|
+
transport.emit(APPLICATION_LIFECYCLE_EVENT_SUBJECT, signalEvent),
|
|
188
|
+
new Promise((resolve) => setTimeout(resolve, 300)),
|
|
189
|
+
]);
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
// Silently ignore telemetry failures
|
|
193
|
+
}
|
|
194
|
+
// Let other signal handlers run and decide when to exit
|
|
195
|
+
};
|
|
196
|
+
// Register signal handlers
|
|
197
|
+
["SIGINT", "SIGTERM"].forEach((signal) => {
|
|
198
|
+
process.on(signal, () => handleSignal(signal));
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return {
|
|
203
|
+
context: {
|
|
204
|
+
eventEmission: {
|
|
205
|
+
transport,
|
|
206
|
+
config,
|
|
207
|
+
emit: (subject, event) => {
|
|
208
|
+
silentEmit(transport, subject, event);
|
|
209
|
+
},
|
|
210
|
+
createBaseEvent: createBaseEventHelper,
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
};
|
|
215
|
+
export { buildApplicationLifecycleEvent, buildErrorEventWithContext, buildErrorEvent, createBaseEvent, } from "./builders";
|
|
216
|
+
export * from "./utils";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../../src/plugins/eventEmission/index.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for Event Emission Plugin
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
5
|
+
import { eventEmissionPlugin } from "./index";
|
|
6
|
+
import { createTransport } from "./transport";
|
|
7
|
+
// Mock transport for testing
|
|
8
|
+
const mockTransport = {
|
|
9
|
+
emit: vi.fn().mockResolvedValue(undefined),
|
|
10
|
+
close: vi.fn().mockResolvedValue(undefined),
|
|
11
|
+
};
|
|
12
|
+
vi.mock("./transport", () => ({
|
|
13
|
+
createTransport: vi.fn(() => mockTransport),
|
|
14
|
+
}));
|
|
15
|
+
describe("eventEmissionPlugin", () => {
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
vi.clearAllMocks();
|
|
18
|
+
});
|
|
19
|
+
it("should create plugin with default configuration", () => {
|
|
20
|
+
const plugin = eventEmissionPlugin({
|
|
21
|
+
sdk: {},
|
|
22
|
+
context: {
|
|
23
|
+
meta: {},
|
|
24
|
+
options: {},
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
expect(plugin.context.eventEmission).toBeDefined();
|
|
28
|
+
expect(plugin.context.eventEmission.emit).toBeDefined();
|
|
29
|
+
expect(plugin.context.eventEmission.transport).toBeDefined();
|
|
30
|
+
expect(plugin.context.eventEmission.config).toBeDefined();
|
|
31
|
+
});
|
|
32
|
+
it("should create noop implementations when disabled", () => {
|
|
33
|
+
const config = { enabled: false };
|
|
34
|
+
const plugin = eventEmissionPlugin({
|
|
35
|
+
sdk: {},
|
|
36
|
+
context: {
|
|
37
|
+
meta: {},
|
|
38
|
+
options: { eventEmission: config },
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
// Should not emit any events when disabled
|
|
42
|
+
plugin.context.eventEmission.emit("platform.sdk.TestEvent", {
|
|
43
|
+
test_event: "data",
|
|
44
|
+
});
|
|
45
|
+
expect(mockTransport.emit).not.toHaveBeenCalled();
|
|
46
|
+
});
|
|
47
|
+
it("should emit events using generic emit method", async () => {
|
|
48
|
+
const plugin = eventEmissionPlugin({
|
|
49
|
+
sdk: {},
|
|
50
|
+
context: {
|
|
51
|
+
meta: {},
|
|
52
|
+
options: {
|
|
53
|
+
eventEmission: {
|
|
54
|
+
enabled: true,
|
|
55
|
+
transport: { type: "console" },
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
const testEvent = {
|
|
61
|
+
test_data: "example",
|
|
62
|
+
value: 123,
|
|
63
|
+
};
|
|
64
|
+
const testSubject = "test.event.TestEvent";
|
|
65
|
+
plugin.context.eventEmission.emit(testSubject, testEvent);
|
|
66
|
+
// Give async emission time to complete
|
|
67
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
68
|
+
expect(mockTransport.emit).toHaveBeenCalledWith(testSubject, testEvent);
|
|
69
|
+
});
|
|
70
|
+
it("should handle transport creation failures silently", () => {
|
|
71
|
+
// Mock createTransport to throw an error
|
|
72
|
+
vi.mocked(createTransport).mockImplementationOnce(() => {
|
|
73
|
+
throw new Error("Transport creation failed");
|
|
74
|
+
});
|
|
75
|
+
expect(() => {
|
|
76
|
+
eventEmissionPlugin({
|
|
77
|
+
sdk: {},
|
|
78
|
+
context: {
|
|
79
|
+
meta: {},
|
|
80
|
+
options: {
|
|
81
|
+
eventEmission: {
|
|
82
|
+
enabled: true,
|
|
83
|
+
transport: { type: "http", endpoint: "invalid-url" },
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
}).not.toThrow();
|
|
89
|
+
});
|
|
90
|
+
it("should handle event emission failures silently", async () => {
|
|
91
|
+
// Mock transport to throw error
|
|
92
|
+
const failingTransport = {
|
|
93
|
+
emit: vi.fn().mockRejectedValue(new Error("Network error")),
|
|
94
|
+
close: vi.fn().mockResolvedValue(undefined),
|
|
95
|
+
};
|
|
96
|
+
vi.mocked(createTransport).mockReturnValueOnce(failingTransport);
|
|
97
|
+
const plugin = eventEmissionPlugin({
|
|
98
|
+
sdk: {},
|
|
99
|
+
context: {
|
|
100
|
+
meta: {},
|
|
101
|
+
options: {
|
|
102
|
+
eventEmission: {
|
|
103
|
+
enabled: true,
|
|
104
|
+
transport: {
|
|
105
|
+
type: "http",
|
|
106
|
+
endpoint: "https://example.com",
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
// Should not throw even if transport fails
|
|
113
|
+
expect(() => {
|
|
114
|
+
plugin.context.eventEmission.emit("test.event.TestEvent", {
|
|
115
|
+
test_event: "data",
|
|
116
|
+
});
|
|
117
|
+
}).not.toThrow();
|
|
118
|
+
// Give async emission time to complete
|
|
119
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
120
|
+
expect(failingTransport.emit).toHaveBeenCalled();
|
|
121
|
+
});
|
|
122
|
+
it("should merge options with defaults", () => {
|
|
123
|
+
const plugin = eventEmissionPlugin({
|
|
124
|
+
sdk: {},
|
|
125
|
+
context: {
|
|
126
|
+
meta: {},
|
|
127
|
+
options: {
|
|
128
|
+
eventEmission: {
|
|
129
|
+
transport: {
|
|
130
|
+
type: "http",
|
|
131
|
+
endpoint: "https://example.com",
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
expect(plugin.context.eventEmission.config.enabled).toBe(true);
|
|
138
|
+
expect(plugin.context.eventEmission.config.transport).toEqual({
|
|
139
|
+
type: "http",
|
|
140
|
+
endpoint: "https://example.com",
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport abstraction layer for event emission
|
|
3
|
+
*
|
|
4
|
+
* Provides configurable transport mechanisms for emitting telemetry events.
|
|
5
|
+
* All transports implement silent failure to prevent SDK disruption.
|
|
6
|
+
*/
|
|
7
|
+
export interface EventTransport {
|
|
8
|
+
emit<T extends any>(subject: string, event: T): Promise<void>;
|
|
9
|
+
close?(): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
export interface TransportConfig {
|
|
12
|
+
type: "http" | "console" | "noop";
|
|
13
|
+
endpoint?: string;
|
|
14
|
+
headers?: Record<string, string>;
|
|
15
|
+
retryAttempts?: number;
|
|
16
|
+
retryDelayMs?: number;
|
|
17
|
+
}
|
|
18
|
+
export declare class HttpTransport implements EventTransport {
|
|
19
|
+
private config;
|
|
20
|
+
constructor(config: {
|
|
21
|
+
endpoint: string;
|
|
22
|
+
headers?: Record<string, string>;
|
|
23
|
+
retryAttempts?: number;
|
|
24
|
+
retryDelayMs?: number;
|
|
25
|
+
});
|
|
26
|
+
emit<T extends any>(subject: string, event: T): Promise<void>;
|
|
27
|
+
private emitWithRetry;
|
|
28
|
+
private delay;
|
|
29
|
+
}
|
|
30
|
+
export declare class ConsoleTransport implements EventTransport {
|
|
31
|
+
emit<T extends any>(subject: string, event: T): Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
export declare class NoopTransport implements EventTransport {
|
|
34
|
+
emit<T extends any>(_subject: string, _event: T): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
export declare function createTransport(config: TransportConfig): EventTransport;
|
|
37
|
+
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../../src/plugins/eventEmission/transport.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,CAAC,SAAS,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB;AAGD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAGD,qBAAa,aAAc,YAAW,cAAc;IAEhD,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACjC,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB;IAGG,IAAI,CAAC,CAAC,SAAS,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;YAYrD,aAAa;YAiCb,KAAK;CAGpB;AAGD,qBAAa,gBAAiB,YAAW,cAAc;IAC/C,IAAI,CAAC,CAAC,SAAS,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;CAUpE;AAGD,qBAAa,aAAc,YAAW,cAAc;IAC5C,IAAI,CAAC,CAAC,SAAS,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;CAGtE;AAGD,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,cAAc,CAyBvE"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport abstraction layer for event emission
|
|
3
|
+
*
|
|
4
|
+
* Provides configurable transport mechanisms for emitting telemetry events.
|
|
5
|
+
* All transports implement silent failure to prevent SDK disruption.
|
|
6
|
+
*/
|
|
7
|
+
// Constants for transport configuration
|
|
8
|
+
const DEFAULT_RETRY_ATTEMPTS = 2;
|
|
9
|
+
const DEFAULT_RETRY_DELAY_MS = 300;
|
|
10
|
+
// HTTP Transport - sends events to remote endpoint
|
|
11
|
+
export class HttpTransport {
|
|
12
|
+
constructor(config) {
|
|
13
|
+
this.config = config;
|
|
14
|
+
}
|
|
15
|
+
async emit(subject, event) {
|
|
16
|
+
try {
|
|
17
|
+
await this.emitWithRetry(subject, event, this.config.retryAttempts || DEFAULT_RETRY_ATTEMPTS);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
// Silent failure - never throw
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
async emitWithRetry(subject, event, attemptsLeft) {
|
|
24
|
+
try {
|
|
25
|
+
const payload = {
|
|
26
|
+
subject,
|
|
27
|
+
properties: event,
|
|
28
|
+
};
|
|
29
|
+
const response = await fetch(this.config.endpoint, {
|
|
30
|
+
method: "POST",
|
|
31
|
+
headers: {
|
|
32
|
+
"Content-Type": "application/json",
|
|
33
|
+
...this.config.headers,
|
|
34
|
+
},
|
|
35
|
+
body: JSON.stringify(payload),
|
|
36
|
+
});
|
|
37
|
+
if (!response.ok && attemptsLeft > 1) {
|
|
38
|
+
await this.delay(this.config.retryDelayMs || DEFAULT_RETRY_DELAY_MS);
|
|
39
|
+
return this.emitWithRetry(subject, event, attemptsLeft - 1);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
if (attemptsLeft > 1) {
|
|
44
|
+
await this.delay(this.config.retryDelayMs || DEFAULT_RETRY_DELAY_MS);
|
|
45
|
+
return this.emitWithRetry(subject, event, attemptsLeft - 1);
|
|
46
|
+
}
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async delay(ms) {
|
|
51
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Console Transport - logs events to console (for development)
|
|
55
|
+
export class ConsoleTransport {
|
|
56
|
+
async emit(subject, event) {
|
|
57
|
+
try {
|
|
58
|
+
console.log("[SDK Telemetry]", JSON.stringify({ subject, properties: event }, null, 2));
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// Silent failure - never throw
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// No-op Transport - discards all events (for testing/disabled state)
|
|
66
|
+
export class NoopTransport {
|
|
67
|
+
async emit(_subject, _event) {
|
|
68
|
+
// Intentionally do nothing
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// Transport factory
|
|
72
|
+
export function createTransport(config) {
|
|
73
|
+
try {
|
|
74
|
+
switch (config.type) {
|
|
75
|
+
case "http":
|
|
76
|
+
if (!config.endpoint) {
|
|
77
|
+
throw new Error("HTTP transport requires endpoint");
|
|
78
|
+
}
|
|
79
|
+
return new HttpTransport({
|
|
80
|
+
endpoint: config.endpoint,
|
|
81
|
+
headers: config.headers,
|
|
82
|
+
retryAttempts: config.retryAttempts,
|
|
83
|
+
retryDelayMs: config.retryDelayMs,
|
|
84
|
+
});
|
|
85
|
+
case "console":
|
|
86
|
+
return new ConsoleTransport();
|
|
87
|
+
case "noop":
|
|
88
|
+
default:
|
|
89
|
+
return new NoopTransport();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// If transport creation fails, return noop to maintain silent operation
|
|
94
|
+
return new NoopTransport();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.test.d.ts","sourceRoot":"","sources":["../../../src/plugins/eventEmission/transport.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|