alp-node 6.2.0 → 7.0.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 +14 -0
- package/dist/AlpNodeApp-node18.mjs +303 -12
- package/dist/AlpNodeApp-node18.mjs.map +1 -1
- package/dist/definitions/AlpNodeApp.d.ts +15 -7
- package/dist/definitions/AlpNodeApp.d.ts.map +1 -1
- package/dist/definitions/config.d.ts +18 -0
- package/dist/definitions/config.d.ts.map +1 -0
- package/dist/definitions/errors.d.ts +4 -0
- package/dist/definitions/errors.d.ts.map +1 -0
- package/dist/definitions/index.d.ts +5 -3
- package/dist/definitions/index.d.ts.map +1 -1
- package/dist/definitions/language.d.ts +7 -0
- package/dist/definitions/language.d.ts.map +1 -0
- package/dist/definitions/listen.d.ts +7 -0
- package/dist/definitions/listen.d.ts.map +1 -0
- package/dist/definitions/params/ParamValid.d.ts +9 -0
- package/dist/definitions/params/ParamValid.d.ts.map +1 -0
- package/dist/definitions/params/ParamValidationResult.d.ts +9 -0
- package/dist/definitions/params/ParamValidationResult.d.ts.map +1 -0
- package/dist/definitions/params/ParamValidationResult.test.d.ts +2 -0
- package/dist/definitions/params/ParamValidationResult.test.d.ts.map +1 -0
- package/dist/definitions/params/ParamValueFromContext.d.ts +13 -0
- package/dist/definitions/params/ParamValueFromContext.d.ts.map +1 -0
- package/dist/definitions/params/ParamValueModelValidator.d.ts +4 -0
- package/dist/definitions/params/ParamValueModelValidator.d.ts.map +1 -0
- package/dist/definitions/params/ParamValueStringValidator.d.ts +5 -0
- package/dist/definitions/params/ParamValueStringValidator.d.ts.map +1 -0
- package/dist/definitions/params/ParamValueValidator.d.ts +10 -0
- package/dist/definitions/params/ParamValueValidator.d.ts.map +1 -0
- package/dist/definitions/params/index.d.ts +15 -0
- package/dist/definitions/params/index.d.ts.map +1 -0
- package/dist/definitions/router.d.ts +14 -0
- package/dist/definitions/router.d.ts.map +1 -0
- package/dist/definitions/translate/index.d.ts +11 -0
- package/dist/definitions/translate/index.d.ts.map +1 -0
- package/dist/definitions/translate/load.d.ts +4 -0
- package/dist/definitions/translate/load.d.ts.map +1 -0
- package/dist/definitions/types.d.ts +53 -0
- package/dist/definitions/types.d.ts.map +1 -0
- package/dist/index-node18.mjs +402 -15
- package/dist/index-node18.mjs.map +1 -1
- package/package.json +8 -9
- package/src/AlpNodeApp.ts +42 -21
- package/src/config.ts +121 -0
- package/src/errors.ts +70 -0
- package/src/index.ts +18 -3
- package/src/language.ts +27 -0
- package/src/listen.ts +68 -0
- package/src/params/ParamValid.ts +15 -0
- package/src/params/ParamValidationResult.test.ts +65 -0
- package/src/params/ParamValidationResult.ts +38 -0
- package/src/params/ParamValueFromContext.ts +42 -0
- package/src/params/ParamValueModelValidator.ts +36 -0
- package/src/params/ParamValueStringValidator.ts +13 -0
- package/src/params/ParamValueValidator.ts +23 -0
- package/src/params/index.ts +66 -0
- package/src/router.ts +64 -0
- package/src/translate/index.ts +45 -0
- package/src/translate/load.ts +30 -0
- package/src/types.ts +67 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ParamValidationResult.d.ts","sourceRoot":"","sources":["../../../src/params/ParamValidationResult.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAEzC,qBAAa,qBAAqB;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAQvD,SAAS,IAAI,MAAM,GAAG,SAAS;IAI/B,SAAS,IAAI,OAAO;IAIpB,OAAO,IAAI,OAAO;CAgBnB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ParamValidationResult.test.d.ts","sourceRoot":"","sources":["../../../src/params/ParamValidationResult.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/// <reference types="koa" />
|
|
2
|
+
import type { Context } from '../AlpNodeApp';
|
|
3
|
+
import type { ParamValidationResult } from './ParamValidationResult';
|
|
4
|
+
import ParamValueStringValidator from './ParamValueStringValidator';
|
|
5
|
+
export declare class ParamValueFromContext {
|
|
6
|
+
readonly validationResult: ParamValidationResult;
|
|
7
|
+
readonly context: Context;
|
|
8
|
+
constructor(context: Context, validationResult: ParamValidationResult);
|
|
9
|
+
namedParam(name: string): ParamValueStringValidator;
|
|
10
|
+
otherParam(position: number): ParamValueStringValidator;
|
|
11
|
+
queryParam(name: string): ParamValueStringValidator;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=ParamValueFromContext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ParamValueFromContext.d.ts","sourceRoot":"","sources":["../../../src/params/ParamValueFromContext.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,yBAAyB,MAAM,6BAA6B,CAAC;AAEpE,qBAAa,qBAAqB;IAChC,QAAQ,CAAC,gBAAgB,EAAE,qBAAqB,CAAC;IAEjD,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;gBAEd,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,qBAAqB;IAKrE,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,yBAAyB;IAQnD,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,yBAAyB;IAQvD,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,yBAAyB;CAWpD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ParamValueModelValidator.d.ts","sourceRoot":"","sources":["../../../src/params/ParamValueModelValidator.ts"],"names":[],"mappings":"AAAA,OAAO,mBAAmB,MAAM,uBAAuB,CAAC;AAExD,MAAM,CAAC,OAAO,OAAO,wBAAwB,CAC3C,CAAC,CACD,SAAQ,mBAAmB,CAAC,CAAC,CAAC;CA+B/B"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import ParamValueValidator from './ParamValueValidator';
|
|
2
|
+
export default class ParamValueStringValidator<T extends string = string> extends ParamValueValidator<T | null | undefined> {
|
|
3
|
+
notEmpty(): ParamValueValidator<T>;
|
|
4
|
+
}
|
|
5
|
+
//# sourceMappingURL=ParamValueStringValidator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ParamValueStringValidator.d.ts","sourceRoot":"","sources":["../../../src/params/ParamValueStringValidator.ts"],"names":[],"mappings":"AAAA,OAAO,mBAAmB,MAAM,uBAAuB,CAAC;AAExD,MAAM,CAAC,OAAO,OAAO,yBAAyB,CAC5C,CAAC,SAAS,MAAM,GAAG,MAAM,CACzB,SAAQ,mBAAmB,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IACjD,QAAQ,IAAI,mBAAmB,CAAC,CAAC,CAAC;CAOnC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ParamValidationResult } from './ParamValidationResult';
|
|
2
|
+
export default class ParamValueValidator<T> {
|
|
3
|
+
readonly validationResult: ParamValidationResult;
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly value: T;
|
|
6
|
+
constructor(validationResult: ParamValidationResult, name: string, value: T);
|
|
7
|
+
isValid(): boolean;
|
|
8
|
+
_error(key: string): void;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=ParamValueValidator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ParamValueValidator.d.ts","sourceRoot":"","sources":["../../../src/params/ParamValueValidator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAErE,MAAM,CAAC,OAAO,OAAO,mBAAmB,CAAC,CAAC;IACxC,QAAQ,CAAC,gBAAgB,EAAE,qBAAqB,CAAC;IAEjD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;gBAEN,gBAAgB,EAAE,qBAAqB,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAM3E,OAAO,IAAI,OAAO;IAIlB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;CAG1B"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AlpNodeApp } from '../AlpNodeApp';
|
|
2
|
+
import { ParamValueFromContext } from './ParamValueFromContext';
|
|
3
|
+
export interface AlpParamsContext {
|
|
4
|
+
params: ParamValueFromContext;
|
|
5
|
+
validParams: ParamValueFromContext;
|
|
6
|
+
namedParam: (name: string) => string | undefined;
|
|
7
|
+
otherParam: (position: number) => string | undefined;
|
|
8
|
+
queryParam: (name: string) => string | undefined;
|
|
9
|
+
bodyParam: <T>(name: string) => T | undefined;
|
|
10
|
+
}
|
|
11
|
+
export interface AlpParamsRequest {
|
|
12
|
+
searchParams: URLSearchParams;
|
|
13
|
+
}
|
|
14
|
+
export default function alpParams(app: AlpNodeApp): void;
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/params/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAW,MAAM,eAAe,CAAC;AAGzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,qBAAqB,CAAC;IAC9B,WAAW,EAAE,qBAAqB,CAAC;IACnC,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACjD,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACrD,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACjD,SAAS,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC,GAAG,SAAS,CAAC;CAC/C;AAED,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,eAAe,CAAC;CAC/B;AAED,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI,CA8CvD"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/// <reference types="koa" />
|
|
2
|
+
import type { Router, LocaleType, RouterBuilder, RouteMatch } from 'router-segments';
|
|
3
|
+
import type { AlpNodeApp, Context } from './AlpNodeApp';
|
|
4
|
+
export type AlpRouter<Locales extends LocaleType> = Router<Locales, AlpRouteRef>;
|
|
5
|
+
export type AlpRouteRef = (ctx: Context) => Promise<void> | void;
|
|
6
|
+
type ReturnType = (app: AlpNodeApp) => AlpRouteRef;
|
|
7
|
+
export interface RouterContext {
|
|
8
|
+
route: RouteMatch<any, AlpRouteRef>;
|
|
9
|
+
}
|
|
10
|
+
export declare const createAlpRouterBuilder: <Locales extends string>() => RouterBuilder<Locales, AlpRouteRef>;
|
|
11
|
+
export type UrlGenerator = <P extends Record<string, unknown> | undefined>(routeKey: string, params?: P) => string;
|
|
12
|
+
export default function alpRouter<Locales extends string>(router: Router<Locales, AlpRouteRef>): ReturnType;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=router.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/router.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EACV,MAAM,EACN,UAAU,EACV,aAAa,EACb,UAAU,EACX,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAExD,MAAM,MAAM,SAAS,CAAC,OAAO,SAAS,UAAU,IAAI,MAAM,CACxD,OAAO,EACP,WAAW,CACZ,CAAC;AACF,MAAM,MAAM,WAAW,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AACjE,KAAK,UAAU,GAAG,CAAC,GAAG,EAAE,UAAU,KAAK,WAAW,CAAC;AAEnD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,UAAU,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;CACrC;AACD,eAAO,MAAM,sBAAsB,mEAGU,CAAC;AAE9C,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EACvE,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,CAAC,KACP,MAAM,CAAC;AAEZ,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,OAAO,SAAS,MAAM,EACtD,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,WAAW,CAAC,GACnC,UAAU,CAgCZ"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AlpNodeApp } from '../AlpNodeApp';
|
|
2
|
+
type Args = Record<string, any>;
|
|
3
|
+
export interface TranslateBaseContext {
|
|
4
|
+
t: (id: string, args: Args) => string;
|
|
5
|
+
}
|
|
6
|
+
export interface TranslateContext {
|
|
7
|
+
readonly language: string;
|
|
8
|
+
}
|
|
9
|
+
export default function alpTranslate(dirname: string): (app: AlpNodeApp) => void;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/translate/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAW,MAAM,eAAe,CAAC;AAMzD,KAAK,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAEhC,MAAM,WAAW,oBAAoB;IACnC,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC;CACvC;AACD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,CAAC,OAAO,UAAU,YAAY,CAClC,OAAO,EAAE,MAAM,GACd,CAAC,GAAG,EAAE,UAAU,KAAK,IAAI,CA0B3B"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import IntlMessageFormatDefault from 'intl-messageformat';
|
|
2
|
+
export type Translations = ReadonlyMap<string, IntlMessageFormatDefault>;
|
|
3
|
+
export default function load(translations: ReadonlyMap<string, unknown>, language: string): Translations;
|
|
4
|
+
//# sourceMappingURL=load.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load.d.ts","sourceRoot":"","sources":["../../../src/translate/load.ts"],"names":[],"mappings":"AAAA,OAAO,wBAAwB,MAAM,oBAAoB,CAAC;AAO1D,MAAM,MAAM,YAAY,GAAG,WAAW,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC;AAEzE,MAAM,CAAC,OAAO,UAAU,IAAI,CAC1B,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1C,QAAQ,EAAE,MAAM,GACf,YAAY,CAiBd"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
type RawConfig = ReadonlyMap<string, any>;
|
|
2
|
+
export interface Config {
|
|
3
|
+
get: <T>(key: string) => T;
|
|
4
|
+
}
|
|
5
|
+
export type PackageConfig = Record<string, any>;
|
|
6
|
+
export interface NodeConfig extends Config {
|
|
7
|
+
loadConfigSync: (name: string) => RawConfig;
|
|
8
|
+
readonly packageConfig: PackageConfig;
|
|
9
|
+
}
|
|
10
|
+
export interface ContextState {
|
|
11
|
+
}
|
|
12
|
+
export interface ContextSanitizedState {
|
|
13
|
+
}
|
|
14
|
+
export interface BaseContext {
|
|
15
|
+
config: Config;
|
|
16
|
+
}
|
|
17
|
+
export interface Context extends BaseContext {
|
|
18
|
+
state: ContextState;
|
|
19
|
+
sanitizedState: ContextSanitizedState;
|
|
20
|
+
status: number;
|
|
21
|
+
response: any;
|
|
22
|
+
redirect: (url: string) => Promise<void>;
|
|
23
|
+
[key: string]: any;
|
|
24
|
+
}
|
|
25
|
+
export interface ApplicationInCreation {
|
|
26
|
+
config?: Config;
|
|
27
|
+
context: BaseContext;
|
|
28
|
+
}
|
|
29
|
+
export interface Application extends ApplicationInCreation {
|
|
30
|
+
config: Config;
|
|
31
|
+
}
|
|
32
|
+
export interface NodeApplicationInCreation extends ApplicationInCreation {
|
|
33
|
+
loadConfigSync: (name: string) => RawConfig;
|
|
34
|
+
}
|
|
35
|
+
export interface BrowserApplicationInCreation extends ApplicationInCreation {
|
|
36
|
+
appVersion: string;
|
|
37
|
+
existsConfig: (name: string) => Promise<boolean> | boolean;
|
|
38
|
+
loadConfig: (name: string) => Promise<RawConfig>;
|
|
39
|
+
createContext: () => Context;
|
|
40
|
+
}
|
|
41
|
+
export interface NodeApplication extends Application, NodeApplicationInCreation {
|
|
42
|
+
config: NodeConfig;
|
|
43
|
+
dirname: string;
|
|
44
|
+
on: (event: 'close', callback: () => void) => void;
|
|
45
|
+
existsConfigSync: (name: string) => boolean;
|
|
46
|
+
loadConfigSync: (name: string) => RawConfig;
|
|
47
|
+
}
|
|
48
|
+
export interface HtmlError extends Error {
|
|
49
|
+
status: number;
|
|
50
|
+
expose?: true;
|
|
51
|
+
}
|
|
52
|
+
export {};
|
|
53
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,KAAK,SAAS,GAAG,WAAW,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAE1C,MAAM,WAAW,MAAM;IACrB,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC,CAAC;CAC5B;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAEhD,MAAM,WAAW,UAAW,SAAQ,MAAM;IACxC,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,SAAS,CAAC;IAC5C,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;CACvC;AAGD,MAAM,WAAW,YAAY;CAAG;AAGhC,MAAM,WAAW,qBAAqB;CAAG;AAEzC,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,OAAQ,SAAQ,WAAW;IAC1C,KAAK,EAAE,YAAY,CAAC;IACpB,cAAc,EAAE,qBAAqB,CAAC;IACtC,MAAM,EAAE,MAAM,CAAC;IAEf,QAAQ,EAAE,GAAG,CAAC;IACd,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,WAAY,SAAQ,qBAAqB;IACxD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,yBAA0B,SAAQ,qBAAqB;IACtE,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,SAAS,CAAC;CAC7C;AAED,MAAM,WAAW,4BAA6B,SAAQ,qBAAqB;IACzE,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;IAC3D,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IACjD,aAAa,EAAE,MAAM,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,eACf,SAAQ,WAAW,EACjB,yBAAyB;IAC3B,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;IACnD,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAC5C,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,SAAS,CAAC;CAC7C;AAED,MAAM,WAAW,SAAU,SAAQ,KAAK;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,IAAI,CAAC;CACf"}
|
package/dist/index-node18.mjs
CHANGED
|
@@ -1,17 +1,381 @@
|
|
|
1
|
-
import { readFileSync,
|
|
1
|
+
import { existsSync, readFileSync, unlinkSync, chmodSync } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import _config, { Config } from 'alp-node-config';
|
|
4
|
-
export { Config } from 'alp-node-config';
|
|
5
3
|
import { Logger } from 'nightingale-logger';
|
|
6
4
|
import { deprecate } from 'node:util';
|
|
7
|
-
import _listen from 'alp-listen';
|
|
8
|
-
import errors from 'alp-node-errors';
|
|
9
|
-
import language from 'alp-node-language';
|
|
10
|
-
import params from 'alp-params';
|
|
11
|
-
import translate from 'alp-translate';
|
|
12
5
|
import Koa from 'koa';
|
|
13
6
|
import compress from 'koa-compress';
|
|
14
7
|
import serve from 'koa-static';
|
|
8
|
+
import deepFreeze from 'deep-freeze-es6';
|
|
9
|
+
import minimist from 'minimist';
|
|
10
|
+
import parseJSON from 'parse-json-object-as-map';
|
|
11
|
+
import { STATUS_CODES, createServer as createServer$2 } from 'node:http';
|
|
12
|
+
import ErrorHtmlRenderer from 'error-html';
|
|
13
|
+
import { defineLazyProperty } from 'object-properties';
|
|
14
|
+
import { createServer as createServer$1 } from 'node:https';
|
|
15
|
+
import IntlMessageFormatDefault from 'intl-messageformat';
|
|
16
|
+
import { createRouterBuilder } from 'router-segments';
|
|
17
|
+
|
|
18
|
+
const argv = minimist(process.argv.slice(2));
|
|
19
|
+
function _existsConfigSync(dirname, name) {
|
|
20
|
+
return existsSync(`${dirname}${name}.json`);
|
|
21
|
+
}
|
|
22
|
+
function _loadConfigSync(dirname, name) {
|
|
23
|
+
const content = readFileSync(`${dirname}${name}.json`, 'utf8');
|
|
24
|
+
return parseJSON(content);
|
|
25
|
+
}
|
|
26
|
+
class Config {
|
|
27
|
+
constructor(dirname, options) {
|
|
28
|
+
this._map = new Map();
|
|
29
|
+
this._dirname = dirname.replace(/\/*$/, '/');
|
|
30
|
+
if (options) {
|
|
31
|
+
this.loadSync(options);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
loadSync(options = {}) {
|
|
35
|
+
const env = process.env.CONFIG_ENV || process.env.NODE_ENV || 'development';
|
|
36
|
+
const {
|
|
37
|
+
argv: argvOverrides = [],
|
|
38
|
+
packageConfig,
|
|
39
|
+
version
|
|
40
|
+
} = options;
|
|
41
|
+
this.packageConfig = packageConfig;
|
|
42
|
+
const config = this.loadConfigSync('common');
|
|
43
|
+
for (const [key, value] of this.loadConfigSync(env)) {
|
|
44
|
+
config.set(key, value);
|
|
45
|
+
}
|
|
46
|
+
if (this.existsConfigSync('local')) {
|
|
47
|
+
for (const [key, value] of this.loadConfigSync('local')) {
|
|
48
|
+
config.set(key, value);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (config.has('version')) {
|
|
52
|
+
throw new Error('Cannot have "version", in config.');
|
|
53
|
+
}
|
|
54
|
+
config.set('version', String(version || argv.version || packageConfig?.version));
|
|
55
|
+
const socketPath = argv.socket || argv['socket-path'] || argv.socketPath;
|
|
56
|
+
if (socketPath) {
|
|
57
|
+
config.set('socketPath', socketPath);
|
|
58
|
+
} else if (argv.port) {
|
|
59
|
+
config.set('port', argv.port);
|
|
60
|
+
config.delete('socketPath');
|
|
61
|
+
} else if (process.env.PORT) {
|
|
62
|
+
config.set('port', Number(process.env.PORT));
|
|
63
|
+
config.delete('socketPath');
|
|
64
|
+
}
|
|
65
|
+
argvOverrides.forEach(key => {
|
|
66
|
+
const splitted = key.split('.');
|
|
67
|
+
const value = splitted.length > 0 &&
|
|
68
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return,unicorn/no-array-reduce, @typescript-eslint/no-shadow
|
|
69
|
+
splitted.reduce((config, partialKey) => config?.[partialKey], argv);
|
|
70
|
+
if (value !== undefined) {
|
|
71
|
+
const last = splitted.pop();
|
|
72
|
+
const map = splitted.length === 0 ? config :
|
|
73
|
+
// eslint-disable-next-line unicorn/no-array-reduce
|
|
74
|
+
splitted.reduce(
|
|
75
|
+
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
76
|
+
(config, partialKey) => config.get(partialKey), config);
|
|
77
|
+
map.set(last, value);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
this._map = deepFreeze(config);
|
|
81
|
+
return this;
|
|
82
|
+
}
|
|
83
|
+
get(key) {
|
|
84
|
+
return this._map.get(key);
|
|
85
|
+
}
|
|
86
|
+
existsConfigSync(name) {
|
|
87
|
+
return _existsConfigSync(this._dirname, name);
|
|
88
|
+
}
|
|
89
|
+
loadConfigSync(name) {
|
|
90
|
+
return _loadConfigSync(this._dirname, name);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function getConfig(app, config) {
|
|
94
|
+
return config;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* eslint-disable complexity */
|
|
98
|
+
const logger$4 = new Logger('alp:errors');
|
|
99
|
+
const errorHtmlRenderer = new ErrorHtmlRenderer({
|
|
100
|
+
appPath: `${process.cwd()}/`
|
|
101
|
+
});
|
|
102
|
+
async function alpNodeErrors(ctx, next) {
|
|
103
|
+
try {
|
|
104
|
+
await next();
|
|
105
|
+
} catch (error) {
|
|
106
|
+
// eslint-disable-next-line no-ex-assign
|
|
107
|
+
if (!error) error = new Error('Unknown error');
|
|
108
|
+
// eslint-disable-next-line no-ex-assign
|
|
109
|
+
if (typeof error === 'string') error = new Error(error);
|
|
110
|
+
ctx.status = error.status || 500;
|
|
111
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
112
|
+
logger$4.error(error);
|
|
113
|
+
switch (ctx.request.accepts('html', 'text', 'json')) {
|
|
114
|
+
case 'json':
|
|
115
|
+
ctx.type = 'application/json';
|
|
116
|
+
if (process.env.NODE_ENV !== 'production' || error.expose) {
|
|
117
|
+
ctx.body = {
|
|
118
|
+
error: error.message
|
|
119
|
+
};
|
|
120
|
+
} else {
|
|
121
|
+
ctx.body = {
|
|
122
|
+
error: STATUS_CODES[ctx.status]
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
break;
|
|
126
|
+
case 'html':
|
|
127
|
+
ctx.type = 'text/html';
|
|
128
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
129
|
+
ctx.body = errorHtmlRenderer.render(error);
|
|
130
|
+
} else if (error.expose) {
|
|
131
|
+
ctx.body = error.message;
|
|
132
|
+
} else {
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
135
|
+
break;
|
|
136
|
+
case 'text':
|
|
137
|
+
default:
|
|
138
|
+
ctx.type = 'text/plain';
|
|
139
|
+
if (process.env.NODE_ENV !== 'production' || error.expose) {
|
|
140
|
+
ctx.body = error.message;
|
|
141
|
+
} else {
|
|
142
|
+
throw error;
|
|
143
|
+
}
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function alpLanguage(app) {
|
|
150
|
+
const config = app.context.config;
|
|
151
|
+
const availableLanguages = config.get('availableLanguages');
|
|
152
|
+
if (!availableLanguages) {
|
|
153
|
+
throw new Error('Missing config "availableLanguages"');
|
|
154
|
+
}
|
|
155
|
+
defineLazyProperty(app.context, 'language', function () {
|
|
156
|
+
return this.acceptsLanguages(availableLanguages) || availableLanguages[0];
|
|
157
|
+
});
|
|
158
|
+
defineLazyProperty(app.context, 'firstAcceptedLanguage', function () {
|
|
159
|
+
return this.acceptsLanguages()[0] || availableLanguages[0];
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const logger$3 = new Logger('alp:listen');
|
|
164
|
+
const createServer = (callback, socketPath, tls, dirname = ''
|
|
165
|
+
// eslint-disable-next-line @typescript-eslint/max-params
|
|
166
|
+
) => {
|
|
167
|
+
const createHttpServer = !socketPath && tls ? createServer$1 : createServer$2;
|
|
168
|
+
if (!tls) {
|
|
169
|
+
return createHttpServer(callback);
|
|
170
|
+
}
|
|
171
|
+
const options = {
|
|
172
|
+
key: readFileSync(`${dirname}/server.key`),
|
|
173
|
+
cert: readFileSync(`${dirname}/server.crt`)
|
|
174
|
+
};
|
|
175
|
+
return createHttpServer(options, callback);
|
|
176
|
+
};
|
|
177
|
+
function alpListen(config, callback, dirname) {
|
|
178
|
+
return new Promise(resolve => {
|
|
179
|
+
const socketPath = config.get('socketPath');
|
|
180
|
+
const port = config.get('port');
|
|
181
|
+
const hostname = config.get('hostname');
|
|
182
|
+
const tls = config.get('tls');
|
|
183
|
+
logger$3.info('Creating server', socketPath ? {
|
|
184
|
+
socketPath
|
|
185
|
+
} : {
|
|
186
|
+
port
|
|
187
|
+
});
|
|
188
|
+
const server = createServer(callback, socketPath, tls, dirname);
|
|
189
|
+
if (socketPath) {
|
|
190
|
+
try {
|
|
191
|
+
unlinkSync(socketPath);
|
|
192
|
+
} catch {}
|
|
193
|
+
server.listen(socketPath, () => {
|
|
194
|
+
if (socketPath) {
|
|
195
|
+
chmodSync(socketPath, '777');
|
|
196
|
+
}
|
|
197
|
+
logger$3.info('Server listening', {
|
|
198
|
+
socketPath
|
|
199
|
+
});
|
|
200
|
+
resolve(server);
|
|
201
|
+
});
|
|
202
|
+
} else {
|
|
203
|
+
server.listen(port, hostname, () => {
|
|
204
|
+
logger$3.info('Server listening', {
|
|
205
|
+
port
|
|
206
|
+
});
|
|
207
|
+
resolve(server);
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
class ParamValidationResult {
|
|
214
|
+
_error(name, key, value) {
|
|
215
|
+
if (!this._errors) {
|
|
216
|
+
this._errors = {};
|
|
217
|
+
}
|
|
218
|
+
this._errors[name] = {
|
|
219
|
+
error: key,
|
|
220
|
+
value
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
getErrors() {
|
|
224
|
+
return this._errors;
|
|
225
|
+
}
|
|
226
|
+
hasErrors() {
|
|
227
|
+
return this._errors !== undefined;
|
|
228
|
+
}
|
|
229
|
+
isValid() {
|
|
230
|
+
return this._errors === undefined;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// string(name: string): ParamValueStringValidator {
|
|
234
|
+
// return new ParamValueStringValidator(this, name, this.context.param(name));
|
|
235
|
+
// }
|
|
236
|
+
/* int(name, position) {
|
|
237
|
+
return new ParamValueIntValidator(this, name, this.context.param(name, position));
|
|
238
|
+
}
|
|
239
|
+
model(modelName, name) {
|
|
240
|
+
name = name || S.string.lcFirst(modelName);
|
|
241
|
+
console.log('paramvalidator model', modelName, M[modelName]);
|
|
242
|
+
let data = this.context.getOrPostParam(name);
|
|
243
|
+
return new ParamValueModelValidator(this, name, !data ? null : new M[modelName](data));
|
|
244
|
+
} */
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
class ParamValid extends ParamValidationResult {
|
|
248
|
+
constructor(context) {
|
|
249
|
+
super();
|
|
250
|
+
this.context = context;
|
|
251
|
+
}
|
|
252
|
+
_error() {
|
|
253
|
+
this.context.throw(400, 'Invalid params', {
|
|
254
|
+
validator: this
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
class ParamValueValidator {
|
|
260
|
+
constructor(validationResult, name, value) {
|
|
261
|
+
this.validationResult = validationResult;
|
|
262
|
+
this.name = name;
|
|
263
|
+
this.value = value;
|
|
264
|
+
}
|
|
265
|
+
isValid() {
|
|
266
|
+
return this.validationResult.isValid();
|
|
267
|
+
}
|
|
268
|
+
_error(key) {
|
|
269
|
+
this.validationResult._error(this.name, key, this.value);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
class ParamValueStringValidator extends ParamValueValidator {
|
|
274
|
+
notEmpty() {
|
|
275
|
+
if (this.value == null || this.value.trim() === '') {
|
|
276
|
+
this._error('notEmpty');
|
|
277
|
+
}
|
|
278
|
+
return this;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
class ParamValueFromContext {
|
|
283
|
+
constructor(context, validationResult) {
|
|
284
|
+
this.validationResult = validationResult;
|
|
285
|
+
this.context = context;
|
|
286
|
+
}
|
|
287
|
+
namedParam(name) {
|
|
288
|
+
return new ParamValueStringValidator(this.validationResult, name, this.context.namedParam(name));
|
|
289
|
+
}
|
|
290
|
+
otherParam(position) {
|
|
291
|
+
return new ParamValueStringValidator(this.validationResult, String(position), this.context.otherParam(position));
|
|
292
|
+
}
|
|
293
|
+
queryParam(name) {
|
|
294
|
+
return new ParamValueStringValidator(this.validationResult, name, this.context.queryParam(name));
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// bodyParam: <T>(name: string): ParamValueValidator<string | undefined> {
|
|
298
|
+
|
|
299
|
+
// }
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function alpParams(app) {
|
|
303
|
+
Object.assign(app.context, {
|
|
304
|
+
namedRouteParam(name) {
|
|
305
|
+
const namedParams = this.route.namedParams;
|
|
306
|
+
return namedParams?.get(name);
|
|
307
|
+
},
|
|
308
|
+
otherRouteParam(position) {
|
|
309
|
+
const otherParams = this.route.otherParams;
|
|
310
|
+
return otherParams?.[position - 1];
|
|
311
|
+
},
|
|
312
|
+
queryParam(name) {
|
|
313
|
+
const searchParams = this.request.searchParams;
|
|
314
|
+
return searchParams.get(name) ?? undefined;
|
|
315
|
+
},
|
|
316
|
+
bodyParam(name) {
|
|
317
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access
|
|
318
|
+
return this.body[name];
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
defineLazyProperty(app.request, 'searchParams', function () {
|
|
322
|
+
return new URLSearchParams(this.search);
|
|
323
|
+
});
|
|
324
|
+
defineLazyProperty(app.context, 'params', function () {
|
|
325
|
+
return new ParamValueFromContext(this, new ParamValidationResult());
|
|
326
|
+
});
|
|
327
|
+
defineLazyProperty(app.context, 'validParams', function () {
|
|
328
|
+
return new ParamValueFromContext(this, new ParamValid(this));
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
333
|
+
const IntlMessageFormat =
|
|
334
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
335
|
+
IntlMessageFormatDefault.default || IntlMessageFormatDefault;
|
|
336
|
+
function load(translations, language) {
|
|
337
|
+
const result = new Map();
|
|
338
|
+
(function loadMap(map, prefix) {
|
|
339
|
+
map.forEach((value, key) => {
|
|
340
|
+
if (typeof value === 'object') {
|
|
341
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
342
|
+
loadMap(value, `${prefix}${key}.`);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
347
|
+
result.set(`${prefix}${key}`, new IntlMessageFormat(value, language));
|
|
348
|
+
});
|
|
349
|
+
})(translations, '');
|
|
350
|
+
return result;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const logger$2 = new Logger('alp:translate');
|
|
354
|
+
function alpTranslate(dirname) {
|
|
355
|
+
dirname = dirname.replace(/\/*$/, '/');
|
|
356
|
+
return app => {
|
|
357
|
+
const appTranslations = new Map();
|
|
358
|
+
Object.assign(app.context, {
|
|
359
|
+
t(id, args) {
|
|
360
|
+
const msg = appTranslations.get(this.language).get(id);
|
|
361
|
+
if (!msg) {
|
|
362
|
+
logger$2.warn('invalid msg', {
|
|
363
|
+
language: this.language,
|
|
364
|
+
id
|
|
365
|
+
});
|
|
366
|
+
return id;
|
|
367
|
+
}
|
|
368
|
+
return msg.format(args);
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
const config = app.config;
|
|
372
|
+
config.get('availableLanguages').forEach(language => {
|
|
373
|
+
const translations = app.loadConfigSync(dirname + language);
|
|
374
|
+
appTranslations.set(language, load(translations, language));
|
|
375
|
+
});
|
|
376
|
+
return appTranslations;
|
|
377
|
+
};
|
|
378
|
+
}
|
|
15
379
|
|
|
16
380
|
const logger$1 = new Logger('alp');
|
|
17
381
|
class AlpNodeApp extends Koa {
|
|
@@ -36,11 +400,11 @@ class AlpNodeApp extends Koa {
|
|
|
36
400
|
});
|
|
37
401
|
this.certPath = certPath || `${packageDirname}/config/cert`;
|
|
38
402
|
this.publicPath = publicPath || `${packageDirname}/public/`;
|
|
39
|
-
this.config =
|
|
403
|
+
this.config = getConfig(this, config);
|
|
40
404
|
this.context.config = this.config;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
405
|
+
alpParams(this);
|
|
406
|
+
alpLanguage(this);
|
|
407
|
+
alpTranslate('locales')(this);
|
|
44
408
|
this.use(compress());
|
|
45
409
|
}
|
|
46
410
|
existsConfigSync(name) {
|
|
@@ -58,7 +422,7 @@ class AlpNodeApp extends Koa {
|
|
|
58
422
|
this.use(serve(this.publicPath)); // static files
|
|
59
423
|
}
|
|
60
424
|
catchErrors() {
|
|
61
|
-
this.use(
|
|
425
|
+
this.use(alpNodeErrors);
|
|
62
426
|
}
|
|
63
427
|
|
|
64
428
|
// eslint-disable-next-line @typescript-eslint/class-methods-use-this
|
|
@@ -78,7 +442,7 @@ class AlpNodeApp extends Koa {
|
|
|
78
442
|
async start(fn) {
|
|
79
443
|
await fn();
|
|
80
444
|
try {
|
|
81
|
-
const server = await
|
|
445
|
+
const server = await alpListen(this.config, this.callback(), this.certPath);
|
|
82
446
|
this._server = server;
|
|
83
447
|
logger$1.success('started');
|
|
84
448
|
if (process.send) process.send('ready');
|
|
@@ -92,6 +456,29 @@ class AlpNodeApp extends Koa {
|
|
|
92
456
|
}
|
|
93
457
|
}
|
|
94
458
|
|
|
459
|
+
const createAlpRouterBuilder = () => createRouterBuilder();
|
|
460
|
+
function alpRouter(router) {
|
|
461
|
+
return app => {
|
|
462
|
+
app.router = router;
|
|
463
|
+
app.context.urlGenerator = function (routeKey, params) {
|
|
464
|
+
return router.toLocalizedPath(this.language, routeKey, params);
|
|
465
|
+
};
|
|
466
|
+
app.context.redirectTo = function (to, params) {
|
|
467
|
+
this.redirect(router.toLocalizedPath(this.language, to, params));
|
|
468
|
+
};
|
|
469
|
+
return async ctx => {
|
|
470
|
+
// eslint-disable-next-line unicorn/no-array-method-this-argument
|
|
471
|
+
const routeMatch = router.find(ctx.request.path, ctx.language);
|
|
472
|
+
if (!routeMatch) {
|
|
473
|
+
ctx.status = 404;
|
|
474
|
+
throw new Error(`Route not found: ${ctx.request.path}`);
|
|
475
|
+
}
|
|
476
|
+
ctx.route = routeMatch;
|
|
477
|
+
await routeMatch.ref(ctx);
|
|
478
|
+
};
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
|
|
95
482
|
const logger = new Logger('alp');
|
|
96
483
|
const appDirname = path.resolve('build');
|
|
97
484
|
const packagePath = path.resolve('package.json');
|
|
@@ -120,5 +507,5 @@ class App extends AlpNodeApp {
|
|
|
120
507
|
}
|
|
121
508
|
}
|
|
122
509
|
|
|
123
|
-
export { appDirname, config, App as default, packageConfig, packageDirname };
|
|
510
|
+
export { Config, appDirname, config, createAlpRouterBuilder, App as default, packageConfig, packageDirname, alpRouter as router };
|
|
124
511
|
//# sourceMappingURL=index-node18.mjs.map
|