@rebasepro/common 0.19.2-canary.g2cd43e9 → 0.19.2-canary.g3502017
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 +12 -2
- package/dist/index.es.js +27 -132
- package/dist/index.es.js.map +1 -1
- package/package.json +3 -3
|
@@ -19,10 +19,20 @@ export interface EntityDataOptions {
|
|
|
19
19
|
*/
|
|
20
20
|
resolveCollection?: (slug: string) => {
|
|
21
21
|
properties?: Record<string, unknown>;
|
|
22
|
-
relations?: unknown[];
|
|
23
|
-
slug?: string;
|
|
24
22
|
} | undefined;
|
|
25
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Build a `RebaseData` object from a `DataDriver` using JavaScript Proxy.
|
|
26
|
+
*
|
|
27
|
+
* This is the key bridge: any property access like `data.products` returns
|
|
28
|
+
* a `CollectionAccessor` backed by the underlying DataDriver, without
|
|
29
|
+
* needing per-collection code generation.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* const data = buildRebaseData(driver);
|
|
33
|
+
* await data.products.create({ name: "Camera", price: 299 });
|
|
34
|
+
* const { data: items } = await data.products.find({ where: { status: ["==", "published"] } });
|
|
35
|
+
*/
|
|
26
36
|
export declare function buildRebaseData(driver: DataDriver, options?: EntityDataOptions): RebaseData;
|
|
27
37
|
/**
|
|
28
38
|
* Wrap a flat {@link RebaseSdkData} into a Entity-shaped {@link RebaseData}.
|
package/dist/index.es.js
CHANGED
|
@@ -5825,89 +5825,6 @@ function createPrimaryKeyResolver(options) {
|
|
|
5825
5825
|
};
|
|
5826
5826
|
}
|
|
5827
5827
|
/**
|
|
5828
|
-
* Build the admin's view model out of the row the wire serves.
|
|
5829
|
-
*
|
|
5830
|
-
* The wire has ONE shape, for every consumer: flat columns, typed the way the
|
|
5831
|
-
* database typed them, and a relation rendered as the target's own columns (or
|
|
5832
|
-
* only its foreign key, when nothing asked for it). That is the REST contract,
|
|
5833
|
-
* what `find()` returns, what `listen()` pushes, and what the generated types
|
|
5834
|
-
* describe.
|
|
5835
|
-
*
|
|
5836
|
-
* The admin renders neither of those directly. Its date field requires a real
|
|
5837
|
-
* `Date` and rejects a string outright; its relation cells read `.data.values`
|
|
5838
|
-
* off a relation ref. Those requirements are the *admin's*, so they are met
|
|
5839
|
-
* here — in the browser, from the collection config the panel already has —
|
|
5840
|
-
* rather than by asking the server for a second wire shape.
|
|
5841
|
-
*
|
|
5842
|
-
* That second shape is what this replaces. Until 2026-09-09 the realtime wire
|
|
5843
|
-
* carried the view model and every other read carried flat rows, so `find()`
|
|
5844
|
-
* and `listen()` answered one query two ways; unifying the wire without doing
|
|
5845
|
-
* this conversion is what left every date cell reading "Invalid date value"
|
|
5846
|
-
* and every relation cell "Unexpected value".
|
|
5847
|
-
*
|
|
5848
|
-
* Values already in view-model form pass through untouched: a driver that
|
|
5849
|
-
* still sends `{ __type: "date" }` or a relation ref (the client revives both)
|
|
5850
|
-
* is served by the same walk.
|
|
5851
|
-
*/
|
|
5852
|
-
function toViewModelValues(values, properties, collection, resolveCollection) {
|
|
5853
|
-
if (!properties) return values;
|
|
5854
|
-
const relations = collection ? resolveCollectionRelations(collection) : {};
|
|
5855
|
-
let out;
|
|
5856
|
-
const write = (key, value) => {
|
|
5857
|
-
out = out ?? { ...values };
|
|
5858
|
-
out[key] = value;
|
|
5859
|
-
};
|
|
5860
|
-
for (const [key, rawProperty] of Object.entries(properties)) {
|
|
5861
|
-
const property = rawProperty;
|
|
5862
|
-
if (!property) continue;
|
|
5863
|
-
if (!(key in values)) {
|
|
5864
|
-
const fkRelation = relations[key];
|
|
5865
|
-
const column = fkRelation && "localKey" in fkRelation ? fkRelation.localKey : void 0;
|
|
5866
|
-
const fk = column !== void 0 ? values[column] ?? values[toWireKey(column)] : void 0;
|
|
5867
|
-
const fkTarget = fkRelation?.targetSlug;
|
|
5868
|
-
if (fkTarget && (typeof fk === "string" || typeof fk === "number")) write(key, new EntityRelation(fk, fkTarget));
|
|
5869
|
-
continue;
|
|
5870
|
-
}
|
|
5871
|
-
const value = values[key];
|
|
5872
|
-
if (value === null || value === void 0) continue;
|
|
5873
|
-
const relation = relations[key];
|
|
5874
|
-
if (relation && (property.type === "relation" || property.of?.type === "relation" || property.type === "array")) {
|
|
5875
|
-
const target = relation.targetSlug;
|
|
5876
|
-
if (!target) continue;
|
|
5877
|
-
const targetProperties = resolveCollection?.(target)?.properties;
|
|
5878
|
-
const targetCollection = resolveCollection?.(target);
|
|
5879
|
-
const toRef = (item) => {
|
|
5880
|
-
if (item instanceof EntityRelation) return item;
|
|
5881
|
-
if (typeof item === "object" && item !== null && "__type" in item) return item;
|
|
5882
|
-
if (typeof item === "object" && item !== null) {
|
|
5883
|
-
const row = item;
|
|
5884
|
-
const keys = targetCollection ? resolvePrimaryKeys(targetCollection) : [];
|
|
5885
|
-
const id = keys.length > 0 ? buildCompositeId(row, keys) : row.id;
|
|
5886
|
-
if (id === void 0 || id === null || id === "") return item;
|
|
5887
|
-
return new EntityRelation(id, target, {
|
|
5888
|
-
id,
|
|
5889
|
-
path: target,
|
|
5890
|
-
values: toViewModelValues(row, targetProperties, targetCollection, resolveCollection)
|
|
5891
|
-
});
|
|
5892
|
-
}
|
|
5893
|
-
if (typeof item === "string" || typeof item === "number") return new EntityRelation(item, target);
|
|
5894
|
-
return item;
|
|
5895
|
-
};
|
|
5896
|
-
write(key, Array.isArray(value) ? value.map(toRef) : toRef(value));
|
|
5897
|
-
continue;
|
|
5898
|
-
}
|
|
5899
|
-
if (property.type === "date" && !(value instanceof Date)) {
|
|
5900
|
-
if (typeof value === "string" || typeof value === "number") {
|
|
5901
|
-
const date = new Date(value);
|
|
5902
|
-
write(key, isNaN(date.getTime()) ? null : date);
|
|
5903
|
-
}
|
|
5904
|
-
continue;
|
|
5905
|
-
}
|
|
5906
|
-
if (property.type === "map" && property.properties && typeof value === "object" && !Array.isArray(value)) write(key, toViewModelValues(value, property.properties, void 0, resolveCollection));
|
|
5907
|
-
}
|
|
5908
|
-
return out ?? values;
|
|
5909
|
-
}
|
|
5910
|
-
/**
|
|
5911
5828
|
* Give a flat row the Entity view-model the admin renders.
|
|
5912
5829
|
*
|
|
5913
5830
|
* The address is *derived here* — it is not a column, and the row it came from
|
|
@@ -5918,12 +5835,12 @@ function toViewModelValues(values, properties, collection, resolveCollection) {
|
|
|
5918
5835
|
* `primaryKeys` empty falls back to a literal `id` on the row: drivers other
|
|
5919
5836
|
* than postgres still serve rows with one, and this keeps them working.
|
|
5920
5837
|
*/
|
|
5921
|
-
function rowToEntity(row, slug, primaryKeys = []
|
|
5838
|
+
function rowToEntity(row, slug, primaryKeys = []) {
|
|
5922
5839
|
const { _matches, ...values } = row;
|
|
5923
5840
|
return {
|
|
5924
5841
|
id: primaryKeys.length > 0 ? buildCompositeId(row, primaryKeys) : row.id,
|
|
5925
5842
|
path: slug,
|
|
5926
|
-
values
|
|
5843
|
+
values,
|
|
5927
5844
|
..._matches ? { searchMatches: _matches } : {}
|
|
5928
5845
|
};
|
|
5929
5846
|
}
|
|
@@ -5943,16 +5860,14 @@ function inlineEnvelope(envelope) {
|
|
|
5943
5860
|
* Replace every relation envelope on a row with the target's flat columns.
|
|
5944
5861
|
*
|
|
5945
5862
|
* The SDK serves one relation shape — the inlined one (see
|
|
5946
|
-
* {@link RestFetchService}) — and
|
|
5947
|
-
*
|
|
5948
|
-
*
|
|
5949
|
-
*
|
|
5863
|
+
* {@link RestFetchService}) — and reads that come back through a *driver*
|
|
5864
|
+
* method rather than the REST pipeline still carry envelopes. Realtime is the
|
|
5865
|
+
* one such read left: there is no `listenForRest`, so the rows arrive shaped
|
|
5866
|
+
* for the admin and are flattened here instead.
|
|
5950
5867
|
*
|
|
5951
5868
|
* Only applied where the REST pipeline is the contract (see `find`); a driver
|
|
5952
|
-
* without a `restFetchService` keeps whatever it returns
|
|
5953
|
-
*
|
|
5954
|
-
* Note this is NOT how the admin gets its view model — that is built in the
|
|
5955
|
-
* browser by {@link toViewModelValues}, from the same flat row.
|
|
5869
|
+
* without a `restFetchService` keeps whatever it returns, so the admin's own
|
|
5870
|
+
* path through {@link buildRebaseData} is untouched.
|
|
5956
5871
|
*/
|
|
5957
5872
|
function inlineRelationRefs(row) {
|
|
5958
5873
|
let out;
|
|
@@ -5965,7 +5880,7 @@ function inlineRelationRefs(row) {
|
|
|
5965
5880
|
}
|
|
5966
5881
|
return out ?? row;
|
|
5967
5882
|
}
|
|
5968
|
-
function createDriverAccessor(driver, slug, getPks = () => []
|
|
5883
|
+
function createDriverAccessor(driver, slug, getPks = () => []) {
|
|
5969
5884
|
const accessor = {
|
|
5970
5885
|
async find(params) {
|
|
5971
5886
|
const filter = params?.where ? deserializeFilter(params.where) : void 0;
|
|
@@ -6014,7 +5929,7 @@ function createDriverAccessor(driver, slug, getPks = () => [], toViewModel) {
|
|
|
6014
5929
|
const last = rows[rows.length - 1];
|
|
6015
5930
|
const nextCursor = hasMore && last && driver.restFetchService?.cursorFor ? driver.restFetchService.cursorFor(slug, last, orderBy) : void 0;
|
|
6016
5931
|
return {
|
|
6017
|
-
data: rows.map((row) => rowToEntity(row, slug, getPks()
|
|
5932
|
+
data: rows.map((row) => rowToEntity(row, slug, getPks())),
|
|
6018
5933
|
meta: {
|
|
6019
5934
|
total,
|
|
6020
5935
|
limit,
|
|
@@ -6030,7 +5945,7 @@ function createDriverAccessor(driver, slug, getPks = () => [], toViewModel) {
|
|
|
6030
5945
|
path: slug,
|
|
6031
5946
|
id
|
|
6032
5947
|
});
|
|
6033
|
-
return row ? rowToEntity(row, slug, getPks()
|
|
5948
|
+
return row ? rowToEntity(row, slug, getPks()) : void 0;
|
|
6034
5949
|
},
|
|
6035
5950
|
aggregate: driver.restFetchService?.aggregate ? async (params) => driver.restFetchService.aggregate(slug, {
|
|
6036
5951
|
aggregates: params.select.map(toDriverAggregate),
|
|
@@ -6046,7 +5961,7 @@ function createDriverAccessor(driver, slug, getPks = () => [], toViewModel) {
|
|
|
6046
5961
|
values: data,
|
|
6047
5962
|
id,
|
|
6048
5963
|
status: "new"
|
|
6049
|
-
}), slug, getPks()
|
|
5964
|
+
}), slug, getPks());
|
|
6050
5965
|
},
|
|
6051
5966
|
createMany: driver.saveMany ? async (data, options) => {
|
|
6052
5967
|
return (await driver.saveMany({
|
|
@@ -6054,7 +5969,7 @@ function createDriverAccessor(driver, slug, getPks = () => [], toViewModel) {
|
|
|
6054
5969
|
rows: data,
|
|
6055
5970
|
upsert: options?.upsert,
|
|
6056
5971
|
onConflict: options?.onConflict
|
|
6057
|
-
})).map((row) => rowToEntity(row, slug, getPks()
|
|
5972
|
+
})).map((row) => rowToEntity(row, slug, getPks()));
|
|
6058
5973
|
} : void 0,
|
|
6059
5974
|
async update(id, data) {
|
|
6060
5975
|
return rowToEntity(await driver.save({
|
|
@@ -6062,7 +5977,7 @@ function createDriverAccessor(driver, slug, getPks = () => [], toViewModel) {
|
|
|
6062
5977
|
values: data,
|
|
6063
5978
|
id,
|
|
6064
5979
|
status: "existing"
|
|
6065
|
-
}), slug, getPks()
|
|
5980
|
+
}), slug, getPks());
|
|
6066
5981
|
},
|
|
6067
5982
|
async delete(id) {
|
|
6068
5983
|
return driver.delete({ row: {
|
|
@@ -6078,7 +5993,7 @@ function createDriverAccessor(driver, slug, getPks = () => [], toViewModel) {
|
|
|
6078
5993
|
id: u.id,
|
|
6079
5994
|
values: u.data
|
|
6080
5995
|
}))
|
|
6081
|
-
})).map((row) => rowToEntity(row, slug, getPks()
|
|
5996
|
+
})).map((row) => rowToEntity(row, slug, getPks()));
|
|
6082
5997
|
} : void 0,
|
|
6083
5998
|
deleteMany: driver.deleteMany ? async (ids) => {
|
|
6084
5999
|
await driver.deleteMany({
|
|
@@ -6110,7 +6025,7 @@ function createDriverAccessor(driver, slug, getPks = () => [], toViewModel) {
|
|
|
6110
6025
|
vectorSearch: params?.vectorSearch,
|
|
6111
6026
|
onUpdate: (entities) => {
|
|
6112
6027
|
onUpdate({
|
|
6113
|
-
data: entities.map((row) => rowToEntity(normalize(row), slug, getPks()
|
|
6028
|
+
data: entities.map((row) => rowToEntity(normalize(row), slug, getPks())),
|
|
6114
6029
|
meta: {
|
|
6115
6030
|
total: offset + entities.length,
|
|
6116
6031
|
limit,
|
|
@@ -6127,7 +6042,7 @@ function createDriverAccessor(driver, slug, getPks = () => [], toViewModel) {
|
|
|
6127
6042
|
return driver.listenOne({
|
|
6128
6043
|
path: slug,
|
|
6129
6044
|
id,
|
|
6130
|
-
onUpdate: (entity) => onUpdate(entity ? rowToEntity(normalize(entity), slug, getPks()
|
|
6045
|
+
onUpdate: (entity) => onUpdate(entity ? rowToEntity(normalize(entity), slug, getPks()) : void 0),
|
|
6131
6046
|
onError
|
|
6132
6047
|
});
|
|
6133
6048
|
} : void 0,
|
|
@@ -6169,32 +6084,13 @@ function createDriverAccessor(driver, slug, getPks = () => [], toViewModel) {
|
|
|
6169
6084
|
* await data.products.create({ name: "Camera", price: 299 });
|
|
6170
6085
|
* const { data: items } = await data.products.find({ where: { status: ["==", "published"] } });
|
|
6171
6086
|
*/
|
|
6172
|
-
/**
|
|
6173
|
-
* The view-model converter for one collection, or `undefined` when there is no
|
|
6174
|
-
* collection config to build it from.
|
|
6175
|
-
*
|
|
6176
|
-
* Absent is the honest answer for every consumer that is not the admin: the
|
|
6177
|
-
* flat SDK derives itself from this same layer (`buildSdkData`) and must keep
|
|
6178
|
-
* the wire's own types, and it registers no collection resolver.
|
|
6179
|
-
*/
|
|
6180
|
-
function createViewModelConverter(options) {
|
|
6181
|
-
if (!options?.resolveCollection) return () => void 0;
|
|
6182
|
-
return function converterFor(slug) {
|
|
6183
|
-
return (values) => {
|
|
6184
|
-
const collection = options.resolveCollection?.(slug);
|
|
6185
|
-
if (!collection) return values;
|
|
6186
|
-
return toViewModelValues(values, collection.properties, collection, options.resolveCollection);
|
|
6187
|
-
};
|
|
6188
|
-
};
|
|
6189
|
-
}
|
|
6190
6087
|
function buildRebaseData(driver, options) {
|
|
6191
6088
|
const cache = /* @__PURE__ */ new Map();
|
|
6192
6089
|
const primaryKeysFor = createPrimaryKeyResolver(options);
|
|
6193
|
-
const viewModelFor = createViewModelConverter(options);
|
|
6194
6090
|
function getAccessor(slug) {
|
|
6195
6091
|
let accessor = cache.get(slug);
|
|
6196
6092
|
if (!accessor) {
|
|
6197
|
-
accessor = createDriverAccessor(driver, slug, () => primaryKeysFor(slug)
|
|
6093
|
+
accessor = createDriverAccessor(driver, slug, () => primaryKeysFor(slug));
|
|
6198
6094
|
cache.set(slug, accessor);
|
|
6199
6095
|
}
|
|
6200
6096
|
return accessor;
|
|
@@ -6459,29 +6355,29 @@ function toSdkCollectionClient(snap, slug = "collection") {
|
|
|
6459
6355
|
* {@link CollectionAccessor}. Every returned row is re-wrapped into the
|
|
6460
6356
|
* `{ id, path, values }` view-model the admin panel renders.
|
|
6461
6357
|
*/
|
|
6462
|
-
function toEntityAccessor(sdk, slug, getPks = () => []
|
|
6358
|
+
function toEntityAccessor(sdk, slug, getPks = () => []) {
|
|
6463
6359
|
const accessor = {
|
|
6464
6360
|
async find(params) {
|
|
6465
6361
|
const res = await sdk.find(params);
|
|
6466
6362
|
return {
|
|
6467
|
-
data: res.data.map((row) => rowToEntity(row, slug, getPks()
|
|
6363
|
+
data: res.data.map((row) => rowToEntity(row, slug, getPks())),
|
|
6468
6364
|
meta: res.meta
|
|
6469
6365
|
};
|
|
6470
6366
|
},
|
|
6471
6367
|
async findById(id) {
|
|
6472
6368
|
const row = await sdk.findById(id);
|
|
6473
|
-
return row ? rowToEntity(row, slug, getPks()
|
|
6369
|
+
return row ? rowToEntity(row, slug, getPks()) : void 0;
|
|
6474
6370
|
},
|
|
6475
6371
|
async create(data, id) {
|
|
6476
|
-
return rowToEntity(await sdk.create(data, id), slug, getPks()
|
|
6372
|
+
return rowToEntity(await sdk.create(data, id), slug, getPks());
|
|
6477
6373
|
},
|
|
6478
6374
|
createMany: sdk.createMany ? async (data, options) => {
|
|
6479
|
-
return (await sdk.createMany(data, options)).map((row) => rowToEntity(row, slug, getPks()
|
|
6375
|
+
return (await sdk.createMany(data, options)).map((row) => rowToEntity(row, slug, getPks()));
|
|
6480
6376
|
} : void 0,
|
|
6481
6377
|
async update(id, data) {
|
|
6482
6378
|
const row = await sdk.update(id, data);
|
|
6483
6379
|
if (!row) throw new Error(`Update returned no data for id ${id}`);
|
|
6484
|
-
return rowToEntity(row, slug, getPks()
|
|
6380
|
+
return rowToEntity(row, slug, getPks());
|
|
6485
6381
|
},
|
|
6486
6382
|
delete(id) {
|
|
6487
6383
|
return sdk.delete(id);
|
|
@@ -6489,10 +6385,10 @@ function toEntityAccessor(sdk, slug, getPks = () => [], toViewModel) {
|
|
|
6489
6385
|
count: isUnsupported(sdk.count) ? void 0 : (params) => sdk.count(params),
|
|
6490
6386
|
aggregate: isUnsupported(sdk.aggregate) ? void 0 : (params) => sdk.aggregate(params),
|
|
6491
6387
|
listen: isUnsupported(sdk.listen) ? void 0 : (params, onUpdate, onError) => sdk.listen(params, (res) => onUpdate({
|
|
6492
|
-
data: res.data.map((row) => rowToEntity(row, slug, getPks()
|
|
6388
|
+
data: res.data.map((row) => rowToEntity(row, slug, getPks())),
|
|
6493
6389
|
meta: res.meta
|
|
6494
6390
|
}), onError),
|
|
6495
|
-
listenById: isUnsupported(sdk.listenById) ? void 0 : (id, onUpdate, onError) => sdk.listenById(id, (row) => onUpdate(row ? rowToEntity(row, slug, getPks()
|
|
6391
|
+
listenById: isUnsupported(sdk.listenById) ? void 0 : (id, onUpdate, onError) => sdk.listenById(id, (row) => onUpdate(row ? rowToEntity(row, slug, getPks()) : void 0), onError),
|
|
6496
6392
|
where(columnOrCondition, operator, value) {
|
|
6497
6393
|
const builder = new QueryBuilder(accessor);
|
|
6498
6394
|
if (typeof columnOrCondition === "object") return builder.where(columnOrCondition);
|
|
@@ -6528,11 +6424,10 @@ function toEntityAccessor(sdk, slug, getPks = () => [], toViewModel) {
|
|
|
6528
6424
|
function wrapAsEntityData(sdkData, options) {
|
|
6529
6425
|
const cache = /* @__PURE__ */ new Map();
|
|
6530
6426
|
const primaryKeysFor = createPrimaryKeyResolver(options);
|
|
6531
|
-
const viewModelFor = createViewModelConverter(options);
|
|
6532
6427
|
function getAccessor(slug) {
|
|
6533
6428
|
let accessor = cache.get(slug);
|
|
6534
6429
|
if (!accessor) {
|
|
6535
|
-
accessor = toEntityAccessor(sdkData.collection(slug), slug, () => primaryKeysFor(slug)
|
|
6430
|
+
accessor = toEntityAccessor(sdkData.collection(slug), slug, () => primaryKeysFor(slug));
|
|
6536
6431
|
cache.set(slug, accessor);
|
|
6537
6432
|
}
|
|
6538
6433
|
return accessor;
|