@mulmoclaude/core 0.8.1 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/collection/core/dynamicIcon.d.ts +24 -0
  2. package/dist/collection/core/schema.d.ts +55 -0
  3. package/dist/collection/core/where.d.ts +32 -0
  4. package/dist/collection/index.cjs +5 -1
  5. package/dist/collection/index.d.ts +2 -0
  6. package/dist/collection/index.js +2 -2
  7. package/dist/collection/registry/server/index.cjs +1 -1
  8. package/dist/collection/registry/server/index.js +1 -1
  9. package/dist/collection/server/discovery.d.ts +51 -0
  10. package/dist/collection/server/dynamicIcon.d.ts +10 -0
  11. package/dist/collection/server/index.cjs +2 -1
  12. package/dist/collection/server/index.d.ts +1 -0
  13. package/dist/collection/server/index.js +2 -2
  14. package/dist/collection-watchers/index.cjs +1 -1
  15. package/dist/collection-watchers/index.js +1 -1
  16. package/dist/{deriveAll-Cb9rWjan.cjs → deriveAll-BQ_p5HSB.cjs} +149 -1
  17. package/dist/deriveAll-BQ_p5HSB.cjs.map +1 -0
  18. package/dist/{deriveAll-D3wFH4Tw.js → deriveAll-CMFXDQ_G.js} +126 -2
  19. package/dist/deriveAll-CMFXDQ_G.js.map +1 -0
  20. package/dist/feeds/index.cjs +2 -2
  21. package/dist/feeds/index.js +2 -2
  22. package/dist/feeds/server/index.cjs +3 -3
  23. package/dist/feeds/server/index.js +3 -3
  24. package/dist/{ingestTypes-DhJ63Ogd.cjs → ingestTypes-C7EheYZX.cjs} +2 -2
  25. package/dist/{ingestTypes-DhJ63Ogd.cjs.map → ingestTypes-C7EheYZX.cjs.map} +1 -1
  26. package/dist/{ingestTypes-BtMZogMX.js → ingestTypes-CmJeOUJc.js} +2 -2
  27. package/dist/{ingestTypes-BtMZogMX.js.map → ingestTypes-CmJeOUJc.js.map} +1 -1
  28. package/dist/{server-BDxrLT41.js → server-DKXXFTbw.js} +96 -4
  29. package/dist/server-DKXXFTbw.js.map +1 -0
  30. package/dist/{server-U2d7Fb1h.cjs → server-G6GtOdaW.cjs} +101 -3
  31. package/dist/server-G6GtOdaW.cjs.map +1 -0
  32. package/package.json +2 -2
  33. package/dist/deriveAll-Cb9rWjan.cjs.map +0 -1
  34. package/dist/deriveAll-D3wFH4Tw.js.map +0 -1
  35. package/dist/server-BDxrLT41.js.map +0 -1
  36. package/dist/server-U2d7Fb1h.cjs.map +0 -1
@@ -0,0 +1,24 @@
1
+ import { CollectionItem, CollectionSchema, DynamicIconSource, DynamicIconSpec } from './schema';
2
+ /** Reduce `records` to the one record that decides the effective icon, per
3
+ * `source`'s `where` filter + `from` strategy:
4
+ * - pool = `source.where`-filtered records, or every record when unset;
5
+ * - an empty pool resolves to `null` (no source record → fallback);
6
+ * - `from: "first"` / `"when"` → the first pool record (storage order);
7
+ * - `from: "latest"` (default), with `orderBy` given → the pool record
8
+ * whose `String(record[orderBy])` sorts highest;
9
+ * - `from: "latest"`, with no `orderBy` → the last pool record.
10
+ * `recordsById` (the source collection's records keyed by primaryKey)
11
+ * resolves any `valueFrom` reference inside `source.where`; omitted for
12
+ * callers with no cross-record lookups. */
13
+ export declare function selectDynamicRecord(records: CollectionItem[], source: DynamicIconSource, orderBy: string | undefined, recordsById?: Record<string, CollectionItem>): CollectionItem | null;
14
+ /** Map a resolved source record to the effective icon: `spec.fallback`
15
+ * (or the collection's own static `icon`) when there's no record or no
16
+ * rule matches; otherwise the `icon` of the first rule whose `where`
17
+ * matches the record. `recordsById` resolves any `valueFrom` reference
18
+ * inside a rule's `where`, same as `selectDynamicRecord`. */
19
+ export declare function resolveIcon(record: CollectionItem | null, spec: DynamicIconSpec, staticIcon: string, recordsById?: Record<string, CollectionItem>): string;
20
+ /** The first field key (declaration order) whose type is `date` or
21
+ * `datetime` — the default `orderBy` for `from: "latest"` when a
22
+ * `DynamicIconSource` doesn't name one. `undefined` when the schema has
23
+ * no date-like field. */
24
+ export declare function firstDateField(schema: CollectionSchema): string | undefined;
@@ -1,3 +1,4 @@
1
+ import { Where } from './where';
1
2
  /** Minimal "this collection is a feed" descriptor carried on the schema.
2
3
  * Deliberately narrow — the canonical collection contract stays
3
4
  * independent of the host's feeds subsystem. The host's richer retrieval
@@ -226,6 +227,48 @@ export interface CollectionAction {
226
227
  * shown. */
227
228
  when?: CollectionWhen;
228
229
  }
230
+ /** One rule in a `dynamicIcon.rules` list: when the resolved source
231
+ * record matches `where` (an AND of typed conditions, see {@link Where}),
232
+ * the collection's effective launcher icon becomes `icon`. Evaluated top
233
+ * to bottom — the first match wins. */
234
+ export interface DynamicIconRule {
235
+ where: Where;
236
+ icon: string;
237
+ }
238
+ /** Where a {@link DynamicIconSpec}'s source record comes from: a (possibly
239
+ * cross-collection) pool of records, optionally narrowed by `where` and
240
+ * reduced to a single record by `from`. */
241
+ export interface DynamicIconSource {
242
+ /** Slug of the collection to read records from — the collection itself
243
+ * (self-reference) or another one (cross-collection). Required. */
244
+ collection: string;
245
+ /** How to reduce the (optionally `where`-filtered) pool to one record.
246
+ * `"latest"` (default): the record with the greatest `orderBy` value.
247
+ * `"first"` / `"when"`: the first record in the pool (storage order). */
248
+ from?: "latest" | "first" | "when";
249
+ /** Field compared for `from: "latest"`. Defaults to the source schema's
250
+ * first `date`/`datetime` field (declaration order); when the source
251
+ * has none, `"latest"` falls back to the last pool record. */
252
+ orderBy?: string;
253
+ /** Optional predicate (AND of typed conditions) narrowing the pool
254
+ * before `from` reduces it to one record (e.g. restrict a shared
255
+ * weather collection to one region). */
256
+ where?: Where;
257
+ }
258
+ /** Declarative "data state → icon" mapping for a collection's launcher
259
+ * shortcut icon (see `CollectionSchema.dynamicIcon`). When absent, the
260
+ * launcher icon is the static `schema.icon`, unchanged from before this
261
+ * field existed. */
262
+ export interface DynamicIconSpec {
263
+ /** The record the `rules` are evaluated against. Required. */
264
+ source: DynamicIconSource;
265
+ /** First-match-wins list of "resolved record matches `where` → `icon`"
266
+ * rules. */
267
+ rules: DynamicIconRule[];
268
+ /** Icon used when no source record resolves or no rule matches.
269
+ * Defaults to the collection's own static `schema.icon`. */
270
+ fallback?: string;
271
+ }
229
272
  export interface CollectionFieldSpec {
230
273
  type: CollectionFieldType;
231
274
  label: string;
@@ -426,12 +469,24 @@ export interface CollectionSchema {
426
469
  * `ingest.template`, and the worker edits the records itself. The host's
427
470
  * feeds subsystem narrows this to its richer `IngestSpec`. */
428
471
  ingest?: CollectionIngest;
472
+ /** Optional data-driven override for the launcher shortcut icon: when
473
+ * set, the host resolves a record from `dynamicIcon.source` and maps
474
+ * it through `dynamicIcon.rules` to compute `CollectionSummary.icon`
475
+ * instead of using this static `icon`. Absent ⇒ unchanged static-icon
476
+ * behaviour. See `computeCollectionIcon` (server) and
477
+ * `selectDynamicRecord`/`resolveIcon` (pure resolver). */
478
+ dynamicIcon?: DynamicIconSpec;
429
479
  }
430
480
  export interface CollectionSummary {
431
481
  slug: string;
432
482
  title: string;
433
483
  icon: string;
434
484
  source: CollectionSource;
485
+ /** Slugs of the source collection(s) a `dynamicIcon` icon was computed
486
+ * from — present only when `schema.dynamicIcon` is set. Lets a client
487
+ * know which collection change-channel(s) to watch for a live icon
488
+ * update (see `useDynamicShortcutIcons`). */
489
+ iconSources?: string[];
435
490
  }
436
491
  export interface CollectionDetail extends CollectionSummary {
437
492
  schema: CollectionSchema;
@@ -0,0 +1,32 @@
1
+ /** Comparison operators one `WhereCond` may apply to `record[field]`. */
2
+ export type WhereOp = "eq" | "ne" | "in" | "gt" | "gte" | "lt" | "lte" | "contains";
3
+ /** Reads the comparison value from a field instead of a schema literal:
4
+ * - `record` set → another record: `recordsById[record][field]` (e.g. a
5
+ * `_config` singleton's `defaultCity`, following a per-user setting);
6
+ * - `record` omitted → the SAME record being matched (field-to-field, e.g.
7
+ * `spent > budget`). */
8
+ export interface ValueRef {
9
+ record?: string;
10
+ field: string;
11
+ }
12
+ /** One typed condition: `record[field] <op> value`. The comparison value is
13
+ * either a literal `value` (a plain string for every op except `in`, which
14
+ * takes the allowed set) or a `valueFrom` reference resolved against the
15
+ * `recordsById` map passed to `matchesWhere`. Exactly one of the two is
16
+ * expected — enforced by zod at the schema boundary (`server/discovery.ts`),
17
+ * not here. */
18
+ export interface WhereCond {
19
+ field: string;
20
+ op: WhereOp;
21
+ value?: string | string[];
22
+ valueFrom?: ValueRef;
23
+ }
24
+ /** A `where` clause is the AND of its conditions — every one must match. */
25
+ export type Where = WhereCond[];
26
+ /** True when `record` satisfies every condition in `where` (AND). An empty
27
+ * `where` matches everything. `recordsById` — the source collection's
28
+ * records keyed by primaryKey — resolves any `valueFrom` reference;
29
+ * omitted (default `{}`) for callers with no cross-record lookups, in
30
+ * which case every `valueFrom` condition is UNRESOLVED and so never
31
+ * matches. */
32
+ export declare function matchesWhere(where: Where, record: Record<string, unknown>, recordsById?: Record<string, Record<string, unknown>>): boolean;
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_deriveAll = require("../deriveAll-Cb9rWjan.cjs");
2
+ const require_deriveAll = require("../deriveAll-BQ_p5HSB.cjs");
3
3
  //#region src/collection/core/presentCollection.ts
4
4
  var TOOL_NAME = "presentCollection";
5
5
  var TOOL_DEFINITION = {
@@ -769,12 +769,14 @@ exports.errorMessage = errorMessage;
769
769
  exports.evaluateDerived = require_deriveAll.evaluateDerived;
770
770
  exports.executePresentCollection = executePresentCollection;
771
771
  exports.fieldVisible = fieldVisible;
772
+ exports.firstDateField = require_deriveAll.firstDateField;
772
773
  exports.firstMissingRequiredField = firstMissingRequiredField;
773
774
  exports.isFieldDrivenEvery = require_deriveAll.isFieldDrivenEvery;
774
775
  exports.isSortableField = isSortableField;
775
776
  exports.itemIdOf = itemIdOf;
776
777
  exports.itemLabelOf = itemLabelOf;
777
778
  exports.labelFieldFor = labelFieldFor;
779
+ exports.matchesWhere = require_deriveAll.matchesWhere;
778
780
  exports.monthAnchorDate = monthAnchorDate;
779
781
  exports.nextSortDirection = nextSortDirection;
780
782
  exports.numericSortValue = numericSortValue;
@@ -783,8 +785,10 @@ exports.parseIsoDateTime = parseIsoDateTime;
783
785
  exports.parseTimeRange = parseTimeRange;
784
786
  exports.recordSpan = recordSpan;
785
787
  exports.resolveEnumColor = resolveEnumColor;
788
+ exports.resolveIcon = require_deriveAll.resolveIcon;
786
789
  exports.resolveRowRefs = require_deriveAll.resolveRowRefs;
787
790
  exports.rowFromItem = rowFromItem;
791
+ exports.selectDynamicRecord = require_deriveAll.selectDynamicRecord;
788
792
  exports.shortHexId = shortHexId;
789
793
  exports.sortItems = sortItems;
790
794
  exports.spanCoversDay = spanCoversDay;
@@ -4,6 +4,8 @@ export * from './core/presentCollection';
4
4
  export * from './core/enumColors';
5
5
  export * from './core/draft';
6
6
  export * from './core/actionVisible';
7
+ export * from './core/where';
8
+ export * from './core/dynamicIcon';
7
9
  export * from './core/derivedFormula';
8
10
  export * from './core/deriveAll';
9
11
  export * from './core/sortItems';
@@ -1,4 +1,4 @@
1
- import { a as FEED_SCHEDULES, c as isFieldDrivenEvery, i as AGENT_INGEST_KIND, n as resolveRowRefs, o as INGEST_KINDS, r as evaluateDerived, s as embedTargetId, t as deriveAll } from "../deriveAll-D3wFH4Tw.js";
1
+ import { a as resolveIcon, c as AGENT_INGEST_KIND, d as embedTargetId, f as isFieldDrivenEvery, i as firstDateField, l as FEED_SCHEDULES, n as resolveRowRefs, o as selectDynamicRecord, r as evaluateDerived, s as matchesWhere, t as deriveAll, u as INGEST_KINDS } from "../deriveAll-CMFXDQ_G.js";
2
2
  //#region src/collection/core/presentCollection.ts
3
3
  var TOOL_NAME = "presentCollection";
4
4
  var TOOL_DEFINITION = {
@@ -735,6 +735,6 @@ function defangForPrompt(value) {
735
735
  return value.replace(/[<>]/g, "").replace(/`/g, "'").replace(/\$\{/g, "$ {").replace(/\s+/g, " ").slice(0, DEFANG_MAX_LEN);
736
736
  }
737
737
  //#endregion
738
- export { AGENT_INGEST_KIND, ENUM_ALERT, ENUM_NEUTRAL, ENUM_NUDGE, FEED_SCHEDULES, INGEST_KINDS, MINUTES_PER_DAY, TOOL_DEFINITION, TOOL_NAME, actionVisible, assignLanes, boolSortValue, bucketRecords, buildMonthGrid, buildUpdatedRecord, coerceInlineValue, compareSortValues, compareYmd, dateOf, dateSortValue, daySlice, defangForPrompt, deriveAll, draftToRecord, embedTargetId, emptyRow, enumColorClasses, enumSortValue, enumValueIndex, errorMessage, evaluateDerived, executePresentCollection, fieldVisible, firstMissingRequiredField, isFieldDrivenEvery, isSortableField, itemIdOf, itemLabelOf, labelFieldFor, monthAnchorDate, nextSortDirection, numericSortValue, parseIsoDate, parseIsoDateTime, parseTimeRange, recordSpan, resolveEnumColor, resolveRowRefs, rowFromItem, shortHexId, sortItems, spanCoversDay, stringSortValue, whenMatches, ymdKey };
738
+ export { AGENT_INGEST_KIND, ENUM_ALERT, ENUM_NEUTRAL, ENUM_NUDGE, FEED_SCHEDULES, INGEST_KINDS, MINUTES_PER_DAY, TOOL_DEFINITION, TOOL_NAME, actionVisible, assignLanes, boolSortValue, bucketRecords, buildMonthGrid, buildUpdatedRecord, coerceInlineValue, compareSortValues, compareYmd, dateOf, dateSortValue, daySlice, defangForPrompt, deriveAll, draftToRecord, embedTargetId, emptyRow, enumColorClasses, enumSortValue, enumValueIndex, errorMessage, evaluateDerived, executePresentCollection, fieldVisible, firstDateField, firstMissingRequiredField, isFieldDrivenEvery, isSortableField, itemIdOf, itemLabelOf, labelFieldFor, matchesWhere, monthAnchorDate, nextSortDirection, numericSortValue, parseIsoDate, parseIsoDateTime, parseTimeRange, recordSpan, resolveEnumColor, resolveIcon, resolveRowRefs, rowFromItem, selectDynamicRecord, shortHexId, sortItems, spanCoversDay, stringSortValue, whenMatches, ymdKey };
739
739
 
740
740
  //# sourceMappingURL=index.js.map
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_rolldown_runtime = require("../../../rolldown-runtime-D6vf50IK.cjs");
3
- const require_server = require("../../../server-U2d7Fb1h.cjs");
3
+ const require_server = require("../../../server-G6GtOdaW.cjs");
4
4
  const require_collection_paths = require("../../paths.cjs");
5
5
  const require_types = require("../../../types-CNqkLT4M.cjs");
6
6
  const require_skill_bridge_index = require("../../../skill-bridge/index.cjs");
@@ -1,4 +1,4 @@
1
- import { K as collectionsRegistriesConfigPath, L as writeFileAtomic, W as safeRecordId, Y as log, _ as acceptParsedSchema, g as CollectionSchemaZ, m as errorMessage, p as ONE_SECOND_MS, y as loadCollection } from "../../../server-BDxrLT41.js";
1
+ import { G as safeRecordId, R as writeFileAtomic, X as log, _ as CollectionSchemaZ, b as loadCollection, m as errorMessage, p as ONE_SECOND_MS, q as collectionsRegistriesConfigPath, v as acceptParsedSchema } from "../../../server-DKXXFTbw.js";
2
2
  import { isSafeActionTemplatePath } from "../../paths.js";
3
3
  import { n as parseRegistryIndex, r as isRecord, t as OFFICIAL_REGISTRY_NAME } from "../../../types-BKVZrtyc.js";
4
4
  import { claudeSkillDir, dataSkillDir, mirrorSkillWrite } from "../../../skill-bridge/index.js";
@@ -192,6 +192,57 @@ export declare const CollectionSchemaZ: z.ZodObject<{
192
192
  role: z.ZodString;
193
193
  template: z.ZodString;
194
194
  }, z.core.$strip>], "kind">>;
195
+ dynamicIcon: z.ZodOptional<z.ZodObject<{
196
+ source: z.ZodObject<{
197
+ collection: z.ZodString;
198
+ from: z.ZodOptional<z.ZodEnum<{
199
+ latest: "latest";
200
+ first: "first";
201
+ when: "when";
202
+ }>>;
203
+ orderBy: z.ZodOptional<z.ZodString>;
204
+ where: z.ZodOptional<z.ZodArray<z.ZodObject<{
205
+ field: z.ZodString;
206
+ op: z.ZodEnum<{
207
+ eq: "eq";
208
+ ne: "ne";
209
+ in: "in";
210
+ gt: "gt";
211
+ gte: "gte";
212
+ lt: "lt";
213
+ lte: "lte";
214
+ contains: "contains";
215
+ }>;
216
+ value: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
217
+ valueFrom: z.ZodOptional<z.ZodObject<{
218
+ record: z.ZodOptional<z.ZodString>;
219
+ field: z.ZodString;
220
+ }, z.core.$strip>>;
221
+ }, z.core.$strip>>>;
222
+ }, z.core.$strip>;
223
+ rules: z.ZodArray<z.ZodObject<{
224
+ where: z.ZodArray<z.ZodObject<{
225
+ field: z.ZodString;
226
+ op: z.ZodEnum<{
227
+ eq: "eq";
228
+ ne: "ne";
229
+ in: "in";
230
+ gt: "gt";
231
+ gte: "gte";
232
+ lt: "lt";
233
+ lte: "lte";
234
+ contains: "contains";
235
+ }>;
236
+ value: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
237
+ valueFrom: z.ZodOptional<z.ZodObject<{
238
+ record: z.ZodOptional<z.ZodString>;
239
+ field: z.ZodString;
240
+ }, z.core.$strip>>;
241
+ }, z.core.$strip>>;
242
+ icon: z.ZodString;
243
+ }, z.core.$strip>>;
244
+ fallback: z.ZodOptional<z.ZodString>;
245
+ }, z.core.$strip>>;
195
246
  }, z.core.$strip>;
196
247
  /** Result of the post-Zod acceptance gates: the resolved record dir on
197
248
  * success, or a one-line reason discovery would skip the schema. */
@@ -0,0 +1,10 @@
1
+ import { DiscoveryOptions } from './discovery';
2
+ import { LoadedCollection } from './discoveredCollection';
3
+ /** Compute the effective launcher icon for `collection`: its static
4
+ * `schema.icon` when it declares no `dynamicIcon`, else the icon
5
+ * resolved from `dynamicIcon.source`'s RAW stored records (no
6
+ * derive/enrich — the icon rules match against stored values) via the
7
+ * pure resolver. Fails soft on any read/discovery error (missing source
8
+ * collection, filesystem error): falls back to `dynamicIcon.fallback ??
9
+ * schema.icon` rather than surfacing to the collections list. */
10
+ export declare function computeCollectionIcon(collection: LoadedCollection, opts?: DiscoveryOptions): Promise<string>;
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_server = require("../../server-U2d7Fb1h.cjs");
2
+ const require_server = require("../../server-G6GtOdaW.cjs");
3
3
  const require_collection_paths = require("../paths.cjs");
4
4
  exports.COMPUTED_TYPES = require_server.COMPUTED_TYPES;
5
5
  exports.CollectionSchemaZ = require_server.CollectionSchemaZ;
@@ -8,6 +8,7 @@ exports.acceptParsedSchema = require_server.acceptParsedSchema;
8
8
  exports.advanceTriggerDate = require_server.advanceTriggerDate;
9
9
  exports.buildActionSeedPrompt = require_server.buildActionSeedPrompt;
10
10
  exports.buildCollectionActionSeedPrompt = require_server.buildCollectionActionSeedPrompt;
11
+ exports.computeCollectionIcon = require_server.computeCollectionIcon;
11
12
  exports.computeSuccessor = require_server.computeSuccessor;
12
13
  exports.configureCollectionHost = require_server.configureCollectionHost;
13
14
  exports.daysInMonth = require_server.daysInMonth;
@@ -6,6 +6,7 @@ export * from './io';
6
6
  export * from './validate';
7
7
  export * from './discovery';
8
8
  export * from './derive';
9
+ export * from './dynamicIcon';
9
10
  export * from './spawn';
10
11
  export * from './delete';
11
12
  export * from './views';
@@ -1,3 +1,3 @@
1
- import { A as promptPathsFor, B as isContainedInWorkspace, C as validateCollectionRecords, D as deleteItem, E as buildCollectionActionSeedPrompt, F as resolveCreateItemId, G as safeSlugName, H as resolveDataDir, I as writeItem, J as getWorkspaceRoot, M as readCustomViewI18n, N as readItem, O as generateItemId, P as readSkillTemplate, R as SCHEMA_FILE, S as COMPUTED_TYPES, T as buildActionSeedPrompt, U as resolveTemplatePath, V as itemFilePath, W as safeRecordId, X as publishCollectionChange, Y as log, Z as setCollectionChangePublisher, _ as acceptParsedSchema, a as computeSuccessor, b as toDetail, c as isTriggerDue, d as resolveEvery, f as successorId, g as CollectionSchemaZ, h as enrichItems, i as advanceTriggerDate, j as readCustomViewHtml, k as listItems, l as maybeSpawnSuccessor, n as deleteCollection, o as daysInMonth, q as configureCollectionHost, r as deleteCollectionRefusalMessage, s as formatCivil, t as deleteCustomView, u as parseCivil, v as discoverCollections, w as validateRecordObject, x as toSummary, y as loadCollection, z as isContainedInRoot } from "../../server-BDxrLT41.js";
1
+ import { A as listItems, B as isContainedInRoot, C as COMPUTED_TYPES, D as buildCollectionActionSeedPrompt, E as buildActionSeedPrompt, F as readSkillTemplate, G as safeRecordId, H as itemFilePath, I as resolveCreateItemId, J as configureCollectionHost, K as safeSlugName, L as writeItem, M as readCustomViewHtml, N as readCustomViewI18n, O as deleteItem, P as readItem, Q as setCollectionChangePublisher, S as toSummary, T as validateRecordObject, U as resolveDataDir, V as isContainedInWorkspace, W as resolveTemplatePath, X as log, Y as getWorkspaceRoot, Z as publishCollectionChange, _ as CollectionSchemaZ, a as computeSuccessor, b as loadCollection, c as isTriggerDue, d as resolveEvery, f as successorId, g as enrichItems, h as computeCollectionIcon, i as advanceTriggerDate, j as promptPathsFor, k as generateItemId, l as maybeSpawnSuccessor, n as deleteCollection, o as daysInMonth, r as deleteCollectionRefusalMessage, s as formatCivil, t as deleteCustomView, u as parseCivil, v as acceptParsedSchema, w as validateCollectionRecords, x as toDetail, y as discoverCollections, z as SCHEMA_FILE } from "../../server-DKXXFTbw.js";
2
2
  import { isSafeActionTemplatePath, isSafeCustomViewI18nPath, isSafeCustomViewPath, isSafeTemplatePath } from "../paths.js";
3
- export { COMPUTED_TYPES, CollectionSchemaZ, SCHEMA_FILE, acceptParsedSchema, advanceTriggerDate, buildActionSeedPrompt, buildCollectionActionSeedPrompt, computeSuccessor, configureCollectionHost, daysInMonth, deleteCollection, deleteCollectionRefusalMessage, deleteCustomView, deleteItem, discoverCollections, enrichItems, formatCivil, generateItemId, getWorkspaceRoot, isContainedInRoot, isContainedInWorkspace, isSafeActionTemplatePath, isSafeCustomViewI18nPath, isSafeCustomViewPath, isSafeTemplatePath, isTriggerDue, itemFilePath, listItems, loadCollection, log, maybeSpawnSuccessor, parseCivil, promptPathsFor, publishCollectionChange, readCustomViewHtml, readCustomViewI18n, readItem, readSkillTemplate, resolveCreateItemId, resolveDataDir, resolveEvery, resolveTemplatePath, safeRecordId, safeSlugName, setCollectionChangePublisher, successorId, toDetail, toSummary, validateCollectionRecords, validateRecordObject, writeItem };
3
+ export { COMPUTED_TYPES, CollectionSchemaZ, SCHEMA_FILE, acceptParsedSchema, advanceTriggerDate, buildActionSeedPrompt, buildCollectionActionSeedPrompt, computeCollectionIcon, computeSuccessor, configureCollectionHost, daysInMonth, deleteCollection, deleteCollectionRefusalMessage, deleteCustomView, deleteItem, discoverCollections, enrichItems, formatCivil, generateItemId, getWorkspaceRoot, isContainedInRoot, isContainedInWorkspace, isSafeActionTemplatePath, isSafeCustomViewI18nPath, isSafeCustomViewPath, isSafeTemplatePath, isTriggerDue, itemFilePath, listItems, loadCollection, log, maybeSpawnSuccessor, parseCivil, promptPathsFor, publishCollectionChange, readCustomViewHtml, readCustomViewI18n, readItem, readSkillTemplate, resolveCreateItemId, resolveDataDir, resolveEvery, resolveTemplatePath, safeRecordId, safeSlugName, setCollectionChangePublisher, successorId, toDetail, toSummary, validateCollectionRecords, validateRecordObject, writeItem };
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_collection_index = require("../collection/index.cjs");
3
- const require_server = require("../server-U2d7Fb1h.cjs");
3
+ const require_server = require("../server-G6GtOdaW.cjs");
4
4
  const require_notifier = require("../notifier-bS8IEeLA.cjs");
5
5
  let node_fs = require("node:fs");
6
6
  let node_fs_promises = require("node:fs/promises");
@@ -1,5 +1,5 @@
1
1
  import { whenMatches } from "../collection/index.js";
2
- import { N as readItem, c as isTriggerDue, k as listItems, l as maybeSpawnSuccessor, v as discoverCollections, y as loadCollection } from "../server-BDxrLT41.js";
2
+ import { A as listItems, P as readItem, b as loadCollection, c as isTriggerDue, l as maybeSpawnSuccessor, y as discoverCollections } from "../server-DKXXFTbw.js";
3
3
  import { d as publish, m as updateForPlugin, n as clear, s as listAll } from "../notifier-ChpY0XrY.js";
4
4
  import { watch } from "node:fs";
5
5
  import { mkdir } from "node:fs/promises";
@@ -38,6 +38,130 @@ function embedTargetId(field, record) {
38
38
  return "";
39
39
  }
40
40
  //#endregion
41
+ //#region src/collection/core/where.ts
42
+ /** True when `record[field]` is absent (`undefined`/`null`) — the only case
43
+ * where `ne` and every other op disagree on the result. */
44
+ function isMissing(raw) {
45
+ return raw === void 0 || raw === null;
46
+ }
47
+ /** The effective comparison value for `cond`: its literal `value`, or — for
48
+ * a `valueFrom` reference — the target field read out of `recordsById`.
49
+ * `undefined` means UNRESOLVED (no such record, or the field on it is
50
+ * missing); the caller must treat that as "never matches", not as a
51
+ * literal `undefined` value to compare against. */
52
+ function resolveValue(cond, record, recordsById) {
53
+ if (!cond.valueFrom) return cond.value;
54
+ const { record: refRecord, field } = cond.valueFrom;
55
+ const raw = (refRecord === void 0 ? record : recordsById[refRecord])?.[field];
56
+ return isMissing(raw) ? void 0 : String(raw);
57
+ }
58
+ function matchesNumericOp(operator, left, right) {
59
+ if (operator === "gt") return left > right;
60
+ if (operator === "gte") return left >= right;
61
+ if (operator === "lt") return left < right;
62
+ return left <= right;
63
+ }
64
+ /** `Number("")` / `Number(" ")` are `0`, not `NaN`, so treat a blank string
65
+ * as non-numeric explicitly — an empty field must fail a numeric compare,
66
+ * not read as zero. */
67
+ function toNumber(raw) {
68
+ return raw.trim() === "" ? NaN : Number(raw);
69
+ }
70
+ function matchesNumeric(operator, raw, value) {
71
+ if (Array.isArray(value)) return false;
72
+ const left = toNumber(raw);
73
+ const right = toNumber(value);
74
+ if (Number.isNaN(left) || Number.isNaN(right)) return false;
75
+ return matchesNumericOp(operator, left, right);
76
+ }
77
+ /** True when the present string `raw` satisfies `operator` against the
78
+ * resolved `value` (field known to exist — MISSING is handled by the
79
+ * caller before this runs, and an UNRESOLVED `valueFrom` never reaches
80
+ * here either). */
81
+ function matchesPresent(operator, raw, value) {
82
+ switch (operator) {
83
+ case "eq": return raw === String(value);
84
+ case "ne": return raw !== String(value);
85
+ case "in": return Array.isArray(value) && value.includes(raw);
86
+ case "contains": return raw.includes(String(value));
87
+ case "gt":
88
+ case "gte":
89
+ case "lt":
90
+ case "lte": return matchesNumeric(operator, raw, value);
91
+ default: return false;
92
+ }
93
+ }
94
+ /** True when `record` satisfies one condition, given `recordsById` to
95
+ * resolve a `valueFrom` reference. Two independent MISSING cases, checked
96
+ * in order:
97
+ * - `record[cond.field]` absent (`undefined`/`null`) → matches only `ne`
98
+ * (vacuously true — "not equal to X" holds when there's no value at
99
+ * all); every other op is false. Unchanged from the literal-`value`
100
+ * behaviour, regardless of whether `valueFrom` would also resolve.
101
+ * - the resolved comparison value is UNRESOLVED (a `valueFrom` whose
102
+ * target record/field doesn't exist) → false for EVERY op, including
103
+ * `ne` — a broken reference must never spuriously match. */
104
+ function matchesCond(cond, record, recordsById) {
105
+ const raw = record[cond.field];
106
+ if (isMissing(raw)) return cond.op === "ne";
107
+ const value = resolveValue(cond, record, recordsById);
108
+ if (value === void 0) return false;
109
+ return matchesPresent(cond.op, String(raw), value);
110
+ }
111
+ /** True when `record` satisfies every condition in `where` (AND). An empty
112
+ * `where` matches everything. `recordsById` — the source collection's
113
+ * records keyed by primaryKey — resolves any `valueFrom` reference;
114
+ * omitted (default `{}`) for callers with no cross-record lookups, in
115
+ * which case every `valueFrom` condition is UNRESOLVED and so never
116
+ * matches. */
117
+ function matchesWhere(where, record, recordsById = {}) {
118
+ return where.every((cond) => matchesCond(cond, record, recordsById));
119
+ }
120
+ //#endregion
121
+ //#region src/collection/core/dynamicIcon.ts
122
+ /** The record with the greatest `String(record[field])` (localeCompare) —
123
+ * ties keep the first-seen record (stable left-to-right `reduce`). */
124
+ function latestByField(pool, field) {
125
+ return pool.reduce((latest, candidate) => String(candidate[field] ?? "").localeCompare(String(latest[field] ?? "")) > 0 ? candidate : latest);
126
+ }
127
+ /** Reduce `records` to the one record that decides the effective icon, per
128
+ * `source`'s `where` filter + `from` strategy:
129
+ * - pool = `source.where`-filtered records, or every record when unset;
130
+ * - an empty pool resolves to `null` (no source record → fallback);
131
+ * - `from: "first"` / `"when"` → the first pool record (storage order);
132
+ * - `from: "latest"` (default), with `orderBy` given → the pool record
133
+ * whose `String(record[orderBy])` sorts highest;
134
+ * - `from: "latest"`, with no `orderBy` → the last pool record.
135
+ * `recordsById` (the source collection's records keyed by primaryKey)
136
+ * resolves any `valueFrom` reference inside `source.where`; omitted for
137
+ * callers with no cross-record lookups. */
138
+ function selectDynamicRecord(records, source, orderBy, recordsById = {}) {
139
+ const { where } = source;
140
+ const pool = where ? records.filter((record) => matchesWhere(where, record, recordsById)) : records;
141
+ if (pool.length === 0) return null;
142
+ if (source.from === "first" || source.from === "when") return pool[0];
143
+ return orderBy ? latestByField(pool, orderBy) : pool[pool.length - 1];
144
+ }
145
+ /** Map a resolved source record to the effective icon: `spec.fallback`
146
+ * (or the collection's own static `icon`) when there's no record or no
147
+ * rule matches; otherwise the `icon` of the first rule whose `where`
148
+ * matches the record. `recordsById` resolves any `valueFrom` reference
149
+ * inside a rule's `where`, same as `selectDynamicRecord`. */
150
+ function resolveIcon(record, spec, staticIcon, recordsById = {}) {
151
+ const fallback = spec.fallback ?? staticIcon;
152
+ if (!record) return fallback;
153
+ const matched = spec.rules.find((rule) => matchesWhere(rule.where, record, recordsById));
154
+ return matched ? matched.icon : fallback;
155
+ }
156
+ var isDateLikeField = (field) => field.type === "date" || field.type === "datetime";
157
+ /** The first field key (declaration order) whose type is `date` or
158
+ * `datetime` — the default `orderBy` for `from: "latest"` when a
159
+ * `DynamicIconSource` doesn't name one. `undefined` when the schema has
160
+ * no date-like field. */
161
+ function firstDateField(schema) {
162
+ return Object.entries(schema.fields).find(([, field]) => isDateLikeField(field))?.[0];
163
+ }
164
+ //#endregion
41
165
  //#region src/collection/core/derivedFormula.ts
42
166
  function evaluateDerived(formula, ctx) {
43
167
  let tokens;
@@ -413,17 +537,41 @@ Object.defineProperty(exports, "evaluateDerived", {
413
537
  return evaluateDerived;
414
538
  }
415
539
  });
540
+ Object.defineProperty(exports, "firstDateField", {
541
+ enumerable: true,
542
+ get: function() {
543
+ return firstDateField;
544
+ }
545
+ });
416
546
  Object.defineProperty(exports, "isFieldDrivenEvery", {
417
547
  enumerable: true,
418
548
  get: function() {
419
549
  return isFieldDrivenEvery;
420
550
  }
421
551
  });
552
+ Object.defineProperty(exports, "matchesWhere", {
553
+ enumerable: true,
554
+ get: function() {
555
+ return matchesWhere;
556
+ }
557
+ });
558
+ Object.defineProperty(exports, "resolveIcon", {
559
+ enumerable: true,
560
+ get: function() {
561
+ return resolveIcon;
562
+ }
563
+ });
422
564
  Object.defineProperty(exports, "resolveRowRefs", {
423
565
  enumerable: true,
424
566
  get: function() {
425
567
  return resolveRowRefs;
426
568
  }
427
569
  });
570
+ Object.defineProperty(exports, "selectDynamicRecord", {
571
+ enumerable: true,
572
+ get: function() {
573
+ return selectDynamicRecord;
574
+ }
575
+ });
428
576
 
429
- //# sourceMappingURL=deriveAll-Cb9rWjan.cjs.map
577
+ //# sourceMappingURL=deriveAll-BQ_p5HSB.cjs.map