feat: declare module permissions
Establishes the one canonical PermissionDeclaration contract (packages/contracts/src/authorization.ts) — id/baseline/description, strict, unknown-property-rejecting — consumed by both Core's authorization registry and module manifests, replacing Core's previously separate interface. Bumps the canonical module manifest to manifestVersion 3: permissions become complete declarations instead of bare identifier strings, with manifest-owned namespace and duplicate-id enforcement. Version 2 and bare string-array permissions are rejected outright; there is no compatibility adapter. Adds composePlatformPermissionRegistry (core/src/modules/permissions.ts), a pure, synchronous composer that builds one platform permission registry from Core's own declarations plus every already-discovered module's, in deterministic order, reusing createPermissionRegistry and rejecting any module-contributed permission in the reserved core.* namespace. core/src/authorization remains independent of core/src/modules; only the module integration layer depends on authorization. Updates the module template, module-development docs, authorization docs, and existing manifest fixtures for manifestVersion 3. Does not wire the combined registry into apps/api — that is pass 2.
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
import { z } from "zod";
|
||||
import { type PermissionDeclaration, permissionDeclarationSchema } from "./authorization.js";
|
||||
import {
|
||||
capabilityIdSchema,
|
||||
logicalBucketIdSchema,
|
||||
moduleIdSchema,
|
||||
packageRelativePathSchema,
|
||||
permissionIdSchema,
|
||||
} from "./identifiers.js";
|
||||
import { semanticVersionRangeSchema, semanticVersionSchema } from "./versions.js";
|
||||
|
||||
@@ -89,7 +89,7 @@ function reportDuplicates(
|
||||
|
||||
export const moduleManifestSchema = z
|
||||
.object({
|
||||
manifestVersion: z.literal(2),
|
||||
manifestVersion: z.literal(3),
|
||||
id: moduleIdSchema,
|
||||
name: z.string().trim().min(1).max(120),
|
||||
description: z.string().trim().min(1).max(500).optional(),
|
||||
@@ -102,7 +102,7 @@ export const moduleManifestSchema = z
|
||||
.strict(),
|
||||
dependencies: dependenciesSchema,
|
||||
capabilities: z.array(moduleCapabilityDeclarationSchema).default(() => []),
|
||||
permissions: z.array(permissionIdSchema).default(() => []),
|
||||
permissions: z.array(permissionDeclarationSchema).default(() => []),
|
||||
storage: moduleStorageSchema,
|
||||
entrypoints: entrypointsSchema,
|
||||
})
|
||||
@@ -114,7 +114,12 @@ export const moduleManifestSchema = z
|
||||
context,
|
||||
"id",
|
||||
);
|
||||
reportDuplicates(manifest.permissions, ["permissions"], context);
|
||||
reportDuplicates(
|
||||
manifest.permissions.map((permission) => permission.id),
|
||||
["permissions"],
|
||||
context,
|
||||
"id",
|
||||
);
|
||||
reportDuplicates(
|
||||
manifest.storage.buckets.map((bucket) => bucket.id),
|
||||
["storage", "buckets"],
|
||||
@@ -133,11 +138,11 @@ export const moduleManifestSchema = z
|
||||
});
|
||||
|
||||
manifest.permissions.forEach((permission, index) => {
|
||||
if (!permission.startsWith(`${manifest.id}.`)) {
|
||||
if (!permission.id.startsWith(`${manifest.id}.`)) {
|
||||
context.addIssue({
|
||||
code: "custom",
|
||||
message: `Permission identifiers owned by this module must begin with ${manifest.id}.`,
|
||||
path: ["permissions", index],
|
||||
path: ["permissions", index, "id"],
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -197,3 +202,7 @@ export type ModuleManifestInput = z.input<typeof moduleManifestSchema>;
|
||||
export type ModuleManifest = z.output<typeof moduleManifestSchema>;
|
||||
export type ModuleBucketDeclaration = z.output<typeof moduleBucketDeclarationSchema>;
|
||||
export type ModuleCapabilityDeclaration = z.output<typeof moduleCapabilityDeclarationSchema>;
|
||||
/** A module manifest's `permissions` array entry — the same `PermissionDeclaration` shape Core's
|
||||
* authorization registry consumes (`packages/contracts/src/authorization.ts`), aliased here for
|
||||
* discoverability alongside the manifest's other per-field declaration types. */
|
||||
export type ModulePermissionDeclaration = PermissionDeclaration;
|
||||
|
||||
Reference in New Issue
Block a user