The toolbar should show up now! Click it to open.
@@ -76,14 +102,55 @@ export const Authenticated = (): JSX.Element => {
)
}
-export const UnAuthenticated = (): JSX.Element => {
- useToolbarStyles()
+export const Default = (): JSX.Element => {
+ return
+}
- return (
-
-
The toolbar should show up now!
-
-
-
- )
+export const Unauthenticated = (): JSX.Element => {
+ return
+}
+
+export const Minimized = (): JSX.Element => {
+ return
+}
+
+export const Heatmap = (): JSX.Element => {
+ return
+}
+
+export const Inspect = (): JSX.Element => {
+ return
+}
+
+export const Actions = (): JSX.Element => {
+ return
+}
+
+export const FeatureFlags = (): JSX.Element => {
+ return
+}
+
+// Dark theme
+export const DefaultDark = (): JSX.Element => {
+ return
+}
+
+export const MinimizedDark = (): JSX.Element => {
+ return
+}
+
+export const HeatmapDark = (): JSX.Element => {
+ return
+}
+
+export const InspectDark = (): JSX.Element => {
+ return
+}
+
+export const ActionsDark = (): JSX.Element => {
+ return
+}
+
+export const FeatureFlagsDark = (): JSX.Element => {
+ return
}
diff --git a/frontend/src/toolbar/ToolbarApp.tsx b/frontend/src/toolbar/ToolbarApp.tsx
index 0b8e6c1790d79..65aac3ed51608 100644
--- a/frontend/src/toolbar/ToolbarApp.tsx
+++ b/frontend/src/toolbar/ToolbarApp.tsx
@@ -4,14 +4,14 @@ import { useRef, useState } from 'react'
import root from 'react-shadow'
import { Slide, ToastContainer } from 'react-toastify'
+import { toolbarConfigLogic } from '~/toolbar/toolbarConfigLogic'
import { ToolbarContainer } from '~/toolbar/ToolbarContainer'
-import { toolbarLogic } from '~/toolbar/toolbarLogic'
import { ToolbarProps } from '~/types'
type HTMLElementWithShadowRoot = HTMLElement & { shadowRoot: ShadowRoot }
export function ToolbarApp(props: ToolbarProps = {}): JSX.Element {
- const { jsURL } = useValues(toolbarLogic(props))
+ const { jsURL } = useValues(toolbarConfigLogic(props))
const shadowRef = useRef
(null)
const [didLoadStyles, setDidLoadStyles] = useState(false)
@@ -24,7 +24,13 @@ export function ToolbarApp(props: ToolbarProps = {}): JSX.Element {
const styleLink = document.createElement('link')
styleLink.rel = 'stylesheet'
styleLink.type = 'text/css'
- styleLink.href = `${jsURL}/static/toolbar.css`
+ // toolbar.js is served from the PostHog CDN, this has a TTL of 24 hours.
+ // the toolbar asset includes a rotating "token" that is valid for 5 minutes.
+ const fiveMinutesInMillis = 5 * 60 * 1000
+ // this ensures that we bust the cache periodically
+ const timestampToNearestFiveMinutes =
+ Math.floor(Date.now() / fiveMinutesInMillis) * fiveMinutesInMillis
+ styleLink.href = `${jsURL}/static/toolbar.css?t=${timestampToNearestFiveMinutes}`
styleLink.onload = () => setDidLoadStyles(true)
const shadowRoot =
shadowRef.current?.shadowRoot || window.document.getElementById('__POSTHOG_TOOLBAR__')?.shadowRoot
diff --git a/frontend/src/toolbar/ToolbarContainer.tsx b/frontend/src/toolbar/ToolbarContainer.tsx
index e6aebff4342e9..2eaf98f9c7f9c 100644
--- a/frontend/src/toolbar/ToolbarContainer.tsx
+++ b/frontend/src/toolbar/ToolbarContainer.tsx
@@ -1,17 +1,29 @@
import { useValues } from 'kea'
import { Fade } from 'lib/components/Fade/Fade'
-import { DraggableButton } from '~/toolbar/button/DraggableButton'
-import { Elements } from '~/toolbar/elements/Elements'
-import { toolbarLogic } from '~/toolbar/toolbarLogic'
+import { Toolbar } from './bar/Toolbar'
+import { toolbarLogic } from './bar/toolbarLogic'
+import { Elements } from './elements/Elements'
+import { HedgehogButton } from './hedgehog/HedgehogButton'
+import { toolbarConfigLogic } from './toolbarConfigLogic'
export function ToolbarContainer(): JSX.Element {
- const { buttonVisible } = useValues(toolbarLogic)
+ const { buttonVisible } = useValues(toolbarConfigLogic)
+ const { theme } = useValues(toolbarLogic)
+
+ // KLUDGE: if we put theme directly on the div then
+ // linting and typescript complain about it not being
+ // a valid attribute. So we put it in a variable and
+ // spread it in. 🤷
+ const themeProps = { theme }
return (
-
+
-
+
+
+
+
)
}
diff --git a/frontend/src/toolbar/__mocks__/list-actions-response.ts b/frontend/src/toolbar/__mocks__/list-actions-response.ts
new file mode 100644
index 0000000000000..a2b84a1b5415f
--- /dev/null
+++ b/frontend/src/toolbar/__mocks__/list-actions-response.ts
@@ -0,0 +1,149 @@
+export const listActionsAPIResponse = {
+ results: [
+ {
+ id: 49103,
+ name: 'Clicked notification row',
+ description: '',
+ post_to_slack: false,
+ slack_message_format: '',
+ steps: [
+ {
+ id: '60889',
+ event: '$autocapture',
+ tag_name: null,
+ text: null,
+ text_matching: null,
+ href: null,
+ href_matching: null,
+ selector: '.notifications-menu .activity-log-row',
+ url: null,
+ name: null,
+ url_matching: 'contains',
+ properties: [],
+ },
+ ],
+ created_at: '2023-08-22T06:31:40.495488Z',
+ created_by: {
+ id: 5042,
+ uuid: '017bcbcf-2391-0000-1868-14fb987285c5',
+ distinct_id: 'PqMBBFBfKo638PwvjCWTSZ6dnswYzJ3RtH8VIHCBWAy',
+ first_name: 'Paul',
+ email: 'paul@posthog.com',
+ is_email_verified: true,
+ },
+ deleted: false,
+ is_calculating: false,
+ last_calculated_at: '2023-08-22T06:31:40.495197Z',
+ team_id: 2,
+ is_action: true,
+ bytecode_error: null,
+ tags: [],
+ },
+ {
+ id: 49059,
+ name: 'clicked nav bar2',
+ description: '',
+ post_to_slack: false,
+ slack_message_format: '',
+ steps: [
+ {
+ id: '60829',
+ event: '$autocapture',
+ tag_name: null,
+ text: 'Products',
+ text_matching: null,
+ href: '/product-analytics',
+ href_matching: null,
+ selector: null,
+ url: null,
+ name: '',
+ url_matching: 'exact',
+ properties: [],
+ },
+ {
+ id: '60830',
+ event: '$autocapture',
+ tag_name: null,
+ text: null,
+ text_matching: null,
+ href: null,
+ href_matching: null,
+ selector: '.border-b .h-full:nth-child(2) > .text-\\[13\\.5px\\]',
+ url: null,
+ name: '',
+ url_matching: 'exact',
+ properties: [],
+ },
+ ],
+ created_at: '2023-08-21T20:54:17.218311Z',
+ created_by: {
+ id: 6352,
+ uuid: '017d5334-911a-0000-6954-2d394263f3ca',
+ distinct_id: '48vHN2rW28SvzA6D4NdgHajLOjBP1ulsI0Rr5DNas4i',
+ first_name: 'Cameron',
+ email: 'cameron@posthog.com',
+ is_email_verified: true,
+ },
+ deleted: false,
+ is_calculating: false,
+ last_calculated_at: '2023-08-21T20:54:17.218023Z',
+ team_id: 2,
+ is_action: true,
+ bytecode_error: null,
+ tags: [],
+ },
+ {
+ id: 49050,
+ name: 'Clicked Products in nav bar',
+ description: '',
+ post_to_slack: false,
+ slack_message_format: '',
+ steps: [
+ {
+ id: '60819',
+ event: '$autocapture',
+ tag_name: null,
+ text: 'Products',
+ text_matching: null,
+ href: '/product-analytics',
+ href_matching: null,
+ selector: null,
+ url: null,
+ name: '',
+ url_matching: 'exact',
+ properties: [],
+ },
+ {
+ id: '60820',
+ event: '$autocapture',
+ tag_name: null,
+ text: 'Pricing',
+ text_matching: null,
+ href: '/pricing',
+ href_matching: null,
+ selector: null,
+ url: null,
+ name: null,
+ url_matching: 'contains',
+ properties: [],
+ },
+ ],
+ created_at: '2023-08-21T18:46:13.210958Z',
+ created_by: {
+ id: 6352,
+ uuid: '017d5334-911a-0000-6954-2d394263f3ca',
+ distinct_id: '48vHN2rW28SvzA6D4NdgHajLOjBP1ulsI0Rr5DNas4i',
+ first_name: 'Cameron',
+ email: 'cameron@posthog.com',
+ is_email_verified: true,
+ },
+ deleted: false,
+ is_calculating: false,
+ last_calculated_at: '2023-08-21T18:46:13.210590Z',
+ team_id: 2,
+ is_action: true,
+ bytecode_error: null,
+ tags: [],
+ },
+ ],
+}
diff --git a/frontend/src/toolbar/__mocks__/list-heatmap-stats-response.ts b/frontend/src/toolbar/__mocks__/list-heatmap-stats-response.ts
new file mode 100644
index 0000000000000..ae1e6bade5314
--- /dev/null
+++ b/frontend/src/toolbar/__mocks__/list-heatmap-stats-response.ts
@@ -0,0 +1,42 @@
+export const listHeatmapStatsAPIResponse = {
+ results: [
+ {
+ count: 780,
+ hash: null,
+ type: '$autocapture',
+
+ elements: [
+ {
+ tag_name: 'button',
+ $el_text: 'Click Me',
+ nth_child: 2,
+ nth_of_type: 1,
+ },
+ {
+ tag_name: 'div',
+ nth_child: 1,
+ nth_of_type: 1,
+ },
+ {
+ tag_name: 'div',
+ attr__id: 'storybook-root',
+ nth_child: 5,
+ nth_of_type: 5,
+ },
+ {
+ tag_name: 'body',
+ classes: ['sb-main-fullscreen', 'sb-show-main'],
+
+ attributes: {
+ attr__class: 'sb-main-fullscreen sb-show-main',
+ attr__theme: 'light',
+ },
+ nth_child: 2,
+ nth_of_type: 1,
+ },
+ ],
+ },
+ ],
+ next: null,
+ previous: null,
+}
diff --git a/frontend/src/toolbar/__mocks__/list-my-flags-response.ts b/frontend/src/toolbar/__mocks__/list-my-flags-response.ts
new file mode 100644
index 0000000000000..f0164c65b361e
--- /dev/null
+++ b/frontend/src/toolbar/__mocks__/list-my-flags-response.ts
@@ -0,0 +1,74 @@
+export const listMyFlagsAPIResponse = [
+ {
+ feature_flag: {
+ id: 15927,
+ name: 'Example flag',
+ key: 'example-one',
+ deleted: false,
+ active: true,
+ created_at: '2023-08-17T19:37:05.259538Z',
+ filters: {},
+ },
+ value: false,
+ },
+ {
+ feature_flag: {
+ id: 15927,
+ name: 'Another example flag',
+ key: 'example-two',
+ deleted: false,
+ active: true,
+ created_at: '2023-08-17T19:37:05.259538Z',
+ filters: {},
+ },
+ value: true,
+ },
+ {
+ feature_flag: {
+ id: 13859,
+ name: 'Example multivariate flag',
+ key: 'my-multi-example',
+ filters: {
+ multivariate: {
+ variants: [
+ {
+ key: 'control',
+ rollout_percentage: 50,
+ },
+ {
+ key: 'test',
+ rollout_percentage: 50,
+ },
+ ],
+ },
+ },
+ deleted: false,
+ active: true,
+ },
+ value: 'control',
+ },
+ {
+ feature_flag: {
+ id: 13859,
+ name: 'Example overridden multivariate flag',
+ key: 'my-other-multi-example',
+ filters: {
+ multivariate: {
+ variants: [
+ {
+ key: 'control',
+ rollout_percentage: 50,
+ },
+ {
+ key: 'test',
+ rollout_percentage: 50,
+ },
+ ],
+ },
+ },
+ deleted: false,
+ active: true,
+ },
+ value: 'control',
+ },
+]
diff --git a/frontend/src/toolbar/actions/ActionsEditingToolbarMenu.tsx b/frontend/src/toolbar/actions/ActionsEditingToolbarMenu.tsx
new file mode 100644
index 0000000000000..4555bc390f0d8
--- /dev/null
+++ b/frontend/src/toolbar/actions/ActionsEditingToolbarMenu.tsx
@@ -0,0 +1,221 @@
+import { LemonDivider } from '@posthog/lemon-ui'
+import { useActions, useValues } from 'kea'
+import { Field, Form, Group } from 'kea-forms'
+import { IconClose, IconDelete, IconEdit, IconMagnifier, IconMinusOutlined, IconPlus } from 'lib/lemon-ui/icons'
+import { LemonButton } from 'lib/lemon-ui/LemonButton'
+import { LemonInput } from 'lib/lemon-ui/LemonInput'
+
+import { actionsTabLogic } from '~/toolbar/actions/actionsTabLogic'
+import { StepField } from '~/toolbar/actions/StepField'
+import { ToolbarMenu } from '~/toolbar/bar/ToolbarMenu'
+import { SelectorEditingModal } from '~/toolbar/elements/SelectorEditingModal'
+import { posthog } from '~/toolbar/posthog'
+import { getShadowRootPopoverContainer } from '~/toolbar/utils'
+
+export const ActionsEditingToolbarMenu = (): JSX.Element => {
+ const {
+ selectedActionId,
+ inspectingElement,
+ editingSelector,
+ elementsChainBeingEdited,
+ editingSelectorValue,
+ actionForm,
+ } = useValues(actionsTabLogic)
+ const {
+ setActionFormValue,
+ selectAction,
+ inspectForElementWithIndex,
+ deleteAction,
+ setElementSelector,
+ editSelectorWithIndex,
+ } = useActions(actionsTabLogic)
+
+ return (
+
+ editSelectorWithIndex(null)}
+ activeElementChain={elementsChainBeingEdited}
+ startingSelector={editingSelectorValue}
+ onChange={(selector) => {
+ if (selector && editingSelector !== null) {
+ posthog.capture('toolbar_manual_selector_applied', {
+ chosenSelector: selector,
+ })
+ setElementSelector(selector, editingSelector)
+ }
+ }}
+ />
+
+
+ )
+}
diff --git a/frontend/src/toolbar/actions/ActionsList.tsx b/frontend/src/toolbar/actions/ActionsList.tsx
deleted file mode 100644
index d8cdfb7c3ffb8..0000000000000
--- a/frontend/src/toolbar/actions/ActionsList.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import { useActions, useValues } from 'kea'
-import { IconPlus } from 'lib/lemon-ui/icons'
-import { LemonButton } from 'lib/lemon-ui/LemonButton'
-import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput'
-import { Spinner } from 'lib/lemon-ui/Spinner/Spinner'
-
-import { ActionsListView } from '~/toolbar/actions/ActionsListView'
-import { actionsLogic } from '~/toolbar/actions/actionsLogic'
-import { actionsTabLogic } from '~/toolbar/actions/actionsTabLogic'
-
-export function ActionsList(): JSX.Element {
- const { allActions, sortedActions, allActionsLoading, searchTerm } = useValues(actionsLogic)
- const { setSearchTerm } = useActions(actionsLogic)
- const { newAction } = useActions(actionsTabLogic)
-
- return (
- <>
- setSearchTerm(s)}
- />
-
-
- newAction()} icon={}>
- New action
-
-
- {allActions.length === 0 && allActionsLoading ? (
-
-
-
- ) : (
-
- )}
-
- >
- )
-}
diff --git a/frontend/src/toolbar/actions/ActionsListView.tsx b/frontend/src/toolbar/actions/ActionsListView.tsx
index 2293a594c5d31..82bcdfd710e75 100644
--- a/frontend/src/toolbar/actions/ActionsListView.tsx
+++ b/frontend/src/toolbar/actions/ActionsListView.tsx
@@ -1,3 +1,4 @@
+import { Link } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { Spinner } from 'lib/lemon-ui/Spinner'
@@ -13,26 +14,29 @@ export function ActionsListView({ actions }: ActionsListViewProps): JSX.Element
const { allActionsLoading, searchTerm } = useValues(actionsLogic)
const { selectAction } = useActions(actionsTabLogic)
return (
-
+
{allActionsLoading ? (
) : actions.length ? (
actions.map((action, index) => (
-
selectAction(action.id || null)}
- >
- {index + 1}.
-
- {action.name || Untitled}
-
-
+ <>
+
selectAction(action.id || null)}
+ className="font-medium my-1"
+ >
+
{index + 1}.
+
+ {action.name || Untitled}
+
+
+ >
))
) : (
-
No {searchTerm.length ? 'matching ' : ''}actions found.
+
No {searchTerm.length ? 'matching ' : ''}actions found.
)}
)
diff --git a/frontend/src/toolbar/actions/ActionsTab.scss b/frontend/src/toolbar/actions/ActionsTab.scss
index 821d9462682fe..1e356ffb7da09 100644
--- a/frontend/src/toolbar/actions/ActionsTab.scss
+++ b/frontend/src/toolbar/actions/ActionsTab.scss
@@ -1,25 +1,3 @@
-.action-block-body {
- form {
- .form-error {
- color: var(--danger);
- }
- }
-}
-
-.action-section {
- &.highlight {
- background: hsl(228deg 14% 96% / 100%);
-
- &:nth-child(even) {
- background: white;
-
- &:last-child {
- padding-bottom: 10px;
- }
- }
- }
-}
-
.action-field {
transition: opacity 0.5s;
@@ -34,16 +12,3 @@
color: rgb(0 0 0 / 50%);
}
}
-
-.ActionsListItem {
- border-bottom: 1px solid #e8e8e8;
- transition: background-color 0.5s;
-
- &:hover {
- background: #f0f0f0;
- }
-
- &:last-child {
- border-bottom: none;
- }
-}
diff --git a/frontend/src/toolbar/actions/ActionsTab.tsx b/frontend/src/toolbar/actions/ActionsTab.tsx
deleted file mode 100644
index 71d06c73c87de..0000000000000
--- a/frontend/src/toolbar/actions/ActionsTab.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import './ActionsTab.scss'
-
-import { Link } from '@posthog/lemon-ui'
-import { useValues } from 'kea'
-import { urls } from 'scenes/urls'
-
-import { ActionsList } from '~/toolbar/actions/ActionsList'
-import { actionsTabLogic } from '~/toolbar/actions/actionsTabLogic'
-import { EditAction } from '~/toolbar/actions/EditAction'
-import { toolbarLogic } from '~/toolbar/toolbarLogic'
-
-export function ActionsTab(): JSX.Element {
- const { selectedAction } = useValues(actionsTabLogic)
- const { apiURL } = useValues(toolbarLogic)
-
- return (
-
-
- {selectedAction ? (
-
- ) : (
- <>
-
-
-
- View & edit all actions
-
-
- >
- )}
-
-
- )
-}
diff --git a/frontend/src/toolbar/actions/ActionsToolbarMenu.tsx b/frontend/src/toolbar/actions/ActionsToolbarMenu.tsx
new file mode 100644
index 0000000000000..fe9cffa88319e
--- /dev/null
+++ b/frontend/src/toolbar/actions/ActionsToolbarMenu.tsx
@@ -0,0 +1,66 @@
+import { useActions, useValues } from 'kea'
+import { IconOpenInNew, IconPlus } from 'lib/lemon-ui/icons'
+import { LemonButton } from 'lib/lemon-ui/LemonButton'
+import { LemonInput } from 'lib/lemon-ui/LemonInput'
+import { Link } from 'lib/lemon-ui/Link'
+import { Spinner } from 'lib/lemon-ui/Spinner'
+import { urls } from 'scenes/urls'
+
+import { ActionsEditingToolbarMenu } from '~/toolbar/actions/ActionsEditingToolbarMenu'
+import { ActionsListView } from '~/toolbar/actions/ActionsListView'
+import { actionsLogic } from '~/toolbar/actions/actionsLogic'
+import { actionsTabLogic } from '~/toolbar/actions/actionsTabLogic'
+import { ToolbarMenu } from '~/toolbar/bar/ToolbarMenu'
+import { toolbarConfigLogic } from '~/toolbar/toolbarConfigLogic'
+
+const ActionsListToolbarMenu = (): JSX.Element => {
+ const { searchTerm } = useValues(actionsLogic)
+ const { setSearchTerm } = useActions(actionsLogic)
+
+ const { newAction } = useActions(actionsTabLogic)
+ const { allActions, sortedActions, allActionsLoading } = useValues(actionsLogic)
+
+ const { apiURL } = useValues(toolbarConfigLogic)
+
+ return (
+
+
+ setSearchTerm(s)}
+ className={'Toolbar__top_input'}
+ />
+
+
+
+ {allActions.length === 0 && allActionsLoading ? (
+
+
+
+ ) : (
+
+ )}
+
+
+
+
+
+ View & edit all actions
+
+ newAction()} icon={}>
+ New action
+
+
+
+
+ )
+}
+
+export const ActionsToolbarMenu = (): JSX.Element => {
+ const { selectedAction } = useValues(actionsTabLogic)
+ return selectedAction ?
:
+}
diff --git a/frontend/src/toolbar/actions/ButtonWindow.stories.tsx b/frontend/src/toolbar/actions/ButtonWindow.stories.tsx
deleted file mode 100644
index b31e1a3e9f13c..0000000000000
--- a/frontend/src/toolbar/actions/ButtonWindow.stories.tsx
+++ /dev/null
@@ -1,336 +0,0 @@
-import { Meta, StoryFn, StoryObj } from '@storybook/react'
-import { useMountedLogic } from 'kea'
-
-import { useStorybookMocks } from '~/mocks/browser'
-import { actionsLogic } from '~/toolbar/actions/actionsLogic'
-import { ActionsTab } from '~/toolbar/actions/ActionsTab'
-import { actionsTabLogic } from '~/toolbar/actions/actionsTabLogic'
-import { ButtonWindow } from '~/toolbar/button/ButtonWindow'
-import { FeatureFlags } from '~/toolbar/flags/FeatureFlags'
-import { featureFlagsLogic } from '~/toolbar/flags/featureFlagsLogic'
-import { useToolbarStyles } from '~/toolbar/Toolbar.stories'
-import { toolbarLogic } from '~/toolbar/toolbarLogic'
-interface StoryProps {
- contents: JSX.Element
- name: string
- label: string
- tagComponent?: JSX.Element
- listActionsAPIResponse?: { results: any[] }
-}
-type Story = StoryObj<(props: StoryProps) => JSX.Element>
-const meta: Meta<(props: StoryProps) => JSX.Element> = {
- title: 'Scenes-Other/Toolbar/Components',
- parameters: {
- layout: 'fullscreen',
- viewMode: 'story',
- },
-}
-export default meta
-
-const Template: StoryFn
= (props: StoryProps) => {
- useStorybookMocks({
- get: {
- '/decide': {
- config: {
- enable_collect_everything: true,
- },
- toolbarParams: {
- toolbarVersion: 'toolbar',
- jsURL: 'http://localhost:8234/',
- },
- isAuthenticated: true,
- supportedCompression: ['gzip', 'gzip-js', 'lz64'],
- featureFlags: {},
- sessionRecording: {
- endpoint: '/s/',
- },
- },
- '/api/element/stats/': {
- results: [{ count: 1592 }, { count: 12 }],
- },
- '/api/projects/@current/feature_flags/my_flags': [
- {
- feature_flag: {
- id: 15927,
- name: 'Example flag',
- key: 'example-one',
- deleted: false,
- active: true,
- created_at: '2023-08-17T19:37:05.259538Z',
- filters: {},
- },
- value: false,
- },
- {
- feature_flag: {
- id: 15927,
- name: 'Another example flag',
- key: 'example-two',
- deleted: false,
- active: true,
- created_at: '2023-08-17T19:37:05.259538Z',
- filters: {},
- },
- value: true,
- },
- {
- feature_flag: {
- id: 13859,
- name: 'Example multivariate flag',
- key: 'my-multi-example',
- filters: {
- multivariate: {
- variants: [
- {
- key: 'control',
- rollout_percentage: 50,
- },
- {
- key: 'test',
- rollout_percentage: 50,
- },
- ],
- },
- },
- deleted: false,
- active: true,
- },
- value: 'control',
- },
- {
- feature_flag: {
- id: 13859,
- name: 'Example overridden multivariate flag',
- key: 'my-other-multi-example',
- filters: {
- multivariate: {
- variants: [
- {
- key: 'control',
- rollout_percentage: 50,
- },
- {
- key: 'test',
- rollout_percentage: 50,
- },
- ],
- },
- },
- deleted: false,
- active: true,
- },
- value: 'control',
- },
- ],
- '/api/projects/@current/actions/': props.listActionsAPIResponse || { results: [] },
- },
- })
-
- const toolbarParams = {
- temporaryToken: 'UExb1dCsoqBtrhrZYxzmxXQ7XdjVH5Ea_zbQjTFuJqk',
- actionId: undefined,
- userIntent: undefined,
- dataAttributes: ['data-attr'],
- apiURL: '/',
- jsURL: 'http://localhost:8234/',
- userEmail: 'foobar@posthog.com',
- }
- useMountedLogic(toolbarLogic(toolbarParams))
- const theMountedActionsLogic = useMountedLogic(actionsLogic)
- if (props.name === 'actions') {
- theMountedActionsLogic.actions.getActions()
- }
- useMountedLogic(actionsTabLogic)
-
- const theMountedFlagsLogic = useMountedLogic(featureFlagsLogic)
- if (props.name === 'flags') {
- theMountedFlagsLogic.actions.storeLocalOverrides({ 'example-two': false, 'my-other-multi-example': 'test' })
- }
-
- useToolbarStyles()
-
- return (
-
- {}}
- position={{ x: 0, y: 0 }}
- savePosition={() => {}}
- >
- {props.contents}
-
-
- )
-}
-
-export const Actions: Story = Template.bind({})
-Actions.args = { contents: , name: 'actions', label: 'Actions' }
-
-export const ActionsWithResults: Story = Template.bind({})
-ActionsWithResults.args = {
- contents: ,
- name: 'actions',
- label: 'Actions',
- listActionsAPIResponse: {
- results: [
- {
- id: 49103,
- name: 'Clicked notification row',
- description: '',
- post_to_slack: false,
- slack_message_format: '',
- steps: [
- {
- id: '60889',
- event: '$autocapture',
- tag_name: null,
- text: null,
- text_matching: null,
- href: null,
- href_matching: null,
- selector: '.notifications-menu .activity-log-row',
- url: null,
- name: null,
- url_matching: 'contains',
- properties: [],
- },
- ],
- created_at: '2023-08-22T06:31:40.495488Z',
- created_by: {
- id: 5042,
- uuid: '017bcbcf-2391-0000-1868-14fb987285c5',
- distinct_id: 'PqMBBFBfKo638PwvjCWTSZ6dnswYzJ3RtH8VIHCBWAy',
- first_name: 'Paul',
- email: 'paul@posthog.com',
- is_email_verified: true,
- },
- deleted: false,
- is_calculating: false,
- last_calculated_at: '2023-08-22T06:31:40.495197Z',
- team_id: 2,
- is_action: true,
- bytecode_error: null,
- tags: [],
- },
- {
- id: 49059,
- name: 'clicked nav bar2',
- description: '',
- post_to_slack: false,
- slack_message_format: '',
- steps: [
- {
- id: '60829',
- event: '$autocapture',
- tag_name: null,
- text: 'Products',
- text_matching: null,
- href: '/product-analytics',
- href_matching: null,
- selector: null,
- url: null,
- name: '',
- url_matching: 'exact',
- properties: [],
- },
- {
- id: '60830',
- event: '$autocapture',
- tag_name: null,
- text: null,
- text_matching: null,
- href: null,
- href_matching: null,
- selector: '.border-b .h-full:nth-child(2) > .text-\\[13\\.5px\\]',
- url: null,
- name: '',
- url_matching: 'exact',
- properties: [],
- },
- ],
- created_at: '2023-08-21T20:54:17.218311Z',
- created_by: {
- id: 6352,
- uuid: '017d5334-911a-0000-6954-2d394263f3ca',
- distinct_id: '48vHN2rW28SvzA6D4NdgHajLOjBP1ulsI0Rr5DNas4i',
- first_name: 'Cameron',
- email: 'cameron@posthog.com',
- is_email_verified: true,
- },
- deleted: false,
- is_calculating: false,
- last_calculated_at: '2023-08-21T20:54:17.218023Z',
- team_id: 2,
- is_action: true,
- bytecode_error: null,
- tags: [],
- },
- {
- id: 49050,
- name: 'Clicked Products in nav bar',
- description: '',
- post_to_slack: false,
- slack_message_format: '',
- steps: [
- {
- id: '60819',
- event: '$autocapture',
- tag_name: null,
- text: 'Products',
- text_matching: null,
- href: '/product-analytics',
- href_matching: null,
- selector: null,
- url: null,
- name: '',
- url_matching: 'exact',
- properties: [],
- },
- {
- id: '60820',
- event: '$autocapture',
- tag_name: null,
- text: 'Pricing',
- text_matching: null,
- href: '/pricing',
- href_matching: null,
- selector: null,
- url: null,
- name: null,
- url_matching: 'contains',
- properties: [],
- },
- ],
- created_at: '2023-08-21T18:46:13.210958Z',
- created_by: {
- id: 6352,
- uuid: '017d5334-911a-0000-6954-2d394263f3ca',
- distinct_id: '48vHN2rW28SvzA6D4NdgHajLOjBP1ulsI0Rr5DNas4i',
- first_name: 'Cameron',
- email: 'cameron@posthog.com',
- is_email_verified: true,
- },
- deleted: false,
- is_calculating: false,
- last_calculated_at: '2023-08-21T18:46:13.210590Z',
- team_id: 2,
- is_action: true,
- bytecode_error: null,
- tags: [],
- },
- ],
- },
-}
-
-// TODO the heatmap window processes the DOM of the page so will need a bunch of extra setup 🙈
-
-export const Flags: Story = Template.bind({})
-Flags.args = {
- contents: ,
- name: 'flags',
- label: 'Feature Flags',
- tagComponent: 1 overridden,
-}
diff --git a/frontend/src/toolbar/actions/EditAction.tsx b/frontend/src/toolbar/actions/EditAction.tsx
deleted file mode 100644
index 782c2c10b42c7..0000000000000
--- a/frontend/src/toolbar/actions/EditAction.tsx
+++ /dev/null
@@ -1,210 +0,0 @@
-import { useActions, useValues } from 'kea'
-import { Field, Form, Group } from 'kea-forms'
-import { IconClose, IconDelete, IconEdit, IconMagnifier, IconMinusOutlined, IconPlus } from 'lib/lemon-ui/icons'
-import { LemonButton } from 'lib/lemon-ui/LemonButton'
-import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput'
-
-import { actionsTabLogic } from '~/toolbar/actions/actionsTabLogic'
-import { StepField } from '~/toolbar/actions/StepField'
-import { SelectorEditingModal } from '~/toolbar/elements/SelectorEditingModal'
-import { posthog } from '~/toolbar/posthog'
-import { getShadowRootPopoverContainer } from '~/toolbar/utils'
-
-export function EditAction(): JSX.Element {
- const {
- selectedActionId,
- inspectingElement,
- editingSelector,
- elementsChainBeingEdited,
- editingSelectorValue,
- actionForm,
- } = useValues(actionsTabLogic)
- const {
- setActionFormValue,
- selectAction,
- inspectForElementWithIndex,
- deleteAction,
- setElementSelector,
- editSelectorWithIndex,
- } = useActions(actionsTabLogic)
-
- return (
-
-
editSelectorWithIndex(null)}
- activeElementChain={elementsChainBeingEdited}
- startingSelector={editingSelectorValue}
- onChange={(selector) => {
- if (selector && editingSelector !== null) {
- posthog.capture('toolbar_manual_selector_applied', {
- chosenSelector: selector,
- })
- setElementSelector(selector, editingSelector)
- }
- }}
- />
-
-
-
- {selectedActionId === 'new' ? 'New ' : 'Edit '}
- action
-
- selectAction(null)}
- sideIcon={}
- >
- Cancel
-
-
-
-
-
- )
-}
diff --git a/frontend/src/toolbar/actions/actionsLogic.test.ts b/frontend/src/toolbar/actions/actionsLogic.test.ts
index 7c52916927a73..943260c5fec46 100644
--- a/frontend/src/toolbar/actions/actionsLogic.test.ts
+++ b/frontend/src/toolbar/actions/actionsLogic.test.ts
@@ -2,7 +2,7 @@ import { expectLogic } from 'kea-test-utils'
import { initKeaTests } from '~/test/init'
import { actionsLogic } from '~/toolbar/actions/actionsLogic'
-import { toolbarLogic } from '~/toolbar/toolbarLogic'
+import { toolbarConfigLogic } from '~/toolbar/toolbarConfigLogic'
import { ActionType } from '~/types'
const unsortedActions: ActionType[] = [
@@ -24,7 +24,7 @@ describe('toolbar actionsLogic', () => {
beforeEach(() => {
initKeaTests()
- toolbarLogic({ apiURL: 'http://localhost' }).mount()
+ toolbarConfigLogic({ apiURL: 'http://localhost' }).mount()
logic = actionsLogic()
logic.mount()
})
diff --git a/frontend/src/toolbar/actions/actionsLogic.ts b/frontend/src/toolbar/actions/actionsLogic.ts
index 68c101a7fb532..f2311a949cc87 100644
--- a/frontend/src/toolbar/actions/actionsLogic.ts
+++ b/frontend/src/toolbar/actions/actionsLogic.ts
@@ -2,8 +2,7 @@ import Fuse from 'fuse.js'
import { actions, kea, path, reducers, selectors } from 'kea'
import { loaders } from 'kea-loaders'
-import { toolbarLogic } from '~/toolbar/toolbarLogic'
-import { toolbarFetch } from '~/toolbar/utils'
+import { toolbarConfigLogic, toolbarFetch } from '~/toolbar/toolbarConfigLogic'
import { ActionType } from '~/types'
import type { actionsLogicType } from './actionsLogicType'
@@ -23,7 +22,7 @@ export const actionsLogic = kea([
const results = await response.json()
if (response.status === 403) {
- toolbarLogic.actions.authenticate()
+ toolbarConfigLogic.actions.authenticate()
return []
}
diff --git a/frontend/src/toolbar/actions/actionsTabLogic.tsx b/frontend/src/toolbar/actions/actionsTabLogic.tsx
index 0a0ab5235c938..e2cf38bfed1dd 100644
--- a/frontend/src/toolbar/actions/actionsTabLogic.tsx
+++ b/frontend/src/toolbar/actions/actionsTabLogic.tsx
@@ -6,9 +6,9 @@ import { lemonToast } from 'lib/lemon-ui/lemonToast'
import { urls } from 'scenes/urls'
import { actionsLogic } from '~/toolbar/actions/actionsLogic'
-import { toolbarButtonLogic } from '~/toolbar/button/toolbarButtonLogic'
+import { toolbarLogic } from '~/toolbar/bar/toolbarLogic'
import { posthog } from '~/toolbar/posthog'
-import { toolbarLogic } from '~/toolbar/toolbarLogic'
+import { toolbarConfigLogic } from '~/toolbar/toolbarConfigLogic'
import { ActionDraftType, ActionForm } from '~/toolbar/types'
import { actionStepToActionStepFormItem, elementToActionStep, stepToDatabaseFormat } from '~/toolbar/utils'
import { ActionType, ElementType } from '~/types'
@@ -69,7 +69,12 @@ export const actionsTabLogic = kea([
}),
connect(() => ({
- values: [toolbarLogic, ['dataAttributes'], actionsLogic, ['allActions']],
+ values: [
+ toolbarConfigLogic,
+ ['dataAttributes', 'apiURL', 'temporaryToken', 'buttonVisible', 'userIntent', 'actionId', 'dataAttributes'],
+ actionsLogic,
+ ['allActions'],
+ ],
})),
reducers({
@@ -149,7 +154,7 @@ export const actionsTabLogic = kea([
...formValues,
steps: formValues.steps?.map(stepToDatabaseFormat) || [],
}
- const { apiURL, temporaryToken } = toolbarLogic.values
+ const { apiURL, temporaryToken } = values
const { selectedActionId } = values
let response: ActionType
@@ -249,22 +254,21 @@ export const actionsTabLogic = kea([
},
selectAction: ({ id }) => {
if (id) {
- if (!toolbarLogic.values.buttonVisible) {
- toolbarLogic.actions.showButton()
+ if (!values.buttonVisible) {
+ toolbarConfigLogic.actions.showButton()
}
if (!values.buttonActionsVisible) {
actions.showButtonActions()
}
- if (!toolbarButtonLogic.values.actionsInfoVisible) {
- toolbarButtonLogic.actions.showActionsInfo()
- }
+
+ toolbarLogic.actions.setVisibleMenu('actions')
}
},
inspectElementSelected: ({ element, index }) => {
if (values.actionForm) {
const actionStep = actionStepToActionStepFormItem(
- elementToActionStep(element, toolbarLogic.values.dataAttributes),
+ elementToActionStep(element, values.dataAttributes),
true
)
const newSteps = (values.actionForm.steps || []).map((step, i) =>
@@ -277,8 +281,7 @@ export const actionsTabLogic = kea([
}
},
deleteAction: async () => {
- const { apiURL, temporaryToken } = toolbarLogic.values
- const { selectedActionId } = values
+ const { selectedActionId, apiURL, temporaryToken } = values
if (selectedActionId && selectedActionId !== 'new') {
await api.delete(
`${apiURL}/api/projects/@current/actions/${selectedActionId}/?temporary_token=${temporaryToken}`
@@ -297,13 +300,13 @@ export const actionsTabLogic = kea([
posthog.capture('toolbar mode triggered', { mode: 'actions', enabled: false })
},
[actionsLogic.actionTypes.getActionsSuccess]: () => {
- const { userIntent } = toolbarLogic.values
+ const { userIntent, actionId } = values
if (userIntent === 'edit-action') {
- actions.selectAction(toolbarLogic.values.actionId)
- toolbarLogic.actions.clearUserIntent()
+ actions.selectAction(actionId)
+ toolbarConfigLogic.actions.clearUserIntent()
} else if (userIntent === 'add-action') {
actions.newAction()
- toolbarLogic.actions.clearUserIntent()
+ toolbarConfigLogic.actions.clearUserIntent()
} else {
actions.setShowActionsTooltip(true)
}
diff --git a/frontend/src/toolbar/assets/Logo.tsx b/frontend/src/toolbar/assets/Logo.tsx
index 8279888ec6a76..41ae93c7c09c2 100644
--- a/frontend/src/toolbar/assets/Logo.tsx
+++ b/frontend/src/toolbar/assets/Logo.tsx
@@ -1,3 +1,4 @@
+// TODO the rest of the app shouldn't be importing this 🙈
export function Logo({ style }: React.PropsWithoutRef): JSX.Element {
return (
- ) : undefined
- }
- {...clickEvents}
- onMouseOver={isAuthenticated ? undefined : () => setExtensionPercentage(1)}
- style={{
- borderRadius: 10,
- height: 46,
- marginTop: -23,
- pointerEvents: hedgehogMode ? 'none' : undefined,
- display: hedgehogMode ? 'none' : 'flex',
- cursor: 'pointer',
- }}
- zIndex={3}
- >
- }
- zIndex={extensionPercentage > 0.95 ? 5 : 2}
- onClick={logout}
- style={{
- cursor: 'pointer',
- background: '#393939',
- borderRadius: 6,
- color: 'white',
- transform: `scale(${0.2 + 0.8 * extensionPercentage})`,
- }}
- />
-
- 0.95 ? 5 : 2}
- onClick={() => setHedgehogMode(!hedgehogMode)}
- style={{
- cursor: 'pointer',
- background: '#FFF',
- borderRadius: 6,
- color: 'white',
- transform: `scale(${0.2 + 0.8 * extensionPercentage})`,
- }}
- />
- {isAuthenticated ? (
- <>
- }
- label="Help"
- zIndex={2}
- onClick={() => window.open(HELP_URL, '_blank')?.focus()}
- labelStyle={{ opacity: extensionPercentage > 0.8 ? (extensionPercentage - 0.8) / 0.2 : 0 }}
- style={{
- cursor: 'pointer',
- background: '#777',
- color: 'white',
- borderRadius: 10,
- transform: `scale(${0.2 + 0.8 * extensionPercentage})`,
- }}
- />
-
- (side === 'right' && r < 0 ? 360 : 0)}
- label="Inspect"
- labelPosition={side === 'left' ? 'right' : 'left'}
- labelStyle={{
- opacity: inspectExtensionPercentage > 0.8 ? (inspectExtensionPercentage - 0.8) / 0.2 : 0,
- }}
- content={
-
-
- {inspectEnabled && selectedElement ? (
-
-
-
- ) : null}
-
- }
- zIndex={1}
- onClick={inspectEnabled ? disableInspect : enableInspect}
- style={{
- cursor: 'pointer',
- background: inspectEnabled ? '#8F98FF' : '#E7EAFD',
- transition: 'transform 0.2s, color 0.2s, background: 0.2s',
- transform: `scale(${0.2 + 0.8 * inspectExtensionPercentage})`,
- borderRadius,
- }}
- />
- (side === 'right' && r < 0 ? 360 : 0)}
- label={heatmapEnabled && !heatmapLoading ? null : 'Heatmap'}
- labelPosition={side === 'left' ? 'right' : 'left'}
- labelStyle={{
- opacity:
- heatmapEnabled && !heatmapLoading
- ? 0
- : heatmapExtensionPercentage > 0.8
- ? (heatmapExtensionPercentage - 0.8) / 0.2
- : 0,
- }}
- content={}
- zIndex={2}
- onClick={heatmapEnabled ? disableHeatmap : enableHeatmap}
- style={{
- cursor: 'pointer',
- background: heatmapEnabled ? '#FF9870' : '#FEE3DA',
- transform: `scale(${0.2 + 0.8 * heatmapExtensionPercentage})`,
- borderRadius,
- }}
- >
- {heatmapEnabled && !heatmapLoading ? (
-
- {elementCount}
-
- }
- zIndex={4}
- onClick={heatmapInfoVisible ? hideHeatmapInfo : showHeatmapInfo}
- style={{
- cursor: 'pointer',
- background: heatmapInfoVisible ? 'hsla(17, 100%, 47%, 1)' : 'hsla(17, 84%, 95%, 1)',
- color: heatmapInfoVisible ? '#FFEB3B' : 'hsl(17, 64%, 32%)',
- width: 'auto',
- minWidth: 26,
- fontSize: '20px',
- lineHeight: '26px',
- padding: '0 4px',
- transform: `scale(${0.2 + 0.8 * heatmapExtensionPercentage})`,
- borderRadius: 7,
- }}
- />
- ) : null}
-
- (side === 'right' && r < 0 ? 360 : 0)}
- label={buttonActionsVisible && (!allActionsLoading || actionCount > 0) ? null : 'Actions'}
- labelPosition={side === 'left' ? 'right' : 'left'}
- labelStyle={{
- opacity: actionsExtensionPercentage > 0.8 ? (actionsExtensionPercentage - 0.8) / 0.2 : 0,
- }}
- content={
-
- }
- zIndex={1}
- onClick={buttonActionsVisible ? hideButtonActions : showButtonActions}
- style={{
- cursor: 'pointer',
- transform: `scale(${0.2 + 0.8 * actionsExtensionPercentage})`,
- background: buttonActionsVisible ? '#f1aa04' : '#fef5e2',
- borderRadius,
- }}
- >
- {buttonActionsVisible && (!allActionsLoading || actionCount > 0) ? (
-
- {actionCount}
-
- }
- zIndex={4}
- onClick={actionsInfoVisible ? hideActionsInfo : showActionsInfo}
- style={{
- cursor: 'pointer',
- background: actionsInfoVisible ? '#f1aa04' : '#fef5e2',
- color: actionsInfoVisible ? '#fef5e2' : '#f1aa04',
- width: 'auto',
- minWidth: 26,
- fontSize: '20px',
- lineHeight: '26px',
- padding: '0 4px',
- transform: `scale(${0.2 + 0.8 * actionsExtensionPercentage})`,
- borderRadius: 7,
- }}
- />
- ) : null}
-
- (side === 'right' && r < 0 ? 360 : 0)}
- label="Feature Flags"
- labelPosition={side === 'left' ? 'right' : 'left'}
- labelStyle={{
- opacity:
- featureFlagsExtensionPercentage > 0.8
- ? (featureFlagsExtensionPercentage - 0.8) / 0.2
- : 0,
- }}
- content={}
- zIndex={1}
- onClick={flagsVisible ? hideFlags : showFlags}
- style={{
- cursor: 'pointer',
- transform: `scale(${0.2 + 0.8 * featureFlagsExtensionPercentage})`,
- background: flagsVisible ? '#94D674' : '#D6EBCC',
- borderRadius,
- }}
- />
- >
- ) : null}
-
- )
-}
diff --git a/frontend/src/toolbar/button/icons/Close.tsx b/frontend/src/toolbar/button/icons/Close.tsx
deleted file mode 100644
index 11b673d374fd1..0000000000000
--- a/frontend/src/toolbar/button/icons/Close.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-export function Close(props: React.PropsWithoutRef): JSX.Element {
- return (
-
- )
-}
diff --git a/frontend/src/toolbar/button/icons/Fire.scss b/frontend/src/toolbar/button/icons/Fire.scss
deleted file mode 100644
index 4b13e407b311a..0000000000000
--- a/frontend/src/toolbar/button/icons/Fire.scss
+++ /dev/null
@@ -1,67 +0,0 @@
-svg.posthog-toolbar-icon-fire.animated {
- path:nth-child(1) {
- animation: Toolbar__fire-1 0.5s infinite;
- }
-
- path:nth-child(2) {
- animation: Toolbar__fire-2 0.5s infinite;
- }
-
- path:nth-child(3) {
- animation: Toolbar__fire-3 0.5s infinite;
- }
-}
-
-@keyframes Toolbar__fire-1 {
- 0% {
- fill: #fb4f0e;
- }
-
- 33.3% {
- fill: #fe6d37;
- }
-
- 66.6% {
- fill: #fcb811;
- }
-
- 100% {
- fill: #fb4f0e;
- }
-}
-
-@keyframes Toolbar__fire-2 {
- 0% {
- fill: #fe6d37;
- }
-
- 33.3% {
- fill: #fcb811;
- }
-
- 66.6% {
- fill: #fb4f0e;
- }
-
- 100% {
- fill: #fe6d37;
- }
-}
-
-@keyframes Toolbar__fire-3 {
- 0% {
- fill: #fcb811;
- }
-
- 33.3% {
- fill: #fb4f0e;
- }
-
- 66.6% {
- fill: #fe6d37;
- }
-
- 100% {
- fill: #fcb811;
- }
-}
diff --git a/frontend/src/toolbar/button/icons/Fire.tsx b/frontend/src/toolbar/button/icons/Fire.tsx
deleted file mode 100644
index 25c7abc9cf993..0000000000000
--- a/frontend/src/toolbar/button/icons/Fire.tsx
+++ /dev/null
@@ -1,39 +0,0 @@
-import './Fire.scss'
-
-const fireColors = {
- engaged: ['#FB4F0E', '#FE6D37', '#FCB811'],
- disengaged: ['#FF9770', '#FF6E37', '#FB4F0D'],
-}
-
-interface FireProps extends React.PropsWithoutRef {
- engaged?: boolean
- animated?: boolean
-}
-
-export function Fire({ engaged = false, animated = false, ...props }: FireProps): JSX.Element {
- const colors = fireColors[engaged ? 'engaged' : 'disengaged']
- return (
-
- )
-}
diff --git a/frontend/src/toolbar/button/icons/Flag.scss b/frontend/src/toolbar/button/icons/Flag.scss
deleted file mode 100644
index 92a0749cf20b0..0000000000000
--- a/frontend/src/toolbar/button/icons/Flag.scss
+++ /dev/null
@@ -1,67 +0,0 @@
-svg.posthog-toolbar-icon-flag.animated {
- path.color1 {
- animation: Toolbar__flag-1 0.5s infinite;
- }
-
- path.color2 {
- animation: Toolbar__flag-2 0.5s infinite;
- }
-
- path.color3 {
- animation: Toolbar__flag-3 0.5s infinite;
- }
-}
-
-@keyframes Toolbar__flag-1 {
- 0% {
- fill: #70aa54;
- }
-
- 33.3% {
- fill: #527940;
- }
-
- 66.6% {
- fill: #415e32;
- }
-
- 100% {
- fill: #70aa54;
- }
-}
-
-@keyframes Toolbar__flag-2 {
- 0% {
- fill: #527940;
- }
-
- 33.3% {
- fill: #415e32;
- }
-
- 66.6% {
- fill: #70aa54;
- }
-
- 100% {
- fill: #527940;
- }
-}
-
-@keyframes Toolbar__flag-3 {
- 0% {
- fill: #415e32;
- }
-
- 33.3% {
- fill: #70aa54;
- }
-
- 66.6% {
- fill: #527940;
- }
-
- 100% {
- fill: #415e32;
- }
-}
diff --git a/frontend/src/toolbar/button/icons/Flag.tsx b/frontend/src/toolbar/button/icons/Flag.tsx
deleted file mode 100644
index b16160dc2a572..0000000000000
--- a/frontend/src/toolbar/button/icons/Flag.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-import './Flag.scss'
-
-interface FlagProps extends React.PropsWithoutRef {
- engaged?: boolean
- animated?: boolean
-}
-
-export function Flag({ engaged, animated, ...props }: FlagProps): JSX.Element {
- return engaged ? (
-
- ) : (
-
- )
-}
diff --git a/frontend/src/toolbar/button/icons/Magnifier.tsx b/frontend/src/toolbar/button/icons/Magnifier.tsx
deleted file mode 100644
index 86e0e71485c3e..0000000000000
--- a/frontend/src/toolbar/button/icons/Magnifier.tsx
+++ /dev/null
@@ -1,68 +0,0 @@
-interface MagnifierProps extends React.PropsWithoutRef {
- engaged?: boolean
-}
-
-export function Magnifier({ engaged = false, ...props }: MagnifierProps): JSX.Element {
- return (
-
- )
-}
diff --git a/frontend/src/toolbar/button/toolbarButtonLogic.ts b/frontend/src/toolbar/button/toolbarButtonLogic.ts
deleted file mode 100644
index 68a0f3b7609d6..0000000000000
--- a/frontend/src/toolbar/button/toolbarButtonLogic.ts
+++ /dev/null
@@ -1,227 +0,0 @@
-import { actions, connect, kea, listeners, path, reducers, selectors } from 'kea'
-import { windowValues } from 'kea-window-values'
-import { HedgehogActor } from 'lib/components/HedgehogBuddy/HedgehogBuddy'
-
-import { actionsTabLogic } from '~/toolbar/actions/actionsTabLogic'
-import { elementsLogic } from '~/toolbar/elements/elementsLogic'
-import { heatmapLogic } from '~/toolbar/elements/heatmapLogic'
-import { posthog } from '~/toolbar/posthog'
-import { inBounds } from '~/toolbar/utils'
-
-import type { toolbarButtonLogicType } from './toolbarButtonLogicType'
-
-export const toolbarButtonLogic = kea([
- path(['toolbar', 'button', 'toolbarButtonLogic']),
- connect(() => ({
- actions: [actionsTabLogic, ['showButtonActions'], elementsLogic, ['enableInspect']],
- })),
- actions(() => ({
- showHeatmapInfo: true,
- hideHeatmapInfo: true,
- showActionsInfo: true,
- hideActionsInfo: true,
- showFlags: true,
- hideFlags: true,
- setHedgehogMode: (hedgehogMode: boolean) => ({ hedgehogMode }),
- setExtensionPercentage: (percentage: number) => ({ percentage }),
- saveDragPosition: (x: number, y: number) => ({ x, y }),
- setDragPosition: (x: number, y: number) => ({ x, y }),
- saveHeatmapPosition: (x: number, y: number) => ({ x, y }),
- saveActionsPosition: (x: number, y: number) => ({ x, y }),
- saveFlagsPosition: (x: number, y: number) => ({ x, y }),
- setHedgehogActor: (actor: HedgehogActor) => ({ actor }),
- })),
- windowValues(() => ({
- windowHeight: (window: Window) => window.innerHeight,
- windowWidth: (window: Window) => Math.min(window.innerWidth, window.document.body.clientWidth),
- })),
- reducers(() => ({
- heatmapInfoVisible: [
- false,
- {
- showHeatmapInfo: () => true,
- hideHeatmapInfo: () => false,
- [heatmapLogic.actionTypes.disableHeatmap]: () => false,
- [heatmapLogic.actionTypes.enableHeatmap]: () => false,
- },
- ],
- actionsInfoVisible: [
- false,
- {
- showActionsInfo: () => true,
- hideActionsInfo: () => false,
- [actionsTabLogic.actionTypes.showButtonActions]: () => false,
- [actionsTabLogic.actionTypes.hideButtonActions]: () => false,
- },
- ],
- flagsVisible: [
- false,
- {
- showFlags: () => true,
- hideFlags: () => false,
- },
- ],
- extensionPercentage: [
- 0,
- {
- setExtensionPercentage: (_, { percentage }) => percentage,
- },
- ],
- lastDragPosition: [
- null as null | {
- x: number
- y: number
- },
- { persist: true },
- {
- setDragPosition: (_, { x, y }) => ({ x, y }),
- },
- ],
- heatmapPosition: [
- { x: 100, y: 100 },
- {
- saveHeatmapPosition: (_, { x, y }) => ({ x, y }),
- },
- ],
- actionsPosition: [
- { x: 120, y: 100 } as {
- x: number
- y: number
- },
- {
- saveActionsPosition: (_, { x, y }) => ({ x, y }),
- },
- ],
- flagsPosition: [
- { x: 140, y: 100 } as {
- x: number
- y: number
- },
- {
- saveFlagsPosition: (_, { x, y }) => ({ x, y }),
- },
- ],
- hedgehogMode: [
- false,
- { persist: true },
- {
- setHedgehogMode: (_, { hedgehogMode }) => hedgehogMode,
- },
- ],
- hedgehogActor: [
- null as HedgehogActor | null,
- {
- setHedgehogActor: (_, { actor }) => actor,
- },
- ],
- })),
- selectors({
- dragPosition: [
- (s) => [s.lastDragPosition, s.windowWidth, s.windowHeight],
- (lastDragPosition, windowWidth, windowHeight) => {
- const widthPadding = 35
- const heightPadding = 30
-
- const { x, y } = lastDragPosition || {
- x: -widthPadding,
- y: 60,
- }
- const dragX = x < 0 ? windowWidth + x : x
- const dragY = y < 0 ? windowHeight + y : y
-
- return {
- x: inBounds(widthPadding, dragX, windowWidth - widthPadding),
- y: inBounds(heightPadding, dragY, windowHeight - heightPadding),
- }
- },
- ],
- toolbarListVerticalPadding: [
- (s) => [s.dragPosition, s.windowHeight],
- ({ y }, windowHeight) => {
- if (y < 90) {
- return -60 + 90 - y
- } else if (y > windowHeight - 160) {
- return -60 - (160 - (windowHeight - y))
- }
- return -60
- },
- ],
- helpButtonOnTop: [(s) => [s.dragPosition, s.windowHeight], ({ y }, windowHeight) => y > windowHeight - 100],
- side: [
- (s) => [s.dragPosition, s.windowWidth],
- ({ x }, windowWidth) => (x < windowWidth / 2 ? 'left' : 'right'),
- ],
- hedgehogModeDistance: [
- (s) => [s.dragPosition, s.windowWidth],
- ({ x, y }, windowWidth) => 90 + (x > windowWidth - 40 || y < 80 ? -28 : 0) + (y < 40 ? -6 : 0),
- ],
- hedgehogModeRotation: [
- (s) => [s.dragPosition, s.windowWidth],
- ({ x, y }, windowWidth) => -68 + (x > windowWidth - 40 || y < 80 ? 10 : 0) + (y < 40 ? 10 : 0),
- ],
- closeDistance: [
- (s) => [s.dragPosition, s.windowWidth],
- ({ x, y }, windowWidth) => 58 + (x > windowWidth - 40 || y < 80 ? -28 : 0) + (y < 40 ? -6 : 0),
- ],
- closeRotation: [
- (s) => [s.dragPosition, s.windowWidth],
- ({ x, y }, windowWidth) => -54 + (x > windowWidth - 40 || y < 80 ? 10 : 0) + (y < 40 ? 10 : 0),
- ],
- inspectExtensionPercentage: [
- (s) => [elementsLogic.selectors.inspectEnabled, s.extensionPercentage],
- (inspectEnabled, extensionPercentage) =>
- inspectEnabled ? Math.max(extensionPercentage, 0.53) : extensionPercentage,
- ],
- heatmapExtensionPercentage: [
- (s) => [heatmapLogic.selectors.heatmapEnabled, s.extensionPercentage],
- (heatmapEnabled, extensionPercentage) =>
- heatmapEnabled ? Math.max(extensionPercentage, 0.53) : extensionPercentage,
- ],
- heatmapWindowVisible: [
- (s) => [s.heatmapInfoVisible, heatmapLogic.selectors.heatmapEnabled],
- (heatmapInfoVisible, heatmapEnabled) => heatmapInfoVisible && heatmapEnabled,
- ],
- actionsExtensionPercentage: [
- (s) => [actionsTabLogic.selectors.buttonActionsVisible, s.extensionPercentage],
- (buttonActionsVisible, extensionPercentage) =>
- buttonActionsVisible ? Math.max(extensionPercentage, 0.53) : extensionPercentage,
- ],
- actionsWindowVisible: [
- (s) => [s.actionsInfoVisible, actionsTabLogic.selectors.buttonActionsVisible],
- (actionsInfoVisible, buttonActionsVisible) => actionsInfoVisible && buttonActionsVisible,
- ],
- featureFlagsExtensionPercentage: [
- (s) => [s.flagsVisible, s.extensionPercentage],
- (flagsVisible, extensionPercentage) =>
- flagsVisible ? Math.max(extensionPercentage, 0.53) : extensionPercentage,
- ],
- }),
- listeners(({ actions, values }) => ({
- showFlags: () => {
- posthog.capture('toolbar mode triggered', { mode: 'flags', enabled: true })
- values.hedgehogActor?.setAnimation('flag')
- },
- hideFlags: () => {
- posthog.capture('toolbar mode triggered', { mode: 'flags', enabled: false })
- },
- showHeatmapInfo: () => {
- values.hedgehogActor?.setAnimation('heatmaps')
- },
- showButtonActions: () => {
- values.hedgehogActor?.setAnimation('action')
- },
- hideActionsInfo: () => {
- actionsTabLogic.actions.selectAction(null)
- },
- enableInspect: () => {
- values.hedgehogActor?.setAnimation('inspect')
- },
- saveDragPosition: ({ x, y }) => {
- const { windowWidth, windowHeight } = values
- actions.setDragPosition(
- x > windowWidth / 2 ? -(windowWidth - x) : x,
- y > windowHeight / 2 ? -(windowHeight - y) : y
- )
- },
- })),
-])
diff --git a/frontend/src/toolbar/elements/elementsLogic.ts b/frontend/src/toolbar/elements/elementsLogic.ts
index 9aaa7a665a8b7..7ddc4bd5407b5 100644
--- a/frontend/src/toolbar/elements/elementsLogic.ts
+++ b/frontend/src/toolbar/elements/elementsLogic.ts
@@ -3,15 +3,14 @@ import { collectAllElementsDeep } from 'query-selector-shadow-dom'
import { actionsLogic } from '~/toolbar/actions/actionsLogic'
import { actionsTabLogic } from '~/toolbar/actions/actionsTabLogic'
-import { toolbarButtonLogic } from '~/toolbar/button/toolbarButtonLogic'
-import { heatmapLogic } from '~/toolbar/elements/heatmapLogic'
import { posthog } from '~/toolbar/posthog'
import { currentPageLogic } from '~/toolbar/stats/currentPageLogic'
-import { toolbarLogic } from '~/toolbar/toolbarLogic'
+import { toolbarConfigLogic } from '~/toolbar/toolbarConfigLogic'
import { ActionElementWithMetadata, ElementWithMetadata } from '~/toolbar/types'
-import { elementToActionStep, getAllClickTargets, getElementForStep, getRectForElement } from '~/toolbar/utils'
+import { elementToActionStep, getAllClickTargets, getElementForStep, getRectForElement } from '../utils'
import type { elementsLogicType } from './elementsLogicType'
+import { heatmapLogic } from './heatmapLogic'
export type ActionElementMap = Map
export type ElementMap = Map
@@ -30,8 +29,8 @@ function debounce) => ReturnType>(
export const elementsLogic = kea([
path(['toolbar', 'elements', 'elementsLogic']),
connect(() => ({
- values: [actionsTabLogic, ['actionForm']],
- actions: [actionsTabLogic, ['selectAction']],
+ values: [actionsTabLogic, ['actionForm'], currentPageLogic, ['href']],
+ actions: [actionsTabLogic, ['selectAction', 'newAction']],
})),
actions({
enableInspect: true,
@@ -128,18 +127,22 @@ export const elementsLogic = kea([
heatmapEnabled: [() => [heatmapLogic.selectors.heatmapEnabled], (heatmapEnabled) => heatmapEnabled],
heatmapElements: [
- (s) => [heatmapLogic.selectors.countedElements, s.rectUpdateCounter, toolbarLogic.selectors.buttonVisible],
+ (s) => [
+ heatmapLogic.selectors.countedElements,
+ s.rectUpdateCounter,
+ toolbarConfigLogic.selectors.buttonVisible,
+ ],
(countedElements) =>
countedElements.map((e) => ({ ...e, rect: getRectForElement(e.element) } as ElementWithMetadata)),
],
allInspectElements: [
- (s) => [s.inspectEnabled, currentPageLogic.selectors.href],
+ (s) => [s.inspectEnabled, s.href],
(inspectEnabled) => (inspectEnabled ? getAllClickTargets() : []),
],
inspectElements: [
- (s) => [s.allInspectElements, s.rectUpdateCounter, toolbarLogic.selectors.buttonVisible],
+ (s) => [s.allInspectElements, s.rectUpdateCounter, toolbarConfigLogic.selectors.buttonVisible],
(allInspectElements) =>
allInspectElements
.map((element) => ({ element, rect: getRectForElement(element) } as ElementWithMetadata))
@@ -173,7 +176,7 @@ export const elementsLogic = kea([
],
actionElements: [
- (s) => [s.allActionElements, s.rectUpdateCounter, toolbarLogic.selectors.buttonVisible],
+ (s) => [s.allActionElements, s.rectUpdateCounter, toolbarConfigLogic.selectors.buttonVisible],
(allActionElements) =>
allActionElements.map((element) =>
element.element ? { ...element, rect: getRectForElement(element.element) } : element
@@ -198,7 +201,11 @@ export const elementsLogic = kea([
],
actionsForElementMap: [
- (s) => [actionsLogic.selectors.sortedActions, s.rectUpdateCounter, toolbarLogic.selectors.buttonVisible],
+ (s) => [
+ actionsLogic.selectors.sortedActions,
+ s.rectUpdateCounter,
+ toolbarConfigLogic.selectors.buttonVisible,
+ ],
(sortedActions): ActionElementMap => {
const allElements = collectAllElementsDeep('*', document)
const actionsForElementMap = new Map()
@@ -281,7 +288,12 @@ export const elementsLogic = kea([
],
selectedElementMeta: [
- (s) => [s.selectedElement, s.elementMap, s.actionsForElementMap, toolbarLogic.selectors.dataAttributes],
+ (s) => [
+ s.selectedElement,
+ s.elementMap,
+ s.actionsForElementMap,
+ toolbarConfigLogic.selectors.dataAttributes,
+ ],
(selectedElement, elementMap, actionsForElementMap, dataAttributes) => {
if (selectedElement) {
const meta = elementMap.get(selectedElement)
@@ -299,7 +311,7 @@ export const elementsLogic = kea([
],
hoverElementMeta: [
- (s) => [s.hoverElement, s.elementMap, s.actionsForElementMap, toolbarLogic.selectors.dataAttributes],
+ (s) => [s.hoverElement, s.elementMap, s.actionsForElementMap, toolbarConfigLogic.selectors.dataAttributes],
(hoverElement, elementMap, actionsForElementMap, dataAttributes) => {
if (hoverElement) {
const meta = elementMap.get(hoverElement)
@@ -317,7 +329,12 @@ export const elementsLogic = kea([
],
highlightElementMeta: [
- (s) => [s.highlightElement, s.elementMap, s.actionsForElementMap, toolbarLogic.selectors.dataAttributes],
+ (s) => [
+ s.highlightElement,
+ s.elementMap,
+ s.actionsForElementMap,
+ toolbarConfigLogic.selectors.dataAttributes,
+ ],
(highlightElement, elementMap, actionsForElementMap, dataAttributes) => {
if (highlightElement) {
const meta = elementMap.get(highlightElement)
@@ -340,7 +357,7 @@ export const elementsLogic = kea([
},
],
}),
- listeners(({ actions, values }) => ({
+ listeners(({ actions }) => ({
enableInspect: () => {
posthog.capture('toolbar mode triggered', { mode: 'inspect', enabled: true })
actionsLogic.actions.getActions()
@@ -361,9 +378,6 @@ export const elementsLogic = kea([
actions.setSelectedElement(element)
}
- const { inspectEnabled, heatmapEnabled, enabledLast, selectedElementMeta } = values
- const { buttonActionsVisible: actionsEnabled } = actionsTabLogic.values
-
// Get list of data-* attributes in the element
const data_attributes = []
if (element?.attributes) {
@@ -385,19 +399,11 @@ export const elementsLogic = kea([
has_data_attr: data_attributes.includes('data-attr'),
data_attributes: data_attributes,
attribute_length: element?.attributes.length,
- inspect_enabled: inspectEnabled,
- heatmap_enabled: heatmapEnabled,
- actions_enabled: actionsEnabled,
- enabled_last: enabledLast,
- heatmap_count: heatmapEnabled ? selectedElementMeta?.count || 0 : undefined,
- actions_count: actionsEnabled ? selectedElementMeta?.actions.length : undefined,
})
},
createAction: ({ element }) => {
- actionsTabLogic.actions.showButtonActions()
- toolbarButtonLogic.actions.showActionsInfo()
- elementsLogic.actions.selectElement(null)
- actionsTabLogic.actions.newAction(element)
+ actions.selectElement(null)
+ actions.newAction(element)
},
})),
events(({ cache, values, actions }) => ({
diff --git a/frontend/src/toolbar/elements/heatmapLogic.ts b/frontend/src/toolbar/elements/heatmapLogic.ts
index 516717c8b701d..8bb148b4f6adc 100644
--- a/frontend/src/toolbar/elements/heatmapLogic.ts
+++ b/frontend/src/toolbar/elements/heatmapLogic.ts
@@ -8,9 +8,9 @@ import { collectAllElementsDeep, querySelectorAllDeep } from 'query-selector-sha
import { posthog } from '~/toolbar/posthog'
import { currentPageLogic } from '~/toolbar/stats/currentPageLogic'
-import { toolbarLogic } from '~/toolbar/toolbarLogic'
+import { toolbarConfigLogic, toolbarFetch } from '~/toolbar/toolbarConfigLogic'
import { CountedHTMLElement, ElementsEventType } from '~/toolbar/types'
-import { elementToActionStep, toolbarFetch, trimElement } from '~/toolbar/utils'
+import { elementToActionStep, trimElement } from '~/toolbar/utils'
import { FilterType, PropertyFilterType, PropertyOperator } from '~/types'
import type { heatmapLogicType } from './heatmapLogicType'
@@ -23,9 +23,10 @@ const emptyElementsStatsPages: PaginatedResponse = {
export const heatmapLogic = kea([
path(['toolbar', 'elements', 'heatmapLogic']),
- connect(() => ({
- values: [toolbarLogic, ['apiURL']],
- })),
+ connect({
+ values: [toolbarConfigLogic, ['apiURL'], currentPageLogic, ['href', 'wildcardHref']],
+ actions: [currentPageLogic, ['setHref', 'setWildcardHref']],
+ }),
actions({
getElementStats: (url?: string | null) => ({
url,
@@ -90,7 +91,7 @@ export const heatmapLogic = kea([
{
resetElementStats: () => emptyElementsStatsPages,
getElementStats: async ({ url }, breakpoint) => {
- const { href, wildcardHref } = currentPageLogic.values
+ const { href, wildcardHref } = values
let defaultUrl: string = ''
if (!url) {
const params: Partial = {
@@ -127,7 +128,7 @@ export const heatmapLogic = kea([
)
if (response.status === 403) {
- toolbarLogic.actions.authenticate()
+ toolbarConfigLogic.actions.authenticate()
return emptyElementsStatsPages
}
@@ -158,8 +159,8 @@ export const heatmapLogic = kea([
elements: [
(selectors) => [
selectors.elementStats,
- toolbarLogic.selectors.dataAttributes,
- currentPageLogic.selectors.href,
+ toolbarConfigLogic.selectors.dataAttributes,
+ selectors.href,
selectors.matchLinksByHref,
],
(elementStats, dataAttributes, href, matchLinksByHref) => {
@@ -242,7 +243,7 @@ export const heatmapLogic = kea([
},
],
countedElements: [
- (selectors) => [selectors.elements, toolbarLogic.selectors.dataAttributes],
+ (selectors) => [selectors.elements, toolbarConfigLogic.selectors.dataAttributes],
(elements, dataAttributes) => {
const normalisedElements = new Map()
;(elements || []).forEach((countedElement) => {
@@ -316,13 +317,13 @@ export const heatmapLogic = kea([
actions.getElementStats(values.elementStats.next)
}
},
- [currentPageLogic.actionTypes.setHref]: () => {
+ setHref: () => {
if (values.heatmapEnabled) {
actions.resetElementStats()
actions.getElementStats()
}
},
- [currentPageLogic.actionTypes.setWildcardHref]: async (_, breakpoint) => {
+ setWildcardHref: async (_, breakpoint) => {
await breakpoint(100)
if (values.heatmapEnabled) {
actions.resetElementStats()
diff --git a/frontend/src/toolbar/flags/FeatureFlags.tsx b/frontend/src/toolbar/flags/FlagsToolbarMenu.tsx
similarity index 63%
rename from frontend/src/toolbar/flags/FeatureFlags.tsx
rename to frontend/src/toolbar/flags/FlagsToolbarMenu.tsx
index 2da4f27b918a7..e022ca16cb501 100644
--- a/frontend/src/toolbar/flags/FeatureFlags.tsx
+++ b/frontend/src/toolbar/flags/FlagsToolbarMenu.tsx
@@ -1,59 +1,57 @@
-import './featureFlags.scss'
-
-import { Link } from '@posthog/lemon-ui'
import clsx from 'clsx'
import { useActions, useValues } from 'kea'
import { AnimatedCollapsible } from 'lib/components/AnimatedCollapsible'
+import { IconOpenInNew } from 'lib/lemon-ui/icons'
import { LemonCheckbox } from 'lib/lemon-ui/LemonCheckbox'
-import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput'
-import { LemonSwitch } from 'lib/lemon-ui/LemonSwitch/LemonSwitch'
+import { LemonInput } from 'lib/lemon-ui/LemonInput'
+import { LemonSwitch } from 'lib/lemon-ui/LemonSwitch'
+import { Link } from 'lib/lemon-ui/Link'
import { Spinner } from 'lib/lemon-ui/Spinner'
import { urls } from 'scenes/urls'
+import { ToolbarMenu } from '~/toolbar/bar/ToolbarMenu'
import { featureFlagsLogic } from '~/toolbar/flags/featureFlagsLogic'
-import { toolbarLogic } from '~/toolbar/toolbarLogic'
+import { toolbarConfigLogic } from '~/toolbar/toolbarConfigLogic'
-export function FeatureFlags(): JSX.Element {
+export const FlagsToolbarMenu = (): JSX.Element => {
const { searchTerm, filteredFlags, userFlagsLoading } = useValues(featureFlagsLogic)
- const { setOverriddenUserFlag, deleteOverriddenUserFlag, setSearchTerm } = useActions(featureFlagsLogic)
- const { apiURL } = useValues(toolbarLogic)
-
+ const { setSearchTerm, setOverriddenUserFlag, deleteOverriddenUserFlag } = useActions(featureFlagsLogic)
+ const { apiURL } = useValues(toolbarConfigLogic)
return (
-
-
- Note, overriding feature flags below will only affect this browser.
-
- <>
+
+
setSearchTerm(s)}
/>
-
+
+
+
+
{filteredFlags.length > 0 ? (
filteredFlags.map(({ feature_flag, value, hasOverride, hasVariants, currentValue }) => (
-
-
-
- {feature_flag.key}
+
+
+
+
+ {feature_flag.key}
+
+
-
+
{
@@ -71,12 +69,7 @@ export function FeatureFlags(): JSX.Element {
-
+
{feature_flag.filters?.multivariate?.variants.map((variant) => (
{userFlagsLoading ? (
-
+
+
+
) : (
`No ${searchTerm.length ? 'matching ' : ''}feature flags found.`
)}
)}
- >
-
+
+
+
+ Note: overriding feature flags will only affect this browser.
+
+
)
}
diff --git a/frontend/src/toolbar/flags/featureFlagsLogic.test.ts b/frontend/src/toolbar/flags/featureFlagsLogic.test.ts
index f0f04d87060dd..0841606815f44 100644
--- a/frontend/src/toolbar/flags/featureFlagsLogic.test.ts
+++ b/frontend/src/toolbar/flags/featureFlagsLogic.test.ts
@@ -2,7 +2,7 @@ import { expectLogic } from 'kea-test-utils'
import { initKeaTests } from '~/test/init'
import { featureFlagsLogic } from '~/toolbar/flags/featureFlagsLogic'
-import { toolbarLogic } from '~/toolbar/toolbarLogic'
+import { toolbarConfigLogic } from '~/toolbar/toolbarConfigLogic'
import { CombinedFeatureFlagAndValueType } from '~/types'
const featureFlags = [
@@ -36,7 +36,7 @@ describe('toolbar featureFlagsLogic', () => {
beforeEach(() => {
initKeaTests()
- toolbarLogic({ apiURL: 'http://localhost' }).mount()
+ toolbarConfigLogic({ apiURL: 'http://localhost' }).mount()
logic = featureFlagsLogic()
logic.mount()
})
@@ -78,6 +78,6 @@ describe('toolbar featureFlagsLogic', () => {
)
await expectLogic(logic, () => {
logic.actions.getUserFlags()
- }).toDispatchActions([toolbarLogic.actionTypes.tokenExpired])
+ }).toDispatchActions([toolbarConfigLogic.actionTypes.tokenExpired])
})
})
diff --git a/frontend/src/toolbar/flags/featureFlagsLogic.ts b/frontend/src/toolbar/flags/featureFlagsLogic.ts
index a6e21b5458aab..747937ab6e19f 100644
--- a/frontend/src/toolbar/flags/featureFlagsLogic.ts
+++ b/frontend/src/toolbar/flags/featureFlagsLogic.ts
@@ -4,16 +4,17 @@ import { loaders } from 'kea-loaders'
import { encodeParams } from 'kea-router'
import type { PostHog } from 'posthog-js'
-import { posthog } from '~/toolbar/posthog'
-import { toolbarLogic } from '~/toolbar/toolbarLogic'
-import { toolbarFetch } from '~/toolbar/utils'
+import { posthog as posthogJS } from '~/toolbar/posthog'
+import { toolbarConfigLogic, toolbarFetch } from '~/toolbar/toolbarConfigLogic'
import { CombinedFeatureFlagAndValueType } from '~/types'
import type { featureFlagsLogicType } from './featureFlagsLogicType'
export const featureFlagsLogic = kea
([
path(['toolbar', 'flags', 'featureFlagsLogic']),
- connect(() => [toolbarLogic]),
+ connect(() => ({
+ values: [toolbarConfigLogic, ['posthog']],
+ })),
actions({
getUserFlags: true,
setOverriddenUserFlag: (flagKey: string, overrideValue: string | boolean) => ({ flagKey, overrideValue }),
@@ -22,20 +23,20 @@ export const featureFlagsLogic = kea([
checkLocalOverrides: true,
storeLocalOverrides: (localOverrides: Record) => ({ localOverrides }),
}),
- loaders(() => ({
+ loaders(({ values }) => ({
userFlags: [
[] as CombinedFeatureFlagAndValueType[],
{
getUserFlags: async (_, breakpoint) => {
const params = {
- groups: getGroups(toolbarLogic.values.posthog),
+ groups: getGroups(values.posthog),
}
const response = await toolbarFetch(
`/api/projects/@current/feature_flags/my_flags${encodeParams(params, '?')}`
)
if (response.status >= 400) {
- toolbarLogic.actions.tokenExpired()
+ toolbarConfigLogic.actions.tokenExpired()
return []
}
@@ -98,23 +99,23 @@ export const featureFlagsLogic = kea([
}),
listeners(({ actions, values }) => ({
checkLocalOverrides: () => {
- const { posthog: clientPostHog } = toolbarLogic.values
+ const clientPostHog = values.posthog
if (clientPostHog) {
const locallyOverrideFeatureFlags = clientPostHog.get_property('$override_feature_flags') || {}
actions.storeLocalOverrides(locallyOverrideFeatureFlags)
}
},
setOverriddenUserFlag: ({ flagKey, overrideValue }) => {
- const { posthog: clientPostHog } = toolbarLogic.values
+ const clientPostHog = values.posthog
if (clientPostHog) {
clientPostHog.featureFlags.override({ ...values.localOverrides, [flagKey]: overrideValue })
- posthog.capture('toolbar feature flag overridden')
+ posthogJS.capture('toolbar feature flag overridden')
actions.checkLocalOverrides()
- toolbarLogic.values.posthog?.featureFlags.reloadFeatureFlags()
+ clientPostHog.featureFlags.reloadFeatureFlags()
}
},
deleteOverriddenUserFlag: ({ flagKey }) => {
- const { posthog: clientPostHog } = toolbarLogic.values
+ const clientPostHog = values.posthog
if (clientPostHog) {
const updatedFlags = { ...values.localOverrides }
delete updatedFlags[flagKey]
@@ -123,9 +124,9 @@ export const featureFlagsLogic = kea([
} else {
clientPostHog.featureFlags.override(false)
}
- posthog.capture('toolbar feature flag override removed')
+ posthogJS.capture('toolbar feature flag override removed')
actions.checkLocalOverrides()
- toolbarLogic.values.posthog?.featureFlags.reloadFeatureFlags()
+ clientPostHog.featureFlags.reloadFeatureFlags()
}
},
})),
diff --git a/frontend/src/toolbar/hedgehog/HedgehogButton.tsx b/frontend/src/toolbar/hedgehog/HedgehogButton.tsx
new file mode 100644
index 0000000000000..7715b3a0fb42e
--- /dev/null
+++ b/frontend/src/toolbar/hedgehog/HedgehogButton.tsx
@@ -0,0 +1,44 @@
+import { useActions, useValues } from 'kea'
+import { HedgehogActor, HedgehogBuddy } from 'lib/components/HedgehogBuddy/HedgehogBuddy'
+import { useEffect, useRef } from 'react'
+
+import { toolbarLogic } from '~/toolbar/bar/toolbarLogic'
+
+import { heatmapLogic } from '../elements/heatmapLogic'
+
+export function HedgehogButton(): JSX.Element {
+ const { hedgehogMode, theme } = useValues(toolbarLogic)
+ const { syncWithHedgehog, setHedgehogActor, toggleMinimized } = useActions(toolbarLogic)
+
+ const { heatmapEnabled } = useValues(heatmapLogic)
+
+ const actorRef = useRef()
+
+ useEffect(() => {
+ if (heatmapEnabled) {
+ actorRef.current?.setAnimation('heatmaps')
+ }
+ }, [heatmapEnabled])
+
+ useEffect(() => {
+ if (actorRef.current) {
+ setHedgehogActor(actorRef.current)
+ }
+ }, [actorRef.current, hedgehogMode])
+
+ return (
+ <>
+ {hedgehogMode && (
+ {}}
+ actorRef={actorRef}
+ isDarkModeOn={theme === 'dark'}
+ onPositionChange={() => {
+ syncWithHedgehog()
+ }}
+ onClick={() => toggleMinimized()}
+ />
+ )}
+ >
+ )
+}
diff --git a/frontend/src/toolbar/hedgehog/HedgehogMenu.tsx b/frontend/src/toolbar/hedgehog/HedgehogMenu.tsx
new file mode 100644
index 0000000000000..23305db09fe85
--- /dev/null
+++ b/frontend/src/toolbar/hedgehog/HedgehogMenu.tsx
@@ -0,0 +1,33 @@
+import { LemonButton } from '@posthog/lemon-ui'
+import { useActions, useValues } from 'kea'
+import { HedgehogAccessories } from 'lib/components/HedgehogBuddy/HedgehogAccessories'
+
+import { ToolbarMenu } from '~/toolbar/bar/ToolbarMenu'
+
+import { toolbarLogic } from '../bar/toolbarLogic'
+
+export const HedgehogMenu = (): JSX.Element => {
+ const { theme } = useValues(toolbarLogic)
+ const { setHedgehogMode, setVisibleMenu } = useActions(toolbarLogic)
+
+ return (
+
+
+
+
+
+
+
+
+
+ setHedgehogMode(false)}>
+ Go away...
+
+ setVisibleMenu('none')}>
+ Carry on!
+
+
+
+
+ )
+}
diff --git a/frontend/src/toolbar/stats/HeatmapStats.tsx b/frontend/src/toolbar/stats/HeatmapToolbarMenu.tsx
similarity index 51%
rename from frontend/src/toolbar/stats/HeatmapStats.tsx
rename to frontend/src/toolbar/stats/HeatmapToolbarMenu.tsx
index 556c0850ecd62..408547773e33e 100644
--- a/frontend/src/toolbar/stats/HeatmapStats.tsx
+++ b/frontend/src/toolbar/stats/HeatmapToolbarMenu.tsx
@@ -2,40 +2,33 @@ import { useActions, useValues } from 'kea'
import { DateFilter } from 'lib/components/DateFilter/DateFilter'
import { IconSync } from 'lib/lemon-ui/icons'
import { LemonButton } from 'lib/lemon-ui/LemonButton'
-import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput'
-import { LemonSwitch } from 'lib/lemon-ui/LemonSwitch/LemonSwitch'
-import { Spinner } from 'lib/lemon-ui/Spinner/Spinner'
+import { LemonInput } from 'lib/lemon-ui/LemonInput'
+import { LemonSwitch } from 'lib/lemon-ui/LemonSwitch'
+import { Spinner } from 'lib/lemon-ui/Spinner'
import { Tooltip } from 'lib/lemon-ui/Tooltip'
+import { ToolbarMenu } from '~/toolbar/bar/ToolbarMenu'
import { elementsLogic } from '~/toolbar/elements/elementsLogic'
import { heatmapLogic } from '~/toolbar/elements/heatmapLogic'
import { currentPageLogic } from '~/toolbar/stats/currentPageLogic'
import { getShadowRootPopoverContainer } from '~/toolbar/utils'
-export function HeatmapStats(): JSX.Element {
- const {
- matchLinksByHref,
- countedElements,
- clickCount,
- heatmapEnabled,
- heatmapLoading,
- heatmapFilter,
- canLoadMoreElementStats,
- } = useValues(heatmapLogic)
- const { setHeatmapFilter, loadMoreElementStats, setMatchLinksByHref } = useActions(heatmapLogic)
- const { setHighlightElement, setSelectedElement } = useActions(elementsLogic)
+export const HeatmapToolbarMenu = (): JSX.Element => {
const { wildcardHref } = useValues(currentPageLogic)
const { setWildcardHref } = useActions(currentPageLogic)
+ const { matchLinksByHref, countedElements, clickCount, heatmapLoading, heatmapFilter, canLoadMoreElementStats } =
+ useValues(heatmapLogic)
+ const { setHeatmapFilter, loadMoreElementStats, setMatchLinksByHref } = useActions(heatmapLogic)
+ const { setHighlightElement, setSelectedElement } = useActions(elementsLogic)
+
return (
-
- {heatmapEnabled ? (
-
-
-
-
Use * as a wildcard
-
-
+
+
+
+
+
Use * as a wildcard
+
- {heatmapLoading ? : null}
-
-
- Found: {countedElements.length} elements / {clickCount} clicks!
-
-
}
type={'secondary'}
@@ -62,6 +49,11 @@ export function HeatmapStats(): JSX.Element {
>
Load more
+
+ {heatmapLoading ? : null}
+
+
+ Found: {countedElements.length} elements / {clickCount} clicks!
-
- {countedElements.map(({ element, count, actionStep }, index) => {
- return (
-
setSelectedElement(element)}
- onMouseEnter={() => setHighlightElement(element)}
- onMouseLeave={() => setHighlightElement(null)}
- >
-
- {index + 1}.
- {actionStep?.text ||
- (actionStep?.tag_name ? (
- <{actionStep.tag_name}>
- ) : (
- Element
- ))}
+
+
+
+
+
+ {heatmapLoading ? (
+
+
+
+ ) : countedElements.length ? (
+ countedElements.map(({ element, count, actionStep }, index) => {
+ return (
+
setSelectedElement(element)}
+ onMouseEnter={() => setHighlightElement(element)}
+ onMouseLeave={() => setHighlightElement(null)}
+ >
+
+ {index + 1}.
+ {actionStep?.text ||
+ (actionStep?.tag_name ? (
+ <{actionStep.tag_name}>
+ ) : (
+ Element
+ ))}
+
+
{count} clicks
-
{count} clicks
-
- )
- })}
+ )
+ })
+ ) : (
+
No elements found.
+ )}
- ) : null}
-
+
+
)
}
diff --git a/frontend/src/toolbar/stats/currentPageLogic.ts b/frontend/src/toolbar/stats/currentPageLogic.ts
index d637a6a488af3..e867653432c52 100644
--- a/frontend/src/toolbar/stats/currentPageLogic.ts
+++ b/frontend/src/toolbar/stats/currentPageLogic.ts
@@ -1,4 +1,4 @@
-import { actions, events, kea, path, reducers } from 'kea'
+import { actions, afterMount, beforeUnmount, kea, path, reducers } from 'kea'
import type { currentPageLogicType } from './currentPageLogicType'
@@ -15,23 +15,16 @@ export const currentPageLogic = kea
([
{ setHref: (_, { href }) => href, setWildcardHref: (_, { href }) => href },
],
})),
- events(({ actions, cache, values }) => ({
- afterMount: () => {
- cache.interval = window.setInterval(() => {
- if (window.location.href !== values.href) {
- actions.setHref(window.location.href)
- }
- }, 500)
- cache.location = () => {
- window.requestAnimationFrame(() => {
- actions.setHref(window.location.href)
- })
+
+ afterMount(({ actions, values, cache }) => {
+ cache.interval = window.setInterval(() => {
+ if (window.location.href !== values.href) {
+ actions.setHref(window.location.href)
}
- window.addEventListener('popstate', cache.location)
- },
- beforeUnmount: () => {
- window.clearInterval(cache.interval)
- window.removeEventListener('popstate', cache.location)
- },
- })),
+ }, 500)
+ }),
+
+ beforeUnmount(({ cache }) => {
+ window.clearInterval(cache.interval)
+ }),
])
diff --git a/frontend/src/toolbar/styles.scss b/frontend/src/toolbar/styles.scss
index 62c100a701ee8..3b1787b06b67f 100644
--- a/frontend/src/toolbar/styles.scss
+++ b/frontend/src/toolbar/styles.scss
@@ -17,16 +17,6 @@
@include root-variables;
}
-.toolbar-block {
- font-size: 14px;
- color: black;
- background: white;
-
- &.no-padding {
- padding: 0;
- }
-}
-
.LemonModal__overlay--force-modal-above-popovers {
z-index: 2147483033 !important;
}
diff --git a/frontend/src/toolbar/toolbarLogic.test.ts b/frontend/src/toolbar/toolbarConfigLogic.test.ts
similarity index 71%
rename from frontend/src/toolbar/toolbarLogic.test.ts
rename to frontend/src/toolbar/toolbarConfigLogic.test.ts
index 0d4354c3441f6..58fa88af5a626 100644
--- a/frontend/src/toolbar/toolbarLogic.test.ts
+++ b/frontend/src/toolbar/toolbarConfigLogic.test.ts
@@ -1,7 +1,7 @@
import { expectLogic } from 'kea-test-utils'
import { initKeaTests } from '~/test/init'
-import { toolbarLogic } from '~/toolbar/toolbarLogic'
+import { toolbarConfigLogic } from '~/toolbar/toolbarConfigLogic'
global.fetch = jest.fn(() =>
Promise.resolve({
@@ -12,11 +12,11 @@ global.fetch = jest.fn(() =>
)
describe('toolbar toolbarLogic', () => {
- let logic: ReturnType
+ let logic: ReturnType
beforeEach(() => {
initKeaTests()
- logic = toolbarLogic({ apiURL: 'http://localhost' })
+ logic = toolbarConfigLogic({ apiURL: 'http://localhost' })
logic.mount()
})
diff --git a/frontend/src/toolbar/toolbarLogic.ts b/frontend/src/toolbar/toolbarConfigLogic.ts
similarity index 55%
rename from frontend/src/toolbar/toolbarLogic.ts
rename to frontend/src/toolbar/toolbarConfigLogic.ts
index 1394e1d1d05f2..e45a2c7b8ee1b 100644
--- a/frontend/src/toolbar/toolbarLogic.ts
+++ b/frontend/src/toolbar/toolbarConfigLogic.ts
@@ -1,23 +1,21 @@
import { actions, afterMount, kea, listeners, path, props, reducers, selectors } from 'kea'
+import { combineUrl, encodeParams } from 'kea-router'
import { lemonToast } from 'lib/lemon-ui/lemonToast'
-import { actionsTabLogic } from '~/toolbar/actions/actionsTabLogic'
-import { toolbarButtonLogic } from '~/toolbar/button/toolbarButtonLogic'
import { posthog } from '~/toolbar/posthog'
-import { clearSessionToolbarToken } from '~/toolbar/utils'
import { ToolbarProps } from '~/types'
-import type { toolbarLogicType } from './toolbarLogicType'
+import type { toolbarConfigLogicType } from './toolbarConfigLogicType'
+import { clearSessionToolbarToken } from './utils'
-export const toolbarLogic = kea([
- path(['toolbar', 'toolbarLogic']),
+export const toolbarConfigLogic = kea([
+ path(['toolbar', 'toolbarConfigLogic']),
props({} as ToolbarProps),
actions({
authenticate: true,
logout: true,
tokenExpired: true,
- processUserIntent: true,
clearUserIntent: true,
showButton: true,
hideButton: true,
@@ -45,7 +43,7 @@ export const toolbarLogic = kea([
isAuthenticated: [(s) => [s.temporaryToken], (temporaryToken) => !!temporaryToken],
}),
- listeners(({ values, props }) => ({
+ listeners(({ values }) => ({
authenticate: () => {
posthog.capture('toolbar authenticate', { is_authenticated: values.isAuthenticated })
const encodedUrl = encodeURIComponent(window.location.href)
@@ -64,16 +62,9 @@ export const toolbarLogic = kea([
}
clearSessionToolbarToken()
},
- processUserIntent: () => {
- if (props.userIntent === 'add-action' || props.userIntent === 'edit-action') {
- actionsTabLogic.actions.showButtonActions()
- toolbarButtonLogic.actions.showActionsInfo()
- // the right view will next be opened in `actionsTabLogic` on `getActionsSuccess`
- }
- },
})),
- afterMount(({ props, actions, values }) => {
+ afterMount(({ props, values }) => {
if (props.instrument) {
const distinctId = props.distinctId
if (distinctId) {
@@ -81,9 +72,57 @@ export const toolbarLogic = kea([
}
posthog.optIn()
}
- if (props.userIntent) {
- actions.processUserIntent()
- }
posthog.capture('toolbar loaded', { is_authenticated: values.isAuthenticated })
}),
])
+
+export async function toolbarFetch(
+ url: string,
+ method: string = 'GET',
+ payload?: Record,
+ /*
+ allows caller to control how the provided URL is altered before use
+ if "full" then the payload and URL are taken apart and reconstructed
+ if "only-add-token" the URL is unchanged, the payload is not used
+ but the temporary token is added to the URL
+ if "use-as-provided" then the URL is used as-is, and the payload is not used
+ this is because the heatmapLogic needs more control over how the query parameters are constructed
+ */
+ urlConstruction: 'full' | 'only-add-token' | 'use-as-provided' = 'full'
+): Promise {
+ const temporaryToken = toolbarConfigLogic.findMounted()?.values.temporaryToken
+ const apiURL = toolbarConfigLogic.findMounted()?.values.apiURL
+
+ let fullUrl: string
+ if (urlConstruction === 'use-as-provided') {
+ fullUrl = url
+ } else if (urlConstruction === 'only-add-token') {
+ fullUrl = `${url}&temporary_token=${temporaryToken}`
+ } else {
+ const { pathname, searchParams } = combineUrl(url)
+ const params = { ...searchParams, temporary_token: temporaryToken }
+ fullUrl = `${apiURL}${pathname}${encodeParams(params, '?')}`
+ }
+
+ const payloadData = payload
+ ? {
+ body: JSON.stringify(payload),
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ }
+ : {}
+
+ const response = await fetch(fullUrl, {
+ method,
+ ...payloadData,
+ })
+ if (response.status === 403) {
+ const responseData = await response.json()
+ // Do not try to authenticate if the user has no project access altogether
+ if (responseData.detail !== "You don't have access to the project.") {
+ toolbarConfigLogic.actions.authenticate()
+ }
+ }
+ return response
+}
diff --git a/frontend/src/toolbar/utils.ts b/frontend/src/toolbar/utils.ts
index 90b37dde6431f..98f411a904d9b 100644
--- a/frontend/src/toolbar/utils.ts
+++ b/frontend/src/toolbar/utils.ts
@@ -1,11 +1,9 @@
import { finder } from '@medv/finder'
-import { combineUrl, encodeParams } from 'kea-router'
import { CLICK_TARGET_SELECTOR, CLICK_TARGETS, escapeRegex, TAGS_TO_IGNORE } from 'lib/actionUtils'
import { cssEscape } from 'lib/utils/cssEscape'
import { querySelectorAllDeep } from 'query-selector-shadow-dom'
import wildcardMatch from 'wildcard-match'
-import { toolbarLogic } from '~/toolbar/toolbarLogic'
import { ActionStepForm, BoxColor, ElementRect } from '~/toolbar/types'
import { ActionStepType, StringMatching } from '~/types'
@@ -77,6 +75,10 @@ export function getShadowRoot(): ShadowRoot | null {
return getToolbarElement()?.shadowRoot || null
}
+export function getToolbarContainer(): HTMLElement {
+ return getShadowRoot()?.getElementById('button-toolbar') as unknown as HTMLElement
+}
+
export function getShadowRootPopoverContainer(): HTMLElement {
return getShadowRoot() as unknown as HTMLElement
}
@@ -413,51 +415,3 @@ export function getHeatMapHue(count: number, maxCount: number): number {
}
return 60 - (count / maxCount) * 40
}
-
-export async function toolbarFetch(
- url: string,
- method: string = 'GET',
- payload?: Record,
- /*
- allows caller to control how the provided URL is altered before use
- if "full" then the payload and URL are taken apart and reconstructed
- if "only-add-token" the URL is unchanged, the payload is not used
- but the temporary token is added to the URL
- if "use-as-provided" then the URL is used as-is, and the payload is not used
- this is because the heatmapLogic needs more control over how the query parameters are constructed
- */
- urlConstruction: 'full' | 'only-add-token' | 'use-as-provided' = 'full'
-): Promise {
- let fullUrl: string
- if (urlConstruction === 'use-as-provided') {
- fullUrl = url
- } else if (urlConstruction === 'only-add-token') {
- fullUrl = `${url}&temporary_token=${toolbarLogic.values.temporaryToken}`
- } else {
- const { pathname, searchParams } = combineUrl(url)
- const params = { ...searchParams, temporary_token: toolbarLogic.values.temporaryToken }
- fullUrl = `${toolbarLogic.values.apiURL}${pathname}${encodeParams(params, '?')}`
- }
-
- const payloadData = payload
- ? {
- body: JSON.stringify(payload),
- headers: {
- 'Content-Type': 'application/json',
- },
- }
- : {}
-
- const response = await fetch(fullUrl, {
- method,
- ...payloadData,
- })
- if (response.status === 403) {
- const responseData = await response.json()
- // Do not try to authenticate if the user has no project access altogether
- if (responseData.detail !== "You don't have access to the project.") {
- toolbarLogic.actions.authenticate()
- }
- }
- return response
-}
diff --git a/frontend/src/types.ts b/frontend/src/types.ts
index bf9bba96c3194..5e027807a2bcf 100644
--- a/frontend/src/types.ts
+++ b/frontend/src/types.ts
@@ -2242,6 +2242,7 @@ export interface RatingSurveyQuestion extends SurveyQuestionBase {
export interface MultipleSurveyQuestion extends SurveyQuestionBase {
type: SurveyQuestionType.SingleChoice | SurveyQuestionType.MultipleChoice
choices: string[]
+ hasOpenChoice?: boolean
}
export type SurveyQuestion = BasicSurveyQuestion | LinkSurveyQuestion | RatingSurveyQuestion | MultipleSurveyQuestion
diff --git a/jest.config.ts b/jest.config.ts
index 54aa39875ab00..3ff4eeff1d470 100644
--- a/jest.config.ts
+++ b/jest.config.ts
@@ -89,6 +89,7 @@ const config: Config = {
'^~/(.*)$': '/$1',
'^@posthog/lemon-ui(|/.*)$': '/../@posthog/lemon-ui/src/$1',
'^@posthog/apps-common(|/.*)$': '/../@posthog/apps-common/src/$1',
+ '^@posthog/ee/exports': ['/../../ee/frontend/exports', '/../@posthog/ee/exports'],
'^lib/(.*)$': '/lib/$1',
'^scenes/(.*)$': '/scenes/$1',
'^antd/es/(.*)$': 'antd/lib/$1',
diff --git a/package.json b/package.json
index 3898842984287..8b457c94afe4b 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,7 @@
"engines": {
"node": ">=18 <19"
},
- "packageManager": "pnpm@8.6.0",
+ "packageManager": "pnpm@8.10.5",
"scripts": {
"copy-scripts": "mkdir -p frontend/dist/ && ./bin/copy-posthog-js",
"test": "pnpm test:unit && pnpm test:visual-regression",
@@ -76,7 +76,7 @@
"@medv/finder": "^2.1.0",
"@microlink/react-json-view": "^1.21.3",
"@monaco-editor/react": "4.4.6",
- "@posthog/icons": "0.4.10",
+ "@posthog/icons": "0.4.11",
"@posthog/plugin-scaffold": "^1.4.4",
"@react-hook/size": "^2.1.2",
"@rrweb/types": "^2.0.0-alpha.11",
@@ -136,7 +136,7 @@
"monaco-editor": "^0.39.0",
"papaparse": "^5.4.1",
"pmtiles": "^2.11.0",
- "posthog-js": "1.92.1",
+ "posthog-js": "1.93.1",
"posthog-js-lite": "2.0.0-alpha5",
"prettier": "^2.8.8",
"prop-types": "^15.7.2",
@@ -182,7 +182,7 @@
"@babel/preset-typescript": "^7.22.5",
"@babel/runtime": "^7.22.10",
"@cypress/webpack-preprocessor": "^5.17.1",
- "@playwright/test": "1.29.2",
+ "@playwright/test": "1.32.2",
"@sentry/types": "7.22.0",
"@storybook/addon-a11y": "^7.5.1",
"@storybook/addon-actions": "^7.5.1",
@@ -197,7 +197,7 @@
"@storybook/csf": "^0.1.1",
"@storybook/react": "^7.5.1",
"@storybook/react-webpack5": "^7.5.1",
- "@storybook/test-runner": "^0.13.0",
+ "@storybook/test-runner": "^0.15.2",
"@storybook/theming": "^7.5.1",
"@storybook/types": "^7.5.1",
"@sucrase/jest-plugin": "^3.0.0",
@@ -271,7 +271,6 @@
"msw": "^0.49.0",
"path-browserify": "^1.0.1",
"pixelmatch": "^5.3.0",
- "playwright-core": "1.29.2",
"pngjs": "^6.0.0",
"postcss": "^8.4.31",
"postcss-loader": "^4.3.0",
@@ -297,6 +296,11 @@
"optionalDependencies": {
"fsevents": "^2.3.2"
},
+ "pnpm": {
+ "overrides": {
+ "playwright": "1.32.2"
+ }
+ },
"lint-staged": {
"*.{json,yaml,yml}": "prettier --write",
"*.{css,scss}": [
diff --git a/playwright.config.ts b/playwright.config.ts
index 27462f7c034e3..a31aa1543f192 100644
--- a/playwright.config.ts
+++ b/playwright.config.ts
@@ -1,5 +1,4 @@
-import type { PlaywrightTestConfig } from '@playwright/test'
-import { devices } from '@playwright/test'
+import { defineConfig, devices } from '@playwright/test'
/**
* Read environment variables from file.
@@ -10,7 +9,7 @@ import { devices } from '@playwright/test'
/**
* See https://playwright.dev/docs/test-configuration.
*/
-const config: PlaywrightTestConfig = {
+export default defineConfig({
testDir: './playwright',
/* Maximum time one test can run for. */
timeout: 30 * 1000,
@@ -102,6 +101,4 @@ const config: PlaywrightTestConfig = {
// command: 'npm run start',
// port: 3000,
// },
-}
-
-export default config
+})
diff --git a/plugin-server/bin/ci_functional_tests.sh b/plugin-server/bin/ci_functional_tests.sh
index 9eff572c71251..9014a4a249a57 100755
--- a/plugin-server/bin/ci_functional_tests.sh
+++ b/plugin-server/bin/ci_functional_tests.sh
@@ -16,6 +16,7 @@ export CONVERSION_BUFFER_ENABLED=true
export BUFFER_CONVERSION_SECONDS=2 # Make sure we don't have to wait for the default 60 seconds
export KAFKA_MAX_MESSAGE_BATCH_SIZE=0
export APP_METRICS_GATHERED_FOR_ALL=true
+export PLUGINS_DEFAULT_LOG_LEVEL=0 # All logs, as debug logs are used in synchronization barriers
export NODE_ENV=production-functional-tests
# Not important at all, but I like to see nice red/green for tests
diff --git a/plugin-server/src/config/config.ts b/plugin-server/src/config/config.ts
index 5d08afb6428fe..bcfbc606b51c6 100644
--- a/plugin-server/src/config/config.ts
+++ b/plugin-server/src/config/config.ts
@@ -1,4 +1,4 @@
-import { LogLevel, PluginsServerConfig, stringToPluginServerMode, ValueMatcher } from '../types'
+import { LogLevel, PluginLogLevel, PluginsServerConfig, stringToPluginServerMode, ValueMatcher } from '../types'
import { isDevEnv, isTestEnv, stringToBoolean } from '../utils/env-utils'
import { KAFKAJS_LOG_LEVEL_MAPPING } from './constants'
import {
@@ -36,6 +36,7 @@ export function getDefaultConfig(): PluginsServerConfig {
CLICKHOUSE_DISABLE_EXTERNAL_SCHEMAS: true,
EVENT_OVERFLOW_BUCKET_CAPACITY: 1000,
EVENT_OVERFLOW_BUCKET_REPLENISH_RATE: 1.0,
+ SKIP_UPDATE_EVENT_AND_PROPERTIES_STEP: false,
KAFKA_HOSTS: 'kafka:9092', // KEEP IN SYNC WITH posthog/settings/data_stores.py
KAFKA_CLIENT_CERT_B64: undefined,
KAFKA_CLIENT_CERT_KEY_B64: undefined,
@@ -72,6 +73,7 @@ export function getDefaultConfig(): PluginsServerConfig {
TASKS_PER_WORKER: 10,
INGESTION_CONCURRENCY: 10,
INGESTION_BATCH_SIZE: 500,
+ PLUGINS_DEFAULT_LOG_LEVEL: isTestEnv() ? PluginLogLevel.Full : PluginLogLevel.Log,
LOG_LEVEL: isTestEnv() ? LogLevel.Warn : LogLevel.Info,
SENTRY_DSN: null,
SENTRY_PLUGIN_SERVER_TRACING_SAMPLE_RATE: 0,
@@ -131,6 +133,7 @@ export function getDefaultConfig(): PluginsServerConfig {
CLOUD_DEPLOYMENT: null,
EXTERNAL_REQUEST_TIMEOUT_MS: 10 * 1000, // 10 seconds
DROP_EVENTS_BY_TOKEN_DISTINCT_ID: '',
+ DROP_EVENTS_BY_TOKEN: '',
POE_EMBRACE_JOIN_FOR_TEAMS: '',
RELOAD_PLUGIN_JITTER_MAX_MS: 60000,
@@ -244,3 +247,18 @@ export function buildIntegerMatcher(config: string | undefined, allowStar: boole
}
}
}
+
+export function buildStringMatcher(config: string | undefined, allowStar: boolean): ValueMatcher {
+ // Builds a ValueMatcher on a comma-separated list of values.
+ // Optionally, supports a '*' value to match everything
+ if (!config || config.trim().length == 0) {
+ return () => false
+ } else if (allowStar && config === '*') {
+ return () => true
+ } else {
+ const values = new Set(config.split(','))
+ return (v: string) => {
+ return values.has(v)
+ }
+ }
+}
diff --git a/plugin-server/src/main/ingestion-queues/analytics-events-ingestion-consumer.ts b/plugin-server/src/main/ingestion-queues/analytics-events-ingestion-consumer.ts
index e8c8e7b9516b3..2b9c4ce77152d 100644
--- a/plugin-server/src/main/ingestion-queues/analytics-events-ingestion-consumer.ts
+++ b/plugin-server/src/main/ingestion-queues/analytics-events-ingestion-consumer.ts
@@ -1,6 +1,7 @@
import { Message } from 'node-rdkafka'
import { Counter } from 'prom-client'
+import { buildStringMatcher } from '../../config/config'
import { KAFKA_EVENTS_PLUGIN_INGESTION, prefix as KAFKA_PREFIX } from '../../config/kafka-topics'
import { Hub } from '../../types'
import { isIngestionOverflowEnabled } from '../../utils/env-utils'
@@ -47,8 +48,10 @@ export const startAnalyticsEventsIngestionConsumer = async ({
// enabling re-production of events to the OVERFLOW topic.
const overflowMode = isIngestionOverflowEnabled() ? IngestionOverflowMode.Reroute : IngestionOverflowMode.Disabled
+
+ const tokenBlockList = buildStringMatcher(hub.DROP_EVENTS_BY_TOKEN, false)
const batchHandler = async (messages: Message[], queue: IngestionConsumer): Promise => {
- await eachBatchParallelIngestion(messages, queue, overflowMode)
+ await eachBatchParallelIngestion(tokenBlockList, messages, queue, overflowMode)
}
const queue = new IngestionConsumer(
diff --git a/plugin-server/src/main/ingestion-queues/analytics-events-ingestion-historical-consumer.ts b/plugin-server/src/main/ingestion-queues/analytics-events-ingestion-historical-consumer.ts
index f7331fb617160..92139751e9760 100644
--- a/plugin-server/src/main/ingestion-queues/analytics-events-ingestion-historical-consumer.ts
+++ b/plugin-server/src/main/ingestion-queues/analytics-events-ingestion-historical-consumer.ts
@@ -1,5 +1,6 @@
import { Message } from 'node-rdkafka'
+import { buildStringMatcher } from '../../config/config'
import { KAFKA_EVENTS_PLUGIN_INGESTION_HISTORICAL, prefix as KAFKA_PREFIX } from '../../config/kafka-topics'
import { Hub } from '../../types'
import { status } from '../../utils/status'
@@ -24,8 +25,9 @@ export const startAnalyticsEventsIngestionHistoricalConsumer = async ({
We don't want to move events to overflow from here, it's fine for the processing to
take longer, but we want the locality constraints to be respected like normal ingestion.
*/
+ const tokenBlockList = buildStringMatcher(hub.DROP_EVENTS_BY_TOKEN, false)
const batchHandler = async (messages: Message[], queue: IngestionConsumer): Promise => {
- await eachBatchParallelIngestion(messages, queue, IngestionOverflowMode.Disabled)
+ await eachBatchParallelIngestion(tokenBlockList, messages, queue, IngestionOverflowMode.Disabled)
}
const queue = new IngestionConsumer(
diff --git a/plugin-server/src/main/ingestion-queues/analytics-events-ingestion-overflow-consumer.ts b/plugin-server/src/main/ingestion-queues/analytics-events-ingestion-overflow-consumer.ts
index a77dbff571dfb..b715aeda2def5 100644
--- a/plugin-server/src/main/ingestion-queues/analytics-events-ingestion-overflow-consumer.ts
+++ b/plugin-server/src/main/ingestion-queues/analytics-events-ingestion-overflow-consumer.ts
@@ -1,5 +1,6 @@
import { Message } from 'node-rdkafka'
+import { buildStringMatcher } from '../../config/config'
import { KAFKA_EVENTS_PLUGIN_INGESTION_OVERFLOW, prefix as KAFKA_PREFIX } from '../../config/kafka-topics'
import { Hub } from '../../types'
import { status } from '../../utils/status'
@@ -29,9 +30,9 @@ export const startAnalyticsEventsIngestionOverflowConsumer = async ({
// workloads ran on the same process they would share the same consumer
// group id. In these cases, updating to this version will result in the
// re-exporting of events still in Kafka `clickhouse_events_json` topic.
-
+ const tokenBlockList = buildStringMatcher(hub.DROP_EVENTS_BY_TOKEN, false)
const batchHandler = async (messages: Message[], queue: IngestionConsumer): Promise => {
- await eachBatchParallelIngestion(messages, queue, IngestionOverflowMode.Consume)
+ await eachBatchParallelIngestion(tokenBlockList, messages, queue, IngestionOverflowMode.Consume)
}
const queue = new IngestionConsumer(
diff --git a/plugin-server/src/main/ingestion-queues/batch-processing/each-batch-ingestion.ts b/plugin-server/src/main/ingestion-queues/batch-processing/each-batch-ingestion.ts
index 5f8777b3fe237..d50042606b24f 100644
--- a/plugin-server/src/main/ingestion-queues/batch-processing/each-batch-ingestion.ts
+++ b/plugin-server/src/main/ingestion-queues/batch-processing/each-batch-ingestion.ts
@@ -2,7 +2,7 @@ import * as Sentry from '@sentry/node'
import { Message, MessageHeader } from 'node-rdkafka'
import { KAFKA_EVENTS_PLUGIN_INGESTION_DLQ, KAFKA_EVENTS_PLUGIN_INGESTION_OVERFLOW } from '../../../config/kafka-topics'
-import { Hub, PipelineEvent } from '../../../types'
+import { Hub, PipelineEvent, ValueMatcher } from '../../../types'
import { formPipelineEvent } from '../../../utils/event'
import { retryIfRetriable } from '../../../utils/retries'
import { status } from '../../../utils/status'
@@ -11,7 +11,7 @@ import { EventPipelineResult, runEventPipeline } from '../../../worker/ingestion
import { captureIngestionWarning } from '../../../worker/ingestion/utils'
import { ingestionPartitionKeyOverflowed } from '../analytics-events-ingestion-consumer'
import { IngestionConsumer } from '../kafka-queue'
-import { latestOffsetTimestampGauge } from '../metrics'
+import { eventDroppedCounter, latestOffsetTimestampGauge } from '../metrics'
import {
ingestionOverflowingMessagesTotal,
ingestionParallelism,
@@ -95,6 +95,7 @@ async function handleProcessingError(
}
export async function eachBatchParallelIngestion(
+ tokenBlockList: ValueMatcher,
messages: Message[],
queue: IngestionConsumer,
overflowMode: IngestionOverflowMode
@@ -112,7 +113,7 @@ export async function eachBatchParallelIngestion(
* and a separate array for single messages, but let's look at profiles before optimizing.
*/
const prepareSpan = transaction.startChild({ op: 'prepareBatch' })
- const splitBatch = splitIngestionBatch(messages, overflowMode)
+ const splitBatch = splitIngestionBatch(tokenBlockList, messages, overflowMode)
splitBatch.toProcess.sort((a, b) => a.length - b.length)
queue.pluginsServer.statsd?.histogram('ingest_event_batching.input_length', messages.length, {
@@ -280,6 +281,7 @@ async function emitToOverflow(queue: IngestionConsumer, kafkaMessages: Message[]
}
export function splitIngestionBatch(
+ tokenBlockList: ValueMatcher,
kafkaMessages: Message[],
overflowMode: IngestionOverflowMode
): IngestionSplitBatch {
@@ -300,7 +302,20 @@ export function splitIngestionBatch(
* so we just return batches of one to increase concurrency.
* TODO: add a PipelineEvent[] field to IngestionSplitBatch for batches of 1
*/
- output.toProcess = kafkaMessages.map((m) => new Array({ message: m, pluginEvent: formPipelineEvent(m) }))
+ for (const message of kafkaMessages) {
+ // Drop based on a token blocklist
+ const pluginEvent = formPipelineEvent(message)
+ if (pluginEvent.token && tokenBlockList(pluginEvent.token)) {
+ eventDroppedCounter
+ .labels({
+ event_type: 'analytics',
+ drop_cause: 'blocked_token',
+ })
+ .inc()
+ continue
+ }
+ output.toProcess.push(new Array({ message: message, pluginEvent }))
+ }
return output
}
@@ -308,10 +323,23 @@ export function splitIngestionBatch(
for (const message of kafkaMessages) {
if (overflowMode === IngestionOverflowMode.Reroute && message.key == null) {
// Overflow detected by capture, reroute to overflow topic
+ // Not applying tokenBlockList to save CPU. TODO: do so once token is in the message headers
output.toOverflow.push(message)
continue
}
const pluginEvent = formPipelineEvent(message)
+
+ // Drop based on a token blocklist
+ if (pluginEvent.token && tokenBlockList(pluginEvent.token)) {
+ eventDroppedCounter
+ .labels({
+ event_type: 'analytics',
+ drop_cause: 'blocked_token',
+ })
+ .inc()
+ continue
+ }
+
const eventKey = computeKey(pluginEvent)
if (
overflowMode === IngestionOverflowMode.Reroute &&
diff --git a/plugin-server/src/types.ts b/plugin-server/src/types.ts
index 700bf4f6cef89..b0b7535954875 100644
--- a/plugin-server/src/types.ts
+++ b/plugin-server/src/types.ts
@@ -146,6 +146,7 @@ export interface PluginsServerConfig {
APP_METRICS_FLUSH_MAX_QUEUE_SIZE: number
BASE_DIR: string // base path for resolving local plugins
PLUGINS_RELOAD_PUBSUB_CHANNEL: string // Redis channel for reload events'
+ PLUGINS_DEFAULT_LOG_LEVEL: PluginLogLevel
LOG_LEVEL: LogLevel
SENTRY_DSN: string | null
SENTRY_PLUGIN_SERVER_TRACING_SAMPLE_RATE: number // Rate of tracing in plugin server (between 0 and 1)
@@ -204,8 +205,10 @@ export interface PluginsServerConfig {
CLOUD_DEPLOYMENT: string | null
EXTERNAL_REQUEST_TIMEOUT_MS: number
DROP_EVENTS_BY_TOKEN_DISTINCT_ID: string
+ DROP_EVENTS_BY_TOKEN: string
POE_EMBRACE_JOIN_FOR_TEAMS: string
RELOAD_PLUGIN_JITTER_MAX_MS: number
+ SKIP_UPDATE_EVENT_AND_PROPERTIES_STEP: boolean
// dump profiles to disk, covering the first N seconds of runtime
STARTUP_PROFILE_DURATION_SECONDS: number
@@ -451,9 +454,10 @@ export enum PluginLogEntryType {
export enum PluginLogLevel {
Full = 0, // all logs
- Debug = 1, // all except log
- Warn = 2, // all except log and info
- Critical = 3, // only error type and system source
+ Log = 1, // all except debug
+ Info = 2, // all expect log and debug
+ Warn = 3, // all except log, debug and info
+ Critical = 4, // only error type and system source
}
export interface PluginLogEntry {
diff --git a/plugin-server/src/utils/db/db.ts b/plugin-server/src/utils/db/db.ts
index cf64f86b600a2..2cb417c7f5a6f 100644
--- a/plugin-server/src/utils/db/db.ts
+++ b/plugin-server/src/utils/db/db.ts
@@ -158,8 +158,8 @@ export class DB {
/** How many unique group types to allow per team */
MAX_GROUP_TYPES_PER_TEAM = 5
- /** Whether to write to clickhouse_person_unique_id topic */
- writeToPersonUniqueId?: boolean
+ /** Default log level for plugins that don't specify it */
+ pluginsDefaultLogLevel: PluginLogLevel
/** How many seconds to keep person info in Redis cache */
PERSONS_AND_GROUPS_CACHE_TTL: number
@@ -170,6 +170,7 @@ export class DB {
kafkaProducer: KafkaProducerWrapper,
clickhouse: ClickHouse,
statsd: StatsD | undefined,
+ pluginsDefaultLogLevel: PluginLogLevel,
personAndGroupsCacheTtl = 1
) {
this.postgres = postgres
@@ -177,6 +178,7 @@ export class DB {
this.kafkaProducer = kafkaProducer
this.clickhouse = clickhouse
this.statsd = statsd
+ this.pluginsDefaultLogLevel = pluginsDefaultLogLevel
this.PERSONS_AND_GROUPS_CACHE_TTL = personAndGroupsCacheTtl
}
@@ -1076,10 +1078,9 @@ export class DB {
public async queuePluginLogEntry(entry: LogEntryPayload): Promise {
const { pluginConfig, source, message, type, timestamp, instanceId } = entry
+ const configuredLogLevel = pluginConfig.plugin?.log_level || this.pluginsDefaultLogLevel
- const logLevel = pluginConfig.plugin?.log_level
-
- if (!shouldStoreLog(logLevel || PluginLogLevel.Full, source, type)) {
+ if (!shouldStoreLog(configuredLogLevel, type)) {
return
}
diff --git a/plugin-server/src/utils/db/hub.ts b/plugin-server/src/utils/db/hub.ts
index 2d5c780b7336e..5a462590d7744 100644
--- a/plugin-server/src/utils/db/hub.ts
+++ b/plugin-server/src/utils/db/hub.ts
@@ -137,7 +137,15 @@ export async function createHub(
const promiseManager = new PromiseManager(serverConfig, statsd)
- const db = new DB(postgres, redisPool, kafkaProducer, clickhouse, statsd, serverConfig.PERSON_INFO_CACHE_TTL)
+ const db = new DB(
+ postgres,
+ redisPool,
+ kafkaProducer,
+ clickhouse,
+ statsd,
+ serverConfig.PLUGINS_DEFAULT_LOG_LEVEL,
+ serverConfig.PERSON_INFO_CACHE_TTL
+ )
const teamManager = new TeamManager(postgres, serverConfig, statsd)
const organizationManager = new OrganizationManager(postgres, teamManager)
const pluginsApiKeyManager = new PluginsApiKeyManager(db)
diff --git a/plugin-server/src/utils/db/utils.ts b/plugin-server/src/utils/db/utils.ts
index a4f940defdefb..de37e8c3f5f67 100644
--- a/plugin-server/src/utils/db/utils.ts
+++ b/plugin-server/src/utils/db/utils.ts
@@ -6,10 +6,9 @@ import { Counter } from 'prom-client'
import { defaultConfig } from '../../config/config'
import { KAFKA_PERSON } from '../../config/kafka-topics'
-import { BasePerson, Person, RawPerson, TimestampFormat } from '../../types'
+import { BasePerson, Person, PluginLogEntryType, PluginLogLevel, RawPerson, TimestampFormat } from '../../types'
import { status } from '../../utils/status'
import { castTimestampOrNow } from '../../utils/utils'
-import { PluginLogEntrySource, PluginLogEntryType, PluginLogLevel } from './../../types'
export function unparsePersonPartial(person: Partial): Partial {
return { ...(person as BasePerson), ...(person.created_at ? { created_at: person.created_at.toISO() } : {}) }
@@ -127,24 +126,19 @@ export function getFinalPostgresQuery(queryString: string, values: any[]): strin
return queryString.replace(/\$([0-9]+)/g, (m, v) => JSON.stringify(values[parseInt(v) - 1]))
}
-export function shouldStoreLog(
- pluginLogLevel: PluginLogLevel,
- source: PluginLogEntrySource,
- type: PluginLogEntryType
-): boolean {
- if (source === PluginLogEntrySource.System) {
- return true
+export function shouldStoreLog(pluginLogLevel: PluginLogLevel, type: PluginLogEntryType): boolean {
+ switch (pluginLogLevel) {
+ case PluginLogLevel.Full:
+ return true
+ case PluginLogLevel.Log:
+ return type !== PluginLogEntryType.Debug
+ case PluginLogLevel.Info:
+ return type !== PluginLogEntryType.Log && type !== PluginLogEntryType.Debug
+ case PluginLogLevel.Warn:
+ return type === PluginLogEntryType.Warn || type === PluginLogEntryType.Error
+ case PluginLogLevel.Critical:
+ return type === PluginLogEntryType.Error
}
-
- if (pluginLogLevel === PluginLogLevel.Critical) {
- return type === PluginLogEntryType.Error
- } else if (pluginLogLevel === PluginLogLevel.Warn) {
- return type !== PluginLogEntryType.Log && type !== PluginLogEntryType.Info
- } else if (pluginLogLevel === PluginLogLevel.Debug) {
- return type !== PluginLogEntryType.Log
- }
-
- return true
}
// keep in sync with posthog/posthog/api/utils.py::safe_clickhouse_string
diff --git a/plugin-server/src/worker/ingestion/process-event.ts b/plugin-server/src/worker/ingestion/process-event.ts
index b85f13dd6e53a..6d5b11f1e527b 100644
--- a/plugin-server/src/worker/ingestion/process-event.ts
+++ b/plugin-server/src/worker/ingestion/process-event.ts
@@ -138,16 +138,19 @@ export class EventsProcessor {
delete properties['$ip']
}
- try {
- await this.propertyDefinitionsManager.updateEventNamesAndProperties(team.id, event, properties)
- } catch (err) {
- Sentry.captureException(err, { tags: { team_id: team.id } })
- status.warn('⚠️', 'Failed to update property definitions for an event', {
- event,
- properties,
- err,
- })
+ if (this.pluginsServer.SKIP_UPDATE_EVENT_AND_PROPERTIES_STEP === false) {
+ try {
+ await this.propertyDefinitionsManager.updateEventNamesAndProperties(team.id, event, properties)
+ } catch (err) {
+ Sentry.captureException(err, { tags: { team_id: team.id } })
+ status.warn('⚠️', 'Failed to update property definitions for an event', {
+ event,
+ properties,
+ err,
+ })
+ }
}
+
// Adds group_0 etc values to properties
properties = await addGroupProperties(team.id, properties, this.groupTypeManager)
diff --git a/plugin-server/tests/config.test.ts b/plugin-server/tests/config.test.ts
index c6a83a2875f64..086882a7cf2d2 100644
--- a/plugin-server/tests/config.test.ts
+++ b/plugin-server/tests/config.test.ts
@@ -1,4 +1,4 @@
-import { buildIntegerMatcher, getDefaultConfig, overrideWithEnv } from '../src/config/config'
+import { buildIntegerMatcher, buildStringMatcher, getDefaultConfig, overrideWithEnv } from '../src/config/config'
describe('config', () => {
test('overrideWithEnv 1', () => {
@@ -91,3 +91,30 @@ describe('buildIntegerMatcher', () => {
expect(matcher(5)).toBe(false)
})
})
+
+describe('buildStringMatcher', () => {
+ test('empty input', () => {
+ const matcher = buildStringMatcher('', false)
+ expect(matcher('b')).toBe(false)
+ })
+ test('ignores star star when not allowed', () => {
+ const matcher = buildStringMatcher('*', false)
+ expect(matcher('b')).toBe(false)
+ })
+ test('matches star when allowed', () => {
+ const matcher = buildStringMatcher('*', true)
+ expect(matcher('b')).toBe(true)
+ })
+ test('can match on a single value', () => {
+ const matcher = buildStringMatcher('b', true)
+ expect(matcher('b')).toBe(true)
+ expect(matcher('a')).toBe(false)
+ })
+ test('can match on several values', () => {
+ const matcher = buildStringMatcher('b,c,d', true)
+ expect(matcher('b')).toBe(true)
+ expect(matcher('c')).toBe(true)
+ expect(matcher('d')).toBe(true)
+ expect(matcher('e')).toBe(false)
+ })
+})
diff --git a/plugin-server/tests/main/ingestion-queues/analytics-events-ingestion-consumer.test.ts b/plugin-server/tests/main/ingestion-queues/analytics-events-ingestion-consumer.test.ts
index 462677a9b46f7..2444b4cd624d4 100644
--- a/plugin-server/tests/main/ingestion-queues/analytics-events-ingestion-consumer.test.ts
+++ b/plugin-server/tests/main/ingestion-queues/analytics-events-ingestion-consumer.test.ts
@@ -1,3 +1,4 @@
+import { buildStringMatcher } from '../../../src/config/config'
import { KAFKA_EVENTS_PLUGIN_INGESTION, KAFKA_EVENTS_PLUGIN_INGESTION_OVERFLOW } from '../../../src/config/kafka-topics'
import {
eachBatchParallelIngestion,
@@ -14,7 +15,7 @@ jest.mock('./../../../src/worker/ingestion/event-pipeline/runner', () => ({
runEventPipeline: jest.fn().mockResolvedValue('default value'),
}))
-const captureEndpointEvent = {
+const captureEndpointEvent1 = {
uuid: 'uuid1',
distinct_id: 'id',
ip: null,
@@ -23,7 +24,21 @@ const captureEndpointEvent = {
event: 'event',
properties: {},
}),
- team_id: 1,
+ token: 'mytoken',
+ now: null,
+ sent_at: null,
+}
+
+const captureEndpointEvent2 = {
+ uuid: 'uuid2',
+ distinct_id: 'id',
+ ip: null,
+ site_url: '',
+ data: JSON.stringify({
+ event: 'event',
+ properties: {},
+ }),
+ token: 'othertoken',
now: null,
sent_at: null,
}
@@ -67,24 +82,26 @@ describe('eachBatchParallelIngestion with overflow reroute', () => {
{
partition: 0,
topic: KAFKA_EVENTS_PLUGIN_INGESTION,
- value: JSON.stringify(captureEndpointEvent),
- timestamp: captureEndpointEvent['timestamp'],
- offset: captureEndpointEvent['offset'],
+ value: JSON.stringify(captureEndpointEvent1),
+ timestamp: captureEndpointEvent1['timestamp'],
+ offset: captureEndpointEvent1['offset'],
key: null,
+ token: 'ok',
},
]
const consume = jest.spyOn(ConfiguredLimiter, 'consume').mockImplementation(() => false)
- await eachBatchParallelIngestion(batch, queue, IngestionOverflowMode.Reroute)
+ const tokenBlockList = buildStringMatcher('another_token,more_token', false)
+ await eachBatchParallelIngestion(tokenBlockList, batch, queue, IngestionOverflowMode.Reroute)
expect(consume).not.toHaveBeenCalled()
expect(captureIngestionWarning).not.toHaveBeenCalled()
expect(queue.pluginsServer.kafkaProducer.produce).toHaveBeenCalledWith({
topic: KAFKA_EVENTS_PLUGIN_INGESTION_OVERFLOW,
- value: JSON.stringify(captureEndpointEvent),
- timestamp: captureEndpointEvent['timestamp'],
- offset: captureEndpointEvent['offset'],
+ value: JSON.stringify(captureEndpointEvent1),
+ timestamp: captureEndpointEvent1['timestamp'],
+ offset: captureEndpointEvent1['offset'],
key: null,
waitForAck: true,
})
@@ -95,22 +112,23 @@ describe('eachBatchParallelIngestion with overflow reroute', () => {
it('reroutes excess events to OVERFLOW topic', async () => {
const now = Date.now()
- const batch = createBatchWithMultipleEventsWithKeys([captureEndpointEvent], now)
+ const batch = createBatchWithMultipleEventsWithKeys([captureEndpointEvent1], now)
const consume = jest.spyOn(ConfiguredLimiter, 'consume').mockImplementation(() => false)
- await eachBatchParallelIngestion(batch, queue, IngestionOverflowMode.Reroute)
+ const tokenBlockList = buildStringMatcher('another_token,more_token', false)
+ await eachBatchParallelIngestion(tokenBlockList, batch, queue, IngestionOverflowMode.Reroute)
expect(consume).toHaveBeenCalledWith(
- captureEndpointEvent['team_id'] + ':' + captureEndpointEvent['distinct_id'],
+ captureEndpointEvent1['token'] + ':' + captureEndpointEvent1['distinct_id'],
1,
now
)
expect(captureIngestionWarning).not.toHaveBeenCalled()
expect(queue.pluginsServer.kafkaProducer.produce).toHaveBeenCalledWith({
topic: KAFKA_EVENTS_PLUGIN_INGESTION_OVERFLOW,
- value: JSON.stringify(captureEndpointEvent),
- timestamp: captureEndpointEvent['timestamp'],
- offset: captureEndpointEvent['offset'],
+ value: JSON.stringify(captureEndpointEvent1),
+ timestamp: captureEndpointEvent1['timestamp'],
+ offset: captureEndpointEvent1['offset'],
key: null,
waitForAck: true,
})
@@ -121,19 +139,47 @@ describe('eachBatchParallelIngestion with overflow reroute', () => {
it('does not reroute if not over capacity limit', async () => {
const now = Date.now()
- const batch = createBatchWithMultipleEventsWithKeys([captureEndpointEvent], now)
+ const batch = createBatchWithMultipleEventsWithKeys([captureEndpointEvent1, captureEndpointEvent2], now)
const consume = jest.spyOn(ConfiguredLimiter, 'consume').mockImplementation(() => true)
- await eachBatchParallelIngestion(batch, queue, IngestionOverflowMode.Reroute)
+ const tokenBlockList = buildStringMatcher('another_token,more_token', false)
+ await eachBatchParallelIngestion(tokenBlockList, batch, queue, IngestionOverflowMode.Reroute)
expect(consume).toHaveBeenCalledWith(
- captureEndpointEvent['team_id'] + ':' + captureEndpointEvent['distinct_id'],
+ captureEndpointEvent1['token'] + ':' + captureEndpointEvent1['distinct_id'],
+ 1,
+ now
+ )
+ expect(consume).toHaveBeenCalledWith(
+ captureEndpointEvent2['token'] + ':' + captureEndpointEvent2['distinct_id'],
1,
now
)
expect(captureIngestionWarning).not.toHaveBeenCalled()
expect(queue.pluginsServer.kafkaProducer.produce).not.toHaveBeenCalled()
// Event is processed
- expect(runEventPipeline).toHaveBeenCalled()
+ expect(runEventPipeline).toHaveBeenCalledTimes(2)
+ })
+
+ it('does drop events from blocked tokens', async () => {
+ const now = Date.now()
+ const batch = createBatchWithMultipleEventsWithKeys(
+ [captureEndpointEvent1, captureEndpointEvent2, captureEndpointEvent1],
+ now
+ )
+ const consume = jest.spyOn(ConfiguredLimiter, 'consume').mockImplementation(() => true)
+
+ const tokenBlockList = buildStringMatcher('mytoken,another_token', false)
+ await eachBatchParallelIngestion(tokenBlockList, batch, queue, IngestionOverflowMode.Reroute)
+
+ // Event captureEndpointEvent1 is dropped , captureEndpointEvent2 goes though
+ expect(consume).toHaveBeenCalledWith(
+ captureEndpointEvent2['token'] + ':' + captureEndpointEvent2['distinct_id'],
+ 1,
+ now
+ )
+ expect(captureIngestionWarning).not.toHaveBeenCalled()
+ expect(queue.pluginsServer.kafkaProducer.produce).not.toHaveBeenCalled()
+ expect(runEventPipeline).toHaveBeenCalledTimes(1)
})
})
diff --git a/plugin-server/tests/main/ingestion-queues/analytics-events-ingestion-overflow-consumer.test.ts b/plugin-server/tests/main/ingestion-queues/analytics-events-ingestion-overflow-consumer.test.ts
index 357ab55d2ee91..8cb50d0e99c36 100644
--- a/plugin-server/tests/main/ingestion-queues/analytics-events-ingestion-overflow-consumer.test.ts
+++ b/plugin-server/tests/main/ingestion-queues/analytics-events-ingestion-overflow-consumer.test.ts
@@ -1,8 +1,10 @@
+import { buildStringMatcher } from '../../../src/config/config'
import {
eachBatchParallelIngestion,
IngestionOverflowMode,
} from '../../../src/main/ingestion-queues/batch-processing/each-batch-ingestion'
import { OverflowWarningLimiter } from '../../../src/utils/token-bucket'
+import { runEventPipeline } from './../../../src/worker/ingestion/event-pipeline/runner'
import { captureIngestionWarning } from './../../../src/worker/ingestion/utils'
jest.mock('../../../src/utils/status')
@@ -10,9 +12,8 @@ jest.mock('./../../../src/worker/ingestion/utils')
jest.mock('./../../../src/worker/ingestion/event-pipeline/runner', () => ({
runEventPipeline: jest.fn().mockResolvedValue('default value'),
}))
-import { runEventPipeline } from './../../../src/worker/ingestion/event-pipeline/runner'
-const captureEndpointEvent = {
+const captureEndpointEvent1 = {
uuid: 'uuid1',
distinct_id: 'id',
ip: null,
@@ -21,7 +22,21 @@ const captureEndpointEvent = {
event: 'event',
properties: {},
}),
- team_id: 1,
+ token: 'mytoken',
+ now: null,
+ sent_at: null,
+}
+
+const captureEndpointEvent2 = {
+ uuid: 'uuid2',
+ distinct_id: 'id',
+ ip: null,
+ site_url: '',
+ data: JSON.stringify({
+ event: 'event',
+ properties: {},
+ }),
+ token: 'othertoken',
now: null,
sent_at: null,
}
@@ -61,45 +76,56 @@ describe('eachBatchParallelIngestion with overflow consume', () => {
})
it('raises ingestion warning when consuming from overflow', async () => {
- const batch = createBatchWithMultipleEventsWithKeys([captureEndpointEvent])
+ const batch = createBatchWithMultipleEventsWithKeys([captureEndpointEvent1])
const consume = jest.spyOn(OverflowWarningLimiter, 'consume').mockImplementation(() => true)
queue.pluginsServer.teamManager.getTeamForEvent.mockResolvedValueOnce({ id: 1 })
- await eachBatchParallelIngestion(batch, queue, IngestionOverflowMode.Consume)
+ const tokenBlockList = buildStringMatcher('another_token,more_token', false)
+ await eachBatchParallelIngestion(tokenBlockList, batch, queue, IngestionOverflowMode.Consume)
expect(queue.pluginsServer.teamManager.getTeamForEvent).toHaveBeenCalledTimes(1)
- expect(consume).toHaveBeenCalledWith(
- captureEndpointEvent['team_id'] + ':' + captureEndpointEvent['distinct_id'],
- 1
- )
- expect(captureIngestionWarning).toHaveBeenCalledWith(
- queue.pluginsServer.db,
- captureEndpointEvent['team_id'],
- 'ingestion_capacity_overflow',
- {
- overflowDistinctId: captureEndpointEvent['distinct_id'],
- }
- )
+ expect(consume).toHaveBeenCalledWith('1:id', 1)
+ expect(captureIngestionWarning).toHaveBeenCalledWith(queue.pluginsServer.db, 1, 'ingestion_capacity_overflow', {
+ overflowDistinctId: captureEndpointEvent1['distinct_id'],
+ })
// Event is processed
expect(runEventPipeline).toHaveBeenCalled()
})
it('does not raise ingestion warning when under threshold', async () => {
- const batch = createBatchWithMultipleEventsWithKeys([captureEndpointEvent])
+ const batch = createBatchWithMultipleEventsWithKeys([captureEndpointEvent1])
const consume = jest.spyOn(OverflowWarningLimiter, 'consume').mockImplementation(() => false)
queue.pluginsServer.teamManager.getTeamForEvent.mockResolvedValueOnce({ id: 1 })
- await eachBatchParallelIngestion(batch, queue, IngestionOverflowMode.Consume)
+ const tokenBlockList = buildStringMatcher('another_token,more_token', false)
+ await eachBatchParallelIngestion(tokenBlockList, batch, queue, IngestionOverflowMode.Consume)
- expect(consume).toHaveBeenCalledWith(
- captureEndpointEvent['team_id'] + ':' + captureEndpointEvent['distinct_id'],
- 1
- )
+ expect(consume).toHaveBeenCalledWith('1:id', 1)
expect(captureIngestionWarning).not.toHaveBeenCalled()
expect(queue.pluginsServer.kafkaProducer.queueMessage).not.toHaveBeenCalled()
// Event is processed
expect(runEventPipeline).toHaveBeenCalled()
})
+
+ it('does drop events from blocked tokens', async () => {
+ const batch = createBatchWithMultipleEventsWithKeys([
+ captureEndpointEvent1,
+ captureEndpointEvent2,
+ captureEndpointEvent1,
+ ])
+ const consume = jest.spyOn(OverflowWarningLimiter, 'consume').mockImplementation(() => false)
+
+ queue.pluginsServer.teamManager.getTeamForEvent.mockResolvedValueOnce({ id: 1 })
+ const tokenBlockList = buildStringMatcher('mytoken,more_token', false)
+ await eachBatchParallelIngestion(tokenBlockList, batch, queue, IngestionOverflowMode.Consume)
+
+ expect(captureIngestionWarning).not.toHaveBeenCalled()
+ expect(queue.pluginsServer.kafkaProducer.queueMessage).not.toHaveBeenCalled()
+
+ // captureEndpointEvent2 is processed, captureEndpointEvent1 are dropped
+ expect(runEventPipeline).toHaveBeenCalledTimes(1)
+ expect(consume).toHaveBeenCalledTimes(1)
+ })
})
diff --git a/plugin-server/tests/main/ingestion-queues/each-batch.test.ts b/plugin-server/tests/main/ingestion-queues/each-batch.test.ts
index c25062008eaee..78cdcabb181fa 100644
--- a/plugin-server/tests/main/ingestion-queues/each-batch.test.ts
+++ b/plugin-server/tests/main/ingestion-queues/each-batch.test.ts
@@ -1,4 +1,4 @@
-import { buildIntegerMatcher } from '../../../src/config/config'
+import { buildIntegerMatcher, buildStringMatcher } from '../../../src/config/config'
import { KAFKA_EVENTS_PLUGIN_INGESTION } from '../../../src/config/kafka-topics'
import {
eachBatchParallelIngestion,
@@ -23,6 +23,7 @@ import { ActionMatcher } from '../../../src/worker/ingestion/action-matcher'
import { HookCommander } from '../../../src/worker/ingestion/hooks'
import { runOnEvent } from '../../../src/worker/plugins/run'
import { pluginConfig39 } from '../../helpers/plugins'
+import { runEventPipeline } from './../../../src/worker/ingestion/event-pipeline/runner'
jest.mock('../../../src/worker/plugins/run')
@@ -39,7 +40,6 @@ jest.mock('./../../../src/worker/ingestion/event-pipeline/runner', () => ({
runEventPipeline: jest.fn().mockResolvedValue('default value'),
// runEventPipeline: jest.fn().mockRejectedValue('default value'),
}))
-import { runEventPipeline } from './../../../src/worker/ingestion/event-pipeline/runner'
const event: PostIngestionEvent = {
eventUuid: 'uuid1',
@@ -408,7 +408,8 @@ describe('eachBatchX', () => {
})
it('calls runEventPipeline', async () => {
const batch = createBatch(captureEndpointEvent)
- await eachBatchParallelIngestion(batch, queue, IngestionOverflowMode.Disabled)
+ const tokenBlockList = buildStringMatcher('another_token,more_token', false)
+ await eachBatchParallelIngestion(tokenBlockList, batch, queue, IngestionOverflowMode.Disabled)
expect(runEventPipeline).toHaveBeenCalledWith(expect.anything(), {
distinct_id: 'id',
@@ -430,7 +431,8 @@ describe('eachBatchX', () => {
it("doesn't fail the batch if runEventPipeline rejects once then succeeds on retry", async () => {
const batch = createBatch(captureEndpointEvent)
runEventPipelineSpy.mockImplementationOnce(() => Promise.reject('runEventPipeline nopes out'))
- await eachBatchParallelIngestion(batch, queue, IngestionOverflowMode.Disabled)
+ const tokenBlockList = buildStringMatcher('another_token,more_token', false)
+ await eachBatchParallelIngestion(tokenBlockList, batch, queue, IngestionOverflowMode.Disabled)
expect(runEventPipeline).toHaveBeenCalledTimes(2)
})
@@ -441,9 +443,10 @@ describe('eachBatchX', () => {
promises: [Promise.resolve(), Promise.reject('deferred nopes out')],
})
)
- await expect(eachBatchParallelIngestion(batch, queue, IngestionOverflowMode.Disabled)).rejects.toBe(
- 'deferred nopes out'
- )
+ const tokenBlockList = buildStringMatcher('another_token,more_token', false)
+ await expect(
+ eachBatchParallelIngestion(tokenBlockList, batch, queue, IngestionOverflowMode.Disabled)
+ ).rejects.toBe('deferred nopes out')
expect(runEventPipeline).toHaveBeenCalledTimes(1)
})
@@ -463,7 +466,8 @@ describe('eachBatchX', () => {
{ ...captureEndpointEvent, team_id: 3, distinct_id: 'a' },
])
const stats = new Map()
- for (const group of splitIngestionBatch(batch, IngestionOverflowMode.Disabled).toProcess) {
+ const tokenBlockList = buildStringMatcher('another_token,more_token', false)
+ for (const group of splitIngestionBatch(tokenBlockList, batch, IngestionOverflowMode.Disabled).toProcess) {
const key = `${group[0].pluginEvent.team_id}:${group[0].pluginEvent.token}:${group[0].pluginEvent.distinct_id}`
for (const { pluginEvent: event } of group) {
expect(`${event.team_id}:${event.token}:${event.distinct_id}`).toEqual(key)
@@ -492,7 +496,8 @@ describe('eachBatchX', () => {
{ ...captureEndpointEvent, team_id: 4, distinct_id: 'a' },
{ ...captureEndpointEvent, team_id: 4, distinct_id: 'a' },
])
- const batches = splitIngestionBatch(input, IngestionOverflowMode.Consume).toProcess
+ const tokenBlockList = buildStringMatcher('another_token,more_token', false)
+ const batches = splitIngestionBatch(tokenBlockList, input, IngestionOverflowMode.Consume).toProcess
expect(batches.length).toEqual(input.length)
for (const group of batches) {
expect(group.length).toEqual(1)
@@ -516,8 +521,8 @@ describe('eachBatchX', () => {
{ ...captureEndpointEvent, offset: 13, team_id: 3 }, // repeat
{ ...captureEndpointEvent, offset: 14, team_id: 5 }, // repeat
])
-
- await eachBatchParallelIngestion(batch, queue, IngestionOverflowMode.Disabled)
+ const tokenBlockList = buildStringMatcher('another_token,more_token', false)
+ await eachBatchParallelIngestion(tokenBlockList, batch, queue, IngestionOverflowMode.Disabled)
expect(runEventPipeline).toHaveBeenCalledTimes(14)
expect(queue.pluginsServer.statsd.histogram).toHaveBeenCalledWith(
'ingest_event_batching.input_length',
@@ -532,14 +537,15 @@ describe('eachBatchX', () => {
})
it('fails the batch if runEventPipeline rejects repeatedly', async () => {
+ const tokenBlockList = buildStringMatcher('another_token,more_token', false)
const batch = createBatch(captureEndpointEvent)
runEventPipelineSpy
.mockImplementationOnce(() => Promise.reject('runEventPipeline nopes out'))
.mockImplementationOnce(() => Promise.reject('runEventPipeline nopes out'))
.mockImplementationOnce(() => Promise.reject('runEventPipeline nopes out'))
- await expect(eachBatchParallelIngestion(batch, queue, IngestionOverflowMode.Disabled)).rejects.toBe(
- 'runEventPipeline nopes out'
- )
+ await expect(
+ eachBatchParallelIngestion(tokenBlockList, batch, queue, IngestionOverflowMode.Disabled)
+ ).rejects.toBe('runEventPipeline nopes out')
expect(runEventPipeline).toHaveBeenCalledTimes(3)
runEventPipelineSpy.mockRestore()
})
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 3cea6488b4311..4c6dfb3248460 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1,9 +1,12 @@
-lockfileVersion: '6.1'
+lockfileVersion: '6.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
+overrides:
+ playwright: 1.32.2
+
dependencies:
'@ant-design/icons':
specifier: ^4.7.0
@@ -36,8 +39,8 @@ dependencies:
specifier: 4.4.6
version: 4.4.6(monaco-editor@0.39.0)(react-dom@18.2.0)(react@18.2.0)
'@posthog/icons':
- specifier: 0.4.10
- version: 0.4.10(react-dom@18.2.0)(react@18.2.0)
+ specifier: 0.4.11
+ version: 0.4.11(react-dom@18.2.0)(react@18.2.0)
'@posthog/plugin-scaffold':
specifier: ^1.4.4
version: 1.4.4
@@ -216,8 +219,8 @@ dependencies:
specifier: ^2.11.0
version: 2.11.0
posthog-js:
- specifier: 1.92.1
- version: 1.92.1
+ specifier: 1.93.1
+ version: 1.93.1
posthog-js-lite:
specifier: 2.0.0-alpha5
version: 2.0.0-alpha5
@@ -355,8 +358,8 @@ devDependencies:
specifier: ^5.17.1
version: 5.17.1(@babel/core@7.22.10)(@babel/preset-env@7.22.10)(babel-loader@8.3.0)(webpack@5.88.2)
'@playwright/test':
- specifier: 1.29.2
- version: 1.29.2
+ specifier: 1.32.2
+ version: 1.32.2
'@sentry/types':
specifier: 7.22.0
version: 7.22.0
@@ -400,8 +403,8 @@ devDependencies:
specifier: ^7.5.1
version: 7.5.1(@babel/core@7.22.10)(@swc/core@1.3.93)(esbuild@0.14.54)(react-dom@18.2.0)(react@18.2.0)(typescript@4.9.5)(webpack-cli@5.1.4)
'@storybook/test-runner':
- specifier: ^0.13.0
- version: 0.13.0(@types/node@18.11.9)(ts-node@10.9.1)
+ specifier: ^0.15.2
+ version: 0.15.2(@types/node@18.11.9)(ts-node@10.9.1)
'@storybook/theming':
specifier: ^7.5.1
version: 7.5.1(react-dom@18.2.0)(react@18.2.0)
@@ -612,9 +615,6 @@ devDependencies:
pixelmatch:
specifier: ^5.3.0
version: 5.3.0
- playwright-core:
- specifier: 1.29.2
- version: 1.29.2
pngjs:
specifier: ^6.0.0
version: 6.0.0
@@ -796,9 +796,9 @@ packages:
resolution: {integrity: sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.23.0
+ '@babel/types': 7.23.4
'@jridgewell/gen-mapping': 0.3.2
- '@jridgewell/trace-mapping': 0.3.17
+ '@jridgewell/trace-mapping': 0.3.20
jsesc: 2.5.2
/@babel/helper-annotate-as-pure@7.22.5:
@@ -811,7 +811,7 @@ packages:
resolution: {integrity: sha512-Av0qubwDQxC56DoUReVDeLfMEjYYSN1nZrTUrWkXd7hpU73ymRANkbuDm3yni9npkn+RXy9nNbEJZEzXr7xrfQ==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.23.0
+ '@babel/types': 7.23.4
dev: true
/@babel/helper-compilation-targets@7.22.10:
@@ -876,19 +876,19 @@ packages:
engines: {node: '>=6.9.0'}
dependencies:
'@babel/template': 7.22.15
- '@babel/types': 7.23.0
+ '@babel/types': 7.23.4
/@babel/helper-hoist-variables@7.22.5:
resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.23.0
+ '@babel/types': 7.23.4
/@babel/helper-member-expression-to-functions@7.22.5:
resolution: {integrity: sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.23.0
+ '@babel/types': 7.23.4
/@babel/helper-module-imports@7.22.5:
resolution: {integrity: sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==}
@@ -913,7 +913,7 @@ packages:
resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.23.0
+ '@babel/types': 7.23.4
/@babel/helper-plugin-utils@7.22.5:
resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==}
@@ -946,19 +946,19 @@ packages:
resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.23.0
+ '@babel/types': 7.23.4
/@babel/helper-skip-transparent-expression-wrappers@7.22.5:
resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.23.0
+ '@babel/types': 7.23.4
/@babel/helper-split-export-declaration@7.22.6:
resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.23.0
+ '@babel/types': 7.23.4
/@babel/helper-string-parser@7.22.5:
resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==}
@@ -967,7 +967,6 @@ packages:
/@babel/helper-string-parser@7.23.4:
resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==}
engines: {node: '>=6.9.0'}
- dev: true
/@babel/helper-validator-identifier@7.22.20:
resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==}
@@ -983,7 +982,7 @@ packages:
dependencies:
'@babel/helper-function-name': 7.23.0
'@babel/template': 7.22.15
- '@babel/types': 7.23.0
+ '@babel/types': 7.23.4
dev: true
/@babel/helpers@7.22.10:
@@ -1017,7 +1016,6 @@ packages:
hasBin: true
dependencies:
'@babel/types': 7.23.4
- dev: true
/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.22.5(@babel/core@7.22.10):
resolution: {integrity: sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ==}
@@ -2097,8 +2095,8 @@ packages:
engines: {node: '>=6.9.0'}
dependencies:
'@babel/code-frame': 7.22.13
- '@babel/parser': 7.23.0
- '@babel/types': 7.23.0
+ '@babel/parser': 7.23.4
+ '@babel/types': 7.23.4
/@babel/template@7.22.5:
resolution: {integrity: sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==}
@@ -2140,7 +2138,6 @@ packages:
'@babel/helper-string-parser': 7.23.4
'@babel/helper-validator-identifier': 7.22.20
to-fast-properties: 2.0.0
- dev: true
/@base2/pretty-print-object@1.0.1:
resolution: {integrity: sha512-4iri8i1AqYHJE2DstZYkyEprg6Pq6sKx3xn5FpySk9sNhH7qN2LLlHJCfDTZRILNwQNPD7mATWM0TBui7uC1pA==}
@@ -2679,71 +2676,28 @@ packages:
engines: {node: '>=8'}
dev: true
- /@jest/console@28.1.3:
- resolution: {integrity: sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
- dependencies:
- '@jest/types': 28.1.3
- '@types/node': 18.18.4
- chalk: 4.1.2
- jest-message-util: 28.1.3
- jest-util: 28.1.3
- slash: 3.0.0
- dev: true
-
/@jest/console@29.3.1:
resolution: {integrity: sha512-IRE6GD47KwcqA09RIWrabKdHPiKDGgtAL31xDxbi/RjQMsr+lY+ppxmHwY0dUEV3qvvxZzoe5Hl0RXZJOjQNUg==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@jest/types': 29.3.1
+ '@jest/types': 29.6.3
'@types/node': 18.18.4
chalk: 4.1.2
- jest-message-util: 29.3.1
- jest-util: 29.3.1
+ jest-message-util: 29.7.0
+ jest-util: 29.7.0
slash: 3.0.0
dev: true
- /@jest/core@28.1.3(ts-node@10.9.1):
- resolution: {integrity: sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
- peerDependencies:
- node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
- peerDependenciesMeta:
- node-notifier:
- optional: true
+ /@jest/console@29.7.0:
+ resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@jest/console': 28.1.3
- '@jest/reporters': 28.1.3
- '@jest/test-result': 28.1.3
- '@jest/transform': 28.1.3
- '@jest/types': 28.1.3
+ '@jest/types': 29.6.3
'@types/node': 18.18.4
- ansi-escapes: 4.3.2
chalk: 4.1.2
- ci-info: 3.5.0
- exit: 0.1.2
- graceful-fs: 4.2.11
- jest-changed-files: 28.1.3
- jest-config: 28.1.3(@types/node@18.18.4)(ts-node@10.9.1)
- jest-haste-map: 28.1.3
- jest-message-util: 28.1.3
- jest-regex-util: 28.0.2
- jest-resolve: 28.1.3
- jest-resolve-dependencies: 28.1.3
- jest-runner: 28.1.3
- jest-runtime: 28.1.3
- jest-snapshot: 28.1.3
- jest-util: 28.1.3
- jest-validate: 28.1.3
- jest-watcher: 28.1.3
- micromatch: 4.0.5
- pretty-format: 28.1.3
- rimraf: 3.0.2
+ jest-message-util: 29.7.0
+ jest-util: 29.7.0
slash: 3.0.0
- strip-ansi: 6.0.1
- transitivePeerDependencies:
- - supports-color
- - ts-node
dev: true
/@jest/core@29.3.1(ts-node@10.9.1):
@@ -2784,6 +2738,50 @@ packages:
slash: 3.0.0
strip-ansi: 6.0.1
transitivePeerDependencies:
+ - babel-plugin-macros
+ - supports-color
+ - ts-node
+ dev: true
+
+ /@jest/core@29.7.0(ts-node@10.9.1):
+ resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ peerDependencies:
+ node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
+ peerDependenciesMeta:
+ node-notifier:
+ optional: true
+ dependencies:
+ '@jest/console': 29.7.0
+ '@jest/reporters': 29.7.0
+ '@jest/test-result': 29.7.0
+ '@jest/transform': 29.7.0
+ '@jest/types': 29.6.3
+ '@types/node': 18.18.4
+ ansi-escapes: 4.3.2
+ chalk: 4.1.2
+ ci-info: 3.5.0
+ exit: 0.1.2
+ graceful-fs: 4.2.11
+ jest-changed-files: 29.7.0
+ jest-config: 29.7.0(@types/node@18.18.4)(ts-node@10.9.1)
+ jest-haste-map: 29.7.0
+ jest-message-util: 29.7.0
+ jest-regex-util: 29.6.3
+ jest-resolve: 29.7.0
+ jest-resolve-dependencies: 29.7.0
+ jest-runner: 29.7.0
+ jest-runtime: 29.7.0
+ jest-snapshot: 29.7.0
+ jest-util: 29.7.0
+ jest-validate: 29.7.0
+ jest-watcher: 29.7.0
+ micromatch: 4.0.5
+ pretty-format: 29.7.0
+ slash: 3.0.0
+ strip-ansi: 6.0.1
+ transitivePeerDependencies:
+ - babel-plugin-macros
- supports-color
- ts-node
dev: true
@@ -2795,16 +2793,6 @@ packages:
'@jest/types': 27.5.1
dev: true
- /@jest/environment@28.1.3:
- resolution: {integrity: sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
- dependencies:
- '@jest/fake-timers': 28.1.3
- '@jest/types': 28.1.3
- '@types/node': 18.18.4
- jest-mock: 28.1.3
- dev: true
-
/@jest/environment@29.3.1:
resolution: {integrity: sha512-pMmvfOPmoa1c1QpfFW0nXYtNLpofqo4BrCIk6f2kW4JFeNlHV2t3vd+3iDLf31e2ot2Mec0uqZfmI+U0K2CFag==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -2815,11 +2803,14 @@ packages:
jest-mock: 29.3.1
dev: true
- /@jest/expect-utils@28.1.3:
- resolution: {integrity: sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ /@jest/environment@29.7.0:
+ resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- jest-get-type: 28.0.2
+ '@jest/fake-timers': 29.7.0
+ '@jest/types': 29.6.3
+ '@types/node': 18.18.4
+ jest-mock: 29.7.0
dev: true
/@jest/expect-utils@29.3.1:
@@ -2829,38 +2820,23 @@ packages:
jest-get-type: 29.2.0
dev: true
- /@jest/expect@28.1.3:
- resolution: {integrity: sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ /@jest/expect-utils@29.7.0:
+ resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- expect: 28.1.3
- jest-snapshot: 28.1.3
- transitivePeerDependencies:
- - supports-color
+ jest-get-type: 29.6.3
dev: true
- /@jest/expect@29.3.1:
- resolution: {integrity: sha512-QivM7GlSHSsIAWzgfyP8dgeExPRZ9BIe2LsdPyEhCGkZkoyA+kGsoIzbKAfZCvvRzfZioKwPtCZIt5SaoxYCvg==}
+ /@jest/expect@29.7.0:
+ resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- expect: 29.3.1
- jest-snapshot: 29.3.1
+ expect: 29.7.0
+ jest-snapshot: 29.7.0
transitivePeerDependencies:
- supports-color
dev: true
- /@jest/fake-timers@28.1.3:
- resolution: {integrity: sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
- dependencies:
- '@jest/types': 28.1.3
- '@sinonjs/fake-timers': 9.1.2
- '@types/node': 18.18.4
- jest-message-util: 28.1.3
- jest-mock: 28.1.3
- jest-util: 28.1.3
- dev: true
-
/@jest/fake-timers@29.3.1:
resolution: {integrity: sha512-iHTL/XpnDlFki9Tq0Q1GGuVeQ8BHZGIYsvCO5eN/O/oJaRzofG9Xndd9HuSDBI/0ZS79pg0iwn07OMTQ7ngF2A==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -2873,32 +2849,45 @@ packages:
jest-util: 29.3.1
dev: true
- /@jest/globals@28.1.3:
- resolution: {integrity: sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ /@jest/fake-timers@29.7.0:
+ resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@jest/environment': 28.1.3
- '@jest/expect': 28.1.3
- '@jest/types': 28.1.3
- transitivePeerDependencies:
- - supports-color
+ '@jest/types': 29.6.3
+ '@sinonjs/fake-timers': 10.3.0
+ '@types/node': 18.18.4
+ jest-message-util: 29.7.0
+ jest-mock: 29.7.0
+ jest-util: 29.7.0
dev: true
/@jest/globals@29.3.1:
resolution: {integrity: sha512-cTicd134vOcwO59OPaB6AmdHQMCtWOe+/DitpTZVxWgMJ+YvXL1HNAmPyiGbSHmF/mXVBkvlm8YYtQhyHPnV6Q==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@jest/environment': 29.3.1
- '@jest/expect': 29.3.1
- '@jest/types': 29.3.1
- jest-mock: 29.3.1
+ '@jest/environment': 29.7.0
+ '@jest/expect': 29.7.0
+ '@jest/types': 29.6.3
+ jest-mock: 29.7.0
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /@jest/globals@29.7.0:
+ resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ dependencies:
+ '@jest/environment': 29.7.0
+ '@jest/expect': 29.7.0
+ '@jest/types': 29.6.3
+ jest-mock: 29.7.0
transitivePeerDependencies:
- supports-color
dev: true
- /@jest/reporters@28.1.3:
- resolution: {integrity: sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ /@jest/reporters@29.3.1:
+ resolution: {integrity: sha512-GhBu3YFuDrcAYW/UESz1JphEAbvUjaY2vShRZRoRY1mxpCMB3yGSJ4j9n0GxVlEOdCf7qjvUfBCrTUUqhVfbRA==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
peerDependencies:
node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
peerDependenciesMeta:
@@ -2906,11 +2895,11 @@ packages:
optional: true
dependencies:
'@bcoe/v8-coverage': 0.2.3
- '@jest/console': 28.1.3
- '@jest/test-result': 28.1.3
- '@jest/transform': 28.1.3
- '@jest/types': 28.1.3
- '@jridgewell/trace-mapping': 0.3.17
+ '@jest/console': 29.3.1
+ '@jest/test-result': 29.7.0
+ '@jest/transform': 29.3.1
+ '@jest/types': 29.6.3
+ '@jridgewell/trace-mapping': 0.3.20
'@types/node': 18.18.4
chalk: 4.1.2
collect-v8-coverage: 1.0.1
@@ -2922,20 +2911,19 @@ packages:
istanbul-lib-report: 3.0.0
istanbul-lib-source-maps: 4.0.1
istanbul-reports: 3.1.5
- jest-message-util: 28.1.3
- jest-util: 28.1.3
- jest-worker: 28.1.3
+ jest-message-util: 29.7.0
+ jest-util: 29.7.0
+ jest-worker: 29.3.1
slash: 3.0.0
string-length: 4.0.2
strip-ansi: 6.0.1
- terminal-link: 2.1.1
v8-to-istanbul: 9.0.1
transitivePeerDependencies:
- supports-color
dev: true
- /@jest/reporters@29.3.1:
- resolution: {integrity: sha512-GhBu3YFuDrcAYW/UESz1JphEAbvUjaY2vShRZRoRY1mxpCMB3yGSJ4j9n0GxVlEOdCf7qjvUfBCrTUUqhVfbRA==}
+ /@jest/reporters@29.7.0:
+ resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
peerDependencies:
node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
@@ -2944,11 +2932,11 @@ packages:
optional: true
dependencies:
'@bcoe/v8-coverage': 0.2.3
- '@jest/console': 29.3.1
- '@jest/test-result': 29.3.1
- '@jest/transform': 29.3.1
- '@jest/types': 29.3.1
- '@jridgewell/trace-mapping': 0.3.17
+ '@jest/console': 29.7.0
+ '@jest/test-result': 29.7.0
+ '@jest/transform': 29.7.0
+ '@jest/types': 29.6.3
+ '@jridgewell/trace-mapping': 0.3.20
'@types/node': 18.18.4
chalk: 4.1.2
collect-v8-coverage: 1.0.1
@@ -2956,13 +2944,13 @@ packages:
glob: 7.2.3
graceful-fs: 4.2.11
istanbul-lib-coverage: 3.2.0
- istanbul-lib-instrument: 5.2.1
+ istanbul-lib-instrument: 6.0.1
istanbul-lib-report: 3.0.0
istanbul-lib-source-maps: 4.0.1
istanbul-reports: 3.1.5
- jest-message-util: 29.3.1
- jest-util: 29.3.1
- jest-worker: 29.3.1
+ jest-message-util: 29.7.0
+ jest-util: 29.7.0
+ jest-worker: 29.7.0
slash: 3.0.0
string-length: 4.0.2
strip-ansi: 6.0.1
@@ -2971,13 +2959,6 @@ packages:
- supports-color
dev: true
- /@jest/schemas@28.1.3:
- resolution: {integrity: sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
- dependencies:
- '@sinclair/typebox': 0.24.51
- dev: true
-
/@jest/schemas@29.0.0:
resolution: {integrity: sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -2985,32 +2966,29 @@ packages:
'@sinclair/typebox': 0.24.51
dev: true
- /@jest/source-map@28.1.2:
- resolution: {integrity: sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ /@jest/schemas@29.6.3:
+ resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@jridgewell/trace-mapping': 0.3.17
- callsites: 3.1.0
- graceful-fs: 4.2.11
+ '@sinclair/typebox': 0.27.8
dev: true
/@jest/source-map@29.2.0:
resolution: {integrity: sha512-1NX9/7zzI0nqa6+kgpSdKPK+WU1p+SJk3TloWZf5MzPbxri9UEeXX5bWZAPCzbQcyuAzubcdUHA7hcNznmRqWQ==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@jridgewell/trace-mapping': 0.3.17
+ '@jridgewell/trace-mapping': 0.3.20
callsites: 3.1.0
graceful-fs: 4.2.11
dev: true
- /@jest/test-result@28.1.3:
- resolution: {integrity: sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ /@jest/source-map@29.6.3:
+ resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@jest/console': 28.1.3
- '@jest/types': 28.1.3
- '@types/istanbul-lib-coverage': 2.0.4
- collect-v8-coverage: 1.0.1
+ '@jridgewell/trace-mapping': 0.3.20
+ callsites: 3.1.0
+ graceful-fs: 4.2.11
dev: true
/@jest/test-result@29.3.1:
@@ -3018,46 +2996,56 @@ packages:
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@jest/console': 29.3.1
- '@jest/types': 29.3.1
+ '@jest/types': 29.6.3
'@types/istanbul-lib-coverage': 2.0.4
collect-v8-coverage: 1.0.1
dev: true
- /@jest/test-sequencer@28.1.3:
- resolution: {integrity: sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ /@jest/test-result@29.7.0:
+ resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@jest/test-result': 28.1.3
- graceful-fs: 4.2.11
- jest-haste-map: 28.1.3
- slash: 3.0.0
+ '@jest/console': 29.7.0
+ '@jest/types': 29.6.3
+ '@types/istanbul-lib-coverage': 2.0.4
+ collect-v8-coverage: 1.0.1
dev: true
/@jest/test-sequencer@29.3.1:
resolution: {integrity: sha512-IqYvLbieTv20ArgKoAMyhLHNrVHJfzO6ARZAbQRlY4UGWfdDnLlZEF0BvKOMd77uIiIjSZRwq3Jb3Fa3I8+2UA==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@jest/test-result': 29.3.1
+ '@jest/test-result': 29.7.0
graceful-fs: 4.2.11
- jest-haste-map: 29.3.1
+ jest-haste-map: 29.7.0
slash: 3.0.0
dev: true
- /@jest/transform@28.1.3:
- resolution: {integrity: sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ /@jest/test-sequencer@29.7.0:
+ resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ dependencies:
+ '@jest/test-result': 29.7.0
+ graceful-fs: 4.2.11
+ jest-haste-map: 29.7.0
+ slash: 3.0.0
+ dev: true
+
+ /@jest/transform@29.3.1:
+ resolution: {integrity: sha512-8wmCFBTVGYqFNLWfcOWoVuMuKYPUBTnTMDkdvFtAYELwDOl9RGwOsvQWGPFxDJ8AWY9xM/8xCXdqmPK3+Q5Lug==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@babel/core': 7.22.10
- '@jest/types': 28.1.3
- '@jridgewell/trace-mapping': 0.3.17
+ '@jest/types': 29.6.3
+ '@jridgewell/trace-mapping': 0.3.20
babel-plugin-istanbul: 6.1.1
chalk: 4.1.2
- convert-source-map: 1.9.0
+ convert-source-map: 2.0.0
fast-json-stable-stringify: 2.1.0
graceful-fs: 4.2.11
- jest-haste-map: 28.1.3
- jest-regex-util: 28.0.2
- jest-util: 28.1.3
+ jest-haste-map: 29.3.1
+ jest-regex-util: 29.2.0
+ jest-util: 29.3.1
micromatch: 4.0.5
pirates: 4.0.5
slash: 3.0.0
@@ -3066,21 +3054,21 @@ packages:
- supports-color
dev: true
- /@jest/transform@29.3.1:
- resolution: {integrity: sha512-8wmCFBTVGYqFNLWfcOWoVuMuKYPUBTnTMDkdvFtAYELwDOl9RGwOsvQWGPFxDJ8AWY9xM/8xCXdqmPK3+Q5Lug==}
+ /@jest/transform@29.7.0:
+ resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@babel/core': 7.22.10
- '@jest/types': 29.3.1
- '@jridgewell/trace-mapping': 0.3.17
+ '@jest/types': 29.6.3
+ '@jridgewell/trace-mapping': 0.3.20
babel-plugin-istanbul: 6.1.1
chalk: 4.1.2
convert-source-map: 2.0.0
fast-json-stable-stringify: 2.1.0
graceful-fs: 4.2.11
- jest-haste-map: 29.3.1
- jest-regex-util: 29.2.0
- jest-util: 29.3.1
+ jest-haste-map: 29.7.0
+ jest-regex-util: 29.6.3
+ jest-util: 29.7.0
micromatch: 4.0.5
pirates: 4.0.5
slash: 3.0.0
@@ -3100,11 +3088,11 @@ packages:
chalk: 4.1.2
dev: true
- /@jest/types@28.1.3:
- resolution: {integrity: sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ /@jest/types@29.3.1:
+ resolution: {integrity: sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@jest/schemas': 28.1.3
+ '@jest/schemas': 29.0.0
'@types/istanbul-lib-coverage': 2.0.4
'@types/istanbul-reports': 3.0.1
'@types/node': 18.18.4
@@ -3112,11 +3100,11 @@ packages:
chalk: 4.1.2
dev: true
- /@jest/types@29.3.1:
- resolution: {integrity: sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA==}
+ /@jest/types@29.6.3:
+ resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@jest/schemas': 29.0.0
+ '@jest/schemas': 29.6.3
'@types/istanbul-lib-coverage': 2.0.4
'@types/istanbul-reports': 3.0.1
'@types/node': 18.18.4
@@ -3137,7 +3125,7 @@ packages:
dependencies:
'@jridgewell/set-array': 1.1.2
'@jridgewell/sourcemap-codec': 1.4.14
- '@jridgewell/trace-mapping': 0.3.17
+ '@jridgewell/trace-mapping': 0.3.20
/@jridgewell/resolve-uri@3.1.0:
resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==}
@@ -3151,7 +3139,7 @@ packages:
resolution: {integrity: sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==}
dependencies:
'@jridgewell/gen-mapping': 0.3.2
- '@jridgewell/trace-mapping': 0.3.17
+ '@jridgewell/trace-mapping': 0.3.20
dev: true
/@jridgewell/sourcemap-codec@1.4.14:
@@ -3163,6 +3151,12 @@ packages:
'@jridgewell/resolve-uri': 3.1.0
'@jridgewell/sourcemap-codec': 1.4.14
+ /@jridgewell/trace-mapping@0.3.20:
+ resolution: {integrity: sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==}
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.0
+ '@jridgewell/sourcemap-codec': 1.4.14
+
/@jridgewell/trace-mapping@0.3.9:
resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
dependencies:
@@ -3376,13 +3370,15 @@ packages:
tslib: 2.6.2
dev: true
- /@playwright/test@1.29.2:
- resolution: {integrity: sha512-+3/GPwOgcoF0xLz/opTnahel1/y42PdcgZ4hs+BZGIUjtmEFSXGg+nFoaH3NSmuc7a6GSFwXDJ5L7VXpqzigNg==}
+ /@playwright/test@1.32.2:
+ resolution: {integrity: sha512-nhaTSDpEdTTttdkDE8Z6K3icuG1DVRxrl98Qq0Lfc63SS9a2sjc9+x8ezysh7MzCKz6Y+nArml3/mmt+gqRmQQ==}
engines: {node: '>=14'}
hasBin: true
dependencies:
'@types/node': 18.18.4
- playwright-core: 1.29.2
+ playwright-core: 1.32.2
+ optionalDependencies:
+ fsevents: 2.3.2
dev: true
/@pmmmwh/react-refresh-webpack-plugin@0.5.11(react-refresh@0.11.0)(webpack@5.88.2):
@@ -3428,8 +3424,8 @@ packages:
resolution: {integrity: sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==}
dev: false
- /@posthog/icons@0.4.10(react-dom@18.2.0)(react@18.2.0):
- resolution: {integrity: sha512-92/pvHxVSWpNri8XoT9cfLfzf7RRvYGn8qMM6vUhMwkebBiurg8/oQHY1rZ0GcKLvCvzyAtgIr4o/N7ma9kWlQ==}
+ /@posthog/icons@0.4.11(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-hpHDrBIZlnc4Z0d8BPjNkMf7gkmR3o7CQGcvXJ8fQzyOL07EU3I0LRfkRHhFaRaZdeDB4TkJkkSDJ8beyuhBPQ==}
peerDependencies:
react: '>=16.14.0'
react-dom: '>=16.14.0'
@@ -4142,6 +4138,10 @@ packages:
resolution: {integrity: sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==}
dev: true
+ /@sinclair/typebox@0.27.8:
+ resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
+ dev: true
+
/@sinonjs/commons@1.8.4:
resolution: {integrity: sha512-RpmQdHVo8hCEHDVpO39zToS9jOhR6nw+/lQAzRNq9ErrGV9IeHM71XCn68svVl/euFeVW6BWX4p35gkhbOcSIQ==}
deprecated: Breaks compatibility with ES5, use v1.8.5
@@ -4149,6 +4149,18 @@ packages:
type-detect: 4.0.8
dev: true
+ /@sinonjs/commons@3.0.0:
+ resolution: {integrity: sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==}
+ dependencies:
+ type-detect: 4.0.8
+ dev: true
+
+ /@sinonjs/fake-timers@10.3.0:
+ resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==}
+ dependencies:
+ '@sinonjs/commons': 3.0.0
+ dev: true
+
/@sinonjs/fake-timers@9.1.2:
resolution: {integrity: sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==}
dependencies:
@@ -4744,7 +4756,7 @@ packages:
dependencies:
'@babel/core': 7.22.10
'@babel/preset-env': 7.22.10(@babel/core@7.22.10)
- '@babel/types': 7.23.0
+ '@babel/types': 7.23.4
'@storybook/csf': 0.1.1
'@storybook/csf-tools': 7.5.1
'@storybook/node-logger': 7.5.1
@@ -4910,9 +4922,9 @@ packages:
resolution: {integrity: sha512-YChGbT1/odLS4RLb2HtK7ixM7mH5s7G5nOsWGKXalbza4SFKZIU2UzllEUsA+X8YfxMHnCD5TC3xLfK0ByxmzQ==}
dependencies:
'@babel/generator': 7.23.0
- '@babel/parser': 7.23.0
+ '@babel/parser': 7.23.4
'@babel/traverse': 7.23.2
- '@babel/types': 7.23.0
+ '@babel/types': 7.23.4
'@storybook/csf': 0.1.1
'@storybook/types': 7.5.1
fs-extra: 11.1.1
@@ -5277,40 +5289,42 @@ packages:
- supports-color
dev: true
- /@storybook/test-runner@0.13.0(@types/node@18.11.9)(ts-node@10.9.1):
- resolution: {integrity: sha512-QIbfgia/iBy7PeUIwCYtPcyeZCHd21ebaPoMNIsRfwUW+VC12J4iG8cGDfOE7MGbMVz1Uu0elAEBB8NGP/YBtQ==}
+ /@storybook/test-runner@0.15.2(@types/node@18.11.9)(ts-node@10.9.1):
+ resolution: {integrity: sha512-nHwThLvxho9wNAVxtESoAcrQD7UolOAJISwcG9uz3bmtTIm7h5DMlpfX+2DKbJyq5REg8nhcauZv5iFvwBdn1Q==}
+ engines: {node: ^16.10.0 || ^18.0.0 || >=20.0.0}
hasBin: true
dependencies:
'@babel/core': 7.22.10
- '@babel/generator': 7.22.10
- '@babel/template': 7.22.5
- '@babel/types': 7.23.0
+ '@babel/generator': 7.23.0
+ '@babel/template': 7.22.15
+ '@babel/types': 7.23.4
'@storybook/core-common': 7.5.1
'@storybook/csf': 0.1.1
'@storybook/csf-tools': 7.5.1
- '@storybook/preview-api': 7.5.1
+ '@storybook/preview-api': 7.5.3
'@swc/core': 1.3.93
'@swc/jest': 0.2.29(@swc/core@1.3.93)
can-bind-to-host: 1.1.2
commander: 9.4.1
expect-playwright: 0.8.0
glob: 10.3.3
- jest: 28.1.3(@types/node@18.11.9)(ts-node@10.9.1)
- jest-circus: 28.1.3
- jest-environment-node: 28.1.3
- jest-junit: 14.0.1
- jest-playwright-preset: 2.0.0(jest-circus@28.1.3)(jest-environment-node@28.1.3)(jest-runner@28.1.3)(jest@28.1.3)
- jest-runner: 28.1.3
+ jest: 29.7.0(@types/node@18.11.9)(ts-node@10.9.1)
+ jest-circus: 29.7.0
+ jest-environment-node: 29.7.0
+ jest-junit: 16.0.0
+ jest-playwright-preset: 3.0.1(jest-circus@29.7.0)(jest-environment-node@29.7.0)(jest-runner@29.7.0)(jest@29.7.0)
+ jest-runner: 29.7.0
jest-serializer-html: 7.1.0
- jest-watch-typeahead: 2.2.2(jest@28.1.3)
+ jest-watch-typeahead: 2.2.2(jest@29.7.0)
node-fetch: 2.6.7
- playwright: 1.29.2
+ playwright: 1.32.2
read-pkg-up: 7.0.1
tempy: 1.0.1
ts-dedent: 2.2.0
transitivePeerDependencies:
- '@swc/helpers'
- '@types/node'
+ - babel-plugin-macros
- debug
- encoding
- node-notifier
@@ -5917,7 +5931,7 @@ packages:
/@types/babel__generator@7.6.6:
resolution: {integrity: sha512-66BXMKb/sUWbMdBNdMvajU7i/44RkrA3z/Yt1c7R5xejt8qh84iU54yUWCtm0QwGJlDcf/gg4zd/x4mpLAlb/w==}
dependencies:
- '@babel/types': 7.23.0
+ '@babel/types': 7.23.4
dev: true
/@types/babel__generator@7.6.7:
@@ -5929,8 +5943,8 @@ packages:
/@types/babel__template@7.4.3:
resolution: {integrity: sha512-ciwyCLeuRfxboZ4isgdNZi/tkt06m8Tw6uGbBSBgWrnnZGNXiEyM27xc/PjXGQLqlZ6ylbgHMnm7ccF9tCkOeQ==}
dependencies:
- '@babel/parser': 7.23.0
- '@babel/types': 7.23.0
+ '@babel/parser': 7.23.4
+ '@babel/types': 7.23.4
dev: true
/@types/babel__template@7.4.4:
@@ -5943,7 +5957,7 @@ packages:
/@types/babel__traverse@7.20.3:
resolution: {integrity: sha512-Lsh766rGEFbaxMIDH7Qa+Yha8cMVI3qAK6CHt3OR0YfxOIn5Z54iHiyDRycHrBqeIiqGa20Kpsv1cavfBKkRSw==}
dependencies:
- '@babel/types': 7.23.0
+ '@babel/types': 7.23.4
dev: true
/@types/babel__traverse@7.20.4:
@@ -7633,17 +7647,17 @@ packages:
'@babel/core': 7.22.10
dev: true
- /babel-jest@28.1.3(@babel/core@7.22.10):
- resolution: {integrity: sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ /babel-jest@29.3.1(@babel/core@7.22.10):
+ resolution: {integrity: sha512-aard+xnMoxgjwV70t0L6wkW/3HQQtV+O0PEimxKgzNqCJnbYmroPojdP2tqKSOAt8QAKV/uSZU8851M7B5+fcA==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
peerDependencies:
'@babel/core': ^7.8.0
dependencies:
'@babel/core': 7.22.10
- '@jest/transform': 28.1.3
- '@types/babel__core': 7.20.3
+ '@jest/transform': 29.7.0
+ '@types/babel__core': 7.20.4
babel-plugin-istanbul: 6.1.1
- babel-preset-jest: 28.1.3(@babel/core@7.22.10)
+ babel-preset-jest: 29.2.0(@babel/core@7.22.10)
chalk: 4.1.2
graceful-fs: 4.2.11
slash: 3.0.0
@@ -7651,17 +7665,17 @@ packages:
- supports-color
dev: true
- /babel-jest@29.3.1(@babel/core@7.22.10):
- resolution: {integrity: sha512-aard+xnMoxgjwV70t0L6wkW/3HQQtV+O0PEimxKgzNqCJnbYmroPojdP2tqKSOAt8QAKV/uSZU8851M7B5+fcA==}
+ /babel-jest@29.7.0(@babel/core@7.22.10):
+ resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
peerDependencies:
'@babel/core': ^7.8.0
dependencies:
'@babel/core': 7.22.10
- '@jest/transform': 29.3.1
- '@types/babel__core': 7.20.3
+ '@jest/transform': 29.7.0
+ '@types/babel__core': 7.20.4
babel-plugin-istanbul: 6.1.1
- babel-preset-jest: 29.2.0(@babel/core@7.22.10)
+ babel-preset-jest: 29.6.3(@babel/core@7.22.10)
chalk: 4.1.2
graceful-fs: 4.2.11
slash: 3.0.0
@@ -7720,24 +7734,24 @@ packages:
- supports-color
dev: true
- /babel-plugin-jest-hoist@28.1.3:
- resolution: {integrity: sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ /babel-plugin-jest-hoist@29.2.0:
+ resolution: {integrity: sha512-TnspP2WNiR3GLfCsUNHqeXw0RoQ2f9U5hQ5L3XFpwuO8htQmSrhh8qsB6vi5Yi8+kuynN1yjDjQsPfkebmB6ZA==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@babel/template': 7.22.15
- '@babel/types': 7.23.0
- '@types/babel__core': 7.20.3
- '@types/babel__traverse': 7.20.3
+ '@babel/types': 7.23.4
+ '@types/babel__core': 7.20.4
+ '@types/babel__traverse': 7.20.4
dev: true
- /babel-plugin-jest-hoist@29.2.0:
- resolution: {integrity: sha512-TnspP2WNiR3GLfCsUNHqeXw0RoQ2f9U5hQ5L3XFpwuO8htQmSrhh8qsB6vi5Yi8+kuynN1yjDjQsPfkebmB6ZA==}
+ /babel-plugin-jest-hoist@29.6.3:
+ resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@babel/template': 7.22.15
- '@babel/types': 7.23.0
- '@types/babel__core': 7.20.3
- '@types/babel__traverse': 7.20.3
+ '@babel/types': 7.23.4
+ '@types/babel__core': 7.20.4
+ '@types/babel__traverse': 7.20.4
dev: true
/babel-plugin-named-exports-order@0.0.2:
@@ -7807,25 +7821,25 @@ packages:
'@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.22.10)
dev: true
- /babel-preset-jest@28.1.3(@babel/core@7.22.10):
- resolution: {integrity: sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ /babel-preset-jest@29.2.0(@babel/core@7.22.10):
+ resolution: {integrity: sha512-z9JmMJppMxNv8N7fNRHvhMg9cvIkMxQBXgFkane3yKVEvEOP+kB50lk8DFRvF9PGqbyXxlmebKWhuDORO8RgdA==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
peerDependencies:
'@babel/core': ^7.0.0
dependencies:
'@babel/core': 7.22.10
- babel-plugin-jest-hoist: 28.1.3
+ babel-plugin-jest-hoist: 29.2.0
babel-preset-current-node-syntax: 1.0.1(@babel/core@7.22.10)
dev: true
- /babel-preset-jest@29.2.0(@babel/core@7.22.10):
- resolution: {integrity: sha512-z9JmMJppMxNv8N7fNRHvhMg9cvIkMxQBXgFkane3yKVEvEOP+kB50lk8DFRvF9PGqbyXxlmebKWhuDORO8RgdA==}
+ /babel-preset-jest@29.6.3(@babel/core@7.22.10):
+ resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
peerDependencies:
'@babel/core': ^7.0.0
dependencies:
'@babel/core': 7.22.10
- babel-plugin-jest-hoist: 29.2.0
+ babel-plugin-jest-hoist: 29.6.3
babel-preset-current-node-syntax: 1.0.1(@babel/core@7.22.10)
dev: true
@@ -8697,15 +8711,34 @@ packages:
typescript: 4.9.5
dev: true
- /create-require@1.1.1:
- resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
- dev: true
-
- /crelt@1.0.5:
- resolution: {integrity: sha512-+BO9wPPi+DWTDcNYhr/W90myha8ptzftZT+LwcmUbbok0rcP/fequmFYCw8NMoH7pkAZQzU78b3kYrlua5a9eA==}
- dev: false
-
- /cross-fetch@3.1.5:
+ /create-jest@29.7.0(@types/node@18.11.9)(ts-node@10.9.1):
+ resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ hasBin: true
+ dependencies:
+ '@jest/types': 29.6.3
+ chalk: 4.1.2
+ exit: 0.1.2
+ graceful-fs: 4.2.11
+ jest-config: 29.7.0(@types/node@18.11.9)(ts-node@10.9.1)
+ jest-util: 29.7.0
+ prompts: 2.4.2
+ transitivePeerDependencies:
+ - '@types/node'
+ - babel-plugin-macros
+ - supports-color
+ - ts-node
+ dev: true
+
+ /create-require@1.1.1:
+ resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
+ dev: true
+
+ /crelt@1.0.5:
+ resolution: {integrity: sha512-+BO9wPPi+DWTDcNYhr/W90myha8ptzftZT+LwcmUbbok0rcP/fequmFYCw8NMoH7pkAZQzU78b3kYrlua5a9eA==}
+ dev: false
+
+ /cross-fetch@3.1.5:
resolution: {integrity: sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==}
dependencies:
node-fetch: 2.6.7
@@ -9391,6 +9424,15 @@ packages:
resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==}
dev: true
+ /dedent@1.5.1:
+ resolution: {integrity: sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==}
+ peerDependencies:
+ babel-plugin-macros: ^3.1.0
+ peerDependenciesMeta:
+ babel-plugin-macros:
+ optional: true
+ dev: true
+
/deep-equal@2.1.0:
resolution: {integrity: sha512-2pxgvWu3Alv1PoWEyVg7HS8YhGlUFUV7N5oOvfL6d+7xAmLSemMwv/c8Zv/i9KFzxV5Kt5CAvQc70fLwVuf4UA==}
dependencies:
@@ -9556,16 +9598,16 @@ packages:
- supports-color
dev: true
- /diff-sequences@28.1.1:
- resolution: {integrity: sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
- dev: true
-
/diff-sequences@29.3.1:
resolution: {integrity: sha512-hlM3QR272NXCi4pq+N4Kok4kOp6EsgOM3ZSpJI7Da3UAs+Ttsi8MRmB6trM/lhyzUxGfOgnpkHtgqm5Q/CTcfQ==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dev: true
+ /diff-sequences@29.6.3:
+ resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ dev: true
+
/diff@4.0.2:
resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==}
engines: {node: '>=0.3.1'}
@@ -9764,11 +9806,6 @@ packages:
/electron-to-chromium@1.4.492:
resolution: {integrity: sha512-36K9b/6skMVwAIEsC7GiQ8I8N3soCALVSHqWHzNDtGemAcI9Xu8hP02cywWM0A794rTHm0b0zHPeLJHtgFVamQ==}
- /emittery@0.10.2:
- resolution: {integrity: sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==}
- engines: {node: '>=12'}
- dev: true
-
/emittery@0.13.1:
resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==}
engines: {node: '>=12'}
@@ -10553,7 +10590,7 @@ packages:
engines: {node: '>=8.3.0'}
dependencies:
'@babel/traverse': 7.23.2
- '@babel/types': 7.23.0
+ '@babel/types': 7.23.4
c8: 7.14.0
transitivePeerDependencies:
- supports-color
@@ -10649,17 +10686,6 @@ packages:
resolution: {integrity: sha512-+kn8561vHAY+dt+0gMqqj1oY+g5xWrsuGMk4QGxotT2WS545nVqqjs37z6hrYfIuucwqthzwJfCJUEYqixyljg==}
dev: true
- /expect@28.1.3:
- resolution: {integrity: sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
- dependencies:
- '@jest/expect-utils': 28.1.3
- jest-get-type: 28.0.2
- jest-matcher-utils: 28.1.3
- jest-message-util: 28.1.3
- jest-util: 28.1.3
- dev: true
-
/expect@29.3.1:
resolution: {integrity: sha512-gGb1yTgU30Q0O/tQq+z30KBWv24ApkMgFUpvKBkyLUBL68Wv8dHdJxTBZFl/iT8K/bqDHvUYRH6IIN3rToopPA==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -10671,6 +10697,17 @@ packages:
jest-util: 29.3.1
dev: true
+ /expect@29.7.0:
+ resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ dependencies:
+ '@jest/expect-utils': 29.7.0
+ jest-get-type: 29.6.3
+ jest-matcher-utils: 29.7.0
+ jest-message-util: 29.7.0
+ jest-util: 29.7.0
+ dev: true
+
/expr-eval@2.0.2:
resolution: {integrity: sha512-4EMSHGOPSwAfBiibw3ndnP0AvjDWLsMvGOvWEZ2F96IGk0bIVdjQisOHxReSkE13mHcfbuCiXw+G4y0zv6N8Eg==}
dev: false
@@ -11204,6 +11241,14 @@ packages:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
dev: true
+ /fsevents@2.3.2:
+ resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
/fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@@ -12475,7 +12520,7 @@ packages:
engines: {node: '>=8'}
dependencies:
'@babel/core': 7.22.10
- '@babel/parser': 7.23.0
+ '@babel/parser': 7.23.4
'@istanbuljs/schema': 0.1.3
istanbul-lib-coverage: 3.2.0
semver: 6.3.1
@@ -12483,6 +12528,19 @@ packages:
- supports-color
dev: true
+ /istanbul-lib-instrument@6.0.1:
+ resolution: {integrity: sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==}
+ engines: {node: '>=10'}
+ dependencies:
+ '@babel/core': 7.22.10
+ '@babel/parser': 7.23.4
+ '@istanbuljs/schema': 0.1.3
+ istanbul-lib-coverage: 3.2.0
+ semver: 7.5.4
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
/istanbul-lib-processinfo@2.0.3:
resolution: {integrity: sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==}
engines: {node: '>=8'}
@@ -12560,14 +12618,6 @@ packages:
moo-color: 1.0.3
dev: true
- /jest-changed-files@28.1.3:
- resolution: {integrity: sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
- dependencies:
- execa: 5.1.1
- p-limit: 3.1.0
- dev: true
-
/jest-changed-files@29.2.0:
resolution: {integrity: sha512-qPVmLLyBmvF5HJrY7krDisx6Voi8DmlV3GZYX0aFNbaQsZeoz1hfxcCMbqDGuQCxU1dJy9eYc2xscE8QrCCYaA==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -12576,63 +12626,47 @@ packages:
p-limit: 3.1.0
dev: true
- /jest-circus@28.1.3:
- resolution: {integrity: sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ /jest-changed-files@29.7.0:
+ resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@jest/environment': 28.1.3
- '@jest/expect': 28.1.3
- '@jest/test-result': 28.1.3
- '@jest/types': 28.1.3
- '@types/node': 18.18.4
- chalk: 4.1.2
- co: 4.6.0
- dedent: 0.7.0
- is-generator-fn: 2.1.0
- jest-each: 28.1.3
- jest-matcher-utils: 28.1.3
- jest-message-util: 28.1.3
- jest-runtime: 28.1.3
- jest-snapshot: 28.1.3
- jest-util: 28.1.3
+ execa: 5.1.1
+ jest-util: 29.7.0
p-limit: 3.1.0
- pretty-format: 28.1.3
- slash: 3.0.0
- stack-utils: 2.0.5
- transitivePeerDependencies:
- - supports-color
dev: true
- /jest-circus@29.3.1:
- resolution: {integrity: sha512-wpr26sEvwb3qQQbdlmei+gzp6yoSSoSL6GsLPxnuayZSMrSd5Ka7IjAvatpIernBvT2+Ic6RLTg+jSebScmasg==}
+ /jest-circus@29.7.0:
+ resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@jest/environment': 29.3.1
- '@jest/expect': 29.3.1
- '@jest/test-result': 29.3.1
- '@jest/types': 29.3.1
+ '@jest/environment': 29.7.0
+ '@jest/expect': 29.7.0
+ '@jest/test-result': 29.7.0
+ '@jest/types': 29.6.3
'@types/node': 18.18.4
chalk: 4.1.2
co: 4.6.0
- dedent: 0.7.0
+ dedent: 1.5.1
is-generator-fn: 2.1.0
- jest-each: 29.3.1
- jest-matcher-utils: 29.3.1
- jest-message-util: 29.3.1
- jest-runtime: 29.3.1
- jest-snapshot: 29.3.1
- jest-util: 29.3.1
+ jest-each: 29.7.0
+ jest-matcher-utils: 29.7.0
+ jest-message-util: 29.7.0
+ jest-runtime: 29.7.0
+ jest-snapshot: 29.7.0
+ jest-util: 29.7.0
p-limit: 3.1.0
- pretty-format: 29.3.1
+ pretty-format: 29.7.0
+ pure-rand: 6.0.4
slash: 3.0.0
stack-utils: 2.0.5
transitivePeerDependencies:
+ - babel-plugin-macros
- supports-color
dev: true
- /jest-cli@28.1.3(@types/node@18.11.9)(ts-node@10.9.1):
- resolution: {integrity: sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ /jest-cli@29.3.1(@types/node@18.11.9)(ts-node@10.9.1):
+ resolution: {integrity: sha512-TO/ewvwyvPOiBBuWZ0gm04z3WWP8TIK8acgPzE4IxgsLKQgb377NYGrQLc3Wl/7ndWzIH2CDNNsUjGxwLL43VQ==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
hasBin: true
peerDependencies:
node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
@@ -12640,26 +12674,27 @@ packages:
node-notifier:
optional: true
dependencies:
- '@jest/core': 28.1.3(ts-node@10.9.1)
- '@jest/test-result': 28.1.3
- '@jest/types': 28.1.3
+ '@jest/core': 29.3.1(ts-node@10.9.1)
+ '@jest/test-result': 29.3.1
+ '@jest/types': 29.3.1
chalk: 4.1.2
exit: 0.1.2
graceful-fs: 4.2.11
import-local: 3.1.0
- jest-config: 28.1.3(@types/node@18.11.9)(ts-node@10.9.1)
- jest-util: 28.1.3
- jest-validate: 28.1.3
+ jest-config: 29.3.1(@types/node@18.11.9)(ts-node@10.9.1)
+ jest-util: 29.3.1
+ jest-validate: 29.3.1
prompts: 2.4.2
yargs: 17.6.2
transitivePeerDependencies:
- '@types/node'
+ - babel-plugin-macros
- supports-color
- ts-node
dev: true
- /jest-cli@29.3.1(@types/node@18.11.9)(ts-node@10.9.1):
- resolution: {integrity: sha512-TO/ewvwyvPOiBBuWZ0gm04z3WWP8TIK8acgPzE4IxgsLKQgb377NYGrQLc3Wl/7ndWzIH2CDNNsUjGxwLL43VQ==}
+ /jest-cli@29.7.0(@types/node@18.11.9)(ts-node@10.9.1):
+ resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
hasBin: true
peerDependencies:
@@ -12668,27 +12703,27 @@ packages:
node-notifier:
optional: true
dependencies:
- '@jest/core': 29.3.1(ts-node@10.9.1)
- '@jest/test-result': 29.3.1
- '@jest/types': 29.3.1
+ '@jest/core': 29.7.0(ts-node@10.9.1)
+ '@jest/test-result': 29.7.0
+ '@jest/types': 29.6.3
chalk: 4.1.2
+ create-jest: 29.7.0(@types/node@18.11.9)(ts-node@10.9.1)
exit: 0.1.2
- graceful-fs: 4.2.11
import-local: 3.1.0
- jest-config: 29.3.1(@types/node@18.11.9)(ts-node@10.9.1)
- jest-util: 29.3.1
- jest-validate: 29.3.1
- prompts: 2.4.2
+ jest-config: 29.7.0(@types/node@18.11.9)(ts-node@10.9.1)
+ jest-util: 29.7.0
+ jest-validate: 29.7.0
yargs: 17.6.2
transitivePeerDependencies:
- '@types/node'
+ - babel-plugin-macros
- supports-color
- ts-node
dev: true
- /jest-config@28.1.3(@types/node@18.11.9)(ts-node@10.9.1):
- resolution: {integrity: sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ /jest-config@29.3.1(@types/node@18.11.9)(ts-node@10.9.1):
+ resolution: {integrity: sha512-y0tFHdj2WnTEhxmGUK1T7fgLen7YK4RtfvpLFBXfQkh2eMJAQq24Vx9472lvn5wg0MAO6B+iPfJfzdR9hJYalg==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
peerDependencies:
'@types/node': '*'
ts-node: '>=9.0.0'
@@ -12699,36 +12734,37 @@ packages:
optional: true
dependencies:
'@babel/core': 7.22.10
- '@jest/test-sequencer': 28.1.3
- '@jest/types': 28.1.3
+ '@jest/test-sequencer': 29.3.1
+ '@jest/types': 29.6.3
'@types/node': 18.11.9
- babel-jest: 28.1.3(@babel/core@7.22.10)
+ babel-jest: 29.3.1(@babel/core@7.22.10)
chalk: 4.1.2
ci-info: 3.5.0
deepmerge: 4.2.2
glob: 7.2.3
graceful-fs: 4.2.11
- jest-circus: 28.1.3
- jest-environment-node: 28.1.3
- jest-get-type: 28.0.2
- jest-regex-util: 28.0.2
- jest-resolve: 28.1.3
- jest-runner: 28.1.3
- jest-util: 28.1.3
- jest-validate: 28.1.3
+ jest-circus: 29.7.0
+ jest-environment-node: 29.7.0
+ jest-get-type: 29.2.0
+ jest-regex-util: 29.2.0
+ jest-resolve: 29.3.1
+ jest-runner: 29.7.0
+ jest-util: 29.7.0
+ jest-validate: 29.3.1
micromatch: 4.0.5
parse-json: 5.2.0
- pretty-format: 28.1.3
+ pretty-format: 29.7.0
slash: 3.0.0
strip-json-comments: 3.1.1
ts-node: 10.9.1(@swc/core@1.3.93)(@types/node@18.11.9)(typescript@4.9.5)
transitivePeerDependencies:
+ - babel-plugin-macros
- supports-color
dev: true
- /jest-config@28.1.3(@types/node@18.18.4)(ts-node@10.9.1):
- resolution: {integrity: sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ /jest-config@29.3.1(@types/node@18.18.4)(ts-node@10.9.1):
+ resolution: {integrity: sha512-y0tFHdj2WnTEhxmGUK1T7fgLen7YK4RtfvpLFBXfQkh2eMJAQq24Vx9472lvn5wg0MAO6B+iPfJfzdR9hJYalg==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
peerDependencies:
'@types/node': '*'
ts-node: '>=9.0.0'
@@ -12739,35 +12775,36 @@ packages:
optional: true
dependencies:
'@babel/core': 7.22.10
- '@jest/test-sequencer': 28.1.3
- '@jest/types': 28.1.3
+ '@jest/test-sequencer': 29.3.1
+ '@jest/types': 29.6.3
'@types/node': 18.18.4
- babel-jest: 28.1.3(@babel/core@7.22.10)
+ babel-jest: 29.3.1(@babel/core@7.22.10)
chalk: 4.1.2
ci-info: 3.5.0
deepmerge: 4.2.2
glob: 7.2.3
graceful-fs: 4.2.11
- jest-circus: 28.1.3
- jest-environment-node: 28.1.3
- jest-get-type: 28.0.2
- jest-regex-util: 28.0.2
- jest-resolve: 28.1.3
- jest-runner: 28.1.3
- jest-util: 28.1.3
- jest-validate: 28.1.3
+ jest-circus: 29.7.0
+ jest-environment-node: 29.7.0
+ jest-get-type: 29.2.0
+ jest-regex-util: 29.2.0
+ jest-resolve: 29.3.1
+ jest-runner: 29.7.0
+ jest-util: 29.7.0
+ jest-validate: 29.3.1
micromatch: 4.0.5
parse-json: 5.2.0
- pretty-format: 28.1.3
+ pretty-format: 29.7.0
slash: 3.0.0
strip-json-comments: 3.1.1
ts-node: 10.9.1(@swc/core@1.3.93)(@types/node@18.11.9)(typescript@4.9.5)
transitivePeerDependencies:
+ - babel-plugin-macros
- supports-color
dev: true
- /jest-config@29.3.1(@types/node@18.11.9)(ts-node@10.9.1):
- resolution: {integrity: sha512-y0tFHdj2WnTEhxmGUK1T7fgLen7YK4RtfvpLFBXfQkh2eMJAQq24Vx9472lvn5wg0MAO6B+iPfJfzdR9hJYalg==}
+ /jest-config@29.7.0(@types/node@18.11.9)(ts-node@10.9.1):
+ resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
peerDependencies:
'@types/node': '*'
@@ -12779,35 +12816,36 @@ packages:
optional: true
dependencies:
'@babel/core': 7.22.10
- '@jest/test-sequencer': 29.3.1
- '@jest/types': 29.3.1
+ '@jest/test-sequencer': 29.7.0
+ '@jest/types': 29.6.3
'@types/node': 18.11.9
- babel-jest: 29.3.1(@babel/core@7.22.10)
+ babel-jest: 29.7.0(@babel/core@7.22.10)
chalk: 4.1.2
ci-info: 3.5.0
deepmerge: 4.2.2
glob: 7.2.3
graceful-fs: 4.2.11
- jest-circus: 29.3.1
- jest-environment-node: 29.3.1
- jest-get-type: 29.2.0
- jest-regex-util: 29.2.0
- jest-resolve: 29.3.1
- jest-runner: 29.3.1
- jest-util: 29.3.1
- jest-validate: 29.3.1
+ jest-circus: 29.7.0
+ jest-environment-node: 29.7.0
+ jest-get-type: 29.6.3
+ jest-regex-util: 29.6.3
+ jest-resolve: 29.7.0
+ jest-runner: 29.7.0
+ jest-util: 29.7.0
+ jest-validate: 29.7.0
micromatch: 4.0.5
parse-json: 5.2.0
- pretty-format: 29.3.1
+ pretty-format: 29.7.0
slash: 3.0.0
strip-json-comments: 3.1.1
ts-node: 10.9.1(@swc/core@1.3.93)(@types/node@18.11.9)(typescript@4.9.5)
transitivePeerDependencies:
+ - babel-plugin-macros
- supports-color
dev: true
- /jest-config@29.3.1(@types/node@18.18.4)(ts-node@10.9.1):
- resolution: {integrity: sha512-y0tFHdj2WnTEhxmGUK1T7fgLen7YK4RtfvpLFBXfQkh2eMJAQq24Vx9472lvn5wg0MAO6B+iPfJfzdR9hJYalg==}
+ /jest-config@29.7.0(@types/node@18.18.4)(ts-node@10.9.1):
+ resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
peerDependencies:
'@types/node': '*'
@@ -12819,43 +12857,34 @@ packages:
optional: true
dependencies:
'@babel/core': 7.22.10
- '@jest/test-sequencer': 29.3.1
- '@jest/types': 29.3.1
+ '@jest/test-sequencer': 29.7.0
+ '@jest/types': 29.6.3
'@types/node': 18.18.4
- babel-jest: 29.3.1(@babel/core@7.22.10)
+ babel-jest: 29.7.0(@babel/core@7.22.10)
chalk: 4.1.2
ci-info: 3.5.0
deepmerge: 4.2.2
glob: 7.2.3
graceful-fs: 4.2.11
- jest-circus: 29.3.1
- jest-environment-node: 29.3.1
- jest-get-type: 29.2.0
- jest-regex-util: 29.2.0
- jest-resolve: 29.3.1
- jest-runner: 29.3.1
- jest-util: 29.3.1
- jest-validate: 29.3.1
+ jest-circus: 29.7.0
+ jest-environment-node: 29.7.0
+ jest-get-type: 29.6.3
+ jest-regex-util: 29.6.3
+ jest-resolve: 29.7.0
+ jest-runner: 29.7.0
+ jest-util: 29.7.0
+ jest-validate: 29.7.0
micromatch: 4.0.5
parse-json: 5.2.0
- pretty-format: 29.3.1
+ pretty-format: 29.7.0
slash: 3.0.0
strip-json-comments: 3.1.1
ts-node: 10.9.1(@swc/core@1.3.93)(@types/node@18.11.9)(typescript@4.9.5)
transitivePeerDependencies:
+ - babel-plugin-macros
- supports-color
dev: true
- /jest-diff@28.1.3:
- resolution: {integrity: sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
- dependencies:
- chalk: 4.1.2
- diff-sequences: 28.1.1
- jest-get-type: 28.0.2
- pretty-format: 28.1.3
- dev: true
-
/jest-diff@29.3.1:
resolution: {integrity: sha512-vU8vyiO7568tmin2lA3r2DP8oRvzhvRcD4DjpXc6uGveQodyk7CKLhQlCSiwgx3g0pFaE88/KLZ0yaTWMc4Uiw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -12863,14 +12892,17 @@ packages:
chalk: 4.1.2
diff-sequences: 29.3.1
jest-get-type: 29.2.0
- pretty-format: 29.3.1
+ pretty-format: 29.7.0
dev: true
- /jest-docblock@28.1.1:
- resolution: {integrity: sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ /jest-diff@29.7.0:
+ resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- detect-newline: 3.1.0
+ chalk: 4.1.2
+ diff-sequences: 29.6.3
+ jest-get-type: 29.6.3
+ pretty-format: 29.7.0
dev: true
/jest-docblock@29.2.0:
@@ -12880,26 +12912,22 @@ packages:
detect-newline: 3.1.0
dev: true
- /jest-each@28.1.3:
- resolution: {integrity: sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ /jest-docblock@29.7.0:
+ resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@jest/types': 28.1.3
- chalk: 4.1.2
- jest-get-type: 28.0.2
- jest-util: 28.1.3
- pretty-format: 28.1.3
+ detect-newline: 3.1.0
dev: true
- /jest-each@29.3.1:
- resolution: {integrity: sha512-qrZH7PmFB9rEzCSl00BWjZYuS1BSOH8lLuC0azQE9lQrAx3PWGKHTDudQiOSwIy5dGAJh7KA0ScYlCP7JxvFYA==}
+ /jest-each@29.7.0:
+ resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@jest/types': 29.3.1
+ '@jest/types': 29.6.3
chalk: 4.1.2
- jest-get-type: 29.2.0
- jest-util: 29.3.1
- pretty-format: 29.3.1
+ jest-get-type: 29.6.3
+ jest-util: 29.7.0
+ pretty-format: 29.7.0
dev: true
/jest-environment-jsdom@29.3.1:
@@ -12925,33 +12953,16 @@ packages:
- utf-8-validate
dev: true
- /jest-environment-node@28.1.3:
- resolution: {integrity: sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
- dependencies:
- '@jest/environment': 28.1.3
- '@jest/fake-timers': 28.1.3
- '@jest/types': 28.1.3
- '@types/node': 18.18.4
- jest-mock: 28.1.3
- jest-util: 28.1.3
- dev: true
-
- /jest-environment-node@29.3.1:
- resolution: {integrity: sha512-xm2THL18Xf5sIHoU7OThBPtuH6Lerd+Y1NLYiZJlkE3hbE+7N7r8uvHIl/FkZ5ymKXJe/11SQuf3fv4v6rUMag==}
+ /jest-environment-node@29.7.0:
+ resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@jest/environment': 29.3.1
- '@jest/fake-timers': 29.3.1
- '@jest/types': 29.3.1
+ '@jest/environment': 29.7.0
+ '@jest/fake-timers': 29.7.0
+ '@jest/types': 29.6.3
'@types/node': 18.18.4
- jest-mock: 29.3.1
- jest-util: 29.3.1
- dev: true
-
- /jest-get-type@28.0.2:
- resolution: {integrity: sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ jest-mock: 29.7.0
+ jest-util: 29.7.0
dev: true
/jest-get-type@29.2.0:
@@ -12959,38 +12970,43 @@ packages:
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dev: true
- /jest-haste-map@28.1.3:
- resolution: {integrity: sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ /jest-get-type@29.6.3:
+ resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ dev: true
+
+ /jest-haste-map@29.3.1:
+ resolution: {integrity: sha512-/FFtvoG1xjbbPXQLFef+WSU4yrc0fc0Dds6aRPBojUid7qlPqZvxdUBA03HW0fnVHXVCnCdkuoghYItKNzc/0A==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@jest/types': 28.1.3
+ '@jest/types': 29.6.3
'@types/graceful-fs': 4.1.5
'@types/node': 18.18.4
anymatch: 3.1.2
fb-watchman: 2.0.2
graceful-fs: 4.2.11
- jest-regex-util: 28.0.2
- jest-util: 28.1.3
- jest-worker: 28.1.3
+ jest-regex-util: 29.2.0
+ jest-util: 29.7.0
+ jest-worker: 29.3.1
micromatch: 4.0.5
walker: 1.0.8
optionalDependencies:
fsevents: 2.3.3
dev: true
- /jest-haste-map@29.3.1:
- resolution: {integrity: sha512-/FFtvoG1xjbbPXQLFef+WSU4yrc0fc0Dds6aRPBojUid7qlPqZvxdUBA03HW0fnVHXVCnCdkuoghYItKNzc/0A==}
+ /jest-haste-map@29.7.0:
+ resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@jest/types': 29.3.1
+ '@jest/types': 29.6.3
'@types/graceful-fs': 4.1.5
'@types/node': 18.18.4
anymatch: 3.1.2
fb-watchman: 2.0.2
graceful-fs: 4.2.11
- jest-regex-util: 29.2.0
- jest-util: 29.3.1
- jest-worker: 29.3.1
+ jest-regex-util: 29.6.3
+ jest-util: 29.7.0
+ jest-worker: 29.7.0
micromatch: 4.0.5
walker: 1.0.8
optionalDependencies:
@@ -13015,8 +13031,8 @@ packages:
ssim.js: 3.5.0
dev: true
- /jest-junit@14.0.1:
- resolution: {integrity: sha512-h7/wwzPbllgpQhhVcRzRC76/cc89GlazThoV1fDxcALkf26IIlRsu/AcTG64f4nR2WPE3Cbd+i/sVf+NCUHrWQ==}
+ /jest-junit@16.0.0:
+ resolution: {integrity: sha512-A94mmw6NfJab4Fg/BlvVOUXzXgF0XIH6EmTgJ5NDPp4xoKq0Kr7sErb+4Xs9nZvu58pJojz5RFGpqnZYJTrRfQ==}
engines: {node: '>=10.12.0'}
dependencies:
mkdirp: 1.0.4
@@ -13025,30 +13041,20 @@ packages:
xml: 1.0.1
dev: true
- /jest-leak-detector@28.1.3:
- resolution: {integrity: sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
- dependencies:
- jest-get-type: 28.0.2
- pretty-format: 28.1.3
- dev: true
-
/jest-leak-detector@29.3.1:
resolution: {integrity: sha512-3DA/VVXj4zFOPagGkuqHnSQf1GZBmmlagpguxEERO6Pla2g84Q1MaVIB3YMxgUaFIaYag8ZnTyQgiZ35YEqAQA==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- jest-get-type: 29.2.0
- pretty-format: 29.3.1
+ jest-get-type: 29.6.3
+ pretty-format: 29.7.0
dev: true
- /jest-matcher-utils@28.1.3:
- resolution: {integrity: sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ /jest-leak-detector@29.7.0:
+ resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- chalk: 4.1.2
- jest-diff: 28.1.3
- jest-get-type: 28.0.2
- pretty-format: 28.1.3
+ jest-get-type: 29.6.3
+ pretty-format: 29.7.0
dev: true
/jest-matcher-utils@29.3.1:
@@ -13061,19 +13067,14 @@ packages:
pretty-format: 29.3.1
dev: true
- /jest-message-util@28.1.3:
- resolution: {integrity: sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ /jest-matcher-utils@29.7.0:
+ resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@babel/code-frame': 7.22.13
- '@jest/types': 28.1.3
- '@types/stack-utils': 2.0.1
chalk: 4.1.2
- graceful-fs: 4.2.11
- micromatch: 4.0.5
- pretty-format: 28.1.3
- slash: 3.0.0
- stack-utils: 2.0.5
+ jest-diff: 29.7.0
+ jest-get-type: 29.6.3
+ pretty-format: 29.7.0
dev: true
/jest-message-util@29.3.1:
@@ -13081,7 +13082,7 @@ packages:
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@babel/code-frame': 7.22.13
- '@jest/types': 29.3.1
+ '@jest/types': 29.6.3
'@types/stack-utils': 2.0.1
chalk: 4.1.2
graceful-fs: 4.2.11
@@ -13091,12 +13092,19 @@ packages:
stack-utils: 2.0.5
dev: true
- /jest-mock@28.1.3:
- resolution: {integrity: sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ /jest-message-util@29.7.0:
+ resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@jest/types': 28.1.3
- '@types/node': 18.18.4
+ '@babel/code-frame': 7.22.13
+ '@jest/types': 29.6.3
+ '@types/stack-utils': 2.0.1
+ chalk: 4.1.2
+ graceful-fs: 4.2.11
+ micromatch: 4.0.5
+ pretty-format: 29.7.0
+ slash: 3.0.0
+ stack-utils: 2.0.5
dev: true
/jest-mock@29.3.1:
@@ -13108,22 +13116,31 @@ packages:
jest-util: 29.3.1
dev: true
- /jest-playwright-preset@2.0.0(jest-circus@28.1.3)(jest-environment-node@28.1.3)(jest-runner@28.1.3)(jest@28.1.3):
- resolution: {integrity: sha512-pV5ruTJJMen3lwshUL4dlSqLlP8z4q9MXqWJkmy+sB6HYfzXoqBHzhl+5hslznhnSVTe4Dwu+reiiwcUJpYUbw==}
+ /jest-mock@29.7.0:
+ resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ dependencies:
+ '@jest/types': 29.6.3
+ '@types/node': 18.18.4
+ jest-util: 29.7.0
+ dev: true
+
+ /jest-playwright-preset@3.0.1(jest-circus@29.7.0)(jest-environment-node@29.7.0)(jest-runner@29.7.0)(jest@29.7.0):
+ resolution: {integrity: sha512-tHqv+JUmheNMZpmH7XyT5CAMHr3ExTUIY9baMPzcJiLYPvCaPTwig9YvuGGnXV2n+Epmch0Ld4429g6py0nq0w==}
peerDependencies:
- jest: ^28.0.0
- jest-circus: ^28.0.0
- jest-environment-node: ^28.0.0
- jest-runner: ^28.0.0
+ jest: ^29.3.1
+ jest-circus: ^29.3.1
+ jest-environment-node: ^29.3.1
+ jest-runner: ^29.3.1
dependencies:
expect-playwright: 0.8.0
- jest: 28.1.3(@types/node@18.11.9)(ts-node@10.9.1)
- jest-circus: 28.1.3
- jest-environment-node: 28.1.3
+ jest: 29.7.0(@types/node@18.11.9)(ts-node@10.9.1)
+ jest-circus: 29.7.0
+ jest-environment-node: 29.7.0
jest-process-manager: 0.3.1
- jest-runner: 28.1.3
+ jest-runner: 29.7.0
nyc: 15.1.0
- playwright-core: 1.29.2
+ playwright-core: 1.32.2
rimraf: 3.0.2
uuid: 8.3.2
transitivePeerDependencies:
@@ -13131,7 +13148,7 @@ packages:
- supports-color
dev: true
- /jest-pnp-resolver@1.2.2(jest-resolve@28.1.3):
+ /jest-pnp-resolver@1.2.2(jest-resolve@29.3.1):
resolution: {integrity: sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==}
engines: {node: '>=6'}
peerDependencies:
@@ -13140,10 +13157,10 @@ packages:
jest-resolve:
optional: true
dependencies:
- jest-resolve: 28.1.3
+ jest-resolve: 29.3.1
dev: true
- /jest-pnp-resolver@1.2.2(jest-resolve@29.3.1):
+ /jest-pnp-resolver@1.2.2(jest-resolve@29.7.0):
resolution: {integrity: sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==}
engines: {node: '>=6'}
peerDependencies:
@@ -13152,7 +13169,7 @@ packages:
jest-resolve:
optional: true
dependencies:
- jest-resolve: 29.3.1
+ jest-resolve: 29.7.0
dev: true
/jest-process-manager@0.3.1:
@@ -13173,24 +13190,14 @@ packages:
- supports-color
dev: true
- /jest-regex-util@28.0.2:
- resolution: {integrity: sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
- dev: true
-
/jest-regex-util@29.2.0:
resolution: {integrity: sha512-6yXn0kg2JXzH30cr2NlThF+70iuO/3irbaB4mh5WyqNIvLLP+B6sFdluO1/1RJmslyh/f9osnefECflHvTbwVA==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dev: true
- /jest-resolve-dependencies@28.1.3:
- resolution: {integrity: sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
- dependencies:
- jest-regex-util: 28.0.2
- jest-snapshot: 28.1.3
- transitivePeerDependencies:
- - supports-color
+ /jest-regex-util@29.6.3:
+ resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dev: true
/jest-resolve-dependencies@29.3.1:
@@ -13198,24 +13205,19 @@ packages:
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
jest-regex-util: 29.2.0
- jest-snapshot: 29.3.1
+ jest-snapshot: 29.7.0
transitivePeerDependencies:
- supports-color
dev: true
- /jest-resolve@28.1.3:
- resolution: {integrity: sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ /jest-resolve-dependencies@29.7.0:
+ resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- chalk: 4.1.2
- graceful-fs: 4.2.11
- jest-haste-map: 28.1.3
- jest-pnp-resolver: 1.2.2(jest-resolve@28.1.3)
- jest-util: 28.1.3
- jest-validate: 28.1.3
- resolve: 1.22.1
- resolve.exports: 1.1.0
- slash: 3.0.0
+ jest-regex-util: 29.6.3
+ jest-snapshot: 29.7.0
+ transitivePeerDependencies:
+ - supports-color
dev: true
/jest-resolve@29.3.1:
@@ -13226,40 +13228,26 @@ packages:
graceful-fs: 4.2.11
jest-haste-map: 29.3.1
jest-pnp-resolver: 1.2.2(jest-resolve@29.3.1)
- jest-util: 29.3.1
+ jest-util: 29.7.0
jest-validate: 29.3.1
resolve: 1.22.1
resolve.exports: 1.1.0
slash: 3.0.0
dev: true
- /jest-runner@28.1.3:
- resolution: {integrity: sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ /jest-resolve@29.7.0:
+ resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@jest/console': 28.1.3
- '@jest/environment': 28.1.3
- '@jest/test-result': 28.1.3
- '@jest/transform': 28.1.3
- '@jest/types': 28.1.3
- '@types/node': 18.18.4
chalk: 4.1.2
- emittery: 0.10.2
graceful-fs: 4.2.11
- jest-docblock: 28.1.1
- jest-environment-node: 28.1.3
- jest-haste-map: 28.1.3
- jest-leak-detector: 28.1.3
- jest-message-util: 28.1.3
- jest-resolve: 28.1.3
- jest-runtime: 28.1.3
- jest-util: 28.1.3
- jest-watcher: 28.1.3
- jest-worker: 28.1.3
- p-limit: 3.1.0
- source-map-support: 0.5.13
- transitivePeerDependencies:
- - supports-color
+ jest-haste-map: 29.7.0
+ jest-pnp-resolver: 1.2.2(jest-resolve@29.7.0)
+ jest-util: 29.7.0
+ jest-validate: 29.7.0
+ resolve: 1.22.1
+ resolve.exports: 2.0.2
+ slash: 3.0.0
dev: true
/jest-runner@29.3.1:
@@ -13267,22 +13255,22 @@ packages:
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@jest/console': 29.3.1
- '@jest/environment': 29.3.1
- '@jest/test-result': 29.3.1
+ '@jest/environment': 29.7.0
+ '@jest/test-result': 29.7.0
'@jest/transform': 29.3.1
- '@jest/types': 29.3.1
+ '@jest/types': 29.6.3
'@types/node': 18.18.4
chalk: 4.1.2
emittery: 0.13.1
graceful-fs: 4.2.11
jest-docblock: 29.2.0
- jest-environment-node: 29.3.1
+ jest-environment-node: 29.7.0
jest-haste-map: 29.3.1
jest-leak-detector: 29.3.1
- jest-message-util: 29.3.1
+ jest-message-util: 29.7.0
jest-resolve: 29.3.1
- jest-runtime: 29.3.1
- jest-util: 29.3.1
+ jest-runtime: 29.7.0
+ jest-util: 29.7.0
jest-watcher: 29.3.1
jest-worker: 29.3.1
p-limit: 3.1.0
@@ -13291,32 +13279,31 @@ packages:
- supports-color
dev: true
- /jest-runtime@28.1.3:
- resolution: {integrity: sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ /jest-runner@29.7.0:
+ resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@jest/environment': 28.1.3
- '@jest/fake-timers': 28.1.3
- '@jest/globals': 28.1.3
- '@jest/source-map': 28.1.2
- '@jest/test-result': 28.1.3
- '@jest/transform': 28.1.3
- '@jest/types': 28.1.3
+ '@jest/console': 29.7.0
+ '@jest/environment': 29.7.0
+ '@jest/test-result': 29.7.0
+ '@jest/transform': 29.7.0
+ '@jest/types': 29.6.3
+ '@types/node': 18.18.4
chalk: 4.1.2
- cjs-module-lexer: 1.2.2
- collect-v8-coverage: 1.0.1
- execa: 5.1.1
- glob: 7.2.3
+ emittery: 0.13.1
graceful-fs: 4.2.11
- jest-haste-map: 28.1.3
- jest-message-util: 28.1.3
- jest-mock: 28.1.3
- jest-regex-util: 28.0.2
- jest-resolve: 28.1.3
- jest-snapshot: 28.1.3
- jest-util: 28.1.3
- slash: 3.0.0
- strip-bom: 4.0.0
+ jest-docblock: 29.7.0
+ jest-environment-node: 29.7.0
+ jest-haste-map: 29.7.0
+ jest-leak-detector: 29.7.0
+ jest-message-util: 29.7.0
+ jest-resolve: 29.7.0
+ jest-runtime: 29.7.0
+ jest-util: 29.7.0
+ jest-watcher: 29.7.0
+ jest-worker: 29.7.0
+ p-limit: 3.1.0
+ source-map-support: 0.5.13
transitivePeerDependencies:
- supports-color
dev: true
@@ -13325,13 +13312,13 @@ packages:
resolution: {integrity: sha512-jLzkIxIqXwBEOZx7wx9OO9sxoZmgT2NhmQKzHQm1xwR1kNW/dn0OjxR424VwHHf1SPN6Qwlb5pp1oGCeFTQ62A==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@jest/environment': 29.3.1
- '@jest/fake-timers': 29.3.1
+ '@jest/environment': 29.7.0
+ '@jest/fake-timers': 29.7.0
'@jest/globals': 29.3.1
'@jest/source-map': 29.2.0
- '@jest/test-result': 29.3.1
+ '@jest/test-result': 29.7.0
'@jest/transform': 29.3.1
- '@jest/types': 29.3.1
+ '@jest/types': 29.6.3
'@types/node': 18.18.4
chalk: 4.1.2
cjs-module-lexer: 1.2.2
@@ -13339,12 +13326,42 @@ packages:
glob: 7.2.3
graceful-fs: 4.2.11
jest-haste-map: 29.3.1
- jest-message-util: 29.3.1
- jest-mock: 29.3.1
+ jest-message-util: 29.7.0
+ jest-mock: 29.7.0
jest-regex-util: 29.2.0
jest-resolve: 29.3.1
- jest-snapshot: 29.3.1
- jest-util: 29.3.1
+ jest-snapshot: 29.7.0
+ jest-util: 29.7.0
+ slash: 3.0.0
+ strip-bom: 4.0.0
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /jest-runtime@29.7.0:
+ resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ dependencies:
+ '@jest/environment': 29.7.0
+ '@jest/fake-timers': 29.7.0
+ '@jest/globals': 29.7.0
+ '@jest/source-map': 29.6.3
+ '@jest/test-result': 29.7.0
+ '@jest/transform': 29.7.0
+ '@jest/types': 29.6.3
+ '@types/node': 18.18.4
+ chalk: 4.1.2
+ cjs-module-lexer: 1.2.2
+ collect-v8-coverage: 1.0.1
+ glob: 7.2.3
+ graceful-fs: 4.2.11
+ jest-haste-map: 29.7.0
+ jest-message-util: 29.7.0
+ jest-mock: 29.7.0
+ jest-regex-util: 29.6.3
+ jest-resolve: 29.7.0
+ jest-snapshot: 29.7.0
+ jest-util: 29.7.0
slash: 3.0.0
strip-bom: 4.0.0
transitivePeerDependencies:
@@ -13357,74 +13374,71 @@ packages:
diffable-html: 4.1.0
dev: true
- /jest-snapshot@28.1.3:
- resolution: {integrity: sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ /jest-snapshot@29.3.1:
+ resolution: {integrity: sha512-+3JOc+s28upYLI2OJM4PWRGK9AgpsMs/ekNryUV0yMBClT9B1DF2u2qay8YxcQd338PPYSFNb0lsar1B49sLDA==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@babel/core': 7.22.10
'@babel/generator': 7.23.0
+ '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.10)
'@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.22.10)
'@babel/traverse': 7.23.2
- '@babel/types': 7.23.0
- '@jest/expect-utils': 28.1.3
- '@jest/transform': 28.1.3
- '@jest/types': 28.1.3
+ '@babel/types': 7.23.4
+ '@jest/expect-utils': 29.3.1
+ '@jest/transform': 29.3.1
+ '@jest/types': 29.6.3
'@types/babel__traverse': 7.20.3
'@types/prettier': 2.7.1
babel-preset-current-node-syntax: 1.0.1(@babel/core@7.22.10)
chalk: 4.1.2
- expect: 28.1.3
+ expect: 29.3.1
graceful-fs: 4.2.11
- jest-diff: 28.1.3
- jest-get-type: 28.0.2
- jest-haste-map: 28.1.3
- jest-matcher-utils: 28.1.3
- jest-message-util: 28.1.3
- jest-util: 28.1.3
+ jest-diff: 29.3.1
+ jest-get-type: 29.2.0
+ jest-haste-map: 29.3.1
+ jest-matcher-utils: 29.7.0
+ jest-message-util: 29.7.0
+ jest-util: 29.7.0
natural-compare: 1.4.0
- pretty-format: 28.1.3
+ pretty-format: 29.7.0
semver: 7.5.4
transitivePeerDependencies:
- supports-color
dev: true
- /jest-snapshot@29.3.1:
- resolution: {integrity: sha512-+3JOc+s28upYLI2OJM4PWRGK9AgpsMs/ekNryUV0yMBClT9B1DF2u2qay8YxcQd338PPYSFNb0lsar1B49sLDA==}
+ /jest-snapshot@29.7.0:
+ resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@babel/core': 7.22.10
'@babel/generator': 7.23.0
'@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.10)
'@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.22.10)
- '@babel/traverse': 7.23.2
- '@babel/types': 7.23.0
- '@jest/expect-utils': 29.3.1
- '@jest/transform': 29.3.1
- '@jest/types': 29.3.1
- '@types/babel__traverse': 7.20.3
- '@types/prettier': 2.7.1
+ '@babel/types': 7.23.4
+ '@jest/expect-utils': 29.7.0
+ '@jest/transform': 29.7.0
+ '@jest/types': 29.6.3
babel-preset-current-node-syntax: 1.0.1(@babel/core@7.22.10)
chalk: 4.1.2
- expect: 29.3.1
+ expect: 29.7.0
graceful-fs: 4.2.11
- jest-diff: 29.3.1
- jest-get-type: 29.2.0
- jest-haste-map: 29.3.1
- jest-matcher-utils: 29.3.1
- jest-message-util: 29.3.1
- jest-util: 29.3.1
+ jest-diff: 29.7.0
+ jest-get-type: 29.6.3
+ jest-matcher-utils: 29.7.0
+ jest-message-util: 29.7.0
+ jest-util: 29.7.0
natural-compare: 1.4.0
- pretty-format: 29.3.1
+ pretty-format: 29.7.0
semver: 7.5.4
transitivePeerDependencies:
- supports-color
dev: true
- /jest-util@28.1.3:
- resolution: {integrity: sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ /jest-util@29.3.1:
+ resolution: {integrity: sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@jest/types': 28.1.3
+ '@jest/types': 29.3.1
'@types/node': 18.18.4
chalk: 4.1.2
ci-info: 3.5.0
@@ -13432,11 +13446,11 @@ packages:
picomatch: 2.3.1
dev: true
- /jest-util@29.3.1:
- resolution: {integrity: sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ==}
+ /jest-util@29.7.0:
+ resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@jest/types': 29.3.1
+ '@jest/types': 29.6.3
'@types/node': 18.18.4
chalk: 4.1.2
ci-info: 3.5.0
@@ -13444,31 +13458,31 @@ packages:
picomatch: 2.3.1
dev: true
- /jest-validate@28.1.3:
- resolution: {integrity: sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ /jest-validate@29.3.1:
+ resolution: {integrity: sha512-N9Lr3oYR2Mpzuelp1F8negJR3YE+L1ebk1rYA5qYo9TTY3f9OWdptLoNSPP9itOCBIRBqjt/S5XHlzYglLN67g==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@jest/types': 28.1.3
+ '@jest/types': 29.6.3
camelcase: 6.3.0
chalk: 4.1.2
- jest-get-type: 28.0.2
+ jest-get-type: 29.2.0
leven: 3.1.0
- pretty-format: 28.1.3
+ pretty-format: 29.7.0
dev: true
- /jest-validate@29.3.1:
- resolution: {integrity: sha512-N9Lr3oYR2Mpzuelp1F8negJR3YE+L1ebk1rYA5qYo9TTY3f9OWdptLoNSPP9itOCBIRBqjt/S5XHlzYglLN67g==}
+ /jest-validate@29.7.0:
+ resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@jest/types': 29.3.1
+ '@jest/types': 29.6.3
camelcase: 6.3.0
chalk: 4.1.2
- jest-get-type: 29.2.0
+ jest-get-type: 29.6.3
leven: 3.1.0
- pretty-format: 29.3.1
+ pretty-format: 29.7.0
dev: true
- /jest-watch-typeahead@2.2.2(jest@28.1.3):
+ /jest-watch-typeahead@2.2.2(jest@29.7.0):
resolution: {integrity: sha512-+QgOFW4o5Xlgd6jGS5X37i08tuuXNW8X0CV9WNFi+3n8ExCIP+E1melYhvYLjv5fE6D0yyzk74vsSO8I6GqtvQ==}
engines: {node: ^14.17.0 || ^16.10.0 || >=18.0.0}
peerDependencies:
@@ -13476,39 +13490,39 @@ packages:
dependencies:
ansi-escapes: 6.0.0
chalk: 5.2.0
- jest: 28.1.3(@types/node@18.11.9)(ts-node@10.9.1)
- jest-regex-util: 29.2.0
- jest-watcher: 29.3.1
+ jest: 29.7.0(@types/node@18.11.9)(ts-node@10.9.1)
+ jest-regex-util: 29.6.3
+ jest-watcher: 29.7.0
slash: 5.0.0
string-length: 5.0.1
strip-ansi: 7.0.1
dev: true
- /jest-watcher@28.1.3:
- resolution: {integrity: sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ /jest-watcher@29.3.1:
+ resolution: {integrity: sha512-RspXG2BQFDsZSRKGCT/NiNa8RkQ1iKAjrO0//soTMWx/QUt+OcxMqMSBxz23PYGqUuWm2+m2mNNsmj0eIoOaFg==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@jest/test-result': 28.1.3
- '@jest/types': 28.1.3
+ '@jest/test-result': 29.7.0
+ '@jest/types': 29.6.3
'@types/node': 18.18.4
ansi-escapes: 4.3.2
chalk: 4.1.2
- emittery: 0.10.2
- jest-util: 28.1.3
+ emittery: 0.13.1
+ jest-util: 29.7.0
string-length: 4.0.2
dev: true
- /jest-watcher@29.3.1:
- resolution: {integrity: sha512-RspXG2BQFDsZSRKGCT/NiNa8RkQ1iKAjrO0//soTMWx/QUt+OcxMqMSBxz23PYGqUuWm2+m2mNNsmj0eIoOaFg==}
+ /jest-watcher@29.7.0:
+ resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@jest/test-result': 29.3.1
- '@jest/types': 29.3.1
+ '@jest/test-result': 29.7.0
+ '@jest/types': 29.6.3
'@types/node': 18.18.4
ansi-escapes: 4.3.2
chalk: 4.1.2
emittery: 0.13.1
- jest-util: 29.3.1
+ jest-util: 29.7.0
string-length: 4.0.2
dev: true
@@ -13521,28 +13535,29 @@ packages:
supports-color: 8.1.1
dev: true
- /jest-worker@28.1.3:
- resolution: {integrity: sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ /jest-worker@29.3.1:
+ resolution: {integrity: sha512-lY4AnnmsEWeiXirAIA0c9SDPbuCBq8IYuDVL8PMm0MZ2PEs2yPvRA/J64QBXuZp7CYKrDM/rmNrc9/i3KJQncw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@types/node': 18.18.4
+ jest-util: 29.7.0
merge-stream: 2.0.0
supports-color: 8.1.1
dev: true
- /jest-worker@29.3.1:
- resolution: {integrity: sha512-lY4AnnmsEWeiXirAIA0c9SDPbuCBq8IYuDVL8PMm0MZ2PEs2yPvRA/J64QBXuZp7CYKrDM/rmNrc9/i3KJQncw==}
+ /jest-worker@29.7.0:
+ resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@types/node': 18.18.4
- jest-util: 29.3.1
+ jest-util: 29.7.0
merge-stream: 2.0.0
supports-color: 8.1.1
dev: true
- /jest@28.1.3(@types/node@18.11.9)(ts-node@10.9.1):
- resolution: {integrity: sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ /jest@29.3.1(@types/node@18.11.9)(ts-node@10.9.1):
+ resolution: {integrity: sha512-6iWfL5DTT0Np6UYs/y5Niu7WIfNv/wRTtN5RSXt2DIEft3dx3zPuw/3WJQBCJfmEzvDiEKwoqMbGD9n49+qLSA==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
hasBin: true
peerDependencies:
node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
@@ -13550,18 +13565,19 @@ packages:
node-notifier:
optional: true
dependencies:
- '@jest/core': 28.1.3(ts-node@10.9.1)
- '@jest/types': 28.1.3
+ '@jest/core': 29.3.1(ts-node@10.9.1)
+ '@jest/types': 29.3.1
import-local: 3.1.0
- jest-cli: 28.1.3(@types/node@18.11.9)(ts-node@10.9.1)
+ jest-cli: 29.3.1(@types/node@18.11.9)(ts-node@10.9.1)
transitivePeerDependencies:
- '@types/node'
+ - babel-plugin-macros
- supports-color
- ts-node
dev: true
- /jest@29.3.1(@types/node@18.11.9)(ts-node@10.9.1):
- resolution: {integrity: sha512-6iWfL5DTT0Np6UYs/y5Niu7WIfNv/wRTtN5RSXt2DIEft3dx3zPuw/3WJQBCJfmEzvDiEKwoqMbGD9n49+qLSA==}
+ /jest@29.7.0(@types/node@18.11.9)(ts-node@10.9.1):
+ resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
hasBin: true
peerDependencies:
@@ -13570,12 +13586,13 @@ packages:
node-notifier:
optional: true
dependencies:
- '@jest/core': 29.3.1(ts-node@10.9.1)
- '@jest/types': 29.3.1
+ '@jest/core': 29.7.0(ts-node@10.9.1)
+ '@jest/types': 29.6.3
import-local: 3.1.0
- jest-cli: 29.3.1(@types/node@18.11.9)(ts-node@10.9.1)
+ jest-cli: 29.7.0(@types/node@18.11.9)(ts-node@10.9.1)
transitivePeerDependencies:
- '@types/node'
+ - babel-plugin-macros
- supports-color
- ts-node
dev: true
@@ -13624,7 +13641,7 @@ packages:
'@babel/preset-env': ^7.1.6
dependencies:
'@babel/core': 7.22.10
- '@babel/parser': 7.23.0
+ '@babel/parser': 7.23.4
'@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.22.10)
'@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.22.10)
'@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.22.10)
@@ -15343,19 +15360,19 @@ packages:
find-up: 6.3.0
dev: true
- /playwright-core@1.29.2:
- resolution: {integrity: sha512-94QXm4PMgFoHAhlCuoWyaBYKb92yOcGVHdQLoxQ7Wjlc7Flg4aC/jbFW7xMR52OfXMVkWicue4WXE7QEegbIRA==}
+ /playwright-core@1.32.2:
+ resolution: {integrity: sha512-zD7aonO+07kOTthsrCR3YCVnDcqSHIJpdFUtZEMOb6//1Rc7/6mZDRdw+nlzcQiQltOOsiqI3rrSyn/SlyjnJQ==}
engines: {node: '>=14'}
hasBin: true
dev: true
- /playwright@1.29.2:
- resolution: {integrity: sha512-hKBYJUtdmYzcjdhYDkP9WGtORwwZBBKAW8+Lz7sr0ZMxtJr04ASXVzH5eBWtDkdb0c3LLFsehfPBTRfvlfKJOA==}
+ /playwright@1.32.2:
+ resolution: {integrity: sha512-jHVnXJke0PXpuPszKtk9y1zZSlzO5+2a+aockT/AND0oeXx46FiJEFrafthurglLygVZA+1gEbtUM1C7qtTV+Q==}
engines: {node: '>=14'}
hasBin: true
requiresBuild: true
dependencies:
- playwright-core: 1.29.2
+ playwright-core: 1.32.2
dev: true
/please-upgrade-node@3.2.0:
@@ -15801,8 +15818,8 @@ packages:
resolution: {integrity: sha512-tlkBdypJuvK/s00n4EiQjwYVfuuZv6vt8BF3g1ooIQa2Gz9Vz80p8q3qsPLZ0V5ErGRy6i3Q4fWC9TDzR7GNRQ==}
dev: false
- /posthog-js@1.92.1:
- resolution: {integrity: sha512-xtuTfM/acfDauiEfIdKF6d911KUZQ7RLii2COAYEoPWr3cVUFoNUoRQz9QJvgDlV2j22Zwl+mnXacUeua+Yi1A==}
+ /posthog-js@1.93.1:
+ resolution: {integrity: sha512-tbzxNN86zqC/D/HEMi4dJgW4GmiUHmUbBJ+zHFKABXxeS53SVzuJKkQxeNL9GdfpS9304i2D7ALsXoJ8pszAvw==}
dependencies:
fflate: 0.4.8
dev: false
@@ -15848,21 +15865,20 @@ packages:
ansi-styles: 5.2.0
react-is: 17.0.2
- /pretty-format@28.1.3:
- resolution: {integrity: sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==}
- engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
+ /pretty-format@29.3.1:
+ resolution: {integrity: sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@jest/schemas': 28.1.3
- ansi-regex: 5.0.1
+ '@jest/schemas': 29.0.0
ansi-styles: 5.2.0
react-is: 18.2.0
dev: true
- /pretty-format@29.3.1:
- resolution: {integrity: sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg==}
+ /pretty-format@29.7.0:
+ resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@jest/schemas': 29.0.0
+ '@jest/schemas': 29.6.3
ansi-styles: 5.2.0
react-is: 18.2.0
dev: true
@@ -16153,6 +16169,10 @@ packages:
resolution: {integrity: sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA==}
dev: false
+ /pure-rand@6.0.4:
+ resolution: {integrity: sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==}
+ dev: true
+
/q@1.5.1:
resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==}
engines: {node: '>=0.6.0', teleport: '>=0.2.0'}
@@ -17405,6 +17425,11 @@ packages:
engines: {node: '>=10'}
dev: true
+ /resolve.exports@2.0.2:
+ resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==}
+ engines: {node: '>=10'}
+ dev: true
+
/resolve@1.22.1:
resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==}
hasBin: true
@@ -18432,14 +18457,6 @@ packages:
dependencies:
has-flag: 4.0.0
- /supports-hyperlinks@2.3.0:
- resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==}
- engines: {node: '>=8'}
- dependencies:
- has-flag: 4.0.0
- supports-color: 7.2.0
- dev: true
-
/supports-hyperlinks@3.0.0:
resolution: {integrity: sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==}
engines: {node: '>=14.18'}
@@ -18584,14 +18601,6 @@ packages:
unique-string: 2.0.0
dev: true
- /terminal-link@2.1.1:
- resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==}
- engines: {node: '>=8'}
- dependencies:
- ansi-escapes: 4.3.2
- supports-hyperlinks: 2.3.0
- dev: true
-
/terser-webpack-plugin@5.3.9(@swc/core@1.3.93)(esbuild@0.14.54)(webpack@5.88.2):
resolution: {integrity: sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==}
engines: {node: '>= 10.13.0'}
@@ -19342,7 +19351,7 @@ packages:
resolution: {integrity: sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==}
engines: {node: '>=10.12.0'}
dependencies:
- '@jridgewell/trace-mapping': 0.3.17
+ '@jridgewell/trace-mapping': 0.3.20
'@types/istanbul-lib-coverage': 2.0.4
convert-source-map: 1.9.0
dev: true
diff --git a/posthog/admin.py b/posthog/admin.py
index 2c0d26a1b01fb..7161cbaf27081 100644
--- a/posthog/admin.py
+++ b/posthog/admin.py
@@ -33,8 +33,6 @@
)
from posthog.warehouse.models import DataWarehouseTable
-admin.site.register(DataWarehouseTable)
-
class DashboardTileInline(admin.TabularInline):
extra = 0
@@ -81,6 +79,39 @@ def organization_link(self, dashboard: Dashboard):
)
+@admin.register(DataWarehouseTable)
+class DataWarehouseTableAdmin(admin.ModelAdmin):
+ list_display = (
+ "id",
+ "name",
+ "format",
+ "url_pattern",
+ "team_link",
+ "organization_link",
+ "created_at",
+ "created_by",
+ )
+ list_display_links = ("id", "name")
+ list_select_related = ("team", "team__organization")
+ search_fields = ("id", "name", "team__name", "team__organization__name")
+ autocomplete_fields = ("team", "created_by")
+ ordering = ("-created_at",)
+
+ def team_link(self, dashboard: Dashboard):
+ return format_html(
+ '{}',
+ dashboard.team.pk,
+ dashboard.team.name,
+ )
+
+ def organization_link(self, dashboard: Dashboard):
+ return format_html(
+ '{}',
+ dashboard.team.organization.pk,
+ dashboard.team.organization.name,
+ )
+
+
@admin.register(Text)
class TextAdmin(admin.ModelAdmin):
autocomplete_fields = ("created_by", "last_modified_by", "team")
diff --git a/posthog/api/cohort.py b/posthog/api/cohort.py
index d417246b76d95..c023d7ffe7ab2 100644
--- a/posthog/api/cohort.py
+++ b/posthog/api/cohort.py
@@ -2,6 +2,7 @@
import json
from django.db import DatabaseError
+from sentry_sdk import start_span
import structlog
from posthog.models.feature_flag.flag_matching import (
@@ -10,6 +11,8 @@
get_feature_flag_hash_key_overrides,
)
from posthog.models.person.person import PersonDistinctId
+from posthog.models.property.property import Property, PropertyGroup
+from posthog.queries.base import property_group_to_Q
from posthog.queries.insight import insight_sync_execute
import posthoganalytics
from posthog.metrics import LABEL_TEAM_ID
@@ -46,6 +49,7 @@
INSIGHT_TRENDS,
LIMIT,
OFFSET,
+ PropertyOperatorType,
)
from posthog.event_usage import report_user_action
from posthog.hogql.context import HogQLContext
@@ -556,7 +560,7 @@ def insert_actors_into_cohort_by_query(cohort: Cohort, query: str, params: Dict[
capture_exception(err)
-def get_cohort_actors_for_feature_flag(cohort_id: int, flag: str, team_id: int, batchsize: int = 5_000):
+def get_cohort_actors_for_feature_flag(cohort_id: int, flag: str, team_id: int, batchsize: int = 1_000):
# :TODO: Find a way to incorporate this into the same code path as feature flag evaluation
try:
feature_flag = FeatureFlag.objects.get(team_id=team_id, key=flag)
@@ -572,20 +576,31 @@ def get_cohort_actors_for_feature_flag(cohort_id: int, flag: str, team_id: int,
cohorts_cache = {}
if feature_flag.uses_cohorts:
+ # TODO: Consider disabling flags with cohorts for creating static cohorts
+ # because this is currently a lot more inefficient for flag matching,
+ # as we're required to go to the database for each person.
cohorts_cache = {cohort.pk: cohort for cohort in Cohort.objects.filter(team_id=team_id, deleted=False)}
default_person_properties = {}
for condition in feature_flag.conditions:
property_list = Filter(data=condition).property_groups.flat
for property in property_list:
- if property.operator not in ("is_set", "is_not_set") and property.type == "person":
- default_person_properties[property.key] = ""
+ default_person_properties.update(get_default_person_property(property, cohorts_cache))
+
+ flag_property_conditions = [Filter(data=condition).property_groups for condition in feature_flag.conditions]
+ flag_property_group = PropertyGroup(type=PropertyOperatorType.OR, values=flag_property_conditions)
try:
# QuerySet.Iterator() doesn't work with pgbouncer, it will load everything into memory and then stream
# which doesn't work for us, so need a manual chunking here.
# Because of this pgbouncer transaction pooling mode, we can't use server-side cursors.
- queryset = Person.objects.filter(team_id=team_id).order_by("id")
+ # We pre-filter all persons to be ones that will match the feature flag, so that we don't have to
+ # iterate through all persons
+ queryset = (
+ Person.objects.filter(team_id=team_id)
+ .filter(property_group_to_Q(flag_property_group, cohorts_cache=cohorts_cache))
+ .order_by("id")
+ )
# get batchsize number of people at a time
start = 0
batch_of_persons = queryset[start : start + batchsize]
@@ -596,7 +611,7 @@ def get_cohort_actors_for_feature_flag(cohort_id: int, flag: str, team_id: int,
# "distinct_id", flat=True
# )[0]
distinct_id_subquery = Subquery(
- PersonDistinctId.objects.filter(person_id=OuterRef("person_id")).values_list("id", flat=True)[:1]
+ PersonDistinctId.objects.filter(person_id=OuterRef("person_id")).values_list("id", flat=True)[:3]
)
prefetch_related_objects(
batch_of_persons,
@@ -611,44 +626,49 @@ def get_cohort_actors_for_feature_flag(cohort_id: int, flag: str, team_id: int,
if len(all_persons) == 0:
break
- for person in all_persons:
- distinct_id = person.distinct_ids[0]
- person_overrides = {}
- if feature_flag.ensure_experience_continuity:
- # :TRICKY: This is inefficient because it tries to get the hashkey overrides one by one.
- # But reusing functions is better for maintainability. Revisit optimising if this becomes a bottleneck.
- person_overrides = get_feature_flag_hash_key_overrides(
- team_id, [distinct_id], person_id_to_distinct_id_mapping={person.id: distinct_id}
- )
+ with start_span(op="batch_flag_matching_with_overrides"):
+ for person in all_persons:
+ # ignore almost-deleted persons / persons with no distinct ids
+ if len(person.distinct_ids) == 0:
+ continue
+
+ distinct_id = person.distinct_ids[0]
+ person_overrides = {}
+ if feature_flag.ensure_experience_continuity:
+ # :TRICKY: This is inefficient because it tries to get the hashkey overrides one by one.
+ # But reusing functions is better for maintainability. Revisit optimising if this becomes a bottleneck.
+ person_overrides = get_feature_flag_hash_key_overrides(
+ team_id, [distinct_id], person_id_to_distinct_id_mapping={person.id: distinct_id}
+ )
- try:
- match = FeatureFlagMatcher(
- [feature_flag],
- distinct_id,
- groups={},
- cache=matcher_cache,
- hash_key_overrides=person_overrides,
- property_value_overrides={**default_person_properties, **person.properties},
- group_property_value_overrides={},
- cohorts_cache=cohorts_cache,
- ).get_match(feature_flag)
- if match.match:
- uuids_to_add_to_cohort.append(str(person.uuid))
- except (DatabaseError, ValueError, ValidationError):
- logger.exception(
- "Error evaluating feature flag for person", person_uuid=str(person.uuid), team_id=team_id
- )
- except Exception as err:
- # matching errors are not fatal, so we just log them and move on.
- # Capturing in sentry for now just in case there are some unexpected errors
- # we did not account for.
- capture_exception(err)
-
- if len(uuids_to_add_to_cohort) >= batchsize - 1:
- cohort.insert_users_list_by_uuid(
- uuids_to_add_to_cohort, insert_in_clickhouse=True, batchsize=batchsize
- )
- uuids_to_add_to_cohort = []
+ try:
+ match = FeatureFlagMatcher(
+ [feature_flag],
+ distinct_id,
+ groups={},
+ cache=matcher_cache,
+ hash_key_overrides=person_overrides,
+ property_value_overrides={**default_person_properties, **person.properties},
+ group_property_value_overrides={},
+ cohorts_cache=cohorts_cache,
+ ).get_match(feature_flag)
+ if match.match:
+ uuids_to_add_to_cohort.append(str(person.uuid))
+ except (DatabaseError, ValueError, ValidationError):
+ logger.exception(
+ "Error evaluating feature flag for person", person_uuid=str(person.uuid), team_id=team_id
+ )
+ except Exception as err:
+ # matching errors are not fatal, so we just log them and move on.
+ # Capturing in sentry for now just in case there are some unexpected errors
+ # we did not account for.
+ capture_exception(err)
+
+ if len(uuids_to_add_to_cohort) >= batchsize:
+ cohort.insert_users_list_by_uuid(
+ uuids_to_add_to_cohort, insert_in_clickhouse=True, batchsize=batchsize
+ )
+ uuids_to_add_to_cohort = []
start += batchsize
batch_of_persons = queryset[start : start + batchsize]
@@ -660,3 +680,30 @@ def get_cohort_actors_for_feature_flag(cohort_id: int, flag: str, team_id: int,
if settings.DEBUG or settings.TEST:
raise err
capture_exception(err)
+
+
+def get_default_person_property(prop: Property, cohorts_cache: Dict[int, Cohort]):
+ default_person_properties = {}
+
+ if prop.operator not in ("is_set", "is_not_set") and prop.type == "person":
+ default_person_properties[prop.key] = ""
+ elif prop.type == "cohort" and not isinstance(prop.value, list):
+ try:
+ parsed_cohort_id = int(prop.value)
+ except (ValueError, TypeError):
+ return None
+ cohort = cohorts_cache.get(parsed_cohort_id)
+ if cohort:
+ return get_default_person_properties_for_cohort(cohort, cohorts_cache)
+ return default_person_properties
+
+
+def get_default_person_properties_for_cohort(cohort: Cohort, cohorts_cache: Dict[int, Cohort]) -> Dict[str, str]:
+ """
+ Returns a dictionary of default person properties to use when evaluating a feature flag
+ """
+ default_person_properties = {}
+ for property in cohort.properties.flat:
+ default_person_properties.update(get_default_person_property(property, cohorts_cache))
+
+ return default_person_properties
diff --git a/posthog/api/feature_flag.py b/posthog/api/feature_flag.py
index 92add84a0bcab..5022fd21676ea 100644
--- a/posthog/api/feature_flag.py
+++ b/posthog/api/feature_flag.py
@@ -636,6 +636,7 @@ def create_static_cohort_for_flag(self, request: request.Request, **kwargs):
"is_static": True,
"key": feature_flag_key,
"name": f'Users with feature flag {feature_flag_key} enabled at {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}',
+ "is_calculating": True,
},
context={
"request": request,
diff --git a/posthog/api/survey.py b/posthog/api/survey.py
index ef3e8c166dac8..70090e575cec3 100644
--- a/posthog/api/survey.py
+++ b/posthog/api/survey.py
@@ -135,6 +135,10 @@ def validate_questions(self, value):
if description and nh3.is_html(description):
cleaned_question["description"] = nh3_clean_with_allow_list(description)
+ choices = raw_question.get("choices")
+ if choices and not isinstance(choices, list):
+ raise serializers.ValidationError("Question choices must be a list of strings")
+
cleaned_questions.append(cleaned_question)
return cleaned_questions
diff --git a/posthog/api/test/__snapshots__/test_feature_flag.ambr b/posthog/api/test/__snapshots__/test_feature_flag.ambr
index c7922b68432eb..e58824ff62f94 100644
--- a/posthog/api/test/__snapshots__/test_feature_flag.ambr
+++ b/posthog/api/test/__snapshots__/test_feature_flag.ambr
@@ -380,7 +380,7 @@
LIMIT 21
'
---
-# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.10
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.2
'
SELECT "posthog_person"."id",
"posthog_person"."created_at",
@@ -393,77 +393,15 @@
"posthog_person"."uuid",
"posthog_person"."version"
FROM "posthog_person"
- WHERE "posthog_person"."team_id" = 2
+ WHERE ("posthog_person"."team_id" = 2
+ AND ("posthog_person"."properties" -> 'key') = '"value"'
+ AND "posthog_person"."properties" ? 'key'
+ AND NOT (("posthog_person"."properties" -> 'key') = 'null'))
ORDER BY "posthog_person"."id" ASC
LIMIT 2
- OFFSET 4
'
---
-# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.11
- '
- SELECT "posthog_featureflag"."id",
- "posthog_featureflag"."key",
- "posthog_featureflag"."name",
- "posthog_featureflag"."filters",
- "posthog_featureflag"."rollout_percentage",
- "posthog_featureflag"."team_id",
- "posthog_featureflag"."created_by_id",
- "posthog_featureflag"."created_at",
- "posthog_featureflag"."deleted",
- "posthog_featureflag"."active",
- "posthog_featureflag"."rollback_conditions",
- "posthog_featureflag"."performed_rollback",
- "posthog_featureflag"."ensure_experience_continuity",
- "posthog_featureflag"."usage_dashboard_id",
- "posthog_featureflag"."has_enriched_analytics"
- FROM "posthog_featureflag"
- WHERE ("posthog_featureflag"."key" = 'some-feature2'
- AND "posthog_featureflag"."team_id" = 2)
- LIMIT 21
- '
----
-# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.12
- '
- SELECT "posthog_cohort"."id",
- "posthog_cohort"."name",
- "posthog_cohort"."description",
- "posthog_cohort"."team_id",
- "posthog_cohort"."deleted",
- "posthog_cohort"."filters",
- "posthog_cohort"."version",
- "posthog_cohort"."pending_version",
- "posthog_cohort"."count",
- "posthog_cohort"."created_by_id",
- "posthog_cohort"."created_at",
- "posthog_cohort"."is_calculating",
- "posthog_cohort"."last_calculation",
- "posthog_cohort"."errors_calculating",
- "posthog_cohort"."is_static",
- "posthog_cohort"."groups"
- FROM "posthog_cohort"
- WHERE "posthog_cohort"."id" = 2
- LIMIT 21
- '
----
-# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.13
- '
- SELECT "posthog_person"."id",
- "posthog_person"."created_at",
- "posthog_person"."properties_last_updated_at",
- "posthog_person"."properties_last_operation",
- "posthog_person"."team_id",
- "posthog_person"."properties",
- "posthog_person"."is_user_id",
- "posthog_person"."is_identified",
- "posthog_person"."uuid",
- "posthog_person"."version"
- FROM "posthog_person"
- WHERE "posthog_person"."team_id" = 2
- ORDER BY "posthog_person"."id" ASC
- LIMIT 10
- '
----
-# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.14
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.3
'
SELECT "posthog_persondistinctid"."id",
"posthog_persondistinctid"."team_id",
@@ -475,7 +413,7 @@
(SELECT U0."id"
FROM "posthog_persondistinctid" U0
WHERE U0."person_id" = "posthog_persondistinctid"."person_id"
- LIMIT 1)
+ LIMIT 3)
AND "posthog_persondistinctid"."person_id" IN (1,
2,
3,
@@ -483,26 +421,7 @@
5 /* ... */))
'
---
-# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.15
- '
- SELECT "posthog_person"."id",
- "posthog_person"."created_at",
- "posthog_person"."properties_last_updated_at",
- "posthog_person"."properties_last_operation",
- "posthog_person"."team_id",
- "posthog_person"."properties",
- "posthog_person"."is_user_id",
- "posthog_person"."is_identified",
- "posthog_person"."uuid",
- "posthog_person"."version"
- FROM "posthog_person"
- WHERE "posthog_person"."team_id" = 2
- ORDER BY "posthog_person"."id" ASC
- LIMIT 10
- OFFSET 10
- '
----
-# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.16
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.4
'
SELECT "posthog_person"."uuid"
FROM "posthog_person"
@@ -517,7 +436,7 @@
LIMIT 1)))
'
---
-# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.17
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.5
'
SELECT "posthog_team"."id",
"posthog_team"."uuid",
@@ -568,13 +487,14 @@
"posthog_team"."event_properties",
"posthog_team"."event_properties_with_usage",
"posthog_team"."event_properties_numerical",
- "posthog_team"."external_data_workspace_id"
+ "posthog_team"."external_data_workspace_id",
+ "posthog_team"."external_data_workspace_last_synced_at"
FROM "posthog_team"
WHERE "posthog_team"."id" = 2
LIMIT 21
'
---
-# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.2
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.6
'
SELECT "posthog_person"."id",
"posthog_person"."created_at",
@@ -587,12 +507,16 @@
"posthog_person"."uuid",
"posthog_person"."version"
FROM "posthog_person"
- WHERE "posthog_person"."team_id" = 2
+ WHERE ("posthog_person"."team_id" = 2
+ AND ("posthog_person"."properties" -> 'key') = '"value"'
+ AND "posthog_person"."properties" ? 'key'
+ AND NOT (("posthog_person"."properties" -> 'key') = 'null'))
ORDER BY "posthog_person"."id" ASC
LIMIT 2
+ OFFSET 2
'
---
-# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.3
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.7
'
SELECT "posthog_persondistinctid"."id",
"posthog_persondistinctid"."team_id",
@@ -604,7 +528,7 @@
(SELECT U0."id"
FROM "posthog_persondistinctid" U0
WHERE U0."person_id" = "posthog_persondistinctid"."person_id"
- LIMIT 1)
+ LIMIT 3)
AND "posthog_persondistinctid"."person_id" IN (1,
2,
3,
@@ -612,7 +536,29 @@
5 /* ... */))
'
---
-# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.4
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.8
+ '
+ SELECT "posthog_person"."id",
+ "posthog_person"."created_at",
+ "posthog_person"."properties_last_updated_at",
+ "posthog_person"."properties_last_operation",
+ "posthog_person"."team_id",
+ "posthog_person"."properties",
+ "posthog_person"."is_user_id",
+ "posthog_person"."is_identified",
+ "posthog_person"."uuid",
+ "posthog_person"."version"
+ FROM "posthog_person"
+ WHERE ("posthog_person"."team_id" = 2
+ AND ("posthog_person"."properties" -> 'key') = '"value"'
+ AND "posthog_person"."properties" ? 'key'
+ AND NOT (("posthog_person"."properties" -> 'key') = 'null'))
+ ORDER BY "posthog_person"."id" ASC
+ LIMIT 2
+ OFFSET 4
+ '
+---
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.9
'
SELECT "posthog_person"."uuid"
FROM "posthog_person"
@@ -627,7 +573,68 @@
LIMIT 1)))
'
---
-# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.5
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_cohort_flag_adds_cohort_props_as_default_too
+ '
+ SELECT "posthog_featureflag"."id",
+ "posthog_featureflag"."key",
+ "posthog_featureflag"."name",
+ "posthog_featureflag"."filters",
+ "posthog_featureflag"."rollout_percentage",
+ "posthog_featureflag"."team_id",
+ "posthog_featureflag"."created_by_id",
+ "posthog_featureflag"."created_at",
+ "posthog_featureflag"."deleted",
+ "posthog_featureflag"."active",
+ "posthog_featureflag"."rollback_conditions",
+ "posthog_featureflag"."performed_rollback",
+ "posthog_featureflag"."ensure_experience_continuity",
+ "posthog_featureflag"."usage_dashboard_id",
+ "posthog_featureflag"."has_enriched_analytics"
+ FROM "posthog_featureflag"
+ WHERE ("posthog_featureflag"."key" = 'some-feature-new'
+ AND "posthog_featureflag"."team_id" = 2)
+ LIMIT 21
+ '
+---
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_cohort_flag_adds_cohort_props_as_default_too.1
+ '
+ SELECT "posthog_cohort"."id",
+ "posthog_cohort"."name",
+ "posthog_cohort"."description",
+ "posthog_cohort"."team_id",
+ "posthog_cohort"."deleted",
+ "posthog_cohort"."filters",
+ "posthog_cohort"."version",
+ "posthog_cohort"."pending_version",
+ "posthog_cohort"."count",
+ "posthog_cohort"."created_by_id",
+ "posthog_cohort"."created_at",
+ "posthog_cohort"."is_calculating",
+ "posthog_cohort"."last_calculation",
+ "posthog_cohort"."errors_calculating",
+ "posthog_cohort"."is_static",
+ "posthog_cohort"."groups"
+ FROM "posthog_cohort"
+ WHERE "posthog_cohort"."id" = 2
+ LIMIT 21
+ '
+---
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_cohort_flag_adds_cohort_props_as_default_too.10
+ '
+ SELECT "posthog_person"."uuid"
+ FROM "posthog_person"
+ WHERE ("posthog_person"."team_id" = 2
+ AND "posthog_person"."uuid" IN ('00000000-0000-0000-0000-000000000000'::uuid,
+ '00000000-0000-0000-0000-000000000001'::uuid /* ... */)
+ AND NOT (EXISTS
+ (SELECT (1) AS "a"
+ FROM "posthog_cohortpeople" U1
+ WHERE (U1."cohort_id" = 2
+ AND U1."person_id" = "posthog_person"."id")
+ LIMIT 1)))
+ '
+---
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_cohort_flag_adds_cohort_props_as_default_too.11
'
SELECT "posthog_team"."id",
"posthog_team"."uuid",
@@ -685,22 +692,30 @@
LIMIT 21
'
---
-# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.6
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_cohort_flag_adds_cohort_props_as_default_too.2
'
- SELECT "posthog_person"."uuid"
- FROM "posthog_person"
- WHERE ("posthog_person"."team_id" = 2
- AND "posthog_person"."uuid" IN ('00000000-0000-0000-0000-000000000000'::uuid,
- '00000000-0000-0000-0000-000000000001'::uuid /* ... */)
- AND NOT (EXISTS
- (SELECT (1) AS "a"
- FROM "posthog_cohortpeople" U1
- WHERE (U1."cohort_id" = 2
- AND U1."person_id" = "posthog_person"."id")
- LIMIT 1)))
+ SELECT "posthog_cohort"."id",
+ "posthog_cohort"."name",
+ "posthog_cohort"."description",
+ "posthog_cohort"."team_id",
+ "posthog_cohort"."deleted",
+ "posthog_cohort"."filters",
+ "posthog_cohort"."version",
+ "posthog_cohort"."pending_version",
+ "posthog_cohort"."count",
+ "posthog_cohort"."created_by_id",
+ "posthog_cohort"."created_at",
+ "posthog_cohort"."is_calculating",
+ "posthog_cohort"."last_calculation",
+ "posthog_cohort"."errors_calculating",
+ "posthog_cohort"."is_static",
+ "posthog_cohort"."groups"
+ FROM "posthog_cohort"
+ WHERE (NOT "posthog_cohort"."deleted"
+ AND "posthog_cohort"."team_id" = 2)
'
---
-# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.7
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_cohort_flag_adds_cohort_props_as_default_too.3
'
SELECT "posthog_person"."id",
"posthog_person"."created_at",
@@ -713,13 +728,33 @@
"posthog_person"."uuid",
"posthog_person"."version"
FROM "posthog_person"
- WHERE "posthog_person"."team_id" = 2
+ WHERE ("posthog_person"."team_id" = 2
+ AND ((("posthog_person"."properties" -> 'group') = '"none"'
+ AND "posthog_person"."properties" ? 'group'
+ AND NOT (("posthog_person"."properties" -> 'group') = 'null'))
+ OR (("posthog_person"."properties" -> 'group2') IN ('1',
+ '2',
+ '3')
+ AND "posthog_person"."properties" ? 'group2'
+ AND NOT (("posthog_person"."properties" -> 'group2') = 'null'))
+ OR EXISTS
+ (SELECT (1) AS "a"
+ FROM "posthog_cohortpeople" U0
+ WHERE (U0."cohort_id" = 2
+ AND U0."cohort_id" = 2
+ AND U0."person_id" = "posthog_person"."id")
+ LIMIT 1)
+ OR (("posthog_person"."properties" -> 'does-not-exist') = '"none"'
+ AND "posthog_person"."properties" ? 'does-not-exist'
+ AND NOT (("posthog_person"."properties" -> 'does-not-exist') = 'null'))
+ OR (("posthog_person"."properties" -> 'key') = '"value"'
+ AND "posthog_person"."properties" ? 'key'
+ AND NOT (("posthog_person"."properties" -> 'key') = 'null'))))
ORDER BY "posthog_person"."id" ASC
- LIMIT 2
- OFFSET 2
+ LIMIT 1000
'
---
-# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.8
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_cohort_flag_adds_cohort_props_as_default_too.4
'
SELECT "posthog_persondistinctid"."id",
"posthog_persondistinctid"."team_id",
@@ -731,7 +766,7 @@
(SELECT U0."id"
FROM "posthog_persondistinctid" U0
WHERE U0."person_id" = "posthog_persondistinctid"."person_id"
- LIMIT 1)
+ LIMIT 3)
AND "posthog_persondistinctid"."person_id" IN (1,
2,
3,
@@ -739,45 +774,123 @@
5 /* ... */))
'
---
-# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.9
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_cohort_flag_adds_cohort_props_as_default_too.5
'
- SELECT "posthog_person"."uuid"
+ SELECT ("posthog_person"."id" IS NULL
+ OR "posthog_person"."id" IS NULL
+ OR EXISTS
+ (SELECT (1) AS "a"
+ FROM "posthog_cohortpeople" U0
+ WHERE (U0."cohort_id" = 2
+ AND U0."cohort_id" = 2
+ AND U0."person_id" = "posthog_person"."id")
+ LIMIT 1)
+ OR "posthog_person"."id" IS NULL) AS "flag_X_condition_0"
FROM "posthog_person"
- WHERE ("posthog_person"."team_id" = 2
- AND "posthog_person"."uuid" IN ('00000000-0000-0000-0000-000000000000'::uuid,
- '00000000-0000-0000-0000-000000000001'::uuid /* ... */)
- AND NOT (EXISTS
- (SELECT (1) AS "a"
- FROM "posthog_cohortpeople" U1
- WHERE (U1."cohort_id" = 2
- AND U1."person_id" = "posthog_person"."id")
- LIMIT 1)))
+ INNER JOIN "posthog_persondistinctid" ON ("posthog_person"."id" = "posthog_persondistinctid"."person_id")
+ WHERE ("posthog_persondistinctid"."distinct_id" = 'person1'
+ AND "posthog_persondistinctid"."team_id" = 2
+ AND "posthog_person"."team_id" = 2)
'
---
-# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_deleted_flag
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_cohort_flag_adds_cohort_props_as_default_too.6
'
- SELECT "posthog_featureflag"."id",
- "posthog_featureflag"."key",
- "posthog_featureflag"."name",
- "posthog_featureflag"."filters",
- "posthog_featureflag"."rollout_percentage",
- "posthog_featureflag"."team_id",
- "posthog_featureflag"."created_by_id",
- "posthog_featureflag"."created_at",
- "posthog_featureflag"."deleted",
- "posthog_featureflag"."active",
- "posthog_featureflag"."rollback_conditions",
- "posthog_featureflag"."performed_rollback",
- "posthog_featureflag"."ensure_experience_continuity",
- "posthog_featureflag"."usage_dashboard_id",
- "posthog_featureflag"."has_enriched_analytics"
- FROM "posthog_featureflag"
- WHERE ("posthog_featureflag"."key" = 'some-feature'
- AND "posthog_featureflag"."team_id" = 2)
- LIMIT 21
+ SELECT ("posthog_person"."id" IS NOT NULL
+ OR "posthog_person"."id" IS NULL
+ OR EXISTS
+ (SELECT (1) AS "a"
+ FROM "posthog_cohortpeople" U0
+ WHERE (U0."cohort_id" = 2
+ AND U0."cohort_id" = 2
+ AND U0."person_id" = "posthog_person"."id")
+ LIMIT 1)
+ OR "posthog_person"."id" IS NULL) AS "flag_X_condition_0"
+ FROM "posthog_person"
+ INNER JOIN "posthog_persondistinctid" ON ("posthog_person"."id" = "posthog_persondistinctid"."person_id")
+ WHERE ("posthog_persondistinctid"."distinct_id" = 'person2'
+ AND "posthog_persondistinctid"."team_id" = 2
+ AND "posthog_person"."team_id" = 2)
'
---
-# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_experience_continuity_flag
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_cohort_flag_adds_cohort_props_as_default_too.7
+ '
+ SELECT ("posthog_person"."id" IS NULL
+ OR "posthog_person"."id" IS NOT NULL
+ OR EXISTS
+ (SELECT (1) AS "a"
+ FROM "posthog_cohortpeople" U0
+ WHERE (U0."cohort_id" = 2
+ AND U0."cohort_id" = 2
+ AND U0."person_id" = "posthog_person"."id")
+ LIMIT 1)
+ OR "posthog_person"."id" IS NULL) AS "flag_X_condition_0"
+ FROM "posthog_person"
+ INNER JOIN "posthog_persondistinctid" ON ("posthog_person"."id" = "posthog_persondistinctid"."person_id")
+ WHERE ("posthog_persondistinctid"."distinct_id" = 'person3'
+ AND "posthog_persondistinctid"."team_id" = 2
+ AND "posthog_person"."team_id" = 2)
+ '
+---
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_cohort_flag_adds_cohort_props_as_default_too.8
+ '
+ SELECT ("posthog_person"."id" IS NULL
+ OR "posthog_person"."id" IS NULL
+ OR EXISTS
+ (SELECT (1) AS "a"
+ FROM "posthog_cohortpeople" U0
+ WHERE (U0."cohort_id" = 2
+ AND U0."cohort_id" = 2
+ AND U0."person_id" = "posthog_person"."id")
+ LIMIT 1)
+ OR "posthog_person"."id" IS NULL) AS "flag_X_condition_0"
+ FROM "posthog_person"
+ INNER JOIN "posthog_persondistinctid" ON ("posthog_person"."id" = "posthog_persondistinctid"."person_id")
+ WHERE ("posthog_persondistinctid"."distinct_id" = 'person4'
+ AND "posthog_persondistinctid"."team_id" = 2
+ AND "posthog_person"."team_id" = 2)
+ '
+---
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_cohort_flag_adds_cohort_props_as_default_too.9
+ '
+ SELECT "posthog_person"."id",
+ "posthog_person"."created_at",
+ "posthog_person"."properties_last_updated_at",
+ "posthog_person"."properties_last_operation",
+ "posthog_person"."team_id",
+ "posthog_person"."properties",
+ "posthog_person"."is_user_id",
+ "posthog_person"."is_identified",
+ "posthog_person"."uuid",
+ "posthog_person"."version"
+ FROM "posthog_person"
+ WHERE ("posthog_person"."team_id" = 2
+ AND ((("posthog_person"."properties" -> 'group') = '"none"'
+ AND "posthog_person"."properties" ? 'group'
+ AND NOT (("posthog_person"."properties" -> 'group') = 'null'))
+ OR (("posthog_person"."properties" -> 'group2') IN ('1',
+ '2',
+ '3')
+ AND "posthog_person"."properties" ? 'group2'
+ AND NOT (("posthog_person"."properties" -> 'group2') = 'null'))
+ OR EXISTS
+ (SELECT (1) AS "a"
+ FROM "posthog_cohortpeople" U0
+ WHERE (U0."cohort_id" = 2
+ AND U0."cohort_id" = 2
+ AND U0."person_id" = "posthog_person"."id")
+ LIMIT 1)
+ OR (("posthog_person"."properties" -> 'does-not-exist') = '"none"'
+ AND "posthog_person"."properties" ? 'does-not-exist'
+ AND NOT (("posthog_person"."properties" -> 'does-not-exist') = 'null'))
+ OR (("posthog_person"."properties" -> 'key') = '"value"'
+ AND "posthog_person"."properties" ? 'key'
+ AND NOT (("posthog_person"."properties" -> 'key') = 'null'))))
+ ORDER BY "posthog_person"."id" ASC
+ LIMIT 1000
+ OFFSET 1000
+ '
+---
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_default_person_properties_adjustment
'
SELECT "posthog_featureflag"."id",
"posthog_featureflag"."key",
@@ -800,7 +913,7 @@
LIMIT 21
'
---
-# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_experience_continuity_flag.1
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_default_person_properties_adjustment.1
'
SELECT "posthog_cohort"."id",
"posthog_cohort"."name",
@@ -823,7 +936,62 @@
LIMIT 21
'
---
-# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_experience_continuity_flag.10
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_default_person_properties_adjustment.10
+ '
+ SELECT "posthog_persondistinctid"."id",
+ "posthog_persondistinctid"."team_id",
+ "posthog_persondistinctid"."person_id",
+ "posthog_persondistinctid"."distinct_id",
+ "posthog_persondistinctid"."version"
+ FROM "posthog_persondistinctid"
+ WHERE ("posthog_persondistinctid"."id" IN
+ (SELECT U0."id"
+ FROM "posthog_persondistinctid" U0
+ WHERE U0."person_id" = "posthog_persondistinctid"."person_id"
+ LIMIT 3)
+ AND "posthog_persondistinctid"."person_id" IN (1,
+ 2,
+ 3,
+ 4,
+ 5 /* ... */))
+ '
+---
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_default_person_properties_adjustment.11
+ '
+ SELECT "posthog_person"."id",
+ "posthog_person"."created_at",
+ "posthog_person"."properties_last_updated_at",
+ "posthog_person"."properties_last_operation",
+ "posthog_person"."team_id",
+ "posthog_person"."properties",
+ "posthog_person"."is_user_id",
+ "posthog_person"."is_identified",
+ "posthog_person"."uuid",
+ "posthog_person"."version"
+ FROM "posthog_person"
+ WHERE ("posthog_person"."team_id" = 2
+ AND ("posthog_person"."properties" -> 'key') IS NOT NULL)
+ ORDER BY "posthog_person"."id" ASC
+ LIMIT 1000
+ OFFSET 1000
+ '
+---
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_default_person_properties_adjustment.12
+ '
+ SELECT "posthog_person"."uuid"
+ FROM "posthog_person"
+ WHERE ("posthog_person"."team_id" = 2
+ AND "posthog_person"."uuid" IN ('00000000-0000-0000-0000-000000000000'::uuid,
+ '00000000-0000-0000-0000-000000000001'::uuid /* ... */)
+ AND NOT (EXISTS
+ (SELECT (1) AS "a"
+ FROM "posthog_cohortpeople" U1
+ WHERE (U1."cohort_id" = 2
+ AND U1."person_id" = "posthog_person"."id")
+ LIMIT 1)))
+ '
+---
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_default_person_properties_adjustment.13
'
SELECT "posthog_team"."id",
"posthog_team"."uuid",
@@ -874,13 +1042,14 @@
"posthog_team"."event_properties",
"posthog_team"."event_properties_with_usage",
"posthog_team"."event_properties_numerical",
- "posthog_team"."external_data_workspace_id"
+ "posthog_team"."external_data_workspace_id",
+ "posthog_team"."external_data_workspace_last_synced_at"
FROM "posthog_team"
WHERE "posthog_team"."id" = 2
LIMIT 21
'
---
-# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_experience_continuity_flag.11
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_default_person_properties_adjustment.2
'
SELECT "posthog_person"."id",
"posthog_person"."created_at",
@@ -893,13 +1062,35 @@
"posthog_person"."uuid",
"posthog_person"."version"
FROM "posthog_person"
- WHERE "posthog_person"."team_id" = 2
+ WHERE ("posthog_person"."team_id" = 2
+ AND UPPER(("posthog_person"."properties" ->> 'key')::text) LIKE UPPER('%value%')
+ AND "posthog_person"."properties" ? 'key'
+ AND NOT (("posthog_person"."properties" -> 'key') = 'null'))
ORDER BY "posthog_person"."id" ASC
- LIMIT 21
- OFFSET 5000
+ LIMIT 1000
+ '
+---
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_default_person_properties_adjustment.3
+ '
+ SELECT "posthog_persondistinctid"."id",
+ "posthog_persondistinctid"."team_id",
+ "posthog_persondistinctid"."person_id",
+ "posthog_persondistinctid"."distinct_id",
+ "posthog_persondistinctid"."version"
+ FROM "posthog_persondistinctid"
+ WHERE ("posthog_persondistinctid"."id" IN
+ (SELECT U0."id"
+ FROM "posthog_persondistinctid" U0
+ WHERE U0."person_id" = "posthog_persondistinctid"."person_id"
+ LIMIT 3)
+ AND "posthog_persondistinctid"."person_id" IN (1,
+ 2,
+ 3,
+ 4,
+ 5 /* ... */))
'
---
-# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_experience_continuity_flag.12
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_default_person_properties_adjustment.4
'
SELECT "posthog_person"."id",
"posthog_person"."created_at",
@@ -912,13 +1103,16 @@
"posthog_person"."uuid",
"posthog_person"."version"
FROM "posthog_person"
- WHERE "posthog_person"."team_id" = 2
+ WHERE ("posthog_person"."team_id" = 2
+ AND UPPER(("posthog_person"."properties" ->> 'key')::text) LIKE UPPER('%value%')
+ AND "posthog_person"."properties" ? 'key'
+ AND NOT (("posthog_person"."properties" -> 'key') = 'null'))
ORDER BY "posthog_person"."id" ASC
- LIMIT 5000
- OFFSET 5000
+ LIMIT 1000
+ OFFSET 1000
'
---
-# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_experience_continuity_flag.13
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_default_person_properties_adjustment.5
'
SELECT "posthog_person"."uuid"
FROM "posthog_person"
@@ -933,7 +1127,7 @@
LIMIT 1)))
'
---
-# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_experience_continuity_flag.14
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_default_person_properties_adjustment.6
'
SELECT "posthog_team"."id",
"posthog_team"."uuid",
@@ -984,12 +1178,124 @@
"posthog_team"."event_properties",
"posthog_team"."event_properties_with_usage",
"posthog_team"."event_properties_numerical",
- "posthog_team"."external_data_workspace_id"
+ "posthog_team"."external_data_workspace_id",
+ "posthog_team"."external_data_workspace_last_synced_at"
FROM "posthog_team"
WHERE "posthog_team"."id" = 2
LIMIT 21
'
---
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_default_person_properties_adjustment.7
+ '
+ SELECT "posthog_featureflag"."id",
+ "posthog_featureflag"."key",
+ "posthog_featureflag"."name",
+ "posthog_featureflag"."filters",
+ "posthog_featureflag"."rollout_percentage",
+ "posthog_featureflag"."team_id",
+ "posthog_featureflag"."created_by_id",
+ "posthog_featureflag"."created_at",
+ "posthog_featureflag"."deleted",
+ "posthog_featureflag"."active",
+ "posthog_featureflag"."rollback_conditions",
+ "posthog_featureflag"."performed_rollback",
+ "posthog_featureflag"."ensure_experience_continuity",
+ "posthog_featureflag"."usage_dashboard_id",
+ "posthog_featureflag"."has_enriched_analytics"
+ FROM "posthog_featureflag"
+ WHERE ("posthog_featureflag"."key" = 'some-feature-new'
+ AND "posthog_featureflag"."team_id" = 2)
+ LIMIT 21
+ '
+---
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_default_person_properties_adjustment.8
+ '
+ SELECT "posthog_cohort"."id",
+ "posthog_cohort"."name",
+ "posthog_cohort"."description",
+ "posthog_cohort"."team_id",
+ "posthog_cohort"."deleted",
+ "posthog_cohort"."filters",
+ "posthog_cohort"."version",
+ "posthog_cohort"."pending_version",
+ "posthog_cohort"."count",
+ "posthog_cohort"."created_by_id",
+ "posthog_cohort"."created_at",
+ "posthog_cohort"."is_calculating",
+ "posthog_cohort"."last_calculation",
+ "posthog_cohort"."errors_calculating",
+ "posthog_cohort"."is_static",
+ "posthog_cohort"."groups"
+ FROM "posthog_cohort"
+ WHERE "posthog_cohort"."id" = 2
+ LIMIT 21
+ '
+---
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_default_person_properties_adjustment.9
+ '
+ SELECT "posthog_person"."id",
+ "posthog_person"."created_at",
+ "posthog_person"."properties_last_updated_at",
+ "posthog_person"."properties_last_operation",
+ "posthog_person"."team_id",
+ "posthog_person"."properties",
+ "posthog_person"."is_user_id",
+ "posthog_person"."is_identified",
+ "posthog_person"."uuid",
+ "posthog_person"."version"
+ FROM "posthog_person"
+ WHERE ("posthog_person"."team_id" = 2
+ AND ("posthog_person"."properties" -> 'key') IS NOT NULL)
+ ORDER BY "posthog_person"."id" ASC
+ LIMIT 1000
+ '
+---
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_experience_continuity_flag
+ '
+ SELECT "posthog_featureflag"."id",
+ "posthog_featureflag"."key",
+ "posthog_featureflag"."name",
+ "posthog_featureflag"."filters",
+ "posthog_featureflag"."rollout_percentage",
+ "posthog_featureflag"."team_id",
+ "posthog_featureflag"."created_by_id",
+ "posthog_featureflag"."created_at",
+ "posthog_featureflag"."deleted",
+ "posthog_featureflag"."active",
+ "posthog_featureflag"."rollback_conditions",
+ "posthog_featureflag"."performed_rollback",
+ "posthog_featureflag"."ensure_experience_continuity",
+ "posthog_featureflag"."usage_dashboard_id",
+ "posthog_featureflag"."has_enriched_analytics"
+ FROM "posthog_featureflag"
+ WHERE ("posthog_featureflag"."key" = 'some-feature2'
+ AND "posthog_featureflag"."team_id" = 2)
+ LIMIT 21
+ '
+---
+# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_experience_continuity_flag.1
+ '
+ SELECT "posthog_cohort"."id",
+ "posthog_cohort"."name",
+ "posthog_cohort"."description",
+ "posthog_cohort"."team_id",
+ "posthog_cohort"."deleted",
+ "posthog_cohort"."filters",
+ "posthog_cohort"."version",
+ "posthog_cohort"."pending_version",
+ "posthog_cohort"."count",
+ "posthog_cohort"."created_by_id",
+ "posthog_cohort"."created_at",
+ "posthog_cohort"."is_calculating",
+ "posthog_cohort"."last_calculation",
+ "posthog_cohort"."errors_calculating",
+ "posthog_cohort"."is_static",
+ "posthog_cohort"."groups"
+ FROM "posthog_cohort"
+ WHERE "posthog_cohort"."id" = 2
+ LIMIT 21
+ '
+---
# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_experience_continuity_flag.2
'
SELECT "posthog_person"."id",
@@ -1003,9 +1309,12 @@
"posthog_person"."uuid",
"posthog_person"."version"
FROM "posthog_person"
- WHERE "posthog_person"."team_id" = 2
+ WHERE ("posthog_person"."team_id" = 2
+ AND ("posthog_person"."properties" -> 'key') = '"value"'
+ AND "posthog_person"."properties" ? 'key'
+ AND NOT (("posthog_person"."properties" -> 'key') = 'null'))
ORDER BY "posthog_person"."id" ASC
- LIMIT 5000
+ LIMIT 1000
'
---
# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_experience_continuity_flag.3
@@ -1020,7 +1329,7 @@
(SELECT U0."id"
FROM "posthog_persondistinctid" U0
WHERE U0."person_id" = "posthog_persondistinctid"."person_id"
- LIMIT 1)
+ LIMIT 3)
AND "posthog_persondistinctid"."person_id" IN (1,
2,
3,
@@ -1083,10 +1392,13 @@
"posthog_person"."uuid",
"posthog_person"."version"
FROM "posthog_person"
- WHERE "posthog_person"."team_id" = 2
+ WHERE ("posthog_person"."team_id" = 2
+ AND ("posthog_person"."properties" -> 'key') = '"value"'
+ AND "posthog_person"."properties" ? 'key'
+ AND NOT (("posthog_person"."properties" -> 'key') = 'null'))
ORDER BY "posthog_person"."id" ASC
- LIMIT 5000
- OFFSET 5000
+ LIMIT 1000
+ OFFSET 1000
'
---
# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_experience_continuity_flag.8
@@ -1162,161 +1474,6 @@
LIMIT 21
'
---
-# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_group_flag
- '
- SELECT "posthog_featureflag"."id",
- "posthog_featureflag"."key",
- "posthog_featureflag"."name",
- "posthog_featureflag"."filters",
- "posthog_featureflag"."rollout_percentage",
- "posthog_featureflag"."team_id",
- "posthog_featureflag"."created_by_id",
- "posthog_featureflag"."created_at",
- "posthog_featureflag"."deleted",
- "posthog_featureflag"."active",
- "posthog_featureflag"."rollback_conditions",
- "posthog_featureflag"."performed_rollback",
- "posthog_featureflag"."ensure_experience_continuity",
- "posthog_featureflag"."usage_dashboard_id",
- "posthog_featureflag"."has_enriched_analytics"
- FROM "posthog_featureflag"
- WHERE ("posthog_featureflag"."key" = 'some-feature3'
- AND "posthog_featureflag"."team_id" = 2)
- LIMIT 21
- '
----
-# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_group_flag.1
- '
- SELECT "posthog_cohort"."id",
- "posthog_cohort"."name",
- "posthog_cohort"."description",
- "posthog_cohort"."team_id",
- "posthog_cohort"."deleted",
- "posthog_cohort"."filters",
- "posthog_cohort"."version",
- "posthog_cohort"."pending_version",
- "posthog_cohort"."count",
- "posthog_cohort"."created_by_id",
- "posthog_cohort"."created_at",
- "posthog_cohort"."is_calculating",
- "posthog_cohort"."last_calculation",
- "posthog_cohort"."errors_calculating",
- "posthog_cohort"."is_static",
- "posthog_cohort"."groups"
- FROM "posthog_cohort"
- WHERE "posthog_cohort"."id" = 2
- LIMIT 21
- '
----
-# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_group_flag.2
- '
- DECLARE "_django_curs_X" NO SCROLL
- CURSOR WITHOUT HOLD
- FOR
- SELECT "posthog_person"."id",
- "posthog_person"."created_at",
- "posthog_person"."properties_last_updated_at",
- "posthog_person"."properties_last_operation",
- "posthog_person"."team_id",
- "posthog_person"."properties",
- "posthog_person"."is_user_id",
- "posthog_person"."is_identified",
- "posthog_person"."uuid",
- "posthog_person"."version"
- FROM "posthog_person"
- WHERE "posthog_person"."team_id" = 2
- '
----
-# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_group_flag.3
- '
- SELECT "posthog_persondistinctid"."distinct_id"
- FROM "posthog_persondistinctid"
- WHERE ("posthog_persondistinctid"."person_id" = 2
- AND "posthog_persondistinctid"."team_id" = 2)
- LIMIT 1
- '
----
-# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_group_flag.4
- '
- SELECT "posthog_grouptypemapping"."id",
- "posthog_grouptypemapping"."team_id",
- "posthog_grouptypemapping"."group_type",
- "posthog_grouptypemapping"."group_type_index",
- "posthog_grouptypemapping"."name_singular",
- "posthog_grouptypemapping"."name_plural"
- FROM "posthog_grouptypemapping"
- WHERE "posthog_grouptypemapping"."team_id" = 2
- '
----
-# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_inactive_flag
- '
- SELECT "posthog_featureflag"."id",
- "posthog_featureflag"."key",
- "posthog_featureflag"."name",
- "posthog_featureflag"."filters",
- "posthog_featureflag"."rollout_percentage",
- "posthog_featureflag"."team_id",
- "posthog_featureflag"."created_by_id",
- "posthog_featureflag"."created_at",
- "posthog_featureflag"."deleted",
- "posthog_featureflag"."active",
- "posthog_featureflag"."rollback_conditions",
- "posthog_featureflag"."performed_rollback",
- "posthog_featureflag"."ensure_experience_continuity",
- "posthog_featureflag"."usage_dashboard_id",
- "posthog_featureflag"."has_enriched_analytics"
- FROM "posthog_featureflag"
- WHERE ("posthog_featureflag"."key" = 'some-feature2'
- AND "posthog_featureflag"."team_id" = 2)
- LIMIT 21
- '
----
-# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_invalid_flags
- '
- SELECT "posthog_featureflag"."id",
- "posthog_featureflag"."key",
- "posthog_featureflag"."name",
- "posthog_featureflag"."filters",
- "posthog_featureflag"."rollout_percentage",
- "posthog_featureflag"."team_id",
- "posthog_featureflag"."created_by_id",
- "posthog_featureflag"."created_at",
- "posthog_featureflag"."deleted",
- "posthog_featureflag"."active",
- "posthog_featureflag"."rollback_conditions",
- "posthog_featureflag"."performed_rollback",
- "posthog_featureflag"."ensure_experience_continuity",
- "posthog_featureflag"."usage_dashboard_id",
- "posthog_featureflag"."has_enriched_analytics"
- FROM "posthog_featureflag"
- WHERE ("posthog_featureflag"."key" = 'some-feature'
- AND "posthog_featureflag"."team_id" = 2)
- LIMIT 21
- '
----
-# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_non_existing_flag
- '
- SELECT "posthog_featureflag"."id",
- "posthog_featureflag"."key",
- "posthog_featureflag"."name",
- "posthog_featureflag"."filters",
- "posthog_featureflag"."rollout_percentage",
- "posthog_featureflag"."team_id",
- "posthog_featureflag"."created_by_id",
- "posthog_featureflag"."created_at",
- "posthog_featureflag"."deleted",
- "posthog_featureflag"."active",
- "posthog_featureflag"."rollback_conditions",
- "posthog_featureflag"."performed_rollback",
- "posthog_featureflag"."ensure_experience_continuity",
- "posthog_featureflag"."usage_dashboard_id",
- "posthog_featureflag"."has_enriched_analytics"
- FROM "posthog_featureflag"
- WHERE ("posthog_featureflag"."key" = 'some-feature2'
- AND "posthog_featureflag"."team_id" = 2)
- LIMIT 21
- '
----
# name: TestFeatureFlag.test_creating_static_cohort
'
SELECT "posthog_user"."id",
@@ -1409,7 +1566,7 @@
(SELECT U0."id"
FROM "posthog_persondistinctid" U0
WHERE U0."person_id" = "posthog_persondistinctid"."person_id"
- LIMIT 1)
+ LIMIT 3)
AND "posthog_persondistinctid"."person_id" IN (1,
2,
3,
@@ -1430,10 +1587,13 @@
"posthog_person"."uuid",
"posthog_person"."version"
FROM "posthog_person"
- WHERE "posthog_person"."team_id" = 2
+ WHERE ("posthog_person"."team_id" = 2
+ AND ("posthog_person"."properties" -> 'key') = '"value"'
+ AND "posthog_person"."properties" ? 'key'
+ AND NOT (("posthog_person"."properties" -> 'key') = 'null'))
ORDER BY "posthog_person"."id" ASC
- LIMIT 5000
- OFFSET 5000 /*controller='project_feature_flags-create-static-cohort-for-flag',route='api/projects/%28%3FP%3Cparent_lookup_team_id%3E%5B%5E/.%5D%2B%29/feature_flags/%28%3FP%3Cpk%3E%5B%5E/.%5D%2B%29/create_static_cohort_for_flag/%3F%24'*/
+ LIMIT 10000
+ OFFSET 10000 /*controller='project_feature_flags-create-static-cohort-for-flag',route='api/projects/%28%3FP%3Cparent_lookup_team_id%3E%5B%5E/.%5D%2B%29/feature_flags/%28%3FP%3Cpk%3E%5B%5E/.%5D%2B%29/create_static_cohort_for_flag/%3F%24'*/
'
---
# name: TestFeatureFlag.test_creating_static_cohort.12
@@ -1882,9 +2042,12 @@
"posthog_person"."uuid",
"posthog_person"."version"
FROM "posthog_person"
- WHERE "posthog_person"."team_id" = 2
+ WHERE ("posthog_person"."team_id" = 2
+ AND ("posthog_person"."properties" -> 'key') = '"value"'
+ AND "posthog_person"."properties" ? 'key'
+ AND NOT (("posthog_person"."properties" -> 'key') = 'null'))
ORDER BY "posthog_person"."id" ASC
- LIMIT 5000 /*controller='project_feature_flags-create-static-cohort-for-flag',route='api/projects/%28%3FP%3Cparent_lookup_team_id%3E%5B%5E/.%5D%2B%29/feature_flags/%28%3FP%3Cpk%3E%5B%5E/.%5D%2B%29/create_static_cohort_for_flag/%3F%24'*/
+ LIMIT 10000 /*controller='project_feature_flags-create-static-cohort-for-flag',route='api/projects/%28%3FP%3Cparent_lookup_team_id%3E%5B%5E/.%5D%2B%29/feature_flags/%28%3FP%3Cpk%3E%5B%5E/.%5D%2B%29/create_static_cohort_for_flag/%3F%24'*/
'
---
# name: TestResiliency.test_feature_flags_v3_with_experience_continuity_working_slow_db
diff --git a/posthog/api/test/test_feature_flag.py b/posthog/api/test/test_feature_flag.py
index 06997913184f8..9e33e55ca1a51 100644
--- a/posthog/api/test/test_feature_flag.py
+++ b/posthog/api/test/test_feature_flag.py
@@ -3738,6 +3738,38 @@ def test_creating_static_cohort_with_group_flag(self):
response = self.client.get(f"/api/cohort/{cohort.pk}/persons")
self.assertEqual(len(response.json()["results"]), 0, response)
+ def test_creating_static_cohort_with_no_person_distinct_ids(self):
+ FeatureFlag.objects.create(
+ team=self.team,
+ rollout_percentage=100,
+ filters={
+ "groups": [{"properties": [], "rollout_percentage": 100}],
+ "multivariate": None,
+ },
+ name="some feature",
+ key="some-feature2",
+ created_by=self.user,
+ )
+
+ Person.objects.create(team=self.team)
+
+ cohort = Cohort.objects.create(
+ team=self.team,
+ is_static=True,
+ name="some cohort",
+ )
+
+ with self.assertNumQueries(5):
+ get_cohort_actors_for_feature_flag(cohort.pk, "some-feature2", self.team.pk)
+
+ cohort.refresh_from_db()
+ self.assertEqual(cohort.name, "some cohort")
+ # don't even try inserting anything, because invalid flag, so None instead of 0
+ self.assertEqual(cohort.count, None)
+
+ response = self.client.get(f"/api/cohort/{cohort.pk}/persons")
+ self.assertEqual(len(response.json()["results"]), 0, response)
+
def test_creating_static_cohort_with_non_existing_flag(self):
cohort = Cohort.objects.create(
team=self.team,
@@ -3783,7 +3815,6 @@ def test_creating_static_cohort_with_experience_continuity_flag(self):
properties={"key": "value"},
)
flush_persons_and_events()
- # TODO: Check right person is added here
FeatureFlagHashKeyOverride.objects.create(
feature_flag_key="some-feature2",
@@ -3852,7 +3883,7 @@ def test_creating_static_cohort_iterator(self):
)
# Extra queries because each batch adds its own queries
- with snapshot_postgres_queries_context(self), self.assertNumQueries(17):
+ with snapshot_postgres_queries_context(self), self.assertNumQueries(14):
get_cohort_actors_for_feature_flag(cohort.pk, "some-feature2", self.team.pk, batchsize=2)
cohort.refresh_from_db()
@@ -3873,6 +3904,177 @@ def test_creating_static_cohort_iterator(self):
response = self.client.get(f"/api/cohort/{cohort.pk}/persons")
self.assertEqual(len(response.json()["results"]), 3, response)
+ def test_creating_static_cohort_with_default_person_properties_adjustment(self):
+ FeatureFlag.objects.create(
+ team=self.team,
+ filters={
+ "groups": [
+ {
+ "properties": [{"key": "key", "value": "value", "type": "person", "operator": "icontains"}],
+ "rollout_percentage": 100,
+ }
+ ],
+ "multivariate": None,
+ },
+ name="some feature",
+ key="some-feature2",
+ created_by=self.user,
+ ensure_experience_continuity=False,
+ )
+ FeatureFlag.objects.create(
+ team=self.team,
+ filters={
+ "groups": [
+ {
+ "properties": [{"key": "key", "value": "value", "type": "person", "operator": "is_set"}],
+ "rollout_percentage": 100,
+ }
+ ],
+ "multivariate": None,
+ },
+ name="some feature",
+ key="some-feature-new",
+ created_by=self.user,
+ ensure_experience_continuity=False,
+ )
+
+ _create_person(team=self.team, distinct_ids=[f"person1"], properties={"key": "value"})
+ _create_person(
+ team=self.team,
+ distinct_ids=[f"person2"],
+ properties={"key": "vaalue"},
+ )
+ _create_person(
+ team=self.team,
+ distinct_ids=[f"person3"],
+ properties={"key22": "value"},
+ )
+ flush_persons_and_events()
+
+ cohort = Cohort.objects.create(
+ team=self.team,
+ is_static=True,
+ name="some cohort",
+ )
+
+ with snapshot_postgres_queries_context(self), self.assertNumQueries(9):
+ # no queries to evaluate flags, because all evaluated using override properties
+ get_cohort_actors_for_feature_flag(cohort.pk, "some-feature2", self.team.pk)
+
+ cohort.refresh_from_db()
+ self.assertEqual(cohort.name, "some cohort")
+ self.assertEqual(cohort.count, 1)
+
+ response = self.client.get(f"/api/cohort/{cohort.pk}/persons")
+ self.assertEqual(len(response.json()["results"]), 1, response)
+
+ cohort2 = Cohort.objects.create(
+ team=self.team,
+ is_static=True,
+ name="some cohort2",
+ )
+
+ with snapshot_postgres_queries_context(self), self.assertNumQueries(9):
+ # person3 doesn't match filter conditions so is pre-filtered out
+ get_cohort_actors_for_feature_flag(cohort2.pk, "some-feature-new", self.team.pk)
+
+ cohort2.refresh_from_db()
+ self.assertEqual(cohort2.name, "some cohort2")
+ self.assertEqual(cohort2.count, 2)
+
+ def test_creating_static_cohort_with_cohort_flag_adds_cohort_props_as_default_too(self):
+ cohort_nested = Cohort.objects.create(
+ team=self.team,
+ filters={
+ "properties": {
+ "type": "OR",
+ "values": [
+ {
+ "type": "OR",
+ "values": [
+ {"key": "does-not-exist", "value": "none", "type": "person"},
+ ],
+ }
+ ],
+ }
+ },
+ )
+ cohort_static = Cohort.objects.create(
+ team=self.team,
+ is_static=True,
+ )
+ cohort_existing = Cohort.objects.create(
+ team=self.team,
+ filters={
+ "properties": {
+ "type": "OR",
+ "values": [
+ {
+ "type": "OR",
+ "values": [
+ {"key": "group", "value": "none", "type": "person"},
+ {"key": "group2", "value": [1, 2, 3], "type": "person"},
+ {"key": "id", "value": cohort_static.pk, "type": "cohort"},
+ {"key": "id", "value": cohort_nested.pk, "type": "cohort"},
+ ],
+ }
+ ],
+ }
+ },
+ name="cohort1",
+ )
+ FeatureFlag.objects.create(
+ team=self.team,
+ filters={
+ "groups": [
+ {
+ "properties": [{"key": "id", "value": cohort_existing.pk, "type": "cohort"}],
+ "rollout_percentage": 100,
+ },
+ {"properties": [{"key": "key", "value": "value", "type": "person"}], "rollout_percentage": 100},
+ ],
+ "multivariate": None,
+ },
+ name="some feature",
+ key="some-feature-new",
+ created_by=self.user,
+ ensure_experience_continuity=False,
+ )
+
+ _create_person(team=self.team, distinct_ids=[f"person1"], properties={"key": "value"})
+ _create_person(
+ team=self.team,
+ distinct_ids=[f"person2"],
+ properties={"group": "none"},
+ )
+ _create_person(
+ team=self.team,
+ distinct_ids=[f"person3"],
+ properties={"key22": "value", "group2": 2},
+ )
+ _create_person(
+ team=self.team,
+ distinct_ids=[f"person4"],
+ properties={},
+ )
+ flush_persons_and_events()
+
+ cohort_static.insert_users_by_list([f"person4"])
+
+ cohort = Cohort.objects.create(
+ team=self.team,
+ is_static=True,
+ name="some cohort",
+ )
+
+ with snapshot_postgres_queries_context(self), self.assertNumQueries(26):
+ # forced to evaluate flags by going to db, because cohorts need db query to evaluate
+ get_cohort_actors_for_feature_flag(cohort.pk, "some-feature-new", self.team.pk)
+
+ cohort.refresh_from_db()
+ self.assertEqual(cohort.name, "some cohort")
+ self.assertEqual(cohort.count, 4)
+
class TestBlastRadius(ClickhouseTestMixin, APIBaseTest):
@snapshot_clickhouse_queries
diff --git a/posthog/api/test/test_survey.py b/posthog/api/test/test_survey.py
index 75cd3d1c91e5b..e1b5d3163605b 100644
--- a/posthog/api/test/test_survey.py
+++ b/posthog/api/test/test_survey.py
@@ -1144,6 +1144,27 @@ def test_validate_malformed_questions_as_array_of_strings(self):
assert response.status_code == status.HTTP_400_BAD_REQUEST, response_data
assert response_data["detail"] == "Questions must be a list of objects"
+ def test_validate_malformed_question_choices_as_string(self):
+ response = self.client.post(
+ f"/api/projects/{self.team.id}/surveys/",
+ data={
+ "name": "Notebooks beta release survey",
+ "description": "Get feedback on the new notebooks feature",
+ "type": "popover",
+ "questions": [
+ {
+ "question": "this is my question",
+ "type": "multiple_choice",
+ "choices": "these are my question choices",
+ }
+ ],
+ },
+ format="json",
+ )
+ response_data = response.json()
+ assert response.status_code == status.HTTP_400_BAD_REQUEST, response_data
+ assert response_data["detail"] == "Question choices must be a list of strings"
+
class TestSurveysAPIList(BaseTest, QueryMatchingTest):
def setUp(self):
diff --git a/posthog/clickhouse/client/execute_async.py b/posthog/clickhouse/client/execute_async.py
index 7e42d52d4836c..d44c20b707223 100644
--- a/posthog/clickhouse/client/execute_async.py
+++ b/posthog/clickhouse/client/execute_async.py
@@ -141,16 +141,21 @@ def get_query_status(team_id, query_id):
def cancel_query(team_id, query_id):
- query_status = get_query_status(team_id, query_id)
+ try:
+ query_status = get_query_status(team_id, query_id)
+
+ if query_status.task_id:
+ logger.info("Got task id %s, attempting to revoke", query_status.task_id)
+ celery.app.control.revoke(query_status.task_id, terminate=True)
- if query_status.task_id:
- logger.info("Got task id %s, attempting to revoke", query_status.task_id)
- celery.app.control.revoke(query_status.task_id, terminate=True)
+ logger.info("Revoked task id %s", query_status.task_id)
+ except QueryNotFoundError:
+ # Continue, to attempt to cancel the query even if it's not a task
+ pass
- from posthog.clickhouse.cancel import cancel_query_on_cluster
+ from posthog.clickhouse.cancel import cancel_query_on_cluster
- logger.info("Revoked task id %s, attempting to cancel on cluster", query_status.task_id)
- cancel_query_on_cluster(team_id, query_id)
+ cancel_query_on_cluster(team_id, query_id)
redis_client = redis.get_client()
key = generate_redis_results_key(query_id, team_id)
diff --git a/posthog/session_recordings/models/session_recording.py b/posthog/session_recordings/models/session_recording.py
index 5ef51b34c2f1b..ca6bbc7b54d6e 100644
--- a/posthog/session_recordings/models/session_recording.py
+++ b/posthog/session_recordings/models/session_recording.py
@@ -84,7 +84,6 @@ def load_metadata(self) -> bool:
self._metadata = metadata
# Some fields of the metadata are persisted fully in the model
- # TODO there is more metadata we can add here
self.distinct_id = metadata["distinct_id"]
self.start_time = metadata["start_time"]
self.end_time = metadata["end_time"]
@@ -92,6 +91,12 @@ def load_metadata(self) -> bool:
self.click_count = metadata["click_count"]
self.keypress_count = metadata["keypress_count"]
self.set_start_url_from_urls(first_url=metadata["first_url"])
+ self.mouse_activity_count = metadata["mouse_activity_count"]
+ self.active_seconds = metadata["active_seconds"]
+ self.inactive_seconds = metadata["duration"] - metadata["active_seconds"]
+ self.console_log_count = metadata["console_log_count"]
+ self.console_warn_count = metadata["console_warn_count"]
+ self.console_error_count = metadata["console_error_count"]
return True
diff --git a/posthog/session_recordings/test/test_session_recordings.py b/posthog/session_recordings/test/test_session_recordings.py
index f1d1c22e9e98e..e349741ab1069 100644
--- a/posthog/session_recordings/test/test_session_recordings.py
+++ b/posthog/session_recordings/test/test_session_recordings.py
@@ -327,12 +327,12 @@ def test_get_single_session_recording_metadata(self):
"click_count": 0,
"keypress_count": 0,
"start_url": None,
- "mouse_activity_count": None,
- "inactive_seconds": None,
- "active_seconds": None,
- "console_error_count": None,
- "console_log_count": None,
- "console_warn_count": None,
+ "mouse_activity_count": 0,
+ "inactive_seconds": 30,
+ "active_seconds": 0,
+ "console_error_count": 0,
+ "console_log_count": 0,
+ "console_warn_count": 0,
"person": {
"id": p.id,
"name": "bob@bob.com",
diff --git a/posthog/settings/sentry.py b/posthog/settings/sentry.py
index d38d73300f792..208c862c7fa7e 100644
--- a/posthog/settings/sentry.py
+++ b/posthog/settings/sentry.py
@@ -114,6 +114,14 @@ def traces_sampler(sampling_context: dict) -> float:
else:
# Default sample rate for Celery tasks
return 0.001 # 0.1%
+ elif op == "queue.task.celery":
+ task = sampling_context.get("celery_job", {}).get("task")
+ if task == "posthog.tasks.calculate_cohort.insert_cohort_from_feature_flag":
+ # sample all cohort calculations via feature flag
+ return 1
+ # Default sample rate
+ return 0.01
+
else:
# Default sample rate for everything else
return 0.01 # 1%
diff --git a/posthog/tasks/calculate_cohort.py b/posthog/tasks/calculate_cohort.py
index 066469636dc37..b4ff3a9aff390 100644
--- a/posthog/tasks/calculate_cohort.py
+++ b/posthog/tasks/calculate_cohort.py
@@ -77,4 +77,4 @@ def insert_cohort_from_insight_filter(cohort_id: int, filter_data: Dict[str, Any
def insert_cohort_from_feature_flag(cohort_id: int, flag_key: str, team_id: int) -> None:
from posthog.api.cohort import get_cohort_actors_for_feature_flag
- get_cohort_actors_for_feature_flag(cohort_id, flag_key, team_id)
+ get_cohort_actors_for_feature_flag(cohort_id, flag_key, team_id, batchsize=10_000)
diff --git a/posthog/warehouse/models/table.py b/posthog/warehouse/models/table.py
index 2acf4fb1faf9b..93e6a20e890f2 100644
--- a/posthog/warehouse/models/table.py
+++ b/posthog/warehouse/models/table.py
@@ -43,6 +43,7 @@
"Array": StringArrayDatabaseField,
"Map": StringJSONDatabaseField,
"Bool": BooleanDatabaseField,
+ "Decimal": IntegerDatabaseField,
}
ExtractErrors = {
diff --git a/requirements.txt b/requirements.txt
index 1efa70e5318f9..752fd3e600466 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,11 +4,13 @@
#
# pip-compile requirements.in
#
-aioboto3==11.1
+aioboto3==11.1.0
# via -r requirements.in
aiobotocore[boto3]==2.5.0
- # via aioboto3
-aiohttp==3.8.5
+ # via
+ # aioboto3
+ # aiobotocore
+aiohttp==3.8.6
# via
# -r requirements.in
# aiobotocore
@@ -223,6 +225,7 @@ geoip2==4.6.0
# via -r requirements.in
google-api-core[grpc]==2.11.1
# via
+ # google-api-core
# google-cloud-bigquery
# google-cloud-core
google-auth==2.22.0
diff --git a/tsconfig.json b/tsconfig.json
index d00e7af6a118b..ef6815e5be0d8 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -9,6 +9,7 @@
"@posthog/lemon-ui": ["@posthog/lemon-ui/src/index"],
"@posthog/lemon-ui/*": ["@posthog/lemon-ui/src/*"],
"storybook/*": ["../.storybook/*"],
+ "@posthog/ee/exports": ["../ee/exports", "@posthog/ee/exports"],
"~/*": ["src/*"],
"public/*": ["public/*"]
},
@@ -33,7 +34,7 @@
"suppressImplicitAnyIndexErrors": true, // Index objects by number
"lib": ["dom", "es2019"]
},
- "include": ["frontend/**/*", ".storybook/**/*"],
+ "include": ["frontend/**/*", ".storybook/**/*", "ee/frontend/**/*"],
"exclude": ["frontend/dist/**/*"],
"ts-node": {
"compilerOptions": {
diff --git a/webpack.config.js b/webpack.config.js
index afa086a96c11c..aef12b7ab0c4b 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -56,6 +56,10 @@ function createEntry(entry) {
scenes: path.resolve(__dirname, 'frontend', 'src', 'scenes'),
'@posthog/apps-common': path.resolve(__dirname, 'frontend', '@posthog', 'apps-common', 'src'),
'@posthog/lemon-ui': path.resolve(__dirname, 'frontend', '@posthog', 'lemon-ui', 'src'),
+ '@posthog/ee/exports': [
+ path.resolve(__dirname, 'ee', 'frontend', 'exports'),
+ path.resolve(__dirname, 'frontend', '@posthog', 'ee', 'exports'),
+ ],
storybook: path.resolve(__dirname, '.storybook'),
types: path.resolve(__dirname, 'frontend', 'types'),
public: path.resolve(__dirname, 'frontend', 'public'),