@objectstack/rest 8.0.1 → 9.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +81 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +81 -16
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -254,6 +254,9 @@ var RouteGroupBuilder = class {
|
|
|
254
254
|
var import_meta = {};
|
|
255
255
|
var logError = (...args) => globalThis.console?.error(...args);
|
|
256
256
|
var TRANSLATABLE_META_TYPES = /* @__PURE__ */ new Set(["view", "action", "object", "app", "dashboard"]);
|
|
257
|
+
function isMetaEnvelope(value) {
|
|
258
|
+
return !!value && typeof value === "object" && typeof value.type === "string" && typeof value.name === "string" && value.item != null && typeof value.item === "object" && !Array.isArray(value.item);
|
|
259
|
+
}
|
|
257
260
|
function mapDataError(error, object) {
|
|
258
261
|
if (error?.code === "CONCURRENT_UPDATE" || error?.name === "ConcurrentUpdateError") {
|
|
259
262
|
return {
|
|
@@ -543,37 +546,49 @@ var RestServer = class {
|
|
|
543
546
|
this.hostnameCache.set(host, { value: result, expiresAt: now + this.hostnameCacheTtlMs });
|
|
544
547
|
return result;
|
|
545
548
|
}
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
+
/**
|
|
550
|
+
* Resolve the environment a request targets: explicit id → tenant hostname
|
|
551
|
+
* → `X-Environment-Id` header → single-project default. Returns undefined
|
|
552
|
+
* for control-plane requests. Shared by every per-environment service
|
|
553
|
+
* resolution (protocol, analytics, …) so they can never disagree about
|
|
554
|
+
* which kernel a request belongs to.
|
|
555
|
+
*/
|
|
556
|
+
async resolveRequestEnvironmentId(environmentId, req) {
|
|
557
|
+
if (environmentId) return environmentId;
|
|
558
|
+
if (req && this.envRegistry && this.kernelManager) {
|
|
549
559
|
const host = this.extractHostname(req);
|
|
550
560
|
if (host) {
|
|
551
561
|
try {
|
|
552
562
|
const result = await this.resolveHostnameCached(host);
|
|
553
|
-
if (result?.environmentId)
|
|
563
|
+
if (result?.environmentId) return result.environmentId;
|
|
554
564
|
} catch {
|
|
555
565
|
}
|
|
556
566
|
}
|
|
557
|
-
if (
|
|
567
|
+
if (typeof this.envRegistry.resolveById === "function") {
|
|
558
568
|
const headerVal = this.extractProjectIdHeader(req);
|
|
559
569
|
if (headerVal) {
|
|
560
570
|
try {
|
|
561
571
|
const driver = await this.envRegistry.resolveById(headerVal);
|
|
562
|
-
if (driver)
|
|
572
|
+
if (driver) return headerVal;
|
|
563
573
|
} catch {
|
|
564
574
|
}
|
|
565
575
|
}
|
|
566
576
|
}
|
|
567
577
|
}
|
|
568
|
-
if (
|
|
578
|
+
if (this.defaultEnvironmentIdProvider) {
|
|
569
579
|
try {
|
|
570
580
|
const def = this.defaultEnvironmentIdProvider();
|
|
571
|
-
if (def)
|
|
581
|
+
if (def) return def;
|
|
572
582
|
} catch {
|
|
573
583
|
}
|
|
574
584
|
}
|
|
575
|
-
|
|
576
|
-
|
|
585
|
+
return void 0;
|
|
586
|
+
}
|
|
587
|
+
async resolveProtocol(environmentId, req) {
|
|
588
|
+
if (environmentId === "platform") return this.protocol;
|
|
589
|
+
const envId = await this.resolveRequestEnvironmentId(environmentId, req);
|
|
590
|
+
if (!envId || !this.kernelManager) return this.protocol;
|
|
591
|
+
const kernel = await this.kernelManager.getOrCreate(envId);
|
|
577
592
|
return kernel.getServiceAsync("protocol");
|
|
578
593
|
}
|
|
579
594
|
/**
|
|
@@ -937,21 +952,28 @@ var RestServer = class {
|
|
|
937
952
|
const locale = this.extractLocale(req, i18n);
|
|
938
953
|
if (!locale) return item;
|
|
939
954
|
const { translateMetadataDocument } = await import("@objectstack/spec/system");
|
|
955
|
+
if (isMetaEnvelope(item)) {
|
|
956
|
+
return { ...item, item: translateMetadataDocument(type, item.item, bundle, { locale }) };
|
|
957
|
+
}
|
|
940
958
|
return translateMetadataDocument(type, item, bundle, { locale });
|
|
941
959
|
}
|
|
942
960
|
/**
|
|
943
961
|
* Translate a list of metadata documents using `translateMetaItem`.
|
|
944
962
|
*/
|
|
945
963
|
async translateMetaItems(req, type, environmentId, items) {
|
|
946
|
-
if (!Array.isArray(items)) return items;
|
|
947
964
|
if (!TRANSLATABLE_META_TYPES.has(type)) return items;
|
|
965
|
+
const arr = Array.isArray(items) ? items : items && typeof items === "object" && Array.isArray(items.items) ? items.items : null;
|
|
966
|
+
if (!arr) return items;
|
|
948
967
|
const i18n = await this.resolveI18nService(environmentId, req);
|
|
949
968
|
const bundle = this.buildTranslationBundle(i18n);
|
|
950
969
|
if (!bundle) return items;
|
|
951
970
|
const locale = this.extractLocale(req, i18n);
|
|
952
971
|
if (!locale) return items;
|
|
953
972
|
const { translateMetadataDocument } = await import("@objectstack/spec/system");
|
|
954
|
-
|
|
973
|
+
const translated = arr.map(
|
|
974
|
+
(item) => isMetaEnvelope(item) ? { ...item, item: translateMetadataDocument(type, item.item, bundle, { locale }) } : translateMetadataDocument(type, item, bundle, { locale })
|
|
975
|
+
);
|
|
976
|
+
return Array.isArray(items) ? translated : { ...items, items: translated };
|
|
955
977
|
}
|
|
956
978
|
/**
|
|
957
979
|
* Translate the `entries` payload returned by `getMetaTypes()` — applies
|
|
@@ -2961,7 +2983,16 @@ var RestServer = class {
|
|
|
2961
2983
|
*/
|
|
2962
2984
|
registerAnalyticsEndpoints(basePath) {
|
|
2963
2985
|
const isScoped = basePath.includes("/environments/:environmentId");
|
|
2964
|
-
const resolveService = async (environmentId) => {
|
|
2986
|
+
const resolveService = async (environmentId, req) => {
|
|
2987
|
+
try {
|
|
2988
|
+
const envId = await this.resolveRequestEnvironmentId(environmentId, req);
|
|
2989
|
+
if (envId && envId !== "platform" && this.kernelManager) {
|
|
2990
|
+
const kernel = await this.kernelManager.getOrCreate(envId);
|
|
2991
|
+
const svc = await kernel.getServiceAsync("analytics").catch(() => void 0);
|
|
2992
|
+
if (svc) return svc;
|
|
2993
|
+
}
|
|
2994
|
+
} catch {
|
|
2995
|
+
}
|
|
2965
2996
|
if (!this.analyticsServiceProvider) return void 0;
|
|
2966
2997
|
try {
|
|
2967
2998
|
return await this.analyticsServiceProvider(environmentId);
|
|
@@ -2977,7 +3008,7 @@ var RestServer = class {
|
|
|
2977
3008
|
const environmentId = isScoped ? req.params?.environmentId : void 0;
|
|
2978
3009
|
const context = await this.resolveExecCtx(environmentId, req);
|
|
2979
3010
|
if (this.enforceAuth(req, res, context)) return;
|
|
2980
|
-
const svc = await resolveService(environmentId);
|
|
3011
|
+
const svc = await resolveService(environmentId, req);
|
|
2981
3012
|
if (!svc || typeof svc.queryDataset !== "function") {
|
|
2982
3013
|
return res.status(501).json({
|
|
2983
3014
|
code: "NOT_IMPLEMENTED",
|
|
@@ -2992,10 +3023,11 @@ var RestServer = class {
|
|
|
2992
3023
|
message: "body.selection.measures must be a non-empty array of measure names."
|
|
2993
3024
|
});
|
|
2994
3025
|
}
|
|
3026
|
+
const previewDrafts = body.previewDrafts === true || req.query?.preview === "draft";
|
|
2995
3027
|
let dataset = body.dataset;
|
|
2996
3028
|
if (!dataset && body.datasetName) {
|
|
2997
3029
|
const p = await this.resolveProtocol(environmentId, req);
|
|
2998
|
-
const items = await p.getMetaItems?.({ type: "dataset" }).catch(() => null);
|
|
3030
|
+
const items = await p.getMetaItems?.({ type: "dataset", previewDrafts }).catch(() => null);
|
|
2999
3031
|
const list = Array.isArray(items?.items) ? items.items : Array.isArray(items) ? items : [];
|
|
3000
3032
|
dataset = list.find((d) => d?.name === body.datasetName);
|
|
3001
3033
|
if (!dataset) {
|
|
@@ -3015,7 +3047,12 @@ var RestServer = class {
|
|
|
3015
3047
|
detail: String(verr?.message ?? verr).slice(0, 1e3)
|
|
3016
3048
|
});
|
|
3017
3049
|
}
|
|
3018
|
-
const result = await svc.queryDataset(
|
|
3050
|
+
const result = await svc.queryDataset(
|
|
3051
|
+
dataset,
|
|
3052
|
+
selection,
|
|
3053
|
+
context ?? void 0,
|
|
3054
|
+
previewDrafts ? { previewDrafts: true } : void 0
|
|
3055
|
+
);
|
|
3019
3056
|
res.json(result);
|
|
3020
3057
|
} catch (error) {
|
|
3021
3058
|
const msg = String(error?.message ?? error ?? "");
|
|
@@ -3643,6 +3680,34 @@ var RestServer = class {
|
|
|
3643
3680
|
};
|
|
3644
3681
|
decisionRoute("approve");
|
|
3645
3682
|
decisionRoute("reject");
|
|
3683
|
+
this.routeManager.register({
|
|
3684
|
+
method: "POST",
|
|
3685
|
+
path: `${dataPath}/approvals/requests/:id/recall`,
|
|
3686
|
+
handler: async (req, res) => {
|
|
3687
|
+
try {
|
|
3688
|
+
const environmentId = isScoped ? req.params?.environmentId : void 0;
|
|
3689
|
+
const context = await this.resolveExecCtx(environmentId, req);
|
|
3690
|
+
if (this.enforceAuth(req, res, context)) return;
|
|
3691
|
+
const svc = await resolveService(environmentId);
|
|
3692
|
+
if (!svc || typeof svc.recall !== "function") return respond501(res);
|
|
3693
|
+
const body = req.body ?? {};
|
|
3694
|
+
try {
|
|
3695
|
+
const out = await svc.recall(req.params.id, {
|
|
3696
|
+
actorId: body.actorId ?? body.actor_id ?? context?.userId,
|
|
3697
|
+
comment: body.comment
|
|
3698
|
+
}, context ?? {});
|
|
3699
|
+
res.json(out);
|
|
3700
|
+
} catch (err) {
|
|
3701
|
+
if (handleApprovalError(res, err)) return;
|
|
3702
|
+
throw err;
|
|
3703
|
+
}
|
|
3704
|
+
} catch (error) {
|
|
3705
|
+
logError("[REST] recall approval error:", error);
|
|
3706
|
+
res.status(500).json({ code: "APPROVAL_RECALL_FAILED", error: String(error?.message ?? error).slice(0, 500) });
|
|
3707
|
+
}
|
|
3708
|
+
},
|
|
3709
|
+
metadata: { summary: "Recall (withdraw) an approval request", tags: ["approvals"] }
|
|
3710
|
+
});
|
|
3646
3711
|
this.routeManager.register({
|
|
3647
3712
|
method: "GET",
|
|
3648
3713
|
path: `${dataPath}/approvals/requests/:id/actions`,
|