@rebasepro/types 0.20.1-canary.g4d882ca → 0.21.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.
@@ -3,6 +3,25 @@ import type { StorageSource } from "./controllers/storage.js";
3
3
  import type { RebaseClient } from "./controllers/client.js";
4
4
  import type { RebaseSdkData } from "./controllers/data.js";
5
5
  import type { User } from "./users/index.js";
6
+ /**
7
+ * The client a collection callback is handed as `context.client`: the Rebase
8
+ * client **without `data`**.
9
+ *
10
+ * A callback's queries go through {@link RebaseCallContext.data}. This client
11
+ * is for everything else — functions, storage, email, and `dataAsAdmin` when a
12
+ * callback deliberately needs an admin's reach.
13
+ *
14
+ * `data` is left off because server-side there is none. The object is the
15
+ * `rebase` singleton, which omits `data` so that its admin-scoped plane has
16
+ * exactly one name. This used to be declared as the full `RebaseClient`, so
17
+ * `context.client.data.collection(…)` compiled and then threw "Cannot read
18
+ * properties of undefined" in production. In the browser the panel's client
19
+ * does carry a `data`, but `context.data` is the accessor there too: one name
20
+ * for a callback's queries on both sides of the wire.
21
+ *
22
+ * @group Hooks and utilities
23
+ */
24
+ export type RebaseCallbackClient<DB = unknown> = Omit<RebaseClient<DB>, "data">;
6
25
  /**
7
26
  * Context that is provided to entity callbacks (hooks).
8
27
  * It contains only the dependencies that are available in both the frontend and the backend.
@@ -51,6 +70,8 @@ export type RebaseCallContext<USER extends User = User> = {
51
70
  * user-scoped operation to admin. For queries in a callback use
52
71
  * {@link data}; come here for functions, storage and email.
53
72
  *
73
+ * There is no `context.client.data`: see {@link RebaseCallbackClient}.
74
+ *
54
75
  * @example
55
76
  * // In a beforeSave callback:
56
77
  * const result = await context.client.functions.invoke('my-function', { ... });
@@ -60,7 +81,7 @@ export type RebaseCallContext<USER extends User = User> = {
60
81
  * const { client } = props.context;
61
82
  * const result = await client.functions.invoke('extract-job', { url });
62
83
  */
63
- client: RebaseClient;
84
+ client: RebaseCallbackClient;
64
85
  /**
65
86
  * Unified data access — `context.data.products.create(...)`.
66
87
  * Access any collection as a dynamic property.
@@ -107,7 +128,7 @@ export type RebaseCallContext<USER extends User = User> = {
107
128
  * practice — the backend has always passed it, and the callbacks guide
108
129
  * documented `context.driver.withAuth(user)` in all six locales. The
109
130
  * contract simply did not name it, so `buildCallContext` was cast through
110
- * `as unknown as RebaseCallContext` and nothing about the object was
131
+ * `as RebaseCallContext` and nothing about the object was
111
132
  * type-checked at all.
112
133
  *
113
134
  * The guide no longer recommends `withAuth` — {@link data} is already
@@ -279,6 +279,25 @@ export interface DeleteManyProps<M extends Record<string, unknown> = Record<stri
279
279
  /** See {@link DeleteProps.hard}. */
280
280
  hard?: boolean;
281
281
  }
282
+ /**
283
+ * Addressing ONE link of a many-to-many, to set the columns it carries.
284
+ *
285
+ * `path` is the relation on a row — `posts/1/tags` — and `targetId` the row on
286
+ * the far side, so the pair names exactly one junction row. Not a `SaveProps`,
287
+ * because a save at that address means "write the target row", and a link's own
288
+ * columns are not the target's: two posts sharing a tag see one tag and two
289
+ * different links.
290
+ *
291
+ * @internal
292
+ */
293
+ export interface UpdateRelationPivotProps {
294
+ /** The nested relation path, e.g. `posts/1/tags`. */
295
+ path: string;
296
+ /** The far side's key. */
297
+ targetId: string | number;
298
+ /** The junction columns to set, keyed by the property key `through.properties` declares. */
299
+ pivot: Record<string, unknown>;
300
+ }
282
301
  /**
283
302
  * One operation of a {@link DataDriver.batchWrite}.
284
303
  *
@@ -429,6 +448,20 @@ export interface DataDriver {
429
448
  * @param path Collection path
430
449
  */
431
450
  deleteAll?(path: string): Promise<void>;
451
+ /**
452
+ * Set the columns ONE many-to-many link carries, leaving the membership
453
+ * alone — `manyToMany`'s `through.properties`.
454
+ *
455
+ * `PATCH /api/data/<c>/<id>/<relation>/<targetId>` with a `_pivot` body
456
+ * reaches this. The membership array cannot express it: sending one element
457
+ * would unlink everything else, and re-sending the whole set to change one
458
+ * value reintroduces the lost update the membership diff exists to avoid.
459
+ *
460
+ * Optional. A driver whose junctions carry nothing but the two keys leaves
461
+ * it undefined, and the REST layer answers `RELATION_PIVOT_UNSUPPORTED`
462
+ * rather than pretending the write landed.
463
+ */
464
+ updateRelationPivot?(props: UpdateRelationPivotProps): Promise<void>;
432
465
  /**
433
466
  * Delete many rows in one transaction, addressed by id.
434
467
  *
package/dist/errors.d.ts CHANGED
@@ -132,21 +132,6 @@ export declare class RebaseClientError extends RebaseApiError {
132
132
  */
133
133
  constructor(message: string, init?: RebaseErrorInit);
134
134
  }
135
- /**
136
- * Build the stub a client installs for a contract method it cannot serve.
137
- *
138
- * `listen`, `listenById` and `count` are part of `SDKCollectionClient`, not
139
- * optional extras — a caller should be able to write
140
- * `client.data.posts.count()` without asking first, and a transport that cannot
141
- * serve it should answer with a sentence naming the configuration that would,
142
- * rather than with `undefined is not a function` at the call site. Where the
143
- * transport genuinely cannot (a client built with `realtime: false`, a driver
144
- * with no `listenCollection`), it installs one of these instead of omitting the
145
- * method.
146
- *
147
- * @param message What to tell the caller, naming the fix.
148
- * @group Errors
149
- */
150
135
  export declare function unsupportedMethod<F>(message: string): F;
151
136
  /**
152
137
  * Can this method actually do anything?
package/dist/index.es.js CHANGED
@@ -81,21 +81,6 @@ var RebaseClientError = class extends RebaseApiError {
81
81
  * agree about it, and a module-local symbol would not.
82
82
  */
83
83
  var UNSUPPORTED_METHOD = Symbol.for("rebase.unsupportedMethod");
84
- /**
85
- * Build the stub a client installs for a contract method it cannot serve.
86
- *
87
- * `listen`, `listenById` and `count` are part of `SDKCollectionClient`, not
88
- * optional extras — a caller should be able to write
89
- * `client.data.posts.count()` without asking first, and a transport that cannot
90
- * serve it should answer with a sentence naming the configuration that would,
91
- * rather than with `undefined is not a function` at the call site. Where the
92
- * transport genuinely cannot (a client built with `realtime: false`, a driver
93
- * with no `listenCollection`), it installs one of these instead of omitting the
94
- * method.
95
- *
96
- * @param message What to tell the caller, naming the fix.
97
- * @group Errors
98
- */
99
84
  function unsupportedMethod(message) {
100
85
  const stub = () => {
101
86
  throw new RebaseClientError(message, { code: "REALTIME_DISABLED" });