@objectstack/runtime 3.2.5 → 3.2.7
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/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +18 -0
- package/dist/index.cjs +175 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -4
- package/dist/index.d.ts +18 -4
- package/dist/index.js +175 -31
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/src/app-plugin.test.ts +167 -0
- package/src/app-plugin.ts +96 -4
- package/src/dispatcher-plugin.ts +33 -3
- package/src/http-dispatcher.test.ts +254 -0
- package/src/http-dispatcher.ts +85 -29
- package/src/seed-loader.ts +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -83,6 +83,7 @@ declare class DriverPlugin implements Plugin {
|
|
|
83
83
|
* Responsibilities:
|
|
84
84
|
* 1. Register App Manifest as a service (for ObjectQL discovery)
|
|
85
85
|
* 2. Execute Runtime `onEnable` hook (for code logic)
|
|
86
|
+
* 3. Auto-load i18n translation bundles into the kernel's i18n service
|
|
86
87
|
*/
|
|
87
88
|
declare class AppPlugin implements Plugin {
|
|
88
89
|
name: string;
|
|
@@ -92,6 +93,15 @@ declare class AppPlugin implements Plugin {
|
|
|
92
93
|
constructor(bundle: any);
|
|
93
94
|
init: (ctx: PluginContext) => Promise<void>;
|
|
94
95
|
start: (ctx: PluginContext) => Promise<void>;
|
|
96
|
+
/**
|
|
97
|
+
* Auto-load i18n translation bundles from the app config into the
|
|
98
|
+
* kernel's i18n service. Handles both `translations` (array of
|
|
99
|
+
* TranslationBundle) and `i18n` config (default locale, etc.).
|
|
100
|
+
*
|
|
101
|
+
* Gracefully skips when the i18n service is not registered —
|
|
102
|
+
* this keeps AppPlugin resilient across server/dev/mock environments.
|
|
103
|
+
*/
|
|
104
|
+
private loadTranslations;
|
|
95
105
|
}
|
|
96
106
|
|
|
97
107
|
interface Logger {
|
|
@@ -155,7 +165,7 @@ interface DispatcherPluginConfig {
|
|
|
155
165
|
* - /graphql (GraphQL)
|
|
156
166
|
* - /analytics (BI queries)
|
|
157
167
|
* - /packages (package management)
|
|
158
|
-
|
|
168
|
+
* - /i18n (internationalization — locales, translations, field labels)
|
|
159
169
|
* - /storage (file storage)
|
|
160
170
|
* - /automation (CRUD + triggers + runs)
|
|
161
171
|
*
|
|
@@ -279,9 +289,13 @@ declare class HttpDispatcher {
|
|
|
279
289
|
private error;
|
|
280
290
|
private ensureBroker;
|
|
281
291
|
/**
|
|
282
|
-
* Generates the discovery JSON response for the API root
|
|
292
|
+
* Generates the discovery JSON response for the API root.
|
|
293
|
+
*
|
|
294
|
+
* Uses the same async `resolveService()` fallback chain that request
|
|
295
|
+
* handlers use, so the reported service status is always consistent
|
|
296
|
+
* with the actual runtime availability.
|
|
283
297
|
*/
|
|
284
|
-
getDiscoveryInfo(prefix: string): {
|
|
298
|
+
getDiscoveryInfo(prefix: string): Promise<{
|
|
285
299
|
name: string;
|
|
286
300
|
version: string;
|
|
287
301
|
environment: string | undefined;
|
|
@@ -498,7 +512,7 @@ declare class HttpDispatcher {
|
|
|
498
512
|
supported: string[];
|
|
499
513
|
timezone: string;
|
|
500
514
|
};
|
|
501
|
-
}
|
|
515
|
+
}>;
|
|
502
516
|
/**
|
|
503
517
|
* Handles GraphQL requests
|
|
504
518
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -83,6 +83,7 @@ declare class DriverPlugin implements Plugin {
|
|
|
83
83
|
* Responsibilities:
|
|
84
84
|
* 1. Register App Manifest as a service (for ObjectQL discovery)
|
|
85
85
|
* 2. Execute Runtime `onEnable` hook (for code logic)
|
|
86
|
+
* 3. Auto-load i18n translation bundles into the kernel's i18n service
|
|
86
87
|
*/
|
|
87
88
|
declare class AppPlugin implements Plugin {
|
|
88
89
|
name: string;
|
|
@@ -92,6 +93,15 @@ declare class AppPlugin implements Plugin {
|
|
|
92
93
|
constructor(bundle: any);
|
|
93
94
|
init: (ctx: PluginContext) => Promise<void>;
|
|
94
95
|
start: (ctx: PluginContext) => Promise<void>;
|
|
96
|
+
/**
|
|
97
|
+
* Auto-load i18n translation bundles from the app config into the
|
|
98
|
+
* kernel's i18n service. Handles both `translations` (array of
|
|
99
|
+
* TranslationBundle) and `i18n` config (default locale, etc.).
|
|
100
|
+
*
|
|
101
|
+
* Gracefully skips when the i18n service is not registered —
|
|
102
|
+
* this keeps AppPlugin resilient across server/dev/mock environments.
|
|
103
|
+
*/
|
|
104
|
+
private loadTranslations;
|
|
95
105
|
}
|
|
96
106
|
|
|
97
107
|
interface Logger {
|
|
@@ -155,7 +165,7 @@ interface DispatcherPluginConfig {
|
|
|
155
165
|
* - /graphql (GraphQL)
|
|
156
166
|
* - /analytics (BI queries)
|
|
157
167
|
* - /packages (package management)
|
|
158
|
-
|
|
168
|
+
* - /i18n (internationalization — locales, translations, field labels)
|
|
159
169
|
* - /storage (file storage)
|
|
160
170
|
* - /automation (CRUD + triggers + runs)
|
|
161
171
|
*
|
|
@@ -279,9 +289,13 @@ declare class HttpDispatcher {
|
|
|
279
289
|
private error;
|
|
280
290
|
private ensureBroker;
|
|
281
291
|
/**
|
|
282
|
-
* Generates the discovery JSON response for the API root
|
|
292
|
+
* Generates the discovery JSON response for the API root.
|
|
293
|
+
*
|
|
294
|
+
* Uses the same async `resolveService()` fallback chain that request
|
|
295
|
+
* handlers use, so the reported service status is always consistent
|
|
296
|
+
* with the actual runtime availability.
|
|
283
297
|
*/
|
|
284
|
-
getDiscoveryInfo(prefix: string): {
|
|
298
|
+
getDiscoveryInfo(prefix: string): Promise<{
|
|
285
299
|
name: string;
|
|
286
300
|
version: string;
|
|
287
301
|
environment: string | undefined;
|
|
@@ -498,7 +512,7 @@ declare class HttpDispatcher {
|
|
|
498
512
|
supported: string[];
|
|
499
513
|
timezone: string;
|
|
500
514
|
};
|
|
501
|
-
}
|
|
515
|
+
}>;
|
|
502
516
|
/**
|
|
503
517
|
* Handles GraphQL requests
|
|
504
518
|
*/
|
package/dist/index.js
CHANGED
|
@@ -76,6 +76,7 @@ var DriverPlugin = class {
|
|
|
76
76
|
};
|
|
77
77
|
|
|
78
78
|
// src/seed-loader.ts
|
|
79
|
+
import { SeedLoaderConfigSchema } from "@objectstack/spec/data";
|
|
79
80
|
var DEFAULT_EXTERNAL_ID_FIELD = "name";
|
|
80
81
|
var SeedLoaderService = class {
|
|
81
82
|
constructor(engine, metadata, logger) {
|
|
@@ -160,7 +161,6 @@ var SeedLoaderService = class {
|
|
|
160
161
|
return { nodes, insertOrder, circularDependencies };
|
|
161
162
|
}
|
|
162
163
|
async validate(datasets, config) {
|
|
163
|
-
const { SeedLoaderConfigSchema } = await import("@objectstack/spec/data");
|
|
164
164
|
const parsedConfig = SeedLoaderConfigSchema.parse({ ...config, dryRun: true });
|
|
165
165
|
return this.load({ datasets, config: parsedConfig });
|
|
166
166
|
}
|
|
@@ -606,7 +606,11 @@ var AppPlugin = class {
|
|
|
606
606
|
this.start = async (ctx) => {
|
|
607
607
|
const sys = this.bundle.manifest || this.bundle;
|
|
608
608
|
const appId = sys.id || sys.name;
|
|
609
|
-
|
|
609
|
+
let ql;
|
|
610
|
+
try {
|
|
611
|
+
ql = ctx.getService("objectql");
|
|
612
|
+
} catch {
|
|
613
|
+
}
|
|
610
614
|
if (!ql) {
|
|
611
615
|
ctx.logger.warn("ObjectQL engine service not found", {
|
|
612
616
|
appName: this.name,
|
|
@@ -640,6 +644,7 @@ var AppPlugin = class {
|
|
|
640
644
|
} else {
|
|
641
645
|
ctx.logger.debug("No runtime.onEnable function found", { appId });
|
|
642
646
|
}
|
|
647
|
+
this.loadTranslations(ctx, appId);
|
|
643
648
|
const seedDatasets = [];
|
|
644
649
|
if (Array.isArray(this.bundle.data)) {
|
|
645
650
|
seedDatasets.push(...this.bundle.data);
|
|
@@ -710,10 +715,72 @@ var AppPlugin = class {
|
|
|
710
715
|
this.name = `plugin.app.${appId}`;
|
|
711
716
|
this.version = sys.version;
|
|
712
717
|
}
|
|
718
|
+
/**
|
|
719
|
+
* Auto-load i18n translation bundles from the app config into the
|
|
720
|
+
* kernel's i18n service. Handles both `translations` (array of
|
|
721
|
+
* TranslationBundle) and `i18n` config (default locale, etc.).
|
|
722
|
+
*
|
|
723
|
+
* Gracefully skips when the i18n service is not registered —
|
|
724
|
+
* this keeps AppPlugin resilient across server/dev/mock environments.
|
|
725
|
+
*/
|
|
726
|
+
loadTranslations(ctx, appId) {
|
|
727
|
+
let i18nService;
|
|
728
|
+
try {
|
|
729
|
+
i18nService = ctx.getService("i18n");
|
|
730
|
+
} catch {
|
|
731
|
+
}
|
|
732
|
+
const bundles = [];
|
|
733
|
+
if (Array.isArray(this.bundle.translations)) {
|
|
734
|
+
bundles.push(...this.bundle.translations);
|
|
735
|
+
}
|
|
736
|
+
const manifest = this.bundle.manifest || this.bundle;
|
|
737
|
+
if (manifest && Array.isArray(manifest.translations) && manifest.translations !== this.bundle.translations) {
|
|
738
|
+
bundles.push(...manifest.translations);
|
|
739
|
+
}
|
|
740
|
+
if (!i18nService) {
|
|
741
|
+
if (bundles.length > 0) {
|
|
742
|
+
ctx.logger.warn(
|
|
743
|
+
`[i18n] App "${appId}" has ${bundles.length} translation bundle(s) but no i18n service is registered. Translations will not be served via REST API. Register I18nServicePlugin from @objectstack/service-i18n, or use DevPlugin which auto-detects translations and registers the i18n service automatically.`
|
|
744
|
+
);
|
|
745
|
+
} else {
|
|
746
|
+
ctx.logger.debug("[i18n] No i18n service registered; skipping translation loading", { appId });
|
|
747
|
+
}
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
const i18nConfig = this.bundle.i18n || (this.bundle.manifest || this.bundle)?.i18n;
|
|
751
|
+
if (i18nConfig?.defaultLocale && typeof i18nService.setDefaultLocale === "function") {
|
|
752
|
+
i18nService.setDefaultLocale(i18nConfig.defaultLocale);
|
|
753
|
+
ctx.logger.debug("[i18n] Set default locale", { appId, locale: i18nConfig.defaultLocale });
|
|
754
|
+
}
|
|
755
|
+
if (bundles.length === 0) {
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
let loadedLocales = 0;
|
|
759
|
+
for (const bundle of bundles) {
|
|
760
|
+
for (const [locale, data] of Object.entries(bundle)) {
|
|
761
|
+
if (data && typeof data === "object") {
|
|
762
|
+
try {
|
|
763
|
+
i18nService.loadTranslations(locale, data);
|
|
764
|
+
loadedLocales++;
|
|
765
|
+
} catch (err) {
|
|
766
|
+
ctx.logger.warn("[i18n] Failed to load translations", { appId, locale, error: err.message });
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
const svcAny = i18nService;
|
|
772
|
+
if (svcAny._fallback || svcAny._dev) {
|
|
773
|
+
ctx.logger.info(
|
|
774
|
+
`[i18n] Loaded ${loadedLocales} locale(s) into in-memory i18n fallback for "${appId}". For production, consider registering I18nServicePlugin from @objectstack/service-i18n.`
|
|
775
|
+
);
|
|
776
|
+
} else {
|
|
777
|
+
ctx.logger.info("[i18n] Loaded translation bundles", { appId, bundles: bundles.length, locales: loadedLocales });
|
|
778
|
+
}
|
|
779
|
+
}
|
|
713
780
|
};
|
|
714
781
|
|
|
715
782
|
// src/http-dispatcher.ts
|
|
716
|
-
import { getEnv } from "@objectstack/core";
|
|
783
|
+
import { getEnv, resolveLocale } from "@objectstack/core";
|
|
717
784
|
import { CoreServiceName } from "@objectstack/spec/system";
|
|
718
785
|
function randomUUID() {
|
|
719
786
|
if (globalThis.crypto && typeof globalThis.crypto.randomUUID === "function") {
|
|
@@ -749,25 +816,61 @@ var HttpDispatcher = class {
|
|
|
749
816
|
return this.kernel.broker;
|
|
750
817
|
}
|
|
751
818
|
/**
|
|
752
|
-
* Generates the discovery JSON response for the API root
|
|
819
|
+
* Generates the discovery JSON response for the API root.
|
|
820
|
+
*
|
|
821
|
+
* Uses the same async `resolveService()` fallback chain that request
|
|
822
|
+
* handlers use, so the reported service status is always consistent
|
|
823
|
+
* with the actual runtime availability.
|
|
753
824
|
*/
|
|
754
|
-
getDiscoveryInfo(prefix) {
|
|
755
|
-
const
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
825
|
+
async getDiscoveryInfo(prefix) {
|
|
826
|
+
const [
|
|
827
|
+
authSvc,
|
|
828
|
+
graphqlSvc,
|
|
829
|
+
searchSvc,
|
|
830
|
+
realtimeSvc,
|
|
831
|
+
filesSvc,
|
|
832
|
+
analyticsSvc,
|
|
833
|
+
workflowSvc,
|
|
834
|
+
aiSvc,
|
|
835
|
+
notificationSvc,
|
|
836
|
+
i18nSvc,
|
|
837
|
+
uiSvc,
|
|
838
|
+
automationSvc,
|
|
839
|
+
cacheSvc,
|
|
840
|
+
queueSvc,
|
|
841
|
+
jobSvc
|
|
842
|
+
] = await Promise.all([
|
|
843
|
+
this.resolveService(CoreServiceName.enum.auth),
|
|
844
|
+
this.resolveService(CoreServiceName.enum.graphql),
|
|
845
|
+
this.resolveService(CoreServiceName.enum.search),
|
|
846
|
+
this.resolveService(CoreServiceName.enum.realtime),
|
|
847
|
+
this.resolveService(CoreServiceName.enum["file-storage"]),
|
|
848
|
+
this.resolveService(CoreServiceName.enum.analytics),
|
|
849
|
+
this.resolveService(CoreServiceName.enum.workflow),
|
|
850
|
+
this.resolveService(CoreServiceName.enum.ai),
|
|
851
|
+
this.resolveService(CoreServiceName.enum.notification),
|
|
852
|
+
this.resolveService(CoreServiceName.enum.i18n),
|
|
853
|
+
this.resolveService(CoreServiceName.enum.ui),
|
|
854
|
+
this.resolveService(CoreServiceName.enum.automation),
|
|
855
|
+
this.resolveService(CoreServiceName.enum.cache),
|
|
856
|
+
this.resolveService(CoreServiceName.enum.queue),
|
|
857
|
+
this.resolveService(CoreServiceName.enum.job)
|
|
858
|
+
]);
|
|
859
|
+
const hasAuth = !!authSvc;
|
|
860
|
+
const hasGraphQL = !!(graphqlSvc || this.kernel.graphql);
|
|
861
|
+
const hasSearch = !!searchSvc;
|
|
862
|
+
const hasWebSockets = !!realtimeSvc;
|
|
863
|
+
const hasFiles = !!filesSvc;
|
|
864
|
+
const hasAnalytics = !!analyticsSvc;
|
|
865
|
+
const hasWorkflow = !!workflowSvc;
|
|
866
|
+
const hasAi = !!aiSvc;
|
|
867
|
+
const hasNotification = !!notificationSvc;
|
|
868
|
+
const hasI18n = !!i18nSvc;
|
|
869
|
+
const hasUi = !!uiSvc;
|
|
870
|
+
const hasAutomation = !!automationSvc;
|
|
871
|
+
const hasCache = !!cacheSvc;
|
|
872
|
+
const hasQueue = !!queueSvc;
|
|
873
|
+
const hasJob = !!jobSvc;
|
|
771
874
|
const routes = {
|
|
772
875
|
data: `${prefix}/data`,
|
|
773
876
|
metadata: `${prefix}/meta`,
|
|
@@ -795,6 +898,16 @@ var HttpDispatcher = class {
|
|
|
795
898
|
status: "unavailable",
|
|
796
899
|
message: `Install a ${name} plugin to enable`
|
|
797
900
|
});
|
|
901
|
+
let locale = { default: "en", supported: ["en"], timezone: "UTC" };
|
|
902
|
+
if (hasI18n && i18nSvc) {
|
|
903
|
+
const defaultLocale = typeof i18nSvc.getDefaultLocale === "function" ? i18nSvc.getDefaultLocale() : "en";
|
|
904
|
+
const locales = typeof i18nSvc.getLocales === "function" ? i18nSvc.getLocales() : [];
|
|
905
|
+
locale = {
|
|
906
|
+
default: defaultLocale,
|
|
907
|
+
supported: locales.length > 0 ? locales : [defaultLocale],
|
|
908
|
+
timezone: "UTC"
|
|
909
|
+
};
|
|
910
|
+
}
|
|
798
911
|
return {
|
|
799
912
|
name: "ObjectOS",
|
|
800
913
|
version: "1.0.0",
|
|
@@ -834,11 +947,7 @@ var HttpDispatcher = class {
|
|
|
834
947
|
"file-storage": hasFiles ? svcAvailable(routes.storage) : svcUnavailable("file-storage"),
|
|
835
948
|
search: hasSearch ? svcAvailable() : svcUnavailable("search")
|
|
836
949
|
},
|
|
837
|
-
locale
|
|
838
|
-
default: "en",
|
|
839
|
-
supported: ["en", "zh-CN"],
|
|
840
|
-
timezone: "UTC"
|
|
841
|
-
}
|
|
950
|
+
locale
|
|
842
951
|
};
|
|
843
952
|
}
|
|
844
953
|
/**
|
|
@@ -1152,13 +1261,24 @@ var HttpDispatcher = class {
|
|
|
1152
1261
|
if (parts[0] === "translations") {
|
|
1153
1262
|
const locale = parts[1] ? decodeURIComponent(parts[1]) : query?.locale;
|
|
1154
1263
|
if (!locale) return { handled: true, response: this.error("Missing locale parameter", 400) };
|
|
1155
|
-
|
|
1264
|
+
let translations = i18nService.getTranslations(locale);
|
|
1265
|
+
if (Object.keys(translations).length === 0) {
|
|
1266
|
+
const availableLocales = typeof i18nService.getLocales === "function" ? i18nService.getLocales() : [];
|
|
1267
|
+
const resolved = resolveLocale(locale, availableLocales);
|
|
1268
|
+
if (resolved && resolved !== locale) {
|
|
1269
|
+
translations = i18nService.getTranslations(resolved);
|
|
1270
|
+
return { handled: true, response: this.success({ locale: resolved, requestedLocale: locale, translations }) };
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1156
1273
|
return { handled: true, response: this.success({ locale, translations }) };
|
|
1157
1274
|
}
|
|
1158
1275
|
if (parts[0] === "labels" && parts.length >= 2) {
|
|
1159
1276
|
const objectName = decodeURIComponent(parts[1]);
|
|
1160
|
-
|
|
1277
|
+
let locale = parts[2] ? decodeURIComponent(parts[2]) : query?.locale;
|
|
1161
1278
|
if (!locale) return { handled: true, response: this.error("Missing locale parameter", 400) };
|
|
1279
|
+
const availableLocales = typeof i18nService.getLocales === "function" ? i18nService.getLocales() : [];
|
|
1280
|
+
const resolved = resolveLocale(locale, availableLocales);
|
|
1281
|
+
if (resolved) locale = resolved;
|
|
1162
1282
|
if (typeof i18nService.getFieldLabels === "function") {
|
|
1163
1283
|
const labels2 = i18nService.getFieldLabels(objectName, locale);
|
|
1164
1284
|
return { handled: true, response: this.success({ object: objectName, locale, labels: labels2 }) };
|
|
@@ -1533,7 +1653,7 @@ var HttpDispatcher = class {
|
|
|
1533
1653
|
async dispatch(method, path, body, query, context) {
|
|
1534
1654
|
const cleanPath = path.replace(/\/$/, "");
|
|
1535
1655
|
if (cleanPath === "" && method === "GET") {
|
|
1536
|
-
const info = this.getDiscoveryInfo("");
|
|
1656
|
+
const info = await this.getDiscoveryInfo("");
|
|
1537
1657
|
return {
|
|
1538
1658
|
handled: true,
|
|
1539
1659
|
response: this.success(info)
|
|
@@ -1681,10 +1801,10 @@ function createDispatcherPlugin(config = {}) {
|
|
|
1681
1801
|
const dispatcher = new HttpDispatcher(kernel);
|
|
1682
1802
|
const prefix = config.prefix || "/api/v1";
|
|
1683
1803
|
server.get("/.well-known/objectstack", async (_req, res) => {
|
|
1684
|
-
res.json({ data: dispatcher.getDiscoveryInfo(prefix) });
|
|
1804
|
+
res.json({ data: await dispatcher.getDiscoveryInfo(prefix) });
|
|
1685
1805
|
});
|
|
1686
1806
|
server.get(`${prefix}/discovery`, async (_req, res) => {
|
|
1687
|
-
res.json({ data: dispatcher.getDiscoveryInfo(prefix) });
|
|
1807
|
+
res.json({ data: await dispatcher.getDiscoveryInfo(prefix) });
|
|
1688
1808
|
});
|
|
1689
1809
|
server.post(`${prefix}/auth/login`, async (req, res) => {
|
|
1690
1810
|
try {
|
|
@@ -1806,6 +1926,30 @@ function createDispatcherPlugin(config = {}) {
|
|
|
1806
1926
|
errorResponse(err, res);
|
|
1807
1927
|
}
|
|
1808
1928
|
});
|
|
1929
|
+
server.get(`${prefix}/i18n/locales`, async (req, res) => {
|
|
1930
|
+
try {
|
|
1931
|
+
const result = await dispatcher.handleI18n("/locales", "GET", req.query, { request: req });
|
|
1932
|
+
sendResult(result, res);
|
|
1933
|
+
} catch (err) {
|
|
1934
|
+
errorResponse(err, res);
|
|
1935
|
+
}
|
|
1936
|
+
});
|
|
1937
|
+
server.get(`${prefix}/i18n/translations/:locale`, async (req, res) => {
|
|
1938
|
+
try {
|
|
1939
|
+
const result = await dispatcher.handleI18n(`/translations/${req.params.locale}`, "GET", req.query, { request: req });
|
|
1940
|
+
sendResult(result, res);
|
|
1941
|
+
} catch (err) {
|
|
1942
|
+
errorResponse(err, res);
|
|
1943
|
+
}
|
|
1944
|
+
});
|
|
1945
|
+
server.get(`${prefix}/i18n/labels/:object/:locale`, async (req, res) => {
|
|
1946
|
+
try {
|
|
1947
|
+
const result = await dispatcher.handleI18n(`/labels/${req.params.object}/${req.params.locale}`, "GET", req.query, { request: req });
|
|
1948
|
+
sendResult(result, res);
|
|
1949
|
+
} catch (err) {
|
|
1950
|
+
errorResponse(err, res);
|
|
1951
|
+
}
|
|
1952
|
+
});
|
|
1809
1953
|
server.get(`${prefix}/automation`, async (req, res) => {
|
|
1810
1954
|
try {
|
|
1811
1955
|
const result = await dispatcher.handleAutomation("", "GET", {}, { request: req });
|