@dataverse-kit/export-engine 1.0.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,658 @@
1
+ import { FormDefinition, BusinessRuleDefinition, RuleVariable, WorkflowDefinition, BusinessProcessFlowDefinition, FormSelectorRule, GridCustomizerDefinition, ControlType, GridColumnDefinition, SubgridColumn, GridColumnDataType } from '@dataverse-kit/form-runtime/types';
2
+ export { V8_ICONS_WITHOUT_V9, V8_PERSONA_SIZE_TO_V9, V8_PRESENCE_TO_V9_STATUS, V8_TO_V9_ICON_MAP, V9IconName, buildV9IconImport, mapV8IconToV9, mapV8PersonaSizeToV9, mapV8PresenceToV9Status, sanitizeV8EnumIdentifier, sanitizeV8PersonaSizeName, sanitizeV8PresenceName } from '@dataverse-kit/form-runtime/icons';
3
+
4
+ /**
5
+ * Export-pipeline types owned by export-engine.
6
+ *
7
+ * These are export concepts (target / method / options) — they belong to the
8
+ * export package, not the app. The form-builder app and the design-only fork
9
+ * each keep their own `types/export.ts`; those are structurally identical, so
10
+ * an app's `ExportOptions`/`ExportTarget` is assignable at the `exportProject`
11
+ * call boundary without either app importing from the other.
12
+ */
13
+ type ExportTarget = 'web-resource' | 'pcf' | 'static-web-app';
14
+ type ExportMethod = 'zip' | 'folder';
15
+ type DeployTarget = {
16
+ type: 'none';
17
+ } | {
18
+ type: 'app';
19
+ appId: string;
20
+ title?: string;
21
+ };
22
+ interface ExportOptions {
23
+ target: ExportTarget;
24
+ projectName: string;
25
+ namespace?: string;
26
+ publisherPrefix?: string;
27
+ includeDeployScript: boolean;
28
+ minify: boolean;
29
+ exportMethod: ExportMethod;
30
+ dataverseUrl?: string;
31
+ deployTarget?: DeployTarget;
32
+ includeSampleData?: boolean;
33
+ /**
34
+ * When set, overrides the smart default for generating the DAL (models,
35
+ * constants, hooks). When undefined, the generator computes a default from
36
+ * the project's bindings and saved preferences.
37
+ */
38
+ generateDataAccessLayer?: boolean;
39
+ /**
40
+ * Per-export override for the component library used by generated code.
41
+ * Precedence: this field → `project.componentLibrary` → `'fluent-v8'`.
42
+ */
43
+ componentLibrary?: 'fluent-v8' | 'fluent-v9';
44
+ /**
45
+ * When true, the export engine additionally emits a "rich entity models"
46
+ * tree under `src/entities/` produced by `@dataverse-kit/entity-gen`
47
+ * (only when the rich-entity generator is registered — see ExportEngine's
48
+ * registration seam). Additive; the `generateDataAccessLayer` output is
49
+ * untouched.
50
+ */
51
+ useRichEntityModels?: boolean;
52
+ /**
53
+ * Snapshot of Dataverse metadata cached in the UI at export time, keyed by
54
+ * entity logical name. ExportEngine is a pure library — it can't reach into
55
+ * a store — so the snapshot is plumbed through here. Only consumed when
56
+ * `useRichEntityModels` is on.
57
+ */
58
+ dataverseMetadataSnapshot?: Record<string, unknown>;
59
+ }
60
+
61
+ /**
62
+ * Project model as export-engine sees it.
63
+ *
64
+ * Owned here (not imported from an app) so the package has no dependency on
65
+ * `apps/*`. The Dataverse-subsystem fields are optional and typed from the
66
+ * shared `@dataverse-kit/form-runtime` types — so both the form-builder app's
67
+ * full project (which populates them) and the design-only fork's stripped
68
+ * project (which omits them) are structurally assignable at the export
69
+ * boundary. Each app still owns its own canonical `FormBuilderProject`; this is
70
+ * the structural contract export-engine consumes.
71
+ */
72
+
73
+ /**
74
+ * Export-time options that persist per project so the user's last choice is
75
+ * remembered. Generators still compute smart defaults when a field is unset.
76
+ */
77
+ interface ProjectExportOptions {
78
+ generateDataAccessLayer?: boolean;
79
+ includeSampleData?: boolean;
80
+ }
81
+ interface FormBuilderProject {
82
+ id: string;
83
+ name: string;
84
+ description: string;
85
+ exportTarget: ExportTarget;
86
+ componentLibrary: 'fluent-v8' | 'fluent-v9';
87
+ forms: FormDefinition[];
88
+ createdAt: string;
89
+ updatedAt: string;
90
+ dataverseUrl?: string;
91
+ /** Schema version for project-level migrations. */
92
+ schemaVersion: number;
93
+ businessRules?: BusinessRuleDefinition[];
94
+ sharedVariables?: RuleVariable[];
95
+ workflows?: WorkflowDefinition[];
96
+ businessProcessFlows?: BusinessProcessFlowDefinition[];
97
+ formSelectorRules?: FormSelectorRule[];
98
+ gridCustomizers?: GridCustomizerDefinition[];
99
+ /** Per-project export preferences that survive across sessions. */
100
+ exportOptions?: ProjectExportOptions;
101
+ }
102
+
103
+ /**
104
+ * Shared helper for resolving the effective component library (Fluent UI v8
105
+ * or v9) for a given export. Generators call this so the precedence rule
106
+ * lives in one place.
107
+ *
108
+ * Phase 3a wires this through to every generator but no generator branches
109
+ * on it yet — output remains v8 for now. Phases 3b–3e add the v9 code paths.
110
+ */
111
+
112
+ type ComponentLibrary = 'fluent-v8' | 'fluent-v9';
113
+ /**
114
+ * Returns the effective component library for a project/export.
115
+ *
116
+ * Priority:
117
+ * 1. Explicit ExportOptions.componentLibrary if present
118
+ * 2. Project-level FormBuilderProject.componentLibrary
119
+ * 3. 'fluent-v8' (legacy default — preserves current behavior)
120
+ */
121
+ declare function resolveComponentLibrary(project: FormBuilderProject, options?: ExportOptions): ComponentLibrary;
122
+
123
+ /** Result of generating code for a single form. */
124
+ interface GeneratedFile {
125
+ path: string;
126
+ content: string;
127
+ }
128
+ /** Options for form code generation. */
129
+ interface GenerateFormCodeOptions {
130
+ /** 'overlay' wraps in Dialog/Panel/Callout; 'fullpage' renders directly for solo web resources. */
131
+ renderMode?: 'overlay' | 'fullpage';
132
+ /** When true, integrates useBusinessRules hook for runtime visibility/lock/required. */
133
+ hasBusinessRules?: boolean;
134
+ /** Grid customizer definitions from the project — enables inline renderer generation for linked subgrids. */
135
+ gridCustomizers?: GridCustomizerDefinition[];
136
+ /** When true, populates subgrids with sample data instead of empty arrays. */
137
+ includeSampleData?: boolean;
138
+ /**
139
+ * When true, subgrids emit helper components that consume the DAL hooks
140
+ * generated by EntityDataLayerGenerator. Callers should only enable this
141
+ * when they are also emitting the DAL files alongside the form code.
142
+ */
143
+ includeDataAccessLayer?: boolean;
144
+ /**
145
+ * Reserved for the v9 code path. Phase 3a wires this through but the
146
+ * generator currently emits Fluent UI v8 only. Phases 3b-3e will branch
147
+ * imports, control mappings, theme provider, and styles based on this.
148
+ */
149
+ componentLibrary?: 'fluent-v8' | 'fluent-v9';
150
+ }
151
+ /** Generate React component source files from a FormDefinition. */
152
+ declare function generateFormCode(form: FormDefinition, options?: GenerateFormCodeOptions): GeneratedFile[];
153
+ /** Options for `generateAllFormCode`. */
154
+ interface GenerateAllFormCodeOptions {
155
+ /** Per-form render mode. Defaults to 'overlay' for any form not listed. */
156
+ renderModes?: Record<string, 'overlay' | 'fullpage'>;
157
+ /** When true, integrates useBusinessRules hook for runtime visibility/lock/required. */
158
+ hasBusinessRules?: boolean;
159
+ /** Grid customizer definitions from the project — enables inline renderer generation for linked subgrids. */
160
+ gridCustomizers?: GridCustomizerDefinition[];
161
+ /** When true, populates subgrids with sample data instead of empty arrays. */
162
+ includeSampleData?: boolean;
163
+ /** When true, subgrids emit helper components that consume the DAL hooks. */
164
+ includeDataAccessLayer?: boolean;
165
+ /** Reserved for the v9 code path; Phase 3a does not branch on this. */
166
+ componentLibrary?: 'fluent-v8' | 'fluent-v9';
167
+ }
168
+ /** Generate code for all forms in a project and an index barrel. */
169
+ declare function generateAllFormCode(forms: FormDefinition[], options?: GenerateAllFormCodeOptions): GeneratedFile[];
170
+
171
+ /**
172
+ * Phase 3f.1 review fix — shared v9 control-type compatibility constants.
173
+ *
174
+ * Both `v9CompatibilityScanner` (consumer-facing) and `FormCodeGenerator`
175
+ * (generator-facing) need to know which control types lack a Fluent UI
176
+ * v9 equivalent. Extracting the constant + predicate here breaks the
177
+ * scanner→generator import edge that the architectural review flagged
178
+ * and gives any future consumer (UI badges, custom-control manifests,
179
+ * exporter validation) a stable, dependency-free import target.
180
+ *
181
+ * Edit `V9_INCOMPATIBLE_CONTROL_TYPES` to add a new Track B control.
182
+ * Reasons live in the scanner module (UI-facing copy).
183
+ */
184
+
185
+ /**
186
+ * Set of control types with no Fluent UI v9 equivalent. Track B in the
187
+ * Phase 3f migration plan — these controls are excluded from v9 exports
188
+ * (placeholder div emitted, ExportDialog warning shown) rather than
189
+ * migrated.
190
+ */
191
+ declare const V9_INCOMPATIBLE_CONTROL_TYPES: ReadonlySet<ControlType>;
192
+ /**
193
+ * Test whether a control type can be emitted under Fluent UI v9. A
194
+ * compatible control either has a v9 equivalent today or has one
195
+ * planned in a future Phase 3f sub-phase (currently emitting v8
196
+ * fallback inside FluentProvider — still renders correctly).
197
+ */
198
+ declare function isControlV9Compatible(controlType: ControlType): boolean;
199
+ /**
200
+ * Phase 1b — symmetric counterpart to `V9_INCOMPATIBLE_CONTROL_TYPES`.
201
+ *
202
+ * Set of control types with no Fluent UI v8 equivalent in
203
+ * `@dataverse-kit/components`. These controls can only render under v9
204
+ * export targets — ExportDialog blocks v8-only targets (Power Pages Web
205
+ * Resource, Canvas App PCF) when the project uses any v9-required control.
206
+ *
207
+ * Currently flagged via `requiresV9: true` on the ControlRegistryEntry in
208
+ * `apps/form-builder/src/utils/controlMetadata.ts`. The constant here is
209
+ * the authoritative list for the export-engine scanner; keeping the names
210
+ * synced between the form-builder registry and this set is enforced by
211
+ * tests in `v9CompatibilityScanner.test.ts`.
212
+ */
213
+ declare const V9_REQUIRED_CONTROL_TYPES: ReadonlySet<ControlType>;
214
+ /**
215
+ * Test whether a control type requires Fluent UI v9. A v9-required
216
+ * control has no v8 equivalent in the workspace component library and
217
+ * therefore cannot render under v8 export targets.
218
+ */
219
+ declare function isControlV9Required(controlType: ControlType): boolean;
220
+
221
+ /**
222
+ * Phase 3f.1 — v9 compatibility scanner.
223
+ *
224
+ * Walks a `FormBuilderProject` and reports every control whose type
225
+ * has no Fluent UI v9 equivalent. Used by:
226
+ * 1. ExportDialog — pre-export warning when user picks v9 + project
227
+ * contains incompatible controls. User can cancel or proceed; if
228
+ * they proceed, the generator emits a placeholder for the control
229
+ * instead of the v8 component.
230
+ * 2. Designer canvas/palette — future hookup to badge incompatible
231
+ * controls with a ⚠ icon when project's componentLibrary is v9.
232
+ *
233
+ * **Track A** controls (have v9 equivalent — to be migrated in 3f.2–3f.7)
234
+ * are NOT reported. Until their migration ships they keep emitting v8
235
+ * fallback inside FluentProvider, which works correctly.
236
+ *
237
+ * **Track B** controls (no v9 equivalent — permanently excluded under v9):
238
+ * - `rating` — no v9 control
239
+ * - `timeline` — custom Dynamics surface, no v9 equivalent
240
+ * - `webresource` — embeds v8 HTML resources, not applicable to v9
241
+ *
242
+ * Custom controls (Item 6, future) will declare their own
243
+ * `componentLibraryCompatibility` in their manifest. When that lands,
244
+ * extend `isControlV9Compatible` to consult the manifest registry.
245
+ */
246
+
247
+ /**
248
+ * Why a control is incompatible with v9. Today only one reason exists,
249
+ * but the scanner returns it as a tagged value so the warning UI can
250
+ * render different copy per category and so future categories (e.g.
251
+ * `'custom-control-no-v9-decl'` from Item 6) slot in cleanly.
252
+ */
253
+ type V9IncompatibilityReason = 'no-v9-control';
254
+ /** A specific incompatible control's location in the project. */
255
+ interface V9IncompatibleRef {
256
+ /** Control type string (e.g. `'rating'`). */
257
+ controlType: ControlType;
258
+ /** Author-friendly name for the control (e.g. `'starRating'`). */
259
+ controlName: string;
260
+ /** Author-friendly label (e.g. `'Customer Satisfaction'`). */
261
+ controlLabel: string;
262
+ /** Reason category for the warning UI. */
263
+ reason: V9IncompatibilityReason;
264
+ /** Human-readable explanation for the warning UI. */
265
+ reasonText: string;
266
+ /** Form containing the control. */
267
+ formId: string;
268
+ formName: string;
269
+ /** Tab containing the control (only for main forms). */
270
+ tabId?: string;
271
+ tabLabel?: string;
272
+ /** Section containing the control. `'header'` for header cells, `'callout:<id>'` for callout sections. */
273
+ sectionId: string;
274
+ sectionLabel: string;
275
+ /** Cell containing the control. */
276
+ cellId: string;
277
+ }
278
+ /** Result returned from `scanV9Compatibility`. */
279
+ interface V9CompatibilityResult {
280
+ /** Controls that will be omitted from v9 output (placeholder emitted instead). */
281
+ excluded: V9IncompatibleRef[];
282
+ /** Convenience flag: true iff `excluded.length === 0`. */
283
+ isCompatible: boolean;
284
+ }
285
+ /**
286
+ * Walk a project and collect every control whose type is in the v9
287
+ * incompatibility set. Idempotent and side-effect-free; safe to call
288
+ * from React render paths.
289
+ */
290
+ declare function scanV9Compatibility(project: FormBuilderProject): V9CompatibilityResult;
291
+ /**
292
+ * Format a result as a single human-readable summary string. Used by
293
+ * ExportDialog when the warning needs to be inlined (e.g. small
294
+ * MessageBar variants without a list view). Returns empty string when
295
+ * `result.isCompatible` is true.
296
+ */
297
+ declare function summarizeV9Incompatibility(result: V9CompatibilityResult): string;
298
+ /**
299
+ * Why a control requires v9. Today only one reason exists (no v8 equivalent
300
+ * in `@dataverse-kit/components`), but the tagged value lets future
301
+ * categories — e.g. custom controls declaring `requiresV9` in their
302
+ * manifest — slot in cleanly.
303
+ */
304
+ type V9RequirementReason = 'no-v8-control';
305
+ /** A specific v9-required control's location in the project. */
306
+ interface V9RequiredRef {
307
+ controlType: ControlType;
308
+ controlName: string;
309
+ controlLabel: string;
310
+ reason: V9RequirementReason;
311
+ reasonText: string;
312
+ formId: string;
313
+ formName: string;
314
+ tabId?: string;
315
+ tabLabel?: string;
316
+ sectionId: string;
317
+ sectionLabel: string;
318
+ cellId: string;
319
+ }
320
+ /** Result returned from `scanV9Requirements`. */
321
+ interface V9RequirementResult {
322
+ /** Controls that cannot render under v8 export targets. */
323
+ required: V9RequiredRef[];
324
+ /** Convenience flag: true iff `required.length === 0`. */
325
+ isV8Compatible: boolean;
326
+ }
327
+ /**
328
+ * Walk a project and collect every control whose type is in the v9-required
329
+ * set. Uses the shared `walkProjectControls` engine so designer/preview/
330
+ * generator/scanner all visit the same surfaces by construction.
331
+ *
332
+ * Used by:
333
+ * 1. ExportDialog — disables v8-only target options + shows error
334
+ * MessageBar listing the offending controls when the project has any.
335
+ * 2. `validateExportTarget` in the export-engine entry — rejects v8
336
+ * target exports with a typed `V9RequirementError` for headless
337
+ * callers that skip the dialog.
338
+ */
339
+ declare function scanV9Requirements(project: FormBuilderProject): V9RequirementResult;
340
+ /**
341
+ * Single human-readable summary for the requirement result. Mirrors
342
+ * `summarizeV9Incompatibility` so ExportDialog can use either consistently.
343
+ */
344
+ declare function summarizeV9Requirements(result: V9RequirementResult): string;
345
+
346
+ /**
347
+ * Rich-entity (Dataverse codegen) generator — injected via a registration
348
+ * seam instead of a static import so the entity-gen package is NOT pulled into
349
+ * browser builds that don't use it. The full root barrel (`./index`) registers
350
+ * the real `generateRichEntityModelFiles`; the browser barrel (`./browser`)
351
+ * does not register anything, so design-only consumers never bundle entity-gen.
352
+ * When unregistered, `generateProjectFiles` simply emits no rich-entity files.
353
+ */
354
+ type RichEntityGenerator = (project: FormBuilderProject, options: ExportOptions) => GeneratedFile[];
355
+ declare function registerRichEntityGenerator(fn: RichEntityGenerator | null): void;
356
+ /**
357
+ * Thrown by `exportProject` / `validateExportTarget` when the chosen
358
+ * export target cannot host a v9-required control. Callers (CLI, headless
359
+ * pipelines, or the ExportDialog as a defense-in-depth) should catch this
360
+ * and surface the offending control list rather than retrying.
361
+ */
362
+ declare class V9RequirementError extends Error {
363
+ readonly required: V9RequiredRef[];
364
+ constructor(required: V9RequiredRef[]);
365
+ }
366
+ /**
367
+ * Pre-export gate: when the resolved component library is v8 and the
368
+ * project contains any v9-required controls (funnel/scatter), reject
369
+ * the export with a typed error listing the offenders.
370
+ *
371
+ * The ExportDialog blocks this via UI affordances, but headless callers
372
+ * (CLI, batch pipelines, future automation) bypass the dialog — keeping
373
+ * the gate at the engine layer protects them too.
374
+ *
375
+ * **Critical**: resolves the effective library via `resolveComponentLibrary`
376
+ * before checking. `options.componentLibrary` is allowed to be undefined
377
+ * (precedence: option → project.componentLibrary → 'fluent-v8'). Without
378
+ * the resolver the gate would fail open when a caller passes undefined on
379
+ * a project whose stored default is v8.
380
+ */
381
+ declare function validateExportTarget(project: FormBuilderProject, options: ExportOptions): {
382
+ ok: true;
383
+ } | {
384
+ ok: false;
385
+ reason: V9RequirementError;
386
+ };
387
+ /** Export project using the chosen method (ZIP download or folder picker). */
388
+ declare function exportProject(project: FormBuilderProject, options: ExportOptions): Promise<void>;
389
+ /**
390
+ * Preview generated files without downloading. Does NOT enforce the
391
+ * v9-required gate — preview is a read-only inspection used by the dialog
392
+ * to show the file tree even when the export would be blocked. Callers
393
+ * that need the gate should invoke `validateExportTarget` themselves.
394
+ */
395
+ declare function previewExport(project: FormBuilderProject, options: ExportOptions): GeneratedFile[];
396
+
397
+ /** Generate a complete CRA + rewire project for Dynamics 365 Web Resources. */
398
+ declare function generateWebResourceProject(project: FormBuilderProject, options?: ExportOptions): GeneratedFile[];
399
+
400
+ /** Generate a PCF control scaffold that embeds the generated forms. */
401
+ declare function generatePcfProject(project: FormBuilderProject, options?: ExportOptions): GeneratedFile[];
402
+
403
+ /** Generate a standard Vite + React project for Azure Static Web Apps or standalone hosting. */
404
+ declare function generateStaticWebAppProject(project: FormBuilderProject, options?: ExportOptions): GeneratedFile[];
405
+
406
+ /**
407
+ * BusinessRuleCodeGenerator — Generates TypeScript code for business rules.
408
+ *
409
+ * Outputs:
410
+ * - Rule definitions as typed constants
411
+ * - useBusinessRules hook for runtime evaluation
412
+ * - FetchXML helper utilities for data source resolution
413
+ * - Deployment manifest for Dataverse rule table
414
+ */
415
+
416
+ interface BusinessRuleCodeGenOptions {
417
+ /** Include inline comments explaining rule logic */
418
+ includeComments?: boolean;
419
+ /** Generate deployment manifest for Dataverse */
420
+ generateManifest?: boolean;
421
+ /** Entity logical name for the rules */
422
+ entityLogicalName?: string;
423
+ /** Publisher prefix for web resources */
424
+ publisherPrefix?: string;
425
+ }
426
+ /**
427
+ * Generate all business rule related files.
428
+ */
429
+ declare function generateBusinessRuleCode(rules: BusinessRuleDefinition[], workflows?: WorkflowDefinition[], sharedVariables?: RuleVariable[], options?: BusinessRuleCodeGenOptions): GeneratedFile[];
430
+
431
+ /**
432
+ * Generates a typed Dataverse data-access layer (constants + models + hooks +
433
+ * sample data) from the entities referenced by a project's forms and/or grid
434
+ * customizers.
435
+ *
436
+ * The form-builder already owns every piece of metadata needed to stop emitting
437
+ * `items={[]}` subgrid stubs: bound-control attribute logical names and types,
438
+ * option-set values with labels, lookup target lists, FetchXML data sources,
439
+ * and grid customizer data sources. This generator walks that metadata and
440
+ * produces:
441
+ *
442
+ * src/constants/{Entity}Constants.ts — attribute logical names + option set enums
443
+ * src/models/{Entity}.ts — BaseModel subclass + static list/retrieve/save
444
+ * src/hooks/use{Entities}.ts — useDataverseQuery-backed list + single record
445
+ * src/sampleData/{entity}.json — mock rows matching the model shape (optional)
446
+ * src/constants/index.ts, models/index.ts, hooks/index.ts — barrels
447
+ * src/lib/dataverse/* — inline runtime (BaseModel, hooks, FetchXML builder)
448
+ *
449
+ * Generated files import shared primitives from `../lib/dataverse` (inlined by
450
+ * `dalInlineTemplates`) so exported projects are self-contained.
451
+ */
452
+
453
+ interface GenerateDataLayerOptions {
454
+ /** When true, `src/sampleData/{entity}.json` files are emitted. */
455
+ includeSampleData?: boolean;
456
+ }
457
+ interface EntityUsageAttribute {
458
+ logicalName: string;
459
+ /** Raw `attributeType` from the control binding ("SingleLineOfText", "DateTime", etc.) */
460
+ rawType: string;
461
+ /** Normalized type used by constants/models/schema. */
462
+ type: 'string' | 'number' | 'integer' | 'boolean' | 'date' | 'datetime' | 'lookup' | 'optionset' | 'money' | 'memo';
463
+ required: boolean;
464
+ maxLength?: number;
465
+ lookupTargets?: string[];
466
+ optionSetOptions?: Array<{
467
+ value: number;
468
+ label: string;
469
+ }>;
470
+ }
471
+ interface EntityUsage {
472
+ entityName: string;
473
+ entityCollectionName: string;
474
+ primaryIdAttribute: string;
475
+ primaryNameAttribute: string;
476
+ displayName: string;
477
+ attributes: Map<string, EntityUsageAttribute>;
478
+ /** Base FetchXML literal (from the form's data source, if any) that list hooks can parameterize. */
479
+ baseFetchXml?: string;
480
+ }
481
+ /**
482
+ * Walks all forms and returns an `EntityUsage` map keyed by entity logical name.
483
+ *
484
+ * When `gridCustomizers` is passed, subgrids whose `gridCustomizerId` resolves
485
+ * to a grid definition with a `dataSource` contribute the richer FetchXML +
486
+ * attribute list from that grid, replacing the bare `entityName`-only path.
487
+ */
488
+ declare function collectEntityUsage(forms: FormDefinition[], gridCustomizers?: GridCustomizerDefinition[]): Map<string, EntityUsage>;
489
+ /**
490
+ * Builds an `EntityUsage` map from a single grid customizer's data source.
491
+ * Used by the grid-scoped DAL generator (standalone PCF / components-only).
492
+ *
493
+ * When the grid has a `nestedRelationship` pointing at a linked nested grid,
494
+ * the nested child entity is walked too so the generated DAL emits model +
495
+ * constants + hook files for both parent and child.
496
+ */
497
+ declare function collectEntityUsageFromGrid(grid: GridCustomizerDefinition, allGrids?: GridCustomizerDefinition[]): Map<string, EntityUsage>;
498
+ /**
499
+ * Form-driven entry point — returns generated files for every entity referenced
500
+ * by the given forms, resolving subgrid → gridCustomizerId → dataSource links.
501
+ */
502
+ declare function generateDataAccessLayer(forms: FormDefinition[], options?: GenerateDataLayerOptions, gridCustomizers?: GridCustomizerDefinition[]): GeneratedFile[];
503
+ /**
504
+ * Grid-driven entry point — emits the DAL file tree for a single grid
505
+ * customizer. Used by Standalone PCF and Components Only grid exports.
506
+ */
507
+ declare function generateDataAccessLayerForGrid(grid: GridCustomizerDefinition, options?: GenerateDataLayerOptions, allGrids?: GridCustomizerDefinition[]): GeneratedFile[];
508
+
509
+ /**
510
+ * Strip non-alphanumeric chars (keep spaces/underscores/dashes as split
511
+ * points), then PascalCase. Result is always a valid TypeScript/JavaScript
512
+ * identifier — leading digits get a `_` prefix because `export class 123`
513
+ * would otherwise be a syntax error in the generated code.
514
+ */
515
+ declare function toPascalCase(s: string): string;
516
+ /** PascalCase name that always ends with exactly one "Form" suffix. */
517
+ declare function toFormComponentName(s: string): string;
518
+ /** Safe kebab-case for file/folder names. */
519
+ declare function toSafeFileName(s: string): string;
520
+
521
+ interface CommandEntry {
522
+ command: string;
523
+ description: string;
524
+ category: 'setup' | 'development' | 'build' | 'deploy';
525
+ }
526
+ declare const categoryLabels: Record<CommandEntry['category'], string>;
527
+ declare const CATEGORY_ORDER: CommandEntry['category'][];
528
+
529
+ declare function getCommandReference(target: ExportTarget, projectName: string, namespace?: string): CommandEntry[];
530
+ /** Generate a README.md from the command reference. */
531
+ declare function generateReadme(target: ExportTarget, projectName: string, namespace?: string): string;
532
+
533
+ /**
534
+ * Shared helpers for deciding whether to emit the generated data-access
535
+ * layer and for injecting the required package dependency into generated
536
+ * `package.json` files.
537
+ *
538
+ * Downstream generators (WebResource, StaticWebApp, PCF) call these so the
539
+ * DAL integration logic lives in one place.
540
+ */
541
+
542
+ /**
543
+ * The DAL runtime is inlined into generated projects under `src/lib/dataverse/`,
544
+ * so no private npm dependency is required. Only the React Query dependency
545
+ * (which is public) gets injected into the generated `package.json`.
546
+ */
547
+ /**
548
+ * Returns the effective "generate data access layer" flag for a project/export.
549
+ *
550
+ * Priority:
551
+ * 1. Explicit ExportOptions.generateDataAccessLayer if present
552
+ * 2. Project-level saved preference under project.exportOptions.generateDataAccessLayer
553
+ * 3. Smart default — on when any form has at least one bound attribute
554
+ */
555
+ declare function resolveDataAccessLayerFlag(project: FormBuilderProject, options?: ExportOptions): boolean;
556
+ /**
557
+ * Injects the public dependencies that the inlined DAL runtime requires into a
558
+ * generated `package.json`. Today that is just React Query — everything else
559
+ * in `src/lib/dataverse/` relies only on built-in Web APIs (`fetch`) and the
560
+ * app's existing React dependency.
561
+ */
562
+ declare function ensureDataAccessLayerDependency(dependencies: Record<string, string>, enabled: boolean): void;
563
+
564
+ /**
565
+ * Grid Customizer Code Generator
566
+ *
567
+ * Generates PCF Grid Customizer code from GridCustomizerDefinition.
568
+ * Two export modes:
569
+ * - 'full': Complete PCF project with webpack, package.json, etc.
570
+ * - 'components-only': Just the renderer files + types + barrel export
571
+ *
572
+ * Output uses Fluent UI v9 to match Microsoft's Grid Customizer docs.
573
+ *
574
+ * Note: `nestedSelectionMode` (independent vs requires-parent gating) is only
575
+ * honored by FormCodeGenerator's nested-grid emission today. The PA Grid Control
576
+ * and components-only export modes don't emit nested grids, so the setting is
577
+ * silently dropped here — extending it is tracked separately.
578
+ */
579
+
580
+ type GridExportMode = 'full' | 'components-only' | 'pa-grid-control';
581
+ interface GridExportOptions {
582
+ mode: GridExportMode;
583
+ namespace?: string;
584
+ /**
585
+ * Emit the typed data-access layer (models, constants, hooks, inline runtime)
586
+ * alongside the renderer output. Applies to `full` and `components-only`
587
+ * modes; ignored for `pa-grid-control` which overlays Microsoft's grid and
588
+ * has no data of its own. Defaults to on when the grid has a `dataSource`
589
+ * and the mode supports it.
590
+ */
591
+ includeDataAccessLayer?: boolean;
592
+ /** When true, also emits `src/sampleData/{entity}.json` mock rows. */
593
+ includeSampleData?: boolean;
594
+ /**
595
+ * Full grid customizer list from the owning project. Required for nested
596
+ * relationship exports — the linked nested grid's data source drives the
597
+ * child entity's DAL emission + the parameterized hook filter.
598
+ */
599
+ allGrids?: GridCustomizerDefinition[];
600
+ /**
601
+ * Library used for cell-renderer + cell-editor emission in modes `full`
602
+ * and `components-only`. Defaults to 'fluent-v9' (the original behavior).
603
+ * Power Pages portals and Canvas App PCF controls need 'fluent-v8'.
604
+ * The `pa-grid-control` mode IGNORES this — it's a v8 platform contract
605
+ * and always emits v8 regardless.
606
+ */
607
+ componentLibrary?: 'fluent-v8' | 'fluent-v9';
608
+ }
609
+ /**
610
+ * Resolves the effective "include data access layer" flag for a grid export.
611
+ * Standalone PCF (`full`) and Components Only emit DAL when the grid has a
612
+ * data source. PA Grid Control never emits DAL.
613
+ */
614
+ declare function resolveGridDalFlag(def: GridCustomizerDefinition, options: GridExportOptions): boolean;
615
+ declare function generateGridCustomizerProject(def: GridCustomizerDefinition, options?: GridExportOptions): GeneratedFile[];
616
+
617
+ /**
618
+ * Mock Data Generator
619
+ *
620
+ * Shared utility for generating type-aware mock/sample data for grid previews.
621
+ * Used by ControlRenderer (subgrid preview) and GridCustomizer (grid preview).
622
+ *
623
+ * Extracted from ControlRenderer.tsx to avoid duplication.
624
+ */
625
+
626
+ /**
627
+ * Given a raw Dataverse row (with all its `@OData.Community.Display.V1.FormattedValue`
628
+ * and `_lookup_value` annotations), return a shape where each column's
629
+ * `fieldName` maps to the best display value. Preference order per column:
630
+ *
631
+ * 1. `fieldName@OData.Community.Display.V1.FormattedValue` — option set label,
632
+ * localized date, formatted money, etc.
633
+ * 2. `_fieldName_value@OData.Community.Display.V1.FormattedValue` — lookup
634
+ * display name when the fieldName is a lookup attribute.
635
+ * 3. `_fieldName_value` — lookup id as last resort.
636
+ * 4. `row[fieldName]` — the raw value (plain strings, numbers, dates).
637
+ *
638
+ * Non-mentioned columns pass through untouched so annotation-less fields stay
639
+ * intact. The returned row is a fresh object — the input is not mutated.
640
+ */
641
+ declare function flattenDataverseRowForPreview(row: Record<string, unknown>, columns: GridColumnDefinition[]): Record<string, unknown>;
642
+ /** Base mock value pools keyed by field name or data type */
643
+ declare const MOCK_VALUE_POOLS: Record<string, string[]>;
644
+ /**
645
+ * Generate mock rows for a subgrid/DetailsList preview.
646
+ * Uses SubgridColumn[] interface from the existing form control system.
647
+ */
648
+ declare function generateMockSubgridData(columns: SubgridColumn[], maxRows: number): Record<string, string>[];
649
+ /** Extended mock value pools for grid customizer data types */
650
+ declare const TYPED_MOCK_POOLS: Record<GridColumnDataType, unknown[]>;
651
+ /**
652
+ * Generate type-aware mock rows for grid customizer preview.
653
+ * Uses GridColumnDefinition[] interface with richer data types.
654
+ * Priority: renderer-specific pool > field-name pool > data-type pool.
655
+ */
656
+ declare function generateMockGridData(columns: GridColumnDefinition[], rowCount?: number): Record<string, unknown>[];
657
+
658
+ export { type BusinessRuleCodeGenOptions, categoryLabels as CATEGORY_LABELS, CATEGORY_ORDER, type CommandEntry, type ComponentLibrary, type EntityUsage, type EntityUsageAttribute, type GenerateAllFormCodeOptions, type GenerateDataLayerOptions, type GenerateFormCodeOptions, type GeneratedFile, type GridExportMode, type GridExportOptions, MOCK_VALUE_POOLS, type RichEntityGenerator, TYPED_MOCK_POOLS, type V9CompatibilityResult, type V9IncompatibilityReason, type V9IncompatibleRef, type V9RequiredRef, V9RequirementError, type V9RequirementReason, type V9RequirementResult, V9_INCOMPATIBLE_CONTROL_TYPES, V9_REQUIRED_CONTROL_TYPES, collectEntityUsage, collectEntityUsageFromGrid, ensureDataAccessLayerDependency, exportProject, flattenDataverseRowForPreview, generateAllFormCode, generateBusinessRuleCode, generateDataAccessLayer, generateDataAccessLayerForGrid, generateFormCode, generateGridCustomizerProject, generateMockGridData, generateMockSubgridData, generatePcfProject, generateReadme, generateStaticWebAppProject, generateWebResourceProject, getCommandReference, isControlV9Compatible, isControlV9Required, previewExport, registerRichEntityGenerator, resolveComponentLibrary, resolveDataAccessLayerFlag, resolveGridDalFlag, scanV9Compatibility, scanV9Requirements, summarizeV9Incompatibility, summarizeV9Requirements, toFormComponentName, toPascalCase, toSafeFileName, validateExportTarget };