@rpcbase/db 0.26.0 → 0.28.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,10 +1305,129 @@ const rtsChangeLogPlugin = (schema) => {
1262
1305
  }
1263
1306
  });
1264
1307
  };
1265
- const RTS_CHANGELOG_EXCLUDED_MODELS = /* @__PURE__ */ new Set(["RBRtsChange", "RBRtsCounter"]);
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
+ };
1266
1427
  const rtsChangeLogApplied = /* @__PURE__ */ new WeakSet();
1428
+ const accessibleRecordsApplied = /* @__PURE__ */ new WeakSet();
1267
1429
  let cachedModels = null;
1268
- const isRtsChangelogExcludedModelName = (name) => name.startsWith("RB") || RTS_CHANGELOG_EXCLUDED_MODELS.has(name);
1430
+ const isRtsChangelogExcludedModelName = (name) => name.startsWith("RB");
1269
1431
  const assertSchema = (exportName, value) => {
1270
1432
  if (value instanceof mongoose.Schema) return value;
1271
1433
  throw new Error(
@@ -1279,6 +1441,10 @@ const assertSchema = (exportName, value) => {
1279
1441
  const buildAppModels = (modules) => Object.entries(modules).filter(([key]) => key.endsWith("Schema")).map(([key, schemaValue]) => {
1280
1442
  const schema = assertSchema(key, schemaValue);
1281
1443
  const name = key.replace(/Schema$/, "");
1444
+ if (!accessibleRecordsApplied.has(schema)) {
1445
+ schema.plugin(accessibleRecordsPlugin);
1446
+ accessibleRecordsApplied.add(schema);
1447
+ }
1282
1448
  if (!isRtsChangelogExcludedModelName(name)) {
1283
1449
  if (!rtsChangeLogApplied.has(schema)) {
1284
1450
  schema.plugin(rtsChangeLogPlugin);
@@ -1293,6 +1459,10 @@ const buildAppModels = (modules) => Object.entries(modules).filter(([key]) => ke
1293
1459
  const buildFrameworkModels = () => Object.entries(frameworkSchemas).filter(([key]) => key.endsWith("Schema")).map(([key, schemaValue]) => {
1294
1460
  const schema = assertSchema(key, schemaValue);
1295
1461
  const name = key.replace(/Schema$/, "");
1462
+ if (!accessibleRecordsApplied.has(schema)) {
1463
+ schema.plugin(accessibleRecordsPlugin);
1464
+ accessibleRecordsApplied.add(schema);
1465
+ }
1296
1466
  if (!isRtsChangelogExcludedModelName(name)) {
1297
1467
  if (!rtsChangeLogApplied.has(schema)) {
1298
1468
  schema.plugin(rtsChangeLogPlugin);
@@ -1313,6 +1483,8 @@ const buildModels = (modules) => {
1313
1483
  }, {});
1314
1484
  };
1315
1485
  const registerModels = (modules) => {
1486
+ registerPoliciesFromModules(frameworkSchemas);
1487
+ registerPoliciesFromModules(modules);
1316
1488
  cachedModels = buildModels(modules);
1317
1489
  };
1318
1490
  const getRegisteredModels = () => {
@@ -1390,7 +1562,9 @@ const getTenantFilesystemDbFromCtx = async (ctx) => {
1390
1562
  };
1391
1563
  export {
1392
1564
  LANGUAGE_CODE_REGEX,
1565
+ RBNotificationPolicy,
1393
1566
  RBNotificationSchema,
1567
+ RBNotificationSettingsPolicy,
1394
1568
  RBNotificationSettingsSchema,
1395
1569
  RBRtsChangeSchema,
1396
1570
  RBRtsCounterSchema,
@@ -1398,6 +1572,7 @@ export {
1398
1572
  RBTenantSubscriptionEventSchema,
1399
1573
  RBTenantSubscriptionSchema,
1400
1574
  RBUploadChunkSchema,
1575
+ RBUploadSessionPolicy,
1401
1576
  RBUploadSessionSchema,
1402
1577
  RBUserSchema,
1403
1578
  ZRBNotification,
@@ -1420,14 +1595,22 @@ export {
1420
1595
  ZRBUploadSession,
1421
1596
  ZRBUploadSessionStatus,
1422
1597
  ZRBUser,
1598
+ buildAbility,
1599
+ buildAbilityFromSession,
1423
1600
  buildLocaleFallbackChain,
1601
+ can,
1424
1602
  createModels,
1425
1603
  extendZod,
1604
+ getAccessibleByQuery,
1605
+ getRegisteredPolicies,
1426
1606
  getTenantFilesystemDb,
1427
1607
  getTenantFilesystemDbFromCtx,
1428
1608
  getTenantFilesystemDbName,
1609
+ getTenantRolesFromSessionUser,
1429
1610
  loadModel,
1430
1611
  loadRbModel,
1612
+ registerPoliciesFromModules,
1613
+ registerPolicy,
1431
1614
  resolveLocalizedString,
1432
1615
  withLocalizedStringFallback,
1433
1616
  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;AAwE3C,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.28.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
  }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=index.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../src/schema/index.test.ts"],"names":[],"mappings":""}