@holo-js/core 0.2.6 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-H43SANQQ.mjs → chunk-M7ZDJZQ7.mjs} +1011 -840
- 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
|
|
@@ -326,8 +349,8 @@ var runtimeModuleInternals = {
|
|
|
326
349
|
// src/storageRuntime.ts
|
|
327
350
|
import { mkdir as mkdir2, readFile as readFile2, readdir, rm as rm2, writeFile as writeFile2 } from "node:fs/promises";
|
|
328
351
|
import { dirname, isAbsolute, join as join2, relative, resolve as resolve2, sep, win32 } from "node:path";
|
|
329
|
-
async function importOptionalModule(specifier) {
|
|
330
|
-
return importOptionalRuntimeModule(specifier);
|
|
352
|
+
async function importOptionalModule(specifier, projectRoot) {
|
|
353
|
+
return importOptionalRuntimeModule(specifier, projectRoot ? { projectRoot } : {});
|
|
331
354
|
}
|
|
332
355
|
function resolveStorageKeyPath(root, key) {
|
|
333
356
|
if (isAbsolute(key) || win32.isAbsolute(key)) {
|
|
@@ -427,8 +450,8 @@ function createFileStorageBackend(root) {
|
|
|
427
450
|
}
|
|
428
451
|
};
|
|
429
452
|
}
|
|
430
|
-
async function createS3StorageBackend(disk) {
|
|
431
|
-
const storageS3 = await importOptionalModule("@holo-js/storage
|
|
453
|
+
async function createS3StorageBackend(projectRoot, disk) {
|
|
454
|
+
const storageS3 = await importOptionalModule("@holo-js/storage-s3", projectRoot);
|
|
432
455
|
if (!storageS3) {
|
|
433
456
|
throw new Error("[@holo-js/core] Storage config references an s3 disk but @holo-js/storage-s3 is not installed.");
|
|
434
457
|
}
|
|
@@ -455,7 +478,7 @@ async function configurePlainNodeStorageRuntime(projectRoot, loadedConfig) {
|
|
|
455
478
|
});
|
|
456
479
|
const backends = /* @__PURE__ */ new Map();
|
|
457
480
|
for (const [diskName, disk] of Object.entries(normalizedStorage.disks)) {
|
|
458
|
-
const backend = disk.driver === "s3" ? await createS3StorageBackend(disk) : createFileStorageBackend(resolve2(projectRoot, disk.root));
|
|
481
|
+
const backend = disk.driver === "s3" ? await createS3StorageBackend(projectRoot, disk) : createFileStorageBackend(resolve2(projectRoot, disk.root));
|
|
459
482
|
backends.set(diskName, backend);
|
|
460
483
|
}
|
|
461
484
|
storageRuntime.configureStorageRuntime({
|
|
@@ -481,150 +504,231 @@ var storageRuntimeInternals = {
|
|
|
481
504
|
importOptionalModule
|
|
482
505
|
};
|
|
483
506
|
|
|
484
|
-
// src/portable/
|
|
507
|
+
// src/portable/discoveryRuntime.ts
|
|
508
|
+
import { existsSync } from "node:fs";
|
|
509
|
+
import { resolve as resolve3 } from "node:path";
|
|
485
510
|
async function preloadGeneratedSchemaModule(projectRoot, registry) {
|
|
486
511
|
const entry = registry?.paths.generatedSchema;
|
|
487
|
-
if (!entry)
|
|
488
|
-
return;
|
|
489
|
-
}
|
|
512
|
+
if (!entry) return;
|
|
490
513
|
const expectedTarget = resolve3(projectRoot, entry);
|
|
491
|
-
if (!existsSync(expectedTarget))
|
|
492
|
-
return;
|
|
493
|
-
}
|
|
514
|
+
if (!existsSync(expectedTarget)) return;
|
|
494
515
|
try {
|
|
495
516
|
await importBundledRuntimeModule(projectRoot, entry);
|
|
496
517
|
} catch (error) {
|
|
497
518
|
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
|
-
}
|
|
519
|
+
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");
|
|
520
|
+
if (failedTarget === expectedTarget) return;
|
|
503
521
|
}
|
|
504
522
|
throw error;
|
|
505
523
|
}
|
|
506
524
|
}
|
|
507
525
|
async function preloadDiscoveredModelModules(projectRoot, registry) {
|
|
508
|
-
if (!registry || registry.models.length === 0)
|
|
509
|
-
return;
|
|
510
|
-
}
|
|
526
|
+
if (!registry || registry.models.length === 0) return;
|
|
511
527
|
for (const entry of registry.models) {
|
|
512
528
|
const sourcePath = resolve3(projectRoot, entry.sourcePath);
|
|
513
|
-
if (
|
|
514
|
-
continue;
|
|
515
|
-
}
|
|
516
|
-
await importBundledRuntimeModule(projectRoot, sourcePath);
|
|
529
|
+
if (existsSync(sourcePath)) await importBundledRuntimeModule(projectRoot, sourcePath);
|
|
517
530
|
}
|
|
518
531
|
}
|
|
519
|
-
|
|
520
|
-
|
|
532
|
+
|
|
533
|
+
// src/portable/configRuntime.ts
|
|
534
|
+
var featureConfigContributionSpecifiers = [
|
|
535
|
+
"@holo-js/auth/config",
|
|
536
|
+
"@holo-js/broadcast/config",
|
|
537
|
+
"@holo-js/cache/config",
|
|
538
|
+
"@holo-js/mail/config",
|
|
539
|
+
"@holo-js/media/config",
|
|
540
|
+
"@holo-js/notifications/config",
|
|
541
|
+
"@holo-js/queue/config",
|
|
542
|
+
"@holo-js/security/config",
|
|
543
|
+
"@holo-js/session/config",
|
|
544
|
+
"@holo-js/storage/config"
|
|
545
|
+
];
|
|
546
|
+
async function loadDirectFeatureConfigContribution(specifier) {
|
|
547
|
+
switch (specifier) {
|
|
548
|
+
case "@holo-js/auth/config":
|
|
549
|
+
await import("@holo-js/auth/config");
|
|
550
|
+
break;
|
|
551
|
+
case "@holo-js/broadcast/config":
|
|
552
|
+
await import("@holo-js/broadcast/config");
|
|
553
|
+
break;
|
|
554
|
+
case "@holo-js/cache/config":
|
|
555
|
+
await import("@holo-js/cache/config");
|
|
556
|
+
break;
|
|
557
|
+
case "@holo-js/mail/config":
|
|
558
|
+
await import("@holo-js/mail/config");
|
|
559
|
+
break;
|
|
560
|
+
case "@holo-js/media/config":
|
|
561
|
+
await import("@holo-js/media/config");
|
|
562
|
+
break;
|
|
563
|
+
case "@holo-js/notifications/config":
|
|
564
|
+
await import("@holo-js/notifications/config");
|
|
565
|
+
break;
|
|
566
|
+
case "@holo-js/queue/config":
|
|
567
|
+
await import("@holo-js/queue/config");
|
|
568
|
+
break;
|
|
569
|
+
case "@holo-js/security/config":
|
|
570
|
+
await import("@holo-js/security/config");
|
|
571
|
+
break;
|
|
572
|
+
case "@holo-js/session/config":
|
|
573
|
+
await import("@holo-js/session/config");
|
|
574
|
+
break;
|
|
575
|
+
case "@holo-js/storage/config":
|
|
576
|
+
await import("@holo-js/storage/config");
|
|
577
|
+
break;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
var defaultLoaders = {
|
|
581
|
+
loadDirect: loadDirectFeatureConfigContribution,
|
|
582
|
+
async loadOptional(specifier, projectRoot) {
|
|
583
|
+
return await importOptionalRuntimeModule(specifier, { projectRoot });
|
|
584
|
+
}
|
|
585
|
+
};
|
|
586
|
+
async function loadInstalledFeatureConfigContributions(projectRoot, loaders = defaultLoaders) {
|
|
587
|
+
for (const specifier of featureConfigContributionSpecifiers) {
|
|
588
|
+
try {
|
|
589
|
+
await loaders.loadDirect(specifier);
|
|
590
|
+
} catch {
|
|
591
|
+
const loaded = await loaders.loadOptional(specifier, projectRoot);
|
|
592
|
+
if (!loaded) continue;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
521
595
|
}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
function
|
|
525
|
-
return
|
|
596
|
+
|
|
597
|
+
// src/portable/authPersistence.ts
|
|
598
|
+
function normalizeDateValue(value) {
|
|
599
|
+
return value instanceof Date ? value : new Date(String(value));
|
|
526
600
|
}
|
|
527
|
-
function
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
601
|
+
function normalizeJsonValue(value) {
|
|
602
|
+
if (typeof value !== "string") return value;
|
|
603
|
+
try {
|
|
604
|
+
return JSON.parse(value);
|
|
605
|
+
} catch {
|
|
606
|
+
return value;
|
|
607
|
+
}
|
|
531
608
|
}
|
|
532
|
-
function
|
|
533
|
-
|
|
609
|
+
function normalizeStoredUserId(value) {
|
|
610
|
+
return typeof value === "number" ? value : String(value);
|
|
534
611
|
}
|
|
535
|
-
function
|
|
536
|
-
|
|
612
|
+
function normalizeAccessTokenRecord(row) {
|
|
613
|
+
const abilities = normalizeJsonValue(row.abilities);
|
|
614
|
+
return Object.freeze({
|
|
615
|
+
id: String(row.id),
|
|
616
|
+
provider: String(row.provider),
|
|
617
|
+
userId: normalizeStoredUserId(row.user_id),
|
|
618
|
+
name: String(row.name),
|
|
619
|
+
abilities: Array.isArray(abilities) ? Object.freeze([...abilities]) : Object.freeze([]),
|
|
620
|
+
tokenHash: String(row.token_hash),
|
|
621
|
+
createdAt: normalizeDateValue(row.created_at),
|
|
622
|
+
lastUsedAt: row.last_used_at ? normalizeDateValue(row.last_used_at) : void 0,
|
|
623
|
+
expiresAt: row.expires_at ? normalizeDateValue(row.expires_at) : null
|
|
624
|
+
});
|
|
537
625
|
}
|
|
538
|
-
function
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
626
|
+
function serializeAccessTokenRecord(record) {
|
|
627
|
+
return {
|
|
628
|
+
id: record.id,
|
|
629
|
+
provider: record.provider,
|
|
630
|
+
user_id: String(record.userId),
|
|
631
|
+
name: record.name,
|
|
632
|
+
abilities: JSON.stringify(record.abilities),
|
|
633
|
+
token_hash: record.tokenHash,
|
|
634
|
+
created_at: record.createdAt.toISOString(),
|
|
635
|
+
last_used_at: record.lastUsedAt?.toISOString() ?? null,
|
|
636
|
+
expires_at: record.expiresAt?.toISOString() ?? null,
|
|
637
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
638
|
+
};
|
|
546
639
|
}
|
|
547
|
-
function
|
|
548
|
-
const state = getRuntimeState();
|
|
549
|
-
const runtime = globalThis;
|
|
640
|
+
function normalizeEmailVerificationTokenRecord(row) {
|
|
550
641
|
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
|
-
} : {}
|
|
642
|
+
id: String(row.id),
|
|
643
|
+
provider: String(row.provider),
|
|
644
|
+
userId: normalizeStoredUserId(row.user_id),
|
|
645
|
+
email: String(row.email),
|
|
646
|
+
tokenHash: String(row.token_hash),
|
|
647
|
+
createdAt: normalizeDateValue(row.created_at),
|
|
648
|
+
expiresAt: normalizeDateValue(row.expires_at)
|
|
568
649
|
});
|
|
569
650
|
}
|
|
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);
|
|
651
|
+
function serializeEmailVerificationTokenRecord(record) {
|
|
652
|
+
return {
|
|
653
|
+
id: record.id,
|
|
654
|
+
provider: record.provider,
|
|
655
|
+
user_id: String(record.userId),
|
|
656
|
+
email: record.email,
|
|
657
|
+
token_hash: record.tokenHash,
|
|
658
|
+
created_at: record.createdAt.toISOString(),
|
|
659
|
+
expires_at: record.expiresAt.toISOString(),
|
|
660
|
+
used_at: null,
|
|
661
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
662
|
+
};
|
|
596
663
|
}
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
664
|
+
function normalizePasswordResetTokenRecord(row) {
|
|
665
|
+
return Object.freeze({
|
|
666
|
+
id: String(row.id),
|
|
667
|
+
provider: typeof row.provider === "string" ? row.provider : "users",
|
|
668
|
+
email: String(row.email),
|
|
669
|
+
table: typeof row.__holo_table === "string" ? row.__holo_table : void 0,
|
|
670
|
+
tokenHash: String(row.token_hash),
|
|
671
|
+
createdAt: normalizeDateValue(row.created_at),
|
|
672
|
+
expiresAt: normalizeDateValue(row.expires_at)
|
|
604
673
|
});
|
|
605
674
|
}
|
|
606
|
-
function
|
|
607
|
-
return
|
|
675
|
+
function serializePasswordResetTokenRecord(record) {
|
|
676
|
+
return {
|
|
677
|
+
id: record.id,
|
|
678
|
+
provider: record.provider,
|
|
679
|
+
email: record.email,
|
|
680
|
+
token_hash: record.tokenHash,
|
|
681
|
+
created_at: record.createdAt.toISOString(),
|
|
682
|
+
expires_at: record.expiresAt.toISOString(),
|
|
683
|
+
used_at: null,
|
|
684
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
685
|
+
};
|
|
608
686
|
}
|
|
609
|
-
|
|
610
|
-
|
|
687
|
+
|
|
688
|
+
// src/portable/pluginRuntime.ts
|
|
689
|
+
import { resolve as resolve4 } from "node:path";
|
|
690
|
+
import {
|
|
691
|
+
loadHoloPluginBootModules,
|
|
692
|
+
loadHoloPluginDefinitions
|
|
693
|
+
} from "@holo-js/kernel";
|
|
694
|
+
function isRecord2(value) {
|
|
695
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
611
696
|
}
|
|
612
|
-
|
|
613
|
-
|
|
697
|
+
var bootedHoloPluginModules = /* @__PURE__ */ new Set();
|
|
698
|
+
function resolveHoloPluginBootKey(projectRoot, bootModule) {
|
|
699
|
+
return [resolve4(projectRoot), bootModule.plugin.packageName, bootModule.runtime].join("\0");
|
|
614
700
|
}
|
|
615
|
-
function
|
|
616
|
-
return
|
|
701
|
+
function resolveLoadedPluginNames(loadedConfig) {
|
|
702
|
+
return loadedConfig.app.plugins;
|
|
617
703
|
}
|
|
618
|
-
function
|
|
619
|
-
|
|
704
|
+
async function bootConfiguredHoloPluginModule(projectRoot, loadedConfig, bootModule) {
|
|
705
|
+
const bootKey = resolveHoloPluginBootKey(projectRoot, bootModule);
|
|
706
|
+
if (bootedHoloPluginModules.has(bootKey)) return;
|
|
707
|
+
const candidate = isRecord2(bootModule.module) && typeof bootModule.module.default !== "undefined" ? bootModule.module.default : bootModule.module;
|
|
708
|
+
if (typeof candidate !== "function") return;
|
|
709
|
+
await candidate({ projectRoot, config: loadedConfig });
|
|
710
|
+
bootedHoloPluginModules.add(bootKey);
|
|
620
711
|
}
|
|
621
|
-
function
|
|
622
|
-
|
|
712
|
+
function resetBootedHoloPluginModules() {
|
|
713
|
+
bootedHoloPluginModules.clear();
|
|
623
714
|
}
|
|
624
|
-
function
|
|
625
|
-
|
|
715
|
+
async function loadConfiguredHoloPluginDefinitions(projectRoot, pluginNames) {
|
|
716
|
+
const normalizedNames = [...new Set(pluginNames.map((name) => name.trim()).filter(Boolean))];
|
|
717
|
+
return loadHoloPluginDefinitions(projectRoot, normalizedNames, { moduleVersion: String(Date.now()) });
|
|
626
718
|
}
|
|
627
|
-
|
|
719
|
+
async function loadConfiguredHoloPluginBootModules(projectRoot, plugins) {
|
|
720
|
+
return loadHoloPluginBootModules(projectRoot, plugins, { moduleVersion: String(Date.now()) });
|
|
721
|
+
}
|
|
722
|
+
function mergeQueueRuntimeDriverFactories(...sources) {
|
|
723
|
+
const factories = /* @__PURE__ */ new Map();
|
|
724
|
+
for (const source of sources) {
|
|
725
|
+
for (const factory of source ?? []) factories.set(factory.driver, factory);
|
|
726
|
+
}
|
|
727
|
+
return Object.freeze([...factories.values()]);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// src/portable/authRequestContext.ts
|
|
731
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
628
732
|
function attachAuthRequestAccessors(context, accessors) {
|
|
629
733
|
return Object.freeze({
|
|
630
734
|
...context,
|
|
@@ -656,141 +760,503 @@ function createRequestAwareAuthContext(context, accessors) {
|
|
|
656
760
|
return resolveRequestContext().redirectResponse?.(url, status);
|
|
657
761
|
},
|
|
658
762
|
setRequestAccessors(nextAccessors) {
|
|
659
|
-
requestAccessorStorage.enterWith({
|
|
660
|
-
accessors: nextAccessors
|
|
661
|
-
});
|
|
763
|
+
requestAccessorStorage.enterWith({ accessors: nextAccessors });
|
|
662
764
|
},
|
|
663
|
-
|
|
664
|
-
return
|
|
665
|
-
accessors: nextAccessors
|
|
666
|
-
|
|
765
|
+
runWithRequestAccessors(nextAccessors, callback) {
|
|
766
|
+
return requestAccessorStorage.run(
|
|
767
|
+
{ accessors: nextAccessors },
|
|
768
|
+
() => context.run ? context.run(callback) : callback()
|
|
769
|
+
);
|
|
667
770
|
}
|
|
668
771
|
});
|
|
669
772
|
}
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
}
|
|
675
|
-
return queueModule;
|
|
676
|
-
}
|
|
677
|
-
async function loadQueueDbModule() {
|
|
678
|
-
return portableRuntimeModuleInternals.importOptionalModule("@holo-js/queue-db");
|
|
773
|
+
|
|
774
|
+
// src/portable/recordPersistence.ts
|
|
775
|
+
function normalizeDateLike(value) {
|
|
776
|
+
return value instanceof Date ? value : new Date(String(value));
|
|
679
777
|
}
|
|
680
|
-
|
|
681
|
-
const
|
|
682
|
-
|
|
778
|
+
function normalizeSessionRecordFromRow(row) {
|
|
779
|
+
const decodedData = (() => {
|
|
780
|
+
if (row.data && typeof row.data === "object") return row.data;
|
|
781
|
+
if (typeof row.data !== "string") return {};
|
|
782
|
+
try {
|
|
783
|
+
const parsed = JSON.parse(row.data);
|
|
784
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
785
|
+
} catch {
|
|
786
|
+
return {};
|
|
787
|
+
}
|
|
788
|
+
})();
|
|
789
|
+
return Object.freeze({
|
|
790
|
+
id: String(row.id),
|
|
791
|
+
store: typeof row.store === "string" ? row.store : "database",
|
|
792
|
+
data: Object.freeze(decodedData),
|
|
793
|
+
createdAt: normalizeDateLike(row.created_at),
|
|
794
|
+
lastActivityAt: normalizeDateLike(row.last_activity_at),
|
|
795
|
+
expiresAt: normalizeDateLike(row.expires_at),
|
|
796
|
+
rememberTokenHash: typeof row.remember_token_hash === "string" ? row.remember_token_hash : void 0
|
|
683
797
|
});
|
|
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
798
|
}
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
799
|
+
function serializeSessionRecordForRow(record) {
|
|
800
|
+
return {
|
|
801
|
+
id: record.id,
|
|
802
|
+
store: record.store,
|
|
803
|
+
data: JSON.stringify(record.data ?? {}),
|
|
804
|
+
created_at: record.createdAt.toISOString(),
|
|
805
|
+
last_activity_at: record.lastActivityAt.toISOString(),
|
|
806
|
+
expires_at: record.expiresAt.toISOString(),
|
|
807
|
+
invalidated_at: null,
|
|
808
|
+
remember_token_hash: record.rememberTokenHash ?? null
|
|
809
|
+
};
|
|
742
810
|
}
|
|
743
|
-
|
|
744
|
-
const
|
|
745
|
-
|
|
811
|
+
function normalizeNotificationRecordFromRow(row) {
|
|
812
|
+
const decodedData = normalizeJsonValue(row.data);
|
|
813
|
+
return Object.freeze({
|
|
814
|
+
id: String(row.id),
|
|
815
|
+
type: typeof row.type === "string" ? row.type : void 0,
|
|
816
|
+
notifiableType: String(row.notifiable_type),
|
|
817
|
+
notifiableId: typeof row.notifiable_id === "number" ? row.notifiable_id : String(row.notifiable_id),
|
|
818
|
+
data: decodedData,
|
|
819
|
+
readAt: row.read_at ? normalizeDateLike(row.read_at) : null,
|
|
820
|
+
createdAt: normalizeDateLike(row.created_at),
|
|
821
|
+
updatedAt: normalizeDateLike(row.updated_at)
|
|
746
822
|
});
|
|
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
823
|
}
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
}
|
|
764
|
-
return authModule;
|
|
824
|
+
function serializeNotificationRecordForRow(record) {
|
|
825
|
+
return {
|
|
826
|
+
id: record.id,
|
|
827
|
+
type: record.type ?? null,
|
|
828
|
+
notifiable_type: record.notifiableType,
|
|
829
|
+
notifiable_id: String(record.notifiableId),
|
|
830
|
+
data: JSON.stringify(record.data ?? null),
|
|
831
|
+
read_at: record.readAt ? record.readAt.toISOString() : null,
|
|
832
|
+
created_at: record.createdAt.toISOString(),
|
|
833
|
+
updated_at: record.updatedAt.toISOString()
|
|
834
|
+
};
|
|
765
835
|
}
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
836
|
+
|
|
837
|
+
// src/portable/notificationPersistence.ts
|
|
838
|
+
import { DB } from "@holo-js/db";
|
|
839
|
+
function createCoreNotificationStore(loadedConfig) {
|
|
840
|
+
const tableName = loadedConfig.notifications.table;
|
|
841
|
+
const connectionName = loadedConfig.database.defaultConnection;
|
|
842
|
+
return Object.freeze({
|
|
843
|
+
async create(record) {
|
|
844
|
+
await DB.table(tableName, connectionName).insert(serializeNotificationRecordForRow(record));
|
|
845
|
+
},
|
|
846
|
+
async list(notifiable) {
|
|
847
|
+
const rows = await DB.table(tableName, connectionName).where("notifiable_type", notifiable.type).where("notifiable_id", String(notifiable.id)).orderBy("created_at", "desc").get();
|
|
848
|
+
return Object.freeze(rows.map((row) => normalizeNotificationRecordFromRow(row)));
|
|
849
|
+
},
|
|
850
|
+
async unread(notifiable) {
|
|
851
|
+
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();
|
|
852
|
+
return Object.freeze(rows.map((row) => normalizeNotificationRecordFromRow(row)));
|
|
853
|
+
},
|
|
854
|
+
async markAsRead(ids) {
|
|
855
|
+
if (ids.length === 0) return 0;
|
|
856
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
857
|
+
const result = await DB.table(tableName, connectionName).whereIn("id", ids).update({ read_at: now, updated_at: now });
|
|
858
|
+
return result.affectedRows ?? 0;
|
|
859
|
+
},
|
|
860
|
+
async markAsUnread(ids) {
|
|
861
|
+
if (ids.length === 0) return 0;
|
|
862
|
+
const result = await DB.table(tableName, connectionName).whereIn("id", ids).update({ read_at: null, updated_at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
863
|
+
return result.affectedRows ?? 0;
|
|
864
|
+
},
|
|
865
|
+
async delete(ids) {
|
|
866
|
+
if (ids.length === 0) return 0;
|
|
867
|
+
const result = await DB.table(tableName, connectionName).whereIn("id", ids).delete();
|
|
868
|
+
return result.affectedRows ?? 0;
|
|
869
|
+
}
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
// src/portable/authMailDelivery.ts
|
|
874
|
+
var authEmailDateFormatter = new Intl.DateTimeFormat("en-US", {
|
|
875
|
+
dateStyle: "long",
|
|
876
|
+
timeStyle: "short",
|
|
877
|
+
timeZone: "UTC"
|
|
878
|
+
});
|
|
879
|
+
function formatAuthEmailExpiration(expiresAt) {
|
|
880
|
+
return `${authEmailDateFormatter.format(expiresAt)} UTC`;
|
|
881
|
+
}
|
|
882
|
+
function escapeAuthEmailHtml(value) {
|
|
883
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
884
|
+
}
|
|
885
|
+
function createNotificationMailText(message) {
|
|
886
|
+
const parts = [
|
|
887
|
+
typeof message.greeting === "string" ? message.greeting.trim() : void 0,
|
|
888
|
+
...(message.lines ?? []).map((line) => line.trim()).filter(Boolean),
|
|
889
|
+
message.action ? `${message.action.label}: ${message.action.url}` : void 0
|
|
890
|
+
].filter((value) => typeof value === "string" && value.length > 0);
|
|
891
|
+
return parts.length > 0 ? parts.join("\n\n") : void 0;
|
|
892
|
+
}
|
|
893
|
+
function createNotificationMailHtml(message) {
|
|
894
|
+
const greeting = typeof message.greeting === "string" ? message.greeting.trim() : void 0;
|
|
895
|
+
const lines = (message.lines ?? []).map((line) => line.trim()).filter(Boolean);
|
|
896
|
+
const sections = [
|
|
897
|
+
greeting ? `<p style="margin:0 0 16px;">${escapeAuthEmailHtml(greeting)}</p>` : "",
|
|
898
|
+
...lines.map((line) => `<p style="margin:0 0 16px;">${escapeAuthEmailHtml(line)}</p>`),
|
|
899
|
+
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>` : "",
|
|
900
|
+
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>` : ""
|
|
901
|
+
].join("");
|
|
902
|
+
return [
|
|
903
|
+
"<!doctype html>",
|
|
904
|
+
'<html><head><meta charset="utf-8">',
|
|
905
|
+
`<title>${escapeAuthEmailHtml(message.subject)}</title>`,
|
|
906
|
+
'</head><body style="margin:0;padding:24px;font-family:Arial,sans-serif;color:#0f172a;background:#f8fafc;">',
|
|
907
|
+
'<div style="max-width:640px;margin:0 auto;background:#ffffff;border:1px solid #e2e8f0;border-radius:12px;padding:32px;">',
|
|
908
|
+
`<h1 style="margin:0 0 24px;font-size:24px;line-height:1.3;">${escapeAuthEmailHtml(message.subject)}</h1>`,
|
|
909
|
+
sections,
|
|
910
|
+
"</div></body></html>"
|
|
911
|
+
].join("");
|
|
912
|
+
}
|
|
913
|
+
function createAuthActionUrl(appUrl, path, token) {
|
|
914
|
+
const normalizedBaseUrl = appUrl.endsWith("/") ? appUrl.slice(0, -1) : appUrl;
|
|
915
|
+
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
916
|
+
const url = new URL(`${normalizedBaseUrl}${normalizedPath}`);
|
|
917
|
+
url.searchParams.set("token", token);
|
|
918
|
+
return url.toString();
|
|
919
|
+
}
|
|
920
|
+
function createAuthEmailHtml(message) {
|
|
921
|
+
return createNotificationMailHtml(message);
|
|
922
|
+
}
|
|
923
|
+
function createCoreNotificationMailSender(mailModule) {
|
|
924
|
+
return Object.freeze({
|
|
925
|
+
async send(message, context) {
|
|
926
|
+
if (!context.route) {
|
|
927
|
+
throw new Error("[@holo-js/core] Email notifications require a resolved email route before bridging into mail.");
|
|
928
|
+
}
|
|
929
|
+
const fallbackText = createNotificationMailText(message);
|
|
930
|
+
await mailModule.sendMail({
|
|
931
|
+
to: context.route,
|
|
932
|
+
subject: message.subject,
|
|
933
|
+
html: typeof message.html === "string" ? message.html : createNotificationMailHtml(message),
|
|
934
|
+
...typeof (message.text ?? fallbackText) === "string" ? { text: message.text ?? fallbackText } : {},
|
|
935
|
+
...message.metadata ? { metadata: message.metadata } : {}
|
|
936
|
+
});
|
|
937
|
+
}
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
function createAuthMailDeliveryHook(mailModule, appUrl) {
|
|
941
|
+
return Object.freeze({
|
|
942
|
+
async sendEmailVerification(input) {
|
|
943
|
+
const recipientName = typeof input.user?.name === "string" ? input.user.name?.trim() : void 0;
|
|
944
|
+
const lines = [
|
|
945
|
+
"Confirm your account to finish signing in.",
|
|
946
|
+
`This verification link expires at ${formatAuthEmailExpiration(input.token.expiresAt)}.`
|
|
947
|
+
];
|
|
948
|
+
const action = {
|
|
949
|
+
label: "Verify email address",
|
|
950
|
+
url: createAuthActionUrl(appUrl, input.route, input.token.plainTextToken)
|
|
951
|
+
};
|
|
952
|
+
await mailModule.sendMail({
|
|
953
|
+
to: { email: input.email, ...recipientName ? { name: recipientName } : {} },
|
|
954
|
+
subject: "Verify your email address",
|
|
955
|
+
html: createAuthEmailHtml({
|
|
956
|
+
subject: "Verify your email address",
|
|
957
|
+
...recipientName ? { greeting: `Hello ${recipientName},` } : {},
|
|
958
|
+
lines,
|
|
959
|
+
action
|
|
960
|
+
}),
|
|
961
|
+
text: createNotificationMailText({
|
|
962
|
+
...recipientName ? { greeting: `Hello ${recipientName},` } : {},
|
|
963
|
+
lines,
|
|
964
|
+
action
|
|
965
|
+
}),
|
|
966
|
+
metadata: { provider: input.provider, tokenId: input.token.id }
|
|
967
|
+
});
|
|
968
|
+
},
|
|
969
|
+
async sendPasswordReset(input) {
|
|
970
|
+
const lines = [
|
|
971
|
+
"Click the link below to choose a new password.",
|
|
972
|
+
`This reset link expires at ${formatAuthEmailExpiration(input.token.expiresAt)}.`
|
|
973
|
+
];
|
|
974
|
+
const action = {
|
|
975
|
+
label: "Reset password",
|
|
976
|
+
url: createAuthActionUrl(appUrl, input.route, input.token.plainTextToken)
|
|
977
|
+
};
|
|
978
|
+
await mailModule.sendMail({
|
|
979
|
+
to: input.email,
|
|
980
|
+
subject: "Reset your password",
|
|
981
|
+
html: createAuthEmailHtml({ subject: "Reset your password", lines, action }),
|
|
982
|
+
text: createNotificationMailText({ lines, action }),
|
|
983
|
+
metadata: { provider: input.provider, tokenId: input.token.id }
|
|
984
|
+
});
|
|
985
|
+
}
|
|
986
|
+
});
|
|
772
987
|
}
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
988
|
+
|
|
989
|
+
// src/portable/optionalFeatureLoader.ts
|
|
990
|
+
function createOptionalFeatureModuleLoader(importer, specifier, missingPackageMessage) {
|
|
991
|
+
async function load(required = false, options = {}) {
|
|
992
|
+
const module = await importer(specifier, options);
|
|
993
|
+
if (!module && required) throw new Error(missingPackageMessage);
|
|
994
|
+
return module;
|
|
777
995
|
}
|
|
778
|
-
return
|
|
996
|
+
return load;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
// src/portable/featureDetection.ts
|
|
1000
|
+
function hasLoadedConfigFile(loadedConfig, configName) {
|
|
1001
|
+
return loadedConfig.loadedFiles.some((filePath) => {
|
|
1002
|
+
const normalizedPath = filePath.replaceAll("\\", "/");
|
|
1003
|
+
return ["ts", "mts", "js", "mjs", "cts", "cjs"].some((extension) => normalizedPath.endsWith(`/config/${configName}.${extension}`));
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
1006
|
+
function queueConfigUsesDatabaseDriver(loadedConfig) {
|
|
1007
|
+
if (!loadedConfig.queue) return false;
|
|
1008
|
+
return Object.values(loadedConfig.queue.connections).some((connection) => connection.driver === "database");
|
|
1009
|
+
}
|
|
1010
|
+
function queueConfigUsesRedisDriver(loadedConfig) {
|
|
1011
|
+
if (!loadedConfig.queue) return false;
|
|
1012
|
+
return Object.values(loadedConfig.queue.connections).some((connection) => connection.driver === "redis");
|
|
1013
|
+
}
|
|
1014
|
+
function queueConfigUsesDatabaseBackedFailedStore(loadedConfig) {
|
|
1015
|
+
if (!loadedConfig.queue) return false;
|
|
1016
|
+
return loadedConfig.queue.failed !== false;
|
|
1017
|
+
}
|
|
1018
|
+
function registryHasJobs(registry) {
|
|
1019
|
+
return (registry?.jobs.length ?? 0) > 0;
|
|
1020
|
+
}
|
|
1021
|
+
function registryHasEvents(registry) {
|
|
1022
|
+
return (registry?.events.length ?? 0) > 0 || (registry?.listeners.length ?? 0) > 0;
|
|
1023
|
+
}
|
|
1024
|
+
function authConfigUsesSocialProviders(loadedConfig) {
|
|
1025
|
+
if (!loadedConfig.auth) return false;
|
|
1026
|
+
return Object.keys(loadedConfig.auth.social).length > 0;
|
|
1027
|
+
}
|
|
1028
|
+
function authConfigUsesWorkosProviders(loadedConfig) {
|
|
1029
|
+
if (!loadedConfig.auth) return false;
|
|
1030
|
+
return Object.entries(loadedConfig.auth.workos).some(([name, provider]) => name !== "provider" && name !== "identityStore" && typeof provider === "object" && provider !== null);
|
|
1031
|
+
}
|
|
1032
|
+
function authConfigUsesClerkProviders(loadedConfig) {
|
|
1033
|
+
if (!loadedConfig.auth) return false;
|
|
1034
|
+
return Object.entries(loadedConfig.auth.clerk).some(([name, provider]) => name !== "provider" && name !== "identityStore" && typeof provider === "object" && provider !== null);
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
// src/portable/runtimeState.ts
|
|
1038
|
+
function createRuntimeStateAccessors() {
|
|
1039
|
+
const getRuntimeState2 = () => {
|
|
1040
|
+
const runtime = globalThis;
|
|
1041
|
+
runtime.__holoRuntime__ ??= {};
|
|
1042
|
+
return runtime.__holoRuntime__;
|
|
1043
|
+
};
|
|
1044
|
+
const snapshotOptionalSubsystemRuntimeBindings2 = () => {
|
|
1045
|
+
const state = getRuntimeState2();
|
|
1046
|
+
const runtime = globalThis;
|
|
1047
|
+
return Object.freeze({
|
|
1048
|
+
...runtime.__holoMailRuntime__?.bindings ? { mail: runtime.__holoMailRuntime__.bindings } : {},
|
|
1049
|
+
...runtime.__holoNotificationsRuntime__?.bindings ? { notifications: runtime.__holoNotificationsRuntime__.bindings } : {},
|
|
1050
|
+
...runtime.__holoBroadcastRuntime__?.bindings ? { broadcast: runtime.__holoBroadcastRuntime__.bindings } : {},
|
|
1051
|
+
...state.sessionRedisAdapters ? { session: Object.freeze({ sessionRedisAdapters: state.sessionRedisAdapters }) } : {},
|
|
1052
|
+
...runtime.__holoSecurityRuntime__?.bindings || state.securityRedisAdapter || typeof state.securityRateLimitStoreManaged !== "undefined" ? {
|
|
1053
|
+
security: Object.freeze({
|
|
1054
|
+
...runtime.__holoSecurityRuntime__?.bindings ? { bindings: runtime.__holoSecurityRuntime__.bindings } : {},
|
|
1055
|
+
...state.securityRedisAdapter ? { securityRedisAdapter: state.securityRedisAdapter } : {},
|
|
1056
|
+
...typeof state.securityRateLimitStoreManaged !== "undefined" ? { securityRateLimitStoreManaged: state.securityRateLimitStoreManaged } : {}
|
|
1057
|
+
})
|
|
1058
|
+
} : {}
|
|
1059
|
+
});
|
|
1060
|
+
};
|
|
1061
|
+
const restoreOptionalSubsystemRuntimeBindings2 = (bindings) => {
|
|
1062
|
+
const state = getRuntimeState2();
|
|
1063
|
+
const runtime = globalThis;
|
|
1064
|
+
if (bindings.mail || runtime.__holoMailRuntime__) {
|
|
1065
|
+
runtime.__holoMailRuntime__ ??= {};
|
|
1066
|
+
runtime.__holoMailRuntime__.bindings = bindings.mail;
|
|
1067
|
+
}
|
|
1068
|
+
if (bindings.notifications || runtime.__holoNotificationsRuntime__) {
|
|
1069
|
+
runtime.__holoNotificationsRuntime__ ??= {};
|
|
1070
|
+
runtime.__holoNotificationsRuntime__.bindings = bindings.notifications;
|
|
1071
|
+
}
|
|
1072
|
+
if (bindings.broadcast || runtime.__holoBroadcastRuntime__) {
|
|
1073
|
+
runtime.__holoBroadcastRuntime__ ??= {};
|
|
1074
|
+
runtime.__holoBroadcastRuntime__.bindings = bindings.broadcast;
|
|
1075
|
+
}
|
|
1076
|
+
state.sessionRedisAdapters = bindings.session?.sessionRedisAdapters;
|
|
1077
|
+
if (bindings.security || runtime.__holoSecurityRuntime__) {
|
|
1078
|
+
runtime.__holoSecurityRuntime__ ??= {};
|
|
1079
|
+
runtime.__holoSecurityRuntime__.bindings = bindings.security?.bindings;
|
|
1080
|
+
state.securityRedisAdapter = bindings.security?.securityRedisAdapter;
|
|
1081
|
+
state.securityRateLimitStoreManaged = bindings.security?.securityRateLimitStoreManaged;
|
|
1082
|
+
}
|
|
1083
|
+
};
|
|
1084
|
+
return {
|
|
1085
|
+
getRuntimeState: getRuntimeState2,
|
|
1086
|
+
restoreOptionalSubsystemRuntimeBindings: restoreOptionalSubsystemRuntimeBindings2,
|
|
1087
|
+
snapshotOptionalSubsystemRuntimeBindings: snapshotOptionalSubsystemRuntimeBindings2
|
|
1088
|
+
};
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
// src/portable/holo.ts
|
|
1092
|
+
function closeSessionRedisAdapter(adapter) {
|
|
1093
|
+
return adapter.disconnect?.() || adapter.close?.();
|
|
1094
|
+
}
|
|
1095
|
+
var CORE_BROADCAST_PUBLISHER_MARKER = /* @__PURE__ */ Symbol.for("holo-js.core.broadcast.publisher");
|
|
1096
|
+
var frameworkBuildEnvKey = "HOLO_INTERNAL_FRAMEWORK_BUILD";
|
|
1097
|
+
function shouldBootRuntimeServices(processEnv = process.env) {
|
|
1098
|
+
return processEnv[frameworkBuildEnvKey] !== "1";
|
|
1099
|
+
}
|
|
1100
|
+
var {
|
|
1101
|
+
getRuntimeState,
|
|
1102
|
+
restoreOptionalSubsystemRuntimeBindings,
|
|
1103
|
+
snapshotOptionalSubsystemRuntimeBindings
|
|
1104
|
+
} = createRuntimeStateAccessors();
|
|
1105
|
+
var BROADCAST_PUBLISH_TIMEOUT_MS = 1e4;
|
|
1106
|
+
async function importOptionalModule2(specifier, options = {}) {
|
|
1107
|
+
return importOptionalRuntimeModule(specifier, options);
|
|
1108
|
+
}
|
|
1109
|
+
var portableRuntimeModuleInternals = {
|
|
1110
|
+
importOptionalModule: importOptionalModule2
|
|
1111
|
+
};
|
|
1112
|
+
var HOLO_AUTH_PROVIDER_MARKER = /* @__PURE__ */ Symbol.for("holo-js.auth.provider");
|
|
1113
|
+
var importOptionalFeature = (specifier, options) => portableRuntimeModuleInternals.importOptionalModule(specifier, options);
|
|
1114
|
+
var loadQueueModule = createOptionalFeatureModuleLoader(
|
|
1115
|
+
importOptionalFeature,
|
|
1116
|
+
"@holo-js/queue",
|
|
1117
|
+
"[@holo-js/core] Queue support requires @holo-js/queue to be installed."
|
|
1118
|
+
);
|
|
1119
|
+
async function loadConfiguredDatabaseDrivers(projectRoot, loadedConfig) {
|
|
1120
|
+
const packageByDriver = {
|
|
1121
|
+
sqlite: { packageName: "@holo-js/db-sqlite", factoryExport: "sqliteDatabaseDriverFactory" },
|
|
1122
|
+
postgres: { packageName: "@holo-js/db-postgres", factoryExport: "postgresDatabaseDriverFactory" },
|
|
1123
|
+
mysql: { packageName: "@holo-js/db-mysql", factoryExport: "mysqlDatabaseDriverFactory" }
|
|
1124
|
+
};
|
|
1125
|
+
const drivers = new Set(Object.values(loadedConfig.database.connections).map((connection) => {
|
|
1126
|
+
if (typeof connection === "string") {
|
|
1127
|
+
if (connection.startsWith("postgres://") || connection.startsWith("postgresql://")) return "postgres";
|
|
1128
|
+
if (connection.startsWith("mysql://") || connection.startsWith("mysql2://")) return "mysql";
|
|
1129
|
+
return "sqlite";
|
|
1130
|
+
}
|
|
1131
|
+
return connection.driver ?? "sqlite";
|
|
1132
|
+
}));
|
|
1133
|
+
const factories = [];
|
|
1134
|
+
for (const driver of drivers) {
|
|
1135
|
+
const contribution = packageByDriver[driver];
|
|
1136
|
+
const { packageName, factoryExport } = contribution;
|
|
1137
|
+
const module = await portableRuntimeModuleInternals.importOptionalModule(packageName, { projectRoot });
|
|
1138
|
+
if (!module) throw new Error(`[@holo-js/core] Database driver "${driver}" requires ${packageName} to be installed.`);
|
|
1139
|
+
const factory = module[factoryExport];
|
|
1140
|
+
if (!factory) throw new Error(`[@holo-js/core] Database driver package ${packageName} does not export ${factoryExport}.`);
|
|
1141
|
+
factories.push(factory);
|
|
1142
|
+
}
|
|
1143
|
+
return factories;
|
|
1144
|
+
}
|
|
1145
|
+
async function loadQueueDbModule(projectRoot) {
|
|
1146
|
+
return createOptionalFeatureModuleLoader(
|
|
1147
|
+
importOptionalFeature,
|
|
1148
|
+
"@holo-js/queue-db",
|
|
1149
|
+
"[@holo-js/core] Database queues require @holo-js/queue-db to be installed."
|
|
1150
|
+
)(false, { projectRoot });
|
|
1151
|
+
}
|
|
1152
|
+
async function loadQueueRedisModule(projectRoot) {
|
|
1153
|
+
return createOptionalFeatureModuleLoader(
|
|
1154
|
+
importOptionalFeature,
|
|
1155
|
+
"@holo-js/queue-redis",
|
|
1156
|
+
"[@holo-js/core] Redis queues require @holo-js/queue-redis to be installed."
|
|
1157
|
+
)(false, { projectRoot });
|
|
1158
|
+
}
|
|
1159
|
+
async function loadCacheModule(required = false, projectRoot) {
|
|
1160
|
+
const loader = createOptionalFeatureModuleLoader(
|
|
1161
|
+
importOptionalFeature,
|
|
1162
|
+
"@holo-js/cache",
|
|
1163
|
+
"[@holo-js/core] Cache support requires @holo-js/cache to be installed."
|
|
1164
|
+
);
|
|
1165
|
+
return required ? loader(true, { projectRoot }) : loader(false, { projectRoot });
|
|
779
1166
|
}
|
|
780
|
-
async function
|
|
781
|
-
const
|
|
782
|
-
|
|
783
|
-
|
|
1167
|
+
async function loadConfiguredCacheDrivers(projectRoot, cacheConfig) {
|
|
1168
|
+
const packageByDriver = {
|
|
1169
|
+
database: "@holo-js/cache-db",
|
|
1170
|
+
redis: "@holo-js/cache-redis"
|
|
1171
|
+
};
|
|
1172
|
+
const drivers = new Set(Object.values(cacheConfig.drivers).map((driver) => driver.driver));
|
|
1173
|
+
for (const driver of drivers) {
|
|
1174
|
+
if (driver !== "database" && driver !== "redis") continue;
|
|
1175
|
+
const packageName = packageByDriver[driver];
|
|
1176
|
+
const module = await portableRuntimeModuleInternals.importOptionalModule(packageName, { projectRoot });
|
|
1177
|
+
if (!module) throw new Error(`[@holo-js/core] Cache driver "${driver}" requires ${packageName} to be installed.`);
|
|
784
1178
|
}
|
|
785
|
-
return workosModule;
|
|
786
1179
|
}
|
|
787
|
-
|
|
788
|
-
const
|
|
789
|
-
if (
|
|
790
|
-
|
|
1180
|
+
function resetCacheRuntimeGlobalsFallback() {
|
|
1181
|
+
const runtime = globalThis;
|
|
1182
|
+
if (runtime.__holoCacheRuntime__) {
|
|
1183
|
+
runtime.__holoCacheRuntime__.bindings = void 0;
|
|
1184
|
+
}
|
|
1185
|
+
if (runtime.__holoCacheQueryBridge__) {
|
|
1186
|
+
runtime.__holoCacheQueryBridge__.dependencyIndex = void 0;
|
|
1187
|
+
}
|
|
1188
|
+
if (runtime.__holoDbQueryCacheBridge__) {
|
|
1189
|
+
runtime.__holoDbQueryCacheBridge__.bridge = void 0;
|
|
791
1190
|
}
|
|
792
|
-
return clerkModule;
|
|
793
1191
|
}
|
|
1192
|
+
var loadEventsModule = createOptionalFeatureModuleLoader(
|
|
1193
|
+
importOptionalFeature,
|
|
1194
|
+
"@holo-js/events",
|
|
1195
|
+
"[@holo-js/core] Events support requires @holo-js/events to be installed."
|
|
1196
|
+
);
|
|
1197
|
+
var loadSessionModule = createOptionalFeatureModuleLoader(
|
|
1198
|
+
importOptionalFeature,
|
|
1199
|
+
"@holo-js/session",
|
|
1200
|
+
"[@holo-js/core] Session support requires @holo-js/session to be installed."
|
|
1201
|
+
);
|
|
1202
|
+
var loadSecurityModule = createOptionalFeatureModuleLoader(
|
|
1203
|
+
importOptionalFeature,
|
|
1204
|
+
"@holo-js/security",
|
|
1205
|
+
"[@holo-js/core] Security support requires @holo-js/security to be installed."
|
|
1206
|
+
);
|
|
1207
|
+
var loadSecurityRedisAdapterModule = createOptionalFeatureModuleLoader(
|
|
1208
|
+
importOptionalFeature,
|
|
1209
|
+
"@holo-js/security/drivers/redis-adapter",
|
|
1210
|
+
"[@holo-js/core] Redis-backed security rate limits require @holo-js/security/drivers/redis-adapter to be installed."
|
|
1211
|
+
);
|
|
1212
|
+
var loadSessionRedisAdapterModule = createOptionalFeatureModuleLoader(
|
|
1213
|
+
importOptionalFeature,
|
|
1214
|
+
"@holo-js/session/drivers/redis-adapter",
|
|
1215
|
+
"[@holo-js/core] Redis-backed session stores require @holo-js/session/drivers/redis-adapter to be installed."
|
|
1216
|
+
);
|
|
1217
|
+
var loadNotificationsModule = createOptionalFeatureModuleLoader(
|
|
1218
|
+
importOptionalFeature,
|
|
1219
|
+
"@holo-js/notifications",
|
|
1220
|
+
"[@holo-js/core] Notifications support requires @holo-js/notifications to be installed."
|
|
1221
|
+
);
|
|
1222
|
+
async function loadBroadcastModule(required = false, projectRoot) {
|
|
1223
|
+
const loader = createOptionalFeatureModuleLoader(
|
|
1224
|
+
importOptionalFeature,
|
|
1225
|
+
"@holo-js/broadcast",
|
|
1226
|
+
"[@holo-js/core] Broadcast support requires @holo-js/broadcast to be installed."
|
|
1227
|
+
);
|
|
1228
|
+
return required ? loader(true, { projectRoot }) : loader(false, { projectRoot });
|
|
1229
|
+
}
|
|
1230
|
+
var loadMailModule = createOptionalFeatureModuleLoader(
|
|
1231
|
+
importOptionalFeature,
|
|
1232
|
+
"@holo-js/mail",
|
|
1233
|
+
"[@holo-js/core] Mail support requires @holo-js/mail to be installed."
|
|
1234
|
+
);
|
|
1235
|
+
var loadAuthModule = createOptionalFeatureModuleLoader(
|
|
1236
|
+
importOptionalFeature,
|
|
1237
|
+
"@holo-js/auth",
|
|
1238
|
+
"[@holo-js/core] Auth support requires @holo-js/auth to be installed."
|
|
1239
|
+
);
|
|
1240
|
+
var loadAuthorizationModule = createOptionalFeatureModuleLoader(
|
|
1241
|
+
importOptionalFeature,
|
|
1242
|
+
"@holo-js/authorization",
|
|
1243
|
+
"[@holo-js/core] Authorization support requires @holo-js/authorization to be installed."
|
|
1244
|
+
);
|
|
1245
|
+
var loadSocialModule = createOptionalFeatureModuleLoader(
|
|
1246
|
+
importOptionalFeature,
|
|
1247
|
+
"@holo-js/auth-social",
|
|
1248
|
+
"[@holo-js/core] Social auth config requires @holo-js/auth-social to be installed."
|
|
1249
|
+
);
|
|
1250
|
+
var loadWorkosModule = createOptionalFeatureModuleLoader(
|
|
1251
|
+
importOptionalFeature,
|
|
1252
|
+
"@holo-js/auth-workos",
|
|
1253
|
+
"[@holo-js/core] WorkOS auth config requires @holo-js/auth-workos to be installed."
|
|
1254
|
+
);
|
|
1255
|
+
var loadClerkModule = createOptionalFeatureModuleLoader(
|
|
1256
|
+
importOptionalFeature,
|
|
1257
|
+
"@holo-js/auth-clerk",
|
|
1258
|
+
"[@holo-js/core] Clerk auth config requires @holo-js/auth-clerk to be installed."
|
|
1259
|
+
);
|
|
794
1260
|
function resolveQueueJobExport(queueModule, moduleValue) {
|
|
795
1261
|
const exports = moduleValue;
|
|
796
1262
|
if (queueModule.isQueueJobDefinition(exports.default)) {
|
|
@@ -831,74 +1297,7 @@ function resolveListenerExport(eventsModule, moduleValue) {
|
|
|
831
1297
|
return Object.values(exports).find((value) => hasListenerDefinitionMarker(value) || eventsModule.isListenerDefinition(value));
|
|
832
1298
|
}
|
|
833
1299
|
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
|
-
};
|
|
1300
|
+
return value.startsWith(".") || !value.startsWith("/") ? resolve5(projectRoot, value) : value;
|
|
902
1301
|
}
|
|
903
1302
|
function getEntityAttributes(value) {
|
|
904
1303
|
if (value && typeof value === "object") {
|
|
@@ -942,20 +1341,20 @@ async function createCoreManagedSessionStores(projectRoot, loadedConfig, session
|
|
|
942
1341
|
const connectionName = config.connection === "default" && !(config.connection in loadedConfig.database.connections) ? loadedConfig.database.defaultConnection : config.connection;
|
|
943
1342
|
stores[name] = sessionModule.createDatabaseSessionStore({
|
|
944
1343
|
async read(sessionId) {
|
|
945
|
-
const row = await
|
|
1344
|
+
const row = await DB2.table(config.table, connectionName).where("id", sessionId).whereNull("invalidated_at").first();
|
|
946
1345
|
return row ? normalizeSessionRecordFromRow(row) : null;
|
|
947
1346
|
},
|
|
948
1347
|
async write(record) {
|
|
949
1348
|
const normalized = serializeSessionRecordForRow(record);
|
|
950
|
-
const existing = await
|
|
1349
|
+
const existing = await DB2.table(config.table, connectionName).find(String(normalized.id));
|
|
951
1350
|
if (existing) {
|
|
952
|
-
await
|
|
1351
|
+
await DB2.table(config.table, connectionName).where("id", normalized.id).update(normalized);
|
|
953
1352
|
return;
|
|
954
1353
|
}
|
|
955
|
-
await
|
|
1354
|
+
await DB2.table(config.table, connectionName).insert(normalized);
|
|
956
1355
|
},
|
|
957
1356
|
async delete(sessionId) {
|
|
958
|
-
await
|
|
1357
|
+
await DB2.table(config.table, connectionName).where("id", sessionId).delete();
|
|
959
1358
|
}
|
|
960
1359
|
});
|
|
961
1360
|
continue;
|
|
@@ -1000,59 +1399,6 @@ async function createCoreManagedSessionStores(projectRoot, loadedConfig, session
|
|
|
1000
1399
|
async function createCoreSessionStores(projectRoot, loadedConfig, sessionModule) {
|
|
1001
1400
|
return (await createCoreManagedSessionStores(projectRoot, loadedConfig, sessionModule)).stores;
|
|
1002
1401
|
}
|
|
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
1402
|
var AUTH_EMAIL_VERIFICATION_NOTIFICATION_PATHS = [
|
|
1057
1403
|
"server/notifications/auth/email-verification.ts",
|
|
1058
1404
|
"server/notifications/auth/email-verification.mts",
|
|
@@ -1073,7 +1419,7 @@ function resolveExistingProjectFile(projectRoot, candidates) {
|
|
|
1073
1419
|
if (!projectRoot) {
|
|
1074
1420
|
return void 0;
|
|
1075
1421
|
}
|
|
1076
|
-
return candidates.find((candidate) =>
|
|
1422
|
+
return candidates.find((candidate) => existsSync2(resolve5(projectRoot, candidate)));
|
|
1077
1423
|
}
|
|
1078
1424
|
function resolveAuthNotification(module, exportName, filePath) {
|
|
1079
1425
|
const notification = module[exportName] ?? module.notification ?? module.default;
|
|
@@ -1232,219 +1578,92 @@ function createCoreNotificationBroadcaster(broadcastModule) {
|
|
|
1232
1578
|
event,
|
|
1233
1579
|
channels,
|
|
1234
1580
|
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
|
-
}
|
|
1581
|
+
...typeof context.notificationType === "string" && context.notificationType.trim() ? { type: context.notificationType.trim() } : {},
|
|
1582
|
+
data: message.data ?? null
|
|
1583
|
+
})
|
|
1418
1584
|
});
|
|
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
|
-
|
|
1585
|
+
}
|
|
1586
|
+
});
|
|
1587
|
+
}
|
|
1588
|
+
function createCoreBroadcastPublisher(loadedConfig) {
|
|
1589
|
+
const connectionHosts = /* @__PURE__ */ new Set(["holo", "pusher"]);
|
|
1590
|
+
const publish = async (input, context) => {
|
|
1591
|
+
const connection = loadedConfig.connections[input.connection];
|
|
1592
|
+
if (!connection) {
|
|
1593
|
+
throw new Error(`[@holo-js/core] Broadcast connection "${input.connection}" is not configured.`);
|
|
1594
|
+
}
|
|
1595
|
+
if (!("appId" in connection) || !("key" in connection) || !("secret" in connection)) {
|
|
1596
|
+
throw new Error(`[@holo-js/core] Broadcast connection "${input.connection}" cannot be published automatically.`);
|
|
1597
|
+
}
|
|
1598
|
+
if (!connectionHosts.has(connection.driver)) {
|
|
1599
|
+
throw new Error(`[@holo-js/core] Broadcast connection "${input.connection}" cannot be published automatically.`);
|
|
1600
|
+
}
|
|
1601
|
+
const options = connection.options;
|
|
1602
|
+
const protocol = options.scheme === "http" ? "http:" : "https:";
|
|
1603
|
+
const url = new URL(`/apps/${encodeURIComponent(connection.appId)}/events`, `${protocol}//${options.host}`);
|
|
1604
|
+
if (typeof options.port === "number") {
|
|
1605
|
+
url.port = String(options.port);
|
|
1606
|
+
}
|
|
1607
|
+
const body = JSON.stringify({
|
|
1608
|
+
name: input.event,
|
|
1609
|
+
channels: input.channels,
|
|
1610
|
+
data: JSON.stringify(input.payload),
|
|
1611
|
+
/* v8 ignore next -- tests do not pass socketId through the publish binding */
|
|
1612
|
+
...typeof input.socketId === "undefined" ? {} : { socket_id: input.socketId }
|
|
1613
|
+
});
|
|
1614
|
+
const bodyMd5 = createHash("md5").update(body).digest("hex");
|
|
1615
|
+
url.searchParams.set("auth_key", connection.key);
|
|
1616
|
+
url.searchParams.set("auth_timestamp", String(Math.floor(Date.now() / 1e3)));
|
|
1617
|
+
url.searchParams.set("auth_version", "1.0");
|
|
1618
|
+
url.searchParams.set("body_md5", bodyMd5);
|
|
1619
|
+
url.searchParams.set(
|
|
1620
|
+
"auth_signature",
|
|
1621
|
+
createHmac("sha256", connection.secret).update(
|
|
1622
|
+
[
|
|
1623
|
+
"POST",
|
|
1624
|
+
url.pathname,
|
|
1625
|
+
[...url.searchParams.entries()].filter(([key]) => key !== "auth_signature").sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join("&")
|
|
1626
|
+
].join("\n")
|
|
1627
|
+
).digest("hex")
|
|
1628
|
+
);
|
|
1629
|
+
const controller = new AbortController();
|
|
1630
|
+
const timeout = setTimeout(() => controller.abort(), BROADCAST_PUBLISH_TIMEOUT_MS);
|
|
1631
|
+
let response;
|
|
1632
|
+
try {
|
|
1633
|
+
response = await fetch(url, {
|
|
1634
|
+
method: "POST",
|
|
1635
|
+
headers: {
|
|
1636
|
+
"content-type": "application/json"
|
|
1637
|
+
},
|
|
1638
|
+
body,
|
|
1639
|
+
signal: controller.signal
|
|
1445
1640
|
});
|
|
1641
|
+
} catch (error) {
|
|
1642
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
1643
|
+
throw new Error(`[@holo-js/core] Broadcast publish request timed out after ${BROADCAST_PUBLISH_TIMEOUT_MS}ms.`);
|
|
1644
|
+
}
|
|
1645
|
+
throw error;
|
|
1646
|
+
} finally {
|
|
1647
|
+
clearTimeout(timeout);
|
|
1648
|
+
}
|
|
1649
|
+
if (!response.ok) {
|
|
1650
|
+
throw new Error(`[@holo-js/core] Broadcast publish request failed with status ${response.status}.`);
|
|
1446
1651
|
}
|
|
1652
|
+
const result = await response.json();
|
|
1653
|
+
return {
|
|
1654
|
+
connection: input.connection,
|
|
1655
|
+
driver: connection.driver,
|
|
1656
|
+
queued: context.queued,
|
|
1657
|
+
publishedChannels: Array.isArray(result.deliveredChannels) ? Object.freeze(result.deliveredChannels.map((value) => String(value))) : Object.freeze([...input.channels])
|
|
1658
|
+
};
|
|
1659
|
+
};
|
|
1660
|
+
Object.defineProperty(publish, CORE_BROADCAST_PUBLISHER_MARKER, {
|
|
1661
|
+
value: true
|
|
1447
1662
|
});
|
|
1663
|
+
return publish;
|
|
1664
|
+
}
|
|
1665
|
+
function isCoreBroadcastPublisher(value) {
|
|
1666
|
+
return typeof value === "function" && CORE_BROADCAST_PUBLISHER_MARKER in value;
|
|
1448
1667
|
}
|
|
1449
1668
|
async function loadConfiguredSocialProviders(projectRootOrLoadedConfig, maybeLoadedConfig) {
|
|
1450
1669
|
const projectRoot = typeof projectRootOrLoadedConfig === "string" ? projectRootOrLoadedConfig : (
|
|
@@ -1471,97 +1690,6 @@ async function loadConfiguredSocialProviders(projectRootOrLoadedConfig, maybeLoa
|
|
|
1471
1690
|
}
|
|
1472
1691
|
return Object.freeze(providers);
|
|
1473
1692
|
}
|
|
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
1693
|
async function createCoreSocialBindings(projectRootOrLoadedConfig, loadedConfigOrSessionModule, maybeSessionModule) {
|
|
1566
1694
|
const projectRoot = typeof projectRootOrLoadedConfig === "string" ? projectRootOrLoadedConfig : (
|
|
1567
1695
|
/* turbopackIgnore: true */
|
|
@@ -1609,7 +1737,7 @@ async function createCoreSocialBindings(projectRootOrLoadedConfig, loadedConfigO
|
|
|
1609
1737
|
});
|
|
1610
1738
|
const identityStore = Object.freeze({
|
|
1611
1739
|
async findByProviderUserId(provider, providerUserId) {
|
|
1612
|
-
const row = await
|
|
1740
|
+
const row = await DB2.table("auth_identities").where("provider", provider).where("provider_user_id", providerUserId).first();
|
|
1613
1741
|
if (!row) {
|
|
1614
1742
|
return null;
|
|
1615
1743
|
}
|
|
@@ -1631,7 +1759,7 @@ async function createCoreSocialBindings(projectRootOrLoadedConfig, loadedConfigO
|
|
|
1631
1759
|
},
|
|
1632
1760
|
async save(record) {
|
|
1633
1761
|
const value = record;
|
|
1634
|
-
const existing = await
|
|
1762
|
+
const existing = await DB2.table("auth_identities").where("provider", value.provider).where("provider_user_id", value.providerUserId).first();
|
|
1635
1763
|
const payload = {
|
|
1636
1764
|
user_id: String(value.userId),
|
|
1637
1765
|
provider: value.provider,
|
|
@@ -1646,10 +1774,10 @@ async function createCoreSocialBindings(projectRootOrLoadedConfig, loadedConfigO
|
|
|
1646
1774
|
updated_at: value.updatedAt.toISOString()
|
|
1647
1775
|
};
|
|
1648
1776
|
if (existing && typeof existing.id !== "undefined") {
|
|
1649
|
-
await
|
|
1777
|
+
await DB2.table("auth_identities").where("id", existing.id).update(payload);
|
|
1650
1778
|
return;
|
|
1651
1779
|
}
|
|
1652
|
-
await
|
|
1780
|
+
await DB2.table("auth_identities").insert(payload);
|
|
1653
1781
|
}
|
|
1654
1782
|
});
|
|
1655
1783
|
return Object.freeze({
|
|
@@ -1683,7 +1811,7 @@ function createCoreHostedIdentityStore(namespace) {
|
|
|
1683
1811
|
};
|
|
1684
1812
|
const findStoredIdentity = async (provider, providerUserId, authProvider = "users") => {
|
|
1685
1813
|
const providerValue = toHostedIdentityProviderValue(namespace, provider);
|
|
1686
|
-
const row = await
|
|
1814
|
+
const row = await DB2.table("auth_identities").where("provider", providerValue).where("provider_user_id", providerUserId).first();
|
|
1687
1815
|
return row ? normalizeRow(row, { provider, providerUserId, authProvider }) : null;
|
|
1688
1816
|
};
|
|
1689
1817
|
const toPayload = (record) => {
|
|
@@ -1707,7 +1835,7 @@ function createCoreHostedIdentityStore(namespace) {
|
|
|
1707
1835
|
},
|
|
1708
1836
|
async findByUserId(provider, authProvider, userId) {
|
|
1709
1837
|
const providerValue = toHostedIdentityProviderValue(namespace, provider);
|
|
1710
|
-
const row = await
|
|
1838
|
+
const row = await DB2.table("auth_identities").where("provider", providerValue).where("auth_provider", authProvider).where("user_id", String(userId)).first();
|
|
1711
1839
|
if (!row) {
|
|
1712
1840
|
return null;
|
|
1713
1841
|
}
|
|
@@ -1719,7 +1847,7 @@ function createCoreHostedIdentityStore(namespace) {
|
|
|
1719
1847
|
},
|
|
1720
1848
|
async claim(record) {
|
|
1721
1849
|
const value = record;
|
|
1722
|
-
await
|
|
1850
|
+
await DB2.table("auth_identities").insertOrIgnore(toPayload(value));
|
|
1723
1851
|
const claimed = await findStoredIdentity(value.provider, value.providerUserId, value.authProvider);
|
|
1724
1852
|
if (!claimed) {
|
|
1725
1853
|
throw new Error("[@holo-js/core] Claimed hosted identity could not be read back from auth_identities.");
|
|
@@ -1729,13 +1857,13 @@ function createCoreHostedIdentityStore(namespace) {
|
|
|
1729
1857
|
async save(record) {
|
|
1730
1858
|
const value = record;
|
|
1731
1859
|
const providerValue = toHostedIdentityProviderValue(namespace, value.provider);
|
|
1732
|
-
const existing = await
|
|
1860
|
+
const existing = await DB2.table("auth_identities").where("provider", providerValue).where("provider_user_id", value.providerUserId).first();
|
|
1733
1861
|
const payload = toPayload(value);
|
|
1734
1862
|
if (existing && typeof existing.id !== "undefined") {
|
|
1735
|
-
await
|
|
1863
|
+
await DB2.table("auth_identities").where("id", existing.id).update(payload);
|
|
1736
1864
|
return;
|
|
1737
1865
|
}
|
|
1738
|
-
await
|
|
1866
|
+
await DB2.table("auth_identities").insert(payload);
|
|
1739
1867
|
}
|
|
1740
1868
|
});
|
|
1741
1869
|
}
|
|
@@ -1743,55 +1871,55 @@ function createCoreAuthStores(loadedConfig) {
|
|
|
1743
1871
|
return Object.freeze({
|
|
1744
1872
|
tokens: Object.freeze({
|
|
1745
1873
|
async create(record) {
|
|
1746
|
-
await
|
|
1874
|
+
await DB2.table("personal_access_tokens").insert(serializeAccessTokenRecord(record));
|
|
1747
1875
|
},
|
|
1748
1876
|
async findById(id) {
|
|
1749
|
-
const row = await
|
|
1877
|
+
const row = await DB2.table("personal_access_tokens").find(id);
|
|
1750
1878
|
return row ? normalizeAccessTokenRecord(row) : null;
|
|
1751
1879
|
},
|
|
1752
1880
|
async listByUserId(provider, userId) {
|
|
1753
|
-
const rows = await
|
|
1881
|
+
const rows = await DB2.table("personal_access_tokens").where("provider", provider).where("user_id", String(userId)).get();
|
|
1754
1882
|
return Object.freeze(rows.map((row) => normalizeAccessTokenRecord(row)));
|
|
1755
1883
|
},
|
|
1756
1884
|
async update(record) {
|
|
1757
1885
|
const payload = serializeAccessTokenRecord(record);
|
|
1758
|
-
await
|
|
1886
|
+
await DB2.table("personal_access_tokens").where("id", String(payload.id)).update(payload);
|
|
1759
1887
|
},
|
|
1760
1888
|
async delete(id) {
|
|
1761
|
-
await
|
|
1889
|
+
await DB2.table("personal_access_tokens").where("id", id).delete();
|
|
1762
1890
|
},
|
|
1763
1891
|
async deleteByUserId(provider, userId) {
|
|
1764
|
-
const result = await
|
|
1892
|
+
const result = await DB2.table("personal_access_tokens").where("provider", provider).where("user_id", String(userId)).delete();
|
|
1765
1893
|
return result.affectedRows ?? 0;
|
|
1766
1894
|
}
|
|
1767
1895
|
}),
|
|
1768
1896
|
emailVerificationTokens: Object.freeze({
|
|
1769
1897
|
async create(record) {
|
|
1770
|
-
await
|
|
1898
|
+
await DB2.table("email_verification_tokens").insert(serializeEmailVerificationTokenRecord(record));
|
|
1771
1899
|
},
|
|
1772
1900
|
async findById(id) {
|
|
1773
|
-
const row = await
|
|
1901
|
+
const row = await DB2.table("email_verification_tokens").where("id", id).whereNull("used_at").first();
|
|
1774
1902
|
return row ? normalizeEmailVerificationTokenRecord(row) : null;
|
|
1775
1903
|
},
|
|
1776
1904
|
async delete(id) {
|
|
1777
|
-
await
|
|
1905
|
+
await DB2.table("email_verification_tokens").where("id", id).delete();
|
|
1778
1906
|
},
|
|
1779
1907
|
async deleteByUserId(provider, userId) {
|
|
1780
|
-
const result = await
|
|
1908
|
+
const result = await DB2.table("email_verification_tokens").where("provider", provider).where("user_id", String(userId)).delete();
|
|
1781
1909
|
return result.affectedRows ?? 0;
|
|
1782
1910
|
}
|
|
1783
1911
|
}),
|
|
1784
1912
|
passwordResetTokens: Object.freeze({
|
|
1785
1913
|
async create(record) {
|
|
1786
1914
|
const value = record;
|
|
1787
|
-
await
|
|
1915
|
+
await DB2.table(value.table ?? "password_reset_tokens").insert(serializePasswordResetTokenRecord(value));
|
|
1788
1916
|
},
|
|
1789
1917
|
async findById(id) {
|
|
1790
1918
|
const tables = Array.from(new Set(
|
|
1791
1919
|
Object.values(loadedConfig.auth.passwords).map((config) => config.table)
|
|
1792
1920
|
));
|
|
1793
1921
|
for (const table of tables) {
|
|
1794
|
-
const row = await
|
|
1922
|
+
const row = await DB2.table(table).where("id", id).whereNull("used_at").first();
|
|
1795
1923
|
if (row) {
|
|
1796
1924
|
return normalizePasswordResetTokenRecord({
|
|
1797
1925
|
...row,
|
|
@@ -1803,7 +1931,7 @@ function createCoreAuthStores(loadedConfig) {
|
|
|
1803
1931
|
},
|
|
1804
1932
|
async findLatestByEmail(provider, email, options) {
|
|
1805
1933
|
const table = options?.table ?? "password_reset_tokens";
|
|
1806
|
-
const row = await
|
|
1934
|
+
const row = await DB2.table(table).where("provider", provider).where("email", email).latest("created_at").first();
|
|
1807
1935
|
if (!row) {
|
|
1808
1936
|
return null;
|
|
1809
1937
|
}
|
|
@@ -1814,20 +1942,20 @@ function createCoreAuthStores(loadedConfig) {
|
|
|
1814
1942
|
},
|
|
1815
1943
|
async delete(id, options) {
|
|
1816
1944
|
const table = options?.table ?? "password_reset_tokens";
|
|
1817
|
-
await
|
|
1945
|
+
await DB2.table(table).where("id", id).delete();
|
|
1818
1946
|
},
|
|
1819
1947
|
async deleteByEmail(provider, email, options) {
|
|
1820
1948
|
const table = options?.table ?? "password_reset_tokens";
|
|
1821
|
-
const result = await
|
|
1949
|
+
const result = await DB2.table(table).where("provider", provider).where("email", email).delete();
|
|
1822
1950
|
return result.affectedRows ?? 0;
|
|
1823
1951
|
}
|
|
1824
1952
|
})
|
|
1825
1953
|
});
|
|
1826
1954
|
}
|
|
1827
1955
|
async function resolveAuthProviderRuntime(projectRoot, loadedConfig, modelName) {
|
|
1828
|
-
const modelsRoot =
|
|
1956
|
+
const modelsRoot = resolve5(projectRoot, loadedConfig.app.paths.models);
|
|
1829
1957
|
for (const extension of [".ts", ".mts", ".js", ".mjs", ".cts", ".cjs"]) {
|
|
1830
|
-
const candidate =
|
|
1958
|
+
const candidate = resolve5(modelsRoot, `${modelName}${extension}`);
|
|
1831
1959
|
try {
|
|
1832
1960
|
const moduleValue = await importRuntimeModule(projectRoot, candidate);
|
|
1833
1961
|
if ("default" in moduleValue) {
|
|
@@ -2053,7 +2181,7 @@ async function registerProjectQueueJobs(projectRoot, registry, queueModule) {
|
|
|
2053
2181
|
if (existing && !existing.sourcePath) {
|
|
2054
2182
|
continue;
|
|
2055
2183
|
}
|
|
2056
|
-
const moduleValue = await importRuntimeModule(projectRoot,
|
|
2184
|
+
const moduleValue = await importRuntimeModule(projectRoot, resolve5(projectRoot, entry.sourcePath));
|
|
2057
2185
|
const job = resolveQueueJobExport(queueModule, moduleValue);
|
|
2058
2186
|
if (!job) {
|
|
2059
2187
|
throw new Error(`Discovered job "${entry.sourcePath}" does not export a Holo job.`);
|
|
@@ -2118,7 +2246,7 @@ async function registerProjectAuthorizationDefinitions(projectRoot, registry, au
|
|
|
2118
2246
|
previousPolicies.set(entry.name, existing);
|
|
2119
2247
|
authorizationModule.authorizationInternals.unregisterPolicyDefinition(entry.name);
|
|
2120
2248
|
}
|
|
2121
|
-
const moduleValue = await importRuntimeModule(projectRoot,
|
|
2249
|
+
const moduleValue = await importRuntimeModule(projectRoot, resolve5(projectRoot, entry.sourcePath));
|
|
2122
2250
|
const policy = resolveAuthorizationDefinitionExport(
|
|
2123
2251
|
moduleValue,
|
|
2124
2252
|
entry.exportName,
|
|
@@ -2146,7 +2274,7 @@ async function registerProjectAuthorizationDefinitions(projectRoot, registry, au
|
|
|
2146
2274
|
previousAbilities.set(entry.name, existing);
|
|
2147
2275
|
authorizationModule.authorizationInternals.unregisterAbilityDefinition(entry.name);
|
|
2148
2276
|
}
|
|
2149
|
-
const moduleValue = await importRuntimeModule(projectRoot,
|
|
2277
|
+
const moduleValue = await importRuntimeModule(projectRoot, resolve5(projectRoot, entry.sourcePath));
|
|
2150
2278
|
const ability = resolveAuthorizationDefinitionExport(
|
|
2151
2279
|
moduleValue,
|
|
2152
2280
|
entry.exportName,
|
|
@@ -2214,7 +2342,7 @@ async function registerProjectEventsAndListeners(projectRoot, registry, eventsMo
|
|
|
2214
2342
|
if (existing && !existing.sourcePath) {
|
|
2215
2343
|
continue;
|
|
2216
2344
|
}
|
|
2217
|
-
const moduleValue = await importRuntimeModule(projectRoot,
|
|
2345
|
+
const moduleValue = await importRuntimeModule(projectRoot, resolve5(projectRoot, entry.sourcePath));
|
|
2218
2346
|
const event = resolveEventExport(moduleValue);
|
|
2219
2347
|
if (!event || !eventsModule.isEventDefinition(event)) {
|
|
2220
2348
|
throw new Error(`Discovered event "${entry.sourcePath}" does not export a Holo event.`);
|
|
@@ -2231,7 +2359,7 @@ async function registerProjectEventsAndListeners(projectRoot, registry, eventsMo
|
|
|
2231
2359
|
if (existing && !existing.sourcePath) {
|
|
2232
2360
|
continue;
|
|
2233
2361
|
}
|
|
2234
|
-
const moduleValue = await importRuntimeModule(projectRoot,
|
|
2362
|
+
const moduleValue = await importRuntimeModule(projectRoot, resolve5(projectRoot, entry.sourcePath));
|
|
2235
2363
|
const listener = resolveListenerExport(eventsModule, moduleValue);
|
|
2236
2364
|
if (!listener) {
|
|
2237
2365
|
throw new Error(`Discovered listener "${entry.sourcePath}" does not export a Holo listener.`);
|
|
@@ -2277,13 +2405,27 @@ function unregisterProjectEventsAndListeners(eventsModule, eventNames, listenerI
|
|
|
2277
2405
|
}
|
|
2278
2406
|
}
|
|
2279
2407
|
async function reconfigureOptionalHoloSubsystems(projectRoot, loadedConfig, options = {}) {
|
|
2408
|
+
const pluginDefinitions = await loadConfiguredHoloPluginDefinitions(projectRoot, resolveLoadedPluginNames(loadedConfig));
|
|
2280
2409
|
const cacheConfigured = hasLoadedConfigFile(loadedConfig, "cache");
|
|
2281
2410
|
const cacheModule = await loadCacheModule(cacheConfigured, projectRoot);
|
|
2282
|
-
|
|
2411
|
+
const cacheConfig = loadedConfig.cache;
|
|
2412
|
+
if (cacheModule && cacheConfig) {
|
|
2413
|
+
await loadConfiguredCacheDrivers(projectRoot, cacheConfig);
|
|
2414
|
+
const configuredPluginDrivers = Object.entries(cacheConfig.drivers).flatMap(([name, driver]) => {
|
|
2415
|
+
if (driver.driver === "memory" || driver.driver === "file" || driver.driver === "redis" || driver.driver === "database") return [];
|
|
2416
|
+
return [{ ...driver, name, driver: driver.driver }];
|
|
2417
|
+
});
|
|
2418
|
+
const loadedPluginDrivers = await cacheModule.loadConfiguredCachePluginDriverContracts(
|
|
2419
|
+
projectRoot,
|
|
2420
|
+
resolveLoadedPluginNames(loadedConfig),
|
|
2421
|
+
configuredPluginDrivers
|
|
2422
|
+
);
|
|
2423
|
+
const cachePluginDrivers = new Map(loadedPluginDrivers.map((driver) => [driver.name, driver]));
|
|
2283
2424
|
cacheModule.configureCacheRuntime({
|
|
2284
|
-
config:
|
|
2425
|
+
config: cacheConfig,
|
|
2285
2426
|
databaseConfig: loadedConfig.database,
|
|
2286
|
-
redisConfig: loadedConfig.redis
|
|
2427
|
+
redisConfig: loadedConfig.redis,
|
|
2428
|
+
...cachePluginDrivers.size > 0 ? { drivers: cachePluginDrivers } : {}
|
|
2287
2429
|
});
|
|
2288
2430
|
}
|
|
2289
2431
|
const queueConfigured = hasLoadedConfigFile(loadedConfig, "queue");
|
|
@@ -2291,14 +2433,30 @@ async function reconfigureOptionalHoloSubsystems(projectRoot, loadedConfig, opti
|
|
|
2291
2433
|
if (queueModule) {
|
|
2292
2434
|
const queueUsesExplicitDatabaseFeatures = queueConfigured && (queueConfigUsesDatabaseDriver(loadedConfig) || queueConfigUsesDatabaseBackedFailedStore(loadedConfig));
|
|
2293
2435
|
const queueUsesImplicitDefaultFailedStore = !queueConfigured && queueConfigUsesDatabaseBackedFailedStore(loadedConfig);
|
|
2294
|
-
const queueDbModule = queueUsesExplicitDatabaseFeatures || queueUsesImplicitDefaultFailedStore ? await loadQueueDbModule() : void 0;
|
|
2436
|
+
const queueDbModule = queueUsesExplicitDatabaseFeatures || queueUsesImplicitDefaultFailedStore ? await loadQueueDbModule(projectRoot) : void 0;
|
|
2437
|
+
const queueUsesRedis = queueConfigUsesRedisDriver(loadedConfig);
|
|
2438
|
+
const queueRedisModule = queueUsesRedis ? await loadQueueRedisModule(projectRoot) : void 0;
|
|
2295
2439
|
if (queueUsesExplicitDatabaseFeatures && !queueDbModule) {
|
|
2296
2440
|
throw new Error("[@holo-js/core] Database-backed queue features require @holo-js/queue-db to be installed.");
|
|
2297
2441
|
}
|
|
2442
|
+
if (queueUsesRedis && !queueRedisModule) {
|
|
2443
|
+
throw new Error("[@holo-js/core] Redis-backed queue connections require @holo-js/queue-redis to be installed.");
|
|
2444
|
+
}
|
|
2445
|
+
const queuePluginDriverFactories = await queueModule.loadQueuePluginDriverFactories(
|
|
2446
|
+
projectRoot,
|
|
2447
|
+
resolveLoadedPluginNames(loadedConfig)
|
|
2448
|
+
);
|
|
2449
|
+
const queueDbRuntimeOptions = queueDbModule?.createQueueDbRuntimeOptions() ?? {};
|
|
2450
|
+
const queueDriverFactories = mergeQueueRuntimeDriverFactories(
|
|
2451
|
+
queuePluginDriverFactories,
|
|
2452
|
+
queueDbRuntimeOptions.driverFactories,
|
|
2453
|
+
queueRedisModule ? [queueRedisModule.redisQueueDriverFactory] : void 0
|
|
2454
|
+
);
|
|
2298
2455
|
queueModule.configureQueueRuntime({
|
|
2299
2456
|
config: loadedConfig.queue,
|
|
2300
2457
|
redisConfig: loadedConfig.redis,
|
|
2301
|
-
...
|
|
2458
|
+
...queueDbRuntimeOptions,
|
|
2459
|
+
...queueDriverFactories.length > 0 ? { driverFactories: queueDriverFactories } : {}
|
|
2302
2460
|
});
|
|
2303
2461
|
}
|
|
2304
2462
|
const storageConfigured = hasLoadedConfigFile(loadedConfig, "storage");
|
|
@@ -2306,7 +2464,7 @@ async function reconfigureOptionalHoloSubsystems(projectRoot, loadedConfig, opti
|
|
|
2306
2464
|
if (!storageInstalled && storageConfigured) {
|
|
2307
2465
|
throw new Error("[@holo-js/core] Storage support requires @holo-js/storage to be installed.");
|
|
2308
2466
|
}
|
|
2309
|
-
if (storageInstalled) {
|
|
2467
|
+
if (storageInstalled && loadedConfig.storage) {
|
|
2310
2468
|
await configurePlainNodeStorageRuntime(projectRoot, loadedConfig);
|
|
2311
2469
|
}
|
|
2312
2470
|
const mailConfigured = hasLoadedConfigFile(loadedConfig, "mail");
|
|
@@ -2316,7 +2474,9 @@ async function reconfigureOptionalHoloSubsystems(projectRoot, loadedConfig, opti
|
|
|
2316
2474
|
mailModule.configureMailRuntime({
|
|
2317
2475
|
...existingMailBindings,
|
|
2318
2476
|
config: loadedConfig.mail,
|
|
2319
|
-
|
|
2477
|
+
projectRoot,
|
|
2478
|
+
plugins: loadedConfig.app.plugins,
|
|
2479
|
+
...options.renderView ?? getHoloRenderingRuntime().renderView ? { renderView: options.renderView ?? getHoloRenderingRuntime().renderView } : {}
|
|
2320
2480
|
});
|
|
2321
2481
|
}
|
|
2322
2482
|
const broadcastConfigured = hasLoadedConfigFile(loadedConfig, "broadcast");
|
|
@@ -2326,6 +2486,8 @@ async function reconfigureOptionalHoloSubsystems(projectRoot, loadedConfig, opti
|
|
|
2326
2486
|
broadcastModule.configureBroadcastRuntime({
|
|
2327
2487
|
...existingBroadcastBindings,
|
|
2328
2488
|
config: loadedConfig.broadcast,
|
|
2489
|
+
projectRoot,
|
|
2490
|
+
plugins: loadedConfig.app.plugins,
|
|
2329
2491
|
...!existingBroadcastBindings.publish || isCoreBroadcastPublisher(existingBroadcastBindings.publish) ? {
|
|
2330
2492
|
publish: createCoreBroadcastPublisher(loadedConfig.broadcast)
|
|
2331
2493
|
} : {}
|
|
@@ -2338,6 +2500,16 @@ async function reconfigureOptionalHoloSubsystems(projectRoot, loadedConfig, opti
|
|
|
2338
2500
|
notificationsModule.configureNotificationsRuntime({
|
|
2339
2501
|
...existingNotificationsBindings,
|
|
2340
2502
|
config: loadedConfig.notifications,
|
|
2503
|
+
deferAfterCommit(callback) {
|
|
2504
|
+
const connection = connectionAsyncContext.getActive()?.connection;
|
|
2505
|
+
if (!connection || connection.getScope().kind === "root") {
|
|
2506
|
+
return false;
|
|
2507
|
+
}
|
|
2508
|
+
connection.afterCommit(callback);
|
|
2509
|
+
return true;
|
|
2510
|
+
},
|
|
2511
|
+
projectRoot,
|
|
2512
|
+
plugins: loadedConfig.app.plugins,
|
|
2341
2513
|
store: existingNotificationsBindings.store ?? createCoreNotificationStore(loadedConfig),
|
|
2342
2514
|
...!existingNotificationsBindings.mailer && mailModule ? { mailer: createCoreNotificationMailSender(mailModule) } : {},
|
|
2343
2515
|
...!existingNotificationsBindings.broadcaster && broadcastModule ? { broadcaster: createCoreNotificationBroadcaster(broadcastModule) } : {}
|
|
@@ -2509,6 +2681,9 @@ async function reconfigureOptionalHoloSubsystems(projectRoot, loadedConfig, opti
|
|
|
2509
2681
|
} else if (authorizationModule) {
|
|
2510
2682
|
authorizationModule.authorizationInternals.resetAuthorizationAuthIntegration();
|
|
2511
2683
|
}
|
|
2684
|
+
for (const bootModule of await loadConfiguredHoloPluginBootModules(projectRoot, pluginDefinitions)) {
|
|
2685
|
+
await bootConfiguredHoloPluginModule(projectRoot, loadedConfig, bootModule);
|
|
2686
|
+
}
|
|
2512
2687
|
return Object.freeze({
|
|
2513
2688
|
/* v8 ignore next -- only toggles shape when queue support is absent */
|
|
2514
2689
|
...queueModule ? { queueModule } : {},
|
|
@@ -2518,6 +2693,7 @@ async function reconfigureOptionalHoloSubsystems(projectRoot, loadedConfig, opti
|
|
|
2518
2693
|
});
|
|
2519
2694
|
}
|
|
2520
2695
|
async function resetOptionalHoloSubsystems() {
|
|
2696
|
+
resetBootedHoloPluginModules();
|
|
2521
2697
|
const projectRoot = getRuntimeState().current?.projectRoot ?? getRuntimeState().pendingProjectRoot;
|
|
2522
2698
|
await resetOptionalStorageRuntime();
|
|
2523
2699
|
const cacheModule = await loadCacheModule(false, projectRoot);
|
|
@@ -2527,7 +2703,10 @@ async function resetOptionalHoloSubsystems() {
|
|
|
2527
2703
|
resetCacheRuntimeGlobalsFallback();
|
|
2528
2704
|
}
|
|
2529
2705
|
const queueModule = await loadQueueModule();
|
|
2530
|
-
|
|
2706
|
+
if (queueModule) {
|
|
2707
|
+
await queueModule.shutdownQueueRuntime();
|
|
2708
|
+
queueModule.resetQueueRuntime?.();
|
|
2709
|
+
}
|
|
2531
2710
|
const mailModule = await loadMailModule();
|
|
2532
2711
|
mailModule?.resetMailRuntime();
|
|
2533
2712
|
const notificationsModule = await loadNotificationsModule();
|
|
@@ -2567,15 +2746,32 @@ async function resetOptionalHoloSubsystems() {
|
|
|
2567
2746
|
securityModule?.resetSecurityRuntime();
|
|
2568
2747
|
}
|
|
2569
2748
|
async function createHolo(projectRoot, options = {}) {
|
|
2749
|
+
await loadInstalledFeatureConfigContributions(projectRoot);
|
|
2570
2750
|
const loadedConfig = await loadConfigDirectory(projectRoot, {
|
|
2571
2751
|
envName: options.envName,
|
|
2572
2752
|
preferCache: options.preferCache,
|
|
2573
2753
|
processEnv: options.processEnv
|
|
2574
2754
|
});
|
|
2755
|
+
const fallbackQueueConfig = Object.freeze({
|
|
2756
|
+
default: "sync",
|
|
2757
|
+
failed: Object.freeze({
|
|
2758
|
+
driver: "database",
|
|
2759
|
+
connection: "default",
|
|
2760
|
+
table: "failed_jobs"
|
|
2761
|
+
}),
|
|
2762
|
+
connections: Object.freeze({
|
|
2763
|
+
sync: Object.freeze({
|
|
2764
|
+
driver: "sync",
|
|
2765
|
+
queue: "default"
|
|
2766
|
+
})
|
|
2767
|
+
})
|
|
2768
|
+
});
|
|
2769
|
+
const queueConfig = loadedConfig.queue ?? fallbackQueueConfig;
|
|
2575
2770
|
const runtimeConfig = {
|
|
2576
2771
|
db: loadedConfig.database,
|
|
2577
|
-
queue:
|
|
2772
|
+
queue: queueConfig
|
|
2578
2773
|
};
|
|
2774
|
+
const databaseDriverFactories = await loadConfiguredDatabaseDrivers(projectRoot, loadedConfig);
|
|
2579
2775
|
const manager = resolveRuntimeConnectionManagerOptions(runtimeConfig);
|
|
2580
2776
|
const registry = await loadGeneratedProjectRegistry(projectRoot);
|
|
2581
2777
|
const accessors = createConfigAccessors(loadedConfig.all);
|
|
@@ -2591,11 +2787,105 @@ async function createHolo(projectRoot, options = {}) {
|
|
|
2591
2787
|
let activeAuthRuntime;
|
|
2592
2788
|
let activeAuthContext;
|
|
2593
2789
|
let previousOptionalSubsystemBindings;
|
|
2594
|
-
const previousRenderView = options.renderView ?
|
|
2790
|
+
const previousRenderView = options.renderView ? getHoloRenderingRuntime().renderView : void 0;
|
|
2595
2791
|
const fallbackQueueRuntime = Object.freeze({
|
|
2596
|
-
config:
|
|
2792
|
+
config: queueConfig,
|
|
2597
2793
|
drivers: /* @__PURE__ */ new Map()
|
|
2598
2794
|
});
|
|
2795
|
+
const unregisterRuntimeContributions = () => {
|
|
2796
|
+
unregisterProjectEventsAndListeners(activeEventsModule, runtimeOwnedEventNames, runtimeOwnedListenerIds);
|
|
2797
|
+
runtimeOwnedEventNames.splice(0);
|
|
2798
|
+
runtimeOwnedListenerIds.splice(0);
|
|
2799
|
+
unregisterProjectAuthorizationDefinitions(activeAuthorizationModule, runtimeOwnedAuthorizationPolicyNames, runtimeOwnedAuthorizationAbilityNames);
|
|
2800
|
+
runtimeOwnedAuthorizationPolicyNames.splice(0);
|
|
2801
|
+
runtimeOwnedAuthorizationAbilityNames.splice(0);
|
|
2802
|
+
unregisterProjectQueueJobs(activeQueueModule, runtimeOwnedQueueJobNames);
|
|
2803
|
+
runtimeOwnedQueueJobNames.splice(0);
|
|
2804
|
+
activeAuthorizationModule = void 0;
|
|
2805
|
+
activeEventsModule = void 0;
|
|
2806
|
+
activeQueueModule = void 0;
|
|
2807
|
+
activeSessionRuntime = void 0;
|
|
2808
|
+
activeAuthRuntime = void 0;
|
|
2809
|
+
activeAuthContext = void 0;
|
|
2810
|
+
};
|
|
2811
|
+
const initializeRuntimeServices = async () => {
|
|
2812
|
+
if (!shouldBootRuntimeServices(options.processEnv)) return;
|
|
2813
|
+
const optionalSubsystems = await reconfigureOptionalHoloSubsystems(projectRoot, loadedConfig, {
|
|
2814
|
+
renderView: options.renderView,
|
|
2815
|
+
authRequest: options.authRequest,
|
|
2816
|
+
authorizationError: options.authorizationError
|
|
2817
|
+
});
|
|
2818
|
+
activeQueueModule = optionalSubsystems.queueModule;
|
|
2819
|
+
activeSessionRuntime = optionalSubsystems.session;
|
|
2820
|
+
activeAuthRuntime = optionalSubsystems.auth;
|
|
2821
|
+
activeAuthContext = optionalSubsystems.authContext;
|
|
2822
|
+
const optionalEventsModule = activeQueueModule ? await loadEventsModule() : void 0;
|
|
2823
|
+
if (activeQueueModule && optionalEventsModule) {
|
|
2824
|
+
await optionalEventsModule.ensureEventsQueueJobRegisteredAsync?.();
|
|
2825
|
+
}
|
|
2826
|
+
if (registryHasEvents(registry)) {
|
|
2827
|
+
const eventsModule = await loadEventsModule(true);
|
|
2828
|
+
if (!eventsModule) throw new Error("[@holo-js/core] Events support requires @holo-js/events to be installed.");
|
|
2829
|
+
activeEventsModule = eventsModule;
|
|
2830
|
+
const eventRegistration = await registerProjectEventsAndListeners(projectRoot, registry, eventsModule, activeQueueModule);
|
|
2831
|
+
runtimeOwnedEventNames.push(...eventRegistration.eventNames);
|
|
2832
|
+
runtimeOwnedListenerIds.push(...eventRegistration.listenerIds);
|
|
2833
|
+
}
|
|
2834
|
+
activeAuthorizationModule = await loadAuthorizationModule();
|
|
2835
|
+
const authorizationRegistration = await registerProjectAuthorizationDefinitions(projectRoot, registry, activeAuthorizationModule);
|
|
2836
|
+
runtimeOwnedAuthorizationPolicyNames.push(...authorizationRegistration.policyNames);
|
|
2837
|
+
runtimeOwnedAuthorizationAbilityNames.push(...authorizationRegistration.abilityNames);
|
|
2838
|
+
if (options.registerProjectQueueJobs !== false && registryHasJobs(registry)) {
|
|
2839
|
+
if (!activeQueueModule) throw new Error("[@holo-js/core] Project jobs require @holo-js/queue to be installed.");
|
|
2840
|
+
runtimeOwnedQueueJobNames.push(...await registerProjectQueueJobs(projectRoot, registry, activeQueueModule));
|
|
2841
|
+
}
|
|
2842
|
+
};
|
|
2843
|
+
const runtimeLifecycle = createRuntimeLifecycle([
|
|
2844
|
+
{
|
|
2845
|
+
name: "configuration",
|
|
2846
|
+
async initialize() {
|
|
2847
|
+
configureConfigRuntime(loadedConfig.all);
|
|
2848
|
+
await preloadGeneratedSchemaModule(projectRoot, registry);
|
|
2849
|
+
await preloadDiscoveredModelModules(projectRoot, registry);
|
|
2850
|
+
previousOptionalSubsystemBindings = snapshotOptionalSubsystemRuntimeBindings();
|
|
2851
|
+
if (options.renderView) configureHoloRenderingRuntime({ renderView: options.renderView });
|
|
2852
|
+
},
|
|
2853
|
+
dispose() {
|
|
2854
|
+
if (options.renderView) restoreHoloRenderingRuntime(previousRenderView);
|
|
2855
|
+
resetConfigRuntime();
|
|
2856
|
+
}
|
|
2857
|
+
},
|
|
2858
|
+
{
|
|
2859
|
+
name: "database",
|
|
2860
|
+
dependsOn: ["configuration"],
|
|
2861
|
+
async initialize() {
|
|
2862
|
+
for (const factory of databaseDriverFactories) registerDatabaseDriverFactory(factory);
|
|
2863
|
+
configureDB(manager);
|
|
2864
|
+
if (shouldBootRuntimeServices(options.processEnv)) await manager.initializeAll();
|
|
2865
|
+
},
|
|
2866
|
+
async dispose() {
|
|
2867
|
+
try {
|
|
2868
|
+
await manager.disconnectAll();
|
|
2869
|
+
} finally {
|
|
2870
|
+
resetDB();
|
|
2871
|
+
for (const factory of [...databaseDriverFactories].reverse()) unregisterDatabaseDriverFactory(factory);
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2874
|
+
},
|
|
2875
|
+
{
|
|
2876
|
+
name: "optional-subsystems",
|
|
2877
|
+
dependsOn: ["database"],
|
|
2878
|
+
initialize: initializeRuntimeServices,
|
|
2879
|
+
async dispose() {
|
|
2880
|
+
unregisterRuntimeContributions();
|
|
2881
|
+
try {
|
|
2882
|
+
await resetOptionalHoloSubsystems();
|
|
2883
|
+
} finally {
|
|
2884
|
+
if (previousOptionalSubsystemBindings) restoreOptionalSubsystemRuntimeBindings(previousOptionalSubsystemBindings);
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
}
|
|
2888
|
+
]);
|
|
2599
2889
|
const runtime = {
|
|
2600
2890
|
projectRoot,
|
|
2601
2891
|
loadedConfig,
|
|
@@ -2617,150 +2907,31 @@ async function createHolo(projectRoot, options = {}) {
|
|
|
2617
2907
|
setAuthRequestAccessors(authRequest) {
|
|
2618
2908
|
activeAuthContext?.setRequestAccessors?.(authRequest);
|
|
2619
2909
|
},
|
|
2620
|
-
|
|
2910
|
+
runWithAuthRequestAccessors(authRequest, callback) {
|
|
2621
2911
|
const runner = activeAuthContext?.runWithRequestAccessors;
|
|
2622
|
-
return runner ?
|
|
2912
|
+
return runner ? runner(authRequest, callback) : callback();
|
|
2623
2913
|
},
|
|
2624
2914
|
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
|
-
}
|
|
2915
|
+
if (runtime.initialized) throw new Error("Holo runtime is already initialized.");
|
|
2916
|
+
if (getRuntimeState().current) throw new Error("A Holo runtime is already initialized for this process.");
|
|
2917
|
+
await runtimeLifecycle.initialize({ projectRoot });
|
|
2918
|
+
runtime.initialized = true;
|
|
2919
|
+
getRuntimeState().current = runtime;
|
|
2719
2920
|
},
|
|
2720
2921
|
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
|
-
}
|
|
2922
|
+
runtime.initialized = false;
|
|
2923
|
+
if (getRuntimeState().current === runtime) getRuntimeState().current = void 0;
|
|
2924
|
+
await runtimeLifecycle.dispose({ projectRoot });
|
|
2754
2925
|
}
|
|
2755
2926
|
};
|
|
2756
2927
|
return runtime;
|
|
2757
2928
|
}
|
|
2758
2929
|
async function initializeHolo(projectRoot, options = {}) {
|
|
2759
2930
|
const state = getRuntimeState();
|
|
2760
|
-
const resolvedProjectRoot =
|
|
2931
|
+
const resolvedProjectRoot = resolve5(projectRoot);
|
|
2761
2932
|
const current = state.current;
|
|
2762
2933
|
if (current) {
|
|
2763
|
-
if (
|
|
2934
|
+
if (resolve5(current.projectRoot) !== resolvedProjectRoot) {
|
|
2764
2935
|
throw new Error(`A Holo runtime is already initialized for "${current.projectRoot}".`);
|
|
2765
2936
|
}
|
|
2766
2937
|
;
|
|
@@ -2768,7 +2939,7 @@ async function initializeHolo(projectRoot, options = {}) {
|
|
|
2768
2939
|
return current;
|
|
2769
2940
|
}
|
|
2770
2941
|
if (state.pending) {
|
|
2771
|
-
if (state.pendingProjectRoot &&
|
|
2942
|
+
if (state.pendingProjectRoot && resolve5(state.pendingProjectRoot) !== resolvedProjectRoot) {
|
|
2772
2943
|
throw new Error(`A Holo runtime is already initializing for "${state.pendingProjectRoot}".`);
|
|
2773
2944
|
}
|
|
2774
2945
|
return state.pending.then((runtime) => {
|
|
@@ -2801,7 +2972,7 @@ async function ensureHolo(projectRoot, options = {}) {
|
|
|
2801
2972
|
if (!current) {
|
|
2802
2973
|
return initializeHolo(projectRoot, options);
|
|
2803
2974
|
}
|
|
2804
|
-
if (
|
|
2975
|
+
if (resolve5(current.projectRoot) !== resolve5(projectRoot)) {
|
|
2805
2976
|
throw new Error(`A Holo runtime is already initialized for "${current.projectRoot}".`);
|
|
2806
2977
|
}
|
|
2807
2978
|
return current;
|