@lovrozagar/honey 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/README.md +51 -0
  2. package/docs/api-ref/go.md +399 -0
  3. package/docs/api-ref/python.md +399 -0
  4. package/docs/api-ref/rust.md +399 -0
  5. package/docs/api-ref/typescript.md +399 -0
  6. package/docs/invalidate-spec.md +303 -0
  7. package/docs/sdk.md +722 -0
  8. package/package.json +120 -0
  9. package/src/accepts.ts +78 -0
  10. package/src/body-limit.ts +100 -0
  11. package/src/build/index.ts +174 -0
  12. package/src/cli-go/auth.go +15 -0
  13. package/src/cli-go/config.go +165 -0
  14. package/src/cli-go/errors.go +57 -0
  15. package/src/cli-go/multipart.go +55 -0
  16. package/src/cli-go/output.go +109 -0
  17. package/src/cli-go/root.go +30 -0
  18. package/src/cli-go/stream.go +88 -0
  19. package/src/cli-go/version.go +5 -0
  20. package/src/cli.ts +204 -0
  21. package/src/client/error.ts +27 -0
  22. package/src/client/http.ts +502 -0
  23. package/src/client/index.ts +127 -0
  24. package/src/client/sdk.ts +265 -0
  25. package/src/client/sse.ts +102 -0
  26. package/src/client/types.ts +211 -0
  27. package/src/client/ws.ts +115 -0
  28. package/src/client-go/errors.go +207 -0
  29. package/src/client-go/invalidation.go +373 -0
  30. package/src/client-go/realtime.go +317 -0
  31. package/src/client-go/result.go +20 -0
  32. package/src/client-go/runtime.go +413 -0
  33. package/src/client-go/sse.go +149 -0
  34. package/src/client-go/transport.go +452 -0
  35. package/src/client-go/ws.go +104 -0
  36. package/src/client-mcp/README.md +10 -0
  37. package/src/client-mcp/server.ts +94 -0
  38. package/src/client-mcp/types.ts +19 -0
  39. package/src/client-python/_errors.py +115 -0
  40. package/src/client-python/_invalidation.py +578 -0
  41. package/src/client-python/_realtime.py +156 -0
  42. package/src/client-python/_runtime.py +469 -0
  43. package/src/client-python/_sse.py +99 -0
  44. package/src/client-python/_transport.py +335 -0
  45. package/src/client-python/_ws.py +131 -0
  46. package/src/client-python/py.typed +0 -0
  47. package/src/client-rust/errors.rs +200 -0
  48. package/src/client-rust/invalidation.rs +441 -0
  49. package/src/client-rust/invalidation_sync.rs +306 -0
  50. package/src/client-rust/realtime.rs +695 -0
  51. package/src/client-rust/result.rs +22 -0
  52. package/src/client-rust/runtime.rs +547 -0
  53. package/src/client-rust/runtime_sync.rs +452 -0
  54. package/src/client-rust/sse.rs +167 -0
  55. package/src/client-rust/ws.rs +143 -0
  56. package/src/cloudflare/realtime.ts +236 -0
  57. package/src/codegen-go-cli.ts +1180 -0
  58. package/src/codegen-go.ts +1010 -0
  59. package/src/codegen-hash.ts +8 -0
  60. package/src/codegen-ir.ts +517 -0
  61. package/src/codegen-loaders.ts +53 -0
  62. package/src/codegen-mcp.ts +364 -0
  63. package/src/codegen-python.ts +1028 -0
  64. package/src/codegen-rust.ts +1652 -0
  65. package/src/codegen-sanitize.ts +67 -0
  66. package/src/codegen-schema-naming.ts +146 -0
  67. package/src/codegen.ts +5015 -0
  68. package/src/context.ts +210 -0
  69. package/src/cookie-sign.ts +58 -0
  70. package/src/cookie.ts +89 -0
  71. package/src/cors.ts +96 -0
  72. package/src/crypto.ts +19 -0
  73. package/src/csrf.ts +54 -0
  74. package/src/curl-logger.ts +235 -0
  75. package/src/detect-runtime.ts +19 -0
  76. package/src/error.ts +52 -0
  77. package/src/errors.ts +87 -0
  78. package/src/etag.ts +74 -0
  79. package/src/feature-load.ts +23 -0
  80. package/src/go-type-emitter.ts +404 -0
  81. package/src/honey-response.ts +354 -0
  82. package/src/i18n-slot.ts +20 -0
  83. package/src/i18n.ts +234 -0
  84. package/src/index.ts +3843 -0
  85. package/src/init.ts +149 -0
  86. package/src/input.ts +8 -0
  87. package/src/ip-restrict.ts +192 -0
  88. package/src/logger.ts +149 -0
  89. package/src/middleware.ts +199 -0
  90. package/src/node-request.ts +240 -0
  91. package/src/node.ts +212 -0
  92. package/src/openapi/register.ts +19 -0
  93. package/src/openapi/scalar.ts +33 -0
  94. package/src/openapi/spec-factory.ts +46 -0
  95. package/src/openapi/spec.ts +31 -0
  96. package/src/openapi/swagger.ts +34 -0
  97. package/src/plugin.ts +771 -0
  98. package/src/powered-by.ts +17 -0
  99. package/src/pretty-json.ts +39 -0
  100. package/src/proxy.ts +155 -0
  101. package/src/python-type-emitter.ts +144 -0
  102. package/src/realtime/buffer.ts +101 -0
  103. package/src/realtime/bus.ts +93 -0
  104. package/src/realtime/longpoll.ts +148 -0
  105. package/src/realtime/protocol.ts +128 -0
  106. package/src/realtime/route.ts +104 -0
  107. package/src/realtime/runtime.ts +430 -0
  108. package/src/request-id.ts +23 -0
  109. package/src/request-to-curl.ts +34 -0
  110. package/src/response.ts +414 -0
  111. package/src/rust-type-emitter.ts +412 -0
  112. package/src/secure-headers.ts +69 -0
  113. package/src/serve-register.ts +8 -0
  114. package/src/serve-slot.ts +20 -0
  115. package/src/serve.ts +141 -0
  116. package/src/server-timing.ts +66 -0
  117. package/src/static.ts +60 -0
  118. package/src/telemetry/otel.ts +139 -0
  119. package/src/testing.ts +114 -0
  120. package/src/timeout.ts +36 -0
  121. package/src/tree.ts +464 -0
  122. package/src/ts-type-emitter.ts +106 -0
  123. package/src/type-emitter.ts +524 -0
  124. package/src/type-extractor.ts +749 -0
  125. package/src/types.ts +638 -0
  126. package/src/validation.ts +300 -0
  127. package/src/ws/bun.ts +89 -0
  128. package/src/ws/cloudflare.ts +133 -0
  129. package/src/ws/deno.ts +109 -0
  130. package/src/ws/node.ts +161 -0
  131. package/src/yaml.ts +80 -0
@@ -0,0 +1,306 @@
1
+ /* Code generated by honey. DO NOT EDIT. */
2
+
3
+ #![allow(dead_code)]
4
+
5
+ /* Sync counterpart of StaleTracker. Uses std::sync::Mutex instead of
6
+ tokio::sync::Mutex so it can be called from blocking SyncClient paths
7
+ without requiring an async runtime. Reuses the shared StaleEntry /
8
+ RequestMeta types + pattern/interpolation helpers from invalidation.rs. */
9
+
10
+ use crate::invalidation::{
11
+ path_matches_pattern, resolve_invalidation_targets_for_mutation, RequestMeta, StaleEntry,
12
+ };
13
+ use crate::runtime::InvalidationConfig;
14
+ use std::collections::HashMap;
15
+ use std::sync::Mutex;
16
+ use std::time::{Duration, Instant};
17
+
18
+ struct TrackerStateSync {
19
+ entries: HashMap<String, StaleEntry>,
20
+ order: Vec<String>,
21
+ seq: u64,
22
+ }
23
+
24
+ /// StaleTrackerSync is the blocking counterpart of StaleTracker.
25
+ /// All public methods are safe for concurrent blocking use (interior std Mutex).
26
+ pub struct StaleTrackerSync {
27
+ state: Mutex<TrackerStateSync>,
28
+ enabled: bool,
29
+ stale_time: Duration,
30
+ max_entries: usize,
31
+ max_sources: usize,
32
+ }
33
+
34
+ impl StaleTrackerSync {
35
+ /// new constructs a tracker from an InvalidationConfig.
36
+ /// Tracker is disabled (cheap no-op) when cfg is None or stale_time ≤ 0.
37
+ pub fn new(cfg: Option<&InvalidationConfig>) -> Self {
38
+ let mut enabled = false;
39
+ let mut stale_time = Duration::from_millis(0);
40
+ let mut max_entries = 1000usize;
41
+ let mut max_sources = 16usize;
42
+ if let Some(c) = cfg {
43
+ if c.stale_time > 0 {
44
+ enabled = true;
45
+ stale_time = Duration::from_millis(c.stale_time as u64);
46
+ if c.stale_max_entries > 0 {
47
+ max_entries = c.stale_max_entries;
48
+ }
49
+ if c.max_sources_per_target > 0 {
50
+ max_sources = c.max_sources_per_target;
51
+ }
52
+ }
53
+ }
54
+ StaleTrackerSync {
55
+ state: Mutex::new(TrackerStateSync {
56
+ entries: HashMap::new(),
57
+ order: Vec::new(),
58
+ seq: 0,
59
+ }),
60
+ enabled,
61
+ stale_time,
62
+ max_entries,
63
+ max_sources,
64
+ }
65
+ }
66
+
67
+ /// enabled reports whether the tracker will record / report stale entries.
68
+ pub fn enabled(&self) -> bool {
69
+ self.enabled
70
+ }
71
+
72
+ /// mark_stale bumps seq, expands templated invalidation entries with
73
+ /// `params`, and records each resolved target as freshly stale.
74
+ pub fn mark_stale(
75
+ &self,
76
+ invalidate: &[String],
77
+ params: &HashMap<String, String>,
78
+ mutation_selector: &str,
79
+ ) {
80
+ if !self.enabled || invalidate.is_empty() {
81
+ return;
82
+ }
83
+ let mut s = self.state.lock().unwrap();
84
+ s.seq += 1;
85
+ let seq = s.seq;
86
+ let until = Instant::now() + self.stale_time;
87
+ let resolved = resolve_invalidation_targets_for_mutation(invalidate, params);
88
+ for target in resolved {
89
+ upsert_locked(&mut s, &target, until, seq, mutation_selector, self.max_sources);
90
+ }
91
+ enforce_capacity_locked(&mut s, self.max_entries);
92
+ }
93
+
94
+ /// lookup_stale returns (invalidated_by, is_stale): exact + pattern lookup
95
+ /// with expired-key sweep. Disabled tracker → (vec![], false).
96
+ pub fn lookup_stale(
97
+ &self,
98
+ concrete_selector: &str,
99
+ concrete_path: &str,
100
+ method: &str,
101
+ now: Instant,
102
+ ) -> (Vec<String>, bool) {
103
+ if !self.enabled {
104
+ return (Vec::new(), false);
105
+ }
106
+ let mut s = self.state.lock().unwrap();
107
+ lookup_locked(&mut s, concrete_selector, concrete_path, method, now)
108
+ }
109
+
110
+ /// clear_stale drops stale entries whose seq ≤ seq_snapshot and which match
111
+ /// the given concrete_path under method (exact key + pattern keys).
112
+ pub fn clear_stale(
113
+ &self,
114
+ concrete_selector: &str,
115
+ concrete_path: &str,
116
+ method: &str,
117
+ seq_snapshot: u64,
118
+ ) {
119
+ if !self.enabled {
120
+ return;
121
+ }
122
+ let mut s = self.state.lock().unwrap();
123
+ if let Some(entry) = s.entries.get(concrete_selector) {
124
+ if entry.seq <= seq_snapshot {
125
+ delete_locked(&mut s, concrete_selector);
126
+ }
127
+ }
128
+ let mut to_delete: Vec<String> = Vec::new();
129
+ for (key, entry) in s.entries.iter() {
130
+ if entry.seq > seq_snapshot {
131
+ continue;
132
+ }
133
+ if !key.contains('{') && !key.contains(':') {
134
+ continue;
135
+ }
136
+ let space_idx = match key.find(' ') {
137
+ Some(i) => i,
138
+ None => continue,
139
+ };
140
+ if &key[..space_idx] != method {
141
+ continue;
142
+ }
143
+ if path_matches_pattern(concrete_path, &key[space_idx + 1..]) {
144
+ to_delete.push(key.clone());
145
+ }
146
+ }
147
+ for key in to_delete {
148
+ delete_locked(&mut s, &key);
149
+ }
150
+ }
151
+
152
+ /// is_stale reports whether (method, path) currently sits inside an active
153
+ /// stale window. Walks pattern entries too. Disabled tracker → false.
154
+ pub fn is_stale(&self, method: &str, path: &str) -> bool {
155
+ if !self.enabled {
156
+ return false;
157
+ }
158
+ let mut s = self.state.lock().unwrap();
159
+ let selector = format!("{} {}", method, path);
160
+ let (_, is_stale) = lookup_locked(&mut s, &selector, path, method, Instant::now());
161
+ is_stale
162
+ }
163
+
164
+ /// build_request_meta returns a RequestMeta snapshot for the request about
165
+ /// to fire, or None when tracker is disabled.
166
+ pub fn build_request_meta(
167
+ &self,
168
+ concrete_selector: &str,
169
+ concrete_path: &str,
170
+ method: &str,
171
+ ) -> Option<RequestMeta> {
172
+ if !self.enabled {
173
+ return None;
174
+ }
175
+ let mut s = self.state.lock().unwrap();
176
+ let (by, is_stale) =
177
+ lookup_locked(&mut s, concrete_selector, concrete_path, method, Instant::now());
178
+ Some(RequestMeta {
179
+ selector: concrete_selector.to_string(),
180
+ is_stale,
181
+ invalidated_by: by,
182
+ seq_snapshot: s.seq,
183
+ })
184
+ }
185
+ }
186
+
187
+ fn upsert_locked(
188
+ s: &mut TrackerStateSync,
189
+ key: &str,
190
+ until: Instant,
191
+ seq: u64,
192
+ mutation_selector: &str,
193
+ max_sources: usize,
194
+ ) {
195
+ if let Some(existing) = s.entries.get_mut(key) {
196
+ existing.until = until;
197
+ existing.seq = seq;
198
+ if !mutation_selector.is_empty() && existing.by.len() < max_sources {
199
+ let already = existing.by.iter().any(|b| b == mutation_selector);
200
+ if !already {
201
+ existing.by.push(mutation_selector.to_string());
202
+ }
203
+ }
204
+ touch_order_locked(s, key);
205
+ return;
206
+ }
207
+ let by = if mutation_selector.is_empty() {
208
+ Vec::new()
209
+ } else {
210
+ vec![mutation_selector.to_string()]
211
+ };
212
+ s.entries.insert(key.to_string(), StaleEntry { by, seq, until });
213
+ s.order.push(key.to_string());
214
+ }
215
+
216
+ fn touch_order_locked(s: &mut TrackerStateSync, key: &str) {
217
+ if let Some(idx) = s.order.iter().position(|k| k == key) {
218
+ s.order.remove(idx);
219
+ }
220
+ s.order.push(key.to_string());
221
+ }
222
+
223
+ fn delete_locked(s: &mut TrackerStateSync, key: &str) {
224
+ if s.entries.remove(key).is_none() {
225
+ return;
226
+ }
227
+ if let Some(idx) = s.order.iter().position(|k| k == key) {
228
+ s.order.remove(idx);
229
+ }
230
+ }
231
+
232
+ fn lookup_locked(
233
+ s: &mut TrackerStateSync,
234
+ concrete_selector: &str,
235
+ concrete_path: &str,
236
+ method: &str,
237
+ now: Instant,
238
+ ) -> (Vec<String>, bool) {
239
+ let mut all_by: Vec<String> = Vec::new();
240
+ let mut exact_expired = false;
241
+ if let Some(exact) = s.entries.get(concrete_selector) {
242
+ if exact.until > now {
243
+ all_by.extend(exact.by.iter().cloned());
244
+ } else {
245
+ exact_expired = true;
246
+ }
247
+ }
248
+ if exact_expired {
249
+ delete_locked(s, concrete_selector);
250
+ }
251
+ let mut expired: Vec<String> = Vec::new();
252
+ for (key, entry) in s.entries.iter() {
253
+ if entry.until <= now {
254
+ expired.push(key.clone());
255
+ continue;
256
+ }
257
+ if key == concrete_selector {
258
+ continue;
259
+ }
260
+ if !key.contains('{') && !key.contains(':') {
261
+ continue;
262
+ }
263
+ let space_idx = match key.find(' ') {
264
+ Some(i) => i,
265
+ None => continue,
266
+ };
267
+ if &key[..space_idx] != method {
268
+ continue;
269
+ }
270
+ if path_matches_pattern(concrete_path, &key[space_idx + 1..]) {
271
+ all_by.extend(entry.by.iter().cloned());
272
+ }
273
+ }
274
+ for key in expired {
275
+ delete_locked(s, &key);
276
+ }
277
+ let mut deduped: Vec<String> = Vec::with_capacity(all_by.len());
278
+ for src in all_by {
279
+ if !deduped.iter().any(|d| d == &src) {
280
+ deduped.push(src);
281
+ }
282
+ }
283
+ let any = !deduped.is_empty();
284
+ (deduped, any)
285
+ }
286
+
287
+ fn enforce_capacity_locked(s: &mut TrackerStateSync, max_entries: usize) {
288
+ if s.entries.len() <= max_entries {
289
+ return;
290
+ }
291
+ let now = Instant::now();
292
+ let expired: Vec<String> = s
293
+ .entries
294
+ .iter()
295
+ .filter(|(_, e)| e.until <= now)
296
+ .map(|(k, _)| k.clone())
297
+ .collect();
298
+ for key in expired {
299
+ delete_locked(s, &key);
300
+ }
301
+ let target = if max_entries / 2 < 1 { 1 } else { max_entries / 2 };
302
+ while s.entries.len() > target && !s.order.is_empty() {
303
+ let oldest = s.order[0].clone();
304
+ delete_locked(s, &oldest);
305
+ }
306
+ }