sonamu 0.5.0 → 0.5.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/dist/api/context.d.ts +4 -1
- package/dist/api/context.d.ts.map +1 -1
- package/dist/api/decorators.d.ts.map +1 -1
- package/dist/api/decorators.js +1 -1
- package/dist/api/decorators.js.map +1 -1
- package/dist/api/sonamu.d.ts +7 -6
- package/dist/api/sonamu.d.ts.map +1 -1
- package/dist/api/sonamu.js +1 -1
- package/dist/api/sonamu.js.map +1 -1
- package/dist/bin/build-config.d.ts +4 -0
- package/dist/bin/build-config.d.ts.map +1 -1
- package/dist/bin/build-config.js +1 -1
- package/dist/bin/build-config.js.map +1 -1
- package/dist/bin/cli-wrapper.js +1 -1
- package/dist/bin/cli-wrapper.js.map +1 -1
- package/dist/bin/cli.js +1 -1
- package/dist/bin/cli.js.map +1 -1
- package/dist/database/base-model.d.ts +8 -1
- package/dist/database/base-model.d.ts.map +1 -1
- package/dist/database/base-model.js +1 -1
- package/dist/database/base-model.js.map +1 -1
- package/dist/database/db.js +1 -1
- package/dist/database/db.js.map +1 -1
- package/dist/file-storage/driver.d.ts +3 -0
- package/dist/file-storage/driver.d.ts.map +1 -1
- package/dist/file-storage/driver.js +1 -1
- package/dist/file-storage/driver.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/stream/index.d.ts +2 -0
- package/dist/stream/index.d.ts.map +1 -0
- package/dist/stream/index.js +2 -0
- package/dist/stream/index.js.map +1 -0
- package/dist/stream/sse.d.ts +13 -0
- package/dist/stream/sse.d.ts.map +1 -0
- package/dist/stream/sse.js +2 -0
- package/dist/stream/sse.js.map +1 -0
- package/dist/types/types.d.ts +5 -3
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/types.js.map +1 -1
- package/package.json +6 -4
- package/src/api/context.ts +10 -5
- package/src/api/decorators.ts +1 -3
- package/src/api/sonamu.ts +60 -26
- package/src/bin/build-config.ts +5 -0
- package/src/bin/cli-wrapper.ts +20 -6
- package/src/bin/cli.ts +16 -21
- package/src/database/base-model.ts +29 -3
- package/src/database/db.ts +1 -1
- package/src/file-storage/driver.ts +10 -0
- package/src/index.ts +1 -0
- package/src/stream/index.ts +1 -0
- package/src/stream/sse.ts +49 -0
- package/src/types/types.ts +10 -4
- package/tsconfig.json +4 -0
- package/dist/api/sonamu.types.d.ts +0 -30
- package/dist/api/sonamu.types.d.ts.map +0 -1
- package/dist/api/sonamu.types.js +0 -2
- package/dist/api/sonamu.types.js.map +0 -1
- package/dist/base-model-CEB0H0aO.d.mts +0 -43
- package/dist/base-model-CrqDMYhI.d.ts +0 -43
- package/dist/bin/cli-wrapper.d.mts +0 -1
- package/dist/bin/cli-wrapper.mjs +0 -43
- package/dist/bin/cli-wrapper.mjs.map +0 -1
- package/dist/bin/cli.d.mts +0 -2
- package/dist/bin/cli.mjs +0 -907
- package/dist/bin/cli.mjs.map +0 -1
- package/dist/chunk-2WAC2GER.js +0 -7625
- package/dist/chunk-2WAC2GER.js.map +0 -1
- package/dist/chunk-C3IPIF6O.mjs +0 -1581
- package/dist/chunk-C3IPIF6O.mjs.map +0 -1
- package/dist/chunk-EXHKSVTE.js +0 -280
- package/dist/chunk-EXHKSVTE.js.map +0 -1
- package/dist/chunk-FCERKIIF.mjs +0 -7623
- package/dist/chunk-FCERKIIF.mjs.map +0 -1
- package/dist/chunk-HGIBJYOU.mjs +0 -231
- package/dist/chunk-HGIBJYOU.mjs.map +0 -1
- package/dist/chunk-JKSOJRQA.mjs +0 -280
- package/dist/chunk-JKSOJRQA.mjs.map +0 -1
- package/dist/chunk-OTKKFP3Y.js +0 -1581
- package/dist/chunk-OTKKFP3Y.js.map +0 -1
- package/dist/chunk-PTFDTOJU.mjs +0 -19
- package/dist/chunk-PTFDTOJU.mjs.map +0 -1
- package/dist/chunk-UZ2IY5VE.js +0 -231
- package/dist/chunk-UZ2IY5VE.js.map +0 -1
- package/dist/database/drivers/knex/base-model.d.mts +0 -16
- package/dist/database/drivers/knex/base-model.d.ts +0 -16
- package/dist/database/drivers/knex/base-model.js +0 -55
- package/dist/database/drivers/knex/base-model.js.map +0 -1
- package/dist/database/drivers/knex/base-model.mjs +0 -56
- package/dist/database/drivers/knex/base-model.mjs.map +0 -1
- package/dist/database/drivers/kysely/base-model.d.mts +0 -22
- package/dist/database/drivers/kysely/base-model.d.ts +0 -22
- package/dist/database/drivers/kysely/base-model.js +0 -64
- package/dist/database/drivers/kysely/base-model.js.map +0 -1
- package/dist/database/drivers/kysely/base-model.mjs +0 -65
- package/dist/database/drivers/kysely/base-model.mjs.map +0 -1
- package/dist/database/types.d.ts +0 -39
- package/dist/database/types.d.ts.map +0 -1
- package/dist/database/types.js +0 -2
- package/dist/database/types.js.map +0 -1
- package/dist/index.d.mts +0 -813
- package/dist/index.mjs +0 -435
- package/dist/index.mjs.map +0 -1
- package/dist/model-aFgomcdc.d.mts +0 -1112
- package/dist/model-aFgomcdc.d.ts +0 -1112
- package/dist/smd/smd-manager.d.ts +0 -28
- package/dist/smd/smd-manager.d.ts.map +0 -1
- package/dist/smd/smd-manager.js +0 -2
- package/dist/smd/smd-manager.js.map +0 -1
- package/dist/smd/smd.d.ts +0 -40
- package/dist/smd/smd.d.ts.map +0 -1
- package/dist/smd/smd.js +0 -2
- package/dist/smd/smd.js.map +0 -1
package/src/api/sonamu.ts
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
import { readFile } from "fs/promises";
|
|
3
|
-
import { exists } from "../utils/fs-utils";
|
|
4
1
|
import { AsyncLocalStorage } from "async_hooks";
|
|
5
2
|
import chalk from "chalk";
|
|
6
3
|
import fastify from "fastify";
|
|
4
|
+
import { readFile } from "fs/promises";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import { exists } from "../utils/fs-utils";
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
8
|
+
import type { FSWatcher } from "chokidar";
|
|
9
|
+
import { formatInTimeZone } from "date-fns-tz";
|
|
10
|
+
import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
|
|
11
|
+
import type { IncomingMessage, Server, ServerResponse } from "http";
|
|
12
|
+
import { ZodError, ZodObject } from "zod";
|
|
13
|
+
import { DB, SonamuDBConfig } from "../database/db";
|
|
14
|
+
import { attachOnDuplicateUpdate } from "../database/knex-plugins/knex-on-duplicate-update";
|
|
10
15
|
import {
|
|
11
16
|
BadRequestException,
|
|
12
17
|
NotFoundException,
|
|
13
18
|
} from "../exceptions/so-exceptions";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
19
|
+
import type { Driver } from "../file-storage/driver";
|
|
20
|
+
import { createSSEFactory } from "../stream/sse";
|
|
21
|
+
import type { Syncer } from "../syncer/syncer";
|
|
16
22
|
import {
|
|
17
23
|
ApiParamType,
|
|
18
24
|
SonamuFastifyConfig,
|
|
@@ -20,16 +26,11 @@ import {
|
|
|
20
26
|
} from "../types/types";
|
|
21
27
|
import { isLocal, isTest } from "../utils/controller";
|
|
22
28
|
import { findApiRootPath } from "../utils/utils";
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import
|
|
26
|
-
import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
|
|
27
|
-
import type { IncomingMessage, Server, ServerResponse } from "http";
|
|
29
|
+
import { humanizeZodError } from "../utils/zod-error";
|
|
30
|
+
import { fastifyCaster } from "./caster";
|
|
31
|
+
import { getZodObjectFromApi } from "./code-converters";
|
|
28
32
|
import type { Context, UploadContext } from "./context";
|
|
29
|
-
import type {
|
|
30
|
-
import type { FSWatcher } from "chokidar";
|
|
31
|
-
import { formatInTimeZone } from "date-fns-tz";
|
|
32
|
-
import type { Driver } from "../file-storage/driver";
|
|
33
|
+
import type { ExtendedApi } from "./decorators";
|
|
33
34
|
|
|
34
35
|
export type SonamuConfig = {
|
|
35
36
|
projectName?: string;
|
|
@@ -389,16 +390,31 @@ class SonamuClass {
|
|
|
389
390
|
return cachedData;
|
|
390
391
|
}
|
|
391
392
|
|
|
392
|
-
//
|
|
393
|
-
const
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
393
|
+
// createSSEFactory 함수에 미리 request의 socket과 reply를 바인딩.
|
|
394
|
+
const createSSE = (<T extends ZodObject>(
|
|
395
|
+
_request: FastifyRequest,
|
|
396
|
+
_reply: FastifyReply,
|
|
397
|
+
_events: T
|
|
398
|
+
) => createSSEFactory(_request.socket, _reply, _events)).bind(
|
|
399
|
+
null,
|
|
399
400
|
request,
|
|
400
401
|
reply
|
|
401
402
|
);
|
|
403
|
+
|
|
404
|
+
// 결과 (AsyncLocalStorage 적용)
|
|
405
|
+
const context: Context = {
|
|
406
|
+
...config.contextProvider(
|
|
407
|
+
{
|
|
408
|
+
request,
|
|
409
|
+
reply,
|
|
410
|
+
headers: request.headers,
|
|
411
|
+
createSSE,
|
|
412
|
+
},
|
|
413
|
+
request,
|
|
414
|
+
reply
|
|
415
|
+
),
|
|
416
|
+
};
|
|
417
|
+
|
|
402
418
|
const model = this.syncer.models[api.modelName];
|
|
403
419
|
return this.asyncLocalStorage.run({ context }, async () => {
|
|
404
420
|
const result = await (model as any)[api.methodName].apply(
|
|
@@ -440,10 +456,26 @@ class SonamuClass {
|
|
|
440
456
|
return;
|
|
441
457
|
}
|
|
442
458
|
|
|
443
|
-
|
|
459
|
+
try {
|
|
460
|
+
await this.handleFileChange(event, filePath);
|
|
461
|
+
} catch (e) {
|
|
462
|
+
console.error(e);
|
|
463
|
+
}
|
|
444
464
|
});
|
|
445
465
|
}
|
|
446
466
|
|
|
467
|
+
/*
|
|
468
|
+
A function that automatically handles init and destroy when using Sonamu via scripts.
|
|
469
|
+
*/
|
|
470
|
+
async runScript(fn: () => Promise<void>) {
|
|
471
|
+
await this.init(true, false, undefined, false);
|
|
472
|
+
try {
|
|
473
|
+
await fn();
|
|
474
|
+
} finally {
|
|
475
|
+
await this.destroy();
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
447
479
|
private registerPlugins(
|
|
448
480
|
server: FastifyInstance,
|
|
449
481
|
plugins: SonamuServerOptions["plugins"]
|
|
@@ -453,10 +485,11 @@ class SonamuClass {
|
|
|
453
485
|
}
|
|
454
486
|
|
|
455
487
|
const pluginsModules = {
|
|
456
|
-
formbody: "@fastify/formbody",
|
|
457
|
-
qs: "fastify-qs",
|
|
458
488
|
cors: "@fastify/cors",
|
|
489
|
+
formbody: "@fastify/formbody",
|
|
459
490
|
multipart: "@fastify/multipart",
|
|
491
|
+
qs: "fastify-qs",
|
|
492
|
+
sse: "fastify-sse-v2",
|
|
460
493
|
} as const;
|
|
461
494
|
|
|
462
495
|
const registerPlugin = <K extends keyof NonNullable<typeof plugins>>(
|
|
@@ -561,6 +594,7 @@ class SonamuClass {
|
|
|
561
594
|
const { BaseModel } = require("../database/base-model");
|
|
562
595
|
await BaseModel.destroy();
|
|
563
596
|
await this.watcher?.close();
|
|
597
|
+
this.storage?.destroy();
|
|
564
598
|
}
|
|
565
599
|
}
|
|
566
600
|
export const Sonamu = new SonamuClass();
|
package/src/bin/build-config.ts
CHANGED
|
@@ -3,3 +3,8 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const SWC_BUILD_COMMAND =
|
|
5
5
|
"swc src -d dist --strip-leading-paths --source-maps -C module.type=commonjs -C jsc.parser.syntax=typescript -C jsc.parser.decorators=true -C jsc.target=es5";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* TSC 타입 체크 명령어
|
|
9
|
+
*/
|
|
10
|
+
export const TSC_TYPE_CHECK_COMMAND = "tsc --noEmit";
|
package/src/bin/cli-wrapper.ts
CHANGED
|
@@ -4,24 +4,34 @@ import { spawnSync, execSync } from "child_process";
|
|
|
4
4
|
import { resolve } from "path";
|
|
5
5
|
import { existsSync } from "fs";
|
|
6
6
|
import chalk from "chalk";
|
|
7
|
-
import { SWC_BUILD_COMMAND } from "./build-config";
|
|
7
|
+
import { SWC_BUILD_COMMAND, TSC_TYPE_CHECK_COMMAND } from "./build-config";
|
|
8
8
|
|
|
9
9
|
const scriptPath = resolve(__dirname, "cli.js");
|
|
10
10
|
const args = process.argv.slice(2);
|
|
11
11
|
|
|
12
12
|
// build 명령어는 dist 없이도 실행 가능하도록 cli.ts 외부에서 처리(Sonamu.init에서 dist 필요)
|
|
13
|
-
function build() {
|
|
13
|
+
function build(checkTypes: boolean = false) {
|
|
14
14
|
try {
|
|
15
15
|
execSync(SWC_BUILD_COMMAND, { cwd: process.cwd(), stdio: "inherit" });
|
|
16
16
|
} catch (error) {
|
|
17
17
|
console.error(chalk.red("Build failed."), error);
|
|
18
18
|
process.exit(1);
|
|
19
19
|
}
|
|
20
|
+
|
|
21
|
+
if (checkTypes) {
|
|
22
|
+
try {
|
|
23
|
+
console.log(chalk.blue("Checking types with tsc..."));
|
|
24
|
+
execSync(TSC_TYPE_CHECK_COMMAND, { cwd: process.cwd(), stdio: "inherit" });
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.error(chalk.red("Type check failed."), error);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
20
30
|
}
|
|
21
31
|
|
|
22
32
|
if (args[0] === "build") {
|
|
23
33
|
console.log(chalk.blue("Building the project..."));
|
|
24
|
-
build();
|
|
34
|
+
build(true);
|
|
25
35
|
console.log(chalk.green("Build completed successfully."));
|
|
26
36
|
process.exit(0);
|
|
27
37
|
}
|
|
@@ -35,8 +45,12 @@ if (!existsSync(scriptPath)) {
|
|
|
35
45
|
process.exit(1);
|
|
36
46
|
}
|
|
37
47
|
|
|
38
|
-
const result = spawnSync(
|
|
39
|
-
|
|
40
|
-
|
|
48
|
+
const result = spawnSync(
|
|
49
|
+
process.execPath,
|
|
50
|
+
["--no-warnings", scriptPath, ...args],
|
|
51
|
+
{
|
|
52
|
+
stdio: "inherit",
|
|
53
|
+
}
|
|
54
|
+
);
|
|
41
55
|
|
|
42
56
|
process.exit(result.status ?? 1);
|
package/src/bin/cli.ts
CHANGED
|
@@ -1,27 +1,29 @@
|
|
|
1
|
-
/* Global Begin */
|
|
2
1
|
import chalk from "chalk";
|
|
3
|
-
console.log(chalk.bgBlue(`BEGIN ${new Date()}`));
|
|
4
|
-
|
|
5
2
|
import dotenv from "dotenv";
|
|
6
3
|
dotenv.config();
|
|
7
4
|
|
|
8
5
|
import path from "path";
|
|
9
6
|
import { tsicli } from "tsicli";
|
|
10
7
|
import { execSync } from "child_process";
|
|
11
|
-
import { mkdir,
|
|
8
|
+
import { mkdir, readdir, readFile, writeFile } from "fs/promises";
|
|
12
9
|
import { exists } from "../utils/fs-utils";
|
|
13
10
|
import process from "process";
|
|
14
11
|
import { Sonamu } from "../api";
|
|
15
12
|
import knex, { Knex } from "knex";
|
|
13
|
+
import { findApiRootPath } from "../utils/utils";
|
|
16
14
|
import { EntityManager } from "../entity/entity-manager";
|
|
17
15
|
import { Migrator } from "../migration/migrator";
|
|
18
16
|
import { FixtureManager } from "../testing/fixture-manager";
|
|
19
|
-
import { SWC_BUILD_COMMAND } from "./build-config";
|
|
17
|
+
// import { SWC_BUILD_COMMAND } from "./build-config";
|
|
18
|
+
import { NodemonSettings } from "nodemon";
|
|
20
19
|
|
|
21
20
|
let migrator: Migrator;
|
|
22
21
|
|
|
23
22
|
async function bootstrap() {
|
|
24
|
-
|
|
23
|
+
// dev:serve 명령어가 아닌 경우에만 Sonamu 초기화
|
|
24
|
+
if (process.argv[2] !== "dev:serve") {
|
|
25
|
+
await Sonamu.init(false, false);
|
|
26
|
+
}
|
|
25
27
|
|
|
26
28
|
await tsicli(process.argv, {
|
|
27
29
|
types: {
|
|
@@ -84,16 +86,13 @@ bootstrap().finally(async () => {
|
|
|
84
86
|
await migrator.destroy();
|
|
85
87
|
}
|
|
86
88
|
await FixtureManager.destroy();
|
|
87
|
-
|
|
88
|
-
/* Global End */
|
|
89
|
-
console.log(chalk.bgBlue(`END ${new Date()}\n`));
|
|
90
89
|
});
|
|
91
90
|
|
|
92
91
|
async function dev_serve() {
|
|
93
92
|
const nodemon = await import("nodemon");
|
|
94
93
|
|
|
95
94
|
const nodemonConfig = await (async () => {
|
|
96
|
-
const projectNodemonPath = path.join(
|
|
95
|
+
const projectNodemonPath = path.join(findApiRootPath(), "nodemon.json");
|
|
97
96
|
const hasProjectNodemon = await exists(projectNodemonPath);
|
|
98
97
|
|
|
99
98
|
if (hasProjectNodemon) {
|
|
@@ -104,12 +103,11 @@ async function dev_serve() {
|
|
|
104
103
|
watch: ["src/index.ts"],
|
|
105
104
|
ignore: ["dist/**", "**/*.js", "**/*.d.ts"],
|
|
106
105
|
exec: [
|
|
107
|
-
SWC_BUILD_COMMAND,
|
|
108
|
-
"node -r source-map-support/register -r dotenv/config dist/index.js",
|
|
106
|
+
// SWC_BUILD_COMMAND,
|
|
107
|
+
"node --no-warnings -r source-map-support/register -r dotenv/config dist/index.js",
|
|
109
108
|
].join(" && "),
|
|
110
|
-
};
|
|
109
|
+
} as NodemonSettings;
|
|
111
110
|
})();
|
|
112
|
-
|
|
113
111
|
nodemon.default(nodemonConfig);
|
|
114
112
|
|
|
115
113
|
// 프로세스 종료 처리
|
|
@@ -334,25 +332,22 @@ async function stub_practice(name: string) {
|
|
|
334
332
|
const fileName = `p${currentSeqNo}-${name}.ts`;
|
|
335
333
|
const dstPath = path.join(practiceDir, fileName);
|
|
336
334
|
|
|
337
|
-
// FIXME
|
|
338
335
|
const code = [
|
|
339
|
-
`import {
|
|
336
|
+
`import { Sonamu } from "sonamu";`,
|
|
340
337
|
"",
|
|
341
338
|
`console.clear();`,
|
|
342
339
|
`console.log("${fileName}");`,
|
|
343
340
|
"",
|
|
344
|
-
`async
|
|
341
|
+
`Sonamu.runScript(async () => {`,
|
|
345
342
|
` // TODO`,
|
|
346
|
-
`}`,
|
|
347
|
-
`bootstrap().finally(async () => {`,
|
|
348
|
-
`await BaseModel.destroy();`,
|
|
349
343
|
`});`,
|
|
344
|
+
"",
|
|
350
345
|
].join("\n");
|
|
351
346
|
await writeFile(dstPath, code);
|
|
352
347
|
|
|
353
348
|
execSync(`code ${dstPath}`);
|
|
354
349
|
|
|
355
|
-
const runCode = `yarn node -r source-map-support/register dist/practices/${fileName.replace(
|
|
350
|
+
const runCode = `yarn node -r dotenv/config -r source-map-support/register dist/practices/${fileName.replace(
|
|
356
351
|
".ts",
|
|
357
352
|
".js"
|
|
358
353
|
)}`;
|
|
@@ -218,6 +218,7 @@ export class BaseModelClass {
|
|
|
218
218
|
subset,
|
|
219
219
|
subsetQuery,
|
|
220
220
|
build,
|
|
221
|
+
afterBuild,
|
|
221
222
|
debug,
|
|
222
223
|
db: _db,
|
|
223
224
|
optimizeCountQuery,
|
|
@@ -232,6 +233,13 @@ export class BaseModelClass {
|
|
|
232
233
|
joins: SubsetQuery["joins"];
|
|
233
234
|
virtual: string[];
|
|
234
235
|
}) => Knex.QueryBuilder;
|
|
236
|
+
afterBuild?: (buildParams: {
|
|
237
|
+
qb: Knex.QueryBuilder;
|
|
238
|
+
db: Knex;
|
|
239
|
+
select: (string | Knex.Raw)[];
|
|
240
|
+
joins: SubsetQuery["joins"];
|
|
241
|
+
virtual: string[];
|
|
242
|
+
}) => Knex.QueryBuilder;
|
|
235
243
|
baseTable?: string;
|
|
236
244
|
debug?: boolean | "list" | "count";
|
|
237
245
|
db?: Knex;
|
|
@@ -303,7 +311,16 @@ export class BaseModelClass {
|
|
|
303
311
|
applyJoinClause(clonedQb, joins);
|
|
304
312
|
}
|
|
305
313
|
|
|
306
|
-
const
|
|
314
|
+
const processedQb =
|
|
315
|
+
afterBuild?.({
|
|
316
|
+
qb: clonedQb,
|
|
317
|
+
db,
|
|
318
|
+
select,
|
|
319
|
+
joins,
|
|
320
|
+
virtual,
|
|
321
|
+
}) ?? clonedQb;
|
|
322
|
+
|
|
323
|
+
const parsedQuery = parser.astify(processedQb.toQuery());
|
|
307
324
|
const q = Array.isArray(parsedQuery) ? parsedQuery[0] : parsedQuery;
|
|
308
325
|
if (q.type !== "select") {
|
|
309
326
|
throw new Error("Invalid query");
|
|
@@ -346,10 +363,19 @@ export class BaseModelClass {
|
|
|
346
363
|
}
|
|
347
364
|
|
|
348
365
|
// select, rows
|
|
349
|
-
const
|
|
366
|
+
const clonedQb = qb.clone().select(select);
|
|
350
367
|
|
|
351
368
|
// join
|
|
352
|
-
applyJoinClause(
|
|
369
|
+
applyJoinClause(clonedQb, joins);
|
|
370
|
+
|
|
371
|
+
const listQuery =
|
|
372
|
+
afterBuild?.({
|
|
373
|
+
qb: clonedQb,
|
|
374
|
+
db,
|
|
375
|
+
select,
|
|
376
|
+
joins,
|
|
377
|
+
virtual,
|
|
378
|
+
}) ?? clonedQb;
|
|
353
379
|
|
|
354
380
|
let rows = await listQuery;
|
|
355
381
|
// debug: listQuery
|
package/src/database/db.ts
CHANGED
|
@@ -163,8 +163,8 @@ class DBClass {
|
|
|
163
163
|
);
|
|
164
164
|
const fixture_remote = _.merge({}, defaultKnexConfig, devMasterOptions, {
|
|
165
165
|
connection: {
|
|
166
|
+
// NOTE: fixture remote는 default connection의 DB를 override해선 안됨.
|
|
166
167
|
database: `${config.database}_fixture_remote`,
|
|
167
|
-
...config.defaultOptions?.connection,
|
|
168
168
|
},
|
|
169
169
|
});
|
|
170
170
|
|
|
@@ -24,6 +24,8 @@ export interface Driver {
|
|
|
24
24
|
getUrl(key: string): string;
|
|
25
25
|
|
|
26
26
|
getSignedUrl(key: string, expiresIn?: number): Promise<string>;
|
|
27
|
+
|
|
28
|
+
destroy(): void;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
export type FSDriverConfig = {
|
|
@@ -58,6 +60,10 @@ export class FSDriver implements Driver {
|
|
|
58
60
|
async getSignedUrl(key: string, _expiresIn?: number): Promise<string> {
|
|
59
61
|
return this.getUrl(key);
|
|
60
62
|
}
|
|
63
|
+
|
|
64
|
+
destroy() {
|
|
65
|
+
// 아무것도 하지 않음
|
|
66
|
+
}
|
|
61
67
|
}
|
|
62
68
|
|
|
63
69
|
export type S3DriverConfig = S3ClientConfig & {
|
|
@@ -118,4 +124,8 @@ export class S3Driver implements Driver {
|
|
|
118
124
|
|
|
119
125
|
return visibility;
|
|
120
126
|
}
|
|
127
|
+
|
|
128
|
+
destroy() {
|
|
129
|
+
this.s3.destroy();
|
|
130
|
+
}
|
|
121
131
|
}
|
package/src/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ export * from "./database/puri-wrapper";
|
|
|
10
10
|
export * from "./database/puri.types";
|
|
11
11
|
export * from "./exceptions/error-handler";
|
|
12
12
|
export * from "./exceptions/so-exceptions";
|
|
13
|
+
export * from "./stream/sse";
|
|
13
14
|
export * from "./types/types";
|
|
14
15
|
export * from "./utils/controller";
|
|
15
16
|
export * from "./utils/model";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./sse";
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { FastifyRequest, FastifyReply } from "fastify";
|
|
3
|
+
|
|
4
|
+
// NOTE(Haze, 251106): context provider에서 인자를 채워주면 createSSE(events)만으로 사용 가능
|
|
5
|
+
export function createSSEFactory<T extends z.ZodObject>(
|
|
6
|
+
socket: FastifyRequest["socket"],
|
|
7
|
+
reply: FastifyReply,
|
|
8
|
+
_events: T
|
|
9
|
+
) {
|
|
10
|
+
return new SSEConnection<T>(socket, reply);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
class SSEConnection<T extends z.ZodObject> {
|
|
14
|
+
private _closed = false;
|
|
15
|
+
|
|
16
|
+
constructor(
|
|
17
|
+
private readonly socket: FastifyRequest["socket"],
|
|
18
|
+
private readonly reply: FastifyReply
|
|
19
|
+
) {
|
|
20
|
+
this.socket.on("close", () => {
|
|
21
|
+
this._closed = true;
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
publish<K extends keyof z.infer<T>>(event: K, data: z.infer<T>[K]): void {
|
|
26
|
+
if (this._closed) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
this.reply.sse({
|
|
31
|
+
event: event as string,
|
|
32
|
+
data: JSON.stringify(data),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async end(): Promise<void> {
|
|
37
|
+
if (this._closed) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
this.reply.sse({
|
|
42
|
+
event: "end",
|
|
43
|
+
data: "END",
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
47
|
+
this.reply.raw.end();
|
|
48
|
+
}
|
|
49
|
+
}
|
package/src/types/types.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { z } from "zod";
|
|
|
12
12
|
import type { Context, ApiDecoratorOptions } from "../api";
|
|
13
13
|
import type { FastifyMultipartOptions } from "@fastify/multipart";
|
|
14
14
|
import type { Driver } from "../file-storage/driver";
|
|
15
|
+
import type { SsePluginOptions } from "fastify-sse-v2/lib/types";
|
|
15
16
|
|
|
16
17
|
/*
|
|
17
18
|
Enums
|
|
@@ -822,7 +823,7 @@ export type RelationNode = {
|
|
|
822
823
|
export interface DatabaseSchemaExtend {}
|
|
823
824
|
export type ManyToManyBaseSchema<
|
|
824
825
|
FromIdKey extends string,
|
|
825
|
-
ToIdKey extends string
|
|
826
|
+
ToIdKey extends string,
|
|
826
827
|
> = {
|
|
827
828
|
id: number;
|
|
828
829
|
} & {
|
|
@@ -833,7 +834,10 @@ export type ManyToManyBaseSchema<
|
|
|
833
834
|
|
|
834
835
|
export type SonamuFastifyConfig = {
|
|
835
836
|
contextProvider: (
|
|
836
|
-
defaultContext: Pick<
|
|
837
|
+
defaultContext: Pick<
|
|
838
|
+
Context,
|
|
839
|
+
"request" | "reply" | "headers" | "createSSE"
|
|
840
|
+
>,
|
|
837
841
|
request: FastifyRequest,
|
|
838
842
|
reply: FastifyReply
|
|
839
843
|
) => Context;
|
|
@@ -879,10 +883,12 @@ export type SonamuServerOptions = {
|
|
|
879
883
|
};
|
|
880
884
|
|
|
881
885
|
plugins?: {
|
|
882
|
-
formbody?: boolean | FastifyFormbodyOptions;
|
|
883
|
-
qs?: boolean | QsPluginOptions;
|
|
884
886
|
cors?: boolean | FastifyCorsOptions;
|
|
887
|
+
formbody?: boolean | FastifyFormbodyOptions;
|
|
885
888
|
multipart?: boolean | FastifyMultipartOptions;
|
|
889
|
+
qs?: boolean | QsPluginOptions;
|
|
890
|
+
sse?: boolean | SsePluginOptions;
|
|
891
|
+
|
|
886
892
|
custom?: (server: FastifyInstance) => void;
|
|
887
893
|
};
|
|
888
894
|
|
package/tsconfig.json
CHANGED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import type { FastifyReply, FastifyRequest } from "fastify";
|
|
2
|
-
import type { Context } from "./context";
|
|
3
|
-
import type { ApiParamType, ApiParam } from "../types/types";
|
|
4
|
-
import type { ApiDecoratorOptions } from "./decorators";
|
|
5
|
-
export type SonamuFastifyConfig = {
|
|
6
|
-
contextProvider: (defaultContext: Pick<Context, "headers" | "reply">, request: FastifyRequest, reply: FastifyReply) => Context;
|
|
7
|
-
guardHandler: (guard: string, request: FastifyRequest, api: {
|
|
8
|
-
typeParameters: ApiParamType.TypeParam[];
|
|
9
|
-
parameters: ApiParam[];
|
|
10
|
-
returnType: ApiParamType;
|
|
11
|
-
modelName: string;
|
|
12
|
-
methodName: string;
|
|
13
|
-
path: string;
|
|
14
|
-
options: ApiDecoratorOptions;
|
|
15
|
-
}) => void;
|
|
16
|
-
cache?: {
|
|
17
|
-
get: (key: string) => Promise<unknown | null>;
|
|
18
|
-
put: (key: string, value: unknown, ttl?: number) => Promise<void>;
|
|
19
|
-
resolveKey: (path: string, reqBody: {
|
|
20
|
-
[key: string]: unknown;
|
|
21
|
-
}) => {
|
|
22
|
-
cache: false;
|
|
23
|
-
} | {
|
|
24
|
-
cache: true;
|
|
25
|
-
key: string;
|
|
26
|
-
ttl?: number;
|
|
27
|
-
};
|
|
28
|
-
};
|
|
29
|
-
};
|
|
30
|
-
//# sourceMappingURL=sonamu.types.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sonamu.types.d.ts","sourceRoot":"","sources":["../../src/api/sonamu.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC7D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAExD,MAAM,MAAM,mBAAmB,GAAG;IAChC,eAAe,EAAE,CACf,cAAc,EAAE,IAAI,CAAC,OAAO,EAAE,SAAS,GAAG,OAAO,CAAC,EAClD,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,YAAY,KAChB,OAAO,CAAC;IACb,YAAY,EAAE,CACZ,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,cAAc,EACvB,GAAG,EAAE;QACH,cAAc,EAAE,YAAY,CAAC,SAAS,EAAE,CAAC;QACzC,UAAU,EAAE,QAAQ,EAAE,CAAC;QACvB,UAAU,EAAE,YAAY,CAAC;QACzB,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,mBAAmB,CAAC;KAC9B,KACE,IAAI,CAAC;IACV,KAAK,CAAC,EAAE;QACN,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;QAC9C,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;QAClE,UAAU,EAAE,CACV,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;YACP,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;SACxB,KAEC;YACE,KAAK,EAAE,KAAK,CAAC;SACd,GACD;YACE,KAAK,EAAE,IAAI,CAAC;YACZ,GAAG,EAAE,MAAM,CAAC;YACZ,GAAG,CAAC,EAAE,MAAM,CAAC;SACd,CAAC;KACP,CAAC;CACH,CAAC"}
|
package/dist/api/sonamu.types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/api/sonamu.types.ts"],"names":[],"mappings":""}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { c as DatabaseDriver, d as DriverSpec, S as SubsetQuery, D as DBPreset, B as BaseListParams, K as KnexClient, b as KyselyClient } from './model-aFgomcdc.mjs';
|
|
2
|
-
|
|
3
|
-
declare abstract class BaseModelClassAbstract<D extends DatabaseDriver> {
|
|
4
|
-
modelName: string;
|
|
5
|
-
protected abstract applyJoins(qb: DriverSpec[D]["adapter"], joins: SubsetQuery["joins"]): DriverSpec[D]["adapter"];
|
|
6
|
-
protected abstract executeCountQuery(qb: DriverSpec[D]["queryBuilder"]): Promise<number>;
|
|
7
|
-
abstract getDB(which: DBPreset): DriverSpec[D]["core"];
|
|
8
|
-
abstract destroy(): Promise<void>;
|
|
9
|
-
runSubsetQuery<T extends BaseListParams, U extends string>({ params, baseTable, subset, subsetQuery, build, debug, db: _db, optimizeCountQuery, }: {
|
|
10
|
-
subset: U;
|
|
11
|
-
params: T;
|
|
12
|
-
subsetQuery: SubsetQuery;
|
|
13
|
-
build: (buildParams: {
|
|
14
|
-
qb: DriverSpec[D]["queryBuilder"];
|
|
15
|
-
db: DriverSpec[D]["core"];
|
|
16
|
-
select: SubsetQuery["select"];
|
|
17
|
-
joins: SubsetQuery["joins"];
|
|
18
|
-
virtual: string[];
|
|
19
|
-
}) => DriverSpec[D]["queryBuilder"];
|
|
20
|
-
baseTable?: DriverSpec[D]["table"];
|
|
21
|
-
debug?: boolean | "list" | "count";
|
|
22
|
-
db?: DriverSpec[D]["core"];
|
|
23
|
-
optimizeCountQuery?: boolean;
|
|
24
|
-
}): Promise<{
|
|
25
|
-
rows: any[];
|
|
26
|
-
total?: number;
|
|
27
|
-
subsetQuery: SubsetQuery;
|
|
28
|
-
qb: DriverSpec[D]["queryBuilder"];
|
|
29
|
-
}>;
|
|
30
|
-
useLoaders(db: DriverSpec[D]["adapter"], rows: any[], loaders: SubsetQuery["loaders"]): Promise<any[]>;
|
|
31
|
-
protected buildHasManyQuery(db: DriverSpec[D]["adapter"], loader: SubsetQuery["loaders"][number], fromIds: any[]): Promise<{
|
|
32
|
-
subQ: KnexClient | KyselyClient;
|
|
33
|
-
col: string;
|
|
34
|
-
}>;
|
|
35
|
-
protected buildManyToManyQuery(db: DriverSpec[D]["adapter"], loader: SubsetQuery["loaders"][number], fromIds: any[]): Promise<{
|
|
36
|
-
subQ: KnexClient | KyselyClient;
|
|
37
|
-
col: string;
|
|
38
|
-
}>;
|
|
39
|
-
myNow(timestamp?: number): string;
|
|
40
|
-
hydrate<T>(rows: T[]): T[];
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export { BaseModelClassAbstract as B };
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { c as DatabaseDriver, d as DriverSpec, S as SubsetQuery, D as DBPreset, B as BaseListParams, K as KnexClient, b as KyselyClient } from './model-aFgomcdc.js';
|
|
2
|
-
|
|
3
|
-
declare abstract class BaseModelClassAbstract<D extends DatabaseDriver> {
|
|
4
|
-
modelName: string;
|
|
5
|
-
protected abstract applyJoins(qb: DriverSpec[D]["adapter"], joins: SubsetQuery["joins"]): DriverSpec[D]["adapter"];
|
|
6
|
-
protected abstract executeCountQuery(qb: DriverSpec[D]["queryBuilder"]): Promise<number>;
|
|
7
|
-
abstract getDB(which: DBPreset): DriverSpec[D]["core"];
|
|
8
|
-
abstract destroy(): Promise<void>;
|
|
9
|
-
runSubsetQuery<T extends BaseListParams, U extends string>({ params, baseTable, subset, subsetQuery, build, debug, db: _db, optimizeCountQuery, }: {
|
|
10
|
-
subset: U;
|
|
11
|
-
params: T;
|
|
12
|
-
subsetQuery: SubsetQuery;
|
|
13
|
-
build: (buildParams: {
|
|
14
|
-
qb: DriverSpec[D]["queryBuilder"];
|
|
15
|
-
db: DriverSpec[D]["core"];
|
|
16
|
-
select: SubsetQuery["select"];
|
|
17
|
-
joins: SubsetQuery["joins"];
|
|
18
|
-
virtual: string[];
|
|
19
|
-
}) => DriverSpec[D]["queryBuilder"];
|
|
20
|
-
baseTable?: DriverSpec[D]["table"];
|
|
21
|
-
debug?: boolean | "list" | "count";
|
|
22
|
-
db?: DriverSpec[D]["core"];
|
|
23
|
-
optimizeCountQuery?: boolean;
|
|
24
|
-
}): Promise<{
|
|
25
|
-
rows: any[];
|
|
26
|
-
total?: number;
|
|
27
|
-
subsetQuery: SubsetQuery;
|
|
28
|
-
qb: DriverSpec[D]["queryBuilder"];
|
|
29
|
-
}>;
|
|
30
|
-
useLoaders(db: DriverSpec[D]["adapter"], rows: any[], loaders: SubsetQuery["loaders"]): Promise<any[]>;
|
|
31
|
-
protected buildHasManyQuery(db: DriverSpec[D]["adapter"], loader: SubsetQuery["loaders"][number], fromIds: any[]): Promise<{
|
|
32
|
-
subQ: KnexClient | KyselyClient;
|
|
33
|
-
col: string;
|
|
34
|
-
}>;
|
|
35
|
-
protected buildManyToManyQuery(db: DriverSpec[D]["adapter"], loader: SubsetQuery["loaders"][number], fromIds: any[]): Promise<{
|
|
36
|
-
subQ: KnexClient | KyselyClient;
|
|
37
|
-
col: string;
|
|
38
|
-
}>;
|
|
39
|
-
myNow(timestamp?: number): string;
|
|
40
|
-
hydrate<T>(rows: T[]): T[];
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export { BaseModelClassAbstract as B };
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ts-node
|