@qvac/sdk 0.6.0 → 0.7.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.
Files changed (278) hide show
  1. package/LICENSE +1 -24
  2. package/NOTICE +632 -0
  3. package/README.md +2 -1
  4. package/dist/client/api/embed.js +1 -4
  5. package/dist/client/api/get-model-info.d.ts +10 -3
  6. package/dist/client/api/index.d.ts +2 -0
  7. package/dist/client/api/index.js +2 -0
  8. package/dist/client/api/invoke-plugin.d.ts +14 -0
  9. package/dist/client/api/invoke-plugin.js +38 -0
  10. package/dist/client/api/registry.d.ts +14 -0
  11. package/dist/client/api/registry.js +37 -0
  12. package/dist/client/api/transcribe.js +0 -4
  13. package/dist/client/api/unload-model.js +1 -1
  14. package/dist/client/config-loader/resolve-config.node.d.ts +3 -2
  15. package/dist/client/config-loader/resolve-config.node.js +31 -4
  16. package/dist/client/init-hooks.js +2 -1
  17. package/dist/client/rpc/bare-client.d.ts +2 -0
  18. package/dist/client/rpc/bare-client.js +48 -7
  19. package/dist/client/rpc/expo-device-info.d.ts +6 -0
  20. package/dist/client/rpc/expo-device-info.js +32 -0
  21. package/dist/client/rpc/expo-rpc-client.js +9 -32
  22. package/dist/client/rpc/node-rpc-client.d.ts +1 -1
  23. package/dist/client/rpc/node-rpc-client.js +201 -28
  24. package/dist/client/rpc/rpc-client.d.ts +1 -1
  25. package/dist/client/rpc/rpc-client.js +2 -2
  26. package/dist/constants/index.d.ts +1 -0
  27. package/dist/constants/index.js +1 -0
  28. package/dist/constants/registry.d.ts +2 -0
  29. package/dist/constants/registry.js +1 -0
  30. package/dist/examples/cache-management.js +2 -2
  31. package/dist/examples/config/plugins/plugins.config.d.ts +5 -0
  32. package/dist/examples/config/plugins/plugins.config.js +4 -0
  33. package/dist/examples/download-with-blind-relays.js +1 -1
  34. package/dist/examples/llamacpp-multimodal.js +3 -3
  35. package/dist/examples/llamacpp-native-tools.js +2 -2
  36. package/dist/examples/mcp-websearch.js +2 -2
  37. package/dist/examples/multi-model-demo.js +2 -2
  38. package/dist/examples/ocr-fasttext.js +4 -4
  39. package/dist/examples/parallel-download.js +2 -2
  40. package/dist/examples/plugins.d.ts +14 -0
  41. package/dist/examples/plugins.js +129 -0
  42. package/dist/examples/registry-query.d.ts +2 -0
  43. package/dist/examples/registry-query.js +87 -0
  44. package/dist/examples/translation/translation-bergamot-batch.js +2 -2
  45. package/dist/examples/translation/translation-bergamot.js +4 -4
  46. package/dist/examples/translation/translation-opus.js +2 -2
  47. package/dist/examples/translation/translation-stream.js +2 -2
  48. package/dist/examples/tts/chatterbox.d.ts +2 -0
  49. package/dist/examples/tts/chatterbox.js +59 -0
  50. package/dist/examples/tts/supertonic.d.ts +2 -0
  51. package/dist/examples/tts/supertonic.js +51 -0
  52. package/dist/examples/tts/utils.d.ts +17 -0
  53. package/dist/examples/tts/utils.js +93 -0
  54. package/dist/examples/whispercpp-filesystem.js +2 -3
  55. package/dist/examples/whispercpp-microphone-record.js +1 -2
  56. package/dist/examples/whispercpp-prompt.js +2 -3
  57. package/dist/expo/plugins/index.d.ts +3 -0
  58. package/dist/expo/plugins/index.js +3 -0
  59. package/dist/expo/plugins/resolve-sdk-package-dir.d.ts +15 -0
  60. package/dist/expo/plugins/resolve-sdk-package-dir.js +31 -0
  61. package/dist/expo/plugins/withAndroidArchitecture.d.ts +8 -0
  62. package/dist/expo/plugins/withAndroidArchitecture.js +52 -0
  63. package/dist/expo/plugins/withAndroidNdkVersion.d.ts +8 -0
  64. package/dist/expo/plugins/withAndroidNdkVersion.js +31 -0
  65. package/dist/expo/plugins/withDeviceInfo.d.ts +10 -0
  66. package/dist/expo/plugins/withDeviceInfo.js +48 -0
  67. package/dist/expo/plugins/withMobileBundle.d.ts +5 -2
  68. package/dist/expo/plugins/withMobileBundle.js +121 -65
  69. package/dist/expo/plugins/withQvacSDK.d.ts +4 -0
  70. package/dist/expo/plugins/withQvacSDK.js +26 -1
  71. package/dist/index.d.ts +4 -4
  72. package/dist/index.js +5 -5
  73. package/dist/models/{hyperdrive → registry}/index.js +1 -1
  74. package/dist/models/registry/models.d.ts +9056 -0
  75. package/dist/models/registry/models.js +9351 -0
  76. package/dist/models/update-models/codegen.d.ts +3 -0
  77. package/dist/models/update-models/codegen.js +135 -0
  78. package/dist/models/update-models/history.d.ts +13 -0
  79. package/dist/models/update-models/history.js +88 -0
  80. package/dist/models/update-models/index.d.ts +2 -0
  81. package/dist/models/update-models/index.js +124 -0
  82. package/dist/models/update-models/naming.d.ts +4 -0
  83. package/dist/models/update-models/naming.js +314 -0
  84. package/dist/models/update-models/processing.d.ts +8 -0
  85. package/dist/models/update-models/processing.js +67 -0
  86. package/dist/models/update-models/registry.d.ts +4 -0
  87. package/dist/models/update-models/registry.js +67 -0
  88. package/dist/models/update-models/shards.d.ts +4 -0
  89. package/dist/models/update-models/shards.js +57 -0
  90. package/dist/models/update-models/types.d.ts +60 -0
  91. package/dist/models/update-models/types.js +1 -0
  92. package/dist/models/update-models/utils.d.ts +3 -0
  93. package/dist/models/update-models/utils.js +21 -0
  94. package/dist/pear/pre.d.ts +18 -0
  95. package/dist/pear/pre.js +433 -0
  96. package/dist/schemas/common.d.ts +286 -32
  97. package/dist/schemas/common.js +12 -0
  98. package/dist/schemas/download-asset.d.ts +35 -11
  99. package/dist/schemas/download-asset.js +5 -1
  100. package/dist/schemas/engine-addon-map.d.ts +5 -0
  101. package/dist/schemas/engine-addon-map.js +49 -0
  102. package/dist/schemas/get-model-info.d.ts +32 -16
  103. package/dist/schemas/get-model-info.js +17 -8
  104. package/dist/schemas/index.d.ts +4 -1
  105. package/dist/schemas/index.js +4 -4
  106. package/dist/schemas/load-model.d.ts +2224 -900
  107. package/dist/schemas/load-model.js +130 -25
  108. package/dist/schemas/model-src-utils.d.ts +30 -12
  109. package/dist/schemas/model-src-utils.js +5 -2
  110. package/dist/schemas/model-types.d.ts +15 -4
  111. package/dist/schemas/model-types.js +21 -4
  112. package/dist/schemas/path-validation.d.ts +10 -0
  113. package/dist/schemas/path-validation.js +22 -0
  114. package/dist/schemas/plugin.d.ts +180 -0
  115. package/dist/schemas/plugin.js +132 -0
  116. package/dist/schemas/rag.d.ts +2 -2
  117. package/dist/schemas/rag.js +7 -3
  118. package/dist/schemas/registry.d.ts +338 -0
  119. package/dist/schemas/registry.js +91 -0
  120. package/dist/schemas/sdk-errors-client.d.ts +6 -6
  121. package/dist/schemas/sdk-errors-client.js +15 -15
  122. package/dist/schemas/sdk-errors-registry.d.ts +6 -0
  123. package/dist/schemas/sdk-errors-registry.js +8 -0
  124. package/dist/schemas/sdk-errors-server.d.ts +16 -2
  125. package/dist/schemas/sdk-errors-server.js +90 -10
  126. package/dist/schemas/text-to-speech.d.ts +670 -1
  127. package/dist/schemas/text-to-speech.js +51 -2
  128. package/dist/schemas/translate.d.ts +2 -2
  129. package/dist/scripts/run-security-tests-bare.d.ts +2 -0
  130. package/dist/scripts/run-security-tests-bare.js +78 -0
  131. package/dist/scripts/run-security-tests.d.ts +2 -0
  132. package/dist/scripts/run-security-tests.js +22 -0
  133. package/dist/server/bare/delegate-rpc-client.d.ts +6 -0
  134. package/dist/server/bare/delegate-rpc-client.js +136 -56
  135. package/dist/server/bare/{addons/llamacpp-embedding/handlers → ops}/embed.d.ts +1 -1
  136. package/dist/server/bare/{addons/llamacpp-embedding/handlers → ops}/embed.js +3 -3
  137. package/dist/server/bare/{addons/llamacpp-completion → ops}/kv-cache-utils.js +6 -6
  138. package/dist/server/bare/{addons → ops}/load-model.js +32 -42
  139. package/dist/server/bare/{addons/nmtcpp-translation/handlers → ops}/translate.d.ts +1 -1
  140. package/dist/server/bare/{addons/nmtcpp-translation/handlers → ops}/translate.js +5 -4
  141. package/dist/server/bare/plugins/index.d.ts +7 -0
  142. package/dist/server/bare/plugins/index.js +6 -0
  143. package/dist/server/bare/{addons/llamacpp-completion → plugins/llamacpp-completion/ops}/cache-logger.js +1 -1
  144. package/dist/server/bare/{addons/llamacpp-completion/handlers → plugins/llamacpp-completion/ops}/completion-stream.d.ts +1 -1
  145. package/dist/server/bare/{addons/llamacpp-completion/handlers → plugins/llamacpp-completion/ops}/completion-stream.js +2 -3
  146. package/dist/server/bare/plugins/llamacpp-completion/plugin.d.ts +115 -0
  147. package/dist/server/bare/plugins/llamacpp-completion/plugin.js +142 -0
  148. package/dist/server/bare/plugins/llamacpp-embedding/plugin.d.ts +24 -0
  149. package/dist/server/bare/plugins/llamacpp-embedding/plugin.js +73 -0
  150. package/dist/server/bare/plugins/nmtcpp-translation/plugin.d.ts +45 -0
  151. package/dist/server/bare/plugins/nmtcpp-translation/plugin.js +97 -0
  152. package/dist/server/bare/{addons/onnx-ocr/handlers → plugins/onnx-ocr/ops}/ocr-stream.js +2 -1
  153. package/dist/server/bare/plugins/onnx-ocr/plugin.d.ts +38 -0
  154. package/dist/server/bare/{addons/onnx-ocr/create-model.js → plugins/onnx-ocr/plugin.js} +39 -2
  155. package/dist/server/bare/{addons/onnx-tts/handlers → plugins/onnx-tts/ops}/text-to-speech.js +23 -1
  156. package/dist/server/bare/plugins/onnx-tts/plugin.d.ts +26 -0
  157. package/dist/server/bare/plugins/onnx-tts/plugin.js +162 -0
  158. package/dist/server/bare/plugins/onnx-tts/wav-helper.d.ts +14 -0
  159. package/dist/server/bare/plugins/onnx-tts/wav-helper.js +107 -0
  160. package/dist/server/bare/plugins/whispercpp-transcription/ops/transcribe-stream.d.ts +3 -0
  161. package/dist/server/bare/{addons/whispercpp-transcription/handlers → plugins/whispercpp-transcription/ops}/transcribe-stream.js +1 -2
  162. package/dist/server/bare/plugins/whispercpp-transcription/plugin.d.ts +31 -0
  163. package/dist/server/bare/plugins/whispercpp-transcription/plugin.js +74 -0
  164. package/dist/server/bare/rag-hyperdb/handlers/ingest.js +1 -1
  165. package/dist/server/bare/rag-hyperdb/handlers/search.js +1 -1
  166. package/dist/server/bare/rag-hyperdb/rag-workspace-manager.js +2 -1
  167. package/dist/server/bare/registry/config-registry.js +1 -1
  168. package/dist/server/bare/registry/model-config-utils.js +0 -11
  169. package/dist/server/bare/registry/registry-client.d.ts +5 -0
  170. package/dist/server/bare/registry/registry-client.js +37 -0
  171. package/dist/server/env.d.ts +22 -0
  172. package/dist/server/env.js +57 -0
  173. package/dist/server/plugins/index.d.ts +2 -0
  174. package/dist/server/plugins/index.js +1 -0
  175. package/dist/server/plugins/registry.d.ts +9 -0
  176. package/dist/server/plugins/registry.js +75 -0
  177. package/dist/server/rpc/delegate-transport.d.ts +11 -0
  178. package/dist/server/rpc/delegate-transport.js +60 -0
  179. package/dist/server/rpc/handle-request.d.ts +0 -9
  180. package/dist/server/rpc/handle-request.js +13 -321
  181. package/dist/server/rpc/handler-registry.d.ts +3 -0
  182. package/dist/server/rpc/handler-registry.js +84 -0
  183. package/dist/server/rpc/handler-utils.d.ts +22 -0
  184. package/dist/server/rpc/handler-utils.js +87 -0
  185. package/dist/server/rpc/handlers/cancelHandler.js +1 -1
  186. package/dist/server/rpc/handlers/completion-stream-delegated.js +1 -1
  187. package/dist/server/rpc/handlers/completion-stream.js +2 -43
  188. package/dist/server/rpc/handlers/delete-cache.js +1 -1
  189. package/dist/server/rpc/handlers/embed.js +2 -21
  190. package/dist/server/rpc/handlers/get-model-info.js +19 -15
  191. package/dist/server/rpc/handlers/index.d.ts +9 -0
  192. package/dist/server/rpc/handlers/index.js +9 -0
  193. package/dist/server/rpc/handlers/load-model/download-manager.d.ts +1 -0
  194. package/dist/server/rpc/handlers/load-model/download-manager.js +3 -0
  195. package/dist/server/rpc/handlers/load-model/handler.js +47 -24
  196. package/dist/server/rpc/handlers/load-model/http.js +3 -2
  197. package/dist/server/rpc/handlers/load-model/hyperdrive.js +14 -10
  198. package/dist/server/rpc/handlers/load-model/registry.d.ts +10 -0
  199. package/dist/server/rpc/handlers/load-model/registry.js +571 -0
  200. package/dist/server/rpc/handlers/load-model/resolve.js +18 -5
  201. package/dist/server/rpc/handlers/load-model-delegated.js +4 -2
  202. package/dist/server/rpc/handlers/ocr-stream.js +2 -37
  203. package/dist/server/rpc/handlers/ping.d.ts +3 -0
  204. package/dist/server/rpc/handlers/ping.js +3 -0
  205. package/dist/server/rpc/handlers/plugin-dispatch.d.ts +15 -0
  206. package/dist/server/rpc/handlers/plugin-dispatch.js +54 -0
  207. package/dist/server/rpc/handlers/plugin-invoke.d.ts +4 -0
  208. package/dist/server/rpc/handlers/plugin-invoke.js +87 -0
  209. package/dist/server/rpc/handlers/registry.d.ts +5 -0
  210. package/dist/server/rpc/handlers/registry.js +157 -0
  211. package/dist/server/rpc/handlers/text-to-speech.js +2 -5
  212. package/dist/server/rpc/handlers/transcribe-stream.js +2 -30
  213. package/dist/server/rpc/handlers/translate.js +2 -42
  214. package/dist/server/rpc/handlers/unload-model.js +1 -1
  215. package/dist/server/rpc/index.d.ts +1 -1
  216. package/dist/server/utils/archive.d.ts +1 -0
  217. package/dist/server/utils/archive.js +10 -2
  218. package/dist/server/utils/cache.d.ts +5 -0
  219. package/dist/server/utils/cache.js +26 -2
  220. package/dist/server/utils/gguf-tensor-extractor.js +3 -3
  221. package/dist/server/utils/index.d.ts +1 -1
  222. package/dist/server/utils/index.js +1 -1
  223. package/dist/server/utils/path-security.d.ts +12 -0
  224. package/dist/server/utils/path-security.js +25 -0
  225. package/dist/server/utils/shard-utils.js +3 -3
  226. package/dist/server/worker-core.d.ts +6 -0
  227. package/dist/server/worker-core.js +88 -0
  228. package/dist/server/worker.d.ts +4 -5
  229. package/dist/server/worker.js +18 -116
  230. package/dist/utils/errors-client.d.ts +9 -6
  231. package/dist/utils/errors-client.js +19 -11
  232. package/dist/utils/errors-server.d.ts +44 -5
  233. package/dist/utils/errors-server.js +82 -9
  234. package/dist/utils/path-sanitize.d.ts +17 -0
  235. package/dist/utils/path-sanitize.js +54 -0
  236. package/expo/plugins/patches/android-link.mjs +63 -0
  237. package/expo/plugins/patches/ios-link.mjs +63 -0
  238. package/package.json +74 -12
  239. package/dist/examples/text-to-speech.d.ts +0 -3
  240. package/dist/examples/text-to-speech.js +0 -137
  241. package/dist/models/hyperdrive/models.d.ts +0 -4003
  242. package/dist/models/hyperdrive/models.js +0 -4282
  243. package/dist/server/bare/addons/index.d.ts +0 -4
  244. package/dist/server/bare/addons/index.js +0 -3
  245. package/dist/server/bare/addons/llamacpp-completion/create-model.d.ts +0 -8
  246. package/dist/server/bare/addons/llamacpp-completion/create-model.js +0 -43
  247. package/dist/server/bare/addons/llamacpp-completion/index.d.ts +0 -4
  248. package/dist/server/bare/addons/llamacpp-completion/index.js +0 -3
  249. package/dist/server/bare/addons/llamacpp-embedding/create-model.d.ts +0 -8
  250. package/dist/server/bare/addons/llamacpp-embedding/create-model.js +0 -38
  251. package/dist/server/bare/addons/llamacpp-embedding/index.d.ts +0 -3
  252. package/dist/server/bare/addons/llamacpp-embedding/index.js +0 -2
  253. package/dist/server/bare/addons/nmtcpp-translation/create-model.d.ts +0 -8
  254. package/dist/server/bare/addons/nmtcpp-translation/create-model.js +0 -52
  255. package/dist/server/bare/addons/nmtcpp-translation/index.d.ts +0 -3
  256. package/dist/server/bare/addons/nmtcpp-translation/index.js +0 -2
  257. package/dist/server/bare/addons/onnx-ocr/create-model.d.ts +0 -10
  258. package/dist/server/bare/addons/onnx-ocr/index.d.ts +0 -3
  259. package/dist/server/bare/addons/onnx-ocr/index.js +0 -2
  260. package/dist/server/bare/addons/onnx-tts/create-model.d.ts +0 -10
  261. package/dist/server/bare/addons/onnx-tts/create-model.js +0 -25
  262. package/dist/server/bare/addons/onnx-tts/index.d.ts +0 -3
  263. package/dist/server/bare/addons/onnx-tts/index.js +0 -2
  264. package/dist/server/bare/addons/whispercpp-transcription/create-model.d.ts +0 -10
  265. package/dist/server/bare/addons/whispercpp-transcription/create-model.js +0 -37
  266. package/dist/server/bare/addons/whispercpp-transcription/handlers/transcribe-stream.d.ts +0 -3
  267. package/dist/server/bare/addons/whispercpp-transcription/index.d.ts +0 -3
  268. package/dist/server/bare/addons/whispercpp-transcription/index.js +0 -2
  269. /package/dist/models/{hyperdrive → registry}/index.d.ts +0 -0
  270. /package/dist/server/bare/{addons → ops}/cancel.d.ts +0 -0
  271. /package/dist/server/bare/{addons → ops}/cancel.js +0 -0
  272. /package/dist/server/bare/{addons/llamacpp-completion → ops}/kv-cache-utils.d.ts +0 -0
  273. /package/dist/server/bare/{addons → ops}/load-model.d.ts +0 -0
  274. /package/dist/server/bare/{addons → ops}/unload-model.d.ts +0 -0
  275. /package/dist/server/bare/{addons → ops}/unload-model.js +0 -0
  276. /package/dist/server/bare/{addons/llamacpp-completion → plugins/llamacpp-completion/ops}/cache-logger.d.ts +0 -0
  277. /package/dist/server/bare/{addons/onnx-ocr/handlers → plugins/onnx-ocr/ops}/ocr-stream.d.ts +0 -0
  278. /package/dist/server/bare/{addons/onnx-tts/handlers → plugins/onnx-tts/ops}/text-to-speech.d.ts +0 -0
@@ -0,0 +1,3 @@
1
+ export function handlePing() {
2
+ return { type: "pong", number: Math.random() * 100 };
3
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Dispatches a request to a plugin handler and returns a Promise.
3
+ * Use for non-streaming (reply) handlers.
4
+ *
5
+ * @throws PluginHandlerTypeMismatchError if the handler is streaming
6
+ */
7
+ export declare function dispatchPluginReply<TRequest, TResponse>(modelId: string, handlerName: string, request: TRequest): Promise<TResponse>;
8
+ /**
9
+ * Dispatches a request to a plugin handler and returns an AsyncGenerator.
10
+ * Use for streaming handlers.
11
+ *
12
+ * @throws PluginHandlerTypeMismatchError if the handler is not streaming
13
+ */
14
+ export declare function dispatchPluginStream<TRequest, TResponse>(modelId: string, handlerName: string, request: TRequest): AsyncGenerator<TResponse>;
15
+ //# sourceMappingURL=plugin-dispatch.d.ts.map
@@ -0,0 +1,54 @@
1
+ import { getModelEntry } from "../../bare/registry/model-registry.js";
2
+ import { getPlugin } from "../../plugins/index.js";
3
+ import { ModelNotFoundError, ModelIsDelegatedError, PluginNotFoundError, PluginHandlerNotFoundError, PluginHandlerTypeMismatchError, } from "../../../utils/errors-server.js";
4
+ /**
5
+ * Resolves the plugin handler and returns both the result and streaming flag.
6
+ */
7
+ function resolvePluginHandler(modelId, handlerName, request) {
8
+ const entry = getModelEntry(modelId);
9
+ if (!entry) {
10
+ throw new ModelNotFoundError(modelId);
11
+ }
12
+ if (entry.isDelegated || !entry.local) {
13
+ throw new ModelIsDelegatedError(modelId);
14
+ }
15
+ const plugin = getPlugin(entry.local.modelType);
16
+ if (!plugin) {
17
+ throw new PluginNotFoundError(entry.local.modelType);
18
+ }
19
+ const handlerDef = plugin.handlers[handlerName];
20
+ if (!handlerDef) {
21
+ const availableHandlers = Object.keys(plugin.handlers);
22
+ throw new PluginHandlerNotFoundError(entry.local.modelType, handlerName, availableHandlers);
23
+ }
24
+ return {
25
+ result: handlerDef.handler(request),
26
+ streaming: handlerDef.streaming,
27
+ };
28
+ }
29
+ /**
30
+ * Dispatches a request to a plugin handler and returns a Promise.
31
+ * Use for non-streaming (reply) handlers.
32
+ *
33
+ * @throws PluginHandlerTypeMismatchError if the handler is streaming
34
+ */
35
+ export async function dispatchPluginReply(modelId, handlerName, request) {
36
+ const { result, streaming } = resolvePluginHandler(modelId, handlerName, request);
37
+ if (streaming) {
38
+ throw new PluginHandlerTypeMismatchError(handlerName, "reply", "streaming");
39
+ }
40
+ return result;
41
+ }
42
+ /**
43
+ * Dispatches a request to a plugin handler and returns an AsyncGenerator.
44
+ * Use for streaming handlers.
45
+ *
46
+ * @throws PluginHandlerTypeMismatchError if the handler is not streaming
47
+ */
48
+ export async function* dispatchPluginStream(modelId, handlerName, request) {
49
+ const { result, streaming } = resolvePluginHandler(modelId, handlerName, request);
50
+ if (!streaming) {
51
+ throw new PluginHandlerTypeMismatchError(handlerName, "streaming", "reply");
52
+ }
53
+ yield* result;
54
+ }
@@ -0,0 +1,4 @@
1
+ import type { PluginInvokeRequest, PluginInvokeResponse, PluginInvokeStreamRequest, PluginInvokeStreamResponse } from "../../../schemas/plugin";
2
+ export declare function handlePluginInvoke(request: PluginInvokeRequest): Promise<PluginInvokeResponse>;
3
+ export declare function handlePluginInvokeStream(request: PluginInvokeStreamRequest): AsyncGenerator<PluginInvokeStreamResponse>;
4
+ //# sourceMappingURL=plugin-invoke.d.ts.map
@@ -0,0 +1,87 @@
1
+ import { getModelEntry } from "../../bare/registry/model-registry.js";
2
+ import { getPlugin, getPluginHandler } from "../../plugins/index.js";
3
+ import { PluginNotFoundError, PluginHandlerNotFoundError, PluginHandlerTypeMismatchError, PluginRequestValidationFailedError, PluginResponseValidationFailedError, ModelIsDelegatedError, ModelNotFoundError, } from "../../../utils/errors-server.js";
4
+ import { getServerLogger } from "../../../logging/index.js";
5
+ const logger = getServerLogger();
6
+ function resolvePluginHandler(modelId, handlerName) {
7
+ const modelEntry = getModelEntry(modelId);
8
+ if (!modelEntry) {
9
+ throw new ModelNotFoundError(modelId);
10
+ }
11
+ if (modelEntry.isDelegated || !modelEntry.local) {
12
+ throw new ModelIsDelegatedError(modelId);
13
+ }
14
+ const modelType = modelEntry.local.modelType;
15
+ const plugin = getPlugin(modelType);
16
+ if (!plugin) {
17
+ throw new PluginNotFoundError(modelType);
18
+ }
19
+ const handlerDef = getPluginHandler(modelType, handlerName);
20
+ if (!handlerDef) {
21
+ const availableHandlers = Object.keys(plugin.handlers);
22
+ throw new PluginHandlerNotFoundError(modelType, handlerName, availableHandlers);
23
+ }
24
+ return { modelType, plugin, handlerDef };
25
+ }
26
+ export async function handlePluginInvoke(request) {
27
+ const { modelId, handler: handlerName, params } = request;
28
+ logger.debug(`[pluginInvoke] modelId=${modelId} handler=${handlerName}`);
29
+ const { handlerDef } = resolvePluginHandler(modelId, handlerName);
30
+ if (handlerDef.streaming) {
31
+ throw new PluginHandlerTypeMismatchError(handlerName, "reply", "streaming");
32
+ }
33
+ const parseResult = handlerDef.requestSchema.safeParse(params);
34
+ if (!parseResult.success) {
35
+ const details = parseResult.error.issues
36
+ .map((i) => `${String(i.path.join("."))}: ${i.message}`)
37
+ .join(", ");
38
+ throw new PluginRequestValidationFailedError(handlerName, details);
39
+ }
40
+ const result = await handlerDef.handler(parseResult.data);
41
+ const responseParseResult = handlerDef.responseSchema.safeParse(result);
42
+ if (!responseParseResult.success) {
43
+ const details = responseParseResult.error.issues
44
+ .map((i) => `${String(i.path.join("."))}: ${i.message}`)
45
+ .join(", ");
46
+ throw new PluginResponseValidationFailedError(handlerName, details);
47
+ }
48
+ return {
49
+ type: "pluginInvoke",
50
+ result: responseParseResult.data,
51
+ };
52
+ }
53
+ export async function* handlePluginInvokeStream(request) {
54
+ const { modelId, handler: handlerName, params } = request;
55
+ logger.debug(`[pluginInvokeStream] modelId=${modelId} handler=${handlerName}`);
56
+ const { handlerDef } = resolvePluginHandler(modelId, handlerName);
57
+ if (!handlerDef.streaming) {
58
+ throw new PluginHandlerTypeMismatchError(handlerName, "streaming", "reply");
59
+ }
60
+ const parseResult = handlerDef.requestSchema.safeParse(params);
61
+ if (!parseResult.success) {
62
+ const details = parseResult.error.issues
63
+ .map((i) => `${String(i.path.join("."))}: ${i.message}`)
64
+ .join(", ");
65
+ throw new PluginRequestValidationFailedError(handlerName, details);
66
+ }
67
+ const generator = handlerDef.handler(parseResult.data);
68
+ for await (const chunk of generator) {
69
+ const responseParseResult = handlerDef.responseSchema.safeParse(chunk);
70
+ if (!responseParseResult.success) {
71
+ const details = responseParseResult.error.issues
72
+ .map((i) => `${String(i.path.join("."))}: ${i.message}`)
73
+ .join(", ");
74
+ throw new PluginResponseValidationFailedError(handlerName, details);
75
+ }
76
+ yield {
77
+ type: "pluginInvokeStream",
78
+ result: responseParseResult.data,
79
+ done: false,
80
+ };
81
+ }
82
+ yield {
83
+ type: "pluginInvokeStream",
84
+ result: null,
85
+ done: true,
86
+ };
87
+ }
@@ -0,0 +1,5 @@
1
+ import type { ModelRegistryListResponse, ModelRegistrySearchRequest, ModelRegistrySearchResponse, ModelRegistryGetModelRequest, ModelRegistryGetModelResponse } from "../../../schemas/index";
2
+ export declare function handleModelRegistryList(): Promise<ModelRegistryListResponse>;
3
+ export declare function handleModelRegistrySearch(request: ModelRegistrySearchRequest): Promise<ModelRegistrySearchResponse>;
4
+ export declare function handleModelRegistryGetModel(request: ModelRegistryGetModelRequest): Promise<ModelRegistryGetModelResponse>;
5
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1,157 @@
1
+ import { REGISTRY_ERROR_CODES } from "../../../schemas/sdk-errors-registry.js";
2
+ import { getAddonFromEngine, resolveCanonicalEngine, } from "../../../schemas/engine-addon-map.js";
3
+ import { getRegistryClient } from "../../bare/registry/registry-client.js";
4
+ import { getServerLogger } from "../../../logging/index.js";
5
+ import { ModelRegistryQueryFailedError } from "../../../utils/errors-server.js";
6
+ const logger = getServerLogger();
7
+ function toHexString(value) {
8
+ if (!value)
9
+ return "";
10
+ if (Buffer.isBuffer(value))
11
+ return value.toString("hex");
12
+ if (typeof value === "string")
13
+ return value;
14
+ if (typeof value === "object" && "data" in value) {
15
+ return Buffer.from(value.data).toString("hex");
16
+ }
17
+ return "";
18
+ }
19
+ function processRegistryModel(model) {
20
+ const engine = resolveCanonicalEngine(model.engine || "");
21
+ if (!engine) {
22
+ logger.warn(`Skipping model with unknown engine "${model.engine}": ${model.path}`);
23
+ return null;
24
+ }
25
+ const filename = model.path.split("/").pop() || model.path;
26
+ const blobBinding = model.blobBinding;
27
+ const blobCoreKey = toHexString(blobBinding?.coreKey);
28
+ const blobBlockOffset = blobBinding?.blockOffset ?? 0;
29
+ const blobBlockLength = blobBinding?.blockLength ?? 0;
30
+ const blobByteOffset = blobBinding?.byteOffset ?? 0;
31
+ const expectedSize = blobBinding?.byteLength ?? 0;
32
+ // sha256 lives on blobBinding at runtime (per hyperschema), fall back to top-level
33
+ const sha256Checksum = blobBinding?.["sha256"] ||
34
+ model.sha256 ||
35
+ "";
36
+ const addon = getAddonFromEngine(engine);
37
+ // Use the filename without extension as the display name
38
+ const name = filename.replace(/\.\w+$/, "");
39
+ return {
40
+ name,
41
+ registryPath: model.path,
42
+ registrySource: model.source,
43
+ blobCoreKey,
44
+ blobBlockOffset,
45
+ blobBlockLength,
46
+ blobByteOffset,
47
+ modelId: filename,
48
+ addon,
49
+ expectedSize,
50
+ sha256Checksum,
51
+ engine,
52
+ quantization: model.quantization || "",
53
+ params: model.params || "",
54
+ };
55
+ }
56
+ export async function handleModelRegistryList() {
57
+ logger.debug("Handling QVAC model registry list request");
58
+ try {
59
+ const client = await getRegistryClient();
60
+ const registryModels = await client.findBy();
61
+ const models = registryModels
62
+ .map(processRegistryModel)
63
+ .filter((m) => m !== null);
64
+ logger.debug(`QVAC model registry list returned ${models.length} models`);
65
+ return {
66
+ type: "modelRegistryList",
67
+ success: true,
68
+ models,
69
+ };
70
+ }
71
+ catch (error) {
72
+ logger.error("QVAC model registry list failed:", error);
73
+ // Re-throw registry client errors directly (19001-19003)
74
+ const qvacError = error;
75
+ if (qvacError.code === REGISTRY_ERROR_CODES.FAILED_TO_CONNECT ||
76
+ qvacError.code === REGISTRY_ERROR_CODES.FAILED_TO_CLOSE ||
77
+ qvacError.code === REGISTRY_ERROR_CODES.MODEL_NOT_FOUND) {
78
+ throw error;
79
+ }
80
+ // Wrap unknown errors in SDK error
81
+ throw new ModelRegistryQueryFailedError(error instanceof Error ? error.message : "Unknown error", error);
82
+ }
83
+ }
84
+ export async function handleModelRegistrySearch(request) {
85
+ logger.debug("Handling QVAC model registry search request", request);
86
+ try {
87
+ const client = await getRegistryClient();
88
+ const registryModels = await client.findBy({
89
+ ...(request.quantization && { quantization: request.quantization }),
90
+ });
91
+ let models = registryModels
92
+ .map(processRegistryModel)
93
+ .filter((m) => m !== null);
94
+ if (request.engine) {
95
+ const canonicalEngine = resolveCanonicalEngine(request.engine);
96
+ models = models.filter((m) => m.engine === (canonicalEngine ?? request.engine));
97
+ }
98
+ if (request.filter) {
99
+ const filterLower = request.filter.toLowerCase();
100
+ models = models.filter((m) => m.name.toLowerCase().includes(filterLower) ||
101
+ m.registryPath.toLowerCase().includes(filterLower) ||
102
+ m.addon.toLowerCase().includes(filterLower) ||
103
+ m.engine.toLowerCase().includes(filterLower));
104
+ }
105
+ if (request.addon) {
106
+ models = models.filter((m) => m.addon === request.addon);
107
+ }
108
+ logger.debug(`QVAC model registry search returned ${models.length} models`);
109
+ return {
110
+ type: "modelRegistrySearch",
111
+ success: true,
112
+ models,
113
+ };
114
+ }
115
+ catch (error) {
116
+ logger.error("QVAC model registry search failed:", error);
117
+ // Re-throw registry client errors directly (19001-19003)
118
+ const qvacError = error;
119
+ if (qvacError.code === REGISTRY_ERROR_CODES.FAILED_TO_CONNECT ||
120
+ qvacError.code === REGISTRY_ERROR_CODES.FAILED_TO_CLOSE ||
121
+ qvacError.code === REGISTRY_ERROR_CODES.MODEL_NOT_FOUND) {
122
+ throw error;
123
+ }
124
+ throw new ModelRegistryQueryFailedError(error instanceof Error ? error.message : "Unknown error", error);
125
+ }
126
+ }
127
+ export async function handleModelRegistryGetModel(request) {
128
+ logger.debug("Handling QVAC model registry get model request", request);
129
+ try {
130
+ const client = await getRegistryClient();
131
+ const rawModel = await client.getModel(request.registryPath, request.registrySource);
132
+ if (!rawModel) {
133
+ throw new ModelRegistryQueryFailedError(`Model not found: ${request.registrySource}/${request.registryPath}`);
134
+ }
135
+ const model = processRegistryModel(rawModel);
136
+ if (!model) {
137
+ throw new ModelRegistryQueryFailedError(`Model has unknown engine "${rawModel.engine}": ${request.registryPath}`);
138
+ }
139
+ logger.debug("QVAC model registry get model found:", model.name);
140
+ return {
141
+ type: "modelRegistryGetModel",
142
+ success: true,
143
+ model,
144
+ };
145
+ }
146
+ catch (error) {
147
+ logger.error("QVAC model registry get model failed:", error);
148
+ // Re-throw registry client errors directly (19001-19003)
149
+ const qvacError = error;
150
+ if (qvacError.code === REGISTRY_ERROR_CODES.FAILED_TO_CONNECT ||
151
+ qvacError.code === REGISTRY_ERROR_CODES.FAILED_TO_CLOSE ||
152
+ qvacError.code === REGISTRY_ERROR_CODES.MODEL_NOT_FOUND) {
153
+ throw error;
154
+ }
155
+ throw new ModelRegistryQueryFailedError(error instanceof Error ? error.message : "Unknown error", error);
156
+ }
157
+ }
@@ -1,7 +1,4 @@
1
- import { textToSpeech } from "../../bare/addons/onnx-tts/index.js";
1
+ import { dispatchPluginStream } from "./plugin-dispatch.js";
2
2
  export async function* handleTextToSpeech(request) {
3
- const stream = textToSpeech(request);
4
- for await (const response of stream) {
5
- yield response;
6
- }
3
+ yield* dispatchPluginStream(request.modelId, "textToSpeech", request);
7
4
  }
@@ -1,32 +1,4 @@
1
- import { transcribeStream } from "../../bare/addons/whispercpp-transcription/index.js";
2
- import { getServerLogger } from "../../../logging/index.js";
3
- const logger = getServerLogger();
1
+ import { dispatchPluginStream } from "./plugin-dispatch.js";
4
2
  export async function* handleTranscribeStream(request) {
5
- const { modelId, audioChunk, prompt } = request;
6
- try {
7
- // Stream transcription results in real-time
8
- for await (const textChunk of transcribeStream({
9
- audioChunk,
10
- modelId,
11
- prompt,
12
- })) {
13
- yield {
14
- type: "transcribeStream",
15
- text: textChunk,
16
- };
17
- }
18
- // Signal completion
19
- yield {
20
- type: "transcribeStream",
21
- done: true,
22
- };
23
- }
24
- catch (error) {
25
- logger.error("Error during transcription:", error);
26
- yield {
27
- type: "transcribeStream",
28
- text: "",
29
- error: error instanceof Error ? error.message : "Unknown error",
30
- };
31
- }
3
+ yield* dispatchPluginStream(request.modelId, "transcribeStream", request);
32
4
  }
@@ -1,44 +1,4 @@
1
- import { translate } from "../../bare/addons/nmtcpp-translation/index.js";
2
- import { getServerLogger } from "../../../logging/index.js";
3
- const logger = getServerLogger();
1
+ import { dispatchPluginStream } from "./plugin-dispatch.js";
4
2
  export async function* handleTranslate(request) {
5
- try {
6
- // Remove the 'type' field from request and pass the rest to translate
7
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
8
- const { type, ...translateParams } = request;
9
- const generator = translate(translateParams);
10
- let stats;
11
- let done = false;
12
- let buffer = "";
13
- while (!done) {
14
- const result = await generator.next();
15
- if (result.done) {
16
- stats = result.value;
17
- done = true;
18
- }
19
- else {
20
- buffer += result.value;
21
- if (translateParams.stream) {
22
- yield {
23
- type: "translate",
24
- token: result.value,
25
- };
26
- }
27
- }
28
- }
29
- yield {
30
- type: "translate",
31
- token: buffer,
32
- done: true,
33
- stats,
34
- };
35
- }
36
- catch (error) {
37
- logger.error("Error during translation:", error);
38
- yield {
39
- type: "translate",
40
- token: "",
41
- error: error instanceof Error ? error.message : "Unknown error",
42
- };
43
- }
3
+ yield* dispatchPluginStream(request.modelId, "translate", request);
44
4
  }
@@ -1,4 +1,4 @@
1
- import { unloadModel } from "../../bare/addons/index.js";
1
+ import { unloadModel } from "../../bare/ops/unload-model.js";
2
2
  import { getRegistryStats } from "../../bare/registry/model-registry.js";
3
3
  import { hasActiveProviders } from "../../bare/hyperswarm.js";
4
4
  import { getServerLogger } from "../../../logging/index.js";
@@ -1,3 +1,3 @@
1
1
  export { createBareKitRPCServer, createIPCClient } from "./create-server";
2
- export { handleRequest, type BareRPCRequest } from "./handle-request";
2
+ export { handleRequest } from "./handle-request";
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1,3 +1,4 @@
1
1
  export declare function isArchiveUrl(url: string): boolean;
2
+ export declare function extractTarStream(archivePath: string, extractDir: string, useGunzip: boolean, signal?: AbortSignal): Promise<void>;
2
3
  export declare function extractAndValidateShardedArchive(archivePath: string, extractDir: string, signal?: AbortSignal): Promise<string>;
3
4
  //# sourceMappingURL=archive.d.ts.map
@@ -5,6 +5,7 @@ import { createGunzip } from "bare-zlib";
5
5
  import tarStream from "tar-stream";
6
6
  import { detectShardedModel, generateShardFilenames, extractTensorsFromShards, validateShardedModelCache, } from "./shard-utils.js";
7
7
  import { ModelLoadFailedError, ArchiveExtractionFailedError, ArchiveUnsupportedTypeError, ArchiveMissingShardsError, DownloadCancelledError, } from "../../utils/errors-server.js";
8
+ import { isPathWithinBase } from "./path-security.js";
8
9
  import { getServerLogger } from "../../logging/index.js";
9
10
  import { SUPPORTED_ARCHIVE_EXTENSIONS, filenameToArchiveTypeSchema, } from "../../schemas/archive.js";
10
11
  const logger = getServerLogger();
@@ -63,7 +64,7 @@ async function extractArchive(archivePath, extractDir, signal) {
63
64
  await fsPromises.mkdir(extractDir, { recursive: true });
64
65
  await extractTarStream(archivePath, extractDir, isGzipped, signal);
65
66
  }
66
- async function extractTarStream(archivePath, extractDir, useGunzip, signal) {
67
+ export async function extractTarStream(archivePath, extractDir, useGunzip, signal) {
67
68
  const extract = tarStream.extract();
68
69
  const gunzip = useGunzip ? createGunzip() : null;
69
70
  return new Promise((resolve, reject) => {
@@ -91,7 +92,14 @@ async function extractTarStream(archivePath, extractDir, useGunzip, signal) {
91
92
  next();
92
93
  return;
93
94
  }
94
- const filePath = path.join(extractDir, header.name);
95
+ // Prevent zip-slip: reject entries that escape extractDir (CWE-22)
96
+ const filePath = path.resolve(path.join(extractDir, header.name));
97
+ if (!isPathWithinBase(extractDir, filePath)) {
98
+ logger.warn(`⚠️ Skipping archive entry with path traversal: ${header.name}`);
99
+ stream.resume();
100
+ next();
101
+ return;
102
+ }
95
103
  if (header.type === "file") {
96
104
  const dir = path.dirname(filePath);
97
105
  fs.mkdirSync(dir, { recursive: true });
@@ -7,6 +7,11 @@ export declare function getKVCacheDir(): string;
7
7
  * Returns: cache/sharded/<hyperdriveKey>/
8
8
  */
9
9
  export declare function getShardedModelCacheDir(hyperdriveKey: string): string;
10
+ /**
11
+ * Get full path to ONNX file in cache
12
+ * Returns: cache/onnx/<cacheKey>/<filename>
13
+ */
14
+ export declare function getOnnxModelPath(cacheKey: string, filename: string): string;
10
15
  /**
11
16
  * Get full path to specific shard file
12
17
  * Returns: cache/sharded/<hyperdriveKey>/<shardFilename>
@@ -1,8 +1,9 @@
1
1
  import fs, { promises as fsPromises } from "bare-fs";
2
2
  import path from "bare-path";
3
- import { getEnv } from "../worker.js";
3
+ import { getEnv } from "../env.js";
4
4
  import { getConfiguredCacheDir } from "../bare/registry/config-registry.js";
5
5
  import { calculateFileChecksum } from "./checksum.js";
6
+ import { validateAndJoinPath } from "./path-security.js";
6
7
  import { getServerLogger } from "../../logging/index.js";
7
8
  const logger = getServerLogger();
8
9
  export function getCacheDir(subDir) {
@@ -44,13 +45,36 @@ export function getShardedModelCacheDir(hyperdriveKey) {
44
45
  }
45
46
  return shardDir;
46
47
  }
48
+ /**
49
+ * Get cache directory for ONNX model with external data
50
+ * Returns: cache/onnx/<cacheKey>/
51
+ */
52
+ function getOnnxModelCacheDir(cacheKey) {
53
+ const baseCache = getModelsCacheDir();
54
+ const onnxDir = validateAndJoinPath(baseCache, "onnx", cacheKey);
55
+ try {
56
+ fs.mkdirSync(onnxDir, { recursive: true });
57
+ }
58
+ catch (error) {
59
+ logger.error(`Error creating ONNX model cache directory:`, error instanceof Error ? error.message : String(error));
60
+ }
61
+ return onnxDir;
62
+ }
63
+ /**
64
+ * Get full path to ONNX file in cache
65
+ * Returns: cache/onnx/<cacheKey>/<filename>
66
+ */
67
+ export function getOnnxModelPath(cacheKey, filename) {
68
+ const onnxDir = getOnnxModelCacheDir(cacheKey);
69
+ return validateAndJoinPath(onnxDir, filename);
70
+ }
47
71
  /**
48
72
  * Get full path to specific shard file
49
73
  * Returns: cache/sharded/<hyperdriveKey>/<shardFilename>
50
74
  */
51
75
  export function getShardPath(hyperdriveKey, shardFilename) {
52
76
  const shardDir = getShardedModelCacheDir(hyperdriveKey);
53
- return path.join(shardDir, shardFilename);
77
+ return validateAndJoinPath(shardDir, shardFilename);
54
78
  }
55
79
  /**
56
80
  * Check if all shards exist and are valid (size + checksum check)
@@ -1,7 +1,7 @@
1
1
  import { promises as fsPromises, createReadStream } from "bare-fs";
2
- import path from "bare-path";
3
2
  import { getServerLogger } from "../../logging/index.js";
4
3
  import { ModelLoadFailedError } from "../../utils/errors-server.js";
4
+ import { validateAndJoinPath } from "./path-security.js";
5
5
  const logger = getServerLogger();
6
6
  // GGUF value types
7
7
  const GGUF_TYPE = {
@@ -253,7 +253,7 @@ async function extractTensorNamesFromHeader(filePath) {
253
253
  * @throws ModelLoadFailedError if any shard fails extraction
254
254
  */
255
255
  export async function extractAndWriteTensorsFile(shardDir, shardFilenames, baseFilename) {
256
- const tensorsFilePath = path.join(shardDir, `${baseFilename}.tensors.txt`);
256
+ const tensorsFilePath = validateAndJoinPath(shardDir, `${baseFilename}.tensors.txt`);
257
257
  try {
258
258
  await fsPromises.access(tensorsFilePath);
259
259
  logger.info(`Tensors file already exists: ${tensorsFilePath}`);
@@ -266,7 +266,7 @@ export async function extractAndWriteTensorsFile(shardDir, shardFilenames, baseF
266
266
  const allTensorNames = new Set();
267
267
  const failedShards = [];
268
268
  for (const filename of shardFilenames) {
269
- const shardPath = path.join(shardDir, filename);
269
+ const shardPath = validateAndJoinPath(shardDir, filename);
270
270
  try {
271
271
  logger.debug(`Extracting tensors from ${filename}...`);
272
272
  const tensorNames = await extractTensorNamesFromHeader(shardPath);
@@ -1,10 +1,10 @@
1
1
  export * from "./archive";
2
- export * from "./audio/decoder";
3
2
  export * from "./cache";
4
3
  export * from "./checksum";
5
4
  export * from "./config-transform";
6
5
  export * from "./formatting";
7
6
  export * from "./gguf-tensor-extractor";
8
7
  export * from "./model-path";
8
+ export * from "./path-security";
9
9
  export * from "./shard-utils";
10
10
  //# sourceMappingURL=index.d.ts.map
@@ -1,9 +1,9 @@
1
1
  export * from "./archive.js";
2
- export * from "./audio/decoder.js";
3
2
  export * from "./cache.js";
4
3
  export * from "./checksum.js";
5
4
  export * from "./config-transform.js";
6
5
  export * from "./formatting.js";
7
6
  export * from "./gguf-tensor-extractor.js";
8
7
  export * from "./model-path.js";
8
+ export * from "./path-security.js";
9
9
  export * from "./shard-utils.js";
@@ -0,0 +1,12 @@
1
+ export { sanitizePathComponent } from "../../utils/path-sanitize";
2
+ /**
3
+ * Check whether a target path is contained within a base directory.
4
+ * Both paths are resolved to absolute before comparison.
5
+ */
6
+ export declare function isPathWithinBase(basePath: string, targetPath: string): boolean;
7
+ /**
8
+ * Sanitize components, join them to a base path, and verify the result
9
+ * stays within the base directory. Throws PathTraversalError on escape.
10
+ */
11
+ export declare function validateAndJoinPath(basePath: string, ...components: string[]): string;
12
+ //# sourceMappingURL=path-security.d.ts.map
@@ -0,0 +1,25 @@
1
+ import path from "bare-path";
2
+ import { PathTraversalError } from "../../utils/errors-server.js";
3
+ import { sanitizePathComponent, checkPathWithinBase, } from "../../utils/path-sanitize.js";
4
+ // Re-export sanitizePathComponent unchanged
5
+ export { sanitizePathComponent } from "../../utils/path-sanitize.js";
6
+ /**
7
+ * Check whether a target path is contained within a base directory.
8
+ * Both paths are resolved to absolute before comparison.
9
+ */
10
+ export function isPathWithinBase(basePath, targetPath) {
11
+ return checkPathWithinBase(basePath, targetPath, path.resolve, path.sep || "/");
12
+ }
13
+ /**
14
+ * Sanitize components, join them to a base path, and verify the result
15
+ * stays within the base directory. Throws PathTraversalError on escape.
16
+ */
17
+ export function validateAndJoinPath(basePath, ...components) {
18
+ const sanitized = components.map((c) => sanitizePathComponent(c));
19
+ const joined = path.join(basePath, ...sanitized);
20
+ const resolved = path.resolve(joined);
21
+ if (!isPathWithinBase(basePath, resolved)) {
22
+ throw new PathTraversalError(components.join("/"), basePath);
23
+ }
24
+ return resolved;
25
+ }