bazis 0.139.0 → 0.140.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/core/access/Access.module.ts +6 -1
- package/core/access/catalog/AccessCatalog.ts +56 -7
- package/core/access/catalog/IAccessCatalog.service.ts +28 -2
- package/core/access/catalog/PermissionSourceLoader.ts +43 -0
- package/core/access/catalog/PermissionSources.ts +222 -0
- package/core/access/catalog/moduleFields.ts +60 -6
- package/core/access/catalog/permissions.ts +46 -1
- package/core/access/check/Access.ts +55 -28
- package/core/access/check/accessConfig.ts +9 -1
- package/core/access/check/can.ts +2 -2
- package/core/access/check/conditions.ts +69 -11
- package/core/access/check/decimal.ts +61 -0
- package/core/access/check/filter.ts +39 -8
- package/core/access/contracts/PermissionSource.ts +32 -0
- package/core/access/contracts/types.ts +12 -2
- package/core/access/generated/deps.ts +5 -1
- package/core/access/http/AccessCatalog.controller.ts +9 -2
- package/core/access/index.ts +12 -1
- package/core/infra/connectors/codex/CodexAppServerClient.ts +1 -1
- package/core/scripts/di-generate.ts +34 -4
- package/package.json +1 -1
package/core/access/index.ts
CHANGED
|
@@ -16,8 +16,17 @@ import type { PermissionsOf } from "./catalog/permissions";
|
|
|
16
16
|
*/
|
|
17
17
|
export interface BazisPermissions extends PermissionsOf<typeof accessPermissions> {}
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* The prefixes of the application's permission sources (ACCESS-001 §3.1): any
|
|
21
|
+
* key under one of them is accepted by `can`, `access.can` and `access.filter`.
|
|
22
|
+
* Nobody writes it by hand: `bazis codegen` fills it from `permissionSources`
|
|
23
|
+
* of the application's modules.
|
|
24
|
+
*/
|
|
25
|
+
export interface BazisPermissionSources {}
|
|
26
|
+
|
|
19
27
|
export { AccessModule } from "./Access.module";
|
|
20
|
-
export { definePermissions, type AttributeSpec, type OwnPermissionKey, type PermissionDeclarations, type PermissionKey, type PermissionsOf, type PermissionSpec, type ScopeSource } from "./catalog/permissions";
|
|
28
|
+
export { definePermissions, type AnyPermissionKey, type AttributeSpec, type OwnPermissionKey, type PermissionDeclarations, type PermissionKey, type PermissionsOf, type PermissionSpec, type ScopeSource, type SourcePermissionKey } from "./catalog/permissions";
|
|
29
|
+
export type { PermissionSourceState } from "./catalog/PermissionSources";
|
|
21
30
|
export { IAccessCatalog } from "./catalog/IAccessCatalog.service";
|
|
22
31
|
export type { DeclaredRole, OwnedPermission, OwnedReferenceKind } from "./catalog/AccessCatalog";
|
|
23
32
|
export { IAccess } from "./check/IAccess.service";
|
|
@@ -27,12 +36,14 @@ export { can } from "./check/can";
|
|
|
27
36
|
export { grantsToWhere, type AccessFilter, type ColumnCondition } from "./check/filter";
|
|
28
37
|
export { AccessPolicy, type ScopePrincipals } from "./contracts/AccessPolicy";
|
|
29
38
|
export { AttributeLookup, type LookupColumn } from "./contracts/AttributeLookup";
|
|
39
|
+
export { PermissionSource } from "./contracts/PermissionSource";
|
|
30
40
|
export { References, type ReferenceContext, type ReferenceItem, type ReferencePage } from "./contracts/References";
|
|
31
41
|
export { AccessFilterTooLargeError, AccessSetupError } from "./contracts/errors";
|
|
32
42
|
export type {
|
|
33
43
|
AccessObject,
|
|
34
44
|
AccessSubject,
|
|
35
45
|
AccessValue,
|
|
46
|
+
CompareTest,
|
|
36
47
|
Condition,
|
|
37
48
|
Grant,
|
|
38
49
|
GrantFields,
|
|
@@ -232,7 +232,7 @@ export class CodexAppServerClient implements CodexClient {
|
|
|
232
232
|
if (method === "account/updated") this.modelCache = undefined;
|
|
233
233
|
}, () => { this.clearLogin(); this.modelCache = undefined; });
|
|
234
234
|
const initialized = await client.request("initialize", {
|
|
235
|
-
clientInfo: { name: "bazis", title: "Bazis", version: "0.
|
|
235
|
+
clientInfo: { name: "bazis", title: "Bazis", version: "0.140.0" }, capabilities: { experimentalApi: true },
|
|
236
236
|
}, 15_000, signal);
|
|
237
237
|
if (resolve(text(initialized.codexHome, 4096)) !== this.paths.home) throw new CodexError("PROTOCOL_ERROR");
|
|
238
238
|
client.notify("initialized");
|
|
@@ -191,6 +191,8 @@ interface ConstructorDependencies {
|
|
|
191
191
|
let classDeps = new Map<ts.ClassDeclaration, readonly CollectedDependency[]>();
|
|
192
192
|
/** `permissions` of the target's modules: the exported constant and its file. */
|
|
193
193
|
let permissionDeclarations = new Map<string, { readonly name: string; readonly file: string }>();
|
|
194
|
+
/** The prefixes of `permissionSources` of the target's modules: any key under one is a permission key. */
|
|
195
|
+
let permissionSourcePrefixes = new Set<string>();
|
|
194
196
|
let inferredDiClasses = new Set<ts.ClassDeclaration>();
|
|
195
197
|
let explicitlyBoundDiClasses = new Set<ts.ClassDeclaration>();
|
|
196
198
|
// Known runtime token names: createToken descriptions + class names usable as class-tokens.
|
|
@@ -507,7 +509,7 @@ function collectDynamicImportDiagnostics(source: ts.SourceFile, target: string,
|
|
|
507
509
|
|
|
508
510
|
function resetTargetAnalysis(files: Set<string>, generatedDir: string): void {
|
|
509
511
|
activeGeneratedDir = generatedDir;
|
|
510
|
-
classDeps = new Map(); inferredDiClasses = new Set(); explicitlyBoundDiClasses = new Set(); permissionDeclarations = new Map();
|
|
512
|
+
classDeps = new Map(); inferredDiClasses = new Set(); explicitlyBoundDiClasses = new Set(); permissionDeclarations = new Map(); permissionSourcePrefixes = new Set();
|
|
511
513
|
tokenDescriptions = new Set(); configPrefixesByType = new Map(); knownClassNames = new Set(); warnings = []; fatalErrors = [];
|
|
512
514
|
httpBindings = {}; httpControllerFiles = {}; openApiSchemas = {}; openApiOperations = {};
|
|
513
515
|
grpcBindings = new Map();
|
|
@@ -557,7 +559,7 @@ async function generateTarget(name: string, reachable: Set<string>, production:
|
|
|
557
559
|
stageWrite(`${generatedDir}/httpRequestModels.ts`, requestModels);
|
|
558
560
|
stageWrite(`${generatedDir}/httpListModels.ts`, listModels);
|
|
559
561
|
stageWrite(`${generatedDir}/openapi.ts`, openApi);
|
|
560
|
-
if (permissionDeclarations.size > 0) stageWrite(`${generatedDir}/permissions.ts`, renderPermissions());
|
|
562
|
+
if (permissionDeclarations.size > 0 || permissionSourcePrefixes.size > 0) stageWrite(`${generatedDir}/permissions.ts`, renderPermissions());
|
|
561
563
|
else stageRemove(`${generatedDir}/permissions.ts`);
|
|
562
564
|
// Generated before 0.99.0, when agents were classes; nothing imports it now.
|
|
563
565
|
stageRemove(`${generatedDir}/agentCatalog.ts`);
|
|
@@ -715,9 +717,17 @@ function renderGeneratedRuntime(targetId: string): string {
|
|
|
715
717
|
/** Types only: the keys of the target's permissions for `can`, `access.can`, `access.filter` and `defineRoles`. */
|
|
716
718
|
function renderPermissions(): string {
|
|
717
719
|
const entries = [...permissionDeclarations.values()].sort((left, right) => left.file.localeCompare(right.file) || left.name.localeCompare(right.name));
|
|
718
|
-
const lines = ["// This file is auto-generated by `bazis codegen`.", "// Do not edit manually."
|
|
720
|
+
const lines = ["// This file is auto-generated by `bazis codegen`.", "// Do not edit manually."];
|
|
721
|
+
// Without an import or an export the file would be a script, and `declare module` would replace the module instead of adding to it.
|
|
722
|
+
lines.push(entries.length > 0 ? 'import type { PermissionsOf } from "bazis/core/access";' : "export {};");
|
|
719
723
|
entries.forEach((entry, index) => lines.push(`import type { ${entry.name} as Permissions_${index} } from ${JSON.stringify(toModuleSpecifierFrom(activeGeneratedDir, entry.file))};`));
|
|
720
|
-
lines.push("", 'declare module "bazis/core/access" {'
|
|
724
|
+
lines.push("", 'declare module "bazis/core/access" {');
|
|
725
|
+
if (entries.length > 0) lines.push(` interface BazisPermissions extends ${entries.map((_, index) => `PermissionsOf<typeof Permissions_${index}>`).join(", ")} {}`);
|
|
726
|
+
// The prefixes of `permissionSources`: `${prefix}.${string}` is a permission key too (ACCESS-001 §3.1).
|
|
727
|
+
if (permissionSourcePrefixes.size > 0) {
|
|
728
|
+
lines.push(" interface BazisPermissionSources {", ...[...permissionSourcePrefixes].sort().map((prefix) => ` ${JSON.stringify(prefix)}: true;`), " }");
|
|
729
|
+
}
|
|
730
|
+
lines.push("}", "");
|
|
721
731
|
return lines.join("\n");
|
|
722
732
|
}
|
|
723
733
|
|
|
@@ -1036,6 +1046,9 @@ function collectDiClassRegistrations(source: ts.SourceFile): void {
|
|
|
1036
1046
|
// `references: { "orders.order": OrderReferences }`: the field registers the classes.
|
|
1037
1047
|
const references = objectFor(properties.get("references"));
|
|
1038
1048
|
if (references) for (const entry of propertiesOf(references).values()) record(entry);
|
|
1049
|
+
// `permissionSources: { data: DataPermissionSource }`: the field registers the classes too.
|
|
1050
|
+
const permissionSources = objectFor(properties.get("permissionSources"));
|
|
1051
|
+
if (permissionSources) for (const entry of propertiesOf(permissionSources).values()) record(entry);
|
|
1039
1052
|
// `permissions: orderPermissions`: the field registers the `lookup` classes its permissions name.
|
|
1040
1053
|
for (const lookup of permissionLookups(properties.get("permissions"))) record(lookup);
|
|
1041
1054
|
const imports = properties.get("imports");
|
|
@@ -1177,6 +1190,22 @@ function collectProviderFormErrors(source: ts.SourceFile): void {
|
|
|
1177
1190
|
}
|
|
1178
1191
|
permissionDeclarations.set(`${file}\0${exported.name}`, { name: exported.name, file });
|
|
1179
1192
|
};
|
|
1193
|
+
/**
|
|
1194
|
+
* `permissionSources: { data: DataPermissionSource }`: the prefixes go to the
|
|
1195
|
+
* generated `permissions.ts`, which makes every key under them a permission
|
|
1196
|
+
* key for `can` and `access.filter`. A framework pack names its own in its sources.
|
|
1197
|
+
*/
|
|
1198
|
+
const collectPermissionSources = (module: string, initializer: ts.Expression): void => {
|
|
1199
|
+
const file = normalizeProjectPath(initializer.getSourceFile().fileName);
|
|
1200
|
+
if (file.includes("/node_modules/") || isFrameworkPackPath(file)) return;
|
|
1201
|
+
const object = resolved(initializer, ts.isObjectLiteralExpression);
|
|
1202
|
+
const named = object?.properties.every((property) => ts.isPropertyAssignment(property) && (ts.isIdentifier(property.name) || ts.isStringLiteralLike(property.name)));
|
|
1203
|
+
if (!object || named !== true) {
|
|
1204
|
+
fatalErrors.push(`BAZIS_ACCESS_SOURCES_FORM: ${sourceLocation(initializer)}: permissionSources of ${module}: "${text(initializer)}" — write an object "prefix: Class" in the module, so that the keys under the prefixes become the type of can(...).`);
|
|
1205
|
+
return;
|
|
1206
|
+
}
|
|
1207
|
+
for (const property of object.properties) permissionSourcePrefixes.add((property.name as ts.Identifier | ts.StringLiteralLike).text);
|
|
1208
|
+
};
|
|
1180
1209
|
const checkModule = (module: string, argument: ts.Expression | undefined): void => {
|
|
1181
1210
|
const object = argument && resolved(argument, ts.isObjectLiteralExpression);
|
|
1182
1211
|
if (!object) return;
|
|
@@ -1184,6 +1213,7 @@ function collectProviderFormErrors(source: ts.SourceFile): void {
|
|
|
1184
1213
|
if (!ts.isPropertyAssignment(property)) continue;
|
|
1185
1214
|
const field = property.name.getText().replace(/["']/g, "");
|
|
1186
1215
|
if (field === "permissions") collectPermissions(module, property.initializer);
|
|
1216
|
+
if (field === "permissionSources") collectPermissionSources(module, property.initializer);
|
|
1187
1217
|
if (field !== "providers" && !CLASS_LISTS.includes(field)) continue;
|
|
1188
1218
|
const list = resolved(property.initializer, ts.isArrayLiteralExpression);
|
|
1189
1219
|
if (!list) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bazis",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.140.0",
|
|
4
4
|
"description": "Modular backend framework for Bun in TypeScript: DI wired by codegen, HTTP, a PostgreSQL ORM, JWT, WebSocket, agents and a CLI.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Alexander Alenin",
|