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.
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/metadata-migration.js +8 -1
  17. package/dist/registry/domain/options-sanitiser.d.ts +8 -2
  18. package/dist/registry/domain/options-sanitiser.js +2 -0
  19. package/dist/registry/domain/plugins-initialiser.js +41 -5
  20. package/dist/registry/domain/repository.js +100 -19
  21. package/dist/registry/domain/validators/registry-configuration.d.ts +5 -2
  22. package/dist/registry/domain/validators/registry-configuration.js +5 -0
  23. package/dist/registry/index.js +50 -29
  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 +31 -3
  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
@@ -48,6 +48,7 @@ const server_adapter_1 = __importDefault(require("./domain/server-adapter"));
48
48
  const validator = __importStar(require("./domain/validators"));
49
49
  const middleware = __importStar(require("./middleware"));
50
50
  const router_1 = require("./router");
51
+ const SERVER_ERROR_CODE = 'SERVER_ERROR';
51
52
  const warnAboutCallback = () => (0, deprecate_1.default)({
52
53
  id: 'registry-lifecycle-callbacks',
53
54
  subject: 'Registry lifecycle callbacks',
@@ -71,13 +72,26 @@ function registry(inputOptions) {
71
72
  const adapter = middleware.bind((0, server_adapter_1.default)(options.server.adapter, options.server.options), options);
72
73
  const app = adapter.native();
73
74
  const repository = (0, repository_1.default)(options);
74
- const closePromise = () => {
75
- const closeMetadataStore = () => Promise.resolve(repository.close?.()).catch(() => undefined);
76
- const closeServer = new Promise((resolve, reject) => {
77
- if (!adapter.isListening()) {
78
- reject('not opened');
79
- return;
80
- }
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) => {
81
95
  adapter.close((err) => {
82
96
  if (err) {
83
97
  reject(err);
@@ -87,6 +101,16 @@ function registry(inputOptions) {
87
101
  }
88
102
  });
89
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
+ });
90
114
  return closeServer.finally(closeMetadataStore);
91
115
  };
92
116
  const close = (callback) => {
@@ -116,35 +140,32 @@ function registry(inputOptions) {
116
140
  (0, router_1.create)(adapter, options, repository);
117
141
  const componentsInfo = await repository.init();
118
142
  await (0, app_start_1.default)(repository, options);
119
- return await new Promise((resolve, reject) => {
120
- adapter.listen({
121
- port: options.port,
122
- timeout: options.timeout,
123
- keepAliveTimeout: options.keepAliveTimeout
124
- }, (err) => {
125
- if (err) {
126
- reject(toError(err));
127
- return;
128
- }
129
- events_handler_1.default.fire('start', {});
130
- if (options.verbosity) {
131
- ok(`Registry started at port http://localhost:${options.port}${options.prefix}`);
132
- if (componentsInfo) {
133
- const componentsNumber = Object.keys(componentsInfo.components).length;
134
- const componentsReleases = Object.values(componentsInfo.components).reduce((acc, component) => acc + Object.keys(component).length, 0);
135
- ok(`Registry serving ${componentsNumber} components for a total of ${componentsReleases} releases.`);
136
- }
137
- }
138
- resolve({ app, server: adapter.httpServer() });
139
- });
143
+ const listenPromise = listenAdapter({
144
+ port: options.port,
145
+ timeout: options.timeout,
146
+ keepAliveTimeout: options.keepAliveTimeout
147
+ });
148
+ const serverError = new Promise((_resolve, reject) => {
140
149
  adapter.onServerError((error) => {
141
150
  events_handler_1.default.fire('error', {
142
- code: 'EXPRESS_ERROR',
151
+ code: SERVER_ERROR_CODE,
143
152
  message: error?.message ?? String(error)
144
153
  });
145
154
  reject(toError(error));
146
155
  });
147
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() };
148
169
  }
149
170
  catch (err) {
150
171
  throw toError(err);
@@ -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).
@@ -486,12 +513,13 @@ export interface Template {
486
513
  getInfo: () => TemplateInfo;
487
514
  render: (options: any, cb: (err: Error | null, data: string) => void) => void;
488
515
  }
516
+ export type PluginRegistration<T = any> = (options: T, dependencies: any, next: (error?: Error) => void) => void | Promise<void>;
489
517
  interface BasePLugin<T = any> {
490
518
  description?: string;
491
519
  name: string;
492
520
  options?: T;
493
521
  register: {
494
- register: (options: T, dependencies: any, next: (error?: Error) => void) => void;
522
+ register: PluginRegistration<T>;
495
523
  dependencies?: string[];
496
524
  };
497
525
  }
@@ -515,7 +543,7 @@ export type Plugin<T = any> = BasePLugin<T> & ({
515
543
  */
516
544
  context?: false | undefined;
517
545
  register: {
518
- register: (options: T, dependencies: any, next: (error?: Error) => void) => void;
546
+ register: PluginRegistration<T>;
519
547
  execute: (...args: any[]) => any;
520
548
  dependencies?: string[];
521
549
  };
@@ -534,7 +562,7 @@ export type Plugin<T = any> = BasePLugin<T> & ({
534
562
  */
535
563
  context: true;
536
564
  register: {
537
- register: (options: T, dependencies: any, next: (error?: Error) => void) => void;
565
+ register: PluginRegistration<T>;
538
566
  execute: (context: PluginContext) => (params: any) => any;
539
567
  dependencies?: string[];
540
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;