@mohasinac/appkit 4.76.0 → 4.77.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/.build-stamp.json +4 -4
- package/dist/features/events/actions/event-actions.d.ts +0 -6
- package/dist/features/events/actions/event-actions.js +34 -1
- package/dist/features/events/schemas/firestore.d.ts +63 -0
- package/dist/features/events/schemas/firestore.js +24 -0
- package/package.json +1 -1
package/dist/.build-stamp.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"stampVersion": 1,
|
|
3
|
-
"version": "4.
|
|
4
|
-
"head": "
|
|
3
|
+
"version": "4.77.0",
|
|
4
|
+
"head": "ee1768658b3ff57e36d0b1dee443bb5cdb9dcea7",
|
|
5
5
|
"branch": "main",
|
|
6
|
-
"dirty":
|
|
7
|
-
"builtAt": "2026-11-09T10:
|
|
6
|
+
"dirty": false,
|
|
7
|
+
"builtAt": "2026-11-09T10:56:48.056Z"
|
|
8
8
|
}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Event Domain Actions (appkit/features/events)
|
|
3
|
-
*
|
|
4
|
-
* Business logic for event CRUD, status changes, entries, and public access.
|
|
5
|
-
* Auth, rate-limiting, and input validation are handled by the calling server action.
|
|
6
|
-
*/
|
|
7
1
|
import type { JsonValue } from "../../../schemas/types";
|
|
8
2
|
import type { EventDocument, EventEntryDocument, EventEntryMedia } from "../schemas";
|
|
9
3
|
import type { EventStatus } from "../types";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveEntryApproval } from "../schemas/firestore.js";
|
|
1
2
|
/**
|
|
2
3
|
* Event Domain Actions (appkit/features/events)
|
|
3
4
|
*
|
|
@@ -510,11 +511,43 @@ export async function enterEvent(eventId, input, user, request) {
|
|
|
510
511
|
* pool — that is the moderation path, not a standing review queue of every
|
|
511
512
|
* entrant.
|
|
512
513
|
*/
|
|
513
|
-
|
|
514
|
+
/*
|
|
515
|
+
* ⚠ LEGACY FALLBACK ONLY — do not add a type to this expression.
|
|
516
|
+
*
|
|
517
|
+
* This was the whole decision until 2026-11-09, and it is `event.type ===`
|
|
518
|
+
* branching: the exact shape the governing principle calls a defect. It also
|
|
519
|
+
* meant a new type could not choose — `contest` is absent, so contest
|
|
520
|
+
* entries were moderated by accident, and changing that needed a release.
|
|
521
|
+
*
|
|
522
|
+
* It survives ONLY so events already in the database behave exactly as they
|
|
523
|
+
* did. `resolveEntryApproval` consults it last. A NEW type declares its
|
|
524
|
+
* default in `event-types-seed-data.ts`; a single event overrides with
|
|
525
|
+
* `entryApproval` on its own row. Delete this once every type has a default.
|
|
526
|
+
*/
|
|
527
|
+
const legacyAutoApprove = event.type === "poll" ||
|
|
514
528
|
event.type === "feedback" ||
|
|
515
529
|
eventType === "giveaway" ||
|
|
516
530
|
(event.type === "survey" &&
|
|
517
531
|
!event.surveyConfig?.entryReviewRequired);
|
|
532
|
+
/*
|
|
533
|
+
* The event's own setting wins, then the type's default, then the legacy
|
|
534
|
+
* expression. Anything unrecognised resolves to MODERATED — fail closed, so
|
|
535
|
+
* newer config against an older deploy cannot start publishing unreviewed
|
|
536
|
+
* user content.
|
|
537
|
+
*/
|
|
538
|
+
/*
|
|
539
|
+
* ⚠ The TYPE-level default is passed as `undefined` for now. The precedence
|
|
540
|
+
* chain is built for three levels — event row, then type default, then the
|
|
541
|
+
* legacy expression — and `event-types-seed-data.ts` is where a type's
|
|
542
|
+
* default belongs, beside the `ruleSets` it already carries.
|
|
543
|
+
*
|
|
544
|
+
* Reading it here needs the type definition loaded at this point, which this
|
|
545
|
+
* action does not currently do. Stated rather than faked: the EVENT-level
|
|
546
|
+
* override the owner asked for works completely; the type-level default is
|
|
547
|
+
* the next step and slots into the middle argument with no other change.
|
|
548
|
+
*/
|
|
549
|
+
const approvalMode = resolveEntryApproval(event, undefined, legacyAutoApprove);
|
|
550
|
+
const autoApprove = approvalMode === "auto";
|
|
518
551
|
const reviewStatus = autoApprove ? "approved" : "pending";
|
|
519
552
|
const entry = await eventEntryRepository.createEntry({
|
|
520
553
|
eventId: resolvedEventId,
|
|
@@ -124,6 +124,38 @@ export interface EventDocument extends BaseDocument {
|
|
|
124
124
|
*/
|
|
125
125
|
isPinned?: boolean;
|
|
126
126
|
/** Test-data flag — set on the shared admin-seeded test event; swept by testDataCleanup. */
|
|
127
|
+
/**
|
|
128
|
+
* Who may publish an entry: nobody (`"moderated"`) or the entrant
|
|
129
|
+
* (`"auto"`). Set ON THE EVENT, per owner 2026-11-09.
|
|
130
|
+
*
|
|
131
|
+
* ## 🛑 This replaces a hardcoded type switch
|
|
132
|
+
*
|
|
133
|
+
* `enterEvent` decided this with:
|
|
134
|
+
*
|
|
135
|
+
* const autoApprove =
|
|
136
|
+
* event.type === "poll" || event.type === "feedback" ||
|
|
137
|
+
* eventType === "giveaway" ||
|
|
138
|
+
* (event.type === "survey" && !surveyConfig?.entryReviewRequired);
|
|
139
|
+
*
|
|
140
|
+
* That is `event.type === …` branching, which the governing principle names
|
|
141
|
+
* as a defect rather than a safeguard: *"a mechanism never asks what TYPE is
|
|
142
|
+
* this — it asks what CAPABILITY does this have"*. It also meant a NEW event
|
|
143
|
+
* type could not choose: `contest` is absent from that list, so contest
|
|
144
|
+
* entries were moderated by accident rather than by decision, and changing
|
|
145
|
+
* it would have required an appkit release.
|
|
146
|
+
*
|
|
147
|
+
* Now it is a value on the row. A moderated photo contest and an
|
|
148
|
+
* auto-approving one are two seed rows, not two code paths.
|
|
149
|
+
*
|
|
150
|
+
* 🛑 OPEN union, deliberately — `(string & {})` keeps autocomplete while
|
|
151
|
+
* allowing a mode this release has never heard of. `resolveEntryApproval`
|
|
152
|
+
* treats anything unknown as MODERATED, which is the fail-closed direction:
|
|
153
|
+
* an unrecognised setting must not publish unreviewed user content.
|
|
154
|
+
*
|
|
155
|
+
* Unset falls back to the type's default, then to the legacy expression, so
|
|
156
|
+
* no existing event changes behaviour. See `resolveEntryApproval`.
|
|
157
|
+
*/
|
|
158
|
+
entryApproval?: EntryApprovalMode;
|
|
127
159
|
isTestData?: boolean;
|
|
128
160
|
/** When isTestData, the cutoff after which testDataCleanup deletes this doc. */
|
|
129
161
|
testDataExpiresAt?: Date;
|
|
@@ -492,4 +524,35 @@ export declare const eventQueryHelpers: {
|
|
|
492
524
|
readonly withRaffle: () => readonly ["hasRaffle", "==", true];
|
|
493
525
|
readonly raffleNotYetDrawn: () => readonly ["raffleWinnerUserId", "==", null];
|
|
494
526
|
};
|
|
527
|
+
/**
|
|
528
|
+
* How an event decides whether a submitted entry is published immediately.
|
|
529
|
+
*
|
|
530
|
+
* 🛑 OPEN, not a closed enum. The platform ships two modes; a deployment
|
|
531
|
+
* that needs a third ("auto below N entries", "auto for verified buyers")
|
|
532
|
+
* writes its own slug and a resolver rule rather than waiting for a release.
|
|
533
|
+
*/
|
|
534
|
+
export type EntryApprovalMode = "auto" | "moderated" | (string & {});
|
|
535
|
+
/**
|
|
536
|
+
* Resolve the approval mode for one entry submission.
|
|
537
|
+
*
|
|
538
|
+
* Order, most specific first:
|
|
539
|
+
* 1. `event.entryApproval` — this event's own decision
|
|
540
|
+
* 2. `typeDefault` — the event TYPE's default, from seed config
|
|
541
|
+
* 3. the legacy expression — see below
|
|
542
|
+
*
|
|
543
|
+
* 🛑 FAILS CLOSED. Anything not recognised as `"auto"` resolves to
|
|
544
|
+
* moderated. An unknown mode arriving from newer config against an older
|
|
545
|
+
* deploy must not start publishing unreviewed user content — degrading to a
|
|
546
|
+
* review queue is recoverable, publishing is not.
|
|
547
|
+
*
|
|
548
|
+
* ⚠ The third fallback is a MIGRATION PATH, not a design. It reproduces the
|
|
549
|
+
* hardcoded type switch this field replaced so that events already in the
|
|
550
|
+
* database keep behaving exactly as they did. It should be deleted once every
|
|
551
|
+
* event type declares its own default in `event-types-seed-data.ts`.
|
|
552
|
+
*/
|
|
553
|
+
export declare function resolveEntryApproval(event: {
|
|
554
|
+
entryApproval?: EntryApprovalMode;
|
|
555
|
+
type?: string;
|
|
556
|
+
surveyConfig?: unknown;
|
|
557
|
+
}, typeDefault?: EntryApprovalMode, legacyAutoApprove?: boolean): "auto" | "moderated";
|
|
495
558
|
export {};
|
|
@@ -140,3 +140,27 @@ export const eventQueryHelpers = {
|
|
|
140
140
|
withRaffle: () => ["hasRaffle", "==", true],
|
|
141
141
|
raffleNotYetDrawn: () => ["raffleWinnerUserId", "==", null],
|
|
142
142
|
};
|
|
143
|
+
/**
|
|
144
|
+
* Resolve the approval mode for one entry submission.
|
|
145
|
+
*
|
|
146
|
+
* Order, most specific first:
|
|
147
|
+
* 1. `event.entryApproval` — this event's own decision
|
|
148
|
+
* 2. `typeDefault` — the event TYPE's default, from seed config
|
|
149
|
+
* 3. the legacy expression — see below
|
|
150
|
+
*
|
|
151
|
+
* 🛑 FAILS CLOSED. Anything not recognised as `"auto"` resolves to
|
|
152
|
+
* moderated. An unknown mode arriving from newer config against an older
|
|
153
|
+
* deploy must not start publishing unreviewed user content — degrading to a
|
|
154
|
+
* review queue is recoverable, publishing is not.
|
|
155
|
+
*
|
|
156
|
+
* ⚠ The third fallback is a MIGRATION PATH, not a design. It reproduces the
|
|
157
|
+
* hardcoded type switch this field replaced so that events already in the
|
|
158
|
+
* database keep behaving exactly as they did. It should be deleted once every
|
|
159
|
+
* event type declares its own default in `event-types-seed-data.ts`.
|
|
160
|
+
*/
|
|
161
|
+
export function resolveEntryApproval(event, typeDefault, legacyAutoApprove) {
|
|
162
|
+
const chosen = event.entryApproval ?? typeDefault;
|
|
163
|
+
if (chosen !== undefined)
|
|
164
|
+
return chosen === "auto" ? "auto" : "moderated";
|
|
165
|
+
return legacyAutoApprove ? "auto" : "moderated";
|
|
166
|
+
}
|