@open-mercato/shared 0.4.2-canary-c02407ff85
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/build.mjs +101 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +7 -0
- package/dist/lib/api/crud.js +47 -0
- package/dist/lib/api/crud.js.map +7 -0
- package/dist/lib/api/scoped.js +140 -0
- package/dist/lib/api/scoped.js.map +7 -0
- package/dist/lib/auth/jwt.js +34 -0
- package/dist/lib/auth/jwt.js.map +7 -0
- package/dist/lib/auth/server.js +157 -0
- package/dist/lib/auth/server.js.map +7 -0
- package/dist/lib/boolean.js +22 -0
- package/dist/lib/boolean.js.map +7 -0
- package/dist/lib/bootstrap/appResolver.js +43 -0
- package/dist/lib/bootstrap/appResolver.js.map +7 -0
- package/dist/lib/bootstrap/dynamicLoader.js +108 -0
- package/dist/lib/bootstrap/dynamicLoader.js.map +7 -0
- package/dist/lib/bootstrap/factory.js +59 -0
- package/dist/lib/bootstrap/factory.js.map +7 -0
- package/dist/lib/bootstrap/index.js +11 -0
- package/dist/lib/bootstrap/index.js.map +7 -0
- package/dist/lib/bootstrap/types.js +1 -0
- package/dist/lib/bootstrap/types.js.map +7 -0
- package/dist/lib/cache/segments.js +36 -0
- package/dist/lib/cache/segments.js.map +7 -0
- package/dist/lib/cli/progress.js +46 -0
- package/dist/lib/cli/progress.js.map +7 -0
- package/dist/lib/commands/command-bus.js +285 -0
- package/dist/lib/commands/command-bus.js.map +7 -0
- package/dist/lib/commands/customFieldSnapshots.js +66 -0
- package/dist/lib/commands/customFieldSnapshots.js.map +7 -0
- package/dist/lib/commands/helpers.js +98 -0
- package/dist/lib/commands/helpers.js.map +7 -0
- package/dist/lib/commands/index.js +8 -0
- package/dist/lib/commands/index.js.map +7 -0
- package/dist/lib/commands/operationMetadata.js +32 -0
- package/dist/lib/commands/operationMetadata.js.map +7 -0
- package/dist/lib/commands/registry.js +43 -0
- package/dist/lib/commands/registry.js.map +7 -0
- package/dist/lib/commands/scope.js +44 -0
- package/dist/lib/commands/scope.js.map +7 -0
- package/dist/lib/commands/types.js +8 -0
- package/dist/lib/commands/types.js.map +7 -0
- package/dist/lib/crud/cache-stats.js +98 -0
- package/dist/lib/crud/cache-stats.js.map +7 -0
- package/dist/lib/crud/cache.js +175 -0
- package/dist/lib/crud/cache.js.map +7 -0
- package/dist/lib/crud/custom-fields-client.js +52 -0
- package/dist/lib/crud/custom-fields-client.js.map +7 -0
- package/dist/lib/crud/custom-fields.js +467 -0
- package/dist/lib/crud/custom-fields.js.map +7 -0
- package/dist/lib/crud/errors.js +24 -0
- package/dist/lib/crud/errors.js.map +7 -0
- package/dist/lib/crud/exporters.js +154 -0
- package/dist/lib/crud/exporters.js.map +7 -0
- package/dist/lib/crud/factory.js +1311 -0
- package/dist/lib/crud/factory.js.map +7 -0
- package/dist/lib/crud/types.js +1 -0
- package/dist/lib/crud/types.js.map +7 -0
- package/dist/lib/custom-fields/normalize.js +36 -0
- package/dist/lib/custom-fields/normalize.js.map +7 -0
- package/dist/lib/data/engine.js +396 -0
- package/dist/lib/data/engine.js.map +7 -0
- package/dist/lib/db/escapeLikePattern.js +5 -0
- package/dist/lib/db/escapeLikePattern.js.map +7 -0
- package/dist/lib/db/mikro.js +82 -0
- package/dist/lib/db/mikro.js.map +7 -0
- package/dist/lib/di/container.js +94 -0
- package/dist/lib/di/container.js.map +7 -0
- package/dist/lib/email/send.js +12 -0
- package/dist/lib/email/send.js.map +7 -0
- package/dist/lib/encryption/aes.js +58 -0
- package/dist/lib/encryption/aes.js.map +7 -0
- package/dist/lib/encryption/customFieldValues.js +49 -0
- package/dist/lib/encryption/customFieldValues.js.map +7 -0
- package/dist/lib/encryption/entityFields.js +26 -0
- package/dist/lib/encryption/entityFields.js.map +7 -0
- package/dist/lib/encryption/entityIds.js +80 -0
- package/dist/lib/encryption/entityIds.js.map +7 -0
- package/dist/lib/encryption/find.js +45 -0
- package/dist/lib/encryption/find.js.map +7 -0
- package/dist/lib/encryption/indexDoc.js +69 -0
- package/dist/lib/encryption/indexDoc.js.map +7 -0
- package/dist/lib/encryption/kms.js +282 -0
- package/dist/lib/encryption/kms.js.map +7 -0
- package/dist/lib/encryption/subscriber.js +330 -0
- package/dist/lib/encryption/subscriber.js.map +7 -0
- package/dist/lib/encryption/tenantDataEncryptionService.js +252 -0
- package/dist/lib/encryption/tenantDataEncryptionService.js.map +7 -0
- package/dist/lib/encryption/toggles.js +18 -0
- package/dist/lib/encryption/toggles.js.map +7 -0
- package/dist/lib/entities/naming.js +9 -0
- package/dist/lib/entities/naming.js.map +7 -0
- package/dist/lib/entities/system-entities.js +43 -0
- package/dist/lib/entities/system-entities.js.map +7 -0
- package/dist/lib/frontend/organizationEvents.js +41 -0
- package/dist/lib/frontend/organizationEvents.js.map +7 -0
- package/dist/lib/frontend/useOrganizationScope.js +32 -0
- package/dist/lib/frontend/useOrganizationScope.js.map +7 -0
- package/dist/lib/hotkeys/index.js +128 -0
- package/dist/lib/hotkeys/index.js.map +7 -0
- package/dist/lib/i18n/app-dictionaries.js +17 -0
- package/dist/lib/i18n/app-dictionaries.js.map +7 -0
- package/dist/lib/i18n/config.js +7 -0
- package/dist/lib/i18n/config.js.map +7 -0
- package/dist/lib/i18n/context.js +50 -0
- package/dist/lib/i18n/context.js.map +7 -0
- package/dist/lib/i18n/server.js +68 -0
- package/dist/lib/i18n/server.js.map +7 -0
- package/dist/lib/i18n/translate.js +45 -0
- package/dist/lib/i18n/translate.js.map +7 -0
- package/dist/lib/indexers/error-log.js +82 -0
- package/dist/lib/indexers/error-log.js.map +7 -0
- package/dist/lib/indexers/status-log.js +80 -0
- package/dist/lib/indexers/status-log.js.map +7 -0
- package/dist/lib/lib/auth/jwt.js +34 -0
- package/dist/lib/lib/auth/jwt.js.map +7 -0
- package/dist/lib/lib/auth/server.js +77 -0
- package/dist/lib/lib/auth/server.js.map +7 -0
- package/dist/lib/lib/email/send.js +12 -0
- package/dist/lib/lib/email/send.js.map +7 -0
- package/dist/lib/lib/i18n/config.js +7 -0
- package/dist/lib/lib/i18n/config.js.map +7 -0
- package/dist/lib/lib/i18n/context.js +31 -0
- package/dist/lib/lib/i18n/context.js.map +7 -0
- package/dist/lib/lib/utils.js +9 -0
- package/dist/lib/lib/utils.js.map +7 -0
- package/dist/lib/location/countries.js +68 -0
- package/dist/lib/location/countries.js.map +7 -0
- package/dist/lib/modules/index.js +6 -0
- package/dist/lib/modules/index.js.map +7 -0
- package/dist/lib/modules/registry.js +18 -0
- package/dist/lib/modules/registry.js.map +7 -0
- package/dist/lib/openapi/crud.js +137 -0
- package/dist/lib/openapi/crud.js.map +7 -0
- package/dist/lib/openapi/generator.js +1131 -0
- package/dist/lib/openapi/generator.js.map +7 -0
- package/dist/lib/openapi/index.js +10 -0
- package/dist/lib/openapi/index.js.map +7 -0
- package/dist/lib/openapi/sanitize.js +110 -0
- package/dist/lib/openapi/sanitize.js.map +7 -0
- package/dist/lib/openapi/types.js +1 -0
- package/dist/lib/openapi/types.js.map +7 -0
- package/dist/lib/profiler/index.js +258 -0
- package/dist/lib/profiler/index.js.map +7 -0
- package/dist/lib/query/engine.js +729 -0
- package/dist/lib/query/engine.js.map +7 -0
- package/dist/lib/query/join-utils.js +195 -0
- package/dist/lib/query/join-utils.js.map +7 -0
- package/dist/lib/query/types.js +9 -0
- package/dist/lib/query/types.js.map +7 -0
- package/dist/lib/search/config.js +32 -0
- package/dist/lib/search/config.js.map +7 -0
- package/dist/lib/search/tokenize.js +34 -0
- package/dist/lib/search/tokenize.js.map +7 -0
- package/dist/lib/slugify.js +24 -0
- package/dist/lib/slugify.js.map +7 -0
- package/dist/lib/testing/bootstrap.js +51 -0
- package/dist/lib/testing/bootstrap.js.map +7 -0
- package/dist/lib/testing/index.js +17 -0
- package/dist/lib/testing/index.js.map +7 -0
- package/dist/lib/testing/renderWithProviders.js +15 -0
- package/dist/lib/testing/renderWithProviders.js.map +7 -0
- package/dist/lib/url.js +12 -0
- package/dist/lib/url.js.map +7 -0
- package/dist/lib/utils.js +13 -0
- package/dist/lib/utils.js.map +7 -0
- package/dist/lib/version.js +7 -0
- package/dist/lib/version.js.map +7 -0
- package/dist/modules/dashboard/widgets.js +1 -0
- package/dist/modules/dashboard/widgets.js.map +7 -0
- package/dist/modules/dsl.js +30 -0
- package/dist/modules/dsl.js.map +7 -0
- package/dist/modules/entities/kinds.js +22 -0
- package/dist/modules/entities/kinds.js.map +7 -0
- package/dist/modules/entities/options.js +26 -0
- package/dist/modules/entities/options.js.map +7 -0
- package/dist/modules/entities/validation.js +102 -0
- package/dist/modules/entities/validation.js.map +7 -0
- package/dist/modules/entities/validators.js +88 -0
- package/dist/modules/entities/validators.js.map +7 -0
- package/dist/modules/entities.js +1 -0
- package/dist/modules/entities.js.map +7 -0
- package/dist/modules/navigation/sidebarPreferences.js +50 -0
- package/dist/modules/navigation/sidebarPreferences.js.map +7 -0
- package/dist/modules/perspectives/types.js +1 -0
- package/dist/modules/perspectives/types.js.map +7 -0
- package/dist/modules/registry.js +96 -0
- package/dist/modules/registry.js.map +7 -0
- package/dist/modules/search.js +15 -0
- package/dist/modules/search.js.map +7 -0
- package/dist/modules/vector.js +1 -0
- package/dist/modules/vector.js.map +7 -0
- package/dist/modules/widgets/injection-loader.js +180 -0
- package/dist/modules/widgets/injection-loader.js.map +7 -0
- package/dist/modules/widgets/injection.js +1 -0
- package/dist/modules/widgets/injection.js.map +7 -0
- package/dist/security/features.js +23 -0
- package/dist/security/features.js.map +7 -0
- package/dist/types/pg.d.js +1 -0
- package/dist/types/pg.d.js.map +7 -0
- package/dist/types/react-email.d.js +1 -0
- package/dist/types/react-email.d.js.map +7 -0
- package/dist/types/resend.d.js +1 -0
- package/dist/types/resend.d.js.map +7 -0
- package/jest.config.cjs +22 -0
- package/package.json +88 -0
- package/src/index.ts +0 -0
- package/src/lib/api/__tests__/scoped.test.ts +38 -0
- package/src/lib/api/crud.ts +59 -0
- package/src/lib/api/scoped.ts +239 -0
- package/src/lib/auth/jwt.ts +39 -0
- package/src/lib/auth/server.ts +199 -0
- package/src/lib/boolean.ts +17 -0
- package/src/lib/bootstrap/appResolver.ts +85 -0
- package/src/lib/bootstrap/dynamicLoader.ts +177 -0
- package/src/lib/bootstrap/factory.ts +108 -0
- package/src/lib/bootstrap/index.ts +23 -0
- package/src/lib/bootstrap/types.ts +31 -0
- package/src/lib/cache/segments.ts +56 -0
- package/src/lib/cli/progress.ts +55 -0
- package/src/lib/commands/__tests__/command-bus.test.ts +84 -0
- package/src/lib/commands/__tests__/helpers.test.ts +42 -0
- package/src/lib/commands/command-bus.ts +349 -0
- package/src/lib/commands/customFieldSnapshots.ts +86 -0
- package/src/lib/commands/helpers.ts +143 -0
- package/src/lib/commands/index.ts +4 -0
- package/src/lib/commands/operationMetadata.ts +40 -0
- package/src/lib/commands/registry.ts +46 -0
- package/src/lib/commands/scope.ts +59 -0
- package/src/lib/commands/types.ts +63 -0
- package/src/lib/crud/__tests__/crud-factory.test.ts +333 -0
- package/src/lib/crud/__tests__/custom-fields.test.ts +150 -0
- package/src/lib/crud/cache-stats.ts +127 -0
- package/src/lib/crud/cache.ts +205 -0
- package/src/lib/crud/custom-fields-client.ts +54 -0
- package/src/lib/crud/custom-fields.ts +607 -0
- package/src/lib/crud/errors.ts +23 -0
- package/src/lib/crud/exporters.ts +188 -0
- package/src/lib/crud/factory.ts +1622 -0
- package/src/lib/crud/types.ts +29 -0
- package/src/lib/custom-fields/normalize.ts +45 -0
- package/src/lib/data/engine.ts +562 -0
- package/src/lib/db/escapeLikePattern.ts +2 -0
- package/src/lib/db/mikro.ts +100 -0
- package/src/lib/di/container.ts +105 -0
- package/src/lib/email/send.ts +18 -0
- package/src/lib/encryption/__tests__/customFieldValues.test.ts +63 -0
- package/src/lib/encryption/__tests__/indexDoc.test.ts +115 -0
- package/src/lib/encryption/aes.ts +64 -0
- package/src/lib/encryption/customFieldValues.ts +67 -0
- package/src/lib/encryption/entityFields.ts +39 -0
- package/src/lib/encryption/entityIds.ts +107 -0
- package/src/lib/encryption/find.ts +81 -0
- package/src/lib/encryption/indexDoc.ts +104 -0
- package/src/lib/encryption/kms.ts +337 -0
- package/src/lib/encryption/subscriber.ts +416 -0
- package/src/lib/encryption/tenantDataEncryptionService.ts +313 -0
- package/src/lib/encryption/toggles.ts +15 -0
- package/src/lib/entities/naming.ts +6 -0
- package/src/lib/entities/system-entities.ts +43 -0
- package/src/lib/frontend/organizationEvents.ts +55 -0
- package/src/lib/frontend/useOrganizationScope.ts +30 -0
- package/src/lib/hotkeys/index.ts +168 -0
- package/src/lib/i18n/app-dictionaries.ts +18 -0
- package/src/lib/i18n/config.ts +4 -0
- package/src/lib/i18n/context.tsx +66 -0
- package/src/lib/i18n/server.ts +74 -0
- package/src/lib/i18n/translate.ts +54 -0
- package/src/lib/indexers/error-log.ts +106 -0
- package/src/lib/indexers/status-log.ts +119 -0
- package/src/lib/lib/auth/jwt.ts +39 -0
- package/src/lib/lib/auth/server.ts +94 -0
- package/src/lib/lib/email/send.ts +18 -0
- package/src/lib/lib/i18n/config.ts +4 -0
- package/src/lib/lib/i18n/context.tsx +38 -0
- package/src/lib/lib/utils.ts +6 -0
- package/src/lib/location/countries.ts +97 -0
- package/src/lib/modules/index.ts +1 -0
- package/src/lib/modules/registry.ts +18 -0
- package/src/lib/openapi/crud.ts +218 -0
- package/src/lib/openapi/generator.ts +1311 -0
- package/src/lib/openapi/index.ts +4 -0
- package/src/lib/openapi/sanitize.ts +137 -0
- package/src/lib/openapi/types.ts +79 -0
- package/src/lib/profiler/index.ts +371 -0
- package/src/lib/query/__tests__/engine.test.ts +274 -0
- package/src/lib/query/engine.ts +837 -0
- package/src/lib/query/join-utils.ts +238 -0
- package/src/lib/query/types.ts +121 -0
- package/src/lib/search/config.ts +49 -0
- package/src/lib/search/tokenize.ts +45 -0
- package/src/lib/slugify.ts +28 -0
- package/src/lib/testing/bootstrap.ts +124 -0
- package/src/lib/testing/index.ts +15 -0
- package/src/lib/testing/renderWithProviders.tsx +31 -0
- package/src/lib/url.ts +12 -0
- package/src/lib/utils.ts +17 -0
- package/src/lib/version.ts +5 -0
- package/src/modules/__tests__/dsl.test.ts +35 -0
- package/src/modules/__tests__/registry.test.ts +300 -0
- package/src/modules/dashboard/widgets.ts +57 -0
- package/src/modules/dsl.ts +32 -0
- package/src/modules/entities/__tests__/validation.test.ts +52 -0
- package/src/modules/entities/kinds.ts +20 -0
- package/src/modules/entities/options.ts +36 -0
- package/src/modules/entities/validation.ts +118 -0
- package/src/modules/entities/validators.ts +93 -0
- package/src/modules/entities.ts +102 -0
- package/src/modules/navigation/sidebarPreferences.ts +62 -0
- package/src/modules/perspectives/types.ts +40 -0
- package/src/modules/registry.ts +249 -0
- package/src/modules/search.ts +325 -0
- package/src/modules/vector.ts +122 -0
- package/src/modules/widgets/__tests__/injection.test.ts +48 -0
- package/src/modules/widgets/injection-loader.ts +235 -0
- package/src/modules/widgets/injection.ts +120 -0
- package/src/security/features.ts +22 -0
- package/src/types/pg.d.ts +2 -0
- package/src/types/react-email.d.ts +2 -0
- package/src/types/resend.d.ts +2 -0
- package/tsconfig.build.json +11 -0
- package/tsconfig.json +9 -0
- package/watch.mjs +6 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { createContainer, asValue, InjectionMode } from "awilix";
|
|
2
|
+
import { RequestContext } from "@mikro-orm/core";
|
|
3
|
+
import { getOrm } from "@open-mercato/shared/lib/db/mikro";
|
|
4
|
+
import { BasicQueryEngine } from "@open-mercato/shared/lib/query/engine";
|
|
5
|
+
import { DefaultDataEngine } from "@open-mercato/shared/lib/data/engine";
|
|
6
|
+
import { commandRegistry, CommandBus } from "@open-mercato/shared/lib/commands";
|
|
7
|
+
const GLOBAL_KEY = "__openMercatoDiRegistrars__";
|
|
8
|
+
function getGlobalRegistrars() {
|
|
9
|
+
return globalThis[GLOBAL_KEY] ?? null;
|
|
10
|
+
}
|
|
11
|
+
function setGlobalRegistrars(registrars) {
|
|
12
|
+
globalThis[GLOBAL_KEY] = registrars;
|
|
13
|
+
}
|
|
14
|
+
function registerDiRegistrars(registrars) {
|
|
15
|
+
const existing = getGlobalRegistrars();
|
|
16
|
+
if (existing !== null && process.env.NODE_ENV === "development") {
|
|
17
|
+
console.debug("[Bootstrap] DI registrars re-registered (this may occur during HMR)");
|
|
18
|
+
}
|
|
19
|
+
setGlobalRegistrars(registrars);
|
|
20
|
+
}
|
|
21
|
+
function getDiRegistrars() {
|
|
22
|
+
const registrars = getGlobalRegistrars();
|
|
23
|
+
if (!registrars) {
|
|
24
|
+
throw new Error("[Bootstrap] DI registrars not registered. Call registerDiRegistrars() at bootstrap.");
|
|
25
|
+
}
|
|
26
|
+
return registrars;
|
|
27
|
+
}
|
|
28
|
+
async function createRequestContainer() {
|
|
29
|
+
const diRegistrars = getDiRegistrars();
|
|
30
|
+
const orm = await getOrm();
|
|
31
|
+
const baseEm = RequestContext.getEntityManager() ?? orm.em;
|
|
32
|
+
const em = baseEm.fork({ clear: true, freshEventManager: true, useContext: true });
|
|
33
|
+
const container = createContainer({ injectionMode: InjectionMode.CLASSIC });
|
|
34
|
+
container.register({
|
|
35
|
+
em: asValue(em),
|
|
36
|
+
queryEngine: asValue(new BasicQueryEngine(em, void 0, () => {
|
|
37
|
+
try {
|
|
38
|
+
return container.resolve("tenantEncryptionService");
|
|
39
|
+
} catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
})),
|
|
43
|
+
dataEngine: asValue(new DefaultDataEngine(em, container)),
|
|
44
|
+
commandRegistry: asValue(commandRegistry),
|
|
45
|
+
commandBus: asValue(new CommandBus())
|
|
46
|
+
});
|
|
47
|
+
for (const reg of diRegistrars) {
|
|
48
|
+
try {
|
|
49
|
+
reg?.(container);
|
|
50
|
+
} catch {
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
const { bootstrap } = await import("@open-mercato/core/bootstrap");
|
|
55
|
+
if (bootstrap && typeof bootstrap === "function") {
|
|
56
|
+
const alreadyBootstrapped = !!container.registrations?.eventBus;
|
|
57
|
+
if (!alreadyBootstrapped) {
|
|
58
|
+
await bootstrap(container);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
} catch {
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
const appDi = await import("@/di");
|
|
65
|
+
if (appDi?.register) {
|
|
66
|
+
try {
|
|
67
|
+
const maybe = appDi.register(container);
|
|
68
|
+
if (maybe && typeof maybe.then === "function") await maybe;
|
|
69
|
+
} catch {
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
} catch {
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
const emForEnc = container.resolve("em");
|
|
76
|
+
const tenantEncryptionService = container.hasRegistration("tenantEncryptionService") ? container.resolve("tenantEncryptionService") : null;
|
|
77
|
+
if (emForEnc && tenantEncryptionService?.isEnabled?.()) {
|
|
78
|
+
const { registerTenantEncryptionSubscriber } = await import("@open-mercato/shared/lib/encryption/subscriber");
|
|
79
|
+
registerTenantEncryptionSubscriber(emForEnc, tenantEncryptionService);
|
|
80
|
+
}
|
|
81
|
+
} catch {
|
|
82
|
+
}
|
|
83
|
+
return container;
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
require("server-only");
|
|
87
|
+
} catch {
|
|
88
|
+
}
|
|
89
|
+
export {
|
|
90
|
+
createRequestContainer,
|
|
91
|
+
getDiRegistrars,
|
|
92
|
+
registerDiRegistrars
|
|
93
|
+
};
|
|
94
|
+
//# sourceMappingURL=container.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/di/container.ts"],
|
|
4
|
+
"sourcesContent": ["import { createContainer, asValue, AwilixContainer, InjectionMode } from 'awilix'\nimport { RequestContext } from '@mikro-orm/core'\nimport { getOrm } from '@open-mercato/shared/lib/db/mikro'\nimport { EntityManager } from '@mikro-orm/postgresql'\nimport { BasicQueryEngine } from '@open-mercato/shared/lib/query/engine'\nimport { DefaultDataEngine } from '@open-mercato/shared/lib/data/engine'\nimport { commandRegistry, CommandBus } from '@open-mercato/shared/lib/commands'\n\nexport type AppContainer = AwilixContainer\nexport type DiRegistrar = (container: AwilixContainer) => void\n\n// Registration pattern for publishable packages\n// Use globalThis to survive tsx/esbuild module duplication issue where the same\n// file can be loaded as multiple module instances when mixing dynamic and static imports\nconst GLOBAL_KEY = '__openMercatoDiRegistrars__'\n\nfunction getGlobalRegistrars(): DiRegistrar[] | null {\n return (globalThis as any)[GLOBAL_KEY] ?? null\n}\n\nfunction setGlobalRegistrars(registrars: DiRegistrar[]): void {\n (globalThis as any)[GLOBAL_KEY] = registrars\n}\n\nexport function registerDiRegistrars(registrars: DiRegistrar[]) {\n const existing = getGlobalRegistrars()\n if (existing !== null && process.env.NODE_ENV === 'development') {\n console.debug('[Bootstrap] DI registrars re-registered (this may occur during HMR)')\n }\n setGlobalRegistrars(registrars)\n}\n\nexport function getDiRegistrars(): DiRegistrar[] {\n const registrars = getGlobalRegistrars()\n if (!registrars) {\n throw new Error('[Bootstrap] DI registrars not registered. Call registerDiRegistrars() at bootstrap.')\n }\n return registrars\n}\n\nexport async function createRequestContainer(): Promise<AppContainer> {\n const diRegistrars = getDiRegistrars()\n const orm = await getOrm()\n // Use a fresh event manager so request-level subscribers (e.g., encryption) don't pile up globally\n const baseEm = (RequestContext.getEntityManager() as any) ?? orm.em\n const em = baseEm.fork({ clear: true, freshEventManager: true, useContext: true }) as unknown as EntityManager\n const container = createContainer({ injectionMode: InjectionMode.CLASSIC })\n // Core registrations\n container.register({\n em: asValue(em),\n queryEngine: asValue(new BasicQueryEngine(em, undefined, () => {\n try { return container.resolve('tenantEncryptionService') as any } catch { return null }\n })),\n dataEngine: asValue(new DefaultDataEngine(em, container as any)),\n commandRegistry: asValue(commandRegistry),\n commandBus: asValue(new CommandBus()),\n })\n // Allow modules to override/extend\n for (const reg of diRegistrars) {\n try { reg?.(container) } catch {}\n }\n // Core bootstrap (cache, event bus, encryption subscriber/KMS, module subscribers)\n try {\n const { bootstrap } = await import('@open-mercato/core/bootstrap') as any\n if (bootstrap && typeof bootstrap === 'function') {\n // Avoid double bootstrap if caller already wired it\n const alreadyBootstrapped = !!container.registrations?.eventBus\n if (!alreadyBootstrapped) {\n await bootstrap(container)\n }\n }\n } catch { /* optional */ }\n // App-level DI override (last chance)\n // This import path resolves only in the app context, not in packages\n try {\n // @ts-ignore - @/di only exists in app context, not in packages\n const appDi = await import('@/di') as any\n if (appDi?.register) {\n try {\n const maybe = appDi.register(container)\n if (maybe && typeof maybe.then === 'function') await maybe\n } catch {}\n }\n } catch {}\n // Ensure tenant encryption subscriber is always registered on the fresh request-scoped EM\n try {\n const emForEnc = container.resolve('em') as any\n const tenantEncryptionService = container.hasRegistration('tenantEncryptionService')\n ? (container.resolve('tenantEncryptionService') as any)\n : null\n if (emForEnc && tenantEncryptionService?.isEnabled?.()) {\n const { registerTenantEncryptionSubscriber } = await import('@open-mercato/shared/lib/encryption/subscriber')\n registerTenantEncryptionSubscriber(emForEnc, tenantEncryptionService)\n }\n } catch {\n // best-effort; do not block container creation\n }\n return container\n}\ntry {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n require('server-only')\n} catch {\n // allow CLI/generator usage where Next server-only is not present\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,iBAAiB,SAA0B,qBAAqB;AACzE,SAAS,sBAAsB;AAC/B,SAAS,cAAc;AAEvB,SAAS,wBAAwB;AACjC,SAAS,yBAAyB;AAClC,SAAS,iBAAiB,kBAAkB;AAQ5C,MAAM,aAAa;AAEnB,SAAS,sBAA4C;AACnD,SAAQ,WAAmB,UAAU,KAAK;AAC5C;AAEA,SAAS,oBAAoB,YAAiC;AAC5D,EAAC,WAAmB,UAAU,IAAI;AACpC;AAEO,SAAS,qBAAqB,YAA2B;AAC9D,QAAM,WAAW,oBAAoB;AACrC,MAAI,aAAa,QAAQ,QAAQ,IAAI,aAAa,eAAe;AAC/D,YAAQ,MAAM,qEAAqE;AAAA,EACrF;AACA,sBAAoB,UAAU;AAChC;AAEO,SAAS,kBAAiC;AAC/C,QAAM,aAAa,oBAAoB;AACvC,MAAI,CAAC,YAAY;AACf,UAAM,IAAI,MAAM,qFAAqF;AAAA,EACvG;AACA,SAAO;AACT;AAEA,eAAsB,yBAAgD;AACpE,QAAM,eAAe,gBAAgB;AACrC,QAAM,MAAM,MAAM,OAAO;AAEzB,QAAM,SAAU,eAAe,iBAAiB,KAAa,IAAI;AACjE,QAAM,KAAK,OAAO,KAAK,EAAE,OAAO,MAAM,mBAAmB,MAAM,YAAY,KAAK,CAAC;AACjF,QAAM,YAAY,gBAAgB,EAAE,eAAe,cAAc,QAAQ,CAAC;AAE1E,YAAU,SAAS;AAAA,IACjB,IAAI,QAAQ,EAAE;AAAA,IACd,aAAa,QAAQ,IAAI,iBAAiB,IAAI,QAAW,MAAM;AAC7D,UAAI;AAAE,eAAO,UAAU,QAAQ,yBAAyB;AAAA,MAAS,QAAQ;AAAE,eAAO;AAAA,MAAK;AAAA,IACzF,CAAC,CAAC;AAAA,IACF,YAAY,QAAQ,IAAI,kBAAkB,IAAI,SAAgB,CAAC;AAAA,IAC/D,iBAAiB,QAAQ,eAAe;AAAA,IACxC,YAAY,QAAQ,IAAI,WAAW,CAAC;AAAA,EACtC,CAAC;AAED,aAAW,OAAO,cAAc;AAC9B,QAAI;AAAE,YAAM,SAAS;AAAA,IAAE,QAAQ;AAAA,IAAC;AAAA,EAClC;AAEA,MAAI;AACF,UAAM,EAAE,UAAU,IAAI,MAAM,OAAO,8BAA8B;AACjE,QAAI,aAAa,OAAO,cAAc,YAAY;AAEhD,YAAM,sBAAsB,CAAC,CAAC,UAAU,eAAe;AACvD,UAAI,CAAC,qBAAqB;AACxB,cAAM,UAAU,SAAS;AAAA,MAC3B;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAAiB;AAGzB,MAAI;AAEF,UAAM,QAAQ,MAAM,OAAO,MAAM;AACjC,QAAI,OAAO,UAAU;AACnB,UAAI;AACF,cAAM,QAAQ,MAAM,SAAS,SAAS;AACtC,YAAI,SAAS,OAAO,MAAM,SAAS,WAAY,OAAM;AAAA,MACvD,QAAQ;AAAA,MAAC;AAAA,IACX;AAAA,EACF,QAAQ;AAAA,EAAC;AAET,MAAI;AACF,UAAM,WAAW,UAAU,QAAQ,IAAI;AACvC,UAAM,0BAA0B,UAAU,gBAAgB,yBAAyB,IAC9E,UAAU,QAAQ,yBAAyB,IAC5C;AACJ,QAAI,YAAY,yBAAyB,YAAY,GAAG;AACtD,YAAM,EAAE,mCAAmC,IAAI,MAAM,OAAO,gDAAgD;AAC5G,yCAAmC,UAAU,uBAAuB;AAAA,IACtE;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AACA,IAAI;AAEF,UAAQ,aAAa;AACvB,QAAQ;AAER;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Resend } from "resend";
|
|
2
|
+
async function sendEmail({ to, subject, react, from }) {
|
|
3
|
+
const apiKey = process.env.RESEND_API_KEY;
|
|
4
|
+
if (!apiKey) throw new Error("RESEND_API_KEY is not set");
|
|
5
|
+
const resend = new Resend(apiKey);
|
|
6
|
+
const fromAddr = from || process.env.EMAIL_FROM || "no-reply@localhost";
|
|
7
|
+
await resend.emails.send({ to, subject, from: fromAddr, react });
|
|
8
|
+
}
|
|
9
|
+
export {
|
|
10
|
+
sendEmail
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=send.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/email/send.ts"],
|
|
4
|
+
"sourcesContent": ["import { Resend } from 'resend'\nimport React from 'react'\n\nexport type SendEmailOptions = {\n to: string\n subject: string\n react: React.ReactElement\n from?: string\n}\n\nexport async function sendEmail({ to, subject, react, from }: SendEmailOptions) {\n const apiKey = process.env.RESEND_API_KEY\n if (!apiKey) throw new Error('RESEND_API_KEY is not set')\n const resend = new Resend(apiKey)\n const fromAddr = from || process.env.EMAIL_FROM || 'no-reply@localhost'\n await resend.emails.send({ to, subject, from: fromAddr, react })\n}\n\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,cAAc;AAUvB,eAAsB,UAAU,EAAE,IAAI,SAAS,OAAO,KAAK,GAAqB;AAC9E,QAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,2BAA2B;AACxD,QAAM,SAAS,IAAI,OAAO,MAAM;AAChC,QAAM,WAAW,QAAQ,QAAQ,IAAI,cAAc;AACnD,QAAM,OAAO,OAAO,KAAK,EAAE,IAAI,SAAS,MAAM,UAAU,MAAM,CAAC;AACjE;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import { isEncryptionDebugEnabled } from "./toggles.js";
|
|
3
|
+
function generateDek() {
|
|
4
|
+
return crypto.randomBytes(32).toString("base64");
|
|
5
|
+
}
|
|
6
|
+
function logDebug(event, payload) {
|
|
7
|
+
if (!isEncryptionDebugEnabled()) return;
|
|
8
|
+
try {
|
|
9
|
+
console.debug("[encryption]", event, payload);
|
|
10
|
+
} catch {
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function encryptWithAesGcm(value, dekBase64) {
|
|
14
|
+
const dek = Buffer.from(dekBase64, "base64");
|
|
15
|
+
const iv = crypto.randomBytes(12);
|
|
16
|
+
const cipher = crypto.createCipheriv("aes-256-gcm", dek, iv);
|
|
17
|
+
const ciphertext = Buffer.concat([cipher.update(value, "utf8"), cipher.final()]);
|
|
18
|
+
const tag = cipher.getAuthTag();
|
|
19
|
+
const payload = [
|
|
20
|
+
iv.toString("base64"),
|
|
21
|
+
ciphertext.toString("base64"),
|
|
22
|
+
tag.toString("base64"),
|
|
23
|
+
"v1"
|
|
24
|
+
].join(":");
|
|
25
|
+
logDebug("encrypt", { length: ciphertext.length });
|
|
26
|
+
return { value: payload, raw: payload, version: "v1" };
|
|
27
|
+
}
|
|
28
|
+
function decryptWithAesGcm(payload, dekBase64) {
|
|
29
|
+
if (!payload) return null;
|
|
30
|
+
const parts = payload.split(":");
|
|
31
|
+
if (parts.length !== 4) return null;
|
|
32
|
+
const [ivB64, ciphertextB64, tagB64, version] = parts;
|
|
33
|
+
if (version !== "v1") return null;
|
|
34
|
+
const dek = Buffer.from(dekBase64, "base64");
|
|
35
|
+
const iv = Buffer.from(ivB64, "base64");
|
|
36
|
+
const ciphertext = Buffer.from(ciphertextB64, "base64");
|
|
37
|
+
const tag = Buffer.from(tagB64, "base64");
|
|
38
|
+
try {
|
|
39
|
+
const decipher = crypto.createDecipheriv("aes-256-gcm", dek, iv);
|
|
40
|
+
decipher.setAuthTag(tag);
|
|
41
|
+
const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
|
|
42
|
+
logDebug("decrypt", { iv: ivB64, tag: tagB64 });
|
|
43
|
+
return decrypted;
|
|
44
|
+
} catch (err) {
|
|
45
|
+
logDebug("decrypt_error", { message: err?.message || String(err) });
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function hashForLookup(value) {
|
|
50
|
+
return crypto.createHash("sha256").update(value.toLowerCase().trim()).digest("hex");
|
|
51
|
+
}
|
|
52
|
+
export {
|
|
53
|
+
decryptWithAesGcm,
|
|
54
|
+
encryptWithAesGcm,
|
|
55
|
+
generateDek,
|
|
56
|
+
hashForLookup
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=aes.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/encryption/aes.ts"],
|
|
4
|
+
"sourcesContent": ["import crypto from 'node:crypto'\nimport { isEncryptionDebugEnabled } from './toggles'\n\nexport type EncryptionPayload = {\n value: string | null\n raw: string\n version: string\n}\n\nexport function generateDek(): string {\n return crypto.randomBytes(32).toString('base64')\n}\n\nfunction logDebug(event: string, payload: Record<string, unknown>) {\n if (!isEncryptionDebugEnabled()) return\n try {\n // eslint-disable-next-line no-console\n console.debug('[encryption]', event, payload)\n } catch {\n // ignore\n }\n}\n\nexport function encryptWithAesGcm(value: string, dekBase64: string): EncryptionPayload {\n const dek = Buffer.from(dekBase64, 'base64')\n const iv = crypto.randomBytes(12)\n const cipher = crypto.createCipheriv('aes-256-gcm', dek, iv)\n const ciphertext = Buffer.concat([cipher.update(value, 'utf8'), cipher.final()])\n const tag = cipher.getAuthTag()\n const payload = [\n iv.toString('base64'),\n ciphertext.toString('base64'),\n tag.toString('base64'),\n 'v1',\n ].join(':')\n logDebug('encrypt', { length: ciphertext.length })\n return { value: payload, raw: payload, version: 'v1' }\n}\n\nexport function decryptWithAesGcm(payload: string, dekBase64: string): string | null {\n if (!payload) return null\n const parts = payload.split(':')\n if (parts.length !== 4) return null\n const [ivB64, ciphertextB64, tagB64, version] = parts\n if (version !== 'v1') return null\n const dek = Buffer.from(dekBase64, 'base64')\n const iv = Buffer.from(ivB64, 'base64')\n const ciphertext = Buffer.from(ciphertextB64, 'base64')\n const tag = Buffer.from(tagB64, 'base64')\n try {\n const decipher = crypto.createDecipheriv('aes-256-gcm', dek, iv)\n decipher.setAuthTag(tag)\n const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString('utf8')\n logDebug('decrypt', { iv: ivB64, tag: tagB64 })\n return decrypted\n } catch (err) {\n logDebug('decrypt_error', { message: (err as Error)?.message || String(err) })\n return null\n }\n}\n\nexport function hashForLookup(value: string): string {\n return crypto.createHash('sha256').update(value.toLowerCase().trim()).digest('hex')\n}\n"],
|
|
5
|
+
"mappings": "AAAA,OAAO,YAAY;AACnB,SAAS,gCAAgC;AAQlC,SAAS,cAAsB;AACpC,SAAO,OAAO,YAAY,EAAE,EAAE,SAAS,QAAQ;AACjD;AAEA,SAAS,SAAS,OAAe,SAAkC;AACjE,MAAI,CAAC,yBAAyB,EAAG;AACjC,MAAI;AAEF,YAAQ,MAAM,gBAAgB,OAAO,OAAO;AAAA,EAC9C,QAAQ;AAAA,EAER;AACF;AAEO,SAAS,kBAAkB,OAAe,WAAsC;AACrF,QAAM,MAAM,OAAO,KAAK,WAAW,QAAQ;AAC3C,QAAM,KAAK,OAAO,YAAY,EAAE;AAChC,QAAM,SAAS,OAAO,eAAe,eAAe,KAAK,EAAE;AAC3D,QAAM,aAAa,OAAO,OAAO,CAAC,OAAO,OAAO,OAAO,MAAM,GAAG,OAAO,MAAM,CAAC,CAAC;AAC/E,QAAM,MAAM,OAAO,WAAW;AAC9B,QAAM,UAAU;AAAA,IACd,GAAG,SAAS,QAAQ;AAAA,IACpB,WAAW,SAAS,QAAQ;AAAA,IAC5B,IAAI,SAAS,QAAQ;AAAA,IACrB;AAAA,EACF,EAAE,KAAK,GAAG;AACV,WAAS,WAAW,EAAE,QAAQ,WAAW,OAAO,CAAC;AACjD,SAAO,EAAE,OAAO,SAAS,KAAK,SAAS,SAAS,KAAK;AACvD;AAEO,SAAS,kBAAkB,SAAiB,WAAkC;AACnF,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,QAAQ,QAAQ,MAAM,GAAG;AAC/B,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,QAAM,CAAC,OAAO,eAAe,QAAQ,OAAO,IAAI;AAChD,MAAI,YAAY,KAAM,QAAO;AAC7B,QAAM,MAAM,OAAO,KAAK,WAAW,QAAQ;AAC3C,QAAM,KAAK,OAAO,KAAK,OAAO,QAAQ;AACtC,QAAM,aAAa,OAAO,KAAK,eAAe,QAAQ;AACtD,QAAM,MAAM,OAAO,KAAK,QAAQ,QAAQ;AACxC,MAAI;AACF,UAAM,WAAW,OAAO,iBAAiB,eAAe,KAAK,EAAE;AAC/D,aAAS,WAAW,GAAG;AACvB,UAAM,YAAY,OAAO,OAAO,CAAC,SAAS,OAAO,UAAU,GAAG,SAAS,MAAM,CAAC,CAAC,EAAE,SAAS,MAAM;AAChG,aAAS,WAAW,EAAE,IAAI,OAAO,KAAK,OAAO,CAAC;AAC9C,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,aAAS,iBAAiB,EAAE,SAAU,KAAe,WAAW,OAAO,GAAG,EAAE,CAAC;AAC7E,WAAO;AAAA,EACT;AACF;AAEO,SAAS,cAAc,OAAuB;AACnD,SAAO,OAAO,WAAW,QAAQ,EAAE,OAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,OAAO,KAAK;AACpF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { encryptWithAesGcm, decryptWithAesGcm } from "./aes.js";
|
|
2
|
+
import { TenantDataEncryptionService } from "./tenantDataEncryptionService.js";
|
|
3
|
+
const serviceCache = /* @__PURE__ */ new WeakMap();
|
|
4
|
+
function resolveTenantEncryptionService(em, provided) {
|
|
5
|
+
if (provided) return provided;
|
|
6
|
+
const cached = serviceCache.get(em);
|
|
7
|
+
if (cached) return cached;
|
|
8
|
+
const service = new TenantDataEncryptionService(em);
|
|
9
|
+
serviceCache.set(em, service);
|
|
10
|
+
return service;
|
|
11
|
+
}
|
|
12
|
+
async function resolveDekKey(service, tenantId, cache, opts) {
|
|
13
|
+
const scopedTenantId = tenantId ?? null;
|
|
14
|
+
if (!service || !service.isEnabled() || !scopedTenantId) return null;
|
|
15
|
+
if (cache?.has(scopedTenantId)) return cache.get(scopedTenantId) ?? null;
|
|
16
|
+
const dek = await service.getDek(scopedTenantId);
|
|
17
|
+
let key = dek?.key ?? null;
|
|
18
|
+
if (!key && opts?.createIfMissing && typeof service.createDek === "function") {
|
|
19
|
+
const created = await service.createDek(scopedTenantId);
|
|
20
|
+
key = created?.key ?? null;
|
|
21
|
+
}
|
|
22
|
+
cache?.set(scopedTenantId, key);
|
|
23
|
+
return key;
|
|
24
|
+
}
|
|
25
|
+
async function encryptCustomFieldValue(value, tenantId, service, cache) {
|
|
26
|
+
if (value === void 0 || value === null) return value;
|
|
27
|
+
const key = await resolveDekKey(service, tenantId, cache, { createIfMissing: true });
|
|
28
|
+
if (!key) return value;
|
|
29
|
+
const serialized = typeof value === "string" ? value : JSON.stringify(value);
|
|
30
|
+
return encryptWithAesGcm(serialized, key).value;
|
|
31
|
+
}
|
|
32
|
+
async function decryptCustomFieldValue(value, tenantId, service, cache) {
|
|
33
|
+
if (value === void 0 || value === null || typeof value !== "string") return value;
|
|
34
|
+
const key = await resolveDekKey(service, tenantId, cache);
|
|
35
|
+
if (!key) return value;
|
|
36
|
+
const decrypted = decryptWithAesGcm(value, key);
|
|
37
|
+
if (decrypted === null) return value;
|
|
38
|
+
try {
|
|
39
|
+
return JSON.parse(decrypted);
|
|
40
|
+
} catch {
|
|
41
|
+
return decrypted;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export {
|
|
45
|
+
decryptCustomFieldValue,
|
|
46
|
+
encryptCustomFieldValue,
|
|
47
|
+
resolveTenantEncryptionService
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=customFieldValues.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/encryption/customFieldValues.ts"],
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/core'\nimport { encryptWithAesGcm, decryptWithAesGcm } from './aes'\nimport { TenantDataEncryptionService } from './tenantDataEncryptionService'\n\nconst serviceCache = new WeakMap<EntityManager, TenantDataEncryptionService>()\n\nexport function resolveTenantEncryptionService(\n em: EntityManager,\n provided?: TenantDataEncryptionService | null,\n): TenantDataEncryptionService | null {\n if (provided) return provided\n const cached = serviceCache.get(em)\n if (cached) return cached\n const service = new TenantDataEncryptionService(em as any)\n serviceCache.set(em, service)\n return service\n}\n\nasync function resolveDekKey(\n service: TenantDataEncryptionService | null,\n tenantId: string | null | undefined,\n cache?: Map<string | null, string | null>,\n opts?: { createIfMissing?: boolean },\n): Promise<string | null> {\n const scopedTenantId = tenantId ?? null\n if (!service || !service.isEnabled() || !scopedTenantId) return null\n if (cache?.has(scopedTenantId)) return cache.get(scopedTenantId) ?? null\n const dek = await service.getDek(scopedTenantId)\n let key = dek?.key ?? null\n if (!key && opts?.createIfMissing && typeof service.createDek === 'function') {\n const created = await service.createDek(scopedTenantId)\n key = created?.key ?? null\n }\n cache?.set(scopedTenantId, key)\n return key\n}\n\nexport async function encryptCustomFieldValue(\n value: unknown,\n tenantId: string | null | undefined,\n service: TenantDataEncryptionService | null,\n cache?: Map<string | null, string | null>,\n): Promise<unknown> {\n if (value === undefined || value === null) return value\n const key = await resolveDekKey(service, tenantId, cache, { createIfMissing: true })\n if (!key) return value\n const serialized = typeof value === 'string' ? value : JSON.stringify(value)\n return encryptWithAesGcm(serialized, key).value\n}\n\nexport async function decryptCustomFieldValue(\n value: unknown,\n tenantId: string | null | undefined,\n service: TenantDataEncryptionService | null,\n cache?: Map<string | null, string | null>,\n): Promise<unknown> {\n if (value === undefined || value === null || typeof value !== 'string') return value\n const key = await resolveDekKey(service, tenantId, cache)\n if (!key) return value\n const decrypted = decryptWithAesGcm(value, key)\n if (decrypted === null) return value\n try {\n return JSON.parse(decrypted)\n } catch {\n return decrypted\n }\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,mBAAmB,yBAAyB;AACrD,SAAS,mCAAmC;AAE5C,MAAM,eAAe,oBAAI,QAAoD;AAEtE,SAAS,+BACd,IACA,UACoC;AACpC,MAAI,SAAU,QAAO;AACrB,QAAM,SAAS,aAAa,IAAI,EAAE;AAClC,MAAI,OAAQ,QAAO;AACnB,QAAM,UAAU,IAAI,4BAA4B,EAAS;AACzD,eAAa,IAAI,IAAI,OAAO;AAC5B,SAAO;AACT;AAEA,eAAe,cACb,SACA,UACA,OACA,MACwB;AACxB,QAAM,iBAAiB,YAAY;AACnC,MAAI,CAAC,WAAW,CAAC,QAAQ,UAAU,KAAK,CAAC,eAAgB,QAAO;AAChE,MAAI,OAAO,IAAI,cAAc,EAAG,QAAO,MAAM,IAAI,cAAc,KAAK;AACpE,QAAM,MAAM,MAAM,QAAQ,OAAO,cAAc;AAC/C,MAAI,MAAM,KAAK,OAAO;AACtB,MAAI,CAAC,OAAO,MAAM,mBAAmB,OAAO,QAAQ,cAAc,YAAY;AAC5E,UAAM,UAAU,MAAM,QAAQ,UAAU,cAAc;AACtD,UAAM,SAAS,OAAO;AAAA,EACxB;AACA,SAAO,IAAI,gBAAgB,GAAG;AAC9B,SAAO;AACT;AAEA,eAAsB,wBACpB,OACA,UACA,SACA,OACkB;AAClB,MAAI,UAAU,UAAa,UAAU,KAAM,QAAO;AAClD,QAAM,MAAM,MAAM,cAAc,SAAS,UAAU,OAAO,EAAE,iBAAiB,KAAK,CAAC;AACnF,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,aAAa,OAAO,UAAU,WAAW,QAAQ,KAAK,UAAU,KAAK;AAC3E,SAAO,kBAAkB,YAAY,GAAG,EAAE;AAC5C;AAEA,eAAsB,wBACpB,OACA,UACA,SACA,OACkB;AAClB,MAAI,UAAU,UAAa,UAAU,QAAQ,OAAO,UAAU,SAAU,QAAO;AAC/E,QAAM,MAAM,MAAM,cAAc,SAAS,UAAU,KAAK;AACxD,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,YAAY,kBAAkB,OAAO,GAAG;AAC9C,MAAI,cAAc,KAAM,QAAO;AAC/B,MAAI;AACF,WAAO,KAAK,MAAM,SAAS;AAAA,EAC7B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
let _entityFieldsRegistry = null;
|
|
2
|
+
function registerEntityFields(registry) {
|
|
3
|
+
if (_entityFieldsRegistry !== null && process.env.NODE_ENV === "development") {
|
|
4
|
+
console.debug("[Bootstrap] Entity fields re-registered (this may occur during HMR)");
|
|
5
|
+
}
|
|
6
|
+
_entityFieldsRegistry = registry;
|
|
7
|
+
}
|
|
8
|
+
function getEntityFieldsRegistry(throwIfNotRegistered = true) {
|
|
9
|
+
if (!_entityFieldsRegistry) {
|
|
10
|
+
if (throwIfNotRegistered) {
|
|
11
|
+
throw new Error("[Bootstrap] Entity fields not registered. Call registerEntityFields() at bootstrap.");
|
|
12
|
+
}
|
|
13
|
+
return {};
|
|
14
|
+
}
|
|
15
|
+
return _entityFieldsRegistry;
|
|
16
|
+
}
|
|
17
|
+
function getEntityFields(slug) {
|
|
18
|
+
const registry = getEntityFieldsRegistry(false);
|
|
19
|
+
return registry[slug];
|
|
20
|
+
}
|
|
21
|
+
export {
|
|
22
|
+
getEntityFields,
|
|
23
|
+
getEntityFieldsRegistry,
|
|
24
|
+
registerEntityFields
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=entityFields.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/encryption/entityFields.ts"],
|
|
4
|
+
"sourcesContent": ["// Registration pattern for entity fields (for Turbopack compatibility)\nexport type EntityFieldsRegistry = Record<string, Record<string, string>>\n\nlet _entityFieldsRegistry: EntityFieldsRegistry | null = null\n\nexport function registerEntityFields(registry: EntityFieldsRegistry) {\n if (_entityFieldsRegistry !== null && process.env.NODE_ENV === 'development') {\n console.debug('[Bootstrap] Entity fields re-registered (this may occur during HMR)')\n }\n _entityFieldsRegistry = registry\n}\n\n/**\n * Get registered entity fields.\n *\n * @param throwIfNotRegistered - If true, throws error when entity fields are not registered.\n * If false, returns empty object (useful during module load).\n * Default: true\n */\nexport function getEntityFieldsRegistry(throwIfNotRegistered = true): EntityFieldsRegistry {\n if (!_entityFieldsRegistry) {\n if (throwIfNotRegistered) {\n throw new Error('[Bootstrap] Entity fields not registered. Call registerEntityFields() at bootstrap.')\n }\n return {} as EntityFieldsRegistry\n }\n return _entityFieldsRegistry\n}\n\n/**\n * Get fields for a specific entity by slug.\n *\n * @param slug - The entity slug (e.g., 'user', 'sales_order')\n * @returns The entity's fields or undefined if not found\n */\nexport function getEntityFields(slug: string): Record<string, string> | undefined {\n const registry = getEntityFieldsRegistry(false)\n return registry[slug]\n}\n"],
|
|
5
|
+
"mappings": "AAGA,IAAI,wBAAqD;AAElD,SAAS,qBAAqB,UAAgC;AACnE,MAAI,0BAA0B,QAAQ,QAAQ,IAAI,aAAa,eAAe;AAC5E,YAAQ,MAAM,qEAAqE;AAAA,EACrF;AACA,0BAAwB;AAC1B;AASO,SAAS,wBAAwB,uBAAuB,MAA4B;AACzF,MAAI,CAAC,uBAAuB;AAC1B,QAAI,sBAAsB;AACxB,YAAM,IAAI,MAAM,qFAAqF;AAAA,IACvG;AACA,WAAO,CAAC;AAAA,EACV;AACA,SAAO;AACT;AAQO,SAAS,gBAAgB,MAAkD;AAChF,QAAM,WAAW,wBAAwB,KAAK;AAC9C,SAAO,SAAS,IAAI;AACtB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
let _entityIds = null;
|
|
2
|
+
let _entityIdLookup = null;
|
|
3
|
+
function registerEntityIds(E) {
|
|
4
|
+
if (_entityIds !== null && process.env.NODE_ENV === "development") {
|
|
5
|
+
console.debug("[Bootstrap] Entity IDs re-registered (this may occur during HMR)");
|
|
6
|
+
}
|
|
7
|
+
_entityIds = E;
|
|
8
|
+
_entityIdLookup = null;
|
|
9
|
+
}
|
|
10
|
+
function getEntityIds(throwIfNotRegistered = true) {
|
|
11
|
+
if (!_entityIds) {
|
|
12
|
+
if (throwIfNotRegistered) {
|
|
13
|
+
throw new Error("[Bootstrap] Entity IDs not registered. Call registerEntityIds() at bootstrap.");
|
|
14
|
+
}
|
|
15
|
+
return {};
|
|
16
|
+
}
|
|
17
|
+
return _entityIds;
|
|
18
|
+
}
|
|
19
|
+
const toSnake = (value) => value.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[\s-]+/g, "_").replace(/__+/g, "_").toLowerCase();
|
|
20
|
+
function getEntityIdLookup() {
|
|
21
|
+
if (_entityIdLookup) return _entityIdLookup;
|
|
22
|
+
const E = getEntityIds();
|
|
23
|
+
const map = /* @__PURE__ */ new Map();
|
|
24
|
+
for (const mod of Object.values(E || {})) {
|
|
25
|
+
for (const [key, entityId] of Object.entries(mod || {})) {
|
|
26
|
+
const snake = toSnake(key);
|
|
27
|
+
map.set(snake, entityId);
|
|
28
|
+
map.set(key.toLowerCase(), entityId);
|
|
29
|
+
map.set(
|
|
30
|
+
key.split("_").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(""),
|
|
31
|
+
entityId
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
_entityIdLookup = map;
|
|
36
|
+
return map;
|
|
37
|
+
}
|
|
38
|
+
const normalizeKey = (value) => value.replace(/["'`]/g, "").replace(/[\W]+/g, "_").replace(/__+/g, "_").toLowerCase();
|
|
39
|
+
const maybeSingularize = (value) => {
|
|
40
|
+
if (value.endsWith("ies")) return `${value.slice(0, -3)}y`;
|
|
41
|
+
if (value.endsWith("s")) return value.slice(0, -1);
|
|
42
|
+
return value;
|
|
43
|
+
};
|
|
44
|
+
function resolveEntityIdFromMetadata(meta) {
|
|
45
|
+
if (!meta) return null;
|
|
46
|
+
const candidates = [
|
|
47
|
+
meta.className,
|
|
48
|
+
meta.name,
|
|
49
|
+
meta.collection,
|
|
50
|
+
meta.tableName
|
|
51
|
+
].filter(Boolean);
|
|
52
|
+
for (const raw of candidates) {
|
|
53
|
+
const normalized = normalizeKey(raw);
|
|
54
|
+
const singular = maybeSingularize(normalized);
|
|
55
|
+
const snake = toSnake(raw);
|
|
56
|
+
const snakeSingular = maybeSingularize(snake);
|
|
57
|
+
const variants = [
|
|
58
|
+
normalized,
|
|
59
|
+
singular,
|
|
60
|
+
normalized.replace(/_/g, ""),
|
|
61
|
+
// Pascal-ish fallback
|
|
62
|
+
singular.replace(/_/g, ""),
|
|
63
|
+
snake,
|
|
64
|
+
snakeSingular
|
|
65
|
+
];
|
|
66
|
+
const lookup = getEntityIdLookup();
|
|
67
|
+
for (const candidate of variants) {
|
|
68
|
+
if (!candidate) continue;
|
|
69
|
+
const id = lookup.get(candidate);
|
|
70
|
+
if (id) return id;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
export {
|
|
76
|
+
getEntityIds,
|
|
77
|
+
registerEntityIds,
|
|
78
|
+
resolveEntityIdFromMetadata
|
|
79
|
+
};
|
|
80
|
+
//# sourceMappingURL=entityIds.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/encryption/entityIds.ts"],
|
|
4
|
+
"sourcesContent": ["import type { EntityMetadata } from '@mikro-orm/core'\n\n// Registration pattern for publishable packages\nexport type EntityIds = Record<string, Record<string, string>>\n\nlet _entityIds: EntityIds | null = null\nlet _entityIdLookup: Map<string, string> | null = null\n\nexport function registerEntityIds(E: EntityIds) {\n if (_entityIds !== null && process.env.NODE_ENV === 'development') {\n console.debug('[Bootstrap] Entity IDs re-registered (this may occur during HMR)')\n }\n _entityIds = E\n _entityIdLookup = null // Reset cache on re-registration\n}\n\n/**\n * Get registered entity IDs.\n *\n * @param throwIfNotRegistered - If true, throws error when entity IDs are not registered.\n * If false, returns empty object (useful during module load).\n * Default: true\n */\nexport function getEntityIds(throwIfNotRegistered = true): EntityIds {\n if (!_entityIds) {\n if (throwIfNotRegistered) {\n throw new Error('[Bootstrap] Entity IDs not registered. Call registerEntityIds() at bootstrap.')\n }\n return {} as EntityIds\n }\n return _entityIds\n}\n\nconst toSnake = (value: string): string =>\n value\n .replace(/([a-z0-9])([A-Z])/g, '$1_$2')\n .replace(/[\\s-]+/g, '_')\n .replace(/__+/g, '_')\n .toLowerCase()\n\nfunction getEntityIdLookup(): Map<string, string> {\n if (_entityIdLookup) return _entityIdLookup\n const E = getEntityIds()\n const map = new Map<string, string>()\n for (const mod of Object.values(E || {})) {\n for (const [key, entityId] of Object.entries(mod || {})) {\n const snake = toSnake(key)\n map.set(snake, entityId)\n // Also allow the original key and PascalCase class names to resolve\n map.set(key.toLowerCase(), entityId)\n map.set(\n key\n .split('_')\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join(''),\n entityId,\n )\n }\n }\n _entityIdLookup = map\n return map\n}\n\nconst normalizeKey = (value: string): string =>\n value\n .replace(/[\"'`]/g, '')\n .replace(/[\\W]+/g, '_')\n .replace(/__+/g, '_')\n .toLowerCase()\n\nconst maybeSingularize = (value: string): string => {\n if (value.endsWith('ies')) return `${value.slice(0, -3)}y`\n if (value.endsWith('s')) return value.slice(0, -1)\n return value\n}\n\nexport function resolveEntityIdFromMetadata(meta: EntityMetadata<any> | undefined): string | null {\n if (!meta) return null\n const candidates = [\n (meta as any).className,\n meta.name,\n (meta as any).collection,\n (meta as any).tableName,\n ].filter(Boolean) as string[]\n\n for (const raw of candidates) {\n const normalized = normalizeKey(raw)\n const singular = maybeSingularize(normalized)\n const snake = toSnake(raw)\n const snakeSingular = maybeSingularize(snake)\n const variants = [\n normalized,\n singular,\n normalized.replace(/_/g, ''), // Pascal-ish fallback\n singular.replace(/_/g, ''),\n snake,\n snakeSingular,\n ]\n const lookup = getEntityIdLookup()\n for (const candidate of variants) {\n if (!candidate) continue\n const id = lookup.get(candidate)\n if (id) return id\n }\n }\n return null\n}\n"],
|
|
5
|
+
"mappings": "AAKA,IAAI,aAA+B;AACnC,IAAI,kBAA8C;AAE3C,SAAS,kBAAkB,GAAc;AAC9C,MAAI,eAAe,QAAQ,QAAQ,IAAI,aAAa,eAAe;AACjE,YAAQ,MAAM,kEAAkE;AAAA,EAClF;AACA,eAAa;AACb,oBAAkB;AACpB;AASO,SAAS,aAAa,uBAAuB,MAAiB;AACnE,MAAI,CAAC,YAAY;AACf,QAAI,sBAAsB;AACxB,YAAM,IAAI,MAAM,+EAA+E;AAAA,IACjG;AACA,WAAO,CAAC;AAAA,EACV;AACA,SAAO;AACT;AAEA,MAAM,UAAU,CAAC,UACf,MACG,QAAQ,sBAAsB,OAAO,EACrC,QAAQ,WAAW,GAAG,EACtB,QAAQ,QAAQ,GAAG,EACnB,YAAY;AAEjB,SAAS,oBAAyC;AAChD,MAAI,gBAAiB,QAAO;AAC5B,QAAM,IAAI,aAAa;AACvB,QAAM,MAAM,oBAAI,IAAoB;AACpC,aAAW,OAAO,OAAO,OAAO,KAAK,CAAC,CAAC,GAAG;AACxC,eAAW,CAAC,KAAK,QAAQ,KAAK,OAAO,QAAQ,OAAO,CAAC,CAAC,GAAG;AACvD,YAAM,QAAQ,QAAQ,GAAG;AACzB,UAAI,IAAI,OAAO,QAAQ;AAEvB,UAAI,IAAI,IAAI,YAAY,GAAG,QAAQ;AACnC,UAAI;AAAA,QACF,IACG,MAAM,GAAG,EACT,IAAI,CAAC,SAAS,KAAK,OAAO,CAAC,EAAE,YAAY,IAAI,KAAK,MAAM,CAAC,CAAC,EAC1D,KAAK,EAAE;AAAA,QACV;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,oBAAkB;AAClB,SAAO;AACT;AAEA,MAAM,eAAe,CAAC,UACpB,MACG,QAAQ,UAAU,EAAE,EACpB,QAAQ,UAAU,GAAG,EACrB,QAAQ,QAAQ,GAAG,EACnB,YAAY;AAEjB,MAAM,mBAAmB,CAAC,UAA0B;AAClD,MAAI,MAAM,SAAS,KAAK,EAAG,QAAO,GAAG,MAAM,MAAM,GAAG,EAAE,CAAC;AACvD,MAAI,MAAM,SAAS,GAAG,EAAG,QAAO,MAAM,MAAM,GAAG,EAAE;AACjD,SAAO;AACT;AAEO,SAAS,4BAA4B,MAAsD;AAChG,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,aAAa;AAAA,IAChB,KAAa;AAAA,IACd,KAAK;AAAA,IACJ,KAAa;AAAA,IACb,KAAa;AAAA,EAChB,EAAE,OAAO,OAAO;AAEhB,aAAW,OAAO,YAAY;AAC5B,UAAM,aAAa,aAAa,GAAG;AACnC,UAAM,WAAW,iBAAiB,UAAU;AAC5C,UAAM,QAAQ,QAAQ,GAAG;AACzB,UAAM,gBAAgB,iBAAiB,KAAK;AAC5C,UAAM,WAAW;AAAA,MACf;AAAA,MACA;AAAA,MACA,WAAW,QAAQ,MAAM,EAAE;AAAA;AAAA,MAC3B,SAAS,QAAQ,MAAM,EAAE;AAAA,MACzB;AAAA,MACA;AAAA,IACF;AACA,UAAM,SAAS,kBAAkB;AACjC,eAAW,aAAa,UAAU;AAChC,UAAI,CAAC,UAAW;AAChB,YAAM,KAAK,OAAO,IAAI,SAAS;AAC/B,UAAI,GAAI,QAAO;AAAA,IACjB;AAAA,EACF;AACA,SAAO;AACT;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { decryptEntitiesWithFallbackScope } from "./subscriber.js";
|
|
2
|
+
async function findWithDecryption(em, entityName, where, options, scope) {
|
|
3
|
+
const records = await em.find(entityName, where, options);
|
|
4
|
+
if (!Array.isArray(records) || records.length === 0) return records ?? [];
|
|
5
|
+
await decryptEntitiesWithFallbackScope(records, {
|
|
6
|
+
em,
|
|
7
|
+
tenantId: scope?.tenantId ?? null,
|
|
8
|
+
organizationId: scope?.organizationId ?? null,
|
|
9
|
+
encryptionService: scope?.encryptionService ?? null
|
|
10
|
+
});
|
|
11
|
+
return records;
|
|
12
|
+
}
|
|
13
|
+
async function findOneWithDecryption(em, entityName, where, options, scope) {
|
|
14
|
+
const record = await em.findOne(entityName, where, options);
|
|
15
|
+
if (!record) return record;
|
|
16
|
+
await decryptEntitiesWithFallbackScope(record, {
|
|
17
|
+
em,
|
|
18
|
+
tenantId: scope?.tenantId ?? null,
|
|
19
|
+
organizationId: scope?.organizationId ?? null,
|
|
20
|
+
encryptionService: scope?.encryptionService ?? null
|
|
21
|
+
});
|
|
22
|
+
return record;
|
|
23
|
+
}
|
|
24
|
+
async function findAndCountWithDecryption(em, entityName, where, options, scope) {
|
|
25
|
+
const [recordsRaw, count] = await em.findAndCount(
|
|
26
|
+
entityName,
|
|
27
|
+
where,
|
|
28
|
+
options
|
|
29
|
+
);
|
|
30
|
+
const records = Array.isArray(recordsRaw) ? recordsRaw : [];
|
|
31
|
+
if (!records.length) return [records, count];
|
|
32
|
+
await decryptEntitiesWithFallbackScope(records, {
|
|
33
|
+
em,
|
|
34
|
+
tenantId: scope?.tenantId ?? null,
|
|
35
|
+
organizationId: scope?.organizationId ?? null,
|
|
36
|
+
encryptionService: scope?.encryptionService ?? null
|
|
37
|
+
});
|
|
38
|
+
return [records, count];
|
|
39
|
+
}
|
|
40
|
+
export {
|
|
41
|
+
findAndCountWithDecryption,
|
|
42
|
+
findOneWithDecryption,
|
|
43
|
+
findWithDecryption
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=find.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/encryption/find.ts"],
|
|
4
|
+
"sourcesContent": ["import type {\n EntityManager,\n EntityName,\n FilterQuery,\n FindOneOptions,\n FindOptions,\n} from '@mikro-orm/postgresql'\nimport { decryptEntitiesWithFallbackScope } from './subscriber'\nimport type { TenantDataEncryptionService } from './tenantDataEncryptionService'\n\nexport type DecryptionScope = {\n tenantId?: string | null\n organizationId?: string | null\n encryptionService?: TenantDataEncryptionService | null\n}\n\ntype AnyFindOptions<Entity extends object, Hint extends string = any> = FindOptions<Entity, Hint, any, any>\ntype AnyFindOneOptions<Entity extends object, Hint extends string = any> = FindOneOptions<Entity, Hint, any, any>\n\nexport async function findWithDecryption<Entity extends object, Hint extends string = any>(\n em: EntityManager,\n entityName: EntityName<Entity>,\n where: FilterQuery<Entity>,\n options?: AnyFindOptions<Entity, Hint>,\n scope?: DecryptionScope,\n): Promise<Entity[]> {\n const records = (await em.find<Entity, Hint, any, any>(entityName as any, where as any, options as any)) as any as\n | Entity[]\n | undefined\n if (!Array.isArray(records) || records.length === 0) return records ?? []\n await decryptEntitiesWithFallbackScope(records, {\n em,\n tenantId: scope?.tenantId ?? null,\n organizationId: scope?.organizationId ?? null,\n encryptionService: scope?.encryptionService ?? null,\n })\n return records\n}\n\nexport async function findOneWithDecryption<Entity extends object, Hint extends string = any>(\n em: EntityManager,\n entityName: EntityName<Entity>,\n where: FilterQuery<Entity>,\n options?: AnyFindOneOptions<Entity, Hint>,\n scope?: DecryptionScope,\n): Promise<Entity | null> {\n const record = (await em.findOne<Entity, Hint, any, any>(entityName as any, where as any, options as any)) as any as\n | Entity\n | null\n if (!record) return record\n await decryptEntitiesWithFallbackScope(record, {\n em,\n tenantId: scope?.tenantId ?? null,\n organizationId: scope?.organizationId ?? null,\n encryptionService: scope?.encryptionService ?? null,\n })\n return record\n}\n\nexport async function findAndCountWithDecryption<Entity extends object, Hint extends string = any>(\n em: EntityManager,\n entityName: EntityName<Entity>,\n where: FilterQuery<Entity>,\n options?: AnyFindOptions<Entity, Hint>,\n scope?: DecryptionScope,\n): Promise<[Entity[], number]> {\n const [recordsRaw, count] = await em.findAndCount<Entity, Hint, any, any>(\n entityName as any,\n where as any,\n options as any,\n ) as any as [Entity[] | undefined, number]\n const records = Array.isArray(recordsRaw) ? recordsRaw : []\n if (!records.length) return [records, count]\n await decryptEntitiesWithFallbackScope(records, {\n em,\n tenantId: scope?.tenantId ?? null,\n organizationId: scope?.organizationId ?? null,\n encryptionService: scope?.encryptionService ?? null,\n })\n return [records, count]\n}\n"],
|
|
5
|
+
"mappings": "AAOA,SAAS,wCAAwC;AAYjD,eAAsB,mBACpB,IACA,YACA,OACA,SACA,OACmB;AACnB,QAAM,UAAW,MAAM,GAAG,KAA6B,YAAmB,OAAc,OAAc;AAGtG,MAAI,CAAC,MAAM,QAAQ,OAAO,KAAK,QAAQ,WAAW,EAAG,QAAO,WAAW,CAAC;AACxE,QAAM,iCAAiC,SAAS;AAAA,IAC9C;AAAA,IACA,UAAU,OAAO,YAAY;AAAA,IAC7B,gBAAgB,OAAO,kBAAkB;AAAA,IACzC,mBAAmB,OAAO,qBAAqB;AAAA,EACjD,CAAC;AACD,SAAO;AACT;AAEA,eAAsB,sBACpB,IACA,YACA,OACA,SACA,OACwB;AACxB,QAAM,SAAU,MAAM,GAAG,QAAgC,YAAmB,OAAc,OAAc;AAGxG,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,iCAAiC,QAAQ;AAAA,IAC7C;AAAA,IACA,UAAU,OAAO,YAAY;AAAA,IAC7B,gBAAgB,OAAO,kBAAkB;AAAA,IACzC,mBAAmB,OAAO,qBAAqB;AAAA,EACjD,CAAC;AACD,SAAO;AACT;AAEA,eAAsB,2BACpB,IACA,YACA,OACA,SACA,OAC6B;AAC7B,QAAM,CAAC,YAAY,KAAK,IAAI,MAAM,GAAG;AAAA,IACnC;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,QAAM,UAAU,MAAM,QAAQ,UAAU,IAAI,aAAa,CAAC;AAC1D,MAAI,CAAC,QAAQ,OAAQ,QAAO,CAAC,SAAS,KAAK;AAC3C,QAAM,iCAAiC,SAAS;AAAA,IAC9C;AAAA,IACA,UAAU,OAAO,YAAY;AAAA,IAC7B,gBAAgB,OAAO,kBAAkB;AAAA,IACzC,mBAAmB,OAAO,qBAAqB;AAAA,EACjD,CAAC;AACD,SAAO,CAAC,SAAS,KAAK;AACxB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { decryptCustomFieldValue } from "./customFieldValues.js";
|
|
2
|
+
async function decryptValue(value, scope, service, cache) {
|
|
3
|
+
if (Array.isArray(value)) {
|
|
4
|
+
return Promise.all(value.map((entry) => decryptCustomFieldValue(entry, scope.tenantId, service, cache)));
|
|
5
|
+
}
|
|
6
|
+
return decryptCustomFieldValue(value, scope.tenantId, service, cache);
|
|
7
|
+
}
|
|
8
|
+
async function decryptIndexDocCustomFields(doc, scope, service, cache) {
|
|
9
|
+
const keys = Object.keys(doc).filter((key) => key.startsWith("cf:") || key.startsWith("cf_"));
|
|
10
|
+
if (!keys.length) return doc;
|
|
11
|
+
const working = { ...doc };
|
|
12
|
+
await Promise.all(
|
|
13
|
+
keys.map(async (key) => {
|
|
14
|
+
try {
|
|
15
|
+
working[key] = await decryptValue(working[key], scope, service, cache);
|
|
16
|
+
} catch {
|
|
17
|
+
}
|
|
18
|
+
})
|
|
19
|
+
);
|
|
20
|
+
return working;
|
|
21
|
+
}
|
|
22
|
+
async function decryptIndexDocForSearch(entityId, doc, scope, service, cache) {
|
|
23
|
+
if (!service || typeof service.decryptEntityPayload !== "function") {
|
|
24
|
+
return decryptIndexDocCustomFields(doc, scope, service, cache);
|
|
25
|
+
}
|
|
26
|
+
if (service.isEnabled?.() === false) {
|
|
27
|
+
return decryptIndexDocCustomFields(doc, scope, service, cache);
|
|
28
|
+
}
|
|
29
|
+
let working = doc;
|
|
30
|
+
const decryptEntity = async (targetEntityId) => {
|
|
31
|
+
const decrypted = await service.decryptEntityPayload(
|
|
32
|
+
targetEntityId,
|
|
33
|
+
working,
|
|
34
|
+
scope.tenantId ?? null,
|
|
35
|
+
scope.organizationId ?? null
|
|
36
|
+
);
|
|
37
|
+
working = { ...working, ...decrypted };
|
|
38
|
+
};
|
|
39
|
+
await decryptEntity(entityId);
|
|
40
|
+
if (entityId === "customers:customer_person_profile" || entityId === "customers:customer_company_profile") {
|
|
41
|
+
await decryptEntity("customers:customer_entity");
|
|
42
|
+
}
|
|
43
|
+
return decryptIndexDocCustomFields(working, scope, service, cache);
|
|
44
|
+
}
|
|
45
|
+
async function encryptIndexDocForStorage(entityId, doc, scope, service) {
|
|
46
|
+
if (!service || typeof service.encryptEntityPayload !== "function") return doc;
|
|
47
|
+
if (service.isEnabled?.() === false) return doc;
|
|
48
|
+
let working = doc;
|
|
49
|
+
const encryptEntity = async (targetEntityId) => {
|
|
50
|
+
const encrypted = await service.encryptEntityPayload(
|
|
51
|
+
targetEntityId,
|
|
52
|
+
working,
|
|
53
|
+
scope.tenantId ?? null,
|
|
54
|
+
scope.organizationId ?? null
|
|
55
|
+
);
|
|
56
|
+
working = { ...working, ...encrypted };
|
|
57
|
+
};
|
|
58
|
+
await encryptEntity(entityId);
|
|
59
|
+
if (entityId === "customers:customer_person_profile" || entityId === "customers:customer_company_profile") {
|
|
60
|
+
await encryptEntity("customers:customer_entity");
|
|
61
|
+
}
|
|
62
|
+
return working;
|
|
63
|
+
}
|
|
64
|
+
export {
|
|
65
|
+
decryptIndexDocCustomFields,
|
|
66
|
+
decryptIndexDocForSearch,
|
|
67
|
+
encryptIndexDocForStorage
|
|
68
|
+
};
|
|
69
|
+
//# sourceMappingURL=indexDoc.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/encryption/indexDoc.ts"],
|
|
4
|
+
"sourcesContent": ["import { decryptCustomFieldValue } from './customFieldValues'\nimport type { TenantDataEncryptionService } from './tenantDataEncryptionService'\n\nexport type IndexDocScope = {\n tenantId: string | null\n organizationId?: string | null\n}\n\nasync function decryptValue(\n value: unknown,\n scope: IndexDocScope,\n service: TenantDataEncryptionService | null,\n cache?: Map<string | null, string | null>,\n): Promise<unknown> {\n if (Array.isArray(value)) {\n return Promise.all(value.map((entry) => decryptCustomFieldValue(entry, scope.tenantId, service, cache)))\n }\n return decryptCustomFieldValue(value, scope.tenantId, service, cache)\n}\n\nexport async function decryptIndexDocCustomFields(\n doc: Record<string, unknown>,\n scope: IndexDocScope,\n service: TenantDataEncryptionService | null,\n cache?: Map<string | null, string | null>,\n): Promise<Record<string, unknown>> {\n // HybridQueryEngine aliases cf keys as `cf_<key>` (sanitized), while index docs use `cf:<key>`.\n // Support both shapes to keep decryption consistent across query paths.\n const keys = Object.keys(doc).filter((key) => key.startsWith('cf:') || key.startsWith('cf_'))\n if (!keys.length) return doc\n\n const working: Record<string, unknown> = { ...doc }\n await Promise.all(\n keys.map(async (key) => {\n try {\n working[key] = await decryptValue(working[key], scope, service, cache)\n } catch {\n // ignore; keep original value\n }\n }),\n )\n return working\n}\n\nexport async function decryptIndexDocForSearch(\n entityId: string,\n doc: Record<string, unknown>,\n scope: IndexDocScope,\n service: TenantDataEncryptionService | null,\n cache?: Map<string | null, string | null>,\n): Promise<Record<string, unknown>> {\n if (!service || typeof service.decryptEntityPayload !== 'function') {\n return decryptIndexDocCustomFields(doc, scope, service, cache)\n }\n if (service.isEnabled?.() === false) {\n return decryptIndexDocCustomFields(doc, scope, service, cache)\n }\n\n let working: Record<string, unknown> = doc\n const decryptEntity = async (targetEntityId: string) => {\n const decrypted = await service.decryptEntityPayload(\n targetEntityId,\n working,\n scope.tenantId ?? null,\n scope.organizationId ?? null,\n )\n working = { ...working, ...decrypted }\n }\n\n await decryptEntity(entityId)\n if (entityId === 'customers:customer_person_profile' || entityId === 'customers:customer_company_profile') {\n await decryptEntity('customers:customer_entity')\n }\n\n return decryptIndexDocCustomFields(working, scope, service, cache)\n}\n\nexport async function encryptIndexDocForStorage(\n entityId: string,\n doc: Record<string, unknown>,\n scope: IndexDocScope,\n service: TenantDataEncryptionService | null,\n): Promise<Record<string, unknown>> {\n if (!service || typeof service.encryptEntityPayload !== 'function') return doc\n if (service.isEnabled?.() === false) return doc\n\n let working: Record<string, unknown> = doc\n const encryptEntity = async (targetEntityId: string) => {\n const encrypted = await service.encryptEntityPayload(\n targetEntityId,\n working,\n scope.tenantId ?? null,\n scope.organizationId ?? null,\n )\n working = { ...working, ...encrypted }\n }\n\n await encryptEntity(entityId)\n if (entityId === 'customers:customer_person_profile' || entityId === 'customers:customer_company_profile') {\n await encryptEntity('customers:customer_entity')\n }\n\n return working\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,+BAA+B;AAQxC,eAAe,aACb,OACA,OACA,SACA,OACkB;AAClB,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO,QAAQ,IAAI,MAAM,IAAI,CAAC,UAAU,wBAAwB,OAAO,MAAM,UAAU,SAAS,KAAK,CAAC,CAAC;AAAA,EACzG;AACA,SAAO,wBAAwB,OAAO,MAAM,UAAU,SAAS,KAAK;AACtE;AAEA,eAAsB,4BACpB,KACA,OACA,SACA,OACkC;AAGlC,QAAM,OAAO,OAAO,KAAK,GAAG,EAAE,OAAO,CAAC,QAAQ,IAAI,WAAW,KAAK,KAAK,IAAI,WAAW,KAAK,CAAC;AAC5F,MAAI,CAAC,KAAK,OAAQ,QAAO;AAEzB,QAAM,UAAmC,EAAE,GAAG,IAAI;AAClD,QAAM,QAAQ;AAAA,IACZ,KAAK,IAAI,OAAO,QAAQ;AACtB,UAAI;AACF,gBAAQ,GAAG,IAAI,MAAM,aAAa,QAAQ,GAAG,GAAG,OAAO,SAAS,KAAK;AAAA,MACvE,QAAQ;AAAA,MAER;AAAA,IACF,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAEA,eAAsB,yBACpB,UACA,KACA,OACA,SACA,OACkC;AAClC,MAAI,CAAC,WAAW,OAAO,QAAQ,yBAAyB,YAAY;AAClE,WAAO,4BAA4B,KAAK,OAAO,SAAS,KAAK;AAAA,EAC/D;AACA,MAAI,QAAQ,YAAY,MAAM,OAAO;AACnC,WAAO,4BAA4B,KAAK,OAAO,SAAS,KAAK;AAAA,EAC/D;AAEA,MAAI,UAAmC;AACvC,QAAM,gBAAgB,OAAO,mBAA2B;AACtD,UAAM,YAAY,MAAM,QAAQ;AAAA,MAC9B;AAAA,MACA;AAAA,MACA,MAAM,YAAY;AAAA,MAClB,MAAM,kBAAkB;AAAA,IAC1B;AACA,cAAU,EAAE,GAAG,SAAS,GAAG,UAAU;AAAA,EACvC;AAEA,QAAM,cAAc,QAAQ;AAC5B,MAAI,aAAa,uCAAuC,aAAa,sCAAsC;AACzG,UAAM,cAAc,2BAA2B;AAAA,EACjD;AAEA,SAAO,4BAA4B,SAAS,OAAO,SAAS,KAAK;AACnE;AAEA,eAAsB,0BACpB,UACA,KACA,OACA,SACkC;AAClC,MAAI,CAAC,WAAW,OAAO,QAAQ,yBAAyB,WAAY,QAAO;AAC3E,MAAI,QAAQ,YAAY,MAAM,MAAO,QAAO;AAE5C,MAAI,UAAmC;AACvC,QAAM,gBAAgB,OAAO,mBAA2B;AACtD,UAAM,YAAY,MAAM,QAAQ;AAAA,MAC9B;AAAA,MACA;AAAA,MACA,MAAM,YAAY;AAAA,MAClB,MAAM,kBAAkB;AAAA,IAC1B;AACA,cAAU,EAAE,GAAG,SAAS,GAAG,UAAU;AAAA,EACvC;AAEA,QAAM,cAAc,QAAQ;AAC5B,MAAI,aAAa,uCAAuC,aAAa,sCAAsC;AACzG,UAAM,cAAc,2BAA2B;AAAA,EACjD;AAEA,SAAO;AACT;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|