@blotout/qinto-cli 0.1.5 → 0.1.7
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 +49 -16
- package/dist/index.js +4218 -3652
- package/dist/index.js.map +1 -1
- package/dist/worker-artifact/app-runtime/index.js +75 -31
- package/dist/worker-artifact/apps-ui/index.js +6 -0
- package/dist/worker-artifact/main/index.js +111 -54
- 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).
|
|
@@ -18227,7 +18231,7 @@ var APP_FACT_KINDS = [
|
|
|
18227
18231
|
];
|
|
18228
18232
|
var MAX_APP_FACTS_PER_PAGE = 100;
|
|
18229
18233
|
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";
|
|
18234
|
+
var COLUMNS = "kind, scope, key, value, source, amount, currency, event_id, data_json, first_at, observed_at, synced_at, expires_at";
|
|
18231
18235
|
var rowOf = /* @__PURE__ */ __name((row) => {
|
|
18232
18236
|
return {
|
|
18233
18237
|
kind: row.kind,
|
|
@@ -18241,20 +18245,21 @@ var rowOf = /* @__PURE__ */ __name((row) => {
|
|
|
18241
18245
|
dataJson: row.data_json,
|
|
18242
18246
|
firstAt: row.first_at,
|
|
18243
18247
|
observedAt: row.observed_at,
|
|
18244
|
-
syncedAt: row.synced_at
|
|
18248
|
+
syncedAt: row.synced_at,
|
|
18249
|
+
expiresAt: row.expires_at
|
|
18245
18250
|
};
|
|
18246
18251
|
}, "rowOf");
|
|
18247
18252
|
var appFactOf = /* @__PURE__ */ __name((row) => {
|
|
18248
|
-
const { syncedAt: _syncedAt, ...fact } = rowOf(row);
|
|
18253
|
+
const { syncedAt: _syncedAt, expiresAt: _expiresAt, ...fact } = rowOf(row);
|
|
18249
18254
|
return { ...fact, kind: fact.kind };
|
|
18250
18255
|
}, "appFactOf");
|
|
18251
|
-
var listAppFacts = /* @__PURE__ */ __name((sql, query) => {
|
|
18256
|
+
var listAppFacts = /* @__PURE__ */ __name((sql, query, now) => {
|
|
18252
18257
|
const maxRowId = query.maxRowId ?? sql.exec("SELECT MAX(rowid) AS maxRowId FROM facts").one().maxRowId;
|
|
18253
18258
|
if (maxRowId === null) {
|
|
18254
18259
|
return { items: [], next: null };
|
|
18255
18260
|
}
|
|
18256
|
-
const where = ["rowid > ?", "rowid <= ?"];
|
|
18257
|
-
const params = [query.afterRowId, maxRowId];
|
|
18261
|
+
const where = ["rowid > ?", "rowid <= ?", "(expires_at IS NULL OR expires_at > ?)"];
|
|
18262
|
+
const params = [query.afterRowId, maxRowId, now];
|
|
18258
18263
|
if (query.kinds.length > 0) {
|
|
18259
18264
|
where.push(`kind IN (${query.kinds.map(() => "?").join(", ")})`);
|
|
18260
18265
|
params.push(...query.kinds);
|
|
@@ -18287,7 +18292,7 @@ var listAppFacts = /* @__PURE__ */ __name((sql, query) => {
|
|
|
18287
18292
|
}, "listAppFacts");
|
|
18288
18293
|
var insertFact = /* @__PURE__ */ __name((sql, fact) => {
|
|
18289
18294
|
sql.exec(
|
|
18290
|
-
`INSERT INTO facts (${COLUMNS}) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL)`,
|
|
18295
|
+
`INSERT INTO facts (${COLUMNS}) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?)`,
|
|
18291
18296
|
fact.kind,
|
|
18292
18297
|
fact.scope,
|
|
18293
18298
|
fact.key,
|
|
@@ -18298,7 +18303,8 @@ var insertFact = /* @__PURE__ */ __name((sql, fact) => {
|
|
|
18298
18303
|
fact.eventId ?? null,
|
|
18299
18304
|
fact.dataJson ?? null,
|
|
18300
18305
|
fact.firstAt ?? null,
|
|
18301
|
-
fact.observedAt
|
|
18306
|
+
fact.observedAt,
|
|
18307
|
+
fact.expiresAt ?? null
|
|
18302
18308
|
);
|
|
18303
18309
|
}, "insertFact");
|
|
18304
18310
|
var latestFact = /* @__PURE__ */ __name((sql, coords) => {
|
|
@@ -19051,7 +19057,7 @@ var stillDeclared = /* @__PURE__ */ __name((target, key, source) => {
|
|
|
19051
19057
|
return true;
|
|
19052
19058
|
}
|
|
19053
19059
|
const { queryParams, cookies } = target.app.capture;
|
|
19054
|
-
return source === "query" ? queryParams.
|
|
19060
|
+
return source === "query" ? queryParams.some((entry) => entry.name === key) : cookies.some((entry) => entry.name === key);
|
|
19055
19061
|
}, "stillDeclared");
|
|
19056
19062
|
var deliverableAttribution = /* @__PURE__ */ __name((target, rows) => {
|
|
19057
19063
|
if (rows === void 0) {
|
|
@@ -19339,9 +19345,12 @@ var appSchema = external_exports.object({
|
|
|
19339
19345
|
// Method, auth, and mode stay plain strings for the same tolerance: an unknown value from a newer
|
|
19340
19346
|
// registry refuses that one endpoint instead of turning the app — and every other app — off.
|
|
19341
19347
|
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({
|
|
19348
|
+
// The emitter omits the block entirely for an app that declares no capture, so the default is
|
|
19349
|
+
// what a manifest without a `browser.capture` serializes to.
|
|
19350
|
+
capture: external_exports.object({
|
|
19351
|
+
queryParams: external_exports.array(external_exports.object({ name: external_exports.string(), expiresInSeconds: external_exports.number().optional() }).loose()).default([]),
|
|
19352
|
+
cookies: external_exports.array(external_exports.object({ name: external_exports.string(), expiresInSeconds: external_exports.number().optional() }).loose()).default([])
|
|
19353
|
+
}).default({ queryParams: [], cookies: [] })
|
|
19345
19354
|
});
|
|
19346
19355
|
var registrySchema = external_exports.object({
|
|
19347
19356
|
v: external_exports.literal(1),
|
|
@@ -19397,7 +19406,8 @@ var pushBody = external_exports.object({
|
|
|
19397
19406
|
var rpcBody = external_exports.object({
|
|
19398
19407
|
...identityFields,
|
|
19399
19408
|
kind: external_exports.literal("rpc"),
|
|
19400
|
-
|
|
19409
|
+
// Absent when the caller is an endpoint, a schedule or a ui handler; the callee then sees no user.
|
|
19410
|
+
userId: external_exports.string().min(1).max(64).optional(),
|
|
19401
19411
|
callee: external_exports.string().min(1).max(30),
|
|
19402
19412
|
fn: external_exports.string().min(1).max(64),
|
|
19403
19413
|
args: external_exports.unknown().optional(),
|
|
@@ -19553,7 +19563,7 @@ var calleeFailed = /* @__PURE__ */ __name((callee, reason) => {
|
|
|
19553
19563
|
addSpanEvent("rpc callee failed", { level: "warn", attrs: { app: callee, reason } });
|
|
19554
19564
|
return Response.json({ error: "callee_failed" }, { status: 502 });
|
|
19555
19565
|
}, "calleeFailed");
|
|
19556
|
-
var deliverRpcEffects = /* @__PURE__ */ __name(async (env, body, effects, install) => {
|
|
19566
|
+
var deliverRpcEffects = /* @__PURE__ */ __name(async (env, { body, userId, effects, install }) => {
|
|
19557
19567
|
for (const ord of effects.rejectedOrds) {
|
|
19558
19568
|
addSpanEvent("app effects rejected over the per-install caps", { level: "warn", attrs: { app: body.callee, ord } });
|
|
19559
19569
|
}
|
|
@@ -19562,21 +19572,21 @@ var deliverRpcEffects = /* @__PURE__ */ __name(async (env, body, effects, instal
|
|
|
19562
19572
|
slug: body.callee,
|
|
19563
19573
|
groups: effects.accepted,
|
|
19564
19574
|
depth: body.depth,
|
|
19565
|
-
anonId:
|
|
19575
|
+
anonId: userId,
|
|
19566
19576
|
installs: [install],
|
|
19567
19577
|
deliverPush: /* @__PURE__ */ __name((push) => {
|
|
19568
19578
|
pushes.push(push);
|
|
19569
19579
|
}, "deliverPush"),
|
|
19570
19580
|
// The broker runs outside the user's AnonDO, so this reaches it the same way a push does.
|
|
19571
19581
|
writeAttribution: /* @__PURE__ */ __name(async (slug, writes) => {
|
|
19572
|
-
await anonStub(env,
|
|
19582
|
+
await anonStub(env, userId).appAttribution({ slug, writes }, currentTraceContext());
|
|
19573
19583
|
}, "writeAttribution"),
|
|
19574
19584
|
writeContext: /* @__PURE__ */ __name(async (slug, writes) => {
|
|
19575
|
-
await anonStub(env,
|
|
19585
|
+
await anonStub(env, userId).appContext({ slug, writes }, currentTraceContext());
|
|
19576
19586
|
}, "writeContext")
|
|
19577
19587
|
});
|
|
19578
19588
|
for (const push of pushes) {
|
|
19579
|
-
await anonStub(env,
|
|
19589
|
+
await anonStub(env, userId).appPush(push, currentTraceContext());
|
|
19580
19590
|
}
|
|
19581
19591
|
}, "deliverRpcEffects");
|
|
19582
19592
|
var runRelayRpc = /* @__PURE__ */ __name(async (env, { context, body, callee, install }) => {
|
|
@@ -19593,7 +19603,8 @@ var runRelayRpc = /* @__PURE__ */ __name(async (env, { context, body, callee, in
|
|
|
19593
19603
|
app: { slug: body.callee, version: callee.version },
|
|
19594
19604
|
installs: [install],
|
|
19595
19605
|
// No bucket projection for rpc: the callee sees only the subject's id, never its profile.
|
|
19596
|
-
|
|
19606
|
+
// Omitted entirely when the caller had no visitor, so the callee's ctx.user is null.
|
|
19607
|
+
...body.userId === void 0 ? {} : { user: { anonId: body.userId, globalId: null, properties: {} } },
|
|
19597
19608
|
depth: body.depth,
|
|
19598
19609
|
// JSON.stringify drops an undefined key, and the envelope requires `args` to be present.
|
|
19599
19610
|
rpc: { caller: body.slug, fn: body.fn, args: body.args ?? null }
|
|
@@ -19625,7 +19636,13 @@ var runRelayRpc = /* @__PURE__ */ __name(async (env, { context, body, callee, in
|
|
|
19625
19636
|
if (verdict.rpc === null) {
|
|
19626
19637
|
return calleeFailed(body.callee, "The response carries no rpc result.");
|
|
19627
19638
|
}
|
|
19628
|
-
|
|
19639
|
+
if (body.userId === void 0) {
|
|
19640
|
+
if (verdict.effects.accepted.length > 0) {
|
|
19641
|
+
addSpanEvent("rpc effects dropped: the call carries no subject", { level: "warn", attrs: { app: body.callee } });
|
|
19642
|
+
}
|
|
19643
|
+
} else {
|
|
19644
|
+
await deliverRpcEffects(env, { body, userId: body.userId, effects: verdict.effects, install });
|
|
19645
|
+
}
|
|
19629
19646
|
return Response.json({ result: verdict.rpc.result });
|
|
19630
19647
|
}, "runRelayRpc");
|
|
19631
19648
|
var relayRpc = /* @__PURE__ */ __name(async (env, input) => {
|
|
@@ -23822,7 +23839,7 @@ var declaredCapture = /* @__PURE__ */ __name((app2) => {
|
|
|
23822
23839
|
if (queryParams.length === 0 && cookies.length === 0 || !app2.installs.some((install) => install.active)) {
|
|
23823
23840
|
return void 0;
|
|
23824
23841
|
}
|
|
23825
|
-
return { queryParams, cookies };
|
|
23842
|
+
return { queryParams: queryParams.map((entry) => entry.name), cookies: cookies.map((entry) => entry.name) };
|
|
23826
23843
|
}, "declaredCapture");
|
|
23827
23844
|
var appsMetadata = /* @__PURE__ */ __name(() => {
|
|
23828
23845
|
const { registry: registry3 } = siteRegistry();
|
|
@@ -24299,9 +24316,22 @@ var rowOf2 = /* @__PURE__ */ __name((row) => {
|
|
|
24299
24316
|
value: row.value,
|
|
24300
24317
|
createdAt: row.firstAt,
|
|
24301
24318
|
updatedAt: row.observedAt,
|
|
24302
|
-
source: row.source
|
|
24319
|
+
source: row.source,
|
|
24320
|
+
expiresAt: row.expiresAt
|
|
24303
24321
|
};
|
|
24304
24322
|
}, "rowOf");
|
|
24323
|
+
var deadline = /* @__PURE__ */ __name((sql, write, now) => {
|
|
24324
|
+
if (write.ttlMs === null) {
|
|
24325
|
+
return null;
|
|
24326
|
+
}
|
|
24327
|
+
const { firstSeen } = sql.exec(
|
|
24328
|
+
"SELECT MIN(observed_at) AS firstSeen FROM facts WHERE kind = 'attribution' AND scope = ? AND key = ? AND value = ?",
|
|
24329
|
+
write.appId,
|
|
24330
|
+
write.key,
|
|
24331
|
+
write.value
|
|
24332
|
+
).one();
|
|
24333
|
+
return (firstSeen ?? now) + write.ttlMs;
|
|
24334
|
+
}, "deadline");
|
|
24305
24335
|
var hydrate2 = /* @__PURE__ */ __name((sql, cache) => {
|
|
24306
24336
|
let map2 = cache?.map();
|
|
24307
24337
|
if (map2 === void 0) {
|
|
@@ -24321,16 +24351,18 @@ var withinCaps = /* @__PURE__ */ __name((write, stored) => {
|
|
|
24321
24351
|
}
|
|
24322
24352
|
return stored.has(write.key) || stored.size < MAX_ATTRIBUTION_KEYS_PER_APP;
|
|
24323
24353
|
}, "withinCaps");
|
|
24324
|
-
var persist2 = /* @__PURE__ */ __name((sql, write,
|
|
24354
|
+
var persist2 = /* @__PURE__ */ __name((sql, write, state) => {
|
|
24355
|
+
const { now, previous, expiresAt: expiresAt2 } = state;
|
|
24325
24356
|
const createdAt = previous?.createdAt ?? now;
|
|
24326
24357
|
if (previous !== void 0 && previous.value === write.value) {
|
|
24327
24358
|
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)",
|
|
24359
|
+
"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
24360
|
write.source,
|
|
24361
|
+
expiresAt2,
|
|
24330
24362
|
write.appId,
|
|
24331
24363
|
write.key
|
|
24332
24364
|
);
|
|
24333
|
-
return { value: write.value, createdAt, updatedAt: previous.updatedAt, source: write.source };
|
|
24365
|
+
return { value: write.value, createdAt, updatedAt: previous.updatedAt, source: write.source, expiresAt: expiresAt2 };
|
|
24334
24366
|
}
|
|
24335
24367
|
insertFact(sql, {
|
|
24336
24368
|
kind: "attribution",
|
|
@@ -24339,9 +24371,10 @@ var persist2 = /* @__PURE__ */ __name((sql, write, now, previous) => {
|
|
|
24339
24371
|
value: write.value,
|
|
24340
24372
|
source: write.source,
|
|
24341
24373
|
firstAt: createdAt,
|
|
24342
|
-
observedAt: now
|
|
24374
|
+
observedAt: now,
|
|
24375
|
+
expiresAt: expiresAt2
|
|
24343
24376
|
});
|
|
24344
|
-
return { value: write.value, createdAt, updatedAt: now, source: write.source };
|
|
24377
|
+
return { value: write.value, createdAt, updatedAt: now, source: write.source, expiresAt: expiresAt2 };
|
|
24345
24378
|
}, "persist");
|
|
24346
24379
|
var writeAttribution = /* @__PURE__ */ __name((sql, writes, now, cache) => {
|
|
24347
24380
|
const map2 = hydrate2(sql, cache);
|
|
@@ -24352,18 +24385,19 @@ var writeAttribution = /* @__PURE__ */ __name((sql, writes, now, cache) => {
|
|
|
24352
24385
|
if (!withinCaps(write, forApp)) {
|
|
24353
24386
|
continue;
|
|
24354
24387
|
}
|
|
24355
|
-
|
|
24356
|
-
|
|
24388
|
+
const expiresAt2 = deadline(sql, write, now);
|
|
24389
|
+
if (previous?.value !== write.value || previous.source !== write.source || previous.expiresAt !== expiresAt2) {
|
|
24390
|
+
forApp.set(write.key, persist2(sql, write, { now, previous, expiresAt: expiresAt2 }));
|
|
24357
24391
|
map2.set(write.appId, forApp);
|
|
24358
24392
|
}
|
|
24359
24393
|
accepted.push(write);
|
|
24360
24394
|
}
|
|
24361
24395
|
return accepted;
|
|
24362
24396
|
}, "writeAttribution");
|
|
24363
|
-
var allAttribution = /* @__PURE__ */ __name((sql, cache) => {
|
|
24397
|
+
var allAttribution = /* @__PURE__ */ __name((sql, now, cache) => {
|
|
24364
24398
|
const all = {};
|
|
24365
24399
|
for (const [appId, rows] of hydrate2(sql, cache)) {
|
|
24366
|
-
all[appId] = Object.fromEntries(rows);
|
|
24400
|
+
all[appId] = Object.fromEntries([...rows].filter(([, row]) => row.expiresAt === null || row.expiresAt > now));
|
|
24367
24401
|
}
|
|
24368
24402
|
return all;
|
|
24369
24403
|
}, "allAttribution");
|
|
@@ -24385,12 +24419,17 @@ var appWriteAllowed = /* @__PURE__ */ __name((registry3, consent2, slug, enforce
|
|
|
24385
24419
|
}
|
|
24386
24420
|
return true;
|
|
24387
24421
|
}, "appWriteAllowed");
|
|
24388
|
-
var
|
|
24389
|
-
|
|
24390
|
-
|
|
24422
|
+
var declaration = /* @__PURE__ */ __name((app2, key) => {
|
|
24423
|
+
const param = app2.capture.queryParams.find((entry) => entry.name === key);
|
|
24424
|
+
const declared = param ?? app2.capture.cookies.find((entry) => entry.name === key);
|
|
24425
|
+
if (declared === void 0) {
|
|
24426
|
+
return void 0;
|
|
24391
24427
|
}
|
|
24392
|
-
return
|
|
24393
|
-
|
|
24428
|
+
return {
|
|
24429
|
+
source: param === void 0 ? "cookie" : "query",
|
|
24430
|
+
ttlMs: declared.expiresInSeconds === void 0 ? null : declared.expiresInSeconds * 1e3
|
|
24431
|
+
};
|
|
24432
|
+
}, "declaration");
|
|
24394
24433
|
var acceptedCaptures = /* @__PURE__ */ __name((registry3, consent2, apps, enforced) => {
|
|
24395
24434
|
const accepted = [];
|
|
24396
24435
|
for (const [slug, values] of Object.entries(apps)) {
|
|
@@ -24399,8 +24438,8 @@ var acceptedCaptures = /* @__PURE__ */ __name((registry3, consent2, apps, enforc
|
|
|
24399
24438
|
continue;
|
|
24400
24439
|
}
|
|
24401
24440
|
for (const [key, value] of Object.entries(values)) {
|
|
24402
|
-
const
|
|
24403
|
-
if (
|
|
24441
|
+
const declared = declaration(app2, key);
|
|
24442
|
+
if (declared === void 0) {
|
|
24404
24443
|
addSpanEvent("capture dropped: the app declares no such name", { level: "warn", attrs: { app: slug, key } });
|
|
24405
24444
|
continue;
|
|
24406
24445
|
}
|
|
@@ -24408,7 +24447,7 @@ var acceptedCaptures = /* @__PURE__ */ __name((registry3, consent2, apps, enforc
|
|
|
24408
24447
|
addSpanEvent("capture dropped: over the size cap", { level: "warn", attrs: { app: slug, key } });
|
|
24409
24448
|
continue;
|
|
24410
24449
|
}
|
|
24411
|
-
accepted.push({ appId: slug, key, value, source });
|
|
24450
|
+
accepted.push({ appId: slug, key, value, source: declared.source, ttlMs: declared.ttlMs });
|
|
24412
24451
|
}
|
|
24413
24452
|
}
|
|
24414
24453
|
return accepted;
|
|
@@ -24712,6 +24751,19 @@ var cachedKv = /* @__PURE__ */ __name((kv) => {
|
|
|
24712
24751
|
};
|
|
24713
24752
|
}, "cachedKv");
|
|
24714
24753
|
|
|
24754
|
+
// src/id-graph/migrate.ts
|
|
24755
|
+
var VERSION_KEY = "schemaVersion";
|
|
24756
|
+
var applyMigrations = /* @__PURE__ */ __name((storage, migrations) => {
|
|
24757
|
+
const applied = storage.kv.get(VERSION_KEY) ?? 0;
|
|
24758
|
+
for (const [index, migration] of migrations.entries()) {
|
|
24759
|
+
if (index < applied) {
|
|
24760
|
+
continue;
|
|
24761
|
+
}
|
|
24762
|
+
storage.sql.exec(migration);
|
|
24763
|
+
storage.kv.put(VERSION_KEY, index + 1);
|
|
24764
|
+
}
|
|
24765
|
+
}, "applyMigrations");
|
|
24766
|
+
|
|
24715
24767
|
// src/id-graph/retention.ts
|
|
24716
24768
|
var RETENTION_MS = 90 * 24 * 60 * 60 * 1e3;
|
|
24717
24769
|
var CLEANUP_INTERVAL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
@@ -25223,8 +25275,8 @@ var expiryHorizon = /* @__PURE__ */ __name((storage, lastActivity) => {
|
|
|
25223
25275
|
}, "expiryHorizon");
|
|
25224
25276
|
var cleanupTarget = /* @__PURE__ */ __name((storage, now, lastActivity) => {
|
|
25225
25277
|
const horizon = expiryHorizon(storage, lastActivity);
|
|
25226
|
-
const
|
|
25227
|
-
const target =
|
|
25278
|
+
const deadline2 = nextRetentionDeadline(storage.sql);
|
|
25279
|
+
const target = deadline2 === null ? horizon : Math.min(deadline2, horizon);
|
|
25228
25280
|
return Math.max(target, now + IMMEDIATE_CLEANUP_DELAY_MS);
|
|
25229
25281
|
}, "cleanupTarget");
|
|
25230
25282
|
var standsDown = /* @__PURE__ */ __name(async (storage, staleActivity) => {
|
|
@@ -25389,7 +25441,8 @@ var assignAll = /* @__PURE__ */ __name(async (env, sorted, claim) => {
|
|
|
25389
25441
|
}, "assignAll");
|
|
25390
25442
|
|
|
25391
25443
|
// src/id-graph/anon/schema.ts
|
|
25392
|
-
var
|
|
25444
|
+
var ANON_MIGRATIONS = [
|
|
25445
|
+
`
|
|
25393
25446
|
CREATE TABLE IF NOT EXISTS facts (
|
|
25394
25447
|
kind TEXT NOT NULL,
|
|
25395
25448
|
scope TEXT NOT NULL,
|
|
@@ -25406,7 +25459,9 @@ CREATE TABLE IF NOT EXISTS facts (
|
|
|
25406
25459
|
);
|
|
25407
25460
|
CREATE INDEX IF NOT EXISTS idx_facts_key ON facts (kind, scope, key, observed_at);
|
|
25408
25461
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_facts_conversion ON facts (key) WHERE kind = 'conversion';
|
|
25409
|
-
|
|
25462
|
+
`,
|
|
25463
|
+
"ALTER TABLE facts ADD COLUMN expires_at INTEGER;"
|
|
25464
|
+
];
|
|
25410
25465
|
|
|
25411
25466
|
// src/id-graph/anon/websocket.ts
|
|
25412
25467
|
var MAX_BROWSER_RESULT_BYTES = 1024;
|
|
@@ -25600,7 +25655,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
25600
25655
|
cleanupAlarm;
|
|
25601
25656
|
constructor(ctx, env) {
|
|
25602
25657
|
super(ctx, env);
|
|
25603
|
-
ctx.storage
|
|
25658
|
+
applyMigrations(ctx.storage, ANON_MIGRATIONS);
|
|
25604
25659
|
this.kvCache = cachedKv(ctx.storage.kv);
|
|
25605
25660
|
this.cachedStorage = { kv: this.kvCache, sql: ctx.storage.sql };
|
|
25606
25661
|
}
|
|
@@ -25755,7 +25810,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
25755
25810
|
if (!appWriteAllowed(siteRegistry().registry, consentState2(this.kv), input.slug, consentEnforced(this.env))) {
|
|
25756
25811
|
return;
|
|
25757
25812
|
}
|
|
25758
|
-
this.storeAttribution(input.writes.map((write) => ({ ...write, appId: input.slug, source: "app" })));
|
|
25813
|
+
this.storeAttribution(input.writes.map((write) => ({ ...write, appId: input.slug, source: "app", ttlMs: null })));
|
|
25759
25814
|
});
|
|
25760
25815
|
});
|
|
25761
25816
|
}
|
|
@@ -25877,7 +25932,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
25877
25932
|
if (!consentAllows(consentState2(this.kv), slug, app2.consent.category, consentEnforced(this.env))) {
|
|
25878
25933
|
return null;
|
|
25879
25934
|
}
|
|
25880
|
-
return listAppFacts(this.sql, query);
|
|
25935
|
+
return listAppFacts(this.sql, query, Date.now());
|
|
25881
25936
|
});
|
|
25882
25937
|
});
|
|
25883
25938
|
}
|
|
@@ -26057,7 +26112,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
26057
26112
|
// A wipe dropped the sql tables, so reading them would throw — and reviving for a transform
|
|
26058
26113
|
// snapshot would strand recreated storage when the chain fails or drops.
|
|
26059
26114
|
custom: this.wiped ? {} : customProperties(this.sql, this.customPropsMirror),
|
|
26060
|
-
attribution: this.wiped ? {} : allAttribution(this.sql, this.attributionMirror),
|
|
26115
|
+
attribution: this.wiped ? {} : allAttribution(this.sql, Date.now(), this.attributionMirror),
|
|
26061
26116
|
context: this.wiped ? { private: {}, shared: {} } : {
|
|
26062
26117
|
private: allContext(this.sql, "private", this.contextMirror),
|
|
26063
26118
|
shared: readableSharedContext(
|
|
@@ -26393,7 +26448,7 @@ var AnonDO = class extends DurableObject2 {
|
|
|
26393
26448
|
}
|
|
26394
26449
|
revive() {
|
|
26395
26450
|
if (this.wiped) {
|
|
26396
|
-
this.ctx.storage
|
|
26451
|
+
applyMigrations(this.ctx.storage, ANON_MIGRATIONS);
|
|
26397
26452
|
this.wiped = false;
|
|
26398
26453
|
}
|
|
26399
26454
|
}
|
|
@@ -26595,7 +26650,8 @@ var storedGlobalId = /* @__PURE__ */ __name((kv) => {
|
|
|
26595
26650
|
}, "storedGlobalId");
|
|
26596
26651
|
|
|
26597
26652
|
// src/id-graph/global/schema.ts
|
|
26598
|
-
var
|
|
26653
|
+
var GLOBAL_MIGRATIONS = [
|
|
26654
|
+
`
|
|
26599
26655
|
CREATE TABLE IF NOT EXISTS facts (
|
|
26600
26656
|
kind TEXT NOT NULL,
|
|
26601
26657
|
scope TEXT NOT NULL,
|
|
@@ -26610,7 +26666,8 @@ CREATE TABLE IF NOT EXISTS facts (
|
|
|
26610
26666
|
observed_at INTEGER NOT NULL
|
|
26611
26667
|
);
|
|
26612
26668
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_facts_key ON facts (kind, scope, key);
|
|
26613
|
-
|
|
26669
|
+
`
|
|
26670
|
+
];
|
|
26614
26671
|
|
|
26615
26672
|
// src/id-graph/global/do.ts
|
|
26616
26673
|
var GlobalDO = class extends DurableObject3 {
|
|
@@ -26623,7 +26680,7 @@ var GlobalDO = class extends DurableObject3 {
|
|
|
26623
26680
|
wiped = false;
|
|
26624
26681
|
constructor(ctx, env) {
|
|
26625
26682
|
super(ctx, env);
|
|
26626
|
-
ctx.storage
|
|
26683
|
+
applyMigrations(ctx.storage, GLOBAL_MIGRATIONS);
|
|
26627
26684
|
this.kvCache = cachedKv(ctx.storage.kv);
|
|
26628
26685
|
this.identifiersMirror = identifiersCache2();
|
|
26629
26686
|
}
|
|
@@ -26690,7 +26747,7 @@ var GlobalDO = class extends DurableObject3 {
|
|
|
26690
26747
|
}
|
|
26691
26748
|
revive() {
|
|
26692
26749
|
if (this.wiped) {
|
|
26693
|
-
this.ctx.storage
|
|
26750
|
+
applyMigrations(this.ctx.storage, GLOBAL_MIGRATIONS);
|
|
26694
26751
|
this.wiped = false;
|
|
26695
26752
|
}
|
|
26696
26753
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"main": {
|
|
3
3
|
"version": "0.1.0",
|
|
4
|
-
"gitSha": "
|
|
5
|
-
"builtAt": "2026-08-
|
|
4
|
+
"gitSha": "d2dbd3fccee9436ca4a4e3a6429925e6f9f4d0ce",
|
|
5
|
+
"builtAt": "2026-08-26T15:36:18.499Z",
|
|
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": "d2dbd3fccee9436ca4a4e3a6429925e6f9f4d0ce",
|
|
44
|
+
"builtAt": "2026-08-26T15:36:22.261Z",
|
|
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"] },
|