mirror of
https://fastgit.cc/https://github.com/anomalyco/opencode
synced 2026-04-21 13:21:17 +08:00
refactor(config): migrate permission.ts Info to Effect Schema (#23231)
This commit is contained in:
@@ -1,16 +1,8 @@
|
||||
export * as ConfigPermission from "./permission"
|
||||
import { Schema } from "effect"
|
||||
import z from "zod"
|
||||
import { zod } from "@/util/effect-zod"
|
||||
import { zod, ZodPreprocess } from "@/util/effect-zod"
|
||||
import { withStatics } from "@/util/schema"
|
||||
|
||||
const permissionPreprocess = (val: unknown) => {
|
||||
if (typeof val === "object" && val !== null && !Array.isArray(val)) {
|
||||
return { __originalKeys: globalThis.Object.keys(val), ...val }
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
export const Action = Schema.Literals(["ask", "allow", "deny"])
|
||||
.annotate({ identifier: "PermissionActionConfig" })
|
||||
.pipe(withStatics((s) => ({ zod: zod(s) })))
|
||||
@@ -26,6 +18,48 @@ export const Rule = Schema.Union([Action, Object])
|
||||
.pipe(withStatics((s) => ({ zod: zod(s) })))
|
||||
export type Rule = Schema.Schema.Type<typeof Rule>
|
||||
|
||||
// Captures the user's original property insertion order before Schema.Struct
|
||||
// canonicalises the object. See the `ZodPreprocess` comment in
|
||||
// `util/effect-zod.ts` for the full rationale — in short: rule precedence is
|
||||
// encoded in JSON key order (`evaluate.ts` uses `findLast`, so later keys win)
|
||||
// and `Schema.StructWithRest` would otherwise drop that order.
|
||||
const permissionPreprocess = (val: unknown) => {
|
||||
if (typeof val === "object" && val !== null && !Array.isArray(val)) {
|
||||
return { __originalKeys: globalThis.Object.keys(val), ...val }
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
const ObjectShape = Schema.StructWithRest(
|
||||
Schema.Struct({
|
||||
__originalKeys: Schema.optional(Schema.mutable(Schema.Array(Schema.String))),
|
||||
read: Schema.optional(Rule),
|
||||
edit: Schema.optional(Rule),
|
||||
glob: Schema.optional(Rule),
|
||||
grep: Schema.optional(Rule),
|
||||
list: Schema.optional(Rule),
|
||||
bash: Schema.optional(Rule),
|
||||
task: Schema.optional(Rule),
|
||||
external_directory: Schema.optional(Rule),
|
||||
todowrite: Schema.optional(Action),
|
||||
question: Schema.optional(Action),
|
||||
webfetch: Schema.optional(Action),
|
||||
websearch: Schema.optional(Action),
|
||||
codesearch: Schema.optional(Action),
|
||||
lsp: Schema.optional(Rule),
|
||||
doom_loop: Schema.optional(Action),
|
||||
skill: Schema.optional(Rule),
|
||||
}),
|
||||
[Schema.Record(Schema.String, Rule)],
|
||||
)
|
||||
|
||||
const InnerSchema = Schema.Union([ObjectShape, Action]).annotate({
|
||||
[ZodPreprocess]: permissionPreprocess,
|
||||
})
|
||||
|
||||
// Post-parse: drop the __originalKeys metadata and rebuild the rule map in the
|
||||
// user's original insertion order. A plain string input (the Action branch of
|
||||
// the union) becomes `{ "*": action }`.
|
||||
const transform = (x: unknown): Record<string, Rule> => {
|
||||
if (typeof x === "string") return { "*": x as Action }
|
||||
const obj = x as { __originalKeys?: string[] } & Record<string, unknown>
|
||||
@@ -38,34 +72,7 @@ const transform = (x: unknown): Record<string, Rule> => {
|
||||
return result
|
||||
}
|
||||
|
||||
export const Info = z
|
||||
.preprocess(
|
||||
permissionPreprocess,
|
||||
z
|
||||
.object({
|
||||
__originalKeys: z.string().array().optional(),
|
||||
read: Rule.zod.optional(),
|
||||
edit: Rule.zod.optional(),
|
||||
glob: Rule.zod.optional(),
|
||||
grep: Rule.zod.optional(),
|
||||
list: Rule.zod.optional(),
|
||||
bash: Rule.zod.optional(),
|
||||
task: Rule.zod.optional(),
|
||||
external_directory: Rule.zod.optional(),
|
||||
todowrite: Action.zod.optional(),
|
||||
question: Action.zod.optional(),
|
||||
webfetch: Action.zod.optional(),
|
||||
websearch: Action.zod.optional(),
|
||||
codesearch: Action.zod.optional(),
|
||||
lsp: Rule.zod.optional(),
|
||||
doom_loop: Action.zod.optional(),
|
||||
skill: Rule.zod.optional(),
|
||||
})
|
||||
.catchall(Rule.zod)
|
||||
.or(Action.zod),
|
||||
)
|
||||
export const Info = zod(InnerSchema)
|
||||
.transform(transform)
|
||||
.meta({
|
||||
ref: "PermissionConfig",
|
||||
})
|
||||
export type Info = z.infer<typeof Info>
|
||||
.meta({ ref: "PermissionConfig" })
|
||||
export type Info = Record<string, Rule>
|
||||
|
||||
Reference in New Issue
Block a user