@softr/sdk 0.0.22 → 0.0.24

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.
@@ -46,8 +46,9 @@ function readSessionData(sessionToken) {
46
46
  permissions: payload.permissions
47
47
  };
48
48
  }
49
- function makeSessionTokenCookie(token, secure = requestIsSecure()) {
50
- return serializeCookie(sessionCookieName(SESSION_TOKEN, secure), sealEnvelope(token, SESSION_TOKEN), {
49
+ function makeSessionTokenCookie(token) {
50
+ const { name, secure } = sessionCookie(SESSION_TOKEN);
51
+ return serializeCookie(name, sealEnvelope(token, SESSION_TOKEN), {
51
52
  maxAge: sessionConfig().sessionMaxAge,
52
53
  secure
53
54
  });
@@ -71,19 +72,6 @@ function makeSessionDataCookie(session, sessionToken) {
71
72
  function clearSession() {
72
73
  clearCookies([SESSION_TOKEN, SESSION_DATA]);
73
74
  }
74
- /** The clearing pair, for the same out-of-context caller as `makeSessionTokenCookie`. */
75
- function makeSessionClearingCookies(secure) {
76
- return serializeClearingCookies([SESSION_TOKEN, SESSION_DATA], secure);
77
- }
78
- /**
79
- * The name `readSessionToken` would look the token up under on a request of this
80
- * scheme, for a caller that only needs to know whether one is present and cannot
81
- * decode it. Same `secure` argument as `makeSessionTokenCookie`, so a caller that
82
- * writes and a caller that probes cannot disagree about the name.
83
- */
84
- function sessionTokenCookieName(secure) {
85
- return sessionCookieName(SESSION_TOKEN, secure);
86
- }
87
75
  const sessionDataSchema = z.object({
88
76
  user: userSchema.nullable(),
89
77
  permissions: permissionsSchema,
@@ -95,13 +83,11 @@ const sessionDataSchema = z.object({
95
83
  * schemes, and the one left behind would revive if the scheme changed back.
96
84
  */
97
85
  function clearCookies(bases) {
98
- writeCookies(serializeClearingCookies(bases, requestIsSecure()));
99
- }
100
- function serializeClearingCookies(bases, secure) {
101
- return bases.flatMap((base) => secure ? [sessionCookieName(base, secure), base] : [base]).map((name) => serializeCookie(name, "", {
86
+ const secure = requestIsSecure();
87
+ writeCookies(bases.flatMap((base) => secure ? [sessionCookieName(base, secure), base] : [base]).map((name) => serializeCookie(name, "", {
102
88
  maxAge: 0,
103
89
  secure
104
- }));
90
+ })));
105
91
  }
106
92
  /**
107
93
  * Over the plaintext token, never the cookie value: that is resealed with a
@@ -177,4 +163,4 @@ function openEnvelope(raw, purpose) {
177
163
  }
178
164
 
179
165
  //#endregion
180
- export { clearSession, makeSessionClearingCookies, makeSessionDataCookie, makeSessionTokenCookie, readSessionData, readSessionToken, sessionTokenCookieName };
166
+ export { clearSession, makeSessionDataCookie, makeSessionTokenCookie, readSessionData, readSessionToken };
@@ -1,13 +1,23 @@
1
- import { ChartDataOptions, ChartDataResult, CreateOptions, GetOptions, ListOptions, ListResult, MetricOptions, MetricResult, SoftrRecord, UpdateOptions } from "../types.js";
1
+ import { ChartOptions, ChartResult, CreateOptions, GetOptions, ListOptions, ListResult, MetricOptions, MetricResult, RecordFieldsFor, SoftrRecord, UpdateOptions } from "../types.js";
2
2
 
3
3
  //#region src/core/datasource.d.ts
4
- declare function listRecords(datasourceId: string, options?: ListOptions): Promise<ListResult>;
5
- declare function getRecord(datasourceId: string, options: GetOptions): Promise<SoftrRecord | null>;
6
- declare function createRecord(datasourceId: string, options: CreateOptions): Promise<SoftrRecord>;
7
- declare function updateRecord(datasourceId: string, options: UpdateOptions): Promise<SoftrRecord>;
4
+ declare function listRecords<Id extends string, Sel extends Record<string, string> = Record<never, never>>(datasourceId: Id, options?: Omit<ListOptions, 'select'> & {
5
+ select?: Sel;
6
+ }): Promise<ListResult<SoftrRecord<RecordFieldsFor<Id, Sel>>>>;
7
+ declare function getRecord<Id extends string, Sel extends Record<string, string> = Record<never, never>>(datasourceId: Id, options: Omit<GetOptions, 'select'> & {
8
+ select?: Sel;
9
+ }): Promise<SoftrRecord<RecordFieldsFor<Id, Sel>> | null>;
10
+ declare function createRecord<Id extends string, Sel extends Record<string, string> = Record<never, never>>(datasourceId: Id, options: Omit<CreateOptions, 'select' | 'data'> & {
11
+ select?: Sel;
12
+ data: Partial<RecordFieldsFor<Id, Sel>>;
13
+ }): Promise<SoftrRecord<RecordFieldsFor<Id, Sel>>>;
14
+ declare function updateRecord<Id extends string, Sel extends Record<string, string> = Record<never, never>>(datasourceId: Id, options: Omit<UpdateOptions, 'select' | 'data'> & {
15
+ select?: Sel;
16
+ data: Partial<RecordFieldsFor<Id, Sel>>;
17
+ }): Promise<SoftrRecord<RecordFieldsFor<Id, Sel>>>;
8
18
  declare function deleteRecord(datasourceId: string, id: string): Promise<void>;
9
19
  declare function getMetric(datasourceId: string, options: MetricOptions): Promise<MetricResult>;
10
- declare function getChartData(datasourceId: string, options: ChartDataOptions): Promise<ChartDataResult>;
20
+ declare function getChart(datasourceId: string, options: ChartOptions): Promise<ChartResult>;
11
21
  declare const ds: {
12
22
  list: typeof listRecords;
13
23
  get: typeof getRecord;
@@ -15,7 +25,7 @@ declare const ds: {
15
25
  update: typeof updateRecord;
16
26
  delete: typeof deleteRecord;
17
27
  metric: typeof getMetric;
18
- chartData: typeof getChartData;
28
+ chart: typeof getChart;
19
29
  };
20
30
  //#endregion
21
31
  export { ds };
@@ -3,6 +3,7 @@ import { panic } from "../assertions.js";
3
3
  import { readUserTimezone } from "../cookies.js";
4
4
  import { sessionConfig } from "../auth/config.js";
5
5
  import { readSessionToken } from "../auth/session.js";
6
+ import { assertResponseOk } from "./utils.js";
6
7
 
7
8
  //#region src/core/datasource.ts
8
9
  const MAX_RECORD_COUNT = 100;
@@ -36,7 +37,7 @@ async function listRecords(datasourceId, options = {}) {
36
37
  }
37
38
  })
38
39
  });
39
- if (!res.ok) throw new Error(`Softr datasource list failed for "${datasourceId}": ${res.status}`);
40
+ await assertResponseOk(res, `Softr datasource list failed for "${datasourceId}"`);
40
41
  const payload = await res.json();
41
42
  const nextOffset = payload.offset < payload.total ? payload.offset : null;
42
43
  return {
@@ -60,13 +61,18 @@ async function getRecord(datasourceId, options) {
60
61
  },
61
62
  body: JSON.stringify({ options: { userLocale: "en-US" } })
62
63
  });
63
- if (!res.ok) throw new Error(`Softr datasource get failed for "${datasourceId}": ${res.status}`);
64
+ await assertResponseOk(res, `Softr datasource get failed for "${datasourceId}"`);
64
65
  const contentLength = res.headers.get("Content-Length");
65
66
  if (contentLength && parseInt(contentLength, 10) === 0) return null;
66
- return res.json();
67
+ const record = await res.json();
68
+ return {
69
+ id: record.id,
70
+ fields: options.select ? aliasFields(record.fields, options.select) : record.fields
71
+ };
67
72
  }
68
73
  return (await listRecords(datasourceId, {
69
74
  where: options.where,
75
+ select: options.select,
70
76
  limit: 1,
71
77
  timeZone: options.timeZone
72
78
  })).records[0] ?? null;
@@ -81,12 +87,12 @@ async function createRecord(datasourceId, options) {
81
87
  },
82
88
  body: JSON.stringify({ fields: rawFields })
83
89
  });
84
- if (!res.ok) throw new Error(`Softr datasource create failed for "${datasourceId}": ${res.status}`);
90
+ await assertResponseOk(res, `Softr datasource create failed for "${datasourceId}"`);
85
91
  const record = await res.json();
86
- return options.select ? {
92
+ return {
87
93
  id: record.id,
88
- fields: aliasFields(record.fields, options.select)
89
- } : record;
94
+ fields: options.select ? aliasFields(record.fields, options.select) : record.fields
95
+ };
90
96
  }
91
97
  async function updateRecord(datasourceId, options) {
92
98
  if (!options.id) throw new Error(`Softr datasource update: id must not be empty`);
@@ -99,25 +105,25 @@ async function updateRecord(datasourceId, options) {
99
105
  },
100
106
  body: JSON.stringify({ fields: rawFields })
101
107
  });
102
- if (!res.ok) throw new Error(`Softr datasource update failed for "${datasourceId}": ${res.status}`);
108
+ await assertResponseOk(res, `Softr datasource update failed for "${datasourceId}"`);
103
109
  const record = await res.json();
104
- return options.select ? {
110
+ return {
105
111
  id: record.id,
106
- fields: aliasFields(record.fields, options.select)
107
- } : record;
112
+ fields: options.select ? aliasFields(record.fields, options.select) : record.fields
113
+ };
108
114
  }
109
115
  async function deleteRecord(datasourceId, id) {
110
- const res = await fetch(`${dsUrl(datasourceId)}/records/${id}`, {
116
+ await assertResponseOk(await fetch(`${dsUrl(datasourceId)}/records/${id}`, {
111
117
  method: "DELETE",
112
118
  headers: { "Authorization": `Bearer ${readSessionToken()}` }
113
- });
114
- if (!res.ok) throw new Error(`Softr datasource delete failed for "${datasourceId}": ${res.status}`);
119
+ }), `Softr datasource delete failed for "${datasourceId}"`);
115
120
  }
116
121
  async function getMetric(datasourceId, options) {
117
122
  const { select, where, aggregation, field } = options;
123
+ if (aggregation !== "COUNT" && !field) throw new Error(`field is required for aggregation "${aggregation}"`);
118
124
  const fieldId = field ? select?.[field] ?? field : void 0;
119
125
  const filterCriteria = where ? dealiasFilterCriteria(buildFilterCriteria(where), select ?? {}) : {};
120
- const res = await fetch(`${dsUrl(datasourceId)}/metric/data`, {
126
+ const res = await fetch(`${dsUrl(datasourceId)}/chart/data`, {
121
127
  method: "POST",
122
128
  headers: {
123
129
  "Content-Type": "application/json",
@@ -126,37 +132,36 @@ async function getMetric(datasourceId, options) {
126
132
  },
127
133
  body: JSON.stringify({
128
134
  options: { userLocale: "en-US" },
129
- calculationField: {
135
+ groupBy: [],
136
+ metrics: [{
130
137
  field: fieldId,
131
138
  aggregation
132
- },
139
+ }],
133
140
  filterCriteria
134
141
  })
135
142
  });
136
- if (!res.ok) throw new Error(`Softr datasource metric failed for "${datasourceId}": ${res.status}`);
143
+ await assertResponseOk(res, `Softr datasource metric failed for "${datasourceId}"`);
137
144
  const payload = await res.json();
138
145
  const executionTimestamp = /* @__PURE__ */ new Date(payload.executionTimestamp * 1e3);
139
146
  let value;
140
- if (aggregation === "COUNT") value = payload.data.find((item) => COUNT_CNT in item)?.[COUNT_CNT] ?? null;
141
- else {
142
- if (!fieldId) throw new Error(`field is required for aggregation "${aggregation}"`);
143
- value = payload.data.find((item) => fieldId in item)?.[fieldId] ?? null;
144
- }
147
+ const responseKey = aggregation === "COUNT" && !fieldId ? COUNT_CNT : fieldId;
148
+ value = payload.data.find((item) => responseKey in item)?.[responseKey] ?? null;
145
149
  return {
146
150
  value,
147
151
  executionTimestamp
148
152
  };
149
153
  }
150
- async function getChartData(datasourceId, options) {
154
+ async function getChart(datasourceId, options) {
151
155
  const { select, where, groupBy, metrics, orderBy } = options;
152
156
  const filterCriteria = where ? dealiasFilterCriteria(buildFilterCriteria(where), select ?? {}) : {};
153
- const sortingOption = orderBy ? dealiasOrderBy(buildOrderBy(orderBy), select ?? {})[0] : void 0;
157
+ const sortingOptions = orderBy ? dealiasOrderBy(buildOrderBy(orderBy), select ?? {}) : void 0;
154
158
  const groupByFields = (Array.isArray(groupBy) ? groupBy : [groupBy]).map((field) => ({ field: select?.[field] ?? field }));
155
159
  const yAxisFields = Object.values(metrics).map(({ field, aggregation }) => ({
156
160
  field: field ? select?.[field] ?? field : void 0,
157
161
  aggregation
158
162
  }));
159
- const res = await fetch(`${dsUrl(datasourceId)}/chart`, {
163
+ if (!yAxisFields.length) throw new Error("chart requires at least one metric");
164
+ const res = await fetch(`${dsUrl(datasourceId)}/chart/data`, {
160
165
  method: "POST",
161
166
  headers: {
162
167
  "Content-Type": "application/json",
@@ -166,12 +171,12 @@ async function getChartData(datasourceId, options) {
166
171
  body: JSON.stringify({
167
172
  options: { userLocale: "en-US" },
168
173
  filterCriteria,
169
- sortingOption,
170
- xAxisFields: groupByFields,
171
- yAxisFields
174
+ sortingOptions,
175
+ groupBy: groupByFields,
176
+ metrics: yAxisFields
172
177
  })
173
178
  });
174
- if (!res.ok) throw new Error(`Softr datasource chartData failed for "${datasourceId}": ${res.status}`);
179
+ await assertResponseOk(res, `Softr datasource chart failed for "${datasourceId}"`);
175
180
  const payload = await res.json();
176
181
  const executionTimestamp = /* @__PURE__ */ new Date(payload.executionTimestamp * 1e3);
177
182
  const aliases = {};
@@ -211,11 +216,11 @@ const ds = {
211
216
  update: updateRecord,
212
217
  delete: deleteRecord,
213
218
  metric: getMetric,
214
- chartData: getChartData
219
+ chart: getChart
215
220
  };
216
221
  function withoutTrailingSlash(url) {
217
222
  return url.replace(/\/+$/, "");
218
223
  }
219
224
 
220
225
  //#endregion
221
- export { ds };
226
+ export { ds, dsUrl };
@@ -1,7 +1,7 @@
1
1
  import { readSessionToken } from "../auth/session.js";
2
+ import { dsUrl } from "./datasource.js";
2
3
 
3
4
  //#region src/core/integration.ts
4
- const BASE_URL = () => (import.meta.env.VITE_SOFTR_BASE_URL ?? "").replace(/\/$/, "");
5
5
  async function integrationFetch(integrationId, input, init) {
6
6
  const req = new Request(input, init);
7
7
  if (req.signal.aborted) throw req.signal.reason ?? new DOMException("The operation was aborted.", "AbortError");
@@ -16,7 +16,7 @@ async function integrationFetch(integrationId, input, init) {
16
16
  };
17
17
  const method = req.method.toUpperCase();
18
18
  if (method !== "GET" && method !== "HEAD" && req.body !== null) payload.body = await req.clone().text();
19
- return fetch(`${BASE_URL()}/api/integrations/${integrationId}/proxy`, {
19
+ return fetch(`${dsUrl(integrationId)}/proxy`, {
20
20
  method: "POST",
21
21
  headers: {
22
22
  "Content-Type": "application/json",
@@ -0,0 +1,14 @@
1
+ //#region src/core/utils.ts
2
+ async function assertResponseOk(res, message) {
3
+ if (!res.ok) {
4
+ let fullMessage = `${message}: ${res.status}`;
5
+ if (process.env.NODE_ENV !== "production") {
6
+ const body = await res.text().catch(() => "");
7
+ if (body) fullMessage += `\n\n${body}`;
8
+ }
9
+ throw new Error(fullMessage);
10
+ }
11
+ }
12
+
13
+ //#endregion
14
+ export { assertResponseOk };
@@ -0,0 +1,13 @@
1
+ import * as react0 from "react";
2
+
3
+ //#region src/default-error-overlay.d.ts
4
+ type Props = {
5
+ error: Error;
6
+ reset?: () => void;
7
+ };
8
+ declare function DefaultErrorOverlay({
9
+ error,
10
+ reset
11
+ }: Props): react0.JSX.Element;
12
+ //#endregion
13
+ export { DefaultErrorOverlay };
@@ -0,0 +1,319 @@
1
+ import { useRef, useState } from "react";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+
4
+ //#region src/default-error-overlay.tsx
5
+ function DefaultErrorOverlay({ error, reset }) {
6
+ if (process.env.NODE_ENV === "production") return /* @__PURE__ */ jsx("div", {
7
+ style: s.overlay,
8
+ children: /* @__PURE__ */ jsxs("div", {
9
+ style: s.card,
10
+ children: [
11
+ /* @__PURE__ */ jsx(AlertIcon, {}),
12
+ /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("h1", {
13
+ style: s.heading,
14
+ children: "Something went wrong"
15
+ }), /* @__PURE__ */ jsx("p", {
16
+ style: s.body,
17
+ children: "This page couldn't be shown. Try again in a moment."
18
+ })] }),
19
+ /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(RetryButton, { onClick: reset }) })
20
+ ]
21
+ })
22
+ });
23
+ return /* @__PURE__ */ jsx(DevView, {
24
+ error,
25
+ reset
26
+ });
27
+ }
28
+ function DevView({ error, reset }) {
29
+ return /* @__PURE__ */ jsx("div", {
30
+ style: s.overlay,
31
+ children: /* @__PURE__ */ jsxs("div", {
32
+ style: s.card,
33
+ children: [
34
+ /* @__PURE__ */ jsx(AlertIcon, {}),
35
+ /* @__PURE__ */ jsxs("div", { children: [
36
+ /* @__PURE__ */ jsx("h1", {
37
+ style: s.heading,
38
+ children: "Something went wrong"
39
+ }),
40
+ /* @__PURE__ */ jsx("p", {
41
+ style: s.body,
42
+ children: "A render error was caught. Copy the details to the agent to help fix the issue."
43
+ }),
44
+ /* @__PURE__ */ jsx("p", {
45
+ style: s.note,
46
+ children: "Only shown in development mode."
47
+ })
48
+ ] }),
49
+ /* @__PURE__ */ jsx("pre", {
50
+ style: s.message,
51
+ children: error.message
52
+ }),
53
+ error.stack && /* @__PURE__ */ jsx(StackTrace, { stack: error.stack }),
54
+ /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(RetryButton, { onClick: reset }) })
55
+ ]
56
+ })
57
+ });
58
+ }
59
+ function cleanStack(stack) {
60
+ return stack.split("\n").slice(1).filter((line) => line.trim().startsWith("at ") && !line.includes("/node_modules/")).map((line) => line.replace(/https?:\/\/[^/]+\//, "").replace(/\?[^):]+/g, "")).join("\n");
61
+ }
62
+ function StackTrace({ stack }) {
63
+ const cleaned = cleanStack(stack);
64
+ const [open, setOpen] = useState(false);
65
+ const [copied, setCopied] = useState(false);
66
+ const ref = useRef(null);
67
+ if (!cleaned) return null;
68
+ async function copy() {
69
+ try {
70
+ await navigator.clipboard.writeText(cleaned);
71
+ setCopied(true);
72
+ setTimeout(() => setCopied(false), 1500);
73
+ } catch {
74
+ if (ref.current) getSelection()?.selectAllChildren(ref.current);
75
+ }
76
+ }
77
+ return /* @__PURE__ */ jsxs("div", {
78
+ style: {
79
+ display: "flex",
80
+ flexDirection: "column",
81
+ gap: "0.5rem"
82
+ },
83
+ children: [/* @__PURE__ */ jsxs("button", {
84
+ type: "button",
85
+ style: s.toggle,
86
+ "aria-expanded": open,
87
+ onClick: () => setOpen((v) => !v),
88
+ children: [/* @__PURE__ */ jsx("svg", {
89
+ style: {
90
+ width: "0.875rem",
91
+ height: "0.875rem",
92
+ flexShrink: 0,
93
+ transition: "transform 150ms",
94
+ transform: open ? "rotate(90deg)" : "none"
95
+ },
96
+ viewBox: "0 0 24 24",
97
+ fill: "none",
98
+ stroke: "currentColor",
99
+ strokeWidth: "2",
100
+ strokeLinecap: "round",
101
+ strokeLinejoin: "round",
102
+ children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" })
103
+ }), "Stack trace"]
104
+ }), open && /* @__PURE__ */ jsxs("div", {
105
+ style: { position: "relative" },
106
+ children: [/* @__PURE__ */ jsx("pre", {
107
+ ref,
108
+ style: s.stack,
109
+ children: cleaned
110
+ }), /* @__PURE__ */ jsx("button", {
111
+ type: "button",
112
+ style: s.copy,
113
+ "aria-label": "Copy stack trace",
114
+ onClick: copy,
115
+ children: copied ? /* @__PURE__ */ jsx("svg", {
116
+ style: icon.sm,
117
+ viewBox: "0 0 24 24",
118
+ fill: "none",
119
+ stroke: "currentColor",
120
+ strokeWidth: "2",
121
+ strokeLinecap: "round",
122
+ strokeLinejoin: "round",
123
+ children: /* @__PURE__ */ jsx("path", { d: "M20 6 9 17l-5-5" })
124
+ }) : /* @__PURE__ */ jsxs("svg", {
125
+ style: icon.sm,
126
+ viewBox: "0 0 24 24",
127
+ fill: "none",
128
+ stroke: "currentColor",
129
+ strokeWidth: "2",
130
+ strokeLinecap: "round",
131
+ strokeLinejoin: "round",
132
+ children: [/* @__PURE__ */ jsx("rect", {
133
+ width: "14",
134
+ height: "14",
135
+ x: "8",
136
+ y: "8",
137
+ rx: "2",
138
+ ry: "2"
139
+ }), /* @__PURE__ */ jsx("path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" })]
140
+ })
141
+ })]
142
+ })]
143
+ });
144
+ }
145
+ function AlertIcon() {
146
+ return /* @__PURE__ */ jsx("div", {
147
+ style: s.alert,
148
+ children: /* @__PURE__ */ jsxs("svg", {
149
+ style: icon.md,
150
+ viewBox: "0 0 24 24",
151
+ fill: "none",
152
+ stroke: "currentColor",
153
+ strokeWidth: "2",
154
+ strokeLinecap: "round",
155
+ strokeLinejoin: "round",
156
+ children: [
157
+ /* @__PURE__ */ jsx("path", { d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" }),
158
+ /* @__PURE__ */ jsx("path", { d: "M12 9v4" }),
159
+ /* @__PURE__ */ jsx("path", { d: "M12 17h.01" })
160
+ ]
161
+ })
162
+ });
163
+ }
164
+ function RetryButton({ onClick }) {
165
+ return /* @__PURE__ */ jsxs("button", {
166
+ type: "button",
167
+ style: s.retry,
168
+ onClick,
169
+ children: [/* @__PURE__ */ jsxs("svg", {
170
+ style: icon.sm,
171
+ viewBox: "0 0 24 24",
172
+ fill: "none",
173
+ stroke: "currentColor",
174
+ strokeWidth: "2",
175
+ strokeLinecap: "round",
176
+ strokeLinejoin: "round",
177
+ children: [
178
+ /* @__PURE__ */ jsx("path", { d: "M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8" }),
179
+ /* @__PURE__ */ jsx("path", { d: "M21 3v5h-5" }),
180
+ /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16" }),
181
+ /* @__PURE__ */ jsx("path", { d: "M8 16H3v5" })
182
+ ]
183
+ }), "Try again"]
184
+ });
185
+ }
186
+ const icon = {
187
+ sm: {
188
+ width: "0.875rem",
189
+ height: "0.875rem"
190
+ },
191
+ md: {
192
+ width: "1.25rem",
193
+ height: "1.25rem"
194
+ }
195
+ };
196
+ const s = {
197
+ overlay: {
198
+ minHeight: "100vh",
199
+ display: "grid",
200
+ placeItems: "center",
201
+ gridTemplateColumns: "minmax(0, 1fr)",
202
+ padding: "1.5rem"
203
+ },
204
+ card: {
205
+ width: "100%",
206
+ maxWidth: "36rem",
207
+ display: "flex",
208
+ flexDirection: "column",
209
+ gap: "1.25rem",
210
+ padding: "1.5rem",
211
+ borderRadius: "calc(var(--radius, 0.625rem) + 4px)",
212
+ background: "var(--card, oklch(1 0 0))",
213
+ boxShadow: "0 0 0 1px color-mix(in oklch, var(--foreground, oklch(0.145 0 0)) 10%, transparent)"
214
+ },
215
+ alert: {
216
+ display: "flex",
217
+ alignItems: "center",
218
+ justifyContent: "center",
219
+ width: "2.5rem",
220
+ height: "2.5rem",
221
+ borderRadius: "var(--radius, 0.625rem)",
222
+ background: "color-mix(in oklch, var(--destructive, oklch(0.577 0.245 27.325)) 12%, transparent)",
223
+ color: "var(--destructive, oklch(0.577 0.245 27.325))",
224
+ flexShrink: 0
225
+ },
226
+ heading: {
227
+ margin: 0,
228
+ fontSize: "1rem",
229
+ fontWeight: 500,
230
+ lineHeight: 1.375
231
+ },
232
+ body: {
233
+ margin: "0.375rem 0 0",
234
+ fontSize: "0.875rem",
235
+ color: "var(--muted-foreground, oklch(0.556 0 0))"
236
+ },
237
+ note: {
238
+ margin: "0.75rem 0 0",
239
+ fontSize: "0.875rem",
240
+ color: "var(--muted-foreground, oklch(0.556 0 0))",
241
+ opacity: .65
242
+ },
243
+ message: {
244
+ margin: 0,
245
+ padding: "0.75rem",
246
+ borderRadius: "var(--radius, 0.625rem)",
247
+ background: "var(--muted, oklch(0.97 0 0))",
248
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
249
+ fontSize: "0.8125rem",
250
+ lineHeight: 1.6,
251
+ whiteSpace: "pre-wrap",
252
+ overflowWrap: "anywhere",
253
+ maxHeight: "12rem",
254
+ overflow: "auto"
255
+ },
256
+ toggle: {
257
+ display: "inline-flex",
258
+ alignItems: "center",
259
+ gap: "0.375rem",
260
+ height: "2rem",
261
+ padding: "0 0.75rem 0 0.5rem",
262
+ border: "1px solid var(--input, oklch(0.922 0 0))",
263
+ borderRadius: "var(--radius, 0.625rem)",
264
+ background: "var(--input-fill, oklch(1 0 0))",
265
+ color: "var(--primary-foreground, oklch(0.985 0 0))",
266
+ fontFamily: "inherit",
267
+ fontSize: "0.875rem",
268
+ fontWeight: 500,
269
+ cursor: "pointer",
270
+ userSelect: "none"
271
+ },
272
+ stack: {
273
+ margin: 0,
274
+ padding: "0.75rem",
275
+ paddingRight: "2.75rem",
276
+ borderRadius: "var(--radius, 0.625rem)",
277
+ background: "var(--muted, oklch(0.97 0 0))",
278
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
279
+ fontSize: "0.75rem",
280
+ lineHeight: 1.6,
281
+ maxHeight: "16rem",
282
+ overflow: "auto"
283
+ },
284
+ copy: {
285
+ position: "absolute",
286
+ top: "0.375rem",
287
+ right: "0.375rem",
288
+ display: "inline-flex",
289
+ alignItems: "center",
290
+ justifyContent: "center",
291
+ width: "1.75rem",
292
+ height: "1.75rem",
293
+ padding: 0,
294
+ border: "1px solid var(--input, oklch(0.922 0 0))",
295
+ borderRadius: "calc(var(--radius, 0.625rem) - 2px)",
296
+ background: "var(--card, oklch(1 0 0))",
297
+ color: "var(--muted-foreground, oklch(0.556 0 0))",
298
+ cursor: "pointer"
299
+ },
300
+ retry: {
301
+ display: "inline-flex",
302
+ alignItems: "center",
303
+ gap: "0.375rem",
304
+ height: "2rem",
305
+ padding: "0 0.75rem",
306
+ border: "1px solid transparent",
307
+ borderRadius: "var(--radius, 0.625rem)",
308
+ background: "var(--primary, oklch(0.205 0 0))",
309
+ color: "var(--primary-foreground, oklch(0.985 0 0))",
310
+ fontFamily: "inherit",
311
+ fontSize: "0.875rem",
312
+ fontWeight: 500,
313
+ cursor: "pointer",
314
+ userSelect: "none"
315
+ }
316
+ };
317
+
318
+ //#endregion
319
+ export { DefaultErrorOverlay };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  import { q } from "./core/query.js";
2
2
  import { SoftrUserGroupId, SoftrUserGroups, User } from "./auth/user.js";
3
- import { AggregationFunction, ChartDataOptions, ChartDataResult, CreateOptions, DatasourcePermissions, GetOptions, JsonValue, ListOptions, ListResult, MetricOptions, MetricResult, SoftrMe, SoftrPermissions, SoftrRecord, SoftrUser, UpdateOptions } from "./types.js";
3
+ import { AggregationFunction, ChartOptions, ChartResult, CreateOptions, DatasourcePermissions, DatasourceTypeMap, GetOptions, JsonValue, ListOptions, ListResult, MetricOptions, MetricResult, RecordFieldsFor, SoftrMe, SoftrPermissions, SoftrRecord, SoftrUser, UpdateOptions } from "./types.js";
4
4
  import { Permissions } from "./auth/permissions.js";
5
5
  import { SoftrContext } from "./auth/context.js";
6
6
  import { AccessBehaviour, AccessCheck, AccessError, AccessReason, RouteVisibility, VisibilityHandle, defineVisibility, isAccessError, isAlreadySignedInError, isForbiddenError, isUnauthenticatedError } from "./routes/visibility.js";
7
7
  import { getSoftrContext, initialSoftrContext, softrAuthMiddleware, useCurrentUser, usePermissions } from "./auth/isomorphic.js";
8
8
  import { useUserTimezone } from "./timezone.js";
9
- export { type AccessBehaviour, type AccessCheck, type AccessError, type AccessReason, type AggregationFunction, type ChartDataOptions, type ChartDataResult, type CreateOptions, type DatasourcePermissions, type GetOptions, type JsonValue, type ListOptions, type ListResult, type MetricOptions, type MetricResult, type Permissions, type RouteVisibility, type SoftrContext, type SoftrMe, type SoftrPermissions, type SoftrRecord, type SoftrUser, type SoftrUserGroupId, type SoftrUserGroups, type UpdateOptions, type User, type VisibilityHandle, defineVisibility, getSoftrContext, initialSoftrContext, isAccessError, isAlreadySignedInError, isForbiddenError, isUnauthenticatedError, q, softrAuthMiddleware, useCurrentUser, usePermissions, useUserTimezone };
9
+ import { DefaultErrorOverlay } from "./default-error-overlay.js";
10
+ export { type AccessBehaviour, type AccessCheck, type AccessError, type AccessReason, type AggregationFunction, type ChartOptions, type ChartResult, type CreateOptions, type DatasourcePermissions, type DatasourceTypeMap, DefaultErrorOverlay, type GetOptions, type JsonValue, type ListOptions, type ListResult, type MetricOptions, type MetricResult, type Permissions, type RecordFieldsFor, type RouteVisibility, type SoftrContext, type SoftrMe, type SoftrPermissions, type SoftrRecord, type SoftrUser, type SoftrUserGroupId, type SoftrUserGroups, type UpdateOptions, type User, type VisibilityHandle, defineVisibility, getSoftrContext, initialSoftrContext, isAccessError, isAlreadySignedInError, isForbiddenError, isUnauthenticatedError, q, softrAuthMiddleware, useCurrentUser, usePermissions, useUserTimezone };
package/dist/index.js CHANGED
@@ -2,5 +2,6 @@ import { q } from "./core/query.js";
2
2
  import { defineVisibility, isAccessError, isAlreadySignedInError, isForbiddenError, isUnauthenticatedError } from "./routes/visibility.js";
3
3
  import { getSoftrContext, initialSoftrContext, softrAuthMiddleware, useCurrentUser, usePermissions } from "./auth/isomorphic.js";
4
4
  import { useUserTimezone } from "./timezone.js";
5
+ import { DefaultErrorOverlay } from "./default-error-overlay.js";
5
6
 
6
- export { defineVisibility, getSoftrContext, initialSoftrContext, isAccessError, isAlreadySignedInError, isForbiddenError, isUnauthenticatedError, q, softrAuthMiddleware, useCurrentUser, usePermissions, useUserTimezone };
7
+ export { DefaultErrorOverlay, defineVisibility, getSoftrContext, initialSoftrContext, isAccessError, isAlreadySignedInError, isForbiddenError, isUnauthenticatedError, q, softrAuthMiddleware, useCurrentUser, usePermissions, useUserTimezone };