{"version":3,"file":"itemId-CVrNibwT.cjs","names":[],"sources":["../src/collection/core/fieldText.ts","../src/collection/core/schema.ts","../src/collection/core/ids.ts","../src/collection/core/collectionKey.ts","../src/collection/core/serverTime.ts","../src/collection/core/accentColor.ts","../src/collection/core/calendarGrid.ts","../src/collection/core/itemId.ts"],"sourcesContent":["// Turning a record field into text.\n//\n// `CollectionItem` is `Record<string, unknown>`, so a field holds whatever the\n// record's JSON had — including arrays and objects (real workspace data has\n// plenty: weather `hourly`, GeoJSON `geometry`, `sites` lists). Bare\n// `String(value)` on one of those yields `\"[object Object]\"`, which then gets\n// compared, matched or displayed as if it were a value. Nothing throws; a\n// predicate just silently stops matching, or the UI shows `[object Object]`.\n//\n// These two helpers are the only sanctioned way to read a field as text. The\n// rule they encode was already in `itemLabelOf`: accept primitives, let\n// everything else fall through to the caller's fallback.\n\n/** A field value that has a meaningful text form. Dates are included because\n *  a JSON record can carry one once it has been revived. */\nconst isTextable = (value: unknown): value is string | number | boolean | Date =>\n  typeof value === \"string\" || typeof value === \"number\" || typeof value === \"boolean\" || value instanceof Date;\n\n/** The field's text, or `null` when it has no meaningful one (absent, or an\n *  array/object that would stringify to `\"[object Object]\"`).\n *\n *  Returning `null` rather than `\"\"` keeps \"the field is empty\" distinct from\n *  \"the field can't be text\" — a matcher must not treat an object-valued field\n *  as an empty string and match `\"\"`. */\nexport function fieldTextOrNull(value: unknown): string | null {\n  if (value === undefined || value === null) return null;\n  if (!isTextable(value)) return null;\n  if (value instanceof Date) {\n    // `new Date(\"nonsense\")` is still `instanceof Date`, and `toISOString()`\n    // throws `RangeError` on it. This helper sits on the match, sort and\n    // display paths, so one unparseable date in one record would take the whole\n    // render down — the loud version of the bug this module exists to prevent.\n    return Number.isNaN(value.getTime()) ? null : value.toISOString();\n  }\n  return String(value);\n}\n\n/** The field's text, or `fallback` (default `\"\"`) when it has none. Use where\n *  a string is required and an empty one is a safe stand-in — display, sort\n *  keys, CSV cells. Where the distinction matters, use {@link fieldTextOrNull}. */\nexport function fieldText(value: unknown, fallback = \"\"): string {\n  return fieldTextOrNull(value) ?? fallback;\n}\n","// Schema-driven collection types. A \"collection\" is a skill (under\n// .claude/skills/<slug>/) that also ships a sibling `schema.json`.\n// The host's <CollectionView> reads the schema + records and renders\n// a table/form; Claude reads SKILL.md and CRUDs the records as JSON\n// files.\n//\n// SINGLE SOURCE OF TRUTH: every type describing the schema.json contract is\n// derived (`z.infer`) from the zod definitions in `./schemaZ` — the shapes,\n// their doc comments, and the validation rules live THERE; this module only\n// re-derives the TypeScript names consumers import. The imports from\n// `./schemaZ` are type-only, so zod never reaches the browser bundle through\n// the isomorphic barrel; at runtime the dependency points the other way\n// (schemaZ imports this module's consts).\n//\n// Field specs are a DISCRIMINATED UNION on `type`: narrow with\n// `field.type === \"enum\"` (etc.) before reading a variant key like `values`,\n// `to`, `formula`, or `of`.\n\nimport { fieldText } from \"./fieldText\";\nimport type { z } from \"zod\";\nimport type {\n  ActionSpecZ,\n  CollectionSchemaZ,\n  CustomViewZ,\n  DataSourceZ,\n  DynamicIconRuleZ,\n  DynamicIconSourceZ,\n  DynamicIconSpecZ,\n  EveryFieldDrivenZ,\n  EveryLiteralZ,\n  EveryZ,\n  FieldSpecZ,\n  IngestZ,\n  SpawnZ,\n  StorageZ,\n  SubFieldSpecZ,\n  WhenZ,\n} from \"./schemaZ\";\n\n/** Minimal \"this collection is a feed\" descriptor carried on the schema.\n *  Deliberately narrow — the canonical collection contract stays\n *  independent of the host's feeds subsystem. The host's richer retrieval\n *  spec (`IngestSpec` in `feeds/ingestTypes.ts`) is a subtype, so feed code\n *  reads the extra fields by typing feed schemas with that subtype;\n *  collection rendering only needs these three + the presence check. */\nexport interface CollectionIngest {\n  kind: string;\n  schedule: string;\n  /** Optional time-of-day anchor for `schedule: \"daily\"` — the hour (0–23) to\n   *  refresh around (the host ticks hourly, so the run lands within that hour).\n   *  Ignored for non-daily schedules. Absent ⇒ elapsed-based daily (\"≥24 h since\n   *  the last run\"). NOTE: **UTC**, not local — compared via `getUTCHours()` for\n   *  an unambiguous, DST-free check (matching the rest of the scheduler), so\n   *  convert local times before writing (e.g. 07:00 JST → `atHour: 22`). */\n  atHour?: number;\n  /** Declarative retrievers (`rss`/`atom`/`http-json`) only — the host fetches\n   *  this URL on the schedule. Absent for `kind: \"agent\"`, where the agent owns\n   *  retrieval. */\n  url?: string;\n  /** `kind: \"agent\"` only: role id the scheduled hidden worker runs in. */\n  role?: string;\n  /** `kind: \"agent\"` only: skill-relative template path (under `templates/`)\n   *  whose prose tells the worker how to refresh the records. */\n  template?: string;\n}\n\n/** Declarative retriever kinds a Feed's `ingest.kind` may declare. The host's\n *  feeds engine dispatches on these; they live here (with the schema contract)\n *  so the schema validator can enforce them. The host re-exports these from\n *  `server/workspace/feeds/ingestTypes.ts`. */\nexport const INGEST_KINDS = [\"rss\", \"atom\", \"http-json\"] as const;\nexport type IngestKind = (typeof INGEST_KINDS)[number];\n\n/** The agent-performed ingest kind. Instead of a declarative fetch, the host\n *  dispatches a hidden background chat (origin `system`) in `ingest.role`,\n *  seeded with `ingest.template` + a summary of every record, on the\n *  `ingest.schedule` cadence; the worker edits records via the collections io\n *  layer. Kept separate from {@link INGEST_KINDS} (which the declarative\n *  retriever registry keys on) so the schema validator can model `ingest` as a\n *  discriminated union without the feeds engine gaining an \"agent\" retriever. */\nexport const AGENT_INGEST_KIND = \"agent\" as const;\nexport type AgentIngestKind = typeof AGENT_INGEST_KIND;\n\n/** Refresh cadences a Feed's `ingest.schedule` may declare. */\nexport const FEED_SCHEDULES = [\"hourly\", \"daily\", \"weekly\", \"on-demand\"] as const;\nexport type FeedSchedule = (typeof FEED_SCHEDULES)[number];\n\n// \"feed\" collections live in the non-skill `<workspace>/feeds/` registry\n// and carry an `ingest` block; they reuse the same storage + rendering\n// as skill-backed collections but are never loaded into the agent prompt.\nexport type CollectionSource = \"user\" | \"project\" | \"feed\";\n\n/** One field of a record — a discriminated union on `type`; see the variant\n *  docs in `./schemaZ` (`FieldSpecZ`). */\nexport type CollectionFieldSpec = z.infer<typeof FieldSpecZ>;\n\n/** A `table` field's row sub-schema entry — the field union minus `table` /\n *  `derived` / display-only types (see `SubFieldSpecZ`). */\nexport type CollectionSubFieldSpec = z.infer<typeof SubFieldSpecZ>;\n\nexport type CollectionFieldType = CollectionFieldSpec[\"type\"];\n\n/** The computed-boolean variant — a `where` predicate bound to a field\n *  name; see `FlagFieldZ`. */\nexport type CollectionFlagField = Extract<CollectionFieldSpec, { type: \"flag\" }>;\n\n/** derived/embed/backlinks/rollup/toggle/flag are host-computed or\n *  projected — never written to the record JSON, so required / value\n *  checks and edit-draft slots must not apply to them. THE single source\n *  for \"computed\" — lives here (zod-free at runtime) so browser code\n *  (`./draft`) and the zod record compiler (`./recordZ`, which re-exports\n *  it) share one set instead of drifting copies. */\nexport const COMPUTED_TYPES: ReadonlySet<CollectionFieldType> = new Set<CollectionFieldType>([\"derived\", \"embed\", \"backlinks\", \"rollup\", \"toggle\", \"flag\"]);\n\n/** Optional visibility predicate: the target (an action button or a\n *  field) renders only when the open record's `field` (stringified) is\n *  one of `in`. Generic and domain-free — the host evaluates it against\n *  the record with no knowledge of what the field means. Absent ⇒\n *  always shown. */\nexport type CollectionWhen = z.infer<typeof WhenZ>;\n\n/** @deprecated Name retained for back-compat; use {@link CollectionWhen}.\n *  Both actions and fields share the same predicate shape. No in-repo\n *  consumers, but the package is public API (MulmoTerminal). */\n// eslint-disable-next-line sonarjs/redundant-type-aliases -- deliberate deprecated back-compat export\nexport type CollectionActionWhen = CollectionWhen;\n\n/** A schema-declared, per-record action rendered as a button in the\n *  read-only detail view. Pure UI/behaviour directive — never stored,\n *  never validated against record data. All domain specifics (label,\n *  role, template — or the declarative `set`) live in the schema / skill\n *  folder, so the host stays generic. A discriminated union on `kind`;\n *  see `ActionSpecZ`. */\nexport type CollectionAction = z.infer<typeof ActionSpecZ>;\n\n/** The kind of work an action kicks off: `\"chat\"` (visible LLM chat),\n *  `\"agent\"` (hidden LLM worker), or `\"mutate\"` (declarative host write,\n *  no LLM). */\nexport type CollectionActionKind = CollectionAction[\"kind\"];\n\n/** The LLM-seeded action variants (`role` + `template`). */\nexport type CollectionSeededAction = Extract<CollectionAction, { kind: \"chat\" | \"agent\" }>;\n\n/** The declarative host-write variant (`set` + optional `require`/`params`). */\nexport type CollectionMutateAction = Extract<CollectionAction, { kind: \"mutate\" }>;\n\n/** A custom (LLM-authored) HTML view for a collection. The host renders\n *  `file` in a sandboxed iframe over the collection's records; the view\n *  reaches its data only through a slug- and capability-scoped token (see\n *  `server/api/auth/viewToken.ts`). Pure data — the host holds no\n *  view-specific code; meaning lives in the HTML file + this registration.\n *  See `CustomViewZ` for the per-key contracts. */\nexport type CollectionCustomView = z.infer<typeof CustomViewZ>;\n\n/** What a custom view's capability token is allowed to do against the\n *  collection's data endpoint. `read` returns enriched records (getItems\n *  semantics); `write` validates-and-stores rows (putItems semantics).\n *  There is deliberately no `delete` — a view can never do more than the\n *  agent's own `manageCollection` tool.\n *\n *  TWO HOST INVARIANTS ride on this token, and both are easy to break in a\n *  multi-root host:\n *\n *  1. **The token carries an OPAQUE scope, never a path.** A collection's\n *     identity is `(root, slug)`, so a token minted in a multi-root host has to\n *     say which root — but the token is signed, not encrypted, and is handed to\n *     an LLM-authored iframe. An absolute root in the payload publishes the\n *     user's home directory to that iframe. Mint an opaque project id and\n *     resolve it host-side.\n *  2. **The scope goes in the TOKEN, not on `dataUrl`.** `__MC_VIEW.dataUrl` is\n *     a bare base URL that views concatenate onto (`+ \"?fields=…\"`,\n *     `+ \"/query\"`, `+ \"/actions/…\"`, `+ \"/image?path=…\"` — see\n *     `assets/helps/custom-view.md`). A host that appends its own query\n *     parameter there to carry the project breaks every one of those calls. */\nexport type CollectionViewCapability = NonNullable<CollectionCustomView[\"capabilities\"]>[number];\n\n/** How a `spawn` advances the source item's `triggerField` date to\n *  produce the successor's. All arithmetic is done on the civil\n *  (year, month, day) triple — never by adding milliseconds — so month\n *  lengths and leap years are handled correctly. */\nexport type CollectionEvery = z.infer<typeof EveryLiteralZ>;\n\n/** Recurrence unit for a `spawn.every` advance. */\nexport type CollectionRecurUnit = CollectionEvery[\"unit\"];\n\n/** Field-driven recurrence: the advance interval is selected PER RECORD by\n *  the value of an `enum` field (`fromField`), looked up in `map`. See\n *  `EveryFieldDrivenZ`. */\nexport type CollectionEveryFieldDriven = z.infer<typeof EveryFieldDrivenZ>;\n\n/** The `every` of a `spawn`: either a single literal interval applied to\n *  every record, or a per-record interval selected by an `enum` field. The\n *  literal arm is what `advanceTriggerDate` consumes — the field-driven arm\n *  is resolved down to one of its `map` values before the date math runs. */\nexport type CollectionSpawnEvery = z.infer<typeof EveryZ>;\n\n/** Narrowing guard: true when `every` is the field-driven arm. */\nexport function isFieldDrivenEvery(every: CollectionSpawnEvery): every is CollectionEveryFieldDriven {\n  return \"fromField\" in every;\n}\n\n/** Host-driven recurrence. See `SpawnZ`. */\nexport type CollectionSpawn = z.infer<typeof SpawnZ>;\n\n/** One rule in a `dynamicIcon.rules` list: when the resolved source\n *  record matches `where` (an AND of typed conditions, see `./where`),\n *  the collection's effective launcher icon becomes `icon`. Evaluated top\n *  to bottom — the first match wins. */\nexport type DynamicIconRule = z.infer<typeof DynamicIconRuleZ>;\n\n/** Where a {@link DynamicIconSpec}'s source record comes from: a (possibly\n *  cross-collection) pool of records, optionally narrowed by `where` and\n *  reduced to a single record by `from`. */\nexport type DynamicIconSource = z.infer<typeof DynamicIconSourceZ>;\n\n/** Declarative \"data state → icon\" mapping for a collection's launcher\n *  shortcut icon (see `CollectionSchema.dynamicIcon`). When absent, the\n *  launcher icon is the static `schema.icon`. */\nexport type DynamicIconSpec = z.infer<typeof DynamicIconSpecZ>;\n\n/** The `ingest` block as the schema validator accepts it — a discriminated\n *  union on `kind` (declarative retrievers | agent worker). The feeds\n *  subsystem's `IngestSpec` is the same union under its historical name. */\nexport type CollectionIngestSpec = z.infer<typeof IngestZ>;\n\n/** The `dataSource` block: this collection's records are the rows of an\n *  external read-only data file (v1: CSV). See `DataSourceZ`. */\nexport type CollectionDataSource = z.infer<typeof DataSourceZ>;\n\n/** The `storage` block: an alternative WRITABLE record backend (v1:\n *  sqlite). See `StorageZ`. */\nexport type CollectionStorage = z.infer<typeof StorageZ>;\n\n/** Every storage backend a schema can select. `file` is the implicit\n *  default (`dataPath`); `csv` is implied by `dataSource`; other kinds are\n *  named explicitly via `storage.type`. The server's store factory registry\n *  (`server/store.ts`) is keyed by this. */\nexport type CollectionStorageKind = \"file\" | \"csv\" | \"sqlite\" | \"firestore\";\n\n/** Which storage backend serves this schema's records. Derived, not stored:\n *  existing schemas carry no `storage` key and must keep resolving exactly\n *  as before (`dataSource` ⇒ csv, else file). */\nexport function storageKindFor(schema: Pick<CollectionSchema, \"dataSource\" | \"storage\">): CollectionStorageKind {\n  if (schema.dataSource !== undefined) return \"csv\";\n  return schema.storage?.type ?? \"file\";\n}\n\n/** The whole `schema.json` contract. Key-level docs live on\n *  `CollectionSchemaZ` in `./schemaZ`. */\nexport type CollectionSchema = z.infer<typeof CollectionSchemaZ>;\n\n/** True when `schema` declares an external `dataSource` — i.e. the\n *  collection is READ-ONLY through every UI/tool write path (updates\n *  happen by editing/replacing the data file itself). Isomorphic: both\n *  the server write guards and the client's control hiding key off this\n *  one predicate. */\nexport function isReadOnlySchema(schema: Pick<CollectionSchema, \"dataSource\">): boolean {\n  return schema.dataSource !== undefined;\n}\n\nexport interface CollectionSummary {\n  slug: string;\n  title: string;\n  icon: string;\n  /** Optional accent colour name (see `ACCENT_COLORS`), drawn as a pale chip\n   *  behind the launcher glyph. Absent when the schema names none.\n   *\n   *  Declared NARROW (no `| undefined`), like its `appId` / `readonly`\n   *  siblings: the remote-host handlers pass these shapes through `Jsonify`,\n   *  which drops `undefined` from a member union, so a widened optional stops\n   *  being assignable there. Producers therefore ADD the key conditionally\n   *  rather than assigning `undefined` to it. */\n  color?: string;\n  source: CollectionSource;\n  /** Present (true) when the collection is backed by an external\n   *  `dataSource` and therefore read-only in every UI/tool write path.\n   *  Absent-when-writable, matching the other optional summary flags. */\n  readonly?: true;\n  /** Slugs of the source collection(s) a `dynamicIcon` icon was computed\n   *  from — present only when `schema.dynamicIcon` is set. Lets a client\n   *  know which collection change-channel(s) to watch for a live icon\n   *  update (see `useDynamicShortcutIcons`). */\n  iconSources?: string[];\n  /** The app a SHARED collection belongs to — present iff the schema declares\n   *  `storage.type: \"firestore\"`. A client needs it to subscribe to the right\n   *  live-change channel: a shared collection publishes on\n   *  `collection:app/<aid>/<cid>`, and a subscriber that keys on the name alone\n   *  listens to the LOCAL channel, so the refetch never arrives. Not a secret —\n   *  it is committed in the repository every clone reads. */\n  appId?: string;\n}\n\nexport interface CollectionDetail extends CollectionSummary {\n  schema: CollectionSchema;\n}\n\nexport type CollectionItem = Record<string, unknown>;\n\n/** Resolve an `embed` field's target record id: the fixed `id`, or the value\n *  of the sibling `idField` on this record (empty string when neither applies\n *  — the caller renders that as \"no record\"). Pure + isomorphic so the server\n *  projection (`derive.ts`) and the client preview (`useCollectionRendering`)\n *  resolve embeds identically. Non-`embed` fields resolve to \"no record\". */\nexport function embedTargetId(field: CollectionFieldSpec, record: CollectionItem | null): string {\n  if (field.type !== \"embed\") return \"\";\n  if (field.id) return field.id;\n  if (field.idField && record) return fieldText(record[field.idField]);\n  return \"\";\n}\n","// Pure slug / record-id character rules. Shared by the isomorphic schema\n// validator (`./schemaZ`) — which must stay node-free — and the server-side\n// path sanitisers (`../server/paths`), which wrap these patterns with the\n// `path.basename` round-trip CodeQL recognises as a `js/path-injection`\n// sanitiser. Both layers MUST gate on the same patterns; importing them from\n// here is what keeps them in sync.\n\n// The ONE slug pattern — `server/workspace/skills/catalog.ts` imports it\n// for its own sanitiser, so there is no second copy to keep in sync.\n// Bounded character classes, no nested quantifiers; ReDoS-safe.\nimport { fieldText } from \"./fieldText\";\nimport type { CollectionItem } from \"./schema\";\n\n// eslint-disable-next-line security/detect-unsafe-regex -- non-overlapping character classes, no catastrophic backtracking\nexport const SAFE_SLUG_PATTERN = /^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$/;\n\n// Record ids are a superset of slugs: they're only ever filename stems\n// (`<id>.json`), never directory names or URL segments, so they may carry\n// dots — natural keys like a Slack ts (`1718900000.123456`), a SemVer\n// (`1.2.3`), or a decimal timestamp. The interior class adds `.` to the slug\n// set; the explicit `..` reject in `isSafeRecordId` keeps a\n// parent-dir-looking segment out while still allowing repeated `-`/`_`\n// (`a--b`, `a__b`). Start/end stay alphanumeric so leading/trailing dots\n// (hidden files, the special `.`/`..` names) and `..`-only ids are all\n// excluded.\n// eslint-disable-next-line security/detect-unsafe-regex -- non-overlapping character classes, no catastrophic backtracking\nexport const SAFE_RECORD_ID_PATTERN = /^[a-zA-Z0-9](?:[a-zA-Z0-9_.-]*[a-zA-Z0-9])?$/;\n\n/** True when `value` is a well-formed collection slug (alphanumeric /\n *  hyphen / underscore, no path separators). The pattern admits no `/`,\n *  `\\`, or `.`, so a passing value is trivially also a safe basename —\n *  validation callers need no `path.basename` round-trip (path-building\n *  callers use `../server/paths#safeSlugName`, which adds it). */\nexport function isSafeSlug(value: string): boolean {\n  return typeof value === \"string\" && SAFE_SLUG_PATTERN.test(value);\n}\n\n/** True when `value` is a well-formed record id (slug charset plus interior\n *  dots), with any `..` substring rejected explicitly. Validation-only\n *  counterpart of `../server/paths#safeRecordId`. */\nexport function isSafeRecordId(value: string): boolean {\n  if (typeof value !== \"string\" || !SAFE_RECORD_ID_PATTERN.test(value)) return false;\n  return !value.includes(\"..\");\n}\n\nconst DEFAULT_UNIQUE_ID_ATTEMPTS = 8;\n\n/** Pick an id not already in `existing`, re-rolling `generate()` up to\n *  `maxAttempts` times before giving up and returning the last candidate.\n *  Collisions on a wide id space are astronomically unlikely, so a caller's\n *  own overwrite guard is the final backstop rather than an unbounded loop. */\nexport function generateUniqueId(existing: ReadonlySet<string>, generate: () => string, maxAttempts: number = DEFAULT_UNIQUE_ID_ATTEMPTS): string {\n  let candidate = generate();\n  for (let attempt = 0; attempt < maxAttempts && existing.has(candidate); attempt++) {\n    candidate = generate();\n  }\n  return candidate;\n}\n\n/** An id not already used by any loaded record: collects the in-memory primary\n *  keys and re-rolls `generate()` against them (see {@link generateUniqueId}).\n *  The pure core of the view's create-form id prefill. */\nexport function nextUniqueItemId(\n  items: readonly CollectionItem[],\n  primaryKey: string,\n  generate: () => string,\n  maxAttempts: number = DEFAULT_UNIQUE_ID_ATTEMPTS,\n): string {\n  const existing = new Set(items.map((item) => fieldText(item[primaryKey])));\n  return generateUniqueId(existing, generate, maxAttempts);\n}\n","// What a collection IS, as a value.\n//\n// Until now a collection's identity was `(root, slug)` — see the INVARIANT on\n// `CollectionHost` — and that is still exactly right for a collection that\n// lives in a directory. A SHARED collection does not: it is published to\n// Firestore under `apps/{aid}/collections/{cid}`, several machines resolve it,\n// and no one of their paths is its name. Keying it by the path it happened to\n// be published from would make the same collection two collections.\n//\n// The two identities COEXIST. A local collection's behaviour must not change at\n// all, so this is a discriminated union rather than a widening of either one:\n// every surface the INVARIANT enumerates — a cache, a pubsub channel, a view\n// token, a notification id, a rendered card — keys on this type, and the\n// compiler then refuses the thing that keeps happening by hand, which is keying\n// on the NAME alone (`slug` / `cid`) and letting two collections that share a\n// name collide.\n//\n// Isomorphic on purpose: a card and a channel name are decided on both sides of\n// the wire, so this module imports nothing from node.\n\nimport { SAFE_SLUG_PATTERN } from \"./ids\";\n\n/** A collection in a directory. `root` must already be canonical\n *  (`canonicalRoot`) — it is an identity here, not a path to read, and `/proj`\n *  vs `/proj/` would be two collections. Server callers should build these\n *  through `localCollectionKey` in `collection/server`, which canonicalises;\n *  the arm is spelled out here so the type itself stays isomorphic. */\nexport interface LocalCollectionKey {\n  kind: \"local\";\n  root: string;\n  slug: string;\n}\n\n/** A collection published to a shared app: `apps/{aid}/collections/{cid}`.\n *  `aid` is committed in the repository, so every clone resolves the same\n *  collection and an invitation is about authorization, never discovery. */\nexport interface SharedCollectionKey {\n  kind: \"shared\";\n  aid: string;\n  cid: string;\n}\n\nexport type CollectionKey = LocalCollectionKey | SharedCollectionKey;\n\nexport const isLocalCollectionKey = (key: CollectionKey): key is LocalCollectionKey => key.kind === \"local\";\nexport const isSharedCollectionKey = (key: CollectionKey): key is SharedCollectionKey => key.kind === \"shared\";\n\n/** Build a local key from an ALREADY-CANONICAL root — and refuse one that is\n *  not. See {@link scopePart}. */\nexport const localCollectionKeyOf = (root: string, slug: string): LocalCollectionKey => ({\n  kind: \"local\",\n  root: scopePart(root, \"root\"),\n  slug: namePart(slug, \"slug\"),\n});\n\n/** Build a shared key. */\nexport const sharedCollectionKey = (aid: string, cid: string): SharedCollectionKey => ({\n  kind: \"shared\",\n  aid: namePart(aid, \"aid\"),\n  cid: namePart(cid, \"cid\"),\n});\n\n/** The collection's NAME within its scope: the slug, or the shared `cid`.\n *\n *  This is what a schema file, a URL segment and a label are keyed by, and it\n *  is deliberately NOT enough to identify a collection — that is the whole\n *  point of the union. Use it to look things up INSIDE a known scope, never as\n *  a map key across scopes. */\nexport const collectionKeyName = (key: CollectionKey): string => (key.kind === \"local\" ? key.slug : key.cid);\n\n// NUL separates the parts: it cannot occur in a path, a slug, an app id or a\n// collection id, so the encoding is unambiguous and needs no escaping.\nconst SEP = \"\\u0000\";\n\n/** A ROOT: any non-empty string that can be encoded. A path's charset is the\n *  filesystem's, so the only thing to require is that it survives the encoding.\n *\n *  \"NUL cannot occur in a path\" is true of every real value and is the reason\n *  the encoding needs no escaping — but a type whose whole job is to be an\n *  identity must not take the claim on trust. Without this, `(\"a\\0b\", \"c\")`\n *  and `(\"a\", \"b\\0c\")` encode to the SAME string, so `sameCollectionKey` calls\n *  two different collections equal and the id parses back to nothing. Empty is\n *  refused for the same reason: it makes the id ambiguous about which part was\n *  missing.\n *\n *  A throw, where `parseCollectionKeyId` returns null: building a key is code\n *  making an identity, and a bad one there is a programming error. Parsing is\n *  reading something off a disk or a wire, where an unrecognised entry is a\n *  thing to skip. */\nfunction scopePart(value: string, field: string): string {\n  if (value.length === 0) throw new Error(`CollectionKey: ${field} must not be empty`);\n  if (value.includes(SEP)) throw new Error(`CollectionKey: ${field} must not contain NUL`);\n  if (!isCanonicalRootShape(value)) throw new Error(`CollectionKey: ${field} \"${value}\" is not a canonical root`);\n  return value;\n}\n\n/** Is this root ALREADY in the shape `canonicalRoot` (path.resolve) produces?\n *\n *  This module cannot canonicalise — that needs `node:path` and this file is\n *  isomorphic — so it does the other half: it REFUSES anything that is not\n *  already canonical. Which is the part that matters, because the failure is\n *  silent. `/work/proj/` and `/work/proj` are the same collection, and a key\n *  built from the first compares unequal to a key built from the second: two\n *  cache entries, two channels, two bells for one collection — exactly the\n *  identity split this type exists to remove. A decoder reading ids off a disk\n *  is where such a spelling arrives.\n *\n *  The properties are `path.resolve`'s own: absolute, no `.` or `..` segment,\n *  no doubled separator, no trailing one. The Windows drive form is allowed so\n *  a canonical root there is not refused. */\nfunction isCanonicalRootShape(root: string): boolean {\n  const windows = /^[A-Za-z]:\\\\/.test(root);\n  const sep = windows ? \"\\\\\" : \"/\";\n  if (!windows && !root.startsWith(\"/\")) return false;\n  if (root.length > 1 && root.endsWith(sep)) return false;\n  if (root.includes(sep + sep)) return false;\n  return !root.split(sep).some((segment) => segment === \".\" || segment === \"..\");\n}\n\n/** A NAME — a slug, a shared `cid`, or an `aid`: the collection-slug charset,\n *  `[a-zA-Z0-9]([a-zA-Z0-9_-]*[a-zA-Z0-9])?`.\n *\n *  THIS TYPE IS THE SINGLE SOURCE OF TRUTH for what a name may be, and that is\n *  the point rather than a nicety. A name is re-encoded by every downstream\n *  identity — the completion-bell id (`<scope>\\0<name>:<itemId>`, split at the\n *  first colon), a pubsub channel (`collection:app/<aid>/<name>`), a cache key\n *  — and each of those has a different character it cannot survive. With the\n *  rule stated only downstream, the layers disagree: a cid of `sales:2026`\n *  builds fine, then the bell id decodes as a DIFFERENT collection and the\n *  channel name throws inside a publisher whose catch swallows it, so the live\n *  update simply stops arriving. One rule here makes every encoding safe by\n *  construction; the checks downstream stay as belt-and-braces for callers that\n *  never went through a key.\n *\n *  A local slug is already `safeSlugName`-validated upstream, so this changes\n *  nothing for it. A shared `aid` / `cid` had no gate at all — they are\n *  Firestore document ids under `apps/{aid}/collections/{cid}` AND the name the\n *  same collection has on disk, so the slug charset is what they already are. */\nfunction namePart(value: string, field: string): string {\n  if (!isValidCollectionName(value)) throw new Error(`CollectionKey: ${field} \"${value}\" is not a valid collection name`);\n  return value;\n}\n\n/** The name rule as a predicate, for the encoders that take RAW strings rather\n *  than a key (the completion-bell id, a channel name). They are reachable by a\n *  caller that never built a key, and each has its own delimiter to be broken\n *  by, so they need to apply the same rule — not a rule of their own, which is\n *  how the layers came to disagree in the first place. */\nexport function isValidCollectionName(value: string): boolean {\n  return SAFE_SLUG_PATTERN.test(value);\n}\n\n/** A stable string form, for the places that need a primitive key: a Map, a\n *  pubsub channel name, a notification id, a card's reconciliation key.\n *\n *  Round-trips through {@link parseCollectionKeyId}. The `kind` is written\n *  first so a local key and a shared key can never collide however their parts\n *  are spelled. */\nexport const collectionKeyId = (key: CollectionKey): string =>\n  key.kind === \"local\" ? `local${SEP}${key.root}${SEP}${key.slug}` : `shared${SEP}${key.aid}${SEP}${key.cid}`;\n\n/** Decode a {@link collectionKeyId}, or `null` when the string did not come\n *  from one — or carries values no key may hold.\n *\n *  Decoding goes THROUGH the constructors. Building the union here directly\n *  would let a string off a disk or a wire mint an identity the constructors\n *  refuse: `shared\\0salon\\0sales:2026` would become a key whose name the\n *  completion-bell id and the pubsub channel cannot represent, and the single\n *  source of truth for a name would have a back door.\n *\n *  Null rather than a throw, and a throw from a constructor caught here: these\n *  strings are read back from storage, where an unrecognised or stale entry is\n *  a thing to skip, not a crash.\n *\n *  A non-canonical root is refused here too, for the same reason: `/work/proj/`\n *  and `/work/proj` name one collection, and two keys that compare unequal are\n *  two cache entries, two channels and two bells for it. This module cannot\n *  canonicalise (that needs `node:path`), so it rejects instead — see\n *  {@link scopePart}. */\nexport function parseCollectionKeyId(encoded: string): CollectionKey | null {\n  const parts = encoded.split(SEP);\n  if (parts.length !== 3) return null;\n  const [kind, first, second] = parts;\n  if (kind === undefined || first === undefined || second === undefined) return null;\n  try {\n    if (kind === \"local\") return localCollectionKeyOf(first, second);\n    if (kind === \"shared\") return sharedCollectionKey(first, second);\n  } catch {\n    return null;\n  }\n  return null;\n}\n\n/** Do two keys name the same collection? */\nexport const sameCollectionKey = (one: CollectionKey, other: CollectionKey): boolean => collectionKeyId(one) === collectionKeyId(other);\n","// The one shape a server-stamped instant takes, and the codec that keeps it\n// that way.\n//\n// A shared (firestore) collection can pin a field to the SERVER's clock:\n// `public.submit.<cid>.stampField` makes the rules require\n// `request.resource.data[field] == request.time` on create and freeze it\n// afterwards. What is stored is therefore a Firestore `Timestamp`, and that is\n// not negotiable — it is what stops somebody writing yesterday into the field\n// that decides who was first.\n//\n// Everything ABOVE the database wants a string. Two reasons, and the second is\n// the one that bites:\n//\n//   1. The collection DSL's `datetime` means a string; the record lint, the\n//      table and the calendar all read one.\n//   2. A `Timestamp` does not survive the trip to a page. Structured clone\n//      (mulmoserver's view channel) drops the CLASS and leaves\n//      `{ seconds, nanoseconds }`; JSON (MulmoTerminal's preview, over HTTP,\n//      and the headless run) leaves `{ type: \"firestore/timestamp/1.0\", … }`.\n//      `String()` of either is `\"[object Object]\"`, so a page that sorts by\n//      the field — which is exactly what a first-come app does — compares\n//      every row equal and falls back to whatever order it was handed. The\n//      bundled gym template shipped that sort, and its queue was ordered by\n//      document id rather than by time. Nothing errored; the ranks looked\n//      plausible.\n//\n// So the value is normalized ONCE, at the boundary, and the string is the only\n// thing anything above ever sees.\n//\n// THE FORM IS LOAD-BEARING, not cosmetic:\n//\n//   2026-08-15T01:45:54.605987654Z\n//\n//   - UTC, `Z`, and nothing else. RFC3339 allows `+09:00`, and two strings\n//     with different offsets do not sort in time order — which would give back\n//     the bug this module exists to remove.\n//   - NINE fractional digits, always. A `Timestamp` carries nanoseconds and\n//     Firestore's own order is (seconds, nanoseconds); rounding to\n//     milliseconds makes two submissions in the same millisecond compare\n//     equal, and same-millisecond is precisely the burst a first-come app is\n//     for. Fixed WIDTH matters as much as the precision, for a reason that is\n//     easy to get backwards: trimming TRAILING ZEROS is order-preserving\n//     (`.605` < `.61` either way), but MIXING widths is not — `…605Z` and\n//     `…605987654Z` compare at the fourth fraction character, where `Z` (90)\n//     is greater than any digit, so the millisecond value sorts AFTER a\n//     nanosecond value it is earlier than or equal to. One width, always.\n//   - Lexicographic order is therefore chronological order, which is what lets\n//     an author's page sort with a plain string compare and be right.\n//\n// TIES. Two records can still carry the same instant. The tie-break is the\n// DOCUMENT ID, and it comes for free rather than from code here: every read of\n// a shared collection is ordered by `__name__` (core's `firestoreDocs.list`\n// and mulmoserver's own reader both do it), and `Array.prototype.sort` is\n// stable, so equal keys keep that order in every host. Do not \"improve\" either\n// read into a field ordering without replacing this sentence.\n//\n// Design: mulmoterminal `plans/fix-shared-app-server-time.md` (D2/D3).\n\n/** The canonical instant, exactly: UTC, nine fractional digits, `Z`. */\nconst CANONICAL_RE = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{9}Z$/;\n\nconst NANOS_PER_SECOND = 1_000_000_000;\nconst MILLIS_PER_SECOND = 1000;\nconst NANOS_PER_MILLI = 1_000_000;\n\n/** What a Firestore `Timestamp` looks like once it is only data. Duck-typed on\n *  purpose: this module is pure and imports no SDK, because it runs where the\n *  class is already gone — a page's payload, a JSON body, a test. */\nexport interface ServerTimeParts {\n  seconds: number;\n  nanoseconds: number;\n}\n\nconst isFiniteInteger = (value: unknown): value is number => typeof value === \"number\" && Number.isInteger(value);\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => typeof value === \"object\" && value !== null;\n\n/** Carries the two numbers, whatever else it is. A guard rather than a cast:\n *  what arrives here is genuinely unknown — a page's payload, a JSON body, a\n *  document another client wrote. */\nconst isTimestampLike = (value: unknown): value is ServerTimeParts => isRecord(value) && isFiniteInteger(value.seconds) && isFiniteInteger(value.nanoseconds);\n\n/** Is this the canonical form, and a real instant?\n *\n *  The shape check alone is not enough, and the gap is not theoretical: with it,\n *  `2026-02-30T00:00:00.000000000Z` passes, the record lint reports nothing, and\n *  `Date.parse` then normalises it to March 2 — so a write/read round trip\n *  silently moves the value. The round trip below is the check: a string is\n *  canonical only when re-formatting what it parses to gives it back. */\nexport const isCanonicalServerTime = (value: unknown): value is string => {\n  if (typeof value !== \"string\" || !CANONICAL_RE.test(value)) return false;\n  // Firestore's own range starts at 0001-01-01; the four digits above already\n  // bound the other end. Year zero parses in JS and would only fail later, when\n  // the SDK is handed it — an exception at the write instead of a refusal here.\n  if (value.startsWith(\"0000-\")) return false;\n  const millis = Date.parse(`${value.slice(0, 19)}Z`);\n  if (Number.isNaN(millis)) return false;\n  return new Date(millis).toISOString().slice(0, 19) === value.slice(0, 19);\n};\n\n/** The canonical string for a (seconds, nanoseconds) pair, or null when the\n *  pair is not a real instant.\n *\n *  Built from the SECONDS through `Date` and the nanoseconds as text, never by\n *  turning nanoseconds into a float — `605987654 / 1e9` cannot be represented\n *  exactly, and a rounding error here is a rank in the wrong place. */\nexport function serverTimeOf(parts: ServerTimeParts): string | null {\n  const { seconds, nanoseconds } = parts;\n  if (!isFiniteInteger(seconds) || !isFiniteInteger(nanoseconds)) return null;\n  if (nanoseconds < 0 || nanoseconds >= NANOS_PER_SECOND) return null;\n  const whole = new Date(seconds * MILLIS_PER_SECOND);\n  if (Number.isNaN(whole.getTime())) return null;\n  const formatted = `${whole.toISOString().slice(0, 19)}.${String(nanoseconds).padStart(9, \"0\")}Z`;\n  // A year outside the four-digit range formats as `+275760-…`, which is not\n  // this form. Checked rather than assumed, because a caller that stored the\n  // result would be storing something nothing downstream recognises.\n  return isCanonicalServerTime(formatted) ? formatted : null;\n}\n\n/** A stored server time in any of the shapes it reaches us in, as the\n *  canonical string — or null when the value is not one at all.\n *\n *  The three shapes are the three trips a `Timestamp` can make: still an SDK\n *  instance (a host reading Firestore directly), structured-cloned (a page's\n *  payload), or JSON (`Timestamp.toJSON`, which tags itself). All three carry\n *  `seconds` and `nanoseconds`; the tag is not required, because the clone\n *  drops it. */\nexport function decodeServerTime(value: unknown): string | null {\n  if (!isTimestampLike(value)) return null;\n  return serverTimeOf(value);\n}\n\n/** The inverse: the parts a canonical string stands for, or null for anything\n *  else.\n *\n *  This is what closes the write-back hole. A record is read (decoded), edited,\n *  and written back WHOLE — `putItems`' upsert replaces the document — so\n *  without an encode the stamp would go back as a string. The rules freeze that\n *  field by comparing `diff().affectedKeys()`, so the write is refused and the\n *  record can never be updated again. Dropping the field instead does not help:\n *  a removed key is an affected key too.\n *\n *  Deliberately narrow. Only the EXACT canonical form is encoded, and that form\n *  is only ever produced by `decodeServerTime` — a human or an agent authoring\n *  a `datetime` writes the civil shape, which passes through as the string it\n *  is. So the codec is closed over its own output and cannot re-type somebody\n *  else's data. */\nexport function encodeServerTime(value: unknown): ServerTimeParts | null {\n  if (!isCanonicalServerTime(value)) return null;\n  const millis = Date.parse(`${value.slice(0, 19)}Z`);\n  if (Number.isNaN(millis)) return null;\n  const nanoseconds = Number(value.slice(20, 29));\n  return { seconds: Math.floor(millis / MILLIS_PER_SECOND), nanoseconds };\n}\n\n/** Epoch milliseconds for a canonical instant, for the places that can only\n *  hold a number (a sort key, a civil placement). LOSSY below the millisecond\n *  and knowingly so: whoever needs the full order compares the strings. */\nexport function serverTimeMillis(value: unknown): number | null {\n  const parts = encodeServerTime(value);\n  if (parts === null) return null;\n  return parts.seconds * MILLIS_PER_SECOND + Math.floor(parts.nanoseconds / NANOS_PER_MILLI);\n}\n\n/** The only part of a schema this codec reads.\n *\n *  Structural rather than `CollectionSchema`, because the OTHER host has its own\n *  type for the same document (mulmoserver reads these records without going\n *  through this package's store) and neither side should have to adopt the\n *  other's declaration to call one function. */\nexport interface DeclaredFields {\n  fields: Record<string, { type: string } | undefined>;\n}\n\n/** Which fields of a record may hold an instant: the ones the SCHEMA calls\n *  `datetime`, and no others.\n *\n *  DECLARATION, not shape. Recognising `{ seconds, nanoseconds }` wherever it\n *  appears would rewrite an ordinary value that happens to have those two keys\n *  — a duration, an offset, somebody's own metadata — into a datetime string,\n *  and then into a Firestore timestamp on the next whole-record write. Records\n *  are allowed to carry keys the schema never mentions, so that is live user\n *  data being re-typed on a guess. The same argument runs the other way on the\n *  write: a `string` field whose value happens to look canonical must stay a\n *  string.\n *\n *  A `datetime` field is the one place where both readings mean the same\n *  thing, which is what makes this scope the honest one.\n *\n *  WHY NOT NARROWER STILL — scoped to the app's `stampField`, the one field the\n *  rules pin. It was asked for, and it is not reachable from here: that name\n *  lives in `app.json` under `public.submit.<cid>`, which core reads only for\n *  `aid` (see `../server/appManifest`, \"SCOPE\") and which is the AUTHORED file\n *  rather than the published document the rules actually read. Getting it would\n *  mean a file read per write and crossing a boundary that module states\n *  deliberately.\n *\n *  And the narrower scope buys less than it looks. Within this backend a\n *  `datetime` field's canonical instant has ONE storage form — a timestamp —\n *  and the pair above is exact, so the value re-reads as the identical string\n *  for every reader. What changes for a non-stamped field is the stored TYPE,\n *  which matters only to a rule comparing it as a string; the rules that touch\n *  a datetime compare it to `request.time`, which is the stamped case and needs\n *  the timestamp. A field that must stay a string is declared `string`. */\nconst isDateTimeField = (schema: DeclaredFields, key: string): boolean => schema.fields[key]?.type === \"datetime\";\n\n/** Every stored instant in a `datetime` field, as its canonical string; every\n *  other value untouched. One level deep, which is where a stamped field\n *  lives — the rules stamp a top-level key and nothing writes a server time\n *  into a table row. */\nexport function decodeRecordTimes(record: Record<string, unknown>, schema: DeclaredFields): Record<string, unknown> {\n  let changed = false;\n  const out: Record<string, unknown> = {};\n  for (const [key, value] of Object.entries(record)) {\n    const decoded = isDateTimeField(schema, key) ? decodeServerTime(value) : null;\n    if (decoded !== null) changed = true;\n    out[key] = decoded ?? value;\n  }\n  // The SAME object when nothing was stamped, so a reference comparison\n  // upstream keeps meaning what it meant.\n  return changed ? out : record;\n}\n\n/** The write half, and it encodes ONLY what was stored as an instant.\n *\n *  `previous` is the document as Firestore holds it right now. That is the only\n *  honest source of provenance here: a decoded stamp is a plain string by the\n *  time it comes back, indistinguishable from one an author typed or an import\n *  produced, and no amount of looking at the string can tell them apart. The\n *  declaration that pins the field lives in `app.json` under\n *  `public.submit.<cid>`, which this package reads only for `aid`\n *  (`../server/appManifest`, \"SCOPE\") — so the stored value is what is left, and\n *  it answers exactly the right question: was this field an instant before?\n *\n *  What that buys, concretely: the frozen stamp goes back unchanged, so a\n *  whole-record write survives the rules; and a `datetime` field holding a\n *  canonical-looking string that was NEVER an instant — imported data, a value\n *  somebody typed — stays the string it is. The format alone would have\n *  re-typed it.\n *\n *  `previous` is null for a create, where nothing was stored and nothing can\n *  therefore be preserved. That is correct rather than a gap: the rules require\n *  a created stamp to equal `request.time`, which no client can construct, so a\n *  create through this store never carries a valid one.\n *\n *  `toStored` builds whatever the backend wants for an instant — a Firestore\n *  `Timestamp` — and is injected so this module stays free of the SDK: it also\n *  runs in a browser, where importing that would be a bundle. */\nexport function encodeRecordTimes(\n  record: Record<string, unknown>,\n  previous: Record<string, unknown> | null,\n  toStored: (parts: ServerTimeParts) => unknown,\n): Record<string, unknown> {\n  let changed = false;\n  const out: Record<string, unknown> = {};\n  for (const [key, value] of Object.entries(record)) {\n    const wasInstant = previous !== null && isTimestampLike(previous[key]);\n    const parts = wasInstant ? encodeServerTime(value) : null;\n    if (parts !== null) changed = true;\n    out[key] = parts === null ? value : toStored(parts);\n  }\n  return changed ? out : record;\n}\n","// A collection's / feed's optional accent colour, drawn as a pale chip behind\n// its launcher glyph (#2987).\n//\n// The point is telling look-alike shortcuts apart when they share a generic\n// glyph — `podcasts`, `rss_feed`, `menu_book` are all thin monochrome outlines\n// at 16px, and a pill of them reads as one texture (#2960). Measured against\n// the alternative: tinting only the glyph leaves too little coloured area to\n// register at that size; a pale background does.\n//\n// Tailwind only detects class names that appear as complete string literals,\n// so each colour's classes are spelled out here rather than built from the\n// colour name at runtime — the same rule `enumColors.ts` follows, and for the\n// same reason.\n\n/** The colours a schema may name, ordered around the hue circle.\n *\n *  Deliberately EXCLUDES the warm band (red / orange / amber): those are the\n *  notification severities (`ENUM_ALERT` red, `ENUM_NUDGE` amber in\n *  `enumColors.ts`) and the launcher's own session badges, so an accent can\n *  never make a shortcut read as \"something needs attention\".\n *\n *  Seven rather than eight: rendering all eight candidates on one identical\n *  glyph put `cyan` squarely between `sky` and `teal`, so a pill could hold\n *  three shortcuts in the same blue-green band. A colour that cannot be told\n *  from its neighbour identifies nothing, which is the entire job here — the\n *  set is sized for separation, not for count. */\nexport const ACCENT_COLORS = [\"violet\", \"indigo\", \"sky\", \"teal\", \"emerald\", \"lime\", \"fuchsia\"] as const;\n\nexport type AccentColor = (typeof ACCENT_COLORS)[number];\n\n// Background + glyph colour as one literal per entry. The 50/700 pair, chosen\n// by rendering it: at 16px the GLYPH colour does most of the identifying and the\n// 50-level wash is what separates an accented shortcut from a plain white one.\n// A heavier fill would make the accent, rather than the icon, the loudest thing\n// in a chrome row that is otherwise deliberately quiet.\nconst CHIP_CLASSES: Readonly<Record<AccentColor, string>> = {\n  violet: \"bg-violet-50 text-violet-700\",\n  indigo: \"bg-indigo-50 text-indigo-700\",\n  sky: \"bg-sky-50 text-sky-700\",\n  teal: \"bg-teal-50 text-teal-700\",\n  emerald: \"bg-emerald-50 text-emerald-700\",\n  lime: \"bg-lime-50 text-lime-700\",\n  fuchsia: \"bg-fuchsia-50 text-fuchsia-700\",\n};\n\n/** Narrowing guard: is `value` one of the colours a schema may name? */\nexport function isAccentColor(value: unknown): value is AccentColor {\n  return typeof value === \"string\" && Object.hasOwn(CHIP_CLASSES, value);\n}\n\n/** The chip classes for `color`, or `null` when there is no accent to draw —\n *  absent, blank, or a name this palette does not carry.\n *\n *  Unknown names fail soft rather than throwing, matching how `icon` is\n *  handled: a schema is user/LLM-authored, and a typo should cost the colour,\n *  not the surface. The caller falls back to its own unstyled treatment. */\nexport function accentChipClasses(color: string | undefined): string | null {\n  if (!isAccentColor(color)) return null;\n  return CHIP_CLASSES[color];\n}\n","// Pure, deterministic helpers for the collection calendar view: parse\n// `date`-field values, build a month grid, and bucket records onto the\n// days they cover. No `Date.now()` / `new Date()` (argless) here — every\n// function takes its inputs explicitly so the logic is unit-testable\n// without faking the clock. All internal arithmetic runs in UTC (which\n// has no DST), so fixed 86_400_000 ms steps never skip or double a day.\n//\n// ONE deliberate exception, `localCivilOf`: a server-stamped instant is a point\n// in time rather than a wall clock, so asking which day it lands on is a\n// question about the reader's zone and cannot be answered in UTC without\n// answering it wrongly for most readers. It is the only function here whose\n// result depends on the environment, and its tests pin TZ.\n\nimport { serverTimeMillis } from \"./serverTime\";\n\nconst MS_PER_DAY = 86_400_000;\nconst ISO_DATE_RE = /^(\\d{4})-(\\d{2})-(\\d{2})$/;\n// A two-digit field (hours / minutes / seconds) of a clock value.\nconst TWO_DIGIT_RE = /^\\d{2}$/;\n// A single clock token inside a free-form `time` string field (e.g. the\n// \"14:00-17:00\" / \"17:00-\" / \"16:30\" / \"終日\" shapes seen in user data).\nconst CLOCK_RE = /(\\d{1,2}):(\\d{2})/g;\n// Range separators we tolerate between two clock tokens: ASCII hyphen, en/em\n// dash, tilde, and the Japanese wave dashes.\nconst RANGE_SEP_RE = /[-–—~〜～]/;\n\n/** Minutes in a full day — the timeline's vertical extent. */\nexport const MINUTES_PER_DAY = 1440;\n\n/** A civil date triple. `month` is 1-12 (NOT the 0-based `Date` month). */\nexport interface Ymd {\n  year: number;\n  month: number;\n  day: number;\n}\n\n/** One cell of the 6×7 month grid. */\nexport interface DayCell {\n  ymd: Ymd;\n  /** False for the leading/trailing days that belong to the adjacent\n   *  month (rendered greyed). */\n  inMonth: boolean;\n  /** Canonical `YYYY-MM-DD` key for this cell. */\n  key: string;\n}\n\n/** A record placed on the calendar: the inclusive `[start, end]` span of\n *  days it covers. `end === start` for a single-day record. `startMin` /\n *  `endMin` are minutes-of-day for the time-allocation (day) view, resolved\n *  from either a `datetime` field's clock or a separate time-string field.\n *  `null` means \"no clock\" — `startMin === null && endMin === null` is an\n *  all-day record; a non-null `startMin` with a null `endMin` is a\n *  point-in-time record (rendered as a single line). */\nexport interface RecordSpan<T> {\n  item: T;\n  start: Ymd;\n  end: Ymd;\n  startMin: number | null;\n  endMin: number | null;\n}\n\nfunction pad2(value: number): string {\n  return String(value).padStart(2, \"0\");\n}\n\n/** Canonical `YYYY-MM-DD` string for a civil date. */\nexport function ymdKey(ymd: Ymd): string {\n  return `${String(ymd.year).padStart(4, \"0\")}-${pad2(ymd.month)}-${pad2(ymd.day)}`;\n}\n\n/** Strictly parse a `YYYY-MM-DD` string into a civil date, rejecting\n *  anything that isn't a real calendar day (e.g. `2026-02-30`, `2026-13-01`).\n *  Returns null for non-strings and malformed values so callers can route\n *  records with no usable date into the \"no date\" tray rather than crash. */\nexport function parseIsoDate(value: unknown): Ymd | null {\n  if (typeof value !== \"string\") return null;\n  const match = ISO_DATE_RE.exec(value.trim());\n  if (!match) return null;\n  const year = Number(match[1]);\n  const month = Number(match[2]);\n  const day = Number(match[3]);\n  // Round-trip through a UTC Date to reject impossible days: a value the\n  // Date constructor rolls over (Feb 30 → Mar 2) won't match back.\n  const probe = new Date(Date.UTC(year, month - 1, day));\n  if (probe.getUTCFullYear() !== year || probe.getUTCMonth() !== month - 1 || probe.getUTCDate() !== day) return null;\n  return { year, month, day };\n}\n\n/** Minutes-of-day for an `HH:MM` pair, or null when out of range. */\nfunction clockToMinutes(hours: number, minutes: number): number | null {\n  if (hours < 0 || hours > 23 || minutes < 0 || minutes > 59) return null;\n  return hours * 60 + minutes;\n}\n\n/** Strictly parse a `YYYY-MM-DDTHH:MM` (optional `:SS`) datetime into its\n *  civil date and minutes-of-day. Returns null for anything that isn't a real\n *  calendar day or a valid 24h clock. */\nexport function parseIsoDateTime(value: unknown): { ymd: Ymd; minutes: number } | null {\n  if (typeof value !== \"string\") return null;\n  const trimmed = value.trim();\n  const tIndex = trimmed.indexOf(\"T\");\n  if (tIndex === -1) return null;\n  const ymd = parseIsoDate(trimmed.slice(0, tIndex));\n  if (!ymd) return null;\n  // `HH:MM` with an optional `:SS` the browser appends for non-zero seconds.\n  const parts = trimmed.slice(tIndex + 1).split(\":\");\n  if (parts.length < 2 || parts.length > 3 || !parts.every((part) => TWO_DIGIT_RE.test(part))) return null;\n  const minutes = clockToMinutes(Number(parts[0]), Number(parts[1]));\n  if (minutes === null) return null;\n  return { ymd, minutes };\n}\n\n/** A server-stamped instant as the civil time it happened at HERE.\n *\n *  `parseIsoDateTime` stays strict and stays civil: it is what the grid, the\n *  trigger and the spawn code parse, and teaching it to swallow an absolute\n *  instant would mean placing an instant on a civil grid with no zone — the\n *  thing its strictness prevents. So the conversion happens at the placement\n *  instead, and it happens ONCE.\n *\n *  LOCAL rather than UTC, deliberately. An instant is a point in time, and the\n *  question the grid answers is \"which of MY days was that\". A Tokyo evening\n *  booking sits on the correct day for the person in Tokyo and on the previous\n *  one in UTC. The cost is that two people in different zones can see the same\n *  record on different days — which is what an absolute instant honestly means,\n *  where a civil `YYYY-MM-DDTHH:MM` means the same wall clock for everyone. */\nfunction localCivilOf(value: unknown): { ymd: Ymd; minutes: number } | null {\n  const millis = serverTimeMillis(value);\n  if (millis === null) return null;\n  const instant = new Date(millis);\n  return {\n    ymd: { year: instant.getFullYear(), month: instant.getMonth() + 1, day: instant.getDate() },\n    minutes: instant.getHours() * 60 + instant.getMinutes(),\n  };\n}\n\n/** Civil date from a `YYYY-MM-DD`, a `YYYY-MM-DDTHH:MM`, or a server-stamped\n *  instant, so the month grid buckets all three alike. */\nexport function dateOf(value: unknown): Ymd | null {\n  return parseIsoDate(value) ?? parseIsoDateTime(value)?.ymd ?? localCivilOf(value)?.ymd ?? null;\n}\n\n/** Minutes-of-day from a datetime value, or null for date-only / invalid. */\nfunction timeOf(value: unknown): number | null {\n  return parseIsoDateTime(value)?.minutes ?? localCivilOf(value)?.minutes ?? null;\n}\n\n/** Parse a free-form time-string field into start/end minutes-of-day.\n *  Handles the common shapes in user data:\n *    \"14:00-17:00\" → { start: 840, end: 1020 }   (range → block)\n *    \"17:00-\"      → { start: 1020, end: null }   (open end → single line)\n *    \"16:30\"       → { start: 990, end: null }    (point in time → single line)\n *    \"終日\" / \"\"   → null                          (no clock → all-day)\n *  Returns null when no clock token is parseable. */\nexport function parseTimeRange(value: unknown): { startMin: number | null; endMin: number | null } | null {\n  if (typeof value !== \"string\") return null;\n  const text = value.trim();\n  if (!text) return null;\n  const tokens = [...text.matchAll(CLOCK_RE)];\n  if (tokens.length === 0) return null;\n  const minutesOf = (match: RegExpMatchArray): number | null => clockToMinutes(Number(match[1]), Number(match[2]));\n  // No separator → a single point in time (start only).\n  if (!RANGE_SEP_RE.test(text)) {\n    const [firstToken] = tokens;\n    const startMin = firstToken ? minutesOf(firstToken) : null;\n    return startMin === null ? null : { startMin, endMin: null };\n  }\n  // Separator present → assign each token to the side of the first separator.\n  const sepIndex = text.search(RANGE_SEP_RE);\n  let startMin: number | null = null;\n  let endMin: number | null = null;\n  for (const token of tokens) {\n    if ((token.index ?? 0) < sepIndex) startMin = minutesOf(token);\n    else endMin = minutesOf(token);\n  }\n  // A start-less range (\"-09:00\") has no anchor on the timeline → treat as\n  // unparseable so the record falls back to the all-day strip.\n  if (startMin === null) return null;\n  return { startMin, endMin };\n}\n\nfunction ymdToUtcMs(ymd: Ymd): number {\n  return Date.UTC(ymd.year, ymd.month - 1, ymd.day);\n}\n\nfunction utcMsToYmd(epochMs: number): Ymd {\n  const date = new Date(epochMs);\n  return { year: date.getUTCFullYear(), month: date.getUTCMonth() + 1, day: date.getUTCDate() };\n}\n\n/** Chronological comparison: negative if `left` precedes `right`, 0 if the\n *  same day, positive if after. */\nexport function compareYmd(left: Ymd, right: Ymd): number {\n  return ymdToUtcMs(left) - ymdToUtcMs(right);\n}\n\n/** True iff `day` falls within the inclusive span `[span.start, span.end]`. */\nexport function spanCoversDay<T>(span: RecordSpan<T>, day: Ymd): boolean {\n  return compareYmd(span.start, day) <= 0 && compareYmd(day, span.end) <= 0;\n}\n\n/** Build the 6×7 (42-cell) grid for the given month, including the\n *  leading/trailing days of the adjacent months so every week is full.\n *  `month` is 1-12. `weekStartsOn` is 0 (Sunday) … 6 (Saturday). */\nexport function buildMonthGrid(year: number, month: number, weekStartsOn = 0): DayCell[] {\n  const firstWeekday = new Date(Date.UTC(year, month - 1, 1)).getUTCDay();\n  const lead = (firstWeekday - weekStartsOn + 7) % 7;\n  const startMs = Date.UTC(year, month - 1, 1) - lead * MS_PER_DAY;\n  const cells: DayCell[] = [];\n  for (let i = 0; i < 42; i++) {\n    const ymd = utcMsToYmd(startMs + i * MS_PER_DAY);\n    cells.push({ ymd, inMonth: ymd.year === year && ymd.month === month, key: ymdKey(ymd) });\n  }\n  return cells;\n}\n\n/** Resolve a record's calendar span from its date/datetime fields. Returns\n *  null when the anchor date is missing/invalid (→ the caller's \"no date\"\n *  tray). An end date that is missing, invalid, or earlier than the start\n *  collapses to a single-day span — never an inverted range.\n *\n *  Times for the day (time-allocation) view come from, in priority order:\n *    1. the clock on a `datetime` anchor/end value, else\n *    2. `timeField` — a separate free-form time-string column (e.g. \"14:00-17:00\").\n *  A record with no resolvable clock has `startMin === endMin === null`. */\nexport function recordSpan<T extends Record<string, unknown>>(\n  item: T,\n  anchorField: string,\n  endField?: string,\n  timeField?: string,\n  options: SpanOptions = {},\n): RecordSpan<T> | null {\n  const dated = dateSpan(item, anchorField, endField);\n  if (!dated) return null;\n  // The day boundary is judged from the end FIELD, before a separate time\n  // column can supply a clock the end field never had.\n  const bounded = options.endExclusive && endField !== undefined ? withExclusiveEnd(dated, item[endField]) : dated;\n  return withTimeFieldFallback(bounded, timeField ? item[timeField] : undefined);\n}\n\n/** How a collection reads its end field. */\nexport interface SpanOptions {\n  /** The end names the first moment AFTER the record, as Google Calendar's\n   *  `end` does: an all-day event on the 17th ends on the 18th. */\n  endExclusive?: boolean;\n}\n\n/** The span options a collection's calendar uses for `endField`. Only a\n *  Google Calendar mirror reads its end as exclusive — the field its sync maps\n *  to `end` — because a plain collection may mean its end date inclusively. */\nexport function spanOptionsFor(schema: { googleCalendar?: { map: Record<string, string> } | undefined }, endField: string | undefined): SpanOptions {\n  const mappedTo = endField === undefined ? undefined : schema.googleCalendar?.map[endField];\n  return { endExclusive: mappedTo === \"end\" };\n}\n\n/** `…T00:00` or `…T00:00:00` — midnight to the second. `timeOf` keeps minutes\n *  only, so `…T00:00:30` would otherwise read as midnight too. */\nconst MIDNIGHT_CLOCK_RE = /T00:00(?::00)?$/;\n\n/** Whether an end value names the very start of its day: a bare date, or a\n *  datetime at exactly `00:00`. */\nexport function endsAtDayBoundary(endRaw: unknown): boolean {\n  if (parseIsoDate(endRaw) !== null) return true;\n  return typeof endRaw === \"string\" && parseIsoDateTime(endRaw) !== null && MIDNIGHT_CLOCK_RE.test(endRaw.trim());\n}\n\n/** An exclusive end that falls at a day boundary on a later day does not\n *  occupy that day, so the span stops on the day before. An end with any other\n *  clock does occupy its day and is left alone. `endRaw` is the end field's\n *  own value — the span may already carry a clock from elsewhere. */\nexport function withExclusiveEnd<T>(span: RecordSpan<T>, endRaw: unknown): RecordSpan<T> {\n  if (!endsAtDayBoundary(endRaw) || compareYmd(span.end, span.start) <= 0) return span;\n  const end = utcMsToYmd(ymdToUtcMs(span.end) - MS_PER_DAY);\n  // A clocked span runs to the end of its new last day; a clock-less one stays\n  // all day. The start decides too: a `datetime` start with a bare `date` end.\n  const clocked = span.endMin === 0 || span.startMin !== null;\n  return { ...span, end, endMin: clocked ? MINUTES_PER_DAY : null };\n}\n\n/** The span the date fields alone describe: an end that is missing, invalid,\n *  or earlier than the start collapses to the start day. */\nfunction dateSpan<T extends Record<string, unknown>>(item: T, anchorField: string, endField?: string): RecordSpan<T> | null {\n  const startRaw = item[anchorField];\n  const start = dateOf(startRaw);\n  if (!start) return null;\n  const endRaw = endField ? item[endField] : undefined;\n  const parsedEnd = dateOf(endRaw);\n  const usableEnd = parsedEnd !== null && compareYmd(parsedEnd, start) >= 0 ? parsedEnd : null;\n  return { item, start, end: usableEnd ?? start, startMin: timeOf(startRaw), endMin: usableEnd ? timeOf(endRaw) : null };\n}\n\n/** Fall back to a separate time-string field only when the date fields\n *  carried no clock (the date-only anchor + `time` column shape). */\nfunction withTimeFieldFallback<T>(span: RecordSpan<T>, timeValue: unknown): RecordSpan<T> {\n  if (span.startMin !== null || span.endMin !== null) return span;\n  const range = parseTimeRange(timeValue);\n  return range ? { ...span, startMin: range.startMin, endMin: range.endMin } : span;\n}\n\n/** Split records into those that land on the calendar (with their spans)\n *  and those with no usable anchor date (the \"no date\" tray). Spans are\n *  sorted by start day so same-day stacking is stable across renders. */\nexport function bucketRecords<T extends Record<string, unknown>>(\n  items: readonly T[],\n  anchorField: string,\n  endField?: string,\n  timeField?: string,\n  options: SpanOptions = {},\n): { spans: RecordSpan<T>[]; noDate: T[] } {\n  const spans: RecordSpan<T>[] = [];\n  const noDate: T[] = [];\n  for (const item of items) {\n    const span = recordSpan(item, anchorField, endField, timeField, options);\n    if (span) spans.push(span);\n    else noDate.push(item);\n  }\n  spans.sort((left, right) => compareYmd(left.start, right.start));\n  return { spans, noDate };\n}\n\n/** Geometry for one record on one day of the time-allocation view.\n *  `kind`:\n *    \"allDay\" — no clock anywhere → render in the bottom all-day strip.\n *    \"line\"   — a single point in time → a 1px marker at `startMin`.\n *    \"block\"  — a [startMin, endMin) span, clamped to this day's [0, 1440).\n *  `bleedsBefore` / `bleedsAfter` flag a multi-day span that began on an\n *  earlier day or continues onto a later one (so the view can show arrows). */\nexport interface DaySlice {\n  kind: \"allDay\" | \"line\" | \"block\";\n  startMin: number;\n  endMin: number;\n  bleedsBefore: boolean;\n  bleedsAfter: boolean;\n}\n\n/** Project a record's span onto a single day for the time-allocation view, or\n *  null when the span doesn't cover that day. */\nexport function daySlice<T>(span: RecordSpan<T>, day: Ymd): DaySlice | null {\n  if (!spanCoversDay(span, day)) return null;\n  // Destructured so the `!== null` tests narrow the clocks where they are used;\n  // reading them back off `span` each time would not.\n  const { startMin: spanStart, endMin: spanEnd } = span;\n  if (spanStart === null && spanEnd === null) {\n    return { kind: \"allDay\", startMin: 0, endMin: MINUTES_PER_DAY, bleedsBefore: false, bleedsAfter: false };\n  }\n  const singleDay = compareYmd(span.start, span.end) === 0;\n  const isStartDay = compareYmd(day, span.start) === 0;\n  const isEndDay = compareYmd(day, span.end) === 0;\n  // A point in time: a start clock with no end, all on one day.\n  if (singleDay && spanStart !== null && spanEnd === null) {\n    return { kind: \"line\", startMin: spanStart, endMin: spanStart, bleedsBefore: false, bleedsAfter: false };\n  }\n  const startMin = isStartDay && spanStart !== null ? spanStart : 0;\n  const endMin = isEndDay && spanEnd !== null ? spanEnd : MINUTES_PER_DAY;\n  // Zero-length or inverted same-day range → degrade to a line.\n  if (singleDay && endMin <= startMin) {\n    return { kind: \"line\", startMin, endMin: startMin, bleedsBefore: false, bleedsAfter: false };\n  }\n  return { kind: \"block\", startMin, endMin, bleedsBefore: !isStartDay, bleedsAfter: !isEndDay };\n}\n\n/** Side-by-side lane assignment for overlapping timeline blocks. Each input\n *  is an `[startMin, endMin)` interval; the result (parallel to the input)\n *  gives each item its `lane` (column index) and the `lanes` total of its\n *  overlap cluster, so a renderer can size every block to `1 / lanes` width\n *  and offset it by `lane / lanes`. Non-overlapping items get `lanes === 1`. */\nexport interface LaneSpan {\n  startMin: number;\n  endMin: number;\n}\nexport interface LaneAssignment {\n  lane: number;\n  lanes: number;\n}\n\n/** One input block paired with its position in the caller's array, so lane\n *  assignment can sort freely and still report back in input order. */\ninterface PositionedSpan {\n  index: number;\n  span: LaneSpan;\n}\n\nfunction sortByStart(blocks: readonly LaneSpan[]): PositionedSpan[] {\n  return [...blocks.entries()]\n    .map(([index, span]) => ({ index, span }))\n    .sort((left, right) => left.span.startMin - right.span.startMin || left.span.endMin - right.span.endMin);\n}\n\ninterface ClusterState {\n  /** Clusters already closed. */\n  done: PositionedSpan[][];\n  /** The cluster still accepting blocks. */\n  current: PositionedSpan[];\n  /** Latest end minute seen in `current` — the cutoff for the next block. */\n  end: number;\n}\n\n/** Cut the start-ordered blocks into overlap clusters: a new cluster begins at\n *  the first block that starts at or after every earlier block has ended. */\nfunction splitClusters(ordered: readonly PositionedSpan[]): PositionedSpan[][] {\n  // Appends into the accumulator's arrays rather than rebuilding them. The\n  // spread form read well but copied both arrays on every block, so a day\n  // whose blocks all overlap cost O(n²) allocations to lay out (#2765).\n  const initial: ClusterState = { done: [], current: [], end: Number.NEGATIVE_INFINITY };\n  const state = ordered.reduce<ClusterState>((acc, block) => {\n    if (acc.current.length > 0 && block.span.startMin >= acc.end) {\n      acc.done.push(acc.current);\n      return { done: acc.done, current: [block], end: block.span.endMin };\n    }\n    acc.current.push(block);\n    return { done: acc.done, current: acc.current, end: Math.max(acc.end, block.span.endMin) };\n  }, initial);\n  if (state.current.length > 0) state.done.push(state.current);\n  return state.done;\n}\n\n/** Greedy lane packing inside one cluster: reuse the first lane already free at\n *  this block's start, else open a new one. Every member reports the cluster's\n *  final lane count so a renderer can size each block to `1 / lanes`. */\nfunction packCluster(cluster: readonly PositionedSpan[]): [number, LaneAssignment][] {\n  const laneEnds: number[] = [];\n  const placed = cluster.map(({ index, span }) => {\n    const reusable = laneEnds.findIndex((end) => end <= span.startMin);\n    const lane = reusable === -1 ? laneEnds.length : reusable;\n    laneEnds[lane] = span.endMin;\n    return { index, lane };\n  });\n  return placed.map(({ index, lane }) => [index, { lane, lanes: laneEnds.length }]);\n}\n\nexport function assignLanes(blocks: readonly LaneSpan[]): LaneAssignment[] {\n  const clusters = splitClusters(sortByStart(blocks));\n  const assignments = new Map<number, LaneAssignment>(clusters.flatMap(packCluster));\n  return blocks.map((_, index) => assignments.get(index) ?? { lane: 0, lanes: 1 });\n}\n\n/** Month label key inputs — returns the 1st of the month as a `Date` so the\n *  component can feed it to `Intl.DateTimeFormat(locale, …)` for a localized\n *  \"April 2026\" header without this module taking a locale dependency. */\nexport function monthAnchorDate(year: number, month: number): Date {\n  return new Date(Date.UTC(year, month - 1, 1));\n}\n","/**\n * A generated record id: a v4 UUID with its hyphens stripped — 32 lowercase hex\n * characters, 122 bits.\n *\n * The ONE generator behind both blank-id create paths — the server's\n * `generateItemId()` (../server/io) delegates here, so the UI's pre-filled id\n * and the id a blank-id POST mints are the same thing rather than two\n * implementations that have to agree.\n *\n * It replaced an 8-hex-char id, whose 32 bits collided at a 1.2% rate across\n * 10k records — and a collision surfaced as `item '<id>' already exists`, which\n * named a duplicate that did not exist (#2851).\n *\n * **The hyphens have to go.** A record id in a `googleCalendar` collection is\n * sent to Google as the caller-chosen event id, and Google takes base32hex only\n * (`isClientSettableEventId`, ../../google/pushPlan) — a hyphenated UUID is\n * refused, so every UI-created record would push as \"the record id cannot be\n * used as a Google event id\". Hex is a subset of base32hex, so the stripped\n * form satisfies both that and `SAFE_RECORD_ID_PATTERN` (the id is also the\n * `<id>.json` filename stem).\n */\nexport function newItemId(): string {\n  return crypto.randomUUID().replace(/-/g, \"\");\n}\n\n/** @deprecated Use {@link newItemId}. Kept as a compatibility export because\n *  `@mulmoclaude/core/collection` is public API (same reason as\n *  `CollectionActionWhen` in ./schema). It is the NEW generator, not the old\n *  8-hex one: that width is the bug #2851 reported, and leaving a copy of it\n *  reachable under an old name would keep shipping it. */\nexport const shortHexId = newItemId;\n"],"mappings":"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"}