@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,128 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
const scopes = /* @__PURE__ */ new Map();
|
|
3
|
+
const MODIFIER_SYNONYMS = {
|
|
4
|
+
cmd: "meta",
|
|
5
|
+
command: "meta",
|
|
6
|
+
option: "alt",
|
|
7
|
+
opt: "alt",
|
|
8
|
+
control: "ctrl",
|
|
9
|
+
ctrl: "ctrl",
|
|
10
|
+
shift: "shift",
|
|
11
|
+
meta: "meta",
|
|
12
|
+
alt: "alt"
|
|
13
|
+
};
|
|
14
|
+
let listenersAttached = false;
|
|
15
|
+
const THIRTY_FPS_INTERVAL = 1e3 / 30;
|
|
16
|
+
function normalizeToken(token) {
|
|
17
|
+
const trimmed = token.trim().toLowerCase();
|
|
18
|
+
if (!trimmed) return "";
|
|
19
|
+
if (trimmed in MODIFIER_SYNONYMS) return MODIFIER_SYNONYMS[trimmed];
|
|
20
|
+
if (trimmed === "return") return "enter";
|
|
21
|
+
if (trimmed === "space") return " ";
|
|
22
|
+
return trimmed;
|
|
23
|
+
}
|
|
24
|
+
function serializeCombination(tokens) {
|
|
25
|
+
const filtered = tokens.filter(Boolean);
|
|
26
|
+
filtered.sort();
|
|
27
|
+
return filtered.join("+");
|
|
28
|
+
}
|
|
29
|
+
function parseHotkeys(hotkeys) {
|
|
30
|
+
return new Set(
|
|
31
|
+
hotkeys.split(/\s+/).map(
|
|
32
|
+
(combo) => serializeCombination(
|
|
33
|
+
combo.split("+").map(normalizeToken).filter(Boolean)
|
|
34
|
+
)
|
|
35
|
+
).filter(Boolean)
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
function createRegistration(hotkeys, callback, debounce) {
|
|
39
|
+
const combos = parseHotkeys(hotkeys);
|
|
40
|
+
return {
|
|
41
|
+
combos,
|
|
42
|
+
callback,
|
|
43
|
+
debounce: Math.max(debounce, THIRTY_FPS_INTERVAL),
|
|
44
|
+
lastTriggered: 0
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function activeCombination(event) {
|
|
48
|
+
const keys = [];
|
|
49
|
+
if (event.metaKey) keys.push("meta");
|
|
50
|
+
if (event.ctrlKey) keys.push("ctrl");
|
|
51
|
+
if (event.altKey) keys.push("alt");
|
|
52
|
+
if (event.shiftKey) keys.push("shift");
|
|
53
|
+
const key = normalizeToken(event.key);
|
|
54
|
+
if (key && !(key in MODIFIER_SYNONYMS)) {
|
|
55
|
+
keys.push(key.length === 1 ? key : normalizeToken(key));
|
|
56
|
+
} else if (keys.length === 0) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
return serializeCombination(keys);
|
|
60
|
+
}
|
|
61
|
+
function handleKeydown(event) {
|
|
62
|
+
if (event.defaultPrevented) return;
|
|
63
|
+
const combo = activeCombination(event);
|
|
64
|
+
if (!combo) return;
|
|
65
|
+
const now = Date.now();
|
|
66
|
+
scopes.forEach((registrations) => {
|
|
67
|
+
registrations.forEach((registration) => {
|
|
68
|
+
if (!registration.combos.has(combo)) return;
|
|
69
|
+
if (event.repeat && now - registration.lastTriggered < registration.debounce) return;
|
|
70
|
+
if (now - registration.lastTriggered < registration.debounce) return;
|
|
71
|
+
registration.lastTriggered = now;
|
|
72
|
+
registration.callback(event);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
function handleKeyup() {
|
|
77
|
+
}
|
|
78
|
+
function ensureListeners() {
|
|
79
|
+
if (listenersAttached) return;
|
|
80
|
+
if (typeof document === "undefined") return;
|
|
81
|
+
document.addEventListener("keydown", handleKeydown);
|
|
82
|
+
document.addEventListener("keyup", handleKeyup);
|
|
83
|
+
listenersAttached = true;
|
|
84
|
+
}
|
|
85
|
+
function detachListenersIfIdle() {
|
|
86
|
+
if (!listenersAttached) return;
|
|
87
|
+
if (scopes.size > 0) return;
|
|
88
|
+
if (typeof document === "undefined") return;
|
|
89
|
+
document.removeEventListener("keydown", handleKeydown);
|
|
90
|
+
document.removeEventListener("keyup", handleKeyup);
|
|
91
|
+
listenersAttached = false;
|
|
92
|
+
}
|
|
93
|
+
function registerHotkey(hotkeys, scopeName, callback, debounceTimeInMilliseconds = 150) {
|
|
94
|
+
if (typeof document === "undefined") {
|
|
95
|
+
return {
|
|
96
|
+
bind() {
|
|
97
|
+
},
|
|
98
|
+
unbind() {
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const registration = createRegistration(hotkeys, callback, debounceTimeInMilliseconds);
|
|
103
|
+
let scope = scopes.get(scopeName);
|
|
104
|
+
const bind = () => {
|
|
105
|
+
if (!scope) {
|
|
106
|
+
scope = /* @__PURE__ */ new Set();
|
|
107
|
+
scopes.set(scopeName, scope);
|
|
108
|
+
}
|
|
109
|
+
scope.add(registration);
|
|
110
|
+
ensureListeners();
|
|
111
|
+
};
|
|
112
|
+
const unbind = () => {
|
|
113
|
+
const existingScope = scopes.get(scopeName);
|
|
114
|
+
if (existingScope) {
|
|
115
|
+
existingScope.delete(registration);
|
|
116
|
+
if (existingScope.size === 0) {
|
|
117
|
+
scopes.delete(scopeName);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
detachListenersIfIdle();
|
|
121
|
+
};
|
|
122
|
+
bind();
|
|
123
|
+
return { bind, unbind };
|
|
124
|
+
}
|
|
125
|
+
export {
|
|
126
|
+
registerHotkey
|
|
127
|
+
};
|
|
128
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/hotkeys/index.ts"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\ntype HotkeyRegistration = {\n combos: Set<string>\n callback: (event: KeyboardEvent) => void\n debounce: number\n lastTriggered: number\n}\n\nconst scopes = new Map<string, Set<HotkeyRegistration>>()\n\nconst MODIFIER_SYNONYMS: Record<string, string> = {\n cmd: 'meta',\n command: 'meta',\n option: 'alt',\n opt: 'alt',\n control: 'ctrl',\n ctrl: 'ctrl',\n shift: 'shift',\n meta: 'meta',\n alt: 'alt',\n}\n\nlet listenersAttached = false\n\nconst THIRTY_FPS_INTERVAL = 1000 / 30\n\nfunction normalizeToken(token: string): string {\n const trimmed = token.trim().toLowerCase()\n if (!trimmed) return ''\n if (trimmed in MODIFIER_SYNONYMS) return MODIFIER_SYNONYMS[trimmed]\n if (trimmed === 'return') return 'enter'\n if (trimmed === 'space') return ' '\n return trimmed\n}\n\nfunction serializeCombination(tokens: string[]): string {\n const filtered = tokens.filter(Boolean)\n filtered.sort()\n return filtered.join('+')\n}\n\nfunction parseHotkeys(hotkeys: string): Set<string> {\n return new Set(\n hotkeys\n .split(/\\s+/)\n .map((combo) =>\n serializeCombination(\n combo\n .split('+')\n .map(normalizeToken)\n .filter(Boolean),\n ),\n )\n .filter(Boolean),\n )\n}\n\nfunction createRegistration(\n hotkeys: string,\n callback: (event: KeyboardEvent) => void,\n debounce: number,\n): HotkeyRegistration {\n const combos = parseHotkeys(hotkeys)\n return {\n combos,\n callback,\n debounce: Math.max(debounce, THIRTY_FPS_INTERVAL),\n lastTriggered: 0,\n }\n}\n\nfunction activeCombination(event: KeyboardEvent): string | null {\n const keys: string[] = []\n if (event.metaKey) keys.push('meta')\n if (event.ctrlKey) keys.push('ctrl')\n if (event.altKey) keys.push('alt')\n if (event.shiftKey) keys.push('shift')\n\n const key = normalizeToken(event.key)\n if (key && !(key in MODIFIER_SYNONYMS)) {\n keys.push(key.length === 1 ? key : normalizeToken(key))\n } else if (keys.length === 0) {\n // Ignore pure modifier presses\n return null\n }\n\n return serializeCombination(keys)\n}\n\nfunction handleKeydown(event: KeyboardEvent) {\n if (event.defaultPrevented) return\n const combo = activeCombination(event)\n if (!combo) return\n\n const now = Date.now()\n scopes.forEach((registrations) => {\n registrations.forEach((registration) => {\n if (!registration.combos.has(combo)) return\n if (event.repeat && now - registration.lastTriggered < registration.debounce) return\n if (now - registration.lastTriggered < registration.debounce) return\n registration.lastTriggered = now\n registration.callback(event)\n })\n })\n}\n\nfunction handleKeyup() {\n // No-op placeholder for future extensibility (mirrors API pattern from article reference)\n}\n\nfunction ensureListeners() {\n if (listenersAttached) return\n if (typeof document === 'undefined') return\n document.addEventListener('keydown', handleKeydown)\n document.addEventListener('keyup', handleKeyup)\n listenersAttached = true\n}\n\nfunction detachListenersIfIdle() {\n if (!listenersAttached) return\n if (scopes.size > 0) return\n if (typeof document === 'undefined') return\n document.removeEventListener('keydown', handleKeydown)\n document.removeEventListener('keyup', handleKeyup)\n listenersAttached = false\n}\n\nexport function registerHotkey(\n hotkeys: string,\n scopeName: string,\n callback: (event: KeyboardEvent) => void,\n debounceTimeInMilliseconds = 150,\n) {\n if (typeof document === 'undefined') {\n return {\n bind() {},\n unbind() {},\n }\n }\n\n const registration = createRegistration(hotkeys, callback, debounceTimeInMilliseconds)\n let scope = scopes.get(scopeName)\n\n const bind = () => {\n if (!scope) {\n scope = new Set()\n scopes.set(scopeName, scope)\n }\n scope.add(registration)\n ensureListeners()\n }\n\n const unbind = () => {\n const existingScope = scopes.get(scopeName)\n if (existingScope) {\n existingScope.delete(registration)\n if (existingScope.size === 0) {\n scopes.delete(scopeName)\n }\n }\n detachListenersIfIdle()\n }\n\n bind()\n\n return { bind, unbind }\n}\n"],
|
|
5
|
+
"mappings": ";AASA,MAAM,SAAS,oBAAI,IAAqC;AAExD,MAAM,oBAA4C;AAAA,EAChD,KAAK;AAAA,EACL,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,KAAK;AAAA,EACL,SAAS;AAAA,EACT,MAAM;AAAA,EACN,OAAO;AAAA,EACP,MAAM;AAAA,EACN,KAAK;AACP;AAEA,IAAI,oBAAoB;AAExB,MAAM,sBAAsB,MAAO;AAEnC,SAAS,eAAe,OAAuB;AAC7C,QAAM,UAAU,MAAM,KAAK,EAAE,YAAY;AACzC,MAAI,CAAC,QAAS,QAAO;AACrB,MAAI,WAAW,kBAAmB,QAAO,kBAAkB,OAAO;AAClE,MAAI,YAAY,SAAU,QAAO;AACjC,MAAI,YAAY,QAAS,QAAO;AAChC,SAAO;AACT;AAEA,SAAS,qBAAqB,QAA0B;AACtD,QAAM,WAAW,OAAO,OAAO,OAAO;AACtC,WAAS,KAAK;AACd,SAAO,SAAS,KAAK,GAAG;AAC1B;AAEA,SAAS,aAAa,SAA8B;AAClD,SAAO,IAAI;AAAA,IACT,QACG,MAAM,KAAK,EACX;AAAA,MAAI,CAAC,UACJ;AAAA,QACE,MACG,MAAM,GAAG,EACT,IAAI,cAAc,EAClB,OAAO,OAAO;AAAA,MACnB;AAAA,IACF,EACC,OAAO,OAAO;AAAA,EACnB;AACF;AAEA,SAAS,mBACP,SACA,UACA,UACoB;AACpB,QAAM,SAAS,aAAa,OAAO;AACnC,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,UAAU,KAAK,IAAI,UAAU,mBAAmB;AAAA,IAChD,eAAe;AAAA,EACjB;AACF;AAEA,SAAS,kBAAkB,OAAqC;AAC9D,QAAM,OAAiB,CAAC;AACxB,MAAI,MAAM,QAAS,MAAK,KAAK,MAAM;AACnC,MAAI,MAAM,QAAS,MAAK,KAAK,MAAM;AACnC,MAAI,MAAM,OAAQ,MAAK,KAAK,KAAK;AACjC,MAAI,MAAM,SAAU,MAAK,KAAK,OAAO;AAErC,QAAM,MAAM,eAAe,MAAM,GAAG;AACpC,MAAI,OAAO,EAAE,OAAO,oBAAoB;AACtC,SAAK,KAAK,IAAI,WAAW,IAAI,MAAM,eAAe,GAAG,CAAC;AAAA,EACxD,WAAW,KAAK,WAAW,GAAG;AAE5B,WAAO;AAAA,EACT;AAEA,SAAO,qBAAqB,IAAI;AAClC;AAEA,SAAS,cAAc,OAAsB;AAC3C,MAAI,MAAM,iBAAkB;AAC5B,QAAM,QAAQ,kBAAkB,KAAK;AACrC,MAAI,CAAC,MAAO;AAEZ,QAAM,MAAM,KAAK,IAAI;AACrB,SAAO,QAAQ,CAAC,kBAAkB;AAChC,kBAAc,QAAQ,CAAC,iBAAiB;AACtC,UAAI,CAAC,aAAa,OAAO,IAAI,KAAK,EAAG;AACrC,UAAI,MAAM,UAAU,MAAM,aAAa,gBAAgB,aAAa,SAAU;AAC9E,UAAI,MAAM,aAAa,gBAAgB,aAAa,SAAU;AAC9D,mBAAa,gBAAgB;AAC7B,mBAAa,SAAS,KAAK;AAAA,IAC7B,CAAC;AAAA,EACH,CAAC;AACH;AAEA,SAAS,cAAc;AAEvB;AAEA,SAAS,kBAAkB;AACzB,MAAI,kBAAmB;AACvB,MAAI,OAAO,aAAa,YAAa;AACrC,WAAS,iBAAiB,WAAW,aAAa;AAClD,WAAS,iBAAiB,SAAS,WAAW;AAC9C,sBAAoB;AACtB;AAEA,SAAS,wBAAwB;AAC/B,MAAI,CAAC,kBAAmB;AACxB,MAAI,OAAO,OAAO,EAAG;AACrB,MAAI,OAAO,aAAa,YAAa;AACrC,WAAS,oBAAoB,WAAW,aAAa;AACrD,WAAS,oBAAoB,SAAS,WAAW;AACjD,sBAAoB;AACtB;AAEO,SAAS,eACd,SACA,WACA,UACA,6BAA6B,KAC7B;AACA,MAAI,OAAO,aAAa,aAAa;AACnC,WAAO;AAAA,MACL,OAAO;AAAA,MAAC;AAAA,MACR,SAAS;AAAA,MAAC;AAAA,IACZ;AAAA,EACF;AAEA,QAAM,eAAe,mBAAmB,SAAS,UAAU,0BAA0B;AACrF,MAAI,QAAQ,OAAO,IAAI,SAAS;AAEhC,QAAM,OAAO,MAAM;AACjB,QAAI,CAAC,OAAO;AACV,cAAQ,oBAAI,IAAI;AAChB,aAAO,IAAI,WAAW,KAAK;AAAA,IAC7B;AACA,UAAM,IAAI,YAAY;AACtB,oBAAgB;AAAA,EAClB;AAEA,QAAM,SAAS,MAAM;AACnB,UAAM,gBAAgB,OAAO,IAAI,SAAS;AAC1C,QAAI,eAAe;AACjB,oBAAc,OAAO,YAAY;AACjC,UAAI,cAAc,SAAS,GAAG;AAC5B,eAAO,OAAO,SAAS;AAAA,MACzB;AAAA,IACF;AACA,0BAAsB;AAAA,EACxB;AAEA,OAAK;AAEL,SAAO,EAAE,MAAM,OAAO;AACxB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
let _appDictionaryLoader = null;
|
|
2
|
+
function registerAppDictionaryLoader(loader) {
|
|
3
|
+
_appDictionaryLoader = loader;
|
|
4
|
+
}
|
|
5
|
+
async function loadAppDictionary(locale) {
|
|
6
|
+
if (!_appDictionaryLoader) return {};
|
|
7
|
+
try {
|
|
8
|
+
return await _appDictionaryLoader(locale);
|
|
9
|
+
} catch {
|
|
10
|
+
return {};
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export {
|
|
14
|
+
loadAppDictionary,
|
|
15
|
+
registerAppDictionaryLoader
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=app-dictionaries.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/i18n/app-dictionaries.ts"],
|
|
4
|
+
"sourcesContent": ["import type { Locale } from './config'\n\ntype DictionaryLoader = (locale: Locale) => Promise<Record<string, unknown>>\n\nlet _appDictionaryLoader: DictionaryLoader | null = null\n\nexport function registerAppDictionaryLoader(loader: DictionaryLoader): void {\n _appDictionaryLoader = loader\n}\n\nexport async function loadAppDictionary(locale: Locale): Promise<Record<string, unknown>> {\n if (!_appDictionaryLoader) return {}\n try {\n return await _appDictionaryLoader(locale)\n } catch {\n return {}\n }\n}\n"],
|
|
5
|
+
"mappings": "AAIA,IAAI,uBAAgD;AAE7C,SAAS,4BAA4B,QAAgC;AAC1E,yBAAuB;AACzB;AAEA,eAAsB,kBAAkB,QAAkD;AACxF,MAAI,CAAC,qBAAsB,QAAO,CAAC;AACnC,MAAI;AACF,WAAO,MAAM,qBAAqB,MAAM;AAAA,EAC1C,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/i18n/config.ts"],
|
|
4
|
+
"sourcesContent": ["export type Locale = 'en' | 'pl' | 'es' | 'de'\n\nexport const locales: Locale[] = ['en', 'pl', 'es', 'de']\nexport const defaultLocale: Locale = 'en'\n"],
|
|
5
|
+
"mappings": "AAEO,MAAM,UAAoB,CAAC,MAAM,MAAM,MAAM,IAAI;AACjD,MAAM,gBAAwB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useContext, useMemo } from "react";
|
|
4
|
+
const I18nContext = createContext(null);
|
|
5
|
+
function format(template, params) {
|
|
6
|
+
if (!params) return template;
|
|
7
|
+
return template.replace(/\{\{(\w+)\}\}|\{(\w+)\}/g, (_, doubleKey, singleKey) => {
|
|
8
|
+
const key = doubleKey ?? singleKey;
|
|
9
|
+
if (!key) return _;
|
|
10
|
+
const value = params[key];
|
|
11
|
+
if (value === void 0) {
|
|
12
|
+
return doubleKey ? `{{${key}}}` : `{${key}}`;
|
|
13
|
+
}
|
|
14
|
+
return String(value);
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
function I18nProvider({ children, locale, dict }) {
|
|
18
|
+
const value = useMemo(() => ({
|
|
19
|
+
locale,
|
|
20
|
+
t: (key, fallbackOrParams, params) => {
|
|
21
|
+
let fallback;
|
|
22
|
+
let resolvedParams;
|
|
23
|
+
if (typeof fallbackOrParams === "string") {
|
|
24
|
+
fallback = fallbackOrParams;
|
|
25
|
+
resolvedParams = params;
|
|
26
|
+
} else {
|
|
27
|
+
resolvedParams = fallbackOrParams ?? params;
|
|
28
|
+
}
|
|
29
|
+
const template = dict[key] ?? fallback ?? key;
|
|
30
|
+
return format(template, resolvedParams);
|
|
31
|
+
}
|
|
32
|
+
}), [locale, dict]);
|
|
33
|
+
return /* @__PURE__ */ jsx(I18nContext.Provider, { value, children });
|
|
34
|
+
}
|
|
35
|
+
function useT() {
|
|
36
|
+
const ctx = useContext(I18nContext);
|
|
37
|
+
if (!ctx) throw new Error("useT must be used within I18nProvider");
|
|
38
|
+
return ctx.t;
|
|
39
|
+
}
|
|
40
|
+
function useLocale() {
|
|
41
|
+
const ctx = useContext(I18nContext);
|
|
42
|
+
if (!ctx) throw new Error("useLocale must be used within I18nProvider");
|
|
43
|
+
return ctx.locale;
|
|
44
|
+
}
|
|
45
|
+
export {
|
|
46
|
+
I18nProvider,
|
|
47
|
+
useLocale,
|
|
48
|
+
useT
|
|
49
|
+
};
|
|
50
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/i18n/context.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport React, { createContext, useContext, useMemo } from 'react'\nimport type { Locale } from './config'\n\nexport type Dict = Record<string, string>\n\nexport type TranslateParams = Record<string, string | number>\n\nexport type TranslateFn = (\n key: string,\n fallbackOrParams?: string | TranslateParams,\n params?: TranslateParams\n) => string\n\nexport type I18nContextValue = {\n locale: Locale\n t: TranslateFn\n}\n\nconst I18nContext = createContext<I18nContextValue | null>(null)\n\nfunction format(template: string, params?: TranslateParams) {\n if (!params) return template\n return template.replace(/\\{\\{(\\w+)\\}\\}|\\{(\\w+)\\}/g, (_, doubleKey, singleKey) => {\n const key = doubleKey ?? singleKey\n if (!key) return _\n const value = params[key]\n if (value === undefined) {\n return doubleKey ? `{{${key}}}` : `{${key}}`\n }\n return String(value)\n })\n}\n\nexport function I18nProvider({ children, locale, dict }: { children: React.ReactNode; locale: Locale; dict: Dict }) {\n const value = useMemo<I18nContextValue>(() => ({\n locale,\n t: (key, fallbackOrParams, params) => {\n let fallback: string | undefined\n let resolvedParams: TranslateParams | undefined\n\n if (typeof fallbackOrParams === 'string') {\n fallback = fallbackOrParams\n resolvedParams = params\n } else {\n resolvedParams = fallbackOrParams ?? params\n }\n\n const template = dict[key] ?? fallback ?? key\n return format(template, resolvedParams)\n },\n }), [locale, dict])\n return <I18nContext.Provider value={value}>{children}</I18nContext.Provider>\n}\n\nexport function useT() {\n const ctx = useContext(I18nContext)\n if (!ctx) throw new Error('useT must be used within I18nProvider')\n return ctx.t\n}\n\nexport function useLocale() {\n const ctx = useContext(I18nContext)\n if (!ctx) throw new Error('useLocale must be used within I18nProvider')\n return ctx.locale\n}\n"],
|
|
5
|
+
"mappings": ";AAoDS;AAnDT,SAAgB,eAAe,YAAY,eAAe;AAkB1D,MAAM,cAAc,cAAuC,IAAI;AAE/D,SAAS,OAAO,UAAkB,QAA0B;AAC1D,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,SAAS,QAAQ,4BAA4B,CAAC,GAAG,WAAW,cAAc;AAC/E,UAAM,MAAM,aAAa;AACzB,QAAI,CAAC,IAAK,QAAO;AACjB,UAAM,QAAQ,OAAO,GAAG;AACxB,QAAI,UAAU,QAAW;AACvB,aAAO,YAAY,KAAK,GAAG,OAAO,IAAI,GAAG;AAAA,IAC3C;AACA,WAAO,OAAO,KAAK;AAAA,EACrB,CAAC;AACH;AAEO,SAAS,aAAa,EAAE,UAAU,QAAQ,KAAK,GAA8D;AAClH,QAAM,QAAQ,QAA0B,OAAO;AAAA,IAC7C;AAAA,IACA,GAAG,CAAC,KAAK,kBAAkB,WAAW;AACpC,UAAI;AACJ,UAAI;AAEJ,UAAI,OAAO,qBAAqB,UAAU;AACxC,mBAAW;AACX,yBAAiB;AAAA,MACnB,OAAO;AACL,yBAAiB,oBAAoB;AAAA,MACvC;AAEA,YAAM,WAAW,KAAK,GAAG,KAAK,YAAY;AAC1C,aAAO,OAAO,UAAU,cAAc;AAAA,IACxC;AAAA,EACF,IAAI,CAAC,QAAQ,IAAI,CAAC;AAClB,SAAO,oBAAC,YAAY,UAAZ,EAAqB,OAAe,UAAS;AACvD;AAEO,SAAS,OAAO;AACrB,QAAM,MAAM,WAAW,WAAW;AAClC,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,uCAAuC;AACjE,SAAO,IAAI;AACb;AAEO,SAAS,YAAY;AAC1B,QAAM,MAAM,WAAW,WAAW;AAClC,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,4CAA4C;AACtE,SAAO,IAAI;AACb;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { defaultLocale, locales } from "./config.js";
|
|
2
|
+
import { createFallbackTranslator, createTranslator } from "./translate.js";
|
|
3
|
+
import { getModules } from "../modules/registry.js";
|
|
4
|
+
import { loadAppDictionary } from "./app-dictionaries.js";
|
|
5
|
+
import { registerModules, getModules as getModules2 } from "../modules/registry.js";
|
|
6
|
+
import { registerAppDictionaryLoader } from "./app-dictionaries.js";
|
|
7
|
+
function flattenDictionary(source, prefix = "") {
|
|
8
|
+
if (!source || typeof source !== "object" || Array.isArray(source)) return {};
|
|
9
|
+
const result = {};
|
|
10
|
+
for (const [key, value] of Object.entries(source)) {
|
|
11
|
+
if (!key) continue;
|
|
12
|
+
const nextKey = prefix ? `${prefix}.${key}` : key;
|
|
13
|
+
if (typeof value === "string") {
|
|
14
|
+
result[nextKey] = value;
|
|
15
|
+
} else if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
16
|
+
Object.assign(result, flattenDictionary(value, nextKey));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
21
|
+
async function detectLocale() {
|
|
22
|
+
try {
|
|
23
|
+
const { cookies, headers } = await import("next/headers");
|
|
24
|
+
try {
|
|
25
|
+
const c = (await cookies()).get("locale")?.value;
|
|
26
|
+
if (c && locales.includes(c)) return c;
|
|
27
|
+
} catch {
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
const accept = (await headers()).get("accept-language") || "";
|
|
31
|
+
const match = locales.find((l) => new RegExp(`(^|,)\\s*${l}(-|;|,|$)`, "i").test(accept));
|
|
32
|
+
if (match) return match;
|
|
33
|
+
} catch {
|
|
34
|
+
}
|
|
35
|
+
} catch {
|
|
36
|
+
}
|
|
37
|
+
return defaultLocale;
|
|
38
|
+
}
|
|
39
|
+
async function loadDictionary(locale) {
|
|
40
|
+
const baseRaw = await loadAppDictionary(locale);
|
|
41
|
+
const merged = { ...flattenDictionary(baseRaw) };
|
|
42
|
+
const modules = getModules();
|
|
43
|
+
for (const m of modules) {
|
|
44
|
+
const dict = m.translations?.[locale];
|
|
45
|
+
if (dict) Object.assign(merged, flattenDictionary(dict));
|
|
46
|
+
}
|
|
47
|
+
return merged;
|
|
48
|
+
}
|
|
49
|
+
async function resolveTranslations() {
|
|
50
|
+
const locale = await detectLocale();
|
|
51
|
+
const dict = await loadDictionary(locale);
|
|
52
|
+
const t = createTranslator(dict);
|
|
53
|
+
const translate = createFallbackTranslator(dict);
|
|
54
|
+
return { locale, dict, t, translate };
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
require("server-only");
|
|
58
|
+
} catch {
|
|
59
|
+
}
|
|
60
|
+
export {
|
|
61
|
+
detectLocale,
|
|
62
|
+
getModules2 as getModules,
|
|
63
|
+
loadDictionary,
|
|
64
|
+
registerAppDictionaryLoader,
|
|
65
|
+
registerModules,
|
|
66
|
+
resolveTranslations
|
|
67
|
+
};
|
|
68
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/i18n/server.ts"],
|
|
4
|
+
"sourcesContent": ["import { defaultLocale, locales, type Locale } from './config'\nimport type { Dict } from './context'\nimport { createFallbackTranslator, createTranslator } from './translate'\nimport { getModules } from '../modules/registry'\nimport { loadAppDictionary } from './app-dictionaries'\n\n// Re-export for backwards compatibility\nexport { registerModules, getModules } from '../modules/registry'\nexport { registerAppDictionaryLoader } from './app-dictionaries'\n\nfunction flattenDictionary(source: unknown, prefix = ''): Dict {\n if (!source || typeof source !== 'object' || Array.isArray(source)) return {}\n const result: Dict = {}\n for (const [key, value] of Object.entries(source as Record<string, unknown>)) {\n if (!key) continue\n const nextKey = prefix ? `${prefix}.${key}` : key\n if (typeof value === 'string') {\n result[nextKey] = value\n } else if (value && typeof value === 'object' && !Array.isArray(value)) {\n Object.assign(result, flattenDictionary(value, nextKey))\n }\n }\n return result\n}\n\nexport async function detectLocale(): Promise<Locale> {\n // Dynamic import to avoid requiring Next.js in non-Next.js contexts (CLI, tests)\n try {\n const { cookies, headers } = await import('next/headers')\n try {\n const c = (await cookies()).get('locale')?.value\n if (c && locales.includes(c as Locale)) return c as Locale\n } catch {\n // cookies() may not be available outside request context (e.g., in tests)\n }\n try {\n const accept = (await headers()).get('accept-language') || ''\n const match = locales.find(l => new RegExp(`(^|,)\\\\s*${l}(-|;|,|$)`, 'i').test(accept))\n if (match) return match\n } catch {\n // headers() may not be available outside request context (e.g., in tests)\n }\n } catch {\n // next/headers not available (CLI context)\n }\n return defaultLocale\n}\n\nexport async function loadDictionary(locale: Locale): Promise<Dict> {\n // Load from registry instead of @/ import (works in standalone packages)\n const baseRaw = await loadAppDictionary(locale)\n const merged: Dict = { ...flattenDictionary(baseRaw) }\n const modules = getModules()\n for (const m of modules) {\n const dict = m.translations?.[locale]\n if (dict) Object.assign(merged, flattenDictionary(dict))\n }\n return merged\n}\n\nexport async function resolveTranslations() {\n const locale = await detectLocale()\n const dict = await loadDictionary(locale)\n const t = createTranslator(dict)\n const translate = createFallbackTranslator(dict)\n return { locale, dict, t, translate }\n}\n// Hint Next.js to keep this server-only; ignore if unavailable when running scripts outside Next.\ntry {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n require('server-only')\n} catch {\n // noop: allows running generator scripts without Next's server-only package\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,eAAe,eAA4B;AAEpD,SAAS,0BAA0B,wBAAwB;AAC3D,SAAS,kBAAkB;AAC3B,SAAS,yBAAyB;AAGlC,SAAS,iBAAiB,cAAAA,mBAAkB;AAC5C,SAAS,mCAAmC;AAE5C,SAAS,kBAAkB,QAAiB,SAAS,IAAU;AAC7D,MAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,EAAG,QAAO,CAAC;AAC5E,QAAM,SAAe,CAAC;AACtB,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAiC,GAAG;AAC5E,QAAI,CAAC,IAAK;AACV,UAAM,UAAU,SAAS,GAAG,MAAM,IAAI,GAAG,KAAK;AAC9C,QAAI,OAAO,UAAU,UAAU;AAC7B,aAAO,OAAO,IAAI;AAAA,IACpB,WAAW,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,GAAG;AACtE,aAAO,OAAO,QAAQ,kBAAkB,OAAO,OAAO,CAAC;AAAA,IACzD;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAsB,eAAgC;AAEpD,MAAI;AACF,UAAM,EAAE,SAAS,QAAQ,IAAI,MAAM,OAAO,cAAc;AACxD,QAAI;AACF,YAAM,KAAK,MAAM,QAAQ,GAAG,IAAI,QAAQ,GAAG;AAC3C,UAAI,KAAK,QAAQ,SAAS,CAAW,EAAG,QAAO;AAAA,IACjD,QAAQ;AAAA,IAER;AACA,QAAI;AACF,YAAM,UAAU,MAAM,QAAQ,GAAG,IAAI,iBAAiB,KAAK;AAC3D,YAAM,QAAQ,QAAQ,KAAK,OAAK,IAAI,OAAO,YAAY,CAAC,aAAa,GAAG,EAAE,KAAK,MAAM,CAAC;AACtF,UAAI,MAAO,QAAO;AAAA,IACpB,QAAQ;AAAA,IAER;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAEA,eAAsB,eAAe,QAA+B;AAElE,QAAM,UAAU,MAAM,kBAAkB,MAAM;AAC9C,QAAM,SAAe,EAAE,GAAG,kBAAkB,OAAO,EAAE;AACrD,QAAM,UAAU,WAAW;AAC3B,aAAW,KAAK,SAAS;AACvB,UAAM,OAAO,EAAE,eAAe,MAAM;AACpC,QAAI,KAAM,QAAO,OAAO,QAAQ,kBAAkB,IAAI,CAAC;AAAA,EACzD;AACA,SAAO;AACT;AAEA,eAAsB,sBAAsB;AAC1C,QAAM,SAAS,MAAM,aAAa;AAClC,QAAM,OAAO,MAAM,eAAe,MAAM;AACxC,QAAM,IAAI,iBAAiB,IAAI;AAC/B,QAAM,YAAY,yBAAyB,IAAI;AAC/C,SAAO,EAAE,QAAQ,MAAM,GAAG,UAAU;AACtC;AAEA,IAAI;AAEF,UAAQ,aAAa;AACvB,QAAQ;AAER;",
|
|
6
|
+
"names": ["getModules"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
function format(template, params) {
|
|
2
|
+
if (!params) return template;
|
|
3
|
+
return template.replace(/\{\{(\w+)\}\}|\{(\w+)\}/g, (match, doubleKey, singleKey) => {
|
|
4
|
+
const key = doubleKey ?? singleKey;
|
|
5
|
+
if (!key) return match;
|
|
6
|
+
const value = params[key];
|
|
7
|
+
if (value === void 0) return match;
|
|
8
|
+
return String(value);
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
function createTranslator(dict) {
|
|
12
|
+
const translator = ((key, fallbackOrParams, params) => {
|
|
13
|
+
let fallback;
|
|
14
|
+
let resolvedParams;
|
|
15
|
+
if (typeof fallbackOrParams === "string") {
|
|
16
|
+
fallback = fallbackOrParams;
|
|
17
|
+
resolvedParams = params;
|
|
18
|
+
} else {
|
|
19
|
+
resolvedParams = fallbackOrParams;
|
|
20
|
+
}
|
|
21
|
+
const template = dict[key] ?? fallback ?? key;
|
|
22
|
+
return format(template, resolvedParams);
|
|
23
|
+
});
|
|
24
|
+
return translator;
|
|
25
|
+
}
|
|
26
|
+
function translateWithFallback(t, key, fallback, params) {
|
|
27
|
+
const value = params ? t(key, params) : t(key);
|
|
28
|
+
if (value !== key) return value;
|
|
29
|
+
if (fallback === void 0) return key;
|
|
30
|
+
return format(fallback, params);
|
|
31
|
+
}
|
|
32
|
+
function createTranslatorWithFallback(translate) {
|
|
33
|
+
return (key, fallback, params) => translateWithFallback(translate, key, fallback, params);
|
|
34
|
+
}
|
|
35
|
+
function createFallbackTranslator(dict) {
|
|
36
|
+
const t = createTranslator(dict);
|
|
37
|
+
return (key, fallback, params) => translateWithFallback(t, key, fallback, params);
|
|
38
|
+
}
|
|
39
|
+
export {
|
|
40
|
+
createFallbackTranslator,
|
|
41
|
+
createTranslator,
|
|
42
|
+
createTranslatorWithFallback,
|
|
43
|
+
translateWithFallback
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=translate.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/i18n/translate.ts"],
|
|
4
|
+
"sourcesContent": ["import type { Dict, TranslateFn, TranslateParams } from './context'\n\nexport type TranslateWithFallbackFn = (key: string, fallback?: string, params?: TranslateParams) => string\n\nfunction format(template: string, params?: TranslateParams) {\n if (!params) return template\n return template.replace(/\\{\\{(\\w+)\\}\\}|\\{(\\w+)\\}/g, (match, doubleKey, singleKey) => {\n const key = doubleKey ?? singleKey\n if (!key) return match\n const value = params[key]\n if (value === undefined) return match\n return String(value)\n })\n}\n\nexport function createTranslator(dict: Dict): TranslateFn {\n const translator = ((key: string, fallbackOrParams?: string | TranslateParams, params?: TranslateParams) => {\n let fallback: string | undefined\n let resolvedParams: TranslateParams | undefined\n\n if (typeof fallbackOrParams === 'string') {\n fallback = fallbackOrParams\n resolvedParams = params\n } else {\n resolvedParams = fallbackOrParams\n }\n\n const template = dict[key] ?? fallback ?? key\n return format(template, resolvedParams)\n }) as TranslateFn\n\n return translator\n}\n\nexport function translateWithFallback(\n t: TranslateFn,\n key: string,\n fallback?: string,\n params?: TranslateParams,\n): string {\n const value = params ? t(key, params) : t(key)\n if (value !== key) return value\n if (fallback === undefined) return key\n return format(fallback, params)\n}\n\nexport function createTranslatorWithFallback(translate: TranslateFn): TranslateWithFallbackFn {\n return (key, fallback, params) => translateWithFallback(translate, key, fallback, params)\n}\n\nexport function createFallbackTranslator(dict: Dict): TranslateWithFallbackFn {\n const t = createTranslator(dict)\n return (key, fallback, params) => translateWithFallback(t, key, fallback, params)\n}\n"],
|
|
5
|
+
"mappings": "AAIA,SAAS,OAAO,UAAkB,QAA0B;AAC1D,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,SAAS,QAAQ,4BAA4B,CAAC,OAAO,WAAW,cAAc;AACnF,UAAM,MAAM,aAAa;AACzB,QAAI,CAAC,IAAK,QAAO;AACjB,UAAM,QAAQ,OAAO,GAAG;AACxB,QAAI,UAAU,OAAW,QAAO;AAChC,WAAO,OAAO,KAAK;AAAA,EACrB,CAAC;AACH;AAEO,SAAS,iBAAiB,MAAyB;AACxD,QAAM,cAAc,CAAC,KAAa,kBAA6C,WAA6B;AAC1G,QAAI;AACJ,QAAI;AAEJ,QAAI,OAAO,qBAAqB,UAAU;AACxC,iBAAW;AACX,uBAAiB;AAAA,IACnB,OAAO;AACL,uBAAiB;AAAA,IACnB;AAEA,UAAM,WAAW,KAAK,GAAG,KAAK,YAAY;AAC1C,WAAO,OAAO,UAAU,cAAc;AAAA,EACxC;AAEA,SAAO;AACT;AAEO,SAAS,sBACd,GACA,KACA,UACA,QACQ;AACR,QAAM,QAAQ,SAAS,EAAE,KAAK,MAAM,IAAI,EAAE,GAAG;AAC7C,MAAI,UAAU,IAAK,QAAO;AAC1B,MAAI,aAAa,OAAW,QAAO;AACnC,SAAO,OAAO,UAAU,MAAM;AAChC;AAEO,SAAS,6BAA6B,WAAiD;AAC5F,SAAO,CAAC,KAAK,UAAU,WAAW,sBAAsB,WAAW,KAAK,UAAU,MAAM;AAC1F;AAEO,SAAS,yBAAyB,MAAqC;AAC5E,QAAM,IAAI,iBAAiB,IAAI;AAC/B,SAAO,CAAC,KAAK,UAAU,WAAW,sBAAsB,GAAG,KAAK,UAAU,MAAM;AAClF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
const MAX_MESSAGE_LENGTH = 8192;
|
|
2
|
+
const MAX_STACK_LENGTH = 32768;
|
|
3
|
+
function truncate(input, limit) {
|
|
4
|
+
if (!input) return null;
|
|
5
|
+
return input.length > limit ? `${input.slice(0, limit - 3)}...` : input;
|
|
6
|
+
}
|
|
7
|
+
function normalizeError(error) {
|
|
8
|
+
if (error instanceof Error) {
|
|
9
|
+
return {
|
|
10
|
+
message: error.message || error.name || "Unknown error",
|
|
11
|
+
stack: typeof error.stack === "string" ? error.stack : null
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
if (typeof error === "string") {
|
|
15
|
+
return { message: error, stack: null };
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
const json = JSON.stringify(error);
|
|
19
|
+
return { message: json, stack: null };
|
|
20
|
+
} catch {
|
|
21
|
+
return { message: String(error ?? "Unknown error"), stack: null };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function safeJson(value) {
|
|
25
|
+
if (value === void 0) return null;
|
|
26
|
+
try {
|
|
27
|
+
return JSON.parse(JSON.stringify(value));
|
|
28
|
+
} catch {
|
|
29
|
+
if (value == null) return null;
|
|
30
|
+
if (typeof value === "object") {
|
|
31
|
+
return { note: "unserializable", asString: String(value) };
|
|
32
|
+
}
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function pickKnex(deps) {
|
|
37
|
+
if (deps.knex) return deps.knex;
|
|
38
|
+
if (deps.em) {
|
|
39
|
+
try {
|
|
40
|
+
const connection = deps.em.getConnection();
|
|
41
|
+
if (connection && typeof connection.getKnex === "function") {
|
|
42
|
+
return connection.getKnex();
|
|
43
|
+
}
|
|
44
|
+
} catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
async function recordIndexerError(deps, input) {
|
|
51
|
+
const knex = pickKnex(deps);
|
|
52
|
+
if (!knex) {
|
|
53
|
+
console.error("[indexers] Unable to record indexer error (missing knex connection)", {
|
|
54
|
+
source: input.source,
|
|
55
|
+
handler: input.handler
|
|
56
|
+
});
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const { message, stack } = normalizeError(input.error);
|
|
60
|
+
const payload = safeJson(input.payload);
|
|
61
|
+
const now = /* @__PURE__ */ new Date();
|
|
62
|
+
try {
|
|
63
|
+
await knex("indexer_error_logs").insert({
|
|
64
|
+
source: input.source,
|
|
65
|
+
handler: input.handler,
|
|
66
|
+
entity_type: input.entityType ?? null,
|
|
67
|
+
record_id: input.recordId ?? null,
|
|
68
|
+
tenant_id: input.tenantId ?? null,
|
|
69
|
+
organization_id: input.organizationId ?? null,
|
|
70
|
+
payload,
|
|
71
|
+
message: truncate(message, MAX_MESSAGE_LENGTH),
|
|
72
|
+
stack: truncate(stack, MAX_STACK_LENGTH),
|
|
73
|
+
occurred_at: now
|
|
74
|
+
});
|
|
75
|
+
} catch (loggingError) {
|
|
76
|
+
console.error("[indexers] Failed to persist indexer error", loggingError);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export {
|
|
80
|
+
recordIndexerError
|
|
81
|
+
};
|
|
82
|
+
//# sourceMappingURL=error-log.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/indexers/error-log.ts"],
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { Knex } from 'knex'\n\nexport type IndexerErrorSource = 'query_index' | 'vector' | 'fulltext'\n\nexport type RecordIndexerErrorInput = {\n source: IndexerErrorSource\n handler: string\n error: unknown\n entityType?: string | null\n recordId?: string | null\n tenantId?: string | null\n organizationId?: string | null\n payload?: unknown\n}\n\ntype RecordIndexerErrorDeps = {\n em?: EntityManager\n knex?: Knex\n}\n\nconst MAX_MESSAGE_LENGTH = 8_192\nconst MAX_STACK_LENGTH = 32_768\n\nfunction truncate(input: string | null | undefined, limit: number): string | null {\n if (!input) return null\n return input.length > limit ? `${input.slice(0, limit - 3)}...` : input\n}\n\nfunction normalizeError(error: unknown): { message: string; stack: string | null } {\n if (error instanceof Error) {\n return {\n message: error.message || error.name || 'Unknown error',\n stack: typeof error.stack === 'string' ? error.stack : null,\n }\n }\n if (typeof error === 'string') {\n return { message: error, stack: null }\n }\n try {\n const json = JSON.stringify(error)\n return { message: json, stack: null }\n } catch {\n return { message: String(error ?? 'Unknown error'), stack: null }\n }\n}\n\nfunction safeJson(value: unknown): unknown {\n if (value === undefined) return null\n try {\n return JSON.parse(JSON.stringify(value))\n } catch {\n if (value == null) return null\n if (typeof value === 'object') {\n return { note: 'unserializable', asString: String(value) }\n }\n return value\n }\n}\n\nfunction pickKnex(deps: RecordIndexerErrorDeps): Knex | null {\n if (deps.knex) return deps.knex\n if (deps.em) {\n try {\n const connection = deps.em.getConnection()\n if (connection && typeof connection.getKnex === 'function') {\n return connection.getKnex()\n }\n } catch {\n return null\n }\n }\n return null\n}\n\nexport async function recordIndexerError(deps: RecordIndexerErrorDeps, input: RecordIndexerErrorInput): Promise<void> {\n const knex = pickKnex(deps)\n if (!knex) {\n console.error('[indexers] Unable to record indexer error (missing knex connection)', {\n source: input.source,\n handler: input.handler,\n })\n return\n }\n\n const { message, stack } = normalizeError(input.error)\n const payload = safeJson(input.payload)\n const now = new Date()\n\n try {\n await knex('indexer_error_logs').insert({\n source: input.source,\n handler: input.handler,\n entity_type: input.entityType ?? null,\n record_id: input.recordId ?? null,\n tenant_id: input.tenantId ?? null,\n organization_id: input.organizationId ?? null,\n payload,\n message: truncate(message, MAX_MESSAGE_LENGTH),\n stack: truncate(stack, MAX_STACK_LENGTH),\n occurred_at: now,\n })\n } catch (loggingError) {\n console.error('[indexers] Failed to persist indexer error', loggingError)\n }\n}\n"],
|
|
5
|
+
"mappings": "AAqBA,MAAM,qBAAqB;AAC3B,MAAM,mBAAmB;AAEzB,SAAS,SAAS,OAAkC,OAA8B;AAChF,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,MAAM,SAAS,QAAQ,GAAG,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,QAAQ;AACpE;AAEA,SAAS,eAAe,OAA2D;AACjF,MAAI,iBAAiB,OAAO;AAC1B,WAAO;AAAA,MACL,SAAS,MAAM,WAAW,MAAM,QAAQ;AAAA,MACxC,OAAO,OAAO,MAAM,UAAU,WAAW,MAAM,QAAQ;AAAA,IACzD;AAAA,EACF;AACA,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,EAAE,SAAS,OAAO,OAAO,KAAK;AAAA,EACvC;AACA,MAAI;AACF,UAAM,OAAO,KAAK,UAAU,KAAK;AACjC,WAAO,EAAE,SAAS,MAAM,OAAO,KAAK;AAAA,EACtC,QAAQ;AACN,WAAO,EAAE,SAAS,OAAO,SAAS,eAAe,GAAG,OAAO,KAAK;AAAA,EAClE;AACF;AAEA,SAAS,SAAS,OAAyB;AACzC,MAAI,UAAU,OAAW,QAAO;AAChC,MAAI;AACF,WAAO,KAAK,MAAM,KAAK,UAAU,KAAK,CAAC;AAAA,EACzC,QAAQ;AACN,QAAI,SAAS,KAAM,QAAO;AAC1B,QAAI,OAAO,UAAU,UAAU;AAC7B,aAAO,EAAE,MAAM,kBAAkB,UAAU,OAAO,KAAK,EAAE;AAAA,IAC3D;AACA,WAAO;AAAA,EACT;AACF;AAEA,SAAS,SAAS,MAA2C;AAC3D,MAAI,KAAK,KAAM,QAAO,KAAK;AAC3B,MAAI,KAAK,IAAI;AACX,QAAI;AACF,YAAM,aAAa,KAAK,GAAG,cAAc;AACzC,UAAI,cAAc,OAAO,WAAW,YAAY,YAAY;AAC1D,eAAO,WAAW,QAAQ;AAAA,MAC5B;AAAA,IACF,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAsB,mBAAmB,MAA8B,OAA+C;AACpH,QAAM,OAAO,SAAS,IAAI;AAC1B,MAAI,CAAC,MAAM;AACT,YAAQ,MAAM,uEAAuE;AAAA,MACnF,QAAQ,MAAM;AAAA,MACd,SAAS,MAAM;AAAA,IACjB,CAAC;AACD;AAAA,EACF;AAEA,QAAM,EAAE,SAAS,MAAM,IAAI,eAAe,MAAM,KAAK;AACrD,QAAM,UAAU,SAAS,MAAM,OAAO;AACtC,QAAM,MAAM,oBAAI,KAAK;AAErB,MAAI;AACF,UAAM,KAAK,oBAAoB,EAAE,OAAO;AAAA,MACtC,QAAQ,MAAM;AAAA,MACd,SAAS,MAAM;AAAA,MACf,aAAa,MAAM,cAAc;AAAA,MACjC,WAAW,MAAM,YAAY;AAAA,MAC7B,WAAW,MAAM,YAAY;AAAA,MAC7B,iBAAiB,MAAM,kBAAkB;AAAA,MACzC;AAAA,MACA,SAAS,SAAS,SAAS,kBAAkB;AAAA,MAC7C,OAAO,SAAS,OAAO,gBAAgB;AAAA,MACvC,aAAa;AAAA,IACf,CAAC;AAAA,EACH,SAAS,cAAc;AACrB,YAAQ,MAAM,8CAA8C,YAAY;AAAA,EAC1E;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
const MAX_MESSAGE_LENGTH = 4096;
|
|
2
|
+
const MAX_DELETE_BATCH = 5e3;
|
|
3
|
+
const MAX_LOGS_PER_SOURCE = 1e4;
|
|
4
|
+
function truncate(input, limit) {
|
|
5
|
+
if (!input) return null;
|
|
6
|
+
return input.length > limit ? `${input.slice(0, limit - 3)}...` : input;
|
|
7
|
+
}
|
|
8
|
+
function safeJson(value) {
|
|
9
|
+
if (value === void 0) return null;
|
|
10
|
+
try {
|
|
11
|
+
return JSON.parse(JSON.stringify(value));
|
|
12
|
+
} catch {
|
|
13
|
+
if (value == null) return null;
|
|
14
|
+
if (typeof value === "object") {
|
|
15
|
+
return { note: "unserializable", asString: String(value) };
|
|
16
|
+
}
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function pickKnex(deps) {
|
|
21
|
+
if (deps.knex) return deps.knex;
|
|
22
|
+
if (deps.em) {
|
|
23
|
+
try {
|
|
24
|
+
const connection = deps.em.getConnection();
|
|
25
|
+
if (connection && typeof connection.getKnex === "function") {
|
|
26
|
+
return connection.getKnex();
|
|
27
|
+
}
|
|
28
|
+
} catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
async function pruneExcessLogs(knex, source) {
|
|
35
|
+
const rows = await knex("indexer_status_logs").select("id").where("source", source).orderBy("occurred_at", "desc").orderBy("id", "desc").offset(MAX_LOGS_PER_SOURCE).limit(MAX_DELETE_BATCH);
|
|
36
|
+
if (!rows.length) return;
|
|
37
|
+
const ids = rows.map((row) => row.id).filter(Boolean);
|
|
38
|
+
if (!ids.length) return;
|
|
39
|
+
await knex("indexer_status_logs").whereIn("id", ids).del();
|
|
40
|
+
}
|
|
41
|
+
async function recordIndexerLog(deps, input) {
|
|
42
|
+
const knex = pickKnex(deps);
|
|
43
|
+
if (!knex) {
|
|
44
|
+
console.warn("[indexers] Unable to record indexer log (missing knex connection)", {
|
|
45
|
+
source: input.source,
|
|
46
|
+
handler: input.handler
|
|
47
|
+
});
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const level = input.level === "warn" ? "warn" : "info";
|
|
51
|
+
const message = truncate(input.message, MAX_MESSAGE_LENGTH) ?? "\u2014";
|
|
52
|
+
const details = safeJson(input.details);
|
|
53
|
+
const occurredAt = /* @__PURE__ */ new Date();
|
|
54
|
+
try {
|
|
55
|
+
await knex("indexer_status_logs").insert({
|
|
56
|
+
source: input.source,
|
|
57
|
+
handler: input.handler,
|
|
58
|
+
level,
|
|
59
|
+
entity_type: input.entityType ?? null,
|
|
60
|
+
record_id: input.recordId ?? null,
|
|
61
|
+
tenant_id: input.tenantId ?? null,
|
|
62
|
+
organization_id: input.organizationId ?? null,
|
|
63
|
+
message,
|
|
64
|
+
details,
|
|
65
|
+
occurred_at: occurredAt
|
|
66
|
+
});
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.error("[indexers] Failed to persist indexer log", error);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
await pruneExcessLogs(knex, input.source);
|
|
73
|
+
} catch (pruneError) {
|
|
74
|
+
console.warn("[indexers] Failed to prune indexer logs", pruneError);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
export {
|
|
78
|
+
recordIndexerLog
|
|
79
|
+
};
|
|
80
|
+
//# sourceMappingURL=status-log.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/indexers/status-log.ts"],
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { Knex } from 'knex'\nimport type { IndexerErrorSource } from './error-log'\n\nexport type IndexerLogLevel = 'info' | 'warn'\n\nexport type RecordIndexerLogInput = {\n source: IndexerErrorSource\n handler: string\n message: string\n level?: IndexerLogLevel\n entityType?: string | null\n recordId?: string | null\n tenantId?: string | null\n organizationId?: string | null\n details?: unknown\n}\n\ntype RecordIndexerLogDeps = {\n em?: EntityManager\n knex?: Knex\n}\n\nconst MAX_MESSAGE_LENGTH = 4_096\nconst MAX_DELETE_BATCH = 5_000\nconst MAX_LOGS_PER_SOURCE = 10_000\n\nfunction truncate(input: string | null | undefined, limit: number): string | null {\n if (!input) return null\n return input.length > limit ? `${input.slice(0, limit - 3)}...` : input\n}\n\nfunction safeJson(value: unknown): unknown {\n if (value === undefined) return null\n try {\n return JSON.parse(JSON.stringify(value))\n } catch {\n if (value == null) return null\n if (typeof value === 'object') {\n return { note: 'unserializable', asString: String(value) }\n }\n return value\n }\n}\n\nfunction pickKnex(deps: RecordIndexerLogDeps): Knex | null {\n if (deps.knex) return deps.knex\n if (deps.em) {\n try {\n const connection = deps.em.getConnection()\n if (connection && typeof connection.getKnex === 'function') {\n return connection.getKnex()\n }\n } catch {\n return null\n }\n }\n return null\n}\n\nasync function pruneExcessLogs(knex: Knex, source: IndexerErrorSource): Promise<void> {\n const rows = await knex('indexer_status_logs')\n .select('id')\n .where('source', source)\n .orderBy('occurred_at', 'desc')\n .orderBy('id', 'desc')\n .offset(MAX_LOGS_PER_SOURCE)\n .limit(MAX_DELETE_BATCH)\n\n if (!rows.length) return\n const ids = rows.map((row: any) => row.id).filter(Boolean)\n if (!ids.length) return\n await knex('indexer_status_logs')\n .whereIn('id', ids)\n .del()\n}\n\nexport async function recordIndexerLog(\n deps: RecordIndexerLogDeps,\n input: RecordIndexerLogInput,\n): Promise<void> {\n const knex = pickKnex(deps)\n if (!knex) {\n console.warn('[indexers] Unable to record indexer log (missing knex connection)', {\n source: input.source,\n handler: input.handler,\n })\n return\n }\n\n const level: IndexerLogLevel = input.level === 'warn' ? 'warn' : 'info'\n const message = truncate(input.message, MAX_MESSAGE_LENGTH) ?? '\u2014'\n const details = safeJson(input.details)\n const occurredAt = new Date()\n\n try {\n await knex('indexer_status_logs').insert({\n source: input.source,\n handler: input.handler,\n level,\n entity_type: input.entityType ?? null,\n record_id: input.recordId ?? null,\n tenant_id: input.tenantId ?? null,\n organization_id: input.organizationId ?? null,\n message,\n details,\n occurred_at: occurredAt,\n })\n } catch (error) {\n console.error('[indexers] Failed to persist indexer log', error)\n return\n }\n\n try {\n await pruneExcessLogs(knex, input.source)\n } catch (pruneError) {\n console.warn('[indexers] Failed to prune indexer logs', pruneError)\n }\n}\n"],
|
|
5
|
+
"mappings": "AAuBA,MAAM,qBAAqB;AAC3B,MAAM,mBAAmB;AACzB,MAAM,sBAAsB;AAE5B,SAAS,SAAS,OAAkC,OAA8B;AAChF,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,MAAM,SAAS,QAAQ,GAAG,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,QAAQ;AACpE;AAEA,SAAS,SAAS,OAAyB;AACzC,MAAI,UAAU,OAAW,QAAO;AAChC,MAAI;AACF,WAAO,KAAK,MAAM,KAAK,UAAU,KAAK,CAAC;AAAA,EACzC,QAAQ;AACN,QAAI,SAAS,KAAM,QAAO;AAC1B,QAAI,OAAO,UAAU,UAAU;AAC7B,aAAO,EAAE,MAAM,kBAAkB,UAAU,OAAO,KAAK,EAAE;AAAA,IAC3D;AACA,WAAO;AAAA,EACT;AACF;AAEA,SAAS,SAAS,MAAyC;AACzD,MAAI,KAAK,KAAM,QAAO,KAAK;AAC3B,MAAI,KAAK,IAAI;AACX,QAAI;AACF,YAAM,aAAa,KAAK,GAAG,cAAc;AACzC,UAAI,cAAc,OAAO,WAAW,YAAY,YAAY;AAC1D,eAAO,WAAW,QAAQ;AAAA,MAC5B;AAAA,IACF,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAe,gBAAgB,MAAY,QAA2C;AACpF,QAAM,OAAO,MAAM,KAAK,qBAAqB,EAC1C,OAAO,IAAI,EACX,MAAM,UAAU,MAAM,EACtB,QAAQ,eAAe,MAAM,EAC7B,QAAQ,MAAM,MAAM,EACpB,OAAO,mBAAmB,EAC1B,MAAM,gBAAgB;AAEzB,MAAI,CAAC,KAAK,OAAQ;AAClB,QAAM,MAAM,KAAK,IAAI,CAAC,QAAa,IAAI,EAAE,EAAE,OAAO,OAAO;AACzD,MAAI,CAAC,IAAI,OAAQ;AACjB,QAAM,KAAK,qBAAqB,EAC7B,QAAQ,MAAM,GAAG,EACjB,IAAI;AACT;AAEA,eAAsB,iBACpB,MACA,OACe;AACf,QAAM,OAAO,SAAS,IAAI;AAC1B,MAAI,CAAC,MAAM;AACT,YAAQ,KAAK,qEAAqE;AAAA,MAChF,QAAQ,MAAM;AAAA,MACd,SAAS,MAAM;AAAA,IACjB,CAAC;AACD;AAAA,EACF;AAEA,QAAM,QAAyB,MAAM,UAAU,SAAS,SAAS;AACjE,QAAM,UAAU,SAAS,MAAM,SAAS,kBAAkB,KAAK;AAC/D,QAAM,UAAU,SAAS,MAAM,OAAO;AACtC,QAAM,aAAa,oBAAI,KAAK;AAE5B,MAAI;AACF,UAAM,KAAK,qBAAqB,EAAE,OAAO;AAAA,MACvC,QAAQ,MAAM;AAAA,MACd,SAAS,MAAM;AAAA,MACf;AAAA,MACA,aAAa,MAAM,cAAc;AAAA,MACjC,WAAW,MAAM,YAAY;AAAA,MAC7B,WAAW,MAAM,YAAY;AAAA,MAC7B,iBAAiB,MAAM,kBAAkB;AAAA,MACzC;AAAA,MACA;AAAA,MACA,aAAa;AAAA,IACf,CAAC;AAAA,EACH,SAAS,OAAO;AACd,YAAQ,MAAM,4CAA4C,KAAK;AAC/D;AAAA,EACF;AAEA,MAAI;AACF,UAAM,gBAAgB,MAAM,MAAM,MAAM;AAAA,EAC1C,SAAS,YAAY;AACnB,YAAQ,KAAK,2CAA2C,UAAU;AAAA,EACpE;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
function base64url(input) {
|
|
3
|
+
return (typeof input === "string" ? Buffer.from(input) : input).toString("base64").replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
|
4
|
+
}
|
|
5
|
+
function signJwt(payload, secret = process.env.JWT_SECRET, expiresInSec = 60 * 60 * 8) {
|
|
6
|
+
if (!secret) throw new Error("JWT_SECRET is not set");
|
|
7
|
+
const header = { alg: "HS256", typ: "JWT" };
|
|
8
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
9
|
+
const body = { iat: now, exp: now + expiresInSec, ...payload };
|
|
10
|
+
const encHeader = base64url(JSON.stringify(header));
|
|
11
|
+
const encBody = base64url(JSON.stringify(body));
|
|
12
|
+
const data = `${encHeader}.${encBody}`;
|
|
13
|
+
const sig = crypto.createHmac("sha256", secret).update(data).digest();
|
|
14
|
+
const encSig = base64url(sig);
|
|
15
|
+
return `${data}.${encSig}`;
|
|
16
|
+
}
|
|
17
|
+
function verifyJwt(token, secret = process.env.JWT_SECRET) {
|
|
18
|
+
if (!secret) throw new Error("JWT_SECRET is not set");
|
|
19
|
+
const parts = token.split(".");
|
|
20
|
+
if (parts.length !== 3) return null;
|
|
21
|
+
const [h, p, s] = parts;
|
|
22
|
+
const data = `${h}.${p}`;
|
|
23
|
+
const expected = base64url(crypto.createHmac("sha256", secret).update(data).digest());
|
|
24
|
+
if (!crypto.timingSafeEqual(Buffer.from(s), Buffer.from(expected))) return null;
|
|
25
|
+
const payload = JSON.parse(Buffer.from(p, "base64").toString("utf8"));
|
|
26
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
27
|
+
if (payload.exp && now > payload.exp) return null;
|
|
28
|
+
return payload;
|
|
29
|
+
}
|
|
30
|
+
export {
|
|
31
|
+
signJwt,
|
|
32
|
+
verifyJwt
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=jwt.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/lib/lib/auth/jwt.ts"],
|
|
4
|
+
"sourcesContent": ["import crypto from 'node:crypto'\n\nfunction base64url(input: Buffer | string) {\n return (typeof input === 'string' ? Buffer.from(input) : input)\n .toString('base64')\n .replace(/=/g, '')\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n}\n\nexport type JwtPayload = Record<string, any>\n\nexport function signJwt(payload: JwtPayload, secret = process.env.JWT_SECRET!, expiresInSec = 60 * 60 * 8) {\n if (!secret) throw new Error('JWT_SECRET is not set')\n const header = { alg: 'HS256', typ: 'JWT' }\n const now = Math.floor(Date.now() / 1000)\n const body = { iat: now, exp: now + expiresInSec, ...payload }\n const encHeader = base64url(JSON.stringify(header))\n const encBody = base64url(JSON.stringify(body))\n const data = `${encHeader}.${encBody}`\n const sig = crypto.createHmac('sha256', secret).update(data).digest()\n const encSig = base64url(sig)\n return `${data}.${encSig}`\n}\n\nexport function verifyJwt(token: string, secret = process.env.JWT_SECRET!) {\n if (!secret) throw new Error('JWT_SECRET is not set')\n const parts = token.split('.')\n if (parts.length !== 3) return null\n const [h, p, s] = parts\n const data = `${h}.${p}`\n const expected = base64url(crypto.createHmac('sha256', secret).update(data).digest())\n if (!crypto.timingSafeEqual(Buffer.from(s), Buffer.from(expected))) return null\n const payload = JSON.parse(Buffer.from(p, 'base64').toString('utf8'))\n const now = Math.floor(Date.now() / 1000)\n if (payload.exp && now > payload.exp) return null\n return payload\n}\n\n"],
|
|
5
|
+
"mappings": "AAAA,OAAO,YAAY;AAEnB,SAAS,UAAU,OAAwB;AACzC,UAAQ,OAAO,UAAU,WAAW,OAAO,KAAK,KAAK,IAAI,OACtD,SAAS,QAAQ,EACjB,QAAQ,MAAM,EAAE,EAChB,QAAQ,OAAO,GAAG,EAClB,QAAQ,OAAO,GAAG;AACvB;AAIO,SAAS,QAAQ,SAAqB,SAAS,QAAQ,IAAI,YAAa,eAAe,KAAK,KAAK,GAAG;AACzG,MAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,uBAAuB;AACpD,QAAM,SAAS,EAAE,KAAK,SAAS,KAAK,MAAM;AAC1C,QAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACxC,QAAM,OAAO,EAAE,KAAK,KAAK,KAAK,MAAM,cAAc,GAAG,QAAQ;AAC7D,QAAM,YAAY,UAAU,KAAK,UAAU,MAAM,CAAC;AAClD,QAAM,UAAU,UAAU,KAAK,UAAU,IAAI,CAAC;AAC9C,QAAM,OAAO,GAAG,SAAS,IAAI,OAAO;AACpC,QAAM,MAAM,OAAO,WAAW,UAAU,MAAM,EAAE,OAAO,IAAI,EAAE,OAAO;AACpE,QAAM,SAAS,UAAU,GAAG;AAC5B,SAAO,GAAG,IAAI,IAAI,MAAM;AAC1B;AAEO,SAAS,UAAU,OAAe,SAAS,QAAQ,IAAI,YAAa;AACzE,MAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,uBAAuB;AACpD,QAAM,QAAQ,MAAM,MAAM,GAAG;AAC7B,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,QAAM,CAAC,GAAG,GAAG,CAAC,IAAI;AAClB,QAAM,OAAO,GAAG,CAAC,IAAI,CAAC;AACtB,QAAM,WAAW,UAAU,OAAO,WAAW,UAAU,MAAM,EAAE,OAAO,IAAI,EAAE,OAAO,CAAC;AACpF,MAAI,CAAC,OAAO,gBAAgB,OAAO,KAAK,CAAC,GAAG,OAAO,KAAK,QAAQ,CAAC,EAAG,QAAO;AAC3E,QAAM,UAAU,KAAK,MAAM,OAAO,KAAK,GAAG,QAAQ,EAAE,SAAS,MAAM,CAAC;AACpE,QAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACxC,MAAI,QAAQ,OAAO,MAAM,QAAQ,IAAK,QAAO;AAC7C,SAAO;AACT;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|