oc 0.50.60 → 0.50.62

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.
Files changed (43) hide show
  1. package/.turbo/turbo-build.log +5 -5
  2. package/.turbo/turbo-lint.log +2 -2
  3. package/.turbo/turbo-test-silent.log +12 -11
  4. package/.turbo/turbo-test.log +1806 -1750
  5. package/CHANGELOG.md +13 -0
  6. package/README.md +16 -0
  7. package/dist/cli/domain/local.js +7 -4
  8. package/dist/components/oc-client/_package/package.json +4 -4
  9. package/dist/components/oc-client/_package/server.js +1 -1
  10. package/dist/components/oc-client/package.json +1 -1
  11. package/dist/index.d.ts +2 -2
  12. package/dist/registry/domain/events-handler.d.ts +2 -5
  13. package/dist/registry/domain/http-server/express-adapter.d.ts +2 -2
  14. package/dist/registry/domain/http-server/express-adapter.js +47 -2
  15. package/dist/registry/domain/http-server/types.d.ts +21 -7
  16. package/dist/registry/domain/options-sanitiser.d.ts +8 -2
  17. package/dist/registry/domain/options-sanitiser.js +2 -0
  18. package/dist/registry/domain/plugins-initialiser.js +41 -5
  19. package/dist/registry/domain/repository.js +100 -19
  20. package/dist/registry/domain/validators/registry-configuration.d.ts +5 -2
  21. package/dist/registry/domain/validators/registry-configuration.js +5 -0
  22. package/dist/registry/index.d.ts +11 -6
  23. package/dist/registry/index.js +107 -28
  24. package/dist/registry/middleware/cors.d.ts +4 -0
  25. package/dist/registry/middleware/cors.js +62 -4
  26. package/dist/registry/router.js +4 -2
  27. package/dist/registry/routes/component-info.js +3 -2
  28. package/dist/registry/routes/component-preview.js +3 -2
  29. package/dist/registry/routes/component.d.ts +2 -1
  30. package/dist/registry/routes/component.js +1 -2
  31. package/dist/registry/routes/components.d.ts +2 -1
  32. package/dist/registry/routes/components.js +1 -2
  33. package/dist/registry/routes/helpers/get-component.d.ts +2 -1
  34. package/dist/registry/routes/helpers/get-component.js +7 -8
  35. package/dist/registry/routes/index.js +11 -7
  36. package/dist/resources/index.d.ts +5 -1
  37. package/dist/resources/index.js +5 -1
  38. package/dist/types.d.ts +34 -5
  39. package/dist/utils/bounded-cache.d.ts +10 -0
  40. package/dist/utils/bounded-cache.js +31 -0
  41. package/js-library-optimization-playbook.md +1379 -0
  42. package/package.json +4 -3
  43. package/tsconfig.types.json +10 -0
@@ -38,6 +38,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.default = registry;
40
40
  const colors_1 = __importDefault(require("../utils/colors"));
41
+ const deprecate_1 = __importDefault(require("../utils/deprecate"));
41
42
  const app_start_1 = __importDefault(require("./app-start"));
42
43
  const events_handler_1 = __importDefault(require("./domain/events-handler"));
43
44
  const options_sanitiser_1 = __importDefault(require("./domain/options-sanitiser"));
@@ -47,6 +48,20 @@ const server_adapter_1 = __importDefault(require("./domain/server-adapter"));
47
48
  const validator = __importStar(require("./domain/validators"));
48
49
  const middleware = __importStar(require("./middleware"));
49
50
  const router_1 = require("./router");
51
+ const SERVER_ERROR_CODE = 'SERVER_ERROR';
52
+ const warnAboutCallback = () => (0, deprecate_1.default)({
53
+ id: 'registry-lifecycle-callbacks',
54
+ subject: 'Registry lifecycle callbacks',
55
+ replacement: 'the returned promises'
56
+ });
57
+ const toError = (error) => {
58
+ if (error instanceof Error) {
59
+ return error;
60
+ }
61
+ const errorLike = error;
62
+ const message = errorLike?.message ?? errorLike?.msg ?? error;
63
+ return new Error(String(message));
64
+ };
50
65
  function registry(inputOptions) {
51
66
  const validationResult = validator.validateRegistryConfiguration(inputOptions);
52
67
  if (!validationResult.isValid) {
@@ -57,56 +72,120 @@ function registry(inputOptions) {
57
72
  const adapter = middleware.bind((0, server_adapter_1.default)(options.server.adapter, options.server.options), options);
58
73
  const app = adapter.native();
59
74
  const repository = (0, repository_1.default)(options);
60
- const close = (callback) => {
61
- const closeMetadataStore = () => Promise.resolve(repository.close?.()).catch(() => undefined);
62
- if (adapter.isListening()) {
75
+ const listenAdapter = (serverOptions) => {
76
+ if (adapter.supportsPromiseLifecycle) {
77
+ return adapter.listen(serverOptions);
78
+ }
79
+ return new Promise((resolve, reject) => {
80
+ adapter.listen(serverOptions, (err) => {
81
+ if (err) {
82
+ reject(err);
83
+ }
84
+ else {
85
+ resolve();
86
+ }
87
+ });
88
+ });
89
+ };
90
+ const closeAdapter = () => {
91
+ if (adapter.supportsPromiseLifecycle) {
92
+ return adapter.close();
93
+ }
94
+ return new Promise((resolve, reject) => {
63
95
  adapter.close((err) => {
64
- void closeMetadataStore().finally(() => callback(err));
96
+ if (err) {
97
+ reject(err);
98
+ }
99
+ else {
100
+ resolve();
101
+ }
65
102
  });
66
- return;
103
+ });
104
+ };
105
+ const closePromise = () => {
106
+ const closeMetadataStore = () => Promise.resolve(repository.close?.()).catch(() => undefined);
107
+ const closeServer = new Promise((resolve, reject) => {
108
+ if (!adapter.isListening()) {
109
+ reject('not opened');
110
+ return;
111
+ }
112
+ closeAdapter().then(resolve, reject);
113
+ });
114
+ return closeServer.finally(closeMetadataStore);
115
+ };
116
+ const close = (callback) => {
117
+ const promise = closePromise();
118
+ if (!callback) {
119
+ return promise;
67
120
  }
68
- void closeMetadataStore().finally(() => callback('not opened'));
121
+ warnAboutCallback();
122
+ const callbackPromise = promise.then(() => callback(), (error) => {
123
+ callback(error);
124
+ throw error;
125
+ });
126
+ void callbackPromise.catch(() => undefined);
127
+ return callbackPromise;
69
128
  };
70
129
  const register = (plugin, callback) => {
130
+ if (callback) {
131
+ warnAboutCallback();
132
+ }
71
133
  plugins.push(Object.assign(plugin, { callback }));
134
+ return Promise.resolve();
72
135
  };
73
- const start = async (callback) => {
136
+ const startPromise = async () => {
74
137
  const ok = (msg) => console.log(colors_1.default.green(msg));
75
138
  try {
76
139
  options.plugins = await pluginsInitialiser.init(plugins);
77
140
  (0, router_1.create)(adapter, options, repository);
78
141
  const componentsInfo = await repository.init();
79
142
  await (0, app_start_1.default)(repository, options);
80
- adapter.listen({
143
+ const listenPromise = listenAdapter({
81
144
  port: options.port,
82
145
  timeout: options.timeout,
83
146
  keepAliveTimeout: options.keepAliveTimeout
84
- }, (err) => {
85
- if (err) {
86
- return callback(err);
87
- }
88
- events_handler_1.default.fire('start', {});
89
- if (options.verbosity) {
90
- ok(`Registry started at port http://localhost:${options.port}${options.prefix}`);
91
- if (componentsInfo) {
92
- const componentsNumber = Object.keys(componentsInfo.components).length;
93
- const componentsReleases = Object.values(componentsInfo.components).reduce((acc, component) => acc + Object.keys(component).length, 0);
94
- ok(`Registry serving ${componentsNumber} components for a total of ${componentsReleases} releases.`);
95
- }
96
- }
97
- callback(null, { app, server: adapter.httpServer() });
98
147
  });
99
- adapter.onServerError((error) => {
100
- events_handler_1.default.fire('error', {
101
- code: 'EXPRESS_ERROR',
102
- message: error?.message ?? String(error)
148
+ const serverError = new Promise((_resolve, reject) => {
149
+ adapter.onServerError((error) => {
150
+ events_handler_1.default.fire('error', {
151
+ code: SERVER_ERROR_CODE,
152
+ message: error?.message ?? String(error)
153
+ });
154
+ reject(toError(error));
103
155
  });
104
- callback(error);
105
156
  });
157
+ void serverError.catch(() => undefined);
158
+ await Promise.race([listenPromise, serverError]);
159
+ events_handler_1.default.fire('start', {});
160
+ if (options.verbosity) {
161
+ ok(`Registry started at port http://localhost:${options.port}${options.prefix}`);
162
+ if (componentsInfo) {
163
+ const componentsNumber = Object.keys(componentsInfo.components).length;
164
+ const componentsReleases = Object.values(componentsInfo.components).reduce((acc, component) => acc + Object.keys(component).length, 0);
165
+ ok(`Registry serving ${componentsNumber} components for a total of ${componentsReleases} releases.`);
166
+ }
167
+ }
168
+ return { app, server: adapter.httpServer() };
106
169
  }
107
170
  catch (err) {
108
- callback(err?.msg || err);
171
+ throw toError(err);
172
+ }
173
+ };
174
+ const start = (callback) => {
175
+ const promise = startPromise();
176
+ if (!callback) {
177
+ return promise;
109
178
  }
179
+ warnAboutCallback();
180
+ const callbackPromise = promise.then((result) => {
181
+ callback(null, result);
182
+ return result;
183
+ }, (error) => {
184
+ callback(error);
185
+ throw error;
186
+ });
187
+ void callbackPromise.catch(() => undefined);
188
+ return callbackPromise;
110
189
  };
111
190
  return {
112
191
  close,
@@ -1,3 +1,7 @@
1
+ import type { CorsConfig, CorsOptions } from '../../types';
1
2
  import type { OcHandler } from '../domain/http-server/types';
3
+ export declare const DEFAULT_CORS_CONFIG: CorsConfig;
4
+ export declare const normaliseCorsConfig: (options?: CorsOptions | null) => CorsConfig;
5
+ export declare const validateCorsConfig: (options: unknown) => string | undefined;
2
6
  declare const cors: OcHandler;
3
7
  export default cors;
@@ -1,10 +1,68 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.validateCorsConfig = exports.normaliseCorsConfig = exports.DEFAULT_CORS_CONFIG = void 0;
7
+ const resources_1 = __importDefault(require("../../resources"));
8
+ exports.DEFAULT_CORS_CONFIG = {
9
+ origin: '*',
10
+ credentials: true,
11
+ allowedHeaders: 'Origin, X-Requested-With, Content-Type, Accept, traceparent',
12
+ methods: 'GET, OPTIONS, PUT, POST'
13
+ };
14
+ const asHeaderValue = (value, fallback) => (Array.isArray(value) ? value.join(', ') : (value ?? fallback));
15
+ const normaliseCorsConfig = (options) => ({
16
+ origin: options?.origin ?? exports.DEFAULT_CORS_CONFIG.origin,
17
+ credentials: options?.credentials ?? exports.DEFAULT_CORS_CONFIG.credentials,
18
+ allowedHeaders: asHeaderValue(options?.allowedHeaders, exports.DEFAULT_CORS_CONFIG.allowedHeaders),
19
+ methods: asHeaderValue(options?.methods, exports.DEFAULT_CORS_CONFIG.methods)
20
+ });
21
+ exports.normaliseCorsConfig = normaliseCorsConfig;
22
+ const isStringArray = (value) => Array.isArray(value) && value.every((item) => typeof item === 'string');
23
+ const validateCorsConfig = (options) => {
24
+ if (typeof options === 'undefined') {
25
+ return undefined;
26
+ }
27
+ if (options === null ||
28
+ typeof options !== 'object' ||
29
+ Array.isArray(options)) {
30
+ return resources_1.default.errors.registry.CONFIGURATION_CORS_MUST_BE_OBJECT;
31
+ }
32
+ const config = options;
33
+ if (typeof config.origin !== 'undefined' &&
34
+ (typeof config.origin !== 'string' || config.origin.length === 0)) {
35
+ return resources_1.default.errors.registry.CONFIGURATION_CORS_ORIGIN_MUST_BE_STRING;
36
+ }
37
+ if (typeof config.credentials !== 'undefined' &&
38
+ typeof config.credentials !== 'boolean') {
39
+ return resources_1.default.errors.registry
40
+ .CONFIGURATION_CORS_CREDENTIALS_MUST_BE_BOOLEAN;
41
+ }
42
+ if (typeof config.allowedHeaders !== 'undefined' &&
43
+ typeof config.allowedHeaders !== 'string' &&
44
+ !isStringArray(config.allowedHeaders)) {
45
+ return resources_1.default.errors.registry
46
+ .CONFIGURATION_CORS_ALLOWED_HEADERS_MUST_BE_STRING_ARRAY;
47
+ }
48
+ if (typeof config.methods !== 'undefined' &&
49
+ typeof config.methods !== 'string' &&
50
+ !isStringArray(config.methods)) {
51
+ return resources_1.default.errors.registry
52
+ .CONFIGURATION_CORS_METHODS_MUST_BE_STRING_ARRAY;
53
+ }
54
+ return undefined;
55
+ };
56
+ exports.validateCorsConfig = validateCorsConfig;
3
57
  const cors = (_req, res) => {
58
+ const options = (0, exports.normaliseCorsConfig)(res.conf?.cors);
4
59
  res.removeHeader('X-Powered-By');
5
- res.set('Access-Control-Allow-Credentials', 'true');
6
- res.set('Access-Control-Allow-Origin', '*');
7
- res.set('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, traceparent');
8
- res.set('Access-Control-Allow-Methods', 'GET, OPTIONS, PUT, POST');
60
+ res.removeHeader('Access-Control-Allow-Credentials');
61
+ if (options.credentials) {
62
+ res.set('Access-Control-Allow-Credentials', 'true');
63
+ }
64
+ res.set('Access-Control-Allow-Origin', options.origin);
65
+ res.set('Access-Control-Allow-Headers', options.allowedHeaders);
66
+ res.set('Access-Control-Allow-Methods', options.methods);
9
67
  };
10
68
  exports.default = cors;
@@ -11,6 +11,7 @@ const component_info_1 = __importDefault(require("./routes/component-info"));
11
11
  const component_preview_1 = __importDefault(require("./routes/component-preview"));
12
12
  const components_1 = __importDefault(require("./routes/components"));
13
13
  const dependencies_1 = __importDefault(require("./routes/dependencies"));
14
+ const get_component_1 = __importDefault(require("./routes/helpers/get-component"));
14
15
  const history_1 = __importDefault(require("./routes/history"));
15
16
  const plugins_1 = __importDefault(require("./routes/plugins"));
16
17
  const publish_1 = __importDefault(require("./routes/publish"));
@@ -18,9 +19,10 @@ const static_redirector_1 = __importDefault(require("./routes/static-redirector"
18
19
  const validate_1 = __importDefault(require("./routes/validate"));
19
20
  function create(adapter, conf, repository) {
20
21
  const route = (method, path, id, ...handlers) => adapter.route(method, path, id, handlers);
22
+ const renderComponent = (0, get_component_1.default)(conf, repository);
21
23
  const routes = {
22
- component: (0, component_1.default)(conf, repository),
23
- components: (0, components_1.default)(conf, repository),
24
+ component: (0, component_1.default)(conf, repository, renderComponent),
25
+ components: (0, components_1.default)(conf, repository, renderComponent),
24
26
  componentInfo: (0, component_info_1.default)(conf, repository),
25
27
  componentPreview: (0, component_preview_1.default)(conf, repository),
26
28
  index: (0, routes_1.default)(repository),
@@ -100,9 +100,10 @@ function componentInfo(err, req, res, component, componentDetail) {
100
100
  });
101
101
  }
102
102
  else if (res.conf.discovery.api) {
103
- res.status(200).json(Object.assign(component, {
103
+ res.status(200).json({
104
+ ...component,
104
105
  requestVersion: req.params['componentVersion'] || ''
105
- }));
106
+ });
106
107
  }
107
108
  else {
108
109
  res.status(401);
@@ -71,9 +71,10 @@ function componentPreview(err, req, res, component, templates) {
71
71
  }));
72
72
  }
73
73
  else {
74
- res.status(200).json(Object.assign(component, {
74
+ res.status(200).json({
75
+ ...component,
75
76
  requestVersion: req.params['componentVersion'] || ''
76
- }));
77
+ });
77
78
  }
78
79
  }
79
80
  function componentPreviewRoute(conf, repository) {
@@ -1,4 +1,5 @@
1
1
  import type { Config } from '../../types';
2
2
  import type { OcHandler } from '../domain/http-server/types';
3
3
  import type { Repository } from '../domain/repository';
4
- export default function component(conf: Config, repository: Repository): OcHandler;
4
+ import { type RenderComponent } from './helpers/get-component';
5
+ export default function component(conf: Config, repository: Repository, getComponent?: RenderComponent): OcHandler;
@@ -42,8 +42,7 @@ const turbo_stream_1 = require("@rdevis/turbo-stream");
42
42
  const serialize_error_1 = require("serialize-error");
43
43
  const resources_1 = __importDefault(require("../../resources"));
44
44
  const get_component_1 = __importStar(require("./helpers/get-component"));
45
- function component(conf, repository) {
46
- const getComponent = (0, get_component_1.default)(conf, repository);
45
+ function component(conf, repository, getComponent = (0, get_component_1.default)(conf, repository)) {
47
46
  return (req, res) => {
48
47
  let parameters = req.query;
49
48
  if (req.method === 'POST') {
@@ -1,4 +1,5 @@
1
1
  import type { Config } from '../../types';
2
2
  import type { OcHandler } from '../domain/http-server/types';
3
3
  import type { Repository } from '../domain/repository';
4
- export default function components(conf: Config, repository: Repository): OcHandler;
4
+ import { type RenderComponent } from './helpers/get-component';
5
+ export default function components(conf: Config, repository: Repository, getComponent?: RenderComponent): OcHandler;
@@ -7,8 +7,7 @@ exports.default = components;
7
7
  const resources_1 = __importDefault(require("../../resources"));
8
8
  const pLimit_1 = __importDefault(require("../../utils/pLimit"));
9
9
  const get_component_1 = __importDefault(require("./helpers/get-component"));
10
- function components(conf, repository) {
11
- const getComponent = (0, get_component_1.default)(conf, repository);
10
+ function components(conf, repository, getComponent = (0, get_component_1.default)(conf, repository)) {
12
11
  const setHeaders = (results, res) => {
13
12
  if (results?.length !== 1 || !results[0] || !res.set) {
14
13
  return;
@@ -39,5 +39,6 @@ export interface GetComponentResult {
39
39
  missingDependencies?: string[];
40
40
  };
41
41
  }
42
+ export type RenderComponent = (options: RendererOptions, cb: (result: GetComponentResult) => void) => void;
42
43
  export declare const stream: unique symbol;
43
- export default function getComponent(conf: Config, repository: Repository): (options: RendererOptions, cb: (result: GetComponentResult) => void) => Promise<void>;
44
+ export default function getComponent(conf: Config, repository: Repository): RenderComponent;
@@ -42,12 +42,12 @@ const node_crypto_1 = require("node:crypto");
42
42
  const node_domain_1 = __importDefault(require("node:domain"));
43
43
  const node_vm_1 = __importDefault(require("node:vm"));
44
44
  const accept_language_parser_1 = __importDefault(require("accept-language-parser"));
45
- const nice_cache_1 = __importDefault(require("nice-cache"));
46
45
  const oc_client_1 = __importDefault(require("oc-client"));
47
46
  const oc_empty_response_handler_1 = __importDefault(require("oc-empty-response-handler"));
48
47
  const universalify_1 = require("universalify");
49
48
  const resources_1 = __importDefault(require("../../../resources"));
50
49
  const settings_1 = __importDefault(require("../../../resources/settings"));
50
+ const bounded_cache_1 = __importDefault(require("../../../utils/bounded-cache"));
51
51
  const is_template_legacy_1 = __importDefault(require("../../../utils/is-template-legacy"));
52
52
  const events_handler_1 = __importDefault(require("../../domain/events-handler"));
53
53
  const nested_renderer_1 = __importDefault(require("../../domain/nested-renderer"));
@@ -61,6 +61,7 @@ const format_error_stack_1 = require("./format-error-stack");
61
61
  const getComponentFallback = __importStar(require("./get-component-fallback"));
62
62
  const get_component_retrieving_info_1 = __importDefault(require("./get-component-retrieving-info"));
63
63
  exports.stream = Symbol('stream');
64
+ const MAX_ARTIFACT_CACHE_ENTRIES = 1000;
64
65
  const noop = () => { };
65
66
  const noopConsole = Object.fromEntries(Object.keys(console).map((key) => [key, noop]));
66
67
  const parseTemplatesHeader = (templates) => {
@@ -118,10 +119,7 @@ function pluginConverter(plugins = {}) {
118
119
  }
119
120
  function getComponent(conf, repository) {
120
121
  const client = (0, oc_client_1.default)({ templates: conf.templates });
121
- const cache = new nice_cache_1.default({
122
- verbose: !!conf.verbosity,
123
- refreshInterval: conf.refreshInterval
124
- });
122
+ const cache = new bounded_cache_1.default(MAX_ARTIFACT_CACHE_ENTRIES);
125
123
  const convertPlugins = pluginConverter(conf.plugins);
126
124
  const customHeadersByConfig = new WeakMap();
127
125
  const pluginNamesByConfig = new WeakMap();
@@ -156,7 +154,7 @@ function getComponent(conf, repository) {
156
154
  const getEnv = async (component) => {
157
155
  const cacheKey = `${component.name}/${component.version}/.env`;
158
156
  const cached = cache.get('file-contents', cacheKey);
159
- if (cached)
157
+ if (cached !== undefined)
160
158
  return cached;
161
159
  return singleFlight(cacheKey, async () => {
162
160
  const env = component.oc.files.env
@@ -437,7 +435,7 @@ function getComponent(conf, repository) {
437
435
  });
438
436
  });
439
437
  };
440
- if (cached && !conf.hotReloading) {
438
+ if (cached !== undefined && !conf.hotReloading) {
441
439
  returnResult(cached);
442
440
  }
443
441
  else {
@@ -538,7 +536,7 @@ function getComponent(conf, repository) {
538
536
  }, executionTimeout * 1000);
539
537
  }
540
538
  };
541
- if (cached && !conf.hotReloading) {
539
+ if (cached !== undefined && !conf.hotReloading) {
542
540
  domain.on('error', returnComponent);
543
541
  try {
544
542
  domain.run(() => {
@@ -589,6 +587,7 @@ function getComponent(conf, repository) {
589
587
  exports: {},
590
588
  console: conf.local ? console : noopConsole,
591
589
  setTimeout,
590
+ clearTimeout,
592
591
  Buffer,
593
592
  Error,
594
593
  AbortController: globalThis?.AbortController,
@@ -16,7 +16,16 @@ const getParsedAuthor = (author) => {
16
16
  author = author || {};
17
17
  return typeof author === 'string' ? (0, parse_author_1.default)(author) : author;
18
18
  };
19
- const mapComponentDetails = (component) => Object.assign(component, { author: getParsedAuthor(component.author) });
19
+ const mapComponentDetails = (component) => ({
20
+ ...component,
21
+ author: getParsedAuthor(component.author),
22
+ oc: component.oc.date
23
+ ? {
24
+ ...component.oc,
25
+ stringifiedDate: (0, date_stringify_1.default)(new Date(component.oc.date))
26
+ }
27
+ : component.oc
28
+ });
20
29
  const isHtmlRequest = (headers) => !!headers.accept && headers.accept.indexOf('text/html') >= 0;
21
30
  const excludedMeta = ['dependencies', 'devDependencies'];
22
31
  function default_1(repository) {
@@ -37,12 +46,7 @@ function default_1(repository) {
37
46
  };
38
47
  const componentDetails = await Promise.all(componentNames.map((componentName) => repository.getComponent(componentName, undefined)));
39
48
  if (isHtmlRequest(req.headers) && res.conf.discovery.ui) {
40
- const processedComponents = componentDetails.map((component) => {
41
- if (component.oc?.date) {
42
- component.oc.stringifiedDate = (0, date_stringify_1.default)(new Date(component.oc.date));
43
- }
44
- return mapComponentDetails(component);
45
- });
49
+ const processedComponents = componentDetails.map(mapComponentDetails);
46
50
  const totalReleases = componentDetails.reduce((sum, component) => sum + component.allVersions.length, 0);
47
51
  const stateCounts = {};
48
52
  const componentsList = processedComponents.map((component) => {
@@ -27,6 +27,11 @@ declare const _default: {
27
27
  COMPONENT_SET_HEADER_PARAMETERS_NOT_VALID: string;
28
28
  COMPONENT_SET_COOKIE_PARAMETERS_NOT_VALID: string;
29
29
  CONFIGURATION_DEPENDENCIES_MUST_BE_ARRAY: string;
30
+ CONFIGURATION_CORS_MUST_BE_OBJECT: string;
31
+ CONFIGURATION_CORS_ORIGIN_MUST_BE_STRING: string;
32
+ CONFIGURATION_CORS_CREDENTIALS_MUST_BE_BOOLEAN: string;
33
+ CONFIGURATION_CORS_ALLOWED_HEADERS_MUST_BE_STRING_ARRAY: string;
34
+ CONFIGURATION_CORS_METHODS_MUST_BE_STRING_ARRAY: string;
30
35
  CONFIGURATION_EMPTY: string;
31
36
  CONFIGURATION_METADATA_NOT_VALID: (adapterType: string) => string;
32
37
  CONFIGURATION_METADATA_EXPORT_INTERVAL_NOT_VALID: string;
@@ -108,7 +113,6 @@ declare const _default: {
108
113
  initSuccess: (componentName: string, componentPath: string) => string;
109
114
  installCompiler: (compiler: string) => string;
110
115
  installCompilerSuccess: (template: string, compiler: string, version: string) => string;
111
- legacyTemplateDeprecationWarning: (legacyType: string, newType: string) => string;
112
116
  CHANGES_DETECTED: (file: string) => string;
113
117
  CHECKING_DEPENDENCIES: string;
114
118
  COMPRESSING: (path: string) => string;
@@ -76,6 +76,11 @@ exports.default = {
76
76
  COMPONENT_SET_HEADER_PARAMETERS_NOT_VALID: 'context.setHeader parameters must be strings',
77
77
  COMPONENT_SET_COOKIE_PARAMETERS_NOT_VALID: 'context.setCookie parameters are not valid',
78
78
  CONFIGURATION_DEPENDENCIES_MUST_BE_ARRAY: 'Registry configuration is not valid: dependencies must be an array',
79
+ CONFIGURATION_CORS_MUST_BE_OBJECT: 'Registry configuration is not valid: cors must be an object',
80
+ CONFIGURATION_CORS_ORIGIN_MUST_BE_STRING: 'Registry configuration is not valid: cors.origin must be a non-empty string',
81
+ CONFIGURATION_CORS_CREDENTIALS_MUST_BE_BOOLEAN: 'Registry configuration is not valid: cors.credentials must be a boolean',
82
+ CONFIGURATION_CORS_ALLOWED_HEADERS_MUST_BE_STRING_ARRAY: 'Registry configuration is not valid: cors.allowedHeaders must be a string or an array of strings',
83
+ CONFIGURATION_CORS_METHODS_MUST_BE_STRING_ARRAY: 'Registry configuration is not valid: cors.methods must be a string or an array of strings',
79
84
  CONFIGURATION_EMPTY: 'Registry configuration is empty',
80
85
  CONFIGURATION_METADATA_NOT_VALID: (adapterType) => `Registry configuration is not valid: ${adapterType} is not a valid metadata adapter`,
81
86
  CONFIGURATION_METADATA_EXPORT_INTERVAL_NOT_VALID: 'Registry configuration is not valid: metadata.exportLegacyFilesInterval must be a positive number',
@@ -157,7 +162,6 @@ exports.default = {
157
162
  initSuccess,
158
163
  installCompiler: (compiler) => `Installing ${compiler} from npm...`,
159
164
  installCompilerSuccess: (template, compiler, version) => `${(0, colors_1.green)('✔')} Installed ${compiler} [${template} v${version}]`,
160
- legacyTemplateDeprecationWarning: (legacyType, newType) => `Template-type "${legacyType}" has been deprecated and is now replaced by "${newType}"`,
161
165
  CHANGES_DETECTED: (file) => `Changes detected on file: ${file}`,
162
166
  CHECKING_DEPENDENCIES: 'Ensuring dependencies are loaded...',
163
167
  COMPRESSING: (path) => `Compressing -> ${path}`,
package/dist/types.d.ts CHANGED
@@ -5,6 +5,27 @@ import type { PackageJson } from 'type-fest';
5
5
  import type { HttpServerAdapterFactory, HttpServerAdapterOptions } from './registry/domain/http-server/types';
6
6
  export type { ComponentRow, MetadataStore } from 'oc-metadata-adapters-utils';
7
7
  type Middleware = (req: Request, res: Response, next: NextFunction) => void;
8
+ export interface RegistryErrorEvent {
9
+ /**
10
+ * Stable registry error category. Server adapter failures use
11
+ * `SERVER_ERROR`, regardless of the configured adapter.
12
+ */
13
+ code: string;
14
+ /** The original error message reported by the failing registry operation. */
15
+ message: string;
16
+ }
17
+ export interface CorsConfig {
18
+ origin: string;
19
+ credentials: boolean;
20
+ allowedHeaders: string;
21
+ methods: string;
22
+ }
23
+ export interface CorsOptions {
24
+ origin?: string;
25
+ credentials?: boolean;
26
+ allowedHeaders?: string | string[];
27
+ methods?: string | string[];
28
+ }
8
29
  export interface Author {
9
30
  email?: string;
10
31
  name?: string;
@@ -192,6 +213,12 @@ export interface Config<T = any, TServerAdapter extends HttpServerAdapterFactory
192
213
  * @example "https://components.mycompany.com/"
193
214
  */
194
215
  baseUrl: string;
216
+ /**
217
+ * CORS response headers sent by the registry.
218
+ *
219
+ * @default Existing registry CORS headers
220
+ */
221
+ cors?: CorsConfig;
195
222
  /**
196
223
  * Pre-compiled version of the `oc-client` library generated automatically
197
224
  * at runtime when `compileClient` is enabled (default).
@@ -296,11 +323,12 @@ export interface Config<T = any, TServerAdapter extends HttpServerAdapterFactory
296
323
  secure: boolean;
297
324
  }) => boolean;
298
325
  /**
299
- * Environment variables passed to components in `context.env`.
326
+ * Environment values passed to components in `context.env` and the data
327
+ * provider's `process.env` shim.
300
328
  *
301
329
  * @default {}
302
330
  */
303
- env: Record<string, string>;
331
+ env: Record<string, any>;
304
332
  /**
305
333
  * Maximum execution time of a component’s server-side logic, expressed in
306
334
  * seconds. When the timeout elapses the registry returns a 500 error.
@@ -485,12 +513,13 @@ export interface Template {
485
513
  getInfo: () => TemplateInfo;
486
514
  render: (options: any, cb: (err: Error | null, data: string) => void) => void;
487
515
  }
516
+ export type PluginRegistration<T = any> = (options: T, dependencies: any, next: (error?: Error) => void) => void | Promise<void>;
488
517
  interface BasePLugin<T = any> {
489
518
  description?: string;
490
519
  name: string;
491
520
  options?: T;
492
521
  register: {
493
- register: (options: T, dependencies: any, next: (error?: Error) => void) => void;
522
+ register: PluginRegistration<T>;
494
523
  dependencies?: string[];
495
524
  };
496
525
  }
@@ -514,7 +543,7 @@ export type Plugin<T = any> = BasePLugin<T> & ({
514
543
  */
515
544
  context?: false | undefined;
516
545
  register: {
517
- register: (options: T, dependencies: any, next: (error?: Error) => void) => void;
546
+ register: PluginRegistration<T>;
518
547
  execute: (...args: any[]) => any;
519
548
  dependencies?: string[];
520
549
  };
@@ -533,7 +562,7 @@ export type Plugin<T = any> = BasePLugin<T> & ({
533
562
  */
534
563
  context: true;
535
564
  register: {
536
- register: (options: T, dependencies: any, next: (error?: Error) => void) => void;
565
+ register: PluginRegistration<T>;
537
566
  execute: (context: PluginContext) => (params: any) => any;
538
567
  dependencies?: string[];
539
568
  };
@@ -0,0 +1,10 @@
1
+ export default class BoundedCache {
2
+ private readonly cache;
3
+ constructor(maxEntries: number);
4
+ get<T>(namespace: string, key: string): T | undefined;
5
+ set<T>(namespace: string, key: string, value: T): void;
6
+ delete(namespace: string, key: string): boolean;
7
+ clear(): void;
8
+ get size(): number;
9
+ private toKey;
10
+ }
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const lru_cache_1 = require("lru-cache");
4
+ class BoundedCache {
5
+ cache;
6
+ constructor(maxEntries) {
7
+ if (!Number.isInteger(maxEntries) || maxEntries <= 0) {
8
+ throw new Error('Cache capacity must be a positive integer');
9
+ }
10
+ this.cache = new lru_cache_1.LRUCache({ max: maxEntries });
11
+ }
12
+ get(namespace, key) {
13
+ return this.cache.get(this.toKey(namespace, key));
14
+ }
15
+ set(namespace, key, value) {
16
+ this.cache.set(this.toKey(namespace, key), value);
17
+ }
18
+ delete(namespace, key) {
19
+ return this.cache.delete(this.toKey(namespace, key));
20
+ }
21
+ clear() {
22
+ this.cache.clear();
23
+ }
24
+ get size() {
25
+ return this.cache.size;
26
+ }
27
+ toKey(namespace, key) {
28
+ return JSON.stringify([namespace, key]);
29
+ }
30
+ }
31
+ exports.default = BoundedCache;