@xata.io/client 0.1.1 → 0.1.5

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/README.md ADDED
@@ -0,0 +1 @@
1
+ Visit https://github.com/xataio/client-ts for more information.
package/dist/index.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  export interface XataRecord {
2
- _id: string;
3
- _version: number;
2
+ id: string;
3
+ xata: {
4
+ version: number;
5
+ };
4
6
  read(): Promise<this>;
5
7
  update(data: Selectable<this>): Promise<this>;
6
8
  delete(): Promise<void>;
@@ -17,31 +19,40 @@ export declare type OmitLinks<T> = {
17
19
  export declare type OmitMethods<T> = {
18
20
  [key in keyof T as T[key] extends Function ? never : key]: T[key];
19
21
  };
20
- export declare type Selectable<T> = Omit<OmitQueries<OmitMethods<T>>, '_id' | '_version'>;
22
+ export declare type Selectable<T> = Omit<OmitQueries<OmitMethods<T>>, 'id' | 'xata'>;
21
23
  export declare type Select<T, K extends keyof T> = Pick<T, K> & Queries<T> & XataRecord;
22
24
  export declare type Include<T> = {
23
25
  [key in keyof T as T[key] extends XataRecord ? key : never]?: boolean | Array<keyof Selectable<T[key]>>;
24
26
  };
25
27
  declare type SortDirection = 'asc' | 'desc';
26
- declare type Operator = '_gt' | '_lt' | '_gte' | '_lte' | '_exists' | '_notExists' | '_endsWith' | '_startsWith' | '_pattern' | '_isNot' | '_includes' | '_includesSubstring' | '_includesPattern' | '_includesAll';
27
- declare type Constraint<T> = Partial<Record<Operator, T>>;
28
+ declare type Operator = '$gt' | '$lt' | '$ge' | '$le' | '$exists' | '$notExists' | '$endsWith' | '$startsWith' | '$pattern' | '$is' | '$isNot' | '$contains' | '$includes' | '$includesSubstring' | '$includesPattern' | '$includesAll';
29
+ declare type Constraint<T> = {
30
+ [key in Operator]?: T;
31
+ };
32
+ declare type DeepConstraint<T> = T extends Record<string, any> ? {
33
+ [key in keyof T]?: T[key] | DeepConstraint<T[key]>;
34
+ } : Constraint<T>;
28
35
  declare type ComparableType = number | Date;
29
- export declare const gt: <T extends ComparableType>(value: T) => Partial<Record<Operator, T>>;
30
- export declare const gte: <T extends ComparableType>(value: T) => Partial<Record<Operator, T>>;
31
- export declare const lt: <T extends ComparableType>(value: T) => Partial<Record<Operator, T>>;
32
- export declare const lte: <T extends ComparableType>(value: T) => Partial<Record<Operator, T>>;
36
+ export declare const gt: <T extends ComparableType>(value: T) => Constraint<T>;
37
+ export declare const ge: <T extends ComparableType>(value: T) => Constraint<T>;
38
+ export declare const gte: <T extends ComparableType>(value: T) => Constraint<T>;
39
+ export declare const lt: <T extends ComparableType>(value: T) => Constraint<T>;
40
+ export declare const lte: <T extends ComparableType>(value: T) => Constraint<T>;
41
+ export declare const le: <T extends ComparableType>(value: T) => Constraint<T>;
33
42
  export declare const exists: (column: string) => Constraint<string>;
34
43
  export declare const notExists: (column: string) => Constraint<string>;
35
44
  export declare const startsWith: (value: string) => Constraint<string>;
36
45
  export declare const endsWith: (value: string) => Constraint<string>;
37
46
  export declare const pattern: (value: string) => Constraint<string>;
38
- export declare const isNot: <T>(value: T) => Partial<Record<Operator, T>>;
47
+ export declare const is: <T>(value: T) => Constraint<T>;
48
+ export declare const isNot: <T>(value: T) => Constraint<T>;
49
+ export declare const contains: <T>(value: T) => Constraint<T>;
39
50
  export declare const includes: (value: string) => Constraint<string>;
40
51
  export declare const includesSubstring: (value: string) => Constraint<string>;
41
52
  export declare const includesPattern: (value: string) => Constraint<string>;
42
53
  export declare const includesAll: (value: string) => Constraint<string>;
43
54
  declare type FilterConstraints<T> = {
44
- [key in keyof T]?: T[key] extends Record<string, any> ? FilterConstraints<T[key]> : T[key] | Constraint<T[key]>;
55
+ [key in keyof T]?: T[key] extends Record<string, any> ? FilterConstraints<T[key]> : T[key] | DeepConstraint<T[key]>;
45
56
  };
46
57
  declare type BulkQueryOptions<T> = {
47
58
  filter?: FilterConstraints<T>;
@@ -54,32 +65,23 @@ declare type QueryOrConstraint<T, R> = Query<T, R> | Constraint<T>;
54
65
  export declare class Query<T, R = T> {
55
66
  table: string;
56
67
  repository: Repository<T>;
57
- readonly _any?: QueryOrConstraint<T, R>[];
58
- readonly _all?: QueryOrConstraint<T, R>[];
59
- readonly _not?: QueryOrConstraint<T, R>[];
60
- readonly _none?: QueryOrConstraint<T, R>[];
61
- readonly _sort?: Record<string, SortDirection>;
68
+ readonly $any?: QueryOrConstraint<T, R>[];
69
+ readonly $all?: QueryOrConstraint<T, R>[];
70
+ readonly $not?: QueryOrConstraint<T, R>[];
71
+ readonly $none?: QueryOrConstraint<T, R>[];
72
+ readonly $sort?: Record<string, SortDirection>;
62
73
  constructor(repository: Repository<T> | null, table: string, data: Partial<Query<T, R>>, parent?: Query<T, R>);
63
74
  any(...queries: Query<T, R>[]): Query<T, R>;
64
75
  all(...queries: Query<T, R>[]): Query<T, R>;
65
76
  not(...queries: Query<T, R>[]): Query<T, R>;
66
77
  none(...queries: Query<T, R>[]): Query<T, R>;
67
78
  filter(constraints: FilterConstraints<T>): Query<T, R>;
68
- filter<F extends keyof T>(column: F, value: FilterConstraints<T[F]> | Constraint<T[F]>): Query<T, R>;
79
+ filter<F extends keyof T>(column: F, value: FilterConstraints<T[F]> | DeepConstraint<T[F]>): Query<T, R>;
69
80
  sort<F extends keyof T>(column: F, direction: SortDirection): Query<T, R>;
70
81
  getMany(options?: BulkQueryOptions<T>): Promise<R[]>;
71
82
  getOne(options?: BulkQueryOptions<T>): Promise<R | null>;
72
83
  deleteAll(): Promise<number>;
73
84
  include(columns: Include<T>): this;
74
- toJSON(): {
75
- _filter: {
76
- _any: QueryOrConstraint<T, R>[] | undefined;
77
- _all: QueryOrConstraint<T, R>[] | undefined;
78
- _not: QueryOrConstraint<T, R>[] | undefined;
79
- _none: QueryOrConstraint<T, R>[] | undefined;
80
- } | undefined;
81
- _sort: Record<string, SortDirection> | undefined;
82
- };
83
85
  }
84
86
  export declare abstract class Repository<T> extends Query<T, Selectable<T>> {
85
87
  select<K extends keyof Selectable<T>>(...columns: K[]): Query<T, Select<T, K>>;
@@ -109,8 +111,8 @@ export declare class RestRespositoryFactory implements RepositoryFactory {
109
111
  }
110
112
  export declare type XataClientOptions = {
111
113
  fetch?: unknown;
112
- url: string;
113
- token: string;
114
+ databaseURL: string;
115
+ apiKey: string;
114
116
  repositoryFactory?: RepositoryFactory;
115
117
  };
116
118
  export declare class BaseClient<D extends Record<string, Repository<any>>> {
package/dist/index.js CHANGED
@@ -9,35 +9,43 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.XataError = exports.BaseClient = exports.RestRespositoryFactory = exports.RestRepository = exports.Repository = exports.Query = exports.includesAll = exports.includesPattern = exports.includesSubstring = exports.includes = exports.isNot = exports.pattern = exports.endsWith = exports.startsWith = exports.notExists = exports.exists = exports.lte = exports.lt = exports.gte = exports.gt = void 0;
13
- const gt = (value) => ({ _gt: value });
12
+ exports.XataError = exports.BaseClient = exports.RestRespositoryFactory = exports.RestRepository = exports.Repository = exports.Query = exports.includesAll = exports.includesPattern = exports.includesSubstring = exports.includes = exports.contains = exports.isNot = exports.is = exports.pattern = exports.endsWith = exports.startsWith = exports.notExists = exports.exists = exports.le = exports.lte = exports.lt = exports.gte = exports.ge = exports.gt = void 0;
13
+ const gt = (value) => ({ $gt: value });
14
14
  exports.gt = gt;
15
- const gte = (value) => ({ _gte: value });
15
+ const ge = (value) => ({ $ge: value });
16
+ exports.ge = ge;
17
+ const gte = (value) => ({ $ge: value });
16
18
  exports.gte = gte;
17
- const lt = (value) => ({ _lt: value });
19
+ const lt = (value) => ({ $lt: value });
18
20
  exports.lt = lt;
19
- const lte = (value) => ({ _lte: value });
21
+ const lte = (value) => ({ $le: value });
20
22
  exports.lte = lte;
21
- const exists = (column) => ({ _exists: column });
23
+ const le = (value) => ({ $le: value });
24
+ exports.le = le;
25
+ const exists = (column) => ({ $exists: column });
22
26
  exports.exists = exists;
23
- const notExists = (column) => ({ _notExists: column });
27
+ const notExists = (column) => ({ $notExists: column });
24
28
  exports.notExists = notExists;
25
- const startsWith = (value) => ({ _startsWith: value });
29
+ const startsWith = (value) => ({ $startsWith: value });
26
30
  exports.startsWith = startsWith;
27
- const endsWith = (value) => ({ _endsWith: value });
31
+ const endsWith = (value) => ({ $endsWith: value });
28
32
  exports.endsWith = endsWith;
29
- const pattern = (value) => ({ _pattern: value });
33
+ const pattern = (value) => ({ $pattern: value });
30
34
  exports.pattern = pattern;
31
- const isNot = (value) => ({ _isNot: value });
35
+ const is = (value) => ({ $is: value });
36
+ exports.is = is;
37
+ const isNot = (value) => ({ $isNot: value });
32
38
  exports.isNot = isNot;
39
+ const contains = (value) => ({ $contains: value });
40
+ exports.contains = contains;
33
41
  // TODO: these can only be applied to columns of type "multiple"
34
- const includes = (value) => ({ _includes: value });
42
+ const includes = (value) => ({ $includes: value });
35
43
  exports.includes = includes;
36
- const includesSubstring = (value) => ({ _includesSubstring: value });
44
+ const includesSubstring = (value) => ({ $includesSubstring: value });
37
45
  exports.includesSubstring = includesSubstring;
38
- const includesPattern = (value) => ({ _includesPattern: value });
46
+ const includesPattern = (value) => ({ $includesPattern: value });
39
47
  exports.includesPattern = includesPattern;
40
- const includesAll = (value) => ({ _includesAll: value });
48
+ const includesAll = (value) => ({ $includesAll: value });
41
49
  exports.includesAll = includesAll;
42
50
  class Query {
43
51
  constructor(repository, table, data, parent) {
@@ -50,11 +58,11 @@ class Query {
50
58
  this.table = table;
51
59
  // For some reason Object.assign(this, parent) didn't work in this case
52
60
  // so doing all this manually:
53
- this._any = parent === null || parent === void 0 ? void 0 : parent._any;
54
- this._all = parent === null || parent === void 0 ? void 0 : parent._all;
55
- this._not = parent === null || parent === void 0 ? void 0 : parent._not;
56
- this._none = parent === null || parent === void 0 ? void 0 : parent._none;
57
- this._sort = parent === null || parent === void 0 ? void 0 : parent._sort;
61
+ this.$any = parent === null || parent === void 0 ? void 0 : parent.$any;
62
+ this.$all = parent === null || parent === void 0 ? void 0 : parent.$all;
63
+ this.$not = parent === null || parent === void 0 ? void 0 : parent.$not;
64
+ this.$none = parent === null || parent === void 0 ? void 0 : parent.$none;
65
+ this.$sort = parent === null || parent === void 0 ? void 0 : parent.$sort;
58
66
  Object.assign(this, data);
59
67
  // These bindings are used to support deconstructing
60
68
  // const { any, not, filter, sort } = xata.users.query()
@@ -64,25 +72,27 @@ class Query {
64
72
  this.filter = this.filter.bind(this);
65
73
  this.sort = this.sort.bind(this);
66
74
  this.none = this.none.bind(this);
75
+ Object.defineProperty(this, 'table', { enumerable: false });
76
+ Object.defineProperty(this, 'repository', { enumerable: false });
67
77
  }
68
78
  any(...queries) {
69
79
  return new Query(this.repository, this.table, {
70
- _any: (this._any || []).concat(queries)
80
+ $any: (this.$any || []).concat(queries)
71
81
  }, this);
72
82
  }
73
83
  all(...queries) {
74
84
  return new Query(this.repository, this.table, {
75
- _all: (this._all || []).concat(queries)
85
+ $all: (this.$all || []).concat(queries)
76
86
  }, this);
77
87
  }
78
88
  not(...queries) {
79
89
  return new Query(this.repository, this.table, {
80
- _not: (this._not || []).concat(queries)
90
+ $not: (this.$not || []).concat(queries)
81
91
  }, this);
82
92
  }
83
93
  none(...queries) {
84
94
  return new Query(this.repository, this.table, {
85
- _none: (this._none || []).concat(queries)
95
+ $none: (this.$none || []).concat(queries)
86
96
  }, this);
87
97
  }
88
98
  filter(a, b) {
@@ -93,21 +103,21 @@ class Query {
93
103
  queries.push({ [column]: constraint });
94
104
  }
95
105
  return new Query(this.repository, this.table, {
96
- _any: (this._any || []).concat(queries)
106
+ $all: (this.$all || []).concat(queries)
97
107
  }, this);
98
108
  }
99
109
  else {
100
110
  const column = a;
101
111
  const value = b;
102
112
  return new Query(this.repository, this.table, {
103
- _any: (this._any || []).concat({ [column]: value })
113
+ $all: (this.$all || []).concat({ [column]: value })
104
114
  }, this);
105
115
  }
106
116
  }
107
117
  sort(column, direction) {
108
- const sort = Object.assign(Object.assign({}, this._sort), { [column]: direction });
118
+ const sort = Object.assign(Object.assign({}, this.$sort), { [column]: direction });
109
119
  const q = new Query(this.repository, this.table, {
110
- _sort: sort
120
+ $sort: sort
111
121
  }, this);
112
122
  return q;
113
123
  }
@@ -136,18 +146,6 @@ class Query {
136
146
  // TODO
137
147
  return this;
138
148
  }
139
- toJSON() {
140
- const _filter = {
141
- _any: this._any,
142
- _all: this._all,
143
- _not: this._not,
144
- _none: this._none
145
- };
146
- return {
147
- _filter: Object.values(_filter).some(Boolean) ? _filter : undefined,
148
- _sort: this._sort
149
- };
150
- }
151
149
  }
152
150
  exports.Query = Query;
153
151
  class Repository extends Query {
@@ -186,13 +184,13 @@ class RestRepository extends Repository {
186
184
  }
187
185
  request(method, path, body) {
188
186
  return __awaiter(this, void 0, void 0, function* () {
189
- const { url: xatabaseURL } = this.client.options;
190
- const resp = yield this.fetch(`${xatabaseURL}${path}`, {
187
+ const { databaseURL } = this.client.options;
188
+ const resp = yield this.fetch(`${databaseURL}${path}`, {
191
189
  method,
192
190
  headers: {
193
191
  Accept: '*/*',
194
192
  'Content-Type': 'application/json',
195
- Authorization: `Bearer ${this.client.options.token}`
193
+ Authorization: `Bearer ${this.client.options.apiKey}`
196
194
  },
197
195
  body: JSON.stringify(body)
198
196
  });
@@ -222,7 +220,14 @@ class RestRepository extends Repository {
222
220
  }
223
221
  create(object) {
224
222
  return __awaiter(this, void 0, void 0, function* () {
225
- const obj = yield this.request('POST', `/tables/${this.table}/data`, object);
223
+ const body = Object.assign({}, object);
224
+ for (const key of Object.keys(body)) {
225
+ const value = body[key];
226
+ if (value && typeof value === 'object' && typeof value.id === 'string') {
227
+ body[key] = value.id;
228
+ }
229
+ }
230
+ const obj = yield this.request('POST', `/tables/${this.table}/data`, body);
226
231
  return this.client.initObject(this.table, obj);
227
232
  });
228
233
  }
@@ -252,7 +257,17 @@ class RestRepository extends Repository {
252
257
  }
253
258
  query(query) {
254
259
  return __awaiter(this, void 0, void 0, function* () {
255
- const result = yield this.request('POST', `/tables/${this.table}/query`, query);
260
+ const filter = {
261
+ $any: query.$any,
262
+ $all: query.$all,
263
+ $not: query.$not,
264
+ $none: query.$none
265
+ };
266
+ const body = {
267
+ filter: Object.values(filter).some(Boolean) ? filter : undefined,
268
+ sort: query.$sort
269
+ };
270
+ const result = yield this.request('POST', `/tables/${this.table}/query`, body);
256
271
  return result.records.map((record) => this.client.initObject(this.table, record));
257
272
  });
258
273
  }
@@ -277,15 +292,15 @@ class BaseClient {
277
292
  const [field, linkTable] = link;
278
293
  const value = o[field];
279
294
  if (value && typeof value === 'object') {
280
- const { _id } = value;
281
- if (Object.keys(value).find((col) => !col.startsWith('_'))) {
295
+ const { id } = value;
296
+ if (Object.keys(value).find((col) => col === 'id')) {
282
297
  o[field] = this.initObject(linkTable, value);
283
298
  }
284
- else if (_id) {
299
+ else if (id) {
285
300
  o[field] = {
286
- _id,
301
+ id,
287
302
  get: () => {
288
- this.db[linkTable].read(_id);
303
+ this.db[linkTable].read(id);
289
304
  }
290
305
  };
291
306
  }
@@ -293,13 +308,13 @@ class BaseClient {
293
308
  }
294
309
  const db = this.db;
295
310
  o.read = function () {
296
- return db[table].read(o['_id']);
311
+ return db[table].read(o['id']);
297
312
  };
298
313
  o.update = function (data) {
299
- return db[table].update(o['_id'], data);
314
+ return db[table].update(o['id'], data);
300
315
  };
301
316
  o.delete = function () {
302
- return db[table].delete(o['_id']);
317
+ return db[table].delete(o['id']);
303
318
  };
304
319
  for (const prop of ['read', 'update', 'delete']) {
305
320
  Object.defineProperty(o, prop, { enumerable: false });
@@ -13,8 +13,8 @@ const _1 = require("./");
13
13
  const fetch = jest.fn();
14
14
  const client = new _1.BaseClient({
15
15
  fetch,
16
- token: '1234',
17
- url: 'https://my-workspace-5df34do.staging.xatabase.co/db/xata:main'
16
+ apiKey: '1234',
17
+ databaseURL: 'https://my-workspace-5df34do.staging.xatabase.co/db/xata:main'
18
18
  }, {});
19
19
  const users = new _1.RestRepository(client, 'users');
20
20
  describe('request', () => {
@@ -119,17 +119,17 @@ describe('query', () => {
119
119
  expectRequest(expected, () => users.getMany(), { records: [] });
120
120
  }));
121
121
  test('query with one filter', () => __awaiter(void 0, void 0, void 0, function* () {
122
- const expected = { method: 'POST', path: '/tables/users/query', body: { _filter: { _any: [{ name: 'foo' }] } } };
122
+ const expected = { method: 'POST', path: '/tables/users/query', body: { filter: { $all: [{ name: 'foo' }] } } };
123
123
  expectRequest(expected, () => users.filter('name', 'foo').getMany(), { records: [] });
124
124
  }));
125
125
  });
126
126
  describe('getOne', () => {
127
127
  test('returns a single object', () => __awaiter(void 0, void 0, void 0, function* () {
128
- const result = { records: [{ _id: '1234' }] };
128
+ const result = { records: [{ id: '1234' }] };
129
129
  const expected = { method: 'POST', path: '/tables/users/query', body: {} };
130
130
  expectRequest(expected, () => __awaiter(void 0, void 0, void 0, function* () {
131
131
  const first = yield users.select().getOne();
132
- expect(first === null || first === void 0 ? void 0 : first._id).toBe(result.records[0]._id);
132
+ expect(first === null || first === void 0 ? void 0 : first.id).toBe(result.records[0].id);
133
133
  }), result);
134
134
  }));
135
135
  test('returns null if no objects are returned', () => __awaiter(void 0, void 0, void 0, function* () {
@@ -151,12 +151,12 @@ describe('read', () => {
151
151
  });
152
152
  describe('Repository.update', () => {
153
153
  test('updates and object successfully', () => __awaiter(void 0, void 0, void 0, function* () {
154
- const object = { _id: 'rec_1234', _version: 1, name: 'Ada' };
155
- const expected = { method: 'PUT', path: `/tables/users/data/${object._id}`, body: object };
154
+ const object = { id: 'rec_1234', xata: { version: 1 }, name: 'Ada' };
155
+ const expected = { method: 'PUT', path: `/tables/users/data/${object.id}`, body: object };
156
156
  expectRequest(expected, () => __awaiter(void 0, void 0, void 0, function* () {
157
- const result = yield users.update(object._id, object);
158
- expect(result._id).toBe(object._id);
159
- }), { _id: object._id });
157
+ const result = yield users.update(object.id, object);
158
+ expect(result.id).toBe(object.id);
159
+ }), { id: object.id });
160
160
  }));
161
161
  });
162
162
  describe('Repository.delete', () => {
@@ -171,12 +171,12 @@ describe('Repository.delete', () => {
171
171
  });
172
172
  describe('create', () => {
173
173
  test('successful', () => __awaiter(void 0, void 0, void 0, function* () {
174
- const created = { _id: 'rec_1234', _version: 0 };
174
+ const created = { id: 'rec_1234', _version: 0 };
175
175
  const object = { name: 'Ada' };
176
176
  const expected = { method: 'POST', path: '/tables/users/data', body: object };
177
177
  expectRequest(expected, () => __awaiter(void 0, void 0, void 0, function* () {
178
178
  const result = yield users.create(object);
179
- expect(result._id).toBe(created._id);
179
+ expect(result.id).toBe(created.id);
180
180
  }), created);
181
181
  }));
182
182
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@xata.io/client",
3
- "version": "0.1.1",
4
- "description": "Xata.io SDK for TypesScript and JavaScript",
3
+ "version": "0.1.5",
4
+ "description": "Xata.io SDK for TypeScript and JavaScript",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
7
7
  "scripts": {
package/src/index.test.ts CHANGED
@@ -4,8 +4,8 @@ const fetch = jest.fn();
4
4
  const client = new BaseClient(
5
5
  {
6
6
  fetch,
7
- token: '1234',
8
- url: 'https://my-workspace-5df34do.staging.xatabase.co/db/xata:main'
7
+ apiKey: '1234',
8
+ databaseURL: 'https://my-workspace-5df34do.staging.xatabase.co/db/xata:main'
9
9
  },
10
10
  {}
11
11
  );
@@ -138,20 +138,20 @@ describe('query', () => {
138
138
  });
139
139
 
140
140
  test('query with one filter', async () => {
141
- const expected = { method: 'POST', path: '/tables/users/query', body: { _filter: { _any: [{ name: 'foo' }] } } };
141
+ const expected = { method: 'POST', path: '/tables/users/query', body: { filter: { $all: [{ name: 'foo' }] } } };
142
142
  expectRequest(expected, () => users.filter('name', 'foo').getMany(), { records: [] });
143
143
  });
144
144
  });
145
145
 
146
146
  describe('getOne', () => {
147
147
  test('returns a single object', async () => {
148
- const result = { records: [{ _id: '1234' }] };
148
+ const result = { records: [{ id: '1234' }] };
149
149
  const expected = { method: 'POST', path: '/tables/users/query', body: {} };
150
150
  expectRequest(
151
151
  expected,
152
152
  async () => {
153
153
  const first = await users.select().getOne();
154
- expect(first?._id).toBe(result.records[0]._id);
154
+ expect(first?.id).toBe(result.records[0].id);
155
155
  },
156
156
  result
157
157
  );
@@ -182,15 +182,15 @@ describe('read', () => {
182
182
 
183
183
  describe('Repository.update', () => {
184
184
  test('updates and object successfully', async () => {
185
- const object = { _id: 'rec_1234', _version: 1, name: 'Ada' } as User;
186
- const expected = { method: 'PUT', path: `/tables/users/data/${object._id}`, body: object };
185
+ const object = { id: 'rec_1234', xata: { version: 1 }, name: 'Ada' } as User;
186
+ const expected = { method: 'PUT', path: `/tables/users/data/${object.id}`, body: object };
187
187
  expectRequest(
188
188
  expected,
189
189
  async () => {
190
- const result = await users.update(object._id, object);
191
- expect(result._id).toBe(object._id);
190
+ const result = await users.update(object.id, object);
191
+ expect(result.id).toBe(object.id);
192
192
  },
193
- { _id: object._id }
193
+ { id: object.id }
194
194
  );
195
195
  });
196
196
  });
@@ -207,14 +207,14 @@ describe('Repository.delete', () => {
207
207
 
208
208
  describe('create', () => {
209
209
  test('successful', async () => {
210
- const created = { _id: 'rec_1234', _version: 0 };
210
+ const created = { id: 'rec_1234', _version: 0 };
211
211
  const object = { name: 'Ada' } as User;
212
212
  const expected = { method: 'POST', path: '/tables/users/data', body: object };
213
213
  expectRequest(
214
214
  expected,
215
215
  async () => {
216
216
  const result = await users.create(object);
217
- expect(result._id).toBe(created._id);
217
+ expect(result.id).toBe(created.id);
218
218
  },
219
219
  created
220
220
  );
package/src/index.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  export interface XataRecord {
2
- _id: string;
3
- _version: number;
2
+ id: string;
3
+ xata: {
4
+ version: number;
5
+ };
4
6
  read(): Promise<this>;
5
7
  update(data: Selectable<this>): Promise<this>;
6
8
  delete(): Promise<void>;
@@ -23,7 +25,7 @@ export type OmitMethods<T> = {
23
25
  [key in keyof T as T[key] extends Function ? never : key]: T[key];
24
26
  };
25
27
 
26
- export type Selectable<T> = Omit<OmitQueries<OmitMethods<T>>, '_id' | '_version'>;
28
+ export type Selectable<T> = Omit<OmitQueries<OmitMethods<T>>, 'id' | 'xata'>;
27
29
 
28
30
  export type Select<T, K extends keyof T> = Pick<T, K> & Queries<T> & XataRecord;
29
31
 
@@ -34,46 +36,58 @@ export type Include<T> = {
34
36
  type SortDirection = 'asc' | 'desc';
35
37
 
36
38
  type Operator =
37
- | '_gt'
38
- | '_lt'
39
- | '_gte'
40
- | '_lte'
41
- | '_exists'
42
- | '_notExists'
43
- | '_endsWith'
44
- | '_startsWith'
45
- | '_pattern'
46
- | '_isNot'
47
- | '_includes'
48
- | '_includesSubstring'
49
- | '_includesPattern'
50
- | '_includesAll';
39
+ | '$gt'
40
+ | '$lt'
41
+ | '$ge'
42
+ | '$le'
43
+ | '$exists'
44
+ | '$notExists'
45
+ | '$endsWith'
46
+ | '$startsWith'
47
+ | '$pattern'
48
+ | '$is'
49
+ | '$isNot'
50
+ | '$contains'
51
+ | '$includes'
52
+ | '$includesSubstring'
53
+ | '$includesPattern'
54
+ | '$includesAll';
51
55
 
52
56
  // TODO: restrict constraints depending on type?
53
57
  // E.g. startsWith cannot be used with numbers
54
- type Constraint<T> = Partial<Record<Operator, T>>;
58
+ type Constraint<T> = { [key in Operator]?: T };
59
+
60
+ type DeepConstraint<T> = T extends Record<string, any>
61
+ ? {
62
+ [key in keyof T]?: T[key] | DeepConstraint<T[key]>;
63
+ }
64
+ : Constraint<T>;
55
65
 
56
66
  type ComparableType = number | Date;
57
67
 
58
- export const gt = <T extends ComparableType>(value: T): Constraint<T> => ({ _gt: value });
59
- export const gte = <T extends ComparableType>(value: T): Constraint<T> => ({ _gte: value });
60
- export const lt = <T extends ComparableType>(value: T): Constraint<T> => ({ _lt: value });
61
- export const lte = <T extends ComparableType>(value: T): Constraint<T> => ({ _lte: value });
62
- export const exists = (column: string): Constraint<string> => ({ _exists: column });
63
- export const notExists = (column: string): Constraint<string> => ({ _notExists: column });
64
- export const startsWith = (value: string): Constraint<string> => ({ _startsWith: value });
65
- export const endsWith = (value: string): Constraint<string> => ({ _endsWith: value });
66
- export const pattern = (value: string): Constraint<string> => ({ _pattern: value });
67
- export const isNot = <T>(value: T): Constraint<T> => ({ _isNot: value });
68
+ export const gt = <T extends ComparableType>(value: T): Constraint<T> => ({ $gt: value });
69
+ export const ge = <T extends ComparableType>(value: T): Constraint<T> => ({ $ge: value });
70
+ export const gte = <T extends ComparableType>(value: T): Constraint<T> => ({ $ge: value });
71
+ export const lt = <T extends ComparableType>(value: T): Constraint<T> => ({ $lt: value });
72
+ export const lte = <T extends ComparableType>(value: T): Constraint<T> => ({ $le: value });
73
+ export const le = <T extends ComparableType>(value: T): Constraint<T> => ({ $le: value });
74
+ export const exists = (column: string): Constraint<string> => ({ $exists: column });
75
+ export const notExists = (column: string): Constraint<string> => ({ $notExists: column });
76
+ export const startsWith = (value: string): Constraint<string> => ({ $startsWith: value });
77
+ export const endsWith = (value: string): Constraint<string> => ({ $endsWith: value });
78
+ export const pattern = (value: string): Constraint<string> => ({ $pattern: value });
79
+ export const is = <T>(value: T): Constraint<T> => ({ $is: value });
80
+ export const isNot = <T>(value: T): Constraint<T> => ({ $isNot: value });
81
+ export const contains = <T>(value: T): Constraint<T> => ({ $contains: value });
68
82
 
69
83
  // TODO: these can only be applied to columns of type "multiple"
70
- export const includes = (value: string): Constraint<string> => ({ _includes: value });
71
- export const includesSubstring = (value: string): Constraint<string> => ({ _includesSubstring: value });
72
- export const includesPattern = (value: string): Constraint<string> => ({ _includesPattern: value });
73
- export const includesAll = (value: string): Constraint<string> => ({ _includesAll: value });
84
+ export const includes = (value: string): Constraint<string> => ({ $includes: value });
85
+ export const includesSubstring = (value: string): Constraint<string> => ({ $includesSubstring: value });
86
+ export const includesPattern = (value: string): Constraint<string> => ({ $includesPattern: value });
87
+ export const includesAll = (value: string): Constraint<string> => ({ $includesAll: value });
74
88
 
75
89
  type FilterConstraints<T> = {
76
- [key in keyof T]?: T[key] extends Record<string, any> ? FilterConstraints<T[key]> : T[key] | Constraint<T[key]>;
90
+ [key in keyof T]?: T[key] extends Record<string, any> ? FilterConstraints<T[key]> : T[key] | DeepConstraint<T[key]>;
77
91
  };
78
92
 
79
93
  type BulkQueryOptions<T> = {
@@ -92,11 +106,11 @@ export class Query<T, R = T> {
92
106
  table: string;
93
107
  repository: Repository<T>;
94
108
 
95
- readonly _any?: QueryOrConstraint<T, R>[];
96
- readonly _all?: QueryOrConstraint<T, R>[];
97
- readonly _not?: QueryOrConstraint<T, R>[];
98
- readonly _none?: QueryOrConstraint<T, R>[];
99
- readonly _sort?: Record<string, SortDirection>;
109
+ readonly $any?: QueryOrConstraint<T, R>[];
110
+ readonly $all?: QueryOrConstraint<T, R>[];
111
+ readonly $not?: QueryOrConstraint<T, R>[];
112
+ readonly $none?: QueryOrConstraint<T, R>[];
113
+ readonly $sort?: Record<string, SortDirection>;
100
114
 
101
115
  constructor(repository: Repository<T> | null, table: string, data: Partial<Query<T, R>>, parent?: Query<T, R>) {
102
116
  if (repository) {
@@ -108,11 +122,11 @@ export class Query<T, R = T> {
108
122
 
109
123
  // For some reason Object.assign(this, parent) didn't work in this case
110
124
  // so doing all this manually:
111
- this._any = parent?._any;
112
- this._all = parent?._all;
113
- this._not = parent?._not;
114
- this._none = parent?._none;
115
- this._sort = parent?._sort;
125
+ this.$any = parent?.$any;
126
+ this.$all = parent?.$all;
127
+ this.$not = parent?.$not;
128
+ this.$none = parent?.$none;
129
+ this.$sort = parent?.$sort;
116
130
 
117
131
  Object.assign(this, data);
118
132
  // These bindings are used to support deconstructing
@@ -123,6 +137,9 @@ export class Query<T, R = T> {
123
137
  this.filter = this.filter.bind(this);
124
138
  this.sort = this.sort.bind(this);
125
139
  this.none = this.none.bind(this);
140
+
141
+ Object.defineProperty(this, 'table', { enumerable: false });
142
+ Object.defineProperty(this, 'repository', { enumerable: false });
126
143
  }
127
144
 
128
145
  any(...queries: Query<T, R>[]): Query<T, R> {
@@ -130,7 +147,7 @@ export class Query<T, R = T> {
130
147
  this.repository,
131
148
  this.table,
132
149
  {
133
- _any: (this._any || []).concat(queries)
150
+ $any: (this.$any || []).concat(queries)
134
151
  },
135
152
  this
136
153
  );
@@ -141,7 +158,7 @@ export class Query<T, R = T> {
141
158
  this.repository,
142
159
  this.table,
143
160
  {
144
- _all: (this._all || []).concat(queries)
161
+ $all: (this.$all || []).concat(queries)
145
162
  },
146
163
  this
147
164
  );
@@ -152,7 +169,7 @@ export class Query<T, R = T> {
152
169
  this.repository,
153
170
  this.table,
154
171
  {
155
- _not: (this._not || []).concat(queries)
172
+ $not: (this.$not || []).concat(queries)
156
173
  },
157
174
  this
158
175
  );
@@ -163,14 +180,14 @@ export class Query<T, R = T> {
163
180
  this.repository,
164
181
  this.table,
165
182
  {
166
- _none: (this._none || []).concat(queries)
183
+ $none: (this.$none || []).concat(queries)
167
184
  },
168
185
  this
169
186
  );
170
187
  }
171
188
 
172
189
  filter(constraints: FilterConstraints<T>): Query<T, R>;
173
- filter<F extends keyof T>(column: F, value: FilterConstraints<T[F]> | Constraint<T[F]>): Query<T, R>;
190
+ filter<F extends keyof T>(column: F, value: FilterConstraints<T[F]> | DeepConstraint<T[F]>): Query<T, R>;
174
191
  filter(a: any, b?: any): Query<T, R> {
175
192
  if (arguments.length === 1) {
176
193
  const constraints = a as FilterConstraints<T>;
@@ -182,7 +199,7 @@ export class Query<T, R = T> {
182
199
  this.repository,
183
200
  this.table,
184
201
  {
185
- _any: (this._any || []).concat(queries)
202
+ $all: (this.$all || []).concat(queries)
186
203
  },
187
204
  this
188
205
  );
@@ -193,7 +210,7 @@ export class Query<T, R = T> {
193
210
  this.repository,
194
211
  this.table,
195
212
  {
196
- _any: (this._any || []).concat({ [column]: value })
213
+ $all: (this.$all || []).concat({ [column]: value })
197
214
  },
198
215
  this
199
216
  );
@@ -201,12 +218,12 @@ export class Query<T, R = T> {
201
218
  }
202
219
 
203
220
  sort<F extends keyof T>(column: F, direction: SortDirection): Query<T, R> {
204
- const sort = { ...this._sort, [column]: direction };
221
+ const sort = { ...this.$sort, [column]: direction };
205
222
  const q = new Query<T, R>(
206
223
  this.repository,
207
224
  this.table,
208
225
  {
209
- _sort: sort
226
+ $sort: sort
210
227
  },
211
228
  this
212
229
  );
@@ -236,19 +253,6 @@ export class Query<T, R = T> {
236
253
  // TODO
237
254
  return this;
238
255
  }
239
-
240
- toJSON() {
241
- const _filter = {
242
- _any: this._any,
243
- _all: this._all,
244
- _not: this._not,
245
- _none: this._none
246
- };
247
- return {
248
- _filter: Object.values(_filter).some(Boolean) ? _filter : undefined,
249
- _sort: this._sort
250
- };
251
- }
252
256
  }
253
257
 
254
258
  export abstract class Repository<T> extends Query<T, Selectable<T>> {
@@ -300,13 +304,13 @@ export class RestRepository<T> extends Repository<T> {
300
304
  }
301
305
 
302
306
  async request(method: string, path: string, body?: unknown) {
303
- const { url: xatabaseURL } = this.client.options;
304
- const resp: Response = await this.fetch(`${xatabaseURL}${path}`, {
307
+ const { databaseURL } = this.client.options;
308
+ const resp: Response = await this.fetch(`${databaseURL}${path}`, {
305
309
  method,
306
310
  headers: {
307
311
  Accept: '*/*',
308
312
  'Content-Type': 'application/json',
309
- Authorization: `Bearer ${this.client.options.token}`
313
+ Authorization: `Bearer ${this.client.options.apiKey}`
310
314
  },
311
315
  body: JSON.stringify(body)
312
316
  });
@@ -333,7 +337,14 @@ export class RestRepository<T> extends Repository<T> {
333
337
  }
334
338
 
335
339
  async create(object: T): Promise<T> {
336
- const obj = await this.request('POST', `/tables/${this.table}/data`, object);
340
+ const body = { ...object } as Record<string, unknown>;
341
+ for (const key of Object.keys(body)) {
342
+ const value = body[key];
343
+ if (value && typeof value === 'object' && typeof (value as Record<string, unknown>).id === 'string') {
344
+ body[key] = (value as XataRecord).id;
345
+ }
346
+ }
347
+ const obj = await this.request('POST', `/tables/${this.table}/data`, body);
337
348
  return this.client.initObject(this.table, obj);
338
349
  }
339
350
 
@@ -357,7 +368,17 @@ export class RestRepository<T> extends Repository<T> {
357
368
  }
358
369
 
359
370
  async query<R>(query: Query<T, R>): Promise<R[]> {
360
- const result = await this.request('POST', `/tables/${this.table}/query`, query);
371
+ const filter = {
372
+ $any: query.$any,
373
+ $all: query.$all,
374
+ $not: query.$not,
375
+ $none: query.$none
376
+ };
377
+ const body = {
378
+ filter: Object.values(filter).some(Boolean) ? filter : undefined,
379
+ sort: query.$sort
380
+ };
381
+ const result = await this.request('POST', `/tables/${this.table}/query`, body);
361
382
  return result.records.map((record: object) => this.client.initObject(this.table, record));
362
383
  }
363
384
  }
@@ -374,8 +395,8 @@ export class RestRespositoryFactory implements RepositoryFactory {
374
395
 
375
396
  export type XataClientOptions = {
376
397
  fetch?: unknown;
377
- url: string;
378
- token: string;
398
+ databaseURL: string;
399
+ apiKey: string;
379
400
  repositoryFactory?: RepositoryFactory;
380
401
  };
381
402
 
@@ -399,14 +420,14 @@ export class BaseClient<D extends Record<string, Repository<any>>> {
399
420
  const value = o[field];
400
421
 
401
422
  if (value && typeof value === 'object') {
402
- const { _id } = value as any;
403
- if (Object.keys(value).find((col) => !col.startsWith('_'))) {
423
+ const { id } = value as any;
424
+ if (Object.keys(value).find((col) => col === 'id')) {
404
425
  o[field] = this.initObject(linkTable, value);
405
- } else if (_id) {
426
+ } else if (id) {
406
427
  o[field] = {
407
- _id,
428
+ id,
408
429
  get: () => {
409
- this.db[linkTable].read(_id);
430
+ this.db[linkTable].read(id);
410
431
  }
411
432
  };
412
433
  }
@@ -415,13 +436,13 @@ export class BaseClient<D extends Record<string, Repository<any>>> {
415
436
 
416
437
  const db = this.db;
417
438
  o.read = function () {
418
- return db[table].read(o['_id'] as string);
439
+ return db[table].read(o['id'] as string);
419
440
  };
420
441
  o.update = function (data: any) {
421
- return db[table].update(o['_id'] as string, data);
442
+ return db[table].update(o['id'] as string, data);
422
443
  };
423
444
  o.delete = function () {
424
- return db[table].delete(o['_id'] as string);
445
+ return db[table].delete(o['id'] as string);
425
446
  };
426
447
 
427
448
  for (const prop of ['read', 'update', 'delete']) {