ReviewOS

also looking at this

loghqorg/loghq

Promote main to production: signed-out chrome and the subscriber tables

#17
Merged glennmichael123 wants to merge main into production
54 files +4252 -1353

Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.

config/crosswind.tsmodified+19-38
Changes to config/crosswind.ts
@@ -41,9 +41,10 @@ import type { CrosswindConfig, Theme } from '@cwcss/crosswind'
4141 * of toCSS(). The stale note mattered, because it is why this
4242 * app's tokens went to public/app-chrome.css instead.
4343 *
44 * One constraint is real: a single getCSS() returning several
45 * rules silently loses some of them. One entry per rule; see
46 * topLevelRules below.
44 * One constraint is real, and it is quoting rather than
45 * structure: a double-quoted font family in a preflight makes
46 * the engine substitute its own stack and swallow the rules
47 * after it. Several rules in one getCSS() are fine.
4748 *
4849 * Anything not on the surviving list is decoration. This file used to carry
4950 * `content`, `minify`, and `preflight: true` — and that last one is not even a
@@ -71,6 +72,20 @@ import type { CrosswindConfig, Theme } from '@cwcss/crosswind'
7172 * a preflight lands in @layer cw-base, so every unlayered base would outrank
7273 * its own layered active state and the selected range would stop painting.
7374 *
75 * One entry, not one per rule. An earlier version split it, on the strength of
76 * a measurement taken against @cwcss/crosswind -- which this file imports its
77 * TYPES from and which does not render this app. stx loads @stacksjs/ts-css on
78 * every path, and that engine keeps every rule in a multi-rule getCSS(): all 35
79 * here, and the 80 bughq passes through its own pageCss(). The split was
80 * guarding against a bug in an engine that is not in the loop.
81 *
82 * There IS a real preflight hazard, and it is about quoting rather than
83 * structure: a DOUBLE-quoted font family corrupts the sheet. On ts-css,
84 * `font-family:...,"Segoe UI",sans-serif` came back as the engine's own default
85 * stack with everything up to the next `transform` swallowed, taking a whole
86 * h1-h3 rule and half a @keyframes block with it. This stylesheet quotes no
87 * families, so it is unaffected; commshq's tokens use single quotes for it.
88 *
7489 * Moving it into the layer was checked for cascade damage rather than assumed.
7590 * The picker's markup carries component classes only -- no utility co-occurs
7691 * with .range-tool, .drp-* or .date-popover -- so nothing of equal specificity
@@ -208,42 +223,8 @@ summary:focus-visible {
208223}
209224`
210225
211/**
212 * Split a stylesheet into its top-level rules, one per preflight entry.
213 *
214 * Not organisational taste: a getCSS() that returns several rules loses some of
215 * them. Measured on commshq, where ten rules in one string came back with h1-h3
216 * and the first half of a @keyframes block gone and its `transform:...}to{...`
217 * spliced into the preceding rule. Depth is tracked so an @media block stays a
218 * single rule, which means a comment in this stylesheet must not contain a
219 * brace.
220 */
221function topLevelRules(css: string): string[] {
222 const rules: string[] = []
223 let depth = 0
224 let current = ''
225 for (const ch of css) {
226 current += ch
227 if (ch === '{') {
228 depth++
229 }
230 else if (ch === '}') {
231 depth--
232 if (depth === 0) {
233 rules.push(current.trim())
234 current = ''
235 }
236 }
237 }
238 const tail = current.trim()
239 if (tail)
240 rules.push(tail)
241 // A trailing comment with no rule after it is not a rule.
242 return rules.filter(rule => rule.length > 0 && !/^\/\*[\s\S]*\*\/$/.test(rule))
243}
244
245226export default {
246 preflights: topLevelRules(DATE_RANGE_PICKER_CSS).map(css => ({ getCSS: () => css })),
227 preflights: [{ getCSS: () => DATE_RANGE_PICKER_CSS }],
247228 theme: {
248229 extend: {
249230 borderRadius: {
tests/unit/date-range-picker-css.test.tsmodified+50-21
Changes to tests/unit/date-range-picker-css.test.ts
@@ -28,7 +28,10 @@ const appPreflights = (theme as any).preflights as { getCSS: () => string }[]
2828const flat = sheet.replace(/\/\*[\s\S]*?\*\//g, '').replace(/\s+/g, '').replace(/;\}/g, '}')
2929
3030function contains(rule: string): boolean {
31 return flat.includes(rule.replace(/\s+/g, '').replace(/;\}/g, '}'))
31 // Same normalisation as `flat`, comments included: the splitter hands a
32 // rule its preceding comment, and the sheet has none.
33 const needle = rule.replace(/\/\*[\s\S]*?\*\//g, '').replace(/\s+/g, '').replace(/;\}/g, '}')
34 return needle.length > 0 && flat.includes(needle)
3235}
3336
3437describe('date range picker CSS reaches the generated sheet', () => {
@@ -37,8 +40,8 @@ describe('date range picker CSS reaches the generated sheet', () => {
3740 // the dev-server merge dropped preflights outright (stacksjs/stx#1867).
3841 // If that ever regresses, every rule in this file goes with it.
3942 expect(merged.includePreflight).toBe(true)
40 // The app's own entries, one per rule, plus the engine's built-in reset.
41 expect(appPreflights.length).toBeGreaterThan(30)
43 expect(appPreflights.length).toBe(1)
44 // Plus the engine's own reset, which is a preflight too.
4245 expect(merged.config.preflights.length).toBeGreaterThan(appPreflights.length)
4346 })
4447
@@ -63,33 +66,59 @@ describe('date range picker CSS reaches the generated sheet', () => {
6366 expect(contains('.drp-cal.is-left .drp-nav.is-hidden { visibility: visible; }')).toBe(true)
6467 })
6568
66 test('each preflight entry holds exactly one top-level rule', () => {
69 test('no preflight entry quotes a font family with double quotes', () => {
6770 /*
68 * The invariant that keeps the rest of this file true. A getCSS() returning
69 * several rules silently loses some of them -- measured on commshq, where
70 * ten in one string came back with two rules gone and a @keyframes body
71 * spliced into the rule before it. Adding a rule to an existing entry is
72 * the easy mistake, so it fails here rather than on the page.
71 * The one real preflight hazard, and it is quoting rather than structure.
72 * A double-quoted family name makes the engine substitute its own default
73 * stack AND swallow what follows: on ts-css,
74 * `font-family:...,"Segoe UI",sans-serif` came back as
75 * `font-family: ui-sans-serif, system-ui, sans-serif` with everything up to
76 * the next `transform` gone, which ate a whole h1-h3 rule and the opening
77 * half of a @keyframes block. Single quotes are clean and mean the same
78 * thing in CSS.
7379 *
74 * This app's entries only: the engine contributes its own preflight, and
75 * the Tailwind reset is one entry holding 38 rules.
80 * This stylesheet quotes no families today, so the check is here to keep it
81 * that way -- the failure is silent and looks like a font that never
82 * applied.
83 *
84 * An earlier version of this file asserted one rule per entry instead.
85 * That invariant came from measuring @cwcss/crosswind, which this app
86 * imports types from and which does not render it; stx loads
87 * @stacksjs/ts-css, and that engine keeps every rule in a multi-rule
88 * getCSS().
7689 */
7790 for (const preflight of appPreflights) {
7891 const css = preflight.getCSS()
79 let depth = 0
80 let closes = 0
81 for (const ch of css) {
82 if (ch === '{')
83 depth++
84 else if (ch === '}') {
85 depth--
86 if (depth === 0)
87 closes++
92 const offenders = [...css.matchAll(/font-family\s*:[^;}]*"/g)].map(m => m[0])
93 expect(offenders).toEqual([])
94 }
95 })
96
97 test('every rule in the stylesheet survives the trip', () => {
98 // The whole point: it is plain CSS in a config file now, reached through a
99 // merge, and nothing else would notice if a rule stopped arriving.
100 const rules: string[] = []
101 let depth = 0
102 let current = ''
103 for (const ch of appPreflights[0].getCSS()) {
104 current += ch
105 if (ch === '{') {
106 depth++
107 }
108 else if (ch === '}') {
109 depth--
110 if (depth === 0) {
111 rules.push(current.trim())
112 current = ''
88113 }
89114 }
90 expect(`${css.slice(0, 60)} -> ${closes}`).toBe(`${css.slice(0, 60)} -> 1`)
91115 }
116 const declared = rules.filter(rule => rule && !/^\/\*[\s\S]*\*\/$/.test(rule))
117 expect(declared.length).toBeGreaterThan(30)
118 for (const rule of declared)
119 expect(`${rule.slice(0, 48)}: ${contains(rule)}`).toBe(`${rule.slice(0, 48)}: true`)
92120 })
121
93122})
94123
95124describe('stx-standards rule 3', () => {