@prenta/core 8.9.2 → 8.11.0
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/CHANGELOG.md +105 -0
- package/dist/actions.d.ts +25 -0
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +51 -4
- package/dist/actions.js.map +1 -1
- package/dist/ai/batch-links.d.ts +105 -0
- package/dist/ai/batch-links.d.ts.map +1 -0
- package/dist/ai/batch-links.js +667 -0
- package/dist/ai/batch-links.js.map +1 -0
- package/dist/ai/batch-list.d.ts +127 -4
- package/dist/ai/batch-list.d.ts.map +1 -1
- package/dist/ai/batch-list.js +250 -14
- package/dist/ai/batch-list.js.map +1 -1
- package/dist/ai/providers/http.d.ts +4 -0
- package/dist/ai/providers/http.d.ts.map +1 -1
- package/dist/ai/providers/http.js +9 -8
- package/dist/ai/providers/http.js.map +1 -1
- package/dist/ai/tasks/editor-text.d.ts +39 -0
- package/dist/ai/tasks/editor-text.d.ts.map +1 -0
- package/dist/ai/tasks/editor-text.js +45 -0
- package/dist/ai/tasks/editor-text.js.map +1 -0
- package/dist/ai/tasks/link-embeddings.d.ts +22 -0
- package/dist/ai/tasks/link-embeddings.d.ts.map +1 -1
- package/dist/ai/tasks/link-embeddings.js +24 -10
- package/dist/ai/tasks/link-embeddings.js.map +1 -1
- package/dist/ai/usage.d.ts +7 -0
- package/dist/ai/usage.d.ts.map +1 -1
- package/dist/ai/usage.js +11 -0
- package/dist/ai/usage.js.map +1 -1
- package/dist/api/guarded-db.d.ts +6 -0
- package/dist/api/guarded-db.d.ts.map +1 -1
- package/dist/api/guarded-db.js +13 -1
- package/dist/api/guarded-db.js.map +1 -1
- package/dist/api/handlers.d.ts.map +1 -1
- package/dist/api/handlers.js +2 -0
- package/dist/api/handlers.js.map +1 -1
- package/dist/api/openapi.d.ts.map +1 -1
- package/dist/api/openapi.js +103 -0
- package/dist/api/openapi.js.map +1 -1
- package/dist/api/route-helpers/forms.d.ts.map +1 -1
- package/dist/api/route-helpers/forms.js +7 -1
- package/dist/api/route-helpers/forms.js.map +1 -1
- package/dist/api/routes/ai-authoring.d.ts.map +1 -1
- package/dist/api/routes/ai-authoring.js +1 -0
- package/dist/api/routes/ai-authoring.js.map +1 -1
- package/dist/api/routes/ai-seo.d.ts.map +1 -1
- package/dist/api/routes/ai-seo.js +20 -18
- package/dist/api/routes/ai-seo.js.map +1 -1
- package/dist/api/routes/media.d.ts.map +1 -1
- package/dist/api/routes/media.js +3 -1
- package/dist/api/routes/media.js.map +1 -1
- package/dist/api/routes/scheduling.d.ts.map +1 -1
- package/dist/api/routes/scheduling.js +33 -2
- package/dist/api/routes/scheduling.js.map +1 -1
- package/dist/appearance/color.d.ts +17 -0
- package/dist/appearance/color.d.ts.map +1 -1
- package/dist/appearance/color.js +162 -1
- package/dist/appearance/color.js.map +1 -1
- package/dist/appearance/index.d.ts +3 -3
- package/dist/appearance/index.d.ts.map +1 -1
- package/dist/appearance/index.js +2 -2
- package/dist/appearance/index.js.map +1 -1
- package/dist/appearance/resolve.d.ts.map +1 -1
- package/dist/appearance/resolve.js +4 -2
- package/dist/appearance/resolve.js.map +1 -1
- package/dist/appearance/theme-tokens.d.ts +60 -11
- package/dist/appearance/theme-tokens.d.ts.map +1 -1
- package/dist/appearance/theme-tokens.js +184 -175
- package/dist/appearance/theme-tokens.js.map +1 -1
- package/dist/appearance/types.d.ts +16 -2
- package/dist/appearance/types.d.ts.map +1 -1
- package/dist/astro-site.d.ts +1 -11
- package/dist/astro-site.d.ts.map +1 -1
- package/dist/astro-site.js +4 -0
- package/dist/astro-site.js.map +1 -1
- package/dist/auth/team.d.ts +1 -0
- package/dist/auth/team.d.ts.map +1 -1
- package/dist/auth/team.js +1 -1
- package/dist/auth/team.js.map +1 -1
- package/dist/cache/http.d.ts +20 -0
- package/dist/cache/http.d.ts.map +1 -1
- package/dist/cache/http.js +42 -0
- package/dist/cache/http.js.map +1 -1
- package/dist/chrome/announcement.d.ts +30 -0
- package/dist/chrome/announcement.d.ts.map +1 -0
- package/dist/chrome/announcement.js +190 -0
- package/dist/chrome/announcement.js.map +1 -0
- package/dist/chrome/codegen.d.ts +17 -0
- package/dist/chrome/codegen.d.ts.map +1 -0
- package/dist/chrome/codegen.js +96 -0
- package/dist/chrome/codegen.js.map +1 -0
- package/dist/chrome/contract.d.ts +161 -0
- package/dist/chrome/contract.d.ts.map +1 -0
- package/dist/chrome/contract.js +2 -0
- package/dist/chrome/contract.js.map +1 -0
- package/dist/chrome/memory-store.d.ts +15 -0
- package/dist/chrome/memory-store.d.ts.map +1 -0
- package/dist/chrome/memory-store.js +54 -0
- package/dist/chrome/memory-store.js.map +1 -0
- package/dist/chrome/overrides.d.ts +25 -0
- package/dist/chrome/overrides.d.ts.map +1 -0
- package/dist/chrome/overrides.js +180 -0
- package/dist/chrome/overrides.js.map +1 -0
- package/dist/chrome/parse.d.ts +36 -0
- package/dist/chrome/parse.d.ts.map +1 -0
- package/dist/chrome/parse.js +277 -0
- package/dist/chrome/parse.js.map +1 -0
- package/dist/chrome/prisma-store.d.ts +11 -0
- package/dist/chrome/prisma-store.d.ts.map +1 -0
- package/dist/chrome/prisma-store.js +48 -0
- package/dist/chrome/prisma-store.js.map +1 -0
- package/dist/chrome/public.d.ts +22 -0
- package/dist/chrome/public.d.ts.map +1 -0
- package/dist/chrome/public.js +181 -0
- package/dist/chrome/public.js.map +1 -0
- package/dist/chrome/resolve.d.ts +36 -0
- package/dist/chrome/resolve.d.ts.map +1 -0
- package/dist/chrome/resolve.js +165 -0
- package/dist/chrome/resolve.js.map +1 -0
- package/dist/chrome/revalidate.d.ts +9 -0
- package/dist/chrome/revalidate.d.ts.map +1 -0
- package/dist/chrome/revalidate.js +51 -0
- package/dist/chrome/revalidate.js.map +1 -0
- package/dist/chrome/routes.d.ts +14 -0
- package/dist/chrome/routes.d.ts.map +1 -0
- package/dist/chrome/routes.js +349 -0
- package/dist/chrome/routes.js.map +1 -0
- package/dist/chrome/schema.d.ts +231 -0
- package/dist/chrome/schema.d.ts.map +1 -0
- package/dist/chrome/schema.js +73 -0
- package/dist/chrome/schema.js.map +1 -0
- package/dist/chrome/service.d.ts +76 -0
- package/dist/chrome/service.d.ts.map +1 -0
- package/dist/chrome/service.js +286 -0
- package/dist/chrome/service.js.map +1 -0
- package/dist/chrome/settings.d.ts +14 -0
- package/dist/chrome/settings.d.ts.map +1 -0
- package/dist/chrome/settings.js +142 -0
- package/dist/chrome/settings.js.map +1 -0
- package/dist/chrome/store.d.ts +48 -0
- package/dist/chrome/store.d.ts.map +1 -0
- package/dist/chrome/store.js +26 -0
- package/dist/chrome/store.js.map +1 -0
- package/dist/chrome/values.d.ts +98 -0
- package/dist/chrome/values.d.ts.map +1 -0
- package/dist/chrome/values.js +491 -0
- package/dist/chrome/values.js.map +1 -0
- package/dist/config/define.d.ts +2 -0
- package/dist/config/define.d.ts.map +1 -1
- package/dist/config/define.js +2 -0
- package/dist/config/define.js.map +1 -1
- package/dist/db/model-types.d.ts +62 -0
- package/dist/db/model-types.d.ts.map +1 -1
- package/dist/forms/attribution-settings.d.ts +23 -0
- package/dist/forms/attribution-settings.d.ts.map +1 -0
- package/dist/forms/attribution-settings.js +42 -0
- package/dist/forms/attribution-settings.js.map +1 -0
- package/dist/forms/attribution.d.ts +5 -1
- package/dist/forms/attribution.d.ts.map +1 -1
- package/dist/forms/attribution.js +6 -4
- package/dist/forms/attribution.js.map +1 -1
- package/dist/forms/client-validation.d.ts.map +1 -1
- package/dist/forms/client-validation.js +3 -0
- package/dist/forms/client-validation.js.map +1 -1
- package/dist/forms/form-model.d.ts +109 -0
- package/dist/forms/form-model.d.ts.map +1 -0
- package/dist/forms/form-model.js +192 -0
- package/dist/forms/form-model.js.map +1 -0
- package/dist/forms/index.d.ts +2 -2
- package/dist/forms/index.d.ts.map +1 -1
- package/dist/forms/index.js +1 -1
- package/dist/forms/index.js.map +1 -1
- package/dist/forms/service.d.ts +19 -1
- package/dist/forms/service.d.ts.map +1 -1
- package/dist/forms/service.js +152 -0
- package/dist/forms/service.js.map +1 -1
- package/dist/forms/spam.d.ts +0 -12
- package/dist/forms/spam.d.ts.map +1 -1
- package/dist/forms/spam.js +30 -0
- package/dist/forms/spam.js.map +1 -1
- package/dist/forms/submission.d.ts +7 -0
- package/dist/forms/submission.d.ts.map +1 -1
- package/dist/forms/submission.js +33 -0
- package/dist/forms/submission.js.map +1 -1
- package/dist/forms/types.d.ts +26 -1
- package/dist/forms/types.d.ts.map +1 -1
- package/dist/forms/types.js +9 -1
- package/dist/forms/types.js.map +1 -1
- package/dist/generated/browser.d.ts +35 -3
- package/dist/generated/client.d.ts +35 -3
- package/dist/generated/internal/class.d.ts +44 -0
- package/dist/generated/internal/class.js +4 -4
- package/dist/generated/internal/prismaNamespace.d.ts +403 -1
- package/dist/generated/internal/prismaNamespace.js +95 -1
- package/dist/generated/internal/prismaNamespaceBrowser.d.ts +102 -0
- package/dist/generated/internal/prismaNamespaceBrowser.js +95 -1
- package/dist/generated/models/ChromeRegion.d.ts +1203 -0
- package/dist/generated/models/ChromeRegion.js +1 -0
- package/dist/generated/models/Job.d.ts +732 -5
- package/dist/generated/models/JobEvent.d.ts +1339 -0
- package/dist/generated/models/JobEvent.js +1 -0
- package/dist/generated/models/JobReviewItem.d.ts +1470 -0
- package/dist/generated/models/JobReviewItem.js +1 -0
- package/dist/generated/models/SystemSetting.d.ts +952 -0
- package/dist/generated/models/SystemSetting.js +1 -0
- package/dist/generated/models.d.ts +4 -0
- package/dist/index.d.ts +12 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -3
- package/dist/index.js.map +1 -1
- package/dist/jobs/batch-ai.d.ts +53 -0
- package/dist/jobs/batch-ai.d.ts.map +1 -0
- package/dist/jobs/batch-ai.js +314 -0
- package/dist/jobs/batch-ai.js.map +1 -0
- package/dist/jobs/contract.d.ts +193 -0
- package/dist/jobs/contract.d.ts.map +1 -0
- package/dist/jobs/contract.js +2 -0
- package/dist/jobs/contract.js.map +1 -0
- package/dist/jobs/error-text.d.ts +23 -0
- package/dist/jobs/error-text.d.ts.map +1 -0
- package/dist/jobs/error-text.js +110 -0
- package/dist/jobs/error-text.js.map +1 -0
- package/dist/jobs/handlers.d.ts.map +1 -1
- package/dist/jobs/handlers.js +48 -7
- package/dist/jobs/handlers.js.map +1 -1
- package/dist/jobs/memory-store.d.ts +26 -2
- package/dist/jobs/memory-store.d.ts.map +1 -1
- package/dist/jobs/memory-store.js +157 -3
- package/dist/jobs/memory-store.js.map +1 -1
- package/dist/jobs/outcomes.d.ts +46 -0
- package/dist/jobs/outcomes.d.ts.map +1 -0
- package/dist/jobs/outcomes.js +251 -0
- package/dist/jobs/outcomes.js.map +1 -0
- package/dist/jobs/prisma-store.d.ts +14 -2
- package/dist/jobs/prisma-store.d.ts.map +1 -1
- package/dist/jobs/prisma-store.js +257 -15
- package/dist/jobs/prisma-store.js.map +1 -1
- package/dist/jobs/queue.d.ts +62 -2
- package/dist/jobs/queue.d.ts.map +1 -1
- package/dist/jobs/queue.js +412 -36
- package/dist/jobs/queue.js.map +1 -1
- package/dist/jobs/retention.d.ts +11 -0
- package/dist/jobs/retention.d.ts.map +1 -0
- package/dist/jobs/retention.js +18 -0
- package/dist/jobs/retention.js.map +1 -0
- package/dist/jobs/review.d.ts +111 -0
- package/dist/jobs/review.d.ts.map +1 -0
- package/dist/jobs/review.js +461 -0
- package/dist/jobs/review.js.map +1 -0
- package/dist/jobs/routes.d.ts.map +1 -1
- package/dist/jobs/routes.js +491 -16
- package/dist/jobs/routes.js.map +1 -1
- package/dist/jobs/serialize.d.ts +40 -0
- package/dist/jobs/serialize.d.ts.map +1 -0
- package/dist/jobs/serialize.js +275 -0
- package/dist/jobs/serialize.js.map +1 -0
- package/dist/jobs/types.d.ts +141 -2
- package/dist/jobs/types.d.ts.map +1 -1
- package/dist/jobs/worker-status.d.ts +27 -0
- package/dist/jobs/worker-status.d.ts.map +1 -0
- package/dist/jobs/worker-status.js +66 -0
- package/dist/jobs/worker-status.js.map +1 -0
- package/dist/media/optimize.d.ts.map +1 -1
- package/dist/media/optimize.js +4 -2
- package/dist/media/optimize.js.map +1 -1
- package/dist/next-site.d.ts +1 -0
- package/dist/next-site.d.ts.map +1 -1
- package/dist/next-site.js +3 -0
- package/dist/next-site.js.map +1 -1
- package/dist/seo/audit-runner.d.ts.map +1 -1
- package/dist/seo/audit-runner.js +23 -2
- package/dist/seo/audit-runner.js.map +1 -1
- package/dist/seo/issue-fix-link-insert.d.ts +11 -0
- package/dist/seo/issue-fix-link-insert.d.ts.map +1 -1
- package/dist/seo/issue-fix-link-insert.js +122 -8
- package/dist/seo/issue-fix-link-insert.js.map +1 -1
- package/dist/server-site.d.ts.map +1 -1
- package/dist/server-site.js +15 -0
- package/dist/server-site.js.map +1 -1
- package/dist/site.d.ts +8 -0
- package/dist/site.d.ts.map +1 -1
- package/dist/site.js +6 -0
- package/dist/site.js.map +1 -1
- package/dist/webhooks/index.d.ts +3 -1
- package/dist/webhooks/index.d.ts.map +1 -1
- package/dist/webhooks/index.js.map +1 -1
- package/package.json +6 -1
- package/prisma/migrations/0025_job_cancel_review/down.sql +60 -0
- package/prisma/migrations/0025_job_cancel_review/migration.sql +56 -0
- package/prisma/migrations/0026_job_creator_system_settings/down.sql +30 -0
- package/prisma/migrations/0026_job_creator_system_settings/migration.sql +14 -0
- package/prisma/migrations/0027_chrome_regions/down.sql +24 -0
- package/prisma/migrations/0027_chrome_regions/migration.sql +16 -0
- package/prisma/schema.prisma +105 -15
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chrome operations behind `/chrome/*` and the cron routes. Each write is a
|
|
3
|
+
* conditional update on the row's version; a writer that loses the race reads
|
|
4
|
+
* again and retries, unless the caller sent `If-Match`, which then fails with 412.
|
|
5
|
+
*/
|
|
6
|
+
import { createHash } from 'node:crypto';
|
|
7
|
+
import { boundaryStateForSave, checkAnnouncementSave, dueBoundaries, readAnnouncement, readBoundaryState, } from './announcement.js';
|
|
8
|
+
import { CHROME_REGIONS } from './schema.js';
|
|
9
|
+
import { ANNOUNCEMENT_ROW } from './store.js';
|
|
10
|
+
import { checkRegionSave, effectiveVariant, fromStoredSchema, isRecord, liveOrphans, planSchemaRead, schemaHash, toStoredSchema, } from './values.js';
|
|
11
|
+
const MAX_ATTEMPTS = 5;
|
|
12
|
+
/** JSON with object keys sorted, so a hash doesn't depend on how JSONB ordered them. */
|
|
13
|
+
function canonicalJson(value) {
|
|
14
|
+
return JSON.stringify(value, (_key, item) => {
|
|
15
|
+
if (!isRecord(item))
|
|
16
|
+
return item;
|
|
17
|
+
const sorted = {};
|
|
18
|
+
for (const key of Object.keys(item).sort())
|
|
19
|
+
sorted[key] = item[key];
|
|
20
|
+
return sorted;
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
function shortHash(value) {
|
|
24
|
+
return createHash('sha256').update(canonicalJson(value)).digest('hex').slice(0, 20);
|
|
25
|
+
}
|
|
26
|
+
function sameJson(a, b) {
|
|
27
|
+
return canonicalJson(a) === canonicalJson(b);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* ETag for `GET /chrome/:region`, over what the editor sees: the schema, the
|
|
31
|
+
* values and the live orphans. A cron purge of expired orphans doesn't change it.
|
|
32
|
+
*/
|
|
33
|
+
export function regionEtag(row, now) {
|
|
34
|
+
return `W/"chrome-${row.region}-${shortHash([
|
|
35
|
+
row.schemaHash,
|
|
36
|
+
row.variant,
|
|
37
|
+
row.values,
|
|
38
|
+
liveOrphans(row.orphans, now),
|
|
39
|
+
])}"`;
|
|
40
|
+
}
|
|
41
|
+
/** ETag for `GET /chrome/announcement`, over its content only, so a schedule tick doesn't change it. */
|
|
42
|
+
export function announcementEtag(announcement) {
|
|
43
|
+
return `W/"chrome-announcement-${shortHash(announcement)}"`;
|
|
44
|
+
}
|
|
45
|
+
/** `If-Match` as sent, or null for no precondition. A `*` always matches. */
|
|
46
|
+
export function readIfMatch(request) {
|
|
47
|
+
const value = request.headers.get('if-match')?.trim();
|
|
48
|
+
return value && value !== '*' ? value : null;
|
|
49
|
+
}
|
|
50
|
+
const RETRY_EXHAUSTED = {
|
|
51
|
+
ok: false,
|
|
52
|
+
status: 409,
|
|
53
|
+
error: 'Someone else is changing this right now. Try again.',
|
|
54
|
+
};
|
|
55
|
+
const STALE = {
|
|
56
|
+
ok: false,
|
|
57
|
+
status: 412,
|
|
58
|
+
error: 'This changed since you opened it. Reload to see the latest, then save again.',
|
|
59
|
+
};
|
|
60
|
+
/** Run `attempt` until its conditional write lands. `null` from an attempt means "lost the race". */
|
|
61
|
+
async function retrying(attempt) {
|
|
62
|
+
for (let i = 0; i < MAX_ATTEMPTS; i++) {
|
|
63
|
+
const result = await attempt();
|
|
64
|
+
if (result !== null)
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
return RETRY_EXHAUSTED;
|
|
68
|
+
}
|
|
69
|
+
async function rowFor(store, region, now) {
|
|
70
|
+
const row = await store.get(region);
|
|
71
|
+
if (row)
|
|
72
|
+
return row;
|
|
73
|
+
await store.ensure(region, now);
|
|
74
|
+
const created = await store.get(region);
|
|
75
|
+
if (!created)
|
|
76
|
+
throw new Error(`Chrome row "${region}" could not be created`);
|
|
77
|
+
return created;
|
|
78
|
+
}
|
|
79
|
+
export async function getChromeSchema(store) {
|
|
80
|
+
const rows = new Map((await store.list()).map((row) => [row.region, row]));
|
|
81
|
+
const regions = [];
|
|
82
|
+
for (const region of CHROME_REGIONS) {
|
|
83
|
+
const schema = fromStoredSchema(region, rows.get(region)?.schema);
|
|
84
|
+
if (schema)
|
|
85
|
+
regions.push(schema);
|
|
86
|
+
}
|
|
87
|
+
const latest = regions.reduce((best, schema) => (!best || schema.source.readAt > best.readAt ? schema.source : best), null);
|
|
88
|
+
return {
|
|
89
|
+
regions,
|
|
90
|
+
sync: {
|
|
91
|
+
status: regions.length > 0 ? 'synced' : 'never',
|
|
92
|
+
source: latest,
|
|
93
|
+
regions: regions.map((schema) => schema.region),
|
|
94
|
+
fieldCount: regions.reduce((sum, schema) => sum + Object.keys(schema.fields).length, 0),
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Store a pushed schema and fit each region's values to it. A region the push
|
|
100
|
+
* leaves out loses its schema (the site falls back to layout variants) and its
|
|
101
|
+
* values become orphans, which come back if the region does within 30 days.
|
|
102
|
+
*/
|
|
103
|
+
export async function readChromeSchema(store, input, options) {
|
|
104
|
+
const { now, userId } = options;
|
|
105
|
+
const source = { ...input.source, readAt: now.toISOString() };
|
|
106
|
+
const pushed = new Map(input.regions.map((region) => [region.region, region]));
|
|
107
|
+
const result = { regions: [], source };
|
|
108
|
+
for (const region of CHROME_REGIONS) {
|
|
109
|
+
const next = pushed.get(region) ?? null;
|
|
110
|
+
const outcome = await retrying(async () => {
|
|
111
|
+
const row = next ? await rowFor(store, region, now) : await store.get(region);
|
|
112
|
+
const unchanged = { region, orphaned: [], renamed: [], restored: [], changed: false };
|
|
113
|
+
if (!row)
|
|
114
|
+
return unchanged;
|
|
115
|
+
if (!next && row.schema === null && Object.keys(row.values).length === 0)
|
|
116
|
+
return unchanged;
|
|
117
|
+
const plan = planSchemaRead({ variant: row.variant, values: row.values, orphans: row.orphans }, next, now);
|
|
118
|
+
const hash = next ? schemaHash(next) : null;
|
|
119
|
+
// What an editor saved: only a change to it moves updatedAt and updatedBy.
|
|
120
|
+
const savedChanged = !sameJson(row.values, plan.state.values) || row.variant !== plan.state.variant;
|
|
121
|
+
const orphansChanged = !sameJson(row.orphans, plan.state.orphans);
|
|
122
|
+
const stored = next ? fromStoredSchema(region, row.schema) : null;
|
|
123
|
+
const sameSource = stored !== null &&
|
|
124
|
+
stored.source.repo === source.repo &&
|
|
125
|
+
stored.source.branch === source.branch &&
|
|
126
|
+
stored.source.commit === source.commit;
|
|
127
|
+
// A redeploy of the same commit with nothing to fit writes nothing.
|
|
128
|
+
if (hash === row.schemaHash && !savedChanged && !orphansChanged && (sameSource || !next)) {
|
|
129
|
+
return unchanged;
|
|
130
|
+
}
|
|
131
|
+
const written = await store.update(row, {
|
|
132
|
+
schema: next ? toStoredSchema(next, source) : null,
|
|
133
|
+
schemaHash: hash,
|
|
134
|
+
variant: plan.state.variant,
|
|
135
|
+
values: plan.state.values,
|
|
136
|
+
orphans: plan.state.orphans,
|
|
137
|
+
updatedById: savedChanged ? userId : row.updatedById,
|
|
138
|
+
}, now, { touch: savedChanged });
|
|
139
|
+
if (!written)
|
|
140
|
+
return null;
|
|
141
|
+
return {
|
|
142
|
+
region,
|
|
143
|
+
orphaned: plan.orphaned,
|
|
144
|
+
renamed: plan.renamed,
|
|
145
|
+
restored: plan.restored,
|
|
146
|
+
changed: hash !== row.schemaHash || savedChanged,
|
|
147
|
+
};
|
|
148
|
+
});
|
|
149
|
+
if ('ok' in outcome)
|
|
150
|
+
return outcome;
|
|
151
|
+
result.regions.push(outcome);
|
|
152
|
+
}
|
|
153
|
+
return result;
|
|
154
|
+
}
|
|
155
|
+
function regionView(row, region, now) {
|
|
156
|
+
const schema = fromStoredSchema(region, row.schema);
|
|
157
|
+
if (!schema)
|
|
158
|
+
return null;
|
|
159
|
+
return {
|
|
160
|
+
row,
|
|
161
|
+
schema,
|
|
162
|
+
variant: effectiveVariant(schema, row.variant),
|
|
163
|
+
orphans: liveOrphans(row.orphans, now),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
const NO_SCHEMA = (region) => ({
|
|
167
|
+
ok: false,
|
|
168
|
+
status: 404,
|
|
169
|
+
error: `The site hasn't defined the ${region} yet. Add defineRegion('${region}', …) and run prenta push chrome.`,
|
|
170
|
+
});
|
|
171
|
+
export async function getRegion(store, region, now) {
|
|
172
|
+
const row = await store.get(region);
|
|
173
|
+
return (row && regionView(row, region, now)) ?? NO_SCHEMA(region);
|
|
174
|
+
}
|
|
175
|
+
/** `PUT /chrome/:region`: replace the variant and field values. Orphans are kept. */
|
|
176
|
+
export async function saveRegion(store, region, body, options) {
|
|
177
|
+
const { now, ifMatch } = options;
|
|
178
|
+
const outcome = await retrying(async () => {
|
|
179
|
+
const row = await store.get(region);
|
|
180
|
+
const view = row && regionView(row, region, now);
|
|
181
|
+
if (!row || !view)
|
|
182
|
+
return NO_SCHEMA(region);
|
|
183
|
+
if (ifMatch !== null && ifMatch !== regionEtag(row, now))
|
|
184
|
+
return STALE;
|
|
185
|
+
const input = isRecord(body) && body.variant === undefined ? { ...body, variant: view.variant } : body;
|
|
186
|
+
const check = checkRegionSave(view.schema, input, new Set(view.orphans.map((o) => o.key)));
|
|
187
|
+
if (!check.ok)
|
|
188
|
+
return { ok: false, status: 400, error: check.error };
|
|
189
|
+
const written = await store.update(row, {
|
|
190
|
+
variant: check.value.variant,
|
|
191
|
+
values: check.value.values,
|
|
192
|
+
orphans: view.orphans,
|
|
193
|
+
updatedById: options.userId,
|
|
194
|
+
}, now);
|
|
195
|
+
// Lost the race: read again. With If-Match, the next attempt checks it
|
|
196
|
+
// against the newer row, so only a change the editor would see fails.
|
|
197
|
+
return written ? regionView(written, region, now) : null;
|
|
198
|
+
});
|
|
199
|
+
return outcome;
|
|
200
|
+
}
|
|
201
|
+
/** `DELETE /chrome/:region/orphans/:key`, the orphan notice's "Remove now". */
|
|
202
|
+
export async function removeOrphan(store, region, key, options) {
|
|
203
|
+
const { now } = options;
|
|
204
|
+
return retrying(async () => {
|
|
205
|
+
const row = await store.get(region);
|
|
206
|
+
const view = row && regionView(row, region, now);
|
|
207
|
+
if (!row || !view)
|
|
208
|
+
return NO_SCHEMA(region);
|
|
209
|
+
if (!view.orphans.some((orphan) => orphan.key === key)) {
|
|
210
|
+
return { ok: false, status: 404, error: `${region} has no orphaned value "${key}"` };
|
|
211
|
+
}
|
|
212
|
+
const written = await store.update(row, {
|
|
213
|
+
orphans: view.orphans.filter((orphan) => orphan.key !== key),
|
|
214
|
+
updatedById: options.userId,
|
|
215
|
+
}, now);
|
|
216
|
+
return written ? regionView(written, region, now) : null;
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
/** Cleanup cron: drop orphans older than 30 days. A row that changes meanwhile waits for the next run. */
|
|
220
|
+
export async function purgeExpiredOrphans(store, now) {
|
|
221
|
+
let purged = 0;
|
|
222
|
+
for (const row of await store.list()) {
|
|
223
|
+
const live = liveOrphans(row.orphans, now);
|
|
224
|
+
if (live.length === row.orphans.length)
|
|
225
|
+
continue;
|
|
226
|
+
if (await store.update(row, { orphans: live }, now, { touch: false })) {
|
|
227
|
+
purged += row.orphans.length - live.length;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return purged;
|
|
231
|
+
}
|
|
232
|
+
export async function getAnnouncement(store) {
|
|
233
|
+
const row = await store.get(ANNOUNCEMENT_ROW);
|
|
234
|
+
return { announcement: readAnnouncement(row?.values), row };
|
|
235
|
+
}
|
|
236
|
+
/** `PUT /chrome/announcement`. A boundary already past counts as fired, since the save revalidates. */
|
|
237
|
+
export async function saveAnnouncement(store, body, options) {
|
|
238
|
+
const { now, ifMatch } = options;
|
|
239
|
+
return retrying(async () => {
|
|
240
|
+
const row = await rowFor(store, ANNOUNCEMENT_ROW, now);
|
|
241
|
+
const previous = readAnnouncement(row.values);
|
|
242
|
+
if (ifMatch !== null && ifMatch !== announcementEtag(previous))
|
|
243
|
+
return STALE;
|
|
244
|
+
const check = checkAnnouncementSave(body, previous);
|
|
245
|
+
if (!check.ok)
|
|
246
|
+
return { ok: false, status: 400, error: check.error };
|
|
247
|
+
const written = await store.update(row, {
|
|
248
|
+
values: check.value,
|
|
249
|
+
state: boundaryStateForSave(check.value, now),
|
|
250
|
+
updatedById: options.userId,
|
|
251
|
+
}, now);
|
|
252
|
+
return written ? { announcement: check.value, row: written } : null;
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Publish cron: revalidate the site when a scheduled announcement starts or
|
|
257
|
+
* ends. The webhook goes first and the boundary is marked after, so a crash
|
|
258
|
+
* in between fires it again on the next tick rather than never. Overlapping
|
|
259
|
+
* ticks can both fire; a second `chrome.updated` is harmless.
|
|
260
|
+
*/
|
|
261
|
+
export async function processAnnouncementSchedule(store, options) {
|
|
262
|
+
const { now } = options;
|
|
263
|
+
const fired = new Set();
|
|
264
|
+
for (let i = 0; i < MAX_ATTEMPTS; i++) {
|
|
265
|
+
const row = await store.get(ANNOUNCEMENT_ROW);
|
|
266
|
+
if (!row)
|
|
267
|
+
break;
|
|
268
|
+
const announcement = readAnnouncement(row.values);
|
|
269
|
+
const state = readBoundaryState(row.state);
|
|
270
|
+
const due = dueBoundaries(announcement, state, now);
|
|
271
|
+
if (due.length === 0)
|
|
272
|
+
break;
|
|
273
|
+
await options.revalidate(ANNOUNCEMENT_ROW);
|
|
274
|
+
for (const boundary of due)
|
|
275
|
+
fired.add(boundary);
|
|
276
|
+
const next = { ...state };
|
|
277
|
+
if (due.includes('start'))
|
|
278
|
+
next.start = announcement.schedule?.startsAt ?? null;
|
|
279
|
+
if (due.includes('end'))
|
|
280
|
+
next.end = announcement.schedule?.endsAt ?? null;
|
|
281
|
+
if (await store.update(row, { state: next }, now, { touch: false }))
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
return { fired: [...fired] };
|
|
285
|
+
}
|
|
286
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/chrome/service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { PrentaDB } from '../db/client-types.js';
|
|
2
|
+
import type { ChromeSettings } from './contract.js';
|
|
3
|
+
import type { ChromeFailure } from './service.js';
|
|
4
|
+
/** Only admins see the revalidate URL: like any webhook URL it may carry a secret. */
|
|
5
|
+
export declare function getChromeSettings(db: PrentaDB, options: {
|
|
6
|
+
admin: boolean;
|
|
7
|
+
}): Promise<ChromeSettings>;
|
|
8
|
+
export interface SavedChromeSettings {
|
|
9
|
+
settings: ChromeSettings;
|
|
10
|
+
/** The new endpoint's signing secret. Sent once, when the endpoint is created. */
|
|
11
|
+
secret?: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function saveChromeSettings(db: PrentaDB, body: unknown): Promise<SavedChromeSettings | ChromeFailure>;
|
|
14
|
+
//# sourceMappingURL=settings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/chrome/settings.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAGrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AA6BjD,sFAAsF;AACtF,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,QAAQ,EACZ,OAAO,EAAE;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAC1B,OAAO,CAAC,cAAc,CAAC,CAOzB;AAuBD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,cAAc,CAAA;IACxB,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,wBAAsB,kBAAkB,CACtC,EAAE,EAAE,QAAQ,EACZ,IAAI,EAAE,OAAO,GACZ,OAAO,CAAC,mBAAmB,GAAG,aAAa,CAAC,CAgF9C"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `GET/PUT /chrome/settings`: the Developer tab's Revalidate URL and Preview URL.
|
|
3
|
+
*
|
|
4
|
+
* The revalidate URL is a signed webhook endpoint subscribed to
|
|
5
|
+
* `chrome.updated`, so deliveries are signed, retried and logged like every
|
|
6
|
+
* other webhook. These settings keep that endpoint's id and the preview URL in
|
|
7
|
+
* `prenta_system_settings` (`chrome.settings`). A save is conditional on the
|
|
8
|
+
* row's `updatedAt`; endpoints are only changed after it lands, so two admins
|
|
9
|
+
* saving at once can't leave a second endpoint behind.
|
|
10
|
+
*/
|
|
11
|
+
import { randomBytes } from 'node:crypto';
|
|
12
|
+
import { validateWebhookUrl } from '../security/webhook.js';
|
|
13
|
+
import { isRecord } from './values.js';
|
|
14
|
+
const SETTINGS_KEY = 'chrome.settings';
|
|
15
|
+
const ENDPOINT_NAME = 'Header & Footer revalidate';
|
|
16
|
+
const MAX_URL = 2048;
|
|
17
|
+
function readStored(value) {
|
|
18
|
+
const raw = isRecord(value) ? value : {};
|
|
19
|
+
return {
|
|
20
|
+
previewUrl: typeof raw.previewUrl === 'string' && raw.previewUrl ? raw.previewUrl : null,
|
|
21
|
+
revalidateEndpointId: typeof raw.revalidateEndpointId === 'string' && raw.revalidateEndpointId
|
|
22
|
+
? raw.revalidateEndpointId
|
|
23
|
+
: null,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
async function endpointUrl(db, id) {
|
|
27
|
+
if (!id)
|
|
28
|
+
return null;
|
|
29
|
+
const endpoint = await db.webhookEndpoint.findUnique({ where: { id } });
|
|
30
|
+
return endpoint ? endpoint.url : null;
|
|
31
|
+
}
|
|
32
|
+
/** Only admins see the revalidate URL: like any webhook URL it may carry a secret. */
|
|
33
|
+
export async function getChromeSettings(db, options) {
|
|
34
|
+
const row = await db.systemSetting.findUnique({ where: { key: SETTINGS_KEY } });
|
|
35
|
+
const stored = readStored(row?.value);
|
|
36
|
+
return {
|
|
37
|
+
revalidateUrl: options.admin ? await endpointUrl(db, stored.revalidateEndpointId) : null,
|
|
38
|
+
previewUrl: stored.previewUrl,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function readUrl(body, key) {
|
|
42
|
+
if (!(key in body))
|
|
43
|
+
return { set: false };
|
|
44
|
+
const value = body[key];
|
|
45
|
+
if (value === null || value === '')
|
|
46
|
+
return { set: true, value: null };
|
|
47
|
+
if (typeof value !== 'string')
|
|
48
|
+
return `${key} must be a URL or null`;
|
|
49
|
+
const url = value.trim();
|
|
50
|
+
if (url.length > MAX_URL)
|
|
51
|
+
return `${key} is longer than ${MAX_URL} characters`;
|
|
52
|
+
if (key === 'revalidateUrl') {
|
|
53
|
+
const check = validateWebhookUrl(url);
|
|
54
|
+
if (!check.valid)
|
|
55
|
+
return `revalidateUrl: ${check.error ?? 'Invalid URL'}`;
|
|
56
|
+
}
|
|
57
|
+
else if (!/^https?:\/\/[^\s]+$/i.test(url)) {
|
|
58
|
+
return 'previewUrl must be an http(s) URL';
|
|
59
|
+
}
|
|
60
|
+
return { set: true, value: url };
|
|
61
|
+
}
|
|
62
|
+
export async function saveChromeSettings(db, body) {
|
|
63
|
+
if (!isRecord(body))
|
|
64
|
+
return { ok: false, status: 400, error: 'Send { revalidateUrl, previewUrl }' };
|
|
65
|
+
const revalidate = readUrl(body, 'revalidateUrl');
|
|
66
|
+
if (typeof revalidate === 'string')
|
|
67
|
+
return { ok: false, status: 400, error: revalidate };
|
|
68
|
+
const preview = readUrl(body, 'previewUrl');
|
|
69
|
+
if (typeof preview === 'string')
|
|
70
|
+
return { ok: false, status: 400, error: preview };
|
|
71
|
+
const webhooks = await import('../webhooks/index.js');
|
|
72
|
+
const row = await db.systemSetting.findUnique({ where: { key: SETTINGS_KEY } });
|
|
73
|
+
const stored = readStored(row?.value);
|
|
74
|
+
const existing = stored.revalidateEndpointId
|
|
75
|
+
? await db.webhookEndpoint.findUnique({ where: { id: stored.revalidateEndpointId } })
|
|
76
|
+
: null;
|
|
77
|
+
// A new endpoint is made before the write, so its id can be stored; it's
|
|
78
|
+
// removed again if the write loses. Changing or removing the current one
|
|
79
|
+
// waits until the write has landed.
|
|
80
|
+
let created = null;
|
|
81
|
+
if (revalidate.set && revalidate.value && !existing) {
|
|
82
|
+
const secret = randomBytes(32).toString('hex');
|
|
83
|
+
const { endpoint } = await webhooks.createEndpoint({
|
|
84
|
+
url: revalidate.value,
|
|
85
|
+
events: ['chrome.updated'],
|
|
86
|
+
secret,
|
|
87
|
+
name: ENDPOINT_NAME,
|
|
88
|
+
active: true,
|
|
89
|
+
});
|
|
90
|
+
created = { id: endpoint.id, secret };
|
|
91
|
+
}
|
|
92
|
+
const next = {
|
|
93
|
+
previewUrl: preview.set ? preview.value : stored.previewUrl,
|
|
94
|
+
revalidateEndpointId: !revalidate.set
|
|
95
|
+
? existing
|
|
96
|
+
? existing.id
|
|
97
|
+
: null
|
|
98
|
+
: revalidate.value
|
|
99
|
+
? (created?.id ?? existing?.id ?? null)
|
|
100
|
+
: null,
|
|
101
|
+
};
|
|
102
|
+
const value = next;
|
|
103
|
+
const { count } = row
|
|
104
|
+
? await db.systemSetting.updateMany({
|
|
105
|
+
where: { key: SETTINGS_KEY, updatedAt: row.updatedAt },
|
|
106
|
+
data: { value },
|
|
107
|
+
})
|
|
108
|
+
: await db.systemSetting.createMany({
|
|
109
|
+
data: [{ key: SETTINGS_KEY, value }],
|
|
110
|
+
skipDuplicates: true,
|
|
111
|
+
});
|
|
112
|
+
if (count !== 1) {
|
|
113
|
+
if (created)
|
|
114
|
+
await webhooks.deleteEndpoint(created.id).catch(() => undefined);
|
|
115
|
+
return {
|
|
116
|
+
ok: false,
|
|
117
|
+
status: 409,
|
|
118
|
+
error: 'Someone else saved these settings just now. Reload, then save again.',
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
if (revalidate.set && existing) {
|
|
122
|
+
if (revalidate.value) {
|
|
123
|
+
const events = Array.isArray(existing.events) ? existing.events : [];
|
|
124
|
+
await webhooks.updateEndpoint(existing.id, {
|
|
125
|
+
url: revalidate.value,
|
|
126
|
+
events: [...new Set([...events, 'chrome.updated'])],
|
|
127
|
+
active: true,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
await webhooks.deleteEndpoint(existing.id);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
settings: {
|
|
136
|
+
revalidateUrl: revalidate.set ? revalidate.value : (existing?.url ?? null),
|
|
137
|
+
previewUrl: next.previewUrl,
|
|
138
|
+
},
|
|
139
|
+
...(created ? { secret: created.secret } : {}),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=settings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/chrome/settings.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storage for `prenta_chrome_regions`. Every write names the version it read,
|
|
3
|
+
* so two writers (an editor's save, a schema push, a cron tick) can't
|
|
4
|
+
* overwrite each other: the loser re-reads and tries again, or reports a conflict.
|
|
5
|
+
*/
|
|
6
|
+
import type { ChromeOrphan } from './contract.js';
|
|
7
|
+
/** The row name of the announcement bar. The site's regions use their own names. */
|
|
8
|
+
export declare const ANNOUNCEMENT_ROW = "announcement";
|
|
9
|
+
export interface ChromeRow {
|
|
10
|
+
region: string;
|
|
11
|
+
schema: unknown;
|
|
12
|
+
schemaHash: string | null;
|
|
13
|
+
variant: string | null;
|
|
14
|
+
values: Record<string, unknown>;
|
|
15
|
+
orphans: ChromeOrphan[];
|
|
16
|
+
state: unknown;
|
|
17
|
+
version: number;
|
|
18
|
+
updatedById: string | null;
|
|
19
|
+
updatedAt: Date | null;
|
|
20
|
+
}
|
|
21
|
+
export type ChromeRowPatch = Partial<Pick<ChromeRow, 'schema' | 'schemaHash' | 'variant' | 'values' | 'orphans' | 'state' | 'updatedById'>>;
|
|
22
|
+
/**
|
|
23
|
+
* `touch: false` keeps `updatedAt`, for a write that doesn't change what an
|
|
24
|
+
* editor saved: a redeploy of the same schema, an orphan purge, a schedule
|
|
25
|
+
* tick. `updatedAt` is then when the values last changed, as `updatedBy` is
|
|
26
|
+
* who changed them.
|
|
27
|
+
*/
|
|
28
|
+
export interface ChromeWriteOptions {
|
|
29
|
+
touch?: boolean;
|
|
30
|
+
}
|
|
31
|
+
export interface ChromeStore {
|
|
32
|
+
get(region: string): Promise<ChromeRow | null>;
|
|
33
|
+
list(): Promise<ChromeRow[]>;
|
|
34
|
+
/** Insert an empty row unless one exists. Safe to race. */
|
|
35
|
+
ensure(region: string, now: Date): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Apply `patch` if the row is still at `row.version`. Returns the written
|
|
38
|
+
* row, or null when someone else wrote first.
|
|
39
|
+
*/
|
|
40
|
+
update(row: ChromeRow, patch: ChromeRowPatch, now: Date, options?: ChromeWriteOptions): Promise<ChromeRow | null>;
|
|
41
|
+
}
|
|
42
|
+
/** An empty row, as `ensure` creates it. */
|
|
43
|
+
export declare function emptyRow(region: string): ChromeRow;
|
|
44
|
+
/** When a write leaves `updatedAt`. */
|
|
45
|
+
export declare function writtenAt(row: ChromeRow, now: Date, options?: ChromeWriteOptions): Date;
|
|
46
|
+
/** The row `update` wrote. */
|
|
47
|
+
export declare function patchedRow(row: ChromeRow, patch: ChromeRowPatch, now: Date, options?: ChromeWriteOptions): ChromeRow;
|
|
48
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/chrome/store.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAEjD,oFAAoF;AACpF,eAAO,MAAM,gBAAgB,iBAAiB,CAAA;AAE9C,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,OAAO,CAAA;IACf,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/B,OAAO,EAAE,YAAY,EAAE,CAAA;IACvB,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,SAAS,EAAE,IAAI,GAAG,IAAI,CAAA;CACvB;AAED,MAAM,MAAM,cAAc,GAAG,OAAO,CAClC,IAAI,CACF,SAAS,EACT,QAAQ,GAAG,YAAY,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,aAAa,CACrF,CACF,CAAA;AAED;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAA;IAC9C,IAAI,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC,CAAA;IAC5B,2DAA2D;IAC3D,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAChD;;;OAGG;IACH,MAAM,CACJ,GAAG,EAAE,SAAS,EACd,KAAK,EAAE,cAAc,EACrB,GAAG,EAAE,IAAI,EACT,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAA;CAC7B;AAED,4CAA4C;AAC5C,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAalD;AAED,uCAAuC;AACvC,wBAAgB,SAAS,CAAC,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAEvF;AAED,8BAA8B;AAC9B,wBAAgB,UAAU,CACxB,GAAG,EAAE,SAAS,EACd,KAAK,EAAE,cAAc,EACrB,GAAG,EAAE,IAAI,EACT,OAAO,CAAC,EAAE,kBAAkB,GAC3B,SAAS,CAEX"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** The row name of the announcement bar. The site's regions use their own names. */
|
|
2
|
+
export const ANNOUNCEMENT_ROW = 'announcement';
|
|
3
|
+
/** An empty row, as `ensure` creates it. */
|
|
4
|
+
export function emptyRow(region) {
|
|
5
|
+
return {
|
|
6
|
+
region,
|
|
7
|
+
schema: null,
|
|
8
|
+
schemaHash: null,
|
|
9
|
+
variant: null,
|
|
10
|
+
values: {},
|
|
11
|
+
orphans: [],
|
|
12
|
+
state: null,
|
|
13
|
+
version: 0,
|
|
14
|
+
updatedById: null,
|
|
15
|
+
updatedAt: null,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/** When a write leaves `updatedAt`. */
|
|
19
|
+
export function writtenAt(row, now, options) {
|
|
20
|
+
return options?.touch === false ? (row.updatedAt ?? now) : now;
|
|
21
|
+
}
|
|
22
|
+
/** The row `update` wrote. */
|
|
23
|
+
export function patchedRow(row, patch, now, options) {
|
|
24
|
+
return { ...row, ...patch, version: row.version + 1, updatedAt: writtenAt(row, now, options) };
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/chrome/store.ts"],"names":[],"mappings":"AAOA,oFAAoF;AACpF,MAAM,CAAC,MAAM,gBAAgB,GAAG,cAAc,CAAA;AAiD9C,4CAA4C;AAC5C,MAAM,UAAU,QAAQ,CAAC,MAAc;IACrC,OAAO;QACL,MAAM;QACN,MAAM,EAAE,IAAI;QACZ,UAAU,EAAE,IAAI;QAChB,OAAO,EAAE,IAAI;QACb,MAAM,EAAE,EAAE;QACV,OAAO,EAAE,EAAE;QACX,KAAK,EAAE,IAAI;QACX,OAAO,EAAE,CAAC;QACV,WAAW,EAAE,IAAI;QACjB,SAAS,EAAE,IAAI;KAChB,CAAA;AACH,CAAC;AAED,uCAAuC;AACvC,MAAM,UAAU,SAAS,CAAC,GAAc,EAAE,GAAS,EAAE,OAA4B;IAC/E,OAAO,OAAO,EAAE,KAAK,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAA;AAChE,CAAC;AAED,8BAA8B;AAC9B,MAAM,UAAU,UAAU,CACxB,GAAc,EACd,KAAqB,EACrB,GAAS,EACT,OAA4B;IAE5B,OAAO,EAAE,GAAG,GAAG,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,GAAG,CAAC,EAAE,SAAS,EAAE,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,CAAA;AAChG,CAAC"}
|