@blotout/qinto-cli 0.1.25 → 0.1.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +2246 -2219
- package/dist/index.js.map +1 -1
- package/dist/worker-artifact/app-runtime/index.js +85 -45
- package/dist/worker-artifact/main/61afaba4cf160e30ef4b71a7ce30a398f71f13e8-q.js.txt +1 -0
- package/dist/worker-artifact/main/index.js +265 -49
- package/dist/worker-artifact/version.json +8 -8
- package/package.json +1 -1
- package/dist/worker-artifact/main/944c722e73fe144b1067da970f87716a835c697e-q.js.txt +0 -1
|
@@ -20818,6 +20818,7 @@ var APP_FLOWS = ["transform", "destination"];
|
|
|
20818
20818
|
var TRANSFORM_ACTION_TYPES = ["track", "identify"];
|
|
20819
20819
|
var EVENT_ACTION_TYPES = ["track", "identify", "consent"];
|
|
20820
20820
|
var BROWSER_RESULT_ACTION_TYPES = ["track", "identify"];
|
|
20821
|
+
var BROWSER_HOOKS = ["transform"];
|
|
20821
20822
|
var APP_LIFECYCLE_TYPES = ["installed", "configurationChanged", "enabled", "disabled", "versionChanged", "uninstalled"];
|
|
20822
20823
|
var API_CAPABILITIES = ["track", "identify", "alias", "consentWrite", "consentRead", "propertiesRead", "sessionRead"];
|
|
20823
20824
|
var DATA_CAPABILITIES = [
|
|
@@ -20828,7 +20829,8 @@ var DATA_CAPABILITIES = [
|
|
|
20828
20829
|
"eventContext",
|
|
20829
20830
|
"sharedContext",
|
|
20830
20831
|
"facts",
|
|
20831
|
-
"identity"
|
|
20832
|
+
"identity",
|
|
20833
|
+
"siteInstalls"
|
|
20832
20834
|
];
|
|
20833
20835
|
var MAX_APP_VARIABLES = 32;
|
|
20834
20836
|
var MAX_APP_SCOPE_VARIABLES = 16;
|
|
@@ -21106,7 +21108,8 @@ var baseSchema = external_exports.object({
|
|
|
21106
21108
|
browser: external_exports.object({
|
|
21107
21109
|
entry: external_exports.string().min(1).max(255).optional(),
|
|
21108
21110
|
capture: captureSchema.optional(),
|
|
21109
|
-
eventResults: external_exports.array(external_exports.enum(BROWSER_RESULT_ACTION_TYPES)).max(BROWSER_RESULT_ACTION_TYPES.length).optional()
|
|
21111
|
+
eventResults: external_exports.array(external_exports.enum(BROWSER_RESULT_ACTION_TYPES)).max(BROWSER_RESULT_ACTION_TYPES.length).optional(),
|
|
21112
|
+
hooks: external_exports.array(external_exports.enum(BROWSER_HOOKS)).max(BROWSER_HOOKS.length).optional()
|
|
21110
21113
|
}).strict().optional().meta({
|
|
21111
21114
|
description: "The app's browser surface: an optional entry for the bundle that ships inside the site's q.js, and the query params and cookies it declares for capture."
|
|
21112
21115
|
}),
|
|
@@ -21410,6 +21413,15 @@ var validateCapture = /* @__PURE__ */ __name((manifest, ctx) => {
|
|
|
21410
21413
|
}
|
|
21411
21414
|
}
|
|
21412
21415
|
}, "validateCapture");
|
|
21416
|
+
var validateBrowserHooks = /* @__PURE__ */ __name((manifest, ctx) => {
|
|
21417
|
+
const hooks = manifest.browser?.hooks ?? [];
|
|
21418
|
+
if (hooks.length > 0 && manifest.browser?.entry === void 0) {
|
|
21419
|
+
ctx.addIssue({ code: "custom", path: ["browser", "hooks"], message: "hooks needs a browser entry" });
|
|
21420
|
+
}
|
|
21421
|
+
if (hooks.includes("transform") && !manifest.flows.includes("transform")) {
|
|
21422
|
+
ctx.addIssue({ code: "custom", path: ["browser", "hooks"], message: "The browser transform hook needs the transform flow" });
|
|
21423
|
+
}
|
|
21424
|
+
}, "validateBrowserHooks");
|
|
21413
21425
|
var validateRpc = /* @__PURE__ */ __name((manifest, ctx) => {
|
|
21414
21426
|
const exposes = manifest.rpc?.exposes ?? [];
|
|
21415
21427
|
uniqueOrIssue(
|
|
@@ -21470,6 +21482,7 @@ var appManifestSchema = baseSchema.superRefine((manifest, ctx) => {
|
|
|
21470
21482
|
validatePipelines(manifest, ctx);
|
|
21471
21483
|
validateEndpoints(manifest, ctx);
|
|
21472
21484
|
validateCapture(manifest, ctx);
|
|
21485
|
+
validateBrowserHooks(manifest, ctx);
|
|
21473
21486
|
validateRpc(manifest, ctx);
|
|
21474
21487
|
validateEvents(manifest, ctx);
|
|
21475
21488
|
validateOauth(manifest, ctx);
|
|
@@ -21502,6 +21515,7 @@ var MAX_APPS_Q_BYTES = 4 * 1024 * 1024;
|
|
|
21502
21515
|
var ENVELOPE_VERSION = 3;
|
|
21503
21516
|
var ENVELOPE_ERROR_CODES = ["invalid_arguments"];
|
|
21504
21517
|
var MAX_INSTALLS_PER_APP = 15;
|
|
21518
|
+
var MAX_APPS_PER_DOMAIN = 50;
|
|
21505
21519
|
var MAX_EMITS_PER_INSTALL = 10;
|
|
21506
21520
|
var MAX_PUSHES_PER_INSTALL = 10;
|
|
21507
21521
|
var MAX_CAPTURE_WRITES_PER_INSTALL = 10;
|
|
@@ -21517,6 +21531,10 @@ var MAX_RESPONSE_BYTES = 1024 * 1024;
|
|
|
21517
21531
|
var MAX_RPC_BYTES = 128 * 1024;
|
|
21518
21532
|
var installSchema = external_exports.object({ id: external_exports.string().min(1).max(64), ord: external_exports.number().int().min(1), label: external_exports.string().max(100) }).strict();
|
|
21519
21533
|
var emitSchema = external_exports.object({ type: external_exports.literal("track"), occurredAt: external_exports.number().int().nonnegative().safe(), payload: external_exports.record(external_exports.string(), external_exports.unknown()) }).strict();
|
|
21534
|
+
var installRuleSchema = external_exports.object({
|
|
21535
|
+
mode: external_exports.enum(["except", "only"]),
|
|
21536
|
+
installIds: external_exports.array(external_exports.string().min(1).max(64)).max(MAX_APPS_PER_DOMAIN * MAX_INSTALLS_PER_APP)
|
|
21537
|
+
}).strict();
|
|
21520
21538
|
var pushSchema = external_exports.object({ topic: external_exports.string().min(1).max(64), data: external_exports.unknown(), broadcast: external_exports.enum(PUSH_BROADCASTS).optional() }).strict().refine((push) => {
|
|
21521
21539
|
return new TextEncoder().encode(JSON.stringify(push.data ?? null)).length <= MAX_PUSH_BYTES;
|
|
21522
21540
|
}, "Push data exceeds the per-message size cap");
|
|
@@ -21603,7 +21621,11 @@ var envelopeRequestSchema = external_exports.object({
|
|
|
21603
21621
|
var envelopeResponseSchema = external_exports.object({
|
|
21604
21622
|
v: external_exports.literal(ENVELOPE_VERSION),
|
|
21605
21623
|
// Transform invocations only: pass the action through, drop it, or replace its payload (§5.4).
|
|
21606
|
-
result: external_exports.union([
|
|
21624
|
+
result: external_exports.union([
|
|
21625
|
+
external_exports.literal("pass"),
|
|
21626
|
+
external_exports.literal("drop"),
|
|
21627
|
+
external_exports.object({ payload: external_exports.record(external_exports.string(), external_exports.unknown()).optional(), installs: installRuleSchema.optional() }).strict()
|
|
21628
|
+
]).optional(),
|
|
21607
21629
|
effects: external_exports.array(effectGroupSchema).optional(),
|
|
21608
21630
|
rpc: external_exports.object({ result: external_exports.unknown() }).strict().refine((rpc) => {
|
|
21609
21631
|
return new TextEncoder().encode(JSON.stringify(rpc.result ?? null)).length <= MAX_RPC_BYTES;
|
|
@@ -21901,6 +21923,16 @@ var aggregateEndpointOutcomes = /* @__PURE__ */ __name((outcomes) => {
|
|
|
21901
21923
|
return { status: failed ? 502 : 200 };
|
|
21902
21924
|
}, "aggregateEndpointOutcomes");
|
|
21903
21925
|
|
|
21926
|
+
// ../../libs/app-envelope/src/install-rule.ts
|
|
21927
|
+
var intersectInstallRules = /* @__PURE__ */ __name((a, b) => {
|
|
21928
|
+
if (a.mode === "except" && b.mode === "except") {
|
|
21929
|
+
return { mode: "except", installIds: [.../* @__PURE__ */ new Set([...a.installIds, ...b.installIds])] };
|
|
21930
|
+
}
|
|
21931
|
+
const [only, other] = a.mode === "only" ? [a, b] : [b, a];
|
|
21932
|
+
const listed = new Set(other.installIds);
|
|
21933
|
+
return { mode: "only", installIds: only.installIds.filter((id) => listed.has(id) === (other.mode === "only")) };
|
|
21934
|
+
}, "intersectInstallRules");
|
|
21935
|
+
|
|
21904
21936
|
// ../../libs/app-envelope/src/observability.ts
|
|
21905
21937
|
var APP_OBSERVABILITY_HEADER = "x-qinto-app-observability";
|
|
21906
21938
|
var APP_OBSERVABILITY_VERSION = 1;
|
|
@@ -21970,6 +22002,12 @@ var parseAppObservability = /* @__PURE__ */ __name((header) => {
|
|
|
21970
22002
|
var UI_CONTEXT_HEADER = "x-qinto-ui-context";
|
|
21971
22003
|
var MAX_UI_BODY_BYTES = 1024 * 1024;
|
|
21972
22004
|
var uiInstallSchema = external_exports.object({ id: external_exports.string().min(1).max(64), ord: external_exports.number().int().min(1), label: external_exports.string().max(100), enabled: external_exports.boolean() }).strict();
|
|
22005
|
+
var siteInstallSchema = external_exports.object({
|
|
22006
|
+
id: external_exports.string().min(1).max(64),
|
|
22007
|
+
appSlug: external_exports.string().min(1).max(30),
|
|
22008
|
+
appName: external_exports.string().min(1).max(100),
|
|
22009
|
+
label: external_exports.string().max(100)
|
|
22010
|
+
}).strict();
|
|
21973
22011
|
var uiContextSchema = external_exports.object({
|
|
21974
22012
|
version: external_exports.string().min(1).max(32),
|
|
21975
22013
|
viewer: external_exports.object({
|
|
@@ -21980,8 +22018,35 @@ var uiContextSchema = external_exports.object({
|
|
|
21980
22018
|
}).strict(),
|
|
21981
22019
|
install: uiInstallSchema,
|
|
21982
22020
|
installs: external_exports.array(uiInstallSchema).min(1).max(MAX_INSTALLS_PER_APP),
|
|
22021
|
+
siteInstalls: external_exports.array(siteInstallSchema).max(MAX_APPS_PER_DOMAIN * MAX_INSTALLS_PER_APP).optional(),
|
|
21983
22022
|
writeTicket: external_exports.string().min(1).max(2048).optional()
|
|
21984
22023
|
}).strict();
|
|
22024
|
+
var MAX_UI_SITE_INSTALLS_BYTES = 64 * 1024;
|
|
22025
|
+
var asciiJson = /* @__PURE__ */ __name((value) => {
|
|
22026
|
+
return JSON.stringify(value).replace(/[\u007f-\uffff]/g, (char) => `\\u${char.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
22027
|
+
}, "asciiJson");
|
|
22028
|
+
var encodeUiContext = /* @__PURE__ */ __name((context) => {
|
|
22029
|
+
return asciiJson(context);
|
|
22030
|
+
}, "encodeUiContext");
|
|
22031
|
+
var bySlugThenOrd = /* @__PURE__ */ __name((a, b) => {
|
|
22032
|
+
if (a.appSlug !== b.appSlug) {
|
|
22033
|
+
return a.appSlug < b.appSlug ? -1 : 1;
|
|
22034
|
+
}
|
|
22035
|
+
return a.ord - b.ord;
|
|
22036
|
+
}, "bySlugThenOrd");
|
|
22037
|
+
var boundSiteInstalls = /* @__PURE__ */ __name((installs) => {
|
|
22038
|
+
const listed = [];
|
|
22039
|
+
let bytes = 2;
|
|
22040
|
+
for (const { id, appSlug, appName, label } of [...installs].sort(bySlugThenOrd)) {
|
|
22041
|
+
const install = { id, appSlug, appName, label };
|
|
22042
|
+
bytes += asciiJson(install).length + 1;
|
|
22043
|
+
if (bytes > MAX_UI_SITE_INSTALLS_BYTES) {
|
|
22044
|
+
break;
|
|
22045
|
+
}
|
|
22046
|
+
listed.push(install);
|
|
22047
|
+
}
|
|
22048
|
+
return listed;
|
|
22049
|
+
}, "boundSiteInstalls");
|
|
21985
22050
|
|
|
21986
22051
|
// ../../node_modules/.pnpm/hono@4.13.5/node_modules/hono/dist/compose.js
|
|
21987
22052
|
var compose = /* @__PURE__ */ __name((middleware, onError, onNotFound) => {
|
|
@@ -26322,6 +26387,10 @@ var phoneIdentifier = /* @__PURE__ */ __name((raw2) => {
|
|
|
26322
26387
|
const identifier = { type: "phone", value: normalizePhone(raw2) };
|
|
26323
26388
|
return isValidIdentifier(identifier) ? identifier : void 0;
|
|
26324
26389
|
}, "phoneIdentifier");
|
|
26390
|
+
var customIdentifier = /* @__PURE__ */ __name((raw2) => {
|
|
26391
|
+
const identifier = { type: "custom", value: raw2.trim() };
|
|
26392
|
+
return isValidIdentifier(identifier) ? identifier : void 0;
|
|
26393
|
+
}, "customIdentifier");
|
|
26325
26394
|
var classifyIdentifier = /* @__PURE__ */ __name((raw2) => {
|
|
26326
26395
|
const identifier = emailIdentifier(raw2) ?? (raw2.trim().startsWith("+") ? phoneIdentifier(raw2) : void 0);
|
|
26327
26396
|
return identifier ?? { type: "custom", value: raw2.trim() };
|
|
@@ -27375,22 +27444,80 @@ var handleCurrencyRates = /* @__PURE__ */ __name(async (ctx) => {
|
|
|
27375
27444
|
return Response.json(current);
|
|
27376
27445
|
}, "handleCurrencyRates");
|
|
27377
27446
|
|
|
27378
|
-
// src/
|
|
27379
|
-
var
|
|
27380
|
-
|
|
27381
|
-
|
|
27382
|
-
|
|
27383
|
-
|
|
27384
|
-
|
|
27385
|
-
|
|
27386
|
-
|
|
27447
|
+
// src/id-graph/upsert.ts
|
|
27448
|
+
var identifierValue = external_exports.string().min(1).max(256).optional();
|
|
27449
|
+
var traitValue = external_exports.string().max(256).optional();
|
|
27450
|
+
var personUpsertSchema = external_exports.object({
|
|
27451
|
+
email: identifierValue,
|
|
27452
|
+
phone: identifierValue,
|
|
27453
|
+
customId: identifierValue,
|
|
27454
|
+
firstName: traitValue,
|
|
27455
|
+
lastName: traitValue,
|
|
27456
|
+
city: traitValue,
|
|
27457
|
+
state: traitValue,
|
|
27458
|
+
zip: traitValue,
|
|
27459
|
+
country: traitValue,
|
|
27460
|
+
observedAt: external_exports.number().int().nonnegative().safe().optional()
|
|
27461
|
+
}).strict().refine((person2) => person2.email !== void 0 || person2.phone !== void 0 || person2.customId !== void 0);
|
|
27462
|
+
var IDENTIFIER_PARSERS = { email: emailIdentifier, phone: phoneIdentifier, customId: customIdentifier };
|
|
27463
|
+
var TRAIT_KEYS = {
|
|
27464
|
+
email: "email",
|
|
27465
|
+
phone: "phone",
|
|
27466
|
+
firstName: "first_name",
|
|
27467
|
+
lastName: "last_name",
|
|
27468
|
+
city: "city",
|
|
27469
|
+
state: "state",
|
|
27470
|
+
zip: "postal_code",
|
|
27471
|
+
country: "country"
|
|
27472
|
+
};
|
|
27473
|
+
var upsertIdentifiers = /* @__PURE__ */ __name((upsert) => {
|
|
27474
|
+
return Object.entries(IDENTIFIER_PARSERS).flatMap(([field, parse4]) => {
|
|
27475
|
+
const raw2 = upsert[field];
|
|
27476
|
+
if (raw2 === void 0) {
|
|
27477
|
+
return [];
|
|
27478
|
+
}
|
|
27479
|
+
const identifier = parse4(raw2);
|
|
27480
|
+
if (identifier === void 0) {
|
|
27481
|
+
addSpanEvent("invalid identifier ignored", { level: "warn", attrs: { field } });
|
|
27482
|
+
return [];
|
|
27483
|
+
}
|
|
27484
|
+
return [identifier];
|
|
27485
|
+
});
|
|
27486
|
+
}, "upsertIdentifiers");
|
|
27487
|
+
var upsertTraits = /* @__PURE__ */ __name((upsert) => {
|
|
27488
|
+
const traits = {};
|
|
27489
|
+
for (const [field, key] of Object.entries(TRAIT_KEYS)) {
|
|
27490
|
+
const value = upsert[field];
|
|
27491
|
+
if (value !== void 0) {
|
|
27492
|
+
traits[key] = value;
|
|
27493
|
+
}
|
|
27387
27494
|
}
|
|
27388
|
-
|
|
27389
|
-
|
|
27390
|
-
|
|
27495
|
+
return traits;
|
|
27496
|
+
}, "upsertTraits");
|
|
27497
|
+
var upsertPerson = /* @__PURE__ */ __name(async (env, upsert) => {
|
|
27498
|
+
const [routing, ...others] = upsertIdentifiers(upsert);
|
|
27499
|
+
if (routing === void 0) {
|
|
27500
|
+
return null;
|
|
27391
27501
|
}
|
|
27392
|
-
|
|
27393
|
-
},
|
|
27502
|
+
const candidate = generateUserId();
|
|
27503
|
+
const { anonId } = await claimAnon(env, routing, candidate);
|
|
27504
|
+
const receivedAt = Date.now();
|
|
27505
|
+
const observedAt = upsert.observedAt ?? receivedAt;
|
|
27506
|
+
const action = buildAction({
|
|
27507
|
+
type: "identify",
|
|
27508
|
+
userId: anonId,
|
|
27509
|
+
source: "app",
|
|
27510
|
+
identifier: routing,
|
|
27511
|
+
identifiers: others,
|
|
27512
|
+
graphOnly: true,
|
|
27513
|
+
observedAt,
|
|
27514
|
+
receivedAt,
|
|
27515
|
+
payload: { traits: upsertTraits(upsert) },
|
|
27516
|
+
context: { source: "app", occurredAt: observedAt, offline: false, replay: false }
|
|
27517
|
+
});
|
|
27518
|
+
await ingest(env, action);
|
|
27519
|
+
return { anonId, created: anonId === candidate };
|
|
27520
|
+
}, "upsertPerson");
|
|
27394
27521
|
|
|
27395
27522
|
// src/apps/rate-limit.ts
|
|
27396
27523
|
var limiterAbsenceLogged = false;
|
|
@@ -27407,6 +27534,49 @@ var rateLimited = /* @__PURE__ */ __name(async (env, key, binding = "BROKER_RATE
|
|
|
27407
27534
|
return !outcome.success;
|
|
27408
27535
|
}, "rateLimited");
|
|
27409
27536
|
|
|
27537
|
+
// src/apps/broker-identity.ts
|
|
27538
|
+
var identityUpsertBody = external_exports.object({
|
|
27539
|
+
...identityFields,
|
|
27540
|
+
kind: external_exports.literal("identityUpsert"),
|
|
27541
|
+
person: personUpsertSchema
|
|
27542
|
+
}).strict();
|
|
27543
|
+
var identityGranted = /* @__PURE__ */ __name((slug) => {
|
|
27544
|
+
return siteRegistry().registry.apps[slug]?.data.includes(IDENTITY_CAPABILITY) === true;
|
|
27545
|
+
}, "identityGranted");
|
|
27546
|
+
var identityPermissionDenied = /* @__PURE__ */ __name(() => {
|
|
27547
|
+
return Response.json({ error: "identity_permission_denied" }, { status: 403 });
|
|
27548
|
+
}, "identityPermissionDenied");
|
|
27549
|
+
var handleIdentityUpsert = /* @__PURE__ */ __name(async (env, body) => {
|
|
27550
|
+
if (!identityGranted(body.slug)) {
|
|
27551
|
+
return identityPermissionDenied();
|
|
27552
|
+
}
|
|
27553
|
+
if (await rateLimited(env, `${body.slug}::identity-upsert`)) {
|
|
27554
|
+
return Response.json({ error: "rate_limited" }, { status: 429 });
|
|
27555
|
+
}
|
|
27556
|
+
const upserted = await upsertPerson(env, body.person);
|
|
27557
|
+
if (upserted === null) {
|
|
27558
|
+
return Response.json({ error: "invalid_identifier" }, { status: 400 });
|
|
27559
|
+
}
|
|
27560
|
+
return Response.json(upserted);
|
|
27561
|
+
}, "handleIdentityUpsert");
|
|
27562
|
+
|
|
27563
|
+
// src/apps/emit-payload.ts
|
|
27564
|
+
var emitPayloadSchema = external_exports.object({
|
|
27565
|
+
event: external_exports.string().min(1),
|
|
27566
|
+
eventId: external_exports.string().min(1).optional(),
|
|
27567
|
+
properties: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
|
|
27568
|
+
}).strict();
|
|
27569
|
+
var emittedEventId = /* @__PURE__ */ __name((eventId, properties2) => {
|
|
27570
|
+
if (eventId !== void 0) {
|
|
27571
|
+
return eventId;
|
|
27572
|
+
}
|
|
27573
|
+
const orderId = properties2?.["orderId"];
|
|
27574
|
+
if (typeof orderId === "string" && orderId !== "") {
|
|
27575
|
+
return orderId;
|
|
27576
|
+
}
|
|
27577
|
+
return `evt_${ulid3()}`;
|
|
27578
|
+
}, "emittedEventId");
|
|
27579
|
+
|
|
27410
27580
|
// src/apps/broker-track.ts
|
|
27411
27581
|
var trackBody = external_exports.object({
|
|
27412
27582
|
...identityFields,
|
|
@@ -27414,6 +27584,7 @@ var trackBody = external_exports.object({
|
|
|
27414
27584
|
source: external_exports.enum(APP_HANDLER_SOURCES),
|
|
27415
27585
|
userId: external_exports.string().min(1).max(64),
|
|
27416
27586
|
installId: external_exports.string().min(1).max(64),
|
|
27587
|
+
installs: installRuleSchema.optional(),
|
|
27417
27588
|
event: emitSchema
|
|
27418
27589
|
}).strict();
|
|
27419
27590
|
var handleTrack = /* @__PURE__ */ __name(async (env, body) => {
|
|
@@ -27444,7 +27615,8 @@ var handleTrack = /* @__PURE__ */ __name(async (env, body) => {
|
|
|
27444
27615
|
userId: body.userId,
|
|
27445
27616
|
source: body.source,
|
|
27446
27617
|
payload: { event, eventId: emittedEventId(eventId, properties2), properties: properties2 ?? {} },
|
|
27447
|
-
context: { source: "app", occurredAt: body.event.occurredAt, offline: false, replay: false }
|
|
27618
|
+
context: { source: "app", occurredAt: body.event.occurredAt, offline: false, replay: false },
|
|
27619
|
+
...body.installs === void 0 ? {} : { installs: body.installs }
|
|
27448
27620
|
});
|
|
27449
27621
|
await ingest(env, action, site);
|
|
27450
27622
|
return Response.json({ accepted: true }, { status: 202 });
|
|
@@ -28653,6 +28825,7 @@ var brokerBody = external_exports.discriminatedUnion("kind", [
|
|
|
28653
28825
|
contextWriteBody,
|
|
28654
28826
|
factsReadBody,
|
|
28655
28827
|
identityReadBody,
|
|
28828
|
+
identityUpsertBody,
|
|
28656
28829
|
currencyRatesBody,
|
|
28657
28830
|
trackBody
|
|
28658
28831
|
]);
|
|
@@ -28721,9 +28894,6 @@ var forbidden = /* @__PURE__ */ __name(() => {
|
|
|
28721
28894
|
var factsPermissionDenied = /* @__PURE__ */ __name(() => {
|
|
28722
28895
|
return Response.json({ error: "facts_permission_denied" }, { status: 403 });
|
|
28723
28896
|
}, "factsPermissionDenied");
|
|
28724
|
-
var identityPermissionDenied = /* @__PURE__ */ __name(() => {
|
|
28725
|
-
return Response.json({ error: "identity_permission_denied" }, { status: 403 });
|
|
28726
|
-
}, "identityPermissionDenied");
|
|
28727
28897
|
var handlePush = /* @__PURE__ */ __name(async (env, body) => {
|
|
28728
28898
|
if (new TextEncoder().encode(JSON.stringify(body.data ?? null)).length > MAX_PUSH_BYTES) {
|
|
28729
28899
|
return invalidBody();
|
|
@@ -28963,8 +29133,7 @@ var handleFactsRead = /* @__PURE__ */ __name(async (env, body) => {
|
|
|
28963
29133
|
return Response.json({ items: page.items, nextCursor });
|
|
28964
29134
|
}, "handleFactsRead");
|
|
28965
29135
|
var handleIdentityRead = /* @__PURE__ */ __name(async (env, body) => {
|
|
28966
|
-
|
|
28967
|
-
if (app2 === void 0 || !app2.data.includes(IDENTITY_CAPABILITY)) {
|
|
29136
|
+
if (!identityGranted(body.slug)) {
|
|
28968
29137
|
return identityPermissionDenied();
|
|
28969
29138
|
}
|
|
28970
29139
|
if (await rateLimited(env, `${body.slug}::identity`)) {
|
|
@@ -29041,6 +29210,9 @@ var handleBroker = /* @__PURE__ */ __name(async (request, env, ctx) => {
|
|
|
29041
29210
|
case "identityRead": {
|
|
29042
29211
|
return handleIdentityRead(env, body);
|
|
29043
29212
|
}
|
|
29213
|
+
case "identityUpsert": {
|
|
29214
|
+
return handleIdentityUpsert(env, body);
|
|
29215
|
+
}
|
|
29044
29216
|
case "currencyRates": {
|
|
29045
29217
|
return handleCurrencyRates(ctx);
|
|
29046
29218
|
}
|
|
@@ -30171,6 +30343,19 @@ var preflight = /* @__PURE__ */ __name((origin) => {
|
|
|
30171
30343
|
var uiInstall = /* @__PURE__ */ __name((install) => {
|
|
30172
30344
|
return { id: install.id, ord: install.ord, label: install.label, enabled: install.enabled ?? install.active };
|
|
30173
30345
|
}, "uiInstall");
|
|
30346
|
+
var otherAppInstalls = /* @__PURE__ */ __name((ownSlug) => {
|
|
30347
|
+
const installs = Object.entries(siteRegistry().registry.apps).flatMap(([appSlug, entry]) => {
|
|
30348
|
+
if (appSlug === ownSlug) {
|
|
30349
|
+
return [];
|
|
30350
|
+
}
|
|
30351
|
+
return entry.installs.filter((install) => install.enabled ?? install.active).map((install) => ({ id: install.id, appSlug, appName: entry.name, label: install.label, ord: install.ord }));
|
|
30352
|
+
});
|
|
30353
|
+
const listed = boundSiteInstalls(installs);
|
|
30354
|
+
if (listed.length < installs.length) {
|
|
30355
|
+
addSpanEvent("app ui site installs trimmed", { level: "warn", attrs: { app: ownSlug, listed: listed.length, total: installs.length } });
|
|
30356
|
+
}
|
|
30357
|
+
return listed;
|
|
30358
|
+
}, "otherAppInstalls");
|
|
30174
30359
|
var runForwardToApp2 = /* @__PURE__ */ __name(async (c, input2) => {
|
|
30175
30360
|
const { slug, origin, app: app2, install, viewer, writeTicket, body } = input2;
|
|
30176
30361
|
const context = invocationContext(c.env);
|
|
@@ -30194,9 +30379,10 @@ var runForwardToApp2 = /* @__PURE__ */ __name(async (c, input2) => {
|
|
|
30194
30379
|
viewer,
|
|
30195
30380
|
install: uiInstall(install),
|
|
30196
30381
|
installs: app2.installs.map(uiInstall),
|
|
30382
|
+
...app2.data.includes("siteInstalls") ? { siteInstalls: otherAppInstalls(slug) } : {},
|
|
30197
30383
|
...writeTicket === void 0 ? {} : { writeTicket }
|
|
30198
30384
|
};
|
|
30199
|
-
headers.set(UI_CONTEXT_HEADER,
|
|
30385
|
+
headers.set(UI_CONTEXT_HEADER, encodeUiContext(uiContext));
|
|
30200
30386
|
const trace = currentTraceContext();
|
|
30201
30387
|
if (trace !== void 0) {
|
|
30202
30388
|
headers.set(TRACE_HEADER, serializeTraceContext(trace));
|
|
@@ -31038,14 +31224,14 @@ var open2 = /* @__PURE__ */ __name(async (c) => {
|
|
|
31038
31224
|
|
|
31039
31225
|
// browser-artifact/meta.json
|
|
31040
31226
|
var meta_default = {
|
|
31041
|
-
version: "0.1.
|
|
31042
|
-
gitSha: "
|
|
31043
|
-
builtAt: "2026-
|
|
31044
|
-
hash: "
|
|
31227
|
+
version: "0.1.13",
|
|
31228
|
+
gitSha: "21a2eff7e038bc98c8c087656c9793c8f3a82434",
|
|
31229
|
+
builtAt: "2026-10-01T08:32:14.748Z",
|
|
31230
|
+
hash: "3d5d7fe29d7e160413d4cb0e08de724613f89eb314feed6d5d1ec2e7473f7afd"
|
|
31045
31231
|
};
|
|
31046
31232
|
|
|
31047
31233
|
// src/paths/q-js.ts
|
|
31048
|
-
import qScript from "./
|
|
31234
|
+
import qScript from "./61afaba4cf160e30ef4b71a7ce30a398f71f13e8-q.js.txt";
|
|
31049
31235
|
|
|
31050
31236
|
// src/apps-q.ts
|
|
31051
31237
|
import appsQScript from "./apps-q.json";
|
|
@@ -31532,7 +31718,11 @@ var trackBody2 = external_exports.object({
|
|
|
31532
31718
|
description: "Free-form properties describing the event; omit it when the event name alone says everything.",
|
|
31533
31719
|
example: { orderId: "9241", value: 129.9, currency: "USD" }
|
|
31534
31720
|
}),
|
|
31535
|
-
context: serverActionContextField
|
|
31721
|
+
context: serverActionContextField,
|
|
31722
|
+
installs: installRuleSchema.optional().openapi({
|
|
31723
|
+
description: "Which of the site\u2019s app installs receive the event: `except` delivers to every install but the listed ids, `only` to the listed ids alone. An unknown id is ignored, and an empty `only` list reaches no install while the event is still stored when it qualifies. Omit it to deliver to every install.",
|
|
31724
|
+
example: { mode: "except", installIds: ["3f2a9c1e-8b4d-4e2a-9f6c-1d7b5e0a4c83"] }
|
|
31725
|
+
})
|
|
31536
31726
|
}).strict().openapi("TrackRequest", { description: "An event the user performed, with optional free-form properties." });
|
|
31537
31727
|
var definition10 = createRoute({
|
|
31538
31728
|
method: "post",
|
|
@@ -31562,12 +31752,18 @@ var definition10 = createRoute({
|
|
|
31562
31752
|
}
|
|
31563
31753
|
});
|
|
31564
31754
|
var track = createApiRouter().openapi(definition10, async (c) => {
|
|
31565
|
-
const { identifier, event, eventId, properties: properties2, context } = c.req.valid("json");
|
|
31755
|
+
const { identifier, event, eventId, properties: properties2, context, installs } = c.req.valid("json");
|
|
31566
31756
|
try {
|
|
31567
31757
|
const id = await submitAction(
|
|
31568
31758
|
c.env,
|
|
31569
31759
|
identifier,
|
|
31570
|
-
{
|
|
31760
|
+
{
|
|
31761
|
+
type: "track",
|
|
31762
|
+
source: c.get("actionSource"),
|
|
31763
|
+
payload: { event, eventId, properties: properties2 ?? {} },
|
|
31764
|
+
context,
|
|
31765
|
+
...installs === void 0 ? {} : { installs }
|
|
31766
|
+
},
|
|
31571
31767
|
c.req.raw
|
|
31572
31768
|
);
|
|
31573
31769
|
return respond(c, acceptedResponse, { ok: true, id });
|
|
@@ -31606,6 +31802,9 @@ var gatePasses = /* @__PURE__ */ __name((entry, trigger, consent2, enforced) =>
|
|
|
31606
31802
|
}, "gatePasses");
|
|
31607
31803
|
var eligibleApps = /* @__PURE__ */ __name((registry3, trigger, consent2, enforced) => {
|
|
31608
31804
|
const location = visitorLocation(trigger);
|
|
31805
|
+
const rule = trigger.kind === "event" && trigger.action.type === "track" ? trigger.action.installs : void 0;
|
|
31806
|
+
const listed = new Set(rule?.installIds);
|
|
31807
|
+
const keepListed = rule?.mode === "only";
|
|
31609
31808
|
const targets = [];
|
|
31610
31809
|
const geoSkipped = [];
|
|
31611
31810
|
for (const slug of registry3.order) {
|
|
@@ -31615,7 +31814,7 @@ var eligibleApps = /* @__PURE__ */ __name((registry3, trigger, consent2, enforce
|
|
|
31615
31814
|
}
|
|
31616
31815
|
const { runs, skipped } = geoRunnableInstalls(slug, app2, location);
|
|
31617
31816
|
geoSkipped.push(...skipped);
|
|
31618
|
-
const installs = runs.map(({ id, ord, label }) => ({ id, ord, label }));
|
|
31817
|
+
const installs = runs.filter((install) => listed.has(install.id) === keepListed).map(({ id, ord, label }) => ({ id, ord, label }));
|
|
31619
31818
|
if (installs.length === 0) {
|
|
31620
31819
|
continue;
|
|
31621
31820
|
}
|
|
@@ -31625,7 +31824,6 @@ var eligibleApps = /* @__PURE__ */ __name((registry3, trigger, consent2, enforce
|
|
|
31625
31824
|
}, "eligibleApps");
|
|
31626
31825
|
|
|
31627
31826
|
// src/apps/fan-out.ts
|
|
31628
|
-
var MAX_APPS_PER_DOMAIN = 50;
|
|
31629
31827
|
var fanOutToApps = /* @__PURE__ */ __name(async (env, { trigger, snapshot, trace }, { deliverEffects }) => {
|
|
31630
31828
|
const { registry: registry3, error: error61 } = siteRegistry();
|
|
31631
31829
|
const { targets, geoSkipped } = eligibleApps(registry3, trigger, snapshot.consent, consentEnforced(env));
|
|
@@ -31750,6 +31948,16 @@ var withReplacedPayload = /* @__PURE__ */ __name((entry, action, payload) => {
|
|
|
31750
31948
|
}
|
|
31751
31949
|
return { ...action, payload: parsed.data };
|
|
31752
31950
|
}, "withReplacedPayload");
|
|
31951
|
+
var applyTransformResult = /* @__PURE__ */ __name((entry, action, { payload, installs }) => {
|
|
31952
|
+
const replaced = payload === void 0 ? action : withReplacedPayload(entry, action, payload);
|
|
31953
|
+
if (installs === void 0) {
|
|
31954
|
+
return replaced;
|
|
31955
|
+
}
|
|
31956
|
+
if (replaced.type !== "track") {
|
|
31957
|
+
throw entryFailure(entry, "an identify transform cannot set an install rule");
|
|
31958
|
+
}
|
|
31959
|
+
return { ...replaced, installs: replaced.installs === void 0 ? installs : intersectInstallRules(replaced.installs, installs) };
|
|
31960
|
+
}, "applyTransformResult");
|
|
31753
31961
|
var postTransform = /* @__PURE__ */ __name(async (env, entry, post, dataPoints) => {
|
|
31754
31962
|
let response;
|
|
31755
31963
|
try {
|
|
@@ -31896,7 +32104,7 @@ var runTransformChain = /* @__PURE__ */ __name(async (env, { action, snapshot, d
|
|
|
31896
32104
|
return { dropped: true, effects: collected };
|
|
31897
32105
|
}
|
|
31898
32106
|
if (verdict.result !== "pass") {
|
|
31899
|
-
current =
|
|
32107
|
+
current = applyTransformResult(entry, current, verdict.result);
|
|
31900
32108
|
}
|
|
31901
32109
|
}
|
|
31902
32110
|
return { dropped: false, action: current, effects: collected };
|
|
@@ -33145,7 +33353,8 @@ var trackFrame = external_exports.object({
|
|
|
33145
33353
|
eventId: external_exports.string().min(1),
|
|
33146
33354
|
properties: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
|
|
33147
33355
|
context: browserContextFrame,
|
|
33148
|
-
appResults: browserResults.optional()
|
|
33356
|
+
appResults: browserResults.optional(),
|
|
33357
|
+
installs: installRuleSchema.optional()
|
|
33149
33358
|
}).strict();
|
|
33150
33359
|
var aliasFrame = external_exports.object({
|
|
33151
33360
|
t: external_exports.literal("alias"),
|
|
@@ -33270,7 +33479,8 @@ var actionFromFrame = /* @__PURE__ */ __name((frame, userId, connection = {}) =>
|
|
|
33270
33479
|
context: browserActionContext(frame.context, host, session2),
|
|
33271
33480
|
browserResults: frame.appResults,
|
|
33272
33481
|
geo,
|
|
33273
|
-
host
|
|
33482
|
+
host,
|
|
33483
|
+
...frame.installs === void 0 ? {} : { installs: frame.installs }
|
|
33274
33484
|
});
|
|
33275
33485
|
}
|
|
33276
33486
|
case "alias": {
|
|
@@ -33843,16 +34053,9 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33843
34053
|
);
|
|
33844
34054
|
seedMeta(this.kv, current.userId, current.receivedAt);
|
|
33845
34055
|
touchActivity(this.kv, current.receivedAt);
|
|
33846
|
-
|
|
33847
|
-
if (
|
|
33848
|
-
|
|
33849
|
-
this.sql,
|
|
33850
|
-
{ ...current.identifier, source: current.source, firstSeenAt: current.receivedAt },
|
|
33851
|
-
this.identifiersMirror
|
|
33852
|
-
);
|
|
33853
|
-
if (novelIdentifier && linkedGlobalId(this.kv) !== null) {
|
|
33854
|
-
recordSyncDebt(this.kv, "link");
|
|
33855
|
-
}
|
|
34056
|
+
const novelIdentifier = this.recordIdentifiers(current);
|
|
34057
|
+
if (novelIdentifier && linkedGlobalId(this.kv) !== null) {
|
|
34058
|
+
recordSyncDebt(this.kv, "link");
|
|
33856
34059
|
}
|
|
33857
34060
|
try {
|
|
33858
34061
|
if (current.type === "alias") {
|
|
@@ -33866,7 +34069,9 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33866
34069
|
} else {
|
|
33867
34070
|
await this.ingestConsent(current);
|
|
33868
34071
|
}
|
|
33869
|
-
|
|
34072
|
+
if (current.type !== "identify" || current.graphOnly !== true) {
|
|
34073
|
+
this.appFanOut({ kind: "event", action: current }, current.userId, context);
|
|
34074
|
+
}
|
|
33870
34075
|
this.releaseTransformEmissions(releaseEmissions);
|
|
33871
34076
|
} finally {
|
|
33872
34077
|
if (novelIdentifier) {
|
|
@@ -33874,6 +34079,17 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33874
34079
|
}
|
|
33875
34080
|
}
|
|
33876
34081
|
}
|
|
34082
|
+
recordIdentifiers(action) {
|
|
34083
|
+
const identifiers = [
|
|
34084
|
+
...action.identifier ? [action.identifier] : [],
|
|
34085
|
+
...action.type === "identify" ? action.identifiers ?? [] : []
|
|
34086
|
+
];
|
|
34087
|
+
let novel = false;
|
|
34088
|
+
for (const identifier of identifiers) {
|
|
34089
|
+
novel = upsertIdentifier(this.sql, { ...identifier, source: action.source, firstSeenAt: action.receivedAt }, this.identifiersMirror) || novel;
|
|
34090
|
+
}
|
|
34091
|
+
return novel;
|
|
34092
|
+
}
|
|
33877
34093
|
// null means a transform dropped the action: the turn succeeds with nothing persisted (§5.4).
|
|
33878
34094
|
// The chain runs on the originating site's worker, against its apps and key master; only the
|
|
33879
34095
|
// collected effects come back, and they are released here, past the turn outcome, so a delivery
|
|
@@ -34061,7 +34277,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34061
34277
|
}
|
|
34062
34278
|
async ingestIdentify(action) {
|
|
34063
34279
|
const classified = await traced("pii.classify", {}, () => {
|
|
34064
|
-
const result = classifyTraits(action.payload.traits, action.receivedAt);
|
|
34280
|
+
const result = classifyTraits(action.payload.traits, action.observedAt ?? action.receivedAt);
|
|
34065
34281
|
for (const key of result.dropped) {
|
|
34066
34282
|
addSpanEvent("invalid pii trait ignored", { level: "warn", attrs: { "action.id": action.actionId, key } });
|
|
34067
34283
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"main": {
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"gitSha": "
|
|
5
|
-
"builtAt": "2026-
|
|
3
|
+
"version": "0.1.9",
|
|
4
|
+
"gitSha": "ffbdbe7148d4aba3072a6c3c857e69f0557b0ec5",
|
|
5
|
+
"builtAt": "2026-10-01T08:37:00.893Z",
|
|
6
6
|
"compatibilityDate": "2026-06-23",
|
|
7
7
|
"compatibilityFlags": [
|
|
8
8
|
"nodejs_compat"
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
"mainModule": "index.js",
|
|
11
11
|
"modules": [
|
|
12
12
|
{
|
|
13
|
-
"name": "
|
|
14
|
-
"path": "main/
|
|
13
|
+
"name": "61afaba4cf160e30ef4b71a7ce30a398f71f13e8-q.js.txt",
|
|
14
|
+
"path": "main/61afaba4cf160e30ef4b71a7ce30a398f71f13e8-q.js.txt",
|
|
15
15
|
"contentType": "text/plain"
|
|
16
16
|
},
|
|
17
17
|
{
|
|
@@ -39,9 +39,9 @@
|
|
|
39
39
|
]
|
|
40
40
|
},
|
|
41
41
|
"app-runtime": {
|
|
42
|
-
"version": "0.1.
|
|
43
|
-
"gitSha": "
|
|
44
|
-
"builtAt": "2026-
|
|
42
|
+
"version": "0.1.7",
|
|
43
|
+
"gitSha": "403986012db0fea5975162b971aca874682f3184",
|
|
44
|
+
"builtAt": "2026-10-01T08:37:02.873Z",
|
|
45
45
|
"compatibilityDate": "2026-06-23",
|
|
46
46
|
"compatibilityFlags": [
|
|
47
47
|
"nodejs_compat",
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
(function(){var e=()=>{},t=t=>{t.config?.autoCapture?.pageLoad===!0&&t.q.push([`track`,[`PageView`],e,e])},n=class extends Error{constructor(e){super(e),this.name=`QintoError`}},r=class extends n{constructor(e){super(e),this.name=`QintoTimeoutError`}},i=(e=Date.now())=>`evt_${globalThis.crypto.randomUUID()}_${e}`,a=(e,t)=>{if(e!==void 0)return e;let n=t?.orderId;return typeof n==`string`&&n!==``?n:i()},o=[`necessary`,`functional`,`analytics`,`marketing`,`salesOfData`],s=[`then`,`ready`,`config`],c=()=>{},l=(e,t)=>(...n)=>{let r=new Promise((r,i)=>{e.q.push([t,n,r,i])});return r.catch(c),r},u=e=>{let t=e;return t._q=1,t.q=[],new Proxy(t,{get:(e,n)=>typeof n!=`string`||s.includes(n)||n in t?Reflect.get(t,n):(t[n]=l(t,n),t[n])})},d=()=>{let e=document.currentScript?.dataset.qintoGlobal;return typeof e==`string`&&e!==``?e:`qinto`},f=e=>typeof e==`object`&&!!e&&e._q===1,p=(e=`qinto`)=>{let t=globalThis;if(e in t){let n=t[e];if(!f(n))return null;let r=Array.isArray(n.q)?n:u(n);return r!==n&&Object.defineProperty(t,e,{value:r,writable:!0,enumerable:!0,configurable:!0}),r}let n=u({});return Object.defineProperty(t,e,{value:n,writable:!0,enumerable:!0,configurable:!0}),n},m=(e,t)=>async(r,i)=>{let a=Reflect.get(e,r);if(typeof a!=`function`)throw new n(`[Qinto] ${t}.${r} is not a method of this tag.`);return a.apply(e,Array.from(i))},h=e=>()=>Promise.reject(e),g=(e,t)=>{let[n,r,i,a]=t;e(n,r).then(i,a)},ee=e=>{let t=[];return t.push=(...t)=>{for(let n of t)g(e,n);return 0},t},_=(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=ee(e);for(let n of t)g(e,n)},o=t().then(e=>{a(m(e,n))},e=>{throw a(h(e)),e});return o.catch(c),i.ready=o,o},v=(e,t,n)=>n===void 0||!o.some(e=>e===n)?!1:n===`necessary`||e!==null&&e.categories[n]===!0&&e.apps[t]!==!1,y=()=>{},te=100,ne=1024,re=32768,b=(e,t,n)=>e.target.config?.consentConfiguration?.enabled!==!0||v(n,t,e.target.config?.apps?.[t]?.category),x=e=>Object.values(e.config?.apps??{}).some(e=>e.category!==`necessary`||e.capture!==void 0),S=(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`))},ie=(e,t,n,r)=>r===null?e.slug===t&&(n===void 0||n===e.installId):r.has(e.slug),C=(e,t)=>{console.error(`[Qinto] A handler of app "${e.slug}" (install "${e.installId}") failed.`,t)},w=(e,t,n)=>{for(let r of t)try{let t=r(n);t instanceof Promise&&t.catch(t=>{C(e,t)})}catch(t){C(e,t)}},ae=(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},getSession:async()=>{let n=await e.client.getSession();return e.target.config?.apps?.[t]?.geo===!0?n:{...n,ip:null,geo:null}},track:(t,n)=>{e.client.track(t,n).catch(y)},onEvent:e=>{i.eventHandlers.push(e)},onConsent:e=>{i.consentHandlers.push(e)},onPush:e=>{i.pushHandlers.push(e)},fetch:async(n,i)=>{let a=new URL(`${e.client.destination}/app/${t}${n}`);if(!n.startsWith(`/`)||!a.pathname.startsWith(`/app/${t}/`))throw TypeError(`[Qinto] App "${t}" fetched "${n}"; the path must be absolute and stay inside the app.`);let o=new Headers(i?.headers);return o.set(`x-qinto-install`,r.id),fetch(a,{...i,headers:o,credentials:`include`})},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&&oe(e,t)&&w(i,[n],e.sharedContext)}};try{n(a)}catch(e){console.error(`[Qinto] App "${t}" failed to activate install "${r.id}".`,e)}},T=(e,t,n)=>{if(!b(e,t,e.consent))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)ae(e,t,i,n)},oe=(e,t)=>b(e,t,e.consent)?e.sharedContextConsent===void 0||b(e,t,e.sharedContextConsent):!1,se=e=>{let{categories:t,apps:n}=e;if(typeof t==`object`&&t&&typeof n==`object`&&n)return{categories:t,apps:n}},ce=(e,t)=>{let n=e;n.consent=t,n.sharedContextConsent=void 0;for(let n of e.instances)w(n,n.consentHandlers,t);e.onConsentSettled?.(t);for(let[t,n]of e.apps)T(e,t,n)},E=async e=>{try{ce(e,await e.client.getConsent())}catch(e){console.error(`[Qinto] The apps consent read failed.`,e)}},D=e=>{let t;try{t=JSON.stringify(e)}catch{return}if(!(t===void 0||new TextEncoder().encode(t).length>ne))return JSON.parse(t)},O=e=>{let t={};for(let n of e){if(n.settled!==n.total||n.values.length!==1)continue;let e=D(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<=re&&(t[n.instance.slug]=r)}return Object.keys(t).length===0?void 0:t},k=async(e,t)=>{e.onPageEvent?.(e.consent);let n=[],r=[];for(let i of e.instances){if(!b(e,i.slug,e.consent))continue;if(e.target.config?.apps?.[i.slug]?.eventResults?.includes(t.type)!==!0){w(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,C(i,e);continue}r.push(Promise.resolve(n).then(e=>{a.settled+=1,e!==void 0&&a.values.push(e)},e=>{a.settled+=1,C(i,e)}))}}if(r.length>0){let e=y,t=new Promise(t=>{e=t}),n=setTimeout(e,te);await Promise.race([Promise.all(r),t]),clearTimeout(n)}return O(n)},A=e=>{let{client:t}=e;return{track:async(n,r,i)=>{let o=a(i?.eventId,r),s=t.actionContext(),c=await k(e,{type:`track`,event:n,eventId:o,properties:r??{}});return await t.submitTrack({event:n,eventId:o,properties:r,context:s,appResults:c}),{eventId:o}},identify:async(n,r)=>{let i=t.actionContext(),a=await k(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?.(),x(e.target)){let t=e;t.consent=null,await E(e)}},getAnonId:t.getAnonId,getConsent:t.getConsent,getSession:t.getSession,close:t.close}},j=(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=se(e),a=r??n.consent;n.sharedContextConsent=r;for(let e of n.instances)b(n,e.slug,a)&&w(e,e.sharedContextHandlers,n.sharedContext)},push:e=>{let{app:t,install:n,topic:r,scope:a,to:o}=e,s=a===`site`;if(S(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)ie(e,t,n,l)&&b(c,e.slug,c.consent)&&w(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)=>{T(o,e,t),o.consent===null&&o.target.config?.consentConfiguration?.enabled===!0&&o.target.config?.apps?.[e]?.category!==`necessary`&&E(o)};for(let[e,t]of n)T(o,e,t);return e.config?.consentConfiguration?.enabled===!0&&x(e)?await E(o):o.onConsentSettled?.(o.consent),A(o)}}},M=512,N=864e5,P=e=>Object.fromEntries(Object.entries(e).map(([e,t])=>[e,Object.fromEntries(Object.entries(t).map(([e,t])=>[e,t.value]))])),F=(e,t)=>`qinto:cap:${encodeURIComponent(e)}:${encodeURIComponent(t)}`,I=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>N?null:{at:n.at,anonId:n.anonId,apps:n.apps}}catch{return null}},L=(e,t)=>{try{window.sessionStorage.setItem(e,JSON.stringify(t))}catch{}},R=e=>{try{window.sessionStorage.removeItem(e)}catch{}},z=e=>{try{return decodeURIComponent(e)}catch{return e}},B=()=>{let e={};for(let t of document.cookie.split(`;`)){let n=t.indexOf(`=`);n>0&&(e[t.slice(0,n).trim()]=z(t.slice(n+1)))}return e},V=(e,t)=>{let n={};for(let r of e.queryParams){let e=t.params.get(r);e!==null&&e.length<=M&&(n[r]={value:e,source:`query`})}for(let r of e.cookies){let e=t.cookies[r];e!==void 0&&e.length<=M&&(n[r]={value:e,source:`cookie`})}return n},H=(e,t)=>{let n={params:new URLSearchParams(window.location.search),cookies:B()},r={};for(let[i,a]of Object.entries(e.config?.apps??{})){if(a.capture===void 0||!v(t,i,a.category))continue;let e=V(a.capture,n);Object.keys(e).length>0&&(r[i]=e)}return r},U=(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},W=(e,t,n,r)=>{let i=F(r,n),a=I(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});L(i,{at:Date.now(),anonId:s,apps:o})},f=null,p=()=>{u+=1,o={},l={},R(i)},m=n=>{if(f=n,!c)return;let r=U(H(e,n),{...o,...l});if(Object.keys(r).length===0)return;let i=u;l={...l,...r},t(P(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}},G=Symbol.for(`qinto.connectGate`),K=()=>{},le=e=>{let t=globalThis,n=(t[G]??Promise.resolve()).then(e);return t[G]=n.then(K,K),n},q=class extends n{constructor(e){super(e),this.name=`QintoProtocolError`}},ue=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},de=e=>{if(e.t===`consentState`)return{categories:e.categories,apps:e.apps};if(e.t===`session`)return{anonId:e.anonId,globalId:e.globalId,ip:e.ip,geo:e.geo};if(e.t===`captureAck`)return{anonId:e.anonId,accepted:e.accepted}},fe=e=>{let t=new Map,n=e=>{let n=t.get(e);if(n!==void 0)return clearTimeout(n.timer),t.delete(e),n};return{submit:n=>new Promise((i,a)=>{let o={frame:n,sent:e.send(n),resolve:i,reject:a,timer:setTimeout(()=>{t.delete(n.ref),a(new r(`The Qinto request timed out.`))},e.requestTimeoutMs)};t.set(n.ref,o)}),flush:()=>{for(let n of t.values())n.sent||=e.send(n.frame)},deliver:e=>{let t=e.ref===void 0?void 0:n(e.ref);if(t!==void 0){if(e.t===`error`){t.reject(new q(e.message));return}t.resolve(de(e))}},rejectAll:e=>{let n=[...t.values()];t.clear();for(let t of n)clearTimeout(t.timer),t.reject(e)}}},J=()=>{let e=document.referrer;return{url:window.location.href,...e===``?{}:{referrer:e}}},pe=e=>new WebSocket(e),me=500,he=3e4,Y=4096,ge=8192,_e=(e,t)=>{if(t.url.length>Y)return e;let n=new URLSearchParams({url:t.url});t.referrer!==void 0&&t.referrer.length<=Y&&n.set(`referrer`,t.referrer);let r=`${e}?${n}`;return r.length<=ge?r:e},ve=`session not initialized`,ye=(e,t)=>{let n=Math.min(me*2**e,he);return Math.round(n/2+n/2*t)},be=(e,t)=>e.t===`appPush`?(t.onAppPush?.(e),!0):e.t===`sharedContext`&&(t.onSharedContext?.(e),!0),xe=e=>{let t=`${e.destination.replace(/\/+$/,``)}/open`,n=null,r=null,i=0,a=null,o=fe({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 q(`connection closed`))},l=()=>{let u=pe(_e(t,J()));n=u,u.onopen=()=>{i=0;let e=a;a=null,o.flush(),e?.resolve()},u.onmessage=t=>{let n=ue(t.data);if(n===null){s(new q(`invalid message`));return}be(n,e)||(`anonId`in n&&typeof n.anonId==`string`&&e.onAnonId?.(n.anonId),o.deliver(n),n.t===`error`&&n.message===ve&&u.close())},u.onerror=()=>{s(new q(`connection closed`))},u.onclose=()=>{if(n!==u)return;n=null;let e=a;if(e!==null){a=null,c(),e.reject(new q(`connection closed`));return}o.rejectAll(new q(`connection closed`)),r=setTimeout(()=>{r=null,l()},ye(i,Math.random())),i+=1}};return{connect:()=>new Promise((e,t)=>{a={resolve:e,reject:t},l()}),submit:e=>o.submit(e),close:()=>{c()}}},Se=1e4,Ce=()=>{let e=0;return()=>(e+=1,`q${e}`)},X=(e,t,n)=>{let r=null,i=()=>({occurredAt:Date.now(),page:J()}),o=async()=>{let n=await e.submit({t:`getSession`,ref:t()});return r=n.anonId,n};return{destination:n.replace(/\/+$/,``),actionContext:i,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})},track:async(n,r,o)=>{let s=a(o?.eventId,r);return await e.submit({t:`track`,ref:t(),event:n,eventId:s,properties:r,context:i()}),{eventId:s}},alias:async n=>{await e.submit({t:`alias`,ref:t(),alias:n})},getConsent:async()=>await e.submit({t:`getConsent`,ref:t()}),getSession:o,getAnonId:async()=>(r===null&&await o(),r),close:async()=>{e.close()}}},we=async e=>{let t=xe({destination:e.destination,requestTimeoutMs:e.requestTimeoutMs??Se,onError:e.onError,onAppPush:e.onAppPush,onSharedContext:e.onSharedContext,onAnonId:e.onAnonId});await le(()=>t.connect());let n=Ce();return{client:X(t,n,e.destination),submitCapture:async e=>await t.submit({t:`capture`,ref:n(),apps:e})}},Te=(e,t)=>{let n=null,r=j(e,{onConsentSettled:e=>{n?.settled(e)},onConsentMutation:()=>{n?.clear()},onPageEvent:e=>{n?.settled(e)}});return{connect:async i=>{let a=await we({...i,onAppPush:r.push,onSharedContext:r.sharedContext,onAnonId:e=>{n?.observedAnon(e)}});n=W(e,a.submitCapture,i.destination,t);let o=await r.connect(a.client);return n.needsAnon()&&n.observedAnon((await a.client.getSession()).anonId),o}}},Z=d(),Q=p(Z),$=`window.${Z}`;if(Q!==null&&Q.ready===void 0){let e=Te(Q,Z);t(Q),_(Q,async()=>{let t=Q.config?.destination;if(typeof t!=`string`||t===``)throw Error(`[Qinto] ${$}.config.destination is missing.`);return e.connect({destination:t})},$)}})();
|