@objectstack/runtime 9.10.0 → 10.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.cjs CHANGED
@@ -987,6 +987,35 @@ var init_app_plugin = __esm({
987
987
  error: err?.message ?? String(err)
988
988
  });
989
989
  }
990
+ try {
991
+ const metadata = ctx.getService("metadata");
992
+ if (typeof metadata?.registerInMemory === "function") {
993
+ const securityBundle = this.bundle.manifest ? { ...this.bundle.manifest, ...this.bundle } : this.bundle;
994
+ const SECURITY_FIELDS = [
995
+ ["roles", "role"],
996
+ ["permissions", "permission"],
997
+ ["sharingRules", "sharing_rule"],
998
+ ["policies", "policy"]
999
+ ];
1000
+ let count = 0;
1001
+ for (const [field, type] of SECURITY_FIELDS) {
1002
+ const arr = securityBundle?.[field];
1003
+ if (!Array.isArray(arr)) continue;
1004
+ for (const item of arr) {
1005
+ if (!item?.name) continue;
1006
+ metadata.registerInMemory(type, item.name, item);
1007
+ count += 1;
1008
+ }
1009
+ }
1010
+ if (count > 0) {
1011
+ ctx.logger.info("Registered stack-declared security metadata", { appId, count });
1012
+ }
1013
+ }
1014
+ } catch (err) {
1015
+ ctx.logger.warn("[AppPlugin] failed to register security metadata", {
1016
+ error: err?.message ?? String(err)
1017
+ });
1018
+ }
990
1019
  const stackBundle = this.bundle.default || this.bundle;
991
1020
  const runtime = stackBundle && typeof stackBundle.onEnable === "function" ? stackBundle : this.bundle;
992
1021
  if (runtime && typeof runtime.onEnable === "function") {
@@ -1606,6 +1635,8 @@ async function createStandaloneStack(config) {
1606
1635
  const requires = Array.isArray(artifactBundle?.requires) ? artifactBundle.requires.filter((c) => typeof c === "string") : void 0;
1607
1636
  const objects = Array.isArray(artifactBundle?.objects) ? artifactBundle.objects : void 0;
1608
1637
  const manifest = artifactBundle?.manifest;
1638
+ const permissions = Array.isArray(artifactBundle?.permissions) ? artifactBundle.permissions : void 0;
1639
+ const roles = Array.isArray(artifactBundle?.roles) ? artifactBundle.roles : void 0;
1609
1640
  return {
1610
1641
  plugins,
1611
1642
  api: {
@@ -1614,7 +1645,9 @@ async function createStandaloneStack(config) {
1614
1645
  },
1615
1646
  ...requires ? { requires } : {},
1616
1647
  ...objects ? { objects } : {},
1617
- ...manifest ? { manifest } : {}
1648
+ ...manifest ? { manifest } : {},
1649
+ ...permissions ? { permissions } : {},
1650
+ ...roles ? { roles } : {}
1618
1651
  };
1619
1652
  }
1620
1653
  var import_node_path3, import_node_fs2, import_node_os, import_zod, import_types2, StandaloneStackConfigSchema;
@@ -2065,25 +2098,33 @@ function coerceLocale(value) {
2065
2098
  const s = typeof value === "string" ? value.trim() : value != null ? String(value).trim() : "";
2066
2099
  return s || void 0;
2067
2100
  }
2101
+ function coerceCurrency(value) {
2102
+ const s = typeof value === "string" ? value.trim().toUpperCase() : "";
2103
+ return /^[A-Z]{3}$/.test(s) ? s : void 0;
2104
+ }
2068
2105
  async function resolveLocalization(opts, ql, sctx) {
2069
2106
  try {
2070
2107
  const settings = await opts.getService("settings");
2071
2108
  if (settings && typeof settings.get === "function") {
2072
- const [tzRes, localeRes] = await Promise.all([
2109
+ const [tzRes, localeRes, currencyRes] = await Promise.all([
2073
2110
  settings.get("localization", "timezone", sctx).catch(() => void 0),
2074
- settings.get("localization", "locale", sctx).catch(() => void 0)
2111
+ settings.get("localization", "locale", sctx).catch(() => void 0),
2112
+ settings.get("localization", "currency", sctx).catch(() => void 0)
2075
2113
  ]);
2076
2114
  const tz = coerceTimeZone(tzRes?.value);
2077
2115
  const locale = coerceLocale(localeRes?.value);
2078
- if (tz || locale) return { timezone: tz ?? "UTC", locale: locale ?? "en-US" };
2116
+ const currency = coerceCurrency(currencyRes?.value);
2117
+ if (tz || locale || currency) return { timezone: tz ?? "UTC", locale: locale ?? "en-US", currency };
2079
2118
  }
2080
2119
  } catch {
2081
2120
  }
2082
2121
  const tzRows = await tryFind(ql, "sys_setting", { namespace: "localization", key: "timezone", scope: "tenant" }, 1);
2083
2122
  const localeRows = await tryFind(ql, "sys_setting", { namespace: "localization", key: "locale", scope: "tenant" }, 1);
2123
+ const currencyRows = await tryFind(ql, "sys_setting", { namespace: "localization", key: "currency", scope: "tenant" }, 1);
2084
2124
  return {
2085
2125
  timezone: coerceTimeZone(tzRows[0]?.value) ?? "UTC",
2086
- locale: coerceLocale(localeRows[0]?.value) ?? "en-US"
2126
+ locale: coerceLocale(localeRows[0]?.value) ?? "en-US",
2127
+ currency: coerceCurrency(currencyRows[0]?.value)
2087
2128
  };
2088
2129
  }
2089
2130
  async function resolveExecutionContext(opts) {
@@ -2116,6 +2157,7 @@ async function resolveExecutionContext(opts) {
2116
2157
  userId = sessionData?.user?.id ?? sessionData?.session?.userId;
2117
2158
  tenantId = tenantId ?? sessionData?.session?.activeOrganizationId;
2118
2159
  ctx.accessToken = sessionData?.session?.token ?? ctx.accessToken;
2160
+ if (sessionData?.user?.email) ctx.email = String(sessionData.user.email);
2119
2161
  } catch {
2120
2162
  }
2121
2163
  }
@@ -2124,6 +2166,10 @@ async function resolveExecutionContext(opts) {
2124
2166
  if (!userId) return ctx;
2125
2167
  const ql = await opts.getQl();
2126
2168
  if (!ql) return ctx;
2169
+ if (!ctx.email) {
2170
+ const userRows = await tryFind(ql, "sys_user", { id: userId }, 1);
2171
+ if (userRows[0]?.email) ctx.email = String(userRows[0].email);
2172
+ }
2127
2173
  const memberWhere = tenantId ? { user_id: userId, organization_id: tenantId } : { user_id: userId };
2128
2174
  const members = await tryFind(ql, "sys_member", memberWhere, 50);
2129
2175
  for (const m of members) {
@@ -2133,6 +2179,13 @@ async function resolveExecutionContext(opts) {
2133
2179
  }
2134
2180
  }
2135
2181
  }
2182
+ const userRoleRows = await tryFind(ql, "sys_user_role", { user_id: userId }, 200);
2183
+ for (const ur of userRoleRows) {
2184
+ const org = ur.organization_id ?? null;
2185
+ if (org && tenantId && org !== tenantId) continue;
2186
+ const r = ur.role;
2187
+ if (typeof r === "string" && r && !ctx.roles.includes(r)) ctx.roles.push(r);
2188
+ }
2136
2189
  if (tenantId) {
2137
2190
  const orgMembers = await tryFind(
2138
2191
  ql,
@@ -2219,6 +2272,7 @@ async function resolveExecutionContext(opts) {
2219
2272
  const localization = await resolveLocalization(opts, ql, { tenantId, userId });
2220
2273
  ctx.timezone = localization.timezone;
2221
2274
  ctx.locale = localization.locale;
2275
+ if (localization.currency) ctx.currency = localization.currency;
2222
2276
  return ctx;
2223
2277
  }
2224
2278
  function isPermissionDeniedError(e) {