@midwayjs/faas 3.3.4 → 3.3.6-beta.2
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 +1260 -0
- package/dist/framework.d.ts +13 -4
- package/dist/framework.js +128 -10
- package/dist/interface.d.ts +19 -1
- package/dist/interface.js +6 -0
- package/package.json +9 -8
- package/LICENSE +0 -21
package/dist/framework.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { FaaSContext, IFaaSConfigurationOptions, IMidwayFaaSApplication, InvokeOptions } from './interface';
|
|
3
|
+
import { BaseFramework, ContextMiddlewareManager, FunctionMiddleware, IMidwayBootstrapOptions, MidwayEnvironmentService, MidwayFrameworkType, MidwayMiddlewareService, RouterInfo } from '@midwayjs/core';
|
|
3
4
|
import { LoggerOptions } from '@midwayjs/logger';
|
|
5
|
+
import * as http from 'http';
|
|
4
6
|
export declare class MidwayFaaSFramework extends BaseFramework<IMidwayFaaSApplication, FaaSContext, IFaaSConfigurationOptions> {
|
|
5
7
|
protected defaultHandlerMethod: string;
|
|
6
8
|
protected funMappingStore: Map<string, RouterInfo>;
|
|
@@ -9,6 +11,11 @@ export declare class MidwayFaaSFramework extends BaseFramework<IMidwayFaaSApplic
|
|
|
9
11
|
app: IMidwayFaaSApplication;
|
|
10
12
|
private isReplaceLogger;
|
|
11
13
|
private developmentRun;
|
|
14
|
+
private serverlessRoutes;
|
|
15
|
+
private server;
|
|
16
|
+
private respond;
|
|
17
|
+
private applicationAdapter;
|
|
18
|
+
protected eventMiddlewareManager: ContextMiddlewareManager<FaaSContext, unknown, unknown>;
|
|
12
19
|
environmentService: MidwayEnvironmentService;
|
|
13
20
|
middlewareService: MidwayMiddlewareService<FaaSContext, any>;
|
|
14
21
|
configure(options: IFaaSConfigurationOptions): any;
|
|
@@ -16,17 +23,19 @@ export declare class MidwayFaaSFramework extends BaseFramework<IMidwayFaaSApplic
|
|
|
16
23
|
applicationInitialize(options: IMidwayBootstrapOptions): Promise<void>;
|
|
17
24
|
run(): Promise<void>;
|
|
18
25
|
getFrameworkType(): MidwayFrameworkType;
|
|
19
|
-
handleInvokeWrapper(handlerMapping: string): (...args: any[]) => Promise<any>;
|
|
26
|
+
handleInvokeWrapper(handlerMapping: string, options?: InvokeOptions): (...args: any[]) => Promise<any>;
|
|
20
27
|
/**
|
|
21
28
|
* @deprecated
|
|
22
29
|
* @param middlewareId
|
|
23
30
|
*/
|
|
24
31
|
generateMiddleware(middlewareId: string): Promise<FunctionMiddleware<FaaSContext, any>>;
|
|
25
|
-
getContext(context
|
|
32
|
+
getContext(context?: any): any;
|
|
26
33
|
private invokeHandler;
|
|
27
34
|
protected getFunctionHandler(ctx: any, args: any, target: any, method: any): string;
|
|
28
35
|
createLogger(name: string, option?: LoggerOptions): import("@midwayjs/core").ILogger;
|
|
29
36
|
getFrameworkName(): string;
|
|
37
|
+
getServer(): http.Server;
|
|
38
|
+
protected createHttpContext(req: any, res: any): Promise<unknown>;
|
|
30
39
|
}
|
|
31
40
|
export declare const createModuleServerlessFramework: (globalOption: Omit<IMidwayBootstrapOptions, 'applicationContext'> & IFaaSConfigurationOptions) => Promise<MidwayFaaSFramework>;
|
|
32
41
|
//# sourceMappingURL=framework.d.ts.map
|
package/dist/framework.js
CHANGED
|
@@ -10,10 +10,13 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.createModuleServerlessFramework = exports.MidwayFaaSFramework = void 0;
|
|
13
|
+
const interface_1 = require("./interface");
|
|
13
14
|
const core_1 = require("@midwayjs/core");
|
|
14
15
|
const decorator_1 = require("@midwayjs/decorator");
|
|
15
16
|
const simple_lock_1 = require("@midwayjs/simple-lock");
|
|
16
17
|
const logger_1 = require("@midwayjs/logger");
|
|
18
|
+
const serverless_http_parser_1 = require("@midwayjs/serverless-http-parser");
|
|
19
|
+
const http = require("http");
|
|
17
20
|
const LOCK_KEY = '_faas_starter_start_key';
|
|
18
21
|
let MidwayFaaSFramework = class MidwayFaaSFramework extends core_1.BaseFramework {
|
|
19
22
|
constructor() {
|
|
@@ -23,6 +26,8 @@ let MidwayFaaSFramework = class MidwayFaaSFramework extends core_1.BaseFramework
|
|
|
23
26
|
this.lock = new simple_lock_1.default();
|
|
24
27
|
this.isReplaceLogger = process.env['MIDWAY_SERVERLESS_REPLACE_LOGGER'] === 'true';
|
|
25
28
|
this.developmentRun = false;
|
|
29
|
+
this.serverlessRoutes = [];
|
|
30
|
+
this.eventMiddlewareManager = this.createMiddlewareManager();
|
|
26
31
|
}
|
|
27
32
|
configure(options) {
|
|
28
33
|
var _a;
|
|
@@ -39,13 +44,14 @@ let MidwayFaaSFramework = class MidwayFaaSFramework extends core_1.BaseFramework
|
|
|
39
44
|
return !this.developmentRun;
|
|
40
45
|
}
|
|
41
46
|
async applicationInitialize(options) {
|
|
42
|
-
var _a;
|
|
47
|
+
var _a, _b;
|
|
43
48
|
if (!this.logger) {
|
|
44
49
|
this.logger = options.logger || logger_1.loggers.getLogger('appLogger');
|
|
45
50
|
}
|
|
51
|
+
this.applicationAdapter = this.configurationOptions.applicationAdapter;
|
|
46
52
|
this.app =
|
|
47
|
-
((_a = this.
|
|
48
|
-
|
|
53
|
+
((_a = this.applicationAdapter) === null || _a === void 0 ? void 0 : _a.getApplication()) ||
|
|
54
|
+
new serverless_http_parser_1.Application();
|
|
49
55
|
this.defineApplicationProperties({
|
|
50
56
|
/**
|
|
51
57
|
* return init context value such as aliyun fc
|
|
@@ -68,38 +74,91 @@ let MidwayFaaSFramework = class MidwayFaaSFramework extends core_1.BaseFramework
|
|
|
68
74
|
var _a;
|
|
69
75
|
return (_a = this.configurationOptions.applicationAdapter) === null || _a === void 0 ? void 0 : _a.getFunctionServiceName();
|
|
70
76
|
},
|
|
77
|
+
useEventMiddleware: () => { },
|
|
71
78
|
});
|
|
72
79
|
// hack use method
|
|
73
80
|
this.app.originUse = this.app.use;
|
|
74
81
|
this.app.use = this.app.useMiddleware;
|
|
82
|
+
if ((_b = this.configurationOptions.applicationAdapter) === null || _b === void 0 ? void 0 : _b.runAppHook) {
|
|
83
|
+
this.configurationOptions.applicationAdapter.runAppHook(this.app);
|
|
84
|
+
}
|
|
75
85
|
}
|
|
76
86
|
async run() {
|
|
77
87
|
return this.lock.sureOnce(async () => {
|
|
78
|
-
var _a;
|
|
88
|
+
var _a, _b;
|
|
79
89
|
// set app keys
|
|
80
90
|
this.app['keys'] = (_a = this.configService.getConfiguration('keys')) !== null && _a !== void 0 ? _a : '';
|
|
81
91
|
// store all http function entry
|
|
82
92
|
const collector = new core_1.ServerlessTriggerCollector();
|
|
83
93
|
const functionList = await collector.getFunctionList();
|
|
84
94
|
for (const funcInfo of functionList) {
|
|
95
|
+
// store handler
|
|
85
96
|
this.funMappingStore.set(funcInfo.funcHandlerName, funcInfo);
|
|
97
|
+
if (funcInfo.url) {
|
|
98
|
+
// store router
|
|
99
|
+
this.serverlessRoutes.push({
|
|
100
|
+
matchPattern: (0, core_1.pathToRegexp)(funcInfo.url, [], { end: false }),
|
|
101
|
+
funcInfo: funcInfo,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
this.respond = this.app.callback();
|
|
106
|
+
if (this.environmentService.isDevelopmentEnvironment()) {
|
|
107
|
+
const faasConfig = (_b = this.configService.getConfiguration('faas')) !== null && _b !== void 0 ? _b : {};
|
|
108
|
+
this.server = await new Promise(resolve => {
|
|
109
|
+
const server = http.createServer((req, res) => {
|
|
110
|
+
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
111
|
+
// create event and invoke
|
|
112
|
+
this.handleInvokeWrapper(url.pathname)(req, res, {});
|
|
113
|
+
});
|
|
114
|
+
if (faasConfig['port']) {
|
|
115
|
+
server.listen(faasConfig['port']);
|
|
116
|
+
}
|
|
117
|
+
resolve(server);
|
|
118
|
+
});
|
|
86
119
|
}
|
|
87
120
|
}, LOCK_KEY);
|
|
88
121
|
}
|
|
89
122
|
getFrameworkType() {
|
|
90
123
|
return core_1.MidwayFrameworkType.FAAS;
|
|
91
124
|
}
|
|
92
|
-
handleInvokeWrapper(handlerMapping) {
|
|
93
|
-
|
|
125
|
+
handleInvokeWrapper(handlerMapping, options) {
|
|
126
|
+
let funOptions = this.funMappingStore.get(handlerMapping);
|
|
94
127
|
return async (...args) => {
|
|
128
|
+
var _a, _b;
|
|
95
129
|
if (args.length === 0) {
|
|
96
130
|
throw new Error('first parameter must be function context');
|
|
97
131
|
}
|
|
132
|
+
const event = args[0];
|
|
133
|
+
const isLegacyMode = !options;
|
|
134
|
+
const isHttpFunction = (options === null || options === void 0 ? void 0 : options.triggerType) === interface_1.TRIGGER_TYPE_ENUM.HTTP ||
|
|
135
|
+
event.constructor.name === 'IncomingMessage' ||
|
|
136
|
+
event.constructor.name === 'EventEmitter' ||
|
|
137
|
+
!!(event.headers && event.get);
|
|
138
|
+
if (!funOptions && isHttpFunction) {
|
|
139
|
+
for (const item of this.serverlessRoutes) {
|
|
140
|
+
if (item.matchPattern.test(event.path)) {
|
|
141
|
+
funOptions = item.funcInfo;
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
98
146
|
if (!funOptions) {
|
|
99
147
|
throw new Error(`function handler = ${handlerMapping} not found`);
|
|
100
148
|
}
|
|
101
|
-
|
|
102
|
-
|
|
149
|
+
let context;
|
|
150
|
+
if (isLegacyMode) {
|
|
151
|
+
context = this.getContext(args.shift());
|
|
152
|
+
}
|
|
153
|
+
else if (isHttpFunction) {
|
|
154
|
+
const newReq = ((_a = this.applicationAdapter) === null || _a === void 0 ? void 0 : _a.runRequestHook(...args)) ||
|
|
155
|
+
new serverless_http_parser_1.HTTPRequest(args[0], args[1]);
|
|
156
|
+
const newRes = new serverless_http_parser_1.HTTPResponse();
|
|
157
|
+
context = this.getContext(await this.createHttpContext(newReq, newRes));
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
context = this.getContext(await ((_b = this.applicationAdapter) === null || _b === void 0 ? void 0 : _b.runEventHook(...args)));
|
|
161
|
+
}
|
|
103
162
|
const globalMiddlewareFn = await this.applyMiddleware();
|
|
104
163
|
const middlewareManager = new core_1.ContextMiddlewareManager();
|
|
105
164
|
middlewareManager.insertLast(globalMiddlewareFn);
|
|
@@ -122,7 +181,58 @@ let MidwayFaaSFramework = class MidwayFaaSFramework extends core_1.BaseFramework
|
|
|
122
181
|
return await fn(ctx, next);
|
|
123
182
|
});
|
|
124
183
|
const composeMiddleware = await this.middlewareService.compose(middlewareManager, this.app);
|
|
125
|
-
|
|
184
|
+
const result = await composeMiddleware(context);
|
|
185
|
+
if (isLegacyMode) {
|
|
186
|
+
return result;
|
|
187
|
+
}
|
|
188
|
+
else if (isHttpFunction) {
|
|
189
|
+
if (!context.response._explicitStatus) {
|
|
190
|
+
if (context.body === null || context.body === 'undefined') {
|
|
191
|
+
context.body = '';
|
|
192
|
+
context.type = 'text';
|
|
193
|
+
context.status = 204;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
let encoded = false;
|
|
197
|
+
let data = context.body;
|
|
198
|
+
if (typeof data === 'string') {
|
|
199
|
+
if (!context.type) {
|
|
200
|
+
context.type = 'text/plain';
|
|
201
|
+
}
|
|
202
|
+
context.body = data;
|
|
203
|
+
}
|
|
204
|
+
else if (Buffer.isBuffer(data)) {
|
|
205
|
+
encoded = true;
|
|
206
|
+
if (!context.type) {
|
|
207
|
+
context.type = 'application/octet-stream';
|
|
208
|
+
}
|
|
209
|
+
// data is reserved as buffer
|
|
210
|
+
context.body = data.toString('base64');
|
|
211
|
+
}
|
|
212
|
+
else if (typeof data === 'object') {
|
|
213
|
+
if (!context.type) {
|
|
214
|
+
context.type = 'application/json';
|
|
215
|
+
}
|
|
216
|
+
// set data to string
|
|
217
|
+
context.body = data = JSON.stringify(data);
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
if (!context.type) {
|
|
221
|
+
context.type = 'text/plain';
|
|
222
|
+
}
|
|
223
|
+
// set data to string
|
|
224
|
+
context.body = data = data + '';
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
isBase64Encoded: encoded,
|
|
228
|
+
statusCode: context.status,
|
|
229
|
+
headers: context.res.headers,
|
|
230
|
+
body: context.body,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
return result;
|
|
235
|
+
}
|
|
126
236
|
};
|
|
127
237
|
}
|
|
128
238
|
/**
|
|
@@ -133,7 +243,7 @@ let MidwayFaaSFramework = class MidwayFaaSFramework extends core_1.BaseFramework
|
|
|
133
243
|
const mwIns = await this.getApplicationContext().getAsync(middlewareId);
|
|
134
244
|
return mwIns.resolve();
|
|
135
245
|
}
|
|
136
|
-
getContext(context) {
|
|
246
|
+
getContext(context = {}) {
|
|
137
247
|
if (!context.env) {
|
|
138
248
|
context.env = this.environmentService.getCurrentEnvironment();
|
|
139
249
|
}
|
|
@@ -201,6 +311,14 @@ let MidwayFaaSFramework = class MidwayFaaSFramework extends core_1.BaseFramework
|
|
|
201
311
|
getFrameworkName() {
|
|
202
312
|
return 'midway:faas';
|
|
203
313
|
}
|
|
314
|
+
getServer() {
|
|
315
|
+
return this.server;
|
|
316
|
+
}
|
|
317
|
+
async createHttpContext(req, res) {
|
|
318
|
+
return new Promise(resolve => {
|
|
319
|
+
this.respond(req, res, resolve);
|
|
320
|
+
});
|
|
321
|
+
}
|
|
204
322
|
};
|
|
205
323
|
__decorate([
|
|
206
324
|
(0, decorator_1.Inject)(),
|
package/dist/interface.d.ts
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { MidwayRequestContainer, IMidwayApplication, IConfigurationOptions, IMidwayContext, NextFunction as BaseNextFunction } from '@midwayjs/core';
|
|
2
2
|
import { FaaSHTTPContext } from '@midwayjs/faas-typings';
|
|
3
3
|
import { ILogger } from '@midwayjs/logger';
|
|
4
|
+
import { Application as ServerlessHttpApplication } from '@midwayjs/serverless-http-parser';
|
|
4
5
|
export interface FaaSContext extends IMidwayContext<FaaSHTTPContext> {
|
|
5
6
|
logger: ILogger;
|
|
6
7
|
env: string;
|
|
7
8
|
requestContext: MidwayRequestContainer;
|
|
8
9
|
originContext: any;
|
|
9
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* @deprecated
|
|
13
|
+
*/
|
|
10
14
|
export declare type FaaSMiddleware = ((context: FaaSContext, next: () => Promise<any>) => any) | string;
|
|
11
15
|
export declare type IMidwayFaaSApplication = IMidwayApplication<FaaSContext, {
|
|
12
16
|
getInitializeContext(): any;
|
|
@@ -24,7 +28,8 @@ export declare type IMidwayFaaSApplication = IMidwayApplication<FaaSContext, {
|
|
|
24
28
|
* Get function service name in serverless environment
|
|
25
29
|
*/
|
|
26
30
|
getFunctionServiceName(): string;
|
|
27
|
-
|
|
31
|
+
useEventMiddleware(): void;
|
|
32
|
+
}> & ServerlessHttpApplication;
|
|
28
33
|
export interface Application extends IMidwayFaaSApplication {
|
|
29
34
|
}
|
|
30
35
|
export interface Context extends FaaSContext {
|
|
@@ -37,9 +42,22 @@ export interface IFaaSConfigurationOptions extends IConfigurationOptions {
|
|
|
37
42
|
getApplication(): IMidwayFaaSApplication;
|
|
38
43
|
getFunctionName(): string;
|
|
39
44
|
getFunctionServiceName(): string;
|
|
45
|
+
runAppHook?(app: IMidwayFaaSApplication): void;
|
|
46
|
+
runEventHook?(...args: any[]): any | void;
|
|
47
|
+
runRequestHook?(...args: any[]): any | void;
|
|
40
48
|
};
|
|
41
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* @deprecated
|
|
52
|
+
*/
|
|
42
53
|
export interface IWebMiddleware {
|
|
43
54
|
resolve(): FaaSMiddleware;
|
|
44
55
|
}
|
|
56
|
+
export declare enum TRIGGER_TYPE_ENUM {
|
|
57
|
+
HTTP = "http",
|
|
58
|
+
Event = "event"
|
|
59
|
+
}
|
|
60
|
+
export interface InvokeOptions {
|
|
61
|
+
triggerType: TRIGGER_TYPE_ENUM;
|
|
62
|
+
}
|
|
45
63
|
//# sourceMappingURL=interface.d.ts.map
|
package/dist/interface.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TRIGGER_TYPE_ENUM = void 0;
|
|
4
|
+
var TRIGGER_TYPE_ENUM;
|
|
5
|
+
(function (TRIGGER_TYPE_ENUM) {
|
|
6
|
+
TRIGGER_TYPE_ENUM["HTTP"] = "http";
|
|
7
|
+
TRIGGER_TYPE_ENUM["Event"] = "event";
|
|
8
|
+
})(TRIGGER_TYPE_ENUM = exports.TRIGGER_TYPE_ENUM || (exports.TRIGGER_TYPE_ENUM = {}));
|
|
3
9
|
//# sourceMappingURL=interface.js.map
|
package/package.json
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@midwayjs/faas",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.6-beta.2",
|
|
4
4
|
"main": "dist/index",
|
|
5
5
|
"typings": "index.d.ts",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@midwayjs/core": "^3.3.
|
|
8
|
-
"@midwayjs/faas-typings": "^3.
|
|
7
|
+
"@midwayjs/core": "^3.3.5",
|
|
8
|
+
"@midwayjs/faas-typings": "^3.3.5",
|
|
9
9
|
"@midwayjs/logger": "^2.15.0",
|
|
10
|
-
"@midwayjs/simple-lock": "^1.1.4"
|
|
10
|
+
"@midwayjs/simple-lock": "^1.1.4",
|
|
11
|
+
"@midwayjs/serverless-http-parser": "^3.3.5"
|
|
11
12
|
},
|
|
12
13
|
"devDependencies": {
|
|
13
14
|
"@midwayjs/decorator": "^3.3.4",
|
|
14
|
-
"@midwayjs/mock": "^3.3.
|
|
15
|
-
"@midwayjs/serverless-fc-starter": "^3.3.
|
|
16
|
-
"@midwayjs/serverless-scf-starter": "^3.
|
|
15
|
+
"@midwayjs/mock": "^3.3.5",
|
|
16
|
+
"@midwayjs/serverless-fc-starter": "^3.3.5",
|
|
17
|
+
"@midwayjs/serverless-scf-starter": "^3.3.5",
|
|
17
18
|
"mm": "3.2.0"
|
|
18
19
|
},
|
|
19
20
|
"engines": {
|
|
@@ -45,5 +46,5 @@
|
|
|
45
46
|
"url": "git@github.com:midwayjs/midway.git"
|
|
46
47
|
},
|
|
47
48
|
"license": "MIT",
|
|
48
|
-
"gitHead": "
|
|
49
|
+
"gitHead": "a603d2348d6141f8f723901498f03a162a037708"
|
|
49
50
|
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2013 - Now midwayjs
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|