@dashai/sdk 0.7.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 (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +377 -0
  3. package/dist/auth/client.cjs +185 -0
  4. package/dist/auth/client.cjs.map +1 -0
  5. package/dist/auth/client.d.cts +137 -0
  6. package/dist/auth/client.d.ts +137 -0
  7. package/dist/auth/client.js +175 -0
  8. package/dist/auth/client.js.map +1 -0
  9. package/dist/auth/middleware.cjs +352 -0
  10. package/dist/auth/middleware.cjs.map +1 -0
  11. package/dist/auth/middleware.d.cts +90 -0
  12. package/dist/auth/middleware.d.ts +90 -0
  13. package/dist/auth/middleware.js +343 -0
  14. package/dist/auth/middleware.js.map +1 -0
  15. package/dist/auth/server.cjs +445 -0
  16. package/dist/auth/server.cjs.map +1 -0
  17. package/dist/auth/server.d.cts +170 -0
  18. package/dist/auth/server.d.ts +170 -0
  19. package/dist/auth/server.js +432 -0
  20. package/dist/auth/server.js.map +1 -0
  21. package/dist/auth/types.cjs +31 -0
  22. package/dist/auth/types.cjs.map +1 -0
  23. package/dist/auth/types.d.cts +163 -0
  24. package/dist/auth/types.d.ts +163 -0
  25. package/dist/auth/types.js +29 -0
  26. package/dist/auth/types.js.map +1 -0
  27. package/dist/deps/index.cjs +117 -0
  28. package/dist/deps/index.cjs.map +1 -0
  29. package/dist/deps/index.d.cts +93 -0
  30. package/dist/deps/index.d.ts +93 -0
  31. package/dist/deps/index.js +112 -0
  32. package/dist/deps/index.js.map +1 -0
  33. package/dist/errors-BV75u7b9.d.cts +207 -0
  34. package/dist/errors-BV75u7b9.d.ts +207 -0
  35. package/dist/index.cjs +721 -0
  36. package/dist/index.cjs.map +1 -0
  37. package/dist/index.d.cts +171 -0
  38. package/dist/index.d.ts +171 -0
  39. package/dist/index.js +703 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/query/index.cjs +621 -0
  42. package/dist/query/index.cjs.map +1 -0
  43. package/dist/query/index.d.cts +800 -0
  44. package/dist/query/index.d.ts +800 -0
  45. package/dist/query/index.js +617 -0
  46. package/dist/query/index.js.map +1 -0
  47. package/dist/react/index.cjs +199 -0
  48. package/dist/react/index.cjs.map +1 -0
  49. package/dist/react/index.d.cts +117 -0
  50. package/dist/react/index.d.ts +117 -0
  51. package/dist/react/index.js +195 -0
  52. package/dist/react/index.js.map +1 -0
  53. package/dist/types-BLNQ1S1C.d.cts +396 -0
  54. package/dist/types-BLNQ1S1C.d.ts +396 -0
  55. package/package.json +109 -0
package/dist/index.js ADDED
@@ -0,0 +1,703 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ // src/data/deps.ts
9
+ var MODULE_SLUG_REGEX = /^[a-z][a-z0-9-]*[a-z0-9]$/;
10
+ var TABLE_SLUG_REGEX = /^[a-z][a-z0-9_]*[a-z0-9]$/;
11
+ function assertValidProviderSlug(slug) {
12
+ if (typeof slug !== "string" || !MODULE_SLUG_REGEX.test(slug)) {
13
+ throw new Error(
14
+ `@dashai/sdk: invalid provider slug "${slug}". Provider slugs are the dependency module's slug (kebab-case, e.g. "crm-app"); must match /^[a-z][a-z0-9-]*[a-z0-9]$/.`
15
+ );
16
+ }
17
+ }
18
+ function assertValidTableSlug(slug) {
19
+ if (typeof slug !== "string" || !TABLE_SLUG_REGEX.test(slug)) {
20
+ throw new Error(
21
+ `@dashai/sdk: invalid table slug "${slug}". Table slugs must match /^[a-z][a-z0-9_]*[a-z0-9]$/ (snake_case).`
22
+ );
23
+ }
24
+ }
25
+ function makeDeps(transport, providerSlug) {
26
+ assertValidProviderSlug(providerSlug);
27
+ return {
28
+ db(tableSlug) {
29
+ assertValidTableSlug(tableSlug);
30
+ return makeBorrowedTableClient(transport, providerSlug, tableSlug);
31
+ }
32
+ };
33
+ }
34
+ function makeBorrowedTableClient(transport, providerSlug, tableSlug) {
35
+ const root = `/deps/${providerSlug}/db/${tableSlug}`;
36
+ return {
37
+ async list(opts) {
38
+ const body = opts ?? {};
39
+ const result = await transport.request("POST", `${root}/list`, body);
40
+ if (Array.isArray(result)) {
41
+ return {
42
+ rows: result,
43
+ total: result.length,
44
+ limit: body.limit ?? result.length,
45
+ offset: body.offset ?? 0
46
+ };
47
+ }
48
+ return result;
49
+ },
50
+ async get(id) {
51
+ return transport.request("GET", `${root}/${id}`);
52
+ }
53
+ };
54
+ }
55
+
56
+ // src/data/errors.ts
57
+ var DashwiseErrorCode = {
58
+ // Auth / authz
59
+ UNAUTHENTICATED: "UNAUTHENTICATED",
60
+ FORBIDDEN: "FORBIDDEN",
61
+ TOKEN_EXPIRED: "TOKEN_EXPIRED",
62
+ INSTALLATION_NOT_FOUND: "INSTALLATION_NOT_FOUND",
63
+ INSTALLATION_MODE_UNSUPPORTED: "INSTALLATION_MODE_UNSUPPORTED",
64
+ // Contract enforcement
65
+ CONTRACT_VIOLATION: "CONTRACT_VIOLATION",
66
+ FIELD_NOT_READABLE: "FIELD_NOT_READABLE",
67
+ FILTER_OP_UNKNOWN: "FILTER_OP_UNKNOWN",
68
+ OPERATION_NOT_ALLOWED: "OPERATION_NOT_ALLOWED",
69
+ // Cross-module dependencies (Phase 1 of the SDK query plane —
70
+ // see dashwise-devops-docs/plans/modules-sdk-query-v1.md).
71
+ // Surfaced when reading borrowed tables via `client.deps(slug)`.
72
+ DEPENDENCY_NOT_DECLARED: "DEPENDENCY_NOT_DECLARED",
73
+ TABLE_NOT_IN_CONTRACT: "TABLE_NOT_IN_CONTRACT",
74
+ PROVIDER_TABLE_MISSING: "PROVIDER_TABLE_MISSING",
75
+ OPERATION_NOT_ALLOWED_BY_PROVIDER: "OPERATION_NOT_ALLOWED_BY_PROVIDER",
76
+ // Query-plane joins (Phase 3 — see
77
+ // dashwise-devops-docs/plans/modules-sdk-query-v1-p3-execution.md).
78
+ // Surfaced when `.join('<slug>')` / `.leftJoin('<slug>')` can't
79
+ // resolve the target. Pre-declared in slice 3.1 so slice 3.2's
80
+ // backend translator can emit them through fromBackendEnvelope
81
+ // without an SDK roundtrip.
82
+ LINK_FIELD_NOT_FOUND: "LINK_FIELD_NOT_FOUND",
83
+ NOT_A_LINK_FIELD: "NOT_A_LINK_FIELD",
84
+ JOIN_NOT_LINKED: "JOIN_NOT_LINKED",
85
+ // Cross-module actions (Phase 7 slice 7.3a — see
86
+ // dashwise-devops-docs/plans/modules-sdk-query-v1-p7-cross-module-writes.md).
87
+ // Surfaced when `qb.deps.<dep>.actions.<name>(input)` (generated
88
+ // factory) dispatches against the new backend endpoint
89
+ // `POST :installationId/deps/:providerSlug/actions/:actionName`.
90
+ // Slice 7.2b ships the backend emitter; slice 7.3a (this slice)
91
+ // reserves the codes + maps them to DepActionError in the SDK.
92
+ ACTION_NOT_EXPOSED: "ACTION_NOT_EXPOSED",
93
+ ACTION_NOT_DECLARED: "ACTION_NOT_DECLARED",
94
+ ACTION_INPUT_INVALID: "ACTION_INPUT_INVALID",
95
+ ACTION_OUTPUT_INVALID: "ACTION_OUTPUT_INVALID",
96
+ ACTION_RATE_LIMITED: "ACTION_RATE_LIMITED",
97
+ ACTION_EXECUTION_FAILED: "ACTION_EXECUTION_FAILED",
98
+ // Data plane
99
+ ROW_NOT_FOUND: "ROW_NOT_FOUND",
100
+ TABLE_NOT_FOUND: "TABLE_NOT_FOUND",
101
+ VALIDATION_FAILED: "VALIDATION_FAILED",
102
+ ROW_LIMIT_EXCEEDED: "ROW_LIMIT_EXCEEDED",
103
+ STORAGE_LIMIT_EXCEEDED: "STORAGE_LIMIT_EXCEEDED",
104
+ // Outbound / runtime
105
+ OUTBOUND_NOT_ALLOWED: "OUTBOUND_NOT_ALLOWED",
106
+ OUTBOUND_CONCURRENCY_LIMITED: "OUTBOUND_CONCURRENCY_LIMITED",
107
+ // Transport
108
+ NETWORK_ERROR: "NETWORK_ERROR",
109
+ TIMEOUT: "TIMEOUT",
110
+ // Server
111
+ INTERNAL_ERROR: "INTERNAL_ERROR"
112
+ };
113
+ var DashwiseError = class extends Error {
114
+ code;
115
+ status;
116
+ retriable;
117
+ context;
118
+ /**
119
+ * Backend-issued trace anchor (Phase 6 slice 6.2, 2026-05-19).
120
+ * Mirrors the `x-request-id` response header. Populated whenever
121
+ * the backend's `RuntimeQueryController` (or any future endpoint
122
+ * that echoes the header) is the origin of this error. `null` when
123
+ * the failure is client-side (NetworkError / TimeoutError — the
124
+ * request never reached the server, so there's no server-issued
125
+ * ID to surface).
126
+ *
127
+ * Use this as a debugging trace anchor when filing a bug against a
128
+ * specific failed query — the matching row in `module_query_log`
129
+ * carries the full backend context (ast_hash, error_code,
130
+ * duration_ms, etc.).
131
+ */
132
+ requestId;
133
+ constructor(code, message, options = {}) {
134
+ super(message);
135
+ this.name = this.constructor.name;
136
+ this.code = code;
137
+ this.status = options.status ?? 0;
138
+ this.retriable = options.retriable ?? false;
139
+ this.context = options.context;
140
+ this.requestId = options.requestId ?? null;
141
+ if (options.cause !== void 0) {
142
+ this.cause = options.cause;
143
+ }
144
+ }
145
+ };
146
+ var NetworkError = class extends DashwiseError {
147
+ constructor(message, cause) {
148
+ super(DashwiseErrorCode.NETWORK_ERROR, message, { retriable: true, cause });
149
+ }
150
+ };
151
+ var TimeoutError = class extends DashwiseError {
152
+ constructor(message, timeoutMs) {
153
+ super(DashwiseErrorCode.TIMEOUT, message, {
154
+ retriable: true,
155
+ context: { timeout_ms: timeoutMs }
156
+ });
157
+ }
158
+ };
159
+ var UnauthenticatedError = class extends DashwiseError {
160
+ constructor(message = "Authentication required", requestId = null) {
161
+ super(DashwiseErrorCode.UNAUTHENTICATED, message, { status: 401, requestId });
162
+ }
163
+ };
164
+ var ForbiddenError = class extends DashwiseError {
165
+ constructor(message, context, requestId = null) {
166
+ super(DashwiseErrorCode.FORBIDDEN, message, { status: 403, context, requestId });
167
+ }
168
+ };
169
+ var RowNotFoundError = class extends DashwiseError {
170
+ constructor(message, context, requestId = null) {
171
+ super(DashwiseErrorCode.ROW_NOT_FOUND, message, { status: 404, context, requestId });
172
+ }
173
+ };
174
+ var ContractViolationError = class extends DashwiseError {
175
+ constructor(message, context, requestId = null) {
176
+ super(DashwiseErrorCode.CONTRACT_VIOLATION, message, {
177
+ status: 403,
178
+ context,
179
+ requestId
180
+ });
181
+ }
182
+ };
183
+ var ValidationError = class extends DashwiseError {
184
+ constructor(message, context, requestId = null) {
185
+ super(DashwiseErrorCode.VALIDATION_FAILED, message, {
186
+ status: 400,
187
+ context,
188
+ requestId
189
+ });
190
+ }
191
+ };
192
+ var DependencyContractError = class extends DashwiseError {
193
+ constructor(code, message, context, requestId = null) {
194
+ super(code, message, { status: 404, context, requestId });
195
+ }
196
+ };
197
+ var ProviderTableMissingError = class extends DashwiseError {
198
+ constructor(message, context, requestId = null) {
199
+ super(DashwiseErrorCode.PROVIDER_TABLE_MISSING, message, {
200
+ status: 404,
201
+ context,
202
+ requestId
203
+ });
204
+ }
205
+ };
206
+ var OperationNotAllowedByProviderError = class extends DashwiseError {
207
+ constructor(message, context, requestId = null) {
208
+ super(DashwiseErrorCode.OPERATION_NOT_ALLOWED_BY_PROVIDER, message, {
209
+ status: 403,
210
+ context,
211
+ requestId
212
+ });
213
+ }
214
+ };
215
+ var JoinError = class extends DashwiseError {
216
+ constructor(code, message, context, requestId = null) {
217
+ super(code, message, { status: 400, context, requestId });
218
+ }
219
+ };
220
+ var DepActionError = class extends DashwiseError {
221
+ constructor(code, message, status, context, requestId = null) {
222
+ super(code, message, {
223
+ status,
224
+ retriable: code === DashwiseErrorCode.ACTION_RATE_LIMITED,
225
+ context,
226
+ requestId
227
+ });
228
+ }
229
+ };
230
+ function fromBackendEnvelope(status, envelope, requestId = null) {
231
+ const code = envelope?.code ?? codeFromStatus(status);
232
+ const message = envelope?.message ?? `HTTP ${status}`;
233
+ const context = envelope?.context;
234
+ switch (code) {
235
+ case DashwiseErrorCode.UNAUTHENTICATED:
236
+ case DashwiseErrorCode.TOKEN_EXPIRED:
237
+ return new UnauthenticatedError(message, requestId);
238
+ case DashwiseErrorCode.FORBIDDEN:
239
+ return new ForbiddenError(message, context, requestId);
240
+ case DashwiseErrorCode.ROW_NOT_FOUND:
241
+ return new RowNotFoundError(message, context, requestId);
242
+ case DashwiseErrorCode.CONTRACT_VIOLATION:
243
+ case DashwiseErrorCode.FIELD_NOT_READABLE:
244
+ case DashwiseErrorCode.FILTER_OP_UNKNOWN:
245
+ case DashwiseErrorCode.OPERATION_NOT_ALLOWED:
246
+ return new ContractViolationError(message, context, requestId);
247
+ case DashwiseErrorCode.VALIDATION_FAILED:
248
+ return new ValidationError(message, context, requestId);
249
+ case DashwiseErrorCode.DEPENDENCY_NOT_DECLARED:
250
+ case DashwiseErrorCode.TABLE_NOT_IN_CONTRACT:
251
+ return new DependencyContractError(code, message, context, requestId);
252
+ case DashwiseErrorCode.PROVIDER_TABLE_MISSING:
253
+ return new ProviderTableMissingError(message, context, requestId);
254
+ case DashwiseErrorCode.OPERATION_NOT_ALLOWED_BY_PROVIDER:
255
+ return new OperationNotAllowedByProviderError(message, context, requestId);
256
+ case DashwiseErrorCode.LINK_FIELD_NOT_FOUND:
257
+ case DashwiseErrorCode.NOT_A_LINK_FIELD:
258
+ case DashwiseErrorCode.JOIN_NOT_LINKED:
259
+ return new JoinError(code, message, context, requestId);
260
+ case DashwiseErrorCode.ACTION_NOT_EXPOSED:
261
+ case DashwiseErrorCode.ACTION_NOT_DECLARED:
262
+ return new DepActionError(code, message, 403, context, requestId);
263
+ case DashwiseErrorCode.ACTION_INPUT_INVALID:
264
+ return new DepActionError(code, message, 400, context, requestId);
265
+ case DashwiseErrorCode.ACTION_OUTPUT_INVALID:
266
+ return new DepActionError(code, message, 500, context, requestId);
267
+ case DashwiseErrorCode.ACTION_RATE_LIMITED:
268
+ return new DepActionError(code, message, 429, context, requestId);
269
+ case DashwiseErrorCode.ACTION_EXECUTION_FAILED:
270
+ return new DepActionError(code, message, 200, context, requestId);
271
+ default:
272
+ return new DashwiseError(code, message, {
273
+ status,
274
+ retriable: envelope?.retriable ?? false,
275
+ context,
276
+ requestId
277
+ });
278
+ }
279
+ }
280
+ function codeFromStatus(status) {
281
+ if (status === 401) return DashwiseErrorCode.UNAUTHENTICATED;
282
+ if (status === 403) return DashwiseErrorCode.FORBIDDEN;
283
+ if (status === 404) return DashwiseErrorCode.ROW_NOT_FOUND;
284
+ if (status >= 500) return DashwiseErrorCode.INTERNAL_ERROR;
285
+ return "HTTP_ERROR";
286
+ }
287
+
288
+ // src/data/http.ts
289
+ var DEFAULT_TIMEOUT_MS = 3e4;
290
+ var DEFAULT_SLOW_QUERY_THRESHOLD_MS = 500;
291
+ var TRAILING_SLASH = /\/+$/;
292
+ var LEADING_SLASH = /^\/+/;
293
+ function buildBaseUrl(baseUrl, installationId) {
294
+ if (!baseUrl) {
295
+ throw new Error("@dashai/sdk: `baseUrl` is required");
296
+ }
297
+ const trimmed = baseUrl.replace(TRAILING_SLASH, "");
298
+ return `${trimmed}/api/modules/${installationId}`;
299
+ }
300
+ function resolveFetch(custom) {
301
+ if (custom) return custom;
302
+ if (typeof globalThis.fetch !== "function") {
303
+ throw new Error(
304
+ "@dashai/sdk: no `fetch` available. On Node < 18, pass an explicit `fetch` in `createClient({...})`."
305
+ );
306
+ }
307
+ return globalThis.fetch.bind(globalThis);
308
+ }
309
+ async function readBody(res) {
310
+ if (res.status === 204) return void 0;
311
+ const contentType = res.headers.get("content-type") ?? "";
312
+ if (!contentType.includes("application/json")) {
313
+ try {
314
+ return await res.text();
315
+ } catch {
316
+ return null;
317
+ }
318
+ }
319
+ try {
320
+ return await res.json();
321
+ } catch {
322
+ return null;
323
+ }
324
+ }
325
+ function createHttp(config) {
326
+ const base = buildBaseUrl(config.baseUrl, config.installationId);
327
+ const doFetch = resolveFetch(config.fetch);
328
+ const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
329
+ const staticHeaders = {
330
+ Accept: "application/json",
331
+ ...config.headers ?? {}
332
+ };
333
+ const onSlowQuery = config.onSlowQuery;
334
+ const slowQueryThresholdMs = config.slowQueryThresholdMs ?? DEFAULT_SLOW_QUERY_THRESHOLD_MS;
335
+ function maybeFireSlowQuery(info) {
336
+ if (!onSlowQuery) return;
337
+ if (info.durationMs <= slowQueryThresholdMs) return;
338
+ try {
339
+ onSlowQuery(info);
340
+ } catch {
341
+ }
342
+ }
343
+ async function request(method, path, body) {
344
+ const normalizedPath = `/${path.replace(LEADING_SLASH, "")}`;
345
+ const url = normalizedPath.startsWith("/api/") ? `${config.baseUrl.replace(/\/+$/, "")}${normalizedPath}` : `${base}${normalizedPath}`;
346
+ const token = config.getToken ? await config.getToken() : null;
347
+ const headers = { ...staticHeaders };
348
+ if (body !== void 0) {
349
+ headers["Content-Type"] = "application/json";
350
+ }
351
+ if (token) {
352
+ headers["Authorization"] = `Bearer ${token}`;
353
+ }
354
+ const controller = new AbortController();
355
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
356
+ const start = Date.now();
357
+ let res;
358
+ try {
359
+ res = await doFetch(url, {
360
+ method,
361
+ headers,
362
+ body: body === void 0 ? void 0 : JSON.stringify(body),
363
+ signal: controller.signal
364
+ });
365
+ } catch (err) {
366
+ clearTimeout(timer);
367
+ const durationMs2 = Date.now() - start;
368
+ if (isAbortError(err)) {
369
+ maybeFireSlowQuery({
370
+ method,
371
+ path: normalizedPath,
372
+ durationMs: durationMs2,
373
+ status: 0,
374
+ success: false,
375
+ requestId: null
376
+ });
377
+ throw new TimeoutError(`Request to ${method} ${normalizedPath} timed out`, timeoutMs);
378
+ }
379
+ maybeFireSlowQuery({
380
+ method,
381
+ path: normalizedPath,
382
+ durationMs: durationMs2,
383
+ status: 0,
384
+ success: false,
385
+ requestId: null
386
+ });
387
+ throw new NetworkError(
388
+ `Network failure on ${method} ${normalizedPath}: ${describeError(err)}`,
389
+ err
390
+ );
391
+ }
392
+ clearTimeout(timer);
393
+ const parsed = await readBody(res);
394
+ const durationMs = Date.now() - start;
395
+ const requestId = res.headers.get("x-request-id");
396
+ if (!res.ok) {
397
+ maybeFireSlowQuery({
398
+ method,
399
+ path: normalizedPath,
400
+ durationMs,
401
+ status: res.status,
402
+ success: false,
403
+ requestId
404
+ });
405
+ const envelope = isEnvelope(parsed) ? parsed : null;
406
+ throw fromBackendEnvelope(res.status, envelope, requestId);
407
+ }
408
+ maybeFireSlowQuery({
409
+ method,
410
+ path: normalizedPath,
411
+ durationMs,
412
+ status: res.status,
413
+ success: true,
414
+ requestId
415
+ });
416
+ return parsed;
417
+ }
418
+ async function streamRaw(method, path, body, signal) {
419
+ const normalizedPath = `/${path.replace(LEADING_SLASH, "")}`;
420
+ const url = `${base}${normalizedPath}`;
421
+ const token = config.getToken ? await config.getToken() : null;
422
+ const headers = { ...staticHeaders };
423
+ if (body !== void 0) {
424
+ headers["Content-Type"] = "application/json";
425
+ }
426
+ if (token) {
427
+ headers["Authorization"] = `Bearer ${token}`;
428
+ }
429
+ const timeoutController = new AbortController();
430
+ const timer = setTimeout(() => timeoutController.abort(), timeoutMs);
431
+ const combinedController = new AbortController();
432
+ if (signal) {
433
+ if (signal.aborted) combinedController.abort();
434
+ else signal.addEventListener("abort", () => combinedController.abort(), { once: true });
435
+ }
436
+ timeoutController.signal.addEventListener(
437
+ "abort",
438
+ () => combinedController.abort(),
439
+ { once: true }
440
+ );
441
+ const start = Date.now();
442
+ let res;
443
+ try {
444
+ res = await doFetch(url, {
445
+ method,
446
+ headers,
447
+ body: body === void 0 ? void 0 : JSON.stringify(body),
448
+ signal: combinedController.signal
449
+ });
450
+ } catch (err) {
451
+ clearTimeout(timer);
452
+ const durationMs = Date.now() - start;
453
+ if (isAbortError(err)) {
454
+ maybeFireSlowQuery({
455
+ method,
456
+ path: normalizedPath,
457
+ durationMs,
458
+ status: 0,
459
+ success: false,
460
+ requestId: null
461
+ });
462
+ if (signal?.aborted) {
463
+ const e = new Error("Aborted by caller");
464
+ e.name = "AbortError";
465
+ throw e;
466
+ }
467
+ throw new TimeoutError(
468
+ `Streaming request to ${method} ${normalizedPath} timed out before headers arrived`,
469
+ timeoutMs
470
+ );
471
+ }
472
+ maybeFireSlowQuery({
473
+ method,
474
+ path: normalizedPath,
475
+ durationMs,
476
+ status: 0,
477
+ success: false,
478
+ requestId: null
479
+ });
480
+ throw new NetworkError(
481
+ `Network failure on ${method} ${normalizedPath}: ${describeError(err)}`,
482
+ err
483
+ );
484
+ }
485
+ clearTimeout(timer);
486
+ const requestId = res.headers.get("x-request-id");
487
+ maybeFireSlowQuery({
488
+ method,
489
+ path: normalizedPath,
490
+ durationMs: Date.now() - start,
491
+ status: res.status,
492
+ success: res.ok,
493
+ requestId
494
+ });
495
+ return res;
496
+ }
497
+ return { request, streamRaw };
498
+ }
499
+ function isAbortError(err) {
500
+ if (!err || typeof err !== "object") return false;
501
+ const e = err;
502
+ return e.name === "AbortError" || e.code === "ABORT_ERR";
503
+ }
504
+ function describeError(err) {
505
+ if (err instanceof Error) return err.message;
506
+ if (typeof err === "string") return err;
507
+ try {
508
+ return JSON.stringify(err);
509
+ } catch {
510
+ return "<unknown error>";
511
+ }
512
+ }
513
+ function isEnvelope(value) {
514
+ if (!value || typeof value !== "object") return false;
515
+ const v = value;
516
+ return typeof v.code === "string" || typeof v.message === "string";
517
+ }
518
+
519
+ // src/data/client.ts
520
+ function createClient(config) {
521
+ const transport = createHttp(config);
522
+ return {
523
+ db(tableSlug) {
524
+ assertValidTableSlug2(tableSlug);
525
+ return makeTableClient(transport, tableSlug);
526
+ },
527
+ deps(providerSlug) {
528
+ return makeDeps(transport, providerSlug);
529
+ },
530
+ request(method, path, body) {
531
+ return transport.request(method, path, body);
532
+ },
533
+ streamRaw(method, path, body, signal) {
534
+ return transport.streamRaw(method, path, body, signal);
535
+ },
536
+ async _callAction(providerSlug, actionName, input, opts) {
537
+ assertValidModuleSlug(providerSlug);
538
+ assertValidActionName(actionName);
539
+ const path = `/api/modules/runtime/installations/${config.installationId}/deps/${encodeURIComponent(
540
+ providerSlug
541
+ )}/actions/${encodeURIComponent(actionName)}`;
542
+ const body = { input };
543
+ if (opts?.idempotencyKey !== void 0) {
544
+ body.idempotency_key = opts.idempotencyKey;
545
+ }
546
+ if (opts?.timeoutMs !== void 0) {
547
+ body.timeout_ms = opts.timeoutMs;
548
+ }
549
+ const envelope = await transport.request(
550
+ "POST",
551
+ path,
552
+ body
553
+ );
554
+ if (envelope.ok === false) {
555
+ const requestId = envelope.cross_module?.request_id ?? null;
556
+ throw fromBackendEnvelope(
557
+ 200,
558
+ {
559
+ code: envelope.error.code,
560
+ message: envelope.error.message,
561
+ context: envelope.error.trace ? { trace: envelope.error.trace } : void 0
562
+ },
563
+ requestId
564
+ );
565
+ }
566
+ return envelope.result;
567
+ }
568
+ };
569
+ }
570
+ var ACTION_NAME_REGEX = /^[a-zA-Z_$][a-zA-Z0-9_$]{0,63}$/;
571
+ var MODULE_SLUG_REGEX2 = /^[a-z][a-z0-9-]*[a-z0-9]$/;
572
+ function assertValidModuleSlug(slug) {
573
+ if (!MODULE_SLUG_REGEX2.test(slug)) {
574
+ throw new Error(
575
+ `Invalid provider module slug "${slug}" \u2014 must be kebab-case (^[a-z][a-z0-9-]*[a-z0-9]$).`
576
+ );
577
+ }
578
+ }
579
+ function assertValidActionName(name) {
580
+ if (!ACTION_NAME_REGEX.test(name)) {
581
+ throw new Error(
582
+ `Invalid action name "${name}" \u2014 must be a JS identifier (letters/digits/underscore/$, starts non-digit, \u226464 chars).`
583
+ );
584
+ }
585
+ }
586
+ var TABLE_SLUG_REGEX2 = /^[a-z][a-z0-9_]*$/;
587
+ function assertValidTableSlug2(slug) {
588
+ if (typeof slug !== "string" || !TABLE_SLUG_REGEX2.test(slug)) {
589
+ throw new Error(
590
+ `@dashai/sdk: invalid table slug "${slug}". Slugs must match /^[a-z][a-z0-9_]*$/.`
591
+ );
592
+ }
593
+ }
594
+ function makeTableClient(transport, tableSlug) {
595
+ const root = `/db/${tableSlug}`;
596
+ return {
597
+ async list(opts) {
598
+ const ast = {
599
+ v: 1,
600
+ from: { kind: "owned", slug: tableSlug }
601
+ };
602
+ if (opts?.filter !== void 0) ast.where = opts.filter;
603
+ if (opts?.sort !== void 0 && opts.sort.length > 0) {
604
+ ast.orderBy = opts.sort;
605
+ }
606
+ if (typeof opts?.limit === "number") ast.limit = opts.limit;
607
+ if (typeof opts?.offset === "number") ast.offset = opts.offset;
608
+ const result = await transport.request("POST", "/query", { ast });
609
+ if (Array.isArray(result)) {
610
+ return {
611
+ rows: result,
612
+ total: result.length,
613
+ limit: opts?.limit ?? result.length,
614
+ offset: opts?.offset ?? 0
615
+ };
616
+ }
617
+ return result;
618
+ },
619
+ async get(id) {
620
+ return transport.request("GET", `${root}/${id}`);
621
+ },
622
+ async create(body) {
623
+ return transport.request("POST", root, body);
624
+ },
625
+ async update(id, patch) {
626
+ return transport.request("PATCH", `${root}/${id}`, patch);
627
+ },
628
+ async delete(id) {
629
+ await transport.request("DELETE", `${root}/${id}`);
630
+ }
631
+ };
632
+ }
633
+
634
+ // src/data/dev-client.ts
635
+ function createDevClient(overrides = {}) {
636
+ const fileConfig = readCliConfigFile();
637
+ const baseUrl = overrides.baseUrl ?? readEnv("DASHWISE_API_URL") ?? fileConfig?.apiUrl ?? null;
638
+ if (!baseUrl) {
639
+ throw new Error(
640
+ "@dashai/sdk: createDevClient() could not resolve a baseUrl. Pass `{ baseUrl }` explicitly, set `DASHWISE_API_URL`, or run `dashwise login` to create the CLI config file."
641
+ );
642
+ }
643
+ const installationId = overrides.installationId ?? readEnv("DASHWISE_INSTALLATION_ID") ?? "local";
644
+ const snapshotToken = readEnv("DASHWISE_API_TOKEN") ?? fileConfig?.token ?? null;
645
+ const resolvedGetToken = overrides.getToken ?? (() => snapshotToken);
646
+ return createClient({
647
+ ...overrides,
648
+ baseUrl,
649
+ installationId,
650
+ getToken: resolvedGetToken
651
+ });
652
+ }
653
+ function readEnv(key) {
654
+ if (typeof process === "undefined" || !process.env) return void 0;
655
+ const v = process.env[key];
656
+ return typeof v === "string" && v.length > 0 ? v : void 0;
657
+ }
658
+ function isNodeRuntime() {
659
+ if (typeof process === "undefined") return false;
660
+ if (!process.versions || typeof process.versions.node !== "string") {
661
+ return false;
662
+ }
663
+ return true;
664
+ }
665
+ function readCliConfigFile() {
666
+ if (!isNodeRuntime()) return null;
667
+ if (readEnv("NODE_ENV") === "production") return null;
668
+ try {
669
+ const fs = __require("fs");
670
+ const path = __require("path");
671
+ const os = __require("os");
672
+ const dir = resolveConfigDir(path, os);
673
+ const file = path.join(dir, "auth.json");
674
+ if (!fs.existsSync(file)) return null;
675
+ const raw = fs.readFileSync(file, "utf-8");
676
+ const parsed = JSON.parse(raw);
677
+ if (!isPlausibleFileShape(parsed)) return null;
678
+ return parsed;
679
+ } catch {
680
+ return null;
681
+ }
682
+ }
683
+ function resolveConfigDir(path, os) {
684
+ const xdg = readEnv("XDG_CONFIG_HOME");
685
+ if (xdg) return path.join(xdg, "dashwise");
686
+ if (os.platform() === "win32") {
687
+ const appdata = readEnv("APPDATA");
688
+ if (appdata) return path.join(appdata, "dashwise");
689
+ }
690
+ return path.join(os.homedir(), ".config", "dashwise");
691
+ }
692
+ function isPlausibleFileShape(v) {
693
+ if (!v || typeof v !== "object") return false;
694
+ const o = v;
695
+ return (o.apiUrl === void 0 || typeof o.apiUrl === "string") && (o.token === void 0 || typeof o.token === "string");
696
+ }
697
+
698
+ // src/index.ts
699
+ var VERSION = "0.7.0" ;
700
+
701
+ export { ContractViolationError, DashwiseError, DashwiseErrorCode, DependencyContractError, ForbiddenError, JoinError, NetworkError, OperationNotAllowedByProviderError, ProviderTableMissingError, RowNotFoundError, TimeoutError, UnauthenticatedError, VERSION, ValidationError, createClient, createDevClient, fromBackendEnvelope };
702
+ //# sourceMappingURL=index.js.map
703
+ //# sourceMappingURL=index.js.map