@budibase/server 2.6.19-alpha.26 → 2.6.19-alpha.28

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/index.js CHANGED
@@ -2644,6 +2644,7 @@ var init_utils2 = __esm({
2644
2644
  Databases2["GENERIC_CACHE"] = "data_cache";
2645
2645
  Databases2["WRITE_THROUGH"] = "writeThrough";
2646
2646
  Databases2["LOCKS"] = "locks";
2647
+ Databases2["SOCKET_IO"] = "socket_io";
2647
2648
  return Databases2;
2648
2649
  })(Databases || {});
2649
2650
  SelectableDatabase = /* @__PURE__ */ ((SelectableDatabase2) => {
@@ -21759,9 +21760,12 @@ async function init17() {
21759
21760
  devAppClient = new redis_exports.Client(redis_exports.utils.Databases.DEV_LOCKS);
21760
21761
  debounceClient = new redis_exports.Client(redis_exports.utils.Databases.DEBOUNCE);
21761
21762
  flagClient = new redis_exports.Client(redis_exports.utils.Databases.FLAGS);
21763
+ socketClient = new redis_exports.Client(redis_exports.utils.Databases.SOCKET_IO);
21762
21764
  await devAppClient.init();
21763
21765
  await debounceClient.init();
21764
21766
  await flagClient.init();
21767
+ await socketClient.init();
21768
+ socketSubClient = socketClient.getClient().duplicate();
21765
21769
  }
21766
21770
  async function shutdown6() {
21767
21771
  if (devAppClient)
@@ -21770,6 +21774,10 @@ async function shutdown6() {
21770
21774
  await debounceClient.finish();
21771
21775
  if (flagClient)
21772
21776
  await flagClient.finish();
21777
+ if (socketClient)
21778
+ await socketClient.finish();
21779
+ if (socketSubClient)
21780
+ socketSubClient.disconnect();
21773
21781
  await redis_exports.clients.shutdown();
21774
21782
  console.log("Redis shutdown");
21775
21783
  }
@@ -21822,7 +21830,13 @@ async function checkTestFlag(id) {
21822
21830
  async function clearTestFlag(id) {
21823
21831
  await devAppClient.delete(id);
21824
21832
  }
21825
- var APP_DEV_LOCK_SECONDS, AUTOMATION_TEST_FLAG_SECONDS, devAppClient, debounceClient, flagClient;
21833
+ function getSocketPubSubClients() {
21834
+ return {
21835
+ pub: socketClient.getClient(),
21836
+ sub: socketSubClient
21837
+ };
21838
+ }
21839
+ var APP_DEV_LOCK_SECONDS, AUTOMATION_TEST_FLAG_SECONDS, devAppClient, debounceClient, flagClient, socketClient, socketSubClient;
21826
21840
  var init_redis4 = __esm({
21827
21841
  "src/utilities/redis.ts"() {
21828
21842
  init_src2();
@@ -21842,10 +21856,9 @@ async function checkDevAppLocks(ctx) {
21842
21856
  if (!appId || !appId.startsWith(APP_DEV_PREFIX2)) {
21843
21857
  return;
21844
21858
  }
21845
- if (!await doesUserHaveLock(appId, ctx.user)) {
21846
- ctx.throw(400, "User does not hold app lock.");
21859
+ if (await doesUserHaveLock(appId, ctx.user)) {
21860
+ await updateLock(appId, ctx.user);
21847
21861
  }
21848
- await updateLock(appId, ctx.user);
21849
21862
  }
21850
21863
  async function updateAppUpdatedAt(ctx) {
21851
21864
  const appId = ctx.appId;
@@ -30994,7 +31007,7 @@ var init_file = __esm({
30994
31007
  });
30995
31008
 
30996
31009
  // src/websockets/websocket.ts
30997
- var import_socket, import_http, import_cookies, import_koa_useragent, Socket;
31010
+ var import_socket, import_http, import_cookies, import_koa_useragent, import_redis_adapter, import_uuid4, Socket;
30998
31011
  var init_websocket = __esm({
30999
31012
  "src/websockets/websocket.ts"() {
31000
31013
  import_socket = require("socket.io");
@@ -31003,8 +31016,11 @@ var init_websocket = __esm({
31003
31016
  import_koa_useragent = require("koa-useragent");
31004
31017
  init_src2();
31005
31018
  init_currentapp();
31019
+ import_redis_adapter = require("@socket.io/redis-adapter");
31020
+ init_redis4();
31021
+ import_uuid4 = __toESM(require("uuid"));
31006
31022
  Socket = class {
31007
- constructor(app2, server2, path5, additionalMiddlewares) {
31023
+ constructor(app2, server2, path5 = "/", additionalMiddlewares) {
31008
31024
  this.io = new import_socket.Server(server2, {
31009
31025
  path: path5
31010
31026
  });
@@ -31039,10 +31055,15 @@ var init_websocket = __esm({
31039
31055
  for (let [idx, middleware2] of middlewares.entries()) {
31040
31056
  await middleware2(ctx, () => {
31041
31057
  if (idx === middlewares.length - 1) {
31058
+ const { _id, email, firstName, lastName } = ctx.user;
31042
31059
  socket.data.user = {
31043
- id: ctx.user._id,
31044
- email: ctx.user.email
31060
+ _id,
31061
+ email,
31062
+ firstName,
31063
+ lastName,
31064
+ sessionId: import_uuid4.default.v4()
31045
31065
  };
31066
+ socket.data.appId = ctx.appId;
31046
31067
  next();
31047
31068
  }
31048
31069
  });
@@ -31051,6 +31072,9 @@ var init_websocket = __esm({
31051
31072
  next(error2);
31052
31073
  }
31053
31074
  });
31075
+ const { pub, sub } = getSocketPubSubClients();
31076
+ const opts = { key: `socket.io-${path5}` };
31077
+ this.io.adapter((0, import_redis_adapter.createAdapter)(pub, sub, opts));
31054
31078
  }
31055
31079
  // Emit an event to all sockets
31056
31080
  emit(event, payload) {
@@ -31060,6 +31084,325 @@ var init_websocket = __esm({
31060
31084
  }
31061
31085
  });
31062
31086
 
31087
+ // src/websockets/client.ts
31088
+ var ClientAppWebsocket;
31089
+ var init_client2 = __esm({
31090
+ "src/websockets/client.ts"() {
31091
+ init_websocket();
31092
+ init_authorized();
31093
+ init_src2();
31094
+ ClientAppWebsocket = class extends Socket {
31095
+ constructor(app2, server2) {
31096
+ super(app2, server2, "/socket/client", [authorized_default(permissions_exports.BUILDER)]);
31097
+ }
31098
+ };
31099
+ }
31100
+ });
31101
+
31102
+ // src/api/controllers/user.ts
31103
+ async function fetchMetadata(ctx) {
31104
+ const global = await getGlobalUsers();
31105
+ const metadata = await sdk_default.users.rawUserMetadata();
31106
+ const users2 = [];
31107
+ for (let user2 of global) {
31108
+ const info = metadata.find((meta) => meta._id.includes(user2._id));
31109
+ users2.push({
31110
+ ...user2,
31111
+ ...info,
31112
+ tableId: InternalTables.USER_METADATA,
31113
+ // make sure the ID is always a local ID, not a global one
31114
+ _id: generateUserMetadataID2(user2._id)
31115
+ });
31116
+ }
31117
+ ctx.body = users2;
31118
+ }
31119
+ async function updateSelfMetadata(ctx) {
31120
+ var _a2;
31121
+ ctx.request.body._id = (_a2 = ctx.user) == null ? void 0 : _a2._id;
31122
+ delete ctx.request.body._rev;
31123
+ delete ctx.request.body.csrfToken;
31124
+ await updateMetadata(ctx);
31125
+ }
31126
+ async function updateMetadata(ctx) {
31127
+ const db2 = context_exports.getAppDB();
31128
+ const user2 = ctx.request.body;
31129
+ delete user2.roles;
31130
+ const metadata = {
31131
+ tableId: InternalTables.USER_METADATA,
31132
+ ...user2
31133
+ };
31134
+ ctx.body = await db2.put(metadata);
31135
+ }
31136
+ async function destroyMetadata(ctx) {
31137
+ const db2 = context_exports.getAppDB();
31138
+ try {
31139
+ const dbUser = await db2.get(ctx.params.id);
31140
+ await db2.remove(dbUser._id, dbUser._rev);
31141
+ } catch (err) {
31142
+ }
31143
+ ctx.body = {
31144
+ message: `User metadata ${ctx.params.id} deleted.`
31145
+ };
31146
+ }
31147
+ async function findMetadata(ctx) {
31148
+ ctx.body = await getFullUser(ctx, ctx.params.id);
31149
+ }
31150
+ async function setFlag(ctx) {
31151
+ var _a2;
31152
+ const userId = (_a2 = ctx.user) == null ? void 0 : _a2._id;
31153
+ const { flag, value } = ctx.request.body;
31154
+ if (!flag) {
31155
+ ctx.throw(400, "Must supply a 'flag' field in request body.");
31156
+ }
31157
+ const flagDocId = generateUserFlagID(userId);
31158
+ const db2 = context_exports.getAppDB();
31159
+ let doc;
31160
+ try {
31161
+ doc = await db2.get(flagDocId);
31162
+ } catch (err) {
31163
+ doc = { _id: flagDocId };
31164
+ }
31165
+ doc[flag] = value || true;
31166
+ await db2.put(doc);
31167
+ ctx.body = { message: "Flag set successfully" };
31168
+ }
31169
+ async function getFlags(ctx) {
31170
+ var _a2;
31171
+ const userId = (_a2 = ctx.user) == null ? void 0 : _a2._id;
31172
+ const docId = generateUserFlagID(userId);
31173
+ const db2 = context_exports.getAppDB();
31174
+ let doc;
31175
+ try {
31176
+ doc = await db2.get(docId);
31177
+ } catch (err) {
31178
+ doc = { _id: docId };
31179
+ }
31180
+ ctx.body = doc;
31181
+ }
31182
+ var init_user10 = __esm({
31183
+ "src/api/controllers/user.ts"() {
31184
+ init_utils9();
31185
+ init_utils9();
31186
+ init_global5();
31187
+ init_users11();
31188
+ init_src2();
31189
+ init_sdk3();
31190
+ }
31191
+ });
31192
+
31193
+ // src/integrations/base/query.ts
31194
+ async function makeExternalQuery(datasource2, json2) {
31195
+ datasource2 = await sdk_default.datasources.enrich(datasource2);
31196
+ const Integration19 = await getIntegration(datasource2.source);
31197
+ if (Integration19.prototype.query) {
31198
+ const integration = new Integration19(datasource2.config);
31199
+ return integration.query(json2);
31200
+ } else {
31201
+ throw "Datasource does not support query.";
31202
+ }
31203
+ }
31204
+ var init_query4 = __esm({
31205
+ "src/integrations/base/query.ts"() {
31206
+ init_integrations2();
31207
+ init_sdk3();
31208
+ }
31209
+ });
31210
+
31211
+ // src/api/controllers/view/exporters.ts
31212
+ var exporters_exports = {};
31213
+ __export(exporters_exports, {
31214
+ Format: () => Format,
31215
+ csv: () => csv2,
31216
+ isFormat: () => isFormat,
31217
+ json: () => json,
31218
+ jsonWithSchema: () => jsonWithSchema
31219
+ });
31220
+ function csv2(headers, rows2) {
31221
+ let csv4 = headers.map((key) => `"${key}"`).join(",");
31222
+ for (let row2 of rows2) {
31223
+ csv4 = `${csv4}
31224
+ ${headers.map((header) => {
31225
+ let val = row2[header];
31226
+ val = typeof val === "object" && !(val instanceof Date) ? `"${JSON.stringify(val).replace(/"/g, "'")}"` : val !== void 0 ? `"${val}"` : "";
31227
+ return val.trim();
31228
+ }).join(",")}`;
31229
+ }
31230
+ return csv4;
31231
+ }
31232
+ function json(rows2) {
31233
+ return JSON.stringify(rows2, void 0, 2);
31234
+ }
31235
+ function jsonWithSchema(schema, rows2) {
31236
+ const newSchema = {};
31237
+ Object.values(schema).forEach((column) => {
31238
+ if (!column.autocolumn) {
31239
+ newSchema[column.name] = column;
31240
+ }
31241
+ });
31242
+ return JSON.stringify({ schema: newSchema, rows: rows2 }, void 0, 2);
31243
+ }
31244
+ function isFormat(format) {
31245
+ return Object.values(Format).includes(format);
31246
+ }
31247
+ var Format;
31248
+ var init_exporters = __esm({
31249
+ "src/api/controllers/view/exporters.ts"() {
31250
+ Format = /* @__PURE__ */ ((Format2) => {
31251
+ Format2["CSV"] = "csv";
31252
+ Format2["JSON"] = "json";
31253
+ Format2["JSON_WITH_SCHEMA"] = "jsonWithSchema";
31254
+ return Format2;
31255
+ })(Format || {});
31256
+ }
31257
+ });
31258
+
31259
+ // src/api/controllers/row/utils.ts
31260
+ var utils_exports9 = {};
31261
+ __export(utils_exports9, {
31262
+ cleanExportRows: () => cleanExportRows,
31263
+ findRow: () => findRow,
31264
+ getDatasourceAndQuery: () => getDatasourceAndQuery,
31265
+ getTableId: () => getTableId,
31266
+ validate: () => validate4
31267
+ });
31268
+ async function getDatasourceAndQuery(json2) {
31269
+ const datasourceId = json2.endpoint.datasourceId;
31270
+ const datasource2 = await sdk_default.datasources.get(datasourceId);
31271
+ return makeExternalQuery(datasource2, json2);
31272
+ }
31273
+ async function findRow(ctx, tableId, rowId) {
31274
+ const db2 = context_exports.getAppDB();
31275
+ let row2;
31276
+ if (tableId === InternalTables.USER_METADATA) {
31277
+ ctx.params = {
31278
+ id: rowId
31279
+ };
31280
+ await findMetadata(ctx);
31281
+ row2 = ctx.body;
31282
+ } else {
31283
+ row2 = await db2.get(rowId);
31284
+ }
31285
+ if (row2.tableId !== tableId) {
31286
+ throw "Supplied tableId does not match the rows tableId";
31287
+ }
31288
+ return row2;
31289
+ }
31290
+ async function validate4({
31291
+ tableId,
31292
+ row: row2,
31293
+ table: table2
31294
+ }) {
31295
+ let fetchedTable;
31296
+ if (!table2) {
31297
+ fetchedTable = await sdk_default.tables.getTable(tableId);
31298
+ } else {
31299
+ fetchedTable = table2;
31300
+ }
31301
+ const errors = {};
31302
+ for (let fieldName of Object.keys(fetchedTable.schema)) {
31303
+ const column = fetchedTable.schema[fieldName];
31304
+ const constraints = cloneDeep8(column.constraints);
31305
+ const type = column.type;
31306
+ if (type === "formula" /* FORMULA */ || column.autocolumn) {
31307
+ continue;
31308
+ }
31309
+ if (type === "options" /* OPTIONS */ && constraints.inclusion) {
31310
+ constraints.inclusion.push(null, "");
31311
+ }
31312
+ let res;
31313
+ if (type === "array" /* ARRAY */ && row2[fieldName]) {
31314
+ if (row2[fieldName].length) {
31315
+ if (!Array.isArray(row2[fieldName])) {
31316
+ row2[fieldName] = row2[fieldName].split(",");
31317
+ }
31318
+ row2[fieldName].map((val) => {
31319
+ if (!constraints.inclusion.includes(val) && constraints.inclusion.length !== 0) {
31320
+ errors[fieldName] = "Field not in list";
31321
+ }
31322
+ });
31323
+ } else if (constraints.presence && row2[fieldName].length === 0) {
31324
+ errors[fieldName] = [`${fieldName} is required`];
31325
+ }
31326
+ } else if ((type === "attachment" /* ATTACHMENT */ || type === "json" /* JSON */) && typeof row2[fieldName] === "string") {
31327
+ try {
31328
+ const json2 = JSON.parse(row2[fieldName]);
31329
+ if (type === "attachment" /* ATTACHMENT */) {
31330
+ if (Array.isArray(json2)) {
31331
+ row2[fieldName] = json2;
31332
+ } else {
31333
+ errors[fieldName] = [`Must be an array`];
31334
+ }
31335
+ }
31336
+ } catch (err) {
31337
+ errors[fieldName] = [`Contains invalid JSON`];
31338
+ }
31339
+ } else {
31340
+ res = validateJs.single(row2[fieldName], constraints);
31341
+ }
31342
+ if (res)
31343
+ errors[fieldName] = res;
31344
+ }
31345
+ return { valid: Object.keys(errors).length === 0, errors };
31346
+ }
31347
+ function cleanExportRows(rows2, schema, format, columns) {
31348
+ let cleanRows = [...rows2];
31349
+ const relationships = Object.entries(schema).filter((entry) => entry[1].type === "link" /* LINK */).map((entry) => entry[0]);
31350
+ relationships.forEach((column) => {
31351
+ cleanRows.forEach((row2) => {
31352
+ delete row2[column];
31353
+ });
31354
+ delete schema[column];
31355
+ });
31356
+ if (format === "csv" /* CSV */) {
31357
+ const schemaKeys = Object.keys(schema);
31358
+ for (let key of schemaKeys) {
31359
+ if ((columns == null ? void 0 : columns.length) && columns.indexOf(key) > 0) {
31360
+ continue;
31361
+ }
31362
+ for (let row2 of cleanRows) {
31363
+ if (row2[key] == null) {
31364
+ row2[key] = void 0;
31365
+ }
31366
+ }
31367
+ }
31368
+ }
31369
+ return cleanRows;
31370
+ }
31371
+ function getTableId(ctx) {
31372
+ if (ctx.request.body && ctx.request.body.tableId) {
31373
+ return ctx.request.body.tableId;
31374
+ }
31375
+ if (ctx.params && ctx.params.tableId) {
31376
+ return ctx.params.tableId;
31377
+ }
31378
+ if (ctx.params && ctx.params.viewName) {
31379
+ return ctx.params.viewName;
31380
+ }
31381
+ }
31382
+ var validateJs, cloneDeep8;
31383
+ var init_utils24 = __esm({
31384
+ "src/api/controllers/row/utils.ts"() {
31385
+ init_utils9();
31386
+ init_user10();
31387
+ init_constants4();
31388
+ init_src2();
31389
+ init_query4();
31390
+ init_exporters();
31391
+ init_sdk3();
31392
+ validateJs = require("validate.js");
31393
+ ({ cloneDeep: cloneDeep8 } = require("lodash/fp"));
31394
+ validateJs.extend(validateJs.validators.datetime, {
31395
+ parse: function(value) {
31396
+ return new Date(value).getTime();
31397
+ },
31398
+ // Input is a unix timestamp
31399
+ format: function(value) {
31400
+ return new Date(value).toISOString();
31401
+ }
31402
+ });
31403
+ }
31404
+ });
31405
+
31063
31406
  // src/websockets/grid.ts
31064
31407
  var GridSocket;
31065
31408
  var init_grid = __esm({
@@ -31067,68 +31410,125 @@ var init_grid = __esm({
31067
31410
  init_authorized();
31068
31411
  init_websocket();
31069
31412
  init_src2();
31413
+ init_utils24();
31070
31414
  GridSocket = class extends Socket {
31071
31415
  constructor(app2, server2) {
31072
31416
  super(app2, server2, "/socket/grid", [authorized_default(permissions_exports.BUILDER)]);
31073
31417
  this.io.on("connection", (socket) => {
31074
31418
  const user2 = socket.data.user;
31075
- console.log(`Spreadsheet user connected: ${user2 == null ? void 0 : user2.id}`);
31076
31419
  let currentRoom;
31077
31420
  socket.on("select-table", async (tableId, callback) => {
31078
31421
  if (currentRoom) {
31079
- socket.to(currentRoom).emit("user-disconnect", socket.data.user);
31422
+ socket.to(currentRoom).emit("user-disconnect", user2);
31080
31423
  socket.leave(currentRoom);
31081
31424
  }
31082
31425
  currentRoom = tableId;
31083
31426
  socket.join(currentRoom);
31084
- socket.to(currentRoom).emit("user-update", socket.data.user);
31427
+ socket.to(currentRoom).emit("user-update", user2);
31085
31428
  const sockets = await this.io.in(currentRoom).fetchSockets();
31086
31429
  callback({
31087
- users: sockets.map((socket2) => socket2.data.user),
31088
- id: user2.id
31430
+ users: sockets.map((socket2) => socket2.data.user)
31089
31431
  });
31090
31432
  });
31091
31433
  socket.on("select-cell", (cellId) => {
31092
- socket.data.user.selectedCellId = cellId;
31434
+ socket.data.user.focusedCellId = cellId;
31093
31435
  if (currentRoom) {
31094
- socket.to(currentRoom).emit("user-update", socket.data.user);
31436
+ socket.to(currentRoom).emit("user-update", user2);
31095
31437
  }
31096
31438
  });
31097
31439
  socket.on("disconnect", () => {
31098
31440
  if (currentRoom) {
31099
- socket.to(currentRoom).emit("user-disconnect", socket.data.user);
31441
+ socket.to(currentRoom).emit("user-disconnect", user2);
31100
31442
  }
31101
31443
  });
31102
31444
  });
31103
31445
  }
31446
+ emitRowUpdate(ctx, row2) {
31447
+ const tableId = getTableId(ctx);
31448
+ this.io.in(tableId).emit("row-change", { id: row2._id, row: row2 });
31449
+ }
31450
+ emitRowDeletion(ctx, id) {
31451
+ const tableId = getTableId(ctx);
31452
+ this.io.in(tableId).emit("row-change", { id, row: null });
31453
+ }
31454
+ emitTableUpdate(table2) {
31455
+ this.io.in(table2._id).emit("table-change", { id: table2._id, table: table2 });
31456
+ }
31457
+ emitTableDeletion(id) {
31458
+ this.io.in(id).emit("table-change", { id, table: null });
31459
+ }
31104
31460
  };
31105
31461
  }
31106
31462
  });
31107
31463
 
31108
- // src/websockets/client.ts
31109
- var ClientAppWebsocket;
31110
- var init_client2 = __esm({
31111
- "src/websockets/client.ts"() {
31112
- init_websocket();
31464
+ // src/websockets/builder.ts
31465
+ var BuilderSocket;
31466
+ var init_builder2 = __esm({
31467
+ "src/websockets/builder.ts"() {
31113
31468
  init_authorized();
31469
+ init_websocket();
31114
31470
  init_src2();
31115
- ClientAppWebsocket = class extends Socket {
31471
+ init_websockets();
31472
+ init_redis4();
31473
+ BuilderSocket = class extends Socket {
31116
31474
  constructor(app2, server2) {
31117
- super(app2, server2, "/socket/client", [authorized_default(permissions_exports.BUILDER)]);
31475
+ super(app2, server2, "/socket/builder", [authorized_default(permissions_exports.BUILDER)]);
31476
+ this.io.on("connection", (socket) => {
31477
+ const user2 = socket.data.user;
31478
+ const appId = socket.data.appId;
31479
+ socket.join(appId);
31480
+ socket.to(appId).emit("user-update", user2);
31481
+ socket.on("get-users", async (payload, callback) => {
31482
+ const sockets = await this.io.in(appId).fetchSockets();
31483
+ callback({
31484
+ users: sockets.map((socket2) => socket2.data.user)
31485
+ });
31486
+ });
31487
+ socket.on("disconnect", async () => {
31488
+ socket.to(appId).emit("user-disconnect", user2);
31489
+ try {
31490
+ const sockets = await this.io.in(appId).fetchSockets();
31491
+ const hasOtherConnection = sockets.some((socket2) => {
31492
+ const { _id, sessionId } = socket2.data.user;
31493
+ return _id === user2._id && sessionId !== user2.sessionId;
31494
+ });
31495
+ if (!hasOtherConnection) {
31496
+ await clearLock(appId, user2);
31497
+ }
31498
+ } catch (e) {
31499
+ }
31500
+ });
31501
+ });
31502
+ }
31503
+ emitTableUpdate(ctx, table2) {
31504
+ this.io.in(ctx.appId).emit("table-change", { id: table2._id, table: table2 });
31505
+ gridSocket.emitTableUpdate(table2);
31506
+ }
31507
+ emitTableDeletion(ctx, id) {
31508
+ this.io.in(ctx.appId).emit("table-change", { id, table: null });
31509
+ gridSocket.emitTableDeletion(id);
31510
+ }
31511
+ emitDatasourceUpdate(ctx, datasource2) {
31512
+ this.io.in(ctx.appId).emit("datasource-change", { id: datasource2._id, datasource: datasource2 });
31513
+ }
31514
+ emitDatasourceDeletion(ctx, id) {
31515
+ this.io.in(ctx.appId).emit("datasource-change", { id, datasource: null });
31118
31516
  }
31119
31517
  };
31120
31518
  }
31121
31519
  });
31122
31520
 
31123
31521
  // src/websockets/index.ts
31124
- var clientAppSocket, gridSocket, initialise;
31522
+ var clientAppSocket, gridSocket, builderSocket, initialise;
31125
31523
  var init_websockets = __esm({
31126
31524
  "src/websockets/index.ts"() {
31127
- init_grid();
31128
31525
  init_client2();
31526
+ init_grid();
31527
+ init_builder2();
31129
31528
  initialise = (app2, server2) => {
31130
31529
  clientAppSocket = new ClientAppWebsocket(app2, server2);
31131
31530
  gridSocket = new GridSocket(app2, server2);
31531
+ builderSocket = new BuilderSocket(app2, server2);
31132
31532
  };
31133
31533
  }
31134
31534
  });
@@ -31440,298 +31840,6 @@ var init_sendSmtpEmail = __esm({
31440
31840
  }
31441
31841
  });
31442
31842
 
31443
- // src/api/controllers/user.ts
31444
- async function fetchMetadata(ctx) {
31445
- const global = await getGlobalUsers();
31446
- const metadata = await sdk_default.users.rawUserMetadata();
31447
- const users2 = [];
31448
- for (let user2 of global) {
31449
- const info = metadata.find((meta) => meta._id.includes(user2._id));
31450
- users2.push({
31451
- ...user2,
31452
- ...info,
31453
- tableId: InternalTables.USER_METADATA,
31454
- // make sure the ID is always a local ID, not a global one
31455
- _id: generateUserMetadataID2(user2._id)
31456
- });
31457
- }
31458
- ctx.body = users2;
31459
- }
31460
- async function updateSelfMetadata(ctx) {
31461
- var _a2;
31462
- ctx.request.body._id = (_a2 = ctx.user) == null ? void 0 : _a2._id;
31463
- delete ctx.request.body._rev;
31464
- delete ctx.request.body.csrfToken;
31465
- await updateMetadata(ctx);
31466
- }
31467
- async function updateMetadata(ctx) {
31468
- const db2 = context_exports.getAppDB();
31469
- const user2 = ctx.request.body;
31470
- delete user2.roles;
31471
- const metadata = {
31472
- tableId: InternalTables.USER_METADATA,
31473
- ...user2
31474
- };
31475
- ctx.body = await db2.put(metadata);
31476
- }
31477
- async function destroyMetadata(ctx) {
31478
- const db2 = context_exports.getAppDB();
31479
- try {
31480
- const dbUser = await db2.get(ctx.params.id);
31481
- await db2.remove(dbUser._id, dbUser._rev);
31482
- } catch (err) {
31483
- }
31484
- ctx.body = {
31485
- message: `User metadata ${ctx.params.id} deleted.`
31486
- };
31487
- }
31488
- async function findMetadata(ctx) {
31489
- ctx.body = await getFullUser(ctx, ctx.params.id);
31490
- }
31491
- async function setFlag(ctx) {
31492
- var _a2;
31493
- const userId = (_a2 = ctx.user) == null ? void 0 : _a2._id;
31494
- const { flag, value } = ctx.request.body;
31495
- if (!flag) {
31496
- ctx.throw(400, "Must supply a 'flag' field in request body.");
31497
- }
31498
- const flagDocId = generateUserFlagID(userId);
31499
- const db2 = context_exports.getAppDB();
31500
- let doc;
31501
- try {
31502
- doc = await db2.get(flagDocId);
31503
- } catch (err) {
31504
- doc = { _id: flagDocId };
31505
- }
31506
- doc[flag] = value || true;
31507
- await db2.put(doc);
31508
- ctx.body = { message: "Flag set successfully" };
31509
- }
31510
- async function getFlags(ctx) {
31511
- var _a2;
31512
- const userId = (_a2 = ctx.user) == null ? void 0 : _a2._id;
31513
- const docId = generateUserFlagID(userId);
31514
- const db2 = context_exports.getAppDB();
31515
- let doc;
31516
- try {
31517
- doc = await db2.get(docId);
31518
- } catch (err) {
31519
- doc = { _id: docId };
31520
- }
31521
- ctx.body = doc;
31522
- }
31523
- var init_user10 = __esm({
31524
- "src/api/controllers/user.ts"() {
31525
- init_utils9();
31526
- init_utils9();
31527
- init_global5();
31528
- init_users11();
31529
- init_src2();
31530
- init_sdk3();
31531
- }
31532
- });
31533
-
31534
- // src/integrations/base/query.ts
31535
- async function makeExternalQuery(datasource2, json2) {
31536
- datasource2 = await sdk_default.datasources.enrich(datasource2);
31537
- const Integration19 = await getIntegration(datasource2.source);
31538
- if (Integration19.prototype.query) {
31539
- const integration = new Integration19(datasource2.config);
31540
- return integration.query(json2);
31541
- } else {
31542
- throw "Datasource does not support query.";
31543
- }
31544
- }
31545
- var init_query4 = __esm({
31546
- "src/integrations/base/query.ts"() {
31547
- init_integrations2();
31548
- init_sdk3();
31549
- }
31550
- });
31551
-
31552
- // src/api/controllers/view/exporters.ts
31553
- var exporters_exports = {};
31554
- __export(exporters_exports, {
31555
- Format: () => Format,
31556
- csv: () => csv2,
31557
- isFormat: () => isFormat,
31558
- json: () => json,
31559
- jsonWithSchema: () => jsonWithSchema
31560
- });
31561
- function csv2(headers, rows2) {
31562
- let csv4 = headers.map((key) => `"${key}"`).join(",");
31563
- for (let row2 of rows2) {
31564
- csv4 = `${csv4}
31565
- ${headers.map((header) => {
31566
- let val = row2[header];
31567
- val = typeof val === "object" && !(val instanceof Date) ? `"${JSON.stringify(val).replace(/"/g, "'")}"` : val !== void 0 ? `"${val}"` : "";
31568
- return val.trim();
31569
- }).join(",")}`;
31570
- }
31571
- return csv4;
31572
- }
31573
- function json(rows2) {
31574
- return JSON.stringify(rows2, void 0, 2);
31575
- }
31576
- function jsonWithSchema(schema, rows2) {
31577
- const newSchema = {};
31578
- Object.values(schema).forEach((column) => {
31579
- if (!column.autocolumn) {
31580
- newSchema[column.name] = column;
31581
- }
31582
- });
31583
- return JSON.stringify({ schema: newSchema, rows: rows2 }, void 0, 2);
31584
- }
31585
- function isFormat(format) {
31586
- return Object.values(Format).includes(format);
31587
- }
31588
- var Format;
31589
- var init_exporters = __esm({
31590
- "src/api/controllers/view/exporters.ts"() {
31591
- Format = /* @__PURE__ */ ((Format2) => {
31592
- Format2["CSV"] = "csv";
31593
- Format2["JSON"] = "json";
31594
- Format2["JSON_WITH_SCHEMA"] = "jsonWithSchema";
31595
- return Format2;
31596
- })(Format || {});
31597
- }
31598
- });
31599
-
31600
- // src/api/controllers/row/utils.ts
31601
- var utils_exports9 = {};
31602
- __export(utils_exports9, {
31603
- cleanExportRows: () => cleanExportRows,
31604
- findRow: () => findRow,
31605
- getDatasourceAndQuery: () => getDatasourceAndQuery,
31606
- validate: () => validate4
31607
- });
31608
- async function getDatasourceAndQuery(json2) {
31609
- const datasourceId = json2.endpoint.datasourceId;
31610
- const datasource2 = await sdk_default.datasources.get(datasourceId);
31611
- return makeExternalQuery(datasource2, json2);
31612
- }
31613
- async function findRow(ctx, tableId, rowId) {
31614
- const db2 = context_exports.getAppDB();
31615
- let row2;
31616
- if (tableId === InternalTables.USER_METADATA) {
31617
- ctx.params = {
31618
- id: rowId
31619
- };
31620
- await findMetadata(ctx);
31621
- row2 = ctx.body;
31622
- } else {
31623
- row2 = await db2.get(rowId);
31624
- }
31625
- if (row2.tableId !== tableId) {
31626
- throw "Supplied tableId does not match the rows tableId";
31627
- }
31628
- return row2;
31629
- }
31630
- async function validate4({
31631
- tableId,
31632
- row: row2,
31633
- table: table2
31634
- }) {
31635
- let fetchedTable;
31636
- if (!table2) {
31637
- fetchedTable = await sdk_default.tables.getTable(tableId);
31638
- } else {
31639
- fetchedTable = table2;
31640
- }
31641
- const errors = {};
31642
- for (let fieldName of Object.keys(fetchedTable.schema)) {
31643
- const column = fetchedTable.schema[fieldName];
31644
- const constraints = cloneDeep8(column.constraints);
31645
- const type = column.type;
31646
- if (type === "formula" /* FORMULA */ || column.autocolumn) {
31647
- continue;
31648
- }
31649
- if (type === "options" /* OPTIONS */ && constraints.inclusion) {
31650
- constraints.inclusion.push(null, "");
31651
- }
31652
- let res;
31653
- if (type === "array" /* ARRAY */ && row2[fieldName]) {
31654
- if (row2[fieldName].length) {
31655
- if (!Array.isArray(row2[fieldName])) {
31656
- row2[fieldName] = row2[fieldName].split(",");
31657
- }
31658
- row2[fieldName].map((val) => {
31659
- if (!constraints.inclusion.includes(val) && constraints.inclusion.length !== 0) {
31660
- errors[fieldName] = "Field not in list";
31661
- }
31662
- });
31663
- } else if (constraints.presence && row2[fieldName].length === 0) {
31664
- errors[fieldName] = [`${fieldName} is required`];
31665
- }
31666
- } else if ((type === "attachment" /* ATTACHMENT */ || type === "json" /* JSON */) && typeof row2[fieldName] === "string") {
31667
- try {
31668
- const json2 = JSON.parse(row2[fieldName]);
31669
- if (type === "attachment" /* ATTACHMENT */) {
31670
- if (Array.isArray(json2)) {
31671
- row2[fieldName] = json2;
31672
- } else {
31673
- errors[fieldName] = [`Must be an array`];
31674
- }
31675
- }
31676
- } catch (err) {
31677
- errors[fieldName] = [`Contains invalid JSON`];
31678
- }
31679
- } else {
31680
- res = validateJs.single(row2[fieldName], constraints);
31681
- }
31682
- if (res)
31683
- errors[fieldName] = res;
31684
- }
31685
- return { valid: Object.keys(errors).length === 0, errors };
31686
- }
31687
- function cleanExportRows(rows2, schema, format, columns) {
31688
- let cleanRows = [...rows2];
31689
- const relationships = Object.entries(schema).filter((entry) => entry[1].type === "link" /* LINK */).map((entry) => entry[0]);
31690
- relationships.forEach((column) => {
31691
- cleanRows.forEach((row2) => {
31692
- delete row2[column];
31693
- });
31694
- delete schema[column];
31695
- });
31696
- if (format === "csv" /* CSV */) {
31697
- const schemaKeys = Object.keys(schema);
31698
- for (let key of schemaKeys) {
31699
- if ((columns == null ? void 0 : columns.length) && columns.indexOf(key) > 0) {
31700
- continue;
31701
- }
31702
- for (let row2 of cleanRows) {
31703
- if (row2[key] == null) {
31704
- row2[key] = void 0;
31705
- }
31706
- }
31707
- }
31708
- }
31709
- return cleanRows;
31710
- }
31711
- var validateJs, cloneDeep8;
31712
- var init_utils24 = __esm({
31713
- "src/api/controllers/row/utils.ts"() {
31714
- init_utils9();
31715
- init_user10();
31716
- init_constants4();
31717
- init_src2();
31718
- init_query4();
31719
- init_exporters();
31720
- init_sdk3();
31721
- validateJs = require("validate.js");
31722
- ({ cloneDeep: cloneDeep8 } = require("lodash/fp"));
31723
- validateJs.extend(validateJs.validators.datetime, {
31724
- parse: function(value) {
31725
- return new Date(value).getTime();
31726
- },
31727
- // Input is a unix timestamp
31728
- format: function(value) {
31729
- return new Date(value).toISOString();
31730
- }
31731
- });
31732
- }
31733
- });
31734
-
31735
31843
  // src/api/controllers/row/internalSearch.ts
31736
31844
  async function paginatedSearch2(query3, params2) {
31737
31845
  const appId = context_exports.getAppId();
@@ -33224,18 +33332,8 @@ function pickApi(tableId) {
33224
33332
  }
33225
33333
  return internal_exports;
33226
33334
  }
33227
- function getTableId(ctx) {
33228
- if (ctx.request.body && ctx.request.body.tableId) {
33229
- return ctx.request.body.tableId;
33230
- }
33231
- if (ctx.params && ctx.params.tableId) {
33232
- return ctx.params.tableId;
33233
- }
33234
- if (ctx.params && ctx.params.viewName) {
33235
- return ctx.params.viewName;
33236
- }
33237
- }
33238
33335
  async function patch3(ctx) {
33336
+ var _a2;
33239
33337
  const appId = ctx.appId;
33240
33338
  const tableId = getTableId(ctx);
33241
33339
  const body2 = ctx.request.body;
@@ -33256,6 +33354,7 @@ async function patch3(ctx) {
33256
33354
  ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:update`, appId, row2, table2);
33257
33355
  ctx.message = `${table2.name} updated successfully.`;
33258
33356
  ctx.body = row2;
33357
+ (_a2 = gridSocket) == null ? void 0 : _a2.emitRowUpdate(ctx, row2);
33259
33358
  } catch (err) {
33260
33359
  ctx.throw(400, err);
33261
33360
  }
@@ -33279,6 +33378,7 @@ async function find7(ctx) {
33279
33378
  });
33280
33379
  }
33281
33380
  async function destroy10(ctx) {
33381
+ var _a2, _b2;
33282
33382
  const appId = ctx.appId;
33283
33383
  const inputs = ctx.request.body;
33284
33384
  const tableId = getTableId(ctx);
@@ -33294,6 +33394,7 @@ async function destroy10(ctx) {
33294
33394
  response2 = rows2;
33295
33395
  for (let row3 of rows2) {
33296
33396
  ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:delete`, appId, row3);
33397
+ (_a2 = gridSocket) == null ? void 0 : _a2.emitRowDeletion(ctx, row3._id);
33297
33398
  }
33298
33399
  } else {
33299
33400
  let resp = await quotas_exports4.addQuery(() => pickApi(tableId).destroy(ctx), {
@@ -33303,6 +33404,7 @@ async function destroy10(ctx) {
33303
33404
  response2 = resp.response;
33304
33405
  row2 = resp.row;
33305
33406
  ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:delete`, appId, row2);
33407
+ (_b2 = gridSocket) == null ? void 0 : _b2.emitRowDeletion(ctx, row2._id);
33306
33408
  }
33307
33409
  ctx.status = 200;
33308
33410
  ctx.row = row2 || {};
@@ -33343,7 +33445,9 @@ var init_row2 = __esm({
33343
33445
  init_external();
33344
33446
  init_utils19();
33345
33447
  init_utils24();
33448
+ init_websockets();
33346
33449
  save11 = async (ctx) => {
33450
+ var _a2;
33347
33451
  const appId = ctx.appId;
33348
33452
  const tableId = getTableId(ctx);
33349
33453
  const body2 = ctx.request.body;
@@ -33359,6 +33463,7 @@ var init_row2 = __esm({
33359
33463
  ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:save`, appId, row2, table2);
33360
33464
  ctx.message = `${table2.name} saved successfully`;
33361
33465
  ctx.body = row2;
33466
+ (_a2 = gridSocket) == null ? void 0 : _a2.emitRowUpdate(ctx, row2);
33362
33467
  };
33363
33468
  exportRows3 = async (ctx) => {
33364
33469
  const tableId = getTableId(ctx);
@@ -34339,6 +34444,7 @@ async function update7(ctx) {
34339
34444
  ctx.body = {
34340
34445
  datasource: await sdk_default.datasources.removeSecretSingle(datasource2)
34341
34446
  };
34447
+ builderSocket.emitDatasourceUpdate(ctx, datasource2);
34342
34448
  }
34343
34449
  async function save13(ctx) {
34344
34450
  const db2 = context_exports.getAppDB();
@@ -34372,6 +34478,7 @@ async function save13(ctx) {
34372
34478
  response2.error = schemaError;
34373
34479
  }
34374
34480
  ctx.body = response2;
34481
+ builderSocket.emitDatasourceUpdate(ctx, datasource2);
34375
34482
  }
34376
34483
  async function destroyInternalTablesBySourceId(datasourceId) {
34377
34484
  const db2 = context_exports.getAppDB();
@@ -34417,6 +34524,7 @@ async function destroy12(ctx) {
34417
34524
  await events_exports.datasource.deleted(datasource2);
34418
34525
  ctx.message = `Datasource deleted.`;
34419
34526
  ctx.status = 200;
34527
+ builderSocket.emitDatasourceDeletion(ctx, datasourceId);
34420
34528
  }
34421
34529
  async function find8(ctx) {
34422
34530
  const database = context_exports.getAppDB();
@@ -34441,6 +34549,7 @@ var init_datasource5 = __esm({
34441
34549
  init_utils18();
34442
34550
  init_src2();
34443
34551
  init_sdk3();
34552
+ init_websockets();
34444
34553
  }
34445
34554
  });
34446
34555
 
@@ -36533,6 +36642,7 @@ async function save16(ctx) {
36533
36642
  ctx.message = `Table ${table2.name} saved successfully.`;
36534
36643
  ctx.eventEmitter && ctx.eventEmitter.emitTable(`table:save`, appId, savedTable);
36535
36644
  ctx.body = savedTable;
36645
+ builderSocket.emitTableUpdate(ctx, savedTable);
36536
36646
  }
36537
36647
  async function destroy15(ctx) {
36538
36648
  const appId = ctx.appId;
@@ -36543,6 +36653,7 @@ async function destroy15(ctx) {
36543
36653
  ctx.status = 200;
36544
36654
  ctx.table = deletedTable;
36545
36655
  ctx.body = { message: `Table ${tableId} deleted.` };
36656
+ builderSocket.emitTableDeletion(ctx, tableId);
36546
36657
  }
36547
36658
  async function bulkImport3(ctx) {
36548
36659
  const tableId = ctx.params.tableId;
@@ -36592,6 +36703,7 @@ var init_table4 = __esm({
36592
36703
  init_src2();
36593
36704
  init_sdk3();
36594
36705
  init_csv();
36706
+ init_websockets();
36595
36707
  }
36596
36708
  });
36597
36709
 
@@ -38259,7 +38371,8 @@ async function fetchAppPackage(ctx) {
38259
38371
  application: { ...application2, upgradableVersion: environment_default.VERSION },
38260
38372
  screens,
38261
38373
  layouts,
38262
- clientLibPath
38374
+ clientLibPath,
38375
+ hasLock: await doesUserHaveLock(application2.appId, ctx.user)
38263
38376
  };
38264
38377
  }
38265
38378
  async function performAppCreate(ctx) {
@@ -39464,17 +39577,15 @@ async function save18(ctx) {
39464
39577
  if (!view2.meta.schema) {
39465
39578
  view2.meta.schema = table2.schema;
39466
39579
  }
39467
- table2.views[viewName] = view2.meta;
39580
+ table2.views[viewName] = { ...view2.meta, name: viewName };
39468
39581
  if (originalName) {
39469
39582
  delete table2.views[originalName];
39470
39583
  existingTable.views[viewName] = existingTable.views[originalName];
39471
39584
  }
39472
39585
  await db2.put(table2);
39473
39586
  await handleViewEvents(existingTable.views[viewName], table2.views[viewName]);
39474
- ctx.body = {
39475
- ...table2.views[viewToSave.name],
39476
- name: viewToSave.name
39477
- };
39587
+ ctx.body = table2.views[viewName];
39588
+ builderSocket.emitTableUpdate(ctx, table2);
39478
39589
  }
39479
39590
  async function calculationEvents(existingView, newView) {
39480
39591
  const existingCalculation = existingView && existingView.calculation;
@@ -39520,6 +39631,7 @@ async function destroy18(ctx) {
39520
39631
  await db2.put(table2);
39521
39632
  await events_exports.view.deleted(view2);
39522
39633
  ctx.body = view2;
39634
+ builderSocket.emitTableUpdate(ctx, table2);
39523
39635
  }
39524
39636
  async function exportView(ctx) {
39525
39637
  const viewName = decodeURIComponent(ctx.query.view);
@@ -39583,6 +39695,7 @@ var init_view4 = __esm({
39583
39695
  init_sdk3();
39584
39696
  init_constants4();
39585
39697
  init_utils24();
39698
+ init_websockets();
39586
39699
  ({ cloneDeep: cloneDeep17, isEqual: isEqual6 } = require("lodash"));
39587
39700
  }
39588
39701
  });
@@ -42794,7 +42907,7 @@ async function prepareUpload({ s3Key, bucket, metadata, file }) {
42794
42907
  key: response2.Key
42795
42908
  };
42796
42909
  }
42797
- var import_string_templates10, import_aws_sdk4, import_fs14, uuid5, send2, toggleBetaUiFeature, serveBuilder, uploadFile, deleteObjects, serveApp, serveBuilderPreview, serveClientLibrary, getSignedUploadURL;
42910
+ var import_string_templates10, import_aws_sdk4, import_fs14, uuid6, send2, toggleBetaUiFeature, serveBuilder, uploadFile, deleteObjects, serveApp, serveBuilderPreview, serveClientLibrary, getSignedUploadURL;
42798
42911
  var init_static = __esm({
42799
42912
  "src/api/controllers/static/index.ts"() {
42800
42913
  init_centralPath();
@@ -42809,7 +42922,7 @@ var init_static = __esm({
42809
42922
  init_sdk3();
42810
42923
  init_src4();
42811
42924
  require("svelte/register");
42812
- uuid5 = require("uuid");
42925
+ uuid6 = require("uuid");
42813
42926
  send2 = require("koa-send");
42814
42927
  toggleBetaUiFeature = async function(ctx) {
42815
42928
  const cookieName = `beta:${ctx.params.feature}`;
@@ -42841,7 +42954,7 @@ var init_static = __esm({
42841
42954
  let files = ctx.request.files.file.length > 1 ? Array.from(ctx.request.files.file) : [ctx.request.files.file];
42842
42955
  const uploads = files.map(async (file) => {
42843
42956
  const fileExtension = [...file.name.split(".")].pop();
42844
- const processedFileName = `${uuid5.v4()}.${fileExtension}`;
42957
+ const processedFileName = `${uuid6.v4()}.${fileExtension}`;
42845
42958
  return prepareUpload({
42846
42959
  file,
42847
42960
  s3Key: `${context_exports.getProdAppId()}/attachments/${processedFileName}`,
@@ -44390,6 +44503,7 @@ async function startup(app2, server2) {
44390
44503
  init9();
44391
44504
  await init17();
44392
44505
  init19();
44506
+ initialise(app2, server2);
44393
44507
  if (!environment_default2.HTTP_MIGRATIONS && !environment_default2.isTest()) {
44394
44508
  try {
44395
44509
  await migrate();
@@ -44450,6 +44564,7 @@ var init_startup = __esm({
44450
44564
  init_src4();
44451
44565
  init_api8();
44452
44566
  init_sdk3();
44567
+ init_websockets();
44453
44568
  STARTUP_RAN = false;
44454
44569
  }
44455
44570
  });
@@ -44484,7 +44599,6 @@ var init_app10 = __esm({
44484
44599
  init_automations8();
44485
44600
  init_threads();
44486
44601
  init_redis4();
44487
- init_websockets();
44488
44602
  init_src2();
44489
44603
  init_startup();
44490
44604
  if (process.env.DD_APM_ENABLED) {
@@ -44527,7 +44641,6 @@ var init_app10 = __esm({
44527
44641
  }
44528
44642
  server = import_http2.default.createServer(app.callback());
44529
44643
  destroyable(server);
44530
- initialise(app, server);
44531
44644
  shuttingDown = false;
44532
44645
  errCode = 0;
44533
44646
  server.on("close", async () => {