glasswork 0.12.2 → 0.13.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.
Files changed (108) hide show
  1. package/dist/auth/abilities.d.ts +41 -0
  2. package/dist/auth/abilities.d.ts.map +1 -0
  3. package/dist/auth/abilities.js +62 -0
  4. package/dist/auth/abilities.js.map +1 -0
  5. package/dist/auth/assert.d.ts +20 -0
  6. package/dist/auth/assert.d.ts.map +1 -0
  7. package/dist/auth/assert.js +28 -0
  8. package/dist/auth/assert.js.map +1 -0
  9. package/dist/auth/better-auth-provider.d.ts +56 -0
  10. package/dist/auth/better-auth-provider.d.ts.map +1 -0
  11. package/dist/auth/better-auth-provider.js +62 -0
  12. package/dist/auth/better-auth-provider.js.map +1 -0
  13. package/dist/auth/index.d.ts +6 -0
  14. package/dist/auth/index.d.ts.map +1 -0
  15. package/dist/auth/index.js +5 -0
  16. package/dist/auth/index.js.map +1 -0
  17. package/dist/auth/middleware.d.ts +31 -0
  18. package/dist/auth/middleware.d.ts.map +1 -0
  19. package/dist/auth/middleware.js +92 -0
  20. package/dist/auth/middleware.js.map +1 -0
  21. package/dist/auth/types.d.ts +54 -0
  22. package/dist/auth/types.d.ts.map +1 -0
  23. package/dist/auth/types.js +2 -0
  24. package/dist/auth/types.js.map +1 -0
  25. package/dist/core/bootstrap.d.ts.map +1 -1
  26. package/dist/core/bootstrap.js +5 -2
  27. package/dist/core/bootstrap.js.map +1 -1
  28. package/dist/core/types.d.ts +8 -2
  29. package/dist/core/types.d.ts.map +1 -1
  30. package/dist/http/route-helpers.d.ts +52 -9
  31. package/dist/http/route-helpers.d.ts.map +1 -1
  32. package/dist/http/route-helpers.js +90 -40
  33. package/dist/http/route-helpers.js.map +1 -1
  34. package/dist/index.d.ts +8 -2
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +7 -1
  37. package/dist/index.js.map +1 -1
  38. package/dist/jobs/define-job.d.ts +34 -2
  39. package/dist/jobs/define-job.d.ts.map +1 -1
  40. package/dist/jobs/define-job.js.map +1 -1
  41. package/dist/jobs/drivers/eventbridge-scheduler-driver.d.ts +75 -0
  42. package/dist/jobs/drivers/eventbridge-scheduler-driver.d.ts.map +1 -0
  43. package/dist/jobs/drivers/eventbridge-scheduler-driver.js +135 -0
  44. package/dist/jobs/drivers/eventbridge-scheduler-driver.js.map +1 -0
  45. package/dist/jobs/drivers/mock-driver.d.ts +45 -10
  46. package/dist/jobs/drivers/mock-driver.d.ts.map +1 -1
  47. package/dist/jobs/drivers/mock-driver.js +36 -17
  48. package/dist/jobs/drivers/mock-driver.js.map +1 -1
  49. package/dist/jobs/drivers/sqs-driver.d.ts +26 -19
  50. package/dist/jobs/drivers/sqs-driver.d.ts.map +1 -1
  51. package/dist/jobs/drivers/sqs-driver.js +54 -69
  52. package/dist/jobs/drivers/sqs-driver.js.map +1 -1
  53. package/dist/jobs/errors.d.ts +8 -4
  54. package/dist/jobs/errors.d.ts.map +1 -1
  55. package/dist/jobs/errors.js +13 -7
  56. package/dist/jobs/errors.js.map +1 -1
  57. package/dist/jobs/index.d.ts +4 -3
  58. package/dist/jobs/index.d.ts.map +1 -1
  59. package/dist/jobs/index.js +3 -2
  60. package/dist/jobs/index.js.map +1 -1
  61. package/dist/jobs/job-registry.d.ts +4 -4
  62. package/dist/jobs/job-registry.d.ts.map +1 -1
  63. package/dist/jobs/job-registry.js.map +1 -1
  64. package/dist/jobs/job-service.js +1 -1
  65. package/dist/jobs/job-service.js.map +1 -1
  66. package/dist/jobs/schedule-constants.d.ts +0 -4
  67. package/dist/jobs/schedule-constants.d.ts.map +1 -1
  68. package/dist/jobs/schedule-constants.js +0 -4
  69. package/dist/jobs/schedule-constants.js.map +1 -1
  70. package/dist/jobs/scheduler.d.ts +1 -1
  71. package/dist/jobs/scheduler.d.ts.map +1 -1
  72. package/dist/jobs/scheduler.js +46 -10
  73. package/dist/jobs/scheduler.js.map +1 -1
  74. package/dist/jobs/types.d.ts +59 -50
  75. package/dist/jobs/types.d.ts.map +1 -1
  76. package/dist/jobs/utils.d.ts +0 -9
  77. package/dist/jobs/utils.d.ts.map +1 -1
  78. package/dist/jobs/utils.js +3 -23
  79. package/dist/jobs/utils.js.map +1 -1
  80. package/dist/jobs/worker.d.ts +2 -3
  81. package/dist/jobs/worker.d.ts.map +1 -1
  82. package/dist/jobs/worker.js +183 -82
  83. package/dist/jobs/worker.js.map +1 -1
  84. package/dist/list-query/casl.d.ts +18 -19
  85. package/dist/list-query/casl.d.ts.map +1 -1
  86. package/dist/list-query/casl.js +40 -46
  87. package/dist/list-query/casl.js.map +1 -1
  88. package/dist/uploads/file-upload.d.ts +44 -0
  89. package/dist/uploads/file-upload.d.ts.map +1 -0
  90. package/dist/uploads/file-upload.js +145 -0
  91. package/dist/uploads/file-upload.js.map +1 -0
  92. package/dist/uploads/index.d.ts +6 -0
  93. package/dist/uploads/index.d.ts.map +1 -0
  94. package/dist/uploads/index.js +7 -0
  95. package/dist/uploads/index.js.map +1 -0
  96. package/dist/uploads/uploads.service.d.ts +98 -0
  97. package/dist/uploads/uploads.service.d.ts.map +1 -0
  98. package/dist/uploads/uploads.service.js +134 -0
  99. package/dist/uploads/uploads.service.js.map +1 -0
  100. package/dist/uploads/validators.d.ts +41 -0
  101. package/dist/uploads/validators.d.ts.map +1 -0
  102. package/dist/uploads/validators.js +94 -0
  103. package/dist/uploads/validators.js.map +1 -0
  104. package/package.json +34 -3
  105. package/dist/email/compiler/code-generator.d.ts +0 -11
  106. package/dist/email/compiler/code-generator.d.ts.map +0 -1
  107. package/dist/email/compiler/code-generator.js +0 -319
  108. package/dist/email/compiler/code-generator.js.map +0 -1
@@ -0,0 +1,134 @@
1
+ import { DeleteObjectCommand, GetObjectCommand, PutObjectCommand, S3Client, S3ServiceException, } from '@aws-sdk/client-s3';
2
+ import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
3
+ import { NotFoundException } from '../http/errors.js';
4
+ // ============================================================================
5
+ // Constants
6
+ // ============================================================================
7
+ const DEFAULT_URL_EXPIRATION = 3600; // 1 hour
8
+ const MILLISECONDS_PER_SECOND = 1000;
9
+ // ============================================================================
10
+ // Service
11
+ // ============================================================================
12
+ /**
13
+ * Service for S3 file operations including presigned URL generation and streaming.
14
+ * Framework-agnostic - can be used anywhere TypeScript runs.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const uploadsService = new UploadsService({
19
+ * region: 'eu-west-1',
20
+ * bucketName: 'my-files-bucket',
21
+ * urlExpiration: 3600,
22
+ * });
23
+ *
24
+ * // Generate presigned upload URL
25
+ * const { uploadUrl, path } = await uploadsService.getSignedUploadUrl({
26
+ * dir: 'uploads/avatars/user123',
27
+ * fileName: 'photo.jpg',
28
+ * });
29
+ *
30
+ * // Stream file for private access
31
+ * const { body, contentType } = await uploadsService.streamFile('/uploads/docs/file.pdf');
32
+ *
33
+ * // Generate presigned download URL for private files
34
+ * const { downloadUrl } = await uploadsService.getSignedDownloadUrl('/uploads/docs/private.pdf');
35
+ * ```
36
+ */
37
+ export class UploadsService {
38
+ client;
39
+ bucketName;
40
+ urlExpiration;
41
+ constructor(config) {
42
+ this.client = new S3Client({ region: config.region });
43
+ this.bucketName = config.bucketName;
44
+ this.urlExpiration = config.urlExpiration ?? DEFAULT_URL_EXPIRATION;
45
+ }
46
+ /**
47
+ * Generate a presigned URL for uploading a file to S3.
48
+ * The URL allows direct upload from the client without going through Lambda.
49
+ */
50
+ async getSignedUploadUrl(fileConfig) {
51
+ const filePath = `${fileConfig.dir}/${fileConfig.fileName}`;
52
+ const command = new PutObjectCommand({
53
+ Bucket: this.bucketName,
54
+ Key: filePath,
55
+ });
56
+ const url = await getSignedUrl(this.client, command, {
57
+ expiresIn: this.urlExpiration,
58
+ });
59
+ return {
60
+ uploadUrl: url,
61
+ path: `/${filePath}`,
62
+ expiresAt: new Date(Date.now() + this.urlExpiration * MILLISECONDS_PER_SECOND),
63
+ };
64
+ }
65
+ /**
66
+ * Stream a file from S3 for private file access.
67
+ * Returns a readable stream that can be piped to the HTTP response.
68
+ *
69
+ * **Note**: Lambda has a 6MB response payload limit for synchronous invocations.
70
+ * For larger files, consider using presigned GET URLs with short expiration.
71
+ */
72
+ async streamFile(filePath) {
73
+ try {
74
+ const key = this.normalizeKey(filePath);
75
+ const command = new GetObjectCommand({
76
+ Bucket: this.bucketName,
77
+ Key: key,
78
+ });
79
+ const response = await this.client.send(command);
80
+ const body = response.Body;
81
+ return {
82
+ body,
83
+ contentType: response.ContentType,
84
+ contentLength: response.ContentLength,
85
+ };
86
+ }
87
+ catch (error) {
88
+ if (error instanceof S3ServiceException && error.name === 'NoSuchKey') {
89
+ throw new NotFoundException('File not found');
90
+ }
91
+ throw error;
92
+ }
93
+ }
94
+ /**
95
+ * Generate a presigned URL for downloading a file from S3.
96
+ * Use this instead of streaming for files that may exceed Lambda's 6MB response limit.
97
+ *
98
+ * @param filePath - Relative path to the file (e.g., '/uploads/docs/file.pdf')
99
+ * @param expiresIn - Optional expiration in seconds (default: service urlExpiration)
100
+ */
101
+ async getSignedDownloadUrl(filePath, expiresIn) {
102
+ const key = this.normalizeKey(filePath);
103
+ const expiration = expiresIn ?? this.urlExpiration;
104
+ const command = new GetObjectCommand({
105
+ Bucket: this.bucketName,
106
+ Key: key,
107
+ });
108
+ const url = await getSignedUrl(this.client, command, {
109
+ expiresIn: expiration,
110
+ });
111
+ return {
112
+ downloadUrl: url,
113
+ expiresAt: new Date(Date.now() + expiration * MILLISECONDS_PER_SECOND),
114
+ };
115
+ }
116
+ /**
117
+ * Delete a file from S3 by path.
118
+ */
119
+ async deleteFile(filePath) {
120
+ const key = this.normalizeKey(filePath);
121
+ const command = new DeleteObjectCommand({
122
+ Bucket: this.bucketName,
123
+ Key: key,
124
+ });
125
+ await this.client.send(command);
126
+ }
127
+ /**
128
+ * Normalize file path to S3 key format (strip leading slashes).
129
+ */
130
+ normalizeKey(filePath) {
131
+ return filePath.replace(/^\/+/, '').replace(/\/+/g, '/');
132
+ }
133
+ }
134
+ //# sourceMappingURL=uploads.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uploads.service.js","sourceRoot":"","sources":["../../src/uploads/uploads.service.ts"],"names":[],"mappings":"AACA,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,QAAQ,EACR,kBAAkB,GACnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AA+CtD,+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;IACR,MAAM,CAAW;IACjB,UAAU,CAAS;IACnB,aAAa,CAAS;IAEvC,YAAY,MAA4B;QACtC,IAAI,CAAC,MAAM,GAAG,IAAI,QAAQ,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QACtD,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,CAAC,UAA4B;QACnD,MAAM,QAAQ,GAAG,GAAG,UAAU,CAAC,GAAG,IAAI,UAAU,CAAC,QAAQ,EAAE,CAAC;QAC5D,MAAM,OAAO,GAAG,IAAI,gBAAgB,CAAC;YACnC,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,SAAS,EAAE,IAAI,CAAC,aAAa;SAC9B,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,IAAI,CAAC,aAAa,GAAG,uBAAuB,CAAC;SAC/E,CAAC;IACJ,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,UAAU,CAAC,QAAgB;QAC/B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;YACxC,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;QAElB,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;QACxC,MAAM,UAAU,GAAG,SAAS,IAAI,IAAI,CAAC,aAAa,CAAC;QACnD,MAAM,OAAO,GAAG,IAAI,gBAAgB,CAAC;YACnC,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,CAAC,QAAgB;QAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;QACxC,MAAM,OAAO,GAAG,IAAI,mBAAmB,CAAC;YACtC,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;;OAEG;IACK,YAAY,CAAC,QAAgB;QACnC,OAAO,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3D,CAAC;CACF"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * File name validation with extension check AND sanitization.
3
+ * Validates that the file has an allowed extension, then transforms it to a URL-safe slug.
4
+ *
5
+ * @param allowedExtensions - Extensions to allow (e.g., ['.jpg', '.png', '.pdf'])
6
+ * @param errorMessage - Custom error message for invalid file type
7
+ * @returns Valibot schema that validates and sanitizes the filename
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * // Define allowed extensions per use case
12
+ * const ALLOWED_IMAGE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.webp'] as const;
13
+ * const ALLOWED_DOCUMENT_EXTENSIONS = ['.pdf', '.docx'] as const;
14
+ *
15
+ * // Use in schema
16
+ * const GetUploadUrlQuery = v.object({
17
+ * name: sanitizedFileName(ALLOWED_IMAGE_EXTENSIONS),
18
+ * });
19
+ *
20
+ * // Input: "My Photo (1).PNG" → Output: "my-photo-1.png"
21
+ * // Input: "test.txt" → ValidationError (extension not allowed)
22
+ * ```
23
+ */
24
+ export declare const sanitizedFileName: (allowedExtensions: readonly string[], errorMessage?: string) => import("valibot").SchemaWithPipe<readonly [import("valibot").StringSchema<undefined>, import("valibot").CheckAction<string, string>, import("valibot").TransformAction<string, string>]>;
25
+ /**
26
+ * File name validation with extension check (no transformation).
27
+ * Use when you want to validate the extension but keep the original filename.
28
+ *
29
+ * @param allowedExtensions - Extensions to allow (e.g., ['.jpg', '.png'])
30
+ * @param errorMessage - Custom error message for invalid file type
31
+ * @returns Valibot schema that validates the filename extension
32
+ *
33
+ * @example
34
+ * ```typescript
35
+ * const schema = v.object({
36
+ * name: fileNameWithExtension(['.pdf', '.docx']),
37
+ * });
38
+ * ```
39
+ */
40
+ export declare const fileNameWithExtension: (allowedExtensions: readonly string[], errorMessage?: string) => import("valibot").SchemaWithPipe<readonly [import("valibot").StringSchema<undefined>, import("valibot").CheckAction<string, string>]>;
41
+ //# sourceMappingURL=validators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../../src/uploads/validators.ts"],"names":[],"mappings":"AAmBA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,iBAAiB,GAAI,mBAAmB,SAAS,MAAM,EAAE,EAAE,eAAe,MAAM,6LAwC1F,CAAC;AAEJ;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,qBAAqB,GAChC,mBAAmB,SAAS,MAAM,EAAE,EACpC,eAAe,MAAM,0IAoBpB,CAAC"}
@@ -0,0 +1,94 @@
1
+ import { check, pipe, string, transform } from 'valibot';
2
+ // ============================================================================
3
+ // File Name Validators
4
+ // ============================================================================
5
+ /**
6
+ * Slugify a filename base for URL-safe paths.
7
+ * Converts to lowercase, removes diacritics, replaces non-alphanumeric with hyphens, trims hyphens.
8
+ */
9
+ function slugify(baseName) {
10
+ return baseName
11
+ .normalize('NFD')
12
+ .replace(/[\u0300-\u036f]/g, '')
13
+ .toLowerCase()
14
+ .replace(/[^a-z0-9]+/g, '-')
15
+ .replace(/^-+|-+$/g, '');
16
+ }
17
+ /**
18
+ * File name validation with extension check AND sanitization.
19
+ * Validates that the file has an allowed extension, then transforms it to a URL-safe slug.
20
+ *
21
+ * @param allowedExtensions - Extensions to allow (e.g., ['.jpg', '.png', '.pdf'])
22
+ * @param errorMessage - Custom error message for invalid file type
23
+ * @returns Valibot schema that validates and sanitizes the filename
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * // Define allowed extensions per use case
28
+ * const ALLOWED_IMAGE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.webp'] as const;
29
+ * const ALLOWED_DOCUMENT_EXTENSIONS = ['.pdf', '.docx'] as const;
30
+ *
31
+ * // Use in schema
32
+ * const GetUploadUrlQuery = v.object({
33
+ * name: sanitizedFileName(ALLOWED_IMAGE_EXTENSIONS),
34
+ * });
35
+ *
36
+ * // Input: "My Photo (1).PNG" → Output: "my-photo-1.png"
37
+ * // Input: "test.txt" → ValidationError (extension not allowed)
38
+ * ```
39
+ */
40
+ export const sanitizedFileName = (allowedExtensions, errorMessage) => pipe(string(), check((input) => {
41
+ const lastDotIndex = input.lastIndexOf('.');
42
+ if (lastDotIndex === -1 || lastDotIndex === input.length - 1) {
43
+ return false;
44
+ }
45
+ // Check for double extensions like .tar.gz
46
+ const parts = input.toLowerCase().split('.');
47
+ const ext1 = `.${parts[parts.length - 1]}`;
48
+ const ext2 = parts.length > 2 ? `.${parts[parts.length - 2]}.${parts[parts.length - 1]}` : null;
49
+ return !!(allowedExtensions.includes(ext1) || (ext2 && allowedExtensions.includes(ext2)));
50
+ }, errorMessage ?? `Invalid file type. Allowed: ${allowedExtensions.join(', ')}`), transform((input) => {
51
+ const parts = input.split('.');
52
+ // Determine which extension to use based on allowed list
53
+ let extension = parts[parts.length - 1].toLowerCase();
54
+ let baseName = parts.slice(0, parts.length - 1).join('.');
55
+ if (parts.length > 2) {
56
+ const doubleExt = `.${parts[parts.length - 2]}.${parts[parts.length - 1]}`.toLowerCase();
57
+ if (allowedExtensions.includes(doubleExt)) {
58
+ extension = parts
59
+ .slice(parts.length - 2)
60
+ .join('.')
61
+ .toLowerCase();
62
+ baseName = parts.slice(0, parts.length - 2).join('.');
63
+ }
64
+ }
65
+ const slugifiedName = slugify(baseName);
66
+ return slugifiedName ? `${slugifiedName}.${extension}` : `file.${extension}`;
67
+ }));
68
+ /**
69
+ * File name validation with extension check (no transformation).
70
+ * Use when you want to validate the extension but keep the original filename.
71
+ *
72
+ * @param allowedExtensions - Extensions to allow (e.g., ['.jpg', '.png'])
73
+ * @param errorMessage - Custom error message for invalid file type
74
+ * @returns Valibot schema that validates the filename extension
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * const schema = v.object({
79
+ * name: fileNameWithExtension(['.pdf', '.docx']),
80
+ * });
81
+ * ```
82
+ */
83
+ export const fileNameWithExtension = (allowedExtensions, errorMessage) => pipe(string(), check((input) => {
84
+ const lastDotIndex = input.lastIndexOf('.');
85
+ if (lastDotIndex === -1 || lastDotIndex === input.length - 1) {
86
+ return false;
87
+ }
88
+ const lowerInput = input.toLowerCase();
89
+ const parts = lowerInput.split('.');
90
+ const ext1 = `.${parts[parts.length - 1]}`;
91
+ const ext2 = parts.length > 2 ? `.${parts[parts.length - 2]}.${parts[parts.length - 1]}` : null;
92
+ return !!(allowedExtensions.includes(ext1) || (ext2 && allowedExtensions.includes(ext2)));
93
+ }, errorMessage ?? `Invalid file type. Allowed: ${allowedExtensions.join(', ')}`));
94
+ //# sourceMappingURL=validators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validators.js","sourceRoot":"","sources":["../../src/uploads/validators.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzD,+EAA+E;AAC/E,uBAAuB;AACvB,+EAA+E;AAE/E;;;GAGG;AACH,SAAS,OAAO,CAAC,QAAgB;IAC/B,OAAO,QAAQ;SACZ,SAAS,CAAC,KAAK,CAAC;SAChB,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC;SAC/B,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;AAC7B,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,iBAAoC,EAAE,YAAqB,EAAE,EAAE,CAC/F,IAAI,CACF,MAAM,EAAE,EACR,KAAK,CACH,CAAC,KAAK,EAAE,EAAE;IACR,MAAM,YAAY,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAC5C,IAAI,YAAY,KAAK,CAAC,CAAC,IAAI,YAAY,KAAK,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7D,OAAO,KAAK,CAAC;IACf,CAAC;IACD,2CAA2C;IAC3C,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7C,MAAM,IAAI,GAAG,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC;IAC3C,MAAM,IAAI,GACR,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAErF,OAAO,CAAC,CAAC,CAAC,iBAAiB,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,iBAAiB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAC5F,CAAC,EACD,YAAY,IAAI,+BAA+B,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC9E,EACD,SAAS,CAAC,CAAC,KAAK,EAAU,EAAE;IAC1B,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAE/B,yDAAyD;IACzD,IAAI,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IACtD,IAAI,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAE1D,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,MAAM,SAAS,GAAG,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC;QACzF,IAAI,iBAAiB,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YAC1C,SAAS,GAAG,KAAK;iBACd,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;iBACvB,IAAI,CAAC,GAAG,CAAC;iBACT,WAAW,EAAE,CAAC;YACjB,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACxD,CAAC;IACH,CAAC;IAED,MAAM,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IACxC,OAAO,aAAa,CAAC,CAAC,CAAC,GAAG,aAAa,IAAI,SAAS,EAAE,CAAC,CAAC,CAAC,QAAQ,SAAS,EAAE,CAAC;AAC/E,CAAC,CAAC,CACH,CAAC;AAEJ;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,iBAAoC,EACpC,YAAqB,EACrB,EAAE,CACF,IAAI,CACF,MAAM,EAAE,EACR,KAAK,CACH,CAAC,KAAK,EAAE,EAAE;IACR,MAAM,YAAY,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAC5C,IAAI,YAAY,KAAK,CAAC,CAAC,IAAI,YAAY,KAAK,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7D,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;IACvC,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACpC,MAAM,IAAI,GAAG,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC;IAC3C,MAAM,IAAI,GACR,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAErF,OAAO,CAAC,CAAC,CAAC,iBAAiB,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,iBAAiB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAC5F,CAAC,EACD,YAAY,IAAI,+BAA+B,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC9E,CACF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glasswork",
3
- "version": "0.12.2",
3
+ "version": "0.13.1",
4
4
  "description": "A transparent, Lambda-optimized web framework for building OpenAPI-compliant REST APIs.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -52,6 +52,7 @@
52
52
  "test:coverage": "vitest run --coverage",
53
53
  "lint": "biome check . --write",
54
54
  "lint:ci": "biome check .",
55
+ "typecheck": "tsc --noEmit",
55
56
  "prepublishOnly": "npm run build && npm test",
56
57
  "docs:dev": "vitepress dev docs",
57
58
  "docs:build": "vitepress build docs",
@@ -63,19 +64,31 @@
63
64
  "peerDependencies": {
64
65
  "@aws-sdk/client-cloudwatch": "^3.900.0",
65
66
  "@aws-sdk/client-dynamodb": "^3.900.0",
67
+ "@aws-sdk/client-s3": "^3.900.0",
68
+ "@aws-sdk/client-scheduler": "^3.900.0",
66
69
  "@aws-sdk/client-sesv2": "^3.900.0",
70
+ "@aws-sdk/client-sqs": "^3.900.0",
67
71
  "@aws-sdk/client-ssm": "^3.900.0",
68
72
  "@aws-sdk/lib-dynamodb": "^3.900.0",
73
+ "@aws-sdk/s3-request-presigner": "^3.900.0",
74
+ "@casl/ability": "^6.7.3",
69
75
  "@casl/prisma": "^1.4.0",
70
76
  "@hono/swagger-ui": "^0.5.0",
71
77
  "@prisma/client": "^6.0.0",
72
78
  "awilix": "^12.0.0",
79
+ "better-auth": "^1.0.0",
73
80
  "dotenv": "^16.0.0",
74
81
  "hono": "^4.9.0",
75
82
  "hono-openapi": "^1.0.0",
76
83
  "valibot": "^1.0.0"
77
84
  },
78
85
  "peerDependenciesMeta": {
86
+ "@aws-sdk/client-scheduler": {
87
+ "optional": true
88
+ },
89
+ "@aws-sdk/client-sqs": {
90
+ "optional": true
91
+ },
79
92
  "@aws-sdk/client-cloudwatch": {
80
93
  "optional": true
81
94
  },
@@ -91,6 +104,12 @@
91
104
  "@aws-sdk/lib-dynamodb": {
92
105
  "optional": true
93
106
  },
107
+ "@aws-sdk/client-s3": {
108
+ "optional": true
109
+ },
110
+ "@aws-sdk/s3-request-presigner": {
111
+ "optional": true
112
+ },
94
113
  "@casl/prisma": {
95
114
  "optional": true
96
115
  },
@@ -100,6 +119,12 @@
100
119
  "@prisma/client": {
101
120
  "optional": true
102
121
  },
122
+ "@casl/ability": {
123
+ "optional": true
124
+ },
125
+ "better-auth": {
126
+ "optional": true
127
+ },
103
128
  "dotenv": {
104
129
  "optional": true
105
130
  }
@@ -107,20 +132,25 @@
107
132
  "devDependencies": {
108
133
  "@aws-sdk/client-cloudwatch": "^3.948.0",
109
134
  "@aws-sdk/client-dynamodb": "^3.948.0",
135
+ "@aws-sdk/client-s3": "^3.948.0",
136
+ "@aws-sdk/client-scheduler": "^3.948.0",
110
137
  "@aws-sdk/client-sesv2": "^3.948.0",
138
+ "@aws-sdk/client-sqs": "^3.948.0",
111
139
  "@aws-sdk/client-ssm": "^3.948.0",
112
140
  "@aws-sdk/lib-dynamodb": "^3.948.0",
141
+ "@aws-sdk/s3-request-presigner": "^3.948.0",
113
142
  "@biomejs/biome": "^2.2.5",
114
143
  "@casl/ability": "^6.7.3",
115
144
  "@casl/prisma": "^1.6.0",
116
145
  "@hono/swagger-ui": "^0.5.2",
117
146
  "@prisma/client": "^7.1.0",
147
+ "@types/aws-lambda": "^8.10.159",
118
148
  "@types/html-to-text": "^9.0.4",
119
149
  "@types/node": "^22.19.1",
120
150
  "@vitest/coverage-v8": "^4.0.15",
121
151
  "awilix": "^12.0.5",
122
152
  "dotenv": "^17.2.3",
123
- "hono": "^4.11.0",
153
+ "hono": "^4.11.3",
124
154
  "hono-openapi": "^1.1.2",
125
155
  "html-to-text": "^9.0.5",
126
156
  "mermaid": "^11.12.2",
@@ -139,6 +169,7 @@
139
169
  "@standard-community/standard-openapi": "^0.2.9",
140
170
  "@valibot/to-json-schema": "^1.4.0",
141
171
  "deepmerge": "^4.3.1",
172
+ "nanoid": "^5.0.0",
142
173
  "openapi-types": "^12.1.3"
143
174
  }
144
- }
175
+ }
@@ -1,11 +0,0 @@
1
- import type { InferredType, Token } from './types.js';
2
- /**
3
- * Generates TypeScript code from tokenized template.
4
- * Converts control flow markers and variables to JS template literals.
5
- */
6
- export declare function generateCode(name: string, tokens: Token[], types: Map<string, InferredType>, html: string): string;
7
- /**
8
- * Alternative code generator that produces cleaner output for simple templates
9
- */
10
- export declare function generateCodeWithContext(name: string, compiledHtml: string, tokens: Token[], types: Map<string, InferredType>): string;
11
- //# sourceMappingURL=code-generator.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"code-generator.d.ts","sourceRoot":"","sources":["../../../src/email/compiler/code-generator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEtD;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,KAAK,EAAE,EACf,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,EAChC,IAAI,EAAE,MAAM,GACX,MAAM,CAgDR;AAoID;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,KAAK,EAAE,EACf,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,GAC/B,MAAM,CAgDR"}