@things-factory/ai-dashboard 10.1.213 → 10.1.215
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/components/card-dialogs.ts +24 -1
- package/client/components/dashboard-card.ts +24 -70
- package/client/index.ts +2 -0
- package/client/pages/my-dashboard-page.ts +6 -5
- package/client/utils/card-api.ts +3 -2
- package/client/utils/card-words.ts +11 -0
- package/client/utils/examples.ts +15 -0
- package/dist-client/components/card-dialogs.js +22 -1
- package/dist-client/components/card-dialogs.js.map +1 -1
- package/dist-client/components/dashboard-card.d.ts +6 -5
- package/dist-client/components/dashboard-card.js +23 -73
- package/dist-client/components/dashboard-card.js.map +1 -1
- package/dist-client/index.d.ts +1 -0
- package/dist-client/index.js +2 -0
- package/dist-client/index.js.map +1 -1
- package/dist-client/pages/my-dashboard-page.js +6 -5
- package/dist-client/pages/my-dashboard-page.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/utils/card-api.d.ts +6 -1
- package/dist-client/utils/card-api.js +1 -1
- package/dist-client/utils/card-api.js.map +1 -1
- package/dist-client/utils/card-words.d.ts +14 -0
- package/dist-client/utils/card-words.js +9 -0
- package/dist-client/utils/card-words.js.map +1 -1
- package/dist-client/utils/examples.d.ts +3 -0
- package/dist-client/utils/examples.js +14 -0
- package/dist-client/utils/examples.js.map +1 -0
- package/dist-server/service/card/card-access.d.ts +8 -2
- package/dist-server/service/card/card-access.js +9 -3
- package/dist-server/service/card/card-access.js.map +1 -1
- package/dist-server/service/card/card-definition.d.ts +42 -6
- package/dist-server/service/card/card-definition.js +172 -18
- package/dist-server/service/card/card-definition.js.map +1 -1
- package/dist-server/service/card/card-names.d.ts +5 -0
- package/dist-server/service/card/card-names.js +6 -1
- package/dist-server/service/card/card-names.js.map +1 -1
- package/dist-server/service/card/card-proposal.d.ts +6 -0
- package/dist-server/service/card/card-proposal.js +9 -0
- package/dist-server/service/card/card-proposal.js.map +1 -1
- package/dist-server/service/card/card-resolver.js +24 -5
- package/dist-server/service/card/card-resolver.js.map +1 -1
- package/dist-server/service/card/card-run.js +5 -1
- package/dist-server/service/card/card-run.js.map +1 -1
- package/dist-server/service/card/card-tool.d.ts +66 -8
- package/dist-server/service/card/card-tool.js +88 -17
- package/dist-server/service/card/card-tool.js.map +1 -1
- package/dist-server/service/card/card-types.d.ts +1 -0
- package/dist-server/service/card/card-types.js +8 -4
- package/dist-server/service/card/card-types.js.map +1 -1
- package/dist-server/service/card/dashboard-card.d.ts +2 -0
- package/dist-server/service/card/dashboard-card.js +5 -3
- package/dist-server/service/card/dashboard-card.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/server/service/card/card-access.test.ts +62 -7
- package/server/service/card/card-access.ts +12 -5
- package/server/service/card/card-definition.ts +182 -18
- package/server/service/card/card-intent.test.ts +250 -0
- package/server/service/card/card-names.ts +5 -0
- package/server/service/card/card-proposal.ts +10 -1
- package/server/service/card/card-resolver.ts +26 -8
- package/server/service/card/card-run.ts +5 -1
- package/server/service/card/card-tool.ts +91 -20
- package/server/service/card/card-types.ts +7 -4
- package/server/service/card/dashboard-card.ts +5 -3
- package/test/card-menu.test.ts +34 -0
- package/test/translations.test.ts +1 -1
- package/translations/en.json +3 -3
- package/translations/es.json +3 -3
- package/translations/ja.json +3 -3
- package/translations/ko.json +3 -3
- package/translations/ms.json +3 -3
- package/translations/zh.json +3 -3
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isRelativePeriod = exports.isRelativeDate = exports.MAX_FIGURES = exports.MAX_SERIES = exports.MAX_CHART_POINTS = exports.MAX_TABLE_ROWS = exports.MATCH_OPS = exports.VIEW_KINDS = exports.CARD_SCHEMA_VERSION = void 0;
|
|
3
|
+
exports.isRelativeMonth = exports.isRelativePeriod = exports.isRelativeDate = exports.MAX_FIGURES = exports.MAX_SERIES = exports.MAX_CHART_POINTS = exports.MAX_TABLE_ROWS = exports.LIST_OPS = exports.MATCH_OPS = exports.VIEW_KINDS = exports.CARD_SCHEMA_VERSION = void 0;
|
|
4
4
|
exports.sourceOf = sourceOf;
|
|
5
5
|
exports.todayIn = todayIn;
|
|
6
6
|
exports.resolveRelativeDate = resolveRelativeDate;
|
|
7
|
+
exports.resolveRelativeMonth = resolveRelativeMonth;
|
|
7
8
|
exports.resolveWhere = resolveWhere;
|
|
8
9
|
exports.shapedColumnNames = shapedColumnNames;
|
|
9
10
|
exports.checkDefinition = checkDefinition;
|
|
11
|
+
exports.periodsNamed = periodsNamed;
|
|
12
|
+
exports.checkCardIntent = checkCardIntent;
|
|
10
13
|
exports.CARD_SCHEMA_VERSION = 1;
|
|
11
14
|
exports.VIEW_KINDS = ['number', 'table', 'bar', 'line'];
|
|
12
|
-
exports.MATCH_OPS = ['eq', 'ne', 'gt', 'gte', 'lt', 'lte'];
|
|
15
|
+
exports.MATCH_OPS = ['eq', 'ne', 'gt', 'gte', 'lt', 'lte', 'in', 'not_in'];
|
|
16
|
+
/** The operators whose value is a list - one of these values, none of them. */
|
|
17
|
+
exports.LIST_OPS = ['in', 'not_in'];
|
|
13
18
|
/** The rows a table shows, and the points a chart draws, at most (ADR-0160 decision 4). */
|
|
14
19
|
exports.MAX_TABLE_ROWS = 200;
|
|
15
20
|
exports.MAX_CHART_POINTS = 50;
|
|
@@ -33,10 +38,11 @@ function sourceOf(ref, readTargetOf, detailReadOf) {
|
|
|
33
38
|
query: target.query,
|
|
34
39
|
columns: target.columns,
|
|
35
40
|
conditions: [
|
|
36
|
-
...target.criteria.map(one => ({ name: one.name, type: one.type, values: one.values, description: one.description })),
|
|
41
|
+
...target.criteria.map(one => ({ name: one.name, type: one.type, values: one.values, description: one.description, ...(one.format ? { format: one.format } : {}), ...(one.many ? { many: true } : {}) })),
|
|
37
42
|
...(target.search ? [{ name: 'text', type: 'string', description: target.search.description }] : [])
|
|
38
43
|
],
|
|
39
|
-
target
|
|
44
|
+
target,
|
|
45
|
+
...(target.mine ? { mine: true } : {})
|
|
40
46
|
}
|
|
41
47
|
};
|
|
42
48
|
}
|
|
@@ -58,19 +64,32 @@ function sourceOf(ref, readTargetOf, detailReadOf) {
|
|
|
58
64
|
description: list.description ?? detail.description,
|
|
59
65
|
query: detail.query,
|
|
60
66
|
columns: list.columns,
|
|
61
|
-
conditions: (detail.args ?? []).map(one => ({ name: one.name, type: one.type, values: one.values, description: one.description, required: !!one.required })),
|
|
67
|
+
conditions: (detail.args ?? []).map(one => ({ name: one.name, type: one.type, values: one.values, description: one.description, required: !!one.required, ...(one.format ? { format: one.format } : {}) })),
|
|
62
68
|
detail,
|
|
63
|
-
list
|
|
69
|
+
list,
|
|
70
|
+
...(detail.mine ? { mine: true } : {})
|
|
64
71
|
}
|
|
65
72
|
};
|
|
66
73
|
}
|
|
67
74
|
/* ── Relative dates (ADR-0160 decision 3) ─────────────────────────────────────────────────────────────────── */
|
|
68
|
-
|
|
69
|
-
|
|
75
|
+
/*
|
|
76
|
+
* The words of a period that stays current - the only ones a card keeps:
|
|
77
|
+
* a day today · -30d · this-month-start · last-year-end · -5m-start (the first day of five months ago)
|
|
78
|
+
* a year or a month as an integer this-year · last-month · -5m
|
|
79
|
+
* a month as YYYY-MM this-month · last-month · -5m · this-year-start (its January) · this-year-end
|
|
80
|
+
* "The last six months" is from -5m-start to this-month-end (or a YYYY-MM range from -5m to this-month).
|
|
81
|
+
*/
|
|
82
|
+
const RELATIVE_DATE = /^(today|(this|last|next)-(week|month|quarter|year)-(start|end)|[+-]?\d{1,4}d|-\d{1,2}m-(start|end))$/;
|
|
83
|
+
const RELATIVE_PERIOD = /^((this|last|next)-(year|month)|-\d{1,2}m)$/;
|
|
84
|
+
const RELATIVE_MONTH = /^((this|last|next)-month|-\d{1,2}m|(this|last|next)-year-(start|end))$/;
|
|
85
|
+
/** A value that looks meant as a relative one - checked against the words above, never taken for a literal. */
|
|
86
|
+
const LOOKS_RELATIVE = /^((this|last|next)-|today$|-\d)/;
|
|
70
87
|
const isRelativeDate = (value) => typeof value === 'string' && RELATIVE_DATE.test(value);
|
|
71
88
|
exports.isRelativeDate = isRelativeDate;
|
|
72
89
|
const isRelativePeriod = (value) => typeof value === 'string' && RELATIVE_PERIOD.test(value);
|
|
73
90
|
exports.isRelativePeriod = isRelativePeriod;
|
|
91
|
+
const isRelativeMonth = (value) => typeof value === 'string' && RELATIVE_MONTH.test(value);
|
|
92
|
+
exports.isRelativeMonth = isRelativeMonth;
|
|
74
93
|
const pad = (n) => String(n).padStart(2, '0');
|
|
75
94
|
const ymd = (date) => `${date.getUTCFullYear()}-${pad(date.getUTCMonth() + 1)}-${pad(date.getUTCDate())}`;
|
|
76
95
|
const utc = (y, m, d) => new Date(Date.UTC(y, m, d));
|
|
@@ -91,6 +110,9 @@ function resolveRelativeDate(value, today) {
|
|
|
91
110
|
const days = /^([+-]?\d{1,4})d$/.exec(value);
|
|
92
111
|
if (days)
|
|
93
112
|
return ymd(utc(y, m - 1, d + Number(days[1])));
|
|
113
|
+
const monthsAgo = /^-(\d{1,2})m-(start|end)$/.exec(value);
|
|
114
|
+
if (monthsAgo)
|
|
115
|
+
return ymd(monthsAgo[2] === 'start' ? utc(y, m - 1 - Number(monthsAgo[1]), 1) : utc(y, m - Number(monthsAgo[1]), 0));
|
|
94
116
|
const [, which, unit, edge] = /^(this|last|next)-(week|month|quarter|year)-(start|end)$/.exec(value);
|
|
95
117
|
const step = which === 'last' ? -1 : which === 'next' ? 1 : 0;
|
|
96
118
|
if (unit === 'week') {
|
|
@@ -106,25 +128,40 @@ function resolveRelativeDate(value, today) {
|
|
|
106
128
|
}
|
|
107
129
|
return ymd(edge === 'start' ? utc(y + step, 0, 1) : utc(y + step, 11, 31));
|
|
108
130
|
}
|
|
131
|
+
/** How many months a relative month moves from this one - `last-month` -1, `-5m` -5. */
|
|
132
|
+
const monthStep = (value) => (/^-(\d{1,2})m$/.test(value) ? -Number(value.slice(1, -1)) : value.startsWith('last') ? -1 : value.startsWith('next') ? 1 : 0);
|
|
133
|
+
/** A relative month as YYYY-MM, from `today` - `this-year-start` is this year's January. */
|
|
134
|
+
function resolveRelativeMonth(value, today) {
|
|
135
|
+
const [y, m] = today.split('-').map(Number);
|
|
136
|
+
const year = /^(this|last|next)-year-(start|end)$/.exec(value);
|
|
137
|
+
if (year)
|
|
138
|
+
return `${y + (year[1] === 'last' ? -1 : year[1] === 'next' ? 1 : 0)}-${year[2] === 'start' ? '01' : '12'}`;
|
|
139
|
+
const month = utc(y, m - 1 + monthStep(value), 1);
|
|
140
|
+
return `${month.getUTCFullYear()}-${pad(month.getUTCMonth() + 1)}`;
|
|
141
|
+
}
|
|
109
142
|
/**
|
|
110
|
-
* The relative values of a definition's conditions, as of `today
|
|
111
|
-
* month
|
|
112
|
-
*
|
|
143
|
+
* The relative values of a definition's conditions, as of `today` - each by what its condition holds (`conditions`):
|
|
144
|
+
* a month as YYYY-MM, a day, an integer year or month. A year and a month are read from one month: with a month token
|
|
145
|
+
* in the same conditions, `this-year` is that month's year - January's `last-month` with `this-year` is last December,
|
|
146
|
+
* never this year's December.
|
|
113
147
|
*/
|
|
114
|
-
function resolveWhere(where, today) {
|
|
148
|
+
function resolveWhere(where, today, conditions = []) {
|
|
115
149
|
const given = where ?? {};
|
|
150
|
+
const formats = new Map(conditions.map(one => [one.name, one.format]));
|
|
116
151
|
const [y, m] = today.split('-').map(Number);
|
|
117
|
-
const monthToken = Object.
|
|
118
|
-
const base = monthToken ? utc(y, m - 1 + (monthToken
|
|
152
|
+
const monthToken = Object.entries(given).find(([name, value]) => formats.get(name) !== 'month' && typeof value === 'string' && /^((this|last|next)-month|-\d{1,2}m)$/.test(value))?.[1];
|
|
153
|
+
const base = monthToken ? utc(y, m - 1 + monthStep(monthToken), 1) : utc(y, m - 1, 1);
|
|
119
154
|
const baseYear = base.getUTCFullYear();
|
|
120
155
|
const out = {};
|
|
121
156
|
for (const [name, value] of Object.entries(given)) {
|
|
122
|
-
if ((0, exports.
|
|
157
|
+
if (formats.get(name) === 'month' && (0, exports.isRelativeMonth)(value))
|
|
158
|
+
out[name] = resolveRelativeMonth(value, today);
|
|
159
|
+
else if ((0, exports.isRelativeDate)(value))
|
|
123
160
|
out[name] = resolveRelativeDate(value, today);
|
|
124
161
|
else if ((0, exports.isRelativePeriod)(value)) {
|
|
125
162
|
const [which, unit] = value.split('-');
|
|
126
163
|
const step = which === 'last' ? -1 : which === 'next' ? 1 : 0;
|
|
127
|
-
out[name] = unit === '
|
|
164
|
+
out[name] = unit === 'year' ? baseYear + step : base.getUTCMonth() + 1;
|
|
128
165
|
}
|
|
129
166
|
else
|
|
130
167
|
out[name] = value;
|
|
@@ -166,7 +203,12 @@ function checkDefinition(definition, source) {
|
|
|
166
203
|
issues.push({ code: 'unknown-condition', params: { name, conditions: [...conditions.keys()], ...(column ? { isColumn: true, use: `rows.match: [{ column: '${name}', op: 'eq', value: ${JSON.stringify(value)} }]` } : {}) } });
|
|
167
204
|
continue;
|
|
168
205
|
}
|
|
169
|
-
if (
|
|
206
|
+
if (condition?.format === 'month') {
|
|
207
|
+
if (typeof value === 'string' && LOOKS_RELATIVE.test(value) && !(0, exports.isRelativeMonth)(value))
|
|
208
|
+
issues.push({ code: 'relative-value-unfit', params: { name, value, takes: 'a month as YYYY-MM, or this-month, last-month, -5m, this-year-start, this-year-end' } });
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
if (typeof value === 'string' && LOOKS_RELATIVE.test(value) && !(0, exports.isRelativeDate)(value) && !(0, exports.isRelativePeriod)(value))
|
|
170
212
|
issues.push({ code: 'unknown-relative-value', params: { name, value } });
|
|
171
213
|
if ((0, exports.isRelativePeriod)(value) && condition?.type !== 'integer')
|
|
172
214
|
issues.push({ code: 'relative-period-on-non-integer', params: { name, value } });
|
|
@@ -182,13 +224,50 @@ function checkDefinition(definition, source) {
|
|
|
182
224
|
const columns = new Map(source.columns.map(one => [one.name, one]));
|
|
183
225
|
const columnNames = [...columns.keys()];
|
|
184
226
|
const rows = definition.rows ?? {};
|
|
227
|
+
const scalar = (value) => typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean';
|
|
185
228
|
for (const match of rows.match ?? []) {
|
|
186
229
|
if (!columns.has(match?.column))
|
|
187
230
|
issues.push({ code: 'unknown-column', params: { where: 'match', column: match?.column ?? null, columns: columnNames } });
|
|
188
231
|
if (!known(exports.MATCH_OPS, match?.op))
|
|
189
232
|
issues.push({ code: 'unknown-operator', params: { op: match?.op ?? null, ops: exports.MATCH_OPS } });
|
|
190
|
-
if (
|
|
233
|
+
else if (known(exports.LIST_OPS, match.op)) {
|
|
234
|
+
if (!Array.isArray(match.value) || !match.value.length || !match.value.every(scalar))
|
|
235
|
+
issues.push({ code: 'match-without-values', params: { column: match.column, op: match.op } });
|
|
236
|
+
}
|
|
237
|
+
else if (match?.value === undefined || match?.value === null || !scalar(match.value))
|
|
191
238
|
issues.push({ code: 'match-without-value', params: { column: match?.column ?? null } });
|
|
239
|
+
/*
|
|
240
|
+
* A relative value is read on a date column only - on another it is the literal text 「this-month」, which no row
|
|
241
|
+
* holds: 「이번 달 지출결의 합계」 matched month eq this-month and drew 0 (2026-10-25). A period is the source's
|
|
242
|
+
* condition (a YYYY-MM one takes this-month).
|
|
243
|
+
*/
|
|
244
|
+
const column = columns.get(match?.column);
|
|
245
|
+
const relative = (Array.isArray(match?.value) ? match.value : [match?.value]).filter(one => typeof one === 'string' && LOOKS_RELATIVE.test(one));
|
|
246
|
+
if (column && relative.length) {
|
|
247
|
+
if (column.type !== 'date')
|
|
248
|
+
issues.push({ code: 'relative-value-on-non-date-column', params: { column: column.name, value: relative[0], periodConditions: source.conditions.filter(one => one.type === 'date' || one.format === 'month' || (one.type === 'integer' && /year|month/i.test(one.name))).map(one => one.name) } });
|
|
249
|
+
else if (relative.some(one => !(0, exports.isRelativeDate)(one)))
|
|
250
|
+
issues.push({ code: 'unknown-relative-value', params: { name: column.name, value: relative.find(one => !(0, exports.isRelativeDate)(one)) } });
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/*
|
|
254
|
+
* Conditions that no row can meet - two `eq` of one column (status 작성중 AND 상신), or an `eq` outside its own `in`.
|
|
255
|
+
* Every match must hold, so these draw nothing, or a number of nothing that looks true (2026-10-25).
|
|
256
|
+
*/
|
|
257
|
+
const equals = new Map();
|
|
258
|
+
for (const match of rows.match ?? []) {
|
|
259
|
+
if (!match || typeof match.column !== 'string')
|
|
260
|
+
continue;
|
|
261
|
+
const values = match.op === 'eq' && scalar(match.value) ? [String(match.value)] : match.op === 'in' && Array.isArray(match.value) ? match.value.map(String) : undefined;
|
|
262
|
+
if (!values)
|
|
263
|
+
continue;
|
|
264
|
+
const before = equals.get(match.column);
|
|
265
|
+
const kept = before ? values.filter(one => before.has(one)) : values;
|
|
266
|
+
if (before && !kept.length) {
|
|
267
|
+
issues.push({ code: 'match-never-true', params: { column: match.column, use: `one match of ${match.column} with op in and every value it may hold` } });
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
equals.set(match.column, new Set(kept));
|
|
192
271
|
}
|
|
193
272
|
if (rows.groupBy !== undefined) {
|
|
194
273
|
const by = columns.get(rows.groupBy);
|
|
@@ -234,4 +313,79 @@ function checkDefinition(definition, source) {
|
|
|
234
313
|
issues.push({ code: 'unit-too-long' });
|
|
235
314
|
return issues;
|
|
236
315
|
}
|
|
316
|
+
/*
|
|
317
|
+
* The periods a card's name or line may say, in the languages the module ships the most - a name the dashboard shows
|
|
318
|
+
* every day. 「이번 달 합계」 with no condition of the month summed all time (2026-10-25).
|
|
319
|
+
*/
|
|
320
|
+
const NAMED_PERIODS = [
|
|
321
|
+
['months', /(최근|지난)\s*\d+\s*개월|\d+\s*개월\s*(간|동안)|\b(last|past)\s+\d+\s+months?\b|過去\s*\d+\s*(ヶ|か|カ)月|近\s*\d+\s*个月/i],
|
|
322
|
+
['month', /이번\s*달|이달|금월|당월|지난\s*달|저번\s*달|전월|다음\s*달|\b(this|last|next)\s+month\b|今月|先月|本月|上个?月/i],
|
|
323
|
+
['year', /올해|금년|당해|작년|지난\s*해|전년도|내년|\b(this|last|next)\s+year\b|今年|昨年|去年/i],
|
|
324
|
+
['week', /이번\s*주|금주|지난\s*주|저번\s*주|다음\s*주|\b(this|last|next)\s+week\b|今週|先週|本周|上周/i],
|
|
325
|
+
['day', /오늘|금일|\btoday\b|今日|今天/i]
|
|
326
|
+
];
|
|
327
|
+
/** The periods a text says - none for a text without one. */
|
|
328
|
+
function periodsNamed(text) {
|
|
329
|
+
const given = String(text ?? '');
|
|
330
|
+
return NAMED_PERIODS.filter(([, words]) => words.test(given)).map(([unit]) => unit);
|
|
331
|
+
}
|
|
332
|
+
/** The units a relative value reads - `this-month-start` a month, `-5m` months back, `-30d` days. */
|
|
333
|
+
function unitsOf(value) {
|
|
334
|
+
if (typeof value !== 'string')
|
|
335
|
+
return [];
|
|
336
|
+
if (/^-\d{1,2}m(-(start|end))?$/.test(value))
|
|
337
|
+
return ['months'];
|
|
338
|
+
if (/^today$|^[+-]?\d{1,4}d$/.test(value))
|
|
339
|
+
return /^[+-]?([7-9]|1[0-4])d$/.test(value) ? ['day', 'week'] : ['day'];
|
|
340
|
+
const unit = /^(this|last|next)-(week|month|quarter|year)/.exec(value)?.[2];
|
|
341
|
+
return unit === 'quarter' ? ['months'] : unit ? [unit] : [];
|
|
342
|
+
}
|
|
343
|
+
const FIXED_DAY = /^\d{4}(-\d{2}(-\d{2})?)?$/;
|
|
344
|
+
/** A bound held to one day, month or year - `2026-10-01`, `2026-10`, or an integer year or month (`year: 2026, month: 10`). */
|
|
345
|
+
const isFixed = (value) => typeof value === 'number' || (typeof value === 'string' && FIXED_DAY.test(value));
|
|
346
|
+
/**
|
|
347
|
+
* Whether a card means what its name says, and reads as whoever looks at it. Issues, never repairs:
|
|
348
|
+
* - a period in the name or line (이번 달, 최근 6개월) with no condition of that period - it would sum all time;
|
|
349
|
+
* - a period said relatively but held to fixed days - it would stop being "this month" next month;
|
|
350
|
+
* - a filter on the viewer's own name or id: a read of the viewer's own rows needs none (it is theirs whoever looks),
|
|
351
|
+
* and another read filtered by one person shows that person to everyone the card is shared with - the viewer's own
|
|
352
|
+
* read (`mine`) is the source instead.
|
|
353
|
+
* `mineSources` are the reads of one's own rows the model may use instead (cardSources).
|
|
354
|
+
*/
|
|
355
|
+
function checkCardIntent(card, source, viewer = {}, mineSources = []) {
|
|
356
|
+
const issues = [];
|
|
357
|
+
const { definition } = card;
|
|
358
|
+
const where = definition.source?.where ?? {};
|
|
359
|
+
const matches = definition.rows?.match ?? [];
|
|
360
|
+
const dateColumns = new Set(source.columns.filter(one => one.type === 'date').map(one => one.name));
|
|
361
|
+
const periodConditions = new Set(source.conditions.filter(one => one.type === 'date' || one.format === 'month' || (one.type === 'integer' && /year|month/i.test(one.name))).map(one => one.name));
|
|
362
|
+
/* The values that bound a period - a period condition's, a date column's match. */
|
|
363
|
+
const bounds = [
|
|
364
|
+
...Object.entries(where).filter(([name]) => periodConditions.has(name)).map(([, value]) => value),
|
|
365
|
+
...matches.filter(one => dateColumns.has(one?.column)).map(one => one.value)
|
|
366
|
+
];
|
|
367
|
+
const named = [...new Set([...periodsNamed(card.name), ...periodsNamed(card.description)])];
|
|
368
|
+
if (named.length) {
|
|
369
|
+
if (!bounds.length)
|
|
370
|
+
issues.push({ code: 'period-without-condition', params: { said: named, conditions: [...periodConditions], dateColumns: [...dateColumns] } });
|
|
371
|
+
else if (bounds.some(isFixed))
|
|
372
|
+
issues.push({ code: 'period-held-to-fixed-days', params: { said: named, given: bounds.filter(isFixed) } });
|
|
373
|
+
else {
|
|
374
|
+
const read = new Set(bounds.flatMap(unitsOf));
|
|
375
|
+
const missing = named.filter(unit => !read.has(unit));
|
|
376
|
+
if (read.size && missing.length)
|
|
377
|
+
issues.push({ code: 'period-unlike-condition', params: { said: missing, given: bounds } });
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
const own = [viewer.id, viewer.name, viewer.email].filter((one) => !!one && one.trim().length > 1).map(one => one.trim().toLowerCase());
|
|
381
|
+
const isOwn = (value) => (Array.isArray(value) ? value : [value]).some(one => (typeof one === 'string' || typeof one === 'number') && own.includes(String(one).trim().toLowerCase()));
|
|
382
|
+
const onViewer = [...Object.entries(where).filter(([, value]) => isOwn(value)).map(([name]) => name), ...matches.filter(one => isOwn(one?.value)).map(one => one.column)];
|
|
383
|
+
if (onViewer.length) {
|
|
384
|
+
if (source.mine)
|
|
385
|
+
issues.push({ code: 'viewer-filter-on-own-read', params: { filters: onViewer } });
|
|
386
|
+
else if (mineSources.length)
|
|
387
|
+
issues.push({ code: 'viewer-filter-use-own-read', params: { filters: onViewer, mine: mineSources } });
|
|
388
|
+
}
|
|
389
|
+
return issues;
|
|
390
|
+
}
|
|
237
391
|
//# sourceMappingURL=card-definition.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"card-definition.js","sourceRoot":"","sources":["../../../server/service/card/card-definition.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * What a dashboard card is - a definition, never its values (operato-application/adr/ADR-0160.md).\n *\n * A card names one read an application declared for the assistant (a list read target, or a list inside a detail\n * read's answer), the conditions it is read with, what is done to its rows (filtered, grouped, ordered, cut) and how\n * the rows are drawn (a number, a table, bars or a line). The assistant writes it once, in a conversation; drawing it\n * runs the definition and asks no model. Nothing a definition holds runs as code: a name it does not know is refused,\n * never passed over.\n *\n * Pure - the rules are tested without a server.\n */\nimport type { DetailList, DetailRead, ReadColumn, ReadTarget } from '@things-factory/ai-assistant'\n\nexport const CARD_SCHEMA_VERSION = 1\n\nexport const VIEW_KINDS = ['number', 'table', 'bar', 'line'] as const\nexport type ViewKind = (typeof VIEW_KINDS)[number]\n\nexport const MATCH_OPS = ['eq', 'ne', 'gt', 'gte', 'lt', 'lte'] as const\nexport type MatchOp = (typeof MATCH_OPS)[number]\n\n/** The rows a table shows, and the points a chart draws, at most (ADR-0160 decision 4). */\nexport const MAX_TABLE_ROWS = 200\nexport const MAX_CHART_POINTS = 50\n/** The AI 작업판 draws eight hues and twelve figures; a card that needs more is refused, never folded (decision 5). */\nexport const MAX_SERIES = 8\nexport const MAX_FIGURES = 12\n\nexport interface CardSourceRef {\n /** The read's key - `office.cash-transaction`, `office.receivables`. */\n read: string\n /** A detail read's list key - `by-contract`. Absent for a list read target. */\n list?: string\n /** The conditions (a list) or the arguments (a detail read) - a date may be relative (`this-month-start`). */\n where?: Record<string, unknown>\n}\n\nexport interface CardMatch {\n column: string\n op: MatchOp\n value: string | number | boolean\n}\n\nexport interface CardRows {\n match?: CardMatch[]\n groupBy?: string\n sortBy?: string\n desc?: boolean\n limit?: number\n}\n\nexport interface CardView {\n kind: ViewKind\n /** bar · line: the column along the bottom - the grouped column when rows are grouped. */\n label?: string\n /** number · bar · line: the number columns summed or drawn; none counts the rows. table: shown after `columns`. */\n values?: string[]\n /** table: the columns shown, in order. Absent: every column. */\n columns?: string[]\n /** A unit said beside the numbers when no currency says one (`건`, `%`). */\n unit?: string\n}\n\nexport interface CardDefinition {\n schemaVersion: number\n source: CardSourceRef\n rows?: CardRows\n view: CardView\n}\n\n/** A refusal the card and the model both read - a code and what it is about, never prose. */\nexport interface CardIssue {\n code: string\n params?: Record<string, unknown>\n}\n\n/** A condition a source takes - its name and type, from the declaration. */\nexport interface SourceCondition {\n name: string\n type: string\n values?: string[]\n description?: string\n /** A detail read's argument its query cannot run without. */\n required?: boolean\n}\n\n/** What a card may read from one declaration - the same whichever kind of read it is. */\nexport interface CardSource {\n key: string\n kind: 'list' | 'detail'\n labelKey?: string\n description: string\n query: string\n columns: ReadColumn[]\n conditions: SourceCondition[]\n target?: ReadTarget\n detail?: DetailRead\n list?: DetailList\n}\n\n/** The source a reference names - or why there is none. */\nexport function sourceOf(\n ref: CardSourceRef | undefined,\n readTargetOf: (key: string) => ReadTarget | undefined,\n detailReadOf: (key: string) => DetailRead | undefined\n): { source?: CardSource; issue?: CardIssue } {\n if (!ref || typeof ref.read !== 'string' || !ref.read) return { issue: { code: 'no-source' } }\n const target = readTargetOf(ref.read)\n if (target) {\n if (ref.list) return { issue: { code: 'list-of-a-list', params: { read: ref.read } } }\n return {\n source: {\n key: target.key,\n kind: 'list',\n labelKey: target.labelKey,\n description: target.description,\n query: target.query,\n columns: target.columns,\n conditions: [\n ...target.criteria.map(one => ({ name: one.name, type: one.type, values: one.values, description: one.description })),\n ...(target.search ? [{ name: 'text', type: 'string', description: target.search.description }] : [])\n ],\n target\n }\n }\n }\n const detail = detailReadOf(ref.read)\n if (!detail) return { issue: { code: 'unknown-read', params: { read: ref.read } } }\n const lists = detail.lists ?? []\n if (!lists.length) return { issue: { code: 'not-a-card-source', params: { read: ref.read } } }\n /* One list needs no name; with several, the card names one. */\n const list = ref.list ? lists.find(one => one.key === ref.list) : lists.length === 1 ? lists[0] : undefined\n if (!list) return { issue: { code: ref.list ? 'unknown-list' : 'list-not-named', params: { read: ref.read, list: ref.list ?? null, lists: lists.map(one => one.key) } } }\n return {\n source: {\n key: detail.key,\n kind: 'detail',\n labelKey: list.labelKey ?? detail.labelKey,\n description: list.description ?? detail.description,\n query: detail.query,\n columns: list.columns,\n conditions: (detail.args ?? []).map(one => ({ name: one.name, type: one.type, values: one.values, description: one.description, required: !!one.required })),\n detail,\n list\n }\n }\n}\n\n/* ── Relative dates (ADR-0160 decision 3) ─────────────────────────────────────────────────────────────────── */\n\nconst RELATIVE_DATE = /^(today|(this|last|next)-(week|month|quarter|year)-(start|end)|[+-]?\\d{1,4}d)$/\nconst RELATIVE_PERIOD = /^(this|last|next)-(year|month)$/\n\nexport const isRelativeDate = (value: unknown) => typeof value === 'string' && RELATIVE_DATE.test(value)\nexport const isRelativePeriod = (value: unknown) => typeof value === 'string' && RELATIVE_PERIOD.test(value)\n\nconst pad = (n: number) => String(n).padStart(2, '0')\nconst ymd = (date: Date) => `${date.getUTCFullYear()}-${pad(date.getUTCMonth() + 1)}-${pad(date.getUTCDate())}`\nconst utc = (y: number, m: number, d: number) => new Date(Date.UTC(y, m, d))\n\n/** Today in a time zone - `YYYY-MM-DD`; the system's zone when the domain names none or one the runtime does not know. */\nexport function todayIn(timeZone: string | undefined, now: Date = new Date()): string {\n try {\n return new Intl.DateTimeFormat('en-CA', { timeZone: timeZone || undefined, year: 'numeric', month: '2-digit', day: '2-digit' }).format(now)\n } catch {\n return new Intl.DateTimeFormat('en-CA', { year: 'numeric', month: '2-digit', day: '2-digit' }).format(now)\n }\n}\n\n/** A relative date as a day, from `today` - weeks start on Monday, an end is that last day (a range's `to` takes the whole day). */\nexport function resolveRelativeDate(value: string, today: string): string {\n const [y, m, d] = today.split('-').map(Number)\n if (value === 'today') return today\n const days = /^([+-]?\\d{1,4})d$/.exec(value)\n if (days) return ymd(utc(y, m - 1, d + Number(days[1])))\n const [, which, unit, edge] = /^(this|last|next)-(week|month|quarter|year)-(start|end)$/.exec(value)!\n const step = which === 'last' ? -1 : which === 'next' ? 1 : 0\n if (unit === 'week') {\n const weekday = (utc(y, m - 1, d).getUTCDay() + 6) % 7\n const monday = d - weekday + step * 7\n return ymd(utc(y, m - 1, edge === 'start' ? monday : monday + 6))\n }\n if (unit === 'month') return ymd(edge === 'start' ? utc(y, m - 1 + step, 1) : utc(y, m + step, 0))\n if (unit === 'quarter') {\n const first = Math.floor((m - 1) / 3) * 3 + step * 3\n return ymd(edge === 'start' ? utc(y, first, 1) : utc(y, first + 3, 0))\n }\n return ymd(edge === 'start' ? utc(y + step, 0, 1) : utc(y + step, 11, 31))\n}\n\n/**\n * The relative values of a definition's conditions, as of `today`. A year and a month are read from one month: with a\n * month token in the same conditions, `this-year` is that month's year - January's `last-month` with `this-year` is\n * last December, never this year's December.\n */\nexport function resolveWhere(where: Record<string, unknown> | undefined, today: string): Record<string, unknown> {\n const given = where ?? {}\n const [y, m] = today.split('-').map(Number)\n const monthToken = Object.values(given).find(value => typeof value === 'string' && /^(this|last|next)-month$/.test(value)) as string | undefined\n const base = monthToken ? utc(y, m - 1 + (monthToken.startsWith('last') ? -1 : monthToken.startsWith('next') ? 1 : 0), 1) : utc(y, m - 1, 1)\n const baseYear = base.getUTCFullYear()\n const out: Record<string, unknown> = {}\n for (const [name, value] of Object.entries(given)) {\n if (isRelativeDate(value)) out[name] = resolveRelativeDate(value as string, today)\n else if (isRelativePeriod(value)) {\n const [which, unit] = (value as string).split('-')\n const step = which === 'last' ? -1 : which === 'next' ? 1 : 0\n out[name] = unit === 'month' ? base.getUTCMonth() + 1 : baseYear + step\n } else out[name] = value\n }\n return out\n}\n\n/* ── Checking a definition against its source ─────────────────────────────────────────────────────────────── */\n\n/** The columns the rows have after `rows` - a grouped card's group column, its count and its sums. */\nexport function shapedColumnNames(source: CardSource, rows: CardRows | undefined): Map<string, ReadColumn> {\n if (!rows?.groupBy) return new Map(source.columns.map(one => [one.name, one]))\n const by = source.columns.find(one => one.name === rows.groupBy)\n const out = new Map<string, ReadColumn>()\n if (by) out.set(by.name, { ...by, type: by.type === 'date' ? 'string' : by.type })\n out.set('count', { name: 'count', type: 'number' })\n for (const column of source.columns.filter(one => one.type === 'number' && one.name !== rows.groupBy)) out.set(column.name, column)\n return out\n}\n\nconst known = (list: readonly string[], value: unknown) => typeof value === 'string' && list.includes(value)\n\n/**\n * Whether a definition fits its source - every name it uses is the source's, every value one it takes. Issues, never\n * repairs: a definition the model wrote wrong is sent back with what the source has.\n */\nexport function checkDefinition(definition: CardDefinition | undefined, source: CardSource): CardIssue[] {\n const issues: CardIssue[] = []\n if (!definition || typeof definition !== 'object') return [{ code: 'no-definition' }]\n if (definition.schemaVersion !== CARD_SCHEMA_VERSION) issues.push({ code: 'unknown-schema-version', params: { given: definition.schemaVersion ?? null, known: CARD_SCHEMA_VERSION } })\n\n const conditions = new Map(source.conditions.map(one => [one.name, one]))\n const where = definition.source?.where ?? {}\n for (const [name, value] of Object.entries(where)) {\n const condition = conditions.get(name)\n if (!condition && !(source.kind === 'list' && name === 'id')) {\n /* A column put among the conditions - the rows are filtered by it with rows.match instead. */\n const column = source.columns.some(one => one.name === name)\n issues.push({ code: 'unknown-condition', params: { name, conditions: [...conditions.keys()], ...(column ? { isColumn: true, use: `rows.match: [{ column: '${name}', op: 'eq', value: ${JSON.stringify(value)} }]` } : {}) } })\n continue\n }\n if (typeof value === 'string' && /^(this|last|next)-/.test(value) && !isRelativeDate(value) && !isRelativePeriod(value)) issues.push({ code: 'unknown-relative-value', params: { name, value } })\n if (isRelativePeriod(value) && condition?.type !== 'integer') issues.push({ code: 'relative-period-on-non-integer', params: { name, value } })\n if (isRelativeDate(value) && condition?.type !== 'date') issues.push({ code: 'relative-date-on-non-date', params: { name, value } })\n }\n\n /* What the query cannot run without - said before it runs, with what it takes. */\n for (const condition of source.conditions.filter(one => one.required)) {\n const value = where[condition.name]\n if (value === undefined || value === null || value === '') issues.push({ code: 'missing-condition', params: { name: condition.name, ...(condition.values ? { values: condition.values } : {}), ...(condition.description ? { description: condition.description } : {}) } })\n }\n\n const columns = new Map(source.columns.map(one => [one.name, one]))\n const columnNames = [...columns.keys()]\n const rows = definition.rows ?? {}\n for (const match of rows.match ?? []) {\n if (!columns.has(match?.column)) issues.push({ code: 'unknown-column', params: { where: 'match', column: match?.column ?? null, columns: columnNames } })\n if (!known(MATCH_OPS, match?.op)) issues.push({ code: 'unknown-operator', params: { op: match?.op ?? null, ops: MATCH_OPS } })\n if (match?.value === undefined || match?.value === null || typeof match.value === 'object') issues.push({ code: 'match-without-value', params: { column: match?.column ?? null } })\n }\n if (rows.groupBy !== undefined) {\n const by = columns.get(rows.groupBy)\n if (!by) issues.push({ code: 'unknown-column', params: { where: 'groupBy', column: rows.groupBy, columns: columnNames } })\n else if (by.type === 'number') issues.push({ code: 'group-by-number', params: { column: rows.groupBy } })\n }\n const shaped = shapedColumnNames(source, rows)\n const shapedNames = [...shaped.keys()]\n if (rows.sortBy !== undefined && !shaped.has(rows.sortBy)) issues.push({ code: 'unknown-column', params: { where: 'sortBy', column: rows.sortBy, columns: shapedNames } })\n if (rows.desc !== undefined && typeof rows.desc !== 'boolean') issues.push({ code: 'desc-not-boolean' })\n\n const view = definition.view\n if (!view || !known(VIEW_KINDS, view.kind)) {\n issues.push({ code: 'unknown-view', params: { kind: view?.kind ?? null, kinds: VIEW_KINDS } })\n return issues\n }\n const cap = view.kind === 'table' ? MAX_TABLE_ROWS : view.kind === 'number' ? Infinity : MAX_CHART_POINTS\n if (rows.limit !== undefined && (!Number.isInteger(rows.limit) || rows.limit < 1 || rows.limit > cap)) issues.push({ code: 'limit-out-of-range', params: { limit: rows.limit, max: cap === Infinity ? null : cap } })\n for (const name of view.values ?? []) {\n const column = shaped.get(name)\n if (!column) issues.push({ code: 'unknown-column', params: { where: 'values', column: name, columns: shapedNames } })\n else if (column.type !== 'number') issues.push({ code: 'value-not-a-number', params: { column: name } })\n }\n for (const name of view.columns ?? []) if (!shaped.has(name)) issues.push({ code: 'unknown-column', params: { where: 'columns', column: name, columns: shapedNames } })\n if (view.kind === 'bar' || view.kind === 'line') {\n const label = view.label ?? rows.groupBy\n if (!label) issues.push({ code: 'chart-without-label', params: { columns: shapedNames.filter(one => shaped.get(one)!.type !== 'number') } })\n else if (!shaped.has(label)) issues.push({ code: 'unknown-column', params: { where: 'label', column: label, columns: shapedNames } })\n if (!rows.groupBy && !(view.values ?? []).length) issues.push({ code: 'chart-without-values', params: { columns: shapedNames.filter(one => shaped.get(one)!.type === 'number') } })\n }\n if (view.unit !== undefined && (typeof view.unit !== 'string' || view.unit.length > 20)) issues.push({ code: 'unit-too-long' })\n return issues\n}\n"]}
|
|
1
|
+
{"version":3,"file":"card-definition.js","sourceRoot":"","sources":["../../../server/service/card/card-definition.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * What a dashboard card is - a definition, never its values (operato-application/adr/ADR-0160.md).\n *\n * A card names one read an application declared for the assistant (a list read target, or a list inside a detail\n * read's answer), the conditions it is read with, what is done to its rows (filtered, grouped, ordered, cut) and how\n * the rows are drawn (a number, a table, bars or a line). The assistant writes it once, in a conversation; drawing it\n * runs the definition and asks no model. Nothing a definition holds runs as code: a name it does not know is refused,\n * never passed over.\n *\n * Pure - the rules are tested without a server.\n */\nimport type { DetailList, DetailRead, ReadColumn, ReadTarget } from '@things-factory/ai-assistant'\n\nexport const CARD_SCHEMA_VERSION = 1\n\nexport const VIEW_KINDS = ['number', 'table', 'bar', 'line'] as const\nexport type ViewKind = (typeof VIEW_KINDS)[number]\n\nexport const MATCH_OPS = ['eq', 'ne', 'gt', 'gte', 'lt', 'lte', 'in', 'not_in'] as const\n/** The operators whose value is a list - one of these values, none of them. */\nexport const LIST_OPS = ['in', 'not_in'] as const\nexport type MatchOp = (typeof MATCH_OPS)[number]\n\n/** The rows a table shows, and the points a chart draws, at most (ADR-0160 decision 4). */\nexport const MAX_TABLE_ROWS = 200\nexport const MAX_CHART_POINTS = 50\n/** The AI 작업판 draws eight hues and twelve figures; a card that needs more is refused, never folded (decision 5). */\nexport const MAX_SERIES = 8\nexport const MAX_FIGURES = 12\n\nexport interface CardSourceRef {\n /** The read's key - `office.cash-transaction`, `office.receivables`. */\n read: string\n /** A detail read's list key - `by-contract`. Absent for a list read target. */\n list?: string\n /** The conditions (a list) or the arguments (a detail read) - a date may be relative (`this-month-start`). */\n where?: Record<string, unknown>\n}\n\nexport interface CardMatch {\n column: string\n op: MatchOp\n /** A list for `in` and `not_in` (status not_in [APPROVED, REJECTED, WITHDRAWN]); one value otherwise. */\n value: string | number | boolean | (string | number)[]\n}\n\nexport interface CardRows {\n match?: CardMatch[]\n groupBy?: string\n sortBy?: string\n desc?: boolean\n limit?: number\n}\n\nexport interface CardView {\n kind: ViewKind\n /** bar · line: the column along the bottom - the grouped column when rows are grouped. */\n label?: string\n /** number · bar · line: the number columns summed or drawn; none counts the rows. table: shown after `columns`. */\n values?: string[]\n /** table: the columns shown, in order. Absent: every column. */\n columns?: string[]\n /** A unit said beside the numbers when no currency says one (`건`, `%`). */\n unit?: string\n}\n\nexport interface CardDefinition {\n schemaVersion: number\n source: CardSourceRef\n rows?: CardRows\n view: CardView\n}\n\n/** A refusal the card and the model both read - a code and what it is about, never prose. */\nexport interface CardIssue {\n code: string\n params?: Record<string, unknown>\n}\n\n/** A condition a source takes - its name and type, from the declaration. */\nexport interface SourceCondition {\n name: string\n type: string\n values?: string[]\n description?: string\n /** A detail read's argument its query cannot run without. */\n required?: boolean\n /** A string condition that holds a month as YYYY-MM - it takes a relative month (this-month, -5m). */\n format?: 'month'\n /** It takes several values at once - a list read's statuses (`status: [..]`, `statusNot: [..]`). */\n many?: boolean\n}\n\n/** What a card may read from one declaration - the same whichever kind of read it is. */\nexport interface CardSource {\n key: string\n kind: 'list' | 'detail'\n labelKey?: string\n description: string\n query: string\n columns: ReadColumn[]\n conditions: SourceCondition[]\n target?: ReadTarget\n detail?: DetailRead\n list?: DetailList\n /** The rows are the viewer's own, whoever looks (내 지출결의, 나의 전략 업무) - ReadTarget.mine · DetailRead.mine. */\n mine?: boolean\n}\n\n/** The source a reference names - or why there is none. */\nexport function sourceOf(\n ref: CardSourceRef | undefined,\n readTargetOf: (key: string) => ReadTarget | undefined,\n detailReadOf: (key: string) => DetailRead | undefined\n): { source?: CardSource; issue?: CardIssue } {\n if (!ref || typeof ref.read !== 'string' || !ref.read) return { issue: { code: 'no-source' } }\n const target = readTargetOf(ref.read)\n if (target) {\n if (ref.list) return { issue: { code: 'list-of-a-list', params: { read: ref.read } } }\n return {\n source: {\n key: target.key,\n kind: 'list',\n labelKey: target.labelKey,\n description: target.description,\n query: target.query,\n columns: target.columns,\n conditions: [\n ...target.criteria.map(one => ({ name: one.name, type: one.type, values: one.values, description: one.description, ...(one.format ? { format: one.format } : {}), ...(one.many ? { many: true } : {}) })),\n ...(target.search ? [{ name: 'text', type: 'string', description: target.search.description }] : [])\n ],\n target,\n ...(target.mine ? { mine: true } : {})\n }\n }\n }\n const detail = detailReadOf(ref.read)\n if (!detail) return { issue: { code: 'unknown-read', params: { read: ref.read } } }\n const lists = detail.lists ?? []\n if (!lists.length) return { issue: { code: 'not-a-card-source', params: { read: ref.read } } }\n /* One list needs no name; with several, the card names one. */\n const list = ref.list ? lists.find(one => one.key === ref.list) : lists.length === 1 ? lists[0] : undefined\n if (!list) return { issue: { code: ref.list ? 'unknown-list' : 'list-not-named', params: { read: ref.read, list: ref.list ?? null, lists: lists.map(one => one.key) } } }\n return {\n source: {\n key: detail.key,\n kind: 'detail',\n labelKey: list.labelKey ?? detail.labelKey,\n description: list.description ?? detail.description,\n query: detail.query,\n columns: list.columns,\n conditions: (detail.args ?? []).map(one => ({ name: one.name, type: one.type, values: one.values, description: one.description, required: !!one.required, ...(one.format ? { format: one.format } : {}) })),\n detail,\n list,\n ...(detail.mine ? { mine: true } : {})\n }\n }\n}\n\n/* ── Relative dates (ADR-0160 decision 3) ─────────────────────────────────────────────────────────────────── */\n\n/*\n * The words of a period that stays current - the only ones a card keeps:\n * a day today · -30d · this-month-start · last-year-end · -5m-start (the first day of five months ago)\n * a year or a month as an integer this-year · last-month · -5m\n * a month as YYYY-MM this-month · last-month · -5m · this-year-start (its January) · this-year-end\n * \"The last six months\" is from -5m-start to this-month-end (or a YYYY-MM range from -5m to this-month).\n */\nconst RELATIVE_DATE = /^(today|(this|last|next)-(week|month|quarter|year)-(start|end)|[+-]?\\d{1,4}d|-\\d{1,2}m-(start|end))$/\nconst RELATIVE_PERIOD = /^((this|last|next)-(year|month)|-\\d{1,2}m)$/\nconst RELATIVE_MONTH = /^((this|last|next)-month|-\\d{1,2}m|(this|last|next)-year-(start|end))$/\n/** A value that looks meant as a relative one - checked against the words above, never taken for a literal. */\nconst LOOKS_RELATIVE = /^((this|last|next)-|today$|-\\d)/\n\nexport const isRelativeDate = (value: unknown) => typeof value === 'string' && RELATIVE_DATE.test(value)\nexport const isRelativePeriod = (value: unknown) => typeof value === 'string' && RELATIVE_PERIOD.test(value)\nexport const isRelativeMonth = (value: unknown) => typeof value === 'string' && RELATIVE_MONTH.test(value)\n\nconst pad = (n: number) => String(n).padStart(2, '0')\nconst ymd = (date: Date) => `${date.getUTCFullYear()}-${pad(date.getUTCMonth() + 1)}-${pad(date.getUTCDate())}`\nconst utc = (y: number, m: number, d: number) => new Date(Date.UTC(y, m, d))\n\n/** Today in a time zone - `YYYY-MM-DD`; the system's zone when the domain names none or one the runtime does not know. */\nexport function todayIn(timeZone: string | undefined, now: Date = new Date()): string {\n try {\n return new Intl.DateTimeFormat('en-CA', { timeZone: timeZone || undefined, year: 'numeric', month: '2-digit', day: '2-digit' }).format(now)\n } catch {\n return new Intl.DateTimeFormat('en-CA', { year: 'numeric', month: '2-digit', day: '2-digit' }).format(now)\n }\n}\n\n/** A relative date as a day, from `today` - weeks start on Monday, an end is that last day (a range's `to` takes the whole day). */\nexport function resolveRelativeDate(value: string, today: string): string {\n const [y, m, d] = today.split('-').map(Number)\n if (value === 'today') return today\n const days = /^([+-]?\\d{1,4})d$/.exec(value)\n if (days) return ymd(utc(y, m - 1, d + Number(days[1])))\n const monthsAgo = /^-(\\d{1,2})m-(start|end)$/.exec(value)\n if (monthsAgo) return ymd(monthsAgo[2] === 'start' ? utc(y, m - 1 - Number(monthsAgo[1]), 1) : utc(y, m - Number(monthsAgo[1]), 0))\n const [, which, unit, edge] = /^(this|last|next)-(week|month|quarter|year)-(start|end)$/.exec(value)!\n const step = which === 'last' ? -1 : which === 'next' ? 1 : 0\n if (unit === 'week') {\n const weekday = (utc(y, m - 1, d).getUTCDay() + 6) % 7\n const monday = d - weekday + step * 7\n return ymd(utc(y, m - 1, edge === 'start' ? monday : monday + 6))\n }\n if (unit === 'month') return ymd(edge === 'start' ? utc(y, m - 1 + step, 1) : utc(y, m + step, 0))\n if (unit === 'quarter') {\n const first = Math.floor((m - 1) / 3) * 3 + step * 3\n return ymd(edge === 'start' ? utc(y, first, 1) : utc(y, first + 3, 0))\n }\n return ymd(edge === 'start' ? utc(y + step, 0, 1) : utc(y + step, 11, 31))\n}\n\n/** How many months a relative month moves from this one - `last-month` -1, `-5m` -5. */\nconst monthStep = (value: string) => (/^-(\\d{1,2})m$/.test(value) ? -Number(value.slice(1, -1)) : value.startsWith('last') ? -1 : value.startsWith('next') ? 1 : 0)\n\n/** A relative month as YYYY-MM, from `today` - `this-year-start` is this year's January. */\nexport function resolveRelativeMonth(value: string, today: string): string {\n const [y, m] = today.split('-').map(Number)\n const year = /^(this|last|next)-year-(start|end)$/.exec(value)\n if (year) return `${y + (year[1] === 'last' ? -1 : year[1] === 'next' ? 1 : 0)}-${year[2] === 'start' ? '01' : '12'}`\n const month = utc(y, m - 1 + monthStep(value), 1)\n return `${month.getUTCFullYear()}-${pad(month.getUTCMonth() + 1)}`\n}\n\n/**\n * The relative values of a definition's conditions, as of `today` - each by what its condition holds (`conditions`):\n * a month as YYYY-MM, a day, an integer year or month. A year and a month are read from one month: with a month token\n * in the same conditions, `this-year` is that month's year - January's `last-month` with `this-year` is last December,\n * never this year's December.\n */\nexport function resolveWhere(where: Record<string, unknown> | undefined, today: string, conditions: readonly SourceCondition[] = []): Record<string, unknown> {\n const given = where ?? {}\n const formats = new Map(conditions.map(one => [one.name, one.format]))\n const [y, m] = today.split('-').map(Number)\n const monthToken = Object.entries(given).find(([name, value]) => formats.get(name) !== 'month' && typeof value === 'string' && /^((this|last|next)-month|-\\d{1,2}m)$/.test(value))?.[1] as string | undefined\n const base = monthToken ? utc(y, m - 1 + monthStep(monthToken), 1) : utc(y, m - 1, 1)\n const baseYear = base.getUTCFullYear()\n const out: Record<string, unknown> = {}\n for (const [name, value] of Object.entries(given)) {\n if (formats.get(name) === 'month' && isRelativeMonth(value)) out[name] = resolveRelativeMonth(value as string, today)\n else if (isRelativeDate(value)) out[name] = resolveRelativeDate(value as string, today)\n else if (isRelativePeriod(value)) {\n const [which, unit] = (value as string).split('-')\n const step = which === 'last' ? -1 : which === 'next' ? 1 : 0\n out[name] = unit === 'year' ? baseYear + step : base.getUTCMonth() + 1\n } else out[name] = value\n }\n return out\n}\n\n/* ── Checking a definition against its source ─────────────────────────────────────────────────────────────── */\n\n/** The columns the rows have after `rows` - a grouped card's group column, its count and its sums. */\nexport function shapedColumnNames(source: CardSource, rows: CardRows | undefined): Map<string, ReadColumn> {\n if (!rows?.groupBy) return new Map(source.columns.map(one => [one.name, one]))\n const by = source.columns.find(one => one.name === rows.groupBy)\n const out = new Map<string, ReadColumn>()\n if (by) out.set(by.name, { ...by, type: by.type === 'date' ? 'string' : by.type })\n out.set('count', { name: 'count', type: 'number' })\n for (const column of source.columns.filter(one => one.type === 'number' && one.name !== rows.groupBy)) out.set(column.name, column)\n return out\n}\n\nconst known = (list: readonly string[], value: unknown) => typeof value === 'string' && list.includes(value)\n\n/**\n * Whether a definition fits its source - every name it uses is the source's, every value one it takes. Issues, never\n * repairs: a definition the model wrote wrong is sent back with what the source has.\n */\nexport function checkDefinition(definition: CardDefinition | undefined, source: CardSource): CardIssue[] {\n const issues: CardIssue[] = []\n if (!definition || typeof definition !== 'object') return [{ code: 'no-definition' }]\n if (definition.schemaVersion !== CARD_SCHEMA_VERSION) issues.push({ code: 'unknown-schema-version', params: { given: definition.schemaVersion ?? null, known: CARD_SCHEMA_VERSION } })\n\n const conditions = new Map(source.conditions.map(one => [one.name, one]))\n const where = definition.source?.where ?? {}\n for (const [name, value] of Object.entries(where)) {\n const condition = conditions.get(name)\n if (!condition && !(source.kind === 'list' && name === 'id')) {\n /* A column put among the conditions - the rows are filtered by it with rows.match instead. */\n const column = source.columns.some(one => one.name === name)\n issues.push({ code: 'unknown-condition', params: { name, conditions: [...conditions.keys()], ...(column ? { isColumn: true, use: `rows.match: [{ column: '${name}', op: 'eq', value: ${JSON.stringify(value)} }]` } : {}) } })\n continue\n }\n if (condition?.format === 'month') {\n if (typeof value === 'string' && LOOKS_RELATIVE.test(value) && !isRelativeMonth(value)) issues.push({ code: 'relative-value-unfit', params: { name, value, takes: 'a month as YYYY-MM, or this-month, last-month, -5m, this-year-start, this-year-end' } })\n continue\n }\n if (typeof value === 'string' && LOOKS_RELATIVE.test(value) && !isRelativeDate(value) && !isRelativePeriod(value)) issues.push({ code: 'unknown-relative-value', params: { name, value } })\n if (isRelativePeriod(value) && condition?.type !== 'integer') issues.push({ code: 'relative-period-on-non-integer', params: { name, value } })\n if (isRelativeDate(value) && condition?.type !== 'date') issues.push({ code: 'relative-date-on-non-date', params: { name, value } })\n }\n\n /* What the query cannot run without - said before it runs, with what it takes. */\n for (const condition of source.conditions.filter(one => one.required)) {\n const value = where[condition.name]\n if (value === undefined || value === null || value === '') issues.push({ code: 'missing-condition', params: { name: condition.name, ...(condition.values ? { values: condition.values } : {}), ...(condition.description ? { description: condition.description } : {}) } })\n }\n\n const columns = new Map(source.columns.map(one => [one.name, one]))\n const columnNames = [...columns.keys()]\n const rows = definition.rows ?? {}\n const scalar = (value: unknown) => typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean'\n for (const match of rows.match ?? []) {\n if (!columns.has(match?.column)) issues.push({ code: 'unknown-column', params: { where: 'match', column: match?.column ?? null, columns: columnNames } })\n if (!known(MATCH_OPS, match?.op)) issues.push({ code: 'unknown-operator', params: { op: match?.op ?? null, ops: MATCH_OPS } })\n else if (known(LIST_OPS, match.op)) {\n if (!Array.isArray(match.value) || !match.value.length || !match.value.every(scalar)) issues.push({ code: 'match-without-values', params: { column: match.column, op: match.op } })\n } else if (match?.value === undefined || match?.value === null || !scalar(match.value)) issues.push({ code: 'match-without-value', params: { column: match?.column ?? null } })\n /*\n * A relative value is read on a date column only - on another it is the literal text 「this-month」, which no row\n * holds: 「이번 달 지출결의 합계」 matched month eq this-month and drew 0 (2026-10-25). A period is the source's\n * condition (a YYYY-MM one takes this-month).\n */\n const column = columns.get(match?.column)\n const relative = (Array.isArray(match?.value) ? match.value : [match?.value]).filter(one => typeof one === 'string' && LOOKS_RELATIVE.test(one))\n if (column && relative.length) {\n if (column.type !== 'date') issues.push({ code: 'relative-value-on-non-date-column', params: { column: column.name, value: relative[0], periodConditions: source.conditions.filter(one => one.type === 'date' || one.format === 'month' || (one.type === 'integer' && /year|month/i.test(one.name))).map(one => one.name) } })\n else if (relative.some(one => !isRelativeDate(one))) issues.push({ code: 'unknown-relative-value', params: { name: column.name, value: relative.find(one => !isRelativeDate(one)) } })\n }\n }\n /*\n * Conditions that no row can meet - two `eq` of one column (status 작성중 AND 상신), or an `eq` outside its own `in`.\n * Every match must hold, so these draw nothing, or a number of nothing that looks true (2026-10-25).\n */\n const equals = new Map<string, Set<string>>()\n for (const match of rows.match ?? []) {\n if (!match || typeof match.column !== 'string') continue\n const values = match.op === 'eq' && scalar(match.value) ? [String(match.value)] : match.op === 'in' && Array.isArray(match.value) ? match.value.map(String) : undefined\n if (!values) continue\n const before = equals.get(match.column)\n const kept = before ? values.filter(one => before.has(one)) : values\n if (before && !kept.length) {\n issues.push({ code: 'match-never-true', params: { column: match.column, use: `one match of ${match.column} with op in and every value it may hold` } })\n continue\n }\n equals.set(match.column, new Set(kept))\n }\n if (rows.groupBy !== undefined) {\n const by = columns.get(rows.groupBy)\n if (!by) issues.push({ code: 'unknown-column', params: { where: 'groupBy', column: rows.groupBy, columns: columnNames } })\n else if (by.type === 'number') issues.push({ code: 'group-by-number', params: { column: rows.groupBy } })\n }\n const shaped = shapedColumnNames(source, rows)\n const shapedNames = [...shaped.keys()]\n if (rows.sortBy !== undefined && !shaped.has(rows.sortBy)) issues.push({ code: 'unknown-column', params: { where: 'sortBy', column: rows.sortBy, columns: shapedNames } })\n if (rows.desc !== undefined && typeof rows.desc !== 'boolean') issues.push({ code: 'desc-not-boolean' })\n\n const view = definition.view\n if (!view || !known(VIEW_KINDS, view.kind)) {\n issues.push({ code: 'unknown-view', params: { kind: view?.kind ?? null, kinds: VIEW_KINDS } })\n return issues\n }\n const cap = view.kind === 'table' ? MAX_TABLE_ROWS : view.kind === 'number' ? Infinity : MAX_CHART_POINTS\n if (rows.limit !== undefined && (!Number.isInteger(rows.limit) || rows.limit < 1 || rows.limit > cap)) issues.push({ code: 'limit-out-of-range', params: { limit: rows.limit, max: cap === Infinity ? null : cap } })\n for (const name of view.values ?? []) {\n const column = shaped.get(name)\n if (!column) issues.push({ code: 'unknown-column', params: { where: 'values', column: name, columns: shapedNames } })\n else if (column.type !== 'number') issues.push({ code: 'value-not-a-number', params: { column: name } })\n }\n for (const name of view.columns ?? []) if (!shaped.has(name)) issues.push({ code: 'unknown-column', params: { where: 'columns', column: name, columns: shapedNames } })\n if (view.kind === 'bar' || view.kind === 'line') {\n const label = view.label ?? rows.groupBy\n if (!label) issues.push({ code: 'chart-without-label', params: { columns: shapedNames.filter(one => shaped.get(one)!.type !== 'number') } })\n else if (!shaped.has(label)) issues.push({ code: 'unknown-column', params: { where: 'label', column: label, columns: shapedNames } })\n if (!rows.groupBy && !(view.values ?? []).length) issues.push({ code: 'chart-without-values', params: { columns: shapedNames.filter(one => shaped.get(one)!.type === 'number') } })\n }\n if (view.unit !== undefined && (typeof view.unit !== 'string' || view.unit.length > 20)) issues.push({ code: 'unit-too-long' })\n return issues\n}\n\n/* ── What the card says it shows, against what it reads ───────────────────────────────────────────────────── */\n\nexport type PeriodUnit = 'day' | 'week' | 'month' | 'months' | 'year'\n\n/*\n * The periods a card's name or line may say, in the languages the module ships the most - a name the dashboard shows\n * every day. 「이번 달 합계」 with no condition of the month summed all time (2026-10-25).\n */\nconst NAMED_PERIODS: [PeriodUnit, RegExp][] = [\n ['months', /(최근|지난)\\s*\\d+\\s*개월|\\d+\\s*개월\\s*(간|동안)|\\b(last|past)\\s+\\d+\\s+months?\\b|過去\\s*\\d+\\s*(ヶ|か|カ)月|近\\s*\\d+\\s*个月/i],\n ['month', /이번\\s*달|이달|금월|당월|지난\\s*달|저번\\s*달|전월|다음\\s*달|\\b(this|last|next)\\s+month\\b|今月|先月|本月|上个?月/i],\n ['year', /올해|금년|당해|작년|지난\\s*해|전년도|내년|\\b(this|last|next)\\s+year\\b|今年|昨年|去年/i],\n ['week', /이번\\s*주|금주|지난\\s*주|저번\\s*주|다음\\s*주|\\b(this|last|next)\\s+week\\b|今週|先週|本周|上周/i],\n ['day', /오늘|금일|\\btoday\\b|今日|今天/i]\n]\n\n/** The periods a text says - none for a text without one. */\nexport function periodsNamed(text: string | undefined): PeriodUnit[] {\n const given = String(text ?? '')\n return NAMED_PERIODS.filter(([, words]) => words.test(given)).map(([unit]) => unit)\n}\n\n/** The units a relative value reads - `this-month-start` a month, `-5m` months back, `-30d` days. */\nfunction unitsOf(value: unknown): PeriodUnit[] {\n if (typeof value !== 'string') return []\n if (/^-\\d{1,2}m(-(start|end))?$/.test(value)) return ['months']\n if (/^today$|^[+-]?\\d{1,4}d$/.test(value)) return /^[+-]?([7-9]|1[0-4])d$/.test(value) ? ['day', 'week'] : ['day']\n const unit = /^(this|last|next)-(week|month|quarter|year)/.exec(value)?.[2]\n return unit === 'quarter' ? ['months'] : unit ? [unit as PeriodUnit] : []\n}\n\nconst FIXED_DAY = /^\\d{4}(-\\d{2}(-\\d{2})?)?$/\n/** A bound held to one day, month or year - `2026-10-01`, `2026-10`, or an integer year or month (`year: 2026, month: 10`). */\nconst isFixed = (value: unknown) => typeof value === 'number' || (typeof value === 'string' && FIXED_DAY.test(value))\n\n/** The viewer as the definition may name them by mistake - their user id, name and e-mail. */\nexport interface ViewerNames {\n id?: string\n name?: string\n email?: string\n}\n\n/**\n * Whether a card means what its name says, and reads as whoever looks at it. Issues, never repairs:\n * - a period in the name or line (이번 달, 최근 6개월) with no condition of that period - it would sum all time;\n * - a period said relatively but held to fixed days - it would stop being \"this month\" next month;\n * - a filter on the viewer's own name or id: a read of the viewer's own rows needs none (it is theirs whoever looks),\n * and another read filtered by one person shows that person to everyone the card is shared with - the viewer's own\n * read (`mine`) is the source instead.\n * `mineSources` are the reads of one's own rows the model may use instead (cardSources).\n */\nexport function checkCardIntent(\n card: { name?: string; description?: string; definition: CardDefinition },\n source: CardSource,\n viewer: ViewerNames = {},\n mineSources: string[] = []\n): CardIssue[] {\n const issues: CardIssue[] = []\n const { definition } = card\n const where = definition.source?.where ?? {}\n const matches = definition.rows?.match ?? []\n const dateColumns = new Set(source.columns.filter(one => one.type === 'date').map(one => one.name))\n const periodConditions = new Set(source.conditions.filter(one => one.type === 'date' || one.format === 'month' || (one.type === 'integer' && /year|month/i.test(one.name))).map(one => one.name))\n\n /* The values that bound a period - a period condition's, a date column's match. */\n const bounds = [\n ...Object.entries(where).filter(([name]) => periodConditions.has(name)).map(([, value]) => value),\n ...matches.filter(one => dateColumns.has(one?.column)).map(one => one.value)\n ]\n const named = [...new Set([...periodsNamed(card.name), ...periodsNamed(card.description)])]\n if (named.length) {\n if (!bounds.length) issues.push({ code: 'period-without-condition', params: { said: named, conditions: [...periodConditions], dateColumns: [...dateColumns] } })\n else if (bounds.some(isFixed)) issues.push({ code: 'period-held-to-fixed-days', params: { said: named, given: bounds.filter(isFixed) } })\n else {\n const read = new Set(bounds.flatMap(unitsOf))\n const missing = named.filter(unit => !read.has(unit))\n if (read.size && missing.length) issues.push({ code: 'period-unlike-condition', params: { said: missing, given: bounds } })\n }\n }\n\n const own = [viewer.id, viewer.name, viewer.email].filter((one): one is string => !!one && one.trim().length > 1).map(one => one.trim().toLowerCase())\n const isOwn = (value: unknown) => (Array.isArray(value) ? value : [value]).some(one => (typeof one === 'string' || typeof one === 'number') && own.includes(String(one).trim().toLowerCase()))\n const onViewer = [...Object.entries(where).filter(([, value]) => isOwn(value)).map(([name]) => name), ...matches.filter(one => isOwn(one?.value)).map(one => one.column)]\n if (onViewer.length) {\n if (source.mine) issues.push({ code: 'viewer-filter-on-own-read', params: { filters: onViewer } })\n else if (mineSources.length) issues.push({ code: 'viewer-filter-use-own-read', params: { filters: onViewer, mine: mineSources } })\n }\n return issues\n}\n"]}
|
|
@@ -3,3 +3,8 @@ export declare const CARD_CATEGORY = "dashboard-card";
|
|
|
3
3
|
export declare const CARD_TOOL = "proposeDashboardCard";
|
|
4
4
|
/** The route of the dashboard page - its conversations put the card tool in front. */
|
|
5
5
|
export declare const DASHBOARD_PAGE = "my-dashboard";
|
|
6
|
+
/**
|
|
7
|
+
* The axis of a saved card - its key `dashboard-card:<uuid>` is how a picked card is handed to the conversation, like a
|
|
8
|
+
* picked row of any list (ai-assistant `selectedRecords`). The client's `CARD_AXIS` (card-words.ts) is the same word.
|
|
9
|
+
*/
|
|
10
|
+
export declare const CARD_AXIS = "dashboard-card";
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DASHBOARD_PAGE = exports.CARD_TOOL = exports.CARD_CATEGORY = void 0;
|
|
3
|
+
exports.CARD_AXIS = exports.DASHBOARD_PAGE = exports.CARD_TOOL = exports.CARD_CATEGORY = void 0;
|
|
4
4
|
/** The names other files of this package and its client share - kept apart so the pure rules load nothing else. */
|
|
5
5
|
exports.CARD_CATEGORY = 'dashboard-card';
|
|
6
6
|
exports.CARD_TOOL = 'proposeDashboardCard';
|
|
7
7
|
/** The route of the dashboard page - its conversations put the card tool in front. */
|
|
8
8
|
exports.DASHBOARD_PAGE = 'my-dashboard';
|
|
9
|
+
/**
|
|
10
|
+
* The axis of a saved card - its key `dashboard-card:<uuid>` is how a picked card is handed to the conversation, like a
|
|
11
|
+
* picked row of any list (ai-assistant `selectedRecords`). The client's `CARD_AXIS` (card-words.ts) is the same word.
|
|
12
|
+
*/
|
|
13
|
+
exports.CARD_AXIS = 'dashboard-card';
|
|
9
14
|
//# sourceMappingURL=card-names.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"card-names.js","sourceRoot":"","sources":["../../../server/service/card/card-names.ts"],"names":[],"mappings":";;;AAAA,mHAAmH;AACtG,QAAA,aAAa,GAAG,gBAAgB,CAAA;AAChC,QAAA,SAAS,GAAG,sBAAsB,CAAA;AAC/C,sFAAsF;AACzE,QAAA,cAAc,GAAG,cAAc,CAAA","sourcesContent":["/** The names other files of this package and its client share - kept apart so the pure rules load nothing else. */\nexport const CARD_CATEGORY = 'dashboard-card'\nexport const CARD_TOOL = 'proposeDashboardCard'\n/** The route of the dashboard page - its conversations put the card tool in front. */\nexport const DASHBOARD_PAGE = 'my-dashboard'\n"]}
|
|
1
|
+
{"version":3,"file":"card-names.js","sourceRoot":"","sources":["../../../server/service/card/card-names.ts"],"names":[],"mappings":";;;AAAA,mHAAmH;AACtG,QAAA,aAAa,GAAG,gBAAgB,CAAA;AAChC,QAAA,SAAS,GAAG,sBAAsB,CAAA;AAC/C,sFAAsF;AACzE,QAAA,cAAc,GAAG,cAAc,CAAA;AAC5C;;;GAGG;AACU,QAAA,SAAS,GAAG,gBAAgB,CAAA","sourcesContent":["/** The names other files of this package and its client share - kept apart so the pure rules load nothing else. */\nexport const CARD_CATEGORY = 'dashboard-card'\nexport const CARD_TOOL = 'proposeDashboardCard'\n/** The route of the dashboard page - its conversations put the card tool in front. */\nexport const DASHBOARD_PAGE = 'my-dashboard'\n/**\n * The axis of a saved card - its key `dashboard-card:<uuid>` is how a picked card is handed to the conversation, like a\n * picked row of any list (ai-assistant `selectedRecords`). The client's `CARD_AXIS` (card-words.ts) is the same word.\n */\nexport const CARD_AXIS = 'dashboard-card'\n"]}
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The saved card a model named - its uuid, written alone or as the key `dashboard-card:<uuid>` a picked card is handed
|
|
3
|
+
* by. Nothing for anything else: a key of another record or a name is no card, and is never looked up as one (a
|
|
4
|
+
* database whose ids are uuids refuses the lookup itself - the card edit failed on 「card id not uuid」, 2026-10-25).
|
|
5
|
+
*/
|
|
6
|
+
export declare const cardIdOf: (given: unknown) => string | undefined;
|
|
1
7
|
export interface CardProposal {
|
|
2
8
|
key: string;
|
|
3
9
|
tool: string;
|
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.cardIdOf = void 0;
|
|
3
4
|
exports.findCardProposal = findCardProposal;
|
|
4
5
|
/*
|
|
5
6
|
* The card proposals of a conversation - which one a press means, and whether a later one replaced it
|
|
6
7
|
* (ADR-0160 decision 9). Pure - tested without a database.
|
|
7
8
|
*/
|
|
9
|
+
const ai_assistant_1 = require("@things-factory/ai-assistant");
|
|
8
10
|
const card_names_js_1 = require("./card-names.js");
|
|
11
|
+
/**
|
|
12
|
+
* The saved card a model named - its uuid, written alone or as the key `dashboard-card:<uuid>` a picked card is handed
|
|
13
|
+
* by. Nothing for anything else: a key of another record or a name is no card, and is never looked up as one (a
|
|
14
|
+
* database whose ids are uuids refuses the lookup itself - the card edit failed on 「card id not uuid」, 2026-10-25).
|
|
15
|
+
*/
|
|
16
|
+
const cardIdOf = (given) => (typeof given === 'string' ? (0, ai_assistant_1.recordIdOfKey)(card_names_js_1.CARD_AXIS, given) : undefined);
|
|
17
|
+
exports.cardIdOf = cardIdOf;
|
|
9
18
|
const isCardProposal = (one) => one?.tool === card_names_js_1.CARD_TOOL && one?.kind === 'dashboard-card' && !!one?.card?.definition;
|
|
10
19
|
/** The same card, said again - a change of one saved card, or a new card of the same name. */
|
|
11
20
|
const sameCard = (a, b) => (a.card.cardId || b.card.cardId ? a.card.cardId === b.card.cardId : a.card.name === b.card.name);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"card-proposal.js","sourceRoot":"","sources":["../../../server/service/card/card-proposal.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"card-proposal.js","sourceRoot":"","sources":["../../../server/service/card/card-proposal.ts"],"names":[],"mappings":";;;AAmCA,4CAUC;AA7CD;;;GAGG;AACH,+DAA4D;AAE5D,mDAAsD;AAEtD;;;;GAIG;AACI,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAsB,EAAE,CAAC,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAA,4BAAa,EAAC,yBAAS,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;AAA5H,QAAA,QAAQ,YAAoH;AAgBzI,MAAM,cAAc,GAAG,CAAC,GAAQ,EAAuB,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,yBAAS,IAAI,GAAG,EAAE,IAAI,KAAK,gBAAgB,IAAI,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,UAAU,CAAA;AAE9I,8FAA8F;AAC9F,MAAM,QAAQ,GAAG,CAAC,CAAe,EAAE,CAAe,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAEvJ,2HAA2H;AAC3H,SAAgB,gBAAgB,CAAC,QAAiD,EAAE,GAAW;IAC7F,IAAI,EAAE,GAAG,CAAC,CAAC,CAAA;IACX,IAAI,KAAU,CAAA;IACd,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE;QAC9B,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,SAAS,IAAI,EAAE;YAAE,IAAI,GAAG,EAAE,GAAG,KAAK,GAAG;gBAAE,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,GAAG,GAAG,CAAC,CAAA;IAC1F,CAAC,CAAC,CAAA;IACF,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,CAAA;IACxC,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAA;IACxE,MAAM,UAAU,GAAG,QAAQ,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAA;IAC7I,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,CAAA;AACxC,CAAC","sourcesContent":["/*\n * The card proposals of a conversation - which one a press means, and whether a later one replaced it\n * (ADR-0160 decision 9). Pure - tested without a database.\n */\nimport { recordIdOfKey } from '@things-factory/ai-assistant'\n\nimport { CARD_AXIS, CARD_TOOL } from './card-names.js'\n\n/**\n * The saved card a model named - its uuid, written alone or as the key `dashboard-card:<uuid>` a picked card is handed\n * by. Nothing for anything else: a key of another record or a name is no card, and is never looked up as one (a\n * database whose ids are uuids refuses the lookup itself - the card edit failed on 「card id not uuid」, 2026-10-25).\n */\nexport const cardIdOf = (given: unknown): string | undefined => (typeof given === 'string' ? recordIdOfKey(CARD_AXIS, given) : undefined)\n\nexport interface CardProposal {\n key: string\n tool: string\n kind: string\n label?: string\n card: {\n name: string\n description?: string\n definition: any\n cardId?: string\n baseVersion?: number\n }\n}\n\nconst isCardProposal = (one: any): one is CardProposal => one?.tool === CARD_TOOL && one?.kind === 'dashboard-card' && !!one?.card?.definition\n\n/** The same card, said again - a change of one saved card, or a new card of the same name. */\nconst sameCard = (a: CardProposal, b: CardProposal) => (a.card.cardId || b.card.cardId ? a.card.cardId === b.card.cardId : a.card.name === b.card.name)\n\n/** The proposal a key names in a conversation's messages (oldest first), and whether a later card proposal replaced it. */\nexport function findCardProposal(messages: readonly { proposals?: any[] | null }[], key: string): { proposal?: CardProposal; superseded: boolean; notACard?: boolean } {\n let at = -1\n let found: any\n messages.forEach((message, i) => {\n for (const one of message.proposals ?? []) if (one?.key === key) (at = i), (found = one)\n })\n if (!found) return { superseded: false }\n if (!isCardProposal(found)) return { superseded: false, notACard: true }\n const superseded = messages.slice(at + 1).some(message => (message.proposals ?? []).some(one => isCardProposal(one) && sameCard(one, found)))\n return { proposal: found, superseded }\n}\n"]}
|