@taruvi/sdk 1.5.0-beta.1 → 1.5.0-beta.2

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 (69) hide show
  1. package/README.md +58 -1295
  2. package/package.json +10 -2
  3. package/.claude/settings.local.json +0 -19
  4. package/.github/worflows/publish.yml +0 -57
  5. package/.github/workflows/publish.yml +0 -58
  6. package/.kiro/settings/lsp.json +0 -198
  7. package/MODULE_NAMING_CHANGES.md +0 -81
  8. package/PARAMETER_NAMING_CHANGES.md +0 -106
  9. package/USAGE_EXAMPLE.md +0 -86
  10. package/src/client.ts +0 -88
  11. package/src/index.ts +0 -51
  12. package/src/lib/analytics/AnalyticsClient.ts +0 -24
  13. package/src/lib/analytics/types.ts +0 -8
  14. package/src/lib/app/AppClient.ts +0 -54
  15. package/src/lib/app/types.ts +0 -50
  16. package/src/lib/auth/AuthClient.ts +0 -126
  17. package/src/lib/auth/types.ts +0 -123
  18. package/src/lib/database/DatabaseClient.ts +0 -306
  19. package/src/lib/database/types.ts +0 -156
  20. package/src/lib/functions/FunctionsClient.ts +0 -27
  21. package/src/lib/functions/types.ts +0 -27
  22. package/src/lib/policy/PolicyClient.ts +0 -79
  23. package/src/lib/policy/types.ts +0 -39
  24. package/src/lib/secrets/SecretsClient.ts +0 -75
  25. package/src/lib/secrets/types.ts +0 -59
  26. package/src/lib/settings/SettingsClient.ts +0 -22
  27. package/src/lib/settings/types.ts +0 -9
  28. package/src/lib/storage/StorageClient.ts +0 -131
  29. package/src/lib/storage/types.ts +0 -86
  30. package/src/lib/users/UserClient.ts +0 -63
  31. package/src/lib/users/types.ts +0 -123
  32. package/src/lib-internal/errors/ErrorClient.ts +0 -114
  33. package/src/lib-internal/errors/index.ts +0 -3
  34. package/src/lib-internal/errors/types.ts +0 -29
  35. package/src/lib-internal/http/HttpClient.ts +0 -116
  36. package/src/lib-internal/http/types.ts +0 -12
  37. package/src/lib-internal/routes/AnalyticsRoutes.ts +0 -3
  38. package/src/lib-internal/routes/AppRoutes.ts +0 -9
  39. package/src/lib-internal/routes/AuthRoutes.ts +0 -0
  40. package/src/lib-internal/routes/DatabaseRoutes.ts +0 -10
  41. package/src/lib-internal/routes/FunctionRoutes.ts +0 -3
  42. package/src/lib-internal/routes/PolicyRoutes.ts +0 -4
  43. package/src/lib-internal/routes/SecretsRoutes.ts +0 -5
  44. package/src/lib-internal/routes/SettingsRoutes.ts +0 -4
  45. package/src/lib-internal/routes/StorageRoutes.ts +0 -15
  46. package/src/lib-internal/routes/UserRoutes.ts +0 -12
  47. package/src/lib-internal/routes/index.ts +0 -0
  48. package/src/lib-internal/token/TokenClient.ts +0 -108
  49. package/src/lib-internal/token/types.ts +0 -0
  50. package/src/types.ts +0 -104
  51. package/src/utils/enums.ts +0 -24
  52. package/src/utils/utils.ts +0 -38
  53. package/tests/fixtures/mockClient.ts +0 -19
  54. package/tests/mocks/db.json +0 -1
  55. package/tests/unit/analytics/AnalyticsClient.test.ts +0 -84
  56. package/tests/unit/app/AppClient.test.ts +0 -114
  57. package/tests/unit/auth/AuthClient.test.ts +0 -91
  58. package/tests/unit/client/Client.test.ts +0 -87
  59. package/tests/unit/database/DatabaseClient.test.ts +0 -652
  60. package/tests/unit/edge-cases/robustness.test.ts +0 -258
  61. package/tests/unit/errors/errors.test.ts +0 -236
  62. package/tests/unit/functions/FunctionsClient.test.ts +0 -99
  63. package/tests/unit/policy/PolicyClient.test.ts +0 -180
  64. package/tests/unit/secrets/SecretsClient.test.ts +0 -146
  65. package/tests/unit/settings/SettingsClient.test.ts +0 -50
  66. package/tests/unit/storage/StorageClient.test.ts +0 -252
  67. package/tests/unit/users/UserClient.test.ts +0 -150
  68. package/tsconfig.json +0 -44
  69. package/vitest.config.ts +0 -7
@@ -1,306 +0,0 @@
1
- import type { Client } from "../../client.js";
2
- import { DatabaseRoutes } from "../../lib-internal/routes/DatabaseRoutes.js";
3
- import { HttpMethod } from "../../lib-internal/http/types.js";
4
- import type { TaruviConfig, DatabaseFilters, TaruviResponse } from "../../types.js";
5
- import type { UrlParams, FilterOperator, SortOrder, GraphInclude, GraphFormat, EdgeRequest, EdgeDeleteRequest, BackendFilterTreeRoot } from "./types.js";
6
- import { isBackendFilterTreeRoot } from "./types.js";
7
- import { buildQueryString } from "../../utils/utils.js";
8
-
9
- interface GraphQueryParams {
10
- include?: GraphInclude
11
- depth?: number
12
- format?: GraphFormat
13
- relationship_type?: string[]
14
- }
15
-
16
- // Used to access app data
17
- export class Database<T = Record<string, unknown>> {
18
- private client: Client
19
- private urlParams: UrlParams
20
- private config: TaruviConfig
21
- private operation: HttpMethod | undefined
22
- private body: object | undefined
23
- private queryParams: DatabaseFilters | undefined
24
- private graphParams: GraphQueryParams
25
- private isEdges: boolean
26
- private isUpsert: boolean
27
-
28
- constructor(client: Client, urlParams: UrlParams = {}, operation?: HttpMethod | undefined, body?: object | undefined, queryParams?: DatabaseFilters, graphParams: GraphQueryParams = {}, isEdges: boolean = false, isUpsert: boolean = false) {
29
- this.client = client
30
- this.urlParams = urlParams
31
- this.operation = operation
32
- this.body = body
33
- this.config = this.client.getConfig()
34
- this.queryParams = queryParams
35
- this.graphParams = graphParams
36
- this.isEdges = isEdges
37
- this.isUpsert = isUpsert
38
- }
39
-
40
- from<U = Record<string, unknown>>(dataTables: string): Database<U> {
41
- return new Database<U>(this.client, { ...this.urlParams, dataTables }, undefined, undefined, undefined, {}, this.isEdges)
42
- }
43
-
44
- edges(): Database<T> {
45
- return new Database<T>(this.client, { ...this.urlParams }, undefined, undefined, this.queryParams, { ...this.graphParams }, true)
46
- }
47
-
48
- // Graph traversal methods
49
- include(direction: GraphInclude): Database<T> {
50
- return new Database<T>(this.client, { ...this.urlParams }, this.operation, this.body, this.queryParams, { ...this.graphParams, include: direction }, this.isEdges)
51
- }
52
-
53
- depth(n: number): Database<T> {
54
- return new Database<T>(this.client, { ...this.urlParams }, this.operation, this.body, this.queryParams, { ...this.graphParams, depth: n }, this.isEdges)
55
- }
56
-
57
- format(fmt: GraphFormat): Database<T> {
58
- return new Database<T>(this.client, { ...this.urlParams }, this.operation, this.body, this.queryParams, { ...this.graphParams, format: fmt }, this.isEdges)
59
- }
60
-
61
- types(types: string[]): Database<T> {
62
- return new Database<T>(this.client, { ...this.urlParams }, this.operation, this.body, this.queryParams, { ...this.graphParams, relationship_type: types }, this.isEdges)
63
- }
64
-
65
- // Filter & query methods
66
- /**
67
- * JSON filter tree for the `filters` query param. Must match the platform contract:
68
- * root is an array of logical nodes `{ operator: "and" | "or", value: [...] }`; leaves are
69
- * `{ field, operator, value }` where `operator` is a **backend** token (`contains` = case-insensitive
70
- * substring, `containss` = case-sensitive, `eq`, `in`, …). The SDK only JSON-stringifies this value.
71
- */
72
- filters(tree: BackendFilterTreeRoot): Database<T>
73
- /**
74
- * DRF-style flat filter: `field` or `field__operator` query keys.
75
- */
76
- filters(field: string, operator: FilterOperator, value: string | number | boolean | (string | number | boolean)[]): Database<T>
77
- filters(
78
- arg0: string | BackendFilterTreeRoot,
79
- arg1?: FilterOperator,
80
- arg2?: string | number | boolean | (string | number | boolean)[]
81
- ): Database<T> {
82
- if (typeof arg0 === 'string' && arg1 !== undefined && arg2 !== undefined) {
83
- const field = arg0
84
- const operator = arg1
85
- const value = arg2
86
- const filterKey = operator === 'eq' ? field : `${field}__${operator}`
87
- const filterValue = Array.isArray(value) ? value.join(',') : value
88
- return new Database<T>(this.client, { ...this.urlParams }, undefined, undefined, {
89
- ...this.queryParams,
90
- [filterKey]: filterValue
91
- }, { ...this.graphParams }, this.isEdges)
92
- }
93
-
94
- if (arg1 === undefined && arg2 === undefined && isBackendFilterTreeRoot(arg0)) {
95
- return new Database<T>(this.client, { ...this.urlParams }, undefined, undefined, {
96
- ...this.queryParams,
97
- filters: JSON.stringify(arg0)
98
- }, { ...this.graphParams }, this.isEdges)
99
- }
100
-
101
- throw new TypeError(
102
- 'Database.filters: use filters(tree) with a root array of { operator: "and"|"or", value: [...] }, or filters(field, operator, value).'
103
- )
104
- }
105
-
106
- /**
107
- * Sets the `ordering` query param (DRF-style: `-field` for desc, comma-separated for multiple).
108
- * - `orderBy('created_at', 'desc')` — one column (optional second arg defaults to `'asc'`)
109
- * - `orderBy([{ field: 'salary', order: 'desc' }, { field: 'hire_date' }])` — multiple columns
110
- * - `orderBy('-salary,hire_date')` — raw string (e.g. from `convertRefineSorters`); omit the second arg
111
- */
112
- orderBy(
113
- fieldOrFields: string | Array<{ field: string; order?: SortOrder }>,
114
- order?: SortOrder
115
- ): Database<T> {
116
- let ordering: string
117
- if (typeof fieldOrFields === 'string') {
118
- const o = order ?? 'asc'
119
- ordering = o === 'desc' ? `-${fieldOrFields}` : fieldOrFields
120
- } else {
121
- ordering = fieldOrFields
122
- .map(({ field, order: o = 'asc' }) => (o === 'desc' ? `-${field}` : field))
123
- .join(',')
124
- }
125
- return new Database<T>(this.client, { ...this.urlParams }, undefined, undefined, {
126
- ...this.queryParams,
127
- ordering
128
- }, { ...this.graphParams }, this.isEdges)
129
- }
130
-
131
- pageSize(size: number): Database<T> {
132
- return new Database<T>(this.client, { ...this.urlParams }, undefined, undefined, {
133
- ...this.queryParams,
134
- page_size: size
135
- }, { ...this.graphParams }, this.isEdges)
136
- }
137
-
138
- page(num: number): Database<T> {
139
- return new Database<T>(this.client, { ...this.urlParams }, undefined, undefined, {
140
- ...this.queryParams,
141
- page: num
142
- }, { ...this.graphParams }, this.isEdges)
143
- }
144
-
145
- populate(populate: string[]): Database<T> {
146
- return new Database<T>(this.client, { ...this.urlParams }, undefined, undefined, {
147
- ...this.queryParams,
148
- populate: populate.join(',')
149
- }, { ...this.graphParams }, this.isEdges)
150
- }
151
-
152
- /** Populate all first-level relations (`?populate=*`). */
153
- populateAll(): Database<T> {
154
- return this.populate(['*'])
155
- }
156
-
157
- search(query: string): Database<T> {
158
- return new Database<T>(this.client, { ...this.urlParams }, undefined, undefined, {
159
- ...this.queryParams,
160
- search: query
161
- }, { ...this.graphParams }, this.isEdges)
162
- }
163
-
164
- /** Restrict SELECT to named columns (`?fields=a,b,c`). */
165
- fields(columns: string): Database<T> {
166
- return new Database<T>(this.client, { ...this.urlParams }, undefined, undefined, {
167
- ...this.queryParams,
168
- fields: columns
169
- }, { ...this.graphParams }, this.isEdges)
170
- }
171
-
172
- allowedActions(actions: string[]): Database<T> {
173
- return new Database<T>(this.client, { ...this.urlParams }, undefined, undefined, {
174
- ...this.queryParams,
175
- allowed_actions: actions.join(',')
176
- }, { ...this.graphParams }, this.isEdges)
177
- }
178
-
179
- aggregate(...expressions: string[]): Database<T> {
180
- return new Database<T>(this.client, { ...this.urlParams }, undefined, undefined, {
181
- ...this.queryParams,
182
- _aggregate: expressions.join(',')
183
- }, { ...this.graphParams }, this.isEdges)
184
- }
185
-
186
- groupBy(...fields: string[]): Database<T> {
187
- return new Database<T>(this.client, { ...this.urlParams }, undefined, undefined, {
188
- ...this.queryParams,
189
- _group_by: fields.join(',')
190
- }, { ...this.graphParams }, this.isEdges)
191
- }
192
-
193
- having(condition: string): Database<T> {
194
- return new Database<T>(this.client, { ...this.urlParams }, undefined, undefined, {
195
- ...this.queryParams,
196
- _having: condition
197
- }, { ...this.graphParams }, this.isEdges)
198
- }
199
-
200
- // CRUD methods
201
- get(recordId: string): Database<T> {
202
- return new Database<T>(this.client, { ...this.urlParams, recordId }, HttpMethod.GET, undefined, this.queryParams, { ...this.graphParams }, this.isEdges)
203
- }
204
-
205
- create(body: Partial<T> | Partial<T>[] | EdgeRequest[]): Database<T> {
206
- return new Database<T>(this.client, { ...this.urlParams }, HttpMethod.POST, body as object, this.queryParams, { ...this.graphParams }, this.isEdges)
207
- }
208
-
209
- upsert(body: Partial<T> | Partial<T>[], uniqueFields?: string[]): Database<T> {
210
- const qp = uniqueFields?.length ? { ...this.queryParams, unique_fields: uniqueFields.join(',') } : this.queryParams
211
- return new Database<T>(this.client, { ...this.urlParams }, HttpMethod.POST, body as object, qp, { ...this.graphParams }, this.isEdges, true)
212
- }
213
-
214
- update(body: Partial<T> | EdgeRequest): Database<T> {
215
- return new Database<T>(this.client, { ...this.urlParams }, HttpMethod.PATCH, body as object, this.queryParams, { ...this.graphParams }, this.isEdges)
216
- }
217
-
218
- bulkUpdate(body: Partial<T>[]): Database<T> {
219
- return new Database<T>(this.client, { ...this.urlParams }, HttpMethod.PATCH, body as object, this.queryParams, { ...this.graphParams }, this.isEdges)
220
- }
221
-
222
- delete(recordIdOrEdgeIds: string | number[]): Database<T> {
223
- if (Array.isArray(recordIdOrEdgeIds)) {
224
- const body: EdgeDeleteRequest = { edge_ids: recordIdOrEdgeIds }
225
- return new Database<T>(this.client, { ...this.urlParams }, HttpMethod.DELETE, body, this.queryParams, { ...this.graphParams }, this.isEdges)
226
- }
227
- return new Database<T>(this.client, { ...this.urlParams, recordId: recordIdOrEdgeIds }, HttpMethod.DELETE, undefined, this.queryParams, { ...this.graphParams }, this.isEdges)
228
- }
229
-
230
- bulkDelete(ids: string[]): Database<T> {
231
- return new Database<T>(this.client, { ...this.urlParams }, HttpMethod.DELETE, undefined, {
232
- ...this.queryParams,
233
- ids: ids.join(',')
234
- }, { ...this.graphParams }, this.isEdges)
235
- }
236
-
237
- deleteFiltered(): Database<T> {
238
- return new Database<T>(this.client, { ...this.urlParams }, HttpMethod.DELETE, undefined, this.queryParams, { ...this.graphParams }, this.isEdges)
239
- }
240
-
241
- async first(): Promise<T | null> {
242
- const response = await this.execute()
243
- const data = response.data
244
- if (Array.isArray(data)) {
245
- return data[0] ?? null
246
- }
247
- return data ?? null
248
- }
249
-
250
- async count(): Promise<number> {
251
- const response = await this.execute()
252
- if (response.total !== undefined) {
253
- return response.total
254
- }
255
- return Array.isArray(response.data) ? response.data.length : 0
256
- }
257
-
258
- private getTableName(): string {
259
- const table = this.urlParams.dataTables
260
- if (!table) throw new Error('Table name is required. Call .from(tableName) first.')
261
- return this.isEdges ? `${table}_edges` : table
262
- }
263
-
264
- private buildRoute(): string {
265
- const tableName = this.getTableName()
266
- const base = DatabaseRoutes.baseUrl(this.config.appSlug) +
267
- DatabaseRoutes.dataTables(tableName) +
268
- (this.urlParams.recordId ? DatabaseRoutes.recordId(this.urlParams.recordId) : '') +
269
- (this.isUpsert ? DatabaseRoutes.upsert() : '') +
270
- '/'
271
-
272
- // Merge database filters and graph params into one query string
273
- const allParams: Record<string, unknown> = { ...this.queryParams, ...this.graphParams }
274
- return base + buildQueryString(allParams)
275
- }
276
-
277
- async execute(): Promise<TaruviResponse<T | T[]>> {
278
- if (!this.urlParams.dataTables) {
279
- throw new Error('Table name is required. Call .from(tableName) first.')
280
- }
281
-
282
- const url = this.buildRoute()
283
- const operation = this.operation || HttpMethod.GET
284
-
285
- switch (operation) {
286
- case HttpMethod.POST:
287
- return await this.client.httpClient.post(url, this.body)
288
-
289
- case HttpMethod.PATCH:
290
- if (!this.urlParams.recordId && !Array.isArray(this.body)) {
291
- throw new Error('PATCH operation requires a record ID.')
292
- }
293
- return await this.client.httpClient.patch(url, this.body)
294
-
295
- case HttpMethod.DELETE:
296
- if (this.body) {
297
- return await this.client.httpClient.delete(url, this.body)
298
- }
299
- return await this.client.httpClient.delete(url)
300
-
301
- case HttpMethod.GET:
302
- default:
303
- return await this.client.httpClient.get(url)
304
- }
305
- }
306
- }
@@ -1,156 +0,0 @@
1
- import type { Client } from "../../client.js"
2
- import { HttpMethod } from "../../lib-internal/http/types.js"
3
- import type { TaruviResponse } from "../../types.js"
4
-
5
- export type DatabaseOperation = HttpMethod
6
-
7
- // Filter operators matching backend FilterParams.OPERATORS
8
- export type FilterOperator =
9
- // Comparison
10
- | 'eq' // Equal
11
- | 'ne' // Not equal
12
- | 'gt' // Greater than
13
- | 'gte' // Greater than or equal
14
- | 'lt' // Less than
15
- | 'lte' // Less than or equal
16
- // IN operators
17
- | 'in' // In array
18
- | 'nin' // Not in array
19
- | 'ina' // In array (case-insensitive)
20
- | 'nina' // Not in array (case-insensitive)
21
- // String contains (backend: plain contains uses ILIKE; *s suffix = case-sensitive LIKE)
22
- | 'contains' // Contains (case-insensitive, ILIKE)
23
- | 'ncontains' // Not contains (case-insensitive)
24
- | 'containss' // Contains (case-sensitive LIKE)
25
- | 'ncontainss' // Not contains (case-sensitive LIKE)
26
- | 'icontains' // Alias → contains (case-insensitive)
27
- | 'nicontains' // Alias → ncontains
28
- // Starts with
29
- | 'startswith' // Starts with (case-insensitive)
30
- | 'nstartswith' // Not starts with (case-insensitive)
31
- | 'startswiths' // Starts with (case-sensitive LIKE)
32
- | 'nstartswiths' // Not starts with (case-sensitive LIKE)
33
- // Ends with
34
- | 'endswith' // Ends with (case-insensitive)
35
- | 'nendswith' // Not ends with (case-insensitive)
36
- | 'endswiths' // Ends with (case-sensitive LIKE)
37
- | 'nendswiths' // Not ends with (case-sensitive LIKE)
38
- // Range
39
- | 'between' // Between two values
40
- | 'nbetween' // Not between two values
41
- // Null checks
42
- | 'null' // Is null
43
- | 'nnull' // Is not null
44
- // Search / pattern
45
- | 'search' // Full-text search
46
- | 'like' // SQL LIKE pattern
47
- | 'ilike' // Case-insensitive LIKE
48
- // Array containment (PostgreSQL)
49
- | 'acontains' // Array contains all items (column @> ARRAY[values])
50
- | 'nacontains' // NOT array contains
51
- | 'acontainedby' // Array contained by (column <@ ARRAY[values])
52
- | 'nacontainedby' // NOT array contained by
53
- | 'aoverlap' // Arrays have overlap (column && ARRAY[values])
54
- | 'naoverlap' // No overlap
55
- | 'aelement' // Value exists in array (value = ANY(column))
56
- | 'naelement' // Value not in array (value != ALL(column))
57
- // PostgreSQL range type operators
58
- | 'rcontains' // Range column @> value or [lower, upper]
59
- | 'rcontainedby' // Range column <@ [lower, upper]
60
- | 'roverlaps' // Range column && [lower, upper]
61
- | 'radjacent' // Range column -|- [lower, upper]
62
- | 'rstrictleft' // Range column << [lower, upper]
63
- | 'rstrictright' // Range column >> [lower, upper]
64
-
65
- export type SortOrder = 'asc' | 'desc'
66
-
67
- // Internal types
68
- export interface UrlParams {
69
- appSlug?: string
70
- dataTables?: string
71
- recordId?: string
72
- }
73
-
74
- export interface DatabaseClientInterface {
75
- client: Client
76
- urlParams?: UrlParams
77
- }
78
-
79
- // Request types
80
- export interface DatabaseRequest {
81
- [key: string]: unknown
82
- }
83
-
84
- // Response types - uses standard wrapper
85
- export type DatabaseResponse<T = unknown> = TaruviResponse<T[]>
86
- export type DatabaseSingleResponse<T = unknown> = TaruviResponse<T>
87
-
88
- // Graph traversal types
89
- export type GraphInclude = 'descendants' | 'ancestors' | 'both'
90
- export type GraphFormat = 'tree' | 'graph'
91
-
92
- // Edge types
93
- export interface EdgeRequest {
94
- from_id: number | string
95
- to_id: number | string
96
- type: string
97
- metadata?: Record<string, unknown>
98
- }
99
-
100
- export interface EdgeResponse {
101
- id: number
102
- from_id: number | string
103
- to_id: number | string
104
- type: string
105
- metadata?: Record<string, unknown>
106
- }
107
-
108
- export interface EdgeDeleteRequest {
109
- edge_ids: number[]
110
- }
111
-
112
- export interface PgRangeValue {
113
- lower: string | number | null
114
- upper: string | number | null
115
- bounds: '()' | '(]' | '[)' | '[]'
116
- empty: boolean
117
- }
118
-
119
- /**
120
- * Leaf node for the backend `filters` JSON query param.
121
- * Leaf `operator` strings are platform tokens (see taruvi-platform `FIELD_OPERATORS` /
122
- * `filter_translator`). `contains` = case-insensitive substring; `containss` = case-sensitive.
123
- */
124
- export interface BackendFilterLeafNode {
125
- field: string
126
- operator: string
127
- value: unknown
128
- }
129
-
130
- /** Logical `and` / `or` group for the backend `filters` JSON query param. */
131
- export interface BackendFilterLogicalNode {
132
- operator: 'and' | 'or'
133
- value: BackendFilterNode[]
134
- }
135
-
136
- export type BackendFilterNode = BackendFilterLogicalNode | BackendFilterLeafNode
137
-
138
- /** Top level of the `filters` JSON payload: an array of logical nodes. */
139
- export type BackendFilterTreeRoot = BackendFilterLogicalNode[]
140
-
141
- function isPlainObject(x: unknown): x is Record<string, unknown> {
142
- return typeof x === 'object' && x !== null && !Array.isArray(x)
143
- }
144
-
145
- function isBackendFilterLogicalNode(x: unknown): x is BackendFilterLogicalNode {
146
- if (!isPlainObject(x)) return false
147
- if (x.operator !== 'and' && x.operator !== 'or') return false
148
- if (!Array.isArray(x.value)) return false
149
- if ('field' in x) return false
150
- return true
151
- }
152
-
153
- /** Runtime check for the single-arg `filters(tree)` overload. */
154
- export function isBackendFilterTreeRoot(x: unknown): x is BackendFilterTreeRoot {
155
- return Array.isArray(x) && x.every(isBackendFilterLogicalNode)
156
- }
@@ -1,27 +0,0 @@
1
- import type { Client } from "../../client.js";
2
- import type { TaruviConfig } from "../../types.js";
3
- import type { FunctionRequest, FunctionResponse } from "./types.js";
4
- import { FunctionRoutes } from "../../lib-internal/routes/FunctionRoutes.js";
5
-
6
- export class Functions {
7
- private client: Client
8
- private config: TaruviConfig
9
-
10
- constructor(client: Client) {
11
- this.client = client
12
- this.config = this.client.getConfig()
13
- }
14
-
15
- async execute<T = unknown>(functionSlug: string, options: FunctionRequest = {}): Promise<FunctionResponse<T>> {
16
- const url = `${FunctionRoutes.baseUrl(this.config.appSlug, functionSlug)}/execute/`
17
-
18
- const body = {
19
- async: options.async ?? false,
20
- params: {
21
- ...options.params
22
- }
23
- }
24
-
25
- return await this.client.httpClient.post<FunctionResponse<T>>(url, body)
26
- }
27
- }
@@ -1,27 +0,0 @@
1
- export interface FunctionRequest {
2
- async?: boolean
3
- params?: Record<string, unknown>
4
- }
5
-
6
- export interface FunctionInvocation {
7
- id: number
8
- celery_task_id: string
9
- function: number
10
- function_name: string
11
- function_slug: string
12
- user_id: string | null
13
- user_username: string
14
- user_email: string
15
- task_status: string
16
- trigger_type: string
17
- created_at: string
18
- updated_at?: string
19
- }
20
-
21
- // Response type - matches backend AppDataResponse with invocation
22
- export interface FunctionResponse<T = unknown> {
23
- status: "success" | "error"
24
- message: string
25
- data: T | null
26
- invocation: FunctionInvocation
27
- }
@@ -1,79 +0,0 @@
1
- import type { Client } from "../../client.js"
2
- import { PolicyRoutes } from "../../lib-internal/routes/PolicyRoutes.js"
3
- import type { TaruviConfig } from "../../types.js"
4
- import type { Resources, Resource, PolicyCheckBatchResult, GetAllowedActionsOptions } from "./types.js"
5
-
6
- export class Policy {
7
- private client: Client
8
- private config: TaruviConfig
9
- constructor(client: Client) {
10
- this.client = client
11
- this.config = this.client.getConfig()
12
- }
13
-
14
- async checkResource(resources: Resources): Promise<PolicyCheckBatchResult> {
15
- const url = PolicyRoutes.baseUrl(this.config.appSlug) + PolicyRoutes.checkResource
16
- const body = {
17
- resources: resources.map(r => ({
18
- resource: {
19
- kind: r.resource,
20
- id: r.recordId,
21
- attr: r.attributes || {}
22
- },
23
- actions: r.actions
24
- }))
25
- }
26
-
27
- return await this.client.httpClient.post<PolicyCheckBatchResult>(url, body)
28
- }
29
-
30
- /**
31
- * Get list of allowed actions for a specific resource.
32
- *
33
- * @param resource - Resource with kind and id
34
- * @param options - Optional actions list, principal override, and aux data
35
- * @returns List of action names that are allowed
36
- *
37
- * @example
38
- * ```typescript
39
- * const allowed = await client.policy.getAllowedActions(
40
- * { kind: 'datatable:users', id: '123', attr: {} }
41
- * )
42
- * // Returns: ['read', 'write', 'update'] // 'delete' not allowed
43
- * ```
44
- */
45
- async getAllowedActions(
46
- resource: Resource,
47
- options: GetAllowedActionsOptions = {}
48
- ): Promise<string[]> {
49
- const { actions = ['read', 'write', 'create', 'update', 'delete'], principal, auxData } = options
50
-
51
- const url = PolicyRoutes.baseUrl(this.config.appSlug) + PolicyRoutes.checkResource
52
- const body: Record<string, unknown> = {
53
- resources: [{
54
- resource: resource,
55
- actions: actions
56
- }]
57
- }
58
-
59
- if (principal) {
60
- body.principal = principal
61
- }
62
- if (auxData) {
63
- body.auxData = auxData
64
- }
65
-
66
- const result = await this.client.httpClient.post<PolicyCheckBatchResult>(url, body)
67
-
68
- // Extract allowed actions
69
- if (result.results && result.results.length > 0) {
70
- const firstResult = result.results[0]
71
- const actionResults = firstResult?.actions || {}
72
- return Object.entries(actionResults)
73
- .filter(([_, effect]) => effect === 'EFFECT_ALLOW')
74
- .map(([action, _]) => action)
75
- }
76
-
77
- return []
78
- }
79
- }
@@ -1,39 +0,0 @@
1
- import type { TaruviResponse } from "../../types.js"
2
-
3
- export interface Principal {
4
- id: string
5
- roles: string[]
6
- attr: Record<string, unknown>
7
- }
8
-
9
- export type Resource = {
10
- kind: string
11
- id: string
12
- attr: Record<string, unknown>
13
- }
14
-
15
- export type Resources = {
16
- resource: string
17
- recordId: string
18
- attributes: Record<string, unknown>
19
- actions: string[]
20
- }[]
21
-
22
- export interface PolicyCheckResult {
23
- resource: Resource
24
- actions: Record<string, string>
25
- }
26
-
27
- export interface PolicyCheckBatchResult {
28
- requestId: string
29
- results: PolicyCheckResult[]
30
- }
31
-
32
- export type GetAllowedActionsOptions = {
33
- actions?: string[]
34
- principal?: Principal
35
- auxData?: Record<string, unknown>
36
- }
37
-
38
- // Response types - uses standard wrapper
39
- export type ResourceCheckResponse = TaruviResponse<PolicyCheckBatchResult>