@ganju/utils 0.0.16 → 0.0.18
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/dist/constants.d.ts +9 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +26 -2
- package/dist/customCodeToken.d.ts +15 -0
- package/dist/customCodeToken.d.ts.map +1 -1
- package/dist/customCodeToken.js +49 -1
- package/dist/data.d.ts +6 -0
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +53 -1
- package/dist/dataApi.d.ts +200 -1
- package/dist/dataApi.d.ts.map +1 -1
- package/dist/dataApi.js +106 -1
- package/dist/dataCompiler.d.ts.map +1 -1
- package/dist/dataCompiler.js +5 -1
- package/dist/dataCsv.d.ts +20 -0
- package/dist/dataCsv.d.ts.map +1 -0
- package/dist/dataCsv.js +102 -0
- package/dist/dataDdl.d.ts.map +1 -1
- package/dist/dataDdl.js +3 -1
- package/dist/dataEngine.d.ts.map +1 -1
- package/dist/dataEngine.js +86 -4
- package/dist/dataImport.d.ts +2 -0
- package/dist/dataImport.d.ts.map +1 -1
- package/dist/dataImport.js +4 -1
- package/dist/dataQueries.d.ts +73 -0
- package/dist/dataQueries.d.ts.map +1 -0
- package/dist/dataQueries.js +513 -0
- package/dist/dataRpc.d.ts +44 -0
- package/dist/dataRpc.d.ts.map +1 -1
- package/dist/dataTools.d.ts +40 -0
- package/dist/dataTools.d.ts.map +1 -0
- package/dist/dataTools.js +280 -0
- package/dist/dataTriggers.d.ts +203 -0
- package/dist/dataTriggers.d.ts.map +1 -0
- package/dist/dataTriggers.js +760 -0
- package/dist/dataTypes.d.ts +3 -0
- package/dist/dataTypes.d.ts.map +1 -1
- package/dist/index.d.ts +201 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/localizeDataError.d.ts +18 -0
- package/dist/localizeDataError.d.ts.map +1 -0
- package/dist/localizeDataError.js +359 -0
- package/dist/schema.d.ts +201 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +149 -1
- package/dist/sdkConstants.d.ts +3 -0
- package/dist/sdkConstants.d.ts.map +1 -1
- package/dist/sdkConstants.js +4 -1
- package/package.json +1 -1
- package/src/constants.ts +30 -2
- package/src/customCodeToken.ts +87 -0
- package/src/data.ts +74 -0
- package/src/dataApi.ts +129 -2
- package/src/dataCompiler.ts +5 -1
- package/src/dataCsv.ts +99 -0
- package/src/dataDdl.ts +3 -1
- package/src/dataEngine.ts +94 -4
- package/src/dataImport.ts +3 -0
- package/src/dataQueries.ts +730 -0
- package/src/dataRpc.ts +26 -0
- package/src/dataTools.ts +338 -0
- package/src/dataTriggers.ts +1134 -0
- package/src/dataTypes.ts +9 -0
- package/src/index.ts +11 -1
- package/src/localizeDataError.ts +593 -0
- package/src/schema.ts +158 -1
- package/src/sdkConstants.ts +3 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dataAdminToolDefinition = exports.DATA_ADMIN_ACTIONS = exports.dataToolDefinitions = exports.hasDataToolTables = exports.DATA_TOOL_MAX_LIMIT = exports.DATA_TOOL_TITLES = exports.DATA_TOOL_NAMES = exports.DATA_TOOL_ADMIN = exports.DATA_TOOL_WRITE = exports.DATA_TOOL_QUERY = exports.DATA_TOOL_DESCRIBE = void 0;
|
|
4
|
+
// The data tools as a model sees them: names, descriptions and input schemas,
|
|
5
|
+
// built from the database's schema for one caller. The MCP worker registers
|
|
6
|
+
// them and the tool-design eval measures them, so the text the eval scored is
|
|
7
|
+
// the text that ships.
|
|
8
|
+
const dataTypes_1 = require("./dataTypes");
|
|
9
|
+
exports.DATA_TOOL_DESCRIBE = 'data-describe';
|
|
10
|
+
exports.DATA_TOOL_QUERY = 'data-query';
|
|
11
|
+
exports.DATA_TOOL_WRITE = 'data-write';
|
|
12
|
+
exports.DATA_TOOL_ADMIN = 'data-admin';
|
|
13
|
+
exports.DATA_TOOL_NAMES = [
|
|
14
|
+
exports.DATA_TOOL_DESCRIBE,
|
|
15
|
+
exports.DATA_TOOL_QUERY,
|
|
16
|
+
exports.DATA_TOOL_WRITE,
|
|
17
|
+
exports.DATA_TOOL_ADMIN
|
|
18
|
+
];
|
|
19
|
+
// How Tool health names them: they have no install row, and aren't a name
|
|
20
|
+
// the model made up.
|
|
21
|
+
exports.DATA_TOOL_TITLES = {
|
|
22
|
+
[exports.DATA_TOOL_DESCRIBE]: 'Describe the database',
|
|
23
|
+
[exports.DATA_TOOL_QUERY]: 'Query the database',
|
|
24
|
+
[exports.DATA_TOOL_WRITE]: 'Change rows in the database',
|
|
25
|
+
[exports.DATA_TOOL_ADMIN]: 'Manage the database'
|
|
26
|
+
};
|
|
27
|
+
// How many rows data-query returns at most. The engine pages up to 500; a
|
|
28
|
+
// model reading 500 rows answers worse, not better.
|
|
29
|
+
exports.DATA_TOOL_MAX_LIMIT = 100;
|
|
30
|
+
const READS = {
|
|
31
|
+
readOnlyHint: true,
|
|
32
|
+
destructiveHint: false,
|
|
33
|
+
openWorldHint: false
|
|
34
|
+
};
|
|
35
|
+
// One annotation per tool: a write may delete, so the tool says it is
|
|
36
|
+
// destructive, and the runner and the MCP confirmation decide per call.
|
|
37
|
+
const CHANGES = {
|
|
38
|
+
readOnlyHint: false,
|
|
39
|
+
destructiveHint: true,
|
|
40
|
+
openWorldHint: false
|
|
41
|
+
};
|
|
42
|
+
const WHERE_HELP = 'Filter object. { "column": value } matches exactly; { "column": { "op": value } } with op one of eq, ne, gt, gte, lt, lte, in, notIn (lists), contains, startsWith (text, case-insensitive), isNull (true/false). Several keys must all match; { "or": [ {...}, {...} ] } matches any. A relation column takes the related row\'s id, or filter on a column of the related table with "relation.column", e.g. { "product.name": "Margherita" }. Datetimes are ISO 8601; include the offset for local times, e.g. "2026-10-19T00:00:00-05:00".';
|
|
43
|
+
const accessOf = (caller, table) => caller.restricted ? table.access : 'full';
|
|
44
|
+
const visible = (schema, caller) => schema.tables.filter(table => dataTypes_1.DATA_ACCESS_RANK[accessOf(caller, table)] >= 1);
|
|
45
|
+
// One column as data-query's description lists it: the type when it isn't
|
|
46
|
+
// plain text, so the model can write filters without guessing names.
|
|
47
|
+
const columnHint = (column) => column.type === 'relation'
|
|
48
|
+
? `${column.name} (→ ${column.table})`
|
|
49
|
+
: column.type === 'enum'
|
|
50
|
+
? `${column.name} (${(column.values ?? []).join('|')})`
|
|
51
|
+
: column.type === 'text' || column.type === 'long_text'
|
|
52
|
+
? column.name
|
|
53
|
+
: `${column.name} (${column.type}${column.min !== null ? `, ≥ ${column.min}` : ''})`;
|
|
54
|
+
const tableLine = (caller, table) => {
|
|
55
|
+
const access = accessOf(caller, table);
|
|
56
|
+
const description = table.description?.trim().replace(/\.$/, '');
|
|
57
|
+
return `- ${table.name}${description ? `: ${description}.` : '.'}${caller.restricted && table.customerScoped
|
|
58
|
+
? " Only this customer's own rows are visible."
|
|
59
|
+
: ''}${access === 'read' ? ' Read only.' : access === 'add' ? ' Rows can be added, not changed.' : ''} Columns: ${table.columns
|
|
60
|
+
.map(columnHint)
|
|
61
|
+
.join(', ')}.`;
|
|
62
|
+
};
|
|
63
|
+
const queryLine = (query) => `- ${query.name}${query.description ? `: ${query.description.replace(/\.$/, '')}.` : '.'}${query.params.length
|
|
64
|
+
? ` Params: ${query.params
|
|
65
|
+
.map(p => `${p.name} (${p.type}${p.required === false ? ', optional' : ''})`)
|
|
66
|
+
.join(', ')}.`
|
|
67
|
+
: ''}`;
|
|
68
|
+
/** Whether the caller may use the data tools at all: it sees a table. */
|
|
69
|
+
const hasDataToolTables = (schema, caller) => visible(schema, caller).length > 0;
|
|
70
|
+
exports.hasDataToolTables = hasDataToolTables;
|
|
71
|
+
/**
|
|
72
|
+
* data-describe, data-query and, when the caller may add rows somewhere,
|
|
73
|
+
* data-write. Only tables the caller may see are named, so a hidden table
|
|
74
|
+
* doesn't exist for the model. `queries` are the named queries it may run.
|
|
75
|
+
*/
|
|
76
|
+
const dataToolDefinitions = (schema, caller, options = {}) => {
|
|
77
|
+
const tables = visible(schema, caller);
|
|
78
|
+
if (tables.length === 0)
|
|
79
|
+
return [];
|
|
80
|
+
const queries = options.queries ?? [];
|
|
81
|
+
const timeZone = caller.timeZone || 'UTC';
|
|
82
|
+
const tools = [
|
|
83
|
+
{
|
|
84
|
+
name: exports.DATA_TOOL_DESCRIBE,
|
|
85
|
+
title: exports.DATA_TOOL_TITLES[exports.DATA_TOOL_DESCRIBE],
|
|
86
|
+
description: "Describe the project's database: its tables, their columns (type, allowed values, which table a relation points to, the owner's description of each), row counts, which columns full-text search covers, and what the assistant may do with each table (read, add, or full). Call it before querying a table whose columns you haven't seen in this conversation.",
|
|
87
|
+
inputSchema: {
|
|
88
|
+
type: 'object',
|
|
89
|
+
properties: {
|
|
90
|
+
table: {
|
|
91
|
+
type: 'string',
|
|
92
|
+
description: 'One table to describe. Leave out to describe all of them.'
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
annotations: READS
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: exports.DATA_TOOL_QUERY,
|
|
100
|
+
title: exports.DATA_TOOL_TITLES[exports.DATA_TOOL_QUERY],
|
|
101
|
+
description: [
|
|
102
|
+
"Read rows from the project's database: structured records such as products, prices, stock, orders, bookings or customers. Use it for anything with fields and numbers; use the knowledge/resource tools for documents and pages.",
|
|
103
|
+
'Returns matching rows (relation columns come back as { id, label }), the total that match, and nextCursor when there are more. With "aggregate" it returns counts, sums, averages, minimums or maximums instead of rows, optionally per group: use that for "how many" and "how much" rather than adding rows up yourself.',
|
|
104
|
+
"The database holds only what these tables and columns hold. When a question needs something they don't have (opening hours, parking, calories, reviews, forecasts), look in the knowledge tools if you have them; if nothing has it, say you don't have that information. Never guess it.",
|
|
105
|
+
`Every table also has id, created_at and updated_at. Datetimes come back in the project's time zone (${timeZone}) with their offset; read them as local times.`,
|
|
106
|
+
'Tables:',
|
|
107
|
+
tables.map(table => tableLine(caller, table)).join('\n'),
|
|
108
|
+
...(queries.length
|
|
109
|
+
? [
|
|
110
|
+
'Saved reports: instead of "table", pass "query" with its name and "params"; they return exactly what the report computes.',
|
|
111
|
+
queries.map(queryLine).join('\n')
|
|
112
|
+
]
|
|
113
|
+
: [])
|
|
114
|
+
].join('\n'),
|
|
115
|
+
inputSchema: {
|
|
116
|
+
type: 'object',
|
|
117
|
+
properties: {
|
|
118
|
+
table: { type: 'string', description: 'Table to read.' },
|
|
119
|
+
where: { type: 'object', description: WHERE_HELP },
|
|
120
|
+
search: {
|
|
121
|
+
type: 'string',
|
|
122
|
+
description: "Words to look for in the table's searchable text columns (accents and case ignored, ranked by relevance). Use it when the user names something loosely; use where for exact values."
|
|
123
|
+
},
|
|
124
|
+
sort: {
|
|
125
|
+
type: 'array',
|
|
126
|
+
items: { type: 'string' },
|
|
127
|
+
description: 'Columns to sort by, "-" first for descending, e.g. ["-created_at"]. Relation paths allowed.'
|
|
128
|
+
},
|
|
129
|
+
limit: {
|
|
130
|
+
type: 'integer',
|
|
131
|
+
description: `Rows to return, 1 to ${exports.DATA_TOOL_MAX_LIMIT}. Default 20.`
|
|
132
|
+
},
|
|
133
|
+
cursor: {
|
|
134
|
+
type: 'string',
|
|
135
|
+
description: 'nextCursor from the previous call, to get the next page.'
|
|
136
|
+
},
|
|
137
|
+
include: {
|
|
138
|
+
type: 'array',
|
|
139
|
+
items: { type: 'string' },
|
|
140
|
+
description: 'Relation columns to expand into the full related row, e.g. ["customer"].'
|
|
141
|
+
},
|
|
142
|
+
aggregate: {
|
|
143
|
+
type: 'array',
|
|
144
|
+
description: 'Compute instead of listing rows. Each item is { "fn": "count" } or { "fn": "sum"|"avg"|"min"|"max", "column": "..." } (relation paths allowed, e.g. "order.total"). Add "times": "<number column>" to aggregate a product, e.g. { "fn": "sum", "column": "stock", "times": "unit_cost" } for stock value.',
|
|
145
|
+
items: {
|
|
146
|
+
type: 'object',
|
|
147
|
+
properties: {
|
|
148
|
+
fn: {
|
|
149
|
+
type: 'string',
|
|
150
|
+
enum: ['count', 'sum', 'avg', 'min', 'max']
|
|
151
|
+
},
|
|
152
|
+
column: { type: 'string' },
|
|
153
|
+
times: { type: 'string' }
|
|
154
|
+
},
|
|
155
|
+
required: ['fn']
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
groupBy: {
|
|
159
|
+
type: 'array',
|
|
160
|
+
items: { type: 'string' },
|
|
161
|
+
description: 'With aggregate: columns to group by, e.g. ["status"] or ["product"]. Groups come back largest first.'
|
|
162
|
+
},
|
|
163
|
+
...(queries.length
|
|
164
|
+
? {
|
|
165
|
+
query: {
|
|
166
|
+
type: 'string',
|
|
167
|
+
enum: queries.map(q => q.name),
|
|
168
|
+
description: 'A saved report to run instead of reading a table.'
|
|
169
|
+
},
|
|
170
|
+
params: {
|
|
171
|
+
type: 'object',
|
|
172
|
+
description: 'The saved report\'s parameters, e.g. { "from": "2026-10-01" }.'
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
: {})
|
|
176
|
+
},
|
|
177
|
+
...(queries.length ? {} : { required: ['table'] })
|
|
178
|
+
},
|
|
179
|
+
annotations: READS
|
|
180
|
+
}
|
|
181
|
+
];
|
|
182
|
+
if (tables.some(table => dataTypes_1.DATA_ACCESS_RANK[accessOf(caller, table)] >= dataTypes_1.DATA_ACCESS_RANK.add)) {
|
|
183
|
+
tools.push({
|
|
184
|
+
name: exports.DATA_TOOL_WRITE,
|
|
185
|
+
title: exports.DATA_TOOL_TITLES[exports.DATA_TOOL_WRITE],
|
|
186
|
+
description: [
|
|
187
|
+
"Add, change or delete rows in the project's database. All operations in one call succeed together or none is applied, so put everything that belongs together (an order, its lines and the stock they use) in one call.",
|
|
188
|
+
'- insert: { "op": "insert", "table": "...", "values": { column: value } }. A relation column takes the related row\'s id; to point at a row inserted earlier in the same call use { "$ref": n }, n being that operation\'s position (0-based).',
|
|
189
|
+
'- update: { "op": "update", "table": "...", "id": 12, "set": { column: value } }. For numbers, { "decrement": 2 } or { "increment": 2 } changes the value relative to what is stored. Instead of id you can give "where" (same format as data-query).',
|
|
190
|
+
'- delete: { "op": "delete", "table": "...", "id": 12 } (or "where").',
|
|
191
|
+
'Guards: an update or delete also takes "where" conditions that must hold, e.g. "where": { "stock": { "gte": 2 } }, and "expect", how many rows must change (1 by default when id is given). If a guard fails, nothing in the call is written and the error says why (e.g. how much stock is left).',
|
|
192
|
+
'Only use ids you got from data-query or from an earlier insert; never guess them.'
|
|
193
|
+
].join('\n'),
|
|
194
|
+
inputSchema: {
|
|
195
|
+
type: 'object',
|
|
196
|
+
properties: {
|
|
197
|
+
operations: {
|
|
198
|
+
type: 'array',
|
|
199
|
+
items: {
|
|
200
|
+
type: 'object',
|
|
201
|
+
properties: {
|
|
202
|
+
op: { type: 'string', enum: ['insert', 'update', 'delete'] },
|
|
203
|
+
table: { type: 'string' },
|
|
204
|
+
id: { type: 'integer' },
|
|
205
|
+
values: { type: 'object' },
|
|
206
|
+
set: { type: 'object' },
|
|
207
|
+
where: { type: 'object' },
|
|
208
|
+
expect: { type: 'integer' }
|
|
209
|
+
},
|
|
210
|
+
required: ['op', 'table']
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
required: ['operations']
|
|
215
|
+
},
|
|
216
|
+
annotations: CHANGES
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
return tools;
|
|
220
|
+
};
|
|
221
|
+
exports.dataToolDefinitions = dataToolDefinitions;
|
|
222
|
+
exports.DATA_ADMIN_ACTIONS = [
|
|
223
|
+
'schema',
|
|
224
|
+
'create_table',
|
|
225
|
+
'update_table',
|
|
226
|
+
'drop_table',
|
|
227
|
+
'add_column',
|
|
228
|
+
'update_column',
|
|
229
|
+
'drop_column',
|
|
230
|
+
'save_query',
|
|
231
|
+
'drop_query',
|
|
232
|
+
'import',
|
|
233
|
+
'export'
|
|
234
|
+
];
|
|
235
|
+
/**
|
|
236
|
+
* data-admin: the owner's own MCP client managing the database's structure.
|
|
237
|
+
* One tool, so it costs one slot in the session it exists in; destructive, so
|
|
238
|
+
* every call goes through confirmation and, after outside content, the
|
|
239
|
+
* approval link.
|
|
240
|
+
*/
|
|
241
|
+
const dataAdminToolDefinition = () => ({
|
|
242
|
+
name: exports.DATA_TOOL_ADMIN,
|
|
243
|
+
title: exports.DATA_TOOL_TITLES[exports.DATA_TOOL_ADMIN],
|
|
244
|
+
description: [
|
|
245
|
+
"Manage the project's database structure as its owner: tables, columns, indexes, descriptions, saved reports, imports and exports. To read or change rows use data-query and data-write.",
|
|
246
|
+
'- schema: every table as it would be written again (columns with type, required, unique, filterable, searchable, description, enum values, relation target and minimum), plus the saved reports.',
|
|
247
|
+
'- create_table: "input" is { name, description, displayColumn, access (none|read|add|full: what channel bots may do), customerScoped, columns: [{ name, type, ... }] }. Types: text, long_text, integer, decimal, boolean, date, datetime, enum (with "values"), json, email, url, phone, relation (with "table" and "onDelete": restrict|cascade|set_null), file.',
|
|
248
|
+
'- update_table: "table" and "input" with what changes: name, description, displayColumn, access, customerScoped, history.',
|
|
249
|
+
'- drop_table: "table". It can be undone with a restore from the dashboard.',
|
|
250
|
+
'- add_column: "table" and "input" as one column of create_table.',
|
|
251
|
+
'- update_column: "table", "column" and "input" with what changes: name, type, description, required, unique, filterable (adds an index), searchable, values, min, default. A type change copies the table and refuses if a value doesn\'t fit.',
|
|
252
|
+
'- drop_column: "table" and "column".',
|
|
253
|
+
'- save_query: "input" is { name, description, sql, params: [{ name, type: text|integer|decimal|boolean|date|datetime, description }], assistant }. sql is one SELECT with named parameters (:from); it is checked to only read. assistant: true lets channel bots run it.',
|
|
254
|
+
'- drop_query: "name".',
|
|
255
|
+
'- import: "table" and "resource", the uri of a CSV or Excel file in the project\'s knowledge ("sheet" picks one, the first by default). Headers are matched to columns by name or label, and relation columns by the related row\'s name; rows are added, not replaced. Up to 5 MB.',
|
|
256
|
+
'- export: start a copy of the whole database (format jsonl or csv); download it from the Data page.'
|
|
257
|
+
].join('\n'),
|
|
258
|
+
inputSchema: {
|
|
259
|
+
type: 'object',
|
|
260
|
+
properties: {
|
|
261
|
+
action: { type: 'string', enum: [...exports.DATA_ADMIN_ACTIONS] },
|
|
262
|
+
table: { type: 'string' },
|
|
263
|
+
column: { type: 'string' },
|
|
264
|
+
name: { type: 'string', description: 'drop_query: the saved report.' },
|
|
265
|
+
resource: {
|
|
266
|
+
type: 'string',
|
|
267
|
+
description: 'import: the CSV or Excel resource uri.'
|
|
268
|
+
},
|
|
269
|
+
sheet: {
|
|
270
|
+
type: 'string',
|
|
271
|
+
description: 'import: the Excel sheet; the first by default.'
|
|
272
|
+
},
|
|
273
|
+
format: { type: 'string', enum: ['jsonl', 'csv'] },
|
|
274
|
+
input: { type: 'object' }
|
|
275
|
+
},
|
|
276
|
+
required: ['action']
|
|
277
|
+
},
|
|
278
|
+
annotations: CHANGES
|
|
279
|
+
});
|
|
280
|
+
exports.dataAdminToolDefinition = dataAdminToolDefinition;
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import type { DataCaller, DataSchema, DataTable } from './dataTypes';
|
|
2
|
+
import type { SqlRunner } from './dataEngine';
|
|
3
|
+
export declare const DATA_TRIGGER_EVENTS: readonly ["insert", "update", "delete"];
|
|
4
|
+
export type DataTriggerEvent = (typeof DATA_TRIGGER_EVENTS)[number];
|
|
5
|
+
export declare const DATA_TRIGGER_ACTIONS: readonly ["function", "webhook", "chat", "email", "customer"];
|
|
6
|
+
export type DataTriggerActionType = (typeof DATA_TRIGGER_ACTIONS)[number];
|
|
7
|
+
export declare const DATA_MAX_TRIGGERS = 50;
|
|
8
|
+
export declare const DATA_TRIGGER_MAX_DEPTH = 3;
|
|
9
|
+
export declare const DATA_TRIGGER_DEFAULT_RATE = 60;
|
|
10
|
+
export declare const DATA_TRIGGER_MAX_RATE = 600;
|
|
11
|
+
export declare const DATA_TRIGGER_MAX_TEXT = 2000;
|
|
12
|
+
export declare const DATA_TRIGGER_MAX_EMAILS = 10;
|
|
13
|
+
export declare const DATA_OUTBOX_BATCH = 100;
|
|
14
|
+
export declare const DATA_TRIGGER_MAX_ROW_BYTES = 48000;
|
|
15
|
+
export declare const DATA_TRIGGER_MAX_MESSAGE_BYTES = 120000;
|
|
16
|
+
export declare const utf8Bytes: (value: unknown) => number;
|
|
17
|
+
type Scalar = string | number | boolean | null;
|
|
18
|
+
export interface DataTriggerTest {
|
|
19
|
+
changedTo?: Scalar;
|
|
20
|
+
changedFrom?: Scalar;
|
|
21
|
+
changed?: true;
|
|
22
|
+
equals?: Scalar;
|
|
23
|
+
}
|
|
24
|
+
export type DataTriggerCondition = {
|
|
25
|
+
all: DataTriggerCondition[];
|
|
26
|
+
} | {
|
|
27
|
+
any: DataTriggerCondition[];
|
|
28
|
+
} | {
|
|
29
|
+
[column: string]: DataTriggerTest;
|
|
30
|
+
};
|
|
31
|
+
export interface DataTriggerTemplate {
|
|
32
|
+
name: string;
|
|
33
|
+
language: string;
|
|
34
|
+
params?: string[];
|
|
35
|
+
}
|
|
36
|
+
export type DataTriggerAction = {
|
|
37
|
+
type: 'function';
|
|
38
|
+
function: string;
|
|
39
|
+
} | {
|
|
40
|
+
type: 'webhook';
|
|
41
|
+
url: string;
|
|
42
|
+
} | {
|
|
43
|
+
type: 'chat';
|
|
44
|
+
channelId: string;
|
|
45
|
+
chatId: string;
|
|
46
|
+
text: string;
|
|
47
|
+
} | {
|
|
48
|
+
type: 'email';
|
|
49
|
+
to: string[];
|
|
50
|
+
subject: string;
|
|
51
|
+
text: string;
|
|
52
|
+
} | {
|
|
53
|
+
type: 'customer';
|
|
54
|
+
text: string;
|
|
55
|
+
template?: DataTriggerTemplate;
|
|
56
|
+
};
|
|
57
|
+
export interface DataTriggerInput {
|
|
58
|
+
name?: string;
|
|
59
|
+
description?: string | null;
|
|
60
|
+
table: string;
|
|
61
|
+
on: DataTriggerEvent;
|
|
62
|
+
when?: DataTriggerCondition | null;
|
|
63
|
+
enabled?: boolean;
|
|
64
|
+
replace?: boolean;
|
|
65
|
+
rateLimit?: number;
|
|
66
|
+
function?: string;
|
|
67
|
+
webhook?: string;
|
|
68
|
+
chat?: {
|
|
69
|
+
channelId: string;
|
|
70
|
+
chatId: string;
|
|
71
|
+
text: string;
|
|
72
|
+
};
|
|
73
|
+
email?: {
|
|
74
|
+
to: string[] | string;
|
|
75
|
+
subject?: string;
|
|
76
|
+
text: string;
|
|
77
|
+
};
|
|
78
|
+
customer?: {
|
|
79
|
+
text: string;
|
|
80
|
+
template?: DataTriggerTemplate;
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
export interface DataTrigger {
|
|
84
|
+
id: string;
|
|
85
|
+
name: string;
|
|
86
|
+
description: string | null;
|
|
87
|
+
table: string;
|
|
88
|
+
on: DataTriggerEvent;
|
|
89
|
+
when: DataTriggerCondition | null;
|
|
90
|
+
action: DataTriggerAction;
|
|
91
|
+
enabled: boolean;
|
|
92
|
+
disabledReason: string | null;
|
|
93
|
+
managed: 'cli' | null;
|
|
94
|
+
rateLimit: number;
|
|
95
|
+
createdAt: string;
|
|
96
|
+
updatedAt: string;
|
|
97
|
+
updatedBy: string | null;
|
|
98
|
+
}
|
|
99
|
+
export interface DataTriggerWithSecret extends DataTrigger {
|
|
100
|
+
secret: string | null;
|
|
101
|
+
}
|
|
102
|
+
export interface DataTriggerEventMessage {
|
|
103
|
+
eventId: string;
|
|
104
|
+
artifactId: string;
|
|
105
|
+
triggerId: string;
|
|
106
|
+
triggerName: string;
|
|
107
|
+
table: string;
|
|
108
|
+
event: DataTriggerEvent;
|
|
109
|
+
rowId: number;
|
|
110
|
+
row: Record<string, unknown> | null;
|
|
111
|
+
previous: Record<string, unknown> | null;
|
|
112
|
+
actor: string | null;
|
|
113
|
+
depth: number;
|
|
114
|
+
at: string;
|
|
115
|
+
truncated?: true;
|
|
116
|
+
error?: string;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Whether a change matches a condition. A missing side counts as all null:
|
|
120
|
+
* an insert "changed to" the values it was created with, and a delete
|
|
121
|
+
* "changed from" the values it had. `equals` tests the row after the change,
|
|
122
|
+
* or the deleted row.
|
|
123
|
+
*/
|
|
124
|
+
export declare const conditionMatches: (when: DataTriggerCondition | null | undefined, row: Record<string, unknown> | null, previous: Record<string, unknown> | null) => boolean;
|
|
125
|
+
/**
|
|
126
|
+
* A message built from a row: each {{field}} becomes that field's value as
|
|
127
|
+
* plain text. Never markup: the row is customer-written content, and the
|
|
128
|
+
* senders send it as text.
|
|
129
|
+
*/
|
|
130
|
+
export declare const renderTriggerText: (text: string, row: Record<string, unknown> | null) => string;
|
|
131
|
+
interface NormalisedTrigger {
|
|
132
|
+
name: string;
|
|
133
|
+
defaultName: boolean;
|
|
134
|
+
description: string | null;
|
|
135
|
+
table: DataTable;
|
|
136
|
+
on: DataTriggerEvent;
|
|
137
|
+
when: DataTriggerCondition | null;
|
|
138
|
+
action: DataTriggerAction;
|
|
139
|
+
enabled: boolean | null;
|
|
140
|
+
replace: boolean;
|
|
141
|
+
rateLimit: number;
|
|
142
|
+
}
|
|
143
|
+
export declare const normaliseTrigger: (schema: DataSchema, input: unknown) => NormalisedTrigger;
|
|
144
|
+
export declare const listTriggers: (runner: SqlRunner, schema: DataSchema) => DataTrigger[];
|
|
145
|
+
export declare const getTrigger: (runner: SqlRunner, schema: DataSchema, nameOrId: unknown) => DataTriggerWithSecret;
|
|
146
|
+
export interface SavedTrigger {
|
|
147
|
+
trigger: DataTrigger;
|
|
148
|
+
secret?: string;
|
|
149
|
+
}
|
|
150
|
+
export declare const saveTrigger: (runner: SqlRunner, schema: DataSchema, caller: DataCaller, input: unknown) => SavedTrigger;
|
|
151
|
+
export declare const dropTrigger: (runner: SqlRunner, nameOrId: unknown) => {
|
|
152
|
+
dropped: string;
|
|
153
|
+
};
|
|
154
|
+
export declare const setTriggerEnabled: (runner: SqlRunner, schema: DataSchema, caller: DataCaller, nameOrId: unknown, enabled: boolean, reason?: string | null) => DataTrigger;
|
|
155
|
+
/**
|
|
156
|
+
* The triggers ganju.json deploys: each one saved, and every other trigger
|
|
157
|
+
* the CLI deployed before removed. Those made in the dashboard stay. One
|
|
158
|
+
* transaction, so a bad entry changes nothing.
|
|
159
|
+
*/
|
|
160
|
+
export declare const replaceManagedTriggers: (runner: SqlRunner, schema: DataSchema, caller: DataCaller, inputs: unknown) => {
|
|
161
|
+
triggers: DataTrigger[];
|
|
162
|
+
removed: string[];
|
|
163
|
+
secrets: Record<string, string>;
|
|
164
|
+
};
|
|
165
|
+
/**
|
|
166
|
+
* The SQLite triggers that copy a table's changed rows into _ganju_changes.
|
|
167
|
+
* Each runs only while an enabled trigger of the owner's listens for that
|
|
168
|
+
* event on the table. Named like the table's other triggers, so they are
|
|
169
|
+
* dropped and remade with them when its columns change.
|
|
170
|
+
*/
|
|
171
|
+
export declare const changeCaptureTriggers: (table: Pick<DataTable, "id" | "name" | "columns">) => {
|
|
172
|
+
name: string;
|
|
173
|
+
sql: string;
|
|
174
|
+
}[];
|
|
175
|
+
export declare const ensureChangeCapture: (runner: SqlRunner, table: DataTable) => void;
|
|
176
|
+
/**
|
|
177
|
+
* Turns the changes a write captured into events, inside the write's
|
|
178
|
+
* transaction. A trigger never matches the writes of a run it started
|
|
179
|
+
* (actor `trigger:<id>`), and a chain stops at DATA_TRIGGER_MAX_DEPTH with
|
|
180
|
+
* an event that only records why. Returns how many events were stored.
|
|
181
|
+
*/
|
|
182
|
+
export declare const collectTriggerEvents: (runner: SqlRunner, schema: DataSchema, caller: DataCaller) => number;
|
|
183
|
+
export declare const readOutbox: (runner: SqlRunner, limit?: number) => {
|
|
184
|
+
seq: number;
|
|
185
|
+
message: DataTriggerEventMessage;
|
|
186
|
+
}[];
|
|
187
|
+
export declare const deleteOutbox: (runner: SqlRunner, throughSeq: number) => void;
|
|
188
|
+
export declare const outboxSize: (runner: SqlRunner) => number;
|
|
189
|
+
export declare const clearOutbox: (runner: SqlRunner, before: string) => number;
|
|
190
|
+
/**
|
|
191
|
+
* The triggers whose condition or {{field}} texts name a column. Dropping it
|
|
192
|
+
* is refused while any does, since they are only checked when saved and
|
|
193
|
+
* would quietly stop matching.
|
|
194
|
+
*/
|
|
195
|
+
export declare const triggersUsingColumn: (runner: SqlRunner, tableId: number, column: string) => string[];
|
|
196
|
+
/**
|
|
197
|
+
* A renamed column, renamed in the triggers that use it too, inside the
|
|
198
|
+
* rename's transaction. Those deployed from ganju.json then differ from the
|
|
199
|
+
* file until it is changed to match. Returns the triggers changed.
|
|
200
|
+
*/
|
|
201
|
+
export declare const renameColumnInTriggers: (runner: SqlRunner, tableId: number, from: string, to: string) => string[];
|
|
202
|
+
export {};
|
|
203
|
+
//# sourceMappingURL=dataTriggers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dataTriggers.d.ts","sourceRoot":"","sources":["../src/dataTriggers.ts"],"names":[],"mappings":"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"}
|