@holo-js/core 0.1.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/dist/chunk-SVCNZBIQ.mjs +703 -0
- package/dist/index.d.ts +70 -0
- package/dist/index.mjs +235 -0
- package/dist/runtime/index.d.ts +115 -0
- package/dist/runtime/index.mjs +38 -0
- package/package.json +45 -0
|
@@ -0,0 +1,703 @@
|
|
|
1
|
+
// src/portable/dbRuntime.ts
|
|
2
|
+
import {
|
|
3
|
+
createAdapter,
|
|
4
|
+
createDialect,
|
|
5
|
+
createRuntimeConnectionOptions,
|
|
6
|
+
createRuntimeLogger,
|
|
7
|
+
isSupportedDatabaseDriver,
|
|
8
|
+
parseDatabaseDriver,
|
|
9
|
+
resolveRuntimeConnectionManagerOptions
|
|
10
|
+
} from "@holo-js/db";
|
|
11
|
+
|
|
12
|
+
// src/portable/registry.ts
|
|
13
|
+
import { readFile } from "fs/promises";
|
|
14
|
+
import { resolve } from "path";
|
|
15
|
+
import { DEFAULT_HOLO_PROJECT_PATHS } from "@holo-js/db";
|
|
16
|
+
function isRecord(value) {
|
|
17
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
18
|
+
}
|
|
19
|
+
function normalizeLegacyGeneratedProjectRegistry(value) {
|
|
20
|
+
const paths = isRecord(value.paths) ? value.paths : void 0;
|
|
21
|
+
if (paths && typeof paths.jobs !== "string") {
|
|
22
|
+
paths.jobs = DEFAULT_HOLO_PROJECT_PATHS.jobs;
|
|
23
|
+
}
|
|
24
|
+
if (paths && typeof paths.events !== "string") {
|
|
25
|
+
paths.events = DEFAULT_HOLO_PROJECT_PATHS.events;
|
|
26
|
+
}
|
|
27
|
+
if (paths && typeof paths.listeners !== "string") {
|
|
28
|
+
paths.listeners = DEFAULT_HOLO_PROJECT_PATHS.listeners;
|
|
29
|
+
}
|
|
30
|
+
if (!Array.isArray(value.jobs)) {
|
|
31
|
+
value.jobs = [];
|
|
32
|
+
}
|
|
33
|
+
if (!Array.isArray(value.events)) {
|
|
34
|
+
value.events = [];
|
|
35
|
+
}
|
|
36
|
+
if (!Array.isArray(value.listeners)) {
|
|
37
|
+
value.listeners = [];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function isGeneratedProjectRegistry(value) {
|
|
41
|
+
if (!isRecord(value)) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
normalizeLegacyGeneratedProjectRegistry(value);
|
|
45
|
+
return value.version === 1 && isRecord(value.paths) && Array.isArray(value.models) && Array.isArray(value.migrations) && Array.isArray(value.seeders) && Array.isArray(value.commands) && Array.isArray(value.jobs) && Array.isArray(value.events) && Array.isArray(value.listeners);
|
|
46
|
+
}
|
|
47
|
+
function resolveGeneratedProjectRegistryPath(projectRoot) {
|
|
48
|
+
return resolve(projectRoot, ".holo-js", "generated", "registry.json");
|
|
49
|
+
}
|
|
50
|
+
async function loadGeneratedProjectRegistry(projectRoot) {
|
|
51
|
+
const filePath = resolveGeneratedProjectRegistryPath(projectRoot);
|
|
52
|
+
const contents = await readFile(filePath, "utf8").catch(() => void 0);
|
|
53
|
+
if (!contents) {
|
|
54
|
+
return void 0;
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
const parsed = JSON.parse(contents);
|
|
58
|
+
return isGeneratedProjectRegistry(parsed) ? parsed : void 0;
|
|
59
|
+
} catch {
|
|
60
|
+
return void 0;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
var registryInternals = {
|
|
64
|
+
isGeneratedProjectRegistry,
|
|
65
|
+
normalizeLegacyGeneratedProjectRegistry
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// src/portable/holo.ts
|
|
69
|
+
import { resolve as resolve3 } from "path";
|
|
70
|
+
import {
|
|
71
|
+
config as globalConfig,
|
|
72
|
+
configureConfigRuntime,
|
|
73
|
+
createConfigAccessors,
|
|
74
|
+
loadConfigDirectory,
|
|
75
|
+
resetConfigRuntime,
|
|
76
|
+
useConfig as globalUseConfig
|
|
77
|
+
} from "@holo-js/config";
|
|
78
|
+
import {
|
|
79
|
+
configureDB,
|
|
80
|
+
resetDB
|
|
81
|
+
} from "@holo-js/db";
|
|
82
|
+
import {
|
|
83
|
+
configureQueueRuntime,
|
|
84
|
+
getRegisteredQueueJob,
|
|
85
|
+
getQueueRuntime,
|
|
86
|
+
isQueueJobDefinition,
|
|
87
|
+
normalizeQueueJobDefinition,
|
|
88
|
+
registerQueueJob,
|
|
89
|
+
shutdownQueueRuntime,
|
|
90
|
+
unregisterQueueJob
|
|
91
|
+
} from "@holo-js/queue";
|
|
92
|
+
import {
|
|
93
|
+
ensureEventsQueueJobRegistered,
|
|
94
|
+
getRegisteredEvent,
|
|
95
|
+
getRegisteredListener,
|
|
96
|
+
isEventDefinition,
|
|
97
|
+
isListenerDefinition,
|
|
98
|
+
normalizeListenerDefinition,
|
|
99
|
+
registerEvent,
|
|
100
|
+
registerListener,
|
|
101
|
+
unregisterEvent,
|
|
102
|
+
unregisterListener
|
|
103
|
+
} from "@holo-js/events";
|
|
104
|
+
import { createQueueDbRuntimeOptions } from "@holo-js/queue-db";
|
|
105
|
+
import { resetStorageRuntime } from "@holo-js/storage/runtime";
|
|
106
|
+
|
|
107
|
+
// src/runtimeModule.ts
|
|
108
|
+
import { mkdtemp, mkdir, rm, stat, writeFile } from "fs/promises";
|
|
109
|
+
import { basename, extname, join } from "path";
|
|
110
|
+
import { pathToFileURL } from "url";
|
|
111
|
+
var ESBUILD_MODULE_ID = "esbuild";
|
|
112
|
+
async function importModule(specifier) {
|
|
113
|
+
if (process.env.VITEST) {
|
|
114
|
+
return import(
|
|
115
|
+
/* @vite-ignore */
|
|
116
|
+
specifier
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
const indirectEval = globalThis.eval;
|
|
120
|
+
return indirectEval(`import(${JSON.stringify(specifier)})`);
|
|
121
|
+
}
|
|
122
|
+
async function pathExists(path) {
|
|
123
|
+
try {
|
|
124
|
+
await stat(path);
|
|
125
|
+
return true;
|
|
126
|
+
} catch {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
async function writeLoaderTsconfig(projectRoot, tempDir) {
|
|
131
|
+
const projectTsconfigPath = join(projectRoot, "tsconfig.json");
|
|
132
|
+
if (await pathExists(projectTsconfigPath)) {
|
|
133
|
+
return projectTsconfigPath;
|
|
134
|
+
}
|
|
135
|
+
const tsconfigPath = join(tempDir, "tsconfig.json");
|
|
136
|
+
const contents = JSON.stringify({
|
|
137
|
+
compilerOptions: {
|
|
138
|
+
baseUrl: projectRoot,
|
|
139
|
+
paths: {
|
|
140
|
+
"~/*": ["./*"],
|
|
141
|
+
"@/*": ["./*"]
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}, null, 2);
|
|
145
|
+
await writeFile(tsconfigPath, `${contents}
|
|
146
|
+
`, "utf8");
|
|
147
|
+
return tsconfigPath;
|
|
148
|
+
}
|
|
149
|
+
async function bundleRuntimeModule(projectRoot, entryPath) {
|
|
150
|
+
const runtimeTempRoot = join(projectRoot, ".holo-js", "runtime");
|
|
151
|
+
await mkdir(runtimeTempRoot, { recursive: true });
|
|
152
|
+
const tempDir = await mkdtemp(join(runtimeTempRoot, "bundle-"));
|
|
153
|
+
const tsconfigPath = await writeLoaderTsconfig(projectRoot, tempDir);
|
|
154
|
+
const outfile = join(tempDir, `${basename(entryPath, extname(entryPath))}.mjs`);
|
|
155
|
+
const cleanup = async () => {
|
|
156
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
157
|
+
};
|
|
158
|
+
try {
|
|
159
|
+
await runtimeModuleInternals.runEsbuild({
|
|
160
|
+
absWorkingDir: projectRoot,
|
|
161
|
+
bundle: true,
|
|
162
|
+
entryPoints: [entryPath],
|
|
163
|
+
outfile,
|
|
164
|
+
format: "esm",
|
|
165
|
+
logLevel: "silent",
|
|
166
|
+
packages: "external",
|
|
167
|
+
platform: "node",
|
|
168
|
+
target: "node20",
|
|
169
|
+
tsconfig: tsconfigPath,
|
|
170
|
+
sourcemap: false
|
|
171
|
+
});
|
|
172
|
+
return {
|
|
173
|
+
path: outfile,
|
|
174
|
+
cleanup
|
|
175
|
+
};
|
|
176
|
+
} catch (error) {
|
|
177
|
+
await cleanup();
|
|
178
|
+
if (error && typeof error === "object" && Array.isArray(error.errors)) {
|
|
179
|
+
const message = error.errors.map((entry) => {
|
|
180
|
+
if (typeof entry.text === "string" && entry.text.trim()) {
|
|
181
|
+
return entry.text;
|
|
182
|
+
}
|
|
183
|
+
if (typeof entry.message === "string" && entry.message.trim()) {
|
|
184
|
+
return entry.message;
|
|
185
|
+
}
|
|
186
|
+
return "Unknown build error.";
|
|
187
|
+
}).join("\n");
|
|
188
|
+
throw new Error(message);
|
|
189
|
+
}
|
|
190
|
+
if (error instanceof Error && error.message) {
|
|
191
|
+
throw error;
|
|
192
|
+
}
|
|
193
|
+
throw new Error(`Failed to load ${entryPath}.`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
async function importBundledRuntimeModule(projectRoot, entryPath) {
|
|
197
|
+
const bundled = await bundleRuntimeModule(projectRoot, entryPath);
|
|
198
|
+
try {
|
|
199
|
+
return await runtimeModuleInternals.importModule(
|
|
200
|
+
`${pathToFileURL(bundled.path).href}?t=${Date.now()}`
|
|
201
|
+
);
|
|
202
|
+
} finally {
|
|
203
|
+
await bundled.cleanup();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
async function loadEsbuild() {
|
|
207
|
+
return importModule(ESBUILD_MODULE_ID);
|
|
208
|
+
}
|
|
209
|
+
async function runEsbuild(options) {
|
|
210
|
+
const esbuild = await runtimeModuleInternals.loadEsbuild();
|
|
211
|
+
return esbuild.build(options);
|
|
212
|
+
}
|
|
213
|
+
var runtimeModuleInternals = {
|
|
214
|
+
bundleRuntimeModule,
|
|
215
|
+
importModule,
|
|
216
|
+
loadEsbuild,
|
|
217
|
+
pathExists,
|
|
218
|
+
runEsbuild,
|
|
219
|
+
writeLoaderTsconfig
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// src/storageRuntime.ts
|
|
223
|
+
import { mkdir as mkdir2, readFile as readFile2, readdir, rm as rm2, writeFile as writeFile2 } from "fs/promises";
|
|
224
|
+
import { dirname, join as join2, resolve as resolve2 } from "path";
|
|
225
|
+
import { normalizeModuleOptions } from "@holo-js/storage";
|
|
226
|
+
import {
|
|
227
|
+
configureStorageRuntime
|
|
228
|
+
} from "@holo-js/storage/runtime";
|
|
229
|
+
import createS3Driver from "@holo-js/storage/runtime/drivers/s3";
|
|
230
|
+
function resolveStorageKeyPath(root, key) {
|
|
231
|
+
const segments = key.split(":").filter(Boolean);
|
|
232
|
+
if (segments.includes("..")) {
|
|
233
|
+
throw new Error('[Holo Storage] Storage paths must not contain ".." segments.');
|
|
234
|
+
}
|
|
235
|
+
return resolve2(root, ...segments);
|
|
236
|
+
}
|
|
237
|
+
function createFileStorageBackend(root) {
|
|
238
|
+
async function listStorageKeys(currentRoot, prefix = "") {
|
|
239
|
+
const entries = await readdir(currentRoot, { withFileTypes: true }).catch(() => []);
|
|
240
|
+
const keys = [];
|
|
241
|
+
for (const entry of entries) {
|
|
242
|
+
const nextPrefix = prefix ? `${prefix}:${entry.name}` : entry.name;
|
|
243
|
+
const entryPath = join2(currentRoot, entry.name);
|
|
244
|
+
if (entry.isDirectory()) {
|
|
245
|
+
keys.push(...await listStorageKeys(entryPath, nextPrefix));
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
if (entry.isFile()) {
|
|
249
|
+
keys.push(nextPrefix);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return keys;
|
|
253
|
+
}
|
|
254
|
+
return {
|
|
255
|
+
async getItem(key) {
|
|
256
|
+
const value = await this.getItemRaw(key);
|
|
257
|
+
if (value === null) {
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
const serialized = Buffer.isBuffer(value) ? value.toString("utf8") : value instanceof Uint8Array ? Buffer.from(value).toString("utf8") : value instanceof ArrayBuffer ? Buffer.from(value).toString("utf8") : String(value);
|
|
261
|
+
return JSON.parse(serialized);
|
|
262
|
+
},
|
|
263
|
+
async getItemRaw(key) {
|
|
264
|
+
const targetPath = resolveStorageKeyPath(root, key);
|
|
265
|
+
try {
|
|
266
|
+
return await readFile2(targetPath);
|
|
267
|
+
} catch {
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
async setItem(key, value) {
|
|
272
|
+
await this.setItemRaw(key, JSON.stringify(value));
|
|
273
|
+
},
|
|
274
|
+
async setItemRaw(key, value) {
|
|
275
|
+
const targetPath = resolveStorageKeyPath(root, key);
|
|
276
|
+
await mkdir2(dirname(targetPath), { recursive: true });
|
|
277
|
+
await writeFile2(
|
|
278
|
+
targetPath,
|
|
279
|
+
value instanceof ArrayBuffer ? new Uint8Array(value) : value
|
|
280
|
+
);
|
|
281
|
+
},
|
|
282
|
+
async hasItem(key) {
|
|
283
|
+
return await this.getItemRaw(key) !== null;
|
|
284
|
+
},
|
|
285
|
+
async removeItem(key) {
|
|
286
|
+
const targetPath = resolveStorageKeyPath(root, key);
|
|
287
|
+
await rm2(targetPath, { force: true });
|
|
288
|
+
await rm2(`${targetPath}$`, { force: true });
|
|
289
|
+
},
|
|
290
|
+
async getKeys(base = "") {
|
|
291
|
+
const prefix = base.replace(/:+$/, "");
|
|
292
|
+
const keys = await listStorageKeys(root);
|
|
293
|
+
return keys.filter((key) => {
|
|
294
|
+
if (!prefix) {
|
|
295
|
+
return true;
|
|
296
|
+
}
|
|
297
|
+
return key === prefix || key.startsWith(`${prefix}:`);
|
|
298
|
+
});
|
|
299
|
+
},
|
|
300
|
+
async getMeta(key) {
|
|
301
|
+
return this.getItem(`${key}$`);
|
|
302
|
+
},
|
|
303
|
+
async setMeta(key, value) {
|
|
304
|
+
await this.setItem(`${key}$`, value);
|
|
305
|
+
},
|
|
306
|
+
async removeMeta(key) {
|
|
307
|
+
await this.removeItem(`${key}$`);
|
|
308
|
+
},
|
|
309
|
+
async clear(base = "") {
|
|
310
|
+
const prefix = base.replace(/:+$/, "");
|
|
311
|
+
const targetPath = prefix ? resolveStorageKeyPath(root, prefix) : root;
|
|
312
|
+
await rm2(targetPath, { recursive: true, force: true });
|
|
313
|
+
if (!prefix) {
|
|
314
|
+
await mkdir2(root, { recursive: true });
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
function createS3StorageBackend(disk) {
|
|
320
|
+
const driver = createS3Driver({
|
|
321
|
+
bucket: disk.bucket,
|
|
322
|
+
region: disk.region,
|
|
323
|
+
endpoint: disk.endpoint,
|
|
324
|
+
accessKeyId: disk.accessKeyId,
|
|
325
|
+
secretAccessKey: disk.secretAccessKey,
|
|
326
|
+
sessionToken: disk.sessionToken,
|
|
327
|
+
forcePathStyleEndpoint: disk.forcePathStyleEndpoint
|
|
328
|
+
});
|
|
329
|
+
return {
|
|
330
|
+
getItem(key) {
|
|
331
|
+
return driver.getItem(key);
|
|
332
|
+
},
|
|
333
|
+
getItemRaw: driver.getItemRaw,
|
|
334
|
+
setItem: driver.setItem,
|
|
335
|
+
setItemRaw: driver.setItemRaw,
|
|
336
|
+
hasItem: driver.hasItem,
|
|
337
|
+
removeItem: driver.removeItem,
|
|
338
|
+
getKeys: driver.getKeys,
|
|
339
|
+
getMeta(key) {
|
|
340
|
+
return driver.getMeta(key);
|
|
341
|
+
},
|
|
342
|
+
clear: driver.clear
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
function configurePlainNodeStorageRuntime(projectRoot, loadedConfig) {
|
|
346
|
+
const normalizedStorage = normalizeModuleOptions({
|
|
347
|
+
defaultDisk: loadedConfig.storage.defaultDisk,
|
|
348
|
+
routePrefix: loadedConfig.storage.routePrefix,
|
|
349
|
+
disks: loadedConfig.storage.disks
|
|
350
|
+
});
|
|
351
|
+
const backends = /* @__PURE__ */ new Map();
|
|
352
|
+
configureStorageRuntime({
|
|
353
|
+
getRuntimeConfig: () => ({
|
|
354
|
+
holoStorage: normalizedStorage,
|
|
355
|
+
holo: { appUrl: loadedConfig.app.url }
|
|
356
|
+
}),
|
|
357
|
+
getStorage: (base) => {
|
|
358
|
+
const diskName = base.replace(/^holo:/, "");
|
|
359
|
+
const disk = normalizedStorage.disks[diskName];
|
|
360
|
+
if (!disk) {
|
|
361
|
+
throw new Error(`[Holo Storage] Disk "${diskName}" is not configured.`);
|
|
362
|
+
}
|
|
363
|
+
const existing = backends.get(diskName);
|
|
364
|
+
if (existing) {
|
|
365
|
+
return existing;
|
|
366
|
+
}
|
|
367
|
+
const backend = disk.driver === "s3" ? createS3StorageBackend(disk) : createFileStorageBackend(
|
|
368
|
+
resolve2(projectRoot, disk.root)
|
|
369
|
+
);
|
|
370
|
+
backends.set(diskName, backend);
|
|
371
|
+
return backend;
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// src/portable/holo.ts
|
|
377
|
+
function getRuntimeState() {
|
|
378
|
+
const runtime = globalThis;
|
|
379
|
+
runtime.__holoRuntime__ ??= {};
|
|
380
|
+
return runtime.__holoRuntime__;
|
|
381
|
+
}
|
|
382
|
+
function resolveQueueJobExport(moduleValue) {
|
|
383
|
+
const exports = moduleValue;
|
|
384
|
+
if (isQueueJobDefinition(exports.default)) {
|
|
385
|
+
return exports.default;
|
|
386
|
+
}
|
|
387
|
+
return Object.values(exports).find((value) => isQueueJobDefinition(value));
|
|
388
|
+
}
|
|
389
|
+
var HOLO_EVENT_DEFINITION_MARKER = /* @__PURE__ */ Symbol.for("holo-js.events.definition");
|
|
390
|
+
var HOLO_LISTENER_DEFINITION_MARKER = /* @__PURE__ */ Symbol.for("holo-js.events.listener");
|
|
391
|
+
function hasEventDefinitionMarker(value) {
|
|
392
|
+
return !!value && typeof value === "object" && HOLO_EVENT_DEFINITION_MARKER in value;
|
|
393
|
+
}
|
|
394
|
+
function hasListenerDefinitionMarker(value) {
|
|
395
|
+
return !!value && typeof value === "object" && HOLO_LISTENER_DEFINITION_MARKER in value;
|
|
396
|
+
}
|
|
397
|
+
function resolveEventExport(moduleValue) {
|
|
398
|
+
const exports = moduleValue;
|
|
399
|
+
if (hasEventDefinitionMarker(exports.default)) {
|
|
400
|
+
return exports.default;
|
|
401
|
+
}
|
|
402
|
+
return Object.values(exports).find((value) => hasEventDefinitionMarker(value));
|
|
403
|
+
}
|
|
404
|
+
function resolveListenerExport(moduleValue) {
|
|
405
|
+
const exports = moduleValue;
|
|
406
|
+
if (hasListenerDefinitionMarker(exports.default) || isListenerDefinition(exports.default)) {
|
|
407
|
+
return exports.default;
|
|
408
|
+
}
|
|
409
|
+
return Object.values(exports).find((value) => hasListenerDefinitionMarker(value) || isListenerDefinition(value));
|
|
410
|
+
}
|
|
411
|
+
async function importRuntimeModule(projectRoot, filePath) {
|
|
412
|
+
return importBundledRuntimeModule(projectRoot, filePath);
|
|
413
|
+
}
|
|
414
|
+
async function registerProjectQueueJobs(projectRoot, registry) {
|
|
415
|
+
if (!registry || registry.jobs.length === 0) {
|
|
416
|
+
return Object.freeze([]);
|
|
417
|
+
}
|
|
418
|
+
const registeredJobNames = [];
|
|
419
|
+
try {
|
|
420
|
+
for (const entry of registry.jobs) {
|
|
421
|
+
const existing = getRegisteredQueueJob(entry.name);
|
|
422
|
+
if (existing && !existing.sourcePath) {
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
const moduleValue = await importRuntimeModule(projectRoot, resolve3(projectRoot, entry.sourcePath));
|
|
426
|
+
const job = resolveQueueJobExport(moduleValue);
|
|
427
|
+
if (!job) {
|
|
428
|
+
throw new Error(`Discovered job "${entry.sourcePath}" does not export a Holo job.`);
|
|
429
|
+
}
|
|
430
|
+
registerQueueJob(normalizeQueueJobDefinition(job), {
|
|
431
|
+
name: entry.name,
|
|
432
|
+
sourcePath: entry.sourcePath,
|
|
433
|
+
replaceExisting: !!existing?.sourcePath
|
|
434
|
+
});
|
|
435
|
+
registeredJobNames.push(entry.name);
|
|
436
|
+
}
|
|
437
|
+
} catch (error) {
|
|
438
|
+
unregisterProjectQueueJobs(registeredJobNames);
|
|
439
|
+
throw error;
|
|
440
|
+
}
|
|
441
|
+
return Object.freeze(registeredJobNames);
|
|
442
|
+
}
|
|
443
|
+
function unregisterProjectQueueJobs(jobNames) {
|
|
444
|
+
for (const jobName of jobNames) {
|
|
445
|
+
unregisterQueueJob(jobName);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
async function registerProjectEventsAndListeners(projectRoot, registry) {
|
|
449
|
+
if (!registry || registry.events.length === 0 && registry.listeners.length === 0) {
|
|
450
|
+
return Object.freeze({
|
|
451
|
+
eventNames: Object.freeze([]),
|
|
452
|
+
listenerIds: Object.freeze([])
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
const registeredEventNames = [];
|
|
456
|
+
const registeredListenerIds = [];
|
|
457
|
+
try {
|
|
458
|
+
for (const entry of registry.events) {
|
|
459
|
+
const existing = getRegisteredEvent(entry.name);
|
|
460
|
+
if (existing && !existing.sourcePath) {
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
const moduleValue = await importRuntimeModule(projectRoot, resolve3(projectRoot, entry.sourcePath));
|
|
464
|
+
const event = resolveEventExport(moduleValue);
|
|
465
|
+
if (!event || !isEventDefinition(event)) {
|
|
466
|
+
throw new Error(`Discovered event "${entry.sourcePath}" does not export a Holo event.`);
|
|
467
|
+
}
|
|
468
|
+
registerEvent(event, {
|
|
469
|
+
name: entry.name,
|
|
470
|
+
sourcePath: entry.sourcePath,
|
|
471
|
+
replaceExisting: !!existing?.sourcePath
|
|
472
|
+
});
|
|
473
|
+
registeredEventNames.push(entry.name);
|
|
474
|
+
}
|
|
475
|
+
for (const entry of registry.listeners) {
|
|
476
|
+
const existing = getRegisteredListener(entry.id);
|
|
477
|
+
if (existing && !existing.sourcePath) {
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
const moduleValue = await importRuntimeModule(projectRoot, resolve3(projectRoot, entry.sourcePath));
|
|
481
|
+
const listener = resolveListenerExport(moduleValue);
|
|
482
|
+
if (!listener) {
|
|
483
|
+
throw new Error(`Discovered listener "${entry.sourcePath}" does not export a Holo listener.`);
|
|
484
|
+
}
|
|
485
|
+
const normalizedListener = normalizeListenerDefinition(listener);
|
|
486
|
+
registerListener({
|
|
487
|
+
...normalizedListener,
|
|
488
|
+
listensTo: entry.eventNames
|
|
489
|
+
}, {
|
|
490
|
+
id: entry.id,
|
|
491
|
+
sourcePath: entry.sourcePath,
|
|
492
|
+
replaceExisting: !!existing?.sourcePath
|
|
493
|
+
});
|
|
494
|
+
registeredListenerIds.push(entry.id);
|
|
495
|
+
}
|
|
496
|
+
} catch (error) {
|
|
497
|
+
unregisterProjectEventsAndListeners(registeredEventNames, registeredListenerIds);
|
|
498
|
+
throw error;
|
|
499
|
+
}
|
|
500
|
+
return Object.freeze({
|
|
501
|
+
eventNames: Object.freeze(registeredEventNames),
|
|
502
|
+
listenerIds: Object.freeze(registeredListenerIds)
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
function unregisterProjectEventsAndListeners(eventNames, listenerIds) {
|
|
506
|
+
for (const listenerId of listenerIds) {
|
|
507
|
+
unregisterListener(listenerId);
|
|
508
|
+
}
|
|
509
|
+
for (const eventName of eventNames) {
|
|
510
|
+
unregisterEvent(eventName);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
async function createHolo(projectRoot, options = {}) {
|
|
514
|
+
const loadedConfig = await loadConfigDirectory(projectRoot, {
|
|
515
|
+
envName: options.envName,
|
|
516
|
+
preferCache: options.preferCache,
|
|
517
|
+
processEnv: options.processEnv
|
|
518
|
+
});
|
|
519
|
+
const runtimeConfig = {
|
|
520
|
+
db: loadedConfig.database,
|
|
521
|
+
queue: loadedConfig.queue
|
|
522
|
+
};
|
|
523
|
+
const manager = resolveRuntimeConnectionManagerOptions(runtimeConfig);
|
|
524
|
+
const registry = await loadGeneratedProjectRegistry(projectRoot);
|
|
525
|
+
const accessors = createConfigAccessors(loadedConfig.all);
|
|
526
|
+
const runtimeOwnedQueueJobNames = [];
|
|
527
|
+
const runtimeOwnedEventNames = [];
|
|
528
|
+
const runtimeOwnedListenerIds = [];
|
|
529
|
+
const runtime = {
|
|
530
|
+
projectRoot,
|
|
531
|
+
loadedConfig,
|
|
532
|
+
registry,
|
|
533
|
+
manager,
|
|
534
|
+
runtimeConfig,
|
|
535
|
+
get queue() {
|
|
536
|
+
return getQueueRuntime();
|
|
537
|
+
},
|
|
538
|
+
initialized: false,
|
|
539
|
+
useConfig: accessors.useConfig,
|
|
540
|
+
config: accessors.config,
|
|
541
|
+
async initialize() {
|
|
542
|
+
if (runtime.initialized) {
|
|
543
|
+
throw new Error("Holo runtime is already initialized.");
|
|
544
|
+
}
|
|
545
|
+
if (getRuntimeState().current) {
|
|
546
|
+
throw new Error("A Holo runtime is already initialized for this process.");
|
|
547
|
+
}
|
|
548
|
+
configureConfigRuntime(loadedConfig.all);
|
|
549
|
+
configureDB(manager);
|
|
550
|
+
try {
|
|
551
|
+
await manager.initializeAll();
|
|
552
|
+
configureQueueRuntime({
|
|
553
|
+
config: loadedConfig.queue,
|
|
554
|
+
...createQueueDbRuntimeOptions()
|
|
555
|
+
});
|
|
556
|
+
ensureEventsQueueJobRegistered();
|
|
557
|
+
configurePlainNodeStorageRuntime(projectRoot, loadedConfig);
|
|
558
|
+
const eventRegistration = await registerProjectEventsAndListeners(projectRoot, registry);
|
|
559
|
+
runtimeOwnedEventNames.splice(0, runtimeOwnedEventNames.length, ...eventRegistration.eventNames);
|
|
560
|
+
runtimeOwnedListenerIds.splice(0, runtimeOwnedListenerIds.length, ...eventRegistration.listenerIds);
|
|
561
|
+
if (options.registerProjectQueueJobs === true) {
|
|
562
|
+
runtimeOwnedQueueJobNames.splice(0, runtimeOwnedQueueJobNames.length);
|
|
563
|
+
runtimeOwnedQueueJobNames.push(...await registerProjectQueueJobs(projectRoot, registry));
|
|
564
|
+
}
|
|
565
|
+
runtime.initialized = true;
|
|
566
|
+
getRuntimeState().current = runtime;
|
|
567
|
+
} catch (error) {
|
|
568
|
+
unregisterProjectEventsAndListeners(runtimeOwnedEventNames, runtimeOwnedListenerIds);
|
|
569
|
+
runtimeOwnedEventNames.splice(0, runtimeOwnedEventNames.length);
|
|
570
|
+
runtimeOwnedListenerIds.splice(0, runtimeOwnedListenerIds.length);
|
|
571
|
+
unregisterProjectQueueJobs(runtimeOwnedQueueJobNames);
|
|
572
|
+
runtimeOwnedQueueJobNames.splice(0, runtimeOwnedQueueJobNames.length);
|
|
573
|
+
await manager.disconnectAll().catch(() => {
|
|
574
|
+
});
|
|
575
|
+
resetDB();
|
|
576
|
+
resetStorageRuntime();
|
|
577
|
+
await shutdownQueueRuntime();
|
|
578
|
+
resetConfigRuntime();
|
|
579
|
+
getRuntimeState().current = void 0;
|
|
580
|
+
throw error;
|
|
581
|
+
}
|
|
582
|
+
},
|
|
583
|
+
async shutdown() {
|
|
584
|
+
try {
|
|
585
|
+
if (runtime.initialized) {
|
|
586
|
+
await manager.disconnectAll();
|
|
587
|
+
}
|
|
588
|
+
} finally {
|
|
589
|
+
runtime.initialized = false;
|
|
590
|
+
if (getRuntimeState().current === runtime) {
|
|
591
|
+
getRuntimeState().current = void 0;
|
|
592
|
+
}
|
|
593
|
+
unregisterProjectEventsAndListeners(runtimeOwnedEventNames, runtimeOwnedListenerIds);
|
|
594
|
+
runtimeOwnedEventNames.splice(0, runtimeOwnedEventNames.length);
|
|
595
|
+
runtimeOwnedListenerIds.splice(0, runtimeOwnedListenerIds.length);
|
|
596
|
+
unregisterProjectQueueJobs(runtimeOwnedQueueJobNames);
|
|
597
|
+
runtimeOwnedQueueJobNames.splice(0, runtimeOwnedQueueJobNames.length);
|
|
598
|
+
resetDB();
|
|
599
|
+
resetStorageRuntime();
|
|
600
|
+
await shutdownQueueRuntime();
|
|
601
|
+
resetConfigRuntime();
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
return runtime;
|
|
606
|
+
}
|
|
607
|
+
async function initializeHolo(projectRoot, options = {}) {
|
|
608
|
+
const state = getRuntimeState();
|
|
609
|
+
const resolvedProjectRoot = resolve3(projectRoot);
|
|
610
|
+
const current = state.current;
|
|
611
|
+
if (current) {
|
|
612
|
+
if (resolve3(current.projectRoot) !== resolvedProjectRoot) {
|
|
613
|
+
throw new Error(`A Holo runtime is already initialized for "${current.projectRoot}".`);
|
|
614
|
+
}
|
|
615
|
+
return current;
|
|
616
|
+
}
|
|
617
|
+
if (state.pending) {
|
|
618
|
+
if (state.pendingProjectRoot && resolve3(state.pendingProjectRoot) !== resolvedProjectRoot) {
|
|
619
|
+
throw new Error(`A Holo runtime is already initializing for "${state.pendingProjectRoot}".`);
|
|
620
|
+
}
|
|
621
|
+
return state.pending;
|
|
622
|
+
}
|
|
623
|
+
const pending = (async () => {
|
|
624
|
+
const runtime = await createHolo(projectRoot, options);
|
|
625
|
+
await runtime.initialize();
|
|
626
|
+
return runtime;
|
|
627
|
+
})();
|
|
628
|
+
state.pending = pending;
|
|
629
|
+
state.pendingProjectRoot = resolvedProjectRoot;
|
|
630
|
+
try {
|
|
631
|
+
return await pending;
|
|
632
|
+
} finally {
|
|
633
|
+
if (state.pending === pending) {
|
|
634
|
+
state.pending = void 0;
|
|
635
|
+
state.pendingProjectRoot = void 0;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
function peekHolo() {
|
|
640
|
+
return getRuntimeState().current;
|
|
641
|
+
}
|
|
642
|
+
async function ensureHolo(projectRoot, options = {}) {
|
|
643
|
+
const current = peekHolo();
|
|
644
|
+
if (!current) {
|
|
645
|
+
return initializeHolo(projectRoot, options);
|
|
646
|
+
}
|
|
647
|
+
if (resolve3(current.projectRoot) !== resolve3(projectRoot)) {
|
|
648
|
+
throw new Error(`A Holo runtime is already initialized for "${current.projectRoot}".`);
|
|
649
|
+
}
|
|
650
|
+
return current;
|
|
651
|
+
}
|
|
652
|
+
function getHolo() {
|
|
653
|
+
const current = getRuntimeState().current;
|
|
654
|
+
if (!current) {
|
|
655
|
+
throw new Error("Holo runtime is not initialized.");
|
|
656
|
+
}
|
|
657
|
+
return current;
|
|
658
|
+
}
|
|
659
|
+
async function resetHoloRuntime() {
|
|
660
|
+
const current = getRuntimeState().current;
|
|
661
|
+
getRuntimeState().pending = void 0;
|
|
662
|
+
getRuntimeState().pendingProjectRoot = void 0;
|
|
663
|
+
if (!current) {
|
|
664
|
+
resetDB();
|
|
665
|
+
resetStorageRuntime();
|
|
666
|
+
await shutdownQueueRuntime();
|
|
667
|
+
resetConfigRuntime();
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
await current.shutdown();
|
|
671
|
+
}
|
|
672
|
+
function getConfigValue(path) {
|
|
673
|
+
return globalConfig(path);
|
|
674
|
+
}
|
|
675
|
+
function getConfigSection(key) {
|
|
676
|
+
return globalUseConfig(key);
|
|
677
|
+
}
|
|
678
|
+
var holoRuntimeInternals = {
|
|
679
|
+
getConfigSection,
|
|
680
|
+
getConfigValue
|
|
681
|
+
};
|
|
682
|
+
|
|
683
|
+
export {
|
|
684
|
+
createAdapter,
|
|
685
|
+
createDialect,
|
|
686
|
+
createRuntimeConnectionOptions,
|
|
687
|
+
createRuntimeLogger,
|
|
688
|
+
isSupportedDatabaseDriver,
|
|
689
|
+
parseDatabaseDriver,
|
|
690
|
+
resolveRuntimeConnectionManagerOptions,
|
|
691
|
+
resolveGeneratedProjectRegistryPath,
|
|
692
|
+
loadGeneratedProjectRegistry,
|
|
693
|
+
registryInternals,
|
|
694
|
+
resolveStorageKeyPath,
|
|
695
|
+
configurePlainNodeStorageRuntime,
|
|
696
|
+
createHolo,
|
|
697
|
+
initializeHolo,
|
|
698
|
+
peekHolo,
|
|
699
|
+
ensureHolo,
|
|
700
|
+
getHolo,
|
|
701
|
+
resetHoloRuntime,
|
|
702
|
+
holoRuntimeInternals
|
|
703
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { GeneratedProjectRegistry, HoloRuntime, CreateHoloOptions } from './runtime/index.js';
|
|
2
|
+
export { createHolo, ensureHolo, getHolo, holoRuntimeInternals, initializeHolo, loadGeneratedProjectRegistry, peekHolo, registryInternals, resetHoloRuntime, resolveGeneratedProjectRegistryPath } from './runtime/index.js';
|
|
3
|
+
import { HoloConfigMap, LoadedHoloConfig, DotPath, ValueAtPath } from '@holo-js/config';
|
|
4
|
+
export { createRuntimeConnectionOptions, resolveRuntimeConnectionManagerOptions } from '@holo-js/db';
|
|
5
|
+
import '@holo-js/queue';
|
|
6
|
+
|
|
7
|
+
declare function resolveStorageKeyPath(root: string, key: string): string;
|
|
8
|
+
|
|
9
|
+
interface HoloAdapterProject<TCustom extends HoloConfigMap = HoloConfigMap> {
|
|
10
|
+
readonly projectRoot: string;
|
|
11
|
+
readonly config: LoadedHoloConfig<TCustom>;
|
|
12
|
+
readonly registry?: GeneratedProjectRegistry;
|
|
13
|
+
readonly runtime: HoloRuntime<TCustom>;
|
|
14
|
+
}
|
|
15
|
+
interface HoloFrameworkOptions extends CreateHoloOptions {
|
|
16
|
+
readonly projectRoot?: string;
|
|
17
|
+
}
|
|
18
|
+
interface ResolvedHoloFrameworkOptions {
|
|
19
|
+
readonly projectRoot: string;
|
|
20
|
+
readonly runtime: CreateHoloOptions;
|
|
21
|
+
}
|
|
22
|
+
interface HoloAdapterProjectAccessors<TCustom extends HoloConfigMap = HoloConfigMap> {
|
|
23
|
+
getApp(): Promise<HoloAdapterProject<TCustom>>;
|
|
24
|
+
getProject(): Promise<HoloAdapterProject<TCustom>>;
|
|
25
|
+
useConfig<TKey extends Extract<keyof LoadedHoloConfig<TCustom>['all'], string>>(key: TKey): Promise<LoadedHoloConfig<TCustom>['all'][TKey]>;
|
|
26
|
+
useConfig<TPath extends DotPath<LoadedHoloConfig<TCustom>['all']>>(path: TPath): Promise<ValueAtPath<LoadedHoloConfig<TCustom>['all'], TPath>>;
|
|
27
|
+
config<TPath extends DotPath<LoadedHoloConfig<TCustom>['all']>>(path: TPath): Promise<ValueAtPath<LoadedHoloConfig<TCustom>['all'], TPath>>;
|
|
28
|
+
}
|
|
29
|
+
interface HoloFrameworkAdapterState<TProject extends HoloAdapterProject = HoloAdapterProject> {
|
|
30
|
+
readonly projectRoot?: string;
|
|
31
|
+
readonly project?: TProject;
|
|
32
|
+
readonly sourceSignature?: string;
|
|
33
|
+
}
|
|
34
|
+
interface HoloAdapterCapabilities {
|
|
35
|
+
readonly config: 'layered-env-and-cache';
|
|
36
|
+
readonly discovery: 'generated-registries';
|
|
37
|
+
readonly runtime: 'singleton-runtime';
|
|
38
|
+
readonly requestContext: 'typed-project-accessors';
|
|
39
|
+
readonly typing: 'inferred-file-and-dot-config';
|
|
40
|
+
readonly rendering: 'framework-owned';
|
|
41
|
+
readonly hosting: 'runtime-agnostic';
|
|
42
|
+
}
|
|
43
|
+
declare const HOLO_MINIMUM_ADAPTER_CAPABILITIES: Readonly<HoloAdapterCapabilities>;
|
|
44
|
+
declare function defineHoloAdapterCapabilities<TCapabilities extends HoloAdapterCapabilities>(capabilities: TCapabilities): Readonly<TCapabilities>;
|
|
45
|
+
declare function resolveHoloFrameworkOptions(options?: HoloFrameworkOptions): ResolvedHoloFrameworkOptions;
|
|
46
|
+
declare function createHoloProjectAccessors<TCustom extends HoloConfigMap = HoloConfigMap>(resolveProject: () => Promise<HoloAdapterProject<TCustom>>): HoloAdapterProjectAccessors<TCustom>;
|
|
47
|
+
declare function resetSingletonFrameworkProject(stateKey: string): Promise<void>;
|
|
48
|
+
interface CreateHoloFrameworkAdapterOptions {
|
|
49
|
+
readonly stateKey: string;
|
|
50
|
+
readonly displayName: string;
|
|
51
|
+
readonly capabilities?: HoloAdapterCapabilities;
|
|
52
|
+
}
|
|
53
|
+
declare function createHoloFrameworkAdapter<TOptions extends HoloFrameworkOptions = HoloFrameworkOptions>(options: CreateHoloFrameworkAdapterOptions): {
|
|
54
|
+
capabilities: Readonly<HoloAdapterCapabilities>;
|
|
55
|
+
createProject: <TCustom extends HoloConfigMap = object>(projectOptions?: TOptions) => Promise<HoloAdapterProject<TCustom>>;
|
|
56
|
+
initializeProject: <TCustom extends HoloConfigMap = object>(projectOptions?: TOptions) => Promise<HoloAdapterProject<TCustom>>;
|
|
57
|
+
createHelpers: <TCustom extends HoloConfigMap = object>(projectOptions?: TOptions) => HoloAdapterProjectAccessors<TCustom>;
|
|
58
|
+
resetProject: () => Promise<void>;
|
|
59
|
+
internals: {
|
|
60
|
+
getState: () => HoloFrameworkAdapterState<HoloAdapterProject<object>>;
|
|
61
|
+
resolveOptions: (projectOptions?: TOptions) => ResolvedHoloFrameworkOptions;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
declare function createHoloAdapterProject<TCustom extends HoloConfigMap = HoloConfigMap>(projectRoot: string, options?: CreateHoloOptions): Promise<HoloAdapterProject<TCustom>>;
|
|
65
|
+
declare function initializeHoloAdapterProject<TCustom extends HoloConfigMap = HoloConfigMap>(projectRoot: string, options?: CreateHoloOptions): Promise<HoloAdapterProject<TCustom>>;
|
|
66
|
+
declare const adapterInternals: {
|
|
67
|
+
resolveStorageKeyPath: typeof resolveStorageKeyPath;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export { type CreateHoloFrameworkAdapterOptions, CreateHoloOptions, HOLO_MINIMUM_ADAPTER_CAPABILITIES, type HoloAdapterCapabilities, type HoloAdapterProject, type HoloAdapterProjectAccessors, type HoloFrameworkAdapterState, type HoloFrameworkOptions, HoloRuntime, type ResolvedHoloFrameworkOptions, adapterInternals, createHoloAdapterProject, createHoloFrameworkAdapter, createHoloProjectAccessors, defineHoloAdapterCapabilities, initializeHoloAdapterProject, resetSingletonFrameworkProject, resolveHoloFrameworkOptions };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import {
|
|
2
|
+
configurePlainNodeStorageRuntime,
|
|
3
|
+
createHolo,
|
|
4
|
+
createRuntimeConnectionOptions,
|
|
5
|
+
ensureHolo,
|
|
6
|
+
getHolo,
|
|
7
|
+
holoRuntimeInternals,
|
|
8
|
+
initializeHolo,
|
|
9
|
+
loadGeneratedProjectRegistry,
|
|
10
|
+
peekHolo,
|
|
11
|
+
registryInternals,
|
|
12
|
+
resetHoloRuntime,
|
|
13
|
+
resolveGeneratedProjectRegistryPath,
|
|
14
|
+
resolveRuntimeConnectionManagerOptions,
|
|
15
|
+
resolveStorageKeyPath
|
|
16
|
+
} from "./chunk-SVCNZBIQ.mjs";
|
|
17
|
+
|
|
18
|
+
// src/adapter.ts
|
|
19
|
+
import { readdir, stat } from "fs/promises";
|
|
20
|
+
import { extname, join, resolve } from "path";
|
|
21
|
+
import { configureConfigRuntime, resolveEnvironmentFileOrder } from "@holo-js/config";
|
|
22
|
+
import { configureDB } from "@holo-js/db";
|
|
23
|
+
import { configureQueueRuntime } from "@holo-js/queue";
|
|
24
|
+
import { createQueueDbRuntimeOptions } from "@holo-js/queue-db";
|
|
25
|
+
import {
|
|
26
|
+
resetStorageRuntime
|
|
27
|
+
} from "@holo-js/storage/runtime";
|
|
28
|
+
var HOLO_MINIMUM_ADAPTER_CAPABILITIES = Object.freeze({
|
|
29
|
+
config: "layered-env-and-cache",
|
|
30
|
+
discovery: "generated-registries",
|
|
31
|
+
runtime: "singleton-runtime",
|
|
32
|
+
requestContext: "typed-project-accessors",
|
|
33
|
+
typing: "inferred-file-and-dot-config",
|
|
34
|
+
rendering: "framework-owned",
|
|
35
|
+
hosting: "runtime-agnostic"
|
|
36
|
+
});
|
|
37
|
+
function defineHoloAdapterCapabilities(capabilities) {
|
|
38
|
+
return Object.freeze({ ...capabilities });
|
|
39
|
+
}
|
|
40
|
+
function getFrameworkAdapterStateContainer(stateKey) {
|
|
41
|
+
const runtime = globalThis;
|
|
42
|
+
runtime.__holoFrameworkAdapters__ ??= {};
|
|
43
|
+
runtime.__holoFrameworkAdapters__[stateKey] ??= {};
|
|
44
|
+
return runtime.__holoFrameworkAdapters__[stateKey];
|
|
45
|
+
}
|
|
46
|
+
async function resolveFileStamp(filePath) {
|
|
47
|
+
try {
|
|
48
|
+
const metadata = await stat(filePath);
|
|
49
|
+
return `${filePath}:${metadata.size}:${metadata.mtimeMs}`;
|
|
50
|
+
} catch {
|
|
51
|
+
return `${filePath}:missing`;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
var CONFIG_EXTENSION_PRIORITY = [".ts", ".mts", ".js", ".mjs", ".cts", ".cjs"];
|
|
55
|
+
var SUPPORTED_CONFIG_EXTENSIONS = new Set(CONFIG_EXTENSION_PRIORITY);
|
|
56
|
+
async function resolveConfigDirectoryStamp(projectRoot) {
|
|
57
|
+
const configDir = resolve(projectRoot, "config");
|
|
58
|
+
const entries = await readdir(configDir, { withFileTypes: true }).catch(() => []);
|
|
59
|
+
const files = entries.filter((entry) => entry.isFile() && SUPPORTED_CONFIG_EXTENSIONS.has(extname(entry.name))).map((entry) => join(configDir, entry.name)).sort((left, right) => left.localeCompare(right));
|
|
60
|
+
const stamps = await Promise.all(files.map(resolveFileStamp));
|
|
61
|
+
return stamps.join("|");
|
|
62
|
+
}
|
|
63
|
+
async function resolveProjectSourceSignature(projectRoot, envName) {
|
|
64
|
+
const registryStamp = await resolveFileStamp(resolve(projectRoot, ".holo-js/generated/registry.json"));
|
|
65
|
+
const envFiles = resolveEnvironmentFileOrder(envName).map((relativeName) => resolve(projectRoot, relativeName));
|
|
66
|
+
const envStamps = await Promise.all(envFiles.map(resolveFileStamp));
|
|
67
|
+
const configStamp = await resolveConfigDirectoryStamp(projectRoot);
|
|
68
|
+
const registry = await loadGeneratedProjectRegistry(projectRoot);
|
|
69
|
+
const jobStamps = await Promise.all((registry?.jobs ?? []).map((entry) => resolveFileStamp(resolve(projectRoot, entry.sourcePath))));
|
|
70
|
+
const eventStamps = await Promise.all((registry?.events ?? []).map((entry) => resolveFileStamp(resolve(projectRoot, entry.sourcePath))));
|
|
71
|
+
const listenerStamps = await Promise.all((registry?.listeners ?? []).map((entry) => resolveFileStamp(resolve(projectRoot, entry.sourcePath))));
|
|
72
|
+
return [configStamp, registryStamp, ...envStamps, ...jobStamps, ...eventStamps, ...listenerStamps].join("||");
|
|
73
|
+
}
|
|
74
|
+
function resolveHoloFrameworkOptions(options = {}) {
|
|
75
|
+
const processEnv = options.processEnv ?? process.env;
|
|
76
|
+
return {
|
|
77
|
+
projectRoot: resolve(options.projectRoot ?? process.cwd()),
|
|
78
|
+
runtime: {
|
|
79
|
+
envName: options.envName,
|
|
80
|
+
preferCache: options.preferCache ?? processEnv.NODE_ENV === "production",
|
|
81
|
+
processEnv,
|
|
82
|
+
registerProjectQueueJobs: options.registerProjectQueueJobs
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function createHoloProjectAccessors(resolveProject) {
|
|
87
|
+
const useConfig = (async (path) => {
|
|
88
|
+
const project = await resolveProject();
|
|
89
|
+
return project.runtime.useConfig(path);
|
|
90
|
+
});
|
|
91
|
+
return {
|
|
92
|
+
getApp: resolveProject,
|
|
93
|
+
getProject: resolveProject,
|
|
94
|
+
useConfig,
|
|
95
|
+
async config(path) {
|
|
96
|
+
const project = await resolveProject();
|
|
97
|
+
return project.runtime.config(path);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
async function initializeSingletonFrameworkProject(stateKey, displayName, options, createProject) {
|
|
102
|
+
const resolved = resolveHoloFrameworkOptions(options);
|
|
103
|
+
const state = getFrameworkAdapterStateContainer(stateKey);
|
|
104
|
+
if (state.project) {
|
|
105
|
+
if (state.projectRoot !== resolved.projectRoot) {
|
|
106
|
+
throw new Error(`${displayName} Holo project already initialized for "${state.projectRoot}".`);
|
|
107
|
+
}
|
|
108
|
+
const currentRuntime = peekHolo();
|
|
109
|
+
if (currentRuntime && resolve(currentRuntime.projectRoot) === resolved.projectRoot) {
|
|
110
|
+
if (resolved.runtime.preferCache === false) {
|
|
111
|
+
const currentSignature = await resolveProjectSourceSignature(
|
|
112
|
+
resolved.projectRoot,
|
|
113
|
+
currentRuntime.loadedConfig.environment.name
|
|
114
|
+
);
|
|
115
|
+
if (state.sourceSignature && state.sourceSignature !== currentSignature) {
|
|
116
|
+
await currentRuntime.shutdown();
|
|
117
|
+
state.project = void 0;
|
|
118
|
+
state.projectRoot = void 0;
|
|
119
|
+
state.sourceSignature = void 0;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (!state.project) {
|
|
123
|
+
return initializeSingletonFrameworkProject(stateKey, displayName, options, createProject);
|
|
124
|
+
}
|
|
125
|
+
configureConfigRuntime(currentRuntime.loadedConfig.all);
|
|
126
|
+
configureDB(currentRuntime.manager);
|
|
127
|
+
configureQueueRuntime({
|
|
128
|
+
config: currentRuntime.loadedConfig.queue,
|
|
129
|
+
...createQueueDbRuntimeOptions()
|
|
130
|
+
});
|
|
131
|
+
configurePlainNodeStorageRuntime(state.project.projectRoot, currentRuntime.loadedConfig);
|
|
132
|
+
if (state.project.runtime !== currentRuntime) {
|
|
133
|
+
;
|
|
134
|
+
state.project = {
|
|
135
|
+
...state.project,
|
|
136
|
+
config: currentRuntime.loadedConfig,
|
|
137
|
+
registry: currentRuntime.registry,
|
|
138
|
+
runtime: currentRuntime
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
return state.project;
|
|
142
|
+
}
|
|
143
|
+
;
|
|
144
|
+
state.project = void 0;
|
|
145
|
+
}
|
|
146
|
+
const project = await createProject(resolved);
|
|
147
|
+
state.projectRoot = resolved.projectRoot;
|
|
148
|
+
state.project = project;
|
|
149
|
+
state.sourceSignature = resolved.runtime.preferCache === false ? await resolveProjectSourceSignature(resolved.projectRoot, project.config.environment.name) : void 0;
|
|
150
|
+
return project;
|
|
151
|
+
}
|
|
152
|
+
async function resetSingletonFrameworkProject(stateKey) {
|
|
153
|
+
const state = getFrameworkAdapterStateContainer(stateKey);
|
|
154
|
+
state.project = void 0;
|
|
155
|
+
state.projectRoot = void 0;
|
|
156
|
+
state.sourceSignature = void 0;
|
|
157
|
+
resetStorageRuntime();
|
|
158
|
+
await resetHoloRuntime();
|
|
159
|
+
}
|
|
160
|
+
function createHoloFrameworkAdapter(options) {
|
|
161
|
+
const capabilities = defineHoloAdapterCapabilities(
|
|
162
|
+
options.capabilities ?? HOLO_MINIMUM_ADAPTER_CAPABILITIES
|
|
163
|
+
);
|
|
164
|
+
async function createProject(projectOptions = {}) {
|
|
165
|
+
const resolved = resolveHoloFrameworkOptions(projectOptions);
|
|
166
|
+
return createHoloAdapterProject(resolved.projectRoot, resolved.runtime);
|
|
167
|
+
}
|
|
168
|
+
async function initializeProject(projectOptions = {}) {
|
|
169
|
+
return initializeSingletonFrameworkProject(
|
|
170
|
+
options.stateKey,
|
|
171
|
+
options.displayName,
|
|
172
|
+
projectOptions,
|
|
173
|
+
async (resolved) => initializeHoloAdapterProject(resolved.projectRoot, resolved.runtime)
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
function createHelpers(projectOptions = {}) {
|
|
177
|
+
return createHoloProjectAccessors(() => initializeProject(projectOptions));
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
capabilities,
|
|
181
|
+
createProject,
|
|
182
|
+
initializeProject,
|
|
183
|
+
createHelpers,
|
|
184
|
+
resetProject: () => resetSingletonFrameworkProject(options.stateKey),
|
|
185
|
+
internals: {
|
|
186
|
+
getState: () => getFrameworkAdapterStateContainer(options.stateKey),
|
|
187
|
+
resolveOptions: (projectOptions = {}) => resolveHoloFrameworkOptions(projectOptions)
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
async function createHoloAdapterProject(projectRoot, options = {}) {
|
|
192
|
+
const runtime = await createHolo(projectRoot, options);
|
|
193
|
+
const project = {
|
|
194
|
+
projectRoot: runtime.projectRoot,
|
|
195
|
+
config: runtime.loadedConfig,
|
|
196
|
+
registry: runtime.registry,
|
|
197
|
+
runtime
|
|
198
|
+
};
|
|
199
|
+
return project;
|
|
200
|
+
}
|
|
201
|
+
async function initializeHoloAdapterProject(projectRoot, options = {}) {
|
|
202
|
+
const project = await createHoloAdapterProject(projectRoot, options);
|
|
203
|
+
const runtime = await ensureHolo(project.projectRoot, options);
|
|
204
|
+
configurePlainNodeStorageRuntime(project.projectRoot, runtime.loadedConfig);
|
|
205
|
+
return {
|
|
206
|
+
...project,
|
|
207
|
+
runtime
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
var adapterInternals = {
|
|
211
|
+
resolveStorageKeyPath
|
|
212
|
+
};
|
|
213
|
+
export {
|
|
214
|
+
HOLO_MINIMUM_ADAPTER_CAPABILITIES,
|
|
215
|
+
adapterInternals,
|
|
216
|
+
createHolo,
|
|
217
|
+
createHoloAdapterProject,
|
|
218
|
+
createHoloFrameworkAdapter,
|
|
219
|
+
createHoloProjectAccessors,
|
|
220
|
+
createRuntimeConnectionOptions,
|
|
221
|
+
defineHoloAdapterCapabilities,
|
|
222
|
+
ensureHolo,
|
|
223
|
+
getHolo,
|
|
224
|
+
holoRuntimeInternals,
|
|
225
|
+
initializeHolo,
|
|
226
|
+
initializeHoloAdapterProject,
|
|
227
|
+
loadGeneratedProjectRegistry,
|
|
228
|
+
peekHolo,
|
|
229
|
+
registryInternals,
|
|
230
|
+
resetHoloRuntime,
|
|
231
|
+
resetSingletonFrameworkProject,
|
|
232
|
+
resolveGeneratedProjectRegistryPath,
|
|
233
|
+
resolveHoloFrameworkOptions,
|
|
234
|
+
resolveRuntimeConnectionManagerOptions
|
|
235
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { HoloConfigMap, LoadedHoloConfig, DotPath, ValueAtPath } from '@holo-js/config';
|
|
2
|
+
import { QueueRuntimeBinding } from '@holo-js/queue';
|
|
3
|
+
import { resolveRuntimeConnectionManagerOptions } from '@holo-js/db';
|
|
4
|
+
export { RuntimeConfigInput, RuntimeConnectionConfig, RuntimeDatabaseConfig, RuntimeHoloConfig, SupportedDatabaseDriver, createAdapter, createDialect, createRuntimeConnectionOptions, createRuntimeLogger, isSupportedDatabaseDriver, parseDatabaseDriver, resolveRuntimeConnectionManagerOptions } from '@holo-js/db';
|
|
5
|
+
|
|
6
|
+
interface GeneratedModelRegistryEntry {
|
|
7
|
+
readonly sourcePath: string;
|
|
8
|
+
readonly name: string;
|
|
9
|
+
readonly prunable: boolean;
|
|
10
|
+
}
|
|
11
|
+
interface GeneratedMigrationRegistryEntry {
|
|
12
|
+
readonly sourcePath: string;
|
|
13
|
+
readonly name: string;
|
|
14
|
+
}
|
|
15
|
+
interface GeneratedSeederRegistryEntry {
|
|
16
|
+
readonly sourcePath: string;
|
|
17
|
+
readonly name: string;
|
|
18
|
+
}
|
|
19
|
+
interface GeneratedCommandRegistryEntry {
|
|
20
|
+
readonly sourcePath: string;
|
|
21
|
+
readonly name: string;
|
|
22
|
+
readonly aliases: readonly string[];
|
|
23
|
+
readonly description: string;
|
|
24
|
+
readonly usage?: string;
|
|
25
|
+
}
|
|
26
|
+
interface GeneratedJobRegistryEntry {
|
|
27
|
+
readonly sourcePath: string;
|
|
28
|
+
readonly name: string;
|
|
29
|
+
readonly connection?: string;
|
|
30
|
+
readonly queue?: string;
|
|
31
|
+
readonly tries?: number;
|
|
32
|
+
readonly backoff?: number | readonly number[];
|
|
33
|
+
readonly timeout?: number;
|
|
34
|
+
}
|
|
35
|
+
interface GeneratedEventRegistryEntry {
|
|
36
|
+
readonly sourcePath: string;
|
|
37
|
+
readonly name: string;
|
|
38
|
+
readonly exportName?: string;
|
|
39
|
+
}
|
|
40
|
+
interface GeneratedListenerRegistryEntry {
|
|
41
|
+
readonly sourcePath: string;
|
|
42
|
+
readonly id: string;
|
|
43
|
+
readonly eventNames: readonly string[];
|
|
44
|
+
readonly exportName?: string;
|
|
45
|
+
}
|
|
46
|
+
interface GeneratedProjectRegistry {
|
|
47
|
+
readonly version: 1;
|
|
48
|
+
readonly generatedAt: string;
|
|
49
|
+
readonly paths: {
|
|
50
|
+
readonly models: string;
|
|
51
|
+
readonly migrations: string;
|
|
52
|
+
readonly seeders: string;
|
|
53
|
+
readonly commands: string;
|
|
54
|
+
readonly jobs: string;
|
|
55
|
+
readonly events: string;
|
|
56
|
+
readonly listeners: string;
|
|
57
|
+
readonly generatedSchema: string;
|
|
58
|
+
};
|
|
59
|
+
readonly models: readonly GeneratedModelRegistryEntry[];
|
|
60
|
+
readonly migrations: readonly GeneratedMigrationRegistryEntry[];
|
|
61
|
+
readonly seeders: readonly GeneratedSeederRegistryEntry[];
|
|
62
|
+
readonly commands: readonly GeneratedCommandRegistryEntry[];
|
|
63
|
+
readonly jobs: readonly GeneratedJobRegistryEntry[];
|
|
64
|
+
readonly events: readonly GeneratedEventRegistryEntry[];
|
|
65
|
+
readonly listeners: readonly GeneratedListenerRegistryEntry[];
|
|
66
|
+
}
|
|
67
|
+
declare function normalizeLegacyGeneratedProjectRegistry(value: Record<string, unknown>): void;
|
|
68
|
+
declare function isGeneratedProjectRegistry(value: unknown): value is GeneratedProjectRegistry;
|
|
69
|
+
declare function resolveGeneratedProjectRegistryPath(projectRoot: string): string;
|
|
70
|
+
declare function loadGeneratedProjectRegistry(projectRoot: string): Promise<GeneratedProjectRegistry | undefined>;
|
|
71
|
+
declare const registryInternals: {
|
|
72
|
+
isGeneratedProjectRegistry: typeof isGeneratedProjectRegistry;
|
|
73
|
+
normalizeLegacyGeneratedProjectRegistry: typeof normalizeLegacyGeneratedProjectRegistry;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
type RuntimeConfigRegistry<TCustom extends HoloConfigMap> = LoadedHoloConfig<TCustom>['all'];
|
|
77
|
+
type PortableRuntimeConfig<TCustom extends HoloConfigMap> = {
|
|
78
|
+
readonly db: LoadedHoloConfig<TCustom>['database'];
|
|
79
|
+
readonly queue: LoadedHoloConfig<TCustom>['queue'];
|
|
80
|
+
};
|
|
81
|
+
type PortableConnectionManager = ReturnType<typeof resolveRuntimeConnectionManagerOptions>;
|
|
82
|
+
interface CreateHoloOptions {
|
|
83
|
+
readonly envName?: string;
|
|
84
|
+
readonly preferCache?: boolean;
|
|
85
|
+
readonly processEnv?: NodeJS.ProcessEnv;
|
|
86
|
+
readonly registerProjectQueueJobs?: boolean;
|
|
87
|
+
}
|
|
88
|
+
interface HoloRuntime<TCustom extends HoloConfigMap = HoloConfigMap> {
|
|
89
|
+
readonly projectRoot: string;
|
|
90
|
+
readonly loadedConfig: LoadedHoloConfig<TCustom>;
|
|
91
|
+
readonly registry?: GeneratedProjectRegistry;
|
|
92
|
+
readonly manager: PortableConnectionManager;
|
|
93
|
+
readonly runtimeConfig: PortableRuntimeConfig<TCustom>;
|
|
94
|
+
readonly queue: QueueRuntimeBinding;
|
|
95
|
+
readonly initialized: boolean;
|
|
96
|
+
initialize(): Promise<void>;
|
|
97
|
+
shutdown(): Promise<void>;
|
|
98
|
+
useConfig<TKey extends Extract<keyof RuntimeConfigRegistry<TCustom>, string>>(key: TKey): RuntimeConfigRegistry<TCustom>[TKey];
|
|
99
|
+
useConfig<TPath extends DotPath<RuntimeConfigRegistry<TCustom>>>(path: TPath): ValueAtPath<RuntimeConfigRegistry<TCustom>, TPath>;
|
|
100
|
+
config<TPath extends DotPath<RuntimeConfigRegistry<TCustom>>>(path: TPath): ValueAtPath<RuntimeConfigRegistry<TCustom>, TPath>;
|
|
101
|
+
}
|
|
102
|
+
declare function createHolo<TCustom extends HoloConfigMap = HoloConfigMap>(projectRoot: string, options?: CreateHoloOptions): Promise<HoloRuntime<TCustom>>;
|
|
103
|
+
declare function initializeHolo<TCustom extends HoloConfigMap = HoloConfigMap>(projectRoot: string, options?: CreateHoloOptions): Promise<HoloRuntime<TCustom>>;
|
|
104
|
+
declare function peekHolo<TCustom extends HoloConfigMap = HoloConfigMap>(): HoloRuntime<TCustom> | undefined;
|
|
105
|
+
declare function ensureHolo<TCustom extends HoloConfigMap = HoloConfigMap>(projectRoot: string, options?: CreateHoloOptions): Promise<HoloRuntime<TCustom>>;
|
|
106
|
+
declare function getHolo<TCustom extends HoloConfigMap = HoloConfigMap>(): HoloRuntime<TCustom>;
|
|
107
|
+
declare function resetHoloRuntime(): Promise<void>;
|
|
108
|
+
declare function getConfigValue(path: string): unknown;
|
|
109
|
+
declare function getConfigSection(key: string): unknown;
|
|
110
|
+
declare const holoRuntimeInternals: {
|
|
111
|
+
getConfigSection: typeof getConfigSection;
|
|
112
|
+
getConfigValue: typeof getConfigValue;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export { type CreateHoloOptions, type GeneratedCommandRegistryEntry, type GeneratedEventRegistryEntry, type GeneratedJobRegistryEntry, type GeneratedListenerRegistryEntry, type GeneratedMigrationRegistryEntry, type GeneratedModelRegistryEntry, type GeneratedProjectRegistry, type GeneratedSeederRegistryEntry, type HoloRuntime, createHolo, ensureHolo, getHolo, holoRuntimeInternals, initializeHolo, loadGeneratedProjectRegistry, peekHolo, registryInternals, resetHoloRuntime, resolveGeneratedProjectRegistryPath };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createAdapter,
|
|
3
|
+
createDialect,
|
|
4
|
+
createHolo,
|
|
5
|
+
createRuntimeConnectionOptions,
|
|
6
|
+
createRuntimeLogger,
|
|
7
|
+
ensureHolo,
|
|
8
|
+
getHolo,
|
|
9
|
+
holoRuntimeInternals,
|
|
10
|
+
initializeHolo,
|
|
11
|
+
isSupportedDatabaseDriver,
|
|
12
|
+
loadGeneratedProjectRegistry,
|
|
13
|
+
parseDatabaseDriver,
|
|
14
|
+
peekHolo,
|
|
15
|
+
registryInternals,
|
|
16
|
+
resetHoloRuntime,
|
|
17
|
+
resolveGeneratedProjectRegistryPath,
|
|
18
|
+
resolveRuntimeConnectionManagerOptions
|
|
19
|
+
} from "../chunk-SVCNZBIQ.mjs";
|
|
20
|
+
export {
|
|
21
|
+
createAdapter,
|
|
22
|
+
createDialect,
|
|
23
|
+
createHolo,
|
|
24
|
+
createRuntimeConnectionOptions,
|
|
25
|
+
createRuntimeLogger,
|
|
26
|
+
ensureHolo,
|
|
27
|
+
getHolo,
|
|
28
|
+
holoRuntimeInternals,
|
|
29
|
+
initializeHolo,
|
|
30
|
+
isSupportedDatabaseDriver,
|
|
31
|
+
loadGeneratedProjectRegistry,
|
|
32
|
+
parseDatabaseDriver,
|
|
33
|
+
peekHolo,
|
|
34
|
+
registryInternals,
|
|
35
|
+
resetHoloRuntime,
|
|
36
|
+
resolveGeneratedProjectRegistryPath,
|
|
37
|
+
resolveRuntimeConnectionManagerOptions
|
|
38
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@holo-js/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Holo-JS Framework - Portable runtime core",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.mjs",
|
|
11
|
+
"default": "./dist/index.mjs"
|
|
12
|
+
},
|
|
13
|
+
"./runtime": {
|
|
14
|
+
"types": "./dist/runtime/index.d.ts",
|
|
15
|
+
"import": "./dist/runtime/index.mjs",
|
|
16
|
+
"default": "./dist/runtime/index.mjs"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"main": "./dist/index.mjs",
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsup",
|
|
26
|
+
"stub": "tsup",
|
|
27
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
28
|
+
"test": "vitest --run"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@holo-js/config": "workspace:*",
|
|
32
|
+
"@holo-js/db": "workspace:*",
|
|
33
|
+
"@holo-js/events": "workspace:*",
|
|
34
|
+
"@holo-js/queue": "workspace:*",
|
|
35
|
+
"@holo-js/queue-db": "workspace:*",
|
|
36
|
+
"@holo-js/storage": "workspace:*",
|
|
37
|
+
"esbuild": "catalog:"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "catalog:",
|
|
41
|
+
"tsup": "catalog:",
|
|
42
|
+
"typescript": "catalog:",
|
|
43
|
+
"vitest": "catalog:"
|
|
44
|
+
}
|
|
45
|
+
}
|