@dashai/sdk 0.9.0 → 2.0.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.
Files changed (52) hide show
  1. package/README.md +53 -13
  2. package/dist/auth/client.cjs +23 -1
  3. package/dist/auth/client.cjs.map +1 -1
  4. package/dist/auth/client.d.cts +30 -5
  5. package/dist/auth/client.d.ts +30 -5
  6. package/dist/auth/client.js +23 -1
  7. package/dist/auth/client.js.map +1 -1
  8. package/dist/auth/middleware.cjs +11 -1
  9. package/dist/auth/middleware.cjs.map +1 -1
  10. package/dist/auth/middleware.js +11 -1
  11. package/dist/auth/middleware.js.map +1 -1
  12. package/dist/auth/server.cjs +36 -7
  13. package/dist/auth/server.cjs.map +1 -1
  14. package/dist/auth/server.js +36 -7
  15. package/dist/auth/server.js.map +1 -1
  16. package/dist/auth/types.cjs.map +1 -1
  17. package/dist/auth/types.d.cts +13 -0
  18. package/dist/auth/types.d.ts +13 -0
  19. package/dist/auth/types.js.map +1 -1
  20. package/dist/deps/index.cjs +82 -7
  21. package/dist/deps/index.cjs.map +1 -1
  22. package/dist/deps/index.d.cts +28 -10
  23. package/dist/deps/index.d.ts +28 -10
  24. package/dist/deps/index.js +81 -8
  25. package/dist/deps/index.js.map +1 -1
  26. package/dist/index-BsSFz58g.d.cts +431 -0
  27. package/dist/index-BsSFz58g.d.ts +431 -0
  28. package/dist/index.cjs +318 -25
  29. package/dist/index.cjs.map +1 -1
  30. package/dist/index.d.cts +66 -53
  31. package/dist/index.d.ts +66 -53
  32. package/dist/index.js +313 -26
  33. package/dist/index.js.map +1 -1
  34. package/dist/query/index.cjs +158 -2
  35. package/dist/query/index.cjs.map +1 -1
  36. package/dist/query/index.d.cts +7 -7
  37. package/dist/query/index.d.ts +7 -7
  38. package/dist/query/index.js +158 -2
  39. package/dist/query/index.js.map +1 -1
  40. package/dist/react/index.d.cts +1 -1
  41. package/dist/react/index.d.ts +1 -1
  42. package/dist/{types-DXsbCVkb.d.cts → types-CkAfiS4k.d.cts} +106 -38
  43. package/dist/{types-DXsbCVkb.d.ts → types-CkAfiS4k.d.ts} +106 -38
  44. package/dist/uses/index.cjs +147 -0
  45. package/dist/uses/index.cjs.map +1 -0
  46. package/dist/uses/index.d.cts +1 -0
  47. package/dist/uses/index.d.ts +1 -0
  48. package/dist/uses/index.js +142 -0
  49. package/dist/uses/index.js.map +1 -0
  50. package/package.json +6 -1
  51. package/dist/errors-BV75u7b9.d.cts +0 -207
  52. package/dist/errors-BV75u7b9.d.ts +0 -207
@@ -190,37 +190,43 @@ type FetchImpl = typeof globalThis.fetch;
190
190
  */
191
191
  interface ClientConfig {
192
192
  /**
193
- * Base URL of the DashWise API. No trailing slash; the client will
194
- * append `/api/...` paths. Example: `https://api.dashwise.io`.
193
+ * Base URL of the DashWise API. No trailing slash; the client
194
+ * appends `/api/module-api/...` paths. Example: `https://api.dashwise.io`.
195
+ *
196
+ * Phase 2 wire protocol (contract W2): the generated production/local
197
+ * module bootstrap constructs the client as
198
+ * `createClient({ apiUrl: process.env.DASHWISE_API_URL, apiKey: ... })`.
199
+ * `apiUrl` is the canonical field; when set it takes precedence over
200
+ * `baseUrl`. Exactly one of `apiUrl` / `baseUrl` MUST be provided —
201
+ * the transport throws at construction otherwise.
202
+ */
203
+ apiUrl?: string;
204
+ /**
205
+ * Legacy alias for {@link ClientConfig.apiUrl}. The frozen sandbox
206
+ * generation (E2B builder template) still emits `createClient({ baseUrl, ... })`
207
+ * byte-for-byte, so this alias stays supported indefinitely. New code
208
+ * should prefer `apiUrl`. When both are set, `apiUrl` wins.
195
209
  */
196
- baseUrl: string;
210
+ baseUrl?: string;
197
211
  /**
198
212
  * Token resolver. Called once per request, before the HTTP call.
199
213
  * Resolved tokens are attached as `Authorization: Bearer <token>`.
200
214
  */
201
215
  getToken?: TokenResolver;
202
216
  /**
203
- * Custom Modules Platform v1 — a static `dwk_` API key used as the
204
- * Bearer credential. Convenience over `getToken: () => apiKey` for the
205
- * common "deployed module with one long-lived key" case (set it from
206
- * `process.env.DASHWISE_API_KEY`). Precedence: a non-null `getToken()`
207
- * result wins; otherwise `apiKey` is used. NEVER expose this to the
208
- * browser — keep it server-only (route client data through server
209
- * actions / route handlers).
210
- */
211
- apiKey?: string;
212
- /**
213
- * Installation ID this client is scoped to. Required for module
214
- * runtime calls. Typical values:
215
- * - a numeric `module_installations.id` for production modules
216
- * - `'sandbox'` for AI builder draft loops (no installation row yet)
217
- * - `'local'` for `dashwise module dev` against a dev token
217
+ * A static `dwk_` API key used as the Bearer credential. Convenience
218
+ * over `getToken: () => apiKey` for the common "deployed module with
219
+ * one long-lived key" case (set it from `process.env.DASHWISE_API_KEY`).
220
+ * Precedence: a non-null `getToken()` result wins; otherwise `apiKey`
221
+ * is used. NEVER expose this to the browser — keep it server-only
222
+ * (route client data through server actions / route handlers).
218
223
  *
219
- * Accepts any string-shaped id (or number) so consumers can pass
220
- * `process.env.DASHWISE_INSTALLATION_ID` directly without coercion.
221
- * The value is interpolated into the URL path as-is.
224
+ * Phase 2 wire protocol: the install this client operates on is
225
+ * resolved server-side from this credential (the key's bound install),
226
+ * NOT from any URL segment there is no `installationId` config
227
+ * anymore. All requests hit the install-agnostic `/api/module-api`.
222
228
  */
223
- installationId: number | string;
229
+ apiKey?: string;
224
230
  /**
225
231
  * Optional `fetch` override. Defaults to `globalThis.fetch`. Set this
226
232
  * for tests or runtimes that need a custom HTTP agent.
@@ -258,7 +264,7 @@ interface ClientConfig {
258
264
  *
259
265
  * @example
260
266
  * const client = createClient({
261
- * baseUrl, installationId,
267
+ * baseUrl, apiKey,
262
268
  * slowQueryThresholdMs: 500,
263
269
  * onSlowQuery: (info) => {
264
270
  * sentry.captureMessage('Slow query', { extra: info });
@@ -282,7 +288,7 @@ interface ClientConfig {
282
288
  * Carries enough context for an observability sink to log a useful
283
289
  * message + correlate with the backend's `module_query_log` row via
284
290
  * `requestId`. `path` is the SDK-shaped path (relative to the
285
- * installation-scoped base URL — e.g. `/query` or `/db/tasks/list`);
291
+ * `/api/module-api` base URL — e.g. `/query` or `/db/tasks/list`);
286
292
  * combine with the URL the client is configured against if you want
287
293
  * the full URL.
288
294
  *
@@ -322,7 +328,7 @@ interface Client {
322
328
  * `providerSlug` must be the **module slug** (kebab-case, matches
323
329
  * the manifest's `MODULE_SLUG_REGEX`) of a module listed in the
324
330
  * caller's `module.json#dependencies[]`. Calls hit
325
- * `POST /api/modules/:installation/deps/:providerSlug/db/:tableSlug/list`
331
+ * `POST /api/module-api/deps/:providerSlug/db/:tableSlug/list`
326
332
  * and friends; backend enforces field projection + operation gating
327
333
  * against the declared contract.
328
334
  *
@@ -342,10 +348,10 @@ interface Client {
342
348
  * don't match the `db.<table>` CRUD shape — most notably any future
343
349
  * custom RPC endpoints a module exposes.
344
350
  *
345
- * The path is appended to the configured installation-scoped base
346
- * URL (`<baseUrl>/api/modules/<installationId>`). Token attachment,
347
- * timeout, error mapping, and 204 handling all behave identically to
348
- * the typed CRUD methods.
351
+ * The path is appended to the configured module-API base URL
352
+ * (`<baseUrl>/api/module-api`). Token attachment, timeout, error
353
+ * mapping, and 204 handling all behave identically to the typed
354
+ * CRUD methods.
349
355
  *
350
356
  * For cross-module reads, prefer `client.deps(providerSlug).db(...)`
351
357
  * — it carries typed errors + the no-write enforcement.
@@ -388,7 +394,7 @@ interface Client {
388
394
  * };
389
395
  * ```
390
396
  *
391
- * Sends `POST :installationId/deps/:providerSlug/actions/:actionName`,
397
+ * Sends `POST /api/module-api/deps/:providerSlug/actions/:actionName`,
392
398
  * unwraps the envelope on success (returns `result`), throws
393
399
  * `DepActionError` on dispatch-layer failure or on action-body
394
400
  * failure (envelope `ok: false`). Drift between codegen + runtime
@@ -407,7 +413,7 @@ interface Client {
407
413
  * datahub grants. Requires the client to be authenticated with a
408
414
  * scoped `dwk_` API key that holds matching grants. Hits the
409
415
  * runtime Data Hub bridge:
410
- * GET/POST/PATCH/DELETE :installationId/datahub/tables/:tableId/rows…
416
+ * GET/POST/PATCH/DELETE /api/module-api/datahub/tables/:tableId/rows…
411
417
  *
412
418
  * Untyped in v1 (rows are `Record<string, unknown>` — they follow the
413
419
  * Data Hub table's own field schema, not the module manifest).
@@ -416,21 +422,83 @@ interface Client {
416
422
  }
417
423
  /** Custom Modules Platform v1 (P1b) — Data Hub access surface. */
418
424
  interface DatahubClient {
419
- /** Bind a Data Hub table by its numeric id. */
420
- table(tableId: number): DatahubTableClient;
425
+ /**
426
+ * Bind a Data Hub table by its numeric id. Pass a hand-written row
427
+ * interface as the type argument for typed `filter` / `sort` keys on
428
+ * `list()`; defaults to an untyped `Record` (Data Hub rows have no
429
+ * compile-time schema).
430
+ */
431
+ table<Row = Record<string, unknown>>(tableId: number): DatahubTableClient<Row>;
421
432
  }
422
433
  /** Result envelope of a Data Hub list (mirrors the backend RowQueryService). */
423
434
  interface DatahubListResult {
424
435
  results: Array<Record<string, unknown>>;
425
436
  count: number;
437
+ /**
438
+ * 1-based index of the next page, or `null` when on the last page.
439
+ * Mirrors the backend envelope (`RowQueryService.list` →
440
+ * `offset + size < count ? page + 1 : null`).
441
+ */
442
+ next: number | null;
443
+ /**
444
+ * 1-based index of the previous page, or `null` when on the first page.
445
+ * Mirrors the backend envelope (`page > 1 ? page - 1 : null`).
446
+ */
447
+ previous: number | null;
448
+ }
449
+ /**
450
+ * Typed options for {@link DatahubTableClient.list}. A higher-level
451
+ * mirror of {@link ListOpts} that the runtime compiles to the Data Hub
452
+ * bridge's flat query protocol (`filter__<field>__<op>` /
453
+ * `filters=<json>` / `order_by` / `search` / `size` / `page`).
454
+ *
455
+ * Unlike module tables, Data Hub rows have no compile-time row type, so
456
+ * `Row` defaults to a bare `Record` — pass a hand-written interface as
457
+ * the type argument for editor help on `filter` / `sort` keys.
458
+ *
459
+ * The raw `Record<string, string>` passthrough on `list()` stays
460
+ * supported for back-compat; reach for it when you need a flat param
461
+ * the typed shape doesn't model yet.
462
+ */
463
+ interface DatahubListOpts<Row = Record<string, unknown>> {
464
+ /**
465
+ * Recursive filter clause, same shape as {@link ListOpts.filter}. Top
466
+ * level keys are AND'd; bare primitives are shorthand for
467
+ * `{ equal: ... }`. Simple (non-grouped) filters compile to repeated
468
+ * `filter__<field>__<op>=<value>` params; any clause using `AND` / `OR`
469
+ * grouping compiles to a single `filters=<json>` tree param (the
470
+ * backend's Phase 9b syntax).
471
+ */
472
+ filter?: Where<Row>;
473
+ /**
474
+ * Sort order, same shape as {@link ListOpts.sort}. Compiles to the
475
+ * Data Hub `order_by` param (`-` prefix for descending), joined with
476
+ * commas for multi-field sorts.
477
+ */
478
+ sort?: SortClause<Row>[];
479
+ /** Full-text / substring search across searchable fields → `search`. */
480
+ search?: string;
481
+ /** Page size → `size`. */
482
+ limit?: number;
483
+ /** 1-based page index → `page`. */
484
+ page?: number;
426
485
  }
427
486
  /** Row CRUD against a single Data Hub table, gated by the key's grants. */
428
- interface DatahubTableClient {
487
+ interface DatahubTableClient<Row = Record<string, unknown>> {
429
488
  /**
430
- * List rows. `query` is the Baserow flat shape (e.g.
431
- * `{ size: '50', order_by: '-created_on' }`); values are strings.
489
+ * List rows. Accepts either:
490
+ * - a typed {@link DatahubListOpts} object (`{ filter, sort, search,
491
+ * limit, page }`), which the client compiles to the Data Hub
492
+ * bridge's flat query protocol; or
493
+ * - the raw Baserow flat shape (`Record<string, string>`, e.g.
494
+ * `{ size: '50', order_by: '-created_on' }`) for back-compat and
495
+ * for params the typed shape doesn't model yet.
496
+ *
497
+ * The two are distinguished structurally: an options object carrying
498
+ * any of the known `DatahubListOpts` keys is compiled; anything else
499
+ * is passed through verbatim as raw query params.
432
500
  */
433
- list(query?: Record<string, string>): Promise<DatahubListResult>;
501
+ list(opts?: DatahubListOpts<Row> | Record<string, string>): Promise<DatahubListResult>;
434
502
  get(rowId: number): Promise<Record<string, unknown>>;
435
503
  create(body: Record<string, unknown>): Promise<Record<string, unknown>>;
436
504
  update(rowId: number, body: Record<string, unknown>): Promise<Record<string, unknown>>;
@@ -439,4 +507,4 @@ interface DatahubTableClient {
439
507
  }>;
440
508
  }
441
509
 
442
- export type { BaseInsert as B, ClientConfig as C, Deps as D, FetchImpl as F, ListOpts as L, Page as P, ReadOnlyTableClient as R, SlowQueryInfo as S, TokenResolver as T, Where as W, Client as a, BaseRow as b, BaseUpdate as c, DatahubClient as d, DatahubListResult as e, DatahubTableClient as f, FileRef as g, FilterOps as h, FilterValue as i, SortClause as j, SortDir as k, TableClient as l };
510
+ export type { BaseInsert as B, ClientConfig as C, Deps as D, FetchImpl as F, ListOpts as L, Page as P, ReadOnlyTableClient as R, SlowQueryInfo as S, TokenResolver as T, Where as W, Client as a, BaseRow as b, BaseUpdate as c, DatahubClient as d, DatahubListOpts as e, DatahubListResult as f, DatahubTableClient as g, FileRef as h, FilterOps as i, FilterValue as j, SortClause as k, SortDir as l, TableClient as m };
@@ -190,37 +190,43 @@ type FetchImpl = typeof globalThis.fetch;
190
190
  */
191
191
  interface ClientConfig {
192
192
  /**
193
- * Base URL of the DashWise API. No trailing slash; the client will
194
- * append `/api/...` paths. Example: `https://api.dashwise.io`.
193
+ * Base URL of the DashWise API. No trailing slash; the client
194
+ * appends `/api/module-api/...` paths. Example: `https://api.dashwise.io`.
195
+ *
196
+ * Phase 2 wire protocol (contract W2): the generated production/local
197
+ * module bootstrap constructs the client as
198
+ * `createClient({ apiUrl: process.env.DASHWISE_API_URL, apiKey: ... })`.
199
+ * `apiUrl` is the canonical field; when set it takes precedence over
200
+ * `baseUrl`. Exactly one of `apiUrl` / `baseUrl` MUST be provided —
201
+ * the transport throws at construction otherwise.
202
+ */
203
+ apiUrl?: string;
204
+ /**
205
+ * Legacy alias for {@link ClientConfig.apiUrl}. The frozen sandbox
206
+ * generation (E2B builder template) still emits `createClient({ baseUrl, ... })`
207
+ * byte-for-byte, so this alias stays supported indefinitely. New code
208
+ * should prefer `apiUrl`. When both are set, `apiUrl` wins.
195
209
  */
196
- baseUrl: string;
210
+ baseUrl?: string;
197
211
  /**
198
212
  * Token resolver. Called once per request, before the HTTP call.
199
213
  * Resolved tokens are attached as `Authorization: Bearer <token>`.
200
214
  */
201
215
  getToken?: TokenResolver;
202
216
  /**
203
- * Custom Modules Platform v1 — a static `dwk_` API key used as the
204
- * Bearer credential. Convenience over `getToken: () => apiKey` for the
205
- * common "deployed module with one long-lived key" case (set it from
206
- * `process.env.DASHWISE_API_KEY`). Precedence: a non-null `getToken()`
207
- * result wins; otherwise `apiKey` is used. NEVER expose this to the
208
- * browser — keep it server-only (route client data through server
209
- * actions / route handlers).
210
- */
211
- apiKey?: string;
212
- /**
213
- * Installation ID this client is scoped to. Required for module
214
- * runtime calls. Typical values:
215
- * - a numeric `module_installations.id` for production modules
216
- * - `'sandbox'` for AI builder draft loops (no installation row yet)
217
- * - `'local'` for `dashwise module dev` against a dev token
217
+ * A static `dwk_` API key used as the Bearer credential. Convenience
218
+ * over `getToken: () => apiKey` for the common "deployed module with
219
+ * one long-lived key" case (set it from `process.env.DASHWISE_API_KEY`).
220
+ * Precedence: a non-null `getToken()` result wins; otherwise `apiKey`
221
+ * is used. NEVER expose this to the browser — keep it server-only
222
+ * (route client data through server actions / route handlers).
218
223
  *
219
- * Accepts any string-shaped id (or number) so consumers can pass
220
- * `process.env.DASHWISE_INSTALLATION_ID` directly without coercion.
221
- * The value is interpolated into the URL path as-is.
224
+ * Phase 2 wire protocol: the install this client operates on is
225
+ * resolved server-side from this credential (the key's bound install),
226
+ * NOT from any URL segment there is no `installationId` config
227
+ * anymore. All requests hit the install-agnostic `/api/module-api`.
222
228
  */
223
- installationId: number | string;
229
+ apiKey?: string;
224
230
  /**
225
231
  * Optional `fetch` override. Defaults to `globalThis.fetch`. Set this
226
232
  * for tests or runtimes that need a custom HTTP agent.
@@ -258,7 +264,7 @@ interface ClientConfig {
258
264
  *
259
265
  * @example
260
266
  * const client = createClient({
261
- * baseUrl, installationId,
267
+ * baseUrl, apiKey,
262
268
  * slowQueryThresholdMs: 500,
263
269
  * onSlowQuery: (info) => {
264
270
  * sentry.captureMessage('Slow query', { extra: info });
@@ -282,7 +288,7 @@ interface ClientConfig {
282
288
  * Carries enough context for an observability sink to log a useful
283
289
  * message + correlate with the backend's `module_query_log` row via
284
290
  * `requestId`. `path` is the SDK-shaped path (relative to the
285
- * installation-scoped base URL — e.g. `/query` or `/db/tasks/list`);
291
+ * `/api/module-api` base URL — e.g. `/query` or `/db/tasks/list`);
286
292
  * combine with the URL the client is configured against if you want
287
293
  * the full URL.
288
294
  *
@@ -322,7 +328,7 @@ interface Client {
322
328
  * `providerSlug` must be the **module slug** (kebab-case, matches
323
329
  * the manifest's `MODULE_SLUG_REGEX`) of a module listed in the
324
330
  * caller's `module.json#dependencies[]`. Calls hit
325
- * `POST /api/modules/:installation/deps/:providerSlug/db/:tableSlug/list`
331
+ * `POST /api/module-api/deps/:providerSlug/db/:tableSlug/list`
326
332
  * and friends; backend enforces field projection + operation gating
327
333
  * against the declared contract.
328
334
  *
@@ -342,10 +348,10 @@ interface Client {
342
348
  * don't match the `db.<table>` CRUD shape — most notably any future
343
349
  * custom RPC endpoints a module exposes.
344
350
  *
345
- * The path is appended to the configured installation-scoped base
346
- * URL (`<baseUrl>/api/modules/<installationId>`). Token attachment,
347
- * timeout, error mapping, and 204 handling all behave identically to
348
- * the typed CRUD methods.
351
+ * The path is appended to the configured module-API base URL
352
+ * (`<baseUrl>/api/module-api`). Token attachment, timeout, error
353
+ * mapping, and 204 handling all behave identically to the typed
354
+ * CRUD methods.
349
355
  *
350
356
  * For cross-module reads, prefer `client.deps(providerSlug).db(...)`
351
357
  * — it carries typed errors + the no-write enforcement.
@@ -388,7 +394,7 @@ interface Client {
388
394
  * };
389
395
  * ```
390
396
  *
391
- * Sends `POST :installationId/deps/:providerSlug/actions/:actionName`,
397
+ * Sends `POST /api/module-api/deps/:providerSlug/actions/:actionName`,
392
398
  * unwraps the envelope on success (returns `result`), throws
393
399
  * `DepActionError` on dispatch-layer failure or on action-body
394
400
  * failure (envelope `ok: false`). Drift between codegen + runtime
@@ -407,7 +413,7 @@ interface Client {
407
413
  * datahub grants. Requires the client to be authenticated with a
408
414
  * scoped `dwk_` API key that holds matching grants. Hits the
409
415
  * runtime Data Hub bridge:
410
- * GET/POST/PATCH/DELETE :installationId/datahub/tables/:tableId/rows…
416
+ * GET/POST/PATCH/DELETE /api/module-api/datahub/tables/:tableId/rows…
411
417
  *
412
418
  * Untyped in v1 (rows are `Record<string, unknown>` — they follow the
413
419
  * Data Hub table's own field schema, not the module manifest).
@@ -416,21 +422,83 @@ interface Client {
416
422
  }
417
423
  /** Custom Modules Platform v1 (P1b) — Data Hub access surface. */
418
424
  interface DatahubClient {
419
- /** Bind a Data Hub table by its numeric id. */
420
- table(tableId: number): DatahubTableClient;
425
+ /**
426
+ * Bind a Data Hub table by its numeric id. Pass a hand-written row
427
+ * interface as the type argument for typed `filter` / `sort` keys on
428
+ * `list()`; defaults to an untyped `Record` (Data Hub rows have no
429
+ * compile-time schema).
430
+ */
431
+ table<Row = Record<string, unknown>>(tableId: number): DatahubTableClient<Row>;
421
432
  }
422
433
  /** Result envelope of a Data Hub list (mirrors the backend RowQueryService). */
423
434
  interface DatahubListResult {
424
435
  results: Array<Record<string, unknown>>;
425
436
  count: number;
437
+ /**
438
+ * 1-based index of the next page, or `null` when on the last page.
439
+ * Mirrors the backend envelope (`RowQueryService.list` →
440
+ * `offset + size < count ? page + 1 : null`).
441
+ */
442
+ next: number | null;
443
+ /**
444
+ * 1-based index of the previous page, or `null` when on the first page.
445
+ * Mirrors the backend envelope (`page > 1 ? page - 1 : null`).
446
+ */
447
+ previous: number | null;
448
+ }
449
+ /**
450
+ * Typed options for {@link DatahubTableClient.list}. A higher-level
451
+ * mirror of {@link ListOpts} that the runtime compiles to the Data Hub
452
+ * bridge's flat query protocol (`filter__<field>__<op>` /
453
+ * `filters=<json>` / `order_by` / `search` / `size` / `page`).
454
+ *
455
+ * Unlike module tables, Data Hub rows have no compile-time row type, so
456
+ * `Row` defaults to a bare `Record` — pass a hand-written interface as
457
+ * the type argument for editor help on `filter` / `sort` keys.
458
+ *
459
+ * The raw `Record<string, string>` passthrough on `list()` stays
460
+ * supported for back-compat; reach for it when you need a flat param
461
+ * the typed shape doesn't model yet.
462
+ */
463
+ interface DatahubListOpts<Row = Record<string, unknown>> {
464
+ /**
465
+ * Recursive filter clause, same shape as {@link ListOpts.filter}. Top
466
+ * level keys are AND'd; bare primitives are shorthand for
467
+ * `{ equal: ... }`. Simple (non-grouped) filters compile to repeated
468
+ * `filter__<field>__<op>=<value>` params; any clause using `AND` / `OR`
469
+ * grouping compiles to a single `filters=<json>` tree param (the
470
+ * backend's Phase 9b syntax).
471
+ */
472
+ filter?: Where<Row>;
473
+ /**
474
+ * Sort order, same shape as {@link ListOpts.sort}. Compiles to the
475
+ * Data Hub `order_by` param (`-` prefix for descending), joined with
476
+ * commas for multi-field sorts.
477
+ */
478
+ sort?: SortClause<Row>[];
479
+ /** Full-text / substring search across searchable fields → `search`. */
480
+ search?: string;
481
+ /** Page size → `size`. */
482
+ limit?: number;
483
+ /** 1-based page index → `page`. */
484
+ page?: number;
426
485
  }
427
486
  /** Row CRUD against a single Data Hub table, gated by the key's grants. */
428
- interface DatahubTableClient {
487
+ interface DatahubTableClient<Row = Record<string, unknown>> {
429
488
  /**
430
- * List rows. `query` is the Baserow flat shape (e.g.
431
- * `{ size: '50', order_by: '-created_on' }`); values are strings.
489
+ * List rows. Accepts either:
490
+ * - a typed {@link DatahubListOpts} object (`{ filter, sort, search,
491
+ * limit, page }`), which the client compiles to the Data Hub
492
+ * bridge's flat query protocol; or
493
+ * - the raw Baserow flat shape (`Record<string, string>`, e.g.
494
+ * `{ size: '50', order_by: '-created_on' }`) for back-compat and
495
+ * for params the typed shape doesn't model yet.
496
+ *
497
+ * The two are distinguished structurally: an options object carrying
498
+ * any of the known `DatahubListOpts` keys is compiled; anything else
499
+ * is passed through verbatim as raw query params.
432
500
  */
433
- list(query?: Record<string, string>): Promise<DatahubListResult>;
501
+ list(opts?: DatahubListOpts<Row> | Record<string, string>): Promise<DatahubListResult>;
434
502
  get(rowId: number): Promise<Record<string, unknown>>;
435
503
  create(body: Record<string, unknown>): Promise<Record<string, unknown>>;
436
504
  update(rowId: number, body: Record<string, unknown>): Promise<Record<string, unknown>>;
@@ -439,4 +507,4 @@ interface DatahubTableClient {
439
507
  }>;
440
508
  }
441
509
 
442
- export type { BaseInsert as B, ClientConfig as C, Deps as D, FetchImpl as F, ListOpts as L, Page as P, ReadOnlyTableClient as R, SlowQueryInfo as S, TokenResolver as T, Where as W, Client as a, BaseRow as b, BaseUpdate as c, DatahubClient as d, DatahubListResult as e, DatahubTableClient as f, FileRef as g, FilterOps as h, FilterValue as i, SortClause as j, SortDir as k, TableClient as l };
510
+ export type { BaseInsert as B, ClientConfig as C, Deps as D, FetchImpl as F, ListOpts as L, Page as P, ReadOnlyTableClient as R, SlowQueryInfo as S, TokenResolver as T, Where as W, Client as a, BaseRow as b, BaseUpdate as c, DatahubClient as d, DatahubListOpts as e, DatahubListResult as f, DatahubTableClient as g, FileRef as h, FilterOps as i, FilterValue as j, SortClause as k, SortDir as l, TableClient as m };
@@ -0,0 +1,147 @@
1
+ 'use strict';
2
+
3
+ // src/data/errors.ts
4
+ var DashwiseErrorCode = {
5
+ // Workspace-table `uses` plane (Phase 4 modules-platform-v2 — the
6
+ // declared/portable path for a module to read+write a WORKSPACE table
7
+ // the workspace already owns, as opposed to `deps` = another module's
8
+ // tables). Surfaced at RUNTIME against `client.uses(slug)` /
9
+ // `qb.uses.<slug>` / the `POST /api/module-api/uses/:slug/...` REST
10
+ // plane. The BINDING is the grant on this plane (for sandbox AND
11
+ // api_key auth alike — a kind-local key does NOT bypass it):
12
+ // - USES_UNBOUND 409 — the `uses` slot is declared but its
13
+ // `module_table_bindings` row isn't `status='bound'` (never bound,
14
+ // ambiguous match, no match, table trashed, ops widened pending
15
+ // re-consent, manually unbound). `err.context` carries
16
+ // `{ uses_slug, reason }`. Recoverable: a workspace admin binds a
17
+ // table (`dashwise bind <uses_slug>`), then the read succeeds.
18
+ // - USES_OP_NOT_ALLOWED 403 — the operation isn't in the binding's
19
+ // enforcement set (manifest `ops` ∩ `consented_ops`). `err.context`
20
+ // carries `{ uses_slug, op }`. Fix: widen the manifest ops +
21
+ // re-consent (re-bind), or the admin re-binds to consent.
22
+ // - CONTRACT_FIELD_MISSING 409 — a field the binding maps no longer
23
+ // exists on the physical table (schema drift). `err.context`
24
+ // carries `{ uses_slug, field }`. The binding is best-effort parked
25
+ // on detection; an admin re-binds to a table that has the field.
26
+ USES_UNBOUND: "USES_UNBOUND",
27
+ USES_OP_NOT_ALLOWED: "USES_OP_NOT_ALLOWED",
28
+ CONTRACT_FIELD_MISSING: "CONTRACT_FIELD_MISSING"};
29
+ var DashwiseError = class extends Error {
30
+ code;
31
+ status;
32
+ retriable;
33
+ context;
34
+ /**
35
+ * Backend-issued trace anchor (Phase 6 slice 6.2, 2026-05-19).
36
+ * Mirrors the `x-request-id` response header. Populated whenever
37
+ * the backend's `RuntimeQueryController` (or any future endpoint
38
+ * that echoes the header) is the origin of this error. `null` when
39
+ * the failure is client-side (NetworkError / TimeoutError — the
40
+ * request never reached the server, so there's no server-issued
41
+ * ID to surface).
42
+ *
43
+ * Use this as a debugging trace anchor when filing a bug against a
44
+ * specific failed query — the matching row in `module_query_log`
45
+ * carries the full backend context (ast_hash, error_code,
46
+ * duration_ms, etc.).
47
+ */
48
+ requestId;
49
+ constructor(code, message, options = {}) {
50
+ super(message);
51
+ this.name = this.constructor.name;
52
+ this.code = code;
53
+ this.status = options.status ?? 0;
54
+ this.retriable = options.retriable ?? false;
55
+ this.context = options.context;
56
+ this.requestId = options.requestId ?? null;
57
+ if (options.cause !== void 0) {
58
+ this.cause = options.cause;
59
+ }
60
+ }
61
+ };
62
+ var UsesUnboundReason = {
63
+ NOT_BOUND_YET: "NOT_BOUND_YET",
64
+ AMBIGUOUS_MATCH: "AMBIGUOUS_MATCH",
65
+ NO_MATCH: "NO_MATCH",
66
+ TABLE_DELETED: "TABLE_DELETED",
67
+ OPS_WIDENED_PENDING_CONSENT: "OPS_WIDENED_PENDING_CONSENT",
68
+ MANUALLY_UNBOUND: "MANUALLY_UNBOUND"
69
+ };
70
+ var UsesUnboundError = class extends DashwiseError {
71
+ constructor(message, context, requestId = null) {
72
+ super(DashwiseErrorCode.USES_UNBOUND, message, {
73
+ status: 409,
74
+ context,
75
+ requestId
76
+ });
77
+ }
78
+ /**
79
+ * The manifest `uses[].tables[].slug` of the unbound slot (kebab-case,
80
+ * e.g. `employees`). Read from `context.uses_slug`; `undefined` if the
81
+ * backend omitted it.
82
+ */
83
+ get usesSlug() {
84
+ const v = this.context?.uses_slug;
85
+ return typeof v === "string" ? v : void 0;
86
+ }
87
+ /**
88
+ * Why the slot is unbound — one of {@link UsesUnboundReason} (typed as
89
+ * `string` for forward-compat with reasons the backend may add). Read
90
+ * from `context.reason`; `undefined` if omitted.
91
+ */
92
+ get reason() {
93
+ const v = this.context?.reason;
94
+ return typeof v === "string" ? v : void 0;
95
+ }
96
+ };
97
+ var UsesOpNotAllowedError = class extends DashwiseError {
98
+ constructor(message, context, requestId = null) {
99
+ super(DashwiseErrorCode.USES_OP_NOT_ALLOWED, message, {
100
+ status: 403,
101
+ context,
102
+ requestId
103
+ });
104
+ }
105
+ /** The `uses` slot slug the rejected op targeted. Read from `context.uses_slug`. */
106
+ get usesSlug() {
107
+ const v = this.context?.uses_slug;
108
+ return typeof v === "string" ? v : void 0;
109
+ }
110
+ /**
111
+ * The operation that was rejected — one of `create` / `read` /
112
+ * `update` / `delete`. Read from `context.op`; `undefined` if omitted.
113
+ */
114
+ get op() {
115
+ const v = this.context?.op;
116
+ return typeof v === "string" ? v : void 0;
117
+ }
118
+ };
119
+ var ContractFieldMissingError = class extends DashwiseError {
120
+ constructor(message, context, requestId = null) {
121
+ super(DashwiseErrorCode.CONTRACT_FIELD_MISSING, message, {
122
+ status: 409,
123
+ context,
124
+ requestId
125
+ });
126
+ }
127
+ /** The `uses` slot slug whose contract broke. Read from `context.uses_slug`. */
128
+ get usesSlug() {
129
+ const v = this.context?.uses_slug;
130
+ return typeof v === "string" ? v : void 0;
131
+ }
132
+ /**
133
+ * The declared field slug (the `uses` side name) whose mapped physical
134
+ * field is gone. Read from `context.field`; `undefined` if omitted.
135
+ */
136
+ get field() {
137
+ const v = this.context?.field;
138
+ return typeof v === "string" ? v : void 0;
139
+ }
140
+ };
141
+
142
+ exports.ContractFieldMissingError = ContractFieldMissingError;
143
+ exports.UsesOpNotAllowedError = UsesOpNotAllowedError;
144
+ exports.UsesUnboundError = UsesUnboundError;
145
+ exports.UsesUnboundReason = UsesUnboundReason;
146
+ //# sourceMappingURL=index.cjs.map
147
+ //# sourceMappingURL=index.cjs.map