From 26722d73ae6af455de04a414158f05b75c13c21e Mon Sep 17 00:00:00 2001
From: Andrew Liu <159852527+aliu39@users.noreply.github.com>
Date: Tue, 29 Oct 2024 14:03:14 -0700
Subject: [PATCH 01/19] Init package files
---
packages/launchdarkly/.eslintignore | 2 +
packages/launchdarkly/.eslintrc.js | 39 +++
packages/launchdarkly/.gitignore | 4 +
packages/launchdarkly/LICENSE | 21 ++
packages/launchdarkly/README.md | 25 ++
packages/launchdarkly/package.json | 72 +++++
.../launchdarkly/rollup.bundle.config.mjs | 12 +
packages/launchdarkly/rollup.npm.config.mjs | 19 ++
.../scripts/shim-preact-export.js | 75 ++++++
packages/launchdarkly/src/core/integration.ts | 0
packages/launchdarkly/src/index.ts | 9 +
packages/launchdarkly/test.setup.ts | 255 ++++++++++++++++++
packages/launchdarkly/tsconfig.json | 8 +
packages/launchdarkly/tsconfig.test.json | 15 ++
packages/launchdarkly/tsconfig.types.json | 10 +
packages/launchdarkly/vitest.config.ts | 12 +
.../replay-internal/src/types/launchdarkly.ts | 4 +
17 files changed, 582 insertions(+)
create mode 100644 packages/launchdarkly/.eslintignore
create mode 100644 packages/launchdarkly/.eslintrc.js
create mode 100644 packages/launchdarkly/.gitignore
create mode 100644 packages/launchdarkly/LICENSE
create mode 100644 packages/launchdarkly/README.md
create mode 100644 packages/launchdarkly/package.json
create mode 100644 packages/launchdarkly/rollup.bundle.config.mjs
create mode 100644 packages/launchdarkly/rollup.npm.config.mjs
create mode 100644 packages/launchdarkly/scripts/shim-preact-export.js
create mode 100644 packages/launchdarkly/src/core/integration.ts
create mode 100644 packages/launchdarkly/src/index.ts
create mode 100644 packages/launchdarkly/test.setup.ts
create mode 100644 packages/launchdarkly/tsconfig.json
create mode 100644 packages/launchdarkly/tsconfig.test.json
create mode 100644 packages/launchdarkly/tsconfig.types.json
create mode 100644 packages/launchdarkly/vitest.config.ts
create mode 100644 packages/replay-internal/src/types/launchdarkly.ts
diff --git a/packages/launchdarkly/.eslintignore b/packages/launchdarkly/.eslintignore
new file mode 100644
index 000000000000..b38db2f296ff
--- /dev/null
+++ b/packages/launchdarkly/.eslintignore
@@ -0,0 +1,2 @@
+node_modules/
+build/
diff --git a/packages/launchdarkly/.eslintrc.js b/packages/launchdarkly/.eslintrc.js
new file mode 100644
index 000000000000..dc39a10b354b
--- /dev/null
+++ b/packages/launchdarkly/.eslintrc.js
@@ -0,0 +1,39 @@
+// Note: All paths are relative to the directory in which eslint is being run, rather than the directory where this file
+// lives
+
+// ESLint config docs: https://eslint.org/docs/user-guide/configuring/
+
+module.exports = {
+ extends: ['../../.eslintrc.js'],
+ overrides: [
+ {
+ files: ['src/**/*.ts'],
+ },
+ {
+ files: ['test.setup.ts', 'vitest.config.ts'],
+ parserOptions: {
+ project: ['tsconfig.test.json'],
+ },
+ rules: {
+ 'no-console': 'off',
+ },
+ },
+ {
+ files: ['test/**/*.ts'],
+
+ rules: {
+ // most of these errors come from `new Promise(process.nextTick)`
+ '@typescript-eslint/unbound-method': 'off',
+ // TODO: decide if we want to enable this again after the migration
+ // We can take the freedom to be a bit more lenient with tests
+ '@typescript-eslint/no-floating-promises': 'off',
+ },
+ },
+ {
+ files: ['src/types/deprecated.ts'],
+ rules: {
+ '@typescript-eslint/naming-convention': 'off',
+ },
+ },
+ ],
+};
diff --git a/packages/launchdarkly/.gitignore b/packages/launchdarkly/.gitignore
new file mode 100644
index 000000000000..363d3467c6fa
--- /dev/null
+++ b/packages/launchdarkly/.gitignore
@@ -0,0 +1,4 @@
+node_modules
+/*.tgz
+.eslintcache
+build
diff --git a/packages/launchdarkly/LICENSE b/packages/launchdarkly/LICENSE
new file mode 100644
index 000000000000..6bfafc44539c
--- /dev/null
+++ b/packages/launchdarkly/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023-2024 Functional Software, Inc. dba Sentry
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/launchdarkly/README.md b/packages/launchdarkly/README.md
new file mode 100644
index 000000000000..336e74da6593
--- /dev/null
+++ b/packages/launchdarkly/README.md
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+# Sentry Integration for Feedback
+
+This SDK is **considered experimental and in a beta state**. It may experience breaking changes, and may be discontinued
+at any time. Please reach out on [GitHub](https://github.com/getsentry/sentry-javascript/issues/new/choose) if you have
+any feedback/concerns.
+
+To view Feedback in Sentry, your
+[Sentry organization must be an early adopter](https://docs.sentry.io/product/accounts/early-adopter-features/).
+
+## Installation
+
+Please read the [offical integration documentation](https://docs.sentry.io/platforms/javascript/user-feedback/) for
+installation instructions.
+
+## Configuration
+
+The Feedback integration is highly customizable, please read the
+[official integration documentation](https://docs.sentry.io/platforms/javascript/user-feedback/configuration/) for the
+most up-to-date configuration options.
diff --git a/packages/launchdarkly/package.json b/packages/launchdarkly/package.json
new file mode 100644
index 000000000000..d09f6f977406
--- /dev/null
+++ b/packages/launchdarkly/package.json
@@ -0,0 +1,72 @@
+{
+ "name": "@sentry-internal/launchdarkly",
+ "version": "8.35.0",
+ "description": "Sentry SDK integration for user launchdarkly",
+ "repository": "git://github.com/getsentry/sentry-javascript.git",
+ "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/launchdarkly",
+ "author": "Sentry",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.18"
+ },
+ "files": [
+ "/build/npm"
+ ],
+ "main": "build/npm/cjs/index.js",
+ "module": "build/npm/esm/index.js",
+ "types": "build/npm/types/index.d.ts",
+ "exports": {
+ "./package.json": "./package.json",
+ ".": {
+ "import": {
+ "types": "./build/npm/types/index.d.ts",
+ "default": "./build/npm/esm/index.js"
+ },
+ "require": {
+ "types": "./build/npm/types/index.d.ts",
+ "default": "./build/npm/cjs/index.js"
+ }
+ }
+ },
+ "typesVersions": {
+ "<4.9": {
+ "build/npm/types/index.d.ts": [
+ "build/npm/types-ts3.8/index.d.ts"
+ ]
+ }
+ },
+ "publishConfig": {
+ "access": "public"
+ },
+ "dependencies": {
+ "@sentry/core": "8.35.0",
+ "@sentry/types": "8.35.0",
+ "@sentry/utils": "8.35.0"
+ },
+ "scripts": {
+ "build": "run-p build:transpile build:types build:bundle",
+ "build:transpile": "rollup -c rollup.npm.config.mjs",
+ "build:bundle": "rollup -c rollup.bundle.config.mjs",
+ "build:dev": "run-p build:transpile build:types",
+ "build:types": "run-s build:types:core build:types:downlevel",
+ "build:types:core": "tsc -p tsconfig.types.json",
+ "build:types:downlevel": "yarn downlevel-dts build/npm/types build/npm/types-ts3.8 --to ts3.8 && yarn node ./scripts/shim-preact-export.js",
+ "build:watch": "run-p build:transpile:watch build:bundle:watch build:types:watch",
+ "build:dev:watch": "run-p build:transpile:watch build:types:watch",
+ "build:transpile:watch": "yarn build:transpile --watch",
+ "build:bundle:watch": "yarn build:bundle --watch",
+ "build:types:watch": "tsc -p tsconfig.types.json --watch",
+ "build:tarball": "npm pack",
+ "circularDepCheck": "madge --circular src/index.ts",
+ "clean": "rimraf build sentry-internal-launchdarkly-*.tgz",
+ "fix": "eslint . --format stylish --fix",
+ "lint": "eslint . --format stylish",
+ "test": "jest",
+ "test:watch": "jest --watch",
+ "yalc:publish": "yalc publish --push --sig"
+ },
+ "volta": {
+ "extends": "../../package.json"
+ },
+ "sideEffects": false
+}
diff --git a/packages/launchdarkly/rollup.bundle.config.mjs b/packages/launchdarkly/rollup.bundle.config.mjs
new file mode 100644
index 000000000000..cc29fadbc093
--- /dev/null
+++ b/packages/launchdarkly/rollup.bundle.config.mjs
@@ -0,0 +1,12 @@
+import { makeBaseBundleConfig, makeBundleConfigVariants } from '@sentry-internal/rollup-utils';
+
+const baseBundleConfig = makeBaseBundleConfig({
+ bundleType: 'addon',
+ entrypoints: ['src/index.ts'],
+ licenseTitle: '@sentry-internal/launchdarkly',
+ outputFileBase: () => 'bundles/launchdarkly',
+});
+
+const builds = makeBundleConfigVariants(baseBundleConfig);
+
+export default builds;
diff --git a/packages/launchdarkly/rollup.npm.config.mjs b/packages/launchdarkly/rollup.npm.config.mjs
new file mode 100644
index 000000000000..3b4431fa6829
--- /dev/null
+++ b/packages/launchdarkly/rollup.npm.config.mjs
@@ -0,0 +1,19 @@
+import { makeBaseNPMConfig, makeNPMConfigVariants } from '@sentry-internal/rollup-utils';
+
+export default makeNPMConfigVariants(
+ makeBaseNPMConfig({
+ hasBundles: true,
+ packageSpecificConfig: {
+ output: {
+ // set exports to 'named' or 'auto' so that rollup doesn't warn
+ exports: 'named',
+ // set preserveModules to false because for Replay we actually want
+ // to bundle everything into one file.
+ preserveModules:
+ process.env.SENTRY_BUILD_PRESERVE_MODULES === undefined
+ ? false
+ : Boolean(process.env.SENTRY_BUILD_PRESERVE_MODULES),
+ },
+ },
+ }),
+);
diff --git a/packages/launchdarkly/scripts/shim-preact-export.js b/packages/launchdarkly/scripts/shim-preact-export.js
new file mode 100644
index 000000000000..bd74e4da0a05
--- /dev/null
+++ b/packages/launchdarkly/scripts/shim-preact-export.js
@@ -0,0 +1,75 @@
+// preact does not support more modern TypeScript versions, which breaks our users that depend on older
+// TypeScript versions. To fix this, we shim the types from preact to be any and remove the dependency on preact
+// for types directly. This script is meant to be run after the build/npm/types-ts3.8 directory is created.
+
+// Path: build/npm/types-ts3.8/global.d.ts
+
+const fs = require('fs');
+const path = require('path');
+
+/**
+ * This regex looks for preact imports we can replace and shim out.
+ *
+ * Example:
+ * import { ComponentChildren, VNode } from 'preact';
+ */
+const preactImportRegex = /import\s*{\s*([\w\s,]+)\s*}\s*from\s*'preact'\s*;?/;
+
+function walk(dir) {
+ const files = fs.readdirSync(dir);
+ files.forEach(file => {
+ const filePath = path.join(dir, file);
+ const stat = fs.lstatSync(filePath);
+ if (stat.isDirectory()) {
+ walk(filePath);
+ } else {
+ if (filePath.endsWith('.d.ts')) {
+ const content = fs.readFileSync(filePath, 'utf8');
+ const capture = preactImportRegex.exec(content);
+ if (capture) {
+ const groups = capture[1].split(',').map(s => s.trim());
+
+ // This generates a shim snippet to replace the type imports from preact
+ // It generates a snippet based on the capture groups of preactImportRegex.
+ //
+ // Example:
+ //
+ // import type { ComponentChildren, VNode } from 'preact';
+ // becomes
+ // type ComponentChildren: any;
+ // type VNode: any;
+ const snippet = groups.reduce((acc, curr) => {
+ const searchableValue = curr.includes(' as ') ? curr.split(' as ')[1] : curr;
+
+ // look to see if imported as value, then we have to use declare const
+ if (content.includes(`typeof ${searchableValue}`)) {
+ return `${acc}declare const ${searchableValue}: any;\n`;
+ }
+
+ // look to see if generic type like Foo
+ if (content.includes(`${searchableValue}<`)) {
+ return `${acc}type ${searchableValue} = any;\n`;
+ }
+
+ // otherwise we can just leave as type
+ return `${acc}type ${searchableValue} = any;\n`;
+ }, '');
+
+ // we then can remove the import from preact
+ const newContent = content.replace(preactImportRegex, '// replaced import from preact');
+
+ // and write the new content to the file
+ fs.writeFileSync(filePath, snippet + newContent, 'utf8');
+ }
+ }
+ }
+ });
+}
+
+function run() {
+ // recurse through build/npm/types-ts3.8 directory
+ const dir = path.join('build', 'npm', 'types-ts3.8');
+ walk(dir);
+}
+
+run();
diff --git a/packages/launchdarkly/src/core/integration.ts b/packages/launchdarkly/src/core/integration.ts
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/launchdarkly/src/index.ts b/packages/launchdarkly/src/index.ts
new file mode 100644
index 000000000000..9397a3be9cc3
--- /dev/null
+++ b/packages/launchdarkly/src/index.ts
@@ -0,0 +1,9 @@
+// This file is used as entry point to generate the npm package and CDN bundles.
+
+// export { sendFeedback } from './core/sendFeedback';
+// export { buildFeedbackIntegration } from './core/integration';
+// export { getFeedback } from './core/getFeedback';
+// export { feedbackModalIntegration } from './modal/integration';
+// export { feedbackScreenshotIntegration } from './screenshot/integration';
+
+export { buildLaunchDarklyIntegration } from './core/integration';
diff --git a/packages/launchdarkly/test.setup.ts b/packages/launchdarkly/test.setup.ts
new file mode 100644
index 000000000000..05a762e60d50
--- /dev/null
+++ b/packages/launchdarkly/test.setup.ts
@@ -0,0 +1,255 @@
+import { printDiffOrStringify } from 'jest-matcher-utils';
+import { vi } from 'vitest';
+import type { Mocked, MockedFunction } from 'vitest';
+
+/* eslint-disable @typescript-eslint/no-unsafe-member-access */
+import { getClient } from '@sentry/core';
+import type { ReplayRecordingData, Transport } from '@sentry/types';
+import * as SentryUtils from '@sentry/utils';
+
+import type { ReplayContainer, Session } from './src/types';
+
+type MockTransport = MockedFunction;
+
+vi.spyOn(SentryUtils, 'isBrowser').mockImplementation(() => true);
+
+type EnvelopeHeader = {
+ event_id: string;
+ sent_at: string;
+ sdk: {
+ name: string;
+ version?: string;
+ };
+};
+
+type ReplayEventHeader = { type: 'replay_event' };
+type ReplayEventPayload = Record;
+type RecordingHeader = { type: 'replay_recording'; length: number };
+type RecordingPayloadHeader = Record;
+type SentReplayExpected = {
+ envelopeHeader?: EnvelopeHeader;
+ replayEventHeader?: ReplayEventHeader;
+ replayEventPayload?: ReplayEventPayload;
+ recordingHeader?: RecordingHeader;
+ recordingPayloadHeader?: RecordingPayloadHeader;
+ recordingData?: ReplayRecordingData;
+};
+
+// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
+const toHaveSameSession = function (received: Mocked, expected: undefined | Session) {
+ const pass = this.equals(received.session?.id, expected?.id) as boolean;
+
+ const options = {
+ isNot: this.isNot,
+ promise: this.promise,
+ };
+
+ return {
+ pass,
+ message: () =>
+ `${this.utils.matcherHint('toHaveSameSession', undefined, undefined, options)}\n\n${printDiffOrStringify(
+ expected,
+ received.session,
+ 'Expected',
+ 'Received',
+ )}`,
+ };
+};
+
+type Result = {
+ passed: boolean;
+ key: string;
+ expectedVal: SentReplayExpected[keyof SentReplayExpected];
+ actualVal: SentReplayExpected[keyof SentReplayExpected];
+};
+type Call = [
+ EnvelopeHeader,
+ [
+ [ReplayEventHeader | undefined, ReplayEventPayload | undefined],
+ [RecordingHeader | undefined, RecordingPayloadHeader | undefined],
+ ],
+];
+type CheckCallForSentReplayResult = { pass: boolean; call: Call | undefined; results: Result[] };
+
+function checkCallForSentReplay(
+ call: Call | undefined,
+ expected?: SentReplayExpected | { sample: SentReplayExpected; inverse: boolean },
+): CheckCallForSentReplayResult {
+ const envelopeHeader = call?.[0];
+ const envelopeItems = call?.[1] || [[], []];
+ const [[replayEventHeader, replayEventPayload], [recordingHeader, recordingPayload] = []] = envelopeItems;
+
+ // @ts-expect-error recordingPayload is always a string in our tests
+ const [recordingPayloadHeader, recordingData] = recordingPayload?.split('\n') || [];
+
+ const actualObj: Required = {
+ // @ts-expect-error Custom envelope
+ envelopeHeader: envelopeHeader,
+ // @ts-expect-error Custom envelope
+ replayEventHeader: replayEventHeader,
+ // @ts-expect-error Custom envelope
+ replayEventPayload: replayEventPayload,
+ // @ts-expect-error Custom envelope
+ recordingHeader: recordingHeader,
+ recordingPayloadHeader: recordingPayloadHeader && JSON.parse(recordingPayloadHeader),
+ recordingData,
+ };
+
+ const isObjectContaining = expected && 'sample' in expected && 'inverse' in expected;
+ const expectedObj = isObjectContaining
+ ? (expected as { sample: SentReplayExpected }).sample
+ : (expected as SentReplayExpected);
+
+ if (isObjectContaining) {
+ // eslint-disable-next-line no-console
+ console.warn('`expect.objectContaining` is unnecessary when using the `toHaveSentReplay` matcher');
+ }
+
+ const results = expected
+ ? Object.keys(expectedObj)
+ .map(key => {
+ const actualVal = actualObj[key as keyof SentReplayExpected];
+ const expectedVal = expectedObj[key as keyof SentReplayExpected];
+ const passed = !expectedVal || this.equals(actualVal, expectedVal);
+
+ return { passed, key, expectedVal, actualVal };
+ })
+ .filter(({ passed }) => !passed)
+ : [];
+
+ const pass = Boolean(call && (!expected || results.length === 0));
+
+ return {
+ pass,
+ call,
+ results,
+ };
+}
+
+/**
+ * Only want calls that send replay events, i.e. ignore error events
+ */
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+function getReplayCalls(calls: any[][][]): any[][][] {
+ return calls
+ .map(call => {
+ const arg = call[0];
+ if (arg.length !== 2) {
+ return [];
+ }
+
+ if (!arg[1][0].find(({ type }: { type: string }) => ['replay_event', 'replay_recording'].includes(type))) {
+ return [];
+ }
+
+ return [arg];
+ })
+ .filter(Boolean);
+}
+
+/**
+ * Checks all calls to `fetch` and ensures a replay was uploaded by
+ * checking the `fetch()` request's body.
+ */
+// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
+const toHaveSentReplay = function (
+ _received: Mocked,
+ expected?: SentReplayExpected | { sample: SentReplayExpected; inverse: boolean },
+) {
+ const { calls } = (getClient()?.getTransport()?.send as MockTransport).mock;
+
+ let result: CheckCallForSentReplayResult;
+
+ const expectedKeysLength = expected
+ ? ('sample' in expected ? Object.keys(expected.sample) : Object.keys(expected)).length
+ : 0;
+
+ const replayCalls = getReplayCalls(calls);
+
+ for (const currentCall of replayCalls) {
+ result = checkCallForSentReplay.call(this, currentCall[0], expected);
+ if (result.pass) {
+ break;
+ }
+
+ // stop on the first call where any of the expected obj passes
+ if (result.results.length < expectedKeysLength) {
+ break;
+ }
+ }
+
+ // @ts-expect-error use before assigned
+ const { results, call, pass } = result;
+
+ const options = {
+ isNot: this.isNot,
+ promise: this.promise,
+ };
+
+ return {
+ pass,
+ message: () =>
+ !call
+ ? pass
+ ? 'Expected Replay to not have been sent, but a request was attempted'
+ : 'Expected Replay to have been sent, but a request was not attempted'
+ : `${this.utils.matcherHint('toHaveSentReplay', undefined, undefined, options)}\n\n${results
+ .map(({ key, expectedVal, actualVal }: Result) =>
+ printDiffOrStringify(expectedVal, actualVal, `Expected (key: ${key})`, `Received (key: ${key})`),
+ )
+ .join('\n')}`,
+ };
+};
+
+/**
+ * Checks the last call to `fetch` and ensures a replay was uploaded by
+ * checking the `fetch()` request's body.
+ */
+// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
+const toHaveLastSentReplay = function (
+ _received: Mocked,
+ expected?: SentReplayExpected | { sample: SentReplayExpected; inverse: boolean },
+) {
+ const { calls } = (getClient()?.getTransport()?.send as MockTransport).mock;
+ const replayCalls = getReplayCalls(calls);
+
+ const lastCall = replayCalls[calls.length - 1]?.[0];
+
+ const { results, call, pass } = checkCallForSentReplay.call(this, lastCall, expected);
+
+ const options = {
+ isNot: this.isNot,
+ promise: this.promise,
+ };
+
+ return {
+ pass,
+ message: () =>
+ !call
+ ? pass
+ ? 'Expected Replay to not have been sent, but a request was attempted'
+ : 'Expected Replay to have last been sent, but a request was not attempted'
+ : `${this.utils.matcherHint('toHaveSentReplay', undefined, undefined, options)}\n\n${results
+ .map(({ key, expectedVal, actualVal }: Result) =>
+ printDiffOrStringify(expectedVal, actualVal, `Expected (key: ${key})`, `Received (key: ${key})`),
+ )
+ .join('\n')}`,
+ };
+};
+
+expect.extend({
+ toHaveSameSession,
+ toHaveSentReplay,
+ toHaveLastSentReplay,
+});
+
+interface CustomMatchers {
+ toHaveSentReplay(expected?: SentReplayExpected): R;
+ toHaveLastSentReplay(expected?: SentReplayExpected): R;
+ toHaveSameSession(expected: undefined | Session): R;
+}
+
+declare module 'vitest' {
+ type Assertion = CustomMatchers;
+ type AsymmetricMatchersContaining = CustomMatchers;
+}
diff --git a/packages/launchdarkly/tsconfig.json b/packages/launchdarkly/tsconfig.json
new file mode 100644
index 000000000000..cd1b8207ea06
--- /dev/null
+++ b/packages/launchdarkly/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "lib": ["DOM", "ES2018"],
+ "module": "esnext"
+ },
+ "include": ["src/**/*.ts"]
+}
diff --git a/packages/launchdarkly/tsconfig.test.json b/packages/launchdarkly/tsconfig.test.json
new file mode 100644
index 000000000000..bb7130d948c0
--- /dev/null
+++ b/packages/launchdarkly/tsconfig.test.json
@@ -0,0 +1,15 @@
+{
+ "extends": "./tsconfig.json",
+
+ "include": ["test/**/*.ts", "vitest.config.ts", "test.setup.ts"],
+
+ "compilerOptions": {
+ "types": ["node"],
+ "esModuleInterop": true,
+ "allowJs": true,
+ "noImplicitAny": true,
+ "noImplicitThis": false,
+ "strictNullChecks": true,
+ "strictPropertyInitialization": false
+ }
+}
diff --git a/packages/launchdarkly/tsconfig.types.json b/packages/launchdarkly/tsconfig.types.json
new file mode 100644
index 000000000000..374fd9bc9364
--- /dev/null
+++ b/packages/launchdarkly/tsconfig.types.json
@@ -0,0 +1,10 @@
+{
+ "extends": "./tsconfig.json",
+
+ "compilerOptions": {
+ "declaration": true,
+ "declarationMap": true,
+ "emitDeclarationOnly": true,
+ "outDir": "build/npm/types"
+ }
+}
diff --git a/packages/launchdarkly/vitest.config.ts b/packages/launchdarkly/vitest.config.ts
new file mode 100644
index 000000000000..976d9c37074d
--- /dev/null
+++ b/packages/launchdarkly/vitest.config.ts
@@ -0,0 +1,12 @@
+import { defineConfig } from 'vitest/config';
+
+import baseConfig from '../../vite/vite.config';
+
+export default defineConfig({
+ ...baseConfig,
+ test: {
+ ...baseConfig.test,
+ setupFiles: ['./test.setup.ts'],
+ reporters: ['default'],
+ },
+});
diff --git a/packages/replay-internal/src/types/launchdarkly.ts b/packages/replay-internal/src/types/launchdarkly.ts
new file mode 100644
index 000000000000..b06ecc734266
--- /dev/null
+++ b/packages/replay-internal/src/types/launchdarkly.ts
@@ -0,0 +1,4 @@
+// Integration options
+
+// Integration interface?
+
From 419cd83eaeb762a7d803f8b288c359004d9bd3c9 Mon Sep 17 00:00:00 2001
From: Andrew Liu <159852527+aliu39@users.noreply.github.com>
Date: Tue, 29 Oct 2024 14:11:03 -0700
Subject: [PATCH 02/19] Revert changelog and move types file
---
CHANGELOG.md | 50 -------------------
.../src/types/launchdarkly.ts | 0
2 files changed, 50 deletions(-)
rename packages/{replay-internal => launchdarkly}/src/types/launchdarkly.ts (100%)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d584d65cff65..4bb9478862cc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,56 +10,6 @@
- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott
-## 8.36.0
-
-### Important Changes
-
-- **feat(nuxt): Add Sentry Pinia plugin ([#14047](https://github.com/getsentry/sentry-javascript/pull/14047))**
-
-The Nuxt SDK now allows you to track Pinia state for captured errors. To enable the Pinia plugin, set the `trackPinia` option to `true` in your client config:
-
-```ts
-// sentry.client.config.ts
-
-Sentry.init({
- trackPinia: true,
-});
-```
-
-Read more about the Pinia plugin in the [Sentry Pinia Documentation](https://docs.sentry.io/platforms/javascript/guides/nuxt/features/pinia/).
-
-- **feat(nextjs/vercel-edge/cloudflare): Switch to OTEL for performance monitoring ([#13889](https://github.com/getsentry/sentry-javascript/pull/13889))**
-
-With this release, the Sentry Next.js, and Cloudflare SDKs will now capture performance data based on OpenTelemetry.
-Some exceptions apply in cases where Next.js captures inaccurate data itself.
-
-NOTE: You may experience minor differences in transaction names in Sentry.
-Most importantly transactions for serverside pages router invocations will now be named `GET /[param]/my/route` instead of `/[param]/my/route`.
-This means that those transactions are now better aligned with the OpenTelemetry semantic conventions.
-
-### Other Changes
-
-- deps: Bump bundler plugins and CLI to 2.22.6 and 2.37.0 respectively ([#14050](https://github.com/getsentry/sentry-javascript/pull/14050))
-- feat(deps): bump @opentelemetry/instrumentation-aws-sdk from 0.44.0 to 0.45.0 ([#14099](https://github.com/getsentry/sentry-javascript/pull/14099))
-- feat(deps): bump @opentelemetry/instrumentation-connect from 0.39.0 to 0.40.0 ([#14101](https://github.com/getsentry/sentry-javascript/pull/14101))
-- feat(deps): bump @opentelemetry/instrumentation-express from 0.43.0 to 0.44.0 ([#14102](https://github.com/getsentry/sentry-javascript/pull/14102))
-- feat(deps): bump @opentelemetry/instrumentation-fs from 0.15.0 to 0.16.0 ([#14098](https://github.com/getsentry/sentry-javascript/pull/14098))
-- feat(deps): bump @opentelemetry/instrumentation-kafkajs from 0.3.0 to 0.4.0 ([#14100](https://github.com/getsentry/sentry-javascript/pull/14100))
-- feat(nextjs): Add method and url to route handler request data ([#14084](https://github.com/getsentry/sentry-javascript/pull/14084))
-- feat(node): Add breadcrumbs for `child_process` and `worker_thread` ([#13896](https://github.com/getsentry/sentry-javascript/pull/13896))
-- fix(core): Ensure standalone spans are not sent if SDK is disabled ([#14088](https://github.com/getsentry/sentry-javascript/pull/14088))
-- fix(nextjs): Await flush in api handlers ([#14023](https://github.com/getsentry/sentry-javascript/pull/14023))
-- fix(nextjs): Don't leak webpack types into exports ([#14116](https://github.com/getsentry/sentry-javascript/pull/14116))
-- fix(nextjs): Fix matching logic for file convention type for root level components ([#14038](https://github.com/getsentry/sentry-javascript/pull/14038))
-- fix(nextjs): Respect directives in value injection loader ([#14083](https://github.com/getsentry/sentry-javascript/pull/14083))
-- fix(nuxt): Only wrap `.mjs` entry files in rollup ([#14060](https://github.com/getsentry/sentry-javascript/pull/14060))
-- fix(nuxt): Re-export all exported bindings ([#14086](https://github.com/getsentry/sentry-javascript/pull/14086))
-- fix(nuxt): Server-side setup in readme ([#14049](https://github.com/getsentry/sentry-javascript/pull/14049))
-- fix(profiling-node): Always warn when running on incompatible major version of Node.js ([#14043](https://github.com/getsentry/sentry-javascript/pull/14043))
-- fix(replay): Fix `onError` callback ([#14002](https://github.com/getsentry/sentry-javascript/pull/14002))
-- perf(otel): Only calculate current timestamp once ([#14094](https://github.com/getsentry/sentry-javascript/pull/14094))
-- test(browser-integration): Add sentry DSN route handler by default ([#14095](https://github.com/getsentry/sentry-javascript/pull/14095))
-
## 8.35.0
### Beta release of the official Nuxt Sentry SDK
diff --git a/packages/replay-internal/src/types/launchdarkly.ts b/packages/launchdarkly/src/types/launchdarkly.ts
similarity index 100%
rename from packages/replay-internal/src/types/launchdarkly.ts
rename to packages/launchdarkly/src/types/launchdarkly.ts
From 610da4ded0774726333e0c54e583a5b8d5acc3b7 Mon Sep 17 00:00:00 2001
From: Andrew Liu <159852527+aliu39@users.noreply.github.com>
Date: Tue, 29 Oct 2024 15:05:36 -0700
Subject: [PATCH 03/19] Add ld to dependencies and skeleton code. Get rid of
core/
---
packages/launchdarkly/package.json | 3 +-
packages/launchdarkly/src/core/integration.ts | 0
packages/launchdarkly/src/index.ts | 9 ++--
packages/launchdarkly/src/integration.ts | 43 +++++++++++++++++++
.../launchdarkly/src/types/launchdarkly.ts | 7 ++-
5 files changed, 53 insertions(+), 9 deletions(-)
delete mode 100644 packages/launchdarkly/src/core/integration.ts
create mode 100644 packages/launchdarkly/src/integration.ts
diff --git a/packages/launchdarkly/package.json b/packages/launchdarkly/package.json
index d09f6f977406..6d2a04d3ce19 100644
--- a/packages/launchdarkly/package.json
+++ b/packages/launchdarkly/package.json
@@ -41,7 +41,8 @@
"dependencies": {
"@sentry/core": "8.35.0",
"@sentry/types": "8.35.0",
- "@sentry/utils": "8.35.0"
+ "@sentry/utils": "8.35.0",
+ "launchdarkly-js-client-sdk": "^3.5.0"
},
"scripts": {
"build": "run-p build:transpile build:types build:bundle",
diff --git a/packages/launchdarkly/src/core/integration.ts b/packages/launchdarkly/src/core/integration.ts
deleted file mode 100644
index e69de29bb2d1..000000000000
diff --git a/packages/launchdarkly/src/index.ts b/packages/launchdarkly/src/index.ts
index 9397a3be9cc3..194cee3677c0 100644
--- a/packages/launchdarkly/src/index.ts
+++ b/packages/launchdarkly/src/index.ts
@@ -1,9 +1,6 @@
// This file is used as entry point to generate the npm package and CDN bundles.
-// export { sendFeedback } from './core/sendFeedback';
-// export { buildFeedbackIntegration } from './core/integration';
-// export { getFeedback } from './core/getFeedback';
-// export { feedbackModalIntegration } from './modal/integration';
-// export { feedbackScreenshotIntegration } from './screenshot/integration';
+export { launchDarklyIntegration } from './integration';
-export { buildLaunchDarklyIntegration } from './core/integration';
+// export type {
+// } from './types';
diff --git a/packages/launchdarkly/src/integration.ts b/packages/launchdarkly/src/integration.ts
new file mode 100644
index 000000000000..5e9f22fb00d7
--- /dev/null
+++ b/packages/launchdarkly/src/integration.ts
@@ -0,0 +1,43 @@
+import type { IntegrationFn } from '@sentry/types';
+import type { LDInspectionFlagUsedHandler } from 'launchdarkly-js-client-sdk';
+import type { LaunchDarklyOptions } from './types';
+
+/**
+ * Sentry integration for capturing feature flags from LaunchDarkly.
+ *
+ * See the [feature flag documentation](TODO:) for more information.
+ *
+ * @example
+ *
+ * ```
+ * Sentry.init({
+ * dsn: '__DSN__',
+ * integrations: [Sentry.replayIntegration()],
+ * });
+ * ```
+ */
+export const launchDarklyIntegration = ((options?: LaunchDarklyOptions) => {
+ const { ldClient } = options;
+
+ return {
+ name: 'launchdarkly',
+
+ setup(client) {
+ // type is Sentry SDK client
+
+ // pseudo-code
+ ldClient.addHandler(FlagUsedHandler());
+ },
+ };
+}) satisfies IntegrationFn;
+
+// https://launchdarkly.github.io/js-client-sdk/interfaces/LDInspectionFlagUsedHandler.html //TODO: rm this link
+class FlagUsedHandler implements LDInspectionFlagUsedHandler {
+ public name = 'sentry-feature-flag-monitor'; // eslint-disable-line @sentry-internal/sdk/no-class-field-initializers
+ public synchronous?: boolean;
+ public type = 'flag-used' as const; // eslint-disable-line @sentry-internal/sdk/no-class-field-initializers
+ public method(flagKey, flagDetail, context) {
+ //TODO:
+ return;
+ }
+}
diff --git a/packages/launchdarkly/src/types/launchdarkly.ts b/packages/launchdarkly/src/types/launchdarkly.ts
index b06ecc734266..efe89c01e2e0 100644
--- a/packages/launchdarkly/src/types/launchdarkly.ts
+++ b/packages/launchdarkly/src/types/launchdarkly.ts
@@ -1,4 +1,7 @@
-// Integration options
+// TODO: could we just put everything in a types.ts file?
-// Integration interface?
+import type { LDClient } from 'launchdarkly-js-client-sdk';
+export type LaunchDarklyOptions = {
+ ldClient: LDClient;
+};
From 5b2e5ec7e6c326aeefe434a822b874efd0a2b0c3 Mon Sep 17 00:00:00 2001
From: Andrew Liu <159852527+aliu39@users.noreply.github.com>
Date: Tue, 29 Oct 2024 15:15:53 -0700
Subject: [PATCH 04/19] Fix readme, rename types file, bring back core/
---
packages/launchdarkly/README.md | 10 ----------
packages/launchdarkly/src/{ => core}/integration.ts | 0
packages/launchdarkly/src/index.ts | 2 +-
.../src/types/{launchdarkly.ts => integration.ts} | 2 --
4 files changed, 1 insertion(+), 13 deletions(-)
rename packages/launchdarkly/src/{ => core}/integration.ts (100%)
rename packages/launchdarkly/src/types/{launchdarkly.ts => integration.ts} (67%)
diff --git a/packages/launchdarkly/README.md b/packages/launchdarkly/README.md
index 336e74da6593..eb1e28a91017 100644
--- a/packages/launchdarkly/README.md
+++ b/packages/launchdarkly/README.md
@@ -10,16 +10,6 @@ This SDK is **considered experimental and in a beta state**. It may experience b
at any time. Please reach out on [GitHub](https://github.com/getsentry/sentry-javascript/issues/new/choose) if you have
any feedback/concerns.
-To view Feedback in Sentry, your
-[Sentry organization must be an early adopter](https://docs.sentry.io/product/accounts/early-adopter-features/).
-
## Installation
-Please read the [offical integration documentation](https://docs.sentry.io/platforms/javascript/user-feedback/) for
-installation instructions.
-
## Configuration
-
-The Feedback integration is highly customizable, please read the
-[official integration documentation](https://docs.sentry.io/platforms/javascript/user-feedback/configuration/) for the
-most up-to-date configuration options.
diff --git a/packages/launchdarkly/src/integration.ts b/packages/launchdarkly/src/core/integration.ts
similarity index 100%
rename from packages/launchdarkly/src/integration.ts
rename to packages/launchdarkly/src/core/integration.ts
diff --git a/packages/launchdarkly/src/index.ts b/packages/launchdarkly/src/index.ts
index 194cee3677c0..8952a4bc1264 100644
--- a/packages/launchdarkly/src/index.ts
+++ b/packages/launchdarkly/src/index.ts
@@ -1,6 +1,6 @@
// This file is used as entry point to generate the npm package and CDN bundles.
-export { launchDarklyIntegration } from './integration';
+export { launchDarklyIntegration } from './core/integration';
// export type {
// } from './types';
diff --git a/packages/launchdarkly/src/types/launchdarkly.ts b/packages/launchdarkly/src/types/integration.ts
similarity index 67%
rename from packages/launchdarkly/src/types/launchdarkly.ts
rename to packages/launchdarkly/src/types/integration.ts
index efe89c01e2e0..ef2f57c869cc 100644
--- a/packages/launchdarkly/src/types/launchdarkly.ts
+++ b/packages/launchdarkly/src/types/integration.ts
@@ -1,5 +1,3 @@
-// TODO: could we just put everything in a types.ts file?
-
import type { LDClient } from 'launchdarkly-js-client-sdk';
export type LaunchDarklyOptions = {
From 66b1253d70aaf0318391902d2785fdbeb27bf100 Mon Sep 17 00:00:00 2001
From: Andrew Liu <159852527+aliu39@users.noreply.github.com>
Date: Wed, 30 Oct 2024 10:42:54 -0700
Subject: [PATCH 05/19] Fix readme 2
---
packages/launchdarkly/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/launchdarkly/README.md b/packages/launchdarkly/README.md
index eb1e28a91017..d35afcc2fce5 100644
--- a/packages/launchdarkly/README.md
+++ b/packages/launchdarkly/README.md
@@ -4,7 +4,7 @@
-# Sentry Integration for Feedback
+# Sentry Integration for LaunchDarkly
This SDK is **considered experimental and in a beta state**. It may experience breaking changes, and may be discontinued
at any time. Please reach out on [GitHub](https://github.com/getsentry/sentry-javascript/issues/new/choose) if you have
From 85acc6d5b5a09afdbdf4c63cd025878a85c8d352 Mon Sep 17 00:00:00 2001
From: Andrew Liu <159852527+aliu39@users.noreply.github.com>
Date: Wed, 30 Oct 2024 11:33:08 -0700
Subject: [PATCH 06/19] Finish implementing, minus scope.flags
---
packages/launchdarkly/src/core/integration.ts | 64 ++++++++++++++-----
.../launchdarkly/src/types/integration.ts | 6 +-
2 files changed, 50 insertions(+), 20 deletions(-)
diff --git a/packages/launchdarkly/src/core/integration.ts b/packages/launchdarkly/src/core/integration.ts
index 5e9f22fb00d7..c12130a04448 100644
--- a/packages/launchdarkly/src/core/integration.ts
+++ b/packages/launchdarkly/src/core/integration.ts
@@ -1,7 +1,13 @@
-import type { IntegrationFn } from '@sentry/types';
-import type { LDInspectionFlagUsedHandler } from 'launchdarkly-js-client-sdk';
+/* eslint-disable @sentry-internal/sdk/no-class-field-initializers */
+
+import * as Sentry from '@sentry/browser';
+import type { Client as SentryClient, Event, EventHint, IntegrationFn } from '@sentry/types';
+import type { LDContext, LDEvaluationDetail, LDInspectionFlagUsedHandler } from 'launchdarkly-js-client-sdk';
import type { LaunchDarklyOptions } from './types';
+// import type { Client } from '/client';
+// import type { Event, EventHint } from './event';
+
/**
* Sentry integration for capturing feature flags from LaunchDarkly.
*
@@ -16,28 +22,54 @@ import type { LaunchDarklyOptions } from './types';
* });
* ```
*/
-export const launchDarklyIntegration = ((options?: LaunchDarklyOptions) => {
- const { ldClient } = options;
+export const launchDarklyIntegration = ((_options?: LaunchDarklyOptions) => {
+ // const { _ldClient } = options;
+ // const ldClient = _ldClient as LDClient; // for type hint
return {
name: 'launchdarkly',
- setup(client) {
- // type is Sentry SDK client
-
- // pseudo-code
- ldClient.addHandler(FlagUsedHandler());
+ processEvent(event: Event, hint: EventHint, client: SentryClient): Event | null | PromiseLike {
+ const scope = Sentry.getCurrentScope(); // client doesn't have getCurrentScope
+ const flagData = { values: scope.flags.get() };
+ if (event.contexts) {
+ event.contexts.flags = flagData;
+ } else {
+ event.contexts = { flags: flagData };
+ }
},
};
}) satisfies IntegrationFn;
-// https://launchdarkly.github.io/js-client-sdk/interfaces/LDInspectionFlagUsedHandler.html //TODO: rm this link
-class FlagUsedHandler implements LDInspectionFlagUsedHandler {
- public name = 'sentry-feature-flag-monitor'; // eslint-disable-line @sentry-internal/sdk/no-class-field-initializers
- public synchronous?: boolean;
- public type = 'flag-used' as const; // eslint-disable-line @sentry-internal/sdk/no-class-field-initializers
- public method(flagKey, flagDetail, context) {
- //TODO:
+/**
+ * https://launchdarkly.github.io/js-client-sdk/interfaces/LDInspectionFlagUsedHandler.html //TODO: rm this link
+ * TODO: docstring
+ */
+export class SentryInspector implements LDInspectionFlagUsedHandler {
+ public name = 'sentry-feature-flag-monitor';
+
+ public synchronous = true; // TODO: T or F?
+
+ public type = 'flag-used' as const;
+
+ /**
+ * TODO: docstring
+ */
+ public method(flagKey: string, flagDetail: LDEvaluationDetail, _context: LDContext): void {
+ if (typeof flagDetail.value === 'boolean') {
+ const flags = Sentry.getCurrentScope().flags;
+ flags.set(flagKey, flagDetail.value);
+ }
return;
}
}
+
+/*
+
+import SentryInspector from @sentry/ld
+
+client = LDClient.init(..., SentryInspector)
+
+sentry.init(integrations: [LDIntegration])
+
+*/
diff --git a/packages/launchdarkly/src/types/integration.ts b/packages/launchdarkly/src/types/integration.ts
index ef2f57c869cc..1d9e763b8ad2 100644
--- a/packages/launchdarkly/src/types/integration.ts
+++ b/packages/launchdarkly/src/types/integration.ts
@@ -1,5 +1,3 @@
-import type { LDClient } from 'launchdarkly-js-client-sdk';
+// import type { LDClient } from 'launchdarkly-js-client-sdk';
-export type LaunchDarklyOptions = {
- ldClient: LDClient;
-};
+export type LaunchDarklyOptions = Record;
From 3b3a767fb8bf90c8919fee0047f04ed8982c2a7b Mon Sep 17 00:00:00 2001
From: Andrew Liu <159852527+aliu39@users.noreply.github.com>
Date: Wed, 30 Oct 2024 15:05:51 -0700
Subject: [PATCH 07/19] Implement flag buffer in sentry scope
---
packages/core/src/scope.ts | 40 +++++++++++++++++++
packages/launchdarkly/package.json | 1 +
packages/launchdarkly/src/core/integration.ts | 18 ++++-----
packages/types/src/flags.ts | 2 +
packages/types/src/scope.ts | 11 +++++
5 files changed, 62 insertions(+), 10 deletions(-)
create mode 100644 packages/types/src/flags.ts
diff --git a/packages/core/src/scope.ts b/packages/core/src/scope.ts
index ff89c0d593a9..3fba03feab94 100644
--- a/packages/core/src/scope.ts
+++ b/packages/core/src/scope.ts
@@ -11,6 +11,7 @@ import type {
EventProcessor,
Extra,
Extras,
+ FeatureFlag,
Primitive,
PropagationContext,
RequestSession,
@@ -97,6 +98,12 @@ class ScopeClass implements ScopeInterface {
/** Contains the last event id of a captured event. */
protected _lastEventId?: string;
+ /** LRU cache of flags last evaluated by a feature flag provider. Used by FF integrations. */
+ protected _flagBuffer: FeatureFlag[];
+
+ /** Max size of the flagBuffer */
+ protected _flagBufferSize: number; // TODO: make const?
+
// NOTE: Any field which gets added here should get added not only to the constructor but also to the `clone` method.
public constructor() {
@@ -111,6 +118,9 @@ class ScopeClass implements ScopeInterface {
this._contexts = {};
this._sdkProcessingMetadata = {};
this._propagationContext = generatePropagationContext();
+
+ this._flagBuffer = [];
+ this._flagBufferSize = 100;
}
/**
@@ -499,6 +509,36 @@ class ScopeClass implements ScopeInterface {
return this._propagationContext;
}
+ /**
+ * @inheritDoc
+ */
+ public getFlags(): FeatureFlag[] {
+ return this._flagBuffer || [];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public insertFlag(name: string, value: boolean): void {
+ // Check if the flag is already in the buffer
+ const index = this._flagBuffer.findIndex(f => f.flag === name);
+
+ if (index !== -1) {
+ // Delete flag if it is in the buffer
+ this._flagBuffer.splice(index, 1);
+ } else if (this._flagBuffer.length === this._flagBufferSize) {
+ // If at capacity, we need to remove the earliest flag (pop from front)
+ // This will only happen if not a duplicate flag
+ this._flagBuffer.shift();
+ }
+
+ // Push the flag to the end of the queue
+ this._flagBuffer.push({
+ flag: name,
+ result: value,
+ });
+ }
+
/**
* @inheritDoc
*/
diff --git a/packages/launchdarkly/package.json b/packages/launchdarkly/package.json
index 6d2a04d3ce19..8cd3530d3f1e 100644
--- a/packages/launchdarkly/package.json
+++ b/packages/launchdarkly/package.json
@@ -39,6 +39,7 @@
"access": "public"
},
"dependencies": {
+ "@sentry/browser": "^8.35.0",
"@sentry/core": "8.35.0",
"@sentry/types": "8.35.0",
"@sentry/utils": "8.35.0",
diff --git a/packages/launchdarkly/src/core/integration.ts b/packages/launchdarkly/src/core/integration.ts
index c12130a04448..e9321a70a6a2 100644
--- a/packages/launchdarkly/src/core/integration.ts
+++ b/packages/launchdarkly/src/core/integration.ts
@@ -5,9 +5,6 @@ import type { Client as SentryClient, Event, EventHint, IntegrationFn } from '@s
import type { LDContext, LDEvaluationDetail, LDInspectionFlagUsedHandler } from 'launchdarkly-js-client-sdk';
import type { LaunchDarklyOptions } from './types';
-// import type { Client } from '/client';
-// import type { Event, EventHint } from './event';
-
/**
* Sentry integration for capturing feature flags from LaunchDarkly.
*
@@ -16,6 +13,7 @@ import type { LaunchDarklyOptions } from './types';
* @example
*
* ```
+ * TODO:
* Sentry.init({
* dsn: '__DSN__',
* integrations: [Sentry.replayIntegration()],
@@ -29,14 +27,15 @@ export const launchDarklyIntegration = ((_options?: LaunchDarklyOptions) => {
return {
name: 'launchdarkly',
- processEvent(event: Event, hint: EventHint, client: SentryClient): Event | null | PromiseLike {
+ processEvent(event: Event, _hint: EventHint, _client: SentryClient): Event {
const scope = Sentry.getCurrentScope(); // client doesn't have getCurrentScope
- const flagData = { values: scope.flags.get() };
+ const flagContext = { values: scope.getFlags() };
if (event.contexts) {
- event.contexts.flags = flagData;
+ event.contexts.flags = flagContext;
} else {
- event.contexts = { flags: flagData };
+ event.contexts = { flags: flagContext };
}
+ return event;
},
};
}) satisfies IntegrationFn;
@@ -46,7 +45,7 @@ export const launchDarklyIntegration = ((_options?: LaunchDarklyOptions) => {
* TODO: docstring
*/
export class SentryInspector implements LDInspectionFlagUsedHandler {
- public name = 'sentry-feature-flag-monitor';
+ public name = 'sentry-flag-used-handler';
public synchronous = true; // TODO: T or F?
@@ -57,8 +56,7 @@ export class SentryInspector implements LDInspectionFlagUsedHandler {
*/
public method(flagKey: string, flagDetail: LDEvaluationDetail, _context: LDContext): void {
if (typeof flagDetail.value === 'boolean') {
- const flags = Sentry.getCurrentScope().flags;
- flags.set(flagKey, flagDetail.value);
+ Sentry.getCurrentScope().insertFlag(flagKey, flagDetail.value);
}
return;
}
diff --git a/packages/types/src/flags.ts b/packages/types/src/flags.ts
new file mode 100644
index 000000000000..f7aa2a5ce672
--- /dev/null
+++ b/packages/types/src/flags.ts
@@ -0,0 +1,2 @@
+// Key names match the type used by Sentry frontend.
+export type FeatureFlag = { flag: string; result: boolean };
diff --git a/packages/types/src/scope.ts b/packages/types/src/scope.ts
index a4b91f4b5d96..ea3477eeab28 100644
--- a/packages/types/src/scope.ts
+++ b/packages/types/src/scope.ts
@@ -5,6 +5,7 @@ import type { Context, Contexts } from './context';
import type { Event, EventHint } from './event';
import type { EventProcessor } from './eventprocessor';
import type { Extra, Extras } from './extra';
+import type { FeatureFlag } from './flags';
import type { Primitive } from './misc';
import type { RequestSession, Session } from './session';
import type { SeverityLevel } from './severity';
@@ -231,6 +232,16 @@ export interface Scope {
*/
getPropagationContext(): PropagationContext;
+ /**
+ * TODO: michelle
+ */
+ getFlags(): FeatureFlag[];
+
+ /**
+ * TODO: michelle
+ */
+ insertFlag(name: string, value: boolean): void;
+
/**
* Capture an exception for this scope.
*
From ab3181db8a73d9f2adeee2cb68e50815cfa1c4c3 Mon Sep 17 00:00:00 2001
From: Andrew Liu <159852527+aliu39@users.noreply.github.com>
Date: Wed, 30 Oct 2024 15:07:03 -0700
Subject: [PATCH 08/19] Revert changelog
---
CHANGELOG.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 50 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4bb9478862cc..d584d65cff65 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,56 @@
- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott
+## 8.36.0
+
+### Important Changes
+
+- **feat(nuxt): Add Sentry Pinia plugin ([#14047](https://github.com/getsentry/sentry-javascript/pull/14047))**
+
+The Nuxt SDK now allows you to track Pinia state for captured errors. To enable the Pinia plugin, set the `trackPinia` option to `true` in your client config:
+
+```ts
+// sentry.client.config.ts
+
+Sentry.init({
+ trackPinia: true,
+});
+```
+
+Read more about the Pinia plugin in the [Sentry Pinia Documentation](https://docs.sentry.io/platforms/javascript/guides/nuxt/features/pinia/).
+
+- **feat(nextjs/vercel-edge/cloudflare): Switch to OTEL for performance monitoring ([#13889](https://github.com/getsentry/sentry-javascript/pull/13889))**
+
+With this release, the Sentry Next.js, and Cloudflare SDKs will now capture performance data based on OpenTelemetry.
+Some exceptions apply in cases where Next.js captures inaccurate data itself.
+
+NOTE: You may experience minor differences in transaction names in Sentry.
+Most importantly transactions for serverside pages router invocations will now be named `GET /[param]/my/route` instead of `/[param]/my/route`.
+This means that those transactions are now better aligned with the OpenTelemetry semantic conventions.
+
+### Other Changes
+
+- deps: Bump bundler plugins and CLI to 2.22.6 and 2.37.0 respectively ([#14050](https://github.com/getsentry/sentry-javascript/pull/14050))
+- feat(deps): bump @opentelemetry/instrumentation-aws-sdk from 0.44.0 to 0.45.0 ([#14099](https://github.com/getsentry/sentry-javascript/pull/14099))
+- feat(deps): bump @opentelemetry/instrumentation-connect from 0.39.0 to 0.40.0 ([#14101](https://github.com/getsentry/sentry-javascript/pull/14101))
+- feat(deps): bump @opentelemetry/instrumentation-express from 0.43.0 to 0.44.0 ([#14102](https://github.com/getsentry/sentry-javascript/pull/14102))
+- feat(deps): bump @opentelemetry/instrumentation-fs from 0.15.0 to 0.16.0 ([#14098](https://github.com/getsentry/sentry-javascript/pull/14098))
+- feat(deps): bump @opentelemetry/instrumentation-kafkajs from 0.3.0 to 0.4.0 ([#14100](https://github.com/getsentry/sentry-javascript/pull/14100))
+- feat(nextjs): Add method and url to route handler request data ([#14084](https://github.com/getsentry/sentry-javascript/pull/14084))
+- feat(node): Add breadcrumbs for `child_process` and `worker_thread` ([#13896](https://github.com/getsentry/sentry-javascript/pull/13896))
+- fix(core): Ensure standalone spans are not sent if SDK is disabled ([#14088](https://github.com/getsentry/sentry-javascript/pull/14088))
+- fix(nextjs): Await flush in api handlers ([#14023](https://github.com/getsentry/sentry-javascript/pull/14023))
+- fix(nextjs): Don't leak webpack types into exports ([#14116](https://github.com/getsentry/sentry-javascript/pull/14116))
+- fix(nextjs): Fix matching logic for file convention type for root level components ([#14038](https://github.com/getsentry/sentry-javascript/pull/14038))
+- fix(nextjs): Respect directives in value injection loader ([#14083](https://github.com/getsentry/sentry-javascript/pull/14083))
+- fix(nuxt): Only wrap `.mjs` entry files in rollup ([#14060](https://github.com/getsentry/sentry-javascript/pull/14060))
+- fix(nuxt): Re-export all exported bindings ([#14086](https://github.com/getsentry/sentry-javascript/pull/14086))
+- fix(nuxt): Server-side setup in readme ([#14049](https://github.com/getsentry/sentry-javascript/pull/14049))
+- fix(profiling-node): Always warn when running on incompatible major version of Node.js ([#14043](https://github.com/getsentry/sentry-javascript/pull/14043))
+- fix(replay): Fix `onError` callback ([#14002](https://github.com/getsentry/sentry-javascript/pull/14002))
+- perf(otel): Only calculate current timestamp once ([#14094](https://github.com/getsentry/sentry-javascript/pull/14094))
+- test(browser-integration): Add sentry DSN route handler by default ([#14095](https://github.com/getsentry/sentry-javascript/pull/14095))
+
## 8.35.0
### Beta release of the official Nuxt Sentry SDK
From 22684ea4775145552652e995068e15c4d2b8323e Mon Sep 17 00:00:00 2001
From: Michelle Zhang <56095982+michellewzhang@users.noreply.github.com>
Date: Wed, 30 Oct 2024 15:33:35 -0700
Subject: [PATCH 09/19] fix types
---
packages/types/src/index.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts
index b100c1e9c26a..ab260d2be4db 100644
--- a/packages/types/src/index.ts
+++ b/packages/types/src/index.ts
@@ -173,3 +173,4 @@ export type {
export type { ParameterizedString } from './parameterize';
export type { ContinuousProfiler, ProfilingIntegration, Profiler } from './profiling';
export type { ViewHierarchyData, ViewHierarchyWindow } from './view-hierarchy';
+export type { FeatureFlag } from './flags';
From 79e5b2478d366f7b073dc7639750cab19b916166 Mon Sep 17 00:00:00 2001
From: Michelle Zhang <56095982+michellewzhang@users.noreply.github.com>
Date: Wed, 30 Oct 2024 15:46:24 -0700
Subject: [PATCH 10/19] docstring
---
packages/types/src/scope.ts | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/packages/types/src/scope.ts b/packages/types/src/scope.ts
index ea3477eeab28..11973afb4af0 100644
--- a/packages/types/src/scope.ts
+++ b/packages/types/src/scope.ts
@@ -1,3 +1,4 @@
+
import type { Attachment } from './attachment';
import type { Breadcrumb } from './breadcrumb';
import type { Client } from './client';
@@ -233,12 +234,13 @@ export interface Scope {
getPropagationContext(): PropagationContext;
/**
- * TODO: michelle
+ * Return the list of recently accessed feature flags.
*/
getFlags(): FeatureFlag[];
/**
- * TODO: michelle
+ * When an integration sends data that a flag name and its value have been evaluated,
+ * add it to the list of recently accessed feature flags.
*/
insertFlag(name: string, value: boolean): void;
From 63649c55764b1d9891567fe3e94854ce3f1394f3 Mon Sep 17 00:00:00 2001
From: Andrew Liu <159852527+aliu39@users.noreply.github.com>
Date: Wed, 30 Oct 2024 16:06:46 -0700
Subject: [PATCH 11/19] Export FeatureFlag type in index.ts
---
packages/types/src/index.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts
index b100c1e9c26a..bf187a15edb5 100644
--- a/packages/types/src/index.ts
+++ b/packages/types/src/index.ts
@@ -56,6 +56,7 @@ export type { Event, EventHint, EventType, ErrorEvent, TransactionEvent } from '
export type { EventProcessor } from './eventprocessor';
export type { Exception } from './exception';
export type { Extra, Extras } from './extra';
+export type { FeatureFlag } from './flags';
// eslint-disable-next-line deprecation/deprecation
export type { Hub } from './hub';
export type { Integration, IntegrationClass, IntegrationFn } from './integration';
From bd0475550bc1b9f21d36e4256d1755a9c277f38e Mon Sep 17 00:00:00 2001
From: Andrew Liu <159852527+aliu39@users.noreply.github.com>
Date: Wed, 30 Oct 2024 16:48:53 -0700
Subject: [PATCH 12/19] Clean up comments
---
packages/launchdarkly/src/core/integration.ts | 14 --------------
packages/launchdarkly/src/types/integration.ts | 4 +---
2 files changed, 1 insertion(+), 17 deletions(-)
diff --git a/packages/launchdarkly/src/core/integration.ts b/packages/launchdarkly/src/core/integration.ts
index e9321a70a6a2..90c9a9194b34 100644
--- a/packages/launchdarkly/src/core/integration.ts
+++ b/packages/launchdarkly/src/core/integration.ts
@@ -21,9 +21,6 @@ import type { LaunchDarklyOptions } from './types';
* ```
*/
export const launchDarklyIntegration = ((_options?: LaunchDarklyOptions) => {
- // const { _ldClient } = options;
- // const ldClient = _ldClient as LDClient; // for type hint
-
return {
name: 'launchdarkly',
@@ -41,7 +38,6 @@ export const launchDarklyIntegration = ((_options?: LaunchDarklyOptions) => {
}) satisfies IntegrationFn;
/**
- * https://launchdarkly.github.io/js-client-sdk/interfaces/LDInspectionFlagUsedHandler.html //TODO: rm this link
* TODO: docstring
*/
export class SentryInspector implements LDInspectionFlagUsedHandler {
@@ -61,13 +57,3 @@ export class SentryInspector implements LDInspectionFlagUsedHandler {
return;
}
}
-
-/*
-
-import SentryInspector from @sentry/ld
-
-client = LDClient.init(..., SentryInspector)
-
-sentry.init(integrations: [LDIntegration])
-
-*/
diff --git a/packages/launchdarkly/src/types/integration.ts b/packages/launchdarkly/src/types/integration.ts
index 1d9e763b8ad2..6516bc7a8ff7 100644
--- a/packages/launchdarkly/src/types/integration.ts
+++ b/packages/launchdarkly/src/types/integration.ts
@@ -1,3 +1 @@
-// import type { LDClient } from 'launchdarkly-js-client-sdk';
-
-export type LaunchDarklyOptions = Record;
+export type LaunchDarklyOptions = Record; //TODO:
From 91a4db9f39d3c3a97a981e09bc835f742e217ffc Mon Sep 17 00:00:00 2001
From: Andrew Liu <159852527+aliu39@users.noreply.github.com>
Date: Thu, 31 Oct 2024 15:07:34 -0700
Subject: [PATCH 13/19] Fix build (uses yalc for scope changes) and use LRUMap
util
---
package.json | 1 +
packages/core/src/scope.ts | 37 +++++------
.../launchdarkly/.yalc/@sentry/types/LICENSE | 21 +++++++
.../.yalc/@sentry/types/README.md | 20 ++++++
.../.yalc/@sentry/types/package.json | 63 +++++++++++++++++++
.../launchdarkly/.yalc/@sentry/types/yalc.sig | 1 +
packages/launchdarkly/package.json | 2 +-
packages/launchdarkly/src/core/integration.ts | 2 +-
.../src/{types/integration.ts => types.ts} | 0
packages/launchdarkly/yalc.lock | 10 +++
packages/types/src/index.ts | 1 -
11 files changed, 134 insertions(+), 24 deletions(-)
create mode 100644 packages/launchdarkly/.yalc/@sentry/types/LICENSE
create mode 100644 packages/launchdarkly/.yalc/@sentry/types/README.md
create mode 100644 packages/launchdarkly/.yalc/@sentry/types/package.json
create mode 100644 packages/launchdarkly/.yalc/@sentry/types/yalc.sig
rename packages/launchdarkly/src/{types/integration.ts => types.ts} (100%)
create mode 100644 packages/launchdarkly/yalc.lock
diff --git a/package.json b/package.json
index bee335619d24..5348ebd8c687 100644
--- a/package.json
+++ b/package.json
@@ -63,6 +63,7 @@
"packages/gatsby",
"packages/google-cloud-serverless",
"packages/integration-shims",
+ "packages/launchdarkly",
"packages/nestjs",
"packages/nextjs",
"packages/node",
diff --git a/packages/core/src/scope.ts b/packages/core/src/scope.ts
index 3fba03feab94..d1bcb34441ae 100644
--- a/packages/core/src/scope.ts
+++ b/packages/core/src/scope.ts
@@ -22,7 +22,14 @@ import type {
SeverityLevel,
User,
} from '@sentry/types';
-import { dateTimestampInSeconds, generatePropagationContext, isPlainObject, logger, uuid4 } from '@sentry/utils';
+import {
+ LRUMap,
+ dateTimestampInSeconds,
+ generatePropagationContext,
+ isPlainObject,
+ logger,
+ uuid4,
+} from '@sentry/utils';
import { updateSession } from './session';
import { _getSpanForScope, _setSpanForScope } from './utils/spanOnScope';
@@ -99,7 +106,7 @@ class ScopeClass implements ScopeInterface {
protected _lastEventId?: string;
/** LRU cache of flags last evaluated by a feature flag provider. Used by FF integrations. */
- protected _flagBuffer: FeatureFlag[];
+ protected _flagBuffer: LRUMap;
/** Max size of the flagBuffer */
protected _flagBufferSize: number; // TODO: make const?
@@ -119,8 +126,8 @@ class ScopeClass implements ScopeInterface {
this._sdkProcessingMetadata = {};
this._propagationContext = generatePropagationContext();
- this._flagBuffer = [];
this._flagBufferSize = 100;
+ this._flagBuffer = new LRUMap(this._flagBufferSize);
}
/**
@@ -513,30 +520,18 @@ class ScopeClass implements ScopeInterface {
* @inheritDoc
*/
public getFlags(): FeatureFlag[] {
- return this._flagBuffer || [];
+ const flags: FeatureFlag[] = [];
+ this._flagBuffer.keys().forEach(key => {
+ flags.push({ flag: key, result: this._flagBuffer.get(key) as boolean });
+ });
+ return flags;
}
/**
* @inheritDoc
*/
public insertFlag(name: string, value: boolean): void {
- // Check if the flag is already in the buffer
- const index = this._flagBuffer.findIndex(f => f.flag === name);
-
- if (index !== -1) {
- // Delete flag if it is in the buffer
- this._flagBuffer.splice(index, 1);
- } else if (this._flagBuffer.length === this._flagBufferSize) {
- // If at capacity, we need to remove the earliest flag (pop from front)
- // This will only happen if not a duplicate flag
- this._flagBuffer.shift();
- }
-
- // Push the flag to the end of the queue
- this._flagBuffer.push({
- flag: name,
- result: value,
- });
+ this._flagBuffer.set(name, value);
}
/**
diff --git a/packages/launchdarkly/.yalc/@sentry/types/LICENSE b/packages/launchdarkly/.yalc/@sentry/types/LICENSE
new file mode 100644
index 000000000000..d5b40b7c4219
--- /dev/null
+++ b/packages/launchdarkly/.yalc/@sentry/types/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019-2024 Functional Software, Inc. dba Sentry
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/launchdarkly/.yalc/@sentry/types/README.md b/packages/launchdarkly/.yalc/@sentry/types/README.md
new file mode 100644
index 000000000000..4c0e2d9cbc34
--- /dev/null
+++ b/packages/launchdarkly/.yalc/@sentry/types/README.md
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+# Sentry JavaScript SDK Types
+
+[](https://www.npmjs.com/package/@sentry/types)
+[](https://www.npmjs.com/package/@sentry/types)
+[](https://www.npmjs.com/package/@sentry/types)
+
+## Links
+
+- [Official SDK Docs](https://docs.sentry.io/quickstart/)
+- [TypeDoc](http://getsentry.github.io/sentry-javascript/)
+
+## General
+
+Common types used by the Sentry JavaScript SDKs.
diff --git a/packages/launchdarkly/.yalc/@sentry/types/package.json b/packages/launchdarkly/.yalc/@sentry/types/package.json
new file mode 100644
index 000000000000..09555640efbf
--- /dev/null
+++ b/packages/launchdarkly/.yalc/@sentry/types/package.json
@@ -0,0 +1,63 @@
+{
+ "name": "@sentry/types",
+ "version": "8.35.0+76139869",
+ "description": "Types for all Sentry JavaScript SDKs",
+ "repository": "git://github.com/getsentry/sentry-javascript.git",
+ "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/types",
+ "author": "Sentry",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.18"
+ },
+ "files": [
+ "/build"
+ ],
+ "main": "build/cjs/index.js",
+ "module": "build/esm/index.js",
+ "types": "build/types/index.d.ts",
+ "exports": {
+ "./package.json": "./package.json",
+ ".": {
+ "import": {
+ "types": "./build/types/index.d.ts",
+ "default": "./build/esm/index.js"
+ },
+ "require": {
+ "types": "./build/types/index.d.ts",
+ "default": "./build/cjs/index.js"
+ }
+ }
+ },
+ "typesVersions": {
+ "<4.9": {
+ "build/types/index.d.ts": [
+ "build/types-ts3.8/index.d.ts"
+ ]
+ }
+ },
+ "publishConfig": {
+ "access": "public"
+ },
+ "scripts": {
+ "build": "run-p build:transpile build:types",
+ "build:dev": "yarn build",
+ "build:transpile": "rollup -c rollup.npm.config.mjs",
+ "build:types": "run-s build:types:core build:types:downlevel",
+ "build:types:core": "tsc -p tsconfig.types.json",
+ "build:types:downlevel": "yarn downlevel-dts build/types build/types-ts3.8 --to ts3.8",
+ "build:watch": "run-p build:transpile:watch build:types:watch",
+ "build:dev:watch": "yarn build:watch",
+ "build:transpile:watch": "rollup -c rollup.npm.config.mjs --watch",
+ "build:types:watch": "tsc -p tsconfig.types.json --watch",
+ "build:tarball": "npm pack",
+ "clean": "rimraf build sentry-types-*.tgz",
+ "lint": "eslint . --format stylish",
+ "fix": "eslint . --format stylish --fix",
+ "yalc:publish": "yalc publish --push --sig"
+ },
+ "volta": {
+ "extends": "../../package.json"
+ },
+ "sideEffects": false,
+ "yalcSig": "761398697aa19d98ec1c2085192f95dc"
+}
diff --git a/packages/launchdarkly/.yalc/@sentry/types/yalc.sig b/packages/launchdarkly/.yalc/@sentry/types/yalc.sig
new file mode 100644
index 000000000000..83ab3038045f
--- /dev/null
+++ b/packages/launchdarkly/.yalc/@sentry/types/yalc.sig
@@ -0,0 +1 @@
+761398697aa19d98ec1c2085192f95dc
\ No newline at end of file
diff --git a/packages/launchdarkly/package.json b/packages/launchdarkly/package.json
index 8cd3530d3f1e..c97f24b64981 100644
--- a/packages/launchdarkly/package.json
+++ b/packages/launchdarkly/package.json
@@ -41,7 +41,7 @@
"dependencies": {
"@sentry/browser": "^8.35.0",
"@sentry/core": "8.35.0",
- "@sentry/types": "8.35.0",
+ "@sentry/types": "file:.yalc/@sentry/types",
"@sentry/utils": "8.35.0",
"launchdarkly-js-client-sdk": "^3.5.0"
},
diff --git a/packages/launchdarkly/src/core/integration.ts b/packages/launchdarkly/src/core/integration.ts
index 90c9a9194b34..9539490e025e 100644
--- a/packages/launchdarkly/src/core/integration.ts
+++ b/packages/launchdarkly/src/core/integration.ts
@@ -3,7 +3,7 @@
import * as Sentry from '@sentry/browser';
import type { Client as SentryClient, Event, EventHint, IntegrationFn } from '@sentry/types';
import type { LDContext, LDEvaluationDetail, LDInspectionFlagUsedHandler } from 'launchdarkly-js-client-sdk';
-import type { LaunchDarklyOptions } from './types';
+import type { LaunchDarklyOptions } from '../types';
/**
* Sentry integration for capturing feature flags from LaunchDarkly.
diff --git a/packages/launchdarkly/src/types/integration.ts b/packages/launchdarkly/src/types.ts
similarity index 100%
rename from packages/launchdarkly/src/types/integration.ts
rename to packages/launchdarkly/src/types.ts
diff --git a/packages/launchdarkly/yalc.lock b/packages/launchdarkly/yalc.lock
new file mode 100644
index 000000000000..a6f21bb34c90
--- /dev/null
+++ b/packages/launchdarkly/yalc.lock
@@ -0,0 +1,10 @@
+{
+ "version": "v1",
+ "packages": {
+ "@sentry/types": {
+ "signature": "761398697aa19d98ec1c2085192f95dc",
+ "file": true,
+ "replaced": "8.35.0"
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts
index 0e173b0986f5..bf187a15edb5 100644
--- a/packages/types/src/index.ts
+++ b/packages/types/src/index.ts
@@ -174,4 +174,3 @@ export type {
export type { ParameterizedString } from './parameterize';
export type { ContinuousProfiler, ProfilingIntegration, Profiler } from './profiling';
export type { ViewHierarchyData, ViewHierarchyWindow } from './view-hierarchy';
-export type { FeatureFlag } from './flags';
From 1ca9d536ec6c07618eda6e97d99a8533b9b14f04 Mon Sep 17 00:00:00 2001
From: Andrew Liu <159852527+aliu39@users.noreply.github.com>
Date: Tue, 5 Nov 2024 14:34:13 -0800
Subject: [PATCH 14/19] Tweak hook name and docs
---
packages/launchdarkly/package.json | 2 +-
packages/launchdarkly/src/core/integration.ts | 6 ++++--
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/packages/launchdarkly/package.json b/packages/launchdarkly/package.json
index c97f24b64981..a69bc2e5ae27 100644
--- a/packages/launchdarkly/package.json
+++ b/packages/launchdarkly/package.json
@@ -1,7 +1,7 @@
{
"name": "@sentry-internal/launchdarkly",
"version": "8.35.0",
- "description": "Sentry SDK integration for user launchdarkly",
+ "description": "Sentry SDK integration for Launch Darkly feature flagging",
"repository": "git://github.com/getsentry/sentry-javascript.git",
"homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/launchdarkly",
"author": "Sentry",
diff --git a/packages/launchdarkly/src/core/integration.ts b/packages/launchdarkly/src/core/integration.ts
index 9539490e025e..8e4f45b82aef 100644
--- a/packages/launchdarkly/src/core/integration.ts
+++ b/packages/launchdarkly/src/core/integration.ts
@@ -38,10 +38,12 @@ export const launchDarklyIntegration = ((_options?: LaunchDarklyOptions) => {
}) satisfies IntegrationFn;
/**
- * TODO: docstring
+ * LaunchDarkly hook that listens for flag evaluations and updates the
+ * flagBuffer in our current scope
+ * TODO: finalize docstring
*/
export class SentryInspector implements LDInspectionFlagUsedHandler {
- public name = 'sentry-flag-used-handler';
+ public name = 'sentry-flag-auditor';
public synchronous = true; // TODO: T or F?
From 893cd62cb9526ef2c1ba56465721a966e3a6db4b Mon Sep 17 00:00:00 2001
From: Andrew Liu <159852527+aliu39@users.noreply.github.com>
Date: Tue, 5 Nov 2024 16:03:02 -0800
Subject: [PATCH 15/19] FlagBuffer class and interface, add to scope._contexts.
Remove old flag field and methods
---
packages/core/src/scope.ts | 29 --------
packages/launchdarkly/src/core/integration.ts | 5 +-
packages/types/src/context.ts | 6 ++
packages/types/src/flags.ts | 33 +++++++++
packages/types/src/scope.ts | 13 ----
packages/utils/src/flagBuffer.ts | 72 +++++++++++++++++++
packages/utils/src/index.ts | 1 +
7 files changed, 115 insertions(+), 44 deletions(-)
create mode 100644 packages/utils/src/flagBuffer.ts
diff --git a/packages/core/src/scope.ts b/packages/core/src/scope.ts
index d1bcb34441ae..a50d5b9ffa7e 100644
--- a/packages/core/src/scope.ts
+++ b/packages/core/src/scope.ts
@@ -11,7 +11,6 @@ import type {
EventProcessor,
Extra,
Extras,
- FeatureFlag,
Primitive,
PropagationContext,
RequestSession,
@@ -23,7 +22,6 @@ import type {
User,
} from '@sentry/types';
import {
- LRUMap,
dateTimestampInSeconds,
generatePropagationContext,
isPlainObject,
@@ -105,12 +103,6 @@ class ScopeClass implements ScopeInterface {
/** Contains the last event id of a captured event. */
protected _lastEventId?: string;
- /** LRU cache of flags last evaluated by a feature flag provider. Used by FF integrations. */
- protected _flagBuffer: LRUMap;
-
- /** Max size of the flagBuffer */
- protected _flagBufferSize: number; // TODO: make const?
-
// NOTE: Any field which gets added here should get added not only to the constructor but also to the `clone` method.
public constructor() {
@@ -125,9 +117,6 @@ class ScopeClass implements ScopeInterface {
this._contexts = {};
this._sdkProcessingMetadata = {};
this._propagationContext = generatePropagationContext();
-
- this._flagBufferSize = 100;
- this._flagBuffer = new LRUMap(this._flagBufferSize);
}
/**
@@ -516,24 +505,6 @@ class ScopeClass implements ScopeInterface {
return this._propagationContext;
}
- /**
- * @inheritDoc
- */
- public getFlags(): FeatureFlag[] {
- const flags: FeatureFlag[] = [];
- this._flagBuffer.keys().forEach(key => {
- flags.push({ flag: key, result: this._flagBuffer.get(key) as boolean });
- });
- return flags;
- }
-
- /**
- * @inheritDoc
- */
- public insertFlag(name: string, value: boolean): void {
- this._flagBuffer.set(name, value);
- }
-
/**
* @inheritDoc
*/
diff --git a/packages/launchdarkly/src/core/integration.ts b/packages/launchdarkly/src/core/integration.ts
index 8e4f45b82aef..bac24fe12bc8 100644
--- a/packages/launchdarkly/src/core/integration.ts
+++ b/packages/launchdarkly/src/core/integration.ts
@@ -45,10 +45,11 @@ export const launchDarklyIntegration = ((_options?: LaunchDarklyOptions) => {
export class SentryInspector implements LDInspectionFlagUsedHandler {
public name = 'sentry-flag-auditor';
- public synchronous = true; // TODO: T or F?
-
public type = 'flag-used' as const;
+ // We don't want the handler to impact the performance of the user's flag evaluations.
+ public synchronous = false;
+
/**
* TODO: docstring
*/
diff --git a/packages/types/src/context.ts b/packages/types/src/context.ts
index 10fc61420e25..39985ac62b42 100644
--- a/packages/types/src/context.ts
+++ b/packages/types/src/context.ts
@@ -1,5 +1,6 @@
import type { Primitive } from './misc';
import type { SpanOrigin } from './span';
+import type { FlagBufferInterface } from './flags'
export type Context = Record;
@@ -13,6 +14,7 @@ export interface Contexts extends Record {
cloud_resource?: CloudResourceContext;
state?: StateContext;
profile?: ProfileContext;
+ flags?: FeatureFlagContext;
}
export interface StateContext extends Record {
@@ -124,3 +126,7 @@ export interface MissingInstrumentationContext extends Record {
package: string;
['javascript.is_cjs']?: boolean;
}
+
+export interface FeatureFlagContext extends Record {
+ flag_buffer: FlagBufferInterface;
+}
diff --git a/packages/types/src/flags.ts b/packages/types/src/flags.ts
index f7aa2a5ce672..534f6c333cd7 100644
--- a/packages/types/src/flags.ts
+++ b/packages/types/src/flags.ts
@@ -1,2 +1,35 @@
// Key names match the type used by Sentry frontend.
export type FeatureFlag = { flag: string; result: boolean };
+
+/**
+ * Ordered LRU cache for storing feature flags in the scope context. The name
+ * of each flag in the buffer is unique, and the output of getAll() is ordered
+ * from oldest to newest.
+ */
+export interface FlagBufferInterface {
+ readonly maxSize: number;
+
+ /**
+ * Returns a deep copy of the current FlagBuffer.
+ */
+ clone(): FlagBufferInterface;
+
+ /**
+ * Returns an ordered array of the flags currently stored in the buffer.
+ * This is in the order of insertion (oldest to newest).
+ */
+ getAll(): readonly FeatureFlag[];
+
+ /**
+ * Add a flag to the buffer. After inserting, the flag is guaranteed to be at
+ * the end of the buffer, with no other flags of the same name in it.
+ *
+ * @param flag
+ */
+ insert(name: string, value: boolean): void;
+
+ /**
+ * Clear the buffer. Returns the number of flags removed.
+ */
+ clear(): number;
+}
diff --git a/packages/types/src/scope.ts b/packages/types/src/scope.ts
index 0cc1b1d2eafd..2d5c72230aef 100644
--- a/packages/types/src/scope.ts
+++ b/packages/types/src/scope.ts
@@ -1,4 +1,3 @@
-
import type { Attachment } from './attachment';
import type { Breadcrumb } from './breadcrumb';
import type { Client } from './client';
@@ -6,7 +5,6 @@ import type { Context, Contexts } from './context';
import type { Event, EventHint } from './event';
import type { EventProcessor } from './eventprocessor';
import type { Extra, Extras } from './extra';
-import type { FeatureFlag } from './flags';
import type { Primitive } from './misc';
import type { RequestSession, Session } from './session';
import type { SeverityLevel } from './severity';
@@ -233,17 +231,6 @@ export interface Scope {
*/
getPropagationContext(): PropagationContext;
- /**
- * Return the list of recently accessed feature flags.
- */
- getFlags(): FeatureFlag[];
-
- /**
- * When an integration sends data that a flag name and its value have been evaluated,
- * add it to the list of recently accessed feature flags.
- */
- insertFlag(name: string, value: boolean): void;
-
/**
* Capture an exception for this scope.
*
diff --git a/packages/utils/src/flagBuffer.ts b/packages/utils/src/flagBuffer.ts
new file mode 100644
index 000000000000..d4c30c03fb0d
--- /dev/null
+++ b/packages/utils/src/flagBuffer.ts
@@ -0,0 +1,72 @@
+import type { FeatureFlag } from '@sentry/types';
+import type { FlagBufferInterface } from '@sentry/types/build/types/flags';
+
+export const DEFAULT_FLAG_BUFFER_SIZE = 100;
+
+/**
+ * Array implementation of types/FlagBufferInterface.
+ *
+ * Ordered LRU cache for storing feature flags in the scope context. The name
+ * of each flag in the buffer is unique, and the output of getAll() is ordered
+ * from oldest to newest.
+ */
+export class FlagBuffer implements FlagBufferInterface {
+ public readonly maxSize: number;
+
+ private readonly _flags: FeatureFlag[];
+
+ public constructor(_maxSize: number = DEFAULT_FLAG_BUFFER_SIZE, _initialFlags: FeatureFlag[] = []) {
+ this.maxSize = _maxSize;
+ if (_initialFlags.length > _maxSize) {
+ throw Error(`_initialFlags param exceeds the maxSize of ${_maxSize}`);
+ }
+ this._flags = _initialFlags;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public clone(): FlagBuffer {
+ return new FlagBuffer(this.maxSize, this._flags);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public getAll(): readonly FeatureFlag[] {
+ return [...this._flags]; // shallow copy
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public insert(name: string, value: boolean): void {
+ // Check if the flag is already in the buffer
+ const index = this._flags.findIndex(f => f.flag === name);
+
+ if (index !== -1) {
+ // The flag was found, remove it from its current position - O(n)
+ this._flags.splice(index, 1);
+ }
+
+ if (this._flags.length === this.maxSize) {
+ // If at capacity, pop the earliest flag - O(n)
+ this._flags.shift();
+ }
+
+ // Push the flag to the end - O(1)
+ this._flags.push({
+ flag: name,
+ result: value,
+ });
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public clear(): number {
+ const length = this._flags.length;
+ this._flags.splice(0, length); // O(n)
+ return length;
+ }
+}
diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts
index 4a2d68ca0d8b..c1a1cd3cd10e 100644
--- a/packages/utils/src/index.ts
+++ b/packages/utils/src/index.ts
@@ -40,3 +40,4 @@ export * from './buildPolyfills';
export * from './propagationContext';
export * from './vercelWaitUntil';
export * from './version';
+export * from './flagBuffer';
From f172683fbd1b6630c0196ef0533d158925bfe2db Mon Sep 17 00:00:00 2001
From: Andrew Liu <159852527+aliu39@users.noreply.github.com>
Date: Tue, 5 Nov 2024 16:09:21 -0800
Subject: [PATCH 16/19] Call clone in scope.clone()
---
packages/core/src/scope.ts | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/packages/core/src/scope.ts b/packages/core/src/scope.ts
index a50d5b9ffa7e..46a5e3c12ec2 100644
--- a/packages/core/src/scope.ts
+++ b/packages/core/src/scope.ts
@@ -128,6 +128,11 @@ class ScopeClass implements ScopeInterface {
newScope._tags = { ...this._tags };
newScope._extra = { ...this._extra };
newScope._contexts = { ...this._contexts };
+ if (this._contexts.flags) {
+ // The flags context requires a deep copy.
+ newScope._contexts.flags = {flag_buffer: this._contexts.flags.flag_buffer.clone()};
+ }
+
newScope._user = this._user;
newScope._level = this._level;
newScope._session = this._session;
From b96c17fe3a4a1413f00c011763610baec84560e4 Mon Sep 17 00:00:00 2001
From: Andrew Liu <159852527+aliu39@users.noreply.github.com>
Date: Tue, 5 Nov 2024 17:04:47 -0800
Subject: [PATCH 17/19] Rewrite as a util fx instead of class. Use in LD
integration
---
packages/core/src/scope.ts | 6 +-
.../launchdarkly/.yalc/@sentry/types/LICENSE | 21 ------
.../.yalc/@sentry/types/README.md | 20 ------
.../.yalc/@sentry/types/package.json | 63 ----------------
.../launchdarkly/.yalc/@sentry/types/yalc.sig | 1 -
packages/launchdarkly/package.json | 4 +-
.../launchdarkly/rollup.bundle.config.mjs | 2 +-
packages/launchdarkly/src/core/integration.ts | 39 +++++-----
packages/launchdarkly/yalc.lock | 10 ---
packages/types/src/context.ts | 5 +-
packages/types/src/flags.ts | 35 +--------
packages/utils/src/flagBuffer.ts | 72 -------------------
packages/utils/src/flags.ts | 38 ++++++++++
packages/utils/src/index.ts | 2 +-
yarn.lock | 42 +++++++++++
15 files changed, 115 insertions(+), 245 deletions(-)
delete mode 100644 packages/launchdarkly/.yalc/@sentry/types/LICENSE
delete mode 100644 packages/launchdarkly/.yalc/@sentry/types/README.md
delete mode 100644 packages/launchdarkly/.yalc/@sentry/types/package.json
delete mode 100644 packages/launchdarkly/.yalc/@sentry/types/yalc.sig
delete mode 100644 packages/launchdarkly/yalc.lock
delete mode 100644 packages/utils/src/flagBuffer.ts
create mode 100644 packages/utils/src/flags.ts
diff --git a/packages/core/src/scope.ts b/packages/core/src/scope.ts
index 46a5e3c12ec2..65e5821a3d1f 100644
--- a/packages/core/src/scope.ts
+++ b/packages/core/src/scope.ts
@@ -129,8 +129,10 @@ class ScopeClass implements ScopeInterface {
newScope._extra = { ...this._extra };
newScope._contexts = { ...this._contexts };
if (this._contexts.flags) {
- // The flags context requires a deep copy.
- newScope._contexts.flags = {flag_buffer: this._contexts.flags.flag_buffer.clone()};
+ // The flags context needs a deep copy.
+ newScope._contexts.flags = {
+ values: [...this._contexts.flags.values]
+ }
}
newScope._user = this._user;
diff --git a/packages/launchdarkly/.yalc/@sentry/types/LICENSE b/packages/launchdarkly/.yalc/@sentry/types/LICENSE
deleted file mode 100644
index d5b40b7c4219..000000000000
--- a/packages/launchdarkly/.yalc/@sentry/types/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2019-2024 Functional Software, Inc. dba Sentry
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is furnished to do
-so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/packages/launchdarkly/.yalc/@sentry/types/README.md b/packages/launchdarkly/.yalc/@sentry/types/README.md
deleted file mode 100644
index 4c0e2d9cbc34..000000000000
--- a/packages/launchdarkly/.yalc/@sentry/types/README.md
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-# Sentry JavaScript SDK Types
-
-[](https://www.npmjs.com/package/@sentry/types)
-[](https://www.npmjs.com/package/@sentry/types)
-[](https://www.npmjs.com/package/@sentry/types)
-
-## Links
-
-- [Official SDK Docs](https://docs.sentry.io/quickstart/)
-- [TypeDoc](http://getsentry.github.io/sentry-javascript/)
-
-## General
-
-Common types used by the Sentry JavaScript SDKs.
diff --git a/packages/launchdarkly/.yalc/@sentry/types/package.json b/packages/launchdarkly/.yalc/@sentry/types/package.json
deleted file mode 100644
index 09555640efbf..000000000000
--- a/packages/launchdarkly/.yalc/@sentry/types/package.json
+++ /dev/null
@@ -1,63 +0,0 @@
-{
- "name": "@sentry/types",
- "version": "8.35.0+76139869",
- "description": "Types for all Sentry JavaScript SDKs",
- "repository": "git://github.com/getsentry/sentry-javascript.git",
- "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/types",
- "author": "Sentry",
- "license": "MIT",
- "engines": {
- "node": ">=14.18"
- },
- "files": [
- "/build"
- ],
- "main": "build/cjs/index.js",
- "module": "build/esm/index.js",
- "types": "build/types/index.d.ts",
- "exports": {
- "./package.json": "./package.json",
- ".": {
- "import": {
- "types": "./build/types/index.d.ts",
- "default": "./build/esm/index.js"
- },
- "require": {
- "types": "./build/types/index.d.ts",
- "default": "./build/cjs/index.js"
- }
- }
- },
- "typesVersions": {
- "<4.9": {
- "build/types/index.d.ts": [
- "build/types-ts3.8/index.d.ts"
- ]
- }
- },
- "publishConfig": {
- "access": "public"
- },
- "scripts": {
- "build": "run-p build:transpile build:types",
- "build:dev": "yarn build",
- "build:transpile": "rollup -c rollup.npm.config.mjs",
- "build:types": "run-s build:types:core build:types:downlevel",
- "build:types:core": "tsc -p tsconfig.types.json",
- "build:types:downlevel": "yarn downlevel-dts build/types build/types-ts3.8 --to ts3.8",
- "build:watch": "run-p build:transpile:watch build:types:watch",
- "build:dev:watch": "yarn build:watch",
- "build:transpile:watch": "rollup -c rollup.npm.config.mjs --watch",
- "build:types:watch": "tsc -p tsconfig.types.json --watch",
- "build:tarball": "npm pack",
- "clean": "rimraf build sentry-types-*.tgz",
- "lint": "eslint . --format stylish",
- "fix": "eslint . --format stylish --fix",
- "yalc:publish": "yalc publish --push --sig"
- },
- "volta": {
- "extends": "../../package.json"
- },
- "sideEffects": false,
- "yalcSig": "761398697aa19d98ec1c2085192f95dc"
-}
diff --git a/packages/launchdarkly/.yalc/@sentry/types/yalc.sig b/packages/launchdarkly/.yalc/@sentry/types/yalc.sig
deleted file mode 100644
index 83ab3038045f..000000000000
--- a/packages/launchdarkly/.yalc/@sentry/types/yalc.sig
+++ /dev/null
@@ -1 +0,0 @@
-761398697aa19d98ec1c2085192f95dc
\ No newline at end of file
diff --git a/packages/launchdarkly/package.json b/packages/launchdarkly/package.json
index a69bc2e5ae27..7d4075bca4e7 100644
--- a/packages/launchdarkly/package.json
+++ b/packages/launchdarkly/package.json
@@ -1,5 +1,5 @@
{
- "name": "@sentry-internal/launchdarkly",
+ "name": "@sentry/launchdarkly",
"version": "8.35.0",
"description": "Sentry SDK integration for Launch Darkly feature flagging",
"repository": "git://github.com/getsentry/sentry-javascript.git",
@@ -41,7 +41,7 @@
"dependencies": {
"@sentry/browser": "^8.35.0",
"@sentry/core": "8.35.0",
- "@sentry/types": "file:.yalc/@sentry/types",
+ "@sentry/types": "8.35.0",
"@sentry/utils": "8.35.0",
"launchdarkly-js-client-sdk": "^3.5.0"
},
diff --git a/packages/launchdarkly/rollup.bundle.config.mjs b/packages/launchdarkly/rollup.bundle.config.mjs
index cc29fadbc093..f79e2cd63d7e 100644
--- a/packages/launchdarkly/rollup.bundle.config.mjs
+++ b/packages/launchdarkly/rollup.bundle.config.mjs
@@ -3,7 +3,7 @@ import { makeBaseBundleConfig, makeBundleConfigVariants } from '@sentry-internal
const baseBundleConfig = makeBaseBundleConfig({
bundleType: 'addon',
entrypoints: ['src/index.ts'],
- licenseTitle: '@sentry-internal/launchdarkly',
+ licenseTitle: '@sentry/launchdarkly',
outputFileBase: () => 'bundles/launchdarkly',
});
diff --git a/packages/launchdarkly/src/core/integration.ts b/packages/launchdarkly/src/core/integration.ts
index bac24fe12bc8..e9702251b048 100644
--- a/packages/launchdarkly/src/core/integration.ts
+++ b/packages/launchdarkly/src/core/integration.ts
@@ -4,6 +4,7 @@ import * as Sentry from '@sentry/browser';
import type { Client as SentryClient, Event, EventHint, IntegrationFn } from '@sentry/types';
import type { LDContext, LDEvaluationDetail, LDInspectionFlagUsedHandler } from 'launchdarkly-js-client-sdk';
import type { LaunchDarklyOptions } from '../types';
+import { insertToFlagBuffer } from '@sentry/utils';
/**
* Sentry integration for capturing feature flags from LaunchDarkly.
@@ -11,13 +12,12 @@ import type { LaunchDarklyOptions } from '../types';
* See the [feature flag documentation](TODO:) for more information.
*
* @example
- *
* ```
- * TODO:
- * Sentry.init({
- * dsn: '__DSN__',
- * integrations: [Sentry.replayIntegration()],
- * });
+ * import {SentryInspector, launchDarklyIntegration} from '@sentry/launchdarkly';
+ * import {LDClient} from 'launchdarkly-js-client-sdk';
+ *
+ * Sentry.init(..., integrations: [launchDarklyIntegration()])
+ * const ldClient = LDClient.initialize(..., inspectors: [SentryInspector]);
* ```
*/
export const launchDarklyIntegration = ((_options?: LaunchDarklyOptions) => {
@@ -25,13 +25,13 @@ export const launchDarklyIntegration = ((_options?: LaunchDarklyOptions) => {
name: 'launchdarkly',
processEvent(event: Event, _hint: EventHint, _client: SentryClient): Event {
- const scope = Sentry.getCurrentScope(); // client doesn't have getCurrentScope
- const flagContext = { values: scope.getFlags() };
- if (event.contexts) {
- event.contexts.flags = flagContext;
- } else {
- event.contexts = { flags: flagContext };
+ const scope = Sentry.getCurrentScope();
+ const flagContext = scope.getScopeData().contexts.flags;
+
+ if (event.contexts === undefined) {
+ event.contexts = {};
}
+ event.contexts.flags = flagContext;
return event;
},
};
@@ -39,8 +39,10 @@ export const launchDarklyIntegration = ((_options?: LaunchDarklyOptions) => {
/**
* LaunchDarkly hook that listens for flag evaluations and updates the
- * flagBuffer in our current scope
- * TODO: finalize docstring
+ * flagBuffer in our current scope.
+ *
+ * This needs to be registered separately in the LDClient, after initializing
+ * Sentry.
*/
export class SentryInspector implements LDInspectionFlagUsedHandler {
public name = 'sentry-flag-auditor';
@@ -51,11 +53,16 @@ export class SentryInspector implements LDInspectionFlagUsedHandler {
public synchronous = false;
/**
- * TODO: docstring
+ * Handle a flag evaluation by storing its name and value on the current scope.
*/
public method(flagKey: string, flagDetail: LDEvaluationDetail, _context: LDContext): void {
if (typeof flagDetail.value === 'boolean') {
- Sentry.getCurrentScope().insertFlag(flagKey, flagDetail.value);
+ const scopeContexts = Sentry.getCurrentScope().getScopeData().contexts;
+ if (!scopeContexts.flags) {
+ scopeContexts.flags = {values: []}
+ }
+ const flagBuffer = scopeContexts.flags.values;
+ insertToFlagBuffer(flagBuffer, flagKey, flagDetail.value);
}
return;
}
diff --git a/packages/launchdarkly/yalc.lock b/packages/launchdarkly/yalc.lock
deleted file mode 100644
index a6f21bb34c90..000000000000
--- a/packages/launchdarkly/yalc.lock
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "version": "v1",
- "packages": {
- "@sentry/types": {
- "signature": "761398697aa19d98ec1c2085192f95dc",
- "file": true,
- "replaced": "8.35.0"
- }
- }
-}
\ No newline at end of file
diff --git a/packages/types/src/context.ts b/packages/types/src/context.ts
index 39985ac62b42..3e757928923a 100644
--- a/packages/types/src/context.ts
+++ b/packages/types/src/context.ts
@@ -1,6 +1,6 @@
import type { Primitive } from './misc';
import type { SpanOrigin } from './span';
-import type { FlagBufferInterface } from './flags'
+import type { FeatureFlag } from './flags'
export type Context = Record;
@@ -128,5 +128,6 @@ export interface MissingInstrumentationContext extends Record {
}
export interface FeatureFlagContext extends Record {
- flag_buffer: FlagBufferInterface;
+ // This should only be modified by @sentry/util methods (insertToFlagBuffer).
+ readonly values: FeatureFlag[];
}
diff --git a/packages/types/src/flags.ts b/packages/types/src/flags.ts
index 534f6c333cd7..c117fbd0d686 100644
--- a/packages/types/src/flags.ts
+++ b/packages/types/src/flags.ts
@@ -1,35 +1,2 @@
// Key names match the type used by Sentry frontend.
-export type FeatureFlag = { flag: string; result: boolean };
-
-/**
- * Ordered LRU cache for storing feature flags in the scope context. The name
- * of each flag in the buffer is unique, and the output of getAll() is ordered
- * from oldest to newest.
- */
-export interface FlagBufferInterface {
- readonly maxSize: number;
-
- /**
- * Returns a deep copy of the current FlagBuffer.
- */
- clone(): FlagBufferInterface;
-
- /**
- * Returns an ordered array of the flags currently stored in the buffer.
- * This is in the order of insertion (oldest to newest).
- */
- getAll(): readonly FeatureFlag[];
-
- /**
- * Add a flag to the buffer. After inserting, the flag is guaranteed to be at
- * the end of the buffer, with no other flags of the same name in it.
- *
- * @param flag
- */
- insert(name: string, value: boolean): void;
-
- /**
- * Clear the buffer. Returns the number of flags removed.
- */
- clear(): number;
-}
+export type FeatureFlag = { readonly flag: string; readonly result: boolean };
diff --git a/packages/utils/src/flagBuffer.ts b/packages/utils/src/flagBuffer.ts
deleted file mode 100644
index d4c30c03fb0d..000000000000
--- a/packages/utils/src/flagBuffer.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-import type { FeatureFlag } from '@sentry/types';
-import type { FlagBufferInterface } from '@sentry/types/build/types/flags';
-
-export const DEFAULT_FLAG_BUFFER_SIZE = 100;
-
-/**
- * Array implementation of types/FlagBufferInterface.
- *
- * Ordered LRU cache for storing feature flags in the scope context. The name
- * of each flag in the buffer is unique, and the output of getAll() is ordered
- * from oldest to newest.
- */
-export class FlagBuffer implements FlagBufferInterface {
- public readonly maxSize: number;
-
- private readonly _flags: FeatureFlag[];
-
- public constructor(_maxSize: number = DEFAULT_FLAG_BUFFER_SIZE, _initialFlags: FeatureFlag[] = []) {
- this.maxSize = _maxSize;
- if (_initialFlags.length > _maxSize) {
- throw Error(`_initialFlags param exceeds the maxSize of ${_maxSize}`);
- }
- this._flags = _initialFlags;
- }
-
- /**
- * @inheritdoc
- */
- public clone(): FlagBuffer {
- return new FlagBuffer(this.maxSize, this._flags);
- }
-
- /**
- * @inheritdoc
- */
- public getAll(): readonly FeatureFlag[] {
- return [...this._flags]; // shallow copy
- }
-
- /**
- * @inheritdoc
- */
- public insert(name: string, value: boolean): void {
- // Check if the flag is already in the buffer
- const index = this._flags.findIndex(f => f.flag === name);
-
- if (index !== -1) {
- // The flag was found, remove it from its current position - O(n)
- this._flags.splice(index, 1);
- }
-
- if (this._flags.length === this.maxSize) {
- // If at capacity, pop the earliest flag - O(n)
- this._flags.shift();
- }
-
- // Push the flag to the end - O(1)
- this._flags.push({
- flag: name,
- result: value,
- });
- }
-
- /**
- * @inheritdoc
- */
- public clear(): number {
- const length = this._flags.length;
- this._flags.splice(0, length); // O(n)
- return length;
- }
-}
diff --git a/packages/utils/src/flags.ts b/packages/utils/src/flags.ts
new file mode 100644
index 000000000000..2cc02b989577
--- /dev/null
+++ b/packages/utils/src/flags.ts
@@ -0,0 +1,38 @@
+import type { FeatureFlag } from '@sentry/types';
+
+/**
+ * Ordered LRU cache for storing feature flags in the scope context. The name
+ * of each flag in the buffer is unique, and the output of getAll() is ordered
+ * from oldest to newest.
+ */
+
+export const FLAG_BUFFER_SIZE = 100;
+
+/**
+ * Insert into a FeatureFlag array while maintaining ordered LRU properties.
+ * After inserting:
+ * - The flag is guaranteed to be at the end of `flags`.
+ * - No other flags with the same name exist in `flags`.
+ * - The length of `flags` does not exceed FLAG_BUFFER_SIZE. If needed, the
+ * oldest inserted flag is evicted.
+ */
+export function insertToFlagBuffer(flags: FeatureFlag[], name: string, value: boolean): void {
+ // Check if the flag is already in the buffer
+ const index = flags.findIndex(f => f.flag === name);
+
+ if (index !== -1) {
+ // The flag was found, remove it from its current position - O(n)
+ flags.splice(index, 1);
+ }
+
+ if (flags.length === FLAG_BUFFER_SIZE) {
+ // If at capacity, pop the earliest flag - O(n)
+ flags.shift();
+ }
+
+ // Push the flag to the end - O(1)
+ flags.push({
+ flag: name,
+ result: value,
+ });
+}
diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts
index c1a1cd3cd10e..1f8a86960a03 100644
--- a/packages/utils/src/index.ts
+++ b/packages/utils/src/index.ts
@@ -40,4 +40,4 @@ export * from './buildPolyfills';
export * from './propagationContext';
export * from './vercelWaitUntil';
export * from './version';
-export * from './flagBuffer';
+export * from './flags';
diff --git a/yarn.lock b/yarn.lock
index 652c5721f5b6..4e2971138219 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -8390,6 +8390,14 @@
"@sentry/cli-win32-i686" "2.37.0"
"@sentry/cli-win32-x64" "2.37.0"
+"@sentry/core@8.35.0":
+ version "8.35.0"
+ resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.35.0.tgz#17090f4d2d3bb983d9d99ecd2d27f4e9e107e0b0"
+ integrity sha512-Ci0Nmtw5ETWLqQJGY4dyF+iWh7PWKy6k303fCEoEmqj2czDrKJCp7yHBNV0XYbo00prj2ZTbCr6I7albYiyONA==
+ dependencies:
+ "@sentry/types" "8.35.0"
+ "@sentry/utils" "8.35.0"
+
"@sentry/rollup-plugin@2.22.6":
version "2.22.6"
resolved "https://registry.yarnpkg.com/@sentry/rollup-plugin/-/rollup-plugin-2.22.6.tgz#74e9ab69729ee024a497b21b66be3b1992e786d5"
@@ -8398,6 +8406,18 @@
"@sentry/bundler-plugin-core" "2.22.6"
unplugin "1.0.1"
+"@sentry/types@8.35.0":
+ version "8.35.0"
+ resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.35.0.tgz#535c807800f7e378f61416f30177c0ef81b95012"
+ integrity sha512-AVEZjb16MlYPifiDDvJ19dPQyDn0jlrtC1PHs6ZKO+Rzyz+2EX2BRdszvanqArldexPoU1p5Bn2w81XZNXThBA==
+
+"@sentry/utils@8.35.0":
+ version "8.35.0"
+ resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-8.35.0.tgz#1e099fcbc60040091c79f028a83226c145d588ee"
+ integrity sha512-MdMb6+uXjqND7qIPWhulubpSeHzia6HtxeJa8jYI09OCvIcmNGPydv/Gx/LZBwosfMHrLdTWcFH7Y7aCxrq7cg==
+ dependencies:
+ "@sentry/types" "8.35.0"
+
"@sentry/vite-plugin@2.22.6", "@sentry/vite-plugin@^2.22.6":
version "2.22.6"
resolved "https://registry.yarnpkg.com/@sentry/vite-plugin/-/vite-plugin-2.22.6.tgz#d08a1ede05f137636d5b3c61845d24c0114f0d76"
@@ -18029,6 +18049,11 @@ fake-indexeddb@^4.0.1:
dependencies:
realistic-structured-clone "^3.0.0"
+fast-deep-equal@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
+ integrity sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==
+
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
@@ -22272,6 +22297,23 @@ launch-editor@^2.9.1:
picocolors "^1.0.0"
shell-quote "^1.8.1"
+launchdarkly-js-client-sdk@^3.5.0:
+ version "3.5.0"
+ resolved "https://registry.yarnpkg.com/launchdarkly-js-client-sdk/-/launchdarkly-js-client-sdk-3.5.0.tgz#cb0e3d6fc21e56750aa86fcaf75733d7f510946f"
+ integrity sha512-3dgxC9S8K2ix6qjdArjZGOJPtAytgfQTuE+vWgjWJK7725rpYbuqbHghIFr5B0+WyWyVBYANldjWd1JdtYLwsw==
+ dependencies:
+ escape-string-regexp "^4.0.0"
+ launchdarkly-js-sdk-common "5.4.0"
+
+launchdarkly-js-sdk-common@5.4.0:
+ version "5.4.0"
+ resolved "https://registry.yarnpkg.com/launchdarkly-js-sdk-common/-/launchdarkly-js-sdk-common-5.4.0.tgz#c9787daebe0b583b01d2334218524ea142c85001"
+ integrity sha512-Kb3SDcB6S0HUpFNBZgtEt0YUV/fVkyg+gODfaOCJQ0Y0ApxLKNmmJBZOrPE2qIdzw536u4BqEjtaJdqJWCEElg==
+ dependencies:
+ base64-js "^1.3.0"
+ fast-deep-equal "^2.0.1"
+ uuid "^8.0.0"
+
lazystream@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.1.tgz#494c831062f1f9408251ec44db1cba29242a2638"
From f9a659b8aa2f3d8ebf91ff2b28cc177d856d4bde Mon Sep 17 00:00:00 2001
From: Andrew Liu <159852527+aliu39@users.noreply.github.com>
Date: Tue, 5 Nov 2024 17:09:48 -0800
Subject: [PATCH 18/19] Delete LD pkg
---
packages/launchdarkly/.eslintignore | 2 -
packages/launchdarkly/.eslintrc.js | 39 ---
packages/launchdarkly/.gitignore | 4 -
packages/launchdarkly/LICENSE | 21 --
packages/launchdarkly/README.md | 15 --
packages/launchdarkly/package.json | 74 -----
.../launchdarkly/rollup.bundle.config.mjs | 12 -
packages/launchdarkly/rollup.npm.config.mjs | 19 --
.../scripts/shim-preact-export.js | 75 ------
packages/launchdarkly/src/core/integration.ts | 69 -----
packages/launchdarkly/src/index.ts | 6 -
packages/launchdarkly/src/types.ts | 1 -
packages/launchdarkly/test.setup.ts | 255 ------------------
packages/launchdarkly/tsconfig.json | 8 -
packages/launchdarkly/tsconfig.test.json | 15 --
packages/launchdarkly/tsconfig.types.json | 10 -
packages/launchdarkly/vitest.config.ts | 12 -
17 files changed, 637 deletions(-)
delete mode 100644 packages/launchdarkly/.eslintignore
delete mode 100644 packages/launchdarkly/.eslintrc.js
delete mode 100644 packages/launchdarkly/.gitignore
delete mode 100644 packages/launchdarkly/LICENSE
delete mode 100644 packages/launchdarkly/README.md
delete mode 100644 packages/launchdarkly/package.json
delete mode 100644 packages/launchdarkly/rollup.bundle.config.mjs
delete mode 100644 packages/launchdarkly/rollup.npm.config.mjs
delete mode 100644 packages/launchdarkly/scripts/shim-preact-export.js
delete mode 100644 packages/launchdarkly/src/core/integration.ts
delete mode 100644 packages/launchdarkly/src/index.ts
delete mode 100644 packages/launchdarkly/src/types.ts
delete mode 100644 packages/launchdarkly/test.setup.ts
delete mode 100644 packages/launchdarkly/tsconfig.json
delete mode 100644 packages/launchdarkly/tsconfig.test.json
delete mode 100644 packages/launchdarkly/tsconfig.types.json
delete mode 100644 packages/launchdarkly/vitest.config.ts
diff --git a/packages/launchdarkly/.eslintignore b/packages/launchdarkly/.eslintignore
deleted file mode 100644
index b38db2f296ff..000000000000
--- a/packages/launchdarkly/.eslintignore
+++ /dev/null
@@ -1,2 +0,0 @@
-node_modules/
-build/
diff --git a/packages/launchdarkly/.eslintrc.js b/packages/launchdarkly/.eslintrc.js
deleted file mode 100644
index dc39a10b354b..000000000000
--- a/packages/launchdarkly/.eslintrc.js
+++ /dev/null
@@ -1,39 +0,0 @@
-// Note: All paths are relative to the directory in which eslint is being run, rather than the directory where this file
-// lives
-
-// ESLint config docs: https://eslint.org/docs/user-guide/configuring/
-
-module.exports = {
- extends: ['../../.eslintrc.js'],
- overrides: [
- {
- files: ['src/**/*.ts'],
- },
- {
- files: ['test.setup.ts', 'vitest.config.ts'],
- parserOptions: {
- project: ['tsconfig.test.json'],
- },
- rules: {
- 'no-console': 'off',
- },
- },
- {
- files: ['test/**/*.ts'],
-
- rules: {
- // most of these errors come from `new Promise(process.nextTick)`
- '@typescript-eslint/unbound-method': 'off',
- // TODO: decide if we want to enable this again after the migration
- // We can take the freedom to be a bit more lenient with tests
- '@typescript-eslint/no-floating-promises': 'off',
- },
- },
- {
- files: ['src/types/deprecated.ts'],
- rules: {
- '@typescript-eslint/naming-convention': 'off',
- },
- },
- ],
-};
diff --git a/packages/launchdarkly/.gitignore b/packages/launchdarkly/.gitignore
deleted file mode 100644
index 363d3467c6fa..000000000000
--- a/packages/launchdarkly/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-node_modules
-/*.tgz
-.eslintcache
-build
diff --git a/packages/launchdarkly/LICENSE b/packages/launchdarkly/LICENSE
deleted file mode 100644
index 6bfafc44539c..000000000000
--- a/packages/launchdarkly/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2023-2024 Functional Software, Inc. dba Sentry
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is furnished to do
-so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/packages/launchdarkly/README.md b/packages/launchdarkly/README.md
deleted file mode 100644
index d35afcc2fce5..000000000000
--- a/packages/launchdarkly/README.md
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-# Sentry Integration for LaunchDarkly
-
-This SDK is **considered experimental and in a beta state**. It may experience breaking changes, and may be discontinued
-at any time. Please reach out on [GitHub](https://github.com/getsentry/sentry-javascript/issues/new/choose) if you have
-any feedback/concerns.
-
-## Installation
-
-## Configuration
diff --git a/packages/launchdarkly/package.json b/packages/launchdarkly/package.json
deleted file mode 100644
index 7d4075bca4e7..000000000000
--- a/packages/launchdarkly/package.json
+++ /dev/null
@@ -1,74 +0,0 @@
-{
- "name": "@sentry/launchdarkly",
- "version": "8.35.0",
- "description": "Sentry SDK integration for Launch Darkly feature flagging",
- "repository": "git://github.com/getsentry/sentry-javascript.git",
- "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/launchdarkly",
- "author": "Sentry",
- "license": "MIT",
- "engines": {
- "node": ">=14.18"
- },
- "files": [
- "/build/npm"
- ],
- "main": "build/npm/cjs/index.js",
- "module": "build/npm/esm/index.js",
- "types": "build/npm/types/index.d.ts",
- "exports": {
- "./package.json": "./package.json",
- ".": {
- "import": {
- "types": "./build/npm/types/index.d.ts",
- "default": "./build/npm/esm/index.js"
- },
- "require": {
- "types": "./build/npm/types/index.d.ts",
- "default": "./build/npm/cjs/index.js"
- }
- }
- },
- "typesVersions": {
- "<4.9": {
- "build/npm/types/index.d.ts": [
- "build/npm/types-ts3.8/index.d.ts"
- ]
- }
- },
- "publishConfig": {
- "access": "public"
- },
- "dependencies": {
- "@sentry/browser": "^8.35.0",
- "@sentry/core": "8.35.0",
- "@sentry/types": "8.35.0",
- "@sentry/utils": "8.35.0",
- "launchdarkly-js-client-sdk": "^3.5.0"
- },
- "scripts": {
- "build": "run-p build:transpile build:types build:bundle",
- "build:transpile": "rollup -c rollup.npm.config.mjs",
- "build:bundle": "rollup -c rollup.bundle.config.mjs",
- "build:dev": "run-p build:transpile build:types",
- "build:types": "run-s build:types:core build:types:downlevel",
- "build:types:core": "tsc -p tsconfig.types.json",
- "build:types:downlevel": "yarn downlevel-dts build/npm/types build/npm/types-ts3.8 --to ts3.8 && yarn node ./scripts/shim-preact-export.js",
- "build:watch": "run-p build:transpile:watch build:bundle:watch build:types:watch",
- "build:dev:watch": "run-p build:transpile:watch build:types:watch",
- "build:transpile:watch": "yarn build:transpile --watch",
- "build:bundle:watch": "yarn build:bundle --watch",
- "build:types:watch": "tsc -p tsconfig.types.json --watch",
- "build:tarball": "npm pack",
- "circularDepCheck": "madge --circular src/index.ts",
- "clean": "rimraf build sentry-internal-launchdarkly-*.tgz",
- "fix": "eslint . --format stylish --fix",
- "lint": "eslint . --format stylish",
- "test": "jest",
- "test:watch": "jest --watch",
- "yalc:publish": "yalc publish --push --sig"
- },
- "volta": {
- "extends": "../../package.json"
- },
- "sideEffects": false
-}
diff --git a/packages/launchdarkly/rollup.bundle.config.mjs b/packages/launchdarkly/rollup.bundle.config.mjs
deleted file mode 100644
index f79e2cd63d7e..000000000000
--- a/packages/launchdarkly/rollup.bundle.config.mjs
+++ /dev/null
@@ -1,12 +0,0 @@
-import { makeBaseBundleConfig, makeBundleConfigVariants } from '@sentry-internal/rollup-utils';
-
-const baseBundleConfig = makeBaseBundleConfig({
- bundleType: 'addon',
- entrypoints: ['src/index.ts'],
- licenseTitle: '@sentry/launchdarkly',
- outputFileBase: () => 'bundles/launchdarkly',
-});
-
-const builds = makeBundleConfigVariants(baseBundleConfig);
-
-export default builds;
diff --git a/packages/launchdarkly/rollup.npm.config.mjs b/packages/launchdarkly/rollup.npm.config.mjs
deleted file mode 100644
index 3b4431fa6829..000000000000
--- a/packages/launchdarkly/rollup.npm.config.mjs
+++ /dev/null
@@ -1,19 +0,0 @@
-import { makeBaseNPMConfig, makeNPMConfigVariants } from '@sentry-internal/rollup-utils';
-
-export default makeNPMConfigVariants(
- makeBaseNPMConfig({
- hasBundles: true,
- packageSpecificConfig: {
- output: {
- // set exports to 'named' or 'auto' so that rollup doesn't warn
- exports: 'named',
- // set preserveModules to false because for Replay we actually want
- // to bundle everything into one file.
- preserveModules:
- process.env.SENTRY_BUILD_PRESERVE_MODULES === undefined
- ? false
- : Boolean(process.env.SENTRY_BUILD_PRESERVE_MODULES),
- },
- },
- }),
-);
diff --git a/packages/launchdarkly/scripts/shim-preact-export.js b/packages/launchdarkly/scripts/shim-preact-export.js
deleted file mode 100644
index bd74e4da0a05..000000000000
--- a/packages/launchdarkly/scripts/shim-preact-export.js
+++ /dev/null
@@ -1,75 +0,0 @@
-// preact does not support more modern TypeScript versions, which breaks our users that depend on older
-// TypeScript versions. To fix this, we shim the types from preact to be any and remove the dependency on preact
-// for types directly. This script is meant to be run after the build/npm/types-ts3.8 directory is created.
-
-// Path: build/npm/types-ts3.8/global.d.ts
-
-const fs = require('fs');
-const path = require('path');
-
-/**
- * This regex looks for preact imports we can replace and shim out.
- *
- * Example:
- * import { ComponentChildren, VNode } from 'preact';
- */
-const preactImportRegex = /import\s*{\s*([\w\s,]+)\s*}\s*from\s*'preact'\s*;?/;
-
-function walk(dir) {
- const files = fs.readdirSync(dir);
- files.forEach(file => {
- const filePath = path.join(dir, file);
- const stat = fs.lstatSync(filePath);
- if (stat.isDirectory()) {
- walk(filePath);
- } else {
- if (filePath.endsWith('.d.ts')) {
- const content = fs.readFileSync(filePath, 'utf8');
- const capture = preactImportRegex.exec(content);
- if (capture) {
- const groups = capture[1].split(',').map(s => s.trim());
-
- // This generates a shim snippet to replace the type imports from preact
- // It generates a snippet based on the capture groups of preactImportRegex.
- //
- // Example:
- //
- // import type { ComponentChildren, VNode } from 'preact';
- // becomes
- // type ComponentChildren: any;
- // type VNode: any;
- const snippet = groups.reduce((acc, curr) => {
- const searchableValue = curr.includes(' as ') ? curr.split(' as ')[1] : curr;
-
- // look to see if imported as value, then we have to use declare const
- if (content.includes(`typeof ${searchableValue}`)) {
- return `${acc}declare const ${searchableValue}: any;\n`;
- }
-
- // look to see if generic type like Foo
- if (content.includes(`${searchableValue}<`)) {
- return `${acc}type ${searchableValue} = any;\n`;
- }
-
- // otherwise we can just leave as type
- return `${acc}type ${searchableValue} = any;\n`;
- }, '');
-
- // we then can remove the import from preact
- const newContent = content.replace(preactImportRegex, '// replaced import from preact');
-
- // and write the new content to the file
- fs.writeFileSync(filePath, snippet + newContent, 'utf8');
- }
- }
- }
- });
-}
-
-function run() {
- // recurse through build/npm/types-ts3.8 directory
- const dir = path.join('build', 'npm', 'types-ts3.8');
- walk(dir);
-}
-
-run();
diff --git a/packages/launchdarkly/src/core/integration.ts b/packages/launchdarkly/src/core/integration.ts
deleted file mode 100644
index e9702251b048..000000000000
--- a/packages/launchdarkly/src/core/integration.ts
+++ /dev/null
@@ -1,69 +0,0 @@
-/* eslint-disable @sentry-internal/sdk/no-class-field-initializers */
-
-import * as Sentry from '@sentry/browser';
-import type { Client as SentryClient, Event, EventHint, IntegrationFn } from '@sentry/types';
-import type { LDContext, LDEvaluationDetail, LDInspectionFlagUsedHandler } from 'launchdarkly-js-client-sdk';
-import type { LaunchDarklyOptions } from '../types';
-import { insertToFlagBuffer } from '@sentry/utils';
-
-/**
- * Sentry integration for capturing feature flags from LaunchDarkly.
- *
- * See the [feature flag documentation](TODO:) for more information.
- *
- * @example
- * ```
- * import {SentryInspector, launchDarklyIntegration} from '@sentry/launchdarkly';
- * import {LDClient} from 'launchdarkly-js-client-sdk';
- *
- * Sentry.init(..., integrations: [launchDarklyIntegration()])
- * const ldClient = LDClient.initialize(..., inspectors: [SentryInspector]);
- * ```
- */
-export const launchDarklyIntegration = ((_options?: LaunchDarklyOptions) => {
- return {
- name: 'launchdarkly',
-
- processEvent(event: Event, _hint: EventHint, _client: SentryClient): Event {
- const scope = Sentry.getCurrentScope();
- const flagContext = scope.getScopeData().contexts.flags;
-
- if (event.contexts === undefined) {
- event.contexts = {};
- }
- event.contexts.flags = flagContext;
- return event;
- },
- };
-}) satisfies IntegrationFn;
-
-/**
- * LaunchDarkly hook that listens for flag evaluations and updates the
- * flagBuffer in our current scope.
- *
- * This needs to be registered separately in the LDClient, after initializing
- * Sentry.
- */
-export class SentryInspector implements LDInspectionFlagUsedHandler {
- public name = 'sentry-flag-auditor';
-
- public type = 'flag-used' as const;
-
- // We don't want the handler to impact the performance of the user's flag evaluations.
- public synchronous = false;
-
- /**
- * Handle a flag evaluation by storing its name and value on the current scope.
- */
- public method(flagKey: string, flagDetail: LDEvaluationDetail, _context: LDContext): void {
- if (typeof flagDetail.value === 'boolean') {
- const scopeContexts = Sentry.getCurrentScope().getScopeData().contexts;
- if (!scopeContexts.flags) {
- scopeContexts.flags = {values: []}
- }
- const flagBuffer = scopeContexts.flags.values;
- insertToFlagBuffer(flagBuffer, flagKey, flagDetail.value);
- }
- return;
- }
-}
diff --git a/packages/launchdarkly/src/index.ts b/packages/launchdarkly/src/index.ts
deleted file mode 100644
index 8952a4bc1264..000000000000
--- a/packages/launchdarkly/src/index.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-// This file is used as entry point to generate the npm package and CDN bundles.
-
-export { launchDarklyIntegration } from './core/integration';
-
-// export type {
-// } from './types';
diff --git a/packages/launchdarkly/src/types.ts b/packages/launchdarkly/src/types.ts
deleted file mode 100644
index 6516bc7a8ff7..000000000000
--- a/packages/launchdarkly/src/types.ts
+++ /dev/null
@@ -1 +0,0 @@
-export type LaunchDarklyOptions = Record; //TODO:
diff --git a/packages/launchdarkly/test.setup.ts b/packages/launchdarkly/test.setup.ts
deleted file mode 100644
index 05a762e60d50..000000000000
--- a/packages/launchdarkly/test.setup.ts
+++ /dev/null
@@ -1,255 +0,0 @@
-import { printDiffOrStringify } from 'jest-matcher-utils';
-import { vi } from 'vitest';
-import type { Mocked, MockedFunction } from 'vitest';
-
-/* eslint-disable @typescript-eslint/no-unsafe-member-access */
-import { getClient } from '@sentry/core';
-import type { ReplayRecordingData, Transport } from '@sentry/types';
-import * as SentryUtils from '@sentry/utils';
-
-import type { ReplayContainer, Session } from './src/types';
-
-type MockTransport = MockedFunction;
-
-vi.spyOn(SentryUtils, 'isBrowser').mockImplementation(() => true);
-
-type EnvelopeHeader = {
- event_id: string;
- sent_at: string;
- sdk: {
- name: string;
- version?: string;
- };
-};
-
-type ReplayEventHeader = { type: 'replay_event' };
-type ReplayEventPayload = Record;
-type RecordingHeader = { type: 'replay_recording'; length: number };
-type RecordingPayloadHeader = Record;
-type SentReplayExpected = {
- envelopeHeader?: EnvelopeHeader;
- replayEventHeader?: ReplayEventHeader;
- replayEventPayload?: ReplayEventPayload;
- recordingHeader?: RecordingHeader;
- recordingPayloadHeader?: RecordingPayloadHeader;
- recordingData?: ReplayRecordingData;
-};
-
-// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
-const toHaveSameSession = function (received: Mocked, expected: undefined | Session) {
- const pass = this.equals(received.session?.id, expected?.id) as boolean;
-
- const options = {
- isNot: this.isNot,
- promise: this.promise,
- };
-
- return {
- pass,
- message: () =>
- `${this.utils.matcherHint('toHaveSameSession', undefined, undefined, options)}\n\n${printDiffOrStringify(
- expected,
- received.session,
- 'Expected',
- 'Received',
- )}`,
- };
-};
-
-type Result = {
- passed: boolean;
- key: string;
- expectedVal: SentReplayExpected[keyof SentReplayExpected];
- actualVal: SentReplayExpected[keyof SentReplayExpected];
-};
-type Call = [
- EnvelopeHeader,
- [
- [ReplayEventHeader | undefined, ReplayEventPayload | undefined],
- [RecordingHeader | undefined, RecordingPayloadHeader | undefined],
- ],
-];
-type CheckCallForSentReplayResult = { pass: boolean; call: Call | undefined; results: Result[] };
-
-function checkCallForSentReplay(
- call: Call | undefined,
- expected?: SentReplayExpected | { sample: SentReplayExpected; inverse: boolean },
-): CheckCallForSentReplayResult {
- const envelopeHeader = call?.[0];
- const envelopeItems = call?.[1] || [[], []];
- const [[replayEventHeader, replayEventPayload], [recordingHeader, recordingPayload] = []] = envelopeItems;
-
- // @ts-expect-error recordingPayload is always a string in our tests
- const [recordingPayloadHeader, recordingData] = recordingPayload?.split('\n') || [];
-
- const actualObj: Required = {
- // @ts-expect-error Custom envelope
- envelopeHeader: envelopeHeader,
- // @ts-expect-error Custom envelope
- replayEventHeader: replayEventHeader,
- // @ts-expect-error Custom envelope
- replayEventPayload: replayEventPayload,
- // @ts-expect-error Custom envelope
- recordingHeader: recordingHeader,
- recordingPayloadHeader: recordingPayloadHeader && JSON.parse(recordingPayloadHeader),
- recordingData,
- };
-
- const isObjectContaining = expected && 'sample' in expected && 'inverse' in expected;
- const expectedObj = isObjectContaining
- ? (expected as { sample: SentReplayExpected }).sample
- : (expected as SentReplayExpected);
-
- if (isObjectContaining) {
- // eslint-disable-next-line no-console
- console.warn('`expect.objectContaining` is unnecessary when using the `toHaveSentReplay` matcher');
- }
-
- const results = expected
- ? Object.keys(expectedObj)
- .map(key => {
- const actualVal = actualObj[key as keyof SentReplayExpected];
- const expectedVal = expectedObj[key as keyof SentReplayExpected];
- const passed = !expectedVal || this.equals(actualVal, expectedVal);
-
- return { passed, key, expectedVal, actualVal };
- })
- .filter(({ passed }) => !passed)
- : [];
-
- const pass = Boolean(call && (!expected || results.length === 0));
-
- return {
- pass,
- call,
- results,
- };
-}
-
-/**
- * Only want calls that send replay events, i.e. ignore error events
- */
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-function getReplayCalls(calls: any[][][]): any[][][] {
- return calls
- .map(call => {
- const arg = call[0];
- if (arg.length !== 2) {
- return [];
- }
-
- if (!arg[1][0].find(({ type }: { type: string }) => ['replay_event', 'replay_recording'].includes(type))) {
- return [];
- }
-
- return [arg];
- })
- .filter(Boolean);
-}
-
-/**
- * Checks all calls to `fetch` and ensures a replay was uploaded by
- * checking the `fetch()` request's body.
- */
-// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
-const toHaveSentReplay = function (
- _received: Mocked,
- expected?: SentReplayExpected | { sample: SentReplayExpected; inverse: boolean },
-) {
- const { calls } = (getClient()?.getTransport()?.send as MockTransport).mock;
-
- let result: CheckCallForSentReplayResult;
-
- const expectedKeysLength = expected
- ? ('sample' in expected ? Object.keys(expected.sample) : Object.keys(expected)).length
- : 0;
-
- const replayCalls = getReplayCalls(calls);
-
- for (const currentCall of replayCalls) {
- result = checkCallForSentReplay.call(this, currentCall[0], expected);
- if (result.pass) {
- break;
- }
-
- // stop on the first call where any of the expected obj passes
- if (result.results.length < expectedKeysLength) {
- break;
- }
- }
-
- // @ts-expect-error use before assigned
- const { results, call, pass } = result;
-
- const options = {
- isNot: this.isNot,
- promise: this.promise,
- };
-
- return {
- pass,
- message: () =>
- !call
- ? pass
- ? 'Expected Replay to not have been sent, but a request was attempted'
- : 'Expected Replay to have been sent, but a request was not attempted'
- : `${this.utils.matcherHint('toHaveSentReplay', undefined, undefined, options)}\n\n${results
- .map(({ key, expectedVal, actualVal }: Result) =>
- printDiffOrStringify(expectedVal, actualVal, `Expected (key: ${key})`, `Received (key: ${key})`),
- )
- .join('\n')}`,
- };
-};
-
-/**
- * Checks the last call to `fetch` and ensures a replay was uploaded by
- * checking the `fetch()` request's body.
- */
-// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
-const toHaveLastSentReplay = function (
- _received: Mocked,
- expected?: SentReplayExpected | { sample: SentReplayExpected; inverse: boolean },
-) {
- const { calls } = (getClient()?.getTransport()?.send as MockTransport).mock;
- const replayCalls = getReplayCalls(calls);
-
- const lastCall = replayCalls[calls.length - 1]?.[0];
-
- const { results, call, pass } = checkCallForSentReplay.call(this, lastCall, expected);
-
- const options = {
- isNot: this.isNot,
- promise: this.promise,
- };
-
- return {
- pass,
- message: () =>
- !call
- ? pass
- ? 'Expected Replay to not have been sent, but a request was attempted'
- : 'Expected Replay to have last been sent, but a request was not attempted'
- : `${this.utils.matcherHint('toHaveSentReplay', undefined, undefined, options)}\n\n${results
- .map(({ key, expectedVal, actualVal }: Result) =>
- printDiffOrStringify(expectedVal, actualVal, `Expected (key: ${key})`, `Received (key: ${key})`),
- )
- .join('\n')}`,
- };
-};
-
-expect.extend({
- toHaveSameSession,
- toHaveSentReplay,
- toHaveLastSentReplay,
-});
-
-interface CustomMatchers {
- toHaveSentReplay(expected?: SentReplayExpected): R;
- toHaveLastSentReplay(expected?: SentReplayExpected): R;
- toHaveSameSession(expected: undefined | Session): R;
-}
-
-declare module 'vitest' {
- type Assertion = CustomMatchers;
- type AsymmetricMatchersContaining = CustomMatchers;
-}
diff --git a/packages/launchdarkly/tsconfig.json b/packages/launchdarkly/tsconfig.json
deleted file mode 100644
index cd1b8207ea06..000000000000
--- a/packages/launchdarkly/tsconfig.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "extends": "../../tsconfig.json",
- "compilerOptions": {
- "lib": ["DOM", "ES2018"],
- "module": "esnext"
- },
- "include": ["src/**/*.ts"]
-}
diff --git a/packages/launchdarkly/tsconfig.test.json b/packages/launchdarkly/tsconfig.test.json
deleted file mode 100644
index bb7130d948c0..000000000000
--- a/packages/launchdarkly/tsconfig.test.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "extends": "./tsconfig.json",
-
- "include": ["test/**/*.ts", "vitest.config.ts", "test.setup.ts"],
-
- "compilerOptions": {
- "types": ["node"],
- "esModuleInterop": true,
- "allowJs": true,
- "noImplicitAny": true,
- "noImplicitThis": false,
- "strictNullChecks": true,
- "strictPropertyInitialization": false
- }
-}
diff --git a/packages/launchdarkly/tsconfig.types.json b/packages/launchdarkly/tsconfig.types.json
deleted file mode 100644
index 374fd9bc9364..000000000000
--- a/packages/launchdarkly/tsconfig.types.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "extends": "./tsconfig.json",
-
- "compilerOptions": {
- "declaration": true,
- "declarationMap": true,
- "emitDeclarationOnly": true,
- "outDir": "build/npm/types"
- }
-}
diff --git a/packages/launchdarkly/vitest.config.ts b/packages/launchdarkly/vitest.config.ts
deleted file mode 100644
index 976d9c37074d..000000000000
--- a/packages/launchdarkly/vitest.config.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { defineConfig } from 'vitest/config';
-
-import baseConfig from '../../vite/vite.config';
-
-export default defineConfig({
- ...baseConfig,
- test: {
- ...baseConfig.test,
- setupFiles: ['./test.setup.ts'],
- reporters: ['default'],
- },
-});
From d9c5860c2add9237ed4b0583e52deac43d1e4e81 Mon Sep 17 00:00:00 2001
From: Andrew Liu <159852527+aliu39@users.noreply.github.com>
Date: Tue, 5 Nov 2024 17:16:19 -0800
Subject: [PATCH 19/19] Delete LD from package.json and update lock
---
package.json | 1 -
yarn.lock | 42 ------------------------------------------
2 files changed, 43 deletions(-)
diff --git a/package.json b/package.json
index b776421b71b1..ce3f91932c64 100644
--- a/package.json
+++ b/package.json
@@ -63,7 +63,6 @@
"packages/gatsby",
"packages/google-cloud-serverless",
"packages/integration-shims",
- "packages/launchdarkly",
"packages/nestjs",
"packages/nextjs",
"packages/node",
diff --git a/yarn.lock b/yarn.lock
index 4e2971138219..652c5721f5b6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -8390,14 +8390,6 @@
"@sentry/cli-win32-i686" "2.37.0"
"@sentry/cli-win32-x64" "2.37.0"
-"@sentry/core@8.35.0":
- version "8.35.0"
- resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.35.0.tgz#17090f4d2d3bb983d9d99ecd2d27f4e9e107e0b0"
- integrity sha512-Ci0Nmtw5ETWLqQJGY4dyF+iWh7PWKy6k303fCEoEmqj2czDrKJCp7yHBNV0XYbo00prj2ZTbCr6I7albYiyONA==
- dependencies:
- "@sentry/types" "8.35.0"
- "@sentry/utils" "8.35.0"
-
"@sentry/rollup-plugin@2.22.6":
version "2.22.6"
resolved "https://registry.yarnpkg.com/@sentry/rollup-plugin/-/rollup-plugin-2.22.6.tgz#74e9ab69729ee024a497b21b66be3b1992e786d5"
@@ -8406,18 +8398,6 @@
"@sentry/bundler-plugin-core" "2.22.6"
unplugin "1.0.1"
-"@sentry/types@8.35.0":
- version "8.35.0"
- resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.35.0.tgz#535c807800f7e378f61416f30177c0ef81b95012"
- integrity sha512-AVEZjb16MlYPifiDDvJ19dPQyDn0jlrtC1PHs6ZKO+Rzyz+2EX2BRdszvanqArldexPoU1p5Bn2w81XZNXThBA==
-
-"@sentry/utils@8.35.0":
- version "8.35.0"
- resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-8.35.0.tgz#1e099fcbc60040091c79f028a83226c145d588ee"
- integrity sha512-MdMb6+uXjqND7qIPWhulubpSeHzia6HtxeJa8jYI09OCvIcmNGPydv/Gx/LZBwosfMHrLdTWcFH7Y7aCxrq7cg==
- dependencies:
- "@sentry/types" "8.35.0"
-
"@sentry/vite-plugin@2.22.6", "@sentry/vite-plugin@^2.22.6":
version "2.22.6"
resolved "https://registry.yarnpkg.com/@sentry/vite-plugin/-/vite-plugin-2.22.6.tgz#d08a1ede05f137636d5b3c61845d24c0114f0d76"
@@ -18049,11 +18029,6 @@ fake-indexeddb@^4.0.1:
dependencies:
realistic-structured-clone "^3.0.0"
-fast-deep-equal@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
- integrity sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==
-
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
@@ -22297,23 +22272,6 @@ launch-editor@^2.9.1:
picocolors "^1.0.0"
shell-quote "^1.8.1"
-launchdarkly-js-client-sdk@^3.5.0:
- version "3.5.0"
- resolved "https://registry.yarnpkg.com/launchdarkly-js-client-sdk/-/launchdarkly-js-client-sdk-3.5.0.tgz#cb0e3d6fc21e56750aa86fcaf75733d7f510946f"
- integrity sha512-3dgxC9S8K2ix6qjdArjZGOJPtAytgfQTuE+vWgjWJK7725rpYbuqbHghIFr5B0+WyWyVBYANldjWd1JdtYLwsw==
- dependencies:
- escape-string-regexp "^4.0.0"
- launchdarkly-js-sdk-common "5.4.0"
-
-launchdarkly-js-sdk-common@5.4.0:
- version "5.4.0"
- resolved "https://registry.yarnpkg.com/launchdarkly-js-sdk-common/-/launchdarkly-js-sdk-common-5.4.0.tgz#c9787daebe0b583b01d2334218524ea142c85001"
- integrity sha512-Kb3SDcB6S0HUpFNBZgtEt0YUV/fVkyg+gODfaOCJQ0Y0ApxLKNmmJBZOrPE2qIdzw536u4BqEjtaJdqJWCEElg==
- dependencies:
- base64-js "^1.3.0"
- fast-deep-equal "^2.0.1"
- uuid "^8.0.0"
-
lazystream@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.1.tgz#494c831062f1f9408251ec44db1cba29242a2638"