@opensaas/stack-storage 0.3.0 → 0.5.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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +93 -0
- package/dist/fields/index.d.ts +6 -6
- package/dist/fields/index.d.ts.map +1 -1
- package/dist/fields/index.js +12 -2
- package/dist/fields/index.js.map +1 -1
- package/package.json +9 -9
- package/src/fields/index.ts +33 -14
package/.turbo/turbo-build.log
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,98 @@
|
|
|
1
1
|
# @opensaas/stack-storage
|
|
2
2
|
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
## 0.4.0
|
|
6
|
+
|
|
7
|
+
### Patch Changes
|
|
8
|
+
|
|
9
|
+
- [#172](https://github.com/OpenSaasAU/stack/pull/172) [`929a2a9`](https://github.com/OpenSaasAU/stack/commit/929a2a9a2dfa80b1d973d259dd87828d644ea58d) Thanks [@list<Lists.User.TypeInfo>({](https://github.com/list<Lists.User.TypeInfo>({), [@list<Lists.User.TypeInfo>({](https://github.com/list<Lists.User.TypeInfo>({)! - Improve TypeScript type inference for field configs and list-level hooks by automatically passing TypeInfo from list level down
|
|
10
|
+
|
|
11
|
+
This change eliminates the need to manually specify type parameters on field builders when using features like virtual fields, and fixes a critical bug where list-level hooks weren't receiving properly typed parameters.
|
|
12
|
+
|
|
13
|
+
## Field Type Inference Improvements
|
|
14
|
+
|
|
15
|
+
Previously, users had to write `virtual<Lists.User.TypeInfo>({...})` to get proper type inference. Now TypeScript automatically infers the correct types from the list-level type parameter.
|
|
16
|
+
|
|
17
|
+
**Example:**
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
// Before
|
|
21
|
+
|
|
22
|
+
fields: {
|
|
23
|
+
displayName: virtual<Lists.User.TypeInfo>({
|
|
24
|
+
type: 'string',
|
|
25
|
+
hooks: {
|
|
26
|
+
resolveOutput: ({ item }) => `${item.name} (${item.email})`,
|
|
27
|
+
},
|
|
28
|
+
}),
|
|
29
|
+
},
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
// After
|
|
33
|
+
|
|
34
|
+
fields: {
|
|
35
|
+
displayName: virtual({
|
|
36
|
+
type: 'string',
|
|
37
|
+
hooks: {
|
|
38
|
+
resolveOutput: ({ item }) => `${item.name} (${item.email})`,
|
|
39
|
+
},
|
|
40
|
+
}),
|
|
41
|
+
},
|
|
42
|
+
})
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## List-Level Hooks Type Inference Fix
|
|
46
|
+
|
|
47
|
+
Fixed a critical type parameter mismatch where `Hooks<TTypeInfo>` was passing the entire TypeInfo object as the first parameter instead of properly destructuring it into three required parameters:
|
|
48
|
+
1. `TOutput` - The item type (what's stored in DB)
|
|
49
|
+
2. `TCreateInput` - Prisma create input type
|
|
50
|
+
3. `TUpdateInput` - Prisma update input type
|
|
51
|
+
|
|
52
|
+
**Impact:**
|
|
53
|
+
- `resolveInput` now receives proper Prisma input types (e.g., `PostCreateInput`, `PostUpdateInput`)
|
|
54
|
+
- `validateInput` has access to properly typed input data
|
|
55
|
+
- `beforeOperation` and `afterOperation` have correct item types
|
|
56
|
+
- All list-level hook callbacks now get full IntelliSense and type checking
|
|
57
|
+
|
|
58
|
+
**Example:**
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
Post: list<Lists.Post.TypeInfo>({
|
|
62
|
+
fields: { title: text(), content: text() },
|
|
63
|
+
hooks: {
|
|
64
|
+
resolveInput: async ({ operation, resolvedData }) => {
|
|
65
|
+
// ✅ resolvedData is now properly typed as PostCreateInput or PostUpdateInput
|
|
66
|
+
// ✅ Full autocomplete for title, content, etc.
|
|
67
|
+
if (operation === 'create') {
|
|
68
|
+
console.log(resolvedData.title) // TypeScript knows this is string | undefined
|
|
69
|
+
}
|
|
70
|
+
return resolvedData
|
|
71
|
+
},
|
|
72
|
+
beforeOperation: async ({ operation, item }) => {
|
|
73
|
+
// ✅ item is now properly typed as Post with all fields
|
|
74
|
+
if (operation === 'update' && item) {
|
|
75
|
+
console.log(item.title) // TypeScript knows this is string
|
|
76
|
+
console.log(item.createdAt) // TypeScript knows this is Date
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
})
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Breaking Changes
|
|
84
|
+
- Field types now accept full `TTypeInfo extends TypeInfo` instead of just `TItem`
|
|
85
|
+
- `FieldsWithItemType` utility replaced with `FieldsWithTypeInfo`
|
|
86
|
+
- All field builders updated to use new type signature
|
|
87
|
+
- List-level hooks now receive properly typed parameters (may reveal existing type errors)
|
|
88
|
+
|
|
89
|
+
## Benefits
|
|
90
|
+
- ✨ Cleaner code without manual type parameter repetition
|
|
91
|
+
- 🎯 Better type inference in both field-level and list-level hooks
|
|
92
|
+
- 🔄 Consistent type flow from list configuration down to individual fields
|
|
93
|
+
- 🛡️ Maintained full type safety with improved DX
|
|
94
|
+
- 💡 Full IntelliSense support in all hook callbacks
|
|
95
|
+
|
|
3
96
|
## 0.3.0
|
|
4
97
|
|
|
5
98
|
## 0.2.0
|
package/dist/fields/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type { BaseFieldConfig } from '@opensaas/stack-core';
|
|
1
|
+
import type { BaseFieldConfig, TypeInfo } from '@opensaas/stack-core';
|
|
2
2
|
import type { ComponentType } from 'react';
|
|
3
|
-
import type {
|
|
3
|
+
import type { ImageTransformationConfig } from '../config/types.js';
|
|
4
4
|
import type { FileValidationOptions } from '../utils/upload.js';
|
|
5
5
|
/**
|
|
6
6
|
* File field configuration
|
|
7
7
|
*/
|
|
8
|
-
export interface FileFieldConfig extends
|
|
8
|
+
export interface FileFieldConfig<TTypeInfo extends TypeInfo = TypeInfo> extends BaseFieldConfig<TTypeInfo> {
|
|
9
9
|
type: 'file';
|
|
10
10
|
/** Name of the storage provider from config.storage */
|
|
11
11
|
storage: string;
|
|
@@ -34,7 +34,7 @@ export interface FileFieldConfig extends BaseFieldConfig<FileMetadata | null, Fi
|
|
|
34
34
|
/**
|
|
35
35
|
* Image field configuration
|
|
36
36
|
*/
|
|
37
|
-
export interface ImageFieldConfig extends
|
|
37
|
+
export interface ImageFieldConfig<TTypeInfo extends TypeInfo = TypeInfo> extends BaseFieldConfig<TTypeInfo> {
|
|
38
38
|
type: 'image';
|
|
39
39
|
/** Name of the storage provider from config.storage */
|
|
40
40
|
storage: string;
|
|
@@ -84,7 +84,7 @@ export interface ImageFieldConfig extends BaseFieldConfig<ImageMetadata | null,
|
|
|
84
84
|
* }
|
|
85
85
|
* ```
|
|
86
86
|
*/
|
|
87
|
-
export declare function file(options: Omit<FileFieldConfig
|
|
87
|
+
export declare function file<TTypeInfo extends TypeInfo = TypeInfo>(options: Omit<FileFieldConfig<TTypeInfo>, 'type'>): FileFieldConfig<TTypeInfo>;
|
|
88
88
|
/**
|
|
89
89
|
* Creates an image upload field with optional transformations
|
|
90
90
|
*
|
|
@@ -107,5 +107,5 @@ export declare function file(options: Omit<FileFieldConfig, 'type'>): FileFieldC
|
|
|
107
107
|
* }
|
|
108
108
|
* ```
|
|
109
109
|
*/
|
|
110
|
-
export declare function image(options: Omit<ImageFieldConfig
|
|
110
|
+
export declare function image<TTypeInfo extends TypeInfo = TypeInfo>(options: Omit<ImageFieldConfig<TTypeInfo>, 'type'>): ImageFieldConfig<TTypeInfo>;
|
|
111
111
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fields/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fields/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAErE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAC1C,OAAO,KAAK,EAA+B,yBAAyB,EAAE,MAAM,oBAAoB,CAAA;AAChG,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAE/D;;GAEG;AACH,MAAM,WAAW,eAAe,CAC9B,SAAS,SAAS,QAAQ,GAAG,QAAQ,CACrC,SAAQ,eAAe,CAAC,SAAS,CAAC;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,uDAAuD;IACvD,OAAO,EAAE,MAAM,CAAA;IACf,8BAA8B;IAC9B,UAAU,CAAC,EAAE,qBAAqB,CAAA;IAClC,oEAAoE;IACpE,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,6EAA6E;IAC7E,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,iBAAiB;IACjB,EAAE,CAAC,EAAE;QACH,uDAAuD;QACvD,SAAS,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAA;QAClC,2DAA2D;QAC3D,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,0BAA0B;QAC1B,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,sCAAsC;QACtC,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,uBAAuB;QACvB,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,wDAAwD;QACxD,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KACvB,CAAA;CACF;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB,CAC/B,SAAS,SAAS,QAAQ,GAAG,QAAQ,CACrC,SAAQ,eAAe,CAAC,SAAS,CAAC;IAClC,IAAI,EAAE,OAAO,CAAA;IACb,uDAAuD;IACvD,OAAO,EAAE,MAAM,CAAA;IACf,kDAAkD;IAClD,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,yBAAyB,CAAC,CAAA;IAC3D,8BAA8B;IAC9B,UAAU,CAAC,EAAE,qBAAqB,CAAA;IAClC,oEAAoE;IACpE,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,6EAA6E;IAC7E,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,iBAAiB;IACjB,EAAE,CAAC,EAAE;QACH,uDAAuD;QACvD,SAAS,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAA;QAClC,2DAA2D;QAC3D,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,0BAA0B;QAC1B,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,sCAAsC;QACtC,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,uBAAuB;QACvB,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,yBAAyB;QACzB,WAAW,CAAC,EAAE,OAAO,CAAA;QACrB,qCAAqC;QACrC,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,wDAAwD;QACxD,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KACvB,CAAA;CACF;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,IAAI,CAAC,SAAS,SAAS,QAAQ,GAAG,QAAQ,EACxD,OAAO,EAAE,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,GAChD,eAAe,CAAC,SAAS,CAAC,CA6G5B;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,KAAK,CAAC,SAAS,SAAS,QAAQ,GAAG,QAAQ,EACzD,OAAO,EAAE,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,GACjD,gBAAgB,CAAC,SAAS,CAAC,CAsI7B"}
|
package/dist/fields/index.js
CHANGED
|
@@ -18,10 +18,12 @@ import { z } from 'zod';
|
|
|
18
18
|
* ```
|
|
19
19
|
*/
|
|
20
20
|
export function file(options) {
|
|
21
|
+
const { hooks: userHooks, ...restOptions } = options;
|
|
21
22
|
const fieldConfig = {
|
|
22
23
|
type: 'file',
|
|
23
|
-
...
|
|
24
|
+
...restOptions,
|
|
24
25
|
hooks: {
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Field builder hooks are generic and resolved at runtime
|
|
25
27
|
resolveInput: async ({ inputValue, context, item, fieldName }) => {
|
|
26
28
|
// If null/undefined, return as-is (deletion or no change)
|
|
27
29
|
if (inputValue === null || inputValue === undefined) {
|
|
@@ -62,6 +64,7 @@ export function file(options) {
|
|
|
62
64
|
// Unknown type - return as-is and let validation catch it
|
|
63
65
|
return inputValue;
|
|
64
66
|
},
|
|
67
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Field builder hooks are generic and resolved at runtime
|
|
65
68
|
afterOperation: async ({ operation, item, fieldName, context }) => {
|
|
66
69
|
// Only cleanup on delete if enabled
|
|
67
70
|
if (operation === 'delete' && fieldConfig.cleanupOnDelete) {
|
|
@@ -77,6 +80,8 @@ export function file(options) {
|
|
|
77
80
|
}
|
|
78
81
|
}
|
|
79
82
|
},
|
|
83
|
+
// Merge with user-provided hooks if any
|
|
84
|
+
...userHooks,
|
|
80
85
|
},
|
|
81
86
|
getZodSchema: (_fieldName, _operation) => {
|
|
82
87
|
// File metadata follows the FileMetadata schema
|
|
@@ -130,10 +135,12 @@ export function file(options) {
|
|
|
130
135
|
* ```
|
|
131
136
|
*/
|
|
132
137
|
export function image(options) {
|
|
138
|
+
const { hooks: userHooks, ...restOptions } = options;
|
|
133
139
|
const fieldConfig = {
|
|
134
140
|
type: 'image',
|
|
135
|
-
...
|
|
141
|
+
...restOptions,
|
|
136
142
|
hooks: {
|
|
143
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Field builder hooks are generic and resolved at runtime
|
|
137
144
|
resolveInput: async ({ inputValue, context, item, fieldName }) => {
|
|
138
145
|
// If null/undefined, return as-is (deletion or no change)
|
|
139
146
|
if (inputValue === null || inputValue === undefined) {
|
|
@@ -179,6 +186,7 @@ export function image(options) {
|
|
|
179
186
|
// Unknown type - return as-is and let validation catch it
|
|
180
187
|
return inputValue;
|
|
181
188
|
},
|
|
189
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Field builder hooks are generic and resolved at runtime
|
|
182
190
|
afterOperation: async ({ operation, item, fieldName, context }) => {
|
|
183
191
|
// Only cleanup on delete if enabled
|
|
184
192
|
if (operation === 'delete' && fieldConfig.cleanupOnDelete) {
|
|
@@ -194,6 +202,8 @@ export function image(options) {
|
|
|
194
202
|
}
|
|
195
203
|
}
|
|
196
204
|
},
|
|
205
|
+
// Merge with user-provided hooks if any
|
|
206
|
+
...userHooks,
|
|
197
207
|
},
|
|
198
208
|
getZodSchema: (_fieldName, _operation) => {
|
|
199
209
|
// Image metadata follows the ImageMetadata schema (extends FileMetadata)
|
package/dist/fields/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/fields/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/fields/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opensaas/stack-storage",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "File and image upload field types with pluggable storage providers for OpenSaas Stack",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -24,18 +24,18 @@
|
|
|
24
24
|
"main": "./dist/index.js",
|
|
25
25
|
"types": "./dist/index.d.ts",
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"mime-types": "^3.0.
|
|
28
|
-
"sharp": "^0.34.
|
|
29
|
-
"zod": "^4.1.
|
|
27
|
+
"mime-types": "^3.0.2",
|
|
28
|
+
"sharp": "^0.34.5",
|
|
29
|
+
"zod": "^4.1.13"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@types/mime-types": "^3.0.1",
|
|
33
|
-
"@types/node": "^24.
|
|
34
|
-
"@types/react": "^19.2.
|
|
35
|
-
"@vitest/coverage-v8": "^4.0.
|
|
33
|
+
"@types/node": "^24.10.1",
|
|
34
|
+
"@types/react": "^19.2.7",
|
|
35
|
+
"@vitest/coverage-v8": "^4.0.15",
|
|
36
36
|
"typescript": "^5.9.3",
|
|
37
|
-
"vitest": "^4.0.
|
|
38
|
-
"@opensaas/stack-core": "0.
|
|
37
|
+
"vitest": "^4.0.15",
|
|
38
|
+
"@opensaas/stack-core": "0.5.0"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"@opensaas/stack-core": "^0"
|
package/src/fields/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BaseFieldConfig } from '@opensaas/stack-core'
|
|
1
|
+
import type { BaseFieldConfig, TypeInfo } from '@opensaas/stack-core'
|
|
2
2
|
import { z } from 'zod'
|
|
3
3
|
import type { ComponentType } from 'react'
|
|
4
4
|
import type { FileMetadata, ImageMetadata, ImageTransformationConfig } from '../config/types.js'
|
|
@@ -7,7 +7,9 @@ import type { FileValidationOptions } from '../utils/upload.js'
|
|
|
7
7
|
/**
|
|
8
8
|
* File field configuration
|
|
9
9
|
*/
|
|
10
|
-
export interface FileFieldConfig
|
|
10
|
+
export interface FileFieldConfig<
|
|
11
|
+
TTypeInfo extends TypeInfo = TypeInfo,
|
|
12
|
+
> extends BaseFieldConfig<TTypeInfo> {
|
|
11
13
|
type: 'file'
|
|
12
14
|
/** Name of the storage provider from config.storage */
|
|
13
15
|
storage: string
|
|
@@ -37,8 +39,9 @@ export interface FileFieldConfig extends BaseFieldConfig<FileMetadata | null, Fi
|
|
|
37
39
|
/**
|
|
38
40
|
* Image field configuration
|
|
39
41
|
*/
|
|
40
|
-
export interface ImageFieldConfig
|
|
41
|
-
extends
|
|
42
|
+
export interface ImageFieldConfig<
|
|
43
|
+
TTypeInfo extends TypeInfo = TypeInfo,
|
|
44
|
+
> extends BaseFieldConfig<TTypeInfo> {
|
|
42
45
|
type: 'image'
|
|
43
46
|
/** Name of the storage provider from config.storage */
|
|
44
47
|
storage: string
|
|
@@ -89,13 +92,18 @@ export interface ImageFieldConfig
|
|
|
89
92
|
* }
|
|
90
93
|
* ```
|
|
91
94
|
*/
|
|
92
|
-
export function file
|
|
93
|
-
|
|
95
|
+
export function file<TTypeInfo extends TypeInfo = TypeInfo>(
|
|
96
|
+
options: Omit<FileFieldConfig<TTypeInfo>, 'type'>,
|
|
97
|
+
): FileFieldConfig<TTypeInfo> {
|
|
98
|
+
const { hooks: userHooks, ...restOptions } = options
|
|
99
|
+
|
|
100
|
+
const fieldConfig: FileFieldConfig<TTypeInfo> = {
|
|
94
101
|
type: 'file',
|
|
95
|
-
...
|
|
102
|
+
...restOptions,
|
|
96
103
|
|
|
97
104
|
hooks: {
|
|
98
|
-
|
|
105
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Field builder hooks are generic and resolved at runtime
|
|
106
|
+
resolveInput: async ({ inputValue, context, item, fieldName }: any) => {
|
|
99
107
|
// If null/undefined, return as-is (deletion or no change)
|
|
100
108
|
if (inputValue === null || inputValue === undefined) {
|
|
101
109
|
return inputValue
|
|
@@ -143,7 +151,8 @@ export function file(options: Omit<FileFieldConfig, 'type'>): FileFieldConfig {
|
|
|
143
151
|
return inputValue
|
|
144
152
|
},
|
|
145
153
|
|
|
146
|
-
|
|
154
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Field builder hooks are generic and resolved at runtime
|
|
155
|
+
afterOperation: async ({ operation, item, fieldName, context }: any) => {
|
|
147
156
|
// Only cleanup on delete if enabled
|
|
148
157
|
if (operation === 'delete' && fieldConfig.cleanupOnDelete) {
|
|
149
158
|
const fileMetadata = item[fieldName] as FileMetadata | null
|
|
@@ -158,6 +167,8 @@ export function file(options: Omit<FileFieldConfig, 'type'>): FileFieldConfig {
|
|
|
158
167
|
}
|
|
159
168
|
}
|
|
160
169
|
},
|
|
170
|
+
// Merge with user-provided hooks if any
|
|
171
|
+
...userHooks,
|
|
161
172
|
},
|
|
162
173
|
|
|
163
174
|
getZodSchema: (_fieldName: string, _operation: 'create' | 'update') => {
|
|
@@ -216,13 +227,18 @@ export function file(options: Omit<FileFieldConfig, 'type'>): FileFieldConfig {
|
|
|
216
227
|
* }
|
|
217
228
|
* ```
|
|
218
229
|
*/
|
|
219
|
-
export function image
|
|
220
|
-
|
|
230
|
+
export function image<TTypeInfo extends TypeInfo = TypeInfo>(
|
|
231
|
+
options: Omit<ImageFieldConfig<TTypeInfo>, 'type'>,
|
|
232
|
+
): ImageFieldConfig<TTypeInfo> {
|
|
233
|
+
const { hooks: userHooks, ...restOptions } = options
|
|
234
|
+
|
|
235
|
+
const fieldConfig: ImageFieldConfig<TTypeInfo> = {
|
|
221
236
|
type: 'image',
|
|
222
|
-
...
|
|
237
|
+
...restOptions,
|
|
223
238
|
|
|
224
239
|
hooks: {
|
|
225
|
-
|
|
240
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Field builder hooks are generic and resolved at runtime
|
|
241
|
+
resolveInput: async ({ inputValue, context, item, fieldName }: any) => {
|
|
226
242
|
// If null/undefined, return as-is (deletion or no change)
|
|
227
243
|
if (inputValue === null || inputValue === undefined) {
|
|
228
244
|
return inputValue
|
|
@@ -282,7 +298,8 @@ export function image(options: Omit<ImageFieldConfig, 'type'>): ImageFieldConfig
|
|
|
282
298
|
return inputValue
|
|
283
299
|
},
|
|
284
300
|
|
|
285
|
-
|
|
301
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Field builder hooks are generic and resolved at runtime
|
|
302
|
+
afterOperation: async ({ operation, item, fieldName, context }: any) => {
|
|
286
303
|
// Only cleanup on delete if enabled
|
|
287
304
|
if (operation === 'delete' && fieldConfig.cleanupOnDelete) {
|
|
288
305
|
const imageMetadata = item[fieldName] as ImageMetadata | null
|
|
@@ -297,6 +314,8 @@ export function image(options: Omit<ImageFieldConfig, 'type'>): ImageFieldConfig
|
|
|
297
314
|
}
|
|
298
315
|
}
|
|
299
316
|
},
|
|
317
|
+
// Merge with user-provided hooks if any
|
|
318
|
+
...userHooks,
|
|
300
319
|
},
|
|
301
320
|
|
|
302
321
|
getZodSchema: (_fieldName: string, _operation: 'create' | 'update') => {
|