@mintlify/common 1.0.1310 → 1.0.1311

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 (54) hide show
  1. package/dist/databases/formula/analyze.d.ts +31 -0
  2. package/dist/databases/formula/analyze.js +296 -0
  3. package/dist/databases/formula/ast.d.ts +52 -0
  4. package/dist/databases/formula/ast.js +20 -0
  5. package/dist/databases/formula/compile.d.ts +10 -0
  6. package/dist/databases/formula/compile.js +409 -0
  7. package/dist/databases/formula/dates.d.ts +5 -0
  8. package/dist/databases/formula/dates.js +55 -0
  9. package/dist/databases/formula/errors.d.ts +18 -0
  10. package/dist/databases/formula/errors.js +33 -0
  11. package/dist/databases/formula/filter.d.ts +9 -0
  12. package/dist/databases/formula/filter.js +16 -0
  13. package/dist/databases/formula/format.d.ts +5 -0
  14. package/dist/databases/formula/format.js +66 -0
  15. package/dist/databases/formula/function-docs.d.ts +6 -0
  16. package/dist/databases/formula/function-docs.js +63 -0
  17. package/dist/databases/formula/functions.d.ts +21 -0
  18. package/dist/databases/formula/functions.js +161 -0
  19. package/dist/databases/formula/highlight.d.ts +7 -0
  20. package/dist/databases/formula/highlight.js +54 -0
  21. package/dist/databases/formula/index.d.ts +16 -0
  22. package/dist/databases/formula/index.js +14 -0
  23. package/dist/databases/formula/limits.d.ts +8 -0
  24. package/dist/databases/formula/limits.js +8 -0
  25. package/dist/databases/formula/lower.d.ts +9 -0
  26. package/dist/databases/formula/lower.js +252 -0
  27. package/dist/databases/formula/parse.d.ts +21 -0
  28. package/dist/databases/formula/parse.js +209 -0
  29. package/dist/databases/formula/references.d.ts +20 -0
  30. package/dist/databases/formula/references.js +76 -0
  31. package/dist/databases/formula/rewrite.d.ts +16 -0
  32. package/dist/databases/formula/rewrite.js +29 -0
  33. package/dist/databases/formula/schema.d.ts +29 -0
  34. package/dist/databases/formula/schema.js +117 -0
  35. package/dist/databases/formula/tokenize.d.ts +11 -0
  36. package/dist/databases/formula/tokenize.js +114 -0
  37. package/dist/databases/formula/typecheck.d.ts +11 -0
  38. package/dist/databases/formula/typecheck.js +16 -0
  39. package/dist/databases/formula/types.d.ts +26 -0
  40. package/dist/databases/formula/types.js +27 -0
  41. package/dist/databases/formula/values.d.ts +8 -0
  42. package/dist/databases/formula/values.js +100 -0
  43. package/dist/databases/query/evaluate.d.ts +25 -0
  44. package/dist/databases/query/evaluate.js +481 -0
  45. package/dist/databases/query/index.d.ts +4 -0
  46. package/dist/databases/query/index.js +4 -0
  47. package/dist/databases/query/types.d.ts +69 -0
  48. package/dist/databases/query/types.js +20 -0
  49. package/dist/databases/query/validate.d.ts +8 -0
  50. package/dist/databases/query/validate.js +138 -0
  51. package/dist/databases/query/values.d.ts +4 -0
  52. package/dist/databases/query/values.js +36 -0
  53. package/dist/tsconfig.build.tsbuildinfo +1 -1
  54. package/package.json +10 -2
@@ -0,0 +1,31 @@
1
+ import { type FormulaNode, type FormulaRefNode } from './ast.js';
2
+ import { FormulaError } from './errors.js';
3
+ import { Schema } from './schema.js';
4
+ import { type FormulaColumn, type TypeSet } from './types.js';
5
+ export type Analysis = {
6
+ type: TypeSet;
7
+ diagnostics: FormulaError[];
8
+ failed: boolean;
9
+ cyclic: boolean;
10
+ height: number;
11
+ volatile: boolean;
12
+ truncated: boolean;
13
+ columns: Map<FormulaRefNode, FormulaColumn>;
14
+ types: Map<FormulaNode, TypeSet>;
15
+ options: Map<FormulaNode, string[] | null>;
16
+ dates: Map<FormulaNode, Date>;
17
+ days: Map<FormulaNode, DayRange>;
18
+ memberships: Map<FormulaNode, FormulaColumn>;
19
+ };
20
+ export type DayRange = {
21
+ start: Date;
22
+ end: Date;
23
+ };
24
+ export declare const analyze: (ast: FormulaNode, schema: Schema) => Analysis;
25
+ export declare const schemaFor: (columns: readonly FormulaColumn[]) => Schema;
26
+ export declare const analyzeFor: (ast: FormulaNode, columns: readonly FormulaColumn[], columnId?: string) => {
27
+ analysis: Analysis;
28
+ schema: Schema;
29
+ ast: FormulaNode;
30
+ stored?: FormulaColumn;
31
+ };
@@ -0,0 +1,296 @@
1
+ import { normalizeDate } from '../query/values.js';
2
+ import { COMPARISONS, } from './ast.js';
3
+ import { parseDateUnit } from './dates.js';
4
+ import { FormulaError } from './errors.js';
5
+ import { arityMessage, DATE_FUNCTIONS, getFunction } from './functions.js';
6
+ import { FORMULA_LIMITS } from './limits.js';
7
+ import { Schema } from './schema.js';
8
+ import { ANY, BOOLEAN, DATE, describeTypes, NUMBER, STRING, } from './types.js';
9
+ const DATE_ONLY = /^\d{4}-\d{2}-\d{2}$/;
10
+ const DAY_MS = 86400000;
11
+ const ORDERED = NUMBER | STRING | DATE;
12
+ const isSelect = (column) => (column === null || column === void 0 ? void 0 : column.type) === 'select' || (column === null || column === void 0 ? void 0 : column.type) === 'multi_select';
13
+ export const analyze = (ast, schema) => {
14
+ const analysis = {
15
+ type: ANY,
16
+ diagnostics: [],
17
+ failed: false,
18
+ cyclic: false,
19
+ height: 0,
20
+ volatile: false,
21
+ truncated: false,
22
+ columns: new Map(),
23
+ types: new Map(),
24
+ options: new Map(),
25
+ dates: new Map(),
26
+ days: new Map(),
27
+ memberships: new Map(),
28
+ };
29
+ const report = (code, message, span, severity = 'error') => {
30
+ analysis.diagnostics.push(new FormulaError(code, message, span.start, span.end, severity));
31
+ };
32
+ const refColumn = (node) => node.kind === 'ref' ? analysis.columns.get(node) : undefined;
33
+ const visitRef = (node) => {
34
+ const nameSpan = node.form === 'bare' ? node : { start: node.nameStart, end: node.nameEnd };
35
+ const column = node.id === undefined ? schema.byName.get(node.name) : schema.byId.get(node.id);
36
+ if (!column) {
37
+ const label = node.id === undefined ? node.name : `#${node.id}`;
38
+ report('unknown_property', `Unknown property '${label}'`, nameSpan);
39
+ return ANY;
40
+ }
41
+ analysis.columns.set(node, column);
42
+ if (column.type !== 'formula')
43
+ return schema.storedType(column);
44
+ const info = schema.formula(column);
45
+ analysis.height = Math.max(analysis.height, info.height + 1);
46
+ analysis.volatile || (analysis.volatile = info.volatile);
47
+ analysis.truncated || (analysis.truncated = info.truncated);
48
+ if (!info.cyclic && info.height + 1 > FORMULA_LIMITS.depth) {
49
+ report('depth', 'Formula properties are nested too deeply', node);
50
+ }
51
+ else if (info.cyclic) {
52
+ report('cycle', `Circular reference to '${column.name}'`, node);
53
+ analysis.cyclic = true;
54
+ }
55
+ else if (info.failed) {
56
+ report('dependency', `Property '${column.name}' has an error`, node);
57
+ }
58
+ return info.type;
59
+ };
60
+ const resolveOption = (literal, column) => {
61
+ if (literal.kind !== 'string')
62
+ return;
63
+ if (literal.value === '')
64
+ return;
65
+ const { idsByLabel, labelById } = schema.optionsOf(column);
66
+ const ids = idsByLabel.get(literal.value);
67
+ if (ids === undefined) {
68
+ report('unknown_option', `'${literal.value}' is not an option of '${column.name}'`, literal, 'warning');
69
+ }
70
+ analysis.options.set(literal, ids !== null && ids !== void 0 ? ids : (labelById.has(literal.value) ? null : [literal.value]));
71
+ };
72
+ const coerceDate = (literal) => {
73
+ if (literal.kind !== 'string')
74
+ return false;
75
+ const normalized = normalizeDate(literal.value);
76
+ if (normalized === null) {
77
+ report('type', `'${literal.value}' is not an ISO 8601 date`, literal);
78
+ }
79
+ else {
80
+ analysis.dates.set(literal, new Date(normalized));
81
+ }
82
+ return true;
83
+ };
84
+ const visitCompare = (node, left, right) => {
85
+ const operator = { start: node.operatorStart, end: node.operatorEnd };
86
+ const sides = [
87
+ [node.left, node.right],
88
+ [node.right, node.left],
89
+ ];
90
+ for (const [side, other] of sides) {
91
+ const column = refColumn(side);
92
+ if (!isSelect(column) || !column)
93
+ continue;
94
+ if (column.type === 'multi_select') {
95
+ if (node.operator !== '==' && node.operator !== '!=') {
96
+ report('type', `'${column.name}' is a multi-select and only supports == and !=`, operator);
97
+ return BOOLEAN;
98
+ }
99
+ if (other.kind !== 'string') {
100
+ report('type', `Compare '${column.name}' with an option name`, other);
101
+ return BOOLEAN;
102
+ }
103
+ }
104
+ resolveOption(other, column);
105
+ }
106
+ for (const [side, other] of sides) {
107
+ if (side.kind === 'string' && side.value === '') {
108
+ report('empty_comparison', 'Comparisons with "" are always false; use empty()', side, 'warning');
109
+ }
110
+ const sideType = side === node.left ? left : right;
111
+ if (side.kind === 'string' && side.value === '') {
112
+ if (side === node.left)
113
+ left = right;
114
+ else
115
+ right = left;
116
+ continue;
117
+ }
118
+ if (sideType === DATE && other.kind === 'string' && other.value !== '') {
119
+ if (coerceDate(other)) {
120
+ if (other === node.left)
121
+ left = DATE;
122
+ else
123
+ right = DATE;
124
+ }
125
+ }
126
+ }
127
+ const shared = left & right;
128
+ if (!shared) {
129
+ report('type', `Cannot compare ${describeTypes(left)} and ${describeTypes(right)}`, operator);
130
+ }
131
+ else if (node.operator !== '==' && node.operator !== '!=' && !(shared & ORDERED)) {
132
+ report('type', `Cannot order ${describeTypes(shared)} values`, operator);
133
+ }
134
+ return BOOLEAN;
135
+ };
136
+ const visitBinary = (node) => {
137
+ const left = visit(node.left);
138
+ const right = visit(node.right);
139
+ const operator = { start: node.operatorStart, end: node.operatorEnd };
140
+ if (COMPARISONS.has(node.operator))
141
+ return visitCompare(node, left, right);
142
+ if (node.operator === 'and' || node.operator === 'or') {
143
+ for (const [side, types] of [
144
+ [node.left, left],
145
+ [node.right, right],
146
+ ]) {
147
+ if (!(types & BOOLEAN)) {
148
+ report('type', `Expected a boolean for '${node.operator}'`, side);
149
+ }
150
+ }
151
+ return BOOLEAN;
152
+ }
153
+ if (node.operator === '+') {
154
+ const sum = (left & right & (NUMBER | STRING));
155
+ if (!sum) {
156
+ report('type', `Cannot add ${describeTypes(left)} and ${describeTypes(right)}`, operator);
157
+ return NUMBER | STRING;
158
+ }
159
+ return sum;
160
+ }
161
+ for (const [side, types] of [
162
+ [node.left, left],
163
+ [node.right, right],
164
+ ]) {
165
+ if (!(types & NUMBER))
166
+ report('type', `Expected a number for '${node.operator}'`, side);
167
+ }
168
+ return NUMBER;
169
+ };
170
+ const visitCall = (node) => {
171
+ if (node.name === 'now')
172
+ analysis.volatile = true;
173
+ const types = node.args.map(visit);
174
+ const fn = getFunction(node.name);
175
+ const nameSpan = { start: node.start, end: node.start + node.name.length };
176
+ if (!fn) {
177
+ report('unknown_function', `Unknown function '${node.name}'`, nameSpan);
178
+ return ANY;
179
+ }
180
+ if (node.args.length < fn.min || node.args.length > fn.max) {
181
+ report('arity', arityMessage(node.name, fn), node);
182
+ return fn.returns(types);
183
+ }
184
+ node.args.forEach((arg, index) => {
185
+ var _a;
186
+ const expected = fn.params(index);
187
+ if (!(((_a = types[index]) !== null && _a !== void 0 ? _a : ANY) & expected)) {
188
+ report('type', `${node.name} expects ${describeTypes(expected)}`, arg);
189
+ }
190
+ });
191
+ const [first, second] = node.args;
192
+ if (node.name === 'date' && (first === null || first === void 0 ? void 0 : first.kind) === 'string') {
193
+ const normalized = normalizeDate(first.value);
194
+ if (normalized === null) {
195
+ report('type', `'${first.value}' is not an ISO 8601 date`, first);
196
+ }
197
+ else {
198
+ const start = new Date(normalized);
199
+ analysis.dates.set(node, start);
200
+ if (DATE_ONLY.test(first.value)) {
201
+ analysis.days.set(node, { start, end: new Date(start.getTime() + DAY_MS) });
202
+ }
203
+ }
204
+ }
205
+ const listed = first ? refColumn(first) : undefined;
206
+ if (node.name === 'contains' && (listed === null || listed === void 0 ? void 0 : listed.type) === 'multi_select' && second) {
207
+ if (second.kind === 'string') {
208
+ resolveOption(second, listed);
209
+ analysis.memberships.set(node, listed);
210
+ }
211
+ else {
212
+ report('type', `Check '${listed.name}' for an option name`, second);
213
+ }
214
+ }
215
+ const unit = node.args[2];
216
+ if (DATE_FUNCTIONS.has(node.name) && (unit === null || unit === void 0 ? void 0 : unit.kind) === 'string') {
217
+ try {
218
+ parseDateUnit(unit.value, unit.start);
219
+ }
220
+ catch (_a) {
221
+ report('type', `Unknown date unit '${unit.value}'`, unit);
222
+ }
223
+ }
224
+ return fn.returns(types);
225
+ };
226
+ const visit = (node) => {
227
+ const types = visitNode(node);
228
+ analysis.types.set(node, types);
229
+ return types;
230
+ };
231
+ const visitNode = (node) => {
232
+ switch (node.kind) {
233
+ case 'empty':
234
+ return ANY;
235
+ case 'number':
236
+ return NUMBER;
237
+ case 'string':
238
+ return STRING;
239
+ case 'boolean':
240
+ return BOOLEAN;
241
+ case 'ref':
242
+ return visitRef(node);
243
+ case 'unary': {
244
+ const operand = visit(node.operand);
245
+ const operator = { start: node.start, end: node.start + node.operator.length };
246
+ if (node.operator === 'not') {
247
+ if (!(operand & BOOLEAN))
248
+ report('type', "Expected a boolean for 'not'", operator);
249
+ return BOOLEAN;
250
+ }
251
+ if (!(operand & NUMBER))
252
+ report('type', `Expected a number for '${node.operator}'`, operator);
253
+ return NUMBER;
254
+ }
255
+ case 'binary':
256
+ return visitBinary(node);
257
+ case 'call':
258
+ return visitCall(node);
259
+ }
260
+ };
261
+ analysis.type = visit(ast);
262
+ analysis.failed = analysis.diagnostics.some((diagnostic) => diagnostic.severity === 'error');
263
+ return analysis;
264
+ };
265
+ const sameNode = (left, right) => {
266
+ if (left === right)
267
+ return true;
268
+ if (typeof left !== 'object' || typeof right !== 'object' || !left || !right)
269
+ return false;
270
+ const keys = Object.keys(left);
271
+ if (keys.length !== Object.keys(right).length)
272
+ return false;
273
+ return keys.every((key) => sameNode(left[key], right[key]));
274
+ };
275
+ const schemas = new WeakMap();
276
+ export const schemaFor = (columns) => {
277
+ let schema = schemas.get(columns);
278
+ if (!schema) {
279
+ schema = new Schema(columns, analyze);
280
+ schemas.set(columns, schema);
281
+ }
282
+ return schema;
283
+ };
284
+ export const analyzeFor = (ast, columns, columnId) => {
285
+ var _a;
286
+ const base = schemaFor(columns);
287
+ const column = columnId === undefined ? undefined : base.byId.get(columnId);
288
+ if ((column === null || column === void 0 ? void 0 : column.type) !== 'formula')
289
+ return { analysis: analyze(ast, base), schema: base, ast };
290
+ const stored = base.parse(column);
291
+ const analysis = 'ast' in stored && sameNode(stored.ast, ast) ? base.analysis(column) : undefined;
292
+ if (analysis && 'ast' in stored)
293
+ return { analysis, schema: base, ast: stored.ast, stored: column };
294
+ const schema = base.withExpression(column.id, { ast });
295
+ return { analysis: (_a = schema.analysis(column)) !== null && _a !== void 0 ? _a : analyze(ast, schema), schema, ast };
296
+ };
@@ -0,0 +1,52 @@
1
+ export type BinaryOperator = '+' | '-' | '*' | '/' | '%' | '^' | '==' | '!=' | '<' | '<=' | '>' | '>=' | 'and' | 'or';
2
+ export type UnaryOperator = '-' | '+' | 'not';
3
+ export type Span = {
4
+ start: number;
5
+ end: number;
6
+ };
7
+ export type FormulaNode = Span & ({
8
+ kind: 'empty';
9
+ } | {
10
+ kind: 'number';
11
+ value: number;
12
+ } | {
13
+ kind: 'string';
14
+ value: string;
15
+ } | {
16
+ kind: 'boolean';
17
+ value: boolean;
18
+ } | {
19
+ kind: 'ref';
20
+ name: string;
21
+ id?: string;
22
+ form: 'bare' | 'prop' | 'id';
23
+ nameStart: number;
24
+ nameEnd: number;
25
+ } | {
26
+ kind: 'unary';
27
+ operator: UnaryOperator;
28
+ operand: FormulaNode;
29
+ } | {
30
+ kind: 'binary';
31
+ operator: BinaryOperator;
32
+ operatorStart: number;
33
+ operatorEnd: number;
34
+ left: FormulaNode;
35
+ right: FormulaNode;
36
+ } | {
37
+ kind: 'call';
38
+ name: string;
39
+ args: FormulaNode[];
40
+ });
41
+ export type FormulaRefNode = Extract<FormulaNode, {
42
+ kind: 'ref';
43
+ }>;
44
+ export type FormulaCallNode = Extract<FormulaNode, {
45
+ kind: 'call';
46
+ }>;
47
+ export type FormulaBinaryNode = Extract<FormulaNode, {
48
+ kind: 'binary';
49
+ }>;
50
+ export declare const COMPARISONS: ReadonlySet<BinaryOperator>;
51
+ export declare const childrenOf: (node: FormulaNode) => FormulaNode[];
52
+ export declare const walkFormula: (ast: FormulaNode, visit: (node: FormulaNode) => void) => void;
@@ -0,0 +1,20 @@
1
+ export const COMPARISONS = new Set(['==', '!=', '<', '<=', '>', '>=']);
2
+ export const childrenOf = (node) => {
3
+ if (node.kind === 'unary')
4
+ return [node.operand];
5
+ if (node.kind === 'binary')
6
+ return [node.left, node.right];
7
+ if (node.kind === 'call')
8
+ return node.args;
9
+ return [];
10
+ };
11
+ export const walkFormula = (ast, visit) => {
12
+ const stack = [ast];
13
+ for (let node = stack.pop(); node; node = stack.pop()) {
14
+ visit(node);
15
+ const children = childrenOf(node);
16
+ for (let index = children.length - 1; index >= 0; index--) {
17
+ stack.push(children[index]);
18
+ }
19
+ }
20
+ };
@@ -0,0 +1,10 @@
1
+ import { type FormulaNode } from './ast.js';
2
+ import type { FormulaColumn, FormulaRow, FormulaValue } from './types.js';
3
+ export type CompiledFormula = (row: FormulaRow) => FormulaValue;
4
+ export type CompileOptions = {
5
+ now?: () => Date;
6
+ columnId?: string;
7
+ };
8
+ export declare const compile: (ast: FormulaNode, columns: readonly FormulaColumn[], options?: CompileOptions) => CompiledFormula;
9
+ export type ColumnPrograms = (columnId: string) => CompiledFormula;
10
+ export declare const compileColumns: (columns: readonly FormulaColumn[], options?: Pick<CompileOptions, 'now'>) => ColumnPrograms;