@stndrds/schema 1.0.0-alpha.71 → 1.0.0-alpha.73

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.
@@ -0,0 +1,58 @@
1
+ // src/constants/default-roles.ts
2
+ var SYSTEM_RESOURCES = {
3
+ /** User profiles, invitations, roles and permissions management */
4
+ PEOPLE: "people",
5
+ /** Object definitions, attributes, tenant settings and audit logs */
6
+ WORKSPACE: "workspace"
7
+ };
8
+ var ALL_SYSTEM_RESOURCES = [
9
+ SYSTEM_RESOURCES.PEOPLE,
10
+ SYSTEM_RESOURCES.WORKSPACE
11
+ ];
12
+ var SYSTEM_RESOURCE_LABELS = {
13
+ people: "People",
14
+ workspace: "Workspace"
15
+ };
16
+ var DEFAULT_ROLES = {
17
+ /** Full platform access - can manage everything */
18
+ OWNER: "owner",
19
+ /** Standard user - can work with business data and view people */
20
+ MEMBER: "member"
21
+ };
22
+ var DEFAULT_ROLE_LABELS = {
23
+ owner: "Owner",
24
+ member: "Member"
25
+ };
26
+ var DEFAULT_ROLE_DESCRIPTIONS = {
27
+ owner: "Full access to all platform features and data",
28
+ member: "Can view, create, edit and delete business data"
29
+ };
30
+ var ALL_ACTIONS = ["read", "create", "update", "delete"];
31
+ var DEFAULT_ROLE_PERMISSIONS = {
32
+ owner: {
33
+ system: [{ target: "*", actions: ALL_ACTIONS }],
34
+ object: [{ target: "*", actions: ALL_ACTIONS }]
35
+ },
36
+ member: {
37
+ system: [{ target: "people", actions: ["read"] }],
38
+ object: [{ target: "*", actions: ALL_ACTIONS }]
39
+ }
40
+ };
41
+ function isDefaultRole(roleName) {
42
+ return Object.values(DEFAULT_ROLES).includes(roleName);
43
+ }
44
+ function isAdminRole(roleName) {
45
+ return roleName === DEFAULT_ROLES.OWNER;
46
+ }
47
+
48
+ export {
49
+ SYSTEM_RESOURCES,
50
+ ALL_SYSTEM_RESOURCES,
51
+ SYSTEM_RESOURCE_LABELS,
52
+ DEFAULT_ROLES,
53
+ DEFAULT_ROLE_LABELS,
54
+ DEFAULT_ROLE_DESCRIPTIONS,
55
+ DEFAULT_ROLE_PERMISSIONS,
56
+ isDefaultRole,
57
+ isAdminRole
58
+ };
@@ -13,6 +13,9 @@ import {
13
13
  validateObject,
14
14
  validateObjectOrThrow
15
15
  } from "./chunk-5SZ5OISG.mjs";
16
+ import {
17
+ isAdminRole
18
+ } from "./chunk-6P3NPTYV.mjs";
16
19
  import {
17
20
  __require
18
21
  } from "./chunk-Y6FXYEAI.mjs";
@@ -3700,13 +3703,24 @@ function parsePipeExpression(pipeExpr) {
3700
3703
  }
3701
3704
  function renderLabelExpression(template, values, fallback = DEFAULT_LABEL_FALLBACK) {
3702
3705
  const result = template.replace(/\{\{\s*([^}]+)\s*\}\}/g, (_, expr) => {
3703
- const parts = expr.split("|").map((s) => s.trim());
3704
- const path = parts[0];
3705
- let value = getValue(values, path);
3706
+ const orParts = expr.split("||").map((s) => s.trim());
3707
+ const lastPart = orParts[orParts.length - 1];
3708
+ const pipeSplit = lastPart.split("|").map((s) => s.trim());
3709
+ orParts[orParts.length - 1] = pipeSplit[0];
3710
+ const pipes = pipeSplit.slice(1).filter(Boolean);
3711
+ const alternatives = orParts.filter(Boolean);
3712
+ let value = "";
3713
+ for (const alt of alternatives) {
3714
+ const v = getValue(values, alt);
3715
+ if (v != null && v !== "") {
3716
+ value = v;
3717
+ break;
3718
+ }
3719
+ }
3706
3720
  const isEmpty3 = value == null || value === "";
3707
- if (isEmpty3 && parts.length === 1) return "";
3708
- for (let i = 1; i < parts.length; i++) {
3709
- const { name: pipeName, args } = parsePipeExpression(parts[i]);
3721
+ if (isEmpty3 && pipes.length === 0) return "";
3722
+ for (const pipeExpr of pipes) {
3723
+ const { name: pipeName, args } = parsePipeExpression(pipeExpr);
3710
3724
  const simpleFn = simplePipes[pipeName];
3711
3725
  if (simpleFn) {
3712
3726
  if (value != null && value !== "") {
@@ -3728,13 +3742,19 @@ function isLabelExpression(value) {
3728
3742
  }
3729
3743
  function extractAttributeNames(template) {
3730
3744
  const names = [];
3731
- const regex = /\{\{\s*([^|}]+)/g;
3745
+ const regex = /\{\{\s*([^}]+)\s*\}\}/g;
3732
3746
  let match;
3733
3747
  while ((match = regex.exec(template)) !== null) {
3734
- const path = match[1].trim();
3735
- const rootName = path.split(".")[0];
3736
- if (rootName && !names.includes(rootName)) {
3737
- names.push(rootName);
3748
+ const expr = match[1].trim();
3749
+ const orParts = expr.split("||").map((s) => s.trim());
3750
+ const lastPart = orParts[orParts.length - 1];
3751
+ orParts[orParts.length - 1] = lastPart.split("|")[0].trim();
3752
+ for (const part of orParts) {
3753
+ if (!part) continue;
3754
+ const rootName = part.split(".")[0];
3755
+ if (rootName && !names.includes(rootName)) {
3756
+ names.push(rootName);
3757
+ }
3738
3758
  }
3739
3759
  }
3740
3760
  return names;
@@ -4303,7 +4323,6 @@ function createMockUserProfilesRepository(stores) {
4303
4323
  firstName: data.firstName,
4304
4324
  lastName: data.lastName,
4305
4325
  avatarUrl: data.avatarUrl,
4306
- role: data.role ?? "member",
4307
4326
  status: data.status ?? "active",
4308
4327
  createdAt: /* @__PURE__ */ new Date(),
4309
4328
  updatedAt: /* @__PURE__ */ new Date()
@@ -4336,12 +4355,23 @@ function createMockUserProfilesRepository(stores) {
4336
4355
  }
4337
4356
  return Promise.resolve(results);
4338
4357
  },
4339
- countByRole(role) {
4358
+ getUsersWithRoles(filters) {
4340
4359
  const tenantId = getTenantId();
4341
- const count = Array.from(stores.userProfiles.values()).filter(
4342
- (profile) => profile.tenantId === tenantId && profile.role === role
4343
- ).length;
4344
- return Promise.resolve(count);
4360
+ const profiles = Array.from(stores.userProfiles.values()).filter(
4361
+ (p) => p.tenantId === tenantId
4362
+ );
4363
+ const results = [];
4364
+ for (const profile of profiles) {
4365
+ const roleIds = Array.from(stores.userRoles.values()).filter((ur) => ur.userProfileId === profile.id && ur.tenantId === tenantId).map((ur) => ur.roleId);
4366
+ const roles = Array.from(stores.roles.values()).filter((r) => roleIds.includes(r.id));
4367
+ if (filters?.allowedRoles && filters.allowedRoles.length > 0) {
4368
+ const allowed = filters.allowedRoles;
4369
+ const hasMatchingRole = roles.some((r) => allowed.includes(r.name));
4370
+ if (!hasMatchingRole) continue;
4371
+ }
4372
+ results.push({ ...profile, roles });
4373
+ }
4374
+ return Promise.resolve(results);
4345
4375
  },
4346
4376
  updateLastLogin(id) {
4347
4377
  const profile = stores.userProfiles.get(id);
@@ -4360,7 +4390,6 @@ function createMockUserProfilesRepository(stores) {
4360
4390
  email: data.email,
4361
4391
  firstName: data.firstName,
4362
4392
  lastName: data.lastName,
4363
- role: data.role ?? "member",
4364
4393
  status: "pending",
4365
4394
  createdAt: /* @__PURE__ */ new Date(),
4366
4395
  updatedAt: /* @__PURE__ */ new Date()
@@ -4452,7 +4481,6 @@ function createMockPermissionsRepository(stores) {
4452
4481
  scope: input.scope,
4453
4482
  target: input.target,
4454
4483
  actions: input.actions,
4455
- filter: input.filter,
4456
4484
  createdAt: /* @__PURE__ */ new Date()
4457
4485
  };
4458
4486
  stores.permissions.set(perm.id, perm);
@@ -4497,7 +4525,7 @@ function createMockPermissionsRepository(stores) {
4497
4525
  const tenantId = getTenantId();
4498
4526
  const userRoleIds = Array.from(stores.userRoles.values()).filter((ur) => ur.userProfileId === userProfileId && ur.tenantId === tenantId).map((ur) => ur.roleId);
4499
4527
  const userRoles = Array.from(stores.roles.values()).filter((r) => userRoleIds.includes(r.id));
4500
- const isAdmin = userRoles.some((r) => r.name === "admin");
4528
+ const hasAdmin = userRoles.some((r) => isAdminRole(r.name));
4501
4529
  const userPermissions = Array.from(stores.permissions.values()).filter(
4502
4530
  (p) => userRoleIds.includes(p.roleId)
4503
4531
  );
@@ -4524,7 +4552,18 @@ function createMockPermissionsRepository(stores) {
4524
4552
  }
4525
4553
  }
4526
4554
  }
4527
- return Promise.resolve({ isAdmin, objectPermissions, systemPermissions });
4555
+ return Promise.resolve({ isAdmin: hasAdmin, objectPermissions, systemPermissions });
4556
+ },
4557
+ countUsersWithRole(roleName) {
4558
+ const tenantId = getTenantId();
4559
+ const role = Array.from(stores.roles.values()).find(
4560
+ (r) => r.tenantId === tenantId && r.name === roleName
4561
+ );
4562
+ if (!role) return Promise.resolve(0);
4563
+ const userIds = new Set(
4564
+ Array.from(stores.userRoles.values()).filter((ur) => ur.roleId === role.id && ur.tenantId === tenantId).map((ur) => ur.userProfileId)
4565
+ );
4566
+ return Promise.resolve(userIds.size);
4528
4567
  }
4529
4568
  };
4530
4569
  }
@@ -10072,6 +10111,21 @@ var UserService = class extends BaseService {
10072
10111
  }
10073
10112
  const users = await this.adapter.userProfiles.findByIds([...allIds]);
10074
10113
  const userMap = new Map(users.map((u) => [u.id, u]));
10114
+ const allAllowedRoles = /* @__PURE__ */ new Set();
10115
+ for (const [, { attr }] of attrIdMap) {
10116
+ if (attr.allowedRoles && attr.allowedRoles.length > 0) {
10117
+ for (const role of attr.allowedRoles) {
10118
+ allAllowedRoles.add(role);
10119
+ }
10120
+ }
10121
+ }
10122
+ let userRolesMap;
10123
+ if (allAllowedRoles.size > 0) {
10124
+ const usersWithRoles = await this.adapter.userProfiles.getUsersWithRoles({
10125
+ allowedRoles: [...allAllowedRoles]
10126
+ });
10127
+ userRolesMap = new Map(usersWithRoles.map((u) => [u.id, u.roles.map((r) => r.name)]));
10128
+ }
10075
10129
  for (const [attrName, { attr, ids }] of attrIdMap) {
10076
10130
  const invalidIds = [];
10077
10131
  const roleErrors = [];
@@ -10085,8 +10139,10 @@ var UserService = class extends BaseService {
10085
10139
  invalidIds.push(id);
10086
10140
  continue;
10087
10141
  }
10088
- if (attr.allowedRoles && attr.allowedRoles.length > 0) {
10089
- if (!attr.allowedRoles.includes(user2.role)) {
10142
+ if (attr.allowedRoles && attr.allowedRoles.length > 0 && userRolesMap) {
10143
+ const userRoleNames = userRolesMap.get(id) ?? [];
10144
+ const hasAllowedRole = attr.allowedRoles.some((role) => userRoleNames.includes(role));
10145
+ if (!hasAllowedRole) {
10090
10146
  roleErrors.push(id);
10091
10147
  }
10092
10148
  }
@@ -14877,7 +14933,6 @@ var UserProfileService = class extends BaseService {
14877
14933
  * email: authUser.email,
14878
14934
  * firstName: authUser.user_metadata.first_name,
14879
14935
  * lastName: authUser.user_metadata.last_name,
14880
- * role: "member",
14881
14936
  * status: "active"
14882
14937
  * });
14883
14938
  * ```
@@ -14956,7 +15011,6 @@ var UserProfileService = class extends BaseService {
14956
15011
  * {
14957
15012
  * authId: authUser.id,
14958
15013
  * email: authUser.email,
14959
- * role: "member",
14960
15014
  * status: "active"
14961
15015
  * }
14962
15016
  * );
@@ -14981,7 +15035,6 @@ var UserProfileService = class extends BaseService {
14981
15035
  const changes = buildAuditChanges(existing, data, [
14982
15036
  "firstName",
14983
15037
  "lastName",
14984
- "role",
14985
15038
  "status"
14986
15039
  ]);
14987
15040
  if (changes.length > 0) {
@@ -15005,11 +15058,13 @@ var UserProfileService = class extends BaseService {
15005
15058
  */
15006
15059
  async deleteProfile(profileId, options) {
15007
15060
  const profile = await this.getProfileOrThrow(profileId);
15008
- if (options?.checkAdmin) {
15009
- if (profile.role === "admin") {
15010
- const adminCount = await this.adapter.userProfiles.countByRole("admin");
15011
- if (adminCount <= 1) {
15012
- throw new Error("Cannot delete the last admin user");
15061
+ if (options?.checkAdmin && this.adapter.permissions) {
15062
+ const ownerCount = await this.adapter.permissions.countUsersWithRole("owner");
15063
+ if (ownerCount <= 1) {
15064
+ const userRoles = await this.adapter.permissions.getUserRoles(profileId);
15065
+ const isOwner = userRoles.some((r) => r.name === "owner");
15066
+ if (isOwner) {
15067
+ throw new Error("Cannot delete the last owner user");
15013
15068
  }
15014
15069
  }
15015
15070
  }
@@ -15045,15 +15100,6 @@ var UserProfileService = class extends BaseService {
15045
15100
  async updateLastLogin(profileId) {
15046
15101
  await this.adapter.userProfiles.updateLastLogin(profileId);
15047
15102
  }
15048
- /**
15049
- * Change user role
15050
- *
15051
- * @param profileId - Profile UUID
15052
- * @param newRole - New role
15053
- */
15054
- async changeRole(profileId, newRole) {
15055
- return await this.updateProfile(profileId, { role: newRole });
15056
- }
15057
15103
  /**
15058
15104
  * Change user status
15059
15105
  *
@@ -15075,19 +15121,6 @@ var UserProfileService = class extends BaseService {
15075
15121
  () => this.adapter.userProfiles.findByEmail(email)
15076
15122
  );
15077
15123
  }
15078
- /**
15079
- * Check if user has role
15080
- */
15081
- async hasRole(profileId, role) {
15082
- const profile = await this.getProfile(profileId);
15083
- return profile?.role === role;
15084
- }
15085
- /**
15086
- * Check if user is admin
15087
- */
15088
- async isAdmin(profileId) {
15089
- return await this.hasRole(profileId, "admin");
15090
- }
15091
15124
  /**
15092
15125
  * Invite a new user by email.
15093
15126
  *
@@ -15108,7 +15141,6 @@ var UserProfileService = class extends BaseService {
15108
15141
  * email: "john@example.com",
15109
15142
  * firstName: "John",
15110
15143
  * lastName: "Doe",
15111
- * role: "member",
15112
15144
  * redirectTo: "https://app.example.com/welcome",
15113
15145
  * });
15114
15146
  * // Email sent automatically, profile.status === "pending"
@@ -17348,7 +17380,7 @@ var PermissionService = class extends BaseService {
17348
17380
  DEFAULT_ROLE_LABELS,
17349
17381
  DEFAULT_ROLE_DESCRIPTIONS,
17350
17382
  DEFAULT_ROLE_PERMISSIONS
17351
- } = await import("./default-roles-42X3TJI5.mjs");
17383
+ } = await import("./default-roles-OAMHLOCS.mjs");
17352
17384
  const existingRoles = await this.getRoles();
17353
17385
  const existingRoleNames = existingRoles.reduce((set, r) => set.add(r.name), /* @__PURE__ */ new Set());
17354
17386
  for (const roleName of Object.values(DEFAULT_ROLES)) {