@xata.io/client 0.0.0-beta.59b45f7 → 0.0.0-beta.96dc1f0

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/index.d.ts CHANGED
@@ -54,15 +54,51 @@ export declare const includesAll: (value: string) => Constraint<string>;
54
54
  declare type FilterConstraints<T> = {
55
55
  [key in keyof T]?: T[key] extends Record<string, any> ? FilterConstraints<T[key]> : T[key] | DeepConstraint<T[key]>;
56
56
  };
57
+ declare type CursorNavigationOptions = {
58
+ first?: string;
59
+ } | {
60
+ last?: string;
61
+ } | {
62
+ after?: string;
63
+ before?: string;
64
+ };
65
+ declare type OffsetNavigationOptions = {
66
+ size?: number;
67
+ offset?: number;
68
+ };
69
+ declare type PaginationOptions = CursorNavigationOptions & OffsetNavigationOptions;
57
70
  declare type BulkQueryOptions<T> = {
58
- filter?: FilterConstraints<T>;
59
- sort?: {
60
- column: keyof T;
61
- direction?: SortDirection;
62
- } | keyof T;
71
+ page?: PaginationOptions;
63
72
  };
64
73
  declare type QueryOrConstraint<T, R> = Query<T, R> | Constraint<T>;
65
- export declare class Query<T, R = T> {
74
+ declare type QueryMeta = {
75
+ page: {
76
+ cursor: string;
77
+ more: boolean;
78
+ };
79
+ };
80
+ interface BasePage<T, R> {
81
+ query: Query<T, R>;
82
+ meta: QueryMeta;
83
+ records: R[];
84
+ nextPage(size?: number, offset?: number): Promise<Page<T, R>>;
85
+ previousPage(size?: number, offset?: number): Promise<Page<T, R>>;
86
+ firstPage(size?: number, offset?: number): Promise<Page<T, R>>;
87
+ lastPage(size?: number, offset?: number): Promise<Page<T, R>>;
88
+ hasNextPage(): boolean;
89
+ }
90
+ declare class Page<T, R> implements BasePage<T, R> {
91
+ readonly query: Query<T, R>;
92
+ readonly meta: QueryMeta;
93
+ readonly records: R[];
94
+ constructor(query: Query<T, R>, meta: QueryMeta, records?: R[]);
95
+ nextPage(size?: number, offset?: number): Promise<Page<T, R>>;
96
+ previousPage(size?: number, offset?: number): Promise<Page<T, R>>;
97
+ firstPage(size?: number, offset?: number): Promise<Page<T, R>>;
98
+ lastPage(size?: number, offset?: number): Promise<Page<T, R>>;
99
+ hasNextPage(): boolean;
100
+ }
101
+ export declare class Query<T, R = T> implements BasePage<T, R> {
66
102
  table: string;
67
103
  repository: Repository<T>;
68
104
  readonly $any?: QueryOrConstraint<T, R>[];
@@ -70,6 +106,9 @@ export declare class Query<T, R = T> {
70
106
  readonly $not?: QueryOrConstraint<T, R>[];
71
107
  readonly $none?: QueryOrConstraint<T, R>[];
72
108
  readonly $sort?: Record<string, SortDirection>;
109
+ readonly query: Query<T, R>;
110
+ readonly meta: QueryMeta;
111
+ readonly records: R[];
73
112
  constructor(repository: Repository<T> | null, table: string, data: Partial<Query<T, R>>, parent?: Query<T, R>);
74
113
  any(...queries: Query<T, R>[]): Query<T, R>;
75
114
  all(...queries: Query<T, R>[]): Query<T, R>;
@@ -78,30 +117,40 @@ export declare class Query<T, R = T> {
78
117
  filter(constraints: FilterConstraints<T>): Query<T, R>;
79
118
  filter<F extends keyof T>(column: F, value: FilterConstraints<T[F]> | DeepConstraint<T[F]>): Query<T, R>;
80
119
  sort<F extends keyof T>(column: F, direction: SortDirection): Query<T, R>;
120
+ getPaginated(options?: BulkQueryOptions<T>): Promise<Page<T, R>>;
121
+ [Symbol.asyncIterator](): AsyncIterableIterator<R>;
122
+ getIterator(chunk: number, options?: Omit<BulkQueryOptions<T>, 'page'>): AsyncGenerator<R[]>;
81
123
  getMany(options?: BulkQueryOptions<T>): Promise<R[]>;
82
- getOne(options?: BulkQueryOptions<T>): Promise<R | null>;
124
+ getOne(options?: Omit<BulkQueryOptions<T>, 'page'>): Promise<R | null>;
83
125
  deleteAll(): Promise<number>;
84
126
  include(columns: Include<T>): this;
127
+ nextPage(size?: number, offset?: number): Promise<Page<T, R>>;
128
+ previousPage(size?: number, offset?: number): Promise<Page<T, R>>;
129
+ firstPage(size?: number, offset?: number): Promise<Page<T, R>>;
130
+ lastPage(size?: number, offset?: number): Promise<Page<T, R>>;
131
+ hasNextPage(): boolean;
85
132
  }
86
133
  export declare abstract class Repository<T> extends Query<T, Selectable<T>> {
87
134
  select<K extends keyof Selectable<T>>(...columns: K[]): Query<T, Select<T, K>>;
88
135
  abstract create(object: Selectable<T>): Promise<T>;
136
+ abstract createMany(objects: Selectable<T>[]): Promise<T[]>;
89
137
  abstract read(id: string): Promise<T | null>;
90
138
  abstract update(id: string, object: Partial<T>): Promise<T>;
91
139
  abstract delete(id: string): void;
92
- abstract query<R>(query: Query<T, R>): Promise<R[]>;
140
+ abstract _runQuery<R>(query: Query<T, R>, options?: BulkQueryOptions<T>): Promise<Page<T, R>>;
93
141
  }
94
142
  export declare class RestRepository<T> extends Repository<T> {
95
143
  client: BaseClient<any>;
96
144
  fetch: any;
97
145
  constructor(client: BaseClient<any>, table: string);
98
- request(method: string, path: string, body?: unknown): Promise<any>;
146
+ request<T>(method: string, path: string, body?: unknown): Promise<T | undefined>;
99
147
  select<K extends keyof T>(...columns: K[]): Query<T, Select<T, K>>;
100
148
  create(object: T): Promise<T>;
149
+ createMany(objects: T[]): Promise<T[]>;
101
150
  read(id: string): Promise<T | null>;
102
151
  update(id: string, object: Partial<T>): Promise<T>;
103
152
  delete(id: string): Promise<void>;
104
- query<R>(query: Query<T, R>): Promise<R[]>;
153
+ _runQuery<R>(query: Query<T, R>, options?: BulkQueryOptions<T>): Promise<Page<T, R>>;
105
154
  }
106
155
  interface RepositoryFactory {
107
156
  createRepository<T>(client: BaseClient<any>, table: string): Repository<T>;
@@ -115,7 +164,7 @@ declare type BranchStrategy = BranchStrategyValue | BranchStrategyBuilder;
115
164
  declare type BranchStrategyOption = NonNullable<BranchStrategy | BranchStrategy[]>;
116
165
  export declare type XataClientOptions = {
117
166
  fetch?: unknown;
118
- databaseURL: string;
167
+ databaseURL?: string;
119
168
  branch: BranchStrategyOption;
120
169
  apiKey: string;
121
170
  repositoryFactory?: RepositoryFactory;
package/dist/index.js CHANGED
@@ -15,8 +15,21 @@ var __asyncValues = (this && this.__asyncValues) || function (o) {
15
15
  function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
16
16
  function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
17
17
  };
18
+ var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
19
+ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
20
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
21
+ var g = generator.apply(thisArg, _arguments || []), i, q = [];
22
+ return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
23
+ function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }
24
+ function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
25
+ function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
26
+ function fulfill(value) { resume("next", value); }
27
+ function reject(value) { resume("throw", value); }
28
+ function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
29
+ };
18
30
  Object.defineProperty(exports, "__esModule", { value: true });
19
31
  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;
32
+ const errors_1 = require("./util/errors");
20
33
  const gt = (value) => ({ $gt: value });
21
34
  exports.gt = gt;
22
35
  const ge = (value) => ({ $ge: value });
@@ -54,8 +67,43 @@ const includesPattern = (value) => ({ $includesPattern: value });
54
67
  exports.includesPattern = includesPattern;
55
68
  const includesAll = (value) => ({ $includesAll: value });
56
69
  exports.includesAll = includesAll;
70
+ class Page {
71
+ constructor(query, meta, records = []) {
72
+ this.query = query;
73
+ this.meta = meta;
74
+ this.records = records;
75
+ }
76
+ nextPage(size, offset) {
77
+ return __awaiter(this, void 0, void 0, function* () {
78
+ return this.query.getPaginated({ page: { size, offset, after: this.meta.page.cursor } });
79
+ });
80
+ }
81
+ previousPage(size, offset) {
82
+ return __awaiter(this, void 0, void 0, function* () {
83
+ return this.query.getPaginated({ page: { size, offset, before: this.meta.page.cursor } });
84
+ });
85
+ }
86
+ firstPage(size, offset) {
87
+ return __awaiter(this, void 0, void 0, function* () {
88
+ return this.query.getPaginated({ page: { size, offset, first: this.meta.page.cursor } });
89
+ });
90
+ }
91
+ lastPage(size, offset) {
92
+ return __awaiter(this, void 0, void 0, function* () {
93
+ return this.query.getPaginated({ page: { size, offset, last: this.meta.page.cursor } });
94
+ });
95
+ }
96
+ // TODO: We need to add something on the backend if we want a hasPreviousPage
97
+ hasNextPage() {
98
+ return this.meta.page.more;
99
+ }
100
+ }
57
101
  class Query {
58
102
  constructor(repository, table, data, parent) {
103
+ // Cursor pagination
104
+ this.query = this;
105
+ this.meta = { page: { cursor: 'start', more: true } };
106
+ this.records = [];
59
107
  if (repository) {
60
108
  this.repository = repository;
61
109
  }
@@ -128,24 +176,56 @@ class Query {
128
176
  }, this);
129
177
  return q;
130
178
  }
131
- // TODO: pagination. Maybe implement different methods for different type of paginations
132
- // and one to simply get the first records returned by the query with no pagination.
179
+ getPaginated(options) {
180
+ return __awaiter(this, void 0, void 0, function* () {
181
+ return this.repository._runQuery(this, options);
182
+ });
183
+ }
184
+ [Symbol.asyncIterator]() {
185
+ return __asyncGenerator(this, arguments, function* _a() {
186
+ var e_1, _b;
187
+ try {
188
+ for (var _c = __asyncValues(this.getIterator(1)), _d; _d = yield __await(_c.next()), !_d.done;) {
189
+ const [record] = _d.value;
190
+ yield yield __await(record);
191
+ }
192
+ }
193
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
194
+ finally {
195
+ try {
196
+ if (_d && !_d.done && (_b = _c.return)) yield __await(_b.call(_c));
197
+ }
198
+ finally { if (e_1) throw e_1.error; }
199
+ }
200
+ });
201
+ }
202
+ getIterator(chunk, options = {}) {
203
+ return __asyncGenerator(this, arguments, function* getIterator_1() {
204
+ let offset = 0;
205
+ let end = false;
206
+ while (!end) {
207
+ const { records, meta } = yield __await(this.getPaginated(Object.assign(Object.assign({}, options), { page: { size: chunk, offset } })));
208
+ yield yield __await(records);
209
+ offset += chunk;
210
+ end = !meta.page.more;
211
+ }
212
+ });
213
+ }
133
214
  getMany(options) {
134
215
  return __awaiter(this, void 0, void 0, function* () {
135
- // TODO: use options
136
- return this.repository.query(this);
216
+ const { records } = yield this.getPaginated(options);
217
+ return records;
137
218
  });
138
219
  }
139
- getOne(options) {
220
+ getOne(options = {}) {
140
221
  return __awaiter(this, void 0, void 0, function* () {
141
- // TODO: use options
142
- const arr = yield this.getMany(); // TODO, limit to 1
143
- return arr[0] || null;
222
+ const records = yield this.getMany(Object.assign(Object.assign({}, options), { page: { size: 1 } }));
223
+ return records[0] || null;
144
224
  });
145
225
  }
146
226
  deleteAll() {
147
227
  return __awaiter(this, void 0, void 0, function* () {
148
- // Return number of affected rows
228
+ // TODO: Return number of affected rows
149
229
  return 0;
150
230
  });
151
231
  }
@@ -153,6 +233,29 @@ class Query {
153
233
  // TODO
154
234
  return this;
155
235
  }
236
+ nextPage(size, offset) {
237
+ return __awaiter(this, void 0, void 0, function* () {
238
+ return this.firstPage(size, offset);
239
+ });
240
+ }
241
+ previousPage(size, offset) {
242
+ return __awaiter(this, void 0, void 0, function* () {
243
+ return this.firstPage(size, offset);
244
+ });
245
+ }
246
+ firstPage(size, offset) {
247
+ return __awaiter(this, void 0, void 0, function* () {
248
+ return this.getPaginated({ page: { size, offset } });
249
+ });
250
+ }
251
+ lastPage(size, offset) {
252
+ return __awaiter(this, void 0, void 0, function* () {
253
+ return this.getPaginated({ page: { size, offset, before: 'end' } });
254
+ });
255
+ }
256
+ hasNextPage() {
257
+ return this.meta.page.more;
258
+ }
156
259
  }
157
260
  exports.Query = Query;
158
261
  class Repository extends Query {
@@ -165,25 +268,16 @@ class RestRepository extends Repository {
165
268
  constructor(client, table) {
166
269
  super(null, table, {});
167
270
  this.client = client;
168
- const { fetch } = client.options;
169
- if (fetch) {
271
+ const doWeHaveFetch = typeof fetch !== 'undefined';
272
+ const isInjectedFetchProblematic = !this.client.options.fetch;
273
+ if (doWeHaveFetch) {
170
274
  this.fetch = fetch;
171
275
  }
172
- else if (typeof window === 'object') {
173
- this.fetch = window.fetch;
276
+ else if (isInjectedFetchProblematic) {
277
+ throw new Error(errors_1.errors.falsyFetchImplementation);
174
278
  }
175
- else if (typeof require === 'function') {
176
- try {
177
- this.fetch = require('node-fetch');
178
- }
179
- catch (err) {
180
- try {
181
- this.fetch = require('cross-fetch');
182
- }
183
- catch (err) {
184
- throw new Error('No fetch implementation found. Please provide one in the constructor');
185
- }
186
- }
279
+ else {
280
+ this.fetch = this.client.options.fetch;
187
281
  }
188
282
  Object.defineProperty(this, 'client', { enumerable: false });
189
283
  Object.defineProperty(this, 'fetch', { enumerable: false });
@@ -193,7 +287,8 @@ class RestRepository extends Repository {
193
287
  return __awaiter(this, void 0, void 0, function* () {
194
288
  const { databaseURL, apiKey } = this.client.options;
195
289
  const branch = yield this.client.getBranch();
196
- const resp = yield this.fetch(`${databaseURL}:${branch}${path}`, {
290
+ const fetchImpl = this.fetch;
291
+ const resp = yield fetchImpl(`${databaseURL}:${branch}${path}`, {
197
292
  method,
198
293
  headers: {
199
294
  Accept: '*/*',
@@ -219,7 +314,7 @@ class RestRepository extends Repository {
219
314
  throw new XataError(resp.statusText, resp.status);
220
315
  }
221
316
  if (resp.status === 204)
222
- return;
317
+ return undefined;
223
318
  return resp.json();
224
319
  });
225
320
  }
@@ -228,22 +323,40 @@ class RestRepository extends Repository {
228
323
  }
229
324
  create(object) {
230
325
  return __awaiter(this, void 0, void 0, function* () {
231
- const body = Object.assign({}, object);
232
- for (const key of Object.keys(body)) {
233
- const value = body[key];
234
- if (value && typeof value === 'object' && typeof value.id === 'string') {
235
- body[key] = value.id;
236
- }
326
+ const record = transformObjectLinks(object);
327
+ const response = yield this.request('POST', `/tables/${this.table}/data`, record);
328
+ if (!response) {
329
+ throw new Error("The server didn't return any data for the query");
330
+ }
331
+ const finalObject = yield this.read(response.id);
332
+ if (!finalObject) {
333
+ throw new Error('The server failed to save the record');
237
334
  }
238
- const obj = yield this.request('POST', `/tables/${this.table}/data`, body);
239
- return this.client.initObject(this.table, obj);
335
+ return finalObject;
336
+ });
337
+ }
338
+ createMany(objects) {
339
+ return __awaiter(this, void 0, void 0, function* () {
340
+ const records = objects.map((object) => transformObjectLinks(object));
341
+ const response = yield this.request('POST', `/tables/${this.table}/bulk`, { records });
342
+ if (!response) {
343
+ throw new Error("The server didn't return any data for the query");
344
+ }
345
+ // TODO: Use filer.$any() to get all the records
346
+ const finalObjects = yield Promise.all(response.recordIDs.map((id) => this.read(id)));
347
+ if (finalObjects.some((object) => !object)) {
348
+ throw new Error('The server failed to save the record');
349
+ }
350
+ return finalObjects;
240
351
  });
241
352
  }
242
353
  read(id) {
243
354
  return __awaiter(this, void 0, void 0, function* () {
244
355
  try {
245
- const obj = yield this.request('GET', `/tables/${this.table}/data/${id}`);
246
- return this.client.initObject(this.table, obj);
356
+ const response = yield this.request('GET', `/tables/${this.table}/data/${id}`);
357
+ if (!response)
358
+ return null;
359
+ return this.client.initObject(this.table, response);
247
360
  }
248
361
  catch (err) {
249
362
  if (err.status === 404)
@@ -254,8 +367,12 @@ class RestRepository extends Repository {
254
367
  }
255
368
  update(id, object) {
256
369
  return __awaiter(this, void 0, void 0, function* () {
257
- const obj = yield this.request('PUT', `/tables/${this.table}/data/${id}`, object);
258
- return this.client.initObject(this.table, obj);
370
+ const response = yield this.request('PUT', `/tables/${this.table}/data/${id}`, object);
371
+ if (!response) {
372
+ throw new Error("The server didn't return any data for the query");
373
+ }
374
+ // TODO: Review this, not sure we are properly initializing the object
375
+ return this.client.initObject(this.table, response);
259
376
  });
260
377
  }
261
378
  delete(id) {
@@ -263,7 +380,7 @@ class RestRepository extends Repository {
263
380
  yield this.request('DELETE', `/tables/${this.table}/data/${id}`);
264
381
  });
265
382
  }
266
- query(query) {
383
+ _runQuery(query, options) {
267
384
  return __awaiter(this, void 0, void 0, function* () {
268
385
  const filter = {
269
386
  $any: query.$any,
@@ -273,10 +390,16 @@ class RestRepository extends Repository {
273
390
  };
274
391
  const body = {
275
392
  filter: Object.values(filter).some(Boolean) ? filter : undefined,
276
- sort: query.$sort
393
+ sort: query.$sort,
394
+ page: options === null || options === void 0 ? void 0 : options.page
277
395
  };
278
- const result = yield this.request('POST', `/tables/${this.table}/query`, body);
279
- return result.records.map((record) => this.client.initObject(this.table, record));
396
+ const response = yield this.request('POST', `/tables/${this.table}/query`, body);
397
+ if (!response) {
398
+ throw new Error("The server didn't return any data for the query");
399
+ }
400
+ const { meta, records: objects } = response;
401
+ const records = objects.map((record) => this.client.initObject(this.table, record));
402
+ return new Page(query, meta, records);
280
403
  });
281
404
  }
282
405
  }
@@ -335,7 +458,7 @@ class BaseClient {
335
458
  return o;
336
459
  }
337
460
  getBranch() {
338
- var e_1, _a;
461
+ var e_2, _a;
339
462
  return __awaiter(this, void 0, void 0, function* () {
340
463
  if (this.branch)
341
464
  return this.branch;
@@ -354,12 +477,12 @@ class BaseClient {
354
477
  }
355
478
  }
356
479
  }
357
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
480
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
358
481
  finally {
359
482
  try {
360
483
  if (strategies_1_1 && !strategies_1_1.done && (_a = strategies_1.return)) yield _a.call(strategies_1);
361
484
  }
362
- finally { if (e_1) throw e_1.error; }
485
+ finally { if (e_2) throw e_2.error; }
363
486
  }
364
487
  throw new Error('Unable to resolve branch value');
365
488
  });
@@ -376,3 +499,12 @@ exports.XataError = XataError;
376
499
  const isBranchStrategyBuilder = (strategy) => {
377
500
  return typeof strategy === 'function';
378
501
  };
502
+ // TODO: We can find a better implementation for links
503
+ const transformObjectLinks = (object) => {
504
+ return Object.entries(object).reduce((acc, [key, value]) => {
505
+ if (value && typeof value === 'object' && typeof value.id === 'string') {
506
+ return Object.assign(Object.assign({}, acc), { [key]: value.id });
507
+ }
508
+ return Object.assign(Object.assign({}, acc), { [key]: value });
509
+ }, {});
510
+ };
@@ -0,0 +1,3 @@
1
+ export declare const errors: {
2
+ falsyFetchImplementation: string; /** @todo add a link after docs exist */
3
+ };
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.errors = void 0;
4
+ exports.errors = {
5
+ falsyFetchImplementation: `The \`fetch\` option passed to the Xata client is resolving to a falsy value and may not be correctly imported.
6
+
7
+ More in the docs:
8
+ ` /** @todo add a link after docs exist */
9
+ };
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@xata.io/client",
3
- "version": "0.0.0-beta.59b45f7",
3
+ "version": "0.0.0-beta.96dc1f0",
4
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": {
8
8
  "test": "echo \"Error: no test specified\" && exit 1",
9
- "build": "tsc",
9
+ "build": "tsc -p tsconfig.build.json",
10
10
  "prepack": "npm run build"
11
11
  },
12
12
  "repository": {
@@ -20,5 +20,5 @@
20
20
  "url": "https://github.com/xataio/client-ts/issues"
21
21
  },
22
22
  "homepage": "https://github.com/xataio/client-ts/blob/main/client/README.md",
23
- "gitHead": "59b45f7d852ef680866524c10280592ba7e00948"
23
+ "gitHead": "96dc1f0ad5f0ec5bfdcb2441657cc07b4ba52759"
24
24
  }
@@ -1 +0,0 @@
1
- export {};