@isdk/ai-tool 0.7.0 → 0.8.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/dist/chunk-4FKBOPZI.mjs +1 -0
- package/dist/{chunk-VIG2GB47.mjs → chunk-TGTHY57V.mjs} +1 -1
- package/dist/funcs.d.mts +1 -2
- package/dist/funcs.d.ts +1 -2
- package/dist/funcs.js +1 -1
- package/dist/funcs.mjs +1 -1
- package/dist/{index-BCco-g_I.d.mts → index-BLW3R7VS.d.mts} +114 -69
- package/dist/{index-BCco-g_I.d.ts → index-BLW3R7VS.d.ts} +114 -69
- package/dist/index.d.mts +101 -38
- package/dist/index.d.ts +101 -38
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/test/util.js +1 -1
- package/dist/test/util.mjs +1 -1
- package/docs/api/{namespaces → @isdk/namespaces}/EventStates/README.md +2 -2
- package/docs/api/{namespaces → @isdk/namespaces}/EventStates/variables/ABORT.md +2 -2
- package/docs/api/{namespaces → @isdk/namespaces}/EventStates/variables/CONTINUE.md +2 -2
- package/docs/api/{namespaces → @isdk/namespaces}/EventStates/variables/DONE.md +2 -2
- package/docs/api/{namespaces → @isdk/namespaces}/EventStates/variables/STOPPED.md +2 -2
- package/docs/api/@isdk/namespaces/uuidv5/README.md +12 -0
- package/docs/api/{namespaces → @isdk/namespaces}/uuidv5/variables/DNS.md +2 -2
- package/docs/api/{namespaces → @isdk/namespaces}/uuidv5/variables/URL.md +2 -2
- package/docs/api/_media/pubsub.md +427 -105
- package/docs/api/_media/server_client_tools.md +5 -3
- package/docs/api/_media/toolFunc.md +3 -1
- package/docs/api/classes/AbortError.md +8 -8
- package/docs/api/classes/AlreadyExistsError.md +8 -8
- package/docs/api/classes/BaseError.md +14 -14
- package/docs/api/classes/BinarySemaphore.md +39 -39
- package/docs/api/classes/CancelableAbility.md +43 -39
- package/docs/api/classes/ClientToolTransport.md +22 -22
- package/docs/api/classes/ClientTools.md +182 -154
- package/docs/api/classes/CommonError.md +12 -12
- package/docs/api/classes/ConfigFile.md +6 -6
- package/docs/api/classes/EnvPromptTemplate.md +14 -14
- package/docs/api/classes/EventClient.md +258 -201
- package/docs/api/classes/EventEmitter.md +14 -14
- package/docs/api/classes/EventServer.md +220 -237
- package/docs/api/classes/EventToolFunc.md +144 -116
- package/docs/api/classes/FStringPromptTemplate.md +14 -14
- package/docs/api/classes/FewShotPromptTemplate.md +30 -26
- package/docs/api/classes/GolangPromptTemplate.md +13 -13
- package/docs/api/classes/HttpClientToolTransport.md +23 -23
- package/docs/api/classes/HttpServerToolTransport.md +40 -27
- package/docs/api/classes/IntSet.md +21 -21
- package/docs/api/classes/LRUCache.md +18 -18
- package/docs/api/classes/NotFoundError.md +8 -8
- package/docs/api/classes/NotImplementationError.md +8 -8
- package/docs/api/classes/PromptExampleSelector.md +17 -15
- package/docs/api/classes/PromptTemplate.md +15 -15
- package/docs/api/classes/ReadableStreamError.md +12 -12
- package/docs/api/classes/ResClientTools.md +658 -143
- package/docs/api/classes/ResServerTools.md +643 -133
- package/docs/api/classes/RpcMethodsClientTool.md +3303 -0
- package/docs/api/classes/RpcMethodsServerTool.md +3248 -0
- package/docs/api/classes/SSEChannel.md +142 -52
- package/docs/api/classes/Semaphore.md +40 -40
- package/docs/api/classes/ServerToolTransport.md +32 -27
- package/docs/api/classes/ServerTools.md +167 -139
- package/docs/api/classes/SignalGate.md +19 -17
- package/docs/api/classes/SseClientPubSubTransport.md +38 -10
- package/docs/api/classes/SseServerPubSubTransport.md +115 -42
- package/docs/api/classes/TaskAbortController.md +13 -13
- package/docs/api/classes/ToolFunc.md +157 -129
- package/docs/api/classes/ToolTransport.md +13 -13
- package/docs/api/classes/YamlTypeBaseObject.md +5 -5
- package/docs/api/enumerations/AsyncFeatureBits.md +4 -4
- package/docs/api/enumerations/AsyncFeatures.md +4 -4
- package/docs/api/enumerations/HashAlgorithm.md +12 -12
- package/docs/api/functions/AIArgProcessor.md +2 -2
- package/docs/api/functions/AIStream.md +8 -4
- package/docs/api/functions/ChoiceArgProcessor.md +2 -2
- package/docs/api/functions/DefaultDateFormat.md +2 -2
- package/docs/api/functions/ObjectArgsToArgsInfo.md +1 -1
- package/docs/api/functions/RateLimit.md +2 -2
- package/docs/api/functions/TemplateArgProcessor.md +2 -2
- package/docs/api/functions/addDate.md +7 -3
- package/docs/api/functions/assignDirs.md +1 -1
- package/docs/api/functions/beforeShutdown.md +1 -1
- package/docs/api/functions/calcPerplexity.md +2 -2
- package/docs/api/functions/calcPerplexitySimple.md +1 -1
- package/docs/api/functions/canonicalize.md +1 -1
- package/docs/api/functions/completeSentences.md +1 -1
- package/docs/api/functions/concatText.md +1 -1
- package/docs/api/functions/countLLMTokens.md +1 -1
- package/docs/api/functions/countRegexMatches.md +1 -1
- package/docs/api/functions/createAbilityInjector.md +16 -8
- package/docs/api/functions/createCallbacksTransformer.md +7 -3
- package/docs/api/functions/createEmptyReadableStream.md +1 -1
- package/docs/api/functions/createEndWithRepetitionDetector.md +2 -2
- package/docs/api/functions/createError.md +1 -1
- package/docs/api/functions/createEventStreamTransformer.md +8 -4
- package/docs/api/functions/createHfValueFunc.md +1 -1
- package/docs/api/functions/createLRUCache.md +2 -2
- package/docs/api/functions/createYamlObjectTag.md +1 -1
- package/docs/api/functions/dateToText.md +1 -1
- package/docs/api/functions/decodeCharset.md +2 -2
- package/docs/api/functions/defaultsWithConcat.md +1 -1
- package/docs/api/functions/detectCharset.md +2 -2
- package/docs/api/functions/encodeLLMTokens.md +1 -1
- package/docs/api/functions/ensureQuoted.md +1 -1
- package/docs/api/functions/eventable.md +6 -8
- package/docs/api/functions/expandConfig.md +2 -2
- package/docs/api/functions/expandObjEnv.md +1 -1
- package/docs/api/functions/expandPath.md +2 -2
- package/docs/api/functions/expandPathInObject.md +2 -2
- package/docs/api/functions/expandPaths.md +2 -2
- package/docs/api/functions/fileIsExists.md +1 -1
- package/docs/api/functions/filterValidFnScope.md +2 -2
- package/docs/api/functions/findIndexNonEmptyFrom.md +1 -1
- package/docs/api/functions/findPort.md +1 -1
- package/docs/api/functions/formatISO.md +1 -1
- package/docs/api/functions/formatTextWithSpace.md +2 -2
- package/docs/api/functions/funcGetMeta.md +1 -1
- package/docs/api/functions/funcWithMeta.md +2 -2
- package/docs/api/functions/genUrlParamsStr.md +8 -2
- package/docs/api/functions/getAllEnumKeys.md +4 -2
- package/docs/api/functions/getConfigFileNames.md +2 -2
- package/docs/api/functions/getConfigs.md +2 -2
- package/docs/api/functions/getFileMetaInfo.md +1 -1
- package/docs/api/functions/getHashAlgoBySize.md +1 -1
- package/docs/api/functions/getKeysPath.md +4 -2
- package/docs/api/functions/getLLMTokenizer.md +1 -1
- package/docs/api/functions/getMultiLevelExtname.md +1 -1
- package/docs/api/functions/getPackageDir.md +1 -1
- package/docs/api/functions/getRealFilepath.md +1 -1
- package/docs/api/functions/getResponseErrorReadableStream.md +2 -2
- package/docs/api/functions/getXDGConfigs.md +1 -1
- package/docs/api/functions/hasDirectoryIn.md +2 -2
- package/docs/api/functions/hash.md +1 -1
- package/docs/api/functions/hashFile.md +2 -2
- package/docs/api/functions/hashObject.md +2 -2
- package/docs/api/functions/hashStream.md +1 -1
- package/docs/api/functions/initShutdown.md +1 -1
- package/docs/api/functions/isLangUsingSpaces.md +1 -1
- package/docs/api/functions/isListItemString.md +1 -1
- package/docs/api/functions/isModelNameMatched.md +2 -2
- package/docs/api/functions/isPunctuationChar.md +1 -1
- package/docs/api/functions/isQuoted.md +1 -1
- package/docs/api/functions/isRegExp.md +1 -1
- package/docs/api/functions/isSameString.md +1 -1
- package/docs/api/functions/isSectionString.md +2 -2
- package/docs/api/functions/isSentenceEnding.md +1 -1
- package/docs/api/functions/isSepLineString.md +1 -1
- package/docs/api/functions/isStrWrapped.md +1 -1
- package/docs/api/functions/isSubdirectory.md +1 -1
- package/docs/api/functions/isTitleString.md +2 -2
- package/docs/api/functions/isWebStream.md +1 -1
- package/docs/api/functions/joinSplitWords.md +1 -1
- package/docs/api/functions/jsonFilterToWhere.md +2 -2
- package/docs/api/functions/jsonToMarkdownStr.md +2 -2
- package/docs/api/functions/loadAIConfig.md +1 -1
- package/docs/api/functions/loadConfig.md +1 -1
- package/docs/api/functions/loadConfigFile.md +1 -1
- package/docs/api/functions/loadFileFromPaths.md +2 -2
- package/docs/api/functions/loadTextFromPaths.md +3 -3
- package/docs/api/functions/lrucache.md +1 -1
- package/docs/api/functions/matchUrlProtocol.md +1 -1
- package/docs/api/functions/memoize.md +5 -3
- package/docs/api/functions/mergeArray.md +1 -1
- package/docs/api/functions/messagesToText.md +1 -1
- package/docs/api/functions/nanoid.md +45 -0
- package/docs/api/functions/normalizePath.md +1 -1
- package/docs/api/functions/paramsSizeToScaleStr.md +1 -1
- package/docs/api/functions/parseCommand.md +3 -3
- package/docs/api/functions/parseDateFormat.md +7 -3
- package/docs/api/functions/parseISO.md +7 -3
- package/docs/api/functions/parseJsJson.md +2 -2
- package/docs/api/functions/parseJsJsonSimpleSync.md +2 -2
- package/docs/api/functions/parseObjectArgInfo.md +2 -2
- package/docs/api/functions/parseObjectArgumentInfos.md +2 -2
- package/docs/api/functions/parseObjectArguments.md +2 -2
- package/docs/api/functions/parseObjectArgumentsAsArgInfos.md +2 -2
- package/docs/api/functions/parseYaml.md +1 -1
- package/docs/api/functions/pruneSubdirectories.md +1 -1
- package/docs/api/functions/pruneSubdirectoriesInPlace.md +1 -1
- package/docs/api/functions/quoteStr.md +1 -1
- package/docs/api/functions/readFilenamesRecursiveSync.md +2 -2
- package/docs/api/functions/readTextFileChunks.md +2 -2
- package/docs/api/functions/readTextFileChunksEx.md +2 -2
- package/docs/api/functions/readableFromAsyncIterable.md +4 -2
- package/docs/api/functions/registerCoreTools.md +1 -1
- package/docs/api/functions/removeMarkdownBold.md +2 -2
- package/docs/api/functions/removeMarkdownBoldAndItalic.md +2 -2
- package/docs/api/functions/removeMarkdownItalic.md +2 -2
- package/docs/api/functions/replaceWithPlaceholder.md +2 -2
- package/docs/api/functions/restoreFromPlacehoders.md +2 -2
- package/docs/api/functions/sanitizeFilename.md +1 -1
- package/docs/api/functions/sanitizeFilepath.md +1 -1
- package/docs/api/functions/saveConfigFile.md +1 -1
- package/docs/api/functions/scaleStrToParamsSize.md +1 -1
- package/docs/api/functions/shutdown.md +1 -1
- package/docs/api/functions/simplifyObjectArguments.md +1 -1
- package/docs/api/functions/sleep.md +1 -1
- package/docs/api/functions/sortedValues.md +4 -2
- package/docs/api/functions/splitChunks.md +2 -2
- package/docs/api/functions/splitParagraph.md +2 -2
- package/docs/api/functions/splitSentence.md +1 -1
- package/docs/api/functions/splitWords.md +1 -1
- package/docs/api/functions/stringifyYaml.md +1 -1
- package/docs/api/functions/stripConsoleColor.md +1 -1
- package/docs/api/functions/textToDate.md +1 -1
- package/docs/api/functions/throwError.md +1 -1
- package/docs/api/functions/toDate.md +7 -3
- package/docs/api/functions/toDateTime.md +1 -1
- package/docs/api/functions/trimStartOfStreamHelper.md +3 -3
- package/docs/api/functions/truncTo.md +1 -1
- package/docs/api/functions/truncateByToken.md +2 -2
- package/docs/api/functions/truncateToTokenLimit.md +2 -2
- package/docs/api/functions/truncateToTokenLimitEx.md +2 -2
- package/docs/api/functions/uuid.md +15 -6
- package/docs/api/functions/uuidStringify.md +1 -1
- package/docs/api/functions/uuidv1.md +5 -3
- package/docs/api/functions/uuidv4.md +5 -3
- package/docs/api/functions/uuidv5.md +5 -3
- package/docs/api/functions/uuidv6.md +61 -0
- package/docs/api/functions/uuidv7.md +61 -0
- package/docs/api/functions/wrapEventEmitter.md +1 -1
- package/docs/api/functions/xxhash.md +1 -1
- package/docs/api/functions/xxhash32.md +1 -1
- package/docs/api/functions/xxhash64.md +1 -1
- package/docs/api/functions/xxhashAsStr.md +1 -1
- package/docs/api/functions/yieldExec.md +1 -1
- package/docs/api/globals.md +17 -6
- package/docs/api/interfaces/AIChatAssistantMessageParam.md +12 -12
- package/docs/api/interfaces/AIChatContentPartImage.md +3 -3
- package/docs/api/interfaces/AIChatContentPartText.md +3 -3
- package/docs/api/interfaces/AIChatMessageParamBase.md +7 -7
- package/docs/api/interfaces/AIChatMessageToolCall.md +4 -4
- package/docs/api/interfaces/AIChatSystemMessageParam.md +9 -9
- package/docs/api/interfaces/AIChatToolChoiceFuncObject.md +3 -3
- package/docs/api/interfaces/AIChatToolChoiceObject.md +2 -2
- package/docs/api/interfaces/AIChatToolFunc.md +5 -5
- package/docs/api/interfaces/AIChatToolFuncParam.md +3 -3
- package/docs/api/interfaces/AIChatToolMessageParam.md +10 -10
- package/docs/api/interfaces/AIChatToolParam.md +2 -2
- package/docs/api/interfaces/AIChatToolTypeObject.md +2 -2
- package/docs/api/interfaces/AIChatUserMessageParam.md +13 -13
- package/docs/api/interfaces/AIChoiceConfig.md +8 -8
- package/docs/api/interfaces/AIResult.md +12 -8
- package/docs/api/interfaces/AIStreamParser.md +8 -6
- package/docs/api/interfaces/AIStreamParserOptions.md +2 -2
- package/docs/api/interfaces/BaseFunc.md +17 -17
- package/docs/api/interfaces/BaseFuncItem.md +15 -15
- package/docs/api/interfaces/BinarySemaphoreAcquireOptions.md +2 -2
- package/docs/api/interfaces/BinarySemaphoreOptions.md +5 -5
- package/docs/api/interfaces/BinarySemaphoreReleaseOptions.md +2 -2
- package/docs/api/interfaces/BinarySemaphoreReleaserFunc.md +3 -3
- package/docs/api/interfaces/CancelableAbilityOptions.md +4 -4
- package/docs/api/interfaces/ClientFuncItem.md +21 -25
- package/docs/api/interfaces/EventClientFuncParams.md +5 -5
- package/docs/api/interfaces/EventServerFuncParams.md +14 -6
- package/docs/api/interfaces/FewShotPromptTemplateOptions.md +9 -7
- package/docs/api/interfaces/FuncItem.md +18 -18
- package/docs/api/interfaces/FuncParam.md +5 -5
- package/docs/api/interfaces/FuncParams.md +1 -1
- package/docs/api/interfaces/Funcs.md +1 -1
- package/docs/api/interfaces/HashAlgoParams.md +4 -4
- package/docs/api/interfaces/IClientToolTransport.md +9 -9
- package/docs/api/interfaces/IFileMetaInfo.md +8 -8
- package/docs/api/interfaces/IPubSubClientTransport.md +31 -7
- package/docs/api/interfaces/IPubSubServerTransport.md +92 -47
- package/docs/api/interfaces/IReadTextFileChunksOptions.md +13 -13
- package/docs/api/interfaces/IServerToolTransport.md +19 -14
- package/docs/api/interfaces/IToolTransport.md +6 -6
- package/docs/api/interfaces/ITruncateToTokenLimitOptions.md +11 -11
- package/docs/api/interfaces/JsonFilter.md +1 -1
- package/docs/api/interfaces/ParseObjectArgumentOptions.md +11 -11
- package/docs/api/interfaces/ProbabilityItem.md +4 -4
- package/docs/api/interfaces/PromptExampleSelectorOptions.md +3 -3
- package/docs/api/interfaces/PubSubClientStream.md +26 -14
- package/docs/api/interfaces/PubSubServerSession.md +8 -8
- package/docs/api/interfaces/RemoteFuncItem.md +21 -25
- package/docs/api/interfaces/ReplacePlacehoderOptions.md +5 -5
- package/docs/api/interfaces/ResClientFuncParams.md +5 -5
- package/docs/api/interfaces/ResServerFuncParams.md +14 -10
- package/docs/api/interfaces/RpcMethodsClientFuncParams.md +25 -0
- package/docs/api/interfaces/RpcMethodsServerFuncParams.md +61 -0
- package/docs/api/interfaces/SectionStringOptions.md +3 -3
- package/docs/api/interfaces/SemaphoreOptions.md +7 -7
- package/docs/api/interfaces/SemaphoreTaskItem.md +6 -6
- package/docs/api/interfaces/ServerFuncItem.md +22 -26
- package/docs/api/interfaces/ServerFuncParams.md +4 -3
- package/docs/api/interfaces/SplitSentenceOptions.md +5 -5
- package/docs/api/interfaces/StreamCallbacksAndOptions.md +11 -7
- package/docs/api/interfaces/TaskAbortControllers.md +1 -1
- package/docs/api/interfaces/TaskPromise.md +17 -9
- package/docs/api/interfaces/ToolFuncPackage.md +5 -5
- package/docs/api/type-aliases/AIChatContentPart.md +2 -2
- package/docs/api/type-aliases/AIChatMessageParam.md +2 -2
- package/docs/api/type-aliases/AIChatRole.md +2 -2
- package/docs/api/type-aliases/AIChatToolChoiceParam.md +2 -2
- package/docs/api/type-aliases/AIMessageType.md +2 -2
- package/docs/api/type-aliases/AIModelNameRule.md +2 -2
- package/docs/api/type-aliases/AIModelNameRuleFn.md +2 -2
- package/docs/api/type-aliases/AIModelNameRules.md +2 -2
- package/docs/api/type-aliases/AITextGenerationFinishReason.md +2 -2
- package/docs/api/type-aliases/ActionName.md +2 -2
- package/docs/api/type-aliases/ArrayMergeWay.md +2 -2
- package/docs/api/type-aliases/AsyncTaskId.md +2 -2
- package/docs/api/type-aliases/BeforeShutdownListener.md +2 -2
- package/docs/api/type-aliases/EventErrorListenerFn.md +2 -2
- package/docs/api/type-aliases/EventListenerFn.md +2 -2
- package/docs/api/type-aliases/FuncParamType.md +2 -2
- package/docs/api/type-aliases/PromptExamples.md +5 -3
- package/docs/api/type-aliases/PubSubClientId.md +2 -2
- package/docs/api/type-aliases/PubSubCtx.md +24 -4
- package/docs/api/type-aliases/RpcMethodHandler.md +2 -2
- package/docs/api/type-aliases/SSEClient.md +53 -0
- package/docs/api/type-aliases/SemaphoreIsReadyFuncType.md +2 -2
- package/docs/api/type-aliases/TFunc.md +2 -2
- package/docs/api/type-aliases/UUIDVersions.md +11 -0
- package/docs/api/variables/AIChatRoles.md +1 -1
- package/docs/api/variables/AIMessageTypes.md +1 -1
- package/docs/api/variables/AITextGenerationFinishReasons.md +1 -1
- package/docs/api/variables/ActionNames.md +1 -1
- package/docs/api/variables/ArrayMergeWay.md +2 -2
- package/docs/api/variables/ArrayMergeWaySymbol.md +2 -2
- package/docs/api/variables/ClientEventPrefix.md +11 -0
- package/docs/api/variables/ClientToolFuncSchema.md +4 -2
- package/docs/api/variables/DEFAULT_CONFIG_NAME.md +1 -1
- package/docs/api/variables/DefaultAsyncSemaphoreCapacity.md +1 -1
- package/docs/api/variables/EventBusName.md +1 -1
- package/docs/api/variables/EventName.md +1 -1
- package/docs/api/variables/FuncMetaSymbol.md +2 -2
- package/docs/api/variables/LLM_TOKENIZER_NAMES.md +4 -4
- package/docs/api/variables/LLM_TOKENIZER_NAMES_MAP.md +1 -1
- package/docs/api/variables/PASSING_SCORE.md +1 -1
- package/docs/api/variables/RStreamErrCode.md +1 -1
- package/docs/api/variables/RemoteToolFuncSchema.md +4 -2
- package/docs/api/variables/ResponseRStreamErrCode.md +1 -1
- package/docs/api/variables/RpcMethodsClientToolSchema.md +51 -0
- package/docs/api/variables/RpcMethodsServerToolSchema.md +21 -0
- package/docs/api/variables/SHUTDOWN_SIGNALS.md +1 -1
- package/docs/api/variables/SSEChannelAlreadyClosedErrCode.md +1 -1
- package/docs/api/variables/SecondaryCache.md +1 -1
- package/docs/api/variables/ServerToolFuncSchema.md +4 -2
- package/docs/api/variables/StrangeHumanName.md +1 -1
- package/docs/api/variables/ToolAsyncCancelableBit.md +1 -1
- package/docs/api/variables/ToolAsyncMultiTaskBit.md +1 -1
- package/docs/api/variables/ToolAsyncPriorityBit.md +1 -1
- package/docs/api/variables/ToolFuncSchema.md +4 -2
- package/docs/api/variables/backendEventable.md +11 -0
- package/docs/api/variables/base32768.md +1 -1
- package/docs/api/variables/event.md +1 -1
- package/docs/api/variables/eventClient.md +1 -1
- package/docs/api/variables/eventServer.md +1 -1
- package/docs/api/variables/lrucache.md +1 -1
- package/docs/api/variables/makeToolFuncCancelable.md +11 -0
- package/docs/api/{functions → variables}/wait.md +16 -14
- package/docs/pubsub.md +427 -105
- package/docs/server_client_tools.md +5 -3
- package/docs/toolFunc.md +3 -1
- package/package.json +10 -9
- package/dist/chunk-LNTIQQNN.mjs +0 -1
- package/docs/api/functions/backendEventable.md +0 -29
- package/docs/api/functions/makeToolFuncCancelable.md +0 -29
- package/docs/api/interfaces/PubSubClient.md +0 -27
- package/docs/api/namespaces/uuidv5/README.md +0 -12
|
@@ -1,20 +1,116 @@
|
|
|
1
1
|
# Developer's Guide: Real-time Events with EventServer & EventClient
|
|
2
2
|
|
|
3
|
-
This guide provides a comprehensive overview of the `EventServer` and `EventClient`
|
|
3
|
+
This guide provides a comprehensive overview of the `EventServer` and `EventClient` system, a powerful solution for building real-time, bidirectional communication channels. Its core architecture is built on a **pluggable PubSub transport layer**, making it completely independent of any specific communication protocol. You can seamlessly use Server-Sent Events (SSE), WebSockets, IPC, or any other protocol by simply providing a compatible transport implementation.
|
|
4
4
|
|
|
5
|
-
**Prerequisite:** This document assumes you understand the base transport layer. If not, please review the [`
|
|
5
|
+
**Prerequisite:** This document assumes you understand the base transport layer. If not, please review the [`transport.md`](./transport.md) first.
|
|
6
6
|
|
|
7
7
|
## Core Concept: The Unified Event Bus
|
|
8
8
|
|
|
9
|
-
The primary goal of this system is to create a seamless event bus that spans
|
|
9
|
+
The primary goal of this system is to create a seamless, bidirectional event bus that spans server and client — enabling decoupled communication across your application.
|
|
10
|
+
|
|
11
|
+
It separates **control** and **data** planes:
|
|
12
|
+
|
|
13
|
+
* 🛠️ **Control Plane (RPC)**: Client-to-server event forwarding is **opt-in per event**. Call `eventClient.forwardEvent('event.name')` to enable it. Afterwards, `.emit('event.name', data)` automatically triggers an RPC call (via primary transport), sending the **original event name** (e.g., `'ui.click'`). The server **automatically prefixes it with `client:`** (e.g., `'client:ui.click'`) when processing, to avoid naming conflicts and enforce namespace isolation.
|
|
14
|
+
* 📡 **Data Plane (PubSub)**: Event payloads — whether originating from the server or resulting from client events processed by the server — are delivered asynchronously to clients via a dedicated, abstract, pluggable PubSub transport.
|
|
15
|
+
|
|
16
|
+
The key feature is **configurable event forwarding**:
|
|
17
|
+
|
|
18
|
+
* 🔁 The server can be configured to listen to its internal global `eventBus` and automatically relay selected events to subscribed clients via PubSub.
|
|
19
|
+
* 🔁 **Client → Server event forwarding requires two explicit, independent opt-ins:**
|
|
20
|
+
1. **Client-side**: Call `eventClient.forwardEvent('event.name')` to enable RPC publishing for that specific event.
|
|
21
|
+
2. **Server-side**: Set `EventServer.forwardClientPublishes = true` to allow received events (after being prefixed with `client:`) to be emitted on the server’s internal event bus.
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
// Step 1: Client enables forwarding for a specific event
|
|
25
|
+
eventClient.forwardEvent('ui.click');
|
|
26
|
+
|
|
27
|
+
// Step 2: Server enables processing of client-originated events
|
|
28
|
+
EventServer.forwardClientPublishes = true;
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
⚠️ **Important**: The event handling methods (`.on()`, `.off()`, `.emit()` etc) are **not native** to `EventClient` and `EventServer`. Their event capabilities must be **injected via the `backendEventable(...)` function**. Attempting to call these methods before injection will result in a `TypeError`.
|
|
32
|
+
|
|
33
|
+
This design creates a powerful, loosely-coupled architecture where components communicate through events without direct dependencies, location transparency, or transport concerns. The use of AOP (backendEventable) for capability injection ensures maximum flexibility, testability, and explicit opt-in behavior.
|
|
34
|
+
|
|
35
|
+
```md
|
|
36
|
+
┌──────────────────────────────────────────────────────────────────────────────┐
|
|
37
|
+
│ │
|
|
38
|
+
│ ┌────────────────┐ uses ┌─────────────────────┐ │
|
|
39
|
+
│ │ EventClient │ ──────────────▶ │ ClientTransport │ │
|
|
40
|
+
│ │ - InjectEmitter│ (RPC Call) │ (HTTP/WebSocket RPC)│ │
|
|
41
|
+
│ │ - forwardEvent │ ◀─────────────▶ │ │ │
|
|
42
|
+
│ └────────────────┘ └────────┬────────────┘ │
|
|
43
|
+
│ │ RPC: publish('my.event') │
|
|
44
|
+
│ ▼ │
|
|
45
|
+
│ ┌─────────────────────┐ │
|
|
46
|
+
│ │ ServerTransport │ │
|
|
47
|
+
│ │ (HTTP/WebSocket RPC)│ │
|
|
48
|
+
│ └──────────┬──────────┘ │
|
|
49
|
+
│ │ │
|
|
50
|
+
│ ▼ │
|
|
51
|
+
│ ┌─────────────────────────────┐ │
|
|
52
|
+
│ │ EventServer │ │
|
|
53
|
+
│ │ - Inject Emitter │ │
|
|
54
|
+
│ │ - forwardClientPublishes │ │
|
|
55
|
+
│ │ emit('client:ui.click') │ │
|
|
56
|
+
│ └──────────┬──────────────────┘ │
|
|
57
|
+
│ │ Broadcast │
|
|
58
|
+
│ ▼ │
|
|
59
|
+
│ ┌─────────────────────┐ │
|
|
60
|
+
│ │PubSubServerTransport│ │
|
|
61
|
+
│ │ (WebSocket/MQTT) │ │
|
|
62
|
+
│ └──────────┬──────────┘ │
|
|
63
|
+
│ │ PubSub Message │
|
|
64
|
+
│ ▼ │
|
|
65
|
+
│ ┌─────────────────────┐ │
|
|
66
|
+
│ │PubSubClientTransport│ │
|
|
67
|
+
│ │ (WebSocket/MQTT) │ │
|
|
68
|
+
│ └──────────┬──────────┘ │
|
|
69
|
+
│ │ Deliver Message │
|
|
70
|
+
│ ▼ │
|
|
71
|
+
│ ┌───────────────────────┐ │
|
|
72
|
+
│ │ EventClient │ │
|
|
73
|
+
│ │ → Receive and trigger │ │
|
|
74
|
+
│ │ local events │ │
|
|
75
|
+
│ └───────────────────────┘ │
|
|
76
|
+
│ │
|
|
77
|
+
└──────────────────────────────────────────────────────────────────────────────┘
|
|
78
|
+
```
|
|
10
79
|
|
|
11
|
-
|
|
80
|
+
```mermaid
|
|
81
|
+
graph TD
|
|
82
|
+
%% ========== Client Side ==========
|
|
83
|
+
EC[EventClient<br><i>需 backendEventable 注入 .on/.off/.emit</i>]
|
|
84
|
+
CT[ClientTransport<br><i>RPC: 发起 publish/subscribe</i>]
|
|
85
|
+
PCT[PubSubClientTransport<br><i>接收服务端广播事件</i>]
|
|
86
|
+
|
|
87
|
+
%% ========== Server Side ==========
|
|
88
|
+
ES[EventServer<br><i>需 backendEventable 注入 .on/.off/.emit</i>]
|
|
89
|
+
ST[ServerTransport<br><i>RPC: 接收 publish/subscribe</i>]
|
|
90
|
+
PST[PubSubServerTransport<br><i>向客户端广播事件</i>]
|
|
91
|
+
|
|
92
|
+
%% ========== Connections ==========
|
|
93
|
+
EC -- "RPC: publish('client:xxx', data)(需 forwardEvent)" --> CT
|
|
94
|
+
CT -- "→ RPC 请求" --> ST
|
|
95
|
+
ST -- "→ 交由 EventServer 处理" --> ES
|
|
96
|
+
|
|
97
|
+
ES -- "PubSub: 广播事件" --> PST
|
|
98
|
+
PST -- "→ PubSub 消息" --> PCT
|
|
99
|
+
PCT -- "→ 交由 EventClient 分发" --> EC
|
|
100
|
+
|
|
101
|
+
class EC client
|
|
102
|
+
class ES server
|
|
103
|
+
class CT,ST rpc
|
|
104
|
+
class PCT,PST pubsub
|
|
105
|
+
```
|
|
12
106
|
|
|
13
107
|
### Aspect-Oriented Programming (AoP) and Event Emitters
|
|
14
108
|
|
|
15
109
|
A crucial concept is that `EventClient` (and `ClientTools` in general) can be enhanced with event emitter capabilities. By using a library like `events-ex`, you can give your client-side tool instances standard `on`, `off`, and `emit` methods. This allows `EventClient` to act as a local event bus that is transparently synchronized with the server.
|
|
16
110
|
|
|
17
|
-
|
|
111
|
+
> 💡 Event listening and emitting capabilities are injected into `EventClient` via the AOP-style `backendEventable(EventClient)` function, keeping the core class transport-agnostic and easily testable.
|
|
112
|
+
|
|
113
|
+
## Example: HTTP and Server-Sent Events (SSE)
|
|
18
114
|
|
|
19
115
|
### Architecture (SSE)
|
|
20
116
|
|
|
@@ -35,9 +131,9 @@ graph TD
|
|
|
35
131
|
end
|
|
36
132
|
|
|
37
133
|
subgraph Server-Side
|
|
38
|
-
S_Transport[
|
|
39
|
-
ES -- "
|
|
40
|
-
S_PubSub -- "handles SSE
|
|
134
|
+
S_Transport[HttpServerToolTransport] -- "receives RPC" --> ES[EventServer]
|
|
135
|
+
ES -- "delegates to" --> S_PubSub[SseServerPubSubTransport]
|
|
136
|
+
S_PubSub -- "handles SSE connection" --> ES
|
|
41
137
|
ES -- "interacts with" --> S_EventBus[Global Server EventBus]
|
|
42
138
|
OtherServices[Other Server Logic] -- "emit/on" --> S_EventBus
|
|
43
139
|
end
|
|
@@ -49,13 +145,13 @@ graph TD
|
|
|
49
145
|
|
|
50
146
|
### Server-Side Setup (SSE)
|
|
51
147
|
|
|
52
|
-
|
|
148
|
+
On the server, you must set the desired PubSub transport on the `EventServer` class. When a client makes a request to the `eventServer`'s `list` endpoint, the `EventServer` delegates the connection handling to the transport.
|
|
53
149
|
|
|
54
150
|
```typescript
|
|
55
151
|
// In your server entry file (e.g., server.ts)
|
|
56
152
|
import {
|
|
57
153
|
eventServer,
|
|
58
|
-
|
|
154
|
+
HttpServerToolTransport, // Using a generic HTTP transport
|
|
59
155
|
ResServerTools,
|
|
60
156
|
EventServer, // Import EventServer class
|
|
61
157
|
SseServerPubSubTransport // Import the SSE transport
|
|
@@ -68,7 +164,7 @@ async function main() {
|
|
|
68
164
|
// Register the eventServer tool instance
|
|
69
165
|
eventServer.register();
|
|
70
166
|
|
|
71
|
-
const serverTransport = new
|
|
167
|
+
const serverTransport = new HttpServerToolTransport();
|
|
72
168
|
// Mount the base class; the transport finds all registered tools
|
|
73
169
|
serverTransport.mount(ResServerTools, '/api');
|
|
74
170
|
|
|
@@ -79,7 +175,7 @@ async function main() {
|
|
|
79
175
|
|
|
80
176
|
### Client-Side Setup (SSE)
|
|
81
177
|
|
|
82
|
-
On the client,
|
|
178
|
+
On the client, the setup is designed to be simple and robust. You set up your main RPC transport first, and then you set the PubSub transport. The `EventClient` is smart enough to automatically configure the PubSub transport with the `apiRoot` from the main transport.
|
|
83
179
|
|
|
84
180
|
```typescript
|
|
85
181
|
// In your client-side code
|
|
@@ -93,101 +189,221 @@ import {
|
|
|
93
189
|
} from '@isdk/ai-tool';
|
|
94
190
|
|
|
95
191
|
async function main() {
|
|
96
|
-
const apiRoot = 'http://localhost:
|
|
97
|
-
const clientTransport = new HttpClientToolTransport(
|
|
98
|
-
|
|
192
|
+
const apiRoot = 'http://localhost:3000/api';
|
|
193
|
+
const clientTransport = new HttpClientToolTransport();
|
|
194
|
+
// Mount the main transport, which configures the static ClientTools.apiRoot
|
|
195
|
+
await clientTransport.mount(ResClientTools, apiRoot);
|
|
99
196
|
|
|
100
197
|
// **Crucial Step 1: Set the PubSub transport for the client**
|
|
198
|
+
// It is **critical** that `setPubSubTransport` is called *after* the main
|
|
199
|
+
// transport has been mounted, as this is what makes the `apiRoot` available
|
|
200
|
+
// for automatic configuration.
|
|
101
201
|
EventClient.setPubSubTransport(new SseClientPubSubTransport());
|
|
102
202
|
|
|
103
203
|
// **Crucial Step 2: Make the client eventable**
|
|
104
204
|
backendEventable(EventClient);
|
|
105
205
|
eventClient.register();
|
|
106
206
|
|
|
107
|
-
// Now you can use eventClient.on, .off, .emit
|
|
207
|
+
// Now you can use eventClient.on, .off, .emit to interact with the server
|
|
108
208
|
}
|
|
109
209
|
```
|
|
110
210
|
|
|
111
|
-
|
|
211
|
+
## Example: Electron IPC for Real-time Desktop Apps
|
|
212
|
+
|
|
213
|
+
For desktop applications built with Electron, the event system can operate over the built-in Inter-Process Communication (IPC) channels. This provides a highly efficient, real-time backend within your application shell without needing an HTTP server.
|
|
112
214
|
|
|
113
|
-
|
|
215
|
+
We will cover two scenarios: using IPC for Pub/Sub only, and a fully integrated approach where both RPC and Pub/Sub use IPC.
|
|
114
216
|
|
|
115
|
-
|
|
217
|
+
### Scenario 1: Standalone Pub/Sub over IPC
|
|
116
218
|
|
|
117
|
-
|
|
219
|
+
This approach is ideal if you only need a real-time eventing layer or if your main RPC communication uses a different transport (e.g., HTTP).
|
|
118
220
|
|
|
119
|
-
|
|
221
|
+
#### Main Process Setup (`main.ts`)
|
|
222
|
+
|
|
223
|
+
You only need to set up the `ElectronServerPubSubTransport` and tell it to start listening.
|
|
120
224
|
|
|
121
225
|
```typescript
|
|
122
|
-
// In your Electron
|
|
123
|
-
import {
|
|
124
|
-
EventServer,
|
|
125
|
-
eventServer,
|
|
126
|
-
ElectronServerPubSubTransport,
|
|
127
|
-
} from '@isdk/ai-tool';
|
|
226
|
+
// In your main Electron process (e.g., main.ts)
|
|
227
|
+
import { EventServer, ElectronServerPubSubTransport } from '@isdk/ai-tool';
|
|
128
228
|
|
|
129
|
-
// 1. Define a unique namespace for
|
|
130
|
-
const
|
|
229
|
+
// 1. Define a unique namespace for your pub/sub channels.
|
|
230
|
+
const pubSubNamespace = 'my-app-events';
|
|
131
231
|
|
|
132
|
-
// 2. Create
|
|
133
|
-
const
|
|
232
|
+
// 2. Create an instance of the server transport.
|
|
233
|
+
const serverTransport = new ElectronServerPubSubTransport(pubSubNamespace);
|
|
134
234
|
|
|
135
|
-
// 3. Set the transport
|
|
136
|
-
|
|
137
|
-
// (e.g., one for SSE, one for Electron), you would create subclasses of EventServer
|
|
138
|
-
// to hold each static transport instance separately.
|
|
139
|
-
EventServer.setPubSubTransport(electronTransport);
|
|
235
|
+
// 3. Set the transport for the global EventServer.
|
|
236
|
+
EventServer.setPubSubTransport(serverTransport);
|
|
140
237
|
|
|
141
|
-
// 4.
|
|
142
|
-
|
|
238
|
+
// 4. **Crucial Step: Activate the transport's IPC listeners.**
|
|
239
|
+
serverTransport.listen();
|
|
143
240
|
|
|
144
|
-
// Now
|
|
145
|
-
// will
|
|
241
|
+
// Now the EventServer is ready. You can publish events from anywhere
|
|
242
|
+
// in the main process, and they will be sent to subscribed renderers.
|
|
243
|
+
console.log('[Main] Pub/Sub transport listening...');
|
|
244
|
+
setInterval(() => {
|
|
245
|
+
EventServer.publish('server-tick', { timestamp: Date.now() });
|
|
246
|
+
}, 5000);
|
|
146
247
|
```
|
|
147
248
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
In the renderer process, you must use the same namespace (`apiRoot`) to connect to the correct event bus in the main process.
|
|
249
|
+
#### Renderer Process Setup (`renderer.ts`)
|
|
151
250
|
|
|
152
|
-
|
|
251
|
+
The client setup is also straightforward. You must configure the `EventClient` with the same namespace.
|
|
153
252
|
|
|
154
253
|
```typescript
|
|
155
|
-
// In your
|
|
156
|
-
import {
|
|
157
|
-
EventClient,
|
|
158
|
-
eventClient,
|
|
159
|
-
ElectronClientPubSubTransport,
|
|
160
|
-
backendEventable,
|
|
161
|
-
} from '@isdk/ai-tool';
|
|
254
|
+
// In your client-side code (e.g., renderer.ts)
|
|
255
|
+
import { EventClient, eventClient, ElectronClientPubSubTransport, backendEventable } from '@isdk/ai-tool';
|
|
162
256
|
|
|
163
|
-
// 1.
|
|
164
|
-
const
|
|
257
|
+
// 1. Use the same namespace as the server.
|
|
258
|
+
const pubSubNamespace = 'my-app-events';
|
|
165
259
|
|
|
166
|
-
// 2. Set the PubSub transport
|
|
260
|
+
// 2. Set the PubSub transport for the client.
|
|
167
261
|
EventClient.setPubSubTransport(new ElectronClientPubSubTransport());
|
|
168
262
|
|
|
169
|
-
// 3.
|
|
170
|
-
|
|
263
|
+
// 3. **Crucial Step: Configure the EventClient with the namespace.**
|
|
264
|
+
// This is used as the 'apiRoot' to determine which IPC channels to use.
|
|
265
|
+
EventClient.apiRoot = pubSubNamespace;
|
|
171
266
|
|
|
172
|
-
// 4.
|
|
173
|
-
|
|
174
|
-
eventClient.setApiRoot(electronApiRoot);
|
|
267
|
+
// 4. Make the client eventable and register it.
|
|
268
|
+
backendEventable(EventClient);
|
|
175
269
|
eventClient.register();
|
|
176
270
|
|
|
177
|
-
// 5.
|
|
178
|
-
|
|
179
|
-
await eventClient.subscribe('some-event-from-main');
|
|
271
|
+
// 5. Subscribe to events. This automatically triggers the connection to the main process.
|
|
272
|
+
await eventClient.subscribe('server-tick');
|
|
180
273
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
274
|
+
eventClient.on('server-tick', (data) => {
|
|
275
|
+
console.log('Received tick from main process:', data);
|
|
276
|
+
document.body.innerHTML = `Tick received at: ${data.timestamp}`;
|
|
277
|
+
});
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
> **Note on Security**: This simple example assumes `contextIsolation` is disabled. For modern, secure Electron apps, please see the integrated example below which includes a `preload.js` script.
|
|
281
|
+
|
|
282
|
+
### Scenario 2: Integrated RPC and Pub/Sub over IPC
|
|
283
|
+
|
|
284
|
+
This is the recommended, fully-native approach for Electron apps. It provides both request-response (RPC) and real-time events over the same efficient IPC foundation. This requires a `preload.js` script to securely bridge the main and renderer processes.
|
|
184
285
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
286
|
+
#### Part 1: Preload Script (`preload.ts`)
|
|
287
|
+
|
|
288
|
+
This script acts as a secure bridge, exposing only the necessary IPC functions to the renderer process. Your `BrowserWindow` webPreferences should point to this file.
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
// preload.ts
|
|
292
|
+
import { contextBridge, ipcRenderer, IpcRendererEvent } from 'electron';
|
|
293
|
+
|
|
294
|
+
// Define the API that will be exposed to the renderer process via `window.electronApi`
|
|
295
|
+
const electronApi = {
|
|
296
|
+
// For RPC (invoke/handle)
|
|
297
|
+
invoke: (channel: string, ...args: any[]) => ipcRenderer.invoke(channel, ...args),
|
|
298
|
+
|
|
299
|
+
// For Pub/Sub (send/on)
|
|
300
|
+
send: (channel: string, ...args: any[]) => ipcRenderer.send(channel, ...args),
|
|
301
|
+
on: (channel: string, listener: (event: IpcRendererEvent, ...args: any[]) => void) => {
|
|
302
|
+
ipcRenderer.on(channel, listener);
|
|
303
|
+
},
|
|
304
|
+
off: (channel: string, listener: (...args: any[]) => void) => {
|
|
305
|
+
ipcRenderer.removeListener(channel, listener);
|
|
306
|
+
},
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
// Securely expose the API to the renderer's window object
|
|
310
|
+
contextBridge.exposeInMainWorld('electronApi', electronApi);
|
|
311
|
+
|
|
312
|
+
// Optional: Define a type for your exposed API for better TypeScript support
|
|
313
|
+
export type ElectronApi = typeof electronApi;
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
#### Part 2: Main Process Setup (`main.ts`)
|
|
317
|
+
|
|
318
|
+
Here, we set up both the `IpcServerToolTransport` for RPC and the `ElectronServerPubSubTransport` for Pub/Sub.
|
|
319
|
+
|
|
320
|
+
```typescript
|
|
321
|
+
// main.ts
|
|
322
|
+
import { IpcServerToolTransport } from '@isdk/ai-tool';
|
|
323
|
+
import { ElectronServerPubSubTransport } from '@isdk/ai-tool';
|
|
324
|
+
import { EventServer, ResServerTools, eventServer } from '@isdk/ai-tool';
|
|
325
|
+
|
|
326
|
+
// Use a single, consistent namespace for all IPC channels
|
|
327
|
+
const channelNamespace = 'my-app';
|
|
328
|
+
|
|
329
|
+
// --- 1. Setup IPC for standard RPC ---
|
|
330
|
+
const rpcTransport = new IpcServerToolTransport();
|
|
331
|
+
// This sets up handlers for 'my-app:discover' and 'my-app:rpc'.
|
|
332
|
+
rpcTransport.mount(ResServerTools, channelNamespace);
|
|
333
|
+
rpcTransport.start();
|
|
334
|
+
console.log(`[Main] RPC transport started on namespace: ${channelNamespace}`);
|
|
335
|
+
|
|
336
|
+
// --- 2. Setup IPC for Pub/Sub ---
|
|
337
|
+
const pubsubTransport = new ElectronServerPubSubTransport(channelNamespace);
|
|
338
|
+
EventServer.setPubSubTransport(pubsubTransport);
|
|
339
|
+
pubsubTransport.listen(); // Activate the pub/sub listeners
|
|
340
|
+
console.log(`[Main] Pub/Sub transport listening on namespace: ${channelNamespace}`);
|
|
341
|
+
|
|
342
|
+
// --- 3. Register Tools ---
|
|
343
|
+
// Register the eventServer so its methods (sub, unsub) can be called via RPC
|
|
344
|
+
eventServer.register();
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
#### Part 3: Renderer Process Setup (`renderer.ts`)
|
|
348
|
+
|
|
349
|
+
The renderer will use the `electronApi` exposed by the preload script. To make this work seamlessly, you can create simple wrapper classes for your transports that use the bridged API instead of the raw `ipcRenderer`.
|
|
350
|
+
|
|
351
|
+
```typescript
|
|
352
|
+
// renderer.ts
|
|
353
|
+
import { IpcClientToolTransport, ElectronClientPubSubTransport, EventClient, ResClientTools, eventClient, backendEventable } from '@isdk/ai-tool';
|
|
354
|
+
import type { ElectronApi } from './preload';
|
|
355
|
+
|
|
356
|
+
// Make the bridged API available on the window object for TypeScript
|
|
357
|
+
declare global {
|
|
358
|
+
interface Window { electronApi: ElectronApi; }
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// --- Custom Transports Using the Secure Bridge ---
|
|
362
|
+
|
|
363
|
+
// 1. Create a custom RPC transport that uses the bridged `invoke` method.
|
|
364
|
+
class SecureIpcRpcTransport extends IpcClientToolTransport {
|
|
365
|
+
async _fetch(name: string, args?: any, act?: any, subName?: any) {
|
|
366
|
+
const payload = { toolId: name, params: args || {}, act, id: subName };
|
|
367
|
+
return window.electronApi.invoke(this.channels.rpc, payload);
|
|
368
|
+
}
|
|
369
|
+
async loadApis() {
|
|
370
|
+
return window.electronApi.invoke(this.channels.discover);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// 2. Create a custom Pub/Sub transport that uses the bridged API.
|
|
375
|
+
// This pattern requires that the base transport class allows for dependency injection.
|
|
376
|
+
class SecureIpcPubSubTransport extends ElectronClientPubSubTransport {
|
|
377
|
+
constructor() {
|
|
378
|
+
// Pass the bridged API to the constructor instead of letting it use the global ipcRenderer
|
|
379
|
+
super(window.electronApi as any);
|
|
380
|
+
}
|
|
188
381
|
}
|
|
189
382
|
|
|
190
|
-
|
|
383
|
+
// --- Application Setup ---
|
|
384
|
+
async function main() {
|
|
385
|
+
const channelNamespace = 'my-app';
|
|
386
|
+
|
|
387
|
+
// Use the secure RPC transport
|
|
388
|
+
const rpcTransport = new SecureIpcRpcTransport();
|
|
389
|
+
await rpcTransport.mount(ResClientTools, channelNamespace);
|
|
390
|
+
|
|
391
|
+
// Use the secure Pub/Sub transport
|
|
392
|
+
const pubsubTransport = new SecureIpcPubSubTransport();
|
|
393
|
+
EventClient.setPubSubTransport(pubsubTransport);
|
|
394
|
+
EventClient.apiRoot = channelNamespace;
|
|
395
|
+
|
|
396
|
+
backendEventable(EventClient);
|
|
397
|
+
eventClient.register();
|
|
398
|
+
|
|
399
|
+
// Now you can use RPC and Pub/Sub together seamlessly and securely!
|
|
400
|
+
await eventClient.subscribe('server-time');
|
|
401
|
+
eventClient.on('server-time', (data) => {
|
|
402
|
+
console.log('Received time from main:', data);
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
main();
|
|
191
407
|
```
|
|
192
408
|
|
|
193
409
|
---
|
|
@@ -198,52 +414,105 @@ Once the setup is complete, the API for using the event bus is the same regardle
|
|
|
198
414
|
|
|
199
415
|
### Forwarding and Publishing Events (Server-Side)
|
|
200
416
|
|
|
201
|
-
|
|
417
|
+
There are two ways to send events from the server to clients: through the global `eventBus` (standard approach) or by publishing directly to the transport (advanced approach).
|
|
202
418
|
|
|
203
|
-
|
|
419
|
+
#### Standard Approach: Forwarding from the Event Bus
|
|
420
|
+
|
|
421
|
+
The recommended and most common way to send events is to emit them on the global `eventBus`. You can configure the `eventServer` to automatically listen for specific events on this bus and forward them to subscribed clients. This creates a clean, decoupled architecture.
|
|
204
422
|
|
|
205
423
|
```typescript
|
|
206
|
-
import { eventServer
|
|
424
|
+
import { eventServer } from '@isdk/ai-tool';
|
|
207
425
|
import { event } from '@isdk/ai-tool/funcs/event'; // The global eventBus
|
|
208
426
|
|
|
209
427
|
const eventBus = event.runSync();
|
|
210
428
|
|
|
211
|
-
//
|
|
212
|
-
// Automatically relay events from the global eventBus to subscribed clients.
|
|
429
|
+
// 1. Configure the server to forward events.
|
|
213
430
|
eventServer.forward(['user-updated', 'item-added']);
|
|
214
431
|
|
|
215
|
-
// Now, any other part of your server can simply emit events on the bus
|
|
432
|
+
// 2. Now, any other part of your server can simply emit events on the bus.
|
|
216
433
|
function updateUser(user: any) {
|
|
217
434
|
eventBus.emit('user-updated', { userId: user.id, status: 'active' });
|
|
218
435
|
}
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
#### Advanced Approach: Direct Publishing to the Transport
|
|
219
439
|
|
|
220
|
-
|
|
221
|
-
// The `EventServer.publish` method allows sending an event directly to clients.
|
|
222
|
-
// Its signature is: `publish(event: string, data: any, target?: { clientId: string | string[] })`.
|
|
440
|
+
For special cases where you need to bypass the global `eventBus` and send an event directly to the transport layer, you can use the static `EventServer.publish()` method.
|
|
223
441
|
|
|
224
|
-
|
|
225
|
-
|
|
442
|
+
This is an advanced feature. For it to work correctly, the client must have explicitly registered its interest in the event at the transport level, typically by using `eventClient.init(['event-name'])`.
|
|
443
|
+
|
|
444
|
+
```typescript
|
|
445
|
+
import { EventServer } from '@isdk/ai-tool';
|
|
446
|
+
|
|
447
|
+
// The signature is: `publish(event: string, data: any, target?: { clientId: string | string[] })`.
|
|
448
|
+
|
|
449
|
+
// 1. Broadcast to all clients that used init() to subscribe to 'broadcast-message'.
|
|
226
450
|
function sendBroadcast() {
|
|
227
451
|
EventServer.publish('broadcast-message', { message: 'Server is restarting soon!' });
|
|
228
452
|
}
|
|
229
453
|
|
|
230
454
|
// 2. Send a targeted event to a specific client.
|
|
231
|
-
// This requires knowing the `clientId` of the recipient.
|
|
232
455
|
function sendDirectMessage(clientId: string, message: string) {
|
|
233
456
|
const target = { clientId };
|
|
234
457
|
EventServer.publish('private-message', { text: message }, target);
|
|
235
458
|
}
|
|
459
|
+
```
|
|
236
460
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
461
|
+
### Receiving and Distinguishing Events on the Server
|
|
462
|
+
|
|
463
|
+
A key design feature of the event system is the clear, safe separation between events originating from the server's internal logic and events published by clients. This is achieved by automatically prefixing all client-originated events with `client:`.
|
|
464
|
+
|
|
465
|
+
This mechanism prevents clients from accidentally or maliciously triggering sensitive internal events, and allows server-side plugins to focus on business logic without needing to manually check the origin of every event.
|
|
466
|
+
|
|
467
|
+
#### Enabling/Disabling Client Event Forwarding
|
|
468
|
+
|
|
469
|
+
By default, events published by clients are **not** forwarded to the server's internal event bus. This is a security-first approach that prevents unwanted side effects.
|
|
470
|
+
|
|
471
|
+
To enable this feature, you must set the static `forwardClientPublishes` property to `true` during your server setup.
|
|
472
|
+
|
|
473
|
+
```typescript
|
|
474
|
+
// In your server entry file
|
|
475
|
+
import { EventServer } from '@isdk/ai-tool';
|
|
476
|
+
|
|
477
|
+
// Set to true to allow client events to be emitted on the server bus.
|
|
478
|
+
EventServer.forwardClientPublishes = true;
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
Only when this is set to `true` will the server emit `client:` prefixed events on its internal bus.
|
|
482
|
+
|
|
483
|
+
#### Example 1: Listening for an Internal Server Event
|
|
484
|
+
|
|
485
|
+
This is the standard way to listen for events that are part of the server's own workflow. This listener will **never** be triggered by a client publishing an event with the same name.
|
|
242
486
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
eventBus
|
|
246
|
-
|
|
487
|
+
```typescript
|
|
488
|
+
import { event } from '@isdk/ai-tool/funcs/event';
|
|
489
|
+
const eventBus = event.runSync();
|
|
490
|
+
|
|
491
|
+
// Listens for an event triggered only by server-side logic.
|
|
492
|
+
// e.g., eventBus.emit('data-updated', { id: 123 });
|
|
493
|
+
eventBus.on('data-updated', (data) => {
|
|
494
|
+
console.log('Internal data has been updated. Refreshing cache...');
|
|
495
|
+
// ...purely internal business logic
|
|
496
|
+
});
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
#### Example 2: Listening for a Client-Originated Event
|
|
500
|
+
|
|
501
|
+
To react to an event published by a client, you must explicitly listen for the event name with the `client:` prefix. The listener will also receive a metadata object containing the trusted, server-verified `clientId`.
|
|
502
|
+
|
|
503
|
+
```typescript
|
|
504
|
+
import { event } from '@isdk/ai-tool/funcs/event';
|
|
505
|
+
const eventBus = event.runSync();
|
|
506
|
+
|
|
507
|
+
// A client publishes an event like:
|
|
508
|
+
// eventClient.publish({ event: 'user-action', data: { ... } });
|
|
509
|
+
|
|
510
|
+
// The server-side listener must use the 'client:' prefix.
|
|
511
|
+
eventBus.on('client:user-action', (data, meta) => {
|
|
512
|
+
// The 'meta' object contains the trusted clientId from the transport layer.
|
|
513
|
+
const { clientId } = meta;
|
|
514
|
+
console.log(`User ${clientId} performed an action with data:`, data);
|
|
515
|
+
// ...logic to handle the client interaction
|
|
247
516
|
});
|
|
248
517
|
```
|
|
249
518
|
|
|
@@ -258,6 +527,9 @@ eventClient.on('user-updated', (data: any) => {
|
|
|
258
527
|
});
|
|
259
528
|
|
|
260
529
|
// **Publish an Event to the Server**
|
|
530
|
+
// This sends the event to the server. On the server's internal event bus,
|
|
531
|
+
// it will be prefixed and emitted as 'client:client-action'.
|
|
532
|
+
// Other clients will receive it as the original 'client-action'.
|
|
261
533
|
eventClient.publish({
|
|
262
534
|
event: 'client-action',
|
|
263
535
|
data: { action: 'button-click', value: 123 }
|
|
@@ -267,6 +539,8 @@ eventClient.publish({
|
|
|
267
539
|
eventClient.forwardEvent(['user-settings-changed']);
|
|
268
540
|
|
|
269
541
|
function onSettingsSave(newSettings: any) {
|
|
542
|
+
// This emits the event on the local event bus. Because it was forwarded,
|
|
543
|
+
// it will also be published to the server.
|
|
270
544
|
eventClient.emit('user-settings-changed', newSettings);
|
|
271
545
|
}
|
|
272
546
|
```
|
|
@@ -277,7 +551,11 @@ This transport-agnostic architecture provides a clean, powerful, and decoupled w
|
|
|
277
551
|
|
|
278
552
|
## Implementing a Custom PubSub Transport
|
|
279
553
|
|
|
280
|
-
For developers who need to integrate a different messaging protocol (e.g., WebSockets, MQTT), you can create your own transport by implementing the
|
|
554
|
+
For developers who need to integrate a different messaging protocol (e.g., WebSockets, MQTT), you can create your own transport by implementing the server-side and client-side PubSub interfaces.
|
|
555
|
+
|
|
556
|
+
### Server-Side: `IPubSubServerTransport`
|
|
557
|
+
|
|
558
|
+
The server-side implementation is responsible for managing client connections and broadcasting events. You implement the `IPubSubServerTransport` interface from `@isdk/ai-tool/transports/pubsub/server`.
|
|
281
559
|
|
|
282
560
|
The core interface is defined as follows:
|
|
283
561
|
|
|
@@ -286,25 +564,32 @@ export interface IPubSubServerTransport {
|
|
|
286
564
|
readonly name: string;
|
|
287
565
|
readonly protocol: string;
|
|
288
566
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
567
|
+
/**
|
|
568
|
+
* Subscribes a client to an event stream by taking over an incoming request.
|
|
569
|
+
*
|
|
570
|
+
* This method is designed to be generic. Transport-specific details, such as
|
|
571
|
+
* HTTP request/response objects, are passed inside the `options` parameter.
|
|
572
|
+
*
|
|
573
|
+
* @param events Optional array of event names to initially subscribe the client to.
|
|
574
|
+
* @param options A container for transport-specific parameters.
|
|
575
|
+
* @returns A `PubSubClient` object representing the newly connected client.
|
|
576
|
+
*/
|
|
295
577
|
subscribe: (
|
|
296
578
|
events?: string[],
|
|
297
579
|
options?: {
|
|
298
|
-
req
|
|
299
|
-
res
|
|
580
|
+
req: any; // e.g., http.IncomingMessage
|
|
581
|
+
res: any; // e.g., http.ServerResponse
|
|
300
582
|
clientId?: string;
|
|
301
583
|
[k: string]: any;
|
|
302
584
|
}
|
|
303
585
|
) => PubSubClient; // Return a client object, minimally with a `clientId`.
|
|
304
586
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
587
|
+
/**
|
|
588
|
+
* Publishes an event from the server to clients.
|
|
589
|
+
*
|
|
590
|
+
* The `target` parameter allows for broadcasting (default) or
|
|
591
|
+
* targeted delivery to specific client IDs.
|
|
592
|
+
*/
|
|
308
593
|
publish: (
|
|
309
594
|
event: string,
|
|
310
595
|
data: any,
|
|
@@ -315,12 +600,49 @@ export interface IPubSubServerTransport {
|
|
|
315
600
|
onConnection: (cb: (session: PubSubServerSession) => void) => void;
|
|
316
601
|
onDisconnect: (cb: (session: PubSubServerSession) => void) => void;
|
|
317
602
|
|
|
318
|
-
|
|
319
|
-
|
|
603
|
+
/**
|
|
604
|
+
* Optional: For bidirectional transports (e.g., WebSockets)
|
|
605
|
+
* to handle messages received from the client.
|
|
606
|
+
*/
|
|
320
607
|
onMessage?: (
|
|
321
608
|
cb: (session: PubSubServerSession, event: string, data: any) => void
|
|
322
609
|
) => void;
|
|
323
610
|
}
|
|
324
611
|
```
|
|
325
612
|
|
|
326
|
-
By implementing this interface, your custom transport can be plugged directly into the `EventServer` using `EventServer.setPubSubTransport(new YourCustomTransport())
|
|
613
|
+
By implementing this interface, your custom transport can be plugged directly into the `EventServer` using `EventServer.setPubSubTransport(new YourCustomTransport())`.
|
|
614
|
+
|
|
615
|
+
### Client-Side: `IPubSubClientTransport`
|
|
616
|
+
|
|
617
|
+
The client-side implementation is responsible for establishing a connection to the server and receiving events. This is defined by the `IPubSubClientTransport` interface from `@isdk/ai-tool/transports/pubsub/client`.
|
|
618
|
+
|
|
619
|
+
The core interface is defined as follows:
|
|
620
|
+
|
|
621
|
+
```typescript
|
|
622
|
+
export interface IPubSubClientTransport {
|
|
623
|
+
/**
|
|
624
|
+
* Establishes a connection to a server endpoint.
|
|
625
|
+
* @param url The path of the endpoint, relative to the `apiRoot` configured on the transport. For advanced use or compatibility, this can also be a full, absolute URL.
|
|
626
|
+
* @param params Optional parameters for the connection, such as initial event subscriptions.
|
|
627
|
+
* @returns A `PubSubClientStream` instance representing the connection.
|
|
628
|
+
*/
|
|
629
|
+
connect: (url: string, params?: Record<string, any>) => PubSubClientStream;
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* Optional. Disconnects a given stream.
|
|
633
|
+
*/
|
|
634
|
+
disconnect?: (stream: PubSubClientStream) => void;
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* Optional. Configures the transport with a base URL.
|
|
638
|
+
* If implemented, this allows the transport to resolve relative paths
|
|
639
|
+
* passed to the `connect` method. This is called automatically by `EventClient.setPubSubTransport`.
|
|
640
|
+
* @param apiRoot The base URL for the API.
|
|
641
|
+
*/
|
|
642
|
+
setApiRoot?: (apiRoot: string) => void;
|
|
643
|
+
}
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
The `EventClient` automatically handles the `apiRoot` configuration, making the system easy to use. The `connect` method is the most critical part, as it decouples the `EventClient` from the specifics of how a connection is made.
|
|
647
|
+
|
|
648
|
+
By implementing both interfaces, you can enable the entire real-time event system over your chosen protocol.
|