@softr/sdk 0.0.12 → 0.0.14
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.
- package/dist/assertions.js +68 -0
- package/dist/auth/config.js +27 -0
- package/dist/auth/context.d.ts +18 -0
- package/dist/auth/isomorphic.d.ts +73 -0
- package/dist/auth/isomorphic.js +113 -0
- package/dist/auth/methods/password.d.ts +22 -0
- package/dist/auth/methods/password.js +51 -0
- package/dist/auth/methods/signup.d.ts +37 -0
- package/dist/auth/methods/signup.js +89 -0
- package/dist/auth/methods/utils.d.ts +15 -0
- package/dist/auth/methods/utils.js +13 -0
- package/dist/auth/permissions.d.ts +17 -0
- package/dist/auth/permissions.js +13 -0
- package/dist/auth/platform.js +138 -0
- package/dist/auth/server.d.ts +17 -0
- package/dist/auth/server.js +17 -0
- package/dist/auth/session.js +167 -0
- package/dist/auth/user.d.ts +28 -0
- package/dist/client/timezone.d.ts +4 -0
- package/dist/client/timezone.js +11 -0
- package/dist/client.d.ts +1 -4
- package/dist/client.js +1 -9
- package/dist/cookies.js +47 -0
- package/dist/core/datasource.d.ts +21 -0
- package/dist/core/datasource.js +221 -0
- package/dist/core/integration.d.ts +7 -0
- package/dist/core/integration.js +32 -0
- package/dist/{types-n-Sir_Y2.d.ts → core/query.d.ts} +1 -129
- package/dist/core/query.js +528 -0
- package/dist/core/workflow.d.ts +7 -0
- package/dist/core/workflow.js +19 -0
- package/dist/index.d.ts +8 -172
- package/dist/index.js +4 -176
- package/dist/routes/discover.d.ts +9 -0
- package/dist/routes/discover.js +198 -0
- package/dist/routes/manifest.d.ts +17 -0
- package/dist/routes/manifest.js +148 -0
- package/dist/routes/mem-fs.js +71 -0
- package/dist/routes/visibility.d.ts +142 -0
- package/dist/routes/visibility.js +125 -0
- package/dist/server.d.ts +4 -113
- package/dist/server.js +4 -429
- package/dist/types.d.ts +115 -0
- package/dist/vite.d.ts +1 -21
- package/dist/vite.js +2 -405
- package/package.json +4 -4
- package/dist/session-kTqxT5_K.js +0 -891
package/dist/server.js
CHANGED
|
@@ -1,432 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { auth } from "./auth/server.js";
|
|
2
|
+
import { ds } from "./core/datasource.js";
|
|
3
|
+
import { workflow } from "./core/workflow.js";
|
|
4
|
+
import { integration } from "./core/integration.js";
|
|
3
5
|
import "@tanstack/react-start/server-only";
|
|
4
6
|
|
|
5
|
-
//#region src/auth/methods/utils.ts
|
|
6
|
-
function authError(code, message) {
|
|
7
|
-
return {
|
|
8
|
-
success: false,
|
|
9
|
-
error: {
|
|
10
|
-
code,
|
|
11
|
-
message
|
|
12
|
-
}
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
const errorSchema = z.object({ message: z.string() });
|
|
16
|
-
async function describe(response, action) {
|
|
17
|
-
const body = errorSchema.safeParse(await response.json().catch(() => null));
|
|
18
|
-
const said = body.success && body.data.message ? body.data.message : response.statusText;
|
|
19
|
-
return `Softr platform refused the ${action}: ${response.status} ${said}`.trim();
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
//#endregion
|
|
23
|
-
//#region src/auth/methods/password.ts
|
|
24
|
-
const signInBody = z.object({
|
|
25
|
-
jwt: z.string().nullish(),
|
|
26
|
-
authenticatorAppAction: z.string().nullish()
|
|
27
|
-
});
|
|
28
|
-
/**
|
|
29
|
-
* Signs a visitor in with an email and password.
|
|
30
|
-
*
|
|
31
|
-
* The cache is primed from the same `/me` the resolver would have called anyway,
|
|
32
|
-
* so the next request costs no round-trip.
|
|
33
|
-
*/
|
|
34
|
-
async function signIn(credentials) {
|
|
35
|
-
const response = await platformFetch("sign-in", {
|
|
36
|
-
method: "POST",
|
|
37
|
-
headers: {
|
|
38
|
-
"content-type": "application/json",
|
|
39
|
-
accept: "application/json"
|
|
40
|
-
},
|
|
41
|
-
body: JSON.stringify(credentials)
|
|
42
|
-
});
|
|
43
|
-
if (response.status === 401 || response.status === 429) return authError(response.status === 401 ? "invalidCredentials" : "tooManyAttempts", await describe(response, "sign-in"));
|
|
44
|
-
if (response.status >= 500) panic(await describe(response, "sign-in"));
|
|
45
|
-
if (response.status === 400) todo(await describe(response, "sign-in"));
|
|
46
|
-
if (!response.ok) throw new Error(`${await describe(response, "sign-in")}. Check SOFTR_APPLICATION_ID and that the app allows sign-in.`);
|
|
47
|
-
const raw = await response.text();
|
|
48
|
-
const body = signInBody.parse(raw.length === 0 ? {} : JSON.parse(raw));
|
|
49
|
-
if (body.authenticatorAppAction) todo(`Softr platform asked for an authenticator step (${body.authenticatorAppAction}) to finish the sign-in. This app requires an authenticator app, which the SDK cannot finish yet.`);
|
|
50
|
-
const token = body.jwt;
|
|
51
|
-
if (!token) todo("Softr platform answered the sign-in by emailing a one-time code. This app is configured for passwordless sign-in, which the SDK cannot finish yet.");
|
|
52
|
-
const user = await fetchVisitor(token);
|
|
53
|
-
if (!user) panic("Session token issued, but no user could be resolved for it.");
|
|
54
|
-
writeCookies([makeSessionTokenCookie(token), makeSessionDataCookie(user, token)]);
|
|
55
|
-
return {
|
|
56
|
-
success: true,
|
|
57
|
-
data: user
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Signs the visitor out and clears the session cookies.
|
|
62
|
-
*
|
|
63
|
-
* This revokes **every** outstanding session for that user in this app, on every
|
|
64
|
-
* device: the platform stamps `tokensInvalidatedAt` and rejects anything issued
|
|
65
|
-
* before it. There is no per-device sign-out.
|
|
66
|
-
*
|
|
67
|
-
* A platform that could not revoke leaves the caller signed out here and
|
|
68
|
-
* possibly still signed in elsewhere — loud outside production, logged in it,
|
|
69
|
-
* because the local half already succeeded.
|
|
70
|
-
*/
|
|
71
|
-
async function signOut() {
|
|
72
|
-
const token = readSessionToken();
|
|
73
|
-
clearSession();
|
|
74
|
-
if (!token) return;
|
|
75
|
-
const response = await platformFetch("sign-out", {
|
|
76
|
-
method: "POST",
|
|
77
|
-
headers: {
|
|
78
|
-
authorization: `Bearer ${token}`,
|
|
79
|
-
accept: "application/json"
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
softAssert(response.ok || response.status < 500, () => `Softr platform could not revoke the session: ${response.status}. This browser is signed out, but other devices may still be.`);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
//#endregion
|
|
86
|
-
//#region src/auth/methods/signup.ts
|
|
87
|
-
/**
|
|
88
|
-
* Starts a sign-up flow by emailing a verification code.
|
|
89
|
-
*
|
|
90
|
-
* User only exists once `confirmSignUp` returns the code.
|
|
91
|
-
*/
|
|
92
|
-
async function signUp(details) {
|
|
93
|
-
const response = await platformFetch("sign-up", {
|
|
94
|
-
method: "POST",
|
|
95
|
-
headers: {
|
|
96
|
-
"content-type": "application/json",
|
|
97
|
-
accept: "application/json"
|
|
98
|
-
},
|
|
99
|
-
body: JSON.stringify(signUpBody(details))
|
|
100
|
-
});
|
|
101
|
-
if (response.status >= 500) panic(await describe(response, "sign-up"));
|
|
102
|
-
if (response.status === 409) return authError("emailTaken", await describe(response, "sign-up"));
|
|
103
|
-
if (!response.ok) return authError("invalidDetails", await describe(response, "sign-up"));
|
|
104
|
-
return {
|
|
105
|
-
success: true,
|
|
106
|
-
data: null
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Finishes sign-up flow with the emailed code and signs the visitor in.
|
|
111
|
-
*
|
|
112
|
-
* The platform splits on how the app is configured: a code-only app creates the
|
|
113
|
-
* user on the second `sign-up` request and answers with a token, while a
|
|
114
|
-
* password app answers it with an empty 200 and expects the password on
|
|
115
|
-
* `users/new`.
|
|
116
|
-
*/
|
|
117
|
-
async function confirmSignUp(details) {
|
|
118
|
-
const verified = await platformFetch("sign-up", {
|
|
119
|
-
method: "POST",
|
|
120
|
-
headers: {
|
|
121
|
-
"content-type": "application/json",
|
|
122
|
-
accept: "application/json"
|
|
123
|
-
},
|
|
124
|
-
body: JSON.stringify({
|
|
125
|
-
...signUpBody(details),
|
|
126
|
-
token: details.code
|
|
127
|
-
})
|
|
128
|
-
});
|
|
129
|
-
if (verified.status >= 500) panic(await describe(verified, "sign-up"));
|
|
130
|
-
if (verified.status === 409) return authError("emailTaken", await describe(verified, "sign-up"));
|
|
131
|
-
if (!verified.ok) return authError("invalidCode", await describe(verified, "sign-up"));
|
|
132
|
-
const verifiedBody = await verified.text();
|
|
133
|
-
let token = tokenBody.parse(verifiedBody.length === 0 ? {} : JSON.parse(verifiedBody)).jwt;
|
|
134
|
-
if (!token) {
|
|
135
|
-
const created = await platformFetch("new", {
|
|
136
|
-
method: "POST",
|
|
137
|
-
headers: {
|
|
138
|
-
"content-type": "application/json",
|
|
139
|
-
accept: "application/json"
|
|
140
|
-
},
|
|
141
|
-
body: JSON.stringify({
|
|
142
|
-
...signUpBody(details),
|
|
143
|
-
password: details.password,
|
|
144
|
-
token: details.code
|
|
145
|
-
})
|
|
146
|
-
});
|
|
147
|
-
if (created.status >= 500) panic(await describe(created, "sign-up"));
|
|
148
|
-
if (created.status === 409) return authError("emailTaken", await describe(created, "sign-up"));
|
|
149
|
-
if (created.ok) {
|
|
150
|
-
const createdBody = await created.text();
|
|
151
|
-
token = tokenBody.parse(createdBody.length === 0 ? {} : JSON.parse(createdBody)).jwt;
|
|
152
|
-
} else {
|
|
153
|
-
const signedIn = await signIn({
|
|
154
|
-
email: details.email,
|
|
155
|
-
password: details.password
|
|
156
|
-
});
|
|
157
|
-
if (!signedIn.success) return authError("invalidCode", await describe(created, "sign-up"));
|
|
158
|
-
return signedIn;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
if (!token) todo("Softr platform accepted the sign-up and returned no session token. This app is configured for a sign-up flow the SDK cannot finish yet.");
|
|
162
|
-
const user = await fetchVisitor(token);
|
|
163
|
-
if (!user) panic("Session token issued, but no user could be resolved for it.");
|
|
164
|
-
writeCookies([makeSessionTokenCookie(token), makeSessionDataCookie(user, token)]);
|
|
165
|
-
return {
|
|
166
|
-
success: true,
|
|
167
|
-
data: user
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
/**
|
|
171
|
-
* `name` is dropped when absent rather than sent as `undefined`: the platform's
|
|
172
|
-
* validator runs on whatever key is present, and an explicit null is not the
|
|
173
|
-
* same request as no name at all.
|
|
174
|
-
*/
|
|
175
|
-
function signUpBody(details) {
|
|
176
|
-
return details.name === void 0 ? { email: details.email } : {
|
|
177
|
-
email: details.email,
|
|
178
|
-
name: details.name
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
const tokenBody = z.object({ jwt: z.string().nullish() });
|
|
182
|
-
|
|
183
|
-
//#endregion
|
|
184
|
-
//#region src/auth/server.ts
|
|
185
|
-
/**
|
|
186
|
-
* Server-only auth methods, should be called from the app's own `createServerFn` handlers.
|
|
187
|
-
*/
|
|
188
|
-
const auth = {
|
|
189
|
-
signIn,
|
|
190
|
-
signOut,
|
|
191
|
-
signUp,
|
|
192
|
-
confirmSignUp
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
//#endregion
|
|
196
|
-
//#region src/core/datasource.ts
|
|
197
|
-
const MAX_RECORD_COUNT = 100;
|
|
198
|
-
const COUNT_CNT = "COUNT_CNT";
|
|
199
|
-
const BASE_URL$2 = () => (import.meta.env.VITE_SOFTR_BASE_URL ?? "").replace(/\/$/, "");
|
|
200
|
-
async function listRecords(datasourceId, options = {}) {
|
|
201
|
-
const { select, where, orderBy } = options;
|
|
202
|
-
const count = Math.min(options.limit ?? 20, MAX_RECORD_COUNT);
|
|
203
|
-
const offset = options.offset ?? null;
|
|
204
|
-
const filterCriteria = where ? dealiasFilterCriteria(buildFilterCriteria(where), select ?? {}) : {};
|
|
205
|
-
const sortingOption = orderBy ? dealiasOrderBy(buildOrderBy(orderBy), select ?? {}) : void 0;
|
|
206
|
-
const res = await fetch(`${BASE_URL$2()}/api/datasources/${datasourceId}/records`, {
|
|
207
|
-
method: "POST",
|
|
208
|
-
headers: {
|
|
209
|
-
"Content-Type": "application/json",
|
|
210
|
-
"X-User-Timezone": options.timeZone ?? readUserTimezone() ?? "UTC",
|
|
211
|
-
"Authorization": `Bearer ${readSessionToken()}`
|
|
212
|
-
},
|
|
213
|
-
body: JSON.stringify({
|
|
214
|
-
options: { userLocale: "en-US" },
|
|
215
|
-
filterCriteria,
|
|
216
|
-
sortingOption,
|
|
217
|
-
pagingOption: {
|
|
218
|
-
offset,
|
|
219
|
-
count
|
|
220
|
-
}
|
|
221
|
-
})
|
|
222
|
-
});
|
|
223
|
-
if (!res.ok) throw new Error(`Softr datasource list failed for "${datasourceId}": ${res.status}`);
|
|
224
|
-
const payload = await res.json();
|
|
225
|
-
const nextOffset = payload.offset < payload.total ? payload.offset : null;
|
|
226
|
-
return {
|
|
227
|
-
records: payload.items.map((item) => ({
|
|
228
|
-
id: item.id,
|
|
229
|
-
fields: select ? aliasFields(item.fields, select) : item.fields
|
|
230
|
-
})),
|
|
231
|
-
total: payload.total,
|
|
232
|
-
nextOffset
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
async function getRecord(datasourceId, options) {
|
|
236
|
-
if (options.id !== void 0) {
|
|
237
|
-
if (!options.id) throw new Error(`Softr datasource get: id must not be empty`);
|
|
238
|
-
const res = await fetch(`${BASE_URL$2()}/api/datasources/${datasourceId}/records/${options.id}`, {
|
|
239
|
-
method: "POST",
|
|
240
|
-
headers: {
|
|
241
|
-
"Content-Type": "application/json",
|
|
242
|
-
"X-User-Timezone": options.timeZone ?? readUserTimezone() ?? "UTC",
|
|
243
|
-
"Authorization": `Bearer ${readSessionToken()}`
|
|
244
|
-
},
|
|
245
|
-
body: JSON.stringify({ options: { userLocale: "en-US" } })
|
|
246
|
-
});
|
|
247
|
-
if (!res.ok) throw new Error(`Softr datasource get failed for "${datasourceId}": ${res.status}`);
|
|
248
|
-
const contentLength = res.headers.get("Content-Length");
|
|
249
|
-
if (contentLength && parseInt(contentLength, 10) === 0) return null;
|
|
250
|
-
return res.json();
|
|
251
|
-
}
|
|
252
|
-
return (await listRecords(datasourceId, {
|
|
253
|
-
where: options.where,
|
|
254
|
-
limit: 1,
|
|
255
|
-
timeZone: options.timeZone
|
|
256
|
-
})).records[0] ?? null;
|
|
257
|
-
}
|
|
258
|
-
async function createRecord(datasourceId, options) {
|
|
259
|
-
const res = await fetch(`${BASE_URL$2()}/api/datasources/${datasourceId}/records`, {
|
|
260
|
-
method: "POST",
|
|
261
|
-
headers: {
|
|
262
|
-
"Content-Type": "application/json",
|
|
263
|
-
"Authorization": `Bearer ${readSessionToken()}`
|
|
264
|
-
},
|
|
265
|
-
body: JSON.stringify({ fields: options.fields })
|
|
266
|
-
});
|
|
267
|
-
if (!res.ok) throw new Error(`Softr datasource create failed for "${datasourceId}": ${res.status}`);
|
|
268
|
-
return (await res.json()).record;
|
|
269
|
-
}
|
|
270
|
-
async function updateRecord(datasourceId, options) {
|
|
271
|
-
if (!options.id) throw new Error(`Softr datasource update: id must not be empty`);
|
|
272
|
-
const res = await fetch(`${BASE_URL$2()}/api/datasources/${datasourceId}/records/${options.id}`, {
|
|
273
|
-
method: "PATCH",
|
|
274
|
-
headers: {
|
|
275
|
-
"Content-Type": "application/json",
|
|
276
|
-
"Authorization": `Bearer ${readSessionToken()}`
|
|
277
|
-
},
|
|
278
|
-
body: JSON.stringify({ fields: options.fields })
|
|
279
|
-
});
|
|
280
|
-
if (!res.ok) throw new Error(`Softr datasource update failed for "${datasourceId}": ${res.status}`);
|
|
281
|
-
return (await res.json()).record;
|
|
282
|
-
}
|
|
283
|
-
async function deleteRecord(datasourceId, id) {
|
|
284
|
-
const res = await fetch(`${BASE_URL$2()}/api/datasources/${datasourceId}/records/${id}`, {
|
|
285
|
-
method: "DELETE",
|
|
286
|
-
headers: { "Authorization": `Bearer ${readSessionToken()}` }
|
|
287
|
-
});
|
|
288
|
-
if (!res.ok) throw new Error(`Softr datasource delete failed for "${datasourceId}": ${res.status}`);
|
|
289
|
-
}
|
|
290
|
-
async function getMetric(datasourceId, options) {
|
|
291
|
-
const { select, where, aggregation, field } = options;
|
|
292
|
-
const fieldId = field ? select?.[field] ?? field : void 0;
|
|
293
|
-
const filterCriteria = where ? dealiasFilterCriteria(buildFilterCriteria(where), select ?? {}) : {};
|
|
294
|
-
const res = await fetch(`${BASE_URL$2()}/api/datasources/${datasourceId}/chartMetricData`, {
|
|
295
|
-
method: "POST",
|
|
296
|
-
headers: {
|
|
297
|
-
"Content-Type": "application/json",
|
|
298
|
-
"X-User-Timezone": options.timeZone ?? readUserTimezone() ?? "UTC",
|
|
299
|
-
"Authorization": `Bearer ${readSessionToken()}`
|
|
300
|
-
},
|
|
301
|
-
body: JSON.stringify({
|
|
302
|
-
options: { userLocale: "en-US" },
|
|
303
|
-
calculationField: {
|
|
304
|
-
field: fieldId,
|
|
305
|
-
aggregation
|
|
306
|
-
},
|
|
307
|
-
filterCriteria
|
|
308
|
-
})
|
|
309
|
-
});
|
|
310
|
-
if (!res.ok) throw new Error(`Softr datasource metric failed for "${datasourceId}": ${res.status}`);
|
|
311
|
-
const payload = await res.json();
|
|
312
|
-
const executionTimestamp = /* @__PURE__ */ new Date(payload.executionTimestamp * 1e3);
|
|
313
|
-
let value;
|
|
314
|
-
if (aggregation === "COUNT") value = payload.data.find((item) => COUNT_CNT in item)?.[COUNT_CNT] ?? null;
|
|
315
|
-
else {
|
|
316
|
-
if (!fieldId) throw new Error(`field is required for aggregation "${aggregation}"`);
|
|
317
|
-
value = payload.data.find((item) => fieldId in item)?.[fieldId] ?? null;
|
|
318
|
-
}
|
|
319
|
-
return {
|
|
320
|
-
value,
|
|
321
|
-
executionTimestamp
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
async function getChartData(datasourceId, options) {
|
|
325
|
-
const { select, where, groupBy, metrics, orderBy } = options;
|
|
326
|
-
const filterCriteria = where ? dealiasFilterCriteria(buildFilterCriteria(where), select ?? {}) : {};
|
|
327
|
-
const sortingOption = orderBy ? dealiasOrderBy(buildOrderBy(orderBy), select ?? {})[0] : void 0;
|
|
328
|
-
const groupByFields = (Array.isArray(groupBy) ? groupBy : [groupBy]).map((field) => ({ field: select?.[field] ?? field }));
|
|
329
|
-
const yAxisFields = Object.values(metrics).map(({ field, aggregation }) => ({
|
|
330
|
-
field: field ? select?.[field] ?? field : void 0,
|
|
331
|
-
aggregation
|
|
332
|
-
}));
|
|
333
|
-
const res = await fetch(`${BASE_URL$2()}/api/datasources/${datasourceId}/chartData`, {
|
|
334
|
-
method: "POST",
|
|
335
|
-
headers: {
|
|
336
|
-
"Content-Type": "application/json",
|
|
337
|
-
"X-User-Timezone": options.timeZone ?? readUserTimezone() ?? "UTC",
|
|
338
|
-
"Authorization": `Bearer ${readSessionToken()}`
|
|
339
|
-
},
|
|
340
|
-
body: JSON.stringify({
|
|
341
|
-
options: { userLocale: "en-US" },
|
|
342
|
-
filterCriteria,
|
|
343
|
-
sortingOption,
|
|
344
|
-
xAxisFields: groupByFields,
|
|
345
|
-
yAxisFields
|
|
346
|
-
})
|
|
347
|
-
});
|
|
348
|
-
if (!res.ok) throw new Error(`Softr datasource chartData failed for "${datasourceId}": ${res.status}`);
|
|
349
|
-
const payload = await res.json();
|
|
350
|
-
const executionTimestamp = /* @__PURE__ */ new Date(payload.executionTimestamp * 1e3);
|
|
351
|
-
const aliases = {};
|
|
352
|
-
const multipleMetrics = Object.keys(metrics).length > 1;
|
|
353
|
-
for (const [alias, { field, aggregation }] of Object.entries(metrics)) if (aggregation === "COUNT") aliases[COUNT_CNT] = alias;
|
|
354
|
-
else {
|
|
355
|
-
const fieldId = field ? select?.[field] ?? field : field;
|
|
356
|
-
if (fieldId) {
|
|
357
|
-
const suffix = multipleMetrics ? `_${aggregation.toLowerCase()}` : "";
|
|
358
|
-
aliases[fieldId + suffix] = alias;
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
for (const field of Array.isArray(groupBy) ? groupBy : [groupBy]) aliases[select?.[field] ?? field] = field;
|
|
362
|
-
return {
|
|
363
|
-
data: payload.data.map((item) => {
|
|
364
|
-
const row = {};
|
|
365
|
-
for (const [rawKey, alias] of Object.entries(aliases)) if (rawKey in item) row[alias] = item[rawKey];
|
|
366
|
-
return row;
|
|
367
|
-
}),
|
|
368
|
-
executionTimestamp
|
|
369
|
-
};
|
|
370
|
-
}
|
|
371
|
-
function aliasFields(fields, aliases) {
|
|
372
|
-
const result = {};
|
|
373
|
-
for (const [alias, fieldId] of Object.entries(aliases)) result[alias] = fields[fieldId];
|
|
374
|
-
return result;
|
|
375
|
-
}
|
|
376
|
-
const ds = {
|
|
377
|
-
list: listRecords,
|
|
378
|
-
get: getRecord,
|
|
379
|
-
create: createRecord,
|
|
380
|
-
update: updateRecord,
|
|
381
|
-
delete: deleteRecord,
|
|
382
|
-
metric: getMetric,
|
|
383
|
-
chartData: getChartData
|
|
384
|
-
};
|
|
385
|
-
|
|
386
|
-
//#endregion
|
|
387
|
-
//#region src/core/workflow.ts
|
|
388
|
-
const BASE_URL$1 = () => (import.meta.env.VITE_SOFTR_BASE_URL ?? "").replace(/\/$/, "");
|
|
389
|
-
async function triggerWorkflow(workflowId, payload) {
|
|
390
|
-
const res = await fetch(`${BASE_URL$1()}/api/workflows/${workflowId}/trigger`, {
|
|
391
|
-
method: "POST",
|
|
392
|
-
headers: {
|
|
393
|
-
"Authorization": `Bearer ${readSessionToken()}`,
|
|
394
|
-
...payload ? { "Content-Type": "application/json" } : {}
|
|
395
|
-
},
|
|
396
|
-
...payload ? { body: JSON.stringify(payload) } : {}
|
|
397
|
-
});
|
|
398
|
-
if (!res.ok) throw new Error(`Softr workflow trigger failed: ${res.status}`);
|
|
399
|
-
}
|
|
400
|
-
const workflow = { trigger: triggerWorkflow };
|
|
401
|
-
|
|
402
|
-
//#endregion
|
|
403
|
-
//#region src/core/integration.ts
|
|
404
|
-
const BASE_URL = () => (import.meta.env.VITE_SOFTR_BASE_URL ?? "").replace(/\/$/, "");
|
|
405
|
-
async function integrationFetch(integrationId, input, init) {
|
|
406
|
-
const req = new Request(input, init);
|
|
407
|
-
if (req.signal.aborted) throw req.signal.reason ?? new DOMException("The operation was aborted.", "AbortError");
|
|
408
|
-
const reqHeaders = {};
|
|
409
|
-
new Headers(req.headers).forEach((value, key) => {
|
|
410
|
-
reqHeaders[key] = value;
|
|
411
|
-
});
|
|
412
|
-
const payload = {
|
|
413
|
-
method: req.method,
|
|
414
|
-
url: req.url,
|
|
415
|
-
headers: reqHeaders
|
|
416
|
-
};
|
|
417
|
-
const method = req.method.toUpperCase();
|
|
418
|
-
if (method !== "GET" && method !== "HEAD" && req.body !== null) payload.body = await req.clone().text();
|
|
419
|
-
return fetch(`${BASE_URL()}/api/integrations/${integrationId}/proxy`, {
|
|
420
|
-
method: "POST",
|
|
421
|
-
headers: {
|
|
422
|
-
"Content-Type": "application/json",
|
|
423
|
-
"Authorization": `Bearer ${readSessionToken()}`
|
|
424
|
-
},
|
|
425
|
-
body: JSON.stringify(payload),
|
|
426
|
-
signal: req.signal
|
|
427
|
-
});
|
|
428
|
-
}
|
|
429
|
-
const integration = { fetch: integrationFetch };
|
|
430
|
-
|
|
431
|
-
//#endregion
|
|
432
7
|
export { auth, ds, integration, workflow };
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { AnyConstructable, ConstructableOrderBy } from "./core/query.js";
|
|
2
|
+
import { SoftrUserGroupId } from "./auth/user.js";
|
|
3
|
+
|
|
4
|
+
//#region src/types.d.ts
|
|
5
|
+
interface SoftrUser {
|
|
6
|
+
id: string;
|
|
7
|
+
email: string;
|
|
8
|
+
name: string;
|
|
9
|
+
userGroupIds: Array<SoftrUserGroupId>;
|
|
10
|
+
}
|
|
11
|
+
interface DatasourcePermissions {
|
|
12
|
+
read: boolean;
|
|
13
|
+
create: boolean;
|
|
14
|
+
update: boolean;
|
|
15
|
+
delete: boolean;
|
|
16
|
+
}
|
|
17
|
+
interface SoftrPermissions {
|
|
18
|
+
datasources: Record<string, DatasourcePermissions>;
|
|
19
|
+
workflows: Record<string, boolean>;
|
|
20
|
+
}
|
|
21
|
+
interface SoftrMe {
|
|
22
|
+
user: SoftrUser | null;
|
|
23
|
+
permissions: SoftrPermissions;
|
|
24
|
+
}
|
|
25
|
+
type GetByIdOptions = {
|
|
26
|
+
id: string;
|
|
27
|
+
where?: never;
|
|
28
|
+
timeZone?: string;
|
|
29
|
+
};
|
|
30
|
+
type GetByWhereOptions = {
|
|
31
|
+
id?: never;
|
|
32
|
+
where: AnyConstructable;
|
|
33
|
+
timeZone?: string;
|
|
34
|
+
};
|
|
35
|
+
type GetOptions = GetByIdOptions | GetByWhereOptions;
|
|
36
|
+
interface ListOptions {
|
|
37
|
+
/**
|
|
38
|
+
* Maps human-readable aliases to Softr field IDs.
|
|
39
|
+
* When provided, returned fields are keyed by alias instead of raw field ID.
|
|
40
|
+
*/
|
|
41
|
+
select?: Record<string, string>;
|
|
42
|
+
where?: AnyConstructable;
|
|
43
|
+
orderBy?: ConstructableOrderBy | ConstructableOrderBy[];
|
|
44
|
+
/**
|
|
45
|
+
* Number of records to return per page. Defaults to 20, max 100.
|
|
46
|
+
*/
|
|
47
|
+
limit?: number;
|
|
48
|
+
/**
|
|
49
|
+
* Offset to start from. Pass the `nextOffset` from a previous result to paginate.
|
|
50
|
+
* Omit or pass `null` to start from the beginning.
|
|
51
|
+
*/
|
|
52
|
+
offset?: number | null;
|
|
53
|
+
/** Override the timezone for this request. */
|
|
54
|
+
timeZone?: string;
|
|
55
|
+
}
|
|
56
|
+
interface ListResult<T = SoftrRecord> {
|
|
57
|
+
records: T[];
|
|
58
|
+
total: number;
|
|
59
|
+
/** Offset to pass as `offset` in the next call, or `null` when there are no more pages. */
|
|
60
|
+
nextOffset: number | null;
|
|
61
|
+
}
|
|
62
|
+
interface SoftrRecord {
|
|
63
|
+
id: string;
|
|
64
|
+
fields: Record<string, unknown>;
|
|
65
|
+
}
|
|
66
|
+
type AggregationFunction = 'SUM' | 'AVG' | 'MAX' | 'MIN' | 'COUNT' | 'DISTINCT';
|
|
67
|
+
interface MetricOptions {
|
|
68
|
+
select?: Record<string, string>;
|
|
69
|
+
where?: AnyConstructable;
|
|
70
|
+
aggregation: AggregationFunction;
|
|
71
|
+
/** Field alias (resolved via select) or raw field ID. Not required for COUNT. */
|
|
72
|
+
field?: string;
|
|
73
|
+
/** Override the timezone for this request. */
|
|
74
|
+
timeZone?: string;
|
|
75
|
+
}
|
|
76
|
+
interface MetricResult {
|
|
77
|
+
value: number | null;
|
|
78
|
+
executionTimestamp: Date;
|
|
79
|
+
}
|
|
80
|
+
interface ChartDataOptions {
|
|
81
|
+
select?: Record<string, string>;
|
|
82
|
+
where?: AnyConstructable;
|
|
83
|
+
/** Field alias (resolved via select) or raw field ID. */
|
|
84
|
+
groupBy: string | string[];
|
|
85
|
+
metrics: Record<string, {
|
|
86
|
+
field?: string;
|
|
87
|
+
aggregation: AggregationFunction;
|
|
88
|
+
}>;
|
|
89
|
+
orderBy?: ConstructableOrderBy;
|
|
90
|
+
/** Override the timezone for this request. */
|
|
91
|
+
timeZone?: string;
|
|
92
|
+
}
|
|
93
|
+
interface ChartDataResult {
|
|
94
|
+
data: Record<string, unknown>[];
|
|
95
|
+
executionTimestamp: Date;
|
|
96
|
+
}
|
|
97
|
+
interface CreateOptions {
|
|
98
|
+
/**
|
|
99
|
+
* Maps human-readable aliases to Softr field IDs.
|
|
100
|
+
* When provided, `data` keys are interpreted as aliases.
|
|
101
|
+
*/
|
|
102
|
+
select?: Record<string, string>;
|
|
103
|
+
data: Record<string, unknown>;
|
|
104
|
+
}
|
|
105
|
+
interface UpdateOptions {
|
|
106
|
+
id: string;
|
|
107
|
+
/**
|
|
108
|
+
* Maps human-readable aliases to Softr field IDs.
|
|
109
|
+
* When provided, `data` keys are interpreted as aliases.
|
|
110
|
+
*/
|
|
111
|
+
select?: Record<string, string>;
|
|
112
|
+
data: Record<string, unknown>;
|
|
113
|
+
}
|
|
114
|
+
//#endregion
|
|
115
|
+
export { AggregationFunction, ChartDataOptions, ChartDataResult, CreateOptions, DatasourcePermissions, GetOptions, ListOptions, ListResult, MetricOptions, MetricResult, SoftrMe, SoftrPermissions, SoftrRecord, SoftrUser, UpdateOptions };
|
package/dist/vite.d.ts
CHANGED
|
@@ -1,26 +1,6 @@
|
|
|
1
|
-
import "
|
|
1
|
+
import { Manifest, ManifestNode } from "./routes/manifest.js";
|
|
2
2
|
import { PluginOption } from "vite";
|
|
3
3
|
|
|
4
|
-
//#region src/routes/discover.d.ts
|
|
5
|
-
|
|
6
|
-
type VisibilityValue = string | Array<VisibilityValue> | {
|
|
7
|
-
[key: string]: VisibilityValue;
|
|
8
|
-
};
|
|
9
|
-
//#endregion
|
|
10
|
-
//#region src/routes/manifest.d.ts
|
|
11
|
-
type ManifestNode = {
|
|
12
|
-
routePath: string;
|
|
13
|
-
filePath: string;
|
|
14
|
-
_fsRouteType?: string;
|
|
15
|
-
inferFullPath: string | null;
|
|
16
|
-
createFileRouteProps: Array<string>;
|
|
17
|
-
visibility: VisibilityValue | null;
|
|
18
|
-
children: Array<ManifestNode>;
|
|
19
|
-
};
|
|
20
|
-
type Manifest = {
|
|
21
|
-
root: ManifestNode;
|
|
22
|
-
};
|
|
23
|
-
//#endregion
|
|
24
4
|
//#region src/vite.d.ts
|
|
25
5
|
declare const MANIFEST_RELATIVE_PATH: string;
|
|
26
6
|
/** Writes `<root>/dist/routes.json` and returns its path. Throws on a rule it cannot read. */
|