@getmicdrop/venue-calendar 4.3.63 → 4.3.65

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.
Files changed (65) hide show
  1. package/dist/{AddOnCard-DAk2utke.js → AddOnCard-CmK4bDY6.js} +4 -4
  2. package/dist/{ArrowLeftIcon-B2NmxWA9.js → ArrowLeftIcon-DcG0YVCM.js} +1 -1
  3. package/dist/{BlockPlaceholder-IligENph.js → BlockPlaceholder-BRzS72r6.js} +1 -1
  4. package/dist/{Calendar-DM7xqU81.js → Calendar-BOweKeiV.js} +2 -2
  5. package/dist/{CalendarFoundryView-uG2d_J_1.js → CalendarFoundryView-CcOPZ_Nu.js} +90 -43
  6. package/dist/{CalendarIcon-3jHpw2aE.js → CalendarIcon-DuxP0sxw.js} +1 -1
  7. package/dist/{CartView-C3yY3Umt.js → CartView-YNf1axYi.js} +33 -33
  8. package/dist/{Checkout-AGHr3pGm.js → Checkout-9Qi44T1Y.js} +11 -11
  9. package/dist/{Checkout-D5xQo_-j.js → Checkout-ipuGLBT2.js} +10 -10
  10. package/dist/{CheckoutTimer-Btjo7PEg.js → CheckoutTimer-DTufmYO_.js} +1 -1
  11. package/dist/{ChevronDownIcon-Dp1MGPEN.js → ChevronDownIcon-ClOg3b7E.js} +1 -1
  12. package/dist/{CloseIcon-DKrZf_7a.js → CloseIcon-CWQWjUzZ.js} +1 -1
  13. package/dist/{CollectionView-CbpcbtPQ.js → CollectionView-PRegJwQb.js} +4 -4
  14. package/dist/{Event-Ct04hk5X.js → Event--MH8pGju.js} +63 -63
  15. package/dist/{EventPage-EMPylUho.js → EventPage-BGYvfkiB.js} +9 -9
  16. package/dist/{Input-CLSPTEMv.js → Input-BT3hf13K.js} +1 -1
  17. package/dist/{MailIcon-COatZ9tD.js → MailIcon-ivrn0IXt.js} +1 -1
  18. package/dist/{PasswordInput-BxuPPAe8.js → PasswordInput-CYZVT6fp.js} +1 -1
  19. package/dist/{RecommendationsRail-DGvzlf9S.js → RecommendationsRail-CPr__oRL.js} +11 -11
  20. package/dist/{ScarcityBadge-DU6RgcCz.js → ScarcityBadge-CDDlvbDh.js} +3 -3
  21. package/dist/{Select-CjELEaSF.js → Select-DWFgeD9R.js} +96 -83
  22. package/dist/{SeriesPage-BJf6QYfI.js → SeriesPage-DhmD95h9.js} +2 -2
  23. package/dist/{Success-Cbpn0t0r.js → Success-Cgrr7FqT.js} +10 -10
  24. package/dist/{TagIcon-CfyFlUh_.js → TagIcon-CFtOYvWM.js} +1 -1
  25. package/dist/{WarningCircleIcon-DsYifP07.js → WarningCircleIcon-Nmw1CPAT.js} +1 -1
  26. package/dist/{WarningTriIcon-C2oaxNjx.js → WarningTriIcon-flvVXH1d.js} +1 -1
  27. package/dist/{api-BX4Imu-R.js → api-Vyamuo2p.js} +1 -1
  28. package/dist/{copyToClipboard-CIu22_xq.js → copyToClipboard-BmB4QyKB.js} +1 -1
  29. package/dist/{createErrorGate.svelte-Z3I2WfRr.js → createErrorGate.svelte-CDXAf5Lm.js} +1 -1
  30. package/dist/{scarcity-ClhXuC6t.js → datetime-ClxISJBt.js} +58 -57
  31. package/dist/{event-transform-CG6CJlbD.js → event-transform-L2ZTddgP.js} +19 -19
  32. package/dist/{href-Dz1CMH4c.js → href-DKLRNO3s.js} +1 -1
  33. package/dist/{i18n-DLFy3vxe.js → i18n-DFlVZfRe.js} +9 -9
  34. package/dist/{to-event-card-props-DObG6xUq.js → to-event-card-props-CmDQ3iGb.js} +34 -35
  35. package/dist/types/index.d.ts +602 -597
  36. package/dist/{utils-C15ZjLiK.js → utils-C_wZYlA9.js} +1 -1
  37. package/dist/venue-calendar.es.js +31 -31
  38. package/dist/venue-calendar.iife.js +22 -22
  39. package/dist/venue-calendar.umd.js +50 -50
  40. package/package.json +3 -2
  41. package/src/lib/constants/scarcity.ts +181 -0
  42. /package/dist/locales/{4.3.63 → 4.3.65}/flow/de.js +0 -0
  43. /package/dist/locales/{4.3.63 → 4.3.65}/flow/es.js +0 -0
  44. /package/dist/locales/{4.3.63 → 4.3.65}/flow/fr.js +0 -0
  45. /package/dist/locales/{4.3.63 → 4.3.65}/flow/id.js +0 -0
  46. /package/dist/locales/{4.3.63 → 4.3.65}/flow/it.js +0 -0
  47. /package/dist/locales/{4.3.63 → 4.3.65}/flow/ja.js +0 -0
  48. /package/dist/locales/{4.3.63 → 4.3.65}/flow/ko.js +0 -0
  49. /package/dist/locales/{4.3.63 → 4.3.65}/flow/nl.js +0 -0
  50. /package/dist/locales/{4.3.63 → 4.3.65}/flow/pl.js +0 -0
  51. /package/dist/locales/{4.3.63 → 4.3.65}/flow/pt-br.js +0 -0
  52. /package/dist/locales/{4.3.63 → 4.3.65}/flow/tr.js +0 -0
  53. /package/dist/locales/{4.3.63 → 4.3.65}/flow/zh.js +0 -0
  54. /package/dist/locales/{4.3.63 → 4.3.65}/main/de.js +0 -0
  55. /package/dist/locales/{4.3.63 → 4.3.65}/main/es.js +0 -0
  56. /package/dist/locales/{4.3.63 → 4.3.65}/main/fr.js +0 -0
  57. /package/dist/locales/{4.3.63 → 4.3.65}/main/id.js +0 -0
  58. /package/dist/locales/{4.3.63 → 4.3.65}/main/it.js +0 -0
  59. /package/dist/locales/{4.3.63 → 4.3.65}/main/ja.js +0 -0
  60. /package/dist/locales/{4.3.63 → 4.3.65}/main/ko.js +0 -0
  61. /package/dist/locales/{4.3.63 → 4.3.65}/main/nl.js +0 -0
  62. /package/dist/locales/{4.3.63 → 4.3.65}/main/pl.js +0 -0
  63. /package/dist/locales/{4.3.63 → 4.3.65}/main/pt-br.js +0 -0
  64. /package/dist/locales/{4.3.63 → 4.3.65}/main/tr.js +0 -0
  65. /package/dist/locales/{4.3.63 → 4.3.65}/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.63",
4
+ "version": "4.3.65",
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.",
@@ -154,7 +155,7 @@
154
155
  "_devDependencies_pin_comment": "Three EXACT pins in here are load-bearing and must not be re-caretted by a dependency bot. (1) `@getmicdrop/svelte-components` — the ES/UMD/IIFE builds do NOT externalize SC, so it is COMPILED INTO every shipped artifact. RESTORED TO AN EXACT PIN 2026-08-23, having drifted back to a caret at some point between 6.24.311 and 6.24.316 — exactly the re-caretting this note warns about, which is worth knowing recurs. Under a caretted range like `^6.24.311` the SC version a venue's browser executes is whatever the range floats to on the publish machine at publish time, which is not recorded anywhere and is not reproducible after the fact; the `overrides` block below cannot help (it only ever covered esbuild, and consumers ignore a dependency's overrides entirely). Pinned exact so the artifact and the manifest agree, and vite.config.lib.js stamps the resolved version into the bundle as __VC_SC_VERSION__ so a venue's devtools can answer 'which SC is this?'. (2) `svelte` — the compiler whose private internal/client calls the pre-compiled dist binds to. `^5.56.9` let the compiler move under a peer range that stayed still; see _peerDependencies_comment. (3) `@stripe/stripe-js` in dependencies — see its own note there.",
155
156
  "devDependencies": {
156
157
  "@eslint/js": "^10.0.1",
157
- "@getmicdrop/svelte-components": "6.24.382",
158
+ "@getmicdrop/svelte-components": "6.24.383",
158
159
  "@playwright/test": "^1.60.0",
159
160
  "@size-limit/file": "^13.0.3",
160
161
  "@sveltejs/adapter-auto": "^7.0.1",
@@ -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