diff --git a/package.json b/package.json index 50172c26..2327930c 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,8 @@ "dependencies": { "@npmcli/ci-detect": "^2.0.0", "@oclif/core": "^1.7", + "@segment/analytics-node": "^1.0.0-beta.26", "ajv": "^8.11.0", - "analytics-node": "segmentio/analytics-node", "chalk": "^4", "debug": "^4.3.4", "figures": "^3", @@ -44,7 +44,6 @@ "@oclif/test": "^2.1", "@segment/analytics-next": "^1.51.6", "@segment/analytics-react-native": "^2.2.1", - "@types/analytics-node": "^3.1.9", "@types/chai": "^4", "@types/debug": "^4.1.7", "@types/got": "^9.6.12", @@ -94,14 +93,16 @@ } }, "scripts": { - "build": "shx rm -rf dist && tsc && copyfiles --up 1 \"src/**/*.hbs\" dist", + "build": "shx rm -rf dist && npm run build:typedef-test-artifacts && tsc && copyfiles --up 1 \"src/**/*.hbs\" dist && npm run build:telemetry", + "build:telemetry": "./bin/run -m=prod", + "build:typedef-test-artifacts": "ts-node src/__tests__/ts-typedef-tests/run-build.ts", "lint": "eslint", "lint:fix": "eslint --fix", "postpack": "shx rm -f oclif.manifest.json", "posttest": "yarn lint", "prepack": "yarn build && oclif manifest && oclif readme", "test": "yarn test:typedef && jest --runInBand", - "test:typedef": "npx ts-node src/__tests__/ts-typedef-tests/run.ts", + "test:typedef": "npm run build:typedef-test-artifacts && ts-node src/__tests__/ts-typedef-tests/run-test.ts", "version": "oclif readme && git add README.md" }, "engines": { diff --git a/src/__tests__/commands/__snapshots__/build.test.ts.snap b/src/__tests__/commands/__snapshots__/build.test.ts.snap index ee34559c..0ab84ed8 100644 --- a/src/__tests__/commands/__snapshots__/build.test.ts.snap +++ b/src/__tests__/commands/__snapshots__/build.test.ts.snap @@ -64,7 +64,7 @@ var defaultValidationErrorHandler = function (message, violations) { }; exports.defaultValidationErrorHandler = defaultValidationErrorHandler; var onViolation = exports.defaultValidationErrorHandler; -var missingAnalyticsNodeError = new Error(\\"You must set an analytics-node instance:\\\\n\\\\n>\\\\tconst SegmentAnalytics = require('analytics-node')\\\\n>\\\\tconst { setTypewriterOptions } = require('./analytics')\\\\n>\\\\n>\\\\tconst analytics = new SegmentAnalytics('SEGMENT_WRITE_KEY')\\\\n>\\\\tsetTypewriterOptions({\\\\n>\\\\t\\\\tanalytics: analytics,\\\\n>\\\\t})\\\\n\\\\nFor more information on analytics-node, see: https://segment.com/docs/sources/server/node/quickstart/\\\\n\\"); +var missingAnalyticsNodeError = new Error(\\"You must set an analytics-node instance:\\\\n\\\\n>\\\\timport { Analytics } from '@segment/analytics-node'\\\\n>\\\\timport { setTypewriterOptions } from './analytics'\\\\n>\\\\n> const analytics = new Analytics({ writeKey: 'SEGMENT_WRITE_KEY' })\\\\n>\\\\tsetTypewriterOptions({ analytics: analytics\\\\t})\\\\n\\\\nFor more information on @segment/analytics-node, see: https://segment.com/docs/sources/server/node/quickstart/\\\\n\\"); var analytics = function () { throw missingAnalyticsNodeError; }; @@ -76,7 +76,7 @@ var analytics = function () { * @param {TypewriterOptions} options - the options to upsert * * @typedef {Object} TypewriterOptions - * @property {AnalyticsNode} analytics - Underlying analytics instance where analytics + * @property {Analytics} analytics - Underlying analytics instance where analytics * calls are forwarded on to. * @property {Function} [onViolation] - Handler fired when if an event does not match its spec. This handler does not fire in * production mode, because it requires inlining the full JSON Schema spec for each event in your Tracking Plan. By default, @@ -474,7 +474,7 @@ var clientAPI = { * @param {TypewriterOptions} options - the options to upsert * * @typedef {Object} TypewriterOptions - * @property {AnalyticsNode} analytics - Underlying analytics instance where analytics + * @property {Analytics} analytics - Underlying analytics instance where analytics * calls are forwarded on to. * @property {Function} [onViolation] - Handler fired when if an event does not match its spec. This handler does not fire in * production mode, because it requires inlining the full JSON Schema spec for each event in your Tracking Plan. By default, @@ -5171,8 +5171,17 @@ import Ajv, { ErrorObject } from 'ajv' /** * The analytics.js snippet should be available via window.analytics. - * You can install it by following instructions at: https://segment.com/docs/sources/website/analytics.js/quickstart/ - * Make sure to also include the TypeScript declarations with: \`npm install --dev @types/segment-analytics\` + * You can install it by following instructions at: https://segment.com/docs/connections/sources/catalog/libraries/website/javascript/ + * Make sure to also include the TypeScript declarations with: \`npm install @segment/analytics-next\` (install with --save-dev for type definitions only). + * + * If you don't want to use the snippet, you can also install the \`@segment/analytics-next\` library as a *production* dependency and use it like this: + * \`\`\`ts + * import { Analytics } from '@segment/analytics-next' + * import { setTypewriterOptions } from './analytics' + * + * const analytics = new Analytics({ writeKey: 'SEGMENT_WRITE_KEY' }) + * + * setTypewriterOptions({ analytics: analytics }) */ import type { AnalyticsSnippet, Analytics, AnalyticsBrowser, Options } from '@segment/analytics-next' @@ -6575,123 +6584,31 @@ export interface NoIDType { * You can install it with: \`npm install --save-dev ajv\`. */ import Ajv, { ErrorObject } from 'ajv' -import AnalyticsNode from 'analytics-node' /** -* At least one of userId or anonymousId must be included in any identify call. -*/ -type Identity = { userId: string | number } | { anonymousId: string | number }; + * You can install \`@segment/analytics-node\` by following instructions at: + * https://segment.com/docs/connections/sources/catalog/libraries/server/node + */ +import { Analytics, TrackParams } from '@segment/analytics-node' + +/** + * An ID associated with the user. Note: at least one of userId or anonymousId must be included! + **/ +type Identity = + | { userId: string; anonymousId?: string } + | { userId?: string; anonymousId: string } /** * TrackMessage represents a message payload for an analytics \`.track()\` call. * See: https://segment.com/docs/spec/track/ */ -export type TrackMessage = Options & Record & Identity & { - /** A dictionary of properties for the event. */ - properties?: PropertiesType - /** - * A Javascript date object representing when the track took place. - * If the track just happened, leave it out and we’ll use the server’s - * time. If you’re importing data from the past make sure you to send - * a timestamp. - */ - timestamp?: Date - /** - * MessageId which can be optionally set to override the default one generated by the library. - * This is useful when you want to deduplicate messages. - */ - messageId?: string -} +export type TrackMessage = Omit< + TrackParams, + 'event' | 'properties' +> & { event?: string, properties: PropertiesType } & Identity /** The callback exposed by analytics-node. */ -export type Callback = (err: Error) => void - -/** A dictionary of options. For example, enable or disable specific destinations for the call. */ -export interface Options { - /** - * Selectivly filter destinations. By default all destinations are enabled. - * https://segment.com/docs/sources/website/analytics.js/#selecting-destinations - */ - integrations?: { - [key: string]: boolean | { [key: string]: any }; - }; - /** - * A dictionary of extra context to attach to the call. - * https://segment.com/docs/spec/common/#context - */ - context?: Context; -} - -/** - * Context is a dictionary of extra information that provides useful context about a datapoint. - * @see {@link https://segment.com/docs/spec/common/#context} - */ -export interface Context extends Record { - active?: boolean; - app?: { - name?: string; - version?: string; - build?: string; - }; - campaign?: { - name?: string; - source?: string; - medium?: string; - term?: string; - content?: string; - }; - device?: { - id?: string; - manufacturer?: string; - model?: string; - name?: string; - type?: string; - version?: string; - }; - ip?: string; - locale?: string; - location?: { - city?: string; - country?: string; - latitude?: string; - longitude?: string; - region?: string; - speed?: string; - }; - network?: { - bluetooth?: string; - carrier?: string; - cellular?: string; - wifi?: string; - }; - os?: { - name?: string; - version?: string; - }; - page?: { - hash?: string; - path?: string; - referrer?: string; - search?: string; - title?: string; - url?: string; - }; - referrer?: { - type?: string; - name?: string; - url?: string; - link?: string; - }; - screen?: { - density?: string; - height?: string; - width?: string; - }; - timezone?: string; - groupId?: string; - traits?: Record; - userAgent?: string; -} +export type Callback = Parameters[1] export type ViolationHandler = ( message: TrackMessage>, @@ -6729,18 +6646,16 @@ let onViolation = defaultValidationErrorHandler const missingAnalyticsNodeError = new Error(\`You must set an analytics-node instance: -> const SegmentAnalytics = require('analytics-node') -> const { setTypewriterOptions } = require('./analytics') +> import { Analytics } from '@segment/analytics-node' +> import { setTypewriterOptions } from './analytics' > -> const analytics = new SegmentAnalytics('SEGMENT_WRITE_KEY') -> setTypewriterOptions({ -> analytics: analytics, -> }) +> const analytics = new Analytics({ writeKey: 'SEGMENT_WRITE_KEY' }) +> setTypewriterOptions({ analytics: analytics }) -For more information on analytics-node, see: https://segment.com/docs/sources/server/node/quickstart/ +For more information on @segment/analytics-node, see: https://segment.com/docs/sources/server/node/quickstart/ \`) -let analytics: () => AnalyticsNode | undefined = () => { +let analytics: () => Analytics | undefined = () => { throw missingAnalyticsNodeError } @@ -6749,7 +6664,7 @@ export interface TypewriterOptions { /** * Underlying analytics instance where analytics calls are forwarded on to. */ - analytics: AnalyticsNode + analytics: Analytics /** * Handler fired when if an event does not match its spec. This handler * does not fire in production mode, because it requires inlining the full @@ -6769,7 +6684,7 @@ export interface TypewriterOptions { * @param {TypewriterOptions} options - the options to upsert * * @typedef {Object} TypewriterOptions - * @property {AnalyticsNode} analytics - Underlying analytics instance where analytics + * @property {Analytics} analytics - Underlying analytics instance where analytics * calls are forwarded on to. * @property {Function} [onViolation] - Handler fired when if an event does not match its spec. This handler does not fire in * production mode, because it requires inlining the full JSON Schema spec for each event in your Tracking Plan. By default, @@ -6800,9 +6715,9 @@ function validateAgainstSchema( * Helper to attach metadata on Typewriter to outbound requests. * This is used for attribution and debugging by the Segment team. */ -function withTypewriterContext>( +function withTypewriterContext

, T extends TrackMessage

>( message: T -): T { +) { return { ...message, context: { @@ -7319,7 +7234,7 @@ const clientAPI = { * @param {TypewriterOptions} options - the options to upsert * * @typedef {Object} TypewriterOptions - * @property {AnalyticsNode} analytics - Underlying analytics instance where analytics + * @property {Analytics} analytics - Underlying analytics instance where analytics * calls are forwarded on to. * @property {Function} [onViolation] - Handler fired when if an event does not match its spec. This handler does not fire in * production mode, because it requires inlining the full JSON Schema spec for each event in your Tracking Plan. By default, @@ -8149,123 +8064,31 @@ export interface UnionType { * You can install it with: \`npm install --save-dev ajv\`. */ import Ajv, { ErrorObject } from 'ajv' -import AnalyticsNode from 'analytics-node' /** -* At least one of userId or anonymousId must be included in any identify call. -*/ -type Identity = { userId: string | number } | { anonymousId: string | number }; + * You can install \`@segment/analytics-node\` by following instructions at: + * https://segment.com/docs/connections/sources/catalog/libraries/server/node + */ +import { Analytics, TrackParams } from '@segment/analytics-node' + +/** + * An ID associated with the user. Note: at least one of userId or anonymousId must be included! + **/ +type Identity = + | { userId: string; anonymousId?: string } + | { userId?: string; anonymousId: string } /** * TrackMessage represents a message payload for an analytics \`.track()\` call. * See: https://segment.com/docs/spec/track/ */ -export type TrackMessage = Options & Record & Identity & { - /** A dictionary of properties for the event. */ - properties?: PropertiesType - /** - * A Javascript date object representing when the track took place. - * If the track just happened, leave it out and we’ll use the server’s - * time. If you’re importing data from the past make sure you to send - * a timestamp. - */ - timestamp?: Date - /** - * MessageId which can be optionally set to override the default one generated by the library. - * This is useful when you want to deduplicate messages. - */ - messageId?: string -} +export type TrackMessage = Omit< + TrackParams, + 'event' | 'properties' +> & { event?: string, properties: PropertiesType } & Identity /** The callback exposed by analytics-node. */ -export type Callback = (err: Error) => void - -/** A dictionary of options. For example, enable or disable specific destinations for the call. */ -export interface Options { - /** - * Selectivly filter destinations. By default all destinations are enabled. - * https://segment.com/docs/sources/website/analytics.js/#selecting-destinations - */ - integrations?: { - [key: string]: boolean | { [key: string]: any }; - }; - /** - * A dictionary of extra context to attach to the call. - * https://segment.com/docs/spec/common/#context - */ - context?: Context; -} - -/** - * Context is a dictionary of extra information that provides useful context about a datapoint. - * @see {@link https://segment.com/docs/spec/common/#context} - */ -export interface Context extends Record { - active?: boolean; - app?: { - name?: string; - version?: string; - build?: string; - }; - campaign?: { - name?: string; - source?: string; - medium?: string; - term?: string; - content?: string; - }; - device?: { - id?: string; - manufacturer?: string; - model?: string; - name?: string; - type?: string; - version?: string; - }; - ip?: string; - locale?: string; - location?: { - city?: string; - country?: string; - latitude?: string; - longitude?: string; - region?: string; - speed?: string; - }; - network?: { - bluetooth?: string; - carrier?: string; - cellular?: string; - wifi?: string; - }; - os?: { - name?: string; - version?: string; - }; - page?: { - hash?: string; - path?: string; - referrer?: string; - search?: string; - title?: string; - url?: string; - }; - referrer?: { - type?: string; - name?: string; - url?: string; - link?: string; - }; - screen?: { - density?: string; - height?: string; - width?: string; - }; - timezone?: string; - groupId?: string; - traits?: Record; - userAgent?: string; -} +export type Callback = Parameters[1] export type ViolationHandler = ( message: TrackMessage>, @@ -8303,18 +8126,16 @@ let onViolation = defaultValidationErrorHandler const missingAnalyticsNodeError = new Error(\`You must set an analytics-node instance: -> const SegmentAnalytics = require('analytics-node') -> const { setTypewriterOptions } = require('./analytics') +> import { Analytics } from '@segment/analytics-node' +> import { setTypewriterOptions } from './analytics' > -> const analytics = new SegmentAnalytics('SEGMENT_WRITE_KEY') -> setTypewriterOptions({ -> analytics: analytics, -> }) +> const analytics = new Analytics({ writeKey: 'SEGMENT_WRITE_KEY' }) +> setTypewriterOptions({ analytics: analytics }) -For more information on analytics-node, see: https://segment.com/docs/sources/server/node/quickstart/ +For more information on @segment/analytics-node, see: https://segment.com/docs/sources/server/node/quickstart/ \`) -let analytics: () => AnalyticsNode | undefined = () => { +let analytics: () => Analytics | undefined = () => { throw missingAnalyticsNodeError } @@ -8323,7 +8144,7 @@ export interface TypewriterOptions { /** * Underlying analytics instance where analytics calls are forwarded on to. */ - analytics: AnalyticsNode + analytics: Analytics /** * Handler fired when if an event does not match its spec. This handler * does not fire in production mode, because it requires inlining the full @@ -8343,7 +8164,7 @@ export interface TypewriterOptions { * @param {TypewriterOptions} options - the options to upsert * * @typedef {Object} TypewriterOptions - * @property {AnalyticsNode} analytics - Underlying analytics instance where analytics + * @property {Analytics} analytics - Underlying analytics instance where analytics * calls are forwarded on to. * @property {Function} [onViolation] - Handler fired when if an event does not match its spec. This handler does not fire in * production mode, because it requires inlining the full JSON Schema spec for each event in your Tracking Plan. By default, @@ -8374,9 +8195,9 @@ function validateAgainstSchema( * Helper to attach metadata on Typewriter to outbound requests. * This is used for attribution and debugging by the Segment team. */ -function withTypewriterContext>( +function withTypewriterContext

, T extends TrackMessage

>( message: T -): T { +) { return { ...message, context: { @@ -8864,7 +8685,7 @@ const clientAPI = { * @param {TypewriterOptions} options - the options to upsert * * @typedef {Object} TypewriterOptions - * @property {AnalyticsNode} analytics - Underlying analytics instance where analytics + * @property {Analytics} analytics - Underlying analytics instance where analytics * calls are forwarded on to. * @property {Function} [onViolation] - Handler fired when if an event does not match its spec. This handler does not fire in * production mode, because it requires inlining the full JSON Schema spec for each event in your Tracking Plan. By default, @@ -9684,123 +9505,31 @@ export interface UnionType { * You can install it with: \`npm install --save-dev ajv\`. */ import Ajv, { ErrorObject } from 'ajv' -import AnalyticsNode from 'analytics-node' /** -* At least one of userId or anonymousId must be included in any identify call. -*/ -type Identity = { userId: string | number } | { anonymousId: string | number }; + * You can install \`@segment/analytics-node\` by following instructions at: + * https://segment.com/docs/connections/sources/catalog/libraries/server/node + */ +import { Analytics, TrackParams } from '@segment/analytics-node' + +/** + * An ID associated with the user. Note: at least one of userId or anonymousId must be included! + **/ +type Identity = + | { userId: string; anonymousId?: string } + | { userId?: string; anonymousId: string } /** * TrackMessage represents a message payload for an analytics \`.track()\` call. * See: https://segment.com/docs/spec/track/ */ -export type TrackMessage = Options & Record & Identity & { - /** A dictionary of properties for the event. */ - properties?: PropertiesType - /** - * A Javascript date object representing when the track took place. - * If the track just happened, leave it out and we’ll use the server’s - * time. If you’re importing data from the past make sure you to send - * a timestamp. - */ - timestamp?: Date - /** - * MessageId which can be optionally set to override the default one generated by the library. - * This is useful when you want to deduplicate messages. - */ - messageId?: string -} +export type TrackMessage = Omit< + TrackParams, + 'event' | 'properties' +> & { event?: string, properties: PropertiesType } & Identity /** The callback exposed by analytics-node. */ -export type Callback = (err: Error) => void - -/** A dictionary of options. For example, enable or disable specific destinations for the call. */ -export interface Options { - /** - * Selectivly filter destinations. By default all destinations are enabled. - * https://segment.com/docs/sources/website/analytics.js/#selecting-destinations - */ - integrations?: { - [key: string]: boolean | { [key: string]: any }; - }; - /** - * A dictionary of extra context to attach to the call. - * https://segment.com/docs/spec/common/#context - */ - context?: Context; -} - -/** - * Context is a dictionary of extra information that provides useful context about a datapoint. - * @see {@link https://segment.com/docs/spec/common/#context} - */ -export interface Context extends Record { - active?: boolean; - app?: { - name?: string; - version?: string; - build?: string; - }; - campaign?: { - name?: string; - source?: string; - medium?: string; - term?: string; - content?: string; - }; - device?: { - id?: string; - manufacturer?: string; - model?: string; - name?: string; - type?: string; - version?: string; - }; - ip?: string; - locale?: string; - location?: { - city?: string; - country?: string; - latitude?: string; - longitude?: string; - region?: string; - speed?: string; - }; - network?: { - bluetooth?: string; - carrier?: string; - cellular?: string; - wifi?: string; - }; - os?: { - name?: string; - version?: string; - }; - page?: { - hash?: string; - path?: string; - referrer?: string; - search?: string; - title?: string; - url?: string; - }; - referrer?: { - type?: string; - name?: string; - url?: string; - link?: string; - }; - screen?: { - density?: string; - height?: string; - width?: string; - }; - timezone?: string; - groupId?: string; - traits?: Record; - userAgent?: string; -} +export type Callback = Parameters[1] export type ViolationHandler = ( message: TrackMessage>, @@ -9838,18 +9567,16 @@ let onViolation = defaultValidationErrorHandler const missingAnalyticsNodeError = new Error(\`You must set an analytics-node instance: -> const SegmentAnalytics = require('analytics-node') -> const { setTypewriterOptions } = require('./analytics') +> import { Analytics } from '@segment/analytics-node' +> import { setTypewriterOptions } from './analytics' > -> const analytics = new SegmentAnalytics('SEGMENT_WRITE_KEY') -> setTypewriterOptions({ -> analytics: analytics, -> }) +> const analytics = new Analytics({ writeKey: 'SEGMENT_WRITE_KEY' }) +> setTypewriterOptions({ analytics: analytics }) -For more information on analytics-node, see: https://segment.com/docs/sources/server/node/quickstart/ +For more information on @segment/analytics-node, see: https://segment.com/docs/sources/server/node/quickstart/ \`) -let analytics: () => AnalyticsNode | undefined = () => { +let analytics: () => Analytics | undefined = () => { throw missingAnalyticsNodeError } @@ -9858,7 +9585,7 @@ export interface TypewriterOptions { /** * Underlying analytics instance where analytics calls are forwarded on to. */ - analytics: AnalyticsNode + analytics: Analytics /** * Handler fired when if an event does not match its spec. This handler * does not fire in production mode, because it requires inlining the full @@ -9878,7 +9605,7 @@ export interface TypewriterOptions { * @param {TypewriterOptions} options - the options to upsert * * @typedef {Object} TypewriterOptions - * @property {AnalyticsNode} analytics - Underlying analytics instance where analytics + * @property {Analytics} analytics - Underlying analytics instance where analytics * calls are forwarded on to. * @property {Function} [onViolation] - Handler fired when if an event does not match its spec. This handler does not fire in * production mode, because it requires inlining the full JSON Schema spec for each event in your Tracking Plan. By default, @@ -9909,9 +9636,9 @@ function validateAgainstSchema( * Helper to attach metadata on Typewriter to outbound requests. * This is used for attribution and debugging by the Segment team. */ -function withTypewriterContext>( +function withTypewriterContext

, T extends TrackMessage

>( message: T -): T { +) { return { ...message, context: { @@ -10399,7 +10126,7 @@ const clientAPI = { * @param {TypewriterOptions} options - the options to upsert * * @typedef {Object} TypewriterOptions - * @property {AnalyticsNode} analytics - Underlying analytics instance where analytics + * @property {Analytics} analytics - Underlying analytics instance where analytics * calls are forwarded on to. * @property {Function} [onViolation] - Handler fired when if an event does not match its spec. This handler does not fire in * production mode, because it requires inlining the full JSON Schema spec for each event in your Tracking Plan. By default, diff --git a/src/__tests__/commands/__snapshots__/production.test.ts.snap b/src/__tests__/commands/__snapshots__/production.test.ts.snap index 0082360e..d1baf9d4 100644 --- a/src/__tests__/commands/__snapshots__/production.test.ts.snap +++ b/src/__tests__/commands/__snapshots__/production.test.ts.snap @@ -676,8 +676,17 @@ export interface NoIDType { /** * The analytics.js snippet should be available via window.analytics. - * You can install it by following instructions at: https://segment.com/docs/sources/website/analytics.js/quickstart/ - * Make sure to also include the TypeScript declarations with: \`npm install --dev @types/segment-analytics\` + * You can install it by following instructions at: https://segment.com/docs/connections/sources/catalog/libraries/website/javascript/ + * Make sure to also include the TypeScript declarations with: \`npm install @segment/analytics-next\` (install with --save-dev for type definitions only). + * + * If you don't want to use the snippet, you can also install the \`@segment/analytics-next\` library as a *production* dependency and use it like this: + * \`\`\`ts + * import { Analytics } from '@segment/analytics-next' + * import { setTypewriterOptions } from './analytics' + * + * const analytics = new Analytics({ writeKey: 'SEGMENT_WRITE_KEY' }) + * + * setTypewriterOptions({ analytics: analytics }) */ import type { AnalyticsSnippet, Analytics, AnalyticsBrowser, Options } from '@segment/analytics-next' @@ -2021,123 +2030,31 @@ export interface NoIDType { no_id_prop: string; } -import AnalyticsNode from 'analytics-node' /** -* At least one of userId or anonymousId must be included in any identify call. -*/ -type Identity = { userId: string | number } | { anonymousId: string | number }; + * You can install \`@segment/analytics-node\` by following instructions at: + * https://segment.com/docs/connections/sources/catalog/libraries/server/node + */ +import { Analytics, TrackParams } from '@segment/analytics-node' + +/** + * An ID associated with the user. Note: at least one of userId or anonymousId must be included! + **/ +type Identity = + | { userId: string; anonymousId?: string } + | { userId?: string; anonymousId: string } /** * TrackMessage represents a message payload for an analytics \`.track()\` call. * See: https://segment.com/docs/spec/track/ */ -export type TrackMessage = Options & Record & Identity & { - /** A dictionary of properties for the event. */ - properties?: PropertiesType - /** - * A Javascript date object representing when the track took place. - * If the track just happened, leave it out and we’ll use the server’s - * time. If you’re importing data from the past make sure you to send - * a timestamp. - */ - timestamp?: Date - /** - * MessageId which can be optionally set to override the default one generated by the library. - * This is useful when you want to deduplicate messages. - */ - messageId?: string -} +export type TrackMessage = Omit< + TrackParams, + 'event' | 'properties' +> & { event?: string, properties: PropertiesType } & Identity /** The callback exposed by analytics-node. */ -export type Callback = (err: Error) => void - -/** A dictionary of options. For example, enable or disable specific destinations for the call. */ -export interface Options { - /** - * Selectivly filter destinations. By default all destinations are enabled. - * https://segment.com/docs/sources/website/analytics.js/#selecting-destinations - */ - integrations?: { - [key: string]: boolean | { [key: string]: any }; - }; - /** - * A dictionary of extra context to attach to the call. - * https://segment.com/docs/spec/common/#context - */ - context?: Context; -} - -/** - * Context is a dictionary of extra information that provides useful context about a datapoint. - * @see {@link https://segment.com/docs/spec/common/#context} - */ -export interface Context extends Record { - active?: boolean; - app?: { - name?: string; - version?: string; - build?: string; - }; - campaign?: { - name?: string; - source?: string; - medium?: string; - term?: string; - content?: string; - }; - device?: { - id?: string; - manufacturer?: string; - model?: string; - name?: string; - type?: string; - version?: string; - }; - ip?: string; - locale?: string; - location?: { - city?: string; - country?: string; - latitude?: string; - longitude?: string; - region?: string; - speed?: string; - }; - network?: { - bluetooth?: string; - carrier?: string; - cellular?: string; - wifi?: string; - }; - os?: { - name?: string; - version?: string; - }; - page?: { - hash?: string; - path?: string; - referrer?: string; - search?: string; - title?: string; - url?: string; - }; - referrer?: { - type?: string; - name?: string; - url?: string; - link?: string; - }; - screen?: { - density?: string; - height?: string; - width?: string; - }; - timezone?: string; - groupId?: string; - traits?: Record; - userAgent?: string; -} +export type Callback = Parameters[1] export type ViolationHandler = ( message: TrackMessage>, @@ -2174,18 +2091,16 @@ export type ViolationHandler = ( const missingAnalyticsNodeError = new Error(\`You must set an analytics-node instance: -> const SegmentAnalytics = require('analytics-node') -> const { setTypewriterOptions } = require('./analytics') +> import { Analytics } from '@segment/analytics-node' +> import { setTypewriterOptions } from './analytics' > -> const analytics = new SegmentAnalytics('SEGMENT_WRITE_KEY') -> setTypewriterOptions({ -> analytics: analytics, -> }) +> const analytics = new Analytics({ writeKey: 'SEGMENT_WRITE_KEY' }) +> setTypewriterOptions({ analytics: analytics }) -For more information on analytics-node, see: https://segment.com/docs/sources/server/node/quickstart/ +For more information on @segment/analytics-node, see: https://segment.com/docs/sources/server/node/quickstart/ \`) -let analytics: () => AnalyticsNode | undefined = () => { +let analytics: () => Analytics | undefined = () => { throw missingAnalyticsNodeError } @@ -2194,7 +2109,7 @@ export interface TypewriterOptions { /** * Underlying analytics instance where analytics calls are forwarded on to. */ - analytics: AnalyticsNode + analytics: Analytics /** * Handler fired when if an event does not match its spec. This handler * does not fire in production mode, because it requires inlining the full @@ -2214,7 +2129,7 @@ export interface TypewriterOptions { * @param {TypewriterOptions} options - the options to upsert * * @typedef {Object} TypewriterOptions - * @property {AnalyticsNode} analytics - Underlying analytics instance where analytics + * @property {Analytics} analytics - Underlying analytics instance where analytics * calls are forwarded on to. * @property {Function} [onViolation] - Handler fired when if an event does not match its spec. This handler does not fire in * production mode, because it requires inlining the full JSON Schema spec for each event in your Tracking Plan. By default, @@ -2230,9 +2145,9 @@ export function setTypewriterOptions(options: TypewriterOptions) { * Helper to attach metadata on Typewriter to outbound requests. * This is used for attribution and debugging by the Segment team. */ -function withTypewriterContext>( +function withTypewriterContext

, T extends TrackMessage

>( message: T -): T { +) { return { ...message, context: { @@ -2715,7 +2630,7 @@ const clientAPI = { * @param {TypewriterOptions} options - the options to upsert * * @typedef {Object} TypewriterOptions - * @property {AnalyticsNode} analytics - Underlying analytics instance where analytics + * @property {Analytics} analytics - Underlying analytics instance where analytics * calls are forwarded on to. * @property {Function} [onViolation] - Handler fired when if an event does not match its spec. This handler does not fire in * production mode, because it requires inlining the full JSON Schema spec for each event in your Tracking Plan. By default, diff --git a/src/__tests__/ts-typedef-tests/run-build.ts b/src/__tests__/ts-typedef-tests/run-build.ts new file mode 100644 index 00000000..d7550e0c --- /dev/null +++ b/src/__tests__/ts-typedef-tests/run-build.ts @@ -0,0 +1,3 @@ +import { buildSDKs } from "./build-sdks"; + +buildSDKs(); diff --git a/src/__tests__/ts-typedef-tests/run.ts b/src/__tests__/ts-typedef-tests/run-test.ts similarity index 75% rename from src/__tests__/ts-typedef-tests/run.ts rename to src/__tests__/ts-typedef-tests/run-test.ts index 8acf2187..98a65eea 100644 --- a/src/__tests__/ts-typedef-tests/run.ts +++ b/src/__tests__/ts-typedef-tests/run-test.ts @@ -2,7 +2,6 @@ import { exec } from "node:child_process"; import { promisify } from "node:util"; import path from "node:path"; import fs from "node:fs"; -import { buildSDKs } from "./build-sdks"; /** * Typecheck current project @@ -61,21 +60,21 @@ const typecheckFiles = async (files: string[]) => { } }; - /** - * Run - */ - const _main = (async () => { - try { - await buildSDKs(); - const PATHS_TO_TYPECHECK = [ - path.resolve(__dirname, "build"), - path.resolve(__dirname, "tests"), - ] - const paths = PATHS_TO_TYPECHECK.flatMap(listFiles); - await typecheckFiles(paths); - } catch (err: any) { - console.error(err); - process.exit(1); +/** + * Run + */ +const _main = (async () => { + try { + const buildPath = path.resolve(__dirname, "build"); + const testPath = path.resolve(__dirname, "tests"); + if (!fs.existsSync(buildPath)) { + throw new Error('please run "yarn build"'); } + const paths = [buildPath, testPath].flatMap(listFiles); + await typecheckFiles(paths); + console.log("Type-checking complete."); + } catch (err: any) { + console.error(err); + process.exit(1); } -)(); +})(); diff --git a/src/__tests__/ts-typedef-tests/tests/typescript-analytics-js-import-test.ts b/src/__tests__/ts-typedef-tests/tests/typescript-analytics-js-import-test.ts index ab59a1cb..a8dde7da 100644 --- a/src/__tests__/ts-typedef-tests/tests/typescript-analytics-js-import-test.ts +++ b/src/__tests__/ts-typedef-tests/tests/typescript-analytics-js-import-test.ts @@ -1,8 +1,7 @@ import { AnalyticsBrowser, AnalyticsSnippet } from "@segment/analytics-next"; -// @ts-ignore import typewriter from "../build/typescript-analytics-js"; () => { typewriter.setTypewriterOptions({ analytics: {} as AnalyticsBrowser }); typewriter.setTypewriterOptions({ analytics: {} as AnalyticsSnippet }); -} +}; diff --git a/src/__tests__/ts-typedef-tests/tests/typescript-analytics-node-import-test.ts b/src/__tests__/ts-typedef-tests/tests/typescript-analytics-node-import-test.ts new file mode 100644 index 00000000..214dfeb7 --- /dev/null +++ b/src/__tests__/ts-typedef-tests/tests/typescript-analytics-node-import-test.ts @@ -0,0 +1,49 @@ +import { Analytics } from "@segment/analytics-node"; +import typewriter from "../build/typescript-analytics-node"; + +() => { + // assert expects a node analytics instance + typewriter.setTypewriterOptions({ + analytics: new Analytics({ writeKey: "foo" }), + }); + + // @ts-expect-error - should require user ID OR anonId + typewriter.everyNullableOptionalType({ + properties: { "optional array with properties": [] }, + }); + + // should require user ID OR anonId + typewriter.everyNullableOptionalType({ + userId: "foo", + properties: { "optional array with properties": [] }, + }); + + // should require user ID OR anonId + typewriter.everyNullableOptionalType({ + anonymousId: "foo", + properties: { "optional array with properties": [] }, + }); + + // @ts-expect-error - should require a properties object + typewriter.everyNullableOptionalType({ + anonymousId: "foo", + }); + + // assert empty properties + typewriter.everyNullableOptionalType({ + anonymousId: "foo", + properties: {}, + }); + + // assert context can be passed + typewriter.everyNullableOptionalType({ + anonymousId: "foo", + context: { + traits: { + age: 99, + }, + active: true, + }, + properties: {}, + }); +}; diff --git a/src/base-command.ts b/src/base-command.ts index 92818619..7189cc74 100644 --- a/src/base-command.ts +++ b/src/base-command.ts @@ -127,7 +127,7 @@ export abstract class BaseCommand extends Command { }); // We do a flush here manually cause oclif doesn't run the postrun hook for errors try { - await this.segmentClient.flush(); + await this.segmentClient.closeAndFlush(); } catch {} return super.catch(err); } diff --git a/src/hooks/postrun/telemetry.ts b/src/hooks/postrun/telemetry.ts index 0c796c1f..f5b32d97 100644 --- a/src/hooks/postrun/telemetry.ts +++ b/src/hooks/postrun/telemetry.ts @@ -3,7 +3,7 @@ import { segmentClient } from '../../telemetry'; const hook: Hook<'postrun'> = async function (opts) { // Send any pending segment events - await segmentClient.flush(); + await segmentClient.closeAndFlush(); }; export default hook; diff --git a/src/languages/templates/typescript/analytics-js.hbs b/src/languages/templates/typescript/analytics-js.hbs index fe40419a..7bd8b5c7 100644 --- a/src/languages/templates/typescript/analytics-js.hbs +++ b/src/languages/templates/typescript/analytics-js.hbs @@ -12,11 +12,20 @@ import Ajv, { ErrorObject } from 'ajv' /** * The analytics.js snippet should be available via window.analytics. - * You can install it by following instructions at: https://segment.com/docs/sources/website/analytics.js/quickstart/ - * Make sure to also include the TypeScript declarations with: `npm install --dev @types/segment-analytics` + * You can install it by following instructions at: https://segment.com/docs/connections/sources/catalog/libraries/website/javascript/ + * Make sure to also include the TypeScript declarations with: `npm install @segment/analytics-next` (install with --save-dev for type definitions only). + * + * If you don't want to use the snippet, you can also install the `@segment/analytics-next` library as a *production* dependency and use it like this: + * ```ts + * import { Analytics } from '@segment/analytics-next' + * import { setTypewriterOptions } from './analytics' + * + * const analytics = new Analytics({ writeKey: 'SEGMENT_WRITE_KEY' }) + * + * setTypewriterOptions({ analytics: analytics }) */ import type { AnalyticsSnippet, Analytics, AnalyticsBrowser, Options } from '@segment/analytics-next' - + declare global { interface Window { analytics: AnalyticsSnippet; diff --git a/src/languages/templates/typescript/node.hbs b/src/languages/templates/typescript/node.hbs index 50ec8a2e..60b78c1f 100644 --- a/src/languages/templates/typescript/node.hbs +++ b/src/languages/templates/typescript/node.hbs @@ -9,123 +9,31 @@ */ import Ajv, { ErrorObject } from 'ajv' {{/if}} -import AnalyticsNode from 'analytics-node' /** -* At least one of userId or anonymousId must be included in any identify call. -*/ -type Identity = { userId: string | number } | { anonymousId: string | number }; + * You can install `@segment/analytics-node` by following instructions at: + * https://segment.com/docs/connections/sources/catalog/libraries/server/node + */ +import { Analytics, TrackParams } from '@segment/analytics-node' + +/** + * An ID associated with the user. Note: at least one of userId or anonymousId must be included! + **/ +type Identity = + | { userId: string; anonymousId?: string } + | { userId?: string; anonymousId: string } /** * TrackMessage represents a message payload for an analytics `.track()` call. * See: https://segment.com/docs/spec/track/ */ -export type TrackMessage = Options & Record & Identity & { - /** A dictionary of properties for the event. */ - properties?: PropertiesType - /** - * A Javascript date object representing when the track took place. - * If the track just happened, leave it out and we’ll use the server’s - * time. If you’re importing data from the past make sure you to send - * a timestamp. - */ - timestamp?: Date - /** - * MessageId which can be optionally set to override the default one generated by the library. - * This is useful when you want to deduplicate messages. - */ - messageId?: string -} +export type TrackMessage = Omit< + TrackParams, + 'event' | 'properties' +> & { event?: string, properties: PropertiesType } & Identity /** The callback exposed by analytics-node. */ -export type Callback = (err: Error) => void - -/** A dictionary of options. For example, enable or disable specific destinations for the call. */ -export interface Options { - /** - * Selectivly filter destinations. By default all destinations are enabled. - * https://segment.com/docs/sources/website/analytics.js/#selecting-destinations - */ - integrations?: { - [key: string]: boolean | { [key: string]: any }; - }; - /** - * A dictionary of extra context to attach to the call. - * https://segment.com/docs/spec/common/#context - */ - context?: Context; -} - -/** - * Context is a dictionary of extra information that provides useful context about a datapoint. - * @see {@link https://segment.com/docs/spec/common/#context} - */ -export interface Context extends Record { - active?: boolean; - app?: { - name?: string; - version?: string; - build?: string; - }; - campaign?: { - name?: string; - source?: string; - medium?: string; - term?: string; - content?: string; - }; - device?: { - id?: string; - manufacturer?: string; - model?: string; - name?: string; - type?: string; - version?: string; - }; - ip?: string; - locale?: string; - location?: { - city?: string; - country?: string; - latitude?: string; - longitude?: string; - region?: string; - speed?: string; - }; - network?: { - bluetooth?: string; - carrier?: string; - cellular?: string; - wifi?: string; - }; - os?: { - name?: string; - version?: string; - }; - page?: { - hash?: string; - path?: string; - referrer?: string; - search?: string; - title?: string; - url?: string; - }; - referrer?: { - type?: string; - name?: string; - url?: string; - link?: string; - }; - screen?: { - density?: string; - height?: string; - width?: string; - }; - timezone?: string; - groupId?: string; - traits?: Record; - userAgent?: string; -} +export type Callback = Parameters[1] export type ViolationHandler = ( message: TrackMessage>, @@ -166,18 +74,16 @@ let onViolation = defaultValidationErrorHandler const missingAnalyticsNodeError = new Error(`You must set an analytics-node instance: -> const SegmentAnalytics = require('analytics-node') -> const { setTypewriterOptions } = require('./analytics') +> import { Analytics } from '@segment/analytics-node' +> import { setTypewriterOptions } from './analytics' > -> const analytics = new SegmentAnalytics('SEGMENT_WRITE_KEY') -> setTypewriterOptions({ -> analytics: analytics, -> }) +> const analytics = new Analytics({ writeKey: 'SEGMENT_WRITE_KEY' }) +> setTypewriterOptions({ analytics: analytics }) -For more information on analytics-node, see: https://segment.com/docs/sources/server/node/quickstart/ +For more information on @segment/analytics-node, see: https://segment.com/docs/sources/server/node/quickstart/ `) -let analytics: () => AnalyticsNode | undefined = () => { +let analytics: () => Analytics | undefined = () => { throw missingAnalyticsNodeError } @@ -186,7 +92,7 @@ export interface TypewriterOptions { /** * Underlying analytics instance where analytics calls are forwarded on to. */ - analytics: AnalyticsNode + analytics: Analytics /** * Handler fired when if an event does not match its spec. This handler * does not fire in production mode, because it requires inlining the full @@ -206,7 +112,7 @@ export interface TypewriterOptions { * @param {TypewriterOptions} options - the options to upsert * * @typedef {Object} TypewriterOptions - * @property {AnalyticsNode} analytics - Underlying analytics instance where analytics + * @property {Analytics} analytics - Underlying analytics instance where analytics * calls are forwarded on to. * @property {Function} [onViolation] - Handler fired when if an event does not match its spec. This handler does not fire in * production mode, because it requires inlining the full JSON Schema spec for each event in your Tracking Plan. By default, @@ -241,9 +147,9 @@ function validateAgainstSchema( * Helper to attach metadata on Typewriter to outbound requests. * This is used for attribution and debugging by the Segment team. */ -function withTypewriterContext>( +function withTypewriterContext

, T extends TrackMessage

>( message: T -): T { +) { return { ...message, context: { @@ -300,7 +206,7 @@ const clientAPI = { * @param {TypewriterOptions} options - the options to upsert * * @typedef {Object} TypewriterOptions - * @property {AnalyticsNode} analytics - Underlying analytics instance where analytics + * @property {Analytics} analytics - Underlying analytics instance where analytics * calls are forwarded on to. * @property {Function} [onViolation] - Handler fired when if an event does not match its spec. This handler does not fire in * production mode, because it requires inlining the full JSON Schema spec for each event in your Tracking Plan. By default, diff --git a/src/telemetry/index.ts b/src/telemetry/index.ts index 69e332d5..a757b1c0 100644 --- a/src/telemetry/index.ts +++ b/src/telemetry/index.ts @@ -1,5 +1,6 @@ +import ciDetect from "@npmcli/ci-detect"; import { Config } from "@oclif/core"; -import Analytics from "analytics-node"; +import { Analytics } from "@segment/analytics-node"; import { machineIdSync } from "node-machine-id"; import { TokenMethod, WorkspaceConfig } from "../config"; import typewriterClient, { @@ -19,9 +20,9 @@ const writeKey = : // Development: https://app.segment.com/segment_prod/sources/typewriter_next_dev/overview "WoCqTlHJKOb9D8NepuSLItTGEkXxLKVV"; -const segmentClient = new Analytics(writeKey, { - flushAt: 1, - flushInterval: -1, +const segmentClient = new Analytics({ + writeKey, + maxEventsInBatch: 1, // We won't do anything if the analytics client is unable to contact Segment, just prevent a crash // @ts-ignore errorHandler is not showing up in the analytics-node types but it is publicly supported errorHandler: () => {}, @@ -110,11 +111,16 @@ const getSegmentClient = (config: Config) => { let anonymousId = "unknown"; try { anonymousId = machineIdSync(); - } catch (error) { + } catch (error: any) { typewriterClient.commandError( withContext( { - error: `Failed to generate an anonymous id: ${error}`, + properties: { + error: error, + isCI: `${ciDetect()}`, + rawCommand: '', + errorMessage: `Failed to generate an anonymous id: ${error}`, + }, }, config, anonymousId @@ -143,8 +149,8 @@ const getSegmentClient = (config: Config) => { config, anonymousId ), - flush: () => { - segmentClient.flush(); + closeAndFlush: () => { + return segmentClient.closeAndFlush(); }, }; }; diff --git a/src/telemetry/segment.ts b/src/telemetry/segment.ts index 9fd83479..2e715242 100644 --- a/src/telemetry/segment.ts +++ b/src/telemetry/segment.ts @@ -248,123 +248,31 @@ export interface VersionCommand { version: string; } -import AnalyticsNode from 'analytics-node' /** -* At least one of userId or anonymousId must be included in any identify call. -*/ -type Identity = { userId: string | number } | { anonymousId: string | number }; + * You can install `@segment/analytics-node` by following instructions at: + * https://segment.com/docs/connections/sources/catalog/libraries/server/node + */ +import { Analytics, TrackParams } from '@segment/analytics-node' + +/** + * An ID associated with the user. Note: at least one of userId or anonymousId must be included! + **/ +type Identity = + | { userId: string; anonymousId?: string } + | { userId?: string; anonymousId: string } /** * TrackMessage represents a message payload for an analytics `.track()` call. * See: https://segment.com/docs/spec/track/ */ -export type TrackMessage = Options & Record & Identity & { - /** A dictionary of properties for the event. */ - properties?: PropertiesType - /** - * A Javascript date object representing when the track took place. - * If the track just happened, leave it out and we’ll use the server’s - * time. If you’re importing data from the past make sure you to send - * a timestamp. - */ - timestamp?: Date - /** - * MessageId which can be optionally set to override the default one generated by the library. - * This is useful when you want to deduplicate messages. - */ - messageId?: string -} +export type TrackMessage = Omit< + TrackParams, + 'event' | 'properties' +> & { event?: string, properties: PropertiesType } & Identity /** The callback exposed by analytics-node. */ -export type Callback = (err: Error) => void - -/** A dictionary of options. For example, enable or disable specific destinations for the call. */ -export interface Options { - /** - * Selectivly filter destinations. By default all destinations are enabled. - * https://segment.com/docs/sources/website/analytics.js/#selecting-destinations - */ - integrations?: { - [key: string]: boolean | { [key: string]: any }; - }; - /** - * A dictionary of extra context to attach to the call. - * https://segment.com/docs/spec/common/#context - */ - context?: Context; -} - -/** - * Context is a dictionary of extra information that provides useful context about a datapoint. - * @see {@link https://segment.com/docs/spec/common/#context} - */ -export interface Context extends Record { - active?: boolean; - app?: { - name?: string; - version?: string; - build?: string; - }; - campaign?: { - name?: string; - source?: string; - medium?: string; - term?: string; - content?: string; - }; - device?: { - id?: string; - manufacturer?: string; - model?: string; - name?: string; - type?: string; - version?: string; - }; - ip?: string; - locale?: string; - location?: { - city?: string; - country?: string; - latitude?: string; - longitude?: string; - region?: string; - speed?: string; - }; - network?: { - bluetooth?: string; - carrier?: string; - cellular?: string; - wifi?: string; - }; - os?: { - name?: string; - version?: string; - }; - page?: { - hash?: string; - path?: string; - referrer?: string; - search?: string; - title?: string; - url?: string; - }; - referrer?: { - type?: string; - name?: string; - url?: string; - link?: string; - }; - screen?: { - density?: string; - height?: string; - width?: string; - }; - timezone?: string; - groupId?: string; - traits?: Record; - userAgent?: string; -} +export type Callback = Parameters[1] export type ViolationHandler = ( message: TrackMessage>, @@ -401,18 +309,16 @@ export type ViolationHandler = ( const missingAnalyticsNodeError = new Error(`You must set an analytics-node instance: -> const SegmentAnalytics = require('analytics-node') -> const { setTypewriterOptions } = require('./analytics') +> import { Analytics } from '@segment/analytics-node' +> import { setTypewriterOptions } from './analytics' > -> const analytics = new SegmentAnalytics('SEGMENT_WRITE_KEY') -> setTypewriterOptions({ -> analytics: analytics, -> }) +> const analytics = new Analytics({ writeKey: 'SEGMENT_WRITE_KEY' }) +> setTypewriterOptions({ analytics: analytics }) -For more information on analytics-node, see: https://segment.com/docs/sources/server/node/quickstart/ +For more information on @segment/analytics-node, see: https://segment.com/docs/sources/server/node/quickstart/ `) -let analytics: () => AnalyticsNode | undefined = () => { +let analytics: () => Analytics | undefined = () => { throw missingAnalyticsNodeError } @@ -421,7 +327,7 @@ export interface TypewriterOptions { /** * Underlying analytics instance where analytics calls are forwarded on to. */ - analytics: AnalyticsNode + analytics: Analytics /** * Handler fired when if an event does not match its spec. This handler * does not fire in production mode, because it requires inlining the full @@ -441,7 +347,7 @@ export interface TypewriterOptions { * @param {TypewriterOptions} options - the options to upsert * * @typedef {Object} TypewriterOptions - * @property {AnalyticsNode} analytics - Underlying analytics instance where analytics + * @property {Analytics} analytics - Underlying analytics instance where analytics * calls are forwarded on to. * @property {Function} [onViolation] - Handler fired when if an event does not match its spec. This handler does not fire in * production mode, because it requires inlining the full JSON Schema spec for each event in your Tracking Plan. By default, @@ -457,9 +363,9 @@ export function setTypewriterOptions(options: TypewriterOptions) { * Helper to attach metadata on Typewriter to outbound requests. * This is used for attribution and debugging by the Segment team. */ -function withTypewriterContext>( +function withTypewriterContext

, T extends TrackMessage

>( message: T -): T { +) { return { ...message, context: { @@ -645,7 +551,7 @@ const clientAPI = { * @param {TypewriterOptions} options - the options to upsert * * @typedef {Object} TypewriterOptions - * @property {AnalyticsNode} analytics - Underlying analytics instance where analytics + * @property {Analytics} analytics - Underlying analytics instance where analytics * calls are forwarded on to. * @property {Function} [onViolation] - Handler fired when if an event does not match its spec. This handler does not fire in * production mode, because it requires inlining the full JSON Schema spec for each event in your Tracking Plan. By default, diff --git a/yarn.lock b/yarn.lock index dc79395d..d7eef5fd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1069,6 +1069,17 @@ tslib "^2.4.1" unfetch "^4.1.0" +"@segment/analytics-node@^1.0.0-beta.26": + version "1.0.0-beta.26" + resolved "https://registry.yarnpkg.com/@segment/analytics-node/-/analytics-node-1.0.0-beta.26.tgz#caca5ab9a45546cb6f91320783328f561129587f" + integrity sha512-5wIQdSme61k1nIAOaj554ndam0a/13gNhNM2dNBlBHi1HsieDuMg5Qz2e2Hrs1dA4n0psmQmXdBcJxH5BP3jYA== + dependencies: + "@lukeed/uuid" "^2.0.0" + "@segment/analytics-core" "1.2.4" + buffer "^6.0.3" + node-fetch "^2.6.7" + tslib "^2.4.1" + "@segment/analytics-react-native@^2.2.1": version "2.7.2" resolved "https://registry.yarnpkg.com/@segment/analytics-react-native/-/analytics-react-native-2.7.2.tgz#1666e9512ad3738154976673afed8673a8e5834f" @@ -1110,14 +1121,6 @@ resolved "https://registry.yarnpkg.com/@segment/isodate/-/isodate-1.0.3.tgz#f44e8202d5edd277ce822785239474b2c9411d4a" integrity sha512-BtanDuvJqnACFkeeYje7pWULVv8RgZaqKHWwGFnL/g/TH/CcZjkIVTfGDp/MAxmilYHUkrX70SqwnYSTNEaN7A== -"@segment/loosely-validate-event@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@segment/loosely-validate-event/-/loosely-validate-event-2.0.0.tgz#87dfc979e5b4e7b82c5f1d8b722dfd5d77644681" - integrity sha512-ZMCSfztDBqwotkl848ODgVcAmN4OItEWDCkshcKz0/W6gGSQayuuCtWV/MlodFivAZD793d6UgANd6wCXUfrIw== - dependencies: - component-type "^1.2.1" - join-component "^1.1.0" - "@segment/sovran-react-native@^0.4.4": version "0.4.4" resolved "https://registry.yarnpkg.com/@segment/sovran-react-native/-/sovran-react-native-0.4.4.tgz#2f1e1c0280d2cc3f242970b7c76f8e1dd3ddd18e" @@ -2076,11 +2079,6 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== -"@types/analytics-node@^3.1.9": - version "3.1.9" - resolved "https://registry.yarnpkg.com/@types/analytics-node/-/analytics-node-3.1.9.tgz#1b71e485658dc5501f72a35fdcf30b857a90db9c" - integrity sha512-C7L7/Dd/CmBST7AvgoCnf4yI9h6W5aYfCOcfUZS5GHlXDQjnXLVOUDpTdwTIZjW4lKECeil6+G+Ul6Xzwv/P0g== - "@types/babel__core@^7.1.14": version "7.1.19" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.19.tgz#7b497495b7d1b4812bdb9d02804d0576f43ee460" @@ -2574,19 +2572,6 @@ ajv@^8.11.0: require-from-string "^2.0.2" uri-js "^4.2.2" -analytics-node@segmentio/analytics-node: - version "6.2.0" - resolved "https://codeload.github.com/segmentio/analytics-node/tar.gz/00c88ef91547f3e6ebd79b2e92e911c65fdc65fc" - dependencies: - "@segment/loosely-validate-event" "^2.0.0" - axios "^0.27.2" - axios-retry "3.2.0" - lodash.isstring "^4.0.1" - md5 "^2.2.1" - ms "^2.0.0" - remove-trailing-slash "^0.1.0" - uuid "^8.3.2" - ansi-colors@4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" @@ -2787,21 +2772,6 @@ aws-sdk@^2.1116.0: uuid "8.0.0" xml2js "0.4.19" -axios-retry@3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/axios-retry/-/axios-retry-3.2.0.tgz#eb48e72f90b177fde62329b2896aa8476cfb90ba" - integrity sha512-RK2cLMgIsAQBDhlIsJR5dOhODPigvel18XUv1dDXW+4k1FzebyfRk+C+orot6WPZOYFKSfhLwHPwVmTVOODQ5w== - dependencies: - is-retry-allowed "^1.1.0" - -axios@^0.27.2: - version "0.27.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" - integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== - dependencies: - follow-redirects "^1.14.9" - form-data "^4.0.0" - babel-jest@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-28.1.3.tgz#c1187258197c099072156a0a121c11ee1e3917d5" @@ -3001,6 +2971,14 @@ buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + builtin-modules@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" @@ -3184,11 +3162,6 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -charenc@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" - integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA== - check-error@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" @@ -3390,7 +3363,7 @@ colors@1.0.3: resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" integrity sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw== -combined-stream@^1.0.6, combined-stream@^1.0.8: +combined-stream@^1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -3412,11 +3385,6 @@ commondir@^1.0.1: resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== -component-type@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/component-type/-/component-type-1.2.1.tgz#8a47901700238e4fc32269771230226f24b415a9" - integrity sha512-Kgy+2+Uwr75vAi6ChWXgHuLvd+QLD7ssgpaRq2zCvt80ptvAfMc/hijcJxXkBa2wMlEZcJvC2H8Ubo+A9ATHIg== - concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -3512,11 +3480,6 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -crypt@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" - integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow== - dargs@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" @@ -4515,11 +4478,6 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== -follow-redirects@^1.14.9: - version "1.15.1" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5" - integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA== - for-each@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -4536,15 +4494,6 @@ form-data@^2.5.0: combined-stream "^1.0.6" mime-types "^2.1.12" -form-data@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" - integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - fs-constants@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" @@ -5042,7 +4991,7 @@ ieee754@1.1.13: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== -ieee754@^1.1.13, ieee754@^1.1.4: +ieee754@^1.1.13, ieee754@^1.1.4, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -5183,11 +5132,6 @@ is-boolean-object@^1.1.0: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-buffer@~1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== - is-builtin-module@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.0.tgz#bb0310dfe881f144ca83f30100ceb10cf58835e0" @@ -5934,11 +5878,6 @@ joi@^17.6.0: "@sideway/formula" "^3.0.0" "@sideway/pinpoint" "^2.0.0" -join-component@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/join-component/-/join-component-1.1.0.tgz#b8417b750661a392bee2c2537c68b2a9d4977cd5" - integrity sha512-bF7vcQxbODoGK1imE2P9GS9aw4zD0Sd+Hni68IMZLj7zRnquH7dXUmMw9hDI5S/Jzt7q+IyTXN0rSg2GI0IKhQ== - js-base64@^2.4.3: version "2.6.4" resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.6.4.tgz#f4e686c5de1ea1f867dbcad3d46d969428df98c4" @@ -6165,11 +6104,6 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" -lodash.isstring@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" - integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== - lodash.memoize@4.x: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -6297,15 +6231,6 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" -md5@^2.2.1: - version "2.3.0" - resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f" - integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g== - dependencies: - charenc "0.0.2" - crypt "0.0.2" - is-buffer "~1.1.6" - "mem-fs-editor@^8.1.2 || ^9.0.0": version "9.5.0" resolved "https://registry.yarnpkg.com/mem-fs-editor/-/mem-fs-editor-9.5.0.tgz#9368724bd37f76eebfcf24d71fc6624b01588969" @@ -7586,11 +7511,6 @@ remove-trailing-separator@^1.0.1: resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" integrity sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw== -remove-trailing-slash@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/remove-trailing-slash/-/remove-trailing-slash-0.1.1.tgz#be2285a59f39c74d1bce4f825950061915e3780d" - integrity sha512-o4S4Qh6L2jpnCy83ysZDau+VORNvnFw07CKSAymkd6ICNVEPisMyzlc00KlvvicsxKck94SEwhDnMNdICzO+tA== - replace-ext@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.1.tgz#2d6d996d04a15855d967443631dd5f77825b016a" @@ -8615,11 +8535,6 @@ uuid@8.0.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.0.0.tgz#bc6ccf91b5ff0ac07bbcdbf1c7c4e150db4dbb6c" integrity sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw== -uuid@^8.3.2: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"