create-ec-app 1.6.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portalContainers.js","sourceRoot":"","sources":["../src/portalContainers.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-ec-app",
3
- "version": "1.6.0",
3
+ "version": "1.8.0",
4
4
  "description": "Unified CLI tool to create different types of EC applications: Webresource, Portal, Power Pages, Power Apps Code Apps",
5
5
  "bin": {
6
6
  "create-ec-app": "./dist/index.js"
@@ -23,30 +23,51 @@ const root = postcss.parse(css, { from: cssPath });
23
23
  const failures = [];
24
24
 
25
25
  root.walkRules((rule) => {
26
- if (!rule.nodes?.some((node) => node.type === "decl" && node.prop.startsWith("--"))) {
26
+ if (isKeyframesRule(rule)) {
27
27
  return;
28
28
  }
29
29
 
30
30
  for (const selector of splitSelectorList(rule.selector)) {
31
- if (!selector.includes(".ec-pcf-shell-control")) {
32
- failures.push(`unscoped custom-property rule: ${selector}`);
31
+ if (!selector.includes(".pcf-shell-control")) {
32
+ failures.push(`unscoped PCF rule: ${selector}`);
33
33
  }
34
34
  }
35
35
  });
36
36
 
37
- if (!css.includes("[data-ec-pcf-control=")) {
37
+ if (!css.includes("[data-pcf-control=")) {
38
38
  failures.push("missing PCF control data attribute scope");
39
39
  }
40
40
 
41
+ if (css.includes(".ec-pcf-shell-control") || css.includes("[data-ec-pcf-control=")) {
42
+ failures.push("legacy EC PCF scope is still present");
43
+ }
44
+
41
45
  if (failures.length > 0) {
42
- console.error("PCF CSS variable scope check failed:");
46
+ console.error("PCF CSS scope check failed:");
43
47
  for (const failure of failures) {
44
48
  console.error(`- ${failure}`);
45
49
  }
46
50
  process.exit(1);
47
51
  }
48
52
 
49
- console.log("PCF CSS variable scope check passed.");
53
+ console.log("PCF CSS scope check passed.");
54
+
55
+ function isKeyframesRule(rule) {
56
+ let parent = rule.parent;
57
+
58
+ while (parent) {
59
+ if (
60
+ parent.type === "atrule" &&
61
+ parent.name.toLowerCase().endsWith("keyframes")
62
+ ) {
63
+ return true;
64
+ }
65
+
66
+ parent = parent.parent;
67
+ }
68
+
69
+ return false;
70
+ }
50
71
 
51
72
  function splitSelectorList(selectorList) {
52
73
  const selectors = [];
@@ -4,7 +4,6 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
4
4
 
5
5
  import "./index.css";
6
6
  import App from "./App.tsx";
7
- import { EcAppShell } from "./runtime/EcAppShell.tsx";
8
7
 
9
8
  const queryClient = new QueryClient({
10
9
  defaultOptions: {
@@ -24,9 +23,7 @@ const root = createRoot(document.getElementById("root")!);
24
23
  root.render(
25
24
  <StrictMode>
26
25
  <QueryClientProvider client={queryClient}>
27
- <EcAppShell>
28
- <App />
29
- </EcAppShell>
26
+ <App />
30
27
  </QueryClientProvider>
31
28
  </StrictMode>
32
29
  );
@@ -9,6 +9,29 @@ npm install
9
9
  npm run build
10
10
  ```
11
11
 
12
+ ## Regenerate From Webresource Changes
13
+
14
+ Do not edit this generated PCF folder as the durable source of truth. From the webresource root, rebuild and regenerate:
15
+
16
+ ```bash
17
+ npm run build
18
+ npx create-ec-app@latest \
19
+ --pcf-dir . \
20
+ --output ./pcf/{{PCF_CONSTRUCTOR}} \
21
+ --namespace {{PCF_NAMESPACE}} \
22
+ --constructor {{PCF_CONSTRUCTOR}} \
23
+ --display-name "{{CONTROL_DISPLAY_NAME}}"
24
+ cd pcf/{{PCF_CONSTRUCTOR}}
25
+ npm install
26
+ npm run build
27
+ ```
28
+
29
+ Then run the harness:
30
+
31
+ ```bash
32
+ npm run start -- --no-open
33
+ ```
34
+
12
35
  ## Control Info
13
36
 
14
37
  - Namespace: `{{PCF_NAMESPACE}}`
@@ -18,14 +41,15 @@ npm run build
18
41
 
19
42
  ## Notes
20
43
 
21
- - The wrapper imports `src/App` directly and uses generated `pcf-scoped.css` derived from the webresource build.
44
+ - The wrapper imports `src/App` directly, renders it through the local PCF shell, and uses generated `pcf-scoped.css` derived from the webresource build.
45
+ - Source-app CSS imports are ignored by the PCF webpack config because the built CSS is already included through `pcf-scoped.css`.
22
46
  - Regenerate this folder after rebuilding the webresource whenever the app changes.
23
47
  - The project includes both `pcf-scripts` build support and a `.pcfproj` for Dataverse solution packaging flows.
24
48
 
25
49
  ## CSS scoping
26
50
 
27
- This PCF control renders the app inside the PCF host container class `.ec-pcf-shell-control`.
51
+ This PCF control renders the app inside the PCF host container class `.pcf-shell-control`.
28
52
 
29
- During PCF generation, `create-ec-app` reads the webresource's built `dist/main.css`, scopes CSS custom-property rules to this control's `.ec-pcf-shell-control[data-ec-pcf-control="{{PCF_CONSTRUCTOR}}"]` host selector, and writes the result to `pcf-scoped.css`.
53
+ During PCF generation, `create-ec-app` reads the webresource's built `dist/main.css`, scopes CSS selectors to this control's `.pcf-shell-control[data-pcf-control="{{PCF_CONSTRUCTOR}}"]` host selector, and writes the result to `pcf-scoped.css`.
30
54
 
31
- Tailwind utilities remain unprefixed, while shadcn/Tailwind theme variables are local to the generated PCF control. Radix/shadcn portals render into the app-local portal root where supported.
55
+ Tailwind utilities and base selectors are isolated under the generated PCF host selector. Radix/shadcn portals render into the PCF-local portal root where supported.
@@ -4,7 +4,7 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
4
4
 
5
5
  import App from "{{PROJECT_APP_IMPORT}}";
6
6
  import "{{PROJECT_CSS_IMPORT}}";
7
- import { EcAppShell } from "{{PROJECT_EC_APP_SHELL_IMPORT}}";
7
+ import { PcfAppShell } from "./runtime/PcfAppShell";
8
8
  import type { IInputs, IOutputs } from "./control/generated/ManifestTypes";
9
9
  import type {
10
10
  PcfRuntimeContext,
@@ -16,13 +16,37 @@ function sanitizeGuid(value: string | null | undefined): string | null {
16
16
  return value.replace(/[{}]/g, "");
17
17
  }
18
18
 
19
+ function getPageClientUrl(
20
+ pageContext:
21
+ | {
22
+ clientUrl?: unknown;
23
+ getClientUrl?: unknown;
24
+ }
25
+ | undefined,
26
+ ): string | null {
27
+ const getClientUrl = pageContext?.getClientUrl;
28
+ if (typeof getClientUrl === "function") {
29
+ try {
30
+ const clientUrl = getClientUrl.call(pageContext);
31
+ return typeof clientUrl === "string" ? clientUrl : null;
32
+ } catch {
33
+ return null;
34
+ }
35
+ }
36
+
37
+ return typeof pageContext?.clientUrl === "string"
38
+ ? pageContext.clientUrl
39
+ : null;
40
+ }
41
+
19
42
  function getContextInfo(context: ComponentFramework.Context<IInputs>) {
20
43
  const pageContext = (
21
44
  context as ComponentFramework.Context<IInputs> & {
22
45
  page?: {
46
+ clientUrl?: unknown;
23
47
  entityId?: string;
24
48
  entityTypeName?: string;
25
- getClientUrl?: () => string;
49
+ getClientUrl?: unknown;
26
50
  };
27
51
  }
28
52
  ).page;
@@ -39,7 +63,7 @@ function getContextInfo(context: ComponentFramework.Context<IInputs>) {
39
63
  ),
40
64
  entityName:
41
65
  modeContextInfo?.entityTypeName ?? pageContext?.entityTypeName ?? null,
42
- clientUrl: pageContext?.getClientUrl?.() ?? null,
66
+ clientUrl: getPageClientUrl(pageContext),
43
67
  userId: sanitizeGuid(context.userSettings.userId),
44
68
  };
45
69
  }
@@ -112,8 +136,8 @@ export class {{PCF_CONSTRUCTOR}}
112
136
  _state: ComponentFramework.Dictionary,
113
137
  container: HTMLDivElement,
114
138
  ): void {
115
- container.classList.add("ec-pcf-shell-control");
116
- container.dataset.ecPcfControl = "{{PCF_CONSTRUCTOR}}";
139
+ container.classList.add("pcf-shell-control");
140
+ container.dataset.pcfControl = "{{PCF_CONSTRUCTOR}}";
117
141
  this.runtime = createRuntime(context);
118
142
  this.root = createRoot(container);
119
143
  this.render();
@@ -144,7 +168,7 @@ export class {{PCF_CONSTRUCTOR}}
144
168
  StrictMode,
145
169
  null,
146
170
  React.createElement(
147
- EcAppShell,
171
+ PcfAppShell,
148
172
  null,
149
173
  React.createElement(
150
174
  QueryClientProvider,
@@ -0,0 +1,17 @@
1
+ import * as React from "react";
2
+
3
+ import { PortalContainerContext } from "{{PROJECT_PORTAL_CONTAINER_IMPORT}}";
4
+
5
+ export function PcfAppShell({ children }: { children: React.ReactNode }) {
6
+ const [portalContainer, setPortalContainer] =
7
+ React.useState<HTMLDivElement | null>(null);
8
+
9
+ return (
10
+ <div data-pcf-app-root="">
11
+ <PortalContainerContext.Provider value={portalContainer}>
12
+ {children}
13
+ <div data-pcf-portal-root="" ref={setPortalContainer} />
14
+ </PortalContainerContext.Provider>
15
+ </div>
16
+ );
17
+ }
@@ -0,0 +1 @@
1
+ module.exports = {};
@@ -1,4 +1,7 @@
1
1
  const path = require("path");
2
+ const webpack = require("webpack");
3
+
4
+ const projectSrcPath = path.resolve(__dirname, "{{PROJECT_SRC_ALIAS}}");
2
5
 
3
6
  module.exports = {
4
7
  resolve: {
@@ -8,4 +11,14 @@ module.exports = {
8
11
  "react-dom": path.resolve(__dirname, "{{PROJECT_REACT_DOM_ALIAS}}"),
9
12
  },
10
13
  },
14
+ plugins: [
15
+ new webpack.NormalModuleReplacementPlugin(
16
+ /\.(css|scss|sass)$/,
17
+ (resource) => {
18
+ if (path.resolve(resource.context).startsWith(projectSrcPath)) {
19
+ resource.request = path.resolve(__dirname, "runtime/emptyStyles.js");
20
+ }
21
+ },
22
+ ),
23
+ ],
11
24
  };
@@ -0,0 +1,187 @@
1
+ ## Purpose
2
+
3
+ This repository is a Power Apps code app using React, TypeScript, Vite, and the Power Apps code app SDK.
4
+
5
+ Treat it as a Power Apps-hosted code app, not a Dynamics web resource. Keep it small, readable, and easy to push with the Power Apps tooling.
6
+
7
+ Use the global AGENTS.md rules first. This file adds project-specific constraints.
8
+
9
+ ## Hard Constraints
10
+
11
+ - Keep Power Apps code app hosting working.
12
+ - Keep local Power Apps play support working.
13
+ - Keep generated connector and Dataverse service access working.
14
+ - Keep the app client-side.
15
+ - Make surgical changes.
16
+
17
+ Do not add Dynamics `Xrm`, `token.json`, Power Pages ADAL, Static Web Apps routing, or direct Dataverse Web API auth patterns unless the target changes.
18
+
19
+ ## Runtime Modes
20
+
21
+ The app supports two modes.
22
+
23
+ ### Power Apps-hosted
24
+
25
+ - The Power Apps host manages end-user authentication, app loading, and runtime context.
26
+ - Use `@microsoft/power-apps` APIs when app, environment, user, host, or query-parameter context is needed.
27
+ - Use generated models and services for Dataverse and connector access.
28
+ - Preserve `power.config.json` semantics after `npx power-apps init`.
29
+ - Do not assume model-driven app `window.Xrm` context exists.
30
+
31
+ ### Local dev
32
+
33
+ - Run Vite through `npm run dev`.
34
+ - Open the Local Play URL printed by the Power Apps Vite plugin.
35
+ - Use the same browser profile as the target Power Platform tenant.
36
+ - Run `npx power-apps init` before real push/data-source work.
37
+ - Treat `power.config.example.json` as documentation only; do not rename it to bypass initialization.
38
+
39
+ Do not mix this with webresource `authService.ts`, `token.json`, or Power Pages `AuthContext`.
40
+
41
+ ## Critical Files
42
+
43
+ | File | Rule |
44
+ |---|---|
45
+ | `vite.config.ts` | Preserve `powerApps()` plus React, Tailwind, and alias config. |
46
+ | `power.config.json` | Generated by `npx power-apps init`; environment-specific app metadata and data references live here. |
47
+ | `power.config.example.json` | Reference only. Do not make it the real config before initialization. |
48
+ | `src/generated/models/*` | Generated data-source models. Do not hand-edit unless the generator output is known wrong. |
49
+ | `src/generated/services/*` | Generated service methods for Dataverse/connectors/actions/functions. Prefer these over custom clients. |
50
+ | `src/main.tsx` | Preserve bootstrap, providers, and global theme/style imports. |
51
+
52
+ ## API and Data Access
53
+
54
+ Prefer generated Power Apps services.
55
+
56
+ Use:
57
+
58
+ - `npx power-apps add-data-source` for Dataverse and connector data sources
59
+ - `npx power-apps add-dataverse-api` for Dataverse actions/functions
60
+ - generated service methods from `src/generated/services`
61
+ - generated model types from `src/generated/models`
62
+ - `getContext` from `@microsoft/power-apps/app` when host context is needed
63
+ - narrow `select` options when generated Dataverse services support them
64
+ - clear errors when generated calls fail
65
+
66
+ Avoid:
67
+
68
+ - direct browser calls to Dataverse Web API
69
+ - copied webresource `getApiUrl()` / `getAuthHeaders()` helpers
70
+ - manually maintained connector clients
71
+ - wrapping every generated service in another generic service layer
72
+ - repository/client layers for small features
73
+ - Zod schemas for every generated response
74
+ - silent fallbacks for failed connector or Dataverse calls
75
+
76
+ For known data sources, use the generated service for that source. Add a new data source only when the current feature needs it.
77
+
78
+ ## Validation
79
+
80
+ Use generated TypeScript models for normal connector and Dataverse response shapes.
81
+
82
+ Use runtime validation only when the current feature needs it, such as:
83
+
84
+ - user-entered form data
85
+ - URL/query parameters that control behavior
86
+ - data sent to create/update/delete operations
87
+ - genuinely variable connector data where the UI must branch safely
88
+ - security-sensitive or data-loss-prone paths
89
+
90
+ Do not validate values just because they are shaped like GUIDs, logical names, dates, URLs, or enum strings. If the generated service or platform will reject the value clearly and there is no local UX/security need, pass it through.
91
+
92
+ Do not normalize strings by default. Trim, lowercase, strip braces, or reformat only when the app has a known input source that sends multiple formats.
93
+
94
+ ## Services, Queries, and Mutations
95
+
96
+ Keep service files explicit.
97
+
98
+ Preferred shape:
99
+
100
+ - call the generated service directly from a small domain service when naming the operation helps
101
+ - one TanStack Query hook when components need query state
102
+ - one mutation hook when mutation state or invalidation is needed
103
+ - query keys colocated with the hook when reused for invalidation
104
+
105
+ Do not create wrapper chains such as:
106
+
107
+ ```text
108
+ resolveConfig -> normalizeInput -> validateInput -> resolveGeneratedService -> buildRequest -> executeRequest
109
+ ```
110
+
111
+ Prefer direct flow:
112
+
113
+ ```text
114
+ call generated service -> check result/error -> return typed data
115
+ ```
116
+
117
+ ## State Management
118
+
119
+ - Use TanStack Query for server/connector state.
120
+ - Use local component state for local UI behavior.
121
+ - Use Zustand only for shared client state that has outgrown local state.
122
+ - Do not store server state in Zustand.
123
+ - Do not add Redux unless explicitly requested.
124
+
125
+ ## UI and Styling
126
+
127
+ Stay consistent with the project's existing UI system.
128
+
129
+ - Shadcn/ui projects: use existing `@/components/ui` components and Tailwind utilities.
130
+ - Kendo projects: use Kendo React components for rich controls and Tailwind for layout/composition.
131
+ - Preserve the existing theme and global CSS imports.
132
+ - Do not mix UI systems unless explicitly asked.
133
+ - Do not hand-roll custom CSS unless component props and Tailwind are not enough.
134
+ - Keep layouts compact, scannable, responsive, and suitable for Power Apps hosting.
135
+
136
+ ## Code Shape
137
+
138
+ Prefer:
139
+
140
+ - focused React components
141
+ - direct typed functions
142
+ - existing generated services and components
143
+ - small local helpers only when they remove real duplication or name non-obvious domain logic
144
+ - explicit connector/table/action handling over generic frameworks
145
+
146
+ Avoid:
147
+
148
+ - broad factories
149
+ - generic service clients
150
+ - classes for simple service logic
151
+ - excessive configuration
152
+ - defensive wrappers around every value
153
+ - broad refactors while adding a feature
154
+
155
+ ## Error Handling
156
+
157
+ Connector calls, Dataverse reads/saves/deletes, auth/context failures, uploads, downloads, and required parsing failures should throw.
158
+
159
+ Do not swallow failed generated service calls and treat them as empty data unless the requirement explicitly says the feature is best-effort.
160
+
161
+ Include useful operation context and platform error text where practical.
162
+
163
+ ## Build and Deployment
164
+
165
+ Do not replace Vite, remove `powerApps()`, add SSR, add Next.js, or change the app into a webresource/Power Pages/SWA deployment unless explicitly asked.
166
+
167
+ Use `npm run build` for production assets and `npx power-apps push` for the npm CLI path. Use PAC CLI only when a required option is not available through the npm CLI.
168
+
169
+ ## Checks
170
+
171
+ Run the smallest relevant command for the changed area, such as:
172
+
173
+ - typecheck
174
+ - lint
175
+ - targeted tests
176
+ - Vite build when deployment shape could be affected
177
+ - local Power Apps play smoke test when SDK/plugin/data-source behavior changes
178
+
179
+ Do not run broad expensive checks unless the change touches shared infrastructure or the project requires it.
180
+
181
+ ## Figma MCP
182
+
183
+ When using the Figma MCP server, ensure that you are not just blindly copying the designs. Take note and always place a focus on the following:
184
+
185
+ - Ensure responsiveness on all screen sizes
186
+ - If there are icons as part of the design, use those, don't blindly look for Lucide-React equivalents.
187
+ - Use the exact colours in the design. Don't make up your own.
@@ -0,0 +1,192 @@
1
+ ## Purpose
2
+
3
+ This repository is a Power Pages single-page application using React, TypeScript, and Vite.
4
+
5
+ Treat it as a Power Pages code site. Keep it small, readable, and easy to upload to Power Pages.
6
+
7
+ Use the global AGENTS.md rules first. This file adds project-specific constraints.
8
+
9
+ ## Hard Constraints
10
+
11
+ - Keep Power Pages code-site deployment working.
12
+ - Keep Power Pages `_api` access working.
13
+ - Keep local Vite development working.
14
+ - Keep the app client-side.
15
+ - Make surgical changes.
16
+
17
+ Do not add Dynamics `Xrm`, `token.json`, Static Web Apps routing, or Power Apps code app SDK patterns unless the target changes.
18
+
19
+ ## Runtime Modes
20
+
21
+ The app supports two modes.
22
+
23
+ ### Power Pages-hosted
24
+
25
+ - Run as a browser SPA hosted by Power Pages.
26
+ - Use Power Pages `_api` for Dataverse access from the site.
27
+ - Rely on Power Pages web roles, table permissions, and site settings for data authorization.
28
+ - Preserve code-site build output and `powerpages.config.json`.
29
+ - Do not call the Dataverse organization Web API directly from the browser.
30
+ - Do not assume `window.Xrm` or model-driven app context exists.
31
+
32
+ ### Local dev
33
+
34
+ - Use Vite for local UI development.
35
+ - Use the existing Vite `/_api` proxy when local code needs to call a Power Pages site.
36
+ - Keep the proxy target explicit and environment-specific.
37
+ - Use the existing `AuthContext` flow only where this template already requires it.
38
+ - Never commit client secrets, tenant-specific secrets, or real token values.
39
+
40
+ Do not mix Power Pages auth with webresource `authService.ts` or `token.json`.
41
+
42
+ ## Critical Files
43
+
44
+ | File | Rule |
45
+ |---|---|
46
+ | `src/context/AuthContext.tsx` | Current auth boundary for this template. Reuse it; do not add a second auth system. |
47
+ | `src/main.tsx` | Preserve `AuthProvider`, `QueryClientProvider`, bootstrap, and global style imports. |
48
+ | `vite.config.ts` | Preserve React, Tailwind, alias, and the `/_api` dev proxy when API calls are used locally. |
49
+ | `powerpages.config.json` | Power Pages code-site configuration. Keep compiled path and landing page accurate. |
50
+ | `src/App.tsx` | Keep app behavior client-side and provider-aware. |
51
+
52
+ ## API and Data Access
53
+
54
+ Prefer direct, boring Power Pages Web API calls.
55
+
56
+ Use:
57
+
58
+ - relative `_api/...` URLs
59
+ - the existing auth context only where the current template flow already uses it
60
+ - Power Pages request verification/CSRF handling where the portal Web API requires it
61
+ - Power Pages table permissions and web roles for authorization
62
+ - narrow `$select` queries
63
+ - `URLSearchParams` for normal query parameters
64
+ - direct `fetch` inside service files
65
+ - small TypeScript interfaces for response shapes
66
+ - clear `response.ok` checks with useful status text
67
+
68
+ Avoid:
69
+
70
+ - direct calls to `https://<org>.crm*.dynamics.com/api/data/v9.2` from the browser
71
+ - duplicated auth contexts
72
+ - raw fetch calls inside UI components
73
+ - repository/client layers for small features
74
+ - generic OData builders
75
+ - Zod schemas for every Power Pages response
76
+ - GUID or logical-name regex validation by default
77
+ - silent fallbacks for failed Power Pages calls
78
+
79
+ For known tables, use known entity set names. Fetch metadata only when the feature truly supports arbitrary table names.
80
+
81
+ Escape OData string literals when interpolating inside quoted OData expressions. Do not create a broad escaping/parsing layer for simple queries.
82
+
83
+ ## Validation
84
+
85
+ Use TypeScript types for normal Power Pages Web API response shapes.
86
+
87
+ Use runtime validation only when the current feature needs it, such as:
88
+
89
+ - user-entered form data
90
+ - URL/search parameters that control behavior
91
+ - local config that can be wrong
92
+ - genuinely variable API data where the UI must branch safely
93
+ - security-sensitive or data-loss-prone paths
94
+
95
+ Do not validate values just because they are shaped like GUIDs, logical names, dates, URLs, or enum strings. If Power Pages or Dataverse will reject the value clearly and there is no local UX/security need, pass it through.
96
+
97
+ Do not normalize strings by default. Trim, lowercase, strip braces, or reformat only when the app has a known input source that sends multiple formats.
98
+
99
+ ## Services, Queries, and Mutations
100
+
101
+ Keep service files explicit.
102
+
103
+ Preferred shape:
104
+
105
+ - one fetch/save function for the operation
106
+ - one TanStack Query hook when components need it
107
+ - one mutation hook when mutation state or invalidation is needed
108
+ - query keys colocated with the hook when reused for invalidation
109
+
110
+ Do not create wrapper chains such as:
111
+
112
+ ```text
113
+ resolveConfig -> normalizeInput -> validateInput -> resolveMetadata -> buildRequest -> executeRequest
114
+ ```
115
+
116
+ Prefer direct flow:
117
+
118
+ ```text
119
+ read auth/context -> fetch -> check response -> return typed data
120
+ ```
121
+
122
+ ## State Management
123
+
124
+ - Use TanStack Query for server state.
125
+ - Use local component state for local UI behavior.
126
+ - Use Zustand only for shared client state that has outgrown local state.
127
+ - Do not store server state in Zustand.
128
+ - Do not add Redux unless explicitly requested.
129
+
130
+ ## UI and Styling
131
+
132
+ Stay consistent with the project's existing UI system.
133
+
134
+ - Shadcn/ui projects: use existing `@/components/ui` components and Tailwind utilities.
135
+ - Kendo projects: use Kendo React components for rich controls and Tailwind for layout/composition.
136
+ - Preserve the existing theme and global CSS imports.
137
+ - Do not mix UI systems unless explicitly asked.
138
+ - Do not hand-roll custom CSS unless component props and Tailwind are not enough.
139
+ - Keep layouts compact, scannable, responsive, and suitable for Power Pages.
140
+
141
+ ## Code Shape
142
+
143
+ Prefer:
144
+
145
+ - focused React components
146
+ - direct typed functions
147
+ - existing auth, services, and components
148
+ - small local helpers only when they remove real duplication or name non-obvious domain logic
149
+ - explicit Power Pages table/field handling over generic frameworks
150
+
151
+ Avoid:
152
+
153
+ - broad factories
154
+ - generic service clients
155
+ - classes for simple service logic
156
+ - excessive configuration
157
+ - defensive wrappers around every value
158
+ - broad refactors while adding a feature
159
+
160
+ ## Error Handling
161
+
162
+ Power Pages reads, saves, deletes, uploads, downloads, auth failures, and required parsing failures should throw.
163
+
164
+ Do not swallow failed fetches and treat them as "not found" unless the requirement explicitly says the feature is best-effort.
165
+
166
+ Include response status and useful response text where practical.
167
+
168
+ ## Build and Deployment
169
+
170
+ Do not replace Vite, add SSR, add Next.js, change the code-site output shape, or introduce backend coupling unless explicitly asked.
171
+
172
+ Keep output deployable through the Power Pages code-site flow and keep `powerpages.config.json` aligned with the built `dist` folder.
173
+
174
+ ## Checks
175
+
176
+ Run the smallest relevant command for the changed area, such as:
177
+
178
+ - typecheck
179
+ - lint
180
+ - targeted tests
181
+ - Vite build when deployment shape could be affected
182
+ - local `/_api` proxy smoke test when API routing changes
183
+
184
+ Do not run broad expensive checks unless the change touches shared infrastructure or the project requires it.
185
+
186
+ ## Figma MCP
187
+
188
+ When using the Figma MCP server, ensure that you are not just blindly copying the designs. Take note and always place a focus on the following:
189
+
190
+ - Ensure responsiveness on all screen sizes
191
+ - If there are icons as part of the design, use those, don't blindly look for Lucide-React equivalents.
192
+ - Use the exact colours in the design. Don't make up your own.