veryfront 0.1.1122 → 0.1.1124

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 (205) hide show
  1. package/esm/deno.js +1 -1
  2. package/esm/extensions/ext-llm-openai/src/openai-responses-request-builder.d.ts.map +1 -1
  3. package/esm/extensions/ext-llm-openai/src/openai-responses-request-builder.js +5 -0
  4. package/esm/src/agent/hosted/child-lifecycle.d.ts.map +1 -1
  5. package/esm/src/agent/hosted/child-lifecycle.js +1 -12
  6. package/esm/src/agent/hosted/cloud-agent-chat-execution.d.ts +38 -0
  7. package/esm/src/agent/hosted/cloud-agent-chat-execution.d.ts.map +1 -0
  8. package/esm/src/agent/hosted/cloud-agent-chat-execution.js +349 -0
  9. package/esm/src/agent/hosted/cloud-agent-child-tools.d.ts +80 -0
  10. package/esm/src/agent/hosted/cloud-agent-child-tools.d.ts.map +1 -0
  11. package/esm/src/agent/hosted/cloud-agent-child-tools.js +280 -0
  12. package/esm/src/agent/hosted/cloud-agent-config.d.ts +69 -0
  13. package/esm/src/agent/hosted/cloud-agent-config.d.ts.map +1 -0
  14. package/esm/src/agent/hosted/cloud-agent-config.js +129 -0
  15. package/esm/src/agent/hosted/cloud-agent-paths.d.ts +42 -0
  16. package/esm/src/agent/hosted/cloud-agent-paths.d.ts.map +1 -0
  17. package/esm/src/agent/hosted/cloud-agent-paths.js +117 -0
  18. package/esm/src/agent/hosted/cloud-agent-provider-bootstrap.d.ts +49 -0
  19. package/esm/src/agent/hosted/cloud-agent-provider-bootstrap.d.ts.map +1 -0
  20. package/esm/src/agent/hosted/cloud-agent-provider-bootstrap.js +96 -0
  21. package/esm/src/agent/hosted/durable-child-fork-execution.d.ts.map +1 -1
  22. package/esm/src/agent/hosted/durable-child-fork-execution.js +1 -12
  23. package/esm/src/agent/hosted/runtime-request-config.js +2 -2
  24. package/esm/src/agent/hosted/stream-finalization.d.ts.map +1 -1
  25. package/esm/src/agent/hosted/stream-finalization.js +2 -26
  26. package/esm/src/agent/hosted/veryfront-cloud-agent-service.d.ts +11 -93
  27. package/esm/src/agent/hosted/veryfront-cloud-agent-service.d.ts.map +1 -1
  28. package/esm/src/agent/hosted/veryfront-cloud-agent-service.js +8 -901
  29. package/esm/src/agent/project/agent-runtime.d.ts.map +1 -1
  30. package/esm/src/agent/project/agent-runtime.js +3 -0
  31. package/esm/src/agent/runtime/chat-stream-handler.d.ts.map +1 -1
  32. package/esm/src/agent/runtime/chat-stream-handler.js +1 -28
  33. package/esm/src/agent/streaming/stream-outcome.d.ts +30 -0
  34. package/esm/src/agent/streaming/stream-outcome.d.ts.map +1 -0
  35. package/esm/src/agent/streaming/stream-outcome.js +62 -0
  36. package/esm/src/agent/types.d.ts +3 -0
  37. package/esm/src/agent/types.d.ts.map +1 -1
  38. package/esm/src/chat/message-prep.d.ts.map +1 -1
  39. package/esm/src/chat/message-prep.js +3 -6
  40. package/esm/src/chat/provider-errors.d.ts +3 -10
  41. package/esm/src/chat/provider-errors.d.ts.map +1 -1
  42. package/esm/src/chat/provider-errors.js +2 -9
  43. package/esm/src/embedding/veryfront-cloud/rag-store.d.ts.map +1 -1
  44. package/esm/src/embedding/veryfront-cloud/rag-store.js +5 -2
  45. package/esm/src/eval/studio.d.ts +1 -1
  46. package/esm/src/html/styles-builder/plugin-loader.d.ts.map +1 -1
  47. package/esm/src/html/styles-builder/plugin-loader.js +4 -2
  48. package/esm/src/modules/server/classify.d.ts +54 -0
  49. package/esm/src/modules/server/classify.d.ts.map +1 -0
  50. package/esm/src/modules/server/classify.js +50 -0
  51. package/esm/src/modules/server/fs-probe.d.ts +14 -0
  52. package/esm/src/modules/server/fs-probe.d.ts.map +1 -0
  53. package/esm/src/modules/server/fs-probe.js +17 -0
  54. package/esm/src/modules/server/module-batch-handler.d.ts.map +1 -1
  55. package/esm/src/modules/server/module-batch-handler.js +31 -45
  56. package/esm/src/modules/server/module-server.d.ts.map +1 -1
  57. package/esm/src/modules/server/module-server.js +77 -93
  58. package/esm/src/modules/server/module-transform.d.ts +73 -0
  59. package/esm/src/modules/server/module-transform.d.ts.map +1 -0
  60. package/esm/src/modules/server/module-transform.js +64 -0
  61. package/esm/src/modules/server/ssr-import-rewriter.d.ts +1 -1
  62. package/esm/src/modules/server/ssr-import-rewriter.d.ts.map +1 -1
  63. package/esm/src/observability/error-collector.d.ts.map +1 -1
  64. package/esm/src/observability/error-collector.js +4 -11
  65. package/esm/src/observability/log-buffer.d.ts.map +1 -1
  66. package/esm/src/observability/log-buffer.js +4 -11
  67. package/esm/src/observability/request-profiler.d.ts +4 -0
  68. package/esm/src/observability/request-profiler.d.ts.map +1 -1
  69. package/esm/src/observability/request-profiler.js +9 -4
  70. package/esm/src/platform/adapters/veryfront-api-client/operations.d.ts +3 -3
  71. package/esm/src/platform/adapters/veryfront-api-client/operations.d.ts.map +1 -1
  72. package/esm/src/platform/adapters/veryfront-api-client/operations.js +4 -4
  73. package/esm/src/platform/adapters/veryfront-api-client/retry-handler.d.ts +4 -15
  74. package/esm/src/platform/adapters/veryfront-api-client/retry-handler.d.ts.map +1 -1
  75. package/esm/src/platform/adapters/veryfront-api-client/retry-handler.js +6 -110
  76. package/esm/src/platform/adapters/veryfront-api-transport.d.ts +40 -0
  77. package/esm/src/platform/adapters/veryfront-api-transport.d.ts.map +1 -0
  78. package/esm/src/platform/adapters/veryfront-api-transport.js +142 -0
  79. package/esm/src/provider/runtime-loader/provider-http.d.ts.map +1 -1
  80. package/esm/src/provider/runtime-loader/provider-http.js +5 -1
  81. package/esm/src/proxy/server-timing.d.ts.map +1 -1
  82. package/esm/src/proxy/server-timing.js +2 -14
  83. package/esm/src/react/components/chat/agent-card.d.ts +2 -3
  84. package/esm/src/react/components/chat/agent-card.d.ts.map +1 -1
  85. package/esm/src/react/components/chat/agent-card.js +3 -12
  86. package/esm/src/react/components/chat/agent-picker-context.d.ts +2 -3
  87. package/esm/src/react/components/chat/agent-picker-context.d.ts.map +1 -1
  88. package/esm/src/react/components/chat/agent-picker-context.js +3 -13
  89. package/esm/src/react/components/chat/chat/components/attachment-pill.d.ts +2 -6
  90. package/esm/src/react/components/chat/chat/components/attachment-pill.d.ts.map +1 -1
  91. package/esm/src/react/components/chat/chat/components/attachment-pill.js +3 -15
  92. package/esm/src/react/components/chat/chat/components/attachments-panel.d.ts +2 -12
  93. package/esm/src/react/components/chat/chat/components/attachments-panel.d.ts.map +1 -1
  94. package/esm/src/react/components/chat/chat/components/attachments-panel.js +3 -21
  95. package/esm/src/react/components/chat/chat/components/branch-picker.d.ts.map +1 -1
  96. package/esm/src/react/components/chat/chat/components/branch-picker.js +2 -11
  97. package/esm/src/react/components/chat/chat/components/inline-citation.d.ts.map +1 -1
  98. package/esm/src/react/components/chat/chat/components/inline-citation.js +2 -11
  99. package/esm/src/react/components/chat/chat/components/message-actions.d.ts.map +1 -1
  100. package/esm/src/react/components/chat/chat/components/message-actions.js +2 -11
  101. package/esm/src/react/components/chat/chat/components/message-feedback.d.ts.map +1 -1
  102. package/esm/src/react/components/chat/chat/components/message-feedback.js +2 -11
  103. package/esm/src/react/components/chat/chat/components/reasoning.d.ts +2 -3
  104. package/esm/src/react/components/chat/chat/components/reasoning.d.ts.map +1 -1
  105. package/esm/src/react/components/chat/chat/components/reasoning.js +3 -12
  106. package/esm/src/react/components/chat/chat/components/sidebar.d.ts +2 -3
  107. package/esm/src/react/components/chat/chat/components/sidebar.d.ts.map +1 -1
  108. package/esm/src/react/components/chat/chat/components/sidebar.js +4 -22
  109. package/esm/src/react/components/chat/chat/components/sources.d.ts +2 -3
  110. package/esm/src/react/components/chat/chat/components/sources.d.ts.map +1 -1
  111. package/esm/src/react/components/chat/chat/components/sources.js +3 -12
  112. package/esm/src/react/components/chat/chat/components/step-indicator.d.ts +2 -6
  113. package/esm/src/react/components/chat/chat/components/step-indicator.d.ts.map +1 -1
  114. package/esm/src/react/components/chat/chat/components/step-indicator.js +3 -15
  115. package/esm/src/react/components/chat/chat/components/tool-ui.d.ts +2 -3
  116. package/esm/src/react/components/chat/chat/components/tool-ui.d.ts.map +1 -1
  117. package/esm/src/react/components/chat/chat/components/tool-ui.js +3 -12
  118. package/esm/src/react/components/chat/chat/composition/chat-message-list.d.ts.map +1 -1
  119. package/esm/src/react/components/chat/chat/composition/chat-message-list.js +2 -11
  120. package/esm/src/react/components/chat/chat/contexts/chat-context.d.ts +2 -2
  121. package/esm/src/react/components/chat/chat/contexts/chat-context.d.ts.map +1 -1
  122. package/esm/src/react/components/chat/chat/contexts/chat-context.js +3 -12
  123. package/esm/src/react/components/chat/chat/contexts/composer-context.d.ts +2 -2
  124. package/esm/src/react/components/chat/chat/contexts/composer-context.d.ts.map +1 -1
  125. package/esm/src/react/components/chat/chat/contexts/composer-context.js +3 -12
  126. package/esm/src/react/components/chat/chat/contexts/conversations-context.d.ts +2 -2
  127. package/esm/src/react/components/chat/chat/contexts/conversations-context.d.ts.map +1 -1
  128. package/esm/src/react/components/chat/chat/contexts/conversations-context.js +3 -12
  129. package/esm/src/react/components/chat/chat/contexts/message-context.d.ts +2 -2
  130. package/esm/src/react/components/chat/chat/contexts/message-context.d.ts.map +1 -1
  131. package/esm/src/react/components/chat/chat/contexts/message-context.js +3 -12
  132. package/esm/src/react/components/chat/chat-actions.d.ts +2 -6
  133. package/esm/src/react/components/chat/chat-actions.d.ts.map +1 -1
  134. package/esm/src/react/components/chat/chat-actions.js +3 -15
  135. package/esm/src/react/components/chat/model-selector.d.ts +2 -6
  136. package/esm/src/react/components/chat/model-selector.d.ts.map +1 -1
  137. package/esm/src/react/components/chat/model-selector.js +3 -15
  138. package/esm/src/react/components/create-strict-context.d.ts +44 -0
  139. package/esm/src/react/components/create-strict-context.d.ts.map +1 -0
  140. package/esm/src/react/components/create-strict-context.js +56 -0
  141. package/esm/src/react/components/ui/anchored-surface.d.ts +44 -0
  142. package/esm/src/react/components/ui/anchored-surface.d.ts.map +1 -0
  143. package/esm/src/react/components/ui/anchored-surface.js +58 -0
  144. package/esm/src/react/components/ui/app-shell.d.ts +2 -2
  145. package/esm/src/react/components/ui/app-shell.d.ts.map +1 -1
  146. package/esm/src/react/components/ui/app-shell.js +3 -7
  147. package/esm/src/react/components/ui/collapsible.d.ts.map +1 -1
  148. package/esm/src/react/components/ui/collapsible.js +3 -9
  149. package/esm/src/react/components/ui/color-mode.d.ts +2 -2
  150. package/esm/src/react/components/ui/color-mode.d.ts.map +1 -1
  151. package/esm/src/react/components/ui/color-mode.js +3 -11
  152. package/esm/src/react/components/ui/command.d.ts.map +1 -1
  153. package/esm/src/react/components/ui/command.js +2 -7
  154. package/esm/src/react/components/ui/dialog.d.ts +4 -7
  155. package/esm/src/react/components/ui/dialog.d.ts.map +1 -1
  156. package/esm/src/react/components/ui/dialog.js +13 -59
  157. package/esm/src/react/components/ui/disclosure.d.ts +12 -0
  158. package/esm/src/react/components/ui/disclosure.d.ts.map +1 -0
  159. package/esm/src/react/components/ui/disclosure.js +22 -0
  160. package/esm/src/react/components/ui/drawer.d.ts +5 -7
  161. package/esm/src/react/components/ui/drawer.d.ts.map +1 -1
  162. package/esm/src/react/components/ui/drawer.js +13 -57
  163. package/esm/src/react/components/ui/dropdown-menu.d.ts +4 -7
  164. package/esm/src/react/components/ui/dropdown-menu.d.ts.map +1 -1
  165. package/esm/src/react/components/ui/dropdown-menu.js +12 -31
  166. package/esm/src/react/components/ui/modal-surface.d.ts +43 -0
  167. package/esm/src/react/components/ui/modal-surface.d.ts.map +1 -0
  168. package/esm/src/react/components/ui/modal-surface.js +83 -0
  169. package/esm/src/react/components/ui/popover.d.ts +4 -7
  170. package/esm/src/react/components/ui/popover.d.ts.map +1 -1
  171. package/esm/src/react/components/ui/popover.js +11 -31
  172. package/esm/src/react/components/ui/select.d.ts.map +1 -1
  173. package/esm/src/react/components/ui/select.js +2 -7
  174. package/esm/src/react/components/ui/tabs.d.ts.map +1 -1
  175. package/esm/src/react/components/ui/tabs.js +3 -4
  176. package/esm/src/runs/schemas.d.ts +9 -9
  177. package/esm/src/server/handlers/dev/framework-candidates.generated.d.ts.map +1 -1
  178. package/esm/src/server/handlers/dev/framework-candidates.generated.js +253 -154
  179. package/esm/src/server/reload-notifier.d.ts +0 -1
  180. package/esm/src/server/reload-notifier.d.ts.map +1 -1
  181. package/esm/src/server/reload-notifier.js +7 -25
  182. package/esm/src/server/services/rsc/endpoints/rsc-bundles.generated.d.ts.map +1 -1
  183. package/esm/src/server/services/rsc/endpoints/rsc-bundles.generated.js +1 -1
  184. package/esm/src/utils/base64url.d.ts +9 -1
  185. package/esm/src/utils/base64url.d.ts.map +1 -1
  186. package/esm/src/utils/base64url.js +25 -11
  187. package/esm/src/utils/index.d.ts +3 -1
  188. package/esm/src/utils/index.d.ts.map +1 -1
  189. package/esm/src/utils/index.js +3 -1
  190. package/esm/src/utils/json.d.ts +19 -0
  191. package/esm/src/utils/json.d.ts.map +1 -0
  192. package/esm/src/utils/json.js +17 -0
  193. package/esm/src/utils/logger/redact.d.ts.map +1 -1
  194. package/esm/src/utils/logger/redact.js +7 -3
  195. package/esm/src/utils/subscriber-set.d.ts +19 -0
  196. package/esm/src/utils/subscriber-set.d.ts.map +1 -0
  197. package/esm/src/utils/subscriber-set.js +38 -0
  198. package/esm/src/utils/version-constant.d.ts +1 -1
  199. package/esm/src/utils/version-constant.js +1 -1
  200. package/esm/src/workflow/backends/redis/index.d.ts.map +1 -1
  201. package/esm/src/workflow/backends/redis/index.js +6 -7
  202. package/esm/src/workflow/claude-code/websocket-publisher.d.ts.map +1 -1
  203. package/esm/src/workflow/claude-code/websocket-publisher.js +8 -15
  204. package/esm/src/workflow/schemas/workflow.schema.d.ts +4 -4
  205. package/package.json +5 -5
@@ -5,6 +5,7 @@
5
5
  * for exposure via MCP to coding agents.
6
6
  **************************/
7
7
  import { INVALID_ARGUMENT } from "../errors/index.js";
8
+ import { createSubscriberSet } from "../utils/subscriber-set.js";
8
9
  /**
9
10
  * Map ErrorType to ErrorCategory from slug registry
10
11
  */
@@ -18,7 +19,7 @@ const ERROR_TYPE_TO_CATEGORY = {
18
19
  /** Implement error collector. */
19
20
  export class ErrorCollector {
20
21
  errors = new Map();
21
- subscribers = new Set();
22
+ subscribers = createSubscriberSet();
22
23
  idCounter = 0;
23
24
  maxErrors;
24
25
  constructor(options = {}) {
@@ -45,14 +46,7 @@ export class ErrorCollector {
45
46
  this.errors.delete(oldestId);
46
47
  }
47
48
  this.errors.set(fullError.id, fullError);
48
- for (const subscriber of this.subscribers) {
49
- try {
50
- subscriber(fullError);
51
- }
52
- catch (_) {
53
- /* expected: subscriber errors must not break error collection */
54
- }
55
- }
49
+ this.subscribers.notify(fullError);
56
50
  return fullError;
57
51
  }
58
52
  addTypedError(type, message, details = {}) {
@@ -208,8 +202,7 @@ export class ErrorCollector {
208
202
  return counts;
209
203
  }
210
204
  subscribe(callback) {
211
- this.subscribers.add(callback);
212
- return () => this.subscribers.delete(callback);
205
+ return this.subscribers.subscribe(callback);
213
206
  }
214
207
  toJSON() {
215
208
  return this.getAll();
@@ -1 +1 @@
1
- {"version":3,"file":"log-buffer.d.ts","sourceRoot":"","sources":["../../../src/src/observability/log-buffer.ts"],"names":[],"mappings":"AAEA,yCAAyC;AACzC,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAE3D,2BAA2B;AAC3B,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,QAAQ,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AACD,uDAAuD;AACvD,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,EAAE,QAAQ,GAAG,QAAQ,EAAE,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,8CAA8C;AAC9C,MAAM,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;AAEtD,4BAA4B;AAC5B,qBAAa,SAAS;IACpB,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,GAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAO;IAI9C,OAAO,CAAC,UAAU;IAIlB,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,GAAG,WAAW,CAAC,GAAG,QAAQ;IA2B3D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAW,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,QAAQ;IAInF,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAW,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,QAAQ;IAIlF,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAW,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,QAAQ;IAIlF,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAW,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,QAAQ;IAInF,KAAK,CAAC,MAAM,CAAC,EAAE,SAAS,GAAG,QAAQ,EAAE;IAqCrC,IAAI,CAAC,KAAK,SAAK,GAAG,QAAQ,EAAE;IAI5B,MAAM,IAAI,QAAQ,EAAE;IAIpB,KAAK,IAAI,IAAI;IAIb,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,YAAY,IAAI,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC;IAUxC,SAAS,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,IAAI;IAK9C,MAAM,IAAI,QAAQ,EAAE;IAIpB,MAAM,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,GAAG,MAAM;CAYrC;AAID,yBAAyB;AACzB,wBAAgB,YAAY,IAAI,SAAS,CAGxC;AAED,sCAAsC;AACtC,wBAAgB,cAAc,IAAI,IAAI,CAGrC;AAED,gDAAgD;AAChD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,SAAY,GAAG,MAAM,IAAI,CA6ClF"}
1
+ {"version":3,"file":"log-buffer.d.ts","sourceRoot":"","sources":["../../../src/src/observability/log-buffer.ts"],"names":[],"mappings":"AAGA,yCAAyC;AACzC,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAE3D,2BAA2B;AAC3B,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,QAAQ,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AACD,uDAAuD;AACvD,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,EAAE,QAAQ,GAAG,QAAQ,EAAE,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,8CAA8C;AAC9C,MAAM,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;AAEtD,4BAA4B;AAC5B,qBAAa,SAAS;IACpB,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,WAAW,CAAqC;IACxD,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,GAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAO;IAI9C,OAAO,CAAC,UAAU;IAIlB,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,GAAG,WAAW,CAAC,GAAG,QAAQ;IAqB3D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAW,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,QAAQ;IAInF,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAW,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,QAAQ;IAIlF,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAW,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,QAAQ;IAIlF,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAW,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,QAAQ;IAInF,KAAK,CAAC,MAAM,CAAC,EAAE,SAAS,GAAG,QAAQ,EAAE;IAqCrC,IAAI,CAAC,KAAK,SAAK,GAAG,QAAQ,EAAE;IAI5B,MAAM,IAAI,QAAQ,EAAE;IAIpB,KAAK,IAAI,IAAI;IAIb,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,YAAY,IAAI,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC;IAUxC,SAAS,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,IAAI;IAI9C,MAAM,IAAI,QAAQ,EAAE;IAIpB,MAAM,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,GAAG,MAAM;CAYrC;AAID,yBAAyB;AACzB,wBAAgB,YAAY,IAAI,SAAS,CAGxC;AAED,sCAAsC;AACtC,wBAAgB,cAAc,IAAI,IAAI,CAGrC;AAED,gDAAgD;AAChD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,SAAY,GAAG,MAAM,IAAI,CA6ClF"}
@@ -1,8 +1,9 @@
1
1
  import { redactSensitive } from "../utils/logger/redact.js";
2
+ import { createSubscriberSet } from "../utils/subscriber-set.js";
2
3
  /** Implement log buffer. */
3
4
  export class LogBuffer {
4
5
  entries = [];
5
- subscribers = new Set();
6
+ subscribers = createSubscriberSet();
6
7
  idCounter = 0;
7
8
  maxSize;
8
9
  constructor(options = {}) {
@@ -24,14 +25,7 @@ export class LogBuffer {
24
25
  while (this.entries.length > this.maxSize) {
25
26
  this.entries.shift();
26
27
  }
27
- for (const subscriber of this.subscribers) {
28
- try {
29
- subscriber(fullEntry);
30
- }
31
- catch (_) {
32
- /* expected: subscriber errors must not break log buffering */
33
- }
34
- }
28
+ this.subscribers.notify(fullEntry);
35
29
  return fullEntry;
36
30
  }
37
31
  debug(message, source = "server", data) {
@@ -96,8 +90,7 @@ export class LogBuffer {
96
90
  return counts;
97
91
  }
98
92
  subscribe(callback) {
99
- this.subscribers.add(callback);
100
- return () => this.subscribers.delete(callback);
93
+ return this.subscribers.subscribe(callback);
101
94
  }
102
95
  toJSON() {
103
96
  return this.getAll();
@@ -15,6 +15,8 @@ export interface RequestProfileContextUpdate {
15
15
  projectSlug?: string;
16
16
  requestMode?: string;
17
17
  }
18
+ /** Round to 2 decimal places (Server-Timing millisecond precision). */
19
+ export declare function roundMs(value: number): number;
18
20
  export declare function isRequestProfilingEnabled(pathname?: string): boolean;
19
21
  export declare function runWithRequestProfiling<T>(options: {
20
22
  category: string;
@@ -28,6 +30,8 @@ export declare function markRequestProfilePhase(name: string, durationMs?: numbe
28
30
  export declare function profileSyncPhase<T>(name: string, fn: () => T): T;
29
31
  export declare function updateRequestProfileContext(update: RequestProfileContextUpdate): void;
30
32
  export declare function finalizeRequestProfiling(status?: number): RequestProfileRecord | null;
33
+ /** Build a Server-Timing header value from a total plus named phase durations. */
34
+ export declare function buildServerTimingValue(totalLabel: string, totalMs: number, phases: Iterable<[string, number]>): string;
31
35
  export declare function buildServerTimingHeader(record: RequestProfileRecord): string;
32
36
  export declare function withServerTimingHeader(response: Response, record: RequestProfileRecord | null): Response;
33
37
  export declare function snapshotRequestProfiles(): {
@@ -1 +1 @@
1
- {"version":3,"file":"request-profiler.d.ts","sourceRoot":"","sources":["../../../src/src/observability/request-profiler.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,2BAA2B;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AA+CD,wBAAgB,yBAAyB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAQpE;AAED,wBAAsB,uBAAuB,CAAC,CAAC,EAC7C,OAAO,EAAE;IACP,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,EACD,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CAYZ;AAED,wBAAsB,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAWpF;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,SAAI,GAAG,IAAI,CAK1E;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAWhE;AAED,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,2BAA2B,GAAG,IAAI,CAMrF;AAED,wBAAgB,wBAAwB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI,CAsBrF;AAUD,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAQ5E;AAED,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,oBAAoB,GAAG,IAAI,GAClC,QAAQ,CAiBV;AAED,wBAAgB,uBAAuB,IAAI;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,oBAAoB,EAAE,CAAC;CACjC,CAOA;AAED,wBAAgB,oBAAoB,IAAI,IAAI,CAG3C"}
1
+ {"version":3,"file":"request-profiler.d.ts","sourceRoot":"","sources":["../../../src/src/observability/request-profiler.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,2BAA2B;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAiBD,uEAAuE;AACvE,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE7C;AA4BD,wBAAgB,yBAAyB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAQpE;AAED,wBAAsB,uBAAuB,CAAC,CAAC,EAC7C,OAAO,EAAE;IACP,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,EACD,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CAYZ;AAED,wBAAsB,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAWpF;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,SAAI,GAAG,IAAI,CAK1E;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAWhE;AAED,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,2BAA2B,GAAG,IAAI,CAMrF;AAED,wBAAgB,wBAAwB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI,CAsBrF;AAUD,kFAAkF;AAClF,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,QAAQ,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GACjC,MAAM,CAMR;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAM5E;AAED,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,oBAAoB,GAAG,IAAI,GAClC,QAAQ,CAiBV;AAED,wBAAgB,uBAAuB,IAAI;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,oBAAoB,EAAE,CAAC;CACjC,CAOA;AAED,wBAAgB,oBAAoB,IAAI,IAAI,CAG3C"}
@@ -4,7 +4,8 @@ const storage = new AsyncLocalStorage();
4
4
  const records = [];
5
5
  const MAX_RECORDS = 200;
6
6
  let sequence = 0;
7
- function roundMs(value) {
7
+ /** Round to 2 decimal places (Server-Timing millisecond precision). */
8
+ export function roundMs(value) {
8
9
  return Math.round(value * 100) / 100;
9
10
  }
10
11
  function shouldEnableProfiling() {
@@ -119,13 +120,17 @@ function sanitizeMetricName(name) {
119
120
  function formatDuration(value) {
120
121
  return Math.max(0, roundMs(value)).toFixed(2);
121
122
  }
122
- export function buildServerTimingHeader(record) {
123
- const metrics = [`total;dur=${formatDuration(record.totalMs)}`];
124
- for (const [name, duration] of Object.entries(record.phases).slice(0, 20)) {
123
+ /** Build a Server-Timing header value from a total plus named phase durations. */
124
+ export function buildServerTimingValue(totalLabel, totalMs, phases) {
125
+ const metrics = [`${sanitizeMetricName(totalLabel)};dur=${formatDuration(totalMs)}`];
126
+ for (const [name, duration] of phases) {
125
127
  metrics.push(`${sanitizeMetricName(name)};dur=${formatDuration(duration)}`);
126
128
  }
127
129
  return metrics.join(", ");
128
130
  }
131
+ export function buildServerTimingHeader(record) {
132
+ return buildServerTimingValue("total", record.totalMs, Object.entries(record.phases).slice(0, 20));
133
+ }
129
134
  export function withServerTimingHeader(response, record) {
130
135
  if (!record || !shouldEnableServerTiming())
131
136
  return response;
@@ -1,4 +1,4 @@
1
- import { type RetryConfig } from "./retry-handler.js";
1
+ import { type TransportRetryConfig } from "../veryfront-api-transport.js";
2
2
  import { type LookupDomainResponse, type PageInfo, type Project, type ProjectFile, type ReleaseAssetManifestApiResponse, type ReleaseAssetManifestBuildResponse, type ReleaseAssetManifestStateResponse, type ReleaseAssetUploadResponse } from "./schemas/index.js";
3
3
  export type TokenProvider = () => string;
4
4
  export interface ListFilesOptions {
@@ -63,10 +63,10 @@ export interface ProjectStyleArtifactResolution {
63
63
  }
64
64
  export declare class VeryfrontAPIOperations {
65
65
  private apiBaseUrl;
66
- private retryConfig;
67
66
  private projectId?;
68
67
  private tokenProvider;
69
- constructor(apiBaseUrl: string, tokenOrProvider: string | TokenProvider, retryConfig: RetryConfig, projectId?: string | undefined);
68
+ private transport;
69
+ constructor(apiBaseUrl: string, tokenOrProvider: string | TokenProvider, retryConfig: TransportRetryConfig, projectId?: string | undefined);
70
70
  setTokenProvider(provider: TokenProvider): void;
71
71
  getToken(): string;
72
72
  setProjectId(projectId: string): void;
@@ -1 +1 @@
1
- {"version":3,"file":"operations.d.ts","sourceRoot":"","sources":["../../../../../src/src/platform/adapters/veryfront-api-client/operations.ts"],"names":[],"mappings":"AACA,OAAO,EAAyC,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAE7F,OAAO,EAeL,KAAK,oBAAoB,EACzB,KAAK,QAAQ,EACb,KAAK,OAAO,EACZ,KAAK,WAAW,EAChB,KAAK,+BAA+B,EACpC,KAAK,iCAAiC,EACtC,KAAK,iCAAiC,EACtC,KAAK,0BAA0B,EAChC,MAAM,oBAAoB,CAAC;AAQ5B,MAAM,MAAM,aAAa,GAAG,MAAM,MAAM,CAAC;AAEzC,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,YAAY,CAAC;IAC/B,SAAS,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,SAAS,EAAE,QAAQ,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,cAAc;IAC7B,wFAAwF;IACxF,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,yBAA0B,SAAQ,qBAAqB;IACtE,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,6BAA8B,SAAQ,yBAAyB;IAC9E,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,wBAAyB,SAAQ,yBAAyB;IACzE,MAAM,CAAC,EAAE,UAAU,GAAG,OAAO,GAAG,QAAQ,CAAC;IACzC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,8BAA8B;IAC7C,MAAM,EAAE,OAAO,GAAG,SAAS,GAAG,UAAU,GAAG,QAAQ,CAAC;IACpD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAmFD,qBAAa,sBAAsB;IAI/B,OAAO,CAAC,UAAU;IAElB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,SAAS,CAAC;IANpB,OAAO,CAAC,aAAa,CAAgB;gBAG3B,UAAU,EAAE,MAAM,EAC1B,eAAe,EAAE,MAAM,GAAG,aAAa,EAC/B,WAAW,EAAE,WAAW,EACxB,SAAS,CAAC,EAAE,MAAM,YAAA;IAO5B,gBAAgB,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI;IAI/C,QAAQ,IAAI,MAAM;IAIlB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIrC,YAAY,IAAI,MAAM;IAQhB,YAAY,CAAC,OAAO,CAAC,EAAE;QAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;KAC5B,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAYhB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKhD,eAAe,CACnB,UAAU,EAAE,MAAM,EAClB,SAAS,SAAS,EAClB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,cAAc,CAAC;IAepB,kBAAkB,CACtB,UAAU,EAAE,MAAM,EAClB,SAAS,SAAS,EAClB,OAAO,GAAE,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAM,GAC7C,OAAO,CAAC,WAAW,EAAE,CAAC;IAkBzB,aAAa,CACX,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,UAAU,CAAC;IA8BhB,oBAAoB,CACxB,UAAU,EAAE,MAAM,EAClB,eAAe,SAAe,EAC9B,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,cAAc,CAAC;IAwBpB,uBAAuB,CAC3B,UAAU,EAAE,MAAM,EAClB,eAAe,SAAe,EAC9B,OAAO,GAAE,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAM,GAC7C,OAAO,CAAC,WAAW,EAAE,CAAC;IAkBzB,kBAAkB,CAChB,UAAU,EAAE,MAAM,EAClB,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,UAAU,CAAC;IA+BhB,gBAAgB,CACpB,UAAU,EAAE,MAAM,EAClB,OAAO,SAAW,EAClB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,cAAc,CAAC;IAkBpB,mBAAmB,CACvB,UAAU,EAAE,MAAM,EAClB,OAAO,SAAW,EAClB,OAAO,GAAE,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAM,GAC7C,OAAO,CAAC,WAAW,EAAE,CAAC;IAMzB,cAAc,CACZ,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,UAAU,CAAC;IA+BtB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;IA2CrE,oBAAoB,CACxB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,yBAAyB,GAC/B,OAAO,CAAC,8BAA8B,CAAC;IAcpC,wBAAwB,CAC5B,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,6BAA6B,GACnC,OAAO,CAAC,8BAA8B,CAAC;IA4BpC,mBAAmB,CACvB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,wBAAwB,GAC9B,OAAO,CAAC,8BAA8B,CAAC;IAuCpC,8BAA8B,CAClC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,iCAAiC,CAAC;IAcvC,kBAAkB,CACtB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,UAAU,GAChB,OAAO,CAAC,0BAA0B,CAAC;IAuBhC,uBAAuB,CAC3B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,OAAO,GAChB,OAAO,CAAC,iCAAiC,CAAC;IAcvC,+BAA+B,CACnC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,SAAS,GAAG,QAAQ,EAC3B,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,iCAAiC,CAAC;IAcvC,uBAAuB,CAC3B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,+BAA+B,CAAC;IAU3C,OAAO,CAAC,OAAO;CAahB"}
1
+ {"version":3,"file":"operations.d.ts","sourceRoot":"","sources":["../../../../../src/src/platform/adapters/veryfront-api-client/operations.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { logger as baseLogger } from "../../../utils/index.js";
2
- import { requestWithRetry } from "./retry-handler.js";
2
+ import { createCanonicalVeryfrontApiTransport, } from "../veryfront-api-transport.js";
3
3
  import { API_CLIENT_ERROR, VeryfrontError } from "./types.js";
4
4
  import { getBranchFileDetailSchema, getEnvironmentFileDetailSchema, getListBranchFilesResponseSchema, getListEnvironmentFilesResponseSchema, getListProjectsResponseSchema, getListReleaseFilesResponseSchema, getProjectSchema, getProjectWithEnvironmentsSchema, getReleaseAssetManifestBuildResponseSchema, getReleaseAssetManifestResponseSchema, getReleaseAssetManifestStateResponseSchema, getReleaseAssetUploadResponseSchema, getReleaseFileDetailSchema, getStyleArtifactResolveResponseSchema, } from "./schemas/index.js";
5
5
  import { withSpan } from "../../../observability/tracing/otlp-setup.js";
@@ -73,16 +73,16 @@ async function listAllFiles(list) {
73
73
  }
74
74
  export class VeryfrontAPIOperations {
75
75
  apiBaseUrl;
76
- retryConfig;
77
76
  projectId;
78
77
  tokenProvider;
78
+ transport;
79
79
  constructor(apiBaseUrl, tokenOrProvider, retryConfig, projectId) {
80
80
  this.apiBaseUrl = apiBaseUrl;
81
- this.retryConfig = retryConfig;
82
81
  this.projectId = projectId;
83
82
  this.tokenProvider = typeof tokenOrProvider === "string"
84
83
  ? () => tokenOrProvider
85
84
  : tokenOrProvider;
85
+ this.transport = createCanonicalVeryfrontApiTransport(apiBaseUrl, () => this.tokenProvider(), retryConfig);
86
86
  }
87
87
  setTokenProvider(provider) {
88
88
  this.tokenProvider = provider;
@@ -414,6 +414,6 @@ export class VeryfrontAPIOperations {
414
414
  return getReleaseAssetManifestResponseSchema().parse(raw);
415
415
  }
416
416
  request(endpoint, options = {}) {
417
- return withSpan(SpanNames.API_REQUEST, () => requestWithRetry(`${this.apiBaseUrl}${endpoint}`, this.tokenProvider(), this.retryConfig, options), { "api.endpoint": endpoint, "api.base_url": this.apiBaseUrl });
417
+ return withSpan(SpanNames.API_REQUEST, () => this.transport.request(`${this.apiBaseUrl}${endpoint}`, options), { "api.endpoint": endpoint, "api.base_url": this.apiBaseUrl });
418
418
  }
419
419
  }
@@ -1,17 +1,6 @@
1
- export interface RetryConfig {
2
- maxRetries: number;
3
- initialDelay: number;
4
- maxDelay: number;
5
- }
6
- export interface RequestOptions {
7
- returnText?: boolean;
8
- /** Request timeout in milliseconds. Defaults to 30000ms (30 seconds). */
9
- timeoutMs?: number;
10
- method?: string;
11
- body?: BodyInit | null;
12
- headers?: HeadersInit;
13
- /** Demote an expected 404 miss to debug while preserving thrown error semantics. */
14
- expected404?: boolean;
15
- }
1
+ import { type TransportRequestInit, type TransportRetryConfig } from "../veryfront-api-transport.js";
2
+ export type RetryConfig = TransportRetryConfig;
3
+ export type RequestOptions = TransportRequestInit;
4
+ /** Backward-compat alias; prefer holding a transport instance directly. */
16
5
  export declare function requestWithRetry(url: string, apiToken: string, retryConfig: RetryConfig, options?: RequestOptions): Promise<unknown>;
17
6
  //# sourceMappingURL=retry-handler.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"retry-handler.d.ts","sourceRoot":"","sources":["../../../../../src/src/platform/adapters/veryfront-api-client/retry-handler.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;IACvB,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,oFAAoF;IACpF,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAaD,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,WAAW,EACxB,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,OAAO,CAAC,CA8GlB"}
1
+ {"version":3,"file":"retry-handler.d.ts","sourceRoot":"","sources":["../../../../../src/src/platform/adapters/veryfront-api-client/retry-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EAC1B,MAAM,+BAA+B,CAAC;AAEvC,MAAM,MAAM,WAAW,GAAG,oBAAoB,CAAC;AAC/C,MAAM,MAAM,cAAc,GAAG,oBAAoB,CAAC;AAElD,2EAA2E;AAC3E,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,WAAW,EACxB,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,OAAO,CAAC,CAIlB"}
@@ -1,111 +1,7 @@
1
- import { logger } from "../../../utils/index.js";
2
- import { retryWithBackoff } from "../../../errors/error-handlers.js";
3
- import { injectContext, withSpan } from "../../../observability/tracing/otlp-setup.js";
4
- import { SpanNames } from "../../../observability/tracing/span-names.js";
5
- import { recordApiRequest, recordApiRetry, } from "../../../observability/simple-metrics/metrics-recorder.js";
6
- import { API_CLIENT_ERROR, VeryfrontError } from "./types.js";
7
- const apiLog = logger.component("api");
8
- const veryfrontApiClientLog = logger.component("veryfront-api-client");
9
- /** Default timeout for API requests (30 seconds) */
10
- const DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
11
- function logTimedOut(url, timeoutMs, attempt) {
12
- veryfrontApiClientLog.warn("Request timed out", {
13
- url: url.replace(/token=[^&]+/, "token=***"),
14
- timeoutMs,
15
- attempt: attempt + 1,
16
- });
17
- }
18
- export async function requestWithRetry(url, apiToken, retryConfig, options = {}) {
19
- const urlObj = new URL(url);
20
- const urlPath = urlObj.pathname;
21
- const { maxRetries, initialDelay, maxDelay } = retryConfig;
22
- const timeoutMs = options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
23
- // Note: We only trace the individual fetch attempts (HTTP_CLIENT_FETCH),
24
- // not the outer retry wrapper, to reduce span nesting and trace size.
25
- const result = await retryWithBackoff((signal, attempt) => {
26
- return withSpan(SpanNames.HTTP_CLIENT_FETCH, async () => {
27
- const startTime = performance.now();
28
- const headers = new Headers(options.headers);
29
- headers.set("Authorization", `Bearer ${apiToken}`);
30
- if (!headers.has("Content-Type")) {
31
- headers.set("Content-Type", "application/json");
32
- }
33
- injectContext(headers);
34
- const response = await fetch(url, {
35
- method: options.method ?? "GET",
36
- headers,
37
- body: options.body,
38
- signal,
39
- });
40
- const duration = performance.now() - startTime;
41
- recordApiRequest(response.status);
42
- apiLog.debug("Request completed", {
43
- path: urlPath,
44
- status: response.status,
45
- durationMs: Math.round(duration),
46
- });
47
- if (!response.ok) {
48
- const text = await response.text();
49
- // Optional probes (for example stylesheet candidates) may expect a 404.
50
- // Keep only explicit opt-ins below warn while preserving thrown error semantics.
51
- const isExpected404 = options.expected404 === true && response.status === 404;
52
- // 4xx = client errors (expected, e.g. 404 for missing deno.json) → warn
53
- // 5xx = server errors (unexpected) → error
54
- const logLevel = isExpected404 ? "debug" : response.status >= 500 ? "error" : "warn";
55
- veryfrontApiClientLog[logLevel]("Request failed", {
56
- url: url.replace(/token=[^&]+/, "token=***"),
57
- status: response.status,
58
- statusText: response.statusText,
59
- responseText: text.slice(0, 500),
60
- });
61
- throw API_CLIENT_ERROR.create({
62
- detail: `API request failed: ${response.status} ${response.statusText}`,
63
- status: response.status,
64
- context: { details: { url, responseText: text } },
65
- });
66
- }
67
- const data = options.returnText ? await response.text() : await response.json();
68
- return { data, status: response.status, duration };
69
- }, {
70
- "http.method": options.method ?? "GET",
71
- "http.url": url,
72
- "http.target": urlPath,
73
- "http.host": urlObj.host,
74
- "http.scheme": urlObj.protocol.replace(":", ""),
75
- "http.retry_attempt": attempt,
76
- });
77
- }, {
78
- maxAttempts: maxRetries + 1,
79
- initialDelay,
80
- maxDelay,
81
- timeoutMs,
82
- shouldRetry: (error) => {
83
- if (!(error instanceof VeryfrontError) || error.slug !== "api-client-error")
84
- return true;
85
- const status = error.status;
86
- return !status || status < 400 || status >= 500 || status === 429;
87
- },
88
- onRetry: ({ error, attempt, delay, isTimeout }) => {
89
- if (isTimeout)
90
- logTimedOut(url, timeoutMs, attempt);
91
- recordApiRetry();
92
- veryfrontApiClientLog.warn("Request failed, retrying...", {
93
- attempt: attempt + 1,
94
- maxRetries,
95
- delay,
96
- error: error.message,
97
- timeout: isTimeout,
98
- });
99
- },
100
- wrapFinalError: (lastError, lastAttempt) => {
101
- if (lastError.name === "AbortError")
102
- logTimedOut(url, timeoutMs, lastAttempt);
103
- return API_CLIENT_ERROR.create({
104
- detail: `API request failed after ${maxRetries} retries: ${lastError.message}`,
105
- cause: lastError,
106
- context: { details: { originalError: lastError } },
107
- });
108
- },
109
- });
110
- return result.data;
1
+ import { createCanonicalVeryfrontApiTransport, } from "../veryfront-api-transport.js";
2
+ /** Backward-compat alias; prefer holding a transport instance directly. */
3
+ export function requestWithRetry(url, apiToken, retryConfig, options = {}) {
4
+ const { origin } = new URL(url);
5
+ return createCanonicalVeryfrontApiTransport(origin, () => apiToken, retryConfig)
6
+ .request(url, options);
111
7
  }
@@ -0,0 +1,40 @@
1
+ export interface TransportRetryConfig {
2
+ maxRetries: number;
3
+ initialDelay: number;
4
+ maxDelay: number;
5
+ }
6
+ export interface TransportRequestInit {
7
+ method?: string;
8
+ headers?: HeadersInit;
9
+ body?: BodyInit | null;
10
+ returnText?: boolean;
11
+ expected404?: boolean;
12
+ timeoutMs?: number;
13
+ }
14
+ export interface VeryfrontApiTransportConfig<T> {
15
+ baseUrl: string;
16
+ getToken: () => string;
17
+ retry: TransportRetryConfig;
18
+ timeoutMs?: number;
19
+ defaultHeaders?: Record<string, string>;
20
+ onResponse?: (response: Response, init: TransportRequestInit, url: string) => Promise<T>;
21
+ afterFetch?: (status: number, durationMs: number) => void;
22
+ shouldRetry?: (error: unknown, attempt: number) => boolean;
23
+ onRetry?: (info: {
24
+ error: Error;
25
+ attempt: number;
26
+ delay: number;
27
+ isTimeout: boolean;
28
+ url: string;
29
+ timeoutMs: number;
30
+ }) => void;
31
+ wrapFinalError?: (lastError: Error, lastAttempt: number) => Error;
32
+ wrapFetch?: (fn: () => Promise<T>, url: string, method: string, attempt: number) => Promise<T>;
33
+ }
34
+ export interface VeryfrontApiTransport<T> {
35
+ request(pathOrUrl: string, init?: TransportRequestInit): Promise<T>;
36
+ }
37
+ export declare function createVeryfrontApiTransport<T>(config: VeryfrontApiTransportConfig<T>): VeryfrontApiTransport<T>;
38
+ /** Canonical transport: span tracing, request metrics, API_CLIENT_ERROR mapping. */
39
+ export declare function createCanonicalVeryfrontApiTransport(baseUrl: string, getToken: () => string, retry: TransportRetryConfig): VeryfrontApiTransport<unknown>;
40
+ //# sourceMappingURL=veryfront-api-transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"veryfront-api-transport.d.ts","sourceRoot":"","sources":["../../../../src/src/platform/adapters/veryfront-api-transport.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,IAAI,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,2BAA2B,CAAC,CAAC;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,MAAM,CAAC;IACvB,KAAK,EAAE,oBAAoB,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,oBAAoB,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IACzF,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1D,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IAC3D,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE;QACf,KAAK,EAAE,KAAK,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,OAAO,CAAC;QACnB,GAAG,EAAE,MAAM,CAAC;QACZ,SAAS,EAAE,MAAM,CAAC;KACnB,KAAK,IAAI,CAAC;IACX,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,KAAK,KAAK,CAAC;IAClE,SAAS,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;CAChG;AAED,MAAM,WAAW,qBAAqB,CAAC,CAAC;IACtC,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACrE;AAED,wBAAgB,2BAA2B,CAAC,CAAC,EAC3C,MAAM,EAAE,2BAA2B,CAAC,CAAC,CAAC,GACrC,qBAAqB,CAAC,CAAC,CAAC,CAwE1B;AAED,oFAAoF;AACpF,wBAAgB,oCAAoC,CAClD,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,MAAM,EACtB,KAAK,EAAE,oBAAoB,GAC1B,qBAAqB,CAAC,OAAO,CAAC,CAgChC"}
@@ -0,0 +1,142 @@
1
+ import { retryWithBackoff } from "../../errors/error-handlers.js";
2
+ import { API_CLIENT_ERROR } from "../../errors/error-registry.js";
3
+ import { VeryfrontError } from "../../errors/types.js";
4
+ import { injectContext, withSpan } from "../../observability/tracing/otlp-setup.js";
5
+ import { SpanNames } from "../../observability/tracing/span-names.js";
6
+ import { recordApiRequest, recordApiRetry, } from "../../observability/simple-metrics/metrics-recorder.js";
7
+ import { serverLogger } from "../../utils/logger/logger.js";
8
+ const log = serverLogger.component("veryfront-api-transport");
9
+ const apiClientLog = serverLogger.component("veryfront-api-client");
10
+ const DEFAULT_TIMEOUT_MS = 30_000;
11
+ export function createVeryfrontApiTransport(config) {
12
+ const { baseUrl, getToken, retry: { maxRetries, initialDelay, maxDelay }, timeoutMs: cfgTimeout = DEFAULT_TIMEOUT_MS, defaultHeaders = {}, afterFetch, wrapFetch, } = config;
13
+ const onResponse = config.onResponse ??
14
+ defaultOnResponse;
15
+ const shouldRetry = config.shouldRetry ?? defaultShouldRetry;
16
+ const wrapFinalError = config.wrapFinalError ??
17
+ ((err) => API_CLIENT_ERROR.create({
18
+ detail: `API request failed after ${maxRetries} retries: ${err.message}`,
19
+ cause: err,
20
+ context: { details: { originalError: err } },
21
+ }));
22
+ return {
23
+ request(pathOrUrl, init = {}) {
24
+ const url = pathOrUrl.startsWith("http") ? pathOrUrl : `${baseUrl}${pathOrUrl}`;
25
+ const method = init.method ?? "GET";
26
+ const timeoutMs = init.timeoutMs ?? cfgTimeout;
27
+ // Capture the token once per request: retries of this request must not
28
+ // pick up mid-flight token mutations (setRequestToken/clearRequestToken),
29
+ // matching the pre-transport requestWithRetry semantics.
30
+ const token = getToken();
31
+ return retryWithBackoff((signal, attempt) => {
32
+ const doFetch = async () => {
33
+ const headers = new Headers(init.headers);
34
+ for (const [k, v] of Object.entries(defaultHeaders)) {
35
+ if (!headers.has(k))
36
+ headers.set(k, v);
37
+ }
38
+ headers.set("Authorization", `Bearer ${token}`);
39
+ injectContext(headers);
40
+ const start = performance.now();
41
+ const res = await fetch(url, { method, headers, body: init.body, signal });
42
+ afterFetch?.(res.status, performance.now() - start);
43
+ return onResponse(res, init, url);
44
+ };
45
+ return wrapFetch ? wrapFetch(doFetch, url, method, attempt) : doFetch();
46
+ }, {
47
+ maxAttempts: maxRetries + 1,
48
+ initialDelay,
49
+ maxDelay,
50
+ timeoutMs,
51
+ shouldRetry,
52
+ onRetry: config.onRetry
53
+ ? ({ error, attempt, delay, isTimeout }) => config.onRetry({ error, attempt, delay, isTimeout, url, timeoutMs })
54
+ : ({ error, attempt, delay, isTimeout }) => {
55
+ if (isTimeout)
56
+ logTimeout(url, timeoutMs, attempt);
57
+ log.warn("Request failed, retrying...", {
58
+ attempt: attempt + 1,
59
+ maxRetries,
60
+ delay,
61
+ error: error.message,
62
+ timeout: isTimeout,
63
+ });
64
+ },
65
+ wrapFinalError(lastError, lastAttempt) {
66
+ if (lastError.name === "AbortError")
67
+ logTimeout(url, timeoutMs, lastAttempt);
68
+ return wrapFinalError(lastError, lastAttempt);
69
+ },
70
+ });
71
+ },
72
+ };
73
+ }
74
+ /** Canonical transport: span tracing, request metrics, API_CLIENT_ERROR mapping. */
75
+ export function createCanonicalVeryfrontApiTransport(baseUrl, getToken, retry) {
76
+ return createVeryfrontApiTransport({
77
+ baseUrl,
78
+ getToken,
79
+ retry,
80
+ defaultHeaders: { "Content-Type": "application/json" },
81
+ afterFetch(status) {
82
+ recordApiRequest(status);
83
+ },
84
+ onRetry({ error, attempt, delay, isTimeout, url, timeoutMs }) {
85
+ if (isTimeout)
86
+ logTimeout(url, timeoutMs, attempt);
87
+ recordApiRetry();
88
+ apiClientLog.warn("Request failed, retrying...", {
89
+ attempt: attempt + 1,
90
+ maxRetries: retry.maxRetries,
91
+ delay,
92
+ error: error.message,
93
+ timeout: isTimeout,
94
+ });
95
+ },
96
+ wrapFetch(fn, url, method, attempt) {
97
+ const { pathname, host, protocol } = new URL(url);
98
+ return withSpan(SpanNames.HTTP_CLIENT_FETCH, fn, {
99
+ "http.method": method,
100
+ "http.url": url,
101
+ "http.target": pathname,
102
+ "http.host": host,
103
+ "http.scheme": protocol.replace(":", ""),
104
+ "http.retry_attempt": attempt,
105
+ });
106
+ },
107
+ });
108
+ }
109
+ function logTimeout(url, timeoutMs, attempt) {
110
+ log.warn("Request timed out", {
111
+ url: url.replace(/token=[^&]+/, "token=***"),
112
+ timeoutMs,
113
+ attempt: attempt + 1,
114
+ });
115
+ }
116
+ async function defaultOnResponse(response, init, url) {
117
+ if (!response.ok) {
118
+ const text = await response.text();
119
+ const isExpected404 = init.expected404 === true && response.status === 404;
120
+ const level = isExpected404 ? "debug" : response.status >= 500 ? "error" : "warn";
121
+ const redactedUrl = url.replace(/token=[^&]+/g, "token=***");
122
+ apiClientLog[level]("Request failed", {
123
+ url: redactedUrl,
124
+ status: response.status,
125
+ statusText: response.statusText,
126
+ responseText: text.slice(0, 500),
127
+ });
128
+ throw API_CLIENT_ERROR.create({
129
+ detail: `API request failed: ${response.status} ${response.statusText}`,
130
+ status: response.status,
131
+ // Redacted so error telemetry cannot leak token query params.
132
+ context: { details: { url: redactedUrl, responseText: text } },
133
+ });
134
+ }
135
+ return init.returnText ? response.text() : response.json();
136
+ }
137
+ function defaultShouldRetry(error) {
138
+ if (!(error instanceof VeryfrontError) || error.slug !== "api-client-error")
139
+ return true;
140
+ const { status } = error;
141
+ return !status || status < 400 || status >= 500 || status === 429;
142
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"provider-http.d.ts","sourceRoot":"","sources":["../../../../src/src/provider/runtime-loader/provider-http.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,YAAY,CAAC;AAKxF;;;;;GAKG;AACH,qBAAa,aAAc,SAAQ,KAAK;IACtC,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;OAGG;IACH,SAAiB,YAAY,CAAC,EAAE,MAAM,CAAC;gBAE3B,OAAO,EAAE;QACnB,QAAQ,EAAE,YAAY,CAAC;QACvB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,OAAO,CAAC;QACnB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB;CAUF;AAED,4EAA4E;AAC5E,qBAAa,uBAAwB,SAAQ,aAAa;CAAG;AAE7D,mFAAmF;AACnF,qBAAa,sBAAuB,SAAQ,aAAa;CAAG;AAE5D,2DAA2D;AAC3D,qBAAa,kBAAmB,SAAQ,aAAa;CAAG;AAExD,6DAA6D;AAC7D,qBAAa,oBAAqB,SAAQ,aAAa;CAAG;AAe1D,6BAA6B;AAC7B,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAY3E;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,YAAY,EACtB,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,aAAa,CAAC,CAiJxB;AAED,yCAAyC;AACzC,wBAAsB,WAAW,CAAC,OAAO,EAAE;IACzC,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IACnC,IAAI,EAAE,WAAW,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,YAAY,CAAC;CAC5B,GAAG,OAAO,CAAC,OAAO,CAAC,CASnB;AAED,oCAAoC;AACpC,wBAAsB,aAAa,CAAC,OAAO,EAAE;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IACnC,IAAI,EAAE,WAAW,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,YAAY,CAAC;CAC5B,GAAG,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAkBtC"}
1
+ {"version":3,"file":"provider-http.d.ts","sourceRoot":"","sources":["../../../../src/src/provider/runtime-loader/provider-http.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,YAAY,CAAC;AAKxF;;;;;GAKG;AACH,qBAAa,aAAc,SAAQ,KAAK;IACtC,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;OAGG;IACH,SAAiB,YAAY,CAAC,EAAE,MAAM,CAAC;gBAE3B,OAAO,EAAE;QACnB,QAAQ,EAAE,YAAY,CAAC;QACvB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,OAAO,CAAC;QACnB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB;CAUF;AAED,4EAA4E;AAC5E,qBAAa,uBAAwB,SAAQ,aAAa;CAAG;AAE7D,mFAAmF;AACnF,qBAAa,sBAAuB,SAAQ,aAAa;CAAG;AAE5D,2DAA2D;AAC3D,qBAAa,kBAAmB,SAAQ,aAAa;CAAG;AAExD,6DAA6D;AAC7D,qBAAa,oBAAqB,SAAQ,aAAa;CAAG;AAe1D,6BAA6B;AAC7B,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAY3E;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,YAAY,EACtB,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,aAAa,CAAC,CAqJxB;AAED,yCAAyC;AACzC,wBAAsB,WAAW,CAAC,OAAO,EAAE;IACzC,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IACnC,IAAI,EAAE,WAAW,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,YAAY,CAAC;CAC5B,GAAG,OAAO,CAAC,OAAO,CAAC,CASnB;AAED,oCAAoC;AACpC,wBAAsB,aAAa,CAAC,OAAO,EAAE;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IACnC,IAAI,EAAE,WAAW,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,YAAY,CAAC;CAC5B,GAAG,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAkBtC"}
@@ -193,7 +193,11 @@ export async function buildProviderError(provider, response) {
193
193
  errorType === "invalid_request_error" &&
194
194
  parsedBody !== undefined &&
195
195
  !truncated;
196
- return isStructuredInvalidRequest
196
+ const problemSlug = typeof parsedBody?.slug === "string" ? parsedBody.slug : undefined;
197
+ const isStructuredVeryfrontCreditProblem = status === 402 &&
198
+ (problemSlug === "insufficient-credits" || problemSlug === "resource-limit-exceeded") &&
199
+ !truncated;
200
+ return isStructuredInvalidRequest || isStructuredVeryfrontCreditProblem
197
201
  ? preserveStructuredResponseBody(requestError, rawBody)
198
202
  : requestError;
199
203
  }
@@ -1 +1 @@
1
- {"version":3,"file":"server-timing.d.ts","sourceRoot":"","sources":["../../../src/src/proxy/server-timing.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC7B;AAcD,wBAAgB,6BAA6B,IAAI,OAAO,CAGvD;AAED,wBAAgB,uBAAuB,CACrC,OAAO,UAAkC,GACxC,iBAAiB,CAMnB;AAED,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,iBAAiB,EACzB,IAAI,EAAE,MAAM,EACZ,UAAU,SAAI,GACb,IAAI,CAGN;AAED,wBAAsB,6BAA6B,CAAC,CAAC,EACnD,MAAM,EAAE,iBAAiB,EACzB,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CASZ;AAED,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,iBAAiB,EACzB,OAAO,SAAuC,GAC7C,QAAQ,CAwBV"}
1
+ {"version":3,"file":"server-timing.d.ts","sourceRoot":"","sources":["../../../src/src/proxy/server-timing.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC7B;AAED,wBAAgB,6BAA6B,IAAI,OAAO,CAGvD;AAED,wBAAgB,uBAAuB,CACrC,OAAO,UAAkC,GACxC,iBAAiB,CAMnB;AAED,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,iBAAiB,EACzB,IAAI,EAAE,MAAM,EACZ,UAAU,SAAI,GACb,IAAI,CAGN;AAED,wBAAsB,6BAA6B,CAAC,CAAC,EACnD,MAAM,EAAE,iBAAiB,EACzB,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CASZ;AAED,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,iBAAiB,EACzB,OAAO,SAAuC,GAC7C,QAAQ,CAmBV"}
@@ -1,13 +1,5 @@
1
1
  import { getEnv } from "../platform/compat/process.js";
2
- function roundMs(value) {
3
- return Math.round(value * 100) / 100;
4
- }
5
- function formatDuration(value) {
6
- return Math.max(0, roundMs(value)).toFixed(2);
7
- }
8
- function sanitizeMetricName(name) {
9
- return name.replace(/[^A-Za-z0-9!#$%&'*+\-.^_`|~]/g, "_");
10
- }
2
+ import { buildServerTimingValue, roundMs } from "../observability/request-profiler.js";
11
3
  export function shouldEnableProxyServerTiming() {
12
4
  return getEnv("VERYFRONT_ENABLE_PROXY_SERVER_TIMING") === "1" ||
13
5
  getEnv("VERYFRONT_ENABLE_SERVER_TIMING") === "1";
@@ -38,11 +30,7 @@ export async function profileProxyServerTimingPhase(timing, name, fn) {
38
30
  export function withProxyServerTimingHeader(response, timing, totalMs = performance.now() - timing.startedAt) {
39
31
  if (!timing.enabled)
40
32
  return response;
41
- const metrics = [`proxy.total;dur=${formatDuration(totalMs)}`];
42
- for (const [name, duration] of timing.phases.entries()) {
43
- metrics.push(`${sanitizeMetricName(name)};dur=${formatDuration(duration)}`);
44
- }
45
- const value = metrics.join(", ");
33
+ const value = buildServerTimingValue("proxy.total", totalMs, timing.phases.entries());
46
34
  try {
47
35
  const existing = response.headers.get("Server-Timing");
48
36
  response.headers.set("Server-Timing", existing ? `${existing}, ${value}` : value);