@things-factory/shell 8.0.0-beta.9 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/client/themes/calendar-theme.css +1 -3
  2. package/client/themes/index.css +1 -2
  3. package/dist-server/server-dev.js +4 -2
  4. package/dist-server/server-dev.js.map +1 -1
  5. package/dist-server/server.js +4 -2
  6. package/dist-server/server.js.map +1 -1
  7. package/dist-server/tsconfig.tsbuildinfo +1 -1
  8. package/dist-server/typeorm/json5-transform.js +2 -2
  9. package/dist-server/typeorm/json5-transform.js.map +1 -1
  10. package/package.json +14 -14
  11. package/server/graphql-local-client.ts +59 -0
  12. package/server/index.ts +13 -0
  13. package/server/initializers/database.ts +96 -0
  14. package/server/initializers/naming-strategy.ts +14 -0
  15. package/server/middlewares/domain-middleware.ts +60 -0
  16. package/server/middlewares/index.ts +43 -0
  17. package/server/migrations/1000000000000-SeedDomain.ts +37 -0
  18. package/server/migrations/index.ts +9 -0
  19. package/server/pubsub-log-transport.ts +59 -0
  20. package/server/pubsub.ts +84 -0
  21. package/server/routers/domain-router.ts +13 -0
  22. package/server/routers/global-router.ts +76 -0
  23. package/server/routers/graphql-router.ts +3 -0
  24. package/server/routers/index.ts +3 -0
  25. package/server/schema.ts +163 -0
  26. package/server/server-dev.ts +305 -0
  27. package/server/server.ts +296 -0
  28. package/server/service/attribute-set/attribute-set-item-type.ts +65 -0
  29. package/server/service/attribute-set/attribute-set-mutation.ts +125 -0
  30. package/server/service/attribute-set/attribute-set-query.ts +36 -0
  31. package/server/service/attribute-set/attribute-set-type.ts +46 -0
  32. package/server/service/attribute-set/attribute-set.ts +35 -0
  33. package/server/service/attribute-set/index.ts +6 -0
  34. package/server/service/common-types/index.ts +6 -0
  35. package/server/service/common-types/list-param.ts +61 -0
  36. package/server/service/common-types/log.ts +17 -0
  37. package/server/service/common-types/object-ref.ts +13 -0
  38. package/server/service/common-types/scalar-any.ts +44 -0
  39. package/server/service/common-types/scalar-date.ts +22 -0
  40. package/server/service/common-types/scalar-object.ts +15 -0
  41. package/server/service/directive-transaction/index.ts +1 -0
  42. package/server/service/directive-transaction/transaction.ts +40 -0
  43. package/server/service/domain/domain-mutation.ts +120 -0
  44. package/server/service/domain/domain-query.ts +48 -0
  45. package/server/service/domain/domain-types.ts +63 -0
  46. package/server/service/domain/domain.ts +147 -0
  47. package/server/service/domain/index.ts +6 -0
  48. package/server/service/index.ts +32 -0
  49. package/server/service/subscription-data/data-resolver.ts +37 -0
  50. package/server/service/subscription-data/data-types.ts +16 -0
  51. package/server/service/subscription-data/index.ts +4 -0
  52. package/server/typeorm/encrypt-transform.ts +70 -0
  53. package/server/typeorm/get-data-encryption-key.ts +13 -0
  54. package/server/typeorm/json5-transform.ts +26 -0
  55. package/server/typeorm/round-transform.ts +20 -0
  56. package/server/utils/condition-builder.ts +145 -0
  57. package/server/utils/get-domain.ts +226 -0
  58. package/server/utils/get-query-builder-from-list-params.ts +469 -0
  59. package/server/utils/get-times-for-period.ts +60 -0
  60. package/server/utils/index.ts +8 -0
  61. package/server/utils/list-param-adjuster.ts +21 -0
  62. package/server/utils/list-params-converter.ts +200 -0
  63. package/server/utils/list-query-builder.ts +120 -0
  64. package/server/utils/publish-progress.ts +23 -0
  65. package/dist-server/process-cleaner.d.ts +0 -1
  66. package/dist-server/process-cleaner.js +0 -92
  67. package/dist-server/process-cleaner.js.map +0 -1
@@ -0,0 +1,200 @@
1
+ import { Between, Equal, FindOperator, ILike, In, IsNull, Like, Not, Raw } from 'typeorm'
2
+
3
+ import { Filter, ListParam, Pagination, Sorting } from '../service/common-types'
4
+ import { Domain } from '../service/domain/domain'
5
+ import { adjustFilters } from './list-param-adjuster'
6
+
7
+ const OPERATION_FUNCTION_MAP: { [operator: string]: (value: any) => FindOperator<any> } = {
8
+ search: value => ILike(value),
9
+ eq: value => Equal(value),
10
+ noteq: value => Not(Equal(value)),
11
+ like: value => Like(value),
12
+ i_like: value => ILike(value),
13
+ nlike: value => Not(Like(value)),
14
+ i_nlike: value => Not(ILike(value)),
15
+ lt: value => Raw(alias => `${alias} < ${typeof value === 'string' ? "'" + value + "'" : value}`),
16
+ gt: value => Raw(alias => `${alias} > ${typeof value === 'string' ? "'" + value + "'" : value}`),
17
+ lte: value => Raw(alias => `${alias} <= ${typeof value === 'string' ? "'" + value + "'" : value}`),
18
+ gte: value => Raw(alias => `${alias} >= ${typeof value === 'string' ? "'" + value + "'" : value}`),
19
+ in: value => In(value),
20
+ notin: value => Not(In(value)),
21
+ is_null: () => IsNull(),
22
+ is_not_null: () => Not(IsNull()),
23
+ is_false: () => Raw(alias => `${alias} IS FALSE`),
24
+ in_true: () => Raw(alias => `${alias} IS TRUE`),
25
+ is_not_true: () => Raw(alias => `${alias} IS NOT TRUE`),
26
+ is_present: () => Raw(alias => `${alias} IS PRESENT`),
27
+ is_blank: () => Raw(alias => `${alias} IS BLANK`),
28
+ is_empty_num_id: () => Raw(alias => `${alias} IS EMPTY NUMERIC ID`),
29
+ between: value => Between(value[0], value[1])
30
+ }
31
+
32
+ /**
33
+ * Get the TypeORM FindOperator function for a given filter.
34
+ * @param {Filter} filter - The filter object containing operator and value.
35
+ * @returns {FindOperator<any>} - The corresponding FindOperator function.
36
+ */
37
+ function getOperatorFunction({ operator, value }: Filter): FindOperator<any> {
38
+ return OPERATION_FUNCTION_MAP[operator](value)
39
+ }
40
+
41
+ /**
42
+ * Create pagination parameters for a TypeORM query.
43
+ * @param {Pagination} pagination - The pagination object.
44
+ * @returns {{ skip?: number; take?: number }} - The pagination parameters.
45
+ */
46
+ function makePaginationParams(pagination: Pagination): { skip?: number; take?: number } {
47
+ var result = {} as { skip?: number; take?: number }
48
+ if (pagination) {
49
+ var { page = 0, limit = 0 } = pagination
50
+ var skip = 0
51
+ var take = 0
52
+
53
+ if (limit > 0) {
54
+ skip = Math.max(page - 1, 0) * limit
55
+ take = limit
56
+ Object.assign(result, {
57
+ skip,
58
+ take
59
+ })
60
+ }
61
+ }
62
+
63
+ return result
64
+ }
65
+
66
+ /**
67
+ * Create sorting parameters for a TypeORM query.
68
+ * @param {Sorting[]} sortings - The array of sorting objects.
69
+ * @returns {{ order?: { [name: string]: 'DESC' | 'ASC' } }} - The sorting parameters.
70
+ */
71
+ function makeSortingParams(sortings: Sorting[]): { order?: { [name: string]: 'DESC' | 'ASC' } } {
72
+ var result = {} as { order?: { [name: string]: 'DESC' | 'ASC' } }
73
+ if (sortings) {
74
+ var order = {} as { [name: string]: 'DESC' | 'ASC' }
75
+ sortings.forEach(s => {
76
+ order[s.name] = s.desc ? 'DESC' : 'ASC'
77
+ })
78
+
79
+ Object.assign(result, {
80
+ order
81
+ })
82
+ }
83
+
84
+ return result
85
+ }
86
+
87
+ /**
88
+ * Create filter parameters for a TypeORM query.
89
+ * @param {Filter[]} filters - The array of filter objects.
90
+ * @param {string[]} searchables - The array of searchable field names.
91
+ * @returns {{ where: { [name: string]: FindOperator<any> } | { [name: string]: FindOperator<any> }[] }} - The filter parameters.
92
+ */
93
+ function makeFilterParams(
94
+ filters: Filter[],
95
+ searchables?: string[]
96
+ ): {
97
+ where: { [name: string]: FindOperator<any> } | { [name: string]: FindOperator<any> }[]
98
+ } {
99
+ /* for where AND clauses */
100
+ const columnFilters =
101
+ filters?.filter(filter => {
102
+ if (filter.operator === 'search') {
103
+ return false
104
+ }
105
+ if (filter.operator.toLowerCase().includes('like') && (!searchables || !searchables.includes(filter.name))) {
106
+ return false
107
+ }
108
+ return true
109
+ }) || []
110
+
111
+ const searchFilters =
112
+ searchables instanceof Array
113
+ ? filters?.filter(filter => {
114
+ if (filter.operator !== 'search') {
115
+ return false
116
+ }
117
+ if (!searchables.includes(filter.name)) {
118
+ console.warn('"searchables" setting is required for like searches with a heavy database query load', filter.name)
119
+ return false
120
+ }
121
+ return true
122
+ }) || []
123
+ : []
124
+
125
+ const columnWhere = columnFilters.reduce((where, f) => {
126
+ where[f.name] = getOperatorFunction(f)
127
+ return where
128
+ }, {} as { [name: string]: FindOperator<any> })
129
+
130
+ if (searchFilters.length === 0) {
131
+ return {
132
+ where: columnWhere
133
+ }
134
+ }
135
+
136
+ /* for fulltext searching ... OR-AND composition */
137
+ const searchWheres = searchFilters.map(f => {
138
+ return {
139
+ [f.name]: getOperatorFunction(f),
140
+ ...columnWhere
141
+ }
142
+ })
143
+
144
+ return {
145
+ where: searchWheres
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Convert ListParam object to TypeORM query parameters.
151
+ * @param {ListParam} params - The ListParam object containing pagination, sorting, and filtering options.
152
+ * @param {string | { domain?: string | Domain; searchables?: string[] }} options - Optional domain and searchables parameters.
153
+ * @returns {{ where?: { [name: string]: FindOperator<any> }; order?: { [name: string]: 'DESC' | 'ASC' }; skip?: number; take?: number }} - The TypeORM query parameters.
154
+ */
155
+ export function convertListParams(
156
+ params: ListParam,
157
+ options?:
158
+ | string
159
+ | {
160
+ domain?: string | Domain
161
+ searchables?: string[]
162
+ }
163
+ ): {
164
+ where?: { [name: string]: FindOperator<any> }
165
+ order?: {
166
+ [name: string]: 'DESC' | 'ASC'
167
+ }
168
+ skip?: number
169
+ take?: number
170
+ } {
171
+ var domainId: string | undefined
172
+
173
+ if (options) {
174
+ if (typeof options === 'string' /* for 하위 호환성 */) {
175
+ var domainId = options
176
+ } else {
177
+ var { domain, searchables } = options
178
+ var domainId = typeof domain === 'string' ? domain : domain?.id
179
+ }
180
+ }
181
+
182
+ var { pagination, filters = [], sortings } = params
183
+ var result = {}
184
+
185
+ if (domainId) {
186
+ filters = adjustFilters(filters, [
187
+ {
188
+ name: 'domain',
189
+ operator: 'eq',
190
+ value: domainId
191
+ }
192
+ ])
193
+ }
194
+
195
+ if (pagination) Object.assign(result, makePaginationParams(pagination))
196
+ if (sortings) Object.assign(result, makeSortingParams(sortings))
197
+ if (filters) Object.assign(result, makeFilterParams(filters, searchables))
198
+
199
+ return result
200
+ }
@@ -0,0 +1,120 @@
1
+ import { Brackets } from 'typeorm'
2
+
3
+ import { buildCondition } from './condition-builder'
4
+ import { Filter, ListParam, InheritedValueType } from '../service/common-types/list-param'
5
+
6
+ /**
7
+ * @deprecated This function is recommended to be replaced with the `getQueryBuilderFromListParams` function.
8
+ *
9
+ * Builds a TypeORM query using the provided parameters to filter, paginate, and sort the data.
10
+ *
11
+ * @param {QueryBuilder} queryBuilder - The TypeORM query builder to build the query on.
12
+ * @param {ListParam} params - The list parameters to apply to the query.
13
+ * @param {Object} context - The context object, typically containing information about the user's domain.
14
+ * @param {boolean|Object} options - Additional options for the query builder.
15
+ * @param {boolean} options.domainRef - Indicates whether to include domain reference in the query.
16
+ * @param {string[]} options.searchables - An array of searchable field names.
17
+ */
18
+ export const buildQuery = function (
19
+ queryBuilder: any,
20
+ params: ListParam,
21
+ context: any,
22
+ options?:
23
+ | boolean
24
+ | {
25
+ domainRef?: boolean
26
+ searchables?: string[]
27
+ }
28
+ ) {
29
+ /* default value of domainRef is 'true' */
30
+ var domainRef = typeof options === 'boolean' ? options : true
31
+
32
+ /* for backwards compatibility of function spec */
33
+ if (typeof options === 'object') {
34
+ var { domainRef = true, searchables } = options
35
+ }
36
+
37
+ const columnFilters: Filter[] =
38
+ params.filters?.filter(filter => {
39
+ if (filter.operator === 'search') {
40
+ return false
41
+ }
42
+ if (filter.operator.toLowerCase().includes('like') && (!searchables || !searchables.includes(filter.name))) {
43
+ console.warn('"searchables" setting is required for like searches with a heavy database query load', filter.name)
44
+ return false
45
+ }
46
+ return true
47
+ }) || []
48
+
49
+ const searchFilters =
50
+ searchables instanceof Array
51
+ ? params.filters?.filter(filter => {
52
+ if (filter.operator !== 'search') {
53
+ return false
54
+ }
55
+ if (!searchables.includes(filter.name)) {
56
+ console.warn('"searchables" setting is required for like searches with a heavy database query load', filter.name)
57
+ return false
58
+ }
59
+ return true
60
+ }) || []
61
+ : []
62
+
63
+ const pagination = params.pagination
64
+ const sortings = params.sortings
65
+ const domain = context?.state.domain
66
+
67
+ if (columnFilters && columnFilters.length > 0) {
68
+ columnFilters.forEach(filter => {
69
+ const condition = buildCondition(queryBuilder.alias, filter.name, filter.operator, filter.value, filter.relation, Object.keys(queryBuilder.getParameters()).length)
70
+
71
+ if (condition?.clause) queryBuilder.andWhere(condition.clause)
72
+ if (condition?.parameters) queryBuilder.setParameters(condition.parameters)
73
+ })
74
+ }
75
+
76
+ if (searchFilters.length > 0) {
77
+ queryBuilder.andWhere(
78
+ new Brackets(qb => {
79
+ searchFilters.forEach((filter, index) => {
80
+ const clause = `${queryBuilder.alias}.${filter.name} LIKE :${filter.name}`
81
+ const parameters = { [filter.name]: filter.value }
82
+
83
+ index === 0 ? qb.where(clause, parameters) : qb.orWhere(clause, parameters)
84
+ })
85
+ })
86
+ )
87
+ }
88
+
89
+ if (domainRef) {
90
+ const inherited = params?.inherited || InheritedValueType.None
91
+
92
+ if (!inherited || inherited == InheritedValueType.None) {
93
+ queryBuilder.andWhere(`${queryBuilder.alias}.domain = :domain`, { domain: domain.id })
94
+ } else if (inherited == InheritedValueType.Include) {
95
+ queryBuilder.andWhere(`${queryBuilder.alias}.domain In(:...domains)`, {
96
+ domains: [domain.id, domain.parentId].filter(Boolean)
97
+ })
98
+ } else if (inherited == InheritedValueType.Only) {
99
+ queryBuilder.andWhere(`${queryBuilder.alias}.domain = :domain`, { domain: domain.parentId || 'Impossible' })
100
+ } else {
101
+ queryBuilder.andWhere(`${queryBuilder.alias}.domain = :domain`, { domain: 'Impossible' })
102
+ }
103
+ }
104
+
105
+ if (pagination && pagination.page > 0 && pagination.limit > 0) {
106
+ queryBuilder.skip(pagination.limit * (pagination.page - 1))
107
+ queryBuilder.take(pagination.limit)
108
+ }
109
+
110
+ if (sortings && sortings.length > 0) {
111
+ sortings.forEach((sorting, index) => {
112
+ const sortField = sorting.name.split('.').length > 1 ? sorting.name : `${queryBuilder.alias}.${sorting.name}`
113
+ if (index === 0) {
114
+ queryBuilder.orderBy(sortField, sorting.desc ? 'DESC' : 'ASC')
115
+ } else {
116
+ queryBuilder.addOrderBy(sortField, sorting.desc ? 'DESC' : 'ASC')
117
+ }
118
+ })
119
+ }
120
+ }
@@ -0,0 +1,23 @@
1
+ import { pubsub } from '../pubsub'
2
+
3
+ /**
4
+ * Publishes a progress update message to the 'data' channel in the Pub/Sub system.
5
+ *
6
+ * @param {Object} param - The progress update parameters.
7
+ * @param {string} param.domain - The domain or category of the progress update.
8
+ * @param {string} param.tag - The tag or identifier for the progress update.
9
+ * @param {number} param.progress - The progress value indicating the completion percentage.
10
+ * @param {string} param.message - A message or description of the progress update.
11
+ */
12
+ export function publishProgress({ domain, tag, progress, message }) {
13
+ pubsub.publish('data', {
14
+ data: {
15
+ domain,
16
+ tag,
17
+ data: {
18
+ progress,
19
+ message
20
+ }
21
+ }
22
+ })
23
+ }
@@ -1 +0,0 @@
1
- export declare function setupProcessExitHandlers(): void;
@@ -1,92 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.setupProcessExitHandlers = setupProcessExitHandlers;
4
- const tslib_1 = require("tslib");
5
- const fs = tslib_1.__importStar(require("fs"));
6
- const path = tslib_1.__importStar(require("path"));
7
- const os = tslib_1.__importStar(require("os"));
8
- const child_process_1 = require("child_process");
9
- const TEMP_DIR = os.tmpdir(); // OS별 임시 디렉토리 사용
10
- const PID_FILE = path.join(TEMP_DIR, `puppeteer-${process.pid}.pid`);
11
- // 🛠 **이전에 실행된 유령 프로세스 정리**
12
- function cleanupZombieProcesses() {
13
- if (fs.existsSync(PID_FILE)) {
14
- const pid = fs.readFileSync(PID_FILE, 'utf8').trim();
15
- if (pid) {
16
- console.log(`Cleaning up old Puppeteer process with PID: ${pid}`);
17
- if (os.platform() === 'win32') {
18
- (0, child_process_1.exec)(`taskkill /F /PID ${pid}`, (err, stdout, stderr) => {
19
- if (err)
20
- console.error(`Failed to kill process ${pid} on Windows:`, err);
21
- else
22
- console.log(`Process ${pid} killed on Windows.`);
23
- });
24
- }
25
- else {
26
- try {
27
- process.kill(Number(pid), 'SIGKILL');
28
- console.log(`Process ${pid} killed on Unix-like OS.`);
29
- }
30
- catch (err) {
31
- console.error(`Failed to kill process ${pid}:`, err);
32
- }
33
- }
34
- }
35
- fs.unlinkSync(PID_FILE); // 프로세스 종료 후 PID 파일 삭제
36
- }
37
- }
38
- // **애플리케이션 시작 시 이전 프로세스 정리**
39
- cleanupZombieProcesses();
40
- // **현재 프로세스의 PID 저장**
41
- fs.writeFileSync(PID_FILE, process.pid.toString(), { flag: 'w' });
42
- async function cleanup() {
43
- try {
44
- if (fs.existsSync(PID_FILE)) {
45
- fs.unlinkSync(PID_FILE); // 종료 시 PID 파일 삭제
46
- }
47
- console.log('Cleaning up resources...');
48
- // **프로세스가 존재하는지 확인 후 종료 시도**
49
- try {
50
- process.kill(-process.pid, 'SIGTERM'); // 프로세스 그룹 전체 종료
51
- console.log(`Successfully killed process group (-${process.pid})`);
52
- }
53
- catch (err) {
54
- if (err.code === 'ESRCH') {
55
- console.warn(`Warning: Process group (-${process.pid}) already terminated.`);
56
- }
57
- else {
58
- console.error('Cleanup error:', err);
59
- }
60
- }
61
- }
62
- catch (err) {
63
- console.error('Cleanup error:', err);
64
- }
65
- }
66
- function setupProcessExitHandlers() {
67
- process.once('exit', async () => {
68
- console.log('Parent process is exiting, killing all child processes...');
69
- await cleanup();
70
- });
71
- process.once('SIGINT', async () => {
72
- console.log('\nReceived SIGINT, cleaning up...');
73
- await cleanup();
74
- process.exit(0);
75
- });
76
- process.once('SIGTERM', async () => {
77
- console.log('\nReceived SIGTERM, cleaning up...');
78
- await cleanup();
79
- process.exit(0);
80
- });
81
- process.once('uncaughtException', async (err) => {
82
- console.error('\nUncaught Exception:', err);
83
- await cleanup();
84
- process.exit(1);
85
- });
86
- process.once('unhandledRejection', async (reason) => {
87
- console.error('\nUnhandled Rejection:', reason);
88
- await cleanup();
89
- process.exit(1);
90
- });
91
- }
92
- //# sourceMappingURL=process-cleaner.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"process-cleaner.js","sourceRoot":"","sources":["../server/process-cleaner.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs'\nimport * as path from 'path'\nimport * as os from 'os'\nimport { exec } from 'child_process'\n\nconst TEMP_DIR = os.tmpdir() // OS별 임시 디렉토리 사용\nconst PID_FILE = path.join(TEMP_DIR, `puppeteer-${process.pid}.pid`)\n\n// 🛠 **이전에 실행된 유령 프로세스 정리**\nfunction cleanupZombieProcesses() {\n if (fs.existsSync(PID_FILE)) {\n const pid = fs.readFileSync(PID_FILE, 'utf8').trim()\n\n if (pid) {\n console.log(`Cleaning up old Puppeteer process with PID: ${pid}`)\n\n if (os.platform() === 'win32') {\n exec(`taskkill /F /PID ${pid}`, (err, stdout, stderr) => {\n if (err) console.error(`Failed to kill process ${pid} on Windows:`, err)\n else console.log(`Process ${pid} killed on Windows.`)\n })\n } else {\n try {\n process.kill(Number(pid), 'SIGKILL')\n console.log(`Process ${pid} killed on Unix-like OS.`)\n } catch (err) {\n console.error(`Failed to kill process ${pid}:`, err)\n }\n }\n }\n\n fs.unlinkSync(PID_FILE) // 프로세스 종료 후 PID 파일 삭제\n }\n}\n\n// **애플리케이션 시작 시 이전 프로세스 정리**\ncleanupZombieProcesses()\n\n// **현재 프로세스의 PID 저장**\nfs.writeFileSync(PID_FILE, process.pid.toString(), { flag: 'w' })\n\nasync function cleanup() {\n try {\n if (fs.existsSync(PID_FILE)) {\n fs.unlinkSync(PID_FILE) // 종료 시 PID 파일 삭제\n }\n\n console.log('Cleaning up resources...')\n\n // **프로세스가 존재하는지 확인 후 종료 시도**\n try {\n process.kill(-process.pid, 'SIGTERM') // 프로세스 그룹 전체 종료\n console.log(`Successfully killed process group (-${process.pid})`)\n } catch (err: any) {\n if (err.code === 'ESRCH') {\n console.warn(`Warning: Process group (-${process.pid}) already terminated.`)\n } else {\n console.error('Cleanup error:', err)\n }\n }\n } catch (err) {\n console.error('Cleanup error:', err)\n }\n}\n\nexport function setupProcessExitHandlers() {\n process.once('exit', async () => {\n console.log('Parent process is exiting, killing all child processes...')\n await cleanup()\n })\n\n process.once('SIGINT', async () => {\n console.log('\\nReceived SIGINT, cleaning up...')\n await cleanup()\n process.exit(0)\n })\n\n process.once('SIGTERM', async () => {\n console.log('\\nReceived SIGTERM, cleaning up...')\n await cleanup()\n process.exit(0)\n })\n\n process.once('uncaughtException', async err => {\n console.error('\\nUncaught Exception:', err)\n await cleanup()\n process.exit(1)\n })\n\n process.once('unhandledRejection', async reason => {\n console.error('\\nUnhandled Rejection:', reason)\n await cleanup()\n process.exit(1)\n })\n}\n"]}