@blotout/qinto-cli 0.1.30 → 0.1.31
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 -1
- package/dist/index.js +3226 -3110
- package/dist/index.js.map +1 -1
- package/dist/worker-artifact/app-runtime/index.js +80 -6
- package/dist/worker-artifact/main/index.js +299 -173
- package/dist/worker-artifact/version.json +9 -9
- package/dist/worker-artifact/wfp/index.js +2 -1
- package/package.json +1 -1
|
@@ -21071,6 +21071,7 @@ var endpointSchema = external_exports.object({
|
|
|
21071
21071
|
var eventsSchema = external_exports.object({
|
|
21072
21072
|
publishes: external_exports.array(external_exports.string().min(1).max(MAX_EVENT_TOPIC_LENGTH).regex(EVENT_TOPIC)).max(MAX_PUBLISHED_TOPICS).default([])
|
|
21073
21073
|
}).strict();
|
|
21074
|
+
var dependenciesSchema = external_exports.array(external_exports.object({ app: external_exports.string().regex(APP_SLUG_PATTERN), description: renderableText(1, 200).optional() }).strict()).max(10);
|
|
21074
21075
|
var rpcSchema = external_exports.object({
|
|
21075
21076
|
exposes: external_exports.array(external_exports.object({ name: external_exports.string().regex(FUNCTION_NAME).max(64), visibility: external_exports.enum(["public", "private"]) }).strict()).max(32).optional(),
|
|
21076
21077
|
calls: external_exports.array(external_exports.object({ app: external_exports.string().regex(APP_SLUG_PATTERN), fn: external_exports.string().regex(FUNCTION_NAME).max(64) }).strict()).max(64).optional()
|
|
@@ -21114,6 +21115,9 @@ var baseSchema = external_exports.object({
|
|
|
21114
21115
|
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."
|
|
21115
21116
|
}),
|
|
21116
21117
|
resources: external_exports.array(resourceSchema).max(32).default([]).meta({ description: "The Cloudflare bindings the app worker is given." }),
|
|
21118
|
+
dependencies: dependenciesSchema.optional().meta({
|
|
21119
|
+
description: "The apps this app needs on the same site. Every app it calls over RPC must be listed."
|
|
21120
|
+
}),
|
|
21117
21121
|
rpc: rpcSchema.optional().meta({ description: "The functions this app exposes to other apps, and the ones it calls on them." }),
|
|
21118
21122
|
events: eventsSchema.optional().meta({
|
|
21119
21123
|
description: "The push topics this app broadcasts to every app on the site. Requires the `sharedContext` data capability; a topic left out reaches only this app\u2019s own browser code."
|
|
@@ -21430,12 +21434,33 @@ var validateRpc = /* @__PURE__ */ __name((manifest, ctx) => {
|
|
|
21430
21434
|
["rpc", "exposes"],
|
|
21431
21435
|
"function"
|
|
21432
21436
|
);
|
|
21437
|
+
const dependencies = new Set((manifest.dependencies ?? []).map((dependency) => dependency.app));
|
|
21433
21438
|
for (const [index, call] of (manifest.rpc?.calls ?? []).entries()) {
|
|
21434
21439
|
if (call.app === manifest.slug) {
|
|
21435
21440
|
ctx.addIssue({ code: "custom", path: ["rpc", "calls", index, "app"], message: "An app cannot declare an RPC call to itself" });
|
|
21441
|
+
} else if (!dependencies.has(call.app)) {
|
|
21442
|
+
ctx.addIssue({
|
|
21443
|
+
code: "custom",
|
|
21444
|
+
path: ["rpc", "calls", index, "app"],
|
|
21445
|
+
message: `${call.app} is called over RPC, so it must be in dependencies. Add { "app": "${call.app}" } to dependencies; if your Qinto CLI rejects that field, upgrade it.`
|
|
21446
|
+
});
|
|
21436
21447
|
}
|
|
21437
21448
|
}
|
|
21438
21449
|
}, "validateRpc");
|
|
21450
|
+
var validateDependencies = /* @__PURE__ */ __name((manifest, ctx) => {
|
|
21451
|
+
const dependencies = manifest.dependencies ?? [];
|
|
21452
|
+
uniqueOrIssue(
|
|
21453
|
+
ctx,
|
|
21454
|
+
dependencies.map((dependency) => dependency.app),
|
|
21455
|
+
["dependencies"],
|
|
21456
|
+
"dependency"
|
|
21457
|
+
);
|
|
21458
|
+
for (const [index, dependency] of dependencies.entries()) {
|
|
21459
|
+
if (dependency.app === manifest.slug) {
|
|
21460
|
+
ctx.addIssue({ code: "custom", path: ["dependencies", index, "app"], message: "An app cannot depend on itself" });
|
|
21461
|
+
}
|
|
21462
|
+
}
|
|
21463
|
+
}, "validateDependencies");
|
|
21439
21464
|
var validateServer = /* @__PURE__ */ __name((manifest, ctx) => {
|
|
21440
21465
|
uniqueOrIssue(ctx, manifest.server.hooks.transform, ["server", "hooks", "transform"], "transform hook");
|
|
21441
21466
|
uniqueOrIssue(ctx, manifest.server.hooks.onEvent, ["server", "hooks", "onEvent"], "event hook");
|
|
@@ -21483,6 +21508,7 @@ var appManifestSchema = baseSchema.superRefine((manifest, ctx) => {
|
|
|
21483
21508
|
validateEndpoints(manifest, ctx);
|
|
21484
21509
|
validateCapture(manifest, ctx);
|
|
21485
21510
|
validateBrowserHooks(manifest, ctx);
|
|
21511
|
+
validateDependencies(manifest, ctx);
|
|
21486
21512
|
validateRpc(manifest, ctx);
|
|
21487
21513
|
validateEvents(manifest, ctx);
|
|
21488
21514
|
validateOauth(manifest, ctx);
|
|
@@ -21511,6 +21537,44 @@ var storageLinkBindingName = /* @__PURE__ */ __name((domainId) => {
|
|
|
21511
21537
|
}, "storageLinkBindingName");
|
|
21512
21538
|
var MAX_APPS_Q_BYTES = 4 * 1024 * 1024;
|
|
21513
21539
|
|
|
21540
|
+
// ../../libs/app-envelope/src/geo.ts
|
|
21541
|
+
var cfString = external_exports.string().optional().catch(void 0);
|
|
21542
|
+
var requestGeoSchema = external_exports.object({
|
|
21543
|
+
country: cfString,
|
|
21544
|
+
city: cfString,
|
|
21545
|
+
region: cfString,
|
|
21546
|
+
regionCode: cfString,
|
|
21547
|
+
timezone: cfString,
|
|
21548
|
+
postalCode: cfString,
|
|
21549
|
+
continent: cfString,
|
|
21550
|
+
latitude: cfString,
|
|
21551
|
+
longitude: cfString,
|
|
21552
|
+
isEUCountry: cfString,
|
|
21553
|
+
botManagement: external_exports.object({
|
|
21554
|
+
ja4: cfString,
|
|
21555
|
+
ja3Hash: cfString,
|
|
21556
|
+
score: external_exports.number().optional().catch(void 0),
|
|
21557
|
+
verifiedBot: external_exports.boolean().optional().catch(void 0)
|
|
21558
|
+
}).optional().catch(void 0)
|
|
21559
|
+
});
|
|
21560
|
+
var userGeoSchema = external_exports.object({
|
|
21561
|
+
ip: external_exports.string().optional(),
|
|
21562
|
+
userAgent: external_exports.string().optional(),
|
|
21563
|
+
timezone: external_exports.string().optional(),
|
|
21564
|
+
city: external_exports.string().optional(),
|
|
21565
|
+
postalCode: external_exports.string().optional(),
|
|
21566
|
+
region: external_exports.string().optional(),
|
|
21567
|
+
regionCode: external_exports.string().optional(),
|
|
21568
|
+
country: external_exports.string().optional(),
|
|
21569
|
+
continent: external_exports.string().optional(),
|
|
21570
|
+
isEUCountry: external_exports.string().optional(),
|
|
21571
|
+
latitude: external_exports.string().optional(),
|
|
21572
|
+
longitude: external_exports.string().optional(),
|
|
21573
|
+
ja4: external_exports.string().optional(),
|
|
21574
|
+
ja3: external_exports.string().optional(),
|
|
21575
|
+
botScore: external_exports.string().optional()
|
|
21576
|
+
});
|
|
21577
|
+
|
|
21514
21578
|
// ../../libs/app-envelope/src/envelope.ts
|
|
21515
21579
|
var ENVELOPE_VERSION = 3;
|
|
21516
21580
|
var ENVELOPE_ERROR_CODES = ["invalid_arguments"];
|
|
@@ -21588,11 +21652,21 @@ var envelopeRequestSchema = external_exports.object({
|
|
|
21588
21652
|
anonId: external_exports.string().min(1).max(64),
|
|
21589
21653
|
globalId: external_exports.string().min(1).max(64).nullable(),
|
|
21590
21654
|
createdAt: external_exports.number().int().optional(),
|
|
21591
|
-
properties: external_exports.record(external_exports.string(), external_exports.
|
|
21592
|
-
geo:
|
|
21655
|
+
properties: external_exports.record(external_exports.string(), external_exports.string()),
|
|
21656
|
+
geo: userGeoSchema.optional()
|
|
21657
|
+
}).strict().optional(),
|
|
21658
|
+
consent: external_exports.object({
|
|
21659
|
+
enabled: external_exports.boolean(),
|
|
21660
|
+
categories: external_exports.object({
|
|
21661
|
+
necessary: external_exports.literal(true),
|
|
21662
|
+
functional: external_exports.boolean(),
|
|
21663
|
+
analytics: external_exports.boolean(),
|
|
21664
|
+
marketing: external_exports.boolean(),
|
|
21665
|
+
salesOfData: external_exports.boolean()
|
|
21666
|
+
}),
|
|
21667
|
+
apps: external_exports.record(external_exports.string(), external_exports.boolean())
|
|
21593
21668
|
}).strict().optional(),
|
|
21594
|
-
|
|
21595
|
-
geo: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
|
|
21669
|
+
geo: requestGeoSchema.optional(),
|
|
21596
21670
|
// Connect envelopes only: the page the visitor's socket opened from, and the visit session it joined.
|
|
21597
21671
|
page: pageSchema.optional(),
|
|
21598
21672
|
session: sessionSchema.optional(),
|
|
@@ -21900,7 +21974,7 @@ var endpointContextSchema = external_exports.object({
|
|
|
21900
21974
|
method: external_exports.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]),
|
|
21901
21975
|
mode: external_exports.enum(["webhook", "api"]),
|
|
21902
21976
|
version: external_exports.string().min(1).max(32),
|
|
21903
|
-
geo:
|
|
21977
|
+
geo: requestGeoSchema.optional(),
|
|
21904
21978
|
installs: external_exports.array(installSchema3).min(1).max(15),
|
|
21905
21979
|
user: external_exports.object({ anonId: external_exports.string().min(1).max(64) }).strict().optional()
|
|
21906
21980
|
}).strict().refine((context) => {
|
|
@@ -26665,6 +26739,26 @@ var siteRefOf = /* @__PURE__ */ __name(({ domainId, fingerprint }) => {
|
|
|
26665
26739
|
|
|
26666
26740
|
// ../../libs/cf-data/src/cf-data.ts
|
|
26667
26741
|
var CF_DATA_HEADER = "x-cf-data";
|
|
26742
|
+
var cfDataFrom = /* @__PURE__ */ __name((cf) => {
|
|
26743
|
+
return {
|
|
26744
|
+
country: cf?.country,
|
|
26745
|
+
city: cf?.city,
|
|
26746
|
+
region: cf?.region,
|
|
26747
|
+
regionCode: cf?.regionCode,
|
|
26748
|
+
timezone: cf?.timezone,
|
|
26749
|
+
postalCode: cf?.postalCode,
|
|
26750
|
+
continent: cf?.continent,
|
|
26751
|
+
longitude: cf?.longitude,
|
|
26752
|
+
latitude: cf?.latitude,
|
|
26753
|
+
isEUCountry: cf?.isEUCountry,
|
|
26754
|
+
botManagement: {
|
|
26755
|
+
ja4: cf?.botManagement?.ja4,
|
|
26756
|
+
ja3Hash: cf?.botManagement?.ja3Hash,
|
|
26757
|
+
score: cf?.botManagement?.score,
|
|
26758
|
+
verifiedBot: cf?.botManagement?.verifiedBot
|
|
26759
|
+
}
|
|
26760
|
+
};
|
|
26761
|
+
}, "cfDataFrom");
|
|
26668
26762
|
var parseCfData = /* @__PURE__ */ __name((raw2) => {
|
|
26669
26763
|
if (raw2 === null) {
|
|
26670
26764
|
return void 0;
|
|
@@ -28125,7 +28219,7 @@ var BOT_SCORE_MIN = 1;
|
|
|
28125
28219
|
var BOT_SCORE_MAX = 99;
|
|
28126
28220
|
|
|
28127
28221
|
// ../../libs/bot-protection/src/config.ts
|
|
28128
|
-
var NO_PROTECTION = { minBotScore: null, countries: null };
|
|
28222
|
+
var NO_PROTECTION = { minBotScore: null, countries: null, blockVerifiedBots: false };
|
|
28129
28223
|
var normalizeScore = /* @__PURE__ */ __name((value, problems) => {
|
|
28130
28224
|
if (value === void 0 || value === null) {
|
|
28131
28225
|
return null;
|
|
@@ -28159,6 +28253,13 @@ var normalizeCountries = /* @__PURE__ */ __name((value, problems) => {
|
|
|
28159
28253
|
}
|
|
28160
28254
|
return list.length === 0 ? null : { mode: record2.mode, list };
|
|
28161
28255
|
}, "normalizeCountries");
|
|
28256
|
+
var normalizeBlockVerifiedBots = /* @__PURE__ */ __name((value, problems) => {
|
|
28257
|
+
if (value === void 0 || typeof value === "boolean") {
|
|
28258
|
+
return value ?? false;
|
|
28259
|
+
}
|
|
28260
|
+
problems.push("has a non-boolean blockVerifiedBots");
|
|
28261
|
+
return false;
|
|
28262
|
+
}, "normalizeBlockVerifiedBots");
|
|
28162
28263
|
var parseBotProtection = /* @__PURE__ */ __name((raw2) => {
|
|
28163
28264
|
if (raw2.trim() === "") {
|
|
28164
28265
|
return { config: NO_PROTECTION };
|
|
@@ -28176,15 +28277,19 @@ var parseBotProtection = /* @__PURE__ */ __name((raw2) => {
|
|
|
28176
28277
|
const problems = [];
|
|
28177
28278
|
const config2 = {
|
|
28178
28279
|
minBotScore: normalizeScore(record2.minBotScore, problems),
|
|
28179
|
-
countries: normalizeCountries(record2.countries, problems)
|
|
28280
|
+
countries: normalizeCountries(record2.countries, problems),
|
|
28281
|
+
blockVerifiedBots: normalizeBlockVerifiedBots(record2.blockVerifiedBots, problems)
|
|
28180
28282
|
};
|
|
28181
28283
|
return problems.length > 0 ? { config: config2, error: `BOT_PROTECTION ${problems.join("; ")}` } : { config: config2 };
|
|
28182
28284
|
}, "parseBotProtection");
|
|
28183
28285
|
var isRequestBlocked = /* @__PURE__ */ __name((config2, signal) => {
|
|
28184
|
-
if (
|
|
28286
|
+
if (isCountryBlocked(signal.country, signal.isEU ?? false, config2.countries)) {
|
|
28185
28287
|
return true;
|
|
28186
28288
|
}
|
|
28187
|
-
|
|
28289
|
+
if (signal.verifiedBot === true) {
|
|
28290
|
+
return config2.blockVerifiedBots;
|
|
28291
|
+
}
|
|
28292
|
+
return config2.minBotScore !== null && signal.botScore !== void 0 && signal.botScore < config2.minBotScore;
|
|
28188
28293
|
}, "isRequestBlocked");
|
|
28189
28294
|
|
|
28190
28295
|
// ../../libs/geo-filter/src/us-states.ts
|
|
@@ -28583,7 +28688,7 @@ var buildEnvelope = /* @__PURE__ */ __name((target, trigger, snapshot, context)
|
|
|
28583
28688
|
...geoAllowed ? { geo: snapshot.userGeo } : {}
|
|
28584
28689
|
},
|
|
28585
28690
|
...buckets.includes("consentState") ? { consent: { enabled: context.consentEnforced, categories: snapshot.consent.categories, apps: snapshot.consent.apps } } : {},
|
|
28586
|
-
...geoAllowed && currentGeo !== void 0 ? { geo: currentGeo } : {},
|
|
28691
|
+
...geoAllowed && currentGeo !== void 0 ? { geo: cfDataFrom(currentGeo) } : {},
|
|
28587
28692
|
...trigger.kind === "connect" && buckets.includes("eventContext") ? connectContext(trigger) : {},
|
|
28588
28693
|
// Not gated on a capabilities.data bucket: an app only ever sees what it captured or wrote.
|
|
28589
28694
|
...captures === void 0 ? {} : { captures },
|
|
@@ -29765,7 +29870,7 @@ var streamWithDeadlines = /* @__PURE__ */ __name((source, { idleMs, totalMs }) =
|
|
|
29765
29870
|
}, "streamWithDeadlines");
|
|
29766
29871
|
|
|
29767
29872
|
// src/helpers/bot-check.ts
|
|
29768
|
-
var botRejected = /* @__PURE__ */ __name((env, geo) => {
|
|
29873
|
+
var botRejected = /* @__PURE__ */ __name((env, request, geo) => {
|
|
29769
29874
|
return traced("bot.check", {}, () => {
|
|
29770
29875
|
const { config: protection, error: error61 } = parseBotProtection(env.BOT_PROTECTION);
|
|
29771
29876
|
if (error61 !== void 0) {
|
|
@@ -29774,8 +29879,19 @@ var botRejected = /* @__PURE__ */ __name((env, geo) => {
|
|
|
29774
29879
|
attrs: { error: String(error61) }
|
|
29775
29880
|
});
|
|
29776
29881
|
}
|
|
29777
|
-
const
|
|
29778
|
-
|
|
29882
|
+
const cf = request.cf;
|
|
29883
|
+
const verifiedBot = cf?.botManagement?.verifiedBot;
|
|
29884
|
+
const rejected = isRequestBlocked(protection, {
|
|
29885
|
+
botScore: geo.botScore,
|
|
29886
|
+
verifiedBot,
|
|
29887
|
+
country: geo.country,
|
|
29888
|
+
isEU: geo.isEUCountry === "yes"
|
|
29889
|
+
});
|
|
29890
|
+
setSpanAttributes({
|
|
29891
|
+
"bot.blocked": rejected,
|
|
29892
|
+
...geo.botScore === void 0 ? {} : { "bot.score": geo.botScore },
|
|
29893
|
+
...verifiedBot === void 0 ? {} : { "bot.verified": verifiedBot }
|
|
29894
|
+
});
|
|
29779
29895
|
return rejected;
|
|
29780
29896
|
});
|
|
29781
29897
|
}, "botRejected");
|
|
@@ -29865,7 +29981,7 @@ var withCors = /* @__PURE__ */ __name((response, request, method) => {
|
|
|
29865
29981
|
}, "withCors");
|
|
29866
29982
|
var authorize = /* @__PURE__ */ __name(async (c, { slug, app: app2 }) => {
|
|
29867
29983
|
const geo = geoFromRequest(c.req.raw);
|
|
29868
|
-
if (await botRejected(c.env, geo)) {
|
|
29984
|
+
if (await botRejected(c.env, c.req.raw, geo)) {
|
|
29869
29985
|
return refuse(403, "traffic_rejected");
|
|
29870
29986
|
}
|
|
29871
29987
|
const anonId = getCookie(c, COOKIE_NAME2);
|
|
@@ -30064,7 +30180,7 @@ var runForwardToApp = /* @__PURE__ */ __name(async (c, input2) => {
|
|
|
30064
30180
|
method: endpoint.method,
|
|
30065
30181
|
mode: endpoint.mode,
|
|
30066
30182
|
version: app2.version,
|
|
30067
|
-
...app2.data.includes("geo") && requestGeo !== void 0 ? { geo: requestGeo } : {},
|
|
30183
|
+
...app2.data.includes("geo") && requestGeo !== void 0 ? { geo: cfDataFrom(requestGeo) } : {},
|
|
30068
30184
|
installs: installs.map((install) => ({ id: install.id, ord: install.ord, label: install.label })),
|
|
30069
30185
|
...anonId === void 0 ? {} : { user: { anonId } }
|
|
30070
30186
|
})
|
|
@@ -30660,6 +30776,7 @@ var createApiRouter = /* @__PURE__ */ __name(() => new OpenAPIHono({ defaultHook
|
|
|
30660
30776
|
var CONNECTION_CF_HEADER = "x-connection-cf";
|
|
30661
30777
|
var SESSION_HEADER = "x-session";
|
|
30662
30778
|
var GPC_HEADER = "x-gpc";
|
|
30779
|
+
var RECONNECT_HEADER = "x-reconnect";
|
|
30663
30780
|
var OPEN_PAGE_PARAMS = ["url", "referrer"];
|
|
30664
30781
|
var MAX_HOST_USER_AGENT_LENGTH = 512;
|
|
30665
30782
|
var MAX_HOST_LOCALE_LENGTH = 64;
|
|
@@ -30671,6 +30788,7 @@ var INTERNAL_HEADERS = /* @__PURE__ */ new Set([
|
|
|
30671
30788
|
CONNECTION_CF_HEADER,
|
|
30672
30789
|
SESSION_HEADER,
|
|
30673
30790
|
GPC_HEADER,
|
|
30791
|
+
RECONNECT_HEADER,
|
|
30674
30792
|
TRACE_HEADER,
|
|
30675
30793
|
CF_DATA_HEADER
|
|
30676
30794
|
]);
|
|
@@ -30759,7 +30877,7 @@ var boundedHost = /* @__PURE__ */ __name((request) => {
|
|
|
30759
30877
|
};
|
|
30760
30878
|
return bounded.ip === void 0 && bounded.userAgent === void 0 && bounded.locale === void 0 ? void 0 : bounded;
|
|
30761
30879
|
}, "boundedHost");
|
|
30762
|
-
var connectionAttachment = /* @__PURE__ */ __name((request, geo, siteId) => {
|
|
30880
|
+
var connectionAttachment = /* @__PURE__ */ __name((request, geo, { siteId, anonId }) => {
|
|
30763
30881
|
const userAgent = request.headers.get("user-agent");
|
|
30764
30882
|
const attrs = {
|
|
30765
30883
|
"url.full": withoutQuery(request.url),
|
|
@@ -30775,7 +30893,7 @@ var connectionAttachment = /* @__PURE__ */ __name((request, geo, siteId) => {
|
|
|
30775
30893
|
const sessionBytes = attachmentFieldBytes("session", session2);
|
|
30776
30894
|
const gpc = request.headers.get(GPC_HEADER) === "1";
|
|
30777
30895
|
const gpcBytes = attachmentFieldBytes("gpc", gpc ? true : void 0);
|
|
30778
|
-
const siteBytes = attachmentFieldBytes("siteId", siteId);
|
|
30896
|
+
const siteBytes = attachmentFieldBytes("siteId", siteId) + attachmentFieldBytes("anonId", anonId);
|
|
30779
30897
|
const cf = forwardedCf(request);
|
|
30780
30898
|
const cfSuffix = cf === void 0 ? "" : `,"cf":${JSON.stringify(cf)}`;
|
|
30781
30899
|
const cfBytes = new TextEncoder().encode(cfSuffix).length;
|
|
@@ -30786,6 +30904,7 @@ var connectionAttachment = /* @__PURE__ */ __name((request, geo, siteId) => {
|
|
|
30786
30904
|
return {
|
|
30787
30905
|
...budgeted,
|
|
30788
30906
|
siteId,
|
|
30907
|
+
anonId,
|
|
30789
30908
|
...session2 === void 0 ? {} : { session: session2 },
|
|
30790
30909
|
...browserGeo === void 0 ? {} : { browserGeo },
|
|
30791
30910
|
...includedCf === void 0 ? {} : { cf: includedCf },
|
|
@@ -30793,6 +30912,14 @@ var connectionAttachment = /* @__PURE__ */ __name((request, geo, siteId) => {
|
|
|
30793
30912
|
...gpc ? { gpc: true } : {}
|
|
30794
30913
|
};
|
|
30795
30914
|
}, "connectionAttachment");
|
|
30915
|
+
var attachmentGeo = /* @__PURE__ */ __name((attachment) => {
|
|
30916
|
+
return {
|
|
30917
|
+
// `cf` is the forwarded `request.cf`, kept as plain JSON on the socket.
|
|
30918
|
+
...geoFromCf(attachment.cf),
|
|
30919
|
+
ip: attachment.host?.ip,
|
|
30920
|
+
userAgent: attachment.host?.userAgent
|
|
30921
|
+
};
|
|
30922
|
+
}, "attachmentGeo");
|
|
30796
30923
|
|
|
30797
30924
|
// src/helpers/request-span.ts
|
|
30798
30925
|
var OMITTED_HEADERS = /* @__PURE__ */ new Set(["authorization", DISPATCH_HEADER, CF_DATA_HEADER]);
|
|
@@ -31192,6 +31319,29 @@ var withSetCookie = /* @__PURE__ */ __name((res, cookies) => {
|
|
|
31192
31319
|
}
|
|
31193
31320
|
return out;
|
|
31194
31321
|
}, "withSetCookie");
|
|
31322
|
+
var connectCookies = /* @__PURE__ */ __name(async (c, userId, session2) => {
|
|
31323
|
+
const root = c.env.ROOT_DOMAIN;
|
|
31324
|
+
const hostname3 = new URL(c.req.url).hostname;
|
|
31325
|
+
const domain2 = root !== void 0 && root !== "" && (hostname3 === root || hostname3.endsWith(`.${root}`)) ? root : void 0;
|
|
31326
|
+
const attributes = {
|
|
31327
|
+
httpOnly: true,
|
|
31328
|
+
// `wrangler types` narrows COOKIE_SAME_SITE to its dev default; deploys also set none.
|
|
31329
|
+
sameSite: c.env.COOKIE_SAME_SITE,
|
|
31330
|
+
secure: true,
|
|
31331
|
+
path: "/",
|
|
31332
|
+
maxAge: COOKIE_MAX_AGE
|
|
31333
|
+
};
|
|
31334
|
+
const cookies = [
|
|
31335
|
+
generateCookie(COOKIE_NAME2, userId, { ...attributes, ...domain2 === void 0 ? {} : { domain: domain2 } }),
|
|
31336
|
+
// Re-issued on every connect, which is what makes the session expire after 30 idle minutes.
|
|
31337
|
+
generateCookie(SESSION_COOKIE_NAME, `${session2.id}.${session2.startedAt}`, { ...attributes, maxAge: SESSION_MAX_AGE })
|
|
31338
|
+
];
|
|
31339
|
+
const signature = await signVisitor(c.env, userId);
|
|
31340
|
+
if (signature !== void 0) {
|
|
31341
|
+
cookies.push(generateCookie(VISITOR_SIGNATURE_COOKIE, signature, attributes));
|
|
31342
|
+
}
|
|
31343
|
+
return cookies;
|
|
31344
|
+
}, "connectCookies");
|
|
31195
31345
|
var open2 = /* @__PURE__ */ __name(async (c) => {
|
|
31196
31346
|
if (c.req.header("upgrade")?.toLowerCase() !== "websocket") {
|
|
31197
31347
|
return c.text("expected websocket", 426);
|
|
@@ -31204,13 +31354,14 @@ var open2 = /* @__PURE__ */ __name(async (c) => {
|
|
|
31204
31354
|
});
|
|
31205
31355
|
return enriched;
|
|
31206
31356
|
});
|
|
31207
|
-
if (await botRejected(c.env, geo)) {
|
|
31357
|
+
if (await botRejected(c.env, c.req.raw, geo)) {
|
|
31208
31358
|
return c.text("traffic rejected", 403);
|
|
31209
31359
|
}
|
|
31210
31360
|
const existing = getCookie(c, COOKIE_NAME2);
|
|
31361
|
+
const canonical = existing !== void 0 && isCanonicalId(existing);
|
|
31211
31362
|
let isNewUser = false;
|
|
31212
31363
|
let userId;
|
|
31213
|
-
if (
|
|
31364
|
+
if (canonical) {
|
|
31214
31365
|
userId = existing;
|
|
31215
31366
|
} else {
|
|
31216
31367
|
const adopted = edgeTagUserId(c);
|
|
@@ -31236,6 +31387,10 @@ var open2 = /* @__PURE__ */ __name(async (c) => {
|
|
|
31236
31387
|
headers.set(GPC_HEADER, "1");
|
|
31237
31388
|
setSpanAttributes({ "qinto.gpc": true });
|
|
31238
31389
|
}
|
|
31390
|
+
headers.delete(RECONNECT_HEADER);
|
|
31391
|
+
if (canonical && c.req.query("reconnect") === "1") {
|
|
31392
|
+
headers.set(RECONNECT_HEADER, "1");
|
|
31393
|
+
}
|
|
31239
31394
|
const res = await traced("rpc anon.connect", {}, () => {
|
|
31240
31395
|
headers.delete(TRACE_HEADER);
|
|
31241
31396
|
const trace = currentTraceContext();
|
|
@@ -31244,27 +31399,7 @@ var open2 = /* @__PURE__ */ __name(async (c) => {
|
|
|
31244
31399
|
}
|
|
31245
31400
|
return anonStub(c.env, userId).fetch(new Request(c.req.url, { method: "GET", headers }));
|
|
31246
31401
|
});
|
|
31247
|
-
|
|
31248
|
-
const hostname3 = new URL(c.req.url).hostname;
|
|
31249
|
-
const domain2 = root !== void 0 && root !== "" && (hostname3 === root || hostname3.endsWith(`.${root}`)) ? root : void 0;
|
|
31250
|
-
const attributes = {
|
|
31251
|
-
httpOnly: true,
|
|
31252
|
-
// `wrangler types` narrows COOKIE_SAME_SITE to its dev default; deploys also set none.
|
|
31253
|
-
sameSite: c.env.COOKIE_SAME_SITE,
|
|
31254
|
-
secure: true,
|
|
31255
|
-
path: "/",
|
|
31256
|
-
maxAge: COOKIE_MAX_AGE
|
|
31257
|
-
};
|
|
31258
|
-
const cookies = [
|
|
31259
|
-
generateCookie(COOKIE_NAME2, userId, { ...attributes, ...domain2 === void 0 ? {} : { domain: domain2 } }),
|
|
31260
|
-
// Re-issued on every connect, which is what makes the session expire after 30 idle minutes.
|
|
31261
|
-
generateCookie(SESSION_COOKIE_NAME, `${session2.id}.${session2.startedAt}`, { ...attributes, maxAge: SESSION_MAX_AGE })
|
|
31262
|
-
];
|
|
31263
|
-
const signature = await signVisitor(c.env, userId);
|
|
31264
|
-
if (signature !== void 0) {
|
|
31265
|
-
cookies.push(generateCookie(VISITOR_SIGNATURE_COOKIE, signature, attributes));
|
|
31266
|
-
}
|
|
31267
|
-
return withSetCookie(res, cookies);
|
|
31402
|
+
return withSetCookie(res, await connectCookies(c, userId, session2));
|
|
31268
31403
|
}, "open");
|
|
31269
31404
|
|
|
31270
31405
|
// browser-artifact/meta.json
|
|
@@ -32476,13 +32611,15 @@ var cachedKv = /* @__PURE__ */ __name((kv) => {
|
|
|
32476
32611
|
var VERSION_KEY = "schemaVersion";
|
|
32477
32612
|
var applyMigrations = /* @__PURE__ */ __name((storage, migrations) => {
|
|
32478
32613
|
const applied = storage.kv.get(VERSION_KEY) ?? 0;
|
|
32479
|
-
|
|
32480
|
-
|
|
32481
|
-
continue;
|
|
32482
|
-
}
|
|
32483
|
-
storage.sql.exec(migration);
|
|
32484
|
-
storage.kv.put(VERSION_KEY, index + 1);
|
|
32614
|
+
if (applied >= migrations.length) {
|
|
32615
|
+
return;
|
|
32485
32616
|
}
|
|
32617
|
+
storage.transactionSync(() => {
|
|
32618
|
+
for (const migration of migrations.slice(applied)) {
|
|
32619
|
+
storage.sql.exec(migration);
|
|
32620
|
+
}
|
|
32621
|
+
storage.kv.put(VERSION_KEY, migrations.length);
|
|
32622
|
+
});
|
|
32486
32623
|
}, "applyMigrations");
|
|
32487
32624
|
|
|
32488
32625
|
// src/id-graph/retention.ts
|
|
@@ -32768,15 +32905,15 @@ var seedMeta = /* @__PURE__ */ __name((kv, anonId, createdAt) => {
|
|
|
32768
32905
|
kv.put(META_KEY, { anonId, linkedGlobalId: null, linkedAt: null, createdAt });
|
|
32769
32906
|
return true;
|
|
32770
32907
|
}, "seedMeta");
|
|
32908
|
+
var lastActivityAt = /* @__PURE__ */ __name((kv) => {
|
|
32909
|
+
return kv.get(LAST_ACTIVITY_KEY) ?? kv.get(META_KEY)?.createdAt ?? null;
|
|
32910
|
+
}, "lastActivityAt");
|
|
32771
32911
|
var touchActivity = /* @__PURE__ */ __name((kv, receivedAt) => {
|
|
32772
|
-
const last = kv
|
|
32773
|
-
if (last ===
|
|
32912
|
+
const last = lastActivityAt(kv);
|
|
32913
|
+
if (last === null || receivedAt - last >= CLEANUP_INTERVAL_MS) {
|
|
32774
32914
|
kv.put(LAST_ACTIVITY_KEY, receivedAt);
|
|
32775
32915
|
}
|
|
32776
32916
|
}, "touchActivity");
|
|
32777
|
-
var lastActivityAt = /* @__PURE__ */ __name((kv) => {
|
|
32778
|
-
return kv.get(LAST_ACTIVITY_KEY) ?? null;
|
|
32779
|
-
}, "lastActivityAt");
|
|
32780
32917
|
var linkedGlobalId = /* @__PURE__ */ __name((kv) => {
|
|
32781
32918
|
return kv.get(META_KEY)?.linkedGlobalId ?? null;
|
|
32782
32919
|
}, "linkedGlobalId");
|
|
@@ -33010,7 +33147,7 @@ var standsDown = /* @__PURE__ */ __name(async (storage, staleActivity) => {
|
|
|
33010
33147
|
if (lastActivity === staleActivity) {
|
|
33011
33148
|
return false;
|
|
33012
33149
|
}
|
|
33013
|
-
await storage.setAlarm(cleanupTarget(storage, Date.now(), lastActivity
|
|
33150
|
+
await storage.setAlarm(cleanupTarget(storage, Date.now(), lastActivity));
|
|
33014
33151
|
return true;
|
|
33015
33152
|
}, "standsDown");
|
|
33016
33153
|
var relink = /* @__PURE__ */ __name(async (storage, env, anonId, globalId) => {
|
|
@@ -33029,7 +33166,7 @@ var relink = /* @__PURE__ */ __name(async (storage, env, anonId, globalId) => {
|
|
|
33029
33166
|
addSpanEvent("expiry relink failed", { level: "warn", attrs: { "anon.id": anonId, "global.id": globalId, error: String(error61) } });
|
|
33030
33167
|
}
|
|
33031
33168
|
if (mutated) {
|
|
33032
|
-
await storage.setAlarm(cleanupTarget(storage, Date.now(), lastActivityAt(storage.kv)
|
|
33169
|
+
await storage.setAlarm(cleanupTarget(storage, Date.now(), lastActivityAt(storage.kv)));
|
|
33033
33170
|
}
|
|
33034
33171
|
}, "relink");
|
|
33035
33172
|
var unlinkFromPerson = /* @__PURE__ */ __name((env, anonId, globalId) => {
|
|
@@ -33588,10 +33725,9 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33588
33725
|
// In memory on purpose: a hibernated socket sent nothing, so a fresh bucket on wake is the right one.
|
|
33589
33726
|
frameBuckets = /* @__PURE__ */ new WeakMap();
|
|
33590
33727
|
emitBudget = createTokenBucket(EMIT_BURST, EMITS_PER_SECOND);
|
|
33591
|
-
|
|
33728
|
+
stored;
|
|
33729
|
+
// This instance deleted the visitor's storage; only a later write recreates it.
|
|
33592
33730
|
wiped = false;
|
|
33593
|
-
// True while processAction holds ingestQueue, including the transform await.
|
|
33594
|
-
ingestTurn = false;
|
|
33595
33731
|
// Warm write-through mirrors reset together after an expiry or an erase.
|
|
33596
33732
|
kvCache;
|
|
33597
33733
|
cachedStorage;
|
|
@@ -33607,7 +33743,6 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33607
33743
|
sites;
|
|
33608
33744
|
constructor(ctx, env) {
|
|
33609
33745
|
super(ctx, env);
|
|
33610
|
-
applyMigrations(ctx.storage, ANON_MIGRATIONS);
|
|
33611
33746
|
this.kvCache = cachedKv(ctx.storage.kv);
|
|
33612
33747
|
this.cachedStorage = { kv: this.kvCache, sql: ctx.storage.sql };
|
|
33613
33748
|
this.graph = ownGraphEnv(ctx, env);
|
|
@@ -33630,32 +33765,29 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33630
33765
|
const trace = parseTraceContext(request.headers.get(TRACE_HEADER));
|
|
33631
33766
|
return runInvocation({ name: "do.anon.connect", trace, attrs: connectAttrs(request, geo) }, async () => {
|
|
33632
33767
|
const context = await this.sites.resolve({ domainId: siteId }, Date.now());
|
|
33633
|
-
this.
|
|
33634
|
-
|
|
33635
|
-
this.cleanupAlarm = await seedCleanupAlarm(this.ctx.storage, now, lastActivityAt(this.kv) ?? now, this.cleanupAlarm);
|
|
33636
|
-
seedMeta(this.kv, userId, now);
|
|
33637
|
-
touchActivity(this.kv, now);
|
|
33638
|
-
if (geo !== void 0 && !hasGeoRecord(this.kv)) {
|
|
33639
|
-
this.seedGeoOnConnect(userId, geo, now);
|
|
33768
|
+
if (this.isStored()) {
|
|
33769
|
+
touchActivity(this.kv, Date.now());
|
|
33640
33770
|
}
|
|
33641
33771
|
const { 0: client, 1: server } = new WebSocketPair();
|
|
33642
33772
|
this.ctx.acceptWebSocket(server, [siteId]);
|
|
33643
|
-
const attachment = connectionAttachment(request, geo, siteId);
|
|
33773
|
+
const attachment = connectionAttachment(request, geo, { siteId, anonId: userId });
|
|
33644
33774
|
server.serializeAttachment(attachment);
|
|
33645
33775
|
this.sendSharedContext([server], context);
|
|
33646
|
-
|
|
33647
|
-
|
|
33648
|
-
|
|
33649
|
-
|
|
33650
|
-
|
|
33651
|
-
|
|
33652
|
-
|
|
33653
|
-
|
|
33654
|
-
|
|
33655
|
-
|
|
33656
|
-
|
|
33657
|
-
|
|
33658
|
-
|
|
33776
|
+
if (request.headers.get(RECONNECT_HEADER) !== "1") {
|
|
33777
|
+
const page = connectPage(request);
|
|
33778
|
+
this.appFanOut(
|
|
33779
|
+
{
|
|
33780
|
+
kind: "connect",
|
|
33781
|
+
...attachment.cf === void 0 ? {} : { geo: attachment.cf },
|
|
33782
|
+
host: attachment.host,
|
|
33783
|
+
...page === void 0 ? {} : { page },
|
|
33784
|
+
...attachment.session === void 0 ? {} : { session: attachment.session },
|
|
33785
|
+
gpc: attachment.gpc
|
|
33786
|
+
},
|
|
33787
|
+
userId,
|
|
33788
|
+
context
|
|
33789
|
+
);
|
|
33790
|
+
}
|
|
33659
33791
|
return new Response(null, { status: 101, webSocket: client });
|
|
33660
33792
|
});
|
|
33661
33793
|
}
|
|
@@ -33693,7 +33825,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33693
33825
|
}
|
|
33694
33826
|
if (frame.t === "capture") {
|
|
33695
33827
|
const { accepted } = await this.capture(
|
|
33696
|
-
{ apps: frame.apps, location: socketLocation(attachment.cf), gpc: attachment.gpc === true },
|
|
33828
|
+
{ anonId: userId, apps: frame.apps, location: socketLocation(attachment.cf), gpc: attachment.gpc === true },
|
|
33697
33829
|
site
|
|
33698
33830
|
);
|
|
33699
33831
|
this.respond(ws, { t: "captureAck", ref, anonId: userId, accepted });
|
|
@@ -33738,7 +33870,8 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33738
33870
|
syncFromGlobal(snapshot, trace) {
|
|
33739
33871
|
return runInvocation({ name: "do.anon.sync_from_global", trace, attrs: { "global.id": snapshot.globalId } }, () => {
|
|
33740
33872
|
return this.enqueue(async () => {
|
|
33741
|
-
|
|
33873
|
+
const anonId = storedAnonId(this.kv);
|
|
33874
|
+
if (anonId === null) {
|
|
33742
33875
|
addSpanEvent("snapshot dropped: no visitor state", { level: "warn", attrs: { "global.id": snapshot.globalId } });
|
|
33743
33876
|
return;
|
|
33744
33877
|
}
|
|
@@ -33746,7 +33879,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33746
33879
|
addSpanEvent("foreign snapshot dropped", { level: "warn", attrs: { "global.id": snapshot.globalId } });
|
|
33747
33880
|
return;
|
|
33748
33881
|
}
|
|
33749
|
-
|
|
33882
|
+
await this.storeVisitor(anonId, Date.now());
|
|
33750
33883
|
await this.applyOutcome(applySnapshot(this.cachedStorage, snapshot, this.snapshotCaches()));
|
|
33751
33884
|
});
|
|
33752
33885
|
});
|
|
@@ -33763,7 +33896,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33763
33896
|
this.respond(ws, { t: "error", ref: parsed.ref, message: "invalid message" });
|
|
33764
33897
|
return;
|
|
33765
33898
|
}
|
|
33766
|
-
const userId =
|
|
33899
|
+
const userId = attachment?.anonId ?? null;
|
|
33767
33900
|
setSpanAttributes(frameSpanAttrs(parsed.frame, userId));
|
|
33768
33901
|
if (parsed.frame.t === "getConsent") {
|
|
33769
33902
|
this.respond(ws, this.consentReply(parsed.ref, userId));
|
|
@@ -33813,10 +33946,6 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33813
33946
|
};
|
|
33814
33947
|
return runInvocation({ name: "do.anon.app_push", trace, attrs }, () => {
|
|
33815
33948
|
return this.enqueue(async () => {
|
|
33816
|
-
if (await this.discardIfUnused()) {
|
|
33817
|
-
addSpanEvent("app push dropped: no visitor state", { level: "warn", attrs: { app: push.slug } });
|
|
33818
|
-
return;
|
|
33819
|
-
}
|
|
33820
33949
|
this.deliverAppPush(push, await this.sites.resolve(site, Date.now()));
|
|
33821
33950
|
});
|
|
33822
33951
|
});
|
|
@@ -33836,26 +33965,19 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33836
33965
|
// so a value the caps refused must never be reported as stored.
|
|
33837
33966
|
capture(input2, site, trace) {
|
|
33838
33967
|
return runInvocation({ name: "do.anon.capture", trace }, () => {
|
|
33839
|
-
return this.enqueue(
|
|
33840
|
-
|
|
33841
|
-
addSpanEvent("capture dropped: no visitor state", { level: "warn" });
|
|
33842
|
-
return { accepted: {} };
|
|
33843
|
-
}
|
|
33844
|
-
return this.runCaptures(input2.apps, site, { location: input2.location, gpc: input2.gpc });
|
|
33968
|
+
return this.enqueue(() => {
|
|
33969
|
+
return this.runCaptures(input2.anonId, input2.apps, site, { location: input2.location, gpc: input2.gpc });
|
|
33845
33970
|
});
|
|
33846
33971
|
});
|
|
33847
33972
|
}
|
|
33848
|
-
//
|
|
33849
|
-
// carry click ids before any identify, so unlike the socket this one creates the profile.
|
|
33973
|
+
// A migrated visitor can carry click ids before any identify, so this door creates the profile.
|
|
33850
33974
|
serverCapture(input2, site, trace) {
|
|
33851
33975
|
return runInvocation({ name: "do.anon.server_capture", trace }, () => {
|
|
33852
33976
|
return this.enqueue(async () => {
|
|
33853
|
-
this.revive();
|
|
33854
33977
|
const now = Date.now();
|
|
33855
|
-
|
|
33856
|
-
seedMeta(this.kv, input2.anonId, now);
|
|
33978
|
+
await this.storeVisitor(input2.anonId, now);
|
|
33857
33979
|
touchActivity(this.kv, now);
|
|
33858
|
-
return this.runCaptures(input2.apps, site, { location: null, gpc: false });
|
|
33980
|
+
return this.runCaptures(input2.anonId, input2.apps, site, { location: null, gpc: false });
|
|
33859
33981
|
});
|
|
33860
33982
|
});
|
|
33861
33983
|
}
|
|
@@ -33864,7 +33986,8 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33864
33986
|
appCaptures(input2, site, trace) {
|
|
33865
33987
|
return runInvocation({ name: "do.anon.app_captures", trace, attrs: { "app.slug": input2.slug } }, () => {
|
|
33866
33988
|
return this.enqueue(async () => {
|
|
33867
|
-
|
|
33989
|
+
const anonId = this.visitorId();
|
|
33990
|
+
if (anonId === null) {
|
|
33868
33991
|
addSpanEvent("capture dropped: no visitor state", { level: "warn", attrs: { app: input2.slug } });
|
|
33869
33992
|
return;
|
|
33870
33993
|
}
|
|
@@ -33877,7 +34000,8 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33877
34000
|
})) {
|
|
33878
34001
|
return;
|
|
33879
34002
|
}
|
|
33880
|
-
this.storeCaptures(
|
|
34003
|
+
await this.storeCaptures(
|
|
34004
|
+
anonId,
|
|
33881
34005
|
input2.writes.map((write) => {
|
|
33882
34006
|
return { ...write, appId: input2.slug, source: "app", ttlMs: declaredTtlMs(context.registry, input2.slug, write.key) };
|
|
33883
34007
|
})
|
|
@@ -33885,7 +34009,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33885
34009
|
});
|
|
33886
34010
|
});
|
|
33887
34011
|
}
|
|
33888
|
-
async runCaptures(apps, site, { location, gpc }) {
|
|
34012
|
+
async runCaptures(anonId, apps, site, { location, gpc }) {
|
|
33889
34013
|
const context = await this.sites.resolve(site, Date.now());
|
|
33890
34014
|
const writes = acceptedCaptures(context.registry, apps, {
|
|
33891
34015
|
consent: consentState2(this.kv),
|
|
@@ -33893,13 +34017,13 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33893
34017
|
gpc,
|
|
33894
34018
|
location
|
|
33895
34019
|
});
|
|
33896
|
-
return { accepted: this.storeCaptures(writes) };
|
|
34020
|
+
return { accepted: await this.storeCaptures(anonId, writes) };
|
|
33897
34021
|
}
|
|
33898
|
-
storeCaptures(writes) {
|
|
34022
|
+
async storeCaptures(anonId, writes) {
|
|
33899
34023
|
if (writes.length === 0) {
|
|
33900
34024
|
return {};
|
|
33901
34025
|
}
|
|
33902
|
-
this.
|
|
34026
|
+
await this.storeVisitor(anonId, Date.now());
|
|
33903
34027
|
const accepted = {};
|
|
33904
34028
|
const stored = writeCaptures(this.sql, writes, Date.now(), this.captureMirror);
|
|
33905
34029
|
for (const write of stored) {
|
|
@@ -33915,7 +34039,8 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33915
34039
|
appContext(input2, site, trace) {
|
|
33916
34040
|
return runInvocation({ name: "do.anon.app_context", trace, attrs: { "app.slug": input2.slug } }, () => {
|
|
33917
34041
|
return this.enqueue(async () => {
|
|
33918
|
-
|
|
34042
|
+
const anonId = this.visitorId();
|
|
34043
|
+
if (anonId === null) {
|
|
33919
34044
|
addSpanEvent("context dropped: no visitor state", { level: "warn", attrs: { app: input2.slug } });
|
|
33920
34045
|
return [];
|
|
33921
34046
|
}
|
|
@@ -33940,15 +34065,18 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33940
34065
|
}
|
|
33941
34066
|
return true;
|
|
33942
34067
|
});
|
|
33943
|
-
return this.storeContext(
|
|
34068
|
+
return this.storeContext(
|
|
34069
|
+
anonId,
|
|
34070
|
+
writes.map((write) => ({ ...write, appId: input2.slug }))
|
|
34071
|
+
);
|
|
33944
34072
|
});
|
|
33945
34073
|
});
|
|
33946
34074
|
}
|
|
33947
|
-
async storeContext(writes) {
|
|
34075
|
+
async storeContext(anonId, writes) {
|
|
33948
34076
|
if (writes.length === 0) {
|
|
33949
34077
|
return [];
|
|
33950
34078
|
}
|
|
33951
|
-
this.
|
|
34079
|
+
await this.storeVisitor(anonId, Date.now());
|
|
33952
34080
|
const stored = [];
|
|
33953
34081
|
for (const write of writes) {
|
|
33954
34082
|
if (writeContext(this.sql, [write], Date.now(), this.contextMirror).length === 0) {
|
|
@@ -33966,7 +34094,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33966
34094
|
return stored;
|
|
33967
34095
|
}
|
|
33968
34096
|
hasSharedContext() {
|
|
33969
|
-
return
|
|
34097
|
+
return this.isStored() && Object.keys(allContext(this.sql, "shared", this.contextMirror)).length > 0;
|
|
33970
34098
|
}
|
|
33971
34099
|
// Shared context is the level every app may read, so the browser gets it too — the connecting
|
|
33972
34100
|
// socket on open, every socket on a change. Private never leaves the server. The connect frame is
|
|
@@ -33974,7 +34102,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33974
34102
|
// context from one that has not arrived yet.
|
|
33975
34103
|
sendSharedContext(sockets, context) {
|
|
33976
34104
|
const consent2 = consentState2(this.kv);
|
|
33977
|
-
const stored = allContext(this.sql, "shared", this.contextMirror);
|
|
34105
|
+
const stored = this.isStored() ? allContext(this.sql, "shared", this.contextMirror) : {};
|
|
33978
34106
|
for (const socket of sockets) {
|
|
33979
34107
|
const { gpc } = socket.deserializeAttachment();
|
|
33980
34108
|
sendFrame(socket, sharedContextFrame(context, consent2, stored, gpc === true));
|
|
@@ -34002,12 +34130,10 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34002
34130
|
}
|
|
34003
34131
|
}
|
|
34004
34132
|
}
|
|
34005
|
-
// A read must not resurrect a visitor: an rpc or workflow asking about an expired or unknown anon
|
|
34006
|
-
// would otherwise leave an empty object behind that no cleanup alarm collects.
|
|
34007
34133
|
getAppContext(slug, site, trace) {
|
|
34008
34134
|
return runInvocation({ name: "do.anon.get_app_context", trace, attrs: { "app.slug": slug } }, () => {
|
|
34009
34135
|
return this.enqueue(async () => {
|
|
34010
|
-
if (
|
|
34136
|
+
if (!this.isStored()) {
|
|
34011
34137
|
return readableContext(void 0, {});
|
|
34012
34138
|
}
|
|
34013
34139
|
const context = await this.sites.resolve(site, Date.now());
|
|
@@ -34026,20 +34152,18 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34026
34152
|
});
|
|
34027
34153
|
});
|
|
34028
34154
|
}
|
|
34029
|
-
//
|
|
34030
|
-
// and must not discardIfUnused during an ingest turn (meta is seeded after the transform).
|
|
34155
|
+
// Must not join ingestQueue: a transform that lists facts holds that slot.
|
|
34031
34156
|
getAppFacts(slug, query, site, trace) {
|
|
34032
34157
|
return runInvocation({ name: "do.anon.get_app_facts", trace, attrs: { "app.slug": slug } }, async () => {
|
|
34033
34158
|
const context = await this.sites.resolve(site, Date.now());
|
|
34034
|
-
const unused = this.ingestTurn ? false : await this.discardIfUnused();
|
|
34035
34159
|
const app2 = context.registry.apps[slug];
|
|
34036
|
-
if (
|
|
34160
|
+
if (app2 === void 0 || !app2.data.includes("facts")) {
|
|
34037
34161
|
return null;
|
|
34038
34162
|
}
|
|
34039
34163
|
if (!consentAllows(consentState2(this.kv), slug, app2.consent.category, { enforced: context.consentEnforced, gpc: false })) {
|
|
34040
34164
|
return null;
|
|
34041
34165
|
}
|
|
34042
|
-
if (this.
|
|
34166
|
+
if (!this.isStored()) {
|
|
34043
34167
|
return { items: [], next: null };
|
|
34044
34168
|
}
|
|
34045
34169
|
return listAppFacts(this.sql, query, Date.now());
|
|
@@ -34074,8 +34198,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34074
34198
|
}
|
|
34075
34199
|
}
|
|
34076
34200
|
}
|
|
34077
|
-
//
|
|
34078
|
-
// is one — the tag treats its absence as "cannot vouch for the memo" rather than as a match.
|
|
34201
|
+
// No anonId tells the tag "cannot vouch for the memo", not a match.
|
|
34079
34202
|
consentReply(ref, userId) {
|
|
34080
34203
|
return { t: "consentState", ref, ...userId === null ? {} : { anonId: userId }, ...consentState2(this.kv) };
|
|
34081
34204
|
}
|
|
@@ -34108,7 +34231,6 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34108
34231
|
return run;
|
|
34109
34232
|
}
|
|
34110
34233
|
async processAction(action, context) {
|
|
34111
|
-
this.ingestTurn = true;
|
|
34112
34234
|
let recorded = action;
|
|
34113
34235
|
try {
|
|
34114
34236
|
if (isNoopAction(action)) {
|
|
@@ -34122,12 +34244,15 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34122
34244
|
await this.runAction(transformed.action, transformed.releaseEmissions, context);
|
|
34123
34245
|
} finally {
|
|
34124
34246
|
this.recordActionBlocks(recorded, context);
|
|
34125
|
-
this.ingestTurn = false;
|
|
34126
34247
|
}
|
|
34127
34248
|
}
|
|
34128
34249
|
recordActionBlocks(action, context) {
|
|
34129
34250
|
setSpanAttributes(
|
|
34130
|
-
actionBlocks(
|
|
34251
|
+
actionBlocks(
|
|
34252
|
+
action,
|
|
34253
|
+
this.appsSnapshot(action.userId, context),
|
|
34254
|
+
this.isStored() ? allIdentifiers(this.sql, this.identifiersMirror) : []
|
|
34255
|
+
)
|
|
34131
34256
|
);
|
|
34132
34257
|
}
|
|
34133
34258
|
async runAction(current, releaseEmissions, context) {
|
|
@@ -34136,14 +34261,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34136
34261
|
this.releaseTransformEmissions(releaseEmissions);
|
|
34137
34262
|
return;
|
|
34138
34263
|
}
|
|
34139
|
-
this.
|
|
34140
|
-
this.cleanupAlarm = await seedCleanupAlarm(
|
|
34141
|
-
this.ctx.storage,
|
|
34142
|
-
Date.now(),
|
|
34143
|
-
lastActivityAt(this.kv) ?? current.receivedAt,
|
|
34144
|
-
this.cleanupAlarm
|
|
34145
|
-
);
|
|
34146
|
-
seedMeta(this.kv, current.userId, current.receivedAt);
|
|
34264
|
+
await this.storeVisitor(current.userId, current.receivedAt);
|
|
34147
34265
|
touchActivity(this.kv, current.receivedAt);
|
|
34148
34266
|
const novelIdentifier = this.recordIdentifiers(current);
|
|
34149
34267
|
if (novelIdentifier && linkedGlobalId(this.kv) !== null) {
|
|
@@ -34200,16 +34318,15 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34200
34318
|
}
|
|
34201
34319
|
appsSnapshot(anonId, context) {
|
|
34202
34320
|
const meta3 = storedMeta(this.kv);
|
|
34321
|
+
const stored = this.isStored();
|
|
34203
34322
|
return {
|
|
34204
34323
|
anonId,
|
|
34205
34324
|
globalId: meta3?.linkedGlobalId ?? null,
|
|
34206
|
-
createdAt: meta3?.createdAt ??
|
|
34325
|
+
createdAt: meta3?.createdAt ?? Date.now(),
|
|
34207
34326
|
pii: userProperties(this.kv),
|
|
34208
|
-
|
|
34209
|
-
|
|
34210
|
-
|
|
34211
|
-
captures: this.wiped ? {} : allCaptures(this.sql, Date.now(), this.captureMirror),
|
|
34212
|
-
context: this.wiped ? { private: {}, shared: {} } : {
|
|
34327
|
+
custom: stored ? customProperties(this.sql, this.customPropsMirror) : {},
|
|
34328
|
+
captures: stored ? allCaptures(this.sql, Date.now(), this.captureMirror) : {},
|
|
34329
|
+
context: !stored ? { private: {}, shared: {} } : {
|
|
34213
34330
|
private: allContext(this.sql, "private", this.contextMirror),
|
|
34214
34331
|
shared: readableSharedContext(
|
|
34215
34332
|
context.registry,
|
|
@@ -34244,7 +34361,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34244
34361
|
getProperties(trace) {
|
|
34245
34362
|
return runInvocation({ name: "do.anon.get_properties", trace }, () => {
|
|
34246
34363
|
return this.enqueue(async () => {
|
|
34247
|
-
if (
|
|
34364
|
+
if (!this.isStored()) {
|
|
34248
34365
|
return {};
|
|
34249
34366
|
}
|
|
34250
34367
|
return { ...userProperties(this.kv), ...customProperties(this.sql, this.customPropsMirror) };
|
|
@@ -34254,16 +34371,15 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34254
34371
|
getConsent(trace) {
|
|
34255
34372
|
return runInvocation({ name: "do.anon.get_consent", trace }, () => {
|
|
34256
34373
|
return this.enqueue(async () => {
|
|
34257
|
-
await this.discardIfUnused();
|
|
34258
34374
|
return consentState2(this.kv);
|
|
34259
34375
|
});
|
|
34260
34376
|
});
|
|
34261
34377
|
}
|
|
34262
|
-
// Null means no session: the id
|
|
34378
|
+
// Null means no session: the id stored nothing yet or its profile expired.
|
|
34263
34379
|
getPersonLink(trace) {
|
|
34264
34380
|
return runInvocation({ name: "do.anon.get_person_link", trace }, () => {
|
|
34265
34381
|
return this.enqueue(async () => {
|
|
34266
|
-
if (
|
|
34382
|
+
if (!this.isStored()) {
|
|
34267
34383
|
return null;
|
|
34268
34384
|
}
|
|
34269
34385
|
return { globalId: linkedGlobalId(this.kv) };
|
|
@@ -34273,7 +34389,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34273
34389
|
getGlobalId(trace) {
|
|
34274
34390
|
return runInvocation({ name: "do.anon.get_global_id", trace }, () => {
|
|
34275
34391
|
return this.enqueue(async () => {
|
|
34276
|
-
if (
|
|
34392
|
+
if (!this.isStored()) {
|
|
34277
34393
|
return null;
|
|
34278
34394
|
}
|
|
34279
34395
|
return linkedGlobalId(this.kv);
|
|
@@ -34283,8 +34399,8 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34283
34399
|
erasable(trace) {
|
|
34284
34400
|
return runInvocation({ name: "do.anon.erasable", trace }, () => {
|
|
34285
34401
|
return this.enqueue(async () => {
|
|
34286
|
-
if (
|
|
34287
|
-
return null;
|
|
34402
|
+
if (!this.isStored()) {
|
|
34403
|
+
return this.ctx.getWebSockets().length === 0 ? null : { globalId: null, identifiers: [] };
|
|
34288
34404
|
}
|
|
34289
34405
|
const identifiers = allIdentifiers(this.sql, this.identifiersMirror).map(({ type, value }) => ({ type, value }));
|
|
34290
34406
|
return { globalId: linkedGlobalId(this.kv), identifiers };
|
|
@@ -34294,7 +34410,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34294
34410
|
inspect(trace) {
|
|
34295
34411
|
return runInvocation({ name: "do.anon.inspect", trace }, () => {
|
|
34296
34412
|
return this.enqueue(async () => {
|
|
34297
|
-
if (
|
|
34413
|
+
if (!this.isStored()) {
|
|
34298
34414
|
return { exists: false };
|
|
34299
34415
|
}
|
|
34300
34416
|
return { exists: true, objectId: this.ctx.id.toString(), ...dumpStorage(this.ctx.storage) };
|
|
@@ -34304,7 +34420,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34304
34420
|
inspectTable(table, offset, limit, trace) {
|
|
34305
34421
|
return runInvocation({ name: "do.anon.inspect_table", trace, attrs: { "inspect.table": table } }, () => {
|
|
34306
34422
|
return this.enqueue(async () => {
|
|
34307
|
-
if (
|
|
34423
|
+
if (!this.isStored()) {
|
|
34308
34424
|
return null;
|
|
34309
34425
|
}
|
|
34310
34426
|
return readTable(this.ctx.storage, table, offset, limit);
|
|
@@ -34314,13 +34430,16 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34314
34430
|
erase(trace) {
|
|
34315
34431
|
return runInvocation({ name: "do.anon.erase", trace }, () => {
|
|
34316
34432
|
return this.enqueue(async () => {
|
|
34317
|
-
|
|
34433
|
+
const stored = this.isStored();
|
|
34434
|
+
if (!stored && this.ctx.getWebSockets().length === 0) {
|
|
34318
34435
|
return false;
|
|
34319
34436
|
}
|
|
34320
|
-
|
|
34321
|
-
|
|
34322
|
-
|
|
34323
|
-
|
|
34437
|
+
if (stored) {
|
|
34438
|
+
try {
|
|
34439
|
+
await eraseAnon(this.ctx.storage, this.graph);
|
|
34440
|
+
} finally {
|
|
34441
|
+
this.resetMirrors();
|
|
34442
|
+
}
|
|
34324
34443
|
}
|
|
34325
34444
|
this.wiped = true;
|
|
34326
34445
|
for (const socket of this.ctx.getWebSockets()) {
|
|
@@ -34330,19 +34449,24 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34330
34449
|
});
|
|
34331
34450
|
});
|
|
34332
34451
|
}
|
|
34333
|
-
//
|
|
34334
|
-
|
|
34335
|
-
|
|
34336
|
-
|
|
34337
|
-
|
|
34338
|
-
|
|
34339
|
-
|
|
34452
|
+
// Every write seeds meta through storeVisitor first, so an object without it has no schema to read.
|
|
34453
|
+
isStored() {
|
|
34454
|
+
if (this.stored === void 0) {
|
|
34455
|
+
this.stored = storedMeta(this.kv) !== void 0;
|
|
34456
|
+
if (this.stored) {
|
|
34457
|
+
applyMigrations(this.ctx.storage, ANON_MIGRATIONS);
|
|
34458
|
+
}
|
|
34340
34459
|
}
|
|
34341
|
-
|
|
34342
|
-
|
|
34343
|
-
|
|
34460
|
+
return this.stored;
|
|
34461
|
+
}
|
|
34462
|
+
// Stubs address this object by anon id, but an app may name any user, so only a canonical id counts.
|
|
34463
|
+
visitorId() {
|
|
34464
|
+
const anonId = storedAnonId(this.kv);
|
|
34465
|
+
if (anonId !== null || this.wiped) {
|
|
34466
|
+
return anonId;
|
|
34344
34467
|
}
|
|
34345
|
-
|
|
34468
|
+
const name = this.ctx.id.name;
|
|
34469
|
+
return name !== void 0 && isCanonicalId(name) ? name : null;
|
|
34346
34470
|
}
|
|
34347
34471
|
appendProjectionHistory(outcomes, source) {
|
|
34348
34472
|
for (const { entry } of outcomes) {
|
|
@@ -34551,6 +34675,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34551
34675
|
}
|
|
34552
34676
|
async alarm() {
|
|
34553
34677
|
return runInvocation({ name: "do.anon.cleanup" }, async () => {
|
|
34678
|
+
applyMigrations(this.ctx.storage, ANON_MIGRATIONS);
|
|
34554
34679
|
this.cleanupAlarm = void 0;
|
|
34555
34680
|
const now = Date.now();
|
|
34556
34681
|
const lastActivity = lastActivityAt(this.kv) ?? now;
|
|
@@ -34575,6 +34700,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34575
34700
|
}
|
|
34576
34701
|
resetMirrors() {
|
|
34577
34702
|
this.kvCache.clear();
|
|
34703
|
+
this.stored = void 0;
|
|
34578
34704
|
this.cleanupAlarm = void 0;
|
|
34579
34705
|
this.identifiersMirror.reset();
|
|
34580
34706
|
this.conversionsMirror.reset();
|
|
@@ -34582,14 +34708,22 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34582
34708
|
this.captureMirror.reset();
|
|
34583
34709
|
this.contextMirror.reset();
|
|
34584
34710
|
}
|
|
34585
|
-
|
|
34586
|
-
|
|
34711
|
+
// Callers run in ingestQueue and nothing awaits between the stored check and the seeds.
|
|
34712
|
+
async storeVisitor(anonId, at) {
|
|
34713
|
+
this.cleanupAlarm = await seedCleanupAlarm(this.ctx.storage, Date.now(), lastActivityAt(this.kv) ?? at, this.cleanupAlarm);
|
|
34714
|
+
if (!this.isStored()) {
|
|
34587
34715
|
applyMigrations(this.ctx.storage, ANON_MIGRATIONS);
|
|
34716
|
+
seedMeta(this.kv, anonId, at);
|
|
34717
|
+
this.stored = true;
|
|
34588
34718
|
this.wiped = false;
|
|
34589
34719
|
}
|
|
34720
|
+
const socket = hasGeoRecord(this.kv) ? void 0 : this.ctx.getWebSockets()[0]?.deserializeAttachment();
|
|
34721
|
+
if (socket !== void 0) {
|
|
34722
|
+
updateGeo(this.cachedStorage, attachmentGeo(socket), at);
|
|
34723
|
+
}
|
|
34590
34724
|
}
|
|
34591
34725
|
async retargetCleanupAlarm(now) {
|
|
34592
|
-
const target = cleanupTarget(this.cachedStorage, now, lastActivityAt(this.kv)
|
|
34726
|
+
const target = cleanupTarget(this.cachedStorage, now, lastActivityAt(this.kv));
|
|
34593
34727
|
if (this.cleanupAlarm === target) {
|
|
34594
34728
|
return;
|
|
34595
34729
|
}
|
|
@@ -34599,14 +34733,6 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34599
34733
|
await this.ctx.storage.setAlarm(target);
|
|
34600
34734
|
this.cleanupAlarm = target;
|
|
34601
34735
|
}
|
|
34602
|
-
seedGeoOnConnect(anonId, geo, observedAt) {
|
|
34603
|
-
const run = this.enqueue(async () => {
|
|
34604
|
-
this.revive();
|
|
34605
|
-
updateGeo(this.cachedStorage, geo, observedAt);
|
|
34606
|
-
await this.retargetCleanupAlarm(Date.now());
|
|
34607
|
-
});
|
|
34608
|
-
this.ctx.waitUntil(run.catch((error61) => console.warn("geo seed failed", { anonId, error: error61 })));
|
|
34609
|
-
}
|
|
34610
34736
|
};
|
|
34611
34737
|
|
|
34612
34738
|
// src/id-graph/global/do.ts
|