@aws-amplify/data-schema 1.5.0 → 1.6.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.
@@ -1 +1 @@
1
- {"version":3,"file":"subscription.js","sources":["../../../../../src/runtime/internals/operations/subscription.ts"],"sourcesContent":["\"use strict\";\n// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.subscriptionFactory = void 0;\nconst rxjs_1 = require(\"rxjs\");\nconst APIClient_1 = require(\"../APIClient\");\nfunction subscriptionFactory(client, modelIntrospection, model, operation, getInternals) {\n const { name } = model;\n const subscription = (args) => {\n const query = (0, APIClient_1.generateGraphQLDocument)(modelIntrospection, name, operation, args);\n const variables = (0, APIClient_1.buildGraphQLVariables)(model, operation, args, modelIntrospection);\n const auth = (0, APIClient_1.authModeParams)(client, getInternals, args);\n const headers = (0, APIClient_1.getCustomHeaders)(client, getInternals, args?.headers);\n const observable = client.graphql({\n ...auth,\n query,\n variables,\n }, headers);\n return observable.pipe((0, rxjs_1.map)((value) => {\n const [key] = Object.keys(value.data);\n // Will need flattening here if/when custom selection set support is added:\n const data = value.data[key];\n const [initialized] = (0, APIClient_1.initializeModel)(client, name, [data], modelIntrospection, auth.authMode, auth.authToken);\n return initialized;\n }));\n };\n return subscription;\n}\nexports.subscriptionFactory = subscriptionFactory;\n"],"names":[],"mappings":";;AACA;AACA;AACA,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AAC9D,OAAO,CAAC,mBAAmB,GAAG,KAAK,CAAC,CAAC;AACrC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AAC/B,MAAM,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;AAC5C,SAAS,mBAAmB,CAAC,MAAM,EAAE,kBAAkB,EAAE,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE;AACzF,IAAI,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC;AAC3B,IAAI,MAAM,YAAY,GAAG,CAAC,IAAI,KAAK;AACnC,QAAQ,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC,uBAAuB,EAAE,kBAAkB,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;AAC1G,QAAQ,MAAM,SAAS,GAAG,IAAI,WAAW,CAAC,qBAAqB,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,kBAAkB,CAAC,CAAC;AAC7G,QAAQ,MAAM,IAAI,GAAG,IAAI,WAAW,CAAC,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,CAAC,CAAC;AACjF,QAAQ,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,gBAAgB,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;AAC/F,QAAQ,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC;AAC1C,YAAY,GAAG,IAAI;AACnB,YAAY,KAAK;AACjB,YAAY,SAAS;AACrB,SAAS,EAAE,OAAO,CAAC,CAAC;AACpB,QAAQ,OAAO,UAAU,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,GAAG,EAAE,CAAC,KAAK,KAAK;AAC1D,YAAY,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AAClD;AACA,YAAY,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzC,YAAY,MAAM,CAAC,WAAW,CAAC,GAAG,IAAI,WAAW,CAAC,eAAe,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,kBAAkB,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;AAC5I,YAAY,OAAO,WAAW,CAAC;AAC/B,SAAS,CAAC,CAAC,CAAC;AACZ,KAAK,CAAC;AACN,IAAI,OAAO,YAAY,CAAC;AACxB,CAAC;AACD,OAAO,CAAC,mBAAmB,GAAG,mBAAmB;;"}
1
+ {"version":3,"file":"subscription.js","sources":["../../../../../src/runtime/internals/operations/subscription.ts"],"sourcesContent":["\"use strict\";\n// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.subscriptionFactory = void 0;\nconst rxjs_1 = require(\"rxjs\");\nconst APIClient_1 = require(\"../APIClient\");\nfunction subscriptionFactory(client, modelIntrospection, model, operation, getInternals) {\n const { name } = model;\n const subscription = (args) => {\n const query = (0, APIClient_1.generateGraphQLDocument)(modelIntrospection, name, operation, args);\n const variables = (0, APIClient_1.buildGraphQLVariables)(model, operation, args, modelIntrospection);\n const auth = (0, APIClient_1.authModeParams)(client, getInternals, args);\n const headers = (0, APIClient_1.getCustomHeaders)(client, getInternals, args?.headers);\n const observable = client.graphql({\n ...auth,\n query,\n variables,\n }, headers);\n return observable.pipe((0, rxjs_1.map)((value) => {\n const [key] = Object.keys(value.data);\n const data = value.data[key];\n const flattenedResult = (0, APIClient_1.flattenItems)(modelIntrospection, name, data);\n if (flattenedResult === null) {\n return null;\n }\n else if (args?.selectionSet) {\n return flattenedResult;\n }\n else {\n const [initialized] = (0, APIClient_1.initializeModel)(client, name, [flattenedResult], modelIntrospection, auth.authMode, auth.authToken);\n return initialized;\n }\n }));\n };\n return subscription;\n}\nexports.subscriptionFactory = subscriptionFactory;\n"],"names":[],"mappings":";;AACA;AACA;AACA,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AAC9D,OAAO,CAAC,mBAAmB,GAAG,KAAK,CAAC,CAAC;AACrC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AAC/B,MAAM,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;AAC5C,SAAS,mBAAmB,CAAC,MAAM,EAAE,kBAAkB,EAAE,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE;AACzF,IAAI,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC;AAC3B,IAAI,MAAM,YAAY,GAAG,CAAC,IAAI,KAAK;AACnC,QAAQ,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC,uBAAuB,EAAE,kBAAkB,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;AAC1G,QAAQ,MAAM,SAAS,GAAG,IAAI,WAAW,CAAC,qBAAqB,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,kBAAkB,CAAC,CAAC;AAC7G,QAAQ,MAAM,IAAI,GAAG,IAAI,WAAW,CAAC,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,CAAC,CAAC;AACjF,QAAQ,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,gBAAgB,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;AAC/F,QAAQ,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC;AAC1C,YAAY,GAAG,IAAI;AACnB,YAAY,KAAK;AACjB,YAAY,SAAS;AACrB,SAAS,EAAE,OAAO,CAAC,CAAC;AACpB,QAAQ,OAAO,UAAU,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,GAAG,EAAE,CAAC,KAAK,KAAK;AAC1D,YAAY,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AAClD,YAAY,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzC,YAAY,MAAM,eAAe,GAAG,IAAI,WAAW,CAAC,YAAY,EAAE,kBAAkB,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AAClG,YAAY,IAAI,eAAe,KAAK,IAAI,EAAE;AAC1C,gBAAgB,OAAO,IAAI,CAAC;AAC5B,aAAa;AACb,iBAAiB,IAAI,IAAI,EAAE,YAAY,EAAE;AACzC,gBAAgB,OAAO,eAAe,CAAC;AACvC,aAAa;AACb,iBAAiB;AACjB,gBAAgB,MAAM,CAAC,WAAW,CAAC,GAAG,IAAI,WAAW,CAAC,eAAe,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,eAAe,CAAC,EAAE,kBAAkB,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;AAC3J,gBAAgB,OAAO,WAAW,CAAC;AACnC,aAAa;AACb,SAAS,CAAC,CAAC,CAAC;AACZ,KAAK,CAAC;AACN,IAAI,OAAO,YAAY,CAAC;AACxB,CAAC;AACD,OAAO,CAAC,mBAAmB,GAAG,mBAAmB;;"}
@@ -1,4 +1,4 @@
1
- import type { CustomOperationParamShape } from '../../CustomOperation';
1
+ import type { CustomOperationParamShape, UltimateFunctionHandlerAsyncType } from '../../CustomOperation';
2
2
  import type { BaseModelField } from '../../ModelField';
3
3
  import type { RefType } from '../../RefType';
4
4
  import type { ResolveFieldRequirements } from '../../MappedTypes/ResolveFieldProperties';
@@ -27,7 +27,7 @@ export interface ClientCustomOperation<RefBag extends Record<any, any>, Op exten
27
27
  * }
28
28
  * ```
29
29
  */
30
- functionHandler: AppSyncResolverHandler<CustomOpArguments<Op>, LambdaReturnType<CustomOpReturnType<Op, RefBag>>>;
30
+ functionHandler: AppSyncResolverHandler<CustomOpArguments<Op>, Op['handlers'] extends UltimateFunctionHandlerAsyncType ? void : LambdaReturnType<CustomOpReturnType<Op, RefBag>>>;
31
31
  /**
32
32
  * The `context.arguments` type for lambda function implementations.
33
33
  *
@@ -1,11 +1,11 @@
1
1
  import { SetTypeSubArg } from '@aws-amplify/data-schema-types';
2
2
  import { Brand } from './util';
3
- import { InternalField, type BaseModelField } from './ModelField';
3
+ import { InternalField, ModelField, type BaseModelField } from './ModelField';
4
4
  import { AllowModifierForCustomOperation, Authorization } from './Authorization';
5
5
  import { RefType, InternalRef } from './RefType';
6
6
  import { EnumType } from './EnumType';
7
7
  import { CustomType } from './CustomType';
8
- import type { CustomHandler, FunctionHandler, HandlerType as Handler } from './Handler';
8
+ import type { AsyncFunctionHandler, CustomHandler, FunctionHandler, HandlerType as Handler } from './Handler';
9
9
  import { AiModel, InferenceConfiguration } from './ai/ModelType';
10
10
  declare const queryBrand = "queryCustomOperation";
11
11
  declare const mutationBrand = "mutationCustomOperation";
@@ -18,7 +18,13 @@ type InternalSubscriptionSource = InternalRef;
18
18
  type CustomReturnType = RefType<any> | CustomType<any>;
19
19
  type InternalCustomArguments = Record<string, InternalField>;
20
20
  type InternalCustomReturnType = InternalRef;
21
- type HandlerInputType = FunctionHandler[] | CustomHandler[] | Handler;
21
+ type HandlerInputType = FunctionHandler[] | CustomHandler[] | AsyncFunctionHandler[] | HeterogeneousFunctionHandlerWithLastAsync | HeterogeneousFunctionHandlerType | Handler;
22
+ type HeterogeneousFunctionHandlerType = (FunctionHandler | AsyncFunctionHandler)[];
23
+ type HeterogeneousFunctionHandlerWithLastAsync = [
24
+ ...HeterogeneousFunctionHandlerType,
25
+ AsyncFunctionHandler
26
+ ];
27
+ export type UltimateFunctionHandlerAsyncType = AsyncFunctionHandler | AsyncFunctionHandler[] | HeterogeneousFunctionHandlerWithLastAsync;
22
28
  export declare const CustomOperationNames: readonly ["Query", "Mutation", "Subscription", "Generation"];
23
29
  type CustomOperationName = (typeof CustomOperationNames)[number];
24
30
  type CustomData = {
@@ -49,7 +55,7 @@ export type CustomOperation<T extends CustomOperationParamShape, K extends keyof
49
55
  arguments<Arguments extends CustomArguments>(args: Arguments): CustomOperation<SetTypeSubArg<T, 'arguments', Arguments>, K | 'arguments', B>;
50
56
  returns<ReturnType extends CustomReturnType>(returnType: ReturnType): CustomOperation<SetTypeSubArg<T, 'returnType', ReturnType>, K | 'returns', B>;
51
57
  authorization<AuthRuleType extends Authorization<any, any, any>>(callback: (allow: AllowModifierForCustomOperation) => AuthRuleType | AuthRuleType[]): CustomOperation<SetTypeSubArg<T, 'authorization', AuthRuleType[]>, K | 'authorization', B>;
52
- handler<H extends HandlerInputType>(handlers: H): CustomOperation<T, K | 'handler', B>;
58
+ handler<H extends HandlerInputType>(handlers: H): [H] extends [UltimateFunctionHandlerAsyncType] ? CustomOperation<AsyncFunctionCustomOperation<T>, K | 'handler' | 'returns', B> : CustomOperation<T, K | 'handler', B>;
53
59
  for<Source extends SubscriptionSource>(source: Source | Source[]): CustomOperation<T['typeName'] extends 'Subscription' ? SetTypeSubArg<T, 'returnType', Source extends SubscriptionSource[] ? Source[number] : Source> : T, K | 'for', B>;
54
60
  }, K> & Brand<B>;
55
61
  /**
@@ -129,6 +135,12 @@ export declare function subscription(): CustomOperation<{
129
135
  typeName: 'Subscription';
130
136
  handlers: null;
131
137
  }, 'returns', typeof subscriptionBrand>;
138
+ type AsyncFunctionCustomOperation<T extends CustomOperationParamShape> = SetTypeSubArg<SetTypeSubArg<T, 'returnType', EventInvocationResponseCustomType>, 'handlers', AsyncFunctionHandler>;
139
+ type EventInvocationResponseCustomType = CustomType<{
140
+ fields: {
141
+ success: ModelField<boolean, 'required', undefined>;
142
+ };
143
+ }>;
132
144
  export interface GenerationInput {
133
145
  aiModel: AiModel;
134
146
  systemPrompt: string;
@@ -1,4 +1,4 @@
1
- import { brand } from './util/Brand.mjs';
1
+ import { brand, brandSymbol } from './util/Brand.mjs';
2
2
  import { allowForCustomOperations } from './Authorization.mjs';
3
3
 
4
4
  const queryBrand = 'queryCustomOperation';
@@ -42,6 +42,9 @@ function _custom(typeName, brand, input) {
42
42
  data.handlers = Array.isArray(handlers)
43
43
  ? handlers
44
44
  : [handlers];
45
+ if (lastHandlerIsAsyncFunction(handlers)) {
46
+ data.returnType = eventInvocationResponse;
47
+ }
45
48
  return this;
46
49
  },
47
50
  for(source) {
@@ -106,6 +109,23 @@ function mutation() {
106
109
  function subscription() {
107
110
  return _custom('Subscription', subscriptionBrand);
108
111
  }
112
+ const eventInvocationResponse = {
113
+ data: {
114
+ type: 'ref',
115
+ link: 'EventInvocationResponse',
116
+ valueRequired: false,
117
+ array: false,
118
+ arrayRequired: false,
119
+ mutationOperations: [],
120
+ authorization: [],
121
+ },
122
+ };
123
+ function lastHandlerIsAsyncFunction(handlers) {
124
+ const lastHandlerBrandSymbol = Array.isArray(handlers)
125
+ ? handlers[handlers.length - 1][brandSymbol]
126
+ : handlers[brandSymbol];
127
+ return lastHandlerBrandSymbol === 'asyncFunctionHandler';
128
+ }
109
129
  /**
110
130
  * @experimental
111
131
  *
@@ -1 +1 @@
1
- {"version":3,"file":"CustomOperation.mjs","sources":["../../src/CustomOperation.ts"],"sourcesContent":["import { brand } from './util';\nimport { allowForCustomOperations, } from './Authorization';\nconst queryBrand = 'queryCustomOperation';\nconst mutationBrand = 'mutationCustomOperation';\nconst subscriptionBrand = 'subscriptionCustomOperation';\nconst generationBrand = 'generationCustomOperation';\nexport const CustomOperationNames = [\n 'Query',\n 'Mutation',\n 'Subscription',\n 'Generation',\n];\nfunction brandedBuilder(builder, brandValue) {\n return { ...builder, ...brand(brandValue) };\n}\nfunction _custom(typeName, brand, input) {\n const data = {\n arguments: {},\n returnType: null,\n authorization: [],\n typeName: typeName,\n handlers: null,\n subscriptionSource: [],\n input,\n };\n const builder = brandedBuilder({\n arguments(args) {\n data.arguments = args;\n return this;\n },\n returns(returnType) {\n data.returnType = returnType;\n return this;\n },\n authorization(callback) {\n const rules = callback(allowForCustomOperations);\n data.authorization = Array.isArray(rules) ? rules : [rules];\n return this;\n },\n handler(handlers) {\n data.handlers = Array.isArray(handlers)\n ? handlers\n : [handlers];\n return this;\n },\n for(source) {\n data.subscriptionSource = Array.isArray(source) ? source : [source];\n return this;\n },\n }, brand);\n return { ...builder, data };\n}\n/**\n * Use a custom query to define an API request that will retrieve backend data.\n * @see {@link https://docs.amplify.aws/react/build-a-backend/data/custom-business-logic/}\n * @example\n * const schema = a.schema({\n * echo: a\n * .query()\n * .arguments({ content: a.string() })\n * .returns(a.ref('EchoResponse'))\n * .authorization(allow => [allow.publicApiKey()])\n * // 3. set the function has the handler\n * .handler(a.handler.function(echoHandler)),\n *\n * EchoResponse: a.customType({\n * content: a.string(),\n * executionDuration: a.float()\n * }),\n * });\n * @returns a custom query\n */\nexport function query() {\n return _custom('Query', queryBrand);\n}\n/**\n * Use a custom mutation to define an API request that will modify backend data or trigger a subscription event.\n * @see {@link https://docs.amplify.aws/react/build-a-backend/data/custom-business-logic/}\n * @example\n * likePost: a\n * .mutation()\n * .arguments({ postId: a.string() })\n * .returns(a.ref('Post'))\n * .authorization(allow => [allow.publicApiKey()])\n * .handler(a.handler.function(echoHandler))\n * @returns a custom mutation\n */\nexport function mutation() {\n return _custom('Mutation', mutationBrand);\n}\n/**\n * Define a custom subscription to receive an event when a mutation is triggered\n * @see {@link https://docs.amplify.aws/react/build-a-backend/data/custom-subscription/}\n * @example\n * // Subscribe to incoming messages\n * receive: a.subscription()\n * // subscribes to the 'publish' mutation\n * .for(a.ref('publish'))\n * // subscription handler to set custom filters\n * .handler(a.handler.custom({entry: './receive.js'}))\n * // authorization rules as to who can subscribe to the data\n * .authorization(allow => [allow.publicApiKey()]),\n * @returns a custom subscription\n */\nexport function subscription() {\n return _custom('Subscription', subscriptionBrand);\n}\n/**\n * @experimental\n *\n * Define an AI generation route for single request-response interaction with specified AI model.\n * @example\n * makeRecipe: a.generation({\n * aiModel: { resourcePath },\n * systemPrompt: 'Please make a recipe from the provided ingredients',\n * })\n * .arguments({ ingredients: a.string().array() })\n * .returns(a.ref(\"Recipe\"))\n * @returns a generation route definition\n */\nexport function generation(input) {\n return _custom('Generation', generationBrand, input);\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"CustomOperation.mjs","sources":["../../src/CustomOperation.ts"],"sourcesContent":["import { brand } from './util';\nimport { allowForCustomOperations, } from './Authorization';\nimport { brandSymbol } from './util/Brand';\nconst queryBrand = 'queryCustomOperation';\nconst mutationBrand = 'mutationCustomOperation';\nconst subscriptionBrand = 'subscriptionCustomOperation';\nconst generationBrand = 'generationCustomOperation';\nexport const CustomOperationNames = [\n 'Query',\n 'Mutation',\n 'Subscription',\n 'Generation',\n];\nfunction brandedBuilder(builder, brandValue) {\n return { ...builder, ...brand(brandValue) };\n}\nfunction _custom(typeName, brand, input) {\n const data = {\n arguments: {},\n returnType: null,\n authorization: [],\n typeName: typeName,\n handlers: null,\n subscriptionSource: [],\n input,\n };\n const builder = brandedBuilder({\n arguments(args) {\n data.arguments = args;\n return this;\n },\n returns(returnType) {\n data.returnType = returnType;\n return this;\n },\n authorization(callback) {\n const rules = callback(allowForCustomOperations);\n data.authorization = Array.isArray(rules) ? rules : [rules];\n return this;\n },\n handler(handlers) {\n data.handlers = Array.isArray(handlers)\n ? handlers\n : [handlers];\n if (lastHandlerIsAsyncFunction(handlers)) {\n data.returnType = eventInvocationResponse;\n }\n return this;\n },\n for(source) {\n data.subscriptionSource = Array.isArray(source) ? source : [source];\n return this;\n },\n }, brand);\n return { ...builder, data };\n}\n/**\n * Use a custom query to define an API request that will retrieve backend data.\n * @see {@link https://docs.amplify.aws/react/build-a-backend/data/custom-business-logic/}\n * @example\n * const schema = a.schema({\n * echo: a\n * .query()\n * .arguments({ content: a.string() })\n * .returns(a.ref('EchoResponse'))\n * .authorization(allow => [allow.publicApiKey()])\n * // 3. set the function has the handler\n * .handler(a.handler.function(echoHandler)),\n *\n * EchoResponse: a.customType({\n * content: a.string(),\n * executionDuration: a.float()\n * }),\n * });\n * @returns a custom query\n */\nexport function query() {\n return _custom('Query', queryBrand);\n}\n/**\n * Use a custom mutation to define an API request that will modify backend data or trigger a subscription event.\n * @see {@link https://docs.amplify.aws/react/build-a-backend/data/custom-business-logic/}\n * @example\n * likePost: a\n * .mutation()\n * .arguments({ postId: a.string() })\n * .returns(a.ref('Post'))\n * .authorization(allow => [allow.publicApiKey()])\n * .handler(a.handler.function(echoHandler))\n * @returns a custom mutation\n */\nexport function mutation() {\n return _custom('Mutation', mutationBrand);\n}\n/**\n * Define a custom subscription to receive an event when a mutation is triggered\n * @see {@link https://docs.amplify.aws/react/build-a-backend/data/custom-subscription/}\n * @example\n * // Subscribe to incoming messages\n * receive: a.subscription()\n * // subscribes to the 'publish' mutation\n * .for(a.ref('publish'))\n * // subscription handler to set custom filters\n * .handler(a.handler.custom({entry: './receive.js'}))\n * // authorization rules as to who can subscribe to the data\n * .authorization(allow => [allow.publicApiKey()]),\n * @returns a custom subscription\n */\nexport function subscription() {\n return _custom('Subscription', subscriptionBrand);\n}\nconst eventInvocationResponse = {\n data: {\n type: 'ref',\n link: 'EventInvocationResponse',\n valueRequired: false,\n array: false,\n arrayRequired: false,\n mutationOperations: [],\n authorization: [],\n },\n};\nfunction lastHandlerIsAsyncFunction(handlers) {\n const lastHandlerBrandSymbol = Array.isArray(handlers)\n ? handlers[handlers.length - 1][brandSymbol]\n : handlers[brandSymbol];\n return lastHandlerBrandSymbol === 'asyncFunctionHandler';\n}\n/**\n * @experimental\n *\n * Define an AI generation route for single request-response interaction with specified AI model.\n * @example\n * makeRecipe: a.generation({\n * aiModel: { resourcePath },\n * systemPrompt: 'Please make a recipe from the provided ingredients',\n * })\n * .arguments({ ingredients: a.string().array() })\n * .returns(a.ref(\"Recipe\"))\n * @returns a generation route definition\n */\nexport function generation(input) {\n return _custom('Generation', generationBrand, input);\n}\n"],"names":[],"mappings":"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"}
@@ -1,14 +1,15 @@
1
1
  import type { DefineFunction } from '@aws-amplify/data-schema-types';
2
- import { Brand } from './util';
2
+ import type { brandSymbol } from './util/Brand.js';
3
3
  import { RefType } from './RefType';
4
- export type HandlerType = InlineSqlHandler | SqlReferenceHandler | CustomHandler | FunctionHandler;
4
+ export type HandlerType = InlineSqlHandler | SqlReferenceHandler | CustomHandler | FunctionHandler | AsyncFunctionHandler;
5
5
  declare const dataSymbol: unique symbol;
6
- type AllHandlers = InlineSqlHandler | SqlReferenceHandler | CustomHandler | FunctionHandler;
6
+ type AllHandlers = InlineSqlHandler | SqlReferenceHandler | CustomHandler | FunctionHandler | AsyncFunctionHandler;
7
7
  export declare function getHandlerData<H extends AllHandlers>(handler: H): H[typeof dataSymbol];
8
8
  declare const inlineSqlBrand = "inlineSql";
9
9
  export type InlineSqlHandler = {
10
10
  [dataSymbol]: string;
11
- } & Brand<typeof inlineSqlBrand>;
11
+ [brandSymbol]: typeof inlineSqlBrand;
12
+ };
12
13
  declare function inlineSql(sql: string): InlineSqlHandler;
13
14
  declare const sqlReferenceBrand = "sqlReference";
14
15
  export type SqlReferenceHandlerData = {
@@ -17,7 +18,8 @@ export type SqlReferenceHandlerData = {
17
18
  };
18
19
  export type SqlReferenceHandler = {
19
20
  [dataSymbol]: SqlReferenceHandlerData;
20
- } & Brand<typeof sqlReferenceBrand>;
21
+ [brandSymbol]: typeof sqlReferenceBrand;
22
+ };
21
23
  declare function sqlReference(sqlFilePath: string): SqlReferenceHandler;
22
24
  type CustomHandlerInput = {
23
25
  /**
@@ -40,7 +42,8 @@ export type CustomHandlerData = CustomHandlerInput & {
40
42
  declare const customHandlerBrand = "customHandler";
41
43
  export type CustomHandler = {
42
44
  [dataSymbol]: CustomHandlerData;
43
- } & Brand<typeof customHandlerBrand>;
45
+ [brandSymbol]: typeof customHandlerBrand;
46
+ };
44
47
  /**
45
48
  * Use a custom JavaScript resolver to handle a query, mutation, or subscription.
46
49
  * @see {@link https://docs.amplify.aws/react/build-a-backend/data/custom-business-logic/#step-2---configure-custom-business-logic-handler-code}
@@ -69,11 +72,56 @@ export type CustomHandler = {
69
72
  * });
70
73
  */
71
74
  declare function custom(customHandler: CustomHandlerInput): CustomHandler;
72
- export type FunctionHandlerData = DefineFunction | string;
75
+ export type FunctionHandlerInput = DefineFunction | string;
76
+ export type FunctionHandlerData = {
77
+ handler: FunctionHandlerInput;
78
+ invocationType: 'RequestResponse' | 'Event';
79
+ };
73
80
  declare const functionHandlerBrand = "functionHandler";
74
81
  export type FunctionHandler = {
75
82
  [dataSymbol]: FunctionHandlerData;
76
- } & Brand<typeof functionHandlerBrand>;
83
+ /**
84
+ * Use `async()` to have this function handler invoked asynchronously.
85
+ * If an `async()` function is only handler or the final handler in a pipeline, the return type of this
86
+ * custom query / mutation is `{ success: boolean }`.
87
+ * @example
88
+ * import {
89
+ * type ClientSchema,
90
+ * a,
91
+ * defineData,
92
+ * defineFunction // 1.Import "defineFunction" to create new functions
93
+ * } from '@aws-amplify/backend';
94
+ *
95
+ * // 2. define a function
96
+ * const echoHandler = defineFunction({
97
+ * entry: './echo-handler/handler.ts'
98
+ * })
99
+ *
100
+ * const schema = a.schema({
101
+ * EchoResponse: a.customType({
102
+ * content: a.string(),
103
+ * executionDuration: a.float()
104
+ * }),
105
+ *
106
+ * echo: a
107
+ * .query()
108
+ * .arguments({ content: a.string() })
109
+ * .returns(a.ref('EchoResponse'))
110
+ * .authorization(allow => [allow.publicApiKey()])
111
+ * // 3. set the function has the handler
112
+ * .handler(a.handler.function(echoHandler).async())
113
+ *
114
+ * @see {@link https://docs.amplify.aws/react/build-a-backend/data/INSERT_PATH_WHEN_READY}
115
+ * @returns A function handler for query / mutation that is asynchronously invoked.
116
+ */
117
+ async(): AsyncFunctionHandler;
118
+ [brandSymbol]: typeof functionHandlerBrand;
119
+ };
120
+ declare const asyncFunctionHandlerBrand = "asyncFunctionHandler";
121
+ export type AsyncFunctionHandler = {
122
+ [dataSymbol]: FunctionHandlerData;
123
+ [brandSymbol]: typeof asyncFunctionHandlerBrand;
124
+ };
77
125
  /**
78
126
  * Use a function created via `defineFunction` to handle the custom query/mutation/subscription. In your function handler,
79
127
  * you can use the `Schema["YOUR_QUERY_OR_MUTATION_NAME"]["functionHandler"]` utility type to type the handler function.
@@ -109,7 +157,7 @@ export type FunctionHandler = {
109
157
  * in a corresponding value into the `functionMap` property of defineData.
110
158
  * @returns A handler for the query / mutation / subscription
111
159
  */
112
- declare function fcn(fn: FunctionHandlerData): FunctionHandler;
160
+ declare function fcn(fn: FunctionHandlerInput): FunctionHandler;
113
161
  export declare const handler: {
114
162
  inlineSql: typeof inlineSql;
115
163
  sqlReference: typeof sqlReference;
@@ -60,6 +60,7 @@ function custom(customHandler) {
60
60
  };
61
61
  }
62
62
  const functionHandlerBrand = 'functionHandler';
63
+ const asyncFunctionHandlerBrand = 'asyncFunctionHandler';
63
64
  /**
64
65
  * Use a function created via `defineFunction` to handle the custom query/mutation/subscription. In your function handler,
65
66
  * you can use the `Schema["YOUR_QUERY_OR_MUTATION_NAME"]["functionHandler"]` utility type to type the handler function.
@@ -96,7 +97,25 @@ const functionHandlerBrand = 'functionHandler';
96
97
  * @returns A handler for the query / mutation / subscription
97
98
  */
98
99
  function fcn(fn) {
99
- return { [dataSymbol]: fn, ...buildHandler(functionHandlerBrand) };
100
+ return {
101
+ [dataSymbol]: {
102
+ handler: fn,
103
+ invocationType: 'RequestResponse',
104
+ },
105
+ async() {
106
+ return _async(this);
107
+ },
108
+ ...buildHandler(functionHandlerBrand),
109
+ };
110
+ }
111
+ function _async(fnHandler) {
112
+ return {
113
+ [dataSymbol]: {
114
+ handler: fnHandler[dataSymbol].handler,
115
+ invocationType: 'Event',
116
+ },
117
+ ...buildHandler(asyncFunctionHandlerBrand),
118
+ };
100
119
  }
101
120
  //#endregion
102
121
  const handler = {
@@ -1 +1 @@
1
- {"version":3,"file":"Handler.mjs","sources":["../../src/Handler.ts"],"sourcesContent":["import { brand } from './util';\nconst dataSymbol = Symbol('Data');\nfunction buildHandler(brandName) {\n return brand(brandName);\n}\nexport function getHandlerData(handler) {\n return handler[dataSymbol];\n}\n//#region handler.inlineSql\nconst inlineSqlBrand = 'inlineSql';\nfunction inlineSql(sql) {\n return { [dataSymbol]: sql, ...buildHandler(inlineSqlBrand) };\n}\n//#endregion\n//#region handler.sqlReference\nconst sqlReferenceBrand = 'sqlReference';\nfunction sqlReference(sqlFilePath) {\n // used to determine caller directory in order to resolve relative path downstream\n const stack = new Error().stack;\n return {\n [dataSymbol]: { stack, entry: sqlFilePath },\n ...buildHandler(sqlReferenceBrand),\n };\n}\nconst customHandlerBrand = 'customHandler';\n/**\n * Use a custom JavaScript resolver to handle a query, mutation, or subscription.\n * @see {@link https://docs.amplify.aws/react/build-a-backend/data/custom-business-logic/#step-2---configure-custom-business-logic-handler-code}\n * @param customHandler `{ entry: \"path-to-javascript-resolver-file.js\", dataSource: \"Data Source name added via \"backend.data.add*DataSoruce(...)\"}`\n * @returns A JavaScript resolver attached to the query, mutation, or subscription.\n * @example\n * const schema = a.schema({\n * Post: a.model({\n * content: a.string(),\n * likes: a.integer()\n * .authorization(allow => [allow.authenticated().to(['read'])])\n * }).authorization(allow => [\n * allow.owner(),\n * allow.authenticated().to(['read'])\n * ]),\n *\n * likePost: a\n * .mutation()\n * .arguments({ postId: a.id() })\n * .returns(a.ref('Post'))\n * .authorization(allow => [allow.authenticated()])\n * .handler(a.handler.custom({\n * dataSource: a.ref('Post'),\n * entry: './increment-like.js'\n * }))\n * });\n */\nfunction custom(customHandler) {\n // used to determine caller directory in order to resolve relative path downstream\n const stack = new Error().stack;\n return {\n [dataSymbol]: { ...customHandler, stack },\n ...buildHandler(customHandlerBrand),\n };\n}\nconst functionHandlerBrand = 'functionHandler';\n/**\n * Use a function created via `defineFunction` to handle the custom query/mutation/subscription. In your function handler,\n * you can use the `Schema[\"YOUR_QUERY_OR_MUTATION_NAME\"][\"functionHandler\"]` utility type to type the handler function.\n * @example\n * import {\n * type ClientSchema,\n * a,\n * defineData,\n * defineFunction // 1.Import \"defineFunction\" to create new functions\n * } from '@aws-amplify/backend';\n *\n * // 2. define a function\n * const echoHandler = defineFunction({\n * entry: './echo-handler/handler.ts'\n * })\n *\n * const schema = a.schema({\n * EchoResponse: a.customType({\n * content: a.string(),\n * executionDuration: a.float()\n * }),\n *\n * echo: a\n * .query()\n * .arguments({ content: a.string() })\n * .returns(a.ref('EchoResponse'))\n * .authorization(allow => [allow.publicApiKey()])\n * // 3. set the function has the handler\n * .handler(a.handler.function(echoHandler))\n * });\n * @see {@link https://docs.amplify.aws/react/build-a-backend/data/custom-business-logic/}\n * @param fn A function created via `defineFunction`. Alternatively, you can pass in a \"string\" of the function name and pass\n * in a corresponding value into the `functionMap` property of defineData.\n * @returns A handler for the query / mutation / subscription\n */\nfunction fcn(fn) {\n return { [dataSymbol]: fn, ...buildHandler(functionHandlerBrand) };\n}\n//#endregion\nexport const handler = {\n inlineSql,\n sqlReference,\n custom,\n function: fcn,\n};\n"],"names":[],"mappings":";;AACA,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;AAClC,SAAS,YAAY,CAAC,SAAS,EAAE;AACjC,IAAI,OAAO,KAAK,CAAC,SAAS,CAAC,CAAC;AAC5B,CAAC;AACM,SAAS,cAAc,CAAC,OAAO,EAAE;AACxC,IAAI,OAAO,OAAO,CAAC,UAAU,CAAC,CAAC;AAC/B,CAAC;AACD;AACA,MAAM,cAAc,GAAG,WAAW,CAAC;AACnC,SAAS,SAAS,CAAC,GAAG,EAAE;AACxB,IAAI,OAAO,EAAE,CAAC,UAAU,GAAG,GAAG,EAAE,GAAG,YAAY,CAAC,cAAc,CAAC,EAAE,CAAC;AAClE,CAAC;AACD;AACA;AACA,MAAM,iBAAiB,GAAG,cAAc,CAAC;AACzC,SAAS,YAAY,CAAC,WAAW,EAAE;AACnC;AACA,IAAI,MAAM,KAAK,GAAG,IAAI,KAAK,EAAE,CAAC,KAAK,CAAC;AACpC,IAAI,OAAO;AACX,QAAQ,CAAC,UAAU,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE;AACnD,QAAQ,GAAG,YAAY,CAAC,iBAAiB,CAAC;AAC1C,KAAK,CAAC;AACN,CAAC;AACD,MAAM,kBAAkB,GAAG,eAAe,CAAC;AAC3C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,MAAM,CAAC,aAAa,EAAE;AAC/B;AACA,IAAI,MAAM,KAAK,GAAG,IAAI,KAAK,EAAE,CAAC,KAAK,CAAC;AACpC,IAAI,OAAO;AACX,QAAQ,CAAC,UAAU,GAAG,EAAE,GAAG,aAAa,EAAE,KAAK,EAAE;AACjD,QAAQ,GAAG,YAAY,CAAC,kBAAkB,CAAC;AAC3C,KAAK,CAAC;AACN,CAAC;AACD,MAAM,oBAAoB,GAAG,iBAAiB,CAAC;AAC/C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,GAAG,CAAC,EAAE,EAAE;AACjB,IAAI,OAAO,EAAE,CAAC,UAAU,GAAG,EAAE,EAAE,GAAG,YAAY,CAAC,oBAAoB,CAAC,EAAE,CAAC;AACvE,CAAC;AACD;AACY,MAAC,OAAO,GAAG;AACvB,IAAI,SAAS;AACb,IAAI,YAAY;AAChB,IAAI,MAAM;AACV,IAAI,QAAQ,EAAE,GAAG;AACjB;;;;"}
1
+ {"version":3,"file":"Handler.mjs","sources":["../../src/Handler.ts"],"sourcesContent":["import { brand } from './util';\nconst dataSymbol = Symbol('Data');\nfunction buildHandler(brandName) {\n return brand(brandName);\n}\nexport function getHandlerData(handler) {\n return handler[dataSymbol];\n}\n//#region handler.inlineSql\nconst inlineSqlBrand = 'inlineSql';\nfunction inlineSql(sql) {\n return { [dataSymbol]: sql, ...buildHandler(inlineSqlBrand) };\n}\n//#endregion\n//#region handler.sqlReference\nconst sqlReferenceBrand = 'sqlReference';\nfunction sqlReference(sqlFilePath) {\n // used to determine caller directory in order to resolve relative path downstream\n const stack = new Error().stack;\n return {\n [dataSymbol]: { stack, entry: sqlFilePath },\n ...buildHandler(sqlReferenceBrand),\n };\n}\nconst customHandlerBrand = 'customHandler';\n/**\n * Use a custom JavaScript resolver to handle a query, mutation, or subscription.\n * @see {@link https://docs.amplify.aws/react/build-a-backend/data/custom-business-logic/#step-2---configure-custom-business-logic-handler-code}\n * @param customHandler `{ entry: \"path-to-javascript-resolver-file.js\", dataSource: \"Data Source name added via \"backend.data.add*DataSoruce(...)\"}`\n * @returns A JavaScript resolver attached to the query, mutation, or subscription.\n * @example\n * const schema = a.schema({\n * Post: a.model({\n * content: a.string(),\n * likes: a.integer()\n * .authorization(allow => [allow.authenticated().to(['read'])])\n * }).authorization(allow => [\n * allow.owner(),\n * allow.authenticated().to(['read'])\n * ]),\n *\n * likePost: a\n * .mutation()\n * .arguments({ postId: a.id() })\n * .returns(a.ref('Post'))\n * .authorization(allow => [allow.authenticated()])\n * .handler(a.handler.custom({\n * dataSource: a.ref('Post'),\n * entry: './increment-like.js'\n * }))\n * });\n */\nfunction custom(customHandler) {\n // used to determine caller directory in order to resolve relative path downstream\n const stack = new Error().stack;\n return {\n [dataSymbol]: { ...customHandler, stack },\n ...buildHandler(customHandlerBrand),\n };\n}\nconst functionHandlerBrand = 'functionHandler';\nconst asyncFunctionHandlerBrand = 'asyncFunctionHandler';\n/**\n * Use a function created via `defineFunction` to handle the custom query/mutation/subscription. In your function handler,\n * you can use the `Schema[\"YOUR_QUERY_OR_MUTATION_NAME\"][\"functionHandler\"]` utility type to type the handler function.\n * @example\n * import {\n * type ClientSchema,\n * a,\n * defineData,\n * defineFunction // 1.Import \"defineFunction\" to create new functions\n * } from '@aws-amplify/backend';\n *\n * // 2. define a function\n * const echoHandler = defineFunction({\n * entry: './echo-handler/handler.ts'\n * })\n *\n * const schema = a.schema({\n * EchoResponse: a.customType({\n * content: a.string(),\n * executionDuration: a.float()\n * }),\n *\n * echo: a\n * .query()\n * .arguments({ content: a.string() })\n * .returns(a.ref('EchoResponse'))\n * .authorization(allow => [allow.publicApiKey()])\n * // 3. set the function has the handler\n * .handler(a.handler.function(echoHandler))\n * });\n * @see {@link https://docs.amplify.aws/react/build-a-backend/data/custom-business-logic/}\n * @param fn A function created via `defineFunction`. Alternatively, you can pass in a \"string\" of the function name and pass\n * in a corresponding value into the `functionMap` property of defineData.\n * @returns A handler for the query / mutation / subscription\n */\nfunction fcn(fn) {\n return {\n [dataSymbol]: {\n handler: fn,\n invocationType: 'RequestResponse',\n },\n async() {\n return _async(this);\n },\n ...buildHandler(functionHandlerBrand),\n };\n}\nfunction _async(fnHandler) {\n return {\n [dataSymbol]: {\n handler: fnHandler[dataSymbol].handler,\n invocationType: 'Event',\n },\n ...buildHandler(asyncFunctionHandlerBrand),\n };\n}\n//#endregion\nexport const handler = {\n inlineSql,\n sqlReference,\n custom,\n function: fcn,\n};\n"],"names":[],"mappings":";;AACA,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;AAClC,SAAS,YAAY,CAAC,SAAS,EAAE;AACjC,IAAI,OAAO,KAAK,CAAC,SAAS,CAAC,CAAC;AAC5B,CAAC;AACM,SAAS,cAAc,CAAC,OAAO,EAAE;AACxC,IAAI,OAAO,OAAO,CAAC,UAAU,CAAC,CAAC;AAC/B,CAAC;AACD;AACA,MAAM,cAAc,GAAG,WAAW,CAAC;AACnC,SAAS,SAAS,CAAC,GAAG,EAAE;AACxB,IAAI,OAAO,EAAE,CAAC,UAAU,GAAG,GAAG,EAAE,GAAG,YAAY,CAAC,cAAc,CAAC,EAAE,CAAC;AAClE,CAAC;AACD;AACA;AACA,MAAM,iBAAiB,GAAG,cAAc,CAAC;AACzC,SAAS,YAAY,CAAC,WAAW,EAAE;AACnC;AACA,IAAI,MAAM,KAAK,GAAG,IAAI,KAAK,EAAE,CAAC,KAAK,CAAC;AACpC,IAAI,OAAO;AACX,QAAQ,CAAC,UAAU,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE;AACnD,QAAQ,GAAG,YAAY,CAAC,iBAAiB,CAAC;AAC1C,KAAK,CAAC;AACN,CAAC;AACD,MAAM,kBAAkB,GAAG,eAAe,CAAC;AAC3C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,MAAM,CAAC,aAAa,EAAE;AAC/B;AACA,IAAI,MAAM,KAAK,GAAG,IAAI,KAAK,EAAE,CAAC,KAAK,CAAC;AACpC,IAAI,OAAO;AACX,QAAQ,CAAC,UAAU,GAAG,EAAE,GAAG,aAAa,EAAE,KAAK,EAAE;AACjD,QAAQ,GAAG,YAAY,CAAC,kBAAkB,CAAC;AAC3C,KAAK,CAAC;AACN,CAAC;AACD,MAAM,oBAAoB,GAAG,iBAAiB,CAAC;AAC/C,MAAM,yBAAyB,GAAG,sBAAsB,CAAC;AACzD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,GAAG,CAAC,EAAE,EAAE;AACjB,IAAI,OAAO;AACX,QAAQ,CAAC,UAAU,GAAG;AACtB,YAAY,OAAO,EAAE,EAAE;AACvB,YAAY,cAAc,EAAE,iBAAiB;AAC7C,SAAS;AACT,QAAQ,KAAK,GAAG;AAChB,YAAY,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC;AAChC,SAAS;AACT,QAAQ,GAAG,YAAY,CAAC,oBAAoB,CAAC;AAC7C,KAAK,CAAC;AACN,CAAC;AACD,SAAS,MAAM,CAAC,SAAS,EAAE;AAC3B,IAAI,OAAO;AACX,QAAQ,CAAC,UAAU,GAAG;AACtB,YAAY,OAAO,EAAE,SAAS,CAAC,UAAU,CAAC,CAAC,OAAO;AAClD,YAAY,cAAc,EAAE,OAAO;AACnC,SAAS;AACT,QAAQ,GAAG,YAAY,CAAC,yBAAyB,CAAC;AAClD,KAAK,CAAC;AACN,CAAC;AACD;AACY,MAAC,OAAO,GAAG;AACvB,IAAI,SAAS;AACb,IAAI,YAAY;AAChB,IAAI,MAAM;AACV,IAAI,QAAQ,EAAE,GAAG;AACjB;;;;"}
@@ -2,7 +2,7 @@ import { ModelFieldType, string } from './ModelField.mjs';
2
2
  import { ModelRelationshipTypes } from './ModelRelationalField.mjs';
3
3
  import { accessData, accessSchemaData } from './Authorization.mjs';
4
4
  import { CustomOperationNames } from './CustomOperation.mjs';
5
- import { getBrand } from './util/Brand.mjs';
5
+ import { getBrand, brandSymbol } from './util/Brand.mjs';
6
6
  import { getHandlerData } from './Handler.mjs';
7
7
  import * as os from 'os';
8
8
  import * as path from 'path';
@@ -149,19 +149,23 @@ function transformFunctionHandler(handlers, functionFieldName) {
149
149
  const lambdaFunctionDefinition = {};
150
150
  handlers.forEach((handler, idx) => {
151
151
  const handlerData = getHandlerData(handler);
152
- if (typeof handlerData === 'string') {
153
- gqlHandlerContent += `@function(name: "${handlerData}") `;
152
+ if (typeof handlerData.handler === 'string') {
153
+ gqlHandlerContent += `@function(name: "${handlerData.handler}") `;
154
154
  }
155
- else if (typeof handlerData.getInstance === 'function') {
155
+ else if (typeof handlerData.handler.getInstance === 'function') {
156
156
  const fnName = `Fn${capitalize(functionFieldName)}${idx === 0 ? '' : `${idx + 1}`}`;
157
- lambdaFunctionDefinition[fnName] = handlerData;
158
- gqlHandlerContent += `@function(name: "${fnName}") `;
157
+ lambdaFunctionDefinition[fnName] = handlerData.handler;
158
+ const invocationTypeArg = handlerData.invocationType === 'Event' ? ', invocationType: Event)' : ')';
159
+ gqlHandlerContent += `@function(name: "${fnName}"${invocationTypeArg} `;
159
160
  }
160
161
  else {
161
162
  throw new Error(`Invalid value specified for ${functionFieldName} handler.function(). Expected: defineFunction or string.`);
162
163
  }
163
164
  });
164
- return { gqlHandlerContent, lambdaFunctionDefinition };
165
+ return {
166
+ gqlHandlerContent,
167
+ lambdaFunctionDefinition,
168
+ };
165
169
  }
166
170
  function customOperationToGql(typeName, typeDef, authorization, isCustom = false, databaseType, getRefType) {
167
171
  const { arguments: fieldArgs, typeName: opType, returnType, handlers, subscriptionSource, } = typeDef.data;
@@ -814,6 +818,18 @@ const schemaPreprocessor = (schema) => {
814
818
  ? 'dynamodb'
815
819
  : 'sql';
816
820
  const staticSchema = databaseType === 'sql';
821
+ // If the schema contains a custom operation with an async lambda handler,
822
+ // we need to add the EventInvocationResponse custom type to the schema.
823
+ // This is done here so that:
824
+ // - it only happens once per schema
825
+ // - downstream validation based on `getRefTypeForSchema` finds the EventInvocationResponse type
826
+ const containsAsyncLambdaCustomOperation = Object.entries(schema.data.types).find(([_, typeDef]) => {
827
+ return isCustomOperation(typeDef)
828
+ && finalHandlerIsAsyncFunctionHandler(typeDef.data.handlers);
829
+ });
830
+ if (containsAsyncLambdaCustomOperation) {
831
+ schema.data.types['EventInvocationResponse'] = eventInvocationResponseCustomType;
832
+ }
817
833
  const topLevelTypes = sortTopLevelTypes(Object.entries(schema.data.types));
818
834
  const { schemaAuth, functionSchemaAccess } = extractFunctionSchemaAccess(schema.data.authorization);
819
835
  const getRefType = getRefTypeForSchema(schema);
@@ -976,14 +992,22 @@ function validateCustomOperations(typeDef, typeName, authRules, getRefType) {
976
992
  configuredHandlers.add(getBrand(handler));
977
993
  }
978
994
  if (configuredHandlers.size > 1) {
979
- const configuredHandlersStr = JSON.stringify(Array.from(configuredHandlers));
980
- throw new Error(`Field handlers must be of the same type. ${typeName} has been configured with ${configuredHandlersStr}`);
995
+ configuredHandlers.delete('asyncFunctionHandler');
996
+ configuredHandlers.delete('functionHandler');
997
+ if (configuredHandlers.size > 0) {
998
+ const configuredHandlersStr = JSON.stringify(Array.from(configuredHandlers));
999
+ throw new Error(`Field handlers must be of the same type. ${typeName} has been configured with ${configuredHandlersStr}`);
1000
+ }
981
1001
  }
982
1002
  }
983
1003
  if (typeDef.data.returnType === null &&
984
1004
  (opType === 'Query' || opType === 'Mutation' || opType === 'Generation')) {
985
- const typeDescription = opType === 'Generation' ? 'Generation Route' : `Custom ${opType}`;
986
- throw new Error(`Invalid ${typeDescription} definition. A ${typeDescription} must include a return type. ${typeName} has no return type specified.`);
1005
+ // TODO: There should be a more elegant and readable way to handle this check.
1006
+ // Maybe it's not even necessary anymore since we're the setting returnType in the handler() method.
1007
+ if (!handlers || handlers.length === 0 || handlers[handlers.length - 1][brandSymbol] !== 'asyncFunctionHandler') {
1008
+ const typeDescription = opType === 'Generation' ? 'Generation Route' : `Custom ${opType}`;
1009
+ throw new Error(`Invalid ${typeDescription} definition. A ${typeDescription} must include a return type. ${typeName} has no return type specified.`);
1010
+ }
987
1011
  }
988
1012
  if (opType !== 'Subscription' && subscriptionSource.length > 0) {
989
1013
  throw new Error(`The .for() modifier function can only be used with a custom subscription. ${typeName} is not a custom subscription.`);
@@ -1038,7 +1062,10 @@ const isCustomHandler = (handler) => {
1038
1062
  return Array.isArray(handler) && getBrand(handler[0]) === 'customHandler';
1039
1063
  };
1040
1064
  const isFunctionHandler = (handler) => {
1041
- return Array.isArray(handler) && getBrand(handler[0]) === 'functionHandler';
1065
+ return Array.isArray(handler) && ['functionHandler', 'asyncFunctionHandler'].includes(getBrand(handler[0]));
1066
+ };
1067
+ const finalHandlerIsAsyncFunctionHandler = (handler) => {
1068
+ return Array.isArray(handler) && getBrand(handler[handler.length - 1]) === 'asyncFunctionHandler';
1042
1069
  };
1043
1070
  const normalizeDataSourceName = (dataSource) => {
1044
1071
  // default data source
@@ -1092,6 +1119,21 @@ const handleCustom = (handlers, opType, typeName) => {
1092
1119
  };
1093
1120
  return jsFn;
1094
1121
  };
1122
+ const eventInvocationResponseCustomType = {
1123
+ data: {
1124
+ fields: {
1125
+ success: {
1126
+ data: {
1127
+ fieldType: ModelFieldType.Boolean,
1128
+ required: true,
1129
+ array: false,
1130
+ arrayRequired: false,
1131
+ }
1132
+ }
1133
+ },
1134
+ type: 'customType'
1135
+ }
1136
+ };
1095
1137
  function transformCustomOperations(typeDef, typeName, authRules, databaseType, getRefType) {
1096
1138
  const { typeName: opType, handlers } = typeDef.data;
1097
1139
  let jsFunctionForField = undefined;