@rpcbase/db 0.26.0 → 0.27.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.
@@ -0,0 +1,3 @@
1
+ import { AclAction, AclSubjectType, AppAbility } from './types';
2
+ export declare const getAccessibleByQuery: (ability: AppAbility, action: AclAction, subject: Exclude<AclSubjectType, "all">) => Record<string, unknown>;
3
+ //# sourceMappingURL=accessible.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accessible.d.ts","sourceRoot":"","sources":["../../src/acl/accessible.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAGpE,eAAO,MAAM,oBAAoB,GAC/B,SAAS,UAAU,EACnB,QAAQ,SAAS,EACjB,SAAS,OAAO,CAAC,cAAc,EAAE,KAAK,CAAC,KACtC,MAAM,CAAC,MAAM,EAAE,OAAO,CAExB,CAAA"}
@@ -0,0 +1,3 @@
1
+ import { AclContext, AppAbility } from './types';
2
+ export declare const buildAbility: (ctx: AclContext) => AppAbility;
3
+ //# sourceMappingURL=buildAbility.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildAbility.d.ts","sourceRoot":"","sources":["../../src/acl/buildAbility.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAGrD,eAAO,MAAM,YAAY,GAAI,KAAK,UAAU,KAAG,UAc9C,CAAA"}
@@ -0,0 +1,3 @@
1
+ import { AclAction, AclSubjectMap, AclSubjectType, AppAbility } from './types';
2
+ export declare const can: <TSubject extends Exclude<AclSubjectType, "all">>(ability: AppAbility, action: AclAction, subjectType: TSubject, object?: Partial<AclSubjectMap[TSubject]> | null) => boolean;
3
+ //# sourceMappingURL=can.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"can.d.ts","sourceRoot":"","sources":["../../src/acl/can.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAc,aAAa,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAG/F,eAAO,MAAM,GAAG,GAAI,QAAQ,SAAS,OAAO,CAAC,cAAc,EAAE,KAAK,CAAC,EACjE,SAAS,UAAU,EACnB,QAAQ,SAAS,EACjB,aAAa,QAAQ,EACrB,SAAS,OAAO,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,GAAG,IAAI,KAC/C,OAKF,CAAA"}
@@ -0,0 +1,7 @@
1
+ export * from './types';
2
+ export * from './registry';
3
+ export * from './buildAbility';
4
+ export * from './session';
5
+ export * from './accessible';
6
+ export * from './can';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/acl/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAA;AACvB,cAAc,YAAY,CAAA;AAC1B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,WAAW,CAAA;AACzB,cAAc,cAAc,CAAA;AAC5B,cAAc,OAAO,CAAA"}
@@ -0,0 +1,10 @@
1
+ import { AbilityBuilder } from '@casl/ability';
2
+ import { AclContext, AclSubjectType, AppAbility } from './types';
3
+ export type AclPolicy = {
4
+ subject: Exclude<AclSubjectType, "all">;
5
+ define: (builder: AbilityBuilder<AppAbility>, ctx: AclContext) => void;
6
+ };
7
+ export declare const registerPolicy: (policy: AclPolicy) => void;
8
+ export declare const registerPoliciesFromModules: (modules: Record<string, unknown>) => void;
9
+ export declare const getRegisteredPolicies: () => Array<AclPolicy["define"]>;
10
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/acl/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAEnD,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAGrE,MAAM,MAAM,SAAS,GAAG;IACtB,OAAO,EAAE,OAAO,CAAC,cAAc,EAAE,KAAK,CAAC,CAAA;IACvC,MAAM,EAAE,CAAC,OAAO,EAAE,cAAc,CAAC,UAAU,CAAC,EAAE,GAAG,EAAE,UAAU,KAAK,IAAI,CAAA;CACvE,CAAA;AAuBD,eAAO,MAAM,cAAc,GAAI,QAAQ,SAAS,KAAG,IAIlD,CAAA;AAED,eAAO,MAAM,2BAA2B,GAAI,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,IAmB9E,CAAA;AAED,eAAO,MAAM,qBAAqB,QAAO,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,CAMjE,CAAA"}
@@ -0,0 +1,17 @@
1
+ import { AppAbility } from './types';
2
+ type SessionUserLike = {
3
+ id?: unknown;
4
+ signedInTenants?: unknown;
5
+ tenantRoles?: unknown;
6
+ };
7
+ type SessionLike = {
8
+ user?: SessionUserLike;
9
+ };
10
+ export declare const getTenantRolesFromSessionUser: (user: unknown, tenantId: string) => string[];
11
+ export declare const buildAbilityFromSession: ({ tenantId, session, claims, }: {
12
+ tenantId: string;
13
+ session: SessionLike | null | undefined;
14
+ claims?: Record<string, unknown>;
15
+ }) => AppAbility;
16
+ export {};
17
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/acl/session.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAyB,MAAM,SAAS,CAAA;AAGhE,KAAK,eAAe,GAAG;IACrB,EAAE,CAAC,EAAE,OAAO,CAAA;IACZ,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB,CAAA;AAED,KAAK,WAAW,GAAG;IACjB,IAAI,CAAC,EAAE,eAAe,CAAA;CACvB,CAAA;AA+BD,eAAO,MAAM,6BAA6B,GAAI,MAAM,OAAO,EAAE,UAAU,MAAM,KAAG,MAAM,EAKrF,CAAA;AAED,eAAO,MAAM,uBAAuB,GAAI,gCAIrC;IACD,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,CAAA;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACjC,KAAG,UAuBH,CAAA"}
@@ -0,0 +1,22 @@
1
+ import { ForcedSubject, MongoAbility } from '@casl/ability';
2
+ export type AclAction = "manage" | "create" | "read" | "update" | "delete";
3
+ export interface AclSubjectMap {
4
+ RBUploadSession: Record<string, unknown>;
5
+ RBNotification: Record<string, unknown>;
6
+ RBNotificationSettings: Record<string, unknown>;
7
+ }
8
+ export type AclSubjectType = keyof AclSubjectMap | "all";
9
+ type AclSubjectObject = {
10
+ [K in keyof AclSubjectMap]: ForcedSubject<K> & Partial<AclSubjectMap[K]>;
11
+ }[keyof AclSubjectMap];
12
+ export type AclSubject = AclSubjectType | AclSubjectObject;
13
+ export type AppAbility = MongoAbility<[AclAction, AclSubject]>;
14
+ export type TenantRolesByTenantId = Record<string, string[]>;
15
+ export type AclContext = {
16
+ tenantId: string;
17
+ userId: string | null;
18
+ roles: string[];
19
+ claims?: Record<string, unknown>;
20
+ };
21
+ export {};
22
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/acl/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAGhE,MAAM,MAAM,SAAS,GACjB,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,QAAQ,GACR,QAAQ,CAAA;AAEZ,MAAM,WAAW,aAAa;IAC5B,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACxC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACvC,sBAAsB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAChD;AAED,MAAM,MAAM,cAAc,GAAG,MAAM,aAAa,GAAG,KAAK,CAAA;AAExD,KAAK,gBAAgB,GAAG;KACrB,CAAC,IAAI,MAAM,aAAa,GAAG,aAAa,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;CACzE,CAAC,MAAM,aAAa,CAAC,CAAA;AAEtB,MAAM,MAAM,UAAU,GAAG,cAAc,GAAG,gBAAgB,CAAA;AAE1D,MAAM,MAAM,UAAU,GAAG,YAAY,CAAC,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAA;AAE9D,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;AAE5D,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACjC,CAAA"}
package/dist/acl.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './acl/index';
2
+ //# sourceMappingURL=acl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acl.d.ts","sourceRoot":"","sources":["../src/acl.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA"}
package/dist/index.d.ts CHANGED
@@ -3,4 +3,5 @@ export * from './schema';
3
3
  export * from './createModels';
4
4
  export * from './loadModel';
5
5
  export * from './tenantFilesystemDb';
6
+ export * from './acl';
6
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAA;AACxB,cAAc,UAAU,CAAA;AACxB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,sBAAsB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAA;AACxB,cAAc,UAAU,CAAA;AACxB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,sBAAsB,CAAA;AACpC,cAAc,OAAO,CAAA"}
package/dist/index.js CHANGED
@@ -3,12 +3,15 @@ import { z, ZodMap, ZodRecord, ZodAny, ZodUnion, ZodNullable, ZodOptional, ZodDe
3
3
  export * from "zod";
4
4
  import { z as z2 } from "zod";
5
5
  import assert from "assert";
6
+ import { accessibleBy, accessibleRecordsPlugin } from "@casl/mongoose";
7
+ import { AbilityBuilder, createMongoAbility, subject } from "@casl/ability";
6
8
  const ZRBUser = z.object({
7
9
  email: z.string().email().optional(),
8
10
  password: z.string(),
9
11
  name: z.string().optional(),
10
12
  phone: z.string().optional(),
11
13
  tenants: z.array(z.string()),
14
+ tenantRoles: z.record(z.string(), z.array(z.string())).optional(),
12
15
  emailVerificationCode: z.string().length(6).optional(),
13
16
  emailVerificationExpiresAt: z.date().optional()
14
17
  });
@@ -18,6 +21,7 @@ const RBUserSchema = new Schema({
18
21
  password: { type: String, required: true },
19
22
  name: String,
20
23
  tenants: { type: [String], index: true, required: true },
24
+ tenantRoles: { type: Map, of: [String], required: false, default: {} },
21
25
  emailVerificationCode: { type: String, required: false },
22
26
  emailVerificationExpiresAt: { type: Date, required: false }
23
27
  }, { collection: "users" });
@@ -244,6 +248,23 @@ const RBUploadSessionSchema = new Schema(
244
248
  }
245
249
  );
246
250
  RBUploadSessionSchema.index({ expiresAt: 1 }, { expireAfterSeconds: 0 });
251
+ const RBUploadSessionPolicy = {
252
+ subject: "RBUploadSession",
253
+ define: (builder, ctx) => {
254
+ builder.can("create", "RBUploadSession");
255
+ if (ctx.userId) {
256
+ builder.can("read", "RBUploadSession", { userId: ctx.userId });
257
+ builder.can("update", "RBUploadSession", { userId: ctx.userId });
258
+ builder.can("delete", "RBUploadSession", { userId: ctx.userId });
259
+ }
260
+ const uploadKeyHash = typeof ctx.claims?.uploadKeyHash === "string" ? ctx.claims.uploadKeyHash.trim() : "";
261
+ if (uploadKeyHash) {
262
+ builder.can("read", "RBUploadSession", { ownerKeyHash: uploadKeyHash });
263
+ builder.can("update", "RBUploadSession", { ownerKeyHash: uploadKeyHash });
264
+ builder.can("delete", "RBUploadSession", { ownerKeyHash: uploadKeyHash });
265
+ }
266
+ }
267
+ };
247
268
  const ZRBUploadChunk = z.object({
248
269
  uploadId: z.string(),
249
270
  index: z.number().int().min(0),
@@ -305,6 +326,16 @@ RBNotificationSchema.index({ userId: 1, archivedAt: 1, createdAt: -1 });
305
326
  RBNotificationSchema.index({ userId: 1, seenAt: 1, archivedAt: 1, createdAt: -1 });
306
327
  RBNotificationSchema.index({ userId: 1, readAt: 1, archivedAt: 1, createdAt: -1 });
307
328
  RBNotificationSchema.index({ archivedAt: 1 }, { expireAfterSeconds: TTL_90_DAYS_S });
329
+ const RBNotificationPolicy = {
330
+ subject: "RBNotification",
331
+ define: (builder, ctx) => {
332
+ if (!ctx.userId) return;
333
+ builder.can("create", "RBNotification");
334
+ builder.can("read", "RBNotification", { userId: ctx.userId });
335
+ builder.can("update", "RBNotification", { userId: ctx.userId });
336
+ builder.can("delete", "RBNotification", { userId: ctx.userId });
337
+ }
338
+ };
308
339
  const ZRBNotificationDigestFrequency = z.enum(["off", "daily", "weekly"]);
309
340
  const ZRBNotificationTopicPreference = z.object({
310
341
  topic: z.string(),
@@ -350,9 +381,20 @@ const RBNotificationSettingsSchema = new Schema(
350
381
  );
351
382
  RBNotificationSettingsSchema.index({ userId: 1 }, { unique: true });
352
383
  RBNotificationSettingsSchema.index({ userId: 1, "topicPreferences.topic": 1 });
384
+ const RBNotificationSettingsPolicy = {
385
+ subject: "RBNotificationSettings",
386
+ define: (builder, ctx) => {
387
+ if (!ctx.userId) return;
388
+ builder.can("create", "RBNotificationSettings");
389
+ builder.can("read", "RBNotificationSettings", { userId: ctx.userId });
390
+ builder.can("update", "RBNotificationSettings", { userId: ctx.userId });
391
+ }
392
+ };
353
393
  const frameworkSchemas = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
354
394
  __proto__: null,
395
+ RBNotificationPolicy,
355
396
  RBNotificationSchema,
397
+ RBNotificationSettingsPolicy,
356
398
  RBNotificationSettingsSchema,
357
399
  RBRtsChangeSchema,
358
400
  RBRtsCounterSchema,
@@ -360,6 +402,7 @@ const frameworkSchemas = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.de
360
402
  RBTenantSubscriptionEventSchema,
361
403
  RBTenantSubscriptionSchema,
362
404
  RBUploadChunkSchema,
405
+ RBUploadSessionPolicy,
363
406
  RBUploadSessionSchema,
364
407
  RBUserSchema,
365
408
  ZRBNotification,
@@ -1262,8 +1305,128 @@ const rtsChangeLogPlugin = (schema) => {
1262
1305
  }
1263
1306
  });
1264
1307
  };
1308
+ const POLICIES_GLOBAL_KEY = /* @__PURE__ */ Symbol.for("@rpcbase/db/acl/policiesBySubject");
1309
+ const getGlobalPoliciesMap = () => {
1310
+ const store = globalThis;
1311
+ const existing = store[POLICIES_GLOBAL_KEY];
1312
+ if (existing instanceof Map) {
1313
+ return existing;
1314
+ }
1315
+ const created = /* @__PURE__ */ new Map();
1316
+ store[POLICIES_GLOBAL_KEY] = created;
1317
+ return created;
1318
+ };
1319
+ const policiesBySubject = getGlobalPoliciesMap();
1320
+ const isPolicy = (value) => {
1321
+ if (!value || typeof value !== "object") return false;
1322
+ const maybe = value;
1323
+ return typeof maybe.subject === "string" && typeof maybe.define === "function";
1324
+ };
1325
+ const registerPolicy = (policy) => {
1326
+ const set = policiesBySubject.get(policy.subject) ?? /* @__PURE__ */ new Set();
1327
+ set.add(policy.define);
1328
+ policiesBySubject.set(policy.subject, set);
1329
+ };
1330
+ const registerPoliciesFromModules = (modules) => {
1331
+ for (const [exportName, value] of Object.entries(modules)) {
1332
+ if (!isPolicy(value)) continue;
1333
+ if (!exportName.endsWith("Policy")) {
1334
+ throw new Error(
1335
+ `Invalid policy export name "${exportName}". Policies must be exported as "<ModelName>Policy".`
1336
+ );
1337
+ }
1338
+ const expectedSubject = exportName.slice(0, -"Policy".length);
1339
+ if (value.subject !== expectedSubject) {
1340
+ throw new Error(
1341
+ `Invalid policy "${exportName}": expected subject "${expectedSubject}", got "${value.subject}".`
1342
+ );
1343
+ }
1344
+ registerPolicy(value);
1345
+ }
1346
+ };
1347
+ const getRegisteredPolicies = () => {
1348
+ const out = [];
1349
+ for (const set of policiesBySubject.values()) {
1350
+ for (const fn of set) out.push(fn);
1351
+ }
1352
+ return out;
1353
+ };
1354
+ const buildAbility = (ctx) => {
1355
+ const builder = new AbilityBuilder(createMongoAbility);
1356
+ const { can: can2 } = builder;
1357
+ if (ctx.roles.includes("owner") || ctx.roles.includes("admin")) {
1358
+ can2("manage", "all");
1359
+ }
1360
+ for (const define of getRegisteredPolicies()) {
1361
+ define(builder, ctx);
1362
+ }
1363
+ return builder.build();
1364
+ };
1365
+ const normalizeRole = (raw) => {
1366
+ if (typeof raw !== "string") return null;
1367
+ const normalized = raw.trim();
1368
+ return normalized ? normalized : null;
1369
+ };
1370
+ const normalizeRoles = (raw) => {
1371
+ if (Array.isArray(raw)) {
1372
+ return raw.map(normalizeRole).filter((r) => Boolean(r));
1373
+ }
1374
+ const role = normalizeRole(raw);
1375
+ return role ? [role] : [];
1376
+ };
1377
+ const normalizeRolesByTenantId = (raw) => {
1378
+ if (!raw || typeof raw !== "object") return null;
1379
+ if (raw instanceof Map) {
1380
+ return Object.fromEntries(Array.from(raw.entries()).map(([key, value]) => [String(key), normalizeRoles(value)]));
1381
+ }
1382
+ const obj = raw;
1383
+ const out = {};
1384
+ for (const [key, value] of Object.entries(obj)) {
1385
+ const tenantId = String(key).trim();
1386
+ if (!tenantId) continue;
1387
+ out[tenantId] = normalizeRoles(value);
1388
+ }
1389
+ return out;
1390
+ };
1391
+ const getTenantRolesFromSessionUser = (user, tenantId) => {
1392
+ if (!user || typeof user !== "object") return [];
1393
+ const rolesByTenantId = normalizeRolesByTenantId(user.tenantRoles);
1394
+ if (!rolesByTenantId) return [];
1395
+ return rolesByTenantId[tenantId]?.filter(Boolean) ?? [];
1396
+ };
1397
+ const buildAbilityFromSession = ({
1398
+ tenantId,
1399
+ session,
1400
+ claims
1401
+ }) => {
1402
+ const user = session?.user;
1403
+ const userId = typeof user?.id === "string" ? user.id.trim() : "";
1404
+ const rolesByTenantId = normalizeRolesByTenantId(user?.tenantRoles);
1405
+ const hasExplicitTenantEntry = Boolean(
1406
+ rolesByTenantId && Object.prototype.hasOwnProperty.call(rolesByTenantId, tenantId)
1407
+ );
1408
+ const signedInTenantsRaw = user?.signedInTenants;
1409
+ const signedInTenants = Array.isArray(signedInTenantsRaw) ? signedInTenantsRaw.map(String) : [];
1410
+ const roles = hasExplicitTenantEntry ? rolesByTenantId[tenantId] ?? [] : userId && signedInTenants.includes(tenantId) ? ["owner"] : getTenantRolesFromSessionUser(user, tenantId);
1411
+ return buildAbility({
1412
+ tenantId,
1413
+ userId: userId || null,
1414
+ roles,
1415
+ claims
1416
+ });
1417
+ };
1418
+ const getAccessibleByQuery = (ability, action, subject2) => {
1419
+ return accessibleBy(ability, action).ofType(subject2);
1420
+ };
1421
+ const can = (ability, action, subjectType, object) => {
1422
+ if (object && typeof object === "object") {
1423
+ return ability.can(action, subject(subjectType, object));
1424
+ }
1425
+ return ability.can(action, subjectType);
1426
+ };
1265
1427
  const RTS_CHANGELOG_EXCLUDED_MODELS = /* @__PURE__ */ new Set(["RBRtsChange", "RBRtsCounter"]);
1266
1428
  const rtsChangeLogApplied = /* @__PURE__ */ new WeakSet();
1429
+ const accessibleRecordsApplied = /* @__PURE__ */ new WeakSet();
1267
1430
  let cachedModels = null;
1268
1431
  const isRtsChangelogExcludedModelName = (name) => name.startsWith("RB") || RTS_CHANGELOG_EXCLUDED_MODELS.has(name);
1269
1432
  const assertSchema = (exportName, value) => {
@@ -1279,6 +1442,10 @@ const assertSchema = (exportName, value) => {
1279
1442
  const buildAppModels = (modules) => Object.entries(modules).filter(([key]) => key.endsWith("Schema")).map(([key, schemaValue]) => {
1280
1443
  const schema = assertSchema(key, schemaValue);
1281
1444
  const name = key.replace(/Schema$/, "");
1445
+ if (!accessibleRecordsApplied.has(schema)) {
1446
+ schema.plugin(accessibleRecordsPlugin);
1447
+ accessibleRecordsApplied.add(schema);
1448
+ }
1282
1449
  if (!isRtsChangelogExcludedModelName(name)) {
1283
1450
  if (!rtsChangeLogApplied.has(schema)) {
1284
1451
  schema.plugin(rtsChangeLogPlugin);
@@ -1293,6 +1460,10 @@ const buildAppModels = (modules) => Object.entries(modules).filter(([key]) => ke
1293
1460
  const buildFrameworkModels = () => Object.entries(frameworkSchemas).filter(([key]) => key.endsWith("Schema")).map(([key, schemaValue]) => {
1294
1461
  const schema = assertSchema(key, schemaValue);
1295
1462
  const name = key.replace(/Schema$/, "");
1463
+ if (!accessibleRecordsApplied.has(schema)) {
1464
+ schema.plugin(accessibleRecordsPlugin);
1465
+ accessibleRecordsApplied.add(schema);
1466
+ }
1296
1467
  if (!isRtsChangelogExcludedModelName(name)) {
1297
1468
  if (!rtsChangeLogApplied.has(schema)) {
1298
1469
  schema.plugin(rtsChangeLogPlugin);
@@ -1313,6 +1484,8 @@ const buildModels = (modules) => {
1313
1484
  }, {});
1314
1485
  };
1315
1486
  const registerModels = (modules) => {
1487
+ registerPoliciesFromModules(frameworkSchemas);
1488
+ registerPoliciesFromModules(modules);
1316
1489
  cachedModels = buildModels(modules);
1317
1490
  };
1318
1491
  const getRegisteredModels = () => {
@@ -1390,7 +1563,9 @@ const getTenantFilesystemDbFromCtx = async (ctx) => {
1390
1563
  };
1391
1564
  export {
1392
1565
  LANGUAGE_CODE_REGEX,
1566
+ RBNotificationPolicy,
1393
1567
  RBNotificationSchema,
1568
+ RBNotificationSettingsPolicy,
1394
1569
  RBNotificationSettingsSchema,
1395
1570
  RBRtsChangeSchema,
1396
1571
  RBRtsCounterSchema,
@@ -1398,6 +1573,7 @@ export {
1398
1573
  RBTenantSubscriptionEventSchema,
1399
1574
  RBTenantSubscriptionSchema,
1400
1575
  RBUploadChunkSchema,
1576
+ RBUploadSessionPolicy,
1401
1577
  RBUploadSessionSchema,
1402
1578
  RBUserSchema,
1403
1579
  ZRBNotification,
@@ -1420,14 +1596,22 @@ export {
1420
1596
  ZRBUploadSession,
1421
1597
  ZRBUploadSessionStatus,
1422
1598
  ZRBUser,
1599
+ buildAbility,
1600
+ buildAbilityFromSession,
1423
1601
  buildLocaleFallbackChain,
1602
+ can,
1424
1603
  createModels,
1425
1604
  extendZod,
1605
+ getAccessibleByQuery,
1606
+ getRegisteredPolicies,
1426
1607
  getTenantFilesystemDb,
1427
1608
  getTenantFilesystemDbFromCtx,
1428
1609
  getTenantFilesystemDbName,
1610
+ getTenantRolesFromSessionUser,
1429
1611
  loadModel,
1430
1612
  loadRbModel,
1613
+ registerPoliciesFromModules,
1614
+ registerPolicy,
1431
1615
  resolveLocalizedString,
1432
1616
  withLocalizedStringFallback,
1433
1617
  z2 as z,
@@ -1,5 +1,6 @@
1
1
  import { Schema } from '../../../vite/node_modules/mongoose';
2
2
  import { z } from '../../../vite/node_modules/zod';
3
+ import { AclPolicy } from '../acl';
3
4
  export declare const ZRBNotification: z.ZodObject<{
4
5
  userId: z.ZodString;
5
6
  topic: z.ZodOptional<z.ZodString>;
@@ -14,4 +15,5 @@ export declare const ZRBNotification: z.ZodObject<{
14
15
  }, z.core.$strip>;
15
16
  export type IRBNotification = z.infer<typeof ZRBNotification>;
16
17
  export declare const RBNotificationSchema: Schema;
18
+ export declare const RBNotificationPolicy: AclPolicy;
17
19
  //# sourceMappingURL=RBNotification.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"RBNotification.d.ts","sourceRoot":"","sources":["../../src/models/RBNotification.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,eAAO,MAAM,eAAe;;;;;;;;;;;iBAW1B,CAAA;AAEF,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAA;AAI7D,eAAO,MAAM,oBAAoB,EAAE,MAiBlC,CAAA"}
1
+ {"version":3,"file":"RBNotification.d.ts","sourceRoot":"","sources":["../../src/models/RBNotification.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AAGvC,eAAO,MAAM,eAAe;;;;;;;;;;;iBAW1B,CAAA;AAEF,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAA;AAI7D,eAAO,MAAM,oBAAoB,EAAE,MAiBlC,CAAA;AAOD,eAAO,MAAM,oBAAoB,EAAE,SASlC,CAAA"}
@@ -1,5 +1,6 @@
1
1
  import { Schema } from '../../../vite/node_modules/mongoose';
2
2
  import { z } from '../../../vite/node_modules/zod';
3
+ import { AclPolicy } from '../acl';
3
4
  export declare const ZRBNotificationDigestFrequency: z.ZodEnum<{
4
5
  off: "off";
5
6
  daily: "daily";
@@ -28,4 +29,5 @@ export declare const ZRBNotificationSettings: z.ZodObject<{
28
29
  }, z.core.$strip>;
29
30
  export type IRBNotificationSettings = z.infer<typeof ZRBNotificationSettings>;
30
31
  export declare const RBNotificationSettingsSchema: Schema;
32
+ export declare const RBNotificationSettingsPolicy: AclPolicy;
31
33
  //# sourceMappingURL=RBNotificationSettings.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"RBNotificationSettings.d.ts","sourceRoot":"","sources":["../../src/models/RBNotificationSettings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,eAAO,MAAM,8BAA8B;;;;EAAqC,CAAA;AAEhF,eAAO,MAAM,8BAA8B;;;;;iBAKzC,CAAA;AAEF,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;iBAKlC,CAAA;AAEF,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAA;AAY7E,eAAO,MAAM,4BAA4B,EAAE,MAoB1C,CAAA"}
1
+ {"version":3,"file":"RBNotificationSettings.d.ts","sourceRoot":"","sources":["../../src/models/RBNotificationSettings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AAGvC,eAAO,MAAM,8BAA8B;;;;EAAqC,CAAA;AAEhF,eAAO,MAAM,8BAA8B;;;;;iBAKzC,CAAA;AAEF,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;iBAKlC,CAAA;AAEF,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAA;AAY7E,eAAO,MAAM,4BAA4B,EAAE,MAoB1C,CAAA;AAKD,eAAO,MAAM,4BAA4B,EAAE,SAQ1C,CAAA"}
@@ -1,5 +1,6 @@
1
1
  import { Schema } from '../../../vite/node_modules/mongoose';
2
2
  import { z } from '../../../vite/node_modules/zod';
3
+ import { AclPolicy } from '../acl';
3
4
  export declare const ZRBUploadSessionStatus: z.ZodEnum<{
4
5
  error: "error";
5
6
  done: "done";
@@ -28,4 +29,5 @@ export declare const ZRBUploadSession: z.ZodObject<{
28
29
  }, z.core.$strip>;
29
30
  export type IRBUploadSession = z.infer<typeof ZRBUploadSession>;
30
31
  export declare const RBUploadSessionSchema: Schema;
32
+ export declare const RBUploadSessionPolicy: AclPolicy;
31
33
  //# sourceMappingURL=RBUploadSession.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"RBUploadSession.d.ts","sourceRoot":"","sources":["../../src/models/RBUploadSession.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,eAAO,MAAM,sBAAsB;;;;;EAAuD,CAAA;AAE1F,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;iBAc3B,CAAA;AAEF,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAA;AAE/D,eAAO,MAAM,qBAAqB,EAAE,MAoBnC,CAAA"}
1
+ {"version":3,"file":"RBUploadSession.d.ts","sourceRoot":"","sources":["../../src/models/RBUploadSession.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AAGvC,eAAO,MAAM,sBAAsB;;;;;EAAuD,CAAA;AAE1F,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;iBAc3B,CAAA;AAEF,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAA;AAE/D,eAAO,MAAM,qBAAqB,EAAE,MAoBnC,CAAA;AAID,eAAO,MAAM,qBAAqB,EAAE,SAkBnC,CAAA"}
@@ -6,6 +6,7 @@ export declare const ZRBUser: z.ZodObject<{
6
6
  name: z.ZodOptional<z.ZodString>;
7
7
  phone: z.ZodOptional<z.ZodString>;
8
8
  tenants: z.ZodArray<z.ZodString>;
9
+ tenantRoles: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
9
10
  emailVerificationCode: z.ZodOptional<z.ZodString>;
10
11
  emailVerificationExpiresAt: z.ZodOptional<z.ZodDate>;
11
12
  }, z.core.$strip>;
@@ -1 +1 @@
1
- {"version":3,"file":"User.d.ts","sourceRoot":"","sources":["../../src/models/User.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,eAAO,MAAM,OAAO;;;;;;;;iBAQlB,CAAA;AAEF,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,OAAO,CAAC,CAAC;AAE9C,eAAO,MAAM,YAAY,EAAE,MAQA,CAAA"}
1
+ {"version":3,"file":"User.d.ts","sourceRoot":"","sources":["../../src/models/User.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,eAAO,MAAM,OAAO;;;;;;;;;iBASlB,CAAA;AAEF,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,OAAO,CAAC,CAAC;AAE9C,eAAO,MAAM,YAAY,EAAE,MASA,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"registerModels.d.ts","sourceRoot":"","sources":["../src/registerModels.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAA;AAU/B,KAAK,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAiE3C,eAAO,MAAM,cAAc,GAAI,SAAS,YAAY,SAEnD,CAAA;AAED,eAAO,MAAM,mBAAmB,sEAK/B,CAAA;AAED,YAAY,EAAE,YAAY,EAAE,CAAA"}
1
+ {"version":3,"file":"registerModels.d.ts","sourceRoot":"","sources":["../src/registerModels.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAA;AAa/B,KAAK,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAyE3C,eAAO,MAAM,cAAc,GAAI,SAAS,YAAY,SAInD,CAAA;AAED,eAAO,MAAM,mBAAmB,sEAK/B,CAAA;AAED,YAAY,EAAE,YAAY,EAAE,CAAA"}
package/package.json CHANGED
@@ -1,12 +1,24 @@
1
1
  {
2
2
  "name": "@rpcbase/db",
3
- "version": "0.26.0",
3
+ "version": "0.27.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"
7
7
  ],
8
8
  "main": "./dist/index.js",
9
9
  "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "default": "./dist/index.js"
15
+ },
16
+ "./acl": {
17
+ "types": "./dist/index.d.ts",
18
+ "import": "./dist/index.js",
19
+ "default": "./dist/index.js"
20
+ }
21
+ },
10
22
  "scripts": {
11
23
  "build": "wireit",
12
24
  "test": "vitest run --coverage",
@@ -55,9 +67,13 @@
55
67
  "mongoose": "^9",
56
68
  "zod": "^4"
57
69
  },
70
+ "dependencies": {
71
+ "@casl/ability": "6.7.5",
72
+ "@casl/mongoose": "8.0.3"
73
+ },
58
74
  "devDependencies": {
59
- "@types/node": "24.10.1",
60
- "@vitest/coverage-v8": "4.0.9",
61
- "vitest": "4.0.9"
75
+ "@types/node": "25.0.3",
76
+ "@vitest/coverage-v8": "4.0.16",
77
+ "vitest": "4.0.16"
62
78
  }
63
79
  }