@devtune/ai-traffic 0.10.0 → 0.12.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.
package/dist/express.js CHANGED
@@ -11,14 +11,18 @@ export function createDevTuneAiTrafficExpressMiddleware(options) {
11
11
  headers: createHeadersLike(request),
12
12
  url: createRequestUrl(request, origin),
13
13
  ip: request.socket?.remoteAddress ?? null,
14
+ method: request.method,
15
+ targetUri: createTargetUri(request),
14
16
  };
15
17
  client.refreshRegistryIfStale();
16
18
  if (!client.mightTrackRequest(requestLike)) {
17
19
  next();
18
20
  return;
19
21
  }
22
+ // Recorded after the response, so keep the arrival time a signature is checked against.
23
+ const receivedAt = client.signedArrivalTime(requestLike);
20
24
  response.once("finish", () => {
21
- client.trackRequest(requestLike, undefined, response.statusCode);
25
+ client.trackRequest(requestLike, undefined, response.statusCode, receivedAt);
22
26
  });
23
27
  next();
24
28
  };
@@ -28,6 +32,11 @@ function createHeadersLike(request) {
28
32
  get(name) {
29
33
  return getHeader(request, name);
30
34
  },
35
+ // Express's getter reads `request.headers` with inherited properties, so
36
+ // its result is trusted only when it is a string or an array of strings.
37
+ getCombined(name) {
38
+ return (combinedHeaderValue(request.get?.(name)) ?? ownCombinedHeaderValue(request.headers, name));
39
+ },
31
40
  };
32
41
  }
33
42
  function createRequestUrl(request, configuredOrigin) {
@@ -39,13 +48,33 @@ function createRequestUrl(request, configuredOrigin) {
39
48
  return requestPath;
40
49
  }
41
50
  }
42
- function getRequestOrigin(request) {
51
+ /**
52
+ * The URL a signature covers, as the request line and the raw host (`X-Forwarded-Host`, then `Host`) carried
53
+ * it. Never parsed, so a signed `https://EXAMPLE.com:443/guide` is forwarded
54
+ * byte for byte. A configured `origin` never replaces the host the agent
55
+ * signed. An absolute-form request target is already the whole URL. Null
56
+ * when there is no host to build it from.
57
+ */
58
+ function createTargetUri(request) {
59
+ const requestPath = request.originalUrl ?? request.url;
60
+ // An absolute-form target is already the whole URL; any other non-path
61
+ // form (`*`, authority-form) is not a URL a signature can cover.
62
+ if (!requestPath.startsWith("/")) {
63
+ return /^https?:\/\//iu.test(requestPath) ? requestPath : null;
64
+ }
65
+ const host = getRequestHost(request);
66
+ return host ? `${getRequestOrigin(request, host)}${requestPath}` : null;
67
+ }
68
+ function getRequestOrigin(request, host = getRequestHost(request) ?? "localhost") {
43
69
  const protocol = getForwardedHeader(request, "x-forwarded-proto") ?? request.protocol ?? "http";
44
- const host = getForwardedHeader(request, "x-forwarded-host") ?? getHeader(request, "host") ?? "localhost";
45
70
  return `${protocol}://${host}`;
46
71
  }
72
+ function getRequestHost(request) {
73
+ return getForwardedHeader(request, "x-forwarded-host") ?? getHeader(request, "host");
74
+ }
75
+ /** Express's getter can return a repeated field as an array; like the raw headers, it reads the first value. */
47
76
  function getHeader(request, name) {
48
- return request.get?.(name) ?? headerValue(request.headers[name.toLowerCase()]);
77
+ return headerValue(request.get?.(name)) ?? headerValue(request.headers[name.toLowerCase()]);
49
78
  }
50
79
  function getForwardedHeader(request, name) {
51
80
  const value = getHeader(request, name);
@@ -57,4 +86,27 @@ function headerValue(value) {
57
86
  }
58
87
  return value?.[0] ?? null;
59
88
  }
89
+ /**
90
+ * A covered field's value, read only as an own property: the names come from
91
+ * a client's `Signature-Input`, so `constructor` or `__proto__` must not
92
+ * reach an inherited value.
93
+ */
94
+ function ownCombinedHeaderValue(headers, name) {
95
+ const key = name.toLowerCase();
96
+ return Object.prototype.hasOwnProperty.call(headers, key)
97
+ ? combinedHeaderValue(headers[key])
98
+ : null;
99
+ }
100
+ /**
101
+ * Repeated values combine with ", " as RFC 9110 field combination does. Only
102
+ * a string or an array of strings is a value; anything else is absent.
103
+ */
104
+ function combinedHeaderValue(value) {
105
+ if (typeof value === "string") {
106
+ return value;
107
+ }
108
+ return Array.isArray(value) && value.length > 0 && value.every((item) => typeof item === "string")
109
+ ? value.join(", ")
110
+ : null;
111
+ }
60
112
  //# sourceMappingURL=express.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"express.js","sourceRoot":"","sources":["../../express/src/express.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,sBAAsB,GAGvB,MAAM,qBAAqB,CAAC;AA0B7B,MAAM,UAAU,uCAAuC,CAAC,OAAuC;IAC7F,MAAM,EAAE,MAAM,EAAE,GAAG,aAAa,EAAE,GAAG,OAAO,CAAC;IAC7C,MAAM,MAAM,GAAG,sBAAsB,CAAC,aAAa,CAAC,CAAC;IAErD,OAAO,SAAS,iCAAiC,CAC/C,OAAuC,EACvC,QAAyC,EACzC,IAAiC;QAEjC,MAAM,WAAW,GAAG;YAClB,OAAO,EAAE,iBAAiB,CAAC,OAAO,CAAC;YACnC,GAAG,EAAE,gBAAgB,CAAC,OAAO,EAAE,MAAM,CAAC;YACtC,EAAE,EAAE,OAAO,CAAC,MAAM,EAAE,aAAa,IAAI,IAAI;SAC1C,CAAC;QAEF,MAAM,CAAC,sBAAsB,EAAE,CAAC;QAEhC,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,WAAW,CAAC,EAAE,CAAC;YAC3C,IAAI,EAAE,CAAC;YAEP,OAAO;QACT,CAAC;QAED,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE;YAC3B,MAAM,CAAC,YAAY,CAAC,WAAW,EAAE,SAAS,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;QACnE,CAAC,CAAC,CAAC;QAEH,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,OAAuC;IAChE,OAAO;QACL,GAAG,CAAC,IAAI;YACN,OAAO,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAClC,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,gBAAgB,CAAC,OAAuC,EAAE,gBAAyB;IAC1F,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC;IAEvD,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,WAAW,EAAE,gBAAgB,IAAI,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACxF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,WAAW,CAAC;IACrB,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,OAAuC;IAC/D,MAAM,QAAQ,GAAG,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,CAAC,IAAI,OAAO,CAAC,QAAQ,IAAI,MAAM,CAAC;IAChG,MAAM,IAAI,GACR,kBAAkB,CAAC,OAAO,EAAE,kBAAkB,CAAC,IAAI,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,WAAW,CAAC;IAE/F,OAAO,GAAG,QAAQ,MAAM,IAAI,EAAE,CAAC;AACjC,CAAC;AAED,SAAS,SAAS,CAAC,OAAuC,EAAE,IAAY;IACtE,OAAO,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;AACjF,CAAC;AAED,SAAS,kBAAkB,CAAC,OAAuC,EAAE,IAAY;IAC/E,MAAM,KAAK,GAAG,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAEvC,OAAO,KAAK,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC;AACjD,CAAC;AAED,SAAS,WAAW,CAAC,KAAkB;IACrC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AAC5B,CAAC"}
1
+ {"version":3,"file":"express.js","sourceRoot":"","sources":["../../express/src/express.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@
4
4
  */
5
5
  export { DEFAULT_INGEST_ENDPOINT, DEFAULT_REGISTRY_URL, DevTuneAiTrafficClient, createDevTuneAiTraffic, createDevTuneAiTrafficMiddleware, mightBeAiBotUserAgent, withDevTuneAiTrafficRoute, type DevTuneAiTrafficEvent, type DevTuneAiTrafficLogger, type DevTuneAiTrafficMatch, type DevTuneAiTrafficOptions, type DevTuneAiTrafficRouteHandler, type HeadersLike, type RequestLike, type WaitUntilLike, } from "./client.js";
6
6
  export { SDK_VERSION, SDK_VERSION_HEADER } from "./version.js";
7
+ export { WEB_BOT_AUTH_FORWARDED_HEADERS, type DevTuneAiTrafficWebBotAuth } from "./web-bot-auth.js";
7
8
  export { detectTrustedProxy, resolveClientIp, type DevTuneAiTrafficTrustedProxy, } from "./client-ip.js";
8
9
  export { FALLBACK_AI_BOT_REGISTRY_ENTRIES, compileAiBotRegistryEntries, matchAiBotRegistryEntry, parseAiBotRegistryPayload, type AiBotRegistryEntry, type CompiledAiBotRegistryEntry, type LlmPlatform, type MachineTrafficClass, } from "./registry.js";
9
10
  export { detectAiCrawler, detectAiReferrer, FALLBACK_AI_REFERRER_REGISTRY_ENTRIES, type AiCrawlerDetection, type AiReferrerDetection, type AiReferrerRegistryEntry, type DetectAiCrawlerOptions, type DetectAiReferrerOptions, type DetectionRequestLike, } from "./detection.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,sBAAsB,EACtB,sBAAsB,EACtB,gCAAgC,EAChC,qBAAqB,EACrB,yBAAyB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,uBAAuB,EAC5B,KAAK,4BAA4B,EACjC,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,aAAa,GACnB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAE/D,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,KAAK,4BAA4B,GAClC,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,gCAAgC,EAChC,2BAA2B,EAC3B,uBAAuB,EACvB,yBAAyB,EACzB,KAAK,kBAAkB,EACvB,KAAK,0BAA0B,EAC/B,KAAK,WAAW,EAChB,KAAK,mBAAmB,GACzB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,qCAAqC,EACrC,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,GAC1B,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,sBAAsB,EACtB,sBAAsB,EACtB,gCAAgC,EAChC,qBAAqB,EACrB,yBAAyB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,uBAAuB,EAC5B,KAAK,4BAA4B,EACjC,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,aAAa,GACnB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAE/D,OAAO,EAAE,8BAA8B,EAAE,KAAK,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAEpG,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,KAAK,4BAA4B,GAClC,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,gCAAgC,EAChC,2BAA2B,EAC3B,uBAAuB,EACvB,yBAAyB,EACzB,KAAK,kBAAkB,EACvB,KAAK,0BAA0B,EAC/B,KAAK,WAAW,EAChB,KAAK,mBAAmB,GACzB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,qCAAqC,EACrC,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,GAC1B,MAAM,gBAAgB,CAAC"}
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@
4
4
  */
5
5
  export { DEFAULT_INGEST_ENDPOINT, DEFAULT_REGISTRY_URL, DevTuneAiTrafficClient, createDevTuneAiTraffic, createDevTuneAiTrafficMiddleware, mightBeAiBotUserAgent, withDevTuneAiTrafficRoute, } from "./client.js";
6
6
  export { SDK_VERSION, SDK_VERSION_HEADER } from "./version.js";
7
+ export { WEB_BOT_AUTH_FORWARDED_HEADERS } from "./web-bot-auth.js";
7
8
  export { detectTrustedProxy, resolveClientIp, } from "./client-ip.js";
8
9
  export { FALLBACK_AI_BOT_REGISTRY_ENTRIES, compileAiBotRegistryEntries, matchAiBotRegistryEntry, parseAiBotRegistryPayload, } from "./registry.js";
9
10
  export { detectAiCrawler, detectAiReferrer, FALLBACK_AI_REFERRER_REGISTRY_ENTRIES, } from "./detection.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,sBAAsB,EACtB,sBAAsB,EACtB,gCAAgC,EAChC,qBAAqB,EACrB,yBAAyB,GAS1B,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAE/D,OAAO,EACL,kBAAkB,EAClB,eAAe,GAEhB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,gCAAgC,EAChC,2BAA2B,EAC3B,uBAAuB,EACvB,yBAAyB,GAK1B,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,qCAAqC,GAOtC,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,sBAAsB,EACtB,sBAAsB,EACtB,gCAAgC,EAChC,qBAAqB,EACrB,yBAAyB,GAS1B,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAE/D,OAAO,EAAE,8BAA8B,EAAmC,MAAM,mBAAmB,CAAC;AAEpG,OAAO,EACL,kBAAkB,EAClB,eAAe,GAEhB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,gCAAgC,EAChC,2BAA2B,EAC3B,uBAAuB,EACvB,yBAAyB,GAK1B,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,qCAAqC,GAOtC,MAAM,gBAAgB,CAAC"}
package/dist/node.d.ts CHANGED
@@ -6,6 +6,7 @@ import { type DevTuneAiTrafficOptions } from "@devtune/ai-traffic";
6
6
  type HeaderValue = string | readonly string[] | undefined;
7
7
  export interface DevTuneAiTrafficNodeRequest {
8
8
  headers: Record<string, HeaderValue>;
9
+ method?: string;
9
10
  socket?: {
10
11
  encrypted?: boolean;
11
12
  remoteAddress?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../node/src/node.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAEL,KAAK,uBAAuB,EAE7B,MAAM,qBAAqB,CAAC;AAE7B,KAAK,WAAW,GAAG,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;AAE1D,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACrC,MAAM,CAAC,EAAE;QACP,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,4BAA4B;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC;CACtD;AAED,MAAM,WAAW,2BAA4B,SAAQ,uBAAuB;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,iCAAiC,CAAC,OAAO,EAAE,2BAA2B,IAKlF,SAAS,2BAA2B,EACpC,UAAU,4BAA4B,UAkBzC"}
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../node/src/node.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAEL,KAAK,uBAAuB,EAE7B,MAAM,qBAAqB,CAAC;AAE7B,KAAK,WAAW,GAAG,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;AAE1D,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE;QACP,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,4BAA4B;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC;CACtD;AAED,MAAM,WAAW,2BAA4B,SAAQ,uBAAuB;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,iCAAiC,CAAC,OAAO,EAAE,2BAA2B,IAKlF,SAAS,2BAA2B,EACpC,UAAU,4BAA4B,UAuBzC"}
package/dist/node.js CHANGED
@@ -11,13 +11,17 @@ export function createDevTuneAiTrafficNodeHandler(options) {
11
11
  headers: createHeadersLike(request),
12
12
  url: createRequestUrl(request, origin),
13
13
  ip: request.socket?.remoteAddress ?? null,
14
+ method: request.method,
15
+ targetUri: createTargetUri(request),
14
16
  };
15
17
  client.refreshRegistryIfStale();
16
18
  if (!client.mightTrackRequest(requestLike)) {
17
19
  return;
18
20
  }
21
+ // Recorded after the response, so keep the arrival time a signature is checked against.
22
+ const receivedAt = client.signedArrivalTime(requestLike);
19
23
  response.once("finish", () => {
20
- client.trackRequest(requestLike, undefined, response.statusCode);
24
+ client.trackRequest(requestLike, undefined, response.statusCode, receivedAt);
21
25
  });
22
26
  };
23
27
  }
@@ -26,6 +30,9 @@ function createHeadersLike(request) {
26
30
  get(name) {
27
31
  return getHeader(request, name);
28
32
  },
33
+ getCombined(name) {
34
+ return ownCombinedHeaderValue(request.headers, name);
35
+ },
29
36
  };
30
37
  }
31
38
  function createRequestUrl(request, configuredOrigin) {
@@ -37,11 +44,30 @@ function createRequestUrl(request, configuredOrigin) {
37
44
  return requestPath;
38
45
  }
39
46
  }
40
- function getRequestOrigin(request) {
47
+ /**
48
+ * The URL a signature covers, as the request line and the raw host (`X-Forwarded-Host`, then `Host`) carried
49
+ * it. Never parsed, so a signed `https://EXAMPLE.com:443/guide` is forwarded
50
+ * byte for byte. A configured `origin` never replaces the host the agent
51
+ * signed. An absolute-form request target is already the whole URL. Null
52
+ * when there is no host to build it from.
53
+ */
54
+ function createTargetUri(request) {
55
+ const requestPath = request.url ?? "/";
56
+ // An absolute-form target is already the whole URL; any other non-path
57
+ // form (`*`, authority-form) is not a URL a signature can cover.
58
+ if (!requestPath.startsWith("/")) {
59
+ return /^https?:\/\//iu.test(requestPath) ? requestPath : null;
60
+ }
61
+ const host = getRequestHost(request);
62
+ return host ? `${getRequestOrigin(request, host)}${requestPath}` : null;
63
+ }
64
+ function getRequestOrigin(request, host = getRequestHost(request) || "localhost") {
41
65
  const forwardedProtocol = getForwardedHeader(request, "x-forwarded-proto");
42
66
  const protocol = forwardedProtocol ?? (request.socket?.encrypted ? "https" : "http");
43
- const host = getForwardedHeader(request, "x-forwarded-host") ?? getHeader(request, "host");
44
- return `${protocol}://${host || "localhost"}`;
67
+ return `${protocol}://${host}`;
68
+ }
69
+ function getRequestHost(request) {
70
+ return getForwardedHeader(request, "x-forwarded-host") ?? getHeader(request, "host");
45
71
  }
46
72
  function getHeader(request, name) {
47
73
  return headerValue(request.headers[name.toLowerCase()]);
@@ -56,4 +82,27 @@ function headerValue(value) {
56
82
  }
57
83
  return value?.[0] ?? null;
58
84
  }
85
+ /**
86
+ * A covered field's value, read only as an own property: the names come from
87
+ * a client's `Signature-Input`, so `constructor` or `__proto__` must not
88
+ * reach an inherited value.
89
+ */
90
+ function ownCombinedHeaderValue(headers, name) {
91
+ const key = name.toLowerCase();
92
+ return Object.prototype.hasOwnProperty.call(headers, key)
93
+ ? combinedHeaderValue(headers[key])
94
+ : null;
95
+ }
96
+ /**
97
+ * Repeated values combine with ", " as RFC 9110 field combination does. Only
98
+ * a string or an array of strings is a value; anything else is absent.
99
+ */
100
+ function combinedHeaderValue(value) {
101
+ if (typeof value === "string") {
102
+ return value;
103
+ }
104
+ return Array.isArray(value) && value.length > 0 && value.every((item) => typeof item === "string")
105
+ ? value.join(", ")
106
+ : null;
107
+ }
59
108
  //# sourceMappingURL=node.js.map
package/dist/node.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"node.js","sourceRoot":"","sources":["../../node/src/node.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,sBAAsB,GAGvB,MAAM,qBAAqB,CAAC;AAsB7B,MAAM,UAAU,iCAAiC,CAAC,OAAoC;IACpF,MAAM,EAAE,MAAM,EAAE,GAAG,aAAa,EAAE,GAAG,OAAO,CAAC;IAC7C,MAAM,MAAM,GAAG,sBAAsB,CAAC,aAAa,CAAC,CAAC;IAErD,OAAO,SAAS,2BAA2B,CACzC,OAAoC,EACpC,QAAsC;QAEtC,MAAM,WAAW,GAAG;YAClB,OAAO,EAAE,iBAAiB,CAAC,OAAO,CAAC;YACnC,GAAG,EAAE,gBAAgB,CAAC,OAAO,EAAE,MAAM,CAAC;YACtC,EAAE,EAAE,OAAO,CAAC,MAAM,EAAE,aAAa,IAAI,IAAI;SAC1C,CAAC;QAEF,MAAM,CAAC,sBAAsB,EAAE,CAAC;QAEhC,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,WAAW,CAAC,EAAE,CAAC;YAC3C,OAAO;QACT,CAAC;QAED,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE;YAC3B,MAAM,CAAC,YAAY,CAAC,WAAW,EAAE,SAAS,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;QACnE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,OAAoC;IAC7D,OAAO;QACL,GAAG,CAAC,IAAI;YACN,OAAO,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAClC,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,gBAAgB,CAAC,OAAoC,EAAE,gBAAyB;IACvF,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC;IAEvC,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,WAAW,EAAE,gBAAgB,IAAI,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACxF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,WAAW,CAAC;IACrB,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,OAAoC;IAC5D,MAAM,iBAAiB,GAAG,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;IAC3E,MAAM,QAAQ,GAAG,iBAAiB,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACrF,MAAM,IAAI,GAAG,kBAAkB,CAAC,OAAO,EAAE,kBAAkB,CAAC,IAAI,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAE3F,OAAO,GAAG,QAAQ,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;AAChD,CAAC;AAED,SAAS,SAAS,CAAC,OAAoC,EAAE,IAAY;IACnE,OAAO,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED,SAAS,kBAAkB,CAAC,OAAoC,EAAE,IAAY;IAC5E,MAAM,KAAK,GAAG,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAEvC,OAAO,KAAK,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC;AACjD,CAAC;AAED,SAAS,WAAW,CAAC,KAAkB;IACrC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AAC5B,CAAC"}
1
+ {"version":3,"file":"node.js","sourceRoot":"","sources":["../../node/src/node.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * ABOUTME: Vercel OIDC credentials for ingest, used instead of an ingest key on Vercel.
3
+ * Filters the per-request token header and exchanges it for DevTune's audience with a cache.
4
+ */
5
+ /** Vercel sets this on every request to a deployment that has OIDC enabled. */
6
+ export declare const VERCEL_OIDC_TOKEN_HEADER = "x-vercel-oidc-token";
7
+ /** Tells the ingest route which DevTune project's Vercel link to check the token against. */
8
+ export declare const DEVTUNE_PROJECT_HEADER = "x-devtune-project";
9
+ export declare const DEVTUNE_INGEST_AUDIENCE = "https://devtune.ai/ai-traffic-ingest";
10
+ export declare const VERCEL_TOKEN_EXCHANGE_URL = "https://oidc.vercel.com/~token";
11
+ /** Tokens this close to expiry are neither captured, exchanged nor served from the cache. */
12
+ export declare const TOKEN_EXPIRY_MARGIN_MS = 60000;
13
+ export declare function isDevTuneProjectId(value: string): boolean;
14
+ /**
15
+ * Returns the token's expiry in milliseconds when the value looks like a live
16
+ * Vercel OIDC token, or null for anything else.
17
+ *
18
+ * The signature is not checked: the SDK has no way to, and the ingest route
19
+ * verifies it. This filter only keeps a client-supplied junk header, a token
20
+ * from another issuer or an expired token from replacing a good one.
21
+ */
22
+ export declare function readVercelOidcTokenExpiry(value: string, nowMs: number): number | null;
23
+ export type VercelOidcExchangeFailureKind = "exchange_status" | "exchange_response";
24
+ /**
25
+ * Thrown when Vercel answers an exchange but not with a usable token. Carries
26
+ * a fixed `kind` and the status, so callers can report the failure without
27
+ * reading any message text.
28
+ */
29
+ export declare class VercelOidcExchangeError extends Error {
30
+ readonly kind: VercelOidcExchangeFailureKind;
31
+ readonly status?: number | undefined;
32
+ constructor(kind: VercelOidcExchangeFailureKind, status?: number | undefined);
33
+ }
34
+ /**
35
+ * Exchanges a deployment's Vercel OIDC token for one scoped to DevTune's
36
+ * ingest audience.
37
+ *
38
+ * Results are cached in memory by source token until their expiry, less the
39
+ * margin, and expired entries are dropped on every call, so the steady state
40
+ * is one entry per live source token. Callers that need the same token at once
41
+ * share one request. Vercel's answers are reported as a
42
+ * `VercelOidcExchangeError`; anything the fetch itself throws is passed on.
43
+ */
44
+ export declare class VercelOidcTokenExchanger {
45
+ private readonly fetchImpl;
46
+ private readonly nowMs;
47
+ private readonly cache;
48
+ private readonly inFlight;
49
+ constructor(fetchImpl: typeof fetch, nowMs: () => number);
50
+ exchange(sourceToken: string): Promise<string>;
51
+ private requestExchange;
52
+ }
53
+ //# sourceMappingURL=vercel-oidc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vercel-oidc.d.ts","sourceRoot":"","sources":["../src/vercel-oidc.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,+EAA+E;AAC/E,eAAO,MAAM,wBAAwB,wBAAwB,CAAC;AAE9D,6FAA6F;AAC7F,eAAO,MAAM,sBAAsB,sBAAsB,CAAC;AAE1D,eAAO,MAAM,uBAAuB,yCAAyC,CAAC;AAC9E,eAAO,MAAM,yBAAyB,mCAAmC,CAAC;AAQ1E,6FAA6F;AAC7F,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAE7C,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,WAE/C;AAED;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,iBA4BrE;AAED,MAAM,MAAM,6BAA6B,GAAG,iBAAiB,GAAG,mBAAmB,CAAC;AAEpF;;;;GAIG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;IAE9C,QAAQ,CAAC,IAAI,EAAE,6BAA6B;IAC5C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM;gBADf,IAAI,EAAE,6BAA6B,EACnC,MAAM,CAAC,EAAE,MAAM,YAAA;CAS3B;AAOD;;;;;;;;;GASG;AACH,qBAAa,wBAAwB;IAKjC,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,KAAK;IALxB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqC;IAC3D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsC;gBAG5C,SAAS,EAAE,OAAO,KAAK,EACvB,KAAK,EAAE,MAAM,MAAM;IAGhC,QAAQ,CAAC,WAAW,EAAE,MAAM;YA8BpB,eAAe;CAsC9B"}
@@ -0,0 +1,152 @@
1
+ /**
2
+ * ABOUTME: Vercel OIDC credentials for ingest, used instead of an ingest key on Vercel.
3
+ * Filters the per-request token header and exchanges it for DevTune's audience with a cache.
4
+ */
5
+ /** Vercel sets this on every request to a deployment that has OIDC enabled. */
6
+ export const VERCEL_OIDC_TOKEN_HEADER = "x-vercel-oidc-token";
7
+ /** Tells the ingest route which DevTune project's Vercel link to check the token against. */
8
+ export const DEVTUNE_PROJECT_HEADER = "x-devtune-project";
9
+ export const DEVTUNE_INGEST_AUDIENCE = "https://devtune.ai/ai-traffic-ingest";
10
+ export const VERCEL_TOKEN_EXCHANGE_URL = "https://oidc.vercel.com/~token";
11
+ const VERCEL_OIDC_ISSUER = "https://oidc.vercel.com";
12
+ const MAX_TOKEN_LENGTH = 8_192;
13
+ const TOKEN_EXCHANGE_TIMEOUT_MS = 5_000;
14
+ const BASE64URL_SEGMENT = /^[A-Za-z0-9_-]+$/u;
15
+ const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu;
16
+ /** Tokens this close to expiry are neither captured, exchanged nor served from the cache. */
17
+ export const TOKEN_EXPIRY_MARGIN_MS = 60_000;
18
+ export function isDevTuneProjectId(value) {
19
+ return UUID_PATTERN.test(value);
20
+ }
21
+ /**
22
+ * Returns the token's expiry in milliseconds when the value looks like a live
23
+ * Vercel OIDC token, or null for anything else.
24
+ *
25
+ * The signature is not checked: the SDK has no way to, and the ingest route
26
+ * verifies it. This filter only keeps a client-supplied junk header, a token
27
+ * from another issuer or an expired token from replacing a good one.
28
+ */
29
+ export function readVercelOidcTokenExpiry(value, nowMs) {
30
+ if (value.length > MAX_TOKEN_LENGTH) {
31
+ return null;
32
+ }
33
+ const segments = value.split(".");
34
+ if (segments.length !== 3 || !segments.every((segment) => BASE64URL_SEGMENT.test(segment))) {
35
+ return null;
36
+ }
37
+ const claims = decodeJsonSegment(segments[1] ?? "");
38
+ if (!claims ||
39
+ typeof claims.iss !== "string" ||
40
+ (claims.iss !== VERCEL_OIDC_ISSUER && !claims.iss.startsWith(`${VERCEL_OIDC_ISSUER}/`)) ||
41
+ typeof claims.owner_id !== "string" ||
42
+ typeof claims.project_id !== "string" ||
43
+ typeof claims.exp !== "number" ||
44
+ !Number.isFinite(claims.exp)) {
45
+ return null;
46
+ }
47
+ const expiresAtMs = claims.exp * 1000;
48
+ return expiresAtMs - nowMs > TOKEN_EXPIRY_MARGIN_MS ? expiresAtMs : null;
49
+ }
50
+ /**
51
+ * Thrown when Vercel answers an exchange but not with a usable token. Carries
52
+ * a fixed `kind` and the status, so callers can report the failure without
53
+ * reading any message text.
54
+ */
55
+ export class VercelOidcExchangeError extends Error {
56
+ kind;
57
+ status;
58
+ constructor(kind, status) {
59
+ super(kind === "exchange_status"
60
+ ? `Vercel OIDC token exchange failed with status ${status}`
61
+ : "Vercel OIDC token exchange returned an unusable response");
62
+ this.kind = kind;
63
+ this.status = status;
64
+ this.name = "VercelOidcExchangeError";
65
+ }
66
+ }
67
+ /**
68
+ * Exchanges a deployment's Vercel OIDC token for one scoped to DevTune's
69
+ * ingest audience.
70
+ *
71
+ * Results are cached in memory by source token until their expiry, less the
72
+ * margin, and expired entries are dropped on every call, so the steady state
73
+ * is one entry per live source token. Callers that need the same token at once
74
+ * share one request. Vercel's answers are reported as a
75
+ * `VercelOidcExchangeError`; anything the fetch itself throws is passed on.
76
+ */
77
+ export class VercelOidcTokenExchanger {
78
+ fetchImpl;
79
+ nowMs;
80
+ cache = new Map();
81
+ inFlight = new Map();
82
+ constructor(fetchImpl, nowMs) {
83
+ this.fetchImpl = fetchImpl;
84
+ this.nowMs = nowMs;
85
+ }
86
+ async exchange(sourceToken) {
87
+ const nowMs = this.nowMs();
88
+ for (const [key, entry] of this.cache) {
89
+ if (entry.usableUntilMs <= nowMs) {
90
+ this.cache.delete(key);
91
+ }
92
+ }
93
+ const cached = this.cache.get(sourceToken);
94
+ if (cached) {
95
+ return cached.token;
96
+ }
97
+ const pending = this.inFlight.get(sourceToken);
98
+ if (pending) {
99
+ return pending;
100
+ }
101
+ const request = this.requestExchange(sourceToken).finally(() => {
102
+ this.inFlight.delete(sourceToken);
103
+ });
104
+ this.inFlight.set(sourceToken, request);
105
+ return request;
106
+ }
107
+ async requestExchange(sourceToken) {
108
+ const response = await this.fetchImpl(VERCEL_TOKEN_EXCHANGE_URL, {
109
+ body: JSON.stringify({ token: sourceToken, aud: DEVTUNE_INGEST_AUDIENCE }),
110
+ headers: {
111
+ accept: "application/json",
112
+ "content-type": "application/json",
113
+ },
114
+ method: "POST",
115
+ signal: AbortSignal.timeout(TOKEN_EXCHANGE_TIMEOUT_MS),
116
+ });
117
+ if (!response.ok) {
118
+ throw new VercelOidcExchangeError("exchange_status", response.status);
119
+ }
120
+ const payload = await response.json().catch(() => null);
121
+ if (!isRecord(payload) ||
122
+ typeof payload.token !== "string" ||
123
+ payload.token.length === 0 ||
124
+ typeof payload.expiry !== "number" ||
125
+ !Number.isFinite(payload.expiry)) {
126
+ throw new VercelOidcExchangeError("exchange_response");
127
+ }
128
+ const usableUntilMs = payload.expiry * 1000 - TOKEN_EXPIRY_MARGIN_MS;
129
+ // A token that is already inside the margin would only be rejected at ingest.
130
+ if (usableUntilMs <= this.nowMs()) {
131
+ throw new VercelOidcExchangeError("exchange_response");
132
+ }
133
+ this.cache.set(sourceToken, { token: payload.token, usableUntilMs });
134
+ return payload.token;
135
+ }
136
+ }
137
+ function decodeJsonSegment(segment) {
138
+ try {
139
+ const base64 = segment.replace(/-/gu, "+").replace(/_/gu, "/");
140
+ const binary = atob(base64.padEnd(Math.ceil(base64.length / 4) * 4, "="));
141
+ const bytes = Uint8Array.from(binary, (character) => character.charCodeAt(0));
142
+ const value = JSON.parse(new TextDecoder().decode(bytes));
143
+ return isRecord(value) ? value : null;
144
+ }
145
+ catch {
146
+ return null;
147
+ }
148
+ }
149
+ function isRecord(value) {
150
+ return typeof value === "object" && value !== null;
151
+ }
152
+ //# sourceMappingURL=vercel-oidc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vercel-oidc.js","sourceRoot":"","sources":["../src/vercel-oidc.ts"],"names":[],"mappings":"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"}
package/dist/version.d.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  * Lets DevTune tell a customer when their sensor has an upgrade available.
4
4
  */
5
5
  /** Must equal `version` in package.json; a unit test and the release check enforce it. */
6
- export declare const SDK_VERSION = "0.10.0";
6
+ export declare const SDK_VERSION = "0.12.0";
7
7
  /** Sent on every ingest and registry request. Carries the version only, no visitor data. */
8
8
  export declare const SDK_VERSION_HEADER = "x-devtune-sdk-version";
9
9
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -3,7 +3,7 @@
3
3
  * Lets DevTune tell a customer when their sensor has an upgrade available.
4
4
  */
5
5
  /** Must equal `version` in package.json; a unit test and the release check enforce it. */
6
- export const SDK_VERSION = "0.10.0";
6
+ export const SDK_VERSION = "0.12.0";
7
7
  /** Sent on every ingest and registry request. Carries the version only, no visitor data. */
8
8
  export const SDK_VERSION_HEADER = "x-devtune-sdk-version";
9
9
  //# sourceMappingURL=version.js.map
@@ -0,0 +1,56 @@
1
+ /**
2
+ * ABOUTME: Collects the Web Bot Auth signature material DevTune needs to verify a signed agent request.
3
+ * Forwards the signature headers verbatim with the request line and covered fields; never verifies, logs or stores them.
4
+ */
5
+ import type { RequestLike } from "./client.js";
6
+ /**
7
+ * What DevTune's ingest needs to check one request's HTTP Message Signature
8
+ * (RFC 9421) as a Web Bot Auth agent signed it. The three signature headers
9
+ * are verbatim, `method` and `targetUri` are the request as received, which
10
+ * never has a query, and `headers` holds any other field the signature
11
+ * covers, by lowercase name. DevTune verifies it on receipt and never stores
12
+ * it.
13
+ */
14
+ export interface DevTuneAiTrafficWebBotAuth {
15
+ signature: string;
16
+ signatureInput: string;
17
+ signatureAgent: string;
18
+ method: string;
19
+ targetUri: string;
20
+ headers?: Record<string, string>;
21
+ }
22
+ /**
23
+ * The only header fields ever forwarded with a signature: non-sensitive
24
+ * request headers. A request whose signature covers any other field, besides
25
+ * the three signature headers, forwards no signature material at all, since
26
+ * an unknown field may carry a credential and dropping just that field would
27
+ * leave a signature that can never verify. Short enough to stay within
28
+ * ingest's limits of 16 fields and 128-character names.
29
+ */
30
+ export declare const WEB_BOT_AUTH_FORWARDED_HEADERS: readonly string[];
31
+ /**
32
+ * Reads the material for a request that carries all three of `Signature`,
33
+ * `Signature-Input` and `Signature-Agent`, or returns null.
34
+ *
35
+ * Also returns null when anything would exceed ingest's limits or is not a
36
+ * value the site could have received, rather than truncating: a truncated
37
+ * signature can never verify, and the event is then sent exactly as an
38
+ * unsigned one would be.
39
+ *
40
+ * `targetUri` must carry the authority and target the agent sent. A target
41
+ * that differs from the event's `url` only makes DevTune's check
42
+ * inconclusive, but a rewritten one that happens to match `url` turns a
43
+ * genuine signature into a failed one.
44
+ *
45
+ * A target with a query, even a bare `?`, forwards nothing. The query can
46
+ * carry a reset token, an OAuth code or search terms, and DevTune could not
47
+ * verify the signature anyway: the event's `url` never has a query, so the
48
+ * target would not match it.
49
+ */
50
+ export declare function readWebBotAuth(request: Pick<RequestLike, "headers" | "method" | "targetUri" | "url">): DevTuneAiTrafficWebBotAuth | null;
51
+ /**
52
+ * Whether a request carries all three Web Bot Auth signature headers, the
53
+ * only requests whose arrival time or signature material is worth reading.
54
+ */
55
+ export declare function hasWebBotAuthHeaders(request: Pick<RequestLike, "headers">): boolean;
56
+ //# sourceMappingURL=web-bot-auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-bot-auth.d.ts","sourceRoot":"","sources":["../src/web-bot-auth.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C;;;;;;;GAOG;AACH,MAAM,WAAW,0BAA0B;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAwBD;;;;;;;GAOG;AACH,eAAO,MAAM,8BAA8B,EAAE,SAAS,MAAM,EAW3D,CAAC;AAWF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,QAAQ,GAAG,WAAW,GAAG,KAAK,CAAC,GACrE,0BAA0B,GAAG,IAAI,CA+EnC;AA4CD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,WAMzE"}