@dnax/core 0.66.0 → 0.66.2
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/app/ctrl.ts +20 -1
- package/app/hono.ts +14 -1
- package/app/index.ts +30 -16
- package/bin/{index.ts → _.ts} +1 -1
- package/driver/mongo/rest.ts +18 -1
- package/index.ts +2 -0
- package/lib/collection.ts +1 -0
- package/lib/workflow/index.ts +84 -0
- package/lib/workflow/types.ts +64 -0
- package/package.json +3 -7
- package/types/index.ts +4 -0
- package/utils/index.ts +10 -0
package/app/ctrl.ts
CHANGED
|
@@ -20,6 +20,25 @@ const actions = [
|
|
|
20
20
|
"listActivity",
|
|
21
21
|
];
|
|
22
22
|
|
|
23
|
+
const forbiddenPipelineOperators = [
|
|
24
|
+
"$merge",
|
|
25
|
+
"$out",
|
|
26
|
+
"$currentOp",
|
|
27
|
+
"$function",
|
|
28
|
+
"$accumulator",
|
|
29
|
+
"$collMod",
|
|
30
|
+
"$collStats",
|
|
31
|
+
"$explain",
|
|
32
|
+
"$indexStats",
|
|
33
|
+
"$planCacheStats",
|
|
34
|
+
"$where",
|
|
35
|
+
"$listLocalSessions",
|
|
36
|
+
"$listSessions",
|
|
37
|
+
"$search",
|
|
38
|
+
"$listCatalog",
|
|
39
|
+
"$unionWith",
|
|
40
|
+
];
|
|
41
|
+
|
|
23
42
|
function getAction(action: string) {
|
|
24
43
|
if (actions?.includes(action)) {
|
|
25
44
|
return true;
|
|
@@ -27,4 +46,4 @@ function getAction(action: string) {
|
|
|
27
46
|
return false;
|
|
28
47
|
}
|
|
29
48
|
|
|
30
|
-
export { getAction };
|
|
49
|
+
export { getAction, forbiddenPipelineOperators };
|
package/app/hono.ts
CHANGED
|
@@ -6,6 +6,8 @@ import path from "path";
|
|
|
6
6
|
import mime from "mime-types";
|
|
7
7
|
import { setApiStudio } from "./studio";
|
|
8
8
|
import { Hono } from "hono";
|
|
9
|
+
import { compress } from "hono/compress";
|
|
10
|
+
|
|
9
11
|
import { getCookie, setCookie } from "hono/cookie";
|
|
10
12
|
import colors from "@colors/colors/safe";
|
|
11
13
|
import { serveStatic, getConnInfo } from "hono/bun";
|
|
@@ -24,7 +26,7 @@ import {
|
|
|
24
26
|
pick,
|
|
25
27
|
stringToBoolean,
|
|
26
28
|
} from "../utils";
|
|
27
|
-
import { getAction } from "./ctrl";
|
|
29
|
+
import { forbiddenPipelineOperators, getAction } from "./ctrl";
|
|
28
30
|
import { getCollection } from "../lib/collection";
|
|
29
31
|
import { useRest } from "../driver/mongo/rest";
|
|
30
32
|
import moment from "moment";
|
|
@@ -42,6 +44,7 @@ import { logger } from "hono/logger";
|
|
|
42
44
|
import { csrf } from "hono/csrf";
|
|
43
45
|
import { v4 } from "uuid";
|
|
44
46
|
import type { SearchParams } from "meilisearch";
|
|
47
|
+
import { utils } from "..";
|
|
45
48
|
const cache = bentoCache.namespace("DNAX_API");
|
|
46
49
|
const app = new Hono();
|
|
47
50
|
const API_PATH = "/api";
|
|
@@ -92,6 +95,7 @@ function HonoInstance(): typeof app {
|
|
|
92
95
|
})
|
|
93
96
|
);
|
|
94
97
|
app.use(secureHeaders());
|
|
98
|
+
app.use(compress());
|
|
95
99
|
|
|
96
100
|
//eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoibm9tIiwiaWF0IjoxNzE3Nzc0MDQzLCJleHAiOjE3MTc3NzQxMDN9.Ud4-0y8pa4SMIcSn8PU1A-sjC-hT4ZVe_u3AdChyIJU
|
|
97
101
|
// Middlewares Injection
|
|
@@ -506,6 +510,15 @@ function HonoInstance(): typeof app {
|
|
|
506
510
|
|
|
507
511
|
// aggregate
|
|
508
512
|
if (action == "aggregate") {
|
|
513
|
+
if (
|
|
514
|
+
utils.containsForbiddenOperators(
|
|
515
|
+
body?.pipeline || [],
|
|
516
|
+
forbiddenPipelineOperators
|
|
517
|
+
) &&
|
|
518
|
+
!col?.customApi?.aggregate
|
|
519
|
+
) {
|
|
520
|
+
return fn.error("Forbidden operators in pipeline", 403);
|
|
521
|
+
}
|
|
509
522
|
response = await rest.aggregate(collection, body.pipeline || []);
|
|
510
523
|
}
|
|
511
524
|
|
package/app/index.ts
CHANGED
|
@@ -9,10 +9,10 @@ import pidusage from "pidusage";
|
|
|
9
9
|
import "@colors/colors";
|
|
10
10
|
import pkg from "../package.json";
|
|
11
11
|
import findPort from "find-open-port";
|
|
12
|
-
import { Server } from "socket.io";
|
|
13
12
|
import { webSocketServer } from "../lib/socket/instance";
|
|
14
13
|
import { HonoInstance } from "./hono";
|
|
15
14
|
import { utils } from "..";
|
|
15
|
+
import type { Server } from "bun";
|
|
16
16
|
type configRunApp = {
|
|
17
17
|
register?: Array<Function>;
|
|
18
18
|
beforeStart?: Array<Function>;
|
|
@@ -21,7 +21,7 @@ type configRunApp = {
|
|
|
21
21
|
destroy?: Array<Function>;
|
|
22
22
|
dbSync?: Array<Function>;
|
|
23
23
|
};
|
|
24
|
-
async function runApp(config?: configRunApp, clb?: Function) {
|
|
24
|
+
async function runApp(config?: configRunApp, clb?: Function): Promise<Server> {
|
|
25
25
|
hookDatabase.cb = config?.dbSync || [];
|
|
26
26
|
await loadCfg(); // Load Config
|
|
27
27
|
|
|
@@ -39,7 +39,7 @@ async function runApp(config?: configRunApp, clb?: Function) {
|
|
|
39
39
|
}); */
|
|
40
40
|
|
|
41
41
|
// available port Server
|
|
42
|
-
await findPort
|
|
42
|
+
return await findPort
|
|
43
43
|
.isAvailable(PORT)
|
|
44
44
|
.then(async (available: boolean) => {
|
|
45
45
|
// Start App server
|
|
@@ -76,36 +76,48 @@ async function runApp(config?: configRunApp, clb?: Function) {
|
|
|
76
76
|
websocket: webSocketServer(server),
|
|
77
77
|
});
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
let serverName =
|
|
80
|
+
process.env.SERVER_NAME || Cfg?.server?.name || "SERVER";
|
|
81
|
+
|
|
82
|
+
/* console.log(
|
|
83
|
+
boxen(`SERVER : ${PORT}`, {
|
|
84
|
+
borderStyle: "round",
|
|
85
|
+
title: `@dnax/server ${pkg.version}`,
|
|
86
|
+
padding: 1,
|
|
87
|
+
dimBorder: true,
|
|
88
|
+
titleAlignment: "center",
|
|
89
|
+
//float: "center",
|
|
90
|
+
//margin: 1,
|
|
91
|
+
borderColor: "gray",
|
|
92
|
+
})
|
|
93
|
+
); */
|
|
80
94
|
let info = "";
|
|
81
|
-
|
|
95
|
+
let envName = process.env?.NODE_ENV || "dev";
|
|
96
|
+
info += `${serverName}`.gray.underline.bold;
|
|
82
97
|
info += `\n`;
|
|
83
98
|
info += `\n`;
|
|
84
|
-
info +=
|
|
85
|
-
|
|
86
|
-
process.env.NODE_ENV === "production"
|
|
87
|
-
? "production"
|
|
88
|
-
: process.env?.NODE_ENV || "development"
|
|
89
|
-
}`.green + "\n";
|
|
90
|
-
info += `Server: http://localhost:${PORT}\n`.green;
|
|
99
|
+
info += `Env: ${envName}`.green.bold + "\n";
|
|
100
|
+
info += `url: http://localhost:${PORT}\n`.gray.bold;
|
|
91
101
|
//info += `Jwt Secret: ${Cfg.server.jwt?.secret}\n`.gray;
|
|
92
102
|
info += `\n`;
|
|
93
|
-
info += "TENANTS
|
|
103
|
+
info += "TENANTS :".gray.underline.bold;
|
|
94
104
|
info += `\n`;
|
|
95
105
|
Cfg.tenants?.map((t: any) => {
|
|
96
|
-
info += `\n${t?.name?.blue || "_"} : ${t?.id?.green}`.
|
|
106
|
+
info += `\n${t?.name?.blue || "_"} : ${t?.id?.green}`.bold;
|
|
97
107
|
});
|
|
98
108
|
info += `\n\n`;
|
|
99
109
|
info += `🔄 ${new Date().toLocaleString()}`.gray;
|
|
100
110
|
|
|
101
111
|
if (clb) clb();
|
|
102
112
|
|
|
113
|
+
console.log();
|
|
103
114
|
console.log(
|
|
104
115
|
boxen(info, {
|
|
105
|
-
borderStyle: "
|
|
116
|
+
borderStyle: "double",
|
|
106
117
|
title: `@dnax/server ${pkg.version}`,
|
|
107
|
-
padding: 1,
|
|
118
|
+
padding: 1.5,
|
|
108
119
|
dimBorder: true,
|
|
120
|
+
|
|
109
121
|
titleAlignment: "center",
|
|
110
122
|
//float: "center",
|
|
111
123
|
//margin: 1,
|
|
@@ -122,6 +134,8 @@ async function runApp(config?: configRunApp, clb?: Function) {
|
|
|
122
134
|
}
|
|
123
135
|
}
|
|
124
136
|
}
|
|
137
|
+
|
|
138
|
+
return server;
|
|
125
139
|
})
|
|
126
140
|
.catch((err: any) => {
|
|
127
141
|
console.error("Warning", err?.message);
|
package/bin/{index.ts → _.ts}
RENAMED
package/driver/mongo/rest.ts
CHANGED
|
@@ -73,6 +73,8 @@ const omitUpdate = [
|
|
|
73
73
|
"$set.updatedAt",
|
|
74
74
|
"$unset",
|
|
75
75
|
"$rename",
|
|
76
|
+
"$replaceRoot",
|
|
77
|
+
"$replaceWith",
|
|
76
78
|
];
|
|
77
79
|
|
|
78
80
|
class useRest {
|
|
@@ -258,7 +260,7 @@ class useRest {
|
|
|
258
260
|
let result = {
|
|
259
261
|
docs: [],
|
|
260
262
|
};
|
|
261
|
-
|
|
263
|
+
let useCustomApi = options?.useCustomApi ?? this.#useCustomApi;
|
|
262
264
|
let useHook = options?.useHook ?? this.#useHook;
|
|
263
265
|
let sharedData = {};
|
|
264
266
|
let col = getCollection(collection, this.#tenant_id);
|
|
@@ -297,6 +299,21 @@ class useRest {
|
|
|
297
299
|
});
|
|
298
300
|
}
|
|
299
301
|
|
|
302
|
+
if (col?.customApi?.aggregate && useCustomApi) {
|
|
303
|
+
let result = await col?.customApi?.aggregate({
|
|
304
|
+
error: fn.error,
|
|
305
|
+
io: Cfg.io,
|
|
306
|
+
session: sessionStorage(),
|
|
307
|
+
pipeline: toJson(pipeline),
|
|
308
|
+
rest: new useRest({
|
|
309
|
+
useHook: false,
|
|
310
|
+
tenant_id: this.#tenant_id,
|
|
311
|
+
useCustomApi: false,
|
|
312
|
+
}),
|
|
313
|
+
});
|
|
314
|
+
return resolve(result);
|
|
315
|
+
}
|
|
316
|
+
|
|
300
317
|
result.docs = await this.#tenant.database.db
|
|
301
318
|
?.collection(collection)
|
|
302
319
|
.aggregate(toBson(pipeline), {
|
package/index.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { FilesystemSftpAdapter, MinioAdapter } from "./lib/media";
|
|
|
11
11
|
import { crypt } from "./lib/crypto";
|
|
12
12
|
import { contextStorage } from "./lib/asyncLocalStorage";
|
|
13
13
|
import { Cron as Task } from "croner";
|
|
14
|
+
import { useWorkflow } from "./lib/workflow";
|
|
14
15
|
import { serveStatic } from "hono/bun";
|
|
15
16
|
// Adapter
|
|
16
17
|
|
|
@@ -37,4 +38,5 @@ export {
|
|
|
37
38
|
$,
|
|
38
39
|
contextStorage,
|
|
39
40
|
Adapter,
|
|
41
|
+
//useWorkflow,
|
|
40
42
|
};
|
package/lib/collection.ts
CHANGED
|
@@ -14,6 +14,7 @@ import consola from "consola";
|
|
|
14
14
|
|
|
15
15
|
async function loadAllCollections() {
|
|
16
16
|
let collections: Collection[] = [];
|
|
17
|
+
//collections.push(workflowCol);
|
|
17
18
|
if (Cfg.tenants) {
|
|
18
19
|
for await (let t of Cfg.tenants) {
|
|
19
20
|
let tenantPath = `${t.dir}/collections/**/**.model.{ts,js}`;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { useRest } from "../../driver/mongo";
|
|
2
|
+
import { v4 } from "uuid";
|
|
3
|
+
|
|
4
|
+
type WorkFlowConfig = {
|
|
5
|
+
tenant_id: string;
|
|
6
|
+
version?: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
type StepMeta = {
|
|
10
|
+
ms?: number;
|
|
11
|
+
[key: string]: any;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type StepStatus = "done" | "failed";
|
|
15
|
+
|
|
16
|
+
type StepFunction<TData = any> = (ctx: {
|
|
17
|
+
rest: useRest;
|
|
18
|
+
traceId: string;
|
|
19
|
+
data: TData;
|
|
20
|
+
meta?: Record<string, any>;
|
|
21
|
+
output: any;
|
|
22
|
+
}) => Promise<void>;
|
|
23
|
+
|
|
24
|
+
type Step = {
|
|
25
|
+
name: string;
|
|
26
|
+
handler: StepFunction;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
class Workflow<Tdata> {
|
|
30
|
+
name: string;
|
|
31
|
+
config: WorkFlowConfig;
|
|
32
|
+
#rest: useRest;
|
|
33
|
+
traceId?: string;
|
|
34
|
+
#steps: Step[] = [];
|
|
35
|
+
constructor(name: string, config: WorkFlowConfig) {
|
|
36
|
+
this.name = name;
|
|
37
|
+
this.config = config;
|
|
38
|
+
this.#rest = new useRest({
|
|
39
|
+
tenant_id: config.tenant_id,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
step(name: string, handler: StepFunction<Tdata>) {
|
|
44
|
+
this.#steps.push({
|
|
45
|
+
name,
|
|
46
|
+
handler,
|
|
47
|
+
});
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async run(data: Tdata, traceId?: string): Promise<void> {
|
|
52
|
+
this.traceId = traceId || v4();
|
|
53
|
+
let output: any = {};
|
|
54
|
+
for (const step of this.#steps) {
|
|
55
|
+
let startAt = Date.now();
|
|
56
|
+
let ev = {
|
|
57
|
+
name: step.name,
|
|
58
|
+
traceId: this.traceId,
|
|
59
|
+
duration: 0,
|
|
60
|
+
status: "pending",
|
|
61
|
+
startedAt: new Date(),
|
|
62
|
+
endedAt: new Date(),
|
|
63
|
+
};
|
|
64
|
+
output = await step.handler({
|
|
65
|
+
rest: this.#rest,
|
|
66
|
+
traceId: this.traceId,
|
|
67
|
+
data,
|
|
68
|
+
output,
|
|
69
|
+
});
|
|
70
|
+
let endAt = Date.now();
|
|
71
|
+
// duration en ms
|
|
72
|
+
let duration = endAt - startAt;
|
|
73
|
+
ev.duration = duration;
|
|
74
|
+
ev.status = "done";
|
|
75
|
+
ev.endedAt = new Date();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function useWorkflow<T>(name: string, config: WorkFlowConfig) {
|
|
81
|
+
return new Workflow<T>(name, config);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export { useWorkflow };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { useRest } from "../../driver/mongo";
|
|
2
|
+
|
|
3
|
+
export type StepActions = {
|
|
4
|
+
done: (comment?: string) => void;
|
|
5
|
+
fail: (comment?: string) => void;
|
|
6
|
+
abort: (comment?: string) => void;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export type Step<Name extends string = string> = {
|
|
10
|
+
name: Name;
|
|
11
|
+
description?: string;
|
|
12
|
+
exec: (ctx: { data: any; rest: useRest; action?: StepActions }) => void;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type stepData = {
|
|
16
|
+
name: string;
|
|
17
|
+
data: any;
|
|
18
|
+
at: Date;
|
|
19
|
+
comment?: string;
|
|
20
|
+
description?: string;
|
|
21
|
+
status: "done" | "failed" | "aborted";
|
|
22
|
+
traceId: string;
|
|
23
|
+
ms: number;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type EventStep = {
|
|
27
|
+
status: "failed" | "aborted" | "done";
|
|
28
|
+
data: any;
|
|
29
|
+
at: Date;
|
|
30
|
+
name: string;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type WorkflowData = {
|
|
34
|
+
_id: string;
|
|
35
|
+
name: string;
|
|
36
|
+
traceId: string;
|
|
37
|
+
currentStep: string;
|
|
38
|
+
steps: stepData[];
|
|
39
|
+
events: [];
|
|
40
|
+
status:
|
|
41
|
+
| "processing"
|
|
42
|
+
| "completed"
|
|
43
|
+
| "failed"
|
|
44
|
+
| "aborted"
|
|
45
|
+
| "cancelled"
|
|
46
|
+
| "initialized";
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export type WorkflowType = {
|
|
50
|
+
traceId: string;
|
|
51
|
+
_id: string;
|
|
52
|
+
name: string;
|
|
53
|
+
currentStep: string;
|
|
54
|
+
failedAtStep?: string;
|
|
55
|
+
steps: Step[];
|
|
56
|
+
events: [];
|
|
57
|
+
completedIf: () => boolean;
|
|
58
|
+
failedIf: () => boolean;
|
|
59
|
+
abortedIf: () => boolean;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export type StepInstance = {
|
|
63
|
+
run: (stepName: string, data?: any) => Promise<void>;
|
|
64
|
+
};
|
package/package.json
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dnax/core",
|
|
3
|
-
"version": "0.66.
|
|
3
|
+
"version": "0.66.2",
|
|
4
4
|
"module": "index.ts",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"dnaxi": "./bin/index.ts"
|
|
8
|
-
},
|
|
6
|
+
"bin": {},
|
|
9
7
|
"devDependencies": {
|
|
10
8
|
"@types/bun": "latest",
|
|
11
9
|
"@types/dot-object": "^2.1.6",
|
|
@@ -22,7 +20,6 @@
|
|
|
22
20
|
"typescript": "^5.8.3"
|
|
23
21
|
},
|
|
24
22
|
"dependencies": {
|
|
25
|
-
"@clack/prompts": "^0.10.0",
|
|
26
23
|
"@colors/colors": "^1.6.0",
|
|
27
24
|
"@lukeed/ms": "^2.0.2",
|
|
28
25
|
"bentocache": "^1.2.1",
|
|
@@ -47,14 +44,13 @@
|
|
|
47
44
|
"mingo": "^6.5.0",
|
|
48
45
|
"minio": "^8.0.5",
|
|
49
46
|
"moment": "^2.30.1",
|
|
50
|
-
"mongodb": "6.
|
|
47
|
+
"mongodb": "6.17.0",
|
|
51
48
|
"nodemailer": "^6.9.14",
|
|
52
49
|
"pidusage": "^4.0.0",
|
|
53
50
|
"pizzip": "^3.1.8",
|
|
54
51
|
"radash": "^12.1.0",
|
|
55
52
|
"rfc6902": "^5.1.2",
|
|
56
53
|
"sharp": "^0.33.5",
|
|
57
|
-
"signaldb": "^0.24.5",
|
|
58
54
|
"ssh2": "^1.16.0",
|
|
59
55
|
"ufo": "^1.5.4",
|
|
60
56
|
"urlencode": "^2.0.0",
|
package/types/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { Cron } from "croner";
|
|
|
3
3
|
//import type{ updateParams } from "./../driver/mongo/@types";
|
|
4
4
|
//import * as v from "valibot";
|
|
5
5
|
import type { Db, MongoClient } from "mongodb";
|
|
6
|
+
import { WorkflowInstance } from "../lib/workflow";
|
|
6
7
|
|
|
7
8
|
import { useRest } from "../driver/mongo/rest";
|
|
8
9
|
import { sessionStorage } from "../lib/asyncLocalStorage";
|
|
@@ -271,6 +272,7 @@ export type ctxApi = {
|
|
|
271
272
|
session?: sessionCtx;
|
|
272
273
|
io: socketIoType;
|
|
273
274
|
params?: findParam;
|
|
275
|
+
pipeline?: Array<object>;
|
|
274
276
|
id?: string;
|
|
275
277
|
ids?: string[];
|
|
276
278
|
error: typeof fn.error;
|
|
@@ -307,6 +309,7 @@ export type Collection = {
|
|
|
307
309
|
filesystemAdapter?: any;
|
|
308
310
|
};
|
|
309
311
|
customApi?: {
|
|
312
|
+
aggregate?: (ctx: ctxApi) => Array<object> | null | undefined;
|
|
310
313
|
insertOne?: (ctx: ctxApi) => object | null | undefined;
|
|
311
314
|
insertMany?: (ctx: ctxApi) => Array<object> | null | undefined;
|
|
312
315
|
updateOne?: (ctx: ctxApi) => object | null | undefined;
|
|
@@ -546,6 +549,7 @@ export type Config = {
|
|
|
546
549
|
onError?: (error: ApiError) => Promise<ApiError> | ApiError | void;
|
|
547
550
|
};
|
|
548
551
|
server: {
|
|
552
|
+
name?: string;
|
|
549
553
|
id?: string;
|
|
550
554
|
logger?: Boolean | loggerFunction;
|
|
551
555
|
whiteListIps?: Array<string>;
|
package/utils/index.ts
CHANGED
|
@@ -540,6 +540,15 @@ const file = {
|
|
|
540
540
|
},
|
|
541
541
|
};
|
|
542
542
|
|
|
543
|
+
function containsForbiddenOperators(
|
|
544
|
+
pipeline: any[],
|
|
545
|
+
forbiddenOperators: string[]
|
|
546
|
+
): boolean {
|
|
547
|
+
return pipeline.some((stage) =>
|
|
548
|
+
Object.keys(stage).some((operator) => forbiddenOperators.includes(operator))
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
|
|
543
552
|
const contextError = ContextError;
|
|
544
553
|
export {
|
|
545
554
|
file,
|
|
@@ -575,4 +584,5 @@ export {
|
|
|
575
584
|
deepEqual,
|
|
576
585
|
applyPatch,
|
|
577
586
|
createPatch,
|
|
587
|
+
containsForbiddenOperators,
|
|
578
588
|
};
|