@commonpub/server 2.88.0 → 2.90.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/dist/admin/admin.d.ts +9 -0
- package/dist/admin/admin.d.ts.map +1 -1
- package/dist/admin/admin.js +50 -13
- package/dist/admin/admin.js.map +1 -1
- package/dist/content/content.d.ts +0 -5
- package/dist/content/content.d.ts.map +1 -1
- package/dist/content/content.js +118 -122
- package/dist/content/content.js.map +1 -1
- package/dist/content/index.d.ts +1 -1
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/index.js +1 -1
- package/dist/content/index.js.map +1 -1
- package/dist/contest/contest.d.ts +7 -300
- package/dist/contest/contest.d.ts.map +1 -1
- package/dist/contest/contest.js +99 -882
- package/dist/contest/contest.js.map +1 -1
- package/dist/contest/entries.d.ts +41 -0
- package/dist/contest/entries.d.ts.map +1 -0
- package/dist/contest/entries.js +285 -0
- package/dist/contest/entries.js.map +1 -0
- package/dist/contest/export.d.ts +20 -0
- package/dist/contest/export.d.ts.map +1 -0
- package/dist/contest/export.js +131 -0
- package/dist/contest/export.js.map +1 -0
- package/dist/contest/index.d.ts +12 -3
- package/dist/contest/index.d.ts.map +1 -1
- package/dist/contest/index.js +9 -2
- package/dist/contest/index.js.map +1 -1
- package/dist/contest/judges.js +9 -8
- package/dist/contest/judges.js.map +1 -1
- package/dist/contest/judging.d.ts +38 -0
- package/dist/contest/judging.d.ts.map +1 -0
- package/dist/contest/judging.js +274 -0
- package/dist/contest/judging.js.map +1 -0
- package/dist/contest/read.d.ts +44 -0
- package/dist/contest/read.d.ts.map +1 -0
- package/dist/contest/read.js +164 -0
- package/dist/contest/read.js.map +1 -0
- package/dist/contest/stages.d.ts +28 -0
- package/dist/contest/stages.d.ts.map +1 -0
- package/dist/contest/stages.js +52 -0
- package/dist/contest/stages.js.map +1 -0
- package/dist/contest/stakeholders.d.ts +32 -7
- package/dist/contest/stakeholders.d.ts.map +1 -1
- package/dist/contest/stakeholders.js +55 -14
- package/dist/contest/stakeholders.js.map +1 -1
- package/dist/contest/submissions.d.ts +90 -0
- package/dist/contest/submissions.d.ts.map +1 -0
- package/dist/contest/submissions.js +275 -0
- package/dist/contest/submissions.js.map +1 -0
- package/dist/contest/types.d.ts +197 -0
- package/dist/contest/types.d.ts.map +1 -0
- package/dist/contest/types.js +2 -0
- package/dist/contest/types.js.map +1 -0
- package/dist/contest/validation.d.ts +32 -0
- package/dist/contest/validation.d.ts.map +1 -0
- package/dist/contest/validation.js +132 -0
- package/dist/contest/validation.js.map +1 -0
- package/dist/docs/docs.d.ts +9 -3
- package/dist/docs/docs.d.ts.map +1 -1
- package/dist/docs/docs.js +16 -6
- package/dist/docs/docs.js.map +1 -1
- package/dist/events/events.d.ts.map +1 -1
- package/dist/events/events.js +12 -6
- package/dist/events/events.js.map +1 -1
- package/dist/federation/activityDedup.d.ts +14 -0
- package/dist/federation/activityDedup.d.ts.map +1 -0
- package/dist/federation/activityDedup.js +34 -0
- package/dist/federation/activityDedup.js.map +1 -0
- package/dist/federation/assertPublicHost.d.ts +14 -0
- package/dist/federation/assertPublicHost.d.ts.map +1 -0
- package/dist/federation/assertPublicHost.js +62 -0
- package/dist/federation/assertPublicHost.js.map +1 -0
- package/dist/federation/delivery.d.ts.map +1 -1
- package/dist/federation/delivery.js +37 -51
- package/dist/federation/delivery.js.map +1 -1
- package/dist/federation/federation.d.ts.map +1 -1
- package/dist/federation/federation.js +11 -7
- package/dist/federation/federation.js.map +1 -1
- package/dist/federation/hubMirroring.d.ts.map +1 -1
- package/dist/federation/hubMirroring.js +85 -66
- package/dist/federation/hubMirroring.js.map +1 -1
- package/dist/federation/inboxHandlers.d.ts.map +1 -1
- package/dist/federation/inboxHandlers.js +84 -73
- package/dist/federation/inboxHandlers.js.map +1 -1
- package/dist/federation/inboxParsing.d.ts +28 -0
- package/dist/federation/inboxParsing.d.ts.map +1 -0
- package/dist/federation/inboxParsing.js +71 -0
- package/dist/federation/inboxParsing.js.map +1 -0
- package/dist/federation/index.d.ts +2 -0
- package/dist/federation/index.d.ts.map +1 -1
- package/dist/federation/index.js +2 -0
- package/dist/federation/index.js.map +1 -1
- package/dist/federation/mastodonLogin.d.ts.map +1 -1
- package/dist/federation/mastodonLogin.js +19 -0
- package/dist/federation/mastodonLogin.js.map +1 -1
- package/dist/federation/outboxQueries.js +1 -1
- package/dist/federation/outboxQueries.js.map +1 -1
- package/dist/federation/timeline.d.ts +11 -0
- package/dist/federation/timeline.d.ts.map +1 -1
- package/dist/federation/timeline.js +101 -69
- package/dist/federation/timeline.js.map +1 -1
- package/dist/hub/hub.d.ts.map +1 -1
- package/dist/hub/hub.js +41 -3
- package/dist/hub/hub.js.map +1 -1
- package/dist/hub/index.d.ts +1 -1
- package/dist/hub/index.d.ts.map +1 -1
- package/dist/hub/index.js +1 -1
- package/dist/hub/index.js.map +1 -1
- package/dist/hub/members.d.ts +19 -0
- package/dist/hub/members.d.ts.map +1 -1
- package/dist/hub/members.js +158 -13
- package/dist/hub/members.js.map +1 -1
- package/dist/hub/moderation.d.ts +1 -1
- package/dist/hub/moderation.d.ts.map +1 -1
- package/dist/hub/moderation.js +25 -11
- package/dist/hub/moderation.js.map +1 -1
- package/dist/hub/posts.d.ts.map +1 -1
- package/dist/hub/posts.js +18 -10
- package/dist/hub/posts.js.map +1 -1
- package/dist/hub/resources.js +2 -2
- package/dist/hub/resources.js.map +1 -1
- package/dist/identity/mastodonFactory.d.ts.map +1 -1
- package/dist/identity/mastodonFactory.js +7 -0
- package/dist/identity/mastodonFactory.js.map +1 -1
- package/dist/index.d.ts +8 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -6
- package/dist/index.js.map +1 -1
- package/dist/learning/learning.d.ts.map +1 -1
- package/dist/learning/learning.js +42 -22
- package/dist/learning/learning.js.map +1 -1
- package/dist/messaging/messaging.d.ts.map +1 -1
- package/dist/messaging/messaging.js +3 -3
- package/dist/messaging/messaging.js.map +1 -1
- package/dist/notification/notification.d.ts.map +1 -1
- package/dist/notification/notification.js +4 -2
- package/dist/notification/notification.js.map +1 -1
- package/dist/product/product.d.ts.map +1 -1
- package/dist/product/product.js +75 -37
- package/dist/product/product.js.map +1 -1
- package/dist/profile/index.d.ts +2 -1
- package/dist/profile/index.d.ts.map +1 -1
- package/dist/profile/index.js +1 -1
- package/dist/profile/index.js.map +1 -1
- package/dist/profile/profile.d.ts +24 -2
- package/dist/profile/profile.d.ts.map +1 -1
- package/dist/profile/profile.js +34 -8
- package/dist/profile/profile.js.map +1 -1
- package/dist/query.d.ts +18 -2
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +24 -6
- package/dist/query.js.map +1 -1
- package/dist/rbac/admin.d.ts +43 -0
- package/dist/rbac/admin.d.ts.map +1 -0
- package/dist/rbac/admin.js +172 -0
- package/dist/rbac/admin.js.map +1 -0
- package/dist/rbac/index.d.ts +4 -0
- package/dist/rbac/index.d.ts.map +1 -1
- package/dist/rbac/index.js +2 -0
- package/dist/rbac/index.js.map +1 -1
- package/dist/rbac/seed.d.ts +30 -0
- package/dist/rbac/seed.d.ts.map +1 -0
- package/dist/rbac/seed.js +75 -0
- package/dist/rbac/seed.js.map +1 -0
- package/dist/search/contentSearch.d.ts +1 -1
- package/dist/search/contentSearch.d.ts.map +1 -1
- package/dist/search/contentSearch.js +22 -12
- package/dist/search/contentSearch.js.map +1 -1
- package/dist/social/social.d.ts +4 -2
- package/dist/social/social.d.ts.map +1 -1
- package/dist/social/social.js +25 -8
- package/dist/social/social.js.map +1 -1
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/video/video.d.ts +3 -0
- package/dist/video/video.d.ts.map +1 -1
- package/dist/video/video.js +17 -13
- package/dist/video/video.js.map +1 -1
- package/dist/voting/voting.d.ts.map +1 -1
- package/dist/voting/voting.js +39 -1
- package/dist/voting/voting.js.map +1 -1
- package/package.json +9 -7
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { eq, and, sql } from 'drizzle-orm';
|
|
2
|
+
import { contests, contestEntries, contestAgreementAcceptances, contestEntryPrivateFields, } from '@commonpub/schema';
|
|
3
|
+
import { countRows } from '../query.js';
|
|
4
|
+
import { createContent, deleteContent } from '../content/content.js';
|
|
5
|
+
import { normalizeStages, currentStage, isEliminated } from './stages.js';
|
|
6
|
+
import { validateSubmissionFields, hashTerms } from './validation.js';
|
|
7
|
+
// DB-backed submission writers. Pure validation/partition lives in validation.ts.
|
|
8
|
+
/**
|
|
9
|
+
* Persist the PII + agreement halves of a partitioned submission within an open
|
|
10
|
+
* transaction. PII is upserted (one row per entry, merged with any prior PII so
|
|
11
|
+
* a later stage's PII doesn't wipe an earlier stage's); agreements are appended
|
|
12
|
+
* as immutable acceptance rows (terms hash + snapshot). No-op when both empty.
|
|
13
|
+
*/
|
|
14
|
+
export async function recordPrivateAndAgreements(tx, args) {
|
|
15
|
+
const { contestId, entryId, userId, stageId, pii, agreements, ip } = args;
|
|
16
|
+
if (Object.keys(pii).length > 0) {
|
|
17
|
+
const [existing] = await tx
|
|
18
|
+
.select({ fields: contestEntryPrivateFields.fields })
|
|
19
|
+
.from(contestEntryPrivateFields)
|
|
20
|
+
.where(eq(contestEntryPrivateFields.entryId, entryId))
|
|
21
|
+
.for('update');
|
|
22
|
+
const merged = { ...(existing?.fields ?? {}), ...pii };
|
|
23
|
+
await tx
|
|
24
|
+
.insert(contestEntryPrivateFields)
|
|
25
|
+
.values({ contestId, entryId, userId, fields: merged })
|
|
26
|
+
.onConflictDoUpdate({
|
|
27
|
+
target: contestEntryPrivateFields.entryId,
|
|
28
|
+
set: { fields: merged, updatedAt: new Date() },
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
if (agreements.length > 0) {
|
|
32
|
+
await tx.insert(contestAgreementAcceptances).values(agreements.map((a) => ({
|
|
33
|
+
contestId,
|
|
34
|
+
entryId,
|
|
35
|
+
userId,
|
|
36
|
+
stageId,
|
|
37
|
+
fieldKey: a.fieldKey,
|
|
38
|
+
termsHash: hashTerms(a.terms),
|
|
39
|
+
termsSnapshot: a.terms,
|
|
40
|
+
ip: ip ?? null,
|
|
41
|
+
})));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Submit (or update) an entrant's per-stage artifact: the filled template
|
|
46
|
+
* values for one `submission` stage, snapshotted onto the entry's
|
|
47
|
+
* `stageSubmissions`. Owner-only. The stage must be the contest's CURRENT
|
|
48
|
+
* stage while the contest is `active` (status stays the gating truth — the
|
|
49
|
+
* organizer maps a later submission round back to `active` when advancing).
|
|
50
|
+
* Re-submitting while the stage is open replaces that stage's artifact.
|
|
51
|
+
* Cohort gate: an entry culled at a prior review stage can no longer submit.
|
|
52
|
+
*/
|
|
53
|
+
export async function submitStageArtifact(db, entryId, stageId, fields, userId, ip) {
|
|
54
|
+
const fail = (error) => ({ submitted: false, error });
|
|
55
|
+
const existing = await db
|
|
56
|
+
.select({
|
|
57
|
+
contestId: contestEntries.contestId,
|
|
58
|
+
entrantId: contestEntries.userId,
|
|
59
|
+
stageState: contestEntries.stageState,
|
|
60
|
+
contestStatus: contests.status,
|
|
61
|
+
stages: contests.stages,
|
|
62
|
+
currentStageId: contests.currentStageId,
|
|
63
|
+
startDate: contests.startDate,
|
|
64
|
+
endDate: contests.endDate,
|
|
65
|
+
judgingEndDate: contests.judgingEndDate,
|
|
66
|
+
})
|
|
67
|
+
.from(contestEntries)
|
|
68
|
+
.innerJoin(contests, eq(contestEntries.contestId, contests.id))
|
|
69
|
+
.where(eq(contestEntries.id, entryId))
|
|
70
|
+
.limit(1);
|
|
71
|
+
if (existing.length === 0)
|
|
72
|
+
return fail('Entry not found');
|
|
73
|
+
const row = existing[0];
|
|
74
|
+
if (row.entrantId !== userId)
|
|
75
|
+
return fail('Not the entry owner');
|
|
76
|
+
if (row.contestStatus !== 'active') {
|
|
77
|
+
return fail('Stage submissions are only open while the contest is active');
|
|
78
|
+
}
|
|
79
|
+
const source = {
|
|
80
|
+
status: row.contestStatus,
|
|
81
|
+
startDate: row.startDate,
|
|
82
|
+
endDate: row.endDate,
|
|
83
|
+
judgingEndDate: row.judgingEndDate,
|
|
84
|
+
stages: row.stages,
|
|
85
|
+
currentStageId: row.currentStageId,
|
|
86
|
+
};
|
|
87
|
+
const stages = normalizeStages(source);
|
|
88
|
+
const stage = stages.find((s) => s.id === stageId);
|
|
89
|
+
if (!stage)
|
|
90
|
+
return fail('Unknown stage');
|
|
91
|
+
if (stage.kind !== 'submission')
|
|
92
|
+
return fail('This stage does not accept submissions');
|
|
93
|
+
const template = stage.submissionTemplate ?? [];
|
|
94
|
+
if (template.length === 0)
|
|
95
|
+
return fail('This stage has no submission template');
|
|
96
|
+
const current = currentStage(source);
|
|
97
|
+
if (current?.id !== stageId)
|
|
98
|
+
return fail('This stage is not currently open');
|
|
99
|
+
// Cohort gate: once a review cut culled the field, eliminated entries are
|
|
100
|
+
// out of every later round (mirrors judgeContestEntry's gate).
|
|
101
|
+
if (isEliminated({ stageState: row.stageState })) {
|
|
102
|
+
return fail('This entry was not advanced and can no longer submit');
|
|
103
|
+
}
|
|
104
|
+
const validated = validateSubmissionFields(template, fields);
|
|
105
|
+
if (!validated.ok)
|
|
106
|
+
return fail(validated.error);
|
|
107
|
+
const { artifact, pii, agreements } = validated.result;
|
|
108
|
+
// Atomic read-modify-write: lock the entry row so two concurrent saves of
|
|
109
|
+
// the same artifact can't clobber each other (same pattern as judgeScores).
|
|
110
|
+
// PII + agreements (if the template has any) are persisted in the SAME tx so
|
|
111
|
+
// they can never land in the public stageSubmissions jsonb.
|
|
112
|
+
return db.transaction(async (tx) => {
|
|
113
|
+
const [locked] = await tx
|
|
114
|
+
.select({ stageSubmissions: contestEntries.stageSubmissions })
|
|
115
|
+
.from(contestEntries)
|
|
116
|
+
.where(eq(contestEntries.id, entryId))
|
|
117
|
+
.for('update');
|
|
118
|
+
const submissions = (locked?.stageSubmissions ?? []);
|
|
119
|
+
const record = { stageId, fields: artifact, submittedAt: new Date().toISOString() };
|
|
120
|
+
const idx = submissions.findIndex((s) => s.stageId === stageId);
|
|
121
|
+
if (idx >= 0)
|
|
122
|
+
submissions[idx] = record;
|
|
123
|
+
else
|
|
124
|
+
submissions.push(record);
|
|
125
|
+
await tx
|
|
126
|
+
.update(contestEntries)
|
|
127
|
+
.set({ stageSubmissions: submissions })
|
|
128
|
+
.where(eq(contestEntries.id, entryId));
|
|
129
|
+
await recordPrivateAndAgreements(tx, { contestId: row.contestId, entryId, userId, stageId, pii, agreements, ip });
|
|
130
|
+
return { submitted: true, stageSubmissions: submissions };
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
// --- Form-first proposal submissions (Phase 4) ---
|
|
134
|
+
/** Content types a placeholder proposal project may be created as. */
|
|
135
|
+
const PLACEHOLDER_TYPES = ['project', 'blog', 'explainer'];
|
|
136
|
+
/**
|
|
137
|
+
* Form-first proposal submission (Phase 4). For a `submission` stage in
|
|
138
|
+
* `proposal` mode: validate the form, create a DRAFT placeholder project, link a
|
|
139
|
+
* contest entry to it (relaxing the published-only gate that `submitContestEntry`
|
|
140
|
+
* enforces), snapshot the artifact, record agreement acceptances, store PII in
|
|
141
|
+
* the private table, and bump `entryCount`. The placeholder + the entry +
|
|
142
|
+
* PII/agreements are written atomically: the placeholder is created first, then
|
|
143
|
+
* the entry tx; if the entry tx fails the placeholder is removed (compensating
|
|
144
|
+
* delete) so a failed submit never leaves an orphan draft.
|
|
145
|
+
*
|
|
146
|
+
* Flag (`features.contestProposals`) is enforced at the route; this fn enforces
|
|
147
|
+
* the structural gate that the target stage is current + proposal-mode.
|
|
148
|
+
*/
|
|
149
|
+
export async function submitContestProposal(db, args) {
|
|
150
|
+
const { contestId, stageId, fields, userId, ip } = args;
|
|
151
|
+
const fail = (error) => ({ ok: false, error });
|
|
152
|
+
const [contest] = await db
|
|
153
|
+
.select({
|
|
154
|
+
id: contests.id,
|
|
155
|
+
title: contests.title,
|
|
156
|
+
status: contests.status,
|
|
157
|
+
stages: contests.stages,
|
|
158
|
+
currentStageId: contests.currentStageId,
|
|
159
|
+
startDate: contests.startDate,
|
|
160
|
+
endDate: contests.endDate,
|
|
161
|
+
judgingEndDate: contests.judgingEndDate,
|
|
162
|
+
maxEntriesPerUser: contests.maxEntriesPerUser,
|
|
163
|
+
eligibleContentTypes: contests.eligibleContentTypes,
|
|
164
|
+
})
|
|
165
|
+
.from(contests)
|
|
166
|
+
.where(eq(contests.id, contestId))
|
|
167
|
+
.limit(1);
|
|
168
|
+
if (!contest)
|
|
169
|
+
return fail('Contest not found');
|
|
170
|
+
if (contest.status !== 'active')
|
|
171
|
+
return fail('Proposals are only open while the contest is active');
|
|
172
|
+
const source = {
|
|
173
|
+
status: contest.status,
|
|
174
|
+
startDate: contest.startDate,
|
|
175
|
+
endDate: contest.endDate,
|
|
176
|
+
judgingEndDate: contest.judgingEndDate,
|
|
177
|
+
stages: contest.stages,
|
|
178
|
+
currentStageId: contest.currentStageId,
|
|
179
|
+
};
|
|
180
|
+
const stage = normalizeStages(source).find((s) => s.id === stageId);
|
|
181
|
+
if (!stage)
|
|
182
|
+
return fail('Unknown stage');
|
|
183
|
+
if (stage.kind !== 'submission')
|
|
184
|
+
return fail('This stage does not accept submissions');
|
|
185
|
+
if (stage.submissionMode !== 'proposal')
|
|
186
|
+
return fail('This stage is not accepting proposals');
|
|
187
|
+
if (currentStage(source)?.id !== stageId)
|
|
188
|
+
return fail('This stage is not currently open');
|
|
189
|
+
const template = stage.submissionTemplate ?? [];
|
|
190
|
+
const validated = validateSubmissionFields(template, fields);
|
|
191
|
+
if (!validated.ok)
|
|
192
|
+
return fail(validated.error);
|
|
193
|
+
const { artifact, pii, agreements } = validated.result;
|
|
194
|
+
// Per-user entry cap (mirrors submitContestEntry).
|
|
195
|
+
if (contest.maxEntriesPerUser != null) {
|
|
196
|
+
const existing = await countRows(db, contestEntries, and(eq(contestEntries.contestId, contestId), eq(contestEntries.userId, userId)));
|
|
197
|
+
if (existing >= contest.maxEntriesPerUser)
|
|
198
|
+
return fail('You have reached the entry limit for this contest');
|
|
199
|
+
}
|
|
200
|
+
// Pick the placeholder content type: the contest's first eligible type when
|
|
201
|
+
// it's one we can create, else a project.
|
|
202
|
+
const eligible = (contest.eligibleContentTypes ?? []).find((t) => PLACEHOLDER_TYPES.includes(t));
|
|
203
|
+
const placeholderType = (eligible ?? 'project');
|
|
204
|
+
const title = artifact.title?.trim() || `${contest.title} proposal`;
|
|
205
|
+
// 1) Create the DRAFT placeholder (its own writes). The entrant develops it in
|
|
206
|
+
// the editor; the proposal text lives in the stage artifact below.
|
|
207
|
+
const placeholder = await createContent(db, userId, { type: placeholderType, title });
|
|
208
|
+
// 2) Link the entry + persist artifact/PII/agreements/count atomically. On any
|
|
209
|
+
// failure, remove the placeholder so a failed submit leaves nothing behind.
|
|
210
|
+
try {
|
|
211
|
+
const entryId = await db.transaction(async (tx) => {
|
|
212
|
+
const [inserted] = await tx
|
|
213
|
+
.insert(contestEntries)
|
|
214
|
+
.values({
|
|
215
|
+
contestId,
|
|
216
|
+
contentId: placeholder.id,
|
|
217
|
+
userId,
|
|
218
|
+
stageSubmissions: [{ stageId, fields: artifact, submittedAt: new Date().toISOString() }],
|
|
219
|
+
})
|
|
220
|
+
.onConflictDoNothing()
|
|
221
|
+
.returning({ id: contestEntries.id });
|
|
222
|
+
if (!inserted)
|
|
223
|
+
throw new Error('entry-conflict');
|
|
224
|
+
await tx
|
|
225
|
+
.update(contests)
|
|
226
|
+
.set({ entryCount: sql `${contests.entryCount} + 1` })
|
|
227
|
+
.where(eq(contests.id, contestId));
|
|
228
|
+
await recordPrivateAndAgreements(tx, { contestId, entryId: inserted.id, userId, stageId, pii, agreements, ip });
|
|
229
|
+
return inserted.id;
|
|
230
|
+
});
|
|
231
|
+
return { ok: true, entryId, projectSlug: placeholder.slug, contentId: placeholder.id, contentType: placeholder.type };
|
|
232
|
+
}
|
|
233
|
+
catch (err) {
|
|
234
|
+
await deleteContent(db, placeholder.id, userId).catch(() => { });
|
|
235
|
+
if (err instanceof Error && err.message === 'entry-conflict') {
|
|
236
|
+
return fail('You already have an entry for this contest');
|
|
237
|
+
}
|
|
238
|
+
throw err;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Read an entry's PII + agreement acceptances. NEVER reachable through the
|
|
243
|
+
* normal entries endpoints — the calling route gates this on the `contest.pii`
|
|
244
|
+
* permission OR the requester being the entrant. Returns null when the entry has
|
|
245
|
+
* no stored PII and no agreements.
|
|
246
|
+
*/
|
|
247
|
+
export async function getEntryPrivateData(db, entryId) {
|
|
248
|
+
const [priv] = await db
|
|
249
|
+
.select()
|
|
250
|
+
.from(contestEntryPrivateFields)
|
|
251
|
+
.where(eq(contestEntryPrivateFields.entryId, entryId))
|
|
252
|
+
.limit(1);
|
|
253
|
+
const agreements = await db
|
|
254
|
+
.select({
|
|
255
|
+
fieldKey: contestAgreementAcceptances.fieldKey,
|
|
256
|
+
stageId: contestAgreementAcceptances.stageId,
|
|
257
|
+
termsHash: contestAgreementAcceptances.termsHash,
|
|
258
|
+
termsSnapshot: contestAgreementAcceptances.termsSnapshot,
|
|
259
|
+
acceptedAt: contestAgreementAcceptances.acceptedAt,
|
|
260
|
+
ip: contestAgreementAcceptances.ip,
|
|
261
|
+
})
|
|
262
|
+
.from(contestAgreementAcceptances)
|
|
263
|
+
.where(eq(contestAgreementAcceptances.entryId, entryId));
|
|
264
|
+
if (!priv && agreements.length === 0)
|
|
265
|
+
return null;
|
|
266
|
+
return {
|
|
267
|
+
contestId: priv?.contestId ?? '',
|
|
268
|
+
entryId,
|
|
269
|
+
userId: priv?.userId ?? '',
|
|
270
|
+
fields: (priv?.fields ?? {}),
|
|
271
|
+
updatedAt: priv?.updatedAt ?? new Date(0),
|
|
272
|
+
agreements,
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
//# sourceMappingURL=submissions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"submissions.js","sourceRoot":"","sources":["../../src/contest/submissions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import type { ContestStatus, ContestStage, ContestStageSubmission } from '@commonpub/schema';
|
|
2
|
+
import type { DB } from '../types.js';
|
|
3
|
+
export type ContestJudgingVisibility = 'public' | 'judges-only' | 'private';
|
|
4
|
+
export type ContestVisibility = 'public' | 'unlisted' | 'private';
|
|
5
|
+
export interface ContestPrize {
|
|
6
|
+
place?: number;
|
|
7
|
+
category?: string;
|
|
8
|
+
/** Optional — a prize can be description-only (no forced placement title). */
|
|
9
|
+
title?: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
value?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface ContestJudgingCriterion {
|
|
14
|
+
label: string;
|
|
15
|
+
weight?: number;
|
|
16
|
+
description?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface CriterionScore {
|
|
19
|
+
label: string;
|
|
20
|
+
score: number;
|
|
21
|
+
max: number;
|
|
22
|
+
}
|
|
23
|
+
export interface JudgeScoreEntry {
|
|
24
|
+
judgeId: string;
|
|
25
|
+
score: number;
|
|
26
|
+
feedback?: string;
|
|
27
|
+
criteriaScores?: CriterionScore[];
|
|
28
|
+
/**
|
|
29
|
+
* The review stage this score was given in (Phase B2.5 per-round isolation). A
|
|
30
|
+
* judge has one score per round; the entry's live `score` aggregates only the
|
|
31
|
+
* CURRENT round. Absent on pre-B2.5 scores (treated as a single bucket).
|
|
32
|
+
*/
|
|
33
|
+
roundId?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface ContestListItem {
|
|
36
|
+
id: string;
|
|
37
|
+
title: string;
|
|
38
|
+
slug: string;
|
|
39
|
+
subheading: string | null;
|
|
40
|
+
description: string | null;
|
|
41
|
+
bannerUrl: string | null;
|
|
42
|
+
coverImageUrl: string | null;
|
|
43
|
+
status: ContestStatus;
|
|
44
|
+
startDate: Date;
|
|
45
|
+
endDate: Date;
|
|
46
|
+
entryCount: number;
|
|
47
|
+
createdAt: Date;
|
|
48
|
+
}
|
|
49
|
+
export interface ContestDetail extends ContestListItem {
|
|
50
|
+
subheading: string | null;
|
|
51
|
+
rules: string | null;
|
|
52
|
+
prizesDescription: string | null;
|
|
53
|
+
/** Per-field render mode ('markdown' | 'html'), independent for each field. */
|
|
54
|
+
descriptionFormat: 'markdown' | 'html';
|
|
55
|
+
rulesFormat: 'markdown' | 'html';
|
|
56
|
+
prizesDescriptionFormat: 'markdown' | 'html';
|
|
57
|
+
/** Block-editor body (BlockTuple[]); when non-null the viewer renders these
|
|
58
|
+
* instead of the legacy description/rules text + format toggle. */
|
|
59
|
+
descriptionBlocks: unknown[] | null;
|
|
60
|
+
rulesBlocks: unknown[] | null;
|
|
61
|
+
prizesBlocks: unknown[] | null;
|
|
62
|
+
showPrizes: boolean;
|
|
63
|
+
prizes: ContestPrize[] | null;
|
|
64
|
+
judgingCriteria: ContestJudgingCriterion[] | null;
|
|
65
|
+
judgingVisibility: ContestJudgingVisibility;
|
|
66
|
+
judgingEndDate: Date | null;
|
|
67
|
+
communityVotingEnabled: boolean;
|
|
68
|
+
eligibleContentTypes: string[] | null;
|
|
69
|
+
maxEntriesPerUser: number | null;
|
|
70
|
+
visibility: ContestVisibility;
|
|
71
|
+
visibleToRoles: string[] | null;
|
|
72
|
+
createdById: string;
|
|
73
|
+
/** Phase B1 — explicit stage timeline (`[]` ⇒ classic synthesized flow). */
|
|
74
|
+
stages: ContestStage[];
|
|
75
|
+
currentStageId: string | null;
|
|
76
|
+
/**
|
|
77
|
+
* Per-request view-model flag (NOT persisted): whether the CURRENT viewer may
|
|
78
|
+
* manage this contest — owner, a per-contest `editor` stakeholder, or a
|
|
79
|
+
* `contest.manage` holder. Set by the contest GET route; undefined elsewhere.
|
|
80
|
+
* Drives the client Edit/manage affordances (server remains the enforcement
|
|
81
|
+
* boundary).
|
|
82
|
+
*/
|
|
83
|
+
viewerCanManage?: boolean;
|
|
84
|
+
}
|
|
85
|
+
export interface ContestFilters {
|
|
86
|
+
status?: ContestStatus;
|
|
87
|
+
limit?: number;
|
|
88
|
+
offset?: number;
|
|
89
|
+
}
|
|
90
|
+
export interface CreateContestInput {
|
|
91
|
+
title: string;
|
|
92
|
+
slug: string;
|
|
93
|
+
subheading?: string;
|
|
94
|
+
description?: string;
|
|
95
|
+
rules?: string;
|
|
96
|
+
prizesDescription?: string;
|
|
97
|
+
descriptionFormat?: 'markdown' | 'html';
|
|
98
|
+
rulesFormat?: 'markdown' | 'html';
|
|
99
|
+
prizesDescriptionFormat?: 'markdown' | 'html';
|
|
100
|
+
/** Block-editor body (BlockTuple[]) for overview/rules. */
|
|
101
|
+
descriptionBlocks?: unknown[];
|
|
102
|
+
rulesBlocks?: unknown[];
|
|
103
|
+
prizesBlocks?: unknown[];
|
|
104
|
+
showPrizes?: boolean;
|
|
105
|
+
bannerUrl?: string;
|
|
106
|
+
coverImageUrl?: string;
|
|
107
|
+
prizes?: ContestPrize[];
|
|
108
|
+
judgingCriteria?: ContestJudgingCriterion[];
|
|
109
|
+
stages?: ContestStage[];
|
|
110
|
+
currentStageId?: string;
|
|
111
|
+
/** Seed-only: populates the contest_judges table at creation. */
|
|
112
|
+
judges?: string[];
|
|
113
|
+
communityVotingEnabled?: boolean;
|
|
114
|
+
judgingVisibility?: ContestJudgingVisibility;
|
|
115
|
+
eligibleContentTypes?: string[];
|
|
116
|
+
maxEntriesPerUser?: number;
|
|
117
|
+
visibility?: ContestVisibility;
|
|
118
|
+
visibleToRoles?: string[];
|
|
119
|
+
/** Seed-only: populates the contest_stakeholders table at creation. */
|
|
120
|
+
stakeholders?: string[];
|
|
121
|
+
startDate: string;
|
|
122
|
+
endDate: string;
|
|
123
|
+
judgingEndDate?: string;
|
|
124
|
+
createdBy: string;
|
|
125
|
+
}
|
|
126
|
+
/** A contest-like object the pure stage helpers (stages.ts) operate on. */
|
|
127
|
+
export type StageSource = {
|
|
128
|
+
status: string;
|
|
129
|
+
startDate: Date | string;
|
|
130
|
+
endDate: Date | string;
|
|
131
|
+
judgingEndDate: Date | string | null;
|
|
132
|
+
stages?: ContestStage[] | null;
|
|
133
|
+
currentStageId?: string | null;
|
|
134
|
+
};
|
|
135
|
+
export interface ContestEntryItem {
|
|
136
|
+
id: string;
|
|
137
|
+
contestId: string;
|
|
138
|
+
contentId: string;
|
|
139
|
+
userId: string;
|
|
140
|
+
score: number | null;
|
|
141
|
+
rank: number | null;
|
|
142
|
+
/** Phase B2 — per-stage cohort outcome; `eliminated` is the derived convenience. */
|
|
143
|
+
stageState: Array<{
|
|
144
|
+
stageId: string;
|
|
145
|
+
status: 'advanced' | 'eliminated';
|
|
146
|
+
score?: number | null;
|
|
147
|
+
rank?: number | null;
|
|
148
|
+
}>;
|
|
149
|
+
eliminated: boolean;
|
|
150
|
+
/**
|
|
151
|
+
* Per-stage artifacts (proposal/prototype field values). Present only when
|
|
152
|
+
* the caller asked for them (privileged viewers, or the entrant's own rows
|
|
153
|
+
* via `stageSubmissionsViewerId`) — never on the public listing.
|
|
154
|
+
*/
|
|
155
|
+
stageSubmissions?: ContestStageSubmission[];
|
|
156
|
+
submittedAt: Date;
|
|
157
|
+
contentTitle: string;
|
|
158
|
+
contentSlug: string;
|
|
159
|
+
contentType: string;
|
|
160
|
+
contentCoverImageUrl: string | null;
|
|
161
|
+
authorName: string;
|
|
162
|
+
authorUsername: string;
|
|
163
|
+
authorAvatarUrl: string | null;
|
|
164
|
+
judgeScores?: JudgeScoreEntry[];
|
|
165
|
+
}
|
|
166
|
+
/** An accepted agreement field, ready to be recorded as an acceptance row. */
|
|
167
|
+
export interface AgreementAcceptanceInput {
|
|
168
|
+
fieldKey: string;
|
|
169
|
+
label: string;
|
|
170
|
+
terms: string;
|
|
171
|
+
termsFormat: 'markdown' | 'html';
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* The result of validating + partitioning a submission form. Agreements and PII
|
|
175
|
+
* are split OUT of the public `artifact` so they never reach `stageSubmissions`:
|
|
176
|
+
* agreements become immutable acceptance rows, PII lands in the private table.
|
|
177
|
+
*/
|
|
178
|
+
export interface PartitionedSubmission {
|
|
179
|
+
/** Non-PII, non-agreement fields → the public `stageSubmissions` artifact. */
|
|
180
|
+
artifact: Record<string, string>;
|
|
181
|
+
/** PII fields (`pii: true` or `address`) → `contest_entry_private_fields`. */
|
|
182
|
+
pii: Record<string, string>;
|
|
183
|
+
/** Accepted agreements → `contest_agreement_acceptances`. */
|
|
184
|
+
agreements: AgreementAcceptanceInput[];
|
|
185
|
+
}
|
|
186
|
+
export interface AdvanceStageInput {
|
|
187
|
+
/** The `review` stage whose advancement cut we're applying. */
|
|
188
|
+
reviewStageId: string;
|
|
189
|
+
mode: 'topN' | 'manual';
|
|
190
|
+
/** topN mode: how many of the eligible cohort advance (ties broken deterministically). */
|
|
191
|
+
topN?: number;
|
|
192
|
+
/** manual mode: explicit entry ids that advance; the rest of the cohort is eliminated. */
|
|
193
|
+
advancedEntryIds?: string[];
|
|
194
|
+
}
|
|
195
|
+
/** The transaction handle drizzle passes to `db.transaction(async (tx) => …)`. */
|
|
196
|
+
export type ContestTx = Parameters<Parameters<DB['transaction']>[0]>[0];
|
|
197
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/contest/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC7F,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAMtC,MAAM,MAAM,wBAAwB,GAAG,QAAQ,GAAG,aAAa,GAAG,SAAS,CAAC;AAC5E,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;AAElE,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,cAAc,EAAE,CAAC;IAClC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,EAAE,IAAI,CAAC;IAChB,OAAO,EAAE,IAAI,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,aAAc,SAAQ,eAAe;IACpD,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,+EAA+E;IAC/E,iBAAiB,EAAE,UAAU,GAAG,MAAM,CAAC;IACvC,WAAW,EAAE,UAAU,GAAG,MAAM,CAAC;IACjC,uBAAuB,EAAE,UAAU,GAAG,MAAM,CAAC;IAC7C;wEACoE;IACpE,iBAAiB,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IACpC,WAAW,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC9B,YAAY,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC/B,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,YAAY,EAAE,GAAG,IAAI,CAAC;IAC9B,eAAe,EAAE,uBAAuB,EAAE,GAAG,IAAI,CAAC;IAClD,iBAAiB,EAAE,wBAAwB,CAAC;IAC5C,cAAc,EAAE,IAAI,GAAG,IAAI,CAAC;IAC5B,sBAAsB,EAAE,OAAO,CAAC;IAChC,oBAAoB,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACtC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,UAAU,EAAE,iBAAiB,CAAC;IAC9B,cAAc,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IACxC,WAAW,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IAClC,uBAAuB,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IAC9C,2DAA2D;IAC3D,iBAAiB,CAAC,EAAE,OAAO,EAAE,CAAC;IAC9B,WAAW,CAAC,EAAE,OAAO,EAAE,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,EAAE,CAAC;IACzB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;IACxB,eAAe,CAAC,EAAE,uBAAuB,EAAE,CAAC;IAC5C,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,iBAAiB,CAAC,EAAE,wBAAwB,CAAC;IAC7C,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAC/B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,uEAAuE;IACvE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,2EAA2E;AAC3E,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,IAAI,GAAG,MAAM,CAAC;IACzB,OAAO,EAAE,IAAI,GAAG,MAAM,CAAC;IACvB,cAAc,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC;IACrC,MAAM,CAAC,EAAE,YAAY,EAAE,GAAG,IAAI,CAAC;IAC/B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC,CAAC;AAEF,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,oFAAoF;IACpF,UAAU,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,GAAG,YAAY,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;IACvH,UAAU,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,sBAAsB,EAAE,CAAC;IAC5C,WAAW,EAAE,IAAI,CAAC;IAElB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;CACjC;AAED,8EAA8E;AAC9E,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,UAAU,GAAG,MAAM,CAAC;CAClC;AAED;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACpC,8EAA8E;IAC9E,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,8EAA8E;IAC9E,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,6DAA6D;IAC7D,UAAU,EAAE,wBAAwB,EAAE,CAAC;CACxC;AAED,MAAM,WAAW,iBAAiB;IAChC,+DAA+D;IAC/D,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAC;IACxB,0FAA0F;IAC1F,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0FAA0F;IAC1F,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED,kFAAkF;AAClF,MAAM,MAAM,SAAS,GAAG,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/contest/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ContestSubmissionTemplateField } from '@commonpub/schema';
|
|
2
|
+
import type { PartitionedSubmission } from './types.js';
|
|
3
|
+
/** sha-256 hex of an agreement's terms text (integrity check vs the snapshot). */
|
|
4
|
+
export declare function hashTerms(terms: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* Validate an entrant's submission values against a stage template AND partition
|
|
7
|
+
* them into artifact / PII / agreements. Pure + exhaustively testable. Domain
|
|
8
|
+
* checks, not just shape: unknown keys rejected (no smuggling values outside the
|
|
9
|
+
* template), required fields non-blank, `url`/`email`/`number`/`select`/`date`/
|
|
10
|
+
* `address` validated for their kind, agreements must be accepted when required.
|
|
11
|
+
* PII (`pii`/`address`) and agreement fields are kept OUT of `artifact`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function validateSubmissionFields(template: ContestSubmissionTemplateField[], values: Record<string, string>): {
|
|
14
|
+
ok: true;
|
|
15
|
+
result: PartitionedSubmission;
|
|
16
|
+
} | {
|
|
17
|
+
ok: false;
|
|
18
|
+
error: string;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Back-compat artifact-only validator (the original per-stage-artifact contract).
|
|
22
|
+
* Delegates to `validateSubmissionFields` and returns just the public artifact —
|
|
23
|
+
* PII/agreement fields, if any, are partitioned out and handled by the caller.
|
|
24
|
+
*/
|
|
25
|
+
export declare function validateStageArtifactFields(template: ContestSubmissionTemplateField[], fields: Record<string, string>): {
|
|
26
|
+
ok: true;
|
|
27
|
+
fields: Record<string, string>;
|
|
28
|
+
} | {
|
|
29
|
+
ok: false;
|
|
30
|
+
error: string;
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/contest/validation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,mBAAmB,CAAC;AACxE,OAAO,KAAK,EAA4B,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAelF,kFAAkF;AAClF,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,8BAA8B,EAAE,EAC1C,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC7B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,qBAAqB,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CA4F5E;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,8BAA8B,EAAE,EAC1C,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC7B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAI7E"}
|