@blotout/qinto-cli 0.1.30 → 0.1.32
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 +3962 -3838
- package/dist/index.js.map +1 -1
- package/dist/worker-artifact/app-runtime/index.js +161 -29
- package/dist/worker-artifact/main/73ef5f028ec35836560c0489d1abdeea83eceede-q.js.txt +1 -0
- package/dist/worker-artifact/main/index.js +1488 -1271
- package/dist/worker-artifact/version.json +11 -11
- package/dist/worker-artifact/wfp/index.js +2 -1
- package/package.json +1 -1
- package/dist/worker-artifact/main/8809017061697bf9b90fa4d72f7eb2e0f0e00ad4-q.js.txt +0 -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,9 +21537,47 @@ 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
|
-
var ENVELOPE_ERROR_CODES = ["invalid_arguments"];
|
|
21580
|
+
var ENVELOPE_ERROR_CODES = ["invalid_arguments", "result_too_large", "callee_timeout", "rpc_error"];
|
|
21517
21581
|
var MAX_INSTALLS_PER_APP = 15;
|
|
21518
21582
|
var MAX_APPS_PER_DOMAIN = 50;
|
|
21519
21583
|
var MAX_EMITS_PER_INSTALL = 10;
|
|
@@ -21529,6 +21593,7 @@ var MAX_SHARED_CONTEXT_ENTRY_BYTES = 4 * 1024;
|
|
|
21529
21593
|
var MAX_PUSH_BYTES = 16 * 1024;
|
|
21530
21594
|
var MAX_RESPONSE_BYTES = 1024 * 1024;
|
|
21531
21595
|
var MAX_RPC_BYTES = 128 * 1024;
|
|
21596
|
+
var TRAFFIC_CLASSES = ["hot", "cold"];
|
|
21532
21597
|
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();
|
|
21533
21598
|
var installRuleSchema = external_exports.object({
|
|
21534
21599
|
mode: external_exports.enum(["except", "only"]),
|
|
@@ -21588,11 +21653,21 @@ var envelopeRequestSchema = external_exports.object({
|
|
|
21588
21653
|
anonId: external_exports.string().min(1).max(64),
|
|
21589
21654
|
globalId: external_exports.string().min(1).max(64).nullable(),
|
|
21590
21655
|
createdAt: external_exports.number().int().optional(),
|
|
21591
|
-
properties: external_exports.record(external_exports.string(), external_exports.
|
|
21592
|
-
geo:
|
|
21656
|
+
properties: external_exports.record(external_exports.string(), external_exports.string()),
|
|
21657
|
+
geo: userGeoSchema.optional()
|
|
21593
21658
|
}).strict().optional(),
|
|
21594
|
-
consent: external_exports.object({
|
|
21595
|
-
|
|
21659
|
+
consent: external_exports.object({
|
|
21660
|
+
enabled: external_exports.boolean(),
|
|
21661
|
+
categories: external_exports.object({
|
|
21662
|
+
necessary: external_exports.literal(true),
|
|
21663
|
+
functional: external_exports.boolean(),
|
|
21664
|
+
analytics: external_exports.boolean(),
|
|
21665
|
+
marketing: external_exports.boolean(),
|
|
21666
|
+
salesOfData: external_exports.boolean()
|
|
21667
|
+
}),
|
|
21668
|
+
apps: external_exports.record(external_exports.string(), external_exports.boolean())
|
|
21669
|
+
}).strict().optional(),
|
|
21670
|
+
geo: requestGeoSchema.optional(),
|
|
21596
21671
|
// Connect envelopes only: the page the visitor's socket opened from, and the visit session it joined.
|
|
21597
21672
|
page: pageSchema.optional(),
|
|
21598
21673
|
session: sessionSchema.optional(),
|
|
@@ -21603,7 +21678,15 @@ var envelopeRequestSchema = external_exports.object({
|
|
|
21603
21678
|
context: external_exports.object({ private: contextEntrySchema.optional(), shared: external_exports.record(external_exports.string().min(1).max(30), contextEntrySchema).optional() }).strict().optional(),
|
|
21604
21679
|
// RPC depth is capped at 3 by the broker; hook depth at 2 by the emit path.
|
|
21605
21680
|
depth: external_exports.number().int().min(0).max(3),
|
|
21606
|
-
rpc: external_exports.object({
|
|
21681
|
+
rpc: external_exports.object({
|
|
21682
|
+
caller: external_exports.string().min(1).max(30).nullable(),
|
|
21683
|
+
fn: external_exports.string().min(1).max(64),
|
|
21684
|
+
args: external_exports.unknown(),
|
|
21685
|
+
// Optional on the wire: a site worker predating traffic classes sends none, which means hot.
|
|
21686
|
+
traffic: external_exports.enum(TRAFFIC_CLASSES).optional(),
|
|
21687
|
+
// Cold calls only: the broker mints it so the callee can query its own catalog.
|
|
21688
|
+
queryTicket: external_exports.string().min(1).max(2048).optional()
|
|
21689
|
+
}).strict().refine((rpc) => {
|
|
21607
21690
|
return new TextEncoder().encode(JSON.stringify(rpc.args ?? null)).length <= MAX_RPC_BYTES;
|
|
21608
21691
|
}, "RPC args exceed the size cap").optional(),
|
|
21609
21692
|
action: external_exports.object({
|
|
@@ -21900,7 +21983,7 @@ var endpointContextSchema = external_exports.object({
|
|
|
21900
21983
|
method: external_exports.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]),
|
|
21901
21984
|
mode: external_exports.enum(["webhook", "api"]),
|
|
21902
21985
|
version: external_exports.string().min(1).max(32),
|
|
21903
|
-
geo:
|
|
21986
|
+
geo: requestGeoSchema.optional(),
|
|
21904
21987
|
installs: external_exports.array(installSchema3).min(1).max(15),
|
|
21905
21988
|
user: external_exports.object({ anonId: external_exports.string().min(1).max(64) }).strict().optional()
|
|
21906
21989
|
}).strict().refine((context) => {
|
|
@@ -26495,6 +26578,207 @@ var constantTimeEqual = /* @__PURE__ */ __name((expected, presented) => {
|
|
|
26495
26578
|
return expectedBytes.length === presentedBytes.length && timingSafeEqual(expectedBytes, presentedBytes);
|
|
26496
26579
|
}, "constantTimeEqual");
|
|
26497
26580
|
|
|
26581
|
+
// src/helpers/durable.ts
|
|
26582
|
+
var ownGraphEnv = /* @__PURE__ */ __name((ctx, env) => {
|
|
26583
|
+
const exports = ctx.exports;
|
|
26584
|
+
return {
|
|
26585
|
+
...env,
|
|
26586
|
+
get ANON_DO() {
|
|
26587
|
+
return exports.AnonDO;
|
|
26588
|
+
},
|
|
26589
|
+
get GLOBAL_DO() {
|
|
26590
|
+
return exports.GlobalDO;
|
|
26591
|
+
},
|
|
26592
|
+
get ALIAS_DO() {
|
|
26593
|
+
return exports.AliasDO;
|
|
26594
|
+
}
|
|
26595
|
+
};
|
|
26596
|
+
}, "ownGraphEnv");
|
|
26597
|
+
var JURISDICTIONS = /* @__PURE__ */ new Set(["eu", "fedramp", "fedramp-high", "us"]);
|
|
26598
|
+
var LOCATION_HINTS = /* @__PURE__ */ new Set([
|
|
26599
|
+
"wnam",
|
|
26600
|
+
"enam",
|
|
26601
|
+
"sam",
|
|
26602
|
+
"weur",
|
|
26603
|
+
"eeur",
|
|
26604
|
+
"apac",
|
|
26605
|
+
"apac-ne",
|
|
26606
|
+
"apac-se",
|
|
26607
|
+
"oc",
|
|
26608
|
+
"afr",
|
|
26609
|
+
"me"
|
|
26610
|
+
]);
|
|
26611
|
+
var residencyStub = /* @__PURE__ */ __name((namespace, env, name) => {
|
|
26612
|
+
const residency = env;
|
|
26613
|
+
const jurisdiction = residency.DO_JURISDICTION;
|
|
26614
|
+
const scoped = jurisdiction !== void 0 && JURISDICTIONS.has(jurisdiction) ? namespace.jurisdiction(jurisdiction) : namespace;
|
|
26615
|
+
const locationHint = residency.LOCATION_HINT;
|
|
26616
|
+
const options = locationHint !== void 0 && LOCATION_HINTS.has(locationHint) ? { locationHint } : void 0;
|
|
26617
|
+
return scoped.get(scoped.idFromName(name), options);
|
|
26618
|
+
}, "residencyStub");
|
|
26619
|
+
|
|
26620
|
+
// src/id-graph/anon/client.ts
|
|
26621
|
+
var anonStub = /* @__PURE__ */ __name((env, anonId) => {
|
|
26622
|
+
return residencyStub(env.ANON_DO, env, anonId);
|
|
26623
|
+
}, "anonStub");
|
|
26624
|
+
|
|
26625
|
+
// src/id-graph/consent.ts
|
|
26626
|
+
var CONSENT_CATEGORIES2 = ["necessary", "functional", "analytics", "marketing", "salesOfData"];
|
|
26627
|
+
var CONSENT_CATEGORY_PREFIX = "consent.category.";
|
|
26628
|
+
var CONSENT_APP_PREFIX = "consent.app.";
|
|
26629
|
+
var consentEntries = /* @__PURE__ */ __name((payload, observedAt) => {
|
|
26630
|
+
const entries = [];
|
|
26631
|
+
for (const category of CONSENT_CATEGORIES2) {
|
|
26632
|
+
const granted = payload.categories[category];
|
|
26633
|
+
if (granted === void 0) {
|
|
26634
|
+
continue;
|
|
26635
|
+
}
|
|
26636
|
+
entries.push({ key: `${CONSENT_CATEGORY_PREFIX}${category}`, value: String(granted), observedAt });
|
|
26637
|
+
}
|
|
26638
|
+
for (const [appId, granted] of Object.entries(payload.apps)) {
|
|
26639
|
+
entries.push({ key: `${CONSENT_APP_PREFIX}${appId}`, value: String(granted), observedAt });
|
|
26640
|
+
}
|
|
26641
|
+
return entries;
|
|
26642
|
+
}, "consentEntries");
|
|
26643
|
+
|
|
26644
|
+
// src/id-graph/projections.ts
|
|
26645
|
+
var PII_PREFIX = "pii.";
|
|
26646
|
+
var CUSTOM_PREFIX = "custom.";
|
|
26647
|
+
var GEO_PREFIX = "geo.";
|
|
26648
|
+
var PROJECTION_KINDS = ["pii", "custom", "consent_category", "consent_app", "geo"];
|
|
26649
|
+
var PROJECTION_PREFIX = {
|
|
26650
|
+
pii: PII_PREFIX,
|
|
26651
|
+
custom: CUSTOM_PREFIX,
|
|
26652
|
+
consent_category: CONSENT_CATEGORY_PREFIX,
|
|
26653
|
+
consent_app: CONSENT_APP_PREFIX,
|
|
26654
|
+
geo: GEO_PREFIX
|
|
26655
|
+
};
|
|
26656
|
+
var FANOUT_KINDS = ["pii", "custom", "consent_category", "consent_app"];
|
|
26657
|
+
var projectionFact = /* @__PURE__ */ __name((projectionKey) => {
|
|
26658
|
+
for (const kind2 of PROJECTION_KINDS) {
|
|
26659
|
+
const prefix = PROJECTION_PREFIX[kind2];
|
|
26660
|
+
if (projectionKey.startsWith(prefix)) {
|
|
26661
|
+
return { kind: kind2, scope: "", key: projectionKey.slice(prefix.length) };
|
|
26662
|
+
}
|
|
26663
|
+
}
|
|
26664
|
+
throw new Error(`Unknown projection key: ${projectionKey}`);
|
|
26665
|
+
}, "projectionFact");
|
|
26666
|
+
|
|
26667
|
+
// src/id-graph/anon/facts.ts
|
|
26668
|
+
var FACT_KINDS = [
|
|
26669
|
+
...PROJECTION_KINDS,
|
|
26670
|
+
"capture",
|
|
26671
|
+
"identifier",
|
|
26672
|
+
"conversion",
|
|
26673
|
+
"context_private_kv",
|
|
26674
|
+
"context_private_text",
|
|
26675
|
+
"context_shared_kv",
|
|
26676
|
+
"context_shared_text"
|
|
26677
|
+
];
|
|
26678
|
+
var APP_FACT_KINDS = ["custom", "capture", "identifier", "conversion", "pii", "consent_category", "consent_app", "geo"];
|
|
26679
|
+
var MAX_APP_FACTS_PER_PAGE = 100;
|
|
26680
|
+
var MAX_APP_FACT_PAGE_BYTES = 1024 * 1024;
|
|
26681
|
+
var COLUMNS = "kind, scope, key, value, source, type, amount, currency, event_id, data_json, first_at, observed_at, synced_at, expires_at";
|
|
26682
|
+
var rowOf = /* @__PURE__ */ __name((row) => {
|
|
26683
|
+
return {
|
|
26684
|
+
kind: row.kind,
|
|
26685
|
+
scope: row.scope,
|
|
26686
|
+
key: row.key,
|
|
26687
|
+
value: row.value,
|
|
26688
|
+
source: row.source,
|
|
26689
|
+
type: row.type,
|
|
26690
|
+
amount: row.amount,
|
|
26691
|
+
currency: row.currency,
|
|
26692
|
+
eventId: row.event_id,
|
|
26693
|
+
dataJson: row.data_json,
|
|
26694
|
+
firstAt: row.first_at,
|
|
26695
|
+
observedAt: row.observed_at,
|
|
26696
|
+
syncedAt: row.synced_at,
|
|
26697
|
+
expiresAt: row.expires_at
|
|
26698
|
+
};
|
|
26699
|
+
}, "rowOf");
|
|
26700
|
+
var appFactOf = /* @__PURE__ */ __name((row) => {
|
|
26701
|
+
const { syncedAt: _syncedAt, expiresAt: _expiresAt, ...fact } = rowOf(row);
|
|
26702
|
+
return { ...fact, kind: fact.kind };
|
|
26703
|
+
}, "appFactOf");
|
|
26704
|
+
var listAppFacts = /* @__PURE__ */ __name((sql, query, now) => {
|
|
26705
|
+
const maxRowId = query.maxRowId ?? sql.exec("SELECT MAX(rowid) AS maxRowId FROM facts").one().maxRowId;
|
|
26706
|
+
if (maxRowId === null) {
|
|
26707
|
+
return { items: [], next: null };
|
|
26708
|
+
}
|
|
26709
|
+
const where = ["rowid > ?", "rowid <= ?", "(expires_at IS NULL OR expires_at > ?)"];
|
|
26710
|
+
const params = [query.afterRowId, maxRowId, now];
|
|
26711
|
+
if (query.kinds.length > 0) {
|
|
26712
|
+
where.push(`kind IN (${query.kinds.map(() => "?").join(", ")})`);
|
|
26713
|
+
params.push(...query.kinds);
|
|
26714
|
+
}
|
|
26715
|
+
if (query.observedAtGte !== null) {
|
|
26716
|
+
where.push("observed_at >= ?");
|
|
26717
|
+
params.push(query.observedAtGte);
|
|
26718
|
+
}
|
|
26719
|
+
if (query.observedAtLt !== null) {
|
|
26720
|
+
where.push("observed_at < ?");
|
|
26721
|
+
params.push(query.observedAtLt);
|
|
26722
|
+
}
|
|
26723
|
+
const rows = sql.exec(`SELECT rowid, ${COLUMNS} FROM facts WHERE ${where.join(" AND ")} ORDER BY rowid LIMIT ?`, ...params, query.limit + 1).toArray();
|
|
26724
|
+
const items = [];
|
|
26725
|
+
let lastRowId = query.afterRowId;
|
|
26726
|
+
for (const row of rows.slice(0, query.limit)) {
|
|
26727
|
+
const fact = appFactOf(row);
|
|
26728
|
+
const candidate = [...items, fact];
|
|
26729
|
+
if (new TextEncoder().encode(JSON.stringify(candidate)).byteLength > MAX_APP_FACT_PAGE_BYTES) {
|
|
26730
|
+
if (items.length === 0) {
|
|
26731
|
+
throw new Error("fact_too_large");
|
|
26732
|
+
}
|
|
26733
|
+
break;
|
|
26734
|
+
}
|
|
26735
|
+
items.push(fact);
|
|
26736
|
+
lastRowId = row.rowid;
|
|
26737
|
+
}
|
|
26738
|
+
const hasMore = rows.length > items.length;
|
|
26739
|
+
return { items, next: hasMore ? { afterRowId: lastRowId, maxRowId } : null };
|
|
26740
|
+
}, "listAppFacts");
|
|
26741
|
+
var insertFact = /* @__PURE__ */ __name((sql, fact) => {
|
|
26742
|
+
sql.exec(
|
|
26743
|
+
`INSERT INTO facts (${COLUMNS}) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?)`,
|
|
26744
|
+
fact.kind,
|
|
26745
|
+
fact.scope,
|
|
26746
|
+
fact.key,
|
|
26747
|
+
fact.value ?? null,
|
|
26748
|
+
fact.source ?? null,
|
|
26749
|
+
fact.type ?? null,
|
|
26750
|
+
fact.amount ?? null,
|
|
26751
|
+
fact.currency ?? null,
|
|
26752
|
+
fact.eventId ?? null,
|
|
26753
|
+
fact.dataJson ?? null,
|
|
26754
|
+
fact.firstAt ?? null,
|
|
26755
|
+
fact.observedAt,
|
|
26756
|
+
fact.expiresAt ?? null
|
|
26757
|
+
);
|
|
26758
|
+
}, "insertFact");
|
|
26759
|
+
var latestFact = /* @__PURE__ */ __name((sql, coords) => {
|
|
26760
|
+
const row = sql.exec(
|
|
26761
|
+
`SELECT ${COLUMNS} FROM facts WHERE kind = ? AND scope = ? AND key = ? ORDER BY observed_at DESC, rowid DESC LIMIT 1`,
|
|
26762
|
+
coords.kind,
|
|
26763
|
+
coords.scope,
|
|
26764
|
+
coords.key
|
|
26765
|
+
).toArray().at(0);
|
|
26766
|
+
return row === void 0 ? void 0 : rowOf(row);
|
|
26767
|
+
}, "latestFact");
|
|
26768
|
+
var latestFacts = /* @__PURE__ */ __name((sql, kind2) => {
|
|
26769
|
+
const rows = sql.exec(`SELECT ${COLUMNS} FROM facts WHERE kind = ? ORDER BY scope, key, observed_at, rowid`, kind2).toArray();
|
|
26770
|
+
const latest = [];
|
|
26771
|
+
for (const row of rows) {
|
|
26772
|
+
const previous = latest.at(-1);
|
|
26773
|
+
if (previous !== void 0 && previous.scope === row.scope && previous.key === row.key) {
|
|
26774
|
+
latest[latest.length - 1] = rowOf(row);
|
|
26775
|
+
continue;
|
|
26776
|
+
}
|
|
26777
|
+
latest.push(rowOf(row));
|
|
26778
|
+
}
|
|
26779
|
+
return latest;
|
|
26780
|
+
}, "latestFacts");
|
|
26781
|
+
|
|
26498
26782
|
// src/apps/consent-gate.ts
|
|
26499
26783
|
var parseEnforced = /* @__PURE__ */ __name((raw2) => {
|
|
26500
26784
|
let parsed;
|
|
@@ -26577,6 +26861,9 @@ var appSchema = external_exports.object({
|
|
|
26577
26861
|
// Absent from an old embedded registry; the default never equals a real claim, so the ui proxy
|
|
26578
26862
|
// refuses stale registries with token_stale instead of turning the app off.
|
|
26579
26863
|
versionId: external_exports.string().min(1).default(""),
|
|
26864
|
+
// Absent from a registry deployed before cold rpc calls carried a query ticket; without it the
|
|
26865
|
+
// broker sends the callee none.
|
|
26866
|
+
appId: external_exports.string().min(1).optional(),
|
|
26580
26867
|
consent: external_exports.object({ category: external_exports.string() }),
|
|
26581
26868
|
// Plain strings, not the bucket enum: an unknown bucket from a newer registry must not turn apps off.
|
|
26582
26869
|
data: external_exports.array(external_exports.string()).default([]),
|
|
@@ -26663,210 +26950,6 @@ var siteRefOf = /* @__PURE__ */ __name(({ domainId, fingerprint }) => {
|
|
|
26663
26950
|
return { domainId, fingerprint };
|
|
26664
26951
|
}, "siteRefOf");
|
|
26665
26952
|
|
|
26666
|
-
// ../../libs/cf-data/src/cf-data.ts
|
|
26667
|
-
var CF_DATA_HEADER = "x-cf-data";
|
|
26668
|
-
var parseCfData = /* @__PURE__ */ __name((raw2) => {
|
|
26669
|
-
if (raw2 === null) {
|
|
26670
|
-
return void 0;
|
|
26671
|
-
}
|
|
26672
|
-
let parsed;
|
|
26673
|
-
try {
|
|
26674
|
-
parsed = JSON.parse(raw2);
|
|
26675
|
-
} catch {
|
|
26676
|
-
return void 0;
|
|
26677
|
-
}
|
|
26678
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
26679
|
-
return void 0;
|
|
26680
|
-
}
|
|
26681
|
-
return parsed;
|
|
26682
|
-
}, "parseCfData");
|
|
26683
|
-
|
|
26684
|
-
// src/helpers/geo.ts
|
|
26685
|
-
var UNDETERMINED_COUNTRIES = /* @__PURE__ */ new Set(["XX", "T1"]);
|
|
26686
|
-
var detectedCountry = /* @__PURE__ */ __name((cf) => {
|
|
26687
|
-
const country = cf?.country;
|
|
26688
|
-
return country && !UNDETERMINED_COUNTRIES.has(country) ? country : void 0;
|
|
26689
|
-
}, "detectedCountry");
|
|
26690
|
-
var euCountry = /* @__PURE__ */ __name((cf, country) => {
|
|
26691
|
-
if (cf?.isEUCountry === "1") {
|
|
26692
|
-
return "yes";
|
|
26693
|
-
}
|
|
26694
|
-
return country ? "no" : "unknown";
|
|
26695
|
-
}, "euCountry");
|
|
26696
|
-
var withForwardedCf = /* @__PURE__ */ __name((request) => {
|
|
26697
|
-
if (request.cf !== void 0) {
|
|
26698
|
-
return request;
|
|
26699
|
-
}
|
|
26700
|
-
const cf = parseCfData(request.headers.get(CF_DATA_HEADER));
|
|
26701
|
-
return cf === void 0 ? request : new Request(request, { cf });
|
|
26702
|
-
}, "withForwardedCf");
|
|
26703
|
-
var loggableGeo = /* @__PURE__ */ __name((geo) => {
|
|
26704
|
-
const fields = {};
|
|
26705
|
-
for (const [key, value] of Object.entries(geo)) {
|
|
26706
|
-
if (value !== void 0) {
|
|
26707
|
-
fields[key] = String(value);
|
|
26708
|
-
}
|
|
26709
|
-
}
|
|
26710
|
-
return fields;
|
|
26711
|
-
}, "loggableGeo");
|
|
26712
|
-
var hostFromRequest = /* @__PURE__ */ __name((request) => {
|
|
26713
|
-
return {
|
|
26714
|
-
ip: request.headers.get("cf-connecting-ip") ?? void 0,
|
|
26715
|
-
userAgent: request.headers.get("user-agent") ?? void 0,
|
|
26716
|
-
locale: request.headers.get("accept-language")?.split(",")[0]?.trim() || void 0
|
|
26717
|
-
};
|
|
26718
|
-
}, "hostFromRequest");
|
|
26719
|
-
var geoFromCf = /* @__PURE__ */ __name((cf) => {
|
|
26720
|
-
const bot = cf?.botManagement;
|
|
26721
|
-
const country = detectedCountry(cf);
|
|
26722
|
-
return {
|
|
26723
|
-
timezone: cf?.timezone ?? void 0,
|
|
26724
|
-
city: cf?.city ?? void 0,
|
|
26725
|
-
postalCode: cf?.postalCode ?? void 0,
|
|
26726
|
-
region: cf?.region ?? void 0,
|
|
26727
|
-
regionCode: cf?.regionCode ?? void 0,
|
|
26728
|
-
country,
|
|
26729
|
-
continent: cf?.continent ?? void 0,
|
|
26730
|
-
isEUCountry: euCountry(cf, country),
|
|
26731
|
-
latitude: cf?.latitude ?? void 0,
|
|
26732
|
-
longitude: cf?.longitude ?? void 0,
|
|
26733
|
-
ja4: bot?.ja4,
|
|
26734
|
-
ja3: bot?.ja3Hash,
|
|
26735
|
-
botScore: bot?.score
|
|
26736
|
-
};
|
|
26737
|
-
}, "geoFromCf");
|
|
26738
|
-
var geoFromRequest = /* @__PURE__ */ __name((request) => {
|
|
26739
|
-
return {
|
|
26740
|
-
...hostFromRequest(request),
|
|
26741
|
-
...geoFromCf(request.cf)
|
|
26742
|
-
};
|
|
26743
|
-
}, "geoFromRequest");
|
|
26744
|
-
|
|
26745
|
-
// src/helpers/durable.ts
|
|
26746
|
-
var ownGraphEnv = /* @__PURE__ */ __name((ctx, env) => {
|
|
26747
|
-
const exports = ctx.exports;
|
|
26748
|
-
return {
|
|
26749
|
-
...env,
|
|
26750
|
-
get ANON_DO() {
|
|
26751
|
-
return exports.AnonDO;
|
|
26752
|
-
},
|
|
26753
|
-
get GLOBAL_DO() {
|
|
26754
|
-
return exports.GlobalDO;
|
|
26755
|
-
},
|
|
26756
|
-
get ALIAS_DO() {
|
|
26757
|
-
return exports.AliasDO;
|
|
26758
|
-
}
|
|
26759
|
-
};
|
|
26760
|
-
}, "ownGraphEnv");
|
|
26761
|
-
var JURISDICTIONS = /* @__PURE__ */ new Set(["eu", "fedramp", "fedramp-high", "us"]);
|
|
26762
|
-
var LOCATION_HINTS = /* @__PURE__ */ new Set([
|
|
26763
|
-
"wnam",
|
|
26764
|
-
"enam",
|
|
26765
|
-
"sam",
|
|
26766
|
-
"weur",
|
|
26767
|
-
"eeur",
|
|
26768
|
-
"apac",
|
|
26769
|
-
"apac-ne",
|
|
26770
|
-
"apac-se",
|
|
26771
|
-
"oc",
|
|
26772
|
-
"afr",
|
|
26773
|
-
"me"
|
|
26774
|
-
]);
|
|
26775
|
-
var residencyStub = /* @__PURE__ */ __name((namespace, env, name) => {
|
|
26776
|
-
const residency = env;
|
|
26777
|
-
const jurisdiction = residency.DO_JURISDICTION;
|
|
26778
|
-
const scoped = jurisdiction !== void 0 && JURISDICTIONS.has(jurisdiction) ? namespace.jurisdiction(jurisdiction) : namespace;
|
|
26779
|
-
const locationHint = residency.LOCATION_HINT;
|
|
26780
|
-
const options = locationHint !== void 0 && LOCATION_HINTS.has(locationHint) ? { locationHint } : void 0;
|
|
26781
|
-
return scoped.get(scoped.idFromName(name), options);
|
|
26782
|
-
}, "residencyStub");
|
|
26783
|
-
|
|
26784
|
-
// src/id-graph/anon/client.ts
|
|
26785
|
-
var anonStub = /* @__PURE__ */ __name((env, anonId) => {
|
|
26786
|
-
return residencyStub(env.ANON_DO, env, anonId);
|
|
26787
|
-
}, "anonStub");
|
|
26788
|
-
|
|
26789
|
-
// src/id-graph/consent.ts
|
|
26790
|
-
var CONSENT_CATEGORIES2 = ["necessary", "functional", "analytics", "marketing", "salesOfData"];
|
|
26791
|
-
var CONSENT_CATEGORY_PREFIX = "consent.category.";
|
|
26792
|
-
var CONSENT_APP_PREFIX = "consent.app.";
|
|
26793
|
-
var consentEntries = /* @__PURE__ */ __name((payload, observedAt) => {
|
|
26794
|
-
const entries = [];
|
|
26795
|
-
for (const category of CONSENT_CATEGORIES2) {
|
|
26796
|
-
const granted = payload.categories[category];
|
|
26797
|
-
if (granted === void 0) {
|
|
26798
|
-
continue;
|
|
26799
|
-
}
|
|
26800
|
-
entries.push({ key: `${CONSENT_CATEGORY_PREFIX}${category}`, value: String(granted), observedAt });
|
|
26801
|
-
}
|
|
26802
|
-
for (const [appId, granted] of Object.entries(payload.apps)) {
|
|
26803
|
-
entries.push({ key: `${CONSENT_APP_PREFIX}${appId}`, value: String(granted), observedAt });
|
|
26804
|
-
}
|
|
26805
|
-
return entries;
|
|
26806
|
-
}, "consentEntries");
|
|
26807
|
-
|
|
26808
|
-
// src/id-graph/traits.ts
|
|
26809
|
-
var PII_KEYS = ["first_name", "last_name", "email", "phone", "country", "state", "city", "postal_code"];
|
|
26810
|
-
var snakeCase = /* @__PURE__ */ __name((key) => {
|
|
26811
|
-
return key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
|
|
26812
|
-
}, "snakeCase");
|
|
26813
|
-
var stringifyTrait = /* @__PURE__ */ __name((value) => {
|
|
26814
|
-
return typeof value === "string" ? value : JSON.stringify(value);
|
|
26815
|
-
}, "stringifyTrait");
|
|
26816
|
-
var piiTrait = /* @__PURE__ */ __name((key, rawValue) => {
|
|
26817
|
-
if (key !== "email" && key !== "phone") {
|
|
26818
|
-
return stringifyTrait(rawValue);
|
|
26819
|
-
}
|
|
26820
|
-
if (typeof rawValue !== "string") {
|
|
26821
|
-
return void 0;
|
|
26822
|
-
}
|
|
26823
|
-
return (key === "email" ? emailIdentifier(rawValue) : phoneIdentifier(rawValue))?.value;
|
|
26824
|
-
}, "piiTrait");
|
|
26825
|
-
var resolvePiiTrait = /* @__PURE__ */ __name((key, rawValue, unset) => {
|
|
26826
|
-
return unset ? null : piiTrait(key, rawValue);
|
|
26827
|
-
}, "resolvePiiTrait");
|
|
26828
|
-
var classifyTraits = /* @__PURE__ */ __name((traits, observedAt) => {
|
|
26829
|
-
const classified = { pii: {}, custom: {}, latest: [], dropped: [] };
|
|
26830
|
-
for (const [rawKey, rawValue] of Object.entries(traits)) {
|
|
26831
|
-
const key = snakeCase(rawKey);
|
|
26832
|
-
const unset = rawValue === null || rawValue === void 0;
|
|
26833
|
-
if (PII_KEYS.includes(key)) {
|
|
26834
|
-
const value2 = resolvePiiTrait(key, rawValue, unset);
|
|
26835
|
-
if (value2 === void 0) {
|
|
26836
|
-
classified.dropped.push(key);
|
|
26837
|
-
continue;
|
|
26838
|
-
}
|
|
26839
|
-
classified.pii[key] = value2;
|
|
26840
|
-
classified.latest.push({ key: `pii.${key}`, value: value2, observedAt });
|
|
26841
|
-
continue;
|
|
26842
|
-
}
|
|
26843
|
-
const value = unset ? null : stringifyTrait(rawValue);
|
|
26844
|
-
classified.custom[key] = value;
|
|
26845
|
-
classified.latest.push({ key: `custom.${key}`, value, observedAt });
|
|
26846
|
-
}
|
|
26847
|
-
return classified;
|
|
26848
|
-
}, "classifyTraits");
|
|
26849
|
-
var conversionFromTrack = /* @__PURE__ */ __name((payload) => {
|
|
26850
|
-
const amount = payload.properties["value"];
|
|
26851
|
-
if (typeof amount !== "number" || !Number.isFinite(amount)) {
|
|
26852
|
-
return void 0;
|
|
26853
|
-
}
|
|
26854
|
-
const orderId = payload.properties["orderId"];
|
|
26855
|
-
if (typeof orderId !== "string" || orderId === "") {
|
|
26856
|
-
return void 0;
|
|
26857
|
-
}
|
|
26858
|
-
const currency = payload.properties["currency"];
|
|
26859
|
-
const contents = payload.properties["contents"];
|
|
26860
|
-
return {
|
|
26861
|
-
eventName: payload.event,
|
|
26862
|
-
eventId: payload.eventId,
|
|
26863
|
-
orderId,
|
|
26864
|
-
amount,
|
|
26865
|
-
currency: typeof currency === "string" ? currency : null,
|
|
26866
|
-
contents: contents === void 0 ? null : JSON.stringify(contents)
|
|
26867
|
-
};
|
|
26868
|
-
}, "conversionFromTrack");
|
|
26869
|
-
|
|
26870
26953
|
// src/id-graph/alias/client.ts
|
|
26871
26954
|
var sha256Hex = /* @__PURE__ */ __name(async (value) => {
|
|
26872
26955
|
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(value));
|
|
@@ -26902,291 +26985,6 @@ var claimPair = /* @__PURE__ */ __name(async (env, identifier, globalId, anonId)
|
|
|
26902
26985
|
});
|
|
26903
26986
|
}, "claimPair");
|
|
26904
26987
|
|
|
26905
|
-
// src/id-graph/user.ts
|
|
26906
|
-
var EDGETAG_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}-[0-9]{13}$/;
|
|
26907
|
-
var isEdgeTagId = /* @__PURE__ */ __name((id) => {
|
|
26908
|
-
return EDGETAG_ID.test(id);
|
|
26909
|
-
}, "isEdgeTagId");
|
|
26910
|
-
var isCanonicalId = /* @__PURE__ */ __name((id) => {
|
|
26911
|
-
return id.startsWith("anon_") || isEdgeTagId(id);
|
|
26912
|
-
}, "isCanonicalId");
|
|
26913
|
-
var generateUserId = /* @__PURE__ */ __name(() => {
|
|
26914
|
-
return `anon_${crypto.randomUUID()}`;
|
|
26915
|
-
}, "generateUserId");
|
|
26916
|
-
var restoreUser = /* @__PURE__ */ __name(async (env, cookie, candidate) => {
|
|
26917
|
-
if (cookie === void 0) {
|
|
26918
|
-
return candidate;
|
|
26919
|
-
}
|
|
26920
|
-
const assignment = await claimFallbackAnon(env, cookie, candidate);
|
|
26921
|
-
setSpanAttributes({ "qinto.fallback_restored": assignment.anonId !== candidate });
|
|
26922
|
-
return assignment.anonId;
|
|
26923
|
-
}, "restoreUser");
|
|
26924
|
-
var resolveUser = /* @__PURE__ */ __name(async (env, id) => {
|
|
26925
|
-
if (isCanonicalId(id)) {
|
|
26926
|
-
return { id };
|
|
26927
|
-
}
|
|
26928
|
-
const identifier = classifyIdentifier(id);
|
|
26929
|
-
const assignment = await claimAnon(env, identifier, generateUserId());
|
|
26930
|
-
return { id: assignment.anonId, identifier };
|
|
26931
|
-
}, "resolveUser");
|
|
26932
|
-
var resolveExistingUser = /* @__PURE__ */ __name(async (env, id) => {
|
|
26933
|
-
if (isCanonicalId(id)) {
|
|
26934
|
-
return { id };
|
|
26935
|
-
}
|
|
26936
|
-
const identifier = classifyIdentifier(id);
|
|
26937
|
-
const anonId = (await getAssignment(env, identifier))?.anonId ?? null;
|
|
26938
|
-
return anonId === null ? null : { id: anonId, identifier };
|
|
26939
|
-
}, "resolveExistingUser");
|
|
26940
|
-
var lookupUser = /* @__PURE__ */ __name(async (env, id) => {
|
|
26941
|
-
if (isCanonicalId(id)) {
|
|
26942
|
-
const globalId = await traced("rpc anon.get_global_id", {}, () => {
|
|
26943
|
-
return anonStub(env, id).getGlobalId(currentTraceContext());
|
|
26944
|
-
});
|
|
26945
|
-
return { anonId: id, globalId };
|
|
26946
|
-
}
|
|
26947
|
-
const assignment = await getAssignment(env, classifyIdentifier(id));
|
|
26948
|
-
return { anonId: assignment?.anonId ?? null, globalId: assignment?.globalId ?? null };
|
|
26949
|
-
}, "lookupUser");
|
|
26950
|
-
|
|
26951
|
-
// src/id-graph/noop.ts
|
|
26952
|
-
var isNoopConsent = /* @__PURE__ */ __name((payload) => {
|
|
26953
|
-
return consentEntries(payload, 0).length === 0;
|
|
26954
|
-
}, "isNoopConsent");
|
|
26955
|
-
var isNoopIdentify = /* @__PURE__ */ __name((traits) => {
|
|
26956
|
-
return Object.keys(traits).length === 0;
|
|
26957
|
-
}, "isNoopIdentify");
|
|
26958
|
-
var isNoopSubmission = /* @__PURE__ */ __name((userId, args) => {
|
|
26959
|
-
if (args.type === "consent") {
|
|
26960
|
-
return isNoopConsent(args.payload);
|
|
26961
|
-
}
|
|
26962
|
-
if (args.type === "identify") {
|
|
26963
|
-
return isCanonicalId(userId) && isNoopIdentify(args.payload.traits);
|
|
26964
|
-
}
|
|
26965
|
-
return false;
|
|
26966
|
-
}, "isNoopSubmission");
|
|
26967
|
-
var isNoopAction = /* @__PURE__ */ __name((action) => {
|
|
26968
|
-
if (action.identifier !== void 0) {
|
|
26969
|
-
return false;
|
|
26970
|
-
}
|
|
26971
|
-
return action.type === "consent" && isNoopConsent(action.payload);
|
|
26972
|
-
}, "isNoopAction");
|
|
26973
|
-
var isDeliveryOnlyAction = /* @__PURE__ */ __name((action) => {
|
|
26974
|
-
if (action.identifier !== void 0 || action.type === "consent") {
|
|
26975
|
-
return false;
|
|
26976
|
-
}
|
|
26977
|
-
return action.type === "track" ? conversionFromTrack(action.payload) === void 0 : isNoopIdentify(action.payload.traits);
|
|
26978
|
-
}, "isDeliveryOnlyAction");
|
|
26979
|
-
|
|
26980
|
-
// src/id-graph/action.ts
|
|
26981
|
-
var UnknownUserError = class extends Error {
|
|
26982
|
-
static {
|
|
26983
|
-
__name(this, "UnknownUserError");
|
|
26984
|
-
}
|
|
26985
|
-
constructor() {
|
|
26986
|
-
super("the identifier resolves to no user");
|
|
26987
|
-
this.name = "UnknownUserError";
|
|
26988
|
-
}
|
|
26989
|
-
};
|
|
26990
|
-
var mintActionId = /* @__PURE__ */ __name(() => {
|
|
26991
|
-
return `act_${ulid3()}`;
|
|
26992
|
-
}, "mintActionId");
|
|
26993
|
-
var buildAction = /* @__PURE__ */ __name((args) => {
|
|
26994
|
-
const receivedAt = args.receivedAt ?? Date.now();
|
|
26995
|
-
return {
|
|
26996
|
-
...args,
|
|
26997
|
-
actionId: mintActionId(),
|
|
26998
|
-
receivedAt
|
|
26999
|
-
};
|
|
27000
|
-
}, "buildAction");
|
|
27001
|
-
var ingest = /* @__PURE__ */ __name(async (env, action, site = ownSite(env)) => {
|
|
27002
|
-
await traced("rpc anon.ingest", {}, () => {
|
|
27003
|
-
return anonStub(env, action.userId).ingest(action, site, currentTraceContext());
|
|
27004
|
-
});
|
|
27005
|
-
}, "ingest");
|
|
27006
|
-
var serverActionContext = /* @__PURE__ */ __name((input2, receivedAt) => {
|
|
27007
|
-
const network = input2?.network;
|
|
27008
|
-
return {
|
|
27009
|
-
source: "server",
|
|
27010
|
-
occurredAt: input2?.occurredAt ?? receivedAt,
|
|
27011
|
-
...input2?.page === void 0 ? {} : { page: input2.page },
|
|
27012
|
-
...network === void 0 || network.ip === void 0 && network.userAgent === void 0 && network.locale === void 0 ? {} : { network: { ...network, provenance: "caller" } },
|
|
27013
|
-
...input2?.session === void 0 ? {} : { session: input2.session },
|
|
27014
|
-
offline: input2?.offline ?? false,
|
|
27015
|
-
replay: input2?.replay ?? false
|
|
27016
|
-
};
|
|
27017
|
-
}, "serverActionContext");
|
|
27018
|
-
var submitAction = /* @__PURE__ */ __name(async (env, userId, args, request) => {
|
|
27019
|
-
const attrs = {
|
|
27020
|
-
"action.type": args.type,
|
|
27021
|
-
"action.payload": JSON.stringify(args.payload),
|
|
27022
|
-
"action.identifier": userId,
|
|
27023
|
-
...args.type === "track" ? { "action.event": args.payload.event } : {}
|
|
27024
|
-
};
|
|
27025
|
-
return traced(`action ${args.type}`, { attrs }, async () => {
|
|
27026
|
-
if (isNoopSubmission(userId, args)) {
|
|
27027
|
-
setSpanAttributes({ "action.noop": true, ...isCanonicalId(userId) ? { "action.user_id": userId } : {} });
|
|
27028
|
-
return mintActionId();
|
|
27029
|
-
}
|
|
27030
|
-
const resolved = await traced("resolve_user", {}, () => {
|
|
27031
|
-
return args.type === "track" ? resolveExistingUser(env, userId) : resolveUser(env, userId);
|
|
27032
|
-
});
|
|
27033
|
-
if (resolved === null) {
|
|
27034
|
-
throw new UnknownUserError();
|
|
27035
|
-
}
|
|
27036
|
-
const receivedAt = Date.now();
|
|
27037
|
-
const action = buildAction({
|
|
27038
|
-
...args,
|
|
27039
|
-
userId: resolved.id,
|
|
27040
|
-
identifier: resolved.identifier,
|
|
27041
|
-
receivedAt,
|
|
27042
|
-
context: serverActionContext(args.context, receivedAt),
|
|
27043
|
-
geo: request?.cf,
|
|
27044
|
-
host: request === void 0 ? void 0 : hostFromRequest(request)
|
|
27045
|
-
});
|
|
27046
|
-
setSpanAttributes({ "action.user_id": resolved.id, "action.id": action.actionId });
|
|
27047
|
-
await ingest(env, action);
|
|
27048
|
-
return action.actionId;
|
|
27049
|
-
});
|
|
27050
|
-
}, "submitAction");
|
|
27051
|
-
|
|
27052
|
-
// src/id-graph/projections.ts
|
|
27053
|
-
var PII_PREFIX = "pii.";
|
|
27054
|
-
var CUSTOM_PREFIX = "custom.";
|
|
27055
|
-
var GEO_PREFIX = "geo.";
|
|
27056
|
-
var PROJECTION_KINDS = ["pii", "custom", "consent_category", "consent_app", "geo"];
|
|
27057
|
-
var PROJECTION_PREFIX = {
|
|
27058
|
-
pii: PII_PREFIX,
|
|
27059
|
-
custom: CUSTOM_PREFIX,
|
|
27060
|
-
consent_category: CONSENT_CATEGORY_PREFIX,
|
|
27061
|
-
consent_app: CONSENT_APP_PREFIX,
|
|
27062
|
-
geo: GEO_PREFIX
|
|
27063
|
-
};
|
|
27064
|
-
var FANOUT_KINDS = ["pii", "custom", "consent_category", "consent_app"];
|
|
27065
|
-
var projectionFact = /* @__PURE__ */ __name((projectionKey) => {
|
|
27066
|
-
for (const kind2 of PROJECTION_KINDS) {
|
|
27067
|
-
const prefix = PROJECTION_PREFIX[kind2];
|
|
27068
|
-
if (projectionKey.startsWith(prefix)) {
|
|
27069
|
-
return { kind: kind2, scope: "", key: projectionKey.slice(prefix.length) };
|
|
27070
|
-
}
|
|
27071
|
-
}
|
|
27072
|
-
throw new Error(`Unknown projection key: ${projectionKey}`);
|
|
27073
|
-
}, "projectionFact");
|
|
27074
|
-
|
|
27075
|
-
// src/id-graph/anon/facts.ts
|
|
27076
|
-
var FACT_KINDS = [
|
|
27077
|
-
...PROJECTION_KINDS,
|
|
27078
|
-
"capture",
|
|
27079
|
-
"identifier",
|
|
27080
|
-
"conversion",
|
|
27081
|
-
"context_private_kv",
|
|
27082
|
-
"context_private_text",
|
|
27083
|
-
"context_shared_kv",
|
|
27084
|
-
"context_shared_text"
|
|
27085
|
-
];
|
|
27086
|
-
var APP_FACT_KINDS = ["custom", "capture", "identifier", "conversion", "pii", "consent_category", "consent_app", "geo"];
|
|
27087
|
-
var MAX_APP_FACTS_PER_PAGE = 100;
|
|
27088
|
-
var MAX_APP_FACT_PAGE_BYTES = 1024 * 1024;
|
|
27089
|
-
var COLUMNS = "kind, scope, key, value, source, type, amount, currency, event_id, data_json, first_at, observed_at, synced_at, expires_at";
|
|
27090
|
-
var rowOf = /* @__PURE__ */ __name((row) => {
|
|
27091
|
-
return {
|
|
27092
|
-
kind: row.kind,
|
|
27093
|
-
scope: row.scope,
|
|
27094
|
-
key: row.key,
|
|
27095
|
-
value: row.value,
|
|
27096
|
-
source: row.source,
|
|
27097
|
-
type: row.type,
|
|
27098
|
-
amount: row.amount,
|
|
27099
|
-
currency: row.currency,
|
|
27100
|
-
eventId: row.event_id,
|
|
27101
|
-
dataJson: row.data_json,
|
|
27102
|
-
firstAt: row.first_at,
|
|
27103
|
-
observedAt: row.observed_at,
|
|
27104
|
-
syncedAt: row.synced_at,
|
|
27105
|
-
expiresAt: row.expires_at
|
|
27106
|
-
};
|
|
27107
|
-
}, "rowOf");
|
|
27108
|
-
var appFactOf = /* @__PURE__ */ __name((row) => {
|
|
27109
|
-
const { syncedAt: _syncedAt, expiresAt: _expiresAt, ...fact } = rowOf(row);
|
|
27110
|
-
return { ...fact, kind: fact.kind };
|
|
27111
|
-
}, "appFactOf");
|
|
27112
|
-
var listAppFacts = /* @__PURE__ */ __name((sql, query, now) => {
|
|
27113
|
-
const maxRowId = query.maxRowId ?? sql.exec("SELECT MAX(rowid) AS maxRowId FROM facts").one().maxRowId;
|
|
27114
|
-
if (maxRowId === null) {
|
|
27115
|
-
return { items: [], next: null };
|
|
27116
|
-
}
|
|
27117
|
-
const where = ["rowid > ?", "rowid <= ?", "(expires_at IS NULL OR expires_at > ?)"];
|
|
27118
|
-
const params = [query.afterRowId, maxRowId, now];
|
|
27119
|
-
if (query.kinds.length > 0) {
|
|
27120
|
-
where.push(`kind IN (${query.kinds.map(() => "?").join(", ")})`);
|
|
27121
|
-
params.push(...query.kinds);
|
|
27122
|
-
}
|
|
27123
|
-
if (query.observedAtGte !== null) {
|
|
27124
|
-
where.push("observed_at >= ?");
|
|
27125
|
-
params.push(query.observedAtGte);
|
|
27126
|
-
}
|
|
27127
|
-
if (query.observedAtLt !== null) {
|
|
27128
|
-
where.push("observed_at < ?");
|
|
27129
|
-
params.push(query.observedAtLt);
|
|
27130
|
-
}
|
|
27131
|
-
const rows = sql.exec(`SELECT rowid, ${COLUMNS} FROM facts WHERE ${where.join(" AND ")} ORDER BY rowid LIMIT ?`, ...params, query.limit + 1).toArray();
|
|
27132
|
-
const items = [];
|
|
27133
|
-
let lastRowId = query.afterRowId;
|
|
27134
|
-
for (const row of rows.slice(0, query.limit)) {
|
|
27135
|
-
const fact = appFactOf(row);
|
|
27136
|
-
const candidate = [...items, fact];
|
|
27137
|
-
if (new TextEncoder().encode(JSON.stringify(candidate)).byteLength > MAX_APP_FACT_PAGE_BYTES) {
|
|
27138
|
-
if (items.length === 0) {
|
|
27139
|
-
throw new Error("fact_too_large");
|
|
27140
|
-
}
|
|
27141
|
-
break;
|
|
27142
|
-
}
|
|
27143
|
-
items.push(fact);
|
|
27144
|
-
lastRowId = row.rowid;
|
|
27145
|
-
}
|
|
27146
|
-
const hasMore = rows.length > items.length;
|
|
27147
|
-
return { items, next: hasMore ? { afterRowId: lastRowId, maxRowId } : null };
|
|
27148
|
-
}, "listAppFacts");
|
|
27149
|
-
var insertFact = /* @__PURE__ */ __name((sql, fact) => {
|
|
27150
|
-
sql.exec(
|
|
27151
|
-
`INSERT INTO facts (${COLUMNS}) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?)`,
|
|
27152
|
-
fact.kind,
|
|
27153
|
-
fact.scope,
|
|
27154
|
-
fact.key,
|
|
27155
|
-
fact.value ?? null,
|
|
27156
|
-
fact.source ?? null,
|
|
27157
|
-
fact.type ?? null,
|
|
27158
|
-
fact.amount ?? null,
|
|
27159
|
-
fact.currency ?? null,
|
|
27160
|
-
fact.eventId ?? null,
|
|
27161
|
-
fact.dataJson ?? null,
|
|
27162
|
-
fact.firstAt ?? null,
|
|
27163
|
-
fact.observedAt,
|
|
27164
|
-
fact.expiresAt ?? null
|
|
27165
|
-
);
|
|
27166
|
-
}, "insertFact");
|
|
27167
|
-
var latestFact = /* @__PURE__ */ __name((sql, coords) => {
|
|
27168
|
-
const row = sql.exec(
|
|
27169
|
-
`SELECT ${COLUMNS} FROM facts WHERE kind = ? AND scope = ? AND key = ? ORDER BY observed_at DESC, rowid DESC LIMIT 1`,
|
|
27170
|
-
coords.kind,
|
|
27171
|
-
coords.scope,
|
|
27172
|
-
coords.key
|
|
27173
|
-
).toArray().at(0);
|
|
27174
|
-
return row === void 0 ? void 0 : rowOf(row);
|
|
27175
|
-
}, "latestFact");
|
|
27176
|
-
var latestFacts = /* @__PURE__ */ __name((sql, kind2) => {
|
|
27177
|
-
const rows = sql.exec(`SELECT ${COLUMNS} FROM facts WHERE kind = ? ORDER BY scope, key, observed_at, rowid`, kind2).toArray();
|
|
27178
|
-
const latest = [];
|
|
27179
|
-
for (const row of rows) {
|
|
27180
|
-
const previous = latest.at(-1);
|
|
27181
|
-
if (previous !== void 0 && previous.scope === row.scope && previous.key === row.key) {
|
|
27182
|
-
latest[latest.length - 1] = rowOf(row);
|
|
27183
|
-
continue;
|
|
27184
|
-
}
|
|
27185
|
-
latest.push(rowOf(row));
|
|
27186
|
-
}
|
|
27187
|
-
return latest;
|
|
27188
|
-
}, "latestFacts");
|
|
27189
|
-
|
|
27190
26988
|
// src/id-graph/global/client.ts
|
|
27191
26989
|
var globalStub = /* @__PURE__ */ __name((env, globalId) => {
|
|
27192
26990
|
return residencyStub(env.GLOBAL_DO, env, globalId);
|
|
@@ -27255,14 +27053,60 @@ var linkedAnonsPage = /* @__PURE__ */ __name((sql, afterRowId, limit) => {
|
|
|
27255
27053
|
anonIds.push(row.key);
|
|
27256
27054
|
lastRowId = row.rowid;
|
|
27257
27055
|
}
|
|
27258
|
-
return { anonIds, next: hasMore ? lastRowId : null };
|
|
27259
|
-
}, "linkedAnonsPage");
|
|
27260
|
-
var linkedAnonsExcept = /* @__PURE__ */ __name((sql, originAnonId) => {
|
|
27261
|
-
return sql.exec("SELECT key FROM facts WHERE kind = 'anon' AND key != ?", originAnonId).toArray().map((row) => row.key);
|
|
27262
|
-
}, "linkedAnonsExcept");
|
|
27263
|
-
var allLinkedAnons = /* @__PURE__ */ __name((sql) => {
|
|
27264
|
-
return sql.exec("SELECT key FROM facts WHERE kind = 'anon' ORDER BY rowid").toArray().map((row) => row.key);
|
|
27265
|
-
}, "allLinkedAnons");
|
|
27056
|
+
return { anonIds, next: hasMore ? lastRowId : null };
|
|
27057
|
+
}, "linkedAnonsPage");
|
|
27058
|
+
var linkedAnonsExcept = /* @__PURE__ */ __name((sql, originAnonId) => {
|
|
27059
|
+
return sql.exec("SELECT key FROM facts WHERE kind = 'anon' AND key != ?", originAnonId).toArray().map((row) => row.key);
|
|
27060
|
+
}, "linkedAnonsExcept");
|
|
27061
|
+
var allLinkedAnons = /* @__PURE__ */ __name((sql) => {
|
|
27062
|
+
return sql.exec("SELECT key FROM facts WHERE kind = 'anon' ORDER BY rowid").toArray().map((row) => row.key);
|
|
27063
|
+
}, "allLinkedAnons");
|
|
27064
|
+
|
|
27065
|
+
// src/id-graph/user.ts
|
|
27066
|
+
var EDGETAG_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}-[0-9]{13}$/;
|
|
27067
|
+
var isEdgeTagId = /* @__PURE__ */ __name((id) => {
|
|
27068
|
+
return EDGETAG_ID.test(id);
|
|
27069
|
+
}, "isEdgeTagId");
|
|
27070
|
+
var isCanonicalId = /* @__PURE__ */ __name((id) => {
|
|
27071
|
+
return id.startsWith("anon_") || isEdgeTagId(id);
|
|
27072
|
+
}, "isCanonicalId");
|
|
27073
|
+
var generateUserId = /* @__PURE__ */ __name(() => {
|
|
27074
|
+
return `anon_${crypto.randomUUID()}`;
|
|
27075
|
+
}, "generateUserId");
|
|
27076
|
+
var restoreUser = /* @__PURE__ */ __name(async (env, cookie, candidate) => {
|
|
27077
|
+
if (cookie === void 0) {
|
|
27078
|
+
return candidate;
|
|
27079
|
+
}
|
|
27080
|
+
const assignment = await claimFallbackAnon(env, cookie, candidate);
|
|
27081
|
+
setSpanAttributes({ "qinto.fallback_restored": assignment.anonId !== candidate });
|
|
27082
|
+
return assignment.anonId;
|
|
27083
|
+
}, "restoreUser");
|
|
27084
|
+
var resolveUser = /* @__PURE__ */ __name(async (env, id) => {
|
|
27085
|
+
if (isCanonicalId(id)) {
|
|
27086
|
+
return { id };
|
|
27087
|
+
}
|
|
27088
|
+
const identifier = classifyIdentifier(id);
|
|
27089
|
+
const assignment = await claimAnon(env, identifier, generateUserId());
|
|
27090
|
+
return { id: assignment.anonId, identifier };
|
|
27091
|
+
}, "resolveUser");
|
|
27092
|
+
var resolveExistingUser = /* @__PURE__ */ __name(async (env, id) => {
|
|
27093
|
+
if (isCanonicalId(id)) {
|
|
27094
|
+
return { id };
|
|
27095
|
+
}
|
|
27096
|
+
const identifier = classifyIdentifier(id);
|
|
27097
|
+
const anonId = (await getAssignment(env, identifier))?.anonId ?? null;
|
|
27098
|
+
return anonId === null ? null : { id: anonId, identifier };
|
|
27099
|
+
}, "resolveExistingUser");
|
|
27100
|
+
var lookupUser = /* @__PURE__ */ __name(async (env, id) => {
|
|
27101
|
+
if (isCanonicalId(id)) {
|
|
27102
|
+
const globalId = await traced("rpc anon.get_global_id", {}, () => {
|
|
27103
|
+
return anonStub(env, id).getGlobalId(currentTraceContext());
|
|
27104
|
+
});
|
|
27105
|
+
return { anonId: id, globalId };
|
|
27106
|
+
}
|
|
27107
|
+
const assignment = await getAssignment(env, classifyIdentifier(id));
|
|
27108
|
+
return { anonId: assignment?.anonId ?? null, globalId: assignment?.globalId ?? null };
|
|
27109
|
+
}, "lookupUser");
|
|
27266
27110
|
|
|
27267
27111
|
// src/id-graph/person.ts
|
|
27268
27112
|
var IDENTITY_CAPABILITY = "identity";
|
|
@@ -27377,86 +27221,348 @@ var parseCurrencyFeed = /* @__PURE__ */ __name((feed) => {
|
|
|
27377
27221
|
if (rates === null) {
|
|
27378
27222
|
return { ok: false, reason: "the rates literal does not parse" };
|
|
27379
27223
|
}
|
|
27380
|
-
if (rates["USD"] !== 1) {
|
|
27381
|
-
return { ok: false, reason: "USD is not 1" };
|
|
27224
|
+
if (rates["USD"] !== 1) {
|
|
27225
|
+
return { ok: false, reason: "USD is not 1" };
|
|
27226
|
+
}
|
|
27227
|
+
const invalid = Object.entries(rates).find(([, rate]) => !Number.isFinite(rate) || rate <= 0);
|
|
27228
|
+
if (invalid !== void 0) {
|
|
27229
|
+
return { ok: false, reason: `${invalid[0]} is not a positive finite rate` };
|
|
27230
|
+
}
|
|
27231
|
+
const count = Object.keys(rates).length;
|
|
27232
|
+
if (count < MIN_CURRENCY_CODES) {
|
|
27233
|
+
return { ok: false, reason: `the feed has ${count} codes` };
|
|
27234
|
+
}
|
|
27235
|
+
return { ok: true, rates };
|
|
27236
|
+
}, "parseCurrencyFeed");
|
|
27237
|
+
var fetchCurrencyRates = /* @__PURE__ */ __name(async () => {
|
|
27238
|
+
return traced("currency.fetch", { attrs: { "http.url": CURRENCY_FEED_URL } }, async () => {
|
|
27239
|
+
let feed;
|
|
27240
|
+
try {
|
|
27241
|
+
const response = await fetch(CURRENCY_FEED_URL, {
|
|
27242
|
+
signal: AbortSignal.timeout(FEED_TIMEOUT_MS),
|
|
27243
|
+
cf: { cacheTtlByStatus: { "200-299": FEED_EDGE_CACHE_TTL_S, "300-599": 0 } }
|
|
27244
|
+
});
|
|
27245
|
+
if (!response.ok) {
|
|
27246
|
+
addSpanEvent("currency fetch failed", { level: "error", attrs: { status: response.status } });
|
|
27247
|
+
return null;
|
|
27248
|
+
}
|
|
27249
|
+
feed = await response.text();
|
|
27250
|
+
} catch (error61) {
|
|
27251
|
+
addSpanEvent("currency fetch failed", { level: "error", attrs: { error: String(error61) } });
|
|
27252
|
+
return null;
|
|
27253
|
+
}
|
|
27254
|
+
const verdict = parseCurrencyFeed(feed);
|
|
27255
|
+
if (!verdict.ok) {
|
|
27256
|
+
addSpanEvent("currency feed rejected", { level: "error", attrs: { reason: verdict.reason } });
|
|
27257
|
+
return null;
|
|
27258
|
+
}
|
|
27259
|
+
return verdict.rates;
|
|
27260
|
+
});
|
|
27261
|
+
}, "fetchCurrencyRates");
|
|
27262
|
+
var refreshCurrencyRates = /* @__PURE__ */ __name(async () => {
|
|
27263
|
+
const rates = await fetchCurrencyRates();
|
|
27264
|
+
const now = Date.now();
|
|
27265
|
+
if (rates === null) {
|
|
27266
|
+
const current2 = currencyMemo?.current ?? null;
|
|
27267
|
+
currencyMemo = { current: current2, nextAttemptAt: now + RETRY_AFTER_FAILURE_MS };
|
|
27268
|
+
return current2;
|
|
27269
|
+
}
|
|
27270
|
+
const current = { rates, fetchedAt: now };
|
|
27271
|
+
currencyMemo = { current, nextAttemptAt: now + RATES_TTL_MS };
|
|
27272
|
+
return current;
|
|
27273
|
+
}, "refreshCurrencyRates");
|
|
27274
|
+
var currentCurrencyRates = /* @__PURE__ */ __name(async (ctx) => {
|
|
27275
|
+
const now = Date.now();
|
|
27276
|
+
if (currencyMemo !== void 0 && now < currencyMemo.nextAttemptAt) {
|
|
27277
|
+
return currencyMemo.current;
|
|
27278
|
+
}
|
|
27279
|
+
const stale = currencyMemo?.current ?? null;
|
|
27280
|
+
if (stale === null) {
|
|
27281
|
+
return refreshCurrencyRates();
|
|
27282
|
+
}
|
|
27283
|
+
currencyMemo = { current: stale, nextAttemptAt: now + RETRY_AFTER_FAILURE_MS };
|
|
27284
|
+
ctx.waitUntil(
|
|
27285
|
+
runInvocation({ name: "currency.refresh", trace: currentTraceContext() }, () => {
|
|
27286
|
+
return refreshCurrencyRates();
|
|
27287
|
+
})
|
|
27288
|
+
);
|
|
27289
|
+
return stale;
|
|
27290
|
+
}, "currentCurrencyRates");
|
|
27291
|
+
|
|
27292
|
+
// src/apps/broker-currency.ts
|
|
27293
|
+
var currencyRatesBody = external_exports.object({
|
|
27294
|
+
...identityFields,
|
|
27295
|
+
kind: external_exports.literal("currencyRates")
|
|
27296
|
+
}).strict();
|
|
27297
|
+
var handleCurrencyRates = /* @__PURE__ */ __name(async (ctx) => {
|
|
27298
|
+
const current = await currentCurrencyRates(ctx);
|
|
27299
|
+
if (current === null) {
|
|
27300
|
+
return Response.json({ error: "currency_rates_unavailable" }, { status: 503 });
|
|
27301
|
+
}
|
|
27302
|
+
return Response.json(current);
|
|
27303
|
+
}, "handleCurrencyRates");
|
|
27304
|
+
|
|
27305
|
+
// ../../libs/cf-data/src/cf-data.ts
|
|
27306
|
+
var CF_DATA_HEADER = "x-cf-data";
|
|
27307
|
+
var cfDataFrom = /* @__PURE__ */ __name((cf) => {
|
|
27308
|
+
return {
|
|
27309
|
+
country: cf?.country,
|
|
27310
|
+
city: cf?.city,
|
|
27311
|
+
region: cf?.region,
|
|
27312
|
+
regionCode: cf?.regionCode,
|
|
27313
|
+
timezone: cf?.timezone,
|
|
27314
|
+
postalCode: cf?.postalCode,
|
|
27315
|
+
continent: cf?.continent,
|
|
27316
|
+
longitude: cf?.longitude,
|
|
27317
|
+
latitude: cf?.latitude,
|
|
27318
|
+
isEUCountry: cf?.isEUCountry,
|
|
27319
|
+
botManagement: {
|
|
27320
|
+
ja4: cf?.botManagement?.ja4,
|
|
27321
|
+
ja3Hash: cf?.botManagement?.ja3Hash,
|
|
27322
|
+
score: cf?.botManagement?.score,
|
|
27323
|
+
verifiedBot: cf?.botManagement?.verifiedBot
|
|
27324
|
+
}
|
|
27325
|
+
};
|
|
27326
|
+
}, "cfDataFrom");
|
|
27327
|
+
var parseCfData = /* @__PURE__ */ __name((raw2) => {
|
|
27328
|
+
if (raw2 === null) {
|
|
27329
|
+
return void 0;
|
|
27330
|
+
}
|
|
27331
|
+
let parsed;
|
|
27332
|
+
try {
|
|
27333
|
+
parsed = JSON.parse(raw2);
|
|
27334
|
+
} catch {
|
|
27335
|
+
return void 0;
|
|
27336
|
+
}
|
|
27337
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
27338
|
+
return void 0;
|
|
27339
|
+
}
|
|
27340
|
+
return parsed;
|
|
27341
|
+
}, "parseCfData");
|
|
27342
|
+
|
|
27343
|
+
// src/helpers/geo.ts
|
|
27344
|
+
var UNDETERMINED_COUNTRIES = /* @__PURE__ */ new Set(["XX", "T1"]);
|
|
27345
|
+
var detectedCountry = /* @__PURE__ */ __name((cf) => {
|
|
27346
|
+
const country = cf?.country;
|
|
27347
|
+
return country && !UNDETERMINED_COUNTRIES.has(country) ? country : void 0;
|
|
27348
|
+
}, "detectedCountry");
|
|
27349
|
+
var euCountry = /* @__PURE__ */ __name((cf, country) => {
|
|
27350
|
+
if (cf?.isEUCountry === "1") {
|
|
27351
|
+
return "yes";
|
|
27352
|
+
}
|
|
27353
|
+
return country ? "no" : "unknown";
|
|
27354
|
+
}, "euCountry");
|
|
27355
|
+
var withForwardedCf = /* @__PURE__ */ __name((request) => {
|
|
27356
|
+
if (request.cf !== void 0) {
|
|
27357
|
+
return request;
|
|
27358
|
+
}
|
|
27359
|
+
const cf = parseCfData(request.headers.get(CF_DATA_HEADER));
|
|
27360
|
+
return cf === void 0 ? request : new Request(request, { cf });
|
|
27361
|
+
}, "withForwardedCf");
|
|
27362
|
+
var loggableGeo = /* @__PURE__ */ __name((geo) => {
|
|
27363
|
+
const fields = {};
|
|
27364
|
+
for (const [key, value] of Object.entries(geo)) {
|
|
27365
|
+
if (value !== void 0) {
|
|
27366
|
+
fields[key] = String(value);
|
|
27367
|
+
}
|
|
27368
|
+
}
|
|
27369
|
+
return fields;
|
|
27370
|
+
}, "loggableGeo");
|
|
27371
|
+
var hostFromRequest = /* @__PURE__ */ __name((request) => {
|
|
27372
|
+
return {
|
|
27373
|
+
ip: request.headers.get("cf-connecting-ip") ?? void 0,
|
|
27374
|
+
userAgent: request.headers.get("user-agent") ?? void 0,
|
|
27375
|
+
locale: request.headers.get("accept-language")?.split(",")[0]?.trim() || void 0
|
|
27376
|
+
};
|
|
27377
|
+
}, "hostFromRequest");
|
|
27378
|
+
var geoFromCf = /* @__PURE__ */ __name((cf) => {
|
|
27379
|
+
const bot = cf?.botManagement;
|
|
27380
|
+
const country = detectedCountry(cf);
|
|
27381
|
+
return {
|
|
27382
|
+
timezone: cf?.timezone ?? void 0,
|
|
27383
|
+
city: cf?.city ?? void 0,
|
|
27384
|
+
postalCode: cf?.postalCode ?? void 0,
|
|
27385
|
+
region: cf?.region ?? void 0,
|
|
27386
|
+
regionCode: cf?.regionCode ?? void 0,
|
|
27387
|
+
country,
|
|
27388
|
+
continent: cf?.continent ?? void 0,
|
|
27389
|
+
isEUCountry: euCountry(cf, country),
|
|
27390
|
+
latitude: cf?.latitude ?? void 0,
|
|
27391
|
+
longitude: cf?.longitude ?? void 0,
|
|
27392
|
+
ja4: bot?.ja4,
|
|
27393
|
+
ja3: bot?.ja3Hash,
|
|
27394
|
+
botScore: bot?.score
|
|
27395
|
+
};
|
|
27396
|
+
}, "geoFromCf");
|
|
27397
|
+
var geoFromRequest = /* @__PURE__ */ __name((request) => {
|
|
27398
|
+
return {
|
|
27399
|
+
...hostFromRequest(request),
|
|
27400
|
+
...geoFromCf(request.cf)
|
|
27401
|
+
};
|
|
27402
|
+
}, "geoFromRequest");
|
|
27403
|
+
|
|
27404
|
+
// src/id-graph/traits.ts
|
|
27405
|
+
var PII_KEYS = ["first_name", "last_name", "email", "phone", "country", "state", "city", "postal_code"];
|
|
27406
|
+
var snakeCase = /* @__PURE__ */ __name((key) => {
|
|
27407
|
+
return key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
|
|
27408
|
+
}, "snakeCase");
|
|
27409
|
+
var stringifyTrait = /* @__PURE__ */ __name((value) => {
|
|
27410
|
+
return typeof value === "string" ? value : JSON.stringify(value);
|
|
27411
|
+
}, "stringifyTrait");
|
|
27412
|
+
var piiTrait = /* @__PURE__ */ __name((key, rawValue) => {
|
|
27413
|
+
if (key !== "email" && key !== "phone") {
|
|
27414
|
+
return stringifyTrait(rawValue);
|
|
27415
|
+
}
|
|
27416
|
+
if (typeof rawValue !== "string") {
|
|
27417
|
+
return void 0;
|
|
27418
|
+
}
|
|
27419
|
+
return (key === "email" ? emailIdentifier(rawValue) : phoneIdentifier(rawValue))?.value;
|
|
27420
|
+
}, "piiTrait");
|
|
27421
|
+
var resolvePiiTrait = /* @__PURE__ */ __name((key, rawValue, unset) => {
|
|
27422
|
+
return unset ? null : piiTrait(key, rawValue);
|
|
27423
|
+
}, "resolvePiiTrait");
|
|
27424
|
+
var classifyTraits = /* @__PURE__ */ __name((traits, observedAt) => {
|
|
27425
|
+
const classified = { pii: {}, custom: {}, latest: [], dropped: [] };
|
|
27426
|
+
for (const [rawKey, rawValue] of Object.entries(traits)) {
|
|
27427
|
+
const key = snakeCase(rawKey);
|
|
27428
|
+
const unset = rawValue === null || rawValue === void 0;
|
|
27429
|
+
if (PII_KEYS.includes(key)) {
|
|
27430
|
+
const value2 = resolvePiiTrait(key, rawValue, unset);
|
|
27431
|
+
if (value2 === void 0) {
|
|
27432
|
+
classified.dropped.push(key);
|
|
27433
|
+
continue;
|
|
27434
|
+
}
|
|
27435
|
+
classified.pii[key] = value2;
|
|
27436
|
+
classified.latest.push({ key: `pii.${key}`, value: value2, observedAt });
|
|
27437
|
+
continue;
|
|
27438
|
+
}
|
|
27439
|
+
const value = unset ? null : stringifyTrait(rawValue);
|
|
27440
|
+
classified.custom[key] = value;
|
|
27441
|
+
classified.latest.push({ key: `custom.${key}`, value, observedAt });
|
|
27442
|
+
}
|
|
27443
|
+
return classified;
|
|
27444
|
+
}, "classifyTraits");
|
|
27445
|
+
var conversionFromTrack = /* @__PURE__ */ __name((payload) => {
|
|
27446
|
+
const amount = payload.properties["value"];
|
|
27447
|
+
if (typeof amount !== "number" || !Number.isFinite(amount)) {
|
|
27448
|
+
return void 0;
|
|
27449
|
+
}
|
|
27450
|
+
const orderId = payload.properties["orderId"];
|
|
27451
|
+
if (typeof orderId !== "string" || orderId === "") {
|
|
27452
|
+
return void 0;
|
|
27453
|
+
}
|
|
27454
|
+
const currency = payload.properties["currency"];
|
|
27455
|
+
const contents = payload.properties["contents"];
|
|
27456
|
+
return {
|
|
27457
|
+
eventName: payload.event,
|
|
27458
|
+
eventId: payload.eventId,
|
|
27459
|
+
orderId,
|
|
27460
|
+
amount,
|
|
27461
|
+
currency: typeof currency === "string" ? currency : null,
|
|
27462
|
+
contents: contents === void 0 ? null : JSON.stringify(contents)
|
|
27463
|
+
};
|
|
27464
|
+
}, "conversionFromTrack");
|
|
27465
|
+
|
|
27466
|
+
// src/id-graph/noop.ts
|
|
27467
|
+
var isNoopConsent = /* @__PURE__ */ __name((payload) => {
|
|
27468
|
+
return consentEntries(payload, 0).length === 0;
|
|
27469
|
+
}, "isNoopConsent");
|
|
27470
|
+
var isNoopIdentify = /* @__PURE__ */ __name((traits) => {
|
|
27471
|
+
return Object.keys(traits).length === 0;
|
|
27472
|
+
}, "isNoopIdentify");
|
|
27473
|
+
var isNoopSubmission = /* @__PURE__ */ __name((userId, args) => {
|
|
27474
|
+
if (args.type === "consent") {
|
|
27475
|
+
return isNoopConsent(args.payload);
|
|
27476
|
+
}
|
|
27477
|
+
if (args.type === "identify") {
|
|
27478
|
+
return isCanonicalId(userId) && isNoopIdentify(args.payload.traits);
|
|
27479
|
+
}
|
|
27480
|
+
return false;
|
|
27481
|
+
}, "isNoopSubmission");
|
|
27482
|
+
var isNoopAction = /* @__PURE__ */ __name((action) => {
|
|
27483
|
+
if (action.identifier !== void 0) {
|
|
27484
|
+
return false;
|
|
27382
27485
|
}
|
|
27383
|
-
|
|
27384
|
-
|
|
27385
|
-
|
|
27486
|
+
return action.type === "consent" && isNoopConsent(action.payload);
|
|
27487
|
+
}, "isNoopAction");
|
|
27488
|
+
var isDeliveryOnlyAction = /* @__PURE__ */ __name((action) => {
|
|
27489
|
+
if (action.identifier !== void 0 || action.type === "consent") {
|
|
27490
|
+
return false;
|
|
27386
27491
|
}
|
|
27387
|
-
|
|
27388
|
-
|
|
27389
|
-
|
|
27492
|
+
return action.type === "track" ? conversionFromTrack(action.payload) === void 0 : isNoopIdentify(action.payload.traits);
|
|
27493
|
+
}, "isDeliveryOnlyAction");
|
|
27494
|
+
|
|
27495
|
+
// src/id-graph/action.ts
|
|
27496
|
+
var UnknownUserError = class extends Error {
|
|
27497
|
+
static {
|
|
27498
|
+
__name(this, "UnknownUserError");
|
|
27390
27499
|
}
|
|
27391
|
-
|
|
27392
|
-
|
|
27393
|
-
|
|
27394
|
-
|
|
27395
|
-
|
|
27396
|
-
|
|
27397
|
-
|
|
27398
|
-
|
|
27399
|
-
|
|
27400
|
-
|
|
27401
|
-
|
|
27402
|
-
|
|
27403
|
-
|
|
27404
|
-
|
|
27405
|
-
|
|
27406
|
-
|
|
27407
|
-
|
|
27408
|
-
|
|
27500
|
+
constructor() {
|
|
27501
|
+
super("the identifier resolves to no user");
|
|
27502
|
+
this.name = "UnknownUserError";
|
|
27503
|
+
}
|
|
27504
|
+
};
|
|
27505
|
+
var mintActionId = /* @__PURE__ */ __name(() => {
|
|
27506
|
+
return `act_${ulid3()}`;
|
|
27507
|
+
}, "mintActionId");
|
|
27508
|
+
var buildAction = /* @__PURE__ */ __name((args) => {
|
|
27509
|
+
const receivedAt = args.receivedAt ?? Date.now();
|
|
27510
|
+
return {
|
|
27511
|
+
...args,
|
|
27512
|
+
actionId: mintActionId(),
|
|
27513
|
+
receivedAt
|
|
27514
|
+
};
|
|
27515
|
+
}, "buildAction");
|
|
27516
|
+
var ingest = /* @__PURE__ */ __name(async (env, action, site = ownSite(env)) => {
|
|
27517
|
+
await traced("rpc anon.ingest", {}, () => {
|
|
27518
|
+
return anonStub(env, action.userId).ingest(action, site, currentTraceContext());
|
|
27519
|
+
});
|
|
27520
|
+
}, "ingest");
|
|
27521
|
+
var serverActionContext = /* @__PURE__ */ __name((input2, receivedAt) => {
|
|
27522
|
+
const network = input2?.network;
|
|
27523
|
+
return {
|
|
27524
|
+
source: "server",
|
|
27525
|
+
occurredAt: input2?.occurredAt ?? receivedAt,
|
|
27526
|
+
...input2?.page === void 0 ? {} : { page: input2.page },
|
|
27527
|
+
...network === void 0 || network.ip === void 0 && network.userAgent === void 0 && network.locale === void 0 ? {} : { network: { ...network, provenance: "caller" } },
|
|
27528
|
+
...input2?.session === void 0 ? {} : { session: input2.session },
|
|
27529
|
+
offline: input2?.offline ?? false,
|
|
27530
|
+
replay: input2?.replay ?? false
|
|
27531
|
+
};
|
|
27532
|
+
}, "serverActionContext");
|
|
27533
|
+
var submitAction = /* @__PURE__ */ __name(async (env, userId, args, request) => {
|
|
27534
|
+
const attrs = {
|
|
27535
|
+
"action.type": args.type,
|
|
27536
|
+
"action.payload": JSON.stringify(args.payload),
|
|
27537
|
+
"action.identifier": userId,
|
|
27538
|
+
...args.type === "track" ? { "action.event": args.payload.event } : {}
|
|
27539
|
+
};
|
|
27540
|
+
return traced(`action ${args.type}`, { attrs }, async () => {
|
|
27541
|
+
if (isNoopSubmission(userId, args)) {
|
|
27542
|
+
setSpanAttributes({ "action.noop": true, ...isCanonicalId(userId) ? { "action.user_id": userId } : {} });
|
|
27543
|
+
return mintActionId();
|
|
27409
27544
|
}
|
|
27410
|
-
const
|
|
27411
|
-
|
|
27412
|
-
|
|
27413
|
-
|
|
27545
|
+
const resolved = await traced("resolve_user", {}, () => {
|
|
27546
|
+
return args.type === "track" ? resolveExistingUser(env, userId) : resolveUser(env, userId);
|
|
27547
|
+
});
|
|
27548
|
+
if (resolved === null) {
|
|
27549
|
+
throw new UnknownUserError();
|
|
27414
27550
|
}
|
|
27415
|
-
|
|
27551
|
+
const receivedAt = Date.now();
|
|
27552
|
+
const action = buildAction({
|
|
27553
|
+
...args,
|
|
27554
|
+
userId: resolved.id,
|
|
27555
|
+
identifier: resolved.identifier,
|
|
27556
|
+
receivedAt,
|
|
27557
|
+
context: serverActionContext(args.context, receivedAt),
|
|
27558
|
+
geo: request?.cf,
|
|
27559
|
+
host: request === void 0 ? void 0 : hostFromRequest(request)
|
|
27560
|
+
});
|
|
27561
|
+
setSpanAttributes({ "action.user_id": resolved.id, "action.id": action.actionId });
|
|
27562
|
+
await ingest(env, action);
|
|
27563
|
+
return action.actionId;
|
|
27416
27564
|
});
|
|
27417
|
-
}, "
|
|
27418
|
-
var refreshCurrencyRates = /* @__PURE__ */ __name(async () => {
|
|
27419
|
-
const rates = await fetchCurrencyRates();
|
|
27420
|
-
const now = Date.now();
|
|
27421
|
-
if (rates === null) {
|
|
27422
|
-
const current2 = currencyMemo?.current ?? null;
|
|
27423
|
-
currencyMemo = { current: current2, nextAttemptAt: now + RETRY_AFTER_FAILURE_MS };
|
|
27424
|
-
return current2;
|
|
27425
|
-
}
|
|
27426
|
-
const current = { rates, fetchedAt: now };
|
|
27427
|
-
currencyMemo = { current, nextAttemptAt: now + RATES_TTL_MS };
|
|
27428
|
-
return current;
|
|
27429
|
-
}, "refreshCurrencyRates");
|
|
27430
|
-
var currentCurrencyRates = /* @__PURE__ */ __name(async (ctx) => {
|
|
27431
|
-
const now = Date.now();
|
|
27432
|
-
if (currencyMemo !== void 0 && now < currencyMemo.nextAttemptAt) {
|
|
27433
|
-
return currencyMemo.current;
|
|
27434
|
-
}
|
|
27435
|
-
const stale = currencyMemo?.current ?? null;
|
|
27436
|
-
if (stale === null) {
|
|
27437
|
-
return refreshCurrencyRates();
|
|
27438
|
-
}
|
|
27439
|
-
currencyMemo = { current: stale, nextAttemptAt: now + RETRY_AFTER_FAILURE_MS };
|
|
27440
|
-
ctx.waitUntil(
|
|
27441
|
-
runInvocation({ name: "currency.refresh", trace: currentTraceContext() }, () => {
|
|
27442
|
-
return refreshCurrencyRates();
|
|
27443
|
-
})
|
|
27444
|
-
);
|
|
27445
|
-
return stale;
|
|
27446
|
-
}, "currentCurrencyRates");
|
|
27447
|
-
|
|
27448
|
-
// src/apps/broker-currency.ts
|
|
27449
|
-
var currencyRatesBody = external_exports.object({
|
|
27450
|
-
...identityFields,
|
|
27451
|
-
kind: external_exports.literal("currencyRates")
|
|
27452
|
-
}).strict();
|
|
27453
|
-
var handleCurrencyRates = /* @__PURE__ */ __name(async (ctx) => {
|
|
27454
|
-
const current = await currentCurrencyRates(ctx);
|
|
27455
|
-
if (current === null) {
|
|
27456
|
-
return Response.json({ error: "currency_rates_unavailable" }, { status: 503 });
|
|
27457
|
-
}
|
|
27458
|
-
return Response.json(current);
|
|
27459
|
-
}, "handleCurrencyRates");
|
|
27565
|
+
}, "submitAction");
|
|
27460
27566
|
|
|
27461
27567
|
// src/id-graph/upsert.ts
|
|
27462
27568
|
var identifierValue = external_exports.string().min(1).max(256).optional();
|
|
@@ -27544,35 +27650,249 @@ var rateLimited = /* @__PURE__ */ __name(async (env, key, binding = "BROKER_RATE
|
|
|
27544
27650
|
}
|
|
27545
27651
|
return false;
|
|
27546
27652
|
}
|
|
27547
|
-
const outcome = await limiter.limit({ key });
|
|
27548
|
-
return !outcome.success;
|
|
27549
|
-
}, "rateLimited");
|
|
27653
|
+
const outcome = await limiter.limit({ key });
|
|
27654
|
+
return !outcome.success;
|
|
27655
|
+
}, "rateLimited");
|
|
27656
|
+
|
|
27657
|
+
// src/apps/broker-identity.ts
|
|
27658
|
+
var identityUpsertBody = external_exports.object({
|
|
27659
|
+
...identityFields,
|
|
27660
|
+
kind: external_exports.literal("identityUpsert"),
|
|
27661
|
+
person: personUpsertSchema
|
|
27662
|
+
}).strict();
|
|
27663
|
+
var identityGranted = /* @__PURE__ */ __name((slug) => {
|
|
27664
|
+
return siteRegistry().registry.apps[slug]?.data.includes(IDENTITY_CAPABILITY) === true;
|
|
27665
|
+
}, "identityGranted");
|
|
27666
|
+
var identityPermissionDenied = /* @__PURE__ */ __name(() => {
|
|
27667
|
+
return Response.json({ error: "identity_permission_denied" }, { status: 403 });
|
|
27668
|
+
}, "identityPermissionDenied");
|
|
27669
|
+
var handleIdentityUpsert = /* @__PURE__ */ __name(async (env, body) => {
|
|
27670
|
+
if (!identityGranted(body.slug)) {
|
|
27671
|
+
return identityPermissionDenied();
|
|
27672
|
+
}
|
|
27673
|
+
if (await rateLimited(env, `${body.slug}::identity-upsert`)) {
|
|
27674
|
+
return Response.json({ error: "rate_limited" }, { status: 429 });
|
|
27675
|
+
}
|
|
27676
|
+
const upserted = await upsertPerson(env, body.person);
|
|
27677
|
+
if (upserted === null) {
|
|
27678
|
+
return Response.json({ error: "invalid_identifier" }, { status: 400 });
|
|
27679
|
+
}
|
|
27680
|
+
return Response.json(upserted);
|
|
27681
|
+
}, "handleIdentityUpsert");
|
|
27682
|
+
|
|
27683
|
+
// ../../libs/app-ui-token/src/base64url.ts
|
|
27684
|
+
var base64UrlEncode = /* @__PURE__ */ __name((bytes) => {
|
|
27685
|
+
let binary = "";
|
|
27686
|
+
for (const byte of bytes) {
|
|
27687
|
+
binary += String.fromCharCode(byte);
|
|
27688
|
+
}
|
|
27689
|
+
return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
|
|
27690
|
+
}, "base64UrlEncode");
|
|
27691
|
+
var base64UrlDecode = /* @__PURE__ */ __name((text) => {
|
|
27692
|
+
const padded = text.replaceAll("-", "+").replaceAll("_", "/").padEnd(text.length + (4 - text.length % 4) % 4, "=");
|
|
27693
|
+
let binary;
|
|
27694
|
+
try {
|
|
27695
|
+
binary = atob(padded);
|
|
27696
|
+
} catch {
|
|
27697
|
+
return null;
|
|
27698
|
+
}
|
|
27699
|
+
const bytes = new Uint8Array(binary.length);
|
|
27700
|
+
for (let index = 0; index < binary.length; index += 1) {
|
|
27701
|
+
bytes[index] = binary.charCodeAt(index);
|
|
27702
|
+
}
|
|
27703
|
+
return bytes;
|
|
27704
|
+
}, "base64UrlDecode");
|
|
27705
|
+
|
|
27706
|
+
// ../../libs/app-ui-token/src/env.ts
|
|
27707
|
+
var envSchema = external_exports.object({
|
|
27708
|
+
v: external_exports.literal(1),
|
|
27709
|
+
keys: external_exports.record(external_exports.string().min(1).max(16), external_exports.string().min(1))
|
|
27710
|
+
}).strict();
|
|
27711
|
+
var parseAppUiKeyEnv = /* @__PURE__ */ __name((raw2) => {
|
|
27712
|
+
let json2;
|
|
27713
|
+
try {
|
|
27714
|
+
json2 = JSON.parse(raw2);
|
|
27715
|
+
} catch {
|
|
27716
|
+
return null;
|
|
27717
|
+
}
|
|
27718
|
+
const parsed = envSchema.safeParse(json2);
|
|
27719
|
+
if (!parsed.success) {
|
|
27720
|
+
return null;
|
|
27721
|
+
}
|
|
27722
|
+
const keys = {};
|
|
27723
|
+
for (const [kid, encoded] of Object.entries(parsed.data.keys)) {
|
|
27724
|
+
const bytes = base64UrlDecode(encoded);
|
|
27725
|
+
if (bytes === null) {
|
|
27726
|
+
return null;
|
|
27727
|
+
}
|
|
27728
|
+
keys[kid] = bytes;
|
|
27729
|
+
}
|
|
27730
|
+
return keys;
|
|
27731
|
+
}, "parseAppUiKeyEnv");
|
|
27732
|
+
|
|
27733
|
+
// ../../libs/app-ui-token/src/jwt.ts
|
|
27734
|
+
var headerSchema = external_exports.object({
|
|
27735
|
+
alg: external_exports.literal("HS256"),
|
|
27736
|
+
typ: external_exports.literal("JWT"),
|
|
27737
|
+
kid: external_exports.string().min(1).max(16)
|
|
27738
|
+
}).strict();
|
|
27739
|
+
var encodeJsonPart = /* @__PURE__ */ __name((value) => {
|
|
27740
|
+
return base64UrlEncode(new TextEncoder().encode(JSON.stringify(value)));
|
|
27741
|
+
}, "encodeJsonPart");
|
|
27742
|
+
var parseJsonPart = /* @__PURE__ */ __name((part) => {
|
|
27743
|
+
const bytes = base64UrlDecode(part);
|
|
27744
|
+
if (bytes === null) {
|
|
27745
|
+
return null;
|
|
27746
|
+
}
|
|
27747
|
+
try {
|
|
27748
|
+
return JSON.parse(new TextDecoder().decode(bytes));
|
|
27749
|
+
} catch {
|
|
27750
|
+
return null;
|
|
27751
|
+
}
|
|
27752
|
+
}, "parseJsonPart");
|
|
27753
|
+
var hmacKey = /* @__PURE__ */ __name((key, usage) => {
|
|
27754
|
+
return crypto.subtle.importKey("raw", key, { name: "HMAC", hash: "SHA-256" }, false, [usage]);
|
|
27755
|
+
}, "hmacKey");
|
|
27756
|
+
var signCompact = /* @__PURE__ */ __name(async (kid, key, claims) => {
|
|
27757
|
+
const signingInput = `${encodeJsonPart({ alg: "HS256", typ: "JWT", kid })}.${encodeJsonPart(claims)}`;
|
|
27758
|
+
const signature = await crypto.subtle.sign("HMAC", await hmacKey(key, "sign"), new TextEncoder().encode(signingInput));
|
|
27759
|
+
return `${signingInput}.${base64UrlEncode(new Uint8Array(signature))}`;
|
|
27760
|
+
}, "signCompact");
|
|
27761
|
+
var verifyCompactSignature = /* @__PURE__ */ __name(async (token, keys) => {
|
|
27762
|
+
const [headerPart, claimsPart, signaturePart, extra] = token.split(".");
|
|
27763
|
+
if (headerPart === void 0 || claimsPart === void 0 || signaturePart === void 0 || extra !== void 0) {
|
|
27764
|
+
return { ok: false, reason: "malformed" };
|
|
27765
|
+
}
|
|
27766
|
+
const header = headerSchema.safeParse(parseJsonPart(headerPart));
|
|
27767
|
+
if (!header.success) {
|
|
27768
|
+
return { ok: false, reason: "malformed" };
|
|
27769
|
+
}
|
|
27770
|
+
const key = keys[header.data.kid];
|
|
27771
|
+
if (key === void 0) {
|
|
27772
|
+
return { ok: false, reason: "unknown_kid" };
|
|
27773
|
+
}
|
|
27774
|
+
const signature = base64UrlDecode(signaturePart);
|
|
27775
|
+
if (signature === null) {
|
|
27776
|
+
return { ok: false, reason: "malformed" };
|
|
27777
|
+
}
|
|
27778
|
+
const signed = await crypto.subtle.verify(
|
|
27779
|
+
"HMAC",
|
|
27780
|
+
await hmacKey(key, "verify"),
|
|
27781
|
+
signature,
|
|
27782
|
+
new TextEncoder().encode(`${headerPart}.${claimsPart}`)
|
|
27783
|
+
);
|
|
27784
|
+
if (!signed) {
|
|
27785
|
+
return { ok: false, reason: "bad_signature" };
|
|
27786
|
+
}
|
|
27787
|
+
return { ok: true, claims: parseJsonPart(claimsPart), kid: header.data.kid, key };
|
|
27788
|
+
}, "verifyCompactSignature");
|
|
27550
27789
|
|
|
27551
|
-
// src/
|
|
27552
|
-
var
|
|
27553
|
-
|
|
27554
|
-
|
|
27555
|
-
|
|
27790
|
+
// ../../libs/app-ui-token/src/token.ts
|
|
27791
|
+
var APP_UI_TOKEN_SKEW_SECONDS = 60;
|
|
27792
|
+
var APP_UI_TOKEN_ISSUER = "qinto-portal";
|
|
27793
|
+
var claimsSchema = external_exports.object({
|
|
27794
|
+
iss: external_exports.literal(APP_UI_TOKEN_ISSUER),
|
|
27795
|
+
aud: external_exports.string().min(1),
|
|
27796
|
+
sub: external_exports.string().min(1),
|
|
27797
|
+
userId: external_exports.string().min(1),
|
|
27798
|
+
iat: external_exports.number().int(),
|
|
27799
|
+
exp: external_exports.number().int(),
|
|
27800
|
+
domainId: external_exports.string().min(1),
|
|
27801
|
+
appId: external_exports.string().min(1),
|
|
27802
|
+
appInstallId: external_exports.string().min(1),
|
|
27803
|
+
appVersionId: external_exports.string().min(1),
|
|
27804
|
+
access: external_exports.enum(["read", "write"]),
|
|
27805
|
+
// Absent means `full`; see the mint below.
|
|
27806
|
+
appRole: external_exports.enum(APP_ROLES).optional()
|
|
27556
27807
|
}).strict();
|
|
27557
|
-
var
|
|
27558
|
-
return
|
|
27559
|
-
}, "
|
|
27560
|
-
var
|
|
27561
|
-
|
|
27562
|
-
|
|
27563
|
-
|
|
27564
|
-
if (!identityGranted(body.slug)) {
|
|
27565
|
-
return identityPermissionDenied();
|
|
27808
|
+
var appUiAudience = /* @__PURE__ */ __name((slug) => {
|
|
27809
|
+
return `app-ui:${slug}`;
|
|
27810
|
+
}, "appUiAudience");
|
|
27811
|
+
var verifyAppUiToken = /* @__PURE__ */ __name(async (input2) => {
|
|
27812
|
+
const signature = await verifyCompactSignature(input2.token, input2.keys);
|
|
27813
|
+
if (!signature.ok) {
|
|
27814
|
+
return signature;
|
|
27566
27815
|
}
|
|
27567
|
-
|
|
27568
|
-
|
|
27816
|
+
const claims = claimsSchema.safeParse(signature.claims);
|
|
27817
|
+
if (!claims.success) {
|
|
27818
|
+
return { ok: false, reason: "bad_claims" };
|
|
27569
27819
|
}
|
|
27570
|
-
|
|
27571
|
-
|
|
27572
|
-
return Response.json({ error: "invalid_identifier" }, { status: 400 });
|
|
27820
|
+
if (claims.data.aud !== appUiAudience(input2.slug)) {
|
|
27821
|
+
return { ok: false, reason: "wrong_app" };
|
|
27573
27822
|
}
|
|
27574
|
-
|
|
27575
|
-
|
|
27823
|
+
const now = Math.floor(input2.now.getTime() / 1e3);
|
|
27824
|
+
if (claims.data.iat > now + (input2.skewSeconds ?? APP_UI_TOKEN_SKEW_SECONDS)) {
|
|
27825
|
+
return { ok: false, reason: "future_iat" };
|
|
27826
|
+
}
|
|
27827
|
+
if (claims.data.exp <= now) {
|
|
27828
|
+
return { ok: false, reason: "expired" };
|
|
27829
|
+
}
|
|
27830
|
+
return { ok: true, claims: claims.data, kid: signature.kid, key: signature.key };
|
|
27831
|
+
}, "verifyAppUiToken");
|
|
27832
|
+
|
|
27833
|
+
// ../../libs/app-ui-token/src/runtime-query-ticket.ts
|
|
27834
|
+
var APP_RUNTIME_QUERY_TICKET_TTL_SECONDS = 600;
|
|
27835
|
+
var APP_RUNTIME_QUERY_TICKET_ISSUER = "qinto-portal";
|
|
27836
|
+
var claimsSchema2 = external_exports.object({
|
|
27837
|
+
iss: external_exports.literal(APP_RUNTIME_QUERY_TICKET_ISSUER),
|
|
27838
|
+
aud: external_exports.string().min(1),
|
|
27839
|
+
sub: external_exports.string().min(1),
|
|
27840
|
+
iat: external_exports.number().int(),
|
|
27841
|
+
exp: external_exports.number().int(),
|
|
27842
|
+
domainId: external_exports.string().min(1),
|
|
27843
|
+
appId: external_exports.string().min(1),
|
|
27844
|
+
appInstallId: external_exports.string().min(1),
|
|
27845
|
+
appVersionId: external_exports.string().min(1)
|
|
27846
|
+
}).strict();
|
|
27847
|
+
var appRuntimeQueryAudience = /* @__PURE__ */ __name((slug) => {
|
|
27848
|
+
return `app-runtime-query:${slug}`;
|
|
27849
|
+
}, "appRuntimeQueryAudience");
|
|
27850
|
+
var mintAppRuntimeQueryTicket = /* @__PURE__ */ __name(async (input2) => {
|
|
27851
|
+
const iat = Math.floor(input2.now.getTime() / 1e3);
|
|
27852
|
+
const claims = {
|
|
27853
|
+
iss: APP_RUNTIME_QUERY_TICKET_ISSUER,
|
|
27854
|
+
aud: appRuntimeQueryAudience(input2.slug),
|
|
27855
|
+
sub: input2.appInstallId,
|
|
27856
|
+
iat,
|
|
27857
|
+
exp: iat + (input2.ttlSeconds ?? APP_RUNTIME_QUERY_TICKET_TTL_SECONDS),
|
|
27858
|
+
domainId: input2.domainId,
|
|
27859
|
+
appId: input2.appId,
|
|
27860
|
+
appInstallId: input2.appInstallId,
|
|
27861
|
+
appVersionId: input2.appVersionId
|
|
27862
|
+
};
|
|
27863
|
+
return signCompact(input2.kid, input2.key, claims);
|
|
27864
|
+
}, "mintAppRuntimeQueryTicket");
|
|
27865
|
+
|
|
27866
|
+
// ../../libs/app-ui-token/src/write-ticket.ts
|
|
27867
|
+
var APP_UI_WRITE_TICKET_TTL_SECONDS = 60;
|
|
27868
|
+
var APP_UI_WRITE_TICKET_ISSUER = "qinto-site";
|
|
27869
|
+
var claimsSchema3 = external_exports.object({
|
|
27870
|
+
iss: external_exports.literal(APP_UI_WRITE_TICKET_ISSUER),
|
|
27871
|
+
aud: external_exports.string().min(1),
|
|
27872
|
+
sub: external_exports.string().min(1),
|
|
27873
|
+
iat: external_exports.number().int(),
|
|
27874
|
+
exp: external_exports.number().int(),
|
|
27875
|
+
domainId: external_exports.string().min(1),
|
|
27876
|
+
appId: external_exports.string().min(1),
|
|
27877
|
+
appVersionId: external_exports.string().min(1)
|
|
27878
|
+
}).strict();
|
|
27879
|
+
var appUiWriteAudience = /* @__PURE__ */ __name((slug) => {
|
|
27880
|
+
return `app-ui-write:${slug}`;
|
|
27881
|
+
}, "appUiWriteAudience");
|
|
27882
|
+
var mintAppUiWriteTicket = /* @__PURE__ */ __name(async (input2) => {
|
|
27883
|
+
const iat = Math.floor(input2.now.getTime() / 1e3);
|
|
27884
|
+
const claims = {
|
|
27885
|
+
iss: APP_UI_WRITE_TICKET_ISSUER,
|
|
27886
|
+
aud: appUiWriteAudience(input2.slug),
|
|
27887
|
+
sub: input2.userId,
|
|
27888
|
+
iat,
|
|
27889
|
+
exp: iat + (input2.ttlSeconds ?? APP_UI_WRITE_TICKET_TTL_SECONDS),
|
|
27890
|
+
domainId: input2.domainId,
|
|
27891
|
+
appId: input2.appId,
|
|
27892
|
+
appVersionId: input2.appVersionId
|
|
27893
|
+
};
|
|
27894
|
+
return signCompact(input2.kid, input2.key, claims);
|
|
27895
|
+
}, "mintAppUiWriteTicket");
|
|
27576
27896
|
|
|
27577
27897
|
// src/apps/emit-payload.ts
|
|
27578
27898
|
var emitPayloadSchema = external_exports.object({
|
|
@@ -27591,51 +27911,6 @@ var emittedEventId = /* @__PURE__ */ __name((eventId, properties2) => {
|
|
|
27591
27911
|
return `evt_${ulid3()}`;
|
|
27592
27912
|
}, "emittedEventId");
|
|
27593
27913
|
|
|
27594
|
-
// src/apps/broker-track.ts
|
|
27595
|
-
var trackBody = external_exports.object({
|
|
27596
|
-
...identityFields,
|
|
27597
|
-
kind: external_exports.literal("track"),
|
|
27598
|
-
source: external_exports.enum(APP_HANDLER_SOURCES),
|
|
27599
|
-
userId: external_exports.string().min(1).max(64),
|
|
27600
|
-
installId: external_exports.string().min(1).max(64),
|
|
27601
|
-
installs: installRuleSchema.optional(),
|
|
27602
|
-
event: emitSchema.omit({ installs: true })
|
|
27603
|
-
}).strict();
|
|
27604
|
-
var handleTrack = /* @__PURE__ */ __name(async (env, body) => {
|
|
27605
|
-
const app2 = siteRegistry().registry.apps[body.slug];
|
|
27606
|
-
if (app2 === void 0) {
|
|
27607
|
-
return Response.json({ error: "forbidden" }, { status: 403 });
|
|
27608
|
-
}
|
|
27609
|
-
if (!app2.installs.some((install) => install.active && install.id === body.installId)) {
|
|
27610
|
-
return Response.json({ error: "invalid_install" }, { status: 400 });
|
|
27611
|
-
}
|
|
27612
|
-
const parsed = emitPayloadSchema.safeParse(body.event.payload);
|
|
27613
|
-
if (!parsed.success) {
|
|
27614
|
-
return Response.json({ error: "invalid_event" }, { status: 400 });
|
|
27615
|
-
}
|
|
27616
|
-
if (!isCanonicalId(body.userId)) {
|
|
27617
|
-
return Response.json({ error: "invalid_user" }, { status: 400 });
|
|
27618
|
-
}
|
|
27619
|
-
if (await rateLimited(env, `${body.slug}:${body.installId}:track`)) {
|
|
27620
|
-
return Response.json({ error: "rate_limited" }, { status: 429 });
|
|
27621
|
-
}
|
|
27622
|
-
const site = ownSite(env);
|
|
27623
|
-
if (!await anonStub(env, body.userId).appAllowed(body.slug, site, currentTraceContext())) {
|
|
27624
|
-
return Response.json({ error: "forbidden" }, { status: 403 });
|
|
27625
|
-
}
|
|
27626
|
-
const { event, eventId, properties: properties2 } = parsed.data;
|
|
27627
|
-
const action = buildAction({
|
|
27628
|
-
type: "track",
|
|
27629
|
-
userId: body.userId,
|
|
27630
|
-
source: body.source,
|
|
27631
|
-
payload: { event, eventId: emittedEventId(eventId, properties2), properties: properties2 ?? {} },
|
|
27632
|
-
context: { source: "app", occurredAt: body.event.occurredAt, offline: false, replay: false },
|
|
27633
|
-
...body.installs === void 0 ? {} : { installs: body.installs }
|
|
27634
|
-
});
|
|
27635
|
-
await ingest(env, action, site);
|
|
27636
|
-
return Response.json({ accepted: true }, { status: 202 });
|
|
27637
|
-
}, "handleTrack");
|
|
27638
|
-
|
|
27639
27914
|
// src/id-graph/anon/context.ts
|
|
27640
27915
|
var LEVEL_KINDS = {
|
|
27641
27916
|
private: { kv: "context_private_kv", text: "context_private_text" },
|
|
@@ -28125,7 +28400,7 @@ var BOT_SCORE_MIN = 1;
|
|
|
28125
28400
|
var BOT_SCORE_MAX = 99;
|
|
28126
28401
|
|
|
28127
28402
|
// ../../libs/bot-protection/src/config.ts
|
|
28128
|
-
var NO_PROTECTION = { minBotScore: null, countries: null };
|
|
28403
|
+
var NO_PROTECTION = { minBotScore: null, countries: null, blockVerifiedBots: false };
|
|
28129
28404
|
var normalizeScore = /* @__PURE__ */ __name((value, problems) => {
|
|
28130
28405
|
if (value === void 0 || value === null) {
|
|
28131
28406
|
return null;
|
|
@@ -28159,6 +28434,13 @@ var normalizeCountries = /* @__PURE__ */ __name((value, problems) => {
|
|
|
28159
28434
|
}
|
|
28160
28435
|
return list.length === 0 ? null : { mode: record2.mode, list };
|
|
28161
28436
|
}, "normalizeCountries");
|
|
28437
|
+
var normalizeBlockVerifiedBots = /* @__PURE__ */ __name((value, problems) => {
|
|
28438
|
+
if (value === void 0 || typeof value === "boolean") {
|
|
28439
|
+
return value ?? false;
|
|
28440
|
+
}
|
|
28441
|
+
problems.push("has a non-boolean blockVerifiedBots");
|
|
28442
|
+
return false;
|
|
28443
|
+
}, "normalizeBlockVerifiedBots");
|
|
28162
28444
|
var parseBotProtection = /* @__PURE__ */ __name((raw2) => {
|
|
28163
28445
|
if (raw2.trim() === "") {
|
|
28164
28446
|
return { config: NO_PROTECTION };
|
|
@@ -28176,15 +28458,19 @@ var parseBotProtection = /* @__PURE__ */ __name((raw2) => {
|
|
|
28176
28458
|
const problems = [];
|
|
28177
28459
|
const config2 = {
|
|
28178
28460
|
minBotScore: normalizeScore(record2.minBotScore, problems),
|
|
28179
|
-
countries: normalizeCountries(record2.countries, problems)
|
|
28461
|
+
countries: normalizeCountries(record2.countries, problems),
|
|
28462
|
+
blockVerifiedBots: normalizeBlockVerifiedBots(record2.blockVerifiedBots, problems)
|
|
28180
28463
|
};
|
|
28181
28464
|
return problems.length > 0 ? { config: config2, error: `BOT_PROTECTION ${problems.join("; ")}` } : { config: config2 };
|
|
28182
28465
|
}, "parseBotProtection");
|
|
28183
28466
|
var isRequestBlocked = /* @__PURE__ */ __name((config2, signal) => {
|
|
28184
|
-
if (
|
|
28467
|
+
if (isCountryBlocked(signal.country, signal.isEU ?? false, config2.countries)) {
|
|
28185
28468
|
return true;
|
|
28186
28469
|
}
|
|
28187
|
-
|
|
28470
|
+
if (signal.verifiedBot === true) {
|
|
28471
|
+
return config2.blockVerifiedBots;
|
|
28472
|
+
}
|
|
28473
|
+
return config2.minBotScore !== null && signal.botScore !== void 0 && signal.botScore < config2.minBotScore;
|
|
28188
28474
|
}, "isRequestBlocked");
|
|
28189
28475
|
|
|
28190
28476
|
// ../../libs/geo-filter/src/us-states.ts
|
|
@@ -28486,7 +28772,8 @@ var adoptAppSpans = /* @__PURE__ */ __name((response) => {
|
|
|
28486
28772
|
}, "adoptAppSpans");
|
|
28487
28773
|
|
|
28488
28774
|
// src/apps/invoke.ts
|
|
28489
|
-
var
|
|
28775
|
+
var RPC_HOT_TIMEOUT_MS = 1e4;
|
|
28776
|
+
var RPC_COLD_TIMEOUT_MS = 4e4;
|
|
28490
28777
|
var APP_HOOK_TIMEOUT_MS = 25e3;
|
|
28491
28778
|
var MAX_EMIT_DEPTH = 2;
|
|
28492
28779
|
var HOOK_URL = "https://app/__qinto/hook";
|
|
@@ -28583,7 +28870,7 @@ var buildEnvelope = /* @__PURE__ */ __name((target, trigger, snapshot, context)
|
|
|
28583
28870
|
...geoAllowed ? { geo: snapshot.userGeo } : {}
|
|
28584
28871
|
},
|
|
28585
28872
|
...buckets.includes("consentState") ? { consent: { enabled: context.consentEnforced, categories: snapshot.consent.categories, apps: snapshot.consent.apps } } : {},
|
|
28586
|
-
...geoAllowed && currentGeo !== void 0 ? { geo: currentGeo } : {},
|
|
28873
|
+
...geoAllowed && currentGeo !== void 0 ? { geo: cfDataFrom(currentGeo) } : {},
|
|
28587
28874
|
...trigger.kind === "connect" && buckets.includes("eventContext") ? connectContext(trigger) : {},
|
|
28588
28875
|
// Not gated on a capabilities.data bucket: an app only ever sees what it captured or wrote.
|
|
28589
28876
|
...captures === void 0 ? {} : { captures },
|
|
@@ -28777,85 +29064,21 @@ var invokeApp = /* @__PURE__ */ __name(async (env, input2) => {
|
|
|
28777
29064
|
);
|
|
28778
29065
|
}, "invokeApp");
|
|
28779
29066
|
|
|
28780
|
-
// src/apps/broker.ts
|
|
29067
|
+
// src/apps/broker-rpc.ts
|
|
28781
29068
|
var MAX_RPC_DEPTH = 3;
|
|
28782
|
-
var BROKER_PATHS = ["/__qinto/push", "/__qinto/rpc"];
|
|
28783
|
-
var pushBody = external_exports.object({
|
|
28784
|
-
...identityFields,
|
|
28785
|
-
kind: external_exports.literal("push"),
|
|
28786
|
-
userId: external_exports.string().min(1).max(64),
|
|
28787
|
-
installId: external_exports.string().min(1).max(64).optional(),
|
|
28788
|
-
broadcast: external_exports.enum(PUSH_BROADCASTS).optional(),
|
|
28789
|
-
topic: external_exports.string().min(1).max(64),
|
|
28790
|
-
data: external_exports.unknown().optional()
|
|
28791
|
-
}).strict();
|
|
28792
29069
|
var rpcBody = external_exports.object({
|
|
28793
29070
|
...identityFields,
|
|
28794
29071
|
kind: external_exports.literal("rpc"),
|
|
28795
29072
|
// Absent when the caller is an endpoint, a schedule or a ui handler; the callee then sees no user.
|
|
28796
|
-
userId: external_exports.string().min(1).max(64).optional(),
|
|
28797
|
-
callee: external_exports.string().min(1).max(30),
|
|
28798
|
-
fn: external_exports.string().min(1).max(64),
|
|
28799
|
-
args: external_exports.unknown().optional(),
|
|
28800
|
-
installId: external_exports.string().min(1).max(64).optional(),
|
|
28801
|
-
depth: external_exports.number().int().min(0)
|
|
28802
|
-
|
|
28803
|
-
|
|
28804
|
-
...identityFields,
|
|
28805
|
-
kind: external_exports.literal("installs"),
|
|
28806
|
-
callee: external_exports.string().min(1).max(30)
|
|
28807
|
-
}).strict();
|
|
28808
|
-
var activeBody = external_exports.object({
|
|
28809
|
-
...identityFields,
|
|
28810
|
-
kind: external_exports.literal("active"),
|
|
28811
|
-
ord: external_exports.number().int().min(1),
|
|
28812
|
-
userId: external_exports.string().min(1).max(64).optional()
|
|
28813
|
-
}).strict();
|
|
28814
|
-
var contextReadBody = external_exports.object({
|
|
28815
|
-
...identityFields,
|
|
28816
|
-
kind: external_exports.literal("contextRead"),
|
|
28817
|
-
userId: external_exports.string().min(1).max(64)
|
|
28818
|
-
}).strict();
|
|
28819
|
-
var contextWriteBody = external_exports.object({
|
|
28820
|
-
...identityFields,
|
|
28821
|
-
kind: external_exports.literal("contextWrite"),
|
|
28822
|
-
userId: external_exports.string().min(1).max(64),
|
|
28823
|
-
writes: external_exports.array(contextWriteSchema).min(1).max(MAX_CONTEXT_WRITES_PER_INSTALL)
|
|
28824
|
-
}).strict();
|
|
28825
|
-
var factsQuery = external_exports.object({
|
|
28826
|
-
kinds: external_exports.array(external_exports.enum(APP_FACT_KINDS)).min(1).max(APP_FACT_KINDS.length).optional(),
|
|
28827
|
-
observedAtGte: external_exports.number().int().nonnegative().safe().optional(),
|
|
28828
|
-
observedAtLt: external_exports.number().int().nonnegative().safe().optional(),
|
|
28829
|
-
limit: external_exports.number().int().min(1).max(MAX_APP_FACTS_PER_PAGE).optional()
|
|
28830
|
-
}).strict().refine((query) => query.observedAtGte === void 0 || query.observedAtLt === void 0 || query.observedAtGte < query.observedAtLt);
|
|
28831
|
-
var factsReadBody = external_exports.object({
|
|
28832
|
-
...identityFields,
|
|
28833
|
-
kind: external_exports.literal("factsRead"),
|
|
28834
|
-
userId: external_exports.string().min(1).max(64),
|
|
28835
|
-
query: factsQuery.optional(),
|
|
28836
|
-
cursor: external_exports.string().min(1).max(4096).optional()
|
|
28837
|
-
}).strict().refine((body) => body.query === void 0 !== (body.cursor === void 0));
|
|
28838
|
-
var identityReadBody = external_exports.object({
|
|
28839
|
-
...identityFields,
|
|
28840
|
-
kind: external_exports.literal("identityRead"),
|
|
28841
|
-
identifier: identifierField2,
|
|
28842
|
-
cursor: external_exports.string().regex(/^\d+$/).optional()
|
|
29073
|
+
userId: external_exports.string().min(1).max(64).optional(),
|
|
29074
|
+
callee: external_exports.string().min(1).max(30),
|
|
29075
|
+
fn: external_exports.string().min(1).max(64),
|
|
29076
|
+
args: external_exports.unknown().optional(),
|
|
29077
|
+
installId: external_exports.string().min(1).max(64).optional(),
|
|
29078
|
+
depth: external_exports.number().int().min(0),
|
|
29079
|
+
// Optional on the wire: a runtime predating traffic classes sends none, which means hot.
|
|
29080
|
+
traffic: external_exports.enum(TRAFFIC_CLASSES).optional()
|
|
28843
29081
|
}).strict();
|
|
28844
|
-
var brokerBody = external_exports.discriminatedUnion("kind", [
|
|
28845
|
-
pushBody,
|
|
28846
|
-
rpcBody,
|
|
28847
|
-
installsBody,
|
|
28848
|
-
activeBody,
|
|
28849
|
-
contextReadBody,
|
|
28850
|
-
contextWriteBody,
|
|
28851
|
-
factsReadBody,
|
|
28852
|
-
identityReadBody,
|
|
28853
|
-
identityUpsertBody,
|
|
28854
|
-
currencyRatesBody,
|
|
28855
|
-
trackBody
|
|
28856
|
-
]);
|
|
28857
|
-
var BODY_ENVELOPE_ALLOWANCE_BYTES = 2 * 1024;
|
|
28858
|
-
var MAX_BODY_BYTES = MAX_RPC_BYTES + BODY_ENVELOPE_ALLOWANCE_BYTES;
|
|
28859
29082
|
var parseJson = /* @__PURE__ */ __name((raw2) => {
|
|
28860
29083
|
try {
|
|
28861
29084
|
return JSON.parse(raw2);
|
|
@@ -28863,85 +29086,6 @@ var parseJson = /* @__PURE__ */ __name((raw2) => {
|
|
|
28863
29086
|
return null;
|
|
28864
29087
|
}
|
|
28865
29088
|
}, "parseJson");
|
|
28866
|
-
var factsCursor = external_exports.object({
|
|
28867
|
-
userId: external_exports.string().min(1).max(64),
|
|
28868
|
-
kinds: external_exports.array(external_exports.enum(APP_FACT_KINDS)).min(1).max(APP_FACT_KINDS.length),
|
|
28869
|
-
observedAtGte: external_exports.number().int().nonnegative().safe().nullable(),
|
|
28870
|
-
observedAtLt: external_exports.number().int().nonnegative().safe().nullable(),
|
|
28871
|
-
limit: external_exports.number().int().min(1).max(MAX_APP_FACTS_PER_PAGE),
|
|
28872
|
-
afterRowId: external_exports.number().int().nonnegative().safe(),
|
|
28873
|
-
maxRowId: external_exports.number().int().positive().safe().nullable()
|
|
28874
|
-
}).strict();
|
|
28875
|
-
var cursorKey = /* @__PURE__ */ __name(async (hex3) => {
|
|
28876
|
-
const bytes = Uint8Array.from(hex3.matchAll(/.{2}/g), ([pair]) => Number.parseInt(pair, 16));
|
|
28877
|
-
return crypto.subtle.importKey("raw", bytes, "AES-GCM", false, ["encrypt", "decrypt"]);
|
|
28878
|
-
}, "cursorKey");
|
|
28879
|
-
var encodeBase64Url = /* @__PURE__ */ __name((bytes) => {
|
|
28880
|
-
let binary = "";
|
|
28881
|
-
for (const byte of bytes) {
|
|
28882
|
-
binary += String.fromCharCode(byte);
|
|
28883
|
-
}
|
|
28884
|
-
return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
|
|
28885
|
-
}, "encodeBase64Url");
|
|
28886
|
-
var decodeBase64Url = /* @__PURE__ */ __name((value) => {
|
|
28887
|
-
const base643 = value.replaceAll("-", "+").replaceAll("_", "/");
|
|
28888
|
-
const padded = base643.padEnd(Math.ceil(base643.length / 4) * 4, "=");
|
|
28889
|
-
const binary = atob(padded);
|
|
28890
|
-
return Uint8Array.from(binary, (character) => character.charCodeAt(0));
|
|
28891
|
-
}, "decodeBase64Url");
|
|
28892
|
-
var encryptFactsCursor = /* @__PURE__ */ __name(async (key, cursor) => {
|
|
28893
|
-
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
28894
|
-
const plaintext = new TextEncoder().encode(JSON.stringify(cursor));
|
|
28895
|
-
const ciphertext = new Uint8Array(await crypto.subtle.encrypt({ name: "AES-GCM", iv }, await cursorKey(key), plaintext));
|
|
28896
|
-
const packed = new Uint8Array(iv.byteLength + ciphertext.byteLength);
|
|
28897
|
-
packed.set(iv);
|
|
28898
|
-
packed.set(ciphertext, iv.byteLength);
|
|
28899
|
-
return encodeBase64Url(packed);
|
|
28900
|
-
}, "encryptFactsCursor");
|
|
28901
|
-
var decryptFactsCursor = /* @__PURE__ */ __name(async (key, encoded) => {
|
|
28902
|
-
try {
|
|
28903
|
-
const packed = decodeBase64Url(encoded);
|
|
28904
|
-
if (packed.byteLength <= 12) {
|
|
28905
|
-
return null;
|
|
28906
|
-
}
|
|
28907
|
-
const plaintext = await crypto.subtle.decrypt({ name: "AES-GCM", iv: packed.slice(0, 12) }, await cursorKey(key), packed.slice(12));
|
|
28908
|
-
return JSON.parse(new TextDecoder().decode(plaintext));
|
|
28909
|
-
} catch {
|
|
28910
|
-
return null;
|
|
28911
|
-
}
|
|
28912
|
-
}, "decryptFactsCursor");
|
|
28913
|
-
var invalidBody = /* @__PURE__ */ __name(() => {
|
|
28914
|
-
return Response.json({ error: "invalid_body" }, { status: 400 });
|
|
28915
|
-
}, "invalidBody");
|
|
28916
|
-
var forbidden = /* @__PURE__ */ __name(() => {
|
|
28917
|
-
return Response.json({ error: "forbidden" }, { status: 403 });
|
|
28918
|
-
}, "forbidden");
|
|
28919
|
-
var factsPermissionDenied = /* @__PURE__ */ __name(() => {
|
|
28920
|
-
return Response.json({ error: "facts_permission_denied" }, { status: 403 });
|
|
28921
|
-
}, "factsPermissionDenied");
|
|
28922
|
-
var handlePush = /* @__PURE__ */ __name(async (env, body) => {
|
|
28923
|
-
if (new TextEncoder().encode(JSON.stringify(body.data ?? null)).length > MAX_PUSH_BYTES) {
|
|
28924
|
-
return invalidBody();
|
|
28925
|
-
}
|
|
28926
|
-
const broadcast = body.broadcast ?? "install";
|
|
28927
|
-
if (broadcast === "install" && body.installId === void 0) {
|
|
28928
|
-
return invalidBody();
|
|
28929
|
-
}
|
|
28930
|
-
const installId = broadcast === "install" ? body.installId : null;
|
|
28931
|
-
if (await rateLimited(env, `${body.slug}:${installId ?? ""}:push`)) {
|
|
28932
|
-
return Response.json({ error: "rate_limited" }, { status: 429 });
|
|
28933
|
-
}
|
|
28934
|
-
const app2 = siteRegistry().registry.apps[body.slug];
|
|
28935
|
-
if (broadcast === "site" && (app2 === void 0 || !app2.events.publishes.includes(body.topic) || !app2.data.includes(SHARED_CONTEXT_CAPABILITY))) {
|
|
28936
|
-
return Response.json({ error: "topic_not_public" }, { status: 400 });
|
|
28937
|
-
}
|
|
28938
|
-
await anonStub(env, body.userId).appPush(
|
|
28939
|
-
{ slug: body.slug, installId, topic: body.topic, data: body.data, scope: broadcast === "site" ? "site" : "app" },
|
|
28940
|
-
ownSite(env),
|
|
28941
|
-
currentTraceContext()
|
|
28942
|
-
);
|
|
28943
|
-
return Response.json({ accepted: true }, { status: 202 });
|
|
28944
|
-
}, "handlePush");
|
|
28945
29089
|
var resolveInstall = /* @__PURE__ */ __name((callee, installId) => {
|
|
28946
29090
|
const active = callee.installs.filter((install) => install.active);
|
|
28947
29091
|
if (installId !== void 0) {
|
|
@@ -28955,6 +29099,14 @@ var calleeFailed = /* @__PURE__ */ __name((callee, reason) => {
|
|
|
28955
29099
|
addSpanEvent("rpc callee failed", { level: "warn", attrs: { app: callee, reason } });
|
|
28956
29100
|
return Response.json({ error: "callee_failed" }, { status: 502 });
|
|
28957
29101
|
}, "calleeFailed");
|
|
29102
|
+
var calleeError = /* @__PURE__ */ __name((callee, error61, message) => {
|
|
29103
|
+
addSpanEvent("rpc callee failed", { level: "warn", attrs: { app: callee, reason: message, code: error61 } });
|
|
29104
|
+
return Response.json({ error: error61, message }, { status: 502 });
|
|
29105
|
+
}, "calleeError");
|
|
29106
|
+
var resultTooLarge = /* @__PURE__ */ __name((candidate) => {
|
|
29107
|
+
const result = candidate?.rpc?.result;
|
|
29108
|
+
return new TextEncoder().encode(JSON.stringify(result ?? null)).length > MAX_RPC_BYTES;
|
|
29109
|
+
}, "resultTooLarge");
|
|
28958
29110
|
var deliverRpcEffects = /* @__PURE__ */ __name(async (env, { body, userId, effects, install }) => {
|
|
28959
29111
|
for (const ord of effects.rejectedOrds) {
|
|
28960
29112
|
addSpanEvent("app effects rejected over the per-install caps", { level: "warn", attrs: { app: body.callee, ord } });
|
|
@@ -28980,10 +29132,32 @@ var deliverRpcEffects = /* @__PURE__ */ __name(async (env, { body, userId, effec
|
|
|
28980
29132
|
}, "ingest")
|
|
28981
29133
|
});
|
|
28982
29134
|
}, "deliverRpcEffects");
|
|
28983
|
-
var
|
|
28984
|
-
|
|
29135
|
+
var calleeQueryTicket = /* @__PURE__ */ __name(async (env, { context, body, callee, install }) => {
|
|
29136
|
+
const keyEnv = env.APP_UI_KEY;
|
|
29137
|
+
const [signing] = Object.entries((keyEnv === void 0 ? null : parseAppUiKeyEnv(keyEnv)) ?? {});
|
|
29138
|
+
if (callee.appId === void 0 || signing === void 0) {
|
|
29139
|
+
return void 0;
|
|
29140
|
+
}
|
|
29141
|
+
const [kid, key] = signing;
|
|
29142
|
+
return mintAppRuntimeQueryTicket({
|
|
29143
|
+
kid,
|
|
29144
|
+
key,
|
|
29145
|
+
now: /* @__PURE__ */ new Date(),
|
|
29146
|
+
slug: body.callee,
|
|
29147
|
+
domainId: context.domainId,
|
|
29148
|
+
appId: callee.appId,
|
|
29149
|
+
appInstallId: install.id,
|
|
29150
|
+
// The portal authorizes a ticket only for the version the site serves, which is the registry's.
|
|
29151
|
+
appVersionId: callee.versionId
|
|
29152
|
+
});
|
|
29153
|
+
}, "calleeQueryTicket");
|
|
29154
|
+
var postToCallee = /* @__PURE__ */ __name(async (env, input2) => {
|
|
29155
|
+
const { context, body, callee, install } = input2;
|
|
29156
|
+
const traffic = body.traffic ?? "hot";
|
|
29157
|
+
const timeoutMs = traffic === "cold" ? RPC_COLD_TIMEOUT_MS : RPC_HOT_TIMEOUT_MS;
|
|
28985
29158
|
try {
|
|
28986
29159
|
const key = await deriveAppInternalKey(context.master, context.domainId, body.callee);
|
|
29160
|
+
const queryTicket = traffic === "cold" ? await calleeQueryTicket(env, input2) : void 0;
|
|
28987
29161
|
const response = await postHookEnvelope(env, {
|
|
28988
29162
|
binding: callee.binding,
|
|
28989
29163
|
key,
|
|
@@ -28997,9 +29171,9 @@ var runRelayRpc = /* @__PURE__ */ __name(async (env, { context, body, callee, in
|
|
|
28997
29171
|
...body.userId === void 0 ? {} : { user: { anonId: body.userId, globalId: null, properties: {} } },
|
|
28998
29172
|
depth: body.depth,
|
|
28999
29173
|
// JSON.stringify drops an undefined key, and the envelope requires `args` to be present.
|
|
29000
|
-
rpc: { caller: body.slug, fn: body.fn, args: body.args ?? null }
|
|
29174
|
+
rpc: { caller: body.slug, fn: body.fn, args: body.args ?? null, traffic, ...queryTicket === void 0 ? {} : { queryTicket } }
|
|
29001
29175
|
},
|
|
29002
|
-
timeoutMs
|
|
29176
|
+
timeoutMs
|
|
29003
29177
|
});
|
|
29004
29178
|
if (response === void 0) {
|
|
29005
29179
|
return calleeFailed(body.callee, "The callee service binding is missing.");
|
|
@@ -29015,13 +29189,27 @@ var runRelayRpc = /* @__PURE__ */ __name(async (env, { context, body, callee, in
|
|
|
29015
29189
|
}
|
|
29016
29190
|
const bytes = await readBoundedBody(response, MAX_RESPONSE_BYTES);
|
|
29017
29191
|
if (bytes === null) {
|
|
29018
|
-
return
|
|
29192
|
+
return calleeError(body.callee, "result_too_large", `The callee response exceeds ${MAX_RESPONSE_BYTES / 1024 / 1024} MB.`);
|
|
29019
29193
|
}
|
|
29020
|
-
|
|
29194
|
+
return new TextDecoder().decode(bytes);
|
|
29021
29195
|
} catch (error61) {
|
|
29196
|
+
if (error61 instanceof Error && error61.name === "TimeoutError") {
|
|
29197
|
+
return calleeError(body.callee, "callee_timeout", `The callee did not answer within ${timeoutMs / 1e3} s.`);
|
|
29198
|
+
}
|
|
29022
29199
|
return calleeFailed(body.callee, String(error61));
|
|
29023
29200
|
}
|
|
29024
|
-
|
|
29201
|
+
}, "postToCallee");
|
|
29202
|
+
var runRelayRpc = /* @__PURE__ */ __name(async (env, input2) => {
|
|
29203
|
+
const { body, install } = input2;
|
|
29204
|
+
const raw2 = await postToCallee(env, input2);
|
|
29205
|
+
if (raw2 instanceof Response) {
|
|
29206
|
+
return raw2;
|
|
29207
|
+
}
|
|
29208
|
+
const candidate = parseJson(raw2);
|
|
29209
|
+
if (resultTooLarge(candidate)) {
|
|
29210
|
+
return calleeError(body.callee, "result_too_large", `The rpc result exceeds ${MAX_RPC_BYTES / 1024} KB.`);
|
|
29211
|
+
}
|
|
29212
|
+
const verdict = validateEnvelopeResponse(candidate, 1);
|
|
29025
29213
|
if (!verdict.ok) {
|
|
29026
29214
|
return calleeFailed(body.callee, verdict.reason);
|
|
29027
29215
|
}
|
|
@@ -29030,6 +29218,9 @@ var runRelayRpc = /* @__PURE__ */ __name(async (env, { context, body, callee, in
|
|
|
29030
29218
|
if (verdict.errorCode === "invalid_arguments") {
|
|
29031
29219
|
return Response.json({ error: "invalid_arguments", message: reason }, { status: 400 });
|
|
29032
29220
|
}
|
|
29221
|
+
if (verdict.errorCode === "rpc_error") {
|
|
29222
|
+
return calleeError(body.callee, "rpc_error", reason);
|
|
29223
|
+
}
|
|
29033
29224
|
return calleeFailed(body.callee, reason);
|
|
29034
29225
|
}
|
|
29035
29226
|
if (verdict.rpc === null) {
|
|
@@ -29058,7 +29249,8 @@ var relayRpc = /* @__PURE__ */ __name(async (env, input2) => {
|
|
|
29058
29249
|
"app.install_ord": install.ord,
|
|
29059
29250
|
"app.install_label": install.label,
|
|
29060
29251
|
"app.rpc_caller": body.slug,
|
|
29061
|
-
"app.rpc_depth": body.depth
|
|
29252
|
+
"app.rpc_depth": body.depth,
|
|
29253
|
+
"app.rpc_traffic": body.traffic ?? "hot"
|
|
29062
29254
|
}
|
|
29063
29255
|
},
|
|
29064
29256
|
() => {
|
|
@@ -29072,24 +29264,223 @@ var handleRpc = /* @__PURE__ */ __name(async (env, context, body) => {
|
|
|
29072
29264
|
}
|
|
29073
29265
|
const callee = siteRegistry().registry.apps[body.callee];
|
|
29074
29266
|
if (callee === void 0) {
|
|
29075
|
-
return forbidden
|
|
29267
|
+
return Response.json({ error: "forbidden" }, { status: 403 });
|
|
29268
|
+
}
|
|
29269
|
+
const visibility = callee.rpc.exposes[body.fn];
|
|
29270
|
+
if (visibility !== "public" && !(visibility === "private" && (callee.rpc.grants[body.slug] ?? []).includes(body.fn))) {
|
|
29271
|
+
return Response.json({ error: "forbidden" }, { status: 403 });
|
|
29272
|
+
}
|
|
29273
|
+
const install = resolveInstall(callee, body.installId);
|
|
29274
|
+
if (install === "invalid_install" || install === "install_required") {
|
|
29275
|
+
return Response.json({ error: install }, { status: 400 });
|
|
29276
|
+
}
|
|
29277
|
+
if (new TextEncoder().encode(JSON.stringify(body.args ?? null)).length > MAX_RPC_BYTES) {
|
|
29278
|
+
return Response.json({ error: "too_large" }, { status: 413 });
|
|
29279
|
+
}
|
|
29280
|
+
if (await rateLimited(env, `${body.slug}:${install.id}:rpc`)) {
|
|
29281
|
+
return Response.json({ error: "rate_limited" }, { status: 429 });
|
|
29282
|
+
}
|
|
29283
|
+
return relayRpc(env, { context, body, callee, install });
|
|
29284
|
+
}, "handleRpc");
|
|
29285
|
+
|
|
29286
|
+
// src/apps/broker-track.ts
|
|
29287
|
+
var trackBody = external_exports.object({
|
|
29288
|
+
...identityFields,
|
|
29289
|
+
kind: external_exports.literal("track"),
|
|
29290
|
+
source: external_exports.enum(APP_HANDLER_SOURCES),
|
|
29291
|
+
userId: external_exports.string().min(1).max(64),
|
|
29292
|
+
installId: external_exports.string().min(1).max(64),
|
|
29293
|
+
installs: installRuleSchema.optional(),
|
|
29294
|
+
event: emitSchema.omit({ installs: true })
|
|
29295
|
+
}).strict();
|
|
29296
|
+
var handleTrack = /* @__PURE__ */ __name(async (env, body) => {
|
|
29297
|
+
const app2 = siteRegistry().registry.apps[body.slug];
|
|
29298
|
+
if (app2 === void 0) {
|
|
29299
|
+
return Response.json({ error: "forbidden" }, { status: 403 });
|
|
29300
|
+
}
|
|
29301
|
+
if (!app2.installs.some((install) => install.active && install.id === body.installId)) {
|
|
29302
|
+
return Response.json({ error: "invalid_install" }, { status: 400 });
|
|
29303
|
+
}
|
|
29304
|
+
const parsed = emitPayloadSchema.safeParse(body.event.payload);
|
|
29305
|
+
if (!parsed.success) {
|
|
29306
|
+
return Response.json({ error: "invalid_event" }, { status: 400 });
|
|
29307
|
+
}
|
|
29308
|
+
if (!isCanonicalId(body.userId)) {
|
|
29309
|
+
return Response.json({ error: "invalid_user" }, { status: 400 });
|
|
29310
|
+
}
|
|
29311
|
+
if (await rateLimited(env, `${body.slug}:${body.installId}:track`)) {
|
|
29312
|
+
return Response.json({ error: "rate_limited" }, { status: 429 });
|
|
29313
|
+
}
|
|
29314
|
+
const site = ownSite(env);
|
|
29315
|
+
if (!await anonStub(env, body.userId).appAllowed(body.slug, site, currentTraceContext())) {
|
|
29316
|
+
return Response.json({ error: "forbidden" }, { status: 403 });
|
|
29317
|
+
}
|
|
29318
|
+
const { event, eventId, properties: properties2 } = parsed.data;
|
|
29319
|
+
const action = buildAction({
|
|
29320
|
+
type: "track",
|
|
29321
|
+
userId: body.userId,
|
|
29322
|
+
source: body.source,
|
|
29323
|
+
payload: { event, eventId: emittedEventId(eventId, properties2), properties: properties2 ?? {} },
|
|
29324
|
+
context: { source: "app", occurredAt: body.event.occurredAt, offline: false, replay: false },
|
|
29325
|
+
...body.installs === void 0 ? {} : { installs: body.installs }
|
|
29326
|
+
});
|
|
29327
|
+
await ingest(env, action, site);
|
|
29328
|
+
return Response.json({ accepted: true }, { status: 202 });
|
|
29329
|
+
}, "handleTrack");
|
|
29330
|
+
|
|
29331
|
+
// src/apps/broker.ts
|
|
29332
|
+
var BROKER_PATHS = ["/__qinto/push", "/__qinto/rpc"];
|
|
29333
|
+
var pushBody = external_exports.object({
|
|
29334
|
+
...identityFields,
|
|
29335
|
+
kind: external_exports.literal("push"),
|
|
29336
|
+
userId: external_exports.string().min(1).max(64),
|
|
29337
|
+
installId: external_exports.string().min(1).max(64).optional(),
|
|
29338
|
+
broadcast: external_exports.enum(PUSH_BROADCASTS).optional(),
|
|
29339
|
+
topic: external_exports.string().min(1).max(64),
|
|
29340
|
+
data: external_exports.unknown().optional()
|
|
29341
|
+
}).strict();
|
|
29342
|
+
var installsBody = external_exports.object({
|
|
29343
|
+
...identityFields,
|
|
29344
|
+
kind: external_exports.literal("installs"),
|
|
29345
|
+
callee: external_exports.string().min(1).max(30)
|
|
29346
|
+
}).strict();
|
|
29347
|
+
var activeBody = external_exports.object({
|
|
29348
|
+
...identityFields,
|
|
29349
|
+
kind: external_exports.literal("active"),
|
|
29350
|
+
ord: external_exports.number().int().min(1),
|
|
29351
|
+
userId: external_exports.string().min(1).max(64).optional()
|
|
29352
|
+
}).strict();
|
|
29353
|
+
var contextReadBody = external_exports.object({
|
|
29354
|
+
...identityFields,
|
|
29355
|
+
kind: external_exports.literal("contextRead"),
|
|
29356
|
+
userId: external_exports.string().min(1).max(64)
|
|
29357
|
+
}).strict();
|
|
29358
|
+
var contextWriteBody = external_exports.object({
|
|
29359
|
+
...identityFields,
|
|
29360
|
+
kind: external_exports.literal("contextWrite"),
|
|
29361
|
+
userId: external_exports.string().min(1).max(64),
|
|
29362
|
+
writes: external_exports.array(contextWriteSchema).min(1).max(MAX_CONTEXT_WRITES_PER_INSTALL)
|
|
29363
|
+
}).strict();
|
|
29364
|
+
var factsQuery = external_exports.object({
|
|
29365
|
+
kinds: external_exports.array(external_exports.enum(APP_FACT_KINDS)).min(1).max(APP_FACT_KINDS.length).optional(),
|
|
29366
|
+
observedAtGte: external_exports.number().int().nonnegative().safe().optional(),
|
|
29367
|
+
observedAtLt: external_exports.number().int().nonnegative().safe().optional(),
|
|
29368
|
+
limit: external_exports.number().int().min(1).max(MAX_APP_FACTS_PER_PAGE).optional()
|
|
29369
|
+
}).strict().refine((query) => query.observedAtGte === void 0 || query.observedAtLt === void 0 || query.observedAtGte < query.observedAtLt);
|
|
29370
|
+
var factsReadBody = external_exports.object({
|
|
29371
|
+
...identityFields,
|
|
29372
|
+
kind: external_exports.literal("factsRead"),
|
|
29373
|
+
userId: external_exports.string().min(1).max(64),
|
|
29374
|
+
query: factsQuery.optional(),
|
|
29375
|
+
cursor: external_exports.string().min(1).max(4096).optional()
|
|
29376
|
+
}).strict().refine((body) => body.query === void 0 !== (body.cursor === void 0));
|
|
29377
|
+
var identityReadBody = external_exports.object({
|
|
29378
|
+
...identityFields,
|
|
29379
|
+
kind: external_exports.literal("identityRead"),
|
|
29380
|
+
identifier: identifierField2,
|
|
29381
|
+
cursor: external_exports.string().regex(/^\d+$/).optional()
|
|
29382
|
+
}).strict();
|
|
29383
|
+
var brokerBody = external_exports.discriminatedUnion("kind", [
|
|
29384
|
+
pushBody,
|
|
29385
|
+
rpcBody,
|
|
29386
|
+
installsBody,
|
|
29387
|
+
activeBody,
|
|
29388
|
+
contextReadBody,
|
|
29389
|
+
contextWriteBody,
|
|
29390
|
+
factsReadBody,
|
|
29391
|
+
identityReadBody,
|
|
29392
|
+
identityUpsertBody,
|
|
29393
|
+
currencyRatesBody,
|
|
29394
|
+
trackBody
|
|
29395
|
+
]);
|
|
29396
|
+
var BODY_ENVELOPE_ALLOWANCE_BYTES = 2 * 1024;
|
|
29397
|
+
var MAX_BODY_BYTES = MAX_RPC_BYTES + BODY_ENVELOPE_ALLOWANCE_BYTES;
|
|
29398
|
+
var parseJson2 = /* @__PURE__ */ __name((raw2) => {
|
|
29399
|
+
try {
|
|
29400
|
+
return JSON.parse(raw2);
|
|
29401
|
+
} catch {
|
|
29402
|
+
return null;
|
|
29403
|
+
}
|
|
29404
|
+
}, "parseJson");
|
|
29405
|
+
var factsCursor = external_exports.object({
|
|
29406
|
+
userId: external_exports.string().min(1).max(64),
|
|
29407
|
+
kinds: external_exports.array(external_exports.enum(APP_FACT_KINDS)).min(1).max(APP_FACT_KINDS.length),
|
|
29408
|
+
observedAtGte: external_exports.number().int().nonnegative().safe().nullable(),
|
|
29409
|
+
observedAtLt: external_exports.number().int().nonnegative().safe().nullable(),
|
|
29410
|
+
limit: external_exports.number().int().min(1).max(MAX_APP_FACTS_PER_PAGE),
|
|
29411
|
+
afterRowId: external_exports.number().int().nonnegative().safe(),
|
|
29412
|
+
maxRowId: external_exports.number().int().positive().safe().nullable()
|
|
29413
|
+
}).strict();
|
|
29414
|
+
var cursorKey = /* @__PURE__ */ __name(async (hex3) => {
|
|
29415
|
+
const bytes = Uint8Array.from(hex3.matchAll(/.{2}/g), ([pair]) => Number.parseInt(pair, 16));
|
|
29416
|
+
return crypto.subtle.importKey("raw", bytes, "AES-GCM", false, ["encrypt", "decrypt"]);
|
|
29417
|
+
}, "cursorKey");
|
|
29418
|
+
var encodeBase64Url = /* @__PURE__ */ __name((bytes) => {
|
|
29419
|
+
let binary = "";
|
|
29420
|
+
for (const byte of bytes) {
|
|
29421
|
+
binary += String.fromCharCode(byte);
|
|
29076
29422
|
}
|
|
29077
|
-
|
|
29078
|
-
|
|
29079
|
-
|
|
29423
|
+
return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
|
|
29424
|
+
}, "encodeBase64Url");
|
|
29425
|
+
var decodeBase64Url = /* @__PURE__ */ __name((value) => {
|
|
29426
|
+
const base643 = value.replaceAll("-", "+").replaceAll("_", "/");
|
|
29427
|
+
const padded = base643.padEnd(Math.ceil(base643.length / 4) * 4, "=");
|
|
29428
|
+
const binary = atob(padded);
|
|
29429
|
+
return Uint8Array.from(binary, (character) => character.charCodeAt(0));
|
|
29430
|
+
}, "decodeBase64Url");
|
|
29431
|
+
var encryptFactsCursor = /* @__PURE__ */ __name(async (key, cursor) => {
|
|
29432
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
29433
|
+
const plaintext = new TextEncoder().encode(JSON.stringify(cursor));
|
|
29434
|
+
const ciphertext = new Uint8Array(await crypto.subtle.encrypt({ name: "AES-GCM", iv }, await cursorKey(key), plaintext));
|
|
29435
|
+
const packed = new Uint8Array(iv.byteLength + ciphertext.byteLength);
|
|
29436
|
+
packed.set(iv);
|
|
29437
|
+
packed.set(ciphertext, iv.byteLength);
|
|
29438
|
+
return encodeBase64Url(packed);
|
|
29439
|
+
}, "encryptFactsCursor");
|
|
29440
|
+
var decryptFactsCursor = /* @__PURE__ */ __name(async (key, encoded) => {
|
|
29441
|
+
try {
|
|
29442
|
+
const packed = decodeBase64Url(encoded);
|
|
29443
|
+
if (packed.byteLength <= 12) {
|
|
29444
|
+
return null;
|
|
29445
|
+
}
|
|
29446
|
+
const plaintext = await crypto.subtle.decrypt({ name: "AES-GCM", iv: packed.slice(0, 12) }, await cursorKey(key), packed.slice(12));
|
|
29447
|
+
return JSON.parse(new TextDecoder().decode(plaintext));
|
|
29448
|
+
} catch {
|
|
29449
|
+
return null;
|
|
29080
29450
|
}
|
|
29081
|
-
|
|
29082
|
-
|
|
29083
|
-
|
|
29451
|
+
}, "decryptFactsCursor");
|
|
29452
|
+
var invalidBody = /* @__PURE__ */ __name(() => {
|
|
29453
|
+
return Response.json({ error: "invalid_body" }, { status: 400 });
|
|
29454
|
+
}, "invalidBody");
|
|
29455
|
+
var forbidden = /* @__PURE__ */ __name(() => {
|
|
29456
|
+
return Response.json({ error: "forbidden" }, { status: 403 });
|
|
29457
|
+
}, "forbidden");
|
|
29458
|
+
var factsPermissionDenied = /* @__PURE__ */ __name(() => {
|
|
29459
|
+
return Response.json({ error: "facts_permission_denied" }, { status: 403 });
|
|
29460
|
+
}, "factsPermissionDenied");
|
|
29461
|
+
var handlePush = /* @__PURE__ */ __name(async (env, body) => {
|
|
29462
|
+
if (new TextEncoder().encode(JSON.stringify(body.data ?? null)).length > MAX_PUSH_BYTES) {
|
|
29463
|
+
return invalidBody();
|
|
29084
29464
|
}
|
|
29085
|
-
|
|
29086
|
-
|
|
29465
|
+
const broadcast = body.broadcast ?? "install";
|
|
29466
|
+
if (broadcast === "install" && body.installId === void 0) {
|
|
29467
|
+
return invalidBody();
|
|
29087
29468
|
}
|
|
29088
|
-
|
|
29469
|
+
const installId = broadcast === "install" ? body.installId : null;
|
|
29470
|
+
if (await rateLimited(env, `${body.slug}:${installId ?? ""}:push`)) {
|
|
29089
29471
|
return Response.json({ error: "rate_limited" }, { status: 429 });
|
|
29090
29472
|
}
|
|
29091
|
-
|
|
29092
|
-
|
|
29473
|
+
const app2 = siteRegistry().registry.apps[body.slug];
|
|
29474
|
+
if (broadcast === "site" && (app2 === void 0 || !app2.events.publishes.includes(body.topic) || !app2.data.includes(SHARED_CONTEXT_CAPABILITY))) {
|
|
29475
|
+
return Response.json({ error: "topic_not_public" }, { status: 400 });
|
|
29476
|
+
}
|
|
29477
|
+
await anonStub(env, body.userId).appPush(
|
|
29478
|
+
{ slug: body.slug, installId, topic: body.topic, data: body.data, scope: broadcast === "site" ? "site" : "app" },
|
|
29479
|
+
ownSite(env),
|
|
29480
|
+
currentTraceContext()
|
|
29481
|
+
);
|
|
29482
|
+
return Response.json({ accepted: true }, { status: 202 });
|
|
29483
|
+
}, "handlePush");
|
|
29093
29484
|
var handleInstalls = /* @__PURE__ */ __name(async (env, body) => {
|
|
29094
29485
|
const callee = siteRegistry().registry.apps[body.callee];
|
|
29095
29486
|
if (callee === void 0 || !(Object.values(callee.rpc.exposes).includes("public") || (callee.rpc.grants[body.slug] ?? []).length > 0)) {
|
|
@@ -29188,7 +29579,7 @@ var handleBroker = /* @__PURE__ */ __name(async (request, env, ctx) => {
|
|
|
29188
29579
|
if (bytes === null) {
|
|
29189
29580
|
return Response.json({ error: "too_large" }, { status: 413 });
|
|
29190
29581
|
}
|
|
29191
|
-
const candidate =
|
|
29582
|
+
const candidate = parseJson2(new TextDecoder().decode(bytes));
|
|
29192
29583
|
const parsed = brokerBody.safeParse(candidate);
|
|
29193
29584
|
if (!parsed.success) {
|
|
29194
29585
|
if (typeof candidate === "object" && candidate !== null && "kind" in candidate && candidate.kind === "factsRead") {
|
|
@@ -29367,7 +29758,7 @@ var textBody = /* @__PURE__ */ __name(async (request) => {
|
|
|
29367
29758
|
const bytes = await readBoundedBody(request, MAX_CONTROL_BYTES);
|
|
29368
29759
|
return bytes === null ? null : new TextDecoder().decode(bytes);
|
|
29369
29760
|
}, "textBody");
|
|
29370
|
-
var
|
|
29761
|
+
var parseJson3 = /* @__PURE__ */ __name((value) => {
|
|
29371
29762
|
try {
|
|
29372
29763
|
return JSON.parse(value);
|
|
29373
29764
|
} catch {
|
|
@@ -29396,7 +29787,7 @@ var handleControl = /* @__PURE__ */ __name(async (request, env) => {
|
|
|
29396
29787
|
if (authentication === null) {
|
|
29397
29788
|
return Response.json({ error: "unauthorized" }, { status: 401 });
|
|
29398
29789
|
}
|
|
29399
|
-
const raw2 =
|
|
29790
|
+
const raw2 = parseJson3(body);
|
|
29400
29791
|
if (raw2 === void 0) {
|
|
29401
29792
|
return Response.json({ error: "invalid_body" }, { status: 400 });
|
|
29402
29793
|
}
|
|
@@ -29434,7 +29825,7 @@ var handleControl = /* @__PURE__ */ __name(async (request, env) => {
|
|
|
29434
29825
|
if (responseBytes === null) {
|
|
29435
29826
|
return Response.json({ error: "invalid_response" }, { status: 502 });
|
|
29436
29827
|
}
|
|
29437
|
-
const responseBody =
|
|
29828
|
+
const responseBody = parseJson3(new TextDecoder().decode(responseBytes));
|
|
29438
29829
|
if (responseBody === void 0) {
|
|
29439
29830
|
return Response.json({ error: "invalid_response" }, { status: 502 });
|
|
29440
29831
|
}
|
|
@@ -29765,7 +30156,7 @@ var streamWithDeadlines = /* @__PURE__ */ __name((source, { idleMs, totalMs }) =
|
|
|
29765
30156
|
}, "streamWithDeadlines");
|
|
29766
30157
|
|
|
29767
30158
|
// src/helpers/bot-check.ts
|
|
29768
|
-
var botRejected = /* @__PURE__ */ __name((env, geo) => {
|
|
30159
|
+
var botRejected = /* @__PURE__ */ __name((env, request, geo) => {
|
|
29769
30160
|
return traced("bot.check", {}, () => {
|
|
29770
30161
|
const { config: protection, error: error61 } = parseBotProtection(env.BOT_PROTECTION);
|
|
29771
30162
|
if (error61 !== void 0) {
|
|
@@ -29774,8 +30165,19 @@ var botRejected = /* @__PURE__ */ __name((env, geo) => {
|
|
|
29774
30165
|
attrs: { error: String(error61) }
|
|
29775
30166
|
});
|
|
29776
30167
|
}
|
|
29777
|
-
const
|
|
29778
|
-
|
|
30168
|
+
const cf = request.cf;
|
|
30169
|
+
const verifiedBot = cf?.botManagement?.verifiedBot;
|
|
30170
|
+
const rejected = isRequestBlocked(protection, {
|
|
30171
|
+
botScore: geo.botScore,
|
|
30172
|
+
verifiedBot,
|
|
30173
|
+
country: geo.country,
|
|
30174
|
+
isEU: geo.isEUCountry === "yes"
|
|
30175
|
+
});
|
|
30176
|
+
setSpanAttributes({
|
|
30177
|
+
"bot.blocked": rejected,
|
|
30178
|
+
...geo.botScore === void 0 ? {} : { "bot.score": geo.botScore },
|
|
30179
|
+
...verifiedBot === void 0 ? {} : { "bot.verified": verifiedBot }
|
|
30180
|
+
});
|
|
29779
30181
|
return rejected;
|
|
29780
30182
|
});
|
|
29781
30183
|
}, "botRejected");
|
|
@@ -29865,7 +30267,7 @@ var withCors = /* @__PURE__ */ __name((response, request, method) => {
|
|
|
29865
30267
|
}, "withCors");
|
|
29866
30268
|
var authorize = /* @__PURE__ */ __name(async (c, { slug, app: app2 }) => {
|
|
29867
30269
|
const geo = geoFromRequest(c.req.raw);
|
|
29868
|
-
if (await botRejected(c.env, geo)) {
|
|
30270
|
+
if (await botRejected(c.env, c.req.raw, geo)) {
|
|
29869
30271
|
return refuse(403, "traffic_rejected");
|
|
29870
30272
|
}
|
|
29871
30273
|
const anonId = getCookie(c, COOKIE_NAME2);
|
|
@@ -29999,7 +30401,7 @@ var passThroughResponse = /* @__PURE__ */ __name((upstream) => {
|
|
|
29999
30401
|
});
|
|
30000
30402
|
return new Response(body, { status: upstream.status, headers });
|
|
30001
30403
|
}, "passThroughResponse");
|
|
30002
|
-
var
|
|
30404
|
+
var parseJson4 = /* @__PURE__ */ __name((text) => {
|
|
30003
30405
|
try {
|
|
30004
30406
|
return JSON.parse(text);
|
|
30005
30407
|
} catch {
|
|
@@ -30014,7 +30416,7 @@ var webhookAck = /* @__PURE__ */ __name(async (slug, upstream) => {
|
|
|
30014
30416
|
if (bytes === null) {
|
|
30015
30417
|
return upstreamFailed(slug, "The endpoint outcomes exceed the size cap.");
|
|
30016
30418
|
}
|
|
30017
|
-
const parsed = endpointOutcomesSchema.safeParse(
|
|
30419
|
+
const parsed = endpointOutcomesSchema.safeParse(parseJson4(new TextDecoder().decode(bytes)));
|
|
30018
30420
|
if (!parsed.success) {
|
|
30019
30421
|
return upstreamFailed(slug, "The endpoint outcomes are malformed.");
|
|
30020
30422
|
}
|
|
@@ -30064,7 +30466,7 @@ var runForwardToApp = /* @__PURE__ */ __name(async (c, input2) => {
|
|
|
30064
30466
|
method: endpoint.method,
|
|
30065
30467
|
mode: endpoint.mode,
|
|
30066
30468
|
version: app2.version,
|
|
30067
|
-
...app2.data.includes("geo") && requestGeo !== void 0 ? { geo: requestGeo } : {},
|
|
30469
|
+
...app2.data.includes("geo") && requestGeo !== void 0 ? { geo: cfDataFrom(requestGeo) } : {},
|
|
30068
30470
|
installs: installs.map((install) => ({ id: install.id, ord: install.ord, label: install.label })),
|
|
30069
30471
|
...anonId === void 0 ? {} : { user: { anonId } }
|
|
30070
30472
|
})
|
|
@@ -30072,286 +30474,91 @@ var runForwardToApp = /* @__PURE__ */ __name(async (c, input2) => {
|
|
|
30072
30474
|
let upstream;
|
|
30073
30475
|
try {
|
|
30074
30476
|
const url2 = `https://app/__qinto/endpoint${endpoint.route}${search}`;
|
|
30075
|
-
const init = { method: endpoint.method, headers, body };
|
|
30076
|
-
upstream = endpoint.mode === "api" ? await fetchApiResponse(service, url2, init) : await service.fetch(url2, { ...init, signal: AbortSignal.timeout(WEBHOOK_ENDPOINT_TIMEOUT_MS) });
|
|
30077
|
-
} catch (error61) {
|
|
30078
|
-
return upstreamFailed(slug, String(error61));
|
|
30079
|
-
}
|
|
30080
|
-
adoptAppSpans(upstream);
|
|
30081
|
-
writeAppObservability(c.env, upstream, {
|
|
30082
|
-
app: observabilityApp(slug, app2),
|
|
30083
|
-
installs,
|
|
30084
|
-
budget: createDataPointBudget()
|
|
30085
|
-
});
|
|
30086
|
-
setSpanAttributes({ "app.status": upstream.status });
|
|
30087
|
-
return endpoint.mode === "api" ? passThroughResponse(upstream) : webhookAck(slug, upstream);
|
|
30088
|
-
}, "runForwardToApp");
|
|
30089
|
-
var forwardToApp = /* @__PURE__ */ __name(async (c, input2) => {
|
|
30090
|
-
const { slug, app: app2, endpoint, installs } = input2;
|
|
30091
|
-
return traced(
|
|
30092
|
-
`app.endpoint ${slug}`,
|
|
30093
|
-
{
|
|
30094
|
-
attrs: {
|
|
30095
|
-
"app.slug": slug,
|
|
30096
|
-
"app.version": app2.version,
|
|
30097
|
-
"app.binding": app2.binding,
|
|
30098
|
-
"app.hook": `endpoint.${endpoint.method} ${endpoint.route}`,
|
|
30099
|
-
"app.endpoint_mode": endpoint.mode,
|
|
30100
|
-
"app.installs": installs.length
|
|
30101
|
-
}
|
|
30102
|
-
},
|
|
30103
|
-
() => {
|
|
30104
|
-
return runForwardToApp(c, input2);
|
|
30105
|
-
}
|
|
30106
|
-
);
|
|
30107
|
-
}, "forwardToApp");
|
|
30108
|
-
var serveEndpoint = /* @__PURE__ */ __name(async (c, input2) => {
|
|
30109
|
-
const { slug, app: app2, endpoint, search } = input2;
|
|
30110
|
-
const refusal = await authorizeEndpoint(c, slug, endpoint);
|
|
30111
|
-
if (refusal !== void 0) {
|
|
30112
|
-
return refusal;
|
|
30113
|
-
}
|
|
30114
|
-
const installs = app2.installs.filter((install) => install.active);
|
|
30115
|
-
const [first] = installs;
|
|
30116
|
-
if (first === void 0) {
|
|
30117
|
-
return refuse2(503, "inactive");
|
|
30118
|
-
}
|
|
30119
|
-
const key = endpoint.mode === "api" ? `${slug}:${first.id}:endpoint` : `${slug}:${endpoint.route}:endpoint`;
|
|
30120
|
-
if (await rateLimited(c.env, key)) {
|
|
30121
|
-
return refuse2(429, "rate_limited");
|
|
30122
|
-
}
|
|
30123
|
-
let body;
|
|
30124
|
-
if (endpoint.method !== "GET") {
|
|
30125
|
-
const bytes = await readBoundedBody(c.req.raw, MAX_ENDPOINT_BODY_BYTES);
|
|
30126
|
-
if (bytes === null) {
|
|
30127
|
-
return refuse2(413, "too_large");
|
|
30128
|
-
}
|
|
30129
|
-
body = bytes;
|
|
30130
|
-
}
|
|
30131
|
-
return forwardToApp(c, { slug, app: app2, endpoint, installs, body, search });
|
|
30132
|
-
}, "serveEndpoint");
|
|
30133
|
-
var handleAppEndpoint = /* @__PURE__ */ __name(async (c, slug) => {
|
|
30134
|
-
const app2 = siteRegistry().registry.apps[slug];
|
|
30135
|
-
if (app2 === void 0) {
|
|
30136
|
-
return refuse2(404, "not_found");
|
|
30137
|
-
}
|
|
30138
|
-
const url2 = new URL(c.req.url);
|
|
30139
|
-
const prefix = `/app/${slug}`;
|
|
30140
|
-
if (url2.pathname !== prefix && !url2.pathname.startsWith(`${prefix}/`)) {
|
|
30141
|
-
return refuse2(404, "not_found");
|
|
30142
|
-
}
|
|
30143
|
-
const preflight2 = c.req.method === "OPTIONS";
|
|
30144
|
-
const method = preflight2 ? c.req.header("access-control-request-method") : c.req.method;
|
|
30145
|
-
const endpoint = matchEndpoint(app2, url2.pathname.slice(prefix.length) || "/", method ?? "");
|
|
30146
|
-
if (endpoint === void 0 || preflight2 && endpoint.auth !== "visitor") {
|
|
30147
|
-
return refuse2(404, "not_found");
|
|
30148
|
-
}
|
|
30149
|
-
if (endpoint.auth === "visitor") {
|
|
30150
|
-
return handleVisitorEndpoint(c, { slug, app: app2, method: endpoint.method, preflight: preflight2 }, (visitor, body) => {
|
|
30151
|
-
return forwardToApp(c, { slug, app: app2, endpoint, installs: [visitor.install], body, search: url2.search, anonId: visitor.anonId });
|
|
30152
|
-
});
|
|
30153
|
-
}
|
|
30154
|
-
return serveEndpoint(c, { slug, app: app2, endpoint, search: url2.search });
|
|
30155
|
-
}, "handleAppEndpoint");
|
|
30156
|
-
var appEndpointRoutes = new Hono2();
|
|
30157
|
-
appEndpointRoutes.all("/app/:slug/*", (c) => {
|
|
30158
|
-
return handleAppEndpoint(c, c.req.param("slug"));
|
|
30159
|
-
});
|
|
30160
|
-
|
|
30161
|
-
// ../../libs/app-ui-token/src/base64url.ts
|
|
30162
|
-
var base64UrlEncode = /* @__PURE__ */ __name((bytes) => {
|
|
30163
|
-
let binary = "";
|
|
30164
|
-
for (const byte of bytes) {
|
|
30165
|
-
binary += String.fromCharCode(byte);
|
|
30166
|
-
}
|
|
30167
|
-
return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
|
|
30168
|
-
}, "base64UrlEncode");
|
|
30169
|
-
var base64UrlDecode = /* @__PURE__ */ __name((text) => {
|
|
30170
|
-
const padded = text.replaceAll("-", "+").replaceAll("_", "/").padEnd(text.length + (4 - text.length % 4) % 4, "=");
|
|
30171
|
-
let binary;
|
|
30172
|
-
try {
|
|
30173
|
-
binary = atob(padded);
|
|
30174
|
-
} catch {
|
|
30175
|
-
return null;
|
|
30176
|
-
}
|
|
30177
|
-
const bytes = new Uint8Array(binary.length);
|
|
30178
|
-
for (let index = 0; index < binary.length; index += 1) {
|
|
30179
|
-
bytes[index] = binary.charCodeAt(index);
|
|
30180
|
-
}
|
|
30181
|
-
return bytes;
|
|
30182
|
-
}, "base64UrlDecode");
|
|
30183
|
-
|
|
30184
|
-
// ../../libs/app-ui-token/src/env.ts
|
|
30185
|
-
var envSchema = external_exports.object({
|
|
30186
|
-
v: external_exports.literal(1),
|
|
30187
|
-
keys: external_exports.record(external_exports.string().min(1).max(16), external_exports.string().min(1))
|
|
30188
|
-
}).strict();
|
|
30189
|
-
var parseAppUiKeyEnv = /* @__PURE__ */ __name((raw2) => {
|
|
30190
|
-
let json2;
|
|
30191
|
-
try {
|
|
30192
|
-
json2 = JSON.parse(raw2);
|
|
30193
|
-
} catch {
|
|
30194
|
-
return null;
|
|
30195
|
-
}
|
|
30196
|
-
const parsed = envSchema.safeParse(json2);
|
|
30197
|
-
if (!parsed.success) {
|
|
30198
|
-
return null;
|
|
30199
|
-
}
|
|
30200
|
-
const keys = {};
|
|
30201
|
-
for (const [kid, encoded] of Object.entries(parsed.data.keys)) {
|
|
30202
|
-
const bytes = base64UrlDecode(encoded);
|
|
30203
|
-
if (bytes === null) {
|
|
30204
|
-
return null;
|
|
30205
|
-
}
|
|
30206
|
-
keys[kid] = bytes;
|
|
30207
|
-
}
|
|
30208
|
-
return keys;
|
|
30209
|
-
}, "parseAppUiKeyEnv");
|
|
30210
|
-
|
|
30211
|
-
// ../../libs/app-ui-token/src/jwt.ts
|
|
30212
|
-
var headerSchema = external_exports.object({
|
|
30213
|
-
alg: external_exports.literal("HS256"),
|
|
30214
|
-
typ: external_exports.literal("JWT"),
|
|
30215
|
-
kid: external_exports.string().min(1).max(16)
|
|
30216
|
-
}).strict();
|
|
30217
|
-
var encodeJsonPart = /* @__PURE__ */ __name((value) => {
|
|
30218
|
-
return base64UrlEncode(new TextEncoder().encode(JSON.stringify(value)));
|
|
30219
|
-
}, "encodeJsonPart");
|
|
30220
|
-
var parseJsonPart = /* @__PURE__ */ __name((part) => {
|
|
30221
|
-
const bytes = base64UrlDecode(part);
|
|
30222
|
-
if (bytes === null) {
|
|
30223
|
-
return null;
|
|
30224
|
-
}
|
|
30225
|
-
try {
|
|
30226
|
-
return JSON.parse(new TextDecoder().decode(bytes));
|
|
30227
|
-
} catch {
|
|
30228
|
-
return null;
|
|
30229
|
-
}
|
|
30230
|
-
}, "parseJsonPart");
|
|
30231
|
-
var hmacKey = /* @__PURE__ */ __name((key, usage) => {
|
|
30232
|
-
return crypto.subtle.importKey("raw", key, { name: "HMAC", hash: "SHA-256" }, false, [usage]);
|
|
30233
|
-
}, "hmacKey");
|
|
30234
|
-
var signCompact = /* @__PURE__ */ __name(async (kid, key, claims) => {
|
|
30235
|
-
const signingInput = `${encodeJsonPart({ alg: "HS256", typ: "JWT", kid })}.${encodeJsonPart(claims)}`;
|
|
30236
|
-
const signature = await crypto.subtle.sign("HMAC", await hmacKey(key, "sign"), new TextEncoder().encode(signingInput));
|
|
30237
|
-
return `${signingInput}.${base64UrlEncode(new Uint8Array(signature))}`;
|
|
30238
|
-
}, "signCompact");
|
|
30239
|
-
var verifyCompactSignature = /* @__PURE__ */ __name(async (token, keys) => {
|
|
30240
|
-
const [headerPart, claimsPart, signaturePart, extra] = token.split(".");
|
|
30241
|
-
if (headerPart === void 0 || claimsPart === void 0 || signaturePart === void 0 || extra !== void 0) {
|
|
30242
|
-
return { ok: false, reason: "malformed" };
|
|
30243
|
-
}
|
|
30244
|
-
const header = headerSchema.safeParse(parseJsonPart(headerPart));
|
|
30245
|
-
if (!header.success) {
|
|
30246
|
-
return { ok: false, reason: "malformed" };
|
|
30247
|
-
}
|
|
30248
|
-
const key = keys[header.data.kid];
|
|
30249
|
-
if (key === void 0) {
|
|
30250
|
-
return { ok: false, reason: "unknown_kid" };
|
|
30251
|
-
}
|
|
30252
|
-
const signature = base64UrlDecode(signaturePart);
|
|
30253
|
-
if (signature === null) {
|
|
30254
|
-
return { ok: false, reason: "malformed" };
|
|
30477
|
+
const init = { method: endpoint.method, headers, body };
|
|
30478
|
+
upstream = endpoint.mode === "api" ? await fetchApiResponse(service, url2, init) : await service.fetch(url2, { ...init, signal: AbortSignal.timeout(WEBHOOK_ENDPOINT_TIMEOUT_MS) });
|
|
30479
|
+
} catch (error61) {
|
|
30480
|
+
return upstreamFailed(slug, String(error61));
|
|
30255
30481
|
}
|
|
30256
|
-
|
|
30257
|
-
|
|
30258
|
-
|
|
30259
|
-
|
|
30260
|
-
|
|
30482
|
+
adoptAppSpans(upstream);
|
|
30483
|
+
writeAppObservability(c.env, upstream, {
|
|
30484
|
+
app: observabilityApp(slug, app2),
|
|
30485
|
+
installs,
|
|
30486
|
+
budget: createDataPointBudget()
|
|
30487
|
+
});
|
|
30488
|
+
setSpanAttributes({ "app.status": upstream.status });
|
|
30489
|
+
return endpoint.mode === "api" ? passThroughResponse(upstream) : webhookAck(slug, upstream);
|
|
30490
|
+
}, "runForwardToApp");
|
|
30491
|
+
var forwardToApp = /* @__PURE__ */ __name(async (c, input2) => {
|
|
30492
|
+
const { slug, app: app2, endpoint, installs } = input2;
|
|
30493
|
+
return traced(
|
|
30494
|
+
`app.endpoint ${slug}`,
|
|
30495
|
+
{
|
|
30496
|
+
attrs: {
|
|
30497
|
+
"app.slug": slug,
|
|
30498
|
+
"app.version": app2.version,
|
|
30499
|
+
"app.binding": app2.binding,
|
|
30500
|
+
"app.hook": `endpoint.${endpoint.method} ${endpoint.route}`,
|
|
30501
|
+
"app.endpoint_mode": endpoint.mode,
|
|
30502
|
+
"app.installs": installs.length
|
|
30503
|
+
}
|
|
30504
|
+
},
|
|
30505
|
+
() => {
|
|
30506
|
+
return runForwardToApp(c, input2);
|
|
30507
|
+
}
|
|
30261
30508
|
);
|
|
30262
|
-
|
|
30263
|
-
|
|
30509
|
+
}, "forwardToApp");
|
|
30510
|
+
var serveEndpoint = /* @__PURE__ */ __name(async (c, input2) => {
|
|
30511
|
+
const { slug, app: app2, endpoint, search } = input2;
|
|
30512
|
+
const refusal = await authorizeEndpoint(c, slug, endpoint);
|
|
30513
|
+
if (refusal !== void 0) {
|
|
30514
|
+
return refusal;
|
|
30264
30515
|
}
|
|
30265
|
-
|
|
30266
|
-
|
|
30267
|
-
|
|
30268
|
-
|
|
30269
|
-
var APP_UI_TOKEN_SKEW_SECONDS = 60;
|
|
30270
|
-
var APP_UI_TOKEN_ISSUER = "qinto-portal";
|
|
30271
|
-
var claimsSchema = external_exports.object({
|
|
30272
|
-
iss: external_exports.literal(APP_UI_TOKEN_ISSUER),
|
|
30273
|
-
aud: external_exports.string().min(1),
|
|
30274
|
-
sub: external_exports.string().min(1),
|
|
30275
|
-
userId: external_exports.string().min(1),
|
|
30276
|
-
iat: external_exports.number().int(),
|
|
30277
|
-
exp: external_exports.number().int(),
|
|
30278
|
-
domainId: external_exports.string().min(1),
|
|
30279
|
-
appId: external_exports.string().min(1),
|
|
30280
|
-
appInstallId: external_exports.string().min(1),
|
|
30281
|
-
appVersionId: external_exports.string().min(1),
|
|
30282
|
-
access: external_exports.enum(["read", "write"]),
|
|
30283
|
-
// Absent means `full`; see the mint below.
|
|
30284
|
-
appRole: external_exports.enum(APP_ROLES).optional()
|
|
30285
|
-
}).strict();
|
|
30286
|
-
var appUiAudience = /* @__PURE__ */ __name((slug) => {
|
|
30287
|
-
return `app-ui:${slug}`;
|
|
30288
|
-
}, "appUiAudience");
|
|
30289
|
-
var verifyAppUiToken = /* @__PURE__ */ __name(async (input2) => {
|
|
30290
|
-
const signature = await verifyCompactSignature(input2.token, input2.keys);
|
|
30291
|
-
if (!signature.ok) {
|
|
30292
|
-
return signature;
|
|
30516
|
+
const installs = app2.installs.filter((install) => install.active);
|
|
30517
|
+
const [first] = installs;
|
|
30518
|
+
if (first === void 0) {
|
|
30519
|
+
return refuse2(503, "inactive");
|
|
30293
30520
|
}
|
|
30294
|
-
const
|
|
30295
|
-
if (
|
|
30296
|
-
return
|
|
30521
|
+
const key = endpoint.mode === "api" ? `${slug}:${first.id}:endpoint` : `${slug}:${endpoint.route}:endpoint`;
|
|
30522
|
+
if (await rateLimited(c.env, key)) {
|
|
30523
|
+
return refuse2(429, "rate_limited");
|
|
30297
30524
|
}
|
|
30298
|
-
|
|
30299
|
-
|
|
30525
|
+
let body;
|
|
30526
|
+
if (endpoint.method !== "GET") {
|
|
30527
|
+
const bytes = await readBoundedBody(c.req.raw, MAX_ENDPOINT_BODY_BYTES);
|
|
30528
|
+
if (bytes === null) {
|
|
30529
|
+
return refuse2(413, "too_large");
|
|
30530
|
+
}
|
|
30531
|
+
body = bytes;
|
|
30300
30532
|
}
|
|
30301
|
-
|
|
30302
|
-
|
|
30303
|
-
|
|
30533
|
+
return forwardToApp(c, { slug, app: app2, endpoint, installs, body, search });
|
|
30534
|
+
}, "serveEndpoint");
|
|
30535
|
+
var handleAppEndpoint = /* @__PURE__ */ __name(async (c, slug) => {
|
|
30536
|
+
const app2 = siteRegistry().registry.apps[slug];
|
|
30537
|
+
if (app2 === void 0) {
|
|
30538
|
+
return refuse2(404, "not_found");
|
|
30304
30539
|
}
|
|
30305
|
-
|
|
30306
|
-
|
|
30540
|
+
const url2 = new URL(c.req.url);
|
|
30541
|
+
const prefix = `/app/${slug}`;
|
|
30542
|
+
if (url2.pathname !== prefix && !url2.pathname.startsWith(`${prefix}/`)) {
|
|
30543
|
+
return refuse2(404, "not_found");
|
|
30307
30544
|
}
|
|
30308
|
-
|
|
30309
|
-
|
|
30310
|
-
|
|
30311
|
-
|
|
30312
|
-
|
|
30313
|
-
|
|
30314
|
-
|
|
30315
|
-
|
|
30316
|
-
|
|
30317
|
-
|
|
30318
|
-
|
|
30319
|
-
|
|
30320
|
-
|
|
30321
|
-
|
|
30322
|
-
|
|
30323
|
-
|
|
30324
|
-
|
|
30325
|
-
// ../../libs/app-ui-token/src/write-ticket.ts
|
|
30326
|
-
var APP_UI_WRITE_TICKET_TTL_SECONDS = 60;
|
|
30327
|
-
var APP_UI_WRITE_TICKET_ISSUER = "qinto-site";
|
|
30328
|
-
var claimsSchema3 = external_exports.object({
|
|
30329
|
-
iss: external_exports.literal(APP_UI_WRITE_TICKET_ISSUER),
|
|
30330
|
-
aud: external_exports.string().min(1),
|
|
30331
|
-
sub: external_exports.string().min(1),
|
|
30332
|
-
iat: external_exports.number().int(),
|
|
30333
|
-
exp: external_exports.number().int(),
|
|
30334
|
-
domainId: external_exports.string().min(1),
|
|
30335
|
-
appId: external_exports.string().min(1),
|
|
30336
|
-
appVersionId: external_exports.string().min(1)
|
|
30337
|
-
}).strict();
|
|
30338
|
-
var appUiWriteAudience = /* @__PURE__ */ __name((slug) => {
|
|
30339
|
-
return `app-ui-write:${slug}`;
|
|
30340
|
-
}, "appUiWriteAudience");
|
|
30341
|
-
var mintAppUiWriteTicket = /* @__PURE__ */ __name(async (input2) => {
|
|
30342
|
-
const iat = Math.floor(input2.now.getTime() / 1e3);
|
|
30343
|
-
const claims = {
|
|
30344
|
-
iss: APP_UI_WRITE_TICKET_ISSUER,
|
|
30345
|
-
aud: appUiWriteAudience(input2.slug),
|
|
30346
|
-
sub: input2.userId,
|
|
30347
|
-
iat,
|
|
30348
|
-
exp: iat + (input2.ttlSeconds ?? APP_UI_WRITE_TICKET_TTL_SECONDS),
|
|
30349
|
-
domainId: input2.domainId,
|
|
30350
|
-
appId: input2.appId,
|
|
30351
|
-
appVersionId: input2.appVersionId
|
|
30352
|
-
};
|
|
30353
|
-
return signCompact(input2.kid, input2.key, claims);
|
|
30354
|
-
}, "mintAppUiWriteTicket");
|
|
30545
|
+
const preflight2 = c.req.method === "OPTIONS";
|
|
30546
|
+
const method = preflight2 ? c.req.header("access-control-request-method") : c.req.method;
|
|
30547
|
+
const endpoint = matchEndpoint(app2, url2.pathname.slice(prefix.length) || "/", method ?? "");
|
|
30548
|
+
if (endpoint === void 0 || preflight2 && endpoint.auth !== "visitor") {
|
|
30549
|
+
return refuse2(404, "not_found");
|
|
30550
|
+
}
|
|
30551
|
+
if (endpoint.auth === "visitor") {
|
|
30552
|
+
return handleVisitorEndpoint(c, { slug, app: app2, method: endpoint.method, preflight: preflight2 }, (visitor, body) => {
|
|
30553
|
+
return forwardToApp(c, { slug, app: app2, endpoint, installs: [visitor.install], body, search: url2.search, anonId: visitor.anonId });
|
|
30554
|
+
});
|
|
30555
|
+
}
|
|
30556
|
+
return serveEndpoint(c, { slug, app: app2, endpoint, search: url2.search });
|
|
30557
|
+
}, "handleAppEndpoint");
|
|
30558
|
+
var appEndpointRoutes = new Hono2();
|
|
30559
|
+
appEndpointRoutes.all("/app/:slug/*", (c) => {
|
|
30560
|
+
return handleAppEndpoint(c, c.req.param("slug"));
|
|
30561
|
+
});
|
|
30355
30562
|
|
|
30356
30563
|
// src/apps/ui-proxy.ts
|
|
30357
30564
|
var APP_UI_TIMEOUT_MS = 6e4;
|
|
@@ -30660,6 +30867,7 @@ var createApiRouter = /* @__PURE__ */ __name(() => new OpenAPIHono({ defaultHook
|
|
|
30660
30867
|
var CONNECTION_CF_HEADER = "x-connection-cf";
|
|
30661
30868
|
var SESSION_HEADER = "x-session";
|
|
30662
30869
|
var GPC_HEADER = "x-gpc";
|
|
30870
|
+
var RECONNECT_HEADER = "x-reconnect";
|
|
30663
30871
|
var OPEN_PAGE_PARAMS = ["url", "referrer"];
|
|
30664
30872
|
var MAX_HOST_USER_AGENT_LENGTH = 512;
|
|
30665
30873
|
var MAX_HOST_LOCALE_LENGTH = 64;
|
|
@@ -30671,6 +30879,7 @@ var INTERNAL_HEADERS = /* @__PURE__ */ new Set([
|
|
|
30671
30879
|
CONNECTION_CF_HEADER,
|
|
30672
30880
|
SESSION_HEADER,
|
|
30673
30881
|
GPC_HEADER,
|
|
30882
|
+
RECONNECT_HEADER,
|
|
30674
30883
|
TRACE_HEADER,
|
|
30675
30884
|
CF_DATA_HEADER
|
|
30676
30885
|
]);
|
|
@@ -30759,7 +30968,7 @@ var boundedHost = /* @__PURE__ */ __name((request) => {
|
|
|
30759
30968
|
};
|
|
30760
30969
|
return bounded.ip === void 0 && bounded.userAgent === void 0 && bounded.locale === void 0 ? void 0 : bounded;
|
|
30761
30970
|
}, "boundedHost");
|
|
30762
|
-
var connectionAttachment = /* @__PURE__ */ __name((request, geo, siteId) => {
|
|
30971
|
+
var connectionAttachment = /* @__PURE__ */ __name((request, geo, { siteId, anonId }) => {
|
|
30763
30972
|
const userAgent = request.headers.get("user-agent");
|
|
30764
30973
|
const attrs = {
|
|
30765
30974
|
"url.full": withoutQuery(request.url),
|
|
@@ -30775,7 +30984,7 @@ var connectionAttachment = /* @__PURE__ */ __name((request, geo, siteId) => {
|
|
|
30775
30984
|
const sessionBytes = attachmentFieldBytes("session", session2);
|
|
30776
30985
|
const gpc = request.headers.get(GPC_HEADER) === "1";
|
|
30777
30986
|
const gpcBytes = attachmentFieldBytes("gpc", gpc ? true : void 0);
|
|
30778
|
-
const siteBytes = attachmentFieldBytes("siteId", siteId);
|
|
30987
|
+
const siteBytes = attachmentFieldBytes("siteId", siteId) + attachmentFieldBytes("anonId", anonId);
|
|
30779
30988
|
const cf = forwardedCf(request);
|
|
30780
30989
|
const cfSuffix = cf === void 0 ? "" : `,"cf":${JSON.stringify(cf)}`;
|
|
30781
30990
|
const cfBytes = new TextEncoder().encode(cfSuffix).length;
|
|
@@ -30786,6 +30995,7 @@ var connectionAttachment = /* @__PURE__ */ __name((request, geo, siteId) => {
|
|
|
30786
30995
|
return {
|
|
30787
30996
|
...budgeted,
|
|
30788
30997
|
siteId,
|
|
30998
|
+
anonId,
|
|
30789
30999
|
...session2 === void 0 ? {} : { session: session2 },
|
|
30790
31000
|
...browserGeo === void 0 ? {} : { browserGeo },
|
|
30791
31001
|
...includedCf === void 0 ? {} : { cf: includedCf },
|
|
@@ -30793,6 +31003,14 @@ var connectionAttachment = /* @__PURE__ */ __name((request, geo, siteId) => {
|
|
|
30793
31003
|
...gpc ? { gpc: true } : {}
|
|
30794
31004
|
};
|
|
30795
31005
|
}, "connectionAttachment");
|
|
31006
|
+
var attachmentGeo = /* @__PURE__ */ __name((attachment) => {
|
|
31007
|
+
return {
|
|
31008
|
+
// `cf` is the forwarded `request.cf`, kept as plain JSON on the socket.
|
|
31009
|
+
...geoFromCf(attachment.cf),
|
|
31010
|
+
ip: attachment.host?.ip,
|
|
31011
|
+
userAgent: attachment.host?.userAgent
|
|
31012
|
+
};
|
|
31013
|
+
}, "attachmentGeo");
|
|
30796
31014
|
|
|
30797
31015
|
// src/helpers/request-span.ts
|
|
30798
31016
|
var OMITTED_HEADERS = /* @__PURE__ */ new Set(["authorization", DISPATCH_HEADER, CF_DATA_HEADER]);
|
|
@@ -31192,6 +31410,29 @@ var withSetCookie = /* @__PURE__ */ __name((res, cookies) => {
|
|
|
31192
31410
|
}
|
|
31193
31411
|
return out;
|
|
31194
31412
|
}, "withSetCookie");
|
|
31413
|
+
var connectCookies = /* @__PURE__ */ __name(async (c, userId, session2) => {
|
|
31414
|
+
const root = c.env.ROOT_DOMAIN;
|
|
31415
|
+
const hostname3 = new URL(c.req.url).hostname;
|
|
31416
|
+
const domain2 = root !== void 0 && root !== "" && (hostname3 === root || hostname3.endsWith(`.${root}`)) ? root : void 0;
|
|
31417
|
+
const attributes = {
|
|
31418
|
+
httpOnly: true,
|
|
31419
|
+
// `wrangler types` narrows COOKIE_SAME_SITE to its dev default; deploys also set none.
|
|
31420
|
+
sameSite: c.env.COOKIE_SAME_SITE,
|
|
31421
|
+
secure: true,
|
|
31422
|
+
path: "/",
|
|
31423
|
+
maxAge: COOKIE_MAX_AGE
|
|
31424
|
+
};
|
|
31425
|
+
const cookies = [
|
|
31426
|
+
generateCookie(COOKIE_NAME2, userId, { ...attributes, ...domain2 === void 0 ? {} : { domain: domain2 } }),
|
|
31427
|
+
// Re-issued on every connect, which is what makes the session expire after 30 idle minutes.
|
|
31428
|
+
generateCookie(SESSION_COOKIE_NAME, `${session2.id}.${session2.startedAt}`, { ...attributes, maxAge: SESSION_MAX_AGE })
|
|
31429
|
+
];
|
|
31430
|
+
const signature = await signVisitor(c.env, userId);
|
|
31431
|
+
if (signature !== void 0) {
|
|
31432
|
+
cookies.push(generateCookie(VISITOR_SIGNATURE_COOKIE, signature, attributes));
|
|
31433
|
+
}
|
|
31434
|
+
return cookies;
|
|
31435
|
+
}, "connectCookies");
|
|
31195
31436
|
var open2 = /* @__PURE__ */ __name(async (c) => {
|
|
31196
31437
|
if (c.req.header("upgrade")?.toLowerCase() !== "websocket") {
|
|
31197
31438
|
return c.text("expected websocket", 426);
|
|
@@ -31204,13 +31445,14 @@ var open2 = /* @__PURE__ */ __name(async (c) => {
|
|
|
31204
31445
|
});
|
|
31205
31446
|
return enriched;
|
|
31206
31447
|
});
|
|
31207
|
-
if (await botRejected(c.env, geo)) {
|
|
31448
|
+
if (await botRejected(c.env, c.req.raw, geo)) {
|
|
31208
31449
|
return c.text("traffic rejected", 403);
|
|
31209
31450
|
}
|
|
31210
31451
|
const existing = getCookie(c, COOKIE_NAME2);
|
|
31452
|
+
const canonical = existing !== void 0 && isCanonicalId(existing);
|
|
31211
31453
|
let isNewUser = false;
|
|
31212
31454
|
let userId;
|
|
31213
|
-
if (
|
|
31455
|
+
if (canonical) {
|
|
31214
31456
|
userId = existing;
|
|
31215
31457
|
} else {
|
|
31216
31458
|
const adopted = edgeTagUserId(c);
|
|
@@ -31236,6 +31478,10 @@ var open2 = /* @__PURE__ */ __name(async (c) => {
|
|
|
31236
31478
|
headers.set(GPC_HEADER, "1");
|
|
31237
31479
|
setSpanAttributes({ "qinto.gpc": true });
|
|
31238
31480
|
}
|
|
31481
|
+
headers.delete(RECONNECT_HEADER);
|
|
31482
|
+
if (canonical && c.req.query("reconnect") === "1") {
|
|
31483
|
+
headers.set(RECONNECT_HEADER, "1");
|
|
31484
|
+
}
|
|
31239
31485
|
const res = await traced("rpc anon.connect", {}, () => {
|
|
31240
31486
|
headers.delete(TRACE_HEADER);
|
|
31241
31487
|
const trace = currentTraceContext();
|
|
@@ -31244,39 +31490,19 @@ var open2 = /* @__PURE__ */ __name(async (c) => {
|
|
|
31244
31490
|
}
|
|
31245
31491
|
return anonStub(c.env, userId).fetch(new Request(c.req.url, { method: "GET", headers }));
|
|
31246
31492
|
});
|
|
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);
|
|
31493
|
+
return withSetCookie(res, await connectCookies(c, userId, session2));
|
|
31268
31494
|
}, "open");
|
|
31269
31495
|
|
|
31270
31496
|
// browser-artifact/meta.json
|
|
31271
31497
|
var meta_default = {
|
|
31272
|
-
version: "0.1.
|
|
31273
|
-
gitSha: "
|
|
31274
|
-
builtAt: "2026-10-
|
|
31275
|
-
hash: "
|
|
31498
|
+
version: "0.1.15",
|
|
31499
|
+
gitSha: "b90e20a6f236a81312f9525a2b089d2019d0c027",
|
|
31500
|
+
builtAt: "2026-10-09T06:11:09.581Z",
|
|
31501
|
+
hash: "a0cb067dc6246faae53d7842489effb3c46aa848ef529484ab6ee3868fd86762"
|
|
31276
31502
|
};
|
|
31277
31503
|
|
|
31278
31504
|
// src/paths/q-js.ts
|
|
31279
|
-
import qScript from "./
|
|
31505
|
+
import qScript from "./73ef5f028ec35836560c0489d1abdeea83eceede-q.js.txt";
|
|
31280
31506
|
|
|
31281
31507
|
// src/apps-q.ts
|
|
31282
31508
|
import appsQScript from "./apps-q.json";
|
|
@@ -32476,13 +32702,15 @@ var cachedKv = /* @__PURE__ */ __name((kv) => {
|
|
|
32476
32702
|
var VERSION_KEY = "schemaVersion";
|
|
32477
32703
|
var applyMigrations = /* @__PURE__ */ __name((storage, migrations) => {
|
|
32478
32704
|
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);
|
|
32705
|
+
if (applied >= migrations.length) {
|
|
32706
|
+
return;
|
|
32485
32707
|
}
|
|
32708
|
+
storage.transactionSync(() => {
|
|
32709
|
+
for (const migration of migrations.slice(applied)) {
|
|
32710
|
+
storage.sql.exec(migration);
|
|
32711
|
+
}
|
|
32712
|
+
storage.kv.put(VERSION_KEY, migrations.length);
|
|
32713
|
+
});
|
|
32486
32714
|
}, "applyMigrations");
|
|
32487
32715
|
|
|
32488
32716
|
// src/id-graph/retention.ts
|
|
@@ -32768,15 +32996,15 @@ var seedMeta = /* @__PURE__ */ __name((kv, anonId, createdAt) => {
|
|
|
32768
32996
|
kv.put(META_KEY, { anonId, linkedGlobalId: null, linkedAt: null, createdAt });
|
|
32769
32997
|
return true;
|
|
32770
32998
|
}, "seedMeta");
|
|
32999
|
+
var lastActivityAt = /* @__PURE__ */ __name((kv) => {
|
|
33000
|
+
return kv.get(LAST_ACTIVITY_KEY) ?? kv.get(META_KEY)?.createdAt ?? null;
|
|
33001
|
+
}, "lastActivityAt");
|
|
32771
33002
|
var touchActivity = /* @__PURE__ */ __name((kv, receivedAt) => {
|
|
32772
|
-
const last = kv
|
|
32773
|
-
if (last ===
|
|
33003
|
+
const last = lastActivityAt(kv);
|
|
33004
|
+
if (last === null || receivedAt - last >= CLEANUP_INTERVAL_MS) {
|
|
32774
33005
|
kv.put(LAST_ACTIVITY_KEY, receivedAt);
|
|
32775
33006
|
}
|
|
32776
33007
|
}, "touchActivity");
|
|
32777
|
-
var lastActivityAt = /* @__PURE__ */ __name((kv) => {
|
|
32778
|
-
return kv.get(LAST_ACTIVITY_KEY) ?? null;
|
|
32779
|
-
}, "lastActivityAt");
|
|
32780
33008
|
var linkedGlobalId = /* @__PURE__ */ __name((kv) => {
|
|
32781
33009
|
return kv.get(META_KEY)?.linkedGlobalId ?? null;
|
|
32782
33010
|
}, "linkedGlobalId");
|
|
@@ -33010,7 +33238,7 @@ var standsDown = /* @__PURE__ */ __name(async (storage, staleActivity) => {
|
|
|
33010
33238
|
if (lastActivity === staleActivity) {
|
|
33011
33239
|
return false;
|
|
33012
33240
|
}
|
|
33013
|
-
await storage.setAlarm(cleanupTarget(storage, Date.now(), lastActivity
|
|
33241
|
+
await storage.setAlarm(cleanupTarget(storage, Date.now(), lastActivity));
|
|
33014
33242
|
return true;
|
|
33015
33243
|
}, "standsDown");
|
|
33016
33244
|
var relink = /* @__PURE__ */ __name(async (storage, env, anonId, globalId) => {
|
|
@@ -33029,7 +33257,7 @@ var relink = /* @__PURE__ */ __name(async (storage, env, anonId, globalId) => {
|
|
|
33029
33257
|
addSpanEvent("expiry relink failed", { level: "warn", attrs: { "anon.id": anonId, "global.id": globalId, error: String(error61) } });
|
|
33030
33258
|
}
|
|
33031
33259
|
if (mutated) {
|
|
33032
|
-
await storage.setAlarm(cleanupTarget(storage, Date.now(), lastActivityAt(storage.kv)
|
|
33260
|
+
await storage.setAlarm(cleanupTarget(storage, Date.now(), lastActivityAt(storage.kv)));
|
|
33033
33261
|
}
|
|
33034
33262
|
}, "relink");
|
|
33035
33263
|
var unlinkFromPerson = /* @__PURE__ */ __name((env, anonId, globalId) => {
|
|
@@ -33588,10 +33816,9 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33588
33816
|
// In memory on purpose: a hibernated socket sent nothing, so a fresh bucket on wake is the right one.
|
|
33589
33817
|
frameBuckets = /* @__PURE__ */ new WeakMap();
|
|
33590
33818
|
emitBudget = createTokenBucket(EMIT_BURST, EMITS_PER_SECOND);
|
|
33591
|
-
|
|
33819
|
+
stored;
|
|
33820
|
+
// This instance deleted the visitor's storage; only a later write recreates it.
|
|
33592
33821
|
wiped = false;
|
|
33593
|
-
// True while processAction holds ingestQueue, including the transform await.
|
|
33594
|
-
ingestTurn = false;
|
|
33595
33822
|
// Warm write-through mirrors reset together after an expiry or an erase.
|
|
33596
33823
|
kvCache;
|
|
33597
33824
|
cachedStorage;
|
|
@@ -33607,7 +33834,6 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33607
33834
|
sites;
|
|
33608
33835
|
constructor(ctx, env) {
|
|
33609
33836
|
super(ctx, env);
|
|
33610
|
-
applyMigrations(ctx.storage, ANON_MIGRATIONS);
|
|
33611
33837
|
this.kvCache = cachedKv(ctx.storage.kv);
|
|
33612
33838
|
this.cachedStorage = { kv: this.kvCache, sql: ctx.storage.sql };
|
|
33613
33839
|
this.graph = ownGraphEnv(ctx, env);
|
|
@@ -33630,32 +33856,29 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33630
33856
|
const trace = parseTraceContext(request.headers.get(TRACE_HEADER));
|
|
33631
33857
|
return runInvocation({ name: "do.anon.connect", trace, attrs: connectAttrs(request, geo) }, async () => {
|
|
33632
33858
|
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);
|
|
33859
|
+
if (this.isStored()) {
|
|
33860
|
+
touchActivity(this.kv, Date.now());
|
|
33640
33861
|
}
|
|
33641
33862
|
const { 0: client, 1: server } = new WebSocketPair();
|
|
33642
33863
|
this.ctx.acceptWebSocket(server, [siteId]);
|
|
33643
|
-
const attachment = connectionAttachment(request, geo, siteId);
|
|
33864
|
+
const attachment = connectionAttachment(request, geo, { siteId, anonId: userId });
|
|
33644
33865
|
server.serializeAttachment(attachment);
|
|
33645
33866
|
this.sendSharedContext([server], context);
|
|
33646
|
-
|
|
33647
|
-
|
|
33648
|
-
|
|
33649
|
-
|
|
33650
|
-
|
|
33651
|
-
|
|
33652
|
-
|
|
33653
|
-
|
|
33654
|
-
|
|
33655
|
-
|
|
33656
|
-
|
|
33657
|
-
|
|
33658
|
-
|
|
33867
|
+
if (request.headers.get(RECONNECT_HEADER) !== "1") {
|
|
33868
|
+
const page = connectPage(request);
|
|
33869
|
+
this.appFanOut(
|
|
33870
|
+
{
|
|
33871
|
+
kind: "connect",
|
|
33872
|
+
...attachment.cf === void 0 ? {} : { geo: attachment.cf },
|
|
33873
|
+
host: attachment.host,
|
|
33874
|
+
...page === void 0 ? {} : { page },
|
|
33875
|
+
...attachment.session === void 0 ? {} : { session: attachment.session },
|
|
33876
|
+
gpc: attachment.gpc
|
|
33877
|
+
},
|
|
33878
|
+
userId,
|
|
33879
|
+
context
|
|
33880
|
+
);
|
|
33881
|
+
}
|
|
33659
33882
|
return new Response(null, { status: 101, webSocket: client });
|
|
33660
33883
|
});
|
|
33661
33884
|
}
|
|
@@ -33693,7 +33916,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33693
33916
|
}
|
|
33694
33917
|
if (frame.t === "capture") {
|
|
33695
33918
|
const { accepted } = await this.capture(
|
|
33696
|
-
{ apps: frame.apps, location: socketLocation(attachment.cf), gpc: attachment.gpc === true },
|
|
33919
|
+
{ anonId: userId, apps: frame.apps, location: socketLocation(attachment.cf), gpc: attachment.gpc === true },
|
|
33697
33920
|
site
|
|
33698
33921
|
);
|
|
33699
33922
|
this.respond(ws, { t: "captureAck", ref, anonId: userId, accepted });
|
|
@@ -33738,7 +33961,8 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33738
33961
|
syncFromGlobal(snapshot, trace) {
|
|
33739
33962
|
return runInvocation({ name: "do.anon.sync_from_global", trace, attrs: { "global.id": snapshot.globalId } }, () => {
|
|
33740
33963
|
return this.enqueue(async () => {
|
|
33741
|
-
|
|
33964
|
+
const anonId = storedAnonId(this.kv);
|
|
33965
|
+
if (anonId === null) {
|
|
33742
33966
|
addSpanEvent("snapshot dropped: no visitor state", { level: "warn", attrs: { "global.id": snapshot.globalId } });
|
|
33743
33967
|
return;
|
|
33744
33968
|
}
|
|
@@ -33746,7 +33970,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33746
33970
|
addSpanEvent("foreign snapshot dropped", { level: "warn", attrs: { "global.id": snapshot.globalId } });
|
|
33747
33971
|
return;
|
|
33748
33972
|
}
|
|
33749
|
-
|
|
33973
|
+
await this.storeVisitor(anonId, Date.now());
|
|
33750
33974
|
await this.applyOutcome(applySnapshot(this.cachedStorage, snapshot, this.snapshotCaches()));
|
|
33751
33975
|
});
|
|
33752
33976
|
});
|
|
@@ -33763,7 +33987,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33763
33987
|
this.respond(ws, { t: "error", ref: parsed.ref, message: "invalid message" });
|
|
33764
33988
|
return;
|
|
33765
33989
|
}
|
|
33766
|
-
const userId =
|
|
33990
|
+
const userId = attachment?.anonId ?? null;
|
|
33767
33991
|
setSpanAttributes(frameSpanAttrs(parsed.frame, userId));
|
|
33768
33992
|
if (parsed.frame.t === "getConsent") {
|
|
33769
33993
|
this.respond(ws, this.consentReply(parsed.ref, userId));
|
|
@@ -33813,10 +34037,6 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33813
34037
|
};
|
|
33814
34038
|
return runInvocation({ name: "do.anon.app_push", trace, attrs }, () => {
|
|
33815
34039
|
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
34040
|
this.deliverAppPush(push, await this.sites.resolve(site, Date.now()));
|
|
33821
34041
|
});
|
|
33822
34042
|
});
|
|
@@ -33836,26 +34056,19 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33836
34056
|
// so a value the caps refused must never be reported as stored.
|
|
33837
34057
|
capture(input2, site, trace) {
|
|
33838
34058
|
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 });
|
|
34059
|
+
return this.enqueue(() => {
|
|
34060
|
+
return this.runCaptures(input2.anonId, input2.apps, site, { location: input2.location, gpc: input2.gpc });
|
|
33845
34061
|
});
|
|
33846
34062
|
});
|
|
33847
34063
|
}
|
|
33848
|
-
//
|
|
33849
|
-
// carry click ids before any identify, so unlike the socket this one creates the profile.
|
|
34064
|
+
// A migrated visitor can carry click ids before any identify, so this door creates the profile.
|
|
33850
34065
|
serverCapture(input2, site, trace) {
|
|
33851
34066
|
return runInvocation({ name: "do.anon.server_capture", trace }, () => {
|
|
33852
34067
|
return this.enqueue(async () => {
|
|
33853
|
-
this.revive();
|
|
33854
34068
|
const now = Date.now();
|
|
33855
|
-
|
|
33856
|
-
seedMeta(this.kv, input2.anonId, now);
|
|
34069
|
+
await this.storeVisitor(input2.anonId, now);
|
|
33857
34070
|
touchActivity(this.kv, now);
|
|
33858
|
-
return this.runCaptures(input2.apps, site, { location: null, gpc: false });
|
|
34071
|
+
return this.runCaptures(input2.anonId, input2.apps, site, { location: null, gpc: false });
|
|
33859
34072
|
});
|
|
33860
34073
|
});
|
|
33861
34074
|
}
|
|
@@ -33864,7 +34077,8 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33864
34077
|
appCaptures(input2, site, trace) {
|
|
33865
34078
|
return runInvocation({ name: "do.anon.app_captures", trace, attrs: { "app.slug": input2.slug } }, () => {
|
|
33866
34079
|
return this.enqueue(async () => {
|
|
33867
|
-
|
|
34080
|
+
const anonId = this.visitorId();
|
|
34081
|
+
if (anonId === null) {
|
|
33868
34082
|
addSpanEvent("capture dropped: no visitor state", { level: "warn", attrs: { app: input2.slug } });
|
|
33869
34083
|
return;
|
|
33870
34084
|
}
|
|
@@ -33877,7 +34091,8 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33877
34091
|
})) {
|
|
33878
34092
|
return;
|
|
33879
34093
|
}
|
|
33880
|
-
this.storeCaptures(
|
|
34094
|
+
await this.storeCaptures(
|
|
34095
|
+
anonId,
|
|
33881
34096
|
input2.writes.map((write) => {
|
|
33882
34097
|
return { ...write, appId: input2.slug, source: "app", ttlMs: declaredTtlMs(context.registry, input2.slug, write.key) };
|
|
33883
34098
|
})
|
|
@@ -33885,7 +34100,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33885
34100
|
});
|
|
33886
34101
|
});
|
|
33887
34102
|
}
|
|
33888
|
-
async runCaptures(apps, site, { location, gpc }) {
|
|
34103
|
+
async runCaptures(anonId, apps, site, { location, gpc }) {
|
|
33889
34104
|
const context = await this.sites.resolve(site, Date.now());
|
|
33890
34105
|
const writes = acceptedCaptures(context.registry, apps, {
|
|
33891
34106
|
consent: consentState2(this.kv),
|
|
@@ -33893,13 +34108,13 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33893
34108
|
gpc,
|
|
33894
34109
|
location
|
|
33895
34110
|
});
|
|
33896
|
-
return { accepted: this.storeCaptures(writes) };
|
|
34111
|
+
return { accepted: await this.storeCaptures(anonId, writes) };
|
|
33897
34112
|
}
|
|
33898
|
-
storeCaptures(writes) {
|
|
34113
|
+
async storeCaptures(anonId, writes) {
|
|
33899
34114
|
if (writes.length === 0) {
|
|
33900
34115
|
return {};
|
|
33901
34116
|
}
|
|
33902
|
-
this.
|
|
34117
|
+
await this.storeVisitor(anonId, Date.now());
|
|
33903
34118
|
const accepted = {};
|
|
33904
34119
|
const stored = writeCaptures(this.sql, writes, Date.now(), this.captureMirror);
|
|
33905
34120
|
for (const write of stored) {
|
|
@@ -33915,7 +34130,8 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33915
34130
|
appContext(input2, site, trace) {
|
|
33916
34131
|
return runInvocation({ name: "do.anon.app_context", trace, attrs: { "app.slug": input2.slug } }, () => {
|
|
33917
34132
|
return this.enqueue(async () => {
|
|
33918
|
-
|
|
34133
|
+
const anonId = this.visitorId();
|
|
34134
|
+
if (anonId === null) {
|
|
33919
34135
|
addSpanEvent("context dropped: no visitor state", { level: "warn", attrs: { app: input2.slug } });
|
|
33920
34136
|
return [];
|
|
33921
34137
|
}
|
|
@@ -33940,15 +34156,18 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33940
34156
|
}
|
|
33941
34157
|
return true;
|
|
33942
34158
|
});
|
|
33943
|
-
return this.storeContext(
|
|
34159
|
+
return this.storeContext(
|
|
34160
|
+
anonId,
|
|
34161
|
+
writes.map((write) => ({ ...write, appId: input2.slug }))
|
|
34162
|
+
);
|
|
33944
34163
|
});
|
|
33945
34164
|
});
|
|
33946
34165
|
}
|
|
33947
|
-
async storeContext(writes) {
|
|
34166
|
+
async storeContext(anonId, writes) {
|
|
33948
34167
|
if (writes.length === 0) {
|
|
33949
34168
|
return [];
|
|
33950
34169
|
}
|
|
33951
|
-
this.
|
|
34170
|
+
await this.storeVisitor(anonId, Date.now());
|
|
33952
34171
|
const stored = [];
|
|
33953
34172
|
for (const write of writes) {
|
|
33954
34173
|
if (writeContext(this.sql, [write], Date.now(), this.contextMirror).length === 0) {
|
|
@@ -33966,7 +34185,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33966
34185
|
return stored;
|
|
33967
34186
|
}
|
|
33968
34187
|
hasSharedContext() {
|
|
33969
|
-
return
|
|
34188
|
+
return this.isStored() && Object.keys(allContext(this.sql, "shared", this.contextMirror)).length > 0;
|
|
33970
34189
|
}
|
|
33971
34190
|
// Shared context is the level every app may read, so the browser gets it too — the connecting
|
|
33972
34191
|
// socket on open, every socket on a change. Private never leaves the server. The connect frame is
|
|
@@ -33974,7 +34193,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
33974
34193
|
// context from one that has not arrived yet.
|
|
33975
34194
|
sendSharedContext(sockets, context) {
|
|
33976
34195
|
const consent2 = consentState2(this.kv);
|
|
33977
|
-
const stored = allContext(this.sql, "shared", this.contextMirror);
|
|
34196
|
+
const stored = this.isStored() ? allContext(this.sql, "shared", this.contextMirror) : {};
|
|
33978
34197
|
for (const socket of sockets) {
|
|
33979
34198
|
const { gpc } = socket.deserializeAttachment();
|
|
33980
34199
|
sendFrame(socket, sharedContextFrame(context, consent2, stored, gpc === true));
|
|
@@ -34002,12 +34221,10 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34002
34221
|
}
|
|
34003
34222
|
}
|
|
34004
34223
|
}
|
|
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
34224
|
getAppContext(slug, site, trace) {
|
|
34008
34225
|
return runInvocation({ name: "do.anon.get_app_context", trace, attrs: { "app.slug": slug } }, () => {
|
|
34009
34226
|
return this.enqueue(async () => {
|
|
34010
|
-
if (
|
|
34227
|
+
if (!this.isStored()) {
|
|
34011
34228
|
return readableContext(void 0, {});
|
|
34012
34229
|
}
|
|
34013
34230
|
const context = await this.sites.resolve(site, Date.now());
|
|
@@ -34026,20 +34243,18 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34026
34243
|
});
|
|
34027
34244
|
});
|
|
34028
34245
|
}
|
|
34029
|
-
//
|
|
34030
|
-
// and must not discardIfUnused during an ingest turn (meta is seeded after the transform).
|
|
34246
|
+
// Must not join ingestQueue: a transform that lists facts holds that slot.
|
|
34031
34247
|
getAppFacts(slug, query, site, trace) {
|
|
34032
34248
|
return runInvocation({ name: "do.anon.get_app_facts", trace, attrs: { "app.slug": slug } }, async () => {
|
|
34033
34249
|
const context = await this.sites.resolve(site, Date.now());
|
|
34034
|
-
const unused = this.ingestTurn ? false : await this.discardIfUnused();
|
|
34035
34250
|
const app2 = context.registry.apps[slug];
|
|
34036
|
-
if (
|
|
34251
|
+
if (app2 === void 0 || !app2.data.includes("facts")) {
|
|
34037
34252
|
return null;
|
|
34038
34253
|
}
|
|
34039
34254
|
if (!consentAllows(consentState2(this.kv), slug, app2.consent.category, { enforced: context.consentEnforced, gpc: false })) {
|
|
34040
34255
|
return null;
|
|
34041
34256
|
}
|
|
34042
|
-
if (this.
|
|
34257
|
+
if (!this.isStored()) {
|
|
34043
34258
|
return { items: [], next: null };
|
|
34044
34259
|
}
|
|
34045
34260
|
return listAppFacts(this.sql, query, Date.now());
|
|
@@ -34074,8 +34289,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34074
34289
|
}
|
|
34075
34290
|
}
|
|
34076
34291
|
}
|
|
34077
|
-
//
|
|
34078
|
-
// is one — the tag treats its absence as "cannot vouch for the memo" rather than as a match.
|
|
34292
|
+
// No anonId tells the tag "cannot vouch for the memo", not a match.
|
|
34079
34293
|
consentReply(ref, userId) {
|
|
34080
34294
|
return { t: "consentState", ref, ...userId === null ? {} : { anonId: userId }, ...consentState2(this.kv) };
|
|
34081
34295
|
}
|
|
@@ -34108,7 +34322,6 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34108
34322
|
return run;
|
|
34109
34323
|
}
|
|
34110
34324
|
async processAction(action, context) {
|
|
34111
|
-
this.ingestTurn = true;
|
|
34112
34325
|
let recorded = action;
|
|
34113
34326
|
try {
|
|
34114
34327
|
if (isNoopAction(action)) {
|
|
@@ -34122,12 +34335,15 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34122
34335
|
await this.runAction(transformed.action, transformed.releaseEmissions, context);
|
|
34123
34336
|
} finally {
|
|
34124
34337
|
this.recordActionBlocks(recorded, context);
|
|
34125
|
-
this.ingestTurn = false;
|
|
34126
34338
|
}
|
|
34127
34339
|
}
|
|
34128
34340
|
recordActionBlocks(action, context) {
|
|
34129
34341
|
setSpanAttributes(
|
|
34130
|
-
actionBlocks(
|
|
34342
|
+
actionBlocks(
|
|
34343
|
+
action,
|
|
34344
|
+
this.appsSnapshot(action.userId, context),
|
|
34345
|
+
this.isStored() ? allIdentifiers(this.sql, this.identifiersMirror) : []
|
|
34346
|
+
)
|
|
34131
34347
|
);
|
|
34132
34348
|
}
|
|
34133
34349
|
async runAction(current, releaseEmissions, context) {
|
|
@@ -34136,14 +34352,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34136
34352
|
this.releaseTransformEmissions(releaseEmissions);
|
|
34137
34353
|
return;
|
|
34138
34354
|
}
|
|
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);
|
|
34355
|
+
await this.storeVisitor(current.userId, current.receivedAt);
|
|
34147
34356
|
touchActivity(this.kv, current.receivedAt);
|
|
34148
34357
|
const novelIdentifier = this.recordIdentifiers(current);
|
|
34149
34358
|
if (novelIdentifier && linkedGlobalId(this.kv) !== null) {
|
|
@@ -34200,16 +34409,15 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34200
34409
|
}
|
|
34201
34410
|
appsSnapshot(anonId, context) {
|
|
34202
34411
|
const meta3 = storedMeta(this.kv);
|
|
34412
|
+
const stored = this.isStored();
|
|
34203
34413
|
return {
|
|
34204
34414
|
anonId,
|
|
34205
34415
|
globalId: meta3?.linkedGlobalId ?? null,
|
|
34206
|
-
createdAt: meta3?.createdAt ??
|
|
34416
|
+
createdAt: meta3?.createdAt ?? Date.now(),
|
|
34207
34417
|
pii: userProperties(this.kv),
|
|
34208
|
-
|
|
34209
|
-
|
|
34210
|
-
|
|
34211
|
-
captures: this.wiped ? {} : allCaptures(this.sql, Date.now(), this.captureMirror),
|
|
34212
|
-
context: this.wiped ? { private: {}, shared: {} } : {
|
|
34418
|
+
custom: stored ? customProperties(this.sql, this.customPropsMirror) : {},
|
|
34419
|
+
captures: stored ? allCaptures(this.sql, Date.now(), this.captureMirror) : {},
|
|
34420
|
+
context: !stored ? { private: {}, shared: {} } : {
|
|
34213
34421
|
private: allContext(this.sql, "private", this.contextMirror),
|
|
34214
34422
|
shared: readableSharedContext(
|
|
34215
34423
|
context.registry,
|
|
@@ -34244,7 +34452,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34244
34452
|
getProperties(trace) {
|
|
34245
34453
|
return runInvocation({ name: "do.anon.get_properties", trace }, () => {
|
|
34246
34454
|
return this.enqueue(async () => {
|
|
34247
|
-
if (
|
|
34455
|
+
if (!this.isStored()) {
|
|
34248
34456
|
return {};
|
|
34249
34457
|
}
|
|
34250
34458
|
return { ...userProperties(this.kv), ...customProperties(this.sql, this.customPropsMirror) };
|
|
@@ -34254,16 +34462,15 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34254
34462
|
getConsent(trace) {
|
|
34255
34463
|
return runInvocation({ name: "do.anon.get_consent", trace }, () => {
|
|
34256
34464
|
return this.enqueue(async () => {
|
|
34257
|
-
await this.discardIfUnused();
|
|
34258
34465
|
return consentState2(this.kv);
|
|
34259
34466
|
});
|
|
34260
34467
|
});
|
|
34261
34468
|
}
|
|
34262
|
-
// Null means no session: the id
|
|
34469
|
+
// Null means no session: the id stored nothing yet or its profile expired.
|
|
34263
34470
|
getPersonLink(trace) {
|
|
34264
34471
|
return runInvocation({ name: "do.anon.get_person_link", trace }, () => {
|
|
34265
34472
|
return this.enqueue(async () => {
|
|
34266
|
-
if (
|
|
34473
|
+
if (!this.isStored()) {
|
|
34267
34474
|
return null;
|
|
34268
34475
|
}
|
|
34269
34476
|
return { globalId: linkedGlobalId(this.kv) };
|
|
@@ -34273,7 +34480,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34273
34480
|
getGlobalId(trace) {
|
|
34274
34481
|
return runInvocation({ name: "do.anon.get_global_id", trace }, () => {
|
|
34275
34482
|
return this.enqueue(async () => {
|
|
34276
|
-
if (
|
|
34483
|
+
if (!this.isStored()) {
|
|
34277
34484
|
return null;
|
|
34278
34485
|
}
|
|
34279
34486
|
return linkedGlobalId(this.kv);
|
|
@@ -34283,8 +34490,8 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34283
34490
|
erasable(trace) {
|
|
34284
34491
|
return runInvocation({ name: "do.anon.erasable", trace }, () => {
|
|
34285
34492
|
return this.enqueue(async () => {
|
|
34286
|
-
if (
|
|
34287
|
-
return null;
|
|
34493
|
+
if (!this.isStored()) {
|
|
34494
|
+
return this.ctx.getWebSockets().length === 0 ? null : { globalId: null, identifiers: [] };
|
|
34288
34495
|
}
|
|
34289
34496
|
const identifiers = allIdentifiers(this.sql, this.identifiersMirror).map(({ type, value }) => ({ type, value }));
|
|
34290
34497
|
return { globalId: linkedGlobalId(this.kv), identifiers };
|
|
@@ -34294,7 +34501,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34294
34501
|
inspect(trace) {
|
|
34295
34502
|
return runInvocation({ name: "do.anon.inspect", trace }, () => {
|
|
34296
34503
|
return this.enqueue(async () => {
|
|
34297
|
-
if (
|
|
34504
|
+
if (!this.isStored()) {
|
|
34298
34505
|
return { exists: false };
|
|
34299
34506
|
}
|
|
34300
34507
|
return { exists: true, objectId: this.ctx.id.toString(), ...dumpStorage(this.ctx.storage) };
|
|
@@ -34304,7 +34511,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34304
34511
|
inspectTable(table, offset, limit, trace) {
|
|
34305
34512
|
return runInvocation({ name: "do.anon.inspect_table", trace, attrs: { "inspect.table": table } }, () => {
|
|
34306
34513
|
return this.enqueue(async () => {
|
|
34307
|
-
if (
|
|
34514
|
+
if (!this.isStored()) {
|
|
34308
34515
|
return null;
|
|
34309
34516
|
}
|
|
34310
34517
|
return readTable(this.ctx.storage, table, offset, limit);
|
|
@@ -34314,13 +34521,16 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34314
34521
|
erase(trace) {
|
|
34315
34522
|
return runInvocation({ name: "do.anon.erase", trace }, () => {
|
|
34316
34523
|
return this.enqueue(async () => {
|
|
34317
|
-
|
|
34524
|
+
const stored = this.isStored();
|
|
34525
|
+
if (!stored && this.ctx.getWebSockets().length === 0) {
|
|
34318
34526
|
return false;
|
|
34319
34527
|
}
|
|
34320
|
-
|
|
34321
|
-
|
|
34322
|
-
|
|
34323
|
-
|
|
34528
|
+
if (stored) {
|
|
34529
|
+
try {
|
|
34530
|
+
await eraseAnon(this.ctx.storage, this.graph);
|
|
34531
|
+
} finally {
|
|
34532
|
+
this.resetMirrors();
|
|
34533
|
+
}
|
|
34324
34534
|
}
|
|
34325
34535
|
this.wiped = true;
|
|
34326
34536
|
for (const socket of this.ctx.getWebSockets()) {
|
|
@@ -34330,19 +34540,24 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34330
34540
|
});
|
|
34331
34541
|
});
|
|
34332
34542
|
}
|
|
34333
|
-
//
|
|
34334
|
-
|
|
34335
|
-
|
|
34336
|
-
|
|
34337
|
-
|
|
34338
|
-
|
|
34339
|
-
|
|
34543
|
+
// Every write seeds meta through storeVisitor first, so an object without it has no schema to read.
|
|
34544
|
+
isStored() {
|
|
34545
|
+
if (this.stored === void 0) {
|
|
34546
|
+
this.stored = storedMeta(this.kv) !== void 0;
|
|
34547
|
+
if (this.stored) {
|
|
34548
|
+
applyMigrations(this.ctx.storage, ANON_MIGRATIONS);
|
|
34549
|
+
}
|
|
34340
34550
|
}
|
|
34341
|
-
|
|
34342
|
-
|
|
34343
|
-
|
|
34551
|
+
return this.stored;
|
|
34552
|
+
}
|
|
34553
|
+
// Stubs address this object by anon id, but an app may name any user, so only a canonical id counts.
|
|
34554
|
+
visitorId() {
|
|
34555
|
+
const anonId = storedAnonId(this.kv);
|
|
34556
|
+
if (anonId !== null || this.wiped) {
|
|
34557
|
+
return anonId;
|
|
34344
34558
|
}
|
|
34345
|
-
|
|
34559
|
+
const name = this.ctx.id.name;
|
|
34560
|
+
return name !== void 0 && isCanonicalId(name) ? name : null;
|
|
34346
34561
|
}
|
|
34347
34562
|
appendProjectionHistory(outcomes, source) {
|
|
34348
34563
|
for (const { entry } of outcomes) {
|
|
@@ -34551,6 +34766,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34551
34766
|
}
|
|
34552
34767
|
async alarm() {
|
|
34553
34768
|
return runInvocation({ name: "do.anon.cleanup" }, async () => {
|
|
34769
|
+
applyMigrations(this.ctx.storage, ANON_MIGRATIONS);
|
|
34554
34770
|
this.cleanupAlarm = void 0;
|
|
34555
34771
|
const now = Date.now();
|
|
34556
34772
|
const lastActivity = lastActivityAt(this.kv) ?? now;
|
|
@@ -34575,6 +34791,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34575
34791
|
}
|
|
34576
34792
|
resetMirrors() {
|
|
34577
34793
|
this.kvCache.clear();
|
|
34794
|
+
this.stored = void 0;
|
|
34578
34795
|
this.cleanupAlarm = void 0;
|
|
34579
34796
|
this.identifiersMirror.reset();
|
|
34580
34797
|
this.conversionsMirror.reset();
|
|
@@ -34582,14 +34799,22 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34582
34799
|
this.captureMirror.reset();
|
|
34583
34800
|
this.contextMirror.reset();
|
|
34584
34801
|
}
|
|
34585
|
-
|
|
34586
|
-
|
|
34802
|
+
// Callers run in ingestQueue and nothing awaits between the stored check and the seeds.
|
|
34803
|
+
async storeVisitor(anonId, at) {
|
|
34804
|
+
this.cleanupAlarm = await seedCleanupAlarm(this.ctx.storage, Date.now(), lastActivityAt(this.kv) ?? at, this.cleanupAlarm);
|
|
34805
|
+
if (!this.isStored()) {
|
|
34587
34806
|
applyMigrations(this.ctx.storage, ANON_MIGRATIONS);
|
|
34807
|
+
seedMeta(this.kv, anonId, at);
|
|
34808
|
+
this.stored = true;
|
|
34588
34809
|
this.wiped = false;
|
|
34589
34810
|
}
|
|
34811
|
+
const socket = hasGeoRecord(this.kv) ? void 0 : this.ctx.getWebSockets()[0]?.deserializeAttachment();
|
|
34812
|
+
if (socket !== void 0) {
|
|
34813
|
+
updateGeo(this.cachedStorage, attachmentGeo(socket), at);
|
|
34814
|
+
}
|
|
34590
34815
|
}
|
|
34591
34816
|
async retargetCleanupAlarm(now) {
|
|
34592
|
-
const target = cleanupTarget(this.cachedStorage, now, lastActivityAt(this.kv)
|
|
34817
|
+
const target = cleanupTarget(this.cachedStorage, now, lastActivityAt(this.kv));
|
|
34593
34818
|
if (this.cleanupAlarm === target) {
|
|
34594
34819
|
return;
|
|
34595
34820
|
}
|
|
@@ -34599,14 +34824,6 @@ var AnonDO = class extends DurableObject2 {
|
|
|
34599
34824
|
await this.ctx.storage.setAlarm(target);
|
|
34600
34825
|
this.cleanupAlarm = target;
|
|
34601
34826
|
}
|
|
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
34827
|
};
|
|
34611
34828
|
|
|
34612
34829
|
// src/id-graph/global/do.ts
|