sonamu 0.4.10 → 0.4.12
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/dist/{base-model-w9EuyYif.d.ts → base-model-Br6krkwK.d.ts} +1 -1
- package/dist/{base-model-ChAfKHR7.d.mts → base-model-BvVra-8f.d.mts} +1 -1
- package/dist/bin/cli.js +51 -51
- package/dist/bin/cli.mjs +2 -2
- package/dist/{chunk-C7NTXZWZ.mjs → chunk-FKZK27YL.mjs} +2 -2
- package/dist/{chunk-IPTFUZN2.js → chunk-FYLFH3Q6.js} +100 -100
- package/dist/{chunk-I7O4VXTH.js → chunk-IEMX4VPN.js} +4 -4
- package/dist/{chunk-QT3GBIMP.js → chunk-INTZUNZ6.js} +7 -7
- package/dist/{chunk-IPFCELQ3.mjs → chunk-JQJTQQ7D.mjs} +2 -2
- package/dist/{chunk-EEUCHV4P.mjs → chunk-LNZTU4JC.mjs} +70 -41
- package/dist/chunk-LNZTU4JC.mjs.map +1 -0
- package/dist/{chunk-M6MCZP64.mjs → chunk-NPLUHS5L.mjs} +2 -2
- package/dist/{chunk-E2I4OSFY.js → chunk-ZLFDB43J.js} +70 -41
- package/dist/chunk-ZLFDB43J.js.map +1 -0
- package/dist/database/drivers/knex/base-model.d.mts +2 -2
- package/dist/database/drivers/knex/base-model.d.ts +2 -2
- package/dist/database/drivers/knex/base-model.js +8 -8
- package/dist/database/drivers/knex/base-model.mjs +3 -3
- package/dist/database/drivers/kysely/base-model.d.mts +2 -2
- package/dist/database/drivers/kysely/base-model.d.ts +2 -2
- package/dist/database/drivers/kysely/base-model.js +9 -9
- package/dist/database/drivers/kysely/base-model.mjs +3 -3
- package/dist/index.d.mts +13 -5
- package/dist/index.d.ts +13 -5
- package/dist/index.js +18 -7
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +14 -3
- package/dist/index.mjs.map +1 -1
- package/dist/{model-BF9CDic2.d.ts → model-DWoinpJ7.d.mts} +73 -5
- package/dist/{model-BF9CDic2.d.mts → model-DWoinpJ7.d.ts} +73 -5
- package/package.json +1 -1
- package/src/api/base-frame.ts +14 -0
- package/src/api/decorators.ts +2 -1
- package/src/index.ts +1 -0
- package/src/syncer/syncer.ts +77 -40
- package/src/templates/service.template.ts +4 -6
- package/src/types/types.ts +11 -1
- package/dist/chunk-E2I4OSFY.js.map +0 -1
- package/dist/chunk-EEUCHV4P.mjs.map +0 -1
- /package/dist/{chunk-C7NTXZWZ.mjs.map → chunk-FKZK27YL.mjs.map} +0 -0
- /package/dist/{chunk-IPTFUZN2.js.map → chunk-FYLFH3Q6.js.map} +0 -0
- /package/dist/{chunk-I7O4VXTH.js.map → chunk-IEMX4VPN.js.map} +0 -0
- /package/dist/{chunk-QT3GBIMP.js.map → chunk-INTZUNZ6.js.map} +0 -0
- /package/dist/{chunk-IPFCELQ3.mjs.map → chunk-JQJTQQ7D.mjs.map} +0 -0
- /package/dist/{chunk-M6MCZP64.mjs.map → chunk-NPLUHS5L.mjs.map} +0 -0
|
@@ -469,11 +469,59 @@ declare const TemplateOptions: z.ZodObject<{
|
|
|
469
469
|
entityId: string;
|
|
470
470
|
}>;
|
|
471
471
|
service: z.ZodObject<{
|
|
472
|
-
|
|
472
|
+
namesRecord: z.ZodObject<{
|
|
473
|
+
fs: z.ZodString;
|
|
474
|
+
fsPlural: z.ZodString;
|
|
475
|
+
camel: z.ZodString;
|
|
476
|
+
camelPlural: z.ZodString;
|
|
477
|
+
capital: z.ZodString;
|
|
478
|
+
capitalPlural: z.ZodString;
|
|
479
|
+
upper: z.ZodString;
|
|
480
|
+
constant: z.ZodString;
|
|
481
|
+
}, "strip", z.ZodTypeAny, {
|
|
482
|
+
fs: string;
|
|
483
|
+
fsPlural: string;
|
|
484
|
+
camel: string;
|
|
485
|
+
camelPlural: string;
|
|
486
|
+
capital: string;
|
|
487
|
+
capitalPlural: string;
|
|
488
|
+
upper: string;
|
|
489
|
+
constant: string;
|
|
490
|
+
}, {
|
|
491
|
+
fs: string;
|
|
492
|
+
fsPlural: string;
|
|
493
|
+
camel: string;
|
|
494
|
+
camelPlural: string;
|
|
495
|
+
capital: string;
|
|
496
|
+
capitalPlural: string;
|
|
497
|
+
upper: string;
|
|
498
|
+
constant: string;
|
|
499
|
+
}>;
|
|
500
|
+
modelTsPath: z.ZodString;
|
|
473
501
|
}, "strip", z.ZodTypeAny, {
|
|
474
|
-
|
|
502
|
+
namesRecord: {
|
|
503
|
+
fs: string;
|
|
504
|
+
fsPlural: string;
|
|
505
|
+
camel: string;
|
|
506
|
+
camelPlural: string;
|
|
507
|
+
capital: string;
|
|
508
|
+
capitalPlural: string;
|
|
509
|
+
upper: string;
|
|
510
|
+
constant: string;
|
|
511
|
+
};
|
|
512
|
+
modelTsPath: string;
|
|
475
513
|
}, {
|
|
476
|
-
|
|
514
|
+
namesRecord: {
|
|
515
|
+
fs: string;
|
|
516
|
+
fsPlural: string;
|
|
517
|
+
camel: string;
|
|
518
|
+
camelPlural: string;
|
|
519
|
+
capital: string;
|
|
520
|
+
capitalPlural: string;
|
|
521
|
+
upper: string;
|
|
522
|
+
constant: string;
|
|
523
|
+
};
|
|
524
|
+
modelTsPath: string;
|
|
477
525
|
}>;
|
|
478
526
|
view_list: z.ZodObject<{
|
|
479
527
|
entityId: z.ZodString;
|
|
@@ -611,7 +659,17 @@ declare const TemplateOptions: z.ZodObject<{
|
|
|
611
659
|
entityId: string;
|
|
612
660
|
};
|
|
613
661
|
service: {
|
|
614
|
-
|
|
662
|
+
namesRecord: {
|
|
663
|
+
fs: string;
|
|
664
|
+
fsPlural: string;
|
|
665
|
+
camel: string;
|
|
666
|
+
camelPlural: string;
|
|
667
|
+
capital: string;
|
|
668
|
+
capitalPlural: string;
|
|
669
|
+
upper: string;
|
|
670
|
+
constant: string;
|
|
671
|
+
};
|
|
672
|
+
modelTsPath: string;
|
|
615
673
|
};
|
|
616
674
|
view_list: {
|
|
617
675
|
entityId: string;
|
|
@@ -683,7 +741,17 @@ declare const TemplateOptions: z.ZodObject<{
|
|
|
683
741
|
entityId: string;
|
|
684
742
|
};
|
|
685
743
|
service: {
|
|
686
|
-
|
|
744
|
+
namesRecord: {
|
|
745
|
+
fs: string;
|
|
746
|
+
fsPlural: string;
|
|
747
|
+
camel: string;
|
|
748
|
+
camelPlural: string;
|
|
749
|
+
capital: string;
|
|
750
|
+
capitalPlural: string;
|
|
751
|
+
upper: string;
|
|
752
|
+
constant: string;
|
|
753
|
+
};
|
|
754
|
+
modelTsPath: string;
|
|
687
755
|
};
|
|
688
756
|
view_list: {
|
|
689
757
|
entityId: string;
|
package/package.json
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Knex } from "knex";
|
|
2
|
+
import { DB } from "../database/db";
|
|
3
|
+
import { DBPreset } from "../database/types";
|
|
4
|
+
import { UpsertBuilder } from "../database/upsert-builder";
|
|
5
|
+
|
|
6
|
+
export abstract class BaseFrameClass {
|
|
7
|
+
getDB(which: DBPreset): Knex {
|
|
8
|
+
return DB.getDB(which) as Knex;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
getUpsertBuilder() {
|
|
12
|
+
return new UpsertBuilder<"knex">();
|
|
13
|
+
}
|
|
14
|
+
}
|
package/src/api/decorators.ts
CHANGED
|
@@ -49,8 +49,9 @@ export function api(options: ApiDecoratorOptions = {}) {
|
|
|
49
49
|
return function (target: Object, propertyKey: string) {
|
|
50
50
|
const modelName = target.constructor.name.match(/(.+)Class$/)![1];
|
|
51
51
|
const methodName = propertyKey;
|
|
52
|
+
|
|
52
53
|
const defaultPath = `/${inflection.camelize(
|
|
53
|
-
modelName.replace(/Model$/, ""),
|
|
54
|
+
modelName.replace(/Model$/, "").replace(/Frame$/, ""),
|
|
54
55
|
true
|
|
55
56
|
)}/${inflection.camelize(propertyKey, true)}`;
|
|
56
57
|
|
package/src/index.ts
CHANGED
|
@@ -2,6 +2,7 @@ export * from "./api/code-converters";
|
|
|
2
2
|
export * from "./api/context";
|
|
3
3
|
export * from "./api/decorators";
|
|
4
4
|
export * from "./api/sonamu";
|
|
5
|
+
export * from "./api/base-frame";
|
|
5
6
|
export * from "./database/db";
|
|
6
7
|
export * from "./database/upsert-builder";
|
|
7
8
|
export * from "./database/types";
|
package/src/syncer/syncer.ts
CHANGED
|
@@ -5,7 +5,7 @@ import crypto from "crypto";
|
|
|
5
5
|
import equal from "fast-deep-equal";
|
|
6
6
|
import _ from "lodash";
|
|
7
7
|
import inflection from "inflection";
|
|
8
|
-
import { EntityManager } from "../entity/entity-manager";
|
|
8
|
+
import { EntityManager, EntityNamesRecord } from "../entity/entity-manager";
|
|
9
9
|
import ts from "typescript";
|
|
10
10
|
import {
|
|
11
11
|
ApiParam,
|
|
@@ -79,7 +79,13 @@ import { Template__kysely_interface } from "../templates/kysely_types.template";
|
|
|
79
79
|
import { DB } from "../database/db";
|
|
80
80
|
import { setTimeout as setTimeoutPromises } from "timers/promises";
|
|
81
81
|
|
|
82
|
-
type FileType =
|
|
82
|
+
type FileType =
|
|
83
|
+
| "model"
|
|
84
|
+
| "types"
|
|
85
|
+
| "functions"
|
|
86
|
+
| "generated"
|
|
87
|
+
| "entity"
|
|
88
|
+
| "frame";
|
|
83
89
|
type GlobPattern = {
|
|
84
90
|
[key in FileType]: string;
|
|
85
91
|
};
|
|
@@ -200,7 +206,7 @@ export class Syncer {
|
|
|
200
206
|
// 다른 부분 찾아 액션
|
|
201
207
|
const diffGroups = _.groupBy(diffFiles, (r) => {
|
|
202
208
|
const matched = r.match(
|
|
203
|
-
/\.(model|types|functions|entity|generated)\.[tj]s/
|
|
209
|
+
/\.(model|types|functions|entity|generated|frame)\.[tj]s/
|
|
204
210
|
);
|
|
205
211
|
return matched![1];
|
|
206
212
|
}) as unknown as DiffGroups;
|
|
@@ -255,12 +261,44 @@ export class Syncer {
|
|
|
255
261
|
}
|
|
256
262
|
|
|
257
263
|
// 트리거: model
|
|
258
|
-
if (diffTypes.includes("model")) {
|
|
259
|
-
const entityIds = this.getEntityIdFromPath(diffGroups["model"]);
|
|
264
|
+
if (diffTypes.includes("model") || diffTypes.includes("frame")) {
|
|
260
265
|
console.log("// 액션: 서비스 생성");
|
|
261
|
-
|
|
266
|
+
const mergedGroup = [
|
|
267
|
+
...(diffGroups["model"] ?? []),
|
|
268
|
+
...(diffGroups["frame"] ?? []),
|
|
269
|
+
];
|
|
270
|
+
const params: { namesRecord: EntityNamesRecord; modelTsPath: string }[] =
|
|
271
|
+
mergedGroup.map((modelPath) => {
|
|
272
|
+
if (modelPath.endsWith(".model.js")) {
|
|
273
|
+
const entityId = this.getEntityIdFromPath([modelPath])[0];
|
|
274
|
+
return {
|
|
275
|
+
namesRecord: EntityManager.getNamesFromId(entityId),
|
|
276
|
+
modelTsPath: path.join(
|
|
277
|
+
Sonamu.apiRootPath,
|
|
278
|
+
modelPath
|
|
279
|
+
.replace("/dist/", "/src/")
|
|
280
|
+
.replace(".model.js", ".model.ts")
|
|
281
|
+
),
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
if (modelPath.endsWith("frame.js")) {
|
|
285
|
+
const [, frameName] = modelPath.match(/.+\/(.+)\.frame.js$/) ?? [];
|
|
286
|
+
return {
|
|
287
|
+
namesRecord: EntityManager.getNamesFromId(frameName),
|
|
288
|
+
modelTsPath: path.join(
|
|
289
|
+
Sonamu.apiRootPath,
|
|
290
|
+
modelPath
|
|
291
|
+
.replace("/dist/", "/src/")
|
|
292
|
+
.replace(".frame.js", ".frame.ts")
|
|
293
|
+
),
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
throw new Error("not reachable");
|
|
297
|
+
});
|
|
298
|
+
await this.actionGenerateServices(params);
|
|
299
|
+
|
|
262
300
|
console.log("// 액션: HTTP파일 생성");
|
|
263
|
-
await this.actionGenerateHttps(
|
|
301
|
+
await this.actionGenerateHttps();
|
|
264
302
|
}
|
|
265
303
|
|
|
266
304
|
// 저장
|
|
@@ -292,19 +330,18 @@ export class Syncer {
|
|
|
292
330
|
.flat();
|
|
293
331
|
}
|
|
294
332
|
|
|
295
|
-
async actionGenerateServices(
|
|
333
|
+
async actionGenerateServices(
|
|
334
|
+
paramsArray: {
|
|
335
|
+
namesRecord: EntityNamesRecord;
|
|
336
|
+
modelTsPath: string;
|
|
337
|
+
}[]
|
|
338
|
+
): Promise<string[]> {
|
|
296
339
|
return (
|
|
297
340
|
await Promise.all(
|
|
298
|
-
|
|
299
|
-
this.generateTemplate(
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
entityId,
|
|
303
|
-
},
|
|
304
|
-
{
|
|
305
|
-
overwrite: true,
|
|
306
|
-
}
|
|
307
|
-
)
|
|
341
|
+
paramsArray.map(async (params) =>
|
|
342
|
+
this.generateTemplate("service", params, {
|
|
343
|
+
overwrite: true,
|
|
344
|
+
})
|
|
308
345
|
)
|
|
309
346
|
)
|
|
310
347
|
)
|
|
@@ -312,10 +349,10 @@ export class Syncer {
|
|
|
312
349
|
.flat();
|
|
313
350
|
}
|
|
314
351
|
|
|
315
|
-
async actionGenerateHttps(
|
|
352
|
+
async actionGenerateHttps(): Promise<string[]> {
|
|
316
353
|
const [res] = await this.generateTemplate(
|
|
317
354
|
"generated_http",
|
|
318
|
-
{
|
|
355
|
+
{},
|
|
319
356
|
{ overwrite: true }
|
|
320
357
|
);
|
|
321
358
|
return res;
|
|
@@ -383,6 +420,7 @@ export class Syncer {
|
|
|
383
420
|
functions: Sonamu.apiRootPath + "/src/application/**/*.functions.ts",
|
|
384
421
|
/* compiled-JS 체크 */
|
|
385
422
|
model: Sonamu.apiRootPath + "/dist/application/**/*.model.js",
|
|
423
|
+
frame: Sonamu.apiRootPath + "/dist/application/**/*.frame.js",
|
|
386
424
|
};
|
|
387
425
|
|
|
388
426
|
const filePaths = (
|
|
@@ -525,6 +563,12 @@ export class Syncer {
|
|
|
525
563
|
method.methodName === api.methodName
|
|
526
564
|
);
|
|
527
565
|
});
|
|
566
|
+
if (currentModelApis.length === 0) {
|
|
567
|
+
// const p = path.join(tmpdir(), "sonamu-syncer-error.json");
|
|
568
|
+
// writeFileSync(p, JSON.stringify(registeredApis, null, 2));
|
|
569
|
+
// execSync(`open ${p}`);
|
|
570
|
+
throw new Error(`현재 파일에 사전 등록된 API가 없습니다. ${filePath}`);
|
|
571
|
+
}
|
|
528
572
|
|
|
529
573
|
// 등록된 API에 현재 메소드 타입 정보 확장
|
|
530
574
|
const extendedApis = currentModelApis.map((api) => {
|
|
@@ -722,7 +766,7 @@ export class Syncer {
|
|
|
722
766
|
async autoloadApis() {
|
|
723
767
|
const pathPattern = path.join(
|
|
724
768
|
Sonamu.apiRootPath,
|
|
725
|
-
"/src/application/**/*.model.ts"
|
|
769
|
+
"/src/application/**/*.{model,frame}.ts"
|
|
726
770
|
);
|
|
727
771
|
// console.debug(chalk.yellow(`autoload:APIs @ ${pathPattern}`));
|
|
728
772
|
|
|
@@ -737,7 +781,7 @@ export class Syncer {
|
|
|
737
781
|
async autoloadModels(): Promise<{ [modelName: string]: unknown }> {
|
|
738
782
|
const pathPattern = path.join(
|
|
739
783
|
Sonamu.apiRootPath,
|
|
740
|
-
"dist/application/**/*.model.js"
|
|
784
|
+
"dist/application/**/*.{model,frame}.js"
|
|
741
785
|
);
|
|
742
786
|
// console.debug(chalk.yellow(`autoload:models @ ${pathPattern}`));
|
|
743
787
|
|
|
@@ -752,7 +796,9 @@ export class Syncer {
|
|
|
752
796
|
.map(({ imported }) => Object.entries(imported))
|
|
753
797
|
.flat();
|
|
754
798
|
this.models = Object.fromEntries(
|
|
755
|
-
functions.filter(
|
|
799
|
+
functions.filter(
|
|
800
|
+
([name]) => name.endsWith("Model") || name.endsWith("Frame")
|
|
801
|
+
)
|
|
756
802
|
);
|
|
757
803
|
return this.models;
|
|
758
804
|
}
|
|
@@ -839,22 +885,13 @@ export class Syncer {
|
|
|
839
885
|
const template: Template = this.getTemplate(key);
|
|
840
886
|
|
|
841
887
|
let extra: unknown[] = [];
|
|
842
|
-
if (
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
)
|
|
846
|
-
) {
|
|
847
|
-
const entityId = (options as TemplateOptions["
|
|
848
|
-
|
|
849
|
-
if (key === "service" || key === "generated_http") {
|
|
850
|
-
// service 필요 정보 (API 리스트)
|
|
851
|
-
const entity = EntityManager.get(entityId!);
|
|
852
|
-
const modelTsPath = `${path.join(
|
|
853
|
-
Sonamu.apiRootPath,
|
|
854
|
-
"/src/application"
|
|
855
|
-
)}/${entity.names.fs}/${entity.names.fs}.model.ts`;
|
|
856
|
-
extra = [await this.readApisFromFile(modelTsPath)];
|
|
857
|
-
} else if (key === "view_list" || key === "model") {
|
|
888
|
+
if (key === "service") {
|
|
889
|
+
// service 필요 정보 (API 리스트)
|
|
890
|
+
const { modelTsPath } = options as TemplateOptions["service"];
|
|
891
|
+
extra = [await this.readApisFromFile(modelTsPath)];
|
|
892
|
+
} else if (["model", "view_list", "view_form"].includes(key)) {
|
|
893
|
+
const entityId = (options as TemplateOptions["model"]).entityId;
|
|
894
|
+
if (key === "view_list" || key === "model") {
|
|
858
895
|
// view_list 필요 정보 (컬럼 노드, 리스트파라미터 노드)
|
|
859
896
|
const columnsNode = await this.getColumnsNode(entityId, "A");
|
|
860
897
|
const listParamsZodType = await this.getZodTypeById(
|
|
@@ -1383,7 +1420,7 @@ export class Syncer {
|
|
|
1383
1420
|
// generate schemas, types
|
|
1384
1421
|
await Promise.all([
|
|
1385
1422
|
this.actionGenerateSchemas(),
|
|
1386
|
-
...(form.
|
|
1423
|
+
...(form.parentId === undefined
|
|
1387
1424
|
? [
|
|
1388
1425
|
this.generateTemplate("init_types", {
|
|
1389
1426
|
entityId: form.entityId,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import inflection from "inflection";
|
|
2
2
|
import _ from "lodash";
|
|
3
3
|
import { TemplateOptions } from "../types/types";
|
|
4
|
-
import {
|
|
4
|
+
import { EntityNamesRecord } from "../entity/entity-manager";
|
|
5
5
|
import { ApiParamType, ApiParam } from "../types/types";
|
|
6
6
|
import {
|
|
7
7
|
apiParamTypeToTsType,
|
|
@@ -24,9 +24,7 @@ export class Template__service extends Template {
|
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
render({
|
|
28
|
-
const names = EntityManager.getNamesFromId(entityId);
|
|
29
|
-
|
|
27
|
+
render({ namesRecord }: TemplateOptions["service"], apis: ExtendedApi[]) {
|
|
30
28
|
// 서비스 TypeSource
|
|
31
29
|
const { lines, importKeys } = this.getTypeSource(apis);
|
|
32
30
|
|
|
@@ -36,7 +34,7 @@ export class Template__service extends Template {
|
|
|
36
34
|
);
|
|
37
35
|
|
|
38
36
|
return {
|
|
39
|
-
...this.getTargetAndPath(
|
|
37
|
+
...this.getTargetAndPath(namesRecord),
|
|
40
38
|
body: lines.join("\n"),
|
|
41
39
|
importKeys: importKeys.filter(
|
|
42
40
|
(key) => ["ListResult"].includes(key) === false
|
|
@@ -154,7 +152,7 @@ export class Template__service extends Template {
|
|
|
154
152
|
})
|
|
155
153
|
.join("\n\n");
|
|
156
154
|
|
|
157
|
-
return `export namespace ${modelName.replace(/Model$/, "Service")} {
|
|
155
|
+
return `export namespace ${modelName.replace(/Model$/, "Service").replace(/Frame$/, "Service")} {
|
|
158
156
|
${methodCodes}
|
|
159
157
|
}`;
|
|
160
158
|
})
|
package/src/types/types.ts
CHANGED
|
@@ -686,7 +686,17 @@ export const TemplateOptions = z.object({
|
|
|
686
686
|
entityId: z.string(),
|
|
687
687
|
}),
|
|
688
688
|
service: z.object({
|
|
689
|
-
|
|
689
|
+
namesRecord: z.object({
|
|
690
|
+
fs: z.string(),
|
|
691
|
+
fsPlural: z.string(),
|
|
692
|
+
camel: z.string(),
|
|
693
|
+
camelPlural: z.string(),
|
|
694
|
+
capital: z.string(),
|
|
695
|
+
capitalPlural: z.string(),
|
|
696
|
+
upper: z.string(),
|
|
697
|
+
constant: z.string(),
|
|
698
|
+
}),
|
|
699
|
+
modelTsPath: z.string(),
|
|
690
700
|
}),
|
|
691
701
|
view_list: z.object({
|
|
692
702
|
entityId: z.string(),
|