Agent Skillswebiny/webiny-js › webiny-add-feature-flag

webiny-add-feature-flag

GitHub

指导在 Webiny 系统中添加新功能标志,涵盖 DTO 类型、已知标志联合、toDto 方法、Zod 验证及许可证装饰器模式,实现配置与权限的门控逻辑。

skills/repo-skills/add-feature-flag/SKILL.md webiny/webiny-js

Trigger Scenarios

需要新增功能开关 配置功能门控逻辑 集成许可证检查

Install

npx skills add webiny/webiny-js --skill webiny-add-feature-flag -g -y
More Options

Non-standard path

npx skills add https://github.com/webiny/webiny-js/tree/next/skills/repo-skills/add-feature-flag -g -y

Use without installing

npx skills use webiny/webiny-js@webiny-add-feature-flag

指定 Agent (Claude Code)

npx skills add webiny/webiny-js --skill webiny-add-feature-flag -a claude-code -g -y

安装 repo 全部 skill

npx skills add webiny/webiny-js --all -g -y

预览 repo 内 skill

npx skills add webiny/webiny-js --list

SKILL.md

Frontmatter
{
    "name": "webiny-add-feature-flag",
    "description": "Adding a new feature flag to the Webiny system. Use this skill when creating a new feature flag (simple boolean or nested group), gating a feature at the config\/admin\/API level, or wiring a flag into the WCP license system. Covers IFeatureFlagsDto, KnownFeatureFlag, Zod schema, FeatureFlag.CanUse components, useFeatureFlags().isEnabled(), the API FeatureFlags abstraction, toDto(), and the LICENSE_CHECKS decorator pattern."
}

Adding a New Feature Flag

A WCP license is required for feature flags to work. The license is the gate; the config is the switch within the gate.

Decision Flow

1. No license at all             → false (everything off, config ignored)
2. License blocks the flag       → false (config ignored)
3. License allows + config=false → false (config can disable what license allows)
4. License allows + config=true  → true
5. License allows + config unset → true  (license is the authority for unset flags)
6. Not in LICENSE_CHECKS + license exists + config unset → true
7. Not in LICENSE_CHECKS + license exists + config=false → false

Key points:

  • Config can disable what the license allows, but cannot enable what the license blocks.
  • Flags not governed by a license (LICENSE_CHECKS) still require a license to exist — then config decides.
  • Without any license, all flags are off regardless of config.

Architecture

  • FeatureFlags class (packages/feature-flags/src/FeatureFlags.ts) — single isEnabled(name) method resolves dot-path strings against the DTO. Flags are disabled by default (undefined → false). Also provides isExplicitlyDisabled(name) to distinguish "not set" from "set to false".
  • IFeatureFlagsDto (packages/feature-flags/src/types.ts) — the typed DTO interface.
  • KnownFeatureFlag (packages/feature-flags/src/FeatureFlags.ts) — string literal union for autocomplete.
  • Zod schema (packages/project/src/extensions/FeatureFlags.tsx) — validates the config input.
  • toDto() returns the fully resolved state (all flags explicitly set), used by the featureFlags GraphQL query.
  • License decorators intercept isEnabled() and apply the decision flow above via a LICENSE_CHECKS map.

Steps to Add a Simple Boolean Flag

1. Add to DTO type

File: packages/feature-flags/src/types.ts

Add the new flag to IFeatureFlagsDto:

export interface IFeatureFlagsDto {
  // ... existing flags
  myNewFeature?: boolean;
}

2. Add to KnownFeatureFlag union

File: packages/feature-flags/src/FeatureFlags.ts

Add the string to the KnownFeatureFlag type:

export type KnownFeatureFlag =
  // ... existing flags
  "myNewFeature";

3. Add to toDto()

File: packages/feature-flags/src/FeatureFlags.ts

Add the flag to the toDto() method so the API returns it:

toDto() {
    return {
        // ... existing flags
        myNewFeature: this.isEnabled("myNewFeature")
    };
}

4. Add to Zod schema

File: packages/project/src/extensions/FeatureFlags.tsx

Add to the paramsSchema so users get validation in webiny.config.tsx:

myNewFeature: z.boolean().optional();

5. Gate the feature

At the config level (controls whether extensions mount at build time):

// In the extension component (e.g., MyFeature.tsx)
import { FeatureFlag } from "@webiny/project";

export const MyFeature = () => (
    <FeatureFlag.CanUse name="myNewFeature">
        <Api.Extension src={...} />
        <Admin.Extension src={...} />
    </FeatureFlag.CanUse>
);

Or add a named convenience component in packages/project/src/components/FeatureFlag.tsx:

function CanUseMyNewFeature({ children }: { children: React.ReactNode }) {
  return <CanUse name="myNewFeature">{children}</CanUse>;
}

At the admin runtime level (controls UI visibility):

import { useFeatureFlags } from "@webiny/app-admin";

const featureFlags = useFeatureFlags();
if (!featureFlags.isEnabled("myNewFeature")) {
  return null;
}

At the API runtime level (controls backend behavior):

import { FeatureFlags } from "~/features/featureFlags/abstractions.js";

// In a DI-resolved class:
constructor(private featureFlags: FeatureFlags.Interface) {}

someMethod() {
    if (!this.featureFlags.get().isEnabled("myNewFeature")) {
        return;
    }
}

6. User configuration

Users configure flags in webiny.config.tsx:

export const FeatureFlags = () => (
  <Project.FeatureFlags
    features={{
      myNewFeature: false // disabled
    }}
  />
);

Omitting a flag means the license decides (enabled if licensed, disabled if not). Setting a flag to false disables it even if the license allows it.

Adding a Nested Flag Group

For flags with sub-options (like aiPowerups or advancedAccessControlLayer):

DTO type — use a union:

export interface IMyFeatureOptions {
  subFeatureA?: boolean;
  subFeatureB?: boolean;
}

export interface IFeatureFlagsDto {
  myFeature?: boolean | IMyFeatureOptions;
}

KnownFeatureFlag — add parent and children:

export type KnownFeatureFlag = "myFeature" | "myFeature.subFeatureA" | "myFeature.subFeatureB";

toDto() — collapse parent when disabled:

myFeature: this.isEnabled("myFeature")
  ? {
      subFeatureA: this.isEnabled("myFeature.subFeatureA"),
      subFeatureB: this.isEnabled("myFeature.subFeatureB")
    }
  : false;

Zod schema — union type:

myFeature: z.union([
  z.boolean(),
  z.object({
    subFeatureA: z.boolean().optional(),
    subFeatureB: z.boolean().optional()
  })
]).optional();

User config:

// Disable entirely
<Project.FeatureFlags features={{ myFeature: false }} />

// Disable specific sub-feature
<Project.FeatureFlags features={{ myFeature: { subFeatureA: false } }} />

WCP License Gating

A WCP license is required for any feature flag to work. Without a license, all flags return false.

Flags NOT in LICENSE_CHECKS (like remoteComponents): a license must exist, but the license doesn't explicitly govern this flag. Config decides. Do NOT add a flag to LICENSE_CHECKS until the WCP backend supports it.

Flags IN LICENSE_CHECKS: the license explicitly gates the feature. If the license blocks it, the flag is false regardless of config. If the license allows it, config can still disable it.

To make a flag license-governed, add it to the LICENSE_CHECKS map in all three decorators:

  • API level: packages/api-core/src/features/featureFlags/decorators/FeatureFlagsWithLicenseDecorator.ts
  • Build level: packages/project/src/decorators/GetFeatureFlagsWithLicense.ts
  • Config level: packages/project/src/services/GetProjectConfigService/LicenseDecoratedFeatureFlags.ts
const LICENSE_CHECKS: Record<string, (license: ILicense) => boolean> = {
  // ... existing checks
  myNewFeature: l => l.canUseMyNewFeature()
};

This also requires adding canUseMyNewFeature() to the ILicense interface and its implementations in @webiny/wcp (License.ts, NullLicense.ts). Only do this when the WCP backend supports the flag.

Files Reference

Purpose File
DTO type packages/feature-flags/src/types.ts
FeatureFlags class + KnownFeatureFlag packages/feature-flags/src/FeatureFlags.ts
Zod schema packages/project/src/extensions/FeatureFlags.tsx
Config-level CanUse components packages/project/src/components/FeatureFlag.tsx
Admin hook packages/app-admin/src/presentation/featureFlags/useFeatureFlags.ts
API abstraction packages/api-core/src/features/featureFlags/abstractions.ts
API license decorator packages/api-core/src/features/featureFlags/decorators/FeatureFlagsWithLicenseDecorator.ts
Build license decorator packages/project/src/decorators/GetFeatureFlagsWithLicense.ts
Config license decorator packages/project/src/services/GetProjectConfigService/LicenseDecoratedFeatureFlags.ts
GraphQL query packages/api-core/src/graphql/featureFlags/FeatureFlagsSchemaFactory.ts

Version History

  • 80eb1c5 Current 2026-08-20 10:06

Same Skill Collection

.claude/skills/grill-me/SKILL.md
.claude/skills/prd-to-plan/SKILL.md
.claude/skills/preflight/SKILL.md
.claude/skills/tester/SKILL.md
.claude/skills/write-a-prd/SKILL.md
skills/user-skills/admin/admin-architect/SKILL.md
skills/user-skills/admin/admin-permissions/SKILL.md
skills/user-skills/admin/form-model/SKILL.md
skills/user-skills/admin/new-entry-wizard/SKILL.md
skills/user-skills/admin/website-builder/page-settings/SKILL.md
skills/user-skills/admin/website-builder/wb-preview-url-modifier/SKILL.md
skills/user-skills/api-bundle-size-limit/SKILL.md
skills/user-skills/api/api-architect/SKILL.md
skills/user-skills/api/cms-bulk-actions/SKILL.md
skills/user-skills/api/custom-field-type/SKILL.md
skills/user-skills/api/event-handler-pattern/SKILL.md
skills/user-skills/api/graphql-api/SKILL.md
skills/user-skills/api/http-route/SKILL.md
skills/user-skills/api/permissions/SKILL.md
skills/user-skills/api/use-case-pattern/SKILL.md
skills/user-skills/api/v5-to-v6-migration/SKILL.md
skills/user-skills/api/websocket-notifications/SKILL.md
skills/user-skills/cli-extensions/SKILL.md
skills/user-skills/configure-auth0/SKILL.md
skills/user-skills/configure-entraid/SKILL.md
skills/user-skills/configure-okta/SKILL.md
skills/user-skills/dependency-injection/SKILL.md
skills/user-skills/full-stack-architect/SKILL.md
skills/user-skills/generated/api/aco/SKILL.md
skills/user-skills/generated/api/cms/SKILL.md
skills/user-skills/generated/api/file-manager/SKILL.md
skills/user-skills/generated/api/scheduler/SKILL.md
skills/user-skills/generated/api/security/SKILL.md
skills/user-skills/generated/api/system/SKILL.md
skills/user-skills/generated/api/tenancy/SKILL.md
skills/user-skills/generated/api/tenant-manager/SKILL.md
skills/user-skills/generated/api/website-builder/SKILL.md
skills/user-skills/generated/infra/SKILL.md
skills/user-skills/infrastructure-extensions/SKILL.md
skills/user-skills/local-development/SKILL.md
skills/user-skills/mailer-smtp/SKILL.md
skills/user-skills/project-structure/SKILL.md
.claude/skills/webiny-skill-creator/SKILL.md
skills/user-skills/admin/ui-extensions/SKILL.md
skills/user-skills/api/ai-powerups-content/SKILL.md
skills/user-skills/cognito-federation/SKILL.md
skills/user-skills/content-models/SKILL.md
skills/user-skills/generated/admin/aco/SKILL.md
skills/user-skills/generated/admin/ai-powerups/SKILL.md

Metadata

Files
0
Version
80eb1c5
Hash
46749f02
Indexed
2026-08-20 10:06

inicio - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-08-22 01:47
浙ICP备14020137号-1 $mapa de visitantes$