@ai-matrx/records 0.73.4 → 0.73.5

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.
@@ -46,6 +46,12 @@ type Answer = {
46
46
  declare function memoryEntityDescribe(e: MemoryEntity): Json$1;
47
47
  /** What `drill_rows` answers: one page, rows in words. */
48
48
  declare function memoryEntityRows(e: MemoryEntity, question: Json$1): Json$1;
49
+ /**
50
+ * What `drill_ask` answers for a memory entity: one row per group of the asked Dimensions (a choice
51
+ * or a lookup, both already words here), then the `total` row; `count`, `sum_<col>` and ad hoc
52
+ * `{op, of}` measures keyed `<op>_<of>` exactly as the live door keys them; `where` narrows first.
53
+ */
54
+ declare function memoryEntityAsk(e: MemoryEntity, question: Json$1): Json$1[];
49
55
  /** What `entity_row_write` does: a choice by its word (any case), refused in the door's words otherwise. */
50
56
  declare function memoryEntityWrite(e: MemoryEntity, recordId: string, columns: Json$1): Answer;
51
57
 
@@ -181,4 +187,4 @@ declare function entityStore(specs: MemoryEntitySpec[], options?: {
181
187
  world: MemoryWorld;
182
188
  };
183
189
 
184
- export { MEMORY_KERNEL, type MemoryDashboard, type MemoryEntity, type MemoryEntityColumn, type MemoryEntitySpec, type MemoryField, type MemoryForm, type MemoryOption, type MemoryTable, type MemoryView, type MemoryWorld, PREVIEW_REFUSAL, type TemplatePreview, type TemplateWorldOptions, entityStore, memoryDataSource, memoryEntityDescribe, memoryEntityRows, memoryEntityWrite, stableId, templatePreview, templateWorld, withEntities };
190
+ export { MEMORY_KERNEL, type MemoryDashboard, type MemoryEntity, type MemoryEntityColumn, type MemoryEntitySpec, type MemoryField, type MemoryForm, type MemoryOption, type MemoryTable, type MemoryView, type MemoryWorld, PREVIEW_REFUSAL, type TemplatePreview, type TemplateWorldOptions, entityStore, memoryDataSource, memoryEntityAsk, memoryEntityDescribe, memoryEntityRows, memoryEntityWrite, stableId, templatePreview, templateWorld, withEntities };
@@ -46,6 +46,12 @@ type Answer = {
46
46
  declare function memoryEntityDescribe(e: MemoryEntity): Json$1;
47
47
  /** What `drill_rows` answers: one page, rows in words. */
48
48
  declare function memoryEntityRows(e: MemoryEntity, question: Json$1): Json$1;
49
+ /**
50
+ * What `drill_ask` answers for a memory entity: one row per group of the asked Dimensions (a choice
51
+ * or a lookup, both already words here), then the `total` row; `count`, `sum_<col>` and ad hoc
52
+ * `{op, of}` measures keyed `<op>_<of>` exactly as the live door keys them; `where` narrows first.
53
+ */
54
+ declare function memoryEntityAsk(e: MemoryEntity, question: Json$1): Json$1[];
49
55
  /** What `entity_row_write` does: a choice by its word (any case), refused in the door's words otherwise. */
50
56
  declare function memoryEntityWrite(e: MemoryEntity, recordId: string, columns: Json$1): Answer;
51
57
 
@@ -181,4 +187,4 @@ declare function entityStore(specs: MemoryEntitySpec[], options?: {
181
187
  world: MemoryWorld;
182
188
  };
183
189
 
184
- export { MEMORY_KERNEL, type MemoryDashboard, type MemoryEntity, type MemoryEntityColumn, type MemoryEntitySpec, type MemoryField, type MemoryForm, type MemoryOption, type MemoryTable, type MemoryView, type MemoryWorld, PREVIEW_REFUSAL, type TemplatePreview, type TemplateWorldOptions, entityStore, memoryDataSource, memoryEntityDescribe, memoryEntityRows, memoryEntityWrite, stableId, templatePreview, templateWorld, withEntities };
190
+ export { MEMORY_KERNEL, type MemoryDashboard, type MemoryEntity, type MemoryEntityColumn, type MemoryEntitySpec, type MemoryField, type MemoryForm, type MemoryOption, type MemoryTable, type MemoryView, type MemoryWorld, PREVIEW_REFUSAL, type TemplatePreview, type TemplateWorldOptions, entityStore, memoryDataSource, memoryEntityAsk, memoryEntityDescribe, memoryEntityRows, memoryEntityWrite, stableId, templatePreview, templateWorld, withEntities };
@@ -66,6 +66,61 @@ function memoryEntityRows(e, question2) {
66
66
  ...offset + page.length < rows.length ? { next_offset: offset + page.length } : {}
67
67
  };
68
68
  }
69
+ function memoryEntityAsk(e, question2) {
70
+ const by = (question2["by"] ?? []).map((k) => k.split(":")[0]);
71
+ const shown = question2["show"] ?? ["count"];
72
+ const measures = (shown.length ? shown : ["count"]).map((m) => typeof m === "string" ? m === "count" ? { key: "count", op: "count", of: null } : { key: m, op: m.split("_")[0], of: m.slice(m.indexOf("_") + 1) } : { key: m.op === "count" ? "count" : `${m.op}_${m.of ?? ""}`, op: m.op, of: m.of ?? null });
73
+ const rows = e.rows.filter((r) => matches(r, question2["where"]));
74
+ const measure = (list) => {
75
+ const out2 = {};
76
+ for (const m of measures) {
77
+ if (m.op === "count") {
78
+ out2[m.key] = list.length;
79
+ continue;
80
+ }
81
+ const nums = list.map((r) => r[m.of ?? ""]).filter((v) => typeof v === "number");
82
+ const sum = nums.reduce((a, b) => a + b, 0);
83
+ out2[m.key] = !nums.length ? null : m.op === "sum" ? sum : m.op === "avg" ? sum / nums.length : m.op === "min" ? Math.min(...nums) : m.op === "max" ? Math.max(...nums) : null;
84
+ }
85
+ return out2;
86
+ };
87
+ const row = (kind, groups, list) => ({
88
+ kind,
89
+ groups,
90
+ measures: measure(list),
91
+ row_count: list.length,
92
+ prior_groups: null,
93
+ prior_measures: null,
94
+ prior_row_count: null,
95
+ delta: null,
96
+ compare: null,
97
+ distinct_groups: null,
98
+ labels: {},
99
+ says: null,
100
+ as_of: null
101
+ });
102
+ const out = [];
103
+ if (by.length) {
104
+ const groups = /* @__PURE__ */ new Map();
105
+ for (const r of rows) {
106
+ const g = {};
107
+ for (const k of by) {
108
+ const v = r[k];
109
+ g[k] = v === void 0 || v === "" ? null : v;
110
+ }
111
+ const key = JSON.stringify(g);
112
+ const hit = groups.get(key) ?? { groups: g, rows: [] };
113
+ hit.rows.push(r);
114
+ groups.set(key, hit);
115
+ }
116
+ const first = measures[0].key;
117
+ const answered = [...groups.values()].map((g) => row("group", g.groups, g.rows));
118
+ answered.sort((a, b) => Number(b["measures"][first] ?? 0) - Number(a["measures"][first] ?? 0));
119
+ out.push(...answered.slice(0, Number(question2["limit"] ?? 50)));
120
+ }
121
+ out.push(row("total", {}, rows));
122
+ return out;
123
+ }
69
124
  function memoryEntityWrite(e, recordId, columns) {
70
125
  const row = e.rows.find((r) => r["id"] === recordId);
71
126
  if (!row) return refuse(`There is no ${e.label} you can open with that id.`, "02000");
@@ -95,6 +150,10 @@ function memoryEntityDoors(entities) {
95
150
  const e = of(args);
96
151
  return e ? ok(memoryEntityDescribe(e)) : null;
97
152
  },
153
+ drill_ask: (args) => {
154
+ const e = of(args);
155
+ return e ? ok(memoryEntityAsk(e, args["p_question"] ?? {})) : null;
156
+ },
98
157
  drill_rows: (args) => {
99
158
  const e = of(args);
100
159
  return e ? ok(memoryEntityRows(e, args["p_question"] ?? {})) : null;
@@ -2523,16 +2582,11 @@ function groupValues(doc, stored, key) {
2523
2582
  const out = list.map((v) => v === void 0 || v === null || v === "" ? null : String(v));
2524
2583
  return out.length ? out : [null];
2525
2584
  }
2526
- function runBlock(world, subject, raw, canvas, index) {
2527
- const table = world.tableById.get(String(raw["table_id"] ?? subject));
2528
- const title = String(raw["title"] ?? `Block ${index + 1}`);
2529
- const base = { ...raw, title, table_id: table?.id ?? subject, ms: 0 };
2530
- if (!table) return { ...base, rows: [], refused: "This block's table is not in this template." };
2531
- if (raw["kind"] === "stuck") return { ...base, rows: [] };
2532
- const measures = (raw["measures"] ?? [{ op: "count" }]).map((m) => ({ op: String(m["op"] ?? "count"), key: m["key"] }));
2533
- const groupBy = (raw["group_by"] ?? []).slice();
2534
- const bucket = raw["bucket"] ?? null;
2535
- const ids = table.order.filter((id) => admits(world, table, id, raw["filter"]) && admits(world, table, id, canvas));
2585
+ function aggregateRows(world, table, ask) {
2586
+ const { groupBy, measures, bucket } = ask;
2587
+ const search = (ask.search ?? "").trim().toLowerCase();
2588
+ let ids = table.order.filter((id) => ask.filters.every((f) => admits(world, table, id, f)));
2589
+ if (search) ids = ids.filter((id) => JSON.stringify(readDocument(world, table, id)).toLowerCase().includes(search));
2536
2590
  const groups = /* @__PURE__ */ new Map();
2537
2591
  for (const id of ids) {
2538
2592
  const doc = readDocument(world, table, id);
@@ -2574,8 +2628,25 @@ function runBlock(world, subject, raw, canvas, index) {
2574
2628
  const k = `${bucket.key}_${bucket.by ?? "month"}`;
2575
2629
  rows.sort((a, b) => String(a.groups[k]).localeCompare(String(b.groups[k])));
2576
2630
  } else rows.sort((a, b) => (b.measures[primary] ?? 0) - (a.measures[primary] ?? 0));
2577
- const limit = Number(raw["limit"] ?? 0);
2631
+ const limit = Number(ask.limit ?? 0);
2578
2632
  if (limit > 0) rows = rows.slice(0, limit);
2633
+ return rows;
2634
+ }
2635
+ function measuresOf(raw) {
2636
+ const list = Array.isArray(raw) ? raw : [];
2637
+ const out = list.map((m) => ({ op: String(m["op"] ?? "count"), key: m["key"] }));
2638
+ return out.length ? out : [{ op: "count" }];
2639
+ }
2640
+ function runBlock(world, subject, raw, canvas, index) {
2641
+ const table = world.tableById.get(String(raw["table_id"] ?? subject));
2642
+ const title = String(raw["title"] ?? `Block ${index + 1}`);
2643
+ const base = { ...raw, title, table_id: table?.id ?? subject, ms: 0 };
2644
+ if (!table) return { ...base, rows: [], refused: "This block's table is not in this template." };
2645
+ if (raw["kind"] === "stuck") return { ...base, rows: [] };
2646
+ const measures = measuresOf(raw["measures"]);
2647
+ const groupBy = (raw["group_by"] ?? []).slice();
2648
+ const bucket = raw["bucket"] ?? null;
2649
+ const rows = aggregateRows(world, table, { groupBy, measures, bucket, filters: [raw["filter"], canvas], limit: Number(raw["limit"] ?? 0) });
2579
2650
  return { ...base, measures, group_by: groupBy, rows };
2580
2651
  }
2581
2652
  function bookingAvailability(world, raw) {
@@ -2951,7 +3022,22 @@ function memoryDataSource(world) {
2951
3022
  blocks: d.blocks.map((b, i) => runBlock(world, d.table_id, b, canvas, i))
2952
3023
  });
2953
3024
  },
2954
- dashboard_stuck: () => ok2([])
3025
+ dashboard_stuck: () => ok2([]),
3026
+ // THE EIGHTH VERB, in memory: the same groups and measures as the live `custom.record_aggregate`
3027
+ // (and its row shape: no comparison asked ⇒ every prior_* null).
3028
+ record_aggregate: (args) => {
3029
+ const t = tableOf(args);
3030
+ if (!t) return refuse2("That table is not in this template.", "P0002");
3031
+ const rows = aggregateRows(world, t, {
3032
+ groupBy: (args["p_group_by"] ?? []).slice(),
3033
+ measures: measuresOf(args["p_measures"]),
3034
+ bucket: args["p_bucket"] ?? null,
3035
+ filters: [args["p_filter"]],
3036
+ ...typeof args["p_search"] === "string" ? { search: args["p_search"] } : {},
3037
+ limit: Number(args["p_limit"] ?? 200)
3038
+ });
3039
+ return ok2(rows.map((r) => ({ ...r, prior_groups: null, prior_measures: null, prior_row_count: null, delta: null, compare: null })));
3040
+ }
2955
3041
  };
2956
3042
  const source = {
2957
3043
  world,
@@ -3024,6 +3110,7 @@ export {
3024
3110
  PREVIEW_REFUSAL,
3025
3111
  entityStore,
3026
3112
  memoryDataSource,
3113
+ memoryEntityAsk,
3027
3114
  memoryEntityDescribe,
3028
3115
  memoryEntityRows,
3029
3116
  memoryEntityWrite,