{"_id":"@avv/ucd-ibus","_rev":"3-f318a693a3070e5ca2ba82b07ffc7968","name":"@avv/ucd-ibus","dist-tags":{"latest":"1.1.0"},"versions":{"1.0.0":{"name":"@avv/ucd-ibus","version":"1.0.0","keywords":["ucd-ibus"],"author":{"name":"avv"},"license":"ISC","_id":"@avv/ucd-ibus@1.0.0","maintainers":[{"name":"avv","email":"varbachakov@yandex.ru"}],"homepage":"","dist":{"shasum":"a10f57f266ce9b9315e4725e7a8e20af7efe5196","tarball":"https://registry.npmjs.org/@avv/ucd-ibus/-/ucd-ibus-1.0.0.tgz","fileCount":126,"integrity":"sha512-3Y5UX3KrH5Xh6EVKSgVgAM9l2Y5EyLbDPrMMRwPMDhXs4WECXgF5ZIOXOuqC8tQkjkfYYqZVIDhyb8AbYsHV6Q==","signatures":[{"sig":"MEYCIQCl7wNu+ralZH0ykwRvshCALThWZhEay9x1tMp9ssux/gIhANy7WfmCaZBrcVQ66j7QeH48H3vs0hVk2Rr8WAW4L5ku","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":135490},"main":"lib/index.js","types":"lib/types/index.d.ts","gitHead":"d5043d41022f86a41d47ca5c6cbd655b52c96f6f","scripts":{"test":"jest","build":"tsc --project tsconfig.json","test:coverage":"jest --coverage=true --verbose"},"_npmUser":{"name":"avv","email":"varbachakov@yandex.ru"},"repository":{"url":"","type":"git"},"_npmVersion":"9.5.1","description":"Post messaging processor.","directories":{},"_nodeVersion":"18.16.0","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/ucd-ibus_1.0.0_1744963033892_0.5861750425002095","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@avv/ucd-ibus","version":"1.0.1","keywords":["ucd-ibus"],"author":{"name":"avv"},"license":"ISC","_id":"@avv/ucd-ibus@1.0.1","maintainers":[{"name":"avv","email":"varbachakov@yandex.ru"}],"homepage":"","dist":{"shasum":"3e79174cc9ceaed3f21448dd8966eebfe5e612e0","tarball":"https://registry.npmjs.org/@avv/ucd-ibus/-/ucd-ibus-1.0.1.tgz","fileCount":126,"integrity":"sha512-qsFTmfXIoqI/yvL+9TqgyQZvNOtx7b4vMyaNv6omNdbjZFBM3eeSonNoNpSKkwBQ+q68T/6cmpZdT1ys2TmKdA==","signatures":[{"sig":"MEUCIDyhNfY3Bmr005G7tR7xCc/Kuy8oE+UMkqlQmAzMsUikAiEAzyyWJzENLxpLcppXSgy3JP6poShSrPY/iFQOTMX140U=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":134187},"main":"lib/index.js","types":"lib/types/index.d.ts","gitHead":"d5043d41022f86a41d47ca5c6cbd655b52c96f6f","scripts":{"test":"jest","build":"tsc --project tsconfig.json","test:coverage":"jest --coverage=true --verbose"},"_npmUser":{"name":"avv","email":"varbachakov@yandex.ru"},"repository":{"url":"","type":"git"},"_npmVersion":"9.5.1","description":"Post messaging processor.","directories":{},"_nodeVersion":"18.16.0","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/ucd-ibus_1.0.1_1744964173528_0.2847080752910758","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@avv/ucd-ibus","version":"1.1.0","description":"Post messaging processor.","main":"lib/index.js","types":"lib/types/index.d.ts","scripts":{"build":"tsc --project tsconfig.json","test":"jest","test:coverage":"jest --coverage=true --verbose"},"homepage":"","repository":{"type":"git","url":""},"keywords":["ucd-ibus"],"author":{"name":"avv"},"license":"ISC","gitHead":"d5043d41022f86a41d47ca5c6cbd655b52c96f6f","_id":"@avv/ucd-ibus@1.1.0","_nodeVersion":"18.16.0","_npmVersion":"9.5.1","dist":{"integrity":"sha512-hIElkZnZ7lmPL3soxphcx/iYeRYH3Hc8N9BT1gafqJUipG6fp0m/ksrHMCvOtp2iFk0D6YxnbYPasRSHLVDpFQ==","shasum":"31211ef8900fca107f64af09051a07653bfc5d0e","tarball":"https://registry.npmjs.org/@avv/ucd-ibus/-/ucd-ibus-1.1.0.tgz","fileCount":126,"unpackedSize":133976,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIEzsjIzJzZSD+T1wvLg30j7nu8rHVeJDoskkk98ZSUYlAiAZwXe1TuMIJaRwJFu32kf1sdbPOyURsm98IPN5NW7bfQ=="}]},"_npmUser":{"name":"avv","email":"varbachakov@yandex.ru"},"directories":{},"maintainers":[{"name":"avv","email":"varbachakov@yandex.ru"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/ucd-ibus_1.1.0_1744965228210_0.18144085180512692"},"_hasShrinkwrap":false}},"time":{"created":"2025-04-18T07:57:13.786Z","modified":"2025-04-18T08:33:48.566Z","1.0.0":"2025-04-18T07:57:14.077Z","1.0.1":"2025-04-18T08:16:13.699Z","1.1.0":"2025-04-18T08:33:48.390Z"},"author":{"name":"avv"},"license":"ISC","keywords":["ucd-ibus"],"repository":{"type":"git","url":""},"description":"Post messaging processor.","maintainers":[{"name":"avv","email":"varbachakov@yandex.ru"}],"readme":"# @avv/ucd-ibus\r\n\r\n## Установка\r\n\r\n```sh\r\nnpm i @avv/ucd-ibus\r\n```\r\n\r\n# Описание postMessage обработчика (v1.0.0)\r\n\r\nМодуль служит задаче обеспечения коммуникации через postMessage web API. Модуль реализует некоторый протокол обмена сообщениями, поэтому предполагается, что он будет установлен на обеих сторонах коммуникации. Однако, это не обязательно, если следовать описанному ниже протоколу.\r\n\r\nПомимо этого, модуль реализует некоторую политику безопасности, согласно которой можно настраивать доступ другой стороны к функционалу текущей.\r\n\r\n## Краткое описание работы с модулем\r\n\r\nДля начала работы, необходимо инциализировать `message` обработку. Для этого необходимо импортировать `initialize` функцию из пакета и вызвать её с коллекцией функций и опциональным объектом дополнительных параметров:\r\n\r\n```ts\r\nimport { initialize } from 'ucd_post-messaging'\r\n\r\nconst postMessageActions = {\r\n    someMethod: () => {},\r\n    groupMethods: {\r\n        someMethod2: (payload, sender, additionalParams) => {},\r\n        someMethod3: async (payload, sender, additionalParams) => {}\r\n    }\r\n}\r\nconst additionalParams = {\r\n    // некоторые дополнительные данные\r\n}\r\n\r\nconst {\r\n    open,\r\n    close,\r\n    allow,\r\n    forbid,\r\n    setPermissions,\r\n    getSender\r\n} = initialize(postMessageActions, additionalParams)\r\n```\r\n\r\nЭто необходимо сделать при запуске всего приложения в самом начале. Возможно, до монитирования в DOM дерево.\r\n\r\nКоллекция функций `postMessageActions` может быть древовидной, таким образом функции можно группировать по смыслу. Функция принимает:\r\n\r\n* `payload` - данные из объекта сообщения;\r\n* `sender` - имя отправителя;\r\n* `additionalParams` - объект дополнительных параметров, переданных при инициализациии.\r\n\r\nФункция может быть синхронной и асинхронной. Её результат ложится в `payload` объект ответного сообщения.\r\n\r\nНа этапе инициализации приложения, в корневом компоненте, необходимо вызвать метод `open`, а при размонтировании - метод `close`:\r\n\r\n```ts\r\nconst RootAppComponent = () => {\r\n    // некоторая логика...\r\n    useEffect(() => {\r\n        // ...\r\n        open();\r\n        return () => {\r\n            // ...\r\n            close();\r\n            // ...\r\n        };\r\n    }, []);\r\n    // дальнейшая логика\r\n}\r\n```\r\n\r\nМетоды `open` и `close` создают и навешивают/уничтожают соответственно callback обработчик `message` события на объекте `Window`.\r\n\r\nДля того, чтобы обмен сообщениями стал возможен, необходимо на обеих сторонах вызвать `allow` метод с параметрами соединения. Для закрытия соединения, можно вызвать `forbid` метод с именем соединения.\r\n\r\n```ts\r\nallow('Pparent', 'http://dashboard.host.com/', { 'someMethod': false, 'anotherMethod': true }, null, window.parent);\r\n// ...\r\nforbid('Parent');\r\n```\r\n\r\nПример компонента, создающего iframe:\r\n\r\n```ts\r\nconst Iframe = ({\r\n    name,\r\n    origin,\r\n    url,\r\n    permissions = true,\r\n    height = 100,\r\n    onSourceChange = null,\r\n    source = null\r\n}) => {\r\n    useEffect(() => {\r\n        allow(name, origin, permissions, onSourceChange, source);\r\n        return () => {\r\n            forbid(name);\r\n        }\r\n    }, []);\r\n\r\n    // render <iframe .../> элемента...\r\n};\r\n```\r\n\r\nИнтерфейс метода `allow`:\r\n* `name` - имя соединения;\r\n* `origin` - `protocol://host:port` соединения (желательно знать заранее);\r\n* `permissions` - настройки доступа к методам текущего окна (подробно рассмотрено ниже);\r\n* `onSourceChange` - callback, с помощью которого можно подписаться на событие смены ссылки на объект `Window` соседнего фрейма.\r\n* `source` - ссылка на объект `Window` соседнего фрейма (_может быть равным null, в таком случае будет проинициализирован при обработке `@INIT` сообщении_).\r\n\r\nПо объекту `permissions` стоит заметить, что если нет необходимости регулировать доступ к функциям, и нужно разрешить полный доступ - то можно просто передать `true` значение вместо объекта. Принцип объекта `permissions` описан ниже.\r\n\r\nЗдесь есть рекомендация: инициировать коммуникацию удобнее всего из дочернего фрейма, так как в нём точно можно поймать момент, когда он готов к коммуникации. Поэтому обычно именно дочерний фрейм инициирует коммуникацию и первым запрашивает нужные данные. Однако, если нужно, чтобы первым данные отправил родительский фрейм - это можно осуществить через `onSourceChange` callback. Он будет вызван, как только прилетит `@INIT` сообщение и будет получена ссылка на объект `Window` дочернего фрейма. В этот момент можно начинать отправлять сообщения в дочерний фрейм.\r\n\r\nЧтобы отправить сообщение, нужно получить callback `sender` путём вызова `getSender` метода:\r\n\r\n```ts\r\nconst sender = getSender('Parent');\r\n// ...\r\nconst answer = await sender(new ActionMessage('someMethod', { someParam: 'someValue' }))\r\n```\r\n\r\nCallback `sender` возвращает `Promise` объект. Он резолвится с ответом, и реджектится с ошибкой. Конструктор объекта сообщения первым параметром принимает имя метода, вторым - объект данных в произвольном формате. Второй параметр опциональный.\r\n\r\nЕсли нам не нужно получать ответа на наш запрос, можно в `sender` вторым параметром передать `false`:\r\n\r\n```ts\r\nsender(new ActionMessage('groupMethods/someMethod2'), false); // ответ не ожидается к обработке.\r\n```\r\n\r\nЗдесь важно заметить, что для обращения к методу, содержащемуся в глубине коллекции функций принимающей системы, разделителем служит косая черта.\r\n\r\n## Протокол и общая логика работы\r\n\r\n### Структура сообщения\r\n\r\nОбщем случае, структура сообщений реализует интерфейс:\r\n\r\n```ts\r\ninterface IMessage<T> {\r\n    messageId: number;\r\n    provider: string;\r\n    receiver: string;\r\n    sender?: string;\r\n    message: IMessageBody<T>;\r\n}\r\n```\r\n\r\nПоля имеют следующие значения:\r\n\r\n* `messageId` - идентификатор сообщения, обязательное поле; идентификатор сообщение генерируется автоматически внутри модуля при отправке сообщения;\r\n* `provider` - строковый идентификатор модуля, обязательное поле; поле нужно для того, чтобы была возможность отфильтровывать сообщения от других подсистем, отправляющих и получающих сообщения через postMessage API;\r\n* `receiver` - имя получателя, имя получатея назначается отправителем; поле обрабатывается автоматически;\r\n* `sender` - имя отправителя, поля может не быть в init сообщении, имя назначается получателем; поле обрабатывается автоматически;\r\n* `message` - тело сообщения, его интерфейс разбирается ниже.\r\n\r\n```ts\r\ninterface IMessageBody<T> {\r\n    type: MESSAGE_TYPE,\r\n    action?: string,\r\n    status?: RESPONSE_STATUS,\r\n    payload?: IMessagePayload<T>\r\n}\r\n\r\ntype IMessagePayload<T> = T | IErrorPayload | undefined\r\n\r\ninterface IErrorPayload {\r\n    code: ERROR_CODES,\r\n    error: string\r\n}\r\n\r\nenum MESSAGE_TYPE {\r\n    INIT = '@INIT',\r\n    REQUEST = '@REQUEST',\r\n    RESPONSE = '@RESPONSE',\r\n    END = '@END'\r\n}\r\n\r\nenum RESPONSE_STATUS {\r\n    SUCCESS = 'SUCCESS',\r\n    ERROR = 'ERROR'\r\n}\r\n```\r\n\r\nПоля тела сообщения имеют следующие значения:\r\n\r\n* `type` - тип сообщения, обязательное поле, поле может содержать одно из четырёх значений из `MESSAGE_TYPE` списка:\r\n    * `@INIT` - начальное инициирующее сообщение, обрабатывается полностью автоматически модулем;\r\n    * `@REQUEST` - запрос, данных или каких-либо действий;\r\n    * `@RESPONSE` - ответ, возвращает результат работы метода, к которому обращались;\r\n    * `@END` - завершающее сообщение, закрывает соединение;\r\n* `action` - поле обязательно для сообщения-запроса (`type = @REQUEST`), содержит имя метода, который нужно вызвать и вернуть его результат;\r\n* `status` - поле обязательно для сообщения-ответа (`type = @RESPONSE`), может содержать одно из двух значений из `RESPONSE_TYPE`:\r\n    * `SUCCESS` - запрос успешно обработан, и возможно содержит ответ в `payload`;\r\n    * `ERROR` - обработка запроса завершилась с ошибкой, в `payload` содержится объект ошибки;\r\n* `payload` - полезная нагрузка сообщения; может передаваться в запросе и в ответном сообщении; содержит данные при успешной обработке запроса и при возникновении ошибок (интерфейс `IErrorPayload` выше); може быть пустым; в запросе именно объект `payload` передаётся на вход вызываемого метода в системе-получателе; имеет произвольную структуру; логика обработки содержимого этого поля определяется разработчиком.\r\n\r\n### Установление соединения\r\n\r\nДля того, чтобы два экземпляра модуля могли установить друг с другом связь, необходимо эту коммуникацию разрешить. Разрешение на коммуникацию с выдаётся путём вызова `allow` метода с передачей:\r\n\r\n* имени \"собеседника\",\r\n* `origin` \"собеседника\",\r\n* настроек прав доступа \"собеседника\",\r\n* обработчика смены ссылки на объект `Window` \"собеседника\" (если необходимо),\r\n* ссылки на объект `Window` \"собеседника\" (если есть).\r\n\r\n```ts\r\n// в дочернем фрейме разрешена коммуникация с родительским\r\nallow('Parent', '*', true, null, window.parent)\r\n```\r\n\r\n```ts\r\n// в родительском фрейме разрешена коммуникация с дочерним\r\nallow('Porter', 'https://porter.com/', true, null, null)\r\n```\r\n\r\nИнтерфейс метода `allow` будет рассмотрен подробнее ниже.\r\n\r\nИмя \"собеседника\" необходимо передать в обязательном порядке, и оно должно быть уникальным. Крайне желательно, на момент разрешения, точно знать `origin` \"собеседника\" - это гарантирует безопасность коммуникации. Однако, если `origin` неизвестен, то можно передать `*` - при обработке ответа на `@INIT` сообщение будет проставлен реальный `origin` отправителя ответа.\r\n\r\nДля отправки `@INIT` сообщения, один из участников коммуникации должен иметь ссылку на объект `Window` получателя. Чаще всего, это дочерний iframe. Соответственно, именно дочернему iframe удобнее всего первым отправить `@INIT` сообщение. Родительский фрейм может изначально не иметь ссылки на объект `Window` дочернего, но он получит эту ссылку при обработке `@INIT` запроса и сохранит у себя.\r\n\r\nНачальное сообщение:\r\n\r\n```json\r\n{\r\n    \"messageId\": 1,\r\n    \"sender\": null,\r\n    \"receiver\": \"Parent\",\r\n    \"provider\": \"POST-MESSAGING-PACKAGE-V1\",\r\n    \"message\": {\r\n        \"type\": \"@INIT\"\r\n    }\r\n}\r\n```\r\n\r\nОтвет:\r\n\r\n```json\r\n{\r\n    \"messageId\": 1,\r\n    \"sender\": \"Parent\",\r\n    \"receiver\": \"Porter\",\r\n    \"provider\": \"POST-MESSAGING-PACKAGE-V1\",\r\n    \"message\": {\r\n        \"type\": \"@RESPONSE\",\r\n        \"payload\": {\r\n          \"inited\": true\r\n        }\r\n    }\r\n}\r\n```\r\n\r\nС момента получения ответа на `@INIT` сообщение, возможна полноценная коммуникация в обе стороны.\r\n\r\n### Обмен сообщениями\r\n\r\nЗапрос-ответ связываются друг с другом внутри модуля через идентификатор запроса: ответное сообщение имеет тот же идентификатор.\r\n\r\nСообщение-запрос имеет тип `type = @REQUEST`, содержит имя вызываемого метода в поле `action = method/name`, опционально может содержать `payload` с произвольным содержимым. Содержимо поля `payload` будет передано в функцию `method/name`  в принимающей системе.\r\n\r\nСообщение-ответ имеет тип `type = @RESPONSE`, содержит статус ответа в поле `status`, и в поле `payload` (так же опциональном) произвольные данные. С этими данными резолвится промис, возвращаемый при отправке сообщения. Состояние промиса можно обработать произвольным образом. Содержимое `payload` рвно тому, что вернется в результате вызова `method/name` метода.\r\n\r\nПример сообщения-запроса:\r\n\r\n```json\r\n{\r\n    \"messageId\": 2,\r\n    \"sender\": \"Porter\",\r\n    \"receiver\": \"Parent\",\r\n    \"provider\": \"POST-MESSAGING-PACKAGE-V1\",\r\n    \"message\": {\r\n        \"type\": \"@REQUEST\",\r\n        \"action\": \"dataRequest/getClient\"\r\n    }\r\n}\r\n```\r\n\r\nПример сообщения-ответа:\r\n\r\n```json\r\n{\r\n    \"messageId\": 2,\r\n    \"sender\": \"Parent\",\r\n    \"receiver\": \"Porter\",\r\n    \"provider\": \"POST-MESSAGING-PACKAGE-V1\",\r\n    \"message\": {\r\n        \"type\": \"@RESPONSE\",\r\n        \"status\": \"SUCCESS\",\r\n        \"payload\": {\r\n            \"base\": {\r\n                \"guid\": \"sdglk234234j2l34jk234l2j\"\r\n            }\r\n        }\r\n    }\r\n}\r\n```\r\n\r\nДля отправки сообщения, необходимо вызвать `getSender` с именем получателя (зарегистрированном при вызове `allow`) получить ссылку на callback, и вызвать этот его с экземпляром объекта сообщения:\r\n\r\n```ts\r\nconst sender = getSender('Parent');\r\nsender(new ActionMessage('dataRequest/getSomeData', { param: 'value' }))\r\n    .then((answer) => {\r\n        // логика обработки ответа\r\n    });\r\n\r\n// или \r\n\r\nconst answer = await sender(new ActionMessage('dataRequest/getSomeData', { param: 'value' }));\r\n```\r\n\r\nЭкземпляр объекта сообщения первым параметром получает имя вызываемого метода, вторым - данные для этого метода в произвольном формате. Второй параметр не обязательный.\r\n\r\nCallback `sender` возвращает `Promise` объект.\r\n\r\n### Закрытие коммуникации\r\n\r\nКоммуникация между двумя окнами закрывается при отправке `@END` сообщения. При этом на обеих сторонах уничтожается информация друг о друге. Ответ на это сообщение не отправляется.\r\n\r\nПример завершающего сообщения:\r\n\r\n```json\r\n{\r\n    \"messageId\": 1224,\r\n    \"sender\": \"Porter\",\r\n    \"receiver\": \"Parent\",\r\n    \"provider\": \"POST-MESSAGING-PACKAGE-V1\",\r\n    \"message\": {\r\n        \"type\": \"@END\"\r\n    }\r\n}\r\n```\r\n\r\nОтправить завершающее сообщение можно через `sender` callback. Вызов `forbid` метода не отправляет никаких сообщений, но закрывает коммуникацию с другим фреймом молча.\r\n\r\n```ts\r\nconst sender = getSender('Parent');\r\nsender(new EndMessage());\r\n\r\n// или \r\n\r\nforbid('Parent');\r\n```\r\n\r\nCallback `sender`, как обычно вернет `Promise` объект. Он зарезолвится как только сообщение физически будет отправлено, а данные соединения будут уничтожены. Происходит это синхронно, поэтому обычно нет надобности подписываться на событие закрытия соединения. Метод `forbid` ничего не возвращает.\r\n\r\n## API модуля\r\n\r\n### Функция`initialize`\r\n\r\nМодуль экспортирует одну функцию `initialize`. Она при вызове принимает объект коллекции функций и объект произвольных дополнительных параметров (опциональный), который впоследствии будет передан в вызываемые при обработке запросов методы из коллекции функций.\r\n\r\nВызов функции инициализирует модуль, но ещё не создаёт обработчик `message` события.\r\n\r\n```ts\r\nconst {\r\n    open,\r\n    close,\r\n    allow,\r\n    forbid,\r\n    setPermissions,\r\n    getSender\r\n} = initialize(postMessageActions, additionalParams)\r\n```\r\n\r\nВозвращает объект со вспомогательными методами.\r\n\r\n#### Метод `open`\r\n\r\nСоздает обработчик события `message` и инициализирует все внутренние механизмы модуля. Повторный вызов метода от одного экземпляра `initialize` проблем не создаст: повторной регистрации обработчика `message` и инициализации модуля - не случится. Никаких параметров не принимает. Ничего не возвращает.\r\n\r\n#### Метод `close`\r\n\r\nУничтожает обработчик `message` события для данного экземпляра модуля и останавливает все внутренние процессы в модуле, уничтожает все данные. Никаких параметров не принимает и ничего не возвращает.\r\n\r\n#### Метод `allow`\r\n\r\nМетод разрешающий коммуникацию с фреймом с указанными параметрами. Принимает параметры (уже было описано выше):\r\n\r\n* `name` - имя соединения;\r\n* `origin` - `protocol://host:port` соединения;\r\n* `permissions` - настройки доступа к методам текущего окна;\r\n* `onSourceChange` - callback, с помощью которого можно подписаться на событие смены ссылки на объект `Window` соседнего фрейма.\r\n* `source` - ссылка на объект `Window` соседнего фрейма.\r\n\r\nМетод ничего не возвращает.\r\n\r\nВажно отметить, что желательно чтобы `origin` и `source` были известны на момент вызова. Однако не всегда это доступно или удобно. Поэтому здесь допустимы следующие ситуации:\r\n\r\n* `origin` неизвестен, `source` известен\r\n\r\n    В таком случае в качестве `origin` можно передать `*` - то есть он может быть любым. Фрейм, который находится в таком положении должен первым отправить сообщение `@INIT`. В ответном сообщении будет `origin` отправителя, и именно он будет в итоге записан вместо `*`.\r\n\r\n* `origin` известен, `source` неизвестен\r\n\r\n    Фрейм, находящийся в таком состоянии вынужден ждать `@INIT` сообщения от фрейма с указанным `origin`. Именно указанный `origin` становится идентификатором, позволяющий точно понять к какому соединению относится данный запрос. Внутри модуля из запроса извлекается `source` и записывается в параметры данного соединения.\r\n\r\n* оба параметры известны\r\n\r\n    Формально, они могут друг-другу одновременно отправить `@INIT` сообщение. В таком случае финальным состоянием соединения будет состояние по итогам обработки второго `@INIT` сообщения. Но лучше принять решение, о том какая сторона инициирует коммуникацию первой.\r\n\r\nСитуация, когда оба параметра неизвестны - недопустима. В таком случае, объективно, нет возможности однозначно и точно понять источник запроса `@INIT` и корректно проинициализировать параметры соединения. Не говоря уже о том, что неизвестный `origin` создаёт потенциальные проблемы безопасности.\r\n\r\n#### Метод `forbid`\r\n\r\nЗакрывает соединение с указанным именем, принимает только строку. Имя должно совпадать с именем переданным в `allow` первым параметром. Ничего не возвращает. Соедиение будет закрыто \"молча\", сообщение `@END` отправлено не будет.\r\n\r\n#### Метод `setPermissions`\r\n\r\nМетод принимает имя соединения (или `origin`) и объект (или `boolean` значение) `permissions`. Принцип работы настроек доступа описан ниже. Метод ничего не возвращает.\r\n\r\n#### Метод `getSender`\r\n\r\nМетод принимает имя или `origin` соединения и возвращает функцию отправки сообщения.\r\n\r\nВозвращённый callback принимает экземляр объекта сообщения, созданного конструкторами `ActionMessage`, `InitMessage`, `EndMessage`. Вторым параметром принимает `boolean` значение, по-умолчанию равно `true`: ожидается ли ответ на запрос. Если ожидается ответ, то будет возвращён `Promise` объект, который резолвится с ответом на запрос. Если ответ не ожидается, что callback ничего не вернёт.\r\n\r\n### Конструкторы сообщений\r\n\r\nМодуль экспортирует три конструктора сообщений и один конструктор параметров ошибки в `payload` ответного сообщения:\r\n\r\n* `InitMessage`\r\n* `EndMessage`\r\n* `ActionMessage`\r\n* `ErrorPayload`\r\n\r\nДля передачи `@INIT` или `@END` сообщений, достаточно в `sender` callback передать соответствующие экземпляры:\r\n\r\n```ts\r\nsender(new InitMessage());\r\nsender(new EndMessage());\r\n```\r\n\r\nКонструктор `ActionMessage` принимает два параметра: имя вызываемого метода и `payload` сообщения. Второй параметр опциональный. Объект `payload` может быть произвольным (может быть и скалярным значением).\r\n\r\n## Политика безопасности\r\n\r\nПараметр `permissions` в методе `allow` может принимать либо булево значение, либо передаваться в виде объекта (плоский HashMap). Во втором случае ключами являются имена методов, значениями статус доступа `true|false`.\r\n\r\nЕсли передаётся `permissions = true`, это значает что данному источнику сообщений предоставляется полный доступ ко всем имеющимся методам. Передача `permissions = false` рвносильно полному запрету всякого доступа. Может использоваться для временной полной блокировки.\r\n\r\nПередача объекта позволяет гибко настроить доступ к разным методам. Например, мы имеем коллкцию функций:\r\n\r\n```ts\r\nconst functions = {\r\n    client: {\r\n        getClient: async () => {},\r\n        setClientName: async (payload, sender, additionalParams) => {},\r\n        getMoney: async (payload, sender, additionalParams) => {}\r\n    },\r\n    settings: {\r\n        getSettings: () => {},\r\n        setParam: (payload, sender, additionalParams) => {}\r\n    }\r\n}\r\n```\r\n\r\nВ какой-то момент мы вынуждены работать с фреймом, которому доступ к данным клиента предоставлять нельзя, но можно выполнять разные операции с настройками системы. Мы можем передать в `permissions` следующие настройки:\r\n\r\n```ts\r\nconst permissions = {\r\n    'client': false,\r\n    'settings': true\r\n}\r\n```\r\n\r\nИли, фрейм может обращаться ко всем методам, кроме `getMoney`:\r\n\r\n```ts\r\nconst permissions = {\r\n    'client': true,\r\n    'client/getMoney': false,\r\n    'settings': true\r\n}\r\n```\r\n\r\nИли, фрейм может обращаться ко всем методам настроек системы, и может запрашивать данные клиента, но ему запрещено модифицировать данные клиента и выполнять манипуляции с деньгами:\r\n\r\n```ts\r\nconst permissions = {\r\n    'client/getClient': true,\r\n    'settings': true\r\n}\r\n```\r\n\r\nОбъект настроек доступа работает по следующим правилам:\r\n\r\n* если `permissions = true` разрешается доступ ко всему;\r\n* если `permissions = false` ко всему доступ запрещён;\r\n* если передан объект, следует принципу \"все запрещено, если не указано иное\":\r\n    * если в объекте не указано имя конкретного метода или группы методов - доступ к методу и группе методов запрещён (если объект пустой - доступ ко всему запрещён);\r\n    * если в объекте указано имя конкретного метода или группы методов, и в нём содержится `true`, то доступ к этому методу или группе методов разрешен, ко всему остальному доступ запрещён;\r\n    * если в объекте указано имя конкретного метода или группы методов, и в нём содержится `false`, то доступ к этому методу или группе методов запрещён, даже если есть имя надгруппы и там содержится значение `true`;\r\n\r\n\r\n\r\n## Дальнейшие доработки и развитие модуля\r\n\r\n* адаптировать модуль к postMessage взаимодействию с Worker объектом (один на один);\r\n* предоставить возможность разработчку самому создавать обработчик входящих сообщений (передавать генератор в стиле саги) - **может быть это не нужно**;\r\n* ввиду схожести механизма WebSocket коммуникации, возможно есть смысл его адаптировать и под такую возможность.\r\n","readmeFilename":"README.md"}