@qvac/sdk 0.6.1 → 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 (277) 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/registry-client.d.ts +5 -0
  169. package/dist/server/bare/registry/registry-client.js +37 -0
  170. package/dist/server/env.d.ts +22 -0
  171. package/dist/server/env.js +57 -0
  172. package/dist/server/plugins/index.d.ts +2 -0
  173. package/dist/server/plugins/index.js +1 -0
  174. package/dist/server/plugins/registry.d.ts +9 -0
  175. package/dist/server/plugins/registry.js +75 -0
  176. package/dist/server/rpc/delegate-transport.d.ts +11 -0
  177. package/dist/server/rpc/delegate-transport.js +60 -0
  178. package/dist/server/rpc/handle-request.d.ts +0 -9
  179. package/dist/server/rpc/handle-request.js +13 -321
  180. package/dist/server/rpc/handler-registry.d.ts +3 -0
  181. package/dist/server/rpc/handler-registry.js +84 -0
  182. package/dist/server/rpc/handler-utils.d.ts +22 -0
  183. package/dist/server/rpc/handler-utils.js +87 -0
  184. package/dist/server/rpc/handlers/cancelHandler.js +1 -1
  185. package/dist/server/rpc/handlers/completion-stream-delegated.js +1 -1
  186. package/dist/server/rpc/handlers/completion-stream.js +2 -43
  187. package/dist/server/rpc/handlers/delete-cache.js +1 -1
  188. package/dist/server/rpc/handlers/embed.js +2 -21
  189. package/dist/server/rpc/handlers/get-model-info.js +19 -15
  190. package/dist/server/rpc/handlers/index.d.ts +9 -0
  191. package/dist/server/rpc/handlers/index.js +9 -0
  192. package/dist/server/rpc/handlers/load-model/download-manager.d.ts +1 -0
  193. package/dist/server/rpc/handlers/load-model/download-manager.js +3 -0
  194. package/dist/server/rpc/handlers/load-model/handler.js +47 -24
  195. package/dist/server/rpc/handlers/load-model/http.js +3 -2
  196. package/dist/server/rpc/handlers/load-model/hyperdrive.js +14 -10
  197. package/dist/server/rpc/handlers/load-model/registry.d.ts +10 -0
  198. package/dist/server/rpc/handlers/load-model/registry.js +571 -0
  199. package/dist/server/rpc/handlers/load-model/resolve.js +18 -5
  200. package/dist/server/rpc/handlers/load-model-delegated.js +4 -2
  201. package/dist/server/rpc/handlers/ocr-stream.js +2 -37
  202. package/dist/server/rpc/handlers/ping.d.ts +3 -0
  203. package/dist/server/rpc/handlers/ping.js +3 -0
  204. package/dist/server/rpc/handlers/plugin-dispatch.d.ts +15 -0
  205. package/dist/server/rpc/handlers/plugin-dispatch.js +54 -0
  206. package/dist/server/rpc/handlers/plugin-invoke.d.ts +4 -0
  207. package/dist/server/rpc/handlers/plugin-invoke.js +87 -0
  208. package/dist/server/rpc/handlers/registry.d.ts +5 -0
  209. package/dist/server/rpc/handlers/registry.js +157 -0
  210. package/dist/server/rpc/handlers/text-to-speech.js +2 -5
  211. package/dist/server/rpc/handlers/transcribe-stream.js +2 -30
  212. package/dist/server/rpc/handlers/translate.js +2 -42
  213. package/dist/server/rpc/handlers/unload-model.js +1 -1
  214. package/dist/server/rpc/index.d.ts +1 -1
  215. package/dist/server/utils/archive.d.ts +1 -0
  216. package/dist/server/utils/archive.js +10 -2
  217. package/dist/server/utils/cache.d.ts +5 -0
  218. package/dist/server/utils/cache.js +26 -2
  219. package/dist/server/utils/gguf-tensor-extractor.js +3 -3
  220. package/dist/server/utils/index.d.ts +1 -1
  221. package/dist/server/utils/index.js +1 -1
  222. package/dist/server/utils/path-security.d.ts +12 -0
  223. package/dist/server/utils/path-security.js +25 -0
  224. package/dist/server/utils/shard-utils.js +3 -3
  225. package/dist/server/worker-core.d.ts +6 -0
  226. package/dist/server/worker-core.js +88 -0
  227. package/dist/server/worker.d.ts +4 -5
  228. package/dist/server/worker.js +18 -116
  229. package/dist/utils/errors-client.d.ts +9 -6
  230. package/dist/utils/errors-client.js +19 -11
  231. package/dist/utils/errors-server.d.ts +44 -5
  232. package/dist/utils/errors-server.js +82 -9
  233. package/dist/utils/path-sanitize.d.ts +17 -0
  234. package/dist/utils/path-sanitize.js +54 -0
  235. package/expo/plugins/patches/android-link.mjs +63 -0
  236. package/expo/plugins/patches/ios-link.mjs +63 -0
  237. package/package.json +72 -10
  238. package/dist/examples/text-to-speech.d.ts +0 -3
  239. package/dist/examples/text-to-speech.js +0 -137
  240. package/dist/models/hyperdrive/models.d.ts +0 -4003
  241. package/dist/models/hyperdrive/models.js +0 -4282
  242. package/dist/server/bare/addons/index.d.ts +0 -4
  243. package/dist/server/bare/addons/index.js +0 -3
  244. package/dist/server/bare/addons/llamacpp-completion/create-model.d.ts +0 -8
  245. package/dist/server/bare/addons/llamacpp-completion/create-model.js +0 -43
  246. package/dist/server/bare/addons/llamacpp-completion/index.d.ts +0 -4
  247. package/dist/server/bare/addons/llamacpp-completion/index.js +0 -3
  248. package/dist/server/bare/addons/llamacpp-embedding/create-model.d.ts +0 -8
  249. package/dist/server/bare/addons/llamacpp-embedding/create-model.js +0 -38
  250. package/dist/server/bare/addons/llamacpp-embedding/index.d.ts +0 -3
  251. package/dist/server/bare/addons/llamacpp-embedding/index.js +0 -2
  252. package/dist/server/bare/addons/nmtcpp-translation/create-model.d.ts +0 -8
  253. package/dist/server/bare/addons/nmtcpp-translation/create-model.js +0 -52
  254. package/dist/server/bare/addons/nmtcpp-translation/index.d.ts +0 -3
  255. package/dist/server/bare/addons/nmtcpp-translation/index.js +0 -2
  256. package/dist/server/bare/addons/onnx-ocr/create-model.d.ts +0 -10
  257. package/dist/server/bare/addons/onnx-ocr/index.d.ts +0 -3
  258. package/dist/server/bare/addons/onnx-ocr/index.js +0 -2
  259. package/dist/server/bare/addons/onnx-tts/create-model.d.ts +0 -10
  260. package/dist/server/bare/addons/onnx-tts/create-model.js +0 -25
  261. package/dist/server/bare/addons/onnx-tts/index.d.ts +0 -3
  262. package/dist/server/bare/addons/onnx-tts/index.js +0 -2
  263. package/dist/server/bare/addons/whispercpp-transcription/create-model.d.ts +0 -10
  264. package/dist/server/bare/addons/whispercpp-transcription/create-model.js +0 -37
  265. package/dist/server/bare/addons/whispercpp-transcription/handlers/transcribe-stream.d.ts +0 -3
  266. package/dist/server/bare/addons/whispercpp-transcription/index.d.ts +0 -3
  267. package/dist/server/bare/addons/whispercpp-transcription/index.js +0 -2
  268. /package/dist/models/{hyperdrive → registry}/index.d.ts +0 -0
  269. /package/dist/server/bare/{addons → ops}/cancel.d.ts +0 -0
  270. /package/dist/server/bare/{addons → ops}/cancel.js +0 -0
  271. /package/dist/server/bare/{addons/llamacpp-completion → ops}/kv-cache-utils.d.ts +0 -0
  272. /package/dist/server/bare/{addons → ops}/load-model.d.ts +0 -0
  273. /package/dist/server/bare/{addons → ops}/unload-model.d.ts +0 -0
  274. /package/dist/server/bare/{addons → ops}/unload-model.js +0 -0
  275. /package/dist/server/bare/{addons/llamacpp-completion → plugins/llamacpp-completion/ops}/cache-logger.d.ts +0 -0
  276. /package/dist/server/bare/{addons/onnx-ocr/handlers → plugins/onnx-ocr/ops}/ocr-stream.d.ts +0 -0
  277. /package/dist/server/bare/{addons/onnx-tts/handlers → plugins/onnx-tts/ops}/text-to-speech.d.ts +0 -0
@@ -0,0 +1,433 @@
1
+ /**
2
+ * QVAC Pear Pre-Hook
3
+ *
4
+ * Invoked by Pear before `pear run` or `pear stage` to:
5
+ * 1. Generate `qvac/worker.pear.entry.mjs` with selected plugins
6
+ * 2. Persist `pear.stage.entrypoints` to package.json for routing
7
+ *
8
+ * @example package.json configuration
9
+ * ```json
10
+ * {
11
+ * "pear": {
12
+ * "pre": ["@qvac/sdk/pear-pre"]
13
+ * }
14
+ * }
15
+ * ```
16
+ */
17
+ import * as cenc from "compact-encoding";
18
+ import pearPipe from "pear-pipe";
19
+ import fs from "bare-fs";
20
+ import path from "bare-path";
21
+ const CONFIG_CANDIDATES = [
22
+ "qvac.config.json",
23
+ "qvac.config.js",
24
+ "qvac.config.mjs",
25
+ "qvac.config.ts",
26
+ ];
27
+ const BUILTIN_PLUGINS = [
28
+ "@qvac/sdk/llamacpp-completion/plugin",
29
+ "@qvac/sdk/llamacpp-embedding/plugin",
30
+ "@qvac/sdk/whispercpp-transcription/plugin",
31
+ "@qvac/sdk/nmtcpp-translation/plugin",
32
+ "@qvac/sdk/onnx-tts/plugin",
33
+ "@qvac/sdk/onnx-ocr/plugin",
34
+ ];
35
+ const BUILTIN_PLUGIN_EXPORTS = {
36
+ "llamacpp-completion": "llmPlugin",
37
+ "llamacpp-embedding": "embeddingsPlugin",
38
+ "whispercpp-transcription": "whisperPlugin",
39
+ "nmtcpp-translation": "nmtPlugin",
40
+ "onnx-tts": "ttsPlugin",
41
+ "onnx-ocr": "ocrPlugin",
42
+ };
43
+ const SDK_NAME = "@qvac/sdk";
44
+ const DEFAULT_PEAR_WORKER = "worker.js";
45
+ const PEAR_WORKER_ENTRY_PATH = "qvac/worker.pear.entry.mjs";
46
+ const LOG_PREFIX = "[qvac/pear-pre]";
47
+ /** Normalize path to Pear config format (POSIX-style, leading slash) */
48
+ function toPearConfigPath(pathFromAppRoot) {
49
+ let p = String(pathFromAppRoot).replaceAll("\\", "/");
50
+ if (p.startsWith("./"))
51
+ p = p.slice(2);
52
+ if (!p.startsWith("/"))
53
+ p = `/${p}`;
54
+ return p.replaceAll("/./", "/").replace(/\/{2,}/g, "/");
55
+ }
56
+ function toPosixPath(p) {
57
+ return String(p).replaceAll("\\", "/");
58
+ }
59
+ function pathToFileUrl(absPath) {
60
+ let p = toPosixPath(absPath);
61
+ // Windows drive letter paths need a leading slash in file:// URLs.
62
+ if (/^[A-Za-z]:\//.test(p))
63
+ p = `/${p}`;
64
+ if (!p.startsWith("/"))
65
+ p = `/${p}`;
66
+ const segments = p.split("/");
67
+ const encoded = segments
68
+ .map((seg, idx) => {
69
+ if (idx === 0)
70
+ return "";
71
+ // Preserve "C:" drive segment in Windows file URLs (file:///C:/...)
72
+ if (idx === 1 && /^[A-Za-z]:$/.test(seg))
73
+ return seg;
74
+ return encodeURIComponent(seg);
75
+ })
76
+ .join("/");
77
+ return `file://${encoded}`;
78
+ }
79
+ function detectJsonIndent(raw) {
80
+ const match = raw.match(/\n([ \t]+)"/);
81
+ if (match?.[1])
82
+ return match[1];
83
+ return 2;
84
+ }
85
+ function detectJsonNewline(raw) {
86
+ return raw.includes("\r\n") ? "\r\n" : "\n";
87
+ }
88
+ function ensureObjectProp(obj, prop) {
89
+ const existing = obj[prop];
90
+ if (existing === undefined) {
91
+ const created = {};
92
+ obj[prop] = created;
93
+ return created;
94
+ }
95
+ if (typeof existing === "object" && existing && !Array.isArray(existing)) {
96
+ return existing;
97
+ }
98
+ throw new Error(`package.json "${prop}" must be an object`);
99
+ }
100
+ function normalizeStringArray(value) {
101
+ if (value === undefined || value === null)
102
+ return [];
103
+ if (typeof value === "string")
104
+ return value.length ? [value] : [];
105
+ if (Array.isArray(value))
106
+ return uniqStrings(value);
107
+ throw new Error("Expected an array of strings");
108
+ }
109
+ function atomicWriteFileSync(targetPath, content) {
110
+ const tmpPath = `${targetPath}.qvac-pre.tmp`;
111
+ fs.writeFileSync(tmpPath, content, "utf8");
112
+ fs.renameSync(tmpPath, targetPath);
113
+ }
114
+ function normalizePearWorkerPath(value) {
115
+ if (typeof value !== "string")
116
+ return DEFAULT_PEAR_WORKER;
117
+ let p = value.trim();
118
+ if (!p)
119
+ return DEFAULT_PEAR_WORKER;
120
+ p = toPosixPath(p);
121
+ if (/^[A-Za-z]:\//.test(p)) {
122
+ throw new Error(`"pearWorker" must be a path inside the app root (got absolute path: ${JSON.stringify(value)})`);
123
+ }
124
+ if (p.startsWith("./"))
125
+ p = p.slice(2);
126
+ while (p.startsWith("/"))
127
+ p = p.slice(1);
128
+ if (!p)
129
+ return DEFAULT_PEAR_WORKER;
130
+ if (p === "." || p === ".." || p.startsWith("../") || p.includes("/../")) {
131
+ throw new Error(`"pearWorker" must be a path inside the app root (got: ${JSON.stringify(value)})`);
132
+ }
133
+ return p;
134
+ }
135
+ /**
136
+ * Converts an absolute path to a relative ESM import specifier, using POSIX separators.
137
+ */
138
+ function toRelativeImportSpecifier(fromDir, targetPath) {
139
+ let rel = path.relative(fromDir, targetPath);
140
+ rel = toPosixPath(rel);
141
+ if (!rel.startsWith("."))
142
+ rel = `./${rel}`;
143
+ return rel;
144
+ }
145
+ /** Extract unique strings from an array, preserving order */
146
+ function uniqStrings(values) {
147
+ if (!Array.isArray(values))
148
+ return [];
149
+ const out = [];
150
+ const seen = new Set();
151
+ for (const v of values) {
152
+ if (typeof v === "string" && !seen.has(v)) {
153
+ seen.add(v);
154
+ out.push(v);
155
+ }
156
+ }
157
+ return out;
158
+ }
159
+ /** Ensure array contains required values, returning new array and change flag */
160
+ function ensureArrayIncludes(existing, required) {
161
+ const next = [...existing];
162
+ const set = new Set(existing);
163
+ let changed = false;
164
+ for (const v of required) {
165
+ if (!set.has(v)) {
166
+ set.add(v);
167
+ next.push(v);
168
+ changed = true;
169
+ }
170
+ }
171
+ return { next, changed };
172
+ }
173
+ /**
174
+ * Persist pear.stage.entrypoints to package.json.
175
+ *
176
+ * Required because workers are spawned with --no-pre, bypassing dynamic config.
177
+ * pear-state merges stage.entrypoints into unrouted automatically.
178
+ */
179
+ function persistStageEntrypointsToPackageJson(appRoot, requiredEntrypoints) {
180
+ const packageJsonPath = path.join(appRoot, "package.json");
181
+ if (!fs.existsSync(packageJsonPath)) {
182
+ throw new Error(`package.json not found at ${packageJsonPath}`);
183
+ }
184
+ const raw = fs.readFileSync(packageJsonPath, "utf8");
185
+ const newline = detectJsonNewline(raw);
186
+ const indent = detectJsonIndent(raw);
187
+ const hasFinalNewline = raw.endsWith("\n") || raw.endsWith("\r\n");
188
+ let pkg;
189
+ try {
190
+ pkg = JSON.parse(raw);
191
+ }
192
+ catch (err) {
193
+ throw new Error(`Failed to parse ${packageJsonPath}: ${String(err)}`);
194
+ }
195
+ const pear = ensureObjectProp(pkg, "pear");
196
+ const stage = ensureObjectProp(pear, "stage");
197
+ const existing = normalizeStringArray(stage["entrypoints"]);
198
+ const required = uniqStrings(requiredEntrypoints);
199
+ const { next, changed } = ensureArrayIncludes(existing, required);
200
+ if (!changed)
201
+ return;
202
+ stage["entrypoints"] = next;
203
+ let nextRaw = JSON.stringify(pkg, null, indent);
204
+ if (newline === "\r\n")
205
+ nextRaw = nextRaw.replaceAll("\n", "\r\n");
206
+ if (hasFinalNewline)
207
+ nextRaw += newline;
208
+ atomicWriteFileSync(packageJsonPath, nextRaw);
209
+ }
210
+ /** Extract app root directory from Pear's applink */
211
+ function getAppRoot() {
212
+ const applink = Pear.config?.applink;
213
+ if (typeof applink !== "string" || applink.length === 0) {
214
+ throw new Error("Pear.config.applink is not available");
215
+ }
216
+ // Pear provides a file:// URL to the application root.
217
+ let pathname;
218
+ let url;
219
+ try {
220
+ url = new URL(applink);
221
+ }
222
+ catch {
223
+ // Some environments may provide a plain filesystem path instead of a URL.
224
+ return path.normalize(applink);
225
+ }
226
+ if (url.protocol !== "file:") {
227
+ throw new Error(`Expected Pear.config.applink to be a file:// URL (got: ${applink})`);
228
+ }
229
+ pathname = url.pathname;
230
+ try {
231
+ pathname = decodeURIComponent(pathname);
232
+ }
233
+ catch {
234
+ // If decoding fails, keep the raw pathname.
235
+ }
236
+ // Handle Windows paths (e.g., /C:/path)
237
+ if (pathname[0] === "/" && pathname[2] === ":") {
238
+ pathname = pathname.slice(1);
239
+ }
240
+ return path.normalize(pathname);
241
+ }
242
+ /** Find the first existing qvac config file */
243
+ function findConfigFile(appRoot) {
244
+ for (const candidate of CONFIG_CANDIDATES) {
245
+ const configPath = path.join(appRoot, candidate);
246
+ if (fs.existsSync(configPath)) {
247
+ return configPath;
248
+ }
249
+ }
250
+ return null;
251
+ }
252
+ /** Load and parse a qvac config file */
253
+ async function loadConfig(configPath) {
254
+ const ext = path.extname(configPath).toLowerCase();
255
+ if (ext === ".json") {
256
+ const content = fs.readFileSync(configPath, "utf8");
257
+ try {
258
+ return JSON.parse(content);
259
+ }
260
+ catch (err) {
261
+ throw new Error(`Failed to parse ${configPath}: ${String(err)}`);
262
+ }
263
+ }
264
+ if (ext === ".js" || ext === ".mjs") {
265
+ const module = (await import(pathToFileUrl(configPath)));
266
+ return module.default ?? module;
267
+ }
268
+ if (ext === ".ts") {
269
+ throw new Error("TypeScript config not supported in Pear pre-hook. Use qvac.config.json or qvac.config.mjs.");
270
+ }
271
+ throw new Error(`Unsupported config format: ${ext}. Use .json, .js, or .mjs`);
272
+ }
273
+ /** Discover and load qvac config, returning null if not found */
274
+ async function discoverConfig(appRoot) {
275
+ const configPath = findConfigFile(appRoot);
276
+ if (!configPath) {
277
+ return { config: null, configFile: null };
278
+ }
279
+ const config = await loadConfig(configPath);
280
+ return { config, configFile: path.basename(configPath) };
281
+ }
282
+ /** Resolve plugin set from config or default to all built-in plugins */
283
+ function resolvePlugins(config) {
284
+ const fromConfig = uniqStrings(config?.plugins);
285
+ if (fromConfig.length)
286
+ return fromConfig;
287
+ return [...BUILTIN_PLUGINS];
288
+ }
289
+ /** Parse SDK builtin plugin specifier to extract export info */
290
+ function parseBuiltinSpecifier(specifier) {
291
+ const prefix = `${SDK_NAME}/`;
292
+ const pluginSuffix = "/plugin";
293
+ if (specifier.startsWith(prefix) && specifier.endsWith(pluginSuffix)) {
294
+ const middle = specifier.slice(prefix.length, -pluginSuffix.length);
295
+ const exportName = BUILTIN_PLUGIN_EXPORTS[middle];
296
+ if (!middle.includes("/") && exportName) {
297
+ return { suffix: middle, exportName };
298
+ }
299
+ }
300
+ return null;
301
+ }
302
+ /** Generate the Pear worker entry file content */
303
+ function generatePearWorkerEntry(plugins, appWorkerPath) {
304
+ const imports = [];
305
+ const registrations = [];
306
+ let varIndex = 0;
307
+ for (const specifier of plugins) {
308
+ const builtin = parseBuiltinSpecifier(specifier);
309
+ if (builtin) {
310
+ imports.push(`import { ${builtin.exportName} } from "${SDK_NAME}/${builtin.suffix}/plugin";`);
311
+ registrations.push(`registerPlugin(${builtin.exportName});`);
312
+ }
313
+ else {
314
+ const varName = `customPlugin${varIndex++}`;
315
+ imports.push(`import ${varName} from "${specifier}";`);
316
+ registrations.push(`registerPlugin(${varName});`);
317
+ }
318
+ }
319
+ const pluginsList = plugins.map((p) => `* - ${p}`).join("\n");
320
+ return `/**
321
+ * QVAC Pear Worker Entry (auto-generated)
322
+ * Plugins: ${plugins.length}
323
+ *
324
+ ${pluginsList}
325
+ */
326
+
327
+ import { registerPlugin } from "${SDK_NAME}/plugins";
328
+
329
+ ${imports.join("\n")}
330
+
331
+ ${registrations.join("\n")}
332
+
333
+ await import(${JSON.stringify(appWorkerPath)});
334
+ `;
335
+ }
336
+ /** Write worker entry file if content changed (idempotent) */
337
+ function writeWorkerEntryIfChanged(appRoot, content) {
338
+ const outputDir = path.join(appRoot, "qvac");
339
+ const entryPath = path.join(appRoot, PEAR_WORKER_ENTRY_PATH);
340
+ if (fs.existsSync(entryPath)) {
341
+ try {
342
+ if (fs.readFileSync(entryPath, "utf8") === content) {
343
+ return false;
344
+ }
345
+ }
346
+ catch {
347
+ // Will rewrite on error
348
+ }
349
+ }
350
+ if (!fs.existsSync(outputDir)) {
351
+ fs.mkdirSync(outputDir, { recursive: true });
352
+ }
353
+ atomicWriteFileSync(entryPath, content);
354
+ return true;
355
+ }
356
+ /** Main configuration handler */
357
+ async function configure(options) {
358
+ const appRoot = getAppRoot();
359
+ const { config: qvacConfig } = await discoverConfig(appRoot);
360
+ const plugins = resolvePlugins(qvacConfig);
361
+ const pearWorker = normalizePearWorkerPath(qvacConfig?.pearWorker);
362
+ // Validate worker file exists
363
+ const workerAbs = path.join(appRoot, pearWorker);
364
+ if (!fs.existsSync(workerAbs)) {
365
+ const hint = qvacConfig?.pearWorker
366
+ ? `"${String(qvacConfig.pearWorker)}" (resolved to ${workerAbs})`
367
+ : `"${DEFAULT_PEAR_WORKER}". Set "pearWorker" in qvac.config.json to specify your worker path.`;
368
+ throw new Error(`Worker file not found: ${hint}`);
369
+ }
370
+ // Generate worker entry with relative import path from qvac/ directory
371
+ const outputDir = path.join(appRoot, "qvac");
372
+ const appWorkerImport = toRelativeImportSpecifier(outputDir, workerAbs);
373
+ const workerEntryContent = generatePearWorkerEntry(plugins, appWorkerImport);
374
+ writeWorkerEntryIfChanged(appRoot, workerEntryContent);
375
+ // Persist entrypoint to package.json for worker routing
376
+ const workerEntryPath = toPearConfigPath(PEAR_WORKER_ENTRY_PATH);
377
+ persistStageEntrypointsToPackageJson(appRoot, [workerEntryPath]);
378
+ return options;
379
+ }
380
+ // IPC Protocol
381
+ const pipe = pearPipe();
382
+ if (!pipe) {
383
+ console.error(`${LOG_PREFIX} No IPC pipe available`);
384
+ process.exit(1);
385
+ }
386
+ pipe.autoexit = true;
387
+ let exitCode = 0;
388
+ pipe.on("end", () => {
389
+ try {
390
+ Pear.pipe.end();
391
+ }
392
+ finally {
393
+ if (exitCode !== 0)
394
+ process.exit(exitCode);
395
+ }
396
+ });
397
+ pipe.once("error", (err) => {
398
+ exitCode = exitCode || 1;
399
+ console.error(LOG_PREFIX, err);
400
+ });
401
+ pipe.once("data", (data) => {
402
+ void (async () => {
403
+ let options = null;
404
+ try {
405
+ options = cenc.decode(cenc.any, data);
406
+ }
407
+ catch (err) {
408
+ exitCode = 1;
409
+ console.error(LOG_PREFIX, err);
410
+ try {
411
+ pipe.end(cenc.encode(cenc.any, { tag: "configure", data: {} }));
412
+ }
413
+ catch {
414
+ pipe.destroy(err);
415
+ }
416
+ return;
417
+ }
418
+ try {
419
+ const config = await configure(options);
420
+ pipe.end(cenc.encode(cenc.any, { tag: "configure", data: config }));
421
+ }
422
+ catch (err) {
423
+ exitCode = 1;
424
+ console.error(LOG_PREFIX, err);
425
+ try {
426
+ pipe.end(cenc.encode(cenc.any, { tag: "configure", data: options }));
427
+ }
428
+ catch {
429
+ pipe.destroy(err);
430
+ }
431
+ }
432
+ })();
433
+ });