@objectstack/rest 5.1.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -246,7 +246,7 @@ function mapDataError(error, object) {
246
246
  }
247
247
  const raw = String(error?.message ?? error ?? "");
248
248
  const lower = raw.toLowerCase();
249
- if (raw.includes("[ProjectKernelFactory]") && (lower.includes("missing database_url") || lower.includes("not found"))) {
249
+ if (raw.includes("[EnvironmentKernelFactory]") && (lower.includes("missing database_url") || lower.includes("not found"))) {
250
250
  const isProvisioning = lower.includes("status='provisioning'") || lower.includes("status='pending'");
251
251
  const isFailed = lower.includes("status='failed'");
252
252
  return {
@@ -403,13 +403,19 @@ function rowsToCsv(fields, rows, includeHeader) {
403
403
  return lines.join("\r\n") + (lines.length > 0 ? "\r\n" : "");
404
404
  }
405
405
  var RestServer = class {
406
- constructor(server, protocol, config = {}, kernelManager, envRegistry, defaultProjectIdProvider, authServiceProvider, objectQLProvider, emailServiceProvider, sharingServiceProvider, reportsServiceProvider, approvalsServiceProvider, sharingRulesServiceProvider, i18nServiceProvider) {
406
+ constructor(server, protocol, config = {}, kernelManager, envRegistry, defaultEnvironmentIdProvider, authServiceProvider, objectQLProvider, emailServiceProvider, sharingServiceProvider, reportsServiceProvider, approvalsServiceProvider, sharingRulesServiceProvider, i18nServiceProvider) {
407
+ /**
408
+ * Lazily load the OpenAPI spec JSON shipped by @objectstack/spec.
409
+ * Cached after first read. Resilient to missing files / parse errors
410
+ * so a degraded environment still boots.
411
+ */
412
+ this._openApiSpecCache = void 0;
407
413
  this.protocol = protocol;
408
414
  this.config = this.normalizeConfig(config);
409
415
  this.routeManager = new RouteManager(server);
410
416
  this.kernelManager = kernelManager;
411
417
  this.envRegistry = envRegistry;
412
- this.defaultProjectIdProvider = defaultProjectIdProvider;
418
+ this.defaultEnvironmentIdProvider = defaultEnvironmentIdProvider;
413
419
  this.authServiceProvider = authServiceProvider;
414
420
  this.objectQLProvider = objectQLProvider;
415
421
  this.emailServiceProvider = emailServiceProvider;
@@ -420,20 +426,20 @@ var RestServer = class {
420
426
  this.i18nServiceProvider = i18nServiceProvider;
421
427
  }
422
428
  /**
423
- * Resolve the protocol for a given request. When `projectId` is present
429
+ * Resolve the protocol for a given request. When `environmentId` is present
424
430
  * and a KernelManager is wired, fetch the per-project kernel's
425
431
  * `protocol` service so metadata / data / UI reads hit the project's
426
432
  * own registry and datastore.
427
433
  *
428
- * When `projectId` is absent on an unscoped route and an `envRegistry`
434
+ * When `environmentId` is absent on an unscoped route and an `envRegistry`
429
435
  * is wired (runtime mode), the resolution chain is:
430
- * 1. Hostname → projectId (`envRegistry.resolveByHostname`)
431
- * 2. `X-Project-Id` header → projectId (`envRegistry.resolveById`)
432
- * 3. Default-project fallback (`defaultProjectIdProvider`, set by
433
- * `createSingleProjectPlugin`)
436
+ * 1. Hostname → environmentId (`envRegistry.resolveByHostname`)
437
+ * 2. `X-Environment-Id` header → environmentId (`envRegistry.resolveById`)
438
+ * 3. Default-project fallback (`defaultEnvironmentIdProvider`, set by
439
+ * `createSingleEnvironmentPlugin`)
434
440
  * 4. Control-plane protocol captured at boot.
435
441
  *
436
- * Special case: `projectId === 'platform'` is a reserved virtual id used
442
+ * Special case: `environmentId === 'platform'` is a reserved virtual id used
437
443
  * by Studio to address the control plane through the regular project
438
444
  * URL shape (`/projects/platform/...`). It is NOT a row in the projects
439
445
  * table, so we must never call `KernelManager.getOrCreate('platform')`.
@@ -441,37 +447,37 @@ var RestServer = class {
441
447
  * (and any other client) speak a single, uniform URL family without
442
448
  * duplicating route logic for the platform surface.
443
449
  */
444
- async resolveProtocol(projectId, req) {
445
- if (projectId === "platform") return this.protocol;
446
- if (!projectId && req && this.envRegistry && this.kernelManager) {
450
+ async resolveProtocol(environmentId, req) {
451
+ if (environmentId === "platform") return this.protocol;
452
+ if (!environmentId && req && this.envRegistry && this.kernelManager) {
447
453
  const host = this.extractHostname(req);
448
454
  if (host) {
449
455
  try {
450
456
  const result = await this.envRegistry.resolveByHostname(host);
451
- if (result?.projectId) projectId = result.projectId;
457
+ if (result?.environmentId) environmentId = result.environmentId;
452
458
  } catch {
453
459
  }
454
460
  }
455
- if (!projectId && typeof this.envRegistry.resolveById === "function") {
461
+ if (!environmentId && typeof this.envRegistry.resolveById === "function") {
456
462
  const headerVal = this.extractProjectIdHeader(req);
457
463
  if (headerVal) {
458
464
  try {
459
465
  const driver = await this.envRegistry.resolveById(headerVal);
460
- if (driver) projectId = headerVal;
466
+ if (driver) environmentId = headerVal;
461
467
  } catch {
462
468
  }
463
469
  }
464
470
  }
465
471
  }
466
- if (!projectId && this.defaultProjectIdProvider) {
472
+ if (!environmentId && this.defaultEnvironmentIdProvider) {
467
473
  try {
468
- const def = this.defaultProjectIdProvider();
469
- if (def) projectId = def;
474
+ const def = this.defaultEnvironmentIdProvider();
475
+ if (def) environmentId = def;
470
476
  } catch {
471
477
  }
472
478
  }
473
- if (!projectId || !this.kernelManager) return this.protocol;
474
- const kernel = await this.kernelManager.getOrCreate(projectId);
479
+ if (!environmentId || !this.kernelManager) return this.protocol;
480
+ const kernel = await this.kernelManager.getOrCreate(environmentId);
475
481
  return kernel.getServiceAsync("protocol");
476
482
  }
477
483
  /**
@@ -480,43 +486,43 @@ var RestServer = class {
480
486
  * registered, so callers can short-circuit and skip translation rather
481
487
  * than failing.
482
488
  *
483
- * Mirrors `resolveProtocol`'s lookup chain: explicit `projectId` from the
489
+ * Mirrors `resolveProtocol`'s lookup chain: explicit `environmentId` from the
484
490
  * route → kernel-managed `i18n` service. Control-plane / unscoped
485
491
  * requests intentionally return `undefined` because the platform kernel
486
492
  * does not own per-app translation bundles.
487
493
  */
488
- async resolveI18nService(projectId, req) {
489
- if (projectId === "platform") return void 0;
490
- if (!projectId && req && this.envRegistry && this.kernelManager) {
494
+ async resolveI18nService(environmentId, req) {
495
+ if (environmentId === "platform") return void 0;
496
+ if (!environmentId && req && this.envRegistry && this.kernelManager) {
491
497
  const host = this.extractHostname(req);
492
498
  if (host) {
493
499
  try {
494
500
  const result = await this.envRegistry.resolveByHostname(host);
495
- if (result?.projectId) projectId = result.projectId;
501
+ if (result?.environmentId) environmentId = result.environmentId;
496
502
  } catch {
497
503
  }
498
504
  }
499
- if (!projectId && typeof this.envRegistry.resolveById === "function") {
505
+ if (!environmentId && typeof this.envRegistry.resolveById === "function") {
500
506
  const headerVal = this.extractProjectIdHeader(req);
501
507
  if (headerVal) {
502
508
  try {
503
509
  const driver = await this.envRegistry.resolveById(headerVal);
504
- if (driver) projectId = headerVal;
510
+ if (driver) environmentId = headerVal;
505
511
  } catch {
506
512
  }
507
513
  }
508
514
  }
509
515
  }
510
- if (!projectId && this.defaultProjectIdProvider) {
516
+ if (!environmentId && this.defaultEnvironmentIdProvider) {
511
517
  try {
512
- const def = this.defaultProjectIdProvider();
513
- if (def) projectId = def;
518
+ const def = this.defaultEnvironmentIdProvider();
519
+ if (def) environmentId = def;
514
520
  } catch {
515
521
  }
516
522
  }
517
- if (projectId && this.kernelManager) {
523
+ if (environmentId && this.kernelManager) {
518
524
  try {
519
- const kernel = await this.kernelManager.getOrCreate(projectId);
525
+ const kernel = await this.kernelManager.getOrCreate(environmentId);
520
526
  const svc = await kernel.getServiceAsync("i18n");
521
527
  if (svc) return svc;
522
528
  } catch {
@@ -524,7 +530,7 @@ var RestServer = class {
524
530
  }
525
531
  if (this.i18nServiceProvider) {
526
532
  try {
527
- return await this.i18nServiceProvider(projectId);
533
+ return await this.i18nServiceProvider(environmentId);
528
534
  } catch {
529
535
  return void 0;
530
536
  }
@@ -556,23 +562,23 @@ var RestServer = class {
556
562
  * `undefined` for anonymous requests so callers can pass `context` as-is
557
563
  * to the protocol layer (the SecurityPlugin treats undefined as anon).
558
564
  */
559
- async resolveExecCtx(projectId, req) {
565
+ async resolveExecCtx(environmentId, req) {
560
566
  try {
561
- if (!projectId && req && this.envRegistry && this.kernelManager) {
567
+ if (!environmentId && req && this.envRegistry && this.kernelManager) {
562
568
  const host = this.extractHostname(req);
563
569
  if (host) {
564
570
  try {
565
571
  const result = await this.envRegistry.resolveByHostname(host);
566
- if (result?.projectId) projectId = result.projectId;
572
+ if (result?.environmentId) environmentId = result.environmentId;
567
573
  } catch {
568
574
  }
569
575
  }
570
- if (!projectId && typeof this.envRegistry.resolveById === "function") {
576
+ if (!environmentId && typeof this.envRegistry.resolveById === "function") {
571
577
  const headerVal = this.extractProjectIdHeader(req);
572
578
  if (headerVal) {
573
579
  try {
574
580
  const driver = await this.envRegistry.resolveById(headerVal);
575
- if (driver) projectId = headerVal;
581
+ if (driver) environmentId = headerVal;
576
582
  } catch {
577
583
  }
578
584
  }
@@ -580,13 +586,13 @@ var RestServer = class {
580
586
  }
581
587
  let authService;
582
588
  let kernel;
583
- if (projectId && projectId !== "platform" && this.kernelManager) {
584
- kernel = await this.kernelManager.getOrCreate(projectId);
589
+ if (environmentId && environmentId !== "platform" && this.kernelManager) {
590
+ kernel = await this.kernelManager.getOrCreate(environmentId);
585
591
  authService = await kernel.getServiceAsync("auth").catch(() => void 0);
586
592
  }
587
- if (!authService && this.defaultProjectIdProvider && this.kernelManager) {
593
+ if (!authService && this.defaultEnvironmentIdProvider && this.kernelManager) {
588
594
  try {
589
- const def = this.defaultProjectIdProvider();
595
+ const def = this.defaultEnvironmentIdProvider();
590
596
  if (def) {
591
597
  kernel = await this.kernelManager.getOrCreate(def);
592
598
  authService = await kernel.getServiceAsync("auth").catch(() => void 0);
@@ -595,7 +601,7 @@ var RestServer = class {
595
601
  }
596
602
  }
597
603
  if (!authService && this.authServiceProvider) {
598
- authService = await this.authServiceProvider(projectId).catch(() => void 0);
604
+ authService = await this.authServiceProvider(environmentId).catch(() => void 0);
599
605
  }
600
606
  if (!authService) return void 0;
601
607
  let api = authService.api;
@@ -628,7 +634,7 @@ var RestServer = class {
628
634
  ql = await kernel.getServiceAsync("objectql").catch(() => void 0);
629
635
  }
630
636
  if (!ql && this.objectQLProvider) {
631
- ql = await this.objectQLProvider(projectId).catch(() => void 0);
637
+ ql = await this.objectQLProvider(environmentId).catch(() => void 0);
632
638
  }
633
639
  if (ql && typeof ql.find === "function") {
634
640
  const sysOpts = { context: { isSystem: true } };
@@ -670,12 +676,40 @@ var RestServer = class {
670
676
  }
671
677
  } catch {
672
678
  }
679
+ let org_user_ids = [userId];
680
+ if (tenantId) {
681
+ try {
682
+ let ql;
683
+ if (kernel) {
684
+ ql = await kernel.getServiceAsync("objectql").catch(() => void 0);
685
+ }
686
+ if (!ql && this.objectQLProvider) {
687
+ ql = await this.objectQLProvider(environmentId).catch(() => void 0);
688
+ }
689
+ if (ql && typeof ql.find === "function") {
690
+ const sysOpts = { context: { isSystem: true } };
691
+ const memberRows = await ql.find("sys_member", {
692
+ where: { organization_id: tenantId },
693
+ limit: 1e3,
694
+ ...sysOpts
695
+ }).catch(() => []);
696
+ const ids = /* @__PURE__ */ new Set([userId]);
697
+ for (const m of memberRows ?? []) {
698
+ const uid = m.user_id ?? m.userId;
699
+ if (typeof uid === "string" && uid.length > 0) ids.add(uid);
700
+ }
701
+ org_user_ids = Array.from(ids);
702
+ }
703
+ } catch {
704
+ }
705
+ }
673
706
  return {
674
707
  userId,
675
708
  tenantId,
676
709
  roles,
677
710
  permissions,
678
- isSystem: false
711
+ isSystem: false,
712
+ org_user_ids
679
713
  };
680
714
  } catch {
681
715
  return void 0;
@@ -729,10 +763,10 @@ var RestServer = class {
729
763
  * locale yields a match. Falls through unchanged for unsupported types
730
764
  * or missing translations.
731
765
  */
732
- async translateMetaItem(req, type, projectId, item) {
766
+ async translateMetaItem(req, type, environmentId, item) {
733
767
  if (!item || typeof item !== "object") return item;
734
768
  if (type !== "view" && type !== "action" && type !== "object") return item;
735
- const i18n = await this.resolveI18nService(projectId, req);
769
+ const i18n = await this.resolveI18nService(environmentId, req);
736
770
  const bundle = this.buildTranslationBundle(i18n);
737
771
  if (!bundle) return item;
738
772
  const locale = this.extractLocale(req, i18n);
@@ -743,10 +777,10 @@ var RestServer = class {
743
777
  /**
744
778
  * Translate a list of metadata documents using `translateMetaItem`.
745
779
  */
746
- async translateMetaItems(req, type, projectId, items) {
780
+ async translateMetaItems(req, type, environmentId, items) {
747
781
  if (!Array.isArray(items)) return items;
748
782
  if (type !== "view" && type !== "action" && type !== "object") return items;
749
- const i18n = await this.resolveI18nService(projectId, req);
783
+ const i18n = await this.resolveI18nService(environmentId, req);
750
784
  const bundle = this.buildTranslationBundle(i18n);
751
785
  if (!bundle) return items;
752
786
  const locale = this.extractLocale(req, i18n);
@@ -780,7 +814,7 @@ var RestServer = class {
780
814
  return String(host).split(":")[0].toLowerCase();
781
815
  }
782
816
  /**
783
- * Pull the `X-Project-Id` header from a Node- or Fetch-style request.
817
+ * Pull the `X-Environment-Id` header from a Node- or Fetch-style request.
784
818
  * Header names are case-insensitive; we probe both casings to cover
785
819
  * adapters that don't normalize headers (e.g. raw Node http).
786
820
  */
@@ -789,9 +823,9 @@ var RestServer = class {
789
823
  if (!headers) return void 0;
790
824
  let val;
791
825
  if (typeof headers.get === "function") {
792
- val = headers.get("x-project-id") ?? headers.get("X-Project-Id");
826
+ val = headers.get("x-environment-id") ?? headers.get("X-Environment-Id");
793
827
  } else {
794
- val = headers["x-project-id"] ?? headers["X-Project-Id"];
828
+ val = headers["x-environment-id"] ?? headers["X-Environment-Id"];
795
829
  }
796
830
  if (Array.isArray(val)) val = val[0];
797
831
  if (typeof val !== "string") return void 0;
@@ -817,6 +851,7 @@ var RestServer = class {
817
851
  enableUi: api.enableUi ?? true,
818
852
  enableBatch: api.enableBatch ?? true,
819
853
  enableDiscovery: api.enableDiscovery ?? true,
854
+ enableOpenApi: api.enableOpenApi ?? true,
820
855
  enableSearch: api.enableSearch ?? true,
821
856
  enableProjectScoping: api.enableProjectScoping ?? false,
822
857
  projectResolution: api.projectResolution ?? "auto",
@@ -875,16 +910,16 @@ var RestServer = class {
875
910
  }
876
911
  /**
877
912
  * Get the project-scoped base path for a given unscoped base.
878
- * Example: `/api/v1` → `/api/v1/projects/:projectId`.
913
+ * Example: `/api/v1` → `/api/v1/environments/:environmentId`.
879
914
  */
880
915
  getScopedBasePath(basePath) {
881
- return `${basePath}/projects/:projectId`;
916
+ return `${basePath}/environments/:environmentId`;
882
917
  }
883
918
  /**
884
919
  * Register all REST API routes
885
920
  *
886
921
  * When `enableProjectScoping` is true, routes are registered under
887
- * `/api/v1/projects/:projectId/...`. The `projectResolution` strategy
922
+ * `/api/v1/environments/:environmentId/...`. The `projectResolution` strategy
888
923
  * controls whether unscoped legacy routes remain available:
889
924
  * - `required` → only scoped routes registered.
890
925
  * - `optional` / `auto` → both scoped and unscoped routes registered.
@@ -896,6 +931,9 @@ var RestServer = class {
896
931
  if (this.config.api.enableDiscovery) {
897
932
  this.registerDiscoveryEndpoints(bp);
898
933
  }
934
+ if (this.config.api.enableOpenApi ?? true) {
935
+ this.registerOpenApiEndpoints(bp);
936
+ }
899
937
  if (this.config.api.enableMetadata) {
900
938
  this.registerMetadataEndpoints(bp);
901
939
  }
@@ -935,12 +973,12 @@ var RestServer = class {
935
973
  * Register discovery endpoints
936
974
  */
937
975
  registerDiscoveryEndpoints(basePath) {
938
- const isScoped = basePath.includes("/projects/:projectId");
976
+ const isScoped = basePath.includes("/environments/:environmentId");
939
977
  const discoveryHandler = async (req, res) => {
940
978
  try {
941
979
  const discovery = await this.protocol.getDiscovery();
942
980
  discovery.version = this.config.api.version;
943
- const realBase = isScoped ? basePath.replace(":projectId", req.params?.projectId ?? ":projectId") : basePath;
981
+ const realBase = isScoped ? basePath.replace(":environmentId", req.params?.environmentId ?? ":environmentId") : basePath;
944
982
  if (discovery.routes) {
945
983
  if (this.config.api.enableCrud) {
946
984
  discovery.routes.data = `${realBase}${this.config.crud.dataPrefix}`;
@@ -952,7 +990,7 @@ var RestServer = class {
952
990
  discovery.routes.ui = `${realBase}/ui`;
953
991
  }
954
992
  if (discovery.routes.auth) {
955
- const unscopedBase = isScoped ? basePath.replace(/\/projects\/:projectId$/, "") : basePath;
993
+ const unscopedBase = isScoped ? basePath.replace(/\/projects\/:environmentId$/, "") : basePath;
956
994
  discovery.routes.auth = `${unscopedBase}/auth`;
957
995
  }
958
996
  }
@@ -960,7 +998,7 @@ var RestServer = class {
960
998
  enabled: this.config.api.enableProjectScoping,
961
999
  resolution: this.config.api.projectResolution,
962
1000
  scoped: isScoped,
963
- projectId: isScoped ? req.params?.projectId : void 0
1001
+ environmentId: isScoped ? req.params?.environmentId : void 0
964
1002
  };
965
1003
  res.json(discovery);
966
1004
  } catch (error) {
@@ -987,21 +1025,150 @@ var RestServer = class {
987
1025
  }
988
1026
  });
989
1027
  }
1028
+ /**
1029
+ * Register OpenAPI 3.1 spec + interactive docs viewer.
1030
+ *
1031
+ * GET <basePath>/openapi.json → enriched OpenAPI document
1032
+ * GET <basePath>/docs → Scalar-rendered HTML (CDN, no dep)
1033
+ *
1034
+ * Enrichment at request time:
1035
+ * - servers[0].url — derived from the request's Host header
1036
+ * - paths — `{object}` placeholders expanded into
1037
+ * one concrete path per registered object
1038
+ * from the protocol's discovery metadata
1039
+ *
1040
+ * The base spec is loaded lazily from @objectstack/spec/openapi.json
1041
+ * (shipped pre-generated by spec's build pipeline) so we don't pay
1042
+ * the cost of regenerating on every request, and a missing or
1043
+ * malformed file degrades to a stub instead of crashing.
1044
+ */
1045
+ registerOpenApiEndpoints(basePath) {
1046
+ const isScoped = basePath.includes("/environments/:environmentId");
1047
+ const openApiHandler = async (req, res) => {
1048
+ try {
1049
+ const spec = await this.loadOpenApiSpec();
1050
+ if (!spec) {
1051
+ res.status?.(503);
1052
+ res.json({
1053
+ error: "openapi_unavailable",
1054
+ message: "OpenAPI spec is not bundled with this runtime."
1055
+ });
1056
+ return;
1057
+ }
1058
+ const enriched = { ...spec, servers: [...spec.servers ?? []] };
1059
+ const host = req.headers?.host ?? req.headers?.["host"];
1060
+ const proto = req.headers?.["x-forwarded-proto"] || req.protocol || "http";
1061
+ if (host) {
1062
+ enriched.servers = [
1063
+ { url: `${proto}://${host}`, description: "Current server" },
1064
+ ...spec.servers ?? []
1065
+ ];
1066
+ }
1067
+ try {
1068
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
1069
+ const protocol = await this.resolveProtocol(environmentId, req);
1070
+ const items = await protocol?.getMetaItems?.({ type: "object" }).catch(() => null);
1071
+ const objects = Array.isArray(items?.items) ? items.items.map((i) => i?.name).filter(Boolean) : Array.isArray(items) ? items.map((i) => i?.name).filter(Boolean) : [];
1072
+ if (objects.length > 0 && enriched.paths) {
1073
+ const expanded = {};
1074
+ for (const [p, def] of Object.entries(enriched.paths)) {
1075
+ if (p.includes("{object}")) {
1076
+ expanded[p] = { ...def, "x-template": true };
1077
+ for (const obj of objects) {
1078
+ expanded[p.replace("{object}", obj)] = def;
1079
+ }
1080
+ } else {
1081
+ expanded[p] = def;
1082
+ }
1083
+ }
1084
+ enriched.paths = expanded;
1085
+ }
1086
+ } catch {
1087
+ }
1088
+ if (enriched.info) {
1089
+ enriched.info = {
1090
+ ...enriched.info,
1091
+ version: this.config.api.version || enriched.info.version
1092
+ };
1093
+ }
1094
+ res.json(enriched);
1095
+ } catch (error) {
1096
+ logError("[REST] openapi.json error:", error);
1097
+ sendError(res, error);
1098
+ }
1099
+ };
1100
+ this.routeManager.register({
1101
+ method: "GET",
1102
+ path: `${basePath}/openapi.json`,
1103
+ handler: openApiHandler,
1104
+ metadata: {
1105
+ summary: "OpenAPI 3.1 specification (machine-readable)",
1106
+ tags: ["openapi"]
1107
+ }
1108
+ });
1109
+ this.routeManager.register({
1110
+ method: "GET",
1111
+ path: `${basePath}/docs`,
1112
+ handler: async (req, res) => {
1113
+ const reqPath = req.path || req.url || `${basePath}/docs`;
1114
+ const apiBase = reqPath.replace(/\/docs\/?$/, "");
1115
+ const specUrl = `${apiBase}/openapi.json`;
1116
+ const html = `<!doctype html>
1117
+ <html>
1118
+ <head>
1119
+ <meta charset="utf-8" />
1120
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
1121
+ <title>ObjectStack API Docs</title>
1122
+ </head>
1123
+ <body>
1124
+ <script id="api-reference" data-url="${specUrl}"></script>
1125
+ <script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
1126
+ </body>
1127
+ </html>`;
1128
+ if (res.setHeader) res.setHeader("content-type", "text/html; charset=utf-8");
1129
+ if (res.send) res.send(html);
1130
+ else if (res.body) res.body = html;
1131
+ else res.json?.(html);
1132
+ },
1133
+ metadata: {
1134
+ summary: "Interactive API docs (Scalar viewer)",
1135
+ tags: ["openapi"]
1136
+ }
1137
+ });
1138
+ }
1139
+ async loadOpenApiSpec() {
1140
+ if (this._openApiSpecCache !== void 0) return this._openApiSpecCache;
1141
+ try {
1142
+ const mod = await import("module");
1143
+ const requireFn = mod.createRequire(import.meta.url);
1144
+ const pkgJsonPath = requireFn.resolve("@objectstack/spec/package.json");
1145
+ const pathMod = await import("path");
1146
+ const fsMod = await import("fs");
1147
+ const specPath = pathMod.join(pathMod.dirname(pkgJsonPath), "json-schema", "openapi.json");
1148
+ const raw = await fsMod.promises.readFile(specPath, "utf-8");
1149
+ this._openApiSpecCache = JSON.parse(raw);
1150
+ return this._openApiSpecCache;
1151
+ } catch (err) {
1152
+ logError("[REST] Failed to load OpenAPI spec:", err?.message ?? err);
1153
+ this._openApiSpecCache = null;
1154
+ return null;
1155
+ }
1156
+ }
990
1157
  /**
991
1158
  * Register metadata endpoints
992
1159
  */
993
1160
  registerMetadataEndpoints(basePath) {
994
1161
  const { metadata } = this.config;
995
1162
  const metaPath = `${basePath}${metadata.prefix}`;
996
- const isScoped = basePath.includes("/projects/:projectId");
1163
+ const isScoped = basePath.includes("/environments/:environmentId");
997
1164
  if (metadata.endpoints.types !== false) {
998
1165
  this.routeManager.register({
999
1166
  method: "GET",
1000
1167
  path: metaPath,
1001
1168
  handler: async (req, res) => {
1002
1169
  try {
1003
- const projectId = isScoped ? req.params?.projectId : void 0;
1004
- const p = await this.resolveProtocol(projectId, req);
1170
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
1171
+ const p = await this.resolveProtocol(environmentId, req);
1005
1172
  const types = await p.getMetaTypes();
1006
1173
  res.json(types);
1007
1174
  } catch (error) {
@@ -1022,14 +1189,14 @@ var RestServer = class {
1022
1189
  handler: async (req, res) => {
1023
1190
  try {
1024
1191
  const packageId = req.query?.package || void 0;
1025
- const projectId = isScoped ? req.params?.projectId : void 0;
1026
- const p = await this.resolveProtocol(projectId, req);
1192
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
1193
+ const p = await this.resolveProtocol(environmentId, req);
1027
1194
  const items = await p.getMetaItems({
1028
1195
  type: req.params.type,
1029
1196
  packageId,
1030
- ...projectId ? { projectId } : {}
1197
+ ...environmentId ? { environmentId } : {}
1031
1198
  });
1032
- const translated = await this.translateMetaItems(req, req.params.type, projectId, items);
1199
+ const translated = await this.translateMetaItems(req, req.params.type, environmentId, items);
1033
1200
  res.header("Vary", "Accept-Language");
1034
1201
  res.json(translated);
1035
1202
  } catch (error) {
@@ -1049,8 +1216,8 @@ var RestServer = class {
1049
1216
  path: `${metaPath}/:type/:name`,
1050
1217
  handler: async (req, res) => {
1051
1218
  try {
1052
- const projectId = isScoped ? req.params?.projectId : void 0;
1053
- const p = await this.resolveProtocol(projectId, req);
1219
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
1220
+ const p = await this.resolveProtocol(environmentId, req);
1054
1221
  if (metadata.enableCache && p.getMetaItemCached) {
1055
1222
  const cacheRequest = {
1056
1223
  ifNoneMatch: req.headers["if-none-match"],
@@ -1060,7 +1227,7 @@ var RestServer = class {
1060
1227
  type: req.params.type,
1061
1228
  name: req.params.name,
1062
1229
  cacheRequest,
1063
- ...projectId ? { projectId } : {}
1230
+ ...environmentId ? { environmentId } : {}
1064
1231
  });
1065
1232
  if (result.notModified) {
1066
1233
  res.status(304).send();
@@ -1079,7 +1246,7 @@ var RestServer = class {
1079
1246
  res.header("Cache-Control", directives + maxAge);
1080
1247
  }
1081
1248
  res.header("Vary", "Accept-Language");
1082
- res.json(await this.translateMetaItem(req, req.params.type, projectId, result.data));
1249
+ res.json(await this.translateMetaItem(req, req.params.type, environmentId, result.data));
1083
1250
  } else {
1084
1251
  const packageId = req.query?.package || void 0;
1085
1252
  const item = await p.getMetaItem({
@@ -1088,7 +1255,7 @@ var RestServer = class {
1088
1255
  packageId
1089
1256
  });
1090
1257
  res.header("Vary", "Accept-Language");
1091
- res.json(await this.translateMetaItem(req, req.params.type, projectId, item));
1258
+ res.json(await this.translateMetaItem(req, req.params.type, environmentId, item));
1092
1259
  }
1093
1260
  } catch (error) {
1094
1261
  logError("[REST] Unhandled error:", error);
@@ -1106,8 +1273,8 @@ var RestServer = class {
1106
1273
  path: `${metaPath}/:type/:name`,
1107
1274
  handler: async (req, res) => {
1108
1275
  try {
1109
- const projectId = isScoped ? req.params?.projectId : void 0;
1110
- const p = await this.resolveProtocol(projectId, req);
1276
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
1277
+ const p = await this.resolveProtocol(environmentId, req);
1111
1278
  if (!p.saveMetaItem) {
1112
1279
  res.status(501).json({ error: "Save operation not supported by protocol implementation" });
1113
1280
  return;
@@ -1122,7 +1289,7 @@ var RestServer = class {
1122
1289
  type: req.params.type,
1123
1290
  name: req.params.name,
1124
1291
  item,
1125
- ...projectId ? { projectId } : {},
1292
+ ...environmentId ? { environmentId } : {},
1126
1293
  ...parentVersion !== void 0 ? { parentVersion } : {},
1127
1294
  ...actor ? { actor } : {}
1128
1295
  });
@@ -1142,8 +1309,8 @@ var RestServer = class {
1142
1309
  path: `${metaPath}/:type/:name`,
1143
1310
  handler: async (req, res) => {
1144
1311
  try {
1145
- const projectId = isScoped ? req.params?.projectId : void 0;
1146
- const p = await this.resolveProtocol(projectId, req);
1312
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
1313
+ const p = await this.resolveProtocol(environmentId, req);
1147
1314
  if (!p.deleteMetaItem) {
1148
1315
  res.status(501).json({
1149
1316
  error: "Reset operation not supported by protocol implementation"
@@ -1157,7 +1324,7 @@ var RestServer = class {
1157
1324
  const result = await p.deleteMetaItem({
1158
1325
  type: req.params.type,
1159
1326
  name: req.params.name,
1160
- ...projectId ? { projectId } : {},
1327
+ ...environmentId ? { environmentId } : {},
1161
1328
  ...parentVersion !== void 0 ? { parentVersion } : {},
1162
1329
  ...actor ? { actor } : {}
1163
1330
  });
@@ -1177,8 +1344,8 @@ var RestServer = class {
1177
1344
  path: `${metaPath}/:type/:name/history`,
1178
1345
  handler: async (req, res) => {
1179
1346
  try {
1180
- const projectId = isScoped ? req.params?.projectId : void 0;
1181
- const p = await this.resolveProtocol(projectId, req);
1347
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
1348
+ const p = await this.resolveProtocol(environmentId, req);
1182
1349
  if (!p.historyMetaItem) {
1183
1350
  res.status(501).json({
1184
1351
  error: "History query not supported by protocol implementation"
@@ -1190,7 +1357,7 @@ var RestServer = class {
1190
1357
  const result = await p.historyMetaItem({
1191
1358
  type: req.params.type,
1192
1359
  name: req.params.name,
1193
- ...projectId ? { projectId } : {},
1360
+ ...environmentId ? { environmentId } : {},
1194
1361
  ...sinceSeq !== void 0 && Number.isFinite(sinceSeq) ? { sinceSeq } : {},
1195
1362
  ...limit !== void 0 && Number.isFinite(limit) ? { limit } : {}
1196
1363
  });
@@ -1211,8 +1378,8 @@ var RestServer = class {
1211
1378
  path: `${metaPath}/:type/:section/:name`,
1212
1379
  handler: async (req, res) => {
1213
1380
  try {
1214
- const projectId = isScoped ? req.params?.projectId : void 0;
1215
- const p = await this.resolveProtocol(projectId, req);
1381
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
1382
+ const p = await this.resolveProtocol(environmentId, req);
1216
1383
  const compoundName = `${req.params.section}/${req.params.name}`;
1217
1384
  const packageId = req.query?.package || void 0;
1218
1385
  const item = await p.getMetaItem({
@@ -1221,7 +1388,7 @@ var RestServer = class {
1221
1388
  packageId
1222
1389
  });
1223
1390
  res.header("Vary", "Accept-Language");
1224
- res.json(await this.translateMetaItem(req, req.params.type, projectId, item));
1391
+ res.json(await this.translateMetaItem(req, req.params.type, environmentId, item));
1225
1392
  } catch (error) {
1226
1393
  logError("[REST] Unhandled error:", error);
1227
1394
  sendError(res, error);
@@ -1238,8 +1405,8 @@ var RestServer = class {
1238
1405
  path: `${metaPath}/:type/:section/:name`,
1239
1406
  handler: async (req, res) => {
1240
1407
  try {
1241
- const projectId = isScoped ? req.params?.projectId : void 0;
1242
- const p = await this.resolveProtocol(projectId, req);
1408
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
1409
+ const p = await this.resolveProtocol(environmentId, req);
1243
1410
  if (!p.saveMetaItem) {
1244
1411
  res.status(501).json({ error: "Save operation not supported by protocol implementation" });
1245
1412
  return;
@@ -1253,7 +1420,7 @@ var RestServer = class {
1253
1420
  type: req.params.type,
1254
1421
  name: compoundName,
1255
1422
  item: req.body,
1256
- ...projectId ? { projectId } : {},
1423
+ ...environmentId ? { environmentId } : {},
1257
1424
  ...parentVersion !== void 0 ? { parentVersion } : {},
1258
1425
  ...actor ? { actor } : {}
1259
1426
  });
@@ -1274,19 +1441,19 @@ var RestServer = class {
1274
1441
  */
1275
1442
  registerUiEndpoints(basePath) {
1276
1443
  const uiPath = `${basePath}/ui`;
1277
- const isScoped = basePath.includes("/projects/:projectId");
1444
+ const isScoped = basePath.includes("/environments/:environmentId");
1278
1445
  this.routeManager.register({
1279
1446
  method: "GET",
1280
1447
  path: `${uiPath}/view/:object/:type`,
1281
1448
  handler: async (req, res) => {
1282
1449
  try {
1283
- const projectId = isScoped ? req.params?.projectId : void 0;
1284
- const p = await this.resolveProtocol(projectId, req);
1450
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
1451
+ const p = await this.resolveProtocol(environmentId, req);
1285
1452
  if (p.getUiView) {
1286
1453
  const view = await p.getUiView({
1287
1454
  object: req.params.object,
1288
1455
  type: req.params.type,
1289
- ...projectId ? { projectId } : {}
1456
+ ...environmentId ? { environmentId } : {}
1290
1457
  });
1291
1458
  res.json(view);
1292
1459
  } else {
@@ -1309,7 +1476,7 @@ var RestServer = class {
1309
1476
  registerCrudEndpoints(basePath) {
1310
1477
  const { crud } = this.config;
1311
1478
  const dataPath = `${basePath}${crud.dataPrefix}`;
1312
- const isScoped = basePath.includes("/projects/:projectId");
1479
+ const isScoped = basePath.includes("/environments/:environmentId");
1313
1480
  const operations = crud.operations;
1314
1481
  if (operations.list) {
1315
1482
  this.routeManager.register({
@@ -1317,14 +1484,14 @@ var RestServer = class {
1317
1484
  path: `${dataPath}/:object`,
1318
1485
  handler: async (req, res) => {
1319
1486
  try {
1320
- const projectId = isScoped ? req.params?.projectId : void 0;
1321
- const p = await this.resolveProtocol(projectId, req);
1322
- const context = await this.resolveExecCtx(projectId, req);
1487
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
1488
+ const p = await this.resolveProtocol(environmentId, req);
1489
+ const context = await this.resolveExecCtx(environmentId, req);
1323
1490
  if (this.enforceAuth(req, res, context)) return;
1324
1491
  const result = await p.findData({
1325
1492
  object: req.params.object,
1326
1493
  query: req.query,
1327
- ...projectId ? { projectId } : {},
1494
+ ...environmentId ? { environmentId } : {},
1328
1495
  ...context ? { context } : {}
1329
1496
  });
1330
1497
  res.json(result);
@@ -1350,17 +1517,17 @@ var RestServer = class {
1350
1517
  path: `${dataPath}/:object/:id`,
1351
1518
  handler: async (req, res) => {
1352
1519
  try {
1353
- const projectId = isScoped ? req.params?.projectId : void 0;
1354
- const p = await this.resolveProtocol(projectId, req);
1520
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
1521
+ const p = await this.resolveProtocol(environmentId, req);
1355
1522
  const { select, expand } = req.query || {};
1356
- const context = await this.resolveExecCtx(projectId, req);
1523
+ const context = await this.resolveExecCtx(environmentId, req);
1357
1524
  if (this.enforceAuth(req, res, context)) return;
1358
1525
  const result = await p.getData({
1359
1526
  object: req.params.object,
1360
1527
  id: req.params.id,
1361
1528
  ...select != null ? { select } : {},
1362
1529
  ...expand != null ? { expand } : {},
1363
- ...projectId ? { projectId } : {},
1530
+ ...environmentId ? { environmentId } : {},
1364
1531
  ...context ? { context } : {}
1365
1532
  });
1366
1533
  res.json(result);
@@ -1382,14 +1549,14 @@ var RestServer = class {
1382
1549
  path: `${dataPath}/:object`,
1383
1550
  handler: async (req, res) => {
1384
1551
  try {
1385
- const projectId = isScoped ? req.params?.projectId : void 0;
1386
- const p = await this.resolveProtocol(projectId, req);
1387
- const context = await this.resolveExecCtx(projectId, req);
1552
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
1553
+ const p = await this.resolveProtocol(environmentId, req);
1554
+ const context = await this.resolveExecCtx(environmentId, req);
1388
1555
  if (this.enforceAuth(req, res, context)) return;
1389
1556
  const result = await p.createData({
1390
1557
  object: req.params.object,
1391
1558
  data: req.body,
1392
- ...projectId ? { projectId } : {},
1559
+ ...environmentId ? { environmentId } : {},
1393
1560
  ...context ? { context } : {}
1394
1561
  });
1395
1562
  res.status(201).json(result);
@@ -1411,14 +1578,14 @@ var RestServer = class {
1411
1578
  path: `${dataPath}/:object/query`,
1412
1579
  handler: async (req, res) => {
1413
1580
  try {
1414
- const projectId = isScoped ? req.params?.projectId : void 0;
1415
- const p = await this.resolveProtocol(projectId, req);
1416
- const context = await this.resolveExecCtx(projectId, req);
1581
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
1582
+ const p = await this.resolveProtocol(environmentId, req);
1583
+ const context = await this.resolveExecCtx(environmentId, req);
1417
1584
  if (this.enforceAuth(req, res, context)) return;
1418
1585
  const result = await p.findData({
1419
1586
  object: req.params.object,
1420
1587
  query: req.body || {},
1421
- ...projectId ? { projectId } : {},
1588
+ ...environmentId ? { environmentId } : {},
1422
1589
  ...context ? { context } : {}
1423
1590
  });
1424
1591
  res.json(result);
@@ -1440,9 +1607,9 @@ var RestServer = class {
1440
1607
  path: `${dataPath}/:object/:id`,
1441
1608
  handler: async (req, res) => {
1442
1609
  try {
1443
- const projectId = isScoped ? req.params?.projectId : void 0;
1444
- const p = await this.resolveProtocol(projectId, req);
1445
- const context = await this.resolveExecCtx(projectId, req);
1610
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
1611
+ const p = await this.resolveProtocol(environmentId, req);
1612
+ const context = await this.resolveExecCtx(environmentId, req);
1446
1613
  if (this.enforceAuth(req, res, context)) return;
1447
1614
  const ifMatchHeader = req.headers?.["if-match"] ?? req.headers?.["If-Match"];
1448
1615
  const bodyVersion = req.body && typeof req.body === "object" ? req.body.expectedVersion : void 0;
@@ -1457,7 +1624,7 @@ var RestServer = class {
1457
1624
  id: req.params.id,
1458
1625
  data,
1459
1626
  ...expectedVersion ? { expectedVersion: String(expectedVersion) } : {},
1460
- ...projectId ? { projectId } : {},
1627
+ ...environmentId ? { environmentId } : {},
1461
1628
  ...context ? { context } : {}
1462
1629
  });
1463
1630
  res.json(result);
@@ -1479,9 +1646,9 @@ var RestServer = class {
1479
1646
  path: `${dataPath}/:object/:id`,
1480
1647
  handler: async (req, res) => {
1481
1648
  try {
1482
- const projectId = isScoped ? req.params?.projectId : void 0;
1483
- const p = await this.resolveProtocol(projectId, req);
1484
- const context = await this.resolveExecCtx(projectId, req);
1649
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
1650
+ const p = await this.resolveProtocol(environmentId, req);
1651
+ const context = await this.resolveExecCtx(environmentId, req);
1485
1652
  if (this.enforceAuth(req, res, context)) return;
1486
1653
  const ifMatchHeader = req.headers?.["if-match"] ?? req.headers?.["If-Match"];
1487
1654
  const queryVersion = req.query && typeof req.query === "object" ? req.query.expectedVersion : void 0;
@@ -1490,7 +1657,7 @@ var RestServer = class {
1490
1657
  object: req.params.object,
1491
1658
  id: req.params.id,
1492
1659
  ...expectedVersion ? { expectedVersion: String(expectedVersion) } : {},
1493
- ...projectId ? { projectId } : {},
1660
+ ...environmentId ? { environmentId } : {},
1494
1661
  ...context ? { context } : {}
1495
1662
  });
1496
1663
  res.json(result);
@@ -1516,7 +1683,7 @@ var RestServer = class {
1516
1683
  * POST {basePath}/data/lead/:id/convert — M10.6 lead conversion.
1517
1684
  */
1518
1685
  registerDataActionEndpoints(basePath) {
1519
- const isScoped = basePath.includes("/projects/:projectId");
1686
+ const isScoped = basePath.includes("/environments/:environmentId");
1520
1687
  const { crud } = this.config;
1521
1688
  const dataPath = `${basePath}${crud.dataPrefix}`;
1522
1689
  this.routeManager.register({
@@ -1524,9 +1691,9 @@ var RestServer = class {
1524
1691
  path: `${dataPath}/lead/:id/convert`,
1525
1692
  handler: async (req, res) => {
1526
1693
  try {
1527
- const projectId = isScoped ? req.params?.projectId : void 0;
1528
- const p = await this.resolveProtocol(projectId, req);
1529
- const context = await this.resolveExecCtx(projectId, req);
1694
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
1695
+ const p = await this.resolveProtocol(environmentId, req);
1696
+ const context = await this.resolveExecCtx(environmentId, req);
1530
1697
  if (this.enforceAuth(req, res, context)) return;
1531
1698
  const convertLead = p.convertLead;
1532
1699
  if (typeof convertLead !== "function") {
@@ -1559,9 +1726,9 @@ var RestServer = class {
1559
1726
  path: `${dataPath}/:object/import`,
1560
1727
  handler: async (req, res) => {
1561
1728
  try {
1562
- const projectId = isScoped ? req.params?.projectId : void 0;
1563
- const p = await this.resolveProtocol(projectId, req);
1564
- const context = await this.resolveExecCtx(projectId, req);
1729
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
1730
+ const p = await this.resolveProtocol(environmentId, req);
1731
+ const context = await this.resolveExecCtx(environmentId, req);
1565
1732
  if (this.enforceAuth(req, res, context)) return;
1566
1733
  const objectName = String(req.params.object || "");
1567
1734
  if (!objectName) {
@@ -1642,9 +1809,9 @@ var RestServer = class {
1642
1809
  path: `${dataPath}/:object/export`,
1643
1810
  handler: async (req, res) => {
1644
1811
  try {
1645
- const projectId = isScoped ? req.params?.projectId : void 0;
1646
- const p = await this.resolveProtocol(projectId, req);
1647
- const context = await this.resolveExecCtx(projectId, req);
1812
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
1813
+ const p = await this.resolveProtocol(environmentId, req);
1814
+ const context = await this.resolveExecCtx(environmentId, req);
1648
1815
  if (this.enforceAuth(req, res, context)) return;
1649
1816
  const objectName = String(req.params.object || "");
1650
1817
  if (!objectName) {
@@ -1693,7 +1860,7 @@ var RestServer = class {
1693
1860
  }
1694
1861
  if (!fields || fields.length === 0) {
1695
1862
  try {
1696
- const schema = await p.getObjectSchema?.(objectName, projectId);
1863
+ const schema = await p.getObjectSchema?.(objectName, environmentId);
1697
1864
  const schemaFields = schema?.fields;
1698
1865
  if (Array.isArray(schemaFields)) {
1699
1866
  fields = schemaFields.map((f) => f.name).filter((n) => typeof n === "string");
@@ -1727,7 +1894,7 @@ var RestServer = class {
1727
1894
  $top: take,
1728
1895
  $skip: skip
1729
1896
  },
1730
- ...projectId ? { projectId } : {},
1897
+ ...environmentId ? { environmentId } : {},
1731
1898
  ...context ? { context } : {}
1732
1899
  };
1733
1900
  const result = await p.findData(findArgs);
@@ -1775,15 +1942,15 @@ var RestServer = class {
1775
1942
  * GET {basePath}/search?q=acme&objects=lead,account&limit=20&perObject=5
1776
1943
  */
1777
1944
  registerSearchEndpoints(basePath) {
1778
- const isScoped = basePath.includes("/projects/:projectId");
1945
+ const isScoped = basePath.includes("/environments/:environmentId");
1779
1946
  this.routeManager.register({
1780
1947
  method: "GET",
1781
1948
  path: `${basePath}/search`,
1782
1949
  handler: async (req, res) => {
1783
1950
  try {
1784
- const projectId = isScoped ? req.params?.projectId : void 0;
1785
- const p = await this.resolveProtocol(projectId, req);
1786
- const context = await this.resolveExecCtx(projectId, req);
1951
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
1952
+ const p = await this.resolveProtocol(environmentId, req);
1953
+ const context = await this.resolveExecCtx(environmentId, req);
1787
1954
  if (this.enforceAuth(req, res, context)) return;
1788
1955
  const searchAll = p.searchAll;
1789
1956
  if (typeof searchAll !== "function") {
@@ -1835,14 +2002,14 @@ var RestServer = class {
1835
2002
  * }
1836
2003
  */
1837
2004
  registerEmailEndpoints(basePath) {
1838
- const isScoped = basePath.includes("/projects/:projectId");
2005
+ const isScoped = basePath.includes("/environments/:environmentId");
1839
2006
  this.routeManager.register({
1840
2007
  method: "POST",
1841
2008
  path: `${basePath}/email/send`,
1842
2009
  handler: async (req, res) => {
1843
2010
  try {
1844
- const projectId = isScoped ? req.params?.projectId : void 0;
1845
- const context = await this.resolveExecCtx(projectId, req);
2011
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
2012
+ const context = await this.resolveExecCtx(environmentId, req);
1846
2013
  if (this.enforceAuth(req, res, context)) return;
1847
2014
  if (!this.emailServiceProvider) {
1848
2015
  res.status(501).json({
@@ -1851,7 +2018,7 @@ var RestServer = class {
1851
2018
  });
1852
2019
  return;
1853
2020
  }
1854
- const emailService = await this.emailServiceProvider(projectId).catch(() => void 0);
2021
+ const emailService = await this.emailServiceProvider(environmentId).catch(() => void 0);
1855
2022
  if (!emailService || typeof emailService.send !== "function") {
1856
2023
  res.status(501).json({
1857
2024
  code: "NOT_IMPLEMENTED",
@@ -1930,7 +2097,7 @@ var RestServer = class {
1930
2097
  * `mapViewSpecToEmbeddableConfig` expects.
1931
2098
  */
1932
2099
  registerFormEndpoints(basePath) {
1933
- const isScoped = basePath.includes("/projects/:projectId");
2100
+ const isScoped = basePath.includes("/environments/:environmentId");
1934
2101
  const slugMatchesPublicLink = (publicLink, slug) => {
1935
2102
  if (!publicLink || typeof publicLink !== "string") return false;
1936
2103
  const normalized = publicLink.replace(/^\/+/, "").replace(/^forms\//, "");
@@ -1960,12 +2127,12 @@ var RestServer = class {
1960
2127
  }
1961
2128
  return null;
1962
2129
  };
1963
- const resolveFormBySlug = async (projectId, req, slug) => {
1964
- const p = await this.resolveProtocol(projectId, req);
2130
+ const resolveFormBySlug = async (environmentId, req, slug) => {
2131
+ const p = await this.resolveProtocol(environmentId, req);
1965
2132
  if (typeof p.getMetaItems !== "function") return null;
1966
2133
  const result = await p.getMetaItems({
1967
2134
  type: "view",
1968
- ...projectId ? { projectId } : {}
2135
+ ...environmentId ? { environmentId } : {}
1969
2136
  });
1970
2137
  const items = Array.isArray(result?.items) ? result.items : Array.isArray(result) ? result : [];
1971
2138
  return findPublicFormView(items, slug);
@@ -1975,13 +2142,13 @@ var RestServer = class {
1975
2142
  path: `${basePath}/forms/:slug`,
1976
2143
  handler: async (req, res) => {
1977
2144
  try {
1978
- const projectId = isScoped ? req.params?.projectId : void 0;
2145
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
1979
2146
  const slug = String(req.params?.slug ?? "").trim();
1980
2147
  if (!slug) {
1981
2148
  res.status(400).json({ code: "INVALID_REQUEST", error: "slug is required" });
1982
2149
  return;
1983
2150
  }
1984
- const match = await resolveFormBySlug(projectId, req, slug);
2151
+ const match = await resolveFormBySlug(environmentId, req, slug);
1985
2152
  if (!match) {
1986
2153
  res.status(404).json({
1987
2154
  code: "FORM_NOT_FOUND",
@@ -1991,11 +2158,11 @@ var RestServer = class {
1991
2158
  }
1992
2159
  let objectSchema = null;
1993
2160
  try {
1994
- const p = await this.resolveProtocol(projectId, req);
2161
+ const p = await this.resolveProtocol(environmentId, req);
1995
2162
  if (typeof p.getMetaItems === "function") {
1996
2163
  const r = await p.getMetaItems({
1997
2164
  type: "object",
1998
- ...projectId ? { projectId } : {}
2165
+ ...environmentId ? { environmentId } : {}
1999
2166
  });
2000
2167
  const items = Array.isArray(r?.items) ? r.items : Array.isArray(r) ? r : [];
2001
2168
  const obj = items.find((o) => o?.name === match.object);
@@ -2015,7 +2182,7 @@ var RestServer = class {
2015
2182
  }
2016
2183
  objectSchema = { name: obj.name, label: obj.label, fields };
2017
2184
  try {
2018
- const i18n = await this.resolveI18nService(projectId, req);
2185
+ const i18n = await this.resolveI18nService(environmentId, req);
2019
2186
  const bundle = this.buildTranslationBundle(i18n);
2020
2187
  const locale = this.extractLocale(req, i18n);
2021
2188
  if (bundle && locale) {
@@ -2075,13 +2242,13 @@ var RestServer = class {
2075
2242
  path: `${basePath}/forms/:slug/submit`,
2076
2243
  handler: async (req, res) => {
2077
2244
  try {
2078
- const projectId = isScoped ? req.params?.projectId : void 0;
2245
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
2079
2246
  const slug = String(req.params?.slug ?? "").trim();
2080
2247
  if (!slug) {
2081
2248
  res.status(400).json({ code: "INVALID_REQUEST", error: "slug is required" });
2082
2249
  return;
2083
2250
  }
2084
- const match = await resolveFormBySlug(projectId, req, slug);
2251
+ const match = await resolveFormBySlug(environmentId, req, slug);
2085
2252
  if (!match) {
2086
2253
  res.status(404).json({
2087
2254
  code: "FORM_NOT_FOUND",
@@ -2109,11 +2276,11 @@ var RestServer = class {
2109
2276
  permissions: ["guest_portal"],
2110
2277
  anonymous: true
2111
2278
  };
2112
- const p = await this.resolveProtocol(projectId, req);
2279
+ const p = await this.resolveProtocol(environmentId, req);
2113
2280
  const result = await p.createData({
2114
2281
  object: match.object,
2115
2282
  data: filteredData,
2116
- ...projectId ? { projectId } : {},
2283
+ ...environmentId ? { environmentId } : {},
2117
2284
  context
2118
2285
  });
2119
2286
  res.status(201).json(result);
@@ -2135,14 +2302,14 @@ var RestServer = class {
2135
2302
  path: `${basePath}/forms/:slug/lookup/:field`,
2136
2303
  handler: async (req, res) => {
2137
2304
  try {
2138
- const projectId = isScoped ? req.params?.projectId : void 0;
2305
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
2139
2306
  const slug = String(req.params?.slug ?? "").trim();
2140
2307
  const fieldName = String(req.params?.field ?? "").trim();
2141
2308
  if (!slug || !fieldName) {
2142
2309
  res.status(400).json({ code: "INVALID_REQUEST", error: "slug and field are required" });
2143
2310
  return;
2144
2311
  }
2145
- const match = await resolveFormBySlug(projectId, req, slug);
2312
+ const match = await resolveFormBySlug(environmentId, req, slug);
2146
2313
  if (!match) {
2147
2314
  res.status(404).json({
2148
2315
  code: "FORM_NOT_FOUND",
@@ -2169,13 +2336,13 @@ var RestServer = class {
2169
2336
  });
2170
2337
  return;
2171
2338
  }
2172
- const p = await this.resolveProtocol(projectId, req);
2339
+ const p = await this.resolveProtocol(environmentId, req);
2173
2340
  let referenceTo = picker.object;
2174
2341
  if (!referenceTo && typeof p.getMetaItems === "function") {
2175
2342
  try {
2176
2343
  const r = await p.getMetaItems({
2177
2344
  type: "object",
2178
- ...projectId ? { projectId } : {}
2345
+ ...environmentId ? { environmentId } : {}
2179
2346
  });
2180
2347
  const items = Array.isArray(r?.items) ? r.items : Array.isArray(r) ? r : [];
2181
2348
  const obj = items.find((o) => o?.name === match.object);
@@ -2211,7 +2378,7 @@ var RestServer = class {
2211
2378
  select: ["id", ...displayFields],
2212
2379
  sort: picker.sort ?? [{ field: displayFields[0], order: "asc" }]
2213
2380
  },
2214
- ...projectId ? { projectId } : {},
2381
+ ...environmentId ? { environmentId } : {},
2215
2382
  context
2216
2383
  });
2217
2384
  const rows = Array.isArray(result?.data) ? result.data : Array.isArray(result?.items) ? result.items : [];
@@ -2261,11 +2428,11 @@ var RestServer = class {
2261
2428
  registerSharingEndpoints(basePath) {
2262
2429
  const { crud } = this.config;
2263
2430
  const dataPath = `${basePath}${crud.dataPrefix}`;
2264
- const isScoped = basePath.includes("/projects/:projectId");
2265
- const resolveService = async (projectId) => {
2431
+ const isScoped = basePath.includes("/environments/:environmentId");
2432
+ const resolveService = async (environmentId) => {
2266
2433
  if (!this.sharingServiceProvider) return void 0;
2267
2434
  try {
2268
- return await this.sharingServiceProvider(projectId);
2435
+ return await this.sharingServiceProvider(environmentId);
2269
2436
  } catch {
2270
2437
  return void 0;
2271
2438
  }
@@ -2279,10 +2446,10 @@ var RestServer = class {
2279
2446
  path: `${dataPath}/:object/:id/shares`,
2280
2447
  handler: async (req, res) => {
2281
2448
  try {
2282
- const projectId = isScoped ? req.params?.projectId : void 0;
2283
- const context = await this.resolveExecCtx(projectId, req);
2449
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
2450
+ const context = await this.resolveExecCtx(environmentId, req);
2284
2451
  if (this.enforceAuth(req, res, context)) return;
2285
- const svc = await resolveService(projectId);
2452
+ const svc = await resolveService(environmentId);
2286
2453
  if (!svc) return respond501(res);
2287
2454
  const rows = await svc.listShares(req.params.object, req.params.id, context ?? {});
2288
2455
  res.json({ data: rows });
@@ -2298,10 +2465,10 @@ var RestServer = class {
2298
2465
  path: `${dataPath}/:object/:id/shares`,
2299
2466
  handler: async (req, res) => {
2300
2467
  try {
2301
- const projectId = isScoped ? req.params?.projectId : void 0;
2302
- const context = await this.resolveExecCtx(projectId, req);
2468
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
2469
+ const context = await this.resolveExecCtx(environmentId, req);
2303
2470
  if (this.enforceAuth(req, res, context)) return;
2304
- const svc = await resolveService(projectId);
2471
+ const svc = await resolveService(environmentId);
2305
2472
  if (!svc) return respond501(res);
2306
2473
  const body = req.body ?? {};
2307
2474
  const input = {
@@ -2340,10 +2507,10 @@ var RestServer = class {
2340
2507
  path: `${dataPath}/:object/:id/shares/:shareId`,
2341
2508
  handler: async (req, res) => {
2342
2509
  try {
2343
- const projectId = isScoped ? req.params?.projectId : void 0;
2344
- const context = await this.resolveExecCtx(projectId, req);
2510
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
2511
+ const context = await this.resolveExecCtx(environmentId, req);
2345
2512
  if (this.enforceAuth(req, res, context)) return;
2346
- const svc = await resolveService(projectId);
2513
+ const svc = await resolveService(environmentId);
2347
2514
  if (!svc) return respond501(res);
2348
2515
  await svc.revoke(req.params.shareId, context ?? {});
2349
2516
  res.status(204).end();
@@ -2369,11 +2536,11 @@ var RestServer = class {
2369
2536
  */
2370
2537
  registerSharingRuleEndpoints(basePath) {
2371
2538
  const dataPath = basePath;
2372
- const isScoped = basePath.includes("/projects/:projectId");
2373
- const resolveService = async (projectId) => {
2539
+ const isScoped = basePath.includes("/environments/:environmentId");
2540
+ const resolveService = async (environmentId) => {
2374
2541
  if (!this.sharingRulesServiceProvider) return void 0;
2375
2542
  try {
2376
- return await this.sharingRulesServiceProvider(projectId);
2543
+ return await this.sharingRulesServiceProvider(environmentId);
2377
2544
  } catch {
2378
2545
  return void 0;
2379
2546
  }
@@ -2398,10 +2565,10 @@ var RestServer = class {
2398
2565
  path: `${dataPath}/sharing/rules`,
2399
2566
  handler: async (req, res) => {
2400
2567
  try {
2401
- const projectId = isScoped ? req.params?.projectId : void 0;
2402
- const context = await this.resolveExecCtx(projectId, req);
2568
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
2569
+ const context = await this.resolveExecCtx(environmentId, req);
2403
2570
  if (this.enforceAuth(req, res, context)) return;
2404
- const svc = await resolveService(projectId);
2571
+ const svc = await resolveService(environmentId);
2405
2572
  if (!svc) return respond501(res);
2406
2573
  const rows = await svc.listRules({
2407
2574
  object: req.query?.object,
@@ -2419,10 +2586,10 @@ var RestServer = class {
2419
2586
  path: `${dataPath}/sharing/rules`,
2420
2587
  handler: async (req, res) => {
2421
2588
  try {
2422
- const projectId = isScoped ? req.params?.projectId : void 0;
2423
- const context = await this.resolveExecCtx(projectId, req);
2589
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
2590
+ const context = await this.resolveExecCtx(environmentId, req);
2424
2591
  if (this.enforceAuth(req, res, context)) return;
2425
- const svc = await resolveService(projectId);
2592
+ const svc = await resolveService(environmentId);
2426
2593
  if (!svc) return respond501(res);
2427
2594
  const body = req.body ?? {};
2428
2595
  const input = {
@@ -2449,10 +2616,10 @@ var RestServer = class {
2449
2616
  path: `${dataPath}/sharing/rules/:idOrName`,
2450
2617
  handler: async (req, res) => {
2451
2618
  try {
2452
- const projectId = isScoped ? req.params?.projectId : void 0;
2453
- const context = await this.resolveExecCtx(projectId, req);
2619
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
2620
+ const context = await this.resolveExecCtx(environmentId, req);
2454
2621
  if (this.enforceAuth(req, res, context)) return;
2455
- const svc = await resolveService(projectId);
2622
+ const svc = await resolveService(environmentId);
2456
2623
  if (!svc) return respond501(res);
2457
2624
  const row = await svc.getRule(req.params.idOrName, context ?? {});
2458
2625
  if (!row) return res.status(404).json({ code: "RULE_NOT_FOUND" });
@@ -2468,10 +2635,10 @@ var RestServer = class {
2468
2635
  path: `${dataPath}/sharing/rules/:idOrName`,
2469
2636
  handler: async (req, res) => {
2470
2637
  try {
2471
- const projectId = isScoped ? req.params?.projectId : void 0;
2472
- const context = await this.resolveExecCtx(projectId, req);
2638
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
2639
+ const context = await this.resolveExecCtx(environmentId, req);
2473
2640
  if (this.enforceAuth(req, res, context)) return;
2474
- const svc = await resolveService(projectId);
2641
+ const svc = await resolveService(environmentId);
2475
2642
  if (!svc) return respond501(res);
2476
2643
  await svc.deleteRule(req.params.idOrName, context ?? {});
2477
2644
  res.status(204).end();
@@ -2486,10 +2653,10 @@ var RestServer = class {
2486
2653
  path: `${dataPath}/sharing/rules/:idOrName/evaluate`,
2487
2654
  handler: async (req, res) => {
2488
2655
  try {
2489
- const projectId = isScoped ? req.params?.projectId : void 0;
2490
- const context = await this.resolveExecCtx(projectId, req);
2656
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
2657
+ const context = await this.resolveExecCtx(environmentId, req);
2491
2658
  if (this.enforceAuth(req, res, context)) return;
2492
- const svc = await resolveService(projectId);
2659
+ const svc = await resolveService(environmentId);
2493
2660
  if (!svc) return respond501(res);
2494
2661
  const result = await svc.evaluateRule(req.params.idOrName, context ?? {});
2495
2662
  res.json(result);
@@ -2523,11 +2690,11 @@ var RestServer = class {
2523
2690
  */
2524
2691
  registerReportsEndpoints(basePath) {
2525
2692
  const dataPath = basePath;
2526
- const isScoped = basePath.includes("/projects/:projectId");
2527
- const resolveService = async (projectId) => {
2693
+ const isScoped = basePath.includes("/environments/:environmentId");
2694
+ const resolveService = async (environmentId) => {
2528
2695
  if (!this.reportsServiceProvider) return void 0;
2529
2696
  try {
2530
- return await this.reportsServiceProvider(projectId);
2697
+ return await this.reportsServiceProvider(environmentId);
2531
2698
  } catch {
2532
2699
  return void 0;
2533
2700
  }
@@ -2556,10 +2723,10 @@ var RestServer = class {
2556
2723
  path: `${dataPath}/reports`,
2557
2724
  handler: async (req, res) => {
2558
2725
  try {
2559
- const projectId = isScoped ? req.params?.projectId : void 0;
2560
- const context = await this.resolveExecCtx(projectId, req);
2726
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
2727
+ const context = await this.resolveExecCtx(environmentId, req);
2561
2728
  if (this.enforceAuth(req, res, context)) return;
2562
- const svc = await resolveService(projectId);
2729
+ const svc = await resolveService(environmentId);
2563
2730
  if (!svc) return respond501(res);
2564
2731
  const q = req.query ?? {};
2565
2732
  const rows = await svc.listReports({ object: q.object, ownerId: q.ownerId }, context ?? {});
@@ -2576,10 +2743,10 @@ var RestServer = class {
2576
2743
  path: `${dataPath}/reports`,
2577
2744
  handler: async (req, res) => {
2578
2745
  try {
2579
- const projectId = isScoped ? req.params?.projectId : void 0;
2580
- const context = await this.resolveExecCtx(projectId, req);
2746
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
2747
+ const context = await this.resolveExecCtx(environmentId, req);
2581
2748
  if (this.enforceAuth(req, res, context)) return;
2582
- const svc = await resolveService(projectId);
2749
+ const svc = await resolveService(environmentId);
2583
2750
  if (!svc) return respond501(res);
2584
2751
  try {
2585
2752
  const row = await svc.saveReport(req.body ?? {}, context ?? {});
@@ -2600,10 +2767,10 @@ var RestServer = class {
2600
2767
  path: `${dataPath}/reports/:id`,
2601
2768
  handler: async (req, res) => {
2602
2769
  try {
2603
- const projectId = isScoped ? req.params?.projectId : void 0;
2604
- const context = await this.resolveExecCtx(projectId, req);
2770
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
2771
+ const context = await this.resolveExecCtx(environmentId, req);
2605
2772
  if (this.enforceAuth(req, res, context)) return;
2606
- const svc = await resolveService(projectId);
2773
+ const svc = await resolveService(environmentId);
2607
2774
  if (!svc) return respond501(res);
2608
2775
  const row = await svc.getReport(req.params.id, context ?? {});
2609
2776
  if (!row) {
@@ -2623,10 +2790,10 @@ var RestServer = class {
2623
2790
  path: `${dataPath}/reports/:id`,
2624
2791
  handler: async (req, res) => {
2625
2792
  try {
2626
- const projectId = isScoped ? req.params?.projectId : void 0;
2627
- const context = await this.resolveExecCtx(projectId, req);
2793
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
2794
+ const context = await this.resolveExecCtx(environmentId, req);
2628
2795
  if (this.enforceAuth(req, res, context)) return;
2629
- const svc = await resolveService(projectId);
2796
+ const svc = await resolveService(environmentId);
2630
2797
  if (!svc) return respond501(res);
2631
2798
  await svc.deleteReport(req.params.id, context ?? {});
2632
2799
  res.status(204).end();
@@ -2642,10 +2809,10 @@ var RestServer = class {
2642
2809
  path: `${dataPath}/reports/:id/run`,
2643
2810
  handler: async (req, res) => {
2644
2811
  try {
2645
- const projectId = isScoped ? req.params?.projectId : void 0;
2646
- const context = await this.resolveExecCtx(projectId, req);
2812
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
2813
+ const context = await this.resolveExecCtx(environmentId, req);
2647
2814
  if (this.enforceAuth(req, res, context)) return;
2648
- const svc = await resolveService(projectId);
2815
+ const svc = await resolveService(environmentId);
2649
2816
  if (!svc) return respond501(res);
2650
2817
  try {
2651
2818
  const result = await svc.run(req.params.id, context ?? {});
@@ -2666,10 +2833,10 @@ var RestServer = class {
2666
2833
  path: `${dataPath}/reports/:id/schedule`,
2667
2834
  handler: async (req, res) => {
2668
2835
  try {
2669
- const projectId = isScoped ? req.params?.projectId : void 0;
2670
- const context = await this.resolveExecCtx(projectId, req);
2836
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
2837
+ const context = await this.resolveExecCtx(environmentId, req);
2671
2838
  if (this.enforceAuth(req, res, context)) return;
2672
- const svc = await resolveService(projectId);
2839
+ const svc = await resolveService(environmentId);
2673
2840
  if (!svc) return respond501(res);
2674
2841
  const body = req.body ?? {};
2675
2842
  try {
@@ -2702,10 +2869,10 @@ var RestServer = class {
2702
2869
  path: `${dataPath}/reports/:id/schedules`,
2703
2870
  handler: async (req, res) => {
2704
2871
  try {
2705
- const projectId = isScoped ? req.params?.projectId : void 0;
2706
- const context = await this.resolveExecCtx(projectId, req);
2872
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
2873
+ const context = await this.resolveExecCtx(environmentId, req);
2707
2874
  if (this.enforceAuth(req, res, context)) return;
2708
- const svc = await resolveService(projectId);
2875
+ const svc = await resolveService(environmentId);
2709
2876
  if (!svc) return respond501(res);
2710
2877
  const rows = await svc.listSchedules({ reportId: req.params.id }, context ?? {});
2711
2878
  res.json({ data: rows });
@@ -2721,10 +2888,10 @@ var RestServer = class {
2721
2888
  path: `${dataPath}/reports/schedules/:scheduleId`,
2722
2889
  handler: async (req, res) => {
2723
2890
  try {
2724
- const projectId = isScoped ? req.params?.projectId : void 0;
2725
- const context = await this.resolveExecCtx(projectId, req);
2891
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
2892
+ const context = await this.resolveExecCtx(environmentId, req);
2726
2893
  if (this.enforceAuth(req, res, context)) return;
2727
- const svc = await resolveService(projectId);
2894
+ const svc = await resolveService(environmentId);
2728
2895
  if (!svc) return respond501(res);
2729
2896
  await svc.unscheduleReport(req.params.scheduleId, context ?? {});
2730
2897
  res.status(204).end();
@@ -2757,11 +2924,11 @@ var RestServer = class {
2757
2924
  */
2758
2925
  registerApprovalsEndpoints(basePath) {
2759
2926
  const dataPath = basePath;
2760
- const isScoped = basePath.includes("/projects/:projectId");
2761
- const resolveService = async (projectId) => {
2927
+ const isScoped = basePath.includes("/environments/:environmentId");
2928
+ const resolveService = async (environmentId) => {
2762
2929
  if (!this.approvalsServiceProvider) return void 0;
2763
2930
  try {
2764
- return await this.approvalsServiceProvider(projectId);
2931
+ return await this.approvalsServiceProvider(environmentId);
2765
2932
  } catch {
2766
2933
  return void 0;
2767
2934
  }
@@ -2794,10 +2961,10 @@ var RestServer = class {
2794
2961
  path: `${dataPath}/approvals/processes`,
2795
2962
  handler: async (req, res) => {
2796
2963
  try {
2797
- const projectId = isScoped ? req.params?.projectId : void 0;
2798
- const context = await this.resolveExecCtx(projectId, req);
2964
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
2965
+ const context = await this.resolveExecCtx(environmentId, req);
2799
2966
  if (this.enforceAuth(req, res, context)) return;
2800
- const svc = await resolveService(projectId);
2967
+ const svc = await resolveService(environmentId);
2801
2968
  if (!svc) return respond501(res);
2802
2969
  const q = req.query ?? {};
2803
2970
  const rows = await svc.listProcesses({
@@ -2817,10 +2984,10 @@ var RestServer = class {
2817
2984
  path: `${dataPath}/approvals/processes`,
2818
2985
  handler: async (req, res) => {
2819
2986
  try {
2820
- const projectId = isScoped ? req.params?.projectId : void 0;
2821
- const context = await this.resolveExecCtx(projectId, req);
2987
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
2988
+ const context = await this.resolveExecCtx(environmentId, req);
2822
2989
  if (this.enforceAuth(req, res, context)) return;
2823
- const svc = await resolveService(projectId);
2990
+ const svc = await resolveService(environmentId);
2824
2991
  if (!svc) return respond501(res);
2825
2992
  try {
2826
2993
  const row = await svc.defineProcess(req.body ?? {}, context ?? {});
@@ -2841,10 +3008,10 @@ var RestServer = class {
2841
3008
  path: `${dataPath}/approvals/processes/:id`,
2842
3009
  handler: async (req, res) => {
2843
3010
  try {
2844
- const projectId = isScoped ? req.params?.projectId : void 0;
2845
- const context = await this.resolveExecCtx(projectId, req);
3011
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
3012
+ const context = await this.resolveExecCtx(environmentId, req);
2846
3013
  if (this.enforceAuth(req, res, context)) return;
2847
- const svc = await resolveService(projectId);
3014
+ const svc = await resolveService(environmentId);
2848
3015
  if (!svc) return respond501(res);
2849
3016
  const row = await svc.getProcess(req.params.id, context ?? {});
2850
3017
  if (!row) {
@@ -2864,10 +3031,10 @@ var RestServer = class {
2864
3031
  path: `${dataPath}/approvals/processes/:id`,
2865
3032
  handler: async (req, res) => {
2866
3033
  try {
2867
- const projectId = isScoped ? req.params?.projectId : void 0;
2868
- const context = await this.resolveExecCtx(projectId, req);
3034
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
3035
+ const context = await this.resolveExecCtx(environmentId, req);
2869
3036
  if (this.enforceAuth(req, res, context)) return;
2870
- const svc = await resolveService(projectId);
3037
+ const svc = await resolveService(environmentId);
2871
3038
  if (!svc) return respond501(res);
2872
3039
  await svc.deleteProcess(req.params.id, context ?? {});
2873
3040
  res.status(204).end();
@@ -2883,10 +3050,10 @@ var RestServer = class {
2883
3050
  path: `${dataPath}/approvals/requests`,
2884
3051
  handler: async (req, res) => {
2885
3052
  try {
2886
- const projectId = isScoped ? req.params?.projectId : void 0;
2887
- const context = await this.resolveExecCtx(projectId, req);
3053
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
3054
+ const context = await this.resolveExecCtx(environmentId, req);
2888
3055
  if (this.enforceAuth(req, res, context)) return;
2889
- const svc = await resolveService(projectId);
3056
+ const svc = await resolveService(environmentId);
2890
3057
  if (!svc) return respond501(res);
2891
3058
  const body = req.body ?? {};
2892
3059
  try {
@@ -2915,10 +3082,10 @@ var RestServer = class {
2915
3082
  path: `${dataPath}/approvals/requests`,
2916
3083
  handler: async (req, res) => {
2917
3084
  try {
2918
- const projectId = isScoped ? req.params?.projectId : void 0;
2919
- const context = await this.resolveExecCtx(projectId, req);
3085
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
3086
+ const context = await this.resolveExecCtx(environmentId, req);
2920
3087
  if (this.enforceAuth(req, res, context)) return;
2921
- const svc = await resolveService(projectId);
3088
+ const svc = await resolveService(environmentId);
2922
3089
  if (!svc) {
2923
3090
  res.json({ data: [] });
2924
3091
  return;
@@ -2944,10 +3111,10 @@ var RestServer = class {
2944
3111
  path: `${dataPath}/approvals/requests/:id`,
2945
3112
  handler: async (req, res) => {
2946
3113
  try {
2947
- const projectId = isScoped ? req.params?.projectId : void 0;
2948
- const context = await this.resolveExecCtx(projectId, req);
3114
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
3115
+ const context = await this.resolveExecCtx(environmentId, req);
2949
3116
  if (this.enforceAuth(req, res, context)) return;
2950
- const svc = await resolveService(projectId);
3117
+ const svc = await resolveService(environmentId);
2951
3118
  if (!svc) return respond501(res);
2952
3119
  const row = await svc.getRequest(req.params.id, context ?? {});
2953
3120
  if (!row) {
@@ -2968,10 +3135,10 @@ var RestServer = class {
2968
3135
  path: `${dataPath}/approvals/requests/:id/${suffix}`,
2969
3136
  handler: async (req, res) => {
2970
3137
  try {
2971
- const projectId = isScoped ? req.params?.projectId : void 0;
2972
- const context = await this.resolveExecCtx(projectId, req);
3138
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
3139
+ const context = await this.resolveExecCtx(environmentId, req);
2973
3140
  if (this.enforceAuth(req, res, context)) return;
2974
- const svc = await resolveService(projectId);
3141
+ const svc = await resolveService(environmentId);
2975
3142
  if (!svc) return respond501(res);
2976
3143
  const body = req.body ?? {};
2977
3144
  try {
@@ -3000,10 +3167,10 @@ var RestServer = class {
3000
3167
  path: `${dataPath}/approvals/requests/:id/actions`,
3001
3168
  handler: async (req, res) => {
3002
3169
  try {
3003
- const projectId = isScoped ? req.params?.projectId : void 0;
3004
- const context = await this.resolveExecCtx(projectId, req);
3170
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
3171
+ const context = await this.resolveExecCtx(environmentId, req);
3005
3172
  if (this.enforceAuth(req, res, context)) return;
3006
- const svc = await resolveService(projectId);
3173
+ const svc = await resolveService(environmentId);
3007
3174
  if (!svc) return respond501(res);
3008
3175
  const rows = await svc.listActions(req.params.id, context ?? {});
3009
3176
  res.json({ data: rows });
@@ -3021,7 +3188,7 @@ var RestServer = class {
3021
3188
  registerBatchEndpoints(basePath) {
3022
3189
  const { crud, batch } = this.config;
3023
3190
  const dataPath = `${basePath}${crud.dataPrefix}`;
3024
- const isScoped = basePath.includes("/projects/:projectId");
3191
+ const isScoped = basePath.includes("/environments/:environmentId");
3025
3192
  const operations = batch.operations;
3026
3193
  if (batch.enableBatchEndpoint && this.protocol.batchData) {
3027
3194
  this.routeManager.register({
@@ -3029,14 +3196,14 @@ var RestServer = class {
3029
3196
  path: `${dataPath}/:object/batch`,
3030
3197
  handler: async (req, res) => {
3031
3198
  try {
3032
- const projectId = isScoped ? req.params?.projectId : void 0;
3033
- const p = await this.resolveProtocol(projectId, req);
3034
- const context = await this.resolveExecCtx(projectId, req);
3199
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
3200
+ const p = await this.resolveProtocol(environmentId, req);
3201
+ const context = await this.resolveExecCtx(environmentId, req);
3035
3202
  if (this.enforceAuth(req, res, context)) return;
3036
3203
  const result = await p.batchData({
3037
3204
  object: req.params.object,
3038
3205
  request: req.body,
3039
- ...projectId ? { projectId } : {},
3206
+ ...environmentId ? { environmentId } : {},
3040
3207
  ...context ? { context } : {}
3041
3208
  });
3042
3209
  res.json(result);
@@ -3057,14 +3224,14 @@ var RestServer = class {
3057
3224
  path: `${dataPath}/:object/createMany`,
3058
3225
  handler: async (req, res) => {
3059
3226
  try {
3060
- const projectId = isScoped ? req.params?.projectId : void 0;
3061
- const p = await this.resolveProtocol(projectId, req);
3062
- const context = await this.resolveExecCtx(projectId, req);
3227
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
3228
+ const p = await this.resolveProtocol(environmentId, req);
3229
+ const context = await this.resolveExecCtx(environmentId, req);
3063
3230
  if (this.enforceAuth(req, res, context)) return;
3064
3231
  const result = await p.createManyData({
3065
3232
  object: req.params.object,
3066
3233
  records: req.body || [],
3067
- ...projectId ? { projectId } : {},
3234
+ ...environmentId ? { environmentId } : {},
3068
3235
  ...context ? { context } : {}
3069
3236
  });
3070
3237
  res.status(201).json(result);
@@ -3085,14 +3252,14 @@ var RestServer = class {
3085
3252
  path: `${dataPath}/:object/updateMany`,
3086
3253
  handler: async (req, res) => {
3087
3254
  try {
3088
- const projectId = isScoped ? req.params?.projectId : void 0;
3089
- const p = await this.resolveProtocol(projectId, req);
3090
- const context = await this.resolveExecCtx(projectId, req);
3255
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
3256
+ const p = await this.resolveProtocol(environmentId, req);
3257
+ const context = await this.resolveExecCtx(environmentId, req);
3091
3258
  if (this.enforceAuth(req, res, context)) return;
3092
3259
  const result = await p.updateManyData({
3093
3260
  object: req.params.object,
3094
3261
  ...req.body,
3095
- ...projectId ? { projectId } : {},
3262
+ ...environmentId ? { environmentId } : {},
3096
3263
  ...context ? { context } : {}
3097
3264
  });
3098
3265
  res.json(result);
@@ -3113,14 +3280,14 @@ var RestServer = class {
3113
3280
  path: `${dataPath}/:object/deleteMany`,
3114
3281
  handler: async (req, res) => {
3115
3282
  try {
3116
- const projectId = isScoped ? req.params?.projectId : void 0;
3117
- const p = await this.resolveProtocol(projectId, req);
3118
- const context = await this.resolveExecCtx(projectId, req);
3283
+ const environmentId = isScoped ? req.params?.environmentId : void 0;
3284
+ const p = await this.resolveProtocol(environmentId, req);
3285
+ const context = await this.resolveExecCtx(environmentId, req);
3119
3286
  if (this.enforceAuth(req, res, context)) return;
3120
3287
  const result = await p.deleteManyData({
3121
3288
  object: req.params.object,
3122
3289
  ...req.body,
3123
- ...projectId ? { projectId } : {},
3290
+ ...environmentId ? { environmentId } : {},
3124
3291
  ...context ? { context } : {}
3125
3292
  });
3126
3293
  res.json(result);
@@ -3298,64 +3465,64 @@ function createRestApiPlugin(config = {}) {
3298
3465
  envRegistry = ctx.getService("env-registry");
3299
3466
  } catch (e) {
3300
3467
  }
3301
- const defaultProjectIdProvider = () => {
3468
+ const defaultEnvironmentIdProvider = () => {
3302
3469
  try {
3303
3470
  const dp = ctx.getService("default-project");
3304
- return dp?.projectId;
3471
+ return dp?.environmentId;
3305
3472
  } catch {
3306
3473
  return void 0;
3307
3474
  }
3308
3475
  };
3309
- const authServiceProvider = async (_projectId) => {
3476
+ const authServiceProvider = async (_environmentId) => {
3310
3477
  try {
3311
3478
  return ctx.getService("auth");
3312
3479
  } catch {
3313
3480
  return void 0;
3314
3481
  }
3315
3482
  };
3316
- const objectQLProvider = async (_projectId) => {
3483
+ const objectQLProvider = async (_environmentId) => {
3317
3484
  try {
3318
3485
  return ctx.getService("objectql");
3319
3486
  } catch {
3320
3487
  return void 0;
3321
3488
  }
3322
3489
  };
3323
- const emailServiceProvider = async (_projectId) => {
3490
+ const emailServiceProvider = async (_environmentId) => {
3324
3491
  try {
3325
3492
  return ctx.getService("email");
3326
3493
  } catch {
3327
3494
  return void 0;
3328
3495
  }
3329
3496
  };
3330
- const sharingServiceProvider = async (_projectId) => {
3497
+ const sharingServiceProvider = async (_environmentId) => {
3331
3498
  try {
3332
3499
  return ctx.getService("sharing");
3333
3500
  } catch {
3334
3501
  return void 0;
3335
3502
  }
3336
3503
  };
3337
- const reportsServiceProvider = async (_projectId) => {
3504
+ const reportsServiceProvider = async (_environmentId) => {
3338
3505
  try {
3339
3506
  return ctx.getService("reports");
3340
3507
  } catch {
3341
3508
  return void 0;
3342
3509
  }
3343
3510
  };
3344
- const approvalsServiceProvider = async (_projectId) => {
3511
+ const approvalsServiceProvider = async (_environmentId) => {
3345
3512
  try {
3346
3513
  return ctx.getService("approvals");
3347
3514
  } catch {
3348
3515
  return void 0;
3349
3516
  }
3350
3517
  };
3351
- const sharingRulesServiceProvider = async (_projectId) => {
3518
+ const sharingRulesServiceProvider = async (_environmentId) => {
3352
3519
  try {
3353
3520
  return ctx.getService("sharingRules");
3354
3521
  } catch {
3355
3522
  return void 0;
3356
3523
  }
3357
3524
  };
3358
- const i18nServiceProvider = async (_projectId) => {
3525
+ const i18nServiceProvider = async (_environmentId) => {
3359
3526
  try {
3360
3527
  return ctx.getService("i18n");
3361
3528
  } catch {
@@ -3372,7 +3539,7 @@ function createRestApiPlugin(config = {}) {
3372
3539
  }
3373
3540
  ctx.logger.info("Hydrating REST API from Protocol...");
3374
3541
  try {
3375
- const restServer = new RestServer(server, protocol, config.api, kernelManager, envRegistry, defaultProjectIdProvider, authServiceProvider, objectQLProvider, emailServiceProvider, sharingServiceProvider, reportsServiceProvider, approvalsServiceProvider, sharingRulesServiceProvider, i18nServiceProvider);
3542
+ const restServer = new RestServer(server, protocol, config.api, kernelManager, envRegistry, defaultEnvironmentIdProvider, authServiceProvider, objectQLProvider, emailServiceProvider, sharingServiceProvider, reportsServiceProvider, approvalsServiceProvider, sharingRulesServiceProvider, i18nServiceProvider);
3376
3543
  restServer.registerRoutes();
3377
3544
  ctx.logger.info("REST API successfully registered");
3378
3545
  } catch (err) {
@@ -3388,13 +3555,13 @@ function createRestApiPlugin(config = {}) {
3388
3555
  const enableProjectScoping = config.api?.api?.enableProjectScoping ?? false;
3389
3556
  const projectResolution = config.api?.api?.projectResolution ?? "auto";
3390
3557
  if (enableProjectScoping && projectResolution === "required") {
3391
- registerPackageRoutes(server, packageService, `${versionedBase}/projects/:projectId`, {
3558
+ registerPackageRoutes(server, packageService, `${versionedBase}/environments/:environmentId`, {
3392
3559
  protocol
3393
3560
  });
3394
3561
  } else {
3395
3562
  registerPackageRoutes(server, packageService, versionedBase, { protocol });
3396
3563
  if (enableProjectScoping) {
3397
- registerPackageRoutes(server, packageService, `${versionedBase}/projects/:projectId`, {
3564
+ registerPackageRoutes(server, packageService, `${versionedBase}/environments/:environmentId`, {
3398
3565
  protocol
3399
3566
  });
3400
3567
  }