@90soft/parse-server-kit 2.9.0 → 2.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +183 -0
- package/CLAUDE.md +734 -698
- package/dist/config.d.ts +68 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +35 -0
- package/dist/config.js.map +1 -0
- package/dist/database/indexes.d.ts.map +1 -1
- package/dist/database/indexes.js +14 -1
- package/dist/database/indexes.js.map +1 -1
- package/dist/decorators/cloudDecorator.d.ts.map +1 -1
- package/dist/decorators/cloudDecorator.js +19 -0
- package/dist/decorators/cloudDecorator.js.map +1 -1
- package/dist/decorators/cloudRegistry.d.ts +13 -0
- package/dist/decorators/cloudRegistry.d.ts.map +1 -1
- package/dist/decorators/cloudRegistry.js +23 -1
- package/dist/decorators/cloudRegistry.js.map +1 -1
- package/dist/decorators/parseDecorators.d.ts +1 -1
- package/dist/decorators/parseDecorators.d.ts.map +1 -1
- package/dist/decorators/parseDecorators.js +131 -4
- package/dist/decorators/parseDecorators.js.map +1 -1
- package/dist/decorators/triggerDecorator.d.ts +14 -0
- package/dist/decorators/triggerDecorator.d.ts.map +1 -1
- package/dist/decorators/triggerDecorator.js +31 -2
- package/dist/decorators/triggerDecorator.js.map +1 -1
- package/dist/decorators/triggerRegistry.d.ts +8 -0
- package/dist/decorators/triggerRegistry.d.ts.map +1 -1
- package/dist/decorators/triggerRegistry.js +18 -0
- package/dist/decorators/triggerRegistry.js.map +1 -1
- package/dist/decorators/types/cloudTypes.d.ts +23 -0
- package/dist/decorators/types/cloudTypes.d.ts.map +1 -1
- package/dist/middleware/middleware.d.ts.map +1 -1
- package/dist/middleware/middleware.js +66 -5
- package/dist/middleware/middleware.js.map +1 -1
- package/dist/middleware/rateLimit.d.ts +25 -6
- package/dist/middleware/rateLimit.d.ts.map +1 -1
- package/dist/middleware/rateLimit.js +59 -13
- package/dist/middleware/rateLimit.js.map +1 -1
- package/dist/models/BaseModel.d.ts.map +1 -1
- package/dist/models/BaseModel.js +5 -2
- package/dist/models/BaseModel.js.map +1 -1
- package/dist/utils/dynamicImport.d.ts.map +1 -1
- package/dist/utils/dynamicImport.js +33 -11
- package/dist/utils/dynamicImport.js.map +1 -1
- package/dist/utils/roleCache.d.ts.map +1 -1
- package/dist/utils/roleCache.js +4 -1
- package/dist/utils/roleCache.js.map +1 -1
- package/package.json +8 -7
|
@@ -1,7 +1,24 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AfterEvent = exports.BeforeSubscribe = exports.BeforeConnect = exports.AfterSaveConfig = exports.BeforeSaveConfig = exports.AfterFindFile = exports.BeforeFindFile = exports.AfterDeleteFile = exports.BeforeDeleteFile = exports.AfterSaveFile = exports.BeforeSaveFile = exports.BeforePasswordResetRequest = exports.AfterLogout = exports.AfterLogin = exports.BeforeLogin = exports.AfterFind = exports.BeforeFind = exports.AfterDelete = exports.BeforeDelete = exports.AfterSave = exports.BeforeSave = void 0;
|
|
3
|
+
exports.AfterEvent = exports.BeforeSubscribe = exports.BeforeConnect = exports.AfterSaveConfig = exports.BeforeSaveConfig = exports.AfterFindFile = exports.BeforeFindFile = exports.AfterDeleteFile = exports.BeforeDeleteFile = exports.AfterSaveFile = exports.BeforeSaveFile = exports.BeforePasswordResetRequest = exports.AfterLogout = exports.AfterLogin = exports.BeforeLogin = exports.AfterFind = exports.BeforeFind = exports.AfterDelete = exports.BeforeDelete = exports.AfterSave = exports.BeforeSave = exports.pendingTriggerOwners = void 0;
|
|
4
|
+
exports.markTriggersFlushed = markTriggersFlushed;
|
|
4
5
|
require("reflect-metadata");
|
|
6
|
+
/**
|
|
7
|
+
* Classes that have parked a trigger but not yet had it flushed.
|
|
8
|
+
*
|
|
9
|
+
* A trigger decorator cannot register anything on its own — it does not know
|
|
10
|
+
* the Parse class name, which only `@ParseClass` supplies. So it parks the
|
|
11
|
+
* handler in metadata and waits. On a class that never gets `@ParseClass`, it
|
|
12
|
+
* waits forever: the trigger is simply never registered, and nothing says so.
|
|
13
|
+
*
|
|
14
|
+
* Recording the constructor here lets `TriggerRegistry.initialize()` name the
|
|
15
|
+
* classes still waiting. Exported for that purpose only.
|
|
16
|
+
*/
|
|
17
|
+
exports.pendingTriggerOwners = new Map();
|
|
18
|
+
/** Called by `@ParseClass` once it has flushed a class's pending triggers. */
|
|
19
|
+
function markTriggersFlushed(constructor) {
|
|
20
|
+
exports.pendingTriggerOwners.delete(constructor);
|
|
21
|
+
}
|
|
5
22
|
function createTriggerDecorator(type) {
|
|
6
23
|
return function (config = {}) {
|
|
7
24
|
return function (target, propertyKey, descriptor) {
|
|
@@ -10,7 +27,16 @@ function createTriggerDecorator(type) {
|
|
|
10
27
|
throw new Error(`@${type} decorator can only be applied to methods.`);
|
|
11
28
|
}
|
|
12
29
|
const constructor = typeof target === 'function' ? target : target.constructor;
|
|
13
|
-
|
|
30
|
+
/*
|
|
31
|
+
* Clone-on-first-write — the same trap as `parse:fields`.
|
|
32
|
+
*
|
|
33
|
+
* `getMetadata` walks the prototype chain, so a subclass with no triggers
|
|
34
|
+
* of its own is handed the PARENT's array. Pushing into it registers this
|
|
35
|
+
* subclass's trigger against the parent and every sibling, so a
|
|
36
|
+
* `beforeSave` written for Order also fires for Product.
|
|
37
|
+
*/
|
|
38
|
+
const ownTriggers = Reflect.getOwnMetadata('parse:pendingTriggers', constructor);
|
|
39
|
+
const pendingTriggers = ownTriggers ?? [...(Reflect.getMetadata('parse:pendingTriggers', constructor) || [])];
|
|
14
40
|
pendingTriggers.push({
|
|
15
41
|
type,
|
|
16
42
|
handler,
|
|
@@ -18,6 +44,9 @@ function createTriggerDecorator(type) {
|
|
|
18
44
|
validation: config.validation,
|
|
19
45
|
});
|
|
20
46
|
Reflect.defineMetadata('parse:pendingTriggers', pendingTriggers, constructor);
|
|
47
|
+
const waiting = exports.pendingTriggerOwners.get(constructor) ?? [];
|
|
48
|
+
waiting.push(type);
|
|
49
|
+
exports.pendingTriggerOwners.set(constructor, waiting);
|
|
21
50
|
return descriptor;
|
|
22
51
|
};
|
|
23
52
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"triggerDecorator.js","sourceRoot":"","sources":["../../src/decorators/triggerDecorator.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"triggerDecorator.js","sourceRoot":"","sources":["../../src/decorators/triggerDecorator.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,6 +5,14 @@ export declare class TriggerRegistry {
|
|
|
5
5
|
static register(metadata: TriggerMetadata): void;
|
|
6
6
|
static getTriggers(): TriggerMetadata[];
|
|
7
7
|
static getTriggersForClass(className: string): TriggerMetadata[];
|
|
8
|
+
/**
|
|
9
|
+
* Name any class that declared a trigger but never got `@ParseClass`.
|
|
10
|
+
*
|
|
11
|
+
* Those triggers are parked in metadata waiting for a class name that never
|
|
12
|
+
* arrives, so they are never registered and never fire. Previously the only
|
|
13
|
+
* evidence was that the trigger did not run.
|
|
14
|
+
*/
|
|
15
|
+
private static reportUnflushed;
|
|
8
16
|
static initialize(): void;
|
|
9
17
|
}
|
|
10
18
|
//# sourceMappingURL=triggerRegistry.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"triggerRegistry.d.ts","sourceRoot":"","sources":["../../src/decorators/triggerRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,eAAe,EAAc,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"triggerRegistry.d.ts","sourceRoot":"","sources":["../../src/decorators/triggerRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,eAAe,EAAc,MAAM,sBAAsB,CAAC;AAuBlE,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA2C;IAElE,OAAO,CAAC,MAAM,CAAC,MAAM;IAIrB,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI;IAQhD,MAAM,CAAC,WAAW,IAAI,eAAe,EAAE;IAIvC,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe,EAAE;IAIhE;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,CAAC,eAAe;IAc9B,MAAM,CAAC,UAAU,IAAI,IAAI;CAuD1B"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.TriggerRegistry = void 0;
|
|
4
|
+
const triggerDecorator_1 = require("./triggerDecorator");
|
|
4
5
|
/**
|
|
5
6
|
* Triggers on Parse's own built-in entities.
|
|
6
7
|
*
|
|
@@ -39,7 +40,24 @@ class TriggerRegistry {
|
|
|
39
40
|
static getTriggersForClass(className) {
|
|
40
41
|
return this.getTriggers().filter(t => t.className === className);
|
|
41
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Name any class that declared a trigger but never got `@ParseClass`.
|
|
45
|
+
*
|
|
46
|
+
* Those triggers are parked in metadata waiting for a class name that never
|
|
47
|
+
* arrives, so they are never registered and never fire. Previously the only
|
|
48
|
+
* evidence was that the trigger did not run.
|
|
49
|
+
*/
|
|
50
|
+
static reportUnflushed() {
|
|
51
|
+
if (triggerDecorator_1.pendingTriggerOwners.size === 0)
|
|
52
|
+
return;
|
|
53
|
+
for (const [constructor, types] of triggerDecorator_1.pendingTriggerOwners) {
|
|
54
|
+
console.warn(`[Triggers] ${constructor.name || 'A class'} declares ` +
|
|
55
|
+
`${types.join(', ')} but has no @ParseClass, so ${types.length > 1 ? 'they are' : 'it is'} NOT registered and will never fire. ` +
|
|
56
|
+
'Trigger decorators need @ParseClass to tell them which Parse class they belong to.');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
42
59
|
static initialize() {
|
|
60
|
+
this.reportUnflushed();
|
|
43
61
|
const triggers = this.getTriggers();
|
|
44
62
|
if (triggers.length === 0) {
|
|
45
63
|
console.log('[Triggers] No triggers to register');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"triggerRegistry.js","sourceRoot":"","sources":["../../src/decorators/triggerRegistry.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"triggerRegistry.js","sourceRoot":"","sources":["../../src/decorators/triggerRegistry.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,9 +12,21 @@ export interface SwaggerDocConfig {
|
|
|
12
12
|
export interface RouteConfig {
|
|
13
13
|
methods: HttpMethod[];
|
|
14
14
|
requiresAuth?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Per-function rate limit, keyed on the CLIENT ADDRESS.
|
|
17
|
+
*
|
|
18
|
+
* Enforced per process: with N app instances the effective limit is N x `max`.
|
|
19
|
+
* For cross-instance enforcement use Parse Server's own `rateLimit` server
|
|
20
|
+
* option with `redisUrl`.
|
|
21
|
+
*
|
|
22
|
+
* Requires Express `trust proxy` to be configured precisely (a hop count or
|
|
23
|
+
* the proxy's address) when running behind a reverse proxy. A permissive
|
|
24
|
+
* value lets a caller spoof the key with `X-Forwarded-For`.
|
|
25
|
+
*/
|
|
15
26
|
rateLimit?: {
|
|
16
27
|
windowMs: number;
|
|
17
28
|
max: number;
|
|
29
|
+
keyBy?: (req: any) => string;
|
|
18
30
|
};
|
|
19
31
|
validation?: Parse.Cloud.Validator;
|
|
20
32
|
description?: string;
|
|
@@ -37,7 +49,18 @@ export interface RouteConfig {
|
|
|
37
49
|
export interface CloudFunctionMetadata {
|
|
38
50
|
name: string;
|
|
39
51
|
config: RouteConfig;
|
|
52
|
+
/**
|
|
53
|
+
* The method as it stood when `@CloudFunction` was applied.
|
|
54
|
+
*
|
|
55
|
+
* Kept as a fallback. The registry prefers to re-read the method from its
|
|
56
|
+
* owner at `initialize()` time, so that decorators applied *above*
|
|
57
|
+
* `@CloudFunction` are still in place — see `owner`.
|
|
58
|
+
*/
|
|
40
59
|
handler: (request: Parse.Cloud.FunctionRequest) => Promise<any> | any;
|
|
60
|
+
/** The class the method lives on, so the registry can re-read it later. */
|
|
61
|
+
owner?: any;
|
|
62
|
+
/** The property name on `owner`. */
|
|
63
|
+
propertyKey?: string;
|
|
41
64
|
routePrefix?: string;
|
|
42
65
|
}
|
|
43
66
|
//# sourceMappingURL=cloudTypes.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloudTypes.d.ts","sourceRoot":"","sources":["../../../src/decorators/types/cloudTypes.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAC;AAErE,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC,CAAC;CACnE;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"cloudTypes.d.ts","sourceRoot":"","sources":["../../../src/decorators/types/cloudTypes.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAC;AAErE,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC,CAAC;CACnE;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;;;;;;OAUG;IACH,SAAS,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,MAAM,CAAA;KAAE,CAAC;IAC5E,UAAU,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;;;;;OASG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,OAAO,CAAC,EAAE,gBAAgB,CAAC;CAC5B;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,CAAC;IACpB;;;;;;OAMG;IACH,OAAO,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,eAAe,KAAK,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;IACtE,2EAA2E;IAC3E,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/middleware/middleware.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/middleware/middleware.ts"],"names":[],"mappings":"AACA,OAAO,OAAO,EAAE,EAAC,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAC,MAAM,SAAS,CAAC;AAMjE;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,YAAY,qDAyBnB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,YAAY,qDAgDnB;AAKD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,GAAG,EACR,GAAG,EAAE,QAAQ,EACb,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,cAAc,QAuBzB;AA6DD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,qDAsDzE;AAeD;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,YAAY,QAOnB;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,QAWnE"}
|
|
@@ -9,6 +9,7 @@ exports.extractMasterKey = extractMasterKey;
|
|
|
9
9
|
exports.restrictRoutes = restrictRoutes;
|
|
10
10
|
exports.conditionalJsonMiddleware = conditionalJsonMiddleware;
|
|
11
11
|
exports.removeResultMiddleware = removeResultMiddleware;
|
|
12
|
+
const node_crypto_1 = require("node:crypto");
|
|
12
13
|
const express_1 = __importDefault(require("express"));
|
|
13
14
|
const cloudRegistry_1 = require("../decorators/cloudRegistry");
|
|
14
15
|
const routeDecorator_1 = require("../decorators/routeDecorator");
|
|
@@ -22,7 +23,12 @@ function validateFunctionRoutes(req, res, next) {
|
|
|
22
23
|
if (req._entityRouteHandled) {
|
|
23
24
|
return next();
|
|
24
25
|
}
|
|
25
|
-
|
|
26
|
+
// Tolerate a leading "functions" segment, so this works whether it is
|
|
27
|
+
// mounted at the app root (paths arrive as /functions/myFn) or under
|
|
28
|
+
// {mountPath}/functions (arriving as /myFn). Reading segment [1] blindly
|
|
29
|
+
// meant every lookup 404'd in the first case.
|
|
30
|
+
const segments = req.path.split('/').filter(Boolean);
|
|
31
|
+
const functionName = segments[0] === 'functions' ? segments[1] : segments[0];
|
|
26
32
|
const metadata = cloudRegistry_1.CloudFunctionRegistry.getFunction(functionName);
|
|
27
33
|
if (!metadata) {
|
|
28
34
|
return res.status(404).json({ message: 'Function not found' });
|
|
@@ -116,7 +122,13 @@ function extractMasterKey(req, res, buf, encoding) {
|
|
|
116
122
|
// deliberately so this stays a performance change only. Malformed JSON
|
|
117
123
|
// without a marker now reaches body-parser instead, which answers 400
|
|
118
124
|
// cleanly — so this path is merely reached far less often than before.
|
|
119
|
-
|
|
125
|
+
/*
|
|
126
|
+
* Throw only. This used to also send a 400, which left Express reporting
|
|
127
|
+
* "Cannot set headers after they are sent" on top of the real problem.
|
|
128
|
+
* body-parser catches the throw and answers 400 itself, so the caller gets
|
|
129
|
+
* one clean response and the log shows one cause.
|
|
130
|
+
*/
|
|
131
|
+
throw new Error('Error parsing JSON body');
|
|
120
132
|
}
|
|
121
133
|
}
|
|
122
134
|
/**
|
|
@@ -134,13 +146,33 @@ function extractMasterKey(req, res, buf, encoding) {
|
|
|
134
146
|
* An unset `masterKey` env var never matches, so a misconfigured server fails
|
|
135
147
|
* closed rather than letting every request through as master.
|
|
136
148
|
*/
|
|
149
|
+
/**
|
|
150
|
+
* Compare two secrets without leaking their contents through timing.
|
|
151
|
+
*
|
|
152
|
+
* `===` on strings stops at the first differing character, so how long it takes
|
|
153
|
+
* to fail is a function of how much of the key was right. Given enough samples
|
|
154
|
+
* that is enough to recover a key one character at a time.
|
|
155
|
+
*
|
|
156
|
+
* `timingSafeEqual` needs equal-length buffers, so the length is checked first
|
|
157
|
+
* — that does leak the key's length, which is not worth defending and cannot be
|
|
158
|
+
* avoided here.
|
|
159
|
+
*/
|
|
160
|
+
function secretsMatch(provided, expected) {
|
|
161
|
+
if (typeof provided !== 'string')
|
|
162
|
+
return false;
|
|
163
|
+
const a = Buffer.from(provided, 'utf8');
|
|
164
|
+
const b = Buffer.from(expected, 'utf8');
|
|
165
|
+
if (a.length !== b.length)
|
|
166
|
+
return false;
|
|
167
|
+
return (0, node_crypto_1.timingSafeEqual)(a, b);
|
|
168
|
+
}
|
|
137
169
|
function hasMasterKey(req) {
|
|
138
170
|
const expected = process.env.masterKey;
|
|
139
171
|
if (!expected)
|
|
140
172
|
return false;
|
|
141
|
-
if (req.headers?.['x-parse-master-key']
|
|
173
|
+
if (secretsMatch(req.headers?.['x-parse-master-key'], expected))
|
|
142
174
|
return true;
|
|
143
|
-
if (req['x-master-key']
|
|
175
|
+
if (secretsMatch(req['x-master-key'], expected)) {
|
|
144
176
|
if (!warnedBodyMasterKey) {
|
|
145
177
|
warnedBodyMasterKey = true;
|
|
146
178
|
console.warn('[Security] Master key received in the request body. This is deprecated ' +
|
|
@@ -172,7 +204,36 @@ function restrictRoutes(req, res, next) {
|
|
|
172
204
|
return next();
|
|
173
205
|
}
|
|
174
206
|
// Allow /functions for internal use (rewritten by validateEntityRoutes)
|
|
175
|
-
|
|
207
|
+
/*
|
|
208
|
+
* `/functions/{name}` — where validateEntityRoutes rewrites entity routes to,
|
|
209
|
+
* and also reachable directly.
|
|
210
|
+
*
|
|
211
|
+
* The RATE LIMIT used to be skipped on that direct path, because it is
|
|
212
|
+
* applied in validateEntityRoutes, which returns early for anything that is
|
|
213
|
+
* not a registered entity prefix. So `rateLimit: {max: 2}` could be called
|
|
214
|
+
* any number of times just by asking for `/functions/thatName` instead.
|
|
215
|
+
*
|
|
216
|
+
* The declared `methods` deliberately do NOT follow. `/functions` is Parse's
|
|
217
|
+
* own protocol endpoint, and every SDK's `Parse.Cloud.run` POSTs to it —
|
|
218
|
+
* always. `methods` describes the REST facade in FRONT of that, so enforcing
|
|
219
|
+
* it here would 405 every GET-declared function called through Cloud.run.
|
|
220
|
+
*
|
|
221
|
+
* A request the entity middleware already rewrote carries
|
|
222
|
+
* `_entityRouteHandled` and has been counted once; counting again would
|
|
223
|
+
* charge the limiter twice for one call.
|
|
224
|
+
*
|
|
225
|
+
* Exact segment match, so `/functionsX` is not treated as a function route.
|
|
226
|
+
*/
|
|
227
|
+
if (req.path === '/functions' || req.path.startsWith('/functions/')) {
|
|
228
|
+
if (req._entityRouteHandled)
|
|
229
|
+
return next();
|
|
230
|
+
const fnName = req.path.slice('/functions/'.length).split('/')[0];
|
|
231
|
+
const fnMeta = fnName ? cloudRegistry_1.CloudFunctionRegistry.getFunction(fnName) : undefined;
|
|
232
|
+
if (fnMeta?.config.rateLimit) {
|
|
233
|
+
const allowed = (0, rateLimit_1.checkRateLimit)(req, res, fnName, fnMeta.config.rateLimit);
|
|
234
|
+
if (!allowed)
|
|
235
|
+
return; // 429 already sent
|
|
236
|
+
}
|
|
176
237
|
return next();
|
|
177
238
|
}
|
|
178
239
|
return res.status(403).json({ message: 'Route not allowed' });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/middleware/middleware.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/middleware/middleware.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,29 @@
|
|
|
1
1
|
import { Request, Response } from 'express';
|
|
2
|
-
/**
|
|
3
|
-
|
|
4
|
-
* Returns null if allowed, or an error response object if exceeded.
|
|
5
|
-
*/
|
|
6
|
-
export declare function checkRateLimit(req: Request, res: Response, functionName: string, config: {
|
|
2
|
+
/** Per-function configuration, as declared on `@CloudFunction({rateLimit})`. */
|
|
3
|
+
export interface RateLimitConfig {
|
|
7
4
|
windowMs: number;
|
|
8
5
|
max: number;
|
|
9
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Optional custom bucket key.
|
|
8
|
+
*
|
|
9
|
+
* This middleware runs BEFORE Parse Server validates the session, so there is
|
|
10
|
+
* no authenticated user here. Supply this only if the app can derive a
|
|
11
|
+
* trustworthy identity on its own (e.g. a validated API key). Returning
|
|
12
|
+
* anything the caller controls freely re-opens the bypass described below.
|
|
13
|
+
*/
|
|
14
|
+
keyBy?: (req: Request) => string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Check the rate limit for a function + client.
|
|
18
|
+
*
|
|
19
|
+
* Returns true when the request may proceed. Returns false after sending a 429,
|
|
20
|
+
* in which case the caller must not continue the chain.
|
|
21
|
+
*
|
|
22
|
+
* NOTE — the store is in-memory and therefore PER PROCESS. With N app instances
|
|
23
|
+
* the effective limit is N x `max`. For cross-instance enforcement use Parse
|
|
24
|
+
* Server's own `rateLimit` server option with `redisUrl`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function checkRateLimit(req: Request, res: Response, functionName: string, config: RateLimitConfig): boolean;
|
|
27
|
+
/** Clear all buckets. Intended for tests. */
|
|
28
|
+
export declare function resetRateLimits(): void;
|
|
10
29
|
//# sourceMappingURL=rateLimit.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rateLimit.d.ts","sourceRoot":"","sources":["../../src/middleware/rateLimit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAE,QAAQ,EAAC,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"rateLimit.d.ts","sourceRoot":"","sources":["../../src/middleware/rateLimit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAE,QAAQ,EAAC,MAAM,SAAS,CAAC;AAO1C,gFAAgF;AAChF,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,CAAC;CAClC;AA8DD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,eAAe,GACtB,OAAO,CAwCT;AAED,6CAA6C;AAC7C,wBAAgB,eAAe,IAAI,IAAI,CAEtC"}
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.checkRateLimit = checkRateLimit;
|
|
4
|
+
exports.resetRateLimits = resetRateLimits;
|
|
4
5
|
// Map<functionName, Map<clientKey, RateLimitEntry>>
|
|
5
6
|
const store = new Map();
|
|
7
|
+
/**
|
|
8
|
+
* Hard ceiling on tracked clients per function. A caller rotating addresses
|
|
9
|
+
* would otherwise grow this map without bound — turning the rate limiter into
|
|
10
|
+
* a memory-exhaustion vector. When the ceiling is hit we drop the oldest
|
|
11
|
+
* entries rather than stop limiting.
|
|
12
|
+
*/
|
|
13
|
+
const MAX_CLIENTS_PER_FUNCTION = 10000;
|
|
6
14
|
// Cleanup old entries every 5 minutes
|
|
7
15
|
const cleanupTimer = setInterval(() => {
|
|
8
16
|
const now = Date.now();
|
|
@@ -24,34 +32,68 @@ const cleanupTimer = setInterval(() => {
|
|
|
24
32
|
// which is the only time it has anything to do.
|
|
25
33
|
cleanupTimer.unref();
|
|
26
34
|
/**
|
|
27
|
-
*
|
|
35
|
+
* Resolve the bucket key for a request.
|
|
36
|
+
*
|
|
37
|
+
* SECURITY — why this is the client address and NOT the session token:
|
|
38
|
+
*
|
|
39
|
+
* Until v2.11.0 the key was `req.headers['x-parse-session-token'] || req.ip`.
|
|
40
|
+
* That header is supplied by the caller and is NOT validated at this point in
|
|
41
|
+
* the pipeline, so sending a fresh random token on every request minted a fresh
|
|
42
|
+
* bucket each time and the limit did nothing at all. No account was needed.
|
|
43
|
+
*
|
|
44
|
+
* The address is the only identifier available here that the caller cannot
|
|
45
|
+
* multiply at will.
|
|
46
|
+
*
|
|
47
|
+
* `req.ip` honours Express's `trust proxy` setting. The app MUST configure that
|
|
48
|
+
* precisely — a hop count, or the proxy's address. With a permissive value
|
|
49
|
+
* (`true`) a caller sets `X-Forwarded-For: 127.0.0.1` and spoofs the key, which
|
|
50
|
+
* is the same bypass Parse Server fixed in 9.10.1 for its own limiter. When
|
|
51
|
+
* `trust proxy` is unset, Express already ignores the header and `req.ip` is
|
|
52
|
+
* the socket address, which is what we want.
|
|
28
53
|
*/
|
|
29
|
-
function getClientKey(req) {
|
|
30
|
-
|
|
54
|
+
function getClientKey(req, config) {
|
|
55
|
+
if (config.keyBy) {
|
|
56
|
+
return config.keyBy(req);
|
|
57
|
+
}
|
|
58
|
+
return req.ip || req.socket?.remoteAddress || 'unknown';
|
|
31
59
|
}
|
|
32
60
|
/**
|
|
33
|
-
* Check rate limit for a function + client.
|
|
34
|
-
*
|
|
61
|
+
* Check the rate limit for a function + client.
|
|
62
|
+
*
|
|
63
|
+
* Returns true when the request may proceed. Returns false after sending a 429,
|
|
64
|
+
* in which case the caller must not continue the chain.
|
|
65
|
+
*
|
|
66
|
+
* NOTE — the store is in-memory and therefore PER PROCESS. With N app instances
|
|
67
|
+
* the effective limit is N x `max`. For cross-instance enforcement use Parse
|
|
68
|
+
* Server's own `rateLimit` server option with `redisUrl`.
|
|
35
69
|
*/
|
|
36
70
|
function checkRateLimit(req, res, functionName, config) {
|
|
37
|
-
const clientKey = getClientKey(req);
|
|
71
|
+
const clientKey = getClientKey(req, config);
|
|
38
72
|
const now = Date.now();
|
|
39
|
-
// Get or create function map
|
|
40
73
|
if (!store.has(functionName)) {
|
|
41
74
|
store.set(functionName, new Map());
|
|
42
75
|
}
|
|
43
76
|
const clients = store.get(functionName);
|
|
44
|
-
// Get or create client entry
|
|
45
77
|
const entry = clients.get(clientKey);
|
|
46
78
|
if (!entry || now > entry.resetAt) {
|
|
47
|
-
// First request
|
|
79
|
+
// First request in this window — start fresh.
|
|
80
|
+
if (!entry && clients.size >= MAX_CLIENTS_PER_FUNCTION) {
|
|
81
|
+
// Evict the oldest-expiring entries to make room. Map preserves insertion
|
|
82
|
+
// order, so the head is the least recently added.
|
|
83
|
+
const toEvict = Math.ceil(MAX_CLIENTS_PER_FUNCTION * 0.1);
|
|
84
|
+
let evicted = 0;
|
|
85
|
+
for (const key of clients.keys()) {
|
|
86
|
+
clients.delete(key);
|
|
87
|
+
if (++evicted >= toEvict)
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
48
91
|
clients.set(clientKey, { count: 1, resetAt: now + config.windowMs });
|
|
49
|
-
return true;
|
|
92
|
+
return true;
|
|
50
93
|
}
|
|
51
94
|
if (entry.count < config.max) {
|
|
52
|
-
// Under limit
|
|
53
95
|
entry.count++;
|
|
54
|
-
return true;
|
|
96
|
+
return true;
|
|
55
97
|
}
|
|
56
98
|
// Exceeded — send 429
|
|
57
99
|
const retryAfter = Math.ceil((entry.resetAt - now) / 1000);
|
|
@@ -60,6 +102,10 @@ function checkRateLimit(req, res, functionName, config) {
|
|
|
60
102
|
message: `Too many requests. Try again in ${retryAfter} seconds.`,
|
|
61
103
|
retryAfter,
|
|
62
104
|
});
|
|
63
|
-
return false;
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
/** Clear all buckets. Intended for tests. */
|
|
108
|
+
function resetRateLimits() {
|
|
109
|
+
store.clear();
|
|
64
110
|
}
|
|
65
111
|
//# sourceMappingURL=rateLimit.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rateLimit.js","sourceRoot":"","sources":["../../src/middleware/rateLimit.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"rateLimit.js","sourceRoot":"","sources":["../../src/middleware/rateLimit.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseModel.d.ts","sourceRoot":"","sources":["../../src/models/BaseModel.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,WAAW,EAA8B,KAAK,SAAS,CAAC,EAAE,MAAM,KAAK,KAAK,CAAC,MAAM,CAAC;AAExF,qBAAa,SAAU,SAAQ,WAAW;IACxC;;;;;;OAMG;IACH,SAAS,CAAC,MAAM,CAAC,wBAAwB,EAAE,MAAM,EAAE,CAAmB;IAEtE,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,OAAO,SAAS,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC;IAMhF,MAAM,CAAC,UAAU,CAAC,CAAC,SAAS,OAAO,SAAS,EAC1C,IAAI,EAAE,CAAC,EACP,MAAM,EAAE,GAAG,GACV,YAAY,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"BaseModel.d.ts","sourceRoot":"","sources":["../../src/models/BaseModel.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,WAAW,EAA8B,KAAK,SAAS,CAAC,EAAE,MAAM,KAAK,KAAK,CAAC,MAAM,CAAC;AAExF,qBAAa,SAAU,SAAQ,WAAW;IACxC;;;;;;OAMG;IACH,SAAS,CAAC,MAAM,CAAC,wBAAwB,EAAE,MAAM,EAAE,CAAmB;IAEtE,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,OAAO,SAAS,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC;IAMhF,MAAM,CAAC,UAAU,CAAC,CAAC,SAAS,OAAO,SAAS,EAC1C,IAAI,EAAE,CAAC,EACP,MAAM,EAAE,GAAG,GACV,YAAY,CAAC,CAAC,CAAC;CAkFnB"}
|
package/dist/models/BaseModel.js
CHANGED
|
@@ -9,8 +9,11 @@ class BaseModel extends ParseObject {
|
|
|
9
9
|
return obj;
|
|
10
10
|
}
|
|
11
11
|
static fromParams(params) {
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
// A copy, so this never writes `className` into the caller's `req.params`.
|
|
13
|
+
// Mutating the request object meant a second call — a retried transaction,
|
|
14
|
+
// say — saw an input the first call had already altered.
|
|
15
|
+
const source = { ...params, className: new this().className };
|
|
16
|
+
const raw = Parse.Object.fromJSON(source, true);
|
|
14
17
|
Object.setPrototypeOf(raw, this.prototype);
|
|
15
18
|
const obj = raw;
|
|
16
19
|
if (params.id)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseModel.js","sourceRoot":"","sources":["../../src/models/BaseModel.ts"],"names":[],"mappings":";;;AAAA,MAAM,WAAW,GAAG,KAAK,CAAC,MAA6D,CAAC;AAExF,MAAa,SAAU,SAAQ,WAAW;IAUxC,MAAM,CAAC,OAAO,CAAsC,EAAU;QAC5D,MAAM,GAAG,GAAG,IAAI,IAAI,EAAqB,CAAC;QAC1C,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC;QACZ,OAAO,GAAG,CAAC;IACb,CAAC;IAED,MAAM,CAAC,UAAU,CAEf,MAAW;QAEX,MAAM,
|
|
1
|
+
{"version":3,"file":"BaseModel.js","sourceRoot":"","sources":["../../src/models/BaseModel.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dynamicImport.d.ts","sourceRoot":"","sources":["../../src/utils/dynamicImport.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;;;;OAWG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;GAGG;AACH,eAAO,MAAM,WAAW,GACtB,eAAe,MAAM,EACrB,UAAS,kBAAuB,
|
|
1
|
+
{"version":3,"file":"dynamicImport.d.ts","sourceRoot":"","sources":["../../src/utils/dynamicImport.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;;;;OAWG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;GAGG;AACH,eAAO,MAAM,WAAW,GACtB,eAAe,MAAM,EACrB,UAAS,kBAAuB,SA8CjC,CAAC"}
|
|
@@ -13,18 +13,40 @@ const importFiles = (directoryPath, options = {}) => {
|
|
|
13
13
|
// `.d.ts` is a declaration, never a module to execute — importing one under
|
|
14
14
|
// ts-node is at best a no-op and at worst a crash.
|
|
15
15
|
const wanted = new Set(extensions.map(e => (e.startsWith('.') ? e : `.${e}`)));
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
const counts = { imported: 0, passedOver: new Set() };
|
|
17
|
+
const walk = (dir) => {
|
|
18
|
+
for (const file of (0, fs_1.readdirSync)(dir)) {
|
|
19
|
+
const filePath = (0, path_1.join)(dir, file);
|
|
20
|
+
if (isDirectory(filePath)) {
|
|
21
|
+
walk(filePath);
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
const ext = (0, path_1.extname)(filePath);
|
|
25
|
+
if (wanted.has(ext) && !file.endsWith('.d.ts')) {
|
|
26
|
+
if (verbose)
|
|
27
|
+
console.log(filePath);
|
|
28
|
+
require(filePath);
|
|
29
|
+
counts.imported += 1;
|
|
30
|
+
}
|
|
31
|
+
else if (ext && !file.endsWith('.d.ts')) {
|
|
32
|
+
counts.passedOver.add(ext);
|
|
33
|
+
}
|
|
21
34
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
35
|
+
};
|
|
36
|
+
walk(directoryPath);
|
|
37
|
+
// Importing nothing is the single easiest way to end up with a server that
|
|
38
|
+
// starts perfectly and has no models, no schema and no routes. It looks like
|
|
39
|
+
// the library did nothing, so say what actually happened.
|
|
40
|
+
if (counts.imported === 0 && counts.passedOver.size > 0) {
|
|
41
|
+
const found = [...counts.passedOver].sort().join(', ');
|
|
42
|
+
const asked = [...wanted].sort().join(', ');
|
|
43
|
+
console.warn(`[importFiles] Imported 0 files from ${directoryPath}. ` +
|
|
44
|
+
`Looking for ${asked}, but the only files there are ${found}. ` +
|
|
45
|
+
(counts.passedOver.has('.ts')
|
|
46
|
+
? "Running from TypeScript sources? Pass {extensions: ['.js', '.ts']}."
|
|
47
|
+
: 'Check the path and the `extensions` option.') +
|
|
48
|
+
' Nothing has been registered: no models, no cloud functions, no routes.');
|
|
49
|
+
}
|
|
28
50
|
};
|
|
29
51
|
exports.importFiles = importFiles;
|
|
30
52
|
//# sourceMappingURL=dynamicImport.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dynamicImport.js","sourceRoot":"","sources":["../../src/utils/dynamicImport.ts"],"names":[],"mappings":";;;AAAA,2BAAyC;AACzC,+BAAmC;AAEnC,MAAM,WAAW,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAA,aAAQ,EAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;AAoBnE;;;GAGG;AACI,MAAM,WAAW,GAAG,CACzB,aAAqB,EACrB,UAA8B,EAAE,EAChC,EAAE;IACF,MAAM,EAAC,UAAU,GAAG,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,IAAI,EAAC,GAAG,OAAO,CAAC;IAEvD,4EAA4E;IAC5E,mDAAmD;IACnD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAE/E,MAAM,
|
|
1
|
+
{"version":3,"file":"dynamicImport.js","sourceRoot":"","sources":["../../src/utils/dynamicImport.ts"],"names":[],"mappings":";;;AAAA,2BAAyC;AACzC,+BAAmC;AAEnC,MAAM,WAAW,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAA,aAAQ,EAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;AAoBnE;;;GAGG;AACI,MAAM,WAAW,GAAG,CACzB,aAAqB,EACrB,UAA8B,EAAE,EAChC,EAAE;IACF,MAAM,EAAC,UAAU,GAAG,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,IAAI,EAAC,GAAG,OAAO,CAAC;IAEvD,4EAA4E;IAC5E,mDAAmD;IACnD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAE/E,MAAM,MAAM,GAAG,EAAC,QAAQ,EAAE,CAAC,EAAE,UAAU,EAAE,IAAI,GAAG,EAAU,EAAC,CAAC;IAE5D,MAAM,IAAI,GAAG,CAAC,GAAW,EAAQ,EAAE;QACjC,KAAK,MAAM,IAAI,IAAI,IAAA,gBAAW,EAAC,GAAG,CAAC,EAAE,CAAC;YACpC,MAAM,QAAQ,GAAG,IAAA,WAAI,EAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YACjC,IAAI,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC1B,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACf,SAAS;YACX,CAAC;YAED,MAAM,GAAG,GAAG,IAAA,cAAO,EAAC,QAAQ,CAAC,CAAC;YAC9B,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC/C,IAAI,OAAO;oBAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;gBACnC,OAAO,CAAC,QAAQ,CAAC,CAAC;gBAClB,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC;YACvB,CAAC;iBAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC1C,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC7B,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IAEF,IAAI,CAAC,aAAa,CAAC,CAAC;IAEpB,2EAA2E;IAC3E,6EAA6E;IAC7E,0DAA0D;IAC1D,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;QACxD,MAAM,KAAK,GAAG,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvD,MAAM,KAAK,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5C,OAAO,CAAC,IAAI,CACV,uCAAuC,aAAa,IAAI;YACtD,eAAe,KAAK,kCAAkC,KAAK,IAAI;YAC/D,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC;gBAC3B,CAAC,CAAC,qEAAqE;gBACvE,CAAC,CAAC,6CAA6C,CAAC;YAClD,yEAAyE,CAC5E,CAAC;IACJ,CAAC;AACH,CAAC,CAAC;AAhDW,QAAA,WAAW,eAgDtB"}
|