@oondemand/oon-core-back 0.7.11 → 0.7.12

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oondemand/oon-core-back",
3
- "version": "0.7.11",
3
+ "version": "0.7.12",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/oondemand/oon-platform.git",
package/src/index.js CHANGED
@@ -23,6 +23,7 @@ const localDevelopment = require("./core/runtime/localDevelopment");
23
23
  const localExecutionPolicy = require("./core/runtime/localExecutionPolicy");
24
24
 
25
25
  module.exports = {
26
+ appArchitecture: require("./manifest/appArchitecture"),
26
27
  start, createApp, activate,
27
28
  defineCentral: define.defineCentral, defineModel: define.defineModel,
28
29
  defineCollection: define.defineCollection, defineDocument: define.defineDocument,
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+
3
+ // Canonical #290 contract. Deployment is a derived technical projection, never
4
+ // a second architectural authority. This module has no commercial policy.
5
+ const DEPLOYMENT_BY_TENANCY = Object.freeze({ none: "singleton", single_tenant: "dedicated", multi_tenant: "shared" });
6
+ const AVAILABILITY_TENANCY = Object.freeze({
7
+ system: Object.freeze(["none"]), common: Object.freeze(["none", "multi_tenant"]),
8
+ self: Object.freeze(["single_tenant"]), licensed: Object.freeze(["single_tenant", "multi_tenant"]),
9
+ });
10
+ function invalid(code, field, message) {
11
+ throw Object.assign(new Error(message), { code, statusCode: 422, details: { field } });
12
+ }
13
+ function deriveDeploymentMode(tenancyModel) {
14
+ if (!Object.hasOwn(DEPLOYMENT_BY_TENANCY, tenancyModel)) invalid("APP_TENANCY_INVALID", "tenancyModel", "Modelo de tenancy inválido.");
15
+ return DEPLOYMENT_BY_TENANCY[tenancyModel];
16
+ }
17
+ function resolveAppArchitecture(input) {
18
+ const { availability, tenancyModel } = input || {};
19
+ const deploymentMode = deriveDeploymentMode(tenancyModel);
20
+ if (!Object.hasOwn(AVAILABILITY_TENANCY, availability) || !AVAILABILITY_TENANCY[availability].includes(tenancyModel)) {
21
+ invalid("APP_ARCHITECTURE_INVALID", "availability", "Disponibilidade e tenancy fora da matriz suportada.");
22
+ }
23
+ if (input.ecosystemRole && (input.ecosystemRole === "root") !== (availability === "system")) {
24
+ invalid("APP_ARCHITECTURE_INVALID", "ecosystemRole", "Somente a Central root usa availability=system.");
25
+ }
26
+ if (input.deploymentMode !== undefined && input.deploymentMode !== deploymentMode) {
27
+ invalid(input.deploymentMode === "hybrid" ? "APP_HYBRID_UNSUPPORTED" : "APP_DEPLOYMENT_CONTRADICTION", "deploymentMode", "Implantação legada deve concordar com tenancyModel; hybrid não é suportado.");
28
+ }
29
+ return Object.freeze({ availability, tenancyModel, deploymentMode });
30
+ }
31
+ module.exports = { DEPLOYMENT_BY_TENANCY, AVAILABILITY_TENANCY, deriveDeploymentMode, resolveAppArchitecture };
@@ -5,6 +5,7 @@ const path = require("node:path");
5
5
  const ctx = require("../core/context");
6
6
  const corePackage = require("../../package.json");
7
7
  const { validateRbac } = require("./rbacManifest");
8
+ const { deriveDeploymentMode, resolveAppArchitecture } = require("./appArchitecture");
8
9
 
9
10
  const APP_MANIFEST_FILENAME = "central.app.json";
10
11
  const APP_MANIFEST_SCHEMA_VERSION = 2;
@@ -13,10 +14,10 @@ const SUPPORTED_MODULES = Object.freeze([
13
14
  "collections", "documents", "pipelines", "assistants", "currencies",
14
15
  ]);
15
16
  const SUPPORTED_TENANCY_MODELS = Object.freeze(["none", "single_tenant", "multi_tenant"]);
16
- const SUPPORTED_AVAILABILITIES = Object.freeze(["system", "common", "licensed"]);
17
+ const SUPPORTED_AVAILABILITIES = Object.freeze(["system", "common", "self", "licensed"]);
17
18
  const SUPPORTED_ACCESS_MODES = Object.freeze(["tenant_members", "explicit_users", "authenticated_users"]);
18
19
  const SUPPORTED_AUTHORIZATION_MODES = Object.freeze(["none", "rbac"]);
19
- const SUPPORTED_DEPLOYMENT_MODES = Object.freeze(["singleton", "shared", "dedicated", "hybrid"]);
20
+ const SUPPORTED_DEPLOYMENT_MODES = Object.freeze(["singleton", "shared", "dedicated"]);
20
21
  const TRANSACTIONAL_EMAIL_CAPABILITY = "core.transactional-email";
21
22
  const EMAIL_SCOPE_POLICIES = Object.freeze(["app_only", "tenant_required", "tenant_override"]);
22
23
  const APP_KIND_ROLE = Object.freeze({
@@ -201,7 +202,7 @@ function dimensionsFor(manifest) {
201
202
  availability: manifest.availability || defaults.availability,
202
203
  accessMode: manifest.accessMode || defaults.accessMode,
203
204
  authorizationMode: manifest.authorizationMode || defaults.authorizationMode,
204
- deploymentMode: manifest.deploymentMode || defaults.deploymentMode,
205
+ deploymentMode: deriveDeploymentMode(SUPPORTED_TENANCY_MODELS.includes(manifest.tenancyModel) ? manifest.tenancyModel : defaults.tenancyModel),
205
206
  };
206
207
  }
207
208
 
@@ -238,6 +239,11 @@ function validateAppManifest(input, { coreVersion = corePackage.version } = {})
238
239
  validateDimension(input.deploymentMode, "deploymentMode", SUPPORTED_DEPLOYMENT_MODES, issues);
239
240
  const dimensions = dimensionsFor(input);
240
241
  const role = APP_KIND_ROLE[input.appKind];
242
+ try {
243
+ resolveAppArchitecture({ ...dimensions, ecosystemRole: role, ...(input.deploymentMode !== undefined ? { deploymentMode: input.deploymentMode } : {}) });
244
+ } catch (error) {
245
+ addIssue(issues, error.details?.field || "tenancyModel", `${error.code}: ${error.message}`);
246
+ }
241
247
  if (role === "root" && (
242
248
  dimensions.tenancyModel !== "none" ||
243
249
  dimensions.availability !== "system" ||