@blotout/qinto-cli 0.1.6 → 0.1.8
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 +41 -14
- package/dist/index.js +4205 -3469
- package/dist/index.js.map +1 -1
- package/dist/worker-artifact/app-runtime/index.js +129 -32
- package/dist/worker-artifact/apps-ui/index.js +6 -0
- package/dist/worker-artifact/main/index.js +134 -56
- package/dist/worker-artifact/version.json +6 -6
- package/package.json +1 -1
- package/templates/app/full/qinto.app.jsonc +4 -1
|
@@ -15769,7 +15769,7 @@ var envelopeRequestSchema = external_exports.object({
|
|
|
15769
15769
|
globalId: external_exports.string().min(1).max(64).nullable(),
|
|
15770
15770
|
properties: external_exports.record(external_exports.string(), external_exports.unknown()),
|
|
15771
15771
|
geo: external_exports.record(external_exports.string(), external_exports.string()).optional()
|
|
15772
|
-
}).strict(),
|
|
15772
|
+
}).strict().optional(),
|
|
15773
15773
|
consent: external_exports.object({ enabled: external_exports.boolean(), categories: external_exports.record(external_exports.string(), external_exports.boolean()), apps: external_exports.record(external_exports.string(), external_exports.boolean()) }).strict().optional(),
|
|
15774
15774
|
geo: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
|
|
15775
15775
|
// Only what this app captured or wrote for this visitor; `updatedAt` is when the current value
|
|
@@ -15793,7 +15793,11 @@ var envelopeRequestSchema = external_exports.object({
|
|
|
15793
15793
|
browser: external_exports.record(external_exports.string().min(1).max(64), external_exports.unknown()).optional(),
|
|
15794
15794
|
origin: external_exports.object({ app: external_exports.string().min(1).max(30), install: external_exports.number().int().min(1), depth: external_exports.number().int().min(0) }).strict().optional()
|
|
15795
15795
|
}).strict().optional()
|
|
15796
|
-
}).strict()
|
|
15796
|
+
}).strict().superRefine((envelope, ctx) => {
|
|
15797
|
+
if (envelope.kind !== "rpc" && envelope.user === void 0) {
|
|
15798
|
+
ctx.addIssue({ code: "custom", path: ["user"], message: `A ${envelope.kind} envelope needs a user` });
|
|
15799
|
+
}
|
|
15800
|
+
});
|
|
15797
15801
|
var envelopeResponseSchema = external_exports.object({
|
|
15798
15802
|
v: external_exports.literal(ENVELOPE_VERSION),
|
|
15799
15803
|
// Transform invocations only: pass the action through, drop it, or replace its payload (§5.4).
|
|
@@ -15913,7 +15917,14 @@ var scheduleSchema = external_exports.object({
|
|
|
15913
15917
|
id: external_exports.string().min(1).max(32),
|
|
15914
15918
|
cron: external_exports.string().min(1).max(MAX_CRON_LENGTH),
|
|
15915
15919
|
scheduledTime: external_exports.number().int().nonnegative().safe(),
|
|
15916
|
-
installs: external_exports.array(
|
|
15920
|
+
installs: external_exports.array(
|
|
15921
|
+
external_exports.object({
|
|
15922
|
+
id: external_exports.string().min(1).max(64),
|
|
15923
|
+
ord: external_exports.number().int().min(1),
|
|
15924
|
+
label: external_exports.string().max(100),
|
|
15925
|
+
catalogQueryTicket: external_exports.string().min(1).max(2048)
|
|
15926
|
+
}).strict()
|
|
15927
|
+
).min(1).max(MAX_INSTALLS_PER_APP)
|
|
15917
15928
|
}).strict();
|
|
15918
15929
|
var controlEnvelopeSchema = external_exports.object({
|
|
15919
15930
|
v: external_exports.literal(CONTROL_ENVELOPE_VERSION),
|
|
@@ -18227,7 +18238,7 @@ var APP_FACT_KINDS = [
|
|
|
18227
18238
|
];
|
|
18228
18239
|
var MAX_APP_FACTS_PER_PAGE = 100;
|
|
18229
18240
|
var MAX_APP_FACT_PAGE_BYTES = 1024 * 1024;
|
|
18230
|
-
var COLUMNS = "kind, scope, key, value, source, amount, currency, event_id, data_json, first_at, observed_at, synced_at";
|
|
18241
|
+
var COLUMNS = "kind, scope, key, value, source, amount, currency, event_id, data_json, first_at, observed_at, synced_at, expires_at";
|
|
18231
18242
|
var rowOf = /* @__PURE__ */ __name((row) => {
|
|
18232
18243
|
return {
|
|
18233
18244
|
kind: row.kind,
|
|
@@ -18241,20 +18252,21 @@ var rowOf = /* @__PURE__ */ __name((row) => {
|
|
|
18241
18252
|
dataJson: row.data_json,
|
|
18242
18253
|
firstAt: row.first_at,
|
|
18243
18254
|
observedAt: row.observed_at,
|
|
18244
|
-
syncedAt: row.synced_at
|
|
18255
|
+
syncedAt: row.synced_at,
|
|
18256
|
+
expiresAt: row.expires_at
|
|
18245
18257
|
};
|
|
18246
18258
|
}, "rowOf");
|
|
18247
18259
|
var appFactOf = /* @__PURE__ */ __name((row) => {
|
|
18248
|
-
const { syncedAt: _syncedAt, ...fact } = rowOf(row);
|
|
18260
|
+
const { syncedAt: _syncedAt, expiresAt: _expiresAt, ...fact } = rowOf(row);
|
|
18249
18261
|
return { ...fact, kind: fact.kind };
|
|
18250
18262
|
}, "appFactOf");
|
|
18251
|
-
var listAppFacts = /* @__PURE__ */ __name((sql, query) => {
|
|
18263
|
+
var listAppFacts = /* @__PURE__ */ __name((sql, query, now) => {
|
|
18252
18264
|
const maxRowId = query.maxRowId ?? sql.exec("SELECT MAX(rowid) AS maxRowId FROM facts").one().maxRowId;
|
|
18253
18265
|
if (maxRowId === null) {
|
|
18254
18266
|
return { items: [], next: null };
|
|
18255
18267
|
}
|
|
18256
|
-
const where = ["rowid > ?", "rowid <= ?"];
|
|
18257
|
-
const params = [query.afterRowId, maxRowId];
|
|
18268
|
+
const where = ["rowid > ?", "rowid <= ?", "(expires_at IS NULL OR expires_at > ?)"];
|
|
18269
|
+
const params = [query.afterRowId, maxRowId, now];
|
|
18258
18270
|
if (query.kinds.length > 0) {
|
|
18259
18271
|
where.push(`kind IN (${query.kinds.map(() => "?").join(", ")})`);
|
|
18260
18272
|
params.push(...query.kinds);
|
|
@@ -18287,7 +18299,7 @@ var listAppFacts = /* @__PURE__ */ __name((sql, query) => {
|
|
|
18287
18299
|
}, "listAppFacts");
|
|
18288
18300
|
var insertFact = /* @__PURE__ */ __name((sql, fact) => {
|
|
18289
18301
|
sql.exec(
|
|
18290
|
-
`INSERT INTO facts (${COLUMNS}) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL)`,
|
|
18302
|
+
`INSERT INTO facts (${COLUMNS}) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?)`,
|
|
18291
18303
|
fact.kind,
|
|
18292
18304
|
fact.scope,
|
|
18293
18305
|
fact.key,
|
|
@@ -18298,7 +18310,8 @@ var insertFact = /* @__PURE__ */ __name((sql, fact) => {
|
|
|
18298
18310
|
fact.eventId ?? null,
|
|
18299
18311
|
fact.dataJson ?? null,
|
|
18300
18312
|
fact.firstAt ?? null,
|
|
18301
|
-
fact.observedAt
|
|
18313
|
+
fact.observedAt,
|
|
18314
|
+
fact.expiresAt ?? null
|
|
18302
18315
|
);
|
|
18303
18316
|
}, "insertFact");
|
|
18304
18317
|
var latestFact = /* @__PURE__ */ __name((sql, coords) => {
|
|
@@ -19051,7 +19064,7 @@ var stillDeclared = /* @__PURE__ */ __name((target, key, source) => {
|
|
|
19051
19064
|
return true;
|
|
19052
19065
|
}
|
|
19053
19066
|
const { queryParams, cookies } = target.app.capture;
|
|
19054
|
-
return source === "query" ? queryParams.
|
|
19067
|
+
return source === "query" ? queryParams.some((entry) => entry.name === key) : cookies.some((entry) => entry.name === key);
|
|
19055
19068
|
}, "stillDeclared");
|
|
19056
19069
|
var deliverableAttribution = /* @__PURE__ */ __name((target, rows) => {
|
|
19057
19070
|
if (rows === void 0) {
|
|
@@ -19339,9 +19352,12 @@ var appSchema = external_exports.object({
|
|
|
19339
19352
|
// Method, auth, and mode stay plain strings for the same tolerance: an unknown value from a newer
|
|
19340
19353
|
// registry refuses that one endpoint instead of turning the app — and every other app — off.
|
|
19341
19354
|
endpoints: external_exports.array(external_exports.object({ path: external_exports.string(), methods: external_exports.array(external_exports.string()), auth: external_exports.string(), mode: external_exports.string() }).loose()).default([]),
|
|
19342
|
-
//
|
|
19343
|
-
//
|
|
19344
|
-
capture: external_exports.object({
|
|
19355
|
+
// The emitter omits the block entirely for an app that declares no capture, so the default is
|
|
19356
|
+
// what a manifest without a `browser.capture` serializes to.
|
|
19357
|
+
capture: external_exports.object({
|
|
19358
|
+
queryParams: external_exports.array(external_exports.object({ name: external_exports.string(), expiresInSeconds: external_exports.number().optional() }).loose()).default([]),
|
|
19359
|
+
cookies: external_exports.array(external_exports.object({ name: external_exports.string(), expiresInSeconds: external_exports.number().optional() }).loose()).default([])
|
|
19360
|
+
}).default({ queryParams: [], cookies: [] })
|
|
19345
19361
|
});
|
|
19346
19362
|
var registrySchema = external_exports.object({
|
|
19347
19363
|
v: external_exports.literal(1),
|
|
@@ -19397,7 +19413,8 @@ var pushBody = external_exports.object({
|
|
|
19397
19413
|
var rpcBody = external_exports.object({
|
|
19398
19414
|
...identityFields,
|
|
19399
19415
|
kind: external_exports.literal("rpc"),
|
|
19400
|
-
|
|
19416
|
+
// Absent when the caller is an endpoint, a schedule or a ui handler; the callee then sees no user.
|
|
19417
|
+
userId: external_exports.string().min(1).max(64).optional(),
|
|
19401
19418
|
callee: external_exports.string().min(1).max(30),
|
|
19402
19419
|
fn: external_exports.string().min(1).max(64),
|
|
19403
19420
|
args: external_exports.unknown().optional(),
|
|
@@ -19553,7 +19570,7 @@ var calleeFailed = /* @__PURE__ */ __name((callee, reason) => {
|
|
|
19553
19570
|
addSpanEvent("rpc callee failed", { level: "warn", attrs: { app: callee, reason } });
|
|
19554
19571
|
return Response.json({ error: "callee_failed" }, { status: 502 });
|
|
19555
19572
|
}, "calleeFailed");
|
|
19556
|
-
var deliverRpcEffects = /* @__PURE__ */ __name(async (env, body, effects, install) => {
|
|
19573
|
+
var deliverRpcEffects = /* @__PURE__ */ __name(async (env, { body, userId, effects, install }) => {
|
|
19557
19574
|
for (const ord of effects.rejectedOrds) {
|
|
19558
19575
|
addSpanEvent("app effects rejected over the per-install caps", { level: "warn", attrs: { app: body.callee, ord } });
|
|
19559
19576
|
}
|
|
@@ -19562,21 +19579,21 @@ var deliverRpcEffects = /* @__PURE__ */ __name(async (env, body, effects, instal
|
|
|
19562
19579
|
slug: body.callee,
|
|
19563
19580
|
groups: effects.accepted,
|
|
19564
19581
|
depth: body.depth,
|
|
19565
|
-
anonId:
|
|
19582
|
+
anonId: userId,
|
|
19566
19583
|
installs: [install],
|
|
19567
19584
|
deliverPush: /* @__PURE__ */ __name((push) => {
|
|
19568
19585
|
pushes.push(push);
|
|
19569
19586
|
}, "deliverPush"),
|
|
19570
19587
|
// The broker runs outside the user's AnonDO, so this reaches it the same way a push does.
|
|
19571
19588
|
writeAttribution: /* @__PURE__ */ __name(async (slug, writes) => {
|
|
19572
|
-
await anonStub(env,
|
|
19589
|
+
await anonStub(env, userId).appAttribution({ slug, writes }, currentTraceContext());
|
|
19573
19590
|
}, "writeAttribution"),
|
|
19574
19591
|
writeContext: /* @__PURE__ */ __name(async (slug, writes) => {
|
|
19575
|
-
await anonStub(env,
|
|
19592
|
+
await anonStub(env, userId).appContext({ slug, writes }, currentTraceContext());
|
|
19576
19593
|
}, "writeContext")
|
|
19577
19594
|
});
|
|
19578
19595
|
for (const push of pushes) {
|
|
19579
|
-
await anonStub(env,
|
|
19596
|
+
await anonStub(env, userId).appPush(push, currentTraceContext());
|
|
19580
19597
|
}
|
|
19581
19598
|
}, "deliverRpcEffects");
|
|
19582
19599
|
var runRelayRpc = /* @__PURE__ */ __name(async (env, { context, body, callee, install }) => {
|
|
@@ -19593,7 +19610,8 @@ var runRelayRpc = /* @__PURE__ */ __name(async (env, { context, body, callee, in
|
|
|
19593
19610
|
app: { slug: body.callee, version: callee.version },
|
|
19594
19611
|
installs: [install],
|
|
19595
19612
|
// No bucket projection for rpc: the callee sees only the subject's id, never its profile.
|
|
19596
|
-
|
|
19613
|
+
// Omitted entirely when the caller had no visitor, so the callee's ctx.user is null.
|
|
19614
|
+
...body.userId === void 0 ? {} : { user: { anonId: body.userId, globalId: null, properties: {} } },
|
|
19597
19615
|
depth: body.depth,
|
|
19598
19616
|
// JSON.stringify drops an undefined key, and the envelope requires `args` to be present.
|
|
19599
19617
|
rpc: { caller: body.slug, fn: body.fn, args: body.args ?? null }
|
|
@@ -19625,7 +19643,13 @@ var runRelayRpc = /* @__PURE__ */ __name(async (env, { context, body, callee, in
|
|
|
19625
19643
|
if (verdict.rpc === null) {
|
|
19626
19644
|
return calleeFailed(body.callee, "The response carries no rpc result.");
|
|
19627
19645
|
}
|
|
19628
|
-
|
|
19646
|
+
if (body.userId === void 0) {
|
|
19647
|
+
if (verdict.effects.accepted.length > 0) {
|
|
19648
|
+
addSpanEvent("rpc effects dropped: the call carries no subject", { level: "warn", attrs: { app: body.callee } });
|
|
19649
|
+
}
|
|
19650
|
+
} else {
|
|
19651
|
+
await deliverRpcEffects(env, { body, userId: body.userId, effects: verdict.effects, install });
|
|
19652
|
+
}
|
|
19629
19653
|
return Response.json({ result: verdict.rpc.result });
|
|
19630
19654
|
}, "runRelayRpc");
|
|
19631
19655
|
var relayRpc = /* @__PURE__ */ __name(async (env, input) => {
|
|
@@ -20523,10 +20547,24 @@ var verifyAppUiToken = /* @__PURE__ */ __name(async (input) => {
|
|
|
20523
20547
|
return { ok: true, claims: claims.data, kid: signature.kid, key: signature.key };
|
|
20524
20548
|
}, "verifyAppUiToken");
|
|
20525
20549
|
|
|
20550
|
+
// ../../libs/app-ui-token/src/schedule-catalog-ticket.ts
|
|
20551
|
+
var APP_SCHEDULE_CATALOG_TICKET_ISSUER = "qinto-portal";
|
|
20552
|
+
var claimsSchema2 = external_exports.object({
|
|
20553
|
+
iss: external_exports.literal(APP_SCHEDULE_CATALOG_TICKET_ISSUER),
|
|
20554
|
+
aud: external_exports.string().min(1),
|
|
20555
|
+
sub: external_exports.string().min(1),
|
|
20556
|
+
iat: external_exports.number().int(),
|
|
20557
|
+
exp: external_exports.number().int(),
|
|
20558
|
+
domainId: external_exports.string().min(1),
|
|
20559
|
+
appId: external_exports.string().min(1),
|
|
20560
|
+
appInstallId: external_exports.string().min(1),
|
|
20561
|
+
appVersionId: external_exports.string().min(1)
|
|
20562
|
+
}).strict();
|
|
20563
|
+
|
|
20526
20564
|
// ../../libs/app-ui-token/src/write-ticket.ts
|
|
20527
20565
|
var APP_UI_WRITE_TICKET_TTL_SECONDS = 60;
|
|
20528
20566
|
var APP_UI_WRITE_TICKET_ISSUER = "qinto-site";
|
|
20529
|
-
var
|
|
20567
|
+
var claimsSchema3 = external_exports.object({
|
|
20530
20568
|
iss: external_exports.literal(APP_UI_WRITE_TICKET_ISSUER),
|
|
20531
20569
|
aud: external_exports.string().min(1),
|
|
20532
20570
|
sub: external_exports.string().min(1),
|
|
@@ -23822,7 +23860,7 @@ var declaredCapture = /* @__PURE__ */ __name((app2) => {
|
|
|
23822
23860
|
if (queryParams.length === 0 && cookies.length === 0 || !app2.installs.some((install) => install.active)) {
|
|
23823
23861
|
return void 0;
|
|
23824
23862
|
}
|
|
23825
|
-
return { queryParams, cookies };
|
|
23863
|
+
return { queryParams: queryParams.map((entry) => entry.name), cookies: cookies.map((entry) => entry.name) };
|
|
23826
23864
|
}, "declaredCapture");
|
|
23827
23865
|
var appsMetadata = /* @__PURE__ */ __name(() => {
|
|
23828
23866
|
const { registry: registry3 } = siteRegistry();
|
|
@@ -24299,9 +24337,22 @@ var rowOf2 = /* @__PURE__ */ __name((row) => {
|
|
|
24299
24337
|
value: row.value,
|
|
24300
24338
|
createdAt: row.firstAt,
|
|
24301
24339
|
updatedAt: row.observedAt,
|
|
24302
|
-
source: row.source
|
|
24340
|
+
source: row.source,
|
|
24341
|
+
expiresAt: row.expiresAt
|
|
24303
24342
|
};
|
|
24304
24343
|
}, "rowOf");
|
|
24344
|
+
var deadline = /* @__PURE__ */ __name((sql, write, now) => {
|
|
24345
|
+
if (write.ttlMs === null) {
|
|
24346
|
+
return null;
|
|
24347
|
+
}
|
|
24348
|
+
const { firstSeen } = sql.exec(
|
|
24349
|
+
"SELECT MIN(observed_at) AS firstSeen FROM facts WHERE kind = 'attribution' AND scope = ? AND key = ? AND value = ?",
|
|
24350
|
+
write.appId,
|
|
24351
|
+
write.key,
|
|
24352
|
+
write.value
|
|
24353
|
+
).one();
|
|
24354
|
+
return (firstSeen ?? now) + write.ttlMs;
|
|
24355
|
+
}, "deadline");
|
|
24305
24356
|
var hydrate2 = /* @__PURE__ */ __name((sql, cache) => {
|
|
24306
24357
|
let map2 = cache?.map();
|
|
24307
24358
|
if (map2 === void 0) {
|
|
@@ -24321,16 +24372,18 @@ var withinCaps = /* @__PURE__ */ __name((write, stored) => {
|
|
|
24321
24372
|
}
|
|
24322
24373
|
return stored.has(write.key) || stored.size < MAX_ATTRIBUTION_KEYS_PER_APP;
|
|
24323
24374
|
}, "withinCaps");
|
|
24324
|
-
var persist2 = /* @__PURE__ */ __name((sql, write,
|
|
24375
|
+
var persist2 = /* @__PURE__ */ __name((sql, write, state) => {
|
|
24376
|
+
const { now, previous, expiresAt: expiresAt2 } = state;
|
|
24325
24377
|
const createdAt = previous?.createdAt ?? now;
|
|
24326
24378
|
if (previous !== void 0 && previous.value === write.value) {
|
|
24327
24379
|
sql.exec(
|
|
24328
|
-
"UPDATE facts SET source = ? WHERE rowid = (SELECT rowid FROM facts WHERE kind = 'attribution' AND scope = ? AND key = ? ORDER BY observed_at DESC, rowid DESC LIMIT 1)",
|
|
24380
|
+
"UPDATE facts SET source = ?, expires_at = ? WHERE rowid = (SELECT rowid FROM facts WHERE kind = 'attribution' AND scope = ? AND key = ? ORDER BY observed_at DESC, rowid DESC LIMIT 1)",
|
|
24329
24381
|
write.source,
|
|
24382
|
+
expiresAt2,
|
|
24330
24383
|
write.appId,
|
|
24331
24384
|
write.key
|
|
24332
24385
|
);
|
|
24333
|
-
return { value: write.value, createdAt, updatedAt: previous.updatedAt, source: write.source };
|
|
24386
|
+
return { value: write.value, createdAt, updatedAt: previous.updatedAt, source: write.source, expiresAt: expiresAt2 };
|
|
24334
24387
|
}
|
|
24335
24388
|
insertFact(sql, {
|
|
24336
24389
|
kind: "attribution",
|
|
@@ -24339,9 +24392,10 @@ var persist2 = /* @__PURE__ */ __name((sql, write, now, previous) => {
|
|
|
24339
24392
|
value: write.value,
|
|
24340
24393
|
source: write.source,
|
|
24341
24394
|
firstAt: createdAt,
|
|
24342
|
-
observedAt: now
|
|
24395
|
+
observedAt: now,
|
|
24396
|
+
expiresAt: expiresAt2
|
|
24343
24397
|
});
|
|
24344
|
-
return { value: write.value, createdAt, updatedAt: now, source: write.source };
|
|
24398
|
+
return { value: write.value, createdAt, updatedAt: now, source: write.source, expiresAt: expiresAt2 };
|
|
24345
24399
|
}, "persist");
|
|
24346
24400
|
var writeAttribution = /* @__PURE__ */ __name((sql, writes, now, cache) => {
|
|
24347
24401
|
const map2 = hydrate2(sql, cache);
|
|
@@ -24352,18 +24406,19 @@ var writeAttribution = /* @__PURE__ */ __name((sql, writes, now, cache) => {
|
|
|
24352
24406
|
if (!withinCaps(write, forApp)) {
|
|
24353
24407
|
continue;
|
|
24354
24408
|
}
|
|
24355
|
-
|
|
24356
|
-
|
|
24409
|
+
const expiresAt2 = deadline(sql, write, now);
|
|
24410
|
+
if (previous?.value !== write.value || previous.source !== write.source || previous.expiresAt !== expiresAt2) {
|
|
24411
|
+
forApp.set(write.key, persist2(sql, write, { now, previous, expiresAt: expiresAt2 }));
|
|
24357
24412
|
map2.set(write.appId, forApp);
|
|
24358
24413
|
}
|
|
24359
24414
|
accepted.push(write);
|
|
24360
24415
|
}
|
|
24361
24416
|
return accepted;
|
|
24362
24417
|
}, "writeAttribution");
|
|
24363
|
-
var allAttribution = /* @__PURE__ */ __name((sql, cache) => {
|
|
24418
|
+
var allAttribution = /* @__PURE__ */ __name((sql, now, cache) => {
|
|
24364
24419
|
const all = {};
|
|
24365
24420
|
for (const [appId, rows] of hydrate2(sql, cache)) {
|
|
24366
|
-
all[appId] = Object.fromEntries(rows);
|
|
24421
|
+
all[appId] = Object.fromEntries([...rows].filter(([, row]) => row.expiresAt === null || row.expiresAt > now));
|
|
24367
24422
|
}
|
|
24368
24423
|
return all;
|
|
24369
24424
|
}, "allAttribution");
|
|
@@ -24385,12 +24440,17 @@ var appWriteAllowed = /* @__PURE__ */ __name((registry3, consent2, slug, enforce
|
|
|
24385
24440
|
}
|
|
24386
24441
|
return true;
|
|
24387
24442
|
}, "appWriteAllowed");
|
|
24388
|
-
var
|
|
24389
|
-
|
|
24390
|
-
|
|
24443
|
+
var declaration = /* @__PURE__ */ __name((app2, key) => {
|
|
24444
|
+
const param = app2.capture.queryParams.find((entry) => entry.name === key);
|
|
24445
|
+
const declared = param ?? app2.capture.cookies.find((entry) => entry.name === key);
|
|
24446
|
+
if (declared === void 0) {
|
|
24447
|
+
return void 0;
|
|
24391
24448
|
}
|
|
24392
|
-
return
|
|
24393
|
-
|
|
24449
|
+
return {
|
|
24450
|
+
source: param === void 0 ? "cookie" : "query",
|
|
24451
|
+
ttlMs: declared.expiresInSeconds === void 0 ? null : declared.expiresInSeconds * 1e3
|
|
24452
|
+
};
|
|
24453
|
+
}, "declaration");
|
|
24394
24454
|
var acceptedCaptures = /* @__PURE__ */ __name((registry3, consent2, apps, enforced) => {
|
|
24395
24455
|
const accepted = [];
|
|
24396
24456
|
for (const [slug, values] of Object.entries(apps)) {
|
|
@@ -24399,8 +24459,8 @@ var acceptedCaptures = /* @__PURE__ */ __name((registry3, consent2, apps, enforc
|
|
|
24399
24459
|
continue;
|
|
24400
24460
|
}
|
|
24401
24461
|
for (const [key, value] of Object.entries(values)) {
|
|
24402
|
-
const
|
|
24403
|
-
if (
|
|
24462
|
+
const declared = declaration(app2, key);
|
|
24463
|
+
if (declared === void 0) {
|
|
24404
24464
|
addSpanEvent("capture dropped: the app declares no such name", { level: "warn", attrs: { app: slug, key } });
|
|
24405
24465
|
continue;
|
|
24406
24466
|
}
|
|
@@ -24408,7 +24468,7 @@ var acceptedCaptures = /* @__PURE__ */ __name((registry3, consent2, apps, enforc
|
|
|
24408
24468
|
addSpanEvent("capture dropped: over the size cap", { level: "warn", attrs: { app: slug, key } });
|
|
24409
24469
|
continue;
|
|
24410
24470
|
}
|
|
24411
|
-
accepted.push({ appId: slug, key, value, source });
|
|
24471
|
+
accepted.push({ appId: slug, key, value, source: declared.source, ttlMs: declared.ttlMs });
|
|
24412
24472
|
}
|
|
24413
24473
|
}
|
|
24414
24474
|
return accepted;
|
|
@@ -24712,6 +24772,19 @@ var cachedKv = /* @__PURE__ */ __name((kv) => {
|
|
|
24712
24772
|
};
|
|
24713
24773
|
}, "cachedKv");
|
|
24714
24774
|
|
|
24775
|
+
// src/id-graph/migrate.ts
|
|
24776
|
+
var VERSION_KEY = "schemaVersion";
|
|
24777
|
+
var applyMigrations = /* @__PURE__ */ __name((storage, migrations) => {
|
|
24778
|
+
const applied = storage.kv.get(VERSION_KEY) ?? 0;
|
|
24779
|
+
for (const [index, migration] of migrations.entries()) {
|
|
24780
|
+
if (index < applied) {
|
|
24781
|
+
continue;
|
|
24782
|
+
}
|
|
24783
|
+
storage.sql.exec(migration);
|
|
24784
|
+
storage.kv.put(VERSION_KEY, index + 1);
|
|
24785
|
+
}
|
|
24786
|
+
}, "applyMigrations");
|
|
24787
|
+
|
|
24715
24788
|
// src/id-graph/retention.ts
|
|
24716
24789
|
var RETENTION_MS = 90 * 24 * 60 * 60 * 1e3;
|
|
24717
24790
|
var CLEANUP_INTERVAL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
@@ -25223,8 +25296,8 @@ var expiryHorizon = /* @__PURE__ */ __name((storage, lastActivity) => {
|
|
|
25223
25296
|
}, "expiryHorizon");
|
|
25224
25297
|
var cleanupTarget = /* @__PURE__ */ __name((storage, now, lastActivity) => {
|
|
25225
25298
|
const horizon = expiryHorizon(storage, lastActivity);
|
|
25226
|
-
const
|
|
25227
|
-
const target =
|
|
25299
|
+
const deadline2 = nextRetentionDeadline(storage.sql);
|
|
25300
|
+
const target = deadline2 === null ? horizon : Math.min(deadline2, horizon);
|
|
25228
25301
|
return Math.max(target, now + IMMEDIATE_CLEANUP_DELAY_MS);
|
|
25229
25302
|
}, "cleanupTarget");
|
|
25230
25303
|
var standsDown = /* @__PURE__ */ __name(async (storage, staleActivity) => {
|
|
@@ -25389,7 +25462,8 @@ var assignAll = /* @__PURE__ */ __name(async (env, sorted, claim) => {
|
|
|
25389
25462
|
}, "assignAll");
|
|
25390
25463
|
|
|
25391
25464
|
// src/id-graph/anon/schema.ts
|
|
25392
|
-
var
|
|
25465
|
+
var ANON_MIGRATIONS = [
|
|
25466
|
+
`
|
|
25393
25467
|
CREATE TABLE IF NOT EXISTS facts (
|
|
25394
25468
|
kind TEXT NOT NULL,
|
|
25395
25469
|
scope TEXT NOT NULL,
|
|
@@ -25406,7 +25480,9 @@ CREATE TABLE IF NOT EXISTS facts (
|
|
|
25406
25480
|
);
|
|
25407
25481
|
CREATE INDEX IF NOT EXISTS idx_facts_key ON facts (kind, scope, key, observed_at);
|
|
25408
25482
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_facts_conversion ON facts (key) WHERE kind = 'conversion';
|
|
25409
|
-
|
|
25483
|
+
`,
|
|
25484
|
+
"ALTER TABLE facts ADD COLUMN expires_at INTEGER;"
|
|
25485
|
+
];
|
|
25410
25486
|
|
|
25411
25487
|
// src/id-graph/anon/websocket.ts
|
|
25412
25488
|
var MAX_BROWSER_RESULT_BYTES = 1024;
|
|
@@ -25600,7 +25676,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
25600
25676
|
cleanupAlarm;
|
|
25601
25677
|
constructor(ctx, env) {
|
|
25602
25678
|
super(ctx, env);
|
|
25603
|
-
ctx.storage
|
|
25679
|
+
applyMigrations(ctx.storage, ANON_MIGRATIONS);
|
|
25604
25680
|
this.kvCache = cachedKv(ctx.storage.kv);
|
|
25605
25681
|
this.cachedStorage = { kv: this.kvCache, sql: ctx.storage.sql };
|
|
25606
25682
|
}
|
|
@@ -25755,7 +25831,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
25755
25831
|
if (!appWriteAllowed(siteRegistry().registry, consentState2(this.kv), input.slug, consentEnforced(this.env))) {
|
|
25756
25832
|
return;
|
|
25757
25833
|
}
|
|
25758
|
-
this.storeAttribution(input.writes.map((write) => ({ ...write, appId: input.slug, source: "app" })));
|
|
25834
|
+
this.storeAttribution(input.writes.map((write) => ({ ...write, appId: input.slug, source: "app", ttlMs: null })));
|
|
25759
25835
|
});
|
|
25760
25836
|
});
|
|
25761
25837
|
}
|
|
@@ -25877,7 +25953,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
25877
25953
|
if (!consentAllows(consentState2(this.kv), slug, app2.consent.category, consentEnforced(this.env))) {
|
|
25878
25954
|
return null;
|
|
25879
25955
|
}
|
|
25880
|
-
return listAppFacts(this.sql, query);
|
|
25956
|
+
return listAppFacts(this.sql, query, Date.now());
|
|
25881
25957
|
});
|
|
25882
25958
|
});
|
|
25883
25959
|
}
|
|
@@ -26057,7 +26133,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
26057
26133
|
// A wipe dropped the sql tables, so reading them would throw — and reviving for a transform
|
|
26058
26134
|
// snapshot would strand recreated storage when the chain fails or drops.
|
|
26059
26135
|
custom: this.wiped ? {} : customProperties(this.sql, this.customPropsMirror),
|
|
26060
|
-
attribution: this.wiped ? {} : allAttribution(this.sql, this.attributionMirror),
|
|
26136
|
+
attribution: this.wiped ? {} : allAttribution(this.sql, Date.now(), this.attributionMirror),
|
|
26061
26137
|
context: this.wiped ? { private: {}, shared: {} } : {
|
|
26062
26138
|
private: allContext(this.sql, "private", this.contextMirror),
|
|
26063
26139
|
shared: readableSharedContext(
|
|
@@ -26393,7 +26469,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
26393
26469
|
}
|
|
26394
26470
|
revive() {
|
|
26395
26471
|
if (this.wiped) {
|
|
26396
|
-
this.ctx.storage
|
|
26472
|
+
applyMigrations(this.ctx.storage, ANON_MIGRATIONS);
|
|
26397
26473
|
this.wiped = false;
|
|
26398
26474
|
}
|
|
26399
26475
|
}
|
|
@@ -26595,7 +26671,8 @@ var storedGlobalId = /* @__PURE__ */ __name((kv) => {
|
|
|
26595
26671
|
}, "storedGlobalId");
|
|
26596
26672
|
|
|
26597
26673
|
// src/id-graph/global/schema.ts
|
|
26598
|
-
var
|
|
26674
|
+
var GLOBAL_MIGRATIONS = [
|
|
26675
|
+
`
|
|
26599
26676
|
CREATE TABLE IF NOT EXISTS facts (
|
|
26600
26677
|
kind TEXT NOT NULL,
|
|
26601
26678
|
scope TEXT NOT NULL,
|
|
@@ -26610,7 +26687,8 @@ CREATE TABLE IF NOT EXISTS facts (
|
|
|
26610
26687
|
observed_at INTEGER NOT NULL
|
|
26611
26688
|
);
|
|
26612
26689
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_facts_key ON facts (kind, scope, key);
|
|
26613
|
-
|
|
26690
|
+
`
|
|
26691
|
+
];
|
|
26614
26692
|
|
|
26615
26693
|
// src/id-graph/global/do.ts
|
|
26616
26694
|
var GlobalDO = class extends DurableObject3 {
|
|
@@ -26623,7 +26701,7 @@ var GlobalDO = class extends DurableObject3 {
|
|
|
26623
26701
|
wiped = false;
|
|
26624
26702
|
constructor(ctx, env) {
|
|
26625
26703
|
super(ctx, env);
|
|
26626
|
-
ctx.storage
|
|
26704
|
+
applyMigrations(ctx.storage, GLOBAL_MIGRATIONS);
|
|
26627
26705
|
this.kvCache = cachedKv(ctx.storage.kv);
|
|
26628
26706
|
this.identifiersMirror = identifiersCache2();
|
|
26629
26707
|
}
|
|
@@ -26690,7 +26768,7 @@ var GlobalDO = class extends DurableObject3 {
|
|
|
26690
26768
|
}
|
|
26691
26769
|
revive() {
|
|
26692
26770
|
if (this.wiped) {
|
|
26693
|
-
this.ctx.storage
|
|
26771
|
+
applyMigrations(this.ctx.storage, GLOBAL_MIGRATIONS);
|
|
26694
26772
|
this.wiped = false;
|
|
26695
26773
|
}
|
|
26696
26774
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"main": {
|
|
3
3
|
"version": "0.1.0",
|
|
4
|
-
"gitSha": "
|
|
5
|
-
"builtAt": "2026-08-
|
|
4
|
+
"gitSha": "38683fc9a0a7f29125a35ad77e2c0c701992c82b",
|
|
5
|
+
"builtAt": "2026-08-27T07:08:20.610Z",
|
|
6
6
|
"compatibilityDate": "2026-06-23",
|
|
7
7
|
"compatibilityFlags": [
|
|
8
8
|
"nodejs_compat"
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
},
|
|
41
41
|
"app-runtime": {
|
|
42
42
|
"version": "0.1.0",
|
|
43
|
-
"gitSha": "
|
|
44
|
-
"builtAt": "2026-08-
|
|
43
|
+
"gitSha": "db5a262cc87eefb7191739982830998dd648ff8b",
|
|
44
|
+
"builtAt": "2026-08-27T05:45:00.519Z",
|
|
45
45
|
"compatibilityDate": "2026-06-23",
|
|
46
46
|
"compatibilityFlags": [
|
|
47
47
|
"nodejs_compat"
|
|
@@ -57,8 +57,8 @@
|
|
|
57
57
|
},
|
|
58
58
|
"apps-ui": {
|
|
59
59
|
"version": "0.1.0",
|
|
60
|
-
"gitSha": "
|
|
61
|
-
"builtAt": "2026-08-
|
|
60
|
+
"gitSha": "d2dbd3fccee9436ca4a4e3a6429925e6f9f4d0ce",
|
|
61
|
+
"builtAt": "2026-08-26T15:50:13.405Z",
|
|
62
62
|
"compatibilityDate": "2026-06-23",
|
|
63
63
|
"compatibilityFlags": [
|
|
64
64
|
"nodejs_compat"
|
package/package.json
CHANGED
|
@@ -36,7 +36,10 @@
|
|
|
36
36
|
// "capture" names the query params and cookies the platform records against each visitor for
|
|
37
37
|
// this app, which only this app reads back. Each name belongs to one list. Uncomment to use it —
|
|
38
38
|
// a site manager sees the list and must accept any name a later version adds.
|
|
39
|
-
//
|
|
39
|
+
//
|
|
40
|
+
// "expiresInSeconds" is optional and runs from the first sighting of that value; seeing the same
|
|
41
|
+
// value again never extends it. Omit it to keep the value for the life of the visitor profile.
|
|
42
|
+
// "capture": { "queryParams": [{ "name": "fbclid", "expiresInSeconds": 604800 }], "cookies": [{ "name": "_fbp" }] },
|
|
40
43
|
// eventResults lets the matching server install receive this browser handler's return value on
|
|
41
44
|
// the same action as action.browser. It is untrusted page input and delivery stays fail-open.
|
|
42
45
|
"browser": { "entry": "src/browser.ts", "eventResults": ["track"] },
|