├── .nvmrc ├── .gitattributes ├── packages ├── browser │ ├── .gitignore │ ├── .lintstagedrc.js │ ├── src │ │ ├── core │ │ │ ├── constants │ │ │ │ └── index.ts │ │ │ ├── callback │ │ │ │ └── index.ts │ │ │ ├── environment │ │ │ │ └── index.ts │ │ │ ├── connection │ │ │ │ ├── index.ts │ │ │ │ └── __tests__ │ │ │ │ │ └── index.test.ts │ │ │ ├── page │ │ │ │ └── index.ts │ │ │ ├── stats │ │ │ │ ├── __tests__ │ │ │ │ │ └── index.test.ts │ │ │ │ ├── index.ts │ │ │ │ └── metric-helpers.ts │ │ │ ├── query-string │ │ │ │ ├── gracefulDecodeURIComponent.ts │ │ │ │ ├── __tests__ │ │ │ │ │ ├── gracefulDecodeURIComponent.test.ts │ │ │ │ │ └── pickPrefix.test.ts │ │ │ │ └── pickPrefix.ts │ │ │ ├── inspector │ │ │ │ └── index.ts │ │ │ ├── storage │ │ │ │ ├── memoryStorage.ts │ │ │ │ ├── settings.ts │ │ │ │ └── __tests__ │ │ │ │ │ └── test-helpers.ts │ │ │ ├── context │ │ │ │ └── index.ts │ │ │ ├── events │ │ │ │ └── interfaces.ts │ │ │ ├── queue │ │ │ │ └── event-queue.ts │ │ │ └── plugin │ │ │ │ └── index.ts │ │ ├── generated │ │ │ ├── version.ts │ │ │ └── __tests__ │ │ │ │ └── version.test.ts │ │ ├── lib │ │ │ ├── sleep.ts │ │ │ ├── priority-queue │ │ │ │ ├── index.ts │ │ │ │ ├── backoff.ts │ │ │ │ └── __tests__ │ │ │ │ │ └── backoff.test.ts │ │ │ ├── __tests__ │ │ │ │ ├── get-process-env.test.ts │ │ │ │ ├── embedded-write-key.test.ts │ │ │ │ ├── is-thenable.test.ts │ │ │ │ ├── pick.test.ts │ │ │ │ └── pick.typedef.ts │ │ │ ├── is-thenable.ts │ │ │ ├── version-type.ts │ │ │ ├── fetch.ts │ │ │ ├── p-while.ts │ │ │ ├── get-process-env.ts │ │ │ ├── get-global.ts │ │ │ ├── client-hints │ │ │ │ └── index.ts │ │ │ ├── bind-all.ts │ │ │ ├── embedded-write-key.ts │ │ │ ├── is-plan-event-enabled.ts │ │ │ ├── pick.ts │ │ │ ├── browser-polyfill.ts │ │ │ ├── group-by.ts │ │ │ ├── csp-detection.ts │ │ │ ├── global-analytics-helper.ts │ │ │ ├── to-facade.ts │ │ │ └── on-page-change.ts │ │ ├── test-helpers │ │ │ ├── fixtures │ │ │ │ ├── index.ts │ │ │ │ ├── page-context.ts │ │ │ │ ├── client-hints.ts │ │ │ │ └── classic-destination.ts │ │ │ ├── fetch-parse.ts │ │ │ ├── test-writekeys.ts │ │ │ ├── helpers.ts │ │ │ ├── type-assertions.ts │ │ │ └── factories.ts │ │ ├── tester │ │ │ ├── __fixtures__ │ │ │ │ └── index.html │ │ │ ├── server.js │ │ │ └── ajs-perf.ts │ │ ├── node │ │ │ ├── node.browser.ts │ │ │ └── __tests__ │ │ │ │ └── node-integration.test.ts │ │ ├── plugins │ │ │ ├── segmentio │ │ │ │ └── ratelimit-error.ts │ │ │ └── legacy-video-plugins │ │ │ │ └── index.ts │ │ ├── browser │ │ │ ├── standalone-interface.ts │ │ │ └── browser-umd.ts │ │ ├── vendor │ │ │ └── tsub │ │ │ │ └── types.ts │ │ └── index.ts │ ├── architecture │ │ └── architecture.png │ ├── scripts │ │ ├── build-prep.sh │ │ ├── run.sh │ │ ├── vendor │ │ │ ├── README.md │ │ │ ├── webpack.config.vendor.js │ │ │ ├── run.js │ │ │ └── helpers.js │ │ ├── ci.sh │ │ ├── release.sh │ │ └── umd-diff.sh │ ├── .eslintrc.js │ ├── tsconfig.build.json │ ├── tsconfig.json │ ├── jest.config.js │ ├── jest.setup.js │ ├── qa │ │ ├── lib │ │ │ └── browser.ts │ │ └── README.md │ ├── e2e-tests │ │ └── local-server.ts │ └── LICENSE.MD ├── generic-utils │ ├── src │ │ ├── emitter │ │ │ └── index.ts │ │ ├── create-deferred │ │ │ ├── index.ts │ │ │ └── create-deferred.ts │ │ └── index.ts │ ├── .lintstagedrc.js │ ├── jest.config.js │ ├── .eslintrc.js │ ├── README.md │ ├── tsconfig.build.json │ ├── tsconfig.json │ └── LICENSE ├── consent │ ├── consent-tools-integration-tests │ │ ├── .gitignore │ │ ├── .lintstagedrc.js │ │ ├── .eslintrc.js │ │ ├── src │ │ │ ├── types │ │ │ │ └── analytics.d.ts │ │ │ ├── page-bundles │ │ │ │ ├── onetrust │ │ │ │ │ └── index.ts │ │ │ │ ├── consent-tools-vanilla-opt-in │ │ │ │ │ └── index.ts │ │ │ │ ├── consent-tools-vanilla-opt-out │ │ │ │ │ └── index.ts │ │ │ │ └── helpers │ │ │ │ │ └── mock-cmp-wrapper.ts │ │ │ ├── tests │ │ │ │ └── onetrust.test.ts │ │ │ └── page-objects │ │ │ │ └── consent-tools-vanilla.ts │ │ ├── tsconfig.json │ │ ├── playwright.global-setup.ts │ │ ├── public │ │ │ ├── consent-tools-vanilla-opt-in.html │ │ │ └── consent-tools-vanilla-opt-out.html │ │ └── README.md │ ├── consent-tools │ │ ├── src │ │ │ ├── domain │ │ │ │ ├── validation │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── __tests__ │ │ │ │ │ │ └── validation-error.test.ts │ │ │ │ │ ├── validation-error.ts │ │ │ │ │ └── common-validators.ts │ │ │ │ ├── analytics │ │ │ │ │ └── index.ts │ │ │ │ ├── logger.ts │ │ │ │ ├── pruned-categories.ts │ │ │ │ ├── consent-stamping.ts │ │ │ │ ├── __tests__ │ │ │ │ │ ├── typedef-tests.ts │ │ │ │ │ └── assertions │ │ │ │ │ │ └── integrations-assertions.ts │ │ │ │ └── config-helpers.ts │ │ │ ├── test-helpers │ │ │ │ └── mocks │ │ │ │ │ ├── index.ts │ │ │ │ │ └── analytics-mock.ts │ │ │ ├── types │ │ │ │ ├── index.ts │ │ │ │ └── errors.ts │ │ │ ├── utils │ │ │ │ ├── uniq.ts │ │ │ │ ├── index.ts │ │ │ │ ├── pick.ts │ │ │ │ ├── pipe.ts │ │ │ │ ├── resolve-when.ts │ │ │ │ └── ts-helpers.ts │ │ │ └── index.ts │ │ ├── .lintstagedrc.js │ │ ├── .eslintrc.js │ │ ├── jest.config.js │ │ ├── jest.setup.js │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── consent-wrapper-onetrust │ │ ├── src │ │ │ ├── lib │ │ │ │ └── validation │ │ │ │ │ ├── index.ts │ │ │ │ │ └── onetrust-api-error.ts │ │ │ ├── index.ts │ │ │ ├── test-helpers │ │ │ │ ├── onetrust-globals.d.ts │ │ │ │ └── utils.ts │ │ │ └── index.umd.ts │ │ ├── .lintstagedrc.js │ │ ├── img │ │ │ ├── onetrust-popup.jpg │ │ │ ├── consent-mgmt-ui.png │ │ │ └── onetrust-cat-id.jpg │ │ ├── .eslintrc.js │ │ ├── jest.config.js │ │ ├── tsconfig.build.json │ │ ├── jest.setup.js │ │ ├── tsconfig.json │ │ ├── webpack.config.js │ │ └── bundle-build-tests │ │ │ └── global.test.ts │ └── README.md ├── core │ ├── src │ │ ├── user │ │ │ └── index.ts │ │ ├── validation │ │ │ ├── errors.ts │ │ │ └── helpers.ts │ │ ├── utils │ │ │ ├── has-properties.ts │ │ │ ├── pick.ts │ │ │ ├── is-thenable.ts │ │ │ ├── p-while.ts │ │ │ ├── ts-helpers.ts │ │ │ ├── get-global.ts │ │ │ ├── bind-all.ts │ │ │ ├── group-by.ts │ │ │ ├── is-plain-object.ts │ │ │ └── __tests__ │ │ │ │ └── is-thenable.test.ts │ │ ├── analytics │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── task │ │ │ ├── __tests__ │ │ │ │ └── task-group.test.ts │ │ │ └── task-group.ts │ │ ├── priority-queue │ │ │ ├── backoff.ts │ │ │ └── __tests__ │ │ │ │ └── backoff.test.ts │ │ └── emitter │ │ │ └── interface.ts │ ├── .lintstagedrc.js │ ├── test-helpers │ │ ├── index.ts │ │ ├── test-event-queue.ts │ │ └── test-ctx.ts │ ├── jest.config.js │ ├── .eslintrc.js │ ├── README.md │ ├── tsconfig.build.json │ ├── tsconfig.json │ ├── jest.setup.js │ └── LICENSE.MD ├── node │ ├── src │ │ ├── lib │ │ │ ├── uuid.ts │ │ │ ├── base-64-encode.ts │ │ │ ├── create-url.ts │ │ │ ├── get-message-id.ts │ │ │ ├── fetch.ts │ │ │ ├── __tests__ │ │ │ │ └── get-message-id.test.ts │ │ │ └── env.ts │ │ ├── generated │ │ │ └── version.ts │ │ ├── app │ │ │ ├── types │ │ │ │ ├── index.ts │ │ │ │ ├── plugin.ts │ │ │ │ └── segment-event.ts │ │ │ ├── context.ts │ │ │ ├── event-queue.ts │ │ │ └── emitter.ts │ │ ├── __tests__ │ │ │ ├── test-helpers │ │ │ │ ├── assert-shape │ │ │ │ │ ├── index.ts │ │ │ │ │ └── http-request-event.ts │ │ │ │ ├── sleep.ts │ │ │ │ ├── is-valid-date.ts │ │ │ │ ├── resolve-emitter.ts │ │ │ │ ├── factories.ts │ │ │ │ ├── test-plugin.ts │ │ │ │ └── resolve-ctx.ts │ │ │ ├── settings.test.ts │ │ │ ├── plugins.test.ts │ │ │ └── argument-validation.integration.test.ts │ │ ├── index.ts │ │ └── index.common.ts │ ├── .lintstagedrc.js │ ├── jest.config.js │ ├── jest.setup.js │ ├── tsconfig.build.json │ ├── .eslintrc.js │ ├── tsconfig.json │ ├── scripts │ │ └── version.sh │ └── LICENSE ├── test-helpers │ ├── src │ │ ├── jest │ │ │ └── serializers │ │ │ │ ├── index.ts │ │ │ │ └── timestamp.ts │ │ ├── analytics │ │ │ └── index.ts │ │ ├── utils │ │ │ ├── index.ts │ │ │ ├── sleep.ts │ │ │ └── promise-timeout.ts │ │ ├── index.ts │ │ └── constants │ │ │ └── index.ts │ ├── .lintstagedrc.js │ ├── jest.config.js │ ├── .eslintrc.js │ ├── tsconfig.build.json │ ├── tsconfig.json │ └── package.json ├── page-tools │ ├── tsup.config.js │ ├── .lintstagedrc.js │ ├── README.md │ ├── .eslintrc.js │ ├── jest.config.js │ ├── tsconfig.json │ ├── src │ │ └── index.ts │ ├── CHANGELOG.md │ └── tsconfig.build.json ├── browser-integration-tests │ ├── .lintstagedrc.js │ ├── .eslintrc.js │ ├── jest.config.js │ ├── src │ │ ├── shims.d.ts │ │ └── helpers │ │ │ └── extract-writekey.ts │ ├── README.md │ ├── tsconfig.json │ └── package.json ├── core-integration-tests │ ├── .lintstagedrc.js │ ├── .eslintrc.js │ ├── jest.config.js │ ├── src │ │ ├── public-api.test.ts │ │ └── typedef-tests.ts │ ├── tsconfig.json │ ├── README.md │ └── package.json ├── node-integration-tests │ ├── .lintstagedrc.js │ ├── .eslintrc.js │ ├── jest.config.js │ ├── src │ │ ├── server │ │ │ ├── types.ts │ │ │ ├── fetch-polyfill.ts │ │ │ ├── fixtures.ts │ │ │ ├── nock.ts │ │ │ └── autocannon.ts │ │ ├── cloudflare-tests │ │ │ └── workers │ │ │ │ ├── README.md │ │ │ │ ├── forgot-close-and-flush.ts │ │ │ │ ├── send-single-event.ts │ │ │ │ ├── send-multiple-events.ts │ │ │ │ └── send-each-event-type.ts │ │ ├── perf-tests │ │ │ ├── server-start-no-analytics.ts │ │ │ ├── server-start-old-analytics.ts │ │ │ └── server-start-analytics.ts │ │ ├── smoke │ │ │ └── smoke.ts │ │ └── durability-tests │ │ │ ├── server-start-analytics.ts │ │ │ └── durability-tests.ts │ └── tsconfig.json ├── config │ ├── src │ │ ├── lint-staged │ │ │ └── config.js │ │ └── index.js │ └── package.json ├── config-webpack │ ├── README.md │ └── package.json └── config-tsup │ ├── package.json │ └── config.js ├── example.png ├── playgrounds ├── with-vite │ ├── src │ │ ├── vite-env.d.ts │ │ ├── main.tsx │ │ ├── index.css │ │ └── App.css │ ├── vite.config.ts │ ├── tsconfig.node.json │ ├── .gitignore │ ├── index.html │ ├── tsconfig.json │ └── package.json ├── standalone-playground │ ├── README.md │ ├── index.html │ └── package.json ├── README.md └── next-playground │ ├── public │ ├── fira-code.webfont │ │ ├── fira-code_regular.eot │ │ ├── fira-code_regular.ttf │ │ ├── fira-code_regular.woff │ │ └── webfont.css │ └── demos │ │ ├── faulty-load.js │ │ ├── faulty-track.js │ │ ├── faulty-middleware.js │ │ ├── signals.js │ │ └── identity.js │ ├── .eslintrc.js │ ├── next-env.d.ts │ ├── pages │ ├── _app.tsx │ ├── iframe │ │ ├── childPage.tsx │ │ └── index.tsx │ └── vanilla │ │ ├── other-page.tsx │ │ └── index.tsx │ ├── utils │ └── hooks │ │ ├── useConfig.ts │ │ └── useDidMountEffect.ts │ ├── .lintstagedrc.js │ ├── styles │ ├── dracula │ │ ├── card.css │ │ ├── badge.css │ │ ├── dracula-ui.css │ │ ├── typography.css │ │ └── avatar.css │ ├── logs-table.css │ └── globals.css │ ├── README.md │ ├── .gitignore │ ├── next.config.js │ └── tsconfig.json ├── .husky ├── pre-push └── pre-commit ├── scripts ├── .lintstagedrc.js ├── env.d.ts ├── jest.config.js ├── .eslintrc.js ├── create-release-from-tags │ ├── run.ts │ └── __tests__ │ │ └── fixtures │ │ ├── first-release-example.md │ │ └── reg-example.md ├── utils │ └── exists.ts ├── clean.sh ├── tsconfig.json ├── update-lockfile.sh └── package.json ├── .github ├── architecture.png ├── CODEOWNERS ├── workflows │ ├── md-link-check.config.json │ └── md-link-check.yml └── PULL_REQUEST_TEMPLATE ├── img └── twilio-segment-logo-2x.png ├── .prettierrc ├── .vscode └── extensions.json ├── .buildkite ├── Dockerfile.agent ├── Makefile └── Readme.md ├── codecov.yml ├── .editorconfig ├── typings ├── get-monorepo-packages.d.ts └── spawn.d.ts ├── .changeset ├── config.json └── README.md ├── .yarnrc.yml ├── tsconfig.json ├── .gitignore ├── .eslintrc.isomorphic.js ├── CONTRIBUTING.md ├── jest.config.js └── DEVELOPMENT.md /.nvmrc: -------------------------------------------------------------------------------- 1 | 20.9.0 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /packages/browser/.gitignore: -------------------------------------------------------------------------------- 1 | umd.old 2 | -------------------------------------------------------------------------------- /packages/generic-utils/src/emitter/index.ts: -------------------------------------------------------------------------------- 1 | export * from './emitter' 2 | -------------------------------------------------------------------------------- /packages/consent/consent-tools-integration-tests/.gitignore: -------------------------------------------------------------------------------- 1 | driver-logs 2 | -------------------------------------------------------------------------------- /packages/core/src/user/index.ts: -------------------------------------------------------------------------------- 1 | export type ID = string | null | undefined 2 | -------------------------------------------------------------------------------- /packages/node/src/lib/uuid.ts: -------------------------------------------------------------------------------- 1 | export { v4 as uuid } from '@lukeed/uuid' 2 | -------------------------------------------------------------------------------- /example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segmentio/analytics-next/HEAD/example.png -------------------------------------------------------------------------------- /packages/test-helpers/src/jest/serializers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './timestamp' 2 | -------------------------------------------------------------------------------- /playgrounds/with-vite/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /.husky/pre-push: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn prepush 5 | -------------------------------------------------------------------------------- /packages/test-helpers/src/analytics/index.ts: -------------------------------------------------------------------------------- 1 | export * from './cdn-settings-builder' 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn lint-staged 5 | -------------------------------------------------------------------------------- /packages/generic-utils/src/create-deferred/index.ts: -------------------------------------------------------------------------------- 1 | export * from './create-deferred' 2 | -------------------------------------------------------------------------------- /scripts/.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require("@internal/config").lintStagedConfig 2 | -------------------------------------------------------------------------------- /packages/core/.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require("@internal/config").lintStagedConfig 2 | -------------------------------------------------------------------------------- /packages/node/.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require("@internal/config").lintStagedConfig 2 | -------------------------------------------------------------------------------- /packages/page-tools/tsup.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('@internal/config-tsup/config') 2 | -------------------------------------------------------------------------------- /packages/browser/.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require("@internal/config").lintStagedConfig 2 | -------------------------------------------------------------------------------- /packages/browser/src/core/constants/index.ts: -------------------------------------------------------------------------------- 1 | export const SEGMENT_API_HOST = 'api.segment.io/v1' 2 | -------------------------------------------------------------------------------- /packages/consent/consent-tools/src/domain/validation/index.ts: -------------------------------------------------------------------------------- 1 | export * from './options-validators' 2 | -------------------------------------------------------------------------------- /packages/consent/consent-tools/src/test-helpers/mocks/index.ts: -------------------------------------------------------------------------------- 1 | export * from './analytics-mock' 2 | -------------------------------------------------------------------------------- /packages/generic-utils/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './create-deferred' 2 | export * from './emitter' 3 | -------------------------------------------------------------------------------- /packages/page-tools/.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require("@internal/config").lintStagedConfig 2 | -------------------------------------------------------------------------------- /.github/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segmentio/analytics-next/HEAD/.github/architecture.png -------------------------------------------------------------------------------- /packages/browser/src/generated/version.ts: -------------------------------------------------------------------------------- 1 | // This file is generated. 2 | export const version = '1.81.1' 3 | -------------------------------------------------------------------------------- /packages/core/test-helpers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './test-ctx' 2 | export * from './test-event-queue' 3 | -------------------------------------------------------------------------------- /packages/generic-utils/.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require("@internal/config").lintStagedConfig 2 | -------------------------------------------------------------------------------- /packages/node/src/generated/version.ts: -------------------------------------------------------------------------------- 1 | // This file is generated. 2 | export const version = '2.3.0' 3 | -------------------------------------------------------------------------------- /packages/test-helpers/.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require("@internal/config").lintStagedConfig 2 | -------------------------------------------------------------------------------- /packages/test-helpers/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './sleep' 2 | export * from './promise-timeout' 3 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | @libraries-web-team 2 | @nettofarah 3 | @juliofarah 4 | @danieljackins 5 | @pooyaj 6 | @dk1027 -------------------------------------------------------------------------------- /packages/consent/consent-tools/.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require("@internal/config").lintStagedConfig 2 | -------------------------------------------------------------------------------- /packages/consent/consent-wrapper-onetrust/src/lib/validation/index.ts: -------------------------------------------------------------------------------- 1 | export * from './onetrust-api-error' 2 | -------------------------------------------------------------------------------- /packages/browser-integration-tests/.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require("@internal/config").lintStagedConfig 2 | -------------------------------------------------------------------------------- /packages/browser/src/core/callback/index.ts: -------------------------------------------------------------------------------- 1 | export { invokeCallback, pTimeout } from '@segment/analytics-core' 2 | -------------------------------------------------------------------------------- /packages/core-integration-tests/.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require("@internal/config").lintStagedConfig 2 | -------------------------------------------------------------------------------- /packages/node-integration-tests/.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require("@internal/config").lintStagedConfig 2 | -------------------------------------------------------------------------------- /img/twilio-segment-logo-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segmentio/analytics-next/HEAD/img/twilio-segment-logo-2x.png -------------------------------------------------------------------------------- /packages/consent/consent-tools/src/domain/analytics/index.ts: -------------------------------------------------------------------------------- 1 | export { AnalyticsService } from './analytics-service' 2 | -------------------------------------------------------------------------------- /packages/consent/consent-wrapper-onetrust/.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require("@internal/config").lintStagedConfig 2 | -------------------------------------------------------------------------------- /scripts/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /packages/consent/consent-tools-integration-tests/.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require("@internal/config").lintStagedConfig 2 | -------------------------------------------------------------------------------- /packages/node/src/app/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from './params' 2 | export * from './segment-event' 3 | export * from './plugin' 4 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "printWidth": 80, 5 | "tabWidth": 2, 6 | "arrowParens": "always" 7 | } -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["esbenp.prettier-vscode", "dbaeumer.vscode-eslint", "wix.vscode-import-cost"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/consent/consent-tools/src/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from './wrapper' 2 | export * from './settings' 3 | export * from './errors' 4 | -------------------------------------------------------------------------------- /packages/node/src/__tests__/test-helpers/assert-shape/index.ts: -------------------------------------------------------------------------------- 1 | export * from './http-request-event' 2 | export * from './segment-http-api' 3 | -------------------------------------------------------------------------------- /packages/page-tools/README.md: -------------------------------------------------------------------------------- 1 | # @segment/analytics-page-tools 2 | 3 | Browser-only helpers for generating page events with analytics.js. 4 | -------------------------------------------------------------------------------- /scripts/jest.config.js: -------------------------------------------------------------------------------- 1 | const { createJestTSConfig } = require('@internal/config') 2 | 3 | module.exports = createJestTSConfig(__dirname) 4 | -------------------------------------------------------------------------------- /packages/core/jest.config.js: -------------------------------------------------------------------------------- 1 | const { createJestTSConfig } = require('@internal/config') 2 | 3 | module.exports = createJestTSConfig(__dirname) 4 | -------------------------------------------------------------------------------- /packages/browser/architecture/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segmentio/analytics-next/HEAD/packages/browser/architecture/architecture.png -------------------------------------------------------------------------------- /packages/page-tools/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** @type { import('eslint').Linter.Config } */ 2 | module.exports = { 3 | extends: ['../../.eslintrc'], 4 | } 5 | -------------------------------------------------------------------------------- /packages/config/src/lint-staged/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | '*.{js,jsx,ts,tsx}': ['eslint --fix'], 3 | '*.json*': ['prettier --write'], 4 | } 5 | -------------------------------------------------------------------------------- /packages/core/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** @type { import('eslint').Linter.Config } */ 2 | module.exports = { 3 | extends: ['../../.eslintrc.isomorphic'], 4 | } 5 | -------------------------------------------------------------------------------- /packages/generic-utils/jest.config.js: -------------------------------------------------------------------------------- 1 | const { createJestTSConfig } = require('@internal/config') 2 | 3 | module.exports = createJestTSConfig(__dirname) 4 | -------------------------------------------------------------------------------- /packages/test-helpers/jest.config.js: -------------------------------------------------------------------------------- 1 | const { createJestTSConfig } = require('@internal/config') 2 | 3 | module.exports = createJestTSConfig(__dirname) 4 | -------------------------------------------------------------------------------- /packages/core-integration-tests/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** @type { import('eslint').Linter.Config } */ 2 | module.exports = { 3 | extends: ['../../.eslintrc'], 4 | } 5 | -------------------------------------------------------------------------------- /packages/generic-utils/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** @type { import('eslint').Linter.Config } */ 2 | module.exports = { 3 | extends: ['../../.eslintrc.isomorphic'], 4 | } 5 | -------------------------------------------------------------------------------- /packages/node-integration-tests/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** @type { import('eslint').Linter.Config } */ 2 | module.exports = { 3 | extends: ['../../.eslintrc'], 4 | } 5 | -------------------------------------------------------------------------------- /packages/browser-integration-tests/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** @type { import('eslint').Linter.Config } */ 2 | module.exports = { 3 | extends: ['../../.eslintrc'], 4 | } 5 | -------------------------------------------------------------------------------- /packages/browser-integration-tests/jest.config.js: -------------------------------------------------------------------------------- 1 | const { createJestTSConfig } = require('@internal/config') 2 | 3 | module.exports = createJestTSConfig(__dirname) 4 | -------------------------------------------------------------------------------- /packages/browser/src/lib/sleep.ts: -------------------------------------------------------------------------------- 1 | export const sleep = (time: number): Promise => 2 | new Promise((resolve) => { 3 | setTimeout(resolve, time) 4 | }) 5 | -------------------------------------------------------------------------------- /packages/core-integration-tests/jest.config.js: -------------------------------------------------------------------------------- 1 | const { createJestTSConfig } = require('@internal/config') 2 | 3 | module.exports = createJestTSConfig(__dirname) 4 | -------------------------------------------------------------------------------- /packages/node-integration-tests/jest.config.js: -------------------------------------------------------------------------------- 1 | const { createJestTSConfig } = require('@internal/config') 2 | 3 | module.exports = createJestTSConfig(__dirname) 4 | -------------------------------------------------------------------------------- /playgrounds/standalone-playground/README.md: -------------------------------------------------------------------------------- 1 | This is a testing playground for the standalone "snippet" version of the app. 2 | 3 | Run: 4 | ``` 5 | yarn start 6 | ``` -------------------------------------------------------------------------------- /.buildkite/Dockerfile.agent: -------------------------------------------------------------------------------- 1 | FROM 528451384384.dkr.ecr.us-west-2.amazonaws.com/buildkite-agent-node20 2 | 3 | RUN npx playwright install-deps 4 | 5 | ENTRYPOINT [] 6 | -------------------------------------------------------------------------------- /packages/test-helpers/src/utils/sleep.ts: -------------------------------------------------------------------------------- 1 | export const sleep = (time: number): Promise => 2 | new Promise((resolve) => { 3 | setTimeout(resolve, time) 4 | }) 5 | -------------------------------------------------------------------------------- /playgrounds/README.md: -------------------------------------------------------------------------------- 1 | # Application Playground 2 | 3 | These applications are not meant to be vanilla examples. Please refer to the individual READMEs for more information. 4 | -------------------------------------------------------------------------------- /scripts/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** @type { import('eslint').Linter.Config } */ 2 | module.exports = { 3 | extends: ['../.eslintrc'], 4 | env: { 5 | node: true, 6 | }, 7 | } 8 | -------------------------------------------------------------------------------- /packages/browser/src/lib/priority-queue/index.ts: -------------------------------------------------------------------------------- 1 | import { PriorityQueue, ON_REMOVE_FROM_FUTURE } from '@segment/analytics-core' 2 | 3 | export { PriorityQueue, ON_REMOVE_FROM_FUTURE } 4 | -------------------------------------------------------------------------------- /packages/config/src/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | createJestTSConfig: require('./jest/config').createJestTSConfig, 3 | lintStagedConfig: require('./lint-staged/config'), 4 | } 5 | -------------------------------------------------------------------------------- /packages/consent/consent-wrapper-onetrust/img/onetrust-popup.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segmentio/analytics-next/HEAD/packages/consent/consent-wrapper-onetrust/img/onetrust-popup.jpg -------------------------------------------------------------------------------- /packages/node-integration-tests/src/server/types.ts: -------------------------------------------------------------------------------- 1 | export interface ServerReport { 2 | totalBatchEvents: number 3 | totalApiRequests: number 4 | averagePerBatch: number 5 | } 6 | -------------------------------------------------------------------------------- /packages/test-helpers/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './utils' 2 | export * from './analytics' 3 | export * from './constants' 4 | export * as JestSerializers from './jest/serializers' 5 | -------------------------------------------------------------------------------- /packages/consent/consent-tools/src/utils/uniq.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This function removes duplicates from an array 3 | */ 4 | export const uniq = (arr: T[]): T[] => Array.from(new Set(arr)) 5 | -------------------------------------------------------------------------------- /packages/consent/consent-wrapper-onetrust/img/consent-mgmt-ui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segmentio/analytics-next/HEAD/packages/consent/consent-wrapper-onetrust/img/consent-mgmt-ui.png -------------------------------------------------------------------------------- /packages/consent/consent-wrapper-onetrust/img/onetrust-cat-id.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segmentio/analytics-next/HEAD/packages/consent/consent-wrapper-onetrust/img/onetrust-cat-id.jpg -------------------------------------------------------------------------------- /packages/node/src/__tests__/test-helpers/sleep.ts: -------------------------------------------------------------------------------- 1 | export function sleep(timeoutInMs: number): Promise { 2 | return new Promise((resolve) => setTimeout(resolve, timeoutInMs)) 3 | } 4 | -------------------------------------------------------------------------------- /packages/test-helpers/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** @type { import('eslint').Linter.Config } */ 2 | module.exports = { 3 | extends: ['../../.eslintrc'], 4 | env: { 5 | node: true, 6 | }, 7 | } 8 | -------------------------------------------------------------------------------- /packages/consent/consent-tools/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './pipe' 2 | export * from './resolve-when' 3 | export * from './uniq' 4 | export * from './pick' 5 | export * from './ts-helpers' 6 | -------------------------------------------------------------------------------- /packages/page-tools/jest.config.js: -------------------------------------------------------------------------------- 1 | const { createJestTSConfig } = require('@internal/config') 2 | 3 | module.exports = createJestTSConfig(__dirname, { 4 | testEnvironment: 'jsdom', 5 | }) 6 | -------------------------------------------------------------------------------- /packages/node/jest.config.js: -------------------------------------------------------------------------------- 1 | const { createJestTSConfig } = require('@internal/config') 2 | 3 | module.exports = createJestTSConfig(__dirname, { 4 | setupFilesAfterEnv: ['./jest.setup.js'], 5 | }) 6 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: 4 | default: 5 | target: auto 6 | threshold: 0% 7 | base: auto 8 | comment: 9 | show_carryforward_flags: true 10 | -------------------------------------------------------------------------------- /packages/browser/src/test-helpers/fixtures/index.ts: -------------------------------------------------------------------------------- 1 | export * from './page-context' 2 | export * from './create-fetch-method' 3 | export * from './classic-destination' 4 | export * from './cdn-settings' 5 | -------------------------------------------------------------------------------- /packages/browser/src/tester/__fixtures__/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 🍻 7 | 8 | 9 | -------------------------------------------------------------------------------- /packages/config-webpack/README.md: -------------------------------------------------------------------------------- 1 | # @internal/config-webpack 2 | 3 | This package is for sharing basic webpack configuration / browser support between all of the analytics.js artifacts in this monorepo. 4 | -------------------------------------------------------------------------------- /packages/consent/consent-tools/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** @type { import('eslint').Linter.Config } */ 2 | module.exports = { 3 | extends: ['../../../.eslintrc'], 4 | env: { 5 | browser: true, 6 | }, 7 | } 8 | -------------------------------------------------------------------------------- /playgrounds/next-playground/public/fira-code.webfont/fira-code_regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segmentio/analytics-next/HEAD/playgrounds/next-playground/public/fira-code.webfont/fira-code_regular.eot -------------------------------------------------------------------------------- /playgrounds/next-playground/public/fira-code.webfont/fira-code_regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segmentio/analytics-next/HEAD/playgrounds/next-playground/public/fira-code.webfont/fira-code_regular.ttf -------------------------------------------------------------------------------- /playgrounds/next-playground/public/fira-code.webfont/fira-code_regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segmentio/analytics-next/HEAD/playgrounds/next-playground/public/fira-code.webfont/fira-code_regular.woff -------------------------------------------------------------------------------- /packages/browser-integration-tests/src/shims.d.ts: -------------------------------------------------------------------------------- 1 | import type { AnalyticsSnippet } from '@segment/analytics-next' 2 | 3 | declare global { 4 | interface Window { 5 | analytics: AnalyticsSnippet 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/browser/src/core/environment/index.ts: -------------------------------------------------------------------------------- 1 | export function isBrowser(): boolean { 2 | return typeof window !== 'undefined' 3 | } 4 | 5 | export function isServer(): boolean { 6 | return !isBrowser() 7 | } 8 | -------------------------------------------------------------------------------- /packages/consent/consent-wrapper-onetrust/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** @type { import('eslint').Linter.Config } */ 2 | module.exports = { 3 | extends: ['../../../.eslintrc'], 4 | env: { 5 | browser: true, 6 | }, 7 | } 8 | -------------------------------------------------------------------------------- /packages/node/src/__tests__/test-helpers/is-valid-date.ts: -------------------------------------------------------------------------------- 1 | export const isValidDate = (date: string) => { 2 | if (!date) { 3 | throw new Error('no date found.') 4 | } 5 | return !isNaN(Date.parse(date)) 6 | } 7 | -------------------------------------------------------------------------------- /packages/consent/consent-tools-integration-tests/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** @type { import('eslint').Linter.Config } */ 2 | module.exports = { 3 | extends: ['../../../.eslintrc'], 4 | env: { 5 | browser: true, 6 | }, 7 | } 8 | -------------------------------------------------------------------------------- /playgrounds/with-vite/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | ; editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | -------------------------------------------------------------------------------- /packages/browser-integration-tests/README.md: -------------------------------------------------------------------------------- 1 | Core tests that require AnalyticsBrowser, etc. 2 | This exists because we can't create circular dependencies -- so, for example, installing AnalyticsBrowser as a dev dependency on core. -------------------------------------------------------------------------------- /packages/browser/src/lib/__tests__/get-process-env.test.ts: -------------------------------------------------------------------------------- 1 | import { getProcessEnv } from '../get-process-env' 2 | 3 | it('it matches the contents of process.env', () => { 4 | expect(getProcessEnv()).toBe(process.env) 5 | }) 6 | -------------------------------------------------------------------------------- /packages/browser/src/node/node.browser.ts: -------------------------------------------------------------------------------- 1 | export class AnalyticsNode { 2 | static load(): Promise { 3 | return Promise.reject( 4 | new Error('AnalyticsNode is not available in browsers.') 5 | ) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /scripts/create-release-from-tags/run.ts: -------------------------------------------------------------------------------- 1 | import { createReleaseFromTags, getConfig } from '.' 2 | 3 | async function run() { 4 | const config = await getConfig() 5 | return createReleaseFromTags(config) 6 | } 7 | 8 | void run() 9 | -------------------------------------------------------------------------------- /packages/config-tsup/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@internal/config-tsup", 3 | "version": "0.0.0", 4 | "private": true, 5 | "packageManager": "yarn@3.4.1", 6 | "devDependencies": { 7 | "tsup": "^8.4.0" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/consent/consent-tools-integration-tests/src/types/analytics.d.ts: -------------------------------------------------------------------------------- 1 | import type { AnalyticsSnippet } from '@segment/analytics-next' 2 | 3 | declare global { 4 | interface Window { 5 | analytics: AnalyticsSnippet 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/core/src/validation/errors.ts: -------------------------------------------------------------------------------- 1 | export class ValidationError extends Error { 2 | field: string 3 | 4 | constructor(field: string, message: string) { 5 | super(`${field} ${message}`) 6 | this.field = field 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/core/test-helpers/test-event-queue.ts: -------------------------------------------------------------------------------- 1 | import { CoreEventQueue, PriorityQueue } from '../src' 2 | 3 | export class TestEventQueue extends CoreEventQueue { 4 | constructor() { 5 | super(new PriorityQueue(4, [])) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/node/jest.setup.js: -------------------------------------------------------------------------------- 1 | const fetch = require('node-fetch') 2 | 3 | // eslint-disable-next-line no-undef 4 | globalThis.fetch = fetch // polyfill fetch so nock will work correctly on node 18 (https://github.com/nock/nock/issues/2336) 5 | -------------------------------------------------------------------------------- /packages/node/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './index.common' 2 | 3 | // export Analytics as both a named export and a default export (for backwards-compat. reasons) 4 | import { Analytics } from './index.common' 5 | export default Analytics 6 | -------------------------------------------------------------------------------- /packages/core/README.md: -------------------------------------------------------------------------------- 1 | # @segment/analytics-core 2 | 3 | This package represents core 'shared' functionality that is shared by analytics packages. This is not designed to be used directly, but internal to analytics-node and analytics-browser. 4 | -------------------------------------------------------------------------------- /packages/core/test-helpers/test-ctx.ts: -------------------------------------------------------------------------------- 1 | import { CoreContext } from '../src/context' 2 | 3 | export class TestCtx extends CoreContext { 4 | static override system() { 5 | return new this({ type: 'track', event: 'system' }) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/consent/consent-tools/jest.config.js: -------------------------------------------------------------------------------- 1 | const { createJestTSConfig } = require('@internal/config') 2 | 3 | module.exports = createJestTSConfig(__dirname, { 4 | testEnvironment: 'jsdom', 5 | setupFilesAfterEnv: ['./jest.setup.js'], 6 | }) 7 | -------------------------------------------------------------------------------- /playgrounds/next-playground/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** @type { import('eslint').Linter.Config } */ 2 | module.exports = { 3 | extends: ['../../.eslintrc', 'plugin:@next/next/recommended'], 4 | env: { 5 | browser: true, 6 | node: true, 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /packages/consent/consent-tools/jest.setup.js: -------------------------------------------------------------------------------- 1 | const fetch = require('node-fetch') 2 | 3 | // eslint-disable-next-line no-undef 4 | globalThis.fetch = fetch // polyfill fetch so nock will work correctly on node 18 (https://github.com/nock/nock/issues/2336) 5 | -------------------------------------------------------------------------------- /packages/consent/consent-wrapper-onetrust/jest.config.js: -------------------------------------------------------------------------------- 1 | const { createJestTSConfig } = require('@internal/config') 2 | 3 | module.exports = createJestTSConfig(__dirname, { 4 | testEnvironment: 'jsdom', 5 | setupFilesAfterEnv: ['./jest.setup.js'], 6 | }) 7 | -------------------------------------------------------------------------------- /playgrounds/next-playground/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /packages/browser/scripts/build-prep.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | PKG_VERSION=$(node --eval="process.stdout.write(require('./package.json').version)") 3 | 4 | cat <src/generated/version.ts 5 | // This file is generated. 6 | export const version = '$PKG_VERSION' 7 | EOF 8 | -------------------------------------------------------------------------------- /packages/config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@internal/config", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./src", 6 | "packageManager": "yarn@3.4.1", 7 | "devDependencies": { 8 | "app-root-path": "^3.1.0" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /playgrounds/with-vite/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "composite": true, 5 | "module": "esnext", 6 | "moduleResolution": "node" 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /typings/get-monorepo-packages.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'get-monorepo-packages' { 2 | export default function getPackages(pathToRoot: string): { 3 | location: string 4 | package: { 5 | name: string 6 | version: string 7 | } 8 | }[] 9 | } 10 | -------------------------------------------------------------------------------- /packages/browser/scripts/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Allow the service to run without chamber (for ci, docker-compose, etc) 4 | if [ -z "$NO_CHAMBER" ];then 5 | exec chamber exec analytics-next -- node dist/src/boot.js 6 | else 7 | exec node dist/src/boot.js 8 | fi; 9 | -------------------------------------------------------------------------------- /packages/node/src/app/types/plugin.ts: -------------------------------------------------------------------------------- 1 | import type { CorePlugin } from '@segment/analytics-core' 2 | import type { Analytics } from '../analytics-node' 3 | import type { Context } from '../context' 4 | 5 | export interface Plugin extends CorePlugin {} 6 | -------------------------------------------------------------------------------- /packages/node-integration-tests/src/cloudflare-tests/workers/README.md: -------------------------------------------------------------------------------- 1 | These workers can also be ran directly via: 2 | 3 | ``` 4 | npx wrangler dev ./src/cloudflare/workers/ 5 | ``` 6 | 7 | This can be useful if you need to debug a worker with dev tools. 8 | -------------------------------------------------------------------------------- /.buildkite/Makefile: -------------------------------------------------------------------------------- 1 | ECR_REPOSITORY = 528451384384.dkr.ecr.us-west-2.amazonaws.com 2 | IMAGE = ${ECR_REPOSITORY}/analytics-next-ci-agent:latest 3 | 4 | agent: 5 | docker build --pull . -f Dockerfile.agent -t ${IMAGE} 6 | aws-okta exec ops-write -- docker push ${IMAGE} 7 | .PHONY: agent -------------------------------------------------------------------------------- /packages/node/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src"], 4 | "exclude": ["**/__tests__/**"], 5 | "compilerOptions": { 6 | "noEmit": false, 7 | "outDir": "./dist/esm", 8 | "declarationDir": "./dist/types" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/browser/scripts/vendor/README.md: -------------------------------------------------------------------------------- 1 | # Vendor library 2 | 3 | This script vendors the following library: 4 | https://github.com/segmentio/tsub-js 5 | 6 | Usage for updating tsub: 7 | - update tsub to new version (tsub should be a _dev dependency_) 8 | - run `yarn vendor` from package root -------------------------------------------------------------------------------- /packages/node/src/app/types/segment-event.ts: -------------------------------------------------------------------------------- 1 | import type { CoreSegmentEvent } from '@segment/analytics-core' 2 | 3 | type SegmentEventType = 'track' | 'page' | 'identify' | 'alias' | 'screen' 4 | 5 | export interface SegmentEvent extends CoreSegmentEvent { 6 | type: SegmentEventType 7 | } 8 | -------------------------------------------------------------------------------- /packages/test-helpers/src/constants/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Matches an ISO 8601 timestamp string. 3 | * @example 4 | * expect('2022-01-01T00:00:00.000Z').toEqual(expect.any(ISO_TIMESTAMP_REGEX)) 5 | */ 6 | export const ISO_TIMESTAMP_REGEX = 7 | /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/ 8 | -------------------------------------------------------------------------------- /packages/consent/consent-tools-integration-tests/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | "compilerOptions": { 4 | "moduleResolution": "node", 5 | "module": "ESNext", 6 | "target": "es6", 7 | "noEmit": true, 8 | "types": ["node"] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/browser-integration-tests/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "exclude": ["node_modules", "dist"], 4 | "compilerOptions": { 5 | "module": "esnext", 6 | "target": "ES5", 7 | "moduleResolution": "node", 8 | "lib": ["es2020"] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/browser/scripts/ci.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo '--- Build bundles' 5 | make build 6 | 7 | echo '--- Check Size' 8 | yarn run -T browser size-limit 9 | 10 | echo '--- Lint files' 11 | make lint 12 | 13 | echo '--- Run tests' 14 | make test-unit 15 | make test-integration 16 | -------------------------------------------------------------------------------- /packages/consent/consent-tools/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src"], 4 | "exclude": ["**/__tests__/**"], 5 | "compilerOptions": { 6 | "noEmit": false, 7 | "outDir": "./dist/esm", 8 | "declarationDir": "./dist/types" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src"], 4 | "exclude": ["**/__tests__/**", "**/*.test.*"], 5 | "compilerOptions": { 6 | "noEmit": false, 7 | "outDir": "./dist/esm", 8 | "declarationDir": "./dist/types" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/consent/consent-wrapper-onetrust/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This is the public API for this package. 3 | * We avoid using splat (*) exports so that we can control what is exposed. 4 | */ 5 | export { withOneTrust } from './domain/wrapper' 6 | export type { OneTrustSettings } from './domain/wrapper' 7 | -------------------------------------------------------------------------------- /packages/generic-utils/README.md: -------------------------------------------------------------------------------- 1 | # @segment/analytics-generic-utils 2 | 3 | This monorepo's version of "lodash". This package contains shared generic utilities that can be used within the ecosystem. This package should not have dependencies, and should not contain any references to the Analytics domain. 4 | -------------------------------------------------------------------------------- /packages/generic-utils/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src"], 4 | "exclude": ["**/__tests__/**", "**/*.test.*"], 5 | "compilerOptions": { 6 | "noEmit": false, 7 | "outDir": "./dist/esm", 8 | "declarationDir": "./dist/types" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/page-tools/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "exclude": ["node_modules", "dist"], 4 | "compilerOptions": { 5 | "module": "esnext", 6 | "target": "ES5", 7 | "moduleResolution": "node", 8 | "lib": ["es2020", "DOM", "DOM.Iterable"] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/test-helpers/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src"], 4 | "exclude": ["**/__tests__/**", "**/*.test.*"], 5 | "compilerOptions": { 6 | "noEmit": false, 7 | "outDir": "./dist/esm", 8 | "declarationDir": "./dist/types" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /scripts/utils/exists.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This type guard can be passed into a function such as native filter 3 | * in order to remove nullish values from a list in a type-safe way. 4 | */ 5 | export const exists = (value: T): value is NonNullable => { 6 | return value != null && value !== undefined 7 | } 8 | -------------------------------------------------------------------------------- /packages/core/src/utils/has-properties.ts: -------------------------------------------------------------------------------- 1 | export function hasProperties( 2 | obj: T, 3 | ...keys: K[] 4 | ): obj is T & { [J in K]: unknown } { 5 | // eslint-disable-next-line no-prototype-builtins 6 | return !!obj && keys.every((key) => obj.hasOwnProperty(key)) 7 | } 8 | -------------------------------------------------------------------------------- /playgrounds/with-vite/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')!).render( 7 | 8 | 9 | 10 | ) 11 | -------------------------------------------------------------------------------- /packages/browser/src/plugins/segmentio/ratelimit-error.ts: -------------------------------------------------------------------------------- 1 | export class RateLimitError extends Error { 2 | retryTimeout: number 3 | 4 | constructor(message: string, retryTimeout: number) { 5 | super(message) 6 | this.retryTimeout = retryTimeout 7 | this.name = 'RateLimitError' 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/core/src/utils/pick.ts: -------------------------------------------------------------------------------- 1 | export const pickBy = ( 2 | obj: T, 3 | fn: (key: K, v: T[K]) => boolean 4 | ) => { 5 | return (Object.keys(obj) as K[]) 6 | .filter((k) => fn(k, obj[k])) 7 | .reduce((acc, key) => ((acc[key] = obj[key]), acc), {} as Partial) 8 | } 9 | -------------------------------------------------------------------------------- /packages/core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "exclude": ["node_modules", "dist"], 4 | "compilerOptions": { 5 | "module": "esnext", 6 | "target": "ES5", 7 | "moduleResolution": "node", 8 | "resolveJsonModule": true, 9 | "lib": ["es2020"] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/page-tools/src/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | BufferedPageContextDiscriminant, 3 | createBufferedPageContext, 4 | createPageContext, 5 | getDefaultBufferedPageContext, 6 | PageContext, 7 | BufferedPageContext, 8 | getDefaultPageContext, 9 | isBufferedPageContext, 10 | } from './page-context' 11 | -------------------------------------------------------------------------------- /playgrounds/next-playground/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import '../styles/globals.css' 2 | import '../styles/dracula/dracula-ui.css' 3 | import '../styles/dracula/prism.css' 4 | import '../styles/logs-table.css' 5 | 6 | export default function ExampleApp({ Component, pageProps }) { 7 | return 8 | } 9 | -------------------------------------------------------------------------------- /.buildkite/Readme.md: -------------------------------------------------------------------------------- 1 | # Buildkite 2 | 3 | ## How to update the buildkite docker agent 4 | 1. Make your changes to `Dockerfile.agent`. 5 | 2. Push the changes to ecr 6 | (will need `Ops Write` permission). 7 | ```bash 8 | $ cd .buildkite 9 | $ robo-tooling.docker.login-privileged 10 | $ make agent 11 | ``` 12 | -------------------------------------------------------------------------------- /packages/browser-integration-tests/src/helpers/extract-writekey.ts: -------------------------------------------------------------------------------- 1 | export function extractWriteKeyFromUrl(url: string): string | undefined { 2 | const matches = url.match( 3 | /https:\/\/cdn.segment.com\/v1\/projects\/(.+)\/settings/ 4 | ) 5 | 6 | if (matches) { 7 | return matches[1] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/generic-utils/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "exclude": ["node_modules", "dist"], 4 | "compilerOptions": { 5 | "module": "esnext", 6 | "target": "ES5", 7 | "moduleResolution": "node", 8 | "resolveJsonModule": true, 9 | "lib": ["es2020"] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/node/src/lib/base-64-encode.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/no-nodejs-modules 2 | import { Buffer } from 'buffer' 3 | /** 4 | * Base64 encoder that works in browser, worker, node runtimes. 5 | */ 6 | export const b64encode = (str: string): string => { 7 | return Buffer.from(str).toString('base64') 8 | } 9 | -------------------------------------------------------------------------------- /packages/test-helpers/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "exclude": ["node_modules", "dist"], 4 | "compilerOptions": { 5 | "resolveJsonModule": true, 6 | "module": "esnext", 7 | "target": "ES5", 8 | "moduleResolution": "node", 9 | "lib": ["es2020"] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /scripts/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Clear build artifacts and build cache 3 | 4 | find . \( -name ".turbo" -o -name "dist" -o -name ".next" -o -name "tsconfig.tsbuildinfo" -o -name "coverage" \) ! -path "*/node_modules/*" -print0 | xargs -0 rm -rf 5 | rm -rf node_modules/.cache 6 | 7 | echo "Build files and cache deleted." 8 | -------------------------------------------------------------------------------- /packages/browser/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** @type { import('eslint').Linter.Config } */ 2 | module.exports = { 3 | ignorePatterns: ['e2e-tests', 'qa', '/*.tmp.*/'], 4 | extends: ['../../.eslintrc'], 5 | env: { 6 | node: true, // TODO: change to false when node is abstracted out 7 | browser: true, 8 | }, 9 | } 10 | -------------------------------------------------------------------------------- /packages/browser/src/core/connection/index.ts: -------------------------------------------------------------------------------- 1 | import { isBrowser } from '../environment' 2 | 3 | export function isOnline(): boolean { 4 | if (isBrowser()) { 5 | return window.navigator.onLine 6 | } 7 | 8 | return true 9 | } 10 | 11 | export function isOffline(): boolean { 12 | return !isOnline() 13 | } 14 | -------------------------------------------------------------------------------- /packages/browser/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src"], 4 | "exclude": ["**/__tests__/**", "**/test-helpers/**", "**/tester/**"], 5 | "compilerOptions": { 6 | "noEmit": false, 7 | "outDir": "./dist/pkg", 8 | "declarationDir": "./dist/types" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/browser/src/lib/is-thenable.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Check if thenable 3 | * (instanceof Promise doesn't respect realms) 4 | */ 5 | export const isThenable = (value: unknown): boolean => 6 | typeof value === 'object' && 7 | value !== null && 8 | 'then' in value && 9 | typeof (value as any).then === 'function' 10 | -------------------------------------------------------------------------------- /packages/core/src/utils/is-thenable.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Check if thenable 3 | * (instanceof Promise doesn't respect realms) 4 | */ 5 | export const isThenable = (value: unknown): boolean => 6 | typeof value === 'object' && 7 | value !== null && 8 | 'then' in value && 9 | typeof (value as any).then === 'function' 10 | -------------------------------------------------------------------------------- /packages/node-integration-tests/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "exclude": ["node_modules", "dist"], 4 | "compilerOptions": { 5 | "resolveJsonModule": true, 6 | "module": "esnext", 7 | "target": "ES5", 8 | "moduleResolution": "node", 9 | "lib": ["es2020"] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /scripts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "exclude": ["node_modules", "dist"], 4 | "compilerOptions": { 5 | "noEmit": true, 6 | "resolveJsonModule": true, 7 | "module": "esnext", 8 | "target": "es6", 9 | "moduleResolution": "node", 10 | "lib": ["es2020"] 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/consent/consent-wrapper-onetrust/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src"], 4 | "exclude": ["**/__tests__/**", "**/test-helpers/**"], 5 | "compilerOptions": { 6 | "noEmit": false, 7 | "outDir": "./dist/esm", 8 | "declarationDir": "./dist/types" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/node/src/__tests__/settings.test.ts: -------------------------------------------------------------------------------- 1 | import { validateSettings } from '../app/settings' 2 | 3 | describe('validateSettings', () => { 4 | it('should throw an error if no write key', () => { 5 | expect(() => validateSettings({ writeKey: undefined as any })).toThrowError( 6 | /writeKey/i 7 | ) 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /packages/consent/consent-tools/src/test-helpers/mocks/analytics-mock.ts: -------------------------------------------------------------------------------- 1 | import { AnyAnalytics } from '../../types' 2 | 3 | export const analyticsMock: jest.Mocked = { 4 | addDestinationMiddleware: jest.fn(), 5 | addSourceMiddleware: jest.fn(), 6 | page: jest.fn(), 7 | load: jest.fn(), 8 | track: jest.fn(), 9 | } 10 | -------------------------------------------------------------------------------- /packages/node/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** @type { import('eslint').Linter.Config } */ 2 | module.exports = { 3 | extends: ['../../.eslintrc.isomorphic'], 4 | rules: { 5 | '@typescript-eslint/no-empty-interface': 'off', // since this is a lib, sometimes we want to use interfaces rather than types for the ease of declaration merging. 6 | }, 7 | } 8 | -------------------------------------------------------------------------------- /packages/browser/src/test-helpers/fetch-parse.ts: -------------------------------------------------------------------------------- 1 | export type FetchCall = [input: RequestInfo, init?: RequestInit | undefined] 2 | 3 | export const parseFetchCall = ([url, request]: FetchCall) => ({ 4 | url, 5 | method: request?.method, 6 | headers: request?.headers, 7 | body: request?.body ? JSON.parse(request.body as any) : undefined, 8 | }) 9 | -------------------------------------------------------------------------------- /packages/node-integration-tests/src/perf-tests/server-start-no-analytics.ts: -------------------------------------------------------------------------------- 1 | import { startServer } from '../server/server' 2 | 3 | startServer() 4 | .then((app) => { 5 | app.get('/', (_, res) => { 6 | res.sendStatus(200) 7 | }) 8 | }) 9 | .catch((err) => { 10 | console.error(err) 11 | process.exit(1) 12 | }) 13 | -------------------------------------------------------------------------------- /packages/page-tools/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @segment/analytics-page-tools 2 | 3 | ## 1.0.0 4 | 5 | ### Major Changes 6 | 7 | - [#1271](https://github.com/segmentio/analytics-next/pull/1271) [`f2c2b764`](https://github.com/segmentio/analytics-next/commit/f2c2b764c168b954218f1fedce19c1aabfb5d26d) Thanks [@silesky](https://github.com/silesky)! - Release package 8 | -------------------------------------------------------------------------------- /.github/workflows/md-link-check.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "ignorePatterns": [ 3 | { 4 | "pattern": "^http://127\.0\.0\.1.*" 5 | }, 6 | { 7 | "pattern": "^http://localhost.*" 8 | } 9 | ], 10 | "replacementPatterns": [ 11 | { 12 | "pattern": "^/", 13 | "replacement": "{{BASEURL}}/" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /packages/browser/src/lib/version-type.ts: -------------------------------------------------------------------------------- 1 | // Default value will be updated to 'web' in `bundle-umd.ts` for web build. 2 | let _version: 'web' | 'npm' = 'npm' 3 | 4 | export function setVersionType(version: typeof _version) { 5 | _version = version 6 | } 7 | 8 | export function getVersionType(): typeof _version { 9 | return _version 10 | } 11 | -------------------------------------------------------------------------------- /packages/node/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "exclude": ["node_modules", "dist"], 4 | "compilerOptions": { 5 | "module": "ESNext", 6 | "target": "es2022", // node 18 7 | "moduleResolution": "node", 8 | "lib": ["es2022"] // TODO: es2023 https://www.npmjs.com/package/@tsconfig/node18 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/core-integration-tests/src/public-api.test.ts: -------------------------------------------------------------------------------- 1 | import { CoreContext } from '@segment/analytics-core' 2 | 3 | class TestCtx extends CoreContext {} 4 | 5 | it('should be able to import and instantiate some module from core', () => { 6 | // Test the ability to do basic imports 7 | expect(typeof new TestCtx({ type: 'alias' })).toBe('object') 8 | }) 9 | -------------------------------------------------------------------------------- /playgrounds/next-playground/pages/iframe/childPage.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { AnalyticsProvider } from '../../context/analytics' 3 | 4 | const ChildPage: React.FC = () => { 5 | return
Hello world!
6 | } 7 | 8 | export default () => ( 9 | 10 | 11 | 12 | ) 13 | -------------------------------------------------------------------------------- /packages/browser/src/lib/fetch.ts: -------------------------------------------------------------------------------- 1 | import unfetch from 'unfetch' 2 | import { getGlobal } from './get-global' 3 | 4 | /** 5 | * Wrapper around native `fetch` containing `unfetch` fallback. 6 | */ 7 | export const fetch: typeof global.fetch = (...args) => { 8 | const global = getGlobal() 9 | return ((global && global.fetch) || unfetch)(...args) 10 | } 11 | -------------------------------------------------------------------------------- /packages/core-integration-tests/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "exclude": ["node_modules", "dist"], 4 | "compilerOptions": { 5 | "noUnusedLocals": false, 6 | "noUnusedParameters": false, 7 | "module": "esnext", 8 | "target": "ES5", 9 | "moduleResolution": "node", 10 | "lib": ["es2020"] 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /typings/spawn.d.ts: -------------------------------------------------------------------------------- 1 | declare module '@npmcli/promise-spawn' { 2 | import { EventEmitter } from 'events' 3 | import { SpawnOptions } from 'child_process' 4 | 5 | export default function spawn( 6 | cmd: string, 7 | args?: string[], 8 | opts?: SpawnOptions 9 | ): Promise<{ stdout: Buffer; code: number; stderr: Buffer }> & EventEmitter 10 | } 11 | -------------------------------------------------------------------------------- /packages/consent/consent-tools-integration-tests/playwright.global-setup.ts: -------------------------------------------------------------------------------- 1 | import type { FullConfig } from '@playwright/test' 2 | import { execSync } from 'child_process' 3 | 4 | export default function globalSetup(_cfg: FullConfig) { 5 | console.log('Executing global setup...') 6 | execSync('yarn build', { stdio: 'inherit' }) 7 | console.log('Finished global setup.') 8 | } 9 | -------------------------------------------------------------------------------- /packages/node/scripts/version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Generate a version.ts file from the version in the package.json 3 | 4 | PKG_VERSION=$(node --eval="process.stdout.write(require('./package.json').version)") 5 | 6 | cat <src/generated/version.ts 7 | // This file is generated. 8 | export const version = '$PKG_VERSION' 9 | EOF 10 | 11 | git add src/generated/version.ts 12 | -------------------------------------------------------------------------------- /playgrounds/next-playground/utils/hooks/useConfig.ts: -------------------------------------------------------------------------------- 1 | import { useLocalStorage } from './useLocalStorage' 2 | 3 | export const useWriteKey = () => 4 | useLocalStorage( 5 | 'segment_playground_write_key', 6 | process.env.NEXT_PUBLIC_WRITEKEY 7 | ) 8 | 9 | export const useCDNUrl = () => 10 | useLocalStorage('segment_playground_cdn_url', 'https://cdn.segment.com') 11 | -------------------------------------------------------------------------------- /packages/browser/scripts/release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | : "${SEGMENT_LIB_PATH:?"is a required environment variable"}" 6 | : "${AJS_PRIVATE_ASSETS_UPLOAD:?"is a required environment variable"}" 7 | 8 | source "${SEGMENT_LIB_PATH}/aws.bash" 9 | 10 | function main() { 11 | node scripts/release.js 12 | } 13 | 14 | run-with-role ${AJS_PRIVATE_ASSETS_UPLOAD} main 15 | -------------------------------------------------------------------------------- /packages/node-integration-tests/src/server/fetch-polyfill.ts: -------------------------------------------------------------------------------- 1 | import fetch from 'node-fetch' 2 | 3 | const majorVersion = parseInt( 4 | process.version.replace('v', '').split('.')[0], 5 | 10 6 | ) 7 | 8 | if (majorVersion >= 18) { 9 | ;(globalThis as any).fetch = fetch // polyfill fetch so nock will work on node >= 18 -- see: https://github.com/nock/nock/issues/2336 10 | } 11 | -------------------------------------------------------------------------------- /packages/node-integration-tests/src/smoke/smoke.ts: -------------------------------------------------------------------------------- 1 | import { default as AnalyticsDefaultImport } from '@segment/analytics-node' 2 | import { Analytics as AnalyticsNamedImport } from '@segment/analytics-node' 3 | 4 | { 5 | // test named imports vs default imports 6 | new AnalyticsNamedImport({ writeKey: 'hello world' }) 7 | new AnalyticsDefaultImport({ writeKey: 'hello world' }) 8 | } 9 | -------------------------------------------------------------------------------- /packages/browser/src/core/page/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | BufferedPageContextDiscriminant, 3 | createBufferedPageContext, 4 | createPageContext, 5 | getDefaultBufferedPageContext, 6 | getDefaultPageContext, 7 | isBufferedPageContext, 8 | type PageContext, 9 | type BufferedPageContext, 10 | } from '@segment/analytics-page-tools' 11 | 12 | export * from './add-page-context' 13 | -------------------------------------------------------------------------------- /packages/node/src/lib/create-url.ts: -------------------------------------------------------------------------------- 1 | const stripTrailingSlash = (str: string) => str.replace(/\/$/, '') 2 | 3 | /** 4 | * 5 | * @param host e.g. "http://foo.com" 6 | * @param path e.g. "/bar" 7 | * @returns "e.g." "http://foo.com/bar" 8 | */ 9 | export const tryCreateFormattedUrl = (host: string, path?: string) => { 10 | return stripTrailingSlash(new URL(path || '', host).href) 11 | } 12 | -------------------------------------------------------------------------------- /packages/browser/src/lib/p-while.ts: -------------------------------------------------------------------------------- 1 | export const pWhile = async ( 2 | condition: (value: T | undefined) => boolean, 3 | action: () => T | PromiseLike 4 | ): Promise => { 5 | const loop = async (actionResult: T | undefined): Promise => { 6 | if (condition(actionResult)) { 7 | return loop(await action()) 8 | } 9 | } 10 | 11 | return loop(undefined) 12 | } 13 | -------------------------------------------------------------------------------- /packages/browser/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "module": "esnext", 5 | "target": "ES5", 6 | "moduleResolution": "node", 7 | "resolveJsonModule": true, 8 | "lib": ["es2020", "DOM", "DOM.Iterable"], 9 | "baseUrl": "./src", 10 | "keyofStringsOnly": true 11 | }, 12 | "exclude": ["node_modules", "dist"] 13 | } 14 | -------------------------------------------------------------------------------- /packages/consent/consent-tools-integration-tests/public/consent-tools-vanilla-opt-in.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

Hello World - Serving Analytics

6 |

Please Check Network tab

7 |

This page can used as playground or run by Playwright

8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /packages/core/src/utils/p-while.ts: -------------------------------------------------------------------------------- 1 | export const pWhile = async ( 2 | condition: (value: T | undefined) => boolean, 3 | action: () => T | PromiseLike 4 | ): Promise => { 5 | const loop = async (actionResult: T | undefined): Promise => { 6 | if (condition(actionResult)) { 7 | return loop(await action()) 8 | } 9 | } 10 | 11 | return loop(undefined) 12 | } 13 | -------------------------------------------------------------------------------- /packages/page-tools/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src"], 4 | "exclude": ["**/__tests__/**"], 5 | "compilerOptions": { 6 | "noEmit": false 7 | // Options tsup Ignores: 8 | // module: tsup always outputs ESM or CommonJS based on its --format option. 9 | // outDir: tsup uses its own --out-dir option to control output. 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /playgrounds/next-playground/.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | // https://nextjs.org/docs/basic-features/eslint#lint-staged 2 | const path = require('path') 3 | 4 | const buildEslintCommand = (filenames) => 5 | `next lint --fix --file ${filenames 6 | .map((f) => path.relative(process.cwd(), f)) 7 | .join(' --file ')}` 8 | 9 | module.exports = { 10 | '*.{js,jsx,ts,tsx}': [buildEslintCommand], 11 | } 12 | -------------------------------------------------------------------------------- /playgrounds/next-playground/utils/hooks/useDidMountEffect.ts: -------------------------------------------------------------------------------- 1 | import { useRef, useEffect } from 'react' 2 | 3 | // This function runs on dependency change but not on initial render. 4 | export const useDidMountEffect = (func, deps) => { 5 | const didMount = useRef(false) 6 | 7 | useEffect(() => { 8 | if (didMount.current) func() 9 | else didMount.current = true 10 | }, deps) 11 | } 12 | -------------------------------------------------------------------------------- /packages/browser/src/browser/standalone-interface.ts: -------------------------------------------------------------------------------- 1 | import { Analytics, InitOptions } from '../core/analytics' 2 | 3 | export interface AnalyticsSnippet extends AnalyticsStandalone { 4 | load: (writeKey: string, options?: InitOptions) => void 5 | } 6 | 7 | export interface AnalyticsStandalone extends Analytics { 8 | _loadOptions?: InitOptions 9 | _writeKey?: string 10 | _cdn?: string 11 | } 12 | -------------------------------------------------------------------------------- /packages/browser/src/lib/get-process-env.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Returns `process.env` if it is available in the environment. 3 | * Always returns an object make it similarly easy to use as `process.env`. 4 | */ 5 | export function getProcessEnv(): { [key: string]: string | undefined } { 6 | if (typeof process === 'undefined' || !process.env) { 7 | return {} 8 | } 9 | 10 | return process.env 11 | } 12 | -------------------------------------------------------------------------------- /packages/core/src/utils/ts-helpers.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Remove Index Signature 3 | */ 4 | export type RemoveIndexSignature = { 5 | [K in keyof T as {} extends Record ? never : K]: T[K] 6 | } 7 | 8 | /** 9 | * Recursively make all object properties nullable 10 | */ 11 | export type DeepNullable = { 12 | [K in keyof T]: T[K] extends object ? DeepNullable | null : T[K] | null 13 | } 14 | -------------------------------------------------------------------------------- /playgrounds/next-playground/styles/dracula/card.css: -------------------------------------------------------------------------------- 1 | .drac-card { 2 | border-radius: var(--corner-radius); 3 | box-shadow: 0px var(--spacing-md) var(--spacing-lg) rgba(255, 255, 255, 0.25); 4 | } 5 | 6 | .drac-card-portrait { 7 | max-width: 30rem; 8 | } 9 | 10 | .drac-card-subtle { 11 | box-shadow: none; 12 | background: none; 13 | border-width: 2px; 14 | border-style: solid; 15 | } 16 | -------------------------------------------------------------------------------- /packages/browser/src/test-helpers/test-writekeys.ts: -------------------------------------------------------------------------------- 1 | // Writekeys that are used in different unit/integration tests. These writekeys 2 | // were created for the purpose of testing AJS and are not used for anything else. 3 | 4 | export const TEST_WRITEKEY = 'D8frB7upBChqDN9PMWksNvZYDaKJIYo6' // Test segment writekey 5 | export const AMPLITUDE_WRITEKEY = 'c56fd8ca27d0f9adfe8ad78d846dfcc8' // Test amplitude writekey 6 | -------------------------------------------------------------------------------- /playgrounds/with-vite/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | 26 | .yarn 27 | -------------------------------------------------------------------------------- /playgrounds/next-playground/public/fira-code.webfont/webfont.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'fira-code-regular'; 3 | src: url('fira-code_regular.eot'); 4 | src: url('fira-code_regular.eot?#iefix') format('embedded-opentype'), 5 | url('fira-code_regular.woff') format('woff'), 6 | url('fira-code_regular.ttf') format('truetype'), 7 | url('fira-code_regular.svg#fira-code-regular') format('svg'); 8 | } 9 | -------------------------------------------------------------------------------- /packages/browser/src/test-helpers/fixtures/page-context.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type BufferedPageContext, 3 | type PageContext, 4 | getDefaultBufferedPageContext, 5 | getDefaultPageContext, 6 | } from '../../core/page' 7 | 8 | export const getPageCtxFixture = (): PageContext => getDefaultPageContext() 9 | 10 | export const getBufferedPageCtxFixture = (): BufferedPageContext => 11 | getDefaultBufferedPageContext() 12 | -------------------------------------------------------------------------------- /playgrounds/next-playground/public/demos/faulty-load.js: -------------------------------------------------------------------------------- 1 | const faultyLoad = { 2 | name: 'Fails to load', 3 | type: 'destination', 4 | version: '1.0', 5 | 6 | load: () => { 7 | return new Promise((_res, rej) => { 8 | setTimeout(() => { 9 | rej(new Error('aaay')) 10 | }, 2000) 11 | }) 12 | }, 13 | 14 | isLoaded: () => false, 15 | } 16 | 17 | window.analytics.register(faultyLoad) 18 | -------------------------------------------------------------------------------- /packages/consent/consent-tools-integration-tests/public/consent-tools-vanilla-opt-out.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

Hello World - Serving Analytics (Consent Tools Vanilla Opt Out)

6 |

Please Check Network tab

7 |

This page can used as playground or run by Playwright

8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /packages/node/src/lib/get-message-id.ts: -------------------------------------------------------------------------------- 1 | import { uuid } from './uuid' 2 | 3 | /** 4 | * get a unique messageId with a very low chance of collisions 5 | * using @lukeed/uuid/secure uses the node crypto module, which is the fastest 6 | * @example "node-next-1668208232027-743be593-7789-4b74-8078-cbcc8894c586" 7 | */ 8 | export const createMessageId = (): string => { 9 | return `node-next-${Date.now()}-${uuid()}` 10 | } 11 | -------------------------------------------------------------------------------- /playgrounds/next-playground/public/demos/faulty-track.js: -------------------------------------------------------------------------------- 1 | const faultyTrack = { 2 | name: 'Fails to send', 3 | type: 'destination', 4 | version: '1.0', 5 | 6 | load: async () => {}, 7 | isLoaded: () => true, 8 | 9 | track(ctx) { 10 | if (ctx.event.context?.attempts < 2) { 11 | throw new Error('aaay') 12 | } 13 | 14 | return ctx 15 | }, 16 | } 17 | 18 | window.analytics.register(faultyTrack) 19 | -------------------------------------------------------------------------------- /packages/consent/consent-tools-integration-tests/src/page-bundles/onetrust/index.ts: -------------------------------------------------------------------------------- 1 | import { AnalyticsBrowser } from '@segment/analytics-next' 2 | import { withOneTrust } from '@segment/analytics-consent-wrapper-onetrust' 3 | 4 | const analytics = new AnalyticsBrowser() 5 | 6 | withOneTrust(analytics).load( 7 | { 8 | writeKey: 'foo', 9 | }, 10 | { initialPageview: true } 11 | ) 12 | ;(window as any).analytics = analytics 13 | -------------------------------------------------------------------------------- /packages/browser/src/test-helpers/helpers.ts: -------------------------------------------------------------------------------- 1 | import { sleep } from '@segment/analytics-core' 2 | 3 | export const waitForCondition = async ( 4 | condition: () => boolean, 5 | timeout = 1000 6 | ): Promise => { 7 | const start = Date.now() 8 | while (!condition()) { 9 | if (Date.now() - start > timeout) { 10 | throw new Error(`Timeout of ${timeout}ms exceeded!`) 11 | } 12 | await sleep(10) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/consent/consent-wrapper-onetrust/src/test-helpers/onetrust-globals.d.ts: -------------------------------------------------------------------------------- 1 | import { OneTrustGlobal } from '../lib/onetrust-api' 2 | /** 3 | * ALERT: It's OK to declare ambient globals in test code, but __not__ in library code 4 | * This file should not be included in the final package 5 | */ 6 | export declare global { 7 | interface Window { 8 | OneTrust: OneTrustGlobal 9 | OnetrustActiveGroups: string 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /playgrounds/next-playground/public/demos/faulty-middleware.js: -------------------------------------------------------------------------------- 1 | const faultyMiddleware = { 2 | name: 'Fails to run before', 3 | type: 'before', 4 | version: '1.0', 5 | 6 | load: async () => {}, 7 | isLoaded: () => true, 8 | 9 | track(ctx) { 10 | if (ctx.event.context?.attempts < 4) { 11 | throw new Error('aaay') 12 | } 13 | 14 | return ctx 15 | }, 16 | } 17 | 18 | window.analytics.register(faultyMiddleware) 19 | -------------------------------------------------------------------------------- /packages/browser/src/lib/get-global.ts: -------------------------------------------------------------------------------- 1 | // This an imperfect polyfill for globalThis 2 | export const getGlobal = () => { 3 | if (typeof globalThis !== 'undefined') { 4 | return globalThis 5 | } 6 | if (typeof self !== 'undefined') { 7 | return self 8 | } 9 | if (typeof window !== 'undefined') { 10 | return window 11 | } 12 | if (typeof global !== 'undefined') { 13 | return global 14 | } 15 | return null 16 | } 17 | -------------------------------------------------------------------------------- /packages/node-integration-tests/src/server/fixtures.ts: -------------------------------------------------------------------------------- 1 | import crypto from 'crypto' 2 | 3 | export const trackEventSmall = { 4 | userId: '019mr8mf4r', 5 | event: 'Order Completed', 6 | properties: { userId: 'foo', event: 'click' }, 7 | } 8 | 9 | export const trackEventLarge = { 10 | ...trackEventSmall, 11 | properties: { 12 | ...trackEventSmall.properties, 13 | data: crypto.randomBytes(1024 * 6).toString(), 14 | }, 15 | } 16 | -------------------------------------------------------------------------------- /playgrounds/with-vite/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/node/src/app/context.ts: -------------------------------------------------------------------------------- 1 | // create a derived class since we may want to add node specific things to Context later 2 | 3 | import { CoreContext } from '@segment/analytics-core' 4 | import { SegmentEvent } from './types' 5 | 6 | // While this is not a type, it is a definition 7 | export class Context extends CoreContext { 8 | static override system() { 9 | return new this({ type: 'track', event: 'system' }) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /playgrounds/with-vite/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /packages/consent/consent-wrapper-onetrust/src/index.umd.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is meant to be used to create a webpack bundle. 3 | */ 4 | import { withOneTrust } from './index' 5 | export { withOneTrust } 6 | 7 | // this will almost certainly be executed in the browser, but since this is UMD, 8 | // we are checking just for the sake of being thorough 9 | if (typeof window !== 'undefined') { 10 | ;(window as any).withOneTrust = withOneTrust 11 | } 12 | -------------------------------------------------------------------------------- /packages/test-helpers/src/jest/serializers/timestamp.ts: -------------------------------------------------------------------------------- 1 | import { ISO_TIMESTAMP_REGEX } from '../../constants' 2 | 3 | /** 4 | * Jest snapshot serializer for ISO 8601 timestamp strings. 5 | */ 6 | export const jestSnapshotSerializerTimestamp: jest.SnapshotSerializerPlugin = { 7 | test(value: any) { 8 | return typeof value === 'string' && ISO_TIMESTAMP_REGEX.test(value) 9 | }, 10 | print() { 11 | return '' 12 | }, 13 | } 14 | -------------------------------------------------------------------------------- /playgrounds/next-playground/public/demos/signals.js: -------------------------------------------------------------------------------- 1 | const signals = { 2 | name: 'Signals', 3 | type: 'utility', 4 | version: '0.1.0', 5 | 6 | load: async (_ctx, analytics) => { 7 | document.querySelector('#shuffle').addEventListener('click', async () => { 8 | const ctx = await analytics.track('Event Shuffled') 9 | ctx.flush() 10 | }) 11 | }, 12 | 13 | isLoaded: () => true, 14 | } 15 | 16 | window.analytics.register(signals) 17 | -------------------------------------------------------------------------------- /packages/consent/consent-tools/src/utils/pick.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @example 3 | * pick({ a: 1, b: 2, c: 3 }, ['a', 'c']) => { a: 1, c: 3 } 4 | */ 5 | export const pick = < 6 | Obj extends Record, 7 | Key extends keyof Obj 8 | >( 9 | obj: Obj, 10 | keys: Key[] 11 | ): Pick => { 12 | return keys.reduce((acc, k) => { 13 | if (k in obj) { 14 | acc[k] = obj[k] 15 | } 16 | return acc 17 | }, {} as Pick) 18 | } 19 | -------------------------------------------------------------------------------- /packages/browser/jest.config.js: -------------------------------------------------------------------------------- 1 | const { createJestTSConfig } = require('@internal/config') 2 | 3 | module.exports = createJestTSConfig(__dirname, { 4 | modulePathIgnorePatterns: ['/e2e-tests', '/qa'], 5 | setupFilesAfterEnv: ['./jest.setup.js'], 6 | testEnvironment: 'jsdom', 7 | coverageThreshold: { 8 | global: { 9 | branches: 0, 10 | functions: 0, 11 | lines: 0, 12 | statements: 0, 13 | }, 14 | }, 15 | }) 16 | -------------------------------------------------------------------------------- /packages/core/src/analytics/index.ts: -------------------------------------------------------------------------------- 1 | export interface CoreAnalytics { 2 | track(...args: unknown[]): unknown 3 | page(...args: unknown[]): unknown 4 | identify(...args: unknown[]): unknown 5 | group(...args: unknown[]): unknown 6 | alias(...args: unknown[]): unknown 7 | screen(...args: unknown[]): unknown 8 | register(...plugins: unknown[]): Promise 9 | deregister(...plugins: unknown[]): Promise 10 | readonly VERSION: string 11 | } 12 | -------------------------------------------------------------------------------- /packages/consent/consent-wrapper-onetrust/jest.setup.js: -------------------------------------------------------------------------------- 1 | const fetch = require('node-fetch') 2 | const { TextEncoder, TextDecoder } = require('util') 3 | 4 | // fix: "ReferenceError: TextEncoder is not defined" after upgrading JSDOM 5 | global.TextEncoder = TextEncoder 6 | global.TextDecoder = TextDecoder 7 | 8 | // eslint-disable-next-line no-undef 9 | globalThis.fetch = fetch // polyfill fetch so nock will work correctly on node 18 (https://github.com/nock/nock/issues/2336) 10 | -------------------------------------------------------------------------------- /playgrounds/standalone-playground/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Standalone Playground 9 | 10 | 11 | 12 |

Standalone Playground

13 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /scripts/update-lockfile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # # Changesets does not support yarn, so if the resolutions change, we want to commit them as part of the version pipeline. 3 | 4 | echo "pwd: $(pwd)" 5 | echo "Checking if yarn.lock is up-to-date" 6 | 7 | yarn_path=$(grep "yarnPath:" .yarnrc.yml | awk 'NF>1{print $NF}') 8 | YARN_ENABLE_IMMUTABLE_INSTALLS=false node "$yarn_path" # https://github.com/changesets/action/issues/170 9 | git add yarn.lock 10 | git status --porcelain 11 | 12 | -------------------------------------------------------------------------------- /packages/consent/consent-tools/src/utils/pipe.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Everyday variadic pipe function (reverse of 'compose') 3 | * @example pipe(fn1, fn2, fn3)(value) // fn3(fn2(fn1(value))) 4 | */ 5 | export const pipe = ( 6 | fn: (...args: T) => U, 7 | ...fns: ((a: U) => U)[] 8 | ) => { 9 | const piped = fns.reduce( 10 | (prevFn, nextFn) => (value: U) => nextFn(prevFn(value)), 11 | (value) => value 12 | ) 13 | return (...args: T) => piped(fn(...args)) 14 | } 15 | -------------------------------------------------------------------------------- /packages/core/src/utils/get-global.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-restricted-globals */ 2 | // This an imperfect polyfill for globalThis 3 | export const getGlobal = () => { 4 | if (typeof globalThis !== 'undefined') { 5 | return globalThis 6 | } 7 | if (typeof self !== 'undefined') { 8 | return self 9 | } 10 | if (typeof window !== 'undefined') { 11 | return window 12 | } 13 | if (typeof global !== 'undefined') { 14 | return global 15 | } 16 | return null 17 | } 18 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@1.6.0/schema.json", 3 | "ignore": ["@playground/*"], 4 | "changelog": [ 5 | "@changesets/changelog-github", 6 | { 7 | "repo": "segmentio/analytics-next" 8 | } 9 | ], 10 | "commit": false, 11 | "access": "public", 12 | "baseBranch": "master", 13 | "linked": [], 14 | "___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": { 15 | "onlyUpdatePeerDependentsWhenOutOfRange": true 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/browser/src/vendor/tsub/types.ts: -------------------------------------------------------------------------------- 1 | export interface Rule { 2 | scope: string 3 | target_type: string 4 | matchers: Matcher[] 5 | transformers: Transformer[][] 6 | destinationName?: string 7 | } 8 | export interface Matcher { 9 | type: string 10 | ir: string 11 | } 12 | export interface Transformer { 13 | type: string 14 | config?: any 15 | } 16 | export interface Store { 17 | new (rules?: Rule[]): this 18 | getRulesByDestinationName(destinationName: string): Rule[] 19 | } 20 | -------------------------------------------------------------------------------- /packages/browser/src/core/stats/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import { RemoteMetrics } from '../remote-metrics' 2 | import { Stats } from '..' 3 | 4 | const spy = jest.spyOn(RemoteMetrics.prototype, 'increment') 5 | 6 | describe(Stats, () => { 7 | test('forwards increments to remote metrics endpoint', () => { 8 | Stats.initRemoteMetrics() 9 | 10 | const stats = new Stats() 11 | stats.increment('banana', 1, ['phone:1']) 12 | 13 | expect(spy).toHaveBeenCalledWith('banana', ['phone:1']) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE: -------------------------------------------------------------------------------- 1 | 10 | 11 | - [ ] I've included a changeset (psst. run `yarn changeset`. Read about changesets [here](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md)). 12 | -------------------------------------------------------------------------------- /packages/consent/consent-tools/src/domain/validation/__tests__/validation-error.test.ts: -------------------------------------------------------------------------------- 1 | import { ValidationError } from '../validation-error' 2 | 3 | describe(ValidationError, () => { 4 | it('should have the correct shape', () => { 5 | const err = new ValidationError('foo', 'bar') 6 | 7 | expect(err).toBeInstanceOf(Error) 8 | 9 | expect(err.name).toBe('ValidationError') 10 | 11 | expect(err.message).toMatchInlineSnapshot( 12 | `"[Validation] foo (Received: "bar")"` 13 | ) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /packages/test-helpers/src/utils/promise-timeout.ts: -------------------------------------------------------------------------------- 1 | export function promiseTimeout( 2 | promise: Promise, 3 | timeout: number, 4 | errorMsg?: string 5 | ): Promise { 6 | return new Promise((resolve, reject) => { 7 | const timeoutId = setTimeout(() => { 8 | reject(Error(errorMsg ?? 'Promise timed out')) 9 | }, timeout) 10 | 11 | promise 12 | .then((val) => { 13 | clearTimeout(timeoutId) 14 | return resolve(val) 15 | }) 16 | .catch(reject) 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /playgrounds/next-playground/styles/dracula/badge.css: -------------------------------------------------------------------------------- 1 | .drac-badge { 2 | padding: 1rem 1.5rem; 3 | border: none; 4 | border-radius: var(--corner-radius); 5 | text-transform: uppercase; 6 | } 7 | 8 | .drac-badge .drac-text { 9 | text-transform: uppercase; 10 | } 11 | 12 | .drac-badge-subtle { 13 | border-width: var(--border-size); 14 | border-style: solid; 15 | } 16 | 17 | .drac-badge-outline { 18 | border-width: var(--border-size); 19 | border-style: solid; 20 | background-color: transparent; 21 | } 22 | -------------------------------------------------------------------------------- /playgrounds/next-playground/styles/dracula/dracula-ui.css: -------------------------------------------------------------------------------- 1 | @import './sizes.css'; 2 | @import './colors.css'; 3 | @import './typography.css'; 4 | @import './button.css'; 5 | @import './badge.css'; 6 | @import './input.css'; 7 | @import './select.css'; 8 | @import './avatar.css'; 9 | @import './radio-checkbox-switch.css'; 10 | @import './card.css'; 11 | 12 | .drac { 13 | /* 1rem = 10px */ 14 | font-size: 62.5%; 15 | } 16 | 17 | .drac *, 18 | .drac *::before, 19 | .drac *::after { 20 | box-sizing: border-box; 21 | } 22 | -------------------------------------------------------------------------------- /packages/consent/consent-tools/src/domain/validation/validation-error.ts: -------------------------------------------------------------------------------- 1 | import { AnalyticsConsentError } from '../../types/errors' 2 | 3 | export class ValidationError extends AnalyticsConsentError { 4 | constructor(message: string, received?: any) { 5 | if (arguments.length === 2) { 6 | // to ensure that explicitly passing undefined as second argument still works 7 | message += ` (Received: ${JSON.stringify(received)})` 8 | } 9 | super('ValidationError', `[Validation] ${message}`) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/node/src/__tests__/test-helpers/resolve-emitter.ts: -------------------------------------------------------------------------------- 1 | import { NodeEmitter, NodeEmitterEvents } from '../../app/emitter' 2 | 3 | /** Tester helper that resolves args from emitter event */ 4 | export const resolveEmitterEvent = ( 5 | emitter: NodeEmitter, 6 | eventName: EventName 7 | ): Promise => { 8 | return new Promise((resolve) => { 9 | emitter.once(eventName, (...args) => resolve(args)) 10 | }) 11 | } 12 | -------------------------------------------------------------------------------- /playgrounds/next-playground/styles/logs-table.css: -------------------------------------------------------------------------------- 1 | @import './dracula/colors.css'; 2 | @import './dracula/sizes.css'; 3 | 4 | td { 5 | vertical-align: baseline; 6 | } 7 | 8 | .rc-table-thead { 9 | color: var(--purple); 10 | text-align: left; 11 | } 12 | 13 | .rc-table-cell { 14 | padding-right: var(--spacing-sm); 15 | padding-left: var(--spacing-sm); 16 | } 17 | 18 | .rc-table-cell:first-child { 19 | color: var(--pink); 20 | } 21 | 22 | .rc-table-row:hover { 23 | background: var(--purple-transparent); 24 | } 25 | -------------------------------------------------------------------------------- /packages/browser/src/tester/server.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const express = require('express') 3 | const port = 3001 4 | 5 | const app = express() 6 | 7 | app.get('*.js', function (req, res, next) { 8 | req.url = req.url + '.gz' 9 | res.set('Content-Encoding', 'gzip') 10 | res.set('Content-Type', 'text/javascript') 11 | next() 12 | }) 13 | app.use('/', express.static(path.join(__dirname, '__fixtures__'))) 14 | app.use('/dist/umd', express.static(path.join(__dirname, '../../dist/umd'))) 15 | 16 | app.listen(port) 17 | -------------------------------------------------------------------------------- /.github/workflows/md-link-check.yml: -------------------------------------------------------------------------------- 1 | name: Markdown Links Check 2 | on: 3 | schedule: 4 | # Runs once every 3 days 5 | - cron: "0 0 */3 * *" 6 | jobs: 7 | check-links: 8 | name: Check Markdown Links 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@master 12 | - uses: gaurav-nelson/github-action-markdown-link-check@v1 13 | with: 14 | use-quiet-mode: "yes" 15 | use-verbose-mode: "yes" 16 | config-file: ".github/workflows/md-link-check.config.json" 17 | -------------------------------------------------------------------------------- /packages/consent/consent-tools-integration-tests/src/page-bundles/consent-tools-vanilla-opt-in/index.ts: -------------------------------------------------------------------------------- 1 | import { AnalyticsBrowser } from '@segment/analytics-next' 2 | import { initMockConsentManager } from '../helpers/mock-cmp' 3 | import { withMockCMP } from '../helpers/mock-cmp-wrapper' 4 | 5 | initMockConsentManager({ consentModel: 'opt-in' }) 6 | 7 | const analytics = new AnalyticsBrowser() 8 | 9 | // for testing 10 | ;(window as any).analytics = analytics 11 | 12 | withMockCMP(analytics).load({ 13 | writeKey: 'foo', 14 | }) 15 | -------------------------------------------------------------------------------- /packages/core-integration-tests/README.md: -------------------------------------------------------------------------------- 1 | # Core Integration Tests 2 | This can contain a mix of tests which cover the public API of the package. This can range anywhere from typical integration tests that might stub out the API (which may or may not also be in the package itself), to tests around the specific npm packaged artifact. Examples include: 3 | - Is a license included in npm pack? 4 | - can you import a module (e.g. is the package.json path correctly to allow consumers to import)? 5 | - are there missing depenndencies in package.json? 6 | -------------------------------------------------------------------------------- /packages/node-integration-tests/src/cloudflare-tests/workers/forgot-close-and-flush.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import { Analytics } from '@segment/analytics-node' 3 | 4 | export default { 5 | fetch(_request: Request, _env: {}, _ctx: ExecutionContext) { 6 | const analytics = new Analytics({ 7 | writeKey: '__TEST__', 8 | host: 'http://localhost:3000', 9 | }) 10 | 11 | analytics.track({ userId: 'some-user', event: 'some-event' }) 12 | return new Response('ok') 13 | }, 14 | } 15 | -------------------------------------------------------------------------------- /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /packages/browser/src/test-helpers/type-assertions.ts: -------------------------------------------------------------------------------- 1 | type IsAny = unknown extends T ? (T extends {} ? T : never) : never 2 | type NotAny = T extends IsAny ? never : T 3 | type NotUnknown = unknown extends T ? never : T 4 | 5 | type NotTopType = NotAny & NotUnknown 6 | 7 | // this is not meant to be run, just for type tests 8 | export function assertNotAny(_val: NotTopType) {} 9 | 10 | // this is not meant to be run, just for type tests 11 | export function assertIs(_val: T) {} 12 | -------------------------------------------------------------------------------- /packages/consent/consent-tools/src/types/errors.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Base Consent Error 3 | */ 4 | export abstract class AnalyticsConsentError extends Error { 5 | /** 6 | * 7 | * @param name - Pass the name explicitly to work around the limitation that 'name' is automatically set to the parent class. 8 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/extends#using_extends 9 | * @param message - Error message 10 | */ 11 | constructor(public name: string, message: string) { 12 | super(message) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/core/jest.setup.js: -------------------------------------------------------------------------------- 1 | const { TextEncoder, TextDecoder } = require('util') 2 | const { setImmediate } = require('timers') 3 | 4 | // fix: "ReferenceError: TextEncoder is not defined" after upgrading JSDOM 5 | global.TextEncoder = TextEncoder 6 | global.TextDecoder = TextDecoder 7 | // fix: jsdom uses setImmediate under the hood for preflight XHR requests, 8 | // and jest removed setImmediate, so we need to provide it to prevent console 9 | // logging ReferenceErrors made by integration tests that call Amplitude. 10 | global.setImmediate = setImmediate 11 | -------------------------------------------------------------------------------- /playgrounds/next-playground/README.md: -------------------------------------------------------------------------------- 1 | ### ⚠️ This is not a vanilla analytics.js-next.js example. 2 | If you're looking for how to implement Analytics.js with Next.js, see: 3 | - https://github.com/vercel/next.js/tree/canary/examples/with-segment-analytics 4 | - https://github.com/vercel/next.js/tree/canary/examples/with-segment-analytics-pages-router 5 | 6 | ### Getting Started 7 | First, run the development server: 8 | 9 | ```bash 10 | yarn dev 11 | ``` 12 | 13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 14 | -------------------------------------------------------------------------------- /packages/browser/jest.setup.js: -------------------------------------------------------------------------------- 1 | const { TextEncoder, TextDecoder } = require('util') 2 | const { setImmediate } = require('timers') 3 | 4 | // fix: "ReferenceError: TextEncoder is not defined" after upgrading JSDOM 5 | global.TextEncoder = TextEncoder 6 | global.TextDecoder = TextDecoder 7 | // fix: jsdom uses setImmediate under the hood for preflight XHR requests, 8 | // and jest removed setImmediate, so we need to provide it to prevent console 9 | // logging ReferenceErrors made by integration tests that call Amplitude. 10 | global.setImmediate = setImmediate 11 | -------------------------------------------------------------------------------- /packages/browser/scripts/vendor/webpack.config.vendor.js: -------------------------------------------------------------------------------- 1 | const path = require('node:path') 2 | 3 | /** @type { import('webpack').Configuration } */ 4 | module.exports = { 5 | entry: require.resolve('@segment/tsub'), 6 | output: { 7 | path: path.resolve(__dirname, 'dist.vendor'), // Output directory 8 | filename: 'tsub.js', 9 | library: { 10 | type: 'umd', 11 | }, 12 | }, 13 | resolve: { 14 | extensions: ['.js'], // Resolve these extensions 15 | }, 16 | mode: 'production', // Use production mode for minification, etc. 17 | } 18 | -------------------------------------------------------------------------------- /packages/browser/src/browser/browser-umd.ts: -------------------------------------------------------------------------------- 1 | import { getCDN, setGlobalCDNUrl } from '../lib/parse-cdn' 2 | import { setVersionType } from '../lib/version-type' 3 | 4 | if (process.env.IS_WEBPACK_BUILD) { 5 | if (process.env.ASSET_PATH) { 6 | // @ts-ignore 7 | __webpack_public_path__ = process.env.ASSET_PATH 8 | } else { 9 | const cdn = getCDN() 10 | setGlobalCDNUrl(cdn) 11 | 12 | // @ts-ignore 13 | __webpack_public_path__ = cdn + '/analytics-next/bundles/' 14 | } 15 | } 16 | 17 | setVersionType('web') 18 | 19 | export * from '.' 20 | -------------------------------------------------------------------------------- /packages/browser/src/test-helpers/factories.ts: -------------------------------------------------------------------------------- 1 | export const createSuccess = (body: any, overrides: Partial = {}) => { 2 | return Promise.resolve({ 3 | json: () => Promise.resolve(body), 4 | ok: true, 5 | status: 200, 6 | statusText: 'OK', 7 | ...overrides, 8 | }) as Promise 9 | } 10 | 11 | export const createError = (overrides: Partial = {}) => { 12 | return Promise.resolve({ 13 | ok: false, 14 | status: 404, 15 | statusText: 'Not Found', 16 | ...overrides, 17 | }) as Promise 18 | } 19 | -------------------------------------------------------------------------------- /packages/consent/consent-wrapper-onetrust/src/lib/validation/onetrust-api-error.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * An Errot that represents that the OneTrust API is not in the expected format. 3 | * This is not something that could happen unless our API types are wrong and something is very wonky. 4 | * Not a recoverable error. 5 | */ 6 | export class OneTrustApiValidationError extends Error { 7 | name = 'OtConsentWrapperValidationError' 8 | constructor(message: string, received: any) { 9 | super(`Invariant: ${message} (Received: ${JSON.stringify(received)})`) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/node-integration-tests/src/perf-tests/server-start-old-analytics.ts: -------------------------------------------------------------------------------- 1 | import Analytics from 'analytics-node' 2 | import { startServer } from '../server/server' 3 | import { trackEventSmall } from '../server/fixtures' 4 | 5 | startServer() 6 | .then((app) => { 7 | const analytics = new Analytics('foo', { flushInterval: 1000, flushAt: 15 }) 8 | app.get('/', (_, res) => { 9 | analytics.track(trackEventSmall) 10 | res.sendStatus(200) 11 | }) 12 | }) 13 | .catch((err) => { 14 | console.error(err) 15 | process.exit(1) 16 | }) 17 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | 3 | plugins: 4 | - path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs 5 | spec: "@yarnpkg/plugin-workspace-tools" 6 | - path: .yarn/plugins/@yarnpkg/plugin-constraints.cjs 7 | spec: "@yarnpkg/plugin-constraints" 8 | - path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs 9 | spec: "@yarnpkg/plugin-interactive-tools" 10 | - path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs 11 | spec: "@yarnpkg/plugin-typescript" 12 | 13 | preferInteractive: true 14 | 15 | yarnPath: .yarn/releases/yarn-3.4.1.cjs 16 | -------------------------------------------------------------------------------- /packages/browser/src/lib/client-hints/index.ts: -------------------------------------------------------------------------------- 1 | import { HighEntropyHint, NavigatorUAData, UADataValues } from './interfaces' 2 | 3 | export async function clientHints( 4 | hints?: HighEntropyHint[] 5 | ): Promise { 6 | const userAgentData = (navigator as any).userAgentData as 7 | | NavigatorUAData 8 | | undefined 9 | 10 | if (!userAgentData) return undefined 11 | 12 | if (!hints) return userAgentData.toJSON() 13 | return userAgentData 14 | .getHighEntropyValues(hints) 15 | .catch(() => userAgentData.toJSON()) 16 | } 17 | -------------------------------------------------------------------------------- /packages/browser/src/core/stats/index.ts: -------------------------------------------------------------------------------- 1 | import { CoreStats } from '@segment/analytics-core' 2 | import { MetricsOptions, RemoteMetrics } from './remote-metrics' 3 | 4 | let remoteMetrics: RemoteMetrics | undefined 5 | 6 | export class Stats extends CoreStats { 7 | static initRemoteMetrics(options?: MetricsOptions) { 8 | remoteMetrics = new RemoteMetrics(options) 9 | } 10 | 11 | override increment(metric: string, by?: number, tags?: string[]): void { 12 | super.increment(metric, by, tags) 13 | remoteMetrics?.increment(metric, tags ?? []) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/consent/consent-tools-integration-tests/src/page-bundles/consent-tools-vanilla-opt-out/index.ts: -------------------------------------------------------------------------------- 1 | import { AnalyticsBrowser } from '@segment/analytics-next' 2 | import { initMockConsentManager } from '../helpers/mock-cmp' 3 | import { withMockCMP } from '../helpers/mock-cmp-wrapper' 4 | 5 | initMockConsentManager({ 6 | consentModel: 'opt-out', 7 | }) 8 | 9 | const analytics = new AnalyticsBrowser() 10 | 11 | withMockCMP(analytics).load( 12 | { 13 | writeKey: 'foo', 14 | }, 15 | { initialPageview: true } 16 | ) 17 | ;(window as any).analytics = analytics 18 | -------------------------------------------------------------------------------- /packages/node/src/__tests__/test-helpers/factories.ts: -------------------------------------------------------------------------------- 1 | export const createSuccess = (body?: any) => { 2 | return Promise.resolve({ 3 | json: () => Promise.resolve(body), 4 | text: () => Promise.resolve(JSON.stringify(body)), 5 | ok: true, 6 | status: 200, 7 | statusText: 'OK', 8 | }) as Promise 9 | } 10 | 11 | export const createError = (overrides: Partial = {}) => { 12 | return Promise.resolve({ 13 | ok: false, 14 | status: 404, 15 | statusText: 'Not Found', 16 | ...overrides, 17 | }) as Promise 18 | } 19 | -------------------------------------------------------------------------------- /packages/browser/src/core/query-string/gracefulDecodeURIComponent.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Tries to gets the unencoded version of an encoded component of a 3 | * Uniform Resource Identifier (URI). If input string is malformed, 4 | * returns it back as-is. 5 | * 6 | * Note: All occurences of the `+` character become ` ` (spaces). 7 | **/ 8 | export function gracefulDecodeURIComponent( 9 | encodedURIComponent: string 10 | ): string { 11 | try { 12 | return decodeURIComponent(encodedURIComponent.replace(/\+/g, ' ')) 13 | } catch { 14 | return encodedURIComponent 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/node/src/__tests__/plugins.test.ts: -------------------------------------------------------------------------------- 1 | import { createTestAnalytics } from './test-helpers/create-test-analytics' 2 | 3 | describe('Plugins', () => { 4 | describe('Initialize', () => { 5 | it('loads analytics-node-next plugin', async () => { 6 | const analytics = createTestAnalytics() 7 | await analytics.ready 8 | 9 | const ajsNodeXt = analytics['_queue'].plugins.find( 10 | (xt) => xt.name === 'Segment.io' 11 | ) 12 | expect(ajsNodeXt).toBeDefined() 13 | expect(ajsNodeXt?.isLoaded()).toBeTruthy() 14 | }) 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /packages/node-integration-tests/src/cloudflare-tests/workers/send-single-event.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import { Analytics } from '@segment/analytics-node' 3 | 4 | export default { 5 | async fetch(_request: Request, _env: {}, _ctx: ExecutionContext) { 6 | const analytics = new Analytics({ 7 | writeKey: '__TEST__', 8 | host: 'http://localhost:3000', 9 | }) 10 | 11 | analytics.track({ userId: 'some-user', event: 'some-event' }) 12 | 13 | await analytics.closeAndFlush() 14 | return new Response('ok') 15 | }, 16 | } 17 | -------------------------------------------------------------------------------- /packages/node/src/__tests__/test-helpers/assert-shape/http-request-event.ts: -------------------------------------------------------------------------------- 1 | import { NodeEmitterEvents } from '../../../app/emitter' 2 | type HttpRequestEmitterEvent = NodeEmitterEvents['http_request'][0] 3 | 4 | export const assertHttpRequestEmittedEvent = ( 5 | event: HttpRequestEmitterEvent 6 | ) => { 7 | const body = JSON.parse(event.body) 8 | expect(Array.isArray(body.batch)).toBeTruthy() 9 | expect(body.batch.length).toBe(1) 10 | expect(typeof event.headers).toBe('object') 11 | expect(typeof event.method).toBe('string') 12 | expect(typeof event.url).toBe('string') 13 | } 14 | -------------------------------------------------------------------------------- /packages/browser/src/generated/__tests__/version.test.ts: -------------------------------------------------------------------------------- 1 | import { version } from '../version' 2 | import { readFileSync } from 'fs' 3 | import { resolve as resolvePath } from 'path' 4 | 5 | function getPackageJsonVersion(): string { 6 | const packageJson = JSON.parse( 7 | readFileSync( 8 | resolvePath(__dirname, '..', '..', '..', 'package.json') 9 | ).toString() 10 | ) 11 | return packageJson.version 12 | } 13 | 14 | describe('version', () => { 15 | it('matches version in package.json', async () => { 16 | expect(version).toBe(getPackageJsonVersion()) 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /packages/consent/consent-wrapper-onetrust/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | "exclude": ["node_modules", "dist"], 4 | "compilerOptions": { 5 | "module": "ESNext", // es6 modules 6 | "target": "ES2020", // don't down-compile *too much* -- if users are using webpack, they can always transpile this library themselves 7 | "lib": ["ES2020", "DOM", "DOM.Iterable"], // assume that consumers will be polyfilling at least down to es2020 8 | "moduleResolution": "node", 9 | "isolatedModules": true // ensure we are friendly to build systems 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /scripts/create-release-from-tags/__tests__/fixtures/first-release-example.md: -------------------------------------------------------------------------------- 1 | # @segment/analytics-core 2 | 3 | ## 1.99.0 4 | 5 | ### Minor Changes 6 | 7 | * [#606](https://github.com/segmentio/analytics-next/pull/606) [\`b9c6356\`](https://github.com/segmentio/analytics-next/commit/b9c6356b7d35ee8acb6ecbd1eebc468d18d63958) Thanks [@silesky] - foo!) 8 | 9 | ### Patch Changes 10 | 11 | * [#404](https://github.com/segmentio/analytics-next/pull/404) [\`b9abc6\`](https://github.com/segmentio/analytics-next/commit/b9c6356b7d35ee8acb6ecbd1eebc468d18d63958) Thanks [@silesky] - bar!) -------------------------------------------------------------------------------- /packages/browser/src/lib/__tests__/embedded-write-key.test.ts: -------------------------------------------------------------------------------- 1 | import { embeddedWriteKey } from '../embedded-write-key' 2 | 3 | it('it guards against undefined', () => { 4 | expect(embeddedWriteKey()).toBe(undefined) 5 | }) 6 | 7 | it('it returns undefined when default parameter is set', () => { 8 | window.analyticsWriteKey = '__WRITE_KEY__' 9 | expect(embeddedWriteKey()).toBe(undefined) 10 | }) 11 | 12 | it('it returns the write key when the key is set properly', () => { 13 | window.analyticsWriteKey = 'abc_123_write_key' 14 | expect(embeddedWriteKey()).toBe('abc_123_write_key') 15 | }) 16 | -------------------------------------------------------------------------------- /packages/consent/consent-tools/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This is the public API for this package. 3 | * We avoid using splat (*) exports so that we can control what is exposed. 4 | */ 5 | export { createWrapper } from './domain/create-wrapper' 6 | export { resolveWhen } from './utils' 7 | export type { ConsentModel } from './domain/load-context' 8 | 9 | export type { 10 | Wrapper, 11 | CreateWrapper, 12 | CreateWrapperSettings, 13 | IntegrationCategoryMappings, 14 | Categories, 15 | GetCategoriesFunction, 16 | RegisterOnConsentChangedFunction, 17 | AnyAnalytics, 18 | } from './types' 19 | -------------------------------------------------------------------------------- /packages/browser/src/node/__tests__/node-integration.test.ts: -------------------------------------------------------------------------------- 1 | import { AnalyticsNode } from '../..' 2 | 3 | const writeKey = 'foo' 4 | 5 | describe('Initialization', () => { 6 | it('loads analytics-node-next plugin', async () => { 7 | const [analytics] = await AnalyticsNode.load({ 8 | writeKey, 9 | }) 10 | 11 | expect(analytics.queue.plugins.length).toBe(1) 12 | 13 | const ajsNodeXt = analytics.queue.plugins.find( 14 | (xt) => xt.name === 'analytics-node-next' 15 | ) 16 | expect(ajsNodeXt).toBeDefined() 17 | expect(ajsNodeXt?.isLoaded()).toBeTruthy() 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /packages/consent/consent-tools/src/domain/logger.ts: -------------------------------------------------------------------------------- 1 | // hacky debug.ts, can be replaced with a proper logging solution 2 | // @ts-ignore 3 | 4 | class Logger { 5 | private get debugLoggingEnabled() { 6 | return (window as any)['SEGMENT_CONSENT_WRAPPER_DEBUG_MODE'] === true 7 | } 8 | 9 | enableDebugLogging() { 10 | ;(window as any)['SEGMENT_CONSENT_WRAPPER_DEBUG_MODE'] = true 11 | } 12 | 13 | debug(...args: any[]): void { 14 | if (this.debugLoggingEnabled) { 15 | console.log('[consent wrapper debug]', ...args) 16 | } 17 | } 18 | } 19 | 20 | export const logger = new Logger() 21 | -------------------------------------------------------------------------------- /packages/node/src/lib/fetch.ts: -------------------------------------------------------------------------------- 1 | import type { HTTPFetchFn } from './http-client' 2 | 3 | export const fetch: HTTPFetchFn = async (...args) => { 4 | if (globalThis.fetch) { 5 | return globalThis.fetch(...args) 6 | } 7 | // This guard causes is important, as it causes dead-code elimination to be enabled inside this block. 8 | // @ts-ignore 9 | else if (typeof EdgeRuntime !== 'string') { 10 | return (await import('node-fetch')).default(...args) 11 | } else { 12 | throw new Error( 13 | 'Invariant: an edge runtime that does not support fetch should not exist' 14 | ) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /playgrounds/next-playground/styles/globals.css: -------------------------------------------------------------------------------- 1 | @import '../public/fira-code.webfont/webfont.css'; 2 | 3 | html, 4 | body { 5 | padding: 0; 6 | margin: 0; 7 | font-family: 'fira-code-regular', -apple-system, BlinkMacSystemFont, Segoe UI, 8 | Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, 9 | sans-serif; 10 | color: white; 11 | background-color: #22212c; 12 | } 13 | 14 | a { 15 | color: inherit; 16 | text-decoration: underline; 17 | } 18 | 19 | * { 20 | box-sizing: border-box; 21 | } 22 | 23 | label > input, 24 | label > button { 25 | margin: 1px 5px; 26 | } 27 | -------------------------------------------------------------------------------- /packages/browser/src/plugins/legacy-video-plugins/index.ts: -------------------------------------------------------------------------------- 1 | import { Analytics } from '../../core/analytics' 2 | 3 | export async function loadLegacyVideoPlugins( 4 | analytics: Analytics 5 | ): Promise { 6 | const plugins = await import( 7 | // @ts-expect-error 8 | '@segment/analytics.js-video-plugins/dist/index.umd.js' 9 | ) 10 | 11 | // This is super gross, but we need to support the `window.analytics.plugins` namespace 12 | // that is linked in the segment docs in order to be backwards compatible with ajs-classic 13 | 14 | // @ts-expect-error 15 | analytics._plugins = plugins 16 | } 17 | -------------------------------------------------------------------------------- /packages/core/src/utils/bind-all.ts: -------------------------------------------------------------------------------- 1 | export function bindAll< 2 | ObjType extends { [key: string]: any }, 3 | KeyType extends keyof ObjType 4 | >(obj: ObjType): ObjType { 5 | const proto = obj.constructor.prototype 6 | for (const key of Object.getOwnPropertyNames(proto)) { 7 | if (key !== 'constructor') { 8 | const desc = Object.getOwnPropertyDescriptor( 9 | obj.constructor.prototype, 10 | key 11 | ) 12 | if (!!desc && typeof desc.value === 'function') { 13 | obj[key as KeyType] = obj[key].bind(obj) 14 | } 15 | } 16 | } 17 | 18 | return obj 19 | } 20 | -------------------------------------------------------------------------------- /packages/node/src/index.common.ts: -------------------------------------------------------------------------------- 1 | export { Analytics } from './app/analytics-node' 2 | export { Context } from './app/context' 3 | export { 4 | HTTPClient, 5 | FetchHTTPClient, 6 | HTTPFetchRequest, 7 | HTTPResponse, 8 | HTTPFetchFn, 9 | HTTPClientRequest, 10 | } from './lib/http-client' 11 | 12 | export { OAuthSettings } from './lib/types' 13 | 14 | export type { 15 | Plugin, 16 | GroupTraits, 17 | UserTraits, 18 | TrackParams, 19 | IdentifyParams, 20 | AliasParams, 21 | GroupParams, 22 | PageParams, 23 | } from './app/types' 24 | export type { AnalyticsSettings } from './app/settings' 25 | -------------------------------------------------------------------------------- /packages/consent/consent-tools/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | "exclude": ["node_modules", "dist"], 4 | "compilerOptions": { 5 | "module": "ESNext", // es6 modules 6 | "target": "ES2020", // don't down-compile *too much* -- if users are using webpack, they can always transpile this library themselves 7 | "lib": ["ES2020", "DOM", "DOM.Iterable"], // assume that consumers will be polyfilling at least down to es2020 8 | "moduleResolution": "node", 9 | "resolveJsonModule": true, 10 | "isolatedModules": true // ensure we are friendly to build systems 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/browser/src/lib/bind-all.ts: -------------------------------------------------------------------------------- 1 | export default function bindAll< 2 | ObjType extends { [key: string]: any }, 3 | KeyType extends keyof ObjType 4 | >(obj: ObjType): ObjType { 5 | const proto = obj.constructor.prototype 6 | for (const key of Object.getOwnPropertyNames(proto)) { 7 | if (key !== 'constructor') { 8 | const desc = Object.getOwnPropertyDescriptor( 9 | obj.constructor.prototype, 10 | key 11 | ) 12 | if (!!desc && typeof desc.value === 'function') { 13 | obj[key as KeyType] = obj[key].bind(obj) 14 | } 15 | } 16 | } 17 | 18 | return obj 19 | } 20 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "esModuleInterop": true, 5 | "skipLibCheck": true, 6 | "noUnusedLocals": true, 7 | "noUnusedParameters": true, 8 | "preserveWatchOutput": true, 9 | "sourceMap": true, 10 | "declaration": true, 11 | "declarationMap": true, 12 | "importHelpers": true, 13 | "noEmit": true 14 | }, 15 | "ts-node": { 16 | "transpileOnly": true, 17 | "files": true, 18 | "compilerOptions": { 19 | "module": "commonjs" // get rid of "Cannot use import statement outside a module" for local scripts 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/consent/consent-wrapper-onetrust/src/test-helpers/utils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Allows mocked objects to throw a helpful error message when a method is called without an implementation 3 | */ 4 | export const addDebugMockImplementation = (mock: jest.Mocked) => { 5 | Object.entries(mock).forEach(([method, value]) => { 6 | // automatically add mock implementation for debugging purposes 7 | if (typeof value === 'function') { 8 | mock[method] = mock[method].mockImplementation((...args: any[]) => { 9 | throw new Error(`Not Implemented: ${method}(${JSON.stringify(args)})`) 10 | }) 11 | } 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /packages/browser/src/core/inspector/index.ts: -------------------------------------------------------------------------------- 1 | import { getGlobal } from '../../lib/get-global' 2 | import type { Analytics } from '../analytics' 3 | 4 | const env = getGlobal() 5 | 6 | // The code below assumes the inspector extension will use Object.assign 7 | // to add the inspect interface on to this object reference (unless the 8 | // extension code ran first and has already set up the variable) 9 | const inspectorHost: { 10 | attach: (analytics: Analytics) => void 11 | } = ((env as any)['__SEGMENT_INSPECTOR__'] ??= {}) 12 | 13 | export const attachInspector = (analytics: Analytics) => 14 | inspectorHost.attach?.(analytics as any) 15 | -------------------------------------------------------------------------------- /packages/consent/consent-tools/src/domain/validation/common-validators.ts: -------------------------------------------------------------------------------- 1 | import { ValidationError } from './validation-error' 2 | 3 | export function assertIsFunction( 4 | val: unknown, 5 | variableName: string 6 | ): asserts val is Function { 7 | if (typeof val !== 'function') { 8 | throw new ValidationError(`${variableName} is not a function`, val) 9 | } 10 | } 11 | 12 | export function assertIsObject( 13 | val: unknown, 14 | variableName: string 15 | ): asserts val is object { 16 | if (val === null || typeof val !== 'object') { 17 | throw new ValidationError(`${variableName} is not an object`, val) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/consent/consent-tools-integration-tests/README.md: -------------------------------------------------------------------------------- 1 | # @internal/consent-tools-integration-tests" 2 | 3 | ## Project structure 4 | - `/public` - Test server root 5 | - `/dist` - Holds the webpacked page-bundles that will be injected into each html page 6 | 7 | - `/src` - Test suite files 8 | - `/page-bundles` - For testing libraries that don't have a UMD bundle (i.e analytics-consent-tools) 9 | - `/page-objects` - Page objects for the test suite 10 | - `/page-tests ` - Tests that will be run on the page 11 | 12 | ## Development 13 | ### Build, start server, run tests (and exit gracefully) 14 | ``` 15 | yarn . test:int 16 | ``` 17 | -------------------------------------------------------------------------------- /packages/node-integration-tests/src/perf-tests/server-start-analytics.ts: -------------------------------------------------------------------------------- 1 | import { Analytics } from '@segment/analytics-node' 2 | import { startServer } from '../server/server' 3 | import { trackEventSmall } from '../server/fixtures' 4 | 5 | const analytics = new Analytics({ 6 | writeKey: 'foo', 7 | flushInterval: 1000, 8 | flushAt: 15, 9 | }) 10 | 11 | startServer({ onClose: analytics.closeAndFlush }) 12 | .then((app) => { 13 | app.get('/', (_, res) => { 14 | analytics.track(trackEventSmall) 15 | res.sendStatus(200) 16 | }) 17 | }) 18 | .catch((err) => { 19 | console.error(err) 20 | process.exit(1) 21 | }) 22 | -------------------------------------------------------------------------------- /packages/browser/src/core/connection/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import { isOffline, isOnline } from '..' 2 | 3 | describe('connection', () => { 4 | let online = true 5 | 6 | Object.defineProperty(window.navigator, 'onLine', { 7 | get() { 8 | return online 9 | }, 10 | }) 11 | 12 | test('checks that the browser is online', () => { 13 | online = true 14 | 15 | expect(isOnline()).toBe(true) 16 | expect(isOffline()).toBe(false) 17 | }) 18 | 19 | test('checks that the browser is offline', () => { 20 | online = false 21 | 22 | expect(isOnline()).toBe(false) 23 | expect(isOffline()).toBe(true) 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /packages/core-integration-tests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@internal/core-integration-tests", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "test": "yarn jest", 7 | "lint": "yarn concurrently 'yarn:eslint .' 'yarn:tsc --noEmit'", 8 | "watch:test": "yarn test --watch", 9 | "tsc": "yarn run -T tsc", 10 | "eslint": "yarn run -T eslint", 11 | "concurrently": "yarn run -T concurrently", 12 | "jest": "yarn run -T jest" 13 | }, 14 | "packageManager": "yarn@3.4.1", 15 | "devDependencies": { 16 | "@internal/config": "workspace:^", 17 | "@segment/analytics-core": "workspace:^" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/node-integration-tests/src/durability-tests/server-start-analytics.ts: -------------------------------------------------------------------------------- 1 | import { Analytics } from '@segment/analytics-node' 2 | import { startServer } from '../server/server' 3 | import { trackEventSmall } from '../server/fixtures' 4 | 5 | const analytics = new Analytics({ 6 | writeKey: 'foo', 7 | flushInterval: 1000, 8 | flushAt: 15, 9 | }) 10 | 11 | startServer({ onClose: analytics.closeAndFlush }) 12 | .then((app) => { 13 | app.get('/', (_, res) => { 14 | analytics.track(trackEventSmall) 15 | res.sendStatus(200) 16 | }) 17 | }) 18 | .catch((err) => { 19 | console.error(err) 20 | process.exit(1) 21 | }) 22 | -------------------------------------------------------------------------------- /playgrounds/next-playground/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | *.tsbuildinfo 34 | 35 | # vercel 36 | .vercel 37 | 38 | # @builder.io/partytown 39 | public/~partytown 40 | -------------------------------------------------------------------------------- /packages/browser/src/test-helpers/fixtures/client-hints.ts: -------------------------------------------------------------------------------- 1 | import { 2 | UADataValues, 3 | UALowEntropyJSON, 4 | } from '../../lib/client-hints/interfaces' 5 | 6 | export const lowEntropyTestData: UALowEntropyJSON = { 7 | brands: [ 8 | { 9 | brand: 'Google Chrome', 10 | version: '113', 11 | }, 12 | { 13 | brand: 'Chromium', 14 | version: '113', 15 | }, 16 | { 17 | brand: 'Not-A.Brand', 18 | version: '24', 19 | }, 20 | ], 21 | mobile: false, 22 | platform: 'macOS', 23 | } 24 | 25 | export const highEntropyTestData: UADataValues = { 26 | architecture: 'x86', 27 | bitness: '64', 28 | } 29 | -------------------------------------------------------------------------------- /playgrounds/next-playground/next.config.js: -------------------------------------------------------------------------------- 1 | const withBundleAnalyzer = require('@next/bundle-analyzer')({ 2 | enabled: process.env.ANALYZE === 'true', 3 | }) 4 | 5 | module.exports = withBundleAnalyzer({ 6 | webpack: (config) => { 7 | if (config.mode === 'development') { 8 | config.module.rules.push({ 9 | test: /\.js$/, 10 | use: ['source-map-loader'], 11 | enforce: 'pre', 12 | }) 13 | if (!Array.isArray(config.ignoreWarnings)) { 14 | config.ignoreWarnings = [] 15 | } 16 | 17 | config.ignoreWarnings.push(/Failed to parse source map/) 18 | } 19 | 20 | return config 21 | }, 22 | }) 23 | -------------------------------------------------------------------------------- /packages/browser/src/core/query-string/__tests__/gracefulDecodeURIComponent.test.ts: -------------------------------------------------------------------------------- 1 | import { gracefulDecodeURIComponent } from '../gracefulDecodeURIComponent' 2 | 3 | describe('gracefulDecodeURIComponent', () => { 4 | it('decodes a properly encoded URI component', () => { 5 | const output = gracefulDecodeURIComponent( 6 | 'brown+fox+jumped+%40+the+fence%3F' 7 | ) 8 | 9 | expect(output).toEqual('brown fox jumped @ the fence?') 10 | }) 11 | 12 | it('returns the input string back as-is when input is malformed', () => { 13 | const output = gracefulDecodeURIComponent('25%%2F35%') 14 | 15 | expect(output).toEqual('25%%2F35%') 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /packages/browser/src/core/storage/memoryStorage.ts: -------------------------------------------------------------------------------- 1 | import { Store, StorageObject } from './types' 2 | 3 | /** 4 | * Data Storage using in memory object 5 | */ 6 | export class MemoryStorage 7 | implements Store 8 | { 9 | private cache: Record = {} 10 | 11 | get(key: K): Data[K] | null { 12 | return (this.cache[key] ?? null) as Data[K] | null 13 | } 14 | 15 | set(key: K, value: Data[K] | null): void { 16 | this.cache[key] = value 17 | } 18 | 19 | remove(key: K): void { 20 | delete this.cache[key] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/node/src/__tests__/argument-validation.integration.test.ts: -------------------------------------------------------------------------------- 1 | import { createTestAnalytics } from './test-helpers/create-test-analytics' 2 | 3 | // This is a smoke test. 4 | // More detailed tests can be found in packages/core/src/validation/__tests__/assertions.test.ts 5 | describe('Argument validation', () => { 6 | it('should throw an error if userId/anonId/groupId is not specified', async () => { 7 | const analytics = createTestAnalytics() 8 | 9 | expect(() => 10 | analytics.track({ 11 | event: 'foo', 12 | anonymousId: undefined as any, 13 | userId: undefined as any, 14 | }) 15 | ).toThrow(/userId/) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /playgrounds/with-vite/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"], 20 | "references": [{ "path": "./tsconfig.node.json" }] 21 | } 22 | -------------------------------------------------------------------------------- /packages/browser/src/core/query-string/pickPrefix.ts: -------------------------------------------------------------------------------- 1 | import { QueryStringParams } from '.' 2 | 3 | /** 4 | * Returns an object containing only the properties prefixed by the input 5 | * string. 6 | * Ex: prefix('ajs_traits_', { ajs_traits_address: '123 St' }) 7 | * will return { address: '123 St' } 8 | **/ 9 | export function pickPrefix( 10 | prefix: string, 11 | object: QueryStringParams 12 | ): QueryStringParams { 13 | return Object.keys(object).reduce((acc: QueryStringParams, key: string) => { 14 | if (key.startsWith(prefix)) { 15 | const field = key.substr(prefix.length) 16 | acc[field] = object[key]! 17 | } 18 | return acc 19 | }, {}) 20 | } 21 | -------------------------------------------------------------------------------- /packages/config-tsup/config.js: -------------------------------------------------------------------------------- 1 | const { defineConfig } = require('tsup') 2 | 3 | module.exports = defineConfig(() => ({ 4 | entry: ['src/index.ts'], 5 | format: ['esm', 'cjs'], // Output both ESM and CJS formats 6 | dts: { 7 | resolve: true, 8 | entry: 'src/index.ts', 9 | }, 10 | clean: true, 11 | esbuildOptions(options, context) { 12 | if (context.format === 'esm') { 13 | options.outdir = 'dist/esm' // Output ESM files to dist/esm 14 | options.outExtension = { '.js': '.mjs' } // Use .mjs for ESM files 15 | } else if (context.format === 'cjs') { 16 | options.outdir = 'dist/cjs' // Output CJS files to dist/cjs 17 | } 18 | }, 19 | })) 20 | -------------------------------------------------------------------------------- /packages/generic-utils/src/create-deferred/create-deferred.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Return a promise that can be externally resolved 3 | */ 4 | export const createDeferred = () => { 5 | let resolve!: (value: T | PromiseLike) => void 6 | let reject!: (reason: any) => void 7 | let settled = false 8 | const promise = new Promise((_resolve, _reject) => { 9 | resolve = (...args) => { 10 | settled = true 11 | _resolve(...args) 12 | } 13 | reject = (...args) => { 14 | settled = true 15 | _reject(...args) 16 | } 17 | }) 18 | 19 | return { 20 | resolve, 21 | reject, 22 | promise, 23 | isSettled: () => settled, 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/consent/consent-tools/src/domain/pruned-categories.ts: -------------------------------------------------------------------------------- 1 | import { pick } from '../utils' 2 | import { Categories } from '../types' 3 | import { ValidationError } from './validation/validation-error' 4 | 5 | export const getPrunedCategories = ( 6 | categories: Categories, 7 | allCategories: string[] 8 | ): Categories => { 9 | if (!allCategories.length) { 10 | // No configured integrations found, so no categories will be sent (should not happen unless there's a configuration error) 11 | throw new ValidationError( 12 | 'Invariant: No consent categories defined in Segment', 13 | [] 14 | ) 15 | } 16 | 17 | return pick(categories, allCategories) 18 | } 19 | -------------------------------------------------------------------------------- /packages/config-webpack/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@internal/config-webpack", 3 | "version": "0.0.0", 4 | "private": true, 5 | "packageManager": "yarn@3.4.1", 6 | "devDependencies": { 7 | "@babel/cli": "^7.22.10", 8 | "@babel/core": "^7.23.2", 9 | "@babel/preset-env": "^7.22.10", 10 | "@babel/preset-typescript": "^7.22.11", 11 | "@types/circular-dependency-plugin": "^5", 12 | "babel-loader": "^8.0.0", 13 | "circular-dependency-plugin": "^5.2.2", 14 | "ecma-version-validator-webpack-plugin": "^1.2.1", 15 | "terser-webpack-plugin": "^5.1.4", 16 | "webpack": "^5.94.0", 17 | "webpack-cli": "^4.8.0", 18 | "webpack-merge": "^5.9.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/core/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './emitter/interface' 2 | export * from './plugins' 3 | export * from './events/interfaces' 4 | export * from './events' 5 | export * from './callback' 6 | export * from './priority-queue' 7 | export { backoff } from './priority-queue/backoff' 8 | export * from './context' 9 | export * from './queue/event-queue' 10 | export * from './analytics' 11 | export * from './analytics/dispatch' 12 | export * from './validation/helpers' 13 | export * from './validation/errors' 14 | export * from './validation/assertions' 15 | export * from './utils/bind-all' 16 | export * from './stats' 17 | export { CoreLogger } from './logger' 18 | export * from './queue/delivery' 19 | -------------------------------------------------------------------------------- /packages/node/src/__tests__/test-helpers/test-plugin.ts: -------------------------------------------------------------------------------- 1 | import { Plugin } from '../../app/types' 2 | 3 | export const testPlugin: Plugin = { 4 | isLoaded: jest.fn().mockReturnValue(true), 5 | load: jest.fn().mockResolvedValue(undefined), 6 | unload: jest.fn().mockResolvedValue(undefined), 7 | name: 'Test Plugin', 8 | type: 'destination', 9 | version: '0.1.0', 10 | alias: jest.fn((ctx) => Promise.resolve(ctx)), 11 | group: jest.fn((ctx) => Promise.resolve(ctx)), 12 | identify: jest.fn((ctx) => Promise.resolve(ctx)), 13 | page: jest.fn((ctx) => Promise.resolve(ctx)), 14 | screen: jest.fn((ctx) => Promise.resolve(ctx)), 15 | track: jest.fn((ctx) => Promise.resolve(ctx)), 16 | } 17 | -------------------------------------------------------------------------------- /packages/browser/src/core/context/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CoreContext, 3 | ContextCancelation, 4 | ContextFailedDelivery, 5 | SerializedContext, 6 | CancelationOptions, 7 | } from '@segment/analytics-core' 8 | import { SegmentEvent } from '../events/interfaces' 9 | import { Stats } from '../stats' 10 | 11 | export class Context extends CoreContext { 12 | static override system() { 13 | return new this({ type: 'track', event: 'system' }) 14 | } 15 | constructor(event: SegmentEvent, id?: string) { 16 | super(event, id, new Stats()) 17 | } 18 | } 19 | 20 | export { ContextCancelation } 21 | export type { ContextFailedDelivery, SerializedContext, CancelationOptions } 22 | -------------------------------------------------------------------------------- /packages/core/src/task/__tests__/task-group.test.ts: -------------------------------------------------------------------------------- 1 | import { createTaskGroup } from '../task-group' 2 | 3 | function sleep(ms: number) { 4 | return new Promise((resolve) => setTimeout(resolve, ms)) 5 | } 6 | 7 | describe('TaskGroup', () => { 8 | it('works with concurrent operations', async () => { 9 | const group = createTaskGroup() 10 | const a = jest.fn() 11 | const b = jest.fn() 12 | 13 | void group.run(async () => { 14 | await sleep(100) 15 | a() 16 | }) 17 | void group.run(async () => { 18 | await sleep(1000) 19 | b() 20 | }) 21 | 22 | await group.done() 23 | expect(a).toBeCalled() 24 | expect(b).toBeCalled() 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /playgrounds/next-playground/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "target": "es5", 5 | "lib": [ 6 | "dom", 7 | "dom.iterable", 8 | "esnext" 9 | ], 10 | "allowJs": true, 11 | "strict": false, 12 | "forceConsistentCasingInFileNames": true, 13 | "noEmit": true, 14 | "module": "esnext", 15 | "moduleResolution": "node", 16 | "resolveJsonModule": true, 17 | "isolatedModules": true, 18 | "jsx": "preserve", 19 | "incremental": true 20 | }, 21 | "include": [ 22 | "next-env.d.ts", 23 | "**/*.ts", 24 | "**/*.tsx" 25 | ], 26 | "exclude": [ 27 | "node_modules" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /packages/consent/consent-tools/src/domain/consent-stamping.ts: -------------------------------------------------------------------------------- 1 | import { Categories, SourceMiddlewareFunction } from '../types' 2 | 3 | type CreateConsentMw = ( 4 | getCategories: () => Promise 5 | ) => SourceMiddlewareFunction 6 | 7 | /** 8 | * Create analytics addSourceMiddleware fn that stamps each event 9 | */ 10 | export const createConsentStampingMiddleware: CreateConsentMw = ( 11 | getCategories 12 | ) => { 13 | const fn: SourceMiddlewareFunction = async ({ payload, next }) => { 14 | payload.obj.context.consent = { 15 | ...payload.obj.context.consent, 16 | categoryPreferences: await getCategories(), 17 | } 18 | 19 | next(payload) 20 | } 21 | return fn 22 | } 23 | -------------------------------------------------------------------------------- /playgrounds/next-playground/pages/iframe/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { AnalyticsProvider } from '../../context/analytics' 3 | 4 | function Iframe(): React.ReactElement { 5 | return ( 6 | 13 | ) 14 | } 15 | 16 | export default () => ( 17 | 18 |