@zapier/zapier-sdk 0.15.2 → 0.15.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.
Files changed (34) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/api/auth.d.ts +10 -0
  3. package/dist/api/auth.d.ts.map +1 -1
  4. package/dist/api/auth.js +35 -0
  5. package/dist/api/schemas.d.ts +38 -38
  6. package/dist/index.cjs +209 -27
  7. package/dist/index.d.mts +151 -59
  8. package/dist/index.mjs +209 -27
  9. package/dist/plugins/eventEmission/index.d.ts +1 -1
  10. package/dist/plugins/eventEmission/index.d.ts.map +1 -1
  11. package/dist/plugins/eventEmission/index.js +90 -22
  12. package/dist/plugins/eventEmission/index.test.js +179 -2
  13. package/dist/plugins/fetch/schemas.d.ts +2 -2
  14. package/dist/plugins/getAction/schemas.d.ts +2 -2
  15. package/dist/plugins/getInputFieldsSchema/schemas.d.ts +2 -2
  16. package/dist/plugins/listActions/index.test.js +25 -0
  17. package/dist/plugins/listActions/schemas.d.ts +2 -2
  18. package/dist/plugins/listAuthentications/index.test.js +20 -0
  19. package/dist/plugins/listInputFieldChoices/schemas.d.ts +6 -6
  20. package/dist/plugins/listInputFields/schemas.d.ts +6 -6
  21. package/dist/plugins/manifest/index.d.ts +42 -3
  22. package/dist/plugins/manifest/index.d.ts.map +1 -1
  23. package/dist/plugins/manifest/index.js +68 -1
  24. package/dist/plugins/manifest/index.test.js +513 -1
  25. package/dist/plugins/manifest/schemas.d.ts +75 -2
  26. package/dist/plugins/manifest/schemas.d.ts.map +1 -1
  27. package/dist/plugins/manifest/schemas.js +27 -3
  28. package/dist/plugins/request/schemas.d.ts +4 -4
  29. package/dist/plugins/runAction/schemas.d.ts +6 -6
  30. package/dist/schemas/Action.d.ts +1 -1
  31. package/dist/schemas/Auth.d.ts +6 -6
  32. package/dist/sdk.d.ts +23 -1
  33. package/dist/sdk.d.ts.map +1 -1
  34. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -2699,6 +2699,17 @@ async function readFile(filePath) {
2699
2699
  throw new Error(`File not found: ${filePath}`);
2700
2700
  }
2701
2701
  var DEFAULT_CONFIG_PATH = ".zapierrc";
2702
+ var ActionEntrySchema = z.object({
2703
+ appKey: z.string().describe("App key (slug or implementation name)"),
2704
+ actionKey: z.string().describe("Action key identifier"),
2705
+ actionType: z.string().describe("Action type (e.g., 'read', 'write', 'search')"),
2706
+ authenticationId: z.number().nullable().optional().describe("Authentication ID used"),
2707
+ inputs: z.record(z.unknown()).optional().describe("Resolved input values"),
2708
+ schema: z.record(z.unknown()).describe(
2709
+ "Complete JSON Schema from getInputFieldsSchema (includes $schema, type, properties, required, etc.)"
2710
+ ),
2711
+ createdAt: z.string().describe("ISO 8601 timestamp when created")
2712
+ });
2702
2713
  var ManifestSchema = z.object({
2703
2714
  apps: z.record(
2704
2715
  z.string(),
@@ -2708,8 +2719,9 @@ var ManifestSchema = z.object({
2708
2719
  ),
2709
2720
  version: z.string().describe("Version string (e.g., '1.21.1')")
2710
2721
  })
2711
- )
2712
- }).describe("Manifest mapping app keys to version information");
2722
+ ),
2723
+ actions: z.record(z.string(), ActionEntrySchema).optional().describe("Saved action configurations with their schemas")
2724
+ }).describe("Manifest for app version locking and action configurations");
2713
2725
  z.object({
2714
2726
  manifestPath: z.string().optional().describe("Path to manifest file"),
2715
2727
  manifest: z.record(
@@ -2980,7 +2992,81 @@ var manifestPlugin = (params) => {
2980
2992
  await writeManifestToFile(updatedManifest, configPath);
2981
2993
  resolvedManifest = void 0;
2982
2994
  }
2983
- return [manifestKey, entry, updatedManifest];
2995
+ return {
2996
+ key: manifestKey,
2997
+ entry,
2998
+ manifest: updatedManifest
2999
+ };
3000
+ };
3001
+ const addActionEntry = async (options2) => {
3002
+ const {
3003
+ name,
3004
+ entry,
3005
+ configPath = DEFAULT_CONFIG_PATH,
3006
+ skipWrite = false,
3007
+ manifest: inputManifest
3008
+ } = options2;
3009
+ const manifest2 = inputManifest || await readManifestFromFile(configPath) || { apps: {} };
3010
+ const actions = manifest2.actions || {};
3011
+ if (actions[name] && !skipWrite) {
3012
+ throw new Error(
3013
+ `Action "${name}" already exists. Please choose a different name or remove the existing action first.`
3014
+ );
3015
+ }
3016
+ const updatedManifest = {
3017
+ ...manifest2,
3018
+ actions: {
3019
+ ...actions,
3020
+ [name]: entry
3021
+ }
3022
+ };
3023
+ if (!skipWrite) {
3024
+ await writeManifestToFile(updatedManifest, configPath);
3025
+ resolvedManifest = void 0;
3026
+ }
3027
+ return {
3028
+ name,
3029
+ entry,
3030
+ manifest: updatedManifest
3031
+ };
3032
+ };
3033
+ const findActionEntry = ({
3034
+ name,
3035
+ manifest: manifest2
3036
+ }) => {
3037
+ return manifest2.actions?.[name] || null;
3038
+ };
3039
+ const listActionEntries = async ({
3040
+ configPath = DEFAULT_CONFIG_PATH
3041
+ } = {}) => {
3042
+ const manifest2 = await readManifestFromFile(configPath) || { };
3043
+ return Object.entries(manifest2.actions || {});
3044
+ };
3045
+ const deleteActionEntry = async ({
3046
+ name,
3047
+ configPath = DEFAULT_CONFIG_PATH,
3048
+ skipWrite = false
3049
+ }) => {
3050
+ const manifest2 = await readManifestFromFile(configPath) || { apps: {} };
3051
+ if (!manifest2.actions?.[name]) {
3052
+ throw new Error(`Action "${name}" does not exist.`);
3053
+ }
3054
+ const { [name]: removed, ...remainingActions } = manifest2.actions;
3055
+ const updatedManifest = {
3056
+ ...manifest2,
3057
+ actions: remainingActions
3058
+ };
3059
+ if (!skipWrite) {
3060
+ await writeManifestToFile(updatedManifest, configPath);
3061
+ resolvedManifest = void 0;
3062
+ }
3063
+ return updatedManifest;
3064
+ };
3065
+ const hasActionEntry = ({
3066
+ name,
3067
+ manifest: manifest2
3068
+ }) => {
3069
+ return !!manifest2.actions?.[name];
2984
3070
  };
2985
3071
  return {
2986
3072
  context: {
@@ -2990,7 +3076,14 @@ var manifestPlugin = (params) => {
2990
3076
  api,
2991
3077
  manifest: await getResolvedManifest() ?? { apps: {} }
2992
3078
  }),
2993
- updateManifestEntry
3079
+ updateManifestEntry,
3080
+ addActionEntry,
3081
+ findActionEntry,
3082
+ listActionEntries,
3083
+ deleteActionEntry,
3084
+ hasActionEntry,
3085
+ findManifestEntry,
3086
+ readManifestFromFile
2994
3087
  }
2995
3088
  };
2996
3089
  };
@@ -3084,6 +3177,34 @@ function getAuthorizationHeader(token) {
3084
3177
  }
3085
3178
  return `Bearer ${token}`;
3086
3179
  }
3180
+ function extractUserIdsFromJwt(token) {
3181
+ try {
3182
+ const parts = token.split(".");
3183
+ if (parts.length !== 3) {
3184
+ return { customuser_id: null, account_id: null };
3185
+ }
3186
+ const payload = JSON.parse(
3187
+ Buffer.from(parts[1], "base64url").toString("utf-8")
3188
+ );
3189
+ let actualPayload = payload;
3190
+ if (payload.sub_type === "service" && payload.njwt) {
3191
+ const nestedParts = payload.njwt.split(".");
3192
+ if (nestedParts.length === 3) {
3193
+ actualPayload = JSON.parse(
3194
+ Buffer.from(nestedParts[1], "base64url").toString("utf-8")
3195
+ );
3196
+ }
3197
+ }
3198
+ const accountId = actualPayload["zap:acc"] != null ? parseInt(String(actualPayload["zap:acc"]), 10) : null;
3199
+ const customUserId = actualPayload.sub_type === "customuser" && actualPayload.sub != null ? parseInt(String(actualPayload.sub), 10) : null;
3200
+ return {
3201
+ customuser_id: customUserId !== null && !isNaN(customUserId) ? customUserId : null,
3202
+ account_id: accountId !== null && !isNaN(accountId) ? accountId : null
3203
+ };
3204
+ } catch {
3205
+ return { customuser_id: null, account_id: null };
3206
+ }
3207
+ }
3087
3208
 
3088
3209
  // src/api/debug.ts
3089
3210
  var utilModule = null;
@@ -4480,7 +4601,7 @@ function getCpuTime() {
4480
4601
 
4481
4602
  // package.json
4482
4603
  var package_default = {
4483
- version: "0.15.2"};
4604
+ version: "0.15.4"};
4484
4605
 
4485
4606
  // src/plugins/eventEmission/builders.ts
4486
4607
  function createBaseEvent(context = {}) {
@@ -4551,17 +4672,26 @@ function buildErrorEventWithContext(data, context = {}) {
4551
4672
  }
4552
4673
 
4553
4674
  // src/plugins/eventEmission/index.ts
4675
+ var TELEMETRY_EMIT_TIMEOUT_MS = 300;
4554
4676
  var APPLICATION_LIFECYCLE_EVENT_SUBJECT = "platform.sdk.ApplicationLifecycleEvent";
4555
4677
  var ERROR_OCCURRED_EVENT_SUBJECT = "platform.sdk.ErrorOccurredEvent";
4556
4678
  var transportStates = /* @__PURE__ */ new WeakMap();
4557
- async function silentEmit(transport, subject, event) {
4679
+ async function silentEmit(transport, subject, event, userContextPromise) {
4558
4680
  try {
4559
4681
  let state = transportStates.get(transport);
4560
4682
  if (!state) {
4561
4683
  state = { hasWorked: false, hasLoggedFailure: false };
4562
4684
  transportStates.set(transport, state);
4563
4685
  }
4564
- transport.emit(subject, event).then(() => {
4686
+ let enrichedEvent = event;
4687
+ if (userContextPromise) {
4688
+ try {
4689
+ const userContext = await userContextPromise;
4690
+ enrichedEvent = Object.assign({}, event, userContext);
4691
+ } catch {
4692
+ }
4693
+ }
4694
+ transport.emit(subject, enrichedEvent).then(() => {
4565
4695
  state.hasWorked = true;
4566
4696
  }).catch((error) => {
4567
4697
  if (!state.hasWorked && !state.hasLoggedFailure) {
@@ -4609,6 +4739,19 @@ var eventEmissionPlugin = ({ context }) => {
4609
4739
  )
4610
4740
  )
4611
4741
  };
4742
+ const getUserContext = (async () => {
4743
+ try {
4744
+ const { getToken } = await import('@zapier/zapier-sdk-cli-login');
4745
+ const token = await getToken({
4746
+ baseUrl: context.options.baseUrl
4747
+ });
4748
+ if (token) {
4749
+ return extractUserIdsFromJwt(token);
4750
+ }
4751
+ } catch {
4752
+ }
4753
+ return { customuser_id: null, account_id: null };
4754
+ })();
4612
4755
  const startupTime = Date.now();
4613
4756
  let shutdownStartTime = null;
4614
4757
  if (!config.enabled) {
@@ -4619,7 +4762,7 @@ var eventEmissionPlugin = ({ context }) => {
4619
4762
  config,
4620
4763
  emit: () => {
4621
4764
  },
4622
- createBaseEvent: () => ({
4765
+ createBaseEvent: async () => ({
4623
4766
  event_id: generateEventId(),
4624
4767
  timestamp_ms: getCurrentTimestamp(),
4625
4768
  release_id: getReleaseId(),
@@ -4639,21 +4782,34 @@ var eventEmissionPlugin = ({ context }) => {
4639
4782
  } catch {
4640
4783
  transport = createTransport({ type: "noop" });
4641
4784
  }
4642
- const createBaseEventHelper = () => ({
4643
- event_id: generateEventId(),
4644
- timestamp_ms: getCurrentTimestamp(),
4645
- release_id: getReleaseId(),
4646
- customuser_id: null,
4647
- account_id: null,
4648
- identity_id: null,
4649
- visitor_id: null,
4650
- correlation_id: null
4651
- });
4785
+ const createBaseEventHelper = async () => {
4786
+ const baseEvent = {
4787
+ event_id: generateEventId(),
4788
+ timestamp_ms: getCurrentTimestamp(),
4789
+ release_id: getReleaseId(),
4790
+ customuser_id: null,
4791
+ account_id: null,
4792
+ identity_id: null,
4793
+ visitor_id: null,
4794
+ correlation_id: null
4795
+ };
4796
+ try {
4797
+ const userContext = await getUserContext;
4798
+ return { ...baseEvent, ...userContext };
4799
+ } catch {
4800
+ return baseEvent;
4801
+ }
4802
+ };
4652
4803
  if (config.enabled) {
4653
4804
  const startupEvent = buildApplicationLifecycleEvent({
4654
4805
  lifecycle_event_type: "startup"
4655
4806
  });
4656
- silentEmit(transport, APPLICATION_LIFECYCLE_EVENT_SUBJECT, startupEvent);
4807
+ silentEmit(
4808
+ transport,
4809
+ APPLICATION_LIFECYCLE_EVENT_SUBJECT,
4810
+ startupEvent,
4811
+ getUserContext
4812
+ );
4657
4813
  if (typeof process?.on === "function") {
4658
4814
  process.on("exit", (code) => {
4659
4815
  const uptime = Date.now() - startupTime;
@@ -4665,10 +4821,15 @@ var eventEmissionPlugin = ({ context }) => {
4665
4821
  is_graceful_shutdown: code === 0,
4666
4822
  shutdown_duration_ms: shutdownDuration
4667
4823
  });
4668
- silentEmit(transport, APPLICATION_LIFECYCLE_EVENT_SUBJECT, exitEvent);
4824
+ silentEmit(
4825
+ transport,
4826
+ APPLICATION_LIFECYCLE_EVENT_SUBJECT,
4827
+ exitEvent,
4828
+ getUserContext
4829
+ );
4669
4830
  });
4670
4831
  process.on("uncaughtException", async (error) => {
4671
- const errorEvent = buildErrorEventWithContext({
4832
+ let errorEvent = buildErrorEventWithContext({
4672
4833
  error_message: error.message || "Unknown error",
4673
4834
  error_type: "UncaughtException",
4674
4835
  error_stack_trace: error.stack || null,
@@ -4677,10 +4838,17 @@ var eventEmissionPlugin = ({ context }) => {
4677
4838
  is_recoverable: false,
4678
4839
  execution_start_time: startupTime
4679
4840
  });
4841
+ try {
4842
+ const userContext = await getUserContext;
4843
+ errorEvent = { ...errorEvent, ...userContext };
4844
+ } catch {
4845
+ }
4680
4846
  try {
4681
4847
  await Promise.race([
4682
4848
  transport.emit(ERROR_OCCURRED_EVENT_SUBJECT, errorEvent),
4683
- new Promise((resolve2) => setTimeout(resolve2, 300))
4849
+ new Promise(
4850
+ (resolve2) => setTimeout(resolve2, TELEMETRY_EMIT_TIMEOUT_MS)
4851
+ )
4684
4852
  ]);
4685
4853
  } catch {
4686
4854
  }
@@ -4690,7 +4858,7 @@ var eventEmissionPlugin = ({ context }) => {
4690
4858
  async (reason, promise) => {
4691
4859
  const errorMessage = reason instanceof Error ? reason.message : typeof reason === "string" ? reason : "Unhandled promise rejection";
4692
4860
  const errorStack = reason instanceof Error ? reason.stack : null;
4693
- const errorEvent = buildErrorEventWithContext({
4861
+ let errorEvent = buildErrorEventWithContext({
4694
4862
  error_message: errorMessage,
4695
4863
  error_type: "UnhandledRejection",
4696
4864
  error_stack_trace: errorStack,
@@ -4702,10 +4870,17 @@ var eventEmissionPlugin = ({ context }) => {
4702
4870
  promise: String(promise)
4703
4871
  }
4704
4872
  });
4873
+ try {
4874
+ const userContext = await getUserContext;
4875
+ errorEvent = { ...errorEvent, ...userContext };
4876
+ } catch {
4877
+ }
4705
4878
  try {
4706
4879
  await Promise.race([
4707
4880
  transport.emit(ERROR_OCCURRED_EVENT_SUBJECT, errorEvent),
4708
- new Promise((resolve2) => setTimeout(resolve2, 300))
4881
+ new Promise(
4882
+ (resolve2) => setTimeout(resolve2, TELEMETRY_EMIT_TIMEOUT_MS)
4883
+ )
4709
4884
  ]);
4710
4885
  } catch {
4711
4886
  }
@@ -4714,16 +4889,23 @@ var eventEmissionPlugin = ({ context }) => {
4714
4889
  const handleSignal = async (signal) => {
4715
4890
  shutdownStartTime = Date.now();
4716
4891
  const uptime = Date.now() - startupTime;
4717
- const signalEvent = buildApplicationLifecycleEvent({
4892
+ let signalEvent = buildApplicationLifecycleEvent({
4718
4893
  lifecycle_event_type: "signal_termination",
4719
4894
  signal_name: signal,
4720
4895
  uptime_ms: uptime,
4721
4896
  is_graceful_shutdown: true
4722
4897
  });
4898
+ try {
4899
+ const userContext = await getUserContext;
4900
+ signalEvent = { ...signalEvent, ...userContext };
4901
+ } catch {
4902
+ }
4723
4903
  try {
4724
4904
  await Promise.race([
4725
4905
  transport.emit(APPLICATION_LIFECYCLE_EVENT_SUBJECT, signalEvent),
4726
- new Promise((resolve2) => setTimeout(resolve2, 300))
4906
+ new Promise(
4907
+ (resolve2) => setTimeout(resolve2, TELEMETRY_EMIT_TIMEOUT_MS)
4908
+ )
4727
4909
  ]);
4728
4910
  } catch {
4729
4911
  }
@@ -4739,7 +4921,7 @@ var eventEmissionPlugin = ({ context }) => {
4739
4921
  transport,
4740
4922
  config,
4741
4923
  emit: (subject, event) => {
4742
- silentEmit(transport, subject, event);
4924
+ silentEmit(transport, subject, event, getUserContext);
4743
4925
  },
4744
4926
  createBaseEvent: createBaseEventHelper
4745
4927
  }
@@ -16,7 +16,7 @@ export interface EventEmissionContext {
16
16
  transport: EventTransport;
17
17
  config: EventEmissionConfig;
18
18
  emit<T extends any>(subject: string, event: T): void;
19
- createBaseEvent(): BaseEvent;
19
+ createBaseEvent(): Promise<BaseEvent>;
20
20
  };
21
21
  }
22
22
  export interface EventEmissionProvides {
@@ -1 +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;AA+ED,eAAO,MAAM,mBAAmB,EAAE,MAAM,CACtC,EAAE,EACF;IACE,OAAO,EAAE;QACP,aAAa,CAAC,EAAE,mBAAmB,CAAC;QACpC,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;CACH,EACD,qBAAqB,CAoMtB,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"}
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;AAenE,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,OAAO,CAAC,SAAS,CAAC,CAAC;KACvC,CAAC;CACH;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,oBAAoB,CAAC;CAC/B;AA4FD,eAAO,MAAM,mBAAmB,EAAE,MAAM,CACtC,EAAE,EACF;IACE,OAAO,EAAE;QACP,aAAa,CAAC,EAAE,mBAAmB,CAAC;QACpC,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;CACH,EACD,qBAAqB,CAyQtB,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"}
@@ -6,14 +6,17 @@
6
6
  */
7
7
  import { createTransport } from "./transport";
8
8
  import { generateEventId, getCurrentTimestamp, getReleaseId } from "./utils";
9
+ import { extractUserIdsFromJwt } from "../../api/auth";
9
10
  import { buildApplicationLifecycleEvent, buildErrorEventWithContext, } from "./builders";
10
11
  import { getTrackingBaseUrl } from "../../utils/url-utils";
12
+ // Maximum time to wait for telemetry emission before allowing process to exit
13
+ const TELEMETRY_EMIT_TIMEOUT_MS = 300;
11
14
  const APPLICATION_LIFECYCLE_EVENT_SUBJECT = "platform.sdk.ApplicationLifecycleEvent";
12
15
  const ERROR_OCCURRED_EVENT_SUBJECT = "platform.sdk.ErrorOccurredEvent";
13
16
  // Track transport success/failure so we only log failure once.
14
17
  const transportStates = new WeakMap();
15
18
  // Silent emission wrapper with smart first-failure logging
16
- async function silentEmit(transport, subject, event) {
19
+ async function silentEmit(transport, subject, event, userContextPromise) {
17
20
  try {
18
21
  // Get or initialize state for this transport
19
22
  let state = transportStates.get(transport);
@@ -21,9 +24,21 @@ async function silentEmit(transport, subject, event) {
21
24
  state = { hasWorked: false, hasLoggedFailure: false };
22
25
  transportStates.set(transport, state);
23
26
  }
27
+ // Resolve user context and merge into event
28
+ let enrichedEvent = event;
29
+ if (userContextPromise) {
30
+ try {
31
+ const userContext = await userContextPromise;
32
+ // Use Object.assign to safely merge user context into event
33
+ enrichedEvent = Object.assign({}, event, userContext);
34
+ }
35
+ catch {
36
+ // If user context promise fails, continue with original event
37
+ }
38
+ }
24
39
  // Fire and forget - don't await the transport
25
40
  transport
26
- .emit(subject, event)
41
+ .emit(subject, enrichedEvent)
27
42
  .then(() => {
28
43
  // Mark as working if any emit succeeds
29
44
  state.hasWorked = true;
@@ -76,6 +91,24 @@ export const eventEmissionPlugin = ({ context }) => {
76
91
  : // Otherwise, use option transport or default
77
92
  (context.options.eventEmission?.transport ?? defaultTransport),
78
93
  };
94
+ // Create getUserContext promise for dynamic user context injection
95
+ const getUserContext = (async () => {
96
+ try {
97
+ // Dynamically import the CLI login package if available
98
+ const { getToken } = await import("@zapier/zapier-sdk-cli-login");
99
+ // Pass baseUrl for potential token refresh operations
100
+ const token = await getToken({
101
+ baseUrl: context.options.baseUrl,
102
+ });
103
+ if (token) {
104
+ return extractUserIdsFromJwt(token);
105
+ }
106
+ }
107
+ catch {
108
+ // CLI login package not available or getToken failed, fall back to null context
109
+ }
110
+ return { customuser_id: null, account_id: null };
111
+ })();
79
112
  const startupTime = Date.now();
80
113
  let shutdownStartTime = null;
81
114
  // If disabled, return noop implementations
@@ -86,7 +119,7 @@ export const eventEmissionPlugin = ({ context }) => {
86
119
  transport: createTransport({ type: "noop" }),
87
120
  config,
88
121
  emit: () => { },
89
- createBaseEvent: () => ({
122
+ createBaseEvent: async () => ({
90
123
  event_id: generateEventId(),
91
124
  timestamp_ms: getCurrentTimestamp(),
92
125
  release_id: getReleaseId(),
@@ -109,23 +142,34 @@ export const eventEmissionPlugin = ({ context }) => {
109
142
  transport = createTransport({ type: "noop" });
110
143
  }
111
144
  // Helper to create base event
112
- const createBaseEventHelper = () => ({
113
- event_id: generateEventId(),
114
- timestamp_ms: getCurrentTimestamp(),
115
- release_id: getReleaseId(),
116
- customuser_id: null,
117
- account_id: null,
118
- identity_id: null,
119
- visitor_id: null,
120
- correlation_id: null,
121
- });
145
+ const createBaseEventHelper = async () => {
146
+ const baseEvent = {
147
+ event_id: generateEventId(),
148
+ timestamp_ms: getCurrentTimestamp(),
149
+ release_id: getReleaseId(),
150
+ customuser_id: null,
151
+ account_id: null,
152
+ identity_id: null,
153
+ visitor_id: null,
154
+ correlation_id: null,
155
+ };
156
+ // Enrich with user context if available
157
+ try {
158
+ const userContext = await getUserContext;
159
+ return { ...baseEvent, ...userContext };
160
+ }
161
+ catch {
162
+ // Return base event if user context fails
163
+ return baseEvent;
164
+ }
165
+ };
122
166
  // Register lifecycle event handlers if enabled
123
167
  if (config.enabled) {
124
168
  // Emit startup event
125
169
  const startupEvent = buildApplicationLifecycleEvent({
126
170
  lifecycle_event_type: "startup",
127
171
  });
128
- silentEmit(transport, APPLICATION_LIFECYCLE_EVENT_SUBJECT, startupEvent);
172
+ silentEmit(transport, APPLICATION_LIFECYCLE_EVENT_SUBJECT, startupEvent, getUserContext);
129
173
  // Register process event handlers (Node.js only)
130
174
  if (typeof process?.on === "function") {
131
175
  // Handle normal process exit
@@ -141,11 +185,11 @@ export const eventEmissionPlugin = ({ context }) => {
141
185
  is_graceful_shutdown: code === 0,
142
186
  shutdown_duration_ms: shutdownDuration,
143
187
  });
144
- silentEmit(transport, APPLICATION_LIFECYCLE_EVENT_SUBJECT, exitEvent);
188
+ silentEmit(transport, APPLICATION_LIFECYCLE_EVENT_SUBJECT, exitEvent, getUserContext);
145
189
  });
146
190
  // Handle uncaught exceptions
147
191
  process.on("uncaughtException", async (error) => {
148
- const errorEvent = buildErrorEventWithContext({
192
+ let errorEvent = buildErrorEventWithContext({
149
193
  error_message: error.message || "Unknown error",
150
194
  error_type: "UncaughtException",
151
195
  error_stack_trace: error.stack || null,
@@ -154,11 +198,19 @@ export const eventEmissionPlugin = ({ context }) => {
154
198
  is_recoverable: false,
155
199
  execution_start_time: startupTime,
156
200
  });
201
+ // Enrich with user context if available
202
+ try {
203
+ const userContext = await getUserContext;
204
+ errorEvent = { ...errorEvent, ...userContext };
205
+ }
206
+ catch {
207
+ // Continue with original event if user context fails
208
+ }
157
209
  // Wait up to 300ms for telemetry to send before allowing process to exit
158
210
  try {
159
211
  await Promise.race([
160
212
  transport.emit(ERROR_OCCURRED_EVENT_SUBJECT, errorEvent),
161
- new Promise((resolve) => setTimeout(resolve, 300)),
213
+ new Promise((resolve) => setTimeout(resolve, TELEMETRY_EMIT_TIMEOUT_MS)),
162
214
  ]);
163
215
  }
164
216
  catch {
@@ -174,7 +226,7 @@ export const eventEmissionPlugin = ({ context }) => {
174
226
  ? reason
175
227
  : "Unhandled promise rejection";
176
228
  const errorStack = reason instanceof Error ? reason.stack : null;
177
- const errorEvent = buildErrorEventWithContext({
229
+ let errorEvent = buildErrorEventWithContext({
178
230
  error_message: errorMessage,
179
231
  error_type: "UnhandledRejection",
180
232
  error_stack_trace: errorStack,
@@ -186,11 +238,19 @@ export const eventEmissionPlugin = ({ context }) => {
186
238
  promise: String(promise),
187
239
  },
188
240
  });
241
+ // Enrich with user context if available
242
+ try {
243
+ const userContext = await getUserContext;
244
+ errorEvent = { ...errorEvent, ...userContext };
245
+ }
246
+ catch {
247
+ // Continue with original event if user context fails
248
+ }
189
249
  // Wait up to 300ms for telemetry to send
190
250
  try {
191
251
  await Promise.race([
192
252
  transport.emit(ERROR_OCCURRED_EVENT_SUBJECT, errorEvent),
193
- new Promise((resolve) => setTimeout(resolve, 300)),
253
+ new Promise((resolve) => setTimeout(resolve, TELEMETRY_EMIT_TIMEOUT_MS)),
194
254
  ]);
195
255
  }
196
256
  catch {
@@ -201,17 +261,25 @@ export const eventEmissionPlugin = ({ context }) => {
201
261
  const handleSignal = async (signal) => {
202
262
  shutdownStartTime = Date.now();
203
263
  const uptime = Date.now() - startupTime;
204
- const signalEvent = buildApplicationLifecycleEvent({
264
+ let signalEvent = buildApplicationLifecycleEvent({
205
265
  lifecycle_event_type: "signal_termination",
206
266
  signal_name: signal,
207
267
  uptime_ms: uptime,
208
268
  is_graceful_shutdown: true,
209
269
  });
270
+ // Enrich with user context if available
271
+ try {
272
+ const userContext = await getUserContext;
273
+ signalEvent = { ...signalEvent, ...userContext };
274
+ }
275
+ catch {
276
+ // Continue with original event if user context fails
277
+ }
210
278
  // Wait up to 300ms for telemetry to send
211
279
  try {
212
280
  await Promise.race([
213
281
  transport.emit(APPLICATION_LIFECYCLE_EVENT_SUBJECT, signalEvent),
214
- new Promise((resolve) => setTimeout(resolve, 300)),
282
+ new Promise((resolve) => setTimeout(resolve, TELEMETRY_EMIT_TIMEOUT_MS)),
215
283
  ]);
216
284
  }
217
285
  catch {
@@ -231,7 +299,7 @@ export const eventEmissionPlugin = ({ context }) => {
231
299
  transport,
232
300
  config,
233
301
  emit: (subject, event) => {
234
- silentEmit(transport, subject, event);
302
+ silentEmit(transport, subject, event, getUserContext);
235
303
  },
236
304
  createBaseEvent: createBaseEventHelper,
237
305
  },