@lafken/common 0.10.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/LICENCE +21 -0
- package/README.md +414 -0
- package/lib/constants/env.constants.d.ts +2 -0
- package/lib/constants/env.constants.js +5 -0
- package/lib/constants/index.d.ts +1 -0
- package/lib/constants/index.js +17 -0
- package/lib/decorators/field/field.d.ts +9 -0
- package/lib/decorators/field/field.js +138 -0
- package/lib/decorators/field/field.types.d.ts +71 -0
- package/lib/decorators/field/field.types.js +8 -0
- package/lib/decorators/field/index.d.ts +2 -0
- package/lib/decorators/field/index.js +18 -0
- package/lib/decorators/index.d.ts +4 -0
- package/lib/decorators/index.js +20 -0
- package/lib/decorators/lambda/index.d.ts +2 -0
- package/lib/decorators/lambda/index.js +18 -0
- package/lib/decorators/lambda/lambda.d.ts +7 -0
- package/lib/decorators/lambda/lambda.js +58 -0
- package/lib/decorators/lambda/lambda.types.d.ts +204 -0
- package/lib/decorators/lambda/lambda.types.js +14 -0
- package/lib/decorators/payload/index.d.ts +2 -0
- package/lib/decorators/payload/index.js +18 -0
- package/lib/decorators/payload/payload.d.ts +2 -0
- package/lib/decorators/payload/payload.js +32 -0
- package/lib/decorators/payload/payload.types.d.ts +18 -0
- package/lib/decorators/payload/payload.types.js +2 -0
- package/lib/decorators/resource/index.d.ts +2 -0
- package/lib/decorators/resource/index.js +18 -0
- package/lib/decorators/resource/resource.d.ts +2 -0
- package/lib/decorators/resource/resource.js +25 -0
- package/lib/decorators/resource/resource.types.d.ts +31 -0
- package/lib/decorators/resource/resource.types.js +7 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.js +20 -0
- package/lib/types/env.types.d.ts +4 -0
- package/lib/types/env.types.js +2 -0
- package/lib/types/index.d.ts +7 -0
- package/lib/types/index.js +23 -0
- package/lib/types/output.types.d.ts +55 -0
- package/lib/types/output.types.js +2 -0
- package/lib/types/override-resources.types.d.ts +35 -0
- package/lib/types/override-resources.types.js +2 -0
- package/lib/types/resource.types.d.ts +52 -0
- package/lib/types/resource.types.js +2 -0
- package/lib/types/services.types.d.ts +22 -0
- package/lib/types/services.types.js +2 -0
- package/lib/types/time.types.d.ts +8 -0
- package/lib/types/time.types.js +2 -0
- package/lib/types/utilities.types.d.ts +37 -0
- package/lib/types/utilities.types.js +2 -0
- package/lib/utils/build-env.utils.d.ts +2 -0
- package/lib/utils/build-env.utils.js +12 -0
- package/lib/utils/index.d.ts +4 -0
- package/lib/utils/index.js +20 -0
- package/lib/utils/path.utils.d.ts +1 -0
- package/lib/utils/path.utils.js +16 -0
- package/lib/utils/resource.utils.d.ts +6 -0
- package/lib/utils/resource.utils.js +20 -0
- package/lib/utils/string.utils.d.ts +5 -0
- package/lib/utils/string.utils.js +36 -0
- package/package.json +55 -0
package/LICENCE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Aníbal Emilio Jorquera Cornejo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
# @lafken/common
|
|
2
|
+
|
|
3
|
+
`@lafken/common` is the core utility package for the Lafken framework. It provides the decorator factory functions, metadata reflection utilities, and shared types that power every `@lafken/*` package. If a Lafken package defines a decorator or reads infrastructure metadata, it depends on `@lafken/common`.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @lafken/common
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
Lafken uses TypeScript decorators to declare infrastructure. This package provides the factories that create those decorators and the utilities that read the metadata they produce:
|
|
14
|
+
|
|
15
|
+
| Factory | Level | Purpose |
|
|
16
|
+
| -------------------------- | ------ | -------------------------------------------- |
|
|
17
|
+
| `createResourceDecorator` | Class | Mark a class as a deployable resource |
|
|
18
|
+
| `createLambdaDecorator` | Method | Mark a method as a Lambda handler |
|
|
19
|
+
| `createEventDecorator` | Param | Bind a method parameter to the Lambda event |
|
|
20
|
+
| `createFieldDecorator` | Prop | Describe a typed field inside a payload class |
|
|
21
|
+
| `createPayloadDecorator` | Class | Define a named payload schema |
|
|
22
|
+
|
|
23
|
+
## Decorator Factories
|
|
24
|
+
|
|
25
|
+
### `createResourceDecorator`
|
|
26
|
+
|
|
27
|
+
Creates a **class-level** decorator that marks a class as an infrastructure resource. The factory automatically captures the resource name, the file path and folder (used for Lambda bundling), and any custom metadata you define.
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { createResourceDecorator } from '@lafken/common';
|
|
31
|
+
|
|
32
|
+
export const RESOURCE_TYPE = 'MY_SERVICE' as const;
|
|
33
|
+
|
|
34
|
+
export interface MyServiceProps {
|
|
35
|
+
name?: string;
|
|
36
|
+
retryCount?: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const MyService = createResourceDecorator<MyServiceProps>({
|
|
40
|
+
type: RESOURCE_TYPE,
|
|
41
|
+
callerFileIndex: 5, // Stack-trace index to resolve caller file
|
|
42
|
+
getMetadata: (props) => ({ // Optional — transform props before storing
|
|
43
|
+
...props,
|
|
44
|
+
retryCount: props.retryCount ?? 3,
|
|
45
|
+
}),
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Usage:
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
@MyService({ name: 'notifications', retryCount: 5 })
|
|
53
|
+
export class NotificationService { ... }
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Captured metadata** (`ResourceMetadata`):
|
|
57
|
+
|
|
58
|
+
| Field | Description |
|
|
59
|
+
| -------------- | ---------------------------------------- |
|
|
60
|
+
| `type` | Resolver identifier (`MY_SERVICE`) |
|
|
61
|
+
| `name` | Explicit name or the class name |
|
|
62
|
+
| `foldername` | Directory of the decorated file |
|
|
63
|
+
| `filename` | File name (without `.js` extension) |
|
|
64
|
+
| `originalName` | Original class name (for asset naming) |
|
|
65
|
+
| `minify` | Whether to minify the bundle (`true`) |
|
|
66
|
+
|
|
67
|
+
### `createLambdaDecorator`
|
|
68
|
+
|
|
69
|
+
Creates a **method-level** decorator that registers a method as a Lambda handler. It stores handler metadata and rewrites the method descriptor so the framework can inject arguments (`@Event`, `@Context`) at runtime.
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import { createLambdaDecorator } from '@lafken/common';
|
|
73
|
+
import type { LambdaMetadata } from '@lafken/common';
|
|
74
|
+
|
|
75
|
+
export interface PublishProps {
|
|
76
|
+
name: string;
|
|
77
|
+
lambda?: LambdaProps;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface PublishMetadata extends PublishProps {
|
|
81
|
+
name: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export const Publish = (props: PublishProps) =>
|
|
85
|
+
createLambdaDecorator<PublishProps, PublishMetadata>({
|
|
86
|
+
getLambdaMetadata: (props, methodName) => ({
|
|
87
|
+
...props,
|
|
88
|
+
name: methodName,
|
|
89
|
+
}),
|
|
90
|
+
})(props);
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Usage:
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
class NotificationService {
|
|
97
|
+
@Publish({ name: 'send-email' })
|
|
98
|
+
sendEmail(@Event() event: EmailPayload) {
|
|
99
|
+
// handler logic
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**`LambdaProps`** — Optional Lambda-level configuration available through the `lambda` property:
|
|
105
|
+
|
|
106
|
+
| Property | Type | Description |
|
|
107
|
+
| ------------- | ------------------- | --------------------------------------------- |
|
|
108
|
+
| `timeout` | `number` | Execution timeout in seconds |
|
|
109
|
+
| `memory` | `number` | Memory allocation in MB |
|
|
110
|
+
| `runtime` | `24 \| 22 \| 20` | Node.js runtime version |
|
|
111
|
+
| `services` | `ServicesValues` | IAM service permissions |
|
|
112
|
+
| `env` | `EnvironmentValue` | Environment variables (static or dynamic) |
|
|
113
|
+
| `tags` | `Record<string, string>` | Resource tags |
|
|
114
|
+
| `enableTrace` | `boolean` | Enable AWS X-Ray tracing |
|
|
115
|
+
|
|
116
|
+
### `createEventDecorator`
|
|
117
|
+
|
|
118
|
+
Creates a **parameter-level** decorator that binds a method parameter to the incoming Lambda event. It works with `createFieldDecorator` to map typed payload classes onto the raw event.
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
import { createEventDecorator } from '@lafken/common';
|
|
122
|
+
|
|
123
|
+
export const Event = createEventDecorator({
|
|
124
|
+
prefix: 'my-service',
|
|
125
|
+
enableInLambdaInvocation: false, // Only capture metadata during build
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Usage:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
class NotificationService {
|
|
133
|
+
@Publish({ name: 'send-email' })
|
|
134
|
+
sendEmail(@Event(EmailPayload) event: EmailPayload) { ... }
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### `Context`
|
|
139
|
+
|
|
140
|
+
A built-in parameter decorator that injects the Lambda execution context:
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
import { Context } from '@lafken/common';
|
|
144
|
+
|
|
145
|
+
class MyService {
|
|
146
|
+
@Handler()
|
|
147
|
+
process(@Event(Input) event: Input, @Context() ctx: any) {
|
|
148
|
+
console.log(ctx.functionName);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### `createFieldDecorator`
|
|
154
|
+
|
|
155
|
+
Creates a **property-level** decorator that describes a typed field inside a payload class. The field metadata is collected by resolvers to build event schemas (e.g., Step Functions input/output, API request bodies).
|
|
156
|
+
|
|
157
|
+
Supported types: `String`, `Number`, `Boolean`, nested classes, and arrays.
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
import { createFieldDecorator } from '@lafken/common';
|
|
161
|
+
|
|
162
|
+
export const Field = createFieldDecorator({
|
|
163
|
+
prefix: 'my-service',
|
|
164
|
+
getMetadata: () => ({}),
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Usage:
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
class Address {
|
|
172
|
+
@Field()
|
|
173
|
+
street: string;
|
|
174
|
+
|
|
175
|
+
@Field()
|
|
176
|
+
city: string;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
class UserPayload {
|
|
180
|
+
@Field({ name: 'user_name' }) // Rename the field in the schema
|
|
181
|
+
name: string;
|
|
182
|
+
|
|
183
|
+
@Field()
|
|
184
|
+
age: number;
|
|
185
|
+
|
|
186
|
+
@Field({ type: Address }) // Nested object
|
|
187
|
+
address: Address;
|
|
188
|
+
|
|
189
|
+
@Field({ type: [String] }) // Array of primitives
|
|
190
|
+
tags: string[];
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### `createPayloadDecorator`
|
|
195
|
+
|
|
196
|
+
Creates a **class-level** decorator that gives a payload class a name and optional metadata. This identity is used when resolvers reference the payload in generated infrastructure.
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
import { createPayloadDecorator } from '@lafken/common';
|
|
200
|
+
|
|
201
|
+
export const Payload = createPayloadDecorator({
|
|
202
|
+
prefix: 'my-service',
|
|
203
|
+
createUniqueId: true, // Append a counter if names collide
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Usage:
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
@Payload({ name: 'CreateUserInput' })
|
|
211
|
+
class CreateUserInput {
|
|
212
|
+
@Field()
|
|
213
|
+
email: string;
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Metadata Utilities
|
|
218
|
+
|
|
219
|
+
Resolvers use these functions to read the metadata produced by the decorators above.
|
|
220
|
+
|
|
221
|
+
### `getResourceMetadata`
|
|
222
|
+
|
|
223
|
+
Returns the `ResourceMetadata` stored on a class by `createResourceDecorator`:
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
import { getResourceMetadata } from '@lafken/common';
|
|
227
|
+
|
|
228
|
+
const metadata = getResourceMetadata(NotificationService);
|
|
229
|
+
// { type: 'MY_SERVICE', name: 'notifications', foldername: '...', ... }
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### `getResourceHandlerMetadata`
|
|
233
|
+
|
|
234
|
+
Returns an array of handler metadata stored by `createLambdaDecorator`:
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
import { getResourceHandlerMetadata } from '@lafken/common';
|
|
238
|
+
|
|
239
|
+
const handlers = getResourceHandlerMetadata<PublishMetadata>(NotificationService);
|
|
240
|
+
// [{ name: 'sendEmail', ... }]
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### `getMetadataByKey` / `getMetadataPrototypeByKey`
|
|
244
|
+
|
|
245
|
+
Low-level helpers to read any Reflect metadata by key from a class or its prototype:
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
import { getMetadataByKey, getMetadataPrototypeByKey } from '@lafken/common';
|
|
249
|
+
|
|
250
|
+
const payload = getMetadataByKey<PayloadMetadata>(MyClass, 'my-service:lafken:payload');
|
|
251
|
+
const fields = getMetadataPrototypeByKey<FieldMetadata[]>(MyClass, 'my-service:lafken:field');
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## Build Environment
|
|
255
|
+
|
|
256
|
+
Decorators only capture metadata when the build environment flag is set. This prevents metadata side-effects during normal runtime execution.
|
|
257
|
+
|
|
258
|
+
- **`isBuildEnvironment()`** — Returns `true` when the `LAFKEN_CONTEXT` environment variable equals `BUILD`.
|
|
259
|
+
- **`enableBuildEnvVariable()`** — Sets the flag. **Required in tests** before declaring any decorated class.
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
import { enableBuildEnvVariable } from '@lafken/common';
|
|
263
|
+
|
|
264
|
+
describe('My resolver', () => {
|
|
265
|
+
enableBuildEnvVariable();
|
|
266
|
+
|
|
267
|
+
@MyService({ name: 'test' })
|
|
268
|
+
class TestResource { ... }
|
|
269
|
+
|
|
270
|
+
it('should capture metadata', () => {
|
|
271
|
+
const meta = getResourceMetadata(TestResource);
|
|
272
|
+
expect(meta.name).toBe('test');
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## String Utilities
|
|
278
|
+
|
|
279
|
+
General-purpose string helpers used throughout the framework:
|
|
280
|
+
|
|
281
|
+
| Function | Description | Example |
|
|
282
|
+
| ---------------------- | ---------------------------------------------------- | -------------------------------- |
|
|
283
|
+
| `capitalize(str)` | Uppercase first letter | `'hello'` → `'Hello'` |
|
|
284
|
+
| `kebabCase(str)` | Convert to kebab-case | `'MyService'` → `'my-service'` |
|
|
285
|
+
| `cleanString(str)` | Remove non-alphanumeric characters | `'a-b_c'` → `'abc'` |
|
|
286
|
+
| `cleanAndCapitalize(str)` | Clean + capitalize each word | `'my-service'` → `'MyService'` |
|
|
287
|
+
| `cleanTemplateString(str)` | Trim + collapse multiline strings into one line | — |
|
|
288
|
+
|
|
289
|
+
## Shared Types
|
|
290
|
+
|
|
291
|
+
### Services & Permissions
|
|
292
|
+
|
|
293
|
+
The `Services` type provides typed IAM permission declarations for Lambda handlers:
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
import type { Services } from '@lafken/common';
|
|
297
|
+
|
|
298
|
+
// Shorthand — grants full service access
|
|
299
|
+
const services: Services[] = ['dynamodb', 's3'];
|
|
300
|
+
|
|
301
|
+
// Fine-grained — specific actions and resources
|
|
302
|
+
const services: Services[] = [
|
|
303
|
+
{ type: 'dynamodb', permissions: ['GetItem', 'PutItem'] },
|
|
304
|
+
{ type: 's3', permissions: ['GetObject'], resources: ['arn:aws:s3:::my-bucket/*'] },
|
|
305
|
+
{ type: 'custom', serviceName: 'ses', permissions: ['SendEmail'] },
|
|
306
|
+
];
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Supported service types: `dynamodb`, `s3`, `lambda`, `cloudwatch`, `sqs`, `state_machine`, `kms`, `ssm`, `event`, and `custom`.
|
|
310
|
+
|
|
311
|
+
### Environment Variables
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
import type { EnvironmentValue } from '@lafken/common';
|
|
315
|
+
|
|
316
|
+
// Static values
|
|
317
|
+
const env: EnvironmentValue = { TABLE_NAME: 'users' };
|
|
318
|
+
|
|
319
|
+
// Dynamic values — resolved at deploy time via resource references
|
|
320
|
+
const env: EnvironmentValue = ({ getResourceValue }) => ({
|
|
321
|
+
TABLE_ARN: getResourceValue('dynamo::users-table', 'arn'),
|
|
322
|
+
});
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### Duration
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
import type { Duration } from '@lafken/common';
|
|
329
|
+
|
|
330
|
+
const timeout: Duration = 30; // seconds
|
|
331
|
+
const ttl: Duration = { type: 'days', duration: 7 }; // 7 days
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Type-Safe Resource References
|
|
335
|
+
|
|
336
|
+
The package provides augmentable interfaces that enable type-safe resource names across modules. Packages extend these interfaces so that `getResourceValue` calls are validated at compile time:
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
// In your lafken-types.d.ts
|
|
340
|
+
declare module '@lafken/common' {
|
|
341
|
+
interface ModulesAvailable {
|
|
342
|
+
core: {
|
|
343
|
+
Queue: { 'email-queue': string };
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
interface DynamoTableAvailable {
|
|
347
|
+
'users-table': string;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
## API Reference
|
|
353
|
+
|
|
354
|
+
### Decorator Factories
|
|
355
|
+
|
|
356
|
+
| Export | Description |
|
|
357
|
+
| -------------------------- | ------------------------------------------ |
|
|
358
|
+
| `createResourceDecorator` | Factory for class-level resource decorators |
|
|
359
|
+
| `createLambdaDecorator` | Factory for method-level handler decorators |
|
|
360
|
+
| `createEventDecorator` | Factory for parameter-level event binding |
|
|
361
|
+
| `createFieldDecorator` | Factory for property-level field metadata |
|
|
362
|
+
| `createPayloadDecorator` | Factory for class-level payload naming |
|
|
363
|
+
| `Context` | Parameter decorator for Lambda context |
|
|
364
|
+
|
|
365
|
+
### Metadata Readers
|
|
366
|
+
|
|
367
|
+
| Export | Description |
|
|
368
|
+
| ---------------------------- | ------------------------------------------ |
|
|
369
|
+
| `getResourceMetadata` | Read class resource metadata |
|
|
370
|
+
| `getResourceHandlerMetadata` | Read method handler metadata |
|
|
371
|
+
| `getMetadataByKey` | Read metadata by key from a class |
|
|
372
|
+
| `getMetadataPrototypeByKey` | Read metadata by key from a prototype |
|
|
373
|
+
|
|
374
|
+
### Utilities
|
|
375
|
+
|
|
376
|
+
| Export | Description |
|
|
377
|
+
| ------------------------- | ---------------------------------------- |
|
|
378
|
+
| `enableBuildEnvVariable` | Enable build mode for decorator capture |
|
|
379
|
+
| `isBuildEnvironment` | Check if build mode is active |
|
|
380
|
+
| `getCallerFileName` | Resolve the caller file from the stack |
|
|
381
|
+
| `capitalize` | Capitalize a string |
|
|
382
|
+
| `kebabCase` | Convert to kebab-case |
|
|
383
|
+
| `cleanString` | Strip non-alphanumeric characters |
|
|
384
|
+
| `cleanAndCapitalize` | Clean and capitalize each word |
|
|
385
|
+
| `cleanTemplateString` | Collapse multiline string to one line |
|
|
386
|
+
|
|
387
|
+
### Constants
|
|
388
|
+
|
|
389
|
+
| Export | Description |
|
|
390
|
+
| ---------------------- | ------------------------------------ |
|
|
391
|
+
| `LAFKEN_CONTEXT` | Environment variable name |
|
|
392
|
+
| `LAFKEN_CONTEXT_VALUE` | Expected value for build mode |
|
|
393
|
+
|
|
394
|
+
### Key Types
|
|
395
|
+
|
|
396
|
+
| Export | Description |
|
|
397
|
+
| -------------------- | --------------------------------------------------- |
|
|
398
|
+
| `ResourceProps` | Base props for resource decorators |
|
|
399
|
+
| `ResourceMetadata` | Metadata shape stored by resource decorators |
|
|
400
|
+
| `LambdaProps` | Lambda configuration (timeout, memory, runtime, ...) |
|
|
401
|
+
| `LambdaMetadata` | Metadata shape for handler methods |
|
|
402
|
+
| `FieldMetadata` | Discriminated union of field types |
|
|
403
|
+
| `PayloadMetadata` | Metadata shape for payload classes |
|
|
404
|
+
| `Services` | IAM permission declaration type |
|
|
405
|
+
| `ServicesValues` | Array or function returning `Services[]` |
|
|
406
|
+
| `EnvironmentValue` | Static or dynamic environment variables |
|
|
407
|
+
| `Duration` | Seconds or `{ type, duration }` object |
|
|
408
|
+
| `ClassResource` | Generic class constructor type |
|
|
409
|
+
| `GetResourceValue` | Callback type for cross-resource references |
|
|
410
|
+
| `DeepPartial<T>` | Recursive partial type utility |
|
|
411
|
+
|
|
412
|
+
## License
|
|
413
|
+
|
|
414
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './env.constants';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./env.constants"), exports);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { type AllowedTypes, type BaseFieldMetadata, type CreateFieldDecoratorProps, FieldProperties, type FieldProps, type PrimitiveTypes } from './field.types';
|
|
3
|
+
export declare const primitiveTypeValues: Set<PrimitiveTypes>;
|
|
4
|
+
export declare const primitiveTypeofValues: Set<string>;
|
|
5
|
+
export declare const mapTypeofValueToPrimitiveType: Record<string, PrimitiveTypes>;
|
|
6
|
+
export declare const getPrimitiveType: (type: AllowedTypes) => PrimitiveTypes | undefined;
|
|
7
|
+
export declare const getEventFields: (prefix: string, target?: AllowedTypes, name?: string) => BaseFieldMetadata | undefined;
|
|
8
|
+
export declare const createFieldDecorator: <T extends FieldProps, M>({ prefix, enableInLambdaInvocation, getMetadata, }: CreateFieldDecoratorProps<T, M>) => (props?: T) => (target: any, destinationName: string) => void;
|
|
9
|
+
export declare const createFieldName: (prefix: string, type: FieldProperties) => string;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createFieldName = exports.createFieldDecorator = exports.getEventFields = exports.getPrimitiveType = exports.mapTypeofValueToPrimitiveType = exports.primitiveTypeofValues = exports.primitiveTypeValues = void 0;
|
|
4
|
+
require("reflect-metadata");
|
|
5
|
+
const utils_1 = require("../../utils");
|
|
6
|
+
const build_env_utils_1 = require("../../utils/build-env.utils");
|
|
7
|
+
const field_types_1 = require("./field.types");
|
|
8
|
+
exports.primitiveTypeValues = new Set([
|
|
9
|
+
'Boolean',
|
|
10
|
+
'Number',
|
|
11
|
+
'String',
|
|
12
|
+
]);
|
|
13
|
+
exports.primitiveTypeofValues = new Set(['boolean', 'number', 'string']);
|
|
14
|
+
exports.mapTypeofValueToPrimitiveType = {
|
|
15
|
+
boolean: 'Boolean',
|
|
16
|
+
number: 'Number',
|
|
17
|
+
string: 'String',
|
|
18
|
+
};
|
|
19
|
+
const getPrimitiveType = (type) => {
|
|
20
|
+
if (type === String)
|
|
21
|
+
return 'String';
|
|
22
|
+
if (type === Number)
|
|
23
|
+
return 'Number';
|
|
24
|
+
if (type === Boolean)
|
|
25
|
+
return 'Boolean';
|
|
26
|
+
};
|
|
27
|
+
exports.getPrimitiveType = getPrimitiveType;
|
|
28
|
+
const getEventFields = (prefix, target, name = 'event') => {
|
|
29
|
+
if (!target) {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
return getFieldMetadata({
|
|
33
|
+
destinationName: name,
|
|
34
|
+
type: `${name}_type`,
|
|
35
|
+
fieldProps: {
|
|
36
|
+
type: target,
|
|
37
|
+
},
|
|
38
|
+
prefix,
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
exports.getEventFields = getEventFields;
|
|
42
|
+
const getObjectMetadata = (metadata, payloadClass, prefix) => {
|
|
43
|
+
const payloadMetadata = (0, utils_1.getMetadataByKey)(payloadClass, (0, exports.createFieldName)(prefix, field_types_1.FieldProperties.payload)) || {
|
|
44
|
+
name: payloadClass.name,
|
|
45
|
+
id: payloadClass.name,
|
|
46
|
+
};
|
|
47
|
+
const properties = (0, utils_1.getMetadataPrototypeByKey)(payloadClass, (0, exports.createFieldName)(prefix, field_types_1.FieldProperties.field));
|
|
48
|
+
if (!properties?.length) {
|
|
49
|
+
throw new Error(`should include Field properties in ${payloadClass.name} class`);
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
...metadata,
|
|
53
|
+
properties,
|
|
54
|
+
type: 'Object',
|
|
55
|
+
payload: payloadMetadata,
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
const getFieldMetadata = (props) => {
|
|
59
|
+
const { fieldProps, destinationName, type, prefix } = props;
|
|
60
|
+
const metadata = {
|
|
61
|
+
destinationName,
|
|
62
|
+
name: fieldProps?.name || destinationName,
|
|
63
|
+
};
|
|
64
|
+
let primitiveType = (0, exports.getPrimitiveType)(type);
|
|
65
|
+
if (!primitiveType && fieldProps?.type) {
|
|
66
|
+
primitiveType = (0, exports.getPrimitiveType)(fieldProps.type);
|
|
67
|
+
}
|
|
68
|
+
const typeHasValue = exports.mapTypeofValueToPrimitiveType[typeof fieldProps?.type];
|
|
69
|
+
if (fieldProps?.type !== undefined) {
|
|
70
|
+
if (typeof fieldProps.type === 'function' && !primitiveType) {
|
|
71
|
+
return getObjectMetadata(metadata, fieldProps.type, prefix);
|
|
72
|
+
}
|
|
73
|
+
if (Array.isArray(fieldProps.type)) {
|
|
74
|
+
const arrayPrimitiveType = (0, exports.getPrimitiveType)(fieldProps.type[0]);
|
|
75
|
+
let items;
|
|
76
|
+
if (arrayPrimitiveType) {
|
|
77
|
+
items = {
|
|
78
|
+
type: arrayPrimitiveType,
|
|
79
|
+
name: arrayPrimitiveType,
|
|
80
|
+
destinationName: arrayPrimitiveType,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
if (typeof fieldProps.type[0] === 'function') {
|
|
85
|
+
items = getObjectMetadata({
|
|
86
|
+
name: 'Object',
|
|
87
|
+
destinationName: 'Object',
|
|
88
|
+
}, fieldProps.type[0], prefix);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
...metadata,
|
|
93
|
+
type: 'Array',
|
|
94
|
+
items,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (exports.primitiveTypeValues.has(type) ||
|
|
99
|
+
exports.primitiveTypeValues.has(primitiveType) ||
|
|
100
|
+
typeHasValue) {
|
|
101
|
+
return {
|
|
102
|
+
type: primitiveType || typeHasValue || type,
|
|
103
|
+
initialValue: typeHasValue ? fieldProps?.type : undefined,
|
|
104
|
+
...metadata,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
throw new Error(`unidentified type ${type} in ${destinationName} field`);
|
|
108
|
+
};
|
|
109
|
+
const createFieldDecorator = ({ prefix, enableInLambdaInvocation, getMetadata, }) => (props) => (target, destinationName) => {
|
|
110
|
+
if (!(0, build_env_utils_1.isBuildEnvironment)() && !enableInLambdaInvocation) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const filedKey = (0, exports.createFieldName)(prefix, field_types_1.FieldProperties.field);
|
|
114
|
+
const fields = (0, utils_1.getMetadataByKey)(target, filedKey) || [];
|
|
115
|
+
const propertyType = Reflect.getMetadata('design:type', target, destinationName).name;
|
|
116
|
+
const parentMetadata = getMetadata(props);
|
|
117
|
+
const metadata = [
|
|
118
|
+
...fields,
|
|
119
|
+
{
|
|
120
|
+
...parentMetadata,
|
|
121
|
+
...getFieldMetadata({
|
|
122
|
+
destinationName,
|
|
123
|
+
type: propertyType,
|
|
124
|
+
fieldProps: {
|
|
125
|
+
...props,
|
|
126
|
+
type: parentMetadata.forceType ?? props?.type,
|
|
127
|
+
},
|
|
128
|
+
prefix,
|
|
129
|
+
}),
|
|
130
|
+
},
|
|
131
|
+
];
|
|
132
|
+
Reflect.defineMetadata(filedKey, metadata, target);
|
|
133
|
+
};
|
|
134
|
+
exports.createFieldDecorator = createFieldDecorator;
|
|
135
|
+
const createFieldName = (prefix, type) => {
|
|
136
|
+
return `${prefix}:${type}`;
|
|
137
|
+
};
|
|
138
|
+
exports.createFieldName = createFieldName;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { PayloadMetadata } from '../payload';
|
|
2
|
+
export type BasicTypes = StringConstructor | NumberConstructor | BooleanConstructor;
|
|
3
|
+
export type AllowedTypes = String | Number | Boolean | Function | [String | Number | Boolean | Function];
|
|
4
|
+
export type AllowedTypesWithoutFunction = BasicTypes | [BasicTypes];
|
|
5
|
+
export type EnumValue = (string | number)[];
|
|
6
|
+
export interface FieldProps {
|
|
7
|
+
/**
|
|
8
|
+
* Original field name.
|
|
9
|
+
*
|
|
10
|
+
* Specifies the original name of the field as it is expected
|
|
11
|
+
* in the input or payload. This is used to map incoming data
|
|
12
|
+
* to the corresponding property in the system.
|
|
13
|
+
*/
|
|
14
|
+
name?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Field data type.
|
|
17
|
+
*
|
|
18
|
+
* Specifies the type of the field. By default, the type is inferred
|
|
19
|
+
* from the property that decorates the field. However, it can be
|
|
20
|
+
* explicitly set to a primitive type such as `String`, `Number`,
|
|
21
|
+
* `Boolean`, or to another payload type.
|
|
22
|
+
*
|
|
23
|
+
* This ensures correct parsing, validation, and serialization of the field's value.
|
|
24
|
+
*/
|
|
25
|
+
type?: AllowedTypes;
|
|
26
|
+
}
|
|
27
|
+
export interface BaseFieldMetadata {
|
|
28
|
+
name: string;
|
|
29
|
+
destinationName: string;
|
|
30
|
+
}
|
|
31
|
+
export interface StringField extends BaseFieldMetadata {
|
|
32
|
+
type: 'String';
|
|
33
|
+
initialValue?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface NumberField extends BaseFieldMetadata {
|
|
36
|
+
type: 'Number';
|
|
37
|
+
initialValue?: number;
|
|
38
|
+
}
|
|
39
|
+
export interface BooleanField extends BaseFieldMetadata {
|
|
40
|
+
type: 'Boolean';
|
|
41
|
+
initialValue?: boolean;
|
|
42
|
+
}
|
|
43
|
+
export interface ObjectField extends BaseFieldMetadata {
|
|
44
|
+
type: 'Object';
|
|
45
|
+
properties: FieldMetadata[];
|
|
46
|
+
payload: PayloadMetadata;
|
|
47
|
+
}
|
|
48
|
+
export interface ArrayField extends BaseFieldMetadata {
|
|
49
|
+
type: 'Array';
|
|
50
|
+
items: FieldMetadata;
|
|
51
|
+
}
|
|
52
|
+
export type FieldMetadata = StringField | NumberField | BooleanField | ObjectField | ArrayField;
|
|
53
|
+
export type FieldTypes = FieldMetadata['type'];
|
|
54
|
+
export type PrimitiveTypes = Extract<FieldTypes, 'String' | 'Number' | 'Boolean'>;
|
|
55
|
+
export declare enum FieldProperties {
|
|
56
|
+
field = "lafken:field",
|
|
57
|
+
payload = "lafken:payload"
|
|
58
|
+
}
|
|
59
|
+
export interface CreateFieldDecoratorProps<P extends FieldProps, M> {
|
|
60
|
+
prefix: string;
|
|
61
|
+
enableInLambdaInvocation?: boolean;
|
|
62
|
+
getMetadata: (props?: P) => Omit<M, keyof FieldMetadata> & {
|
|
63
|
+
forceType?: any;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export interface GetFieldMetadataProps {
|
|
67
|
+
type: string;
|
|
68
|
+
prefix: string;
|
|
69
|
+
destinationName: string;
|
|
70
|
+
fieldProps?: FieldProps;
|
|
71
|
+
}
|