@instantdb/admin 0.22.96-experimental.drewh-ts-target.20761590091.1 → 0.22.96-experimental.surgical.20765817844.1
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/commonjs/__types__/typesTests.js +82 -63
- package/dist/commonjs/__types__/typesTests.js.map +1 -1
- package/dist/commonjs/index.js +453 -472
- package/dist/commonjs/index.js.map +1 -1
- package/dist/commonjs/subscribe.js +19 -17
- package/dist/commonjs/subscribe.js.map +1 -1
- package/dist/esm/__types__/typesTests.js +82 -63
- package/dist/esm/__types__/typesTests.js.map +1 -1
- package/dist/esm/index.js +453 -472
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/subscribe.js +19 -17
- package/dist/esm/subscribe.js.map +1 -1
- package/package.json +3 -3
package/dist/esm/index.js
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
1
10
|
import { tx, lookup, getOps, i, id, txInit, version as coreVersion, InstantAPIError, validateQuery, validateTransactions, createInstantRouteHandler, } from '@instantdb/core';
|
|
2
11
|
import version from "./version.js";
|
|
3
12
|
import { subscribe, } from "./subscribe.js";
|
|
@@ -5,14 +14,14 @@ function configWithDefaults(config) {
|
|
|
5
14
|
const defaultConfig = {
|
|
6
15
|
apiURI: 'https://api.instantdb.com',
|
|
7
16
|
};
|
|
8
|
-
const r = {
|
|
17
|
+
const r = Object.assign(Object.assign({}, defaultConfig), config);
|
|
9
18
|
return r;
|
|
10
19
|
}
|
|
11
20
|
function instantConfigWithDefaults(config) {
|
|
12
21
|
const defaultConfig = {
|
|
13
22
|
apiURI: 'https://api.instantdb.com',
|
|
14
23
|
};
|
|
15
|
-
const r = {
|
|
24
|
+
const r = Object.assign(Object.assign({}, defaultConfig), config);
|
|
16
25
|
return r;
|
|
17
26
|
}
|
|
18
27
|
function withImpersonation(headers, opts) {
|
|
@@ -75,32 +84,32 @@ function isNextJSVersionThatCachesFetchByDefault() {
|
|
|
75
84
|
function getDefaultFetchOpts() {
|
|
76
85
|
return isNextJSVersionThatCachesFetchByDefault() ? { cache: 'no-store' } : {};
|
|
77
86
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
function jsonReject(rejectFn, res) {
|
|
88
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
89
|
+
const body = yield res.text();
|
|
90
|
+
try {
|
|
91
|
+
const json = JSON.parse(body);
|
|
92
|
+
return rejectFn(new InstantAPIError({ status: res.status, body: json }));
|
|
93
|
+
}
|
|
94
|
+
catch (_e) {
|
|
95
|
+
return rejectFn(new InstantAPIError({
|
|
96
|
+
status: res.status,
|
|
97
|
+
body: { type: undefined, message: body },
|
|
98
|
+
}));
|
|
99
|
+
}
|
|
100
|
+
});
|
|
90
101
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
return jsonReject((x) => Promise.reject(x), res);
|
|
102
|
+
function jsonFetch(input, init) {
|
|
103
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
104
|
+
const defaultFetchOpts = getDefaultFetchOpts();
|
|
105
|
+
const headers = Object.assign(Object.assign({}, ((init === null || init === void 0 ? void 0 : init.headers) || {})), { 'Instant-Admin-Version': version, 'Instant-Core-Version': coreVersion });
|
|
106
|
+
const res = yield fetch(input, Object.assign(Object.assign(Object.assign({}, defaultFetchOpts), init), { headers }));
|
|
107
|
+
if (res.status === 200) {
|
|
108
|
+
const json = yield res.json();
|
|
109
|
+
return Promise.resolve(json);
|
|
110
|
+
}
|
|
111
|
+
return jsonReject((x) => Promise.reject(x), res);
|
|
112
|
+
});
|
|
104
113
|
}
|
|
105
114
|
/**
|
|
106
115
|
*
|
|
@@ -132,12 +141,8 @@ function init(
|
|
|
132
141
|
// Allows config with missing `useDateObjects`, but keeps `UseDates`
|
|
133
142
|
// as a non-nullable in the InstantConfig type.
|
|
134
143
|
config) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
appId: config.appId?.trim(),
|
|
138
|
-
adminToken: config.adminToken?.trim(),
|
|
139
|
-
useDateObjects: (config.useDateObjects ?? false),
|
|
140
|
-
};
|
|
144
|
+
var _a, _b, _c;
|
|
145
|
+
const configStrict = Object.assign(Object.assign({}, config), { appId: (_a = config.appId) === null || _a === void 0 ? void 0 : _a.trim(), adminToken: (_b = config.adminToken) === null || _b === void 0 ? void 0 : _b.trim(), useDateObjects: ((_c = config.useDateObjects) !== null && _c !== void 0 ? _c : false) });
|
|
141
146
|
return new InstantAdminDatabase(configStrict);
|
|
142
147
|
}
|
|
143
148
|
/**
|
|
@@ -160,171 +165,175 @@ function steps(inputChunks) {
|
|
|
160
165
|
return chunks.flatMap(getOps);
|
|
161
166
|
}
|
|
162
167
|
class Rooms {
|
|
163
|
-
config;
|
|
164
168
|
constructor(config) {
|
|
165
169
|
this.config = config;
|
|
166
170
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
+
getPresence(roomType, roomId) {
|
|
172
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
173
|
+
const res = yield jsonFetch(`${this.config.apiURI}/admin/rooms/presence?room-type=${String(roomType)}&room-id=${roomId}`, {
|
|
174
|
+
method: 'GET',
|
|
175
|
+
headers: authorizedHeaders(this.config),
|
|
176
|
+
});
|
|
177
|
+
return res.sessions || {};
|
|
171
178
|
});
|
|
172
|
-
return res.sessions || {};
|
|
173
179
|
}
|
|
174
180
|
}
|
|
175
181
|
class Auth {
|
|
176
|
-
config;
|
|
177
182
|
constructor(config) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
headers: authorizedHeaders(this.config),
|
|
198
|
-
body: JSON.stringify({ email }),
|
|
183
|
+
/**
|
|
184
|
+
* Generates a magic code for the user with the given email.
|
|
185
|
+
* This is useful if you want to use your own email provider
|
|
186
|
+
* to send magic codes.
|
|
187
|
+
*
|
|
188
|
+
* @example
|
|
189
|
+
* // Generate a magic code
|
|
190
|
+
* const { code } = await db.auth.generateMagicCode({ email })
|
|
191
|
+
* // Send the magic code to the user with your own email provider
|
|
192
|
+
* await customEmailProvider.sendMagicCode(email, code)
|
|
193
|
+
*
|
|
194
|
+
* @see https://instantdb.com/docs/backend#custom-magic-codes
|
|
195
|
+
*/
|
|
196
|
+
this.generateMagicCode = (email) => __awaiter(this, void 0, void 0, function* () {
|
|
197
|
+
return jsonFetch(`${this.config.apiURI}/admin/magic_code`, {
|
|
198
|
+
method: 'POST',
|
|
199
|
+
headers: authorizedHeaders(this.config),
|
|
200
|
+
body: JSON.stringify({ email }),
|
|
201
|
+
});
|
|
199
202
|
});
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
203
|
+
/**
|
|
204
|
+
* Sends a magic code to the user with the given email.
|
|
205
|
+
* This uses Instant's built-in email provider.
|
|
206
|
+
*
|
|
207
|
+
* @example
|
|
208
|
+
* // Send an email to user with magic code
|
|
209
|
+
* await db.auth.sendMagicCode({ email })
|
|
210
|
+
*
|
|
211
|
+
* @see https://instantdb.com/docs/backend#custom-magic-codes
|
|
212
|
+
*/
|
|
213
|
+
this.sendMagicCode = (email) => __awaiter(this, void 0, void 0, function* () {
|
|
214
|
+
return jsonFetch(`${this.config.apiURI}/admin/send_magic_code`, {
|
|
215
|
+
method: 'POST',
|
|
216
|
+
headers: authorizedHeaders(this.config),
|
|
217
|
+
body: JSON.stringify({ email }),
|
|
218
|
+
});
|
|
216
219
|
});
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
220
|
+
/**
|
|
221
|
+
* Verifies a magic code for the user with the given email.
|
|
222
|
+
*
|
|
223
|
+
* @example
|
|
224
|
+
* const user = await db.auth.verifyMagicCode({ email, code })
|
|
225
|
+
* console.log("Verified user:", user)
|
|
226
|
+
*
|
|
227
|
+
* @see https://instantdb.com/docs/backend#custom-magic-codes
|
|
228
|
+
*/
|
|
229
|
+
this.verifyMagicCode = (email, code) => __awaiter(this, void 0, void 0, function* () {
|
|
230
|
+
const { user } = yield jsonFetch(`${this.config.apiURI}/admin/verify_magic_code`, {
|
|
231
|
+
method: 'POST',
|
|
232
|
+
headers: authorizedHeaders(this.config),
|
|
233
|
+
body: JSON.stringify({ email, code }),
|
|
234
|
+
});
|
|
235
|
+
return user;
|
|
232
236
|
});
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
237
|
+
/**
|
|
238
|
+
* Verifies a given token and returns the associated user.
|
|
239
|
+
*
|
|
240
|
+
* This is often useful for writing custom endpoints, where you need
|
|
241
|
+
* to authenticate users.
|
|
242
|
+
*
|
|
243
|
+
* @example
|
|
244
|
+
* app.post('/custom_endpoint', async (req, res) => {
|
|
245
|
+
* const user = await db.auth.verifyToken(req.headers['token'])
|
|
246
|
+
* if (!user) {
|
|
247
|
+
* return res.status(401).send('Uh oh, you are not authenticated')
|
|
248
|
+
* }
|
|
249
|
+
* // ...
|
|
250
|
+
* })
|
|
251
|
+
* @see https://instantdb.com/docs/backend#custom-endpoints
|
|
252
|
+
*/
|
|
253
|
+
this.verifyToken = (token) => __awaiter(this, void 0, void 0, function* () {
|
|
254
|
+
const res = yield jsonFetch(`${this.config.apiURI}/runtime/auth/verify_refresh_token`, {
|
|
255
|
+
method: 'POST',
|
|
256
|
+
headers: { 'content-type': 'application/json' },
|
|
257
|
+
body: JSON.stringify({
|
|
258
|
+
'app-id': this.config.appId,
|
|
259
|
+
'refresh-token': token,
|
|
260
|
+
}),
|
|
261
|
+
});
|
|
262
|
+
return res.user;
|
|
241
263
|
});
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
body: JSON.stringify({
|
|
265
|
-
'app-id': this.config.appId,
|
|
266
|
-
'refresh-token': token,
|
|
267
|
-
}),
|
|
264
|
+
/**
|
|
265
|
+
* Retrieves an app user by id, email, or refresh token.
|
|
266
|
+
*
|
|
267
|
+
* @example
|
|
268
|
+
* try {
|
|
269
|
+
* const user = await db.auth.getUser({ email })
|
|
270
|
+
* console.log("Found user:", user)
|
|
271
|
+
* } catch (err) {
|
|
272
|
+
* console.error("Failed to retrieve user:", err.message);
|
|
273
|
+
* }
|
|
274
|
+
*
|
|
275
|
+
* @see https://instantdb.com/docs/backend#retrieve-a-user
|
|
276
|
+
*/
|
|
277
|
+
this.getUser = (params) => __awaiter(this, void 0, void 0, function* () {
|
|
278
|
+
const qs = Object.entries(params)
|
|
279
|
+
.map(([k, v]) => `${k}=${encodeURIComponent(v)}`)
|
|
280
|
+
.join('&');
|
|
281
|
+
const response = yield jsonFetch(`${this.config.apiURI}/admin/users?${qs}`, {
|
|
282
|
+
method: 'GET',
|
|
283
|
+
headers: authorizedHeaders(this.config),
|
|
284
|
+
});
|
|
285
|
+
return response.user;
|
|
268
286
|
});
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
.map(([k, v]) => `${k}=${
|
|
287
|
-
.
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
287
|
+
/**
|
|
288
|
+
* Deletes an app user by id, email, or refresh token.
|
|
289
|
+
*
|
|
290
|
+
* NB: This _only_ deletes the user; it does not delete all user data.
|
|
291
|
+
* You will need to handle this manually.
|
|
292
|
+
*
|
|
293
|
+
* @example
|
|
294
|
+
* try {
|
|
295
|
+
* const deletedUser = await db.auth.deleteUser({ email })
|
|
296
|
+
* console.log("Deleted user:", deletedUser)
|
|
297
|
+
* } catch (err) {
|
|
298
|
+
* console.error("Failed to delete user:", err.message);
|
|
299
|
+
* }
|
|
300
|
+
*
|
|
301
|
+
* @see https://instantdb.com/docs/backend#delete-a-user
|
|
302
|
+
*/
|
|
303
|
+
this.deleteUser = (params) => __awaiter(this, void 0, void 0, function* () {
|
|
304
|
+
const qs = Object.entries(params).map(([k, v]) => `${k}=${v}`);
|
|
305
|
+
const response = yield jsonFetch(`${this.config.apiURI}/admin/users?${qs}`, {
|
|
306
|
+
method: 'DELETE',
|
|
307
|
+
headers: authorizedHeaders(this.config),
|
|
308
|
+
});
|
|
309
|
+
return response.deleted;
|
|
291
310
|
});
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
* } catch (err) {
|
|
305
|
-
* console.error("Failed to delete user:", err.message);
|
|
306
|
-
* }
|
|
307
|
-
*
|
|
308
|
-
* @see https://instantdb.com/docs/backend#delete-a-user
|
|
309
|
-
*/
|
|
310
|
-
deleteUser = async (params) => {
|
|
311
|
-
const qs = Object.entries(params).map(([k, v]) => `${k}=${v}`);
|
|
312
|
-
const response = await jsonFetch(`${this.config.apiURI}/admin/users?${qs}`, {
|
|
313
|
-
method: 'DELETE',
|
|
314
|
-
headers: authorizedHeaders(this.config),
|
|
311
|
+
this.config = config;
|
|
312
|
+
this.createToken = this.createToken.bind(this);
|
|
313
|
+
}
|
|
314
|
+
createToken(input) {
|
|
315
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
316
|
+
const body = typeof input === 'string' ? { email: input } : input;
|
|
317
|
+
const ret = yield jsonFetch(`${this.config.apiURI}/admin/refresh_tokens`, {
|
|
318
|
+
method: 'POST',
|
|
319
|
+
headers: authorizedHeaders(this.config),
|
|
320
|
+
body: JSON.stringify(body),
|
|
321
|
+
});
|
|
322
|
+
return ret.user.refresh_token;
|
|
315
323
|
});
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
324
|
+
}
|
|
325
|
+
signOut(input) {
|
|
326
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
327
|
+
// If input is a string, we assume it's an email.
|
|
328
|
+
// This is because of backwards compatibility: we used to only
|
|
329
|
+
// accept email strings. Eventually we can remove this
|
|
330
|
+
const params = typeof input === 'string' ? { email: input } : input;
|
|
331
|
+
const config = this.config;
|
|
332
|
+
yield jsonFetch(`${config.apiURI}/admin/sign_out`, {
|
|
333
|
+
method: 'POST',
|
|
334
|
+
headers: authorizedHeaders(config),
|
|
335
|
+
body: JSON.stringify(params),
|
|
336
|
+
});
|
|
328
337
|
});
|
|
329
338
|
}
|
|
330
339
|
}
|
|
@@ -337,137 +346,127 @@ const isWebReadable = (v) => v && typeof v.getReader === 'function';
|
|
|
337
346
|
* Functions to manage file storage.
|
|
338
347
|
*/
|
|
339
348
|
class Storage {
|
|
340
|
-
config;
|
|
341
|
-
impersonationOpts;
|
|
342
349
|
constructor(config, impersonationOpts) {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
const headers = {
|
|
356
|
-
...authorizedHeaders(this.config, this.impersonationOpts),
|
|
357
|
-
path,
|
|
358
|
-
};
|
|
359
|
-
if (metadata.contentDisposition) {
|
|
360
|
-
headers['content-disposition'] = metadata.contentDisposition;
|
|
361
|
-
}
|
|
362
|
-
// headers.content-type will become "undefined" (string)
|
|
363
|
-
// if not removed from the object
|
|
364
|
-
delete headers['content-type'];
|
|
365
|
-
if (metadata.contentType) {
|
|
366
|
-
headers['content-type'] = metadata.contentType;
|
|
367
|
-
}
|
|
368
|
-
let duplex;
|
|
369
|
-
if (isNodeReadable(file)) {
|
|
370
|
-
duplex = 'half'; // one-way stream
|
|
371
|
-
}
|
|
372
|
-
if (isNodeReadable(file) || isWebReadable(file)) {
|
|
373
|
-
if (!metadata.fileSize) {
|
|
374
|
-
throw new Error('fileSize is required in metadata when uploading streams');
|
|
350
|
+
/**
|
|
351
|
+
* Uploads file at the provided path. Accepts a Buffer or a Readable stream.
|
|
352
|
+
*
|
|
353
|
+
* @see https://instantdb.com/docs/storage
|
|
354
|
+
* @example
|
|
355
|
+
* const buffer = fs.readFileSync('demo.png');
|
|
356
|
+
* const isSuccess = await db.storage.uploadFile('photos/demo.png', buffer);
|
|
357
|
+
*/
|
|
358
|
+
this.uploadFile = (path_1, file_1, ...args_1) => __awaiter(this, [path_1, file_1, ...args_1], void 0, function* (path, file, metadata = {}) {
|
|
359
|
+
const headers = Object.assign(Object.assign({}, authorizedHeaders(this.config, this.impersonationOpts)), { path });
|
|
360
|
+
if (metadata.contentDisposition) {
|
|
361
|
+
headers['content-disposition'] = metadata.contentDisposition;
|
|
375
362
|
}
|
|
376
|
-
headers
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
return jsonFetch(`${this.config.apiURI}/admin/storage/files?filename=${encodeURIComponent(pathname)}`, {
|
|
395
|
-
method: 'DELETE',
|
|
396
|
-
headers: authorizedHeaders(this.config, this.impersonationOpts),
|
|
363
|
+
// headers.content-type will become "undefined" (string)
|
|
364
|
+
// if not removed from the object
|
|
365
|
+
delete headers['content-type'];
|
|
366
|
+
if (metadata.contentType) {
|
|
367
|
+
headers['content-type'] = metadata.contentType;
|
|
368
|
+
}
|
|
369
|
+
let duplex;
|
|
370
|
+
if (isNodeReadable(file)) {
|
|
371
|
+
duplex = 'half'; // one-way stream
|
|
372
|
+
}
|
|
373
|
+
if (isNodeReadable(file) || isWebReadable(file)) {
|
|
374
|
+
if (!metadata.fileSize) {
|
|
375
|
+
throw new Error('fileSize is required in metadata when uploading streams');
|
|
376
|
+
}
|
|
377
|
+
headers['content-length'] = metadata.fileSize.toString();
|
|
378
|
+
}
|
|
379
|
+
let options = Object.assign({ method: 'PUT', headers, body: file }, (duplex && { duplex }));
|
|
380
|
+
return jsonFetch(`${this.config.apiURI}/admin/storage/upload`, options);
|
|
397
381
|
});
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
body: JSON.stringify({ filenames: pathnames }),
|
|
382
|
+
/**
|
|
383
|
+
* Deletes a file by its path name (e.g. "photos/demo.png").
|
|
384
|
+
*
|
|
385
|
+
* @see https://instantdb.com/docs/storage
|
|
386
|
+
* @example
|
|
387
|
+
* await db.storage.delete("photos/demo.png");
|
|
388
|
+
*/
|
|
389
|
+
this.delete = (pathname) => __awaiter(this, void 0, void 0, function* () {
|
|
390
|
+
return jsonFetch(`${this.config.apiURI}/admin/storage/files?filename=${encodeURIComponent(pathname)}`, {
|
|
391
|
+
method: 'DELETE',
|
|
392
|
+
headers: authorizedHeaders(this.config, this.impersonationOpts),
|
|
393
|
+
});
|
|
411
394
|
});
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
})
|
|
395
|
+
/**
|
|
396
|
+
* Deletes multiple files by their path names (e.g. "photos/demo.png", "essays/demo.txt").
|
|
397
|
+
*
|
|
398
|
+
* @see https://instantdb.com/docs/storage
|
|
399
|
+
* @example
|
|
400
|
+
* await db.storage.deleteMany(["images/1.png", "images/2.png", "images/3.png"]);
|
|
401
|
+
*/
|
|
402
|
+
this.deleteMany = (pathnames) => __awaiter(this, void 0, void 0, function* () {
|
|
403
|
+
return jsonFetch(`${this.config.apiURI}/admin/storage/files/delete`, {
|
|
404
|
+
method: 'POST',
|
|
405
|
+
headers: authorizedHeaders(this.config, this.impersonationOpts),
|
|
406
|
+
body: JSON.stringify({ filenames: pathnames }),
|
|
407
|
+
});
|
|
425
408
|
});
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
409
|
+
/**
|
|
410
|
+
* @deprecated. This method will be removed in the future. Use `uploadFile`
|
|
411
|
+
* instead
|
|
412
|
+
*/
|
|
413
|
+
this.upload = (pathname_1, file_1, ...args_1) => __awaiter(this, [pathname_1, file_1, ...args_1], void 0, function* (pathname, file, metadata = {}) {
|
|
414
|
+
const { data: presignedUrl } = yield jsonFetch(`${this.config.apiURI}/admin/storage/signed-upload-url`, {
|
|
415
|
+
method: 'POST',
|
|
416
|
+
headers: authorizedHeaders(this.config),
|
|
417
|
+
body: JSON.stringify({
|
|
418
|
+
app_id: this.config.appId,
|
|
419
|
+
filename: pathname,
|
|
420
|
+
}),
|
|
421
|
+
});
|
|
422
|
+
const headers = {};
|
|
423
|
+
const contentType = metadata.contentType;
|
|
424
|
+
if (contentType) {
|
|
425
|
+
headers['Content-Type'] = contentType;
|
|
426
|
+
}
|
|
427
|
+
const { ok } = yield fetch(presignedUrl, {
|
|
428
|
+
method: 'PUT',
|
|
429
|
+
body: file,
|
|
430
|
+
headers,
|
|
431
|
+
});
|
|
432
|
+
return ok;
|
|
435
433
|
});
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
434
|
+
/**
|
|
435
|
+
* @deprecated. This method will be removed in the future. Use `query` instead
|
|
436
|
+
* @example
|
|
437
|
+
* const files = await db.query({ $files: {}})
|
|
438
|
+
*/
|
|
439
|
+
this.list = () => __awaiter(this, void 0, void 0, function* () {
|
|
440
|
+
const { data } = yield jsonFetch(`${this.config.apiURI}/admin/storage/files`, {
|
|
441
|
+
method: 'GET',
|
|
442
|
+
headers: authorizedHeaders(this.config),
|
|
443
|
+
});
|
|
444
|
+
return data;
|
|
447
445
|
});
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
446
|
+
/**
|
|
447
|
+
* @deprecated. getDownloadUrl will be removed in the future.
|
|
448
|
+
* Use `query` instead to query and fetch for valid urls
|
|
449
|
+
*
|
|
450
|
+
* db.useQuery({
|
|
451
|
+
* $files: {
|
|
452
|
+
* $: {
|
|
453
|
+
* where: {
|
|
454
|
+
* path: "moop.png"
|
|
455
|
+
* }
|
|
456
|
+
* }
|
|
457
|
+
* }
|
|
458
|
+
* })
|
|
459
|
+
*/
|
|
460
|
+
this.getDownloadUrl = (pathname) => __awaiter(this, void 0, void 0, function* () {
|
|
461
|
+
const { data } = yield jsonFetch(`${this.config.apiURI}/admin/storage/signed-download-url?app_id=${this.config.appId}&filename=${encodeURIComponent(pathname)}`, {
|
|
462
|
+
method: 'GET',
|
|
463
|
+
headers: authorizedHeaders(this.config),
|
|
464
|
+
});
|
|
465
|
+
return data;
|
|
468
466
|
});
|
|
469
|
-
|
|
470
|
-
|
|
467
|
+
this.config = config;
|
|
468
|
+
this.impersonationOpts = impersonationOpts;
|
|
469
|
+
}
|
|
471
470
|
}
|
|
472
471
|
/**
|
|
473
472
|
*
|
|
@@ -479,72 +478,158 @@ class Storage {
|
|
|
479
478
|
* const db = init({ appId: "my-app-id", adminToken: "my-admin-token" })
|
|
480
479
|
*/
|
|
481
480
|
class InstantAdminDatabase {
|
|
482
|
-
config;
|
|
483
|
-
auth;
|
|
484
|
-
storage;
|
|
485
|
-
rooms;
|
|
486
|
-
impersonationOpts;
|
|
487
|
-
tx = txInit();
|
|
488
481
|
constructor(_config) {
|
|
482
|
+
this.tx = txInit();
|
|
483
|
+
/**
|
|
484
|
+
* Sometimes you want to scope queries to a specific user.
|
|
485
|
+
*
|
|
486
|
+
* You can provide a user's auth token, email, or impersonate a guest.
|
|
487
|
+
*
|
|
488
|
+
* @see https://instantdb.com/docs/backend#impersonating-users
|
|
489
|
+
* @example
|
|
490
|
+
* await db.asUser({email: "stopa@instantdb.com"}).query({ goals: {} })
|
|
491
|
+
*/
|
|
492
|
+
this.asUser = (opts) => {
|
|
493
|
+
const newClient = new InstantAdminDatabase(Object.assign({}, this.config));
|
|
494
|
+
newClient.impersonationOpts = opts;
|
|
495
|
+
newClient.storage = new Storage(this.config, opts);
|
|
496
|
+
return newClient;
|
|
497
|
+
};
|
|
498
|
+
/**
|
|
499
|
+
* Use this to query your data!
|
|
500
|
+
*
|
|
501
|
+
* @see https://instantdb.com/docs/instaql
|
|
502
|
+
*
|
|
503
|
+
* @example
|
|
504
|
+
* // fetch all goals
|
|
505
|
+
* await db.query({ goals: {} })
|
|
506
|
+
*
|
|
507
|
+
* // goals where the title is "Get Fit"
|
|
508
|
+
* await db.query({ goals: { $: { where: { title: "Get Fit" } } } })
|
|
509
|
+
*
|
|
510
|
+
* // all goals, _alongside_ their todos
|
|
511
|
+
* await db.query({ goals: { todos: {} } })
|
|
512
|
+
*/
|
|
513
|
+
this.query = (query, opts = {}) => {
|
|
514
|
+
if (query && opts && 'ruleParams' in opts) {
|
|
515
|
+
query = Object.assign({ $$ruleParams: opts['ruleParams'] }, query);
|
|
516
|
+
}
|
|
517
|
+
if (!this.config.disableValidation) {
|
|
518
|
+
validateQuery(query, this.config.schema);
|
|
519
|
+
}
|
|
520
|
+
const fetchOpts = opts.fetchOpts || {};
|
|
521
|
+
const fetchOptsHeaders = fetchOpts['headers'] || {};
|
|
522
|
+
return jsonFetch(`${this.config.apiURI}/admin/query`, Object.assign(Object.assign({}, fetchOpts), { method: 'POST', headers: Object.assign(Object.assign({}, fetchOptsHeaders), authorizedHeaders(this.config, this.impersonationOpts)), body: JSON.stringify({
|
|
523
|
+
query: query,
|
|
524
|
+
'inference?': !!this.config.schema,
|
|
525
|
+
}) }));
|
|
526
|
+
};
|
|
527
|
+
/**
|
|
528
|
+
* Use this to write data! You can create, update, delete, and link objects
|
|
529
|
+
*
|
|
530
|
+
* @see https://instantdb.com/docs/instaml
|
|
531
|
+
*
|
|
532
|
+
* @example
|
|
533
|
+
* // Create a new object in the `goals` namespace
|
|
534
|
+
* const goalId = id();
|
|
535
|
+
* db.transact(db.tx.goals[goalId].update({title: "Get fit"}))
|
|
536
|
+
*
|
|
537
|
+
* // Update the title
|
|
538
|
+
* db.transact(db.tx.goals[goalId].update({title: "Get super fit"}))
|
|
539
|
+
*
|
|
540
|
+
* // Delete it
|
|
541
|
+
* db.transact(db.tx.goals[goalId].delete())
|
|
542
|
+
*
|
|
543
|
+
* // Or create an association:
|
|
544
|
+
* todoId = id();
|
|
545
|
+
* db.transact([
|
|
546
|
+
* db.tx.todos[todoId].update({ title: 'Go on a run' }),
|
|
547
|
+
* db.tx.goals[goalId].link({todos: todoId}),
|
|
548
|
+
* ])
|
|
549
|
+
*/
|
|
550
|
+
this.transact = (inputChunks) => {
|
|
551
|
+
if (!this.config.disableValidation) {
|
|
552
|
+
validateTransactions(inputChunks, this.config.schema);
|
|
553
|
+
}
|
|
554
|
+
return jsonFetch(`${this.config.apiURI}/admin/transact`, {
|
|
555
|
+
method: 'POST',
|
|
556
|
+
headers: authorizedHeaders(this.config, this.impersonationOpts),
|
|
557
|
+
body: JSON.stringify({
|
|
558
|
+
steps: steps(inputChunks),
|
|
559
|
+
'throw-on-missing-attrs?': !!this.config.schema,
|
|
560
|
+
}),
|
|
561
|
+
});
|
|
562
|
+
};
|
|
563
|
+
/**
|
|
564
|
+
* Like `query`, but returns debugging information
|
|
565
|
+
* for permissions checks along with the result.
|
|
566
|
+
* Useful for inspecting the values returned by the permissions checks.
|
|
567
|
+
* Note, this will return debug information for *all* entities
|
|
568
|
+
* that match the query's `where` clauses.
|
|
569
|
+
*
|
|
570
|
+
* Requires a user/guest context to be set with `asUser`,
|
|
571
|
+
* since permissions checks are user-specific.
|
|
572
|
+
*
|
|
573
|
+
* Accepts an optional configuration object with a `rules` key.
|
|
574
|
+
* The provided rules will override the rules in the database for the query.
|
|
575
|
+
*
|
|
576
|
+
* @see https://instantdb.com/docs/instaql
|
|
577
|
+
*
|
|
578
|
+
* @example
|
|
579
|
+
* await db.asUser({ guest: true }).debugQuery(
|
|
580
|
+
* { goals: {} },
|
|
581
|
+
* { rules: { goals: { allow: { read: "auth.id != null" } } }
|
|
582
|
+
* )
|
|
583
|
+
*/
|
|
584
|
+
this.debugQuery = (query, opts) => __awaiter(this, void 0, void 0, function* () {
|
|
585
|
+
if (query && opts && 'ruleParams' in opts) {
|
|
586
|
+
query = Object.assign({ $$ruleParams: opts['ruleParams'] }, query);
|
|
587
|
+
}
|
|
588
|
+
const response = yield jsonFetch(`${this.config.apiURI}/admin/query_perms_check`, {
|
|
589
|
+
method: 'POST',
|
|
590
|
+
headers: authorizedHeaders(this.config, this.impersonationOpts),
|
|
591
|
+
body: JSON.stringify({ query, 'rules-override': opts === null || opts === void 0 ? void 0 : opts.rules }),
|
|
592
|
+
});
|
|
593
|
+
return {
|
|
594
|
+
result: response.result,
|
|
595
|
+
checkResults: response['check-results'],
|
|
596
|
+
};
|
|
597
|
+
});
|
|
598
|
+
/**
|
|
599
|
+
* Like `transact`, but does not write to the database.
|
|
600
|
+
* Returns debugging information for permissions checks.
|
|
601
|
+
* Useful for inspecting the values returned by the permissions checks.
|
|
602
|
+
*
|
|
603
|
+
* Requires a user/guest context to be set with `asUser`,
|
|
604
|
+
* since permissions checks are user-specific.
|
|
605
|
+
*
|
|
606
|
+
* Accepts an optional configuration object with a `rules` key.
|
|
607
|
+
* The provided rules will override the rules in the database for the duration of the transaction.
|
|
608
|
+
*
|
|
609
|
+
* @example
|
|
610
|
+
* const goalId = id();
|
|
611
|
+
* db.asUser({ guest: true }).debugTransact(
|
|
612
|
+
* [db.tx.goals[goalId].update({title: "Get fit"})],
|
|
613
|
+
* { rules: { goals: { allow: { update: "auth.id != null" } } }
|
|
614
|
+
* )
|
|
615
|
+
*/
|
|
616
|
+
this.debugTransact = (inputChunks, opts) => {
|
|
617
|
+
return jsonFetch(`${this.config.apiURI}/admin/transact_perms_check`, {
|
|
618
|
+
method: 'POST',
|
|
619
|
+
headers: authorizedHeaders(this.config, this.impersonationOpts),
|
|
620
|
+
body: JSON.stringify({
|
|
621
|
+
steps: steps(inputChunks),
|
|
622
|
+
'rules-override': opts === null || opts === void 0 ? void 0 : opts.rules,
|
|
623
|
+
// @ts-expect-error because we're using a private API (for now)
|
|
624
|
+
'dangerously-commit-tx': opts === null || opts === void 0 ? void 0 : opts.__dangerouslyCommit,
|
|
625
|
+
}),
|
|
626
|
+
});
|
|
627
|
+
};
|
|
489
628
|
this.config = instantConfigWithDefaults(_config);
|
|
490
629
|
this.auth = new Auth(this.config);
|
|
491
630
|
this.storage = new Storage(this.config, this.impersonationOpts);
|
|
492
631
|
this.rooms = new Rooms(this.config);
|
|
493
632
|
}
|
|
494
|
-
/**
|
|
495
|
-
* Sometimes you want to scope queries to a specific user.
|
|
496
|
-
*
|
|
497
|
-
* You can provide a user's auth token, email, or impersonate a guest.
|
|
498
|
-
*
|
|
499
|
-
* @see https://instantdb.com/docs/backend#impersonating-users
|
|
500
|
-
* @example
|
|
501
|
-
* await db.asUser({email: "stopa@instantdb.com"}).query({ goals: {} })
|
|
502
|
-
*/
|
|
503
|
-
asUser = (opts) => {
|
|
504
|
-
const newClient = new InstantAdminDatabase({
|
|
505
|
-
...this.config,
|
|
506
|
-
});
|
|
507
|
-
newClient.impersonationOpts = opts;
|
|
508
|
-
newClient.storage = new Storage(this.config, opts);
|
|
509
|
-
return newClient;
|
|
510
|
-
};
|
|
511
|
-
/**
|
|
512
|
-
* Use this to query your data!
|
|
513
|
-
*
|
|
514
|
-
* @see https://instantdb.com/docs/instaql
|
|
515
|
-
*
|
|
516
|
-
* @example
|
|
517
|
-
* // fetch all goals
|
|
518
|
-
* await db.query({ goals: {} })
|
|
519
|
-
*
|
|
520
|
-
* // goals where the title is "Get Fit"
|
|
521
|
-
* await db.query({ goals: { $: { where: { title: "Get Fit" } } } })
|
|
522
|
-
*
|
|
523
|
-
* // all goals, _alongside_ their todos
|
|
524
|
-
* await db.query({ goals: { todos: {} } })
|
|
525
|
-
*/
|
|
526
|
-
query = (query, opts = {}) => {
|
|
527
|
-
if (query && opts && 'ruleParams' in opts) {
|
|
528
|
-
query = { $$ruleParams: opts['ruleParams'], ...query };
|
|
529
|
-
}
|
|
530
|
-
if (!this.config.disableValidation) {
|
|
531
|
-
validateQuery(query, this.config.schema);
|
|
532
|
-
}
|
|
533
|
-
const fetchOpts = opts.fetchOpts || {};
|
|
534
|
-
const fetchOptsHeaders = fetchOpts['headers'] || {};
|
|
535
|
-
return jsonFetch(`${this.config.apiURI}/admin/query`, {
|
|
536
|
-
...fetchOpts,
|
|
537
|
-
method: 'POST',
|
|
538
|
-
headers: {
|
|
539
|
-
...fetchOptsHeaders,
|
|
540
|
-
...authorizedHeaders(this.config, this.impersonationOpts),
|
|
541
|
-
},
|
|
542
|
-
body: JSON.stringify({
|
|
543
|
-
query: query,
|
|
544
|
-
'inference?': !!this.config.schema,
|
|
545
|
-
}),
|
|
546
|
-
});
|
|
547
|
-
};
|
|
548
633
|
/**
|
|
549
634
|
* Use this to to get a live view of your data!
|
|
550
635
|
*
|
|
@@ -582,17 +667,14 @@ class InstantAdminDatabase {
|
|
|
582
667
|
*/
|
|
583
668
|
subscribeQuery(query, cb, opts = {}) {
|
|
584
669
|
if (query && opts && 'ruleParams' in opts) {
|
|
585
|
-
query = { $$ruleParams: opts['ruleParams'],
|
|
670
|
+
query = Object.assign({ $$ruleParams: opts['ruleParams'] }, query);
|
|
586
671
|
}
|
|
587
672
|
if (!this.config.disableValidation) {
|
|
588
673
|
validateQuery(query, this.config.schema);
|
|
589
674
|
}
|
|
590
675
|
const fetchOpts = opts.fetchOpts || {};
|
|
591
676
|
const fetchOptsHeaders = fetchOpts['headers'] || {};
|
|
592
|
-
const headers = {
|
|
593
|
-
...fetchOptsHeaders,
|
|
594
|
-
...authorizedHeaders(this.config, this.impersonationOpts),
|
|
595
|
-
};
|
|
677
|
+
const headers = Object.assign(Object.assign({}, fetchOptsHeaders), authorizedHeaders(this.config, this.impersonationOpts));
|
|
596
678
|
const inference = !!this.config.schema;
|
|
597
679
|
return subscribe(query, cb, {
|
|
598
680
|
headers,
|
|
@@ -600,107 +682,6 @@ class InstantAdminDatabase {
|
|
|
600
682
|
apiURI: this.config.apiURI,
|
|
601
683
|
});
|
|
602
684
|
}
|
|
603
|
-
/**
|
|
604
|
-
* Use this to write data! You can create, update, delete, and link objects
|
|
605
|
-
*
|
|
606
|
-
* @see https://instantdb.com/docs/instaml
|
|
607
|
-
*
|
|
608
|
-
* @example
|
|
609
|
-
* // Create a new object in the `goals` namespace
|
|
610
|
-
* const goalId = id();
|
|
611
|
-
* db.transact(db.tx.goals[goalId].update({title: "Get fit"}))
|
|
612
|
-
*
|
|
613
|
-
* // Update the title
|
|
614
|
-
* db.transact(db.tx.goals[goalId].update({title: "Get super fit"}))
|
|
615
|
-
*
|
|
616
|
-
* // Delete it
|
|
617
|
-
* db.transact(db.tx.goals[goalId].delete())
|
|
618
|
-
*
|
|
619
|
-
* // Or create an association:
|
|
620
|
-
* todoId = id();
|
|
621
|
-
* db.transact([
|
|
622
|
-
* db.tx.todos[todoId].update({ title: 'Go on a run' }),
|
|
623
|
-
* db.tx.goals[goalId].link({todos: todoId}),
|
|
624
|
-
* ])
|
|
625
|
-
*/
|
|
626
|
-
transact = (inputChunks) => {
|
|
627
|
-
if (!this.config.disableValidation) {
|
|
628
|
-
validateTransactions(inputChunks, this.config.schema);
|
|
629
|
-
}
|
|
630
|
-
return jsonFetch(`${this.config.apiURI}/admin/transact`, {
|
|
631
|
-
method: 'POST',
|
|
632
|
-
headers: authorizedHeaders(this.config, this.impersonationOpts),
|
|
633
|
-
body: JSON.stringify({
|
|
634
|
-
steps: steps(inputChunks),
|
|
635
|
-
'throw-on-missing-attrs?': !!this.config.schema,
|
|
636
|
-
}),
|
|
637
|
-
});
|
|
638
|
-
};
|
|
639
|
-
/**
|
|
640
|
-
* Like `query`, but returns debugging information
|
|
641
|
-
* for permissions checks along with the result.
|
|
642
|
-
* Useful for inspecting the values returned by the permissions checks.
|
|
643
|
-
* Note, this will return debug information for *all* entities
|
|
644
|
-
* that match the query's `where` clauses.
|
|
645
|
-
*
|
|
646
|
-
* Requires a user/guest context to be set with `asUser`,
|
|
647
|
-
* since permissions checks are user-specific.
|
|
648
|
-
*
|
|
649
|
-
* Accepts an optional configuration object with a `rules` key.
|
|
650
|
-
* The provided rules will override the rules in the database for the query.
|
|
651
|
-
*
|
|
652
|
-
* @see https://instantdb.com/docs/instaql
|
|
653
|
-
*
|
|
654
|
-
* @example
|
|
655
|
-
* await db.asUser({ guest: true }).debugQuery(
|
|
656
|
-
* { goals: {} },
|
|
657
|
-
* { rules: { goals: { allow: { read: "auth.id != null" } } }
|
|
658
|
-
* )
|
|
659
|
-
*/
|
|
660
|
-
debugQuery = async (query, opts) => {
|
|
661
|
-
if (query && opts && 'ruleParams' in opts) {
|
|
662
|
-
query = { $$ruleParams: opts['ruleParams'], ...query };
|
|
663
|
-
}
|
|
664
|
-
const response = await jsonFetch(`${this.config.apiURI}/admin/query_perms_check`, {
|
|
665
|
-
method: 'POST',
|
|
666
|
-
headers: authorizedHeaders(this.config, this.impersonationOpts),
|
|
667
|
-
body: JSON.stringify({ query, 'rules-override': opts?.rules }),
|
|
668
|
-
});
|
|
669
|
-
return {
|
|
670
|
-
result: response.result,
|
|
671
|
-
checkResults: response['check-results'],
|
|
672
|
-
};
|
|
673
|
-
};
|
|
674
|
-
/**
|
|
675
|
-
* Like `transact`, but does not write to the database.
|
|
676
|
-
* Returns debugging information for permissions checks.
|
|
677
|
-
* Useful for inspecting the values returned by the permissions checks.
|
|
678
|
-
*
|
|
679
|
-
* Requires a user/guest context to be set with `asUser`,
|
|
680
|
-
* since permissions checks are user-specific.
|
|
681
|
-
*
|
|
682
|
-
* Accepts an optional configuration object with a `rules` key.
|
|
683
|
-
* The provided rules will override the rules in the database for the duration of the transaction.
|
|
684
|
-
*
|
|
685
|
-
* @example
|
|
686
|
-
* const goalId = id();
|
|
687
|
-
* db.asUser({ guest: true }).debugTransact(
|
|
688
|
-
* [db.tx.goals[goalId].update({title: "Get fit"})],
|
|
689
|
-
* { rules: { goals: { allow: { update: "auth.id != null" } } }
|
|
690
|
-
* )
|
|
691
|
-
*/
|
|
692
|
-
debugTransact = (inputChunks, opts) => {
|
|
693
|
-
return jsonFetch(`${this.config.apiURI}/admin/transact_perms_check`, {
|
|
694
|
-
method: 'POST',
|
|
695
|
-
headers: authorizedHeaders(this.config, this.impersonationOpts),
|
|
696
|
-
body: JSON.stringify({
|
|
697
|
-
steps: steps(inputChunks),
|
|
698
|
-
'rules-override': opts?.rules,
|
|
699
|
-
// @ts-expect-error because we're using a private API (for now)
|
|
700
|
-
'dangerously-commit-tx': opts?.__dangerouslyCommit,
|
|
701
|
-
}),
|
|
702
|
-
});
|
|
703
|
-
};
|
|
704
685
|
}
|
|
705
686
|
export { init, init_experimental, id, tx, lookup, i, createInstantRouteHandler,
|
|
706
687
|
// error
|