Last active
September 4, 2022 12:10
-
-
Save colelawrence/fe797649a85cd8d3eece42fa2da0050b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// eslint-disable-next-line @typescript-eslint/no-explicit-any | |
type $IntentionalAny = any; | |
/** | |
* This special type can help generate pattern matchers for you! | |
* Just use it as so: | |
* // enum-ts | |
* type Result<Ok, Err> = Enum<{ | |
* Ok: Ok, | |
* Err: Err, | |
* }> | |
*/ | |
export type Enum<T extends { [Variant: string]: {} }> = { | |
[P in keyof T]: Expand< | |
Pick<T, P> & | |
Omit< | |
{ | |
[K in keyof T]?: never; | |
}, | |
P | |
> | |
>; | |
}[keyof T]; | |
type Expand<T> = T extends T | |
? { | |
[K in keyof T]: T[K]; | |
} | |
: never; | |
type U2I<U> = (U extends U ? (u: U) => 0 : never) extends (i: infer I) => 0 ? Extract<I, U> : never; | |
type ExcludeNevers<T> = { | |
[K in keyof T as Required<T>[K] extends never ? never : K]: T[K]; | |
}; | |
type EnumKeysObj<T> = U2I<{ | |
-readonly [K in keyof T]-?: unknown; | |
}>; | |
type EnumKeys<T> = T extends T ? keyof ExcludeNevers<T> : never; | |
type EnumProp<T, K extends PropertyKey> = Required<Pick<Extract<T, Partial<Record<K, unknown>>>, K>>[K]; | |
type EnumOmit<T, K extends PropertyKey> = Exclude<T, Record<K, unknown>>; | |
type Match<T, R = never, O = EnumKeysObj<ExcludeNevers<T>>> = EnumKeys<T> extends never | |
? { $(): R } | |
: [R] extends [never] | |
? { | |
[P in keyof O]: <R1>(cb: (value: EnumProp<T, P>) => R1) => Match<EnumOmit<T, P>, R1>; | |
} | |
: { | |
[P in keyof O]: (cb: (value: EnumProp<T, P>) => R) => Match<EnumOmit<T, P>, R>; | |
} & { | |
_(calculate: (values: T) => R): R; | |
}; | |
interface UndefinedMatch<T extends object | undefined | null> { | |
nullish<R>(value: () => R): Match<NonNullable<T>, R>; | |
} | |
const empty = Symbol("match never"); | |
const THEN = "then"; | |
const EXH = "$"; | |
const OTH = "_"; | |
export function match<T extends object | undefined | null>( | |
value: T, | |
): undefined extends T ? UndefinedMatch<T> : null extends T ? UndefinedMatch<T> : Match<T> { | |
// throw new Error("todo") | |
console.log("variant", { value }); | |
let found: unknown = empty; | |
const proxy = new Proxy( | |
{}, | |
{ | |
get(_, p): $IntentionalAny { | |
console.log(`get ${String(p)}`); | |
if (p === THEN) return undefined; // protect from Promise.resolve(...) | |
if (p === EXH) return () => found; | |
if (p === OTH) return found === empty ? (cb: (val: unknown) => unknown) => cb(value) : () => found; | |
if (found === empty && value && p in value) { | |
const inner = (value as $IntentionalAny)[p]; | |
return (cb: (inner: unknown) => unknown) => { | |
found = cb(inner); | |
return proxy; | |
}; | |
} else { | |
return () => proxy; | |
} | |
}, | |
}, | |
); | |
if (value == null) { | |
return { | |
nullish(cb: () => unknown) { | |
found = cb(); | |
return proxy; | |
}, | |
} as $IntentionalAny; | |
} else { | |
return proxy as $IntentionalAny; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { z, match } from "@autoplay/utils"; | |
import type { AnyID, ID, TypedStorage } from "artprompt-common"; | |
export namespace Security { | |
export enum MVPAccess { | |
/** For now, must have created this item */ | |
Owner, | |
} | |
export type Requirements<_TID extends AnyID> = MVPAccess; // | "anyone"; | |
export function canAccess<TDebugName extends string = string>( | |
access: AccessSummary<ID<TDebugName>>, | |
// At the moment, all requirements are the same... | |
_requirements: Requirements<ID<TDebugName>>, | |
): boolean { | |
return access.data === true; | |
// hmm: if no requirements, then also permit access | |
//|| requirements === MVPAccess.Anyone | |
} | |
export const accessSummaryType = z.literal(true).or(z.undefined()); // true indicates complete owner of this resource | |
// FUTURE: Have a set of bitflags for different access kinds (e.g. `{ pmt: 1 | 2 | 4 }`, see _todo/scopes.ts) | |
// empty obj would indicate no permissions | |
// .or(z.record(z.string().length(3), z.number()).default({})); | |
export type AccessSummary<_ extends AnyID> = { data: typeof accessSummaryType["_output"] }; | |
/** A data wrapping class for checking current access */ | |
export class Authenticated implements Security.Authenticated { | |
_agent: AnyID[]; | |
constructor(private _agentStorage: TypedStorage, agent: AnyID, chainOfCommand: AnyID[] = []) { | |
this._agent = [agent, ...chainOfCommand]; | |
} | |
_resItem(resourceID: AnyID) { | |
return this._agentStorage.item(resourceID.prefixed(), Security.accessSummaryType); | |
} | |
async summarize<R extends AnyID>(resourceID: R): Promise<Security.AccessSummary<R>> { | |
const item = this._resItem(resourceID); | |
return { | |
data: await item | |
.get({ _op: "accessSummary requires get summary" }) | |
.mapErrorTrpc("Error getting access summary", "INTERNAL_SERVER_ERROR"), | |
}; | |
} | |
apply(update: SecurityAccessUpdate): Promise<void> { | |
return match(update) | |
.GrantOwnership(({ to }) => | |
this._resItem(to) | |
.put(true) | |
.then(() => {}), | |
) | |
.Unlink(({ to }) => this._resItem(to).delete()) | |
.$(); | |
} | |
} | |
export type SecurityAccessUpdate = | |
| { | |
GrantOwnership: { to: AnyID }; | |
} | |
| { | |
Unlink: { to: AnyID }; | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment