oc 0.50.55 → 0.50.56
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 +4 -4
- package/.turbo/turbo-test-silent.log +11 -11
- package/.turbo/turbo-test.log +6 -1614
- package/CHANGELOG.md +8 -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 +4 -4
- package/dist/components/oc-client/_package/server.js +1 -1
- package/dist/components/oc-client/package.json +1 -1
- 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/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/repository.d.ts +1 -0
- package/dist/registry/domain/repository.js +106 -7
- package/dist/registry/domain/validators/registry-configuration.js +21 -0
- package/dist/registry/index.d.ts +1 -1
- package/dist/registry/index.js +6 -2
- package/dist/resources/index.d.ts +3 -0
- package/dist/resources/index.js +3 -0
- package/dist/types.d.ts +41 -1
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -32,6 +32,186 @@ We have been using it for more than two years in production at OpenTable, for sh
|
|
|
32
32
|
- [Troubleshooting](../../CONTRIBUTING.md#troubleshooting)
|
|
33
33
|
- [Gitter chat](https://gitter.im/opentable/oc)
|
|
34
34
|
|
|
35
|
+
## Registry metadata stores
|
|
36
|
+
|
|
37
|
+
By default, an OC registry keeps its component index in storage files:
|
|
38
|
+
`components.json` and `components-details.json`. A registry can instead read and
|
|
39
|
+
write that index through a metadata adapter by adding a `metadata` block to the
|
|
40
|
+
registry configuration.
|
|
41
|
+
|
|
42
|
+
Storage is still required in metadata mode. The metadata store contains only the
|
|
43
|
+
component name, version, publish date, and template size. Component static files
|
|
44
|
+
and version `package.json` files continue to live in the configured storage
|
|
45
|
+
adapter.
|
|
46
|
+
|
|
47
|
+
```js
|
|
48
|
+
const azureSqlMetadataAdapter = require('oc-azure-sql-metadata-adapter').default;
|
|
49
|
+
const s3StorageAdapter = require('oc-s3-storage-adapter');
|
|
50
|
+
|
|
51
|
+
registry.configure({
|
|
52
|
+
storage: {
|
|
53
|
+
adapter: s3StorageAdapter,
|
|
54
|
+
options: {
|
|
55
|
+
bucket: process.env.OC_STORAGE_BUCKET,
|
|
56
|
+
region: process.env.OC_STORAGE_REGION,
|
|
57
|
+
componentsDir: 'components',
|
|
58
|
+
path: process.env.OC_STORAGE_BASE_URL
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
metadata: {
|
|
62
|
+
adapter: azureSqlMetadataAdapter,
|
|
63
|
+
options: {
|
|
64
|
+
connectionString: process.env.OC_METADATA_SQL_CONNECTION_STRING
|
|
65
|
+
},
|
|
66
|
+
manageSchema: true,
|
|
67
|
+
reconcileFromStorage: false,
|
|
68
|
+
exportLegacyFiles: false
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`metadata.manageSchema` defaults to `true`. Set it to `false` when operators
|
|
74
|
+
manage the metadata schema/table separately; OC passes the value through to the
|
|
75
|
+
configured metadata adapter during validation, startup, and migration.
|
|
76
|
+
|
|
77
|
+
The registry initialises the metadata store during startup. If startup succeeds,
|
|
78
|
+
reads are served from OC's in-memory cache, and cache polling refreshes from the
|
|
79
|
+
metadata store. Publishing reserves the metadata row first, uploads package files
|
|
80
|
+
to storage only after the reservation succeeds, then commits the row. Duplicate
|
|
81
|
+
or in-progress metadata rows are treated as the existing "component version
|
|
82
|
+
already exists" publish error. When the registry is shut down via
|
|
83
|
+
`registry.close(callback)`, the metadata adapter's optional `close()` hook is
|
|
84
|
+
invoked so the adapter can release its connection pool.
|
|
85
|
+
|
|
86
|
+
Custom metadata adapters should implement the shared contract exported by
|
|
87
|
+
`oc-metadata-adapters-utils`:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import type { ComponentRow, MetadataStore } from 'oc-metadata-adapters-utils';
|
|
91
|
+
import {
|
|
92
|
+
VERSION_ALREADY_EXISTS,
|
|
93
|
+
VERSION_PUBLISH_IN_PROGRESS
|
|
94
|
+
} from 'oc-metadata-adapters-utils';
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Migrating existing registries
|
|
98
|
+
|
|
99
|
+
Use the CLI backfill command before enabling metadata mode in production:
|
|
100
|
+
|
|
101
|
+
```sh
|
|
102
|
+
oc registry migrate-metadata ./registry.config.js
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The argument is a path to a module that exports the same registry config object you would pass to `registry.configure()`. It must include both `storage` and `metadata`, and it must pass registry config validation. The module can be CommonJS, an ES module `default` export, or an async function returning the config — all three are accepted:
|
|
106
|
+
|
|
107
|
+
```js
|
|
108
|
+
// registry.config.js — CommonJS
|
|
109
|
+
const azureSqlMetadataAdapter = require('oc-azure-sql-metadata-adapter').default;
|
|
110
|
+
const s3StorageAdapter = require('oc-s3-storage-adapter');
|
|
111
|
+
|
|
112
|
+
module.exports = {
|
|
113
|
+
baseUrl: 'http://my-registry.example.com/',
|
|
114
|
+
storage: {
|
|
115
|
+
adapter: s3StorageAdapter,
|
|
116
|
+
options: {
|
|
117
|
+
bucket: 'my-bucket',
|
|
118
|
+
region: 'us-east-1',
|
|
119
|
+
componentsDir: 'components',
|
|
120
|
+
path: 'https://cdn.example.com/'
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
metadata: {
|
|
124
|
+
adapter: azureSqlMetadataAdapter,
|
|
125
|
+
options: {
|
|
126
|
+
connectionString: process.env.OC_METADATA_SQL_CONNECTION_STRING
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
// registry.config.ts — ES module (transpiled or run with a native ESM loader)
|
|
134
|
+
import azureSqlMetadataAdapter from 'oc-azure-sql-metadata-adapter';
|
|
135
|
+
import s3StorageAdapter from 'oc-s3-storage-adapter';
|
|
136
|
+
|
|
137
|
+
export default {
|
|
138
|
+
baseUrl: 'http://my-registry.example.com/',
|
|
139
|
+
storage: {
|
|
140
|
+
adapter: s3StorageAdapter,
|
|
141
|
+
options: {
|
|
142
|
+
bucket: 'my-bucket',
|
|
143
|
+
region: 'us-east-1',
|
|
144
|
+
componentsDir: 'components',
|
|
145
|
+
path: 'https://cdn.example.com/'
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
metadata: {
|
|
149
|
+
adapter: azureSqlMetadataAdapter,
|
|
150
|
+
options: {
|
|
151
|
+
connectionString: process.env.OC_METADATA_SQL_CONNECTION_STRING
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
```js
|
|
158
|
+
// registry.config.js — async factory (e.g. to resolve secrets first)
|
|
159
|
+
module.exports = async () => {
|
|
160
|
+
const azureSqlMetadataAdapter = require('oc-azure-sql-metadata-adapter').default;
|
|
161
|
+
const s3StorageAdapter = require('oc-s3-storage-adapter');
|
|
162
|
+
const connectionString = await getSecret('oc-metadata-sql');
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
baseUrl: 'http://my-registry.example.com/',
|
|
166
|
+
storage: {
|
|
167
|
+
adapter: s3StorageAdapter,
|
|
168
|
+
options: {
|
|
169
|
+
bucket: 'my-bucket',
|
|
170
|
+
region: 'us-east-1',
|
|
171
|
+
componentsDir: 'components',
|
|
172
|
+
path: 'https://cdn.example.com/'
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
metadata: {
|
|
176
|
+
adapter: azureSqlMetadataAdapter,
|
|
177
|
+
options: { connectionString }
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
};
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
The command initialises the configured metadata adapter and backfills rows from
|
|
184
|
+
`${componentsDir}/components-details.json`. If that file is missing, it falls back
|
|
185
|
+
to scanning `${componentsDir}/<component>/<version>/package.json`. Existing rows
|
|
186
|
+
are skipped, so the command is idempotent.
|
|
187
|
+
|
|
188
|
+
A safe migration sequence is:
|
|
189
|
+
|
|
190
|
+
1. Deploy the metadata adapter configuration to a non-serving environment.
|
|
191
|
+
2. Run `oc registry migrate-metadata ./registry.config.js`.
|
|
192
|
+
3. Start one registry instance with metadata mode enabled and verify reads.
|
|
193
|
+
4. Roll out metadata mode to the remaining registry instances.
|
|
194
|
+
|
|
195
|
+
### Bake-in and rollback options
|
|
196
|
+
|
|
197
|
+
Two optional flags help run storage and metadata side by side during migration:
|
|
198
|
+
|
|
199
|
+
- `metadata.reconcileFromStorage: true` scans storage on registry startup and
|
|
200
|
+
inserts missing metadata rows before cache hydration. Existing rows are skipped.
|
|
201
|
+
- `metadata.exportLegacyFiles: true` writes DB-derived `components.json` and
|
|
202
|
+
`components-details.json` projections to storage once on registry startup. It
|
|
203
|
+
is **not** triggered per publish, so publishing stays an O(1) append rather
|
|
204
|
+
than a full-registry scan + blob rewrite.
|
|
205
|
+
- `metadata.exportLegacyFilesInterval: <seconds>` additionally refreshes those
|
|
206
|
+
projections on a background timer (non-overlapping) when `exportLegacyFiles`
|
|
207
|
+
is enabled. Config validation rejects setting an interval without
|
|
208
|
+
`metadata.exportLegacyFiles: true`. Omit it to export at startup only. The
|
|
209
|
+
timer is stopped on `registry.close()`.
|
|
210
|
+
|
|
211
|
+
These files are one-way projections from the metadata store. They can help with
|
|
212
|
+
rollback to storage mode, but they do not replace the storage adapter because
|
|
213
|
+
component statics remain in storage.
|
|
214
|
+
|
|
35
215
|
## Requirements and build status
|
|
36
216
|
|
|
37
217
|
Disclaimer: This project is still under heavy development and the API is likely to change at any time. In case you would find any issues, check the [troubleshooting page](../../CONTRIBUTING.md#troubleshooting).
|
package/dist/cli/commands.d.ts
CHANGED
|
@@ -173,6 +173,14 @@ declare const _default: {
|
|
|
173
173
|
description: string;
|
|
174
174
|
usage: string;
|
|
175
175
|
};
|
|
176
|
+
'migrate-metadata': {
|
|
177
|
+
cmd: string;
|
|
178
|
+
example: {
|
|
179
|
+
cmd: string;
|
|
180
|
+
};
|
|
181
|
+
description: string;
|
|
182
|
+
usage: string;
|
|
183
|
+
};
|
|
176
184
|
remove: {
|
|
177
185
|
cmd: string;
|
|
178
186
|
example: {
|
package/dist/cli/commands.js
CHANGED
|
@@ -173,6 +173,14 @@ exports.default = {
|
|
|
173
173
|
description: 'Show oc registries added to the current project',
|
|
174
174
|
usage: 'Usage: $0 registry ls'
|
|
175
175
|
},
|
|
176
|
+
'migrate-metadata': {
|
|
177
|
+
cmd: 'migrate-metadata <configPath>',
|
|
178
|
+
example: {
|
|
179
|
+
cmd: '$0 registry migrate-metadata ./registry.config.js'
|
|
180
|
+
},
|
|
181
|
+
description: 'Backfill a configured metadata store from existing registry metadata files',
|
|
182
|
+
usage: 'Usage: $0 registry migrate-metadata <configPath>'
|
|
183
|
+
},
|
|
176
184
|
remove: {
|
|
177
185
|
cmd: 'remove <registryUrl>',
|
|
178
186
|
example: {
|
package/dist/cli/facade/dev.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ declare const dev: ({ local, logger }: {
|
|
|
18
18
|
verbose?: boolean;
|
|
19
19
|
production?: boolean;
|
|
20
20
|
}): Promise<{
|
|
21
|
-
close: (callback: (err?: Error | undefined | string) => void) => void
|
|
21
|
+
close: (callback: (err?: Error | undefined | string) => void) => void;
|
|
22
22
|
on: <T extends keyof {
|
|
23
23
|
error: {
|
|
24
24
|
code: string;
|
|
@@ -100,7 +100,7 @@ declare const dev: ({ local, logger }: {
|
|
|
100
100
|
verbose?: boolean;
|
|
101
101
|
production?: boolean;
|
|
102
102
|
}, arguments__1: (error: unknown, value: {
|
|
103
|
-
close: (callback: (err?: Error | undefined | string) => void) => void
|
|
103
|
+
close: (callback: (err?: Error | undefined | string) => void) => void;
|
|
104
104
|
on: <T extends keyof {
|
|
105
105
|
error: {
|
|
106
106
|
code: string;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Logger } from '../logger';
|
|
2
|
+
declare const registryMigrateMetadata: ({ logger }: {
|
|
3
|
+
logger: Logger;
|
|
4
|
+
}) => {
|
|
5
|
+
(opts: {
|
|
6
|
+
configPath: string;
|
|
7
|
+
}): Promise<{
|
|
8
|
+
scanned: number;
|
|
9
|
+
inserted: number;
|
|
10
|
+
skipped: number;
|
|
11
|
+
}>;
|
|
12
|
+
(opts: {
|
|
13
|
+
configPath: string;
|
|
14
|
+
}, arguments__1: (error: unknown, value: {
|
|
15
|
+
scanned: number;
|
|
16
|
+
inserted: number;
|
|
17
|
+
skipped: number;
|
|
18
|
+
}) => void): void;
|
|
19
|
+
};
|
|
20
|
+
export default registryMigrateMetadata;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
40
|
+
const node_url_1 = require("node:url");
|
|
41
|
+
const universalify_1 = require("universalify");
|
|
42
|
+
const metadata_adapter_options_1 = __importDefault(require("../../registry/domain/metadata-adapter-options"));
|
|
43
|
+
const metadata_migration_1 = require("../../registry/domain/metadata-migration");
|
|
44
|
+
const options_sanitiser_1 = __importDefault(require("../../registry/domain/options-sanitiser"));
|
|
45
|
+
const storage_adapter_1 = __importDefault(require("../../registry/domain/storage-adapter"));
|
|
46
|
+
const validator = __importStar(require("../../registry/domain/validators"));
|
|
47
|
+
const dynamicImport = new Function('specifier', 'return import(specifier)');
|
|
48
|
+
const loadConfigModule = async (resolvedConfigPath) => {
|
|
49
|
+
try {
|
|
50
|
+
const resolvedRequirePath = require.resolve(resolvedConfigPath);
|
|
51
|
+
delete require.cache[resolvedRequirePath];
|
|
52
|
+
return require(resolvedRequirePath);
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
if (err?.code !== 'ERR_REQUIRE_ESM') {
|
|
56
|
+
throw err;
|
|
57
|
+
}
|
|
58
|
+
return dynamicImport(`${(0, node_url_1.pathToFileURL)(resolvedConfigPath).href}?t=${Date.now()}`);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
const loadRegistryOptions = async (configPath) => {
|
|
62
|
+
const resolvedConfigPath = node_path_1.default.resolve(configPath);
|
|
63
|
+
const configModule = await loadConfigModule(resolvedConfigPath);
|
|
64
|
+
const candidate = configModule?.default ?? configModule;
|
|
65
|
+
const options = typeof candidate === 'function' ? await candidate() : candidate;
|
|
66
|
+
if (!options || typeof options !== 'object') {
|
|
67
|
+
throw new Error('Registry config must export an options object');
|
|
68
|
+
}
|
|
69
|
+
return options;
|
|
70
|
+
};
|
|
71
|
+
const registryMigrateMetadata = ({ logger }) => (0, universalify_1.fromPromise)(async (opts) => {
|
|
72
|
+
try {
|
|
73
|
+
const inputOptions = await loadRegistryOptions(opts.configPath);
|
|
74
|
+
const validationResult = validator.validateRegistryConfiguration(inputOptions);
|
|
75
|
+
if (!validationResult.isValid) {
|
|
76
|
+
throw new Error(validationResult.message);
|
|
77
|
+
}
|
|
78
|
+
const conf = (0, options_sanitiser_1.default)(inputOptions);
|
|
79
|
+
if (!conf.metadata) {
|
|
80
|
+
throw new Error('Registry config must include metadata options');
|
|
81
|
+
}
|
|
82
|
+
const metadataStore = conf.metadata.adapter((0, metadata_adapter_options_1.default)(conf));
|
|
83
|
+
const cdn = (0, storage_adapter_1.default)(conf.storage.adapter(conf.storage.options));
|
|
84
|
+
try {
|
|
85
|
+
await metadataStore.initialise();
|
|
86
|
+
const result = await (0, metadata_migration_1.backfillMetadataFromStorageDetails)({
|
|
87
|
+
metadataStore,
|
|
88
|
+
cdn,
|
|
89
|
+
componentsDir: conf.storage.options.componentsDir
|
|
90
|
+
});
|
|
91
|
+
logger.ok(`Metadata migration completed: ${result.scanned} scanned, ${result.inserted} inserted, ${result.skipped} skipped`);
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
if (metadataStore.close) {
|
|
96
|
+
await metadataStore.close();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
logger.err(String(err?.message || err));
|
|
102
|
+
throw err;
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
exports.default = registryMigrateMetadata;
|
package/dist/cli/index.js
CHANGED
|
@@ -19,6 +19,7 @@ const publish_1 = __importDefault(require("./facade/publish"));
|
|
|
19
19
|
const registry_2 = __importDefault(require("./facade/registry"));
|
|
20
20
|
const registry_add_1 = __importDefault(require("./facade/registry-add"));
|
|
21
21
|
const registry_ls_1 = __importDefault(require("./facade/registry-ls"));
|
|
22
|
+
const registry_migrate_metadata_1 = __importDefault(require("./facade/registry-migrate-metadata"));
|
|
22
23
|
const registry_remove_1 = __importDefault(require("./facade/registry-remove"));
|
|
23
24
|
const validate_1 = __importDefault(require("./facade/validate"));
|
|
24
25
|
const logger_1 = __importDefault(require("./logger"));
|
|
@@ -34,6 +35,7 @@ const cliFunctions = {
|
|
|
34
35
|
registry: registry_2.default,
|
|
35
36
|
'registry-add': registry_add_1.default,
|
|
36
37
|
'registry-ls': registry_ls_1.default,
|
|
38
|
+
'registry-migrate-metadata': registry_migrate_metadata_1.default,
|
|
37
39
|
'registry-remove': registry_remove_1.default,
|
|
38
40
|
validate: validate_1.default
|
|
39
41
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oc-client",
|
|
3
3
|
"description": "The OpenComponents client-side javascript client",
|
|
4
|
-
"version": "0.50.
|
|
4
|
+
"version": "0.50.56",
|
|
5
5
|
"repository": "https://github.com/opencomponents/oc/tree/master/components/oc-client",
|
|
6
6
|
"author": "Matteo Figus <matteofigus@gmail.com>",
|
|
7
7
|
"oc": {
|
|
@@ -23,14 +23,14 @@
|
|
|
23
23
|
],
|
|
24
24
|
"dataProvider": {
|
|
25
25
|
"type": "node.js",
|
|
26
|
-
"hashKey": "
|
|
26
|
+
"hashKey": "620aa4d6457f2972e6d53d8b8b572b9fcb5aa0d3",
|
|
27
27
|
"src": "server.js",
|
|
28
28
|
"size": 644
|
|
29
29
|
}
|
|
30
30
|
},
|
|
31
|
-
"version": "0.50.
|
|
31
|
+
"version": "0.50.56",
|
|
32
32
|
"packaged": true,
|
|
33
|
-
"date":
|
|
33
|
+
"date": 1783178172523
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"oc-template-es6-compiler": "^8.0.0"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=(t,s)=>{const{staticPath:e,templates:a}=t;return s(null,{staticPath:e,templates:a})},r=(t,s)=>{o(t,(e,a,i={})=>{if(e)return s(e);if(a==null)return s(null,{__oc_emptyResponse:!0});const n=t.action?a:Object.assign({},a,{_staticPath:t.staticPath,_baseUrl:t.baseUrl,_componentName:"oc-client",_componentVersion:"0.50.
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=(t,s)=>{const{staticPath:e,templates:a}=t;return s(null,{staticPath:e,templates:a})},r=(t,s)=>{o(t,(e,a,i={})=>{if(e)return s(e);if(a==null)return s(null,{__oc_emptyResponse:!0});const n=t.action?a:Object.assign({},a,{_staticPath:t.staticPath,_baseUrl:t.baseUrl,_componentName:"oc-client",_componentVersion:"0.50.56"}),c=t.staticPath.indexOf("http")===0?t.staticPath:"https:"+t.staticPath;return s(null,Object.assign({},{component:{key:"c4abb6bf4dc6657fb718a45b64bd6b2cb92e874a",src:c+"template.js",props:n,esm:!1,development:void 0}}))})};exports.data=r;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oc-client",
|
|
3
3
|
"description": "The OpenComponents client-side javascript client",
|
|
4
|
-
"version": "0.50.
|
|
4
|
+
"version": "0.50.56",
|
|
5
5
|
"repository": "https://github.com/opencomponents/oc/tree/master/components/oc-client",
|
|
6
6
|
"author": "Matteo Figus <matteofigus@gmail.com>",
|
|
7
7
|
"oc": {
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { type StorageAdapter } from 'oc-storage-adapters-utils';
|
|
2
2
|
import type { ComponentsList, Config } from '../../../types';
|
|
3
|
-
|
|
3
|
+
import type { MetadataIndex } from '../metadata-index';
|
|
4
|
+
export default function componentsCache(conf: Config, cdn: StorageAdapter, metadataIndex?: MetadataIndex): {
|
|
4
5
|
get(): ComponentsList;
|
|
5
6
|
load(): Promise<ComponentsList>;
|
|
6
7
|
refresh(): Promise<ComponentsList>;
|
|
8
|
+
close(): void;
|
|
7
9
|
};
|
|
@@ -9,14 +9,18 @@ const oc_get_unix_utc_timestamp_1 = __importDefault(require("oc-get-unix-utc-tim
|
|
|
9
9
|
const oc_storage_adapters_utils_1 = require("oc-storage-adapters-utils");
|
|
10
10
|
const events_handler_1 = __importDefault(require("../events-handler"));
|
|
11
11
|
const components_list_1 = __importDefault(require("./components-list"));
|
|
12
|
-
function componentsCache(conf, cdn) {
|
|
12
|
+
function componentsCache(conf, cdn, metadataIndex) {
|
|
13
13
|
let cachedComponentsList;
|
|
14
14
|
let refreshLoop;
|
|
15
|
+
let closed = false;
|
|
15
16
|
const componentsList = (0, components_list_1.default)(conf, cdn);
|
|
17
|
+
const getFromMetadataIndex = async () => (await metadataIndex.refresh()).componentsList;
|
|
16
18
|
const poll = () => {
|
|
17
19
|
return setTimeout(async () => {
|
|
18
20
|
try {
|
|
19
|
-
const data =
|
|
21
|
+
const data = metadataIndex
|
|
22
|
+
? await getFromMetadataIndex()
|
|
23
|
+
: await componentsList.getFromJson();
|
|
20
24
|
events_handler_1.default.fire('cache-poll', (0, oc_get_unix_utc_timestamp_1.default)());
|
|
21
25
|
if (data.lastEdit > cachedComponentsList.lastEdit) {
|
|
22
26
|
cachedComponentsList = data;
|
|
@@ -28,12 +32,16 @@ function componentsCache(conf, cdn) {
|
|
|
28
32
|
message: err?.message || String(err)
|
|
29
33
|
});
|
|
30
34
|
}
|
|
31
|
-
|
|
35
|
+
if (!closed) {
|
|
36
|
+
refreshLoop = poll();
|
|
37
|
+
}
|
|
32
38
|
}, conf.pollingInterval * 1000);
|
|
33
39
|
};
|
|
34
40
|
const cacheDataAndStartPolling = (data) => {
|
|
35
41
|
cachedComponentsList = data;
|
|
36
|
-
|
|
42
|
+
if (!closed) {
|
|
43
|
+
refreshLoop = poll();
|
|
44
|
+
}
|
|
37
45
|
return data;
|
|
38
46
|
};
|
|
39
47
|
const throwError = (code, message) => {
|
|
@@ -42,12 +50,21 @@ function componentsCache(conf, cdn) {
|
|
|
42
50
|
};
|
|
43
51
|
return {
|
|
44
52
|
get() {
|
|
53
|
+
const metadataSnapshot = metadataIndex?.get();
|
|
54
|
+
if (metadataSnapshot) {
|
|
55
|
+
cachedComponentsList = metadataSnapshot.componentsList;
|
|
56
|
+
}
|
|
45
57
|
if (!cachedComponentsList) {
|
|
46
58
|
return throwError('components_cache_empty', `The component's cache was empty`);
|
|
47
59
|
}
|
|
48
60
|
return cachedComponentsList;
|
|
49
61
|
},
|
|
50
62
|
async load() {
|
|
63
|
+
if (metadataIndex) {
|
|
64
|
+
const components = await getFromMetadataIndex().catch((err) => throwError('components_list_get', err));
|
|
65
|
+
cacheDataAndStartPolling(components);
|
|
66
|
+
return components;
|
|
67
|
+
}
|
|
51
68
|
const jsonComponents = await componentsList.getFromJson().catch((err) => {
|
|
52
69
|
if (err?.code === oc_storage_adapters_utils_1.strings.errors.STORAGE.FILE_NOT_FOUND_CODE)
|
|
53
70
|
return null;
|
|
@@ -69,13 +86,19 @@ function componentsCache(conf, cdn) {
|
|
|
69
86
|
clearTimeout(refreshLoop);
|
|
70
87
|
try {
|
|
71
88
|
// Passing components that we know are fine, so it doesn't refresh invalid components
|
|
72
|
-
const components =
|
|
89
|
+
const components = metadataIndex
|
|
90
|
+
? await getFromMetadataIndex()
|
|
91
|
+
: await componentsList.refresh(cachedComponentsList);
|
|
73
92
|
cacheDataAndStartPolling(components);
|
|
74
93
|
return components;
|
|
75
94
|
}
|
|
76
95
|
catch (err) {
|
|
77
96
|
return throwError('components_cache_refresh', err);
|
|
78
97
|
}
|
|
98
|
+
},
|
|
99
|
+
close() {
|
|
100
|
+
closed = true;
|
|
101
|
+
clearTimeout(refreshLoop);
|
|
79
102
|
}
|
|
80
103
|
};
|
|
81
104
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { StorageAdapter } from 'oc-storage-adapters-utils';
|
|
2
2
|
import type { ComponentsDetails, ComponentsList, Config } from '../../types';
|
|
3
|
-
|
|
3
|
+
import type { MetadataIndex } from './metadata-index';
|
|
4
|
+
export default function componentsDetails(conf: Config, cdn: StorageAdapter, metadataIndex?: MetadataIndex): {
|
|
4
5
|
get: () => Promise<ComponentsDetails>;
|
|
5
6
|
refresh: (componentsList: ComponentsList) => Promise<ComponentsDetails>;
|
|
7
|
+
close: () => void;
|
|
6
8
|
};
|
|
@@ -8,9 +8,10 @@ const lodash_isequal_1 = __importDefault(require("lodash.isequal"));
|
|
|
8
8
|
const oc_get_unix_utc_timestamp_1 = __importDefault(require("oc-get-unix-utc-timestamp"));
|
|
9
9
|
const pLimit_1 = __importDefault(require("../../utils/pLimit"));
|
|
10
10
|
const events_handler_1 = __importDefault(require("./events-handler"));
|
|
11
|
-
function componentsDetails(conf, cdn) {
|
|
11
|
+
function componentsDetails(conf, cdn, metadataIndex) {
|
|
12
12
|
let cachedComponentsDetails;
|
|
13
13
|
let refreshLoop;
|
|
14
|
+
let closed = false;
|
|
14
15
|
const returnError = (code, message) => {
|
|
15
16
|
events_handler_1.default.fire('error', {
|
|
16
17
|
code,
|
|
@@ -20,10 +21,13 @@ function componentsDetails(conf, cdn) {
|
|
|
20
21
|
};
|
|
21
22
|
const filePath = () => `${conf.storage.options.componentsDir}/components-details.json`;
|
|
22
23
|
const getFromJson = () => cdn.getJson(filePath(), true);
|
|
24
|
+
const getFromMetadataIndex = async () => (await metadataIndex.getOrRefresh()).componentsDetails;
|
|
23
25
|
const poll = () => {
|
|
24
26
|
return setTimeout(async () => {
|
|
25
27
|
try {
|
|
26
|
-
const data =
|
|
28
|
+
const data = metadataIndex
|
|
29
|
+
? await getFromMetadataIndex()
|
|
30
|
+
: await getFromJson();
|
|
27
31
|
events_handler_1.default.fire('cache-poll', (0, oc_get_unix_utc_timestamp_1.default)());
|
|
28
32
|
if (!cachedComponentsDetails ||
|
|
29
33
|
data.lastEdit > cachedComponentsDetails.lastEdit) {
|
|
@@ -36,12 +40,16 @@ function componentsDetails(conf, cdn) {
|
|
|
36
40
|
message: err?.message || String(err)
|
|
37
41
|
});
|
|
38
42
|
}
|
|
39
|
-
|
|
43
|
+
if (!closed) {
|
|
44
|
+
refreshLoop = poll();
|
|
45
|
+
}
|
|
40
46
|
}, conf.pollingInterval * 1000);
|
|
41
47
|
};
|
|
42
48
|
const cacheDataAndStartPolling = (data) => {
|
|
43
49
|
cachedComponentsDetails = data;
|
|
44
|
-
|
|
50
|
+
if (!metadataIndex && !closed) {
|
|
51
|
+
refreshLoop = poll();
|
|
52
|
+
}
|
|
45
53
|
return data;
|
|
46
54
|
};
|
|
47
55
|
const getFromDirectories = async (options) => {
|
|
@@ -74,6 +82,10 @@ function componentsDetails(conf, cdn) {
|
|
|
74
82
|
};
|
|
75
83
|
const save = (data) => cdn.putFileContent(JSON.stringify(data), filePath(), true);
|
|
76
84
|
const get = async () => {
|
|
85
|
+
if (metadataIndex) {
|
|
86
|
+
cachedComponentsDetails = await getFromMetadataIndex();
|
|
87
|
+
return cachedComponentsDetails;
|
|
88
|
+
}
|
|
77
89
|
if (cachedComponentsDetails) {
|
|
78
90
|
return cachedComponentsDetails;
|
|
79
91
|
}
|
|
@@ -82,6 +94,10 @@ function componentsDetails(conf, cdn) {
|
|
|
82
94
|
};
|
|
83
95
|
const refresh = async (componentsList) => {
|
|
84
96
|
clearTimeout(refreshLoop);
|
|
97
|
+
if (metadataIndex) {
|
|
98
|
+
const details = await getFromMetadataIndex().catch((err) => returnError('components_details_get', err));
|
|
99
|
+
return cacheDataAndStartPolling(details);
|
|
100
|
+
}
|
|
85
101
|
const jsonDetails = await getFromJson().catch(() => undefined);
|
|
86
102
|
const dirDetails = await getFromDirectories({
|
|
87
103
|
componentsList,
|
|
@@ -94,8 +110,13 @@ function componentsDetails(conf, cdn) {
|
|
|
94
110
|
}
|
|
95
111
|
return cacheDataAndStartPolling(jsonDetails);
|
|
96
112
|
};
|
|
113
|
+
const close = () => {
|
|
114
|
+
closed = true;
|
|
115
|
+
clearTimeout(refreshLoop);
|
|
116
|
+
};
|
|
97
117
|
return {
|
|
98
118
|
get,
|
|
99
|
-
refresh
|
|
119
|
+
refresh,
|
|
120
|
+
close
|
|
100
121
|
};
|
|
101
122
|
}
|
|
@@ -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;
|