@objectstack/plugin-sharing 9.10.0 → 9.11.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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @objectstack/plugin-sharing@9.10.0 build /home/runner/work/framework/framework/packages/plugins/plugin-sharing
2
+ > @objectstack/plugin-sharing@9.11.0 build /home/runner/work/framework/framework/packages/plugins/plugin-sharing
3
3
  > tsup --config ../../../tsup.config.ts
4
4
 
5
5
  CLI Building entry: src/index.ts
@@ -10,13 +10,13 @@
10
10
  CLI Cleaning output folder
11
11
  ESM Build start
12
12
  CJS Build start
13
- ESM dist/index.mjs 109.89 KB
14
- ESM dist/index.mjs.map 191.90 KB
15
- ESM ⚡️ Build success in 208ms
16
- CJS dist/index.js 111.86 KB
17
- CJS dist/index.js.map 191.93 KB
18
- CJS ⚡️ Build success in 249ms
13
+ CJS dist/index.js 114.82 KB
14
+ CJS dist/index.js.map 199.06 KB
15
+ CJS ⚡️ Build success in 319ms
16
+ ESM dist/index.mjs 112.85 KB
17
+ ESM dist/index.mjs.map 199.03 KB
18
+ ESM ⚡️ Build success in 326ms
19
19
  DTS Build start
20
- DTS ⚡️ Build success in 31225ms
21
- DTS dist/index.d.mts 403.23 KB
22
- DTS dist/index.d.ts 403.23 KB
20
+ DTS ⚡️ Build success in 31341ms
21
+ DTS dist/index.d.mts 403.41 KB
22
+ DTS dist/index.d.ts 403.41 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,48 @@
1
1
  # @objectstack/plugin-sharing
2
2
 
3
+ ## 9.11.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 2365d07: feat(sharing): configurable role-hierarchy widening — `role_and_subordinates` recipient (ADR-0056 D6)
8
+
9
+ Role-hierarchy access widening ("a manager sees records shared with their team") is now
10
+ **implemented and configurable per sharing rule**, not a hardcoded no-op. The
11
+ `role_and_subordinates` recipient (declarable on `sys_sharing_rule.recipient_type`) expands,
12
+ at evaluation time, to the named role **plus every subordinate role** by walking the
13
+ `sys_role.parent` hierarchy via a new `RoleGraphService` (mirroring the department/team
14
+ graphs; cycle-safe). Previously `Role.parent` was declared but never consumed — a silent
15
+ no-op flagged by the ADR-0056 audit. This is the Salesforce "grant access using hierarchies"
16
+ model expressed declaratively: each rule chooses whether to roll up the hierarchy. Unit-proven
17
+ (role-graph traversal, subordinate-user expansion, cycle safety); the recipient is added to
18
+ the authoring select + the `SharingRuleRecipientType` contract.
19
+
20
+ ### Patch Changes
21
+
22
+ - e7f6539: feat(spec,sharing): canonical OWD vocabulary on `object.sharingModel` (ADR-0056 D1)
23
+
24
+ Reconciles the Org-Wide-Default naming so authors use ONE vocabulary. `object.sharingModel`
25
+ now accepts the canonical OWD names — `private` | `public_read` | `public_read_write` |
26
+ `controlled_by_parent` — alongside the legacy `read` / `read_write` / `full` aliases (kept,
27
+ non-breaking). The sharing runtime maps them onto the three enforced behaviours
28
+ (`public_read` ≡ legacy `read` = everyone reads / owner writes; `public_read_write` =
29
+ unscoped). Unknown values remain rejected by the enum (authoring-time, fail-closed). The
30
+ showcase announcement now declares the canonical `public_read`, exercised end-to-end by the
31
+ public-read dogfood proof.
32
+
33
+ - Updated dependencies [e7f6539]
34
+ - Updated dependencies [2365d07]
35
+ - Updated dependencies [6595b53]
36
+ - Updated dependencies [fa8964d]
37
+ - Updated dependencies [36138c7]
38
+ - Updated dependencies [a8e4f3b]
39
+ - Updated dependencies [4c213c2]
40
+ - Updated dependencies [2afb612]
41
+ - @objectstack/spec@9.11.0
42
+ - @objectstack/objectql@9.11.0
43
+ - @objectstack/core@9.11.0
44
+ - @objectstack/platform-objects@9.11.0
45
+
3
46
  ## 9.10.0
4
47
 
5
48
  ### Patch Changes
package/dist/index.d.mts CHANGED
@@ -661,7 +661,7 @@ declare const SysRecordShare: Omit<{
661
661
  clone: boolean;
662
662
  apiMethods?: ("search" | "create" | "import" | "delete" | "list" | "get" | "update" | "upsert" | "bulk" | "aggregate" | "history" | "restore" | "purge" | "export")[] | undefined;
663
663
  } | undefined;
664
- sharingModel?: "full" | "read" | "private" | "read_write" | undefined;
664
+ sharingModel?: "full" | "read" | "private" | "public_read" | "public_read_write" | "controlled_by_parent" | "read_write" | undefined;
665
665
  publicSharing?: {
666
666
  enabled: boolean;
667
667
  allowedAudiences?: ("email" | "public" | "link_only" | "signed_in")[] | undefined;
@@ -3457,7 +3457,7 @@ declare const SysSharingRule: Omit<{
3457
3457
  clone: boolean;
3458
3458
  apiMethods?: ("search" | "create" | "import" | "delete" | "list" | "get" | "update" | "upsert" | "bulk" | "aggregate" | "history" | "restore" | "purge" | "export")[] | undefined;
3459
3459
  } | undefined;
3460
- sharingModel?: "full" | "read" | "private" | "read_write" | undefined;
3460
+ sharingModel?: "full" | "read" | "private" | "public_read" | "public_read_write" | "controlled_by_parent" | "read_write" | undefined;
3461
3461
  publicSharing?: {
3462
3462
  enabled: boolean;
3463
3463
  allowedAudiences?: ("email" | "public" | "link_only" | "signed_in")[] | undefined;
@@ -6383,7 +6383,7 @@ declare const SysShareLink: Omit<{
6383
6383
  clone: boolean;
6384
6384
  apiMethods?: ("search" | "create" | "import" | "delete" | "list" | "get" | "update" | "upsert" | "bulk" | "aggregate" | "history" | "restore" | "purge" | "export")[] | undefined;
6385
6385
  } | undefined;
6386
- sharingModel?: "full" | "read" | "private" | "read_write" | undefined;
6386
+ sharingModel?: "full" | "read" | "private" | "public_read" | "public_read_write" | "controlled_by_parent" | "read_write" | undefined;
6387
6387
  publicSharing?: {
6388
6388
  enabled: boolean;
6389
6389
  allowedAudiences?: ("email" | "public" | "link_only" | "signed_in")[] | undefined;
package/dist/index.d.ts CHANGED
@@ -661,7 +661,7 @@ declare const SysRecordShare: Omit<{
661
661
  clone: boolean;
662
662
  apiMethods?: ("search" | "create" | "import" | "delete" | "list" | "get" | "update" | "upsert" | "bulk" | "aggregate" | "history" | "restore" | "purge" | "export")[] | undefined;
663
663
  } | undefined;
664
- sharingModel?: "full" | "read" | "private" | "read_write" | undefined;
664
+ sharingModel?: "full" | "read" | "private" | "public_read" | "public_read_write" | "controlled_by_parent" | "read_write" | undefined;
665
665
  publicSharing?: {
666
666
  enabled: boolean;
667
667
  allowedAudiences?: ("email" | "public" | "link_only" | "signed_in")[] | undefined;
@@ -3457,7 +3457,7 @@ declare const SysSharingRule: Omit<{
3457
3457
  clone: boolean;
3458
3458
  apiMethods?: ("search" | "create" | "import" | "delete" | "list" | "get" | "update" | "upsert" | "bulk" | "aggregate" | "history" | "restore" | "purge" | "export")[] | undefined;
3459
3459
  } | undefined;
3460
- sharingModel?: "full" | "read" | "private" | "read_write" | undefined;
3460
+ sharingModel?: "full" | "read" | "private" | "public_read" | "public_read_write" | "controlled_by_parent" | "read_write" | undefined;
3461
3461
  publicSharing?: {
3462
3462
  enabled: boolean;
3463
3463
  allowedAudiences?: ("email" | "public" | "link_only" | "signed_in")[] | undefined;
@@ -6383,7 +6383,7 @@ declare const SysShareLink: Omit<{
6383
6383
  clone: boolean;
6384
6384
  apiMethods?: ("search" | "create" | "import" | "delete" | "list" | "get" | "update" | "upsert" | "bulk" | "aggregate" | "history" | "restore" | "purge" | "export")[] | undefined;
6385
6385
  } | undefined;
6386
- sharingModel?: "full" | "read" | "private" | "read_write" | undefined;
6386
+ sharingModel?: "full" | "read" | "private" | "public_read" | "public_read_write" | "controlled_by_parent" | "read_write" | undefined;
6387
6387
  publicSharing?: {
6388
6388
  enabled: boolean;
6389
6389
  allowedAudiences?: ("email" | "public" | "link_only" | "signed_in")[] | undefined;
package/dist/index.js CHANGED
@@ -1448,12 +1448,12 @@ var SysSharingRule = import_data2.ObjectSchema.create({
1448
1448
  group: "Target"
1449
1449
  }),
1450
1450
  recipient_type: import_data2.Field.select(
1451
- ["user", "team", "department", "role", "queue"],
1451
+ ["user", "team", "department", "role", "role_and_subordinates", "queue"],
1452
1452
  {
1453
1453
  label: "Recipient Type",
1454
1454
  required: true,
1455
1455
  defaultValue: "department",
1456
- description: "Kind of principal that receives access \u2014 expanded to user grants at evaluation time. `department` walks the parent_department_id tree; `team` is flat (better-auth).",
1456
+ description: "Kind of principal that receives access \u2014 expanded to user grants at evaluation time. `department` walks the parent_department_id tree; `team` is flat (better-auth); `role` is the role's direct members; `role_and_subordinates` walks the sys_role.parent hierarchy to also include every subordinate role (ADR-0056 D6).",
1457
1457
  group: "Recipient"
1458
1458
  }
1459
1459
  ),
@@ -1711,7 +1711,7 @@ var OWNER_FIELD = "owner_id";
1711
1711
  function effectiveSharingModel(schema) {
1712
1712
  const m = schema?.sharingModel ?? schema?.security?.sharingModel;
1713
1713
  if (m === "private") return "private";
1714
- if (m === "read") return "read";
1714
+ if (m === "read" || m === "public_read") return "read";
1715
1715
  return "public";
1716
1716
  }
1717
1717
  function hasOwnerField(schema) {
@@ -1978,8 +1978,77 @@ async function expandPrincipal(input, ctx) {
1978
1978
  return [`${t}:${v}`];
1979
1979
  }
1980
1980
 
1981
- // src/department-graph.ts
1981
+ // src/role-graph.ts
1982
1982
  var SYSTEM_CTX3 = { isSystem: true, roles: [], permissions: [] };
1983
+ var RoleGraphService = class {
1984
+ constructor(opts) {
1985
+ var _a, _b;
1986
+ this.engine = opts.engine;
1987
+ this.organizationId = opts.organizationId ?? null;
1988
+ this.cache = opts.cache ?? {};
1989
+ (_a = this.cache).descendants ?? (_a.descendants = /* @__PURE__ */ new Map());
1990
+ (_b = this.cache).expand ?? (_b.expand = /* @__PURE__ */ new Map());
1991
+ this.teamGraph = opts.teamGraph ?? new TeamGraphService({ engine: this.engine, organizationId: this.organizationId });
1992
+ }
1993
+ /** Direct child roles of `roleName` (`sys_role.parent === roleName`). */
1994
+ async childRoles(roleName) {
1995
+ const filter = { parent: roleName };
1996
+ if (this.organizationId) filter.organization_id = this.organizationId;
1997
+ let rows = [];
1998
+ try {
1999
+ rows = await this.engine.find("sys_role", {
2000
+ filter,
2001
+ fields: ["name"],
2002
+ limit: 5e3,
2003
+ context: SYSTEM_CTX3
2004
+ });
2005
+ } catch {
2006
+ rows = [];
2007
+ }
2008
+ return Array.from(new Set((rows ?? []).map((r) => String(r.name ?? "")).filter(Boolean)));
2009
+ }
2010
+ /** `roleName` plus every role beneath it in the hierarchy (BFS, cycle-safe). */
2011
+ async descendantRoles(roleName) {
2012
+ if (!roleName) return [];
2013
+ const cached = this.cache.descendants.get(roleName);
2014
+ if (cached) return cached;
2015
+ const out = [];
2016
+ const seen = /* @__PURE__ */ new Set();
2017
+ const queue = [roleName];
2018
+ while (queue.length) {
2019
+ const r = queue.shift();
2020
+ if (seen.has(r)) continue;
2021
+ seen.add(r);
2022
+ out.push(r);
2023
+ for (const child of await this.childRoles(r)) {
2024
+ if (!seen.has(child)) queue.push(child);
2025
+ }
2026
+ }
2027
+ this.cache.descendants.set(roleName, out);
2028
+ return out;
2029
+ }
2030
+ /** Users holding `roleName` OR any subordinate role (the `role_and_subordinates` set). */
2031
+ async expandRoleAndSubordinates(roleName, organizationId) {
2032
+ if (!roleName) return [];
2033
+ const org = organizationId ?? this.organizationId ?? "*";
2034
+ const key = `${org}::${roleName}`;
2035
+ const cached = this.cache.expand.get(key);
2036
+ if (cached) return cached;
2037
+ const roles = await this.descendantRoles(roleName);
2038
+ const users = /* @__PURE__ */ new Set();
2039
+ for (const role of roles) {
2040
+ for (const uid2 of await this.teamGraph.expandRoleUsers(role, organizationId ?? this.organizationId ?? void 0)) {
2041
+ users.add(uid2);
2042
+ }
2043
+ }
2044
+ const result = Array.from(users);
2045
+ this.cache.expand.set(key, result);
2046
+ return result;
2047
+ }
2048
+ };
2049
+
2050
+ // src/department-graph.ts
2051
+ var SYSTEM_CTX4 = { isSystem: true, roles: [], permissions: [] };
1983
2052
  var DepartmentGraphService = class {
1984
2053
  constructor(opts) {
1985
2054
  var _a, _b, _c;
@@ -2001,7 +2070,7 @@ var DepartmentGraphService = class {
2001
2070
  filter: this.orgScope({ id: departmentId }),
2002
2071
  fields: ["id", "active"],
2003
2072
  limit: 1,
2004
- context: SYSTEM_CTX3
2073
+ context: SYSTEM_CTX4
2005
2074
  });
2006
2075
  const seedRow = Array.isArray(seedRows) ? seedRows[0] : null;
2007
2076
  if (!seedRow) seedActive = false;
@@ -2023,7 +2092,7 @@ var DepartmentGraphService = class {
2023
2092
  filter: this.orgScope({ parent_department_id: parent, active: { $ne: false } }),
2024
2093
  fields: ["id"],
2025
2094
  limit: 1e3,
2026
- context: SYSTEM_CTX3
2095
+ context: SYSTEM_CTX4
2027
2096
  });
2028
2097
  } catch {
2029
2098
  children = [];
@@ -2052,7 +2121,7 @@ var DepartmentGraphService = class {
2052
2121
  filter: { department_id: { $in: depts } },
2053
2122
  fields: ["user_id"],
2054
2123
  limit: 1e4,
2055
- context: SYSTEM_CTX3
2124
+ context: SYSTEM_CTX4
2056
2125
  });
2057
2126
  } catch {
2058
2127
  rows = [];
@@ -2072,7 +2141,7 @@ var DepartmentGraphService = class {
2072
2141
  filter: { id: departmentId },
2073
2142
  fields: ["id", "manager_user_id"],
2074
2143
  limit: 1,
2075
- context: SYSTEM_CTX3
2144
+ context: SYSTEM_CTX4
2076
2145
  });
2077
2146
  row = Array.isArray(rows) ? rows[0] : null;
2078
2147
  } catch {
@@ -2090,7 +2159,7 @@ var DepartmentGraphService = class {
2090
2159
  filter: { id: userId },
2091
2160
  fields: ["id", "manager_id"],
2092
2161
  limit: 1,
2093
- context: SYSTEM_CTX3
2162
+ context: SYSTEM_CTX4
2094
2163
  });
2095
2164
  const row = Array.isArray(rows) ? rows[0] : null;
2096
2165
  return row?.manager_id ? String(row.manager_id) : null;
@@ -2105,7 +2174,7 @@ var DepartmentGraphService = class {
2105
2174
  };
2106
2175
 
2107
2176
  // src/sharing-rule-service.ts
2108
- var SYSTEM_CTX4 = { isSystem: true, roles: [], permissions: [] };
2177
+ var SYSTEM_CTX5 = { isSystem: true, roles: [], permissions: [] };
2109
2178
  function uid(prefix) {
2110
2179
  const g = globalThis;
2111
2180
  if (g.crypto?.randomUUID) return `${prefix}_${g.crypto.randomUUID()}`;
@@ -2161,7 +2230,7 @@ var SharingRuleService = class {
2161
2230
  const existing = await this.engine.find("sys_sharing_rule", {
2162
2231
  filter: orgId ? { name: input.name, organization_id: orgId } : { name: input.name },
2163
2232
  limit: 1,
2164
- context: SYSTEM_CTX4
2233
+ context: SYSTEM_CTX5
2165
2234
  });
2166
2235
  if (Array.isArray(existing) && existing[0]) {
2167
2236
  const row = existing[0];
@@ -2177,7 +2246,7 @@ var SharingRuleService = class {
2177
2246
  active,
2178
2247
  updated_at: now
2179
2248
  };
2180
- await this.engine.update("sys_sharing_rule", patch, { context: SYSTEM_CTX4 });
2249
+ await this.engine.update("sys_sharing_rule", patch, { context: SYSTEM_CTX5 });
2181
2250
  return rowFromRule({ ...row, ...patch });
2182
2251
  }
2183
2252
  const newRow = {
@@ -2195,7 +2264,7 @@ var SharingRuleService = class {
2195
2264
  created_at: now,
2196
2265
  updated_at: now
2197
2266
  };
2198
- await this.engine.insert("sys_sharing_rule", newRow, { context: SYSTEM_CTX4 });
2267
+ await this.engine.insert("sys_sharing_rule", newRow, { context: SYSTEM_CTX5 });
2199
2268
  return rowFromRule(newRow);
2200
2269
  }
2201
2270
  async listRules(filter, context) {
@@ -2208,7 +2277,7 @@ var SharingRuleService = class {
2208
2277
  filter: where,
2209
2278
  orderBy: [{ field: "name", order: "asc" }],
2210
2279
  limit: 1e3,
2211
- context: SYSTEM_CTX4
2280
+ context: SYSTEM_CTX5
2212
2281
  });
2213
2282
  return Array.isArray(rows) ? rows.map(rowFromRule) : [];
2214
2283
  }
@@ -2218,13 +2287,13 @@ var SharingRuleService = class {
2218
2287
  const byId = await this.engine.find("sys_sharing_rule", {
2219
2288
  filter: { id: idOrName },
2220
2289
  limit: 1,
2221
- context: SYSTEM_CTX4
2290
+ context: SYSTEM_CTX5
2222
2291
  });
2223
2292
  if (Array.isArray(byId) && byId[0]) return rowFromRule(byId[0]);
2224
2293
  const byName = await this.engine.find("sys_sharing_rule", {
2225
2294
  filter: orgId ? { name: idOrName, organization_id: orgId } : { name: idOrName },
2226
2295
  limit: 1,
2227
- context: SYSTEM_CTX4
2296
+ context: SYSTEM_CTX5
2228
2297
  });
2229
2298
  if (Array.isArray(byName) && byName[0]) return rowFromRule(byName[0]);
2230
2299
  return null;
@@ -2234,11 +2303,11 @@ var SharingRuleService = class {
2234
2303
  if (!row) return;
2235
2304
  await this.engine.delete("sys_record_share", {
2236
2305
  where: { source: "rule", source_id: row.id },
2237
- context: SYSTEM_CTX4
2306
+ context: SYSTEM_CTX5
2238
2307
  });
2239
2308
  await this.engine.delete("sys_sharing_rule", {
2240
2309
  where: { id: row.id },
2241
- context: SYSTEM_CTX4
2310
+ context: SYSTEM_CTX5
2242
2311
  });
2243
2312
  }
2244
2313
  async evaluateRule(idOrName, context) {
@@ -2271,7 +2340,7 @@ var SharingRuleService = class {
2271
2340
  filter,
2272
2341
  fields: ["id"],
2273
2342
  limit: 5e3,
2274
- context: SYSTEM_CTX4
2343
+ context: SYSTEM_CTX5
2275
2344
  });
2276
2345
  return Array.isArray(rows) ? rows.map((r) => String(r.id)).filter(Boolean) : [];
2277
2346
  } catch (err) {
@@ -2286,7 +2355,7 @@ var SharingRuleService = class {
2286
2355
  filter,
2287
2356
  fields: ["id"],
2288
2357
  limit: 1,
2289
- context: SYSTEM_CTX4
2358
+ context: SYSTEM_CTX5
2290
2359
  });
2291
2360
  return Array.isArray(rows) && rows.length > 0;
2292
2361
  } catch {
@@ -2309,6 +2378,14 @@ var SharingRuleService = class {
2309
2378
  return dept.expandUsers(rule.recipient_id);
2310
2379
  }
2311
2380
  if (rule.recipient_type === "role") return team.expandRoleUsers(rule.recipient_id, rule.organization_id ?? void 0);
2381
+ if (rule.recipient_type === "role_and_subordinates") {
2382
+ const roleGraph = new RoleGraphService({
2383
+ engine: this.engine,
2384
+ organizationId: rule.organization_id ?? null,
2385
+ teamGraph: team
2386
+ });
2387
+ return roleGraph.expandRoleAndSubordinates(rule.recipient_id, rule.organization_id ?? void 0);
2388
+ }
2312
2389
  return [];
2313
2390
  }
2314
2391
  async reconcile(rule, matchedIds, users) {
@@ -2316,7 +2393,7 @@ var SharingRuleService = class {
2316
2393
  filter: { source: "rule", source_id: rule.id },
2317
2394
  fields: ["id", "record_id", "recipient_id", "access_level"],
2318
2395
  limit: 1e5,
2319
- context: SYSTEM_CTX4
2396
+ context: SYSTEM_CTX5
2320
2397
  });
2321
2398
  const desired = /* @__PURE__ */ new Map();
2322
2399
  for (const rid of matchedIds) {
@@ -2342,7 +2419,7 @@ var SharingRuleService = class {
2342
2419
  sourceId: rule.id,
2343
2420
  reason: `rule:${rule.name}`
2344
2421
  },
2345
- SYSTEM_CTX4
2422
+ SYSTEM_CTX5
2346
2423
  );
2347
2424
  updated += 1;
2348
2425
  }
@@ -2359,13 +2436,13 @@ var SharingRuleService = class {
2359
2436
  sourceId: rule.id,
2360
2437
  reason: `rule:${rule.name}`
2361
2438
  },
2362
- SYSTEM_CTX4
2439
+ SYSTEM_CTX5
2363
2440
  );
2364
2441
  created += 1;
2365
2442
  }
2366
2443
  }
2367
2444
  for (const [, stale] of existingMap.entries()) {
2368
- await this.sharing.revoke(stale.id, SYSTEM_CTX4);
2445
+ await this.sharing.revoke(stale.id, SYSTEM_CTX5);
2369
2446
  revoked += 1;
2370
2447
  }
2371
2448
  return {
@@ -2382,7 +2459,7 @@ var SharingRuleService = class {
2382
2459
  filter: { source: "rule", source_id: rule.id, record_id: recordId },
2383
2460
  fields: ["id", "record_id", "recipient_id", "access_level"],
2384
2461
  limit: 1e3,
2385
- context: SYSTEM_CTX4
2462
+ context: SYSTEM_CTX5
2386
2463
  });
2387
2464
  const existingMap = /* @__PURE__ */ new Map();
2388
2465
  for (const row of existing ?? []) existingMap.set(String(row.recipient_id), row);
@@ -2405,7 +2482,7 @@ var SharingRuleService = class {
2405
2482
  sourceId: rule.id,
2406
2483
  reason: `rule:${rule.name}`
2407
2484
  },
2408
- SYSTEM_CTX4
2485
+ SYSTEM_CTX5
2409
2486
  );
2410
2487
  updated += 1;
2411
2488
  }
@@ -2422,14 +2499,14 @@ var SharingRuleService = class {
2422
2499
  sourceId: rule.id,
2423
2500
  reason: `rule:${rule.name}`
2424
2501
  },
2425
- SYSTEM_CTX4
2502
+ SYSTEM_CTX5
2426
2503
  );
2427
2504
  created += 1;
2428
2505
  }
2429
2506
  }
2430
2507
  }
2431
2508
  for (const [, stale] of existingMap.entries()) {
2432
- await this.sharing.revoke(stale.id, SYSTEM_CTX4);
2509
+ await this.sharing.revoke(stale.id, SYSTEM_CTX5);
2433
2510
  revoked += 1;
2434
2511
  }
2435
2512
  return {
@@ -2446,11 +2523,11 @@ var SharingRuleService = class {
2446
2523
  filter: { source: "rule", source_id: ruleId },
2447
2524
  fields: ["id"],
2448
2525
  limit: 1e5,
2449
- context: SYSTEM_CTX4
2526
+ context: SYSTEM_CTX5
2450
2527
  });
2451
2528
  let revoked = 0;
2452
2529
  for (const row of existing ?? []) {
2453
- await this.sharing.revoke(row.id, SYSTEM_CTX4);
2530
+ await this.sharing.revoke(row.id, SYSTEM_CTX5);
2454
2531
  revoked += 1;
2455
2532
  }
2456
2533
  return revoked;
@@ -2458,7 +2535,7 @@ var SharingRuleService = class {
2458
2535
  };
2459
2536
 
2460
2537
  // src/share-link-service.ts
2461
- var SYSTEM_CTX5 = { isSystem: true, roles: [], permissions: [] };
2538
+ var SYSTEM_CTX6 = { isSystem: true, roles: [], permissions: [] };
2462
2539
  var TOKEN_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
2463
2540
  var TOKEN_LENGTH = 24;
2464
2541
  var DEFAULT_MAX_EXPIRY_DAYS = 365;
@@ -2598,7 +2675,7 @@ var ShareLinkService = class {
2598
2675
  where: { id: input.recordId },
2599
2676
  fields: ["id"],
2600
2677
  limit: 1,
2601
- context: SYSTEM_CTX5
2678
+ context: SYSTEM_CTX6
2602
2679
  });
2603
2680
  if (!Array.isArray(exists) || exists.length === 0) {
2604
2681
  throw makeError(404, "RECORD_NOT_FOUND", `${input.object}/${input.recordId} does not exist`);
@@ -2624,7 +2701,7 @@ var ShareLinkService = class {
2624
2701
  last_used_at: null,
2625
2702
  use_count: 0
2626
2703
  };
2627
- await this.engine.insert("sys_share_link", row, { context: SYSTEM_CTX5 });
2704
+ await this.engine.insert("sys_share_link", row, { context: SYSTEM_CTX6 });
2628
2705
  return row;
2629
2706
  }
2630
2707
  async revokeLink(idOrToken, _context) {
@@ -2634,7 +2711,7 @@ var ShareLinkService = class {
2634
2711
  where: filter,
2635
2712
  fields: ["id", "revoked_at"],
2636
2713
  limit: 1,
2637
- context: SYSTEM_CTX5
2714
+ context: SYSTEM_CTX6
2638
2715
  });
2639
2716
  const row = Array.isArray(rows) ? rows[0] : void 0;
2640
2717
  if (!row) return;
@@ -2642,7 +2719,7 @@ var ShareLinkService = class {
2642
2719
  await this.engine.update(
2643
2720
  "sys_share_link",
2644
2721
  { id: row.id, revoked_at: (/* @__PURE__ */ new Date()).toISOString() },
2645
- { context: SYSTEM_CTX5 }
2722
+ { context: SYSTEM_CTX6 }
2646
2723
  );
2647
2724
  }
2648
2725
  async listLinks(filter, context) {
@@ -2655,7 +2732,7 @@ var ShareLinkService = class {
2655
2732
  where,
2656
2733
  limit: 200,
2657
2734
  sort: [{ field: "created_at", order: "desc" }],
2658
- context: context.isSystem ? SYSTEM_CTX5 : context
2735
+ context: context.isSystem ? SYSTEM_CTX6 : context
2659
2736
  });
2660
2737
  return Array.isArray(rows) ? rows : [];
2661
2738
  }
@@ -2664,7 +2741,7 @@ var ShareLinkService = class {
2664
2741
  const rows = await this.engine.find("sys_share_link", {
2665
2742
  where: { token },
2666
2743
  limit: 1,
2667
- context: SYSTEM_CTX5
2744
+ context: SYSTEM_CTX6
2668
2745
  });
2669
2746
  const row = Array.isArray(rows) ? rows[0] : void 0;
2670
2747
  if (!row) return null;
@@ -2694,7 +2771,7 @@ var ShareLinkService = class {
2694
2771
  last_used_at: (/* @__PURE__ */ new Date()).toISOString(),
2695
2772
  use_count: (row.use_count ?? 0) + 1
2696
2773
  },
2697
- { context: SYSTEM_CTX5 }
2774
+ { context: SYSTEM_CTX6 }
2698
2775
  );
2699
2776
  } catch {
2700
2777
  }
@@ -2703,7 +2780,7 @@ var ShareLinkService = class {
2703
2780
  };
2704
2781
 
2705
2782
  // src/share-link-routes.ts
2706
- var SYSTEM_CTX6 = { isSystem: true, roles: [], permissions: [] };
2783
+ var SYSTEM_CTX7 = { isSystem: true, roles: [], permissions: [] };
2707
2784
  var defaultContext = (req) => {
2708
2785
  const header = (name) => {
2709
2786
  const v = req.headers?.[name];
@@ -2804,7 +2881,7 @@ function registerShareLinkRoutes(http, service, engine, opts = {}) {
2804
2881
  const probe = await engine.find("sys_share_link", {
2805
2882
  where: { token: req.params.token },
2806
2883
  limit: 1,
2807
- context: SYSTEM_CTX6
2884
+ context: SYSTEM_CTX7
2808
2885
  });
2809
2886
  const row = Array.isArray(probe) && probe[0] ? probe[0] : null;
2810
2887
  if (row && !row.revoked_at && (!row.expires_at || Date.parse(row.expires_at) > Date.now())) {
@@ -2828,7 +2905,7 @@ function registerShareLinkRoutes(http, service, engine, opts = {}) {
2828
2905
  const rows = await engine.find(resolved.link.object_name, {
2829
2906
  where: { id: resolved.link.record_id },
2830
2907
  limit: 1,
2831
- context: SYSTEM_CTX6
2908
+ context: SYSTEM_CTX7
2832
2909
  });
2833
2910
  const record = Array.isArray(rows) && rows[0] ? rows[0] : null;
2834
2911
  if (!record) {
@@ -2865,12 +2942,12 @@ function registerShareLinkRoutes(http, service, engine, opts = {}) {
2865
2942
  sendError(res, 400, "UNSUPPORTED", "This share link does not expose messages");
2866
2943
  return;
2867
2944
  }
2868
- const SYSTEM_CTX8 = { isSystem: true, roles: [], permissions: [] };
2945
+ const SYSTEM_CTX9 = { isSystem: true, roles: [], permissions: [] };
2869
2946
  const rows = await engine.find("ai_messages", {
2870
2947
  where: { conversation_id: resolved.link.record_id },
2871
2948
  sort: [{ field: "created_at", order: "asc" }],
2872
2949
  limit: 500,
2873
- context: SYSTEM_CTX8
2950
+ context: SYSTEM_CTX9
2874
2951
  });
2875
2952
  res.status(200).json({ data: rows ?? [] });
2876
2953
  } catch (err) {
@@ -2885,7 +2962,7 @@ function registerShareLinkRoutes(http, service, engine, opts = {}) {
2885
2962
  }
2886
2963
 
2887
2964
  // src/rule-hooks.ts
2888
- var SYSTEM_CTX7 = { isSystem: true, roles: [], permissions: [] };
2965
+ var SYSTEM_CTX8 = { isSystem: true, roles: [], permissions: [] };
2889
2966
  var SHARING_RULE_HOOK_PACKAGE = "plugin-sharing:rules";
2890
2967
  function bindRuleHooks(engine, service, rules, logger) {
2891
2968
  const objects = /* @__PURE__ */ new Set();
@@ -2900,7 +2977,7 @@ function bindRuleHooks(engine, service, rules, logger) {
2900
2977
  const data = ctx?.result ?? ctx?.input?.data ?? {};
2901
2978
  const id = String(data?.id ?? ctx?.input?.id ?? "");
2902
2979
  if (!id) return;
2903
- await service.evaluateAllForRecord(objectName, id, SYSTEM_CTX7);
2980
+ await service.evaluateAllForRecord(objectName, id, SYSTEM_CTX8);
2904
2981
  } catch (err) {
2905
2982
  logger?.warn?.("[sharing-rule] hook evaluation failed", { object: objectName, error: err?.message });
2906
2983
  }