@rebasepro/common 0.28.2 → 0.29.0
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/data/buildRebaseData.d.ts +15 -7
- package/dist/data/buildRebaseData.es.js +86 -33
- package/dist/data/buildRebaseData.es.js.map +1 -1
- package/dist/data/buildRoutedRebaseData.d.ts +4 -4
- package/dist/data/buildRoutedRebaseData.es.js.map +1 -1
- package/dist/data/filter-dialect.d.ts +7 -1
- package/dist/data/filter-dialect.es.js +108 -7
- package/dist/data/filter-dialect.es.js.map +1 -1
- package/dist/index.es.js +2 -2
- package/dist/util/permissions.es.js +9 -0
- package/dist/util/permissions.es.js.map +1 -1
- package/dist/util/relations.d.ts +3 -1
- package/dist/util/relations.es.js.map +1 -1
- package/dist/util/resolutions.es.js +5 -2
- package/dist/util/resolutions.es.js.map +1 -1
- package/dist/util/storage.es.js +3 -3
- package/dist/util/storage.es.js.map +1 -1
- package/package.json +3 -3
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import { DataDriver, RebaseData, RebaseSdkData } from "@rebasepro/types";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* `sum(total)` → `sum_total`, `count()` → `count`. Written once, here, because
|
|
6
|
-
* the REST parser derives the same alias from `?select=sum(total)` and the two
|
|
7
|
-
* have to agree — a caller reading `row.sum_total` off an SDK result and off an
|
|
8
|
-
* HTTP response is reading the same key or the SDK is broken.
|
|
3
|
+
* The key an aggregate comes back under — `@rebasepro/types`' derivation,
|
|
4
|
+
* which the REST parser and the HTTP client use too.
|
|
9
5
|
*/
|
|
10
|
-
export
|
|
6
|
+
export { aggregateAlias } from "@rebasepro/types";
|
|
11
7
|
export interface EntityDataOptions {
|
|
12
8
|
/**
|
|
13
9
|
* Look up a collection's config by slug, to derive row addresses from its
|
|
@@ -45,6 +41,18 @@ export interface EntityDataOptions {
|
|
|
45
41
|
*/
|
|
46
42
|
resolveAccessor?: (property: string) => string;
|
|
47
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* A row as the server sends it, in the admin's view model of its collection:
|
|
46
|
+
* the `values` `buildRebaseData` and `wrapAsEntityData` give an `Entity` of
|
|
47
|
+
* that collection — a date as a `Date`, a relation as an `EntityRelation`,
|
|
48
|
+
* also under its property's key where the row carries only the foreign key —
|
|
49
|
+
* and a `reference` property's id as an `EntityReference`. A collection the
|
|
50
|
+
* resolver does not know leaves the row as it is.
|
|
51
|
+
*
|
|
52
|
+
* For code that holds a flat row and hands it to what expects the admin's
|
|
53
|
+
* values, such as a collection's `browserCallbacks` or a preview.
|
|
54
|
+
*/
|
|
55
|
+
export declare function rowToViewModel(row: Record<string, unknown>, slug: string, resolveCollection: NonNullable<EntityDataOptions["resolveCollection"]>): Record<string, unknown>;
|
|
48
56
|
export declare function buildRebaseData(driver: DataDriver, options?: EntityDataOptions): RebaseData;
|
|
49
57
|
/**
|
|
50
58
|
* Wrap a flat {@link RebaseSdkData} into a Entity-shaped {@link RebaseData}.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { buildCompositeId, resolvePrimaryKeys } from "../util/identity.es.js";
|
|
2
|
-
import { resolveCollectionRelations } from "../util/relations.es.js";
|
|
2
|
+
import { fieldKeyForColumn, resolveCollectionRelations } from "../util/relations.es.js";
|
|
3
3
|
import { cursorToStartAfter, decodeCursor, reconcileCursorOrder } from "./cursor.es.js";
|
|
4
4
|
import { normalizeOrderBy } from "./sort-dialect.es.js";
|
|
5
5
|
import { mergeIncludeSpecs } from "./include-spec.es.js";
|
|
@@ -7,7 +7,7 @@ import { etagOf } from "./row-etag.es.js";
|
|
|
7
7
|
import { QueryBuilder } from "./query_builder.es.js";
|
|
8
8
|
import { collectAllPages, paginateFind, resolveFindWindow } from "./paginate.es.js";
|
|
9
9
|
import { deserializeFilter } from "./filter-dialect.es.js";
|
|
10
|
-
import { DEFAULT_VECTOR_LIST_LIMIT, EntityRelation, RebaseApiError, RebaseClientError, isUnsupported, sortKeyToString, unsupportedMethod } from "@rebasepro/types";
|
|
10
|
+
import { DEFAULT_VECTOR_LIST_LIMIT, EntityReference, EntityRelation, RebaseApiError, RebaseClientError, aggregateAlias, isUnsupported, sortKeyToString, toDriverAggregate, toDriverAggregateGroup, unsupportedMethod } from "@rebasepro/types";
|
|
11
11
|
import { toSnakeCase, toWireKey } from "@rebasepro/utils";
|
|
12
12
|
//#region src/data/buildRebaseData.ts
|
|
13
13
|
/**
|
|
@@ -20,25 +20,6 @@ var noRealtime = (slug) => `Realtime is not available for "${slug}": its data so
|
|
|
20
20
|
/** What a client says when its data source cannot count. */
|
|
21
21
|
var noCount = (slug) => `Counting is not available for "${slug}": its data source does not support it.`;
|
|
22
22
|
/**
|
|
23
|
-
* Derive the response key an aggregate comes back under.
|
|
24
|
-
*
|
|
25
|
-
* `sum(total)` → `sum_total`, `count()` → `count`. Written once, here, because
|
|
26
|
-
* the REST parser derives the same alias from `?select=sum(total)` and the two
|
|
27
|
-
* have to agree — a caller reading `row.sum_total` off an SDK result and off an
|
|
28
|
-
* HTTP response is reading the same key or the SDK is broken.
|
|
29
|
-
*/
|
|
30
|
-
function aggregateAlias(fn, field) {
|
|
31
|
-
return field ? `${fn}_${field}` : fn;
|
|
32
|
-
}
|
|
33
|
-
function toDriverAggregate(select) {
|
|
34
|
-
const field = select.field;
|
|
35
|
-
return {
|
|
36
|
-
fn: select.fn,
|
|
37
|
-
field,
|
|
38
|
-
alias: aggregateAlias(select.fn, field)
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
23
|
* What a client says when its data source cannot aggregate.
|
|
43
24
|
*
|
|
44
25
|
* A stub rather than a fallback that fetches and reduces in JavaScript: that
|
|
@@ -50,6 +31,8 @@ var noTrash = (slug) => `The trash is not available for "${slug}": its data sour
|
|
|
50
31
|
/** What `softDelete: true` names the field a delete stamps; a restore clears it. */
|
|
51
32
|
var DEFAULT_SOFT_DELETE_FIELD = "deletedAt";
|
|
52
33
|
var noAggregate = (slug) => `Aggregates are not available for "${slug}": its data source does not implement them.`;
|
|
34
|
+
/** What a client says when its data source has no materialized views to refresh. */
|
|
35
|
+
var noRefresh = (slug) => `"${slug}" cannot be refreshed: its data source has no materialized views.`;
|
|
53
36
|
function createPrimaryKeyResolver(options) {
|
|
54
37
|
const cache = /* @__PURE__ */ new Map();
|
|
55
38
|
const warned = /* @__PURE__ */ new Set();
|
|
@@ -109,7 +92,7 @@ function toViewModelValues(values, properties, collection, resolveCollection) {
|
|
|
109
92
|
if (!(key in values)) {
|
|
110
93
|
const fkRelation = relations[key];
|
|
111
94
|
const column = fkRelation && "localKey" in fkRelation ? fkRelation.localKey : void 0;
|
|
112
|
-
const fk = column !== void 0 ? values[column] ?? values[toWireKey(column)] : void 0;
|
|
95
|
+
const fk = column !== void 0 ? values[fieldKeyForColumn(collection, column)] ?? values[column] ?? values[toWireKey(column)] : void 0;
|
|
113
96
|
const fkTarget = fkRelation?.targetSlug;
|
|
114
97
|
if (fkTarget && (typeof fk === "string" || typeof fk === "number")) write(key, new EntityRelation(fk, fkTarget));
|
|
115
98
|
continue;
|
|
@@ -175,6 +158,10 @@ function rowToEntity(row, slug, primaryKeys = [], toViewModel) {
|
|
|
175
158
|
...etag ? { etag } : {}
|
|
176
159
|
};
|
|
177
160
|
}
|
|
161
|
+
/** {@link rowToEntity} over a page of rows, the primary keys looked up once for all of them. */
|
|
162
|
+
function rowsToEntities(rows, slug, primaryKeys, toViewModel) {
|
|
163
|
+
return rows.map((row) => rowToEntity(row, slug, primaryKeys, toViewModel));
|
|
164
|
+
}
|
|
178
165
|
/**
|
|
179
166
|
* The relation envelope `toFlatRow` writes where a relation was:
|
|
180
167
|
* `{ id, path, __type: "relation", data: { id, path, values } }`. It is the
|
|
@@ -274,14 +261,14 @@ function createDriverAccessor(driver, slug, getPks = () => [], toViewModel, stor
|
|
|
274
261
|
}) : void 0
|
|
275
262
|
};
|
|
276
263
|
};
|
|
277
|
-
const { fetched, counted } = driver.readTogether ? await driver.readTogether(read) : await read(driver);
|
|
264
|
+
const { fetched, counted } = counting && driver.readTogether ? await driver.readTogether(read) : await read(driver);
|
|
278
265
|
const rows = probing ? fetched.slice(0, limit) : fetched;
|
|
279
266
|
const total = counted ?? rows.length + offset;
|
|
280
267
|
const hasMore = probing ? fetched.length > limit : offset + rows.length < total;
|
|
281
268
|
const last = rows[rows.length - 1];
|
|
282
269
|
const nextCursor = hasMore && last && driver.restFetchService?.cursorFor ? driver.restFetchService.cursorFor(storedPath(), last, orderBy) : void 0;
|
|
283
270
|
return {
|
|
284
|
-
data: rows
|
|
271
|
+
data: rowsToEntities(rows, slug, getPks(), toViewModel),
|
|
285
272
|
meta: {
|
|
286
273
|
total,
|
|
287
274
|
limit,
|
|
@@ -319,7 +306,7 @@ function createDriverAccessor(driver, slug, getPks = () => [], toViewModel, stor
|
|
|
319
306
|
const limit = (params.groupBy?.length ?? 0) > 0 ? params.limit : void 0;
|
|
320
307
|
const rows = await driver.restFetchService.aggregate(storedPath(), {
|
|
321
308
|
aggregates: params.select.map(toDriverAggregate),
|
|
322
|
-
groupBy: params.groupBy,
|
|
309
|
+
groupBy: params.groupBy?.map((group) => toDriverAggregateGroup(group)),
|
|
323
310
|
filter: params.where ? deserializeFilter(params.where) : void 0,
|
|
324
311
|
logical: params.logical,
|
|
325
312
|
searchString: params.searchString,
|
|
@@ -340,6 +327,7 @@ function createDriverAccessor(driver, slug, getPks = () => [], toViewModel, stor
|
|
|
340
327
|
});
|
|
341
328
|
return page;
|
|
342
329
|
} : void 0,
|
|
330
|
+
refresh: driver.refreshView ? (options) => driver.refreshView(storedPath(), options) : void 0,
|
|
343
331
|
async create(data, id) {
|
|
344
332
|
return rowToEntity(await driver.save({
|
|
345
333
|
path: storedPath(),
|
|
@@ -349,12 +337,12 @@ function createDriverAccessor(driver, slug, getPks = () => [], toViewModel, stor
|
|
|
349
337
|
}), slug, getPks(), toViewModel);
|
|
350
338
|
},
|
|
351
339
|
createMany: driver.saveMany ? async (data, options) => {
|
|
352
|
-
return (await driver.saveMany({
|
|
340
|
+
return rowsToEntities(await driver.saveMany({
|
|
353
341
|
path: storedPath(),
|
|
354
342
|
rows: data,
|
|
355
343
|
upsert: options?.upsert,
|
|
356
344
|
onConflict: options?.onConflict
|
|
357
|
-
})
|
|
345
|
+
}), slug, getPks(), toViewModel);
|
|
358
346
|
} : void 0,
|
|
359
347
|
async update(id, data) {
|
|
360
348
|
return rowToEntity(await driver.save({
|
|
@@ -371,13 +359,13 @@ function createDriverAccessor(driver, slug, getPks = () => [], toViewModel, stor
|
|
|
371
359
|
} });
|
|
372
360
|
},
|
|
373
361
|
updateMany: driver.updateMany ? async (updates) => {
|
|
374
|
-
return (await driver.updateMany({
|
|
362
|
+
return rowsToEntities(await driver.updateMany({
|
|
375
363
|
path: storedPath(),
|
|
376
364
|
updates: updates.map((u) => ({
|
|
377
365
|
id: u.id,
|
|
378
366
|
values: u.data
|
|
379
367
|
}))
|
|
380
|
-
})
|
|
368
|
+
}), slug, getPks(), toViewModel);
|
|
381
369
|
} : void 0,
|
|
382
370
|
deleteMany: driver.deleteMany ? async (ids) => {
|
|
383
371
|
await driver.deleteMany({
|
|
@@ -497,6 +485,69 @@ function createViewModelConverter(options) {
|
|
|
497
485
|
};
|
|
498
486
|
};
|
|
499
487
|
}
|
|
488
|
+
/**
|
|
489
|
+
* A row as the server sends it, in the admin's view model of its collection:
|
|
490
|
+
* the `values` `buildRebaseData` and `wrapAsEntityData` give an `Entity` of
|
|
491
|
+
* that collection — a date as a `Date`, a relation as an `EntityRelation`,
|
|
492
|
+
* also under its property's key where the row carries only the foreign key —
|
|
493
|
+
* and a `reference` property's id as an `EntityReference`. A collection the
|
|
494
|
+
* resolver does not know leaves the row as it is.
|
|
495
|
+
*
|
|
496
|
+
* For code that holds a flat row and hands it to what expects the admin's
|
|
497
|
+
* values, such as a collection's `browserCallbacks` or a preview.
|
|
498
|
+
*/
|
|
499
|
+
function rowToViewModel(row, slug, resolveCollection) {
|
|
500
|
+
const collection = resolveCollection(slug);
|
|
501
|
+
if (!collection) return row;
|
|
502
|
+
return withReferences(toViewModelValues(row, collection.properties, collection, resolveCollection), collection.properties);
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Each `reference` property's value — the referenced id, or `{ id, path }` —
|
|
506
|
+
* as the `EntityReference` the admin's reference field and preview read; an
|
|
507
|
+
* array of references item by item.
|
|
508
|
+
*/
|
|
509
|
+
function withReferences(values, properties) {
|
|
510
|
+
if (!properties) return values;
|
|
511
|
+
let out;
|
|
512
|
+
for (const [key, property] of Object.entries(properties)) {
|
|
513
|
+
if (!(key in values)) continue;
|
|
514
|
+
const value = values[key];
|
|
515
|
+
const path = referencePath(property);
|
|
516
|
+
const arrayPath = isPlainRecord(property) && property.type === "array" ? referencePath(property.of) : void 0;
|
|
517
|
+
if (path !== void 0) {
|
|
518
|
+
out = out ?? { ...values };
|
|
519
|
+
out[key] = toReference(value, path);
|
|
520
|
+
} else if (arrayPath !== void 0 && Array.isArray(value)) {
|
|
521
|
+
out = out ?? { ...values };
|
|
522
|
+
out[key] = value.map((item) => toReference(item, arrayPath));
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
return out ?? values;
|
|
526
|
+
}
|
|
527
|
+
/** The target path of a `reference` property; `undefined` for any other. */
|
|
528
|
+
function referencePath(property) {
|
|
529
|
+
if (!isPlainRecord(property) || property.type !== "reference") return void 0;
|
|
530
|
+
return typeof property.path === "string" ? property.path : "";
|
|
531
|
+
}
|
|
532
|
+
function toReference(value, path) {
|
|
533
|
+
if (value === null || value === void 0 || value === "") return null;
|
|
534
|
+
if (value instanceof EntityReference) return value;
|
|
535
|
+
if ((typeof value === "string" || typeof value === "number") && path) return new EntityReference({
|
|
536
|
+
id: String(value),
|
|
537
|
+
path
|
|
538
|
+
});
|
|
539
|
+
if (isPlainRecord(value) && (typeof value.id === "string" || typeof value.id === "number")) {
|
|
540
|
+
const target = typeof value.path === "string" ? value.path : path;
|
|
541
|
+
if (target) return new EntityReference({
|
|
542
|
+
id: String(value.id),
|
|
543
|
+
path: target
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
return value;
|
|
547
|
+
}
|
|
548
|
+
function isPlainRecord(value) {
|
|
549
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
550
|
+
}
|
|
500
551
|
function buildRebaseData(driver, options) {
|
|
501
552
|
const cache = /* @__PURE__ */ new Map();
|
|
502
553
|
const primaryKeysFor = createPrimaryKeyResolver(options);
|
|
@@ -797,6 +848,7 @@ function toSdkCollectionClient(snap, slug = "collection") {
|
|
|
797
848
|
distinct: (enabled) => new SdkQueryBuilder(client).distinct(enabled),
|
|
798
849
|
after: (cursor) => new SdkQueryBuilder(client).after(cursor),
|
|
799
850
|
aggregate: snap.aggregate ? (params) => snap.aggregate(params) : unsupportedMethod(noAggregate(slug)),
|
|
851
|
+
refresh: snap.refresh ? (options) => snap.refresh(options) : unsupportedMethod(noRefresh(slug)),
|
|
800
852
|
trash: toSdkTrashClient(snap, slug, () => client)
|
|
801
853
|
};
|
|
802
854
|
return client;
|
|
@@ -865,7 +917,7 @@ function toEntityAccessor(sdk, slug, getPks = () => [], toViewModel, viewModelSo
|
|
|
865
917
|
async find(params) {
|
|
866
918
|
const res = await sdk.find(params);
|
|
867
919
|
return {
|
|
868
|
-
data: res.data
|
|
920
|
+
data: rowsToEntities(res.data, slug, getPks(), toViewModel),
|
|
869
921
|
meta: res.meta
|
|
870
922
|
};
|
|
871
923
|
},
|
|
@@ -877,7 +929,7 @@ function toEntityAccessor(sdk, slug, getPks = () => [], toViewModel, viewModelSo
|
|
|
877
929
|
return rowToEntity(await sdk.create(data, id), slug, getPks(), toViewModel);
|
|
878
930
|
},
|
|
879
931
|
createMany: sdk.createMany ? async (data, options) => {
|
|
880
|
-
return (await sdk.createMany(data, options)
|
|
932
|
+
return rowsToEntities(await sdk.createMany(data, options), slug, getPks(), toViewModel);
|
|
881
933
|
} : void 0,
|
|
882
934
|
async update(id, data, options) {
|
|
883
935
|
const row = options?.ifMatch ? await sdk.update(id, data, { ifMatch: options.ifMatch }) : await sdk.update(id, data);
|
|
@@ -889,6 +941,7 @@ function toEntityAccessor(sdk, slug, getPks = () => [], toViewModel, viewModelSo
|
|
|
889
941
|
},
|
|
890
942
|
count: isUnsupported(sdk.count) ? void 0 : (params) => sdk.count(params),
|
|
891
943
|
aggregate: isUnsupported(sdk.aggregate) ? void 0 : (params) => sdk.aggregate(params),
|
|
944
|
+
refresh: isUnsupported(sdk.refresh) ? void 0 : (options) => sdk.refresh(options),
|
|
892
945
|
listen: isUnsupported(sdk.listen) ? void 0 : (params, onUpdate, onError) => {
|
|
893
946
|
let built = /* @__PURE__ */ new WeakMap();
|
|
894
947
|
let builtWith;
|
|
@@ -918,7 +971,7 @@ function toEntityAccessor(sdk, slug, getPks = () => [], toViewModel, viewModelSo
|
|
|
918
971
|
async find(params) {
|
|
919
972
|
const res = await sdk.trash.find(params);
|
|
920
973
|
return {
|
|
921
|
-
data: res.data
|
|
974
|
+
data: rowsToEntities(res.data, slug, getPks(), toViewModel),
|
|
922
975
|
meta: res.meta
|
|
923
976
|
};
|
|
924
977
|
},
|
|
@@ -1033,6 +1086,6 @@ function buildSdkData(driver, options) {
|
|
|
1033
1086
|
return wrapAsSdkData(buildRebaseData(driver), options);
|
|
1034
1087
|
}
|
|
1035
1088
|
//#endregion
|
|
1036
|
-
export { aggregateAlias, buildRebaseData, buildSdkData, wrapAsEntityData, wrapAsSdkData };
|
|
1089
|
+
export { aggregateAlias, buildRebaseData, buildSdkData, rowToViewModel, wrapAsEntityData, wrapAsSdkData };
|
|
1037
1090
|
|
|
1038
1091
|
//# sourceMappingURL=buildRebaseData.es.js.map
|