import { H as HiddenFieldNotice, U as Uuid, g as Timestamp } from './record-C3thPhpR.js'; import { R as RuleExpression } from './rule-CMApHawk.js'; /** The measures the store offers. Asked of `custom.agg_operations()` at run time too. */ type AggregateOperation = "count" | "sum" | "avg" | "min" | "max" /** GRID-PRIMITIVES G1: the summary bar's other four. Blank = no value, JSON null or "". */ | "median" | "filled" | "empty" | "unique" /** * Notion's calculation row (Spaces N6): the share of the rows asked with no value / a value, as a * percent 0..100 (NULL over no rows); and the RANGE, largest minus smallest (a date column: the days * between its earliest and latest moment). */ | "percent_empty" | "percent_filled" | "range"; /** The periods a date Field can be bucketed into (`custom.agg_buckets()`). */ type AggregateBucket = "day" | "week" | "month" | "quarter" | "year"; /** * A HALF-OPEN MOMENT WINDOW: `from` is inclusive, `to` is exclusive. Half-open * is the only shape that makes "September" and "October" meet exactly once โ€” * an inclusive `to` either double-counts the boundary moment or drops it, * depending on the reader, and both of those are a chart telling a lie. */ interface AggregateWindow { from?: string | null; to?: string | null; } /** * ONE FILTER VALUE, as `custom.record_aggregate` and `custom.dashboard_run` * both read it: a SCALAR is an equality, and an OBJECT is a window. It is not * `unknown` on purpose โ€” a filter value the store cannot read is a refusal a * caller should get from the compiler, not from the database. */ type AggregateFilter = string | number | boolean | null | AggregateWindow; /** * ๐Ÿšจ THE STORE'S ONE FILTER SHAPE, AND THE ONLY NAME FOR IT. * * An object of Field key -> value. A scalar is an equality, `null` is UNSET * (the third state of a tick box), an object is a half-open time window. * * It is not the aggregate's private shape and never was. `platform.saved_view` * has carried exactly this as `definition -> 'filters'` across five live * surface keys since DOOR-18; `custom.view_declare` writes it; * `custom.agg_view_admits` reads it to decide whether a change belongs to a * saved view; `custom.record_aggregate` takes it as `p_filter` to COMPUTE a * dashboard number; and `custom.read_records_matching` takes it to OPEN the * rows behind that number. One `custom.record_filter_sql` in the database turns * it into SQL for the last two, so the number and the rows cannot disagree. * * Invent a second one and a chart can compute a total whose drill-through * lands somewhere else, which is the only way a drill-through can lie. */ type RecordFilter = Record; /** * S2-PRIME FILTER-GROUPS โ€” A READ DOOR'S QUESTION, IN EITHER OF ITS TWO SHAPES. * * The flat `RecordFilter` above (field key โ†’ scalar / `null` / window, AND only) is what * dashboards, drill-throughs and the notifier carry. A `RuleExpression` โ€” `{op, args}`, ALL / ANY / * NOT groups nested to any depth, fields by id โ€” is what the condition builder, a view's own * filter and every membership Rule speak. `custom.record_filter_sql(org, table, filter)` turns * EITHER into the one WHERE clause the grid (`list`), the aggregate and the board all use, so the * three can never disagree about which records a view holds. */ type StoreFilter = RecordFilter | RuleExpression; /** * Is this filter a Rule expression? The store's own test (`custom.filter_is_rule`): an object * whose `op` is a word and which carries `args`. */ declare function isRuleFilter(filter: unknown): filter is RuleExpression; /** One measure. `count` counts rows and needs no Field; the rest read one. */ interface AggregateMeasure { op: AggregateOperation; /** The Field's key. Required for every operation except `count`. */ key?: string; } /** * One row of an aggregate: which group it is, what each measure came to, and * how many records it was worked out from. The measure keys are the store's * own (`count`, `sum_amount`, `avg_amount`, โ€ฆ), never renamed on the way out. */ /** * One group's key, as the column holds it (CHAIR-SHEET-STORE b): a number column's key is a number, a * yes/no column's true/false, a date its ISO text, a date period its first moment, everything else text. */ type AggregateGroupValue = string | number | boolean | null; interface AggregateRow { groups: Record; measures: Record; /** * AGG-WITHHELD โ€” a measure over a column this reader may not read is `null` in `measures` (the * "no value" every number reader already handles) and its reason rides HERE by measure name: the * same notice a withheld cell carries (`reason`, `needs`, `or`) plus the store's own sentence in * `says`. The store never errors a read for a withheld column; it says so. A question that * GROUPS or FILTERS by such a column answers one row with every measure null and `row_count` 0. * Absent when nothing was withheld. */ withheld?: Record; row_count: number; /** * S3 โ€” present only when the question carried a comparison. The same group * in the PRIOR window: a bucket is matched by its position in its own window * (week 1 of September beside week 1 of August), so `prior_groups` names the * prior bucket's own date. `null` when the prior window had no such group. */ prior_groups?: Record | null; /** The prior window's measures, keyed exactly like `measures`. */ prior_measures?: Record | null; prior_row_count?: number | null; /** Every measure now and before, the change, and the change as a percent of before. */ delta?: Record | null; /** The two windows this row was worked out over, and the calendar that cut them. */ compare?: AggregateCompareWindows | null; /** * S3 โ€” a compared, bucketed row's POSITION in its window (0 = the first * bucket). A row the current window has not reached yet has a position and * no current bucket, so a chart names it "Week 6" rather than inventing a date. */ position?: number | null; } /** * S3 โ€” WHAT A PERIOD IS COMPARED AGAINST (`custom.agg_compare_kinds()`). * `previous_period` is the period before, of the same length (August before * September; an explicit window shifted back by its own length); * `same_period_last_year`; `range` is a fixed `baseline`. */ type AggregateCompareKind = "previous_period" | "same_period_last_year" | "range"; /** * S3 โ€” ONE COMPARISON, as `custom.record_aggregate(p_compare)` and a * dashboard block's `compare` both take it. The current window is either the * calendar `period` holding `at` (default: now, in the organization's time * zone) or an explicit `from`/`to`. A date alone ("2026-09-01") is that local * day in the organization's time zone. */ interface AggregateCompare { against?: AggregateCompareKind; /** The date Field the period runs along. Defaults to the bucket's key. */ key?: string; period?: AggregateBucket; at?: string; from?: string; to?: string; /** Compare the part of the period that has happened ("month to date"). */ to_date?: boolean; /** The fixed period `range` compares against. Half-open: `to` is excluded. */ baseline?: { from: string; to: string; }; } /** One measure now and before. `change_pct` is null when before was nothing or zero. */ interface AggregateDelta { current: number | null; prior: number | null; change: number | null; change_pct: number | null; } /** * S3 โ€” THE TWO WINDOWS the store actually ran, as local ISO moments with the * organization's offset ("2026-09-01T00:00:00-07:00"), and the calendar that * cut them. A screen labels the comparison from THIS, never from its own clock. */ interface AggregateCompareWindows { key: string; against: AggregateCompareKind; period: AggregateBucket | null; to_date: boolean; time_zone: string; week_start: string; /** The bucket grain the windows were counted in, when the question had a bucket. */ grain: AggregateBucket | null; /** How many buckets the current window holds โ€” what a target's pace is shared over. */ bucket_count: number | null; window: { from: string; to: string; }; prior_window: { from: string; to: string; }; /** On a row from `recordAggregate`: that row's bucket position (see `AggregateRow.position`). */ position?: number | null; } /** The measure key the store answers with, so a chart can find its own series. */ declare function measureKey(measure: AggregateMeasure): string; /** * THE QUESTION, AS A STABLE STRING โ€” key-sorted JSON of a `RecordFilter`. * * A React parent that rebuilds `{ status: "Cancelled" }` on every render hands * a NEW object every time, and a hook that depends on the object re-reads the * whole page on every render forever. Depending on this instead means the page * is re-read exactly when the QUESTION changed, which is what a person means. * * Nested window objects are sorted too, so `{from, to}` and `{to, from}` are * one question. `undefined` and an empty filter are both `"{}"` โ€” the caller * asked nothing, and there is only one way to ask nothing. */ declare function stableFilterKey(filter: StoreFilter | null | undefined): string; /** * AGG-WITHHELD โ€” the store's answer, with the withheld state lifted out of `measures`. * `custom.record_aggregate` puts a withheld measure's notice under `measures._withheld` * (null beside it); no consumer should ever meet that key among the numbers, so the one door * that reads the answer moves it to `row.withheld` and zeroes a count the store left out. */ declare function liftWithheld(rows: AggregateRow[]): AggregateRow[]; /** * ROLLUP-FILTER (2026-10-18) โ€” SEVERAL VALUES FOR ONE COLUMN, IN A FLAT FILTER. * * Beside a single value (`{status: "Paid"}`) and a window (`{paid_on: {relative: "this_month"}}`), a * column of a flat filter (a roll-up's `config.filter`, a saved view's filters) takes * - an any-of list `{status: ["Sent", "Overdue"]}` (the store's list branch, has_any) * - a not-in form `{status: {not: ["Draft", "Cancelled"]}}` (one word works too; list question is_not) * Each word is a choice's key or label, any case. This is the ONE reading of that grammar for the TypeScript * twins of `custom.record_filter_sql` (the memory world, the template checker); the store's own is the SQL. * A blank cell is none of the words, so a not-in form keeps it. */ type FilterWords = { mode: "any" | "none"; words: string[]; }; /** `{not: ...}` / a list as the words it asks about, or `null` when the value is neither (a scalar, a window, null). */ declare function filterWordsOf(want: unknown): FilterWords | null; /** * Does a cell (one value, a list, or nothing) satisfy an any-of / not-in question? `aliases` maps a word to the other * words that name the same choice (its key and label), so a filter written with the key reads a cell kept as the label. */ declare function cellMatchesWords(cell: unknown, q: FilterWords, aliases?: (word: string) => readonly string[]): boolean; /** The seven shapes a block can take, exactly as `custom.dashboard_kinds()` answers. */ type DashboardBlockKind = "number" | "bar" | "column" | "line" | "donut" | "table" | "stuck"; /** * ONE SAVED BLOCK โ€” the spec, not the answer. `table_id` left out means the * dashboard's own subject Table, and `state_key` / `days` belong to `stuck` * alone (the door refuses a `stuck` block that names no field to watch). */ interface DashboardBlock { /** * The block's own id, kept across every save, resize and move (v6 lane 11): the layout and the * per-block refusal tile key on it. The client mints a uuid for a block that has none. */ id?: string; title?: string; kind: DashboardBlockKind; /** Absent = the dashboard's subject Table. */ table_id?: Uuid | null; /** Field keys whose values become the groups. */ group_by?: string[]; /** `count` needs no key; every other operation reads one Field. */ measures?: AggregateMeasure[]; /** A date Field truncated to a period. */ bucket?: { key: string; by: AggregateBucket; } | null; /** A scalar is an equality; an object is a half-open moment window. */ filter?: Record | null; /** Rows this block reads. The door clamps it to 1โ€ฆ500 and defaults to 50. */ limit?: number; /** Columns of the twelve this block occupies. The door clamps it to 2โ€ฆ12 and defaults to 6. */ span?: number; /** `stuck` only: the field whose value is watched for a change. */ state_key?: string; /** `stuck` only: how long a record may sit on the same value. Defaults to 14. */ days?: number; /** * S3 โ€” the comparison this block makes (this month against last month). * The door fills `key` from the bucket when it is left out, and refuses a * comparison along a Field the Table does not have, by name. */ compare?: AggregateCompare | null; /** S3 โ€” the number this block is aiming for. */ target?: DashboardTarget | null; } /** * S3 โ€” A BLOCK'S TARGET. `measure` is one of the block's own measure keys * (default: the first). `per: "period"` is a target for the whole window (a * monthly target โ€” a bucketed chart draws its even share per bucket as the * pace); `per: "bucket"` is a target for every bar or point. * * A target is a FIXED NUMBER (`value`) or A GOAL COLUMN (`field`, a Field key of * the block's Table): the store adds that column up (`op`, default `sum`) over * exactly the records the block's own number read โ€” same filter, same window, * same reader โ€” so "invoiced against quoted" is about the same jobs. Exactly * one of the two; a goal column is always `per: "period"`. */ type DashboardTarget = DashboardTargetBase & ({ value: number; field?: never; op?: never; } | { field: string; op?: DashboardTargetOp; value?: never; }); /** How a goal column is added up across the block's records. */ type DashboardTargetOp = "sum" | "avg" | "min" | "max"; /** What every target carries, whichever kind it is. */ interface DashboardTargetBase { label?: string; measure?: string; per?: "period" | "bucket"; } /** One dashboard as `custom.dashboards` reports it to its organization. */ interface DashboardSummary { dashboard_id: Uuid; /** The subject Table: the one a block that names no table of its own reads. */ table_id: Uuid | null; name: string; blocks: DashboardBlock[]; presentation: Record; block_count: number; version: number; created_at: Timestamp; updated_at: Timestamp; } /** * AT-INTERFACE โ€” ONE INTERFACE as `custom.interface_read` hands it to this viewer. An Interface is a dashboard * record whose `presentation.kind` is `"interface"`; builders (Editor, Full) also get `presentation.draft` and * `presentation.versions`, everybody else only `presentation.published`. `member` = the viewer is in the * interface's organization (elements read through the viewer's own doors); otherwise every element reads * through `custom.interface_rows`. Null from the door = not there, archived, or not shared with her. */ interface InterfaceRead { id: Uuid; organization_id: Uuid; table_id: Uuid | null; name: string; version: number; updated_at: Timestamp; level: "admin" | "editor" | "edit_content" | "commenter" | "viewer"; member: boolean; presentation: Record; blocks: DashboardBlock[]; } /** One column an interface element exposes (`custom.interface_rows`). */ interface InterfaceRowsField { key: string; label: string; type: string; multi: boolean; system: string | null; relation_target: Uuid | null; format: Record | null; sort: number | null; } /** The rows ONE interface element exposes; a number element answers `count` alone. */ interface InterfaceRows { table?: string | null; title_field?: string | null; fields?: InterfaceRowsField[]; rows?: Array<{ id: Uuid; created_at: Timestamp; updated_at: Timestamp; document: Record; }>; count: number; truncated?: boolean; /** Words of the records a cell points at, by id. */ refs?: Record; /** Choice words by field key, then by normalized value. */ labels?: Record>; } /** * One row of a `stuck` block: a record that has sat on the same value of * `state` for `days_unchanged` days. `measured_from` names WHERE that age was * measured from, because a record whose history does not reach back that far is * aged from what the store actually knows and says so. */ interface StuckRow { record_id: Uuid; title: string | null; state: string | null; last_changed_at: Timestamp | null; days_unchanged: number; measured_from: string; } /** * ONE BLOCK'S ANSWER. The door echoes the block back NORMALIZED โ€” the defaults * it applied, the merged filter it actually ran โ€” and adds `rows` and `ms`; * a block it could not run carries `refused` and `sqlstate` INSTEAD of rows, * and a block it could not even normalize carries only its title and kind. */ interface DashboardBlockResult { /** The saved block's own id, when the store echoes it. */ id?: string; title: string; kind: DashboardBlockKind | (string & {}); table_id?: Uuid; group_by?: string[]; measures?: AggregateMeasure[]; bucket?: { key: string; by: AggregateBucket; } | null; /** The block's own filter with the canvas bar merged under it โ€” the block's own key wins. */ filter?: Record; limit?: number; span?: number; state_key?: string; days?: number; /** An aggregate block's groups and measures, or a `stuck` block's records. Absent when refused. */ rows?: AggregateRow[] | StuckRow[]; /** How long this one block took, in milliseconds. Absent when refused. */ ms?: number; /** The store's own sentence, when this block could not be run. */ refused?: string; /** The SQLSTATE that refusal carried. */ sqlstate?: string; /** * S3 โ€” the windows this block was actually compared over; `null` when it was * not compared. (The comparison it was ASKED to make is the saved block's.) */ compare?: AggregateCompareWindows | null; /** S3 โ€” why a canvas comparison was not applied to this block (it has no date). */ compare_refused?: string; /** * S3 โ€” the block's first (or target) measure added up across its rows, now * and before, when the measure adds up (count, sum, filled, empty) or the * block has one row. Worked out by the store from the rows it answered, so * a tile, a chart and a ring cannot disagree. */ totals?: DashboardTotals; /** S3 โ€” the target, with how far the current total has come and its pace per bucket. */ target?: DashboardTargetResult; } /** S3 โ€” one block's total, now and before. */ interface DashboardTotals { measure: string; current: number | null; prior: number | null; change: number | null; change_pct: number | null; } /** S3 โ€” a target as the run answers it. */ interface DashboardTargetResult { /** * The number aimed at: the fixed value, or the goal column added up over this * block's records. `null` when a goal column had nothing to add up, or was refused. */ value: number | null; /** A goal column's key, when the target is read from one. */ field?: string; /** How that column was added up. */ op?: DashboardTargetOp; /** * The store's own sentence when THIS reader may not read the goal column โ€” the * block still answers its number; only the target is withheld. */ refused?: string; label: string; measure: string; per: "period" | "bucket"; /** The current total the target is measured against. */ current: number | null; /** current รท value, 4 decimals. */ progress: number | null; /** The line a bucketed chart draws: the target's even share of one bucket, or the target itself per bucket. */ pace: number | null; } /** What `custom.dashboard_run` answers: the canvas, and every block's own result. */ interface DashboardRunResult { dashboard_id: Uuid; name: string | null; subject_table_id: Uuid | null; presentation: Record; /** The one filter bar this run was asked with. */ filter: Record; /** S3 โ€” the date grain the canvas re-cut every bucketed block to, or null. */ grain?: AggregateBucket | null; /** S3 โ€” the canvas comparison this run was asked with, or null. */ compare?: AggregateCompare | null; /** S3 โ€” the organization's calendar every date on this canvas was cut with. */ calendar?: { time_zone: string; week_start: string; }; blocks: DashboardBlockResult[]; } /** * HOW OFTEN. `instant` fires the moment a record ENTERS the view; the other three * are summaries, assembled by the store at that cadence and sent as ONE message * naming what entered, left and changed since the last one. * * `immediate` and `digest` โ€” the two words this store wrote before 2026-09-20 โ€” * are still accepted everywhere and normalized by `custom.agg_cadence_normalize` * to `instant` and `daily`, so a Rule written then and a Rule written by the * picker now mean the same thing to the same runner. */ type SubscriptionCadence = "instant" | "hourly" | "daily" | "weekly"; /** * THE HOURS NOT TO BE TOLD IN. A send inside them is not dropped โ€” the store moves * it to the moment they end through the notification system's own `next_attempt_at`, * so there is still one sender and one retry policy and nothing disappears because * of the clock. */ interface QuietHours { /** "22:00". Left out (with `end`), the block names no night โ€” only the clock. */ start?: string; /** "07:00" โ€” earlier than `start` means the block crosses midnight, which is the ordinary case. */ end?: string; /** * An IANA zone name, e.g. "America/Chicago". Left out, UTC. THIS IS ALSO THE CLOCK THE * SCHEDULE IS READ IN: `custom.agg_digest_due_at` reads "monday 08:00" in this zone, so a * block carrying only `tz` means "no quiet hours, and 08:00 is my 08:00". */ tz?: string; } /** True when the block names a night (both ends), not only the clock the schedule is read in. */ declare function hasQuietHours(quiet: QuietHours | null | undefined): quiet is QuietHours & { start: string; end: string; }; /** The zone of the person at the keyboard, or null where the runtime cannot say. */ declare function callersTimeZone(): string | null; /** * A SUBSCRIPTION IS READ IN ITS OWNER'S CLOCK (lane HANDOVER, 2026-09-28). * * The store reads a schedule's "08:00" in `quiet_hours.tz` and falls back to UTC. Every caller * that set no quiet hours sent no zone, so Cedar Ridge Physical Therapy's "every Monday at 8" * went at 1 AM in California. `subscriptionDeclare` passes every spec through this: a spec that * names a zone is kept as it is; one that names none gets the caller's own. */ declare function withCallersClock(spec: T, zone?: string | null): T; /** One subscription, exactly as `custom.agg_subscriptions` reports it. */ interface Subscription { rule_id: Uuid; saved_view_id: Uuid | null; cadence: string; /** The digest's schedule. Null for `immediate`. */ schedule: string | null; channel: string; /** Null means this subscription fires at nobody, and the store says so. */ recipient_user_id: Uuid | null; event_key: string; name: string; } /** * ONE SUBSCRIPTION AS ITS OWNER SEES IT โ€” `custom.subscriptions`. * * `custom.agg_subscriptions` is the NOTIFIER's reader: it answers the whole * organization's firing subscriptions so the delivery path can run, and it is * not callable by a client. This is the other side of the same rows โ€” narrowed * to Tables the caller can already open, so the list never reveals a Table โ€” * and it carries the two things a screen needs and the notifier does not: is it * switched off, and may I switch it off. */ interface MySubscription extends Subscription { table_id: Uuid | null; /** Off. `custom.agg_subscriptions` already excludes a muted one, so this is the truth. */ muted: boolean; /** It is addressed to me. */ mine: boolean; /** I may switch it off: mine, or I hold admin on the Table it watches. */ i_may_mute: boolean; /** The hours it will not tell me in. Null means any time. */ quiet_hours: QuietHours | null; /** When this last told me anything at all. Null means it never has. */ last_sent_at: string | null; /** * When the next summary is due โ€” computed by the SAME store functions the runner * uses, so this and the moment it actually fires cannot drift. Null for `instant`, * which has no due time, and null for a muted one, because saying "next Monday" * beside a switch that is off is a screen that lies. */ next_digest_at: string | null; } /** * THE SUMMARY THIS SUBSCRIPTION WOULD SEND RIGHT NOW โ€” `custom.subscription_preview`. * * It assembles and returns: nothing is delivered, nothing is recorded and the * watermark does not move, so "show me one now" never costs somebody a text and * never robs the real Monday summary of a week of news. */ interface DigestPreview { rule_id: Uuid; name: string; cadence: string; schedule: string | null; quiet_hours: QuietHours | null; channel: string; recipient_user_id: Uuid | null; muted: boolean; table_id: Uuid | null; saved_view_id: Uuid | null; window_start: string; window_end: string; entered: Array<{ record_id: Uuid; name: string; }>; left: Array<{ record_id: Uuid; name: string; }>; changed: Array<{ record_id: Uuid; name: string; }>; counts: { entered: number; left: number; changed: number; in_view: number | null; }; /** The grid, filtered by the very view this subscription is over. */ link: string | null; subject: string; body: string; /** * NOTHING FAILS SILENTLY. A subscription that cannot produce a summary โ€” nobody to * tell, no view, a view somebody deleted โ€” says which piece is missing, in the * sentence a person reads, instead of returning an empty summary that looks calm. */ incomplete: string | null; } /** * ONE SAVED VIEW โ€” `custom.views`. A subscription is written OVER one of these, so * a person with no saved view has nothing to be told about; `viewDeclare` is how a * screen gives them one without leaving the screen they are on. * * `filters` is written in the ONE shape `custom.agg_view_admits` reads, so a view * saved here needs no translation before the notifier can admit records to it. */ /** * A ROW TEMPLATE (ROW-TEMPLATES, 2026-10-04) โ€” Notion's database templates: a named preset of a * Table's values ("New Instagram post": Status=Idea, Platform=[Instagram], Page content = a * Hook/Script/Hashtags skeleton) that "New from template" starts a row from. Kept beside the * Table's views in `platform.saved_view` (surface `custom/records/template`); `values` by Field key. */ interface RowTemplateRow { template_id: Uuid; name: string; table_id: Uuid; values: Record; created_at: string; updated_at: string; } interface SavedViewRow { view_id: Uuid; name: string; table_id: Uuid | null; filters: RecordFilter; /** * THE WHOLE VIEW (S0 ONE-SAVED-VIEW, 2026-09-23): `platform.saved_view` is the ONE saved-view * store, so this is what the view bar, the board, the calendar and the gallery draw from โ€” * `layout` (the kind), `group_field`, `measure`, `date_field`, `start_field`, `end_field`, * `image_field`, `sorts`, `rule_id`, * `is_default`, `presentation`, G7's `grid`, G13's `order`, the mover's `moved_from`. */ definition: SavedViewDefinition; created_at: string; } /** Every kind of view the store keeps (`custom.view_keys()` row `layout`). */ type SavedViewLayout = "grid" | "kanban" | "calendar" | "gallery" | "sheet" | "timeline"; /** `presentation.style.colorBy`: a choice or yes/no Field whose colors paint the cards, the calendar and the timeline. */ interface SavedViewColorBy { field: string; target?: string; } /** * THE KEYS OF ONE SAVED VIEW, as `custom.view_keys()` declares them. Fields are named by key * (the store accepts an id and stores the key). Every key the store judges is listed here; the * index signature keeps the rest (`grid`, `order`, `moved_from`, the presentation details) open. */ interface SavedViewDefinition { layout?: SavedViewLayout; /** The board's columns (kanban) or the timeline's rows: any Field. */ group_field?: string | null; swimlane_field?: string | null; measure?: string | null; /** Calendar: the date Field that places a record. */ date_field?: string | null; /** Timeline: the date Field where a record's bar starts. */ start_field?: string | null; /** Calendar and timeline: the date Field where a record ends. Absent, a record is one day. */ end_field?: string | null; /** Gallery: the Field drawn as the card's cover. */ image_field?: string | null; presentation?: { style?: { colorBy?: SavedViewColorBy | null; [key: string]: unknown; } | null; /** Gallery and grids: the Fields hidden from this view, by key. */ hiddenFields?: string[] | null; [key: string]: unknown; } | null; [key: string]: unknown; } /** * What `custom.view_declare` is asked for. With `view_id` the door MERGES: `name` changes only * when sent, `filters` only when the key is sent, each `definition` key replaces that one key and * a key sent as `null` is removed โ€” so a screen sends only what it changed. */ interface SavedViewSpecIn { name?: string; filters?: RecordFilter | null; definition?: SavedViewDefinition | Record | null; view_id?: Uuid | null; } /** What `custom.subscription_declare` is asked for. The store refuses the rest BY NAME. */ interface SubscriptionSpec { name: string; saved_view_id: Uuid; cadence: SubscriptionCadence; /** "monday 08:00", "09:30". Meaningless for `instant`. */ schedule?: string | null; channel?: "in_app" | "email" | "sms"; quiet_hours?: QuietHours | null; /** Left out, the person setting it up โ€” "tell ME". */ recipient_user_id?: Uuid | null; event_key?: string; /** Re-state an existing subscription rather than making a second one. */ rule_id?: Uuid | null; } /** The `subscription` block a Rule record carries. Written through `record_write`. */ interface SubscriptionBlock { saved_view_id: Uuid; cadence: SubscriptionCadence; schedule?: string | null; channel?: string; recipient_user_id?: Uuid | null; event_key?: string; } export { callersTimeZone as N, cellMatchesWords as O, filterWordsOf as P, hasQuietHours as T, liftWithheld as U, measureKey as V, stableFilterKey as W, withCallersClock as X, isRuleFilter as i }; export type { AggregateBucket as A, SavedViewDefinition as B, SavedViewLayout as C, DashboardBlockKind as D, SavedViewSpecIn as E, FilterWords as F, StoreFilter as G, StuckRow as H, InterfaceRead as I, Subscription as J, SubscriptionBlock as K, SubscriptionSpec as L, MySubscription as M, QuietHours as Q, RecordFilter as R, SubscriptionCadence as S, AggregateOperation as a, AggregateFilter as b, AggregateMeasure as c, SavedViewRow as d, AggregateCompare as e, AggregateCompareKind as f, AggregateCompareWindows as g, AggregateDelta as h, AggregateGroupValue as j, AggregateRow as k, AggregateWindow as l, DashboardBlock as m, DashboardBlockResult as n, DashboardRunResult as o, DashboardSummary as p, DashboardTarget as q, DashboardTargetBase as r, DashboardTargetOp as s, DashboardTargetResult as t, DashboardTotals as u, DigestPreview as v, InterfaceRows as w, InterfaceRowsField as x, RowTemplateRow as y, SavedViewColorBy as z };