Skip to content

Commit

Permalink
feat: add block visibility override
Browse files Browse the repository at this point in the history
  • Loading branch information
IncognitaDev committed Jan 16, 2025
1 parent 9dcf96b commit 74908d8
Show file tree
Hide file tree
Showing 8 changed files with 44 additions and 31 deletions.
4 changes: 3 additions & 1 deletion blocks/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ const actionBlock: Block<ActionModule> = {
TProps = any,
>(
mod: ActionModule<TProps>,
key: string,
) => [
applyProps(gateKeeper(mod)),
gateKeeper(mod.defaultVisibility, key),
applyProps(mod),
],
defaultPreview: (result) => {
return {
Expand Down
7 changes: 3 additions & 4 deletions blocks/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -334,13 +334,12 @@ const wrapLoader = (
const loaderBlock: Block<LoaderModule> = {
type: "loaders",
introspect: { includeReturn: true },
adapt: <TProps = any>(mod: LoaderModule<TProps>) => [
adapt: <TProps = any>(mod: LoaderModule<TProps>, key: string) => [
gateKeeper(mod.defaultVisibility, key),
wrapCaughtErrors,
(props: TProps, ctx: HttpContext<{ global: any } & RequestState>) =>
applyProps(
gateKeeper(
wrapLoader(mod, ctx.resolveChain, ctx.context.state.release),
),
wrapLoader(mod, ctx.resolveChain, ctx.context.state.release),
)(
props,
ctx,
Expand Down
43 changes: 20 additions & 23 deletions blocks/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import type { StatusCode as Status } from "@std/http/status";
import type { JSX } from "preact";
import type { AppManifest, ImportMap } from "../blocks/app.ts";
import { isInvokeCtx, LoaderModule } from "../blocks/loader.ts";
import { isInvokeCtx } from "../blocks/loader.ts";
import type { InvocationFunc } from "../clients/withManifest.ts";
import { withSection } from "../components/section.tsx";
import type {
Expand All @@ -27,6 +27,7 @@ import type { InvocationProxy } from "../utils/invoke.types.ts";
import { type Device, deviceOf, isBot as isUABot } from "../utils/userAgent.ts";
import type { HttpContext } from "./handler.ts";
import type { Vary } from "../utils/vary.ts";
import { Context } from "../mod.ts";

export interface GateKeeperAccess {
defaultVisibility?: "private" | "public";
Expand Down Expand Up @@ -254,26 +255,22 @@ export const buildImportMap = (manifest: AppManifest): ImportMap => {
return buildImportMapWith(manifest, builder);
};

export const gateKeeper = (
{
default: handler,
defaultVisibility = "public",
...rest
}: BlockModule & GateKeeperAccess,
) => {
return {
...rest,
default: async (
props: Parameters<typeof handler>[0],
req: Request,
ctx: FnContext<unknown, any>,
): Promise<ReturnType<typeof handler>> => {
if (defaultVisibility === "private" && !ctx.isInvoke) {
return new Response(null, {
status: 403,
});
}
return await handler(props, req, ctx);
},
export const gateKeeper =
(defaultVisibility: GateKeeperAccess["defaultVisibility"], key: string) =>
<
TContext extends ResolverMiddlewareContext<any> = ResolverMiddlewareContext<
any
>,
>(_props: unknown, ctx: TContext) => {
const currentContext = Context.active();

const visibility = currentContext.visibilityOverrides?.[key] ??
defaultVisibility ?? "public";

if (visibility === "private" && !isInvokeCtx(ctx)) {
return new Response(null, {
status: 403,
});
}
return ctx.next!();
};
};
10 changes: 9 additions & 1 deletion deco.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import type { ReleaseResolver } from "./engine/core/mod.ts";
import type { DecofileProvider } from "./engine/decofile/provider.ts";
import type { AppManifest } from "./types.ts";
import { randId } from "./utils/rand.ts";
import { BlockKeys } from "./mod.ts";
import { GateKeeperAccess } from "./blocks/utils.tsx";

export interface DecoRuntimeState<
TAppManifest extends AppManifest = AppManifest,
Expand Down Expand Up @@ -55,6 +57,12 @@ export interface DecoContext<TAppManifest extends AppManifest = AppManifest> {
runtime?: Promise<DecoRuntimeState<TAppManifest>>;
instance: InstanceInfo;
request?: RequestContext;

visibilityOverrides?: Record<
BlockKeys<TAppManifest> extends undefined ? string
: BlockKeys<TAppManifest>,
GateKeeperAccess["defaultVisibility"]
>;
}

export interface RequestContextBinder {
Expand Down Expand Up @@ -107,7 +115,7 @@ export const Context = {
// Function to retrieve the active context
active: <T extends AppManifest = AppManifest>(): DecoContext<T> => {
// Retrieve the context associated with the async ID
return asyncLocalStorage.getStore() ?? defaultContext;
return asyncLocalStorage.getStore() as DecoContext<T> ?? defaultContext;
},
bind: <R, TArgs extends unknown[]>(
ctx: DecoContext,
Expand Down
2 changes: 1 addition & 1 deletion engine/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ export type InstanceOf<
export type BlockTypes<TManifest extends AppManifest = AppManifest> =
keyof Omit<
TManifest,
"config" | "baseUrl"
"config" | "baseUrl" | "name"
>;

export type BlockKeys<TManifest extends AppManifest = AppManifest> = {
Expand Down
2 changes: 2 additions & 0 deletions engine/manifest/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,7 @@ export const newContext = <
instanceId: string | undefined = undefined,
site: string | undefined = undefined,
namespace: string = "site",
visibilityOverrides?: DecoContext<T>["visibilityOverrides"],
): Promise<DecoContext<T>> => {
const currentContext = Context.active<T>();
const ctx: DecoContext<T> = {
Expand All @@ -419,6 +420,7 @@ export const newContext = <
id: instanceId ?? randId(),
startedAt: new Date(),
},
visibilityOverrides,
};

return fulfillContext(ctx, m, currentImportMap, release);
Expand Down
4 changes: 3 additions & 1 deletion runtime/fresh/plugin.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// TODO make fresh plugin use @deco/deco from JSR. so that we can use the same code for both

import type { AppManifest, SiteInfo } from "@deco/deco";
import type { AppManifest, DecoContext, SiteInfo } from "@deco/deco";
import { Deco, type PageData, type PageParams } from "@deco/deco";
import { framework as htmxFramework } from "@deco/deco/htmx";
import type { ComponentType } from "preact";
Expand Down Expand Up @@ -51,6 +51,7 @@ export interface InitOptions<TManifest extends AppManifest = AppManifest> {
site?: SiteInfo;
deco?: Deco<TManifest>;
middlewares?: PluginMiddleware[];
visibilityOverrides?: DecoContext<TManifest>["visibilityOverrides"];
}

export type Options<TManifest extends AppManifest = AppManifest> =
Expand Down Expand Up @@ -89,6 +90,7 @@ export default function decoPlugin<TManifest extends AppManifest = AppManifest>(
site: opt?.site?.name,
namespace: opt?.site?.namespace,
bindings: { framework: opt?.htmx ? htmxFramework : framework },
visibilityOverrides: opt.visibilityOverrides,
});

const catchAll: PluginRoute = {
Expand Down
3 changes: 3 additions & 0 deletions runtime/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ export interface DecoOptions<TAppManifest extends AppManifest = AppManifest> {
manifest?: TAppManifest;
decofile?: DecofileProvider;
bindings?: Bindings<TAppManifest>;

visibilityOverrides?: DecoContext<TAppManifest>["visibilityOverrides"];
}

const NOOP_CALL = () => {};
Expand Down Expand Up @@ -86,6 +88,7 @@ export class Deco<TAppManifest extends AppManifest = AppManifest> {
crypto.randomUUID(),
site,
opts?.namespace,
opts?.visibilityOverrides,
)
);
Context.setDefault(decoContext);
Expand Down

0 comments on commit 74908d8

Please sign in to comment.