@holo-js/core 0.2.6 → 0.3.1
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-H43SANQQ.mjs → chunk-34VBLRNF.mjs} +1039 -848
- package/dist/errors.d.ts +1 -11
- package/dist/errors.mjs +4 -101
- package/dist/index.d.ts +5 -4
- package/dist/index.mjs +1 -1
- package/dist/runtime/index.d.ts +158 -165
- package/dist/runtime/index.mjs +1 -1
- package/package.json +32 -27
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
// src/portable/registry.ts
|
|
13
13
|
import { readFile } from "node:fs/promises";
|
|
14
14
|
import { resolve } from "node:path";
|
|
15
|
-
import { DEFAULT_HOLO_PROJECT_PATHS } from "@holo-js/
|
|
15
|
+
import { DEFAULT_HOLO_PROJECT_PATHS } from "@holo-js/kernel";
|
|
16
16
|
function isRecord(value) {
|
|
17
17
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
18
18
|
}
|
|
@@ -118,11 +118,31 @@ var registryInternals = {
|
|
|
118
118
|
normalizeLegacyGeneratedProjectRegistry
|
|
119
119
|
};
|
|
120
120
|
|
|
121
|
+
// src/portable/renderingRuntime.ts
|
|
122
|
+
function getHoloRenderingRuntime() {
|
|
123
|
+
const runtime = globalThis;
|
|
124
|
+
runtime.__holoRenderingRuntime__ ??= {};
|
|
125
|
+
return runtime.__holoRenderingRuntime__;
|
|
126
|
+
}
|
|
127
|
+
function configureHoloRenderingRuntime(bindings) {
|
|
128
|
+
getHoloRenderingRuntime().renderView = bindings?.renderView;
|
|
129
|
+
}
|
|
130
|
+
function resetHoloRenderingRuntime() {
|
|
131
|
+
getHoloRenderingRuntime().renderView = void 0;
|
|
132
|
+
}
|
|
133
|
+
function restoreHoloRenderingRuntime(renderView) {
|
|
134
|
+
if (renderView) {
|
|
135
|
+
configureHoloRenderingRuntime({ renderView });
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
resetHoloRenderingRuntime();
|
|
139
|
+
}
|
|
140
|
+
|
|
121
141
|
// src/portable/holo.ts
|
|
122
|
-
import {
|
|
123
|
-
import { existsSync } from "node:fs";
|
|
142
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
124
143
|
import { createHash, createHmac } from "node:crypto";
|
|
125
|
-
import { resolve as
|
|
144
|
+
import { resolve as resolve5 } from "node:path";
|
|
145
|
+
import { createRuntimeLifecycle } from "@holo-js/kernel";
|
|
126
146
|
import {
|
|
127
147
|
config as globalConfig,
|
|
128
148
|
configureConfigRuntime,
|
|
@@ -132,10 +152,13 @@ import {
|
|
|
132
152
|
useConfig as globalUseConfig
|
|
133
153
|
} from "@holo-js/config";
|
|
134
154
|
import {
|
|
155
|
+
connectionAsyncContext,
|
|
135
156
|
configureDB,
|
|
136
|
-
DB,
|
|
157
|
+
DB as DB2,
|
|
137
158
|
Entity,
|
|
138
|
-
|
|
159
|
+
registerDatabaseDriverFactory,
|
|
160
|
+
resetDB,
|
|
161
|
+
unregisterDatabaseDriverFactory
|
|
139
162
|
} from "@holo-js/db";
|
|
140
163
|
|
|
141
164
|
// src/runtimeModule.ts
|
|
@@ -156,14 +179,18 @@ async function importModule(specifier) {
|
|
|
156
179
|
);
|
|
157
180
|
}
|
|
158
181
|
var runtimeModuleRequire = createRequire(import.meta.url);
|
|
182
|
+
function resolveRuntimeModuleSearchPaths(projectRoot, runtimeModuleUrl = import.meta.url) {
|
|
183
|
+
const runtimeModulePath = normalizeImportTarget(runtimeModuleUrl);
|
|
184
|
+
return runtimeModulePath.includes("/node_modules/@holo-js/core/") ? void 0 : [projectRoot];
|
|
185
|
+
}
|
|
159
186
|
function resolveOptionalImportSpecifier(specifier, projectRoot) {
|
|
160
187
|
if (!projectRoot) {
|
|
161
188
|
return specifier;
|
|
162
189
|
}
|
|
163
190
|
try {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
191
|
+
const paths = resolveRuntimeModuleSearchPaths(projectRoot);
|
|
192
|
+
const resolved = paths ? runtimeModuleRequire.resolve(specifier, { paths: [...paths] }) : runtimeModuleRequire.resolve(specifier);
|
|
193
|
+
return pathToFileURL(resolved).href;
|
|
167
194
|
} catch {
|
|
168
195
|
return specifier;
|
|
169
196
|
}
|
|
@@ -181,6 +208,16 @@ function normalizeImportSpecifier(specifier) {
|
|
|
181
208
|
function normalizeImportTarget(value) {
|
|
182
209
|
return normalizeImportSpecifier(value).replace(/\\/g, "/");
|
|
183
210
|
}
|
|
211
|
+
function resolvePackageRootSpecifier(specifier) {
|
|
212
|
+
if (specifier.startsWith("@")) {
|
|
213
|
+
const [scope, packageName] = specifier.split("/");
|
|
214
|
+
return scope && packageName ? `${scope}/${packageName}` : void 0;
|
|
215
|
+
}
|
|
216
|
+
if (specifier.startsWith(".") || specifier.startsWith("/") || specifier.includes(":")) {
|
|
217
|
+
return void 0;
|
|
218
|
+
}
|
|
219
|
+
return specifier.split("/")[0];
|
|
220
|
+
}
|
|
184
221
|
function matchesRelativeImportTarget(failedTarget, specifier) {
|
|
185
222
|
if (!failedTarget || !specifier.startsWith(".")) {
|
|
186
223
|
return false;
|
|
@@ -200,6 +237,10 @@ function isMissingOptionalModule(error, specifier, resolvedSpecifier) {
|
|
|
200
237
|
normalizeImportTarget(specifier),
|
|
201
238
|
normalizeImportTarget(resolvedSpecifier)
|
|
202
239
|
]);
|
|
240
|
+
const packageRootSpecifier = resolvePackageRootSpecifier(specifier);
|
|
241
|
+
if (packageRootSpecifier) {
|
|
242
|
+
expectedTargets.add(packageRootSpecifier);
|
|
243
|
+
}
|
|
203
244
|
const normalizedFailedTarget = typeof failedTarget === "string" ? normalizeImportTarget(failedTarget) : void 0;
|
|
204
245
|
const matchesRequestedTarget = typeof normalizedFailedTarget === "string" && (expectedTargets.has(normalizedFailedTarget) || matchesRelativeImportTarget(normalizedFailedTarget, specifier));
|
|
205
246
|
return "code" in error && error.code === "ERR_MODULE_NOT_FOUND" && matchesRequestedTarget || message.startsWith("Cannot find package '") && matchesRequestedTarget || message.startsWith("Cannot find module '") && matchesRequestedTarget || message.includes("Does the file exist?") && message.startsWith("Failed to load url ") && matchesRequestedTarget || message.startsWith('Could not resolve "') && matchesRequestedTarget;
|
|
@@ -317,8 +358,10 @@ var runtimeModuleInternals = {
|
|
|
317
358
|
bundleRuntimeModule,
|
|
318
359
|
importModule,
|
|
319
360
|
importOptionalRuntimeModule,
|
|
361
|
+
isMissingOptionalModule,
|
|
320
362
|
loadEsbuild,
|
|
321
363
|
pathExists,
|
|
364
|
+
resolveRuntimeModuleSearchPaths,
|
|
322
365
|
runEsbuild,
|
|
323
366
|
writeLoaderTsconfig
|
|
324
367
|
};
|
|
@@ -326,8 +369,8 @@ var runtimeModuleInternals = {
|
|
|
326
369
|
// src/storageRuntime.ts
|
|
327
370
|
import { mkdir as mkdir2, readFile as readFile2, readdir, rm as rm2, writeFile as writeFile2 } from "node:fs/promises";
|
|
328
371
|
import { dirname, isAbsolute, join as join2, relative, resolve as resolve2, sep, win32 } from "node:path";
|
|
329
|
-
async function importOptionalModule(specifier) {
|
|
330
|
-
return importOptionalRuntimeModule(specifier);
|
|
372
|
+
async function importOptionalModule(specifier, projectRoot) {
|
|
373
|
+
return importOptionalRuntimeModule(specifier, projectRoot ? { projectRoot } : {});
|
|
331
374
|
}
|
|
332
375
|
function resolveStorageKeyPath(root, key) {
|
|
333
376
|
if (isAbsolute(key) || win32.isAbsolute(key)) {
|
|
@@ -427,8 +470,8 @@ function createFileStorageBackend(root) {
|
|
|
427
470
|
}
|
|
428
471
|
};
|
|
429
472
|
}
|
|
430
|
-
async function createS3StorageBackend(disk) {
|
|
431
|
-
const storageS3 = await importOptionalModule("@holo-js/storage
|
|
473
|
+
async function createS3StorageBackend(projectRoot, disk) {
|
|
474
|
+
const storageS3 = await importOptionalModule("@holo-js/storage-s3", projectRoot);
|
|
432
475
|
if (!storageS3) {
|
|
433
476
|
throw new Error("[@holo-js/core] Storage config references an s3 disk but @holo-js/storage-s3 is not installed.");
|
|
434
477
|
}
|
|
@@ -455,7 +498,7 @@ async function configurePlainNodeStorageRuntime(projectRoot, loadedConfig) {
|
|
|
455
498
|
});
|
|
456
499
|
const backends = /* @__PURE__ */ new Map();
|
|
457
500
|
for (const [diskName, disk] of Object.entries(normalizedStorage.disks)) {
|
|
458
|
-
const backend = disk.driver === "s3" ? await createS3StorageBackend(disk) : createFileStorageBackend(resolve2(projectRoot, disk.root));
|
|
501
|
+
const backend = disk.driver === "s3" ? await createS3StorageBackend(projectRoot, disk) : createFileStorageBackend(resolve2(projectRoot, disk.root));
|
|
459
502
|
backends.set(diskName, backend);
|
|
460
503
|
}
|
|
461
504
|
storageRuntime.configureStorageRuntime({
|
|
@@ -481,150 +524,231 @@ var storageRuntimeInternals = {
|
|
|
481
524
|
importOptionalModule
|
|
482
525
|
};
|
|
483
526
|
|
|
484
|
-
// src/portable/
|
|
527
|
+
// src/portable/discoveryRuntime.ts
|
|
528
|
+
import { existsSync } from "node:fs";
|
|
529
|
+
import { resolve as resolve3 } from "node:path";
|
|
485
530
|
async function preloadGeneratedSchemaModule(projectRoot, registry) {
|
|
486
531
|
const entry = registry?.paths.generatedSchema;
|
|
487
|
-
if (!entry)
|
|
488
|
-
return;
|
|
489
|
-
}
|
|
532
|
+
if (!entry) return;
|
|
490
533
|
const expectedTarget = resolve3(projectRoot, entry);
|
|
491
|
-
if (!existsSync(expectedTarget))
|
|
492
|
-
return;
|
|
493
|
-
}
|
|
534
|
+
if (!existsSync(expectedTarget)) return;
|
|
494
535
|
try {
|
|
495
536
|
await importBundledRuntimeModule(projectRoot, entry);
|
|
496
537
|
} catch (error) {
|
|
497
538
|
if (error instanceof Error && /Cannot find module|ERR_MODULE_NOT_FOUND|MODULE_NOT_FOUND|Failed to load url|Failed to load /.test(error.message)) {
|
|
498
|
-
const
|
|
499
|
-
|
|
500
|
-
if (failedTarget === expectedTarget) {
|
|
501
|
-
return;
|
|
502
|
-
}
|
|
539
|
+
const failedTarget = error.message.match(/Cannot find module '([^']+)'|Cannot find package '([^']+)'|Failed to load url ([^ ]+)|Failed to load ([^ ]+)\./)?.slice(1).find((value) => typeof value === "string");
|
|
540
|
+
if (failedTarget === expectedTarget) return;
|
|
503
541
|
}
|
|
504
542
|
throw error;
|
|
505
543
|
}
|
|
506
544
|
}
|
|
507
545
|
async function preloadDiscoveredModelModules(projectRoot, registry) {
|
|
508
|
-
if (!registry || registry.models.length === 0)
|
|
509
|
-
return;
|
|
510
|
-
}
|
|
546
|
+
if (!registry || registry.models.length === 0) return;
|
|
511
547
|
for (const entry of registry.models) {
|
|
512
548
|
const sourcePath = resolve3(projectRoot, entry.sourcePath);
|
|
513
|
-
if (
|
|
514
|
-
continue;
|
|
515
|
-
}
|
|
516
|
-
await importBundledRuntimeModule(projectRoot, sourcePath);
|
|
549
|
+
if (existsSync(sourcePath)) await importBundledRuntimeModule(projectRoot, sourcePath);
|
|
517
550
|
}
|
|
518
551
|
}
|
|
519
|
-
|
|
520
|
-
|
|
552
|
+
|
|
553
|
+
// src/portable/configRuntime.ts
|
|
554
|
+
var featureConfigContributionSpecifiers = [
|
|
555
|
+
"@holo-js/auth/config",
|
|
556
|
+
"@holo-js/broadcast/config",
|
|
557
|
+
"@holo-js/cache/config",
|
|
558
|
+
"@holo-js/mail/config",
|
|
559
|
+
"@holo-js/media/config",
|
|
560
|
+
"@holo-js/notifications/config",
|
|
561
|
+
"@holo-js/queue/config",
|
|
562
|
+
"@holo-js/security/config",
|
|
563
|
+
"@holo-js/session/config",
|
|
564
|
+
"@holo-js/storage/config"
|
|
565
|
+
];
|
|
566
|
+
async function loadDirectFeatureConfigContribution(specifier) {
|
|
567
|
+
switch (specifier) {
|
|
568
|
+
case "@holo-js/auth/config":
|
|
569
|
+
await import("@holo-js/auth/config");
|
|
570
|
+
break;
|
|
571
|
+
case "@holo-js/broadcast/config":
|
|
572
|
+
await import("@holo-js/broadcast/config");
|
|
573
|
+
break;
|
|
574
|
+
case "@holo-js/cache/config":
|
|
575
|
+
await import("@holo-js/cache/config");
|
|
576
|
+
break;
|
|
577
|
+
case "@holo-js/mail/config":
|
|
578
|
+
await import("@holo-js/mail/config");
|
|
579
|
+
break;
|
|
580
|
+
case "@holo-js/media/config":
|
|
581
|
+
await import("@holo-js/media/config");
|
|
582
|
+
break;
|
|
583
|
+
case "@holo-js/notifications/config":
|
|
584
|
+
await import("@holo-js/notifications/config");
|
|
585
|
+
break;
|
|
586
|
+
case "@holo-js/queue/config":
|
|
587
|
+
await import("@holo-js/queue/config");
|
|
588
|
+
break;
|
|
589
|
+
case "@holo-js/security/config":
|
|
590
|
+
await import("@holo-js/security/config");
|
|
591
|
+
break;
|
|
592
|
+
case "@holo-js/session/config":
|
|
593
|
+
await import("@holo-js/session/config");
|
|
594
|
+
break;
|
|
595
|
+
case "@holo-js/storage/config":
|
|
596
|
+
await import("@holo-js/storage/config");
|
|
597
|
+
break;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
var defaultLoaders = {
|
|
601
|
+
loadDirect: loadDirectFeatureConfigContribution,
|
|
602
|
+
async loadOptional(specifier, projectRoot) {
|
|
603
|
+
return await importOptionalRuntimeModule(specifier, { projectRoot });
|
|
604
|
+
}
|
|
605
|
+
};
|
|
606
|
+
async function loadInstalledFeatureConfigContributions(projectRoot, loaders = defaultLoaders) {
|
|
607
|
+
for (const specifier of featureConfigContributionSpecifiers) {
|
|
608
|
+
try {
|
|
609
|
+
await loaders.loadDirect(specifier);
|
|
610
|
+
} catch {
|
|
611
|
+
const loaded = await loaders.loadOptional(specifier, projectRoot);
|
|
612
|
+
if (!loaded) continue;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
521
615
|
}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
function
|
|
525
|
-
return
|
|
616
|
+
|
|
617
|
+
// src/portable/authPersistence.ts
|
|
618
|
+
function normalizeDateValue(value) {
|
|
619
|
+
return value instanceof Date ? value : new Date(String(value));
|
|
526
620
|
}
|
|
527
|
-
function
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
621
|
+
function normalizeJsonValue(value) {
|
|
622
|
+
if (typeof value !== "string") return value;
|
|
623
|
+
try {
|
|
624
|
+
return JSON.parse(value);
|
|
625
|
+
} catch {
|
|
626
|
+
return value;
|
|
627
|
+
}
|
|
531
628
|
}
|
|
532
|
-
function
|
|
533
|
-
|
|
629
|
+
function normalizeStoredUserId(value) {
|
|
630
|
+
return typeof value === "number" ? value : String(value);
|
|
534
631
|
}
|
|
535
|
-
function
|
|
536
|
-
|
|
632
|
+
function normalizeAccessTokenRecord(row) {
|
|
633
|
+
const abilities = normalizeJsonValue(row.abilities);
|
|
634
|
+
return Object.freeze({
|
|
635
|
+
id: String(row.id),
|
|
636
|
+
provider: String(row.provider),
|
|
637
|
+
userId: normalizeStoredUserId(row.user_id),
|
|
638
|
+
name: String(row.name),
|
|
639
|
+
abilities: Array.isArray(abilities) ? Object.freeze([...abilities]) : Object.freeze([]),
|
|
640
|
+
tokenHash: String(row.token_hash),
|
|
641
|
+
createdAt: normalizeDateValue(row.created_at),
|
|
642
|
+
lastUsedAt: row.last_used_at ? normalizeDateValue(row.last_used_at) : void 0,
|
|
643
|
+
expiresAt: row.expires_at ? normalizeDateValue(row.expires_at) : null
|
|
644
|
+
});
|
|
537
645
|
}
|
|
538
|
-
function
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
646
|
+
function serializeAccessTokenRecord(record) {
|
|
647
|
+
return {
|
|
648
|
+
id: record.id,
|
|
649
|
+
provider: record.provider,
|
|
650
|
+
user_id: String(record.userId),
|
|
651
|
+
name: record.name,
|
|
652
|
+
abilities: JSON.stringify(record.abilities),
|
|
653
|
+
token_hash: record.tokenHash,
|
|
654
|
+
created_at: record.createdAt.toISOString(),
|
|
655
|
+
last_used_at: record.lastUsedAt?.toISOString() ?? null,
|
|
656
|
+
expires_at: record.expiresAt?.toISOString() ?? null,
|
|
657
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
658
|
+
};
|
|
546
659
|
}
|
|
547
|
-
function
|
|
548
|
-
const state = getRuntimeState();
|
|
549
|
-
const runtime = globalThis;
|
|
660
|
+
function normalizeEmailVerificationTokenRecord(row) {
|
|
550
661
|
return Object.freeze({
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
})
|
|
559
|
-
} : {},
|
|
560
|
-
/* v8 ignore stop */
|
|
561
|
-
...runtime.__holoSecurityRuntime__?.bindings || state.securityRedisAdapter || typeof state.securityRateLimitStoreManaged !== "undefined" ? {
|
|
562
|
-
security: Object.freeze({
|
|
563
|
-
...runtime.__holoSecurityRuntime__?.bindings ? { bindings: runtime.__holoSecurityRuntime__.bindings } : {},
|
|
564
|
-
...state.securityRedisAdapter ? { securityRedisAdapter: state.securityRedisAdapter } : {},
|
|
565
|
-
...typeof state.securityRateLimitStoreManaged !== "undefined" ? { securityRateLimitStoreManaged: state.securityRateLimitStoreManaged } : {}
|
|
566
|
-
})
|
|
567
|
-
} : {}
|
|
662
|
+
id: String(row.id),
|
|
663
|
+
provider: String(row.provider),
|
|
664
|
+
userId: normalizeStoredUserId(row.user_id),
|
|
665
|
+
email: String(row.email),
|
|
666
|
+
tokenHash: String(row.token_hash),
|
|
667
|
+
createdAt: normalizeDateValue(row.created_at),
|
|
668
|
+
expiresAt: normalizeDateValue(row.expires_at)
|
|
568
669
|
});
|
|
569
670
|
}
|
|
570
|
-
function
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
runtime.__holoBroadcastRuntime__ ??= {};
|
|
583
|
-
runtime.__holoBroadcastRuntime__.bindings = bindings.broadcast;
|
|
584
|
-
}
|
|
585
|
-
state.sessionRedisAdapters = bindings.session?.sessionRedisAdapters;
|
|
586
|
-
if (bindings.security || runtime.__holoSecurityRuntime__) {
|
|
587
|
-
runtime.__holoSecurityRuntime__ ??= {};
|
|
588
|
-
runtime.__holoSecurityRuntime__.bindings = bindings.security?.bindings;
|
|
589
|
-
state.securityRedisAdapter = bindings.security?.securityRedisAdapter;
|
|
590
|
-
state.securityRateLimitStoreManaged = bindings.security?.securityRateLimitStoreManaged;
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
var BROADCAST_PUBLISH_TIMEOUT_MS = 1e4;
|
|
594
|
-
async function importOptionalModule2(specifier, options = {}) {
|
|
595
|
-
return importOptionalRuntimeModule(specifier, options);
|
|
671
|
+
function serializeEmailVerificationTokenRecord(record) {
|
|
672
|
+
return {
|
|
673
|
+
id: record.id,
|
|
674
|
+
provider: record.provider,
|
|
675
|
+
user_id: String(record.userId),
|
|
676
|
+
email: record.email,
|
|
677
|
+
token_hash: record.tokenHash,
|
|
678
|
+
created_at: record.createdAt.toISOString(),
|
|
679
|
+
expires_at: record.expiresAt.toISOString(),
|
|
680
|
+
used_at: null,
|
|
681
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
682
|
+
};
|
|
596
683
|
}
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
684
|
+
function normalizePasswordResetTokenRecord(row) {
|
|
685
|
+
return Object.freeze({
|
|
686
|
+
id: String(row.id),
|
|
687
|
+
provider: typeof row.provider === "string" ? row.provider : "users",
|
|
688
|
+
email: String(row.email),
|
|
689
|
+
table: typeof row.__holo_table === "string" ? row.__holo_table : void 0,
|
|
690
|
+
tokenHash: String(row.token_hash),
|
|
691
|
+
createdAt: normalizeDateValue(row.created_at),
|
|
692
|
+
expiresAt: normalizeDateValue(row.expires_at)
|
|
604
693
|
});
|
|
605
694
|
}
|
|
606
|
-
function
|
|
607
|
-
return
|
|
695
|
+
function serializePasswordResetTokenRecord(record) {
|
|
696
|
+
return {
|
|
697
|
+
id: record.id,
|
|
698
|
+
provider: record.provider,
|
|
699
|
+
email: record.email,
|
|
700
|
+
token_hash: record.tokenHash,
|
|
701
|
+
created_at: record.createdAt.toISOString(),
|
|
702
|
+
expires_at: record.expiresAt.toISOString(),
|
|
703
|
+
used_at: null,
|
|
704
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
705
|
+
};
|
|
608
706
|
}
|
|
609
|
-
|
|
610
|
-
|
|
707
|
+
|
|
708
|
+
// src/portable/pluginRuntime.ts
|
|
709
|
+
import { resolve as resolve4 } from "node:path";
|
|
710
|
+
import {
|
|
711
|
+
loadHoloPluginBootModules,
|
|
712
|
+
loadHoloPluginDefinitions
|
|
713
|
+
} from "@holo-js/kernel";
|
|
714
|
+
function isRecord2(value) {
|
|
715
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
611
716
|
}
|
|
612
|
-
|
|
613
|
-
|
|
717
|
+
var bootedHoloPluginModules = /* @__PURE__ */ new Set();
|
|
718
|
+
function resolveHoloPluginBootKey(projectRoot, bootModule) {
|
|
719
|
+
return [resolve4(projectRoot), bootModule.plugin.packageName, bootModule.runtime].join("\0");
|
|
614
720
|
}
|
|
615
|
-
function
|
|
616
|
-
return
|
|
721
|
+
function resolveLoadedPluginNames(loadedConfig) {
|
|
722
|
+
return loadedConfig.app.plugins;
|
|
617
723
|
}
|
|
618
|
-
function
|
|
619
|
-
|
|
724
|
+
async function bootConfiguredHoloPluginModule(projectRoot, loadedConfig, bootModule) {
|
|
725
|
+
const bootKey = resolveHoloPluginBootKey(projectRoot, bootModule);
|
|
726
|
+
if (bootedHoloPluginModules.has(bootKey)) return;
|
|
727
|
+
const candidate = isRecord2(bootModule.module) && typeof bootModule.module.default !== "undefined" ? bootModule.module.default : bootModule.module;
|
|
728
|
+
if (typeof candidate !== "function") return;
|
|
729
|
+
await candidate({ projectRoot, config: loadedConfig });
|
|
730
|
+
bootedHoloPluginModules.add(bootKey);
|
|
620
731
|
}
|
|
621
|
-
function
|
|
622
|
-
|
|
732
|
+
function resetBootedHoloPluginModules() {
|
|
733
|
+
bootedHoloPluginModules.clear();
|
|
623
734
|
}
|
|
624
|
-
function
|
|
625
|
-
|
|
735
|
+
async function loadConfiguredHoloPluginDefinitions(projectRoot, pluginNames) {
|
|
736
|
+
const normalizedNames = [...new Set(pluginNames.map((name) => name.trim()).filter(Boolean))];
|
|
737
|
+
return loadHoloPluginDefinitions(projectRoot, normalizedNames, { moduleVersion: String(Date.now()) });
|
|
626
738
|
}
|
|
627
|
-
|
|
739
|
+
async function loadConfiguredHoloPluginBootModules(projectRoot, plugins) {
|
|
740
|
+
return loadHoloPluginBootModules(projectRoot, plugins, { moduleVersion: String(Date.now()) });
|
|
741
|
+
}
|
|
742
|
+
function mergeQueueRuntimeDriverFactories(...sources) {
|
|
743
|
+
const factories = /* @__PURE__ */ new Map();
|
|
744
|
+
for (const source of sources) {
|
|
745
|
+
for (const factory of source ?? []) factories.set(factory.driver, factory);
|
|
746
|
+
}
|
|
747
|
+
return Object.freeze([...factories.values()]);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
// src/portable/authRequestContext.ts
|
|
751
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
628
752
|
function attachAuthRequestAccessors(context, accessors) {
|
|
629
753
|
return Object.freeze({
|
|
630
754
|
...context,
|
|
@@ -656,141 +780,503 @@ function createRequestAwareAuthContext(context, accessors) {
|
|
|
656
780
|
return resolveRequestContext().redirectResponse?.(url, status);
|
|
657
781
|
},
|
|
658
782
|
setRequestAccessors(nextAccessors) {
|
|
659
|
-
requestAccessorStorage.enterWith({
|
|
660
|
-
accessors: nextAccessors
|
|
661
|
-
});
|
|
783
|
+
requestAccessorStorage.enterWith({ accessors: nextAccessors });
|
|
662
784
|
},
|
|
663
|
-
|
|
664
|
-
return
|
|
665
|
-
accessors: nextAccessors
|
|
666
|
-
|
|
785
|
+
runWithRequestAccessors(nextAccessors, callback) {
|
|
786
|
+
return requestAccessorStorage.run(
|
|
787
|
+
{ accessors: nextAccessors },
|
|
788
|
+
() => context.run ? context.run(callback) : callback()
|
|
789
|
+
);
|
|
667
790
|
}
|
|
668
791
|
});
|
|
669
792
|
}
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
}
|
|
675
|
-
return queueModule;
|
|
676
|
-
}
|
|
677
|
-
async function loadQueueDbModule() {
|
|
678
|
-
return portableRuntimeModuleInternals.importOptionalModule("@holo-js/queue-db");
|
|
793
|
+
|
|
794
|
+
// src/portable/recordPersistence.ts
|
|
795
|
+
function normalizeDateLike(value) {
|
|
796
|
+
return value instanceof Date ? value : new Date(String(value));
|
|
679
797
|
}
|
|
680
|
-
|
|
681
|
-
const
|
|
682
|
-
|
|
798
|
+
function normalizeSessionRecordFromRow(row) {
|
|
799
|
+
const decodedData = (() => {
|
|
800
|
+
if (row.data && typeof row.data === "object") return row.data;
|
|
801
|
+
if (typeof row.data !== "string") return {};
|
|
802
|
+
try {
|
|
803
|
+
const parsed = JSON.parse(row.data);
|
|
804
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
805
|
+
} catch {
|
|
806
|
+
return {};
|
|
807
|
+
}
|
|
808
|
+
})();
|
|
809
|
+
return Object.freeze({
|
|
810
|
+
id: String(row.id),
|
|
811
|
+
store: typeof row.store === "string" ? row.store : "database",
|
|
812
|
+
data: Object.freeze(decodedData),
|
|
813
|
+
createdAt: normalizeDateLike(row.created_at),
|
|
814
|
+
lastActivityAt: normalizeDateLike(row.last_activity_at),
|
|
815
|
+
expiresAt: normalizeDateLike(row.expires_at),
|
|
816
|
+
rememberTokenHash: typeof row.remember_token_hash === "string" ? row.remember_token_hash : void 0
|
|
683
817
|
});
|
|
684
|
-
if (!cacheModule && required) {
|
|
685
|
-
throw new Error("[@holo-js/core] Cache support requires @holo-js/cache to be installed.");
|
|
686
|
-
}
|
|
687
|
-
return cacheModule;
|
|
688
|
-
}
|
|
689
|
-
function resetCacheRuntimeGlobalsFallback() {
|
|
690
|
-
const runtime = globalThis;
|
|
691
|
-
if (runtime.__holoCacheRuntime__) {
|
|
692
|
-
runtime.__holoCacheRuntime__.bindings = void 0;
|
|
693
|
-
}
|
|
694
|
-
if (runtime.__holoCacheQueryBridge__) {
|
|
695
|
-
runtime.__holoCacheQueryBridge__.dependencyIndex = void 0;
|
|
696
|
-
}
|
|
697
|
-
if (runtime.__holoDbQueryCacheBridge__) {
|
|
698
|
-
runtime.__holoDbQueryCacheBridge__.bridge = void 0;
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
async function loadEventsModule(required = false) {
|
|
702
|
-
const eventsModule = await portableRuntimeModuleInternals.importOptionalModule("@holo-js/events");
|
|
703
|
-
if (!eventsModule && required) {
|
|
704
|
-
throw new Error("[@holo-js/core] Events support requires @holo-js/events to be installed.");
|
|
705
|
-
}
|
|
706
|
-
return eventsModule;
|
|
707
|
-
}
|
|
708
|
-
async function loadSessionModule(required = false) {
|
|
709
|
-
const sessionModule = await portableRuntimeModuleInternals.importOptionalModule("@holo-js/session");
|
|
710
|
-
if (!sessionModule && required) {
|
|
711
|
-
throw new Error("[@holo-js/core] Session support requires @holo-js/session to be installed.");
|
|
712
|
-
}
|
|
713
|
-
return sessionModule;
|
|
714
|
-
}
|
|
715
|
-
async function loadSecurityModule(required = false) {
|
|
716
|
-
const securityModule = await portableRuntimeModuleInternals.importOptionalModule("@holo-js/security");
|
|
717
|
-
if (!securityModule && required) {
|
|
718
|
-
throw new Error("[@holo-js/core] Security support requires @holo-js/security to be installed.");
|
|
719
|
-
}
|
|
720
|
-
return securityModule;
|
|
721
|
-
}
|
|
722
|
-
async function loadSecurityRedisAdapterModule(required = false) {
|
|
723
|
-
const securityRedisAdapterModule = await portableRuntimeModuleInternals.importOptionalModule("@holo-js/security/drivers/redis-adapter");
|
|
724
|
-
if (!securityRedisAdapterModule && required) {
|
|
725
|
-
throw new Error("[@holo-js/core] Redis-backed security rate limits require @holo-js/security/drivers/redis-adapter to be installed.");
|
|
726
|
-
}
|
|
727
|
-
return securityRedisAdapterModule;
|
|
728
|
-
}
|
|
729
|
-
async function loadSessionRedisAdapterModule(required = false) {
|
|
730
|
-
const sessionRedisAdapterModule = await portableRuntimeModuleInternals.importOptionalModule("@holo-js/session/drivers/redis-adapter");
|
|
731
|
-
if (!sessionRedisAdapterModule && required) {
|
|
732
|
-
throw new Error("[@holo-js/core] Redis-backed session stores require @holo-js/session/drivers/redis-adapter to be installed.");
|
|
733
|
-
}
|
|
734
|
-
return sessionRedisAdapterModule;
|
|
735
818
|
}
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
819
|
+
function serializeSessionRecordForRow(record) {
|
|
820
|
+
return {
|
|
821
|
+
id: record.id,
|
|
822
|
+
store: record.store,
|
|
823
|
+
data: JSON.stringify(record.data ?? {}),
|
|
824
|
+
created_at: record.createdAt.toISOString(),
|
|
825
|
+
last_activity_at: record.lastActivityAt.toISOString(),
|
|
826
|
+
expires_at: record.expiresAt.toISOString(),
|
|
827
|
+
invalidated_at: null,
|
|
828
|
+
remember_token_hash: record.rememberTokenHash ?? null
|
|
829
|
+
};
|
|
742
830
|
}
|
|
743
|
-
|
|
744
|
-
const
|
|
745
|
-
|
|
831
|
+
function normalizeNotificationRecordFromRow(row) {
|
|
832
|
+
const decodedData = normalizeJsonValue(row.data);
|
|
833
|
+
return Object.freeze({
|
|
834
|
+
id: String(row.id),
|
|
835
|
+
type: typeof row.type === "string" ? row.type : void 0,
|
|
836
|
+
notifiableType: String(row.notifiable_type),
|
|
837
|
+
notifiableId: typeof row.notifiable_id === "number" ? row.notifiable_id : String(row.notifiable_id),
|
|
838
|
+
data: decodedData,
|
|
839
|
+
readAt: row.read_at ? normalizeDateLike(row.read_at) : null,
|
|
840
|
+
createdAt: normalizeDateLike(row.created_at),
|
|
841
|
+
updatedAt: normalizeDateLike(row.updated_at)
|
|
746
842
|
});
|
|
747
|
-
if (!broadcastModule && required) {
|
|
748
|
-
throw new Error("[@holo-js/core] Broadcast support requires @holo-js/broadcast to be installed.");
|
|
749
|
-
}
|
|
750
|
-
return broadcastModule;
|
|
751
|
-
}
|
|
752
|
-
async function loadMailModule(required = false) {
|
|
753
|
-
const mailModule = await portableRuntimeModuleInternals.importOptionalModule("@holo-js/mail");
|
|
754
|
-
if (!mailModule && required) {
|
|
755
|
-
throw new Error("[@holo-js/core] Mail support requires @holo-js/mail to be installed.");
|
|
756
|
-
}
|
|
757
|
-
return mailModule;
|
|
758
843
|
}
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
844
|
+
function serializeNotificationRecordForRow(record) {
|
|
845
|
+
return {
|
|
846
|
+
id: record.id,
|
|
847
|
+
type: record.type ?? null,
|
|
848
|
+
notifiable_type: record.notifiableType,
|
|
849
|
+
notifiable_id: String(record.notifiableId),
|
|
850
|
+
data: JSON.stringify(record.data ?? null),
|
|
851
|
+
read_at: record.readAt ? record.readAt.toISOString() : null,
|
|
852
|
+
created_at: record.createdAt.toISOString(),
|
|
853
|
+
updated_at: record.updatedAt.toISOString()
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
// src/portable/notificationPersistence.ts
|
|
858
|
+
import { DB } from "@holo-js/db";
|
|
859
|
+
function createCoreNotificationStore(loadedConfig) {
|
|
860
|
+
const tableName = loadedConfig.notifications.table;
|
|
861
|
+
const connectionName = loadedConfig.database.defaultConnection;
|
|
862
|
+
return Object.freeze({
|
|
863
|
+
async create(record) {
|
|
864
|
+
await DB.table(tableName, connectionName).insert(serializeNotificationRecordForRow(record));
|
|
865
|
+
},
|
|
866
|
+
async list(notifiable) {
|
|
867
|
+
const rows = await DB.table(tableName, connectionName).where("notifiable_type", notifiable.type).where("notifiable_id", String(notifiable.id)).orderBy("created_at", "desc").get();
|
|
868
|
+
return Object.freeze(rows.map((row) => normalizeNotificationRecordFromRow(row)));
|
|
869
|
+
},
|
|
870
|
+
async unread(notifiable) {
|
|
871
|
+
const rows = await DB.table(tableName, connectionName).where("notifiable_type", notifiable.type).where("notifiable_id", String(notifiable.id)).whereNull("read_at").orderBy("created_at", "desc").get();
|
|
872
|
+
return Object.freeze(rows.map((row) => normalizeNotificationRecordFromRow(row)));
|
|
873
|
+
},
|
|
874
|
+
async markAsRead(ids) {
|
|
875
|
+
if (ids.length === 0) return 0;
|
|
876
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
877
|
+
const result = await DB.table(tableName, connectionName).whereIn("id", ids).update({ read_at: now, updated_at: now });
|
|
878
|
+
return result.affectedRows ?? 0;
|
|
879
|
+
},
|
|
880
|
+
async markAsUnread(ids) {
|
|
881
|
+
if (ids.length === 0) return 0;
|
|
882
|
+
const result = await DB.table(tableName, connectionName).whereIn("id", ids).update({ read_at: null, updated_at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
883
|
+
return result.affectedRows ?? 0;
|
|
884
|
+
},
|
|
885
|
+
async delete(ids) {
|
|
886
|
+
if (ids.length === 0) return 0;
|
|
887
|
+
const result = await DB.table(tableName, connectionName).whereIn("id", ids).delete();
|
|
888
|
+
return result.affectedRows ?? 0;
|
|
889
|
+
}
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
// src/portable/authMailDelivery.ts
|
|
894
|
+
var authEmailDateFormatter = new Intl.DateTimeFormat("en-US", {
|
|
895
|
+
dateStyle: "long",
|
|
896
|
+
timeStyle: "short",
|
|
897
|
+
timeZone: "UTC"
|
|
898
|
+
});
|
|
899
|
+
function formatAuthEmailExpiration(expiresAt) {
|
|
900
|
+
return `${authEmailDateFormatter.format(expiresAt)} UTC`;
|
|
901
|
+
}
|
|
902
|
+
function escapeAuthEmailHtml(value) {
|
|
903
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
904
|
+
}
|
|
905
|
+
function createNotificationMailText(message) {
|
|
906
|
+
const parts = [
|
|
907
|
+
typeof message.greeting === "string" ? message.greeting.trim() : void 0,
|
|
908
|
+
...(message.lines ?? []).map((line) => line.trim()).filter(Boolean),
|
|
909
|
+
message.action ? `${message.action.label}: ${message.action.url}` : void 0
|
|
910
|
+
].filter((value) => typeof value === "string" && value.length > 0);
|
|
911
|
+
return parts.length > 0 ? parts.join("\n\n") : void 0;
|
|
912
|
+
}
|
|
913
|
+
function createNotificationMailHtml(message) {
|
|
914
|
+
const greeting = typeof message.greeting === "string" ? message.greeting.trim() : void 0;
|
|
915
|
+
const lines = (message.lines ?? []).map((line) => line.trim()).filter(Boolean);
|
|
916
|
+
const sections = [
|
|
917
|
+
greeting ? `<p style="margin:0 0 16px;">${escapeAuthEmailHtml(greeting)}</p>` : "",
|
|
918
|
+
...lines.map((line) => `<p style="margin:0 0 16px;">${escapeAuthEmailHtml(line)}</p>`),
|
|
919
|
+
message.action ? `<p style="margin:24px 0;"><a href="${escapeAuthEmailHtml(message.action.url)}" style="display:inline-block;padding:12px 18px;background:#111827;color:#ffffff;text-decoration:none;border-radius:8px;font-weight:600;">${escapeAuthEmailHtml(message.action.label)}</a></p>` : "",
|
|
920
|
+
message.action ? `<p style="margin:0;color:#475569;font-size:14px;">If the button does not work, open this link: <a href="${escapeAuthEmailHtml(message.action.url)}">${escapeAuthEmailHtml(message.action.url)}</a></p>` : ""
|
|
921
|
+
].join("");
|
|
922
|
+
return [
|
|
923
|
+
"<!doctype html>",
|
|
924
|
+
'<html><head><meta charset="utf-8">',
|
|
925
|
+
`<title>${escapeAuthEmailHtml(message.subject)}</title>`,
|
|
926
|
+
'</head><body style="margin:0;padding:24px;font-family:Arial,sans-serif;color:#0f172a;background:#f8fafc;">',
|
|
927
|
+
'<div style="max-width:640px;margin:0 auto;background:#ffffff;border:1px solid #e2e8f0;border-radius:12px;padding:32px;">',
|
|
928
|
+
`<h1 style="margin:0 0 24px;font-size:24px;line-height:1.3;">${escapeAuthEmailHtml(message.subject)}</h1>`,
|
|
929
|
+
sections,
|
|
930
|
+
"</div></body></html>"
|
|
931
|
+
].join("");
|
|
932
|
+
}
|
|
933
|
+
function createAuthActionUrl(appUrl, path, token) {
|
|
934
|
+
const normalizedBaseUrl = appUrl.endsWith("/") ? appUrl.slice(0, -1) : appUrl;
|
|
935
|
+
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
936
|
+
const url = new URL(`${normalizedBaseUrl}${normalizedPath}`);
|
|
937
|
+
url.searchParams.set("token", token);
|
|
938
|
+
return url.toString();
|
|
939
|
+
}
|
|
940
|
+
function createAuthEmailHtml(message) {
|
|
941
|
+
return createNotificationMailHtml(message);
|
|
942
|
+
}
|
|
943
|
+
function createCoreNotificationMailSender(mailModule) {
|
|
944
|
+
return Object.freeze({
|
|
945
|
+
async send(message, context) {
|
|
946
|
+
if (!context.route) {
|
|
947
|
+
throw new Error("[@holo-js/core] Email notifications require a resolved email route before bridging into mail.");
|
|
948
|
+
}
|
|
949
|
+
const fallbackText = createNotificationMailText(message);
|
|
950
|
+
await mailModule.sendMail({
|
|
951
|
+
to: context.route,
|
|
952
|
+
subject: message.subject,
|
|
953
|
+
html: typeof message.html === "string" ? message.html : createNotificationMailHtml(message),
|
|
954
|
+
...typeof (message.text ?? fallbackText) === "string" ? { text: message.text ?? fallbackText } : {},
|
|
955
|
+
...message.metadata ? { metadata: message.metadata } : {}
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
});
|
|
959
|
+
}
|
|
960
|
+
function createAuthMailDeliveryHook(mailModule, appUrl) {
|
|
961
|
+
return Object.freeze({
|
|
962
|
+
async sendEmailVerification(input) {
|
|
963
|
+
const recipientName = typeof input.user?.name === "string" ? input.user.name?.trim() : void 0;
|
|
964
|
+
const lines = [
|
|
965
|
+
"Confirm your account to finish signing in.",
|
|
966
|
+
`This verification link expires at ${formatAuthEmailExpiration(input.token.expiresAt)}.`
|
|
967
|
+
];
|
|
968
|
+
const action = {
|
|
969
|
+
label: "Verify email address",
|
|
970
|
+
url: createAuthActionUrl(appUrl, input.route, input.token.plainTextToken)
|
|
971
|
+
};
|
|
972
|
+
await mailModule.sendMail({
|
|
973
|
+
to: { email: input.email, ...recipientName ? { name: recipientName } : {} },
|
|
974
|
+
subject: "Verify your email address",
|
|
975
|
+
html: createAuthEmailHtml({
|
|
976
|
+
subject: "Verify your email address",
|
|
977
|
+
...recipientName ? { greeting: `Hello ${recipientName},` } : {},
|
|
978
|
+
lines,
|
|
979
|
+
action
|
|
980
|
+
}),
|
|
981
|
+
text: createNotificationMailText({
|
|
982
|
+
...recipientName ? { greeting: `Hello ${recipientName},` } : {},
|
|
983
|
+
lines,
|
|
984
|
+
action
|
|
985
|
+
}),
|
|
986
|
+
metadata: { provider: input.provider, tokenId: input.token.id }
|
|
987
|
+
});
|
|
988
|
+
},
|
|
989
|
+
async sendPasswordReset(input) {
|
|
990
|
+
const lines = [
|
|
991
|
+
"Click the link below to choose a new password.",
|
|
992
|
+
`This reset link expires at ${formatAuthEmailExpiration(input.token.expiresAt)}.`
|
|
993
|
+
];
|
|
994
|
+
const action = {
|
|
995
|
+
label: "Reset password",
|
|
996
|
+
url: createAuthActionUrl(appUrl, input.route, input.token.plainTextToken)
|
|
997
|
+
};
|
|
998
|
+
await mailModule.sendMail({
|
|
999
|
+
to: input.email,
|
|
1000
|
+
subject: "Reset your password",
|
|
1001
|
+
html: createAuthEmailHtml({ subject: "Reset your password", lines, action }),
|
|
1002
|
+
text: createNotificationMailText({ lines, action }),
|
|
1003
|
+
metadata: { provider: input.provider, tokenId: input.token.id }
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
// src/portable/optionalFeatureLoader.ts
|
|
1010
|
+
function createOptionalFeatureModuleLoader(importer, specifier, missingPackageMessage) {
|
|
1011
|
+
async function load(required = false, options = {}) {
|
|
1012
|
+
const module = await importer(specifier, options);
|
|
1013
|
+
if (!module && required) throw new Error(missingPackageMessage);
|
|
1014
|
+
return module;
|
|
1015
|
+
}
|
|
1016
|
+
return load;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
// src/portable/featureDetection.ts
|
|
1020
|
+
function hasLoadedConfigFile(loadedConfig, configName) {
|
|
1021
|
+
return loadedConfig.loadedFiles.some((filePath) => {
|
|
1022
|
+
const normalizedPath = filePath.replaceAll("\\", "/");
|
|
1023
|
+
return ["ts", "mts", "js", "mjs", "cts", "cjs"].some((extension) => normalizedPath.endsWith(`/config/${configName}.${extension}`));
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
1026
|
+
function queueConfigUsesDatabaseDriver(loadedConfig) {
|
|
1027
|
+
if (!loadedConfig.queue) return false;
|
|
1028
|
+
return Object.values(loadedConfig.queue.connections).some((connection) => connection.driver === "database");
|
|
1029
|
+
}
|
|
1030
|
+
function queueConfigUsesRedisDriver(loadedConfig) {
|
|
1031
|
+
if (!loadedConfig.queue) return false;
|
|
1032
|
+
return Object.values(loadedConfig.queue.connections).some((connection) => connection.driver === "redis");
|
|
1033
|
+
}
|
|
1034
|
+
function queueConfigUsesDatabaseBackedFailedStore(loadedConfig) {
|
|
1035
|
+
if (!loadedConfig.queue) return false;
|
|
1036
|
+
return loadedConfig.queue.failed !== false;
|
|
1037
|
+
}
|
|
1038
|
+
function registryHasJobs(registry) {
|
|
1039
|
+
return (registry?.jobs.length ?? 0) > 0;
|
|
1040
|
+
}
|
|
1041
|
+
function registryHasEvents(registry) {
|
|
1042
|
+
return (registry?.events.length ?? 0) > 0 || (registry?.listeners.length ?? 0) > 0;
|
|
1043
|
+
}
|
|
1044
|
+
function authConfigUsesSocialProviders(loadedConfig) {
|
|
1045
|
+
if (!loadedConfig.auth) return false;
|
|
1046
|
+
return Object.keys(loadedConfig.auth.social).length > 0;
|
|
1047
|
+
}
|
|
1048
|
+
function authConfigUsesWorkosProviders(loadedConfig) {
|
|
1049
|
+
if (!loadedConfig.auth) return false;
|
|
1050
|
+
return Object.entries(loadedConfig.auth.workos).some(([name, provider]) => name !== "provider" && name !== "identityStore" && typeof provider === "object" && provider !== null);
|
|
1051
|
+
}
|
|
1052
|
+
function authConfigUsesClerkProviders(loadedConfig) {
|
|
1053
|
+
if (!loadedConfig.auth) return false;
|
|
1054
|
+
return Object.entries(loadedConfig.auth.clerk).some(([name, provider]) => name !== "provider" && name !== "identityStore" && typeof provider === "object" && provider !== null);
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
// src/portable/runtimeState.ts
|
|
1058
|
+
function createRuntimeStateAccessors() {
|
|
1059
|
+
const getRuntimeState2 = () => {
|
|
1060
|
+
const runtime = globalThis;
|
|
1061
|
+
runtime.__holoRuntime__ ??= {};
|
|
1062
|
+
return runtime.__holoRuntime__;
|
|
1063
|
+
};
|
|
1064
|
+
const snapshotOptionalSubsystemRuntimeBindings2 = () => {
|
|
1065
|
+
const state = getRuntimeState2();
|
|
1066
|
+
const runtime = globalThis;
|
|
1067
|
+
return Object.freeze({
|
|
1068
|
+
...runtime.__holoMailRuntime__?.bindings ? { mail: runtime.__holoMailRuntime__.bindings } : {},
|
|
1069
|
+
...runtime.__holoNotificationsRuntime__?.bindings ? { notifications: runtime.__holoNotificationsRuntime__.bindings } : {},
|
|
1070
|
+
...runtime.__holoBroadcastRuntime__?.bindings ? { broadcast: runtime.__holoBroadcastRuntime__.bindings } : {},
|
|
1071
|
+
...state.sessionRedisAdapters ? { session: Object.freeze({ sessionRedisAdapters: state.sessionRedisAdapters }) } : {},
|
|
1072
|
+
...runtime.__holoSecurityRuntime__?.bindings || state.securityRedisAdapter || typeof state.securityRateLimitStoreManaged !== "undefined" ? {
|
|
1073
|
+
security: Object.freeze({
|
|
1074
|
+
...runtime.__holoSecurityRuntime__?.bindings ? { bindings: runtime.__holoSecurityRuntime__.bindings } : {},
|
|
1075
|
+
...state.securityRedisAdapter ? { securityRedisAdapter: state.securityRedisAdapter } : {},
|
|
1076
|
+
...typeof state.securityRateLimitStoreManaged !== "undefined" ? { securityRateLimitStoreManaged: state.securityRateLimitStoreManaged } : {}
|
|
1077
|
+
})
|
|
1078
|
+
} : {}
|
|
1079
|
+
});
|
|
1080
|
+
};
|
|
1081
|
+
const restoreOptionalSubsystemRuntimeBindings2 = (bindings) => {
|
|
1082
|
+
const state = getRuntimeState2();
|
|
1083
|
+
const runtime = globalThis;
|
|
1084
|
+
if (bindings.mail || runtime.__holoMailRuntime__) {
|
|
1085
|
+
runtime.__holoMailRuntime__ ??= {};
|
|
1086
|
+
runtime.__holoMailRuntime__.bindings = bindings.mail;
|
|
1087
|
+
}
|
|
1088
|
+
if (bindings.notifications || runtime.__holoNotificationsRuntime__) {
|
|
1089
|
+
runtime.__holoNotificationsRuntime__ ??= {};
|
|
1090
|
+
runtime.__holoNotificationsRuntime__.bindings = bindings.notifications;
|
|
1091
|
+
}
|
|
1092
|
+
if (bindings.broadcast || runtime.__holoBroadcastRuntime__) {
|
|
1093
|
+
runtime.__holoBroadcastRuntime__ ??= {};
|
|
1094
|
+
runtime.__holoBroadcastRuntime__.bindings = bindings.broadcast;
|
|
1095
|
+
}
|
|
1096
|
+
state.sessionRedisAdapters = bindings.session?.sessionRedisAdapters;
|
|
1097
|
+
if (bindings.security || runtime.__holoSecurityRuntime__) {
|
|
1098
|
+
runtime.__holoSecurityRuntime__ ??= {};
|
|
1099
|
+
runtime.__holoSecurityRuntime__.bindings = bindings.security?.bindings;
|
|
1100
|
+
state.securityRedisAdapter = bindings.security?.securityRedisAdapter;
|
|
1101
|
+
state.securityRateLimitStoreManaged = bindings.security?.securityRateLimitStoreManaged;
|
|
1102
|
+
}
|
|
1103
|
+
};
|
|
1104
|
+
return {
|
|
1105
|
+
getRuntimeState: getRuntimeState2,
|
|
1106
|
+
restoreOptionalSubsystemRuntimeBindings: restoreOptionalSubsystemRuntimeBindings2,
|
|
1107
|
+
snapshotOptionalSubsystemRuntimeBindings: snapshotOptionalSubsystemRuntimeBindings2
|
|
1108
|
+
};
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
// src/portable/holo.ts
|
|
1112
|
+
function closeSessionRedisAdapter(adapter) {
|
|
1113
|
+
return adapter.disconnect?.() || adapter.close?.();
|
|
1114
|
+
}
|
|
1115
|
+
var CORE_BROADCAST_PUBLISHER_MARKER = /* @__PURE__ */ Symbol.for("holo-js.core.broadcast.publisher");
|
|
1116
|
+
var frameworkBuildEnvKey = "HOLO_INTERNAL_FRAMEWORK_BUILD";
|
|
1117
|
+
function shouldBootRuntimeServices(processEnv = process.env) {
|
|
1118
|
+
return processEnv[frameworkBuildEnvKey] !== "1";
|
|
1119
|
+
}
|
|
1120
|
+
var {
|
|
1121
|
+
getRuntimeState,
|
|
1122
|
+
restoreOptionalSubsystemRuntimeBindings,
|
|
1123
|
+
snapshotOptionalSubsystemRuntimeBindings
|
|
1124
|
+
} = createRuntimeStateAccessors();
|
|
1125
|
+
var BROADCAST_PUBLISH_TIMEOUT_MS = 1e4;
|
|
1126
|
+
async function importOptionalModule2(specifier, options = {}) {
|
|
1127
|
+
return importOptionalRuntimeModule(specifier, options);
|
|
1128
|
+
}
|
|
1129
|
+
var portableRuntimeModuleInternals = {
|
|
1130
|
+
importOptionalModule: importOptionalModule2
|
|
1131
|
+
};
|
|
1132
|
+
var HOLO_AUTH_PROVIDER_MARKER = /* @__PURE__ */ Symbol.for("holo-js.auth.provider");
|
|
1133
|
+
var importOptionalFeature = (specifier, options) => portableRuntimeModuleInternals.importOptionalModule(specifier, options);
|
|
1134
|
+
var loadQueueModule = createOptionalFeatureModuleLoader(
|
|
1135
|
+
importOptionalFeature,
|
|
1136
|
+
"@holo-js/queue",
|
|
1137
|
+
"[@holo-js/core] Queue support requires @holo-js/queue to be installed."
|
|
1138
|
+
);
|
|
1139
|
+
async function loadConfiguredDatabaseDrivers(projectRoot, loadedConfig) {
|
|
1140
|
+
const packageByDriver = {
|
|
1141
|
+
sqlite: { packageName: "@holo-js/db-sqlite", factoryExport: "sqliteDatabaseDriverFactory" },
|
|
1142
|
+
postgres: { packageName: "@holo-js/db-postgres", factoryExport: "postgresDatabaseDriverFactory" },
|
|
1143
|
+
mysql: { packageName: "@holo-js/db-mysql", factoryExport: "mysqlDatabaseDriverFactory" }
|
|
1144
|
+
};
|
|
1145
|
+
const drivers = new Set(Object.values(loadedConfig.database.connections).map((connection) => {
|
|
1146
|
+
if (typeof connection === "string") {
|
|
1147
|
+
if (connection.startsWith("postgres://") || connection.startsWith("postgresql://")) return "postgres";
|
|
1148
|
+
if (connection.startsWith("mysql://") || connection.startsWith("mysql2://")) return "mysql";
|
|
1149
|
+
return "sqlite";
|
|
1150
|
+
}
|
|
1151
|
+
return connection.driver ?? "sqlite";
|
|
1152
|
+
}));
|
|
1153
|
+
const factories = [];
|
|
1154
|
+
for (const driver of drivers) {
|
|
1155
|
+
const contribution = packageByDriver[driver];
|
|
1156
|
+
const { packageName, factoryExport } = contribution;
|
|
1157
|
+
const module = await portableRuntimeModuleInternals.importOptionalModule(packageName, { projectRoot });
|
|
1158
|
+
if (!module) throw new Error(`[@holo-js/core] Database driver "${driver}" requires ${packageName} to be installed.`);
|
|
1159
|
+
const factory = module[factoryExport];
|
|
1160
|
+
if (!factory) throw new Error(`[@holo-js/core] Database driver package ${packageName} does not export ${factoryExport}.`);
|
|
1161
|
+
factories.push(factory);
|
|
1162
|
+
}
|
|
1163
|
+
return factories;
|
|
1164
|
+
}
|
|
1165
|
+
async function loadQueueDbModule(projectRoot) {
|
|
1166
|
+
return createOptionalFeatureModuleLoader(
|
|
1167
|
+
importOptionalFeature,
|
|
1168
|
+
"@holo-js/queue-db",
|
|
1169
|
+
"[@holo-js/core] Database queues require @holo-js/queue-db to be installed."
|
|
1170
|
+
)(false, { projectRoot });
|
|
1171
|
+
}
|
|
1172
|
+
async function loadQueueRedisModule(projectRoot) {
|
|
1173
|
+
return createOptionalFeatureModuleLoader(
|
|
1174
|
+
importOptionalFeature,
|
|
1175
|
+
"@holo-js/queue-redis",
|
|
1176
|
+
"[@holo-js/core] Redis queues require @holo-js/queue-redis to be installed."
|
|
1177
|
+
)(false, { projectRoot });
|
|
1178
|
+
}
|
|
1179
|
+
async function loadCacheModule(required = false, projectRoot) {
|
|
1180
|
+
const loader = createOptionalFeatureModuleLoader(
|
|
1181
|
+
importOptionalFeature,
|
|
1182
|
+
"@holo-js/cache",
|
|
1183
|
+
"[@holo-js/core] Cache support requires @holo-js/cache to be installed."
|
|
1184
|
+
);
|
|
1185
|
+
return required ? loader(true, { projectRoot }) : loader(false, { projectRoot });
|
|
1186
|
+
}
|
|
1187
|
+
async function loadConfiguredCacheDrivers(projectRoot, cacheConfig) {
|
|
1188
|
+
const packageByDriver = {
|
|
1189
|
+
database: "@holo-js/cache-db",
|
|
1190
|
+
redis: "@holo-js/cache-redis"
|
|
1191
|
+
};
|
|
1192
|
+
const drivers = new Set(Object.values(cacheConfig.drivers).map((driver) => driver.driver));
|
|
1193
|
+
for (const driver of drivers) {
|
|
1194
|
+
if (driver !== "database" && driver !== "redis") continue;
|
|
1195
|
+
const packageName = packageByDriver[driver];
|
|
1196
|
+
const module = await portableRuntimeModuleInternals.importOptionalModule(packageName, { projectRoot });
|
|
1197
|
+
if (!module) throw new Error(`[@holo-js/core] Cache driver "${driver}" requires ${packageName} to be installed.`);
|
|
1198
|
+
}
|
|
786
1199
|
}
|
|
787
|
-
|
|
788
|
-
const
|
|
789
|
-
if (
|
|
790
|
-
|
|
1200
|
+
function resetCacheRuntimeGlobalsFallback() {
|
|
1201
|
+
const runtime = globalThis;
|
|
1202
|
+
if (runtime.__holoCacheRuntime__) {
|
|
1203
|
+
runtime.__holoCacheRuntime__.bindings = void 0;
|
|
1204
|
+
}
|
|
1205
|
+
if (runtime.__holoCacheQueryBridge__) {
|
|
1206
|
+
runtime.__holoCacheQueryBridge__.dependencyIndex = void 0;
|
|
1207
|
+
}
|
|
1208
|
+
if (runtime.__holoDbQueryCacheBridge__) {
|
|
1209
|
+
runtime.__holoDbQueryCacheBridge__.bridge = void 0;
|
|
791
1210
|
}
|
|
792
|
-
return clerkModule;
|
|
793
1211
|
}
|
|
1212
|
+
var loadEventsModule = createOptionalFeatureModuleLoader(
|
|
1213
|
+
importOptionalFeature,
|
|
1214
|
+
"@holo-js/events",
|
|
1215
|
+
"[@holo-js/core] Events support requires @holo-js/events to be installed."
|
|
1216
|
+
);
|
|
1217
|
+
var loadSessionModule = createOptionalFeatureModuleLoader(
|
|
1218
|
+
importOptionalFeature,
|
|
1219
|
+
"@holo-js/session",
|
|
1220
|
+
"[@holo-js/core] Session support requires @holo-js/session to be installed."
|
|
1221
|
+
);
|
|
1222
|
+
var loadSecurityModule = createOptionalFeatureModuleLoader(
|
|
1223
|
+
importOptionalFeature,
|
|
1224
|
+
"@holo-js/security",
|
|
1225
|
+
"[@holo-js/core] Security support requires @holo-js/security to be installed."
|
|
1226
|
+
);
|
|
1227
|
+
var loadSecurityRedisAdapterModule = createOptionalFeatureModuleLoader(
|
|
1228
|
+
importOptionalFeature,
|
|
1229
|
+
"@holo-js/security/drivers/redis-adapter",
|
|
1230
|
+
"[@holo-js/core] Redis-backed security rate limits require @holo-js/security/drivers/redis-adapter to be installed."
|
|
1231
|
+
);
|
|
1232
|
+
var loadSessionRedisAdapterModule = createOptionalFeatureModuleLoader(
|
|
1233
|
+
importOptionalFeature,
|
|
1234
|
+
"@holo-js/session/drivers/redis-adapter",
|
|
1235
|
+
"[@holo-js/core] Redis-backed session stores require @holo-js/session/drivers/redis-adapter to be installed."
|
|
1236
|
+
);
|
|
1237
|
+
var loadNotificationsModule = createOptionalFeatureModuleLoader(
|
|
1238
|
+
importOptionalFeature,
|
|
1239
|
+
"@holo-js/notifications",
|
|
1240
|
+
"[@holo-js/core] Notifications support requires @holo-js/notifications to be installed."
|
|
1241
|
+
);
|
|
1242
|
+
async function loadBroadcastModule(required = false, projectRoot) {
|
|
1243
|
+
const loader = createOptionalFeatureModuleLoader(
|
|
1244
|
+
importOptionalFeature,
|
|
1245
|
+
"@holo-js/broadcast",
|
|
1246
|
+
"[@holo-js/core] Broadcast support requires @holo-js/broadcast to be installed."
|
|
1247
|
+
);
|
|
1248
|
+
return required ? loader(true, { projectRoot }) : loader(false, { projectRoot });
|
|
1249
|
+
}
|
|
1250
|
+
var loadMailModule = createOptionalFeatureModuleLoader(
|
|
1251
|
+
importOptionalFeature,
|
|
1252
|
+
"@holo-js/mail",
|
|
1253
|
+
"[@holo-js/core] Mail support requires @holo-js/mail to be installed."
|
|
1254
|
+
);
|
|
1255
|
+
var loadAuthModule = createOptionalFeatureModuleLoader(
|
|
1256
|
+
importOptionalFeature,
|
|
1257
|
+
"@holo-js/auth",
|
|
1258
|
+
"[@holo-js/core] Auth support requires @holo-js/auth to be installed."
|
|
1259
|
+
);
|
|
1260
|
+
var loadAuthorizationModule = createOptionalFeatureModuleLoader(
|
|
1261
|
+
importOptionalFeature,
|
|
1262
|
+
"@holo-js/authorization",
|
|
1263
|
+
"[@holo-js/core] Authorization support requires @holo-js/authorization to be installed."
|
|
1264
|
+
);
|
|
1265
|
+
var loadSocialModule = createOptionalFeatureModuleLoader(
|
|
1266
|
+
importOptionalFeature,
|
|
1267
|
+
"@holo-js/auth-social",
|
|
1268
|
+
"[@holo-js/core] Social auth config requires @holo-js/auth-social to be installed."
|
|
1269
|
+
);
|
|
1270
|
+
var loadWorkosModule = createOptionalFeatureModuleLoader(
|
|
1271
|
+
importOptionalFeature,
|
|
1272
|
+
"@holo-js/auth-workos",
|
|
1273
|
+
"[@holo-js/core] WorkOS auth config requires @holo-js/auth-workos to be installed."
|
|
1274
|
+
);
|
|
1275
|
+
var loadClerkModule = createOptionalFeatureModuleLoader(
|
|
1276
|
+
importOptionalFeature,
|
|
1277
|
+
"@holo-js/auth-clerk",
|
|
1278
|
+
"[@holo-js/core] Clerk auth config requires @holo-js/auth-clerk to be installed."
|
|
1279
|
+
);
|
|
794
1280
|
function resolveQueueJobExport(queueModule, moduleValue) {
|
|
795
1281
|
const exports = moduleValue;
|
|
796
1282
|
if (queueModule.isQueueJobDefinition(exports.default)) {
|
|
@@ -831,74 +1317,7 @@ function resolveListenerExport(eventsModule, moduleValue) {
|
|
|
831
1317
|
return Object.values(exports).find((value) => hasListenerDefinitionMarker(value) || eventsModule.isListenerDefinition(value));
|
|
832
1318
|
}
|
|
833
1319
|
function resolveProjectRelativePath(projectRoot, value) {
|
|
834
|
-
return value.startsWith(".") || !value.startsWith("/") ?
|
|
835
|
-
}
|
|
836
|
-
function normalizeDateLike(value) {
|
|
837
|
-
return value instanceof Date ? value : new Date(String(value));
|
|
838
|
-
}
|
|
839
|
-
function normalizeSessionRecordFromRow(row) {
|
|
840
|
-
const decodedData = (() => {
|
|
841
|
-
if (row.data && typeof row.data === "object") {
|
|
842
|
-
return row.data;
|
|
843
|
-
}
|
|
844
|
-
if (typeof row.data === "string") {
|
|
845
|
-
try {
|
|
846
|
-
const parsed = JSON.parse(row.data);
|
|
847
|
-
return parsed && typeof parsed === "object" ? parsed : {};
|
|
848
|
-
} catch {
|
|
849
|
-
return {};
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
return {};
|
|
853
|
-
})();
|
|
854
|
-
return Object.freeze({
|
|
855
|
-
id: String(row.id),
|
|
856
|
-
/* v8 ignore next -- runtime rows usually carry an explicit store name; this preserves a safe default */
|
|
857
|
-
store: typeof row.store === "string" ? row.store : "database",
|
|
858
|
-
data: Object.freeze(decodedData),
|
|
859
|
-
createdAt: normalizeDateLike(row.created_at),
|
|
860
|
-
lastActivityAt: normalizeDateLike(row.last_activity_at),
|
|
861
|
-
expiresAt: normalizeDateLike(row.expires_at),
|
|
862
|
-
rememberTokenHash: typeof row.remember_token_hash === "string" ? row.remember_token_hash : void 0
|
|
863
|
-
});
|
|
864
|
-
}
|
|
865
|
-
function serializeSessionRecordForRow(record) {
|
|
866
|
-
return {
|
|
867
|
-
id: record.id,
|
|
868
|
-
store: record.store,
|
|
869
|
-
/* v8 ignore next -- record.data is always present in runtime flows; nullish fallback is purely defensive */
|
|
870
|
-
data: JSON.stringify(record.data ?? {}),
|
|
871
|
-
created_at: record.createdAt.toISOString(),
|
|
872
|
-
last_activity_at: record.lastActivityAt.toISOString(),
|
|
873
|
-
expires_at: record.expiresAt.toISOString(),
|
|
874
|
-
invalidated_at: null,
|
|
875
|
-
remember_token_hash: record.rememberTokenHash ?? null
|
|
876
|
-
};
|
|
877
|
-
}
|
|
878
|
-
function normalizeNotificationRecordFromRow(row) {
|
|
879
|
-
const decodedData = normalizeJsonValue(row.data);
|
|
880
|
-
return Object.freeze({
|
|
881
|
-
id: String(row.id),
|
|
882
|
-
type: typeof row.type === "string" ? row.type : void 0,
|
|
883
|
-
notifiableType: String(row.notifiable_type),
|
|
884
|
-
notifiableId: typeof row.notifiable_id === "number" ? row.notifiable_id : String(row.notifiable_id),
|
|
885
|
-
data: decodedData,
|
|
886
|
-
readAt: row.read_at ? normalizeDateLike(row.read_at) : null,
|
|
887
|
-
createdAt: normalizeDateLike(row.created_at),
|
|
888
|
-
updatedAt: normalizeDateLike(row.updated_at)
|
|
889
|
-
});
|
|
890
|
-
}
|
|
891
|
-
function serializeNotificationRecordForRow(record) {
|
|
892
|
-
return {
|
|
893
|
-
id: record.id,
|
|
894
|
-
type: record.type ?? null,
|
|
895
|
-
notifiable_type: record.notifiableType,
|
|
896
|
-
notifiable_id: String(record.notifiableId),
|
|
897
|
-
data: JSON.stringify(record.data ?? null),
|
|
898
|
-
read_at: record.readAt ? record.readAt.toISOString() : null,
|
|
899
|
-
created_at: record.createdAt.toISOString(),
|
|
900
|
-
updated_at: record.updatedAt.toISOString()
|
|
901
|
-
};
|
|
1320
|
+
return value.startsWith(".") || !value.startsWith("/") ? resolve5(projectRoot, value) : value;
|
|
902
1321
|
}
|
|
903
1322
|
function getEntityAttributes(value) {
|
|
904
1323
|
if (value && typeof value === "object") {
|
|
@@ -942,20 +1361,20 @@ async function createCoreManagedSessionStores(projectRoot, loadedConfig, session
|
|
|
942
1361
|
const connectionName = config.connection === "default" && !(config.connection in loadedConfig.database.connections) ? loadedConfig.database.defaultConnection : config.connection;
|
|
943
1362
|
stores[name] = sessionModule.createDatabaseSessionStore({
|
|
944
1363
|
async read(sessionId) {
|
|
945
|
-
const row = await
|
|
1364
|
+
const row = await DB2.table(config.table, connectionName).where("id", sessionId).whereNull("invalidated_at").first();
|
|
946
1365
|
return row ? normalizeSessionRecordFromRow(row) : null;
|
|
947
1366
|
},
|
|
948
1367
|
async write(record) {
|
|
949
1368
|
const normalized = serializeSessionRecordForRow(record);
|
|
950
|
-
const existing = await
|
|
1369
|
+
const existing = await DB2.table(config.table, connectionName).find(String(normalized.id));
|
|
951
1370
|
if (existing) {
|
|
952
|
-
await
|
|
1371
|
+
await DB2.table(config.table, connectionName).where("id", normalized.id).update(normalized);
|
|
953
1372
|
return;
|
|
954
1373
|
}
|
|
955
|
-
await
|
|
1374
|
+
await DB2.table(config.table, connectionName).insert(normalized);
|
|
956
1375
|
},
|
|
957
1376
|
async delete(sessionId) {
|
|
958
|
-
await
|
|
1377
|
+
await DB2.table(config.table, connectionName).where("id", sessionId).delete();
|
|
959
1378
|
}
|
|
960
1379
|
});
|
|
961
1380
|
continue;
|
|
@@ -1000,59 +1419,6 @@ async function createCoreManagedSessionStores(projectRoot, loadedConfig, session
|
|
|
1000
1419
|
async function createCoreSessionStores(projectRoot, loadedConfig, sessionModule) {
|
|
1001
1420
|
return (await createCoreManagedSessionStores(projectRoot, loadedConfig, sessionModule)).stores;
|
|
1002
1421
|
}
|
|
1003
|
-
function createCoreNotificationStore(loadedConfig) {
|
|
1004
|
-
const tableName = loadedConfig.notifications.table;
|
|
1005
|
-
const connectionName = loadedConfig.database.defaultConnection;
|
|
1006
|
-
const store = {
|
|
1007
|
-
async create(record) {
|
|
1008
|
-
await DB.table(tableName, connectionName).insert(serializeNotificationRecordForRow(record));
|
|
1009
|
-
},
|
|
1010
|
-
async list(notifiable) {
|
|
1011
|
-
const rows = await DB.table(tableName, connectionName).where("notifiable_type", notifiable.type).where("notifiable_id", String(notifiable.id)).orderBy("created_at", "desc").get();
|
|
1012
|
-
return Object.freeze(rows.map((row) => normalizeNotificationRecordFromRow(row)));
|
|
1013
|
-
},
|
|
1014
|
-
async unread(notifiable) {
|
|
1015
|
-
const rows = await DB.table(tableName, connectionName).where("notifiable_type", notifiable.type).where("notifiable_id", String(notifiable.id)).whereNull("read_at").orderBy("created_at", "desc").get();
|
|
1016
|
-
return Object.freeze(rows.map((row) => normalizeNotificationRecordFromRow(row)));
|
|
1017
|
-
},
|
|
1018
|
-
async markAsRead(ids) {
|
|
1019
|
-
if (ids.length === 0) {
|
|
1020
|
-
return 0;
|
|
1021
|
-
}
|
|
1022
|
-
const result = await DB.table(tableName, connectionName).whereIn("id", ids).update({
|
|
1023
|
-
read_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1024
|
-
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1025
|
-
});
|
|
1026
|
-
return result.affectedRows ?? 0;
|
|
1027
|
-
},
|
|
1028
|
-
async markAsUnread(ids) {
|
|
1029
|
-
if (ids.length === 0) {
|
|
1030
|
-
return 0;
|
|
1031
|
-
}
|
|
1032
|
-
const result = await DB.table(tableName, connectionName).whereIn("id", ids).update({
|
|
1033
|
-
read_at: null,
|
|
1034
|
-
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1035
|
-
});
|
|
1036
|
-
return result.affectedRows ?? 0;
|
|
1037
|
-
},
|
|
1038
|
-
async delete(ids) {
|
|
1039
|
-
if (ids.length === 0) {
|
|
1040
|
-
return 0;
|
|
1041
|
-
}
|
|
1042
|
-
const result = await DB.table(tableName, connectionName).whereIn("id", ids).delete();
|
|
1043
|
-
return result.affectedRows ?? 0;
|
|
1044
|
-
}
|
|
1045
|
-
};
|
|
1046
|
-
return Object.freeze(store);
|
|
1047
|
-
}
|
|
1048
|
-
var authEmailDateFormatter = new Intl.DateTimeFormat("en-US", {
|
|
1049
|
-
dateStyle: "long",
|
|
1050
|
-
timeStyle: "short",
|
|
1051
|
-
timeZone: "UTC"
|
|
1052
|
-
});
|
|
1053
|
-
function formatAuthEmailExpiration(expiresAt) {
|
|
1054
|
-
return `${authEmailDateFormatter.format(expiresAt)} UTC`;
|
|
1055
|
-
}
|
|
1056
1422
|
var AUTH_EMAIL_VERIFICATION_NOTIFICATION_PATHS = [
|
|
1057
1423
|
"server/notifications/auth/email-verification.ts",
|
|
1058
1424
|
"server/notifications/auth/email-verification.mts",
|
|
@@ -1073,7 +1439,7 @@ function resolveExistingProjectFile(projectRoot, candidates) {
|
|
|
1073
1439
|
if (!projectRoot) {
|
|
1074
1440
|
return void 0;
|
|
1075
1441
|
}
|
|
1076
|
-
return candidates.find((candidate) =>
|
|
1442
|
+
return candidates.find((candidate) => existsSync2(resolve5(projectRoot, candidate)));
|
|
1077
1443
|
}
|
|
1078
1444
|
function resolveAuthNotification(module, exportName, filePath) {
|
|
1079
1445
|
const notification = module[exportName] ?? module.notification ?? module.default;
|
|
@@ -1232,219 +1598,92 @@ function createCoreNotificationBroadcaster(broadcastModule) {
|
|
|
1232
1598
|
event,
|
|
1233
1599
|
channels,
|
|
1234
1600
|
payload: Object.freeze({
|
|
1235
|
-
...typeof context.notificationType === "string" && context.notificationType.trim() ? { type: context.notificationType.trim() } : {},
|
|
1236
|
-
data: message.data ?? null
|
|
1237
|
-
})
|
|
1238
|
-
});
|
|
1239
|
-
}
|
|
1240
|
-
});
|
|
1241
|
-
}
|
|
1242
|
-
function createCoreBroadcastPublisher(loadedConfig) {
|
|
1243
|
-
const connectionHosts = /* @__PURE__ */ new Set(["holo", "pusher"]);
|
|
1244
|
-
const publish = async (input, context) => {
|
|
1245
|
-
const connection = loadedConfig.connections[input.connection];
|
|
1246
|
-
if (!connection) {
|
|
1247
|
-
throw new Error(`[@holo-js/core] Broadcast connection "${input.connection}" is not configured.`);
|
|
1248
|
-
}
|
|
1249
|
-
if (!("appId" in connection) || !("key" in connection) || !("secret" in connection)) {
|
|
1250
|
-
throw new Error(`[@holo-js/core] Broadcast connection "${input.connection}" cannot be published automatically.`);
|
|
1251
|
-
}
|
|
1252
|
-
if (!connectionHosts.has(connection.driver)) {
|
|
1253
|
-
throw new Error(`[@holo-js/core] Broadcast connection "${input.connection}" cannot be published automatically.`);
|
|
1254
|
-
}
|
|
1255
|
-
const options = connection.options;
|
|
1256
|
-
const protocol = options.scheme === "http" ? "http:" : "https:";
|
|
1257
|
-
const url = new URL(`/apps/${encodeURIComponent(connection.appId)}/events`, `${protocol}//${options.host}`);
|
|
1258
|
-
if (typeof options.port === "number") {
|
|
1259
|
-
url.port = String(options.port);
|
|
1260
|
-
}
|
|
1261
|
-
const body = JSON.stringify({
|
|
1262
|
-
name: input.event,
|
|
1263
|
-
channels: input.channels,
|
|
1264
|
-
data: JSON.stringify(input.payload),
|
|
1265
|
-
/* v8 ignore next -- tests do not pass socketId through the publish binding */
|
|
1266
|
-
...typeof input.socketId === "undefined" ? {} : { socket_id: input.socketId }
|
|
1267
|
-
});
|
|
1268
|
-
const bodyMd5 = createHash("md5").update(body).digest("hex");
|
|
1269
|
-
url.searchParams.set("auth_key", connection.key);
|
|
1270
|
-
url.searchParams.set("auth_timestamp", String(Math.floor(Date.now() / 1e3)));
|
|
1271
|
-
url.searchParams.set("auth_version", "1.0");
|
|
1272
|
-
url.searchParams.set("body_md5", bodyMd5);
|
|
1273
|
-
url.searchParams.set(
|
|
1274
|
-
"auth_signature",
|
|
1275
|
-
createHmac("sha256", connection.secret).update(
|
|
1276
|
-
[
|
|
1277
|
-
"POST",
|
|
1278
|
-
url.pathname,
|
|
1279
|
-
[...url.searchParams.entries()].filter(([key]) => key !== "auth_signature").sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join("&")
|
|
1280
|
-
].join("\n")
|
|
1281
|
-
).digest("hex")
|
|
1282
|
-
);
|
|
1283
|
-
const controller = new AbortController();
|
|
1284
|
-
const timeout = setTimeout(() => controller.abort(), BROADCAST_PUBLISH_TIMEOUT_MS);
|
|
1285
|
-
let response;
|
|
1286
|
-
try {
|
|
1287
|
-
response = await fetch(url, {
|
|
1288
|
-
method: "POST",
|
|
1289
|
-
headers: {
|
|
1290
|
-
"content-type": "application/json"
|
|
1291
|
-
},
|
|
1292
|
-
body,
|
|
1293
|
-
signal: controller.signal
|
|
1294
|
-
});
|
|
1295
|
-
} catch (error) {
|
|
1296
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
1297
|
-
throw new Error(`[@holo-js/core] Broadcast publish request timed out after ${BROADCAST_PUBLISH_TIMEOUT_MS}ms.`);
|
|
1298
|
-
}
|
|
1299
|
-
throw error;
|
|
1300
|
-
} finally {
|
|
1301
|
-
clearTimeout(timeout);
|
|
1302
|
-
}
|
|
1303
|
-
if (!response.ok) {
|
|
1304
|
-
throw new Error(`[@holo-js/core] Broadcast publish request failed with status ${response.status}.`);
|
|
1305
|
-
}
|
|
1306
|
-
const result = await response.json();
|
|
1307
|
-
return {
|
|
1308
|
-
connection: input.connection,
|
|
1309
|
-
driver: connection.driver,
|
|
1310
|
-
queued: context.queued,
|
|
1311
|
-
publishedChannels: Array.isArray(result.deliveredChannels) ? Object.freeze(result.deliveredChannels.map((value) => String(value))) : Object.freeze([...input.channels])
|
|
1312
|
-
};
|
|
1313
|
-
};
|
|
1314
|
-
Object.defineProperty(publish, CORE_BROADCAST_PUBLISHER_MARKER, {
|
|
1315
|
-
value: true
|
|
1316
|
-
});
|
|
1317
|
-
return publish;
|
|
1318
|
-
}
|
|
1319
|
-
function isCoreBroadcastPublisher(value) {
|
|
1320
|
-
return typeof value === "function" && CORE_BROADCAST_PUBLISHER_MARKER in value;
|
|
1321
|
-
}
|
|
1322
|
-
function createNotificationMailText(message) {
|
|
1323
|
-
const parts = [
|
|
1324
|
-
typeof message.greeting === "string" ? message.greeting.trim() : void 0,
|
|
1325
|
-
...(message.lines ?? []).map((line) => line.trim()).filter(Boolean),
|
|
1326
|
-
message.action ? `${message.action.label}: ${message.action.url}` : void 0
|
|
1327
|
-
].filter((value) => typeof value === "string" && value.length > 0);
|
|
1328
|
-
return parts.length > 0 ? parts.join("\n\n") : void 0;
|
|
1329
|
-
}
|
|
1330
|
-
function createNotificationMailHtml(message) {
|
|
1331
|
-
const greeting = typeof message.greeting === "string" ? message.greeting.trim() : void 0;
|
|
1332
|
-
const lines = (message.lines ?? []).map((line) => line.trim()).filter(Boolean);
|
|
1333
|
-
const sections = [
|
|
1334
|
-
greeting ? `<p style="margin:0 0 16px;">${escapeAuthEmailHtml(greeting)}</p>` : "",
|
|
1335
|
-
...lines.map((line) => `<p style="margin:0 0 16px;">${escapeAuthEmailHtml(line)}</p>`),
|
|
1336
|
-
message.action ? `<p style="margin:24px 0;"><a href="${escapeAuthEmailHtml(message.action.url)}" style="display:inline-block;padding:12px 18px;background:#111827;color:#ffffff;text-decoration:none;border-radius:8px;font-weight:600;">${escapeAuthEmailHtml(message.action.label)}</a></p>` : "",
|
|
1337
|
-
message.action ? `<p style="margin:0;color:#475569;font-size:14px;">If the button does not work, open this link: <a href="${escapeAuthEmailHtml(message.action.url)}">${escapeAuthEmailHtml(message.action.url)}</a></p>` : ""
|
|
1338
|
-
].join("");
|
|
1339
|
-
return [
|
|
1340
|
-
"<!doctype html>",
|
|
1341
|
-
'<html><head><meta charset="utf-8">',
|
|
1342
|
-
`<title>${escapeAuthEmailHtml(message.subject)}</title>`,
|
|
1343
|
-
'</head><body style="margin:0;padding:24px;font-family:Arial,sans-serif;color:#0f172a;background:#f8fafc;">',
|
|
1344
|
-
'<div style="max-width:640px;margin:0 auto;background:#ffffff;border:1px solid #e2e8f0;border-radius:12px;padding:32px;">',
|
|
1345
|
-
`<h1 style="margin:0 0 24px;font-size:24px;line-height:1.3;">${escapeAuthEmailHtml(message.subject)}</h1>`,
|
|
1346
|
-
sections,
|
|
1347
|
-
"</div></body></html>"
|
|
1348
|
-
].join("");
|
|
1349
|
-
}
|
|
1350
|
-
function joinAppUrl(baseUrl, path) {
|
|
1351
|
-
const normalizedBaseUrl = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
|
|
1352
|
-
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
1353
|
-
return `${normalizedBaseUrl}${normalizedPath}`;
|
|
1354
|
-
}
|
|
1355
|
-
function createAuthActionUrl(appUrl, path, token) {
|
|
1356
|
-
const url = new URL(joinAppUrl(appUrl, path));
|
|
1357
|
-
url.searchParams.set("token", token);
|
|
1358
|
-
return url.toString();
|
|
1359
|
-
}
|
|
1360
|
-
function escapeAuthEmailHtml(value) {
|
|
1361
|
-
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
1362
|
-
}
|
|
1363
|
-
function createAuthEmailHtml(message) {
|
|
1364
|
-
return createNotificationMailHtml(message);
|
|
1365
|
-
}
|
|
1366
|
-
function createCoreNotificationMailSender(mailModule) {
|
|
1367
|
-
return Object.freeze({
|
|
1368
|
-
async send(message, context) {
|
|
1369
|
-
const route = context.route;
|
|
1370
|
-
if (!route) {
|
|
1371
|
-
throw new Error("[@holo-js/core] Email notifications require a resolved email route before bridging into mail.");
|
|
1372
|
-
}
|
|
1373
|
-
const fallbackText = createNotificationMailText(message);
|
|
1374
|
-
const fallbackHtml = createNotificationMailHtml(message);
|
|
1375
|
-
await mailModule.sendMail({
|
|
1376
|
-
to: route,
|
|
1377
|
-
subject: message.subject,
|
|
1378
|
-
html: typeof message.html === "string" ? message.html : fallbackHtml,
|
|
1379
|
-
...typeof (message.text ?? fallbackText) === "string" ? { text: message.text ?? fallbackText } : {},
|
|
1380
|
-
...message.metadata ? { metadata: message.metadata } : {}
|
|
1381
|
-
});
|
|
1382
|
-
}
|
|
1383
|
-
});
|
|
1384
|
-
}
|
|
1385
|
-
function createAuthMailDeliveryHook(mailModule, appUrl) {
|
|
1386
|
-
return Object.freeze({
|
|
1387
|
-
async sendEmailVerification(input) {
|
|
1388
|
-
const recipientName = typeof input.user?.name === "string" ? input.user.name?.trim() : void 0;
|
|
1389
|
-
const lines = [
|
|
1390
|
-
"Confirm your account to finish signing in.",
|
|
1391
|
-
`This verification link expires at ${formatAuthEmailExpiration(input.token.expiresAt)}.`
|
|
1392
|
-
];
|
|
1393
|
-
const action = {
|
|
1394
|
-
label: "Verify email address",
|
|
1395
|
-
url: createAuthActionUrl(appUrl, input.route, input.token.plainTextToken)
|
|
1396
|
-
};
|
|
1397
|
-
await mailModule.sendMail({
|
|
1398
|
-
to: {
|
|
1399
|
-
email: input.email,
|
|
1400
|
-
...recipientName ? { name: recipientName } : {}
|
|
1401
|
-
},
|
|
1402
|
-
subject: "Verify your email address",
|
|
1403
|
-
html: createAuthEmailHtml({
|
|
1404
|
-
subject: "Verify your email address",
|
|
1405
|
-
...recipientName ? { greeting: `Hello ${recipientName},` } : {},
|
|
1406
|
-
lines,
|
|
1407
|
-
action
|
|
1408
|
-
}),
|
|
1409
|
-
text: createNotificationMailText({
|
|
1410
|
-
...recipientName ? { greeting: `Hello ${recipientName},` } : {},
|
|
1411
|
-
lines,
|
|
1412
|
-
action
|
|
1413
|
-
}),
|
|
1414
|
-
metadata: {
|
|
1415
|
-
provider: input.provider,
|
|
1416
|
-
tokenId: input.token.id
|
|
1417
|
-
}
|
|
1601
|
+
...typeof context.notificationType === "string" && context.notificationType.trim() ? { type: context.notificationType.trim() } : {},
|
|
1602
|
+
data: message.data ?? null
|
|
1603
|
+
})
|
|
1418
1604
|
});
|
|
1419
|
-
}
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1605
|
+
}
|
|
1606
|
+
});
|
|
1607
|
+
}
|
|
1608
|
+
function createCoreBroadcastPublisher(loadedConfig) {
|
|
1609
|
+
const connectionHosts = /* @__PURE__ */ new Set(["holo", "pusher"]);
|
|
1610
|
+
const publish = async (input, context) => {
|
|
1611
|
+
const connection = loadedConfig.connections[input.connection];
|
|
1612
|
+
if (!connection) {
|
|
1613
|
+
throw new Error(`[@holo-js/core] Broadcast connection "${input.connection}" is not configured.`);
|
|
1614
|
+
}
|
|
1615
|
+
if (!("appId" in connection) || !("key" in connection) || !("secret" in connection)) {
|
|
1616
|
+
throw new Error(`[@holo-js/core] Broadcast connection "${input.connection}" cannot be published automatically.`);
|
|
1617
|
+
}
|
|
1618
|
+
if (!connectionHosts.has(connection.driver)) {
|
|
1619
|
+
throw new Error(`[@holo-js/core] Broadcast connection "${input.connection}" cannot be published automatically.`);
|
|
1620
|
+
}
|
|
1621
|
+
const options = connection.options;
|
|
1622
|
+
const protocol = options.scheme === "http" ? "http:" : "https:";
|
|
1623
|
+
const url = new URL(`/apps/${encodeURIComponent(connection.appId)}/events`, `${protocol}//${options.host}`);
|
|
1624
|
+
if (typeof options.port === "number") {
|
|
1625
|
+
url.port = String(options.port);
|
|
1626
|
+
}
|
|
1627
|
+
const body = JSON.stringify({
|
|
1628
|
+
name: input.event,
|
|
1629
|
+
channels: input.channels,
|
|
1630
|
+
data: JSON.stringify(input.payload),
|
|
1631
|
+
/* v8 ignore next -- tests do not pass socketId through the publish binding */
|
|
1632
|
+
...typeof input.socketId === "undefined" ? {} : { socket_id: input.socketId }
|
|
1633
|
+
});
|
|
1634
|
+
const bodyMd5 = createHash("md5").update(body).digest("hex");
|
|
1635
|
+
url.searchParams.set("auth_key", connection.key);
|
|
1636
|
+
url.searchParams.set("auth_timestamp", String(Math.floor(Date.now() / 1e3)));
|
|
1637
|
+
url.searchParams.set("auth_version", "1.0");
|
|
1638
|
+
url.searchParams.set("body_md5", bodyMd5);
|
|
1639
|
+
url.searchParams.set(
|
|
1640
|
+
"auth_signature",
|
|
1641
|
+
createHmac("sha256", connection.secret).update(
|
|
1642
|
+
[
|
|
1643
|
+
"POST",
|
|
1644
|
+
url.pathname,
|
|
1645
|
+
[...url.searchParams.entries()].filter(([key]) => key !== "auth_signature").sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join("&")
|
|
1646
|
+
].join("\n")
|
|
1647
|
+
).digest("hex")
|
|
1648
|
+
);
|
|
1649
|
+
const controller = new AbortController();
|
|
1650
|
+
const timeout = setTimeout(() => controller.abort(), BROADCAST_PUBLISH_TIMEOUT_MS);
|
|
1651
|
+
let response;
|
|
1652
|
+
try {
|
|
1653
|
+
response = await fetch(url, {
|
|
1654
|
+
method: "POST",
|
|
1655
|
+
headers: {
|
|
1656
|
+
"content-type": "application/json"
|
|
1657
|
+
},
|
|
1658
|
+
body,
|
|
1659
|
+
signal: controller.signal
|
|
1445
1660
|
});
|
|
1661
|
+
} catch (error) {
|
|
1662
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
1663
|
+
throw new Error(`[@holo-js/core] Broadcast publish request timed out after ${BROADCAST_PUBLISH_TIMEOUT_MS}ms.`);
|
|
1664
|
+
}
|
|
1665
|
+
throw error;
|
|
1666
|
+
} finally {
|
|
1667
|
+
clearTimeout(timeout);
|
|
1668
|
+
}
|
|
1669
|
+
if (!response.ok) {
|
|
1670
|
+
throw new Error(`[@holo-js/core] Broadcast publish request failed with status ${response.status}.`);
|
|
1446
1671
|
}
|
|
1672
|
+
const result = await response.json();
|
|
1673
|
+
return {
|
|
1674
|
+
connection: input.connection,
|
|
1675
|
+
driver: connection.driver,
|
|
1676
|
+
queued: context.queued,
|
|
1677
|
+
publishedChannels: Array.isArray(result.deliveredChannels) ? Object.freeze(result.deliveredChannels.map((value) => String(value))) : Object.freeze([...input.channels])
|
|
1678
|
+
};
|
|
1679
|
+
};
|
|
1680
|
+
Object.defineProperty(publish, CORE_BROADCAST_PUBLISHER_MARKER, {
|
|
1681
|
+
value: true
|
|
1447
1682
|
});
|
|
1683
|
+
return publish;
|
|
1684
|
+
}
|
|
1685
|
+
function isCoreBroadcastPublisher(value) {
|
|
1686
|
+
return typeof value === "function" && CORE_BROADCAST_PUBLISHER_MARKER in value;
|
|
1448
1687
|
}
|
|
1449
1688
|
async function loadConfiguredSocialProviders(projectRootOrLoadedConfig, maybeLoadedConfig) {
|
|
1450
1689
|
const projectRoot = typeof projectRootOrLoadedConfig === "string" ? projectRootOrLoadedConfig : (
|
|
@@ -1471,97 +1710,6 @@ async function loadConfiguredSocialProviders(projectRootOrLoadedConfig, maybeLoa
|
|
|
1471
1710
|
}
|
|
1472
1711
|
return Object.freeze(providers);
|
|
1473
1712
|
}
|
|
1474
|
-
function normalizeDateValue(value) {
|
|
1475
|
-
return value instanceof Date ? value : new Date(String(value));
|
|
1476
|
-
}
|
|
1477
|
-
function normalizeJsonValue(value) {
|
|
1478
|
-
if (typeof value !== "string") {
|
|
1479
|
-
return value;
|
|
1480
|
-
}
|
|
1481
|
-
try {
|
|
1482
|
-
return JSON.parse(value);
|
|
1483
|
-
} catch {
|
|
1484
|
-
return value;
|
|
1485
|
-
}
|
|
1486
|
-
}
|
|
1487
|
-
function normalizeStoredUserId(value) {
|
|
1488
|
-
return typeof value === "number" ? value : String(value);
|
|
1489
|
-
}
|
|
1490
|
-
function normalizeAccessTokenRecord(row) {
|
|
1491
|
-
const abilities = normalizeJsonValue(row.abilities);
|
|
1492
|
-
return Object.freeze({
|
|
1493
|
-
id: String(row.id),
|
|
1494
|
-
provider: String(row.provider),
|
|
1495
|
-
userId: normalizeStoredUserId(row.user_id),
|
|
1496
|
-
name: String(row.name),
|
|
1497
|
-
abilities: Array.isArray(abilities) ? Object.freeze([...abilities]) : Object.freeze([]),
|
|
1498
|
-
tokenHash: String(row.token_hash),
|
|
1499
|
-
createdAt: normalizeDateValue(row.created_at),
|
|
1500
|
-
lastUsedAt: row.last_used_at ? normalizeDateValue(row.last_used_at) : void 0,
|
|
1501
|
-
expiresAt: row.expires_at ? normalizeDateValue(row.expires_at) : null
|
|
1502
|
-
});
|
|
1503
|
-
}
|
|
1504
|
-
function serializeAccessTokenRecord(record) {
|
|
1505
|
-
return {
|
|
1506
|
-
id: record.id,
|
|
1507
|
-
provider: record.provider,
|
|
1508
|
-
user_id: String(record.userId),
|
|
1509
|
-
name: record.name,
|
|
1510
|
-
abilities: JSON.stringify(record.abilities),
|
|
1511
|
-
token_hash: record.tokenHash,
|
|
1512
|
-
created_at: record.createdAt.toISOString(),
|
|
1513
|
-
last_used_at: record.lastUsedAt?.toISOString() ?? null,
|
|
1514
|
-
expires_at: record.expiresAt?.toISOString() ?? null,
|
|
1515
|
-
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1516
|
-
};
|
|
1517
|
-
}
|
|
1518
|
-
function normalizeEmailVerificationTokenRecord(row) {
|
|
1519
|
-
return Object.freeze({
|
|
1520
|
-
id: String(row.id),
|
|
1521
|
-
provider: String(row.provider),
|
|
1522
|
-
userId: normalizeStoredUserId(row.user_id),
|
|
1523
|
-
email: String(row.email),
|
|
1524
|
-
tokenHash: String(row.token_hash),
|
|
1525
|
-
createdAt: normalizeDateValue(row.created_at),
|
|
1526
|
-
expiresAt: normalizeDateValue(row.expires_at)
|
|
1527
|
-
});
|
|
1528
|
-
}
|
|
1529
|
-
function serializeEmailVerificationTokenRecord(record) {
|
|
1530
|
-
return {
|
|
1531
|
-
id: record.id,
|
|
1532
|
-
provider: record.provider,
|
|
1533
|
-
user_id: String(record.userId),
|
|
1534
|
-
email: record.email,
|
|
1535
|
-
token_hash: record.tokenHash,
|
|
1536
|
-
created_at: record.createdAt.toISOString(),
|
|
1537
|
-
expires_at: record.expiresAt.toISOString(),
|
|
1538
|
-
used_at: null,
|
|
1539
|
-
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1540
|
-
};
|
|
1541
|
-
}
|
|
1542
|
-
function normalizePasswordResetTokenRecord(row) {
|
|
1543
|
-
return Object.freeze({
|
|
1544
|
-
id: String(row.id),
|
|
1545
|
-
provider: typeof row.provider === "string" ? row.provider : "users",
|
|
1546
|
-
email: String(row.email),
|
|
1547
|
-
table: typeof row.__holo_table === "string" ? row.__holo_table : void 0,
|
|
1548
|
-
tokenHash: String(row.token_hash),
|
|
1549
|
-
createdAt: normalizeDateValue(row.created_at),
|
|
1550
|
-
expiresAt: normalizeDateValue(row.expires_at)
|
|
1551
|
-
});
|
|
1552
|
-
}
|
|
1553
|
-
function serializePasswordResetTokenRecord(record) {
|
|
1554
|
-
return {
|
|
1555
|
-
id: record.id,
|
|
1556
|
-
provider: record.provider,
|
|
1557
|
-
email: record.email,
|
|
1558
|
-
token_hash: record.tokenHash,
|
|
1559
|
-
created_at: record.createdAt.toISOString(),
|
|
1560
|
-
expires_at: record.expiresAt.toISOString(),
|
|
1561
|
-
used_at: null,
|
|
1562
|
-
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1563
|
-
};
|
|
1564
|
-
}
|
|
1565
1713
|
async function createCoreSocialBindings(projectRootOrLoadedConfig, loadedConfigOrSessionModule, maybeSessionModule) {
|
|
1566
1714
|
const projectRoot = typeof projectRootOrLoadedConfig === "string" ? projectRootOrLoadedConfig : (
|
|
1567
1715
|
/* turbopackIgnore: true */
|
|
@@ -1609,7 +1757,7 @@ async function createCoreSocialBindings(projectRootOrLoadedConfig, loadedConfigO
|
|
|
1609
1757
|
});
|
|
1610
1758
|
const identityStore = Object.freeze({
|
|
1611
1759
|
async findByProviderUserId(provider, providerUserId) {
|
|
1612
|
-
const row = await
|
|
1760
|
+
const row = await DB2.table("auth_identities").where("provider", provider).where("provider_user_id", providerUserId).first();
|
|
1613
1761
|
if (!row) {
|
|
1614
1762
|
return null;
|
|
1615
1763
|
}
|
|
@@ -1631,7 +1779,7 @@ async function createCoreSocialBindings(projectRootOrLoadedConfig, loadedConfigO
|
|
|
1631
1779
|
},
|
|
1632
1780
|
async save(record) {
|
|
1633
1781
|
const value = record;
|
|
1634
|
-
const existing = await
|
|
1782
|
+
const existing = await DB2.table("auth_identities").where("provider", value.provider).where("provider_user_id", value.providerUserId).first();
|
|
1635
1783
|
const payload = {
|
|
1636
1784
|
user_id: String(value.userId),
|
|
1637
1785
|
provider: value.provider,
|
|
@@ -1646,10 +1794,10 @@ async function createCoreSocialBindings(projectRootOrLoadedConfig, loadedConfigO
|
|
|
1646
1794
|
updated_at: value.updatedAt.toISOString()
|
|
1647
1795
|
};
|
|
1648
1796
|
if (existing && typeof existing.id !== "undefined") {
|
|
1649
|
-
await
|
|
1797
|
+
await DB2.table("auth_identities").where("id", existing.id).update(payload);
|
|
1650
1798
|
return;
|
|
1651
1799
|
}
|
|
1652
|
-
await
|
|
1800
|
+
await DB2.table("auth_identities").insert(payload);
|
|
1653
1801
|
}
|
|
1654
1802
|
});
|
|
1655
1803
|
return Object.freeze({
|
|
@@ -1683,7 +1831,7 @@ function createCoreHostedIdentityStore(namespace) {
|
|
|
1683
1831
|
};
|
|
1684
1832
|
const findStoredIdentity = async (provider, providerUserId, authProvider = "users") => {
|
|
1685
1833
|
const providerValue = toHostedIdentityProviderValue(namespace, provider);
|
|
1686
|
-
const row = await
|
|
1834
|
+
const row = await DB2.table("auth_identities").where("provider", providerValue).where("provider_user_id", providerUserId).first();
|
|
1687
1835
|
return row ? normalizeRow(row, { provider, providerUserId, authProvider }) : null;
|
|
1688
1836
|
};
|
|
1689
1837
|
const toPayload = (record) => {
|
|
@@ -1707,7 +1855,7 @@ function createCoreHostedIdentityStore(namespace) {
|
|
|
1707
1855
|
},
|
|
1708
1856
|
async findByUserId(provider, authProvider, userId) {
|
|
1709
1857
|
const providerValue = toHostedIdentityProviderValue(namespace, provider);
|
|
1710
|
-
const row = await
|
|
1858
|
+
const row = await DB2.table("auth_identities").where("provider", providerValue).where("auth_provider", authProvider).where("user_id", String(userId)).first();
|
|
1711
1859
|
if (!row) {
|
|
1712
1860
|
return null;
|
|
1713
1861
|
}
|
|
@@ -1719,7 +1867,7 @@ function createCoreHostedIdentityStore(namespace) {
|
|
|
1719
1867
|
},
|
|
1720
1868
|
async claim(record) {
|
|
1721
1869
|
const value = record;
|
|
1722
|
-
await
|
|
1870
|
+
await DB2.table("auth_identities").insertOrIgnore(toPayload(value));
|
|
1723
1871
|
const claimed = await findStoredIdentity(value.provider, value.providerUserId, value.authProvider);
|
|
1724
1872
|
if (!claimed) {
|
|
1725
1873
|
throw new Error("[@holo-js/core] Claimed hosted identity could not be read back from auth_identities.");
|
|
@@ -1729,13 +1877,13 @@ function createCoreHostedIdentityStore(namespace) {
|
|
|
1729
1877
|
async save(record) {
|
|
1730
1878
|
const value = record;
|
|
1731
1879
|
const providerValue = toHostedIdentityProviderValue(namespace, value.provider);
|
|
1732
|
-
const existing = await
|
|
1880
|
+
const existing = await DB2.table("auth_identities").where("provider", providerValue).where("provider_user_id", value.providerUserId).first();
|
|
1733
1881
|
const payload = toPayload(value);
|
|
1734
1882
|
if (existing && typeof existing.id !== "undefined") {
|
|
1735
|
-
await
|
|
1883
|
+
await DB2.table("auth_identities").where("id", existing.id).update(payload);
|
|
1736
1884
|
return;
|
|
1737
1885
|
}
|
|
1738
|
-
await
|
|
1886
|
+
await DB2.table("auth_identities").insert(payload);
|
|
1739
1887
|
}
|
|
1740
1888
|
});
|
|
1741
1889
|
}
|
|
@@ -1743,55 +1891,55 @@ function createCoreAuthStores(loadedConfig) {
|
|
|
1743
1891
|
return Object.freeze({
|
|
1744
1892
|
tokens: Object.freeze({
|
|
1745
1893
|
async create(record) {
|
|
1746
|
-
await
|
|
1894
|
+
await DB2.table("personal_access_tokens").insert(serializeAccessTokenRecord(record));
|
|
1747
1895
|
},
|
|
1748
1896
|
async findById(id) {
|
|
1749
|
-
const row = await
|
|
1897
|
+
const row = await DB2.table("personal_access_tokens").find(id);
|
|
1750
1898
|
return row ? normalizeAccessTokenRecord(row) : null;
|
|
1751
1899
|
},
|
|
1752
1900
|
async listByUserId(provider, userId) {
|
|
1753
|
-
const rows = await
|
|
1901
|
+
const rows = await DB2.table("personal_access_tokens").where("provider", provider).where("user_id", String(userId)).get();
|
|
1754
1902
|
return Object.freeze(rows.map((row) => normalizeAccessTokenRecord(row)));
|
|
1755
1903
|
},
|
|
1756
1904
|
async update(record) {
|
|
1757
1905
|
const payload = serializeAccessTokenRecord(record);
|
|
1758
|
-
await
|
|
1906
|
+
await DB2.table("personal_access_tokens").where("id", String(payload.id)).update(payload);
|
|
1759
1907
|
},
|
|
1760
1908
|
async delete(id) {
|
|
1761
|
-
await
|
|
1909
|
+
await DB2.table("personal_access_tokens").where("id", id).delete();
|
|
1762
1910
|
},
|
|
1763
1911
|
async deleteByUserId(provider, userId) {
|
|
1764
|
-
const result = await
|
|
1912
|
+
const result = await DB2.table("personal_access_tokens").where("provider", provider).where("user_id", String(userId)).delete();
|
|
1765
1913
|
return result.affectedRows ?? 0;
|
|
1766
1914
|
}
|
|
1767
1915
|
}),
|
|
1768
1916
|
emailVerificationTokens: Object.freeze({
|
|
1769
1917
|
async create(record) {
|
|
1770
|
-
await
|
|
1918
|
+
await DB2.table("email_verification_tokens").insert(serializeEmailVerificationTokenRecord(record));
|
|
1771
1919
|
},
|
|
1772
1920
|
async findById(id) {
|
|
1773
|
-
const row = await
|
|
1921
|
+
const row = await DB2.table("email_verification_tokens").where("id", id).whereNull("used_at").first();
|
|
1774
1922
|
return row ? normalizeEmailVerificationTokenRecord(row) : null;
|
|
1775
1923
|
},
|
|
1776
1924
|
async delete(id) {
|
|
1777
|
-
await
|
|
1925
|
+
await DB2.table("email_verification_tokens").where("id", id).delete();
|
|
1778
1926
|
},
|
|
1779
1927
|
async deleteByUserId(provider, userId) {
|
|
1780
|
-
const result = await
|
|
1928
|
+
const result = await DB2.table("email_verification_tokens").where("provider", provider).where("user_id", String(userId)).delete();
|
|
1781
1929
|
return result.affectedRows ?? 0;
|
|
1782
1930
|
}
|
|
1783
1931
|
}),
|
|
1784
1932
|
passwordResetTokens: Object.freeze({
|
|
1785
1933
|
async create(record) {
|
|
1786
1934
|
const value = record;
|
|
1787
|
-
await
|
|
1935
|
+
await DB2.table(value.table ?? "password_reset_tokens").insert(serializePasswordResetTokenRecord(value));
|
|
1788
1936
|
},
|
|
1789
1937
|
async findById(id) {
|
|
1790
1938
|
const tables = Array.from(new Set(
|
|
1791
1939
|
Object.values(loadedConfig.auth.passwords).map((config) => config.table)
|
|
1792
1940
|
));
|
|
1793
1941
|
for (const table of tables) {
|
|
1794
|
-
const row = await
|
|
1942
|
+
const row = await DB2.table(table).where("id", id).whereNull("used_at").first();
|
|
1795
1943
|
if (row) {
|
|
1796
1944
|
return normalizePasswordResetTokenRecord({
|
|
1797
1945
|
...row,
|
|
@@ -1803,7 +1951,7 @@ function createCoreAuthStores(loadedConfig) {
|
|
|
1803
1951
|
},
|
|
1804
1952
|
async findLatestByEmail(provider, email, options) {
|
|
1805
1953
|
const table = options?.table ?? "password_reset_tokens";
|
|
1806
|
-
const row = await
|
|
1954
|
+
const row = await DB2.table(table).where("provider", provider).where("email", email).latest("created_at").first();
|
|
1807
1955
|
if (!row) {
|
|
1808
1956
|
return null;
|
|
1809
1957
|
}
|
|
@@ -1814,20 +1962,20 @@ function createCoreAuthStores(loadedConfig) {
|
|
|
1814
1962
|
},
|
|
1815
1963
|
async delete(id, options) {
|
|
1816
1964
|
const table = options?.table ?? "password_reset_tokens";
|
|
1817
|
-
await
|
|
1965
|
+
await DB2.table(table).where("id", id).delete();
|
|
1818
1966
|
},
|
|
1819
1967
|
async deleteByEmail(provider, email, options) {
|
|
1820
1968
|
const table = options?.table ?? "password_reset_tokens";
|
|
1821
|
-
const result = await
|
|
1969
|
+
const result = await DB2.table(table).where("provider", provider).where("email", email).delete();
|
|
1822
1970
|
return result.affectedRows ?? 0;
|
|
1823
1971
|
}
|
|
1824
1972
|
})
|
|
1825
1973
|
});
|
|
1826
1974
|
}
|
|
1827
1975
|
async function resolveAuthProviderRuntime(projectRoot, loadedConfig, modelName) {
|
|
1828
|
-
const modelsRoot =
|
|
1976
|
+
const modelsRoot = resolve5(projectRoot, loadedConfig.app.paths.models);
|
|
1829
1977
|
for (const extension of [".ts", ".mts", ".js", ".mjs", ".cts", ".cjs"]) {
|
|
1830
|
-
const candidate =
|
|
1978
|
+
const candidate = resolve5(modelsRoot, `${modelName}${extension}`);
|
|
1831
1979
|
try {
|
|
1832
1980
|
const moduleValue = await importRuntimeModule(projectRoot, candidate);
|
|
1833
1981
|
if ("default" in moduleValue) {
|
|
@@ -2053,7 +2201,7 @@ async function registerProjectQueueJobs(projectRoot, registry, queueModule) {
|
|
|
2053
2201
|
if (existing && !existing.sourcePath) {
|
|
2054
2202
|
continue;
|
|
2055
2203
|
}
|
|
2056
|
-
const moduleValue = await importRuntimeModule(projectRoot,
|
|
2204
|
+
const moduleValue = await importRuntimeModule(projectRoot, resolve5(projectRoot, entry.sourcePath));
|
|
2057
2205
|
const job = resolveQueueJobExport(queueModule, moduleValue);
|
|
2058
2206
|
if (!job) {
|
|
2059
2207
|
throw new Error(`Discovered job "${entry.sourcePath}" does not export a Holo job.`);
|
|
@@ -2118,7 +2266,7 @@ async function registerProjectAuthorizationDefinitions(projectRoot, registry, au
|
|
|
2118
2266
|
previousPolicies.set(entry.name, existing);
|
|
2119
2267
|
authorizationModule.authorizationInternals.unregisterPolicyDefinition(entry.name);
|
|
2120
2268
|
}
|
|
2121
|
-
const moduleValue = await importRuntimeModule(projectRoot,
|
|
2269
|
+
const moduleValue = await importRuntimeModule(projectRoot, resolve5(projectRoot, entry.sourcePath));
|
|
2122
2270
|
const policy = resolveAuthorizationDefinitionExport(
|
|
2123
2271
|
moduleValue,
|
|
2124
2272
|
entry.exportName,
|
|
@@ -2146,7 +2294,7 @@ async function registerProjectAuthorizationDefinitions(projectRoot, registry, au
|
|
|
2146
2294
|
previousAbilities.set(entry.name, existing);
|
|
2147
2295
|
authorizationModule.authorizationInternals.unregisterAbilityDefinition(entry.name);
|
|
2148
2296
|
}
|
|
2149
|
-
const moduleValue = await importRuntimeModule(projectRoot,
|
|
2297
|
+
const moduleValue = await importRuntimeModule(projectRoot, resolve5(projectRoot, entry.sourcePath));
|
|
2150
2298
|
const ability = resolveAuthorizationDefinitionExport(
|
|
2151
2299
|
moduleValue,
|
|
2152
2300
|
entry.exportName,
|
|
@@ -2214,7 +2362,7 @@ async function registerProjectEventsAndListeners(projectRoot, registry, eventsMo
|
|
|
2214
2362
|
if (existing && !existing.sourcePath) {
|
|
2215
2363
|
continue;
|
|
2216
2364
|
}
|
|
2217
|
-
const moduleValue = await importRuntimeModule(projectRoot,
|
|
2365
|
+
const moduleValue = await importRuntimeModule(projectRoot, resolve5(projectRoot, entry.sourcePath));
|
|
2218
2366
|
const event = resolveEventExport(moduleValue);
|
|
2219
2367
|
if (!event || !eventsModule.isEventDefinition(event)) {
|
|
2220
2368
|
throw new Error(`Discovered event "${entry.sourcePath}" does not export a Holo event.`);
|
|
@@ -2231,7 +2379,7 @@ async function registerProjectEventsAndListeners(projectRoot, registry, eventsMo
|
|
|
2231
2379
|
if (existing && !existing.sourcePath) {
|
|
2232
2380
|
continue;
|
|
2233
2381
|
}
|
|
2234
|
-
const moduleValue = await importRuntimeModule(projectRoot,
|
|
2382
|
+
const moduleValue = await importRuntimeModule(projectRoot, resolve5(projectRoot, entry.sourcePath));
|
|
2235
2383
|
const listener = resolveListenerExport(eventsModule, moduleValue);
|
|
2236
2384
|
if (!listener) {
|
|
2237
2385
|
throw new Error(`Discovered listener "${entry.sourcePath}" does not export a Holo listener.`);
|
|
@@ -2277,13 +2425,27 @@ function unregisterProjectEventsAndListeners(eventsModule, eventNames, listenerI
|
|
|
2277
2425
|
}
|
|
2278
2426
|
}
|
|
2279
2427
|
async function reconfigureOptionalHoloSubsystems(projectRoot, loadedConfig, options = {}) {
|
|
2428
|
+
const pluginDefinitions = await loadConfiguredHoloPluginDefinitions(projectRoot, resolveLoadedPluginNames(loadedConfig));
|
|
2280
2429
|
const cacheConfigured = hasLoadedConfigFile(loadedConfig, "cache");
|
|
2281
2430
|
const cacheModule = await loadCacheModule(cacheConfigured, projectRoot);
|
|
2282
|
-
|
|
2431
|
+
const cacheConfig = loadedConfig.cache;
|
|
2432
|
+
if (cacheModule && cacheConfig) {
|
|
2433
|
+
await loadConfiguredCacheDrivers(projectRoot, cacheConfig);
|
|
2434
|
+
const configuredPluginDrivers = Object.entries(cacheConfig.drivers).flatMap(([name, driver]) => {
|
|
2435
|
+
if (driver.driver === "memory" || driver.driver === "file" || driver.driver === "redis" || driver.driver === "database") return [];
|
|
2436
|
+
return [{ ...driver, name, driver: driver.driver }];
|
|
2437
|
+
});
|
|
2438
|
+
const loadedPluginDrivers = await cacheModule.loadConfiguredCachePluginDriverContracts(
|
|
2439
|
+
projectRoot,
|
|
2440
|
+
resolveLoadedPluginNames(loadedConfig),
|
|
2441
|
+
configuredPluginDrivers
|
|
2442
|
+
);
|
|
2443
|
+
const cachePluginDrivers = new Map(loadedPluginDrivers.map((driver) => [driver.name, driver]));
|
|
2283
2444
|
cacheModule.configureCacheRuntime({
|
|
2284
|
-
config:
|
|
2445
|
+
config: cacheConfig,
|
|
2285
2446
|
databaseConfig: loadedConfig.database,
|
|
2286
|
-
redisConfig: loadedConfig.redis
|
|
2447
|
+
redisConfig: loadedConfig.redis,
|
|
2448
|
+
...cachePluginDrivers.size > 0 ? { drivers: cachePluginDrivers } : {}
|
|
2287
2449
|
});
|
|
2288
2450
|
}
|
|
2289
2451
|
const queueConfigured = hasLoadedConfigFile(loadedConfig, "queue");
|
|
@@ -2291,14 +2453,30 @@ async function reconfigureOptionalHoloSubsystems(projectRoot, loadedConfig, opti
|
|
|
2291
2453
|
if (queueModule) {
|
|
2292
2454
|
const queueUsesExplicitDatabaseFeatures = queueConfigured && (queueConfigUsesDatabaseDriver(loadedConfig) || queueConfigUsesDatabaseBackedFailedStore(loadedConfig));
|
|
2293
2455
|
const queueUsesImplicitDefaultFailedStore = !queueConfigured && queueConfigUsesDatabaseBackedFailedStore(loadedConfig);
|
|
2294
|
-
const queueDbModule = queueUsesExplicitDatabaseFeatures || queueUsesImplicitDefaultFailedStore ? await loadQueueDbModule() : void 0;
|
|
2456
|
+
const queueDbModule = queueUsesExplicitDatabaseFeatures || queueUsesImplicitDefaultFailedStore ? await loadQueueDbModule(projectRoot) : void 0;
|
|
2457
|
+
const queueUsesRedis = queueConfigUsesRedisDriver(loadedConfig);
|
|
2458
|
+
const queueRedisModule = queueUsesRedis ? await loadQueueRedisModule(projectRoot) : void 0;
|
|
2295
2459
|
if (queueUsesExplicitDatabaseFeatures && !queueDbModule) {
|
|
2296
2460
|
throw new Error("[@holo-js/core] Database-backed queue features require @holo-js/queue-db to be installed.");
|
|
2297
2461
|
}
|
|
2462
|
+
if (queueUsesRedis && !queueRedisModule) {
|
|
2463
|
+
throw new Error("[@holo-js/core] Redis-backed queue connections require @holo-js/queue-redis to be installed.");
|
|
2464
|
+
}
|
|
2465
|
+
const queuePluginDriverFactories = await queueModule.loadQueuePluginDriverFactories(
|
|
2466
|
+
projectRoot,
|
|
2467
|
+
resolveLoadedPluginNames(loadedConfig)
|
|
2468
|
+
);
|
|
2469
|
+
const queueDbRuntimeOptions = queueDbModule?.createQueueDbRuntimeOptions() ?? {};
|
|
2470
|
+
const queueDriverFactories = mergeQueueRuntimeDriverFactories(
|
|
2471
|
+
queuePluginDriverFactories,
|
|
2472
|
+
queueDbRuntimeOptions.driverFactories,
|
|
2473
|
+
queueRedisModule ? [queueRedisModule.redisQueueDriverFactory] : void 0
|
|
2474
|
+
);
|
|
2298
2475
|
queueModule.configureQueueRuntime({
|
|
2299
2476
|
config: loadedConfig.queue,
|
|
2300
2477
|
redisConfig: loadedConfig.redis,
|
|
2301
|
-
...
|
|
2478
|
+
...queueDbRuntimeOptions,
|
|
2479
|
+
...queueDriverFactories.length > 0 ? { driverFactories: queueDriverFactories } : {}
|
|
2302
2480
|
});
|
|
2303
2481
|
}
|
|
2304
2482
|
const storageConfigured = hasLoadedConfigFile(loadedConfig, "storage");
|
|
@@ -2306,7 +2484,7 @@ async function reconfigureOptionalHoloSubsystems(projectRoot, loadedConfig, opti
|
|
|
2306
2484
|
if (!storageInstalled && storageConfigured) {
|
|
2307
2485
|
throw new Error("[@holo-js/core] Storage support requires @holo-js/storage to be installed.");
|
|
2308
2486
|
}
|
|
2309
|
-
if (storageInstalled) {
|
|
2487
|
+
if (storageInstalled && loadedConfig.storage) {
|
|
2310
2488
|
await configurePlainNodeStorageRuntime(projectRoot, loadedConfig);
|
|
2311
2489
|
}
|
|
2312
2490
|
const mailConfigured = hasLoadedConfigFile(loadedConfig, "mail");
|
|
@@ -2316,7 +2494,9 @@ async function reconfigureOptionalHoloSubsystems(projectRoot, loadedConfig, opti
|
|
|
2316
2494
|
mailModule.configureMailRuntime({
|
|
2317
2495
|
...existingMailBindings,
|
|
2318
2496
|
config: loadedConfig.mail,
|
|
2319
|
-
|
|
2497
|
+
projectRoot,
|
|
2498
|
+
plugins: loadedConfig.app.plugins,
|
|
2499
|
+
...options.renderView ?? getHoloRenderingRuntime().renderView ? { renderView: options.renderView ?? getHoloRenderingRuntime().renderView } : {}
|
|
2320
2500
|
});
|
|
2321
2501
|
}
|
|
2322
2502
|
const broadcastConfigured = hasLoadedConfigFile(loadedConfig, "broadcast");
|
|
@@ -2326,6 +2506,8 @@ async function reconfigureOptionalHoloSubsystems(projectRoot, loadedConfig, opti
|
|
|
2326
2506
|
broadcastModule.configureBroadcastRuntime({
|
|
2327
2507
|
...existingBroadcastBindings,
|
|
2328
2508
|
config: loadedConfig.broadcast,
|
|
2509
|
+
projectRoot,
|
|
2510
|
+
plugins: loadedConfig.app.plugins,
|
|
2329
2511
|
...!existingBroadcastBindings.publish || isCoreBroadcastPublisher(existingBroadcastBindings.publish) ? {
|
|
2330
2512
|
publish: createCoreBroadcastPublisher(loadedConfig.broadcast)
|
|
2331
2513
|
} : {}
|
|
@@ -2338,6 +2520,16 @@ async function reconfigureOptionalHoloSubsystems(projectRoot, loadedConfig, opti
|
|
|
2338
2520
|
notificationsModule.configureNotificationsRuntime({
|
|
2339
2521
|
...existingNotificationsBindings,
|
|
2340
2522
|
config: loadedConfig.notifications,
|
|
2523
|
+
deferAfterCommit(callback) {
|
|
2524
|
+
const connection = connectionAsyncContext.getActive()?.connection;
|
|
2525
|
+
if (!connection || connection.getScope().kind === "root") {
|
|
2526
|
+
return false;
|
|
2527
|
+
}
|
|
2528
|
+
connection.afterCommit(callback);
|
|
2529
|
+
return true;
|
|
2530
|
+
},
|
|
2531
|
+
projectRoot,
|
|
2532
|
+
plugins: loadedConfig.app.plugins,
|
|
2341
2533
|
store: existingNotificationsBindings.store ?? createCoreNotificationStore(loadedConfig),
|
|
2342
2534
|
...!existingNotificationsBindings.mailer && mailModule ? { mailer: createCoreNotificationMailSender(mailModule) } : {},
|
|
2343
2535
|
...!existingNotificationsBindings.broadcaster && broadcastModule ? { broadcaster: createCoreNotificationBroadcaster(broadcastModule) } : {}
|
|
@@ -2509,6 +2701,9 @@ async function reconfigureOptionalHoloSubsystems(projectRoot, loadedConfig, opti
|
|
|
2509
2701
|
} else if (authorizationModule) {
|
|
2510
2702
|
authorizationModule.authorizationInternals.resetAuthorizationAuthIntegration();
|
|
2511
2703
|
}
|
|
2704
|
+
for (const bootModule of await loadConfiguredHoloPluginBootModules(projectRoot, pluginDefinitions)) {
|
|
2705
|
+
await bootConfiguredHoloPluginModule(projectRoot, loadedConfig, bootModule);
|
|
2706
|
+
}
|
|
2512
2707
|
return Object.freeze({
|
|
2513
2708
|
/* v8 ignore next -- only toggles shape when queue support is absent */
|
|
2514
2709
|
...queueModule ? { queueModule } : {},
|
|
@@ -2518,6 +2713,7 @@ async function reconfigureOptionalHoloSubsystems(projectRoot, loadedConfig, opti
|
|
|
2518
2713
|
});
|
|
2519
2714
|
}
|
|
2520
2715
|
async function resetOptionalHoloSubsystems() {
|
|
2716
|
+
resetBootedHoloPluginModules();
|
|
2521
2717
|
const projectRoot = getRuntimeState().current?.projectRoot ?? getRuntimeState().pendingProjectRoot;
|
|
2522
2718
|
await resetOptionalStorageRuntime();
|
|
2523
2719
|
const cacheModule = await loadCacheModule(false, projectRoot);
|
|
@@ -2527,7 +2723,10 @@ async function resetOptionalHoloSubsystems() {
|
|
|
2527
2723
|
resetCacheRuntimeGlobalsFallback();
|
|
2528
2724
|
}
|
|
2529
2725
|
const queueModule = await loadQueueModule();
|
|
2530
|
-
|
|
2726
|
+
if (queueModule) {
|
|
2727
|
+
await queueModule.shutdownQueueRuntime();
|
|
2728
|
+
queueModule.resetQueueRuntime?.();
|
|
2729
|
+
}
|
|
2531
2730
|
const mailModule = await loadMailModule();
|
|
2532
2731
|
mailModule?.resetMailRuntime();
|
|
2533
2732
|
const notificationsModule = await loadNotificationsModule();
|
|
@@ -2567,15 +2766,32 @@ async function resetOptionalHoloSubsystems() {
|
|
|
2567
2766
|
securityModule?.resetSecurityRuntime();
|
|
2568
2767
|
}
|
|
2569
2768
|
async function createHolo(projectRoot, options = {}) {
|
|
2769
|
+
await loadInstalledFeatureConfigContributions(projectRoot);
|
|
2570
2770
|
const loadedConfig = await loadConfigDirectory(projectRoot, {
|
|
2571
2771
|
envName: options.envName,
|
|
2572
2772
|
preferCache: options.preferCache,
|
|
2573
2773
|
processEnv: options.processEnv
|
|
2574
2774
|
});
|
|
2775
|
+
const fallbackQueueConfig = Object.freeze({
|
|
2776
|
+
default: "sync",
|
|
2777
|
+
failed: Object.freeze({
|
|
2778
|
+
driver: "database",
|
|
2779
|
+
connection: "default",
|
|
2780
|
+
table: "failed_jobs"
|
|
2781
|
+
}),
|
|
2782
|
+
connections: Object.freeze({
|
|
2783
|
+
sync: Object.freeze({
|
|
2784
|
+
driver: "sync",
|
|
2785
|
+
queue: "default"
|
|
2786
|
+
})
|
|
2787
|
+
})
|
|
2788
|
+
});
|
|
2789
|
+
const queueConfig = loadedConfig.queue ?? fallbackQueueConfig;
|
|
2575
2790
|
const runtimeConfig = {
|
|
2576
2791
|
db: loadedConfig.database,
|
|
2577
|
-
queue:
|
|
2792
|
+
queue: queueConfig
|
|
2578
2793
|
};
|
|
2794
|
+
const databaseDriverFactories = await loadConfiguredDatabaseDrivers(projectRoot, loadedConfig);
|
|
2579
2795
|
const manager = resolveRuntimeConnectionManagerOptions(runtimeConfig);
|
|
2580
2796
|
const registry = await loadGeneratedProjectRegistry(projectRoot);
|
|
2581
2797
|
const accessors = createConfigAccessors(loadedConfig.all);
|
|
@@ -2591,11 +2807,105 @@ async function createHolo(projectRoot, options = {}) {
|
|
|
2591
2807
|
let activeAuthRuntime;
|
|
2592
2808
|
let activeAuthContext;
|
|
2593
2809
|
let previousOptionalSubsystemBindings;
|
|
2594
|
-
const previousRenderView = options.renderView ?
|
|
2810
|
+
const previousRenderView = options.renderView ? getHoloRenderingRuntime().renderView : void 0;
|
|
2595
2811
|
const fallbackQueueRuntime = Object.freeze({
|
|
2596
|
-
config:
|
|
2812
|
+
config: queueConfig,
|
|
2597
2813
|
drivers: /* @__PURE__ */ new Map()
|
|
2598
2814
|
});
|
|
2815
|
+
const unregisterRuntimeContributions = () => {
|
|
2816
|
+
unregisterProjectEventsAndListeners(activeEventsModule, runtimeOwnedEventNames, runtimeOwnedListenerIds);
|
|
2817
|
+
runtimeOwnedEventNames.splice(0);
|
|
2818
|
+
runtimeOwnedListenerIds.splice(0);
|
|
2819
|
+
unregisterProjectAuthorizationDefinitions(activeAuthorizationModule, runtimeOwnedAuthorizationPolicyNames, runtimeOwnedAuthorizationAbilityNames);
|
|
2820
|
+
runtimeOwnedAuthorizationPolicyNames.splice(0);
|
|
2821
|
+
runtimeOwnedAuthorizationAbilityNames.splice(0);
|
|
2822
|
+
unregisterProjectQueueJobs(activeQueueModule, runtimeOwnedQueueJobNames);
|
|
2823
|
+
runtimeOwnedQueueJobNames.splice(0);
|
|
2824
|
+
activeAuthorizationModule = void 0;
|
|
2825
|
+
activeEventsModule = void 0;
|
|
2826
|
+
activeQueueModule = void 0;
|
|
2827
|
+
activeSessionRuntime = void 0;
|
|
2828
|
+
activeAuthRuntime = void 0;
|
|
2829
|
+
activeAuthContext = void 0;
|
|
2830
|
+
};
|
|
2831
|
+
const initializeRuntimeServices = async () => {
|
|
2832
|
+
if (!shouldBootRuntimeServices(options.processEnv)) return;
|
|
2833
|
+
const optionalSubsystems = await reconfigureOptionalHoloSubsystems(projectRoot, loadedConfig, {
|
|
2834
|
+
renderView: options.renderView,
|
|
2835
|
+
authRequest: options.authRequest,
|
|
2836
|
+
authorizationError: options.authorizationError
|
|
2837
|
+
});
|
|
2838
|
+
activeQueueModule = optionalSubsystems.queueModule;
|
|
2839
|
+
activeSessionRuntime = optionalSubsystems.session;
|
|
2840
|
+
activeAuthRuntime = optionalSubsystems.auth;
|
|
2841
|
+
activeAuthContext = optionalSubsystems.authContext;
|
|
2842
|
+
const optionalEventsModule = activeQueueModule ? await loadEventsModule() : void 0;
|
|
2843
|
+
if (activeQueueModule && optionalEventsModule) {
|
|
2844
|
+
await optionalEventsModule.ensureEventsQueueJobRegisteredAsync?.();
|
|
2845
|
+
}
|
|
2846
|
+
if (registryHasEvents(registry)) {
|
|
2847
|
+
const eventsModule = await loadEventsModule(true);
|
|
2848
|
+
if (!eventsModule) throw new Error("[@holo-js/core] Events support requires @holo-js/events to be installed.");
|
|
2849
|
+
activeEventsModule = eventsModule;
|
|
2850
|
+
const eventRegistration = await registerProjectEventsAndListeners(projectRoot, registry, eventsModule, activeQueueModule);
|
|
2851
|
+
runtimeOwnedEventNames.push(...eventRegistration.eventNames);
|
|
2852
|
+
runtimeOwnedListenerIds.push(...eventRegistration.listenerIds);
|
|
2853
|
+
}
|
|
2854
|
+
activeAuthorizationModule = await loadAuthorizationModule();
|
|
2855
|
+
const authorizationRegistration = await registerProjectAuthorizationDefinitions(projectRoot, registry, activeAuthorizationModule);
|
|
2856
|
+
runtimeOwnedAuthorizationPolicyNames.push(...authorizationRegistration.policyNames);
|
|
2857
|
+
runtimeOwnedAuthorizationAbilityNames.push(...authorizationRegistration.abilityNames);
|
|
2858
|
+
if (options.registerProjectQueueJobs !== false && registryHasJobs(registry)) {
|
|
2859
|
+
if (!activeQueueModule) throw new Error("[@holo-js/core] Project jobs require @holo-js/queue to be installed.");
|
|
2860
|
+
runtimeOwnedQueueJobNames.push(...await registerProjectQueueJobs(projectRoot, registry, activeQueueModule));
|
|
2861
|
+
}
|
|
2862
|
+
};
|
|
2863
|
+
const runtimeLifecycle = createRuntimeLifecycle([
|
|
2864
|
+
{
|
|
2865
|
+
name: "configuration",
|
|
2866
|
+
async initialize() {
|
|
2867
|
+
configureConfigRuntime(loadedConfig.all);
|
|
2868
|
+
await preloadGeneratedSchemaModule(projectRoot, registry);
|
|
2869
|
+
await preloadDiscoveredModelModules(projectRoot, registry);
|
|
2870
|
+
previousOptionalSubsystemBindings = snapshotOptionalSubsystemRuntimeBindings();
|
|
2871
|
+
if (options.renderView) configureHoloRenderingRuntime({ renderView: options.renderView });
|
|
2872
|
+
},
|
|
2873
|
+
dispose() {
|
|
2874
|
+
if (options.renderView) restoreHoloRenderingRuntime(previousRenderView);
|
|
2875
|
+
resetConfigRuntime();
|
|
2876
|
+
}
|
|
2877
|
+
},
|
|
2878
|
+
{
|
|
2879
|
+
name: "database",
|
|
2880
|
+
dependsOn: ["configuration"],
|
|
2881
|
+
async initialize() {
|
|
2882
|
+
for (const factory of databaseDriverFactories) registerDatabaseDriverFactory(factory);
|
|
2883
|
+
configureDB(manager);
|
|
2884
|
+
if (shouldBootRuntimeServices(options.processEnv)) await manager.initializeAll();
|
|
2885
|
+
},
|
|
2886
|
+
async dispose() {
|
|
2887
|
+
try {
|
|
2888
|
+
await manager.disconnectAll();
|
|
2889
|
+
} finally {
|
|
2890
|
+
resetDB();
|
|
2891
|
+
for (const factory of [...databaseDriverFactories].reverse()) unregisterDatabaseDriverFactory(factory);
|
|
2892
|
+
}
|
|
2893
|
+
}
|
|
2894
|
+
},
|
|
2895
|
+
{
|
|
2896
|
+
name: "optional-subsystems",
|
|
2897
|
+
dependsOn: ["database"],
|
|
2898
|
+
initialize: initializeRuntimeServices,
|
|
2899
|
+
async dispose() {
|
|
2900
|
+
unregisterRuntimeContributions();
|
|
2901
|
+
try {
|
|
2902
|
+
await resetOptionalHoloSubsystems();
|
|
2903
|
+
} finally {
|
|
2904
|
+
if (previousOptionalSubsystemBindings) restoreOptionalSubsystemRuntimeBindings(previousOptionalSubsystemBindings);
|
|
2905
|
+
}
|
|
2906
|
+
}
|
|
2907
|
+
}
|
|
2908
|
+
]);
|
|
2599
2909
|
const runtime = {
|
|
2600
2910
|
projectRoot,
|
|
2601
2911
|
loadedConfig,
|
|
@@ -2617,150 +2927,31 @@ async function createHolo(projectRoot, options = {}) {
|
|
|
2617
2927
|
setAuthRequestAccessors(authRequest) {
|
|
2618
2928
|
activeAuthContext?.setRequestAccessors?.(authRequest);
|
|
2619
2929
|
},
|
|
2620
|
-
|
|
2930
|
+
runWithAuthRequestAccessors(authRequest, callback) {
|
|
2621
2931
|
const runner = activeAuthContext?.runWithRequestAccessors;
|
|
2622
|
-
return runner ?
|
|
2932
|
+
return runner ? runner(authRequest, callback) : callback();
|
|
2623
2933
|
},
|
|
2624
2934
|
async initialize() {
|
|
2625
|
-
if (runtime.initialized)
|
|
2626
|
-
|
|
2627
|
-
}
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
}
|
|
2631
|
-
configureConfigRuntime(loadedConfig.all);
|
|
2632
|
-
configureDB(manager);
|
|
2633
|
-
await preloadGeneratedSchemaModule(projectRoot, registry);
|
|
2634
|
-
await preloadDiscoveredModelModules(projectRoot, registry);
|
|
2635
|
-
previousOptionalSubsystemBindings = snapshotOptionalSubsystemRuntimeBindings();
|
|
2636
|
-
if (options.renderView) {
|
|
2637
|
-
configureHoloRenderingRuntime({
|
|
2638
|
-
renderView: options.renderView
|
|
2639
|
-
});
|
|
2640
|
-
}
|
|
2641
|
-
try {
|
|
2642
|
-
if (shouldBootRuntimeServices(options.processEnv)) {
|
|
2643
|
-
await manager.initializeAll();
|
|
2644
|
-
const optionalSubsystems = await reconfigureOptionalHoloSubsystems(projectRoot, loadedConfig, {
|
|
2645
|
-
renderView: options.renderView,
|
|
2646
|
-
authRequest: options.authRequest,
|
|
2647
|
-
authorizationError: options.authorizationError
|
|
2648
|
-
});
|
|
2649
|
-
activeQueueModule = optionalSubsystems.queueModule;
|
|
2650
|
-
activeSessionRuntime = optionalSubsystems.session;
|
|
2651
|
-
activeAuthRuntime = optionalSubsystems.auth;
|
|
2652
|
-
activeAuthContext = optionalSubsystems.authContext;
|
|
2653
|
-
const optionalEventsModule = activeQueueModule ? await loadEventsModule() : void 0;
|
|
2654
|
-
if (activeQueueModule && optionalEventsModule) {
|
|
2655
|
-
await optionalEventsModule.ensureEventsQueueJobRegisteredAsync?.();
|
|
2656
|
-
}
|
|
2657
|
-
if (registryHasEvents(registry)) {
|
|
2658
|
-
const eventsModule = await loadEventsModule(true);
|
|
2659
|
-
if (!eventsModule) {
|
|
2660
|
-
throw new Error("[@holo-js/core] Events support requires @holo-js/events to be installed.");
|
|
2661
|
-
}
|
|
2662
|
-
activeEventsModule = eventsModule;
|
|
2663
|
-
const eventRegistration = await registerProjectEventsAndListeners(
|
|
2664
|
-
projectRoot,
|
|
2665
|
-
registry,
|
|
2666
|
-
eventsModule,
|
|
2667
|
-
activeQueueModule
|
|
2668
|
-
);
|
|
2669
|
-
runtimeOwnedEventNames.splice(0, runtimeOwnedEventNames.length, ...eventRegistration.eventNames);
|
|
2670
|
-
runtimeOwnedListenerIds.splice(0, runtimeOwnedListenerIds.length, ...eventRegistration.listenerIds);
|
|
2671
|
-
}
|
|
2672
|
-
activeAuthorizationModule = await loadAuthorizationModule();
|
|
2673
|
-
const authorizationRegistration = await registerProjectAuthorizationDefinitions(
|
|
2674
|
-
projectRoot,
|
|
2675
|
-
registry,
|
|
2676
|
-
activeAuthorizationModule
|
|
2677
|
-
);
|
|
2678
|
-
runtimeOwnedAuthorizationPolicyNames.splice(0, runtimeOwnedAuthorizationPolicyNames.length, ...authorizationRegistration.policyNames);
|
|
2679
|
-
runtimeOwnedAuthorizationAbilityNames.splice(0, runtimeOwnedAuthorizationAbilityNames.length, ...authorizationRegistration.abilityNames);
|
|
2680
|
-
if (options.registerProjectQueueJobs !== false && registryHasJobs(registry)) {
|
|
2681
|
-
if (!activeQueueModule) {
|
|
2682
|
-
throw new Error("[@holo-js/core] Project jobs require @holo-js/queue to be installed.");
|
|
2683
|
-
}
|
|
2684
|
-
runtimeOwnedQueueJobNames.splice(0, runtimeOwnedQueueJobNames.length);
|
|
2685
|
-
runtimeOwnedQueueJobNames.push(...await registerProjectQueueJobs(projectRoot, registry, activeQueueModule));
|
|
2686
|
-
}
|
|
2687
|
-
}
|
|
2688
|
-
runtime.initialized = true;
|
|
2689
|
-
getRuntimeState().current = runtime;
|
|
2690
|
-
} catch (error) {
|
|
2691
|
-
unregisterProjectEventsAndListeners(activeEventsModule, runtimeOwnedEventNames, runtimeOwnedListenerIds);
|
|
2692
|
-
runtimeOwnedEventNames.splice(0, runtimeOwnedEventNames.length);
|
|
2693
|
-
runtimeOwnedListenerIds.splice(0, runtimeOwnedListenerIds.length);
|
|
2694
|
-
unregisterProjectAuthorizationDefinitions(activeAuthorizationModule, runtimeOwnedAuthorizationPolicyNames, runtimeOwnedAuthorizationAbilityNames);
|
|
2695
|
-
runtimeOwnedAuthorizationPolicyNames.splice(0, runtimeOwnedAuthorizationPolicyNames.length);
|
|
2696
|
-
runtimeOwnedAuthorizationAbilityNames.splice(0, runtimeOwnedAuthorizationAbilityNames.length);
|
|
2697
|
-
unregisterProjectQueueJobs(activeQueueModule, runtimeOwnedQueueJobNames);
|
|
2698
|
-
runtimeOwnedQueueJobNames.splice(0, runtimeOwnedQueueJobNames.length);
|
|
2699
|
-
activeAuthorizationModule = void 0;
|
|
2700
|
-
activeEventsModule = void 0;
|
|
2701
|
-
activeQueueModule = void 0;
|
|
2702
|
-
activeSessionRuntime = void 0;
|
|
2703
|
-
activeAuthRuntime = void 0;
|
|
2704
|
-
activeAuthContext = void 0;
|
|
2705
|
-
await manager.disconnectAll().catch(() => {
|
|
2706
|
-
});
|
|
2707
|
-
resetDB();
|
|
2708
|
-
await resetOptionalHoloSubsystems();
|
|
2709
|
-
if (previousOptionalSubsystemBindings) {
|
|
2710
|
-
restoreOptionalSubsystemRuntimeBindings(previousOptionalSubsystemBindings);
|
|
2711
|
-
}
|
|
2712
|
-
if (options.renderView) {
|
|
2713
|
-
restoreHoloRenderingRuntime(previousRenderView);
|
|
2714
|
-
}
|
|
2715
|
-
resetConfigRuntime();
|
|
2716
|
-
getRuntimeState().current = void 0;
|
|
2717
|
-
throw error;
|
|
2718
|
-
}
|
|
2935
|
+
if (runtime.initialized) throw new Error("Holo runtime is already initialized.");
|
|
2936
|
+
if (getRuntimeState().current) throw new Error("A Holo runtime is already initialized for this process.");
|
|
2937
|
+
await runtimeLifecycle.initialize({ projectRoot });
|
|
2938
|
+
runtime.initialized = true;
|
|
2939
|
+
getRuntimeState().current = runtime;
|
|
2719
2940
|
},
|
|
2720
2941
|
async shutdown() {
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
}
|
|
2725
|
-
} finally {
|
|
2726
|
-
runtime.initialized = false;
|
|
2727
|
-
if (getRuntimeState().current === runtime) {
|
|
2728
|
-
getRuntimeState().current = void 0;
|
|
2729
|
-
}
|
|
2730
|
-
unregisterProjectEventsAndListeners(activeEventsModule, runtimeOwnedEventNames, runtimeOwnedListenerIds);
|
|
2731
|
-
runtimeOwnedEventNames.splice(0, runtimeOwnedEventNames.length);
|
|
2732
|
-
runtimeOwnedListenerIds.splice(0, runtimeOwnedListenerIds.length);
|
|
2733
|
-
unregisterProjectAuthorizationDefinitions(activeAuthorizationModule, runtimeOwnedAuthorizationPolicyNames, runtimeOwnedAuthorizationAbilityNames);
|
|
2734
|
-
runtimeOwnedAuthorizationPolicyNames.splice(0, runtimeOwnedAuthorizationPolicyNames.length);
|
|
2735
|
-
runtimeOwnedAuthorizationAbilityNames.splice(0, runtimeOwnedAuthorizationAbilityNames.length);
|
|
2736
|
-
unregisterProjectQueueJobs(activeQueueModule, runtimeOwnedQueueJobNames);
|
|
2737
|
-
runtimeOwnedQueueJobNames.splice(0, runtimeOwnedQueueJobNames.length);
|
|
2738
|
-
activeAuthorizationModule = void 0;
|
|
2739
|
-
activeEventsModule = void 0;
|
|
2740
|
-
activeQueueModule = void 0;
|
|
2741
|
-
activeSessionRuntime = void 0;
|
|
2742
|
-
activeAuthRuntime = void 0;
|
|
2743
|
-
activeAuthContext = void 0;
|
|
2744
|
-
resetDB();
|
|
2745
|
-
await resetOptionalHoloSubsystems();
|
|
2746
|
-
if (previousOptionalSubsystemBindings) {
|
|
2747
|
-
restoreOptionalSubsystemRuntimeBindings(previousOptionalSubsystemBindings);
|
|
2748
|
-
}
|
|
2749
|
-
if (options.renderView) {
|
|
2750
|
-
restoreHoloRenderingRuntime(previousRenderView);
|
|
2751
|
-
}
|
|
2752
|
-
resetConfigRuntime();
|
|
2753
|
-
}
|
|
2942
|
+
runtime.initialized = false;
|
|
2943
|
+
if (getRuntimeState().current === runtime) getRuntimeState().current = void 0;
|
|
2944
|
+
await runtimeLifecycle.dispose({ projectRoot });
|
|
2754
2945
|
}
|
|
2755
2946
|
};
|
|
2756
2947
|
return runtime;
|
|
2757
2948
|
}
|
|
2758
2949
|
async function initializeHolo(projectRoot, options = {}) {
|
|
2759
2950
|
const state = getRuntimeState();
|
|
2760
|
-
const resolvedProjectRoot =
|
|
2951
|
+
const resolvedProjectRoot = resolve5(projectRoot);
|
|
2761
2952
|
const current = state.current;
|
|
2762
2953
|
if (current) {
|
|
2763
|
-
if (
|
|
2954
|
+
if (resolve5(current.projectRoot) !== resolvedProjectRoot) {
|
|
2764
2955
|
throw new Error(`A Holo runtime is already initialized for "${current.projectRoot}".`);
|
|
2765
2956
|
}
|
|
2766
2957
|
;
|
|
@@ -2768,7 +2959,7 @@ async function initializeHolo(projectRoot, options = {}) {
|
|
|
2768
2959
|
return current;
|
|
2769
2960
|
}
|
|
2770
2961
|
if (state.pending) {
|
|
2771
|
-
if (state.pendingProjectRoot &&
|
|
2962
|
+
if (state.pendingProjectRoot && resolve5(state.pendingProjectRoot) !== resolvedProjectRoot) {
|
|
2772
2963
|
throw new Error(`A Holo runtime is already initializing for "${state.pendingProjectRoot}".`);
|
|
2773
2964
|
}
|
|
2774
2965
|
return state.pending.then((runtime) => {
|
|
@@ -2801,7 +2992,7 @@ async function ensureHolo(projectRoot, options = {}) {
|
|
|
2801
2992
|
if (!current) {
|
|
2802
2993
|
return initializeHolo(projectRoot, options);
|
|
2803
2994
|
}
|
|
2804
|
-
if (
|
|
2995
|
+
if (resolve5(current.projectRoot) !== resolve5(projectRoot)) {
|
|
2805
2996
|
throw new Error(`A Holo runtime is already initialized for "${current.projectRoot}".`);
|
|
2806
2997
|
}
|
|
2807
2998
|
return current;
|