@teamkeel/functions-runtime 0.411.0 → 0.412.0-next.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.
Files changed (49) hide show
  1. package/dist/index.d.mts +340 -0
  2. package/dist/index.d.ts +340 -0
  3. package/dist/index.js +3093 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/index.mjs +3097 -0
  6. package/dist/index.mjs.map +1 -0
  7. package/package.json +23 -5
  8. package/.env.test +0 -2
  9. package/compose.yaml +0 -10
  10. package/src/Duration.js +0 -40
  11. package/src/Duration.test.js +0 -34
  12. package/src/File.js +0 -295
  13. package/src/ModelAPI.js +0 -377
  14. package/src/ModelAPI.test.js +0 -1428
  15. package/src/QueryBuilder.js +0 -184
  16. package/src/QueryContext.js +0 -90
  17. package/src/RequestHeaders.js +0 -21
  18. package/src/TimePeriod.js +0 -89
  19. package/src/TimePeriod.test.js +0 -148
  20. package/src/applyAdditionalQueryConstraints.js +0 -22
  21. package/src/applyJoins.js +0 -67
  22. package/src/applyWhereConditions.js +0 -124
  23. package/src/auditing.js +0 -110
  24. package/src/auditing.test.js +0 -330
  25. package/src/camelCasePlugin.js +0 -52
  26. package/src/casing.js +0 -54
  27. package/src/casing.test.js +0 -56
  28. package/src/consts.js +0 -14
  29. package/src/database.js +0 -244
  30. package/src/errors.js +0 -160
  31. package/src/handleJob.js +0 -110
  32. package/src/handleJob.test.js +0 -270
  33. package/src/handleRequest.js +0 -153
  34. package/src/handleRequest.test.js +0 -463
  35. package/src/handleRoute.js +0 -112
  36. package/src/handleSubscriber.js +0 -105
  37. package/src/index.d.ts +0 -317
  38. package/src/index.js +0 -38
  39. package/src/parsing.js +0 -113
  40. package/src/parsing.test.js +0 -140
  41. package/src/permissions.js +0 -77
  42. package/src/permissions.test.js +0 -118
  43. package/src/tracing.js +0 -184
  44. package/src/tracing.test.js +0 -147
  45. package/src/tryExecuteFunction.js +0 -91
  46. package/src/tryExecuteJob.js +0 -29
  47. package/src/tryExecuteSubscriber.js +0 -17
  48. package/src/type-utils.js +0 -18
  49. package/vite.config.js +0 -7
package/dist/index.js ADDED
@@ -0,0 +1,3093 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
9
+ var __esm = (fn, res) => function __init() {
10
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
+ };
12
+ var __commonJS = (cb, mod) => function __require() {
13
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
14
+ };
15
+ var __export = (target, all) => {
16
+ for (var name in all)
17
+ __defProp(target, name, { get: all[name], enumerable: true });
18
+ };
19
+ var __copyProps = (to, from, except, desc) => {
20
+ if (from && typeof from === "object" || typeof from === "function") {
21
+ for (let key of __getOwnPropNames(from))
22
+ if (!__hasOwnProp.call(to, key) && key !== except)
23
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
24
+ }
25
+ return to;
26
+ };
27
+ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
28
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
29
+ // If the importer is in node compatibility mode or this is not an ESM
30
+ // file that has been converted to a CommonJS file using a Babel-
31
+ // compatible transform (i.e. "__esModule" has not been set), then set
32
+ // "default" to the CommonJS "module.exports" for node compatibility.
33
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
34
+ mod
35
+ ));
36
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
37
+
38
+ // src/auditing.js
39
+ var require_auditing = __commonJS({
40
+ "src/auditing.js"(exports2, module2) {
41
+ "use strict";
42
+ var { AsyncLocalStorage } = require("async_hooks");
43
+ var TraceParent = require("traceparent");
44
+ var { sql, SelectionNode } = require("kysely");
45
+ var auditContextStorage = new AsyncLocalStorage();
46
+ async function withAuditContext(request, cb) {
47
+ var _a, _b, _c, _d;
48
+ let audit = {};
49
+ if ((_a = request.meta) == null ? void 0 : _a.identity) {
50
+ audit.identityId = request.meta.identity.id;
51
+ }
52
+ if ((_c = (_b = request.meta) == null ? void 0 : _b.tracing) == null ? void 0 : _c.traceparent) {
53
+ audit.traceId = (_d = TraceParent.fromString(
54
+ request.meta.tracing.traceparent
55
+ )) == null ? void 0 : _d.traceId;
56
+ }
57
+ return await auditContextStorage.run(audit, () => {
58
+ return cb();
59
+ });
60
+ }
61
+ __name(withAuditContext, "withAuditContext");
62
+ function getAuditContext() {
63
+ let auditStore = auditContextStorage.getStore();
64
+ return {
65
+ identityId: auditStore == null ? void 0 : auditStore.identityId,
66
+ traceId: auditStore == null ? void 0 : auditStore.traceId
67
+ };
68
+ }
69
+ __name(getAuditContext, "getAuditContext");
70
+ var _AuditContextPlugin = class _AuditContextPlugin {
71
+ constructor() {
72
+ this.identityIdAlias = "__keel_identity_id";
73
+ this.traceIdAlias = "__keel_trace_id";
74
+ }
75
+ // Appends set_identity_id() and set_trace_id() function calls to the returning statement
76
+ // of INSERT, UPDATE and DELETE operations.
77
+ transformQuery(args) {
78
+ switch (args.node.kind) {
79
+ case "InsertQueryNode":
80
+ case "UpdateQueryNode":
81
+ case "DeleteQueryNode":
82
+ const returning = {
83
+ kind: "ReturningNode",
84
+ selections: []
85
+ };
86
+ if (args.node.returning) {
87
+ returning.selections.push(...args.node.returning.selections);
88
+ }
89
+ const audit = getAuditContext();
90
+ if (audit.identityId) {
91
+ const rawNode = sql`set_identity_id(${audit.identityId})`.as(this.identityIdAlias).toOperationNode();
92
+ returning.selections.push(SelectionNode.create(rawNode));
93
+ }
94
+ if (audit.traceId) {
95
+ const rawNode = sql`set_trace_id(${audit.traceId})`.as(this.traceIdAlias).toOperationNode();
96
+ returning.selections.push(SelectionNode.create(rawNode));
97
+ }
98
+ return {
99
+ ...args.node,
100
+ returning
101
+ };
102
+ }
103
+ return {
104
+ ...args.node
105
+ };
106
+ }
107
+ // Drops the set_identity_id() and set_trace_id() fields from the result.
108
+ transformResult(args) {
109
+ var _a;
110
+ if ((_a = args.result) == null ? void 0 : _a.rows) {
111
+ for (let i = 0; i < args.result.rows.length; i++) {
112
+ delete args.result.rows[i][this.identityIdAlias];
113
+ delete args.result.rows[i][this.traceIdAlias];
114
+ }
115
+ }
116
+ return args.result;
117
+ }
118
+ };
119
+ __name(_AuditContextPlugin, "AuditContextPlugin");
120
+ var AuditContextPlugin = _AuditContextPlugin;
121
+ module2.exports.withAuditContext = withAuditContext;
122
+ module2.exports.getAuditContext = getAuditContext;
123
+ module2.exports.AuditContextPlugin = AuditContextPlugin;
124
+ }
125
+ });
126
+
127
+ // src/Duration.js
128
+ var require_Duration = __commonJS({
129
+ "src/Duration.js"(exports2, module2) {
130
+ "use strict";
131
+ var parseInterval = require("postgres-interval");
132
+ var isoRegex = /^P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?)?$/;
133
+ var _Duration = class _Duration {
134
+ constructor(postgresString) {
135
+ this._typename = "Duration";
136
+ this.pgInterval = postgresString;
137
+ this._interval = parseInterval(postgresString);
138
+ }
139
+ static fromISOString(isoString) {
140
+ const match = isoString.match(isoRegex);
141
+ if (match) {
142
+ let d = new _Duration();
143
+ d._interval.years = match[1];
144
+ d._interval.months = match[2];
145
+ d._interval.days = match[3];
146
+ d._interval.hours = match[4];
147
+ d._interval.minutes = match[5];
148
+ d._interval.seconds = match[6];
149
+ return d;
150
+ }
151
+ return new _Duration();
152
+ }
153
+ toISOString() {
154
+ return this._interval.toISOStringShort();
155
+ }
156
+ toPostgres() {
157
+ return this._interval.toPostgres();
158
+ }
159
+ };
160
+ __name(_Duration, "Duration");
161
+ var Duration = _Duration;
162
+ module2.exports = {
163
+ Duration
164
+ };
165
+ }
166
+ });
167
+
168
+ // src/type-utils.js
169
+ var require_type_utils = __commonJS({
170
+ "src/type-utils.js"(exports2, module2) {
171
+ "use strict";
172
+ var { Duration } = require_Duration();
173
+ function isPlainObject(obj) {
174
+ return Object.prototype.toString.call(obj) === "[object Object]";
175
+ }
176
+ __name(isPlainObject, "isPlainObject");
177
+ function isRichType(obj) {
178
+ if (!isPlainObject(obj)) {
179
+ return false;
180
+ }
181
+ return obj instanceof Duration;
182
+ }
183
+ __name(isRichType, "isRichType");
184
+ module2.exports = {
185
+ isPlainObject,
186
+ isRichType
187
+ };
188
+ }
189
+ });
190
+
191
+ // src/camelCasePlugin.js
192
+ var require_camelCasePlugin = __commonJS({
193
+ "src/camelCasePlugin.js"(exports2, module2) {
194
+ "use strict";
195
+ var { CamelCasePlugin } = require("kysely");
196
+ var { isPlainObject, isRichType } = require_type_utils();
197
+ var _KeelCamelCasePlugin = class _KeelCamelCasePlugin {
198
+ constructor(opt) {
199
+ this.opt = opt;
200
+ this.CamelCasePlugin = new CamelCasePlugin(opt);
201
+ }
202
+ transformQuery(args) {
203
+ return this.CamelCasePlugin.transformQuery(args);
204
+ }
205
+ async transformResult(args) {
206
+ if (args.result.rows && Array.isArray(args.result.rows)) {
207
+ return {
208
+ ...args.result,
209
+ rows: args.result.rows.map((row) => this.mapRow(row))
210
+ };
211
+ }
212
+ return args.result;
213
+ }
214
+ mapRow(row) {
215
+ return Object.keys(row).reduce((obj, key) => {
216
+ if (key.endsWith("__sequence")) {
217
+ return obj;
218
+ }
219
+ let value = row[key];
220
+ if (Array.isArray(value)) {
221
+ value = value.map(
222
+ (it) => canMap(it, this.opt) ? this.mapRow(it) : it
223
+ );
224
+ } else if (canMap(value, this.opt)) {
225
+ value = this.mapRow(value);
226
+ }
227
+ obj[this.CamelCasePlugin.camelCase(key)] = value;
228
+ return obj;
229
+ }, {});
230
+ }
231
+ };
232
+ __name(_KeelCamelCasePlugin, "KeelCamelCasePlugin");
233
+ var KeelCamelCasePlugin = _KeelCamelCasePlugin;
234
+ function canMap(obj, opt) {
235
+ return isPlainObject(obj) && !(opt == null ? void 0 : opt.maintainNestedObjectKeys) && !isRichType(obj);
236
+ }
237
+ __name(canMap, "canMap");
238
+ module2.exports.KeelCamelCasePlugin = KeelCamelCasePlugin;
239
+ }
240
+ });
241
+
242
+ // src/tracing.js
243
+ var require_tracing = __commonJS({
244
+ "src/tracing.js"(exports2, module2) {
245
+ "use strict";
246
+ var opentelemetry = require("@opentelemetry/api");
247
+ var { BatchSpanProcessor } = require("@opentelemetry/sdk-trace-base");
248
+ var {
249
+ OTLPTraceExporter
250
+ } = require("@opentelemetry/exporter-trace-otlp-proto");
251
+ var { NodeTracerProvider } = require("@opentelemetry/sdk-trace-node");
252
+ var { envDetectorSync } = require("@opentelemetry/resources");
253
+ async function withSpan(name, fn) {
254
+ return getTracer().startActiveSpan(name, async (span) => {
255
+ try {
256
+ return await fn(span);
257
+ } catch (err) {
258
+ span.recordException(err);
259
+ span.setStatus({
260
+ code: opentelemetry.SpanStatusCode.ERROR,
261
+ message: err.message
262
+ });
263
+ throw err;
264
+ } finally {
265
+ span.end();
266
+ }
267
+ });
268
+ }
269
+ __name(withSpan, "withSpan");
270
+ function patchFetch() {
271
+ if (!globalThis.fetch.patched) {
272
+ const originalFetch = globalThis.fetch;
273
+ globalThis.fetch = async (...args) => {
274
+ return withSpan("fetch", async (span) => {
275
+ const url = new URL(
276
+ args[0] instanceof Request ? args[0].url : String(args[0])
277
+ );
278
+ span.setAttribute("http.url", url.toString());
279
+ const scheme = url.protocol.replace(":", "");
280
+ span.setAttribute("http.scheme", scheme);
281
+ const options = args[0] instanceof Request ? args[0] : args[1] || {};
282
+ const method = (options.method || "GET").toUpperCase();
283
+ span.setAttribute("http.method", method);
284
+ const res = await originalFetch(...args);
285
+ span.setAttribute("http.status", res.status);
286
+ span.setAttribute("http.status_text", res.statusText);
287
+ return res;
288
+ });
289
+ };
290
+ globalThis.fetch.patched = true;
291
+ }
292
+ }
293
+ __name(patchFetch, "patchFetch");
294
+ function patchConsoleLog() {
295
+ if (!console.log.patched) {
296
+ const originalConsoleLog = console.log;
297
+ console.log = (...args) => {
298
+ const span = opentelemetry.trace.getActiveSpan();
299
+ if (span) {
300
+ const output = args.map((arg) => {
301
+ if (arg instanceof Error) {
302
+ return arg.stack;
303
+ }
304
+ if (typeof arg === "object") {
305
+ try {
306
+ return JSON.stringify(arg, getCircularReplacer());
307
+ } catch (error) {
308
+ return "[Object with circular references]";
309
+ }
310
+ }
311
+ if (typeof arg === "function") {
312
+ return arg() || arg.name || arg.toString();
313
+ }
314
+ return String(arg);
315
+ }).join(" ");
316
+ span.addEvent(output);
317
+ }
318
+ originalConsoleLog(...args);
319
+ };
320
+ console.log.patched = true;
321
+ }
322
+ }
323
+ __name(patchConsoleLog, "patchConsoleLog");
324
+ function patchConsoleError() {
325
+ if (!console.error.patched) {
326
+ const originalConsoleError = console.error;
327
+ console.error = (...args) => {
328
+ const span = opentelemetry.trace.getActiveSpan();
329
+ if (span) {
330
+ const output = args.map((arg) => {
331
+ if (arg instanceof Error) {
332
+ return arg.stack;
333
+ }
334
+ if (typeof arg === "object") {
335
+ try {
336
+ return JSON.stringify(arg, getCircularReplacer());
337
+ } catch (error) {
338
+ return "[Object with circular references]";
339
+ }
340
+ }
341
+ if (typeof arg === "function") {
342
+ return arg() || arg.name || arg.toString();
343
+ }
344
+ return String(arg);
345
+ }).join(" ");
346
+ span.setStatus({
347
+ code: opentelemetry.SpanStatusCode.ERROR,
348
+ message: output
349
+ });
350
+ }
351
+ originalConsoleError(...args);
352
+ };
353
+ console.error.patched = true;
354
+ }
355
+ }
356
+ __name(patchConsoleError, "patchConsoleError");
357
+ function getCircularReplacer() {
358
+ const seen = /* @__PURE__ */ new WeakSet();
359
+ return (key, value) => {
360
+ if (typeof value === "object" && value !== null) {
361
+ if (seen.has(value)) {
362
+ return "[Circular]";
363
+ }
364
+ seen.add(value);
365
+ }
366
+ return value;
367
+ };
368
+ }
369
+ __name(getCircularReplacer, "getCircularReplacer");
370
+ function init() {
371
+ if (process.env.KEEL_TRACING_ENABLED == "true") {
372
+ const exporter = new OTLPTraceExporter();
373
+ const processor = new BatchSpanProcessor(exporter);
374
+ const provider = new NodeTracerProvider({
375
+ resource: envDetectorSync.detect(),
376
+ spanProcessors: [processor]
377
+ });
378
+ provider.register();
379
+ }
380
+ patchFetch();
381
+ patchConsoleLog();
382
+ patchConsoleError();
383
+ }
384
+ __name(init, "init");
385
+ async function forceFlush() {
386
+ const provider = opentelemetry.trace.getTracerProvider().getDelegate();
387
+ if (provider && provider.forceFlush) {
388
+ await provider.forceFlush();
389
+ }
390
+ }
391
+ __name(forceFlush, "forceFlush");
392
+ function getTracer() {
393
+ return opentelemetry.trace.getTracer("functions");
394
+ }
395
+ __name(getTracer, "getTracer");
396
+ function spanNameForModelAPI(modelName, action) {
397
+ return `Database ${modelName}.${action}`;
398
+ }
399
+ __name(spanNameForModelAPI, "spanNameForModelAPI");
400
+ module2.exports = {
401
+ getTracer,
402
+ withSpan,
403
+ init,
404
+ forceFlush,
405
+ spanNameForModelAPI
406
+ };
407
+ }
408
+ });
409
+
410
+ // src/database.js
411
+ var require_database = __commonJS({
412
+ "src/database.js"(exports2, module2) {
413
+ "use strict";
414
+ var { Kysely, PostgresDialect } = require("kysely");
415
+ var neonserverless = require("@neondatabase/serverless");
416
+ var { AsyncLocalStorage } = require("async_hooks");
417
+ var { AuditContextPlugin } = require_auditing();
418
+ var { KeelCamelCasePlugin } = require_camelCasePlugin();
419
+ var pg = require("pg");
420
+ var { withSpan } = require_tracing();
421
+ var ws = require("ws");
422
+ var fs = require("fs");
423
+ var { Duration } = require_Duration();
424
+ async function withDatabase(db, requiresTransaction, cb) {
425
+ if (requiresTransaction) {
426
+ return db.transaction().execute(async (transaction) => {
427
+ return dbInstance.run(transaction, async () => {
428
+ return cb({ transaction });
429
+ });
430
+ });
431
+ }
432
+ return db.connection().execute(async (sDb) => {
433
+ return dbInstance.run(sDb, async () => {
434
+ return cb({ sDb });
435
+ });
436
+ });
437
+ }
438
+ __name(withDatabase, "withDatabase");
439
+ var dbInstance = new AsyncLocalStorage();
440
+ var vitestDb = null;
441
+ function useDatabase2() {
442
+ let fromStore = dbInstance.getStore();
443
+ if (fromStore) {
444
+ return fromStore;
445
+ }
446
+ if ("NODE_ENV" in process.env && process.env.NODE_ENV == "test") {
447
+ if (!vitestDb) {
448
+ vitestDb = createDatabaseClient();
449
+ }
450
+ return vitestDb;
451
+ }
452
+ console.trace();
453
+ throw new Error("useDatabase must be called within a function");
454
+ }
455
+ __name(useDatabase2, "useDatabase");
456
+ function createDatabaseClient({ connString } = {}) {
457
+ const db = new Kysely({
458
+ dialect: getDialect(connString),
459
+ plugins: [
460
+ // ensures that the audit context data is written to Postgres configuration parameters
461
+ new AuditContextPlugin(),
462
+ // allows users to query using camelCased versions of the database column names, which
463
+ // should match the names we use in our schema.
464
+ // We're using an extended version of Kysely's CamelCasePlugin which avoids changing keys of objects that represent
465
+ // rich data formats, specific to Keel (e.g. Duration)
466
+ new KeelCamelCasePlugin()
467
+ ],
468
+ log(event) {
469
+ if ("DEBUG" in process.env) {
470
+ if (event.level === "query") {
471
+ console.log(event.query.sql);
472
+ console.log(event.query.parameters);
473
+ }
474
+ }
475
+ }
476
+ });
477
+ return db;
478
+ }
479
+ __name(createDatabaseClient, "createDatabaseClient");
480
+ var _InstrumentedPool = class _InstrumentedPool extends pg.Pool {
481
+ async connect(...args) {
482
+ const _super = super.connect.bind(this);
483
+ return withSpan("Database Connect", function(span) {
484
+ span.setAttribute("dialect", process.env["KEEL_DB_CONN_TYPE"]);
485
+ return _super(...args);
486
+ });
487
+ }
488
+ };
489
+ __name(_InstrumentedPool, "InstrumentedPool");
490
+ var InstrumentedPool = _InstrumentedPool;
491
+ var _InstrumentedNeonServerlessPool = class _InstrumentedNeonServerlessPool extends neonserverless.Pool {
492
+ async connect(...args) {
493
+ const _super = super.connect.bind(this);
494
+ return withSpan("Database Connect", function(span) {
495
+ span.setAttribute("dialect", process.env["KEEL_DB_CONN_TYPE"]);
496
+ return _super(...args);
497
+ });
498
+ }
499
+ };
500
+ __name(_InstrumentedNeonServerlessPool, "InstrumentedNeonServerlessPool");
501
+ var InstrumentedNeonServerlessPool = _InstrumentedNeonServerlessPool;
502
+ var txStatements = {
503
+ begin: "Transaction Begin",
504
+ commit: "Transaction Commit",
505
+ rollback: "Transaction Rollback"
506
+ };
507
+ var _InstrumentedClient = class _InstrumentedClient extends pg.Client {
508
+ async query(...args) {
509
+ const _super = super.query.bind(this);
510
+ const sql = args[0];
511
+ let sqlAttribute = false;
512
+ let spanName = txStatements[sql.toLowerCase()];
513
+ if (!spanName) {
514
+ spanName = "Database Query";
515
+ sqlAttribute = true;
516
+ }
517
+ return withSpan(spanName, function(span) {
518
+ if (sqlAttribute) {
519
+ span.setAttribute("sql", args[0]);
520
+ span.setAttribute("dialect", process.env["KEEL_DB_CONN_TYPE"]);
521
+ }
522
+ return _super(...args);
523
+ });
524
+ }
525
+ };
526
+ __name(_InstrumentedClient, "InstrumentedClient");
527
+ var InstrumentedClient = _InstrumentedClient;
528
+ function getDialect(connString) {
529
+ const dbConnType = process.env.KEEL_DB_CONN_TYPE;
530
+ switch (dbConnType) {
531
+ case "pg":
532
+ pg.types.setTypeParser(pg.types.builtins.NUMERIC, function(val) {
533
+ return parseFloat(val);
534
+ });
535
+ pg.types.setTypeParser(pg.types.builtins.INTERVAL, function(val) {
536
+ return new Duration(val);
537
+ });
538
+ return new PostgresDialect({
539
+ pool: new InstrumentedPool({
540
+ Client: InstrumentedClient,
541
+ // Increased idle time before closing a connection in the local pool (from 10s default).
542
+ // Establising a new connection on (almost) every functions query can be expensive, so this
543
+ // will reduce having to open connections as regularly. https://node-postgres.com/apis/pool
544
+ //
545
+ // NOTE: We should consider setting this to 0 (i.e. never pool locally) and open and close
546
+ // connections with each invocation. This is because the freeze/thaw nature of lambdas can cause problems
547
+ // with long-lived connections - see https://github.com/brianc/node-postgres/issues/2718
548
+ // Once we're "fully regional" this should not be a performance problem anymore.
549
+ //
550
+ // Although I doubt we will run into these freeze/thaw issues if idleTimeoutMillis is always shorter than the
551
+ // time is takes for a lambda to freeze (which is not a constant, but could be as short as several minutes,
552
+ // https://www.pluralsight.com/resources/blog/cloud/how-long-does-aws-lambda-keep-your-idle-functions-around-before-a-cold-start)
553
+ idleTimeoutMillis: 5e4,
554
+ // If connString is not passed fall back to reading from env var
555
+ connectionString: connString || process.env.KEEL_DB_CONN,
556
+ // Allow the setting of a cert (.pem) file. RDS requires this to enforce SSL.
557
+ ...process.env.KEEL_DB_CERT ? { ssl: { ca: fs.readFileSync(process.env.KEEL_DB_CERT) } } : void 0
558
+ })
559
+ });
560
+ case "neon":
561
+ neonserverless.types.setTypeParser(
562
+ pg.types.builtins.NUMERIC,
563
+ function(val) {
564
+ return parseFloat(val);
565
+ }
566
+ );
567
+ neonserverless.types.setTypeParser(
568
+ pg.types.builtins.INTERVAL,
569
+ function(val) {
570
+ return new Duration(val);
571
+ }
572
+ );
573
+ neonserverless.neonConfig.webSocketConstructor = ws;
574
+ const pool = new InstrumentedNeonServerlessPool({
575
+ // If connString is not passed fall back to reading from env var
576
+ connectionString: connString || process.env.KEEL_DB_CONN
577
+ });
578
+ pool.on("connect", (client) => {
579
+ const originalQuery = client.query;
580
+ client.query = function(...args) {
581
+ const sql = args[0];
582
+ let sqlAttribute = false;
583
+ let spanName = txStatements[sql.toLowerCase()];
584
+ if (!spanName) {
585
+ spanName = "Database Query";
586
+ sqlAttribute = true;
587
+ }
588
+ return withSpan(spanName, function(span) {
589
+ if (sqlAttribute) {
590
+ span.setAttribute("sql", args[0]);
591
+ span.setAttribute("dialect", dbConnType);
592
+ }
593
+ return originalQuery.apply(client, args);
594
+ });
595
+ };
596
+ });
597
+ return new PostgresDialect({
598
+ pool
599
+ });
600
+ default:
601
+ throw Error("unexpected KEEL_DB_CONN_TYPE: " + dbConnType);
602
+ }
603
+ }
604
+ __name(getDialect, "getDialect");
605
+ module2.exports = {
606
+ createDatabaseClient,
607
+ useDatabase: useDatabase2,
608
+ withDatabase
609
+ };
610
+ }
611
+ });
612
+
613
+ // src/errors.js
614
+ var require_errors = __commonJS({
615
+ "src/errors.js"(exports2, module2) {
616
+ "use strict";
617
+ var { createJSONRPCErrorResponse } = require("json-rpc-2.0");
618
+ var RuntimeErrors = {
619
+ // Catchall error type for unhandled execution errors during custom function
620
+ UnknownError: -32001,
621
+ // DatabaseError represents any error at pg level that isn't handled explicitly below
622
+ DatabaseError: -32002,
623
+ // No result returned from custom function by user
624
+ NoResultError: -32003,
625
+ // When trying to delete/update a non existent record in the db
626
+ RecordNotFoundError: -32004,
627
+ ForeignKeyConstraintError: -32005,
628
+ NotNullConstraintError: -32006,
629
+ UniqueConstraintError: -32007,
630
+ PermissionError: -32008,
631
+ BadRequestError: -32009
632
+ };
633
+ var _PermissionError = class _PermissionError extends Error {
634
+ };
635
+ __name(_PermissionError, "PermissionError");
636
+ var PermissionError = _PermissionError;
637
+ var _DatabaseError = class _DatabaseError extends Error {
638
+ constructor(error) {
639
+ super(error.message);
640
+ this.error = error;
641
+ }
642
+ };
643
+ __name(_DatabaseError, "DatabaseError");
644
+ var DatabaseError = _DatabaseError;
645
+ var _NotFoundError = class _NotFoundError extends Error {
646
+ errorCode = RuntimeErrors.RecordNotFoundError;
647
+ constructor(message) {
648
+ super(message);
649
+ }
650
+ };
651
+ __name(_NotFoundError, "NotFoundError");
652
+ var NotFoundError = _NotFoundError;
653
+ var _BadRequestError = class _BadRequestError extends Error {
654
+ errorCode = RuntimeErrors.BadRequestError;
655
+ constructor(message = "bad request") {
656
+ super(message);
657
+ }
658
+ };
659
+ __name(_BadRequestError, "BadRequestError");
660
+ var BadRequestError = _BadRequestError;
661
+ var _UnknownError = class _UnknownError extends Error {
662
+ errorCode = RuntimeErrors.UnknownError;
663
+ constructor(message = "unknown error") {
664
+ super(message);
665
+ }
666
+ };
667
+ __name(_UnknownError, "UnknownError");
668
+ var UnknownError = _UnknownError;
669
+ var ErrorPresets = {
670
+ NotFound: NotFoundError,
671
+ BadRequest: BadRequestError,
672
+ Unknown: UnknownError
673
+ };
674
+ function errorToJSONRPCResponse(request, e) {
675
+ switch (e.constructor.name) {
676
+ case "PermissionError":
677
+ return createJSONRPCErrorResponse(
678
+ request.id,
679
+ RuntimeErrors.PermissionError,
680
+ e.message
681
+ );
682
+ // Any error thrown in the ModelAPI class is
683
+ // wrapped in a DatabaseError in order to differentiate 'our code' vs the user's own code.
684
+ case "NoResultError":
685
+ return createJSONRPCErrorResponse(
686
+ request.id,
687
+ // to be matched to https://github.com/teamkeel/keel/blob/e3115ffe381bfc371d4f45bbf96a15072a994ce5/runtime/actions/update.go#L54-L54
688
+ RuntimeErrors.RecordNotFoundError,
689
+ ""
690
+ // Don't pass on the message as we want to normalise these at the runtime layer but still support custom messages in other NotFound errors
691
+ );
692
+ case "DatabaseError":
693
+ let err = e;
694
+ if (e instanceof DatabaseError) {
695
+ err = e.error;
696
+ }
697
+ if (err.constructor.name == "NoResultError") {
698
+ return createJSONRPCErrorResponse(
699
+ request.id,
700
+ // to be matched to https://github.com/teamkeel/keel/blob/e3115ffe381bfc371d4f45bbf96a15072a994ce5/runtime/actions/update.go#L54-L54
701
+ RuntimeErrors.RecordNotFoundError,
702
+ ""
703
+ // Don't pass on the message as we want to normalise these at the runtime layer but still support custom messages in other NotFound errors
704
+ );
705
+ }
706
+ if ("code" in err) {
707
+ const { code, detail, table: table2 } = err;
708
+ let rpcErrorCode, column, value;
709
+ const [col, val] = parseKeyMessage(err.detail);
710
+ column = col;
711
+ value = val;
712
+ switch (code) {
713
+ case "23502":
714
+ rpcErrorCode = RuntimeErrors.NotNullConstraintError;
715
+ column = err.column;
716
+ break;
717
+ case "23503":
718
+ rpcErrorCode = RuntimeErrors.ForeignKeyConstraintError;
719
+ break;
720
+ case "23505":
721
+ rpcErrorCode = RuntimeErrors.UniqueConstraintError;
722
+ break;
723
+ default:
724
+ rpcErrorCode = RuntimeErrors.DatabaseError;
725
+ break;
726
+ }
727
+ return createJSONRPCErrorResponse(request.id, rpcErrorCode, e.message, {
728
+ table: table2,
729
+ column,
730
+ code,
731
+ detail,
732
+ value
733
+ });
734
+ }
735
+ return createJSONRPCErrorResponse(
736
+ request.id,
737
+ RuntimeErrors.DatabaseError,
738
+ e.message
739
+ );
740
+ default:
741
+ return createJSONRPCErrorResponse(
742
+ request.id,
743
+ e.errorCode ?? RuntimeErrors.UnknownError,
744
+ e.message
745
+ );
746
+ }
747
+ }
748
+ __name(errorToJSONRPCResponse, "errorToJSONRPCResponse");
749
+ var keyMessagePattern = /\Key\s[(](.*)[)][=][(](.*)[)]/;
750
+ var parseKeyMessage = /* @__PURE__ */ __name((msg) => {
751
+ const [, col, value] = keyMessagePattern.exec(msg) || [];
752
+ return [col, value];
753
+ }, "parseKeyMessage");
754
+ module2.exports = {
755
+ errorToJSONRPCResponse,
756
+ RuntimeErrors,
757
+ DatabaseError,
758
+ PermissionError,
759
+ ErrorPresets
760
+ };
761
+ }
762
+ });
763
+
764
+ // src/File.js
765
+ var require_File = __commonJS({
766
+ "src/File.js"(exports2, module2) {
767
+ "use strict";
768
+ var {
769
+ S3Client,
770
+ PutObjectCommand,
771
+ GetObjectCommand
772
+ } = require("@aws-sdk/client-s3");
773
+ var { fromEnv } = require("@aws-sdk/credential-providers");
774
+ var { getSignedUrl } = require("@aws-sdk/s3-request-presigner");
775
+ var { useDatabase: useDatabase2 } = require_database();
776
+ var { DatabaseError } = require_errors();
777
+ var KSUID = require("ksuid");
778
+ var s3Client = (() => {
779
+ if (!process.env.KEEL_FILES_BUCKET_NAME) {
780
+ return null;
781
+ }
782
+ const endpoint = process.env.TEST_AWS_ENDPOINT;
783
+ if (!endpoint) {
784
+ return new S3Client({
785
+ region: process.env.KEEL_REGION,
786
+ credentials: fromEnv()
787
+ });
788
+ }
789
+ return new S3Client({
790
+ region: process.env.KEEL_REGION,
791
+ // If a test endpoint is provided then use some test credentials
792
+ credentials: {
793
+ accessKeyId: "test",
794
+ secretAccessKey: "test"
795
+ },
796
+ // If a custom endpoint is set we need to use a custom resolver. Just setting the base endpoint isn't enough for S3 as it
797
+ // as the default resolver uses the bucket name as a sub-domain, which likely won't work with the custom endpoint.
798
+ // By implementing a full resolver we can force it to be the endpoint we want.
799
+ endpointProvider: /* @__PURE__ */ __name(() => {
800
+ return {
801
+ url: new URL(endpoint)
802
+ };
803
+ }, "endpointProvider")
804
+ });
805
+ })();
806
+ var _InlineFile = class _InlineFile {
807
+ constructor({ filename, contentType }) {
808
+ this._filename = filename;
809
+ this._contentType = contentType;
810
+ this._contents = null;
811
+ }
812
+ static fromDataURL(dataURL) {
813
+ var info = dataURL.split(",")[0].split(":")[1];
814
+ var data = dataURL.split(",")[1];
815
+ var mime = info.split(";")[0];
816
+ var name = info.split(";")[1].split("=")[1];
817
+ var buffer = Buffer.from(data, "base64");
818
+ var file = new _InlineFile({ filename: name, contentType: mime });
819
+ file.write(buffer);
820
+ return file;
821
+ }
822
+ get size() {
823
+ if (this._contents) {
824
+ return this._contents.size;
825
+ } else {
826
+ return 0;
827
+ }
828
+ }
829
+ get contentType() {
830
+ return this._contentType;
831
+ }
832
+ get filename() {
833
+ return this._filename;
834
+ }
835
+ write(buffer) {
836
+ this._contents = new Blob([buffer]);
837
+ }
838
+ // Read the contents of the file. If URL is set, it will be read from the remote storage, otherwise, if dataURL is set
839
+ // on the instance, it will return a blob with the file contents
840
+ async read() {
841
+ const arrayBuffer = await this._contents.arrayBuffer();
842
+ const buffer = Buffer.from(arrayBuffer);
843
+ return buffer;
844
+ }
845
+ async store(expires = null) {
846
+ const content = await this.read();
847
+ const key = KSUID.randomSync().string;
848
+ await storeFile(
849
+ content,
850
+ key,
851
+ this._filename,
852
+ this._contentType,
853
+ this.size,
854
+ expires
855
+ );
856
+ return new File({
857
+ key,
858
+ size: this.size,
859
+ filename: this.filename,
860
+ contentType: this.contentType
861
+ });
862
+ }
863
+ };
864
+ __name(_InlineFile, "InlineFile");
865
+ var InlineFile = _InlineFile;
866
+ var _File = class _File extends InlineFile {
867
+ constructor(input) {
868
+ super({ filename: input.filename, contentType: input.contentType });
869
+ this._key = input.key;
870
+ this._size = input.size;
871
+ }
872
+ static fromDbRecord({ key, filename, size, contentType }) {
873
+ return new _File({
874
+ key,
875
+ filename,
876
+ size,
877
+ contentType
878
+ });
879
+ }
880
+ get size() {
881
+ return this._size;
882
+ }
883
+ get key() {
884
+ return this._key;
885
+ }
886
+ async read() {
887
+ if (this._contents) {
888
+ const arrayBuffer = await this._contents.arrayBuffer();
889
+ return Buffer.from(arrayBuffer);
890
+ }
891
+ if (s3Client) {
892
+ const params = {
893
+ Bucket: process.env.KEEL_FILES_BUCKET_NAME,
894
+ Key: "files/" + this.key
895
+ };
896
+ const command = new GetObjectCommand(params);
897
+ const response = await s3Client.send(command);
898
+ const blob = await response.Body.transformToByteArray();
899
+ return Buffer.from(blob);
900
+ }
901
+ const db = useDatabase2();
902
+ try {
903
+ let query = db.selectFrom("keel_storage").select("data").where("id", "=", this.key);
904
+ const row = await query.executeTakeFirstOrThrow();
905
+ return row.data;
906
+ } catch (e) {
907
+ throw new DatabaseError(e);
908
+ }
909
+ }
910
+ async store(expires = null) {
911
+ if (this._contents) {
912
+ const contents = await this.read();
913
+ await storeFile(
914
+ contents,
915
+ this.key,
916
+ this.filename,
917
+ this.contentType,
918
+ expires
919
+ );
920
+ }
921
+ return this;
922
+ }
923
+ async getPresignedUrl() {
924
+ if (s3Client) {
925
+ const command = new GetObjectCommand({
926
+ Bucket: process.env.KEEL_FILES_BUCKET_NAME,
927
+ Key: "files/" + this.key,
928
+ ResponseContentDisposition: "inline"
929
+ });
930
+ const url = await getSignedUrl(s3Client, command, { expiresIn: 60 * 60 });
931
+ return new URL(url);
932
+ } else {
933
+ const contents = await this.read();
934
+ const dataurl = `data:${this.contentType};name=${this.filename};base64,${contents.toString("base64")}`;
935
+ return new URL(dataurl);
936
+ }
937
+ }
938
+ toDbRecord() {
939
+ return {
940
+ key: this.key,
941
+ filename: this.filename,
942
+ contentType: this.contentType,
943
+ size: this.size
944
+ };
945
+ }
946
+ toJSON() {
947
+ return {
948
+ key: this.key,
949
+ filename: this.filename,
950
+ contentType: this.contentType,
951
+ size: this.size
952
+ };
953
+ }
954
+ };
955
+ __name(_File, "File");
956
+ var File = _File;
957
+ async function storeFile(contents, key, filename, contentType, expires) {
958
+ if (s3Client) {
959
+ const params = {
960
+ Bucket: process.env.KEEL_FILES_BUCKET_NAME,
961
+ Key: "files/" + key,
962
+ Body: contents,
963
+ ContentType: contentType,
964
+ ContentDisposition: `attachment; filename="${encodeURIComponent(
965
+ filename
966
+ )}"`,
967
+ Metadata: {
968
+ filename
969
+ },
970
+ ACL: "private"
971
+ };
972
+ if (expires) {
973
+ if (expires instanceof Date) {
974
+ params.Expires = expires;
975
+ } else {
976
+ console.warn("Invalid expires value. Skipping Expires parameter.");
977
+ }
978
+ }
979
+ const command = new PutObjectCommand(params);
980
+ try {
981
+ await s3Client.send(command);
982
+ } catch (error) {
983
+ console.error("Error uploading file:", error);
984
+ throw error;
985
+ }
986
+ } else {
987
+ const db = useDatabase2();
988
+ try {
989
+ let query = db.insertInto("keel_storage").values({
990
+ id: key,
991
+ filename,
992
+ content_type: contentType,
993
+ data: contents
994
+ }).onConflict(
995
+ (oc) => oc.column("id").doUpdateSet(() => ({
996
+ filename,
997
+ content_type: contentType,
998
+ data: contents
999
+ })).where("keel_storage.id", "=", key)
1000
+ ).returningAll();
1001
+ await query.execute();
1002
+ } catch (e) {
1003
+ throw new DatabaseError(e);
1004
+ }
1005
+ }
1006
+ }
1007
+ __name(storeFile, "storeFile");
1008
+ module2.exports = {
1009
+ InlineFile,
1010
+ File
1011
+ };
1012
+ }
1013
+ });
1014
+
1015
+ // src/parsing.js
1016
+ var require_parsing = __commonJS({
1017
+ "src/parsing.js"(exports2, module2) {
1018
+ "use strict";
1019
+ var { Duration } = require_Duration();
1020
+ var { InlineFile, File } = require_File();
1021
+ var { isPlainObject } = require_type_utils();
1022
+ function parseInputs(inputs) {
1023
+ if (inputs != null && typeof inputs === "object") {
1024
+ for (const k of Object.keys(inputs)) {
1025
+ if (inputs[k] !== null && typeof inputs[k] === "object") {
1026
+ if (Array.isArray(inputs[k])) {
1027
+ inputs[k] = inputs[k].map((item) => {
1028
+ if (item && typeof item === "object") {
1029
+ if ("__typename" in item) {
1030
+ return parseComplexInputType(item);
1031
+ }
1032
+ return parseInputs(item);
1033
+ }
1034
+ return item;
1035
+ });
1036
+ } else if ("__typename" in inputs[k]) {
1037
+ inputs[k] = parseComplexInputType(inputs[k]);
1038
+ } else {
1039
+ inputs[k] = parseInputs(inputs[k]);
1040
+ }
1041
+ }
1042
+ }
1043
+ }
1044
+ return inputs;
1045
+ }
1046
+ __name(parseInputs, "parseInputs");
1047
+ function parseComplexInputType(value) {
1048
+ switch (value.__typename) {
1049
+ case "InlineFile":
1050
+ return InlineFile.fromDataURL(value.dataURL);
1051
+ case "Duration":
1052
+ return Duration.fromISOString(value.interval);
1053
+ default:
1054
+ throw new Error("complex type not handled: " + value.__typename);
1055
+ }
1056
+ }
1057
+ __name(parseComplexInputType, "parseComplexInputType");
1058
+ async function parseOutputs(outputs) {
1059
+ if (outputs != null && typeof outputs === "object") {
1060
+ for (const k of Object.keys(outputs)) {
1061
+ if (outputs[k] !== null && typeof outputs[k] === "object") {
1062
+ if (Array.isArray(outputs[k])) {
1063
+ outputs[k] = await Promise.all(
1064
+ outputs[k].map((item) => parseOutputs(item))
1065
+ );
1066
+ } else if (outputs[k] instanceof InlineFile) {
1067
+ const stored = await outputs[k].store();
1068
+ outputs[k] = stored;
1069
+ } else if (outputs[k] instanceof Duration) {
1070
+ outputs[k] = outputs[k].toISOString();
1071
+ } else {
1072
+ outputs[k] = await parseOutputs(outputs[k]);
1073
+ }
1074
+ }
1075
+ }
1076
+ }
1077
+ return outputs;
1078
+ }
1079
+ __name(parseOutputs, "parseOutputs");
1080
+ function transformRichDataTypes(data) {
1081
+ const keys = data ? Object.keys(data) : [];
1082
+ const row = {};
1083
+ for (const key of keys) {
1084
+ const value = data[key];
1085
+ if (Array.isArray(value)) {
1086
+ row[key] = value.map((item) => transformRichDataTypes({ item }).item);
1087
+ } else if (isPlainObject(value)) {
1088
+ if (value._typename == "Duration" && value.pgInterval) {
1089
+ row[key] = new Duration(value.pgInterval);
1090
+ } else if (value.key && value.size && value.filename && value.contentType) {
1091
+ row[key] = File.fromDbRecord(value);
1092
+ } else {
1093
+ row[key] = value;
1094
+ }
1095
+ } else {
1096
+ row[key] = value;
1097
+ }
1098
+ }
1099
+ return row;
1100
+ }
1101
+ __name(transformRichDataTypes, "transformRichDataTypes");
1102
+ function isReferencingExistingRecord(value) {
1103
+ return Object.keys(value).length === 1 && value.id;
1104
+ }
1105
+ __name(isReferencingExistingRecord, "isReferencingExistingRecord");
1106
+ module2.exports = {
1107
+ parseInputs,
1108
+ parseOutputs,
1109
+ transformRichDataTypes,
1110
+ isReferencingExistingRecord
1111
+ };
1112
+ }
1113
+ });
1114
+
1115
+ // src/casing.js
1116
+ var require_casing = __commonJS({
1117
+ "src/casing.js"(exports2, module2) {
1118
+ "use strict";
1119
+ var { snakeCase, camelCase } = require("change-case");
1120
+ function camelCaseObject(obj = {}) {
1121
+ const r = {};
1122
+ for (const key of Object.keys(obj)) {
1123
+ r[camelCase(key, {
1124
+ transform: camelCaseTransform,
1125
+ splitRegexp: [
1126
+ /([a-z0-9])([A-Z])/g,
1127
+ /([A-Z])([A-Z][a-z])/g,
1128
+ /([a-zA-Z])([0-9])/g
1129
+ ]
1130
+ })] = obj[key];
1131
+ }
1132
+ return r;
1133
+ }
1134
+ __name(camelCaseObject, "camelCaseObject");
1135
+ function snakeCaseObject(obj) {
1136
+ const r = {};
1137
+ for (const key of Object.keys(obj)) {
1138
+ r[snakeCase(key, {
1139
+ splitRegexp: [
1140
+ /([a-z0-9])([A-Z])/g,
1141
+ /([A-Z])([A-Z][a-z])/g,
1142
+ /([a-zA-Z])([0-9])/g
1143
+ ]
1144
+ })] = obj[key];
1145
+ }
1146
+ return r;
1147
+ }
1148
+ __name(snakeCaseObject, "snakeCaseObject");
1149
+ function upperCamelCase(s) {
1150
+ s = camelCase(s);
1151
+ return s[0].toUpperCase() + s.substring(1);
1152
+ }
1153
+ __name(upperCamelCase, "upperCamelCase");
1154
+ function camelCaseTransform(input, index) {
1155
+ if (index === 0) return input.toLowerCase();
1156
+ const firstChar = input.charAt(0);
1157
+ const lowerChars = input.substr(1).toLowerCase();
1158
+ return `${firstChar.toUpperCase()}${lowerChars}`;
1159
+ }
1160
+ __name(camelCaseTransform, "camelCaseTransform");
1161
+ module2.exports = {
1162
+ camelCaseObject,
1163
+ snakeCaseObject,
1164
+ snakeCase,
1165
+ camelCase,
1166
+ upperCamelCase
1167
+ };
1168
+ }
1169
+ });
1170
+
1171
+ // src/TimePeriod.js
1172
+ var require_TimePeriod = __commonJS({
1173
+ "src/TimePeriod.js"(exports2, module2) {
1174
+ "use strict";
1175
+ var _TimePeriod = class _TimePeriod {
1176
+ constructor(period = "", value = 0, offset = 0, complete = false) {
1177
+ this.period = period;
1178
+ this.value = value;
1179
+ this.offset = offset;
1180
+ this.complete = complete;
1181
+ }
1182
+ static fromExpression(expression) {
1183
+ const pattern = /^(this|next|last)?\s*(\d+)?\s*(complete)?\s*(second|minute|hour|day|week|month|year|seconds|minutes|hours|days|weeks|months|years)?$/i;
1184
+ const shorthandPattern = /^(now|today|tomorrow|yesterday)$/i;
1185
+ const shorthandMatch = shorthandPattern.exec(expression.trim());
1186
+ if (shorthandMatch) {
1187
+ const shorthand = shorthandMatch[1].toLowerCase();
1188
+ switch (shorthand) {
1189
+ case "now":
1190
+ return new _TimePeriod();
1191
+ case "today":
1192
+ return _TimePeriod.fromExpression("this day");
1193
+ case "tomorrow":
1194
+ return _TimePeriod.fromExpression("next complete day");
1195
+ case "yesterday":
1196
+ return _TimePeriod.fromExpression("last complete day");
1197
+ }
1198
+ }
1199
+ const match = pattern.exec(expression.trim());
1200
+ if (!match) {
1201
+ throw new Error("Invalid time period expression");
1202
+ }
1203
+ const [, direction, rawValue, isComplete, rawPeriod] = match;
1204
+ let period = rawPeriod ? rawPeriod.toLowerCase().replace(/s$/, "") : "";
1205
+ let value = rawValue ? parseInt(rawValue, 10) : 1;
1206
+ let complete = Boolean(isComplete);
1207
+ let offset = 0;
1208
+ switch (direction == null ? void 0 : direction.toLowerCase()) {
1209
+ case "this":
1210
+ offset = 0;
1211
+ complete = true;
1212
+ break;
1213
+ case "next":
1214
+ offset = complete ? 1 : 0;
1215
+ break;
1216
+ case "last":
1217
+ offset = -value;
1218
+ break;
1219
+ default:
1220
+ throw new Error(
1221
+ "Time period expression must start with this, next, or last"
1222
+ );
1223
+ }
1224
+ return new _TimePeriod(period, value, offset, complete);
1225
+ }
1226
+ periodStartSQL() {
1227
+ let sql = "NOW()";
1228
+ if (this.offset !== 0) {
1229
+ sql = `${sql} + INTERVAL '${this.offset} ${this.period}'`;
1230
+ }
1231
+ if (this.complete) {
1232
+ sql = `DATE_TRUNC('${this.period}', ${sql})`;
1233
+ } else {
1234
+ sql = `(${sql})`;
1235
+ }
1236
+ return sql;
1237
+ }
1238
+ periodEndSQL() {
1239
+ let sql = this.periodStartSQL();
1240
+ if (this.value != 0) {
1241
+ sql = `(${sql} + INTERVAL '${this.value} ${this.period}')`;
1242
+ }
1243
+ return sql;
1244
+ }
1245
+ };
1246
+ __name(_TimePeriod, "TimePeriod");
1247
+ var TimePeriod = _TimePeriod;
1248
+ module2.exports = {
1249
+ TimePeriod
1250
+ };
1251
+ }
1252
+ });
1253
+
1254
+ // src/applyWhereConditions.js
1255
+ var require_applyWhereConditions = __commonJS({
1256
+ "src/applyWhereConditions.js"(exports2, module2) {
1257
+ "use strict";
1258
+ var { sql, Kysely } = require("kysely");
1259
+ var { snakeCase } = require_casing();
1260
+ var { TimePeriod } = require_TimePeriod();
1261
+ var opMapping = {
1262
+ startsWith: { op: "like", value: /* @__PURE__ */ __name((v) => `${v}%`, "value") },
1263
+ endsWith: { op: "like", value: /* @__PURE__ */ __name((v) => `%${v}`, "value") },
1264
+ contains: { op: "like", value: /* @__PURE__ */ __name((v) => `%${v}%`, "value") },
1265
+ oneOf: { op: "=", value: /* @__PURE__ */ __name((v) => sql`ANY(${v})`, "value") },
1266
+ greaterThan: { op: ">" },
1267
+ greaterThanOrEquals: { op: ">=" },
1268
+ lessThan: { op: "<" },
1269
+ lessThanOrEquals: { op: "<=" },
1270
+ before: { op: "<" },
1271
+ onOrBefore: { op: "<=" },
1272
+ after: { op: ">" },
1273
+ onOrAfter: { op: ">=" },
1274
+ equals: { op: sql`is not distinct from` },
1275
+ notEquals: { op: sql`is distinct from` },
1276
+ equalsRelative: {
1277
+ op: sql`BETWEEN`,
1278
+ value: /* @__PURE__ */ __name((v) => sql`${sql.raw(
1279
+ TimePeriod.fromExpression(v).periodStartSQL()
1280
+ )} AND ${sql.raw(TimePeriod.fromExpression(v).periodEndSQL())}`, "value")
1281
+ },
1282
+ beforeRelative: {
1283
+ op: "<",
1284
+ value: /* @__PURE__ */ __name((v) => sql`${sql.raw(TimePeriod.fromExpression(v).periodStartSQL())}`, "value")
1285
+ },
1286
+ afterRelative: {
1287
+ op: ">=",
1288
+ value: /* @__PURE__ */ __name((v) => sql`${sql.raw(TimePeriod.fromExpression(v).periodEndSQL())}`, "value")
1289
+ },
1290
+ any: {
1291
+ isArrayQuery: true,
1292
+ greaterThan: { op: ">" },
1293
+ greaterThanOrEquals: { op: ">=" },
1294
+ lessThan: { op: "<" },
1295
+ lessThanOrEquals: { op: "<=" },
1296
+ before: { op: "<" },
1297
+ onOrBefore: { op: "<=" },
1298
+ after: { op: ">" },
1299
+ onOrAfter: { op: ">=" },
1300
+ equals: { op: "=" },
1301
+ notEquals: { op: "=", value: /* @__PURE__ */ __name((v) => sql`NOT ${v}`, "value") }
1302
+ },
1303
+ all: {
1304
+ isArrayQuery: true,
1305
+ greaterThan: { op: ">" },
1306
+ greaterThanOrEquals: { op: ">=" },
1307
+ lessThan: { op: "<" },
1308
+ lessThanOrEquals: { op: "<=" },
1309
+ before: { op: "<" },
1310
+ onOrBefore: { op: "<=" },
1311
+ after: { op: ">" },
1312
+ onOrAfter: { op: ">=" },
1313
+ equals: { op: "=" },
1314
+ notEquals: { op: "=", value: /* @__PURE__ */ __name((v) => sql`NOT ${v}`, "value") }
1315
+ }
1316
+ };
1317
+ function applyWhereConditions(context, qb, where = {}) {
1318
+ const conf = context.tableConfig();
1319
+ for (const key of Object.keys(where)) {
1320
+ const v = where[key];
1321
+ if (conf && conf[snakeCase(key)]) {
1322
+ const rel = conf[snakeCase(key)];
1323
+ context.withJoin(rel.referencesTable, () => {
1324
+ qb = applyWhereConditions(context, qb, v);
1325
+ });
1326
+ continue;
1327
+ }
1328
+ const fieldName = `${context.tableAlias()}.${snakeCase(key)}`;
1329
+ if (Object.prototype.toString.call(v) !== "[object Object]") {
1330
+ qb = qb.where(fieldName, sql`is not distinct from`, sql`${v}`);
1331
+ continue;
1332
+ }
1333
+ for (const op of Object.keys(v)) {
1334
+ const mapping = opMapping[op];
1335
+ if (!mapping) {
1336
+ throw new Error(`invalid where condition: ${op}`);
1337
+ }
1338
+ if (mapping.isArrayQuery) {
1339
+ for (const arrayOp of Object.keys(v[op])) {
1340
+ qb = qb.where(
1341
+ mapping[arrayOp].value ? mapping[arrayOp].value(v[op][arrayOp]) : sql`${v[op][arrayOp]}`,
1342
+ mapping[arrayOp].op,
1343
+ sql`${sql(op)}(${sql.ref(fieldName)})`
1344
+ );
1345
+ }
1346
+ } else {
1347
+ qb = qb.where(
1348
+ fieldName,
1349
+ mapping.op,
1350
+ mapping.value ? mapping.value(v[op]) : sql`${v[op]}`
1351
+ );
1352
+ }
1353
+ }
1354
+ }
1355
+ return qb;
1356
+ }
1357
+ __name(applyWhereConditions, "applyWhereConditions");
1358
+ module2.exports = {
1359
+ applyWhereConditions
1360
+ };
1361
+ }
1362
+ });
1363
+
1364
+ // src/applyAdditionalQueryConstraints.js
1365
+ var require_applyAdditionalQueryConstraints = __commonJS({
1366
+ "src/applyAdditionalQueryConstraints.js"(exports2, module2) {
1367
+ "use strict";
1368
+ var { snakeCase } = require("change-case");
1369
+ function applyLimit(context, qb, limit) {
1370
+ return qb.limit(limit);
1371
+ }
1372
+ __name(applyLimit, "applyLimit");
1373
+ function applyOffset(context, qb, offset) {
1374
+ return qb.offset(offset);
1375
+ }
1376
+ __name(applyOffset, "applyOffset");
1377
+ function applyOrderBy(context, qb, tableName, orderBy = {}) {
1378
+ Object.entries(orderBy).forEach(([key, sortOrder]) => {
1379
+ qb = qb.orderBy(`${tableName}.${snakeCase(key)}`, sortOrder.toLowerCase());
1380
+ });
1381
+ return qb;
1382
+ }
1383
+ __name(applyOrderBy, "applyOrderBy");
1384
+ module2.exports = {
1385
+ applyLimit,
1386
+ applyOffset,
1387
+ applyOrderBy
1388
+ };
1389
+ }
1390
+ });
1391
+
1392
+ // src/applyJoins.js
1393
+ var require_applyJoins = __commonJS({
1394
+ "src/applyJoins.js"(exports2, module2) {
1395
+ "use strict";
1396
+ var { snakeCase } = require_casing();
1397
+ function applyJoins(context, qb, where) {
1398
+ const conf = context.tableConfig();
1399
+ if (!conf) {
1400
+ return qb;
1401
+ }
1402
+ const srcTable = context.tableAlias();
1403
+ for (const key of Object.keys(where)) {
1404
+ const rel = conf[snakeCase(key)];
1405
+ if (!rel) {
1406
+ continue;
1407
+ }
1408
+ const targetTable = rel.referencesTable;
1409
+ if (context.hasJoin(targetTable)) {
1410
+ continue;
1411
+ }
1412
+ context.withJoin(targetTable, () => {
1413
+ switch (rel.relationshipType) {
1414
+ case "hasMany":
1415
+ qb = qb.innerJoin(
1416
+ `${targetTable} as ${context.tableAlias()}`,
1417
+ `${srcTable}.id`,
1418
+ `${context.tableAlias()}.${rel.foreignKey}`
1419
+ );
1420
+ break;
1421
+ case "belongsTo":
1422
+ qb = qb.innerJoin(
1423
+ `${targetTable} as ${context.tableAlias()}`,
1424
+ `${srcTable}.${rel.foreignKey}`,
1425
+ `${context.tableAlias()}.id`
1426
+ );
1427
+ break;
1428
+ default:
1429
+ throw new Error(`unknown relationshipType: ${rel.relationshipType}`);
1430
+ }
1431
+ qb = applyJoins(context, qb, where[key]);
1432
+ });
1433
+ }
1434
+ return qb;
1435
+ }
1436
+ __name(applyJoins, "applyJoins");
1437
+ module2.exports = {
1438
+ applyJoins
1439
+ };
1440
+ }
1441
+ });
1442
+
1443
+ // src/QueryContext.js
1444
+ var require_QueryContext = __commonJS({
1445
+ "src/QueryContext.js"(exports2, module2) {
1446
+ "use strict";
1447
+ var _QueryContext = class _QueryContext {
1448
+ /**
1449
+ * @param {string[]} tablePath This is the path from the "root" table to the "current table".
1450
+ * @param {import("./ModelAPI").TableConfigMap} tableConfigMap
1451
+ * @param {string[]} joins
1452
+ */
1453
+ constructor(tablePath, tableConfigMap, joins = []) {
1454
+ this._tablePath = tablePath;
1455
+ this._tableConfigMap = tableConfigMap;
1456
+ this._joins = joins;
1457
+ }
1458
+ clone() {
1459
+ return new _QueryContext([...this._tablePath], this._tableConfigMap, [
1460
+ ...this._joins
1461
+ ]);
1462
+ }
1463
+ /**
1464
+ * Returns true if, given the current table path, a join to the given
1465
+ * table has already been added.
1466
+ * @param {string} table
1467
+ * @returns {boolean}
1468
+ */
1469
+ hasJoin(table2) {
1470
+ const alias = joinAlias([...this._tablePath, table2]);
1471
+ return this._joins.includes(alias);
1472
+ }
1473
+ /**
1474
+ * Adds table to the QueryContext's path and registers the join,
1475
+ * calls fn, then pops the table off the path.
1476
+ * @param {string} table
1477
+ * @param {Function} fn
1478
+ */
1479
+ withJoin(table2, fn) {
1480
+ this._tablePath.push(table2);
1481
+ this._joins.push(this.tableAlias());
1482
+ fn();
1483
+ this._tablePath.pop();
1484
+ }
1485
+ /**
1486
+ * Returns the alias that will be used for the current table
1487
+ * @returns {string}
1488
+ */
1489
+ tableAlias() {
1490
+ return joinAlias(this._tablePath);
1491
+ }
1492
+ /**
1493
+ * Returns the current table name
1494
+ * @returns {string}
1495
+ */
1496
+ tableName() {
1497
+ return this._tablePath[this._tablePath.length - 1];
1498
+ }
1499
+ /**
1500
+ * Return the TableConfig for the current table
1501
+ * @returns {import("./ModelAPI").TableConfig | undefined}
1502
+ */
1503
+ tableConfig() {
1504
+ return this._tableConfigMap[this.tableName()];
1505
+ }
1506
+ };
1507
+ __name(_QueryContext, "QueryContext");
1508
+ var QueryContext = _QueryContext;
1509
+ function joinAlias(tablePath) {
1510
+ return tablePath.join("$");
1511
+ }
1512
+ __name(joinAlias, "joinAlias");
1513
+ module2.exports = {
1514
+ QueryContext
1515
+ };
1516
+ }
1517
+ });
1518
+
1519
+ // src/QueryBuilder.js
1520
+ var require_QueryBuilder = __commonJS({
1521
+ "src/QueryBuilder.js"(exports2, module2) {
1522
+ "use strict";
1523
+ var { applyWhereConditions } = require_applyWhereConditions();
1524
+ var {
1525
+ applyLimit,
1526
+ applyOffset,
1527
+ applyOrderBy
1528
+ } = require_applyAdditionalQueryConstraints();
1529
+ var { applyJoins } = require_applyJoins();
1530
+ var {
1531
+ camelCaseObject,
1532
+ snakeCaseObject,
1533
+ upperCamelCase
1534
+ } = require_casing();
1535
+ var { useDatabase: useDatabase2 } = require_database();
1536
+ var { transformRichDataTypes } = require_parsing();
1537
+ var { QueryContext } = require_QueryContext();
1538
+ var tracing = require_tracing();
1539
+ var { DatabaseError } = require_errors();
1540
+ var _QueryBuilder = class _QueryBuilder {
1541
+ /**
1542
+ * @param {string} tableName
1543
+ * @param {import("./QueryContext").QueryContext} context
1544
+ * @param {import("kysely").Kysely} db
1545
+ */
1546
+ constructor(tableName, context, db) {
1547
+ this._tableName = tableName;
1548
+ this._context = context;
1549
+ this._db = db;
1550
+ this._modelName = upperCamelCase(this._tableName);
1551
+ }
1552
+ where(where) {
1553
+ const context = this._context.clone();
1554
+ let builder = applyJoins(context, this._db, where);
1555
+ builder = applyWhereConditions(context, builder, where);
1556
+ return new _QueryBuilder(this._tableName, context, builder);
1557
+ }
1558
+ sql() {
1559
+ return this._db.compile().sql;
1560
+ }
1561
+ async update(values) {
1562
+ const name = tracing.spanNameForModelAPI(this._modelName, "update");
1563
+ const db = useDatabase2();
1564
+ return tracing.withSpan(name, async (span) => {
1565
+ const sub = this._db.clearSelect().select("id");
1566
+ const query = db.updateTable(this._tableName).set(snakeCaseObject(values)).returningAll().where("id", "in", sub);
1567
+ try {
1568
+ const result = await query.execute();
1569
+ const numUpdatedRows = result.length;
1570
+ if (numUpdatedRows == 0) {
1571
+ return null;
1572
+ }
1573
+ if (numUpdatedRows > 1) {
1574
+ throw new DatabaseError(
1575
+ new Error(
1576
+ "more than one row matched update constraints - only unique fields should be used when updating."
1577
+ )
1578
+ );
1579
+ }
1580
+ return transformRichDataTypes(camelCaseObject(result[0]));
1581
+ } catch (e) {
1582
+ throw new DatabaseError(e);
1583
+ }
1584
+ });
1585
+ }
1586
+ async delete() {
1587
+ const name = tracing.spanNameForModelAPI(this._modelName, "delete");
1588
+ const db = useDatabase2();
1589
+ return tracing.withSpan(name, async (span) => {
1590
+ const sub = this._db.clearSelect().select("id");
1591
+ let builder = db.deleteFrom(this._tableName).where("id", "in", sub);
1592
+ const query = builder.returning(["id"]);
1593
+ span.setAttribute("sql", query.compile().sql);
1594
+ try {
1595
+ const row = await query.executeTakeFirstOrThrow();
1596
+ return row.id;
1597
+ } catch (e) {
1598
+ throw new DatabaseError(e);
1599
+ }
1600
+ });
1601
+ }
1602
+ async findOne() {
1603
+ const name = tracing.spanNameForModelAPI(this._modelName, "findOne");
1604
+ const db = useDatabase2();
1605
+ return tracing.withSpan(name, async (span) => {
1606
+ let builder = db.selectFrom((qb) => {
1607
+ return this._db.as(this._tableName);
1608
+ }).selectAll();
1609
+ span.setAttribute("sql", builder.compile().sql);
1610
+ const row = await builder.executeTakeFirst();
1611
+ if (!row) {
1612
+ return null;
1613
+ }
1614
+ return transformRichDataTypes(camelCaseObject(row));
1615
+ });
1616
+ }
1617
+ async findMany(params) {
1618
+ const name = tracing.spanNameForModelAPI(this._modelName, "findMany");
1619
+ const db = useDatabase2();
1620
+ return tracing.withSpan(name, async (span) => {
1621
+ const context = new QueryContext([this._tableName], this._tableConfigMap);
1622
+ let builder = db.selectFrom((qb) => {
1623
+ return this._db.as(this._tableName);
1624
+ }).selectAll();
1625
+ if (params == null ? void 0 : params.limit) {
1626
+ builder = applyLimit(context, builder, params.limit);
1627
+ }
1628
+ if (params == null ? void 0 : params.offset) {
1629
+ builder = applyOffset(context, builder, params.offset);
1630
+ }
1631
+ if ((params == null ? void 0 : params.orderBy) !== void 0 && Object.keys(params == null ? void 0 : params.orderBy).length > 0) {
1632
+ builder = applyOrderBy(
1633
+ context,
1634
+ builder,
1635
+ this._tableName,
1636
+ params.orderBy
1637
+ );
1638
+ } else {
1639
+ builder = builder.orderBy(`${this._tableName}.id`);
1640
+ }
1641
+ const query = builder;
1642
+ span.setAttribute("sql", query.compile().sql);
1643
+ const rows = await builder.execute();
1644
+ return rows.map((x) => transformRichDataTypes(camelCaseObject(x)));
1645
+ });
1646
+ }
1647
+ };
1648
+ __name(_QueryBuilder, "QueryBuilder");
1649
+ var QueryBuilder = _QueryBuilder;
1650
+ module2.exports.QueryBuilder = QueryBuilder;
1651
+ }
1652
+ });
1653
+
1654
+ // src/ModelAPI.js
1655
+ var require_ModelAPI = __commonJS({
1656
+ "src/ModelAPI.js"(exports2, module2) {
1657
+ "use strict";
1658
+ var { sql } = require("kysely");
1659
+ var { useDatabase: useDatabase2 } = require_database();
1660
+ var {
1661
+ transformRichDataTypes,
1662
+ isReferencingExistingRecord
1663
+ } = require_parsing();
1664
+ var { isPlainObject } = require_type_utils();
1665
+ var { QueryBuilder } = require_QueryBuilder();
1666
+ var { QueryContext } = require_QueryContext();
1667
+ var { applyWhereConditions } = require_applyWhereConditions();
1668
+ var { applyJoins } = require_applyJoins();
1669
+ var { InlineFile, File } = require_File();
1670
+ var { Duration } = require_Duration();
1671
+ var {
1672
+ applyLimit,
1673
+ applyOffset,
1674
+ applyOrderBy
1675
+ } = require_applyAdditionalQueryConstraints();
1676
+ var {
1677
+ camelCaseObject,
1678
+ snakeCaseObject,
1679
+ upperCamelCase
1680
+ } = require_casing();
1681
+ var tracing = require_tracing();
1682
+ var { DatabaseError } = require_errors();
1683
+ var _ModelAPI = class _ModelAPI {
1684
+ /**
1685
+ * @param {string} tableName The name of the table this API is for
1686
+ * @param {Function} _ Used to be a function that returns the default values for a row in this table. No longer used.
1687
+ * @param {TableConfigMap} tableConfigMap
1688
+ */
1689
+ constructor(tableName, _, tableConfigMap = {}) {
1690
+ this._tableName = tableName;
1691
+ this._tableConfigMap = tableConfigMap;
1692
+ this._modelName = upperCamelCase(this._tableName);
1693
+ }
1694
+ async create(values) {
1695
+ const name = tracing.spanNameForModelAPI(this._modelName, "create");
1696
+ return tracing.withSpan(name, () => {
1697
+ const db = useDatabase2();
1698
+ return create(
1699
+ db,
1700
+ this._tableName,
1701
+ this._tableConfigMap,
1702
+ snakeCaseObject(values)
1703
+ );
1704
+ });
1705
+ }
1706
+ async findOne(where = {}) {
1707
+ const name = tracing.spanNameForModelAPI(this._modelName, "findOne");
1708
+ const db = useDatabase2();
1709
+ return tracing.withSpan(name, async (span) => {
1710
+ let builder = db.selectFrom(this._tableName).distinctOn(`${this._tableName}.id`).selectAll(this._tableName);
1711
+ const context = new QueryContext([this._tableName], this._tableConfigMap);
1712
+ builder = applyJoins(context, builder, where);
1713
+ builder = applyWhereConditions(context, builder, where);
1714
+ span.setAttribute("sql", builder.compile().sql);
1715
+ const row = await builder.executeTakeFirst();
1716
+ if (!row) {
1717
+ return null;
1718
+ }
1719
+ return transformRichDataTypes(camelCaseObject(row));
1720
+ });
1721
+ }
1722
+ async findMany(params) {
1723
+ const name = tracing.spanNameForModelAPI(this._modelName, "findMany");
1724
+ const db = useDatabase2();
1725
+ const where = (params == null ? void 0 : params.where) || {};
1726
+ return tracing.withSpan(name, async (span) => {
1727
+ const context = new QueryContext([this._tableName], this._tableConfigMap);
1728
+ let builder = db.selectFrom((qb) => {
1729
+ let builder2 = qb.selectFrom(this._tableName).distinctOn(`${this._tableName}.id`).selectAll(this._tableName);
1730
+ builder2 = applyJoins(context, builder2, where);
1731
+ builder2 = applyWhereConditions(context, builder2, where);
1732
+ builder2 = builder2.as(this._tableName);
1733
+ return builder2;
1734
+ }).selectAll();
1735
+ if (params == null ? void 0 : params.limit) {
1736
+ builder = applyLimit(context, builder, params.limit);
1737
+ }
1738
+ if (params == null ? void 0 : params.offset) {
1739
+ builder = applyOffset(context, builder, params.offset);
1740
+ }
1741
+ if ((params == null ? void 0 : params.orderBy) !== void 0 && Object.keys(params == null ? void 0 : params.orderBy).length > 0) {
1742
+ builder = applyOrderBy(
1743
+ context,
1744
+ builder,
1745
+ this._tableName,
1746
+ params.orderBy
1747
+ );
1748
+ } else {
1749
+ builder = builder.orderBy(`${this._tableName}.id`);
1750
+ }
1751
+ const query = builder;
1752
+ span.setAttribute("sql", query.compile().sql);
1753
+ const rows = await builder.execute();
1754
+ return rows.map((x) => transformRichDataTypes(camelCaseObject(x)));
1755
+ });
1756
+ }
1757
+ async update(where, values) {
1758
+ const name = tracing.spanNameForModelAPI(this._modelName, "update");
1759
+ const db = useDatabase2();
1760
+ return tracing.withSpan(name, async (span) => {
1761
+ let builder = db.updateTable(this._tableName).returningAll();
1762
+ const keys = values ? Object.keys(values) : [];
1763
+ const row = {};
1764
+ for (const key of keys) {
1765
+ const value = values[key];
1766
+ if (Array.isArray(value)) {
1767
+ row[key] = await Promise.all(
1768
+ value.map(async (item) => {
1769
+ if (item instanceof Duration) {
1770
+ return item.toPostgres();
1771
+ }
1772
+ if (item instanceof InlineFile) {
1773
+ const storedFile = await item.store();
1774
+ return storedFile.toDbRecord();
1775
+ }
1776
+ if (item instanceof File) {
1777
+ return item.toDbRecord();
1778
+ }
1779
+ return item;
1780
+ })
1781
+ );
1782
+ } else if (value instanceof Duration) {
1783
+ row[key] = value.toPostgres();
1784
+ } else if (value instanceof InlineFile) {
1785
+ const storedFile = await value.store();
1786
+ row[key] = storedFile.toDbRecord();
1787
+ } else if (value instanceof File) {
1788
+ row[key] = value.toDbRecord();
1789
+ } else {
1790
+ row[key] = value;
1791
+ }
1792
+ }
1793
+ builder = builder.set(snakeCaseObject(row));
1794
+ const context = new QueryContext([this._tableName], this._tableConfigMap);
1795
+ builder = applyWhereConditions(context, builder, where);
1796
+ span.setAttribute("sql", builder.compile().sql);
1797
+ try {
1798
+ const row2 = await builder.executeTakeFirstOrThrow();
1799
+ return transformRichDataTypes(camelCaseObject(row2));
1800
+ } catch (e) {
1801
+ throw new DatabaseError(e);
1802
+ }
1803
+ });
1804
+ }
1805
+ async delete(where) {
1806
+ const name = tracing.spanNameForModelAPI(this._modelName, "delete");
1807
+ const db = useDatabase2();
1808
+ return tracing.withSpan(name, async (span) => {
1809
+ let builder = db.deleteFrom(this._tableName).returning(["id"]);
1810
+ const context = new QueryContext([this._tableName], this._tableConfigMap);
1811
+ builder = applyWhereConditions(context, builder, where);
1812
+ span.setAttribute("sql", builder.compile().sql);
1813
+ try {
1814
+ const row = await builder.executeTakeFirstOrThrow();
1815
+ return row.id;
1816
+ } catch (e) {
1817
+ throw new DatabaseError(e);
1818
+ }
1819
+ });
1820
+ }
1821
+ where(where) {
1822
+ const db = useDatabase2();
1823
+ let builder = db.selectFrom(this._tableName).distinctOn(`${this._tableName}.id`).selectAll(this._tableName);
1824
+ const context = new QueryContext([this._tableName], this._tableConfigMap);
1825
+ builder = applyJoins(context, builder, where);
1826
+ builder = applyWhereConditions(context, builder, where);
1827
+ return new QueryBuilder(this._tableName, context, builder);
1828
+ }
1829
+ };
1830
+ __name(_ModelAPI, "ModelAPI");
1831
+ var ModelAPI = _ModelAPI;
1832
+ async function create(conn, tableName, tableConfigs, values) {
1833
+ try {
1834
+ let query = conn.insertInto(tableName);
1835
+ const keys = values ? Object.keys(values) : [];
1836
+ const tableConfig = tableConfigs[tableName] || {};
1837
+ const hasManyRecords = [];
1838
+ if (keys.length === 0) {
1839
+ query = query.expression(sql`default values`);
1840
+ } else {
1841
+ const row = {};
1842
+ for (const key of keys) {
1843
+ const value = values[key];
1844
+ const columnConfig = tableConfig[key];
1845
+ if (!columnConfig) {
1846
+ if (Array.isArray(value)) {
1847
+ row[key] = await Promise.all(
1848
+ value.map(async (item) => {
1849
+ if (item instanceof Duration) {
1850
+ return item.toPostgres();
1851
+ }
1852
+ if (item instanceof InlineFile) {
1853
+ const storedFile = await item.store();
1854
+ return storedFile.toDbRecord();
1855
+ }
1856
+ if (item instanceof File) {
1857
+ return item.toDbRecord();
1858
+ }
1859
+ return item;
1860
+ })
1861
+ );
1862
+ } else if (value instanceof Duration) {
1863
+ row[key] = value.toPostgres();
1864
+ } else if (value instanceof InlineFile) {
1865
+ const storedFile = await value.store();
1866
+ row[key] = storedFile.toDbRecord();
1867
+ } else if (value instanceof File) {
1868
+ row[key] = value.toDbRecord();
1869
+ } else {
1870
+ row[key] = value;
1871
+ }
1872
+ continue;
1873
+ }
1874
+ switch (columnConfig.relationshipType) {
1875
+ case "belongsTo":
1876
+ if (!isPlainObject(value)) {
1877
+ throw new Error(
1878
+ `non-object provided for field ${key} of ${tableName}`
1879
+ );
1880
+ }
1881
+ if (isReferencingExistingRecord(value)) {
1882
+ row[columnConfig.foreignKey] = value.id;
1883
+ break;
1884
+ }
1885
+ const created2 = await create(
1886
+ conn,
1887
+ columnConfig.referencesTable,
1888
+ tableConfigs,
1889
+ value
1890
+ );
1891
+ row[columnConfig.foreignKey] = created2.id;
1892
+ break;
1893
+ case "hasMany":
1894
+ if (!Array.isArray(value)) {
1895
+ throw new Error(
1896
+ `non-array provided for has-many field ${key} of ${tableName}`
1897
+ );
1898
+ }
1899
+ for (const v of value) {
1900
+ hasManyRecords.push({
1901
+ key,
1902
+ value: v,
1903
+ columnConfig
1904
+ });
1905
+ }
1906
+ break;
1907
+ default:
1908
+ throw new Error(
1909
+ `unsupported relationship type - ${tableName}.${key} (${columnConfig.relationshipType})`
1910
+ );
1911
+ }
1912
+ }
1913
+ query = query.values(row);
1914
+ }
1915
+ const created = await query.returningAll().executeTakeFirstOrThrow();
1916
+ await Promise.all(
1917
+ hasManyRecords.map(async ({ key, value, columnConfig }) => {
1918
+ if (!isPlainObject(value)) {
1919
+ throw new Error(
1920
+ `non-object provided for field ${key} of ${tableName}`
1921
+ );
1922
+ }
1923
+ if (isReferencingExistingRecord(value)) {
1924
+ throw new Error(
1925
+ `nested update as part of create not supported for ${key} of ${tableConfig}`
1926
+ );
1927
+ }
1928
+ return create(conn, columnConfig.referencesTable, tableConfigs, {
1929
+ ...value,
1930
+ [columnConfig.foreignKey]: created.id
1931
+ });
1932
+ })
1933
+ );
1934
+ return transformRichDataTypes(created);
1935
+ } catch (e) {
1936
+ throw new DatabaseError(e);
1937
+ }
1938
+ }
1939
+ __name(create, "create");
1940
+ module2.exports = {
1941
+ ModelAPI,
1942
+ DatabaseError
1943
+ };
1944
+ }
1945
+ });
1946
+
1947
+ // src/RequestHeaders.js
1948
+ var require_RequestHeaders = __commonJS({
1949
+ "src/RequestHeaders.js"(exports2, module2) {
1950
+ "use strict";
1951
+ var _RequestHeaders = class _RequestHeaders {
1952
+ /**
1953
+ * @param {{Object.<string, string>}} requestHeaders Map of request headers submitted from the client
1954
+ */
1955
+ constructor(requestHeaders) {
1956
+ this._headers = new Headers(requestHeaders);
1957
+ }
1958
+ get(key) {
1959
+ return this._headers.get(key);
1960
+ }
1961
+ has(key) {
1962
+ return this._headers.has(key);
1963
+ }
1964
+ };
1965
+ __name(_RequestHeaders, "RequestHeaders");
1966
+ var RequestHeaders = _RequestHeaders;
1967
+ module2.exports = {
1968
+ RequestHeaders
1969
+ };
1970
+ }
1971
+ });
1972
+
1973
+ // src/permissions.js
1974
+ var require_permissions = __commonJS({
1975
+ "src/permissions.js"(exports2, module2) {
1976
+ "use strict";
1977
+ var { AsyncLocalStorage } = require("async_hooks");
1978
+ var { PermissionError } = require_errors();
1979
+ var PERMISSION_STATE = {
1980
+ UNKNOWN: "unknown",
1981
+ PERMITTED: "permitted",
1982
+ UNPERMITTED: "unpermitted"
1983
+ };
1984
+ var withPermissions = /* @__PURE__ */ __name(async (initialValue, cb) => {
1985
+ const permissions = new Permissions();
1986
+ return await permissionsApiInstance.run({ permitted: initialValue }, () => {
1987
+ return cb({ getPermissionState: permissions.getState });
1988
+ });
1989
+ }, "withPermissions");
1990
+ var permissionsApiInstance = new AsyncLocalStorage();
1991
+ var _Permissions = class _Permissions {
1992
+ // The Go runtime performs role based permission rule checks prior to calling the functions
1993
+ // runtime, so the status could already be granted. If already granted, then we need to inherit that permission state as the state is later used to decide whether to run in process permission checks
1994
+ // TLDR if a role based permission is relevant and it is granted, then it is effectively the same as the end user calling api.permissions.allow() explicitly in terms of behaviour.
1995
+ allow() {
1996
+ const store = permissionsApiInstance.getStore().permitted = true;
1997
+ }
1998
+ deny() {
1999
+ permissionsApiInstance.getStore().permitted = false;
2000
+ throw new PermissionError();
2001
+ }
2002
+ getState() {
2003
+ const permitted = permissionsApiInstance.getStore().permitted;
2004
+ switch (true) {
2005
+ case permitted === false:
2006
+ return PERMISSION_STATE.UNPERMITTED;
2007
+ case permitted === null:
2008
+ return PERMISSION_STATE.UNKNOWN;
2009
+ case permitted === true:
2010
+ return PERMISSION_STATE.PERMITTED;
2011
+ }
2012
+ }
2013
+ };
2014
+ __name(_Permissions, "Permissions");
2015
+ var Permissions = _Permissions;
2016
+ var checkBuiltInPermissions = /* @__PURE__ */ __name(async ({
2017
+ rows,
2018
+ permissionFns,
2019
+ ctx,
2020
+ db,
2021
+ functionName
2022
+ }) => {
2023
+ for (const permissionFn of permissionFns) {
2024
+ const result = await permissionFn(rows, ctx, db);
2025
+ if (result) {
2026
+ return;
2027
+ }
2028
+ }
2029
+ throw new PermissionError(`Not permitted to access ${functionName}`);
2030
+ }, "checkBuiltInPermissions");
2031
+ module2.exports.checkBuiltInPermissions = checkBuiltInPermissions;
2032
+ module2.exports.PermissionError = PermissionError;
2033
+ module2.exports.PERMISSION_STATE = PERMISSION_STATE;
2034
+ module2.exports.Permissions = Permissions;
2035
+ module2.exports.withPermissions = withPermissions;
2036
+ module2.exports.permissionsApiInstance = permissionsApiInstance;
2037
+ }
2038
+ });
2039
+
2040
+ // src/consts.js
2041
+ var require_consts = __commonJS({
2042
+ "src/consts.js"(exports2, module2) {
2043
+ "use strict";
2044
+ var PROTO_ACTION_TYPES = {
2045
+ UNKNOWN: "ACTION_TYPE_UNKNOWN",
2046
+ CREATE: "ACTION_TYPE_CREATE",
2047
+ GET: "ACTION_TYPE_GET",
2048
+ LIST: "ACTION_TYPE_LIST",
2049
+ UPDATE: "ACTION_TYPE_UPDATE",
2050
+ DELETE: "ACTION_TYPE_DELETE",
2051
+ READ: "ACTION_TYPE_READ",
2052
+ WRITE: "ACTION_TYPE_WRITE",
2053
+ JOB: "JOB_TYPE",
2054
+ SUBSCRIBER: "SUBSCRIBER_TYPE",
2055
+ FLOW: "FLOW_TYPE"
2056
+ };
2057
+ module2.exports.PROTO_ACTION_TYPES = PROTO_ACTION_TYPES;
2058
+ }
2059
+ });
2060
+
2061
+ // src/tryExecuteFunction.js
2062
+ var require_tryExecuteFunction = __commonJS({
2063
+ "src/tryExecuteFunction.js"(exports2, module2) {
2064
+ "use strict";
2065
+ var { withDatabase } = require_database();
2066
+ var { withAuditContext } = require_auditing();
2067
+ var {
2068
+ withPermissions,
2069
+ PERMISSION_STATE,
2070
+ checkBuiltInPermissions
2071
+ } = require_permissions();
2072
+ var { PermissionError } = require_errors();
2073
+ var { PROTO_ACTION_TYPES } = require_consts();
2074
+ function tryExecuteFunction({ request, db, permitted, permissionFns, actionType, ctx, functionConfig }, cb) {
2075
+ return withPermissions(permitted, async ({ getPermissionState }) => {
2076
+ let requiresTransaction = true;
2077
+ switch (actionType) {
2078
+ case PROTO_ACTION_TYPES.GET:
2079
+ case PROTO_ACTION_TYPES.LIST:
2080
+ case PROTO_ACTION_TYPES.READ:
2081
+ requiresTransaction = false;
2082
+ break;
2083
+ }
2084
+ if ((functionConfig == null ? void 0 : functionConfig.dbTransaction) !== void 0) {
2085
+ requiresTransaction = functionConfig.dbTransaction;
2086
+ }
2087
+ return withDatabase(db, requiresTransaction, async ({ transaction }) => {
2088
+ const fnResult = await withAuditContext(request, async () => {
2089
+ return cb();
2090
+ });
2091
+ switch (getPermissionState()) {
2092
+ case PERMISSION_STATE.PERMITTED:
2093
+ return fnResult;
2094
+ case PERMISSION_STATE.UNPERMITTED:
2095
+ throw new PermissionError(
2096
+ `Not permitted to access ${request.method}`
2097
+ );
2098
+ default:
2099
+ const relevantPermissions = permissionFns[request.method];
2100
+ const peakInsideTransaction = actionType === PROTO_ACTION_TYPES.CREATE;
2101
+ let rowsForPermissions = [];
2102
+ if (fnResult != null) {
2103
+ switch (actionType) {
2104
+ case PROTO_ACTION_TYPES.LIST:
2105
+ rowsForPermissions = fnResult;
2106
+ break;
2107
+ case PROTO_ACTION_TYPES.DELETE:
2108
+ rowsForPermissions = [{ id: fnResult }];
2109
+ break;
2110
+ case (PROTO_ACTION_TYPES.GET, PROTO_ACTION_TYPES.CREATE):
2111
+ rowsForPermissions = [fnResult];
2112
+ break;
2113
+ default:
2114
+ rowsForPermissions = [fnResult];
2115
+ break;
2116
+ }
2117
+ }
2118
+ await checkBuiltInPermissions({
2119
+ rows: rowsForPermissions,
2120
+ permissionFns: relevantPermissions,
2121
+ // it is important that we pass db here as db represents the connection to the database
2122
+ // *outside* of the current transaction. Given that any changes inside of a transaction
2123
+ // are opaque to the outside, we can utilize this when running permission rules and then deciding to
2124
+ // rollback any changes if they do not pass. However, for creates we need to be able to 'peak' inside the transaction to read the created record, as this won't exist outside of the transaction.
2125
+ db: peakInsideTransaction ? transaction : db,
2126
+ ctx,
2127
+ functionName: request.method
2128
+ });
2129
+ return fnResult;
2130
+ }
2131
+ });
2132
+ });
2133
+ }
2134
+ __name(tryExecuteFunction, "tryExecuteFunction");
2135
+ module2.exports.tryExecuteFunction = tryExecuteFunction;
2136
+ }
2137
+ });
2138
+
2139
+ // src/handleRequest.js
2140
+ var require_handleRequest = __commonJS({
2141
+ "src/handleRequest.js"(exports2, module2) {
2142
+ "use strict";
2143
+ var {
2144
+ createJSONRPCErrorResponse,
2145
+ createJSONRPCSuccessResponse,
2146
+ JSONRPCErrorCode
2147
+ } = require("json-rpc-2.0");
2148
+ var { createDatabaseClient } = require_database();
2149
+ var { tryExecuteFunction } = require_tryExecuteFunction();
2150
+ var { errorToJSONRPCResponse, RuntimeErrors } = require_errors();
2151
+ var opentelemetry = require("@opentelemetry/api");
2152
+ var { withSpan } = require_tracing();
2153
+ var { parseInputs, parseOutputs } = require_parsing();
2154
+ async function handleRequest(request, config) {
2155
+ var _a;
2156
+ const activeContext = opentelemetry.propagation.extract(
2157
+ opentelemetry.context.active(),
2158
+ (_a = request.meta) == null ? void 0 : _a.tracing
2159
+ );
2160
+ if (process.env.KEEL_LOG_LEVEL == "debug") {
2161
+ console.log(request);
2162
+ }
2163
+ return opentelemetry.context.with(activeContext, () => {
2164
+ return withSpan(request.method, async (span) => {
2165
+ var _a2, _b;
2166
+ let db = null;
2167
+ try {
2168
+ const { createContextAPI, functions, permissionFns, actionTypes } = config;
2169
+ if (!(request.method in functions)) {
2170
+ const message = `no corresponding function found for '${request.method}'`;
2171
+ span.setStatus({
2172
+ code: opentelemetry.SpanStatusCode.ERROR,
2173
+ message
2174
+ });
2175
+ return createJSONRPCErrorResponse(
2176
+ request.id,
2177
+ JSONRPCErrorCode.MethodNotFound,
2178
+ message
2179
+ );
2180
+ }
2181
+ const headers = new Headers();
2182
+ const ctx = createContextAPI({
2183
+ responseHeaders: headers,
2184
+ meta: request.meta
2185
+ });
2186
+ const permitted = request.meta && request.meta.permissionState.status === "granted" ? true : null;
2187
+ db = createDatabaseClient({
2188
+ connString: (_b = (_a2 = request.meta) == null ? void 0 : _a2.secrets) == null ? void 0 : _b.KEEL_DB_CONN
2189
+ });
2190
+ const customFunction = functions[request.method];
2191
+ const actionType = actionTypes[request.method];
2192
+ const functionConfig = (customFunction == null ? void 0 : customFunction.config) ?? {};
2193
+ const result = await tryExecuteFunction(
2194
+ {
2195
+ request,
2196
+ ctx,
2197
+ permitted,
2198
+ db,
2199
+ permissionFns,
2200
+ actionType,
2201
+ functionConfig
2202
+ },
2203
+ async () => {
2204
+ const inputs = parseInputs(request.params);
2205
+ const result2 = await customFunction(ctx, inputs);
2206
+ return parseOutputs(result2);
2207
+ }
2208
+ );
2209
+ if (result instanceof Error) {
2210
+ span.recordException(result);
2211
+ span.setStatus({
2212
+ code: opentelemetry.SpanStatusCode.ERROR,
2213
+ message: result.message
2214
+ });
2215
+ return errorToJSONRPCResponse(request, result);
2216
+ }
2217
+ const response = createJSONRPCSuccessResponse(request.id, result);
2218
+ const responseHeaders = {};
2219
+ for (const pair of headers.entries()) {
2220
+ responseHeaders[pair[0]] = pair[1].split(", ");
2221
+ }
2222
+ response.meta = {
2223
+ headers: responseHeaders,
2224
+ status: ctx.response.status
2225
+ };
2226
+ return response;
2227
+ } catch (e) {
2228
+ if (e instanceof Error) {
2229
+ span.recordException(e);
2230
+ span.setStatus({
2231
+ code: opentelemetry.SpanStatusCode.ERROR,
2232
+ message: e.message
2233
+ });
2234
+ return errorToJSONRPCResponse(request, e);
2235
+ }
2236
+ const message = JSON.stringify(e);
2237
+ span.setStatus({
2238
+ code: opentelemetry.SpanStatusCode.ERROR,
2239
+ message
2240
+ });
2241
+ return createJSONRPCErrorResponse(
2242
+ request.id,
2243
+ RuntimeErrors.UnknownError,
2244
+ message
2245
+ );
2246
+ } finally {
2247
+ if (db) {
2248
+ await db.destroy();
2249
+ }
2250
+ }
2251
+ });
2252
+ });
2253
+ }
2254
+ __name(handleRequest, "handleRequest");
2255
+ module2.exports = {
2256
+ handleRequest,
2257
+ RuntimeErrors
2258
+ };
2259
+ }
2260
+ });
2261
+
2262
+ // src/tryExecuteJob.js
2263
+ var require_tryExecuteJob = __commonJS({
2264
+ "src/tryExecuteJob.js"(exports2, module2) {
2265
+ "use strict";
2266
+ var { withDatabase } = require_database();
2267
+ var { withAuditContext } = require_auditing();
2268
+ var { withPermissions, PERMISSION_STATE } = require_permissions();
2269
+ var { PermissionError } = require_errors();
2270
+ function tryExecuteJob({ db, permitted, request, functionConfig }, cb) {
2271
+ return withPermissions(permitted, async ({ getPermissionState }) => {
2272
+ let requiresTransaction = false;
2273
+ if ((functionConfig == null ? void 0 : functionConfig.dbTransaction) !== void 0) {
2274
+ requiresTransaction = functionConfig.dbTransaction;
2275
+ }
2276
+ return withDatabase(db, requiresTransaction, async () => {
2277
+ await withAuditContext(request, async () => {
2278
+ return cb();
2279
+ });
2280
+ if (getPermissionState() === PERMISSION_STATE.UNPERMITTED) {
2281
+ throw new PermissionError(`Not permitted to access ${request.method}`);
2282
+ }
2283
+ });
2284
+ });
2285
+ }
2286
+ __name(tryExecuteJob, "tryExecuteJob");
2287
+ module2.exports.tryExecuteJob = tryExecuteJob;
2288
+ }
2289
+ });
2290
+
2291
+ // src/handleJob.js
2292
+ var require_handleJob = __commonJS({
2293
+ "src/handleJob.js"(exports2, module2) {
2294
+ "use strict";
2295
+ var {
2296
+ createJSONRPCErrorResponse,
2297
+ createJSONRPCSuccessResponse,
2298
+ JSONRPCErrorCode
2299
+ } = require("json-rpc-2.0");
2300
+ var { createDatabaseClient } = require_database();
2301
+ var { errorToJSONRPCResponse, RuntimeErrors } = require_errors();
2302
+ var opentelemetry = require("@opentelemetry/api");
2303
+ var { withSpan } = require_tracing();
2304
+ var { tryExecuteJob } = require_tryExecuteJob();
2305
+ var { parseInputs } = require_parsing();
2306
+ var { PROTO_ACTION_TYPES } = require_consts();
2307
+ async function handleJob(request, config) {
2308
+ var _a;
2309
+ const activeContext = opentelemetry.propagation.extract(
2310
+ opentelemetry.context.active(),
2311
+ (_a = request.meta) == null ? void 0 : _a.tracing
2312
+ );
2313
+ return opentelemetry.context.with(activeContext, () => {
2314
+ return withSpan(request.method, async (span) => {
2315
+ var _a2, _b;
2316
+ let db = null;
2317
+ try {
2318
+ const { createJobContextAPI, jobs } = config;
2319
+ if (!(request.method in jobs)) {
2320
+ const message = `no corresponding job found for '${request.method}'`;
2321
+ span.setStatus({
2322
+ code: opentelemetry.SpanStatusCode.ERROR,
2323
+ message
2324
+ });
2325
+ return createJSONRPCErrorResponse(
2326
+ request.id,
2327
+ JSONRPCErrorCode.MethodNotFound,
2328
+ message
2329
+ );
2330
+ }
2331
+ const ctx = createJobContextAPI({
2332
+ meta: request.meta
2333
+ });
2334
+ const permitted = request.meta && request.meta.permissionState.status === "granted" ? true : null;
2335
+ db = createDatabaseClient({
2336
+ connString: (_b = (_a2 = request.meta) == null ? void 0 : _a2.secrets) == null ? void 0 : _b.KEEL_DB_CONN
2337
+ });
2338
+ const jobFunction = jobs[request.method];
2339
+ const actionType = PROTO_ACTION_TYPES.JOB;
2340
+ const functionConfig = (jobFunction == null ? void 0 : jobFunction.config) ?? {};
2341
+ await tryExecuteJob(
2342
+ { request, permitted, db, actionType, functionConfig },
2343
+ async () => {
2344
+ const inputs = parseInputs(request.params);
2345
+ return jobFunction(ctx, inputs);
2346
+ }
2347
+ );
2348
+ return createJSONRPCSuccessResponse(request.id, null);
2349
+ } catch (e) {
2350
+ if (e instanceof Error) {
2351
+ span.recordException(e);
2352
+ span.setStatus({
2353
+ code: opentelemetry.SpanStatusCode.ERROR,
2354
+ message: e.message
2355
+ });
2356
+ return errorToJSONRPCResponse(request, e);
2357
+ }
2358
+ const message = JSON.stringify(e);
2359
+ span.setStatus({
2360
+ code: opentelemetry.SpanStatusCode.ERROR,
2361
+ message
2362
+ });
2363
+ return createJSONRPCErrorResponse(
2364
+ request.id,
2365
+ RuntimeErrors.UnknownError,
2366
+ message
2367
+ );
2368
+ } finally {
2369
+ if (db) {
2370
+ await db.destroy();
2371
+ }
2372
+ }
2373
+ });
2374
+ });
2375
+ }
2376
+ __name(handleJob, "handleJob");
2377
+ module2.exports = {
2378
+ handleJob,
2379
+ RuntimeErrors
2380
+ };
2381
+ }
2382
+ });
2383
+
2384
+ // src/tryExecuteSubscriber.js
2385
+ var require_tryExecuteSubscriber = __commonJS({
2386
+ "src/tryExecuteSubscriber.js"(exports2, module2) {
2387
+ "use strict";
2388
+ var { withDatabase } = require_database();
2389
+ var { withAuditContext } = require_auditing();
2390
+ function tryExecuteSubscriber({ request, db, functionConfig }, cb) {
2391
+ let requiresTransaction = false;
2392
+ if ((functionConfig == null ? void 0 : functionConfig.dbTransaction) !== void 0) {
2393
+ requiresTransaction = functionConfig.dbTransaction;
2394
+ }
2395
+ return withDatabase(db, requiresTransaction, async () => {
2396
+ await withAuditContext(request, async () => {
2397
+ return cb();
2398
+ });
2399
+ });
2400
+ }
2401
+ __name(tryExecuteSubscriber, "tryExecuteSubscriber");
2402
+ module2.exports.tryExecuteSubscriber = tryExecuteSubscriber;
2403
+ }
2404
+ });
2405
+
2406
+ // src/handleSubscriber.js
2407
+ var require_handleSubscriber = __commonJS({
2408
+ "src/handleSubscriber.js"(exports2, module2) {
2409
+ "use strict";
2410
+ var {
2411
+ createJSONRPCErrorResponse,
2412
+ createJSONRPCSuccessResponse,
2413
+ JSONRPCErrorCode
2414
+ } = require("json-rpc-2.0");
2415
+ var { createDatabaseClient } = require_database();
2416
+ var { errorToJSONRPCResponse, RuntimeErrors } = require_errors();
2417
+ var opentelemetry = require("@opentelemetry/api");
2418
+ var { withSpan } = require_tracing();
2419
+ var { PROTO_ACTION_TYPES } = require_consts();
2420
+ var { tryExecuteSubscriber } = require_tryExecuteSubscriber();
2421
+ var { parseInputs } = require_parsing();
2422
+ async function handleSubscriber(request, config) {
2423
+ var _a;
2424
+ const activeContext = opentelemetry.propagation.extract(
2425
+ opentelemetry.context.active(),
2426
+ (_a = request.meta) == null ? void 0 : _a.tracing
2427
+ );
2428
+ return opentelemetry.context.with(activeContext, () => {
2429
+ return withSpan(request.method, async (span) => {
2430
+ var _a2, _b;
2431
+ let db = null;
2432
+ try {
2433
+ const { createSubscriberContextAPI, subscribers } = config;
2434
+ if (!(request.method in subscribers)) {
2435
+ const message = `no corresponding subscriber found for '${request.method}'`;
2436
+ span.setStatus({
2437
+ code: opentelemetry.SpanStatusCode.ERROR,
2438
+ message
2439
+ });
2440
+ return createJSONRPCErrorResponse(
2441
+ request.id,
2442
+ JSONRPCErrorCode.MethodNotFound,
2443
+ message
2444
+ );
2445
+ }
2446
+ const ctx = createSubscriberContextAPI({
2447
+ meta: request.meta
2448
+ });
2449
+ db = createDatabaseClient({
2450
+ connString: (_b = (_a2 = request.meta) == null ? void 0 : _a2.secrets) == null ? void 0 : _b.KEEL_DB_CONN
2451
+ });
2452
+ const subscriberFunction = subscribers[request.method];
2453
+ const actionType = PROTO_ACTION_TYPES.SUBSCRIBER;
2454
+ const functionConfig = (subscriberFunction == null ? void 0 : subscriberFunction.config) ?? {};
2455
+ await tryExecuteSubscriber(
2456
+ { request, db, actionType, functionConfig },
2457
+ async () => {
2458
+ const inputs = parseInputs(request.params);
2459
+ return subscriberFunction(ctx, inputs);
2460
+ }
2461
+ );
2462
+ return createJSONRPCSuccessResponse(request.id, null);
2463
+ } catch (e) {
2464
+ if (e instanceof Error) {
2465
+ span.recordException(e);
2466
+ span.setStatus({
2467
+ code: opentelemetry.SpanStatusCode.ERROR,
2468
+ message: e.message
2469
+ });
2470
+ return errorToJSONRPCResponse(request, e);
2471
+ }
2472
+ const message = JSON.stringify(e);
2473
+ span.setStatus({
2474
+ code: opentelemetry.SpanStatusCode.ERROR,
2475
+ message
2476
+ });
2477
+ return createJSONRPCErrorResponse(
2478
+ request.id,
2479
+ RuntimeErrors.UnknownError,
2480
+ message
2481
+ );
2482
+ } finally {
2483
+ if (db) {
2484
+ await db.destroy();
2485
+ }
2486
+ }
2487
+ });
2488
+ });
2489
+ }
2490
+ __name(handleSubscriber, "handleSubscriber");
2491
+ module2.exports = {
2492
+ handleSubscriber,
2493
+ RuntimeErrors
2494
+ };
2495
+ }
2496
+ });
2497
+
2498
+ // src/handleRoute.js
2499
+ var require_handleRoute = __commonJS({
2500
+ "src/handleRoute.js"(exports2, module2) {
2501
+ "use strict";
2502
+ var {
2503
+ createJSONRPCErrorResponse,
2504
+ createJSONRPCSuccessResponse,
2505
+ JSONRPCErrorCode
2506
+ } = require("json-rpc-2.0");
2507
+ var { createDatabaseClient, withDatabase } = require_database();
2508
+ var { withAuditContext } = require_auditing();
2509
+ var { errorToJSONRPCResponse, RuntimeErrors } = require_errors();
2510
+ var opentelemetry = require("@opentelemetry/api");
2511
+ var { withSpan } = require_tracing();
2512
+ async function handleRoute(request, config) {
2513
+ var _a;
2514
+ const activeContext = opentelemetry.propagation.extract(
2515
+ opentelemetry.context.active(),
2516
+ (_a = request.meta) == null ? void 0 : _a.tracing
2517
+ );
2518
+ return opentelemetry.context.with(activeContext, () => {
2519
+ return withSpan(request.method, async (span) => {
2520
+ var _a2, _b;
2521
+ let db = null;
2522
+ try {
2523
+ const { createContextAPI, functions } = config;
2524
+ if (!(request.method in functions)) {
2525
+ const message = `no route function found for '${request.method}'`;
2526
+ span.setStatus({
2527
+ code: opentelemetry.SpanStatusCode.ERROR,
2528
+ message
2529
+ });
2530
+ return createJSONRPCErrorResponse(
2531
+ request.id,
2532
+ JSONRPCErrorCode.MethodNotFound,
2533
+ message
2534
+ );
2535
+ }
2536
+ const {
2537
+ headers,
2538
+ response: __,
2539
+ ...ctx
2540
+ } = createContextAPI({
2541
+ responseHeaders: new Headers(),
2542
+ meta: request.meta
2543
+ });
2544
+ request.params.headers = headers;
2545
+ db = createDatabaseClient({
2546
+ connString: (_b = (_a2 = request.meta) == null ? void 0 : _a2.secrets) == null ? void 0 : _b.KEEL_DB_CONN
2547
+ });
2548
+ const routeHandler = functions[request.method];
2549
+ const result = await withDatabase(db, false, () => {
2550
+ return withAuditContext(request, () => {
2551
+ return routeHandler(request.params, ctx);
2552
+ });
2553
+ });
2554
+ if (result instanceof Error) {
2555
+ span.recordException(result);
2556
+ span.setStatus({
2557
+ code: opentelemetry.SpanStatusCode.ERROR,
2558
+ message: result.message
2559
+ });
2560
+ return errorToJSONRPCResponse(request, result);
2561
+ }
2562
+ const response = createJSONRPCSuccessResponse(request.id, result);
2563
+ return response;
2564
+ } catch (e) {
2565
+ if (e instanceof Error) {
2566
+ span.recordException(e);
2567
+ span.setStatus({
2568
+ code: opentelemetry.SpanStatusCode.ERROR,
2569
+ message: e.message
2570
+ });
2571
+ return errorToJSONRPCResponse(request, e);
2572
+ }
2573
+ const message = JSON.stringify(e);
2574
+ span.setStatus({
2575
+ code: opentelemetry.SpanStatusCode.ERROR,
2576
+ message
2577
+ });
2578
+ return createJSONRPCErrorResponse(
2579
+ request.id,
2580
+ RuntimeErrors.UnknownError,
2581
+ message
2582
+ );
2583
+ } finally {
2584
+ if (db) {
2585
+ await db.destroy();
2586
+ }
2587
+ }
2588
+ });
2589
+ });
2590
+ }
2591
+ __name(handleRoute, "handleRoute");
2592
+ module2.exports = {
2593
+ handleRoute,
2594
+ RuntimeErrors
2595
+ };
2596
+ }
2597
+ });
2598
+
2599
+ // src/tryExecuteFlow.js
2600
+ var require_tryExecuteFlow = __commonJS({
2601
+ "src/tryExecuteFlow.js"(exports2, module2) {
2602
+ "use strict";
2603
+ var { withDatabase } = require_database();
2604
+ function tryExecuteFlow(db, cb) {
2605
+ return withDatabase(db, false, async () => {
2606
+ return cb();
2607
+ });
2608
+ }
2609
+ __name(tryExecuteFlow, "tryExecuteFlow");
2610
+ module2.exports.tryExecuteFlow = tryExecuteFlow;
2611
+ }
2612
+ });
2613
+
2614
+ // src/flows/ui/elements/input/text.ts
2615
+ var textInput;
2616
+ var init_text = __esm({
2617
+ "src/flows/ui/elements/input/text.ts"() {
2618
+ "use strict";
2619
+ textInput = /* @__PURE__ */ __name((name, options) => {
2620
+ return {
2621
+ uiConfig: {
2622
+ __type: "ui.input.text",
2623
+ name,
2624
+ label: (options == null ? void 0 : options.label) || name,
2625
+ defaultValue: options == null ? void 0 : options.defaultValue,
2626
+ optional: options == null ? void 0 : options.optional,
2627
+ placeholder: options == null ? void 0 : options.placeholder,
2628
+ multiline: options == null ? void 0 : options.multiline,
2629
+ maxLength: options == null ? void 0 : options.maxLength,
2630
+ minLength: options == null ? void 0 : options.minLength
2631
+ },
2632
+ validate: options == null ? void 0 : options.validate,
2633
+ getData: /* @__PURE__ */ __name((x) => x, "getData")
2634
+ };
2635
+ }, "textInput");
2636
+ }
2637
+ });
2638
+
2639
+ // src/flows/ui/elements/input/number.ts
2640
+ var numberInput;
2641
+ var init_number = __esm({
2642
+ "src/flows/ui/elements/input/number.ts"() {
2643
+ "use strict";
2644
+ numberInput = /* @__PURE__ */ __name((name, options) => {
2645
+ return {
2646
+ uiConfig: {
2647
+ __type: "ui.input.number",
2648
+ name,
2649
+ label: (options == null ? void 0 : options.label) || name,
2650
+ defaultValue: options == null ? void 0 : options.defaultValue,
2651
+ optional: options == null ? void 0 : options.optional,
2652
+ placeholder: options == null ? void 0 : options.placeholder,
2653
+ min: options == null ? void 0 : options.min,
2654
+ max: options == null ? void 0 : options.max
2655
+ },
2656
+ validate: options == null ? void 0 : options.validate,
2657
+ getData: /* @__PURE__ */ __name((x) => x, "getData")
2658
+ };
2659
+ }, "numberInput");
2660
+ }
2661
+ });
2662
+
2663
+ // src/flows/ui/elements/display/divider.ts
2664
+ var divider;
2665
+ var init_divider = __esm({
2666
+ "src/flows/ui/elements/display/divider.ts"() {
2667
+ "use strict";
2668
+ divider = /* @__PURE__ */ __name((options) => {
2669
+ return {
2670
+ uiConfig: {
2671
+ __type: "ui.display.divider"
2672
+ }
2673
+ };
2674
+ }, "divider");
2675
+ }
2676
+ });
2677
+
2678
+ // src/flows/ui/elements/input/boolean.ts
2679
+ var booleanInput;
2680
+ var init_boolean = __esm({
2681
+ "src/flows/ui/elements/input/boolean.ts"() {
2682
+ "use strict";
2683
+ booleanInput = /* @__PURE__ */ __name((name, options) => {
2684
+ return {
2685
+ uiConfig: {
2686
+ __type: "ui.input.boolean",
2687
+ name,
2688
+ label: (options == null ? void 0 : options.label) || name,
2689
+ defaultValue: options == null ? void 0 : options.defaultValue,
2690
+ optional: options == null ? void 0 : options.optional,
2691
+ mode: (options == null ? void 0 : options.mode) || "checkbox"
2692
+ },
2693
+ validate: options == null ? void 0 : options.validate,
2694
+ getData: /* @__PURE__ */ __name((x) => x, "getData")
2695
+ };
2696
+ }, "booleanInput");
2697
+ }
2698
+ });
2699
+
2700
+ // src/flows/ui/elements/display/markdown.ts
2701
+ var markdown;
2702
+ var init_markdown = __esm({
2703
+ "src/flows/ui/elements/display/markdown.ts"() {
2704
+ "use strict";
2705
+ markdown = /* @__PURE__ */ __name((options) => {
2706
+ return {
2707
+ uiConfig: {
2708
+ __type: "ui.display.markdown",
2709
+ content: (options == null ? void 0 : options.content) || ""
2710
+ }
2711
+ };
2712
+ }, "markdown");
2713
+ }
2714
+ });
2715
+
2716
+ // src/flows/ui/elements/display/table.ts
2717
+ var table;
2718
+ var init_table = __esm({
2719
+ "src/flows/ui/elements/display/table.ts"() {
2720
+ "use strict";
2721
+ table = /* @__PURE__ */ __name((options) => {
2722
+ return {
2723
+ uiConfig: {
2724
+ __type: "ui.display.table",
2725
+ data: (options == null ? void 0 : options.data) || [],
2726
+ columns: (options == null ? void 0 : options.columns) || []
2727
+ }
2728
+ };
2729
+ }, "table");
2730
+ }
2731
+ });
2732
+
2733
+ // src/flows/ui/elements/select/single.ts
2734
+ var selectOne;
2735
+ var init_single = __esm({
2736
+ "src/flows/ui/elements/select/single.ts"() {
2737
+ "use strict";
2738
+ selectOne = /* @__PURE__ */ __name((name, options) => {
2739
+ return {
2740
+ uiConfig: {
2741
+ __type: "ui.select.single",
2742
+ name,
2743
+ label: (options == null ? void 0 : options.label) || name,
2744
+ defaultValue: options == null ? void 0 : options.defaultValue,
2745
+ optional: options == null ? void 0 : options.optional,
2746
+ options: []
2747
+ },
2748
+ validate: options == null ? void 0 : options.validate,
2749
+ getData: /* @__PURE__ */ __name((x) => x, "getData")
2750
+ };
2751
+ }, "selectOne");
2752
+ }
2753
+ });
2754
+
2755
+ // src/flows/disrupts.ts
2756
+ var disrupts_exports = {};
2757
+ __export(disrupts_exports, {
2758
+ StepCompletedDisrupt: () => StepCompletedDisrupt,
2759
+ StepErrorDisrupt: () => StepErrorDisrupt,
2760
+ UIRenderDisrupt: () => UIRenderDisrupt
2761
+ });
2762
+ var _FlowDisrupt, FlowDisrupt, _UIRenderDisrupt, UIRenderDisrupt, _StepErrorDisrupt, StepErrorDisrupt, _StepCompletedDisrupt, StepCompletedDisrupt;
2763
+ var init_disrupts = __esm({
2764
+ "src/flows/disrupts.ts"() {
2765
+ "use strict";
2766
+ _FlowDisrupt = class _FlowDisrupt {
2767
+ constructor() {
2768
+ }
2769
+ };
2770
+ __name(_FlowDisrupt, "FlowDisrupt");
2771
+ FlowDisrupt = _FlowDisrupt;
2772
+ _UIRenderDisrupt = class _UIRenderDisrupt extends FlowDisrupt {
2773
+ constructor(stepId, contents) {
2774
+ super();
2775
+ this.stepId = stepId;
2776
+ this.contents = contents;
2777
+ }
2778
+ };
2779
+ __name(_UIRenderDisrupt, "UIRenderDisrupt");
2780
+ UIRenderDisrupt = _UIRenderDisrupt;
2781
+ _StepErrorDisrupt = class _StepErrorDisrupt extends FlowDisrupt {
2782
+ constructor(errorMessage) {
2783
+ super();
2784
+ this.errorMessage = errorMessage;
2785
+ }
2786
+ };
2787
+ __name(_StepErrorDisrupt, "StepErrorDisrupt");
2788
+ StepErrorDisrupt = _StepErrorDisrupt;
2789
+ _StepCompletedDisrupt = class _StepCompletedDisrupt extends FlowDisrupt {
2790
+ constructor() {
2791
+ super();
2792
+ }
2793
+ };
2794
+ __name(_StepCompletedDisrupt, "StepCompletedDisrupt");
2795
+ StepCompletedDisrupt = _StepCompletedDisrupt;
2796
+ }
2797
+ });
2798
+
2799
+ // src/flows/index.ts
2800
+ var flows_exports = {};
2801
+ __export(flows_exports, {
2802
+ createStepContext: () => createStepContext
2803
+ });
2804
+ function createStepContext(runId, data, spanId) {
2805
+ return {
2806
+ step: /* @__PURE__ */ __name(async (name, fn, opts) => {
2807
+ const db = (0, import_database.useDatabase)();
2808
+ const completed = await db.selectFrom("keel_flow_step").where("run_id", "=", runId).where("name", "=", name).where("status", "=", "COMPLETED" /* COMPLETED */).selectAll().executeTakeFirst();
2809
+ if (completed) {
2810
+ return completed.value;
2811
+ }
2812
+ const step = await db.insertInto("keel_flow_step").values({
2813
+ run_id: runId,
2814
+ name,
2815
+ status: "NEW" /* NEW */,
2816
+ type: "FUNCTION" /* FUNCTION */,
2817
+ maxRetries: (opts == null ? void 0 : opts.maxRetries) ?? defaultOpts.maxRetries,
2818
+ timeoutInMs: (opts == null ? void 0 : opts.timeoutInMs) ?? defaultOpts.timeoutInMs
2819
+ }).returningAll().executeTakeFirst();
2820
+ let result = null;
2821
+ try {
2822
+ result = await withTimeout(fn(), step.timeoutInMs);
2823
+ } catch (e) {
2824
+ await db.updateTable("keel_flow_step").set({
2825
+ status: "FAILED" /* FAILED */,
2826
+ spanId
2827
+ // TODO: store error message
2828
+ }).where("id", "=", step.id).returningAll().executeTakeFirst();
2829
+ throw new StepErrorDisrupt(
2830
+ e instanceof Error ? e.message : "an error occurred"
2831
+ );
2832
+ }
2833
+ await db.updateTable("keel_flow_step").set({
2834
+ status: "COMPLETED" /* COMPLETED */,
2835
+ value: JSON.stringify(result),
2836
+ spanId
2837
+ }).where("id", "=", step.id).returningAll().executeTakeFirst();
2838
+ throw new StepCompletedDisrupt();
2839
+ }, "step"),
2840
+ ui: {
2841
+ page: /* @__PURE__ */ __name(async (page) => {
2842
+ const db = (0, import_database.useDatabase)();
2843
+ let step = await db.selectFrom("keel_flow_step").where("run_id", "=", runId).where("name", "=", page.title).selectAll().executeTakeFirst();
2844
+ if (step && step.status === "COMPLETED" /* COMPLETED */) {
2845
+ return step.value;
2846
+ }
2847
+ if (!step) {
2848
+ step = await db.insertInto("keel_flow_step").values({
2849
+ run_id: runId,
2850
+ name: page.title,
2851
+ status: "PENDING" /* PENDING */,
2852
+ type: "UI" /* UI */,
2853
+ maxRetries: 3,
2854
+ timeoutInMs: 1e3
2855
+ }).returningAll().executeTakeFirst();
2856
+ }
2857
+ if (data) {
2858
+ await db.updateTable("keel_flow_step").set({
2859
+ status: "COMPLETED" /* COMPLETED */,
2860
+ value: JSON.stringify(data),
2861
+ spanId
2862
+ }).where("id", "=", step.id).returningAll().executeTakeFirst();
2863
+ return data;
2864
+ } else {
2865
+ throw new UIRenderDisrupt(step.id, page);
2866
+ }
2867
+ }, "page"),
2868
+ inputs: {
2869
+ text: textInput,
2870
+ number: numberInput,
2871
+ boolean: booleanInput
2872
+ },
2873
+ display: {
2874
+ divider,
2875
+ markdown,
2876
+ table
2877
+ },
2878
+ select: {
2879
+ single: selectOne
2880
+ }
2881
+ }
2882
+ };
2883
+ }
2884
+ function wait(milliseconds) {
2885
+ return new Promise((resolve) => setTimeout(resolve, milliseconds));
2886
+ }
2887
+ function withTimeout(promiseFn, timeout) {
2888
+ return Promise.race([
2889
+ promiseFn,
2890
+ wait(timeout).then(() => {
2891
+ throw new Error(`flow times out after ${timeout}ms`);
2892
+ })
2893
+ ]);
2894
+ }
2895
+ var import_database, defaultOpts;
2896
+ var init_flows = __esm({
2897
+ "src/flows/index.ts"() {
2898
+ "use strict";
2899
+ import_database = __toESM(require_database());
2900
+ init_text();
2901
+ init_number();
2902
+ init_divider();
2903
+ init_boolean();
2904
+ init_markdown();
2905
+ init_table();
2906
+ init_single();
2907
+ init_disrupts();
2908
+ defaultOpts = {
2909
+ maxRetries: 5,
2910
+ timeoutInMs: 6e4
2911
+ };
2912
+ __name(createStepContext, "createStepContext");
2913
+ __name(wait, "wait");
2914
+ __name(withTimeout, "withTimeout");
2915
+ }
2916
+ });
2917
+
2918
+ // src/handleFlow.js
2919
+ var require_handleFlow = __commonJS({
2920
+ "src/handleFlow.js"(exports2, module2) {
2921
+ "use strict";
2922
+ var {
2923
+ createJSONRPCErrorResponse,
2924
+ createJSONRPCSuccessResponse,
2925
+ JSONRPCErrorCode
2926
+ } = require("json-rpc-2.0");
2927
+ var { createDatabaseClient } = require_database();
2928
+ var { errorToJSONRPCResponse, RuntimeErrors } = require_errors();
2929
+ var opentelemetry = require("@opentelemetry/api");
2930
+ var { withSpan } = require_tracing();
2931
+ var { tryExecuteFlow } = require_tryExecuteFlow();
2932
+ var { parseInputs } = require_parsing();
2933
+ var { createStepContext: createStepContext2 } = (init_flows(), __toCommonJS(flows_exports));
2934
+ var {
2935
+ StepCompletedDisrupt: StepCompletedDisrupt2,
2936
+ StepErrorDisrupt: StepErrorDisrupt2,
2937
+ UIRenderDisrupt: UIRenderDisrupt2
2938
+ } = (init_disrupts(), __toCommonJS(disrupts_exports));
2939
+ async function handleFlow(request, config) {
2940
+ var _a;
2941
+ const activeContext = opentelemetry.propagation.extract(
2942
+ opentelemetry.context.active(),
2943
+ (_a = request.meta) == null ? void 0 : _a.tracing
2944
+ );
2945
+ return opentelemetry.context.with(activeContext, () => {
2946
+ return withSpan(request.method, async (span) => {
2947
+ var _a2, _b, _c;
2948
+ let db = null;
2949
+ let flowConfig = null;
2950
+ const runId = (_a2 = request.meta) == null ? void 0 : _a2.runId;
2951
+ try {
2952
+ if (!runId) {
2953
+ throw new Error("no runId provided");
2954
+ }
2955
+ const { flows } = config;
2956
+ if (!(request.method in flows)) {
2957
+ const message = `no corresponding flow found for '${request.method}'`;
2958
+ span.setStatus({
2959
+ code: opentelemetry.SpanStatusCode.ERROR,
2960
+ message
2961
+ });
2962
+ return createJSONRPCErrorResponse(
2963
+ request.id,
2964
+ JSONRPCErrorCode.MethodNotFound,
2965
+ message
2966
+ );
2967
+ }
2968
+ db = createDatabaseClient({
2969
+ connString: (_c = (_b = request.meta) == null ? void 0 : _b.secrets) == null ? void 0 : _c.KEEL_DB_CONN
2970
+ });
2971
+ const flowRun = await db.selectFrom("keel_flow_run").where("id", "=", runId).selectAll().executeTakeFirst();
2972
+ if (!flowRun) {
2973
+ throw new Error("no flow run found");
2974
+ }
2975
+ const ctx = createStepContext2(
2976
+ request.meta.runId,
2977
+ request.meta.data,
2978
+ span.spanContext().spanId
2979
+ );
2980
+ const flowFunction = flows[request.method].fn;
2981
+ flowConfig = flows[request.method].config;
2982
+ await tryExecuteFlow(db, async () => {
2983
+ const inputs = parseInputs(flowRun.input);
2984
+ return flowFunction(ctx, inputs);
2985
+ });
2986
+ return createJSONRPCSuccessResponse(request.id, {
2987
+ runId,
2988
+ runCompleted: true,
2989
+ config: flowConfig
2990
+ });
2991
+ } catch (e) {
2992
+ if (e instanceof StepCompletedDisrupt2) {
2993
+ return createJSONRPCSuccessResponse(request.id, {
2994
+ runId,
2995
+ runCompleted: false,
2996
+ config: flowConfig
2997
+ });
2998
+ }
2999
+ if (e instanceof UIRenderDisrupt2) {
3000
+ return createJSONRPCSuccessResponse(request.id, {
3001
+ runId,
3002
+ stepId: e.stepId,
3003
+ config: flowConfig,
3004
+ ui: e.contents
3005
+ });
3006
+ }
3007
+ if (e instanceof Error) {
3008
+ span.recordException(e);
3009
+ span.setStatus({
3010
+ code: opentelemetry.SpanStatusCode.ERROR,
3011
+ message: e.message
3012
+ });
3013
+ return errorToJSONRPCResponse(request, e);
3014
+ }
3015
+ const message = JSON.stringify(e);
3016
+ span.setStatus({
3017
+ code: opentelemetry.SpanStatusCode.ERROR,
3018
+ message
3019
+ });
3020
+ return createJSONRPCErrorResponse(
3021
+ request.id,
3022
+ RuntimeErrors.UnknownError,
3023
+ message
3024
+ );
3025
+ } finally {
3026
+ if (db) {
3027
+ await db.destroy();
3028
+ }
3029
+ }
3030
+ });
3031
+ });
3032
+ }
3033
+ __name(handleFlow, "handleFlow");
3034
+ module2.exports = {
3035
+ handleFlow,
3036
+ RuntimeErrors
3037
+ };
3038
+ }
3039
+ });
3040
+
3041
+ // src/index.js
3042
+ var require_index = __commonJS({
3043
+ "src/index.js"(exports2, module2) {
3044
+ "use strict";
3045
+ var { ModelAPI } = require_ModelAPI();
3046
+ var { RequestHeaders } = require_RequestHeaders();
3047
+ var { handleRequest } = require_handleRequest();
3048
+ var { handleJob } = require_handleJob();
3049
+ var { handleSubscriber } = require_handleSubscriber();
3050
+ var { handleRoute } = require_handleRoute();
3051
+ var { handleFlow } = require_handleFlow();
3052
+ var KSUID = require("ksuid");
3053
+ var { useDatabase: useDatabase2 } = require_database();
3054
+ var {
3055
+ Permissions,
3056
+ PERMISSION_STATE,
3057
+ checkBuiltInPermissions
3058
+ } = require_permissions();
3059
+ var tracing = require_tracing();
3060
+ var { InlineFile, File } = require_File();
3061
+ var { Duration } = require_Duration();
3062
+ var { ErrorPresets } = require_errors();
3063
+ module2.exports = {
3064
+ ModelAPI,
3065
+ RequestHeaders,
3066
+ handleRequest,
3067
+ handleJob,
3068
+ handleSubscriber,
3069
+ handleRoute,
3070
+ handleFlow,
3071
+ KSUID,
3072
+ useDatabase: useDatabase2,
3073
+ Permissions,
3074
+ PERMISSION_STATE,
3075
+ checkBuiltInPermissions,
3076
+ tracing,
3077
+ InlineFile,
3078
+ File,
3079
+ Duration,
3080
+ ErrorPresets,
3081
+ ksuid() {
3082
+ return KSUID.randomSync().string;
3083
+ }
3084
+ };
3085
+ }
3086
+ });
3087
+
3088
+ // src/index.ts
3089
+ var index_exports = {};
3090
+ module.exports = __toCommonJS(index_exports);
3091
+ __reExport(index_exports, __toESM(require_index()), module.exports);
3092
+ init_flows();
3093
+ //# sourceMappingURL=index.js.map