@ganju/utils 0.0.18 → 0.0.21
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 +18 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +50 -3
- package/dist/crypto.d.ts +2 -2
- package/dist/crypto.d.ts.map +1 -1
- package/dist/crypto.js +1 -0
- package/dist/data.d.ts +6 -4
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +13 -3
- package/dist/dataApi.d.ts +54 -8
- package/dist/dataApi.d.ts.map +1 -1
- package/dist/dataApi.js +40 -7
- package/dist/dataAttachment.d.ts +8 -0
- package/dist/dataAttachment.d.ts.map +1 -0
- package/dist/dataAttachment.js +19 -0
- package/dist/dataCompiler.d.ts +3 -0
- package/dist/dataCompiler.d.ts.map +1 -1
- package/dist/dataCompiler.js +25 -1
- package/dist/dataDdl.d.ts.map +1 -1
- package/dist/dataDdl.js +5 -0
- package/dist/dataEngine.d.ts +9 -3
- package/dist/dataEngine.d.ts.map +1 -1
- package/dist/dataEngine.js +141 -5
- package/dist/dataIndexAdvice.d.ts +36 -0
- package/dist/dataIndexAdvice.d.ts.map +1 -0
- package/dist/dataIndexAdvice.js +89 -0
- package/dist/dataRpc.d.ts +12 -1
- package/dist/dataRpc.d.ts.map +1 -1
- package/dist/dataTools.js +3 -3
- package/dist/dataTriggers.d.ts +23 -0
- package/dist/dataTriggers.d.ts.map +1 -1
- package/dist/dataTriggers.js +46 -2
- package/dist/dataTypes.d.ts +46 -0
- package/dist/dataTypes.d.ts.map +1 -1
- package/dist/dataTypes.js +7 -1
- package/dist/index.d.ts +138 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +31 -0
- package/dist/localizeDataError.d.ts.map +1 -1
- package/dist/localizeDataError.js +64 -2
- package/dist/schema.d.ts +85 -2
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +94 -1
- package/dist/sdkConstants.d.ts +1 -0
- package/dist/sdkConstants.d.ts.map +1 -1
- package/dist/sdkConstants.js +2 -1
- package/dist/whatsappTemplates.d.ts +77 -0
- package/dist/whatsappTemplates.d.ts.map +1 -0
- package/dist/whatsappTemplates.js +402 -0
- package/dist/whatsappWebhook.d.ts +28 -0
- package/dist/whatsappWebhook.d.ts.map +1 -0
- package/dist/whatsappWebhook.js +30 -0
- package/package.json +1 -1
- package/src/constants.ts +57 -3
- package/src/crypto.ts +3 -2
- package/src/data.ts +14 -0
- package/src/dataApi.ts +44 -6
- package/src/dataAttachment.ts +23 -0
- package/src/dataCompiler.ts +37 -0
- package/src/dataDdl.ts +7 -0
- package/src/dataEngine.ts +195 -5
- package/src/dataIndexAdvice.ts +127 -0
- package/src/dataRpc.ts +17 -1
- package/src/dataTools.ts +3 -3
- package/src/dataTriggers.ts +71 -1
- package/src/dataTypes.ts +62 -0
- package/src/index.ts +74 -1
- package/src/localizeDataError.ts +101 -3
- package/src/schema.ts +115 -1
- package/src/sdkConstants.ts +1 -0
- package/src/whatsappTemplates.ts +465 -0
- package/src/whatsappWebhook.ts +55 -0
package/dist/dataCompiler.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isSensitiveWrite = exports.tidyArgs = exports.chunkRows = exports.rowsPerInsert = exports.keysetCondition = exports.decodeCursor = exports.encodeCursor = exports.ftsMatch = exports.assertParamCount = exports.compileWhere = exports.resolvePath = exports.fromStored = exports.zoned = exports.zoneOffsetMinutes = exports.toStored = exports.isoSeconds = exports.relationJoin = exports.joinsParams = exports.joinsSql = exports.participantOf = exports.canRead = exports.isScoped = exports.tableFor = exports.visibleTables = exports.accessOf = exports.unknownColumn = exports.columnFor = exports.isSystemColumn = exports.FILTER_OPERATORS = void 0;
|
|
3
|
+
exports.isSensitiveWrite = exports.tidyArgs = exports.chunkRows = exports.rowsPerInsert = exports.keysetCondition = exports.decodeCursor = exports.encodeCursor = exports.ftsMatch = exports.assertParamCount = exports.compileWhere = exports.resolvePath = exports.fromStored = exports.zoned = exports.zoneOffsetMinutes = exports.toStored = exports.unknownFileUris = exports.fileUriOf = exports.isoSeconds = exports.relationJoin = exports.joinsParams = exports.joinsSql = exports.participantOf = exports.canRead = exports.isScoped = exports.tableFor = exports.visibleTables = exports.accessOf = exports.unknownColumn = exports.columnFor = exports.isSystemColumn = exports.FILTER_OPERATORS = void 0;
|
|
4
4
|
// Turns the structured query and write arguments into SQL with bound values.
|
|
5
5
|
// Identifiers are checked against the database's own schema and quoted;
|
|
6
6
|
// every value a caller sends is a parameter. Pure: the engine runs the SQL.
|
|
@@ -162,6 +162,26 @@ const checkSize = (label, value) => {
|
|
|
162
162
|
}
|
|
163
163
|
return value;
|
|
164
164
|
};
|
|
165
|
+
// A knowledge item's uri, given as the text or as { uri } (how reads return
|
|
166
|
+
// it). Any scheme: knowledge holds resource://, website and drive uris.
|
|
167
|
+
const fileUriOf = (value, label) => {
|
|
168
|
+
const raw = value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
169
|
+
? value.uri
|
|
170
|
+
: value;
|
|
171
|
+
const uri = typeof raw === 'string' ? raw.trim() : '';
|
|
172
|
+
if (!/^[a-z][a-z0-9+.-]*:\/\/\S+$/i.test(uri)) {
|
|
173
|
+
throw new dataTypes_1.DataError(`${label} takes a knowledge item's uri, e.g. resource://menu-photo.`);
|
|
174
|
+
}
|
|
175
|
+
if (uri.length > dataTypes_1.DATA_FILE_URI_MAX) {
|
|
176
|
+
throw new dataTypes_1.DataError(`${label}: a uri is at most ${dataTypes_1.DATA_FILE_URI_MAX} characters.`, 'too_large');
|
|
177
|
+
}
|
|
178
|
+
return uri;
|
|
179
|
+
};
|
|
180
|
+
exports.fileUriOf = fileUriOf;
|
|
181
|
+
// A write that links uris no item of the project has: the workers check,
|
|
182
|
+
// since the database object can't see knowledge.
|
|
183
|
+
const unknownFileUris = (uris) => new dataTypes_1.DataError(`No resource in this project has the uri ${uris.join(', ')}; link an existing resource by its uri.`, 'invalid', { uris });
|
|
184
|
+
exports.unknownFileUris = unknownFileUris;
|
|
165
185
|
// Turns a value from a caller into what the column stores, or explains why
|
|
166
186
|
// it can't. `timeZone` is the project's, for datetimes sent without an
|
|
167
187
|
// offset.
|
|
@@ -230,6 +250,8 @@ const toStored = (table, column, value, timeZone) => {
|
|
|
230
250
|
throw new dataTypes_1.DataError(`${label} must be a URL starting with http(s)://.`);
|
|
231
251
|
}
|
|
232
252
|
return checkSize(label, value.trim());
|
|
253
|
+
case 'file':
|
|
254
|
+
return (0, exports.fileUriOf)(value, label);
|
|
233
255
|
case 'phone': {
|
|
234
256
|
if (typeof value !== 'string' && typeof value !== 'number') {
|
|
235
257
|
throw new dataTypes_1.DataError(`${label} must be a phone number.`);
|
|
@@ -392,6 +414,8 @@ const compileWhere = (ctx, where, indexed) => {
|
|
|
392
414
|
continue;
|
|
393
415
|
}
|
|
394
416
|
const resolved = (0, exports.resolvePath)(ctx, key);
|
|
417
|
+
if (resolved.own)
|
|
418
|
+
ctx.filtered?.add(resolved.column.name);
|
|
395
419
|
// { "customer": { "name": "Ana" } } reads as "customer.name": models
|
|
396
420
|
// write relation filters this way unprompted.
|
|
397
421
|
if (resolved.column.type === 'relation' &&
|
package/dist/dataDdl.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dataDdl.d.ts","sourceRoot":"","sources":["../src/dataDdl.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dataDdl.d.ts","sourceRoot":"","sources":["../src/dataDdl.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,SAAS,EACV,MAAM,aAAa,CAAC;AAErB,eAAO,MAAM,YAAY,0CAA0C,CAAC;AAEpE,eAAO,MAAM,eAAe,EAAE,WAAW,CAAC,cAAc,CAOtD,CAAC;AACH,eAAO,MAAM,aAAa,EAAE,WAAW,CAAC,cAAc,CAGpD,CAAC;AAEH,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,cAAc,CAOvD,CAAC;AAEH,eAAO,MAAM,YAAY,EAAE,WAAW,CAAC,cAAc,CAWnD,CAAC;AAIH,eAAO,MAAM,UAAU,GAAI,OAAO,gBAAgB,KAAG,MAQpD,CAAC;AAIF,eAAO,MAAM,SAAS,GAAI,OAAO,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,EAAE,QAAQ,UAAU,WAC3C,CAAC;AAChC,eAAO,MAAM,eAAe,GAC1B,OAAO,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,EAC5B,QAAQ,UAAU,WACc,CAAC;AACnC,eAAO,MAAM,oBAAoB,GAAI,OAAO,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,WACnC,CAAC;AAC/B,eAAO,MAAM,YAAY,GAAI,OAAO,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,WACrC,CAAC;AACvB,eAAO,MAAM,aAAa,GAAI,OAAO,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,WACvC,CAAC;AACpB,eAAO,MAAM,mBAAmB,GAAI,QAAQ,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,WAC9C,CAAC;AACrB,eAAO,MAAM,iBAAiB,GAAI,QAAQ,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,WAC5C,CAAC;AAGrB,eAAO,MAAM,aAAa,GACxB,QAAQ,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,EAChC,OAAO,gBAAgB,KACtB,gBAKF,CAAC;AA2CF,eAAO,MAAM,mBAAmB,GAAI,QAAQ,UAAU,KAAG,MAuBxD,CAAC;AAKF,eAAO,MAAM,cAAc,GACzB,MAAM,MAAM,EACZ,SAAS,SAAS,UAAU,EAAE,KAC7B,MAcW,CAAC;AAEf,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAMD,eAAO,MAAM,cAAc,GAAI,OAAO,SAAS,KAAG,cAAc,EA6B/D,CAAC;AAGF,eAAO,MAAM,eAAe,GAAI,QAAQ,UAAU,KAAG,OAI7B,CAAC;AAEzB,eAAO,MAAM,iBAAiB,GAAI,OAAO,SAAS,KAAG,UAAU,EAG5D,CAAC;AAKJ,eAAO,MAAM,YAAY,GAAI,OAAO,SAAS,KAAG,MAAM,GAAG,IAQxD,CAAC;AAkGF,eAAO,MAAM,eAAe,GAAI,OAAO,SAAS,KAAG,cAAc,EAIhE,CAAC;AAOF,eAAO,MAAM,aAAa,GACxB,UAAU,cAAc,EACxB,IAAI,UAAU,EACd,KAAK,MAAM,KACV,MAgCF,CAAC;AAIF,eAAO,MAAM,yBAAyB,GACpC,IAAI,UAAU,EACd,KAAK,MAAM,EACX,WAAW,MAAM,KAChB,MA+BF,CAAC"}
|
package/dist/dataDdl.js
CHANGED
|
@@ -6,6 +6,7 @@ exports.invalidAfterConversionSql = exports.conversionSql = exports.desiredTrigg
|
|
|
6
6
|
// ours, and every owner-supplied value is a literal escaped by sqlLiteral.
|
|
7
7
|
const dataTriggers_1 = require("./dataTriggers");
|
|
8
8
|
const dataIdentifiers_1 = require("./dataIdentifiers");
|
|
9
|
+
const dataTypes_1 = require("./dataTypes");
|
|
9
10
|
exports.DATA_NOW_SQL = "strftime('%Y-%m-%dT%H:%M:%SZ', 'now')";
|
|
10
11
|
exports.TEXT_LIKE_TYPES = new Set([
|
|
11
12
|
'text',
|
|
@@ -348,6 +349,10 @@ const invalidAfterConversionSql = (to, src, converted) => {
|
|
|
348
349
|
if (to.type === 'url') {
|
|
349
350
|
parts.push(`NOT ((${converted}) LIKE 'http://%' OR (${converted}) LIKE 'https://%')`);
|
|
350
351
|
}
|
|
352
|
+
// A file column holds a knowledge item's uri: scheme://…, no spaces.
|
|
353
|
+
if (to.type === 'file') {
|
|
354
|
+
parts.push(`NOT ((${converted}) GLOB '[A-Za-z]*://?*' AND (${converted}) NOT GLOB '*[ ' || char(9) || char(10) || char(13) || ']*' AND length(${converted}) <= ${dataTypes_1.DATA_FILE_URI_MAX})`);
|
|
355
|
+
}
|
|
351
356
|
if (to.type === 'relation' && to.table) {
|
|
352
357
|
parts.push(`((${converted}) IS NOT NULL AND NOT EXISTS (SELECT 1 FROM ${(0, dataIdentifiers_1.quoteIdentifier)(to.table)} WHERE id = (${converted})))`);
|
|
353
358
|
}
|
package/dist/dataEngine.d.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import type { SqlValue } from './dataCompiler';
|
|
2
2
|
import { toTableInput, DataError } from './dataTypes';
|
|
3
|
-
import type { DataCaller, DataColumn, DataDefaultValue, DataDeleteImpact, DataSchema, DataTable, DataTableCopy } from './dataTypes';
|
|
3
|
+
import type { DataCaller, DataColumn, DataDefaultValue, DataDeleteImpact, DataFileLinks, DataFileLinksReplaced, DataQueryShape, DataSchema, DataTable, DataTableCopy, DataUsage, DataWriteShape } from './dataTypes';
|
|
4
4
|
export type SqlRow = Record<string, SqlValue>;
|
|
5
5
|
export interface SqlRunner {
|
|
6
6
|
all(sql: string, params?: readonly SqlValue[]): SqlRow[];
|
|
7
7
|
iterate(sql: string, params?: readonly SqlValue[]): Iterable<SqlRow>;
|
|
8
8
|
transaction<T>(fn: () => T): T;
|
|
9
9
|
databaseSize(): number;
|
|
10
|
+
usageSoFar?(): DataUsage;
|
|
11
|
+
noteQuery?(shape: DataQueryShape): void;
|
|
12
|
+
noteWrite?(shape: DataWriteShape): void;
|
|
10
13
|
}
|
|
11
14
|
export declare const INTERNAL_MIGRATIONS: readonly (readonly string[])[];
|
|
12
15
|
export declare const INTERNAL_SCHEMA_VERSION: number;
|
|
@@ -45,7 +48,7 @@ export declare const describe: (runner: SqlRunner, schema: DataSchema, caller: D
|
|
|
45
48
|
rowsAbout: number;
|
|
46
49
|
onlyThisCustomersRows?: boolean | undefined;
|
|
47
50
|
description: string | null;
|
|
48
|
-
access: "none" | "
|
|
51
|
+
access: "none" | "add" | "read" | "full";
|
|
49
52
|
label?: string | undefined;
|
|
50
53
|
name: string;
|
|
51
54
|
} | {
|
|
@@ -70,7 +73,7 @@ export declare const describe: (runner: SqlRunner, schema: DataSchema, caller: D
|
|
|
70
73
|
rows: number;
|
|
71
74
|
onlyThisCustomersRows?: boolean | undefined;
|
|
72
75
|
description: string | null;
|
|
73
|
-
access: "none" | "
|
|
76
|
+
access: "none" | "add" | "read" | "full";
|
|
74
77
|
label?: string | undefined;
|
|
75
78
|
name: string;
|
|
76
79
|
})[];
|
|
@@ -83,6 +86,7 @@ export declare const query: (runner: SqlRunner, schema: DataSchema, caller: Data
|
|
|
83
86
|
nextCursor?: string | undefined;
|
|
84
87
|
includeTruncated?: string | undefined;
|
|
85
88
|
rows: ShapedRow[];
|
|
89
|
+
fileFields?: string[][] | undefined;
|
|
86
90
|
searchMatched?: "all words" | "any word" | undefined;
|
|
87
91
|
total?: number | undefined;
|
|
88
92
|
scope?: string | undefined;
|
|
@@ -108,6 +112,8 @@ export declare const write: (runner: SqlRunner, schema: DataSchema, caller: Data
|
|
|
108
112
|
results: WriteResult[];
|
|
109
113
|
};
|
|
110
114
|
export declare const deleteImpact: (runner: SqlRunner, schema: DataSchema, caller: DataCaller, rawArgs: unknown) => DataDeleteImpact;
|
|
115
|
+
export declare const fileLinks: (runner: SqlRunner, schema: DataSchema, caller: DataCaller, rawArgs: unknown) => DataFileLinks;
|
|
116
|
+
export declare const replaceFileLinks: (runner: SqlRunner, schema: DataSchema, caller: DataCaller, rawArgs: unknown) => DataFileLinksReplaced;
|
|
111
117
|
export declare const rowHistory: (runner: SqlRunner, schema: DataSchema, caller: DataCaller, args: {
|
|
112
118
|
table: string;
|
|
113
119
|
id: number;
|
package/dist/dataEngine.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dataEngine.d.ts","sourceRoot":"","sources":["../src/dataEngine.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dataEngine.d.ts","sourceRoot":"","sources":["../src/dataEngine.ts"],"names":[],"mappings":"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"}
|
package/dist/dataEngine.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.exportRows = exports.dropColumn = exports.updateColumn = exports.copyTableStep = exports.clearFailedTableCopy = exports.writeReachesCopy = exports.readTableCopy = exports.dropColumnNeedsRebuild = exports.columnChangeNeedsRebuild = exports.addColumn = exports.dropTable = exports.updateTable = exports.createTable = exports.pruneHistory = exports.rowHistory = exports.deleteImpact = exports.write = exports.assertWritable = exports.assertActor = exports.query = exports.describe = exports.translateSqliteError = exports.normaliseColumn = exports.toTableInput = exports.loadSchema = exports.migrateInternal = exports.setMeta = exports.getMeta = exports.hasInternalTables = exports.INTERNAL_SCHEMA_VERSION = exports.INTERNAL_MIGRATIONS = void 0;
|
|
3
|
+
exports.exportRows = exports.dropColumn = exports.updateColumn = exports.copyTableStep = exports.clearFailedTableCopy = exports.writeReachesCopy = exports.readTableCopy = exports.dropColumnNeedsRebuild = exports.columnChangeNeedsRebuild = exports.addColumn = exports.dropTable = exports.updateTable = exports.createTable = exports.pruneHistory = exports.rowHistory = exports.replaceFileLinks = exports.fileLinks = exports.deleteImpact = exports.write = exports.assertWritable = exports.assertActor = exports.query = exports.describe = exports.translateSqliteError = exports.normaliseColumn = exports.toTableInput = exports.loadSchema = exports.migrateInternal = exports.setMeta = exports.getMeta = exports.hasInternalTables = exports.INTERNAL_SCHEMA_VERSION = exports.INTERNAL_MIGRATIONS = void 0;
|
|
4
4
|
// What a project's database does, written against a tiny SQL runner so the
|
|
5
5
|
// same code runs inside the Durable Object (apps/data) and on node:sqlite in
|
|
6
6
|
// the tests and the tool-design eval. The runner executes; everything about
|
|
@@ -335,8 +335,11 @@ const normaliseColumn = (input, schema, selfTable) => {
|
|
|
335
335
|
if (column.unique && !dataDdl_1.UNIQUE_TYPES.has(column.type)) {
|
|
336
336
|
throw new dataTypes_1.DataError(`${label}: a ${column.type} column can't be unique.`);
|
|
337
337
|
}
|
|
338
|
-
|
|
339
|
-
|
|
338
|
+
// On a file column, searchable means its uploads are indexed in knowledge.
|
|
339
|
+
if (column.searchable &&
|
|
340
|
+
!dataDdl_1.SEARCHABLE_TYPES.has(column.type) &&
|
|
341
|
+
column.type !== 'file') {
|
|
342
|
+
throw new dataTypes_1.DataError(`${label}: only text and file columns can be searchable (text, long_text, email, url, phone, enum, file).`);
|
|
340
343
|
}
|
|
341
344
|
// The search table has a column of its own called rank.
|
|
342
345
|
if (column.searchable && column.name === 'rank') {
|
|
@@ -548,6 +551,9 @@ const shapeRow = (schema, table, raw, caller) => {
|
|
|
548
551
|
: label
|
|
549
552
|
};
|
|
550
553
|
}
|
|
554
|
+
else if (column.type === 'file') {
|
|
555
|
+
row[column.name] = value === null ? null : { uri: String(value) };
|
|
556
|
+
}
|
|
551
557
|
else {
|
|
552
558
|
row[column.name] = value;
|
|
553
559
|
}
|
|
@@ -626,8 +632,19 @@ const query = (runner, schema, caller, rawArgs) => {
|
|
|
626
632
|
caller,
|
|
627
633
|
table,
|
|
628
634
|
joins: new Map(),
|
|
629
|
-
params: []
|
|
635
|
+
params: [],
|
|
636
|
+
filtered: new Set()
|
|
630
637
|
};
|
|
638
|
+
// What this query read, noted for index advice once it has its rows.
|
|
639
|
+
const readBefore = runner.usageSoFar?.().rowsRead ?? 0;
|
|
640
|
+
const sorted = [];
|
|
641
|
+
const noteShape = (returned) => runner.noteQuery?.({
|
|
642
|
+
table: table.name,
|
|
643
|
+
filtered: [...(ctx.filtered ?? [])],
|
|
644
|
+
sorted,
|
|
645
|
+
rowsRead: (runner.usageSoFar?.().rowsRead ?? 0) - readBefore,
|
|
646
|
+
returned
|
|
647
|
+
});
|
|
631
648
|
const indexed = new Set();
|
|
632
649
|
const where = (0, dataCompiler_1.compileWhere)(ctx, args.where, indexed);
|
|
633
650
|
if ((0, dataCompiler_1.isScoped)(caller, table)) {
|
|
@@ -639,7 +656,10 @@ const query = (runner, schema, caller, rawArgs) => {
|
|
|
639
656
|
throw new dataTypes_1.DataError(`"${table.name}" has no searchable columns; filter with "where" instead (e.g. { "name": { "contains": "..." } }).`);
|
|
640
657
|
}
|
|
641
658
|
if (args.aggregate !== undefined && args.aggregate !== null) {
|
|
642
|
-
|
|
659
|
+
const result = aggregate(runner, ctx, args, where, searching, indexed.size > 0);
|
|
660
|
+
const groups = result.groups;
|
|
661
|
+
noteShape(Array.isArray(groups) ? groups.length : 1);
|
|
662
|
+
return result;
|
|
643
663
|
}
|
|
644
664
|
const limit = args.limit === undefined || args.limit === null ? dataTypes_1.DATA_DEFAULT_PAGE : args.limit;
|
|
645
665
|
if (typeof limit !== 'number' || !Number.isInteger(limit) || limit < 1 || limit > dataTypes_1.DATA_MAX_PAGE) {
|
|
@@ -672,6 +692,8 @@ const query = (runner, schema, caller, rawArgs) => {
|
|
|
672
692
|
descending,
|
|
673
693
|
...(servedByIndex ? {} : { unindexed: resolved.column.name })
|
|
674
694
|
});
|
|
695
|
+
if (resolved.own && resolved.column.name !== 'id')
|
|
696
|
+
sorted.push(resolved.column.name);
|
|
675
697
|
if (resolved.own && resolved.column.name === 'id') {
|
|
676
698
|
endsUnique = true;
|
|
677
699
|
break;
|
|
@@ -763,14 +785,17 @@ const query = (runner, schema, caller, rawArgs) => {
|
|
|
763
785
|
throw (0, exports.translateSqliteError)(schema, table, error);
|
|
764
786
|
}
|
|
765
787
|
const { rows: out, last, more } = result;
|
|
788
|
+
noteShape(out.length);
|
|
766
789
|
const includeTruncated = args.include !== undefined && args.include !== null
|
|
767
790
|
? includeRelations(runner, schema, caller, table, out, args.include, dataTypes_1.DATA_MAX_RESULT_BYTES - result.bytes)
|
|
768
791
|
: false;
|
|
792
|
+
const fileFields = fileFieldPaths(schema, table, args.include);
|
|
769
793
|
return {
|
|
770
794
|
table: table.name,
|
|
771
795
|
...((0, dataCompiler_1.isScoped)(caller, table) ? { scope: SCOPE_NOTE } : {}),
|
|
772
796
|
...(result.total !== undefined ? { total: result.total } : {}),
|
|
773
797
|
...(matched ? { searchMatched: matched } : {}),
|
|
798
|
+
...(fileFields.length ? { fileFields } : {}),
|
|
774
799
|
rows: out,
|
|
775
800
|
...(includeTruncated
|
|
776
801
|
? {
|
|
@@ -789,6 +814,25 @@ const query = (runner, schema, caller, rawArgs) => {
|
|
|
789
814
|
};
|
|
790
815
|
};
|
|
791
816
|
exports.query = query;
|
|
817
|
+
// Where a query's rows hold file values, as key paths within each row: the
|
|
818
|
+
// table's file columns, and those of each included relation. The workers
|
|
819
|
+
// fill those in from knowledge and drop this before answering; a json
|
|
820
|
+
// column holding { "uri": … } is never mistaken for one.
|
|
821
|
+
const fileFieldPaths = (schema, table, include) => {
|
|
822
|
+
const own = table.columns
|
|
823
|
+
.filter(column => column.type === 'file')
|
|
824
|
+
.map(column => [column.name]);
|
|
825
|
+
const included = (Array.isArray(include) ? include : []).flatMap(name => {
|
|
826
|
+
const column = typeof name === 'string' ? (0, dataCompiler_1.columnFor)(table, name) : undefined;
|
|
827
|
+
if (!column || (0, dataCompiler_1.isSystemColumn)(column) || column.type !== 'relation')
|
|
828
|
+
return [];
|
|
829
|
+
const target = schema.tables.find(t => t.name === column.table);
|
|
830
|
+
return (target?.columns ?? [])
|
|
831
|
+
.filter(c => c.type === 'file')
|
|
832
|
+
.map(c => [name, c.name]);
|
|
833
|
+
});
|
|
834
|
+
return [...own, ...included];
|
|
835
|
+
};
|
|
792
836
|
// Replaces each relation in `rows` named in `include` with the related row,
|
|
793
837
|
// read as the caller may see it, within `budget` bytes. Returns whether any
|
|
794
838
|
// was left as { id, label } to stay inside it.
|
|
@@ -1353,7 +1397,12 @@ const write = (runner, schema, caller, rawArgs) => {
|
|
|
1353
1397
|
const results = [];
|
|
1354
1398
|
operations.forEach((op, index) => {
|
|
1355
1399
|
try {
|
|
1400
|
+
const before = runner.usageSoFar?.().rowsWritten ?? 0;
|
|
1356
1401
|
results.push(runOperation(runner, schema, caller, op, index, results));
|
|
1402
|
+
runner.noteWrite?.({
|
|
1403
|
+
table: results[results.length - 1].table,
|
|
1404
|
+
rowsWritten: (runner.usageSoFar?.().rowsWritten ?? 0) - before
|
|
1405
|
+
});
|
|
1357
1406
|
}
|
|
1358
1407
|
catch (error) {
|
|
1359
1408
|
if (error instanceof dataTypes_1.DataError) {
|
|
@@ -1461,6 +1510,93 @@ const deleteImpact = (runner, schema, caller, rawArgs) => {
|
|
|
1461
1510
|
};
|
|
1462
1511
|
};
|
|
1463
1512
|
exports.deleteImpact = deleteImpact;
|
|
1513
|
+
// File links
|
|
1514
|
+
const fileColumns = (schema) => schema.tables.flatMap(table => table.columns
|
|
1515
|
+
.filter(column => column.type === 'file')
|
|
1516
|
+
.map(column => ({ table, column })));
|
|
1517
|
+
const fileUriList = (value) => {
|
|
1518
|
+
if (!Array.isArray(value) ||
|
|
1519
|
+
value.length === 0 ||
|
|
1520
|
+
value.length > dataTypes_1.DATA_FILE_LINKS_MAX_URIS) {
|
|
1521
|
+
throw new dataTypes_1.DataError(`"uris" lists 1 to ${dataTypes_1.DATA_FILE_LINKS_MAX_URIS} knowledge item uris.`);
|
|
1522
|
+
}
|
|
1523
|
+
return [...new Set(value.map(uri => (0, dataCompiler_1.fileUriOf)(uri, 'uris')))];
|
|
1524
|
+
};
|
|
1525
|
+
// The rows whose file columns point at `uris`, per column: knowledge asks
|
|
1526
|
+
// before it deletes an item. Each file column is indexed.
|
|
1527
|
+
const fileLinks = (runner, schema, caller, rawArgs) => {
|
|
1528
|
+
(0, exports.assertActor)(caller);
|
|
1529
|
+
if (caller.restricted) {
|
|
1530
|
+
throw new dataTypes_1.DataError('File links are for the owner.', 'forbidden');
|
|
1531
|
+
}
|
|
1532
|
+
const uris = JSON.stringify(fileUriList((0, dataCompiler_1.tidyArgs)(rawArgs).uris));
|
|
1533
|
+
const links = [];
|
|
1534
|
+
for (const { table, column } of fileColumns(schema)) {
|
|
1535
|
+
const rows = Number(first(runner, `SELECT count(*) AS n FROM ${(0, dataIdentifiers_1.quoteIdentifier)(table.name)} WHERE ${(0, dataIdentifiers_1.quoteIdentifier)(column.name)} IN (SELECT value FROM json_each(?))`, [uris])?.n ?? 0);
|
|
1536
|
+
if (rows === 0)
|
|
1537
|
+
continue;
|
|
1538
|
+
links.push({
|
|
1539
|
+
table: table.name,
|
|
1540
|
+
tableLabel: table.label,
|
|
1541
|
+
column: column.name,
|
|
1542
|
+
columnLabel: column.label,
|
|
1543
|
+
required: column.required,
|
|
1544
|
+
rows
|
|
1545
|
+
});
|
|
1546
|
+
}
|
|
1547
|
+
return { links };
|
|
1548
|
+
};
|
|
1549
|
+
exports.fileLinks = fileLinks;
|
|
1550
|
+
// Points the links at `from` to `to`, or clears them (`to: null`) once the
|
|
1551
|
+
// item is gone. A required column keeps its link, which then reads as
|
|
1552
|
+
// missing. Recorded in row history; triggers don't see it.
|
|
1553
|
+
const replaceFileLinks = (runner, schema, caller, rawArgs) => {
|
|
1554
|
+
(0, exports.assertActor)(caller);
|
|
1555
|
+
if (caller.restricted) {
|
|
1556
|
+
throw new dataTypes_1.DataError('File links are for the owner.', 'forbidden');
|
|
1557
|
+
}
|
|
1558
|
+
const raw = (0, dataCompiler_1.tidyArgs)(rawArgs).changes;
|
|
1559
|
+
if (!Array.isArray(raw) ||
|
|
1560
|
+
raw.length === 0 ||
|
|
1561
|
+
raw.length > dataTypes_1.DATA_FILE_LINKS_MAX_URIS) {
|
|
1562
|
+
throw new dataTypes_1.DataError(`"changes" lists 1 to ${dataTypes_1.DATA_FILE_LINKS_MAX_URIS} { from, to } pairs.`);
|
|
1563
|
+
}
|
|
1564
|
+
const changes = raw.map(change => {
|
|
1565
|
+
if (!isPlainObject(change)) {
|
|
1566
|
+
throw new dataTypes_1.DataError('Each change is { from, to }; to is null to clear.');
|
|
1567
|
+
}
|
|
1568
|
+
return {
|
|
1569
|
+
from: (0, dataCompiler_1.fileUriOf)(change.from, 'from'),
|
|
1570
|
+
to: change.to === null ? null : (0, dataCompiler_1.fileUriOf)(change.to, 'to')
|
|
1571
|
+
};
|
|
1572
|
+
});
|
|
1573
|
+
const columns = fileColumns(schema);
|
|
1574
|
+
if (columns.length === 0)
|
|
1575
|
+
return { updated: 0, kept: [] };
|
|
1576
|
+
(0, exports.assertWritable)(runner, caller, changes.some(change => change.to !== null));
|
|
1577
|
+
return runner.transaction(() => {
|
|
1578
|
+
setContextActor(runner, caller.actor);
|
|
1579
|
+
runner.all('DELETE FROM _ganju_changes');
|
|
1580
|
+
let updated = 0;
|
|
1581
|
+
const kept = [];
|
|
1582
|
+
for (const { table, column } of columns) {
|
|
1583
|
+
for (const change of changes) {
|
|
1584
|
+
if (change.to === null && column.required) {
|
|
1585
|
+
const rows = Number(first(runner, `SELECT count(*) AS n FROM ${(0, dataIdentifiers_1.quoteIdentifier)(table.name)} WHERE ${(0, dataIdentifiers_1.quoteIdentifier)(column.name)} = ?`, [change.from])?.n ?? 0);
|
|
1586
|
+
if (rows > 0)
|
|
1587
|
+
kept.push({ table: table.name, column: column.name, rows });
|
|
1588
|
+
continue;
|
|
1589
|
+
}
|
|
1590
|
+
updated += runner.all(`UPDATE ${(0, dataIdentifiers_1.quoteIdentifier)(table.name)} SET ${(0, dataIdentifiers_1.quoteIdentifier)(column.name)} = ?, version = version + 1, updated_at = ${dataDdl_1.DATA_NOW_SQL}, updated_by = ? WHERE ${(0, dataIdentifiers_1.quoteIdentifier)(column.name)} = ? RETURNING id`, [change.to, caller.actor, change.from]).length;
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
// Tidying links after a knowledge change isn't the owner's edit: it
|
|
1594
|
+
// stays in row history but fires no trigger.
|
|
1595
|
+
runner.all('DELETE FROM _ganju_changes');
|
|
1596
|
+
return { updated, kept };
|
|
1597
|
+
});
|
|
1598
|
+
};
|
|
1599
|
+
exports.replaceFileLinks = replaceFileLinks;
|
|
1464
1600
|
// Row history
|
|
1465
1601
|
const rowHistory = (runner, schema, caller, args) => {
|
|
1466
1602
|
const table = (0, dataCompiler_1.tableFor)(schema, caller, args.table);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { DataSchema } from './dataTypes';
|
|
2
|
+
export interface IndexAdviceStat {
|
|
3
|
+
tableName: string;
|
|
4
|
+
columnName: string;
|
|
5
|
+
queries: number;
|
|
6
|
+
rowsRead: number;
|
|
7
|
+
rowsReturned: number;
|
|
8
|
+
rowsWritten: number;
|
|
9
|
+
}
|
|
10
|
+
export interface IndexAdviceCandidate {
|
|
11
|
+
tableName: string;
|
|
12
|
+
columnName: string;
|
|
13
|
+
kind: 'add' | 'unused';
|
|
14
|
+
queries: number;
|
|
15
|
+
avgRowsRead: number;
|
|
16
|
+
avgRowsReturned: number;
|
|
17
|
+
rowsWritten: number;
|
|
18
|
+
}
|
|
19
|
+
export interface IndexAdviceInput {
|
|
20
|
+
schema: DataSchema;
|
|
21
|
+
week: IndexAdviceStat[];
|
|
22
|
+
month: IndexAdviceStat[];
|
|
23
|
+
monthCovered: boolean;
|
|
24
|
+
recentlyIndexed: Set<string>;
|
|
25
|
+
}
|
|
26
|
+
export declare const adviseIndexes: (input: IndexAdviceInput) => IndexAdviceCandidate[];
|
|
27
|
+
export declare const adviceDoubled: (current: {
|
|
28
|
+
queries: number;
|
|
29
|
+
avgRowsRead: number;
|
|
30
|
+
rowsWritten: number;
|
|
31
|
+
}, dismissed: {
|
|
32
|
+
queries: number | null;
|
|
33
|
+
avgRowsRead: number | null;
|
|
34
|
+
rowsWritten: number | null;
|
|
35
|
+
}) => boolean;
|
|
36
|
+
//# sourceMappingURL=dataIndexAdvice.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dataIndexAdvice.d.ts","sourceRoot":"","sources":["../src/dataIndexAdvice.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,KAAK,GAAG,QAAQ,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,UAAU,CAAC;IAEnB,IAAI,EAAE,eAAe,EAAE,CAAC;IACxB,KAAK,EAAE,eAAe,EAAE,CAAC;IAEzB,YAAY,EAAE,OAAO,CAAC;IAEtB,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC9B;AAMD,eAAO,MAAM,aAAa,GACxB,OAAO,gBAAgB,KACtB,oBAAoB,EA+DtB,CAAC;AAGF,eAAO,MAAM,aAAa,GACxB,SAAS;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,EACtE,WAAW;IACT,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,KACA,OASkD,CAAC"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.adviceDoubled = exports.adviseIndexes = void 0;
|
|
4
|
+
// Which columns of a database are worth an index, and which indexes nobody
|
|
5
|
+
// uses, from the API's daily query stats and the database's schema. Pure, so
|
|
6
|
+
// the daily job and the tests apply the same rule.
|
|
7
|
+
const constants_1 = require("./constants");
|
|
8
|
+
const dataDdl_1 = require("./dataDdl");
|
|
9
|
+
// Indexes the owner can add or remove: a column marked filterable. Unique and
|
|
10
|
+
// relation columns are indexed for what they are, and stay.
|
|
11
|
+
const canIndex = (type) => type !== 'json' && type !== 'long_text';
|
|
12
|
+
const adviseIndexes = (input) => {
|
|
13
|
+
const out = [];
|
|
14
|
+
const tables = new Map(input.schema.tables.map(t => [t.name, t]));
|
|
15
|
+
for (const stat of input.week) {
|
|
16
|
+
if (!stat.columnName)
|
|
17
|
+
continue;
|
|
18
|
+
const table = tables.get(stat.tableName);
|
|
19
|
+
const column = table?.columns.find(c => c.name === stat.columnName);
|
|
20
|
+
if (!column || (0, dataDdl_1.isIndexedColumn)(column) || !canIndex(column.type))
|
|
21
|
+
continue;
|
|
22
|
+
if (stat.queries < constants_1.constants.DATA_INDEX_ADVICE_MIN_QUERIES)
|
|
23
|
+
continue;
|
|
24
|
+
const avgRead = stat.rowsRead / stat.queries;
|
|
25
|
+
const avgReturned = stat.rowsReturned / stat.queries;
|
|
26
|
+
if (avgRead < constants_1.constants.DATA_INDEX_ADVICE_MIN_ROWS_READ)
|
|
27
|
+
continue;
|
|
28
|
+
// Scanning, not just a small table: many rows read for each one returned.
|
|
29
|
+
if (stat.rowsRead <
|
|
30
|
+
constants_1.constants.DATA_INDEX_ADVICE_MIN_READ_RATIO * stat.rowsReturned) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
out.push({
|
|
34
|
+
tableName: stat.tableName,
|
|
35
|
+
columnName: stat.columnName,
|
|
36
|
+
kind: constants_1.constants.DATA_INDEX_ADVICE_ADD,
|
|
37
|
+
queries: stat.queries,
|
|
38
|
+
avgRowsRead: Math.round(avgRead),
|
|
39
|
+
avgRowsReturned: Math.round(avgReturned),
|
|
40
|
+
rowsWritten: 0
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
if (input.monthCovered) {
|
|
44
|
+
const used = new Set(input.month
|
|
45
|
+
.filter(s => s.columnName && s.queries > 0)
|
|
46
|
+
.map(s => `${s.tableName}.${s.columnName}`));
|
|
47
|
+
for (const table of input.schema.tables) {
|
|
48
|
+
const totals = input.month.find(s => s.tableName === table.name && s.columnName === '');
|
|
49
|
+
const written = totals?.rowsWritten ?? 0;
|
|
50
|
+
if (written < constants_1.constants.DATA_INDEX_UNUSED_MIN_ROWS_WRITTEN)
|
|
51
|
+
continue;
|
|
52
|
+
if (written <= (totals?.queries ?? 0))
|
|
53
|
+
continue;
|
|
54
|
+
if (input.recentlyIndexed.has(table.name))
|
|
55
|
+
continue;
|
|
56
|
+
for (const column of table.columns) {
|
|
57
|
+
if (!column.filterable || column.unique)
|
|
58
|
+
continue;
|
|
59
|
+
if (column.type === 'relation' || column.type === 'file')
|
|
60
|
+
continue;
|
|
61
|
+
const key = `${table.name}.${column.name}`;
|
|
62
|
+
if (used.has(key) || input.recentlyIndexed.has(key))
|
|
63
|
+
continue;
|
|
64
|
+
out.push({
|
|
65
|
+
tableName: table.name,
|
|
66
|
+
columnName: column.name,
|
|
67
|
+
kind: constants_1.constants.DATA_INDEX_ADVICE_UNUSED,
|
|
68
|
+
queries: 0,
|
|
69
|
+
avgRowsRead: 0,
|
|
70
|
+
avgRowsReturned: 0,
|
|
71
|
+
rowsWritten: written
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
};
|
|
78
|
+
exports.adviseIndexes = adviseIndexes;
|
|
79
|
+
// A dismissed suggestion comes back once its numbers have doubled.
|
|
80
|
+
const adviceDoubled = (current, dismissed) => (dismissed.queries !== null &&
|
|
81
|
+
dismissed.queries > 0 &&
|
|
82
|
+
current.queries >= 2 * dismissed.queries) ||
|
|
83
|
+
(dismissed.avgRowsRead !== null &&
|
|
84
|
+
dismissed.avgRowsRead > 0 &&
|
|
85
|
+
current.avgRowsRead >= 2 * dismissed.avgRowsRead) ||
|
|
86
|
+
(dismissed.rowsWritten !== null &&
|
|
87
|
+
dismissed.rowsWritten > 0 &&
|
|
88
|
+
current.rowsWritten >= 2 * dismissed.rowsWritten);
|
|
89
|
+
exports.adviceDoubled = adviceDoubled;
|
package/dist/dataRpc.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DataCaller, DataDeleteImpact, DataResult, DataSchema, DataTable, DataTableCopy } from './dataTypes';
|
|
1
|
+
import type { DataCaller, DataDeleteImpact, DataFileLinkChange, DataFileLinks, DataFileLinksReplaced, DataResult, DataSchema, DataTable, DataTableCopy } from './dataTypes';
|
|
2
2
|
import type { DataQuery } from './dataQueries';
|
|
3
3
|
import type { DataTrigger, DataTriggerWithSecret, SavedTrigger } from './dataTriggers';
|
|
4
4
|
export type ExportFormat = 'jsonl' | 'csv';
|
|
@@ -45,6 +45,7 @@ export interface DatabaseInfo {
|
|
|
45
45
|
name: string;
|
|
46
46
|
label: string | null;
|
|
47
47
|
columns: number;
|
|
48
|
+
rows: number;
|
|
48
49
|
}[];
|
|
49
50
|
copy: DataTableCopy | null;
|
|
50
51
|
}
|
|
@@ -109,6 +110,10 @@ export interface ProjectDatabaseRpc {
|
|
|
109
110
|
}>;
|
|
110
111
|
describe(caller: DataCaller, args?: unknown): R<unknown>;
|
|
111
112
|
query(caller: DataCaller, args: unknown): R<unknown>;
|
|
113
|
+
eventRow(caller: DataCaller, args: {
|
|
114
|
+
table: string;
|
|
115
|
+
id?: number;
|
|
116
|
+
}): R<Record<string, unknown> | null>;
|
|
112
117
|
write(caller: DataCaller, args: unknown): R<{
|
|
113
118
|
ok: true;
|
|
114
119
|
results: unknown[];
|
|
@@ -117,6 +122,12 @@ export interface ProjectDatabaseRpc {
|
|
|
117
122
|
table: string;
|
|
118
123
|
ids?: number[];
|
|
119
124
|
}): R<DataDeleteImpact>;
|
|
125
|
+
fileLinks(caller: DataCaller, args: {
|
|
126
|
+
uris: string[];
|
|
127
|
+
}): R<DataFileLinks>;
|
|
128
|
+
replaceFileLinks(caller: DataCaller, args: {
|
|
129
|
+
changes: DataFileLinkChange[];
|
|
130
|
+
}): R<DataFileLinksReplaced>;
|
|
120
131
|
history(caller: DataCaller, args: {
|
|
121
132
|
table: string;
|
|
122
133
|
id: number;
|
package/dist/dataRpc.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dataRpc.d.ts","sourceRoot":"","sources":["../src/dataRpc.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,UAAU,EACV,gBAAgB,EAChB,UAAU,EACV,UAAU,EACV,SAAS,EACT,aAAa,EACd,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEvF,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,KAAK,CAAC;AAE3C,MAAM,WAAW,UAAU;IAEzB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE;QACN,GAAG,EAAE,MAAM,CAAC;QACZ,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE;YAAE,UAAU,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;QAC9C,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QAGd,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;KACzB,GAAG,IAAI,CAAC;IACT,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAE5B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"dataRpc.d.ts","sourceRoot":"","sources":["../src/dataRpc.ts"],"names":[],"mappings":"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"}
|
package/dist/dataTools.js
CHANGED
|
@@ -39,7 +39,7 @@ const CHANGES = {
|
|
|
39
39
|
destructiveHint: true,
|
|
40
40
|
openWorldHint: false
|
|
41
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".';
|
|
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" }. A file column matches the item\'s uri. Datetimes are ISO 8601; include the offset for local times, e.g. "2026-10-19T00:00:00-05:00".';
|
|
43
43
|
const accessOf = (caller, table) => caller.restricted ? table.access : 'full';
|
|
44
44
|
const visible = (schema, caller) => schema.tables.filter(table => dataTypes_1.DATA_ACCESS_RANK[accessOf(caller, table)] >= 1);
|
|
45
45
|
// One column as data-query's description lists it: the type when it isn't
|
|
@@ -100,7 +100,7 @@ const dataToolDefinitions = (schema, caller, options = {}) => {
|
|
|
100
100
|
title: exports.DATA_TOOL_TITLES[exports.DATA_TOOL_QUERY],
|
|
101
101
|
description: [
|
|
102
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.',
|
|
103
|
+
'Returns matching rows (relation columns come back as { id, label }; file columns as { uri, title, mimeType }, or { uri, missing: true } when the item is gone: deliver one with send-resource and its uri, or read a document with read-resource), 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
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
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
106
|
'Tables:',
|
|
@@ -185,7 +185,7 @@ const dataToolDefinitions = (schema, caller, options = {}) => {
|
|
|
185
185
|
title: exports.DATA_TOOL_TITLES[exports.DATA_TOOL_WRITE],
|
|
186
186
|
description: [
|
|
187
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).',
|
|
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). A file column takes the uri of an item in the project\'s knowledge (from list-resources or search-resources), or null.',
|
|
189
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
190
|
'- delete: { "op": "delete", "table": "...", "id": 12 } (or "where").',
|
|
191
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).',
|