@blotout/qinto-cli 0.1.4 → 0.1.6
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 +8 -2
- package/dist/index.js +1575 -1437
- package/dist/index.js.map +1 -1
- package/dist/worker-artifact/app-runtime/index.js +289 -93
- package/dist/worker-artifact/main/{9301296b51353552dafe33a97c95e9755e6108db-q.js.txt → 9dcfd236748459af038da772477100c4bb0223b7-q.js.txt} +1 -1
- package/dist/worker-artifact/main/index.js +262 -42
- package/dist/worker-artifact/version.json +6 -6
- package/package.json +1 -1
|
@@ -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
|
-
|
|
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
|
|
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
|
|
15177
|
+
return scope;
|
|
15108
15178
|
};
|
|
15109
15179
|
var setSpanAttributes = (attrs) => {
|
|
15110
|
-
const span =
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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,
|
|
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},$)}})();
|