veryfront 0.1.910 → 0.1.912

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.910",
3
+ "version": "0.1.912",
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;AAsFD,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;AA8TD,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
  }
@@ -33,10 +40,14 @@ function normalizeAttributes(attributes) {
33
40
  normalized.project_id = context.projectId;
34
41
  if (context?.projectSlug)
35
42
  normalized.project_slug = context.projectSlug;
36
- if (context?.environmentName)
37
- normalized.environment = context.environmentName;
38
- if (context && !context.productionMode)
39
- normalized.branch = context.branch ?? "main";
43
+ if (context) {
44
+ const environmentName = context.environmentName ??
45
+ (!context.productionMode ? "preview" : undefined);
46
+ if (environmentName)
47
+ normalized.environment = environmentName;
48
+ if (!context.productionMode)
49
+ normalized.branch = context.branch ?? "main";
50
+ }
40
51
  return normalized;
41
52
  }
42
53
  function attributesKey(attributes) {
@@ -84,14 +95,227 @@ function getGauge(name, options) {
84
95
  gauges.set(name, gauge);
85
96
  return gauge;
86
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 parseHeaders(headerInput) {
117
+ if (!headerInput)
118
+ return {};
119
+ if (headerInput.startsWith("Basic "))
120
+ return { Authorization: headerInput };
121
+ if (headerInput.startsWith("Authorization=")) {
122
+ return { Authorization: headerInput.slice("Authorization=".length) };
123
+ }
124
+ const result = {};
125
+ for (const part of headerInput.split(",")) {
126
+ const [key, ...valueParts] = part.split("=");
127
+ if (key && valueParts.length > 0) {
128
+ result[key.trim()] = valueParts.join("=").trim();
129
+ }
130
+ }
131
+ return result;
132
+ }
133
+ function toOtlpValue(value) {
134
+ if (typeof value === "boolean")
135
+ return { boolValue: value };
136
+ if (typeof value === "number")
137
+ return { doubleValue: value };
138
+ return { stringValue: String(value) };
139
+ }
140
+ function toOtlpAttributes(attributes) {
141
+ return Object.entries(attributes).map(([key, value]) => ({
142
+ key,
143
+ value: toOtlpValue(value),
144
+ }));
145
+ }
146
+ function getUnixNanoTimestamp() {
147
+ return String(BigInt(Date.now()) * 1000000n);
148
+ }
149
+ function buildHistogramBuckets(value) {
150
+ const counts = new Array(HISTOGRAM_BOUNDS.length + 1).fill(0);
151
+ const bucketIndex = HISTOGRAM_BOUNDS.findIndex((bound) => value <= bound);
152
+ counts[bucketIndex === -1 ? counts.length - 1 : bucketIndex] = 1;
153
+ return counts;
154
+ }
155
+ function buildDirectMetric(sample) {
156
+ const attributes = toOtlpAttributes(sample.attributes);
157
+ if (sample.kind === "counter") {
158
+ const key = `${sample.name}:${attributesKey(sample.attributes)}`;
159
+ const total = directCounterTotals.get(key) ?? {
160
+ value: 0,
161
+ startTimeUnixNano: sample.timestampUnixNano,
162
+ };
163
+ total.value += sample.value;
164
+ directCounterTotals.set(key, total);
165
+ return {
166
+ name: sample.name,
167
+ sum: {
168
+ dataPoints: [{
169
+ attributes,
170
+ startTimeUnixNano: total.startTimeUnixNano,
171
+ timeUnixNano: sample.timestampUnixNano,
172
+ asDouble: total.value,
173
+ }],
174
+ aggregationTemporality: 2,
175
+ isMonotonic: true,
176
+ },
177
+ };
178
+ }
179
+ if (sample.kind === "histogram") {
180
+ return {
181
+ name: sample.name,
182
+ histogram: {
183
+ dataPoints: [{
184
+ attributes,
185
+ startTimeUnixNano: sample.timestampUnixNano,
186
+ timeUnixNano: sample.timestampUnixNano,
187
+ count: 1,
188
+ sum: sample.value,
189
+ explicitBounds: HISTOGRAM_BOUNDS,
190
+ bucketCounts: buildHistogramBuckets(sample.value),
191
+ }],
192
+ aggregationTemporality: 1,
193
+ },
194
+ };
195
+ }
196
+ return {
197
+ name: sample.name,
198
+ gauge: {
199
+ dataPoints: [{
200
+ attributes,
201
+ timeUnixNano: sample.timestampUnixNano,
202
+ asDouble: sample.value,
203
+ }],
204
+ },
205
+ };
206
+ }
207
+ function buildDirectOtlpBody(samples) {
208
+ return {
209
+ resourceMetrics: [{
210
+ resource: {
211
+ attributes: toOtlpAttributes({
212
+ "service.name": readEnv("OTEL_SERVICE_NAME") ?? "veryfront",
213
+ "service.version": readEnv("VERYFRONT_VERSION") ??
214
+ readEnv("RELEASE_VERSION") ??
215
+ "unknown",
216
+ }),
217
+ },
218
+ scopeMetrics: [{
219
+ scope: {
220
+ name: "veryfront.project.metrics",
221
+ },
222
+ metrics: samples.map(buildDirectMetric),
223
+ }],
224
+ }],
225
+ };
226
+ }
227
+ function logDirectExportFailure(error) {
228
+ if (readEnv("VERYFRONT_DEBUG") !== "1")
229
+ return;
230
+ console.warn("[metrics] direct OTLP export failed", error);
231
+ }
232
+ async function flushDirectMetrics() {
233
+ if (directFlushTimer) {
234
+ clearTimeout(directFlushTimer);
235
+ directFlushTimer = null;
236
+ }
237
+ const url = resolveOtlpMetricsUrl();
238
+ if (!url || directQueue.length === 0) {
239
+ directQueue.length = 0;
240
+ return;
241
+ }
242
+ const batch = directQueue.splice(0, DIRECT_MAX_BATCH_SIZE);
243
+ try {
244
+ const response = await fetch(url, {
245
+ method: "POST",
246
+ headers: {
247
+ ...parseHeaders(readEnv("OTEL_EXPORTER_OTLP_HEADERS")),
248
+ "Content-Type": "application/json",
249
+ },
250
+ body: JSON.stringify(buildDirectOtlpBody(batch)),
251
+ });
252
+ if (!response.ok) {
253
+ logDirectExportFailure(`HTTP ${response.status}`);
254
+ }
255
+ }
256
+ catch (error) {
257
+ logDirectExportFailure(error);
258
+ }
259
+ if (directQueue.length > 0) {
260
+ scheduleDirectFlush();
261
+ }
262
+ }
263
+ function scheduleDirectFlush() {
264
+ if (directFlushTimer || resolveOtlpMetricsUrl() === null)
265
+ return;
266
+ directFlushTimer = dntShim.setTimeout(() => {
267
+ void flushDirectMetrics();
268
+ }, DIRECT_FLUSH_DELAY_MS);
269
+ try {
270
+ if (typeof directFlushTimer === "number") {
271
+ dntShim.Deno.unrefTimer(directFlushTimer);
272
+ }
273
+ else {
274
+ directFlushTimer.unref?.();
275
+ }
276
+ }
277
+ catch {
278
+ // Some runtimes do not expose unref support; exporting still works there.
279
+ }
280
+ }
281
+ function enqueueDirectMetric(kind, name, value, attributes) {
282
+ if (resolveOtlpMetricsUrl() === null)
283
+ return;
284
+ directQueue.push({
285
+ kind,
286
+ name,
287
+ value,
288
+ attributes,
289
+ timestampUnixNano: getUnixNanoTimestamp(),
290
+ });
291
+ if (directQueue.length >= DIRECT_MAX_BATCH_SIZE) {
292
+ void flushDirectMetrics();
293
+ return;
294
+ }
295
+ scheduleDirectFlush();
296
+ }
87
297
  export function counter(name, value = 1, attributes, options) {
88
- getCounter(name, options)?.add(value, normalizeAttributes(attributes));
298
+ const normalizedAttributes = normalizeAttributes(attributes);
299
+ if (resolveOtlpMetricsUrl() === null) {
300
+ getCounter(name, options)?.add(value, normalizedAttributes);
301
+ return;
302
+ }
303
+ enqueueDirectMetric("counter", name, value, normalizedAttributes);
89
304
  }
90
305
  export function histogram(name, value, attributes, options) {
91
- getHistogram(name, options)?.record(value, normalizeAttributes(attributes));
306
+ const normalizedAttributes = normalizeAttributes(attributes);
307
+ if (resolveOtlpMetricsUrl() === null) {
308
+ getHistogram(name, options)?.record(value, normalizedAttributes);
309
+ return;
310
+ }
311
+ enqueueDirectMetric("histogram", name, value, normalizedAttributes);
92
312
  }
93
313
  export function gauge(name, value, attributes, options) {
94
314
  const normalizedAttributes = normalizeAttributes(attributes);
315
+ if (resolveOtlpMetricsUrl() !== null) {
316
+ enqueueDirectMetric("gauge", name, value, normalizedAttributes);
317
+ return;
318
+ }
95
319
  const target = getGauge(name, options);
96
320
  if (!target)
97
321
  return;
@@ -104,9 +328,18 @@ export const metrics = {
104
328
  counter,
105
329
  histogram,
106
330
  gauge,
331
+ async __flushForTests() {
332
+ await flushDirectMetrics();
333
+ },
107
334
  __resetForTests() {
108
335
  counters.clear();
109
336
  histograms.clear();
110
337
  gauges.clear();
338
+ directQueue.length = 0;
339
+ directCounterTotals.clear();
340
+ if (directFlushTimer) {
341
+ clearTimeout(directFlushTimer);
342
+ directFlushTimer = null;
343
+ }
111
344
  },
112
345
  };
@@ -1,3 +1,3 @@
1
1
  /** Shared version value. */
2
- export declare const VERSION = "0.1.910";
2
+ export declare const VERSION = "0.1.912";
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.910";
4
+ export const VERSION = "0.1.912";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "veryfront",
3
- "version": "0.1.910",
3
+ "version": "0.1.912",
4
4
  "description": "The simplest way to build AI-powered apps",
5
5
  "keywords": [
6
6
  "react",