@terra-graph/core 1.0.0-rc.4 → 1.0.0-rc.5
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/dist/cjs/Runtime/RuntimeProviderLoader/ModuleRuntimeProviderLoader.d.ts +4 -9
- package/dist/cjs/Runtime/RuntimeProviderLoader/ModuleRuntimeProviderLoader.js +77 -40
- package/dist/cjs/Runtime/RuntimeProviderLoader/ModuleRuntimeProviderLoader.test.js +150 -1
- package/dist/esm/Runtime/RuntimeProviderLoader/ModuleRuntimeProviderLoader.d.ts +4 -9
- package/dist/esm/Runtime/RuntimeProviderLoader/ModuleRuntimeProviderLoader.js +77 -40
- package/dist/esm/Runtime/RuntimeProviderLoader/ModuleRuntimeProviderLoader.test.js +153 -4
- package/package.json +3 -1
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
import { RuntimeProvider } from '../RuntimeProvider.js';
|
|
2
2
|
import { RuntimeProviderLoadInput, RuntimeProviderLoader } from '../RuntimeProviderLoader.js';
|
|
3
|
-
type RuntimeProviderModuleShape = RuntimeProvider | (() => RuntimeProvider | Promise<RuntimeProvider>) | {
|
|
4
|
-
runtimeProvider?: RuntimeProvider | (() => RuntimeProvider | Promise<RuntimeProvider>);
|
|
5
|
-
default?: RuntimeProvider | (() => RuntimeProvider | Promise<RuntimeProvider>);
|
|
6
|
-
};
|
|
7
|
-
export declare const __test__: {
|
|
8
|
-
toProvider: (value: RuntimeProviderModuleShape) => Promise<RuntimeProvider>;
|
|
9
|
-
resolveImportSpecifier: (specifier: string, sourceReference?: string) => string;
|
|
10
|
-
};
|
|
11
3
|
export declare class ModuleRuntimeProviderLoader implements RuntimeProviderLoader {
|
|
12
4
|
load(input: RuntimeProviderLoadInput): Promise<RuntimeProvider>;
|
|
5
|
+
private isRuntimeProvider;
|
|
6
|
+
private isRuntimeProviderFactory;
|
|
7
|
+
private toProvider;
|
|
8
|
+
private resolveImportSpecifier;
|
|
13
9
|
}
|
|
14
|
-
export {};
|
|
@@ -33,55 +33,92 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.ModuleRuntimeProviderLoader =
|
|
36
|
+
exports.ModuleRuntimeProviderLoader = void 0;
|
|
37
|
+
const node_module_1 = require("node:module");
|
|
37
38
|
const node_path_1 = require("node:path");
|
|
38
39
|
const node_url_1 = require("node:url");
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
40
|
+
class ModuleRuntimeProviderLoader {
|
|
41
|
+
async load(input) {
|
|
42
|
+
const resolvedSpecifier = this.resolveImportSpecifier(input.specifier, input.sourceReference);
|
|
43
|
+
const loaded = await Promise.resolve(`${resolvedSpecifier}`).then(s => __importStar(require(s)));
|
|
44
|
+
return this.toProvider(loaded);
|
|
45
|
+
}
|
|
46
|
+
isRuntimeProvider(value) {
|
|
47
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
48
|
+
}
|
|
49
|
+
isRuntimeProviderFactory(value) {
|
|
50
|
+
return typeof value === 'function';
|
|
51
|
+
}
|
|
52
|
+
async toProvider(value) {
|
|
53
|
+
const candidate = this.isRuntimeProvider(value) &&
|
|
54
|
+
('runtimeProvider' in value || 'default' in value)
|
|
55
|
+
? (value.runtimeProvider ??
|
|
56
|
+
value.default)
|
|
57
|
+
: value;
|
|
58
|
+
if (this.isRuntimeProviderFactory(candidate)) {
|
|
59
|
+
const resolved = await candidate();
|
|
60
|
+
if (!this.isRuntimeProvider(resolved)) {
|
|
61
|
+
throw new Error('Module does not export a valid runtime provider');
|
|
62
|
+
}
|
|
63
|
+
return resolved;
|
|
64
|
+
}
|
|
65
|
+
if (!this.isRuntimeProvider(candidate)) {
|
|
50
66
|
throw new Error('Module does not export a valid runtime provider');
|
|
51
67
|
}
|
|
52
|
-
return
|
|
68
|
+
return candidate;
|
|
53
69
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
70
|
+
resolveImportSpecifier(specifier, sourceReference) {
|
|
71
|
+
if (specifier.startsWith('.') ||
|
|
72
|
+
specifier.startsWith('/') ||
|
|
73
|
+
specifier.startsWith('file:')) {
|
|
74
|
+
if (specifier.startsWith('file:')) {
|
|
75
|
+
return specifier;
|
|
76
|
+
}
|
|
77
|
+
if ((0, node_path_1.isAbsolute)(specifier)) {
|
|
78
|
+
return (0, node_url_1.pathToFileURL)(specifier).href;
|
|
79
|
+
}
|
|
80
|
+
if (!sourceReference) {
|
|
81
|
+
return (0, node_url_1.pathToFileURL)((0, node_path_1.resolve)(specifier)).href;
|
|
82
|
+
}
|
|
83
|
+
return (0, node_url_1.pathToFileURL)((0, node_path_1.resolve)((0, node_path_1.dirname)(sourceReference), specifier))
|
|
84
|
+
.href;
|
|
85
|
+
}
|
|
86
|
+
if ((0, node_module_1.isBuiltin)(specifier)) {
|
|
64
87
|
return specifier;
|
|
65
88
|
}
|
|
66
|
-
|
|
67
|
-
|
|
89
|
+
const resolveFromBase = (baseFile) => {
|
|
90
|
+
const moduleApi = node_module_1.Module;
|
|
91
|
+
if (!moduleApi._resolveFilename || !moduleApi._nodeModulePaths) {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
const parent = new node_module_1.Module(baseFile);
|
|
96
|
+
parent.filename = baseFile;
|
|
97
|
+
parent.paths = moduleApi._nodeModulePaths((0, node_path_1.dirname)(baseFile));
|
|
98
|
+
const resolved = moduleApi._resolveFilename(specifier, parent, false, {
|
|
99
|
+
conditions: new Set(['import', 'default', 'require']),
|
|
100
|
+
});
|
|
101
|
+
return (0, node_url_1.pathToFileURL)(resolved).href;
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
if (sourceReference) {
|
|
108
|
+
const basePath = sourceReference.startsWith('file:')
|
|
109
|
+
? (0, node_url_1.fileURLToPath)(sourceReference)
|
|
110
|
+
: sourceReference;
|
|
111
|
+
const resolvedFromSource = resolveFromBase(basePath);
|
|
112
|
+
if (resolvedFromSource) {
|
|
113
|
+
return resolvedFromSource;
|
|
114
|
+
}
|
|
68
115
|
}
|
|
69
|
-
|
|
70
|
-
|
|
116
|
+
const cwdBase = (0, node_path_1.resolve)(process.cwd(), 'index.js');
|
|
117
|
+
const resolvedFromCwd = resolveFromBase(cwdBase);
|
|
118
|
+
if (resolvedFromCwd) {
|
|
119
|
+
return resolvedFromCwd;
|
|
71
120
|
}
|
|
72
|
-
return
|
|
73
|
-
}
|
|
74
|
-
return specifier;
|
|
75
|
-
};
|
|
76
|
-
exports.__test__ = {
|
|
77
|
-
toProvider,
|
|
78
|
-
resolveImportSpecifier,
|
|
79
|
-
};
|
|
80
|
-
class ModuleRuntimeProviderLoader {
|
|
81
|
-
async load(input) {
|
|
82
|
-
const resolvedSpecifier = resolveImportSpecifier(input.specifier, input.sourceReference);
|
|
83
|
-
const loaded = await Promise.resolve(`${resolvedSpecifier}`).then(s => __importStar(require(s)));
|
|
84
|
-
return toProvider(loaded);
|
|
121
|
+
return specifier;
|
|
85
122
|
}
|
|
86
123
|
}
|
|
87
124
|
exports.ModuleRuntimeProviderLoader = ModuleRuntimeProviderLoader;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const promises_1 = require("node:fs/promises");
|
|
4
|
+
const node_module_1 = require("node:module");
|
|
4
5
|
const node_os_1 = require("node:os");
|
|
5
6
|
const node_path_1 = require("node:path");
|
|
6
7
|
const node_url_1 = require("node:url");
|
|
@@ -40,7 +41,9 @@ describe('ModuleRuntimeProviderLoader.load', () => {
|
|
|
40
41
|
});
|
|
41
42
|
it('shoud accept raw runtime providers without module export markers', async () => {
|
|
42
43
|
const provider = {};
|
|
43
|
-
|
|
44
|
+
const loader = new ModuleRuntimeProviderLoader_js_1.ModuleRuntimeProviderLoader();
|
|
45
|
+
// @ts-expect-error accessing private method for test coverage
|
|
46
|
+
await expect(loader.toProvider(provider)).resolves.toBe(provider);
|
|
44
47
|
});
|
|
45
48
|
it('shoud load a provider from a default function export', async () => {
|
|
46
49
|
const dir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'runtime-provider-loader-'));
|
|
@@ -115,6 +118,152 @@ describe('ModuleRuntimeProviderLoader.load', () => {
|
|
|
115
118
|
await (0, promises_1.rm)(dir, { recursive: true, force: true });
|
|
116
119
|
}
|
|
117
120
|
});
|
|
121
|
+
it('shoud resolve package providers from sourceReference', async () => {
|
|
122
|
+
const dir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'runtime-provider-loader-'));
|
|
123
|
+
const packageDir = (0, node_path_1.join)(dir, 'node_modules', 'package-provider');
|
|
124
|
+
const configPath = (0, node_path_1.join)(dir, 'runtime.yml');
|
|
125
|
+
await (0, promises_1.mkdir)(packageDir, { recursive: true });
|
|
126
|
+
await (0, promises_1.writeFile)((0, node_path_1.join)(packageDir, 'package.json'), JSON.stringify({
|
|
127
|
+
name: 'package-provider',
|
|
128
|
+
exports: {
|
|
129
|
+
import: './index.cjs',
|
|
130
|
+
require: './index.cjs',
|
|
131
|
+
default: './index.cjs',
|
|
132
|
+
},
|
|
133
|
+
}), 'utf8');
|
|
134
|
+
await (0, promises_1.writeFile)((0, node_path_1.join)(packageDir, 'index.cjs'), 'module.exports = { supportedAdapterOperationsRegistry: { FromPackage: class FromPackage {} } };', 'utf8');
|
|
135
|
+
await (0, promises_1.writeFile)(configPath, 'providers: []', 'utf8');
|
|
136
|
+
try {
|
|
137
|
+
const loader = new ModuleRuntimeProviderLoader_js_1.ModuleRuntimeProviderLoader();
|
|
138
|
+
const provider = await loader.load({
|
|
139
|
+
specifier: 'package-provider',
|
|
140
|
+
sourceReference: configPath,
|
|
141
|
+
});
|
|
142
|
+
expect(provider.supportedAdapterOperationsRegistry).toBeDefined();
|
|
143
|
+
expect(provider.supportedAdapterOperationsRegistry
|
|
144
|
+
? Object.keys(provider.supportedAdapterOperationsRegistry)
|
|
145
|
+
: []).toContain('FromPackage');
|
|
146
|
+
}
|
|
147
|
+
finally {
|
|
148
|
+
await (0, promises_1.rm)(dir, { recursive: true, force: true });
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
it('shoud resolve package providers from cwd when no reference is provided', async () => {
|
|
152
|
+
const dir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'runtime-provider-loader-'));
|
|
153
|
+
const packageDir = (0, node_path_1.join)(dir, 'node_modules', 'cwd-provider');
|
|
154
|
+
const originalCwd = process.cwd();
|
|
155
|
+
await (0, promises_1.mkdir)(packageDir, { recursive: true });
|
|
156
|
+
await (0, promises_1.writeFile)((0, node_path_1.join)(packageDir, 'package.json'), JSON.stringify({
|
|
157
|
+
name: 'cwd-provider',
|
|
158
|
+
exports: {
|
|
159
|
+
import: './index.cjs',
|
|
160
|
+
require: './index.cjs',
|
|
161
|
+
default: './index.cjs',
|
|
162
|
+
},
|
|
163
|
+
}), 'utf8');
|
|
164
|
+
await (0, promises_1.writeFile)((0, node_path_1.join)(packageDir, 'index.cjs'), 'module.exports = { supportedAdapterOperationsRegistry: { FromCwdPackage: class FromCwdPackage {} } };', 'utf8');
|
|
165
|
+
try {
|
|
166
|
+
process.chdir(dir);
|
|
167
|
+
const loader = new ModuleRuntimeProviderLoader_js_1.ModuleRuntimeProviderLoader();
|
|
168
|
+
const provider = await loader.load({ specifier: 'cwd-provider' });
|
|
169
|
+
expect(provider.supportedAdapterOperationsRegistry).toBeDefined();
|
|
170
|
+
expect(provider.supportedAdapterOperationsRegistry
|
|
171
|
+
? Object.keys(provider.supportedAdapterOperationsRegistry)
|
|
172
|
+
: []).toContain('FromCwdPackage');
|
|
173
|
+
}
|
|
174
|
+
finally {
|
|
175
|
+
process.chdir(originalCwd);
|
|
176
|
+
await (0, promises_1.rm)(dir, { recursive: true, force: true });
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
it('shoud resolve ESM-only package specifiers without loading', async () => {
|
|
180
|
+
const dir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'runtime-provider-loader-'));
|
|
181
|
+
const packageDir = (0, node_path_1.join)(dir, 'node_modules', 'esm-only');
|
|
182
|
+
const configPath = (0, node_path_1.join)(dir, 'runtime.yml');
|
|
183
|
+
await (0, promises_1.mkdir)(packageDir, { recursive: true });
|
|
184
|
+
await (0, promises_1.writeFile)((0, node_path_1.join)(packageDir, 'package.json'), JSON.stringify({
|
|
185
|
+
name: 'esm-only',
|
|
186
|
+
type: 'module',
|
|
187
|
+
exports: {
|
|
188
|
+
import: './index.mjs',
|
|
189
|
+
},
|
|
190
|
+
}), 'utf8');
|
|
191
|
+
await (0, promises_1.writeFile)((0, node_path_1.join)(packageDir, 'index.mjs'), 'export default {};', 'utf8');
|
|
192
|
+
await (0, promises_1.writeFile)(configPath, 'providers: []', 'utf8');
|
|
193
|
+
try {
|
|
194
|
+
const loader = new ModuleRuntimeProviderLoader_js_1.ModuleRuntimeProviderLoader();
|
|
195
|
+
// @ts-expect-error accessing private method for test coverage
|
|
196
|
+
const resolved = loader.resolveImportSpecifier('esm-only', configPath);
|
|
197
|
+
const resolvedPath = await (0, promises_1.realpath)((0, node_url_1.fileURLToPath)(resolved));
|
|
198
|
+
const expectedPath = await (0, promises_1.realpath)((0, node_path_1.join)(packageDir, 'index.mjs'));
|
|
199
|
+
expect(resolvedPath).toBe(expectedPath);
|
|
200
|
+
}
|
|
201
|
+
finally {
|
|
202
|
+
await (0, promises_1.rm)(dir, { recursive: true, force: true });
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
it('shoud resolve package providers from file URL sourceReference', async () => {
|
|
206
|
+
const dir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'runtime-provider-loader-'));
|
|
207
|
+
const packageDir = (0, node_path_1.join)(dir, 'node_modules', 'file-url-provider');
|
|
208
|
+
const configPath = (0, node_path_1.join)(dir, 'runtime.yml');
|
|
209
|
+
await (0, promises_1.mkdir)(packageDir, { recursive: true });
|
|
210
|
+
await (0, promises_1.writeFile)((0, node_path_1.join)(packageDir, 'package.json'), JSON.stringify({
|
|
211
|
+
name: 'file-url-provider',
|
|
212
|
+
exports: {
|
|
213
|
+
import: './index.cjs',
|
|
214
|
+
require: './index.cjs',
|
|
215
|
+
default: './index.cjs',
|
|
216
|
+
},
|
|
217
|
+
}), 'utf8');
|
|
218
|
+
await (0, promises_1.writeFile)((0, node_path_1.join)(packageDir, 'index.cjs'), 'module.exports = {};', 'utf8');
|
|
219
|
+
await (0, promises_1.writeFile)(configPath, 'providers: []', 'utf8');
|
|
220
|
+
try {
|
|
221
|
+
const loader = new ModuleRuntimeProviderLoader_js_1.ModuleRuntimeProviderLoader();
|
|
222
|
+
// @ts-expect-error accessing private method for test coverage
|
|
223
|
+
const resolved = loader.resolveImportSpecifier('file-url-provider', (0, node_url_1.pathToFileURL)(configPath).href);
|
|
224
|
+
const resolvedPath = await (0, promises_1.realpath)((0, node_url_1.fileURLToPath)(resolved));
|
|
225
|
+
const expectedPath = await (0, promises_1.realpath)((0, node_path_1.join)(packageDir, 'index.cjs'));
|
|
226
|
+
expect(resolvedPath).toBe(expectedPath);
|
|
227
|
+
}
|
|
228
|
+
finally {
|
|
229
|
+
await (0, promises_1.rm)(dir, { recursive: true, force: true });
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
it('shoud fall back to specifier when package cannot be resolved', async () => {
|
|
233
|
+
const dir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'runtime-provider-loader-'));
|
|
234
|
+
const configPath = (0, node_path_1.join)(dir, 'runtime.yml');
|
|
235
|
+
await (0, promises_1.writeFile)(configPath, 'providers: []', 'utf8');
|
|
236
|
+
try {
|
|
237
|
+
const loader = new ModuleRuntimeProviderLoader_js_1.ModuleRuntimeProviderLoader();
|
|
238
|
+
// @ts-expect-error accessing private method for test coverage
|
|
239
|
+
const resolved = loader.resolveImportSpecifier('missing-package', configPath);
|
|
240
|
+
expect(resolved).toBe('missing-package');
|
|
241
|
+
}
|
|
242
|
+
finally {
|
|
243
|
+
await (0, promises_1.rm)(dir, { recursive: true, force: true });
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
it('shoud fall back when module resolve APIs are unavailable', async () => {
|
|
247
|
+
const dir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'runtime-provider-loader-'));
|
|
248
|
+
const configPath = (0, node_path_1.join)(dir, 'runtime.yml');
|
|
249
|
+
await (0, promises_1.writeFile)(configPath, 'providers: []', 'utf8');
|
|
250
|
+
const moduleApi = node_module_1.Module;
|
|
251
|
+
const originalResolve = moduleApi._resolveFilename;
|
|
252
|
+
const originalPaths = moduleApi._nodeModulePaths;
|
|
253
|
+
try {
|
|
254
|
+
moduleApi._resolveFilename = undefined;
|
|
255
|
+
moduleApi._nodeModulePaths = undefined;
|
|
256
|
+
const loader = new ModuleRuntimeProviderLoader_js_1.ModuleRuntimeProviderLoader();
|
|
257
|
+
// @ts-expect-error accessing private method for test coverage
|
|
258
|
+
const resolved = loader.resolveImportSpecifier('missing-package', configPath);
|
|
259
|
+
expect(resolved).toBe('missing-package');
|
|
260
|
+
}
|
|
261
|
+
finally {
|
|
262
|
+
moduleApi._resolveFilename = originalResolve;
|
|
263
|
+
moduleApi._nodeModulePaths = originalPaths;
|
|
264
|
+
await (0, promises_1.rm)(dir, { recursive: true, force: true });
|
|
265
|
+
}
|
|
266
|
+
});
|
|
118
267
|
it('shoud accept non-file specifiers unchanged', async () => {
|
|
119
268
|
const loader = new ModuleRuntimeProviderLoader_js_1.ModuleRuntimeProviderLoader();
|
|
120
269
|
const provider = await loader.load({ specifier: 'node:fs' });
|
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
import { RuntimeProvider } from '../RuntimeProvider.js';
|
|
2
2
|
import { RuntimeProviderLoadInput, RuntimeProviderLoader } from '../RuntimeProviderLoader.js';
|
|
3
|
-
type RuntimeProviderModuleShape = RuntimeProvider | (() => RuntimeProvider | Promise<RuntimeProvider>) | {
|
|
4
|
-
runtimeProvider?: RuntimeProvider | (() => RuntimeProvider | Promise<RuntimeProvider>);
|
|
5
|
-
default?: RuntimeProvider | (() => RuntimeProvider | Promise<RuntimeProvider>);
|
|
6
|
-
};
|
|
7
|
-
export declare const __test__: {
|
|
8
|
-
toProvider: (value: RuntimeProviderModuleShape) => Promise<RuntimeProvider>;
|
|
9
|
-
resolveImportSpecifier: (specifier: string, sourceReference?: string) => string;
|
|
10
|
-
};
|
|
11
3
|
export declare class ModuleRuntimeProviderLoader implements RuntimeProviderLoader {
|
|
12
4
|
load(input: RuntimeProviderLoadInput): Promise<RuntimeProvider>;
|
|
5
|
+
private isRuntimeProvider;
|
|
6
|
+
private isRuntimeProviderFactory;
|
|
7
|
+
private toProvider;
|
|
8
|
+
private resolveImportSpecifier;
|
|
13
9
|
}
|
|
14
|
-
export {};
|
|
@@ -1,50 +1,87 @@
|
|
|
1
|
+
import { Module, isBuiltin } from 'node:module';
|
|
1
2
|
import { dirname, isAbsolute, resolve as resolvePath } from 'node:path';
|
|
2
|
-
import { pathToFileURL } from 'node:url';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
(
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
3
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
4
|
+
export class ModuleRuntimeProviderLoader {
|
|
5
|
+
async load(input) {
|
|
6
|
+
const resolvedSpecifier = this.resolveImportSpecifier(input.specifier, input.sourceReference);
|
|
7
|
+
const loaded = await import(resolvedSpecifier);
|
|
8
|
+
return this.toProvider(loaded);
|
|
9
|
+
}
|
|
10
|
+
isRuntimeProvider(value) {
|
|
11
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
12
|
+
}
|
|
13
|
+
isRuntimeProviderFactory(value) {
|
|
14
|
+
return typeof value === 'function';
|
|
15
|
+
}
|
|
16
|
+
async toProvider(value) {
|
|
17
|
+
const candidate = this.isRuntimeProvider(value) &&
|
|
18
|
+
('runtimeProvider' in value || 'default' in value)
|
|
19
|
+
? (value.runtimeProvider ??
|
|
20
|
+
value.default)
|
|
21
|
+
: value;
|
|
22
|
+
if (this.isRuntimeProviderFactory(candidate)) {
|
|
23
|
+
const resolved = await candidate();
|
|
24
|
+
if (!this.isRuntimeProvider(resolved)) {
|
|
25
|
+
throw new Error('Module does not export a valid runtime provider');
|
|
26
|
+
}
|
|
27
|
+
return resolved;
|
|
28
|
+
}
|
|
29
|
+
if (!this.isRuntimeProvider(candidate)) {
|
|
14
30
|
throw new Error('Module does not export a valid runtime provider');
|
|
15
31
|
}
|
|
16
|
-
return
|
|
32
|
+
return candidate;
|
|
17
33
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
34
|
+
resolveImportSpecifier(specifier, sourceReference) {
|
|
35
|
+
if (specifier.startsWith('.') ||
|
|
36
|
+
specifier.startsWith('/') ||
|
|
37
|
+
specifier.startsWith('file:')) {
|
|
38
|
+
if (specifier.startsWith('file:')) {
|
|
39
|
+
return specifier;
|
|
40
|
+
}
|
|
41
|
+
if (isAbsolute(specifier)) {
|
|
42
|
+
return pathToFileURL(specifier).href;
|
|
43
|
+
}
|
|
44
|
+
if (!sourceReference) {
|
|
45
|
+
return pathToFileURL(resolvePath(specifier)).href;
|
|
46
|
+
}
|
|
47
|
+
return pathToFileURL(resolvePath(dirname(sourceReference), specifier))
|
|
48
|
+
.href;
|
|
49
|
+
}
|
|
50
|
+
if (isBuiltin(specifier)) {
|
|
28
51
|
return specifier;
|
|
29
52
|
}
|
|
30
|
-
|
|
31
|
-
|
|
53
|
+
const resolveFromBase = (baseFile) => {
|
|
54
|
+
const moduleApi = Module;
|
|
55
|
+
if (!moduleApi._resolveFilename || !moduleApi._nodeModulePaths) {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
const parent = new Module(baseFile);
|
|
60
|
+
parent.filename = baseFile;
|
|
61
|
+
parent.paths = moduleApi._nodeModulePaths(dirname(baseFile));
|
|
62
|
+
const resolved = moduleApi._resolveFilename(specifier, parent, false, {
|
|
63
|
+
conditions: new Set(['import', 'default', 'require']),
|
|
64
|
+
});
|
|
65
|
+
return pathToFileURL(resolved).href;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
if (sourceReference) {
|
|
72
|
+
const basePath = sourceReference.startsWith('file:')
|
|
73
|
+
? fileURLToPath(sourceReference)
|
|
74
|
+
: sourceReference;
|
|
75
|
+
const resolvedFromSource = resolveFromBase(basePath);
|
|
76
|
+
if (resolvedFromSource) {
|
|
77
|
+
return resolvedFromSource;
|
|
78
|
+
}
|
|
32
79
|
}
|
|
33
|
-
|
|
34
|
-
|
|
80
|
+
const cwdBase = resolvePath(process.cwd(), 'index.js');
|
|
81
|
+
const resolvedFromCwd = resolveFromBase(cwdBase);
|
|
82
|
+
if (resolvedFromCwd) {
|
|
83
|
+
return resolvedFromCwd;
|
|
35
84
|
}
|
|
36
|
-
return
|
|
37
|
-
}
|
|
38
|
-
return specifier;
|
|
39
|
-
};
|
|
40
|
-
export const __test__ = {
|
|
41
|
-
toProvider,
|
|
42
|
-
resolveImportSpecifier,
|
|
43
|
-
};
|
|
44
|
-
export class ModuleRuntimeProviderLoader {
|
|
45
|
-
async load(input) {
|
|
46
|
-
const resolvedSpecifier = resolveImportSpecifier(input.specifier, input.sourceReference);
|
|
47
|
-
const loaded = await import(resolvedSpecifier);
|
|
48
|
-
return toProvider(loaded);
|
|
85
|
+
return specifier;
|
|
49
86
|
}
|
|
50
87
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
1
|
+
import { mkdir, mkdtemp, realpath, rm, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { Module } from 'node:module';
|
|
2
3
|
import { tmpdir } from 'node:os';
|
|
3
4
|
import { join } from 'node:path';
|
|
4
|
-
import { pathToFileURL } from 'node:url';
|
|
5
|
-
import { ModuleRuntimeProviderLoader
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
6
|
+
import { ModuleRuntimeProviderLoader } from './ModuleRuntimeProviderLoader.js';
|
|
6
7
|
describe('ModuleRuntimeProviderLoader.load', () => {
|
|
7
8
|
it('shoud load a provider from a module default object export', async () => {
|
|
8
9
|
const dir = await mkdtemp(join(tmpdir(), 'runtime-provider-loader-'));
|
|
@@ -38,7 +39,9 @@ describe('ModuleRuntimeProviderLoader.load', () => {
|
|
|
38
39
|
});
|
|
39
40
|
it('shoud accept raw runtime providers without module export markers', async () => {
|
|
40
41
|
const provider = {};
|
|
41
|
-
|
|
42
|
+
const loader = new ModuleRuntimeProviderLoader();
|
|
43
|
+
// @ts-expect-error accessing private method for test coverage
|
|
44
|
+
await expect(loader.toProvider(provider)).resolves.toBe(provider);
|
|
42
45
|
});
|
|
43
46
|
it('shoud load a provider from a default function export', async () => {
|
|
44
47
|
const dir = await mkdtemp(join(tmpdir(), 'runtime-provider-loader-'));
|
|
@@ -113,6 +116,152 @@ describe('ModuleRuntimeProviderLoader.load', () => {
|
|
|
113
116
|
await rm(dir, { recursive: true, force: true });
|
|
114
117
|
}
|
|
115
118
|
});
|
|
119
|
+
it('shoud resolve package providers from sourceReference', async () => {
|
|
120
|
+
const dir = await mkdtemp(join(tmpdir(), 'runtime-provider-loader-'));
|
|
121
|
+
const packageDir = join(dir, 'node_modules', 'package-provider');
|
|
122
|
+
const configPath = join(dir, 'runtime.yml');
|
|
123
|
+
await mkdir(packageDir, { recursive: true });
|
|
124
|
+
await writeFile(join(packageDir, 'package.json'), JSON.stringify({
|
|
125
|
+
name: 'package-provider',
|
|
126
|
+
exports: {
|
|
127
|
+
import: './index.cjs',
|
|
128
|
+
require: './index.cjs',
|
|
129
|
+
default: './index.cjs',
|
|
130
|
+
},
|
|
131
|
+
}), 'utf8');
|
|
132
|
+
await writeFile(join(packageDir, 'index.cjs'), 'module.exports = { supportedAdapterOperationsRegistry: { FromPackage: class FromPackage {} } };', 'utf8');
|
|
133
|
+
await writeFile(configPath, 'providers: []', 'utf8');
|
|
134
|
+
try {
|
|
135
|
+
const loader = new ModuleRuntimeProviderLoader();
|
|
136
|
+
const provider = await loader.load({
|
|
137
|
+
specifier: 'package-provider',
|
|
138
|
+
sourceReference: configPath,
|
|
139
|
+
});
|
|
140
|
+
expect(provider.supportedAdapterOperationsRegistry).toBeDefined();
|
|
141
|
+
expect(provider.supportedAdapterOperationsRegistry
|
|
142
|
+
? Object.keys(provider.supportedAdapterOperationsRegistry)
|
|
143
|
+
: []).toContain('FromPackage');
|
|
144
|
+
}
|
|
145
|
+
finally {
|
|
146
|
+
await rm(dir, { recursive: true, force: true });
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
it('shoud resolve package providers from cwd when no reference is provided', async () => {
|
|
150
|
+
const dir = await mkdtemp(join(tmpdir(), 'runtime-provider-loader-'));
|
|
151
|
+
const packageDir = join(dir, 'node_modules', 'cwd-provider');
|
|
152
|
+
const originalCwd = process.cwd();
|
|
153
|
+
await mkdir(packageDir, { recursive: true });
|
|
154
|
+
await writeFile(join(packageDir, 'package.json'), JSON.stringify({
|
|
155
|
+
name: 'cwd-provider',
|
|
156
|
+
exports: {
|
|
157
|
+
import: './index.cjs',
|
|
158
|
+
require: './index.cjs',
|
|
159
|
+
default: './index.cjs',
|
|
160
|
+
},
|
|
161
|
+
}), 'utf8');
|
|
162
|
+
await writeFile(join(packageDir, 'index.cjs'), 'module.exports = { supportedAdapterOperationsRegistry: { FromCwdPackage: class FromCwdPackage {} } };', 'utf8');
|
|
163
|
+
try {
|
|
164
|
+
process.chdir(dir);
|
|
165
|
+
const loader = new ModuleRuntimeProviderLoader();
|
|
166
|
+
const provider = await loader.load({ specifier: 'cwd-provider' });
|
|
167
|
+
expect(provider.supportedAdapterOperationsRegistry).toBeDefined();
|
|
168
|
+
expect(provider.supportedAdapterOperationsRegistry
|
|
169
|
+
? Object.keys(provider.supportedAdapterOperationsRegistry)
|
|
170
|
+
: []).toContain('FromCwdPackage');
|
|
171
|
+
}
|
|
172
|
+
finally {
|
|
173
|
+
process.chdir(originalCwd);
|
|
174
|
+
await rm(dir, { recursive: true, force: true });
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
it('shoud resolve ESM-only package specifiers without loading', async () => {
|
|
178
|
+
const dir = await mkdtemp(join(tmpdir(), 'runtime-provider-loader-'));
|
|
179
|
+
const packageDir = join(dir, 'node_modules', 'esm-only');
|
|
180
|
+
const configPath = join(dir, 'runtime.yml');
|
|
181
|
+
await mkdir(packageDir, { recursive: true });
|
|
182
|
+
await writeFile(join(packageDir, 'package.json'), JSON.stringify({
|
|
183
|
+
name: 'esm-only',
|
|
184
|
+
type: 'module',
|
|
185
|
+
exports: {
|
|
186
|
+
import: './index.mjs',
|
|
187
|
+
},
|
|
188
|
+
}), 'utf8');
|
|
189
|
+
await writeFile(join(packageDir, 'index.mjs'), 'export default {};', 'utf8');
|
|
190
|
+
await writeFile(configPath, 'providers: []', 'utf8');
|
|
191
|
+
try {
|
|
192
|
+
const loader = new ModuleRuntimeProviderLoader();
|
|
193
|
+
// @ts-expect-error accessing private method for test coverage
|
|
194
|
+
const resolved = loader.resolveImportSpecifier('esm-only', configPath);
|
|
195
|
+
const resolvedPath = await realpath(fileURLToPath(resolved));
|
|
196
|
+
const expectedPath = await realpath(join(packageDir, 'index.mjs'));
|
|
197
|
+
expect(resolvedPath).toBe(expectedPath);
|
|
198
|
+
}
|
|
199
|
+
finally {
|
|
200
|
+
await rm(dir, { recursive: true, force: true });
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
it('shoud resolve package providers from file URL sourceReference', async () => {
|
|
204
|
+
const dir = await mkdtemp(join(tmpdir(), 'runtime-provider-loader-'));
|
|
205
|
+
const packageDir = join(dir, 'node_modules', 'file-url-provider');
|
|
206
|
+
const configPath = join(dir, 'runtime.yml');
|
|
207
|
+
await mkdir(packageDir, { recursive: true });
|
|
208
|
+
await writeFile(join(packageDir, 'package.json'), JSON.stringify({
|
|
209
|
+
name: 'file-url-provider',
|
|
210
|
+
exports: {
|
|
211
|
+
import: './index.cjs',
|
|
212
|
+
require: './index.cjs',
|
|
213
|
+
default: './index.cjs',
|
|
214
|
+
},
|
|
215
|
+
}), 'utf8');
|
|
216
|
+
await writeFile(join(packageDir, 'index.cjs'), 'module.exports = {};', 'utf8');
|
|
217
|
+
await writeFile(configPath, 'providers: []', 'utf8');
|
|
218
|
+
try {
|
|
219
|
+
const loader = new ModuleRuntimeProviderLoader();
|
|
220
|
+
// @ts-expect-error accessing private method for test coverage
|
|
221
|
+
const resolved = loader.resolveImportSpecifier('file-url-provider', pathToFileURL(configPath).href);
|
|
222
|
+
const resolvedPath = await realpath(fileURLToPath(resolved));
|
|
223
|
+
const expectedPath = await realpath(join(packageDir, 'index.cjs'));
|
|
224
|
+
expect(resolvedPath).toBe(expectedPath);
|
|
225
|
+
}
|
|
226
|
+
finally {
|
|
227
|
+
await rm(dir, { recursive: true, force: true });
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
it('shoud fall back to specifier when package cannot be resolved', async () => {
|
|
231
|
+
const dir = await mkdtemp(join(tmpdir(), 'runtime-provider-loader-'));
|
|
232
|
+
const configPath = join(dir, 'runtime.yml');
|
|
233
|
+
await writeFile(configPath, 'providers: []', 'utf8');
|
|
234
|
+
try {
|
|
235
|
+
const loader = new ModuleRuntimeProviderLoader();
|
|
236
|
+
// @ts-expect-error accessing private method for test coverage
|
|
237
|
+
const resolved = loader.resolveImportSpecifier('missing-package', configPath);
|
|
238
|
+
expect(resolved).toBe('missing-package');
|
|
239
|
+
}
|
|
240
|
+
finally {
|
|
241
|
+
await rm(dir, { recursive: true, force: true });
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
it('shoud fall back when module resolve APIs are unavailable', async () => {
|
|
245
|
+
const dir = await mkdtemp(join(tmpdir(), 'runtime-provider-loader-'));
|
|
246
|
+
const configPath = join(dir, 'runtime.yml');
|
|
247
|
+
await writeFile(configPath, 'providers: []', 'utf8');
|
|
248
|
+
const moduleApi = Module;
|
|
249
|
+
const originalResolve = moduleApi._resolveFilename;
|
|
250
|
+
const originalPaths = moduleApi._nodeModulePaths;
|
|
251
|
+
try {
|
|
252
|
+
moduleApi._resolveFilename = undefined;
|
|
253
|
+
moduleApi._nodeModulePaths = undefined;
|
|
254
|
+
const loader = new ModuleRuntimeProviderLoader();
|
|
255
|
+
// @ts-expect-error accessing private method for test coverage
|
|
256
|
+
const resolved = loader.resolveImportSpecifier('missing-package', configPath);
|
|
257
|
+
expect(resolved).toBe('missing-package');
|
|
258
|
+
}
|
|
259
|
+
finally {
|
|
260
|
+
moduleApi._resolveFilename = originalResolve;
|
|
261
|
+
moduleApi._nodeModulePaths = originalPaths;
|
|
262
|
+
await rm(dir, { recursive: true, force: true });
|
|
263
|
+
}
|
|
264
|
+
});
|
|
116
265
|
it('shoud accept non-file specifiers unchanged', async () => {
|
|
117
266
|
const loader = new ModuleRuntimeProviderLoader();
|
|
118
267
|
const provider = await loader.load({ specifier: 'node:fs' });
|
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"type": "git",
|
|
9
9
|
"url": "git+https://github.com/terra-graph/core.git"
|
|
10
10
|
},
|
|
11
|
-
"version": "1.0.0-rc.
|
|
11
|
+
"version": "1.0.0-rc.5",
|
|
12
12
|
"main": "./dist/cjs/index.js",
|
|
13
13
|
"module": "./dist/esm/index.js",
|
|
14
14
|
"types": "./dist/esm/index.d.ts",
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"@semantic-release/changelog": "^6.0.3",
|
|
48
48
|
"@semantic-release/commit-analyzer": "^13.0.0",
|
|
49
49
|
"@semantic-release/git": "^10.0.1",
|
|
50
|
+
"@semantic-release/github": "^12.0.6",
|
|
50
51
|
"@semantic-release/npm": "^12.0.1",
|
|
51
52
|
"@semantic-release/release-notes-generator": "^14.0.2",
|
|
52
53
|
"@types/graphlib-dot": "^0.6.4",
|
|
@@ -95,6 +96,7 @@
|
|
|
95
96
|
"access": "public"
|
|
96
97
|
}
|
|
97
98
|
],
|
|
99
|
+
"@semantic-release/github",
|
|
98
100
|
[
|
|
99
101
|
"@semantic-release/git",
|
|
100
102
|
{
|