@getmicdrop/venue-calendar 4.3.62 → 4.3.64
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/{AddOnCard-DAk2utke.js → AddOnCard-CmK4bDY6.js} +4 -4
- package/dist/{ArrowLeftIcon-B2NmxWA9.js → ArrowLeftIcon-DcG0YVCM.js} +1 -1
- package/dist/{BlockPlaceholder-IligENph.js → BlockPlaceholder-BRzS72r6.js} +1 -1
- package/dist/{Calendar-DM7xqU81.js → Calendar-BOweKeiV.js} +2 -2
- package/dist/{CalendarFoundryView-uG2d_J_1.js → CalendarFoundryView-CKfpO1x8.js} +17 -17
- package/dist/{CalendarIcon-3jHpw2aE.js → CalendarIcon-DuxP0sxw.js} +1 -1
- package/dist/{CartView-C3yY3Umt.js → CartView-YNf1axYi.js} +33 -33
- package/dist/{Checkout-CtNDr22Y.js → Checkout-Betu62EF.js} +12 -12
- package/dist/{Checkout-D5xQo_-j.js → Checkout-D1VywGiq.js} +10 -10
- package/dist/{CheckoutTimer-Btjo7PEg.js → CheckoutTimer-DTufmYO_.js} +1 -1
- package/dist/{ChevronDownIcon-Dp1MGPEN.js → ChevronDownIcon-ClOg3b7E.js} +1 -1
- package/dist/{CloseIcon-DKrZf_7a.js → CloseIcon-CWQWjUzZ.js} +1 -1
- package/dist/{CollectionView-cwWo1mF3.js → CollectionView-PRegJwQb.js} +4 -4
- package/dist/{Event-DTJ0vZr1.js → Event--MH8pGju.js} +63 -63
- package/dist/{EventPage-CnOqHn95.js → EventPage-BGYvfkiB.js} +9 -9
- package/dist/{Input-CLSPTEMv.js → Input-BT3hf13K.js} +1 -1
- package/dist/{MailIcon-COatZ9tD.js → MailIcon-ivrn0IXt.js} +1 -1
- package/dist/{PasswordInput-BxuPPAe8.js → PasswordInput-CYZVT6fp.js} +1 -1
- package/dist/{RecommendationsRail-D9S_5rVW.js → RecommendationsRail-CPr__oRL.js} +12 -12
- package/dist/{ScarcityBadge-DU6RgcCz.js → ScarcityBadge-CDDlvbDh.js} +3 -3
- package/dist/{Select-CjELEaSF.js → Select-Bxdq5nKS.js} +1 -1
- package/dist/{SeriesPage-BTo2IP4b.js → SeriesPage-DhmD95h9.js} +2 -2
- package/dist/{Success-3ivVwkoj.js → Success-Cgrr7FqT.js} +10 -10
- package/dist/{TagIcon-CfyFlUh_.js → TagIcon-CFtOYvWM.js} +1 -1
- package/dist/{WarningCircleIcon-DsYifP07.js → WarningCircleIcon-Nmw1CPAT.js} +1 -1
- package/dist/{WarningTriIcon-C2oaxNjx.js → WarningTriIcon-flvVXH1d.js} +1 -1
- package/dist/api/transformers/venue.d.ts +1 -1
- package/dist/{api-BX4Imu-R.js → api-Vyamuo2p.js} +1 -1
- package/dist/{copyToClipboard-CIu22_xq.js → copyToClipboard-BmB4QyKB.js} +1 -1
- package/dist/{createErrorGate.svelte-Z3I2WfRr.js → createErrorGate.svelte-CDXAf5Lm.js} +1 -1
- package/dist/{scarcity-ClhXuC6t.js → datetime-ClxISJBt.js} +58 -57
- package/dist/{event-transform-CG6CJlbD.js → event-transform-L2ZTddgP.js} +19 -19
- package/dist/{href-Dz1CMH4c.js → href-DKLRNO3s.js} +1 -1
- package/dist/{i18n-DLFy3vxe.js → i18n-DFlVZfRe.js} +9 -9
- package/dist/{to-event-card-props-DObG6xUq.js → to-event-card-props-CmDQ3iGb.js} +34 -35
- package/dist/types/index.d.ts +602 -597
- package/dist/{utils-C15ZjLiK.js → utils-C_wZYlA9.js} +1 -1
- package/dist/{venue-Dv8MYjz_.js → venue-CMrlfcp2.js} +1 -1
- package/dist/venue-calendar.es.js +31 -31
- package/dist/venue-calendar.iife.js +13 -13
- package/dist/venue-calendar.umd.js +13 -13
- package/package.json +2 -1
- package/src/lib/constants/scarcity.ts +181 -0
- /package/dist/locales/{4.3.62 → 4.3.64}/flow/de.js +0 -0
- /package/dist/locales/{4.3.62 → 4.3.64}/flow/es.js +0 -0
- /package/dist/locales/{4.3.62 → 4.3.64}/flow/fr.js +0 -0
- /package/dist/locales/{4.3.62 → 4.3.64}/flow/id.js +0 -0
- /package/dist/locales/{4.3.62 → 4.3.64}/flow/it.js +0 -0
- /package/dist/locales/{4.3.62 → 4.3.64}/flow/ja.js +0 -0
- /package/dist/locales/{4.3.62 → 4.3.64}/flow/ko.js +0 -0
- /package/dist/locales/{4.3.62 → 4.3.64}/flow/nl.js +0 -0
- /package/dist/locales/{4.3.62 → 4.3.64}/flow/pl.js +0 -0
- /package/dist/locales/{4.3.62 → 4.3.64}/flow/pt-br.js +0 -0
- /package/dist/locales/{4.3.62 → 4.3.64}/flow/tr.js +0 -0
- /package/dist/locales/{4.3.62 → 4.3.64}/flow/zh.js +0 -0
- /package/dist/locales/{4.3.62 → 4.3.64}/main/de.js +0 -0
- /package/dist/locales/{4.3.62 → 4.3.64}/main/es.js +0 -0
- /package/dist/locales/{4.3.62 → 4.3.64}/main/fr.js +0 -0
- /package/dist/locales/{4.3.62 → 4.3.64}/main/id.js +0 -0
- /package/dist/locales/{4.3.62 → 4.3.64}/main/it.js +0 -0
- /package/dist/locales/{4.3.62 → 4.3.64}/main/ja.js +0 -0
- /package/dist/locales/{4.3.62 → 4.3.64}/main/ko.js +0 -0
- /package/dist/locales/{4.3.62 → 4.3.64}/main/nl.js +0 -0
- /package/dist/locales/{4.3.62 → 4.3.64}/main/pl.js +0 -0
- /package/dist/locales/{4.3.62 → 4.3.64}/main/pt-br.js +0 -0
- /package/dist/locales/{4.3.62 → 4.3.64}/main/tr.js +0 -0
- /package/dist/locales/{4.3.62 → 4.3.64}/main/zh.js +0 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getmicdrop/venue-calendar",
|
|
3
3
|
"_version_comment": "4.1.55 was the last PUBLISHED version, and the tree carried 36 commits npm's 4.1.55 does not have — including the gate-password security fix and four money fixes. A filename must never reuse a version npm has taken (jsDelivr/unpkg cache the FIRST bytes they see under `@4.1.55` forever, so republishing that number ships the old widget to every venue that pinned it). Bumped to 4.2.0 here, BEFORE anything is built for publish. `npm run release:preflight` refuses to proceed when this number already exists in the registry.",
|
|
4
|
-
"version": "4.3.
|
|
4
|
+
"version": "4.3.64",
|
|
5
5
|
"description": "A customizable Svelte calendar component for displaying comedy events",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"types": "./dist/types/index.d.ts",
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"dist",
|
|
40
40
|
"!dist/**/*.map",
|
|
41
41
|
"src/lib/theme.js",
|
|
42
|
+
"src/lib/constants/scarcity.ts",
|
|
42
43
|
"README.md"
|
|
43
44
|
],
|
|
44
45
|
"_sideEffects_comment": "There is deliberately NO `sideEffects` field, and adding one is a regression. Measured 2026-08-14 with a scratch consumer that imports a single export from the packed tarball and bundles it: vite 7.3.6 (the toolchain micdrop-frontend and performersportal actually run) produced 3,359,354 B over 53 chunks with no field and 3,359,364 B over 53 chunks with the allowlist — ten bytes LARGER. vite 8.2.1 (this repo's own) produced 3,303,350 B vs 3,303,181 B, a 169-byte saving on 3.30 MB (0.005%), and `sideEffects: false` came out byte-identical to the allowlist, i.e. the VALUE is inert. There is nothing to tree-shake: the ES entry is a pre-bundled single entry whose graph is reached by dynamic import. The risk is not inert. An AST scan of dist found 23 of 94 chunks carrying real import-time side effects — Svelte 5 `.delegate([...])` event registration in Input-*, ModalFooter-*, timezones-* and to-event-card-props-* (all inside the 23-file eager closure), zod's `globalThis.__zod_globalConfig` init in serverTotals-*, and in the entry itself the CSS-injection IIFE, `customElements.define`, the `window.__MICDROP_CALENDAR__` handshake and the auto-mount call. Those chunk filenames are content-hashed and change every build, so no allowlist can name them and stay correct; the first hash churn after a wrong list silently ships an unstyled widget with dead click handlers to third-party venue sites. Zero measurable upside against that is not a trade worth making.",
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scarcity thresholds for ticket availability badges and copy.
|
|
3
|
+
*
|
|
4
|
+
* THE single source of truth for the three-tier urgency model. Every scarcity
|
|
5
|
+
* claim in venue-calendar routes here: the browse grid
|
|
6
|
+
* (browse-widgets/status.ts), the event page + booking widget + ticket cards
|
|
7
|
+
* (public-calendar-flow/getScarcityLevel adapts this function's enum to its
|
|
8
|
+
* own 'none'/'sold_out' shape), the <ScarcityBadge>, and the ticket-selection
|
|
9
|
+
* TicketBlock.
|
|
10
|
+
*
|
|
11
|
+
* Tiers use a SLIDING percent threshold so the claim scales with venue size
|
|
12
|
+
* (Peet UX ruling 2026-07-07 — grow the canon, don't flatten to a flat
|
|
13
|
+
* percent). The base thresholds bend toward an absolute floor/cap:
|
|
14
|
+
*
|
|
15
|
+
* - urgent : remaining <= effective(URGENT_PCT) AND remaining <= MAX_EXACT_COUNT
|
|
16
|
+
* -> "Only N left" with exact count + flame
|
|
17
|
+
* - low : remaining <= effective(LOW_PCT) AND not urgent
|
|
18
|
+
* -> "Few tickets left" / "Selling fast" (vague) + flame
|
|
19
|
+
* - plenty : otherwise -> no badge
|
|
20
|
+
* - sold-out: remaining <= 0 -> "Sold out"
|
|
21
|
+
*
|
|
22
|
+
* ABSOLUTE_CAP keeps big rooms honest: a 1,000-seat show at 200 left (20%) is
|
|
23
|
+
* NOT "selling fast" — the cap forbids any claim above 30 remaining. ABSOLUTE_FLOOR
|
|
24
|
+
* keeps small rooms proportional: a 10-seat room at 5 left (50%) genuinely is
|
|
25
|
+
* going fast, so the floor allows the claim even at a high percent.
|
|
26
|
+
*
|
|
27
|
+
* MAX_EXACT_COUNT caps the exact-count copy: even if percent triggers urgent on
|
|
28
|
+
* a huge inventory, we only reveal exact remaining when the absolute number is
|
|
29
|
+
* small enough to feel like genuine scarcity rather than dump-data ("Only 487 left").
|
|
30
|
+
*
|
|
31
|
+
* SCARCITY_POLICY.CLAIM_ON_UNTOUCHED_POOL answers MIC-3808 and nothing else:
|
|
32
|
+
* whether a pool that has sold NOTHING yet may still make a claim. See that
|
|
33
|
+
* constant's own note — it is the one line Peet's ruling moves.
|
|
34
|
+
*
|
|
35
|
+
* MIRRORED COPY — EDIT BOTH, AND THE MIRROR IS NOW MACHINE-CHECKED: the admin
|
|
36
|
+
* app pins a copy of the block between the `scarcity-canon:begin` /
|
|
37
|
+
* `scarcity-canon:end` sentinels below at
|
|
38
|
+
* `micdrop-frontend/src/lib/foundry/settings-tickets/scarcity.ts` (it cannot
|
|
39
|
+
* import this package at runtime — importing the package entry injects the
|
|
40
|
+
* widget's stylesheet, defines its custom element and auto-mounts).
|
|
41
|
+
*
|
|
42
|
+
* This file is therefore listed in `package.json#files`: the published tarball
|
|
43
|
+
* carries the canon's SOURCE purely so the admin app can read it back out of
|
|
44
|
+
* `node_modules` and diff it.
|
|
45
|
+
* `micdrop-frontend/.../__tests__/scarcity.canon-drift.test.ts` extracts the
|
|
46
|
+
* sentinel block from both files, strips comments, and fails on any difference
|
|
47
|
+
* — numbers OR condition. Nothing between those sentinels may diverge; move a
|
|
48
|
+
* threshold or a comparison on EITHER side and the admin suite goes red,
|
|
49
|
+
* because an operator's low-stock strip that contradicts the buyer's badge is
|
|
50
|
+
* the exact defect MIC-3808 was filed about.
|
|
51
|
+
*
|
|
52
|
+
* Keep the sentinel block free of anything the admin app cannot compile: no
|
|
53
|
+
* imports, no `$lib` paths, no Svelte. Narrative belongs in the comments, which
|
|
54
|
+
* the drift check strips and which the two files are free to word differently.
|
|
55
|
+
*
|
|
56
|
+
* `SCARCITY_THRESHOLDS` and `computeScarcityLevel` are also re-exported from
|
|
57
|
+
* the package entry (`src/lib/VenueCalendar.js`) and hand-declared in
|
|
58
|
+
* `src/lib/types/index.d.ts`.
|
|
59
|
+
*/
|
|
60
|
+
/* --- scarcity-canon:begin --- (mirrored verbatim in micdrop-frontend; see header) */
|
|
61
|
+
export const SCARCITY_THRESHOLDS = {
|
|
62
|
+
/** Base percent remaining at or below which a ticket is "urgent" (exact count). */
|
|
63
|
+
URGENT_PCT: 10,
|
|
64
|
+
/** Base percent remaining at or below which a ticket is "low" (vague badge). */
|
|
65
|
+
LOW_PCT: 20,
|
|
66
|
+
/** Absolute cap on exact-count copy; above this we fall back to vague badge. */
|
|
67
|
+
MAX_EXACT_COUNT: 20,
|
|
68
|
+
/** Always allow a scarcity claim at or below this many remaining (small venues). */
|
|
69
|
+
ABSOLUTE_FLOOR: 5,
|
|
70
|
+
/** Never claim scarcity above this many remaining, even at a low percent (large venues). */
|
|
71
|
+
ABSOLUTE_CAP: 30,
|
|
72
|
+
} as const;
|
|
73
|
+
|
|
74
|
+
export type ScarcityLevel = 'sold-out' | 'urgent' | 'low' | 'plenty';
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* MIC-3808 — may a pool that has sold NOTHING yet make a scarcity claim?
|
|
78
|
+
*
|
|
79
|
+
* ABSOLUTE_FLOOR = 5 has a consequence nobody ever ruled on. The floor lifts
|
|
80
|
+
* the urgent threshold to `5/total%`, so for a pool of five that threshold is
|
|
81
|
+
* 100% — and 100% remaining is what a ticket looks like the instant it goes on
|
|
82
|
+
* sale. A five-seat ticket therefore reads "Only 5 left" before a single seat
|
|
83
|
+
* has moved, and so does every smaller pool:
|
|
84
|
+
*
|
|
85
|
+
* 1 of 1 .. 5 of 5 -> urgent ("Only N left" from minute one)
|
|
86
|
+
* 6 of 6 and larger -> plenty (no claim until seats actually sell)
|
|
87
|
+
*
|
|
88
|
+
* That cliff at six is arithmetic, not a decision. It may well be right — five
|
|
89
|
+
* seats IS nearly nothing — but "scarce because it was never big" and "scarce
|
|
90
|
+
* because it is selling" are different claims in the same words, and only one
|
|
91
|
+
* of them is urgency.
|
|
92
|
+
*
|
|
93
|
+
* So the behaviour is named rather than emergent, and `true` is exactly what
|
|
94
|
+
* production does today. Peet's ruling is this one boolean:
|
|
95
|
+
*
|
|
96
|
+
* true (today) - a full-stock pool of <= ABSOLUTE_FLOOR claims immediately.
|
|
97
|
+
* false - no claim until at least one seat has sold; the ladder is
|
|
98
|
+
* unchanged from the first sale onward, so a 5-seat ticket
|
|
99
|
+
* still reads "Only 4 left" the moment one goes.
|
|
100
|
+
*
|
|
101
|
+
* Flipping it moves the buyer's badge and the operator's low-stock strip
|
|
102
|
+
* together, because the drift check forbids the two copies from disagreeing.
|
|
103
|
+
*
|
|
104
|
+
* Typed as a widened `boolean` on purpose: `as const` would narrow the field to
|
|
105
|
+
* the literal `true` and let a type-aware lint delete the branch below as
|
|
106
|
+
* statically dead, which would make the ruling a code change rather than a
|
|
107
|
+
* value change.
|
|
108
|
+
*/
|
|
109
|
+
export interface ScarcityPolicy {
|
|
110
|
+
readonly CLAIM_ON_UNTOUCHED_POOL: boolean;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export const SCARCITY_POLICY: ScarcityPolicy = {
|
|
114
|
+
CLAIM_ON_UNTOUCHED_POOL: true,
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Bend a base percent threshold toward the absolute floor/cap for this pool
|
|
119
|
+
* size. Returns the percent-remaining at or below which the base tier applies.
|
|
120
|
+
*
|
|
121
|
+
* effective(base) = max( min(base, CAP/total%), FLOOR/total% )
|
|
122
|
+
*
|
|
123
|
+
* The cap lowers the threshold on large venues (30 tickets is a small slice of
|
|
124
|
+
* 1,000, so the % must be tiny); the floor raises it on small venues (5 tickets
|
|
125
|
+
* is half of 10, so a high % still counts). `total` is assumed > 0 (callers
|
|
126
|
+
* guard total <= 0 before calling).
|
|
127
|
+
*/
|
|
128
|
+
function effectiveThreshold(basePct: number, total: number): number {
|
|
129
|
+
const { ABSOLUTE_FLOOR, ABSOLUTE_CAP } = SCARCITY_THRESHOLDS;
|
|
130
|
+
const capPct = (ABSOLUTE_CAP / total) * 100;
|
|
131
|
+
const floorPct = (ABSOLUTE_FLOOR / total) * 100;
|
|
132
|
+
return Math.max(Math.min(basePct, capPct), floorPct);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Compute the scarcity level for a (remaining, total) pair.
|
|
137
|
+
*
|
|
138
|
+
* Returns 'plenty' when total <= 0 (we can't compute a percentage; assume
|
|
139
|
+
* unlimited / unknown capacity, no scarcity claim). Returns 'sold-out'
|
|
140
|
+
* defensively for negative remaining.
|
|
141
|
+
*
|
|
142
|
+
* UNKNOWN CAPACITY IS CHECKED FIRST, and that ordering is the fix for MIC-2532's
|
|
143
|
+
* second half. A hidden-only show ships NO public tickets, so the public payload
|
|
144
|
+
* honestly reports remainingSeats 0 of totalSeats 0 — there is nothing public to
|
|
145
|
+
* count, which is not the same as nothing left to sell. Read remaining-first,
|
|
146
|
+
* 0-of-0 came out 'sold-out' and the event header printed a red "Sold out" badge
|
|
147
|
+
* beside a working Get tickets button: the two-different-stories-at-once the
|
|
148
|
+
* ticket was filed about, still there after the wire flag fixed the button.
|
|
149
|
+
*
|
|
150
|
+
* A genuinely sold-out show is unaffected — it has a real capacity, so
|
|
151
|
+
* total > 0 and remaining <= 0 still lands on 'sold-out'.
|
|
152
|
+
*
|
|
153
|
+
* `policy` defaults to the module's SCARCITY_POLICY and exists so the
|
|
154
|
+
* MIC-3808 branch is reachable from a test without writing to a readonly
|
|
155
|
+
* field. Production must never pass it: a second policy at a call site is a
|
|
156
|
+
* second ladder, which is the thing this module exists to prevent.
|
|
157
|
+
*/
|
|
158
|
+
export function computeScarcityLevel(
|
|
159
|
+
remaining: number,
|
|
160
|
+
total: number,
|
|
161
|
+
policy: ScarcityPolicy = SCARCITY_POLICY
|
|
162
|
+
): ScarcityLevel {
|
|
163
|
+
if (total <= 0) return 'plenty';
|
|
164
|
+
if (remaining <= 0) return 'sold-out';
|
|
165
|
+
|
|
166
|
+
// MIC-3808. `remaining >= total` and not `===`: a stale remaining count can
|
|
167
|
+
// be reported ABOVE the pool it belongs to, and a pool that over-reports has
|
|
168
|
+
// sold even less than nothing — it must not slip past an equality test into
|
|
169
|
+
// a claim the ruling forbids.
|
|
170
|
+
if (!policy.CLAIM_ON_UNTOUCHED_POOL && remaining >= total) return 'plenty';
|
|
171
|
+
|
|
172
|
+
const pct = (remaining / total) * 100;
|
|
173
|
+
const { URGENT_PCT, LOW_PCT, MAX_EXACT_COUNT } = SCARCITY_THRESHOLDS;
|
|
174
|
+
const urgentThreshold = effectiveThreshold(URGENT_PCT, total);
|
|
175
|
+
const lowThreshold = effectiveThreshold(LOW_PCT, total);
|
|
176
|
+
|
|
177
|
+
if (pct <= urgentThreshold && remaining <= MAX_EXACT_COUNT) return 'urgent';
|
|
178
|
+
if (pct <= lowThreshold) return 'low';
|
|
179
|
+
return 'plenty';
|
|
180
|
+
}
|
|
181
|
+
/* --- scarcity-canon:end --- */
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|