@open-mercato/ui 0.6.3-develop.3562.1.71049275ff → 0.6.3-develop.3574.1.f3c5dcf345
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backend/injection/useAppEvent.js +2 -7
- package/dist/backend/injection/useAppEvent.js.map +2 -2
- package/jest.config.cjs +1 -0
- package/package.json +3 -3
- package/src/ai/__tests__/AiAssistantLauncher.test.tsx +1 -1
- package/src/ai/__tests__/AiChat.test.tsx +1 -1
- package/src/backend/__tests__/AppShell.test.tsx +1 -1
- package/src/backend/__tests__/SidebarCustomizationEditor.test.tsx +8 -1
- package/src/backend/injection/__tests__/useAppEvent.test.ts +16 -0
- package/src/backend/injection/useAppEvent.ts +2 -9
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useEffect, useRef } from "react";
|
|
3
|
+
import { matchWildcardPattern } from "@open-mercato/shared/lib/patterns/wildcard";
|
|
3
4
|
const APP_EVENT_DOM_NAME = "om:event";
|
|
4
5
|
function matchesPattern(pattern, eventId) {
|
|
5
|
-
|
|
6
|
-
if (!pattern.includes("*")) return pattern === eventId;
|
|
7
|
-
const escapedPattern = pattern.replace(/[|\\{}()[\]^$+?.]/g, "\\$&").replace(/\*/g, ".*");
|
|
8
|
-
const regex = new RegExp(
|
|
9
|
-
"^" + escapedPattern + "$"
|
|
10
|
-
);
|
|
11
|
-
return regex.test(eventId);
|
|
6
|
+
return matchWildcardPattern(eventId, pattern);
|
|
12
7
|
}
|
|
13
8
|
function useAppEvent(eventPattern, handler, deps = []) {
|
|
14
9
|
const handlerRef = useRef(handler);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/backend/injection/useAppEvent.ts"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport { useEffect, useRef } from 'react'\nimport type { AppEventPayload } from '@open-mercato/shared/modules/widgets/injection'\n\n/**\n * DOM Event Bridge event name.\n * Server-side events with `clientBroadcast: true` are dispatched as\n * CustomEvents with this name on the window object.\n */\nexport const APP_EVENT_DOM_NAME = 'om:event'\n\n/**\n * Match an event ID against a wildcard pattern.\n *\n * Supports:\n * - Exact match: `'example.todo.created'`\n * - Wildcard suffix: `'example.todo.*'` matches `'example.todo.created'`, `'example.todo.updated'`\n * - Global wildcard: `'*'` matches everything\n *\n * @example\n * matchesPattern('example.todo.*', 'example.todo.created') // true\n * matchesPattern('example.todo.*', 'example.item.created') // false\n * matchesPattern('*', 'anything.here') // true\n */\nexport function matchesPattern(pattern: string, eventId: string): boolean {\n
|
|
5
|
-
"mappings": ";AACA,SAAS,WAAW,cAAc;
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport { useEffect, useRef } from 'react'\nimport type { AppEventPayload } from '@open-mercato/shared/modules/widgets/injection'\nimport { matchWildcardPattern } from '@open-mercato/shared/lib/patterns/wildcard'\n\n/**\n * DOM Event Bridge event name.\n * Server-side events with `clientBroadcast: true` are dispatched as\n * CustomEvents with this name on the window object.\n */\nexport const APP_EVENT_DOM_NAME = 'om:event'\n\n/**\n * Match an event ID against a wildcard pattern.\n *\n * Supports:\n * - Exact match: `'example.todo.created'`\n * - Wildcard suffix: `'example.todo.*'` matches `'example.todo.created'`, `'example.todo.updated'`\n * - Global wildcard: `'*'` matches everything\n *\n * @example\n * matchesPattern('example.todo.*', 'example.todo.created') // true\n * matchesPattern('example.todo.*', 'example.item.created') // false\n * matchesPattern('*', 'anything.here') // true\n */\nexport function matchesPattern(pattern: string, eventId: string): boolean {\n return matchWildcardPattern(eventId, pattern)\n}\n\n/**\n * React hook that listens for app events delivered via the DOM Event Bridge.\n *\n * Events are dispatched as `om:event` CustomEvents on `window` by the event bridge\n * (see `eventBridge.ts`). This hook filters events by pattern and calls the handler.\n *\n * @param eventPattern - Pattern to match event IDs against (e.g., 'example.todo.*')\n * @param handler - Callback invoked when a matching event arrives\n * @param deps - Optional dependency array for the handler (defaults to [])\n *\n * @example\n * useAppEvent('example.todo.*', (event) => {\n * console.log('Todo event:', event.id, event.payload)\n * setRefreshKey(k => k + 1)\n * })\n */\nexport function useAppEvent(\n eventPattern: string,\n handler: (payload: AppEventPayload) => void,\n deps: unknown[] = [],\n): void {\n const handlerRef = useRef(handler)\n handlerRef.current = handler\n\n useEffect(() => {\n const listener = (e: Event) => {\n const detail = (e as CustomEvent<AppEventPayload>).detail\n if (!detail || typeof detail.id !== 'string') return\n if (matchesPattern(eventPattern, detail.id)) {\n handlerRef.current(detail)\n }\n }\n\n window.addEventListener(APP_EVENT_DOM_NAME, listener)\n return () => {\n window.removeEventListener(APP_EVENT_DOM_NAME, listener)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [eventPattern, ...deps])\n}\n"],
|
|
5
|
+
"mappings": ";AACA,SAAS,WAAW,cAAc;AAElC,SAAS,4BAA4B;AAO9B,MAAM,qBAAqB;AAe3B,SAAS,eAAe,SAAiB,SAA0B;AACxE,SAAO,qBAAqB,SAAS,OAAO;AAC9C;AAkBO,SAAS,YACd,cACA,SACA,OAAkB,CAAC,GACb;AACN,QAAM,aAAa,OAAO,OAAO;AACjC,aAAW,UAAU;AAErB,YAAU,MAAM;AACd,UAAM,WAAW,CAAC,MAAa;AAC7B,YAAM,SAAU,EAAmC;AACnD,UAAI,CAAC,UAAU,OAAO,OAAO,OAAO,SAAU;AAC9C,UAAI,eAAe,cAAc,OAAO,EAAE,GAAG;AAC3C,mBAAW,QAAQ,MAAM;AAAA,MAC3B;AAAA,IACF;AAEA,WAAO,iBAAiB,oBAAoB,QAAQ;AACpD,WAAO,MAAM;AACX,aAAO,oBAAoB,oBAAoB,QAAQ;AAAA,IACzD;AAAA,EAEF,GAAG,CAAC,cAAc,GAAG,IAAI,CAAC;AAC5B;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/jest.config.cjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/ui",
|
|
3
|
-
"version": "0.6.3-develop.
|
|
3
|
+
"version": "0.6.3-develop.3574.1.f3c5dcf345",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -141,13 +141,13 @@
|
|
|
141
141
|
"remark-gfm": "^4.0.1"
|
|
142
142
|
},
|
|
143
143
|
"peerDependencies": {
|
|
144
|
-
"@open-mercato/shared": "0.6.3-develop.
|
|
144
|
+
"@open-mercato/shared": "0.6.3-develop.3574.1.f3c5dcf345",
|
|
145
145
|
"react": ">=18.0.0",
|
|
146
146
|
"react-dom": ">=18.0.0",
|
|
147
147
|
"react-is": ">=18.0.0"
|
|
148
148
|
},
|
|
149
149
|
"devDependencies": {
|
|
150
|
-
"@open-mercato/shared": "0.6.3-develop.
|
|
150
|
+
"@open-mercato/shared": "0.6.3-develop.3574.1.f3c5dcf345",
|
|
151
151
|
"@testing-library/dom": "^10.4.1",
|
|
152
152
|
"@testing-library/jest-dom": "^6.9.1",
|
|
153
153
|
"@testing-library/react": "^16.3.1",
|
|
@@ -293,7 +293,7 @@ describe('<AiChat>', () => {
|
|
|
293
293
|
expect(parsedUrl.searchParams.get('agent')).toBe('customers.deal_analyzer')
|
|
294
294
|
expect(parsedUrl.searchParams.has('provider')).toBe(false)
|
|
295
295
|
expect(parsedUrl.searchParams.has('model')).toBe(false)
|
|
296
|
-
},
|
|
296
|
+
}, 15_000)
|
|
297
297
|
|
|
298
298
|
it('renders a compact footer before a constrained host is measured', async () => {
|
|
299
299
|
const apiCallMock = apiCall as unknown as jest.Mock
|
|
@@ -600,7 +600,7 @@ describe('AppShell', () => {
|
|
|
600
600
|
|
|
601
601
|
await waitFor(() => {
|
|
602
602
|
expect(screen.getByText('Pay Links')).toBeInTheDocument()
|
|
603
|
-
})
|
|
603
|
+
}, { timeout: 10_000 })
|
|
604
604
|
|
|
605
605
|
const link = screen.getByRole('link', { name: 'Pay Links' })
|
|
606
606
|
expect(link.querySelector('svg.lucide-ticket')).toBeTruthy()
|
|
@@ -55,6 +55,13 @@ jest.mock('../injection/mutationEvents', () => ({
|
|
|
55
55
|
dispatchBackendMutationError: jest.fn(),
|
|
56
56
|
}))
|
|
57
57
|
|
|
58
|
+
jest.mock('../injection/useGuardedMutation', () => ({
|
|
59
|
+
useGuardedMutation: () => ({
|
|
60
|
+
runMutation: async ({ operation }: { operation: () => Promise<unknown> }) => operation(),
|
|
61
|
+
retryLastMutation: jest.fn(async () => false),
|
|
62
|
+
}),
|
|
63
|
+
}))
|
|
64
|
+
|
|
58
65
|
const fakeGroups = [
|
|
59
66
|
{
|
|
60
67
|
id: 'core',
|
|
@@ -218,7 +225,7 @@ describe('SidebarCustomizationEditor', () => {
|
|
|
218
225
|
expect(screen.getByRole('dialog')).toBeInTheDocument()
|
|
219
226
|
|
|
220
227
|
expect(screen.getAllByText(duplicateMessage)).toHaveLength(1)
|
|
221
|
-
})
|
|
228
|
+
}, 15_000)
|
|
222
229
|
|
|
223
230
|
it('clears the inline dialog error when the user edits the name', async () => {
|
|
224
231
|
const duplicateMessage = 'A variant with this name already exists. Choose a different name.'
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { matchesPattern } from '../useAppEvent'
|
|
2
|
+
|
|
3
|
+
describe('matchesPattern', () => {
|
|
4
|
+
it('matches DOM event wildcard patterns without regex execution', () => {
|
|
5
|
+
expect(matchesPattern('*', 'customers.person.created')).toBe(true)
|
|
6
|
+
expect(matchesPattern('customers.person.*', 'customers.person.created')).toBe(true)
|
|
7
|
+
expect(matchesPattern('customers.*.created', 'customers.person.created')).toBe(true)
|
|
8
|
+
expect(matchesPattern('sales.*', 'customers.person.created')).toBe(false)
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
it('treats regex metacharacters as literal event characters', () => {
|
|
12
|
+
expect(matchesPattern('customers.(person).*', 'customers.(person).created')).toBe(true)
|
|
13
|
+
expect(matchesPattern('customers.+.*', 'customers.person.created')).toBe(false)
|
|
14
|
+
})
|
|
15
|
+
})
|
|
16
|
+
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
import { useEffect, useRef } from 'react'
|
|
3
3
|
import type { AppEventPayload } from '@open-mercato/shared/modules/widgets/injection'
|
|
4
|
+
import { matchWildcardPattern } from '@open-mercato/shared/lib/patterns/wildcard'
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* DOM Event Bridge event name.
|
|
@@ -23,15 +24,7 @@ export const APP_EVENT_DOM_NAME = 'om:event'
|
|
|
23
24
|
* matchesPattern('*', 'anything.here') // true
|
|
24
25
|
*/
|
|
25
26
|
export function matchesPattern(pattern: string, eventId: string): boolean {
|
|
26
|
-
|
|
27
|
-
if (!pattern.includes('*')) return pattern === eventId
|
|
28
|
-
const escapedPattern = pattern
|
|
29
|
-
.replace(/[|\\{}()[\]^$+?.]/g, '\\$&')
|
|
30
|
-
.replace(/\*/g, '.*')
|
|
31
|
-
const regex = new RegExp(
|
|
32
|
-
'^' + escapedPattern + '$',
|
|
33
|
-
)
|
|
34
|
-
return regex.test(eventId)
|
|
27
|
+
return matchWildcardPattern(eventId, pattern)
|
|
35
28
|
}
|
|
36
29
|
|
|
37
30
|
/**
|