oc 0.50.61 → 0.50.63
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/.turbo/turbo-build.log +5 -5
- package/.turbo/turbo-lint.log +2 -2
- package/.turbo/turbo-test-silent.log +12 -11
- package/.turbo/turbo-test.log +1806 -1750
- package/CHANGELOG.md +13 -0
- package/README.md +16 -0
- package/dist/cli/domain/local.js +7 -4
- package/dist/components/oc-client/_package/package.json +4 -4
- package/dist/components/oc-client/_package/server.js +1 -1
- package/dist/components/oc-client/package.json +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/registry/domain/events-handler.d.ts +2 -5
- package/dist/registry/domain/http-server/express-adapter.d.ts +2 -2
- package/dist/registry/domain/http-server/express-adapter.js +47 -2
- package/dist/registry/domain/http-server/types.d.ts +21 -7
- package/dist/registry/domain/metadata-migration.js +8 -1
- package/dist/registry/domain/options-sanitiser.d.ts +8 -2
- package/dist/registry/domain/options-sanitiser.js +2 -0
- package/dist/registry/domain/plugins-initialiser.js +41 -5
- package/dist/registry/domain/repository.js +100 -19
- package/dist/registry/domain/validators/registry-configuration.d.ts +5 -2
- package/dist/registry/domain/validators/registry-configuration.js +5 -0
- package/dist/registry/index.js +50 -29
- package/dist/registry/middleware/cors.d.ts +4 -0
- package/dist/registry/middleware/cors.js +62 -4
- package/dist/registry/router.js +4 -2
- package/dist/registry/routes/component-info.js +3 -2
- package/dist/registry/routes/component-preview.js +3 -2
- package/dist/registry/routes/component.d.ts +2 -1
- package/dist/registry/routes/component.js +1 -2
- package/dist/registry/routes/components.d.ts +2 -1
- package/dist/registry/routes/components.js +1 -2
- package/dist/registry/routes/helpers/get-component.d.ts +2 -1
- package/dist/registry/routes/helpers/get-component.js +7 -8
- package/dist/registry/routes/index.js +11 -7
- package/dist/resources/index.d.ts +5 -1
- package/dist/resources/index.js +5 -1
- package/dist/types.d.ts +31 -3
- package/dist/utils/bounded-cache.d.ts +10 -0
- package/dist/utils/bounded-cache.js +31 -0
- package/js-library-optimization-playbook.md +1379 -0
- package/package.json +4 -3
- package/tsconfig.types.json +10 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
## Change Log
|
|
2
2
|
|
|
3
|
+
## 0.50.63
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- fc64c56: Reduce registry startup metadata reconciliation requests by skipping versions already present in metadata storage.
|
|
8
|
+
|
|
9
|
+
## 0.50.62
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- c826175: Add `clearTimeout` to allowed globals for component execution, complementing the existing `setTimeout` global to allow proper timer management.
|
|
14
|
+
- 704890d: Add promise-based `listen` and `close` methods to the HTTP server adapters while retaining callback compatibility with deprecation warnings.
|
|
15
|
+
|
|
3
16
|
## 0.50.61
|
|
4
17
|
|
|
5
18
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -212,6 +212,22 @@ These files are one-way projections from the metadata store. They can help with
|
|
|
212
212
|
rollback to storage mode, but they do not replace the storage adapter because
|
|
213
213
|
component statics remain in storage.
|
|
214
214
|
|
|
215
|
+
## Registry events
|
|
216
|
+
|
|
217
|
+
The registry emits an `error` event with the stable payload shape
|
|
218
|
+
`{ code: string, message: string }`:
|
|
219
|
+
|
|
220
|
+
```js
|
|
221
|
+
registry.on('error', ({ code, message }) => {
|
|
222
|
+
console.error(code, message);
|
|
223
|
+
});
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Errors raised by the configured server adapter use the adapter-neutral code
|
|
227
|
+
`SERVER_ERROR`. The message is preserved from the adapter error, and the existing
|
|
228
|
+
`start` callback and promise rejection behavior is unchanged. The public
|
|
229
|
+
TypeScript payload is exported as `RegistryErrorEvent`.
|
|
230
|
+
|
|
215
231
|
## Requirements and build status
|
|
216
232
|
|
|
217
233
|
Disclaimer: This project is still under heavy development and the API is likely to change at any time. In case you would find any issues, check the [troubleshooting page](../../CONTRIBUTING.md#troubleshooting).
|
package/dist/cli/domain/local.js
CHANGED
|
@@ -41,7 +41,7 @@ const node_util_1 = require("node:util");
|
|
|
41
41
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
42
42
|
const targz_1 = __importDefault(require("targz"));
|
|
43
43
|
const validator = __importStar(require("../../registry/domain/validators"));
|
|
44
|
-
const
|
|
44
|
+
const deprecate_1 = __importDefault(require("../../utils/deprecate"));
|
|
45
45
|
const is_template_legacy_1 = __importDefault(require("../../utils/is-template-legacy"));
|
|
46
46
|
const clean = __importStar(require("./clean"));
|
|
47
47
|
const get_components_by_dir_1 = __importDefault(require("./get-components-by-dir"));
|
|
@@ -67,16 +67,19 @@ function local() {
|
|
|
67
67
|
},
|
|
68
68
|
getComponentsByDir: (0, get_components_by_dir_1.default)(),
|
|
69
69
|
async init(options) {
|
|
70
|
-
const { componentName
|
|
70
|
+
const { componentName } = options;
|
|
71
71
|
let { templateType } = options;
|
|
72
72
|
if (!validator.validateComponentName(componentName)) {
|
|
73
73
|
throw 'name not valid';
|
|
74
74
|
}
|
|
75
|
-
// LEGACY TEMPLATES WARNING
|
|
76
75
|
if ((0, is_template_legacy_1.default)(templateType)) {
|
|
77
76
|
const legacyName = templateType;
|
|
78
77
|
templateType = legacyName.replace(legacyName, `oc-template-${legacyName}`);
|
|
79
|
-
|
|
78
|
+
(0, deprecate_1.default)({
|
|
79
|
+
id: `cli-init-legacy-template-${legacyName}`,
|
|
80
|
+
subject: `The bare \`${legacyName}\` template type`,
|
|
81
|
+
replacement: 'the modern ESM component runtime (`oc-template-es6`)'
|
|
82
|
+
});
|
|
80
83
|
}
|
|
81
84
|
try {
|
|
82
85
|
await (0, init_template_1.default)(Object.assign(options, {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oc-client",
|
|
3
3
|
"description": "The OpenComponents client-side javascript client",
|
|
4
|
-
"version": "0.50.
|
|
4
|
+
"version": "0.50.63",
|
|
5
5
|
"repository": "https://github.com/opencomponents/oc/tree/master/components/oc-client",
|
|
6
6
|
"author": "Matteo Figus <matteofigus@gmail.com>",
|
|
7
7
|
"oc": {
|
|
@@ -23,14 +23,14 @@
|
|
|
23
23
|
],
|
|
24
24
|
"dataProvider": {
|
|
25
25
|
"type": "node.js",
|
|
26
|
-
"hashKey": "
|
|
26
|
+
"hashKey": "879a222b2e89b6a0685c2bad8ca3eb9798b6cc55",
|
|
27
27
|
"src": "server.js",
|
|
28
28
|
"size": 644
|
|
29
29
|
}
|
|
30
30
|
},
|
|
31
|
-
"version": "0.50.
|
|
31
|
+
"version": "0.50.63",
|
|
32
32
|
"packaged": true,
|
|
33
|
-
"date":
|
|
33
|
+
"date": 1787207196137
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"oc-template-es6-compiler": "^8.0.0"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=(t,s)=>{const{staticPath:e,templates:a}=t;return s(null,{staticPath:e,templates:a})},r=(t,s)=>{o(t,(e,a,i={})=>{if(e)return s(e);if(a==null)return s(null,{__oc_emptyResponse:!0});const n=t.action?a:Object.assign({},a,{_staticPath:t.staticPath,_baseUrl:t.baseUrl,_componentName:"oc-client",_componentVersion:"0.50.
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=(t,s)=>{const{staticPath:e,templates:a}=t;return s(null,{staticPath:e,templates:a})},r=(t,s)=>{o(t,(e,a,i={})=>{if(e)return s(e);if(a==null)return s(null,{__oc_emptyResponse:!0});const n=t.action?a:Object.assign({},a,{_staticPath:t.staticPath,_baseUrl:t.baseUrl,_componentName:"oc-client",_componentVersion:"0.50.63"}),c=t.staticPath.indexOf("http")===0?t.staticPath:"https:"+t.staticPath;return s(null,Object.assign({},{component:{key:"c4abb6bf4dc6657fb718a45b64bd6b2cb92e874a",src:c+"template.js",props:n,esm:!1,development:void 0}}))})};exports.data=r;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oc-client",
|
|
3
3
|
"description": "The OpenComponents client-side javascript client",
|
|
4
|
-
"version": "0.50.
|
|
4
|
+
"version": "0.50.63",
|
|
5
5
|
"repository": "https://github.com/opencomponents/oc/tree/master/components/oc-client",
|
|
6
6
|
"author": "Matteo Figus <matteofigus@gmail.com>",
|
|
7
7
|
"oc": {
|
package/dist/index.d.ts
CHANGED
|
@@ -2,5 +2,5 @@ export { default as Client } from 'oc-client';
|
|
|
2
2
|
export { default as cli } from './cli/programmatic-api';
|
|
3
3
|
export type { RegistryType } from './registry';
|
|
4
4
|
export { default as Registry, RegistryOptions } from './registry';
|
|
5
|
-
export type { CookieOptions, ExpressMiddleware, HttpServerAdapter, HttpServerAdapterFactory, Method, NativeApp, OcHandler, OcRequest, OcResponse, UploadedFile } from './registry/domain/http-server/types';
|
|
6
|
-
export type { Plugin, PluginContext } from './types';
|
|
5
|
+
export type { CookieOptions, ExpressMiddleware, HttpServerAdapter, HttpServerAdapterFactory, HttpServerListenOptions, Method, NativeApp, OcHandler, OcRequest, OcResponse, PromiseHttpServerAdapter, UploadedFile } from './registry/domain/http-server/types';
|
|
6
|
+
export type { CorsConfig, CorsOptions, Plugin, PluginContext, RegistryErrorEvent } from './types';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { IncomingHttpHeaders } from 'node:http';
|
|
2
|
-
import type { Component } from '../../types';
|
|
2
|
+
import type { Component, RegistryErrorEvent } from '../../types';
|
|
3
3
|
type Subscription<T = any> = (data: T) => void;
|
|
4
4
|
export interface RequestData {
|
|
5
5
|
body: unknown;
|
|
@@ -15,10 +15,7 @@ export interface RequestData {
|
|
|
15
15
|
errorCode?: string;
|
|
16
16
|
}
|
|
17
17
|
type Events = {
|
|
18
|
-
error:
|
|
19
|
-
code: string;
|
|
20
|
-
message: string;
|
|
21
|
-
};
|
|
18
|
+
error: RegistryErrorEvent;
|
|
22
19
|
start: unknown;
|
|
23
20
|
'cache-poll': number;
|
|
24
21
|
request: RequestData;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { type Express } from 'express';
|
|
2
|
-
import type {
|
|
3
|
-
export default function createExpressAdapter(options?: unknown):
|
|
2
|
+
import type { PromiseHttpServerAdapter } from './types';
|
|
3
|
+
export default function createExpressAdapter(options?: unknown): PromiseHttpServerAdapter<Express>;
|
|
@@ -11,9 +11,15 @@ const express_1 = __importDefault(require("express"));
|
|
|
11
11
|
const morgan_1 = __importDefault(require("morgan"));
|
|
12
12
|
const multer_1 = __importDefault(require("multer"));
|
|
13
13
|
const response_time_1 = __importDefault(require("response-time"));
|
|
14
|
+
const deprecate_1 = __importDefault(require("../../../utils/deprecate"));
|
|
14
15
|
const expressMiddleware = Symbol('expressMiddleware');
|
|
15
16
|
const ocResponseSym = Symbol('ocResponse');
|
|
16
17
|
const ocParamsSym = Symbol('ocParams');
|
|
18
|
+
const warnAboutCallback = () => (0, deprecate_1.default)({
|
|
19
|
+
id: 'http-server-adapter-callbacks',
|
|
20
|
+
subject: 'The HTTP server adapter callback API',
|
|
21
|
+
replacement: 'the returned promises'
|
|
22
|
+
});
|
|
17
23
|
function stream(readable) {
|
|
18
24
|
readable.pipe(this.raw);
|
|
19
25
|
}
|
|
@@ -31,6 +37,7 @@ function createExpressAdapter(options) {
|
|
|
31
37
|
}
|
|
32
38
|
class ExpressHttpServerAdapter {
|
|
33
39
|
name = 'express';
|
|
40
|
+
supportsPromiseLifecycle = true;
|
|
34
41
|
app;
|
|
35
42
|
server;
|
|
36
43
|
serverErrorHandlers = [];
|
|
@@ -119,14 +126,52 @@ class ExpressHttpServerAdapter {
|
|
|
119
126
|
for (const handler of this.serverErrorHandlers) {
|
|
120
127
|
this.server.on('error', handler);
|
|
121
128
|
}
|
|
122
|
-
|
|
129
|
+
if (cb) {
|
|
130
|
+
warnAboutCallback();
|
|
131
|
+
this.server.listen(opts.port, cb);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
return new Promise((resolve, reject) => {
|
|
135
|
+
const onListening = () => {
|
|
136
|
+
cleanup();
|
|
137
|
+
resolve();
|
|
138
|
+
};
|
|
139
|
+
const onError = (err) => {
|
|
140
|
+
cleanup();
|
|
141
|
+
reject(err);
|
|
142
|
+
};
|
|
143
|
+
const cleanup = () => {
|
|
144
|
+
this.server?.off('listening', onListening);
|
|
145
|
+
this.server?.off('error', onError);
|
|
146
|
+
};
|
|
147
|
+
this.server?.once('listening', onListening);
|
|
148
|
+
this.server?.once('error', onError);
|
|
149
|
+
this.server?.listen(opts.port);
|
|
150
|
+
});
|
|
123
151
|
}
|
|
124
152
|
onServerError(cb) {
|
|
125
153
|
this.serverErrorHandlers.push(cb);
|
|
126
154
|
this.server?.on('error', cb);
|
|
127
155
|
}
|
|
128
156
|
close(cb) {
|
|
129
|
-
|
|
157
|
+
if (cb) {
|
|
158
|
+
warnAboutCallback();
|
|
159
|
+
this.server?.close(cb);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (!this.server) {
|
|
163
|
+
return Promise.resolve();
|
|
164
|
+
}
|
|
165
|
+
return new Promise((resolve, reject) => {
|
|
166
|
+
this.server?.close((err) => {
|
|
167
|
+
if (err) {
|
|
168
|
+
reject(err);
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
resolve();
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
});
|
|
130
175
|
}
|
|
131
176
|
isListening() {
|
|
132
177
|
return !!this.server?.listening;
|
|
@@ -69,6 +69,11 @@ export interface OcResponse {
|
|
|
69
69
|
}
|
|
70
70
|
export type OcHandler = (req: OcRequest, res: OcResponse) => void | Promise<void>;
|
|
71
71
|
export type ExpressMiddleware = (req: any, res: any, next: (err?: unknown) => void) => void;
|
|
72
|
+
export interface HttpServerListenOptions {
|
|
73
|
+
port: number | string;
|
|
74
|
+
timeout: number;
|
|
75
|
+
keepAliveTimeout?: number;
|
|
76
|
+
}
|
|
72
77
|
export type HttpServerAdapterFactory<TOptions = unknown, TNative = unknown> = {
|
|
73
78
|
(options?: unknown): HttpServerAdapter<TNative>;
|
|
74
79
|
readonly __serverAdapterOptions?: TOptions;
|
|
@@ -77,7 +82,7 @@ export type HttpServerAdapterOptions<TAdapter> = TAdapter extends {
|
|
|
77
82
|
readonly __serverAdapterOptions?: infer TOptions;
|
|
78
83
|
} ? TOptions : TAdapter extends (options?: infer TOptions) => HttpServerAdapter ? TOptions : unknown;
|
|
79
84
|
export type NativeApp<TAdapter> = TAdapter extends (...args: any[]) => HttpServerAdapter<infer TNative> ? unknown extends TNative ? express.Express : TNative : TAdapter extends HttpServerAdapter<infer TNative> ? unknown extends TNative ? express.Express : TNative : express.Express;
|
|
80
|
-
|
|
85
|
+
interface HttpServerAdapterBase<TNative> {
|
|
81
86
|
name: string;
|
|
82
87
|
enableBodyParser(opts: {
|
|
83
88
|
limit?: number | string;
|
|
@@ -95,14 +100,23 @@ export interface HttpServerAdapter<TNative = unknown> {
|
|
|
95
100
|
use(handler: OcHandler): void;
|
|
96
101
|
route(method: Method, path: string, id: string, handlers: OcHandler[]): void;
|
|
97
102
|
fromConnect(handler: ExpressMiddleware): OcHandler;
|
|
98
|
-
listen(opts: {
|
|
99
|
-
port: number | string;
|
|
100
|
-
timeout: number;
|
|
101
|
-
keepAliveTimeout?: number;
|
|
102
|
-
}, cb: (err?: Error) => void): void;
|
|
103
103
|
onServerError(cb: (err: Error) => void): void;
|
|
104
|
-
close(cb: (err?: Error) => void): void;
|
|
105
104
|
isListening(): boolean;
|
|
106
105
|
native(): TNative;
|
|
107
106
|
httpServer(): http.Server;
|
|
108
107
|
}
|
|
108
|
+
export interface PromiseHttpServerAdapterLifecycle {
|
|
109
|
+
readonly supportsPromiseLifecycle: true;
|
|
110
|
+
listen(opts: HttpServerListenOptions): Promise<void>;
|
|
111
|
+
listen(opts: HttpServerListenOptions, cb: (err?: Error) => void): void;
|
|
112
|
+
close(): Promise<void>;
|
|
113
|
+
close(cb: (err?: Error) => void): void;
|
|
114
|
+
}
|
|
115
|
+
interface CallbackHttpServerAdapterLifecycle {
|
|
116
|
+
readonly supportsPromiseLifecycle?: false | undefined;
|
|
117
|
+
listen(opts: HttpServerListenOptions, cb: (err?: Error) => void): void;
|
|
118
|
+
close(cb: (err?: Error) => void): void;
|
|
119
|
+
}
|
|
120
|
+
export type PromiseHttpServerAdapter<TNative = unknown> = HttpServerAdapterBase<TNative> & PromiseHttpServerAdapterLifecycle;
|
|
121
|
+
export type HttpServerAdapter<TNative = unknown> = PromiseHttpServerAdapter<TNative> | (HttpServerAdapterBase<TNative> & CallbackHttpServerAdapterLifecycle);
|
|
122
|
+
export {};
|
|
@@ -9,6 +9,7 @@ const pLimit_1 = __importDefault(require("../../utils/pLimit"));
|
|
|
9
9
|
const metadata_index_1 = require("./metadata-index");
|
|
10
10
|
const isNotFoundError = (err, code) => err === code || err?.code === code;
|
|
11
11
|
const getTemplateSize = (component) => component.oc.files.template.size;
|
|
12
|
+
const getMetadataRowKey = (row) => JSON.stringify([row.name, row.version]);
|
|
12
13
|
const getComponentRowsFromComponentsDetails = (componentsDetails) => {
|
|
13
14
|
const rows = [];
|
|
14
15
|
for (const [name, versions] of Object.entries(componentsDetails.components || {})) {
|
|
@@ -33,8 +34,14 @@ const backfillMetadataRows = async (metadataStore, rows) => {
|
|
|
33
34
|
inserted: 0,
|
|
34
35
|
skipped: 0
|
|
35
36
|
};
|
|
37
|
+
if (rows.length === 0) {
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
const existingRowKeys = new Set((await metadataStore.getAllComponents()).map(getMetadataRowKey));
|
|
41
|
+
const pendingRows = rows.filter((row) => !existingRowKeys.has(getMetadataRowKey(row)));
|
|
42
|
+
result.skipped = rows.length - pendingRows.length;
|
|
36
43
|
const limit = (0, pLimit_1.default)(10);
|
|
37
|
-
await Promise.all(
|
|
44
|
+
await Promise.all(pendingRows.map((row) => limit(async () => {
|
|
38
45
|
try {
|
|
39
46
|
await metadataStore.addVersion(row);
|
|
40
47
|
result.inserted += 1;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { compileSync } from 'oc-client-browser';
|
|
2
|
-
import type { Config } from '../../types';
|
|
2
|
+
import type { Config, CorsOptions } from '../../types';
|
|
3
3
|
import type { HttpServerAdapterFactory } from './http-server/types';
|
|
4
4
|
type CompileOptions = Omit<Exclude<Parameters<typeof compileSync>[0], undefined>, 'templates'>;
|
|
5
|
-
export interface RegistryOptions<T = any, TServerAdapter extends HttpServerAdapterFactory = HttpServerAdapterFactory> extends Partial<Omit<Config<T, TServerAdapter>, 'beforePublish' | 'dataProvider' | 'discovery' | 'plugins'>> {
|
|
5
|
+
export interface RegistryOptions<T = any, TServerAdapter extends HttpServerAdapterFactory = HttpServerAdapterFactory> extends Partial<Omit<Config<T, TServerAdapter>, 'beforePublish' | 'cors' | 'dataProvider' | 'discovery' | 'plugins'>> {
|
|
6
6
|
/**
|
|
7
7
|
* Configuration for the data provider step, i.e. the component's `server.js`
|
|
8
8
|
* that produces the model consumed by the view.
|
|
@@ -28,6 +28,12 @@ export interface RegistryOptions<T = any, TServerAdapter extends HttpServerAdapt
|
|
|
28
28
|
validate?: boolean;
|
|
29
29
|
robots?: boolean;
|
|
30
30
|
} | boolean;
|
|
31
|
+
/**
|
|
32
|
+
* CORS response headers sent by the registry.
|
|
33
|
+
*
|
|
34
|
+
* @default Existing registry CORS headers
|
|
35
|
+
*/
|
|
36
|
+
cors?: CorsOptions;
|
|
31
37
|
/**
|
|
32
38
|
* Public base URL where the registry will be accessible by consumers.
|
|
33
39
|
* It **must** already include the chosen {@link Config.prefix} and end with a trailing slash.
|
|
@@ -41,6 +41,7 @@ const node_zlib_1 = __importDefault(require("node:zlib"));
|
|
|
41
41
|
const oc_client_browser_1 = require("oc-client-browser");
|
|
42
42
|
const settings_1 = __importDefault(require("../../resources/settings"));
|
|
43
43
|
const deprecate_1 = __importDefault(require("../../utils/deprecate"));
|
|
44
|
+
const cors_1 = require("../middleware/cors");
|
|
44
45
|
const auth = __importStar(require("./authentication"));
|
|
45
46
|
const express_adapter_1 = __importDefault(require("./http-server/express-adapter"));
|
|
46
47
|
const DEFAULT_NODE_KEEPALIVE_MS = 5000;
|
|
@@ -77,6 +78,7 @@ function optionsSanitiser(input) {
|
|
|
77
78
|
...options.dataProvider,
|
|
78
79
|
enabled: options.dataProvider?.enabled !== false
|
|
79
80
|
};
|
|
81
|
+
options.cors = (0, cors_1.normaliseCorsConfig)(options.cors);
|
|
80
82
|
if (typeof options.discovery === 'boolean') {
|
|
81
83
|
(0, deprecate_1.default)({
|
|
82
84
|
id: 'registry-config-discovery-boolean',
|
|
@@ -4,9 +4,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.init = init;
|
|
7
|
-
const node_util_1 = require("node:util");
|
|
8
7
|
const dependency_graph_1 = require("dependency-graph");
|
|
9
8
|
const resources_1 = __importDefault(require("../../resources"));
|
|
9
|
+
const deprecate_1 = __importDefault(require("../../utils/deprecate"));
|
|
10
10
|
const pLimit_1 = __importDefault(require("../../utils/pLimit"));
|
|
11
11
|
function validatePlugins(plugins) {
|
|
12
12
|
for (let idx = 0; idx < plugins.length; idx++) {
|
|
@@ -39,9 +39,46 @@ function checkDependencies(plugins) {
|
|
|
39
39
|
}
|
|
40
40
|
return graph.overallOrder();
|
|
41
41
|
}
|
|
42
|
-
|
|
42
|
+
const isPromiseLike = (value) => typeof value?.then === 'function';
|
|
43
|
+
const registerPlugin = (plugin, dependencies) => {
|
|
44
|
+
const register = plugin.register.register;
|
|
45
|
+
return new Promise((resolve, reject) => {
|
|
46
|
+
let callbackCalled = false;
|
|
47
|
+
let callbackError;
|
|
48
|
+
let useCallback = false;
|
|
49
|
+
let result;
|
|
50
|
+
try {
|
|
51
|
+
result = register(plugin.options || {}, dependencies, (error) => {
|
|
52
|
+
callbackCalled = true;
|
|
53
|
+
callbackError = error;
|
|
54
|
+
if (useCallback) {
|
|
55
|
+
error ? reject(error) : resolve();
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
reject(error);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (isPromiseLike(result)) {
|
|
64
|
+
void result.then(resolve, reject);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
useCallback = true;
|
|
68
|
+
(0, deprecate_1.default)({
|
|
69
|
+
id: 'plugin-register-callback',
|
|
70
|
+
subject: 'Plugin register callbacks',
|
|
71
|
+
replacement: 'an async register(options, dependencies) function'
|
|
72
|
+
});
|
|
73
|
+
if (callbackCalled) {
|
|
74
|
+
callbackError ? reject(callbackError) : resolve();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
};
|
|
43
79
|
async function init(pluginsToRegister) {
|
|
44
80
|
const registered = {};
|
|
81
|
+
const deferredLoads = [];
|
|
45
82
|
validatePlugins(pluginsToRegister);
|
|
46
83
|
checkDependencies(pluginsToRegister);
|
|
47
84
|
const dependenciesRegistered = (dependencies) => {
|
|
@@ -68,9 +105,8 @@ async function init(pluginsToRegister) {
|
|
|
68
105
|
return;
|
|
69
106
|
}
|
|
70
107
|
const dependencies = Object.fromEntries(Object.entries(registered).filter(([key]) => plugin.register.dependencies?.includes(key)));
|
|
71
|
-
const register = (0, node_util_1.promisify)(plugin.register.register);
|
|
72
108
|
const pluginCallback = plugin.callback || (() => { });
|
|
73
|
-
await
|
|
109
|
+
await registerPlugin(plugin, dependencies).catch((err) => {
|
|
74
110
|
pluginCallback(err);
|
|
75
111
|
throw err;
|
|
76
112
|
});
|
|
@@ -84,7 +120,7 @@ async function init(pluginsToRegister) {
|
|
|
84
120
|
const terminator = async () => {
|
|
85
121
|
if (deferredLoads.length > 0) {
|
|
86
122
|
const deferredPlugins = [...deferredLoads];
|
|
87
|
-
deferredLoads =
|
|
123
|
+
deferredLoads.length = 0;
|
|
88
124
|
await Promise.all(deferredPlugins.map((plugin) => onSeries(() => loadPlugin(plugin))));
|
|
89
125
|
return terminator();
|
|
90
126
|
}
|
|
@@ -44,6 +44,7 @@ const oc_get_unix_utc_timestamp_1 = __importDefault(require("oc-get-unix-utc-tim
|
|
|
44
44
|
const oc_metadata_adapters_utils_1 = require("oc-metadata-adapters-utils");
|
|
45
45
|
const resources_1 = __importDefault(require("../../resources"));
|
|
46
46
|
const settings_1 = __importDefault(require("../../resources/settings"));
|
|
47
|
+
const bounded_cache_1 = __importDefault(require("../../utils/bounded-cache"));
|
|
47
48
|
const error_to_string_1 = __importDefault(require("../../utils/error-to-string"));
|
|
48
49
|
const components_cache_1 = __importDefault(require("./components-cache"));
|
|
49
50
|
const components_details_1 = __importDefault(require("./components-details"));
|
|
@@ -56,6 +57,16 @@ const storage_adapter_1 = __importDefault(require("./storage-adapter"));
|
|
|
56
57
|
const validator = __importStar(require("./validators"));
|
|
57
58
|
const versionHandler = __importStar(require("./version-handler"));
|
|
58
59
|
const packageInfo = fs_extra_1.default.readJsonSync(node_path_1.default.join(__dirname, '..', '..', '..', 'package.json'));
|
|
60
|
+
const freezeComponentInfo = (value) => {
|
|
61
|
+
if (value && typeof value === 'object') {
|
|
62
|
+
for (const nestedValue of Object.values(value)) {
|
|
63
|
+
freezeComponentInfo(nestedValue);
|
|
64
|
+
}
|
|
65
|
+
if (!Object.isFrozen(value))
|
|
66
|
+
Object.freeze(value);
|
|
67
|
+
}
|
|
68
|
+
return value;
|
|
69
|
+
};
|
|
59
70
|
function repository(conf) {
|
|
60
71
|
const cdn = !conf.local &&
|
|
61
72
|
(0, storage_adapter_1.default)(conf.storage.adapter(conf.storage.options));
|
|
@@ -71,9 +82,25 @@ function repository(conf) {
|
|
|
71
82
|
: undefined;
|
|
72
83
|
const componentsCache = (0, components_cache_1.default)(conf, cdn, metadataIndex);
|
|
73
84
|
const componentsDetails = (0, components_details_1.default)(conf, cdn, metadataIndex);
|
|
85
|
+
const componentInfoCache = new bounded_cache_1.default(1000);
|
|
86
|
+
const componentInfoLoads = new Map();
|
|
87
|
+
const localVersions = new Map();
|
|
88
|
+
const localVersionLoads = new Map();
|
|
74
89
|
let exportLegacyFilesLoop;
|
|
75
90
|
let closed = false;
|
|
76
91
|
const getFilePath = (component, version, filePath) => `${options.componentsDir}/${component}/${version}/${filePath}`;
|
|
92
|
+
const getComponentInfoKey = (componentName, componentVersion) => JSON.stringify([componentName, componentVersion]);
|
|
93
|
+
const invalidateComponentInfo = (componentName, componentVersion) => {
|
|
94
|
+
const key = getComponentInfoKey(componentName, componentVersion);
|
|
95
|
+
componentInfoCache.delete('component-info', key);
|
|
96
|
+
const entry = componentInfoLoads.get(key);
|
|
97
|
+
if (entry) {
|
|
98
|
+
entry.invalidated = true;
|
|
99
|
+
if (componentInfoLoads.get(key) === entry) {
|
|
100
|
+
componentInfoLoads.delete(key);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
};
|
|
77
104
|
const exportLegacyFiles = () => {
|
|
78
105
|
if (!metadataStore || !conf.metadata?.exportLegacyFiles) {
|
|
79
106
|
return;
|
|
@@ -149,21 +176,37 @@ function repository(conf) {
|
|
|
149
176
|
return components;
|
|
150
177
|
},
|
|
151
178
|
getComponentVersions(componentName) {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
fs_extra_1.default
|
|
155
|
-
.readJson(node_path_1.default.join(__dirname, '../../../package.json'))
|
|
156
|
-
.then((x) => x.version)
|
|
157
|
-
]);
|
|
158
|
-
}
|
|
159
|
-
if (!local.getComponents().includes(componentName)) {
|
|
179
|
+
const isOcClient = componentName === 'oc-client';
|
|
180
|
+
if (!isOcClient && !local.getComponents().includes(componentName)) {
|
|
160
181
|
return Promise.reject(resources_1.default.errors.registry.COMPONENT_NOT_FOUND(componentName, repositorySource));
|
|
161
182
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
183
|
+
const readVersion = () => fs_extra_1.default
|
|
184
|
+
.readJson(isOcClient
|
|
185
|
+
? node_path_1.default.join(__dirname, '../../../package.json')
|
|
186
|
+
: node_path_1.default.join(conf.path, `${componentName}/package.json`))
|
|
187
|
+
.then((componentInfo) => componentInfo.version);
|
|
188
|
+
if (conf.hotReloading !== false) {
|
|
189
|
+
return readVersion().then((version) => [version]);
|
|
190
|
+
}
|
|
191
|
+
const cachedVersion = localVersions.get(componentName);
|
|
192
|
+
if (cachedVersion !== undefined) {
|
|
193
|
+
return Promise.resolve([cachedVersion]);
|
|
194
|
+
}
|
|
195
|
+
const activeLoad = localVersionLoads.get(componentName);
|
|
196
|
+
if (activeLoad)
|
|
197
|
+
return activeLoad;
|
|
198
|
+
const load = readVersion()
|
|
199
|
+
.then((version) => {
|
|
200
|
+
localVersions.set(componentName, version);
|
|
201
|
+
return [version];
|
|
202
|
+
})
|
|
203
|
+
.finally(() => {
|
|
204
|
+
if (localVersionLoads.get(componentName) === load) {
|
|
205
|
+
localVersionLoads.delete(componentName);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
localVersionLoads.set(componentName, load);
|
|
209
|
+
return load;
|
|
167
210
|
},
|
|
168
211
|
getDataProvider(componentName) {
|
|
169
212
|
const ocClientServerPath = '../../components/oc-client/_package/server.js';
|
|
@@ -197,12 +240,48 @@ function repository(conf) {
|
|
|
197
240
|
if (!version) {
|
|
198
241
|
throw resources_1.default.errors.registry.COMPONENT_VERSION_NOT_FOUND(componentName, componentVersion || '', repositorySource);
|
|
199
242
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
243
|
+
let component;
|
|
244
|
+
if (conf.local && conf.hotReloading) {
|
|
245
|
+
component = await repository
|
|
246
|
+
.getComponentInfo(componentName, version)
|
|
247
|
+
.catch((err) => {
|
|
248
|
+
throw `component not available: ${(0, error_to_string_1.default)(err)}`;
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
const key = getComponentInfoKey(componentName, version);
|
|
253
|
+
const cached = componentInfoCache.get('component-info', key);
|
|
254
|
+
if (cached) {
|
|
255
|
+
component = cached;
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
let entry = componentInfoLoads.get(key);
|
|
259
|
+
if (!entry) {
|
|
260
|
+
let currentEntry;
|
|
261
|
+
const promise = Promise.resolve()
|
|
262
|
+
.then(() => repository.getComponentInfo(componentName, version))
|
|
263
|
+
.then((loadedComponent) => {
|
|
264
|
+
const frozenComponent = freezeComponentInfo(loadedComponent);
|
|
265
|
+
if (!currentEntry.invalidated) {
|
|
266
|
+
componentInfoCache.set('component-info', key, frozenComponent);
|
|
267
|
+
}
|
|
268
|
+
return frozenComponent;
|
|
269
|
+
})
|
|
270
|
+
.finally(() => {
|
|
271
|
+
if (componentInfoLoads.get(key) === currentEntry) {
|
|
272
|
+
componentInfoLoads.delete(key);
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
currentEntry = { invalidated: false, promise };
|
|
276
|
+
entry = currentEntry;
|
|
277
|
+
componentInfoLoads.set(key, currentEntry);
|
|
278
|
+
}
|
|
279
|
+
component = await entry.promise.catch((err) => {
|
|
280
|
+
throw `component not available: ${(0, error_to_string_1.default)(err)}`;
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return { ...component, allVersions: [...allVersions] };
|
|
206
285
|
},
|
|
207
286
|
getComponentInfo(componentName, componentVersion) {
|
|
208
287
|
if (conf.local) {
|
|
@@ -344,6 +423,7 @@ function repository(conf) {
|
|
|
344
423
|
try {
|
|
345
424
|
await cdn.putDir(pkgDetails.outputFolder, `${options.componentsDir}/${componentName}/${componentVersion}`);
|
|
346
425
|
await metadataStore.commitVersion(componentName, componentVersion, token);
|
|
426
|
+
invalidateComponentInfo(componentName, componentVersion);
|
|
347
427
|
metadataIndex.add(componentRow);
|
|
348
428
|
}
|
|
349
429
|
catch (err) {
|
|
@@ -355,6 +435,7 @@ function repository(conf) {
|
|
|
355
435
|
return;
|
|
356
436
|
}
|
|
357
437
|
await cdn.putDir(pkgDetails.outputFolder, `${options.componentsDir}/${componentName}/${componentVersion}`);
|
|
438
|
+
invalidateComponentInfo(componentName, componentVersion);
|
|
358
439
|
void componentsCache
|
|
359
440
|
.refresh()
|
|
360
441
|
.then((componentsList) => componentsDetails.refresh(componentsList))
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import type { Config } from '../../../types';
|
|
1
|
+
import type { Config, CorsOptions } from '../../../types';
|
|
2
2
|
type ValidationResult = {
|
|
3
3
|
isValid: true;
|
|
4
4
|
} | {
|
|
5
5
|
isValid: false;
|
|
6
6
|
message: string;
|
|
7
7
|
};
|
|
8
|
-
|
|
8
|
+
type RegistryConfiguration = Partial<Omit<Config, 'dataProvider' | 'discovery' | 'cors'>> & {
|
|
9
|
+
cors?: CorsOptions;
|
|
10
|
+
};
|
|
11
|
+
export default function registryConfiguration(conf: RegistryConfiguration): ValidationResult;
|
|
9
12
|
export {};
|
|
@@ -38,6 +38,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.default = registryConfiguration;
|
|
40
40
|
const resources_1 = __importDefault(require("../../../resources"));
|
|
41
|
+
const cors_1 = require("../../middleware/cors");
|
|
41
42
|
const auth = __importStar(require("../authentication"));
|
|
42
43
|
const metadata_adapter_options_1 = __importDefault(require("../metadata-adapter-options"));
|
|
43
44
|
function registryConfiguration(conf) {
|
|
@@ -59,6 +60,10 @@ function registryConfiguration(conf) {
|
|
|
59
60
|
return returnError(resources_1.default.errors.registry.CONFIGURATION_PREFIX_DOES_NOT_END_WITH_SLASH);
|
|
60
61
|
}
|
|
61
62
|
}
|
|
63
|
+
const corsError = (0, cors_1.validateCorsConfig)(conf.cors);
|
|
64
|
+
if (corsError) {
|
|
65
|
+
return returnError(corsError);
|
|
66
|
+
}
|
|
62
67
|
const publishAuth = conf.publishAuth;
|
|
63
68
|
if (publishAuth) {
|
|
64
69
|
const res = auth.validate(publishAuth);
|