mirror of
https://github.com/hanzoai/insights-js.git
synced 2026-10-30 18:34:19 +00:00
# Conflicts: # .github/workflows/check-insights-major-version.yml # .github/workflows/check-posthog-major-version.yml # .github/workflows/ci-security.yaml # .github/workflows/validate-versioning.yml # AGENTS.md # AGENTS.md~upstream_main # packages/ai/src/otel/capture.ts # packages/ai/src/otel/mappers/aiSdk.ts # packages/ai/src/otel/mappers/index.ts # packages/ai/src/otel/types.ts # packages/core/src/__tests__/posthog.init.spec.ts # packages/react-native/src/PostHogProvider.tsx # packages/react/src/context/PostHogContext.ts # packages/react/src/context/PostHogProvider.tsx # tooling/eslint-plugin-insights/no-unsafe-web-global.js
289 lines
8.8 KiB
TypeScript
289 lines
8.8 KiB
TypeScript
import { insights } from './insights.js'
|
|
import { action, mutation } from './_generated/server.js'
|
|
import { v } from 'convex/values'
|
|
|
|
// --- Fire-and-forget methods (mutations) ---
|
|
// When the identify callback is configured, distinctId is resolved automatically
|
|
// from the signed-in user. Pass distinctId explicitly to override or when the
|
|
// user is not signed in.
|
|
|
|
export const testCapture = mutation({
|
|
args: {
|
|
distinctId: v.optional(v.string()),
|
|
event: v.string(),
|
|
properties: v.optional(v.any()),
|
|
groups: v.optional(v.any()),
|
|
sendFeatureFlags: v.optional(v.boolean()),
|
|
timestamp: v.optional(v.string()),
|
|
uuid: v.optional(v.string()),
|
|
disableGeoip: v.optional(v.boolean()),
|
|
},
|
|
handler: async (ctx, args) => {
|
|
await insights.capture(ctx, {
|
|
distinctId: args.distinctId,
|
|
event: args.event,
|
|
properties: args.properties,
|
|
groups: args.groups,
|
|
sendFeatureFlags: args.sendFeatureFlags,
|
|
timestamp: args.timestamp ? new Date(args.timestamp) : undefined,
|
|
uuid: args.uuid || undefined,
|
|
disableGeoip: args.disableGeoip,
|
|
})
|
|
return { success: true }
|
|
},
|
|
})
|
|
|
|
export const testIdentify = mutation({
|
|
args: {
|
|
distinctId: v.optional(v.string()),
|
|
properties: v.optional(v.any()),
|
|
disableGeoip: v.optional(v.boolean()),
|
|
},
|
|
handler: async (ctx, args) => {
|
|
await insights.identify(ctx, {
|
|
distinctId: args.distinctId,
|
|
properties: args.properties,
|
|
disableGeoip: args.disableGeoip,
|
|
})
|
|
return { success: true }
|
|
},
|
|
})
|
|
|
|
export const testGroupIdentify = mutation({
|
|
args: {
|
|
groupType: v.string(),
|
|
groupKey: v.string(),
|
|
properties: v.optional(v.any()),
|
|
distinctId: v.optional(v.string()),
|
|
disableGeoip: v.optional(v.boolean()),
|
|
},
|
|
handler: async (ctx, args) => {
|
|
await insights.groupIdentify(ctx, {
|
|
groupType: args.groupType,
|
|
groupKey: args.groupKey,
|
|
properties: args.properties,
|
|
distinctId: args.distinctId || undefined,
|
|
disableGeoip: args.disableGeoip,
|
|
})
|
|
return { success: true }
|
|
},
|
|
})
|
|
|
|
export const testAlias = mutation({
|
|
args: {
|
|
distinctId: v.optional(v.string()),
|
|
alias: v.string(),
|
|
disableGeoip: v.optional(v.boolean()),
|
|
},
|
|
handler: async (ctx, args) => {
|
|
await insights.alias(ctx, {
|
|
distinctId: args.distinctId,
|
|
alias: args.alias,
|
|
disableGeoip: args.disableGeoip,
|
|
})
|
|
return { success: true }
|
|
},
|
|
})
|
|
|
|
export const testCaptureException = mutation({
|
|
args: {
|
|
errorMessage: v.string(),
|
|
errorType: v.optional(v.union(v.literal('error'), v.literal('string'), v.literal('object'))),
|
|
distinctId: v.optional(v.string()),
|
|
additionalProperties: v.optional(v.any()),
|
|
},
|
|
handler: async (ctx, args) => {
|
|
let error: unknown
|
|
switch (args.errorType ?? 'error') {
|
|
case 'error':
|
|
error = new Error(args.errorMessage)
|
|
break
|
|
case 'string':
|
|
error = args.errorMessage
|
|
break
|
|
case 'object':
|
|
error = { message: args.errorMessage }
|
|
break
|
|
}
|
|
|
|
await insights.captureException(ctx, {
|
|
error,
|
|
distinctId: args.distinctId || undefined,
|
|
additionalProperties: args.additionalProperties,
|
|
})
|
|
return { success: true }
|
|
},
|
|
})
|
|
|
|
// --- Feature flag methods (actions) ---
|
|
|
|
const featureFlagArgs = {
|
|
distinctId: v.optional(v.string()),
|
|
flagKey: v.string(),
|
|
groups: v.optional(v.any()),
|
|
personProperties: v.optional(v.any()),
|
|
groupProperties: v.optional(v.any()),
|
|
disableGeoip: v.optional(v.boolean()),
|
|
}
|
|
|
|
function featureFlagOptions(args: {
|
|
groups?: unknown
|
|
personProperties?: unknown
|
|
groupProperties?: unknown
|
|
disableGeoip?: boolean
|
|
}) {
|
|
return {
|
|
groups: args.groups as Record<string, string> | undefined,
|
|
personProperties: args.personProperties as Record<string, string> | undefined,
|
|
groupProperties: args.groupProperties as Record<string, Record<string, string>> | undefined,
|
|
disableGeoip: args.disableGeoip,
|
|
}
|
|
}
|
|
|
|
export const testGetFeatureFlag = action({
|
|
args: featureFlagArgs,
|
|
handler: async (ctx, args) => {
|
|
const value = await insights.getFeatureFlag(ctx, {
|
|
key: args.flagKey,
|
|
distinctId: args.distinctId,
|
|
...featureFlagOptions(args),
|
|
})
|
|
return { flagKey: args.flagKey, value }
|
|
},
|
|
})
|
|
|
|
export const testIsFeatureEnabled = action({
|
|
args: featureFlagArgs,
|
|
handler: async (ctx, args) => {
|
|
const enabled = await insights.isFeatureEnabled(ctx, {
|
|
key: args.flagKey,
|
|
distinctId: args.distinctId,
|
|
...featureFlagOptions(args),
|
|
})
|
|
return { flagKey: args.flagKey, enabled }
|
|
},
|
|
})
|
|
|
|
export const testGetFeatureFlagPayload = action({
|
|
args: {
|
|
...featureFlagArgs,
|
|
matchValue: v.optional(v.union(v.boolean(), v.string())),
|
|
},
|
|
handler: async (ctx, args) => {
|
|
const payload = await insights.getFeatureFlagPayload(ctx, {
|
|
key: args.flagKey,
|
|
distinctId: args.distinctId,
|
|
matchValue: args.matchValue,
|
|
...featureFlagOptions(args),
|
|
})
|
|
return { flagKey: args.flagKey, payload }
|
|
},
|
|
})
|
|
|
|
export const testGetFeatureFlagResult = action({
|
|
args: featureFlagArgs,
|
|
handler: async (ctx, args) => {
|
|
const result = await insights.getFeatureFlagResult(ctx, {
|
|
key: args.flagKey,
|
|
distinctId: args.distinctId,
|
|
...featureFlagOptions(args),
|
|
})
|
|
return { flagKey: args.flagKey, result }
|
|
},
|
|
})
|
|
|
|
export const testGetAllFlags = query({
|
|
args: {
|
|
distinctId: v.optional(v.string()),
|
|
groups: v.optional(v.any()),
|
|
personProperties: v.optional(v.any()),
|
|
groupProperties: v.optional(v.any()),
|
|
disableGeoip: v.optional(v.boolean()),
|
|
flagKeys: v.optional(v.array(v.string())),
|
|
},
|
|
handler: async (ctx, args) => {
|
|
const flags = await posthog.getAllFlags(ctx, {
|
|
distinctId: args.distinctId,
|
|
groups: args.groups as Record<string, string> | undefined,
|
|
personProperties: args.personProperties as Record<string, string> | undefined,
|
|
groupProperties: args.groupProperties as Record<string, Record<string, string>> | undefined,
|
|
disableGeoip: args.disableGeoip,
|
|
flagKeys: args.flagKeys,
|
|
})
|
|
return { flags }
|
|
},
|
|
})
|
|
|
|
// --- Cache inspection helpers (used by the demo UI to surface the cron's progress) ---
|
|
|
|
export const flagDefinitionsStatus = query({
|
|
args: {},
|
|
handler: async (ctx) => {
|
|
const row = await ctx.runQuery(components.posthog.lib.getFlagDefinitions, {})
|
|
if (!row) return null
|
|
let flagKeys: string[] = []
|
|
try {
|
|
const parsed = JSON.parse(row.data) as { flags?: Array<{ key?: string }> }
|
|
flagKeys = parsed.flags?.map((f) => f.key ?? '<unnamed>') ?? []
|
|
} catch {
|
|
// ignore parse errors — keys list stays empty.
|
|
}
|
|
return {
|
|
fetchedAt: row.fetchedAt,
|
|
etag: row.etag ?? null,
|
|
flagCount: flagKeys.length,
|
|
flagKeys,
|
|
}
|
|
},
|
|
})
|
|
|
|
// --- Remote feature flag evaluation wrappers ---
|
|
//
|
|
// These are action-context actions that hit PostHog's `/flags` endpoint via the client's
|
|
// `evaluate*` methods. Use them when local eval isn't possible (no personal API key, experience
|
|
// continuity flags, static cohorts, properties you don't have server-side).
|
|
|
|
const remoteFlagArgs = {
|
|
distinctId: v.optional(v.string()),
|
|
flagKey: v.string(),
|
|
groups: v.optional(v.any()),
|
|
personProperties: v.optional(v.any()),
|
|
groupProperties: v.optional(v.any()),
|
|
disableGeoip: v.optional(v.boolean()),
|
|
flagKeys: v.optional(v.array(v.string())),
|
|
},
|
|
handler: async (ctx, args) => {
|
|
const flags = await insights.getAllFlags(ctx, {
|
|
distinctId: args.distinctId,
|
|
groups: args.groups as Record<string, string> | undefined,
|
|
personProperties: args.personProperties as Record<string, string> | undefined,
|
|
groupProperties: args.groupProperties as Record<string, Record<string, string>> | undefined,
|
|
disableGeoip: args.disableGeoip,
|
|
flagKeys: args.flagKeys,
|
|
})
|
|
return { flags }
|
|
},
|
|
})
|
|
|
|
export const testGetAllFlagsAndPayloads = action({
|
|
args: {
|
|
distinctId: v.optional(v.string()),
|
|
groups: v.optional(v.any()),
|
|
personProperties: v.optional(v.any()),
|
|
groupProperties: v.optional(v.any()),
|
|
disableGeoip: v.optional(v.boolean()),
|
|
flagKeys: v.optional(v.array(v.string())),
|
|
},
|
|
handler: async (ctx, args) => {
|
|
const result = await insights.getAllFlagsAndPayloads(ctx, {
|
|
distinctId: args.distinctId,
|
|
groups: args.groups as Record<string, string> | undefined,
|
|
personProperties: args.personProperties as Record<string, string> | undefined,
|
|
groupProperties: args.groupProperties as Record<string, Record<string, string>> | undefined,
|
|
disableGeoip: args.disableGeoip,
|
|
flagKeys: args.flagKeys,
|
|
})
|
|
return result
|
|
},
|
|
})
|