glasswork 1.3.0 → 1.4.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/CHANGELOG.md +23 -1
- package/dist/auth/middleware.js +3 -1
- package/dist/auth/middleware.js.map +1 -1
- package/dist/core/bootstrap.d.ts.map +1 -1
- package/dist/core/bootstrap.js +110 -25
- package/dist/core/bootstrap.js.map +1 -1
- package/dist/core/contract.d.ts +37 -0
- package/dist/core/contract.d.ts.map +1 -0
- package/dist/core/contract.js +19 -0
- package/dist/core/contract.js.map +1 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/types.d.ts +12 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/email/email-service.d.ts +5 -2
- package/dist/email/email-service.d.ts.map +1 -1
- package/dist/email/email-service.js +8 -4
- package/dist/email/email-service.js.map +1 -1
- package/dist/email/index.d.ts +1 -1
- package/dist/email/index.d.ts.map +1 -1
- package/dist/email/index.js.map +1 -1
- package/dist/email/templated-email-service.d.ts.map +1 -1
- package/dist/email/templated-email-service.js +2 -1
- package/dist/email/templated-email-service.js.map +1 -1
- package/dist/email/types.d.ts +9 -4
- package/dist/email/types.d.ts.map +1 -1
- package/dist/email/webhooks/handler-factory.d.ts.map +1 -1
- package/dist/email/webhooks/handler-factory.js +7 -3
- package/dist/email/webhooks/handler-factory.js.map +1 -1
- package/dist/email/webhooks/notification-parser.d.ts +2 -1
- package/dist/email/webhooks/notification-parser.d.ts.map +1 -1
- package/dist/email/webhooks/notification-parser.js +1 -2
- package/dist/email/webhooks/notification-parser.js.map +1 -1
- package/dist/email/webhooks/signature-verification.d.ts.map +1 -1
- package/dist/email/webhooks/signature-verification.js +3 -3
- package/dist/email/webhooks/signature-verification.js.map +1 -1
- package/dist/email/webhooks/subscription-handler.d.ts +2 -0
- package/dist/email/webhooks/subscription-handler.d.ts.map +1 -1
- package/dist/email/webhooks/subscription-handler.js +5 -5
- package/dist/email/webhooks/subscription-handler.js.map +1 -1
- package/dist/email/webhooks/types.d.ts +2 -0
- package/dist/email/webhooks/types.d.ts.map +1 -1
- package/dist/http/error-handler.d.ts +3 -1
- package/dist/http/error-handler.d.ts.map +1 -1
- package/dist/http/error-handler.js +3 -4
- package/dist/http/error-handler.js.map +1 -1
- package/dist/http/index.d.ts +1 -0
- package/dist/http/index.d.ts.map +1 -1
- package/dist/http/index.js +1 -0
- package/dist/http/index.js.map +1 -1
- package/dist/http/route-context.d.ts.map +1 -1
- package/dist/http/route-context.js +7 -2
- package/dist/http/route-context.js.map +1 -1
- package/dist/http/route-helpers.d.ts.map +1 -1
- package/dist/http/route-helpers.js +1 -1
- package/dist/http/route-helpers.js.map +1 -1
- package/dist/http/route-openapi-context.d.ts +2 -0
- package/dist/http/route-openapi-context.d.ts.map +1 -1
- package/dist/http/route-openapi-context.js.map +1 -1
- package/dist/http/route-response.d.ts +2 -1
- package/dist/http/route-response.d.ts.map +1 -1
- package/dist/http/route-response.js +3 -4
- package/dist/http/route-response.js.map +1 -1
- package/dist/jobs/define-job.d.ts +10 -6
- package/dist/jobs/define-job.d.ts.map +1 -1
- package/dist/jobs/define-job.js +2 -0
- package/dist/jobs/define-job.js.map +1 -1
- package/dist/jobs/drivers/mock-driver.d.ts +3 -1
- package/dist/jobs/drivers/mock-driver.d.ts.map +1 -1
- package/dist/jobs/drivers/mock-driver.js +14 -30
- package/dist/jobs/drivers/mock-driver.js.map +1 -1
- package/dist/jobs/job-runner.d.ts +7 -0
- package/dist/jobs/job-runner.d.ts.map +1 -0
- package/dist/jobs/job-runner.js +38 -0
- package/dist/jobs/job-runner.js.map +1 -0
- package/dist/jobs/types.d.ts +15 -5
- package/dist/jobs/types.d.ts.map +1 -1
- package/dist/jobs/worker.d.ts.map +1 -1
- package/dist/jobs/worker.js +5 -11
- package/dist/jobs/worker.js.map +1 -1
- package/dist/list-query/client.d.ts +36 -0
- package/dist/list-query/client.d.ts.map +1 -0
- package/dist/list-query/client.js +74 -0
- package/dist/list-query/client.js.map +1 -0
- package/dist/list-query/parser.d.ts.map +1 -1
- package/dist/list-query/parser.js +8 -46
- package/dist/list-query/parser.js.map +1 -1
- package/dist/list-query/protocol.d.ts +14 -0
- package/dist/list-query/protocol.d.ts.map +1 -0
- package/dist/list-query/protocol.js +44 -0
- package/dist/list-query/protocol.js.map +1 -0
- package/dist/list-query/query-schema.d.ts.map +1 -1
- package/dist/list-query/query-schema.js +6 -5
- package/dist/list-query/query-schema.js.map +1 -1
- package/dist/list-query/types.d.ts +2 -5
- package/dist/list-query/types.d.ts.map +1 -1
- package/dist/middleware/rate-limit.d.ts.map +1 -1
- package/dist/middleware/rate-limit.js +1 -1
- package/dist/middleware/rate-limit.js.map +1 -1
- package/dist/observability/exception-tracking.d.ts +6 -1
- package/dist/observability/exception-tracking.d.ts.map +1 -1
- package/dist/observability/exception-tracking.js +29 -5
- package/dist/observability/exception-tracking.js.map +1 -1
- package/dist/observability/index.d.ts +1 -0
- package/dist/observability/index.d.ts.map +1 -1
- package/dist/observability/index.js +1 -0
- package/dist/observability/index.js.map +1 -1
- package/dist/observability/pino-logger.d.ts +3 -1
- package/dist/observability/pino-logger.d.ts.map +1 -1
- package/dist/observability/pino-logger.js +17 -11
- package/dist/observability/pino-logger.js.map +1 -1
- package/dist/observability/sanitizers.d.ts +14 -0
- package/dist/observability/sanitizers.d.ts.map +1 -0
- package/dist/observability/sanitizers.js +52 -0
- package/dist/observability/sanitizers.js.map +1 -0
- package/dist/openapi/drift.d.ts +6 -0
- package/dist/openapi/drift.d.ts.map +1 -0
- package/dist/openapi/drift.js +18 -0
- package/dist/openapi/drift.js.map +1 -0
- package/dist/openapi/openapi.d.ts +5 -0
- package/dist/openapi/openapi.d.ts.map +1 -1
- package/dist/openapi/openapi.js +5 -2
- package/dist/openapi/openapi.js.map +1 -1
- package/dist/uploads/guardduty.d.ts +20 -0
- package/dist/uploads/guardduty.d.ts.map +1 -0
- package/dist/uploads/guardduty.js +29 -0
- package/dist/uploads/guardduty.js.map +1 -0
- package/dist/uploads/index.d.ts +1 -1
- package/dist/uploads/index.d.ts.map +1 -1
- package/dist/uploads/index.js.map +1 -1
- package/dist/uploads/s3-key.d.ts +3 -0
- package/dist/uploads/s3-key.d.ts.map +1 -0
- package/dist/uploads/s3-key.js +5 -0
- package/dist/uploads/s3-key.js.map +1 -0
- package/dist/uploads/uploads.service.d.ts +27 -7
- package/dist/uploads/uploads.service.d.ts.map +1 -1
- package/dist/uploads/uploads.service.js +50 -14
- package/dist/uploads/uploads.service.js.map +1 -1
- package/package.json +11 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getRequestContext } from './request-context.js';
|
|
2
|
+
import { sanitizeLogMetadata } from './sanitizers.js';
|
|
2
3
|
/**
|
|
3
4
|
* Create a context-aware logger that automatically includes request context.
|
|
4
5
|
*
|
|
@@ -61,29 +62,34 @@ export function createContextAwarePinoLogger(options) {
|
|
|
61
62
|
let obj = { ...bindings };
|
|
62
63
|
// Handle meta arguments
|
|
63
64
|
for (const item of meta) {
|
|
64
|
-
if (item
|
|
65
|
+
if (item instanceof Error) {
|
|
66
|
+
const error = options.sanitizers?.error?.(item) ?? item;
|
|
67
|
+
obj.err = { name: error.name, message: error.message, stack: error.stack };
|
|
68
|
+
}
|
|
69
|
+
else if (item && typeof item === 'object' && !Array.isArray(item)) {
|
|
65
70
|
obj = { ...obj, ...item };
|
|
66
71
|
}
|
|
67
72
|
}
|
|
68
|
-
return obj;
|
|
73
|
+
return sanitizeLogMetadata(obj, options.sanitizers);
|
|
69
74
|
}
|
|
70
75
|
return {
|
|
71
76
|
debug(msg, ...meta) {
|
|
72
|
-
pino.debug(createLogObject(msg, meta), msg);
|
|
77
|
+
pino.debug(createLogObject(msg, meta), options.sanitizers?.message?.(msg) ?? msg);
|
|
73
78
|
},
|
|
74
79
|
info(msg, ...meta) {
|
|
75
|
-
pino.info(createLogObject(msg, meta), msg);
|
|
80
|
+
pino.info(createLogObject(msg, meta), options.sanitizers?.message?.(msg) ?? msg);
|
|
76
81
|
},
|
|
77
82
|
warn(msg, ...meta) {
|
|
78
|
-
pino.warn(createLogObject(msg, meta), msg);
|
|
83
|
+
pino.warn(createLogObject(msg, meta), options.sanitizers?.message?.(msg) ?? msg);
|
|
79
84
|
},
|
|
80
85
|
error(msg, ...meta) {
|
|
81
|
-
pino.error(createLogObject(msg, meta), msg);
|
|
86
|
+
pino.error(createLogObject(msg, meta), options.sanitizers?.message?.(msg) ?? msg);
|
|
82
87
|
},
|
|
83
88
|
child(bindings) {
|
|
84
89
|
return createContextAwarePinoLogger({
|
|
85
|
-
pino: pino.child(bindings),
|
|
90
|
+
pino: pino.child(sanitizeLogMetadata(bindings, options.sanitizers)),
|
|
86
91
|
service,
|
|
92
|
+
sanitizers: options.sanitizers,
|
|
87
93
|
});
|
|
88
94
|
},
|
|
89
95
|
};
|
|
@@ -109,7 +115,7 @@ export function createContextAwarePinoLogger(options) {
|
|
|
109
115
|
* app.use(createPinoHttpMiddleware(logger));
|
|
110
116
|
* ```
|
|
111
117
|
*/
|
|
112
|
-
export function createPinoHttpMiddleware(pino) {
|
|
118
|
+
export function createPinoHttpMiddleware(pino, sanitizers) {
|
|
113
119
|
return async (c, next) => {
|
|
114
120
|
const start = Date.now();
|
|
115
121
|
await next();
|
|
@@ -127,13 +133,13 @@ export function createPinoHttpMiddleware(pino) {
|
|
|
127
133
|
}
|
|
128
134
|
// Use appropriate log level based on status
|
|
129
135
|
if (c.res.status >= 500) {
|
|
130
|
-
pino.error(logObject, 'HTTP Request');
|
|
136
|
+
pino.error(sanitizeLogMetadata(logObject, sanitizers), 'HTTP Request');
|
|
131
137
|
}
|
|
132
138
|
else if (c.res.status >= 400) {
|
|
133
|
-
pino.warn(logObject, 'HTTP Request');
|
|
139
|
+
pino.warn(sanitizeLogMetadata(logObject, sanitizers), 'HTTP Request');
|
|
134
140
|
}
|
|
135
141
|
else {
|
|
136
|
-
pino.info(logObject, 'HTTP Request');
|
|
142
|
+
pino.info(sanitizeLogMetadata(logObject, sanitizers), 'HTTP Request');
|
|
137
143
|
}
|
|
138
144
|
};
|
|
139
145
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pino-logger.js","sourceRoot":"","sources":["../../src/observability/pino-logger.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"pino-logger.js","sourceRoot":"","sources":["../../src/observability/pino-logger.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAgC,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAkCpF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,SAAS,sBAAsB,CAC7B,GAA0C;IAE1C,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,CAAC;IACpB,MAAM,QAAQ,GAA4B,EAAE,CAAC;IAE7C,IAAI,GAAG,CAAC,SAAS;QAAE,QAAQ,CAAC,SAAS,GAAG,GAAG,CAAC,SAAS,CAAC;IACtD,IAAI,GAAG,CAAC,MAAM;QAAE,QAAQ,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;IAC7C,IAAI,GAAG,CAAC,WAAW;QAAE,QAAQ,CAAC,WAAW,GAAG,GAAG,CAAC,WAAW,CAAC;IAC5D,IAAI,GAAG,CAAC,eAAe;QAAE,QAAQ,CAAC,eAAe,GAAG,GAAG,CAAC,eAAe,CAAC;IACxE,IAAI,GAAG,CAAC,eAAe;QAAE,QAAQ,CAAC,eAAe,GAAG,IAAI,CAAC;IACzD,IAAI,GAAG,CAAC,QAAQ;QAAE,QAAQ,CAAC,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC;IACnD,IAAI,GAAG,CAAC,MAAM;QAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAEpD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,4BAA4B,CAAC,OAAgC;IAC3E,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAElC,SAAS,WAAW;QAClB,MAAM,QAAQ,GAAG,sBAAsB,CAAC,iBAAiB,EAAE,CAAC,CAAC;QAC7D,IAAI,OAAO,EAAE,CAAC;YACZ,QAAQ,CAAC,OAAO,GAAG,OAAO,CAAC;QAC7B,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,SAAS,eAAe,CAAC,IAAY,EAAE,IAAe;QACpD,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;QAC/B,IAAI,GAAG,GAA4B,EAAE,GAAG,QAAQ,EAAE,CAAC;QAEnD,wBAAwB;QACxB,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE,CAAC;YACxB,IAAI,IAAI,YAAY,KAAK,EAAE,CAAC;gBAC1B,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;gBACxD,GAAG,CAAC,GAAG,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;YAC7E,CAAC;iBAAM,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;gBACpE,GAAG,GAAG,EAAE,GAAG,GAAG,EAAE,GAAI,IAAgC,EAAE,CAAC;YACzD,CAAC;QACH,CAAC;QAED,OAAO,mBAAmB,CAAC,GAAG,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IACtD,CAAC;IAED,OAAO;QACL,KAAK,CAAC,GAAW,EAAE,GAAG,IAAe;YACnC,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,OAAO,CAAC,UAAU,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC;QACpF,CAAC;QACD,IAAI,CAAC,GAAW,EAAE,GAAG,IAAe;YAClC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,OAAO,CAAC,UAAU,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC;QACnF,CAAC;QACD,IAAI,CAAC,GAAW,EAAE,GAAG,IAAe;YAClC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,OAAO,CAAC,UAAU,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC;QACnF,CAAC;QACD,KAAK,CAAC,GAAW,EAAE,GAAG,IAAe;YACnC,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,OAAO,CAAC,UAAU,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC;QACpF,CAAC;QACD,KAAK,CAAC,QAAiC;YACrC,OAAO,4BAA4B,CAAC;gBAClC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,QAAQ,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;gBACnE,OAAO;gBACP,UAAU,EAAE,OAAO,CAAC,UAAU;aAC/B,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,wBAAwB,CACtC,IAAgB,EAChB,UAAoC;IAEpC,OAAO,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE;QACvB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAEzB,MAAM,IAAI,EAAE,CAAC;QAEb,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;QACpC,MAAM,GAAG,GAAG,iBAAiB,EAAE,CAAC;QAEhC,MAAM,SAAS,GAA4B;YACzC,SAAS,EAAE,GAAG,EAAE,SAAS;YACzB,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM;YACpB,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI;YAChB,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM;YACpB,QAAQ;SACT,CAAC;QAEF,IAAI,GAAG,EAAE,MAAM,EAAE,CAAC;YAChB,SAAS,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;QAChC,CAAC;QAED,4CAA4C;QAC5C,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;YACxB,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,SAAS,EAAE,UAAU,CAAC,EAAE,cAAc,CAAC,CAAC;QACzE,CAAC;aAAM,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;YAC/B,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,SAAS,EAAE,UAAU,CAAC,EAAE,cAAc,CAAC,CAAC;QACxE,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,SAAS,EAAE,UAAU,CAAC,EAAE,cAAc,CAAC,CAAC;QACxE,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,MAAM;IACtC,0CAA0C;IAC1C,UAAU,EAAE;QACV,KAAK,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;KAC7C;IACD,0BAA0B;IAC1B,SAAS,EAAE,SAAS;IACpB,wCAAwC;IACxC,IAAI,EAAE,SAAS;CAChB,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Logger } from '../utils/logger.js';
|
|
2
|
+
import type { ExceptionTracker } from './exception-tracking.js';
|
|
3
|
+
/** Explicit application policy applied before diagnostics reach a logger or tracker. */
|
|
4
|
+
export interface ObservabilitySanitizers {
|
|
5
|
+
path?: (path: string) => string;
|
|
6
|
+
message?: (message: string) => string;
|
|
7
|
+
error?: (error: Error) => Error;
|
|
8
|
+
metadata?: (metadata: Record<string, unknown>) => Record<string, unknown>;
|
|
9
|
+
}
|
|
10
|
+
export declare function sanitizeLogMetadata(metadata: Record<string, unknown>, sanitizers?: ObservabilitySanitizers): Record<string, unknown>;
|
|
11
|
+
/** Preserves the native logger and its children without registering global policy. */
|
|
12
|
+
export declare function createSanitizedLogger(logger: Logger, sanitizers?: ObservabilitySanitizers): Logger;
|
|
13
|
+
export declare function createSanitizedExceptionTracker(tracker: ExceptionTracker, sanitizers?: ObservabilitySanitizers): ExceptionTracker;
|
|
14
|
+
//# sourceMappingURL=sanitizers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitizers.d.ts","sourceRoot":"","sources":["../../src/observability/sanitizers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,wFAAwF;AACxF,MAAM,WAAW,uBAAuB;IACtC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAChC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IACtC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,KAAK,CAAC;IAChC,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC3E;AAED,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,UAAU,GAAE,uBAA4B,GACvC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMzB;AAED,sFAAsF;AACtF,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,EACd,UAAU,GAAE,uBAA4B,GACvC,MAAM,CAgCR;AAED,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,gBAAgB,EACzB,UAAU,GAAE,uBAA4B,GACvC,gBAAgB,CAmBlB"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export function sanitizeLogMetadata(metadata, sanitizers = {}) {
|
|
2
|
+
const result = { ...metadata };
|
|
3
|
+
if (typeof result.path === 'string' && sanitizers.path) {
|
|
4
|
+
result.path = sanitizers.path(result.path);
|
|
5
|
+
}
|
|
6
|
+
return sanitizers.metadata?.(result) ?? result;
|
|
7
|
+
}
|
|
8
|
+
/** Preserves the native logger and its children without registering global policy. */
|
|
9
|
+
export function createSanitizedLogger(logger, sanitizers = {}) {
|
|
10
|
+
const sanitizeValue = (value) => {
|
|
11
|
+
if (value instanceof Error)
|
|
12
|
+
return sanitizers.error?.(value) ?? value;
|
|
13
|
+
if (typeof value === 'string')
|
|
14
|
+
return sanitizers.message?.(value) ?? value;
|
|
15
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
16
|
+
return sanitizeLogMetadata(value, sanitizers);
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
};
|
|
20
|
+
const write = (level, message, values) => {
|
|
21
|
+
logger[level](sanitizers.message?.(message) ?? message, ...values.map(sanitizeValue));
|
|
22
|
+
};
|
|
23
|
+
return {
|
|
24
|
+
debug: (message, ...values) => write('debug', message, values),
|
|
25
|
+
info: (message, ...values) => write('info', message, values),
|
|
26
|
+
warn: (message, ...values) => write('warn', message, values),
|
|
27
|
+
error: (message, ...values) => write('error', message, values),
|
|
28
|
+
child: (bindings) => {
|
|
29
|
+
const safe = sanitizeLogMetadata(bindings, sanitizers);
|
|
30
|
+
const child = logger.child?.(safe) ?? {
|
|
31
|
+
debug: (message, ...values) => logger.debug(message, safe, ...values),
|
|
32
|
+
info: (message, ...values) => logger.info(message, safe, ...values),
|
|
33
|
+
warn: (message, ...values) => logger.warn(message, safe, ...values),
|
|
34
|
+
error: (message, ...values) => logger.error(message, safe, ...values),
|
|
35
|
+
};
|
|
36
|
+
return createSanitizedLogger(child, sanitizers);
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export function createSanitizedExceptionTracker(tracker, sanitizers = {}) {
|
|
41
|
+
return {
|
|
42
|
+
captureException: (error, context) => tracker.captureException(sanitizers.error?.(error) ?? error, context ? sanitizeLogMetadata(context, sanitizers) : undefined),
|
|
43
|
+
captureMessage: (message, level, context) => tracker.captureMessage(sanitizers.message?.(message) ?? message, level, context ? sanitizeLogMetadata(context, sanitizers) : undefined),
|
|
44
|
+
setUser: (user) => {
|
|
45
|
+
const safe = sanitizeLogMetadata(user, sanitizers);
|
|
46
|
+
if (typeof safe.id === 'string')
|
|
47
|
+
tracker.setUser({ ...safe, id: safe.id });
|
|
48
|
+
},
|
|
49
|
+
setContext: (key, data) => tracker.setContext(key, sanitizeLogMetadata(data, sanitizers)),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=sanitizers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitizers.js","sourceRoot":"","sources":["../../src/observability/sanitizers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare class OpenAPIDriftError extends Error {
|
|
2
|
+
constructor(filePath: string);
|
|
3
|
+
}
|
|
4
|
+
/** Compares JSON documents without changing the committed contract file. */
|
|
5
|
+
export declare function assertOpenAPIMatches(document: unknown, filePath: string): Promise<void>;
|
|
6
|
+
//# sourceMappingURL=drift.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drift.d.ts","sourceRoot":"","sources":["../../src/openapi/drift.ts"],"names":[],"mappings":"AAGA,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,YAAY,QAAQ,EAAE,MAAM,EAG3B;CACF;AAED,4EAA4E;AAC5E,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAO7F"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
3
|
+
export class OpenAPIDriftError extends Error {
|
|
4
|
+
constructor(filePath) {
|
|
5
|
+
super(`OpenAPI contract drift: regenerate and commit ${filePath}`);
|
|
6
|
+
this.name = 'OpenAPIDriftError';
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
/** Compares JSON documents without changing the committed contract file. */
|
|
10
|
+
export async function assertOpenAPIMatches(document, filePath) {
|
|
11
|
+
const expected = JSON.parse(await readFile(filePath, 'utf8'));
|
|
12
|
+
// Normalize to JSON so omitted undefined properties match the written artifact.
|
|
13
|
+
const actual = JSON.parse(JSON.stringify(document));
|
|
14
|
+
if (!isDeepStrictEqual(expected, actual)) {
|
|
15
|
+
throw new OpenAPIDriftError(filePath);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=drift.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drift.js","sourceRoot":"","sources":["../../src/openapi/drift.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAE9C,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IAC1C,YAAY,QAAgB;QAC1B,KAAK,CAAC,iDAAiD,QAAQ,EAAE,CAAC,CAAC;QACnE,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AAED,4EAA4E;AAC5E,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,QAAiB,EAAE,QAAgB;IAC5E,MAAM,QAAQ,GAAY,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;IACvE,gFAAgF;IAChF,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC7D,IAAI,CAAC,iBAAiB,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC;QACzC,MAAM,IAAI,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IACxC,CAAC;AACH,CAAC"}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { Hono } from 'hono';
|
|
2
|
+
import { generateSpecs } from 'hono-openapi';
|
|
2
3
|
import type { Environment, MiddlewareOptions, OpenAPIOptions, RateLimitOptions } from '../core/types.js';
|
|
4
|
+
import { type Logger } from '../utils/logger.js';
|
|
3
5
|
export interface ConfigureOpenAPIOptions {
|
|
6
|
+
logger?: Logger;
|
|
4
7
|
app: Hono;
|
|
5
8
|
environment: Environment;
|
|
6
9
|
openapi: OpenAPIOptions;
|
|
@@ -11,6 +14,8 @@ export interface ConfigureOpenAPIOptions {
|
|
|
11
14
|
* Result from configuring OpenAPI, includes optional write function.
|
|
12
15
|
*/
|
|
13
16
|
export interface ConfigureOpenAPIResult {
|
|
17
|
+
/** Generates the document from the same native Hono route registry. */
|
|
18
|
+
generateSpec?: () => Promise<Awaited<ReturnType<typeof generateSpecs>>>;
|
|
14
19
|
/**
|
|
15
20
|
* Write the OpenAPI spec to a file.
|
|
16
21
|
* Call this after all routes have been registered.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openapi.d.ts","sourceRoot":"","sources":["../../src/openapi/openapi.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAqB,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"openapi.d.ts","sourceRoot":"","sources":["../../src/openapi/openapi.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAqB,MAAM,MAAM,CAAC;AACpD,OAAO,EAAE,aAAa,EAAuB,MAAM,cAAc,CAAC;AAElE,OAAO,KAAK,EACV,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EACjB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAG/D,MAAM,WAAW,uBAAuB;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,IAAI,CAAC;IACV,WAAW,EAAE,WAAW,CAAC;IACzB,OAAO,EAAE,cAAc,CAAC;IACxB,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,UAAU,CAAC,EAAE,iBAAiB,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,uEAAuE;IACvE,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC,CAAC,CAAC;IACxE;;;;;;;;;;;;;;OAcG;IACH,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACjC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,sBAAsB,CAiEzF"}
|
package/dist/openapi/openapi.js
CHANGED
|
@@ -32,7 +32,7 @@ export function configureOpenAPI(options) {
|
|
|
32
32
|
}
|
|
33
33
|
// Use 'error' level so errors are always logged (even in test mode)
|
|
34
34
|
// This ensures OpenAPI spec writing errors are visible
|
|
35
|
-
const logger = createLogger('Glasswork:OpenAPI', 'error');
|
|
35
|
+
const logger = options.logger ?? createLogger('Glasswork:OpenAPI', 'error');
|
|
36
36
|
const shouldServeSpecs = openapi.serveSpecs ?? environment === 'development';
|
|
37
37
|
const shouldServeUI = openapi.serveUI ?? environment === 'development';
|
|
38
38
|
// Filter components based on enabled features (only include headers that are actually used)
|
|
@@ -69,7 +69,10 @@ export function configureOpenAPI(options) {
|
|
|
69
69
|
logger.info(`OpenAPI spec written to ${filePath}`);
|
|
70
70
|
};
|
|
71
71
|
}
|
|
72
|
-
return {
|
|
72
|
+
return {
|
|
73
|
+
writeSpec,
|
|
74
|
+
generateSpec: () => generateSpecs(app, { documentation: mergedDocumentation }),
|
|
75
|
+
};
|
|
73
76
|
}
|
|
74
77
|
/**
|
|
75
78
|
* Filter component headers based on enabled features.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openapi.js","sourceRoot":"","sources":["../../src/openapi/openapi.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAQlE,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"openapi.js","sourceRoot":"","sources":["../../src/openapi/openapi.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type GetObjectTaggingCommandInput, type GetObjectTaggingCommandOutput, type S3Client } from '@aws-sdk/client-s3';
|
|
2
|
+
declare const GUARD_DUTY_SCAN_STATUSES: readonly ['NO_THREATS_FOUND', 'THREATS_FOUND', 'UNSUPPORTED', 'ACCESS_DENIED', 'FAILED'];
|
|
3
|
+
export type GuardDutyScanStatus = (typeof GUARD_DUTY_SCAN_STATUSES)[number] | 'pending' | 'unknown';
|
|
4
|
+
export interface GuardDutyScanResult {
|
|
5
|
+
/** Native GuardDuty status, or pending/unknown when no recognized result is available. */
|
|
6
|
+
readonly status: GuardDutyScanStatus;
|
|
7
|
+
/** Original S3 response, including the object version and unmodified tags. */
|
|
8
|
+
readonly response: GetObjectTaggingCommandOutput;
|
|
9
|
+
}
|
|
10
|
+
/** Reads GuardDuty's scan tag without granting access or deciding application policy. */
|
|
11
|
+
export declare class GuardDutyMalwareScanner {
|
|
12
|
+
private readonly config;
|
|
13
|
+
constructor(config: {
|
|
14
|
+
client: S3Client;
|
|
15
|
+
bucketName: string;
|
|
16
|
+
});
|
|
17
|
+
getScanResult(filePath: string, options?: Omit<GetObjectTaggingCommandInput, 'Bucket' | 'Key'>): Promise<GuardDutyScanResult>;
|
|
18
|
+
}
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=guardduty.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guardduty.d.ts","sourceRoot":"","sources":["../../src/uploads/guardduty.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,4BAA4B,EACjC,KAAK,6BAA6B,EAClC,KAAK,QAAQ,EACd,MAAM,oBAAoB,CAAC;AAG5B,QAAA,MAAM,wBAAwB,YAC5B,kBAAkB,EAClB,eAAe,EACf,aAAa,EACb,eAAe,EACf,QAAQ,CACA,CAAC;AAEX,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,wBAAwB,CAAC,CAAC,MAAM,CAAC,GAAG,SAAS,GAAG,SAAS,CAAC;AAEpG,MAAM,WAAW,mBAAmB;IAClC,0FAA0F;IAC1F,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACrC,8EAA8E;IAC9E,QAAQ,CAAC,QAAQ,EAAE,6BAA6B,CAAC;CAClD;AAED,yFAAyF;AACzF,qBAAa,uBAAuB;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAAnC,YAA6B,MAAM,EAAE;QAAE,MAAM,EAAE,QAAQ,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,EAAI;IAE3E,aAAa,CACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,IAAI,CAAC,4BAA4B,EAAE,QAAQ,GAAG,KAAK,CAAM,GACjE,OAAO,CAAC,mBAAmB,CAAC,CAc9B;CACF"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { GetObjectTaggingCommand, } from '@aws-sdk/client-s3';
|
|
2
|
+
import { normalizeS3Key } from './s3-key.js';
|
|
3
|
+
const GUARD_DUTY_SCAN_STATUSES = [
|
|
4
|
+
'NO_THREATS_FOUND',
|
|
5
|
+
'THREATS_FOUND',
|
|
6
|
+
'UNSUPPORTED',
|
|
7
|
+
'ACCESS_DENIED',
|
|
8
|
+
'FAILED',
|
|
9
|
+
];
|
|
10
|
+
/** Reads GuardDuty's scan tag without granting access or deciding application policy. */
|
|
11
|
+
export class GuardDutyMalwareScanner {
|
|
12
|
+
config;
|
|
13
|
+
constructor(config) {
|
|
14
|
+
this.config = config;
|
|
15
|
+
}
|
|
16
|
+
async getScanResult(filePath, options = {}) {
|
|
17
|
+
const response = await this.config.client.send(new GetObjectTaggingCommand({
|
|
18
|
+
...options,
|
|
19
|
+
Bucket: this.config.bucketName,
|
|
20
|
+
Key: normalizeS3Key(filePath),
|
|
21
|
+
}));
|
|
22
|
+
const value = response.TagSet?.find((tag) => tag.Key === 'GuardDutyMalwareScanStatus')?.Value;
|
|
23
|
+
const status = value === undefined
|
|
24
|
+
? 'pending'
|
|
25
|
+
: (GUARD_DUTY_SCAN_STATUSES.find((candidate) => candidate === value) ?? 'unknown');
|
|
26
|
+
return { status, response };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=guardduty.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guardduty.js","sourceRoot":"","sources":["../../src/uploads/guardduty.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,GAIxB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,wBAAwB,GAAG;IAC/B,kBAAkB;IAClB,eAAe;IACf,aAAa;IACb,eAAe;IACf,QAAQ;CACA,CAAC;AAWX,yFAAyF;AACzF,MAAM,OAAO,uBAAuB;IACL,MAAM;IAAnC,YAA6B,MAAgD;sBAAhD,MAAM;IAA6C,CAAC;IAEjF,KAAK,CAAC,aAAa,CACjB,QAAgB,EAChB,OAAO,GAAyD,EAAE;QAElE,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAC5C,IAAI,uBAAuB,CAAC;YAC1B,GAAG,OAAO;YACV,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU;YAC9B,GAAG,EAAE,cAAc,CAAC,QAAQ,CAAC;SAC9B,CAAC,CACH,CAAC;QACF,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,KAAK,4BAA4B,CAAC,EAAE,KAAK,CAAC;QAC9F,MAAM,MAAM,GACV,KAAK,KAAK,SAAS;YACjB,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,CAAC,wBAAwB,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,KAAK,KAAK,CAAC,IAAI,SAAS,CAAC,CAAC;QACvF,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IAC9B,CAAC;CACF"}
|
package/dist/uploads/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type { UploadPathConfig } from './file-upload.js';
|
|
2
2
|
export { assertUploadPathOwnership, createUploadConfig } from './file-upload.js';
|
|
3
|
-
export type { SignedDownloadUrlResponse, SignedUrlResponse, StreamFileResult, UploadFileConfig, UploadsServiceConfig, } from './uploads.service.js';
|
|
3
|
+
export type { FinalizeUploadOptions, SignedDownloadUrlResponse, SignedUrlResponse, StreamFileResult, UploadFileConfig, UploadsServiceConfig, } from './uploads.service.js';
|
|
4
4
|
export { UploadsService } from './uploads.service.js';
|
|
5
5
|
export { fileNameWithExtension, sanitizedFileName } from './validators.js';
|
|
6
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/uploads/index.ts"],"names":[],"mappings":"AAEA,YAAY,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEzD,OAAO,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACjF,YAAY,EACV,yBAAyB,EACzB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGtD,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/uploads/index.ts"],"names":[],"mappings":"AAEA,YAAY,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEzD,OAAO,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACjF,YAAY,EACV,qBAAqB,EACrB,yBAAyB,EACzB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGtD,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/uploads/index.ts"],"names":[],"mappings":"AAAA,UAAU;AAGV,YAAY;AACZ,OAAO,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/uploads/index.ts"],"names":[],"mappings":"AAAA,UAAU;AAGV,YAAY;AACZ,OAAO,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AASjF,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,aAAa;AACb,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"s3-key.d.ts","sourceRoot":"","sources":["../../src/uploads/s3-key.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"s3-key.js","sourceRoot":"","sources":["../../src/uploads/s3-key.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AACvD,CAAC"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { Readable } from 'node:stream';
|
|
2
|
+
import { type CopyObjectCommandInput, type DeleteObjectCommandInput, type GetObjectCommandInput, type HeadObjectCommandInput, type PutObjectCommandInput, S3Client } from '@aws-sdk/client-s3';
|
|
3
|
+
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
2
4
|
export interface UploadFileConfig {
|
|
3
5
|
/** Directory path for the file (e.g., 'uploads/avatars/user123/abc123') */
|
|
4
6
|
readonly dir: string;
|
|
@@ -28,6 +30,8 @@ export interface StreamFileResult {
|
|
|
28
30
|
readonly contentLength: number | undefined;
|
|
29
31
|
}
|
|
30
32
|
export interface UploadsServiceConfig {
|
|
33
|
+
/** Native S3 client for custom endpoints, credentials, and middleware. */
|
|
34
|
+
readonly client?: S3Client;
|
|
31
35
|
/** AWS region (e.g., 'eu-west-1') */
|
|
32
36
|
readonly region: string;
|
|
33
37
|
/** S3 bucket name */
|
|
@@ -35,6 +39,14 @@ export interface UploadsServiceConfig {
|
|
|
35
39
|
/** Presigned URL expiration in seconds (default: 3600) */
|
|
36
40
|
readonly urlExpiration?: number;
|
|
37
41
|
}
|
|
42
|
+
export interface FinalizeUploadOptions {
|
|
43
|
+
/** Copy this exact source version instead of the current object. */
|
|
44
|
+
readonly sourceVersionId?: string;
|
|
45
|
+
/** Delete the source after copying (default: true). Keep false for quarantine workflows. */
|
|
46
|
+
readonly deleteSource?: boolean;
|
|
47
|
+
/** Native copy options; the service controls the source, destination, and inspected ETag. */
|
|
48
|
+
readonly command?: Omit<CopyObjectCommandInput, 'Bucket' | 'Key' | 'CopySource' | 'CopySourceIfMatch'>;
|
|
49
|
+
}
|
|
38
50
|
/**
|
|
39
51
|
* Service for S3 file operations including presigned URL generation and streaming.
|
|
40
52
|
* Framework-agnostic - can be used anywhere TypeScript runs.
|
|
@@ -61,7 +73,7 @@ export interface UploadsServiceConfig {
|
|
|
61
73
|
* ```
|
|
62
74
|
*/
|
|
63
75
|
export declare class UploadsService {
|
|
64
|
-
|
|
76
|
+
readonly client: S3Client;
|
|
65
77
|
private readonly bucketName;
|
|
66
78
|
private readonly urlExpiration;
|
|
67
79
|
constructor(config: UploadsServiceConfig);
|
|
@@ -69,7 +81,12 @@ export declare class UploadsService {
|
|
|
69
81
|
* Generate a presigned URL for uploading a file to S3.
|
|
70
82
|
* The URL allows direct upload from the client without going through Lambda.
|
|
71
83
|
*/
|
|
72
|
-
getSignedUploadUrl(fileConfig: UploadFileConfig
|
|
84
|
+
getSignedUploadUrl(fileConfig: UploadFileConfig, options?: {
|
|
85
|
+
expiresIn?: number;
|
|
86
|
+
command?: Omit<PutObjectCommandInput, 'Bucket' | 'Key'>;
|
|
87
|
+
/** Native AWS signing options; expiration is controlled by expiresIn above. */
|
|
88
|
+
presigner?: Omit<NonNullable<Parameters<typeof getSignedUrl>[2]>, 'expiresIn'>;
|
|
89
|
+
}): Promise<SignedUrlResponse>;
|
|
73
90
|
/**
|
|
74
91
|
* Stream a file from S3 for private file access.
|
|
75
92
|
* Returns a readable stream that can be piped to the HTTP response.
|
|
@@ -85,14 +102,17 @@ export declare class UploadsService {
|
|
|
85
102
|
* @param filePath - Relative path to the file (e.g., '/uploads/docs/file.pdf')
|
|
86
103
|
* @param expiresIn - Optional expiration in seconds (default: service urlExpiration)
|
|
87
104
|
*/
|
|
88
|
-
getSignedDownloadUrl(filePath: string, expiresIn?: number): Promise<SignedDownloadUrlResponse>;
|
|
105
|
+
getSignedDownloadUrl(filePath: string, expiresIn?: number, options?: Omit<GetObjectCommandInput, 'Bucket' | 'Key'>): Promise<SignedDownloadUrlResponse>;
|
|
89
106
|
/**
|
|
90
107
|
* Delete a file from S3 by path.
|
|
91
108
|
*/
|
|
92
|
-
deleteFile(filePath: string): Promise<void>;
|
|
93
|
-
/**
|
|
94
|
-
|
|
109
|
+
deleteFile(filePath: string, options?: Omit<DeleteObjectCommandInput, 'Bucket' | 'Key'>): Promise<void>;
|
|
110
|
+
/** Native object metadata; the application evaluates type, size, and completeness. */
|
|
111
|
+
inspectFile(filePath: string, options?: Omit<HeadObjectCommandInput, 'Bucket' | 'Key'>): Promise<import("@aws-sdk/client-s3").HeadObjectCommandOutput>;
|
|
112
|
+
/** Copies the inspected file and returns the native AWS result.
|
|
113
|
+
* AWS errors remain native. A failed copy never deletes the source.
|
|
114
|
+
* This is not a database transaction; the application owns compensation policy.
|
|
95
115
|
*/
|
|
96
|
-
|
|
116
|
+
finalizeUpload(sourcePath: string, targetPath: string, inspectedETag: string, options?: FinalizeUploadOptions): Promise<import("@aws-sdk/client-s3").CopyObjectCommandOutput>;
|
|
97
117
|
}
|
|
98
118
|
//# sourceMappingURL=uploads.service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uploads.service.d.ts","sourceRoot":"","sources":["../../src/uploads/uploads.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"uploads.service.d.ts","sourceRoot":"","sources":["../../src/uploads/uploads.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { DeleteObjectCommand, GetObjectCommand, PutObjectCommand, S3Client, S3ServiceException, } from '@aws-sdk/client-s3';
|
|
1
|
+
import { CopyObjectCommand, DeleteObjectCommand, GetObjectCommand, HeadObjectCommand, PutObjectCommand, S3Client, S3ServiceException, } from '@aws-sdk/client-s3';
|
|
2
2
|
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
3
3
|
import { NotFoundException } from '../http/errors.js';
|
|
4
|
+
import { normalizeS3Key } from './s3-key.js';
|
|
4
5
|
// ============================================================================
|
|
5
6
|
// Constants
|
|
6
7
|
// ============================================================================
|
|
@@ -39,7 +40,7 @@ export class UploadsService {
|
|
|
39
40
|
bucketName;
|
|
40
41
|
urlExpiration;
|
|
41
42
|
constructor(config) {
|
|
42
|
-
this.client = new S3Client({ region: config.region });
|
|
43
|
+
this.client = config.client ?? new S3Client({ region: config.region });
|
|
43
44
|
this.bucketName = config.bucketName;
|
|
44
45
|
this.urlExpiration = config.urlExpiration ?? DEFAULT_URL_EXPIRATION;
|
|
45
46
|
}
|
|
@@ -47,19 +48,22 @@ export class UploadsService {
|
|
|
47
48
|
* Generate a presigned URL for uploading a file to S3.
|
|
48
49
|
* The URL allows direct upload from the client without going through Lambda.
|
|
49
50
|
*/
|
|
50
|
-
async getSignedUploadUrl(fileConfig) {
|
|
51
|
+
async getSignedUploadUrl(fileConfig, options = {}) {
|
|
51
52
|
const filePath = `${fileConfig.dir}/${fileConfig.fileName}`;
|
|
53
|
+
const expiration = options.expiresIn ?? this.urlExpiration;
|
|
52
54
|
const command = new PutObjectCommand({
|
|
55
|
+
...options.command,
|
|
53
56
|
Bucket: this.bucketName,
|
|
54
57
|
Key: filePath,
|
|
55
58
|
});
|
|
56
59
|
const url = await getSignedUrl(this.client, command, {
|
|
57
|
-
|
|
60
|
+
...options.presigner,
|
|
61
|
+
expiresIn: expiration,
|
|
58
62
|
});
|
|
59
63
|
return {
|
|
60
64
|
uploadUrl: url,
|
|
61
65
|
path: `/${filePath}`,
|
|
62
|
-
expiresAt: new Date(Date.now() +
|
|
66
|
+
expiresAt: new Date(Date.now() + expiration * MILLISECONDS_PER_SECOND),
|
|
63
67
|
};
|
|
64
68
|
}
|
|
65
69
|
/**
|
|
@@ -71,7 +75,7 @@ export class UploadsService {
|
|
|
71
75
|
*/
|
|
72
76
|
async streamFile(filePath) {
|
|
73
77
|
try {
|
|
74
|
-
const key =
|
|
78
|
+
const key = normalizeS3Key(filePath);
|
|
75
79
|
const command = new GetObjectCommand({
|
|
76
80
|
Bucket: this.bucketName,
|
|
77
81
|
Key: key,
|
|
@@ -98,10 +102,11 @@ export class UploadsService {
|
|
|
98
102
|
* @param filePath - Relative path to the file (e.g., '/uploads/docs/file.pdf')
|
|
99
103
|
* @param expiresIn - Optional expiration in seconds (default: service urlExpiration)
|
|
100
104
|
*/
|
|
101
|
-
async getSignedDownloadUrl(filePath, expiresIn) {
|
|
102
|
-
const key =
|
|
105
|
+
async getSignedDownloadUrl(filePath, expiresIn, options) {
|
|
106
|
+
const key = normalizeS3Key(filePath);
|
|
103
107
|
const expiration = expiresIn ?? this.urlExpiration;
|
|
104
108
|
const command = new GetObjectCommand({
|
|
109
|
+
...options,
|
|
105
110
|
Bucket: this.bucketName,
|
|
106
111
|
Key: key,
|
|
107
112
|
});
|
|
@@ -116,19 +121,50 @@ export class UploadsService {
|
|
|
116
121
|
/**
|
|
117
122
|
* Delete a file from S3 by path.
|
|
118
123
|
*/
|
|
119
|
-
async deleteFile(filePath) {
|
|
120
|
-
const key =
|
|
124
|
+
async deleteFile(filePath, options = {}) {
|
|
125
|
+
const key = normalizeS3Key(filePath);
|
|
121
126
|
const command = new DeleteObjectCommand({
|
|
127
|
+
...options,
|
|
122
128
|
Bucket: this.bucketName,
|
|
123
129
|
Key: key,
|
|
124
130
|
});
|
|
125
131
|
await this.client.send(command);
|
|
126
132
|
}
|
|
127
|
-
/**
|
|
128
|
-
|
|
133
|
+
/** Native object metadata; the application evaluates type, size, and completeness. */
|
|
134
|
+
async inspectFile(filePath, options = {}) {
|
|
135
|
+
return this.client.send(new HeadObjectCommand({
|
|
136
|
+
...options,
|
|
137
|
+
Bucket: this.bucketName,
|
|
138
|
+
Key: normalizeS3Key(filePath),
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
141
|
+
/** Copies the inspected file and returns the native AWS result.
|
|
142
|
+
* AWS errors remain native. A failed copy never deletes the source.
|
|
143
|
+
* This is not a database transaction; the application owns compensation policy.
|
|
129
144
|
*/
|
|
130
|
-
|
|
131
|
-
|
|
145
|
+
async finalizeUpload(sourcePath, targetPath, inspectedETag, options = {}) {
|
|
146
|
+
const source = normalizeS3Key(sourcePath);
|
|
147
|
+
const target = normalizeS3Key(targetPath);
|
|
148
|
+
if (!inspectedETag || source === target) {
|
|
149
|
+
throw new Error('Finalization requires an ETag and a distinct destination');
|
|
150
|
+
}
|
|
151
|
+
if (options.sourceVersionId === '') {
|
|
152
|
+
throw new Error('A source version ID cannot be empty');
|
|
153
|
+
}
|
|
154
|
+
const copySource = `${encodeURIComponent(this.bucketName)}/${source.split('/').map(encodeURIComponent).join('/')}`;
|
|
155
|
+
const copied = await this.client.send(new CopyObjectCommand({
|
|
156
|
+
...options.command,
|
|
157
|
+
Bucket: this.bucketName,
|
|
158
|
+
Key: target,
|
|
159
|
+
CopySource: options.sourceVersionId === undefined
|
|
160
|
+
? copySource
|
|
161
|
+
: `${copySource}?versionId=${encodeURIComponent(options.sourceVersionId)}`,
|
|
162
|
+
CopySourceIfMatch: inspectedETag,
|
|
163
|
+
}));
|
|
164
|
+
if (options.deleteSource !== false) {
|
|
165
|
+
await this.deleteFile(source, { VersionId: options.sourceVersionId });
|
|
166
|
+
}
|
|
167
|
+
return copied;
|
|
132
168
|
}
|
|
133
169
|
}
|
|
134
170
|
//# sourceMappingURL=uploads.service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uploads.service.js","sourceRoot":"","sources":["../../src/uploads/uploads.service.ts"],"names":[],"mappings":"AACA,OAAO,EACL,mBAAmB,
|
|
1
|
+
{"version":3,"file":"uploads.service.js","sourceRoot":"","sources":["../../src/uploads/uploads.service.ts"],"names":[],"mappings":"AACA,OAAO,EACL,iBAAiB,EAEjB,mBAAmB,EAEnB,gBAAgB,EAEhB,iBAAiB,EAEjB,gBAAgB,EAEhB,QAAQ,EACR,kBAAkB,GACnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AA6D7C,+EAA+E;AAC/E,YAAY;AACZ,+EAA+E;AAE/E,MAAM,sBAAsB,GAAG,IAAI,CAAC,CAAC,SAAS;AAC9C,MAAM,uBAAuB,GAAG,IAAI,CAAC;AAErC,+EAA+E;AAC/E,UAAU;AACV,+EAA+E;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,OAAO,cAAc;IAChB,MAAM,CAAW;IACT,UAAU,CAAS;IACnB,aAAa,CAAS;IAEvC,YAAY,MAA4B;QACtC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,IAAI,QAAQ,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QACvE,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;QACpC,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC,aAAa,IAAI,sBAAsB,CAAC;IACtE,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,kBAAkB,CACtB,UAA4B,EAC5B,OAAO,GAKH,EAAE;QAEN,MAAM,QAAQ,GAAG,GAAG,UAAU,CAAC,GAAG,IAAI,UAAU,CAAC,QAAQ,EAAE,CAAC;QAC5D,MAAM,UAAU,GAAG,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC,aAAa,CAAC;QAC3D,MAAM,OAAO,GAAG,IAAI,gBAAgB,CAAC;YACnC,GAAG,OAAO,CAAC,OAAO;YAClB,MAAM,EAAE,IAAI,CAAC,UAAU;YACvB,GAAG,EAAE,QAAQ;SACd,CAAC,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE;YACnD,GAAG,OAAO,CAAC,SAAS;YACpB,SAAS,EAAE,UAAU;SACtB,CAAC,CAAC;QACH,OAAO;YACL,SAAS,EAAE,GAAG;YACd,IAAI,EAAE,IAAI,QAAQ,EAAE;YACpB,SAAS,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,UAAU,GAAG,uBAAuB,CAAC;SACvE,CAAC;IACJ,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,UAAU,CAAC,QAAgB;QAC/B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;YACrC,MAAM,OAAO,GAAG,IAAI,gBAAgB,CAAC;gBACnC,MAAM,EAAE,IAAI,CAAC,UAAU;gBACvB,GAAG,EAAE,GAAG;aACT,CAAC,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACjD,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAgB,CAAC;YACvC,OAAO;gBACL,IAAI;gBACJ,WAAW,EAAE,QAAQ,CAAC,WAAW;gBACjC,aAAa,EAAE,QAAQ,CAAC,aAAa;aACtC,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,kBAAkB,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;gBACtE,MAAM,IAAI,iBAAiB,CAAC,gBAAgB,CAAC,CAAC;YAChD,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,oBAAoB,CACxB,QAAgB,EAChB,SAAkB,EAClB,OAAuD;QAEvD,MAAM,GAAG,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;QACrC,MAAM,UAAU,GAAG,SAAS,IAAI,IAAI,CAAC,aAAa,CAAC;QACnD,MAAM,OAAO,GAAG,IAAI,gBAAgB,CAAC;YACnC,GAAG,OAAO;YACV,MAAM,EAAE,IAAI,CAAC,UAAU;YACvB,GAAG,EAAE,GAAG;SACT,CAAC,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE;YACnD,SAAS,EAAE,UAAU;SACtB,CAAC,CAAC;QACH,OAAO;YACL,WAAW,EAAE,GAAG;YAChB,SAAS,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,UAAU,GAAG,uBAAuB,CAAC;SACvE,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU,CACd,QAAgB,EAChB,OAAO,GAAqD,EAAE;QAE9D,MAAM,GAAG,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;QACrC,MAAM,OAAO,GAAG,IAAI,mBAAmB,CAAC;YACtC,GAAG,OAAO;YACV,MAAM,EAAE,IAAI,CAAC,UAAU;YACvB,GAAG,EAAE,GAAG;SACT,CAAC,CAAC;QACH,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC;IAED,sFAAsF;IACtF,KAAK,CAAC,WAAW,CACf,QAAgB,EAChB,OAAO,GAAmD,EAAE;QAE5D,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CACrB,IAAI,iBAAiB,CAAC;YACpB,GAAG,OAAO;YACV,MAAM,EAAE,IAAI,CAAC,UAAU;YACvB,GAAG,EAAE,cAAc,CAAC,QAAQ,CAAC;SAC9B,CAAC,CACH,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,cAAc,CAClB,UAAkB,EAClB,UAAkB,EAClB,aAAqB,EACrB,OAAO,GAA0B,EAAE;QAEnC,MAAM,MAAM,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;QAC1C,IAAI,CAAC,aAAa,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;YACxC,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;QAC9E,CAAC;QACD,IAAI,OAAO,CAAC,eAAe,KAAK,EAAE,EAAE,CAAC;YACnC,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACzD,CAAC;QACD,MAAM,UAAU,GAAG,GAAG,kBAAkB,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACnH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CACnC,IAAI,iBAAiB,CAAC;YACpB,GAAG,OAAO,CAAC,OAAO;YAClB,MAAM,EAAE,IAAI,CAAC,UAAU;YACvB,GAAG,EAAE,MAAM;YACX,UAAU,EACR,OAAO,CAAC,eAAe,KAAK,SAAS;gBACnC,CAAC,CAAC,UAAU;gBACZ,CAAC,CAAC,GAAG,UAAU,cAAc,kBAAkB,CAAC,OAAO,CAAC,eAAe,CAAC,EAAE;YAC9E,iBAAiB,EAAE,aAAa;SACjC,CAAC,CACH,CAAC;QACF,IAAI,OAAO,CAAC,YAAY,KAAK,KAAK,EAAE,CAAC;YACnC,MAAM,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;QACxE,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;CACF"}
|