@blotout/qinto-cli 0.1.4 → 0.1.5

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.
@@ -14566,7 +14566,7 @@ var actionContextFields = {
14566
14566
  locale: external_exports.string().min(1).max(64).optional(),
14567
14567
  provenance: external_exports.enum(["edge", "caller"])
14568
14568
  }).strict().optional(),
14569
- session: external_exports.object({ id: external_exports.string().min(1).max(64), isNew: external_exports.boolean() }).strict().optional()
14569
+ session: external_exports.object({ id: external_exports.string().min(1).max(64), isNew: external_exports.boolean(), startedAt: external_exports.number().int().nonnegative().safe() }).strict().optional()
14570
14570
  };
14571
14571
  var actionContextSchema = external_exports.discriminatedUnion("source", [
14572
14572
  external_exports.object({ source: external_exports.literal("browser"), ...actionContextFields }).strict(),
@@ -14585,7 +14585,7 @@ var envelopeRequestSchema = external_exports.object({
14585
14585
  properties: external_exports.record(external_exports.string(), external_exports.unknown()),
14586
14586
  geo: external_exports.record(external_exports.string(), external_exports.string()).optional()
14587
14587
  }).strict(),
14588
- consent: external_exports.object({ categories: external_exports.record(external_exports.string(), external_exports.boolean()), apps: external_exports.record(external_exports.string(), external_exports.boolean()) }).strict().optional(),
14588
+ consent: external_exports.object({ enabled: external_exports.boolean(), categories: external_exports.record(external_exports.string(), external_exports.boolean()), apps: external_exports.record(external_exports.string(), external_exports.boolean()) }).strict().optional(),
14589
14589
  geo: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
14590
14590
  // Only what this app captured or wrote for this visitor; `updatedAt` is when the current value
14591
14591
  // was first seen, which is the click time a CAPI `fbc` is built from.
@@ -14928,6 +14928,122 @@ var newSpanId = () => {
14928
14928
  return randomHex(8);
14929
14929
  };
14930
14930
 
14931
+ // ../../../libs/tail-trace/src/redact.ts
14932
+ var SENSITIVE_URL_PARAMS = /* @__PURE__ */ new Set([
14933
+ "access_token",
14934
+ "api_key",
14935
+ "api_secret",
14936
+ "client_secret",
14937
+ "code",
14938
+ "key",
14939
+ "password",
14940
+ "refresh_token",
14941
+ "secret",
14942
+ "session",
14943
+ "session_id",
14944
+ "token"
14945
+ ]);
14946
+ var PARAM_MATCHER = /(^|[?&])([^=&?\s]+)=[^&\s]*/g;
14947
+ var SENSITIVE_KEY_STEMS = [
14948
+ "apikey",
14949
+ "authorization",
14950
+ "bearer",
14951
+ "credential",
14952
+ "passwd",
14953
+ "password",
14954
+ "privatekey",
14955
+ "secret",
14956
+ "sessionid",
14957
+ "signature",
14958
+ "token"
14959
+ ];
14960
+ var isSensitiveKey = (key) => {
14961
+ const normalized = key.toLowerCase().replaceAll(/[^a-z0-9]/g, "");
14962
+ return SENSITIVE_KEY_STEMS.some((stem) => normalized.includes(stem));
14963
+ };
14964
+ var redactedJson = (value, redact) => {
14965
+ if (Array.isArray(value)) {
14966
+ return value.map((entry) => redactedJson(entry, redact));
14967
+ }
14968
+ if (typeof value === "object" && value !== null) {
14969
+ return Object.fromEntries(
14970
+ Object.entries(value).map(([key, entry]) => {
14971
+ return [key, isSensitiveKey(key) ? "[REDACTED]" : redactedJson(entry, redact)];
14972
+ })
14973
+ );
14974
+ }
14975
+ return typeof value === "string" ? redact(value) : value;
14976
+ };
14977
+ var redactedBody = (value, redact) => {
14978
+ let parsed;
14979
+ try {
14980
+ parsed = JSON.parse(value);
14981
+ } catch {
14982
+ return redact(value);
14983
+ }
14984
+ return typeof parsed === "object" && parsed !== null ? JSON.stringify(redactedJson(parsed, redact)) : redact(value);
14985
+ };
14986
+ var percentEncoded = (secret) => {
14987
+ try {
14988
+ return [encodeURIComponent(secret)];
14989
+ } catch {
14990
+ return [];
14991
+ }
14992
+ };
14993
+ var encodings = (secret) => {
14994
+ const form = String(new URLSearchParams({ v: secret })).slice(2);
14995
+ return [.../* @__PURE__ */ new Set([secret, JSON.stringify(secret).slice(1, -1), form, ...percentEncoded(secret)])];
14996
+ };
14997
+ var redactUrlParams = (value) => {
14998
+ return value.replaceAll(PARAM_MATCHER, (match, separator, param) => {
14999
+ return SENSITIVE_URL_PARAMS.has(param.toLowerCase()) ? `${separator}${param}=[REDACTED]` : match;
15000
+ });
15001
+ };
15002
+ var mappedAttrs = (attrs, redact) => {
15003
+ const redacted = {};
15004
+ for (const [key, value] of Object.entries(attrs)) {
15005
+ redacted[key] = typeof value === "string" ? redact(value) : value;
15006
+ }
15007
+ return redacted;
15008
+ };
15009
+ var redactAttrs = (attrs) => {
15010
+ return mappedAttrs(attrs, redactUrlParams);
15011
+ };
15012
+ var redactedEvent = (event, redact) => {
15013
+ return {
15014
+ ...event,
15015
+ name: redact(event.name),
15016
+ ...event.attrs === void 0 ? {} : { attrs: mappedAttrs(event.attrs, redact) }
15017
+ };
15018
+ };
15019
+ var redactedSpan = (span, redact) => {
15020
+ return {
15021
+ ...span,
15022
+ name: redact(span.name),
15023
+ ...span.attrs === void 0 ? {} : { attrs: mappedAttrs(span.attrs, redact) },
15024
+ ...span.events === void 0 ? {} : { events: span.events.map((event) => redactedEvent(event, redact)) },
15025
+ ...span.error === void 0 ? {} : { error: redact(span.error) }
15026
+ };
15027
+ };
15028
+ var redactSpans = (spans, redact) => {
15029
+ return spans.map((span) => {
15030
+ return redactedSpan(span, redact);
15031
+ });
15032
+ };
15033
+ var spanRedactor = (secrets) => {
15034
+ const forms = secrets.flatMap(encodings);
15035
+ const known = (value) => {
15036
+ let redacted = redactUrlParams(value);
15037
+ for (const form of forms) {
15038
+ redacted = redacted.replaceAll(form, "[REDACTED]");
15039
+ }
15040
+ return redacted;
15041
+ };
15042
+ return (value) => {
15043
+ return redactedBody(value, known);
15044
+ };
15045
+ };
15046
+
14931
15047
  // ../../../libs/tail-trace/src/scope.ts
14932
15048
  import { AsyncLocalStorage } from "node:async_hooks";
14933
15049
  var scopes = new AsyncLocalStorage();
@@ -14984,7 +15100,7 @@ var logRecord = (record2) => {
14984
15100
  console.log(formatSpansRecord(record2));
14985
15101
  };
14986
15102
  var runInvocation = async (input, run) => {
14987
- const buffer = { spans: [], flushed: false };
15103
+ const buffer = { spans: [], flushed: false, pending: [] };
14988
15104
  const traceId = input.trace?.traceId ?? newTraceId();
14989
15105
  const root = startSpan({ traceId, parent: input.trace?.parent, name: input.name, attrs: input.attrs });
14990
15106
  try {
@@ -14995,10 +15111,12 @@ var runInvocation = async (input, run) => {
14995
15111
  throw error51;
14996
15112
  } finally {
14997
15113
  root.end = Date.now();
15114
+ await Promise.allSettled(buffer.pending);
14998
15115
  buffer.spans.push(root);
14999
15116
  buffer.flushed = true;
15000
15117
  const emit = input.emit ?? logRecord;
15001
- emit(budgetSpans(buffer.spans, input.budget ?? MAX_SPANS_BYTES));
15118
+ const spans = input.redact === void 0 ? buffer.spans : redactSpans(buffer.spans, input.redact);
15119
+ emit(budgetSpans(spans, input.budget ?? MAX_SPANS_BYTES));
15002
15120
  }
15003
15121
  };
15004
15122
 
@@ -15031,54 +15149,6 @@ var parseTraceContext = (header) => {
15031
15149
  return { traceId: parsed["traceId"], ...parent === void 0 ? {} : { parent } };
15032
15150
  };
15033
15151
 
15034
- // ../../../libs/tail-trace/src/redact.ts
15035
- var SENSITIVE_URL_PARAMS = /* @__PURE__ */ new Set([
15036
- "access_token",
15037
- "api_key",
15038
- "api_secret",
15039
- "client_secret",
15040
- "code",
15041
- "key",
15042
- "password",
15043
- "refresh_token",
15044
- "secret",
15045
- "session",
15046
- "session_id",
15047
- "token"
15048
- ]);
15049
- var PARAM_MATCHER = /(^|[?&])([^=&?\s]+)=[^&\s]*/g;
15050
- var redactUrlParams = (value) => {
15051
- return value.replaceAll(PARAM_MATCHER, (match, separator, param) => {
15052
- return SENSITIVE_URL_PARAMS.has(param.toLowerCase()) ? `${separator}${param}=[REDACTED]` : match;
15053
- });
15054
- };
15055
- var redactAttrs = (attrs) => {
15056
- const redacted = {};
15057
- for (const [key, value] of Object.entries(attrs)) {
15058
- redacted[key] = typeof value === "string" ? redactUrlParams(value) : value;
15059
- }
15060
- return redacted;
15061
- };
15062
- var redactedEvent = (event) => {
15063
- return {
15064
- ...event,
15065
- name: redactUrlParams(event.name),
15066
- ...event.attrs === void 0 ? {} : { attrs: redactAttrs(event.attrs) }
15067
- };
15068
- };
15069
- var redactedSpan = (span) => {
15070
- return {
15071
- ...span,
15072
- name: redactUrlParams(span.name),
15073
- ...span.attrs === void 0 ? {} : { attrs: redactAttrs(span.attrs) },
15074
- ...span.events === void 0 ? {} : { events: span.events.map(redactedEvent) },
15075
- ...span.error === void 0 ? {} : { error: redactUrlParams(span.error) }
15076
- };
15077
- };
15078
- var redactAppSpans = (record2) => {
15079
- return { ...record2, spans: record2.spans.map(redactedSpan) };
15080
- };
15081
-
15082
15152
  // ../../../libs/tail-trace/src/trace.ts
15083
15153
  var traced = async (name, options, run) => {
15084
15154
  const scope = currentScope();
@@ -15099,22 +15169,37 @@ var traced = async (name, options, run) => {
15099
15169
  }
15100
15170
  }
15101
15171
  };
15102
- var liveSpan = () => {
15172
+ var liveScope = () => {
15103
15173
  const scope = currentScope();
15104
15174
  if (scope === void 0 || scope.buffer.flushed) {
15105
15175
  return void 0;
15106
15176
  }
15107
- return scope.span;
15177
+ return scope;
15108
15178
  };
15109
15179
  var setSpanAttributes = (attrs) => {
15110
- const span = liveSpan();
15180
+ const span = liveScope()?.span;
15111
15181
  if (span === void 0) {
15112
15182
  return;
15113
15183
  }
15114
15184
  span.attrs = { ...span.attrs, ...attrs };
15115
15185
  };
15186
+ var deferSpanAttributes = (values) => {
15187
+ const scope = liveScope();
15188
+ if (scope === void 0) {
15189
+ return;
15190
+ }
15191
+ scope.buffer.pending.push(
15192
+ // The continuation inherits the async context it was registered in, so this lands on the span
15193
+ // that was current when the capture began, not on whatever is current when the value arrives.
15194
+ values.then((attrs) => {
15195
+ if (attrs !== void 0) {
15196
+ setSpanAttributes(attrs);
15197
+ }
15198
+ })
15199
+ );
15200
+ };
15116
15201
  var addSpanEvent = (name, options = {}) => {
15117
- const span = liveSpan();
15202
+ const span = liveScope()?.span;
15118
15203
  if (span === void 0) {
15119
15204
  return;
15120
15205
  }
@@ -15127,6 +15212,34 @@ var addSpanEvent = (name, options = {}) => {
15127
15212
  span.events = [...span.events ?? [], event];
15128
15213
  };
15129
15214
 
15215
+ // src/read-body.ts
15216
+ var MAX_ENVELOPE_BYTES = 1024 * 1024;
15217
+ var readBody = async (request, maxBytes = MAX_ENVELOPE_BYTES) => {
15218
+ const reader = request.body?.getReader();
15219
+ if (reader === void 0) {
15220
+ return "";
15221
+ }
15222
+ const chunks = [];
15223
+ let received = 0;
15224
+ let result = await reader.read();
15225
+ while (!result.done) {
15226
+ received += result.value.byteLength;
15227
+ if (received > maxBytes) {
15228
+ await reader.cancel();
15229
+ return null;
15230
+ }
15231
+ chunks.push(result.value);
15232
+ result = await reader.read();
15233
+ }
15234
+ const bytes = new Uint8Array(received);
15235
+ let offset = 0;
15236
+ for (const chunk of chunks) {
15237
+ bytes.set(chunk, offset);
15238
+ offset += chunk.byteLength;
15239
+ }
15240
+ return new TextDecoder().decode(bytes);
15241
+ };
15242
+
15130
15243
  // src/apps-client.ts
15131
15244
  var BROKER_URL = "https://site/__qinto/rpc";
15132
15245
  var channelFetch = (env, init) => {
@@ -15146,7 +15259,20 @@ var askBroker = async (env, name, body) => {
15146
15259
  headers[TRACE_HEADER] = serializeTraceContext(trace);
15147
15260
  }
15148
15261
  const response = await channelFetch(env, { method: "POST", headers, body: JSON.stringify(body) });
15149
- const answer = await response.json();
15262
+ const raw = await readBody(response, MAX_RESPONSE_BYTES + 8 * 1024);
15263
+ if (raw === null) {
15264
+ throw new Error("the broker response exceeds the size cap");
15265
+ }
15266
+ let parsed;
15267
+ try {
15268
+ parsed = JSON.parse(raw);
15269
+ } catch {
15270
+ throw new Error("the broker returned invalid JSON");
15271
+ }
15272
+ if (typeof parsed !== "object" || parsed === null) {
15273
+ throw new Error("the broker returned invalid JSON");
15274
+ }
15275
+ const answer = parsed;
15150
15276
  if (answer.error !== void 0 || !response.ok) {
15151
15277
  throw new Error(answer.error ?? `the broker answered ${response.status}`);
15152
15278
  }
@@ -15198,6 +15324,18 @@ var brokerContextWrite = async (env, userId, writes) => {
15198
15324
  throw new Error(`the platform declined the ${declined.map((write) => write.level).join(" and ")} context write`);
15199
15325
  }
15200
15326
  };
15327
+ var brokerFactsRead = async (env, userId, input = {}) => {
15328
+ const continuation = "cursor" in input;
15329
+ const answer = await askBroker(env, "apps.facts read", {
15330
+ v: ENVELOPE_VERSION,
15331
+ kind: "factsRead",
15332
+ slug: callerSlug(env),
15333
+ key: env.QINTO_INTERNAL_KEY,
15334
+ userId,
15335
+ ...continuation ? { cursor: input.cursor } : { query: input }
15336
+ });
15337
+ return { items: answer.items ?? [], nextCursor: answer.nextCursor ?? null };
15338
+ };
15201
15339
  var brokerActive = async (env, input) => {
15202
15340
  let response;
15203
15341
  try {
@@ -15334,6 +15472,26 @@ var projectAppValues = (env, kinds) => {
15334
15472
  }
15335
15473
  return { appVariables, appSecrets };
15336
15474
  };
15475
+ var secretValues = (env) => {
15476
+ const { variableKinds, appVariableKinds } = contextKindsOf(env);
15477
+ const values = /* @__PURE__ */ new Set();
15478
+ for (const [name, byOrd] of parseVariableMaps(env)) {
15479
+ if (variableKinds[name] !== "secret") {
15480
+ continue;
15481
+ }
15482
+ for (const value of Object.values(byOrd)) {
15483
+ if (typeof value === "string" && value !== "") {
15484
+ values.add(value);
15485
+ }
15486
+ }
15487
+ }
15488
+ for (const value of Object.values(projectAppValues(env, appVariableKinds).appSecrets)) {
15489
+ if (value !== "") {
15490
+ values.add(value);
15491
+ }
15492
+ }
15493
+ return [...values];
15494
+ };
15337
15495
  var projectInstallValues = (maps, kinds, ord) => {
15338
15496
  const variables = {};
15339
15497
  const secrets = {};
@@ -15633,6 +15791,11 @@ var buildContext = ({
15633
15791
  }
15634
15792
  }
15635
15793
  },
15794
+ facts: {
15795
+ list: (input) => {
15796
+ return brokerFactsRead(env, envelope.user.anonId, input);
15797
+ }
15798
+ },
15636
15799
  context: {
15637
15800
  private: {
15638
15801
  entry: envelope.context?.private ?? null,
@@ -15655,40 +15818,12 @@ var buildContext = ({
15655
15818
  // src/install-span.ts
15656
15819
  var installSpan = (install, run) => {
15657
15820
  return traced(
15658
- `install #${install.ord}`,
15821
+ install.label,
15659
15822
  { attrs: { "app.install_id": install.id, "app.install_ord": install.ord, "app.install_label": install.label } },
15660
15823
  run
15661
15824
  );
15662
15825
  };
15663
15826
 
15664
- // src/read-body.ts
15665
- var MAX_ENVELOPE_BYTES = 1024 * 1024;
15666
- var readBody = async (request, maxBytes = MAX_ENVELOPE_BYTES) => {
15667
- const reader = request.body?.getReader();
15668
- if (reader === void 0) {
15669
- return "";
15670
- }
15671
- const chunks = [];
15672
- let received = 0;
15673
- let result = await reader.read();
15674
- while (!result.done) {
15675
- received += result.value.byteLength;
15676
- if (received > maxBytes) {
15677
- await reader.cancel();
15678
- return null;
15679
- }
15680
- chunks.push(result.value);
15681
- result = await reader.read();
15682
- }
15683
- const bytes = new Uint8Array(received);
15684
- let offset = 0;
15685
- for (const chunk of chunks) {
15686
- bytes.set(chunk, offset);
15687
- offset += chunk.byteLength;
15688
- }
15689
- return new TextDecoder().decode(bytes);
15690
- };
15691
-
15692
15827
  // src/responses.ts
15693
15828
  var MAX_ERROR_MESSAGE_LENGTH = 2048;
15694
15829
  var json2 = (status, body) => {
@@ -16041,15 +16176,9 @@ var appSlug = (context) => {
16041
16176
  return "app";
16042
16177
  }
16043
16178
  };
16044
- var headerValue = (record2) => {
16045
- const redacted = redactAppSpans(record2);
16046
- const rebudgeted = budgetSpans(redacted.spans, APP_SPANS_JSON_BUDGET);
16047
- const truncated = (record2.truncated ?? 0) + (rebudgeted.truncated ?? 0);
16048
- return serializeAppSpans({ spans: rebudgeted.spans, ...truncated === 0 ? {} : { truncated } });
16049
- };
16050
16179
  var withSpans = (response, record2) => {
16051
16180
  const headers = new Headers(response.headers);
16052
- headers.set(APP_SPANS_HEADER, headerValue(record2));
16181
+ headers.set(APP_SPANS_HEADER, serializeAppSpans(record2));
16053
16182
  return new Response(response.body, { status: response.status, statusText: response.statusText, headers });
16054
16183
  };
16055
16184
  var runAppInvocation = async (input, handle) => {
@@ -16060,6 +16189,7 @@ var runAppInvocation = async (input, handle) => {
16060
16189
  name: appSlug(input.context),
16061
16190
  trace: parseTraceContext(input.request.headers.get(TRACE_HEADER)),
16062
16191
  budget: APP_SPANS_JSON_BUDGET,
16192
+ redact: spanRedactor(input.secrets),
16063
16193
  emit: (emitted) => {
16064
16194
  record2 = emitted;
16065
16195
  }
@@ -16073,19 +16203,84 @@ var runAppInvocation = async (input, handle) => {
16073
16203
  };
16074
16204
 
16075
16205
  // src/traced-fetch.ts
16206
+ var MAX_BODY_CHARS = 65536;
16207
+ var TOO_LARGE = `[not recorded: the body is over ${MAX_BODY_CHARS} characters]`;
16208
+ var INCOMPLETE = "[not recorded: the body did not finish arriving]";
16209
+ var READ_BUDGET_MS = 100;
16076
16210
  var requestParts = (input, init) => {
16077
16211
  if (input instanceof Request) {
16078
16212
  return { method: init?.method ?? input.method, url: input.url };
16079
16213
  }
16080
16214
  return { method: init?.method ?? "GET", url: String(input) };
16081
16215
  };
16216
+ var abandon = (reader) => {
16217
+ void Promise.allSettled([reader.cancel()]);
16218
+ };
16219
+ var capturedBody = async (open) => {
16220
+ let timer;
16221
+ try {
16222
+ const body = open();
16223
+ if (body === null) {
16224
+ return void 0;
16225
+ }
16226
+ const reader = body.getReader();
16227
+ const decoder2 = new TextDecoder();
16228
+ const expired = new Promise((resolve) => {
16229
+ timer = setTimeout(() => resolve(null), READ_BUDGET_MS);
16230
+ });
16231
+ let text = "";
16232
+ for (; ; ) {
16233
+ const read = await Promise.race([reader.read(), expired]);
16234
+ if (read === null) {
16235
+ abandon(reader);
16236
+ return INCOMPLETE;
16237
+ }
16238
+ if (read.done) {
16239
+ break;
16240
+ }
16241
+ text += decoder2.decode(read.value, { stream: true });
16242
+ if (text.length > MAX_BODY_CHARS) {
16243
+ abandon(reader);
16244
+ return TOO_LARGE;
16245
+ }
16246
+ }
16247
+ return text === "" ? void 0 : text;
16248
+ } catch {
16249
+ return void 0;
16250
+ } finally {
16251
+ clearTimeout(timer);
16252
+ }
16253
+ };
16254
+ var requestBody = (input, init) => {
16255
+ const body = init?.body;
16256
+ if (body === void 0) {
16257
+ return input instanceof Request ? capturedBody(() => input.clone().body) : Promise.resolve(void 0);
16258
+ }
16259
+ if (body instanceof ReadableStream) {
16260
+ return Promise.resolve(void 0);
16261
+ }
16262
+ return capturedBody(() => new Request("https://qinto.invalid", { method: "POST", body }).body);
16263
+ };
16264
+ var responseBody = (response) => {
16265
+ if ((response.headers.get("content-type") ?? "").startsWith("text/event-stream")) {
16266
+ return Promise.resolve(void 0);
16267
+ }
16268
+ return capturedBody(() => response.clone().body);
16269
+ };
16270
+ var bodyAttribute = async (key, body) => {
16271
+ const text = await body;
16272
+ return text === void 0 ? void 0 : { [key]: text };
16273
+ };
16082
16274
  var installTracedFetch = () => {
16083
16275
  const original = globalThis.fetch.bind(globalThis);
16084
16276
  globalThis.fetch = async (input, init) => {
16085
16277
  const { method, url: url2 } = requestParts(input, init);
16278
+ const sent = requestBody(input, init);
16086
16279
  return traced(`${method} ${new URL(url2).host}`, { attrs: { "http.request.method": method, "url.full": url2 } }, async () => {
16280
+ deferSpanAttributes(bodyAttribute("http.request.body", sent));
16087
16281
  const response = await original(input, init);
16088
16282
  setSpanAttributes({ "http.response.status_code": response.status });
16283
+ deferSpanAttributes(bodyAttribute("http.response.body", responseBody(response)));
16089
16284
  return response;
16090
16285
  });
16091
16286
  };
@@ -16430,6 +16625,7 @@ var createRuntime = (input) => {
16430
16625
  {
16431
16626
  request,
16432
16627
  context: env.APP_CONTEXT,
16628
+ secrets: secretValues(env),
16433
16629
  failure: () => {
16434
16630
  return jsonError(500, "the app failed");
16435
16631
  }
@@ -1 +1 @@
1
- (function(){var e=class extends Error{constructor(e){super(e),this.name=`QintoError`}},t=class extends e{constructor(e){super(e),this.name=`QintoTimeoutError`}},n=class extends e{constructor(e){super(e),this.name=`QintoEnvironmentError`}},r=`0123456789ABCDEFGHJKMNPQRSTVWXYZ`,i=e=>{let t=new Uint8Array(e);return globalThis.crypto.getRandomValues(t),t},a=e=>{let t=e,n=``;for(let e=0;e<10;e++)n=r.charAt(t%32)+n,t=Math.floor(t/32);let a=``,o=0,s=0;for(let e of i(10))for(o=o<<8|e,s+=8;s>=5;)s-=5,a+=r.charAt(o>>>s&31);return n+a},o=(e=Date.now())=>`evt_${a(e)}_${e}`,s=(e,t)=>{if(e!==void 0)return e;let n=t?.orderId;return typeof n==`string`&&n!==``?n:o()},c=()=>globalThis.document!==void 0,l=[`necessary`,`functional`,`analytics`,`marketing`,`salesOfData`],u=(e,t,n)=>n===void 0||!l.some(e=>e===n)?!1:n===`necessary`||e!==null&&e.categories[n]===!0&&e.apps[t]!==!1,d=()=>{},f=100,p=1024,m=32*1024,h=(e,t,n,r)=>!r||u(e,t,n),g=e=>Object.values(e.config?.apps??{}).some(e=>e.category!==`necessary`||e.capture!==void 0),ee=(e,t)=>{let{app:n,install:r,topic:i,to:a}=e;return typeof n!=`string`||typeof i!=`string`||r!==void 0&&typeof r!=`string`||t&&!(Array.isArray(a)&&a.every(e=>typeof e==`string`))},te=(e,t,n,r)=>r===null?e.slug===t&&(n===void 0||n===e.installId):r.has(e.slug),_=(e,t)=>{console.error(`[Qinto] A handler of app "${e.slug}" (install "${e.installId}") failed.`,t)},v=(e,t,n)=>{for(let r of t)try{let t=r(n);t instanceof Promise&&t.catch(t=>{_(e,t)})}catch(t){_(e,t)}},y=(e,t,n,r)=>{e.activated.add(r.id);let i={slug:t,installId:r.id,eventHandlers:[],consentHandlers:[],pushHandlers:[],sharedContextHandlers:[]};e.instances.push(i);let a={variables:r.variables,install:{id:r.id,label:r.label},track:(t,n)=>{e.client.track(t,n).catch(d)},onEvent:e=>{i.eventHandlers.push(e)},onConsent:e=>{i.consentHandlers.push(e)},onPush:e=>{i.pushHandlers.push(e)},onSharedContext:n=>{if(e.target.config?.apps?.[t]?.sharedContext!==!0){console.error(`[Qinto] App "${t}" reads shared context without declaring the sharedContext capability.`);return}i.sharedContextHandlers.push(n),e.sharedContext!==null&&S(e,t)&&v(i,[n],e.sharedContext)}};try{n(a)}catch(e){console.error(`[Qinto] App "${t}" failed to activate install "${r.id}".`,e)}},b=(e,t,n)=>{if(!h(e.consent,t,e.target.config?.apps?.[t]?.category,e.target.config?.consentConfiguration?.enabled===!0))return;let r=((`_appInstalls`in e.target?e.target._appInstalls:void 0)?.[t]??[]).filter(t=>!e.activated.has(t.id));if(r.length===0)return;let i=n.factory;if(i===void 0){try{i=n.loader()}catch(e){i=null,console.error(`[Qinto] App "${t}" failed to load.`,e)}e.apps.set(t,{...n,factory:i})}if(i!==null)for(let n of r)y(e,t,i,n)},x=(e,t,n)=>h(n,t,e.target.config?.apps?.[t]?.category,e.target.config?.consentConfiguration?.enabled===!0),S=(e,t)=>x(e,t,e.consent)?e.sharedContextConsent===void 0||x(e,t,e.sharedContextConsent):!1,C=e=>{let{categories:t,apps:n}=e;if(!(typeof t!=`object`||!t||typeof n!=`object`||!n))return{categories:t,apps:n}},w=(e,t)=>{let n=e;n.consent=t,n.sharedContextConsent=void 0;for(let n of e.instances)v(n,n.consentHandlers,t);e.onConsentSettled?.(t);for(let[t,n]of e.apps)b(e,t,n)},T=async e=>{try{w(e,await e.client.getConsent())}catch(e){console.error(`[Qinto] The apps consent read failed.`,e)}},ne=e=>{let t;try{t=JSON.stringify(e)}catch{return}if(!(t===void 0||new TextEncoder().encode(t).length>p))return JSON.parse(t)},re=e=>{let t={};for(let n of e){if(n.settled!==n.total||n.values.length!==1)continue;let e=ne(n.values[0]);if(e===void 0)continue;let r={...t[n.instance.slug]??{},[n.instance.installId]:e},i={...t,[n.instance.slug]:r};new TextEncoder().encode(JSON.stringify(i)).length<=m&&(t[n.instance.slug]=r)}return Object.keys(t).length===0?void 0:t},E=async(e,t)=>{e.onPageEvent?.(e.consent);let n=[],r=[];for(let i of e.instances){if(!h(e.consent,i.slug,e.target.config?.apps?.[i.slug]?.category,e.target.config?.consentConfiguration?.enabled===!0))continue;if(e.target.config?.apps?.[i.slug]?.eventResults?.includes(t.type)!==!0){v(i,i.eventHandlers,t);continue}let a={instance:i,total:i.eventHandlers.length,settled:0,values:[]};n.push(a);for(let e of i.eventHandlers){let n;try{n=e(t)}catch(e){a.settled+=1,_(i,e);continue}r.push(Promise.resolve(n).then(e=>{a.settled+=1,e!==void 0&&a.values.push(e)},e=>{a.settled+=1,_(i,e)}))}}if(r.length>0){let e=d,t=new Promise(t=>{e=t}),n=setTimeout(e,f);await Promise.race([Promise.all(r),t]),clearTimeout(n)}return re(n)},ie=e=>{let{client:t}=e;return{track:async(n,r,i)=>{let a=s(i?.eventId,r),o=t.actionContext(),c=await E(e,{type:`track`,event:n,eventId:a,properties:r??{}});return await t.submitTrack({event:n,eventId:a,properties:r,context:o,appResults:c}),{eventId:a}},identify:async(n,r)=>{let i=t.actionContext(),a=await E(e,{type:`identify`,event:`identify`,properties:n??{}});return t.submitIdentify({traits:n,id:r,context:i,appResults:a})},alias:t.alias,consent:async n=>{if(await t.submitConsent(n,t.actionContext()),e.onConsentMutation?.(),g(e.target)){let t=e;t.consent=null,await T(e)}},getAnonId:t.getAnonId,getConsent:t.getConsent,getSession:t.getSession,close:t.close}},ae=(e,t)=>{let n=new Map,r=null,i=null,a=e=>{let[t,i]=e;if(n.has(t))return;let a={loader:i};n.set(t,a),r?.(t,a)},o=e,s=`_apps`in o?o._apps:void 0,c=Array.isArray(s)?s:[];c.push=(...e)=>{for(let t of e)a(t);return 0},o._apps=c;for(let e of c.splice(0))a(e);return{sharedContext:e=>{let{context:t}=e;if(typeof t!=`object`||!t||Array.isArray(t)){console.error(`[Qinto] The server sent a malformed shared context.`,e);return}let n=i;if(n===null)return;n.sharedContext=t;let r=C(e),a=r??n.consent;n.sharedContextConsent=r;for(let e of n.instances)x(n,e.slug,a)&&v(e,e.sharedContextHandlers,n.sharedContext)},push:e=>{let{app:t,install:n,topic:r,scope:a,to:o}=e,s=a===`site`;if(ee(e,s)){console.error(`[Qinto] The server sent a malformed app push.`,e);return}let c=i;if(c===null)return;let l=s?new Set(o):null,u={topic:r,data:e.data,from:t};for(let e of c.instances)te(e,t,n,l)&&h(c.consent,e.slug,c.target.config?.apps?.[e.slug]?.category,c.target.config?.consentConfiguration?.enabled===!0)&&v(e,e.pushHandlers,u)},connect:async a=>{let o={target:e,client:a,apps:n,instances:[],activated:new Set,consent:null,sharedContext:i?.sharedContext??null,sharedContextConsent:i?.sharedContextConsent,onConsentSettled:t?.onConsentSettled,onConsentMutation:t?.onConsentMutation,onPageEvent:t?.onPageEvent};i=o,r=(e,t)=>{b(o,e,t),o.consent===null&&o.target.config?.consentConfiguration?.enabled===!0&&o.target.config?.apps?.[e]?.category!==`necessary`&&T(o)};for(let[e,t]of n)b(o,e,t);return e.config?.consentConfiguration?.enabled===!0&&g(e)?await T(o):o.onConsentSettled?.(o.consent),ie(o)}}},oe=`PageView`,D=()=>{},se=e=>{e.config?.autoCapture?.pageLoad===!0&&e.q.push([`track`,[oe],D,D])},O=512,k=1440*60*1e3,A=e=>Object.fromEntries(Object.entries(e).map(([e,t])=>[e,Object.fromEntries(Object.entries(t).map(([e,t])=>[e,t.value]))])),j=(e,t)=>`qinto:cap:${encodeURIComponent(e)}:${encodeURIComponent(t)}`,M=e=>{try{let t=window.sessionStorage.getItem(e);if(t===null)return null;let n=JSON.parse(t);return typeof n.at!=`number`||typeof n.anonId!=`string`||typeof n.apps!=`object`||n.apps===null||Date.now()-n.at>k?null:{at:n.at,anonId:n.anonId,apps:n.apps}}catch{return null}},N=(e,t)=>{try{window.sessionStorage.setItem(e,JSON.stringify(t))}catch{}},P=e=>{try{window.sessionStorage.removeItem(e)}catch{}},F=e=>{try{return decodeURIComponent(e)}catch{return e}},I=()=>{let e={};for(let t of document.cookie.split(`;`)){let n=t.indexOf(`=`);n>0&&(e[t.slice(0,n).trim()]=F(t.slice(n+1)))}return e},L=(e,t)=>{let n={};for(let r of e.queryParams){let e=t.params.get(r);e!==null&&e.length<=O&&(n[r]={value:e,source:`query`})}for(let r of e.cookies){let e=t.cookies[r];e!==void 0&&e.length<=O&&(n[r]={value:e,source:`cookie`})}return n},R=(e,t)=>{let n={params:new URLSearchParams(window.location.search),cookies:I()},r={};for(let[i,a]of Object.entries(e.config?.apps??{})){if(a.capture===void 0||!u(t,i,a.category))continue;let e=L(a.capture,n);Object.keys(e).length>0&&(r[i]=e)}return r},z=(e,t)=>{let n={};for(let[r,i]of Object.entries(e)){let e=t[r]??{},a={};for(let[t,n]of Object.entries(i))(e[t]?.value!==n.value||e[t]?.source!==n.source)&&(a[t]=n);Object.keys(a).length>0&&(n[r]=a)}return n},B=(e,t,n,r)=>{let i=j(r,n),a=M(i),o=a?.apps??{},s=a?.anonId??null,c=a===null,l={},u=0,d=(e,t)=>{s!==null&&s!==e.anonId&&(o={}),s=e.anonId;let n=new Set(Object.entries(e.accepted).flatMap(([e,t])=>t.map(t=>`${e}\u0000${t}`)));for(let[e,r]of Object.entries(t))for(let[t,i]of Object.entries(r))n.has(`${e}\u0000${t}`)&&(o[e]={...o[e],[t]:i});N(i,{at:Date.now(),anonId:s,apps:o})},f=null,p=()=>{u+=1,o={},l={},P(i)},m=n=>{if(f=n,!c)return;let r=z(R(e,n),{...o,...l});if(Object.keys(r).length===0)return;let i=u;l={...l,...r},t(A(r)).then(e=>{i===u&&d(e,r)}).catch(()=>{}).finally(()=>{l={}})};return{settled:e=>{m(e)},needsAnon:()=>!c,observedAnon:e=>{(e===null||s!==null&&s!==e)&&p(),s=e,c=!0,m(f)},clear:p}},V=Symbol.for(`qinto.connectGate`),H=()=>{},U=e=>{let t=globalThis,n=(t[V]??Promise.resolve()).then(e);return t[V]=n.then(H,H),n},W=class extends e{constructor(e){super(e),this.name=`QintoProtocolError`}},G=e=>{if(typeof e!=`string`)return null;let t;try{t=JSON.parse(e)}catch{return null}if(typeof t!=`object`||!t)return null;let n=t;return n.t===`ack`||n.t===`error`||n.t===`consentState`||n.t===`session`||n.t===`captureAck`||n.t===`appPush`||n.t===`sharedContext`?t:null},K=e=>{if(e.t===`consentState`)return{categories:e.categories,apps:e.apps};if(e.t===`session`)return{anonId:e.anonId,globalId:e.globalId};if(e.t===`captureAck`)return{anonId:e.anonId,accepted:e.accepted}},ce=e=>{let n=new Map,r=e=>{let t=n.get(e);if(t!==void 0)return clearTimeout(t.timer),n.delete(e),t};return{submit:r=>new Promise((i,a)=>{let o={frame:r,sent:e.send(r),resolve:i,reject:a,timer:setTimeout(()=>{n.delete(r.ref),a(new t(`The Qinto request timed out.`))},e.requestTimeoutMs)};n.set(r.ref,o)}),flush:()=>{for(let t of n.values())t.sent||=e.send(t.frame)},deliver:e=>{let t=e.ref===void 0?void 0:r(e.ref);if(t!==void 0){if(e.t===`error`){t.reject(new W(e.message));return}t.resolve(K(e))}},rejectAll:e=>{let t=[...n.values()];n.clear();for(let n of t)clearTimeout(n.timer),n.reject(e)}}},le=e=>new WebSocket(e),ue=500,de=3e4,fe=`session not initialized`,pe=(e,t)=>{let n=Math.min(ue*2**e,de);return Math.round(n/2+n/2*t)},me=(e,t)=>e.t===`appPush`?(t.onAppPush?.(e),!0):e.t===`sharedContext`?(t.onSharedContext?.(e),!0):!1,he=e=>{let t=`${e.destination.replace(/\/+$/,``)}/open`,n=null,r=null,i=0,a=null,o=ce({send:e=>n===null||n.readyState!==1?!1:(n.send(JSON.stringify(e)),!0),requestTimeoutMs:e.requestTimeoutMs}),s=t=>{e.onError?.(t)},c=()=>{if(r!==null&&(clearTimeout(r),r=null),n!==null){let e=n;n=null,e.close()}o.rejectAll(new W(`connection closed`))},l=()=>{let u=le(t);n=u,u.onopen=()=>{i=0;let e=a;a=null,o.flush(),e?.resolve()},u.onmessage=t=>{let n=G(t.data);if(n===null){s(new W(`invalid message`));return}me(n,e)||(`anonId`in n&&typeof n.anonId==`string`&&e.onAnonId?.(n.anonId),o.deliver(n),n.t===`error`&&n.message===fe&&u.close())},u.onerror=()=>{s(new W(`connection closed`))},u.onclose=()=>{if(n!==u)return;n=null;let e=a;if(e!==null){a=null,c(),e.reject(new W(`connection closed`));return}o.rejectAll(new W(`connection closed`)),r=setTimeout(()=>{r=null,l()},pe(i,Math.random())),i+=1}};return{connect:()=>new Promise((e,t)=>{a={resolve:e,reject:t},l()}),submit:e=>o.submit(e),close:()=>{c()}}},ge=1e4,_e=()=>{let e=0;return()=>(e+=1,`q${e}`)},ve=(e,t)=>{let n=`qinto:visit:${e.replace(/\/+$/,``)}:${t}`,r,i=!1;try{let e=sessionStorage.getItem(n);e===null||!/^vis_[0-9a-f-]{36}$/.test(e)?(r=`vis_${crypto.randomUUID()}`,sessionStorage.setItem(n,r),i=!0):r=e}catch{r=`vis_${crypto.randomUUID()}`,i=!0}return()=>{let e={id:r,isNew:i};return i=!1,e}},ye=(e,t,n,r)=>{let i=null,a=ve(n,r),o=()=>{let e=document.referrer;return{occurredAt:Date.now(),page:{url:window.location.href,...e===``?{}:{referrer:e}},session:a()}},c=async()=>{let n=await e.submit({t:`getSession`,ref:t()});return i=n.anonId,n};return{actionContext:o,submitIdentify:async({traits:n,id:r,context:i,appResults:a})=>{await e.submit({t:`identify`,ref:t(),traits:n,id:r,context:i,appResults:a})},submitTrack:async({event:n,eventId:r,properties:i,context:a,appResults:o})=>{await e.submit({t:`track`,ref:t(),event:n,eventId:r,properties:i,context:a,appResults:o})},submitConsent:async(n,r)=>{await e.submit({t:`consent`,ref:t(),categories:n.categories,apps:n.apps,context:r})},identify:async(n,r)=>{await e.submit({t:`identify`,ref:t(),traits:n,id:r,context:o()})},track:async(n,r,i)=>{let a=s(i?.eventId,r);return await e.submit({t:`track`,ref:t(),event:n,eventId:a,properties:r,context:o()}),{eventId:a}},alias:async n=>{await e.submit({t:`alias`,ref:t(),customId:n})},consent:async n=>{await e.submit({t:`consent`,ref:t(),categories:n.categories,apps:n.apps,context:o()})},getConsent:async()=>await e.submit({t:`getConsent`,ref:t()}),getSession:c,getAnonId:async()=>(i===null&&await c(),i),close:()=>{e.close()}}},be=async e=>{if(!c())throw new n(`You are calling init() in a server environment. Use createClient from @blotout/qinto-sdk-server there.`);let t=he({destination:e.destination,requestTimeoutMs:e.requestTimeoutMs??ge,onError:e.onError,onAppPush:e.onAppPush,onSharedContext:e.onSharedContext,onAnonId:e.onAnonId});await U(()=>t.connect());let r=_e();return{client:ye(t,r,e.destination,e.globalName??`qinto`),submitCapture:async e=>await t.submit({t:`capture`,ref:r(),apps:e})}},xe=[`then`,`ready`,`config`],q=()=>{},Se=(e,t)=>(...n)=>{let r=new Promise((r,i)=>{e.q.push([t,n,r,i])});return r.catch(q),r},J=e=>{let t=e;return t._q=1,t.q=[],new Proxy(t,{get:(e,n)=>typeof n!=`string`||xe.includes(n)||n in t?Reflect.get(t,n):(t[n]=Se(t,n),t[n])})},Ce=()=>{let e=document.currentScript?.dataset.qintoGlobal;return typeof e==`string`&&e!==``?e:`qinto`},we=e=>typeof e==`object`&&!!e&&e._q===1,Y=(e=`qinto`)=>{let t=globalThis;if(e in t){let n=t[e];if(!we(n))return null;let r=Array.isArray(n.q)?n:J(n);return r!==n&&Object.defineProperty(t,e,{value:r,writable:!0,enumerable:!0,configurable:!0}),r}let n=J({});return Object.defineProperty(t,e,{value:n,writable:!0,enumerable:!0,configurable:!0}),n},Te=(t,n)=>async(r,i)=>{let a=Reflect.get(t,r);if(typeof a!=`function`)throw new e(`[Qinto] ${n}.${r} is not a method of this tag.`);return a.apply(t,Array.from(i))},Ee=e=>()=>Promise.reject(e),X=(e,t)=>{let[n,r,i,a]=t;e(n,r).then(i,a)},De=e=>{let t=[];return t.push=(...t)=>{for(let n of t)X(e,n);return 0},t},Oe=(e,t,n=`window.qinto`)=>{let r=e.ready;if(r!==void 0)return r;let i=e,a=e=>{let t=i.q;i.q=De(e);for(let n of t)X(e,n)},o=t().then(e=>{a(Te(e,n))},e=>{throw a(Ee(e)),e});return o.catch(q),i.ready=o,o},Z=Ce(),Q=Y(Z),$=`window.${Z}`;if(Q!==null&&Q.ready===void 0){let e=null,t=ae(Q,{onConsentSettled:t=>{e?.settled(t)},onConsentMutation:()=>{e?.clear()},onPageEvent:t=>{e?.settled(t)}});se(Q),Oe(Q,async()=>{let n=Q.config?.destination;if(typeof n!=`string`||n===``)throw Error(`[Qinto] ${$}.config.destination is missing.`);let r=await be({destination:n,globalName:Z,onAppPush:t.push,onSharedContext:t.sharedContext,onAnonId:t=>{e?.observedAnon(t)}});e=B(Q,r.submitCapture,n,Z);let i=await t.connect(r.client);return e.needsAnon()&&e.observedAnon((await r.client.getSession()).anonId),i},$)}})();
1
+ (function(){var e=class extends Error{constructor(e){super(e),this.name=`QintoError`}},t=class extends e{constructor(e){super(e),this.name=`QintoTimeoutError`}},n=class extends e{constructor(e){super(e),this.name=`QintoEnvironmentError`}},r=`0123456789ABCDEFGHJKMNPQRSTVWXYZ`,i=e=>{let t=new Uint8Array(e);return globalThis.crypto.getRandomValues(t),t},a=e=>{let t=e,n=``;for(let e=0;e<10;e++)n=r.charAt(t%32)+n,t=Math.floor(t/32);let a=``,o=0,s=0;for(let e of i(10))for(o=o<<8|e,s+=8;s>=5;)s-=5,a+=r.charAt(o>>>s&31);return n+a},o=(e=Date.now())=>`evt_${a(e)}_${e}`,s=(e,t)=>{if(e!==void 0)return e;let n=t?.orderId;return typeof n==`string`&&n!==``?n:o()},c=()=>globalThis.document!==void 0,l=[`necessary`,`functional`,`analytics`,`marketing`,`salesOfData`],u=(e,t,n)=>n===void 0||!l.some(e=>e===n)?!1:n===`necessary`||e!==null&&e.categories[n]===!0&&e.apps[t]!==!1,d=()=>{},f=100,p=1024,m=32*1024,h=(e,t,n,r)=>!r||u(e,t,n),g=e=>Object.values(e.config?.apps??{}).some(e=>e.category!==`necessary`||e.capture!==void 0),ee=(e,t)=>{let{app:n,install:r,topic:i,to:a}=e;return typeof n!=`string`||typeof i!=`string`||r!==void 0&&typeof r!=`string`||t&&!(Array.isArray(a)&&a.every(e=>typeof e==`string`))},te=(e,t,n,r)=>r===null?e.slug===t&&(n===void 0||n===e.installId):r.has(e.slug),_=(e,t)=>{console.error(`[Qinto] A handler of app "${e.slug}" (install "${e.installId}") failed.`,t)},v=(e,t,n)=>{for(let r of t)try{let t=r(n);t instanceof Promise&&t.catch(t=>{_(e,t)})}catch(t){_(e,t)}},y=(e,t,n,r)=>{e.activated.add(r.id);let i={slug:t,installId:r.id,eventHandlers:[],consentHandlers:[],pushHandlers:[],sharedContextHandlers:[]};e.instances.push(i);let a={variables:r.variables,install:{id:r.id,label:r.label},track:(t,n)=>{e.client.track(t,n).catch(d)},onEvent:e=>{i.eventHandlers.push(e)},onConsent:e=>{i.consentHandlers.push(e)},onPush:e=>{i.pushHandlers.push(e)},onSharedContext:n=>{if(e.target.config?.apps?.[t]?.sharedContext!==!0){console.error(`[Qinto] App "${t}" reads shared context without declaring the sharedContext capability.`);return}i.sharedContextHandlers.push(n),e.sharedContext!==null&&S(e,t)&&v(i,[n],e.sharedContext)}};try{n(a)}catch(e){console.error(`[Qinto] App "${t}" failed to activate install "${r.id}".`,e)}},b=(e,t,n)=>{if(!h(e.consent,t,e.target.config?.apps?.[t]?.category,e.target.config?.consentConfiguration?.enabled===!0))return;let r=((`_appInstalls`in e.target?e.target._appInstalls:void 0)?.[t]??[]).filter(t=>!e.activated.has(t.id));if(r.length===0)return;let i=n.factory;if(i===void 0){try{i=n.loader()}catch(e){i=null,console.error(`[Qinto] App "${t}" failed to load.`,e)}e.apps.set(t,{...n,factory:i})}if(i!==null)for(let n of r)y(e,t,i,n)},x=(e,t,n)=>h(n,t,e.target.config?.apps?.[t]?.category,e.target.config?.consentConfiguration?.enabled===!0),S=(e,t)=>x(e,t,e.consent)?e.sharedContextConsent===void 0||x(e,t,e.sharedContextConsent):!1,C=e=>{let{categories:t,apps:n}=e;if(!(typeof t!=`object`||!t||typeof n!=`object`||!n))return{categories:t,apps:n}},w=(e,t)=>{let n=e;n.consent=t,n.sharedContextConsent=void 0;for(let n of e.instances)v(n,n.consentHandlers,t);e.onConsentSettled?.(t);for(let[t,n]of e.apps)b(e,t,n)},T=async e=>{try{w(e,await e.client.getConsent())}catch(e){console.error(`[Qinto] The apps consent read failed.`,e)}},ne=e=>{let t;try{t=JSON.stringify(e)}catch{return}if(!(t===void 0||new TextEncoder().encode(t).length>p))return JSON.parse(t)},re=e=>{let t={};for(let n of e){if(n.settled!==n.total||n.values.length!==1)continue;let e=ne(n.values[0]);if(e===void 0)continue;let r={...t[n.instance.slug]??{},[n.instance.installId]:e},i={...t,[n.instance.slug]:r};new TextEncoder().encode(JSON.stringify(i)).length<=m&&(t[n.instance.slug]=r)}return Object.keys(t).length===0?void 0:t},E=async(e,t)=>{e.onPageEvent?.(e.consent);let n=[],r=[];for(let i of e.instances){if(!h(e.consent,i.slug,e.target.config?.apps?.[i.slug]?.category,e.target.config?.consentConfiguration?.enabled===!0))continue;if(e.target.config?.apps?.[i.slug]?.eventResults?.includes(t.type)!==!0){v(i,i.eventHandlers,t);continue}let a={instance:i,total:i.eventHandlers.length,settled:0,values:[]};n.push(a);for(let e of i.eventHandlers){let n;try{n=e(t)}catch(e){a.settled+=1,_(i,e);continue}r.push(Promise.resolve(n).then(e=>{a.settled+=1,e!==void 0&&a.values.push(e)},e=>{a.settled+=1,_(i,e)}))}}if(r.length>0){let e=d,t=new Promise(t=>{e=t}),n=setTimeout(e,f);await Promise.race([Promise.all(r),t]),clearTimeout(n)}return re(n)},ie=e=>{let{client:t}=e;return{track:async(n,r,i)=>{let a=s(i?.eventId,r),o=t.actionContext(),c=await E(e,{type:`track`,event:n,eventId:a,properties:r??{}});return await t.submitTrack({event:n,eventId:a,properties:r,context:o,appResults:c}),{eventId:a}},identify:async(n,r)=>{let i=t.actionContext(),a=await E(e,{type:`identify`,event:`identify`,properties:n??{}});return t.submitIdentify({traits:n,id:r,context:i,appResults:a})},alias:t.alias,consent:async n=>{if(await t.submitConsent(n,t.actionContext()),e.onConsentMutation?.(),g(e.target)){let t=e;t.consent=null,await T(e)}},getAnonId:t.getAnonId,getConsent:t.getConsent,getSession:t.getSession,close:t.close}},ae=(e,t)=>{let n=new Map,r=null,i=null,a=e=>{let[t,i]=e;if(n.has(t))return;let a={loader:i};n.set(t,a),r?.(t,a)},o=e,s=`_apps`in o?o._apps:void 0,c=Array.isArray(s)?s:[];c.push=(...e)=>{for(let t of e)a(t);return 0},o._apps=c;for(let e of c.splice(0))a(e);return{sharedContext:e=>{let{context:t}=e;if(typeof t!=`object`||!t||Array.isArray(t)){console.error(`[Qinto] The server sent a malformed shared context.`,e);return}let n=i;if(n===null)return;n.sharedContext=t;let r=C(e),a=r??n.consent;n.sharedContextConsent=r;for(let e of n.instances)x(n,e.slug,a)&&v(e,e.sharedContextHandlers,n.sharedContext)},push:e=>{let{app:t,install:n,topic:r,scope:a,to:o}=e,s=a===`site`;if(ee(e,s)){console.error(`[Qinto] The server sent a malformed app push.`,e);return}let c=i;if(c===null)return;let l=s?new Set(o):null,u={topic:r,data:e.data,from:t};for(let e of c.instances)te(e,t,n,l)&&h(c.consent,e.slug,c.target.config?.apps?.[e.slug]?.category,c.target.config?.consentConfiguration?.enabled===!0)&&v(e,e.pushHandlers,u)},connect:async a=>{let o={target:e,client:a,apps:n,instances:[],activated:new Set,consent:null,sharedContext:i?.sharedContext??null,sharedContextConsent:i?.sharedContextConsent,onConsentSettled:t?.onConsentSettled,onConsentMutation:t?.onConsentMutation,onPageEvent:t?.onPageEvent};i=o,r=(e,t)=>{b(o,e,t),o.consent===null&&o.target.config?.consentConfiguration?.enabled===!0&&o.target.config?.apps?.[e]?.category!==`necessary`&&T(o)};for(let[e,t]of n)b(o,e,t);return e.config?.consentConfiguration?.enabled===!0&&g(e)?await T(o):o.onConsentSettled?.(o.consent),ie(o)}}},oe=`PageView`,D=()=>{},se=e=>{e.config?.autoCapture?.pageLoad===!0&&e.q.push([`track`,[oe],D,D])},O=512,k=1440*60*1e3,A=e=>Object.fromEntries(Object.entries(e).map(([e,t])=>[e,Object.fromEntries(Object.entries(t).map(([e,t])=>[e,t.value]))])),j=(e,t)=>`qinto:cap:${encodeURIComponent(e)}:${encodeURIComponent(t)}`,M=e=>{try{let t=window.sessionStorage.getItem(e);if(t===null)return null;let n=JSON.parse(t);return typeof n.at!=`number`||typeof n.anonId!=`string`||typeof n.apps!=`object`||n.apps===null||Date.now()-n.at>k?null:{at:n.at,anonId:n.anonId,apps:n.apps}}catch{return null}},N=(e,t)=>{try{window.sessionStorage.setItem(e,JSON.stringify(t))}catch{}},P=e=>{try{window.sessionStorage.removeItem(e)}catch{}},F=e=>{try{return decodeURIComponent(e)}catch{return e}},I=()=>{let e={};for(let t of document.cookie.split(`;`)){let n=t.indexOf(`=`);n>0&&(e[t.slice(0,n).trim()]=F(t.slice(n+1)))}return e},L=(e,t)=>{let n={};for(let r of e.queryParams){let e=t.params.get(r);e!==null&&e.length<=O&&(n[r]={value:e,source:`query`})}for(let r of e.cookies){let e=t.cookies[r];e!==void 0&&e.length<=O&&(n[r]={value:e,source:`cookie`})}return n},R=(e,t)=>{let n={params:new URLSearchParams(window.location.search),cookies:I()},r={};for(let[i,a]of Object.entries(e.config?.apps??{})){if(a.capture===void 0||!u(t,i,a.category))continue;let e=L(a.capture,n);Object.keys(e).length>0&&(r[i]=e)}return r},z=(e,t)=>{let n={};for(let[r,i]of Object.entries(e)){let e=t[r]??{},a={};for(let[t,n]of Object.entries(i))(e[t]?.value!==n.value||e[t]?.source!==n.source)&&(a[t]=n);Object.keys(a).length>0&&(n[r]=a)}return n},B=(e,t,n,r)=>{let i=j(r,n),a=M(i),o=a?.apps??{},s=a?.anonId??null,c=a===null,l={},u=0,d=(e,t)=>{s!==null&&s!==e.anonId&&(o={}),s=e.anonId;let n=new Set(Object.entries(e.accepted).flatMap(([e,t])=>t.map(t=>`${e}\u0000${t}`)));for(let[e,r]of Object.entries(t))for(let[t,i]of Object.entries(r))n.has(`${e}\u0000${t}`)&&(o[e]={...o[e],[t]:i});N(i,{at:Date.now(),anonId:s,apps:o})},f=null,p=()=>{u+=1,o={},l={},P(i)},m=n=>{if(f=n,!c)return;let r=z(R(e,n),{...o,...l});if(Object.keys(r).length===0)return;let i=u;l={...l,...r},t(A(r)).then(e=>{i===u&&d(e,r)}).catch(()=>{}).finally(()=>{l={}})};return{settled:e=>{m(e)},needsAnon:()=>!c,observedAnon:e=>{(e===null||s!==null&&s!==e)&&p(),s=e,c=!0,m(f)},clear:p}},V=Symbol.for(`qinto.connectGate`),H=()=>{},U=e=>{let t=globalThis,n=(t[V]??Promise.resolve()).then(e);return t[V]=n.then(H,H),n},W=class extends e{constructor(e){super(e),this.name=`QintoProtocolError`}},G=e=>{if(typeof e!=`string`)return null;let t;try{t=JSON.parse(e)}catch{return null}if(typeof t!=`object`||!t)return null;let n=t;return n.t===`ack`||n.t===`error`||n.t===`consentState`||n.t===`session`||n.t===`captureAck`||n.t===`appPush`||n.t===`sharedContext`?t:null},K=e=>{if(e.t===`consentState`)return{categories:e.categories,apps:e.apps};if(e.t===`session`)return{anonId:e.anonId,globalId:e.globalId};if(e.t===`captureAck`)return{anonId:e.anonId,accepted:e.accepted}},ce=e=>{let n=new Map,r=e=>{let t=n.get(e);if(t!==void 0)return clearTimeout(t.timer),n.delete(e),t};return{submit:r=>new Promise((i,a)=>{let o={frame:r,sent:e.send(r),resolve:i,reject:a,timer:setTimeout(()=>{n.delete(r.ref),a(new t(`The Qinto request timed out.`))},e.requestTimeoutMs)};n.set(r.ref,o)}),flush:()=>{for(let t of n.values())t.sent||=e.send(t.frame)},deliver:e=>{let t=e.ref===void 0?void 0:r(e.ref);if(t!==void 0){if(e.t===`error`){t.reject(new W(e.message));return}t.resolve(K(e))}},rejectAll:e=>{let t=[...n.values()];n.clear();for(let n of t)clearTimeout(n.timer),n.reject(e)}}},le=e=>new WebSocket(e),ue=500,de=3e4,fe=`session not initialized`,pe=(e,t)=>{let n=Math.min(ue*2**e,de);return Math.round(n/2+n/2*t)},me=(e,t)=>e.t===`appPush`?(t.onAppPush?.(e),!0):e.t===`sharedContext`?(t.onSharedContext?.(e),!0):!1,he=e=>{let t=`${e.destination.replace(/\/+$/,``)}/open`,n=null,r=null,i=0,a=null,o=ce({send:e=>n===null||n.readyState!==1?!1:(n.send(JSON.stringify(e)),!0),requestTimeoutMs:e.requestTimeoutMs}),s=t=>{e.onError?.(t)},c=()=>{if(r!==null&&(clearTimeout(r),r=null),n!==null){let e=n;n=null,e.close()}o.rejectAll(new W(`connection closed`))},l=()=>{let u=le(t);n=u,u.onopen=()=>{i=0;let e=a;a=null,o.flush(),e?.resolve()},u.onmessage=t=>{let n=G(t.data);if(n===null){s(new W(`invalid message`));return}me(n,e)||(`anonId`in n&&typeof n.anonId==`string`&&e.onAnonId?.(n.anonId),o.deliver(n),n.t===`error`&&n.message===fe&&u.close())},u.onerror=()=>{s(new W(`connection closed`))},u.onclose=()=>{if(n!==u)return;n=null;let e=a;if(e!==null){a=null,c(),e.reject(new W(`connection closed`));return}o.rejectAll(new W(`connection closed`)),r=setTimeout(()=>{r=null,l()},pe(i,Math.random())),i+=1}};return{connect:()=>new Promise((e,t)=>{a={resolve:e,reject:t},l()}),submit:e=>o.submit(e),close:()=>{c()}}},ge=1e4,_e=()=>{let e=0;return()=>(e+=1,`q${e}`)},ve=(e,t)=>{let n=`qinto:visit:${e.replace(/\/+$/,``)}:${t}`,r,i=!1;try{let e=sessionStorage.getItem(n),t=e===null?null:JSON.parse(e);t===null||typeof t.id!=`string`||!/^vis_[0-9a-f-]{36}$/.test(t.id)||!Number.isSafeInteger(t.startedAt)||t.startedAt<0?(r={id:`vis_${crypto.randomUUID()}`,startedAt:Date.now()},sessionStorage.setItem(n,JSON.stringify(r)),i=!0):r={id:t.id,startedAt:t.startedAt}}catch{r={id:`vis_${crypto.randomUUID()}`,startedAt:Date.now()},i=!0}return()=>{let e={...r,isNew:i};return i=!1,e}},ye=(e,t,n,r)=>{let i=null,a=ve(n,r),o=()=>{let e=document.referrer;return{occurredAt:Date.now(),page:{url:window.location.href,...e===``?{}:{referrer:e}},session:a()}},c=async()=>{let n=await e.submit({t:`getSession`,ref:t()});return i=n.anonId,n};return{actionContext:o,submitIdentify:async({traits:n,id:r,context:i,appResults:a})=>{await e.submit({t:`identify`,ref:t(),traits:n,id:r,context:i,appResults:a})},submitTrack:async({event:n,eventId:r,properties:i,context:a,appResults:o})=>{await e.submit({t:`track`,ref:t(),event:n,eventId:r,properties:i,context:a,appResults:o})},submitConsent:async(n,r)=>{await e.submit({t:`consent`,ref:t(),categories:n.categories,apps:n.apps,context:r})},identify:async(n,r)=>{await e.submit({t:`identify`,ref:t(),traits:n,id:r,context:o()})},track:async(n,r,i)=>{let a=s(i?.eventId,r);return await e.submit({t:`track`,ref:t(),event:n,eventId:a,properties:r,context:o()}),{eventId:a}},alias:async n=>{await e.submit({t:`alias`,ref:t(),customId:n})},consent:async n=>{await e.submit({t:`consent`,ref:t(),categories:n.categories,apps:n.apps,context:o()})},getConsent:async()=>await e.submit({t:`getConsent`,ref:t()}),getSession:c,getAnonId:async()=>(i===null&&await c(),i),close:()=>{e.close()}}},be=async e=>{if(!c())throw new n(`You are calling init() in a server environment. Use createClient from @blotout/qinto-sdk-server there.`);let t=he({destination:e.destination,requestTimeoutMs:e.requestTimeoutMs??ge,onError:e.onError,onAppPush:e.onAppPush,onSharedContext:e.onSharedContext,onAnonId:e.onAnonId});await U(()=>t.connect());let r=_e();return{client:ye(t,r,e.destination,e.globalName??`qinto`),submitCapture:async e=>await t.submit({t:`capture`,ref:r(),apps:e})}},xe=[`then`,`ready`,`config`],q=()=>{},Se=(e,t)=>(...n)=>{let r=new Promise((r,i)=>{e.q.push([t,n,r,i])});return r.catch(q),r},J=e=>{let t=e;return t._q=1,t.q=[],new Proxy(t,{get:(e,n)=>typeof n!=`string`||xe.includes(n)||n in t?Reflect.get(t,n):(t[n]=Se(t,n),t[n])})},Ce=()=>{let e=document.currentScript?.dataset.qintoGlobal;return typeof e==`string`&&e!==``?e:`qinto`},we=e=>typeof e==`object`&&!!e&&e._q===1,Y=(e=`qinto`)=>{let t=globalThis;if(e in t){let n=t[e];if(!we(n))return null;let r=Array.isArray(n.q)?n:J(n);return r!==n&&Object.defineProperty(t,e,{value:r,writable:!0,enumerable:!0,configurable:!0}),r}let n=J({});return Object.defineProperty(t,e,{value:n,writable:!0,enumerable:!0,configurable:!0}),n},Te=(t,n)=>async(r,i)=>{let a=Reflect.get(t,r);if(typeof a!=`function`)throw new e(`[Qinto] ${n}.${r} is not a method of this tag.`);return a.apply(t,Array.from(i))},Ee=e=>()=>Promise.reject(e),X=(e,t)=>{let[n,r,i,a]=t;e(n,r).then(i,a)},De=e=>{let t=[];return t.push=(...t)=>{for(let n of t)X(e,n);return 0},t},Oe=(e,t,n=`window.qinto`)=>{let r=e.ready;if(r!==void 0)return r;let i=e,a=e=>{let t=i.q;i.q=De(e);for(let n of t)X(e,n)},o=t().then(e=>{a(Te(e,n))},e=>{throw a(Ee(e)),e});return o.catch(q),i.ready=o,o},Z=Ce(),Q=Y(Z),$=`window.${Z}`;if(Q!==null&&Q.ready===void 0){let e=null,t=ae(Q,{onConsentSettled:t=>{e?.settled(t)},onConsentMutation:()=>{e?.clear()},onPageEvent:t=>{e?.settled(t)}});se(Q),Oe(Q,async()=>{let n=Q.config?.destination;if(typeof n!=`string`||n===``)throw Error(`[Qinto] ${$}.config.destination is missing.`);let r=await be({destination:n,globalName:Z,onAppPush:t.push,onSharedContext:t.sharedContext,onAnonId:t=>{e?.observedAnon(t)}});e=B(Q,r.submitCapture,n,Z);let i=await t.connect(r.client);return e.needsAnon()&&e.observedAnon((await r.client.getSession()).anonId),i},$)}})();