@encatch/event-publisher 0.0.2-beta.0

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/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # @encatch/event-publisher
2
+
3
+ Internal package for publishing and subscribing to form events across the Encatch ecosystem.
4
+
5
+ ## Installation
6
+
7
+ This is an internal package. Add it to your package dependencies:
8
+
9
+ ```json
10
+ {
11
+ "dependencies": {
12
+ "@encatch/event-publisher": "workspace:*"
13
+ }
14
+ }
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ ```typescript
20
+ import { formEventPublisher } from '@encatch/event-publisher';
21
+
22
+ // Subscribe to form submit events
23
+ const unsubscribe = formEventPublisher.subscribe('form:submit', (payload) => {
24
+ console.log('Form submitted:', payload);
25
+ });
26
+
27
+ // Subscribe with form ID filter
28
+ formEventPublisher.subscribe(
29
+ 'form:submit',
30
+ (payload) => {
31
+ console.log('Specific form submitted:', payload);
32
+ },
33
+ { formId: 'specific-form-id' }
34
+ );
35
+
36
+ // Publish an event
37
+ formEventPublisher.publish('form:submit', {
38
+ feedbackConfigurationId: 'form-id',
39
+ response: { /* form data */ },
40
+ timestamp: Date.now()
41
+ });
42
+
43
+ // Unsubscribe when done
44
+ unsubscribe();
45
+ ```
46
+
47
+ ## API
48
+
49
+ ### Types
50
+
51
+ - `FormEventType`: Event types ('form:view', 'form:submit', 'form:close', etc.)
52
+ - `FormEventPayload`: Payload types for each event
53
+ - `FormIdFilter`: Filter for specific form IDs
54
+ - `SubscriptionOptions`: Options for subscriptions
55
+
56
+ ### Methods
57
+
58
+ - `publish<T>(eventType: T, payload: FormEventPayload[T], options?)`: Publish an event
59
+ - `subscribe<T>(eventType: T, handler: Function, options?)`: Subscribe to events
60
+ - `subscribeAll(handler: Function, options?)`: Subscribe to all event types
61
+ - `getSubscriptionCount(eventType?)`: Get active subscription count
62
+ - `clearSubscriptions(eventType?)`: Clear subscriptions
@@ -0,0 +1,111 @@
1
+ class d extends EventTarget {
2
+ subscriptions = /* @__PURE__ */ new Map();
3
+ targetOrigin = "*";
4
+ /**
5
+ * Check if a form ID matches the filter
6
+ */
7
+ matchesFilter(s, i) {
8
+ return !i || i === "*" || i === null || i === void 0 ? !0 : typeof i == "string" ? s === i : Array.isArray(i) ? i.includes(s || "") : typeof i == "function" ? i(s || "") : !1;
9
+ }
10
+ /**
11
+ * Get form ID from payload
12
+ */
13
+ getFormIdFromPayload(s) {
14
+ return s?.feedbackConfigurationId;
15
+ }
16
+ /**
17
+ * Publish an event to all matching subscribers
18
+ */
19
+ publish(s, i, e) {
20
+ typeof window < "u" && window.dispatchEvent(
21
+ new CustomEvent(s, {
22
+ detail: i,
23
+ bubbles: !0
24
+ })
25
+ ), e?.usePostMessage !== !1 && this.sendPostMessage(s, i);
26
+ }
27
+ /**
28
+ * Subscribe to form events with optional form ID filtering
29
+ */
30
+ subscribe(s, i, e) {
31
+ const n = Symbol("subscription"), t = {
32
+ eventType: s,
33
+ handler: i,
34
+ filter: e?.formId,
35
+ id: n,
36
+ once: e?.once
37
+ };
38
+ this.subscriptions.has(s) || this.subscriptions.set(s, []), this.subscriptions.get(s).push(t);
39
+ const r = (o) => {
40
+ const c = o, u = this.getFormIdFromPayload(c.detail);
41
+ this.matchesFilter(u, e?.formId) && (i(c.detail), e?.once && typeof window < "u" && window.removeEventListener(s, r));
42
+ };
43
+ return typeof window < "u" && window.addEventListener(s, r), () => {
44
+ this.unsubscribe(n), typeof window < "u" && window.removeEventListener(s, r);
45
+ };
46
+ }
47
+ /**
48
+ * Unsubscribe by subscription ID
49
+ */
50
+ unsubscribe(s) {
51
+ for (const [i, e] of this.subscriptions.entries()) {
52
+ const n = e.findIndex((t) => t.id === s);
53
+ if (n !== -1) {
54
+ e.splice(n, 1), e.length === 0 && this.subscriptions.delete(i);
55
+ break;
56
+ }
57
+ }
58
+ }
59
+ /**
60
+ * Subscribe to all form events with optional filtering
61
+ */
62
+ subscribeAll(s, i) {
63
+ const e = [
64
+ "form:view",
65
+ "form:submit",
66
+ "form:close",
67
+ "form:section:change",
68
+ "form:question:answered",
69
+ "form:error"
70
+ ], n = [];
71
+ return e.forEach((t) => {
72
+ const r = this.subscribe(
73
+ t,
74
+ (o) => s(t, o),
75
+ i
76
+ );
77
+ n.push(r);
78
+ }), () => {
79
+ n.forEach((t) => t());
80
+ };
81
+ }
82
+ /**
83
+ * Get active subscriptions count (for debugging)
84
+ */
85
+ getSubscriptionCount(s) {
86
+ return s ? this.subscriptions.get(s)?.length || 0 : Array.from(this.subscriptions.values()).reduce((i, e) => i + e.length, 0);
87
+ }
88
+ /**
89
+ * Clear all subscriptions for an event type (or all events)
90
+ */
91
+ clearSubscriptions(s) {
92
+ s ? this.subscriptions.delete(s) : this.subscriptions.clear();
93
+ }
94
+ sendPostMessage(s, i) {
95
+ const e = {
96
+ type: `encatch:${s}`,
97
+ payload: i,
98
+ source: "encatch-form-engine"
99
+ };
100
+ window.parent && window.parent !== window && window.parent.postMessage(e, this.targetOrigin), document.querySelectorAll("iframe").forEach((t) => {
101
+ t.contentWindow && t.contentWindow.postMessage(e, this.targetOrigin);
102
+ }), typeof window.ReactNativeWebView < "u" && window.ReactNativeWebView.postMessage(JSON.stringify({
103
+ action: s.replace("form:", ""),
104
+ data: JSON.stringify(i)
105
+ }));
106
+ }
107
+ }
108
+ const f = new d();
109
+ export {
110
+ f as formEventPublisher
111
+ };
@@ -0,0 +1 @@
1
+ (function(o,r){typeof exports=="object"&&typeof module<"u"?r(exports):typeof define=="function"&&define.amd?define(["exports"],r):(o=typeof globalThis<"u"?globalThis:o||self,r(o.EventPublisher={}))})(this,(function(o){"use strict";class r extends EventTarget{subscriptions=new Map;targetOrigin="*";matchesFilter(e,s){return!s||s==="*"||s===null||s===void 0?!0:typeof s=="string"?e===s:Array.isArray(s)?s.includes(e||""):typeof s=="function"?s(e||""):!1}getFormIdFromPayload(e){return e?.feedbackConfigurationId}publish(e,s,i){typeof window<"u"&&window.dispatchEvent(new CustomEvent(e,{detail:s,bubbles:!0})),i?.usePostMessage!==!1&&this.sendPostMessage(e,s)}subscribe(e,s,i){const n=Symbol("subscription"),t={eventType:e,handler:s,filter:i?.formId,id:n,once:i?.once};this.subscriptions.has(e)||this.subscriptions.set(e,[]),this.subscriptions.get(e).push(t);const u=c=>{const d=c,a=this.getFormIdFromPayload(d.detail);this.matchesFilter(a,i?.formId)&&(s(d.detail),i?.once&&typeof window<"u"&&window.removeEventListener(e,u))};return typeof window<"u"&&window.addEventListener(e,u),()=>{this.unsubscribe(n),typeof window<"u"&&window.removeEventListener(e,u)}}unsubscribe(e){for(const[s,i]of this.subscriptions.entries()){const n=i.findIndex(t=>t.id===e);if(n!==-1){i.splice(n,1),i.length===0&&this.subscriptions.delete(s);break}}}subscribeAll(e,s){const i=["form:view","form:submit","form:close","form:section:change","form:question:answered","form:error"],n=[];return i.forEach(t=>{const u=this.subscribe(t,c=>e(t,c),s);n.push(u)}),()=>{n.forEach(t=>t())}}getSubscriptionCount(e){return e?this.subscriptions.get(e)?.length||0:Array.from(this.subscriptions.values()).reduce((s,i)=>s+i.length,0)}clearSubscriptions(e){e?this.subscriptions.delete(e):this.subscriptions.clear()}sendPostMessage(e,s){const i={type:`encatch:${e}`,payload:s,source:"encatch-form-engine"};window.parent&&window.parent!==window&&window.parent.postMessage(i,this.targetOrigin),document.querySelectorAll("iframe").forEach(t=>{t.contentWindow&&t.contentWindow.postMessage(i,this.targetOrigin)}),typeof window.ReactNativeWebView<"u"&&window.ReactNativeWebView.postMessage(JSON.stringify({action:e.replace("form:",""),data:JSON.stringify(s)}))}}const f=new r;o.formEventPublisher=f,Object.defineProperty(o,Symbol.toStringTag,{value:"Module"})}));
@@ -0,0 +1,90 @@
1
+ export declare type FormEventPayload = {
2
+ 'form:view': {
3
+ feedbackConfigurationId: string;
4
+ feedbackIdentifier?: string;
5
+ timestamp: number;
6
+ };
7
+ 'form:submit': {
8
+ feedbackConfigurationId: string;
9
+ feedbackIdentifier?: string;
10
+ response: any;
11
+ timestamp: number;
12
+ };
13
+ 'form:close': {
14
+ feedbackConfigurationId?: string;
15
+ feedbackIdentifier?: string;
16
+ timestamp: number;
17
+ };
18
+ 'form:section:change': {
19
+ feedbackConfigurationId: string;
20
+ sectionIndex: number;
21
+ sectionId: string;
22
+ timestamp: number;
23
+ };
24
+ 'form:question:answered': {
25
+ feedbackConfigurationId: string;
26
+ questionId: string;
27
+ questionType: string;
28
+ answer: any;
29
+ timestamp: number;
30
+ };
31
+ 'form:error': {
32
+ feedbackConfigurationId: string;
33
+ questionId?: string;
34
+ error: string;
35
+ timestamp: number;
36
+ };
37
+ };
38
+
39
+ declare class FormEventPublisher extends EventTarget {
40
+ private subscriptions;
41
+ private targetOrigin;
42
+ /**
43
+ * Check if a form ID matches the filter
44
+ */
45
+ private matchesFilter;
46
+ /**
47
+ * Get form ID from payload
48
+ */
49
+ private getFormIdFromPayload;
50
+ /**
51
+ * Publish an event to all matching subscribers
52
+ */
53
+ publish<T extends FormEventType>(eventType: T, payload: FormEventPayload[T], options?: {
54
+ usePostMessage?: boolean;
55
+ }): void;
56
+ /**
57
+ * Subscribe to form events with optional form ID filtering
58
+ */
59
+ subscribe<T extends FormEventType>(eventType: T, handler: (payload: FormEventPayload[T]) => void, options?: SubscriptionOptions): () => void;
60
+ /**
61
+ * Unsubscribe by subscription ID
62
+ */
63
+ private unsubscribe;
64
+ /**
65
+ * Subscribe to all form events with optional filtering
66
+ */
67
+ subscribeAll(handler: (eventType: FormEventType, payload: any) => void, options?: SubscriptionOptions): () => void;
68
+ /**
69
+ * Get active subscriptions count (for debugging)
70
+ */
71
+ getSubscriptionCount(eventType?: FormEventType): number;
72
+ /**
73
+ * Clear all subscriptions for an event type (or all events)
74
+ */
75
+ clearSubscriptions(eventType?: FormEventType): void;
76
+ private sendPostMessage;
77
+ }
78
+
79
+ export declare const formEventPublisher: FormEventPublisher;
80
+
81
+ export declare type FormEventType = 'form:view' | 'form:submit' | 'form:close' | 'form:section:change' | 'form:question:answered' | 'form:error';
82
+
83
+ export declare type FormIdFilter = string | string[] | ((formId: string) => boolean) | '*' | null | undefined;
84
+
85
+ export declare interface SubscriptionOptions {
86
+ formId?: FormIdFilter;
87
+ once?: boolean;
88
+ }
89
+
90
+ export { }
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@encatch/event-publisher",
3
+ "version": "0.0.2-beta.0",
4
+ "type": "module",
5
+ "main": "./dist/event-publisher.umd.cjs",
6
+ "module": "./dist/event-publisher.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/event-publisher.js",
12
+ "require": "./dist/event-publisher.umd.cjs"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "devDependencies": {
19
+ "@types/node": "^24.6.0",
20
+ "typescript": "~5.9.3",
21
+ "vite": "^7.1.7",
22
+ "vite-plugin-dts": "^4.3.0"
23
+ },
24
+ "scripts": {
25
+ "dev": "vite",
26
+ "build": "vite build && tsc --emitDeclarationOnly --declaration",
27
+ "clean": "rm -rf dist"
28
+ }
29
+ }