@classytic/arc 1.1.0 → 2.1.2

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 (322) hide show
  1. package/README.md +247 -794
  2. package/bin/arc.js +91 -52
  3. package/dist/EventTransport-BD2U0BTc.d.mts +100 -0
  4. package/dist/EventTransport-BD2U0BTc.d.mts.map +1 -0
  5. package/dist/HookSystem-BsGV-j2l.mjs +405 -0
  6. package/dist/HookSystem-BsGV-j2l.mjs.map +1 -0
  7. package/dist/ResourceRegistry-DsN4KJjV.mjs +250 -0
  8. package/dist/ResourceRegistry-DsN4KJjV.mjs.map +1 -0
  9. package/dist/adapters/index.d.mts +5 -0
  10. package/dist/adapters/index.mjs +3 -0
  11. package/dist/audit/index.d.mts +82 -0
  12. package/dist/audit/index.d.mts.map +1 -0
  13. package/dist/audit/index.mjs +276 -0
  14. package/dist/audit/index.mjs.map +1 -0
  15. package/dist/audit/mongodb.d.mts +5 -0
  16. package/dist/audit/mongodb.mjs +3 -0
  17. package/dist/audited-C3T5DTUx.mjs +141 -0
  18. package/dist/audited-C3T5DTUx.mjs.map +1 -0
  19. package/dist/auth/index.d.mts +189 -0
  20. package/dist/auth/index.d.mts.map +1 -0
  21. package/dist/auth/index.mjs +1102 -0
  22. package/dist/auth/index.mjs.map +1 -0
  23. package/dist/auth/redis-session.d.mts +44 -0
  24. package/dist/auth/redis-session.d.mts.map +1 -0
  25. package/dist/auth/redis-session.mjs +76 -0
  26. package/dist/auth/redis-session.mjs.map +1 -0
  27. package/dist/betterAuthOpenApi-BrHKeSAx.mjs +250 -0
  28. package/dist/betterAuthOpenApi-BrHKeSAx.mjs.map +1 -0
  29. package/dist/cache/index.d.mts +146 -0
  30. package/dist/cache/index.d.mts.map +1 -0
  31. package/dist/cache/index.mjs +92 -0
  32. package/dist/cache/index.mjs.map +1 -0
  33. package/dist/caching-Bl28lYsR.mjs +94 -0
  34. package/dist/caching-Bl28lYsR.mjs.map +1 -0
  35. package/dist/chunk-C7Uep-_p.mjs +20 -0
  36. package/dist/circuitBreaker-DeY4FCjs.mjs +1097 -0
  37. package/dist/circuitBreaker-DeY4FCjs.mjs.map +1 -0
  38. package/dist/cli/commands/describe.d.mts +19 -0
  39. package/dist/cli/commands/describe.d.mts.map +1 -0
  40. package/dist/cli/commands/describe.mjs +239 -0
  41. package/dist/cli/commands/describe.mjs.map +1 -0
  42. package/dist/cli/commands/docs.d.mts +14 -0
  43. package/dist/cli/commands/docs.d.mts.map +1 -0
  44. package/dist/cli/commands/docs.mjs +53 -0
  45. package/dist/cli/commands/docs.mjs.map +1 -0
  46. package/dist/cli/commands/{generate.d.ts → generate.d.mts} +3 -1
  47. package/dist/cli/commands/generate.d.mts.map +1 -0
  48. package/dist/cli/commands/generate.mjs +358 -0
  49. package/dist/cli/commands/generate.mjs.map +1 -0
  50. package/dist/cli/commands/{init.d.ts → init.d.mts} +12 -8
  51. package/dist/cli/commands/init.d.mts.map +1 -0
  52. package/dist/cli/commands/{init.js → init.mjs} +807 -616
  53. package/dist/cli/commands/init.mjs.map +1 -0
  54. package/dist/cli/commands/introspect.d.mts +11 -0
  55. package/dist/cli/commands/introspect.d.mts.map +1 -0
  56. package/dist/cli/commands/introspect.mjs +76 -0
  57. package/dist/cli/commands/introspect.mjs.map +1 -0
  58. package/dist/cli/index.d.mts +17 -0
  59. package/dist/cli/index.d.mts.map +1 -0
  60. package/dist/cli/index.mjs +157 -0
  61. package/dist/cli/index.mjs.map +1 -0
  62. package/dist/constants-DdXFXQtN.mjs +85 -0
  63. package/dist/constants-DdXFXQtN.mjs.map +1 -0
  64. package/dist/core/index.d.mts +5 -0
  65. package/dist/core/index.mjs +4 -0
  66. package/dist/createApp-CUgNqegw.mjs +560 -0
  67. package/dist/createApp-CUgNqegw.mjs.map +1 -0
  68. package/dist/defineResource-k0_BDn8v.mjs +2197 -0
  69. package/dist/defineResource-k0_BDn8v.mjs.map +1 -0
  70. package/dist/discovery/index.d.mts +47 -0
  71. package/dist/discovery/index.d.mts.map +1 -0
  72. package/dist/discovery/index.mjs +110 -0
  73. package/dist/discovery/index.mjs.map +1 -0
  74. package/dist/docs/index.d.mts +163 -0
  75. package/dist/docs/index.d.mts.map +1 -0
  76. package/dist/docs/index.mjs +73 -0
  77. package/dist/docs/index.mjs.map +1 -0
  78. package/dist/elevation-BRy3yFWT.mjs +113 -0
  79. package/dist/elevation-BRy3yFWT.mjs.map +1 -0
  80. package/dist/elevation-B_2dRLVP.d.mts +88 -0
  81. package/dist/elevation-B_2dRLVP.d.mts.map +1 -0
  82. package/dist/errorHandler-BbcgBmIH.d.mts +73 -0
  83. package/dist/errorHandler-BbcgBmIH.d.mts.map +1 -0
  84. package/dist/errorHandler-C1okiriz.mjs +109 -0
  85. package/dist/errorHandler-C1okiriz.mjs.map +1 -0
  86. package/dist/errors-B9bZok84.mjs +212 -0
  87. package/dist/errors-B9bZok84.mjs.map +1 -0
  88. package/dist/errors-ChKiFz62.d.mts +125 -0
  89. package/dist/errors-ChKiFz62.d.mts.map +1 -0
  90. package/dist/eventPlugin-CTrLH3mt.d.mts +125 -0
  91. package/dist/eventPlugin-CTrLH3mt.d.mts.map +1 -0
  92. package/dist/eventPlugin-DGR_B2on.mjs +230 -0
  93. package/dist/eventPlugin-DGR_B2on.mjs.map +1 -0
  94. package/dist/events/index.d.mts +54 -0
  95. package/dist/events/index.d.mts.map +1 -0
  96. package/dist/events/index.mjs +52 -0
  97. package/dist/events/index.mjs.map +1 -0
  98. package/dist/events/transports/redis-stream-entry.d.mts +2 -0
  99. package/dist/events/transports/redis-stream-entry.mjs +178 -0
  100. package/dist/events/transports/redis-stream-entry.mjs.map +1 -0
  101. package/dist/events/transports/redis.d.mts +77 -0
  102. package/dist/events/transports/redis.d.mts.map +1 -0
  103. package/dist/events/transports/redis.mjs +125 -0
  104. package/dist/events/transports/redis.mjs.map +1 -0
  105. package/dist/externalPaths-DlINfKbP.d.mts +51 -0
  106. package/dist/externalPaths-DlINfKbP.d.mts.map +1 -0
  107. package/dist/factory/index.d.mts +64 -0
  108. package/dist/factory/index.d.mts.map +1 -0
  109. package/dist/factory/index.mjs +3 -0
  110. package/dist/fastifyAdapter-BkrGrlFi.d.mts +217 -0
  111. package/dist/fastifyAdapter-BkrGrlFi.d.mts.map +1 -0
  112. package/dist/fields-DyaDVX4J.d.mts +110 -0
  113. package/dist/fields-DyaDVX4J.d.mts.map +1 -0
  114. package/dist/fields-iagOozy0.mjs +115 -0
  115. package/dist/fields-iagOozy0.mjs.map +1 -0
  116. package/dist/hooks/index.d.mts +4 -0
  117. package/dist/hooks/index.mjs +3 -0
  118. package/dist/idempotency/index.d.mts +97 -0
  119. package/dist/idempotency/index.d.mts.map +1 -0
  120. package/dist/idempotency/index.mjs +320 -0
  121. package/dist/idempotency/index.mjs.map +1 -0
  122. package/dist/idempotency/mongodb.d.mts +2 -0
  123. package/dist/idempotency/mongodb.mjs +115 -0
  124. package/dist/idempotency/mongodb.mjs.map +1 -0
  125. package/dist/idempotency/redis.d.mts +2 -0
  126. package/dist/idempotency/redis.mjs +104 -0
  127. package/dist/idempotency/redis.mjs.map +1 -0
  128. package/dist/index.d.mts +261 -0
  129. package/dist/index.d.mts.map +1 -0
  130. package/dist/index.mjs +105 -0
  131. package/dist/index.mjs.map +1 -0
  132. package/dist/integrations/event-gateway.d.mts +47 -0
  133. package/dist/integrations/event-gateway.d.mts.map +1 -0
  134. package/dist/integrations/event-gateway.mjs +44 -0
  135. package/dist/integrations/event-gateway.mjs.map +1 -0
  136. package/dist/integrations/index.d.mts +5 -0
  137. package/dist/integrations/index.mjs +1 -0
  138. package/dist/integrations/jobs.d.mts +104 -0
  139. package/dist/integrations/jobs.d.mts.map +1 -0
  140. package/dist/integrations/jobs.mjs +124 -0
  141. package/dist/integrations/jobs.mjs.map +1 -0
  142. package/dist/integrations/streamline.d.mts +61 -0
  143. package/dist/integrations/streamline.d.mts.map +1 -0
  144. package/dist/integrations/streamline.mjs +126 -0
  145. package/dist/integrations/streamline.mjs.map +1 -0
  146. package/dist/integrations/websocket.d.mts +83 -0
  147. package/dist/integrations/websocket.d.mts.map +1 -0
  148. package/dist/integrations/websocket.mjs +289 -0
  149. package/dist/integrations/websocket.mjs.map +1 -0
  150. package/dist/interface-B01JvPVc.d.mts +78 -0
  151. package/dist/interface-B01JvPVc.d.mts.map +1 -0
  152. package/dist/interface-CZe8IkMf.d.mts +55 -0
  153. package/dist/interface-CZe8IkMf.d.mts.map +1 -0
  154. package/dist/interface-Ch8HU9uM.d.mts +1098 -0
  155. package/dist/interface-Ch8HU9uM.d.mts.map +1 -0
  156. package/dist/introspectionPlugin-rFdO8ZUa.mjs +54 -0
  157. package/dist/introspectionPlugin-rFdO8ZUa.mjs.map +1 -0
  158. package/dist/keys-BqNejWup.mjs +43 -0
  159. package/dist/keys-BqNejWup.mjs.map +1 -0
  160. package/dist/logger-Df2O2WsW.mjs +79 -0
  161. package/dist/logger-Df2O2WsW.mjs.map +1 -0
  162. package/dist/memory-cQgelFOj.mjs +144 -0
  163. package/dist/memory-cQgelFOj.mjs.map +1 -0
  164. package/dist/migrations/index.d.mts +157 -0
  165. package/dist/migrations/index.d.mts.map +1 -0
  166. package/dist/migrations/index.mjs +261 -0
  167. package/dist/migrations/index.mjs.map +1 -0
  168. package/dist/mongodb-BfJVlUJH.mjs +94 -0
  169. package/dist/mongodb-BfJVlUJH.mjs.map +1 -0
  170. package/dist/mongodb-CGzRbfAK.d.mts +119 -0
  171. package/dist/mongodb-CGzRbfAK.d.mts.map +1 -0
  172. package/dist/mongodb-JN-9JA7K.d.mts +72 -0
  173. package/dist/mongodb-JN-9JA7K.d.mts.map +1 -0
  174. package/dist/openapi-G3Cw7XuM.mjs +524 -0
  175. package/dist/openapi-G3Cw7XuM.mjs.map +1 -0
  176. package/dist/org/index.d.mts +69 -0
  177. package/dist/org/index.d.mts.map +1 -0
  178. package/dist/org/index.mjs +514 -0
  179. package/dist/org/index.mjs.map +1 -0
  180. package/dist/org/types.d.mts +83 -0
  181. package/dist/org/types.d.mts.map +1 -0
  182. package/dist/org/types.mjs +1 -0
  183. package/dist/permissions/index.d.mts +279 -0
  184. package/dist/permissions/index.d.mts.map +1 -0
  185. package/dist/permissions/index.mjs +579 -0
  186. package/dist/permissions/index.mjs.map +1 -0
  187. package/dist/plugins/index.d.mts +173 -0
  188. package/dist/plugins/index.d.mts.map +1 -0
  189. package/dist/plugins/index.mjs +523 -0
  190. package/dist/plugins/index.mjs.map +1 -0
  191. package/dist/plugins/response-cache.d.mts +88 -0
  192. package/dist/plugins/response-cache.d.mts.map +1 -0
  193. package/dist/plugins/response-cache.mjs +284 -0
  194. package/dist/plugins/response-cache.mjs.map +1 -0
  195. package/dist/plugins/tracing-entry.d.mts +2 -0
  196. package/dist/plugins/tracing-entry.mjs +186 -0
  197. package/dist/plugins/tracing-entry.mjs.map +1 -0
  198. package/dist/pluralize-CEweyOEm.mjs +87 -0
  199. package/dist/pluralize-CEweyOEm.mjs.map +1 -0
  200. package/dist/policies/{index.d.ts → index.d.mts} +204 -169
  201. package/dist/policies/index.d.mts.map +1 -0
  202. package/dist/policies/index.mjs +322 -0
  203. package/dist/policies/index.mjs.map +1 -0
  204. package/dist/presets/{index.d.ts → index.d.mts} +63 -131
  205. package/dist/presets/index.d.mts.map +1 -0
  206. package/dist/presets/index.mjs +144 -0
  207. package/dist/presets/index.mjs.map +1 -0
  208. package/dist/presets/multiTenant.d.mts +25 -0
  209. package/dist/presets/multiTenant.d.mts.map +1 -0
  210. package/dist/presets/multiTenant.mjs +114 -0
  211. package/dist/presets/multiTenant.mjs.map +1 -0
  212. package/dist/presets-BITljm96.mjs +120 -0
  213. package/dist/presets-BITljm96.mjs.map +1 -0
  214. package/dist/presets-DzSMwlKj.d.mts +58 -0
  215. package/dist/presets-DzSMwlKj.d.mts.map +1 -0
  216. package/dist/prisma-DJbMt3yf.mjs +628 -0
  217. package/dist/prisma-DJbMt3yf.mjs.map +1 -0
  218. package/dist/prisma-Dg9GoVdj.d.mts +275 -0
  219. package/dist/prisma-Dg9GoVdj.d.mts.map +1 -0
  220. package/dist/queryCachePlugin-7THaI5mt.d.mts +72 -0
  221. package/dist/queryCachePlugin-7THaI5mt.d.mts.map +1 -0
  222. package/dist/queryCachePlugin-DMBnp2Q0.mjs +139 -0
  223. package/dist/queryCachePlugin-DMBnp2Q0.mjs.map +1 -0
  224. package/dist/redis-D-JAeLtm.d.mts +50 -0
  225. package/dist/redis-D-JAeLtm.d.mts.map +1 -0
  226. package/dist/redis-stream-Bdh_vUU8.d.mts +104 -0
  227. package/dist/redis-stream-Bdh_vUU8.d.mts.map +1 -0
  228. package/dist/registry/index.d.mts +12 -0
  229. package/dist/registry/index.d.mts.map +1 -0
  230. package/dist/registry/index.mjs +4 -0
  231. package/dist/requestContext-QQD6ROJc.mjs +56 -0
  232. package/dist/requestContext-QQD6ROJc.mjs.map +1 -0
  233. package/dist/schemaConverter-BwrmWroW.mjs +99 -0
  234. package/dist/schemaConverter-BwrmWroW.mjs.map +1 -0
  235. package/dist/schemas/index.d.mts +64 -0
  236. package/dist/schemas/index.d.mts.map +1 -0
  237. package/dist/schemas/index.mjs +83 -0
  238. package/dist/schemas/index.mjs.map +1 -0
  239. package/dist/scope/index.d.mts +22 -0
  240. package/dist/scope/index.d.mts.map +1 -0
  241. package/dist/scope/index.mjs +66 -0
  242. package/dist/scope/index.mjs.map +1 -0
  243. package/dist/sessionManager-jPKLbHE0.d.mts +187 -0
  244. package/dist/sessionManager-jPKLbHE0.d.mts.map +1 -0
  245. package/dist/sse-B3c3_yZp.mjs +124 -0
  246. package/dist/sse-B3c3_yZp.mjs.map +1 -0
  247. package/dist/testing/index.d.mts +908 -0
  248. package/dist/testing/index.d.mts.map +1 -0
  249. package/dist/testing/index.mjs +1977 -0
  250. package/dist/testing/index.mjs.map +1 -0
  251. package/dist/tracing-Cc7vVQPp.d.mts +71 -0
  252. package/dist/tracing-Cc7vVQPp.d.mts.map +1 -0
  253. package/dist/typeGuards-DhMNLuvU.mjs +10 -0
  254. package/dist/typeGuards-DhMNLuvU.mjs.map +1 -0
  255. package/dist/types/index.d.mts +947 -0
  256. package/dist/types/index.d.mts.map +1 -0
  257. package/dist/types/index.mjs +15 -0
  258. package/dist/types/index.mjs.map +1 -0
  259. package/dist/types-Beqn1Un7.mjs +39 -0
  260. package/dist/types-Beqn1Un7.mjs.map +1 -0
  261. package/dist/types-CIgB7UUl.d.mts +446 -0
  262. package/dist/types-CIgB7UUl.d.mts.map +1 -0
  263. package/dist/types-aYB4V7uN.d.mts +87 -0
  264. package/dist/types-aYB4V7uN.d.mts.map +1 -0
  265. package/dist/utils/index.d.mts +748 -0
  266. package/dist/utils/index.d.mts.map +1 -0
  267. package/dist/utils/index.mjs +6 -0
  268. package/package.json +194 -68
  269. package/dist/BaseController-DVAiHxEQ.d.ts +0 -233
  270. package/dist/adapters/index.d.ts +0 -237
  271. package/dist/adapters/index.js +0 -668
  272. package/dist/arcCorePlugin-CsShQdyP.d.ts +0 -273
  273. package/dist/audit/index.d.ts +0 -195
  274. package/dist/audit/index.js +0 -319
  275. package/dist/auth/index.d.ts +0 -47
  276. package/dist/auth/index.js +0 -174
  277. package/dist/cli/commands/docs.d.ts +0 -11
  278. package/dist/cli/commands/docs.js +0 -474
  279. package/dist/cli/commands/generate.js +0 -334
  280. package/dist/cli/commands/introspect.d.ts +0 -8
  281. package/dist/cli/commands/introspect.js +0 -338
  282. package/dist/cli/index.d.ts +0 -4
  283. package/dist/cli/index.js +0 -3269
  284. package/dist/core/index.d.ts +0 -220
  285. package/dist/core/index.js +0 -2786
  286. package/dist/createApp-Ce9wl8W9.d.ts +0 -77
  287. package/dist/docs/index.d.ts +0 -166
  288. package/dist/docs/index.js +0 -658
  289. package/dist/errors-8WIxGS_6.d.ts +0 -122
  290. package/dist/events/index.d.ts +0 -117
  291. package/dist/events/index.js +0 -89
  292. package/dist/factory/index.d.ts +0 -38
  293. package/dist/factory/index.js +0 -1652
  294. package/dist/hooks/index.d.ts +0 -4
  295. package/dist/hooks/index.js +0 -199
  296. package/dist/idempotency/index.d.ts +0 -323
  297. package/dist/idempotency/index.js +0 -500
  298. package/dist/index-B4t03KQ0.d.ts +0 -1366
  299. package/dist/index.d.ts +0 -135
  300. package/dist/index.js +0 -4756
  301. package/dist/migrations/index.d.ts +0 -185
  302. package/dist/migrations/index.js +0 -274
  303. package/dist/org/index.d.ts +0 -129
  304. package/dist/org/index.js +0 -220
  305. package/dist/permissions/index.d.ts +0 -144
  306. package/dist/permissions/index.js +0 -103
  307. package/dist/plugins/index.d.ts +0 -46
  308. package/dist/plugins/index.js +0 -1069
  309. package/dist/policies/index.js +0 -196
  310. package/dist/presets/index.js +0 -384
  311. package/dist/presets/multiTenant.d.ts +0 -39
  312. package/dist/presets/multiTenant.js +0 -112
  313. package/dist/registry/index.d.ts +0 -16
  314. package/dist/registry/index.js +0 -253
  315. package/dist/testing/index.d.ts +0 -618
  316. package/dist/testing/index.js +0 -48020
  317. package/dist/types/index.d.ts +0 -4
  318. package/dist/types/index.js +0 -8
  319. package/dist/types-B99TBmFV.d.ts +0 -76
  320. package/dist/types-BvckRbs2.d.ts +0 -143
  321. package/dist/utils/index.d.ts +0 -679
  322. package/dist/utils/index.js +0 -931
@@ -0,0 +1,186 @@
1
+ import { createRequire } from "node:module";
2
+ import fp from "fastify-plugin";
3
+
4
+ //#region src/plugins/tracing.ts
5
+ const require = createRequire(import.meta.url);
6
+ let trace;
7
+ let context;
8
+ let SpanStatusCode;
9
+ let NodeTracerProvider;
10
+ let BatchSpanProcessor;
11
+ let OTLPTraceExporter;
12
+ let getNodeAutoInstrumentations;
13
+ let isAvailable = false;
14
+ try {
15
+ const api = require("@opentelemetry/api");
16
+ trace = api.trace;
17
+ context = api.context;
18
+ SpanStatusCode = api.SpanStatusCode;
19
+ const sdkNode = require("@opentelemetry/sdk-node");
20
+ NodeTracerProvider = sdkNode.NodeTracerProvider;
21
+ BatchSpanProcessor = sdkNode.BatchSpanProcessor;
22
+ OTLPTraceExporter = require("@opentelemetry/exporter-trace-otlp-http").OTLPTraceExporter;
23
+ require("@opentelemetry/instrumentation-http").HttpInstrumentation;
24
+ require("@opentelemetry/instrumentation-mongodb").MongoDBInstrumentation;
25
+ getNodeAutoInstrumentations = require("@opentelemetry/auto-instrumentations-node").getNodeAutoInstrumentations;
26
+ isAvailable = true;
27
+ } catch (e) {}
28
+ /**
29
+ * Create a tracer provider
30
+ */
31
+ function createTracerProvider(options) {
32
+ if (!isAvailable) return null;
33
+ const { serviceName = "@classytic/arc", exporterUrl = "http://localhost:4318/v1/traces" } = options;
34
+ const exporter = new OTLPTraceExporter({ url: exporterUrl });
35
+ const provider = new NodeTracerProvider({ resource: { attributes: {
36
+ "service.name": serviceName,
37
+ "service.version": "1.0.0"
38
+ } } });
39
+ provider.addSpanProcessor(new BatchSpanProcessor(exporter));
40
+ provider.register();
41
+ return provider;
42
+ }
43
+ /**
44
+ * OpenTelemetry Distributed Tracing Plugin
45
+ */
46
+ async function tracingPlugin(fastify, options = {}) {
47
+ const { serviceName = "@classytic/arc", autoInstrumentation = true, sampleRate = 1 } = options;
48
+ if (!isAvailable) {
49
+ fastify.log.warn("OpenTelemetry not installed. Tracing disabled.");
50
+ fastify.log.warn("Install: npm install @opentelemetry/api @opentelemetry/sdk-node");
51
+ return;
52
+ }
53
+ if (!createTracerProvider(options)) return;
54
+ if (autoInstrumentation && getNodeAutoInstrumentations) {
55
+ const instrumentations = getNodeAutoInstrumentations({
56
+ "@opentelemetry/instrumentation-http": { enabled: true },
57
+ "@opentelemetry/instrumentation-mongodb": { enabled: true }
58
+ });
59
+ for (const instrumentation of instrumentations) instrumentation.enable();
60
+ fastify.log.debug("OpenTelemetry auto-instrumentation enabled");
61
+ }
62
+ const tracer = trace.getTracer(serviceName);
63
+ fastify.decorateRequest("tracer", void 0);
64
+ fastify.addHook("onRequest", async (request, reply) => {
65
+ if (Math.random() > sampleRate) return;
66
+ const span = tracer.startSpan(`HTTP ${request.method} ${request.url}`, {
67
+ kind: 1,
68
+ attributes: {
69
+ "http.method": request.method,
70
+ "http.url": request.url,
71
+ "http.target": request.routeOptions?.url ?? request.url,
72
+ "http.host": request.hostname,
73
+ "http.scheme": request.protocol,
74
+ "http.user_agent": request.headers["user-agent"]
75
+ }
76
+ });
77
+ request.tracer = {
78
+ tracer,
79
+ currentSpan: span
80
+ };
81
+ context.with(trace.setSpan(context.active(), span), () => {});
82
+ });
83
+ fastify.addHook("onResponse", async (request, reply) => {
84
+ if (!request.tracer?.currentSpan) return;
85
+ const span = request.tracer.currentSpan;
86
+ span.setAttributes({
87
+ "http.status_code": reply.statusCode,
88
+ "http.response_content_length": reply.getHeader("content-length")
89
+ });
90
+ if (reply.statusCode >= 500) span.setStatus({
91
+ code: SpanStatusCode.ERROR,
92
+ message: `HTTP ${reply.statusCode}`
93
+ });
94
+ else span.setStatus({ code: SpanStatusCode.OK });
95
+ span.end();
96
+ });
97
+ fastify.addHook("onError", async (request, reply, error) => {
98
+ if (!request.tracer?.currentSpan) return;
99
+ const span = request.tracer.currentSpan;
100
+ span.recordException(error);
101
+ span.setStatus({
102
+ code: SpanStatusCode.ERROR,
103
+ message: error.message
104
+ });
105
+ });
106
+ fastify.log.debug({ serviceName }, "OpenTelemetry tracing enabled");
107
+ }
108
+ /**
109
+ * Utility to create custom spans in your code
110
+ *
111
+ * @example
112
+ * import { createSpan } from '@classytic/arc/plugins';
113
+ *
114
+ * async function expensiveOperation(req) {
115
+ * return createSpan(req, 'expensiveOperation', async (span) => {
116
+ * span.setAttribute('custom.attribute', 'value');
117
+ * return await doWork();
118
+ * });
119
+ * }
120
+ */
121
+ function createSpan(request, name, fn, attributes) {
122
+ if (!isAvailable || !request.tracer) return fn(null);
123
+ const { tracer, currentSpan } = request.tracer;
124
+ const span = tracer.startSpan(name, {
125
+ parent: currentSpan,
126
+ attributes: attributes || {}
127
+ }, trace.setSpan(context.active(), currentSpan));
128
+ return context.with(trace.setSpan(context.active(), span), async () => {
129
+ try {
130
+ const result = await fn(span);
131
+ span.setStatus({ code: SpanStatusCode.OK });
132
+ return result;
133
+ } catch (error) {
134
+ span.recordException(error);
135
+ span.setStatus({
136
+ code: SpanStatusCode.ERROR,
137
+ message: error.message
138
+ });
139
+ throw error;
140
+ } finally {
141
+ span.end();
142
+ }
143
+ });
144
+ }
145
+ /**
146
+ * Decorator to automatically trace repository methods
147
+ *
148
+ * @example
149
+ * class ProductRepository extends Repository {
150
+ * @traced()
151
+ * async findActive() {
152
+ * return this.findAll({ filter: { isActive: true } });
153
+ * }
154
+ * }
155
+ */
156
+ function traced(spanName) {
157
+ return function(target, propertyKey, descriptor) {
158
+ const originalMethod = descriptor.value;
159
+ descriptor.value = async function(...args) {
160
+ const request = args.find((arg) => arg && arg.tracer);
161
+ if (!request?.tracer) return originalMethod.apply(this, args);
162
+ return createSpan(request, spanName || `${target.constructor.name}.${propertyKey}`, async (span) => {
163
+ if (span) {
164
+ span.setAttribute("db.operation", propertyKey);
165
+ span.setAttribute("db.system", "mongodb");
166
+ }
167
+ return originalMethod.apply(this, args);
168
+ });
169
+ };
170
+ return descriptor;
171
+ };
172
+ }
173
+ /**
174
+ * Check if OpenTelemetry is available
175
+ */
176
+ function isTracingAvailable() {
177
+ return isAvailable;
178
+ }
179
+ var tracing_default = fp(tracingPlugin, {
180
+ name: "arc-tracing",
181
+ fastify: "5.x"
182
+ });
183
+
184
+ //#endregion
185
+ export { createSpan, isTracingAvailable, traced, tracing_default as tracingPlugin };
186
+ //# sourceMappingURL=tracing-entry.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracing-entry.mjs","names":[],"sources":["../../src/plugins/tracing.ts"],"sourcesContent":["/**\n * OpenTelemetry Distributed Tracing Plugin\n *\n * Traces HTTP requests, repository operations, and MongoDB queries\n * across the entire application lifecycle.\n *\n * @example\n * import { tracingPlugin } from '@classytic/arc/plugins';\n *\n * await fastify.register(tracingPlugin, {\n * serviceName: 'my-api',\n * exporterUrl: 'http://localhost:4318/v1/traces', // OTLP endpoint\n * });\n */\n\nimport type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';\nimport fp from 'fastify-plugin';\nimport { createRequire } from 'node:module';\n\nconst require = createRequire(import.meta.url);\n\n// OpenTelemetry imports (peer dependencies)\nlet trace: any;\nlet context: any;\nlet SpanStatusCode: any;\nlet NodeTracerProvider: any;\nlet BatchSpanProcessor: any;\nlet OTLPTraceExporter: any;\nlet HttpInstrumentation: any;\nlet MongoDBInstrumentation: any;\nlet getNodeAutoInstrumentations: any;\n\n// Try to load OpenTelemetry (optional peer dependency)\nlet isAvailable = false;\ntry {\n const api = require('@opentelemetry/api');\n trace = api.trace;\n context = api.context;\n SpanStatusCode = api.SpanStatusCode;\n\n const sdkNode = require('@opentelemetry/sdk-node');\n NodeTracerProvider = sdkNode.NodeTracerProvider;\n BatchSpanProcessor = sdkNode.BatchSpanProcessor;\n\n const exporterTraceOtlp = require('@opentelemetry/exporter-trace-otlp-http');\n OTLPTraceExporter = exporterTraceOtlp.OTLPTraceExporter;\n\n const instrHttp = require('@opentelemetry/instrumentation-http');\n HttpInstrumentation = instrHttp.HttpInstrumentation;\n\n const instrMongo = require('@opentelemetry/instrumentation-mongodb');\n MongoDBInstrumentation = instrMongo.MongoDBInstrumentation;\n\n const autoInstr = require('@opentelemetry/auto-instrumentations-node');\n getNodeAutoInstrumentations = autoInstr.getNodeAutoInstrumentations;\n\n isAvailable = true;\n} catch (e) {\n // OpenTelemetry not installed - plugin will be no-op\n}\n\nexport interface TracingOptions {\n /**\n * Service name for traces\n */\n serviceName?: string;\n\n /**\n * OTLP exporter endpoint URL\n * @default 'http://localhost:4318/v1/traces'\n */\n exporterUrl?: string;\n\n /**\n * Enable auto-instrumentation for HTTP, MongoDB, etc.\n * @default true\n */\n autoInstrumentation?: boolean;\n\n /**\n * Sample rate (0.0 to 1.0)\n * @default 1.0 (trace everything)\n */\n sampleRate?: number;\n}\n\ninterface TracerContext {\n tracer: any;\n currentSpan: any;\n}\n\ndeclare module 'fastify' {\n interface FastifyRequest {\n tracer?: TracerContext;\n }\n}\n\n/**\n * Create a tracer provider\n */\nfunction createTracerProvider(options: TracingOptions) {\n if (!isAvailable) {\n return null;\n }\n\n const { serviceName = '@classytic/arc', exporterUrl = 'http://localhost:4318/v1/traces' } =\n options;\n\n const exporter = new OTLPTraceExporter({\n url: exporterUrl,\n });\n\n const provider = new NodeTracerProvider({\n resource: {\n attributes: {\n 'service.name': serviceName,\n 'service.version': '1.0.0',\n },\n },\n });\n\n provider.addSpanProcessor(new BatchSpanProcessor(exporter));\n provider.register();\n\n return provider;\n}\n\n/**\n * OpenTelemetry Distributed Tracing Plugin\n */\nasync function tracingPlugin(fastify: FastifyInstance, options: TracingOptions = {}) {\n const {\n serviceName = '@classytic/arc',\n autoInstrumentation = true,\n sampleRate = 1.0,\n } = options;\n\n // Skip if OpenTelemetry is not available\n if (!isAvailable) {\n fastify.log.warn('OpenTelemetry not installed. Tracing disabled.');\n fastify.log.warn('Install: npm install @opentelemetry/api @opentelemetry/sdk-node');\n return;\n }\n\n // Initialize tracer provider\n const provider = createTracerProvider(options);\n if (!provider) {\n return;\n }\n\n // Auto-instrumentation — enable HTTP + MongoDB tracing\n if (autoInstrumentation && getNodeAutoInstrumentations) {\n const instrumentations = getNodeAutoInstrumentations({\n '@opentelemetry/instrumentation-http': {\n enabled: true,\n },\n '@opentelemetry/instrumentation-mongodb': {\n enabled: true,\n },\n });\n for (const instrumentation of instrumentations) {\n instrumentation.enable();\n }\n fastify.log.debug('OpenTelemetry auto-instrumentation enabled');\n }\n\n const tracer = trace.getTracer(serviceName);\n\n // Add tracer to request\n fastify.decorateRequest('tracer', undefined);\n\n // Create span for each HTTP request\n fastify.addHook('onRequest', async (request: FastifyRequest, reply: FastifyReply) => {\n // Sampling\n if (Math.random() > sampleRate) {\n return;\n }\n\n const span = tracer.startSpan(`HTTP ${request.method} ${request.url}`, {\n kind: 1, // SpanKind.SERVER\n attributes: {\n 'http.method': request.method,\n 'http.url': request.url,\n 'http.target': request.routeOptions?.url ?? request.url,\n 'http.host': request.hostname,\n 'http.scheme': request.protocol,\n 'http.user_agent': request.headers['user-agent'],\n },\n });\n\n // Store span in request for child spans\n request.tracer = {\n tracer,\n currentSpan: span,\n };\n\n // Set active context\n context.with(trace.setSpan(context.active(), span), () => {\n // Context is now active for this request\n });\n });\n\n // End span after response\n fastify.addHook('onResponse', async (request: FastifyRequest, reply: FastifyReply) => {\n if (!request.tracer?.currentSpan) {\n return;\n }\n\n const span = request.tracer.currentSpan;\n\n // Add response attributes\n span.setAttributes({\n 'http.status_code': reply.statusCode,\n 'http.response_content_length': reply.getHeader('content-length'),\n });\n\n // Set span status\n if (reply.statusCode >= 500) {\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: `HTTP ${reply.statusCode}`,\n });\n } else {\n span.setStatus({ code: SpanStatusCode.OK });\n }\n\n span.end();\n });\n\n // Error tracking\n fastify.addHook('onError', async (request: FastifyRequest, reply: FastifyReply, error: Error) => {\n if (!request.tracer?.currentSpan) {\n return;\n }\n\n const span = request.tracer.currentSpan;\n span.recordException(error);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error.message,\n });\n });\n\n fastify.log.debug({ serviceName }, 'OpenTelemetry tracing enabled');\n}\n\n/**\n * Utility to create custom spans in your code\n *\n * @example\n * import { createSpan } from '@classytic/arc/plugins';\n *\n * async function expensiveOperation(req) {\n * return createSpan(req, 'expensiveOperation', async (span) => {\n * span.setAttribute('custom.attribute', 'value');\n * return await doWork();\n * });\n * }\n */\nexport function createSpan<T>(\n request: FastifyRequest,\n name: string,\n fn: (span: any) => Promise<T>,\n attributes?: Record<string, any>\n): Promise<T> {\n if (!isAvailable || !request.tracer) {\n // No tracing available, just execute function\n return fn(null);\n }\n\n const { tracer, currentSpan } = request.tracer;\n\n const span = tracer.startSpan(\n name,\n {\n parent: currentSpan,\n attributes: attributes || {},\n },\n trace.setSpan(context.active(), currentSpan)\n );\n\n return context.with(trace.setSpan(context.active(), span), async () => {\n try {\n const result = await fn(span);\n span.setStatus({ code: SpanStatusCode.OK });\n return result;\n } catch (error: any) {\n span.recordException(error);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error.message,\n });\n throw error;\n } finally {\n span.end();\n }\n });\n}\n\n/**\n * Decorator to automatically trace repository methods\n *\n * @example\n * class ProductRepository extends Repository {\n * @traced()\n * async findActive() {\n * return this.findAll({ filter: { isActive: true } });\n * }\n * }\n */\nexport function traced(spanName?: string) {\n return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {\n const originalMethod = descriptor.value;\n\n descriptor.value = async function (this: any, ...args: any[]) {\n // Extract request from args if available\n const request = args.find((arg) => arg && arg.tracer);\n\n if (!request?.tracer) {\n // No tracing context, just execute\n return originalMethod.apply(this, args);\n }\n\n const name = spanName || `${target.constructor.name}.${propertyKey}`;\n return createSpan(request, name, async (span) => {\n if (span) {\n span.setAttribute('db.operation', propertyKey);\n span.setAttribute('db.system', 'mongodb');\n }\n return originalMethod.apply(this, args);\n });\n };\n\n return descriptor;\n };\n}\n\n/**\n * Check if OpenTelemetry is available\n */\nexport function isTracingAvailable(): boolean {\n return isAvailable;\n}\n\nexport default fp(tracingPlugin, {\n name: 'arc-tracing',\n fastify: '5.x',\n});\n"],"mappings":"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"}
@@ -0,0 +1,87 @@
1
+ //#region src/cli/utils/pluralize.ts
2
+ /**
3
+ * Lightweight English pluralization for the Arc CLI.
4
+ *
5
+ * Covers the common cases developers hit when naming resources:
6
+ * company → companies
7
+ * category → categories
8
+ * status → statuses
9
+ * address → addresses
10
+ * person → people
11
+ * child → children
12
+ * bus → buses
13
+ * box → boxes
14
+ * quiz → quizzes
15
+ * leaf → leaves
16
+ * wolf → wolves
17
+ *
18
+ * No external dependencies — designed to keep the CLI install-free.
19
+ */
20
+ const IRREGULARS = {
21
+ person: "people",
22
+ child: "children",
23
+ man: "men",
24
+ woman: "women",
25
+ mouse: "mice",
26
+ goose: "geese",
27
+ tooth: "teeth",
28
+ foot: "feet",
29
+ ox: "oxen",
30
+ datum: "data",
31
+ medium: "media",
32
+ index: "indices",
33
+ matrix: "matrices",
34
+ vertex: "vertices",
35
+ criterion: "criteria"
36
+ };
37
+ const UNCOUNTABLES = new Set([
38
+ "sheep",
39
+ "fish",
40
+ "deer",
41
+ "series",
42
+ "species",
43
+ "money",
44
+ "rice",
45
+ "information",
46
+ "equipment",
47
+ "media",
48
+ "data"
49
+ ]);
50
+ /**
51
+ * Pluralize an English word.
52
+ *
53
+ * @param word - Singular noun (e.g. "company", "product", "person")
54
+ * @returns Plural form (e.g. "companies", "products", "people")
55
+ */
56
+ function pluralize(word) {
57
+ const lower = word.toLowerCase();
58
+ if (UNCOUNTABLES.has(lower)) return word;
59
+ if (IRREGULARS[lower]) {
60
+ const plural = IRREGULARS[lower];
61
+ return word[0] === word[0].toUpperCase() ? plural.charAt(0).toUpperCase() + plural.slice(1) : plural;
62
+ }
63
+ if (lower.endsWith("fe")) return word.slice(0, -2) + "ves";
64
+ if (lower.endsWith("f") && !lower.endsWith("ff") && !lower.endsWith("roof") && !lower.endsWith("chief") && !lower.endsWith("belief")) return word.slice(0, -1) + "ves";
65
+ if (lower.endsWith("y") && !/[aeiou]y$/i.test(lower)) return word.slice(0, -1) + "ies";
66
+ if (lower.endsWith("is")) return word.slice(0, -2) + "es";
67
+ if (new Set([
68
+ "cactus",
69
+ "stimulus",
70
+ "focus",
71
+ "fungus",
72
+ "nucleus",
73
+ "syllabus",
74
+ "radius",
75
+ "alumnus",
76
+ "terminus",
77
+ "bacillus"
78
+ ]).has(lower)) return word.slice(0, -2) + "i";
79
+ if (lower.endsWith("z") && !lower.endsWith("zz")) return word + "zes";
80
+ if (/(?:s|sh|ch|x|zz)$/i.test(lower)) return word + "es";
81
+ if (lower.endsWith("o") && !/[aeiou]o$/i.test(lower)) return word + "es";
82
+ return word + "s";
83
+ }
84
+
85
+ //#endregion
86
+ export { pluralize as t };
87
+ //# sourceMappingURL=pluralize-CEweyOEm.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pluralize-CEweyOEm.mjs","names":[],"sources":["../src/cli/utils/pluralize.ts"],"sourcesContent":["/**\n * Lightweight English pluralization for the Arc CLI.\n *\n * Covers the common cases developers hit when naming resources:\n * company → companies\n * category → categories\n * status → statuses\n * address → addresses\n * person → people\n * child → children\n * bus → buses\n * box → boxes\n * quiz → quizzes\n * leaf → leaves\n * wolf → wolves\n *\n * No external dependencies — designed to keep the CLI install-free.\n */\n\n// Irregular nouns that can't be handled by suffix rules\nconst IRREGULARS: Record<string, string> = {\n person: 'people',\n child: 'children',\n man: 'men',\n woman: 'women',\n mouse: 'mice',\n goose: 'geese',\n tooth: 'teeth',\n foot: 'feet',\n ox: 'oxen',\n datum: 'data',\n medium: 'media',\n index: 'indices',\n matrix: 'matrices',\n vertex: 'vertices',\n criterion: 'criteria',\n};\n\n// Words that are the same singular and plural\nconst UNCOUNTABLES = new Set([\n 'sheep', 'fish', 'deer', 'series', 'species', 'money',\n 'rice', 'information', 'equipment', 'media', 'data',\n]);\n\n/**\n * Pluralize an English word.\n *\n * @param word - Singular noun (e.g. \"company\", \"product\", \"person\")\n * @returns Plural form (e.g. \"companies\", \"products\", \"people\")\n */\nexport function pluralize(word: string): string {\n const lower = word.toLowerCase();\n\n // Uncountable — return as-is\n if (UNCOUNTABLES.has(lower)) return word;\n\n // Irregular — preserve original casing of first char\n if (IRREGULARS[lower]) {\n const plural = IRREGULARS[lower];\n return word[0]! === word[0]!.toUpperCase()\n ? plural.charAt(0).toUpperCase() + plural.slice(1)\n : plural;\n }\n\n // Suffix rules (order matters — most specific first)\n\n // -fe / -f → -ves (leaf → leaves, wolf → wolves, knife → knives)\n if (lower.endsWith('fe')) return word.slice(0, -2) + 'ves';\n if (lower.endsWith('f') && !lower.endsWith('ff') && !lower.endsWith('roof') && !lower.endsWith('chief') && !lower.endsWith('belief')) {\n return word.slice(0, -1) + 'ves';\n }\n\n // consonant + y → -ies (company → companies, category → categories)\n if (lower.endsWith('y') && !/[aeiou]y$/i.test(lower)) {\n return word.slice(0, -1) + 'ies';\n }\n\n // -is → -es (analysis → analyses, crisis → crises)\n if (lower.endsWith('is')) return word.slice(0, -2) + 'es';\n\n // -us → -i (only Latin-origin words, not status/bus/campus/virus)\n const LATIN_US_TO_I = new Set([\n 'cactus', 'stimulus', 'focus', 'fungus', 'nucleus',\n 'syllabus', 'radius', 'alumnus', 'terminus', 'bacillus',\n ]);\n if (LATIN_US_TO_I.has(lower)) return word.slice(0, -2) + 'i';\n\n // -z at end → double z + -es (quiz → quizzes, fez → fezzes)\n if (lower.endsWith('z') && !lower.endsWith('zz')) return word + 'zes';\n\n // sibilant endings: -s, -ss, -sh, -ch, -x, -zz → -es\n if (/(?:s|sh|ch|x|zz)$/i.test(lower)) return word + 'es';\n\n // -o → -es for common cases (hero → heroes, tomato → tomatoes)\n // but not for words ending in a vowel + o (radio → radios)\n if (lower.endsWith('o') && !/[aeiou]o$/i.test(lower)) return word + 'es';\n\n // Default: just add -s\n return word + 's';\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAoBA,MAAM,aAAqC;CACzC,QAAQ;CACR,OAAO;CACP,KAAK;CACL,OAAO;CACP,OAAO;CACP,OAAO;CACP,OAAO;CACP,MAAM;CACN,IAAI;CACJ,OAAO;CACP,QAAQ;CACR,OAAO;CACP,QAAQ;CACR,QAAQ;CACR,WAAW;CACZ;AAGD,MAAM,eAAe,IAAI,IAAI;CAC3B;CAAS;CAAQ;CAAQ;CAAU;CAAW;CAC9C;CAAQ;CAAe;CAAa;CAAS;CAC9C,CAAC;;;;;;;AAQF,SAAgB,UAAU,MAAsB;CAC9C,MAAM,QAAQ,KAAK,aAAa;AAGhC,KAAI,aAAa,IAAI,MAAM,CAAE,QAAO;AAGpC,KAAI,WAAW,QAAQ;EACrB,MAAM,SAAS,WAAW;AAC1B,SAAO,KAAK,OAAQ,KAAK,GAAI,aAAa,GACtC,OAAO,OAAO,EAAE,CAAC,aAAa,GAAG,OAAO,MAAM,EAAE,GAChD;;AAMN,KAAI,MAAM,SAAS,KAAK,CAAE,QAAO,KAAK,MAAM,GAAG,GAAG,GAAG;AACrD,KAAI,MAAM,SAAS,IAAI,IAAI,CAAC,MAAM,SAAS,KAAK,IAAI,CAAC,MAAM,SAAS,OAAO,IAAI,CAAC,MAAM,SAAS,QAAQ,IAAI,CAAC,MAAM,SAAS,SAAS,CAClI,QAAO,KAAK,MAAM,GAAG,GAAG,GAAG;AAI7B,KAAI,MAAM,SAAS,IAAI,IAAI,CAAC,aAAa,KAAK,MAAM,CAClD,QAAO,KAAK,MAAM,GAAG,GAAG,GAAG;AAI7B,KAAI,MAAM,SAAS,KAAK,CAAE,QAAO,KAAK,MAAM,GAAG,GAAG,GAAG;AAOrD,KAJsB,IAAI,IAAI;EAC5B;EAAU;EAAY;EAAS;EAAU;EACzC;EAAY;EAAU;EAAW;EAAY;EAC9C,CAAC,CACgB,IAAI,MAAM,CAAE,QAAO,KAAK,MAAM,GAAG,GAAG,GAAG;AAGzD,KAAI,MAAM,SAAS,IAAI,IAAI,CAAC,MAAM,SAAS,KAAK,CAAE,QAAO,OAAO;AAGhE,KAAI,qBAAqB,KAAK,MAAM,CAAE,QAAO,OAAO;AAIpD,KAAI,MAAM,SAAS,IAAI,IAAI,CAAC,aAAa,KAAK,MAAM,CAAE,QAAO,OAAO;AAGpE,QAAO,OAAO"}