@krak-stack/registry 0.1.39 → 0.1.41
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/README.md +158 -0
- package/dist/components/ui/data-table.d.ts +27 -10
- package/dist/components/ui/data-table.js +81 -36
- package/dist/components/ui/google-map.d.ts +14 -1
- package/dist/components/ui/google-map.js +97 -18
- package/dist/lib/query.d.ts +30 -3
- package/dist/lib/query.js +13 -3
- package/dist/services/opentelemetry/api.d.ts +1 -1
- package/dist/services/opentelemetry/api.js +165 -13
- package/dist/services/opentelemetry/browser.d.ts +21 -3
- package/dist/services/opentelemetry/browser.js +267 -21
- package/dist/services/opentelemetry/events.d.ts +37 -0
- package/dist/services/opentelemetry/schema.d.ts +44 -0
- package/dist/services/opentelemetry/server.d.ts +20 -0
- package/dist/services/opentelemetry/server.js +154 -0
- package/package.json +5 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// ../../src/components/ui/google-map.tsx
|
|
2
|
-
import { Minus, Plus } from "lucide-react";
|
|
2
|
+
import { LocateFixed, Minus, Plus } from "lucide-react";
|
|
3
3
|
import { useEffect, useEffectEvent, useRef, useState } from "react";
|
|
4
4
|
import { Schema } from "effect";
|
|
5
5
|
|
|
@@ -8,7 +8,7 @@ import { cn } from "cn";
|
|
|
8
8
|
|
|
9
9
|
// ../../src/components/ui/google-map.tsx
|
|
10
10
|
import { useKrakstackLocale } from "@krak-stack/registry/krakstack-provider";
|
|
11
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
12
12
|
var googleMapsScriptId = "krak-stack-google-maps-script";
|
|
13
13
|
var googleMapsCallbackName = "__krakStackGoogleMapsInit";
|
|
14
14
|
var googleMapsPromise;
|
|
@@ -21,7 +21,8 @@ var defaultMessages = {
|
|
|
21
21
|
errorTitle: "Map unavailable",
|
|
22
22
|
errorBody: "Google Maps could not be loaded. Try again later.",
|
|
23
23
|
zoomIn: "Zoom in",
|
|
24
|
-
zoomOut: "Zoom out"
|
|
24
|
+
zoomOut: "Zoom out",
|
|
25
|
+
showArea: "Show selected area"
|
|
25
26
|
},
|
|
26
27
|
fr: {
|
|
27
28
|
mapLabel: "Carte Google",
|
|
@@ -31,7 +32,8 @@ var defaultMessages = {
|
|
|
31
32
|
errorTitle: "Carte indisponible",
|
|
32
33
|
errorBody: "Impossible de charger Google Maps. Réessayez plus tard.",
|
|
33
34
|
zoomIn: "Zoom avant",
|
|
34
|
-
zoomOut: "Zoom arrière"
|
|
35
|
+
zoomOut: "Zoom arrière",
|
|
36
|
+
showArea: "Afficher la zone sélectionnée"
|
|
35
37
|
}
|
|
36
38
|
};
|
|
37
39
|
var googleMapMessages = (locale, translations) => ({
|
|
@@ -104,6 +106,8 @@ function GoogleMap({
|
|
|
104
106
|
radius,
|
|
105
107
|
interactive = true,
|
|
106
108
|
zoomControls = true,
|
|
109
|
+
fitRadius = false,
|
|
110
|
+
fitPadding = 32,
|
|
107
111
|
minZoom = 3,
|
|
108
112
|
maxZoom = 20,
|
|
109
113
|
className,
|
|
@@ -135,6 +139,18 @@ function GoogleMap({
|
|
|
135
139
|
const radiusStrokeColor = radius?.strokeColor;
|
|
136
140
|
const radiusStrokeOpacity = radius?.strokeOpacity;
|
|
137
141
|
const radiusStrokeWeight = radius?.strokeWeight;
|
|
142
|
+
const showSelectedArea = () => {
|
|
143
|
+
try {
|
|
144
|
+
const element = mapElementRef.current;
|
|
145
|
+
const bounds = circleRef.current?.getBounds()?.toJSON();
|
|
146
|
+
if (!element?.clientWidth || !element.clientHeight || !bounds)
|
|
147
|
+
return;
|
|
148
|
+
mapRef.current?.fitBounds(bounds, Math.max(0, fitPadding));
|
|
149
|
+
} catch {
|
|
150
|
+
setLoadState("error");
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
const fitSelection = useEffectEvent(showSelectedArea);
|
|
138
154
|
const initializeMap = useEffectEvent((api, element) => {
|
|
139
155
|
const map = new api.maps.Map(element, mapOptions(center, zoom, interactive, normalizedMinZoom, normalizedMaxZoom));
|
|
140
156
|
apiRef.current = api;
|
|
@@ -182,17 +198,36 @@ function GoogleMap({
|
|
|
182
198
|
};
|
|
183
199
|
}, [normalizedApiKey]);
|
|
184
200
|
useEffect(() => {
|
|
185
|
-
const api = apiRef.current;
|
|
186
201
|
const map = mapRef.current;
|
|
187
|
-
if (loadState !== "ready" || !
|
|
202
|
+
if (loadState !== "ready" || !map)
|
|
188
203
|
return;
|
|
204
|
+
const nextCenter = { lat: centerLat, lng: centerLng };
|
|
205
|
+
const nextZoom = clampZoom(zoom, normalizedMinZoom, normalizedMaxZoom);
|
|
189
206
|
try {
|
|
190
|
-
|
|
191
|
-
const nextZoom = clampZoom(zoom, normalizedMinZoom, normalizedMaxZoom);
|
|
207
|
+
map.setOptions(mapOptions(nextCenter, nextZoom, interactive, normalizedMinZoom, normalizedMaxZoom));
|
|
192
208
|
map.setCenter(nextCenter);
|
|
193
209
|
map.setZoom(nextZoom);
|
|
194
|
-
map.setOptions(mapOptions(nextCenter, nextZoom, interactive, normalizedMinZoom, normalizedMaxZoom));
|
|
195
210
|
setCurrentZoom(nextZoom);
|
|
211
|
+
} catch {
|
|
212
|
+
setLoadState("error");
|
|
213
|
+
}
|
|
214
|
+
}, [
|
|
215
|
+
centerLat,
|
|
216
|
+
centerLng,
|
|
217
|
+
zoom,
|
|
218
|
+
interactive,
|
|
219
|
+
fitRadius,
|
|
220
|
+
normalizedMinZoom,
|
|
221
|
+
normalizedMaxZoom,
|
|
222
|
+
loadState
|
|
223
|
+
]);
|
|
224
|
+
useEffect(() => {
|
|
225
|
+
const api = apiRef.current;
|
|
226
|
+
const map = mapRef.current;
|
|
227
|
+
if (loadState !== "ready" || !api || !map)
|
|
228
|
+
return;
|
|
229
|
+
try {
|
|
230
|
+
const nextCenter = { lat: centerLat, lng: centerLng };
|
|
196
231
|
if (markerEnabled) {
|
|
197
232
|
if (markerRef.current) {
|
|
198
233
|
markerRef.current.setMap(map);
|
|
@@ -241,19 +276,46 @@ function GoogleMap({
|
|
|
241
276
|
}, [
|
|
242
277
|
centerLat,
|
|
243
278
|
centerLng,
|
|
244
|
-
interactive,
|
|
245
279
|
loadState,
|
|
246
280
|
markerEnabled,
|
|
247
281
|
markerIcon,
|
|
248
|
-
normalizedMaxZoom,
|
|
249
|
-
normalizedMinZoom,
|
|
250
282
|
radiusFillColor,
|
|
251
283
|
radiusFillOpacity,
|
|
252
284
|
radiusMeters,
|
|
253
285
|
radiusStrokeColor,
|
|
254
286
|
radiusStrokeOpacity,
|
|
255
|
-
radiusStrokeWeight
|
|
256
|
-
|
|
287
|
+
radiusStrokeWeight
|
|
288
|
+
]);
|
|
289
|
+
useEffect(() => {
|
|
290
|
+
const element = mapElementRef.current;
|
|
291
|
+
if (loadState !== "ready" || !fitRadius || radiusMeters === undefined || !element)
|
|
292
|
+
return;
|
|
293
|
+
fitSelection();
|
|
294
|
+
if (!globalThis.ResizeObserver)
|
|
295
|
+
return;
|
|
296
|
+
let width = element.clientWidth;
|
|
297
|
+
let height = element.clientHeight;
|
|
298
|
+
const observer = new ResizeObserver(() => {
|
|
299
|
+
const nextWidth = element.clientWidth;
|
|
300
|
+
const nextHeight = element.clientHeight;
|
|
301
|
+
if (nextWidth === width && nextHeight === height)
|
|
302
|
+
return;
|
|
303
|
+
width = nextWidth;
|
|
304
|
+
height = nextHeight;
|
|
305
|
+
fitSelection();
|
|
306
|
+
});
|
|
307
|
+
observer.observe(element);
|
|
308
|
+
return () => observer.disconnect();
|
|
309
|
+
}, [
|
|
310
|
+
loadState,
|
|
311
|
+
fitRadius,
|
|
312
|
+
fitPadding,
|
|
313
|
+
interactive,
|
|
314
|
+
centerLat,
|
|
315
|
+
centerLng,
|
|
316
|
+
radiusMeters,
|
|
317
|
+
normalizedMinZoom,
|
|
318
|
+
normalizedMaxZoom
|
|
257
319
|
]);
|
|
258
320
|
const changeZoom = (delta) => {
|
|
259
321
|
const map = mapRef.current;
|
|
@@ -264,7 +326,7 @@ function GoogleMap({
|
|
|
264
326
|
setCurrentZoom(nextZoom);
|
|
265
327
|
};
|
|
266
328
|
return /* @__PURE__ */ jsxs("div", {
|
|
267
|
-
className: cn("bg-muted relative min-h-64 overflow-hidden rounded-lg border", className),
|
|
329
|
+
className: cn("bg-muted relative isolate min-h-64 min-w-0 overflow-hidden rounded-lg border", className),
|
|
268
330
|
children: [
|
|
269
331
|
/* @__PURE__ */ jsx("div", {
|
|
270
332
|
ref: mapElementRef,
|
|
@@ -273,11 +335,28 @@ function GoogleMap({
|
|
|
273
335
|
"aria-label": labels.mapLabel
|
|
274
336
|
}),
|
|
275
337
|
loadState === "ready" && zoomControls ? /* @__PURE__ */ jsxs("div", {
|
|
276
|
-
className: "bg-background/95 absolute
|
|
338
|
+
className: "bg-background/95 absolute top-3 right-3 z-10 overflow-hidden rounded-md border shadow-sm backdrop-blur",
|
|
277
339
|
children: [
|
|
340
|
+
fitRadius && radiusMeters !== undefined ? /* @__PURE__ */ jsxs(Fragment, {
|
|
341
|
+
children: [
|
|
342
|
+
/* @__PURE__ */ jsx("button", {
|
|
343
|
+
type: "button",
|
|
344
|
+
onClick: showSelectedArea,
|
|
345
|
+
"aria-label": labels.showArea,
|
|
346
|
+
title: labels.showArea,
|
|
347
|
+
className: "text-foreground hover:bg-accent focus-visible:ring-ring relative flex size-11 items-center justify-center focus-visible:z-10 focus-visible:ring-2 focus-visible:ring-inset",
|
|
348
|
+
children: /* @__PURE__ */ jsx(LocateFixed, {
|
|
349
|
+
className: "size-4"
|
|
350
|
+
})
|
|
351
|
+
}),
|
|
352
|
+
/* @__PURE__ */ jsx("div", {
|
|
353
|
+
className: "bg-border h-px"
|
|
354
|
+
})
|
|
355
|
+
]
|
|
356
|
+
}) : null,
|
|
278
357
|
/* @__PURE__ */ jsx("button", {
|
|
279
358
|
type: "button",
|
|
280
|
-
className: "text-
|
|
359
|
+
className: "text-foreground hover:bg-accent focus-visible:ring-ring relative flex size-11 items-center justify-center transition-colors focus-visible:z-10 focus-visible:ring-2 focus-visible:ring-inset disabled:pointer-events-none disabled:opacity-50",
|
|
281
360
|
disabled: currentZoom >= normalizedMaxZoom,
|
|
282
361
|
onClick: () => changeZoom(1),
|
|
283
362
|
"aria-label": labels.zoomIn,
|
|
@@ -290,7 +369,7 @@ function GoogleMap({
|
|
|
290
369
|
}),
|
|
291
370
|
/* @__PURE__ */ jsx("button", {
|
|
292
371
|
type: "button",
|
|
293
|
-
className: "text-
|
|
372
|
+
className: "text-foreground hover:bg-accent focus-visible:ring-ring relative flex size-11 items-center justify-center transition-colors focus-visible:z-10 focus-visible:ring-2 focus-visible:ring-inset disabled:pointer-events-none disabled:opacity-50",
|
|
294
373
|
disabled: currentZoom <= normalizedMinZoom,
|
|
295
374
|
onClick: () => changeZoom(-1),
|
|
296
375
|
"aria-label": labels.zoomOut,
|
package/dist/lib/query.d.ts
CHANGED
|
@@ -29,8 +29,8 @@ export declare const SortParams: Schema.declare<readonly {
|
|
|
29
29
|
readonly direction: "asc" | "desc";
|
|
30
30
|
}[]>;
|
|
31
31
|
export declare const Query: Schema.Struct<{
|
|
32
|
-
readonly page: Schema.decodeTo<Schema.Int, Schema.
|
|
33
|
-
readonly pageSize: Schema.decodeTo<Schema.Int, Schema.
|
|
32
|
+
readonly page: Schema.decodeTo<Schema.Int, Schema.optional<Schema.Union<readonly [Schema.Number, Schema.NumberFromString]>>, never, never>;
|
|
33
|
+
readonly pageSize: Schema.decodeTo<Schema.Int, Schema.optional<Schema.Union<readonly [Schema.Number, Schema.NumberFromString]>>, never, never>;
|
|
34
34
|
readonly globalFilter: Schema.optional<Schema.String>;
|
|
35
35
|
readonly sort: Schema.optional<Schema.declare<readonly {
|
|
36
36
|
readonly id: string;
|
|
@@ -42,7 +42,34 @@ export declare const Query: Schema.Struct<{
|
|
|
42
42
|
}>;
|
|
43
43
|
export type QueryType = typeof Query.Type;
|
|
44
44
|
export type QueryEncoded = typeof Query.Encoded;
|
|
45
|
-
export declare const QueryStandard:
|
|
45
|
+
export declare const QueryStandard: import("effect/StandardSchema").StandardSchemaV1<{
|
|
46
|
+
readonly globalFilter?: string | undefined;
|
|
47
|
+
readonly sort?: readonly {
|
|
48
|
+
readonly id: string;
|
|
49
|
+
readonly direction: "asc" | "desc";
|
|
50
|
+
}[] | undefined;
|
|
51
|
+
readonly page?: string | number | undefined;
|
|
52
|
+
readonly pageSize?: string | number | undefined;
|
|
53
|
+
}, {
|
|
54
|
+
readonly globalFilter?: string | undefined;
|
|
55
|
+
readonly sort?: readonly {
|
|
56
|
+
readonly id: string;
|
|
57
|
+
readonly direction: "asc" | "desc";
|
|
58
|
+
}[] | undefined;
|
|
59
|
+
readonly page?: number | undefined;
|
|
60
|
+
readonly pageSize?: number | undefined;
|
|
61
|
+
}> & Schema.Struct<{
|
|
62
|
+
readonly globalFilter: Schema.optional<Schema.String>;
|
|
63
|
+
readonly sort: Schema.optional<Schema.declare<readonly {
|
|
64
|
+
readonly id: string;
|
|
65
|
+
readonly direction: "asc" | "desc";
|
|
66
|
+
}[], readonly {
|
|
67
|
+
readonly id: string;
|
|
68
|
+
readonly direction: "asc" | "desc";
|
|
69
|
+
}[]>>;
|
|
70
|
+
readonly page: Schema.decodeTo<Schema.optional<Schema.Int>, Schema.decodeTo<Schema.Int, Schema.optional<Schema.Union<readonly [Schema.Number, Schema.NumberFromString]>>, never, never>, never, never>;
|
|
71
|
+
readonly pageSize: Schema.decodeTo<Schema.optional<Schema.Int>, Schema.decodeTo<Schema.Int, Schema.optional<Schema.Union<readonly [Schema.Number, Schema.NumberFromString]>>, never, never>, never, never>;
|
|
72
|
+
}>;
|
|
46
73
|
export declare const PaginationMeta: Schema.Struct<{
|
|
47
74
|
readonly page: Schema.Int;
|
|
48
75
|
readonly pageSize: Schema.Int;
|
package/dist/lib/query.js
CHANGED
|
@@ -81,11 +81,11 @@ var SortParams = Schema.declare(Schema.is(SortParamsArray), {
|
|
|
81
81
|
toCodecStringTree: () => Schema.link()(SortParamsFromString, SchemaTransformation.passthrough())
|
|
82
82
|
});
|
|
83
83
|
var Query = Schema.Struct({
|
|
84
|
-
page: Schema.
|
|
84
|
+
page: Schema.optional(Schema.Union([Schema.Number, Schema.NumberFromString]).check(Schema.isInt(), Schema.isGreaterThanOrEqualTo(0))).pipe(Schema.decodeTo(Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)), {
|
|
85
85
|
decode: SchemaGetter.withDefault(Effect.succeed(0)),
|
|
86
86
|
encode: SchemaGetter.transformOptional(Option.filter((page) => page !== 0))
|
|
87
87
|
})),
|
|
88
|
-
pageSize: Schema.
|
|
88
|
+
pageSize: Schema.optional(Schema.Union([Schema.Number, Schema.NumberFromString]).check(Schema.isInt(), Schema.isBetween({ minimum: 1, maximum: 100 }))).pipe(Schema.decodeTo(Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 100 })), {
|
|
89
89
|
decode: SchemaGetter.withDefault(Effect.succeed(20)),
|
|
90
90
|
encode: SchemaGetter.transformOptional(Option.filter((pageSize) => pageSize !== 20))
|
|
91
91
|
})),
|
|
@@ -107,7 +107,17 @@ var Query = Schema.Struct({
|
|
|
107
107
|
}
|
|
108
108
|
]
|
|
109
109
|
});
|
|
110
|
-
var QueryStandard = Schema.toStandardSchemaV1(
|
|
110
|
+
var QueryStandard = Schema.toStandardSchemaV1(Schema.Struct({
|
|
111
|
+
...Query.fields,
|
|
112
|
+
page: Query.fields.page.pipe(Schema.decodeTo(Schema.optional(Schema.Int), {
|
|
113
|
+
decode: SchemaGetter.transform((page) => page === 0 ? undefined : page),
|
|
114
|
+
encode: SchemaGetter.transform((page) => page ?? 0)
|
|
115
|
+
})),
|
|
116
|
+
pageSize: Query.fields.pageSize.pipe(Schema.decodeTo(Schema.optional(Schema.Int), {
|
|
117
|
+
decode: SchemaGetter.transform((pageSize) => pageSize === 20 ? undefined : pageSize),
|
|
118
|
+
encode: SchemaGetter.transform((pageSize) => pageSize ?? 20)
|
|
119
|
+
}))
|
|
120
|
+
}).annotate({ identifier: "QuerySearch" }));
|
|
111
121
|
var PaginationMeta = Schema.Struct({
|
|
112
122
|
page: Schema.Int,
|
|
113
123
|
pageSize: Schema.Int,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Config, Layer } from "effect";
|
|
2
2
|
import { HttpRouter } from "effect/http";
|
|
3
3
|
export declare class HttpApiOtlp {
|
|
4
|
-
static readonly layer: Layer.Layer<import("effect/FileSystem").FileSystem | import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("effect/Path").Path, Config.ConfigError, HttpRouter.HttpRouter>;
|
|
4
|
+
static readonly layer: Layer.Layer<import("effect/FileSystem").FileSystem | import("effect/observability/OtlpExporter").Flusher | import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("./events.js").OtlpEvents | import("effect/Path").Path, Config.ConfigError, HttpRouter.HttpRouter>;
|
|
5
5
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// ../../src/services/opentelemetry/api.ts
|
|
2
|
-
import { Config, Effect, Layer, Schema } from "effect";
|
|
2
|
+
import { Config as Config2, Effect as Effect3, Layer as Layer3, Schema as Schema4 } from "effect";
|
|
3
3
|
import {
|
|
4
|
-
FetchHttpClient,
|
|
4
|
+
FetchHttpClient as FetchHttpClient2,
|
|
5
5
|
HttpClient,
|
|
6
6
|
HttpClientRequest,
|
|
7
7
|
HttpMiddleware,
|
|
@@ -10,15 +10,167 @@ import {
|
|
|
10
10
|
HttpServerRequest,
|
|
11
11
|
HttpServerResponse
|
|
12
12
|
} from "effect/http";
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
|
|
14
|
+
// ../../src/services/opentelemetry/server.ts
|
|
15
|
+
import { Config, Effect as Effect2, Layer as Layer2, Schema as Schema3 } from "effect";
|
|
16
|
+
import { FetchHttpClient } from "effect/http";
|
|
17
|
+
import { Otlp, OtlpExporter as OtlpExporter2, OtlpSerialization as OtlpSerialization2 } from "effect/observability";
|
|
18
|
+
|
|
19
|
+
// ../../src/services/opentelemetry/events.ts
|
|
20
|
+
import {
|
|
21
|
+
Clock,
|
|
22
|
+
Context,
|
|
23
|
+
DateTime,
|
|
24
|
+
Effect,
|
|
25
|
+
Layer,
|
|
26
|
+
Option,
|
|
27
|
+
References,
|
|
28
|
+
Schema as Schema2
|
|
29
|
+
} from "effect";
|
|
30
|
+
import {
|
|
31
|
+
OtlpExporter,
|
|
32
|
+
OtlpResource,
|
|
33
|
+
OtlpSerialization
|
|
34
|
+
} from "effect/observability";
|
|
35
|
+
|
|
36
|
+
// ../../src/services/opentelemetry/schema.ts
|
|
37
|
+
import { Schema } from "effect";
|
|
38
|
+
var EventAttributeValue = Schema.Union([
|
|
39
|
+
Schema.String,
|
|
40
|
+
Schema.Finite,
|
|
41
|
+
Schema.Boolean,
|
|
42
|
+
Schema.Array(Schema.String),
|
|
43
|
+
Schema.Array(Schema.Finite),
|
|
44
|
+
Schema.Array(Schema.Boolean)
|
|
45
|
+
]);
|
|
46
|
+
var EventAttributesSchema = Schema.Record(Schema.String, EventAttributeValue).annotate({ identifier: "EventAttributes" });
|
|
47
|
+
var EventOptionsSchema = Schema.Struct({
|
|
48
|
+
userId: Schema.optional(Schema.NonEmptyString),
|
|
49
|
+
anonymousId: Schema.optional(Schema.NonEmptyString),
|
|
50
|
+
sessionId: Schema.optional(Schema.NonEmptyString),
|
|
51
|
+
messageId: Schema.optional(Schema.NonEmptyString),
|
|
52
|
+
timestamp: Schema.optional(Schema.DateTimeUtc),
|
|
53
|
+
context: Schema.optional(EventAttributesSchema)
|
|
54
|
+
}).annotate({ identifier: "EventOptions" });
|
|
55
|
+
var TelemetryEvent = Schema.Struct({
|
|
56
|
+
name: Schema.NonEmptyString,
|
|
57
|
+
properties: Schema.optional(EventAttributesSchema),
|
|
58
|
+
options: Schema.optional(EventOptionsSchema)
|
|
59
|
+
}).annotate({ identifier: "TelemetryEvent" });
|
|
60
|
+
var PageEvent = Schema.Struct({
|
|
61
|
+
name: Schema.optional(Schema.NonEmptyString),
|
|
62
|
+
category: Schema.optional(Schema.NonEmptyString),
|
|
63
|
+
path: Schema.optional(Schema.NonEmptyString),
|
|
64
|
+
properties: Schema.optional(EventAttributesSchema),
|
|
65
|
+
options: Schema.optional(EventOptionsSchema)
|
|
66
|
+
}).annotate({ identifier: "PageEvent" });
|
|
67
|
+
var BrowserIdentity = Schema.Struct({
|
|
68
|
+
userId: Schema.optional(Schema.NonEmptyString),
|
|
69
|
+
organizationId: Schema.optional(Schema.NonEmptyString)
|
|
70
|
+
}).annotate({ identifier: "BrowserTelemetryIdentity" });
|
|
71
|
+
|
|
72
|
+
// ../../src/services/opentelemetry/events.ts
|
|
73
|
+
class OtlpEvents extends Context.Service()("krakstack/opentelemetry/Events") {
|
|
74
|
+
static disabledLayer = Layer.succeed(this, { push: () => {} });
|
|
75
|
+
static layer = (options) => Layer.effect(this, Effect.gen(function* () {
|
|
76
|
+
const serialization = yield* OtlpSerialization.OtlpSerialization;
|
|
77
|
+
const resource = yield* OtlpResource.fromConfig(options.resource);
|
|
78
|
+
return yield* OtlpExporter.make({
|
|
79
|
+
...options,
|
|
80
|
+
label: "OtlpEvents",
|
|
81
|
+
body: (logRecords) => [
|
|
82
|
+
serialization.logs({
|
|
83
|
+
resourceLogs: [
|
|
84
|
+
{
|
|
85
|
+
resource,
|
|
86
|
+
scopeLogs: [
|
|
87
|
+
{
|
|
88
|
+
scope: { name: "krakstack.events" },
|
|
89
|
+
logRecords
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
]
|
|
94
|
+
}),
|
|
95
|
+
Effect.void
|
|
96
|
+
]
|
|
97
|
+
});
|
|
98
|
+
}));
|
|
99
|
+
}
|
|
100
|
+
var track = Effect.fnUntraced(function* (name, properties, options) {
|
|
101
|
+
const event = yield* Schema2.decodeUnknownEffect(TelemetryEvent)({
|
|
102
|
+
name,
|
|
103
|
+
properties,
|
|
104
|
+
options
|
|
105
|
+
});
|
|
106
|
+
const events = yield* OtlpEvents;
|
|
107
|
+
const clock = yield* Clock.Clock;
|
|
108
|
+
const observedTime = clock.currentTimeNanosUnsafe().toString();
|
|
109
|
+
const timestamp = event.options?.timestamp ? (BigInt(DateTime.toEpochMillis(event.options.timestamp)) * 1000000n).toString() : observedTime;
|
|
110
|
+
const span = yield* Effect.currentSpan.pipe(Effect.option);
|
|
111
|
+
const attributes = { ...event.options?.context, ...event.properties };
|
|
112
|
+
if (event.options?.userId)
|
|
113
|
+
attributes["user.id"] = event.options.userId;
|
|
114
|
+
if (event.options?.anonymousId)
|
|
115
|
+
attributes["analytics.anonymous_id"] = event.options.anonymousId;
|
|
116
|
+
if (event.options?.sessionId)
|
|
117
|
+
attributes["session.id"] = event.options.sessionId;
|
|
118
|
+
if (event.options?.messageId)
|
|
119
|
+
attributes["log.record.uid"] = event.options.messageId;
|
|
120
|
+
const annotations = yield* References.CurrentLogAnnotations;
|
|
121
|
+
events.push({
|
|
122
|
+
eventName: event.name,
|
|
123
|
+
timeUnixNano: timestamp,
|
|
124
|
+
observedTimeUnixNano: observedTime,
|
|
125
|
+
severityNumber: 9,
|
|
126
|
+
attributes: OtlpResource.entriesToAttributes(Object.entries({
|
|
127
|
+
...attributes,
|
|
128
|
+
...annotations
|
|
129
|
+
})),
|
|
130
|
+
droppedAttributesCount: 0,
|
|
131
|
+
traceId: Option.isSome(span) ? span.value.traceId : undefined,
|
|
132
|
+
spanId: Option.isSome(span) ? span.value.spanId : undefined
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// ../../src/services/opentelemetry/server.ts
|
|
137
|
+
var ServerEventsLive = Layer2.unwrap(Effect2.gen(function* () {
|
|
138
|
+
const disabled = yield* Config.Boolean("OTEL_SDK_DISABLED").pipe(Config.withDefault(false));
|
|
139
|
+
const exporters = yield* Config.Array(Schema3.String, "OTEL_LOGS_EXPORTER").pipe(Config.withDefault([]));
|
|
140
|
+
if (disabled || !exporters.some((value) => value.trim().toLowerCase() === "otlp")) {
|
|
141
|
+
return OtlpEvents.disabledLayer;
|
|
142
|
+
}
|
|
143
|
+
const endpoint = yield* Config.URL("OTEL_EXPORTER_OTLP_LOGS_ENDPOINT").pipe(Config.orElse(() => Config.URL("OTEL_EXPORTER_OTLP_ENDPOINT").pipe(Config.map((url) => {
|
|
144
|
+
url.pathname += `${url.pathname.endsWith("/") ? "" : "/"}v1/logs`;
|
|
145
|
+
return url;
|
|
146
|
+
}))), Config.withDefault(undefined));
|
|
147
|
+
if (!endpoint) {
|
|
148
|
+
return OtlpEvents.disabledLayer;
|
|
149
|
+
}
|
|
150
|
+
const headersConfig = (name) => Config.Record(Schema3.String, Schema3.StringFromUriComponent, name);
|
|
151
|
+
const headers = yield* headersConfig("OTEL_EXPORTER_OTLP_LOGS_HEADERS").pipe(Config.orElse(() => headersConfig("OTEL_EXPORTER_OTLP_HEADERS")), Config.withDefault(undefined));
|
|
152
|
+
return OtlpEvents.layer({
|
|
153
|
+
url: endpoint.toString(),
|
|
154
|
+
headers,
|
|
155
|
+
exportInterval: "1 second",
|
|
156
|
+
maxBatchSize: 1000,
|
|
157
|
+
shutdownTimeout: "3 seconds"
|
|
158
|
+
});
|
|
159
|
+
}).pipe(Effect2.orDie));
|
|
160
|
+
|
|
161
|
+
class ServerOtlp {
|
|
162
|
+
static track = track;
|
|
163
|
+
static layer = Layer2.mergeAll(Otlp.layerFromConfig(), ServerEventsLive).pipe(Layer2.provideMerge(OtlpExporter2.layerFlusher), Layer2.provide(OtlpSerialization2.layerJson), Layer2.provide(FetchHttpClient.layer));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ../../src/services/opentelemetry/api.ts
|
|
15
167
|
var otlpProxyPaths = [
|
|
16
168
|
"/api/otel/v1/traces",
|
|
17
169
|
"/api/otel/v1/metrics",
|
|
18
170
|
"/api/otel/v1/logs"
|
|
19
171
|
];
|
|
20
|
-
var exporterConfig = (signal) =>
|
|
21
|
-
var proxyOtlp = (baseUrl, signal) =>
|
|
172
|
+
var exporterConfig = (signal) => Config2.Array(Schema4.String, `OTEL_${signal}_EXPORTER`).pipe(Config2.map((exporters) => exporters.map((exporter) => exporter.toLowerCase().trim())), Config2.withDefault([]));
|
|
173
|
+
var proxyOtlp = (baseUrl, signal) => Effect3.gen(function* () {
|
|
22
174
|
const incoming = yield* HttpServerRequest.HttpServerRequest;
|
|
23
175
|
const client = yield* HttpClient.HttpClient;
|
|
24
176
|
const contentType = incoming.headers["content-type"] ?? "application/json";
|
|
@@ -33,20 +185,20 @@ var proxyOtlp = (baseUrl, signal) => Effect.gen(function* () {
|
|
|
33
185
|
"retry-after": upstream.headers["retry-after"]
|
|
34
186
|
}
|
|
35
187
|
});
|
|
36
|
-
}).pipe(
|
|
37
|
-
var otlpProxyLayer =
|
|
38
|
-
const baseUrl = yield*
|
|
39
|
-
const exporters = yield*
|
|
188
|
+
}).pipe(Effect3.withTracerEnabled(false), HttpMiddleware.withLoggerDisabled, Effect3.catch((error) => Effect3.logError("Failed to proxy browser telemetry", error).pipe(Effect3.as(HttpServerResponse.empty({ status: 502 })))), Effect3.provide(FetchHttpClient2.layer));
|
|
189
|
+
var otlpProxyLayer = Layer3.unwrap(Effect3.gen(function* () {
|
|
190
|
+
const baseUrl = yield* Config2.URL("OTEL_EXPORTER_OTLP_ENDPOINT");
|
|
191
|
+
const exporters = yield* Config2.all({
|
|
40
192
|
logs: exporterConfig("LOGS"),
|
|
41
193
|
metrics: exporterConfig("METRICS"),
|
|
42
194
|
traces: exporterConfig("TRACES")
|
|
43
195
|
});
|
|
44
|
-
return
|
|
196
|
+
return Layer3.mergeAll(exporters.traces.includes("otlp") ? HttpRouter.add("POST", otlpProxyPaths[0], proxyOtlp(baseUrl, "traces")) : Layer3.empty, exporters.metrics.includes("otlp") ? HttpRouter.add("POST", otlpProxyPaths[1], proxyOtlp(baseUrl, "metrics")) : Layer3.empty, exporters.logs.includes("otlp") ? HttpRouter.add("POST", otlpProxyPaths[2], proxyOtlp(baseUrl, "logs")) : Layer3.empty);
|
|
45
197
|
}));
|
|
46
|
-
var serverServices = HttpServer.layerServices.pipe(
|
|
198
|
+
var serverServices = HttpServer.layerServices.pipe(Layer3.provideMerge(HttpMiddleware.layerTracerDisabledForUrls(otlpProxyPaths)));
|
|
47
199
|
|
|
48
200
|
class HttpApiOtlp {
|
|
49
|
-
static layer =
|
|
201
|
+
static layer = Layer3.mergeAll(ServerOtlp.layer, otlpProxyLayer, serverServices);
|
|
50
202
|
}
|
|
51
203
|
export {
|
|
52
204
|
HttpApiOtlp
|
|
@@ -1,5 +1,15 @@
|
|
|
1
|
-
import { Layer } from "effect";
|
|
1
|
+
import { Context, Effect, Layer, Ref, Schema } from "effect";
|
|
2
2
|
import { type Otlp, OtlpExporter, OtlpLogger, OtlpMetrics, OtlpTracer } from "effect/observability";
|
|
3
|
+
import { OtlpEvents, type EventExportOptions } from "./events.js";
|
|
4
|
+
import { BrowserIdentity, type EventAttributes, type EventOptions, type PageOptions } from "./schema.js";
|
|
5
|
+
export type { BrowserIdentity, EventAttributes, EventOptions, PageOptions, } from "./schema.js";
|
|
6
|
+
declare const BrowserIdentityState_base: Context.ServiceClass<BrowserIdentityState, "krakstack/opentelemetry/BrowserIdentity", Ref.Ref<{
|
|
7
|
+
readonly userId?: string | undefined;
|
|
8
|
+
readonly organizationId?: string | undefined;
|
|
9
|
+
}>>;
|
|
10
|
+
declare class BrowserIdentityState extends BrowserIdentityState_base {
|
|
11
|
+
static readonly layer: Layer.Layer<BrowserIdentityState, never, never>;
|
|
12
|
+
}
|
|
3
13
|
type OtlpOptions = Parameters<typeof Otlp.layerJson>[0];
|
|
4
14
|
type SignalOptions<Options> = Omit<Options, "url" | "resource" | "headers">;
|
|
5
15
|
export interface BrowserOtlpOptions {
|
|
@@ -11,8 +21,16 @@ export interface BrowserOtlpOptions {
|
|
|
11
21
|
readonly traces?: false | SignalOptions<Parameters<typeof OtlpTracer.layer>[0]>;
|
|
12
22
|
readonly metrics?: false | SignalOptions<Parameters<typeof OtlpMetrics.layer>[0]>;
|
|
13
23
|
readonly logs?: false | SignalOptions<Parameters<typeof OtlpLogger.layer>[0]>;
|
|
24
|
+
readonly events?: false | (EventExportOptions & {
|
|
25
|
+
/** Initial page and pathname changes; enabled by default. */
|
|
26
|
+
readonly pageViews?: boolean;
|
|
27
|
+
});
|
|
14
28
|
}
|
|
15
29
|
export declare class BrowserOtlp {
|
|
16
|
-
|
|
30
|
+
/** Uses the active browser layer; invalid identity warns without failing or changing state. */
|
|
31
|
+
static readonly identify: (identity: BrowserIdentity | null) => Effect.Effect<void, never, never>;
|
|
32
|
+
static readonly track: (name: string, properties?: EventAttributes, options?: EventOptions) => Effect.Effect<void, Schema.SchemaError, never>;
|
|
33
|
+
static readonly page: (options?: PageOptions) => Effect.Effect<void, Schema.SchemaError, never>;
|
|
34
|
+
static readonly pageView: (options?: PageOptions) => Effect.Effect<void, Schema.SchemaError, never>;
|
|
35
|
+
static readonly layer: ({ attributes, baseUrl, logs, events, metrics, serviceName, serviceVersion, traces, headers, }: BrowserOtlpOptions) => Layer.Layer<BrowserIdentityState | OtlpExporter.Flusher | OtlpEvents, never, never>;
|
|
17
36
|
}
|
|
18
|
-
export {};
|