@baseplate-dev/fastify-generators 0.6.3 → 0.6.5
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 +30 -0
- package/dist/constants/fastify-packages.d.ts +3 -3
- package/dist/constants/fastify-packages.js +3 -3
- package/dist/generators/auth/auth-roles/auth-roles.generator.d.ts +1 -0
- package/dist/generators/auth/auth-roles/auth-roles.generator.d.ts.map +1 -1
- package/dist/generators/auth/auth-roles/auth-roles.generator.js +5 -1
- package/dist/generators/auth/auth-roles/auth-roles.generator.js.map +1 -1
- package/dist/generators/auth/auth-roles/templates/module/constants/auth-roles.constants.ts +4 -3
- package/dist/generators/pothos/pothos-prisma-enum/pothos-prisma-enum.generator.d.ts +1 -0
- package/dist/generators/pothos/pothos-prisma-enum/pothos-prisma-enum.generator.d.ts.map +1 -1
- package/dist/generators/pothos/pothos-prisma-enum/pothos-prisma-enum.generator.js +11 -2
- package/dist/generators/pothos/pothos-prisma-enum/pothos-prisma-enum.generator.js.map +1 -1
- package/dist/generators/prisma/_shared/build-data-helpers/build-schema-fragments.d.ts +47 -0
- package/dist/generators/prisma/_shared/build-data-helpers/build-schema-fragments.d.ts.map +1 -0
- package/dist/generators/prisma/_shared/build-data-helpers/build-schema-fragments.js +56 -0
- package/dist/generators/prisma/_shared/build-data-helpers/build-schema-fragments.js.map +1 -0
- package/dist/generators/prisma/_shared/build-data-helpers/build-transform-operation-parts.d.ts +71 -0
- package/dist/generators/prisma/_shared/build-data-helpers/build-transform-operation-parts.d.ts.map +1 -0
- package/dist/generators/prisma/_shared/build-data-helpers/build-transform-operation-parts.js +153 -0
- package/dist/generators/prisma/_shared/build-data-helpers/build-transform-operation-parts.js.map +1 -0
- package/dist/generators/prisma/_shared/build-data-helpers/generate-authorization-statements.d.ts +46 -0
- package/dist/generators/prisma/_shared/build-data-helpers/generate-authorization-statements.d.ts.map +1 -0
- package/dist/generators/prisma/_shared/build-data-helpers/generate-authorization-statements.js +44 -0
- package/dist/generators/prisma/_shared/build-data-helpers/generate-authorization-statements.js.map +1 -0
- package/dist/generators/prisma/_shared/build-data-helpers/generate-relation-build-data.d.ts +6 -0
- package/dist/generators/prisma/_shared/build-data-helpers/generate-relation-build-data.d.ts.map +1 -1
- package/dist/generators/prisma/_shared/build-data-helpers/generate-relation-build-data.js +13 -1
- package/dist/generators/prisma/_shared/build-data-helpers/generate-relation-build-data.js.map +1 -1
- package/dist/generators/prisma/_shared/build-data-helpers/generate-where-type.d.ts +24 -0
- package/dist/generators/prisma/_shared/build-data-helpers/generate-where-type.d.ts.map +1 -0
- package/dist/generators/prisma/_shared/build-data-helpers/generate-where-type.js +49 -0
- package/dist/generators/prisma/_shared/build-data-helpers/generate-where-type.js.map +1 -0
- package/dist/generators/prisma/_shared/field-definition-generators/generate-scalar-input-field.d.ts +1 -4
- package/dist/generators/prisma/_shared/field-definition-generators/generate-scalar-input-field.d.ts.map +1 -1
- package/dist/generators/prisma/_shared/field-definition-generators/generate-scalar-input-field.js +10 -2
- package/dist/generators/prisma/_shared/field-definition-generators/generate-scalar-input-field.js.map +1 -1
- package/dist/generators/prisma/_shared/field-definition-generators/types.d.ts +47 -1
- package/dist/generators/prisma/_shared/field-definition-generators/types.d.ts.map +1 -1
- package/dist/generators/prisma/data-utils/data-utils.generator.d.ts +17 -74
- package/dist/generators/prisma/data-utils/data-utils.generator.d.ts.map +1 -1
- package/dist/generators/prisma/data-utils/generated/index.d.ts +50 -145
- package/dist/generators/prisma/data-utils/generated/index.d.ts.map +1 -1
- package/dist/generators/prisma/data-utils/generated/template-paths.d.ts +5 -6
- package/dist/generators/prisma/data-utils/generated/template-paths.d.ts.map +1 -1
- package/dist/generators/prisma/data-utils/generated/template-paths.js +5 -6
- package/dist/generators/prisma/data-utils/generated/template-paths.js.map +1 -1
- package/dist/generators/prisma/data-utils/generated/template-renderers.d.ts +0 -21
- package/dist/generators/prisma/data-utils/generated/template-renderers.d.ts.map +1 -1
- package/dist/generators/prisma/data-utils/generated/template-renderers.js +1 -7
- package/dist/generators/prisma/data-utils/generated/template-renderers.js.map +1 -1
- package/dist/generators/prisma/data-utils/generated/ts-import-providers.d.ts +51 -159
- package/dist/generators/prisma/data-utils/generated/ts-import-providers.d.ts.map +1 -1
- package/dist/generators/prisma/data-utils/generated/ts-import-providers.js +32 -64
- package/dist/generators/prisma/data-utils/generated/ts-import-providers.js.map +1 -1
- package/dist/generators/prisma/data-utils/generated/typed-templates.d.ts +66 -142
- package/dist/generators/prisma/data-utils/generated/typed-templates.d.ts.map +1 -1
- package/dist/generators/prisma/data-utils/generated/typed-templates.js +47 -98
- package/dist/generators/prisma/data-utils/generated/typed-templates.js.map +1 -1
- package/dist/generators/prisma/data-utils/templates/src/utils/data-operations/define-transformer.ts +130 -0
- package/dist/generators/prisma/data-utils/templates/src/utils/data-operations/execute-transform-plan.ts +108 -0
- package/dist/generators/prisma/data-utils/templates/src/utils/data-operations/nested-transformers.ts +364 -0
- package/dist/generators/prisma/data-utils/templates/src/utils/data-operations/prepare-transformers.ts +73 -0
- package/dist/generators/prisma/data-utils/templates/src/utils/data-operations/prisma-types.ts +6 -83
- package/dist/generators/prisma/data-utils/templates/src/utils/data-operations/transformer-types.ts +118 -0
- package/dist/generators/prisma/prisma-authorizer-utils/generated/index.d.ts +34 -106
- package/dist/generators/prisma/prisma-authorizer-utils/generated/index.d.ts.map +1 -1
- package/dist/generators/prisma/prisma-authorizer-utils/generated/template-renderers.d.ts +17 -53
- package/dist/generators/prisma/prisma-authorizer-utils/generated/template-renderers.d.ts.map +1 -1
- package/dist/generators/prisma/prisma-authorizer-utils/generated/typed-templates.d.ts +34 -106
- package/dist/generators/prisma/prisma-authorizer-utils/generated/typed-templates.d.ts.map +1 -1
- package/dist/generators/prisma/prisma-authorizer-utils/prisma-authorizer-utils.generator.d.ts +17 -53
- package/dist/generators/prisma/prisma-authorizer-utils/prisma-authorizer-utils.generator.d.ts.map +1 -1
- package/dist/generators/prisma/prisma-authorizer-utils/templates/src/utils/authorizers.ts +11 -11
- package/dist/generators/prisma/prisma-data-create/prisma-data-create.generator.d.ts +39 -49
- package/dist/generators/prisma/prisma-data-create/prisma-data-create.generator.d.ts.map +1 -1
- package/dist/generators/prisma/prisma-data-create/prisma-data-create.generator.js +82 -45
- package/dist/generators/prisma/prisma-data-create/prisma-data-create.generator.js.map +1 -1
- package/dist/generators/prisma/prisma-data-delete/prisma-data-delete.generator.d.ts +39 -52
- package/dist/generators/prisma/prisma-data-delete/prisma-data-delete.generator.d.ts.map +1 -1
- package/dist/generators/prisma/prisma-data-delete/prisma-data-delete.generator.js +48 -32
- package/dist/generators/prisma/prisma-data-delete/prisma-data-delete.generator.js.map +1 -1
- package/dist/generators/prisma/prisma-data-nested-field/nested-field-writer.d.ts +16 -2
- package/dist/generators/prisma/prisma-data-nested-field/nested-field-writer.d.ts.map +1 -1
- package/dist/generators/prisma/prisma-data-nested-field/nested-field-writer.js +282 -138
- package/dist/generators/prisma/prisma-data-nested-field/nested-field-writer.js.map +1 -1
- package/dist/generators/prisma/prisma-data-nested-field/prisma-data-nested-field.generator.d.ts +19 -55
- package/dist/generators/prisma/prisma-data-nested-field/prisma-data-nested-field.generator.d.ts.map +1 -1
- package/dist/generators/prisma/prisma-data-nested-field/prisma-data-nested-field.generator.js +18 -4
- package/dist/generators/prisma/prisma-data-nested-field/prisma-data-nested-field.generator.js.map +1 -1
- package/dist/generators/prisma/prisma-data-service/prisma-data-service.generator.d.ts +37 -92
- package/dist/generators/prisma/prisma-data-service/prisma-data-service.generator.d.ts.map +1 -1
- package/dist/generators/prisma/prisma-data-service/prisma-data-service.generator.js +86 -29
- package/dist/generators/prisma/prisma-data-service/prisma-data-service.generator.js.map +1 -1
- package/dist/generators/prisma/prisma-data-update/prisma-data-update.generator.d.ts +40 -52
- package/dist/generators/prisma/prisma-data-update/prisma-data-update.generator.d.ts.map +1 -1
- package/dist/generators/prisma/prisma-data-update/prisma-data-update.generator.js +102 -49
- package/dist/generators/prisma/prisma-data-update/prisma-data-update.generator.js.map +1 -1
- package/dist/generators/prisma/prisma-query-filter-utils/generated/index.d.ts +51 -159
- package/dist/generators/prisma/prisma-query-filter-utils/generated/index.d.ts.map +1 -1
- package/dist/generators/prisma/prisma-query-filter-utils/generated/template-renderers.d.ts +17 -53
- package/dist/generators/prisma/prisma-query-filter-utils/generated/template-renderers.d.ts.map +1 -1
- package/dist/generators/prisma/prisma-query-filter-utils/generated/typed-templates.d.ts +68 -212
- package/dist/generators/prisma/prisma-query-filter-utils/generated/typed-templates.d.ts.map +1 -1
- package/dist/generators/prisma/prisma-query-filter-utils/prisma-query-filter-utils.generator.d.ts +17 -53
- package/dist/generators/prisma/prisma-query-filter-utils/prisma-query-filter-utils.generator.d.ts.map +1 -1
- package/dist/generators/prisma/prisma-relation-field/prisma-relation-field.generator.js +1 -1
- package/dist/generators/prisma/prisma-relation-field/prisma-relation-field.generator.js.map +1 -1
- package/dist/writers/prisma-schema/fields.d.ts +5 -1
- package/dist/writers/prisma-schema/fields.d.ts.map +1 -1
- package/dist/writers/prisma-schema/fields.js +8 -2
- package/dist/writers/prisma-schema/fields.js.map +1 -1
- package/package.json +8 -8
- package/dist/generators/prisma/_shared/build-data-helpers/generate-authorize-fragment.d.ts +0 -20
- package/dist/generators/prisma/_shared/build-data-helpers/generate-authorize-fragment.d.ts.map +0 -1
- package/dist/generators/prisma/_shared/build-data-helpers/generate-authorize-fragment.js +0 -28
- package/dist/generators/prisma/_shared/build-data-helpers/generate-authorize-fragment.js.map +0 -1
- package/dist/generators/prisma/_shared/build-data-helpers/generate-operation-callbacks.d.ts +0 -130
- package/dist/generators/prisma/_shared/build-data-helpers/generate-operation-callbacks.d.ts.map +0 -1
- package/dist/generators/prisma/_shared/build-data-helpers/generate-operation-callbacks.js +0 -221
- package/dist/generators/prisma/_shared/build-data-helpers/generate-operation-callbacks.js.map +0 -1
- package/dist/generators/prisma/_shared/build-data-helpers/index.d.ts +0 -4
- package/dist/generators/prisma/_shared/build-data-helpers/index.d.ts.map +0 -1
- package/dist/generators/prisma/_shared/build-data-helpers/index.js +0 -4
- package/dist/generators/prisma/_shared/build-data-helpers/index.js.map +0 -1
- package/dist/generators/prisma/data-utils/templates/src/utils/data-operations/commit-operations.ts +0 -366
- package/dist/generators/prisma/data-utils/templates/src/utils/data-operations/compose-operations.ts +0 -131
- package/dist/generators/prisma/data-utils/templates/src/utils/data-operations/field-definitions.ts +0 -777
- package/dist/generators/prisma/data-utils/templates/src/utils/data-operations/field-utils.ts +0 -201
- package/dist/generators/prisma/data-utils/templates/src/utils/data-operations/prisma-utils.ts +0 -90
- package/dist/generators/prisma/data-utils/templates/src/utils/data-operations/types.ts +0 -721
package/dist/generators/prisma/data-utils/templates/src/utils/data-operations/define-transformer.ts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
AfterExecuteHook,
|
|
5
|
+
BoundTransformer,
|
|
6
|
+
MaybePromise,
|
|
7
|
+
Transformer,
|
|
8
|
+
TransformerResult,
|
|
9
|
+
} from '$transformerTypes';
|
|
10
|
+
import type { ServiceContext } from '%serviceContextImports';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Configuration for `defineTransformer`.
|
|
14
|
+
*
|
|
15
|
+
* Provides a single `processInput` function that handles both create and update
|
|
16
|
+
* operations via a discriminated union on the `context` parameter.
|
|
17
|
+
*
|
|
18
|
+
* @template TInput - The input value type
|
|
19
|
+
* @template TCreateArgs - Tuple of additional create args
|
|
20
|
+
* @template TUpdateArgs - Tuple of additional update args
|
|
21
|
+
* @template TCreateOutput - Prisma data type for create operations
|
|
22
|
+
* @template TUpdateOutput - Prisma data type for update operations
|
|
23
|
+
*/
|
|
24
|
+
export interface DefineTransformerConfig<
|
|
25
|
+
TInput,
|
|
26
|
+
TCreateArgs extends unknown[],
|
|
27
|
+
TUpdateArgs extends unknown[],
|
|
28
|
+
TCreateOutput,
|
|
29
|
+
TUpdateOutput,
|
|
30
|
+
> {
|
|
31
|
+
/**
|
|
32
|
+
* Process the input value for either a create or update operation.
|
|
33
|
+
*
|
|
34
|
+
* The `context` parameter is a discriminated union:
|
|
35
|
+
* - `{ type: 'create', args: TCreateArgs }` — creating a new record
|
|
36
|
+
* - `{ type: 'update', args: TUpdateArgs }` — updating an existing record
|
|
37
|
+
*
|
|
38
|
+
* Return `{ data: { create?, update? } }` with the Prisma-compatible data
|
|
39
|
+
* for each operation type, plus optional `afterExecute` hooks.
|
|
40
|
+
*/
|
|
41
|
+
processInput(
|
|
42
|
+
value: TInput,
|
|
43
|
+
context:
|
|
44
|
+
| { type: 'create'; args: TCreateArgs }
|
|
45
|
+
| { type: 'update'; args: TUpdateArgs },
|
|
46
|
+
ctx: { serviceContext: ServiceContext },
|
|
47
|
+
): MaybePromise<{
|
|
48
|
+
data?: { create?: TCreateOutput; update?: TUpdateOutput };
|
|
49
|
+
afterExecute?: AfterExecuteHook[];
|
|
50
|
+
}>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Define a transformer from a single `processInput` function.
|
|
55
|
+
*
|
|
56
|
+
* This helper generates `forCreate(input, ...args)` and `forUpdate(input, ...args)`
|
|
57
|
+
* methods from one function that uses a discriminated union to distinguish operations.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* const passwordTransformer = defineTransformer<string, [], [], string, string>({
|
|
62
|
+
* async processInput(value) {
|
|
63
|
+
* const hashed = await hash(value);
|
|
64
|
+
* return { data: { create: hashed, update: hashed } };
|
|
65
|
+
* },
|
|
66
|
+
* });
|
|
67
|
+
*
|
|
68
|
+
* // Usage:
|
|
69
|
+
* passwordTransformer.forCreate(password)
|
|
70
|
+
* passwordTransformer.forUpdate(password)
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
export function defineTransformer<
|
|
74
|
+
TInput,
|
|
75
|
+
TCreateArgs extends unknown[],
|
|
76
|
+
TUpdateArgs extends unknown[],
|
|
77
|
+
TCreateOutput,
|
|
78
|
+
TUpdateOutput,
|
|
79
|
+
>(
|
|
80
|
+
config: DefineTransformerConfig<
|
|
81
|
+
TInput,
|
|
82
|
+
TCreateArgs,
|
|
83
|
+
TUpdateArgs,
|
|
84
|
+
TCreateOutput,
|
|
85
|
+
TUpdateOutput
|
|
86
|
+
>,
|
|
87
|
+
): Transformer<TInput, TCreateArgs, TUpdateArgs, TCreateOutput, TUpdateOutput> {
|
|
88
|
+
return {
|
|
89
|
+
forCreate(
|
|
90
|
+
input: TInput,
|
|
91
|
+
...args: TCreateArgs
|
|
92
|
+
): BoundTransformer<TCreateOutput> {
|
|
93
|
+
return {
|
|
94
|
+
async process(ctx): Promise<TransformerResult<TCreateOutput>> {
|
|
95
|
+
const result = await config.processInput(
|
|
96
|
+
input,
|
|
97
|
+
{ type: 'create', args },
|
|
98
|
+
ctx,
|
|
99
|
+
);
|
|
100
|
+
return {
|
|
101
|
+
data: result.data?.create,
|
|
102
|
+
afterExecute: result.afterExecute,
|
|
103
|
+
};
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
forUpdate(
|
|
109
|
+
input: TInput | undefined,
|
|
110
|
+
...args: TUpdateArgs
|
|
111
|
+
): BoundTransformer<TUpdateOutput> {
|
|
112
|
+
return {
|
|
113
|
+
async process(ctx): Promise<TransformerResult<TUpdateOutput>> {
|
|
114
|
+
if (input === undefined) {
|
|
115
|
+
return {};
|
|
116
|
+
}
|
|
117
|
+
const result = await config.processInput(
|
|
118
|
+
input,
|
|
119
|
+
{ type: 'update', args },
|
|
120
|
+
ctx,
|
|
121
|
+
);
|
|
122
|
+
return {
|
|
123
|
+
data: result.data?.update,
|
|
124
|
+
afterExecute: result.afterExecute,
|
|
125
|
+
};
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
AnyBoundTransformer,
|
|
5
|
+
InferTransformed,
|
|
6
|
+
InferUnresolvedTransformed,
|
|
7
|
+
TransformPlan,
|
|
8
|
+
} from '$transformerTypes';
|
|
9
|
+
import type { Prisma } from '%prismaGeneratedImports';
|
|
10
|
+
|
|
11
|
+
import { prisma } from '%prismaImports';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Resolve any deferred data in the transformed map.
|
|
15
|
+
*
|
|
16
|
+
* Transformer data can be either a direct value or a deferred function
|
|
17
|
+
* `(tx) => Promise<value>`. This resolves all deferred values inside
|
|
18
|
+
* the transaction.
|
|
19
|
+
*/
|
|
20
|
+
async function resolveTransformed<
|
|
21
|
+
TTransformers extends Record<string, AnyBoundTransformer>,
|
|
22
|
+
>(
|
|
23
|
+
transformed: InferUnresolvedTransformed<TTransformers>,
|
|
24
|
+
tx: Prisma.TransactionClient,
|
|
25
|
+
): Promise<InferTransformed<TTransformers>> {
|
|
26
|
+
const resolved: Record<string, unknown> = {};
|
|
27
|
+
|
|
28
|
+
for (const key of Object.keys(transformed)) {
|
|
29
|
+
const value = (transformed as Record<string, unknown>)[key];
|
|
30
|
+
resolved[key] =
|
|
31
|
+
typeof value === 'function'
|
|
32
|
+
? await (value as (tx: Prisma.TransactionClient) => Promise<unknown>)(
|
|
33
|
+
tx,
|
|
34
|
+
)
|
|
35
|
+
: value;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return resolved as InferTransformed<TTransformers>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Execute a transform plan inside a Prisma transaction.
|
|
43
|
+
*
|
|
44
|
+
* Resolves any deferred transformer data (functions that need `tx`),
|
|
45
|
+
* calls the `execute` callback with resolved data, then runs all
|
|
46
|
+
* `afterExecute` hooks collected from the transformers.
|
|
47
|
+
*
|
|
48
|
+
* If `refetch` is provided, calls it after the transaction with the
|
|
49
|
+
* result to reload the record with includes/relations. Cannot be used
|
|
50
|
+
* with `tx` since the parent transaction may not have committed yet.
|
|
51
|
+
*
|
|
52
|
+
* If `tx` is provided, reuses that transaction (for nested plans).
|
|
53
|
+
* Otherwise opens a new `$transaction`.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* const result = await executeTransformPlan(plan, {
|
|
58
|
+
* execute: async ({ tx, transformed }) =>
|
|
59
|
+
* tx.todoList.create({ data: { ...rest, ...transformed } }),
|
|
60
|
+
* refetch: (item) =>
|
|
61
|
+
* prisma.todoList.findUniqueOrThrow({ where: { id: item.id }, ...query }),
|
|
62
|
+
* });
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export async function executeTransformPlan<
|
|
66
|
+
TTransformers extends Record<string, AnyBoundTransformer>,
|
|
67
|
+
TResult,
|
|
68
|
+
TRefetchResult = TResult,
|
|
69
|
+
>(
|
|
70
|
+
plan: TransformPlan<TTransformers>,
|
|
71
|
+
config: {
|
|
72
|
+
tx?: Prisma.TransactionClient;
|
|
73
|
+
execute: (args: {
|
|
74
|
+
tx: Prisma.TransactionClient;
|
|
75
|
+
transformed: InferTransformed<TTransformers>;
|
|
76
|
+
}) => Promise<TResult>;
|
|
77
|
+
refetch?: (result: TResult) => Promise<TRefetchResult>;
|
|
78
|
+
},
|
|
79
|
+
): Promise<TResult | TRefetchResult> {
|
|
80
|
+
const { execute, refetch } = config;
|
|
81
|
+
|
|
82
|
+
if (config.tx && refetch) {
|
|
83
|
+
throw new Error(
|
|
84
|
+
'Cannot use refetch with an existing transaction — the parent transaction may not have committed yet.',
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const runInTx = async (tx: Prisma.TransactionClient): Promise<TResult> => {
|
|
89
|
+
const resolved = await resolveTransformed(plan.transformed, tx);
|
|
90
|
+
const result = await execute({ tx, transformed: resolved });
|
|
91
|
+
|
|
92
|
+
for (const hook of plan.afterExecute) {
|
|
93
|
+
await hook({ tx, result });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return result;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const txResult = config.tx
|
|
100
|
+
? await runInTx(config.tx)
|
|
101
|
+
: await prisma.$transaction(async (tx) => runInTx(tx));
|
|
102
|
+
|
|
103
|
+
if (refetch) {
|
|
104
|
+
return refetch(txResult);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return txResult;
|
|
108
|
+
}
|
package/dist/generators/prisma/data-utils/templates/src/utils/data-operations/nested-transformers.ts
ADDED
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
|
|
3
|
+
import type { GetResult, ModelPropName } from '$prismaTypes';
|
|
4
|
+
import type {
|
|
5
|
+
BoundTransformer,
|
|
6
|
+
MaybePromise,
|
|
7
|
+
Transformer,
|
|
8
|
+
TransformerResult,
|
|
9
|
+
} from '$transformerTypes';
|
|
10
|
+
import type { Prisma } from '%prismaGeneratedImports';
|
|
11
|
+
import type { ServiceContext } from '%serviceContextImports';
|
|
12
|
+
import type { z } from 'zod';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* =========================================
|
|
16
|
+
* Shared Types
|
|
17
|
+
* =========================================
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/** Context passed to nested processCreate / processUpdate callbacks */
|
|
21
|
+
interface NestedProcessContext {
|
|
22
|
+
serviceContext: ServiceContext;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* A deferred operation returned by processCreate / processUpdate.
|
|
27
|
+
* Called inside the transaction after the parent record is created/updated.
|
|
28
|
+
*
|
|
29
|
+
* @template TParentResult - The type of the parent record
|
|
30
|
+
*/
|
|
31
|
+
type DeferredOperation<TParentResult> = (
|
|
32
|
+
tx: Prisma.TransactionClient,
|
|
33
|
+
parent: TParentResult,
|
|
34
|
+
) => Promise<void>;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* =========================================
|
|
38
|
+
* One-to-One Transformer
|
|
39
|
+
* =========================================
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Configuration for a one-to-one nested transformer.
|
|
44
|
+
* Types are inferred from `parentModel`, `model`, and `schema`.
|
|
45
|
+
*
|
|
46
|
+
* @template TParentModelName - Prisma parent model name (for typing parent in deferred ops)
|
|
47
|
+
* @template TModelName - Prisma child model name (for typing existing)
|
|
48
|
+
* @template TInputSchema - Zod schema (for typing input)
|
|
49
|
+
*/
|
|
50
|
+
export interface OneToOneTransformerConfig<
|
|
51
|
+
TParentModelName extends ModelPropName,
|
|
52
|
+
TModelName extends ModelPropName,
|
|
53
|
+
TInputSchema extends z.ZodType,
|
|
54
|
+
> {
|
|
55
|
+
/** Prisma parent model name — used for type inference only */
|
|
56
|
+
parentModel: TParentModelName;
|
|
57
|
+
/** Prisma child model name — used for type inference only */
|
|
58
|
+
model: TModelName;
|
|
59
|
+
/** Zod schema for the nested entity input — used for type inference only */
|
|
60
|
+
schema: TInputSchema;
|
|
61
|
+
|
|
62
|
+
/** Process a create operation. Returns a deferred operation for the transaction. */
|
|
63
|
+
processCreate: (
|
|
64
|
+
input: z.output<TInputSchema>,
|
|
65
|
+
ctx: NestedProcessContext,
|
|
66
|
+
) => MaybePromise<DeferredOperation<GetResult<TParentModelName>>>;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Process an update operation. Only called when existing child is defined.
|
|
70
|
+
* If no existing child, `processCreate` is called instead.
|
|
71
|
+
* If omitted, updates are a no-op.
|
|
72
|
+
*/
|
|
73
|
+
processUpdate?: (
|
|
74
|
+
input: z.output<TInputSchema>,
|
|
75
|
+
existing: GetResult<TModelName>,
|
|
76
|
+
ctx: NestedProcessContext,
|
|
77
|
+
) => MaybePromise<DeferredOperation<GetResult<TParentModelName>>>;
|
|
78
|
+
|
|
79
|
+
/** Delete the nested entity. Called when input is `null` on update and existing is defined. */
|
|
80
|
+
processDelete: (
|
|
81
|
+
existing: GetResult<TModelName>,
|
|
82
|
+
ctx: NestedProcessContext,
|
|
83
|
+
) => DeferredOperation<GetResult<TParentModelName>>;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Create a one-to-one nested transformer.
|
|
88
|
+
*
|
|
89
|
+
* Types are inferred from config — no manual generics needed:
|
|
90
|
+
* - `parent` in deferred ops is typed from `parentModel`
|
|
91
|
+
* - `existing` in processUpdate is typed from `model`
|
|
92
|
+
* - `input` in processCreate/processUpdate is typed from `schema`
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```typescript
|
|
96
|
+
* const profileTransformer = oneToOneTransformer({
|
|
97
|
+
* parentModel: 'user',
|
|
98
|
+
* model: 'userProfile',
|
|
99
|
+
* schema: profileInputSchema,
|
|
100
|
+
* processCreate: (input, ctx) => async (tx, parent) => {
|
|
101
|
+
* // parent is typed as GetResult<'user'>
|
|
102
|
+
* // input is typed from profileInputSchema
|
|
103
|
+
* await tx.userProfile.create({ data: { ...input, userId: parent.id } });
|
|
104
|
+
* },
|
|
105
|
+
* processUpdate: (input, existing, ctx) => async (tx, parent) => { ... },
|
|
106
|
+
* processDelete: (existing) => async (tx, parent) => { ... },
|
|
107
|
+
* });
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
export function oneToOneTransformer<
|
|
111
|
+
TParentModelName extends ModelPropName,
|
|
112
|
+
TModelName extends ModelPropName,
|
|
113
|
+
TInputSchema extends z.ZodType,
|
|
114
|
+
>(
|
|
115
|
+
config: OneToOneTransformerConfig<TParentModelName, TModelName, TInputSchema>,
|
|
116
|
+
): Transformer<
|
|
117
|
+
z.output<TInputSchema> | null | undefined,
|
|
118
|
+
[],
|
|
119
|
+
[
|
|
120
|
+
context: {
|
|
121
|
+
loadExisting: () => Promise<GetResult<TModelName> | null>;
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
undefined,
|
|
125
|
+
undefined
|
|
126
|
+
> {
|
|
127
|
+
return {
|
|
128
|
+
forCreate(
|
|
129
|
+
input: z.output<TInputSchema> | null | undefined,
|
|
130
|
+
): BoundTransformer<undefined> {
|
|
131
|
+
return {
|
|
132
|
+
async process(ctx): Promise<TransformerResult<undefined>> {
|
|
133
|
+
if (input === null || input === undefined) return {};
|
|
134
|
+
|
|
135
|
+
const deferredOp = await config.processCreate(input, ctx);
|
|
136
|
+
return {
|
|
137
|
+
afterExecute: [
|
|
138
|
+
async ({ tx, result }) => {
|
|
139
|
+
await deferredOp(tx, result as GetResult<TParentModelName>);
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
};
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
forUpdate(
|
|
148
|
+
input: z.output<TInputSchema> | null | undefined,
|
|
149
|
+
context: {
|
|
150
|
+
loadExisting: () => Promise<GetResult<TModelName> | null>;
|
|
151
|
+
},
|
|
152
|
+
): BoundTransformer<undefined> {
|
|
153
|
+
return {
|
|
154
|
+
async process(ctx): Promise<TransformerResult<undefined>> {
|
|
155
|
+
if (input === undefined) return {};
|
|
156
|
+
|
|
157
|
+
const existing = (await context.loadExisting()) ?? undefined;
|
|
158
|
+
|
|
159
|
+
if (input === null) {
|
|
160
|
+
if (!existing) return {};
|
|
161
|
+
const deferredOp = config.processDelete(existing, ctx);
|
|
162
|
+
return {
|
|
163
|
+
afterExecute: [
|
|
164
|
+
async ({ tx, result }) => {
|
|
165
|
+
await deferredOp(tx, result as GetResult<TParentModelName>);
|
|
166
|
+
},
|
|
167
|
+
],
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const deferredOp =
|
|
172
|
+
existing && config.processUpdate
|
|
173
|
+
? await config.processUpdate(input, existing, ctx)
|
|
174
|
+
: await config.processCreate(input, ctx);
|
|
175
|
+
return {
|
|
176
|
+
afterExecute: [
|
|
177
|
+
async ({ tx, result }) => {
|
|
178
|
+
await deferredOp(tx, result as GetResult<TParentModelName>);
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
};
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* =========================================
|
|
190
|
+
* One-to-Many Transformer
|
|
191
|
+
* =========================================
|
|
192
|
+
*/
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Configuration for a one-to-many nested transformer.
|
|
196
|
+
* Types are inferred from `parentModel`, `model`, and `schema`.
|
|
197
|
+
*
|
|
198
|
+
* @template TParentModelName - Prisma parent model name
|
|
199
|
+
* @template TModelName - Prisma child model name
|
|
200
|
+
* @template TInputSchema - Zod schema for each item
|
|
201
|
+
*/
|
|
202
|
+
export interface OneToManyTransformerConfig<
|
|
203
|
+
TParentModelName extends ModelPropName,
|
|
204
|
+
TModelName extends ModelPropName,
|
|
205
|
+
TInputSchema extends z.ZodType,
|
|
206
|
+
> {
|
|
207
|
+
/** Prisma parent model name — used for type inference only */
|
|
208
|
+
parentModel: TParentModelName;
|
|
209
|
+
/** Prisma child model name — used for type inference only */
|
|
210
|
+
model: TModelName;
|
|
211
|
+
/** Zod schema for each item's input — used for type inference only */
|
|
212
|
+
schema: TInputSchema;
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Compare an input item to an existing item. Return true if they represent
|
|
216
|
+
* the same entity (e.g., matching IDs). If omitted, all items are treated
|
|
217
|
+
* as creates and all existing items are deleted (delete + recreate pattern).
|
|
218
|
+
*/
|
|
219
|
+
compareItem?: (
|
|
220
|
+
input: z.output<TInputSchema>,
|
|
221
|
+
existing: GetResult<TModelName>,
|
|
222
|
+
) => boolean;
|
|
223
|
+
|
|
224
|
+
/** Process a create operation for a single item. */
|
|
225
|
+
processCreate: (
|
|
226
|
+
itemInput: z.output<TInputSchema>,
|
|
227
|
+
ctx: NestedProcessContext,
|
|
228
|
+
) => MaybePromise<DeferredOperation<GetResult<TParentModelName>>>;
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Process an update operation for a single item.
|
|
232
|
+
* Only called when `compareItem` finds a match.
|
|
233
|
+
* If omitted, matched items fall through to `processCreate`.
|
|
234
|
+
*/
|
|
235
|
+
processUpdate?: (
|
|
236
|
+
itemInput: z.output<TInputSchema>,
|
|
237
|
+
existingItem: GetResult<TModelName>,
|
|
238
|
+
ctx: NestedProcessContext,
|
|
239
|
+
) => MaybePromise<DeferredOperation<GetResult<TParentModelName>>>;
|
|
240
|
+
|
|
241
|
+
/** Delete removed items not present in the input array. Called inside the transaction after the main operation. */
|
|
242
|
+
deleteRemoved: (
|
|
243
|
+
tx: Prisma.TransactionClient,
|
|
244
|
+
removedItems: GetResult<TModelName>[],
|
|
245
|
+
parent: GetResult<TParentModelName>,
|
|
246
|
+
) => Promise<void>;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Create a one-to-many nested transformer.
|
|
251
|
+
*
|
|
252
|
+
* Types are inferred from config — no manual generics needed.
|
|
253
|
+
* Diff/match logic is provided by the caller via `loadExisting` in `.forUpdate()`.
|
|
254
|
+
*
|
|
255
|
+
* @example
|
|
256
|
+
* ```typescript
|
|
257
|
+
* const imagesTransformer = oneToManyTransformer({
|
|
258
|
+
* parentModel: 'user',
|
|
259
|
+
* model: 'userImage',
|
|
260
|
+
* schema: imageInputSchema,
|
|
261
|
+
* processCreate: (item, ctx) => async (tx, parent) => { ... },
|
|
262
|
+
* processUpdate: (item, existing, ctx) => async (tx, parent) => { ... },
|
|
263
|
+
* deleteRemoved: async (tx, removed, parent) => { ... },
|
|
264
|
+
* });
|
|
265
|
+
* ```
|
|
266
|
+
*/
|
|
267
|
+
export function oneToManyTransformer<
|
|
268
|
+
TParentModelName extends ModelPropName,
|
|
269
|
+
TModelName extends ModelPropName,
|
|
270
|
+
TInputSchema extends z.ZodType,
|
|
271
|
+
>(
|
|
272
|
+
config: OneToManyTransformerConfig<
|
|
273
|
+
TParentModelName,
|
|
274
|
+
TModelName,
|
|
275
|
+
TInputSchema
|
|
276
|
+
>,
|
|
277
|
+
): Transformer<
|
|
278
|
+
z.output<TInputSchema>[] | undefined,
|
|
279
|
+
[],
|
|
280
|
+
[
|
|
281
|
+
context: {
|
|
282
|
+
loadExisting: () => Promise<GetResult<TModelName>[]>;
|
|
283
|
+
},
|
|
284
|
+
],
|
|
285
|
+
undefined,
|
|
286
|
+
undefined
|
|
287
|
+
> {
|
|
288
|
+
return {
|
|
289
|
+
forCreate(
|
|
290
|
+
items: z.output<TInputSchema>[] | undefined,
|
|
291
|
+
): BoundTransformer<undefined> {
|
|
292
|
+
return {
|
|
293
|
+
async process(ctx): Promise<TransformerResult<undefined>> {
|
|
294
|
+
if (!items || items.length === 0) return {};
|
|
295
|
+
|
|
296
|
+
const deferredOps = await Promise.all(
|
|
297
|
+
items.map(async (item) => config.processCreate(item, ctx)),
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
return {
|
|
301
|
+
afterExecute: [
|
|
302
|
+
async ({ tx, result }) => {
|
|
303
|
+
for (const op of deferredOps) {
|
|
304
|
+
await op(tx, result as GetResult<TParentModelName>);
|
|
305
|
+
}
|
|
306
|
+
},
|
|
307
|
+
],
|
|
308
|
+
};
|
|
309
|
+
},
|
|
310
|
+
};
|
|
311
|
+
},
|
|
312
|
+
|
|
313
|
+
forUpdate(
|
|
314
|
+
items: z.output<TInputSchema>[] | undefined,
|
|
315
|
+
context: {
|
|
316
|
+
loadExisting: () => Promise<GetResult<TModelName>[]>;
|
|
317
|
+
},
|
|
318
|
+
): BoundTransformer<undefined> {
|
|
319
|
+
return {
|
|
320
|
+
async process(ctx): Promise<TransformerResult<undefined>> {
|
|
321
|
+
if (items === undefined) return {};
|
|
322
|
+
|
|
323
|
+
const existing = await context.loadExisting();
|
|
324
|
+
|
|
325
|
+
const { compareItem } = config;
|
|
326
|
+
const matched = items.map((item) => ({
|
|
327
|
+
item,
|
|
328
|
+
existingItem: compareItem
|
|
329
|
+
? existing.find((e) => compareItem(item, e))
|
|
330
|
+
: undefined,
|
|
331
|
+
}));
|
|
332
|
+
|
|
333
|
+
const matchedExisting = new Set(
|
|
334
|
+
matched.map((m) => m.existingItem).filter(Boolean),
|
|
335
|
+
);
|
|
336
|
+
const removedItems = existing.filter((e) => !matchedExisting.has(e));
|
|
337
|
+
|
|
338
|
+
const deferredOps = await Promise.all(
|
|
339
|
+
matched.map(async ({ item, existingItem }) =>
|
|
340
|
+
existingItem && config.processUpdate
|
|
341
|
+
? config.processUpdate(item, existingItem, ctx)
|
|
342
|
+
: config.processCreate(item, ctx),
|
|
343
|
+
),
|
|
344
|
+
);
|
|
345
|
+
|
|
346
|
+
return {
|
|
347
|
+
afterExecute: [
|
|
348
|
+
async ({ tx, result }) => {
|
|
349
|
+
await config.deleteRemoved(
|
|
350
|
+
tx,
|
|
351
|
+
removedItems,
|
|
352
|
+
result as GetResult<TParentModelName>,
|
|
353
|
+
);
|
|
354
|
+
for (const op of deferredOps) {
|
|
355
|
+
await op(tx, result as GetResult<TParentModelName>);
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
],
|
|
359
|
+
};
|
|
360
|
+
},
|
|
361
|
+
};
|
|
362
|
+
},
|
|
363
|
+
};
|
|
364
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
AfterExecuteHook,
|
|
5
|
+
AnyBoundTransformer,
|
|
6
|
+
InferUnresolvedTransformed,
|
|
7
|
+
TransformPlan,
|
|
8
|
+
} from '$transformerTypes';
|
|
9
|
+
import type { ServiceContext } from '%serviceContextImports';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Configuration for `prepareTransformers`.
|
|
13
|
+
*
|
|
14
|
+
* @template TTransformers - Record of bound transformers (from `.forCreate()` / `.forUpdate()`)
|
|
15
|
+
*/
|
|
16
|
+
export interface PrepareTransformersConfig<
|
|
17
|
+
TTransformers extends Record<string, AnyBoundTransformer>,
|
|
18
|
+
> {
|
|
19
|
+
/** Bound transformers to process. Each should be the result of `.forCreate()` or `.forUpdate()`. */
|
|
20
|
+
transformers: TTransformers;
|
|
21
|
+
/** Service context with user info, request details, etc. */
|
|
22
|
+
serviceContext: ServiceContext;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Process all bound transformers and collect their results into a `TransformPlan`.
|
|
27
|
+
*
|
|
28
|
+
* Each bound transformer's `process` method is called to produce Prisma-compatible
|
|
29
|
+
* data and optional afterExecute hooks. The results are aggregated into a plan
|
|
30
|
+
* that can be passed to `executeTransformPlan`.
|
|
31
|
+
*
|
|
32
|
+
* @template TTransformers - Record of bound transformers
|
|
33
|
+
* @param config - Configuration with bound transformers and service context
|
|
34
|
+
* @returns A `TransformPlan` with resolved data and collected hooks
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* const plan = await prepareTransformers({
|
|
39
|
+
* transformers: {
|
|
40
|
+
* coverPhoto: coverPhotoTransformer.forCreate(coverPhoto),
|
|
41
|
+
* },
|
|
42
|
+
* serviceContext,
|
|
43
|
+
* });
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export async function prepareTransformers<
|
|
47
|
+
TTransformers extends Record<string, AnyBoundTransformer>,
|
|
48
|
+
>(
|
|
49
|
+
config: PrepareTransformersConfig<TTransformers>,
|
|
50
|
+
): Promise<TransformPlan<TTransformers>> {
|
|
51
|
+
const { transformers, serviceContext } = config;
|
|
52
|
+
|
|
53
|
+
const entries = Object.entries(transformers);
|
|
54
|
+
const results = await Promise.all(
|
|
55
|
+
entries.map(async ([, boundTransformer]) =>
|
|
56
|
+
boundTransformer.process({ serviceContext }),
|
|
57
|
+
),
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const transformed = {} as InferUnresolvedTransformed<TTransformers>;
|
|
61
|
+
const afterExecute: AfterExecuteHook[] = [];
|
|
62
|
+
|
|
63
|
+
for (const [index, result] of results.entries()) {
|
|
64
|
+
const key = entries[index][0];
|
|
65
|
+
(transformed as Record<string, unknown>)[key] = result.data;
|
|
66
|
+
|
|
67
|
+
if (result.afterExecute) {
|
|
68
|
+
afterExecute.push(...result.afterExecute);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return { transformed, afterExecute };
|
|
73
|
+
}
|