@ganju/utils 0.0.15 → 0.0.16

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 (57) hide show
  1. package/dist/constants.d.ts +23 -0
  2. package/dist/constants.d.ts.map +1 -1
  3. package/dist/constants.js +95 -8
  4. package/dist/data.d.ts +9 -0
  5. package/dist/data.d.ts.map +1 -0
  6. package/dist/data.js +78 -0
  7. package/dist/dataApi.d.ts +511 -0
  8. package/dist/dataApi.d.ts.map +1 -0
  9. package/dist/dataApi.js +355 -0
  10. package/dist/dataCompiler.d.ts +68 -0
  11. package/dist/dataCompiler.d.ts.map +1 -0
  12. package/dist/dataCompiler.js +726 -0
  13. package/dist/dataDdl.d.ts +29 -0
  14. package/dist/dataDdl.d.ts.map +1 -0
  15. package/dist/dataDdl.js +354 -0
  16. package/dist/dataEngine.d.ts +143 -0
  17. package/dist/dataEngine.d.ts.map +1 -0
  18. package/dist/dataEngine.js +2022 -0
  19. package/dist/dataIdentifiers.d.ts +8 -0
  20. package/dist/dataIdentifiers.d.ts.map +1 -0
  21. package/dist/dataIdentifiers.js +55 -0
  22. package/dist/dataImport.d.ts +77 -0
  23. package/dist/dataImport.d.ts.map +1 -0
  24. package/dist/dataImport.js +450 -0
  25. package/dist/dataRpc.d.ts +131 -0
  26. package/dist/dataRpc.d.ts.map +1 -0
  27. package/dist/dataRpc.js +2 -0
  28. package/dist/dataTypes.d.ts +157 -0
  29. package/dist/dataTypes.d.ts.map +1 -0
  30. package/dist/dataTypes.js +160 -0
  31. package/dist/index.d.ts +308 -6
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +2 -0
  34. package/dist/oauth.d.ts +14 -0
  35. package/dist/oauth.d.ts.map +1 -1
  36. package/dist/oauth.js +42 -2
  37. package/dist/resourceRerank.d.ts +3 -2
  38. package/dist/resourceRerank.d.ts.map +1 -1
  39. package/dist/resourceRerank.js +5 -2
  40. package/dist/schema.d.ts +285 -4
  41. package/dist/schema.d.ts.map +1 -1
  42. package/dist/schema.js +203 -0
  43. package/package.json +17 -2
  44. package/src/constants.ts +108 -8
  45. package/src/data.ts +74 -0
  46. package/src/dataApi.ts +428 -0
  47. package/src/dataCompiler.ts +948 -0
  48. package/src/dataDdl.ts +419 -0
  49. package/src/dataEngine.ts +2753 -0
  50. package/src/dataIdentifiers.ts +63 -0
  51. package/src/dataImport.ts +554 -0
  52. package/src/dataRpc.ts +121 -0
  53. package/src/dataTypes.ts +312 -0
  54. package/src/index.ts +4 -0
  55. package/src/oauth.ts +42 -1
  56. package/src/resourceRerank.ts +5 -2
  57. package/src/schema.ts +246 -0
@@ -0,0 +1,355 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.dataOpenApi = exports.DATA_API_ROUTES = exports.DATA_API_BASE = void 0;
4
+ // The Data API's routes, as one list: apps/api registers its data routes from
5
+ // it, and the OpenAPI file is generated from it, so the documented API and the
6
+ // served one can't drift apart. Entry point `@ganju/utils/dataApi`.
7
+ const zod_1 = require("zod");
8
+ const schema_1 = require("./schema");
9
+ exports.DATA_API_BASE = '/organization/:organizationId/project/:projectId/artifact/data';
10
+ const { DATA_DATABASE_CREATE, DATA_DATABASE_DELETE, DATA_TABLE_INPUT, DATA_TABLE_PATCH, DATA_COLUMN_INPUT, DATA_COLUMN_PATCH, DATA_TABLE_DROP_BODY, DATA_ROW_LIST_QUERY, DATA_ROW_GET_QUERY, DATA_ROW_CREATE_BODY, DATA_ROW_UPDATE_BODY, DATA_ROW_DELETE_BODY, DATA_DELETE_PREVIEW_BODY, DATA_WRITE_BODY, DATA_ROW_HISTORY_QUERY, DATA_RESTORE_POINT_BODY, DATA_EXPORT_BODY, DATA_EXPORT_QUERY, DATA_EXPORT_FILE_QUERY, DATA_CHANGE_LIST_QUERY } = schema_1.Schema;
11
+ exports.DATA_API_ROUTES = {
12
+ getDatabase: {
13
+ method: 'get',
14
+ path: '',
15
+ tag: 'Database',
16
+ summary: "The project's database",
17
+ description: 'Its status and size, and its tables when it is ready. `database` is null when the project has none.',
18
+ returns: '{ database }'
19
+ },
20
+ create: {
21
+ method: 'post',
22
+ path: '',
23
+ tag: 'Database',
24
+ summary: 'Create the database',
25
+ description: "Pro and Enterprise. Safe to repeat: a second call returns the same database. `name` defaults to the project's slug. While a deleted database is in its grace period this is refused unless `replaceDeleted` is true: the deleted one can then no longer be restored or exported, and is emptied when its grace period ends.",
26
+ body: DATA_DATABASE_CREATE.pick({ name: true, replaceDeleted: true }),
27
+ returns: '{ database }',
28
+ status: 201
29
+ },
30
+ remove: {
31
+ method: 'delete',
32
+ path: '',
33
+ tag: 'Database',
34
+ summary: 'Delete the database',
35
+ description: 'Needs `confirmName`, the database name typed out. It can be restored for 7 days.',
36
+ body: DATA_DATABASE_DELETE.pick({ confirmName: true }),
37
+ returns: '{ database }'
38
+ },
39
+ restore: {
40
+ method: 'post',
41
+ path: '/restore',
42
+ tag: 'Database',
43
+ summary: 'Undo a delete',
44
+ description: 'Within the 7-day grace period.',
45
+ returns: '{ database }'
46
+ },
47
+ restorePoint: {
48
+ method: 'post',
49
+ path: '/restore-point',
50
+ tag: 'Database',
51
+ summary: 'Restore to a point in time',
52
+ description: 'Replaces everything with the database as it was at `timestamp` (within 30 days), or at the `bookmark` a checkpoint, a dropped table or column, a type change or an earlier restore answered with. The answer carries `undoBookmark`, which undoes the restore. Calls in flight while it restarts fail.',
53
+ body: DATA_RESTORE_POINT_BODY,
54
+ returns: '{ bookmark, undoBookmark }'
55
+ },
56
+ checkpoint: {
57
+ method: 'post',
58
+ path: '/checkpoint',
59
+ tag: 'Database',
60
+ summary: 'Take a restore point',
61
+ description: "Answers with the `bookmark` of the database as it is right now, which `POST /restore-point` accepts. Take one before a large change, such as replacing a table's rows, to undo exactly that change: a time is not that precise.",
62
+ returns: '{ bookmark }'
63
+ },
64
+ dismissCopy: {
65
+ method: 'delete',
66
+ path: '/copy',
67
+ tag: 'Tables',
68
+ summary: 'Dismiss a failed table copy',
69
+ description: 'A column change on a table over 100,000 rows copies it in the background; table changes, and writes that would change its rows, wait until it finishes (`copy` on `GET` shows how far it got). A copy that failed left the table as it was and stays in `copy` with its `error` until dismissed, or until the next column change.',
70
+ returns: '{ copy: null }'
71
+ },
72
+ listTables: {
73
+ method: 'get',
74
+ path: '/table',
75
+ tag: 'Tables',
76
+ summary: 'Tables and their columns',
77
+ returns: '{ tables }'
78
+ },
79
+ createTable: {
80
+ method: 'post',
81
+ path: '/table',
82
+ tag: 'Tables',
83
+ summary: 'Create a table',
84
+ body: DATA_TABLE_INPUT,
85
+ returns: '{ table }',
86
+ status: 201
87
+ },
88
+ updateTable: {
89
+ method: 'patch',
90
+ path: '/table/:table',
91
+ tag: 'Tables',
92
+ summary: 'Rename a table or change its settings',
93
+ body: DATA_TABLE_PATCH,
94
+ returns: '{ table }'
95
+ },
96
+ dropTable: {
97
+ method: 'delete',
98
+ path: '/table/:table',
99
+ tag: 'Tables',
100
+ summary: 'Drop a table',
101
+ description: 'Needs `confirmName`, the table name typed out. The answer carries a `bookmark`: restoring to it brings the table back.',
102
+ body: DATA_TABLE_DROP_BODY,
103
+ returns: '{ dropped, bookmark }'
104
+ },
105
+ addColumn: {
106
+ method: 'post',
107
+ path: '/table/:table/column',
108
+ tag: 'Tables',
109
+ summary: 'Add a column',
110
+ body: DATA_COLUMN_INPUT,
111
+ returns: '{ table }',
112
+ status: 201
113
+ },
114
+ updateColumn: {
115
+ method: 'patch',
116
+ path: '/table/:table/column/:column',
117
+ tag: 'Tables',
118
+ summary: 'Rename a column or change it',
119
+ description: 'A type change copies the table and lists the first rows that would not fit. When it copies, the answer carries a `bookmark` from just before. Over 100,000 rows the copy runs in the background: the answer carries `copy`, the table keeps its old columns until it finishes, and meanwhile table changes, and writes that would change its rows, are refused.',
120
+ body: DATA_COLUMN_PATCH,
121
+ returns: '{ table, bookmark, copy }'
122
+ },
123
+ dropColumn: {
124
+ method: 'delete',
125
+ path: '/table/:table/column/:column',
126
+ tag: 'Tables',
127
+ summary: 'Drop a column',
128
+ description: 'The answer carries a `bookmark`: restoring to it brings the column back. Dropping a relation column copies the table, in the background over 100,000 rows, as for a type change.',
129
+ returns: '{ table, bookmark, copy }'
130
+ },
131
+ listRows: {
132
+ method: 'get',
133
+ path: '/table/:table/row',
134
+ tag: 'Rows',
135
+ summary: 'List, filter and search rows',
136
+ description: 'Up to 500 rows or 1 MB a page; `nextCursor` reads the next. `where` is JSON, e.g. `{"status":"open","price":{"lt":30000}}`. `sort` is a list of columns, `-` for descending. `q` searches the searchable columns.',
137
+ query: DATA_ROW_LIST_QUERY,
138
+ returns: '{ table, rows, total?, nextCursor? }'
139
+ },
140
+ createRows: {
141
+ method: 'post',
142
+ path: '/table/:table/row',
143
+ tag: 'Rows',
144
+ summary: 'Insert one row, or up to 500',
145
+ body: DATA_ROW_CREATE_BODY,
146
+ returns: '{ ids }',
147
+ status: 201
148
+ },
149
+ getRow: {
150
+ method: 'get',
151
+ path: '/table/:table/row/:id',
152
+ tag: 'Rows',
153
+ summary: 'One row',
154
+ query: DATA_ROW_GET_QUERY,
155
+ returns: '{ row }'
156
+ },
157
+ updateRow: {
158
+ method: 'patch',
159
+ path: '/table/:table/row/:id',
160
+ tag: 'Rows',
161
+ summary: 'Change a row',
162
+ description: 'Send the `version` you read: if the row changed since, the answer is 409 with the current row and who changed it.',
163
+ body: DATA_ROW_UPDATE_BODY,
164
+ returns: '{ row }'
165
+ },
166
+ deleteRow: {
167
+ method: 'delete',
168
+ path: '/table/:table/row/:id',
169
+ tag: 'Rows',
170
+ summary: 'Delete a row',
171
+ description: 'Send the `version` you read, as for a change.',
172
+ body: DATA_ROW_DELETE_BODY,
173
+ returns: '{ deleted }'
174
+ },
175
+ deletePreview: {
176
+ method: 'post',
177
+ path: '/table/:table/delete-preview',
178
+ tag: 'Rows',
179
+ summary: 'What deleting rows would do',
180
+ description: 'For `ids`, or every row without them: each relation column pointing at a row the delete removes, with how many rows it would delete (cascade), clear (set null) or that refuse the delete (restrict). Cascades are followed all the way down. `blocked` says a restrict would refuse it. Past 200,000 affected rows `truncated` is true and the counts are lower bounds. Nothing is deleted.',
181
+ body: DATA_DELETE_PREVIEW_BODY,
182
+ returns: '{ table, rows, effects, blocked, truncated }'
183
+ },
184
+ rowHistory: {
185
+ method: 'get',
186
+ path: '/table/:table/row/:id/history',
187
+ tag: 'History and export',
188
+ summary: "A row's history",
189
+ description: 'Newest first, kept 90 days, with who made each change.',
190
+ query: DATA_ROW_HISTORY_QUERY,
191
+ returns: '{ entries, nextCursor? }'
192
+ },
193
+ write: {
194
+ method: 'post',
195
+ path: '/write',
196
+ tag: 'Rows',
197
+ summary: 'Several writes, all or nothing',
198
+ description: 'Inserts, updates and deletes across tables in one transaction. A guard (`where` plus `expect`) that fails rolls everything back. A later step uses an earlier single-row insert\'s id as `{ "$ref": <step> }`, in its values or as the `id` it updates or deletes.',
199
+ body: DATA_WRITE_BODY,
200
+ returns: '{ results }'
201
+ },
202
+ startExport: {
203
+ method: 'post',
204
+ path: '/export',
205
+ tag: 'History and export',
206
+ summary: 'Start an export',
207
+ description: 'Writes a copy in the background: schema.json and one JSONL or CSV file per table. Poll `GET /export` until `status` is `done`. Works during the 7-day grace period after a delete too.',
208
+ body: DATA_EXPORT_BODY,
209
+ returns: '{ exportId, status }',
210
+ status: 201
211
+ },
212
+ exportStatus: {
213
+ method: 'get',
214
+ path: '/export',
215
+ tag: 'History and export',
216
+ summary: 'The latest export',
217
+ query: DATA_EXPORT_QUERY,
218
+ returns: '{ export }'
219
+ },
220
+ exportFile: {
221
+ method: 'get',
222
+ path: '/export/file',
223
+ tag: 'History and export',
224
+ summary: "Download one of an export's files",
225
+ description: "A table's file, or schema.json without `table`.",
226
+ query: DATA_EXPORT_FILE_QUERY,
227
+ returns: 'The file',
228
+ file: true
229
+ },
230
+ listChanges: {
231
+ method: 'get',
232
+ path: '/change',
233
+ tag: 'History and export',
234
+ summary: 'The schema change log',
235
+ description: 'Newest first: tables and columns changed, restores, exports, deletes.',
236
+ query: DATA_CHANGE_LIST_QUERY,
237
+ returns: '{ changes, nextCursor? }'
238
+ }
239
+ };
240
+ // `/table/:table` → `/table/{table}`
241
+ const openApiPath = (path) => path.replace(/:([A-Za-z]+)/g, '{$1}');
242
+ const jsonSchema = (schema) => zod_1.z.toJSONSchema(schema, {
243
+ io: 'input',
244
+ unrepresentable: 'any',
245
+ target: 'draft-2020-12'
246
+ });
247
+ const errorResponse = (description) => ({
248
+ description,
249
+ content: {
250
+ 'application/json': { schema: { $ref: '#/components/schemas/Error' } }
251
+ }
252
+ });
253
+ /**
254
+ * The OpenAPI 3.1 document for the Data API, served by apps/api at
255
+ * /openapi/data.json.
256
+ */
257
+ const dataOpenApi = (serverUrl) => {
258
+ const paths = {};
259
+ for (const [id, route] of Object.entries(exports.DATA_API_ROUTES)) {
260
+ const fullPath = openApiPath(`${exports.DATA_API_BASE}${route.path}`);
261
+ const pathParams = [...fullPath.matchAll(/\{([A-Za-z]+)\}/g)].map(m => ({
262
+ name: m[1],
263
+ in: 'path',
264
+ required: true,
265
+ schema: m[1] === 'id' ? { type: 'integer', minimum: 1 } : { type: 'string' }
266
+ }));
267
+ const queryParams = route.query
268
+ ? Object.entries(route.query.shape).map(([name, field]) => ({
269
+ name,
270
+ in: 'query',
271
+ required: !field.safeParse(undefined).success,
272
+ schema: jsonSchema(field)
273
+ }))
274
+ : [];
275
+ const status = String(route.status ?? 200);
276
+ paths[fullPath] ??= {};
277
+ paths[fullPath][route.method] = {
278
+ operationId: id,
279
+ tags: [route.tag],
280
+ summary: route.summary,
281
+ ...(route.description ? { description: route.description } : {}),
282
+ parameters: [...pathParams, ...queryParams],
283
+ ...(route.body
284
+ ? {
285
+ requestBody: {
286
+ required: true,
287
+ content: {
288
+ 'application/json': { schema: jsonSchema(route.body) }
289
+ }
290
+ }
291
+ }
292
+ : {}),
293
+ responses: {
294
+ [status]: route.file
295
+ ? {
296
+ description: route.returns,
297
+ content: { 'application/octet-stream': {} }
298
+ }
299
+ : {
300
+ description: route.returns,
301
+ content: { 'application/json': { schema: { type: 'object' } } }
302
+ },
303
+ '400': errorResponse('The request is not valid; `errors` lists each field.'),
304
+ '401': errorResponse('No session or token.'),
305
+ '402': errorResponse('The plan does not include Data.'),
306
+ '403': errorResponse('Not an admin of the project, or the token is for another project.'),
307
+ '404': errorResponse('No database, table or row by that name.'),
308
+ '409': errorResponse('A conflict: a stale `version` (with `details.current`), a guard that failed, a constraint, or a full database.'),
309
+ '429': errorResponse('Too many requests for this token; wait a minute.')
310
+ }
311
+ };
312
+ }
313
+ return {
314
+ openapi: '3.1.0',
315
+ info: {
316
+ title: 'Ganju Data API',
317
+ version: '1',
318
+ description: "Tables, rows and search in a project's database. Authenticate with a personal access token (`ganju token create`) as a bearer token; a token reaches only its own project."
319
+ },
320
+ servers: [{ url: serverUrl }],
321
+ security: [{ bearer: [] }],
322
+ tags: [
323
+ { name: 'Database' },
324
+ { name: 'Tables' },
325
+ { name: 'Rows' },
326
+ { name: 'History and export' }
327
+ ],
328
+ paths,
329
+ components: {
330
+ securitySchemes: { bearer: { type: 'http', scheme: 'bearer' } },
331
+ schemas: {
332
+ Error: {
333
+ type: 'object',
334
+ properties: {
335
+ error: { type: 'string' },
336
+ code: { type: 'string' },
337
+ details: { type: 'object' },
338
+ // A request that didn't parse: one entry per field.
339
+ errors: {
340
+ type: 'array',
341
+ items: {
342
+ type: 'object',
343
+ properties: {
344
+ path: { type: 'string' },
345
+ message: { type: 'string' }
346
+ }
347
+ }
348
+ }
349
+ }
350
+ }
351
+ }
352
+ }
353
+ };
354
+ };
355
+ exports.dataOpenApi = dataOpenApi;
@@ -0,0 +1,68 @@
1
+ import { DataError } from './dataTypes';
2
+ import type { DataAccess, DataCaller, DataColumn, DataColumnType, DataSchema, DataTable } from './dataTypes';
3
+ export type SqlValue = string | number | null;
4
+ export declare const FILTER_OPERATORS: readonly ["eq", "ne", "gt", "gte", "lt", "lte", "in", "notIn", "contains", "startsWith", "isNull"];
5
+ export type ResolvableColumn = DataColumn | {
6
+ name: string;
7
+ type: DataColumnType;
8
+ system: true;
9
+ unique?: boolean;
10
+ filterable?: boolean;
11
+ };
12
+ export declare const isSystemColumn: (column: ResolvableColumn) => column is Extract<ResolvableColumn, {
13
+ system: true;
14
+ }>;
15
+ export declare const columnFor: (table: DataTable, name: string) => ResolvableColumn | undefined;
16
+ export declare const unknownColumn: (table: DataTable, name: string) => DataError;
17
+ export declare const accessOf: (caller: DataCaller, table: DataTable) => DataAccess;
18
+ export declare const visibleTables: (schema: DataSchema, caller: DataCaller) => DataTable[];
19
+ export declare const tableFor: (schema: DataSchema, caller: DataCaller, name: unknown, need?: DataAccess) => DataTable;
20
+ export declare const isScoped: (caller: DataCaller, table: DataTable) => boolean;
21
+ export declare const canRead: (caller: DataCaller, table: DataTable) => boolean;
22
+ export declare const participantOf: (caller: DataCaller) => string;
23
+ export interface SqlJoin {
24
+ sql: string;
25
+ params: SqlValue[];
26
+ }
27
+ export declare const joinsSql: (joins: Map<string, SqlJoin>) => string;
28
+ export declare const joinsParams: (joins: Map<string, SqlJoin>) => SqlValue[];
29
+ export declare const relationJoin: (caller: DataCaller, target: DataTable, alias: string, on: string) => SqlJoin;
30
+ export declare const isoSeconds: (date: Date) => string;
31
+ export declare const toStored: (table: DataTable, column: ResolvableColumn, value: unknown, timeZone?: string) => SqlValue;
32
+ export declare const zoneOffsetMinutes: (date: Date, timeZone?: string) => number;
33
+ export declare const zoned: (iso: string, timeZone?: string) => string;
34
+ export declare const fromStored: (column: ResolvableColumn, value: unknown, timeZone?: string) => unknown;
35
+ export interface ResolvedPath {
36
+ sql: string;
37
+ column: ResolvableColumn;
38
+ table: DataTable;
39
+ own: boolean;
40
+ }
41
+ export interface CompileContext {
42
+ schema: DataSchema;
43
+ caller: DataCaller;
44
+ table: DataTable;
45
+ joins: Map<string, SqlJoin>;
46
+ params: SqlValue[];
47
+ }
48
+ export declare const resolvePath: (ctx: CompileContext, path: unknown) => ResolvedPath;
49
+ export declare const compileWhere: (ctx: CompileContext, where: unknown, indexed?: Set<string>) => string[];
50
+ export declare const assertParamCount: (params: readonly unknown[]) => void;
51
+ export declare const ftsMatch: (text: unknown, any: boolean) => string;
52
+ export interface SortKey {
53
+ sql: string;
54
+ descending: boolean;
55
+ }
56
+ export interface KeysetCursor {
57
+ k: string;
58
+ v: SqlValue[];
59
+ m?: 'any';
60
+ }
61
+ export declare const encodeCursor: (cursor: KeysetCursor) => string;
62
+ export declare const decodeCursor: (raw: unknown, signature: string) => KeysetCursor;
63
+ export declare const keysetCondition: (keys: readonly SortKey[], values: readonly SqlValue[], params: SqlValue[]) => string;
64
+ export declare const rowsPerInsert: (columnsPerRow: number) => number;
65
+ export declare const chunkRows: <T>(rows: readonly T[], columnsPerRow: number) => T[][];
66
+ export declare const tidyArgs: (args: unknown) => Record<string, unknown>;
67
+ export declare const isSensitiveWrite: (rawArgs: unknown, schema?: DataSchema) => boolean;
68
+ //# sourceMappingURL=dataCompiler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataCompiler.d.ts","sourceRoot":"","sources":["../src/dataCompiler.ts"],"names":[],"mappings":"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"}