veryfront 0.1.911 → 0.1.913

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.
package/esm/deno.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  "name": "veryfront",
3
- "version": "0.1.911",
3
+ "version": "0.1.913",
4
4
  "license": "Apache-2.0",
5
5
  "nodeModulesDir": "auto",
6
6
  "minimumDependencyAge": {
@@ -26,6 +26,7 @@ export declare const metrics: {
26
26
  counter: typeof counter;
27
27
  histogram: typeof histogram;
28
28
  gauge: typeof gauge;
29
+ __flushForTests(): Promise<void>;
29
30
  __resetForTests(): void;
30
31
  };
31
32
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/src/metrics/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,yBAAyB,CAAC;AAYjC,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;AAChF,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;AAEpE,MAAM,WAAW,uBAAuB;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AA0FD,wBAAgB,OAAO,CACrB,IAAI,EAAE,MAAM,EACZ,KAAK,SAAI,EACT,UAAU,CAAC,EAAE,gBAAgB,EAC7B,OAAO,CAAC,EAAE,uBAAuB,GAChC,IAAI,CAEN;AAED,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,UAAU,CAAC,EAAE,gBAAgB,EAC7B,OAAO,CAAC,EAAE,uBAAuB,GAChC,IAAI,CAEN;AAED,wBAAgB,KAAK,CACnB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,UAAU,CAAC,EAAE,gBAAgB,EAC7B,OAAO,CAAC,EAAE,uBAAuB,GAChC,IAAI,CASN;AAED,eAAO,MAAM,OAAO;;;;uBAIC,IAAI;CAKxB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/src/metrics/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,yBAAyB,CAAC;AAcjC,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;AAChF,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;AAEpE,MAAM,WAAW,uBAAuB;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAuWD,wBAAgB,OAAO,CACrB,IAAI,EAAE,MAAM,EACZ,KAAK,SAAI,EACT,UAAU,CAAC,EAAE,gBAAgB,EAC7B,OAAO,CAAC,EAAE,uBAAuB,GAChC,IAAI,CAON;AAED,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,UAAU,CAAC,EAAE,gBAAgB,EAC7B,OAAO,CAAC,EAAE,uBAAuB,GAChC,IAAI,CAON;AAED,wBAAgB,KAAK,CACnB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,UAAU,CAAC,EAAE,gBAAgB,EAC7B,OAAO,CAAC,EAAE,uBAAuB,GAChC,IAAI,CAaN;AAED,eAAO,MAAM,OAAO;;;;uBAIO,OAAO,CAAC,IAAI,CAAC;uBAGnB,IAAI;CAWxB,CAAC"}
@@ -13,11 +13,18 @@
13
13
  * ```
14
14
  */
15
15
  import "../../_dnt.polyfills.js";
16
+ import * as dntShim from "../../_dnt.shims.js";
16
17
  import { getGlobalMetricsAPI, } from "../observability/tracing/api-shim.js";
17
18
  import { getCurrentRequestContext } from "../platform/adapters/fs/veryfront/request-context.js";
18
19
  const counters = new Map();
19
20
  const histograms = new Map();
20
21
  const gauges = new Map();
22
+ const directQueue = [];
23
+ const directCounterTotals = new Map();
24
+ let directFlushTimer = null;
25
+ const DIRECT_FLUSH_DELAY_MS = 1_000;
26
+ const DIRECT_MAX_BATCH_SIZE = 100;
27
+ const HISTOGRAM_BOUNDS = [0, 10, 50, 100, 250, 500, 1_000, 2_500, 5_000, 10_000];
21
28
  function getMeter() {
22
29
  return getGlobalMetricsAPI()?.getMeter("veryfront.project.metrics");
23
30
  }
@@ -88,14 +95,259 @@ function getGauge(name, options) {
88
95
  gauges.set(name, gauge);
89
96
  return gauge;
90
97
  }
98
+ function readEnv(name) {
99
+ try {
100
+ return dntShim.Deno.env.get(name);
101
+ }
102
+ catch {
103
+ return undefined;
104
+ }
105
+ }
106
+ function resolveOtlpMetricsUrl() {
107
+ if (readEnv("OTEL_METRICS_ENABLED") !== "true")
108
+ return null;
109
+ const endpoint = readEnv("OTEL_EXPORTER_OTLP_METRICS_ENDPOINT") ??
110
+ readEnv("OTEL_EXPORTER_OTLP_ENDPOINT");
111
+ if (!endpoint)
112
+ return null;
113
+ const trimmed = endpoint.replace(/\/$/, "");
114
+ return trimmed.endsWith("/v1/metrics") ? trimmed : `${trimmed}/v1/metrics`;
115
+ }
116
+ function resolveInternalMetricsUrl() {
117
+ if (readEnv("OTEL_METRICS_ENABLED") !== "true")
118
+ return null;
119
+ const apiBaseUrl = readEnv("VERYFRONT_API_BASE_URL") ?? readEnv("VERYFRONT_API_URL");
120
+ const username = readEnv("VERYFRONT_API_INTERNAL_USER");
121
+ const password = readEnv("VERYFRONT_API_INTERNAL_PASS");
122
+ if (!apiBaseUrl || !username || !password)
123
+ return null;
124
+ return `${apiBaseUrl.replace(/\/$/, "")}/internal/metrics/otlp/v1/metrics`;
125
+ }
126
+ function buildBasicAuth(username, password) {
127
+ const credentials = `${username}:${password}`;
128
+ return `Basic ${globalThis.btoa(credentials)}`;
129
+ }
130
+ function parseHeaders(headerInput) {
131
+ if (!headerInput)
132
+ return {};
133
+ if (headerInput.startsWith("Basic "))
134
+ return { Authorization: headerInput };
135
+ if (headerInput.startsWith("Authorization=")) {
136
+ return { Authorization: headerInput.slice("Authorization=".length) };
137
+ }
138
+ const result = {};
139
+ for (const part of headerInput.split(",")) {
140
+ const [key, ...valueParts] = part.split("=");
141
+ if (key && valueParts.length > 0) {
142
+ result[key.trim()] = valueParts.join("=").trim();
143
+ }
144
+ }
145
+ return result;
146
+ }
147
+ function resolveDirectMetricsTarget() {
148
+ const internalUrl = resolveInternalMetricsUrl();
149
+ if (internalUrl) {
150
+ return {
151
+ url: internalUrl,
152
+ headers: {
153
+ Authorization: buildBasicAuth(readEnv("VERYFRONT_API_INTERNAL_USER") ?? "", readEnv("VERYFRONT_API_INTERNAL_PASS") ?? ""),
154
+ },
155
+ };
156
+ }
157
+ const otlpUrl = resolveOtlpMetricsUrl();
158
+ if (!otlpUrl)
159
+ return null;
160
+ return {
161
+ url: otlpUrl,
162
+ headers: parseHeaders(readEnv("OTEL_EXPORTER_OTLP_HEADERS")),
163
+ };
164
+ }
165
+ function toOtlpValue(value) {
166
+ if (typeof value === "boolean")
167
+ return { boolValue: value };
168
+ if (typeof value === "number")
169
+ return { doubleValue: value };
170
+ return { stringValue: String(value) };
171
+ }
172
+ function toOtlpAttributes(attributes) {
173
+ return Object.entries(attributes).map(([key, value]) => ({
174
+ key,
175
+ value: toOtlpValue(value),
176
+ }));
177
+ }
178
+ function getUnixNanoTimestamp() {
179
+ return String(BigInt(Date.now()) * 1000000n);
180
+ }
181
+ function buildHistogramBuckets(value) {
182
+ const counts = new Array(HISTOGRAM_BOUNDS.length + 1).fill(0);
183
+ const bucketIndex = HISTOGRAM_BOUNDS.findIndex((bound) => value <= bound);
184
+ counts[bucketIndex === -1 ? counts.length - 1 : bucketIndex] = 1;
185
+ return counts;
186
+ }
187
+ function buildDirectMetric(sample) {
188
+ const attributes = toOtlpAttributes(sample.attributes);
189
+ if (sample.kind === "counter") {
190
+ const key = `${sample.name}:${attributesKey(sample.attributes)}`;
191
+ const total = directCounterTotals.get(key) ?? {
192
+ value: 0,
193
+ startTimeUnixNano: sample.timestampUnixNano,
194
+ };
195
+ total.value += sample.value;
196
+ directCounterTotals.set(key, total);
197
+ return {
198
+ name: sample.name,
199
+ sum: {
200
+ dataPoints: [{
201
+ attributes,
202
+ startTimeUnixNano: total.startTimeUnixNano,
203
+ timeUnixNano: sample.timestampUnixNano,
204
+ asDouble: total.value,
205
+ }],
206
+ aggregationTemporality: 2,
207
+ isMonotonic: true,
208
+ },
209
+ };
210
+ }
211
+ if (sample.kind === "histogram") {
212
+ return {
213
+ name: sample.name,
214
+ histogram: {
215
+ dataPoints: [{
216
+ attributes,
217
+ startTimeUnixNano: sample.timestampUnixNano,
218
+ timeUnixNano: sample.timestampUnixNano,
219
+ count: 1,
220
+ sum: sample.value,
221
+ explicitBounds: HISTOGRAM_BOUNDS,
222
+ bucketCounts: buildHistogramBuckets(sample.value),
223
+ }],
224
+ aggregationTemporality: 1,
225
+ },
226
+ };
227
+ }
228
+ return {
229
+ name: sample.name,
230
+ gauge: {
231
+ dataPoints: [{
232
+ attributes,
233
+ timeUnixNano: sample.timestampUnixNano,
234
+ asDouble: sample.value,
235
+ }],
236
+ },
237
+ };
238
+ }
239
+ function buildDirectOtlpBody(samples) {
240
+ return {
241
+ resourceMetrics: [{
242
+ resource: {
243
+ attributes: toOtlpAttributes({
244
+ "service.name": readEnv("OTEL_SERVICE_NAME") ?? "veryfront",
245
+ "service.version": readEnv("VERYFRONT_VERSION") ??
246
+ readEnv("RELEASE_VERSION") ??
247
+ "unknown",
248
+ }),
249
+ },
250
+ scopeMetrics: [{
251
+ scope: {
252
+ name: "veryfront.project.metrics",
253
+ },
254
+ metrics: samples.map(buildDirectMetric),
255
+ }],
256
+ }],
257
+ };
258
+ }
259
+ function logDirectExportFailure(error) {
260
+ if (readEnv("VERYFRONT_DEBUG") !== "1")
261
+ return;
262
+ console.warn("[metrics] direct OTLP export failed", error);
263
+ }
264
+ async function flushDirectMetrics() {
265
+ if (directFlushTimer) {
266
+ clearTimeout(directFlushTimer);
267
+ directFlushTimer = null;
268
+ }
269
+ const target = resolveDirectMetricsTarget();
270
+ if (!target || directQueue.length === 0) {
271
+ directQueue.length = 0;
272
+ return;
273
+ }
274
+ const batch = directQueue.splice(0, DIRECT_MAX_BATCH_SIZE);
275
+ try {
276
+ const response = await fetch(target.url, {
277
+ method: "POST",
278
+ headers: {
279
+ ...target.headers,
280
+ "Content-Type": "application/json",
281
+ },
282
+ body: JSON.stringify(buildDirectOtlpBody(batch)),
283
+ });
284
+ if (!response.ok) {
285
+ logDirectExportFailure(`HTTP ${response.status}`);
286
+ }
287
+ }
288
+ catch (error) {
289
+ logDirectExportFailure(error);
290
+ }
291
+ if (directQueue.length > 0) {
292
+ scheduleDirectFlush();
293
+ }
294
+ }
295
+ function scheduleDirectFlush() {
296
+ if (directFlushTimer || resolveDirectMetricsTarget() === null)
297
+ return;
298
+ directFlushTimer = dntShim.setTimeout(() => {
299
+ void flushDirectMetrics();
300
+ }, DIRECT_FLUSH_DELAY_MS);
301
+ try {
302
+ if (typeof directFlushTimer === "number") {
303
+ dntShim.Deno.unrefTimer(directFlushTimer);
304
+ }
305
+ else {
306
+ directFlushTimer.unref?.();
307
+ }
308
+ }
309
+ catch {
310
+ // Some runtimes do not expose unref support; exporting still works there.
311
+ }
312
+ }
313
+ function enqueueDirectMetric(kind, name, value, attributes) {
314
+ if (resolveDirectMetricsTarget() === null)
315
+ return;
316
+ directQueue.push({
317
+ kind,
318
+ name,
319
+ value,
320
+ attributes,
321
+ timestampUnixNano: getUnixNanoTimestamp(),
322
+ });
323
+ if (directQueue.length >= DIRECT_MAX_BATCH_SIZE) {
324
+ void flushDirectMetrics();
325
+ return;
326
+ }
327
+ scheduleDirectFlush();
328
+ }
91
329
  export function counter(name, value = 1, attributes, options) {
92
- getCounter(name, options)?.add(value, normalizeAttributes(attributes));
330
+ const normalizedAttributes = normalizeAttributes(attributes);
331
+ if (resolveDirectMetricsTarget() === null) {
332
+ getCounter(name, options)?.add(value, normalizedAttributes);
333
+ return;
334
+ }
335
+ enqueueDirectMetric("counter", name, value, normalizedAttributes);
93
336
  }
94
337
  export function histogram(name, value, attributes, options) {
95
- getHistogram(name, options)?.record(value, normalizeAttributes(attributes));
338
+ const normalizedAttributes = normalizeAttributes(attributes);
339
+ if (resolveDirectMetricsTarget() === null) {
340
+ getHistogram(name, options)?.record(value, normalizedAttributes);
341
+ return;
342
+ }
343
+ enqueueDirectMetric("histogram", name, value, normalizedAttributes);
96
344
  }
97
345
  export function gauge(name, value, attributes, options) {
98
346
  const normalizedAttributes = normalizeAttributes(attributes);
347
+ if (resolveDirectMetricsTarget() !== null) {
348
+ enqueueDirectMetric("gauge", name, value, normalizedAttributes);
349
+ return;
350
+ }
99
351
  const target = getGauge(name, options);
100
352
  if (!target)
101
353
  return;
@@ -108,9 +360,18 @@ export const metrics = {
108
360
  counter,
109
361
  histogram,
110
362
  gauge,
363
+ async __flushForTests() {
364
+ await flushDirectMetrics();
365
+ },
111
366
  __resetForTests() {
112
367
  counters.clear();
113
368
  histograms.clear();
114
369
  gauges.clear();
370
+ directQueue.length = 0;
371
+ directCounterTotals.clear();
372
+ if (directFlushTimer) {
373
+ clearTimeout(directFlushTimer);
374
+ directFlushTimer = null;
375
+ }
115
376
  },
116
377
  };
@@ -1,3 +1,3 @@
1
1
  /** Shared version value. */
2
- export declare const VERSION = "0.1.911";
2
+ export declare const VERSION = "0.1.913";
3
3
  //# sourceMappingURL=version-constant.d.ts.map
@@ -1,4 +1,4 @@
1
1
  // Keep in sync with deno.json version.
2
2
  // scripts/release.ts updates this constant during releases.
3
3
  /** Shared version value. */
4
- export const VERSION = "0.1.911";
4
+ export const VERSION = "0.1.913";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "veryfront",
3
- "version": "0.1.911",
3
+ "version": "0.1.913",
4
4
  "description": "The simplest way to build AI-powered apps",
5
5
  "keywords": [
6
6
  "react",