@aws-amplify/api-graphql 4.0.22 → 4.0.23
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/dist/cjs/Providers/AWSAppSyncRealTimeProvider/index.js +40 -31
- package/dist/cjs/Providers/AWSAppSyncRealTimeProvider/index.js.map +1 -1
- package/dist/cjs/internals/APIClient.js +34 -19
- package/dist/cjs/internals/APIClient.js.map +1 -1
- package/dist/cjs/internals/InternalGraphQLAPI.js +10 -9
- package/dist/cjs/internals/InternalGraphQLAPI.js.map +1 -1
- package/dist/cjs/internals/generateClient.js +4 -1
- package/dist/cjs/internals/generateClient.js.map +1 -1
- package/dist/cjs/internals/generateCustomOperationsProperty.js +6 -1
- package/dist/cjs/internals/generateCustomOperationsProperty.js.map +1 -1
- package/dist/cjs/internals/operations/custom.js +90 -7
- package/dist/cjs/internals/operations/custom.js.map +1 -1
- package/dist/cjs/internals/operations/list.js +1 -1
- package/dist/cjs/internals/operations/list.js.map +1 -1
- package/dist/cjs/internals/server/generateClientWithAmplifyInstance.js +1 -1
- package/dist/cjs/internals/server/generateClientWithAmplifyInstance.js.map +1 -1
- package/dist/cjs/internals/server/generateModelsProperty.js +1 -1
- package/dist/cjs/internals/server/generateModelsProperty.js.map +1 -1
- package/dist/cjs/internals/v6.js +0 -2
- package/dist/cjs/internals/v6.js.map +1 -1
- package/dist/cjs/utils/errors/repackageAuthError.js +1 -1
- package/dist/cjs/utils/errors/repackageAuthError.js.map +1 -1
- package/dist/cjs/utils/resolveOwnerFields.js +3 -0
- package/dist/cjs/utils/resolveOwnerFields.js.map +1 -1
- package/dist/esm/GraphQLAPI.d.ts +2 -2
- package/dist/esm/GraphQLAPI.mjs.map +1 -1
- package/dist/esm/Providers/AWSAppSyncRealTimeProvider/index.d.ts +8 -8
- package/dist/esm/Providers/AWSAppSyncRealTimeProvider/index.mjs +40 -31
- package/dist/esm/Providers/AWSAppSyncRealTimeProvider/index.mjs.map +1 -1
- package/dist/esm/internals/APIClient.d.ts +5 -6
- package/dist/esm/internals/APIClient.mjs +34 -19
- package/dist/esm/internals/APIClient.mjs.map +1 -1
- package/dist/esm/internals/InternalGraphQLAPI.d.ts +1 -1
- package/dist/esm/internals/InternalGraphQLAPI.mjs +10 -9
- package/dist/esm/internals/InternalGraphQLAPI.mjs.map +1 -1
- package/dist/esm/internals/generateClient.mjs +5 -2
- package/dist/esm/internals/generateClient.mjs.map +1 -1
- package/dist/esm/internals/generateCustomOperationsProperty.d.ts +4 -3
- package/dist/esm/internals/generateCustomOperationsProperty.mjs +5 -1
- package/dist/esm/internals/generateCustomOperationsProperty.mjs.map +1 -1
- package/dist/esm/internals/operations/custom.d.ts +6 -3
- package/dist/esm/internals/operations/custom.mjs +90 -7
- package/dist/esm/internals/operations/custom.mjs.map +1 -1
- package/dist/esm/internals/operations/get.d.ts +1 -1
- package/dist/esm/internals/operations/get.mjs.map +1 -1
- package/dist/esm/internals/operations/indexQuery.d.ts +3 -3
- package/dist/esm/internals/operations/indexQuery.mjs.map +1 -1
- package/dist/esm/internals/operations/list.d.ts +1 -1
- package/dist/esm/internals/operations/list.mjs +1 -1
- package/dist/esm/internals/operations/list.mjs.map +1 -1
- package/dist/esm/internals/operations/subscription.d.ts +1 -1
- package/dist/esm/internals/operations/subscription.mjs.map +1 -1
- package/dist/esm/internals/server/generateClientWithAmplifyInstance.d.ts +1 -1
- package/dist/esm/internals/server/generateClientWithAmplifyInstance.mjs +1 -1
- package/dist/esm/internals/server/generateClientWithAmplifyInstance.mjs.map +1 -1
- package/dist/esm/internals/server/generateModelsProperty.d.ts +3 -3
- package/dist/esm/internals/server/generateModelsProperty.mjs +1 -1
- package/dist/esm/internals/server/generateModelsProperty.mjs.map +1 -1
- package/dist/esm/internals/types.d.ts +2 -2
- package/dist/esm/internals/utils/clientProperties/generateModelsProperty.d.ts +2 -2
- package/dist/esm/internals/v6.d.ts +1 -1
- package/dist/esm/internals/v6.mjs +0 -2
- package/dist/esm/internals/v6.mjs.map +1 -1
- package/dist/esm/types/index.d.ts +34 -35
- package/dist/esm/utils/ConnectionStateMonitor.d.ts +2 -2
- package/dist/esm/utils/ConnectionStateMonitor.mjs.map +1 -1
- package/dist/esm/utils/ReachabilityMonitor/index.native.mjs.map +1 -1
- package/dist/esm/utils/errors/repackageAuthError.d.ts +2 -2
- package/dist/esm/utils/errors/repackageAuthError.mjs +1 -1
- package/dist/esm/utils/errors/repackageAuthError.mjs.map +1 -1
- package/dist/esm/utils/findIndexByFields.d.ts +1 -1
- package/dist/esm/utils/resolveOwnerFields.mjs +3 -0
- package/dist/esm/utils/resolveOwnerFields.mjs.map +1 -1
- package/package.json +8 -7
- package/src/GraphQLAPI.ts +4 -3
- package/src/Providers/AWSAppSyncRealTimeProvider/index.ts +84 -64
- package/src/internals/APIClient.ts +83 -58
- package/src/internals/InternalGraphQLAPI.ts +26 -30
- package/src/internals/clientUtils.ts +1 -1
- package/src/internals/generateClient.ts +23 -10
- package/src/internals/generateCustomOperationsProperty.ts +31 -8
- package/src/internals/operations/custom.ts +159 -32
- package/src/internals/operations/get.ts +9 -8
- package/src/internals/operations/indexQuery.ts +12 -11
- package/src/internals/operations/list.ts +11 -15
- package/src/internals/operations/observeQuery.ts +3 -1
- package/src/internals/operations/subscription.ts +12 -10
- package/src/internals/server/generateClientWithAmplifyInstance.ts +7 -7
- package/src/internals/server/generateModelsProperty.ts +12 -6
- package/src/internals/types.ts +2 -2
- package/src/internals/utils/clientProperties/generateEnumsProperty.ts +7 -6
- package/src/internals/utils/clientProperties/generateModelsProperty.ts +8 -7
- package/src/internals/v6.ts +6 -4
- package/src/server/generateClient.ts +4 -1
- package/src/types/PubSub.ts +1 -0
- package/src/types/index.ts +51 -47
- package/src/utils/ConnectionStateMonitor.ts +8 -3
- package/src/utils/ReachabilityMonitor/index.native.ts +1 -1
- package/src/utils/ReachabilityMonitor/index.ts +1 -0
- package/src/utils/ReconnectionMonitor.ts +1 -0
- package/src/utils/errors/repackageAuthError.ts +6 -7
- package/src/utils/findIndexByFields.ts +1 -1
- package/src/utils/resolveConfig.ts +1 -0
- package/src/utils/resolveLibraryOptions.ts +1 -0
- package/src/utils/resolveOwnerFields.ts +16 -7
- package/src/utils/resolvePKFields.ts +1 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"v6.js","sources":["../../../src/internals/v6.ts"],"sourcesContent":["\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.isCancelError = exports.cancel = exports.graphql = void 0;\
|
|
1
|
+
{"version":3,"file":"v6.js","sources":["../../../src/internals/v6.ts"],"sourcesContent":["\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.isCancelError = exports.cancel = exports.graphql = void 0;\nconst GraphQLAPI_1 = require(\"../GraphQLAPI\");\nconst types_1 = require(\"../types\");\n/**\n * Invokes graphql operations against a graphql service, providing correct input and\n * output types if Amplify-generated graphql from a recent version of the CLI/codegen\n * are used *or* correct typing is provided via the type argument.\n *\n * Amplify-generated \"branded\" graphql queries will look similar to this:\n *\n * ```ts\n * //\n * // |-- branding\n * // v\n * export const getModel = `...` as GeneratedQuery<\n * \tGetModelQueryVariables,\n * \tGetModelQuery\n * >;\n * ```\n *\n * If this branding is not in your generated graphql, update to a newer version of\n * CLI/codegen and regenerate your graphql using `amplify codegen`.\n *\n * ## Using Amplify-generated graphql\n *\n * ```ts\n * import * as queries from './graphql/queries';\n *\n * //\n * // |-- correctly typed graphql response containing a Widget\n * // v\n * const queryResult = await graphql({\n * \tquery: queries.getWidget,\n * \tvariables: {\n * \t\tid: \"abc\", // <-- type hinted/enforced\n * \t},\n * });\n *\n * //\n * // |-- a correctly typed Widget\n * // v\n * const fetchedWidget = queryResult.data?.getWidget!;\n * ```\n *\n * ## Custom input + result types\n *\n * To provide input types (`variables`) and result types:\n *\n * ```ts\n * type GetById_NameOnly = {\n * \tvariables: {\n * \t\tid: string\n * \t},\n * \tresult: Promise<{\n * \t\tdata: { getWidget: { name: string } }\n * \t}>\n * }\n *\n * //\n * // |-- type is GetById_NameOnly[\"result\"]\n * // v\n * const result = graphql<GetById_NameOnly>({\n * \tquery: \"...\",\n * \tvariables: { id: \"abc\" } // <-- type of GetById_NameOnly[\"variables\"]\n * });\n * ```\n *\n * ## Custom result type only\n *\n * To specify result types only, use a type that is *not* in the `{variables, result}` shape:\n *\n * ```ts\n * type MyResultType = Promise<{\n * \tdata: {\n * \t\tgetWidget: { name: string }\n * \t}\n * }>\n *\n * //\n * // |-- type is MyResultType\n * // v\n * const result = graphql<MyResultType>({query: \"...\"});\n * ```\n *\n * @param options\n * @param additionalHeaders\n */\nfunction graphql(options, additionalHeaders) {\n // inject client-level auth\n options.authMode = options.authMode || this[types_1.__authMode];\n options.authToken = options.authToken || this[types_1.__authToken];\n /**\n * The correctness of these typings depends on correct string branding or overrides.\n * Neither of these can actually be validated at runtime. Hence, we don't perform\n * any validation or type-guarding here.\n */\n const result = GraphQLAPI_1.GraphQLAPI.graphql(this[types_1.__amplify], options, additionalHeaders);\n return result;\n}\nexports.graphql = graphql;\n/**\n * Cancels an inflight request. Only applicable for graphql queries and mutations\n * @param {any} request - request to cancel\n * @returns - A boolean indicating if the request was cancelled\n */\nfunction cancel(promise, message) {\n return GraphQLAPI_1.GraphQLAPI.cancel(promise, message);\n}\nexports.cancel = cancel;\n/**\n * Checks to see if an error thrown is from an api request cancellation\n * @param {any} error - Any error\n * @returns - A boolean indicating if the error was from an api request cancellation\n */\nfunction isCancelError(error) {\n return GraphQLAPI_1.GraphQLAPI.isCancelError(error);\n}\nexports.isCancelError = isCancelError;\n"],"names":[],"mappings":";;AACA,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AAC9D,OAAO,CAAC,aAAa,GAAG,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,KAAK,CAAC,CAAC;AAClE,MAAM,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;AAC9C,MAAM,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AACpC;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;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,OAAO,CAAC,OAAO,EAAE,iBAAiB,EAAE;AAC7C;AACA,IAAI,OAAO,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;AACpE,IAAI,OAAO,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;AACvE;AACA;AACA;AACA;AACA;AACA,IAAI,MAAM,MAAM,GAAG,YAAY,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,iBAAiB,CAAC,CAAC;AACxG,IAAI,OAAO,MAAM,CAAC;AAClB,CAAC;AACD,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC;AAC1B;AACA;AACA;AACA;AACA;AACA,SAAS,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE;AAClC,IAAI,OAAO,YAAY,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAC5D,CAAC;AACD,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC;AACxB;AACA;AACA;AACA;AACA;AACA,SAAS,aAAa,CAAC,KAAK,EAAE;AAC9B,IAAI,OAAO,YAAY,CAAC,UAAU,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;AACxD,CAAC;AACD,OAAO,CAAC,aAAa,GAAG,aAAa;;"}
|
|
@@ -26,7 +26,7 @@ function repackageUnauthError(content) {
|
|
|
26
26
|
exports.repackageUnauthError = repackageUnauthError;
|
|
27
27
|
function isUnauthError(error) {
|
|
28
28
|
// Error pattern corresponding to appsync calls
|
|
29
|
-
if (error?.
|
|
29
|
+
if (error?.originalError?.name?.startsWith('UnauthorizedException')) {
|
|
30
30
|
return true;
|
|
31
31
|
}
|
|
32
32
|
// Error pattern corresponding to appsync subscriptions
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repackageAuthError.js","sources":["../../../../src/utils/errors/repackageAuthError.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.repackageUnauthError = void 0;\n/**\n * Checks to see if the given response or subscription message contains an\n * unauth error. If it does, it changes the error message to include instructions\n * for the app developer.\n */\nfunction repackageUnauthError(content) {\n if (content.errors && Array.isArray(content.errors)) {\n content.errors.forEach(e => {\n if (isUnauthError(e)) {\n e.message = 'Unauthorized';\n e.recoverySuggestion =\n `If you're calling an Amplify-generated API, make sure ` +\n `to set the \"authMode\" in generateClient({ authMode: '...' }) to the backend authorization ` +\n `rule's auth provider ('apiKey', 'userPool', 'iam', 'oidc', 'lambda')`;\n }\n });\n }\n return content;\n}\nexports.repackageUnauthError = repackageUnauthError;\nfunction isUnauthError(error) {\n // Error pattern corresponding to appsync calls\n if (error?.
|
|
1
|
+
{"version":3,"file":"repackageAuthError.js","sources":["../../../../src/utils/errors/repackageAuthError.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.repackageUnauthError = void 0;\n/**\n * Checks to see if the given response or subscription message contains an\n * unauth error. If it does, it changes the error message to include instructions\n * for the app developer.\n */\nfunction repackageUnauthError(content) {\n if (content.errors && Array.isArray(content.errors)) {\n content.errors.forEach(e => {\n if (isUnauthError(e)) {\n e.message = 'Unauthorized';\n e.recoverySuggestion =\n `If you're calling an Amplify-generated API, make sure ` +\n `to set the \"authMode\" in generateClient({ authMode: '...' }) to the backend authorization ` +\n `rule's auth provider ('apiKey', 'userPool', 'iam', 'oidc', 'lambda')`;\n }\n });\n }\n return content;\n}\nexports.repackageUnauthError = repackageUnauthError;\nfunction isUnauthError(error) {\n // Error pattern corresponding to appsync calls\n if (error?.originalError?.name?.startsWith('UnauthorizedException')) {\n return true;\n }\n // Error pattern corresponding to appsync subscriptions\n if (error.message?.startsWith('Connection failed:') &&\n error.message?.includes('Permission denied')) {\n return true;\n }\n return false;\n}\n"],"names":[],"mappings":";;AACA;AACA;AACA,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AAC9D,OAAO,CAAC,oBAAoB,GAAG,KAAK,CAAC,CAAC;AACtC;AACA;AACA;AACA;AACA;AACA,SAAS,oBAAoB,CAAC,OAAO,EAAE;AACvC,IAAI,IAAI,OAAO,CAAC,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;AACzD,QAAQ,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI;AACpC,YAAY,IAAI,aAAa,CAAC,CAAC,CAAC,EAAE;AAClC,gBAAgB,CAAC,CAAC,OAAO,GAAG,cAAc,CAAC;AAC3C,gBAAgB,CAAC,CAAC,kBAAkB;AACpC,oBAAoB,CAAC,sDAAsD,CAAC;AAC5E,wBAAwB,CAAC,0FAA0F,CAAC;AACpH,wBAAwB,CAAC,oEAAoE,CAAC,CAAC;AAC/F,aAAa;AACb,SAAS,CAAC,CAAC;AACX,KAAK;AACL,IAAI,OAAO,OAAO,CAAC;AACnB,CAAC;AACD,OAAO,CAAC,oBAAoB,GAAG,oBAAoB,CAAC;AACpD,SAAS,aAAa,CAAC,KAAK,EAAE;AAC9B;AACA,IAAI,IAAI,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,UAAU,CAAC,uBAAuB,CAAC,EAAE;AACzE,QAAQ,OAAO,IAAI,CAAC;AACpB,KAAK;AACL;AACA,IAAI,IAAI,KAAK,CAAC,OAAO,EAAE,UAAU,CAAC,oBAAoB,CAAC;AACvD,QAAQ,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC,mBAAmB,CAAC,EAAE;AACtD,QAAQ,OAAO,IAAI,CAAC;AACpB,KAAK;AACL,IAAI,OAAO,KAAK,CAAC;AACjB;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveOwnerFields.js","sources":["../../../src/utils/resolveOwnerFields.ts"],"sourcesContent":["\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.resolveOwnerFields = void 0;\n/**\n * Given an introspection schema model, returns all owner fields.\n *\n * @param model Model from an introspection schema\n * @returns List of owner field names\n */\nfunction resolveOwnerFields(model) {\n const ownerFields = new Set();\n for (const attr of model.attributes || []) {\n if (isAuthAttribute(attr)) {\n for (const rule of attr.properties.rules) {\n if (rule.allow === 'owner') {\n ownerFields.add(rule.ownerField || 'owner');\n }\n }\n }\n }\n return Array.from(ownerFields);\n}\nexports.resolveOwnerFields = resolveOwnerFields;\n/**\n * Type guard that identifies an auth attribute with an attached rules list that\n * specifies an `allow` attribute at a minimum.\n *\n * @param attribute Any object. Ideally a model introspection schema model attribute\n * @returns True if given object is an auth attribute\n */\nfunction isAuthAttribute(attribute) {\n if (attribute?.type === 'auth') {\n if (typeof attribute?.properties === 'object') {\n if (Array.isArray(attribute?.properties?.rules)) {\n return (attribute?.properties?.rules).every(rule => !!rule.allow);\n }\n }\n }\n return false;\n}\n"],"names":[],"mappings":";;AACA,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AAC9D,OAAO,CAAC,kBAAkB,GAAG,KAAK,CAAC,CAAC;AACpC;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,kBAAkB,CAAC,KAAK,EAAE;AACnC,IAAI,MAAM,WAAW,GAAG,IAAI,GAAG,EAAE,CAAC;AAClC,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,UAAU,IAAI,EAAE,EAAE;AAC/C,QAAQ,IAAI,eAAe,CAAC,IAAI,CAAC,EAAE;AACnC,YAAY,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE;AACtD,gBAAgB,IAAI,IAAI,CAAC,KAAK,KAAK,OAAO,EAAE;AAC5C,oBAAoB,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,OAAO,CAAC,CAAC;AAChE,iBAAiB;AACjB,aAAa;AACb,SAAS;AACT,KAAK;AACL,IAAI,OAAO,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;AACnC,CAAC;AACD,OAAO,CAAC,kBAAkB,GAAG,kBAAkB,CAAC;AAChD;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,eAAe,CAAC,SAAS,EAAE;AACpC,IAAI,IAAI,SAAS,EAAE,IAAI,KAAK,MAAM,EAAE;AACpC,QAAQ,IAAI,OAAO,SAAS,EAAE,UAAU,KAAK,QAAQ,EAAE;AACvD,YAAY,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE;AAC7D,gBAAgB,OAAO,CAAC,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAClF,aAAa;AACb,SAAS;AACT,KAAK;AACL,IAAI,OAAO,KAAK,CAAC;AACjB;;"}
|
|
1
|
+
{"version":3,"file":"resolveOwnerFields.js","sources":["../../../src/utils/resolveOwnerFields.ts"],"sourcesContent":["\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.resolveOwnerFields = void 0;\n/**\n * Given an introspection schema model, returns all owner fields.\n *\n * @param model Model from an introspection schema\n * @returns List of owner field names\n */\nfunction resolveOwnerFields(model) {\n const ownerFields = new Set();\n for (const attr of model.attributes || []) {\n if (isAuthAttribute(attr)) {\n for (const rule of attr.properties.rules) {\n if (rule.allow === 'owner') {\n ownerFields.add(rule.ownerField || 'owner');\n }\n else if (rule.allow === 'groups') {\n ownerFields.add(rule.groupsField);\n }\n }\n }\n }\n return Array.from(ownerFields);\n}\nexports.resolveOwnerFields = resolveOwnerFields;\n/**\n * Type guard that identifies an auth attribute with an attached rules list that\n * specifies an `allow` attribute at a minimum.\n *\n * @param attribute Any object. Ideally a model introspection schema model attribute\n * @returns True if given object is an auth attribute\n */\nfunction isAuthAttribute(attribute) {\n if (attribute?.type === 'auth') {\n if (typeof attribute?.properties === 'object') {\n if (Array.isArray(attribute?.properties?.rules)) {\n return (attribute?.properties?.rules).every(rule => !!rule.allow);\n }\n }\n }\n return false;\n}\n"],"names":[],"mappings":";;AACA,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AAC9D,OAAO,CAAC,kBAAkB,GAAG,KAAK,CAAC,CAAC;AACpC;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,kBAAkB,CAAC,KAAK,EAAE;AACnC,IAAI,MAAM,WAAW,GAAG,IAAI,GAAG,EAAE,CAAC;AAClC,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,UAAU,IAAI,EAAE,EAAE;AAC/C,QAAQ,IAAI,eAAe,CAAC,IAAI,CAAC,EAAE;AACnC,YAAY,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE;AACtD,gBAAgB,IAAI,IAAI,CAAC,KAAK,KAAK,OAAO,EAAE;AAC5C,oBAAoB,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,OAAO,CAAC,CAAC;AAChE,iBAAiB;AACjB,qBAAqB,IAAI,IAAI,CAAC,KAAK,KAAK,QAAQ,EAAE;AAClD,oBAAoB,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;AACtD,iBAAiB;AACjB,aAAa;AACb,SAAS;AACT,KAAK;AACL,IAAI,OAAO,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;AACnC,CAAC;AACD,OAAO,CAAC,kBAAkB,GAAG,kBAAkB,CAAC;AAChD;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,eAAe,CAAC,SAAS,EAAE;AACpC,IAAI,IAAI,SAAS,EAAE,IAAI,KAAK,MAAM,EAAE;AACpC,QAAQ,IAAI,OAAO,SAAS,EAAE,UAAU,KAAK,QAAQ,EAAE;AACvD,YAAY,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE;AAC7D,gBAAgB,OAAO,CAAC,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAClF,aAAa;AACb,SAAS;AACT,KAAK;AACL,IAAI,OAAO,KAAK,CAAC;AACjB;;"}
|
package/dist/esm/GraphQLAPI.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { AmplifyClassV6 } from '@aws-amplify/core';
|
|
2
|
-
import { GraphQLOptions, GraphQLResult } from './types';
|
|
3
|
-
import { InternalGraphQLAPIClass } from './internals/InternalGraphQLAPI';
|
|
4
2
|
import { CustomHeaders } from '@aws-amplify/data-schema-types';
|
|
5
3
|
import { Observable } from 'rxjs';
|
|
4
|
+
import { GraphQLOptions, GraphQLResult } from './types';
|
|
5
|
+
import { InternalGraphQLAPIClass } from './internals/InternalGraphQLAPI';
|
|
6
6
|
export declare const graphqlOperation: (query: any, variables?: {}, authToken?: string) => {
|
|
7
7
|
query: any;
|
|
8
8
|
variables: {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GraphQLAPI.mjs","sources":["../../src/GraphQLAPI.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"GraphQLAPI.mjs","sources":["../../src/GraphQLAPI.ts"],"sourcesContent":["import { ApiAction, Category } from '@aws-amplify/core/internals/utils';\nimport { InternalGraphQLAPIClass } from './internals/InternalGraphQLAPI';\nexport const graphqlOperation = (query, variables = {}, authToken) => ({\n query,\n variables,\n authToken,\n});\n/**\n * Export Cloud Logic APIs\n */\nexport class GraphQLAPIClass extends InternalGraphQLAPIClass {\n getModuleName() {\n return 'GraphQLAPI';\n }\n /**\n * Executes a GraphQL operation\n *\n * @param options - GraphQL Options\n * @param [additionalHeaders] - headers to merge in after any `libraryConfigHeaders` set in the config\n * @returns An Observable if the query is a subscription query, else a promise of the graphql result.\n */\n graphql(amplify, options, additionalHeaders) {\n return super.graphql(amplify, options, additionalHeaders, {\n category: Category.API,\n action: ApiAction.GraphQl,\n });\n }\n /**\n * Checks to see if an error thrown is from an api request cancellation\n * @param error - Any error\n * @returns A boolean indicating if the error was from an api request cancellation\n */\n isCancelError(error) {\n return super.isCancelError(error);\n }\n /**\n * Cancels an inflight request. Only applicable for graphql queries and mutations\n * @param {any} request - request to cancel\n * @returns A boolean indicating if the request was cancelled\n */\n cancel(request, message) {\n return super.cancel(request, message);\n }\n}\nexport const GraphQLAPI = new GraphQLAPIClass();\n"],"names":[],"mappings":";;;AAEY,MAAC,gBAAgB,GAAG,CAAC,KAAK,EAAE,SAAS,GAAG,EAAE,EAAE,SAAS,MAAM;AACvE,IAAI,KAAK;AACT,IAAI,SAAS;AACb,IAAI,SAAS;AACb,CAAC,EAAE;AACH;AACA;AACA;AACO,MAAM,eAAe,SAAS,uBAAuB,CAAC;AAC7D,IAAI,aAAa,GAAG;AACpB,QAAQ,OAAO,YAAY,CAAC;AAC5B,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE;AACjD,QAAQ,OAAO,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE;AAClE,YAAY,QAAQ,EAAE,QAAQ,CAAC,GAAG;AAClC,YAAY,MAAM,EAAE,SAAS,CAAC,OAAO;AACrC,SAAS,CAAC,CAAC;AACX,KAAK;AACL;AACA;AACA;AACA;AACA;AACA,IAAI,aAAa,CAAC,KAAK,EAAE;AACzB,QAAQ,OAAO,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;AAC1C,KAAK;AACL;AACA;AACA;AACA;AACA;AACA,IAAI,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE;AAC7B,QAAQ,OAAO,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAC9C,KAAK;AACL,CAAC;AACW,MAAC,UAAU,GAAG,IAAI,eAAe;;;;"}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { Observable } from 'rxjs';
|
|
2
|
-
import {
|
|
2
|
+
import { CustomUserAgentDetails, DocumentType, GraphQLAuthMode } from '@aws-amplify/core/internals/utils';
|
|
3
|
+
import { CustomHeaders } from '@aws-amplify/data-schema-types';
|
|
3
4
|
import { PubSubContentObserver } from '../../types/PubSub';
|
|
4
5
|
import { SUBSCRIPTION_STATUS } from '../constants';
|
|
5
|
-
|
|
6
|
-
export type ObserverQuery = {
|
|
6
|
+
export interface ObserverQuery {
|
|
7
7
|
observer: PubSubContentObserver;
|
|
8
8
|
query: string;
|
|
9
9
|
variables: Record<string, DocumentType>;
|
|
10
10
|
subscriptionState: SUBSCRIPTION_STATUS;
|
|
11
|
-
subscriptionReadyCallback
|
|
12
|
-
subscriptionFailedCallback?:
|
|
11
|
+
subscriptionReadyCallback?(): void;
|
|
12
|
+
subscriptionFailedCallback?(reason?: any): void;
|
|
13
13
|
startAckTimeoutId?: ReturnType<typeof setTimeout>;
|
|
14
|
-
}
|
|
14
|
+
}
|
|
15
15
|
export interface AWSAppSyncRealTimeProviderOptions {
|
|
16
16
|
appSyncGraphqlEndpoint?: string;
|
|
17
17
|
authenticationType?: GraphQLAuthMode;
|
|
@@ -19,7 +19,7 @@ export interface AWSAppSyncRealTimeProviderOptions {
|
|
|
19
19
|
variables?: Record<string, DocumentType>;
|
|
20
20
|
apiKey?: string;
|
|
21
21
|
region?: string;
|
|
22
|
-
libraryConfigHeaders
|
|
22
|
+
libraryConfigHeaders?(): Promise<Record<string, unknown> | Headers>;
|
|
23
23
|
additionalHeaders?: CustomHeaders;
|
|
24
24
|
additionalCustomHeaders?: Record<string, string>;
|
|
25
25
|
authToken?: string;
|
|
@@ -36,7 +36,7 @@ export declare class AWSAppSyncRealTimeProvider {
|
|
|
36
36
|
private readonly connectionStateMonitor;
|
|
37
37
|
private readonly reconnectionMonitor;
|
|
38
38
|
private connectionStateMonitorSubscription;
|
|
39
|
-
constructor(
|
|
39
|
+
constructor();
|
|
40
40
|
/**
|
|
41
41
|
* Mark the socket closed and release all active listeners
|
|
42
42
|
*/
|
|
@@ -14,10 +14,10 @@ const logger = new ConsoleLogger('AWSAppSyncRealTimeProvider');
|
|
|
14
14
|
const dispatchApiEvent = (payload) => {
|
|
15
15
|
Hub.dispatch('api', payload, 'PubSub', AMPLIFY_SYMBOL);
|
|
16
16
|
};
|
|
17
|
-
const standardDomainPattern = /^https:\/\/\w{26}\.appsync
|
|
17
|
+
const standardDomainPattern = /^https:\/\/\w{26}\.appsync-api\.\w{2}(?:(?:-\w{2,})+)-\d\.amazonaws.com(?:\.cn)?\/graphql$/i;
|
|
18
18
|
const customDomainPath = '/realtime';
|
|
19
19
|
class AWSAppSyncRealTimeProvider {
|
|
20
|
-
constructor(
|
|
20
|
+
constructor() {
|
|
21
21
|
this.socketStatus = SOCKET_STATUS.CLOSED;
|
|
22
22
|
this.keepAliveTimeout = DEFAULT_KEEP_ALIVE_TIMEOUT;
|
|
23
23
|
this.subscriptionObserverMap = new Map();
|
|
@@ -119,10 +119,9 @@ class AWSAppSyncRealTimeProvider {
|
|
|
119
119
|
});
|
|
120
120
|
}
|
|
121
121
|
};
|
|
122
|
-
let reconnectSubscription;
|
|
123
122
|
// Add an observable to the reconnection list to manage reconnection for this subscription
|
|
124
|
-
reconnectSubscription = new Observable(
|
|
125
|
-
this.reconnectionMonitor.addObserver(
|
|
123
|
+
const reconnectSubscription = new Observable(reconnectSubscriptionObserver => {
|
|
124
|
+
this.reconnectionMonitor.addObserver(reconnectSubscriptionObserver);
|
|
126
125
|
}).subscribe(() => {
|
|
127
126
|
startSubscription();
|
|
128
127
|
});
|
|
@@ -245,7 +244,7 @@ class AWSAppSyncRealTimeProvider {
|
|
|
245
244
|
subscriptionReadyCallback,
|
|
246
245
|
subscriptionFailedCallback,
|
|
247
246
|
startAckTimeoutId: setTimeout(() => {
|
|
248
|
-
this._timeoutStartSubscriptionAck
|
|
247
|
+
this._timeoutStartSubscriptionAck(subscriptionId);
|
|
249
248
|
}, START_ACK_TIMEOUT),
|
|
250
249
|
});
|
|
251
250
|
if (this.awsRealTimeSocket) {
|
|
@@ -257,7 +256,7 @@ class AWSAppSyncRealTimeProvider {
|
|
|
257
256
|
logger.debug({ err });
|
|
258
257
|
const message = String(err.message ?? '');
|
|
259
258
|
// Resolving to give the state observer time to propogate the update
|
|
260
|
-
|
|
259
|
+
this.connectionStateMonitor.record(CONNECTION_CHANGE.CLOSED);
|
|
261
260
|
// Capture the error only when the network didn't cause disruption
|
|
262
261
|
if (this.connectionState !== ConnectionState.ConnectionDisruptedPendingNetwork) {
|
|
263
262
|
// When the error is non-retriable, error out the observable
|
|
@@ -287,15 +286,15 @@ class AWSAppSyncRealTimeProvider {
|
|
|
287
286
|
const { subscriptionState } = subscriptionObserver;
|
|
288
287
|
// This in case unsubscribe is invoked before sending start subscription message
|
|
289
288
|
if (subscriptionState === SUBSCRIPTION_STATUS.PENDING) {
|
|
290
|
-
return new Promise((
|
|
291
|
-
const { observer, subscriptionState, variables, query } = subscriptionObserver;
|
|
289
|
+
return new Promise((resolve, reject) => {
|
|
290
|
+
const { observer, subscriptionState: observedSubscriptionState, variables, query, } = subscriptionObserver;
|
|
292
291
|
this.subscriptionObserverMap.set(subscriptionId, {
|
|
293
292
|
observer,
|
|
294
|
-
subscriptionState,
|
|
293
|
+
subscriptionState: observedSubscriptionState,
|
|
295
294
|
variables,
|
|
296
295
|
query,
|
|
297
|
-
subscriptionReadyCallback:
|
|
298
|
-
subscriptionFailedCallback:
|
|
296
|
+
subscriptionReadyCallback: resolve,
|
|
297
|
+
subscriptionFailedCallback: reject,
|
|
299
298
|
});
|
|
300
299
|
});
|
|
301
300
|
}
|
|
@@ -406,7 +405,9 @@ class AWSAppSyncRealTimeProvider {
|
|
|
406
405
|
clearTimeout(this.keepAliveTimeoutId);
|
|
407
406
|
if (this.keepAliveAlertTimeoutId)
|
|
408
407
|
clearTimeout(this.keepAliveAlertTimeoutId);
|
|
409
|
-
this.keepAliveTimeoutId = setTimeout(() =>
|
|
408
|
+
this.keepAliveTimeoutId = setTimeout(() => {
|
|
409
|
+
this._errorDisconnect(CONTROL_MSG.TIMEOUT_DISCONNECT);
|
|
410
|
+
}, this.keepAliveTimeout);
|
|
410
411
|
this.keepAliveAlertTimeoutId = setTimeout(() => {
|
|
411
412
|
this.connectionStateMonitor.record(CONNECTION_CHANGE.KEEP_ALIVE_MISSED);
|
|
412
413
|
}, DEFAULT_KEEP_ALIVE_ALERT_TIMEOUT);
|
|
@@ -470,8 +471,10 @@ class AWSAppSyncRealTimeProvider {
|
|
|
470
471
|
if (this.socketStatus === SOCKET_STATUS.READY) {
|
|
471
472
|
return;
|
|
472
473
|
}
|
|
473
|
-
|
|
474
|
-
|
|
474
|
+
// TODO(Eslint): refactor to now use async function as the promise executor
|
|
475
|
+
// eslint-disable-next-line no-async-promise-executor
|
|
476
|
+
return new Promise(async (resolve, reject) => {
|
|
477
|
+
this.promiseArray.push({ res: resolve, rej: reject });
|
|
475
478
|
if (this.socketStatus === SOCKET_STATUS.CLOSED) {
|
|
476
479
|
try {
|
|
477
480
|
this.socketStatus = SOCKET_STATUS.CONNECTING;
|
|
@@ -514,7 +517,9 @@ class AWSAppSyncRealTimeProvider {
|
|
|
514
517
|
}
|
|
515
518
|
catch (err) {
|
|
516
519
|
logger.debug('Connection exited with', err);
|
|
517
|
-
this.promiseArray.forEach(({ rej }) =>
|
|
520
|
+
this.promiseArray.forEach(({ rej }) => {
|
|
521
|
+
rej(err);
|
|
522
|
+
});
|
|
518
523
|
this.promiseArray = [];
|
|
519
524
|
if (this.awsRealTimeSocket &&
|
|
520
525
|
this.awsRealTimeSocket.readyState === WebSocket.OPEN) {
|
|
@@ -536,23 +541,23 @@ class AWSAppSyncRealTimeProvider {
|
|
|
536
541
|
// Step 1: connect websocket
|
|
537
542
|
try {
|
|
538
543
|
await (() => {
|
|
539
|
-
return new Promise((
|
|
544
|
+
return new Promise((resolve, reject) => {
|
|
540
545
|
const newSocket = this.getNewWebSocket(awsRealTimeUrl, 'graphql-ws');
|
|
541
546
|
newSocket.onerror = () => {
|
|
542
547
|
logger.debug(`WebSocket connection error`);
|
|
543
548
|
};
|
|
544
549
|
newSocket.onclose = () => {
|
|
545
|
-
|
|
550
|
+
reject(new Error('Connection handshake error'));
|
|
546
551
|
};
|
|
547
552
|
newSocket.onopen = () => {
|
|
548
553
|
this.awsRealTimeSocket = newSocket;
|
|
549
|
-
|
|
554
|
+
resolve();
|
|
550
555
|
};
|
|
551
556
|
});
|
|
552
557
|
})();
|
|
553
558
|
// Step 2: wait for ack from AWS AppSyncReaTime after sending init
|
|
554
559
|
await (() => {
|
|
555
|
-
return new Promise((
|
|
560
|
+
return new Promise((resolve, reject) => {
|
|
556
561
|
if (this.awsRealTimeSocket) {
|
|
557
562
|
let ackOk = false;
|
|
558
563
|
this.awsRealTimeSocket.onerror = error => {
|
|
@@ -560,7 +565,7 @@ class AWSAppSyncRealTimeProvider {
|
|
|
560
565
|
};
|
|
561
566
|
this.awsRealTimeSocket.onclose = event => {
|
|
562
567
|
logger.debug(`WebSocket closed ${event.reason}`);
|
|
563
|
-
|
|
568
|
+
reject(new Error(JSON.stringify(event)));
|
|
564
569
|
};
|
|
565
570
|
this.awsRealTimeSocket.onmessage = (message) => {
|
|
566
571
|
if (typeof message.data !== 'string') {
|
|
@@ -584,25 +589,29 @@ class AWSAppSyncRealTimeProvider {
|
|
|
584
589
|
this._errorDisconnect(CONTROL_MSG.CONNECTION_CLOSED);
|
|
585
590
|
};
|
|
586
591
|
}
|
|
587
|
-
|
|
592
|
+
resolve('Cool, connected to AWS AppSyncRealTime');
|
|
588
593
|
return;
|
|
589
594
|
}
|
|
590
595
|
if (type === MESSAGE_TYPES.GQL_CONNECTION_ERROR) {
|
|
591
596
|
const { payload: { errors: [{ errorType = '', errorCode = 0 } = {}] = [], } = {}, } = data;
|
|
592
|
-
|
|
597
|
+
// TODO(Eslint): refactor to reject an Error object instead of a plain object
|
|
598
|
+
// eslint-disable-next-line prefer-promise-reject-errors
|
|
599
|
+
reject({ errorType, errorCode });
|
|
593
600
|
}
|
|
594
601
|
};
|
|
595
602
|
const gqlInit = {
|
|
596
603
|
type: MESSAGE_TYPES.GQL_CONNECTION_INIT,
|
|
597
604
|
};
|
|
598
605
|
this.awsRealTimeSocket.send(JSON.stringify(gqlInit));
|
|
599
|
-
const checkAckOk = (
|
|
600
|
-
if (!
|
|
606
|
+
const checkAckOk = (targetAckOk) => {
|
|
607
|
+
if (!targetAckOk) {
|
|
601
608
|
this.connectionStateMonitor.record(CONNECTION_CHANGE.CONNECTION_FAILED);
|
|
602
|
-
|
|
609
|
+
reject(new Error(`Connection timeout: ack from AWSAppSyncRealTime was not received after ${CONNECTION_INIT_TIMEOUT} ms`));
|
|
603
610
|
}
|
|
604
611
|
};
|
|
605
|
-
setTimeout(() =>
|
|
612
|
+
setTimeout(() => {
|
|
613
|
+
checkAckOk(ackOk);
|
|
614
|
+
}, CONNECTION_INIT_TIMEOUT);
|
|
606
615
|
}
|
|
607
616
|
});
|
|
608
617
|
})();
|
|
@@ -664,7 +673,7 @@ class AWSAppSyncRealTimeProvider {
|
|
|
664
673
|
}
|
|
665
674
|
async _awsRealTimeApiKeyHeader({ apiKey, host, }) {
|
|
666
675
|
const dt = new Date();
|
|
667
|
-
const dtStr = dt.toISOString().replace(/[
|
|
676
|
+
const dtStr = dt.toISOString().replace(/[:-]|\.\d{3}/g, '');
|
|
668
677
|
return {
|
|
669
678
|
host,
|
|
670
679
|
'x-amz-date': dtStr,
|
|
@@ -683,7 +692,7 @@ class AWSAppSyncRealTimeProvider {
|
|
|
683
692
|
method: 'POST',
|
|
684
693
|
headers: { ...AWS_APPSYNC_REALTIME_HEADERS },
|
|
685
694
|
};
|
|
686
|
-
const
|
|
695
|
+
const signedParams = signRequest({
|
|
687
696
|
headers: request.headers,
|
|
688
697
|
method: request.method,
|
|
689
698
|
url: new AmplifyUrl(request.url),
|
|
@@ -694,7 +703,7 @@ class AWSAppSyncRealTimeProvider {
|
|
|
694
703
|
signingRegion: endpointInfo.region,
|
|
695
704
|
signingService: endpointInfo.service,
|
|
696
705
|
});
|
|
697
|
-
return
|
|
706
|
+
return signedParams.headers;
|
|
698
707
|
}
|
|
699
708
|
_customAuthHeader({ host, additionalCustomHeaders, }) {
|
|
700
709
|
/**
|
|
@@ -702,7 +711,7 @@ class AWSAppSyncRealTimeProvider {
|
|
|
702
711
|
* the headers that are returned by that function will already have been
|
|
703
712
|
* provided before this function is called.
|
|
704
713
|
*/
|
|
705
|
-
if (!additionalCustomHeaders?.
|
|
714
|
+
if (!additionalCustomHeaders?.Authorization) {
|
|
706
715
|
throw new Error('No auth token specified');
|
|
707
716
|
}
|
|
708
717
|
return {
|