oc 0.50.55 → 0.50.57-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +16 -18
- package/.turbo/turbo-lint.log +2 -2
- package/.turbo/turbo-test-silent.log +28 -46
- package/.turbo/turbo-test.log +17 -1627
- package/CHANGELOG.md +14 -0
- package/README.md +180 -0
- package/dist/cli/commands.d.ts +8 -0
- package/dist/cli/commands.js +8 -0
- package/dist/cli/facade/dev.d.ts +2 -2
- package/dist/cli/facade/registry-migrate-metadata.d.ts +20 -0
- package/dist/cli/facade/registry-migrate-metadata.js +105 -0
- package/dist/cli/index.js +2 -0
- package/dist/components/oc-client/_package/package.json +1 -38
- package/dist/components/oc-client/_package/server.js +1 -1
- package/dist/components/oc-client/package.json +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/registry/domain/components-cache/index.d.ts +3 -1
- package/dist/registry/domain/components-cache/index.js +28 -5
- package/dist/registry/domain/components-details.d.ts +3 -1
- package/dist/registry/domain/components-details.js +26 -5
- package/dist/registry/domain/extract-package.d.ts +3 -2
- package/dist/registry/domain/http-server/express-adapter.d.ts +2 -0
- package/dist/registry/domain/http-server/express-adapter.js +196 -0
- package/dist/registry/domain/http-server/types.d.ts +99 -0
- package/dist/registry/domain/http-server/types.js +2 -0
- package/dist/registry/domain/metadata-adapter-options.d.ts +6 -0
- package/dist/registry/domain/metadata-adapter-options.js +12 -0
- package/dist/registry/domain/metadata-index.d.ts +15 -0
- package/dist/registry/domain/metadata-index.js +132 -0
- package/dist/registry/domain/metadata-migration.d.ts +38 -0
- package/dist/registry/domain/metadata-migration.js +128 -0
- package/dist/registry/domain/options-sanitiser.js +13 -0
- package/dist/registry/domain/repository.d.ts +1 -0
- package/dist/registry/domain/repository.js +106 -7
- package/dist/registry/domain/server-adapter.d.ts +4 -0
- package/dist/registry/domain/server-adapter.js +20 -0
- package/dist/registry/domain/validators/registry-configuration.js +21 -0
- package/dist/registry/domain/validators/uploaded-package.d.ts +5 -4
- package/dist/registry/index.d.ts +3 -3
- package/dist/registry/index.js +19 -17
- package/dist/registry/middleware/base-url-handler.d.ts +3 -2
- package/dist/registry/middleware/base-url-handler.js +3 -4
- package/dist/registry/middleware/compression.d.ts +2 -2
- package/dist/registry/middleware/compression.js +1 -1
- package/dist/registry/middleware/cors.d.ts +3 -2
- package/dist/registry/middleware/cors.js +7 -8
- package/dist/registry/middleware/discovery-handler.d.ts +3 -2
- package/dist/registry/middleware/discovery-handler.js +3 -4
- package/dist/registry/middleware/index.d.ts +2 -2
- package/dist/registry/middleware/index.js +39 -36
- package/dist/registry/router.d.ts +2 -2
- package/dist/registry/router.js +33 -27
- package/dist/registry/routes/component-info.d.ts +2 -2
- package/dist/registry/routes/component-info.js +1 -1
- package/dist/registry/routes/component-preview.d.ts +2 -2
- package/dist/registry/routes/component.d.ts +2 -2
- package/dist/registry/routes/component.js +3 -5
- package/dist/registry/routes/components.d.ts +2 -2
- package/dist/registry/routes/components.js +3 -1
- package/dist/registry/routes/dependencies.d.ts +2 -2
- package/dist/registry/routes/helpers/get-component-fallback.d.ts +3 -3
- package/dist/registry/routes/helpers/get-component.d.ts +1 -1
- package/dist/registry/routes/history.d.ts +2 -2
- package/dist/registry/routes/history.js +1 -1
- package/dist/registry/routes/index.d.ts +2 -2
- package/dist/registry/routes/plugins.d.ts +2 -2
- package/dist/registry/routes/publish.d.ts +2 -2
- package/dist/registry/routes/static-redirector.d.ts +3 -2
- package/dist/registry/routes/static-redirector.js +8 -12
- package/dist/registry/routes/validate.d.ts +2 -2
- package/dist/resources/index.d.ts +3 -0
- package/dist/resources/index.js +3 -0
- package/dist/types.d.ts +49 -1
- package/package.json +7 -6
- package/dist/registry/middleware/file-uploads.d.ts +0 -2
- package/dist/registry/middleware/file-uploads.js +0 -23
- package/dist/registry/middleware/request-handler.d.ts +0 -2
- package/dist/registry/middleware/request-handler.js +0 -30
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.default = createExpressAdapter;
|
|
7
|
+
const node_http_1 = __importDefault(require("node:http"));
|
|
8
|
+
const cookie_parser_1 = __importDefault(require("cookie-parser"));
|
|
9
|
+
const errorhandler_1 = __importDefault(require("errorhandler"));
|
|
10
|
+
const express_1 = __importDefault(require("express"));
|
|
11
|
+
const morgan_1 = __importDefault(require("morgan"));
|
|
12
|
+
const multer_1 = __importDefault(require("multer"));
|
|
13
|
+
const response_time_1 = __importDefault(require("response-time"));
|
|
14
|
+
const expressMiddleware = Symbol('expressMiddleware');
|
|
15
|
+
const ocResponseSym = Symbol('ocResponse');
|
|
16
|
+
const ocParamsSym = Symbol('ocParams');
|
|
17
|
+
function stream(readable) {
|
|
18
|
+
readable.pipe(this.raw);
|
|
19
|
+
}
|
|
20
|
+
function normaliseParams(raw) {
|
|
21
|
+
const params = { ...raw };
|
|
22
|
+
const splat = raw['splat'];
|
|
23
|
+
if (Array.isArray(splat)) {
|
|
24
|
+
params['splat'] = splat.join('/');
|
|
25
|
+
}
|
|
26
|
+
return params;
|
|
27
|
+
}
|
|
28
|
+
function createExpressAdapter(options) {
|
|
29
|
+
const adapterOptions = options;
|
|
30
|
+
return new ExpressHttpServerAdapter(typeof adapterOptions === 'object' ? adapterOptions.port : adapterOptions);
|
|
31
|
+
}
|
|
32
|
+
class ExpressHttpServerAdapter {
|
|
33
|
+
name = 'express';
|
|
34
|
+
app;
|
|
35
|
+
server;
|
|
36
|
+
serverErrorHandlers = [];
|
|
37
|
+
constructor(port) {
|
|
38
|
+
this.app = (0, express_1.default)();
|
|
39
|
+
if (typeof port !== 'undefined') {
|
|
40
|
+
this.app.set('port', port);
|
|
41
|
+
}
|
|
42
|
+
this.app.set('json spaces', 0);
|
|
43
|
+
this.app.set('etag', 'strong');
|
|
44
|
+
}
|
|
45
|
+
enableBodyParser(opts) {
|
|
46
|
+
this.app.use(express_1.default.json({
|
|
47
|
+
inflate: true,
|
|
48
|
+
limit: opts.limit
|
|
49
|
+
}));
|
|
50
|
+
this.app.use(express_1.default.urlencoded({
|
|
51
|
+
extended: true,
|
|
52
|
+
limit: opts.limit
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
enableCookies() {
|
|
56
|
+
this.app.use((0, cookie_parser_1.default)());
|
|
57
|
+
}
|
|
58
|
+
enableFileUploads(opts) {
|
|
59
|
+
const upload = (0, multer_1.default)({
|
|
60
|
+
limits: {
|
|
61
|
+
fieldSize: 10
|
|
62
|
+
},
|
|
63
|
+
storage: multer_1.default.diskStorage({
|
|
64
|
+
destination: opts.tempDir,
|
|
65
|
+
filename: (_req, file, cb) => cb(null, opts.filename(file.originalname))
|
|
66
|
+
})
|
|
67
|
+
});
|
|
68
|
+
this.app.use(upload.any());
|
|
69
|
+
}
|
|
70
|
+
enableRequestTiming(onDone) {
|
|
71
|
+
this.app.use((0, response_time_1.default)((req, res, time) => {
|
|
72
|
+
onDone(this.toOcRequest(req), this.toOcResponse(res), time);
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
enableLogging(opts) {
|
|
76
|
+
this.app.use((0, morgan_1.default)('dev', {
|
|
77
|
+
skip: (req, res) => opts.skip(this.toOcRequest(req), this.toOcResponse(res))
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
enableErrorHandler() {
|
|
81
|
+
this.app.use((0, errorhandler_1.default)());
|
|
82
|
+
}
|
|
83
|
+
use(handler) {
|
|
84
|
+
this.app.use(this.toExpressHandler(handler, true));
|
|
85
|
+
}
|
|
86
|
+
route(method, path, id, handlers) {
|
|
87
|
+
const setRouteId = (req, _res, next) => {
|
|
88
|
+
req.routeId = id;
|
|
89
|
+
next();
|
|
90
|
+
};
|
|
91
|
+
this.app[method](path, setRouteId, ...handlers.map((handler) => this.toExpressHandler(handler)));
|
|
92
|
+
}
|
|
93
|
+
fromConnect(handler) {
|
|
94
|
+
const wrapped = (req, res) => new Promise((resolve, reject) => {
|
|
95
|
+
handler(req.raw ?? req, res.raw ?? res, (err) => {
|
|
96
|
+
if (err) {
|
|
97
|
+
reject(err);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
resolve();
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
wrapped[expressMiddleware] = handler;
|
|
105
|
+
return wrapped;
|
|
106
|
+
}
|
|
107
|
+
listen(opts, cb) {
|
|
108
|
+
this.server = node_http_1.default.createServer(this.app);
|
|
109
|
+
this.server.timeout = opts.timeout;
|
|
110
|
+
if (opts.keepAliveTimeout) {
|
|
111
|
+
this.server.keepAliveTimeout = opts.keepAliveTimeout;
|
|
112
|
+
}
|
|
113
|
+
for (const handler of this.serverErrorHandlers) {
|
|
114
|
+
this.server.on('error', handler);
|
|
115
|
+
}
|
|
116
|
+
this.server.listen(opts.port, cb);
|
|
117
|
+
}
|
|
118
|
+
onServerError(cb) {
|
|
119
|
+
this.serverErrorHandlers.push(cb);
|
|
120
|
+
this.server?.on('error', cb);
|
|
121
|
+
}
|
|
122
|
+
close(cb) {
|
|
123
|
+
this.server?.close(cb);
|
|
124
|
+
}
|
|
125
|
+
isListening() {
|
|
126
|
+
return !!this.server?.listening;
|
|
127
|
+
}
|
|
128
|
+
native() {
|
|
129
|
+
return this.app;
|
|
130
|
+
}
|
|
131
|
+
httpServer() {
|
|
132
|
+
if (!this.server) {
|
|
133
|
+
throw new Error('HTTP server has not been started');
|
|
134
|
+
}
|
|
135
|
+
return this.server;
|
|
136
|
+
}
|
|
137
|
+
toExpressHandler(handler, continueWhenNotSent = false) {
|
|
138
|
+
const native = handler[expressMiddleware];
|
|
139
|
+
if (native) {
|
|
140
|
+
return native;
|
|
141
|
+
}
|
|
142
|
+
return (req, res, next) => {
|
|
143
|
+
let result;
|
|
144
|
+
try {
|
|
145
|
+
result = handler(this.toOcRequest(req), this.toOcResponse(res));
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
next(err);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (result && typeof result.then === 'function') {
|
|
152
|
+
result
|
|
153
|
+
.then(() => {
|
|
154
|
+
if (continueWhenNotSent && !res.headersSent) {
|
|
155
|
+
next();
|
|
156
|
+
}
|
|
157
|
+
})
|
|
158
|
+
.catch(next);
|
|
159
|
+
}
|
|
160
|
+
else if (continueWhenNotSent && !res.headersSent) {
|
|
161
|
+
next();
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
toOcRequest(req) {
|
|
166
|
+
const memo = req[ocParamsSym];
|
|
167
|
+
const current = (req.params ?? {});
|
|
168
|
+
if (memo && memo.source === current) {
|
|
169
|
+
return req;
|
|
170
|
+
}
|
|
171
|
+
if (!memo) {
|
|
172
|
+
Object.assign(req, {
|
|
173
|
+
raw: req,
|
|
174
|
+
routeId: req.routeId ?? ''
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
const normalized = normaliseParams(current);
|
|
178
|
+
Object.assign(req, { params: normalized });
|
|
179
|
+
req[ocParamsSym] = {
|
|
180
|
+
source: normalized
|
|
181
|
+
};
|
|
182
|
+
return req;
|
|
183
|
+
}
|
|
184
|
+
toOcResponse(res) {
|
|
185
|
+
const cached = res[ocResponseSym];
|
|
186
|
+
if (cached) {
|
|
187
|
+
return cached;
|
|
188
|
+
}
|
|
189
|
+
const ocRes = Object.assign(res, {
|
|
190
|
+
raw: res,
|
|
191
|
+
stream
|
|
192
|
+
});
|
|
193
|
+
res[ocResponseSym] = ocRes;
|
|
194
|
+
return ocRes;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type http from 'node:http';
|
|
2
|
+
import type { IncomingHttpHeaders } from 'node:http';
|
|
3
|
+
import type { Config } from '../../../types';
|
|
4
|
+
export interface CookieOptions {
|
|
5
|
+
domain?: string;
|
|
6
|
+
encode?: (value: string) => string;
|
|
7
|
+
expires?: Date;
|
|
8
|
+
httpOnly?: boolean;
|
|
9
|
+
maxAge?: number;
|
|
10
|
+
partitioned?: boolean;
|
|
11
|
+
path?: string;
|
|
12
|
+
priority?: 'low' | 'medium' | 'high';
|
|
13
|
+
sameSite?: boolean | 'lax' | 'strict' | 'none';
|
|
14
|
+
secure?: boolean;
|
|
15
|
+
signed?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export type Method = 'get' | 'post' | 'put' | 'patch' | 'head' | 'delete';
|
|
18
|
+
export interface UploadedFile {
|
|
19
|
+
fieldname: string;
|
|
20
|
+
originalname: string;
|
|
21
|
+
encoding: string;
|
|
22
|
+
mimetype: string;
|
|
23
|
+
size: number;
|
|
24
|
+
destination: string;
|
|
25
|
+
filename: string;
|
|
26
|
+
path: string;
|
|
27
|
+
buffer?: Buffer;
|
|
28
|
+
stream?: NodeJS.ReadableStream;
|
|
29
|
+
truncated?: boolean;
|
|
30
|
+
}
|
|
31
|
+
export interface OcRequest {
|
|
32
|
+
method: string;
|
|
33
|
+
url: string;
|
|
34
|
+
path: string;
|
|
35
|
+
originalUrl: string;
|
|
36
|
+
protocol: string;
|
|
37
|
+
secure: boolean;
|
|
38
|
+
ip: string;
|
|
39
|
+
headers: IncomingHttpHeaders;
|
|
40
|
+
params: Record<string, string>;
|
|
41
|
+
query: Record<string, unknown>;
|
|
42
|
+
body: any;
|
|
43
|
+
cookies: Record<string, string>;
|
|
44
|
+
files?: UploadedFile[] | Record<string, UploadedFile[]>;
|
|
45
|
+
user?: string;
|
|
46
|
+
routeId: string;
|
|
47
|
+
get(header: string): string | undefined;
|
|
48
|
+
raw: http.IncomingMessage;
|
|
49
|
+
}
|
|
50
|
+
export interface OcResponse {
|
|
51
|
+
conf: Config;
|
|
52
|
+
errorCode?: string;
|
|
53
|
+
errorDetails?: string;
|
|
54
|
+
statusCode: number;
|
|
55
|
+
status(code: number): this;
|
|
56
|
+
json(body: unknown): void;
|
|
57
|
+
send(body: unknown): void;
|
|
58
|
+
set(field: string | Record<string, string>, value?: string): this;
|
|
59
|
+
header(name: string, value: string): this;
|
|
60
|
+
setHeader(name: string, value: string): this;
|
|
61
|
+
removeHeader(name: string): this;
|
|
62
|
+
type(mime: string): this;
|
|
63
|
+
cookie(name: string, value: string, opts?: CookieOptions): this;
|
|
64
|
+
redirect(url: string): void;
|
|
65
|
+
end(chunk?: unknown): void;
|
|
66
|
+
stream(readable: NodeJS.ReadableStream): void;
|
|
67
|
+
raw: http.ServerResponse;
|
|
68
|
+
}
|
|
69
|
+
export type OcHandler = (req: OcRequest, res: OcResponse) => void | Promise<void>;
|
|
70
|
+
export type ExpressMiddleware = (req: any, res: any, next: (err?: unknown) => void) => void;
|
|
71
|
+
export interface HttpServerAdapter {
|
|
72
|
+
name: string;
|
|
73
|
+
enableBodyParser(opts: {
|
|
74
|
+
limit?: number | string;
|
|
75
|
+
}): void;
|
|
76
|
+
enableCookies(): void;
|
|
77
|
+
enableFileUploads(opts: {
|
|
78
|
+
tempDir: string;
|
|
79
|
+
filename: (originalName: string) => string;
|
|
80
|
+
}): void;
|
|
81
|
+
enableRequestTiming(onDone: (req: OcRequest, res: OcResponse, ms: number) => void): void;
|
|
82
|
+
enableLogging(opts: {
|
|
83
|
+
skip: (req: OcRequest, res: OcResponse) => boolean;
|
|
84
|
+
}): void;
|
|
85
|
+
enableErrorHandler(): void;
|
|
86
|
+
use(handler: OcHandler): void;
|
|
87
|
+
route(method: Method, path: string, id: string, handlers: OcHandler[]): void;
|
|
88
|
+
fromConnect(handler: ExpressMiddleware): OcHandler;
|
|
89
|
+
listen(opts: {
|
|
90
|
+
port: number | string;
|
|
91
|
+
timeout: number;
|
|
92
|
+
keepAliveTimeout?: number;
|
|
93
|
+
}, cb: (err?: Error) => void): void;
|
|
94
|
+
onServerError(cb: (err: Error) => void): void;
|
|
95
|
+
close(cb: (err?: Error) => void): void;
|
|
96
|
+
isListening(): boolean;
|
|
97
|
+
native(): unknown;
|
|
98
|
+
httpServer(): http.Server;
|
|
99
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { MetadataConfig } from '../../types';
|
|
2
|
+
type MetadataOptionsConfig = {
|
|
3
|
+
metadata?: Pick<MetadataConfig, 'manageSchema' | 'options'>;
|
|
4
|
+
};
|
|
5
|
+
declare const getMetadataAdapterOptions: (conf: MetadataOptionsConfig) => any;
|
|
6
|
+
export default getMetadataAdapterOptions;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const getMetadataAdapterOptions = (conf) => {
|
|
4
|
+
if (typeof conf.metadata?.manageSchema === 'undefined') {
|
|
5
|
+
return conf.metadata?.options;
|
|
6
|
+
}
|
|
7
|
+
return {
|
|
8
|
+
...(conf.metadata.options || {}),
|
|
9
|
+
manageSchema: conf.metadata.manageSchema
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
exports.default = getMetadataAdapterOptions;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Component, ComponentRow, ComponentsDetails, ComponentsList, MetadataStore } from '../../types';
|
|
2
|
+
export declare const getComponentsListFromRows: (rows: ComponentRow[]) => ComponentsList;
|
|
3
|
+
export declare const getComponentsDetailsFromRows: (rows: ComponentRow[]) => ComponentsDetails;
|
|
4
|
+
export declare const getComponentRow: (name: string, version: string, component: Component) => ComponentRow;
|
|
5
|
+
export interface MetadataSnapshot {
|
|
6
|
+
componentsList: ComponentsList;
|
|
7
|
+
componentsDetails: ComponentsDetails;
|
|
8
|
+
}
|
|
9
|
+
export interface MetadataIndex {
|
|
10
|
+
get(): MetadataSnapshot | undefined;
|
|
11
|
+
add(row: ComponentRow): MetadataSnapshot;
|
|
12
|
+
refresh(): Promise<MetadataSnapshot>;
|
|
13
|
+
getOrRefresh(): Promise<MetadataSnapshot>;
|
|
14
|
+
}
|
|
15
|
+
export declare const createMetadataIndex: (metadataStore: MetadataStore) => MetadataIndex;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createMetadataIndex = exports.getComponentRow = exports.getComponentsDetailsFromRows = exports.getComponentsListFromRows = void 0;
|
|
7
|
+
const semver_1 = __importDefault(require("semver"));
|
|
8
|
+
// Derive `lastEdit` from the data itself (the most recent publish) rather than
|
|
9
|
+
// the wall-clock time the snapshot happened to be built. This keeps the value
|
|
10
|
+
// semantically meaningful (it only advances when something is actually
|
|
11
|
+
// published) so the poll's `data.lastEdit > cached.lastEdit` guard works and
|
|
12
|
+
// the exported legacy `components.json` reflects the real last edit.
|
|
13
|
+
const getLastEditFromRows = (rows) => rows.reduce((max, row) => (row.publishDate > max ? row.publishDate : max), 0);
|
|
14
|
+
const getComponentsListFromRows = (rows) => {
|
|
15
|
+
const components = {};
|
|
16
|
+
for (const row of rows) {
|
|
17
|
+
const versions = components[row.name] || [];
|
|
18
|
+
versions.push(row.version);
|
|
19
|
+
components[row.name] = versions;
|
|
20
|
+
}
|
|
21
|
+
for (const versions of Object.values(components)) {
|
|
22
|
+
versions.sort(semver_1.default.compare);
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
lastEdit: getLastEditFromRows(rows),
|
|
26
|
+
components
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
exports.getComponentsListFromRows = getComponentsListFromRows;
|
|
30
|
+
const getComponentsDetailsFromRows = (rows) => {
|
|
31
|
+
const components = {};
|
|
32
|
+
for (const row of rows) {
|
|
33
|
+
const componentDetails = components[row.name] || {};
|
|
34
|
+
componentDetails[row.version] = { publishDate: row.publishDate };
|
|
35
|
+
if (row.templateSize !== undefined) {
|
|
36
|
+
componentDetails[row.version].templateSize = row.templateSize;
|
|
37
|
+
}
|
|
38
|
+
components[row.name] = componentDetails;
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
lastEdit: getLastEditFromRows(rows),
|
|
42
|
+
components
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
exports.getComponentsDetailsFromRows = getComponentsDetailsFromRows;
|
|
46
|
+
const getComponentRow = (name, version, component) => ({
|
|
47
|
+
name,
|
|
48
|
+
version,
|
|
49
|
+
publishDate: component.oc.date || 0,
|
|
50
|
+
templateSize: component.oc.files.template.size
|
|
51
|
+
});
|
|
52
|
+
exports.getComponentRow = getComponentRow;
|
|
53
|
+
const FORCED_REFRESH_INTERVAL_MS = 5 * 60 * 1000;
|
|
54
|
+
const buildSnapshot = (rows) => ({
|
|
55
|
+
componentsList: (0, exports.getComponentsListFromRows)(rows),
|
|
56
|
+
componentsDetails: (0, exports.getComponentsDetailsFromRows)(rows)
|
|
57
|
+
});
|
|
58
|
+
const createMetadataIndex = (metadataStore) => {
|
|
59
|
+
let snapshot;
|
|
60
|
+
let lastChangeToken;
|
|
61
|
+
let lastForcedRefreshAt = 0;
|
|
62
|
+
let localAddGeneration = 0;
|
|
63
|
+
const refresh = async () => {
|
|
64
|
+
const refreshGeneration = localAddGeneration;
|
|
65
|
+
const changeToken = metadataStore.getChangeToken
|
|
66
|
+
? await metadataStore.getChangeToken()
|
|
67
|
+
: undefined;
|
|
68
|
+
const now = Date.now();
|
|
69
|
+
if (snapshot &&
|
|
70
|
+
changeToken !== undefined &&
|
|
71
|
+
changeToken === lastChangeToken &&
|
|
72
|
+
now - lastForcedRefreshAt < FORCED_REFRESH_INTERVAL_MS) {
|
|
73
|
+
return snapshot;
|
|
74
|
+
}
|
|
75
|
+
const rows = await metadataStore.getAllComponents();
|
|
76
|
+
if (snapshot && refreshGeneration !== localAddGeneration) {
|
|
77
|
+
return snapshot;
|
|
78
|
+
}
|
|
79
|
+
snapshot = buildSnapshot(rows);
|
|
80
|
+
lastChangeToken = changeToken;
|
|
81
|
+
lastForcedRefreshAt = now;
|
|
82
|
+
return snapshot;
|
|
83
|
+
};
|
|
84
|
+
const add = (row) => {
|
|
85
|
+
// No snapshot yet: build one from the single row.
|
|
86
|
+
if (!snapshot) {
|
|
87
|
+
snapshot = buildSnapshot([row]);
|
|
88
|
+
localAddGeneration += 1;
|
|
89
|
+
return snapshot;
|
|
90
|
+
}
|
|
91
|
+
const { name, version } = row;
|
|
92
|
+
const prevList = snapshot.componentsList.components;
|
|
93
|
+
const prevDetails = snapshot.componentsDetails.components;
|
|
94
|
+
// Already present: keep the snapshot unchanged (mirrors the unique-constraint
|
|
95
|
+
// guarantee, so a duplicate publish doesn't disturb the cache).
|
|
96
|
+
if (prevDetails[name]?.[version]) {
|
|
97
|
+
return snapshot;
|
|
98
|
+
}
|
|
99
|
+
// Only the published component is rebuilt; every other component entry is
|
|
100
|
+
// shared by reference, so the cost is O(versions-of-this-component) rather
|
|
101
|
+
// than O(registry). New container objects are produced (rather than mutating
|
|
102
|
+
// in place) so in-flight readers keep a consistent view of the old snapshot.
|
|
103
|
+
const versions = [...(prevList[name] ?? []), version].sort(semver_1.default.compare);
|
|
104
|
+
const detail = {
|
|
105
|
+
...(prevDetails[name] ?? {}),
|
|
106
|
+
[version]: { publishDate: row.publishDate }
|
|
107
|
+
};
|
|
108
|
+
if (row.templateSize !== undefined) {
|
|
109
|
+
detail[version].templateSize = row.templateSize;
|
|
110
|
+
}
|
|
111
|
+
const lastEdit = Math.max(snapshot.componentsList.lastEdit, row.publishDate);
|
|
112
|
+
snapshot = {
|
|
113
|
+
componentsList: {
|
|
114
|
+
lastEdit,
|
|
115
|
+
components: { ...prevList, [name]: versions }
|
|
116
|
+
},
|
|
117
|
+
componentsDetails: {
|
|
118
|
+
lastEdit,
|
|
119
|
+
components: { ...prevDetails, [name]: detail }
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
localAddGeneration += 1;
|
|
123
|
+
return snapshot;
|
|
124
|
+
};
|
|
125
|
+
return {
|
|
126
|
+
get: () => snapshot,
|
|
127
|
+
add,
|
|
128
|
+
refresh,
|
|
129
|
+
getOrRefresh: () => (snapshot ? Promise.resolve(snapshot) : refresh())
|
|
130
|
+
};
|
|
131
|
+
};
|
|
132
|
+
exports.createMetadataIndex = createMetadataIndex;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { StorageAdapter } from 'oc-storage-adapters-utils';
|
|
2
|
+
import type { ComponentRow, ComponentsDetails, MetadataStore } from '../../types';
|
|
3
|
+
export interface MetadataMigrationResult {
|
|
4
|
+
scanned: number;
|
|
5
|
+
inserted: number;
|
|
6
|
+
skipped: number;
|
|
7
|
+
}
|
|
8
|
+
export interface MetadataExportResult {
|
|
9
|
+
exported: number;
|
|
10
|
+
files: string[];
|
|
11
|
+
}
|
|
12
|
+
export declare const getComponentRowsFromComponentsDetails: (componentsDetails: ComponentsDetails) => ComponentRow[];
|
|
13
|
+
export declare const backfillMetadataRows: (metadataStore: MetadataStore, rows: ComponentRow[]) => Promise<MetadataMigrationResult>;
|
|
14
|
+
export declare const backfillMetadataFromComponentsDetails: (metadataStore: MetadataStore, componentsDetails: ComponentsDetails) => Promise<MetadataMigrationResult>;
|
|
15
|
+
export declare const getComponentRowsFromStorageDirectories: (options: {
|
|
16
|
+
cdn: StorageAdapter;
|
|
17
|
+
componentsDir: string;
|
|
18
|
+
}) => Promise<ComponentRow[]>;
|
|
19
|
+
export declare const backfillMetadataFromStorageDirectories: (options: {
|
|
20
|
+
metadataStore: MetadataStore;
|
|
21
|
+
cdn: StorageAdapter;
|
|
22
|
+
componentsDir: string;
|
|
23
|
+
}) => Promise<MetadataMigrationResult>;
|
|
24
|
+
export declare const reconcileMetadataFromStorage: (options: {
|
|
25
|
+
metadataStore: MetadataStore;
|
|
26
|
+
cdn: StorageAdapter;
|
|
27
|
+
componentsDir: string;
|
|
28
|
+
}) => Promise<MetadataMigrationResult>;
|
|
29
|
+
export declare const exportLegacyMetadataFiles: (options: {
|
|
30
|
+
metadataStore: MetadataStore;
|
|
31
|
+
cdn: StorageAdapter;
|
|
32
|
+
componentsDir: string;
|
|
33
|
+
}) => Promise<MetadataExportResult>;
|
|
34
|
+
export declare const backfillMetadataFromStorageDetails: (options: {
|
|
35
|
+
metadataStore: MetadataStore;
|
|
36
|
+
cdn: StorageAdapter;
|
|
37
|
+
componentsDir: string;
|
|
38
|
+
}) => Promise<MetadataMigrationResult>;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.backfillMetadataFromStorageDetails = exports.exportLegacyMetadataFiles = exports.reconcileMetadataFromStorage = exports.backfillMetadataFromStorageDirectories = exports.getComponentRowsFromStorageDirectories = exports.backfillMetadataFromComponentsDetails = exports.backfillMetadataRows = exports.getComponentRowsFromComponentsDetails = void 0;
|
|
7
|
+
const oc_metadata_adapters_utils_1 = require("oc-metadata-adapters-utils");
|
|
8
|
+
const pLimit_1 = __importDefault(require("../../utils/pLimit"));
|
|
9
|
+
const metadata_index_1 = require("./metadata-index");
|
|
10
|
+
const isNotFoundError = (err, code) => err === code || err?.code === code;
|
|
11
|
+
const getTemplateSize = (component) => component.oc.files.template.size;
|
|
12
|
+
const getComponentRowsFromComponentsDetails = (componentsDetails) => {
|
|
13
|
+
const rows = [];
|
|
14
|
+
for (const [name, versions] of Object.entries(componentsDetails.components || {})) {
|
|
15
|
+
for (const [version, details] of Object.entries(versions)) {
|
|
16
|
+
const row = {
|
|
17
|
+
name,
|
|
18
|
+
version,
|
|
19
|
+
publishDate: details.publishDate
|
|
20
|
+
};
|
|
21
|
+
if (details.templateSize !== undefined) {
|
|
22
|
+
row.templateSize = details.templateSize;
|
|
23
|
+
}
|
|
24
|
+
rows.push(row);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return rows;
|
|
28
|
+
};
|
|
29
|
+
exports.getComponentRowsFromComponentsDetails = getComponentRowsFromComponentsDetails;
|
|
30
|
+
const backfillMetadataRows = async (metadataStore, rows) => {
|
|
31
|
+
const result = {
|
|
32
|
+
scanned: rows.length,
|
|
33
|
+
inserted: 0,
|
|
34
|
+
skipped: 0
|
|
35
|
+
};
|
|
36
|
+
const limit = (0, pLimit_1.default)(10);
|
|
37
|
+
await Promise.all(rows.map((row) => limit(async () => {
|
|
38
|
+
try {
|
|
39
|
+
await metadataStore.addVersion(row);
|
|
40
|
+
result.inserted += 1;
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
if (err?.code === oc_metadata_adapters_utils_1.VERSION_ALREADY_EXISTS ||
|
|
44
|
+
err?.code === oc_metadata_adapters_utils_1.VERSION_PUBLISH_IN_PROGRESS) {
|
|
45
|
+
result.skipped += 1;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
throw err;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
})));
|
|
52
|
+
return result;
|
|
53
|
+
};
|
|
54
|
+
exports.backfillMetadataRows = backfillMetadataRows;
|
|
55
|
+
const backfillMetadataFromComponentsDetails = async (metadataStore, componentsDetails) => (0, exports.backfillMetadataRows)(metadataStore, (0, exports.getComponentRowsFromComponentsDetails)(componentsDetails));
|
|
56
|
+
exports.backfillMetadataFromComponentsDetails = backfillMetadataFromComponentsDetails;
|
|
57
|
+
const getComponentRowsFromStorageDirectories = async (options) => {
|
|
58
|
+
let components;
|
|
59
|
+
try {
|
|
60
|
+
components = await options.cdn.listSubDirectories(options.componentsDir);
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
if (isNotFoundError(err, 'dir_not_found')) {
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
throw err;
|
|
67
|
+
}
|
|
68
|
+
const maxConcurrentRequests = options.cdn.maxConcurrentRequests || 10;
|
|
69
|
+
const componentLimit = (0, pLimit_1.default)(maxConcurrentRequests);
|
|
70
|
+
const packageLimit = (0, pLimit_1.default)(maxConcurrentRequests);
|
|
71
|
+
const rowsByComponent = await Promise.all(components.map((name) => componentLimit(async () => {
|
|
72
|
+
const versions = await options.cdn.listSubDirectories(`${options.componentsDir}/${name}`);
|
|
73
|
+
const rows = await Promise.all(versions.map((version) => packageLimit(async () => {
|
|
74
|
+
try {
|
|
75
|
+
const component = await options.cdn.getJson(`${options.componentsDir}/${name}/${version}/package.json`, true);
|
|
76
|
+
const row = {
|
|
77
|
+
name,
|
|
78
|
+
version,
|
|
79
|
+
publishDate: component.oc.date || 0
|
|
80
|
+
};
|
|
81
|
+
const templateSize = getTemplateSize(component);
|
|
82
|
+
if (templateSize !== undefined) {
|
|
83
|
+
row.templateSize = templateSize;
|
|
84
|
+
}
|
|
85
|
+
return row;
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
if (isNotFoundError(err, 'file_not_found')) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
throw err;
|
|
92
|
+
}
|
|
93
|
+
})));
|
|
94
|
+
return rows.filter((row) => row !== null);
|
|
95
|
+
})));
|
|
96
|
+
return rowsByComponent.flat();
|
|
97
|
+
};
|
|
98
|
+
exports.getComponentRowsFromStorageDirectories = getComponentRowsFromStorageDirectories;
|
|
99
|
+
const backfillMetadataFromStorageDirectories = async (options) => (0, exports.backfillMetadataRows)(options.metadataStore, await (0, exports.getComponentRowsFromStorageDirectories)(options));
|
|
100
|
+
exports.backfillMetadataFromStorageDirectories = backfillMetadataFromStorageDirectories;
|
|
101
|
+
exports.reconcileMetadataFromStorage = exports.backfillMetadataFromStorageDirectories;
|
|
102
|
+
const exportLegacyMetadataFiles = async (options) => {
|
|
103
|
+
const rows = await options.metadataStore.getAllComponents();
|
|
104
|
+
const componentsListPath = `${options.componentsDir}/components.json`;
|
|
105
|
+
const componentsDetailsPath = `${options.componentsDir}/components-details.json`;
|
|
106
|
+
await Promise.all([
|
|
107
|
+
options.cdn.putFileContent(JSON.stringify((0, metadata_index_1.getComponentsListFromRows)(rows)), componentsListPath, true),
|
|
108
|
+
options.cdn.putFileContent(JSON.stringify((0, metadata_index_1.getComponentsDetailsFromRows)(rows)), componentsDetailsPath, true)
|
|
109
|
+
]);
|
|
110
|
+
return {
|
|
111
|
+
exported: rows.length,
|
|
112
|
+
files: [componentsListPath, componentsDetailsPath]
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
exports.exportLegacyMetadataFiles = exportLegacyMetadataFiles;
|
|
116
|
+
const backfillMetadataFromStorageDetails = async (options) => {
|
|
117
|
+
try {
|
|
118
|
+
const componentsDetails = await options.cdn.getJson(`${options.componentsDir}/components-details.json`, true);
|
|
119
|
+
return (0, exports.backfillMetadataFromComponentsDetails)(options.metadataStore, componentsDetails);
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
if (isNotFoundError(err, 'file_not_found')) {
|
|
123
|
+
return (0, exports.backfillMetadataFromStorageDirectories)(options);
|
|
124
|
+
}
|
|
125
|
+
throw err;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
exports.backfillMetadataFromStorageDetails = backfillMetadataFromStorageDetails;
|
|
@@ -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 auth = __importStar(require("./authentication"));
|
|
44
|
+
const express_adapter_1 = __importDefault(require("./http-server/express-adapter"));
|
|
44
45
|
const DEFAULT_NODE_KEEPALIVE_MS = 5000;
|
|
45
46
|
function optionsSanitiser(input) {
|
|
46
47
|
const options = { ...input };
|
|
@@ -165,6 +166,18 @@ function optionsSanitiser(input) {
|
|
|
165
166
|
options.timeout = options.timeout || 1000 * 60 * 2;
|
|
166
167
|
options.keepAliveTimeout =
|
|
167
168
|
options.keepAliveTimeout || DEFAULT_NODE_KEEPALIVE_MS;
|
|
169
|
+
if (!options.server) {
|
|
170
|
+
options.server = {
|
|
171
|
+
adapter: express_adapter_1.default,
|
|
172
|
+
options: { port: options.port }
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
if (!options.server.adapter) {
|
|
176
|
+
options.server.adapter = express_adapter_1.default;
|
|
177
|
+
}
|
|
178
|
+
if (typeof options.server.options === 'undefined') {
|
|
179
|
+
options.server.options = { port: options.port };
|
|
180
|
+
}
|
|
168
181
|
if (options.s3) {
|
|
169
182
|
options.storage = {
|
|
170
183
|
adapter: require('oc-s3-storage-adapter'),
|