@rpcbase/db 0.35.0 → 0.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -15
- package/dist/acl/index.js +11 -0
- package/dist/can-urGFf45M.js +131 -0
- package/dist/extendMongooseSchema.d.ts +6 -0
- package/dist/extendMongooseSchema.d.ts.map +1 -0
- package/dist/index.browser.d.ts +2 -0
- package/dist/index.browser.d.ts.map +1 -0
- package/dist/index.browser.js +22 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +309 -1137
- package/dist/models/{Tenant.d.ts → RBTenant.d.ts} +1 -1
- package/dist/models/RBTenant.d.ts.map +1 -0
- package/dist/models/{User.d.ts → RBUser.d.ts} +1 -1
- package/dist/models/RBUser.d.ts.map +1 -0
- package/dist/models/index.d.ts +2 -2
- package/dist/models/index.d.ts.map +1 -1
- package/dist/mongoose/index.d.ts +4 -0
- package/dist/mongoose/index.d.ts.map +1 -0
- package/dist/zod/extension.d.ts +17 -0
- package/dist/zod/extension.d.ts.map +1 -0
- package/dist/zod/index.d.ts +3 -0
- package/dist/zod/index.d.ts.map +1 -0
- package/package.json +5 -4
- package/dist/acl.d.ts +0 -2
- package/dist/acl.d.ts.map +0 -1
- package/dist/models/Tenant.d.ts.map +0 -1
- package/dist/models/User.d.ts.map +0 -1
- package/dist/schema/assertions/assertions.d.ts +0 -20
- package/dist/schema/assertions/assertions.d.ts.map +0 -1
- package/dist/schema/assertions/constructor.d.ts +0 -10
- package/dist/schema/assertions/constructor.d.ts.map +0 -1
- package/dist/schema/assertions/custom.d.ts +0 -6
- package/dist/schema/assertions/custom.d.ts.map +0 -1
- package/dist/schema/assertions/instanceOf.d.ts +0 -10
- package/dist/schema/assertions/instanceOf.d.ts.map +0 -1
- package/dist/schema/assertions/staticNames.d.ts +0 -10
- package/dist/schema/assertions/staticNames.d.ts.map +0 -1
- package/dist/schema/assertions/types.d.ts +0 -17
- package/dist/schema/assertions/types.d.ts.map +0 -1
- package/dist/schema/extension.d.ts +0 -78
- package/dist/schema/extension.d.ts.map +0 -1
- package/dist/schema/index.d.ts +0 -73
- package/dist/schema/index.d.ts.map +0 -1
- package/dist/schema/mongoose.types.d.ts +0 -93
- package/dist/schema/mongoose.types.d.ts.map +0 -1
- package/dist/setupFile.d.ts +0 -2
- package/dist/setupFile.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -1,25 +1,20 @@
|
|
|
1
1
|
## `@rpcbase/db`
|
|
2
2
|
|
|
3
|
-
###
|
|
3
|
+
### Zod extensions
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
This package provides a small Zod extension that adds `.unique()` and `.sparse()` to `z.string()`, `z.number()`, and `z.date()` (for chaining/compatibility).
|
|
6
6
|
|
|
7
7
|
```ts
|
|
8
|
-
import { z } from "
|
|
9
|
-
import { zI18nString, zodSchema } from "@rpcbase/db"
|
|
8
|
+
import { z } from "@rpcbase/db"
|
|
10
9
|
|
|
11
|
-
const
|
|
12
|
-
|
|
10
|
+
const ZUser = z.object({
|
|
11
|
+
email: z.string().email().unique().sparse(),
|
|
12
|
+
createdAt: z.date().sparse(),
|
|
13
13
|
})
|
|
14
|
-
|
|
15
|
-
const schema = zodSchema(ZPost)
|
|
16
14
|
```
|
|
17
15
|
|
|
18
|
-
|
|
16
|
+
### Mongoose re-export
|
|
19
17
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
- `doc.title.get("fr-FR")` → fallback (read helper)
|
|
24
|
-
- `doc.title.getExact("fr-FR")` → exact (no fallback)
|
|
25
|
-
- On write, because it’s `Mixed`, avoid silent deep mutations: prefer replacing the object (`doc.title = { ...doc.title, fr: "Salut" }`) or call `doc.markModified("title")`.
|
|
18
|
+
```ts
|
|
19
|
+
import { Schema, model, mongoose } from "@rpcbase/db"
|
|
20
|
+
```
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { b, d, f, e, g, c, a, r } from "../can-urGFf45M.js";
|
|
2
|
+
export {
|
|
3
|
+
b as buildAbility,
|
|
4
|
+
d as buildAbilityFromSession,
|
|
5
|
+
f as can,
|
|
6
|
+
e as getAccessibleByQuery,
|
|
7
|
+
g as getRegisteredPolicies,
|
|
8
|
+
c as getTenantRolesFromSessionUser,
|
|
9
|
+
a as registerPoliciesFromModules,
|
|
10
|
+
r as registerPolicy
|
|
11
|
+
};
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { AbilityBuilder, createMongoAbility, subject } from "@casl/ability";
|
|
2
|
+
import { accessibleBy } from "@casl/mongoose";
|
|
3
|
+
const POLICIES_GLOBAL_KEY = /* @__PURE__ */ Symbol.for("@rpcbase/db/acl/policiesBySubject");
|
|
4
|
+
const getGlobalPoliciesMap = () => {
|
|
5
|
+
const store = globalThis;
|
|
6
|
+
const existing = store[POLICIES_GLOBAL_KEY];
|
|
7
|
+
if (existing instanceof Map) {
|
|
8
|
+
return existing;
|
|
9
|
+
}
|
|
10
|
+
const created = /* @__PURE__ */ new Map();
|
|
11
|
+
store[POLICIES_GLOBAL_KEY] = created;
|
|
12
|
+
return created;
|
|
13
|
+
};
|
|
14
|
+
const policiesBySubject = getGlobalPoliciesMap();
|
|
15
|
+
const isPolicy = (value) => {
|
|
16
|
+
if (!value || typeof value !== "object") return false;
|
|
17
|
+
const maybe = value;
|
|
18
|
+
return typeof maybe.subject === "string" && typeof maybe.define === "function";
|
|
19
|
+
};
|
|
20
|
+
const registerPolicy = (policy) => {
|
|
21
|
+
const set = policiesBySubject.get(policy.subject) ?? /* @__PURE__ */ new Set();
|
|
22
|
+
set.add(policy.define);
|
|
23
|
+
policiesBySubject.set(policy.subject, set);
|
|
24
|
+
};
|
|
25
|
+
const registerPoliciesFromModules = (modules) => {
|
|
26
|
+
for (const [exportName, value] of Object.entries(modules)) {
|
|
27
|
+
if (!isPolicy(value)) continue;
|
|
28
|
+
if (!exportName.endsWith("Policy")) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
`Invalid policy export name "${exportName}". Policies must be exported as "<ModelName>Policy".`
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
const expectedSubject = exportName.slice(0, -"Policy".length);
|
|
34
|
+
if (value.subject !== expectedSubject) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
`Invalid policy "${exportName}": expected subject "${expectedSubject}", got "${value.subject}".`
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
registerPolicy(value);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
const getRegisteredPolicies = () => {
|
|
43
|
+
const out = [];
|
|
44
|
+
for (const set of policiesBySubject.values()) {
|
|
45
|
+
for (const fn of set) out.push(fn);
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
};
|
|
49
|
+
const buildAbility = (ctx) => {
|
|
50
|
+
const builder = new AbilityBuilder(createMongoAbility);
|
|
51
|
+
const { can: can2 } = builder;
|
|
52
|
+
if (ctx.roles.includes("owner") || ctx.roles.includes("admin")) {
|
|
53
|
+
can2("manage", "all");
|
|
54
|
+
}
|
|
55
|
+
for (const define of getRegisteredPolicies()) {
|
|
56
|
+
define(builder, ctx);
|
|
57
|
+
}
|
|
58
|
+
return builder.build();
|
|
59
|
+
};
|
|
60
|
+
const normalizeRole = (raw) => {
|
|
61
|
+
if (typeof raw !== "string") return null;
|
|
62
|
+
const normalized = raw.trim();
|
|
63
|
+
return normalized ? normalized : null;
|
|
64
|
+
};
|
|
65
|
+
const normalizeRoles = (raw) => {
|
|
66
|
+
if (Array.isArray(raw)) {
|
|
67
|
+
return raw.map(normalizeRole).filter((r) => Boolean(r));
|
|
68
|
+
}
|
|
69
|
+
const role = normalizeRole(raw);
|
|
70
|
+
return role ? [role] : [];
|
|
71
|
+
};
|
|
72
|
+
const normalizeRolesByTenantId = (raw) => {
|
|
73
|
+
if (!raw || typeof raw !== "object") return null;
|
|
74
|
+
if (raw instanceof Map) {
|
|
75
|
+
return Object.fromEntries(Array.from(raw.entries()).map(([key, value]) => [String(key), normalizeRoles(value)]));
|
|
76
|
+
}
|
|
77
|
+
const obj = raw;
|
|
78
|
+
const out = {};
|
|
79
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
80
|
+
const tenantId = String(key).trim();
|
|
81
|
+
if (!tenantId) continue;
|
|
82
|
+
out[tenantId] = normalizeRoles(value);
|
|
83
|
+
}
|
|
84
|
+
return out;
|
|
85
|
+
};
|
|
86
|
+
const getTenantRolesFromSessionUser = (user, tenantId) => {
|
|
87
|
+
if (!user || typeof user !== "object") return [];
|
|
88
|
+
const rolesByTenantId = normalizeRolesByTenantId(user.tenantRoles);
|
|
89
|
+
if (!rolesByTenantId) return [];
|
|
90
|
+
return rolesByTenantId[tenantId]?.filter(Boolean) ?? [];
|
|
91
|
+
};
|
|
92
|
+
const buildAbilityFromSession = ({
|
|
93
|
+
tenantId,
|
|
94
|
+
session,
|
|
95
|
+
claims
|
|
96
|
+
}) => {
|
|
97
|
+
const user = session?.user;
|
|
98
|
+
const userId = typeof user?.id === "string" ? user.id.trim() : "";
|
|
99
|
+
const rolesByTenantId = normalizeRolesByTenantId(user?.tenantRoles);
|
|
100
|
+
const hasExplicitTenantEntry = Boolean(
|
|
101
|
+
rolesByTenantId && Object.prototype.hasOwnProperty.call(rolesByTenantId, tenantId)
|
|
102
|
+
);
|
|
103
|
+
const signedInTenantsRaw = user?.signedInTenants;
|
|
104
|
+
const signedInTenants = Array.isArray(signedInTenantsRaw) ? signedInTenantsRaw.map(String) : [];
|
|
105
|
+
const roles = hasExplicitTenantEntry ? rolesByTenantId[tenantId] ?? [] : userId && signedInTenants.includes(tenantId) ? ["owner"] : getTenantRolesFromSessionUser(user, tenantId);
|
|
106
|
+
return buildAbility({
|
|
107
|
+
tenantId,
|
|
108
|
+
userId: userId || null,
|
|
109
|
+
roles,
|
|
110
|
+
claims
|
|
111
|
+
});
|
|
112
|
+
};
|
|
113
|
+
const getAccessibleByQuery = (ability, action, subject2) => {
|
|
114
|
+
return accessibleBy(ability, action).ofType(subject2);
|
|
115
|
+
};
|
|
116
|
+
const can = (ability, action, subjectType, object) => {
|
|
117
|
+
if (object && typeof object === "object") {
|
|
118
|
+
return ability.can(action, subject(subjectType, object));
|
|
119
|
+
}
|
|
120
|
+
return ability.can(action, subjectType);
|
|
121
|
+
};
|
|
122
|
+
export {
|
|
123
|
+
registerPoliciesFromModules as a,
|
|
124
|
+
buildAbility as b,
|
|
125
|
+
getTenantRolesFromSessionUser as c,
|
|
126
|
+
buildAbilityFromSession as d,
|
|
127
|
+
getAccessibleByQuery as e,
|
|
128
|
+
can as f,
|
|
129
|
+
getRegisteredPolicies as g,
|
|
130
|
+
registerPolicy as r
|
|
131
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Schema } from '../../vite/node_modules/mongoose';
|
|
2
|
+
type MongooseSchemaExtension = Parameters<Schema["add"]>[0];
|
|
3
|
+
export declare function extendMongooseSchema<TSchema extends Schema>(baseSchema: TSchema, ...extensions: MongooseSchemaExtension[]): TSchema;
|
|
4
|
+
export declare function omitSchemaPaths<TSchema extends Schema>(schema: TSchema, paths: string[]): TSchema;
|
|
5
|
+
export {};
|
|
6
|
+
//# sourceMappingURL=extendMongooseSchema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extendMongooseSchema.d.ts","sourceRoot":"","sources":["../src/extendMongooseSchema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAGjC,KAAK,uBAAuB,GAAG,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AAE3D,wBAAgB,oBAAoB,CAAC,OAAO,SAAS,MAAM,EACzD,UAAU,EAAE,OAAO,EACnB,GAAG,UAAU,EAAE,uBAAuB,EAAE,GACvC,OAAO,CAMT;AAED,wBAAgB,eAAe,CAAC,OAAO,SAAS,MAAM,EACpD,MAAM,EAAE,OAAO,EACf,KAAK,EAAE,MAAM,EAAE,GACd,OAAO,CAIT"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.browser.d.ts","sourceRoot":"","sources":["../src/index.browser.ts"],"names":[],"mappings":"AAAA,cAAc,OAAO,CAAA"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { z as z2 } from "zod";
|
|
3
|
+
let zodExtended = false;
|
|
4
|
+
function extendZod(zod) {
|
|
5
|
+
if (zodExtended) return;
|
|
6
|
+
zodExtended = true;
|
|
7
|
+
const supported = [zod.ZodString, zod.ZodNumber, zod.ZodDate];
|
|
8
|
+
for (const type of supported) {
|
|
9
|
+
const proto = type?.prototype;
|
|
10
|
+
if (!proto) continue;
|
|
11
|
+
proto.unique = function unique(_flag = true) {
|
|
12
|
+
return this;
|
|
13
|
+
};
|
|
14
|
+
proto.sparse = function sparse(_flag = true) {
|
|
15
|
+
return this;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
extendZod(z);
|
|
20
|
+
export {
|
|
21
|
+
z2 as z
|
|
22
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
export * from './acl';
|
|
1
2
|
export * from './models';
|
|
2
|
-
export * from './
|
|
3
|
+
export * from './mongoose';
|
|
4
|
+
export * from './pagination';
|
|
5
|
+
export * from './zod';
|
|
3
6
|
export * from './createModels';
|
|
7
|
+
export * from './extendMongooseSchema';
|
|
4
8
|
export * from './modelsApi';
|
|
5
9
|
export * from './tenantFilesystemDb';
|
|
6
|
-
export * from './acl';
|
|
7
|
-
export * from './pagination';
|
|
8
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,OAAO,CAAA;AACrB,cAAc,UAAU,CAAA;AACxB,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA;AAC5B,cAAc,OAAO,CAAA;AAErB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,wBAAwB,CAAA;AACtC,cAAc,aAAa,CAAA;AAC3B,cAAc,sBAAsB,CAAA"}
|