@flusys/nestjs-entity-builder 9.2.0 → 9.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -22
- package/config/entity-builder.constants.d.ts +1 -0
- package/config/message-keys.d.ts +11 -2
- package/controllers/definition-bundle.controller.d.ts +2 -2
- package/controllers/entity-definition.controller.d.ts +0 -1
- package/controllers/field-definition.controller.d.ts +0 -1
- package/controllers/flow-definition.controller.d.ts +2 -1
- package/controllers/flow-runtime.controller.d.ts +4 -3
- package/fesm/21.js +13 -5
- package/fesm/{458.js → 28.js} +104 -62
- package/fesm/677.js +1234 -469
- package/fesm/794.js +1 -1
- package/fesm/857.js +1 -2
- package/fesm/870.js +415 -332
- package/fesm/897.js +23 -29
- package/fesm/946.js +67 -42
- package/fesm/config/index.js +10 -14
- package/fesm/docs/index.js +1 -1
- package/fesm/guards/index.js +15 -9
- package/fesm/index.js +25 -25
- package/fesm/modules/index.js +4 -3
- package/fesm/services/index.js +29 -58
- package/flow-engine/flow-code-runner.d.ts +16 -0
- package/flow-engine/flow-graph.validator.d.ts +1 -0
- package/flow-engine/flow-input.validator.d.ts +14 -0
- package/flow-engine/flow-run.types.d.ts +15 -0
- package/flow-engine/flow.types.d.ts +1 -1
- package/guards/designer-writable.interceptor.d.ts +1 -0
- package/interfaces/definition-bundle.interface.d.ts +2 -0
- package/interfaces/entity-builder-module.interface.d.ts +9 -0
- package/modules/entity-builder.module.d.ts +1 -0
- package/modules/index.d.ts +1 -0
- package/modules/tenant-context-id.strategy.d.ts +16 -0
- package/package.json +3 -4
- package/services/definition-bundle.diff.d.ts +0 -2
- package/services/definition-bundle.file.d.ts +2 -1
- package/services/definition-bundle.service.d.ts +0 -1
- package/services/entity-builder-config.service.d.ts +3 -0
- package/services/entity-builder-datasource.provider.d.ts +1 -0
- package/services/field-value-validator.d.ts +7 -0
- package/services/flow-definition.service.d.ts +10 -2
- package/services/flow-executor.service.d.ts +3 -0
- package/services/flow-runtime.service.d.ts +6 -1
- package/services/generic-entity-service.factory.d.ts +5 -1
- package/services/generic-entity.service.d.ts +24 -2
- package/services/identifier-rules.d.ts +0 -1
- package/services/save-loaded.d.ts +2 -0
- package/services/schema-columns.d.ts +7 -2
- package/services/schema-evolution.service.d.ts +8 -2
- package/services/schema-registry.service.d.ts +12 -0
- package/services/schema-sync.service.d.ts +3 -1
- package/flow-engine/flow-code-sandbox.d.ts +0 -5
package/fesm/677.js
CHANGED
|
@@ -9,11 +9,13 @@ import * as __rspack_external_node_url_3991086a from "node:url";
|
|
|
9
9
|
import * as __rspack_external_node_worker_threads_28ca7740 from "node:worker_threads";
|
|
10
10
|
import * as __rspack_external__flusys_nestjs_shared_exceptions_3edf22e9 from "@flusys/nestjs-shared/exceptions";
|
|
11
11
|
import * as __rspack_external_node_dns_c6249ebb from "node:dns";
|
|
12
|
+
import * as __rspack_external_typeorm_persistence_Subject_js_50dfc7e4 from "typeorm/persistence/Subject.js";
|
|
13
|
+
import * as __rspack_external_typeorm_persistence_SubjectExecutor_js_7cea48f1 from "typeorm/persistence/SubjectExecutor.js";
|
|
12
14
|
export const __rspack_esm_id = 677;
|
|
13
15
|
export const __rspack_esm_ids = [677];
|
|
14
16
|
export const __webpack_modules__ = {
|
|
15
17
|
982(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
16
|
-
/**
|
|
18
|
+
/** The entity names the module publishes events under; also reserved as entity codes. */ const ENTITY_BUILDER_EVENT_ENTITIES = {
|
|
17
19
|
ENTITY_DEFINITION: 'entity_definition',
|
|
18
20
|
FIELD_DEFINITION: 'field_definition',
|
|
19
21
|
SCHEMA_CHANGE_LOG: 'schema_change_log',
|
|
@@ -84,7 +86,7 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
84
86
|
|
|
85
87
|
|
|
86
88
|
},
|
|
87
|
-
|
|
89
|
+
3931(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
88
90
|
|
|
89
91
|
// EXPORTS
|
|
90
92
|
__webpack_require__.d(__webpack_exports__, {
|
|
@@ -114,7 +116,7 @@ var flow_types = __webpack_require__(6256);
|
|
|
114
116
|
|
|
115
117
|
// EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/flow-engine/redact.ts
|
|
116
118
|
var redact = __webpack_require__(2678);
|
|
117
|
-
;// CONCATENATED MODULE: ./projects/nestjs-entity-builder/src/flow-engine/flow-code-
|
|
119
|
+
;// CONCATENATED MODULE: ./projects/nestjs-entity-builder/src/flow-engine/flow-code-runner.ts
|
|
118
120
|
|
|
119
121
|
|
|
120
122
|
|
|
@@ -123,7 +125,7 @@ var redact = __webpack_require__(2678);
|
|
|
123
125
|
|
|
124
126
|
|
|
125
127
|
|
|
126
|
-
const PRELUDE = `(function (input, hostLog, sensitive, maxChars) {
|
|
128
|
+
const PRELUDE = `(function (input, hostLog, sensitive, maxChars, hostRequire, hostFetch, hostTimers) {
|
|
127
129
|
'use strict';
|
|
128
130
|
const freeze = (o) => {
|
|
129
131
|
if (o && typeof o === 'object' && !Object.isFrozen(o)) {
|
|
@@ -148,136 +150,191 @@ const PRELUDE = `(function (input, hostLog, sensitive, maxChars) {
|
|
|
148
150
|
const held = (code) => typeof code === 'string' && granted.some((p) => p === code || p === '*' || (p.endsWith('.*') && code.startsWith(p.slice(0, -1))));
|
|
149
151
|
const codes = (args) => args.flat().filter((c) => typeof c === 'string' && c);
|
|
150
152
|
const constant = (name, value) => Object.defineProperty(globalThis, name, { value, writable: false, configurable: false });
|
|
153
|
+
const textOf = (body) => (typeof body === 'string' ? body : JSON.stringify(body));
|
|
154
|
+
const timer = (repeat) => (fn, ms, ...args) => (typeof fn === 'function' ? hostTimers.set(() => fn(...args), Number(ms) || 0, repeat) : 0);
|
|
155
|
+
const clear = (id) => { hostTimers.clear(id); };
|
|
151
156
|
Object.defineProperty(globalThis, 'ctx', { value: ctx, writable: false, configurable: false, enumerable: true });
|
|
152
|
-
constant('console', freeze({ log, info: log, warn: log, error: log }));
|
|
157
|
+
constant('console', freeze({ log, info: log, warn: log, error: log, debug: log }));
|
|
153
158
|
constant('hasPermission', (code) => held(code));
|
|
154
159
|
constant('hasAnyPermission', (...list) => codes(list).some(held));
|
|
155
160
|
constant('hasAllPermissions', (...list) => { const c = codes(list); return c.length > 0 && c.every(held); });
|
|
161
|
+
constant('require', (name) => hostRequire(String(name)));
|
|
162
|
+
constant('fetch', async (url, init) => {
|
|
163
|
+
const options = init && typeof init === 'object' ? init : {};
|
|
164
|
+
const headers = {};
|
|
165
|
+
for (const [k, v] of Object.entries(options.headers && typeof options.headers === 'object' ? options.headers : {})) headers[k] = String(v);
|
|
166
|
+
const body = options.body === undefined || options.body === null ? undefined : textOf(options.body);
|
|
167
|
+
const r = JSON.parse(await hostFetch(String(url), String(options.method || 'GET').toUpperCase(), JSON.stringify(headers), body));
|
|
168
|
+
return freeze({
|
|
169
|
+
status: r.status,
|
|
170
|
+
ok: r.ok,
|
|
171
|
+
headers: r.headers,
|
|
172
|
+
text: async () => textOf(r.body),
|
|
173
|
+
json: async () => (typeof r.body === 'string' ? JSON.parse(r.body) : r.body),
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
constant('setTimeout', timer(false));
|
|
177
|
+
constant('setInterval', timer(true));
|
|
178
|
+
constant('clearTimeout', clear);
|
|
179
|
+
constant('clearInterval', clear);
|
|
156
180
|
})`;
|
|
157
|
-
|
|
158
|
-
/** Runs in a worker thread, so a busy QuickJS loop never blocks the server's event loop. */ const WORKER_SOURCE = `
|
|
181
|
+
/** Runs in a worker thread, so a busy code step never blocks the server's event loop. */ const WORKER_SOURCE = `
|
|
159
182
|
'use strict';
|
|
183
|
+
const vm = require('node:vm');
|
|
160
184
|
const { parentPort, workerData } = require('node:worker_threads');
|
|
161
185
|
const { createRequire } = require('node:module');
|
|
162
|
-
const { bases, prelude,
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
186
|
+
const { bases, prelude, sensitive, limits, keys } = workerData;
|
|
187
|
+
const requirers = bases.map((base) => createRequire(base));
|
|
188
|
+
const packages = new Map();
|
|
189
|
+
const fetches = new Map();
|
|
190
|
+
let fetchCalls = 0;
|
|
191
|
+
const TIMED_OUT = Symbol('timed out');
|
|
192
|
+
const HOST_GLOBALS = { Buffer, URL, URLSearchParams, TextEncoder, TextDecoder, AbortController, AbortSignal, atob, btoa, structuredClone };
|
|
193
|
+
|
|
194
|
+
// Code that drops a rejected promise must not take the worker down.
|
|
195
|
+
process.on('unhandledRejection', () => {});
|
|
196
|
+
|
|
197
|
+
function load(specifier) {
|
|
198
|
+
if (!packages.has(specifier)) {
|
|
199
|
+
let outcome = { error: new Error('The package "' + specifier + '" is not installed on the server.') };
|
|
200
|
+
for (const req of requirers) {
|
|
169
201
|
try {
|
|
170
|
-
|
|
202
|
+
outcome = { value: req(specifier) };
|
|
171
203
|
break;
|
|
172
204
|
} catch (error) {
|
|
173
|
-
|
|
205
|
+
if (!error || error.code !== 'MODULE_NOT_FOUND') {
|
|
206
|
+
outcome = { error };
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
174
209
|
}
|
|
175
210
|
}
|
|
176
|
-
|
|
177
|
-
quickJs.catch(() => { quickJs = undefined; });
|
|
211
|
+
packages.set(specifier, outcome);
|
|
178
212
|
}
|
|
179
|
-
|
|
213
|
+
const outcome = packages.get(specifier);
|
|
214
|
+
if (outcome.error) throw outcome.error;
|
|
215
|
+
return outcome.value;
|
|
180
216
|
}
|
|
181
217
|
|
|
182
|
-
const failure = (key, vars, detail, logs) => ({ failure: { key, vars, detail, logs } });
|
|
218
|
+
const failure = (key, vars, detail, logs, recycle) => ({ failure: { key, vars, detail, logs }, recycle });
|
|
183
219
|
|
|
184
|
-
function
|
|
185
|
-
let name = '';
|
|
186
|
-
let message = '';
|
|
220
|
+
function describe(error) {
|
|
187
221
|
try {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
} else {
|
|
193
|
-
message = String(dumped);
|
|
222
|
+
if (error && typeof error === 'object') {
|
|
223
|
+
const name = typeof error.name === 'string' ? error.name : '';
|
|
224
|
+
const message = typeof error.message === 'string' ? error.message : String(error);
|
|
225
|
+
return (name ? name + ': ' + message : message).slice(0, limits.logLineChars);
|
|
194
226
|
}
|
|
227
|
+
return String(error).slice(0, limits.logLineChars);
|
|
195
228
|
} catch (e) {
|
|
196
|
-
|
|
197
|
-
}
|
|
198
|
-
if (/out of memory/i.test(message)) {
|
|
199
|
-
const mb = limits.memoryBytes / (1024 * 1024);
|
|
200
|
-
return failure(keys.outOfMemory, { node: job.node, mb }, 'The code used more than ' + mb + ' MB of memory.', logs);
|
|
201
|
-
}
|
|
202
|
-
if ((name === 'InternalError' && message === 'interrupted') || Date.now() > deadline) {
|
|
203
|
-
return failure(keys.timeout, { node: job.node, ms: job.timeoutMs }, 'The code ran longer than ' + job.timeoutMs + ' ms.', logs);
|
|
229
|
+
return 'unknown error';
|
|
204
230
|
}
|
|
205
|
-
const detail = (name ? name + ': ' + message : message).slice(0, limits.logLineChars);
|
|
206
|
-
return failure(keys.threw, { node: job.node, detail }, detail, logs);
|
|
207
231
|
}
|
|
208
232
|
|
|
209
|
-
function run(
|
|
233
|
+
async function run(job) {
|
|
210
234
|
const logs = [];
|
|
211
235
|
const deadline = Date.now() + job.timeoutMs;
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
}
|
|
224
|
-
|
|
236
|
+
const remaining = () => Math.max(1, deadline - Date.now());
|
|
237
|
+
const timedOut = (recycle) => failure(keys.timeout, { node: job.node, ms: job.timeoutMs }, 'The code ran longer than ' + job.timeoutMs + ' ms.', logs, recycle);
|
|
238
|
+
const timers = new Map();
|
|
239
|
+
let nextTimer = 1;
|
|
240
|
+
let failAsync;
|
|
241
|
+
const asyncFailure = new Promise((resolve, reject) => { failAsync = reject; });
|
|
242
|
+
asyncFailure.catch(() => {});
|
|
243
|
+
|
|
244
|
+
const modules = job.modules;
|
|
245
|
+
// Loaded before the clock starts, so a package's first load never eats the code's time limit.
|
|
246
|
+
for (const specifier of Object.values(modules)) {
|
|
247
|
+
try { load(specifier); } catch (e) { /* reported when the code asks for it */ }
|
|
248
|
+
}
|
|
249
|
+
const hostRequire = (name) => {
|
|
250
|
+
if (!Object.hasOwn(modules, name)) {
|
|
251
|
+
const allowed = Object.keys(modules).join(', ') || 'none';
|
|
252
|
+
throw new Error('The package "' + name + '" is not allowed. Allowed: ' + allowed + '.');
|
|
253
|
+
}
|
|
254
|
+
return load(modules[name]);
|
|
255
|
+
};
|
|
256
|
+
const hostFetch = (url, method, headers, body) =>
|
|
257
|
+
new Promise((resolve, reject) => {
|
|
258
|
+
const call = ++fetchCalls;
|
|
259
|
+
fetches.set(call, { resolve, reject });
|
|
260
|
+
parentPort.postMessage({ type: 'fetch', id: job.id, call, request: { url, method, headers: JSON.parse(headers), body } });
|
|
261
|
+
});
|
|
262
|
+
const hostTimers = {
|
|
263
|
+
set(fn, ms, repeat) {
|
|
264
|
+
const id = nextTimer++;
|
|
265
|
+
const tick = () => {
|
|
266
|
+
if (!repeat) timers.delete(id);
|
|
267
|
+
try { fn(); } catch (error) { failAsync(error); }
|
|
268
|
+
};
|
|
269
|
+
timers.set(id, repeat ? setInterval(tick, Math.max(0, ms)) : setTimeout(tick, Math.max(0, ms)));
|
|
270
|
+
return id;
|
|
271
|
+
},
|
|
272
|
+
clear(id) {
|
|
273
|
+
const handle = timers.get(id);
|
|
274
|
+
if (handle === undefined) return;
|
|
275
|
+
clearTimeout(handle);
|
|
276
|
+
timers.delete(id);
|
|
277
|
+
},
|
|
225
278
|
};
|
|
279
|
+
const hostLog = (line) => {
|
|
280
|
+
if (logs.length < limits.logLines) logs.push(String(line));
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
const context = vm.createContext({ ...HOST_GLOBALS }, { name: 'flow-code', codeGeneration: { strings: true, wasm: false } });
|
|
284
|
+
let watch;
|
|
226
285
|
try {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
keep(serialized.error);
|
|
241
|
-
if (Date.now() > deadline) return toFailure(vm, serialized.error, job, deadline, logs);
|
|
286
|
+
vm.runInContext(prelude, context, { filename: 'flow-prelude.js' })(job.input, hostLog, sensitive, limits.logLineChars, hostRequire, hostFetch, hostTimers);
|
|
287
|
+
const pending = vm.runInContext("(async function () {\\n'use strict';\\n" + job.code + '\\n})()', context, { filename: 'flow-code.js', timeout: job.timeoutMs });
|
|
288
|
+
const clock = new Promise((resolve, reject) => { watch = setTimeout(() => reject(TIMED_OUT), remaining()); });
|
|
289
|
+
const value = await Promise.race([pending, clock, asyncFailure]);
|
|
290
|
+
clearTimeout(watch);
|
|
291
|
+
|
|
292
|
+
const slot = '__flow_result_' + job.id;
|
|
293
|
+
Object.defineProperty(context, slot, { value, configurable: true });
|
|
294
|
+
let json;
|
|
295
|
+
try {
|
|
296
|
+
json = vm.runInContext('(function (v) { const s = JSON.stringify(v); return s === undefined ? "null" : s; })(globalThis.' + slot + ')', context, { filename: 'flow-serialize.js', timeout: remaining() });
|
|
297
|
+
} catch (error) {
|
|
298
|
+
if (error && error.code === 'ERR_SCRIPT_EXECUTION_TIMEOUT') return timedOut(false);
|
|
242
299
|
return failure(keys.outputInvalid, { node: job.node }, 'The code returned a value that is not JSON.', logs);
|
|
243
300
|
}
|
|
244
|
-
const json = vm.getString(keep(serialized.value));
|
|
245
301
|
if (Buffer.byteLength(json, 'utf8') > limits.outputBytes) {
|
|
246
302
|
return failure(keys.outputTooLarge, { node: job.node, bytes: limits.outputBytes }, 'The code output is larger than ' + limits.outputBytes + ' bytes.', logs);
|
|
247
303
|
}
|
|
248
304
|
return { json, logs };
|
|
249
305
|
} catch (error) {
|
|
250
|
-
|
|
251
|
-
|
|
306
|
+
// Work the code left running after its deadline would carry on into the next run, so that worker is replaced.
|
|
307
|
+
if (error === TIMED_OUT) return timedOut(true);
|
|
308
|
+
if (error && error.code === 'ERR_SCRIPT_EXECUTION_TIMEOUT') return timedOut(false);
|
|
309
|
+
const detail = describe(error);
|
|
252
310
|
return failure(keys.threw, { node: job.node, detail }, detail, logs);
|
|
253
311
|
} finally {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
runtime.dispose();
|
|
312
|
+
clearTimeout(watch);
|
|
313
|
+
for (const handle of timers.values()) clearTimeout(handle);
|
|
314
|
+
timers.clear();
|
|
315
|
+
fetches.clear();
|
|
259
316
|
}
|
|
260
317
|
}
|
|
261
318
|
|
|
262
|
-
parentPort.on('message', async (
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
319
|
+
parentPort.on('message', async (message) => {
|
|
320
|
+
if (message.type === 'fetch') {
|
|
321
|
+
const pending = fetches.get(message.call);
|
|
322
|
+
if (!pending) return;
|
|
323
|
+
fetches.delete(message.call);
|
|
324
|
+
if (message.error !== undefined) pending.reject(new Error(message.error));
|
|
325
|
+
else pending.resolve(message.json);
|
|
269
326
|
return;
|
|
270
327
|
}
|
|
271
|
-
parentPort.postMessage({ id:
|
|
328
|
+
parentPort.postMessage({ type: 'done', id: message.id, ...(await run(message)) });
|
|
272
329
|
});
|
|
273
330
|
`;
|
|
274
|
-
const
|
|
331
|
+
const CODE_RUNNER_LIMITS = {
|
|
275
332
|
MAX_WORKERS: Math.max(1, Math.min(4, (0,__rspack_external_node_os_4f3c9d58.availableParallelism)() - 1)),
|
|
276
333
|
MAX_QUEUED_RUNS: 200,
|
|
277
|
-
/** Extra time the host waits past the
|
|
278
|
-
|
|
334
|
+
/** Extra time the host waits past the code's deadline before killing the worker thread. */ WATCHDOG_GRACE_MS: 1000,
|
|
335
|
+
/** How often the host checks a running worker's memory: buffers live outside the heap limit a worker can set itself. */ MEMORY_CHECK_MS: 20
|
|
279
336
|
};
|
|
280
|
-
/** Where the worker resolves
|
|
337
|
+
/** Where the worker resolves the configured packages from: this module's own file first, then the app. */ function resolutionBases() {
|
|
281
338
|
const bases = [];
|
|
282
339
|
const original = Error.prepareStackTrace;
|
|
283
340
|
try {
|
|
@@ -297,39 +354,51 @@ const CODE_SANDBOX_LIMITS = {
|
|
|
297
354
|
const WORKER_DATA = {
|
|
298
355
|
bases: resolutionBases(),
|
|
299
356
|
prelude: PRELUDE,
|
|
300
|
-
serialize: SERIALIZE,
|
|
301
357
|
sensitive: redact/* .SENSITIVE_KEY.source */.X6.source,
|
|
302
358
|
limits: {
|
|
303
|
-
memoryBytes: flow_types/* .FLOW_LIMITS.CODE_MEMORY_LIMIT_BYTES */.Or.CODE_MEMORY_LIMIT_BYTES,
|
|
304
|
-
stackBytes: flow_types/* .FLOW_LIMITS.CODE_STACK_SIZE_BYTES */.Or.CODE_STACK_SIZE_BYTES,
|
|
305
359
|
outputBytes: flow_types/* .FLOW_LIMITS.MAX_CODE_OUTPUT_BYTES */.Or.MAX_CODE_OUTPUT_BYTES,
|
|
306
360
|
logLines: flow_types/* .FLOW_LIMITS.MAX_CODE_LOG_LINES */.Or.MAX_CODE_LOG_LINES,
|
|
307
361
|
logLineChars: flow_types/* .FLOW_LIMITS.MAX_CODE_LOG_LINE_CHARS */.Or.MAX_CODE_LOG_LINE_CHARS
|
|
308
362
|
},
|
|
309
363
|
keys: {
|
|
310
|
-
unavailable: message_keys/* .FLOW_ERROR_MESSAGES.CODE_UNAVAILABLE */.zr.CODE_UNAVAILABLE,
|
|
311
364
|
threw: message_keys/* .FLOW_ERROR_MESSAGES.CODE_THREW */.zr.CODE_THREW,
|
|
312
365
|
timeout: message_keys/* .FLOW_ERROR_MESSAGES.CODE_TIMEOUT */.zr.CODE_TIMEOUT,
|
|
313
|
-
outOfMemory: message_keys/* .FLOW_ERROR_MESSAGES.CODE_OUT_OF_MEMORY */.zr.CODE_OUT_OF_MEMORY,
|
|
314
366
|
outputInvalid: message_keys/* .FLOW_ERROR_MESSAGES.CODE_OUTPUT_INVALID */.zr.CODE_OUTPUT_INVALID,
|
|
315
367
|
outputTooLarge: message_keys/* .FLOW_ERROR_MESSAGES.CODE_OUTPUT_TOO_LARGE */.zr.CODE_OUTPUT_TOO_LARGE
|
|
316
368
|
}
|
|
317
369
|
};
|
|
370
|
+
/** The worker gets none of the server's secrets: only the variables packages commonly read. */ function workerEnv() {
|
|
371
|
+
return Object.fromEntries([
|
|
372
|
+
'NODE_ENV',
|
|
373
|
+
'TZ',
|
|
374
|
+
'LANG'
|
|
375
|
+
].flatMap((name)=>process.env[name] ? [
|
|
376
|
+
[
|
|
377
|
+
name,
|
|
378
|
+
process.env[name]
|
|
379
|
+
]
|
|
380
|
+
] : []));
|
|
381
|
+
}
|
|
382
|
+
const MEMORY_MB = flow_types/* .FLOW_LIMITS.CODE_MEMORY_LIMIT_BYTES */.Or.CODE_MEMORY_LIMIT_BYTES / (1024 * 1024);
|
|
318
383
|
const timeoutError = (job)=>new flow_errors/* .FlowCodeError */.yL(message_keys/* .FLOW_ERROR_MESSAGES.CODE_TIMEOUT */.zr.CODE_TIMEOUT, {
|
|
319
384
|
node: job.node,
|
|
320
385
|
ms: job.timeoutMs
|
|
321
386
|
}, `The code ran longer than ${job.timeoutMs} ms.`);
|
|
387
|
+
const outOfMemoryError = (job)=>new flow_errors/* .FlowCodeError */.yL(message_keys/* .FLOW_ERROR_MESSAGES.CODE_OUT_OF_MEMORY */.zr.CODE_OUT_OF_MEMORY, {
|
|
388
|
+
node: job.node,
|
|
389
|
+
mb: MEMORY_MB
|
|
390
|
+
}, `The code used more than ${MEMORY_MB} MB of memory.`);
|
|
322
391
|
const unavailableError = (node, detail)=>new flow_errors/* .FlowCodeError */.yL(message_keys/* .FLOW_ERROR_MESSAGES.CODE_UNAVAILABLE */.zr.CODE_UNAVAILABLE, {
|
|
323
392
|
node
|
|
324
|
-
}, `The code
|
|
325
|
-
let
|
|
393
|
+
}, `The code runner could not run the code: ${detail}`);
|
|
394
|
+
let flow_code_runner_CodeRunnerPool = class CodeRunnerPool {
|
|
326
395
|
idle = [];
|
|
327
396
|
running = new Map();
|
|
328
397
|
queue = [];
|
|
329
398
|
workers = 0;
|
|
330
399
|
nextId = 1;
|
|
331
|
-
run(code, input, timeoutMs, node) {
|
|
332
|
-
if (this.queue.length >=
|
|
400
|
+
run(code, input, timeoutMs, node, options) {
|
|
401
|
+
if (this.queue.length >= CODE_RUNNER_LIMITS.MAX_QUEUED_RUNS) {
|
|
333
402
|
return Promise.reject(unavailableError(node, 'too many code steps are waiting to run.'));
|
|
334
403
|
}
|
|
335
404
|
return new Promise((resolve, reject)=>{
|
|
@@ -339,6 +408,8 @@ let flow_code_sandbox_CodeSandboxPool = class CodeSandboxPool {
|
|
|
339
408
|
input,
|
|
340
409
|
timeoutMs,
|
|
341
410
|
node,
|
|
411
|
+
modules: options.modules ?? {},
|
|
412
|
+
http: options.http,
|
|
342
413
|
resolve,
|
|
343
414
|
reject
|
|
344
415
|
});
|
|
@@ -346,7 +417,7 @@ let flow_code_sandbox_CodeSandboxPool = class CodeSandboxPool {
|
|
|
346
417
|
});
|
|
347
418
|
}
|
|
348
419
|
dispatch() {
|
|
349
|
-
while(this.queue.length && (this.idle.length || this.workers <
|
|
420
|
+
while(this.queue.length && (this.idle.length || this.workers < CODE_RUNNER_LIMITS.MAX_WORKERS)){
|
|
350
421
|
const job = this.queue.shift();
|
|
351
422
|
let worker;
|
|
352
423
|
try {
|
|
@@ -361,66 +432,96 @@ let flow_code_sandbox_CodeSandboxPool = class CodeSandboxPool {
|
|
|
361
432
|
start(worker, job) {
|
|
362
433
|
this.running.set(worker, job);
|
|
363
434
|
worker.ref();
|
|
364
|
-
job.watchdog = setTimeout(()=>
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
435
|
+
job.watchdog = setTimeout(()=>this.kill(worker, job, timeoutError(job)), job.timeoutMs + CODE_RUNNER_LIMITS.WATCHDOG_GRACE_MS);
|
|
436
|
+
job.memoryWatch = setInterval(()=>{
|
|
437
|
+
worker.getHeapStatistics().then((stats)=>{
|
|
438
|
+
const external = stats.external_memory ?? 0;
|
|
439
|
+
if (stats.used_heap_size + external > flow_types/* .FLOW_LIMITS.CODE_MEMORY_LIMIT_BYTES */.Or.CODE_MEMORY_LIMIT_BYTES) this.kill(worker, job, outOfMemoryError(job));
|
|
440
|
+
}, ()=>undefined);
|
|
441
|
+
}, CODE_RUNNER_LIMITS.MEMORY_CHECK_MS);
|
|
369
442
|
// oxlint-disable-next-line unicorn/require-post-message-target-origin -- a worker_threads Worker has no origin
|
|
370
443
|
worker.postMessage({
|
|
371
444
|
id: job.id,
|
|
372
445
|
code: job.code,
|
|
373
446
|
input: job.input,
|
|
374
447
|
timeoutMs: job.timeoutMs,
|
|
375
|
-
node: job.node
|
|
448
|
+
node: job.node,
|
|
449
|
+
modules: job.modules
|
|
376
450
|
});
|
|
377
451
|
}
|
|
452
|
+
kill(worker, job, error) {
|
|
453
|
+
if (this.running.get(worker) !== job) return;
|
|
454
|
+
this.take(worker);
|
|
455
|
+
job.reject(error);
|
|
456
|
+
void worker.terminate();
|
|
457
|
+
}
|
|
378
458
|
spawn() {
|
|
379
459
|
const worker = new __rspack_external_node_worker_threads_28ca7740.Worker(WORKER_SOURCE, {
|
|
380
460
|
eval: true,
|
|
381
461
|
workerData: WORKER_DATA,
|
|
462
|
+
env: workerEnv(),
|
|
382
463
|
resourceLimits: {
|
|
383
|
-
maxOldGenerationSizeMb:
|
|
464
|
+
maxOldGenerationSizeMb: MEMORY_MB
|
|
384
465
|
}
|
|
385
466
|
});
|
|
386
467
|
this.workers++;
|
|
387
|
-
worker.on('message', (
|
|
468
|
+
worker.on('message', (message)=>message.type === 'fetch' ? this.fetch(worker, message) : this.settle(worker, message));
|
|
388
469
|
worker.on('error', (error)=>{
|
|
389
470
|
const job = this.take(worker);
|
|
390
471
|
if (!job) return;
|
|
391
|
-
|
|
392
|
-
const mb = flow_types/* .FLOW_LIMITS.CODE_MEMORY_LIMIT_BYTES */.Or.CODE_MEMORY_LIMIT_BYTES / (1024 * 1024);
|
|
393
|
-
job.reject(new flow_errors/* .FlowCodeError */.yL(message_keys/* .FLOW_ERROR_MESSAGES.CODE_OUT_OF_MEMORY */.zr.CODE_OUT_OF_MEMORY, {
|
|
394
|
-
node: job.node,
|
|
395
|
-
mb
|
|
396
|
-
}, `The code used more than ${mb} MB of memory.`));
|
|
397
|
-
} else {
|
|
398
|
-
job.reject(unavailableError(job.node, error.message));
|
|
399
|
-
}
|
|
472
|
+
job.reject(error.code === 'ERR_WORKER_OUT_OF_MEMORY' ? outOfMemoryError(job) : unavailableError(job.node, error.message));
|
|
400
473
|
});
|
|
401
474
|
worker.on('exit', ()=>{
|
|
402
475
|
this.workers--;
|
|
403
476
|
const index = this.idle.indexOf(worker);
|
|
404
477
|
if (index >= 0) this.idle.splice(index, 1);
|
|
405
478
|
const job = this.take(worker);
|
|
406
|
-
job?.reject(unavailableError(job.node, 'the
|
|
479
|
+
job?.reject(unavailableError(job.node, 'the code worker stopped.'));
|
|
407
480
|
this.dispatch();
|
|
408
481
|
});
|
|
409
482
|
return worker;
|
|
410
483
|
}
|
|
484
|
+
fetch(worker, message) {
|
|
485
|
+
const job = this.running.get(worker);
|
|
486
|
+
if (!job || job.id !== message.id) return;
|
|
487
|
+
const reply = (answer)=>{
|
|
488
|
+
// oxlint-disable-next-line unicorn/require-post-message-target-origin -- a worker_threads Worker has no origin
|
|
489
|
+
if (this.running.get(worker) === job) worker.postMessage({
|
|
490
|
+
type: 'fetch',
|
|
491
|
+
call: message.call,
|
|
492
|
+
...answer
|
|
493
|
+
});
|
|
494
|
+
};
|
|
495
|
+
if (!job.http) {
|
|
496
|
+
reply({
|
|
497
|
+
error: 'fetch is not available here.'
|
|
498
|
+
});
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
job.http(message.request).then((response)=>reply({
|
|
502
|
+
json: JSON.stringify(response)
|
|
503
|
+
}), (error)=>reply({
|
|
504
|
+
error: error instanceof Error ? error.message : String(error)
|
|
505
|
+
}));
|
|
506
|
+
}
|
|
411
507
|
take(worker) {
|
|
412
508
|
const job = this.running.get(worker);
|
|
413
509
|
if (!job) return undefined;
|
|
414
510
|
this.running.delete(worker);
|
|
415
511
|
clearTimeout(job.watchdog);
|
|
512
|
+
clearInterval(job.memoryWatch);
|
|
416
513
|
return job;
|
|
417
514
|
}
|
|
418
515
|
settle(worker, reply) {
|
|
419
516
|
const job = this.running.get(worker);
|
|
420
517
|
if (!job || job.id !== reply.id) return;
|
|
421
518
|
this.take(worker);
|
|
422
|
-
|
|
423
|
-
|
|
519
|
+
if (reply.recycle) {
|
|
520
|
+
void worker.terminate();
|
|
521
|
+
} else {
|
|
522
|
+
worker.unref();
|
|
523
|
+
this.idle.push(worker);
|
|
524
|
+
}
|
|
424
525
|
if (reply.failure) {
|
|
425
526
|
const { key, vars, detail, logs } = reply.failure;
|
|
426
527
|
job.reject(new flow_errors/* .FlowCodeError */.yL(key, vars, detail, logs));
|
|
@@ -435,14 +536,16 @@ let flow_code_sandbox_CodeSandboxPool = class CodeSandboxPool {
|
|
|
435
536
|
};
|
|
436
537
|
let pool;
|
|
437
538
|
/**
|
|
438
|
-
* Runs author-written JavaScript
|
|
439
|
-
*
|
|
440
|
-
*
|
|
441
|
-
*
|
|
442
|
-
*
|
|
443
|
-
*
|
|
444
|
-
*
|
|
445
|
-
|
|
539
|
+
* Runs author-written JavaScript on the server, in Node, on a worker thread (a small pool, so a
|
|
540
|
+
* busy code step never blocks the event loop). Each run gets a fresh `vm` context holding a frozen
|
|
541
|
+
* JSON copy of its input as `ctx`, a captured `console`, the permission helpers, `require` for the
|
|
542
|
+
* packages the app allows, a `fetch` the host carries out with the HTTP step's rules, timers, and
|
|
543
|
+
* the common web globals. The code body is async and must return JSON-serializable data.
|
|
544
|
+
* Limits: the time limit (synchronous code is interrupted; a run still busy after it plus a grace
|
|
545
|
+
* period has its worker killed), the worker's memory (heap plus buffers, checked by the host) and
|
|
546
|
+
* the output size. The worker sees none of the server's env variables. This is not a security
|
|
547
|
+
* boundary: a package's objects lead back to Node itself, so code steps are for trusted authors.
|
|
548
|
+
*/ async function runFlowCode(code, ctx, timeoutMs, node, options = {}) {
|
|
446
549
|
let input;
|
|
447
550
|
try {
|
|
448
551
|
input = JSON.stringify(ctx) ?? '{}';
|
|
@@ -452,12 +555,14 @@ let pool;
|
|
|
452
555
|
detail: error.message
|
|
453
556
|
}, error.message);
|
|
454
557
|
}
|
|
455
|
-
pool ??= new
|
|
456
|
-
return pool.run(code, input, timeoutMs, node);
|
|
558
|
+
pool ??= new flow_code_runner_CodeRunnerPool();
|
|
559
|
+
return pool.run(code, input, timeoutMs, node, options);
|
|
457
560
|
}
|
|
458
561
|
|
|
459
562
|
// EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/flow-engine/flow-graph.validator.ts
|
|
460
563
|
var flow_graph_validator = __webpack_require__(3186);
|
|
564
|
+
// EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/flow-engine/flow-input.validator.ts
|
|
565
|
+
var flow_input_validator = __webpack_require__(6390);
|
|
461
566
|
// EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/rule-engine/rule-values.ts
|
|
462
567
|
var rule_values = __webpack_require__(6622);
|
|
463
568
|
;// CONCATENATED MODULE: ./projects/nestjs-entity-builder/src/flow-engine/flow-node-handlers.ts
|
|
@@ -469,6 +574,7 @@ var rule_values = __webpack_require__(6622);
|
|
|
469
574
|
|
|
470
575
|
|
|
471
576
|
|
|
577
|
+
|
|
472
578
|
/** With `env`, `lookup` expressions read through the flow (its transaction). */ function evaluationContext(state, env) {
|
|
473
579
|
const base = {
|
|
474
580
|
input: state.input,
|
|
@@ -678,7 +784,7 @@ async function isGrantedScope(userId, companyId, branchId, env) {
|
|
|
678
784
|
]);
|
|
679
785
|
}
|
|
680
786
|
const USES_PERMISSIONS = /\b(permissions|hasPermission|hasAnyPermission|hasAllPermissions)\b/;
|
|
681
|
-
/** The caller as a code step sees it; permission codes (current branch) are fetched only for code that reads them. */ async function codeUser(code, state) {
|
|
787
|
+
/** The caller as a code step sees it; permission codes (current branch) are fetched only for code that reads them. */ async function codeUser(code, env, state) {
|
|
682
788
|
const user = state.user;
|
|
683
789
|
if (!user) return null;
|
|
684
790
|
const base = {
|
|
@@ -689,12 +795,27 @@ const USES_PERMISSIONS = /\b(permissions|hasPermission|hasAnyPermission|hasAllPe
|
|
|
689
795
|
branchId: user.branchId ?? null
|
|
690
796
|
};
|
|
691
797
|
if (!USES_PERMISSIONS.test(code)) return base;
|
|
798
|
+
const permissions = state.permissions ?? env.ruleEngine.createPermissionResolver(user);
|
|
692
799
|
return {
|
|
693
800
|
...base,
|
|
694
|
-
permissions: await
|
|
801
|
+
permissions: await permissions.granted(undefined)
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
/** The code's `fetch`, held to the rules of an HTTP request step: skipped in a test run, counted, private addresses refused. */ function codeHttp(env, state) {
|
|
805
|
+
return (request)=>{
|
|
806
|
+
if (state.mode === 'dry_run') return Promise.reject(new Error('fetch does not run in a test run, like an HTTP request step.'));
|
|
807
|
+
if (++state.httpCalls > flow_types/* .FLOW_LIMITS.MAX_HTTP_CALLS */.Or.MAX_HTTP_CALLS) return Promise.reject(new flow_errors/* .FlowLimitError */.IM(message_keys/* .FLOW_ERROR_MESSAGES.TOO_MANY_HTTP_CALLS */.zr.TOO_MANY_HTTP_CALLS, {
|
|
808
|
+
max: flow_types/* .FLOW_LIMITS.MAX_HTTP_CALLS */.Or.MAX_HTTP_CALLS
|
|
809
|
+
}));
|
|
810
|
+
const timeoutMs = Math.max(1, Math.min(flow_types/* .FLOW_LIMITS.MAX_HTTP_TIMEOUT_MS */.Or.MAX_HTTP_TIMEOUT_MS, state.deadline - Date.now()));
|
|
811
|
+
return env.http({
|
|
812
|
+
...request,
|
|
813
|
+
timeoutMs,
|
|
814
|
+
allowPrivate: env.allowPrivateHttp
|
|
815
|
+
});
|
|
695
816
|
};
|
|
696
817
|
}
|
|
697
|
-
async function runCode(node, cfg, state) {
|
|
818
|
+
async function runCode(node, cfg, env, state) {
|
|
698
819
|
const remaining = state.deadline - Date.now();
|
|
699
820
|
if (remaining <= 0) throw new flow_errors/* .FlowLimitError */.IM(message_keys/* .FLOW_ERROR_MESSAGES.OUT_OF_TIME */.zr.OUT_OF_TIME);
|
|
700
821
|
const timeoutMs = Math.min(cfg.timeoutMs ?? flow_types/* .FLOW_LIMITS.DEFAULT_CODE_TIMEOUT_MS */.Or.DEFAULT_CODE_TIMEOUT_MS, flow_types/* .FLOW_LIMITS.MAX_CODE_TIMEOUT_MS */.Or.MAX_CODE_TIMEOUT_MS, remaining);
|
|
@@ -703,9 +824,12 @@ async function runCode(node, cfg, state) {
|
|
|
703
824
|
vars: state.vars,
|
|
704
825
|
context: state.context,
|
|
705
826
|
loop: state.loop ?? null,
|
|
706
|
-
user: await codeUser(cfg.code, state)
|
|
827
|
+
user: await codeUser(cfg.code, env, state)
|
|
707
828
|
};
|
|
708
|
-
const { output, logs } = await
|
|
829
|
+
const { output, logs } = await runFlowCode(cfg.code, ctx, timeoutMs, (0,flow_types/* .nodeLabel */.dA)(node), {
|
|
830
|
+
modules: env.codeModules,
|
|
831
|
+
http: codeHttp(env, state)
|
|
832
|
+
});
|
|
709
833
|
return {
|
|
710
834
|
output,
|
|
711
835
|
...logs.length ? {
|
|
@@ -723,10 +847,16 @@ function writeOptions(state) {
|
|
|
723
847
|
return state.qr ? {
|
|
724
848
|
queryRunner: state.qr,
|
|
725
849
|
publishEvents: false
|
|
726
|
-
} : {
|
|
850
|
+
} : {
|
|
851
|
+
statementTimeoutMs: state.deadline - Date.now()
|
|
852
|
+
};
|
|
727
853
|
}
|
|
728
|
-
/** A get_all request's own query: what a `fromRequest`
|
|
729
|
-
|
|
854
|
+
/** A get_all request's own query: what a `fromRequest` step reads, checked by the get_all rules whatever the Request step's type. A list body has none. */ function requestQuery(state) {
|
|
855
|
+
const { query, errors } = (0,flow_input_validator/* .readRequestQuery */.M2)(state.input);
|
|
856
|
+
if (errors.length) throw new flow_errors/* .FlowHttpError */.hk(400, 'Validation failed', errors, {
|
|
857
|
+
messageKey: message_keys/* .FLOW_MESSAGES.INPUT_INVALID */.wu.INPUT_INVALID
|
|
858
|
+
});
|
|
859
|
+
return query;
|
|
730
860
|
}
|
|
731
861
|
/** A request's filter the way the API controller reads it: a value matches as equal, a list as one of its items - never an operator the caller sent. */ function requestFilter(filter) {
|
|
732
862
|
return Object.fromEntries(Object.entries(filter ?? {}).map(([key, value])=>[
|
|
@@ -759,7 +889,7 @@ async function runQuery(node, cfg, env, state) {
|
|
|
759
889
|
[cfg.sort.field]: cfg.sort.direction === 'DESC' ? 'DESC' : 'ASC'
|
|
760
890
|
} : request.sort;
|
|
761
891
|
const page = request.pagination?.currentPage ?? 0;
|
|
762
|
-
const pageSize = Math.min(request.pagination?.pageSize ?? cfg.limit ??
|
|
892
|
+
const pageSize = Math.min(request.pagination?.pageSize ?? cfg.limit ?? flow_types/* .FLOW_LIMITS.DEFAULT_QUERY_LIMIT */.Or.DEFAULT_QUERY_LIMIT, flow_types/* .FLOW_LIMITS.MAX_QUERY_LIMIT */.Or.MAX_QUERY_LIMIT);
|
|
763
893
|
const withDeleted = request.withDeleted === true ? {
|
|
764
894
|
withDeleted: true
|
|
765
895
|
} : {};
|
|
@@ -829,7 +959,8 @@ async function runQuery(node, cfg, env, state) {
|
|
|
829
959
|
}
|
|
830
960
|
};
|
|
831
961
|
}
|
|
832
|
-
|
|
962
|
+
// Only the defaults of the list meta: a request whose paging cannot be read falls back to them.
|
|
963
|
+
const pagination = (0,flow_input_validator/* .readRequestQuery */.M2)(state.input).query.pagination;
|
|
833
964
|
const total = await count('total', data.length);
|
|
834
965
|
const page = await count('page', pagination?.currentPage ?? 0);
|
|
835
966
|
const pageSize = await count('pageSize', pagination?.pageSize ?? data.length);
|
|
@@ -915,6 +1046,20 @@ async function updateRecord(entityCode, service, values, state) {
|
|
|
915
1046
|
});
|
|
916
1047
|
return saved;
|
|
917
1048
|
}
|
|
1049
|
+
/** An update by filter's match: the row was just read on this transaction, so the service need not read it again. */ async function updateMatched(entityCode, service, row, values, state) {
|
|
1050
|
+
if (!service.updateLoaded) return updateRecord(entityCode, service, {
|
|
1051
|
+
...values,
|
|
1052
|
+
id: row.id
|
|
1053
|
+
}, state);
|
|
1054
|
+
countWrite(state);
|
|
1055
|
+
const saved = await service.updateLoaded(row, values, state.user, writeOptions(state));
|
|
1056
|
+
if (state.qr) state.pendingEvents.push({
|
|
1057
|
+
entityCode,
|
|
1058
|
+
action: 'updated',
|
|
1059
|
+
row: saved
|
|
1060
|
+
});
|
|
1061
|
+
return saved;
|
|
1062
|
+
}
|
|
918
1063
|
/** @returns false when the record does not exist and `skip` allows that. */ async function deleteRecord(entityCode, service, id, skipMissing, state) {
|
|
919
1064
|
countWrite(state);
|
|
920
1065
|
let action;
|
|
@@ -944,11 +1089,12 @@ async function updateRecord(entityCode, service, values, state) {
|
|
|
944
1089
|
});
|
|
945
1090
|
}
|
|
946
1091
|
const limit = Math.min(cfg.limit ?? flow_types/* .FLOW_LIMITS.DEFAULT_WRITE_FILTER_LIMIT */.Or.DEFAULT_WRITE_FILTER_LIMIT, flow_types/* .FLOW_LIMITS.MAX_WRITE_ITEMS */.Or.MAX_WRITE_ITEMS);
|
|
1092
|
+
// One row past the limit: getAll then counts only when there are too many, to name how many.
|
|
947
1093
|
const { data, total } = await service.getAll(undefined, {
|
|
948
1094
|
filter,
|
|
949
1095
|
pagination: {
|
|
950
1096
|
currentPage: 0,
|
|
951
|
-
pageSize: limit
|
|
1097
|
+
pageSize: limit + 1
|
|
952
1098
|
}
|
|
953
1099
|
}, readOptions(state));
|
|
954
1100
|
if (total > limit) throw new flow_errors/* .FlowLimitError */.IM(message_keys/* .FLOW_ERROR_MESSAGES.TOO_MANY_MATCHES */.zr.TOO_MANY_MATCHES, {
|
|
@@ -984,6 +1130,9 @@ async function insertChildren(node, child, parentId, env, state) {
|
|
|
984
1130
|
if (writeTarget(cfg) === 'one') return {
|
|
985
1131
|
output: children.length ? await env.atomic(state, insertOne) : await insertOne()
|
|
986
1132
|
};
|
|
1133
|
+
if (!children.length && insertsInBatch(service, cfg)) return {
|
|
1134
|
+
output: await env.atomic(state, ()=>insertBatch(node, cfg, service, env, state))
|
|
1135
|
+
};
|
|
987
1136
|
return {
|
|
988
1137
|
output: await env.atomic(state, async ()=>{
|
|
989
1138
|
const items = await eachItem(state, await resolveList(node, cfg.items, flow_types/* .FLOW_LIMITS.MAX_WRITE_ITEMS */.Or.MAX_WRITE_ITEMS, env, state), insertOne);
|
|
@@ -994,6 +1143,54 @@ async function insertChildren(node, child, parentId, env, state) {
|
|
|
994
1143
|
})
|
|
995
1144
|
};
|
|
996
1145
|
}
|
|
1146
|
+
/** A field mapping that reads records (a lookup) must see the rows inserted before it, so it keeps the row-by-row insert. */ function readsRecords(value) {
|
|
1147
|
+
if (Array.isArray(value)) return value.some(readsRecords);
|
|
1148
|
+
if (!value || typeof value !== 'object') return false;
|
|
1149
|
+
return value.type === 'lookup' || Object.values(value).some(readsRecords);
|
|
1150
|
+
}
|
|
1151
|
+
function insertsInBatch(service, cfg) {
|
|
1152
|
+
return service.batchInsertable === true && !!service.prepareInsert && !!service.firstMissingRelation && !!service.insertValidated && !readsRecords(cfg.fields);
|
|
1153
|
+
}
|
|
1154
|
+
/**
|
|
1155
|
+
* `many` in one statement per chunk, answering exactly as row by row would: each row's values are mapped, counted
|
|
1156
|
+
* and checked in order, and the first row that would have failed (a mapping, a check or a missing related record)
|
|
1157
|
+
* is the failure reported, with the write count it had reached.
|
|
1158
|
+
*/ async function insertBatch(node, cfg, service, env, state) {
|
|
1159
|
+
const items = await resolveList(node, cfg.items, flow_types/* .FLOW_LIMITS.MAX_WRITE_ITEMS */.Or.MAX_WRITE_ITEMS, env, state);
|
|
1160
|
+
const writesBefore = state.writes;
|
|
1161
|
+
const rows = [];
|
|
1162
|
+
let failure;
|
|
1163
|
+
try {
|
|
1164
|
+
await eachItem(state, items, async ()=>{
|
|
1165
|
+
const values = await mapFieldValues(cfg.fields, env, state);
|
|
1166
|
+
countWrite(state);
|
|
1167
|
+
rows.push(service.prepareInsert(values));
|
|
1168
|
+
});
|
|
1169
|
+
} catch (error) {
|
|
1170
|
+
failure = {
|
|
1171
|
+
error
|
|
1172
|
+
};
|
|
1173
|
+
}
|
|
1174
|
+
const missing = await service.firstMissingRelation(rows, state.qr);
|
|
1175
|
+
if (missing) {
|
|
1176
|
+
state.writes = writesBefore + missing.index + 1;
|
|
1177
|
+
throw missing.error;
|
|
1178
|
+
}
|
|
1179
|
+
if (failure) throw failure.error;
|
|
1180
|
+
const saved = await service.insertValidated(rows, state.user, {
|
|
1181
|
+
queryRunner: state.qr,
|
|
1182
|
+
publishEvents: false
|
|
1183
|
+
});
|
|
1184
|
+
for (const row of saved)state.pendingEvents.push({
|
|
1185
|
+
entityCode: cfg.entityCode,
|
|
1186
|
+
action: 'created',
|
|
1187
|
+
row
|
|
1188
|
+
});
|
|
1189
|
+
return {
|
|
1190
|
+
items: saved,
|
|
1191
|
+
count: saved.length
|
|
1192
|
+
};
|
|
1193
|
+
}
|
|
997
1194
|
/** Updates the record `id` names; one that does not exist (or an empty id, for insert) is handled as `onNotFound` says. */ async function updateOrCreate(cfg, service, fallbackId, env, state) {
|
|
998
1195
|
const onNotFound = cfg.onNotFound ?? 'error';
|
|
999
1196
|
const raw = cfg.id === undefined ? fallbackId : await env.ruleEngine.resolveExpression(cfg.id, evaluationContext(state, env));
|
|
@@ -1031,10 +1228,7 @@ async function insertChildren(node, child, parentId, env, state) {
|
|
|
1031
1228
|
return {
|
|
1032
1229
|
output: await env.atomic(state, async ()=>{
|
|
1033
1230
|
const outcomes = target === 'filter' ? await eachItem(state, await matchingRecords(node, cfg, service, env, state), async (row)=>({
|
|
1034
|
-
row: await
|
|
1035
|
-
...await mapFieldValues(cfg.fields, env, state),
|
|
1036
|
-
id: row.id
|
|
1037
|
-
}, state),
|
|
1231
|
+
row: await updateMatched(cfg.entityCode, service, row, await mapFieldValues(cfg.fields, env, state), state),
|
|
1038
1232
|
result: 'updated'
|
|
1039
1233
|
})) : await eachItem(state, await resolveList(node, cfg.items, flow_types/* .FLOW_LIMITS.MAX_WRITE_ITEMS */.Or.MAX_WRITE_ITEMS, env, state), (item)=>updateOrCreate(cfg, service, itemId(item), env, state));
|
|
1040
1234
|
const items = outcomes.flatMap((o)=>o.row ? [
|
|
@@ -1465,7 +1659,7 @@ const nobody = (extra = {})=>({
|
|
|
1465
1659
|
case 'validate':
|
|
1466
1660
|
return runValidate(c, env, state);
|
|
1467
1661
|
case 'code':
|
|
1468
|
-
return runCode(node, c, state);
|
|
1662
|
+
return runCode(node, c, env, state);
|
|
1469
1663
|
case 'permission_check':
|
|
1470
1664
|
return runPermissionCheck(c, env, state);
|
|
1471
1665
|
case 'company_branch_check':
|
|
@@ -2650,6 +2844,12 @@ function conditionProblem(node, options) {
|
|
|
2650
2844
|
validateInputFields(field.fields, itemType === 'object' ? `${name}[].` : `${name}.`, depth + 1, err);
|
|
2651
2845
|
}
|
|
2652
2846
|
}
|
|
2847
|
+
const REQUIRE_CALL = /\brequire\s*\(\s*(['"`])([^'"`\n]+)\1\s*\)/g;
|
|
2848
|
+
/** The package names a code step asks for with a literal `require('name')`. */ function requiredPackages(code) {
|
|
2849
|
+
return [
|
|
2850
|
+
...code.matchAll(REQUIRE_CALL)
|
|
2851
|
+
].map((match)=>match[2]);
|
|
2852
|
+
}
|
|
2653
2853
|
function validateNodeConfig(node, flow, context, err, warn) {
|
|
2654
2854
|
const at = {
|
|
2655
2855
|
node: (0,_flow_types_js__rspack_import_5/* .nodeLabel */.dA)(node)
|
|
@@ -2768,6 +2968,8 @@ function validateNodeConfig(node, flow, context, err, warn) {
|
|
|
2768
2968
|
const serviceRunning = (service, unavailable)=>{
|
|
2769
2969
|
if (context.messageServices && !context.messageServices[service]) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(unavailable, at));
|
|
2770
2970
|
};
|
|
2971
|
+
// A failure carried past a step with no `error` port would follow `out`, which such a step does not have (or, for a check, is never carried past).
|
|
2972
|
+
if (node.onError === 'continue' && node.type !== 'commit' && (node.type === 'switch' || !(0,_flow_types_js__rspack_import_5/* .portsOf */.a5)(node).includes('error'))) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_CONTINUE_UNSUPPORTED */.qj.NODE_CONTINUE_UNSUPPORTED, at));
|
|
2771
2973
|
switch(node.type){
|
|
2772
2974
|
case 'trigger':
|
|
2773
2975
|
{
|
|
@@ -3093,6 +3295,16 @@ function validateNodeConfig(node, flow, context, err, warn) {
|
|
|
3093
3295
|
max: _flow_types_js__rspack_import_5/* .FLOW_LIMITS.MAX_CODE_TIMEOUT_MS */.Or.MAX_CODE_TIMEOUT_MS
|
|
3094
3296
|
}));
|
|
3095
3297
|
}
|
|
3298
|
+
if (context.codeModules && typeof c.code === 'string') {
|
|
3299
|
+
const allowed = context.codeModules;
|
|
3300
|
+
for (const name of new Set(requiredPackages(c.code))){
|
|
3301
|
+
if (!allowed.includes(name)) err((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_7/* .FLOW_VALIDATION_MESSAGES.NODE_CODE_PACKAGE_NOT_ALLOWED */.qj.NODE_CODE_PACKAGE_NOT_ALLOWED, {
|
|
3302
|
+
...at,
|
|
3303
|
+
package: name,
|
|
3304
|
+
allowed: allowed.join(', ') || '-'
|
|
3305
|
+
}));
|
|
3306
|
+
}
|
|
3307
|
+
}
|
|
3096
3308
|
break;
|
|
3097
3309
|
}
|
|
3098
3310
|
}
|
|
@@ -3625,29 +3837,116 @@ function keepRest(source, values) {
|
|
|
3625
3837
|
}
|
|
3626
3838
|
function contractErrors(values) {
|
|
3627
3839
|
const errors = [];
|
|
3628
|
-
|
|
3629
|
-
if (
|
|
3630
|
-
|
|
3631
|
-
|
|
3840
|
+
if (isPlainObject(values['sort'])) errors.push(...sortErrors(values['sort']));
|
|
3841
|
+
if (Array.isArray(values['select'])) errors.push(...selectErrors(values['select']));
|
|
3842
|
+
return errors;
|
|
3843
|
+
}
|
|
3844
|
+
function sortErrors(sort) {
|
|
3845
|
+
return Object.entries(sort).flatMap(([field, direction])=>!FIELD_NAME.test(field) || direction !== 'ASC' && direction !== 'DESC' ? [
|
|
3846
|
+
{
|
|
3632
3847
|
field: `sort.${field}`,
|
|
3633
3848
|
...(0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_4/* .FLOW_MESSAGES.INPUT_SORT_INVALID */.wu.INPUT_SORT_INVALID, {
|
|
3634
3849
|
field
|
|
3635
3850
|
})
|
|
3636
|
-
}
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
if (typeof field !== 'string' || !FIELD_NAME.test(field)) errors.push({
|
|
3851
|
+
}
|
|
3852
|
+
] : []);
|
|
3853
|
+
}
|
|
3854
|
+
function selectErrors(select) {
|
|
3855
|
+
return select.flatMap((field, i)=>typeof field !== 'string' || !FIELD_NAME.test(field) ? [
|
|
3856
|
+
{
|
|
3643
3857
|
field: `select[${i}]`,
|
|
3644
3858
|
...(0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_4/* .FLOW_MESSAGES.INPUT_SELECT_INVALID */.wu.INPUT_SELECT_INVALID, {
|
|
3645
3859
|
field: String(field)
|
|
3646
3860
|
})
|
|
3861
|
+
}
|
|
3862
|
+
] : []);
|
|
3863
|
+
}
|
|
3864
|
+
/** A whole number, from a number or numeric text; null for anything else. */ function wholeNumber(value) {
|
|
3865
|
+
const n = typeof value === 'string' && value.trim() !== '' ? Number(value) : value;
|
|
3866
|
+
return typeof n === 'number' && Number.isInteger(n) ? n : null;
|
|
3867
|
+
}
|
|
3868
|
+
/**
|
|
3869
|
+
* Reads `filter`, `sort`, `pagination`, `select` and `withDeleted` from a request body by the get_all
|
|
3870
|
+
* contract's rules, whatever the Request step's type: `currentPage` a whole number of 0 or more (left
|
|
3871
|
+
* out: 0), `pageSize` one from 1 to MAX_QUERY_LIMIT (left out: the step's own limit), numeric text
|
|
3872
|
+
* accepted; `sort` ASC / DESC per plain field name; `select` plain field names. A list body has none.
|
|
3873
|
+
*/ function readRequestQuery(body) {
|
|
3874
|
+
const query = {};
|
|
3875
|
+
const errors = [];
|
|
3876
|
+
if (!isPlainObject(body)) return {
|
|
3877
|
+
query,
|
|
3878
|
+
errors
|
|
3879
|
+
};
|
|
3880
|
+
const source = body;
|
|
3881
|
+
const present = (key)=>source[key] !== undefined && source[key] !== null;
|
|
3882
|
+
if (isPlainObject(source['filter'])) query.filter = source['filter'];
|
|
3883
|
+
if (present('sort')) {
|
|
3884
|
+
const sort = source['sort'];
|
|
3885
|
+
const problems = isPlainObject(sort) ? sortErrors(sort) : [
|
|
3886
|
+
{
|
|
3887
|
+
field: 'sort',
|
|
3888
|
+
...(0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_4/* .FLOW_MESSAGES.INPUT_SORT_INVALID */.wu.INPUT_SORT_INVALID, {
|
|
3889
|
+
field: 'sort'
|
|
3890
|
+
})
|
|
3891
|
+
}
|
|
3892
|
+
];
|
|
3893
|
+
if (problems.length) errors.push(...problems);
|
|
3894
|
+
else query.sort = sort;
|
|
3895
|
+
}
|
|
3896
|
+
if (present('pagination')) {
|
|
3897
|
+
const pagination = source['pagination'];
|
|
3898
|
+
if (!isPlainObject(pagination)) {
|
|
3899
|
+
errors.push({
|
|
3900
|
+
field: 'pagination',
|
|
3901
|
+
...(0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_4/* .FIELD_VALIDATION_MESSAGES.MUST_BE_OBJECT */.KE.MUST_BE_OBJECT, {
|
|
3902
|
+
label: 'pagination'
|
|
3903
|
+
})
|
|
3647
3904
|
});
|
|
3648
|
-
}
|
|
3905
|
+
} else {
|
|
3906
|
+
const { currentPage: rawPage, pageSize: rawSize } = pagination;
|
|
3907
|
+
const currentPage = rawPage === undefined || rawPage === null ? 0 : wholeNumber(rawPage);
|
|
3908
|
+
const pageSize = rawSize === undefined || rawSize === null ? undefined : wholeNumber(rawSize);
|
|
3909
|
+
if (currentPage === null || currentPage < 0) errors.push({
|
|
3910
|
+
field: 'pagination.currentPage',
|
|
3911
|
+
...(0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_4/* .FLOW_MESSAGES.INPUT_PAGE_INVALID */.wu.INPUT_PAGE_INVALID, {
|
|
3912
|
+
field: 'pagination.currentPage'
|
|
3913
|
+
})
|
|
3914
|
+
});
|
|
3915
|
+
if (pageSize === null || pageSize !== undefined && (pageSize < 1 || pageSize > _flow_types_js__rspack_import_2/* .FLOW_LIMITS.MAX_QUERY_LIMIT */.Or.MAX_QUERY_LIMIT)) {
|
|
3916
|
+
errors.push({
|
|
3917
|
+
field: 'pagination.pageSize',
|
|
3918
|
+
...(0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_4/* .FLOW_MESSAGES.INPUT_PAGE_SIZE_INVALID */.wu.INPUT_PAGE_SIZE_INVALID, {
|
|
3919
|
+
field: 'pagination.pageSize',
|
|
3920
|
+
max: _flow_types_js__rspack_import_2/* .FLOW_LIMITS.MAX_QUERY_LIMIT */.Or.MAX_QUERY_LIMIT
|
|
3921
|
+
})
|
|
3922
|
+
});
|
|
3923
|
+
}
|
|
3924
|
+
if (currentPage !== null && currentPage >= 0 && pageSize !== null) query.pagination = {
|
|
3925
|
+
currentPage,
|
|
3926
|
+
...pageSize !== undefined ? {
|
|
3927
|
+
pageSize
|
|
3928
|
+
} : {}
|
|
3929
|
+
};
|
|
3930
|
+
}
|
|
3649
3931
|
}
|
|
3650
|
-
|
|
3932
|
+
if (present('select')) {
|
|
3933
|
+
const select = source['select'];
|
|
3934
|
+
const problems = Array.isArray(select) ? selectErrors(select) : [
|
|
3935
|
+
{
|
|
3936
|
+
field: 'select',
|
|
3937
|
+
...(0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_4/* .FLOW_MESSAGES.INPUT_SELECT_INVALID */.wu.INPUT_SELECT_INVALID, {
|
|
3938
|
+
field: 'select'
|
|
3939
|
+
})
|
|
3940
|
+
}
|
|
3941
|
+
];
|
|
3942
|
+
if (problems.length) errors.push(...problems);
|
|
3943
|
+
else query.select = select;
|
|
3944
|
+
}
|
|
3945
|
+
if (source['withDeleted'] === true) query.withDeleted = true;
|
|
3946
|
+
return {
|
|
3947
|
+
query,
|
|
3948
|
+
errors
|
|
3949
|
+
};
|
|
3651
3950
|
}
|
|
3652
3951
|
function checkList(schema, body) {
|
|
3653
3952
|
if (!Array.isArray(body)) return {
|
|
@@ -3803,7 +4102,8 @@ function checkValue(field, supplied, raw, path, label) {
|
|
|
3803
4102
|
}
|
|
3804
4103
|
|
|
3805
4104
|
__webpack_require__.d(__webpack_exports__, {
|
|
3806
|
-
|
|
4105
|
+
GP: () => (validateEndpointInput),
|
|
4106
|
+
M2: () => (readRequestQuery)
|
|
3807
4107
|
});
|
|
3808
4108
|
|
|
3809
4109
|
|
|
@@ -3904,9 +4204,17 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
3904
4204
|
},
|
|
3905
4205
|
2678(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
3906
4206
|
const SENSITIVE_KEY = /pass(word)?|secret|token|authorization|api[-_]?key|cookie|credential|private[-_]?key/i;
|
|
4207
|
+
const MAX_DEPTH = 8;
|
|
4208
|
+
const MAX_ITEMS = 100;
|
|
4209
|
+
const TWO_PASS = new Error('preview: needs the two-pass path');
|
|
4210
|
+
const BOXED = new Set([
|
|
4211
|
+
'[object String]',
|
|
4212
|
+
'[object Number]',
|
|
4213
|
+
'[object Boolean]'
|
|
4214
|
+
]);
|
|
3907
4215
|
/** Copy of `value` with the content of sensitive-looking keys replaced, safe to store in a log. */ function redactSensitive(value, depth = 0) {
|
|
3908
|
-
if (depth >
|
|
3909
|
-
if (Array.isArray(value)) return value.slice(0,
|
|
4216
|
+
if (depth > MAX_DEPTH) return '[deep]';
|
|
4217
|
+
if (Array.isArray(value)) return value.slice(0, MAX_ITEMS).map((v)=>redactSensitive(v, depth + 1));
|
|
3910
4218
|
// Dates (and anything else with its own JSON form) have no own keys: walked as objects they would log as `{}`.
|
|
3911
4219
|
if (value && typeof value.toJSON === 'function') return redactSensitive(value.toJSON(), depth + 1);
|
|
3912
4220
|
if (value && typeof value === 'object') {
|
|
@@ -3918,13 +4226,43 @@ const SENSITIVE_KEY = /pass(word)?|secret|token|authorization|api[-_]?key|cookie
|
|
|
3918
4226
|
}
|
|
3919
4227
|
return value;
|
|
3920
4228
|
}
|
|
4229
|
+
/**
|
|
4230
|
+
* `JSON.stringify(redactSensitive(value))` in one pass, without building the redacted copy first: the same
|
|
4231
|
+
* text for every value a flow step returns. Shapes one pass cannot write the same way (cycles, boxed
|
|
4232
|
+
* primitives, a `toJSON` result with a `toJSON` of its own) throw, and the caller takes the two-pass path.
|
|
4233
|
+
*/ function redactedJson(value) {
|
|
4234
|
+
const depths = new WeakMap();
|
|
4235
|
+
return JSON.stringify(value, function(key, current) {
|
|
4236
|
+
const holder = this;
|
|
4237
|
+
const inArray = Array.isArray(holder);
|
|
4238
|
+
if (!inArray && SENSITIVE_KEY.test(key)) return '***';
|
|
4239
|
+
if (inArray && !(key in holder)) return undefined;
|
|
4240
|
+
const original = holder[key];
|
|
4241
|
+
const viaToJSON = original !== null && typeof original === 'object' && typeof original.toJSON === 'function';
|
|
4242
|
+
if (viaToJSON && current !== null && typeof current === 'object' && typeof current.toJSON === 'function') throw TWO_PASS;
|
|
4243
|
+
if (current !== null && typeof current === 'object' && BOXED.has(Object.prototype.toString.call(current))) throw TWO_PASS;
|
|
4244
|
+
const depth = (depths.get(holder) ?? -1) + 1 + (viaToJSON ? 1 : 0);
|
|
4245
|
+
if (depth > MAX_DEPTH) return '[deep]';
|
|
4246
|
+
if (Array.isArray(current)) {
|
|
4247
|
+
const items = current.length > MAX_ITEMS ? current.slice(0, MAX_ITEMS) : current;
|
|
4248
|
+
depths.set(items, depth);
|
|
4249
|
+
return items;
|
|
4250
|
+
}
|
|
4251
|
+
if (current !== null && typeof current === 'object') depths.set(current, depth);
|
|
4252
|
+
return current;
|
|
4253
|
+
});
|
|
4254
|
+
}
|
|
3921
4255
|
/** Redacted, size-limited text for a trace/log entry. */ function preview(value, maxChars) {
|
|
3922
4256
|
if (value === undefined) return '';
|
|
3923
4257
|
let text;
|
|
3924
4258
|
try {
|
|
3925
|
-
text =
|
|
4259
|
+
text = redactedJson(value) ?? '';
|
|
3926
4260
|
} catch {
|
|
3927
|
-
|
|
4261
|
+
try {
|
|
4262
|
+
text = JSON.stringify(redactSensitive(value)) ?? '';
|
|
4263
|
+
} catch {
|
|
4264
|
+
text = '[unserializable]';
|
|
4265
|
+
}
|
|
3928
4266
|
}
|
|
3929
4267
|
return text.length > maxChars ? `${text.slice(0, maxChars)}... (${text.length} chars)` : text;
|
|
3930
4268
|
}
|
|
@@ -4177,9 +4515,29 @@ class EntityBuilderConfigService {
|
|
|
4177
4515
|
getConfig() {
|
|
4178
4516
|
return this.options.config;
|
|
4179
4517
|
}
|
|
4518
|
+
isFastUpdatesEnabled() {
|
|
4519
|
+
return this.getConfig()?.performance?.fastUpdates ?? true;
|
|
4520
|
+
}
|
|
4521
|
+
isPartialIndexesEnabled() {
|
|
4522
|
+
return this.getConfig()?.performance?.partialIndexes ?? false;
|
|
4523
|
+
}
|
|
4180
4524
|
/** `designer.readOnly`, else the `ENTITY_BUILDER_READ_ONLY=true` env variable (production). */ isDesignerReadOnly() {
|
|
4181
4525
|
return this.getConfig()?.designer?.readOnly ?? process.env['ENTITY_BUILDER_READ_ONLY'] === 'true';
|
|
4182
4526
|
}
|
|
4527
|
+
/** The packages a code step may `require`: `ENTITY_BUILDER_CODE_MODULES` (`dayjs,_=lodash`: a name, or name=specifier), overridden by `flows.code.modules`. */ getCodeModules() {
|
|
4528
|
+
const fromEnv = (process.env['ENTITY_BUILDER_CODE_MODULES'] ?? '').split(',').map((entry)=>entry.trim()).filter(Boolean).map((entry)=>{
|
|
4529
|
+
const [name, specifier] = entry.split('=').map((part)=>part.trim());
|
|
4530
|
+
return [
|
|
4531
|
+
name,
|
|
4532
|
+
specifier || name
|
|
4533
|
+
];
|
|
4534
|
+
});
|
|
4535
|
+
const modules = {
|
|
4536
|
+
...Object.fromEntries(fromEnv),
|
|
4537
|
+
...this.getConfig()?.flows?.code?.modules
|
|
4538
|
+
};
|
|
4539
|
+
return Object.fromEntries(Object.entries(modules).filter((entry)=>!!entry[0] && typeof entry[1] === 'string' && !!entry[1].trim()));
|
|
4540
|
+
}
|
|
4183
4541
|
/** The values behind `${var:NAME}` in promoted flows: every non-empty `ENTITY_BUILDER_VAR_<NAME>` env variable, overridden by `bundles.variables`; names a placeholder cannot carry are left out. */ getBundleVariables() {
|
|
4184
4542
|
const fromEnv = Object.entries(process.env).filter((entry)=>entry[0].startsWith(VARIABLE_ENV_PREFIX) && !!entry[1]).map(([key, value])=>[
|
|
4185
4543
|
key.slice(VARIABLE_ENV_PREFIX.length),
|
|
@@ -4296,26 +4654,52 @@ class EntityBuilderDataSourceProvider extends _flusys_nestjs_shared_modules__rsp
|
|
|
4296
4654
|
* just by instance identity, so a fresh schema instance with the same name still
|
|
4297
4655
|
* resolves correctly after a rebuild.
|
|
4298
4656
|
*/ async registerDynamicSchema(schema) {
|
|
4657
|
+
await this.registerDynamicSchemas([
|
|
4658
|
+
schema
|
|
4659
|
+
]);
|
|
4660
|
+
}
|
|
4661
|
+
/**
|
|
4662
|
+
* Several schemas with ONE metadata rebuild - each rebuild reads every entity of the DataSource, so
|
|
4663
|
+
* registering N entities one by one costs N full rebuilds. A failed rebuild puts `options.entities`
|
|
4664
|
+
* back as it was and rebuilds it, so one bad schema never breaks later registrations.
|
|
4665
|
+
*/ async registerDynamicSchemas(schemas) {
|
|
4299
4666
|
const dataSource = await this.getDataSource();
|
|
4300
4667
|
const perDsMap = this.statics.registeredSchemas.get(dataSource) ?? new Map();
|
|
4301
4668
|
this.statics.registeredSchemas.set(dataSource, perDsMap);
|
|
4302
|
-
if (perDsMap.get(schema.options.name) === schema) return;
|
|
4669
|
+
if (schemas.every((schema)=>perDsMap.get(schema.options.name) === schema)) return;
|
|
4303
4670
|
// Serialize concurrent registrations against the SAME DataSource - buildMetadatas()
|
|
4304
4671
|
// does a full rebuild from `options.entities`, so two racing pushes must not interleave.
|
|
4305
4672
|
const prior = this.statics.rebuildLocks.get(dataSource) ?? Promise.resolve();
|
|
4306
4673
|
const next = prior.then(async ()=>{
|
|
4307
|
-
perDsMap.
|
|
4674
|
+
const pending = schemas.filter((schema)=>perDsMap.get(schema.options.name) !== schema);
|
|
4675
|
+
if (!pending.length) return;
|
|
4308
4676
|
const entitiesArr = dataSource.options.entities;
|
|
4309
|
-
const
|
|
4310
|
-
|
|
4311
|
-
|
|
4677
|
+
const before = [
|
|
4678
|
+
...entitiesArr
|
|
4679
|
+
];
|
|
4680
|
+
const replaced = new Map(pending.map((schema)=>[
|
|
4681
|
+
schema.options.name,
|
|
4682
|
+
perDsMap.get(schema.options.name)
|
|
4683
|
+
]));
|
|
4684
|
+
for (const schema of pending){
|
|
4685
|
+
perDsMap.set(schema.options.name, schema);
|
|
4686
|
+
const idx = entitiesArr.findIndex((e)=>e instanceof Object && e.options?.name === schema.options.name);
|
|
4687
|
+
if (idx >= 0) entitiesArr[idx] = schema;
|
|
4688
|
+
else entitiesArr.push(schema);
|
|
4689
|
+
}
|
|
4312
4690
|
// buildMetadatas() is `protected` in TypeORM's public .d.ts (a compile-time-only
|
|
4313
4691
|
// restriction, not enforced by the JS runtime) but is the only way to rebuild
|
|
4314
4692
|
// entityMetadatas from options.entities after DataSource.initialize() has run.
|
|
4693
|
+
const rebuild = ()=>dataSource.buildMetadatas();
|
|
4315
4694
|
try {
|
|
4316
|
-
await
|
|
4695
|
+
await rebuild();
|
|
4317
4696
|
} catch (error) {
|
|
4318
|
-
|
|
4697
|
+
entitiesArr.splice(0, entitiesArr.length, ...before);
|
|
4698
|
+
for (const [name, previous] of replaced){
|
|
4699
|
+
if (previous) perDsMap.set(name, previous);
|
|
4700
|
+
else perDsMap.delete(name);
|
|
4701
|
+
}
|
|
4702
|
+
await rebuild().catch(()=>undefined);
|
|
4319
4703
|
throw error;
|
|
4320
4704
|
}
|
|
4321
4705
|
});
|
|
@@ -4354,11 +4738,22 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
4354
4738
|
},
|
|
4355
4739
|
3525(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
4356
4740
|
/* import */ var _flusys_nestjs_shared_utils__rspack_import_0 = __webpack_require__(9216);
|
|
4357
|
-
/* import */ var
|
|
4358
|
-
/* import */ var
|
|
4741
|
+
/* import */ var _config_message_keys_js__rspack_import_3 = __webpack_require__(5732);
|
|
4742
|
+
/* import */ var _enums_field_type_enum_js__rspack_import_2 = __webpack_require__(9614);
|
|
4743
|
+
/* import */ var _rule_engine_rule_values_js__rspack_import_1 = __webpack_require__(6622);
|
|
4359
4744
|
|
|
4360
4745
|
|
|
4361
4746
|
|
|
4747
|
+
|
|
4748
|
+
/** A field's value rules alone, optional and without a default: checks one value (a default, a backfill) as a record field would. */ const valueRulesOf = (field)=>({
|
|
4749
|
+
code: field.code,
|
|
4750
|
+
label: field.label ?? field.code,
|
|
4751
|
+
fieldType: field.fieldType,
|
|
4752
|
+
nullable: true,
|
|
4753
|
+
defaultValue: null,
|
|
4754
|
+
selectOptions: field.selectOptions ?? null,
|
|
4755
|
+
validationRules: field.validationRules ?? null
|
|
4756
|
+
});
|
|
4362
4757
|
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
4363
4758
|
const EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
4364
4759
|
const DATE = /^\d{4}-\d{2}-\d{2}$/;
|
|
@@ -4390,18 +4785,18 @@ function isEmpty(value) {
|
|
|
4390
4785
|
})
|
|
4391
4786
|
});
|
|
4392
4787
|
switch(field.fieldType){
|
|
4393
|
-
case
|
|
4394
|
-
case
|
|
4395
|
-
case
|
|
4788
|
+
case _enums_field_type_enum_js__rspack_import_2/* .FieldType.TEXT */.P.TEXT:
|
|
4789
|
+
case _enums_field_type_enum_js__rspack_import_2/* .FieldType.EMAIL */.P.EMAIL:
|
|
4790
|
+
case _enums_field_type_enum_js__rspack_import_2/* .FieldType.SINGLE_SELECT */.P.SINGLE_SELECT:
|
|
4396
4791
|
{
|
|
4397
|
-
if (typeof value !== 'string') return fail(
|
|
4398
|
-
if (value.length > TEXT_MAX) return fail(
|
|
4792
|
+
if (typeof value !== 'string') return fail(_config_message_keys_js__rspack_import_3/* .FIELD_VALIDATION_MESSAGES.MUST_BE_TEXT */.KE.MUST_BE_TEXT);
|
|
4793
|
+
if (value.length > TEXT_MAX) return fail(_config_message_keys_js__rspack_import_3/* .FIELD_VALIDATION_MESSAGES.MAX_LENGTH */.KE.MAX_LENGTH, {
|
|
4399
4794
|
max: TEXT_MAX
|
|
4400
4795
|
});
|
|
4401
|
-
if (field.fieldType ===
|
|
4402
|
-
if (field.fieldType ===
|
|
4796
|
+
if (field.fieldType === _enums_field_type_enum_js__rspack_import_2/* .FieldType.EMAIL */.P.EMAIL && !EMAIL.test(value)) return fail(_config_message_keys_js__rspack_import_3/* .FIELD_VALIDATION_MESSAGES.INVALID_EMAIL */.KE.INVALID_EMAIL);
|
|
4797
|
+
if (field.fieldType === _enums_field_type_enum_js__rspack_import_2/* .FieldType.SINGLE_SELECT */.P.SINGLE_SELECT) {
|
|
4403
4798
|
const options = optionValues(field);
|
|
4404
|
-
if (options && !options.includes(value)) return fail(
|
|
4799
|
+
if (options && !options.includes(value)) return fail(_config_message_keys_js__rspack_import_3/* .FIELD_VALIDATION_MESSAGES.ONE_OF */.KE.ONE_OF, {
|
|
4405
4800
|
options: options.join(', ')
|
|
4406
4801
|
});
|
|
4407
4802
|
}
|
|
@@ -4409,19 +4804,19 @@ function isEmpty(value) {
|
|
|
4409
4804
|
value
|
|
4410
4805
|
};
|
|
4411
4806
|
}
|
|
4412
|
-
case
|
|
4413
|
-
if (typeof value !== 'string') return fail(
|
|
4414
|
-
if (value.length > LONG_TEXT_MAX) return fail(
|
|
4807
|
+
case _enums_field_type_enum_js__rspack_import_2/* .FieldType.LONG_TEXT */.P.LONG_TEXT:
|
|
4808
|
+
if (typeof value !== 'string') return fail(_config_message_keys_js__rspack_import_3/* .FIELD_VALIDATION_MESSAGES.MUST_BE_TEXT */.KE.MUST_BE_TEXT);
|
|
4809
|
+
if (value.length > LONG_TEXT_MAX) return fail(_config_message_keys_js__rspack_import_3/* .FIELD_VALIDATION_MESSAGES.MAX_LENGTH */.KE.MAX_LENGTH, {
|
|
4415
4810
|
max: LONG_TEXT_MAX
|
|
4416
4811
|
});
|
|
4417
4812
|
return {
|
|
4418
4813
|
value
|
|
4419
4814
|
};
|
|
4420
|
-
case
|
|
4815
|
+
case _enums_field_type_enum_js__rspack_import_2/* .FieldType.INTEGER */.P.INTEGER:
|
|
4421
4816
|
{
|
|
4422
4817
|
const n = typeof value === 'string' && value.trim() !== '' ? Number(value) : value;
|
|
4423
|
-
if (typeof n !== 'number' || !Number.isInteger(n)) return fail(
|
|
4424
|
-
if (n < INT_MIN || n > INT_MAX) return fail(
|
|
4818
|
+
if (typeof n !== 'number' || !Number.isInteger(n)) return fail(_config_message_keys_js__rspack_import_3/* .FIELD_VALIDATION_MESSAGES.WHOLE_NUMBER */.KE.WHOLE_NUMBER);
|
|
4819
|
+
if (n < INT_MIN || n > INT_MAX) return fail(_config_message_keys_js__rspack_import_3/* .FIELD_VALIDATION_MESSAGES.NUMBER_BETWEEN */.KE.NUMBER_BETWEEN, {
|
|
4425
4820
|
min: INT_MIN,
|
|
4426
4821
|
max: INT_MAX
|
|
4427
4822
|
});
|
|
@@ -4429,16 +4824,16 @@ function isEmpty(value) {
|
|
|
4429
4824
|
value: n
|
|
4430
4825
|
};
|
|
4431
4826
|
}
|
|
4432
|
-
case
|
|
4827
|
+
case _enums_field_type_enum_js__rspack_import_2/* .FieldType.DECIMAL */.P.DECIMAL:
|
|
4433
4828
|
{
|
|
4434
4829
|
const n = typeof value === 'string' && value.trim() !== '' ? Number(value) : value;
|
|
4435
|
-
if (typeof n !== 'number' || !Number.isFinite(n)) return fail(
|
|
4436
|
-
if (Math.abs(n) >= DECIMAL_LIMIT) return fail(
|
|
4830
|
+
if (typeof n !== 'number' || !Number.isFinite(n)) return fail(_config_message_keys_js__rspack_import_3/* .FIELD_VALIDATION_MESSAGES.NUMBER */.KE.NUMBER);
|
|
4831
|
+
if (Math.abs(n) >= DECIMAL_LIMIT) return fail(_config_message_keys_js__rspack_import_3/* .FIELD_VALIDATION_MESSAGES.NUMBER_TOO_LARGE */.KE.NUMBER_TOO_LARGE);
|
|
4437
4832
|
return {
|
|
4438
4833
|
value: n
|
|
4439
4834
|
};
|
|
4440
4835
|
}
|
|
4441
|
-
case
|
|
4836
|
+
case _enums_field_type_enum_js__rspack_import_2/* .FieldType.BOOLEAN */.P.BOOLEAN:
|
|
4442
4837
|
{
|
|
4443
4838
|
if (typeof value === 'boolean') return {
|
|
4444
4839
|
value
|
|
@@ -4449,46 +4844,47 @@ function isEmpty(value) {
|
|
|
4449
4844
|
if (value === 'false') return {
|
|
4450
4845
|
value: false
|
|
4451
4846
|
};
|
|
4452
|
-
return fail(
|
|
4847
|
+
return fail(_config_message_keys_js__rspack_import_3/* .FIELD_VALIDATION_MESSAGES.BOOLEAN */.KE.BOOLEAN);
|
|
4453
4848
|
}
|
|
4454
|
-
case
|
|
4849
|
+
case _enums_field_type_enum_js__rspack_import_2/* .FieldType.DATE */.P.DATE:
|
|
4455
4850
|
if (typeof value !== 'string' || !DATE.test(value) || !isRealCalendarDate(value)) {
|
|
4456
|
-
return fail(
|
|
4851
|
+
return fail(_config_message_keys_js__rspack_import_3/* .FIELD_VALIDATION_MESSAGES.REAL_DATE */.KE.REAL_DATE);
|
|
4457
4852
|
}
|
|
4458
4853
|
return {
|
|
4459
4854
|
value
|
|
4460
4855
|
};
|
|
4461
|
-
case
|
|
4856
|
+
case _enums_field_type_enum_js__rspack_import_2/* .FieldType.DATETIME */.P.DATETIME:
|
|
4462
4857
|
{
|
|
4463
|
-
|
|
4464
|
-
|
|
4858
|
+
// Zone-less ISO text is UTC, never the server's local time.
|
|
4859
|
+
const time = value instanceof Date ? value.getTime() : typeof value === 'string' ? (0,_rule_engine_rule_values_js__rspack_import_1/* .parseDateText */.hz)(value) : Number.NaN;
|
|
4860
|
+
if (!Number.isFinite(time)) return fail(_config_message_keys_js__rspack_import_3/* .FIELD_VALIDATION_MESSAGES.ISO_DATETIME */.KE.ISO_DATETIME);
|
|
4465
4861
|
return {
|
|
4466
|
-
value:
|
|
4862
|
+
value: new Date(time).toISOString()
|
|
4467
4863
|
};
|
|
4468
4864
|
}
|
|
4469
|
-
case
|
|
4865
|
+
case _enums_field_type_enum_js__rspack_import_2/* .FieldType.MULTI_SELECT */.P.MULTI_SELECT:
|
|
4470
4866
|
{
|
|
4471
|
-
if (!Array.isArray(value) || value.some((v)=>typeof v !== 'string')) return fail(
|
|
4867
|
+
if (!Array.isArray(value) || value.some((v)=>typeof v !== 'string')) return fail(_config_message_keys_js__rspack_import_3/* .FIELD_VALIDATION_MESSAGES.TEXT_LIST */.KE.TEXT_LIST);
|
|
4472
4868
|
const options = optionValues(field);
|
|
4473
4869
|
const invalid = options ? value.filter((v)=>!options.includes(v)) : [];
|
|
4474
|
-
if (invalid.length) return fail(
|
|
4870
|
+
if (invalid.length) return fail(_config_message_keys_js__rspack_import_3/* .FIELD_VALIDATION_MESSAGES.UNKNOWN_OPTIONS */.KE.UNKNOWN_OPTIONS, {
|
|
4475
4871
|
options: invalid.join(', ')
|
|
4476
4872
|
});
|
|
4477
4873
|
return {
|
|
4478
4874
|
value
|
|
4479
4875
|
};
|
|
4480
4876
|
}
|
|
4481
|
-
case
|
|
4482
|
-
case
|
|
4483
|
-
if (typeof value !== 'string' || !UUID.test(value)) return fail(
|
|
4877
|
+
case _enums_field_type_enum_js__rspack_import_2/* .FieldType.RELATION */.P.RELATION:
|
|
4878
|
+
case _enums_field_type_enum_js__rspack_import_2/* .FieldType.FILE */.P.FILE:
|
|
4879
|
+
if (typeof value !== 'string' || !UUID.test(value)) return fail(_config_message_keys_js__rspack_import_3/* .FIELD_VALIDATION_MESSAGES.UUID */.KE.UUID);
|
|
4484
4880
|
return {
|
|
4485
4881
|
value
|
|
4486
4882
|
};
|
|
4487
|
-
case
|
|
4883
|
+
case _enums_field_type_enum_js__rspack_import_2/* .FieldType.JSON */.P.JSON:
|
|
4488
4884
|
try {
|
|
4489
4885
|
JSON.stringify(value);
|
|
4490
4886
|
} catch {
|
|
4491
|
-
return fail(
|
|
4887
|
+
return fail(_config_message_keys_js__rspack_import_3/* .FIELD_VALIDATION_MESSAGES.JSON */.KE.JSON);
|
|
4492
4888
|
}
|
|
4493
4889
|
return {
|
|
4494
4890
|
value
|
|
@@ -4503,38 +4899,38 @@ function checkRules(field, value) {
|
|
|
4503
4899
|
const rules = field.validationRules;
|
|
4504
4900
|
if (!rules) return null;
|
|
4505
4901
|
const label = field.label;
|
|
4506
|
-
const numeric = field.fieldType ===
|
|
4902
|
+
const numeric = field.fieldType === _enums_field_type_enum_js__rspack_import_2/* .FieldType.INTEGER */.P.INTEGER || field.fieldType === _enums_field_type_enum_js__rspack_import_2/* .FieldType.DECIMAL */.P.DECIMAL;
|
|
4507
4903
|
const text = typeof value === 'string';
|
|
4508
4904
|
if (numeric && typeof value === 'number') {
|
|
4509
|
-
if (typeof rules.min === 'number' && value < rules.min) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(
|
|
4905
|
+
if (typeof rules.min === 'number' && value < rules.min) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_3/* .FIELD_VALIDATION_MESSAGES.MIN_VALUE */.KE.MIN_VALUE, {
|
|
4510
4906
|
label,
|
|
4511
4907
|
min: rules.min
|
|
4512
4908
|
});
|
|
4513
|
-
if (typeof rules.max === 'number' && value > rules.max) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(
|
|
4909
|
+
if (typeof rules.max === 'number' && value > rules.max) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_3/* .FIELD_VALIDATION_MESSAGES.MAX_VALUE */.KE.MAX_VALUE, {
|
|
4514
4910
|
label,
|
|
4515
4911
|
max: rules.max
|
|
4516
4912
|
});
|
|
4517
4913
|
}
|
|
4518
4914
|
if (text) {
|
|
4519
4915
|
if (typeof rules.minLength === 'number' && value.length < rules.minLength) {
|
|
4520
|
-
return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(
|
|
4916
|
+
return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_3/* .FIELD_VALIDATION_MESSAGES.MIN_LENGTH */.KE.MIN_LENGTH, {
|
|
4521
4917
|
label,
|
|
4522
4918
|
min: rules.minLength
|
|
4523
4919
|
});
|
|
4524
4920
|
}
|
|
4525
4921
|
if (typeof rules.maxLength === 'number' && value.length > rules.maxLength) {
|
|
4526
|
-
return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(
|
|
4922
|
+
return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_3/* .FIELD_VALIDATION_MESSAGES.MAX_LENGTH */.KE.MAX_LENGTH, {
|
|
4527
4923
|
label,
|
|
4528
4924
|
max: rules.maxLength
|
|
4529
4925
|
});
|
|
4530
4926
|
}
|
|
4531
4927
|
}
|
|
4532
4928
|
if (Array.isArray(value)) {
|
|
4533
|
-
if (typeof rules.minItems === 'number' && value.length < rules.minItems) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(
|
|
4929
|
+
if (typeof rules.minItems === 'number' && value.length < rules.minItems) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_3/* .FIELD_VALIDATION_MESSAGES.MIN_ITEMS */.KE.MIN_ITEMS, {
|
|
4534
4930
|
label,
|
|
4535
4931
|
min: rules.minItems
|
|
4536
4932
|
});
|
|
4537
|
-
if (typeof rules.maxItems === 'number' && value.length > rules.maxItems) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(
|
|
4933
|
+
if (typeof rules.maxItems === 'number' && value.length > rules.maxItems) return (0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_3/* .FIELD_VALIDATION_MESSAGES.MAX_ITEMS */.KE.MAX_ITEMS, {
|
|
4538
4934
|
label,
|
|
4539
4935
|
max: rules.maxItems
|
|
4540
4936
|
});
|
|
@@ -4564,7 +4960,7 @@ function checkRules(field, value) {
|
|
|
4564
4960
|
if (isEmpty(raw)) {
|
|
4565
4961
|
if (!field.nullable) errors.push({
|
|
4566
4962
|
field: field.code,
|
|
4567
|
-
...(0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(
|
|
4963
|
+
...(0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_3/* .FIELD_VALIDATION_MESSAGES.REQUIRED */.KE.REQUIRED, {
|
|
4568
4964
|
label: field.label
|
|
4569
4965
|
})
|
|
4570
4966
|
});
|
|
@@ -4597,6 +4993,8 @@ function checkRules(field, value) {
|
|
|
4597
4993
|
|
|
4598
4994
|
__webpack_require__.d(__webpack_exports__, {
|
|
4599
4995
|
e: () => (validateRecord)
|
|
4996
|
+
}, {
|
|
4997
|
+
C: valueRulesOf
|
|
4600
4998
|
});
|
|
4601
4999
|
|
|
4602
5000
|
|
|
@@ -4608,17 +5006,19 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
4608
5006
|
/* import */ var _flusys_nestjs_shared_modules__rspack_import_3 = __webpack_require__(1236);
|
|
4609
5007
|
/* import */ var _nestjs_common__rspack_import_4 = __webpack_require__(6157);
|
|
4610
5008
|
/* import */ var typeorm__rspack_import_5 = __webpack_require__(7158);
|
|
4611
|
-
/* import */ var
|
|
4612
|
-
/* import */ var
|
|
5009
|
+
/* import */ var _config_event_actions_js__rspack_import_18 = __webpack_require__(982);
|
|
5010
|
+
/* import */ var _config_message_keys_js__rspack_import_16 = __webpack_require__(5732);
|
|
4613
5011
|
/* import */ var _entities_entity_definition_entity_js__rspack_import_6 = __webpack_require__(3189);
|
|
4614
5012
|
/* import */ var _entities_flow_definition_entity_js__rspack_import_7 = __webpack_require__(3770);
|
|
4615
|
-
/* import */ var
|
|
4616
|
-
/* import */ var
|
|
4617
|
-
/* import */ var
|
|
4618
|
-
/* import */ var
|
|
4619
|
-
/* import */ var
|
|
4620
|
-
/* import */ var
|
|
4621
|
-
/* import */ var
|
|
5013
|
+
/* import */ var _entities_flow_execution_entity_js__rspack_import_8 = __webpack_require__(9641);
|
|
5014
|
+
/* import */ var _entities_flow_version_entity_js__rspack_import_9 = __webpack_require__(2399);
|
|
5015
|
+
/* import */ var _flow_engine_flow_api_key_js__rspack_import_10 = __webpack_require__(4816);
|
|
5016
|
+
/* import */ var _flow_engine_flow_permission_logic_js__rspack_import_15 = __webpack_require__(8220);
|
|
5017
|
+
/* import */ var _flow_engine_flow_graph_validator_js__rspack_import_11 = __webpack_require__(3186);
|
|
5018
|
+
/* import */ var _flow_engine_flow_types_js__rspack_import_12 = __webpack_require__(6256);
|
|
5019
|
+
/* import */ var _interfaces_flow_definition_interface_js__rspack_import_17 = __webpack_require__(9609);
|
|
5020
|
+
/* import */ var _entity_builder_config_service_js__rspack_import_13 = __webpack_require__(4281);
|
|
5021
|
+
/* import */ var _entity_builder_datasource_provider_js__rspack_import_14 = __webpack_require__(424);
|
|
4622
5022
|
function _ts_decorate(decorators, target, key, desc) {
|
|
4623
5023
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4624
5024
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
@@ -4659,16 +5059,19 @@ function _ts_param(paramIndex, decorator) {
|
|
|
4659
5059
|
|
|
4660
5060
|
|
|
4661
5061
|
|
|
5062
|
+
|
|
5063
|
+
|
|
4662
5064
|
const defaultFlowPermissionCode = (slug)=>`entity_builder.flow.${slug}.execute`;
|
|
4663
|
-
/** The rule a `permission` URL asks callers to pass: its own, or the flow's execute permission. */ const flowPermissions = (endpoint, slug)=>endpoint.permissions ?? (0,
|
|
5065
|
+
/** The rule a `permission` URL asks callers to pass: its own, or the flow's execute permission. */ const flowPermissions = (endpoint, slug)=>endpoint.permissions ?? (0,_flow_engine_flow_permission_logic_js__rspack_import_15/* .permissionAction */.s)(defaultFlowPermissionCode(slug));
|
|
4664
5066
|
/** A `permission` URL without a rule of its own: it asks for the flow's execute permission, which publishing registers. */ const asksExecutePermission = (endpoint)=>endpoint.authMode === 'permission' && !endpoint.permissions;
|
|
5067
|
+
const logger = new _nestjs_common__rspack_import_4.Logger('FlowDefinitionService');
|
|
4665
5068
|
const flowNotFound = ()=>new _nestjs_common__rspack_import_4.NotFoundException({
|
|
4666
5069
|
message: 'Flow not found.',
|
|
4667
|
-
messageKey:
|
|
5070
|
+
messageKey: _config_message_keys_js__rspack_import_16/* .FLOW_MESSAGES.NOT_FOUND */.wu.NOT_FOUND
|
|
4668
5071
|
});
|
|
4669
5072
|
/** A flow that has been published at least once: its columns are what callers run. */ const isPublished = (flow)=>flow.version > 0;
|
|
4670
5073
|
/** The versioned part of a flow (or of a draft), in a fixed key order so two snapshots compare as text. */ function snapshotOf(source) {
|
|
4671
|
-
return Object.fromEntries(
|
|
5074
|
+
return Object.fromEntries(_interfaces_flow_definition_interface_js__rspack_import_17/* .FLOW_VERSIONED_FIELDS.filter */.v.filter((key)=>source[key] !== undefined).map((key)=>[
|
|
4672
5075
|
key,
|
|
4673
5076
|
source[key]
|
|
4674
5077
|
]));
|
|
@@ -4694,8 +5097,9 @@ class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import
|
|
|
4694
5097
|
notifications;
|
|
4695
5098
|
email;
|
|
4696
5099
|
companyAccess;
|
|
4697
|
-
|
|
4698
|
-
|
|
5100
|
+
config;
|
|
5101
|
+
constructor(cacheManager, utilsService, provider, permissionCache, actionRegistry, notifications, email, companyAccess, config){
|
|
5102
|
+
super(_config_event_actions_js__rspack_import_18/* .ENTITY_BUILDER_EVENT_ENTITIES.FLOW_DEFINITION */.P.FLOW_DEFINITION, cacheManager, utilsService, FlowDefinitionService.name, true, _config_event_actions_js__rspack_import_18/* .ENTITY_BUILDER_EVENT_MODULE */.S, provider, _entities_flow_definition_entity_js__rspack_import_7/* .FlowDefinition */.G), this.cacheManager = cacheManager, this.utilsService = utilsService, this.provider = provider, this.permissionCache = permissionCache, this.actionRegistry = actionRegistry, this.notifications = notifications, this.email = email, this.companyAccess = companyAccess, this.config = config;
|
|
4699
5103
|
}
|
|
4700
5104
|
async findOrFail(id) {
|
|
4701
5105
|
const flow = await (await this.provider.getRepository(_entities_flow_definition_entity_js__rspack_import_7/* .FlowDefinition */.G)).findOne({
|
|
@@ -4713,7 +5117,7 @@ class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import
|
|
|
4713
5117
|
}
|
|
4714
5118
|
});
|
|
4715
5119
|
const flows = await (await this.provider.getRepository(_entities_flow_definition_entity_js__rspack_import_7/* .FlowDefinition */.G)).find();
|
|
4716
|
-
return (0,
|
|
5120
|
+
return (0,_flow_engine_flow_graph_validator_js__rspack_import_11/* .validateFlow */.nC)(settings, {
|
|
4717
5121
|
knownEntityCodes: new Set(entities.map((e)=>e.code)),
|
|
4718
5122
|
knownFlows: new Map(flows.map((flow)=>[
|
|
4719
5123
|
flow.slug,
|
|
@@ -4723,9 +5127,13 @@ class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import
|
|
|
4723
5127
|
notification: !!this.notifications,
|
|
4724
5128
|
email: !!this.email
|
|
4725
5129
|
},
|
|
4726
|
-
companyMembers: !!this.companyAccess
|
|
5130
|
+
companyMembers: !!this.companyAccess,
|
|
5131
|
+
codeModules: this.config ? this.codePackages() : undefined
|
|
4727
5132
|
});
|
|
4728
5133
|
}
|
|
5134
|
+
/** The names a code step may `require`, as the app configured them. */ codePackages() {
|
|
5135
|
+
return Object.keys(this.config?.getCodeModules() ?? {});
|
|
5136
|
+
}
|
|
4729
5137
|
/** Names of the other flows whose `call_flow` nodes run `slug`. */ async callersOf(slug, excludeIds) {
|
|
4730
5138
|
const flows = await (await this.provider.getRepository(_entities_flow_definition_entity_js__rspack_import_7/* .FlowDefinition */.G)).find({
|
|
4731
5139
|
select: {
|
|
@@ -4741,7 +5149,7 @@ class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import
|
|
|
4741
5149
|
if (!callers.length) return;
|
|
4742
5150
|
throw new _nestjs_common__rspack_import_4.ConflictException({
|
|
4743
5151
|
message: `The flow '${slug}' is called by: ${callers.join(', ')}. Change those flows first.`,
|
|
4744
|
-
messageKey:
|
|
5152
|
+
messageKey: _config_message_keys_js__rspack_import_16/* .FLOW_MESSAGES.CALLED_BY_OTHER_FLOWS */.wu.CALLED_BY_OTHER_FLOWS,
|
|
4745
5153
|
messageVariables: {
|
|
4746
5154
|
slug,
|
|
4747
5155
|
flows: callers.join(', ')
|
|
@@ -4780,11 +5188,11 @@ class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import
|
|
|
4780
5188
|
const settings = toFlowSettings(caller);
|
|
4781
5189
|
// Counted, not a set: two steps with the same label can share a problem, and only the new one is added.
|
|
4782
5190
|
const existing = new Map();
|
|
4783
|
-
for (const problem of (0,
|
|
5191
|
+
for (const problem of (0,_flow_engine_flow_graph_validator_js__rspack_import_11/* .callProblems */.r7)(settings, before.slug, now)){
|
|
4784
5192
|
const key = JSON.stringify(problem);
|
|
4785
5193
|
existing.set(key, (existing.get(key) ?? 0) + 1);
|
|
4786
5194
|
}
|
|
4787
|
-
const added = (0,
|
|
5195
|
+
const added = (0,_flow_engine_flow_graph_validator_js__rspack_import_11/* .callProblems */.r7)(settings, before.slug, next).filter((problem)=>{
|
|
4788
5196
|
const key = JSON.stringify(problem);
|
|
4789
5197
|
const left = existing.get(key) ?? 0;
|
|
4790
5198
|
existing.set(key, left - 1);
|
|
@@ -4799,7 +5207,7 @@ class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import
|
|
|
4799
5207
|
const names = broken.map((b)=>b.flow).join(', ');
|
|
4800
5208
|
throw new _nestjs_common__rspack_import_4.ConflictException({
|
|
4801
5209
|
message: `This change breaks flows that call '${before.slug}': ${names}. Change those flows first.`,
|
|
4802
|
-
messageKey:
|
|
5210
|
+
messageKey: _config_message_keys_js__rspack_import_16/* .FLOW_MESSAGES.CALLERS_BROKEN */.wu.CALLERS_BROKEN,
|
|
4803
5211
|
messageVariables: {
|
|
4804
5212
|
slug: before.slug,
|
|
4805
5213
|
flows: names
|
|
@@ -4812,8 +5220,8 @@ class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import
|
|
|
4812
5220
|
});
|
|
4813
5221
|
}
|
|
4814
5222
|
/** A URL other flows' Call flow steps start at cannot be removed or renamed from under them. */ async assertEndpointsNotCalled(before, after) {
|
|
4815
|
-
const kept = new Set((0,
|
|
4816
|
-
const removed = (0,
|
|
5223
|
+
const kept = new Set((0,_flow_engine_flow_types_js__rspack_import_12/* .flowEndpoints */.Xr)(after).map((e)=>e.path));
|
|
5224
|
+
const removed = (0,_flow_engine_flow_types_js__rspack_import_12/* .flowEndpoints */.Xr)(before).map((e)=>e.path).filter((path)=>!kept.has(path));
|
|
4817
5225
|
if (!removed.length) return;
|
|
4818
5226
|
const flows = await (await this.provider.getRepository(_entities_flow_definition_entity_js__rspack_import_7/* .FlowDefinition */.G)).find({
|
|
4819
5227
|
select: {
|
|
@@ -4828,7 +5236,7 @@ class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import
|
|
|
4828
5236
|
const url = `api-flows/${before.slug}${path ? `/${path}` : ''}`;
|
|
4829
5237
|
throw new _nestjs_common__rspack_import_4.ConflictException({
|
|
4830
5238
|
message: `The URL ${url} is called by: ${callers.join(', ')}. Change those flows first.`,
|
|
4831
|
-
messageKey:
|
|
5239
|
+
messageKey: _config_message_keys_js__rspack_import_16/* .FLOW_MESSAGES.ENDPOINT_CALLED_BY_OTHER_FLOWS */.wu.ENDPOINT_CALLED_BY_OTHER_FLOWS,
|
|
4832
5240
|
messageVariables: {
|
|
4833
5241
|
url,
|
|
4834
5242
|
flows: callers.join(', ')
|
|
@@ -4841,7 +5249,7 @@ class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import
|
|
|
4841
5249
|
if (errors.length) {
|
|
4842
5250
|
throw new _nestjs_common__rspack_import_4.BadRequestException({
|
|
4843
5251
|
message: 'The flow has problems.',
|
|
4844
|
-
messageKey:
|
|
5252
|
+
messageKey: _config_message_keys_js__rspack_import_16/* .FLOW_MESSAGES.INVALID */.wu.INVALID,
|
|
4845
5253
|
errors: errors.map((error)=>({
|
|
4846
5254
|
field: 'flow',
|
|
4847
5255
|
...error
|
|
@@ -4852,13 +5260,12 @@ class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import
|
|
|
4852
5260
|
const clash = await (await this.provider.getRepository(_entities_flow_definition_entity_js__rspack_import_7/* .FlowDefinition */.G)).findOne({
|
|
4853
5261
|
where: {
|
|
4854
5262
|
slug: settings.slug
|
|
4855
|
-
}
|
|
4856
|
-
withDeleted: true
|
|
5263
|
+
}
|
|
4857
5264
|
});
|
|
4858
5265
|
if (clash && clash.id !== selfId) {
|
|
4859
5266
|
throw new _nestjs_common__rspack_import_4.ConflictException({
|
|
4860
5267
|
message: `The URL name '${settings.slug}' is already used by another flow.`,
|
|
4861
|
-
messageKey:
|
|
5268
|
+
messageKey: _config_message_keys_js__rspack_import_16/* .FLOW_MESSAGES.SLUG_TAKEN */.wu.SLUG_TAKEN,
|
|
4862
5269
|
messageVariables: {
|
|
4863
5270
|
slug: settings.slug
|
|
4864
5271
|
}
|
|
@@ -4890,7 +5297,7 @@ class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import
|
|
|
4890
5297
|
dto
|
|
4891
5298
|
]){
|
|
4892
5299
|
await this.assertSaveable(toFlowSettings(d), user);
|
|
4893
|
-
d.nodes = (0,
|
|
5300
|
+
d.nodes = (0,_flow_engine_flow_api_key_js__rspack_import_10/* .sealApiKeys */.ue)(d.nodes);
|
|
4894
5301
|
d.version = 0;
|
|
4895
5302
|
}
|
|
4896
5303
|
}
|
|
@@ -4911,7 +5318,7 @@ class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import
|
|
|
4911
5318
|
isActive: d.isActive ?? existing.isActive,
|
|
4912
5319
|
version: existing.version
|
|
4913
5320
|
});
|
|
4914
|
-
if (d.nodes) d.nodes = (0,
|
|
5321
|
+
if (d.nodes) d.nodes = (0,_flow_engine_flow_api_key_js__rspack_import_10/* .sealApiKeys */.ue)(d.nodes);
|
|
4915
5322
|
}
|
|
4916
5323
|
}
|
|
4917
5324
|
/** A published flow keeps running as published: the edits go to its draft (none when they match what is live). */ async convertSingleDtoToEntity(dto, user) {
|
|
@@ -4935,7 +5342,7 @@ class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import
|
|
|
4935
5342
|
const published = isPublished(existing);
|
|
4936
5343
|
if (published && !existing.draft) throw new _nestjs_common__rspack_import_4.BadRequestException({
|
|
4937
5344
|
message: 'There are no changes to publish.',
|
|
4938
|
-
messageKey:
|
|
5345
|
+
messageKey: _config_message_keys_js__rspack_import_16/* .FLOW_MESSAGES.NOTHING_TO_PUBLISH */.wu.NOTHING_TO_PUBLISH
|
|
4939
5346
|
});
|
|
4940
5347
|
const next = workingCopy(existing);
|
|
4941
5348
|
const settings = toFlowSettings(next);
|
|
@@ -4948,7 +5355,7 @@ class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import
|
|
|
4948
5355
|
try {
|
|
4949
5356
|
await qr.connect();
|
|
4950
5357
|
await qr.startTransaction();
|
|
4951
|
-
const versions = qr.manager.getRepository(
|
|
5358
|
+
const versions = qr.manager.getRepository(_entities_flow_version_entity_js__rspack_import_9/* .FlowVersion */.p);
|
|
4952
5359
|
Object.assign(existing, next, {
|
|
4953
5360
|
draft: null,
|
|
4954
5361
|
version: existing.version + 1,
|
|
@@ -4978,7 +5385,7 @@ class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import
|
|
|
4978
5385
|
}
|
|
4979
5386
|
/** Newest first, without the definitions (`getVersion` returns one in full). */ async listVersions(flowId) {
|
|
4980
5387
|
await this.findOrFail(flowId);
|
|
4981
|
-
return (await this.provider.getRepository(
|
|
5388
|
+
return (await this.provider.getRepository(_entities_flow_version_entity_js__rspack_import_9/* .FlowVersion */.p)).find({
|
|
4982
5389
|
where: {
|
|
4983
5390
|
flowId
|
|
4984
5391
|
},
|
|
@@ -4996,7 +5403,7 @@ class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import
|
|
|
4996
5403
|
});
|
|
4997
5404
|
}
|
|
4998
5405
|
async getVersion(flowId, version) {
|
|
4999
|
-
const row = await (await this.provider.getRepository(
|
|
5406
|
+
const row = await (await this.provider.getRepository(_entities_flow_version_entity_js__rspack_import_9/* .FlowVersion */.p)).findOne({
|
|
5000
5407
|
where: {
|
|
5001
5408
|
flowId,
|
|
5002
5409
|
version
|
|
@@ -5004,7 +5411,7 @@ class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import
|
|
|
5004
5411
|
});
|
|
5005
5412
|
if (!row) throw new _nestjs_common__rspack_import_4.NotFoundException({
|
|
5006
5413
|
message: `Version ${version} of this flow was not found.`,
|
|
5007
|
-
messageKey:
|
|
5414
|
+
messageKey: _config_message_keys_js__rspack_import_16/* .FLOW_MESSAGES.VERSION_NOT_FOUND */.wu.VERSION_NOT_FOUND,
|
|
5008
5415
|
messageVariables: {
|
|
5009
5416
|
version
|
|
5010
5417
|
}
|
|
@@ -5026,7 +5433,7 @@ class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import
|
|
|
5026
5433
|
const flow = await this.findOrFail(flowId);
|
|
5027
5434
|
if (!flow.draft) throw new _nestjs_common__rspack_import_4.BadRequestException({
|
|
5028
5435
|
message: 'This flow has no unpublished changes.',
|
|
5029
|
-
messageKey:
|
|
5436
|
+
messageKey: _config_message_keys_js__rspack_import_16/* .FLOW_MESSAGES.NO_DRAFT */.wu.NO_DRAFT
|
|
5030
5437
|
});
|
|
5031
5438
|
flow.draft = null;
|
|
5032
5439
|
return this.saveDraft(flow, user);
|
|
@@ -5039,25 +5446,66 @@ class FlowDefinitionService extends _flusys_nestjs_shared_classes__rspack_import
|
|
|
5039
5446
|
await this.invalidateCache();
|
|
5040
5447
|
return flow;
|
|
5041
5448
|
}
|
|
5042
|
-
async
|
|
5449
|
+
async delete(option, user) {
|
|
5450
|
+
if (option.type === 'restore') {
|
|
5451
|
+
throw new _nestjs_common__rspack_import_4.BadRequestException({
|
|
5452
|
+
message: 'A deleted flow is removed for good and cannot be restored.',
|
|
5453
|
+
messageKey: _config_message_keys_js__rspack_import_16/* .FLOW_MESSAGES.RESTORE_UNSUPPORTED */.wu.RESTORE_UNSUPPORTED
|
|
5454
|
+
});
|
|
5455
|
+
}
|
|
5456
|
+
const ids = Array.isArray(option.id) ? option.id : [
|
|
5457
|
+
option.id
|
|
5458
|
+
];
|
|
5459
|
+
const flows = await (await this.provider.getRepository(_entities_flow_definition_entity_js__rspack_import_7/* .FlowDefinition */.G)).find({
|
|
5460
|
+
select: {
|
|
5461
|
+
id: true,
|
|
5462
|
+
slug: true
|
|
5463
|
+
},
|
|
5464
|
+
where: {
|
|
5465
|
+
id: (0,typeorm__rspack_import_5.In)(ids)
|
|
5466
|
+
}
|
|
5467
|
+
});
|
|
5468
|
+
await super.delete({
|
|
5469
|
+
...option,
|
|
5470
|
+
type: 'permanent'
|
|
5471
|
+
}, user);
|
|
5472
|
+
await this.deprovisionPermissions(flows);
|
|
5473
|
+
return null;
|
|
5474
|
+
}
|
|
5475
|
+
async beforeDeleteOperation(dto, _user, _qr) {
|
|
5043
5476
|
const ids = Array.isArray(dto.id) ? dto.id : [
|
|
5044
5477
|
dto.id
|
|
5045
5478
|
];
|
|
5046
5479
|
const flows = await (await this.provider.getRepository(_entities_flow_definition_entity_js__rspack_import_7/* .FlowDefinition */.G)).find({
|
|
5047
5480
|
where: {
|
|
5048
5481
|
id: (0,typeorm__rspack_import_5.In)(ids)
|
|
5049
|
-
}
|
|
5050
|
-
withDeleted: true
|
|
5482
|
+
}
|
|
5051
5483
|
});
|
|
5052
5484
|
if (flows.length !== ids.length) throw flowNotFound();
|
|
5053
|
-
if (dto.type === 'restore') return;
|
|
5054
5485
|
for (const flow of flows)await this.assertNotCalled(flow.slug, ids);
|
|
5055
5486
|
}
|
|
5487
|
+
async afterDeleteOperation(deleted, _dto, _user, qr) {
|
|
5488
|
+
const flowId = (0,typeorm__rspack_import_5.In)(deleted.map((flow)=>flow.id));
|
|
5489
|
+
await qr.manager.getRepository(_entities_flow_version_entity_js__rspack_import_9/* .FlowVersion */.p).delete({
|
|
5490
|
+
flowId
|
|
5491
|
+
});
|
|
5492
|
+
await qr.manager.getRepository(_entities_flow_execution_entity_js__rspack_import_8/* .FlowExecution */.j).delete({
|
|
5493
|
+
flowId
|
|
5494
|
+
});
|
|
5495
|
+
}
|
|
5496
|
+
/** After the commit: a failure leaves an unused action behind, never a flow that is half deleted. */ async deprovisionPermissions(flows) {
|
|
5497
|
+
if (!this.actionRegistry || !flows.length) return;
|
|
5498
|
+
try {
|
|
5499
|
+
await this.actionRegistry.removeActionsByCodes(flows.map((flow)=>defaultFlowPermissionCode(flow.slug)));
|
|
5500
|
+
} catch (error) {
|
|
5501
|
+
logger.warn(`The execute permissions of deleted flows could not be removed: ${error.message}`);
|
|
5502
|
+
}
|
|
5503
|
+
}
|
|
5056
5504
|
/** The flow's execute permission, registered so it can be granted, when a published URL asks for it (a URL with its own rule names existing permissions). */ async provisionPermissions(flows) {
|
|
5057
5505
|
if (!this.actionRegistry) return;
|
|
5058
5506
|
const actions = new Map();
|
|
5059
5507
|
for (const flow of flows){
|
|
5060
|
-
for (const endpoint of (0,
|
|
5508
|
+
for (const endpoint of (0,_flow_engine_flow_types_js__rspack_import_12/* .flowEndpoints */.Xr)(flow)){
|
|
5061
5509
|
if (!asksExecutePermission(endpoint)) continue;
|
|
5062
5510
|
const code = defaultFlowPermissionCode(flow.slug);
|
|
5063
5511
|
if (!actions.has(code)) actions.set(code, endpoint.path ? `Run ${flow.name} (${endpoint.path})` : `Run ${flow.name}`);
|
|
@@ -5077,7 +5525,7 @@ FlowDefinitionService = _ts_decorate([
|
|
|
5077
5525
|
}),
|
|
5078
5526
|
_ts_param(0, (0,_nestjs_common__rspack_import_4.Inject)('CACHE_INSTANCE')),
|
|
5079
5527
|
_ts_param(1, (0,_nestjs_common__rspack_import_4.Inject)(_flusys_nestjs_shared_modules__rspack_import_3.UtilsService)),
|
|
5080
|
-
_ts_param(2, (0,_nestjs_common__rspack_import_4.Inject)(
|
|
5528
|
+
_ts_param(2, (0,_nestjs_common__rspack_import_4.Inject)(_entity_builder_datasource_provider_js__rspack_import_14/* .EntityBuilderDataSourceProvider */.D)),
|
|
5081
5529
|
_ts_param(3, (0,_nestjs_common__rspack_import_4.Inject)(_flusys_nestjs_shared_modules__rspack_import_3.SharedPermissionCacheService)),
|
|
5082
5530
|
_ts_param(4, (0,_nestjs_common__rspack_import_4.Optional)()),
|
|
5083
5531
|
_ts_param(4, (0,_nestjs_common__rspack_import_4.Inject)(_flusys_nestjs_shared__rspack_import_0.PERMISSION_ACTION_REGISTRY)),
|
|
@@ -5087,16 +5535,19 @@ FlowDefinitionService = _ts_decorate([
|
|
|
5087
5535
|
_ts_param(6, (0,_nestjs_common__rspack_import_4.Inject)(_flusys_nestjs_shared_interfaces__rspack_import_2.EMAIL_ADAPTER)),
|
|
5088
5536
|
_ts_param(7, (0,_nestjs_common__rspack_import_4.Optional)()),
|
|
5089
5537
|
_ts_param(7, (0,_nestjs_common__rspack_import_4.Inject)(_flusys_nestjs_shared_interfaces__rspack_import_2.COMPANY_ACCESS_RESOLVER)),
|
|
5538
|
+
_ts_param(8, (0,_nestjs_common__rspack_import_4.Optional)()),
|
|
5539
|
+
_ts_param(8, (0,_nestjs_common__rspack_import_4.Inject)(_entity_builder_config_service_js__rspack_import_13/* .EntityBuilderConfigService */.E)),
|
|
5090
5540
|
_ts_metadata("design:type", Function),
|
|
5091
5541
|
_ts_metadata("design:paramtypes", [
|
|
5092
5542
|
typeof _flusys_nestjs_shared_classes__rspack_import_1.HybridCache === "undefined" ? Object : _flusys_nestjs_shared_classes__rspack_import_1.HybridCache,
|
|
5093
5543
|
typeof _flusys_nestjs_shared_modules__rspack_import_3.UtilsService === "undefined" ? Object : _flusys_nestjs_shared_modules__rspack_import_3.UtilsService,
|
|
5094
|
-
typeof
|
|
5544
|
+
typeof _entity_builder_datasource_provider_js__rspack_import_14/* .EntityBuilderDataSourceProvider */.D === "undefined" ? Object : _entity_builder_datasource_provider_js__rspack_import_14/* .EntityBuilderDataSourceProvider */.D,
|
|
5095
5545
|
typeof _flusys_nestjs_shared_modules__rspack_import_3.SharedPermissionCacheService === "undefined" ? Object : _flusys_nestjs_shared_modules__rspack_import_3.SharedPermissionCacheService,
|
|
5096
5546
|
typeof IPermissionActionRegistry === "undefined" ? Object : IPermissionActionRegistry,
|
|
5097
5547
|
typeof INotificationAdapter === "undefined" ? Object : INotificationAdapter,
|
|
5098
5548
|
typeof IEmailAdapter === "undefined" ? Object : IEmailAdapter,
|
|
5099
|
-
typeof ICompanyAccessResolver === "undefined" ? Object : ICompanyAccessResolver
|
|
5549
|
+
typeof ICompanyAccessResolver === "undefined" ? Object : ICompanyAccessResolver,
|
|
5550
|
+
typeof _entity_builder_config_service_js__rspack_import_13/* .EntityBuilderConfigService */.E === "undefined" ? Object : _entity_builder_config_service_js__rspack_import_13/* .EntityBuilderConfigService */.E
|
|
5100
5551
|
])
|
|
5101
5552
|
], FlowDefinitionService);
|
|
5102
5553
|
/** What the `call_flow` nodes of a flow run: a slug, and the path of the Request step they start at (null: the flow's own URL). */ function calledTargets(nodes) {
|
|
@@ -5111,7 +5562,7 @@ FlowDefinitionService = _ts_decorate([
|
|
|
5111
5562
|
/** What a Call flow step checks about a flow: it runs only once published and switched on. */ function callableOf(flow) {
|
|
5112
5563
|
return {
|
|
5113
5564
|
isActive: flow.isActive && isPublished(flow),
|
|
5114
|
-
endpoints: (0,
|
|
5565
|
+
endpoints: (0,_flow_engine_flow_types_js__rspack_import_12/* .flowEndpoints */.Xr)(flow),
|
|
5115
5566
|
calls: calledSlugs(flow.nodes)
|
|
5116
5567
|
};
|
|
5117
5568
|
}
|
|
@@ -5141,9 +5592,9 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
5141
5592
|
/* import */ var _nestjs_common__rspack_import_0 = __webpack_require__(6157);
|
|
5142
5593
|
/* import */ var typeorm__rspack_import_1 = __webpack_require__(7158);
|
|
5143
5594
|
/* import */ var _entities_flow_execution_entity_js__rspack_import_2 = __webpack_require__(9641);
|
|
5144
|
-
/* import */ var _flow_engine_flow_engine_js__rspack_import_3 = __webpack_require__(
|
|
5145
|
-
/* import */ var
|
|
5146
|
-
/* import */ var
|
|
5595
|
+
/* import */ var _flow_engine_flow_engine_js__rspack_import_3 = __webpack_require__(3931);
|
|
5596
|
+
/* import */ var _flow_engine_redact_js__rspack_import_4 = __webpack_require__(2678);
|
|
5597
|
+
/* import */ var _entity_builder_datasource_provider_js__rspack_import_5 = __webpack_require__(424);
|
|
5147
5598
|
function _ts_decorate(decorators, target, key, desc) {
|
|
5148
5599
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
5149
5600
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
@@ -5187,7 +5638,7 @@ class FlowExecutionService {
|
|
|
5187
5638
|
/** Never throws: a logging problem must not change the answer the caller gets. */ async record(entry) {
|
|
5188
5639
|
try {
|
|
5189
5640
|
const repo = await this.repo();
|
|
5190
|
-
const redactedInput = (0,
|
|
5641
|
+
const redactedInput = (0,_flow_engine_redact_js__rspack_import_4/* .redactSensitive */.Qr)(entry.input);
|
|
5191
5642
|
const inputText = JSON.stringify(redactedInput ?? null);
|
|
5192
5643
|
await repo.save(repo.create({
|
|
5193
5644
|
flowId: entry.flowId,
|
|
@@ -5203,7 +5654,7 @@ class FlowExecutionService {
|
|
|
5203
5654
|
_truncated: true,
|
|
5204
5655
|
preview: inputText.slice(0, INPUT_LIMIT_CHARS)
|
|
5205
5656
|
} : redactedInput,
|
|
5206
|
-
output: (0,
|
|
5657
|
+
output: (0,_flow_engine_redact_js__rspack_import_4/* .preview */.VH)(entry.result.body, OUTPUT_LIMIT_CHARS),
|
|
5207
5658
|
error: entry.result.error?.slice(0, 2000) ?? null,
|
|
5208
5659
|
trace: entry.result.trace,
|
|
5209
5660
|
durationMs: entry.result.durationMs,
|
|
@@ -5297,10 +5748,10 @@ FlowExecutionService = _ts_decorate([
|
|
|
5297
5748
|
(0,_nestjs_common__rspack_import_0.Injectable)({
|
|
5298
5749
|
scope: _nestjs_common__rspack_import_0.Scope.REQUEST
|
|
5299
5750
|
}),
|
|
5300
|
-
_ts_param(0, (0,_nestjs_common__rspack_import_0.Inject)(
|
|
5751
|
+
_ts_param(0, (0,_nestjs_common__rspack_import_0.Inject)(_entity_builder_datasource_provider_js__rspack_import_5/* .EntityBuilderDataSourceProvider */.D)),
|
|
5301
5752
|
_ts_metadata("design:type", Function),
|
|
5302
5753
|
_ts_metadata("design:paramtypes", [
|
|
5303
|
-
typeof
|
|
5754
|
+
typeof _entity_builder_datasource_provider_js__rspack_import_5/* .EntityBuilderDataSourceProvider */.D === "undefined" ? Object : _entity_builder_datasource_provider_js__rspack_import_5/* .EntityBuilderDataSourceProvider */.D
|
|
5304
5755
|
])
|
|
5305
5756
|
], FlowExecutionService);
|
|
5306
5757
|
|
|
@@ -5343,7 +5794,7 @@ var message_keys = __webpack_require__(5732);
|
|
|
5343
5794
|
// EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/entities/flow-definition.entity.ts
|
|
5344
5795
|
var flow_definition_entity = __webpack_require__(3770);
|
|
5345
5796
|
// EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/flow-engine/flow-engine.ts + 6 modules
|
|
5346
|
-
var flow_engine = __webpack_require__(
|
|
5797
|
+
var flow_engine = __webpack_require__(3931);
|
|
5347
5798
|
// EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/flow-engine/flow.errors.ts
|
|
5348
5799
|
var flow_errors = __webpack_require__(8542);
|
|
5349
5800
|
// EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/flow-engine/flow-input.validator.ts
|
|
@@ -5573,8 +6024,8 @@ var entity_builder_config_service = __webpack_require__(4281);
|
|
|
5573
6024
|
var entity_builder_datasource_provider = __webpack_require__(424);
|
|
5574
6025
|
// EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/services/flow-definition.service.ts
|
|
5575
6026
|
var flow_definition_service = __webpack_require__(5229);
|
|
5576
|
-
// EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/services/generic-entity.service.ts
|
|
5577
|
-
var generic_entity_service = __webpack_require__(
|
|
6027
|
+
// EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/services/generic-entity.service.ts + 3 modules
|
|
6028
|
+
var generic_entity_service = __webpack_require__(3052);
|
|
5578
6029
|
// EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/services/generic-entity-service.factory.ts
|
|
5579
6030
|
var generic_entity_service_factory = __webpack_require__(7705);
|
|
5580
6031
|
;// CONCATENATED MODULE: ./projects/nestjs-entity-builder/src/services/flow-executor.service.ts
|
|
@@ -5643,6 +6094,7 @@ class FlowExecutorService {
|
|
|
5643
6094
|
email;
|
|
5644
6095
|
branchHierarchy;
|
|
5645
6096
|
logger = new common_.Logger(FlowExecutorService.name);
|
|
6097
|
+
/** Sends still going out after their answers, for `whenPublished`. */ inFlight = new Set();
|
|
5646
6098
|
constructor(provider, ruleEngine, moduleRef, permissionCache, config, companyAccess, notifications, email, branchHierarchy){
|
|
5647
6099
|
this.provider = provider;
|
|
5648
6100
|
this.ruleEngine = ruleEngine;
|
|
@@ -5736,17 +6188,17 @@ class FlowExecutorService {
|
|
|
5736
6188
|
state.ownsTransaction = false;
|
|
5737
6189
|
await qr.release().catch(()=>undefined);
|
|
5738
6190
|
}
|
|
5739
|
-
if (state.mode === 'live')
|
|
6191
|
+
if (state.mode === 'live') this.publishAfterCommit(state, state.pendingEvents.splice(0));
|
|
5740
6192
|
}
|
|
5741
|
-
|
|
5742
|
-
if (await this.provider.getDriver() === 'postgres') await qr.query(
|
|
6193
|
+
async limitStatementTime(qr, timeoutMs) {
|
|
6194
|
+
if (await this.provider.getDriver() === 'postgres') await qr.query((0,generic_entity_service/* .statementTimeoutSql */.xV)(timeoutMs));
|
|
5743
6195
|
}
|
|
5744
6196
|
async commitProgress(state) {
|
|
5745
6197
|
const qr = state.qr;
|
|
5746
6198
|
await qr.commitTransaction();
|
|
5747
6199
|
await qr.startTransaction();
|
|
5748
6200
|
await this.limitStatementTime(qr, state.deadline - Date.now());
|
|
5749
|
-
|
|
6201
|
+
this.publishAfterCommit(state, state.pendingEvents.splice(0));
|
|
5750
6202
|
}
|
|
5751
6203
|
/** A multi-record step outside a transactional run: its own transaction, its record events published after it commits. */ async atomic(state, work) {
|
|
5752
6204
|
if (state.qr) return work();
|
|
@@ -5757,6 +6209,7 @@ class FlowExecutorService {
|
|
|
5757
6209
|
try {
|
|
5758
6210
|
await qr.connect();
|
|
5759
6211
|
await qr.startTransaction();
|
|
6212
|
+
await this.limitStatementTime(qr, state.deadline - Date.now());
|
|
5760
6213
|
state.qr = qr;
|
|
5761
6214
|
result = await work();
|
|
5762
6215
|
await qr.commitTransaction();
|
|
@@ -5769,7 +6222,7 @@ class FlowExecutorService {
|
|
|
5769
6222
|
state.qr = undefined;
|
|
5770
6223
|
await qr.release().catch(()=>undefined);
|
|
5771
6224
|
}
|
|
5772
|
-
|
|
6225
|
+
this.publishAfterCommit(state, state.pendingEvents.splice(queued));
|
|
5773
6226
|
return result;
|
|
5774
6227
|
}
|
|
5775
6228
|
/**
|
|
@@ -5800,7 +6253,7 @@ class FlowExecutorService {
|
|
|
5800
6253
|
}, `Flow '${slug}' has no URL '${endpoint ?? ''}'.`);
|
|
5801
6254
|
}
|
|
5802
6255
|
await this.assertMayCall(flow, start, parent, env);
|
|
5803
|
-
const checked = (0,flow_input_validator/* .validateEndpointInput */.
|
|
6256
|
+
const checked = (0,flow_input_validator/* .validateEndpointInput */.GP)(start, input);
|
|
5804
6257
|
if (checked.errors.length) {
|
|
5805
6258
|
throw new flow_errors/* .FlowHttpError */.hk(400, 'Validation failed', checked.errors, {
|
|
5806
6259
|
messageKey: message_keys/* .FLOW_MESSAGES.INPUT_INVALID */.wu.INPUT_INVALID,
|
|
@@ -5830,6 +6283,9 @@ class FlowExecutorService {
|
|
|
5830
6283
|
flow.slug
|
|
5831
6284
|
],
|
|
5832
6285
|
pendingEvents: parent.qr ? parent.pendingEvents : [],
|
|
6286
|
+
publisher: parent.publisher ??= {
|
|
6287
|
+
chain: Promise.resolve()
|
|
6288
|
+
},
|
|
5833
6289
|
trace: []
|
|
5834
6290
|
};
|
|
5835
6291
|
// A failed call inside the caller's transaction is rolled back with it (or to the step's savepoint), so its queued events must go too.
|
|
@@ -5864,7 +6320,6 @@ class FlowExecutorService {
|
|
|
5864
6320
|
where: {
|
|
5865
6321
|
slug,
|
|
5866
6322
|
isActive: true,
|
|
5867
|
-
deletedAt: (0,external_typeorm_.IsNull)(),
|
|
5868
6323
|
version: (0,external_typeorm_.MoreThan)(0)
|
|
5869
6324
|
}
|
|
5870
6325
|
});
|
|
@@ -5890,18 +6345,23 @@ class FlowExecutorService {
|
|
|
5890
6345
|
}
|
|
5891
6346
|
createEnv() {
|
|
5892
6347
|
const entityServices = new Map();
|
|
6348
|
+
/** One factory per run: one DI context, and related entities built once for every entity the run writes. */ let factory;
|
|
5893
6349
|
/** One lookup per user for the whole run, however many steps ask. */ const access = new Map();
|
|
5894
6350
|
let permissions;
|
|
5895
6351
|
const env = {
|
|
5896
6352
|
ruleEngine: this.ruleEngine,
|
|
5897
6353
|
allowPrivateHttp: this.config.getConfig()?.flows?.allowPrivateHttp === true || process.env['ENTITY_BUILDER_FLOW_ALLOW_PRIVATE_HTTP'] === 'true',
|
|
6354
|
+
codeModules: this.config.getCodeModules(),
|
|
5898
6355
|
entityService: (entityCode, queryRunner)=>{
|
|
5899
6356
|
if (!entityServices.has(entityCode)) {
|
|
5900
6357
|
const loading = (async ()=>{
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
6358
|
+
if (!factory) {
|
|
6359
|
+
factory = this.moduleRef.resolve(generic_entity_service_factory/* .GenericEntityServiceFactory */.G, core_.ContextIdFactory.create(), {
|
|
6360
|
+
strict: false
|
|
6361
|
+
});
|
|
6362
|
+
factory.catch(()=>factory = undefined);
|
|
6363
|
+
}
|
|
6364
|
+
return (await factory).forEntityCode(entityCode, queryRunner);
|
|
5905
6365
|
})();
|
|
5906
6366
|
entityServices.set(entityCode, loading);
|
|
5907
6367
|
// A failed load (e.g. on a transaction that later rolled back) must not be remembered for the rest of the run.
|
|
@@ -5948,6 +6408,23 @@ class FlowExecutorService {
|
|
|
5948
6408
|
};
|
|
5949
6409
|
return env;
|
|
5950
6410
|
}
|
|
6411
|
+
/**
|
|
6412
|
+
* Queues `events` behind the ones this run already queued, so they go out in commit order, without holding up
|
|
6413
|
+
* the answer. Runs never wait on each other (one slow listener cannot stall another caller's events). Nothing
|
|
6414
|
+
* to wait for: a failing publish or send is logged, never turned into an error.
|
|
6415
|
+
*/ publishAfterCommit(state, events) {
|
|
6416
|
+
if (!events.length) return;
|
|
6417
|
+
const publisher = state.publisher ??= {
|
|
6418
|
+
chain: Promise.resolve()
|
|
6419
|
+
};
|
|
6420
|
+
const sent = publisher.chain.then(()=>this.publishPendingEvents(events, state.user));
|
|
6421
|
+
publisher.chain = sent;
|
|
6422
|
+
this.inFlight.add(sent);
|
|
6423
|
+
void sent.finally(()=>this.inFlight.delete(sent));
|
|
6424
|
+
}
|
|
6425
|
+
/** Resolves once everything queued so far went out (or failed and was logged). */ async whenPublished() {
|
|
6426
|
+
while(this.inFlight.size)await Promise.all(this.inFlight);
|
|
6427
|
+
}
|
|
5951
6428
|
/** After commit, so listeners see committed data and messages go out only for what was saved. A failing publish or send never turns a completed run into an error. */ async publishPendingEvents(events, user) {
|
|
5952
6429
|
for (const event of events){
|
|
5953
6430
|
try {
|
|
@@ -6042,17 +6519,20 @@ FlowExecutorService = _ts_decorate([
|
|
|
6042
6519
|
|
|
6043
6520
|
},
|
|
6044
6521
|
2300(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
6045
|
-
/* import */ var
|
|
6046
|
-
/* import */ var
|
|
6047
|
-
/* import */ var
|
|
6048
|
-
/* import */ var
|
|
6049
|
-
/* import */ var
|
|
6050
|
-
/* import */ var
|
|
6051
|
-
/* import */ var
|
|
6052
|
-
/* import */ var
|
|
6053
|
-
/* import */ var
|
|
6054
|
-
/* import */ var
|
|
6055
|
-
/* import */ var
|
|
6522
|
+
/* import */ var _flusys_nestjs_shared_classes__rspack_import_0 = __webpack_require__(6937);
|
|
6523
|
+
/* import */ var _flusys_nestjs_shared_modules__rspack_import_1 = __webpack_require__(1236);
|
|
6524
|
+
/* import */ var _flusys_nestjs_shared_utils__rspack_import_2 = __webpack_require__(9216);
|
|
6525
|
+
/* import */ var _nestjs_common__rspack_import_3 = __webpack_require__(6157);
|
|
6526
|
+
/* import */ var typeorm__rspack_import_4 = __webpack_require__(7158);
|
|
6527
|
+
/* import */ var _config_event_actions_js__rspack_import_13 = __webpack_require__(982);
|
|
6528
|
+
/* import */ var _config_message_keys_js__rspack_import_12 = __webpack_require__(5732);
|
|
6529
|
+
/* import */ var _entities_flow_definition_entity_js__rspack_import_5 = __webpack_require__(3770);
|
|
6530
|
+
/* import */ var _flow_engine_flow_input_validator_js__rspack_import_6 = __webpack_require__(6390);
|
|
6531
|
+
/* import */ var _flow_engine_flow_types_js__rspack_import_7 = __webpack_require__(6256);
|
|
6532
|
+
/* import */ var _entity_builder_datasource_provider_js__rspack_import_8 = __webpack_require__(424);
|
|
6533
|
+
/* import */ var _flow_definition_service_js__rspack_import_9 = __webpack_require__(5229);
|
|
6534
|
+
/* import */ var _flow_execution_service_js__rspack_import_10 = __webpack_require__(7810);
|
|
6535
|
+
/* import */ var _flow_executor_service_js__rspack_import_11 = __webpack_require__(3908);
|
|
6056
6536
|
function _ts_decorate(decorators, target, key, desc) {
|
|
6057
6537
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
6058
6538
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
@@ -6087,6 +6567,10 @@ function _ts_param(paramIndex, decorator) {
|
|
|
6087
6567
|
|
|
6088
6568
|
|
|
6089
6569
|
|
|
6570
|
+
|
|
6571
|
+
|
|
6572
|
+
|
|
6573
|
+
const ACTIVE_FLOW_TTL_MS = 60_000;
|
|
6090
6574
|
const HIDDEN_HEADERS = new Set([
|
|
6091
6575
|
'authorization',
|
|
6092
6576
|
'cookie',
|
|
@@ -6150,7 +6634,7 @@ const inputRejected = (errors)=>({
|
|
|
6150
6634
|
body: {
|
|
6151
6635
|
success: false,
|
|
6152
6636
|
message: 'Validation failed',
|
|
6153
|
-
messageKey:
|
|
6637
|
+
messageKey: _config_message_keys_js__rspack_import_12/* .FLOW_MESSAGES.INPUT_INVALID */.wu.INPUT_INVALID,
|
|
6154
6638
|
errors
|
|
6155
6639
|
},
|
|
6156
6640
|
trace: [],
|
|
@@ -6161,34 +6645,64 @@ class FlowRuntimeService {
|
|
|
6161
6645
|
executor;
|
|
6162
6646
|
executions;
|
|
6163
6647
|
definitions;
|
|
6164
|
-
|
|
6648
|
+
cache;
|
|
6649
|
+
utils;
|
|
6650
|
+
constructor(provider, executor, executions, definitions, cache, utils){
|
|
6165
6651
|
this.provider = provider;
|
|
6166
6652
|
this.executor = executor;
|
|
6167
6653
|
this.executions = executions;
|
|
6168
6654
|
this.definitions = definitions;
|
|
6655
|
+
this.cache = cache;
|
|
6656
|
+
this.utils = utils;
|
|
6169
6657
|
}
|
|
6170
|
-
/**
|
|
6171
|
-
|
|
6658
|
+
/**
|
|
6659
|
+
* A flow never published is not live yet: it looks the same as an unknown one. Cached under the flow definitions'
|
|
6660
|
+
* stamp, which every flow save / publish / delete, schema change and bundle import moves - so no caller runs a
|
|
6661
|
+
* replaced flow. Without a reachable cache the read goes straight to the database.
|
|
6662
|
+
*/ async findActive(slug) {
|
|
6663
|
+
const key = await this.activeFlowKey(slug);
|
|
6664
|
+
if (key) {
|
|
6665
|
+
const cached = await this.cache.get(key).catch(()=>undefined);
|
|
6666
|
+
if (cached) return cached.flow;
|
|
6667
|
+
}
|
|
6668
|
+
const flow = await (await this.provider.getRepository(_entities_flow_definition_entity_js__rspack_import_5/* .FlowDefinition */.G)).findOne({
|
|
6172
6669
|
where: {
|
|
6173
6670
|
slug,
|
|
6174
6671
|
isActive: true,
|
|
6175
|
-
|
|
6176
|
-
version: (0,typeorm__rspack_import_2.MoreThan)(0)
|
|
6672
|
+
version: (0,typeorm__rspack_import_4.MoreThan)(0)
|
|
6177
6673
|
}
|
|
6178
6674
|
});
|
|
6675
|
+
if (key) await this.cache.set(key, {
|
|
6676
|
+
flow
|
|
6677
|
+
}, ACTIVE_FLOW_TTL_MS).catch(()=>undefined);
|
|
6678
|
+
return flow;
|
|
6679
|
+
}
|
|
6680
|
+
/** Read BEFORE the database, like ApiService: a write committing mid-read moves the stamp, so the stale row lands under a key nobody asks for. */ async activeFlowKey(slug) {
|
|
6681
|
+
if (!this.cache || !this.utils) return null;
|
|
6682
|
+
try {
|
|
6683
|
+
const tenant = (0,_flusys_nestjs_shared_utils__rspack_import_2.cacheTenantId)(this.provider);
|
|
6684
|
+
const stamp = await this.utils.getCacheStamp(this.cache, [
|
|
6685
|
+
_config_event_actions_js__rspack_import_13/* .ENTITY_BUILDER_EVENT_ENTITIES.FLOW_DEFINITION */.P.FLOW_DEFINITION
|
|
6686
|
+
], tenant);
|
|
6687
|
+
return this.utils.getCacheKey(_config_event_actions_js__rspack_import_13/* .ENTITY_BUILDER_EVENT_ENTITIES.FLOW_DEFINITION */.P.FLOW_DEFINITION, {
|
|
6688
|
+
activeSlug: slug
|
|
6689
|
+
}, undefined, tenant, stamp);
|
|
6690
|
+
} catch {
|
|
6691
|
+
return null;
|
|
6692
|
+
}
|
|
6179
6693
|
}
|
|
6180
6694
|
/** The tenant of this request ('' for a single database): tenant databases cloned from one template share flow ids. */ tenantScope() {
|
|
6181
|
-
return (0,
|
|
6695
|
+
return (0,_flusys_nestjs_shared_utils__rspack_import_2.cacheTenantId)(this.provider) ?? '';
|
|
6182
6696
|
}
|
|
6183
6697
|
/** The public entry: a caller hit `POST api-flows/:slug` or `POST api-flows/:slug/:endpoint`. */ async handle(flow, endpoint, body, caller, user) {
|
|
6184
6698
|
const startedAt = new Date();
|
|
6185
|
-
const checked = (0,
|
|
6699
|
+
const checked = (0,_flow_engine_flow_input_validator_js__rspack_import_6/* .validateEndpointInput */.GP)(endpoint, body);
|
|
6186
6700
|
if (checked.errors.length) {
|
|
6187
6701
|
const result = inputRejected(checked.errors);
|
|
6188
|
-
|
|
6702
|
+
this.logWebhook(flow, endpoint, body, result, startedAt, user, caller);
|
|
6189
6703
|
return result;
|
|
6190
6704
|
}
|
|
6191
|
-
const result = await this.executor.run((0,
|
|
6705
|
+
const result = await this.executor.run((0,_flow_definition_service_js__rspack_import_9/* .toFlowSettings */.RK)(flow), {
|
|
6192
6706
|
mode: 'live',
|
|
6193
6707
|
user,
|
|
6194
6708
|
input: checked.values,
|
|
@@ -6197,7 +6711,7 @@ class FlowRuntimeService {
|
|
|
6197
6711
|
authorization: callerAuthorization(caller, user),
|
|
6198
6712
|
exposeDetails: false
|
|
6199
6713
|
});
|
|
6200
|
-
|
|
6714
|
+
this.logWebhook(flow, endpoint, checked.values, result, startedAt, user, caller);
|
|
6201
6715
|
return result;
|
|
6202
6716
|
}
|
|
6203
6717
|
/** The designer's "Test run": a saved flow or an unsaved draft. */ async testRun(dto, user, caller) {
|
|
@@ -6208,17 +6722,17 @@ class FlowRuntimeService {
|
|
|
6208
6722
|
}
|
|
6209
6723
|
if (dto.definition) {
|
|
6210
6724
|
// The designer's current draft, even for a saved flow: `flow` above is kept only to attribute the run.
|
|
6211
|
-
settings = (0,
|
|
6725
|
+
settings = (0,_flow_definition_service_js__rspack_import_9/* .toFlowSettings */.RK)(dto.definition);
|
|
6212
6726
|
} else if (flow) {
|
|
6213
|
-
settings = (0,
|
|
6727
|
+
settings = (0,_flow_definition_service_js__rspack_import_9/* .toFlowSettings */.RK)(flow);
|
|
6214
6728
|
} else {
|
|
6215
|
-
throw new
|
|
6729
|
+
throw new _nestjs_common__rspack_import_3.BadRequestException({
|
|
6216
6730
|
message: 'Send a flowId or a definition to test.',
|
|
6217
|
-
messageKey:
|
|
6731
|
+
messageKey: _config_message_keys_js__rspack_import_12/* .FLOW_MESSAGES.NOTHING_TO_TEST */.wu.NOTHING_TO_TEST
|
|
6218
6732
|
});
|
|
6219
6733
|
}
|
|
6220
6734
|
const mode = dto.mode ?? 'dry_run';
|
|
6221
|
-
const endpoint = (0,
|
|
6735
|
+
const endpoint = (0,_flow_engine_flow_types_js__rspack_import_7/* .findEndpoint */.vS)(settings, dto.endpoint);
|
|
6222
6736
|
// Checked before anything runs.
|
|
6223
6737
|
await this.definitions.assertMayTest(settings, endpoint, user, {
|
|
6224
6738
|
mode,
|
|
@@ -6226,9 +6740,9 @@ class FlowRuntimeService {
|
|
|
6226
6740
|
saved: !!flow
|
|
6227
6741
|
});
|
|
6228
6742
|
if (!endpoint) {
|
|
6229
|
-
throw new
|
|
6743
|
+
throw new _nestjs_common__rspack_import_3.BadRequestException({
|
|
6230
6744
|
message: `This flow has no URL '${dto.endpoint ?? ''}'.`,
|
|
6231
|
-
messageKey:
|
|
6745
|
+
messageKey: _config_message_keys_js__rspack_import_12/* .FLOW_MESSAGES.ENDPOINT_NOT_FOUND */.wu.ENDPOINT_NOT_FOUND,
|
|
6232
6746
|
messageVariables: {
|
|
6233
6747
|
endpoint: dto.endpoint ?? ''
|
|
6234
6748
|
}
|
|
@@ -6236,9 +6750,9 @@ class FlowRuntimeService {
|
|
|
6236
6750
|
}
|
|
6237
6751
|
const validation = await this.definitions.validate(settings);
|
|
6238
6752
|
if (validation.errors.length) {
|
|
6239
|
-
throw new
|
|
6753
|
+
throw new _nestjs_common__rspack_import_3.BadRequestException({
|
|
6240
6754
|
message: 'The flow has problems.',
|
|
6241
|
-
messageKey:
|
|
6755
|
+
messageKey: _config_message_keys_js__rspack_import_12/* .FLOW_MESSAGES.INVALID */.wu.INVALID,
|
|
6242
6756
|
errors: validation.errors.map((error)=>({
|
|
6243
6757
|
field: 'flow',
|
|
6244
6758
|
...error
|
|
@@ -6248,7 +6762,7 @@ class FlowRuntimeService {
|
|
|
6248
6762
|
const startedAt = new Date();
|
|
6249
6763
|
const simulated = simulatedCaller(caller, dto.request);
|
|
6250
6764
|
const body = dto.input ?? (endpoint.bodyType === 'list' ? [] : {});
|
|
6251
|
-
const checked = (0,
|
|
6765
|
+
const checked = (0,_flow_engine_flow_input_validator_js__rspack_import_6/* .validateEndpointInput */.GP)(endpoint, body);
|
|
6252
6766
|
const result = checked.errors.length ? inputRejected(checked.errors) : await this.executor.run(settings, {
|
|
6253
6767
|
mode,
|
|
6254
6768
|
user,
|
|
@@ -6277,8 +6791,8 @@ class FlowRuntimeService {
|
|
|
6277
6791
|
warnings: validation.warnings
|
|
6278
6792
|
};
|
|
6279
6793
|
}
|
|
6280
|
-
|
|
6281
|
-
|
|
6794
|
+
/** Written after the answer is on its way: `record` never throws, and a live caller never waits for the log. */ logWebhook(flow, endpoint, input, result, startedAt, user, caller) {
|
|
6795
|
+
void this.executions.record({
|
|
6282
6796
|
flowId: flow.id,
|
|
6283
6797
|
flowSlug: flow.slug,
|
|
6284
6798
|
flowVersion: flow.version,
|
|
@@ -6295,19 +6809,25 @@ class FlowRuntimeService {
|
|
|
6295
6809
|
}
|
|
6296
6810
|
}
|
|
6297
6811
|
FlowRuntimeService = _ts_decorate([
|
|
6298
|
-
(0,
|
|
6299
|
-
scope:
|
|
6812
|
+
(0,_nestjs_common__rspack_import_3.Injectable)({
|
|
6813
|
+
scope: _nestjs_common__rspack_import_3.Scope.REQUEST
|
|
6300
6814
|
}),
|
|
6301
|
-
_ts_param(0, (0,
|
|
6302
|
-
_ts_param(1, (0,
|
|
6303
|
-
_ts_param(2, (0,
|
|
6304
|
-
_ts_param(3, (0,
|
|
6815
|
+
_ts_param(0, (0,_nestjs_common__rspack_import_3.Inject)(_entity_builder_datasource_provider_js__rspack_import_8/* .EntityBuilderDataSourceProvider */.D)),
|
|
6816
|
+
_ts_param(1, (0,_nestjs_common__rspack_import_3.Inject)(_flow_executor_service_js__rspack_import_11/* .FlowExecutorService */.F)),
|
|
6817
|
+
_ts_param(2, (0,_nestjs_common__rspack_import_3.Inject)(_flow_execution_service_js__rspack_import_10/* .FlowExecutionService */.A)),
|
|
6818
|
+
_ts_param(3, (0,_nestjs_common__rspack_import_3.Inject)(_flow_definition_service_js__rspack_import_9/* .FlowDefinitionService */.Tk)),
|
|
6819
|
+
_ts_param(4, (0,_nestjs_common__rspack_import_3.Optional)()),
|
|
6820
|
+
_ts_param(4, (0,_nestjs_common__rspack_import_3.Inject)('CACHE_INSTANCE')),
|
|
6821
|
+
_ts_param(5, (0,_nestjs_common__rspack_import_3.Optional)()),
|
|
6822
|
+
_ts_param(5, (0,_nestjs_common__rspack_import_3.Inject)(_flusys_nestjs_shared_modules__rspack_import_1.UtilsService)),
|
|
6305
6823
|
_ts_metadata("design:type", Function),
|
|
6306
6824
|
_ts_metadata("design:paramtypes", [
|
|
6307
|
-
typeof
|
|
6308
|
-
typeof
|
|
6309
|
-
typeof
|
|
6310
|
-
typeof
|
|
6825
|
+
typeof _entity_builder_datasource_provider_js__rspack_import_8/* .EntityBuilderDataSourceProvider */.D === "undefined" ? Object : _entity_builder_datasource_provider_js__rspack_import_8/* .EntityBuilderDataSourceProvider */.D,
|
|
6826
|
+
typeof _flow_executor_service_js__rspack_import_11/* .FlowExecutorService */.F === "undefined" ? Object : _flow_executor_service_js__rspack_import_11/* .FlowExecutorService */.F,
|
|
6827
|
+
typeof _flow_execution_service_js__rspack_import_10/* .FlowExecutionService */.A === "undefined" ? Object : _flow_execution_service_js__rspack_import_10/* .FlowExecutionService */.A,
|
|
6828
|
+
typeof _flow_definition_service_js__rspack_import_9/* .FlowDefinitionService */.Tk === "undefined" ? Object : _flow_definition_service_js__rspack_import_9/* .FlowDefinitionService */.Tk,
|
|
6829
|
+
typeof _flusys_nestjs_shared_classes__rspack_import_0.HybridCache === "undefined" ? Object : _flusys_nestjs_shared_classes__rspack_import_0.HybridCache,
|
|
6830
|
+
typeof _flusys_nestjs_shared_modules__rspack_import_1.UtilsService === "undefined" ? Object : _flusys_nestjs_shared_modules__rspack_import_1.UtilsService
|
|
6311
6831
|
])
|
|
6312
6832
|
], FlowRuntimeService);
|
|
6313
6833
|
|
|
@@ -6320,13 +6840,12 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
6320
6840
|
},
|
|
6321
6841
|
7705(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
6322
6842
|
/* import */ var _nestjs_common__rspack_import_0 = __webpack_require__(6157);
|
|
6323
|
-
/* import */ var
|
|
6324
|
-
/* import */ var
|
|
6325
|
-
/* import */ var
|
|
6326
|
-
/* import */ var
|
|
6327
|
-
/* import */ var
|
|
6328
|
-
/* import */ var
|
|
6329
|
-
/* import */ var _schema_registry_service_js__rspack_import_6 = __webpack_require__(2134);
|
|
6843
|
+
/* import */ var _config_message_keys_js__rspack_import_6 = __webpack_require__(5732);
|
|
6844
|
+
/* import */ var _entities_entity_definition_entity_js__rspack_import_1 = __webpack_require__(3189);
|
|
6845
|
+
/* import */ var _entity_builder_config_service_js__rspack_import_2 = __webpack_require__(4281);
|
|
6846
|
+
/* import */ var _entity_builder_datasource_provider_js__rspack_import_3 = __webpack_require__(424);
|
|
6847
|
+
/* import */ var _generic_entity_service_js__rspack_import_4 = __webpack_require__(3052);
|
|
6848
|
+
/* import */ var _schema_registry_service_js__rspack_import_5 = __webpack_require__(2134);
|
|
6330
6849
|
function _ts_decorate(decorators, target, key, desc) {
|
|
6331
6850
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
6332
6851
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
@@ -6357,13 +6876,15 @@ function _ts_param(paramIndex, decorator) {
|
|
|
6357
6876
|
|
|
6358
6877
|
|
|
6359
6878
|
|
|
6360
|
-
|
|
6361
6879
|
class GenericEntityServiceFactory {
|
|
6362
6880
|
schemaRegistry;
|
|
6363
6881
|
dataSourceProvider;
|
|
6364
|
-
|
|
6882
|
+
config;
|
|
6883
|
+
/** A RELATION target, built once per factory however many rows and fields point at it. */ relatedById = new Map();
|
|
6884
|
+
constructor(schemaRegistry, dataSourceProvider, config){
|
|
6365
6885
|
this.schemaRegistry = schemaRegistry;
|
|
6366
6886
|
this.dataSourceProvider = dataSourceProvider;
|
|
6887
|
+
this.config = config;
|
|
6367
6888
|
}
|
|
6368
6889
|
/** `queryRunner`: an open transaction to read the metadata on, so the caller never needs a second pooled connection while holding one. */ async forEntityCode(entityCode, queryRunner) {
|
|
6369
6890
|
return this.build({
|
|
@@ -6375,44 +6896,49 @@ class GenericEntityServiceFactory {
|
|
|
6375
6896
|
id: entityDefinitionId
|
|
6376
6897
|
}, entityDefinitionId, queryRunner);
|
|
6377
6898
|
}
|
|
6899
|
+
related(entityDefinitionId, queryRunner) {
|
|
6900
|
+
let loading = this.relatedById.get(entityDefinitionId);
|
|
6901
|
+
if (!loading) {
|
|
6902
|
+
loading = this.forEntityDefinitionId(entityDefinitionId, queryRunner);
|
|
6903
|
+
this.relatedById.set(entityDefinitionId, loading);
|
|
6904
|
+
loading.catch(()=>this.relatedById.delete(entityDefinitionId));
|
|
6905
|
+
}
|
|
6906
|
+
return loading;
|
|
6907
|
+
}
|
|
6378
6908
|
async build(where, reference, queryRunner) {
|
|
6379
|
-
|
|
6909
|
+
await this.schemaRegistry.warmUp(queryRunner);
|
|
6910
|
+
const definitionRepository = queryRunner ? queryRunner.manager.getRepository(_entities_entity_definition_entity_js__rspack_import_1/* .EntityDefinition */.f) : await this.dataSourceProvider.getRepository(_entities_entity_definition_entity_js__rspack_import_1/* .EntityDefinition */.f);
|
|
6380
6911
|
const definition = await definitionRepository.findOne({
|
|
6381
6912
|
where
|
|
6382
6913
|
});
|
|
6383
6914
|
if (!definition) {
|
|
6384
6915
|
throw new _nestjs_common__rspack_import_0.NotFoundException({
|
|
6385
6916
|
message: `Entity '${reference}' not found.`,
|
|
6386
|
-
messageKey:
|
|
6917
|
+
messageKey: _config_message_keys_js__rspack_import_6/* .ENTITY_DEFINITION_MESSAGES.NOT_FOUND */.pL.NOT_FOUND,
|
|
6387
6918
|
messageVariables: {
|
|
6388
6919
|
id: reference
|
|
6389
6920
|
}
|
|
6390
6921
|
});
|
|
6391
6922
|
}
|
|
6392
|
-
const
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
entityDefinitionId: definition.id,
|
|
6396
|
-
deprecatedAt: (0,typeorm__rspack_import_1.IsNull)()
|
|
6397
|
-
},
|
|
6398
|
-
order: {
|
|
6399
|
-
order: 'ASC'
|
|
6400
|
-
}
|
|
6923
|
+
const { schema, fields } = await this.schemaRegistry.resolve(definition, queryRunner);
|
|
6924
|
+
return new _generic_entity_service_js__rspack_import_4/* .GenericEntityService */.GK(definition, schema, fields, this.dataSourceProvider, (targetId, runner)=>this.related(targetId, runner), {
|
|
6925
|
+
fastUpdates: this.config?.isFastUpdatesEnabled() ?? true
|
|
6401
6926
|
});
|
|
6402
|
-
const schema = await this.schemaRegistry.getSchema(definition.id, queryRunner);
|
|
6403
|
-
return new _generic_entity_service_js__rspack_import_5/* .GenericEntityService */.GK(definition, schema, fields, this.dataSourceProvider, (targetId, runner)=>this.forEntityDefinitionId(targetId, runner));
|
|
6404
6927
|
}
|
|
6405
6928
|
}
|
|
6406
6929
|
GenericEntityServiceFactory = _ts_decorate([
|
|
6407
6930
|
(0,_nestjs_common__rspack_import_0.Injectable)({
|
|
6408
6931
|
scope: _nestjs_common__rspack_import_0.Scope.REQUEST
|
|
6409
6932
|
}),
|
|
6410
|
-
_ts_param(0, (0,_nestjs_common__rspack_import_0.Inject)(
|
|
6411
|
-
_ts_param(1, (0,_nestjs_common__rspack_import_0.Inject)(
|
|
6933
|
+
_ts_param(0, (0,_nestjs_common__rspack_import_0.Inject)(_schema_registry_service_js__rspack_import_5/* .SchemaRegistryService */.C)),
|
|
6934
|
+
_ts_param(1, (0,_nestjs_common__rspack_import_0.Inject)(_entity_builder_datasource_provider_js__rspack_import_3/* .EntityBuilderDataSourceProvider */.D)),
|
|
6935
|
+
_ts_param(2, (0,_nestjs_common__rspack_import_0.Optional)()),
|
|
6936
|
+
_ts_param(2, (0,_nestjs_common__rspack_import_0.Inject)(_entity_builder_config_service_js__rspack_import_2/* .EntityBuilderConfigService */.E)),
|
|
6412
6937
|
_ts_metadata("design:type", Function),
|
|
6413
6938
|
_ts_metadata("design:paramtypes", [
|
|
6414
|
-
typeof
|
|
6415
|
-
typeof
|
|
6939
|
+
typeof _schema_registry_service_js__rspack_import_5/* .SchemaRegistryService */.C === "undefined" ? Object : _schema_registry_service_js__rspack_import_5/* .SchemaRegistryService */.C,
|
|
6940
|
+
typeof _entity_builder_datasource_provider_js__rspack_import_3/* .EntityBuilderDataSourceProvider */.D === "undefined" ? Object : _entity_builder_datasource_provider_js__rspack_import_3/* .EntityBuilderDataSourceProvider */.D,
|
|
6941
|
+
typeof _entity_builder_config_service_js__rspack_import_2/* .EntityBuilderConfigService */.E === "undefined" ? Object : _entity_builder_config_service_js__rspack_import_2/* .EntityBuilderConfigService */.E
|
|
6416
6942
|
])
|
|
6417
6943
|
], GenericEntityServiceFactory);
|
|
6418
6944
|
|
|
@@ -6422,19 +6948,80 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
6422
6948
|
|
|
6423
6949
|
|
|
6424
6950
|
},
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
|
|
6429
|
-
|
|
6430
|
-
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
|
|
6434
|
-
|
|
6435
|
-
|
|
6436
|
-
|
|
6951
|
+
3052(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
6952
|
+
|
|
6953
|
+
// EXPORTS
|
|
6954
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
6955
|
+
zb: () => (/* binding */ ACTOR_BRANCH_METADATA_KEY),
|
|
6956
|
+
GK: () => (/* binding */ GenericEntityService),
|
|
6957
|
+
DG: () => (/* binding */ actorMetadata),
|
|
6958
|
+
xV: () => (/* binding */ statementTimeoutSql)
|
|
6959
|
+
});
|
|
6960
|
+
|
|
6961
|
+
// EXTERNAL MODULE: external "@flusys/nestjs-shared/classes"
|
|
6962
|
+
var classes_ = __webpack_require__(6937);
|
|
6963
|
+
// EXTERNAL MODULE: external "@flusys/nestjs-shared/utils"
|
|
6964
|
+
var utils_ = __webpack_require__(9216);
|
|
6965
|
+
// EXTERNAL MODULE: external "@nestjs/common"
|
|
6966
|
+
var common_ = __webpack_require__(6157);
|
|
6967
|
+
// EXTERNAL MODULE: external "typeorm"
|
|
6968
|
+
var external_typeorm_ = __webpack_require__(7158);
|
|
6969
|
+
// EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/config/event-actions.ts
|
|
6970
|
+
var event_actions = __webpack_require__(982);
|
|
6971
|
+
// EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/config/message-keys.ts
|
|
6972
|
+
var message_keys = __webpack_require__(5732);
|
|
6973
|
+
// EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/enums/entity-status.enum.ts
|
|
6974
|
+
var entity_status_enum = __webpack_require__(1313);
|
|
6975
|
+
// EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/enums/field-type.enum.ts
|
|
6976
|
+
var field_type_enum = __webpack_require__(9614);
|
|
6977
|
+
// EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/interfaces/generic-record.interface.ts
|
|
6978
|
+
var generic_record_interface = __webpack_require__(3372);
|
|
6979
|
+
// EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/services/database-error.mapper.ts
|
|
6980
|
+
var database_error_mapper = __webpack_require__(8323);
|
|
6981
|
+
;// CONCATENATED MODULE: external "typeorm/persistence/Subject.js"
|
|
6982
|
+
|
|
6983
|
+
;// CONCATENATED MODULE: external "typeorm/persistence/SubjectExecutor.js"
|
|
6984
|
+
|
|
6985
|
+
;// CONCATENATED MODULE: ./projects/nestjs-entity-builder/src/services/save-loaded.ts
|
|
6986
|
+
|
|
6987
|
+
|
|
6988
|
+
/**
|
|
6989
|
+
* `manager.save(target, entity)` for a row the caller has already read in this transaction: TypeORM's own
|
|
6990
|
+
* persistence (`SubjectExecutor` - change detection, UPDATE of the changed columns, `updated_at`, reload of
|
|
6991
|
+
* generated values, nulls for nullable columns left out) run against `databaseEntity`, skipping the SELECT
|
|
6992
|
+
* `save()` would make to find it. `databaseEntity` must be the row as loaded, before `entity` was merged into it.
|
|
6993
|
+
* Entity-builder schemas have no relations, so the cascade / relation subject builders `save()` runs have nothing to do.
|
|
6994
|
+
*/ async function saveLoaded(queryRunner, target, entity, databaseEntity) {
|
|
6995
|
+
const metadata = queryRunner.connection.getMetadata(target).findInheritanceMetadata(entity);
|
|
6996
|
+
const subject = new __rspack_external_typeorm_persistence_Subject_js_50dfc7e4.Subject({
|
|
6997
|
+
metadata,
|
|
6998
|
+
entity,
|
|
6999
|
+
canBeInserted: true,
|
|
7000
|
+
canBeUpdated: true
|
|
7001
|
+
});
|
|
7002
|
+
subject.databaseEntity = databaseEntity;
|
|
7003
|
+
subject.databaseEntityLoaded = true;
|
|
7004
|
+
const executor = new __rspack_external_typeorm_persistence_SubjectExecutor_js_7cea48f1.SubjectExecutor(queryRunner, [
|
|
7005
|
+
subject
|
|
7006
|
+
]);
|
|
7007
|
+
if (!executor.hasExecutableOperations) return entity;
|
|
7008
|
+
const ownTransaction = !queryRunner.isTransactionActive;
|
|
7009
|
+
if (ownTransaction) await queryRunner.startTransaction();
|
|
7010
|
+
try {
|
|
7011
|
+
await executor.execute();
|
|
7012
|
+
if (ownTransaction) await queryRunner.commitTransaction();
|
|
7013
|
+
} catch (error) {
|
|
7014
|
+
if (ownTransaction) await queryRunner.rollbackTransaction();
|
|
7015
|
+
throw error;
|
|
7016
|
+
}
|
|
7017
|
+
return entity;
|
|
7018
|
+
}
|
|
6437
7019
|
|
|
7020
|
+
// EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/services/field-value-validator.ts
|
|
7021
|
+
var field_value_validator = __webpack_require__(3525);
|
|
7022
|
+
// EXTERNAL MODULE: ./projects/nestjs-entity-builder/src/services/schema-columns.ts
|
|
7023
|
+
var schema_columns = __webpack_require__(5313);
|
|
7024
|
+
;// CONCATENATED MODULE: ./projects/nestjs-entity-builder/src/services/generic-entity.service.ts
|
|
6438
7025
|
|
|
6439
7026
|
|
|
6440
7027
|
|
|
@@ -6445,6 +7032,17 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
6445
7032
|
|
|
6446
7033
|
|
|
6447
7034
|
|
|
7035
|
+
|
|
7036
|
+
|
|
7037
|
+
|
|
7038
|
+
const IDLE_IN_TRANSACTION_MARGIN_MS = 10_000;
|
|
7039
|
+
/**
|
|
7040
|
+
* `SET LOCAL` lasts one transaction, so every transaction a flow run opens sets it again. The idle limit ends a
|
|
7041
|
+
* transaction left open past the run's time (a connection never released) instead of letting it hold its locks.
|
|
7042
|
+
*/ const statementTimeoutSql = (timeoutMs)=>{
|
|
7043
|
+
const statementMs = Math.max(1000, Math.floor(timeoutMs));
|
|
7044
|
+
return `SET LOCAL statement_timeout = ${statementMs}; SET LOCAL idle_in_transaction_session_timeout = ${statementMs + IDLE_IN_TRANSACTION_MARGIN_MS}`;
|
|
7045
|
+
};
|
|
6448
7046
|
/** The event envelope's actor carries userId/companyId but not branchId, and permissions are
|
|
6449
7047
|
* cached per user+company+branch - so consumers that act as the original caller read the branch from here. */ const ACTOR_BRANCH_METADATA_KEY = '_actorBranchId';
|
|
6450
7048
|
function actorMetadata(user) {
|
|
@@ -6452,7 +7050,9 @@ function actorMetadata(user) {
|
|
|
6452
7050
|
[ACTOR_BRANCH_METADATA_KEY]: user.branchId
|
|
6453
7051
|
} : {};
|
|
6454
7052
|
}
|
|
6455
|
-
const SYSTEM_COLUMNS = new Set(
|
|
7053
|
+
const SYSTEM_COLUMNS = new Set(schema_columns/* .SYSTEM_COLUMN_NAMES */.mq);
|
|
7054
|
+
const INSERT_CHUNK_ROWS = 500;
|
|
7055
|
+
/** Bind parameters per statement, under PostgreSQL's 65535 (MySQL allows more). */ const INSERT_CHUNK_PARAMS = 30_000;
|
|
6456
7056
|
const FILTER_COMPARISON_SQL = {
|
|
6457
7057
|
eq: '=',
|
|
6458
7058
|
ne: '<>',
|
|
@@ -6468,18 +7068,13 @@ const FILTER_COMPARISON_SQL = {
|
|
|
6468
7068
|
ends_with: (t)=>`%${t}`
|
|
6469
7069
|
};
|
|
6470
7070
|
const TEXT_FIELD_TYPES = new Set([
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
7071
|
+
field_type_enum/* .FieldType.TEXT */.P.TEXT,
|
|
7072
|
+
field_type_enum/* .FieldType.LONG_TEXT */.P.LONG_TEXT,
|
|
7073
|
+
field_type_enum/* .FieldType.EMAIL */.P.EMAIL,
|
|
7074
|
+
field_type_enum/* .FieldType.SINGLE_SELECT */.P.SINGLE_SELECT
|
|
6475
7075
|
]);
|
|
6476
7076
|
/**
|
|
6477
|
-
*
|
|
6478
|
-
* (transactional writes, lifecycle event publishing) but
|
|
6479
|
-
* operating on Record<string, unknown> against a runtime EntitySchema instead of a
|
|
6480
|
-
* compile-time entity class. One instance per request, produced by
|
|
6481
|
-
* GenericEntityServiceFactory - see that class for why this isn't itself `@Injectable`.
|
|
6482
|
-
*
|
|
7077
|
+
* Reads and writes the records of one runtime entity (built per call by GenericEntityServiceFactory).
|
|
6483
7078
|
* Records hold only the fields the entity designer defines: scoping them to a company or branch
|
|
6484
7079
|
* is the flow's job, through fields such as `company_id` mapped from the caller.
|
|
6485
7080
|
*/ class GenericEntityService {
|
|
@@ -6488,15 +7083,17 @@ const TEXT_FIELD_TYPES = new Set([
|
|
|
6488
7083
|
fields;
|
|
6489
7084
|
dataSourceProvider;
|
|
6490
7085
|
resolveRelated;
|
|
7086
|
+
options;
|
|
6491
7087
|
repository;
|
|
6492
7088
|
activeFieldCodes;
|
|
6493
7089
|
/** The system columns this entity's table really has - a filter or sort on any other one is refused, not sent to SQL. */ presentSystemColumns;
|
|
6494
|
-
constructor(definition, schema, fields, dataSourceProvider, resolveRelated){
|
|
7090
|
+
constructor(definition, schema, fields, dataSourceProvider, resolveRelated, options = {}){
|
|
6495
7091
|
this.definition = definition;
|
|
6496
7092
|
this.schema = schema;
|
|
6497
7093
|
this.fields = fields;
|
|
6498
7094
|
this.dataSourceProvider = dataSourceProvider;
|
|
6499
7095
|
this.resolveRelated = resolveRelated;
|
|
7096
|
+
this.options = options;
|
|
6500
7097
|
this.activeFieldCodes = new Set(fields.filter((f)=>!f.deprecatedAt && !f.isSystemField).map((f)=>f.code));
|
|
6501
7098
|
this.presentSystemColumns = new Set([
|
|
6502
7099
|
'created_at',
|
|
@@ -6516,15 +7113,17 @@ const TEXT_FIELD_TYPES = new Set([
|
|
|
6516
7113
|
}
|
|
6517
7114
|
validateValues(values, mode) {
|
|
6518
7115
|
const fields = this.fields.filter((f)=>!f.deprecatedAt && !f.isSystemField);
|
|
6519
|
-
const { values: normalized, errors } = (0,
|
|
7116
|
+
const { values: normalized, errors } = (0,field_value_validator/* .validateRecord */.e)(fields, values, mode);
|
|
6520
7117
|
this.throwIfInvalid(errors);
|
|
6521
7118
|
return normalized;
|
|
6522
7119
|
}
|
|
6523
7120
|
throwIfInvalid(errors) {
|
|
6524
|
-
if (errors.length
|
|
6525
|
-
|
|
7121
|
+
if (errors.length) throw this.invalid(errors);
|
|
7122
|
+
}
|
|
7123
|
+
invalid(errors) {
|
|
7124
|
+
return new common_.BadRequestException({
|
|
6526
7125
|
message: 'Validation failed',
|
|
6527
|
-
messageKey:
|
|
7126
|
+
messageKey: message_keys/* .GENERIC_RECORD_MESSAGES.VALIDATION_FAILED */.j4.VALIDATION_FAILED,
|
|
6528
7127
|
errors
|
|
6529
7128
|
});
|
|
6530
7129
|
}
|
|
@@ -6533,24 +7132,43 @@ const TEXT_FIELD_TYPES = new Set([
|
|
|
6533
7132
|
const errors = [];
|
|
6534
7133
|
for (const field of this.fields){
|
|
6535
7134
|
const value = values[field.code];
|
|
6536
|
-
if (field.fieldType !==
|
|
7135
|
+
if (field.fieldType !== field_type_enum/* .FieldType.RELATION */.P.RELATION || !field.relationTargetEntityId || typeof value !== 'string') continue;
|
|
6537
7136
|
const related = await this.resolveRelated(field.relationTargetEntityId, queryRunner);
|
|
6538
7137
|
const found = await related.exists(value, {
|
|
6539
7138
|
queryRunner
|
|
6540
7139
|
});
|
|
6541
|
-
if (!found) errors.push(
|
|
6542
|
-
field: field.code,
|
|
6543
|
-
...(0,_flusys_nestjs_shared_utils__rspack_import_1.messageRef)(_config_message_keys_js__rspack_import_8/* .FIELD_VALIDATION_MESSAGES.RELATED_NOT_FOUND */.KE.RELATED_NOT_FOUND, {
|
|
6544
|
-
label: field.label
|
|
6545
|
-
})
|
|
6546
|
-
});
|
|
7140
|
+
if (!found) errors.push(this.relatedNotFound(field));
|
|
6547
7141
|
}
|
|
6548
7142
|
this.throwIfInvalid(errors);
|
|
6549
7143
|
}
|
|
7144
|
+
relatedNotFound(field) {
|
|
7145
|
+
return {
|
|
7146
|
+
field: field.code,
|
|
7147
|
+
...(0,utils_.messageRef)(message_keys/* .FIELD_VALIDATION_MESSAGES.RELATED_NOT_FOUND */.KE.RELATED_NOT_FOUND, {
|
|
7148
|
+
label: field.label
|
|
7149
|
+
})
|
|
7150
|
+
};
|
|
7151
|
+
}
|
|
7152
|
+
/** The ids each RELATION field of `rows` names that really exist - one query per field, however many rows. */ async existingRelatedIds(rows, queryRunner) {
|
|
7153
|
+
const found = new Map();
|
|
7154
|
+
if (!this.resolveRelated) return found;
|
|
7155
|
+
for (const field of this.fields){
|
|
7156
|
+
if (field.fieldType !== field_type_enum/* .FieldType.RELATION */.P.RELATION || !field.relationTargetEntityId) continue;
|
|
7157
|
+
const ids = [
|
|
7158
|
+
...new Set(rows.map((row)=>row[field.code]).filter((value)=>typeof value === 'string'))
|
|
7159
|
+
];
|
|
7160
|
+
if (!ids.length) continue;
|
|
7161
|
+
const related = await this.resolveRelated(field.relationTargetEntityId, queryRunner);
|
|
7162
|
+
found.set(field.code, await related.existingIds(ids, {
|
|
7163
|
+
queryRunner
|
|
7164
|
+
}));
|
|
7165
|
+
}
|
|
7166
|
+
return found;
|
|
7167
|
+
}
|
|
6550
7168
|
notFound(id) {
|
|
6551
|
-
return new
|
|
7169
|
+
return new common_.NotFoundException({
|
|
6552
7170
|
message: `Record '${id}' not found on entity '${this.entityCode}'.`,
|
|
6553
|
-
messageKey:
|
|
7171
|
+
messageKey: message_keys/* .GENERIC_RECORD_MESSAGES.NOT_FOUND */.j4.NOT_FOUND,
|
|
6554
7172
|
messageVariables: {
|
|
6555
7173
|
id,
|
|
6556
7174
|
entity: this.entityCode
|
|
@@ -6566,19 +7184,11 @@ const TEXT_FIELD_TYPES = new Set([
|
|
|
6566
7184
|
updated_by_id: user.id
|
|
6567
7185
|
};
|
|
6568
7186
|
}
|
|
6569
|
-
async stampDeletedBy(queryRunner, target, id, userId) {
|
|
6570
|
-
if (!this.definition.isAuditEnabled) return;
|
|
6571
|
-
await queryRunner.manager.update(target, [
|
|
6572
|
-
id
|
|
6573
|
-
], {
|
|
6574
|
-
deleted_by_id: userId
|
|
6575
|
-
});
|
|
6576
|
-
}
|
|
6577
7187
|
ensureActive() {
|
|
6578
|
-
if (this.definition.status ===
|
|
6579
|
-
throw new
|
|
7188
|
+
if (this.definition.status === entity_status_enum/* .EntityStatus.DEPRECATED */.c.DEPRECATED) {
|
|
7189
|
+
throw new common_.ForbiddenException({
|
|
6580
7190
|
message: `Entity '${this.definition.code}' has been deprecated and no longer accepts writes.`,
|
|
6581
|
-
messageKey:
|
|
7191
|
+
messageKey: message_keys/* .ENTITY_DEFINITION_MESSAGES.DEPRECATED */.pL.DEPRECATED,
|
|
6582
7192
|
messageVariables: {
|
|
6583
7193
|
code: this.definition.code
|
|
6584
7194
|
}
|
|
@@ -6598,24 +7208,22 @@ const TEXT_FIELD_TYPES = new Set([
|
|
|
6598
7208
|
}
|
|
6599
7209
|
return this.repository;
|
|
6600
7210
|
}
|
|
6601
|
-
/**
|
|
6602
|
-
* keeps mass-assignment and column-name-as-SQL-injection (see getFilterQuery/getSortQuery
|
|
6603
|
-
* below) both structurally impossible, since only known-good column names are ever used. */ validateKeys(dto) {
|
|
7211
|
+
/** Only active field codes may be written: no mass assignment, and no client key ever reaches SQL as a column name. */ validateKeys(dto) {
|
|
6604
7212
|
for (const key of Object.keys(dto)){
|
|
6605
7213
|
// `id` included: an insert that names an id would overwrite the record that has it.
|
|
6606
7214
|
if (SYSTEM_COLUMNS.has(key)) {
|
|
6607
|
-
throw new
|
|
7215
|
+
throw new common_.BadRequestException({
|
|
6608
7216
|
message: `'${key}' is a system column and cannot be set directly.`,
|
|
6609
|
-
messageKey:
|
|
7217
|
+
messageKey: message_keys/* .GENERIC_RECORD_MESSAGES.SYSTEM_COLUMN_READONLY */.j4.SYSTEM_COLUMN_READONLY,
|
|
6610
7218
|
messageVariables: {
|
|
6611
7219
|
key
|
|
6612
7220
|
}
|
|
6613
7221
|
});
|
|
6614
7222
|
}
|
|
6615
7223
|
if (!this.activeFieldCodes.has(key)) {
|
|
6616
|
-
throw new
|
|
7224
|
+
throw new common_.BadRequestException({
|
|
6617
7225
|
message: `'${key}' is not a field on entity '${this.entityCode}'.`,
|
|
6618
|
-
messageKey:
|
|
7226
|
+
messageKey: message_keys/* .GENERIC_RECORD_MESSAGES.UNKNOWN_FIELD */.j4.UNKNOWN_FIELD,
|
|
6619
7227
|
messageVariables: {
|
|
6620
7228
|
key,
|
|
6621
7229
|
entity: this.entityCode
|
|
@@ -6643,6 +7251,71 @@ const TEXT_FIELD_TYPES = new Set([
|
|
|
6643
7251
|
};
|
|
6644
7252
|
}, user);
|
|
6645
7253
|
}
|
|
7254
|
+
/**
|
|
7255
|
+
* The checks `insert` makes before touching the table, for a caller that inserts several rows in one
|
|
7256
|
+
* statement (`insertValidated`): the same errors, thrown at the same row. Returns the values to insert.
|
|
7257
|
+
*/ prepareInsert(dto) {
|
|
7258
|
+
this.ensureActive();
|
|
7259
|
+
this.validateKeys(dto);
|
|
7260
|
+
return this.validateValues(dto, 'insert');
|
|
7261
|
+
}
|
|
7262
|
+
/** The first of `rows` (in order) naming a related record that does not exist, as `insert` would reject it - one query per RELATION field. */ async firstMissingRelation(rows, queryRunner) {
|
|
7263
|
+
try {
|
|
7264
|
+
const existing = await this.existingRelatedIds(rows, queryRunner);
|
|
7265
|
+
for(let index = 0; index < rows.length; index++){
|
|
7266
|
+
const errors = this.missingRelations(rows[index], existing);
|
|
7267
|
+
if (errors.length) return {
|
|
7268
|
+
index,
|
|
7269
|
+
error: this.invalid(errors)
|
|
7270
|
+
};
|
|
7271
|
+
}
|
|
7272
|
+
return null;
|
|
7273
|
+
} catch (error) {
|
|
7274
|
+
throw (0,database_error_mapper/* .mapDatabaseError */.E)(error, [
|
|
7275
|
+
...this.activeFieldCodes
|
|
7276
|
+
]);
|
|
7277
|
+
}
|
|
7278
|
+
}
|
|
7279
|
+
/**
|
|
7280
|
+
* Rows from `prepareInsert`, whose relations were checked, in one multi-row statement per chunk - each saved record
|
|
7281
|
+
* comes back as `insert` returns it. On the caller's transaction, which publishes the events.
|
|
7282
|
+
*/ async insertValidated(rows, user, options) {
|
|
7283
|
+
const repository = await this.ensureRepository();
|
|
7284
|
+
return this.withTransaction({
|
|
7285
|
+
...options,
|
|
7286
|
+
publishEvents: false
|
|
7287
|
+
}, async (queryRunner)=>{
|
|
7288
|
+
const entities = rows.map((values)=>repository.create({
|
|
7289
|
+
...values,
|
|
7290
|
+
...this.auditFields('created', user)
|
|
7291
|
+
}));
|
|
7292
|
+
const chunk = Math.max(1, Math.min(INSERT_CHUNK_ROWS, Math.floor(INSERT_CHUNK_PARAMS / (this.activeFieldCodes.size + 6))));
|
|
7293
|
+
for(let start = 0; start < entities.length; start += chunk){
|
|
7294
|
+
await queryRunner.manager.createQueryBuilder().insert().into(repository.target).values(entities.slice(start, start + chunk)).execute();
|
|
7295
|
+
}
|
|
7296
|
+
// What `save()` adds to a record it inserted: a nullable column left out reads back as null.
|
|
7297
|
+
const nullable = repository.metadata.columns.filter((column)=>column.isNullable && !column.isVirtual && !column.isDeleteDate);
|
|
7298
|
+
for (const entity of entities){
|
|
7299
|
+
for (const column of nullable)if (column.getEntityValue(entity) === undefined) column.setEntityValue(entity, null);
|
|
7300
|
+
}
|
|
7301
|
+
return {
|
|
7302
|
+
saved: entities,
|
|
7303
|
+
events: []
|
|
7304
|
+
};
|
|
7305
|
+
}, user);
|
|
7306
|
+
}
|
|
7307
|
+
/** No unique field: a multi-row insert then fails only where a row-by-row one would, so the two answer alike. */ get batchInsertable() {
|
|
7308
|
+
return !this.fields.some((f)=>f.unique && !f.deprecatedAt && !f.isSystemField);
|
|
7309
|
+
}
|
|
7310
|
+
missingRelations(values, existing) {
|
|
7311
|
+
const errors = [];
|
|
7312
|
+
for (const field of this.fields){
|
|
7313
|
+
const value = values[field.code];
|
|
7314
|
+
const known = existing.get(field.code);
|
|
7315
|
+
if (known && typeof value === 'string' && !known.has(value)) errors.push(this.relatedNotFound(field));
|
|
7316
|
+
}
|
|
7317
|
+
return errors;
|
|
7318
|
+
}
|
|
6646
7319
|
async update(dto, user, options = {}) {
|
|
6647
7320
|
this.ensureActive();
|
|
6648
7321
|
this.assertUpdatable(dto);
|
|
@@ -6671,6 +7344,42 @@ const TEXT_FIELD_TYPES = new Set([
|
|
|
6671
7344
|
});
|
|
6672
7345
|
return queryRunner.manager.save(repository.target, entity);
|
|
6673
7346
|
}
|
|
7347
|
+
/**
|
|
7348
|
+
* `update` of a record the caller has just read in this transaction (an update by filter's matches): the same
|
|
7349
|
+
* checks and result, without reading it again. Falls back to `update` when `fastUpdates` is off.
|
|
7350
|
+
*/ async updateLoaded(row, dto, user, options = {}) {
|
|
7351
|
+
const { id: _id, ...values } = dto;
|
|
7352
|
+
if (this.options.fastUpdates === false) return this.update({
|
|
7353
|
+
...values,
|
|
7354
|
+
id: row.id
|
|
7355
|
+
}, user, options);
|
|
7356
|
+
this.ensureActive();
|
|
7357
|
+
this.assertUpdatable({
|
|
7358
|
+
...values,
|
|
7359
|
+
id: row.id
|
|
7360
|
+
});
|
|
7361
|
+
const repository = await this.ensureRepository();
|
|
7362
|
+
return this.withTransaction(options, async (queryRunner)=>{
|
|
7363
|
+
const validated = this.validateValues(values, 'update');
|
|
7364
|
+
await this.assertRelationsExist(validated, queryRunner);
|
|
7365
|
+
const saved = await saveLoaded(queryRunner, repository.target, Object.assign({
|
|
7366
|
+
...row
|
|
7367
|
+
}, validated, this.auditFields('updated', user)), {
|
|
7368
|
+
...row
|
|
7369
|
+
});
|
|
7370
|
+
return {
|
|
7371
|
+
saved,
|
|
7372
|
+
events: [
|
|
7373
|
+
{
|
|
7374
|
+
action: 'updated',
|
|
7375
|
+
rows: [
|
|
7376
|
+
saved
|
|
7377
|
+
]
|
|
7378
|
+
}
|
|
7379
|
+
]
|
|
7380
|
+
};
|
|
7381
|
+
}, user);
|
|
7382
|
+
}
|
|
6674
7383
|
async updateRow(dto, user, repository, queryRunner) {
|
|
6675
7384
|
const { id, ...rest } = dto;
|
|
6676
7385
|
const existing = await queryRunner.manager.findOne(repository.target, {
|
|
@@ -6681,40 +7390,40 @@ const TEXT_FIELD_TYPES = new Set([
|
|
|
6681
7390
|
if (!existing) throw this.notFound(id);
|
|
6682
7391
|
const validated = this.validateValues(rest, 'update');
|
|
6683
7392
|
await this.assertRelationsExist(validated, queryRunner);
|
|
6684
|
-
|
|
6685
|
-
|
|
7393
|
+
if (this.options.fastUpdates === false) {
|
|
7394
|
+
return queryRunner.manager.save(repository.target, Object.assign(existing, validated, this.auditFields('updated', user)));
|
|
7395
|
+
}
|
|
7396
|
+
const loaded = {
|
|
7397
|
+
...existing
|
|
7398
|
+
};
|
|
7399
|
+
return saveLoaded(queryRunner, repository.target, Object.assign(existing, validated, this.auditFields('updated', user)), loaded);
|
|
6686
7400
|
}
|
|
6687
7401
|
assertUpdatable(dto) {
|
|
6688
7402
|
if (!dto.id) {
|
|
6689
|
-
throw new
|
|
7403
|
+
throw new common_.BadRequestException({
|
|
6690
7404
|
message: 'id is required to update a record.',
|
|
6691
|
-
messageKey:
|
|
7405
|
+
messageKey: message_keys/* .GENERIC_RECORD_MESSAGES.ID_REQUIRED */.j4.ID_REQUIRED
|
|
6692
7406
|
});
|
|
6693
7407
|
}
|
|
6694
7408
|
const { id: _id, ...rest } = dto;
|
|
6695
7409
|
this.validateKeys(rest);
|
|
6696
7410
|
}
|
|
6697
|
-
/** Soft-deletes the record, or deletes it for good when the entity has soft delete turned off. */ async delete(id, user, options = {}) {
|
|
7411
|
+
/** Soft-deletes the record, or deletes it for good when the entity has soft delete turned off. One statement either way: no row affected = not found. */ async delete(id, user, options = {}) {
|
|
6698
7412
|
this.ensureActive();
|
|
6699
7413
|
const repository = await this.ensureRepository();
|
|
6700
7414
|
const soft = this.definition.isSoftDeleteEnabled;
|
|
6701
7415
|
await this.withTransaction(options, async (queryRunner)=>{
|
|
6702
|
-
const
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
7416
|
+
const result = soft ? await queryRunner.manager.createQueryBuilder().update(repository.target).set({
|
|
7417
|
+
deleted_at: ()=>'CURRENT_TIMESTAMP',
|
|
7418
|
+
...this.definition.isAuditEnabled ? {
|
|
7419
|
+
deleted_by_id: user?.id ?? null
|
|
7420
|
+
} : {}
|
|
7421
|
+
}).where('id = :id', {
|
|
7422
|
+
id
|
|
7423
|
+
}).andWhere('deleted_at IS NULL').execute() : await queryRunner.manager.delete(repository.target, {
|
|
7424
|
+
id
|
|
6706
7425
|
});
|
|
6707
|
-
if (!
|
|
6708
|
-
if (soft) {
|
|
6709
|
-
await queryRunner.manager.softDelete(repository.target, [
|
|
6710
|
-
id
|
|
6711
|
-
]);
|
|
6712
|
-
await this.stampDeletedBy(queryRunner, repository.target, id, user?.id ?? null);
|
|
6713
|
-
} else {
|
|
6714
|
-
await queryRunner.manager.delete(repository.target, [
|
|
6715
|
-
id
|
|
6716
|
-
]);
|
|
6717
|
-
}
|
|
7426
|
+
if (!result.affected) throw this.notFound(id);
|
|
6718
7427
|
const rows = [
|
|
6719
7428
|
{
|
|
6720
7429
|
id
|
|
@@ -6750,6 +7459,19 @@ const TEXT_FIELD_TYPES = new Set([
|
|
|
6750
7459
|
}
|
|
6751
7460
|
});
|
|
6752
7461
|
}
|
|
7462
|
+
/** Which of `ids` exist (soft-deleted ones do not), in one query. */ async existingIds(ids, options = {}) {
|
|
7463
|
+
if (!ids.length) return new Set();
|
|
7464
|
+
const repository = await this.repositoryFor(options.queryRunner);
|
|
7465
|
+
const rows = await repository.find({
|
|
7466
|
+
select: {
|
|
7467
|
+
id: true
|
|
7468
|
+
},
|
|
7469
|
+
where: {
|
|
7470
|
+
id: (0,external_typeorm_.In)(ids)
|
|
7471
|
+
}
|
|
7472
|
+
});
|
|
7473
|
+
return new Set(rows.map((row)=>row.id));
|
|
7474
|
+
}
|
|
6753
7475
|
async getAll(search, query, options = {}) {
|
|
6754
7476
|
const repository = await this.repositoryFor(options.queryRunner);
|
|
6755
7477
|
let builder = repository.createQueryBuilder(this.entityCode);
|
|
@@ -6767,25 +7489,27 @@ const TEXT_FIELD_TYPES = new Set([
|
|
|
6767
7489
|
const { currentPage, pageSize } = query.pagination;
|
|
6768
7490
|
builder.skip(currentPage * pageSize).take(pageSize);
|
|
6769
7491
|
}
|
|
6770
|
-
const
|
|
7492
|
+
const data = await builder.getMany();
|
|
6771
7493
|
return {
|
|
6772
7494
|
data,
|
|
6773
|
-
total
|
|
7495
|
+
total: await this.totalOf(builder, data.length, query.pagination)
|
|
6774
7496
|
};
|
|
6775
7497
|
}
|
|
6776
|
-
/**
|
|
6777
|
-
|
|
6778
|
-
|
|
6779
|
-
|
|
6780
|
-
|
|
7498
|
+
/** The page itself tells the total unless it came back full (or empty past the first page): only then is COUNT run. */ async totalOf(builder, rows, pagination) {
|
|
7499
|
+
if (!pagination) return rows;
|
|
7500
|
+
const offset = pagination.currentPage * pagination.pageSize;
|
|
7501
|
+
if (rows < pagination.pageSize && (rows > 0 || offset === 0)) return offset + rows;
|
|
7502
|
+
return builder.getCount();
|
|
7503
|
+
}
|
|
7504
|
+
/** Every key is checked against the field-code whitelist before it reaches SQL; operators map to fixed SQL and every value is a bound parameter. */ applyFilter(query, filter) {
|
|
6781
7505
|
const postgres = query.connection.options.type === 'postgres';
|
|
6782
7506
|
Object.entries(filter).forEach(([key, value], index)=>{
|
|
6783
7507
|
this.assertKnownColumn(key);
|
|
6784
|
-
if (!(0,
|
|
7508
|
+
if (!(0,generic_record_interface/* .isRecordFilterCondition */.t1)(value) && value !== null && typeof value === 'object') {
|
|
6785
7509
|
const op = value.op;
|
|
6786
|
-
if (typeof op === 'string' && !('type' in value) && !
|
|
7510
|
+
if (typeof op === 'string' && !('type' in value) && !generic_record_interface/* .RECORD_FILTER_OPS.includes */.ox.includes(op)) throw this.unknownOperator(key, op);
|
|
6787
7511
|
}
|
|
6788
|
-
const { op, value: operand } = (0,
|
|
7512
|
+
const { op, value: operand } = (0,generic_record_interface/* .isRecordFilterCondition */.t1)(value) ? value : {
|
|
6789
7513
|
op: 'eq',
|
|
6790
7514
|
value
|
|
6791
7515
|
};
|
|
@@ -6796,8 +7520,8 @@ const TEXT_FIELD_TYPES = new Set([
|
|
|
6796
7520
|
return query;
|
|
6797
7521
|
}
|
|
6798
7522
|
/** One filter as SQL: the column is a checked field code and every value is a bound parameter. */ conditionSql(column, key, op, operand, param, postgres) {
|
|
6799
|
-
const multiSelect = this.fieldTypeOf(key) ===
|
|
6800
|
-
switch(
|
|
7523
|
+
const multiSelect = this.fieldTypeOf(key) === field_type_enum/* .FieldType.MULTI_SELECT */.P.MULTI_SELECT;
|
|
7524
|
+
switch(generic_record_interface/* .FILTER_OP_VALUE_KIND */.w9[op]){
|
|
6801
7525
|
case 'none':
|
|
6802
7526
|
if (op === 'is_null') return [
|
|
6803
7527
|
`${column} IS NULL`,
|
|
@@ -6915,13 +7639,13 @@ const TEXT_FIELD_TYPES = new Set([
|
|
|
6915
7639
|
operand
|
|
6916
7640
|
] : operand;
|
|
6917
7641
|
if (!Array.isArray(list) || !list.every(isFilterScalar)) throw this.invalidFilterValue(key, op);
|
|
6918
|
-
if (list.length >
|
|
6919
|
-
throw new
|
|
6920
|
-
message: `The '${key}' filter lists more than ${
|
|
6921
|
-
messageKey:
|
|
7642
|
+
if (list.length > generic_record_interface/* .RECORD_FILTER_LIMITS.MAX_IN_VALUES */.M5.MAX_IN_VALUES) {
|
|
7643
|
+
throw new common_.BadRequestException({
|
|
7644
|
+
message: `The '${key}' filter lists more than ${generic_record_interface/* .RECORD_FILTER_LIMITS.MAX_IN_VALUES */.M5.MAX_IN_VALUES} values.`,
|
|
7645
|
+
messageKey: message_keys/* .GENERIC_RECORD_MESSAGES.FILTER_TOO_MANY_VALUES */.j4.FILTER_TOO_MANY_VALUES,
|
|
6922
7646
|
messageVariables: {
|
|
6923
7647
|
key,
|
|
6924
|
-
max:
|
|
7648
|
+
max: generic_record_interface/* .RECORD_FILTER_LIMITS.MAX_IN_VALUES */.M5.MAX_IN_VALUES
|
|
6925
7649
|
}
|
|
6926
7650
|
});
|
|
6927
7651
|
}
|
|
@@ -6933,11 +7657,11 @@ const TEXT_FIELD_TYPES = new Set([
|
|
|
6933
7657
|
/** Text operators need a text field, multi-select operators a multi-select one, and nothing but null / blank checks reads a list or JSON field. */ assertOperatorFits(key, op) {
|
|
6934
7658
|
const type = this.fieldTypeOf(key);
|
|
6935
7659
|
const textField = type !== undefined && TEXT_FIELD_TYPES.has(type);
|
|
6936
|
-
const fits =
|
|
7660
|
+
const fits = generic_record_interface/* .TEXT_FILTER_OPS.includes */.wk.includes(op) ? textField : generic_record_interface/* .LIST_FIELD_FILTER_OPS.includes */.yV.includes(op) ? type === field_type_enum/* .FieldType.MULTI_SELECT */.P.MULTI_SELECT : generic_record_interface/* .EMPTY_FILTER_OPS.includes */.Oh.includes(op) ? textField || type === field_type_enum/* .FieldType.MULTI_SELECT */.P.MULTI_SELECT : op === 'is_null' || op === 'not_null' || type !== field_type_enum/* .FieldType.MULTI_SELECT */.P.MULTI_SELECT && type !== field_type_enum/* .FieldType.JSON */.P.JSON;
|
|
6937
7661
|
if (!fits) {
|
|
6938
|
-
throw new
|
|
7662
|
+
throw new common_.BadRequestException({
|
|
6939
7663
|
message: `The '${op}' filter cannot be used on '${key}'.`,
|
|
6940
|
-
messageKey:
|
|
7664
|
+
messageKey: message_keys/* .GENERIC_RECORD_MESSAGES.FILTER_OPERATOR_NOT_FOR_FIELD */.j4.FILTER_OPERATOR_NOT_FOR_FIELD,
|
|
6941
7665
|
messageVariables: {
|
|
6942
7666
|
key,
|
|
6943
7667
|
op
|
|
@@ -6946,20 +7670,20 @@ const TEXT_FIELD_TYPES = new Set([
|
|
|
6946
7670
|
}
|
|
6947
7671
|
}
|
|
6948
7672
|
unknownOperator(key, op) {
|
|
6949
|
-
return new
|
|
7673
|
+
return new common_.BadRequestException({
|
|
6950
7674
|
message: `'${op}' is not a filter operator.`,
|
|
6951
|
-
messageKey:
|
|
7675
|
+
messageKey: message_keys/* .GENERIC_RECORD_MESSAGES.FILTER_OPERATOR_UNKNOWN */.j4.FILTER_OPERATOR_UNKNOWN,
|
|
6952
7676
|
messageVariables: {
|
|
6953
7677
|
key,
|
|
6954
7678
|
op,
|
|
6955
|
-
ops:
|
|
7679
|
+
ops: generic_record_interface/* .RECORD_FILTER_OPS.join */.ox.join(', ')
|
|
6956
7680
|
}
|
|
6957
7681
|
});
|
|
6958
7682
|
}
|
|
6959
7683
|
invalidFilterValue(key, op) {
|
|
6960
|
-
return new
|
|
7684
|
+
return new common_.BadRequestException({
|
|
6961
7685
|
message: `The '${key}' filter has an invalid value for '${op}'.`,
|
|
6962
|
-
messageKey:
|
|
7686
|
+
messageKey: message_keys/* .GENERIC_RECORD_MESSAGES.FILTER_VALUE_INVALID */.j4.FILTER_VALUE_INVALID,
|
|
6963
7687
|
messageVariables: {
|
|
6964
7688
|
key,
|
|
6965
7689
|
op
|
|
@@ -6986,9 +7710,9 @@ const TEXT_FIELD_TYPES = new Set([
|
|
|
6986
7710
|
}
|
|
6987
7711
|
assertKnownColumn(key) {
|
|
6988
7712
|
if (key !== 'id' && !this.presentSystemColumns.has(key) && !this.activeFieldCodes.has(key)) {
|
|
6989
|
-
throw new
|
|
7713
|
+
throw new common_.BadRequestException({
|
|
6990
7714
|
message: `'${key}' is not a field on entity '${this.entityCode}'.`,
|
|
6991
|
-
messageKey:
|
|
7715
|
+
messageKey: message_keys/* .GENERIC_RECORD_MESSAGES.UNKNOWN_FIELD */.j4.UNKNOWN_FIELD,
|
|
6992
7716
|
messageVariables: {
|
|
6993
7717
|
key,
|
|
6994
7718
|
entity: this.entityCode
|
|
@@ -7007,6 +7731,9 @@ const TEXT_FIELD_TYPES = new Set([
|
|
|
7007
7731
|
await queryRunner.connect();
|
|
7008
7732
|
await queryRunner.startTransaction();
|
|
7009
7733
|
started = true;
|
|
7734
|
+
if (options.statementTimeoutMs !== undefined && queryRunner.connection.options.type === 'postgres') {
|
|
7735
|
+
await queryRunner.query(statementTimeoutSql(options.statementTimeoutMs));
|
|
7736
|
+
}
|
|
7010
7737
|
}
|
|
7011
7738
|
const { saved, events } = await fn(queryRunner);
|
|
7012
7739
|
if (!externalQueryRunner) {
|
|
@@ -7016,8 +7743,8 @@ const TEXT_FIELD_TYPES = new Set([
|
|
|
7016
7743
|
if (options.publishEvents !== false) {
|
|
7017
7744
|
for (const { action, rows } of events){
|
|
7018
7745
|
if (!rows.length) continue;
|
|
7019
|
-
await (0,
|
|
7020
|
-
module:
|
|
7746
|
+
await (0,classes_.publishDomainEvent)({
|
|
7747
|
+
module: event_actions/* .ENTITY_BUILDER_EVENT_MODULE */.S,
|
|
7021
7748
|
entity: this.entityCode,
|
|
7022
7749
|
action,
|
|
7023
7750
|
ids: rows.map((r)=>r.id),
|
|
@@ -7034,7 +7761,7 @@ const TEXT_FIELD_TYPES = new Set([
|
|
|
7034
7761
|
if (started && !committed) {
|
|
7035
7762
|
await queryRunner.rollbackTransaction();
|
|
7036
7763
|
}
|
|
7037
|
-
throw (0,
|
|
7764
|
+
throw (0,database_error_mapper/* .mapDatabaseError */.E)(error, [
|
|
7038
7765
|
...this.activeFieldCodes
|
|
7039
7766
|
]);
|
|
7040
7767
|
} finally{
|
|
@@ -7048,13 +7775,6 @@ function isFilterScalar(value) {
|
|
|
7048
7775
|
return typeof value === 'string' || typeof value === 'number' && Number.isFinite(value) || typeof value === 'boolean';
|
|
7049
7776
|
}
|
|
7050
7777
|
|
|
7051
|
-
__webpack_require__.d(__webpack_exports__, {
|
|
7052
|
-
DG: () => (actorMetadata),
|
|
7053
|
-
GK: () => (GenericEntityService)
|
|
7054
|
-
}, {
|
|
7055
|
-
zb: ACTOR_BRANCH_METADATA_KEY
|
|
7056
|
-
});
|
|
7057
|
-
|
|
7058
7778
|
|
|
7059
7779
|
},
|
|
7060
7780
|
7259(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
@@ -7282,6 +8002,8 @@ class SchemaRegistryService {
|
|
|
7282
8002
|
dataSourceProvider;
|
|
7283
8003
|
dialectAdapter;
|
|
7284
8004
|
static cache = new Map();
|
|
8005
|
+
/** Per tenant: the one-time registration of every entity's schema (see warmUp). */ static warming = new Map();
|
|
8006
|
+
logger = new _nestjs_common__rspack_import_1.Logger(SchemaRegistryService.name);
|
|
7285
8007
|
constructor(dataSourceProvider, dialectAdapter){
|
|
7286
8008
|
this.dataSourceProvider = dataSourceProvider;
|
|
7287
8009
|
this.dialectAdapter = dialectAdapter;
|
|
@@ -7293,10 +8015,9 @@ class SchemaRegistryService {
|
|
|
7293
8015
|
id: entityDefinitionId
|
|
7294
8016
|
}
|
|
7295
8017
|
});
|
|
7296
|
-
const cacheKey = this.cacheKey(entityDefinitionId);
|
|
7297
8018
|
if (!definition) {
|
|
7298
8019
|
// Dropped, possibly on another instance: its schema is never asked for again.
|
|
7299
|
-
SchemaRegistryService.cache.delete(cacheKey);
|
|
8020
|
+
SchemaRegistryService.cache.delete(this.cacheKey(entityDefinitionId));
|
|
7300
8021
|
throw new _nestjs_common__rspack_import_1.NotFoundException({
|
|
7301
8022
|
message: `Entity definition '${entityDefinitionId}' not found.`,
|
|
7302
8023
|
messageKey: _config_message_keys_js__rspack_import_8/* .ENTITY_DEFINITION_MESSAGES.NOT_FOUND */.pL.NOT_FOUND,
|
|
@@ -7305,26 +8026,70 @@ class SchemaRegistryService {
|
|
|
7305
8026
|
}
|
|
7306
8027
|
});
|
|
7307
8028
|
}
|
|
8029
|
+
return (await this.resolve(definition, queryRunner)).schema;
|
|
8030
|
+
}
|
|
8031
|
+
/** For a definition the caller has just read: its fields are read only when its schemaVersion is not cached yet. */ async resolve(definition, queryRunner) {
|
|
8032
|
+
const cacheKey = this.cacheKey(definition.id);
|
|
7308
8033
|
const cached = SchemaRegistryService.cache.get(cacheKey);
|
|
7309
|
-
if (cached && cached.version === definition.schemaVersion)
|
|
7310
|
-
return cached.schema;
|
|
7311
|
-
}
|
|
8034
|
+
if (cached && cached.version === definition.schemaVersion) return cached;
|
|
7312
8035
|
const fieldRepository = queryRunner ? queryRunner.manager.getRepository(_entities_field_definition_entity_js__rspack_import_4/* .FieldDefinition */.c) : await this.dataSourceProvider.getRepository(_entities_field_definition_entity_js__rspack_import_4/* .FieldDefinition */.c);
|
|
7313
8036
|
const fields = await fieldRepository.find({
|
|
7314
8037
|
where: {
|
|
7315
|
-
entityDefinitionId,
|
|
8038
|
+
entityDefinitionId: definition.id,
|
|
7316
8039
|
deprecatedAt: (0,typeorm__rspack_import_2.IsNull)()
|
|
7317
8040
|
},
|
|
7318
8041
|
order: {
|
|
7319
8042
|
order: 'ASC'
|
|
7320
8043
|
}
|
|
7321
8044
|
});
|
|
7322
|
-
|
|
7323
|
-
|
|
8045
|
+
return this.store(definition, fields);
|
|
8046
|
+
}
|
|
8047
|
+
/**
|
|
8048
|
+
* Once per tenant per process, before its first entity is used: every entity's schema, built from two queries
|
|
8049
|
+
* and registered with ONE metadata rebuild - one by one, each first use would rebuild the metadata of the
|
|
8050
|
+
* whole DataSource. Never throws (logged instead, and not retried): the per-entity path works without it.
|
|
8051
|
+
*/ async warmUp(queryRunner) {
|
|
8052
|
+
const tenant = (0,_flusys_nestjs_shared_utils__rspack_import_0.cacheTenantId)(this.dataSourceProvider) ?? '';
|
|
8053
|
+
let warming = SchemaRegistryService.warming.get(tenant);
|
|
8054
|
+
if (!warming) {
|
|
8055
|
+
warming = this.registerAll(queryRunner).catch((error)=>this.logger.warn(`Entity schemas are registered one by one instead: ${error.message}`));
|
|
8056
|
+
SchemaRegistryService.warming.set(tenant, warming);
|
|
8057
|
+
}
|
|
8058
|
+
await warming;
|
|
8059
|
+
}
|
|
8060
|
+
async registerAll(queryRunner) {
|
|
8061
|
+
const repositoryOf = async (entity)=>queryRunner ? queryRunner.manager.getRepository(entity) : this.dataSourceProvider.getRepository(entity);
|
|
8062
|
+
const definitions = await (await repositoryOf(_entities_entity_definition_entity_js__rspack_import_3/* .EntityDefinition */.f)).find();
|
|
8063
|
+
const fields = await (await repositoryOf(_entities_field_definition_entity_js__rspack_import_4/* .FieldDefinition */.c)).find({
|
|
8064
|
+
where: {
|
|
8065
|
+
deprecatedAt: (0,typeorm__rspack_import_2.IsNull)()
|
|
8066
|
+
},
|
|
8067
|
+
order: {
|
|
8068
|
+
order: 'ASC'
|
|
8069
|
+
}
|
|
8070
|
+
});
|
|
8071
|
+
const fieldsOf = new Map();
|
|
8072
|
+
for (const field of fields)fieldsOf.set(field.entityDefinitionId, [
|
|
8073
|
+
...fieldsOf.get(field.entityDefinitionId) ?? [],
|
|
8074
|
+
field
|
|
8075
|
+
]);
|
|
8076
|
+
const schemas = [];
|
|
8077
|
+
for (const definition of definitions){
|
|
8078
|
+
const cached = SchemaRegistryService.cache.get(this.cacheKey(definition.id));
|
|
8079
|
+
schemas.push(cached && cached.version === definition.schemaVersion ? cached.schema : (await this.store(definition, fieldsOf.get(definition.id) ?? [])).schema);
|
|
8080
|
+
}
|
|
8081
|
+
await this.dataSourceProvider.registerDynamicSchemas(schemas);
|
|
8082
|
+
}
|
|
8083
|
+
async store(definition, fields) {
|
|
8084
|
+
const resolved = {
|
|
8085
|
+
schema: await this.buildEntitySchema(definition, fields),
|
|
8086
|
+
fields
|
|
8087
|
+
};
|
|
8088
|
+
SchemaRegistryService.cache.set(this.cacheKey(definition.id), {
|
|
7324
8089
|
version: definition.schemaVersion,
|
|
7325
|
-
|
|
8090
|
+
...resolved
|
|
7326
8091
|
});
|
|
7327
|
-
return
|
|
8092
|
+
return resolved;
|
|
7328
8093
|
}
|
|
7329
8094
|
invalidate(entityDefinitionId) {
|
|
7330
8095
|
SchemaRegistryService.cache.delete(this.cacheKey(entityDefinitionId));
|