@teamkeel/functions-runtime 0.412.0-next.3 → 0.412.0

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