@geekmidas/constructs 9.0.2 → 9.1.0
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 +41 -0
- package/dist/{AWSScheduledFunction-Bn99MceY.d.cts → AWSScheduledFunction-BHHlWlfV.d.cts} +2 -2
- package/dist/{AWSScheduledFunction-ysaCsdWx.d.mts.map → AWSScheduledFunction-BHHlWlfV.d.cts.map} +1 -1
- package/dist/{AWSScheduledFunction-ysaCsdWx.d.mts → AWSScheduledFunction-BzGCUbkk.d.mts} +2 -2
- package/dist/{AWSScheduledFunction-Bn99MceY.d.cts.map → AWSScheduledFunction-BzGCUbkk.d.mts.map} +1 -1
- package/dist/{AWSScheduledFunction-DbkDr8CA.cjs → AWSScheduledFunction-C42adp8A.cjs} +16 -5
- package/dist/AWSScheduledFunction-C42adp8A.cjs.map +1 -0
- package/dist/{AWSScheduledFunction-CufMMhGB.mjs → AWSScheduledFunction-KP6lqbsW.mjs} +15 -4
- package/dist/AWSScheduledFunction-KP6lqbsW.mjs.map +1 -0
- package/dist/{Construct-r0U2zBwW.mjs → Construct-BWF5HxaM.mjs} +5 -2
- package/dist/Construct-BWF5HxaM.mjs.map +1 -0
- package/dist/{Construct-_1wX7p_Y.d.cts → Construct-JlyQkBkv.d.cts} +39 -4
- package/dist/Construct-JlyQkBkv.d.cts.map +1 -0
- package/dist/{Construct-vXMZJgRp.cjs → Construct-TGIM_E4o.cjs} +5 -2
- package/dist/Construct-TGIM_E4o.cjs.map +1 -0
- package/dist/{Construct-DCMuYOox.d.mts → Construct-pxwC6fDD.d.mts} +39 -4
- package/dist/Construct-pxwC6fDD.d.mts.map +1 -0
- package/dist/{Endpoint-CLPSIl6S.d.mts → Endpoint-B0P_pEMr.d.mts} +3 -3
- package/dist/{Endpoint-CLPSIl6S.d.mts.map → Endpoint-B0P_pEMr.d.mts.map} +1 -1
- package/dist/{Endpoint-DgfRslct.d.cts → Endpoint-Cwjhc1Hl.d.cts} +2 -2
- package/dist/Endpoint-Cwjhc1Hl.d.cts.map +1 -0
- package/dist/{HonoEndpointAdaptor-DJDT3PMx.mjs → HonoEndpointAdaptor-BMVwx2cz.mjs} +18 -11
- package/dist/HonoEndpointAdaptor-BMVwx2cz.mjs.map +1 -0
- package/dist/{HonoEndpointAdaptor-CkJENANP.cjs → HonoEndpointAdaptor-DPDsDi-D.cjs} +19 -12
- package/dist/HonoEndpointAdaptor-DPDsDi-D.cjs.map +1 -0
- package/dist/{Queue-HBDf0fqz.d.mts → Queue-BL68oDJ-.d.mts} +2 -2
- package/dist/{Queue-DaxjwmOQ.d.cts.map → Queue-BL68oDJ-.d.mts.map} +1 -1
- package/dist/{Queue-DaxjwmOQ.d.cts → Queue-DxJ6btRE.d.cts} +2 -2
- package/dist/{Queue-HBDf0fqz.d.mts.map → Queue-DxJ6btRE.d.cts.map} +1 -1
- package/dist/{Subscriber-B69ngPwl.d.cts → Subscriber-BpVhcGhX.d.cts} +2 -2
- package/dist/{Subscriber-B69ngPwl.d.cts.map → Subscriber-BpVhcGhX.d.cts.map} +1 -1
- package/dist/{Subscriber-D8Xm-86W.d.mts → Subscriber-DF6xsBAJ.d.mts} +2 -2
- package/dist/{Subscriber-D8Xm-86W.d.mts.map → Subscriber-DF6xsBAJ.d.mts.map} +1 -1
- package/dist/{Topic-DT10AqHk.d.mts → Topic-DJXoDeMP.d.cts} +3 -3
- package/dist/{Topic-C5ZMfveW.d.cts.map → Topic-DJXoDeMP.d.cts.map} +1 -1
- package/dist/{Topic-C5ZMfveW.d.cts → Topic-DxWQBgMN.d.mts} +3 -3
- package/dist/{Topic-DT10AqHk.d.mts.map → Topic-DxWQBgMN.d.mts.map} +1 -1
- package/dist/adaptors/aws.cjs +14 -7
- package/dist/adaptors/aws.cjs.map +1 -1
- package/dist/adaptors/aws.d.cts +6 -6
- package/dist/adaptors/aws.d.cts.map +1 -1
- package/dist/adaptors/aws.d.mts +8 -8
- package/dist/adaptors/aws.d.mts.map +1 -1
- package/dist/adaptors/aws.mjs +14 -7
- package/dist/adaptors/aws.mjs.map +1 -1
- package/dist/adaptors/hono.cjs +7 -6
- package/dist/adaptors/hono.d.cts +3 -3
- package/dist/adaptors/hono.d.cts.map +1 -1
- package/dist/adaptors/hono.d.mts +4 -4
- package/dist/adaptors/hono.d.mts.map +1 -1
- package/dist/adaptors/hono.mjs +7 -6
- package/dist/adaptors/testing.cjs +161 -126
- package/dist/adaptors/testing.cjs.map +1 -1
- package/dist/adaptors/testing.d.cts +7 -5
- package/dist/adaptors/testing.d.cts.map +1 -1
- package/dist/adaptors/testing.d.mts +8 -6
- package/dist/adaptors/testing.d.mts.map +1 -1
- package/dist/adaptors/testing.mjs +162 -127
- package/dist/adaptors/testing.mjs.map +1 -1
- package/dist/clone-I_WIpiFf.cjs +45 -0
- package/dist/clone-I_WIpiFf.cjs.map +1 -0
- package/dist/clone-pdXxc0t3.mjs +39 -0
- package/dist/clone-pdXxc0t3.mjs.map +1 -0
- package/dist/crons/index.cjs +14 -32
- package/dist/crons/index.cjs.map +1 -1
- package/dist/crons/index.d.cts +7 -7
- package/dist/crons/index.d.cts.map +1 -1
- package/dist/crons/index.d.mts +3 -3
- package/dist/crons/index.d.mts.map +1 -1
- package/dist/crons/index.mjs +14 -32
- package/dist/crons/index.mjs.map +1 -1
- package/dist/endpoints/index.cjs +74 -61
- package/dist/endpoints/index.cjs.map +1 -1
- package/dist/endpoints/index.d.cts +14 -6
- package/dist/endpoints/index.d.cts.map +1 -1
- package/dist/endpoints/index.d.mts +13 -5
- package/dist/endpoints/index.d.mts.map +1 -1
- package/dist/endpoints/index.mjs +74 -61
- package/dist/endpoints/index.mjs.map +1 -1
- package/dist/functions/index.cjs +3 -2
- package/dist/functions/index.d.cts +2 -2
- package/dist/functions/index.d.mts +2 -2
- package/dist/functions/index.mjs +3 -2
- package/dist/{functions-B-qt91O7.mjs → functions-EqTloHjo.mjs} +107 -46
- package/dist/functions-EqTloHjo.mjs.map +1 -0
- package/dist/{functions-C8i1kCMM.cjs → functions-SqtCDc9c.cjs} +118 -45
- package/dist/functions-SqtCDc9c.cjs.map +1 -0
- package/dist/{index-DP6fqv0I.d.mts → index-C7iynzLI.d.mts} +8 -2
- package/dist/index-C7iynzLI.d.mts.map +1 -0
- package/dist/{index-BVs9DvIj.d.cts → index-DdgJRm4q.d.cts} +12 -6
- package/dist/index-DdgJRm4q.d.cts.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +1 -1
- package/dist/{lazyAccessors-DfqZR6C4.mjs → lazyAccessors-CCTK19xj.mjs} +3 -3
- package/dist/{lazyAccessors-DfqZR6C4.mjs.map → lazyAccessors-CCTK19xj.mjs.map} +1 -1
- package/dist/{lazyAccessors-DmTG8sQH.cjs → lazyAccessors-D04oftGw.cjs} +3 -3
- package/dist/{lazyAccessors-DmTG8sQH.cjs.map → lazyAccessors-D04oftGw.cjs.map} +1 -1
- package/dist/monitors/index.cjs +49 -0
- package/dist/monitors/index.cjs.map +1 -0
- package/dist/monitors/index.d.cts +45 -0
- package/dist/monitors/index.d.cts.map +1 -0
- package/dist/monitors/index.d.mts +45 -0
- package/dist/monitors/index.d.mts.map +1 -0
- package/dist/monitors/index.mjs +48 -0
- package/dist/monitors/index.mjs.map +1 -0
- package/dist/{processAudits-CKjjivyz.cjs → processAudits-BJZT5gmy.cjs} +2 -2
- package/dist/{processAudits-CKjjivyz.cjs.map → processAudits-BJZT5gmy.cjs.map} +1 -1
- package/dist/{processAudits-ySnizdMG.mjs → processAudits-CwyaaSQc.mjs} +1 -1
- package/dist/{processAudits-ySnizdMG.mjs.map → processAudits-CwyaaSQc.mjs.map} +1 -1
- package/dist/{publisher-lFQleddL.cjs → publisher-BZgcrVPy.cjs} +1 -1
- package/dist/{publisher-lFQleddL.cjs.map → publisher-BZgcrVPy.cjs.map} +1 -1
- package/dist/{publisher-BrFOF847.mjs → publisher-CnpeMv-r.mjs} +1 -1
- package/dist/{publisher-BrFOF847.mjs.map → publisher-CnpeMv-r.mjs.map} +1 -1
- package/dist/queue/index.cjs +9 -22
- package/dist/queue/index.cjs.map +1 -1
- package/dist/queue/index.d.cts +4 -4
- package/dist/queue/index.d.cts.map +1 -1
- package/dist/queue/index.d.mts +2 -2
- package/dist/queue/index.d.mts.map +1 -1
- package/dist/queue/index.mjs +9 -22
- package/dist/queue/index.mjs.map +1 -1
- package/dist/subscribers/index.cjs +9 -22
- package/dist/subscribers/index.cjs.map +1 -1
- package/dist/subscribers/index.d.cts +5 -5
- package/dist/subscribers/index.d.cts.map +1 -1
- package/dist/subscribers/index.d.mts +3 -3
- package/dist/subscribers/index.d.mts.map +1 -1
- package/dist/subscribers/index.mjs +9 -22
- package/dist/subscribers/index.mjs.map +1 -1
- package/dist/{telemetry-3vdxGn4q.d.mts → telemetry-C--EgYVr.d.mts} +1 -1
- package/dist/{telemetry-3vdxGn4q.d.mts.map → telemetry-C--EgYVr.d.mts.map} +1 -1
- package/dist/topic/index.cjs +4 -7
- package/dist/topic/index.cjs.map +1 -1
- package/dist/topic/index.d.cts +2 -2
- package/dist/topic/index.d.cts.map +1 -1
- package/dist/topic/index.d.mts +2 -2
- package/dist/topic/index.d.mts.map +1 -1
- package/dist/topic/index.mjs +4 -7
- package/dist/topic/index.mjs.map +1 -1
- package/dist/{types-BDNGr7pv.d.mts → types-DNngH0fX.d.mts} +1 -1
- package/dist/{types-BDNGr7pv.d.mts.map → types-DNngH0fX.d.mts.map} +1 -1
- package/dist/types.d.mts +1 -1
- package/package.json +32 -22
- package/src/Construct.ts +5 -0
- package/src/__tests__/builder-reuse.spec.ts +587 -0
- package/src/__tests__/monitor.spec.ts +636 -0
- package/src/clone.ts +38 -0
- package/src/crons/CronBuilder.ts +35 -45
- package/src/crons/__tests__/Cron.spec.ts +10 -2
- package/src/crons/__tests__/CronBuilder.state-isolation.spec.ts +64 -18
- package/src/endpoints/AmazonApiGatewayEndpointAdaptor.ts +23 -2
- package/src/endpoints/EndpointBuilder.ts +61 -70
- package/src/endpoints/EndpointFactory.ts +31 -0
- package/src/endpoints/HonoEndpointAdaptor.ts +16 -1
- package/src/endpoints/TestEndpointAdaptor.ts +194 -150
- package/src/endpoints/__tests__/EndpointBuilder.audit.spec.ts +6 -3
- package/src/endpoints/__tests__/EndpointBuilder.responseType.spec.ts +4 -1
- package/src/endpoints/__tests__/EndpointBuilder.spec.ts +16 -8
- package/src/functions/AWSLambdaFunction.ts +28 -2
- package/src/functions/BaseFunctionBuilder.ts +33 -22
- package/src/functions/FunctionBuilder.ts +41 -51
- package/src/functions/TestFunctionAdaptor.ts +66 -31
- package/src/functions/__tests__/FunctionBuilder.state-isolation.spec.ts +55 -12
- package/src/index.ts +6 -0
- package/src/monitor.ts +145 -0
- package/src/monitors/heartbeat.ts +86 -0
- package/src/monitors/index.ts +6 -0
- package/src/queue/QueueBuilder.ts +22 -22
- package/src/subscribers/SubscriberBuilder.ts +19 -22
- package/src/topic/TopicBuilder.ts +9 -7
- package/tsdown.config.ts +1 -0
- package/dist/AWSScheduledFunction-CufMMhGB.mjs.map +0 -1
- package/dist/AWSScheduledFunction-DbkDr8CA.cjs.map +0 -1
- package/dist/Construct-DCMuYOox.d.mts.map +0 -1
- package/dist/Construct-_1wX7p_Y.d.cts.map +0 -1
- package/dist/Construct-r0U2zBwW.mjs.map +0 -1
- package/dist/Construct-vXMZJgRp.cjs.map +0 -1
- package/dist/Endpoint-DgfRslct.d.cts.map +0 -1
- package/dist/HonoEndpointAdaptor-CkJENANP.cjs.map +0 -1
- package/dist/HonoEndpointAdaptor-DJDT3PMx.mjs.map +0 -1
- package/dist/functions-B-qt91O7.mjs.map +0 -1
- package/dist/functions-C8i1kCMM.cjs.map +0 -1
- package/dist/index-BVs9DvIj.d.cts.map +0 -1
- package/dist/index-DP6fqv0I.d.mts.map +0 -1
package/src/crons/CronBuilder.ts
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import type { EventPublisher } from '@geekmidas/events';
|
|
2
2
|
import type { Logger } from '@geekmidas/logger';
|
|
3
|
-
import { ConsoleLogger } from '@geekmidas/logger/console';
|
|
4
3
|
import type { ComposableStandardSchema } from '@geekmidas/schema';
|
|
5
4
|
import type { Service } from '@geekmidas/services';
|
|
6
5
|
import type { StandardSchemaV1 } from '@standard-schema/spec';
|
|
7
6
|
import uniqBy from 'lodash.uniqby';
|
|
8
7
|
import { ConstructType } from '../Construct';
|
|
8
|
+
import { cloneWith } from '../clone';
|
|
9
9
|
import { FunctionBuilder, type FunctionHandler } from '../functions';
|
|
10
10
|
import { Cron, type ScheduleExpression } from './Cron';
|
|
11
11
|
|
|
12
|
-
const DEFAULT_LOGGER = new ConsoleLogger() as any;
|
|
13
|
-
|
|
14
12
|
export class CronBuilder<
|
|
15
13
|
TInput extends ComposableStandardSchema,
|
|
16
14
|
TServices extends Service[],
|
|
@@ -39,8 +37,7 @@ export class CronBuilder<
|
|
|
39
37
|
}
|
|
40
38
|
|
|
41
39
|
override memorySize(memorySize: number): this {
|
|
42
|
-
this
|
|
43
|
-
return this;
|
|
40
|
+
return cloneWith(this, { _memorySize: memorySize });
|
|
44
41
|
}
|
|
45
42
|
|
|
46
43
|
schedule(
|
|
@@ -55,8 +52,7 @@ export class CronBuilder<
|
|
|
55
52
|
TDatabase,
|
|
56
53
|
TDatabaseServiceName
|
|
57
54
|
> {
|
|
58
|
-
this
|
|
59
|
-
return this;
|
|
55
|
+
return cloneWith(this, { _schedule: _expression });
|
|
60
56
|
}
|
|
61
57
|
|
|
62
58
|
override input<T extends ComposableStandardSchema>(
|
|
@@ -71,9 +67,9 @@ export class CronBuilder<
|
|
|
71
67
|
TDatabase,
|
|
72
68
|
TDatabaseServiceName
|
|
73
69
|
> {
|
|
74
|
-
this
|
|
75
|
-
|
|
76
|
-
|
|
70
|
+
return cloneWith(this, {
|
|
71
|
+
inputSchema: schema as unknown as TInput,
|
|
72
|
+
}) as unknown as CronBuilder<
|
|
77
73
|
T,
|
|
78
74
|
TServices,
|
|
79
75
|
TLogger,
|
|
@@ -97,9 +93,9 @@ export class CronBuilder<
|
|
|
97
93
|
TDatabase,
|
|
98
94
|
TDatabaseServiceName
|
|
99
95
|
> {
|
|
100
|
-
this
|
|
101
|
-
|
|
102
|
-
|
|
96
|
+
return cloneWith(this, {
|
|
97
|
+
outputSchema: schema as unknown as OutSchema,
|
|
98
|
+
}) as unknown as CronBuilder<
|
|
103
99
|
TInput,
|
|
104
100
|
TServices,
|
|
105
101
|
TLogger,
|
|
@@ -123,12 +119,12 @@ export class CronBuilder<
|
|
|
123
119
|
TDatabase,
|
|
124
120
|
TDatabaseServiceName
|
|
125
121
|
> {
|
|
126
|
-
this
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
122
|
+
return cloneWith(this, {
|
|
123
|
+
_services: uniqBy(
|
|
124
|
+
[...this._services, ...services],
|
|
125
|
+
(s) => s.serviceName,
|
|
126
|
+
) as TServices,
|
|
127
|
+
}) as unknown as CronBuilder<
|
|
132
128
|
TInput,
|
|
133
129
|
[...TServices, ...T],
|
|
134
130
|
TLogger,
|
|
@@ -152,9 +148,9 @@ export class CronBuilder<
|
|
|
152
148
|
TDatabase,
|
|
153
149
|
TDatabaseServiceName
|
|
154
150
|
> {
|
|
155
|
-
this
|
|
156
|
-
|
|
157
|
-
|
|
151
|
+
return cloneWith(this, {
|
|
152
|
+
_logger: logger as unknown as TLogger,
|
|
153
|
+
}) as unknown as CronBuilder<
|
|
158
154
|
TInput,
|
|
159
155
|
TServices,
|
|
160
156
|
T,
|
|
@@ -178,12 +174,12 @@ export class CronBuilder<
|
|
|
178
174
|
TDatabase,
|
|
179
175
|
TDatabaseServiceName
|
|
180
176
|
> {
|
|
181
|
-
this
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
177
|
+
return cloneWith(this, {
|
|
178
|
+
_publisher: publisher as unknown as Service<
|
|
179
|
+
TEventPublisherServiceName,
|
|
180
|
+
TEventPublisher
|
|
181
|
+
>,
|
|
182
|
+
}) as unknown as CronBuilder<
|
|
187
183
|
TInput,
|
|
188
184
|
TServices,
|
|
189
185
|
TLogger,
|
|
@@ -211,12 +207,12 @@ export class CronBuilder<
|
|
|
211
207
|
T,
|
|
212
208
|
TName
|
|
213
209
|
> {
|
|
214
|
-
this
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
210
|
+
return cloneWith(this, {
|
|
211
|
+
_databaseService: service as unknown as Service<
|
|
212
|
+
TDatabaseServiceName,
|
|
213
|
+
TDatabase
|
|
214
|
+
>,
|
|
215
|
+
}) as unknown as CronBuilder<
|
|
220
216
|
TInput,
|
|
221
217
|
TServices,
|
|
222
218
|
TLogger,
|
|
@@ -254,17 +250,11 @@ export class CronBuilder<
|
|
|
254
250
|
this._databaseService,
|
|
255
251
|
);
|
|
256
252
|
|
|
257
|
-
//
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
this._databaseService = undefined;
|
|
263
|
-
this._schedule = undefined;
|
|
264
|
-
this.inputSchema = undefined;
|
|
265
|
-
this.outputSchema = undefined;
|
|
266
|
-
this._timeout = undefined;
|
|
267
|
-
this._memorySize = undefined;
|
|
253
|
+
// No reset. `.handle()` reads this builder and leaves it alone, so a
|
|
254
|
+
// configured base keeps its configuration for every construct built
|
|
255
|
+
// from it. The reset that used to be here wiped the base on first use.
|
|
256
|
+
|
|
257
|
+
cron.monitors = this._monitors;
|
|
268
258
|
|
|
269
259
|
return cron;
|
|
270
260
|
}
|
|
@@ -210,7 +210,12 @@ describe('Cron', () => {
|
|
|
210
210
|
const schedule: CronExpression = 'cron(0 0 * * SUN)';
|
|
211
211
|
|
|
212
212
|
const result = builder.schedule(schedule);
|
|
213
|
-
|
|
213
|
+
|
|
214
|
+
// A new builder carrying the schedule, so the base it came from stays
|
|
215
|
+
// clean for the next cron built off it.
|
|
216
|
+
expect(result).toBeInstanceOf(CronBuilder);
|
|
217
|
+
expect(result).not.toBe(builder);
|
|
218
|
+
expect((result as any)._schedule).toBe(schedule);
|
|
214
219
|
});
|
|
215
220
|
|
|
216
221
|
it('should build Cron with handle() method', () => {
|
|
@@ -261,7 +266,10 @@ describe('Cron', () => {
|
|
|
261
266
|
.schedule(schedule)
|
|
262
267
|
.input(z.object({ data: z.string() }));
|
|
263
268
|
|
|
264
|
-
expect(chained).
|
|
269
|
+
expect(chained).toBeInstanceOf(CronBuilder);
|
|
270
|
+
expect(chained).not.toBe(builder);
|
|
271
|
+
expect((chained as any)._schedule).toBe(schedule);
|
|
272
|
+
expect((chained as any)._timeout).toBe(30000);
|
|
265
273
|
});
|
|
266
274
|
});
|
|
267
275
|
|
|
@@ -17,6 +17,8 @@ const ServiceB = {
|
|
|
17
17
|
},
|
|
18
18
|
} satisfies Service<'b', any>;
|
|
19
19
|
|
|
20
|
+
const customLogger = new ConsoleLogger({ app: 'reusable-base' });
|
|
21
|
+
|
|
20
22
|
describe('CronBuilder - State Isolation', () => {
|
|
21
23
|
describe('singleton instance state reset', () => {
|
|
22
24
|
it('should reset services after handle() is called', () => {
|
|
@@ -135,28 +137,26 @@ describe('CronBuilder - State Isolation', () => {
|
|
|
135
137
|
it('should not share references between different builder chains', () => {
|
|
136
138
|
const c = new CronBuilder();
|
|
137
139
|
|
|
138
|
-
//
|
|
139
|
-
|
|
140
|
-
|
|
140
|
+
// Two chains off one base. They used to be the *same object* — every
|
|
141
|
+
// method mutated the base and returned it — so chain 1's function came
|
|
142
|
+
// out holding chain 2's services as well, and the base only survived
|
|
143
|
+
// because `.handle()` wiped it afterwards. That wipe is also what made
|
|
144
|
+
// a configured base unusable twice.
|
|
145
|
+
const builder1 = c.services([ServiceA]);
|
|
146
|
+
const builder2 = c.services([ServiceB]);
|
|
141
147
|
|
|
142
|
-
|
|
143
|
-
expect(builder1).toBe(
|
|
144
|
-
expect(builder1).toBe(c);
|
|
148
|
+
expect(builder1).not.toBe(builder2);
|
|
149
|
+
expect(builder1).not.toBe(c);
|
|
145
150
|
|
|
146
|
-
|
|
147
|
-
const
|
|
151
|
+
const fn1 = builder1.handle(async () => ({}));
|
|
152
|
+
const fn2 = builder2.handle(async () => ({}));
|
|
148
153
|
|
|
149
|
-
//
|
|
150
|
-
expect((
|
|
154
|
+
// Each function gets exactly what its own chain declared.
|
|
155
|
+
expect(fn1.services.map((s) => s.serviceName)).toEqual(['a']);
|
|
156
|
+
expect(fn2.services.map((s) => s.serviceName)).toEqual(['b']);
|
|
151
157
|
|
|
152
|
-
//
|
|
153
|
-
|
|
154
|
-
.schedule('rate(15 minutes)')
|
|
155
|
-
.services([ServiceB])
|
|
156
|
-
.handle(async () => ({}));
|
|
157
|
-
|
|
158
|
-
expect(cron1.services.map((s) => s.serviceName)).toEqual(['a', 'b']);
|
|
159
|
-
expect(cron2.services.map((s) => s.serviceName)).toEqual(['b']);
|
|
158
|
+
// And the base is untouched, so it stays reusable.
|
|
159
|
+
expect((c as any)._services).toEqual([]);
|
|
160
160
|
});
|
|
161
161
|
});
|
|
162
162
|
|
|
@@ -224,4 +224,50 @@ describe('CronBuilder - State Isolation', () => {
|
|
|
224
224
|
expect(cron3.schedule).toBe('rate(1 hour)');
|
|
225
225
|
});
|
|
226
226
|
});
|
|
227
|
+
|
|
228
|
+
describe('a configured base, reused', () => {
|
|
229
|
+
it('carries its configuration into every cron built from it', () => {
|
|
230
|
+
const base = new CronBuilder()
|
|
231
|
+
.logger(customLogger)
|
|
232
|
+
.timeout(60_000)
|
|
233
|
+
.schedule('rate(1 day)');
|
|
234
|
+
|
|
235
|
+
const first = base.services([ServiceA]).handle(async () => ({}));
|
|
236
|
+
const second = base.services([ServiceB]).handle(async () => ({}));
|
|
237
|
+
|
|
238
|
+
// The point of the whole shape: `const base = c.logger(log)` is a base you
|
|
239
|
+
// can keep. It used to survive exactly one `.handle()` — the reset put
|
|
240
|
+
// the logger and the timeout back to their defaults — so the second
|
|
241
|
+
// cron built from it silently logged somewhere else and timed out
|
|
242
|
+
// on a different clock.
|
|
243
|
+
for (const built of [first, second]) {
|
|
244
|
+
expect(built.logger).toBe(customLogger);
|
|
245
|
+
expect(built.timeout).toBe(60_000);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// And each still gets only what its own chain declared.
|
|
249
|
+
expect(first.services.map((s) => s.serviceName)).toEqual(['a']);
|
|
250
|
+
expect(second.services.map((s) => s.serviceName)).toEqual(['b']);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('persists configuration when handle() is called on the base itself', () => {
|
|
254
|
+
const base = new CronBuilder()
|
|
255
|
+
.logger(customLogger)
|
|
256
|
+
.timeout(60_000)
|
|
257
|
+
.schedule('rate(1 day)');
|
|
258
|
+
|
|
259
|
+
// No clone in between: `.handle()` is called straight on the base,
|
|
260
|
+
// twice. Cloning the config methods was not enough on its own —
|
|
261
|
+
// `.handle()` still ran a reset block that wiped whatever it was
|
|
262
|
+
// called on, so the second cron came back on the default logger and
|
|
263
|
+
// the default timeout.
|
|
264
|
+
const first = base.handle(async () => ({}));
|
|
265
|
+
const second = base.handle(async () => ({}));
|
|
266
|
+
|
|
267
|
+
for (const built of [first, second]) {
|
|
268
|
+
expect(built.logger).toBe(customLogger);
|
|
269
|
+
expect(built.timeout).toBe(60_000);
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
});
|
|
227
273
|
});
|
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
ServiceDiscovery,
|
|
39
39
|
type ServiceRecord,
|
|
40
40
|
} from '@geekmidas/services';
|
|
41
|
+
import { startMonitors } from '../monitor';
|
|
41
42
|
import { publishConstructEvents } from '../publisher';
|
|
42
43
|
import type { MappedAudit } from './audit';
|
|
43
44
|
import type { CookieFn, HeaderFn } from './Endpoint';
|
|
@@ -134,6 +135,8 @@ export abstract class AmazonApiGatewayEndpoint<
|
|
|
134
135
|
'Error processing request',
|
|
135
136
|
);
|
|
136
137
|
const wrappedError = wrapError(req.error);
|
|
138
|
+
// Kept for the monitors, which only see the response otherwise.
|
|
139
|
+
(req.event as any).__error = req.error;
|
|
137
140
|
|
|
138
141
|
// Set the response with the proper status code from the HttpError
|
|
139
142
|
req.response = {
|
|
@@ -600,8 +603,26 @@ export abstract class AmazonApiGatewayEndpoint<
|
|
|
600
603
|
|
|
601
604
|
// Cast event to any since Middy middlewares enrich the event with
|
|
602
605
|
// services, logger, session, etc. during the middleware chain execution
|
|
603
|
-
return runWithRequestContext(
|
|
604
|
-
|
|
606
|
+
return runWithRequestContext(
|
|
607
|
+
{ logger, requestId, startTime },
|
|
608
|
+
async () => {
|
|
609
|
+
const monitor = await startMonitors(
|
|
610
|
+
this.endpoint,
|
|
611
|
+
ServiceDiscovery.getInstance(this.envParser),
|
|
612
|
+
logger,
|
|
613
|
+
);
|
|
614
|
+
const response = await chain(
|
|
615
|
+
event as unknown as Parameters<typeof chain>[0],
|
|
616
|
+
context,
|
|
617
|
+
);
|
|
618
|
+
// Awaited: Lambda freezes the process once the response is
|
|
619
|
+
// returned, so an unawaited ping may never be sent.
|
|
620
|
+
await monitor.settle(
|
|
621
|
+
(response as any)?.statusCode ?? 200,
|
|
622
|
+
(event as any).__error,
|
|
623
|
+
);
|
|
624
|
+
return response;
|
|
625
|
+
},
|
|
605
626
|
);
|
|
606
627
|
};
|
|
607
628
|
|
|
@@ -10,6 +10,7 @@ import type { Service } from '@geekmidas/services';
|
|
|
10
10
|
import type { StandardSchemaV1 } from '@standard-schema/spec';
|
|
11
11
|
import uniqBy from 'lodash.uniqby';
|
|
12
12
|
import { ConstructType } from '../Construct';
|
|
13
|
+
import { cloneWith } from '../clone';
|
|
13
14
|
import { BaseFunctionBuilder } from '../functions';
|
|
14
15
|
import type { HttpMethod } from '../types';
|
|
15
16
|
import type { Authorizer, SecurityScheme } from './Authorizer';
|
|
@@ -102,30 +103,27 @@ export class EndpointBuilder<
|
|
|
102
103
|
}
|
|
103
104
|
|
|
104
105
|
description(description: string): this {
|
|
105
|
-
this
|
|
106
|
-
return this;
|
|
106
|
+
return cloneWith(this, { _description: description });
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
status(status: SuccessStatus): this {
|
|
110
|
-
this
|
|
111
|
-
return this;
|
|
110
|
+
return cloneWith(this, { _status: status });
|
|
112
111
|
}
|
|
113
112
|
|
|
114
113
|
override event<TEvent extends MappedEvent<TEventPublisher, OutSchema>>(
|
|
115
114
|
event: TEvent,
|
|
116
115
|
): this {
|
|
117
|
-
|
|
118
|
-
|
|
116
|
+
// Replaced rather than pushed: a clone shares whatever array the field
|
|
117
|
+
// points at, so an in-place push would be seen by the base it came from.
|
|
118
|
+
return cloneWith(this, { _events: [...this._events, event] });
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
tags(tags: string[]): this {
|
|
122
|
-
this
|
|
123
|
-
return this;
|
|
122
|
+
return cloneWith(this, { _tags: tags });
|
|
124
123
|
}
|
|
125
124
|
|
|
126
125
|
memorySize(memorySize: number): this {
|
|
127
|
-
this
|
|
128
|
-
return this;
|
|
126
|
+
return cloneWith(this, { _memorySize: memorySize });
|
|
129
127
|
}
|
|
130
128
|
|
|
131
129
|
/**
|
|
@@ -147,8 +145,7 @@ export class EndpointBuilder<
|
|
|
147
145
|
* ```
|
|
148
146
|
*/
|
|
149
147
|
responseType(type: string): this {
|
|
150
|
-
this
|
|
151
|
-
return this;
|
|
148
|
+
return cloneWith(this, { _responseType: type });
|
|
152
149
|
}
|
|
153
150
|
|
|
154
151
|
override publisher<T extends EventPublisher<any>, TName extends string>(
|
|
@@ -170,12 +167,12 @@ export class EndpointBuilder<
|
|
|
170
167
|
TDatabase,
|
|
171
168
|
TDatabaseServiceName
|
|
172
169
|
> {
|
|
173
|
-
this
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
170
|
+
return cloneWith(this, {
|
|
171
|
+
_publisher: publisher as unknown as Service<
|
|
172
|
+
TEventPublisherServiceName,
|
|
173
|
+
TEventPublisher
|
|
174
|
+
>,
|
|
175
|
+
}) as unknown as EndpointBuilder<
|
|
179
176
|
TRoute,
|
|
180
177
|
TMethod,
|
|
181
178
|
TInput,
|
|
@@ -213,9 +210,10 @@ export class EndpointBuilder<
|
|
|
213
210
|
TDatabase,
|
|
214
211
|
TDatabaseServiceName
|
|
215
212
|
> {
|
|
216
|
-
|
|
213
|
+
// A new object rather than a field set on the shared one: a clone
|
|
214
|
+
// keeps its base's `schemas` reference.
|
|
217
215
|
// @ts-expect-error
|
|
218
|
-
return this;
|
|
216
|
+
return cloneWith(this, { schemas: { ...this.schemas, body: schema } });
|
|
219
217
|
}
|
|
220
218
|
|
|
221
219
|
search<T extends StandardSchemaV1>(
|
|
@@ -237,9 +235,10 @@ export class EndpointBuilder<
|
|
|
237
235
|
TDatabase,
|
|
238
236
|
TDatabaseServiceName
|
|
239
237
|
> {
|
|
240
|
-
|
|
238
|
+
// A new object rather than a field set on the shared one: a clone
|
|
239
|
+
// keeps its base's `schemas` reference.
|
|
241
240
|
// @ts-expect-error
|
|
242
|
-
return this;
|
|
241
|
+
return cloneWith(this, { schemas: { ...this.schemas, query: schema } });
|
|
243
242
|
}
|
|
244
243
|
|
|
245
244
|
query<T extends StandardSchemaV1>(
|
|
@@ -283,21 +282,20 @@ export class EndpointBuilder<
|
|
|
283
282
|
TDatabase,
|
|
284
283
|
TDatabaseServiceName
|
|
285
284
|
> {
|
|
286
|
-
|
|
285
|
+
// A new object rather than a field set on the shared one: a clone
|
|
286
|
+
// keeps its base's `schemas` reference.
|
|
287
287
|
// @ts-expect-error
|
|
288
|
-
return this;
|
|
288
|
+
return cloneWith(this, { schemas: { ...this.schemas, params: schema } });
|
|
289
289
|
}
|
|
290
290
|
|
|
291
291
|
authorize(
|
|
292
292
|
fn: AuthorizeFn<TServices, TLogger, TSession, TInput, TDatabase>,
|
|
293
293
|
): this {
|
|
294
|
-
this
|
|
295
|
-
return this;
|
|
294
|
+
return cloneWith(this, { _authorize: fn });
|
|
296
295
|
}
|
|
297
296
|
|
|
298
297
|
rateLimit(config: RateLimitConfig): this {
|
|
299
|
-
this
|
|
300
|
-
return this;
|
|
298
|
+
return cloneWith(this, { _rateLimit: config });
|
|
301
299
|
}
|
|
302
300
|
|
|
303
301
|
authorizer(
|
|
@@ -321,8 +319,7 @@ export class EndpointBuilder<
|
|
|
321
319
|
> {
|
|
322
320
|
// Special case: 'none' explicitly marks endpoint as having no authorizer
|
|
323
321
|
if (name === 'none') {
|
|
324
|
-
this
|
|
325
|
-
return this;
|
|
322
|
+
return cloneWith(this, { _authorizerName: undefined });
|
|
326
323
|
}
|
|
327
324
|
|
|
328
325
|
// Validate that the authorizer exists in available authorizers
|
|
@@ -337,8 +334,7 @@ export class EndpointBuilder<
|
|
|
337
334
|
`Authorizer "${name as string}" not found in available authorizers: ${available}`,
|
|
338
335
|
);
|
|
339
336
|
}
|
|
340
|
-
this
|
|
341
|
-
return this;
|
|
337
|
+
return cloneWith(this, { _authorizerName: name });
|
|
342
338
|
}
|
|
343
339
|
|
|
344
340
|
services<T extends Service[]>(
|
|
@@ -360,12 +356,12 @@ export class EndpointBuilder<
|
|
|
360
356
|
TDatabase,
|
|
361
357
|
TDatabaseServiceName
|
|
362
358
|
> {
|
|
363
|
-
this
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
359
|
+
return cloneWith(this, {
|
|
360
|
+
_services: uniqBy(
|
|
361
|
+
[...this._services, ...services],
|
|
362
|
+
(s) => s.serviceName,
|
|
363
|
+
) as TServices,
|
|
364
|
+
}) as unknown as EndpointBuilder<
|
|
369
365
|
TRoute,
|
|
370
366
|
TMethod,
|
|
371
367
|
TInput,
|
|
@@ -403,9 +399,9 @@ export class EndpointBuilder<
|
|
|
403
399
|
TDatabase,
|
|
404
400
|
TDatabaseServiceName
|
|
405
401
|
> {
|
|
406
|
-
this
|
|
407
|
-
|
|
408
|
-
|
|
402
|
+
return cloneWith(this, {
|
|
403
|
+
_logger: logger as unknown as TLogger,
|
|
404
|
+
}) as unknown as EndpointBuilder<
|
|
409
405
|
TRoute,
|
|
410
406
|
TMethod,
|
|
411
407
|
TInput,
|
|
@@ -443,9 +439,9 @@ export class EndpointBuilder<
|
|
|
443
439
|
TDatabase,
|
|
444
440
|
TDatabaseServiceName
|
|
445
441
|
> {
|
|
446
|
-
this
|
|
447
|
-
|
|
448
|
-
|
|
442
|
+
return cloneWith(this, {
|
|
443
|
+
outputSchema: schema as unknown as OutSchema,
|
|
444
|
+
}) as unknown as EndpointBuilder<
|
|
449
445
|
TRoute,
|
|
450
446
|
TMethod,
|
|
451
447
|
TInput,
|
|
@@ -488,12 +484,12 @@ export class EndpointBuilder<
|
|
|
488
484
|
TDatabase,
|
|
489
485
|
TDatabaseServiceName
|
|
490
486
|
> {
|
|
491
|
-
this
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
487
|
+
return cloneWith(this, {
|
|
488
|
+
_auditorStorage: storage as unknown as Service<
|
|
489
|
+
TAuditStorageServiceName,
|
|
490
|
+
TAuditStorage
|
|
491
|
+
>,
|
|
492
|
+
}) as unknown as EndpointBuilder<
|
|
497
493
|
TRoute,
|
|
498
494
|
TMethod,
|
|
499
495
|
TInput,
|
|
@@ -535,8 +531,7 @@ export class EndpointBuilder<
|
|
|
535
531
|
TDatabase,
|
|
536
532
|
TDatabaseServiceName
|
|
537
533
|
> {
|
|
538
|
-
this
|
|
539
|
-
return this;
|
|
534
|
+
return cloneWith(this, { _actorExtractor: extractor });
|
|
540
535
|
}
|
|
541
536
|
|
|
542
537
|
/**
|
|
@@ -557,8 +552,7 @@ export class EndpointBuilder<
|
|
|
557
552
|
* ```
|
|
558
553
|
*/
|
|
559
554
|
audit(audits: MappedAudit<TAuditAction, OutSchema>[]): this {
|
|
560
|
-
this
|
|
561
|
-
return this;
|
|
555
|
+
return cloneWith(this, { _audits: audits });
|
|
562
556
|
}
|
|
563
557
|
|
|
564
558
|
/**
|
|
@@ -595,12 +589,12 @@ export class EndpointBuilder<
|
|
|
595
589
|
T,
|
|
596
590
|
TName
|
|
597
591
|
> {
|
|
598
|
-
this
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
592
|
+
return cloneWith(this, {
|
|
593
|
+
_databaseService: service as unknown as Service<
|
|
594
|
+
TDatabaseServiceName,
|
|
595
|
+
TDatabase
|
|
596
|
+
>,
|
|
597
|
+
}) as unknown as EndpointBuilder<
|
|
604
598
|
TRoute,
|
|
605
599
|
TMethod,
|
|
606
600
|
TInput,
|
|
@@ -642,13 +636,9 @@ export class EndpointBuilder<
|
|
|
642
636
|
config: RlsConfig<TServices, TSession, TLogger> | false | RlsBypass,
|
|
643
637
|
): this {
|
|
644
638
|
if (config === false || config === RLS_BYPASS) {
|
|
645
|
-
this
|
|
646
|
-
this._rlsConfig = undefined;
|
|
647
|
-
} else {
|
|
648
|
-
this._rlsConfig = config;
|
|
649
|
-
this._rlsBypass = false;
|
|
639
|
+
return cloneWith(this, { _rlsBypass: true, _rlsConfig: undefined });
|
|
650
640
|
}
|
|
651
|
-
return this;
|
|
641
|
+
return cloneWith(this, { _rlsConfig: config, _rlsBypass: false });
|
|
652
642
|
}
|
|
653
643
|
|
|
654
644
|
/**
|
|
@@ -665,9 +655,7 @@ export class EndpointBuilder<
|
|
|
665
655
|
* ```
|
|
666
656
|
*/
|
|
667
657
|
rlsBypass(): this {
|
|
668
|
-
this
|
|
669
|
-
this._rlsConfig = undefined;
|
|
670
|
-
return this;
|
|
658
|
+
return cloneWith(this, { _rlsBypass: true, _rlsConfig: undefined });
|
|
671
659
|
}
|
|
672
660
|
|
|
673
661
|
// EndpointBuilder doesn't have a generic input method - it uses body, query, params instead
|
|
@@ -727,7 +715,7 @@ export class EndpointBuilder<
|
|
|
727
715
|
}
|
|
728
716
|
}
|
|
729
717
|
|
|
730
|
-
|
|
718
|
+
const endpoint = new Endpoint({
|
|
731
719
|
fn,
|
|
732
720
|
method: this.method,
|
|
733
721
|
route: this.route,
|
|
@@ -754,5 +742,8 @@ export class EndpointBuilder<
|
|
|
754
742
|
rlsBypass: this._rlsBypass,
|
|
755
743
|
responseType: this._responseType,
|
|
756
744
|
});
|
|
745
|
+
endpoint.monitors = this._monitors;
|
|
746
|
+
|
|
747
|
+
return endpoint;
|
|
757
748
|
}
|
|
758
749
|
}
|