@eik/core 2.0.27 → 2.1.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 CHANGED
@@ -1,3 +1,11 @@
1
+ # [2.1.0](https://github.com/eik-lib/core/compare/v2.0.27...v2.1.0) (2025-09-30)
2
+
3
+
4
+ ### Features
5
+
6
+ * add new alias route handler ([e7d78f6](https://github.com/eik-lib/core/commit/e7d78f6559cc9efb61c78e992ad094e0bb312a9d))
7
+ * add stale while revalidate to new alias route ([7639c11](https://github.com/eik-lib/core/commit/7639c11dda5a0353de499552294611fe96914fab))
8
+
1
9
  ## [2.0.27](https://github.com/eik-lib/core/compare/v2.0.26...v2.0.27) (2025-09-24)
2
10
 
3
11
 
@@ -0,0 +1,146 @@
1
+ import { validators } from "@eik/common";
2
+ import originalUrl from "original-url";
3
+ import HttpError from "http-errors";
4
+ import abslog from "abslog";
5
+ import Metrics from "@metrics/client";
6
+ import Asset from "../classes/asset.js";
7
+
8
+ import { decodeUriComponent, readJSON } from "../utils/utils.js";
9
+ import {
10
+ createFilePathToAlias,
11
+ createFilePathToAsset,
12
+ } from "../utils/path-builders-fs.js";
13
+ import HttpOutgoing from "../classes/http-outgoing.js";
14
+ import config from "../utils/defaults.js";
15
+
16
+ /**
17
+ * @typedef {object} AliasGetOptions
18
+ * @property {boolean} [etag]
19
+ * @property {string} [cacheControl]
20
+ * @property {Array<[string, string]>} [organizations] List of key-value pairs [hostname, organization]
21
+ * @property {import("@eik/sink").default} [sink]
22
+ * @property {import("abslog").AbstractLoggerOptions} [logger]
23
+ */
24
+
25
+ const AliasGet = class AliasGet {
26
+ /**
27
+ * @param {AliasGetOptions} options
28
+ */
29
+ constructor({ organizations, cacheControl, logger, sink, etag } = {}) {
30
+ this._organizations = organizations || config.organizations;
31
+ // 2 weeks stale while revalidate
32
+ this._cacheControl =
33
+ cacheControl || "public, max-age=1200, stale-while-revalidate=1209600";
34
+ this._sink = sink;
35
+ this._etag = etag || config.etag;
36
+ this._log = abslog(logger);
37
+ this._metrics = new Metrics();
38
+ this._histogram = this._metrics.histogram({
39
+ name: "eik_core_alias_swr_get_handler",
40
+ description:
41
+ "Histogram measuring time taken in @eik/core stale-while-revalidate powered AliasGet handler method",
42
+ labels: {
43
+ success: true,
44
+ type: "unknown",
45
+ },
46
+ buckets: [0.005, 0.01, 0.06, 0.1, 0.6, 1.0, 2.0, 4.0],
47
+ });
48
+ this._orgRegistry = new Map(this._organizations);
49
+ }
50
+
51
+ get metrics() {
52
+ return this._metrics;
53
+ }
54
+
55
+ async handler(req, type, name, alias, extra) {
56
+ const end = this._histogram.timer();
57
+
58
+ const pAlias = decodeUriComponent(alias);
59
+ const pExtra = decodeUriComponent(extra);
60
+ const pName = decodeUriComponent(name);
61
+
62
+ try {
63
+ validators.alias(pAlias);
64
+ validators.extra(pExtra);
65
+ validators.name(pName);
66
+ validators.type(type);
67
+ } catch (error) {
68
+ this._log.debug(`alias:get - Validation failed - ${error.message}`);
69
+ const e = new HttpError.NotFound();
70
+ end({ labels: { success: false, status: e.status } });
71
+ throw e;
72
+ }
73
+
74
+ const url = originalUrl(req);
75
+ const org = this._orgRegistry.get(url.hostname);
76
+
77
+ if (!org) {
78
+ this._log.info(
79
+ `alias:get - Hostname does not match a configured organization - ${url.hostname}`,
80
+ );
81
+ const e = new HttpError.BadRequest();
82
+ end({ labels: { success: false, status: e.status, type } });
83
+ throw e;
84
+ }
85
+
86
+ const path = createFilePathToAlias({
87
+ org,
88
+ type,
89
+ name: pName,
90
+ alias: pAlias,
91
+ });
92
+
93
+ try {
94
+ const obj = await readJSON(this._sink, path);
95
+
96
+ const asset = new Asset({
97
+ pathname: pExtra,
98
+ version: obj.version,
99
+ name: obj.name,
100
+ type: obj.type,
101
+ org: obj.org,
102
+ });
103
+
104
+ const assetPath = createFilePathToAsset(asset);
105
+
106
+ const file = await this._sink.read(assetPath);
107
+ const outgoing = new HttpOutgoing();
108
+ outgoing.cacheControl = this._cacheControl;
109
+ outgoing.mimeType = asset.mimeType;
110
+
111
+ if (this._etag) {
112
+ outgoing.etag = file.etag;
113
+ }
114
+
115
+ if (this._etag && req.headers["if-none-match"] === file.etag) {
116
+ outgoing.statusCode = 304;
117
+ file.stream.destroy();
118
+ } else {
119
+ outgoing.statusCode = 200;
120
+ outgoing.stream = file.stream;
121
+
122
+ outgoing.stream.on("error", (err) => {
123
+ this._log.info(`alias:get - File stream error - ${err}`);
124
+ end({ labels: { success: false, status: 503, type } });
125
+ });
126
+
127
+ outgoing.stream.on("end", () => {
128
+ end({ labels: { status: outgoing.statusCode, type } });
129
+ });
130
+ }
131
+
132
+ this._log.debug(`alias:get - Alias found - Pathname: ${path}`);
133
+
134
+ end({ labels: { status: outgoing.statusCode, type } });
135
+
136
+ return outgoing;
137
+ // eslint-disable-next-line no-unused-vars
138
+ } catch (error) {
139
+ this._log.debug(`alias:get - Alias not found - Pathname: ${path}`);
140
+ const e = new HttpError.NotFound();
141
+ end({ labels: { success: false, status: e.status, type } });
142
+ throw e;
143
+ }
144
+ }
145
+ };
146
+ export default AliasGet;
package/lib/main.js CHANGED
@@ -5,6 +5,7 @@ import VersionsGet from "./handlers/versions.get.js";
5
5
  import AliasPost from "./handlers/alias.post.js";
6
6
  import AliasPut from "./handlers/alias.put.js";
7
7
  import AliasGet from "./handlers/alias.get.js";
8
+ import AliasGetV2 from "./handlers/alias.get-v2.js";
8
9
  import AliasDel from "./handlers/alias.delete.js";
9
10
  import AuthPost from "./handlers/auth.post.js";
10
11
  import PkgLog from "./handlers/pkg.log.js";
@@ -23,6 +24,7 @@ const http = {
23
24
  AliasPost,
24
25
  AliasPut,
25
26
  AliasGet,
27
+ AliasGetV2,
26
28
  AliasDel,
27
29
  AuthPost,
28
30
  PkgLog,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eik/core",
3
- "version": "2.0.27",
3
+ "version": "2.1.0",
4
4
  "description": "Core server package",
5
5
  "main": "lib/main.js",
6
6
  "types": "./types/main.d.ts",
@@ -0,0 +1,51 @@
1
+ export default AliasGet;
2
+ export type AliasGetOptions = {
3
+ etag?: boolean;
4
+ cacheControl?: string;
5
+ /**
6
+ * List of key-value pairs [hostname, organization]
7
+ */
8
+ organizations?: Array<[string, string]>;
9
+ sink?: import("@eik/sink").default;
10
+ logger?: import("abslog").AbstractLoggerOptions;
11
+ };
12
+ /**
13
+ * @typedef {object} AliasGetOptions
14
+ * @property {boolean} [etag]
15
+ * @property {string} [cacheControl]
16
+ * @property {Array<[string, string]>} [organizations] List of key-value pairs [hostname, organization]
17
+ * @property {import("@eik/sink").default} [sink]
18
+ * @property {import("abslog").AbstractLoggerOptions} [logger]
19
+ */
20
+ declare const AliasGet: {
21
+ new ({ organizations, cacheControl, logger, sink, etag }?: AliasGetOptions): {
22
+ _organizations: [string, string][];
23
+ _cacheControl: string;
24
+ _sink: import("@eik/sink").default;
25
+ _etag: boolean;
26
+ _log: abslog.ValidLogger;
27
+ _metrics: Metrics;
28
+ _histogram: Metrics.MetricsHistogram;
29
+ _orgRegistry: Map<string, string>;
30
+ readonly metrics: Metrics;
31
+ handler(req: any, type: any, name: any, alias: any, extra: any): Promise<{
32
+ _cacheControl: string;
33
+ _statusCode: number;
34
+ _mimeType: string;
35
+ _location: string;
36
+ _stream: any;
37
+ _body: any;
38
+ _etag: string;
39
+ cacheControl: string;
40
+ statusCode: number;
41
+ location: string;
42
+ mimeType: string;
43
+ stream: any;
44
+ body: any;
45
+ etag: string;
46
+ readonly [Symbol.toStringTag]: string;
47
+ }>;
48
+ };
49
+ };
50
+ import abslog from "abslog";
51
+ import Metrics from "@metrics/client";
package/types/main.d.ts CHANGED
@@ -11,6 +11,7 @@ declare namespace http {
11
11
  export { AliasPost };
12
12
  export { AliasPut };
13
13
  export { AliasGet };
14
+ export { AliasGetV2 };
14
15
  export { AliasDel };
15
16
  export { AuthPost };
16
17
  export { PkgLog };
@@ -35,6 +36,7 @@ import VersionsGet from "./handlers/versions.get.js";
35
36
  import AliasPost from "./handlers/alias.post.js";
36
37
  import AliasPut from "./handlers/alias.put.js";
37
38
  import AliasGet from "./handlers/alias.get.js";
39
+ import AliasGetV2 from "./handlers/alias.get-v2.js";
38
40
  import AliasDel from "./handlers/alias.delete.js";
39
41
  import AuthPost from "./handlers/auth.post.js";
40
42
  import PkgLog from "./handlers/pkg.log.js";