sonamu 0.9.4 → 0.9.6
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/ai/providers/rtzr/utils.js +2 -2
- package/dist/api/config.d.ts +13 -2
- package/dist/api/config.d.ts.map +1 -1
- package/dist/api/config.js +1 -1
- package/dist/api/context.d.ts +17 -7
- package/dist/api/context.d.ts.map +1 -1
- package/dist/api/context.js +1 -1
- package/dist/api/decorators.d.ts +18 -0
- package/dist/api/decorators.d.ts.map +1 -1
- package/dist/api/decorators.js +54 -3
- package/dist/api/index.js +8 -3
- package/dist/api/sonamu.d.ts +24 -9
- package/dist/api/sonamu.d.ts.map +1 -1
- package/dist/api/sonamu.js +365 -79
- package/dist/api/websocket-helpers.d.ts +24 -0
- package/dist/api/websocket-helpers.d.ts.map +1 -0
- package/dist/api/websocket-helpers.js +77 -0
- package/dist/bin/cli.js +12 -4
- package/dist/database/upsert-builder.js +4 -4
- package/dist/dict/sonamu-dictionary.js +6 -6
- package/dist/entity/entity-manager.js +1 -1
- package/dist/entity/entity.js +3 -3
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -4
- package/dist/migration/code-generation.d.ts.map +1 -1
- package/dist/migration/code-generation.js +8 -9
- package/dist/stream/index.d.ts +6 -0
- package/dist/stream/index.d.ts.map +1 -1
- package/dist/stream/index.js +13 -2
- package/dist/stream/ws-audience-resolver.d.ts +15 -0
- package/dist/stream/ws-audience-resolver.d.ts.map +1 -0
- package/dist/stream/ws-audience-resolver.js +31 -0
- package/dist/stream/ws-audience.d.ts +28 -0
- package/dist/stream/ws-audience.d.ts.map +1 -0
- package/dist/stream/ws-audience.js +46 -0
- package/dist/stream/ws-cluster-bus.d.ts +23 -0
- package/dist/stream/ws-cluster-bus.d.ts.map +1 -0
- package/dist/stream/ws-cluster-bus.js +18 -0
- package/dist/stream/ws-core.d.ts +15 -0
- package/dist/stream/ws-core.d.ts.map +1 -0
- package/dist/stream/ws-core.js +1 -0
- package/dist/stream/ws-delivery.d.ts +24 -0
- package/dist/stream/ws-delivery.d.ts.map +1 -0
- package/dist/stream/ws-delivery.js +103 -0
- package/dist/stream/ws-local-connection-store.d.ts +10 -0
- package/dist/stream/ws-local-connection-store.d.ts.map +1 -0
- package/dist/stream/ws-local-connection-store.js +44 -0
- package/dist/stream/ws-presence-store.d.ts +61 -0
- package/dist/stream/ws-presence-store.d.ts.map +1 -0
- package/dist/stream/ws-presence-store.js +236 -0
- package/dist/stream/ws-registry.d.ts +42 -0
- package/dist/stream/ws-registry.d.ts.map +1 -0
- package/dist/stream/ws-registry.js +108 -0
- package/dist/stream/ws.d.ts +52 -0
- package/dist/stream/ws.d.ts.map +1 -0
- package/dist/stream/ws.js +397 -0
- package/dist/syncer/api-parser.d.ts.map +1 -1
- package/dist/syncer/api-parser.js +72 -2
- package/dist/syncer/checksum.d.ts.map +1 -1
- package/dist/syncer/checksum.js +13 -12
- package/dist/syncer/code-generator.d.ts.map +1 -1
- package/dist/syncer/code-generator.js +7 -4
- package/dist/syncer/event-batcher.d.ts +27 -0
- package/dist/syncer/event-batcher.d.ts.map +1 -0
- package/dist/syncer/event-batcher.js +69 -0
- package/dist/syncer/file-patterns.d.ts +48 -26
- package/dist/syncer/file-patterns.d.ts.map +1 -1
- package/dist/syncer/file-patterns.js +71 -23
- package/dist/syncer/file-tracking.d.ts +13 -0
- package/dist/syncer/file-tracking.d.ts.map +1 -0
- package/dist/syncer/file-tracking.js +33 -0
- package/dist/syncer/index.js +2 -2
- package/dist/syncer/module-loader.d.ts +2 -11
- package/dist/syncer/module-loader.d.ts.map +1 -1
- package/dist/syncer/module-loader.js +3 -3
- package/dist/syncer/syncer-actions.d.ts +39 -6
- package/dist/syncer/syncer-actions.d.ts.map +1 -1
- package/dist/syncer/syncer-actions.js +125 -10
- package/dist/syncer/syncer.d.ts +33 -19
- package/dist/syncer/syncer.d.ts.map +1 -1
- package/dist/syncer/syncer.js +168 -168
- package/dist/syncer/watcher.d.ts +8 -0
- package/dist/syncer/watcher.d.ts.map +1 -0
- package/dist/syncer/watcher.js +105 -0
- package/dist/tasks/workflow-manager.d.ts.map +1 -1
- package/dist/tasks/workflow-manager.js +2 -1
- package/dist/template/implementations/services.template.d.ts.map +1 -1
- package/dist/template/implementations/services.template.js +36 -1
- package/dist/testing/bootstrap.d.ts.map +1 -1
- package/dist/testing/bootstrap.js +8 -1
- package/dist/testing/data-explorer.d.ts.map +1 -1
- package/dist/testing/data-explorer.js +5 -3
- package/dist/testing/fixture-manager.js +1 -1
- package/dist/types/types.d.ts +2 -1
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/types.js +2 -2
- package/dist/ui/api.d.ts.map +1 -1
- package/dist/ui/api.js +4 -3
- package/dist/ui/cdd-service.js +1 -1
- package/dist/ui-web/assets/{index-C5KUjXm0.js → index-BmThfg-s.js} +39 -39
- package/dist/ui-web/assets/index-D4rYm-Xz.css +1 -0
- package/dist/ui-web/index.html +2 -2
- package/dist/utils/async-utils.d.ts +27 -3
- package/dist/utils/async-utils.d.ts.map +1 -1
- package/dist/utils/async-utils.js +56 -6
- package/dist/utils/formatter.d.ts +7 -1
- package/dist/utils/formatter.d.ts.map +1 -1
- package/dist/utils/formatter.js +95 -60
- package/dist/utils/fs-utils.d.ts +2 -0
- package/dist/utils/fs-utils.d.ts.map +1 -1
- package/dist/utils/fs-utils.js +10 -2
- package/dist/utils/process-utils.d.ts +6 -0
- package/dist/utils/process-utils.d.ts.map +1 -1
- package/dist/utils/process-utils.js +16 -3
- package/dist/utils/utils.d.ts +1 -0
- package/dist/utils/utils.d.ts.map +1 -1
- package/dist/utils/utils.js +2 -2
- package/package.json +7 -5
- package/src/ai/providers/rtzr/utils.ts +1 -1
- package/src/api/__tests__/sonamu.websocket.test.ts +64 -0
- package/src/api/__tests__/websocket-context.types.test.ts +58 -0
- package/src/api/config.ts +28 -2
- package/src/api/context.ts +21 -7
- package/src/api/decorators.ts +101 -1
- package/src/api/sonamu.ts +529 -127
- package/src/api/websocket-helpers.ts +122 -0
- package/src/bin/cli.ts +10 -2
- package/src/database/upsert-builder.ts +3 -3
- package/src/dict/sonamu-dictionary.ts +3 -3
- package/src/entity/entity.ts +1 -1
- package/src/index.ts +6 -0
- package/src/migration/code-generation.ts +6 -11
- package/src/shared/app.shared.ts.txt +312 -4
- package/src/shared/web.shared.ts.txt +340 -4
- package/src/stream/__tests__/ws-contracts.test.ts +381 -0
- package/src/stream/__tests__/ws.test.ts +449 -0
- package/src/stream/index.ts +6 -0
- package/src/stream/ws-audience-resolver.ts +35 -0
- package/src/stream/ws-audience.ts +62 -0
- package/src/stream/ws-cluster-bus.ts +32 -0
- package/src/stream/ws-core.ts +16 -0
- package/src/stream/ws-delivery.ts +138 -0
- package/src/stream/ws-local-connection-store.ts +44 -0
- package/src/stream/ws-presence-store.ts +326 -0
- package/src/stream/ws-registry.ts +138 -0
- package/src/stream/ws.ts +591 -0
- package/src/syncer/__tests__/api-parser.websocket-type-ref.test.ts +78 -0
- package/src/syncer/api-parser.ts +112 -1
- package/src/syncer/checksum.ts +23 -29
- package/src/syncer/code-generator.ts +4 -1
- package/src/syncer/event-batcher.ts +72 -0
- package/src/syncer/file-patterns.ts +98 -30
- package/src/syncer/file-tracking.ts +27 -0
- package/src/syncer/module-loader.ts +5 -12
- package/src/syncer/syncer-actions.ts +179 -17
- package/src/syncer/syncer.ts +250 -287
- package/src/syncer/watcher.ts +128 -0
- package/src/tasks/workflow-manager.ts +1 -0
- package/src/template/__tests__/services.template.websocket.test.ts +79 -0
- package/src/template/implementations/services.template.ts +69 -0
- package/src/testing/bootstrap.ts +8 -1
- package/src/testing/data-explorer.ts +3 -2
- package/src/types/types.ts +20 -2
- package/src/ui/api.ts +10 -1
- package/src/utils/async-utils.ts +71 -4
- package/src/utils/formatter.ts +114 -75
- package/src/utils/fs-utils.ts +9 -0
- package/src/utils/process-utils.ts +17 -0
- package/src/utils/utils.ts +1 -1
- package/dist/ui-web/assets/index-Dr8pRJC_.css +0 -1
package/src/api/config.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { type FastifyCorsOptions } from "@fastify/cors";
|
|
|
3
3
|
import { type FastifyFormbodyOptions } from "@fastify/formbody";
|
|
4
4
|
import { type FastifyMultipartOptions } from "@fastify/multipart";
|
|
5
5
|
import { type FastifyStaticOptions } from "@fastify/static";
|
|
6
|
+
import { type WebsocketPluginOptions } from "@fastify/websocket";
|
|
6
7
|
import { type BetterAuthOptions } from "better-auth";
|
|
7
8
|
import {
|
|
8
9
|
type FastifyInstance,
|
|
@@ -18,9 +19,10 @@ import { type CacheConfig } from "../cache/types";
|
|
|
18
19
|
import { type SonamuDBConfig } from "../database/db";
|
|
19
20
|
import { type SonamuLoggingOptions } from "../logger/configure";
|
|
20
21
|
import { type StorageConfig } from "../storage/types";
|
|
22
|
+
import { type WebSocketRuntimeOptions } from "../stream/ws";
|
|
21
23
|
import { type WorkflowOptions } from "../tasks/workflow-manager";
|
|
22
24
|
import { type Executable, type SonamuFastifyConfig } from "../types/types";
|
|
23
|
-
import { type Context } from "./context";
|
|
25
|
+
import { type Context, type WebSocketContext } from "./context";
|
|
24
26
|
|
|
25
27
|
export type DatabaseConfig = Omit<Knex.Config, "connection"> & {
|
|
26
28
|
connection?: Knex.PgConnectionConfig;
|
|
@@ -180,6 +182,7 @@ export type SonamuServerOptions = {
|
|
|
180
182
|
qs?: boolean | QsPluginOptions;
|
|
181
183
|
sse?: boolean | SsePluginOptions;
|
|
182
184
|
static?: boolean | FastifyStaticOptions;
|
|
185
|
+
ws?: boolean | WebsocketPluginOptions;
|
|
183
186
|
|
|
184
187
|
custom?: (server: FastifyInstance) => void;
|
|
185
188
|
};
|
|
@@ -200,6 +203,14 @@ export type SonamuServerOptions = {
|
|
|
200
203
|
};
|
|
201
204
|
};
|
|
202
205
|
|
|
206
|
+
/**
|
|
207
|
+
* WebSocket runtime 설정.
|
|
208
|
+
*
|
|
209
|
+
* 단일 인스턴스에서는 기본값으로 충분하며, 멀티 인스턴스/대규모 환경에서는
|
|
210
|
+
* presence store와 cluster bus를 여기서 주입합니다.
|
|
211
|
+
*/
|
|
212
|
+
websocket?: WebSocketRuntimeOptions;
|
|
213
|
+
|
|
203
214
|
apiConfig: SonamuFastifyConfig;
|
|
204
215
|
|
|
205
216
|
/**
|
|
@@ -255,9 +266,24 @@ export type SonamuTaskOptions = {
|
|
|
255
266
|
contextProvider: (
|
|
256
267
|
defaultContext: Pick<
|
|
257
268
|
Context,
|
|
258
|
-
|
|
269
|
+
| "transport"
|
|
270
|
+
| "reply"
|
|
271
|
+
| "request"
|
|
272
|
+
| "headers"
|
|
273
|
+
| "createSSE"
|
|
274
|
+
| "naiteStore"
|
|
275
|
+
| "locale"
|
|
276
|
+
| "user"
|
|
277
|
+
| "session"
|
|
259
278
|
>,
|
|
260
279
|
) => Context | Promise<Context>;
|
|
280
|
+
websocketContextProvider?: (
|
|
281
|
+
defaultContext: Pick<
|
|
282
|
+
WebSocketContext,
|
|
283
|
+
"transport" | "request" | "headers" | "ws" | "naiteStore" | "locale" | "user" | "session"
|
|
284
|
+
>,
|
|
285
|
+
request: FastifyRequest,
|
|
286
|
+
) => WebSocketContext | Promise<WebSocketContext>;
|
|
261
287
|
};
|
|
262
288
|
|
|
263
289
|
export type SonamuSSROptions = {
|
package/src/api/context.ts
CHANGED
|
@@ -14,24 +14,38 @@ import { type NaiteStore } from "../naite/naite";
|
|
|
14
14
|
import { type BufferedFile } from "../storage/buffered-file";
|
|
15
15
|
import { type UploadedFile } from "../storage/uploaded-file";
|
|
16
16
|
import { type createSSEFactory } from "../stream/sse";
|
|
17
|
+
import { type WebSocketConnection, type WebSocketEventMap } from "../stream/ws";
|
|
17
18
|
|
|
18
19
|
// oxlint-disable-next-line @typescript-eslint/no-empty-interface -- Context 확장 타입
|
|
19
20
|
export interface ContextExtend {}
|
|
20
|
-
|
|
21
|
+
type BaseContext = {
|
|
21
22
|
request: FastifyRequest;
|
|
22
|
-
reply: FastifyReply<Server, IncomingMessage, ServerResponse, RouteGenericInterface>;
|
|
23
23
|
headers: IncomingHttpHeaders;
|
|
24
|
-
createSSE: <T extends ZodObject>(events: T) => ReturnType<typeof createSSEFactory<T>>;
|
|
25
24
|
naiteStore: NaiteStore;
|
|
26
25
|
/** 현재 요청의 locale */
|
|
27
26
|
locale: string;
|
|
27
|
+
/** 현재 로그인한 사용자 (null이면 미인증) */
|
|
28
|
+
user: User | null;
|
|
29
|
+
/** 현재 세션 정보 (null이면 미인증) */
|
|
30
|
+
session: Session | null;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type Context = BaseContext & {
|
|
34
|
+
transport: "http";
|
|
35
|
+
reply: FastifyReply<Server, IncomingMessage, ServerResponse, RouteGenericInterface>;
|
|
36
|
+
createSSE: <T extends ZodObject>(events: T) => ReturnType<typeof createSSEFactory<T>>;
|
|
28
37
|
/** buffer 모드에서 업로드된 파일 */
|
|
29
38
|
bufferedFiles?: BufferedFile[];
|
|
30
39
|
/** stream 모드에서 업로드된 파일 */
|
|
31
40
|
uploadedFiles?: UploadedFile[];
|
|
41
|
+
} & ContextExtend;
|
|
32
42
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
43
|
+
export type WebSocketContext<
|
|
44
|
+
TOut extends WebSocketEventMap = WebSocketEventMap,
|
|
45
|
+
TIn extends WebSocketEventMap = WebSocketEventMap,
|
|
46
|
+
> = BaseContext & {
|
|
47
|
+
transport: "ws";
|
|
48
|
+
ws: WebSocketConnection<TOut, TIn>;
|
|
37
49
|
} & ContextExtend;
|
|
50
|
+
|
|
51
|
+
export type RuntimeContext = Context | WebSocketContext;
|
package/src/api/decorators.ts
CHANGED
|
@@ -61,6 +61,24 @@ export type StreamDecoratorOptions = {
|
|
|
61
61
|
guards?: GuardKey[];
|
|
62
62
|
description?: string;
|
|
63
63
|
};
|
|
64
|
+
export type WebSocketDecoratorOptions = {
|
|
65
|
+
// oxlint-disable-next-line @typescript-eslint/no-explicit-any -- 이벤트 키별로 넘겨주는 값이므로 어떤 타입이든 상관없음
|
|
66
|
+
outEvents: z.ZodObject<any>;
|
|
67
|
+
// oxlint-disable-next-line @typescript-eslint/no-explicit-any -- 이벤트 키별로 넘겨주는 값이므로 어떤 타입이든 상관없음
|
|
68
|
+
inEvents: z.ZodObject<any>;
|
|
69
|
+
path?: string;
|
|
70
|
+
resourceName?: string;
|
|
71
|
+
guards?: GuardKey[];
|
|
72
|
+
description?: string;
|
|
73
|
+
heartbeat?: number;
|
|
74
|
+
maxPayload?: number;
|
|
75
|
+
namespace?: string;
|
|
76
|
+
};
|
|
77
|
+
export type ResolvedWebSocketDecoratorOptions = WebSocketDecoratorOptions & {
|
|
78
|
+
// codegen이 타입 이름을 재사용할 수 있도록 syncer가 AST에서 보강하는 메타데이터
|
|
79
|
+
outEventsTypeRef?: ApiParamType.Ref;
|
|
80
|
+
inEventsTypeRef?: ApiParamType.Ref;
|
|
81
|
+
};
|
|
64
82
|
|
|
65
83
|
type BufferUploadOptions = {
|
|
66
84
|
consume?: "buffer";
|
|
@@ -85,6 +103,7 @@ export const registeredApis: {
|
|
|
85
103
|
path: string;
|
|
86
104
|
options: ApiDecoratorOptions;
|
|
87
105
|
streamOptions?: StreamDecoratorOptions;
|
|
106
|
+
websocketOptions?: ResolvedWebSocketDecoratorOptions;
|
|
88
107
|
uploadOptions?: UploadDecoratorOptions;
|
|
89
108
|
}[] = [];
|
|
90
109
|
export type ExtendedApi = {
|
|
@@ -93,6 +112,7 @@ export type ExtendedApi = {
|
|
|
93
112
|
path: string;
|
|
94
113
|
options: ApiDecoratorOptions;
|
|
95
114
|
streamOptions?: StreamDecoratorOptions;
|
|
115
|
+
websocketOptions?: ResolvedWebSocketDecoratorOptions;
|
|
96
116
|
uploadOptions?: UploadDecoratorOptions;
|
|
97
117
|
typeParameters: ApiParamType.TypeParam[];
|
|
98
118
|
parameters: ApiParam[];
|
|
@@ -103,6 +123,7 @@ type DecoratorTarget = { constructor: { name: string } };
|
|
|
103
123
|
const DECORATOR_TYPES = {
|
|
104
124
|
API: Symbol("api"),
|
|
105
125
|
STREAM: Symbol("stream"),
|
|
126
|
+
WEBSOCKET: Symbol("websocket"),
|
|
106
127
|
UPLOAD: Symbol("upload"),
|
|
107
128
|
} as const;
|
|
108
129
|
|
|
@@ -110,7 +131,7 @@ function checkSingleDecorator(target: DecoratorTarget, propertyKey: string, deco
|
|
|
110
131
|
const method = target[propertyKey as keyof typeof target] as { __decoratorType?: symbol };
|
|
111
132
|
if (method?.__decoratorType && method?.__decoratorType !== decoratorType) {
|
|
112
133
|
throw new Error(
|
|
113
|
-
`@${decoratorType.description ?? String(decoratorType)} decorator can only be used once on ${target.constructor.name}.${propertyKey}. You can use only one of @api or @
|
|
134
|
+
`@${decoratorType.description ?? String(decoratorType)} decorator can only be used once on ${target.constructor.name}.${propertyKey}. You can use only one of @api, @stream, @websocket, or @upload decorator on the same method.`,
|
|
114
135
|
);
|
|
115
136
|
} else {
|
|
116
137
|
method.__decoratorType = decoratorType;
|
|
@@ -278,6 +299,85 @@ export function stream(options: StreamDecoratorOptions) {
|
|
|
278
299
|
};
|
|
279
300
|
}
|
|
280
301
|
|
|
302
|
+
export function websocket(options: WebSocketDecoratorOptions) {
|
|
303
|
+
return (target: DecoratorTarget, propertyKey: string, descriptor: PropertyDescriptor) => {
|
|
304
|
+
const modelName = target.constructor.name.match(/(.+)Class$/)?.[1];
|
|
305
|
+
assert(
|
|
306
|
+
modelName,
|
|
307
|
+
`modelName is required on @websocket decorator on ${target.constructor.name}.${propertyKey}`,
|
|
308
|
+
);
|
|
309
|
+
const methodName = propertyKey;
|
|
310
|
+
|
|
311
|
+
checkSingleDecorator(target, propertyKey, DECORATOR_TYPES.WEBSOCKET);
|
|
312
|
+
|
|
313
|
+
const defaultPath = `/${inflection.camelize(
|
|
314
|
+
modelName.replace(/Model$/, "").replace(/Frame$/, ""),
|
|
315
|
+
true,
|
|
316
|
+
)}/${inflection.camelize(propertyKey, true)}`;
|
|
317
|
+
const path = options.path ?? defaultPath;
|
|
318
|
+
const { outEvents: _outEvents, inEvents: _inEvents, ...apiOptions } = options;
|
|
319
|
+
const optionsWithDefaults = {
|
|
320
|
+
...apiOptions,
|
|
321
|
+
httpMethod: "GET" as HTTPMethods,
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
const existingApi = registeredApis.find(
|
|
325
|
+
(api) => api.modelName === modelName && api.methodName === methodName,
|
|
326
|
+
);
|
|
327
|
+
if (existingApi) {
|
|
328
|
+
assertNoConflictingPath("websocket", modelName, methodName, existingApi.path, path);
|
|
329
|
+
existingApi.path = path;
|
|
330
|
+
|
|
331
|
+
assertNoConflictingOptions(
|
|
332
|
+
"websocket",
|
|
333
|
+
modelName,
|
|
334
|
+
methodName,
|
|
335
|
+
existingApi.options,
|
|
336
|
+
optionsWithDefaults,
|
|
337
|
+
);
|
|
338
|
+
existingApi.options = {
|
|
339
|
+
...existingApi.options,
|
|
340
|
+
...optionsWithDefaults,
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
existingApi.websocketOptions = options;
|
|
344
|
+
} else {
|
|
345
|
+
registeredApis.push({
|
|
346
|
+
modelName,
|
|
347
|
+
methodName,
|
|
348
|
+
path,
|
|
349
|
+
options: optionsWithDefaults,
|
|
350
|
+
websocketOptions: options,
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const originalMethod = descriptor.value;
|
|
355
|
+
descriptor.value = async function (this: BaseModelClass | BaseFrameClass, ...args: unknown[]) {
|
|
356
|
+
if (this instanceof BaseModelClass) {
|
|
357
|
+
getLogger(convertDomainToCategory(this.modelName, "model")).debug(
|
|
358
|
+
"websocket: {model}.{method}",
|
|
359
|
+
{
|
|
360
|
+
model: modelName,
|
|
361
|
+
method: methodName,
|
|
362
|
+
},
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (this instanceof BaseFrameClass) {
|
|
367
|
+
getLogger(convertDomainToCategory(this.frameName, "frame")).debug(
|
|
368
|
+
"websocket: {model}.{method}",
|
|
369
|
+
{
|
|
370
|
+
model: modelName,
|
|
371
|
+
method: methodName,
|
|
372
|
+
},
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
return originalMethod.apply(this, args);
|
|
377
|
+
};
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
|
|
281
381
|
export function transactional(options: TransactionalOptions = {}) {
|
|
282
382
|
const { isolation, readOnly, dbPreset = "w" } = options;
|
|
283
383
|
|