sonamu 0.7.17 → 0.7.19

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 (58) hide show
  1. package/dist/api/config.d.ts +19 -2
  2. package/dist/api/config.d.ts.map +1 -1
  3. package/dist/api/config.js +1 -1
  4. package/dist/api/context.d.ts +3 -3
  5. package/dist/api/context.d.ts.map +1 -1
  6. package/dist/api/context.js +1 -1
  7. package/dist/api/decorators.d.ts.map +1 -1
  8. package/dist/api/decorators.js +4 -8
  9. package/dist/api/index.d.ts +0 -2
  10. package/dist/api/index.d.ts.map +1 -1
  11. package/dist/api/index.js +1 -3
  12. package/dist/api/sonamu.d.ts +5 -3
  13. package/dist/api/sonamu.d.ts.map +1 -1
  14. package/dist/api/sonamu.js +10 -8
  15. package/dist/index.d.ts +0 -1
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +1 -2
  18. package/dist/storage/drivers.d.ts +14 -0
  19. package/dist/storage/drivers.d.ts.map +1 -0
  20. package/dist/storage/drivers.js +11 -0
  21. package/dist/storage/index.d.ts +5 -0
  22. package/dist/storage/index.d.ts.map +1 -0
  23. package/dist/storage/index.js +6 -0
  24. package/dist/storage/storage-manager.d.ts +21 -0
  25. package/dist/storage/storage-manager.d.ts.map +1 -0
  26. package/dist/storage/storage-manager.js +33 -0
  27. package/dist/storage/types.d.ts +12 -0
  28. package/dist/storage/types.d.ts.map +1 -0
  29. package/dist/storage/types.js +5 -0
  30. package/dist/storage/uploaded-file.d.ts +35 -0
  31. package/dist/storage/uploaded-file.d.ts.map +1 -0
  32. package/dist/storage/uploaded-file.js +58 -0
  33. package/dist/template/implementations/services.template.d.ts.map +1 -1
  34. package/dist/template/implementations/services.template.js +23 -2
  35. package/dist/ui-web/assets/{index-DzqUrTB-.js → index-DFqVuxOB.js} +1 -1
  36. package/dist/ui-web/index.html +1 -1
  37. package/package.json +8 -3
  38. package/src/api/config.ts +19 -2
  39. package/src/api/context.ts +3 -3
  40. package/src/api/decorators.ts +3 -8
  41. package/src/api/index.ts +0 -2
  42. package/src/api/sonamu.ts +12 -9
  43. package/src/index.ts +0 -1
  44. package/src/storage/drivers.ts +15 -0
  45. package/src/storage/index.ts +5 -0
  46. package/src/storage/storage-manager.ts +39 -0
  47. package/src/storage/types.ts +12 -0
  48. package/src/storage/uploaded-file.ts +81 -0
  49. package/src/template/implementations/service.template.ts.txt +328 -0
  50. package/src/template/implementations/services.template.ts +40 -1
  51. package/dist/file-storage/driver.d.ts +0 -48
  52. package/dist/file-storage/driver.d.ts.map +0 -1
  53. package/dist/file-storage/driver.js +0 -79
  54. package/dist/file-storage/file-storage.d.ts +0 -50
  55. package/dist/file-storage/file-storage.d.ts.map +0 -1
  56. package/dist/file-storage/file-storage.js +0 -75
  57. package/src/file-storage/driver.ts +0 -131
  58. package/src/file-storage/file-storage.ts +0 -100
@@ -0,0 +1,328 @@
1
+ import assert from "assert";
2
+ import inflection from "inflection";
3
+ import { diff, group, sort, unique } from "radashi";
4
+ import {
5
+ apiParamToTsCode,
6
+ apiParamToTsCodeAsObject,
7
+ apiParamTypeToTsType,
8
+ unwrapPromiseOnce,
9
+ } from "../../api/code-converters";
10
+ import type { ExtendedApi } from "../../api/decorators";
11
+ import { Sonamu } from "../../api/sonamu";
12
+ import type { EntityNamesRecord } from "../../entity/entity-manager";
13
+ import { Naite } from "../../naite/naite";
14
+ import type { TemplateOptions } from "../../types/types";
15
+ import { type ApiParam, ApiParamType } from "../../types/types";
16
+ import { assertDefined } from "../../utils/utils";
17
+ import { Template } from "../template";
18
+ import { zodTypeToTsTypeDef } from "../zod-converter";
19
+
20
+ export class Template__service extends Template {
21
+ constructor() {
22
+ super("service");
23
+ }
24
+
25
+ getTargetAndPath(names: EntityNamesRecord) {
26
+ return {
27
+ target: ":target/src/services",
28
+ path: `${names.fs}/${names.fs}.service.ts`,
29
+ };
30
+ }
31
+
32
+ render({ namesRecord }: TemplateOptions["service"]) {
33
+ Naite.t("render", { namesRecord });
34
+
35
+ const {
36
+ syncer: { apis },
37
+ } = Sonamu;
38
+
39
+ const apisForThisModel = apis.filter(
40
+ (api) =>
41
+ api.modelName === `${namesRecord.capital}Model` ||
42
+ api.modelName === `${namesRecord.capital}Frame`,
43
+ );
44
+
45
+ // 서비스 TypeSource
46
+ const { lines, importKeys } = this.getTypeSource(apisForThisModel);
47
+
48
+ // AxiosProgressEvent 있는지 확인
49
+ const hasAxiosProgressEvent = apis.find((api) =>
50
+ (api.options.clients ?? []).includes("axios-multipart"),
51
+ );
52
+
53
+ return {
54
+ ...this.getTargetAndPath(namesRecord),
55
+ body: lines.join("\n"),
56
+ importKeys: importKeys.filter((key) => ["ListResult"].includes(key) === false),
57
+ customHeaders: [
58
+ `import { z } from 'zod';`,
59
+ `import qs from "qs";`,
60
+ `import useSWR, { type SWRResponse } from "swr";`,
61
+ `import { fetch, ListResult, SWRError, SwrOptions, handleConditional, swrPostFetcher, EventHandlers, SSEStreamOptions, useSSEStream } from '../sonamu.shared';`,
62
+ ...(hasAxiosProgressEvent ? [`import { type AxiosProgressEvent } from 'axios';`] : []),
63
+ ],
64
+ };
65
+ }
66
+
67
+ getTypeSource(apis: ExtendedApi[]): {
68
+ lines: string[];
69
+ importKeys: string[];
70
+ } {
71
+ const importKeys: string[] = [];
72
+
73
+ // 제네릭에서 선언한 타입, importKeys에서 제외 필요
74
+ let typeParamNames: string[] = [];
75
+
76
+ const groups = group(apis, (api) => api.modelName);
77
+ const body = Object.keys(groups)
78
+ .map((modelName) => {
79
+ const methods = groups[modelName];
80
+ assert(methods);
81
+ const methodCodes = methods
82
+ .map((api) => {
83
+ // 컨텍스트 제외된 파라미터 리스트
84
+ const paramsWithoutContext = api.parameters.filter(
85
+ (param) =>
86
+ !ApiParamType.isContext(param.type) &&
87
+ !ApiParamType.isRefKnex(param.type) &&
88
+ !(param.optional === true && param.name.startsWith("_")), // _로 시작하는 파라미터는 제외
89
+ );
90
+
91
+ // 파라미터 타입 정의
92
+ const typeParametersAsTsType = api.typeParameters
93
+ .map((typeParam) => {
94
+ return apiParamTypeToTsType(typeParam, importKeys);
95
+ })
96
+ .join(", ");
97
+ const typeParamsDef = typeParametersAsTsType ? `<${typeParametersAsTsType}>` : "";
98
+ typeParamNames = typeParamNames.concat(
99
+ api.typeParameters.map((typeParam) => typeParam.id),
100
+ );
101
+
102
+ // 파라미터 정의
103
+ const paramsDef = apiParamToTsCode(paramsWithoutContext, importKeys);
104
+
105
+ // 파라미터 정의 (객체 형태)
106
+ const paramsDefAsObject = apiParamToTsCodeAsObject(paramsWithoutContext, importKeys);
107
+
108
+ // 리턴 타입 정의
109
+ const returnTypeDef = apiParamTypeToTsType(
110
+ assertDefined(unwrapPromiseOnce(api.returnType)),
111
+ importKeys,
112
+ );
113
+
114
+ // 페이로드 데이터 정의
115
+ const payloadDef = `{ ${paramsWithoutContext.map((param) => param.name).join(", ")} }`;
116
+
117
+ // 기본 URL
118
+ const apiBaseUrl = `${Sonamu.config.api.route.prefix}${api.path}`;
119
+
120
+ const clients = api.options.clients ?? [];
121
+ return [
122
+ // 클라이언트별로 생성
123
+ ...sort(clients, (client) => (client === "swr" ? 0 : 1)).map((client) => {
124
+ switch (client) {
125
+ case "axios":
126
+ return this.renderAxios(
127
+ api,
128
+ apiBaseUrl,
129
+ typeParamsDef,
130
+ paramsDef,
131
+ returnTypeDef,
132
+ payloadDef,
133
+ );
134
+ case "axios-multipart":
135
+ return this.renderAxiosMultipart(
136
+ api,
137
+ apiBaseUrl,
138
+ typeParamsDef,
139
+ paramsDef,
140
+ returnTypeDef,
141
+ paramsWithoutContext,
142
+ );
143
+ case "swr":
144
+ return this.renderSwr(
145
+ api,
146
+ apiBaseUrl,
147
+ typeParamsDef,
148
+ paramsDef,
149
+ returnTypeDef,
150
+ payloadDef,
151
+ );
152
+ case "window-fetch":
153
+ return this.renderWindowFetch(
154
+ api,
155
+ apiBaseUrl,
156
+ typeParamsDef,
157
+ paramsDef,
158
+ payloadDef,
159
+ );
160
+ default:
161
+ return `// Not supported ${inflection.camelize(client, true)} yet.`;
162
+ }
163
+ }),
164
+ // 스트리밍인 경우
165
+ ...(api.streamOptions ? [this.renderStream(api, apiBaseUrl, paramsDefAsObject)] : []),
166
+ ].join("\n");
167
+ })
168
+ .join("\n\n");
169
+
170
+ return `export namespace ${modelName.replace(/Model$/, "Service").replace(/Frame$/, "Service")} {
171
+ ${methodCodes}
172
+ }`;
173
+ })
174
+ .join("\n\n");
175
+
176
+ return {
177
+ lines: [body],
178
+ importKeys: diff(unique(importKeys), typeParamNames),
179
+ };
180
+ }
181
+
182
+ renderAxios(
183
+ api: ExtendedApi,
184
+ apiBaseUrl: string,
185
+ typeParamsDef: string,
186
+ paramsDef: string,
187
+ returnTypeDef: string,
188
+ payloadDef: string,
189
+ ) {
190
+ const methodNameAxios = api.options.resourceName
191
+ ? `get${inflection.camelize(api.options.resourceName)}`
192
+ : api.methodName;
193
+
194
+ if (api.options.httpMethod === "GET") {
195
+ return `
196
+ export async function ${methodNameAxios}${typeParamsDef}(${paramsDef}): Promise<${returnTypeDef}> {
197
+ return fetch({
198
+ method: "GET",
199
+ url: \`${apiBaseUrl}?\${qs.stringify(${payloadDef})}\`,
200
+ ${api.options.timeout ? `signal: AbortSignal.timeout(${api.options.timeout}),` : ""}
201
+ });
202
+ }
203
+ `.trim();
204
+ } else {
205
+ return `
206
+ export async function ${methodNameAxios}${typeParamsDef}(${paramsDef}): Promise<${returnTypeDef}> {
207
+ return fetch({
208
+ method: '${api.options.httpMethod}',
209
+ url: \`${apiBaseUrl}\`,
210
+ data: ${payloadDef},
211
+ ${api.options.timeout ? `signal: AbortSignal.timeout(${api.options.timeout}),` : ""}
212
+ });
213
+ }
214
+ `.trim();
215
+ }
216
+ }
217
+
218
+ renderAxiosMultipart(
219
+ api: ExtendedApi,
220
+ apiBaseUrl: string,
221
+ typeParamsDef: string,
222
+ paramsDef: string,
223
+ returnTypeDef: string,
224
+ paramsWithoutContext: ApiParam[],
225
+ ) {
226
+ const isMultiple = api.uploadOptions?.mode === "multiple";
227
+ const fileParamName = isMultiple ? "files" : "file";
228
+ const fileParamType = isMultiple ? "File[]" : "File";
229
+
230
+ const formDataDef = isMultiple
231
+ ? [
232
+ `${fileParamName}.forEach(f => { formData.append("${fileParamName}", f) } ); `,
233
+ ...paramsWithoutContext.map(
234
+ (param) => `formData.append('${param.name}', String(${param.name}));`,
235
+ ),
236
+ ].join("\n")
237
+ : [
238
+ `formData.append("${fileParamName}", ${fileParamName});`,
239
+ ...paramsWithoutContext.map(
240
+ (param) => `formData.append('${param.name}', String(${param.name}));`,
241
+ ),
242
+ ].join("\n");
243
+
244
+ const paramsDefComma = paramsDef !== "" ? ", " : "";
245
+ return `
246
+ export async function ${api.methodName}${typeParamsDef}(
247
+ ${paramsDef}${paramsDefComma}
248
+ ${fileParamName}: ${fileParamType},
249
+ onUploadProgress?: (pe:AxiosProgressEvent) => void
250
+ ): Promise<${returnTypeDef}> {
251
+ const formData = new FormData();
252
+ ${formDataDef}
253
+ return fetch({
254
+ method: 'POST',
255
+ url: \`${apiBaseUrl}\`,
256
+ headers: {
257
+ "Content-Type": "multipart/form-data",
258
+ },
259
+ onUploadProgress,
260
+ data: formData,
261
+ ${api.options.timeout ? `signal: AbortSignal.timeout(${api.options.timeout}),` : ""}
262
+ });
263
+ }
264
+ `.trim();
265
+ }
266
+
267
+ renderSwr(
268
+ api: ExtendedApi,
269
+ apiBaseUrl: string,
270
+ typeParamsDef: string,
271
+ paramsDef: string,
272
+ returnTypeDef: string,
273
+ payloadDef: string,
274
+ ) {
275
+ const methodNameSwr = api.options.resourceName
276
+ ? `use${inflection.camelize(api.options.resourceName)}`
277
+ : `use${inflection.camelize(api.methodName)}`;
278
+ return ` export function ${inflection.camelize(methodNameSwr, true)}${typeParamsDef}(${[
279
+ paramsDef,
280
+ "swrOptions?: SwrOptions",
281
+ ]
282
+ .filter((p) => p !== "")
283
+ .join(",")}, ): SWRResponse<${returnTypeDef}, SWRError> {
284
+ return useSWR(handleConditional([
285
+ \`${apiBaseUrl}\`,
286
+ ${payloadDef},
287
+ ], swrOptions?.conditional)${api.options.httpMethod === "POST" ? ", swrPostFetcher" : ""}${
288
+ api.options.timeout ? `, { loadingTimeout: ${api.options.timeout} }` : ""
289
+ });
290
+ }`;
291
+ }
292
+
293
+ renderWindowFetch(
294
+ api: ExtendedApi,
295
+ apiBaseUrl: string,
296
+ typeParamsDef: string,
297
+ paramsDef: string,
298
+ payloadDef: string,
299
+ ) {
300
+ return `
301
+ export async function ${api.methodName}${typeParamsDef}(${paramsDef}): Promise<Response> {
302
+ return window.fetch(\`${apiBaseUrl}?\${qs.stringify(${payloadDef})}\`${
303
+ api.options.timeout ? `, { signal: AbortSignal.timeout(${api.options.timeout}) }` : ""
304
+ });
305
+ }
306
+ `.trim();
307
+ }
308
+
309
+ renderStream(api: ExtendedApi, apiBaseUrl: string, paramsDefAsObject: string) {
310
+ if (!api.streamOptions) {
311
+ return "// streamOptions not found";
312
+ }
313
+
314
+ const methodNameStream = api.options.resourceName
315
+ ? `use${inflection.camelize(api.options.resourceName)}`
316
+ : `use${inflection.camelize(api.methodName)}`;
317
+ const methodNameStreamCamelized = inflection.camelize(methodNameStream, true);
318
+
319
+ const eventsTypeDef = zodTypeToTsTypeDef(api.streamOptions.events);
320
+
321
+ return ` export function ${methodNameStreamCamelized}(
322
+ params: ${paramsDefAsObject},
323
+ handlers: EventHandlers<${eventsTypeDef} & { end?: () => void }>,
324
+ options: SSEStreamOptions) {
325
+ return useSSEStream<${eventsTypeDef}>(\`${apiBaseUrl}\`, params, handlers, options);
326
+ }`;
327
+ }
328
+ }
@@ -11,6 +11,7 @@ import type { TemplateOptions } from "../../types/types";
11
11
  import { ApiParamType } from "../../types/types";
12
12
  import { assertDefined } from "../../utils/utils";
13
13
  import { Template } from "../template";
14
+ import { zodTypeToTsTypeDef } from "../zod-converter";
14
15
 
15
16
  export class Template__services extends Template {
16
17
  constructor() {
@@ -45,6 +46,44 @@ export class Template__services extends Template {
45
46
  const functions: string[] = [];
46
47
 
47
48
  for (const api of modelApis) {
49
+ // @stream 데코레이터가 있으면 SSE 스트림 함수 생성
50
+ if (api.streamOptions) {
51
+ const paramsWithoutContext = api.parameters.filter(
52
+ (param) =>
53
+ !ApiParamType.isContext(param.type) &&
54
+ !ApiParamType.isRefKnex(param.type) &&
55
+ !(param.optional === true && param.name.startsWith("_")),
56
+ );
57
+
58
+ const apiBaseUrl = `${Sonamu.config.api.route.prefix}${api.path}`;
59
+
60
+ const methodNameStream = api.options.resourceName
61
+ ? `use${inflection.camelize(api.options.resourceName)}`
62
+ : `use${inflection.camelize(api.methodName)}`;
63
+ const methodNameStreamCamelized = inflection.camelize(methodNameStream, true);
64
+
65
+ const eventsTypeDef = zodTypeToTsTypeDef(api.streamOptions.events);
66
+
67
+ // 파라미터를 객체 형태로 정의 (타입과 실제 값 모두에 사용)
68
+ const paramsDefAsObject =
69
+ paramsWithoutContext.length > 0
70
+ ? `{ ${paramsWithoutContext.map((p) => `${p.name}: ${apiParamTypeToTsType(p.type, importKeys)}`).join(", ")} }`
71
+ : "{}";
72
+
73
+ functions.push(
74
+ `
75
+ export function ${methodNameStreamCamelized}(
76
+ params: ${paramsDefAsObject},
77
+ handlers: EventHandlers<${eventsTypeDef} & { end?: () => void }>,
78
+ options: SSEStreamOptions
79
+ ) {
80
+ return useSSEStream<${eventsTypeDef}>(\`${apiBaseUrl}\`, params, handlers, options);
81
+ }
82
+ `.trim(),
83
+ );
84
+ continue;
85
+ }
86
+
48
87
  // Context 제외한 파라미터
49
88
  const paramsWithoutContext = api.parameters.filter(
50
89
  (param) =>
@@ -219,7 +258,7 @@ ${functions.join("\n\n")}
219
258
  `import { queryOptions, useQuery, useMutation } from '@tanstack/react-query';`,
220
259
  `import type { AxiosProgressEvent } from 'axios';`,
221
260
  `import qs from 'qs';`,
222
- `import { type ListResult, fetch } from './sonamu.shared';`,
261
+ `import { type ListResult, fetch, type EventHandlers, type SSEStreamOptions, useSSEStream } from './sonamu.shared';`,
223
262
  ],
224
263
  };
225
264
  }
@@ -1,48 +0,0 @@
1
- import { S3Client, type S3ClientConfig } from "@aws-sdk/client-s3";
2
- /**
3
- * 파일 저장소의 공통 인터페이스
4
- */
5
- export interface Driver {
6
- put(key: string, contents: Buffer, options?: {
7
- contentType?: string;
8
- visibility?: "public" | "private";
9
- }): Promise<void>;
10
- del(key: string): Promise<void>;
11
- getUrl(key: string): string;
12
- getSignedUrl(key: string, expiresIn?: number): Promise<string>;
13
- destroy(): void;
14
- }
15
- export type FSDriverConfig = {
16
- location: string;
17
- urlPrefix: string;
18
- };
19
- /**
20
- * 로컬 파일시스템
21
- */
22
- export declare class FSDriver implements Driver {
23
- private config;
24
- constructor(config: FSDriverConfig);
25
- put(key: string, contents: Buffer): Promise<void>;
26
- del(key: string): Promise<void>;
27
- getUrl(key: string): string;
28
- getSignedUrl(key: string, _expiresIn?: number): Promise<string>;
29
- destroy(): void;
30
- }
31
- export type S3DriverConfig = S3ClientConfig & {
32
- bucket: string;
33
- };
34
- export declare class S3Driver implements Driver {
35
- private config;
36
- s3: S3Client;
37
- constructor(config: S3DriverConfig);
38
- put(key: string, contents: Buffer, options?: {
39
- contentType?: string;
40
- visibility?: "public" | "private";
41
- }): Promise<void>;
42
- del(key: string): Promise<void>;
43
- getUrl(key: string): string;
44
- getSignedUrl(key: string, expiresIn?: number): Promise<string>;
45
- private getAcl;
46
- destroy(): void;
47
- }
48
- //# sourceMappingURL=driver.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"driver.d.ts","sourceRoot":"","sources":["../../src/file-storage/driver.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,QAAQ,EACR,KAAK,cAAc,EACpB,MAAM,oBAAoB,CAAC;AAK5B;;GAEG;AACH,MAAM,WAAW,MAAM;IACrB,GAAG,CACD,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAA;KAAE,GACpE,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IAE5B,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAE/D,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;GAEG;AACH,qBAAa,QAAS,YAAW,MAAM;IACzB,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,cAAc;IAEpC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASjD,GAAG,CAAC,GAAG,EAAE,MAAM;IAIrB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAKrB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIrE,OAAO;CAGR;AAED,MAAM,MAAM,cAAc,GAAG,cAAc,GAAG;IAC5C,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,qBAAa,QAAS,YAAW,MAAM;IAGzB,OAAO,CAAC,MAAM;IAF1B,EAAE,EAAE,QAAQ,CAAC;gBAEO,MAAM,EAAE,cAAc;IAIpC,GAAG,CACP,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAA;KAAE,GACpE,OAAO,CAAC,IAAI,CAAC;IAYV,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASrC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAIrB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAWpE,OAAO,CAAC,MAAM;IAQd,OAAO;CAGR"}
@@ -1,79 +0,0 @@
1
- import { DeleteObjectCommand, GetObjectCommand, PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
2
- import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
3
- import fs from "node:fs/promises";
4
- import path from "path";
5
- /**
6
- * 로컬 파일시스템
7
- */ export class FSDriver {
8
- config;
9
- constructor(config){
10
- this.config = config;
11
- }
12
- async put(key, contents) {
13
- const filePath = path.join(this.config.location, key);
14
- const dir = path.dirname(filePath);
15
- await fs.mkdir(dir, {
16
- recursive: true
17
- });
18
- await fs.writeFile(filePath, contents);
19
- }
20
- async del(key) {
21
- await fs.rm(path.join(this.config.location, key));
22
- }
23
- getUrl(key) {
24
- return `${this.config.urlPrefix}/${key}`;
25
- }
26
- // 로컬 파일시스템은 signed URL을 지원하지 않으므로 일반 URL 반환
27
- async getSignedUrl(key, _expiresIn) {
28
- return this.getUrl(key);
29
- }
30
- destroy() {
31
- // 아무것도 하지 않음
32
- }
33
- }
34
- export class S3Driver {
35
- config;
36
- s3;
37
- constructor(config){
38
- this.config = config;
39
- this.s3 = new S3Client(config);
40
- }
41
- async put(key, contents, options) {
42
- await this.s3.send(new PutObjectCommand({
43
- Bucket: this.config.bucket,
44
- Key: key,
45
- Body: contents,
46
- ContentType: options?.contentType,
47
- ACL: this.getAcl(options?.visibility)
48
- }));
49
- }
50
- async del(key) {
51
- await this.s3.send(new DeleteObjectCommand({
52
- Bucket: this.config.bucket,
53
- Key: key
54
- }));
55
- }
56
- getUrl(key) {
57
- return `https://${this.config.bucket}.s3.${this.config.region}.amazonaws.com/${key}`;
58
- }
59
- async getSignedUrl(key, expiresIn) {
60
- const command = new GetObjectCommand({
61
- Bucket: this.config.bucket,
62
- Key: key
63
- });
64
- return getSignedUrl(this.s3, command, {
65
- expiresIn: expiresIn ?? 60 * 60 * 24 * 7
66
- });
67
- }
68
- getAcl(visibility) {
69
- if (visibility === "public") {
70
- return "public-read";
71
- }
72
- return visibility;
73
- }
74
- destroy() {
75
- this.s3.destroy();
76
- }
77
- }
78
-
79
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9maWxlLXN0b3JhZ2UvZHJpdmVyLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIERlbGV0ZU9iamVjdENvbW1hbmQsXG4gIEdldE9iamVjdENvbW1hbmQsXG4gIFB1dE9iamVjdENvbW1hbmQsXG4gIFMzQ2xpZW50LFxuICB0eXBlIFMzQ2xpZW50Q29uZmlnLFxufSBmcm9tIFwiQGF3cy1zZGsvY2xpZW50LXMzXCI7XG5pbXBvcnQgeyBnZXRTaWduZWRVcmwgfSBmcm9tIFwiQGF3cy1zZGsvczMtcmVxdWVzdC1wcmVzaWduZXJcIjtcbmltcG9ydCBmcyBmcm9tIFwiZnMvcHJvbWlzZXNcIjtcbmltcG9ydCBwYXRoIGZyb20gXCJwYXRoXCI7XG5cbi8qKlxuICog7YyM7J28IOyggOyepeyGjOydmCDqs7XthrUg7J247YSw7Y6Y7J207IqkXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgRHJpdmVyIHtcbiAgcHV0KFxuICAgIGtleTogc3RyaW5nLFxuICAgIGNvbnRlbnRzOiBCdWZmZXIsXG4gICAgb3B0aW9ucz86IHsgY29udGVudFR5cGU/OiBzdHJpbmc7IHZpc2liaWxpdHk/OiBcInB1YmxpY1wiIHwgXCJwcml2YXRlXCIgfSxcbiAgKTogUHJvbWlzZTx2b2lkPjtcblxuICBkZWwoa2V5OiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+O1xuXG4gIGdldFVybChrZXk6IHN0cmluZyk6IHN0cmluZztcblxuICBnZXRTaWduZWRVcmwoa2V5OiBzdHJpbmcsIGV4cGlyZXNJbj86IG51bWJlcik6IFByb21pc2U8c3RyaW5nPjtcblxuICBkZXN0cm95KCk6IHZvaWQ7XG59XG5cbmV4cG9ydCB0eXBlIEZTRHJpdmVyQ29uZmlnID0ge1xuICBsb2NhdGlvbjogc3RyaW5nO1xuICB1cmxQcmVmaXg6IHN0cmluZztcbn07XG5cbi8qKlxuICog66Gc7LusIO2MjOydvOyLnOyKpO2FnFxuICovXG5leHBvcnQgY2xhc3MgRlNEcml2ZXIgaW1wbGVtZW50cyBEcml2ZXIge1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIGNvbmZpZzogRlNEcml2ZXJDb25maWcpIHt9XG5cbiAgYXN5bmMgcHV0KGtleTogc3RyaW5nLCBjb250ZW50czogQnVmZmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgZmlsZVBhdGggPSBwYXRoLmpvaW4odGhpcy5jb25maWcubG9jYXRpb24sIGtleSk7XG4gICAgY29uc3QgZGlyID0gcGF0aC5kaXJuYW1lKGZpbGVQYXRoKTtcblxuICAgIGF3YWl0IGZzLm1rZGlyKGRpciwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG5cbiAgICBhd2FpdCBmcy53cml0ZUZpbGUoZmlsZVBhdGgsIGNvbnRlbnRzKTtcbiAgfVxuXG4gIGFzeW5jIGRlbChrZXk6IHN0cmluZykge1xuICAgIGF3YWl0IGZzLnJtKHBhdGguam9pbih0aGlzLmNvbmZpZy5sb2NhdGlvbiwga2V5KSk7XG4gIH1cblxuICBnZXRVcmwoa2V5OiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIHJldHVybiBgJHt0aGlzLmNvbmZpZy51cmxQcmVmaXh9LyR7a2V5fWA7XG4gIH1cblxuICAvLyDroZzsu6wg7YyM7J287Iuc7Iqk7YWc7J2AIHNpZ25lZCBVUkzsnYQg7KeA7JuQ7ZWY7KeAIOyViuycvOuvgOuhnCDsnbzrsJggVVJMIOuwmO2ZmFxuICBhc3luYyBnZXRTaWduZWRVcmwoa2V5OiBzdHJpbmcsIF9leHBpcmVzSW4/OiBudW1iZXIpOiBQcm9taXNlPHN0cmluZz4ge1xuICAgIHJldHVybiB0aGlzLmdldFVybChrZXkpO1xuICB9XG5cbiAgZGVzdHJveSgpIHtcbiAgICAvLyDslYTrrLTqsoPrj4Qg7ZWY7KeAIOyViuydjFxuICB9XG59XG5cbmV4cG9ydCB0eXBlIFMzRHJpdmVyQ29uZmlnID0gUzNDbGllbnRDb25maWcgJiB7XG4gIGJ1Y2tldDogc3RyaW5nO1xufTtcblxuZXhwb3J0IGNsYXNzIFMzRHJpdmVyIGltcGxlbWVudHMgRHJpdmVyIHtcbiAgczM6IFMzQ2xpZW50O1xuXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgY29uZmlnOiBTM0RyaXZlckNvbmZpZykge1xuICAgIHRoaXMuczMgPSBuZXcgUzNDbGllbnQoY29uZmlnKTtcbiAgfVxuXG4gIGFzeW5jIHB1dChcbiAgICBrZXk6IHN0cmluZyxcbiAgICBjb250ZW50czogQnVmZmVyLFxuICAgIG9wdGlvbnM/OiB7IGNvbnRlbnRUeXBlPzogc3RyaW5nOyB2aXNpYmlsaXR5PzogXCJwdWJsaWNcIiB8IFwicHJpdmF0ZVwiIH0sXG4gICk6IFByb21pc2U8dm9pZD4ge1xuICAgIGF3YWl0IHRoaXMuczMuc2VuZChcbiAgICAgIG5ldyBQdXRPYmplY3RDb21tYW5kKHtcbiAgICAgICAgQnVja2V0OiB0aGlzLmNvbmZpZy5idWNrZXQsXG4gICAgICAgIEtleToga2V5LFxuICAgICAgICBCb2R5OiBjb250ZW50cyxcbiAgICAgICAgQ29udGVudFR5cGU6IG9wdGlvbnM/LmNvbnRlbnRUeXBlLFxuICAgICAgICBBQ0w6IHRoaXMuZ2V0QWNsKG9wdGlvbnM/LnZpc2liaWxpdHkpLFxuICAgICAgfSksXG4gICAgKTtcbiAgfVxuXG4gIGFzeW5jIGRlbChrZXk6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgIGF3YWl0IHRoaXMuczMuc2VuZChcbiAgICAgIG5ldyBEZWxldGVPYmplY3RDb21tYW5kKHtcbiAgICAgICAgQnVja2V0OiB0aGlzLmNvbmZpZy5idWNrZXQsXG4gICAgICAgIEtleToga2V5LFxuICAgICAgfSksXG4gICAgKTtcbiAgfVxuXG4gIGdldFVybChrZXk6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgcmV0dXJuIGBodHRwczovLyR7dGhpcy5jb25maWcuYnVja2V0fS5zMy4ke3RoaXMuY29uZmlnLnJlZ2lvbn0uYW1hem9uYXdzLmNvbS8ke2tleX1gO1xuICB9XG5cbiAgYXN5bmMgZ2V0U2lnbmVkVXJsKGtleTogc3RyaW5nLCBleHBpcmVzSW4/OiBudW1iZXIpOiBQcm9taXNlPHN0cmluZz4ge1xuICAgIGNvbnN0IGNvbW1hbmQgPSBuZXcgR2V0T2JqZWN0Q29tbWFuZCh7XG4gICAgICBCdWNrZXQ6IHRoaXMuY29uZmlnLmJ1Y2tldCxcbiAgICAgIEtleToga2V5LFxuICAgIH0pO1xuXG4gICAgcmV0dXJuIGdldFNpZ25lZFVybCh0aGlzLnMzLCBjb21tYW5kLCB7XG4gICAgICBleHBpcmVzSW46IGV4cGlyZXNJbiA/PyA2MCAqIDYwICogMjQgKiA3LFxuICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBnZXRBY2wodmlzaWJpbGl0eT86IFwicHVibGljXCIgfCBcInByaXZhdGVcIikge1xuICAgIGlmICh2aXNpYmlsaXR5ID09PSBcInB1YmxpY1wiKSB7XG4gICAgICByZXR1cm4gXCJwdWJsaWMtcmVhZFwiO1xuICAgIH1cblxuICAgIHJldHVybiB2aXNpYmlsaXR5O1xuICB9XG5cbiAgZGVzdHJveSgpIHtcbiAgICB0aGlzLnMzLmRlc3Ryb3koKTtcbiAgfVxufVxuIl0sIm5hbWVzIjpbIkRlbGV0ZU9iamVjdENvbW1hbmQiLCJHZXRPYmplY3RDb21tYW5kIiwiUHV0T2JqZWN0Q29tbWFuZCIsIlMzQ2xpZW50IiwiZ2V0U2lnbmVkVXJsIiwiZnMiLCJwYXRoIiwiRlNEcml2ZXIiLCJjb25maWciLCJwdXQiLCJrZXkiLCJjb250ZW50cyIsImZpbGVQYXRoIiwiam9pbiIsImxvY2F0aW9uIiwiZGlyIiwiZGlybmFtZSIsIm1rZGlyIiwicmVjdXJzaXZlIiwid3JpdGVGaWxlIiwiZGVsIiwicm0iLCJnZXRVcmwiLCJ1cmxQcmVmaXgiLCJfZXhwaXJlc0luIiwiZGVzdHJveSIsIlMzRHJpdmVyIiwiczMiLCJvcHRpb25zIiwic2VuZCIsIkJ1Y2tldCIsImJ1Y2tldCIsIktleSIsIkJvZHkiLCJDb250ZW50VHlwZSIsImNvbnRlbnRUeXBlIiwiQUNMIiwiZ2V0QWNsIiwidmlzaWJpbGl0eSIsInJlZ2lvbiIsImV4cGlyZXNJbiIsImNvbW1hbmQiXSwibWFwcGluZ3MiOiJBQUFBLFNBQ0VBLG1CQUFtQixF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
@@ -1,50 +0,0 @@
1
- import type { MultipartFile } from "@fastify/multipart";
2
- import type { Driver } from "./driver";
3
- /**
4
- * @fastify/multipart의 MultipartFile 래퍼
5
- */
6
- export declare class FileStorage {
7
- private _file;
8
- private _buffer?;
9
- private _driver;
10
- constructor(file: MultipartFile, driver: Driver);
11
- /**
12
- * 사용자 컴퓨터의 원본 파일명
13
- */
14
- get clientName(): string;
15
- /**
16
- * 파일명 (clientName의 별칭)
17
- */
18
- get filename(): string;
19
- /**
20
- * HTML input 필드명
21
- */
22
- get fieldName(): string;
23
- /**
24
- * 파일 크기 (바이트)
25
- */
26
- get size(): number;
27
- /**
28
- * 파일 확장자 (점 제외)
29
- */
30
- get extname(): string | false;
31
- get mimetype(): string;
32
- get encoding(): string;
33
- toBuffer(): Promise<Buffer>;
34
- md5(): Promise<string>;
35
- /**
36
- * 파일을 저장소에 저장
37
- *
38
- * @example
39
- * ```typescript
40
- * const { file } = Sonamu.getUploadContext();
41
- * const url = await file.saveToDisk('uploads/avatar.png');
42
- * ```
43
- */
44
- saveToDisk(key: string, options?: {
45
- contentType?: string;
46
- visibility?: "public" | "private";
47
- }): Promise<string>;
48
- get raw(): MultipartFile;
49
- }
50
- //# sourceMappingURL=file-storage.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"file-storage.d.ts","sourceRoot":"","sources":["../../src/file-storage/file-storage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGxD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,OAAO,CAAC,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;gBAEZ,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM;IAK/C;;OAEG;IACH,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED;;OAEG;IACH,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED;;OAEG;IACH,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;OAEG;IACH,IAAI,OAAO,IAAI,MAAM,GAAG,KAAK,CAE5B;IAED,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,IAAI,QAAQ,IAAI,MAAM,CAErB;IAEK,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;IAO3B,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC;IAK5B;;;;;;;;OAQG;IACG,UAAU,CACd,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAA;KAAE,GACpE,OAAO,CAAC,MAAM,CAAC;IAWlB,IAAI,GAAG,IAAI,aAAa,CAEvB;CACF"}
@@ -1,75 +0,0 @@
1
- import { createHash } from "crypto";
2
- import mime from "mime-types";
3
- /**
4
- * @fastify/multipart의 MultipartFile 래퍼
5
- */ export class FileStorage {
6
- _file;
7
- _buffer;
8
- _driver;
9
- constructor(file, driver){
10
- this._file = file;
11
- this._driver = driver;
12
- }
13
- /**
14
- * 사용자 컴퓨터의 원본 파일명
15
- */ get clientName() {
16
- return this._file.filename;
17
- }
18
- /**
19
- * 파일명 (clientName의 별칭)
20
- */ get filename() {
21
- return this._file.filename;
22
- }
23
- /**
24
- * HTML input 필드명
25
- */ get fieldName() {
26
- return this._file.fieldname;
27
- }
28
- /**
29
- * 파일 크기 (바이트)
30
- */ get size() {
31
- return this._file.file.bytesRead;
32
- }
33
- /**
34
- * 파일 확장자 (점 제외)
35
- */ get extname() {
36
- return mime.extension(this._file.mimetype);
37
- }
38
- get mimetype() {
39
- return this._file.mimetype;
40
- }
41
- get encoding() {
42
- return this._file.encoding;
43
- }
44
- async toBuffer() {
45
- if (!this._buffer) {
46
- this._buffer = await this._file.toBuffer();
47
- }
48
- return this._buffer;
49
- }
50
- async md5() {
51
- const buffer = await this.toBuffer();
52
- return createHash("md5").update(buffer).digest("hex");
53
- }
54
- /**
55
- * 파일을 저장소에 저장
56
- *
57
- * @example
58
- * ```typescript
59
- * const { file } = Sonamu.getUploadContext();
60
- * const url = await file.saveToDisk('uploads/avatar.png');
61
- * ```
62
- */ async saveToDisk(key, options) {
63
- const buffer = await this.toBuffer();
64
- await this._driver.put(key, buffer, {
65
- contentType: options?.contentType ?? this.mimetype,
66
- visibility: options?.visibility
67
- });
68
- return this._driver.getSignedUrl(key);
69
- }
70
- get raw() {
71
- return this._file;
72
- }
73
- }
74
-
75
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9maWxlLXN0b3JhZ2UvZmlsZS1zdG9yYWdlLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0eXBlIHsgTXVsdGlwYXJ0RmlsZSB9IGZyb20gXCJAZmFzdGlmeS9tdWx0aXBhcnRcIjtcbmltcG9ydCB7IGNyZWF0ZUhhc2ggfSBmcm9tIFwiY3J5cHRvXCI7XG5pbXBvcnQgbWltZSBmcm9tIFwibWltZS10eXBlc1wiO1xuaW1wb3J0IHR5cGUgeyBEcml2ZXIgfSBmcm9tIFwiLi9kcml2ZXJcIjtcblxuLyoqXG4gKiBAZmFzdGlmeS9tdWx0aXBhcnTsnZggTXVsdGlwYXJ0RmlsZSDrnpjtjbxcbiAqL1xuZXhwb3J0IGNsYXNzIEZpbGVTdG9yYWdlIHtcbiAgcHJpdmF0ZSBfZmlsZTogTXVsdGlwYXJ0RmlsZTtcbiAgcHJpdmF0ZSBfYnVmZmVyPzogQnVmZmVyO1xuICBwcml2YXRlIF9kcml2ZXI6IERyaXZlcjtcblxuICBjb25zdHJ1Y3RvcihmaWxlOiBNdWx0aXBhcnRGaWxlLCBkcml2ZXI6IERyaXZlcikge1xuICAgIHRoaXMuX2ZpbGUgPSBmaWxlO1xuICAgIHRoaXMuX2RyaXZlciA9IGRyaXZlcjtcbiAgfVxuXG4gIC8qKlxuICAgKiDsgqzsmqnsnpAg7Lu07ZOo7YSw7J2YIOybkOuzuCDtjIzsnbzrqoVcbiAgICovXG4gIGdldCBjbGllbnROYW1lKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRoaXMuX2ZpbGUuZmlsZW5hbWU7XG4gIH1cblxuICAvKipcbiAgICog7YyM7J2866qFIChjbGllbnROYW1l7J2YIOuzhOy5rSlcbiAgICovXG4gIGdldCBmaWxlbmFtZSgpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLl9maWxlLmZpbGVuYW1lO1xuICB9XG5cbiAgLyoqXG4gICAqIEhUTUwgaW5wdXQg7ZWE65Oc66qFXG4gICAqL1xuICBnZXQgZmllbGROYW1lKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRoaXMuX2ZpbGUuZmllbGRuYW1lO1xuICB9XG5cbiAgLyoqXG4gICAqIO2MjOydvCDtgazquLAgKOuwlOydtO2KuClcbiAgICovXG4gIGdldCBzaXplKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMuX2ZpbGUuZmlsZS5ieXRlc1JlYWQ7XG4gIH1cblxuICAvKipcbiAgICog7YyM7J28IO2ZleyepeyekCAo7KCQIOygnOyZuClcbiAgICovXG4gIGdldCBleHRuYW1lKCk6IHN0cmluZyB8IGZhbHNlIHtcbiAgICByZXR1cm4gbWltZS5leHRlbnNpb24odGhpcy5fZmlsZS5taW1ldHlwZSk7XG4gIH1cblxuICBnZXQgbWltZXR5cGUoKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdGhpcy5fZmlsZS5taW1ldHlwZTtcbiAgfVxuXG4gIGdldCBlbmNvZGluZygpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLl9maWxlLmVuY29kaW5nO1xuICB9XG5cbiAgYXN5bmMgdG9CdWZmZXIoKTogUHJvbWlzZTxCdWZmZXI+IHtcbiAgICBpZiAoIXRoaXMuX2J1ZmZlcikge1xuICAgICAgdGhpcy5fYnVmZmVyID0gYXdhaXQgdGhpcy5fZmlsZS50b0J1ZmZlcigpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5fYnVmZmVyO1xuICB9XG5cbiAgYXN5bmMgbWQ1KCk6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgY29uc3QgYnVmZmVyID0gYXdhaXQgdGhpcy50b0J1ZmZlcigpO1xuICAgIHJldHVybiBjcmVhdGVIYXNoKFwibWQ1XCIpLnVwZGF0ZShidWZmZXIpLmRpZ2VzdChcImhleFwiKTtcbiAgfVxuXG4gIC8qKlxuICAgKiDtjIzsnbzsnYQg7KCA7J6l7IaM7JeQIOyggOyepVxuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiBgYGB0eXBlc2NyaXB0XG4gICAqIGNvbnN0IHsgZmlsZSB9ID0gU29uYW11LmdldFVwbG9hZENvbnRleHQoKTtcbiAgICogY29uc3QgdXJsID0gYXdhaXQgZmlsZS5zYXZlVG9EaXNrKCd1cGxvYWRzL2F2YXRhci5wbmcnKTtcbiAgICogYGBgXG4gICAqL1xuICBhc3luYyBzYXZlVG9EaXNrKFxuICAgIGtleTogc3RyaW5nLFxuICAgIG9wdGlvbnM/OiB7IGNvbnRlbnRUeXBlPzogc3RyaW5nOyB2aXNpYmlsaXR5PzogXCJwdWJsaWNcIiB8IFwicHJpdmF0ZVwiIH0sXG4gICk6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgY29uc3QgYnVmZmVyID0gYXdhaXQgdGhpcy50b0J1ZmZlcigpO1xuXG4gICAgYXdhaXQgdGhpcy5fZHJpdmVyLnB1dChrZXksIGJ1ZmZlciwge1xuICAgICAgY29udGVudFR5cGU6IG9wdGlvbnM/LmNvbnRlbnRUeXBlID8/IHRoaXMubWltZXR5cGUsXG4gICAgICB2aXNpYmlsaXR5OiBvcHRpb25zPy52aXNpYmlsaXR5LFxuICAgIH0pO1xuXG4gICAgcmV0dXJuIHRoaXMuX2RyaXZlci5nZXRTaWduZWRVcmwoa2V5KTtcbiAgfVxuXG4gIGdldCByYXcoKTogTXVsdGlwYXJ0RmlsZSB7XG4gICAgcmV0dXJuIHRoaXMuX2ZpbGU7XG4gIH1cbn1cbiJdLCJuYW1lcyI6WyJjcmVhdGVIYXNoIiwibWltZSIsIkZpbGVTdG9yYWdlIiwiX2ZpbGUiLCJfYnVmZmVyIiwiX2RyaXZlciIsImZpbGUiLCJkcml2ZXIiLCJjbGllbnROYW1lIiwiZmlsZW5hbWUiLCJmaWVsZE5hbWUiLCJmaWVsZG5hbWUiLCJzaXplIiwiYnl0ZXNSZWFkIiwiZXh0bmFtZSIsImV4dGVuc2lvbiIsIm1pbWV0eXBlIiwiZW5jb2RpbmciLCJ0b0J1ZmZlciIsIm1kNSIsImJ1ZmZlciIsInVwZGF0ZSIsImRpZ2VzdCIsInNhdmVUb0Rpc2siLCJrZXkiLCJvcHRpb25zIiwicHV0IiwiY29udGVudFR5cGUiLCJ2aXNpYmlsaXR5IiwiZ2V0U2lnbmVkVXJsIiwicmF3Il0sIm1hcHBpbmdzIjoiQUFDQSxTQUFTQSxVQUFVLFFBQVEsU0FBUztBQUNwQyxPQUFPQyxVQUFVLGFBQWE7QUFHOUI7O0NBRUMsR0FDRCxPQUFPLE1BQU1DO0lBQ0hDLE1BQXFCO0lBQ3JCQyxRQUFpQjtJQUNqQkMsUUFBZ0I7SUFFeEIsWUFBWUMsSUFBbUIsRUFBRUMsTUFBYyxDQUFFO1FBQy9DLElBQUksQ0FBQ0osS0FBSyxHQUFHRztRQUNiLElBQUksQ0FBQ0QsT0FBTyxHQUFHRTtJQUNqQjtJQUVBOztHQUVDLEdBQ0QsSUFBSUMsYUFBcUI7UUFDdkIsT0FBTyxJQUFJLENBQUNMLEtBQUssQ0FBQ00sUUFBUTtJQUM1QjtJQUVBOztHQUVDLEdBQ0QsSUFBSUEsV0FBbUI7UUFDckIsT0FBTyxJQUFJLENBQUNOLEtBQUssQ0FBQ00sUUFBUTtJQUM1QjtJQUVBOztHQUVDLEdBQ0QsSUFBSUMsWUFBb0I7UUFDdEIsT0FBTyxJQUFJLENBQUNQLEtBQUssQ0FBQ1EsU0FBUztJQUM3QjtJQUVBOztHQUVDLEdBQ0QsSUFBSUMsT0FBZTtRQUNqQixPQUFPLElBQUksQ0FBQ1QsS0FBSyxDQUFDRyxJQUFJLENBQUNPLFNBQVM7SUFDbEM7SUFFQTs7R0FFQyxHQUNELElBQUlDLFVBQTBCO1FBQzVCLE9BQU9iLEtBQUtjLFNBQVMsQ0FBQyxJQUFJLENBQUNaLEtBQUssQ0FBQ2EsUUFBUTtJQUMzQztJQUVBLElBQUlBLFdBQW1CO1FBQ3JCLE9BQU8sSUFBSSxDQUFDYixLQUFLLENBQUNhLFFBQVE7SUFDNUI7SUFFQSxJQUFJQyxXQUFtQjtRQUNyQixPQUFPLElBQUksQ0FBQ2QsS0FBSyxDQUFDYyxRQUFRO0lBQzVCO0lBRUEsTUFBTUMsV0FBNEI7UUFDaEMsSUFBSSxDQUFDLElBQUksQ0FBQ2QsT0FBTyxFQUFFO1lBQ2pCLElBQUksQ0FBQ0EsT0FBTyxHQUFHLE1BQU0sSUFBSSxDQUFDRCxLQUFLLENBQUNlLFFBQVE7UUFDMUM7UUFDQSxPQUFPLElBQUksQ0FBQ2QsT0FBTztJQUNyQjtJQUVBLE1BQU1lLE1BQXVCO1FBQzNCLE1BQU1DLFNBQVMsTUFBTSxJQUFJLENBQUNGLFFBQVE7UUFDbEMsT0FBT2xCLFdBQVcsT0FBT3FCLE1BQU0sQ0FBQ0QsUUFBUUUsTUFBTSxDQUFDO0lBQ2pEO0lBRUE7Ozs7Ozs7O0dBUUMsR0FDRCxNQUFNQyxXQUNKQyxHQUFXLEVBQ1hDLE9BQXFFLEVBQ3BEO1FBQ2pCLE1BQU1MLFNBQVMsTUFBTSxJQUFJLENBQUNGLFFBQVE7UUFFbEMsTUFBTSxJQUFJLENBQUNiLE9BQU8sQ0FBQ3FCLEdBQUcsQ0FBQ0YsS0FBS0osUUFBUTtZQUNsQ08sYUFBYUYsU0FBU0UsZUFBZSxJQUFJLENBQUNYLFFBQVE7WUFDbERZLFlBQVlILFNBQVNHO1FBQ3ZCO1FBRUEsT0FBTyxJQUFJLENBQUN2QixPQUFPLENBQUN3QixZQUFZLENBQUNMO0lBQ25DO0lBRUEsSUFBSU0sTUFBcUI7UUFDdkIsT0FBTyxJQUFJLENBQUMzQixLQUFLO0lBQ25CO0FBQ0YifQ==