@vyriy/event 0.1.9

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vyriy contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,123 @@
1
+ # @vyriy/event
2
+
3
+ Shared helpers for building and dispatching `CustomEvent` in Vyriy microfrontends.
4
+
5
+ ## Purpose
6
+
7
+ This package implements two small outbound event helper groups aligned with the openmfe event rules:
8
+
9
+ - `custom` validates and dispatches microfrontend-owned custom events
10
+ - `analytics` creates and dispatches the standard `openmfe.analytics` event
11
+
12
+ Invalid input throws an `Error`.
13
+
14
+ The root package entry point re-exports both groups for backwards-compatible imports.
15
+
16
+ ## Install
17
+
18
+ With npm:
19
+
20
+ ```bash
21
+ npm install @vyriy/event
22
+ ```
23
+
24
+ With Yarn:
25
+
26
+ ```bash
27
+ yarn add @vyriy/event
28
+ ```
29
+
30
+ ## Event Naming Rules
31
+
32
+ Regular custom event names must:
33
+
34
+ - use lowercase latin letters, numbers, dashes, and dots
35
+ - start with the microfrontend tag name
36
+ - contain at least one additional name level
37
+ - start and end with a lowercase latin letter
38
+
39
+ Example of a valid event name:
40
+
41
+ ```text
42
+ mf-shell.checkout.completed
43
+ ```
44
+
45
+ ## Usage
46
+
47
+ Create a validated event:
48
+
49
+ ```ts
50
+ import { createCustomEvent } from '@vyriy/event';
51
+
52
+ const event = createCustomEvent('mf-shell', 'mf-shell.checkout.completed', {
53
+ orderId: '42',
54
+ });
55
+ ```
56
+
57
+ Dispatch an event from the microfrontend element itself:
58
+
59
+ ```ts
60
+ import { dispatchCustomEvent } from '@vyriy/event';
61
+
62
+ const element = document.querySelector('mf-shell');
63
+
64
+ dispatchCustomEvent(element!, 'mf-shell.checkout.completed', {
65
+ orderId: '42',
66
+ });
67
+ ```
68
+
69
+ Emit the standard openmfe analytics event:
70
+
71
+ ```ts
72
+ import { dispatchAnalyticsEvent } from '@vyriy/event';
73
+
74
+ const element = document.querySelector('mf-shell');
75
+
76
+ dispatchAnalyticsEvent(element!, {
77
+ name: 'checkout_submit',
78
+ action: 'submit form',
79
+ category: 'checkout',
80
+ variant: 'b',
81
+ data: {
82
+ step: 2,
83
+ },
84
+ });
85
+ ```
86
+
87
+ This produces a `CustomEvent` with the fixed name `openmfe.analytics` and a `detail` payload like:
88
+
89
+ ```ts
90
+ {
91
+ name: 'checkout_submit',
92
+ origin: 'mf-shell',
93
+ id: 'checkout-widget',
94
+ variant: 'b',
95
+ action: 'submit form',
96
+ category: 'checkout',
97
+ data: {
98
+ step: 2,
99
+ },
100
+ }
101
+ ```
102
+
103
+ When `id`, `variant`, or `category` are not available, they are set to `null`. During dispatch, `origin` is inferred from the element `tagName`, and `id` is inferred from the element `id` unless explicitly provided.
104
+
105
+ ## API
106
+
107
+ Root entry:
108
+
109
+ - `@vyriy/event` re-exports the public `custom` and `analytics` helpers
110
+ - `validateEventName(origin, name)` validates and normalizes a regular custom event name
111
+ - `createCustomEvent(origin, name, detail, options?)` creates a validated custom `CustomEvent`
112
+ - `dispatchCustomEvent(target, name, detail, options?)` creates and dispatches a validated custom event on the provided target
113
+ - `createAnalyticsEvent(origin, input, options?)` creates the standard `openmfe.analytics` event
114
+ - `dispatchAnalyticsEvent(target, input, options?)` creates and dispatches the standard analytics event
115
+
116
+ ## Exported Types
117
+
118
+ - `CustomEventTargetLike`
119
+ - `CustomEventOptions`
120
+ - `AnalyticsEventTargetLike`
121
+ - `AnalyticsEventOptions`
122
+ - `AnalyticsInput<Data>`
123
+ - `AnalyticsDetail<Data>`
@@ -0,0 +1 @@
1
+ export declare const OPENMFE_ANALYTICS_EVENT_NAME = "openmfe.analytics";
@@ -0,0 +1 @@
1
+ export const OPENMFE_ANALYTICS_EVENT_NAME = 'openmfe.analytics';
@@ -0,0 +1,3 @@
1
+ import type { CreateAnalyticsEvent, DispatchAnalyticsEvent } from './types.js';
2
+ export declare const createAnalyticsEvent: CreateAnalyticsEvent;
3
+ export declare const dispatchAnalyticsEvent: DispatchAnalyticsEvent;
@@ -0,0 +1,31 @@
1
+ import { OPENMFE_ANALYTICS_EVENT_NAME } from './constants.js';
2
+ import { readAnalyticsId, readOriginFromTarget } from './target.js';
3
+ import { assertNonEmpty, normalizeOrigin } from './validation.js';
4
+ export const createAnalyticsEvent = (origin, input, options = {}) => {
5
+ const normalizedOrigin = normalizeOrigin(origin);
6
+ const name = assertNonEmpty(input.name, 'Analytics event detail.name');
7
+ const action = assertNonEmpty(input.action, 'Analytics event detail.action');
8
+ const id = input.id ?? null;
9
+ const category = input.category ?? null;
10
+ const variant = input.variant ?? null;
11
+ return new CustomEvent(OPENMFE_ANALYTICS_EVENT_NAME, {
12
+ ...options,
13
+ detail: {
14
+ action,
15
+ category,
16
+ data: input.data,
17
+ id,
18
+ name,
19
+ origin: normalizedOrigin,
20
+ variant,
21
+ },
22
+ });
23
+ };
24
+ export const dispatchAnalyticsEvent = (target, input, options = {}) => {
25
+ const event = createAnalyticsEvent(readOriginFromTarget(target), {
26
+ ...input,
27
+ id: readAnalyticsId(target, input),
28
+ }, options);
29
+ target.dispatchEvent(event);
30
+ return event;
31
+ };
@@ -0,0 +1,2 @@
1
+ export { createAnalyticsEvent, dispatchAnalyticsEvent } from './event.js';
2
+ export type * from './types.js';
@@ -0,0 +1 @@
1
+ export { createAnalyticsEvent, dispatchAnalyticsEvent } from './event.js';
@@ -0,0 +1,4 @@
1
+ import type { AnalyticsEventTargetLike, AnalyticsInput } from './types.js';
2
+ export declare const readOriginFromTarget: (target: AnalyticsEventTargetLike) => string;
3
+ export declare const readIdFromTarget: (target: AnalyticsEventTargetLike) => string | null;
4
+ export declare const readAnalyticsId: <Data>(target: AnalyticsEventTargetLike, input: AnalyticsInput<Data>) => string | null;
@@ -0,0 +1,18 @@
1
+ import { normalizeOrigin } from './validation.js';
2
+ export const readOriginFromTarget = (target) => {
3
+ const tagName = target.tagName ?? '';
4
+ return normalizeOrigin(tagName);
5
+ };
6
+ export const readIdFromTarget = (target) => {
7
+ if (typeof target.id !== 'string') {
8
+ return null;
9
+ }
10
+ const normalized = target.id.trim();
11
+ return normalized || null;
12
+ };
13
+ export const readAnalyticsId = (target, input) => {
14
+ if ('id' in input) {
15
+ return input.id ?? null;
16
+ }
17
+ return readIdFromTarget(target);
18
+ };
@@ -0,0 +1,28 @@
1
+ export type AnalyticsEventTargetLike = Pick<EventTarget, 'dispatchEvent'> & {
2
+ id?: string;
3
+ tagName?: string;
4
+ };
5
+ export type AnalyticsEventOptions = {
6
+ bubbles?: boolean;
7
+ cancelable?: boolean;
8
+ composed?: boolean;
9
+ };
10
+ export type AnalyticsDetail<Data = Record<string, unknown>> = {
11
+ name: string;
12
+ origin: string;
13
+ id: string | null;
14
+ variant: string | null;
15
+ action: string;
16
+ category: string | null;
17
+ data: Data;
18
+ };
19
+ export type AnalyticsInput<Data = Record<string, unknown>> = {
20
+ name: string;
21
+ id?: string | null;
22
+ variant?: string | null;
23
+ action: string;
24
+ category?: string | null;
25
+ data: Data;
26
+ };
27
+ export type CreateAnalyticsEvent = <Data>(origin: string, input: AnalyticsInput<Data>, options?: AnalyticsEventOptions) => CustomEvent<AnalyticsDetail<Data>>;
28
+ export type DispatchAnalyticsEvent = <Data>(target: AnalyticsEventTargetLike, input: AnalyticsInput<Data>, options?: AnalyticsEventOptions) => CustomEvent<AnalyticsDetail<Data>>;
@@ -0,0 +1,2 @@
1
+ export declare const assertNonEmpty: (value: string, label: string) => string;
2
+ export declare const normalizeOrigin: (origin: string) => string;
@@ -0,0 +1,14 @@
1
+ export const assertNonEmpty = (value, label) => {
2
+ const normalized = value.trim();
3
+ if (!normalized) {
4
+ throw new Error(`${label} must not be empty.`);
5
+ }
6
+ return normalized;
7
+ };
8
+ export const normalizeOrigin = (origin) => {
9
+ const normalized = assertNonEmpty(origin, 'Event origin').toLowerCase();
10
+ if (!/^[a-z](?:[a-z0-9-]*[a-z])?$/.test(normalized)) {
11
+ throw new Error(`Invalid event origin "${origin}". Origins must contain only lowercase latin letters, numbers, and dashes, and must start and end with a lowercase latin letter.`);
12
+ }
13
+ return normalized;
14
+ };
@@ -0,0 +1,2 @@
1
+ export declare const OPENMFE_NAME_PATTERN: RegExp;
2
+ export declare const TAG_NAME_PATTERN: RegExp;
@@ -0,0 +1,2 @@
1
+ export const OPENMFE_NAME_PATTERN = /^[a-z](?:[a-z0-9-]*[a-z])?(?:\.[a-z](?:[a-z0-9-]*[a-z])?)+$/;
2
+ export const TAG_NAME_PATTERN = /^[a-z](?:[a-z0-9-]*[a-z])?$/;
@@ -0,0 +1,3 @@
1
+ import type { CreateCustomEvent, DispatchCustomEvent } from './types.js';
2
+ export declare const createCustomEvent: CreateCustomEvent;
3
+ export declare const dispatchCustomEvent: DispatchCustomEvent;
@@ -0,0 +1,11 @@
1
+ import { readOriginFromTarget } from './target.js';
2
+ import { validateEventName } from './validation.js';
3
+ export const createCustomEvent = (origin, name, detail, options = {}) => new CustomEvent(validateEventName(origin, name), {
4
+ ...options,
5
+ detail,
6
+ });
7
+ export const dispatchCustomEvent = (target, name, detail, options = {}) => {
8
+ const event = createCustomEvent(readOriginFromTarget(target), name, detail, options);
9
+ target.dispatchEvent(event);
10
+ return event;
11
+ };
@@ -0,0 +1,3 @@
1
+ export { createCustomEvent, dispatchCustomEvent } from './event.js';
2
+ export { validateEventName } from './validation.js';
3
+ export type * from './types.js';
@@ -0,0 +1,2 @@
1
+ export { createCustomEvent, dispatchCustomEvent } from './event.js';
2
+ export { validateEventName } from './validation.js';
@@ -0,0 +1,2 @@
1
+ import type { CustomEventTargetLike } from './types.js';
2
+ export declare const readOriginFromTarget: (target: CustomEventTargetLike) => string;
@@ -0,0 +1,5 @@
1
+ import { normalizeOrigin } from './validation.js';
2
+ export const readOriginFromTarget = (target) => {
3
+ const tagName = target.tagName ?? '';
4
+ return normalizeOrigin(tagName);
5
+ };
@@ -0,0 +1,11 @@
1
+ export type CustomEventTargetLike = Pick<EventTarget, 'dispatchEvent'> & {
2
+ tagName?: string;
3
+ };
4
+ export type CustomEventOptions = {
5
+ bubbles?: boolean;
6
+ cancelable?: boolean;
7
+ composed?: boolean;
8
+ };
9
+ export type ValidateEventName = (origin: string, name: string) => string;
10
+ export type CreateCustomEvent = <Detail>(origin: string, name: string, detail: Detail, options?: CustomEventOptions) => CustomEvent<Detail>;
11
+ export type DispatchCustomEvent = <Detail>(target: CustomEventTargetLike, name: string, detail: Detail, options?: CustomEventOptions) => CustomEvent<Detail>;
@@ -0,0 +1,4 @@
1
+ import type { ValidateEventName } from './types.js';
2
+ export declare const assertNonEmpty: (value: string, label: string) => string;
3
+ export declare const normalizeOrigin: (origin: string) => string;
4
+ export declare const validateEventName: ValidateEventName;
@@ -0,0 +1,27 @@
1
+ import { OPENMFE_NAME_PATTERN, TAG_NAME_PATTERN } from './constants.js';
2
+ export const assertNonEmpty = (value, label) => {
3
+ const normalized = value.trim();
4
+ if (!normalized) {
5
+ throw new Error(`${label} must not be empty.`);
6
+ }
7
+ return normalized;
8
+ };
9
+ export const normalizeOrigin = (origin) => {
10
+ const normalized = assertNonEmpty(origin, 'Event origin').toLowerCase();
11
+ if (!TAG_NAME_PATTERN.test(normalized)) {
12
+ throw new Error(`Invalid event origin "${origin}". Origins must contain only lowercase latin letters, numbers, and dashes, and must start and end with a lowercase latin letter.`);
13
+ }
14
+ return normalized;
15
+ };
16
+ export const validateEventName = (origin, name) => {
17
+ const normalizedOrigin = normalizeOrigin(origin);
18
+ const normalizedName = assertNonEmpty(name, 'Event name').toLowerCase();
19
+ if (!OPENMFE_NAME_PATTERN.test(normalizedName)) {
20
+ throw new Error(`Invalid event name "${name}". Event names must use reverse domain notation with lowercase latin letters, numbers, dashes, and dots.`);
21
+ }
22
+ const [rootName] = normalizedName.split('.');
23
+ if (rootName !== normalizedOrigin) {
24
+ throw new Error(`Invalid event name "${name}". Event names must start with the microfrontend tag name "${normalizedOrigin}".`);
25
+ }
26
+ return normalizedName;
27
+ };
package/index.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ export { createAnalyticsEvent, dispatchAnalyticsEvent } from './analytics/index.js';
2
+ export type { AnalyticsDetail, AnalyticsEventOptions, AnalyticsEventTargetLike, AnalyticsInput, CreateAnalyticsEvent, DispatchAnalyticsEvent, } from './analytics/index.js';
3
+ export { createCustomEvent, dispatchCustomEvent, validateEventName } from './custom/index.js';
4
+ export type { CreateCustomEvent, CustomEventOptions, CustomEventTargetLike, DispatchCustomEvent, ValidateEventName, } from './custom/index.js';
package/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { createAnalyticsEvent, dispatchAnalyticsEvent } from './analytics/index.js';
2
+ export { createCustomEvent, dispatchCustomEvent, validateEventName } from './custom/index.js';
package/package.json ADDED
@@ -0,0 +1,126 @@
1
+ {
2
+ "name": "@vyriy/event",
3
+ "version": "0.1.9",
4
+ "description": "Shared event package for Vyriy projects",
5
+ "type": "module",
6
+ "main": "./index.js",
7
+ "license": "MIT",
8
+ "types": "./index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./index.d.ts",
12
+ "import": "./index.js",
13
+ "default": "./index.js"
14
+ },
15
+ "./analytics/constants": {
16
+ "types": "./analytics/constants.d.ts",
17
+ "import": "./analytics/constants.js",
18
+ "default": "./analytics/constants.js"
19
+ },
20
+ "./analytics/constants.js": {
21
+ "types": "./analytics/constants.d.ts",
22
+ "import": "./analytics/constants.js",
23
+ "default": "./analytics/constants.js"
24
+ },
25
+ "./analytics/event": {
26
+ "types": "./analytics/event.d.ts",
27
+ "import": "./analytics/event.js",
28
+ "default": "./analytics/event.js"
29
+ },
30
+ "./analytics/event.js": {
31
+ "types": "./analytics/event.d.ts",
32
+ "import": "./analytics/event.js",
33
+ "default": "./analytics/event.js"
34
+ },
35
+ "./analytics/index": {
36
+ "types": "./analytics/index.d.ts",
37
+ "import": "./analytics/index.js",
38
+ "default": "./analytics/index.js"
39
+ },
40
+ "./analytics/index.js": {
41
+ "types": "./analytics/index.d.ts",
42
+ "import": "./analytics/index.js",
43
+ "default": "./analytics/index.js"
44
+ },
45
+ "./analytics/target": {
46
+ "types": "./analytics/target.d.ts",
47
+ "import": "./analytics/target.js",
48
+ "default": "./analytics/target.js"
49
+ },
50
+ "./analytics/target.js": {
51
+ "types": "./analytics/target.d.ts",
52
+ "import": "./analytics/target.js",
53
+ "default": "./analytics/target.js"
54
+ },
55
+ "./analytics/validation": {
56
+ "types": "./analytics/validation.d.ts",
57
+ "import": "./analytics/validation.js",
58
+ "default": "./analytics/validation.js"
59
+ },
60
+ "./analytics/validation.js": {
61
+ "types": "./analytics/validation.d.ts",
62
+ "import": "./analytics/validation.js",
63
+ "default": "./analytics/validation.js"
64
+ },
65
+ "./custom/constants": {
66
+ "types": "./custom/constants.d.ts",
67
+ "import": "./custom/constants.js",
68
+ "default": "./custom/constants.js"
69
+ },
70
+ "./custom/constants.js": {
71
+ "types": "./custom/constants.d.ts",
72
+ "import": "./custom/constants.js",
73
+ "default": "./custom/constants.js"
74
+ },
75
+ "./custom/event": {
76
+ "types": "./custom/event.d.ts",
77
+ "import": "./custom/event.js",
78
+ "default": "./custom/event.js"
79
+ },
80
+ "./custom/event.js": {
81
+ "types": "./custom/event.d.ts",
82
+ "import": "./custom/event.js",
83
+ "default": "./custom/event.js"
84
+ },
85
+ "./custom/index": {
86
+ "types": "./custom/index.d.ts",
87
+ "import": "./custom/index.js",
88
+ "default": "./custom/index.js"
89
+ },
90
+ "./custom/index.js": {
91
+ "types": "./custom/index.d.ts",
92
+ "import": "./custom/index.js",
93
+ "default": "./custom/index.js"
94
+ },
95
+ "./custom/target": {
96
+ "types": "./custom/target.d.ts",
97
+ "import": "./custom/target.js",
98
+ "default": "./custom/target.js"
99
+ },
100
+ "./custom/target.js": {
101
+ "types": "./custom/target.d.ts",
102
+ "import": "./custom/target.js",
103
+ "default": "./custom/target.js"
104
+ },
105
+ "./custom/validation": {
106
+ "types": "./custom/validation.d.ts",
107
+ "import": "./custom/validation.js",
108
+ "default": "./custom/validation.js"
109
+ },
110
+ "./custom/validation.js": {
111
+ "types": "./custom/validation.d.ts",
112
+ "import": "./custom/validation.js",
113
+ "default": "./custom/validation.js"
114
+ },
115
+ "./index": {
116
+ "types": "./index.d.ts",
117
+ "import": "./index.js",
118
+ "default": "./index.js"
119
+ },
120
+ "./index.js": {
121
+ "types": "./index.d.ts",
122
+ "import": "./index.js",
123
+ "default": "./index.js"
124
+ }
125
+ }
126
+ }