@altertable/data-app 0.63.0 → 0.64.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/CONTRIBUTING.md CHANGED
@@ -30,6 +30,10 @@ Use `@/src/...` imports in repository source and `@altertable/data-app/<entry>`
30
30
  in consumer tests and examples. Run `bun run lint:fix` and `bun run format` for
31
31
  style fixes.
32
32
 
33
+ Compose the gallery from provided components, including layouts. Let primitives
34
+ own sizing, spacing, and responsive behavior. Report missing primitives and keep
35
+ fallbacks minimal.
36
+
33
37
  ## Verify changes
34
38
 
35
39
  Tests should tell the package's public usage stories: define an operation,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  DataSourceError2
3
- } from "./contract-wd8qe3mt.js";
3
+ } from "./contract-yxbjea23.js";
4
4
  import {
5
5
  BRIDGE,
6
6
  PARENT_PARAM,
@@ -1,7 +1,3 @@
1
- import {
2
- invariant
3
- } from "./contract-8q35dcyh.js";
4
-
5
1
  // src/core/appearance.ts
6
2
  var defaults = {
7
3
  theme: "light",
@@ -34,38 +30,42 @@ function font(value) {
34
30
  return typeof value === "string" && value.length > 0 && value.length <= 120 && !/[;{}()]/.test(value);
35
31
  }
36
32
  function parseAppearance2(value) {
37
- if (value === undefined)
38
- return {
39
- ...defaults,
40
- chartColors: [...defaults.chartColors],
41
- typography: { ...defaults.typography }
42
- };
43
- invariant(record(value) && Object.keys(value).every((key) => [
44
- "theme",
45
- "baseColor",
46
- "accentColor",
47
- "darkAccentColor",
48
- "chartColors",
49
- "density",
50
- "cornerRadius",
51
- "elevation",
52
- "typography"
53
- ].includes(key)), "Invalid appearance settings.");
54
- const typography = value.typography;
55
- const validFields = (value.theme === undefined || oneOf(value.theme, ["light", "dark", "system"])) && (value.baseColor === undefined || oneOf(value.baseColor, ["neutral", "slate", "warm"])) && (value.accentColor === undefined || color(value.accentColor)) && (value.darkAccentColor === undefined || color(value.darkAccentColor)) && (value.chartColors === undefined || Array.isArray(value.chartColors) && value.chartColors.length >= 1 && value.chartColors.length <= 8 && value.chartColors.every(color)) && (value.density === undefined || oneOf(value.density, ["compact", "comfortable", "spacious"])) && (value.cornerRadius === undefined || oneOf(value.cornerRadius, ["none", "small", "medium", "large"])) && (value.elevation === undefined || oneOf(value.elevation, ["flat", "subtle", "raised"])) && (typography === undefined || record(typography) && Object.keys(typography).every((key) => key === "body" || key === "heading") && (typography.body === undefined || font(typography.body)) && (typography.heading === undefined || font(typography.heading)));
56
- invariant(validFields, "Invalid appearance settings.");
33
+ if (!record(value))
34
+ return normalizeAppearance2();
35
+ const typography = record(value.typography) ? value.typography : {};
36
+ return normalizeAppearance2({
37
+ theme: oneOf(value.theme, ["light", "dark", "system"]) ? value.theme : undefined,
38
+ baseColor: oneOf(value.baseColor, ["neutral", "slate", "warm"]) ? value.baseColor : undefined,
39
+ accentColor: color(value.accentColor) ? value.accentColor : undefined,
40
+ darkAccentColor: color(value.darkAccentColor) ? value.darkAccentColor : undefined,
41
+ chartColors: Array.isArray(value.chartColors) && value.chartColors.length >= 1 && value.chartColors.length <= 8 && value.chartColors.every(color) ? value.chartColors : undefined,
42
+ density: oneOf(value.density, ["compact", "comfortable", "spacious"]) ? value.density : undefined,
43
+ cornerRadius: oneOf(value.cornerRadius, [
44
+ "none",
45
+ "small",
46
+ "medium",
47
+ "large"
48
+ ]) ? value.cornerRadius : undefined,
49
+ elevation: oneOf(value.elevation, ["flat", "subtle", "raised"]) ? value.elevation : undefined,
50
+ typography: {
51
+ body: font(typography.body) ? typography.body : undefined,
52
+ heading: font(typography.heading) ? typography.heading : undefined
53
+ }
54
+ });
55
+ }
56
+ function normalizeAppearance2(value = {}) {
57
57
  return {
58
58
  theme: value.theme ?? defaults.theme,
59
59
  baseColor: value.baseColor ?? defaults.baseColor,
60
60
  accentColor: value.accentColor ?? defaults.accentColor,
61
- darkAccentColor: value.darkAccentColor ?? (value.accentColor === undefined ? defaults.darkAccentColor : undefined),
62
- chartColors: value.chartColors ?? defaults.chartColors,
63
61
  density: value.density ?? defaults.density,
64
62
  cornerRadius: value.cornerRadius ?? defaults.cornerRadius,
65
63
  elevation: value.elevation ?? defaults.elevation,
64
+ darkAccentColor: value.darkAccentColor ?? (value.accentColor === undefined ? defaults.darkAccentColor : undefined),
65
+ chartColors: [...value.chartColors ?? defaults.chartColors],
66
66
  typography: {
67
- body: record(typography) && typeof typography.body === "string" ? typography.body : defaults.typography.body,
68
- heading: record(typography) && typeof typography.heading === "string" ? typography.heading : defaults.typography.heading
67
+ body: value.typography?.body ?? defaults.typography.body,
68
+ heading: value.typography?.heading ?? defaults.typography.heading
69
69
  }
70
70
  };
71
71
  }
@@ -98,7 +98,7 @@ function fontStack(family) {
98
98
  return family === "system" ? "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, ui-sans-serif, sans-serif" : `${JSON.stringify(family)}, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, ui-sans-serif, sans-serif`;
99
99
  }
100
100
  function applyAppearance2(value) {
101
- const settings = parseAppearance2(value);
101
+ const settings = normalizeAppearance2(value);
102
102
  const root = document.documentElement;
103
103
  const preference = window.matchMedia("(prefers-color-scheme: dark)");
104
104
  const [xs, sm, md, lg, xl] = spaces[settings.density];
@@ -183,6 +183,6 @@ function createThemeController2(initialTheme = "light") {
183
183
  };
184
184
  }
185
185
 
186
- export { parseAppearance2, applyAppearance2, createThemeController2 };
186
+ export { parseAppearance2, normalizeAppearance2, applyAppearance2, createThemeController2 };
187
187
 
188
- //# debugId=90CD7598768B570C64756E2164756E21
188
+ //# debugId=E968EAFDC43C9C1764756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/core/appearance.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * Brand settings; parsing is portable, while theme application requires the browser.\n * @module @altertable/data-app/appearance\n */\nexport type Theme = 'light' | 'dark';\nexport type ThemePreference = Theme | 'system';\n\nexport type AppearanceSettings = {\n theme: ThemePreference;\n baseColor: 'neutral' | 'slate' | 'warm';\n accentColor: string;\n darkAccentColor?: string;\n chartColors: string[];\n density: 'compact' | 'comfortable' | 'spacious';\n cornerRadius: 'none' | 'small' | 'medium' | 'large';\n elevation: 'flat' | 'subtle' | 'raised';\n typography: { body: string; heading: string };\n};\n\n/** App-authored overrides; omitted fields use the package defaults. */\nexport type AppearanceOptions = Partial<\n Omit<AppearanceSettings, 'typography'>\n> & { typography?: Partial<AppearanceSettings['typography']> };\n\nexport type ThemeController = {\n getTheme: () => ThemePreference;\n setTheme: (theme: ThemePreference) => void;\n subscribe: (listener: () => void) => () => void;\n};\n\nconst defaults: AppearanceSettings = {\n theme: 'light',\n baseColor: 'neutral',\n accentColor: '#405d47',\n darkAccentColor: '#a6c4ad',\n chartColors: [\n '#285fc0',\n '#a95319',\n '#147862',\n '#7243aa',\n '#aa3958',\n '#475569',\n ],\n density: 'comfortable',\n cornerRadius: 'medium',\n elevation: 'subtle',\n typography: { body: 'system', heading: 'system' },\n};\n\nfunction record(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction oneOf<T extends string>(\n value: unknown,\n options: readonly T[]\n): value is T {\n return typeof value === 'string' && options.includes(value as T);\n}\n\nfunction color(value: unknown): value is string {\n return typeof value === 'string' && /^#[0-9a-fA-F]{6}$/.test(value);\n}\n\nfunction font(value: unknown): value is string {\n return (\n typeof value === 'string' &&\n value.length > 0 &&\n value.length <= 120 &&\n !/[;{}()]/.test(value)\n );\n}\n\n/** Preserve supported external settings and use defaults for invalid fields. */\nexport function parseAppearance(value: unknown): AppearanceSettings {\n if (!record(value)) return normalizeAppearance();\n const typography = record(value.typography) ? value.typography : {};\n\n return normalizeAppearance({\n theme: oneOf(value.theme, ['light', 'dark', 'system'])\n ? value.theme\n : undefined,\n baseColor: oneOf(value.baseColor, ['neutral', 'slate', 'warm'])\n ? value.baseColor\n : undefined,\n accentColor: color(value.accentColor) ? value.accentColor : undefined,\n darkAccentColor: color(value.darkAccentColor)\n ? value.darkAccentColor\n : undefined,\n chartColors:\n Array.isArray(value.chartColors) &&\n value.chartColors.length >= 1 &&\n value.chartColors.length <= 8 &&\n value.chartColors.every(color)\n ? value.chartColors\n : undefined,\n density: oneOf(value.density, ['compact', 'comfortable', 'spacious'])\n ? value.density\n : undefined,\n cornerRadius: oneOf(value.cornerRadius, [\n 'none',\n 'small',\n 'medium',\n 'large',\n ])\n ? value.cornerRadius\n : undefined,\n elevation: oneOf(value.elevation, ['flat', 'subtle', 'raised'])\n ? value.elevation\n : undefined,\n typography: {\n body: font(typography.body) ? typography.body : undefined,\n heading: font(typography.heading) ? typography.heading : undefined,\n },\n });\n}\n\n/** Resolve typed app settings without repeating authoring checks at runtime. */\nexport function normalizeAppearance(\n value: AppearanceOptions = {}\n): AppearanceSettings {\n return {\n theme: value.theme ?? defaults.theme,\n baseColor: value.baseColor ?? defaults.baseColor,\n accentColor: value.accentColor ?? defaults.accentColor,\n density: value.density ?? defaults.density,\n cornerRadius: value.cornerRadius ?? defaults.cornerRadius,\n elevation: value.elevation ?? defaults.elevation,\n darkAccentColor:\n value.darkAccentColor ??\n (value.accentColor === undefined ? defaults.darkAccentColor : undefined),\n chartColors: [...(value.chartColors ?? defaults.chartColors)],\n typography: {\n body: value.typography?.body ?? defaults.typography.body,\n heading: value.typography?.heading ?? defaults.typography.heading,\n },\n };\n}\n\nconst palettes = {\n neutral: {\n light: ['#ffffff', '#ffffff', '#f6f7f7', '#202124', '#687078', '#e3e6e8'],\n dark: ['#151719', '#202326', '#292d31', '#f2f3f4', '#aeb5bc', '#3b4248'],\n },\n slate: {\n light: ['#f8fafc', '#ffffff', '#f1f5f9', '#17212f', '#607083', '#dce3ea'],\n dark: ['#111820', '#1b2530', '#263340', '#f0f4f8', '#a8b5c3', '#3a4857'],\n },\n warm: {\n light: ['#fbfaf8', '#ffffff', '#f5f2ed', '#292723', '#736e66', '#e8e2da'],\n dark: ['#1b1916', '#25221e', '#302c27', '#f5f1eb', '#bcb3a8', '#494239'],\n },\n} as const;\n\nconst spaces = {\n compact: ['4px', '8px', '12px', '18px', '24px'],\n comfortable: ['5px', '10px', '16px', '24px', '32px'],\n spacious: ['6px', '12px', '20px', '30px', '40px'],\n} as const;\n\nconst radii = {\n none: ['0px', '0px', '0px'],\n small: ['4px', '7px', '9px'],\n medium: ['7px', '12px', '16px'],\n large: ['10px', '18px', '24px'],\n} as const;\n\nfunction fontStack(family: string): string {\n return family === 'system'\n ? \"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, ui-sans-serif, sans-serif\"\n : `${JSON.stringify(family)}, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, ui-sans-serif, sans-serif`;\n}\n\n/**\n * Install semantic tokens on the document root, including portaled UI. Returns a cleanup for\n * system-theme listening.\n */\nexport function applyAppearance(value?: AppearanceOptions): () => void {\n const settings = normalizeAppearance(value);\n const root = document.documentElement;\n const preference = window.matchMedia('(prefers-color-scheme: dark)');\n const [xs, sm, md, lg, xl] = spaces[settings.density];\n const [control, surface, overlay] = radii[settings.cornerRadius];\n const tokens: Record<string, string> = {\n '--at-font': fontStack(settings.typography.body),\n '--at-font-heading': fontStack(settings.typography.heading),\n '--at-space-xs': xs,\n '--at-space-sm': sm,\n '--at-space-md': md,\n '--at-space-lg': lg,\n '--at-space-xl': xl,\n '--at-layout-gap': `clamp(${md}, 2.5vw, ${lg})`,\n '--at-radius-control': control,\n '--at-radius-surface': surface,\n '--at-radius-overlay': overlay,\n '--at-shadow-overlay':\n settings.elevation === 'flat'\n ? 'none'\n : settings.elevation === 'raised'\n ? '0 24px 72px rgb(0 0 0 / 24%)'\n : '0 18px 50px rgb(0 0 0 / 16%)',\n '--at-shadow-surface':\n settings.elevation === 'flat'\n ? 'none'\n : settings.elevation === 'raised'\n ? '0 8px 28px rgb(20 28 40 / 10%)'\n : '0 3px 16px rgb(20 28 40 / 5%)',\n };\n\n function applyColors(): void {\n const dark =\n settings.theme === 'dark' ||\n (settings.theme === 'system' && preference.matches);\n const [background, surfaceColor, subtle, text, muted, border] =\n palettes[settings.baseColor][dark ? 'dark' : 'light'];\n const accent = dark\n ? (settings.darkAccentColor ??\n `color-mix(in srgb, ${settings.accentColor} 50%, white)`)\n : settings.accentColor;\n settings.chartColors.forEach((chartColor, index) => {\n tokens[`--at-chart-${index + 1}`] = dark\n ? `color-mix(in srgb, ${chartColor} 60%, white)`\n : chartColor;\n });\n Object.assign(tokens, {\n '--at-background': background,\n '--at-surface': surfaceColor,\n '--at-subtle': subtle,\n '--at-text': text,\n '--at-muted': muted,\n '--at-border': border,\n '--at-accent': accent,\n '--at-focus-color': muted,\n '--at-control-hover-border': `color-mix(in srgb, ${muted} 45%, ${border})`,\n '--at-accent-hover': `color-mix(in srgb, ${accent} 80%, ${dark ? 'white' : 'black'})`,\n '--at-accent-subtle': `color-mix(in srgb, ${accent} ${dark ? 22 : 12}%, ${surfaceColor})`,\n '--at-on-accent': dark ? background : '#ffffff',\n '--at-danger': dark ? '#f97066' : '#b42318',\n '--at-backdrop': dark ? 'rgb(0 0 0 / 55%)' : 'rgb(15 23 30 / 22%)',\n '--at-code-surface': dark ? '#242a31' : '#f4f6f9',\n '--at-code-text': dark ? '#e5e9ef' : '#273242',\n });\n root.style.colorScheme = dark ? 'dark' : 'light';\n for (const [name, token] of Object.entries(tokens))\n root.style.setProperty(name, token);\n }\n applyColors();\n if (settings.theme === 'system')\n preference.addEventListener('change', applyColors);\n\n return () => {\n preference.removeEventListener('change', applyColors);\n };\n}\n\n/** Persist a viewer theme preference. Applying appearance belongs to the UI owner. */\nexport function createThemeController(\n initialTheme: ThemePreference = 'light'\n): ThemeController {\n const storageKey = 'altertable.data-app.theme';\n let theme = initialTheme;\n try {\n const saved = window.localStorage.getItem(storageKey);\n if (oneOf(saved, ['light', 'dark', 'system'])) theme = saved;\n } catch {\n // Storage can be unavailable in private browsing or embedded contexts.\n }\n const listeners = new Set<() => void>();\n\n return {\n getTheme() {\n return theme;\n },\n setTheme(nextTheme) {\n if (nextTheme === theme) return;\n theme = nextTheme;\n try {\n window.localStorage.setItem(storageKey, theme);\n } catch {\n // The selection still applies to this page.\n }\n listeners.forEach(listener => listener());\n },\n subscribe(listener) {\n listeners.add(listener);\n\n return () => listeners.delete(listener);\n },\n };\n}\n"
6
+ ],
7
+ "mappings": "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",
8
+ "debugId": "E968EAFDC43C9C1764756E2164756E21",
9
+ "names": []
10
+ }
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/core/config.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * App identity and display scope shared by browser and server.\n * @module @altertable/data-app/config\n */\nexport type DataAppConfig = {\n title: string;\n /** Display labels; scope does not grant data access. */\n scope: { organization: string; environment: string };\n appearance: unknown;\n};\n\nexport function dataAppTitle(\n config: Pick<DataAppConfig, 'title' | 'scope'>\n): string {\n return `${config.title} • ${config.scope.organization}/${config.scope.environment} • Altertable app`;\n}\n"
5
+ "/**\n * App identity and display scope shared by browser and server.\n * @module @altertable/data-app/config\n */\nimport type { AppearanceOptions } from '@/src/core/appearance';\n\nexport type DataAppConfig = {\n title: string;\n /** Display labels; scope does not grant data access. */\n scope: { organization: string; environment: string };\n appearance: AppearanceOptions;\n};\n\nexport function dataAppTitle(\n config: Pick<DataAppConfig, 'title' | 'scope'>\n): string {\n return `${config.title} • ${config.scope.organization}/${config.scope.environment} • Altertable app`;\n}\n"
6
6
  ],
7
- "mappings": ";AAWO,SAAS,aAAY,CAC1B,QACQ;AAAA,EACR,OAAO,GAAG,OAAO,WAAW,OAAO,MAAM,gBAAgB,OAAO,MAAM;AAAA;",
7
+ "mappings": ";AAaO,SAAS,aAAY,CAC1B,QACQ;AAAA,EACR,OAAO,GAAG,OAAO,WAAW,OAAO,MAAM,gBAAgB,OAAO,MAAM;AAAA;",
8
8
  "debugId": "B8EA593317DFC02A64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -6,7 +6,7 @@ import {
6
6
  getDataAppTransport2,
7
7
  executeDataOperation,
8
8
  toDataOperationFailure
9
- } from "./contract-8wdybxj7.js";
9
+ } from "./contract-mwe7gnmh.js";
10
10
  import {
11
11
  PARENT_PARAM
12
12
  } from "./contract-cxr9t12b.js";
@@ -1,13 +1,16 @@
1
1
  import {
2
2
  formatDateRange2
3
3
  } from "./contract-mev09s5v.js";
4
- import {
5
- invariant
6
- } from "./contract-8q35dcyh.js";
7
4
 
8
5
  // src/core/contract.ts
9
6
  import { parseDate, today } from "@internationalized/date";
10
7
 
8
+ // src/core/invariant.ts
9
+ function invariant(condition, message) {
10
+ if (!condition)
11
+ throw new Error(`Invariant failed: ${message}`);
12
+ }
13
+
11
14
  // src/core/dimension.ts
12
15
  var allSelection = { kind: "all" };
13
16
  function dimensionMemberKey(member) {
@@ -16,7 +19,6 @@ function dimensionMemberKey(member) {
16
19
  function dimensionFilter2(config) {
17
20
  const { key, label, selection, valueType } = config;
18
21
  const options = config.options ?? [];
19
- invariant(!!config.options !== !!config.facet, "Choose fixed options or one facet operation.");
20
22
  const optionKeys = options.map((option) => dimensionMemberKey({ kind: "value", value: option.value }));
21
23
  invariant(!!key && options.length <= 200 && new Set(optionKeys).size === options.length, "Dimension options need a key, at most 200 distinct values.");
22
24
  invariant(options.every((option) => typeof option.value === valueType && !!option.label.trim() && (option.count === undefined || Number.isSafeInteger(option.count) && option.count >= 0)), "Dimension options need typed values, labels, and nonnegative counts.");
@@ -321,6 +323,6 @@ function connectionCheck2() {
321
323
  });
322
324
  }
323
325
 
324
- export { dimensionMemberKey, dimensionFilter2, parseDimensionSelection2, parseFacetOptions2, dimensionPredicate2, parseEmptyInput2, parseTrue2, previousDateRange2, defineDateRangeContract2, parseDateRangeInput2, parseCount2, parseLabel2, rowsAsRecords2, defineQueryNames2, DataSourceError2, defineOperation2, connectionQueryNames2, connectionCheck2 };
326
+ export { invariant, dimensionMemberKey, dimensionFilter2, parseDimensionSelection2, parseFacetOptions2, dimensionPredicate2, parseEmptyInput2, parseTrue2, previousDateRange2, defineDateRangeContract2, parseDateRangeInput2, parseCount2, parseLabel2, rowsAsRecords2, defineQueryNames2, DataSourceError2, defineOperation2, connectionQueryNames2, connectionCheck2 };
325
327
 
326
- //# debugId=C0344E56C3A0436364756E2164756E21
328
+ //# debugId=44D567B58A731E4A64756E2164756E21
@@ -0,0 +1,12 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/core/contract.ts", "../src/core/invariant.ts", "../src/core/dimension.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * Shared operation contracts and input/output validation for browser and server.\n * @module @altertable/data-app/contract\n * @see https://github.com/altertable-ai/data-app/blob/main/docs/contract.md\n */\nimport { parseDate, today } from '@internationalized/date';\nimport { formatDateRange } from '@/src/core/format';\nimport { invariant } from '@/src/core/invariant';\nimport type { DateRange as DateRangeInput } from '@/src/core/date-range';\nimport type {\n QueryResult,\n DataOperation,\n OperationContext,\n} from '@/src/core/operation-types';\nexport type {\n QueryResult,\n DisclosedQuery,\n Lakehouse,\n OperationContext,\n DataOperation,\n DataOperations,\n} from '@/src/core/operation-types';\n\nexport function parseEmptyInput(value: unknown): Record<string, never> {\n invariant(\n typeof value === 'object' &&\n value !== null &&\n !Array.isArray(value) &&\n Object.keys(value).length === 0,\n 'This operation takes no inputs.'\n );\n\n return {};\n}\n\nexport function parseTrue(value: unknown): true {\n invariant(value === true, 'Expected a successful check.');\n\n return true;\n}\n\nexport type { DateRange as DateRangeInput } from '@/src/core/date-range';\nexport type DateRangeRequest = {\n range: DateRangeInput;\n comparison: DateRangeInput | null;\n};\n\n/** The immediately preceding, equally long set of calendar days. */\nexport function previousDateRange({\n start,\n end,\n}: DateRangeInput): DateRangeInput {\n const first = parseDate(start);\n const last = parseDate(end);\n const days =\n (last.toDate('UTC').getTime() - first.toDate('UTC').getTime()) /\n 86_400_000 +\n 1;\n invariant(days >= 1, 'Date range must start before it ends.');\n const previousEnd = first.subtract({ days: 1 });\n\n return {\n start: previousEnd.subtract({ days: days - 1 }).toString(),\n end: previousEnd.toString(),\n };\n}\n\nexport type ReportingPeriod =\n | { kind: 'rolling'; amount: number; unit: 'hour' | 'day'; end: string }\n | { kind: 'calendar'; start: string; end: string; timeZone: string };\n\nexport type DateRangeContractOptions = {\n minDate?: string;\n maxDate?: string | (() => string);\n maxRangeDays: number;\n timeZone: string;\n completeDays?: boolean;\n};\n\n/** One source policy for the server parser, URL variable, and displayed reporting period. */\nexport function defineDateRangeContract(options: DateRangeContractOptions) {\n invariant(\n Number.isInteger(options.maxRangeDays) && options.maxRangeDays > 0,\n 'Date range needs a positive maxRangeDays.'\n );\n if (options.minDate) parseDate(options.minDate);\n if (typeof options.maxDate === 'string') parseDate(options.maxDate);\n today(options.timeZone);\n\n function bounds() {\n const latest = today(options.timeZone).subtract({\n days: options.completeDays === false ? 0 : 1,\n });\n const sourceMax =\n typeof options.maxDate === 'function'\n ? options.maxDate()\n : options.maxDate;\n const maxDate =\n sourceMax && parseDate(sourceMax).compare(latest) < 0\n ? parseDate(sourceMax).toString()\n : latest.toString();\n\n return {\n minDate: options.minDate,\n maxDate,\n maxRangeDays: options.maxRangeDays,\n timeZone: options.timeZone,\n };\n }\n\n function period({ start, end }: DateRangeInput): ReportingPeriod {\n return {\n kind: 'calendar',\n start,\n end,\n timeZone: options.timeZone,\n };\n }\n\n function describeInput(input: DateRangeInput) {\n return `${formatDateRange(input)} ${options.timeZone}`;\n }\n\n function comparison(input: DateRangeInput): DateRangeInput | null {\n const previous = previousDateRange(input);\n\n return options.minDate && previous.start < options.minDate\n ? null\n : previous;\n }\n\n function request(value: unknown, compare = false): DateRangeRequest {\n const range = parseDateRangeInput(value, bounds());\n const previous = compare ? comparison(range) : null;\n invariant(\n !compare || previous,\n 'Comparison is outside available source coverage.'\n );\n\n return { range, comparison: previous };\n }\n\n function parseRequest(value: unknown): DateRangeRequest {\n invariant(value && typeof value === 'object', 'Choose a date range.');\n const input = value as Record<string, unknown>;\n const parsed = request(input.range, input.comparison != null);\n if (input.comparison != null) {\n const supplied = parseDateRangeInput(input.comparison, bounds());\n invariant(\n supplied.start === parsed.comparison?.start &&\n supplied.end === parsed.comparison?.end,\n 'Comparison must be the preceding equal-length range.'\n );\n }\n\n return parsed;\n }\n\n return {\n request,\n parseRequest,\n bounds,\n parse(value: unknown) {\n return parseDateRangeInput(value, bounds());\n },\n period,\n comparison,\n describeInput,\n view: { period, describeInput },\n };\n}\n\nexport type DateRangeContract = ReturnType<typeof defineDateRangeContract>;\n\n/** Validate calendar dates and a bounded inclusive range on the server. */\nexport function parseDateRangeInput(\n value: unknown,\n {\n minDate,\n maxDate,\n maxRangeDays,\n }: { minDate?: string; maxDate?: string; maxRangeDays: number }\n): DateRangeInput {\n invariant(\n value && typeof value === 'object' && !Array.isArray(value),\n 'Choose a date range.'\n );\n const { start, end } = value as Record<string, unknown>;\n\n function day(input: unknown): number {\n invariant(\n typeof input === 'string' && /^\\d{4}-\\d{2}-\\d{2}$/.test(input),\n 'Dates must be YYYY-MM-DD.'\n );\n const timestamp = Date.parse(`${input}T00:00:00Z`);\n invariant(\n Number.isFinite(timestamp) &&\n new Date(timestamp).toISOString().slice(0, 10) === input,\n 'Choose valid calendar dates.'\n );\n\n return timestamp;\n }\n const first = day(start);\n const last = day(end);\n invariant(\n Number.isInteger(maxRangeDays) && maxRangeDays > 0,\n 'Maximum range days must be positive.'\n );\n invariant(\n first <= last &&\n (last - first) / 86_400_000 < maxRangeDays &&\n (minDate === undefined || first >= day(minDate)) &&\n (maxDate === undefined || last <= day(maxDate)),\n `Choose up to ${maxRangeDays} days within the available range.`\n );\n\n return { start: start as string, end: end as string };\n}\n\n/** Accepts numeric strings from DuckDB; rejects negative, fractional, and unsafe integers. */\nexport function parseCount(value: unknown): number {\n const number =\n typeof value === 'number' || typeof value === 'string'\n ? Number(value)\n : NaN;\n invariant(\n Number.isSafeInteger(number) &&\n number >= 0 &&\n (typeof value !== 'string' || !!value.trim()),\n 'Invalid count in query result.'\n );\n\n return number;\n}\n\nexport function parseLabel(value: unknown, maxLength = 100): string {\n invariant(\n typeof value === 'string' && !!value && value.length <= maxLength,\n 'Invalid label in query result.'\n );\n\n return value;\n}\n\n/** Rejects missing or duplicate columns and rows whose width differs from the column list. */\nexport function rowsAsRecords(\n result: QueryResult,\n requiredColumns: readonly string[]\n): Record<string, unknown>[] {\n const names = result.columns.map(column => column.name);\n invariant(\n new Set(names).size === names.length &&\n requiredColumns.every(name => names.includes(name)),\n 'Query result columns do not match the expected shape.'\n );\n\n return result.rows.map(row => {\n invariant(\n row.length === names.length,\n 'Query result row has the wrong width.'\n );\n\n return Object.fromEntries(names.map((name, index) => [name, row[index]]));\n });\n}\n\nexport function defineQueryNames<const Names extends Record<string, string>>(\n names: Names\n): Names {\n const values = Object.values(names);\n invariant(\n values.every(name => !!name.trim()) &&\n new Set(values).size === values.length,\n 'Query names must be nonempty and unique.'\n );\n\n return names;\n}\n\nconst sourceErrorBrand = Symbol.for('@altertable/data-app/DataSourceError');\n\nexport class DataSourceError extends Error {\n readonly [sourceErrorBrand] = true;\n queryName?: string;\n\n static [Symbol.hasInstance](value: unknown): boolean {\n return (\n value instanceof Error &&\n (value as { [sourceErrorBrand]?: unknown })[sourceErrorBrand] === true\n );\n }\n\n constructor(\n public readonly reason:\n | 'unauthorized'\n | 'forbidden'\n | 'rate_limited'\n | 'query_rejected'\n | 'unavailable',\n public readonly status?: number\n ) {\n super(`Data source ${reason}${status ? ` (${status})` : ''}.`);\n this.name = 'DataSourceError';\n }\n}\n\nexport type OperationQuery<Names extends Readonly<Record<string, string>>> = (\n name: Names[keyof Names],\n statement: string,\n options?: { limit?: number }\n) => Promise<QueryResult>;\n\nexport function defineOperation<\n Input,\n Output,\n const Names extends Readonly<Record<string, string>> = Record<string, never>,\n>(\n operation: Omit<DataOperation<Input, Output>, 'run' | 'queryNames'> & {\n queryNames?: Names;\n run: (\n context: OperationContext & { query: OperationQuery<NoInfer<Names>> },\n input: Input\n ) => Promise<Output>;\n }\n): DataOperation<Input, Output> & { queryNames?: Names } {\n invariant(\n Array.isArray(operation.checks) &&\n operation.checks.length > 0 &&\n Number.isInteger(operation.policy.maxQueryRows) &&\n operation.policy.maxQueryRows > 0 &&\n Number.isInteger(operation.policy.maxDurationMs) &&\n operation.policy.maxDurationMs > 0 &&\n (operation.policy.maxResponseBytes === undefined ||\n (Number.isInteger(operation.policy.maxResponseBytes) &&\n operation.policy.maxResponseBytes > 0)),\n 'Each data operation needs check inputs and positive row and duration limits.'\n );\n if (operation.queryNames) defineQueryNames(operation.queryNames);\n for (const input of operation.checks) operation.input(input);\n\n return {\n ...operation,\n run(context, input) {\n function query(\n name: Names[keyof Names],\n statement: string,\n options?: { limit?: number }\n ): Promise<QueryResult> {\n invariant(\n operation.queryNames &&\n Object.values(operation.queryNames).includes(name),\n `Unknown query name: ${name}.`\n );\n\n return context.lakehouse.queryAll(statement, {\n name,\n limit: options?.limit ?? operation.policy.maxQueryRows,\n signal: context.signal,\n });\n }\n\n return operation.run({ ...context, query }, input);\n },\n };\n}\n\nexport const connectionQueryNames = defineQueryNames({\n connection: 'connection-check',\n});\n\n/** Success requires a bounded SQL query; it does not establish access to a particular dataset. */\nexport function connectionCheck(): DataOperation<Record<string, never>, true> {\n return defineOperation({\n input: parseEmptyInput,\n output: parseTrue,\n checks: [{}],\n queryNames: connectionQueryNames,\n policy: { maxQueryRows: 1, maxDurationMs: 15_000, exposeSql: true },\n async run({ query }): Promise<true> {\n await query(\n connectionQueryNames.connection,\n 'SELECT 1 AS connection_check'\n );\n\n return true;\n },\n });\n}\n\nexport {\n dimensionFilter,\n parseDimensionSelection,\n parseFacetOptions,\n dimensionPredicate,\n} from '@/src/core/dimension';\nexport type {\n DimensionSelection,\n DimensionMember,\n DimensionOption,\n DimensionVariable,\n DimensionFilterOptions,\n} from '@/src/core/dimension';\n\nexport {\n createMessageRouter,\n defineMessageRoute,\n defineDataQueryRoute,\n MessageRoutingError,\n dataAppRoutes,\n navigationUpdateRoute,\n sqlQueryRoute,\n} from '@/src/core/messages';\nexport type {\n MessageContext,\n RoutedMessage,\n MessageDispatcher,\n MessageTransport,\n MessageRoute,\n MessageRoutes,\n MessageInput,\n MessageHandlers,\n MessageOutput,\n OperationContracts,\n DataQueryInput,\n DataQueryBody,\n DataQueryRoute,\n NavigationUpdate,\n SqlQueryInput,\n} from '@/src/core/messages';\nexport type { TransportResponse } from '@/src/core/bridge';\n",
6
+ "/** Assert an author or data contract while preserving a useful failure message. */\nexport function invariant(\n condition: unknown,\n message: string\n): asserts condition {\n if (!condition) throw new Error(`Invariant failed: ${message}`);\n}\n",
7
+ "import type { AppVariable, HistoryMode } from '@/src/core/variables';\nimport { invariant } from '@/src/core/invariant';\n\nexport type DimensionValue = string | number;\nexport type DimensionMember<T extends DimensionValue = DimensionValue> =\n | { kind: 'value'; value: T }\n | { kind: 'missing' };\nexport type DimensionSelection<T extends DimensionValue = DimensionValue> =\n | { kind: 'all' }\n | { kind: 'include'; members: readonly DimensionMember<T>[] };\nexport type DimensionOption<T extends DimensionValue = DimensionValue> = {\n value: T;\n label: string;\n count?: number;\n};\nexport type DimensionVariable<T extends DimensionValue = DimensionValue> = Omit<\n AppVariable<DimensionSelection<T>, 'dimension'>,\n 'options'\n> & {\n label: string;\n selection: 'single' | 'multiple';\n valueType: 'string' | 'number';\n allowMissing: boolean;\n maxSelected: number;\n options: readonly DimensionOption<T>[];\n facet?: {\n operation: string;\n input: (values: Record<string, unknown>) => unknown;\n };\n describe: (selection: DimensionSelection<T>) => string;\n};\n\nconst allSelection: DimensionSelection<never> = { kind: 'all' };\n\nexport function dimensionMemberKey(member: DimensionMember) {\n return member.kind === 'missing'\n ? 'missing'\n : `value:${typeof member.value}:${member.value}`;\n}\n\nexport type DimensionFilterOptions<T extends DimensionValue> = {\n key: string;\n label: string;\n valueType: T extends number ? 'number' : 'string';\n selection: 'single' | 'multiple';\n allowMissing?: boolean;\n maxSelected?: number;\n history?: HistoryMode;\n} & (\n | { options: readonly DimensionOption<T>[]; facet?: never }\n | {\n /** A bounded operation supplies options and counts; input chooses other filters affecting counts. */\n facet: {\n operation: string;\n input: (values: Record<string, unknown>) => unknown;\n };\n options?: never;\n }\n);\n\n/** A bounded categorical filter. URL tags distinguish missing from literal \"null\". */\nexport function dimensionFilter<const T extends DimensionValue>(\n config: DimensionFilterOptions<T>\n): DimensionVariable<T> {\n const { key, label, selection, valueType } = config;\n const options = config.options ?? [];\n const optionKeys = options.map(option =>\n dimensionMemberKey({ kind: 'value', value: option.value })\n );\n invariant(\n !!key &&\n options.length <= 200 &&\n new Set(optionKeys).size === options.length,\n 'Dimension options need a key, at most 200 distinct values.'\n );\n invariant(\n options.every(\n option =>\n typeof option.value === valueType &&\n !!option.label.trim() &&\n (option.count === undefined ||\n (Number.isSafeInteger(option.count) && option.count >= 0))\n ),\n 'Dimension options need typed values, labels, and nonnegative counts.'\n );\n const allowed = new Set(optionKeys);\n const maxSelected = config.maxSelected ?? (selection === 'single' ? 1 : 20);\n invariant(\n Number.isInteger(maxSelected) &&\n maxSelected >= 1 &&\n maxSelected <= 50 &&\n (selection !== 'single' || maxSelected === 1),\n 'Dimension selection limit is invalid.'\n );\n const allowMissing = config.allowMissing ?? false;\n const defaultValue = allSelection;\n\n function validMember(member: DimensionMember<T>): boolean {\n if (member.kind === 'missing') return allowMissing;\n if (member.kind !== 'value' || typeof member.value !== valueType)\n return false;\n if (!config.facet) return allowed.has(dimensionMemberKey(member));\n\n return typeof member.value === 'number'\n ? Number.isFinite(member.value)\n : member.value.length <= 100;\n }\n\n function valid(value: DimensionSelection<T>): boolean {\n if (!value || typeof value !== 'object') return false;\n if (value.kind === 'all') return true;\n if (\n value.kind !== 'include' ||\n !Array.isArray(value.members) ||\n !value.members.length ||\n value.members.length > maxSelected\n )\n return false;\n const ids = value.members.map(dimensionMemberKey);\n\n return new Set(ids).size === ids.length && value.members.every(validMember);\n }\n\n function encode(value: DimensionSelection<T>) {\n return value.kind === 'all'\n ? null\n : JSON.stringify(\n value.members.map(member =>\n member.kind === 'missing' ? ['m'] : ['v', member.value]\n )\n );\n }\n\n function parse(raw: string | null): DimensionSelection<T> {\n if (raw === null) return defaultValue;\n if (raw.length > 4096) return defaultValue;\n try {\n const tokens = JSON.parse(raw);\n if (!Array.isArray(tokens)) return defaultValue;\n const value = {\n kind: 'include' as const,\n members: tokens.map(token =>\n Array.isArray(token) && token.length === 1 && token[0] === 'm'\n ? { kind: 'missing' as const }\n : Array.isArray(token) && token.length === 2 && token[0] === 'v'\n ? { kind: 'value' as const, value: token[1] as T }\n : { kind: 'invalid' as const }\n ),\n };\n\n return valid(value as DimensionSelection<T>)\n ? (value as DimensionSelection<T>)\n : defaultValue;\n } catch {\n return defaultValue;\n }\n }\n\n return {\n kind: 'dimension',\n label,\n valueType,\n selection,\n options,\n facet: config.facet,\n allowMissing,\n maxSelected,\n urlKeys: [key],\n defaultValue,\n history: config.history ?? 'push',\n valid,\n same(left, right) {\n return encode(left) === encode(right);\n },\n read(params) {\n return parse(params.get(key));\n },\n write(value) {\n return { [key]: encode(value) };\n },\n describe(value) {\n return value.kind === 'all'\n ? `All ${label.toLocaleLowerCase()}`\n : value.members\n .map(member =>\n member.kind === 'missing'\n ? 'Missing'\n : (options.find(option => option.value === member.value)\n ?.label ?? String(member.value))\n )\n .join(', ');\n },\n };\n}\n\n/** Server-side parser; call from the operation input parser before building SQL. */\nexport function parseDimensionSelection<T extends DimensionValue>(\n value: unknown,\n filter: DimensionVariable<T>\n): DimensionSelection<T> {\n invariant(\n filter.valid(value as DimensionSelection<T>),\n `Invalid ${filter.label} selection.`\n );\n\n return value as DimensionSelection<T>;\n}\n\n/** Validate bounded facet results before showing them in a picker. */\nexport function parseFacetOptions<T extends DimensionValue>(\n value: unknown,\n filter: DimensionVariable<T>\n): DimensionOption<T>[] {\n invariant(\n Array.isArray(value) && value.length <= 200,\n 'Facet options exceed their bound.'\n );\n const options: DimensionOption<T>[] = [];\n const seen = new Set<string>();\n for (const item of value) {\n invariant(item && typeof item === 'object', 'Invalid facet option.');\n const option = item as DimensionOption<T>;\n invariant(\n typeof option.value === filter.valueType &&\n filter.valid({\n kind: 'include',\n members: [{ kind: 'value', value: option.value }],\n }) &&\n typeof option.label === 'string' &&\n !!option.label.trim() &&\n option.label.length <= 100 &&\n (option.count === undefined ||\n (Number.isSafeInteger(option.count) && option.count >= 0)),\n 'Invalid facet option.'\n );\n const id = dimensionMemberKey({ kind: 'value', value: option.value });\n invariant(!seen.has(id), 'Duplicate facet option.');\n seen.add(id);\n options.push(option);\n }\n\n return options;\n}\n\n/** Central SQL literal encoding for transports without bound parameters. Column is an allowlisted identifier. */\nexport function dimensionPredicate<\n T extends DimensionValue,\n const Columns extends readonly string[],\n>(\n column: Columns[number],\n selection: DimensionSelection<T>,\n allowedColumns: Columns\n): string {\n invariant(\n allowedColumns.includes(column) &&\n /^[A-Za-z_][A-Za-z_0-9]*(?:\\.[A-Za-z_][A-Za-z_0-9]*)*$/.test(column),\n 'Invalid dimension column.'\n );\n if (selection.kind === 'all') return '';\n invariant(\n selection.members.length > 0,\n 'Empty dimension inclusion is invalid.'\n );\n const values = selection.members.filter(\n (member): member is Extract<DimensionMember<T>, { kind: 'value' }> =>\n member.kind === 'value'\n );\n const missing = selection.members.some(member => member.kind === 'missing');\n const literals = values.map(({ value }) => {\n if (typeof value === 'number') {\n invariant(Number.isFinite(value), 'Invalid numeric dimension value.');\n\n return String(value);\n }\n const text = value as string;\n for (let index = 0; index < text.length; index++)\n invariant(\n text.charCodeAt(index) >= 32 && text.charCodeAt(index) !== 127,\n 'Invalid control character in dimension value.'\n );\n\n return `'${text.replaceAll(\"'\", \"''\")}'`;\n });\n const clauses = [\n ...(literals.length ? [`${column} IN (${literals.join(', ')})`] : []),\n ...(missing ? [`${column} IS NULL`] : []),\n ];\n\n return `(${clauses.join(' OR ')})`;\n}\n"
8
+ ],
9
+ "mappings": "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",
10
+ "debugId": "44D567B58A731E4A64756E2164756E21",
11
+ "names": []
12
+ }
@@ -1,10 +1,10 @@
1
1
  import"../chunks/contract-mev09s5v.js";
2
- import"../chunks/contract-wd8qe3mt.js";
2
+ import"../chunks/contract-yxbjea23.js";
3
3
  import {
4
4
  createDataClient2,
5
5
  createDataAppNavigation2,
6
6
  getDataAppNavigation2
7
- } from "../chunks/contract-tkc552ze.js";
7
+ } from "../chunks/contract-xtxza1fy.js";
8
8
  import {
9
9
  DataAppError2,
10
10
  createHttpTransport2,
@@ -12,7 +12,7 @@ import {
12
12
  createIframeTransport2,
13
13
  installDataAppTransport2,
14
14
  getDataAppTransport2
15
- } from "../chunks/contract-8wdybxj7.js";
15
+ } from "../chunks/contract-mwe7gnmh.js";
16
16
  export {
17
17
  DataAppError2 as DataAppError,
18
18
  createDataAppNavigation2 as createDataAppNavigation,
@@ -1,12 +1,14 @@
1
1
  import {
2
2
  parseAppearance2,
3
+ normalizeAppearance2,
3
4
  applyAppearance2,
4
5
  createThemeController2
5
- } from "../chunks/contract-farfe948.js";
6
+ } from "../chunks/contract-txjy0en2.js";
6
7
  export {
7
8
  applyAppearance2 as applyAppearance,
8
9
  createThemeController2 as createThemeController,
10
+ normalizeAppearance2 as normalizeAppearance,
9
11
  parseAppearance2 as parseAppearance
10
12
  };
11
13
 
12
- //# debugId=FFA707298E88F52F64756E2164756E21
14
+ //# debugId=E53C24BC954D4BB064756E2164756E21
@@ -4,6 +4,6 @@
4
4
  "sourcesContent": [
5
5
  ],
6
6
  "mappings": "",
7
- "debugId": "FFA707298E88F52F64756E2164756E21",
7
+ "debugId": "E53C24BC954D4BB064756E2164756E21",
8
8
  "names": []
9
9
  }
@@ -16,7 +16,7 @@ import {
16
16
  defineOperation2,
17
17
  connectionQueryNames2,
18
18
  connectionCheck2
19
- } from "../chunks/contract-wd8qe3mt.js";
19
+ } from "../chunks/contract-yxbjea23.js";
20
20
  import"../chunks/contract-mev09s5v.js";
21
21
  import {
22
22
  MessageRoutingError2,
@@ -1,10 +1,10 @@
1
1
  import"../chunks/contract-mev09s5v.js";
2
- import"../chunks/contract-wd8qe3mt.js";
2
+ import"../chunks/contract-yxbjea23.js";
3
3
  import {
4
4
  createIframeTransport2,
5
5
  installDataAppTransport2,
6
6
  toDataOperationFailure
7
- } from "../chunks/contract-8wdybxj7.js";
7
+ } from "../chunks/contract-mwe7gnmh.js";
8
8
  import {
9
9
  attachDataAppBridge2
10
10
  } from "../chunks/contract-zr3jd7mr.js";
@@ -9,28 +9,26 @@ import {
9
9
  formatPercent2
10
10
  } from "../chunks/contract-mev09s5v.js";
11
11
  import {
12
- parseAppearance2,
12
+ normalizeAppearance2,
13
13
  applyAppearance2,
14
14
  createThemeController2
15
- } from "../chunks/contract-farfe948.js";
15
+ } from "../chunks/contract-txjy0en2.js";
16
16
  import {
17
+ invariant,
17
18
  dimensionMemberKey,
18
19
  dimensionFilter2,
19
20
  parseFacetOptions2,
20
21
  defineQueryNames2
21
- } from "../chunks/contract-wd8qe3mt.js";
22
+ } from "../chunks/contract-yxbjea23.js";
22
23
  import {
23
24
  createDataClient2,
24
25
  getDataAppNavigation2
25
- } from "../chunks/contract-tkc552ze.js";
26
+ } from "../chunks/contract-xtxza1fy.js";
26
27
  import {
27
28
  DataAppError2,
28
29
  localFrameBridge,
29
30
  getDataAppTransport2
30
- } from "../chunks/contract-8wdybxj7.js";
31
- import {
32
- invariant
33
- } from "../chunks/contract-8q35dcyh.js";
31
+ } from "../chunks/contract-mwe7gnmh.js";
34
32
 
35
33
  // src/react/injectStyles.ts
36
34
  function injectStyles({
@@ -2405,6 +2403,10 @@ function injectDataAppStyles(options = {}) {
2405
2403
  min-width: 0;
2406
2404
  }
2407
2405
 
2406
+ .react-aria-TabPanel[data-inert] {
2407
+ display: none;
2408
+ }
2409
+
2408
2410
  .react-aria-TabPanel[data-focus-visible] {
2409
2411
  outline: var(--at-focus-outline, 1px solid #687078);
2410
2412
  outline-offset: var(--at-focus-ring-offset, 2px);
@@ -3750,6 +3752,7 @@ button {
3750
3752
  /* src/react/ui/Stack.css */
3751
3753
  .altertable-stack {
3752
3754
  display: grid;
3755
+ grid-template-columns: minmax(0, 1fr);
3753
3756
  align-content: start;
3754
3757
  min-width: 0;
3755
3758
  }
@@ -5691,7 +5694,6 @@ function dateRangeVariable({
5691
5694
  };
5692
5695
  }
5693
5696
  };
5694
- invariant(!defaultValue.comparison, `Date variable ${key} comparison must be activated by the reader.`);
5695
5697
  invariant(variable.valid(defaultValue), `Date variable ${key} has a default outside its available data range.`);
5696
5698
  return variable;
5697
5699
  }
@@ -7464,7 +7466,6 @@ function createDataHooks(client) {
7464
7466
  };
7465
7467
  }
7466
7468
  function defineTimeView(definition) {
7467
- invariant(!definition.variables || !("period" in definition.variables), "The period input is owned by defineTimeView.");
7468
7469
  const period = dateRangeVariable({ ...definition.time, key: "period" });
7469
7470
  return defineDataView({
7470
7471
  operation: definition.operation,
@@ -7565,10 +7566,10 @@ import { useEffect as useEffect14, useRef as useRef6 } from "react";
7565
7566
  // src/react/ui/useAppAppearance.ts
7566
7567
  import { useLayoutEffect, useState as useState8, useSyncExternalStore as useSyncExternalStore2 } from "react";
7567
7568
  function useAppAppearance(appearance, hostTheme) {
7568
- const [controller] = useState8(() => createThemeController2(parseAppearance2(appearance).theme));
7569
+ const [controller] = useState8(() => createThemeController2(normalizeAppearance2(appearance).theme));
7569
7570
  const viewerPreference = useSyncExternalStore2(controller.subscribe, controller.getTheme, controller.getTheme);
7570
7571
  useLayoutEffect(() => applyAppearance2({
7571
- ...parseAppearance2(appearance),
7572
+ ...normalizeAppearance2(appearance),
7572
7573
  theme: hostTheme ?? viewerPreference
7573
7574
  }), [appearance, hostTheme, viewerPreference]);
7574
7575
  return hostTheme ? undefined : controller;
@@ -10323,7 +10324,6 @@ import { jsx as jsx52, jsxs as jsxs36, Fragment as Fragment11 } from "react/jsx-
10323
10324
  function TableWidget(props) {
10324
10325
  const { pagination, limit, columns } = props;
10325
10326
  invariant(!pagination || Number.isSafeInteger(pagination.pageSize) && pagination.pageSize >= 1, "TableWidget pagination.pageSize must be a positive integer.");
10326
- invariant(limit === undefined || pagination === undefined, "TableWidget limit and pagination are mutually exclusive.");
10327
10327
  invariant(limit === undefined || Number.isSafeInteger(limit) && limit >= 1, "TableWidget limit must be a positive integer.");
10328
10328
  const columnIds = columns.map((column) => column.id);
10329
10329
  invariant(columnIds.length > 0 && columnIds.every((id) => !!id.trim()) && new Set(columnIds).size === columnIds.length, "TableWidget column IDs must be nonempty and unique.");
@@ -11479,4 +11479,4 @@ export {
11479
11479
  writeSearch
11480
11480
  };
11481
11481
 
11482
- //# debugId=103599A830311DAF64756E2164756E21
11482
+ //# debugId=087F64F77A0D4F5564756E2164756E21