@keystrokehq/segment 0.0.1
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/README.md +232 -0
- package/dist/_official/index.d.mts +2 -0
- package/dist/_official/index.mjs +3 -0
- package/dist/_runtime/index.d.mts +1 -0
- package/dist/_runtime/index.mjs +1 -0
- package/dist/audiences.d.mts +282 -0
- package/dist/audiences.mjs +205 -0
- package/dist/client.d.mts +90 -0
- package/dist/client.mjs +337 -0
- package/dist/common-CdGiJbjq.mjs +56 -0
- package/dist/computed-traits.d.mts +215 -0
- package/dist/computed-traits.mjs +149 -0
- package/dist/connection.d.mts +2 -0
- package/dist/connection.mjs +3 -0
- package/dist/crud-SWa_79Hg.mjs +140 -0
- package/dist/deletion-suppression.d.mts +191 -0
- package/dist/deletion-suppression.mjs +103 -0
- package/dist/delivery-overview.d.mts +79 -0
- package/dist/delivery-overview.mjs +54 -0
- package/dist/destination-filters.d.mts +190 -0
- package/dist/destination-filters.mjs +115 -0
- package/dist/destinations.d.mts +473 -0
- package/dist/destinations.mjs +257 -0
- package/dist/edge-functions.d.mts +168 -0
- package/dist/edge-functions.mjs +72 -0
- package/dist/errors-4FGnrowW.mjs +27 -0
- package/dist/events-catalog.d.mts +111 -0
- package/dist/events-catalog.mjs +65 -0
- package/dist/events.d.mts +167 -0
- package/dist/events.mjs +134 -0
- package/dist/factory-CvfKfzMq.mjs +8 -0
- package/dist/functions.d.mts +347 -0
- package/dist/functions.mjs +180 -0
- package/dist/iam-groups.d.mts +284 -0
- package/dist/iam-groups.mjs +144 -0
- package/dist/iam-users.d.mts +205 -0
- package/dist/iam-users.mjs +91 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.mjs +1 -0
- package/dist/integration-B5zYasBZ.mjs +25 -0
- package/dist/integration-D2yRaKFR.d.mts +67 -0
- package/dist/labels.d.mts +100 -0
- package/dist/labels.mjs +58 -0
- package/dist/monitoring.d.mts +182 -0
- package/dist/monitoring.mjs +88 -0
- package/dist/profiles-sync.d.mts +127 -0
- package/dist/profiles-sync.mjs +89 -0
- package/dist/profiles.d.mts +197 -0
- package/dist/profiles.mjs +137 -0
- package/dist/reverse-etl.d.mts +448 -0
- package/dist/reverse-etl.mjs +228 -0
- package/dist/roles.d.mts +48 -0
- package/dist/roles.mjs +25 -0
- package/dist/schemas/index.d.mts +57 -0
- package/dist/schemas/index.mjs +3 -0
- package/dist/sources.d.mts +560 -0
- package/dist/sources.mjs +259 -0
- package/dist/tracking-plans.d.mts +351 -0
- package/dist/tracking-plans.mjs +160 -0
- package/dist/tracking.d.mts +499 -0
- package/dist/tracking.mjs +255 -0
- package/dist/transformations.d.mts +188 -0
- package/dist/transformations.mjs +83 -0
- package/dist/triggers.d.mts +54 -0
- package/dist/triggers.mjs +341 -0
- package/dist/usage.d.mts +70 -0
- package/dist/usage.mjs +55 -0
- package/dist/verification.d.mts +17 -0
- package/dist/verification.mjs +51 -0
- package/dist/warehouses.d.mts +341 -0
- package/dist/warehouses.mjs +176 -0
- package/dist/workspaces.d.mts +95 -0
- package/dist/workspaces.mjs +67 -0
- package/package.json +188 -0
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import { t as segment } from "./integration-B5zYasBZ.mjs";
|
|
2
|
+
import { createSegmentClient } from "./client.mjs";
|
|
3
|
+
import { segmentAudienceEventSchema, segmentAudienceSyncEventSchema, segmentAudienceWebhookEnvelopeSchema, segmentComputedTraitEventSchema, segmentDeletionRegulationEventSchema, segmentDestinationCreatedEventSchema, segmentFunctionVersionEventSchema, segmentMonitoringAlertEnvelopeSchema, segmentMonitoringEventSchema, segmentReverseEtlRunEventSchema, segmentSourceCreatedEventSchema, segmentTrackingPlanViolationEventSchema } from "./events.mjs";
|
|
4
|
+
import { verifySegmentWebhookFromRequest } from "./verification.mjs";
|
|
5
|
+
import { createPollingTriggerBindingFactory, createWebhookTriggerBindingFactory } from "@keystrokehq/integration-authoring";
|
|
6
|
+
|
|
7
|
+
//#region src/triggers.ts
|
|
8
|
+
function isAudienceEntered(envelope) {
|
|
9
|
+
const type = typeof envelope.type === "string" ? envelope.type.toLowerCase() : "";
|
|
10
|
+
const properties = envelope.properties;
|
|
11
|
+
const propsType = typeof properties?.type === "string" ? properties.type.toLowerCase() : "";
|
|
12
|
+
if (!audienceKeyFromEnvelope(envelope)) return false;
|
|
13
|
+
return type === "entered" || propsType === "entered" || audienceValueFromEnvelope(envelope) === true;
|
|
14
|
+
}
|
|
15
|
+
function isAudienceExited(envelope) {
|
|
16
|
+
const type = typeof envelope.type === "string" ? envelope.type.toLowerCase() : "";
|
|
17
|
+
const properties = envelope.properties;
|
|
18
|
+
const propsType = typeof properties?.type === "string" ? properties.type.toLowerCase() : "";
|
|
19
|
+
if (!audienceKeyFromEnvelope(envelope)) return false;
|
|
20
|
+
return type === "exited" || propsType === "exited" || audienceValueFromEnvelope(envelope) === false;
|
|
21
|
+
}
|
|
22
|
+
function audienceKeyFromEnvelope(envelope) {
|
|
23
|
+
const props = envelope.properties;
|
|
24
|
+
if (props && typeof props.audience_key === "string") return props.audience_key;
|
|
25
|
+
if (props && typeof props.audienceKey === "string") return props.audienceKey;
|
|
26
|
+
if (typeof envelope.event === "string" && envelope.event.length > 0) return envelope.event;
|
|
27
|
+
}
|
|
28
|
+
function audienceValueFromEnvelope(envelope) {
|
|
29
|
+
const props = envelope.properties;
|
|
30
|
+
if (!props) return void 0;
|
|
31
|
+
const key = audienceKeyFromEnvelope(envelope);
|
|
32
|
+
if (!key) return void 0;
|
|
33
|
+
const value = props[key];
|
|
34
|
+
return typeof value === "boolean" ? value : void 0;
|
|
35
|
+
}
|
|
36
|
+
function mapAudienceEvent(envelope, type) {
|
|
37
|
+
const audienceKey = audienceKeyFromEnvelope(envelope);
|
|
38
|
+
if (!audienceKey) return null;
|
|
39
|
+
return segmentAudienceEventSchema.parse({
|
|
40
|
+
type,
|
|
41
|
+
audienceKey,
|
|
42
|
+
...envelope.userId !== void 0 ? { userId: envelope.userId } : {},
|
|
43
|
+
...envelope.anonymousId !== void 0 ? { anonymousId: envelope.anonymousId } : {},
|
|
44
|
+
...envelope.userId !== void 0 ? { profileId: envelope.userId } : {},
|
|
45
|
+
...envelope.traits !== void 0 ? { traits: envelope.traits } : {},
|
|
46
|
+
...envelope.properties !== void 0 ? { properties: envelope.properties } : {},
|
|
47
|
+
...envelope.messageId !== void 0 ? { messageId: envelope.messageId } : {},
|
|
48
|
+
...envelope.timestamp !== void 0 ? { timestamp: envelope.timestamp } : {},
|
|
49
|
+
raw: envelope
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function mapComputedTraitEvent(envelope) {
|
|
53
|
+
const key = audienceKeyFromEnvelope(envelope);
|
|
54
|
+
if (!key) return null;
|
|
55
|
+
const props = envelope.properties;
|
|
56
|
+
const value = props ? props[key] : void 0;
|
|
57
|
+
return segmentComputedTraitEventSchema.parse({
|
|
58
|
+
traitKey: key,
|
|
59
|
+
value,
|
|
60
|
+
...envelope.userId !== void 0 ? { userId: envelope.userId } : {},
|
|
61
|
+
...envelope.userId !== void 0 ? { profileId: envelope.userId } : {},
|
|
62
|
+
...envelope.messageId !== void 0 ? { messageId: envelope.messageId } : {},
|
|
63
|
+
...envelope.timestamp !== void 0 ? { timestamp: envelope.timestamp } : {},
|
|
64
|
+
raw: envelope
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
function mapMonitoringEvent(envelope) {
|
|
68
|
+
if (!envelope.id) return null;
|
|
69
|
+
return segmentMonitoringEventSchema.parse({
|
|
70
|
+
alertId: envelope.id,
|
|
71
|
+
eventType: envelope.eventType ?? "unknown",
|
|
72
|
+
...envelope.severity !== void 0 ? { severity: envelope.severity } : {},
|
|
73
|
+
...envelope.firedAt !== void 0 ? { firedAt: envelope.firedAt } : {},
|
|
74
|
+
...envelope.sourceId !== void 0 ? { sourceId: envelope.sourceId } : {},
|
|
75
|
+
...envelope.destinationId !== void 0 ? { destinationId: envelope.destinationId } : {},
|
|
76
|
+
...envelope.functionId !== void 0 ? { functionId: envelope.functionId } : {},
|
|
77
|
+
...envelope.message !== void 0 ? { message: envelope.message } : {},
|
|
78
|
+
raw: envelope
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
function verifyAudienceRequest(request, credentials) {
|
|
82
|
+
const secret = credentials.SEGMENT_WEBHOOK_SHARED_SECRET;
|
|
83
|
+
if (!secret) throw new Error("Segment webhook triggers require SEGMENT_WEBHOOK_SHARED_SECRET. Set it on the connection and configure the same shared secret on the Segment-side Webhook destination.");
|
|
84
|
+
if (!verifySegmentWebhookFromRequest(request, secret)) throw new Error("Invalid Segment webhook signature.");
|
|
85
|
+
}
|
|
86
|
+
function buildWebhookBinding(config) {
|
|
87
|
+
return createWebhookTriggerBindingFactory({
|
|
88
|
+
defaultName: config.defaultName,
|
|
89
|
+
defaultDescription: config.defaultDescription,
|
|
90
|
+
path: "/segment",
|
|
91
|
+
method: "POST",
|
|
92
|
+
payload: config.envelope,
|
|
93
|
+
credentialSets: [segment],
|
|
94
|
+
verify: (request, ctx) => {
|
|
95
|
+
const credentials = ctx.credentials.segment;
|
|
96
|
+
verifyAudienceRequest(request, credentials);
|
|
97
|
+
},
|
|
98
|
+
definitionFilter: config.filter,
|
|
99
|
+
mapPayload: config.mapPayload,
|
|
100
|
+
idempotencyKey: config.idempotencyKey,
|
|
101
|
+
response: { successStatus: 200 }
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
const audienceEnteredBinding = buildWebhookBinding({
|
|
105
|
+
defaultName: "Segment audience entered",
|
|
106
|
+
defaultDescription: "A profile entered a Segment Engage audience.",
|
|
107
|
+
envelope: segmentAudienceWebhookEnvelopeSchema,
|
|
108
|
+
filter: isAudienceEntered,
|
|
109
|
+
mapPayload: (envelope) => mapAudienceEvent(envelope, "entered"),
|
|
110
|
+
idempotencyKey: (env, payload) => payload?.messageId ?? `${payload?.audienceKey ?? env.event ?? "unknown"}:${payload?.userId ?? payload?.profileId ?? env.userId ?? "anon"}:${payload?.timestamp ?? env.timestamp ?? ""}:entered`
|
|
111
|
+
});
|
|
112
|
+
const audienceExitedBinding = buildWebhookBinding({
|
|
113
|
+
defaultName: "Segment audience exited",
|
|
114
|
+
defaultDescription: "A profile exited a Segment Engage audience.",
|
|
115
|
+
envelope: segmentAudienceWebhookEnvelopeSchema,
|
|
116
|
+
filter: isAudienceExited,
|
|
117
|
+
mapPayload: (envelope) => mapAudienceEvent(envelope, "exited"),
|
|
118
|
+
idempotencyKey: (env, payload) => payload?.messageId ?? `${payload?.audienceKey ?? env.event ?? "unknown"}:${payload?.userId ?? payload?.profileId ?? env.userId ?? "anon"}:${payload?.timestamp ?? env.timestamp ?? ""}:exited`
|
|
119
|
+
});
|
|
120
|
+
const computedTraitsUpdatedBinding = buildWebhookBinding({
|
|
121
|
+
defaultName: "Segment computed trait updated",
|
|
122
|
+
defaultDescription: "A profile computed trait value changed.",
|
|
123
|
+
envelope: segmentAudienceWebhookEnvelopeSchema,
|
|
124
|
+
filter: (envelope) => Boolean(audienceKeyFromEnvelope(envelope)),
|
|
125
|
+
mapPayload: mapComputedTraitEvent,
|
|
126
|
+
idempotencyKey: (env, payload) => payload?.messageId ?? `${payload?.traitKey ?? env.event ?? "unknown"}:${payload?.userId ?? payload?.profileId ?? env.userId ?? "anon"}:${payload?.timestamp ?? env.timestamp ?? ""}`
|
|
127
|
+
});
|
|
128
|
+
const functionsInvocationFailedBinding = buildWebhookBinding({
|
|
129
|
+
defaultName: "Segment function invocation failed",
|
|
130
|
+
defaultDescription: "A Segment Function invocation failure alert fired.",
|
|
131
|
+
envelope: segmentMonitoringAlertEnvelopeSchema,
|
|
132
|
+
filter: (envelope) => typeof envelope.eventType === "string" && envelope.eventType.toLowerCase().includes("function"),
|
|
133
|
+
mapPayload: mapMonitoringEvent,
|
|
134
|
+
idempotencyKey: (env, payload) => `${payload?.alertId ?? env.id ?? "unknown"}:${payload?.firedAt ?? env.firedAt ?? ""}`
|
|
135
|
+
});
|
|
136
|
+
const deliveryFailureSpikeBinding = buildWebhookBinding({
|
|
137
|
+
defaultName: "Segment delivery failure spike",
|
|
138
|
+
defaultDescription: "A destination delivery failure rate exceeded threshold.",
|
|
139
|
+
envelope: segmentMonitoringAlertEnvelopeSchema,
|
|
140
|
+
filter: (envelope) => typeof envelope.eventType === "string" && envelope.eventType.toLowerCase().includes("delivery"),
|
|
141
|
+
mapPayload: mapMonitoringEvent,
|
|
142
|
+
idempotencyKey: (env, payload) => `${payload?.alertId ?? env.id ?? "unknown"}:${payload?.firedAt ?? env.firedAt ?? ""}`
|
|
143
|
+
});
|
|
144
|
+
const webhooks = Object.freeze({
|
|
145
|
+
audienceEntered: (options) => audienceEnteredBinding(options),
|
|
146
|
+
audienceExited: (options) => audienceExitedBinding(options),
|
|
147
|
+
computedTraitsUpdated: (options) => computedTraitsUpdatedBinding(options),
|
|
148
|
+
functionsInvocationFailed: (options) => functionsInvocationFailedBinding(options),
|
|
149
|
+
deliveryFailureSpike: (options) => deliveryFailureSpikeBinding(options)
|
|
150
|
+
});
|
|
151
|
+
function buildPollingBinding(definition, options) {
|
|
152
|
+
const schedule = options?.schedule ?? definition.defaultSchedule;
|
|
153
|
+
return createPollingTriggerBindingFactory({
|
|
154
|
+
defaultName: definition.defaultName,
|
|
155
|
+
defaultDescription: definition.defaultDescription,
|
|
156
|
+
schedule,
|
|
157
|
+
credentialSets: [segment],
|
|
158
|
+
response: definition.eventSchema,
|
|
159
|
+
poll: async (ctx) => {
|
|
160
|
+
const credentials = ctx.credentials.segment;
|
|
161
|
+
return definition.poll(credentials, ctx.lastPolledAt);
|
|
162
|
+
},
|
|
163
|
+
idempotencyKey: (event) => definition.idempotencyKey(event)
|
|
164
|
+
})({
|
|
165
|
+
...options?.name ? { name: options.name } : {},
|
|
166
|
+
...options?.description ? { description: options.description } : {},
|
|
167
|
+
...options?.filter ? { filter: options.filter } : {},
|
|
168
|
+
...options?.transform ? { transform: options.transform } : {}
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
function audienceSyncCompleted(params, options) {
|
|
172
|
+
return buildPollingBinding({
|
|
173
|
+
defaultName: "Segment audience sync completed",
|
|
174
|
+
defaultDescription: `Poll /spaces/${params.spaceId}/audiences/${params.audienceId}/syncs and emit the most recent completed sync.`,
|
|
175
|
+
defaultSchedule: "5m",
|
|
176
|
+
eventSchema: segmentAudienceSyncEventSchema,
|
|
177
|
+
poll: async (credentials) => {
|
|
178
|
+
const rec = await createSegmentClient(credentials).publicApi.request(`/spaces/${encodeURIComponent(params.spaceId)}/audiences/${encodeURIComponent(params.audienceId)}/syncs`);
|
|
179
|
+
const obj = (rec.data?.syncs ?? rec.syncs ?? [])[0] ?? {};
|
|
180
|
+
return segmentAudienceSyncEventSchema.parse({
|
|
181
|
+
syncId: obj.id ?? "unknown",
|
|
182
|
+
audienceId: params.audienceId,
|
|
183
|
+
spaceId: params.spaceId,
|
|
184
|
+
status: obj.status ?? "success",
|
|
185
|
+
...typeof obj.finishedAt === "string" ? { finishedAt: obj.finishedAt } : {},
|
|
186
|
+
raw: obj
|
|
187
|
+
});
|
|
188
|
+
},
|
|
189
|
+
idempotencyKey: (event) => `${event.audienceId}:${event.syncId}:${event.finishedAt ?? ""}`
|
|
190
|
+
}, options);
|
|
191
|
+
}
|
|
192
|
+
function reverseEtlRunPollDefinition(params, status, label) {
|
|
193
|
+
return {
|
|
194
|
+
defaultName: `Segment reverse-ETL run ${label}`,
|
|
195
|
+
defaultDescription: `Poll /reverse-etl-models/${params.modelId}/runs and emit the latest run that is ${status}.`,
|
|
196
|
+
defaultSchedule: "5m",
|
|
197
|
+
eventSchema: segmentReverseEtlRunEventSchema,
|
|
198
|
+
poll: async (credentials) => {
|
|
199
|
+
const rec = await createSegmentClient(credentials).publicApi.request(`/reverse-etl-models/${encodeURIComponent(params.modelId)}/runs`);
|
|
200
|
+
const obj = (rec.data?.runs ?? rec.runs ?? []).find((run) => {
|
|
201
|
+
if (!run || typeof run !== "object") return false;
|
|
202
|
+
return run.status === status;
|
|
203
|
+
}) ?? {};
|
|
204
|
+
return segmentReverseEtlRunEventSchema.parse({
|
|
205
|
+
runId: obj.id ?? "unknown",
|
|
206
|
+
modelId: params.modelId,
|
|
207
|
+
status,
|
|
208
|
+
...typeof obj.startedAt === "string" ? { startedAt: obj.startedAt } : {},
|
|
209
|
+
...typeof obj.finishedAt === "string" ? { finishedAt: obj.finishedAt } : {},
|
|
210
|
+
...typeof obj.error === "string" ? { error: obj.error } : {},
|
|
211
|
+
raw: obj
|
|
212
|
+
});
|
|
213
|
+
},
|
|
214
|
+
idempotencyKey: (event) => `${event.modelId}:${event.runId}:${status}:${event.finishedAt ?? event.startedAt ?? ""}`
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
function reverseEtlRunSucceeded(params, options) {
|
|
218
|
+
return buildPollingBinding(reverseEtlRunPollDefinition(params, "SUCCEEDED", "succeeded"), options);
|
|
219
|
+
}
|
|
220
|
+
function reverseEtlRunFailed(params, options) {
|
|
221
|
+
return buildPollingBinding(reverseEtlRunPollDefinition(params, "FAILED", "failed"), options);
|
|
222
|
+
}
|
|
223
|
+
function reverseEtlRunStarted(params, options) {
|
|
224
|
+
return buildPollingBinding(reverseEtlRunPollDefinition(params, "IN_PROGRESS", "started"), options);
|
|
225
|
+
}
|
|
226
|
+
function functionsDeploymentCompleted(params, options) {
|
|
227
|
+
return buildPollingBinding({
|
|
228
|
+
defaultName: "Segment function deployment completed",
|
|
229
|
+
defaultDescription: `Poll /functions/${params.functionId}/versions and emit the latest version.`,
|
|
230
|
+
defaultSchedule: "5m",
|
|
231
|
+
eventSchema: segmentFunctionVersionEventSchema,
|
|
232
|
+
poll: async (credentials) => {
|
|
233
|
+
const rec = await createSegmentClient(credentials).publicApi.request(`/functions/${encodeURIComponent(params.functionId)}/versions`);
|
|
234
|
+
const obj = (rec.data?.versions ?? rec.versions ?? [])[0] ?? {};
|
|
235
|
+
return segmentFunctionVersionEventSchema.parse({
|
|
236
|
+
functionId: params.functionId,
|
|
237
|
+
versionId: obj.id ?? "unknown",
|
|
238
|
+
...typeof obj.createdAt === "string" ? { createdAt: obj.createdAt } : {},
|
|
239
|
+
raw: obj
|
|
240
|
+
});
|
|
241
|
+
},
|
|
242
|
+
idempotencyKey: (event) => `${event.functionId}:${event.versionId}`
|
|
243
|
+
}, options);
|
|
244
|
+
}
|
|
245
|
+
function trackingPlanViolationDetected(options) {
|
|
246
|
+
return buildPollingBinding({
|
|
247
|
+
defaultName: "Segment tracking plan violation detected",
|
|
248
|
+
defaultDescription: "Poll /events/violations and emit the most recent violation.",
|
|
249
|
+
defaultSchedule: "10m",
|
|
250
|
+
eventSchema: segmentTrackingPlanViolationEventSchema,
|
|
251
|
+
poll: async (credentials) => {
|
|
252
|
+
const rec = await createSegmentClient(credentials).publicApi.request("/events/violations");
|
|
253
|
+
const obj = (rec.data?.violations ?? rec.violations ?? [])[0] ?? {};
|
|
254
|
+
return segmentTrackingPlanViolationEventSchema.parse({
|
|
255
|
+
violationId: obj.id ?? "unknown",
|
|
256
|
+
...typeof obj.eventName === "string" ? { eventName: obj.eventName } : {},
|
|
257
|
+
...typeof obj.trackingPlanId === "string" ? { trackingPlanId: obj.trackingPlanId } : {},
|
|
258
|
+
...typeof obj.sourceId === "string" ? { sourceId: obj.sourceId } : {},
|
|
259
|
+
...typeof obj.messageId === "string" ? { messageId: obj.messageId } : {},
|
|
260
|
+
raw: obj
|
|
261
|
+
});
|
|
262
|
+
},
|
|
263
|
+
idempotencyKey: (event) => event.violationId
|
|
264
|
+
}, options);
|
|
265
|
+
}
|
|
266
|
+
function sourceCreated(options) {
|
|
267
|
+
return buildPollingBinding({
|
|
268
|
+
defaultName: "Segment source created",
|
|
269
|
+
defaultDescription: "Poll /sources and emit the most recent source.",
|
|
270
|
+
defaultSchedule: "10m",
|
|
271
|
+
eventSchema: segmentSourceCreatedEventSchema,
|
|
272
|
+
poll: async (credentials) => {
|
|
273
|
+
const rec = await createSegmentClient(credentials).publicApi.request("/sources");
|
|
274
|
+
const obj = (rec.data?.sources ?? rec.sources ?? [])[0] ?? {};
|
|
275
|
+
return segmentSourceCreatedEventSchema.parse({
|
|
276
|
+
sourceId: obj.id ?? "unknown",
|
|
277
|
+
...typeof obj.name === "string" ? { name: obj.name } : {},
|
|
278
|
+
...typeof obj.createdAt === "string" ? { createdAt: obj.createdAt } : {},
|
|
279
|
+
raw: obj
|
|
280
|
+
});
|
|
281
|
+
},
|
|
282
|
+
idempotencyKey: (event) => event.sourceId
|
|
283
|
+
}, options);
|
|
284
|
+
}
|
|
285
|
+
function destinationCreated(options) {
|
|
286
|
+
return buildPollingBinding({
|
|
287
|
+
defaultName: "Segment destination created",
|
|
288
|
+
defaultDescription: "Poll /destinations and emit the most recent destination.",
|
|
289
|
+
defaultSchedule: "10m",
|
|
290
|
+
eventSchema: segmentDestinationCreatedEventSchema,
|
|
291
|
+
poll: async (credentials) => {
|
|
292
|
+
const rec = await createSegmentClient(credentials).publicApi.request("/destinations");
|
|
293
|
+
const obj = (rec.data?.destinations ?? rec.destinations ?? [])[0] ?? {};
|
|
294
|
+
return segmentDestinationCreatedEventSchema.parse({
|
|
295
|
+
destinationId: obj.id ?? "unknown",
|
|
296
|
+
...typeof obj.name === "string" ? { name: obj.name } : {},
|
|
297
|
+
...typeof obj.sourceId === "string" ? { sourceId: obj.sourceId } : {},
|
|
298
|
+
...typeof obj.createdAt === "string" ? { createdAt: obj.createdAt } : {},
|
|
299
|
+
raw: obj
|
|
300
|
+
});
|
|
301
|
+
},
|
|
302
|
+
idempotencyKey: (event) => event.destinationId
|
|
303
|
+
}, options);
|
|
304
|
+
}
|
|
305
|
+
function deletionRegulationCompleted(options) {
|
|
306
|
+
return buildPollingBinding({
|
|
307
|
+
defaultName: "Segment deletion regulation completed",
|
|
308
|
+
defaultDescription: "Poll /workspace/regulations for finished GDPR regulations.",
|
|
309
|
+
defaultSchedule: "15m",
|
|
310
|
+
eventSchema: segmentDeletionRegulationEventSchema,
|
|
311
|
+
poll: async (credentials) => {
|
|
312
|
+
const rec = await createSegmentClient(credentials).publicApi.request("/workspace/regulations");
|
|
313
|
+
const obj = (rec.data?.regulations ?? rec.regulations ?? []).find((r) => {
|
|
314
|
+
if (!r || typeof r !== "object") return false;
|
|
315
|
+
const record = r;
|
|
316
|
+
return typeof record.finishedAt === "string" && record.finishedAt.length > 0;
|
|
317
|
+
}) ?? {};
|
|
318
|
+
return segmentDeletionRegulationEventSchema.parse({
|
|
319
|
+
regulationId: obj.id ?? "unknown",
|
|
320
|
+
...typeof obj.regulationType === "string" ? { regulationType: obj.regulationType } : {},
|
|
321
|
+
...typeof obj.finishedAt === "string" ? { finishedAt: obj.finishedAt } : {},
|
|
322
|
+
raw: obj
|
|
323
|
+
});
|
|
324
|
+
},
|
|
325
|
+
idempotencyKey: (event) => event.regulationId
|
|
326
|
+
}, options);
|
|
327
|
+
}
|
|
328
|
+
const polling = Object.freeze({
|
|
329
|
+
audienceSyncCompleted,
|
|
330
|
+
reverseEtlRunSucceeded,
|
|
331
|
+
reverseEtlRunFailed,
|
|
332
|
+
reverseEtlRunStarted,
|
|
333
|
+
functionsDeploymentCompleted,
|
|
334
|
+
trackingPlanViolationDetected,
|
|
335
|
+
sourceCreated,
|
|
336
|
+
destinationCreated,
|
|
337
|
+
deletionRegulationCompleted
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
//#endregion
|
|
341
|
+
export { audienceSyncCompleted, deletionRegulationCompleted, destinationCreated, functionsDeploymentCompleted, polling, reverseEtlRunFailed, reverseEtlRunStarted, reverseEtlRunSucceeded, sourceCreated, trackingPlanViolationDetected, webhooks };
|
package/dist/usage.d.mts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import * as _keystrokehq_core_credential_set0 from "@keystrokehq/core/credential-set";
|
|
3
|
+
import * as _keystrokehq_core0 from "@keystrokehq/core";
|
|
4
|
+
|
|
5
|
+
//#region src/usage.d.ts
|
|
6
|
+
declare const getDailyPerSourceUsage: _keystrokehq_core0.Operation<z.ZodObject<{
|
|
7
|
+
startDate: z.ZodOptional<z.ZodString>;
|
|
8
|
+
endDate: z.ZodOptional<z.ZodString>;
|
|
9
|
+
sourceId: z.ZodOptional<z.ZodString>;
|
|
10
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
11
|
+
items: z.ZodArray<z.ZodObject<{
|
|
12
|
+
date: z.ZodOptional<z.ZodString>;
|
|
13
|
+
sourceId: z.ZodOptional<z.ZodString>;
|
|
14
|
+
callCount: z.ZodOptional<z.ZodNumber>;
|
|
15
|
+
}, z.core.$catchall<z.ZodUnknown>>>;
|
|
16
|
+
}, z.core.$strip>, readonly [_keystrokehq_core0.CredentialSet<"segment", z.ZodObject<{
|
|
17
|
+
SEGMENT_PUBLIC_API_TOKEN: z.ZodString;
|
|
18
|
+
SEGMENT_REGION: z.ZodDefault<z.ZodEnum<{
|
|
19
|
+
us: "us";
|
|
20
|
+
eu: "eu";
|
|
21
|
+
}>>;
|
|
22
|
+
SEGMENT_DEFAULT_WRITE_KEY: z.ZodOptional<z.ZodString>;
|
|
23
|
+
SEGMENT_WRITE_KEYS_JSON: z.ZodOptional<z.ZodString>;
|
|
24
|
+
SEGMENT_PROFILE_API_TOKEN: z.ZodOptional<z.ZodString>;
|
|
25
|
+
SEGMENT_PROFILE_SPACE_ID: z.ZodOptional<z.ZodString>;
|
|
26
|
+
SEGMENT_WEBHOOK_SHARED_SECRET: z.ZodOptional<z.ZodString>;
|
|
27
|
+
}, z.core.$strip>, readonly _keystrokehq_core_credential_set0.CredentialConnection<z.ZodObject<{
|
|
28
|
+
SEGMENT_PUBLIC_API_TOKEN: z.ZodString;
|
|
29
|
+
SEGMENT_REGION: z.ZodDefault<z.ZodEnum<{
|
|
30
|
+
us: "us";
|
|
31
|
+
eu: "eu";
|
|
32
|
+
}>>;
|
|
33
|
+
SEGMENT_DEFAULT_WRITE_KEY: z.ZodOptional<z.ZodString>;
|
|
34
|
+
SEGMENT_WRITE_KEYS_JSON: z.ZodOptional<z.ZodString>;
|
|
35
|
+
SEGMENT_PROFILE_API_TOKEN: z.ZodOptional<z.ZodString>;
|
|
36
|
+
SEGMENT_PROFILE_SPACE_ID: z.ZodOptional<z.ZodString>;
|
|
37
|
+
SEGMENT_WEBHOOK_SHARED_SECRET: z.ZodOptional<z.ZodString>;
|
|
38
|
+
}, z.core.$strip>>[] | undefined>], undefined>;
|
|
39
|
+
declare const getMonthlyWorkspaceUsage: _keystrokehq_core0.Operation<z.ZodObject<{
|
|
40
|
+
month: z.ZodOptional<z.ZodString>;
|
|
41
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
42
|
+
month: z.ZodOptional<z.ZodString>;
|
|
43
|
+
mtu: z.ZodOptional<z.ZodNumber>;
|
|
44
|
+
events: z.ZodOptional<z.ZodNumber>;
|
|
45
|
+
breakdowns: z.ZodOptional<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
46
|
+
}, z.core.$catchall<z.ZodUnknown>>, readonly [_keystrokehq_core0.CredentialSet<"segment", z.ZodObject<{
|
|
47
|
+
SEGMENT_PUBLIC_API_TOKEN: z.ZodString;
|
|
48
|
+
SEGMENT_REGION: z.ZodDefault<z.ZodEnum<{
|
|
49
|
+
us: "us";
|
|
50
|
+
eu: "eu";
|
|
51
|
+
}>>;
|
|
52
|
+
SEGMENT_DEFAULT_WRITE_KEY: z.ZodOptional<z.ZodString>;
|
|
53
|
+
SEGMENT_WRITE_KEYS_JSON: z.ZodOptional<z.ZodString>;
|
|
54
|
+
SEGMENT_PROFILE_API_TOKEN: z.ZodOptional<z.ZodString>;
|
|
55
|
+
SEGMENT_PROFILE_SPACE_ID: z.ZodOptional<z.ZodString>;
|
|
56
|
+
SEGMENT_WEBHOOK_SHARED_SECRET: z.ZodOptional<z.ZodString>;
|
|
57
|
+
}, z.core.$strip>, readonly _keystrokehq_core_credential_set0.CredentialConnection<z.ZodObject<{
|
|
58
|
+
SEGMENT_PUBLIC_API_TOKEN: z.ZodString;
|
|
59
|
+
SEGMENT_REGION: z.ZodDefault<z.ZodEnum<{
|
|
60
|
+
us: "us";
|
|
61
|
+
eu: "eu";
|
|
62
|
+
}>>;
|
|
63
|
+
SEGMENT_DEFAULT_WRITE_KEY: z.ZodOptional<z.ZodString>;
|
|
64
|
+
SEGMENT_WRITE_KEYS_JSON: z.ZodOptional<z.ZodString>;
|
|
65
|
+
SEGMENT_PROFILE_API_TOKEN: z.ZodOptional<z.ZodString>;
|
|
66
|
+
SEGMENT_PROFILE_SPACE_ID: z.ZodOptional<z.ZodString>;
|
|
67
|
+
SEGMENT_WEBHOOK_SHARED_SECRET: z.ZodOptional<z.ZodString>;
|
|
68
|
+
}, z.core.$strip>>[] | undefined>], undefined>;
|
|
69
|
+
//#endregion
|
|
70
|
+
export { getDailyPerSourceUsage, getMonthlyWorkspaceUsage };
|
package/dist/usage.mjs
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { createSegmentClient } from "./client.mjs";
|
|
2
|
+
import { a as segmentLooseObjectSchema } from "./common-CdGiJbjq.mjs";
|
|
3
|
+
import { t as segmentOperation } from "./factory-CvfKfzMq.mjs";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
|
|
6
|
+
//#region src/usage.ts
|
|
7
|
+
/**
|
|
8
|
+
* segment/usage.ts — API call usage per source + monthly workspace usage.
|
|
9
|
+
* 2 actions (PLAN § 6.22).
|
|
10
|
+
*/
|
|
11
|
+
const dailyEntrySchema = segmentLooseObjectSchema({
|
|
12
|
+
date: z.string().optional(),
|
|
13
|
+
sourceId: z.string().optional(),
|
|
14
|
+
callCount: z.number().optional()
|
|
15
|
+
});
|
|
16
|
+
const monthlyWorkspaceSchema = segmentLooseObjectSchema({
|
|
17
|
+
month: z.string().optional(),
|
|
18
|
+
mtu: z.number().optional(),
|
|
19
|
+
events: z.number().optional(),
|
|
20
|
+
breakdowns: z.array(z.record(z.string(), z.unknown())).optional()
|
|
21
|
+
});
|
|
22
|
+
const getDailyPerSourceUsage = segmentOperation({
|
|
23
|
+
id: "segment.usage.getDailyPerSource",
|
|
24
|
+
name: "Get daily per-source usage",
|
|
25
|
+
description: "Daily API call counts per source.",
|
|
26
|
+
input: z.object({
|
|
27
|
+
startDate: z.string().optional(),
|
|
28
|
+
endDate: z.string().optional(),
|
|
29
|
+
sourceId: z.string().optional()
|
|
30
|
+
}),
|
|
31
|
+
output: z.object({ items: z.array(dailyEntrySchema) }),
|
|
32
|
+
run: async (input, credentials) => {
|
|
33
|
+
const rec = await createSegmentClient(credentials).publicApi.request("/usage/sources/daily-api-calls", { query: {
|
|
34
|
+
startDate: input.startDate,
|
|
35
|
+
endDate: input.endDate,
|
|
36
|
+
sourceId: input.sourceId
|
|
37
|
+
} });
|
|
38
|
+
return { items: (rec.data?.usage ?? rec.usage ?? []).map((entry) => dailyEntrySchema.parse(entry)) };
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
const getMonthlyWorkspaceUsage = segmentOperation({
|
|
42
|
+
id: "segment.usage.getMonthlyWorkspace",
|
|
43
|
+
name: "Get monthly workspace usage",
|
|
44
|
+
description: "Monthly MTU and event totals for the workspace.",
|
|
45
|
+
input: z.object({ month: z.string().optional() }),
|
|
46
|
+
output: monthlyWorkspaceSchema,
|
|
47
|
+
run: async (input, credentials) => {
|
|
48
|
+
const body = await createSegmentClient(credentials).publicApi.request("/usage/workspace/monthly", { query: { month: input.month } });
|
|
49
|
+
const rec = body;
|
|
50
|
+
return monthlyWorkspaceSchema.parse(rec.data ?? body);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
//#endregion
|
|
55
|
+
export { getDailyPerSourceUsage, getMonthlyWorkspaceUsage };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { WebhookRequest } from "@keystrokehq/core";
|
|
2
|
+
|
|
3
|
+
//#region src/verification.d.ts
|
|
4
|
+
/** Default header name Segment uses for its webhook signature. */
|
|
5
|
+
declare const SEGMENT_SIGNATURE_HEADER = "x-signature";
|
|
6
|
+
/** Hex-encoded HMAC-SHA1 of `rawBody` using `secret`. */
|
|
7
|
+
declare function buildSegmentWebhookSignature(rawBody: string, secret: string): string;
|
|
8
|
+
interface VerifySegmentWebhookOptions {
|
|
9
|
+
readonly rawBody: string;
|
|
10
|
+
readonly signatureHeader: string | undefined;
|
|
11
|
+
readonly secret: string;
|
|
12
|
+
}
|
|
13
|
+
declare function verifySegmentWebhookRequest(options: VerifySegmentWebhookOptions): boolean;
|
|
14
|
+
/** Convenience wrapper for the `WebhookRequest` shape the runtime passes. */
|
|
15
|
+
declare function verifySegmentWebhookFromRequest(request: Pick<WebhookRequest, 'headers' | 'rawBody'>, secret: string, signatureHeader?: string): boolean;
|
|
16
|
+
//#endregion
|
|
17
|
+
export { SEGMENT_SIGNATURE_HEADER, VerifySegmentWebhookOptions, buildSegmentWebhookSignature, verifySegmentWebhookFromRequest, verifySegmentWebhookRequest };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
2
|
+
|
|
3
|
+
//#region src/verification.ts
|
|
4
|
+
/**
|
|
5
|
+
* segment/verification.ts
|
|
6
|
+
*
|
|
7
|
+
* HMAC signature verification for Segment Audience webhook destinations,
|
|
8
|
+
* Engage computed-trait webhook destinations, Functions webhooks, and
|
|
9
|
+
* Monitoring alert webhooks.
|
|
10
|
+
*
|
|
11
|
+
* Segment signs webhook bodies with HMAC-SHA1 by default when a shared
|
|
12
|
+
* secret is configured on the Webhook destination / subscription. The
|
|
13
|
+
* signature is sent as a hex string in the `X-Signature` header.
|
|
14
|
+
*
|
|
15
|
+
* Reference:
|
|
16
|
+
* https://segment.com/docs/connections/destinations/catalog/webhooks/#authentication
|
|
17
|
+
* https://segment.com/docs/connections/functions/destination-functions/#shared-secret
|
|
18
|
+
*
|
|
19
|
+
* We expose helpers for:
|
|
20
|
+
* - `verifySegmentWebhookRequest({ rawBody, signatureHeader, secret })`
|
|
21
|
+
* — constant-time compare against `sha1(secret, rawBody)` in hex.
|
|
22
|
+
* - `buildSegmentWebhookSignature(rawBody, secret)` — produce the expected
|
|
23
|
+
* hex signature (useful for tests).
|
|
24
|
+
*/
|
|
25
|
+
/** Default header name Segment uses for its webhook signature. */
|
|
26
|
+
const SEGMENT_SIGNATURE_HEADER = "x-signature";
|
|
27
|
+
/** Hex-encoded HMAC-SHA1 of `rawBody` using `secret`. */
|
|
28
|
+
function buildSegmentWebhookSignature(rawBody, secret) {
|
|
29
|
+
return createHmac("sha1", secret).update(rawBody, "utf8").digest("hex");
|
|
30
|
+
}
|
|
31
|
+
function verifySegmentWebhookRequest(options) {
|
|
32
|
+
if (!options.signatureHeader || !options.secret) return false;
|
|
33
|
+
const expected = buildSegmentWebhookSignature(options.rawBody, options.secret);
|
|
34
|
+
const provided = options.signatureHeader.trim().toLowerCase();
|
|
35
|
+
const expectedBuf = Buffer.from(expected, "utf8");
|
|
36
|
+
const providedBuf = Buffer.from(provided, "utf8");
|
|
37
|
+
if (expectedBuf.length !== providedBuf.length) return false;
|
|
38
|
+
return timingSafeEqual(expectedBuf, providedBuf);
|
|
39
|
+
}
|
|
40
|
+
/** Convenience wrapper for the `WebhookRequest` shape the runtime passes. */
|
|
41
|
+
function verifySegmentWebhookFromRequest(request, secret, signatureHeader = SEGMENT_SIGNATURE_HEADER) {
|
|
42
|
+
const header = request.headers[signatureHeader] ?? request.headers[signatureHeader.toLowerCase()];
|
|
43
|
+
return verifySegmentWebhookRequest({
|
|
44
|
+
rawBody: request.rawBody,
|
|
45
|
+
signatureHeader: header,
|
|
46
|
+
secret
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
//#endregion
|
|
51
|
+
export { SEGMENT_SIGNATURE_HEADER, buildSegmentWebhookSignature, verifySegmentWebhookFromRequest, verifySegmentWebhookRequest };
|