@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.
@@ -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 = [], toViewModel) {
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: toViewModel ? toViewModel(values) : 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 Postgres now serves it on every read, so
5947
- * against that driver this walk finds nothing to do. It stays for the drivers
5948
- * whose own `fetchCollection` still answers with refs: a developer reading
5949
- * through this accessor gets one shape whichever driver is underneath.
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 = () => [], toViewModel) {
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(), toViewModel)),
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(), toViewModel) : void 0;
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(), toViewModel);
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(), toViewModel));
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(), toViewModel);
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(), toViewModel));
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(), toViewModel)),
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(), toViewModel) : void 0),
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), viewModelFor(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 = () => [], toViewModel) {
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(), toViewModel)),
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(), toViewModel) : void 0;
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(), toViewModel);
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(), toViewModel));
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(), toViewModel);
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(), toViewModel)),
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(), toViewModel) : void 0), onError),
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), viewModelFor(slug));
6430
+ accessor = toEntityAccessor(sdkData.collection(slug), slug, () => primaryKeysFor(slug));
6536
6431
  cache.set(slug, accessor);
6537
6432
  }
6538
6433
  return accessor;